From 8792331516d5f4fe94cdc07294f6b3d374e77c2b Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Tue, 21 Sep 2021 20:19:02 +0200 Subject: [PATCH] Text configuration serialize / deserialize to metadata string --- src/libslic3r/Format/3mf.cpp | 48 +++++--- .../TextConfigurationSerialization.cpp | 116 ++++++++++-------- .../TextConfigurationSerialization.hpp | 6 +- 3 files changed, 97 insertions(+), 73 deletions(-) diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index 2c4bb68ecd..544886a0f0 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -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 << " \n"; } } diff --git a/src/libslic3r/TextConfigurationSerialization.cpp b/src/libslic3r/TextConfigurationSerialization.cpp index ad6518bfcb..a70d875165 100644 --- a/src/libslic3r/TextConfigurationSerialization.cpp +++ b/src/libslic3r/TextConfigurationSerialization.cpp @@ -8,14 +8,16 @@ const std::map TextConfigurationSerialization::to_s {FontItem::Type::wx_font_descr, "wx_font_descriptor"} }; -const char TextConfigurationSerialization::separator = '|'; - const std::map 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 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 << "\n"; - stream << indent << "\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 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); } \ No newline at end of file diff --git a/src/libslic3r/TextConfigurationSerialization.hpp b/src/libslic3r/TextConfigurationSerialization.hpp index bdc65c2fc5..2fc0379824 100644 --- a/src/libslic3r/TextConfigurationSerialization.hpp +++ b/src/libslic3r/TextConfigurationSerialization.hpp @@ -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 from_string(const std::string &data); + static std::optional deserialize(const std::string &data); // convert type to string and vice versa static const std::map to_type; @@ -25,7 +23,7 @@ public: static const char separator; - // Move to map utility + // Move to map utils template static std::map create_oposit_map( const std::map &map)