Text configuration serialize / deserialize to metadata string

This commit is contained in:
Filip Sykala 2021-09-21 20:19:02 +02:00
parent 28528c8344
commit 8792331516
3 changed files with 97 additions and 73 deletions

View file

@ -121,6 +121,7 @@ static constexpr const char* VOLUME_TYPE = "volume";
static constexpr const char* NAME_KEY = "name";
static constexpr const char* MODIFIER_KEY = "modifier";
static constexpr const char* TEXT_CONFIGURATION_KEY = "text_configuration";
static constexpr const char* VOLUME_TYPE_KEY = "volume_type";
static constexpr const char* MATRIX_KEY = "matrix";
static constexpr const char* SOURCE_FILE_KEY = "source_file";
@ -1985,6 +1986,8 @@ namespace Slic3r {
volume->set_type(ModelVolumeType::PARAMETER_MODIFIER);
else if (metadata.key == VOLUME_TYPE_KEY)
volume->set_type(ModelVolume::type_from_string(metadata.value));
else if (metadata.key == TEXT_CONFIGURATION_KEY)
volume->text_configuration = TextConfigurationSerialization::deserialize(metadata.value);
else if (metadata.key == SOURCE_FILE_KEY)
volume->source.input_file = metadata.value;
else if (metadata.key == SOURCE_OBJECT_ID_KEY)
@ -2900,6 +2903,24 @@ namespace Slic3r {
bool _3MF_Exporter::_add_model_config_file_to_archive(mz_zip_archive& archive, const Model& model, const IdToObjectDataMap &objects_data)
{
enum class MetadataType{
object,
volume
};
auto add_metadata = [](std::stringstream &stream, unsigned indent, MetadataType type,
const std::string &key, const std::string &value) {
const char *type_value;
switch (type) {
case MetadataType::object: type_value = OBJECT_TYPE; break;
case MetadataType::volume: type_value = VOLUME_TYPE; break;
};
stream << std::string(indent, ' ') << '<' << METADATA_TAG << " "
<< TYPE_ATTR << "=\"" << type_value << "\" "
<< KEY_ATTR << "=\"" << key << "\" "
<< VALUE_ATTR << "=\"" << value << "\"/>\n";
};
std::stringstream stream;
// Store mesh transformation in full precision, as the volumes are stored transformed and they need to be transformed back
// when loaded as accurately as possible.
@ -2914,13 +2935,11 @@ namespace Slic3r {
stream << " <" << OBJECT_TAG << " " << ID_ATTR << "=\"" << obj_metadata.first << "\" " << INSTANCESCOUNT_ATTR << "=\"" << obj->instances.size() << "\">\n";
// stores object's name
if (!obj->name.empty())
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << OBJECT_TYPE << "\" " << KEY_ATTR << "=\"name\" " << VALUE_ATTR << "=\"" << xml_escape(obj->name) << "\"/>\n";
if (!obj->name.empty())
add_metadata(stream, 2, MetadataType::object, "name", xml_escape(obj->name));
// stores object's config data
for (const std::string& key : obj->config.keys()) {
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << OBJECT_TYPE << "\" " << KEY_ATTR << "=\"" << key << "\" " << VALUE_ATTR << "=\"" << obj->config.opt_serialize(key) << "\"/>\n";
}
for (const std::string& key : obj->config.keys())
add_metadata(stream, 2, MetadataType::object, key, obj->config.opt_serialize(key));
for (const ModelVolume* volume : obj_metadata.second.object->volumes) {
if (volume != nullptr) {
@ -2934,18 +2953,18 @@ namespace Slic3r {
// stores volume's name
if (!volume->name.empty())
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << NAME_KEY << "\" " << VALUE_ATTR << "=\"" << xml_escape(volume->name) << "\"/>\n";
add_metadata(stream, 3, MetadataType::volume, NAME_KEY, xml_escape(volume->name));
// stores volume's modifier field (legacy, to support old slicers)
if (volume->is_modifier())
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << MODIFIER_KEY << "\" " << VALUE_ATTR << "=\"1\"/>\n";
add_metadata(stream, 3, MetadataType::volume, MODIFIER_KEY, "1");
// stores volume's type (overrides the modifier field above)
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << VOLUME_TYPE_KEY << "\" " <<
VALUE_ATTR << "=\"" << ModelVolume::type_to_string(volume->type()) << "\"/>\n";
add_metadata(stream, 3, MetadataType::volume, VOLUME_TYPE_KEY, ModelVolume::type_to_string(volume->type()));
// stores volume's text data
if (volume->text_configuration.has_value())
stream << " " << TextConfigurationSerialization::to_string(*volume->text_configuration) << "\n";
add_metadata(stream, 3, MetadataType::volume, TEXT_CONFIGURATION_KEY,
xml_escape(TextConfigurationSerialization::serialize(*volume->text_configuration)));
// stores volume's local matrix
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << MATRIX_KEY << "\" " << VALUE_ATTR << "=\"";
@ -2979,10 +2998,9 @@ namespace Slic3r {
}
// stores volume's config data
for (const std::string& key : volume->config.keys()) {
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << key << "\" " << VALUE_ATTR << "=\"" << volume->config.opt_serialize(key) << "\"/>\n";
}
for (const std::string& key : volume->config.keys())
add_metadata(stream, 3, MetadataType::volume, key, volume->config.opt_serialize(key));
stream << " </" << VOLUME_TAG << ">\n";
}
}

View file

@ -8,14 +8,16 @@ const std::map<FontItem::Type, std::string> TextConfigurationSerialization::to_s
{FontItem::Type::wx_font_descr, "wx_font_descriptor"}
};
const char TextConfigurationSerialization::separator = '|';
const std::map<std::string, FontItem::Type> TextConfigurationSerialization::to_type =
TextConfigurationSerialization::create_oposit_map(TextConfigurationSerialization::to_string);
const char TextConfigurationSerialization::separator = '|';
std::string TextConfigurationSerialization::serialize(const TextConfiguration &text_configuration)
{
// IMPROVE: make general and move to string utils
auto twice_separator = [](const std::string& data) {
// no value is one space
if (data.empty()) return std::string(" ");
std::string::size_type pos = data.find(separator);
if (pos == data.npos) return data;
@ -41,61 +43,67 @@ std::string TextConfigurationSerialization::serialize(const TextConfiguration &t
std::to_string(font_prop.line_gap);
}
void TextConfigurationSerialization::to_xml(
const TextConfiguration &text_configuration,
unsigned count_indent,
std::stringstream & stream)
std::optional<TextConfiguration> TextConfigurationSerialization::deserialize(const std::string &data)
{
// Because of back compatibility must be main tag metadata (source: 3mf.cpp)
const std::string_view main_tag = "metadata";
const std::string_view main_name_attr = "name";
const std::string_view main_name_value= "TextConfiguration";
const std::string_view font_item_tag = "fontItem";
const std::string_view name_attr = "name";
const std::string_view path_attr = "path";
const std::string_view type_attr = "type";
const std::string_view font_prop_tag = "fontProp";
const std::string_view char_gap_attr = "charGap";
const std::string_view line_gap_attr = "lineGap";
const std::string_view flatness_attr = "flatness";
const std::string_view height_attr = "height";
const std::string_view depth_attr = "depth";
const std::string_view text_tag = "text";
auto get_path = [&text_configuration]() {
const std::string &path = text_configuration.font_item.path;
return xml_escape(
(text_configuration.font_item.type == FontItem::Type::file_path) ?
path.substr(path.find_last_of("/\\") + 1) : path);
// IMPROVE: make general and move to string utils
auto find_separator = [&data](std::string::size_type pos) {
pos = data.find(separator, pos);
while (pos != data.npos && data[pos + 1] == separator)
pos = data.find(separator, pos + 2);
return pos;
};
// IMPROVE: make general and move to string utils
auto reduce_separator = [](const std::string& item) {
std::string::size_type pos = item.find(separator);
if (pos == item.npos) return item;
std::string copy = item;
do {
assert(copy[pos] == separator);
assert(copy[pos+1] == separator);
copy.erase(pos, size_t(1));
pos = copy.find(separator, pos + 1);
} while (pos != copy.npos);
return copy;
};
std::string indent = std::string(count_indent, ' ');
std::string indent2 = std::string(count_indent+1, ' ');
std::string::size_type start = 0;
std::string::size_type size = find_separator(start);
auto reduce_and_move = [&data, &start, &size, &reduce_separator, &find_separator]() {
if (start == data.npos) return std::string();
std::string res = reduce_separator(data.substr(start, size));
start = size + 1;
size = find_separator(start) - start;
return res;
};
auto get_float_and_move = [&data, &start, &size, &find_separator]() {
if (start == data.npos) return 0.f;
float res = std::atof(data.substr(start, size).c_str());
start = size + 1;
size = find_separator(start) - start;
return res;
};
auto get_int_and_move = [&data, &start, &size, &find_separator]() {
if (start == data.npos) return 0;
int res = std::atoi(data.substr(start, size).c_str());
start = size + 1;
size = find_separator(start) - start;
return res;
};
stream << indent << "<" << main_tag << " " << main_name_attr << "=\"" << main_name_value << "\">\n";
stream << indent2 << "<" << font_item_tag
<< ' ' << name_attr << "=\"" << xml_escape(text_configuration.font_item.name) << '"'
<< ' ' << path_attr << "=\"" << get_path() << '"'
<< ' ' << type_attr << "=\"" << to_string.at(text_configuration.font_item.type) << '"'
<< "/>\n";
stream << indent2 << "<" << font_prop_tag
<< ' ' << char_gap_attr << "=\"" << text_configuration.font_prop.char_gap << '"'
<< ' ' << line_gap_attr << "=\"" << text_configuration.font_prop.line_gap << '"'
<< ' ' << flatness_attr << "=\"" << text_configuration.font_prop.flatness << '"'
<< ' ' << height_attr << "=\"" << text_configuration.font_prop.size_in_mm << '"'
<< ' ' << depth_attr << "=\"" << text_configuration.font_prop.emboss << '"'
<< "/>\n";
stream << indent2 << "<" << text_tag << ">";
stream << xml_escape(text_configuration.text);
stream << indent2 << "</" << text_tag << ">\n";
stream << indent << "</" << main_tag << ">\n";
}
std::string text = reduce_and_move();
std::string name = reduce_and_move();
std::string path = reduce_and_move();
std::string type = reduce_and_move();
auto it = to_type.find(type);
if (it == to_type.end()) return {}; // no valid type
FontItem font_item(name,path,it->second);
std::optional<TextConfiguration> TextConfigurationSerialization::from_string(const std::string &data)
{
TextConfiguration tc;
return tc;
FontProp font_prop;
font_prop.emboss = get_float_and_move();
font_prop.flatness = get_float_and_move();
font_prop.size_in_mm = get_float_and_move();
font_prop.char_gap = get_int_and_move();
if (start == data.npos) return {}; // no valid data
font_prop.line_gap = get_int_and_move();
return TextConfiguration(font_item, font_prop, text);
}

View file

@ -15,9 +15,7 @@ class TextConfigurationSerialization
public:
TextConfigurationSerialization() = delete; // only static functions
static std::string serialize(const TextConfiguration &text_configuration);
static void to_xml(const TextConfiguration &text_configuration, unsigned count_indent, std::stringstream& stream);
static std::optional<TextConfiguration> from_string(const std::string &data);
static std::optional<TextConfiguration> deserialize(const std::string &data);
// convert type to string and vice versa
static const std::map<std::string, FontItem::Type> to_type;
@ -25,7 +23,7 @@ public:
static const char separator;
// Move to map utility
// Move to map utils
template<typename Key, typename Value>
static std::map<Value, Key> create_oposit_map(
const std::map<Key, Value> &map)