From 1b5bdbeb18a2aee486f31e6453d647b6db11604d Mon Sep 17 00:00:00 2001 From: Thomas Fussell Date: Fri, 12 Aug 2016 00:22:14 -0400 Subject: [PATCH] improve manifest interface, work on round-tripping --- include/xlnt/packaging/manifest.hpp | 234 +++++-------- include/xlnt/packaging/uri.hpp | 2 + include/xlnt/styles/border.hpp | 2 + include/xlnt/utils/path.hpp | 2 + source/detail/workbook_impl.hpp | 2 +- source/detail/xlsx_consumer.cpp | 133 ++----- source/detail/xlsx_producer.cpp | 271 +++++++------- source/detail/xlsx_producer.hpp | 45 +-- source/packaging/manifest.cpp | 369 ++++++-------------- source/packaging/relationship.cpp | 10 +- source/packaging/tests/test_zip_file.hpp | 14 +- source/packaging/uri.cpp | 28 ++ source/packaging/zip_file.cpp | 42 +-- source/styles/color.cpp | 4 +- source/utils/path.cpp | 249 ++++--------- source/utils/tests/test_path.hpp | 2 +- source/workbook/tests/test_produce_xlsx.hpp | 2 +- source/workbook/tests/test_workbook.hpp | 4 +- source/workbook/workbook.cpp | 71 ++-- source/worksheet/tests/test_worksheet.hpp | 5 +- tests/data/xlsx/8_default-excel.xlsx | Bin 20979 -> 20982 bytes tests/helpers/path_helper.hpp | 8 +- tests/helpers/temporary_file.hpp | 4 +- tests/helpers/xml_helper.hpp | 34 +- 24 files changed, 615 insertions(+), 922 deletions(-) create mode 100644 source/packaging/uri.cpp diff --git a/include/xlnt/packaging/manifest.hpp b/include/xlnt/packaging/manifest.hpp index b031aba9..ab171c28 100644 --- a/include/xlnt/packaging/manifest.hpp +++ b/include/xlnt/packaging/manifest.hpp @@ -32,42 +32,6 @@ namespace xlnt { -enum class content_type -{ - core_properties, - extended_properties, - custom_properties, - - calculation_chain, - chartsheet, - comments, - connections, - custom_property, - custom_xml_mappings, - dialogsheet, - drawings, - external_workbook_references, - metadata, - pivot_table, - pivot_table_cache_definition, - pivot_table_cache_records, - query_table, - shared_string_table, - shared_workbook_revision_headers, - shared_workbook, - revision_log, - shared_workbook_user_data, - single_cell_table_definitions, - styles, - table_definition, - theme, - volatile_dependencies, - workbook, - worksheet, - - unknown -}; - /// /// The manifest keeps track of all files in the OOXML package and /// their type and relationships. @@ -76,122 +40,69 @@ class XLNT_CLASS manifest { public: /// - /// Convenience method for clear_types() and clear_relationships() + /// Unregisters all default and override type and all relationships and known parts. /// void clear(); - + + /// + /// Returns the path to all internal package parts registered as a source + /// or target of a relationship. + /// + std::vector get_parts() const; + + // Relationships + /// - /// Convenince method for clear_default_types() and clear_override_types() + /// Returns true if the manifest contains a relationship with the given type with part as the source. /// - void clear_types(); - - /// - /// Unregisters every default content type (i.e. type based on part extension). - /// - void clear_default_types(); - - /// - /// Unregisters every content type for every part except default types. - /// - void clear_override_types(); - - /// - /// Convenince method for clear_package_relationships() and clear_part_relationships() - /// - void clear_relationships(); - - /// - /// Unregisters every relationship with the package root as the source. - /// - void clear_package_relationships(); - - /// - /// Unregisters every relationship except those with the package root as the source. - /// - void clear_part_relationships(); - - /// - /// Returns true if the manifest contains a package relationship with the given type. - /// - bool has_package_relationship(relationship::type type) const; - - /// - /// Returns true if the manifest contains a package relationship with the given id. - /// - bool has_package_relationship(const std::string &rel_id) const; + bool has_relationship(const path &source, relationship::type type) const; /// /// Returns true if the manifest contains a relationship with the given type with part as the source. /// - bool has_part_relationship(const path &part, relationship::type type) const; + bool has_relationship(const path &source, const std::string &rel_id) const; + + /// + /// Returns the relationship with "source" as the source and with a type of "type". + /// Throws a key_not_found exception if no such relationship is found. + /// + relationship get_relationship(const path &source, relationship::type type) const; + + /// + /// Returns the relationship with "source" as the source and with an ID of "rel_id". + /// Throws a key_not_found exception if no such relationship is found. + /// + relationship get_relationship(const path &source, const std::string &rel_id) const; + + /// + /// Returns all relationship with "source" as the source. + /// + std::vector get_relationships(const path &source) const; + + /// + /// Returns all relationships with "source" as the source and with a type of "type". + /// + std::vector get_relationships(const path &source, relationship::type type) const; + + /// + /// + /// + std::string register_relationship(const uri &source, relationship::type type, const uri &target, target_mode mode); /// - /// Returns true if the manifest contains a relationship with the given type with part as the source. + /// /// - bool has_part_relationship(const path &part, const std::string &rel_id) const; + std::string register_relationship(const uri &source, relationship::type type, const uri &target, target_mode mode, const std::string &rel_id); - relationship get_package_relationship(relationship::type type) const; - - relationship get_package_relationship(const std::string &rel_id) const; - - std::vector get_package_relationships() const; - - std::vector get_package_relationships(relationship::type type) const; - - relationship get_part_relationship(const path &part, relationship::type type) const; - - relationship get_part_relationship(const path &part, const std::string &rel_id) const; - - std::vector get_part_relationships(const path &part) const; - - std::vector get_part_relationships(const path &part, relationship::type type) const; - - /// - /// Given the path to a part, returns the content type of the part as - /// a content_type enum value or content_type::unknown if it isn't known. - /// - content_type get_content_type(const path &part) const; + // Content Types /// /// Given the path to a part, returns the content type of the part as a string. /// - std::string get_content_type_string(const path &part) const; - - void register_override_type(const path &part, const std::string &content_type); - - /// - /// Unregisters the path of the part of the given type and removes all relationships - /// with the part as a source or target. - /// - void unregister_override_type(const path &part); - - /// - /// Returns true if the part at the given path has been registered in this manifest. - /// - bool has_override_type(const path &part) const; - - std::vector get_overriden_parts() const; - - /// - /// - /// - std::string register_package_relationship(relationship::type type, const path &target_uri, target_mode mode); - - /// - /// - /// - std::string register_package_relationship(relationship::type type, const path &target_uri, target_mode mode, const std::string &rel_id); - - /// - /// - /// - std::string register_part_relationship(const path &source_uri, relationship::type type, const path &target_uri, target_mode mode); - - /// - /// - /// - std::string register_part_relationship(const path &source_uri, relationship::type type, const path &target_uri, target_mode mode, const std::string &rel_id); + std::string get_content_type(const path &part) const; + // Default Content Types + /// /// Returns true if a default content type for the extension has been registered. /// @@ -200,9 +111,7 @@ public: /// /// Returns a vector of all extensions with registered default content types. /// - std::vector get_default_type_extensions() const; - - std::vector get_parts_with_relationships() const; + std::vector get_extensions_with_default_types() const; /// /// Returns the registered default content type for parts of the given extension. @@ -212,29 +121,50 @@ public: /// /// Associates the given extension with the given content type. /// - void register_default_type(const std::string &extension, const std::string &content_type); + void register_default_type(const std::string &extension, const std::string &type); /// /// Unregisters the default content type for the given extension. /// void unregister_default_type(const std::string &extension); + + // Override Content Types + + /// + /// Returns true if a content type overriding the default content type has been registered + /// for the given part. + /// + bool has_override_type(const path &part) const; + + /// + /// Returns the override content type registered for the given part. + /// Throws key_not_found exception if no override type was found. + /// + std::string get_override_type(const path &part) const; + + /// + /// Returns the path of every part in this manifest with an overriden content type. + /// + std::vector get_parts_with_overriden_types() const; + + /// + /// Overrides any default type registered for the part's extension with the given content type. + /// + void register_override_type(const path &part, const std::string &type); + + /// + /// Unregisters the overriding content type of the given part. + /// + void unregister_override_type(const path &part); private: - struct part_info - { - std::string content_type; - std::vector relationships; - }; - - std::string next_package_relationship_id() const; - std::string next_relationship_id(const path &part) const; - std::unordered_map part_infos_; - - std::vector package_relationships_; - - std::unordered_map extension_content_type_map_; + std::unordered_map default_content_types_; + + std::unordered_map override_content_types_; + + std::unordered_map> relationships_; }; } // namespace xlnt diff --git a/include/xlnt/packaging/uri.hpp b/include/xlnt/packaging/uri.hpp index 7c0924d9..b3a45bca 100644 --- a/include/xlnt/packaging/uri.hpp +++ b/include/xlnt/packaging/uri.hpp @@ -63,6 +63,8 @@ public: uri make_absolute(const uri &base); uri make_reference(const uri &base); + friend XLNT_FUNCTION bool operator==(const uri &left, const uri &right); + private: bool absolute_; std::string scheme_; diff --git a/include/xlnt/styles/border.hpp b/include/xlnt/styles/border.hpp index a7377ade..def7a78b 100644 --- a/include/xlnt/styles/border.hpp +++ b/include/xlnt/styles/border.hpp @@ -104,8 +104,10 @@ protected: private: std::unordered_map sides_; + /* bool outline_ = true; diagonal_direction diagonal_direction_ = diagonal_direction::neither; + */ }; } // namespace xlnt diff --git a/include/xlnt/utils/path.hpp b/include/xlnt/utils/path.hpp index 1ae5617d..f0b29fda 100644 --- a/include/xlnt/utils/path.hpp +++ b/include/xlnt/utils/path.hpp @@ -154,6 +154,8 @@ public: /// Append the provided part to this path and return the result. /// path append(const path &to_append) const; + + friend XLNT_FUNCTION bool operator==(const path &left, const path &right); private: /// diff --git a/source/detail/workbook_impl.hpp b/source/detail/workbook_impl.hpp index 7379a1af..4ecc71cc 100644 --- a/source/detail/workbook_impl.hpp +++ b/source/detail/workbook_impl.hpp @@ -71,9 +71,9 @@ struct workbook_impl guess_types_(other.guess_types_), data_only_(other.data_only_), stylesheet_(other.stylesheet_), + manifest_(other.manifest_), has_theme_(other.has_theme_), theme_(other.theme_), - manifest_(other.manifest_), write_core_properties_(other.write_core_properties_), creator_(other.creator_), last_modified_by_(other.last_modified_by_), diff --git a/source/detail/xlsx_consumer.cpp b/source/detail/xlsx_consumer.cpp index 45c9165b..89b10245 100644 --- a/source/detail/xlsx_consumer.cpp +++ b/source/detail/xlsx_consumer.cpp @@ -19,11 +19,6 @@ bool is_true(const std::string &bool_string) return bool_string == "1" || bool_string == "true"; } -bool is_false(const std::string &bool_string) -{ - return bool_string == "0" || bool_string == "false"; -} - std::size_t string_to_size_t(const std::string &s) { #if ULLONG_MAX == SIZE_MAX @@ -59,16 +54,6 @@ struct EnumClassHash } }; -std::string string_lower(std::string str) -{ - for (std::size_t i = 0; i < str.size(); i++) - { - str[i] = std::tolower(str[i]); - } - - return str; -} - template T from_string(const std::string &string); @@ -649,45 +634,21 @@ std::vector read_relationships(const xlnt::path &part, xlnt: document.load_string(archive.read(part).c_str()); auto root_node = document.child("Relationships"); + xlnt::uri source(part.string()); for (auto relationship_node : root_node.children("Relationship")) { std::string id(relationship_node.attribute("Id").value()); std::string type_string(relationship_node.attribute("Type").value()); auto type = from_string(type_string); - xlnt::path rel_target(relationship_node.attribute("Target").value()); + xlnt::uri target(relationship_node.attribute("Target").value()); - relationships.push_back(xlnt::relationship(id, type, rel_target, xlnt::target_mode::internal)); + relationships.push_back(xlnt::relationship(id, type, source, target, xlnt::target_mode::internal)); } return relationships; } -std::string::size_type find_string_in_string(const std::string &string, const std::string &substring) -{ - std::string::size_type possible_match_index = string.find(substring.at(0)); - - while (possible_match_index != std::string::npos) - { - if (string.substr(possible_match_index, substring.size()) == substring) - { - return possible_match_index; - } - - possible_match_index = string.find(substring.at(0), possible_match_index + 1); - } - - return possible_match_index; -} - -/// -/// Returns true if d is exactly equal to an integer. -/// -bool is_integral(long double d) -{ - return d == static_cast(d); -} - void check_document_type(const std::string &document_content_type) { if (document_content_type != "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml" @@ -733,12 +694,11 @@ void xlsx_consumer::populate_workbook() { auto &manifest = destination_.get_manifest(); read_manifest(); - path workbook_part; - for (const auto &rel : manifest.get_package_relationships()) + for (const auto &rel : manifest.get_relationships(path("/"))) { pugi::xml_document document; - document.load_string(source_.read(path(rel.get_target_uri())).c_str()); + document.load_string(source_.read(rel.get_target().get_path()).c_str()); switch (rel.get_type()) { @@ -752,20 +712,9 @@ void xlsx_consumer::populate_workbook() read_custom_property(document.root()); break; case relationship::type::office_document: - check_document_type(manifest.get_content_type_string(rel.get_target_uri())); - workbook_part = rel.get_target_uri(); + check_document_type(manifest.get_content_type(rel.get_target().get_path())); read_workbook(document.root()); break; - } - } - - for (const auto &rel : manifest.get_part_relationships(workbook_part)) - { - pugi::xml_document document; - document.load_string(source_.read(path(rel.get_target_uri())).c_str()); - - switch (rel.get_type()) - { case relationship::type::calculation_chain: read_calculation_chain(document.root()); break; @@ -799,13 +748,17 @@ void xlsx_consumer::populate_workbook() case relationship::type::volatile_dependencies: read_volatile_dependencies(document.root()); break; + default: + break; } } - for (const auto &rel : manifest.get_part_relationships(workbook_part)) + const auto workbook_rel = manifest.get_relationship(path("/"), relationship::type::office_document); + + for (const auto &rel : manifest.get_relationships(workbook_rel.get_target().get_path())) { pugi::xml_document document; - document.load_string(source_.read(path(rel.get_target_uri())).c_str()); + document.load_string(source_.read(path(rel.get_target().get_path())).c_str()); switch (rel.get_type()) { @@ -818,6 +771,8 @@ void xlsx_consumer::populate_workbook() case relationship::type::worksheet: read_worksheet(document.root(), rel.get_id()); break; + default: + break; } } @@ -846,18 +801,6 @@ void xlsx_consumer::read_manifest() const auto root_node = document.child("Types"); auto &manifest = destination_.get_manifest(); - auto make_relative = [](const path &p) - { - path copy; - - for (const auto &part : std::vector(p.begin() + 1, p.end())) - { - copy.append(part); - } - - return copy; - }; - for (const auto child : root_node.children()) { if (child.name() == std::string("Default")) @@ -874,8 +817,9 @@ void xlsx_consumer::read_manifest() for (const auto &package_rel : package_rels) { - manifest.register_package_relationship(package_rel.get_type(), - package_rel.get_target_uri(), + manifest.register_relationship(uri("/"), + package_rel.get_type(), + package_rel.get_target(), package_rel.get_target_mode(), package_rel.get_id()); } @@ -886,14 +830,13 @@ void xlsx_consumer::read_manifest() || relationship_source.filename.extension() != ".rels") continue; auto part_rels = read_relationships(relationship_source.filename, source_); - auto source_uri = relationship_source.filename.parent().parent() - .append(relationship_source.filename.basename()); + uri source(relationship_source.filename.parent().parent() + .append(relationship_source.filename.filename()).string()); for (const auto part_rel : part_rels) { - auto part = source_uri; - manifest.register_part_relationship(source_uri, part_rel.get_type(), - part_rel.get_target_uri(), part_rel.get_target_mode(), part_rel.get_id()); + manifest.register_relationship(source, part_rel.get_type(), + part_rel.get_target(), part_rel.get_target_mode(), part_rel.get_id()); } } } @@ -934,7 +877,7 @@ void xlsx_consumer::read_custom_file_properties(const pugi::xml_node root) { pugi::xml_document document; document.load_string(source_.read(path("[Content Types].xml")).c_str()); - auto root_node = document.append_child("customFileProperties"); + /*auto root_node = */document.append_child("customFileProperties"); } // Write SpreadsheetML-Specific Package Parts @@ -977,56 +920,56 @@ void xlsx_consumer::read_chartsheet(const pugi::xml_node root, const std::string { pugi::xml_document document; document.load_string(source_.read(path("[Content Types].xml")).c_str()); - auto root_node = document.append_child("chartsheet"); + /*auto root_node = */ document.append_child("chartsheet"); } void xlsx_consumer::read_connections(const pugi::xml_node root) { pugi::xml_document document; document.load_string(source_.read(path("[Content Types].xml")).c_str()); - auto root_node = document.append_child("connections"); + /*auto root_node = */ document.append_child("connections"); } void xlsx_consumer::read_custom_property(const pugi::xml_node root) { pugi::xml_document document; document.load_string(source_.read(path("[Content Types].xml")).c_str()); - auto root_node = document.append_child("customProperty"); + /*auto root_node = */ document.append_child("customProperty"); } void xlsx_consumer::read_custom_xml_mappings(const pugi::xml_node root) { pugi::xml_document document; document.load_string(source_.read(path("[Content Types].xml")).c_str()); - auto root_node = document.append_child("connections"); + /*auto root_node = */ document.append_child("connections"); } void xlsx_consumer::read_dialogsheet(const pugi::xml_node root, const std::string &title) { pugi::xml_document document; document.load_string(source_.read(path("[Content Types].xml")).c_str()); - auto root_node = document.append_child("dialogsheet"); + /*auto root_node = */ document.append_child("dialogsheet"); } void xlsx_consumer::read_external_workbook_references(const pugi::xml_node root) { pugi::xml_document document; document.load_string(source_.read(path("[Content Types].xml")).c_str()); - auto root_node = document.append_child("externalLink"); + /*auto root_node = */ document.append_child("externalLink"); } void xlsx_consumer::read_metadata(const pugi::xml_node root) { pugi::xml_document document; document.load_string(source_.read(path("[Content Types].xml")).c_str()); - auto root_node = document.append_child("metadata"); + /*auto root_node = */ document.append_child("metadata"); } void xlsx_consumer::read_pivot_table(const pugi::xml_node root) { pugi::xml_document document; document.load_string(source_.read(path("[Content Types].xml")).c_str()); - auto root_node = document.append_child("pivotTableDefinition"); + /*auto root_node = */ document.append_child("pivotTableDefinition"); } void xlsx_consumer::read_shared_string_table(const pugi::xml_node root) @@ -1109,21 +1052,21 @@ void xlsx_consumer::read_shared_workbook_revision_headers(const pugi::xml_node r { pugi::xml_document document; document.load_string(source_.read(path("[Content Types].xml")).c_str()); - auto root_node = document.append_child("headers"); + /*auto root_node = */ document.append_child("headers"); } void xlsx_consumer::read_shared_workbook(const pugi::xml_node root) { pugi::xml_document document; document.load_string(source_.read(path("[Content Types].xml")).c_str()); - auto root_node = document.append_child("revisions"); + /*auto root_node = */ document.append_child("revisions"); } void xlsx_consumer::read_shared_workbook_user_data(const pugi::xml_node root) { pugi::xml_document document; document.load_string(source_.read(path("[Content Types].xml")).c_str()); - auto root_node = document.append_child("users"); + /*auto root_node = */ document.append_child("users"); } void xlsx_consumer::read_stylesheet(const pugi::xml_node root) @@ -1150,7 +1093,7 @@ void xlsx_consumer::read_volatile_dependencies(const pugi::xml_node root) { pugi::xml_document document; document.load_string(source_.read(path("[Content Types].xml")).c_str()); - auto root_node = document.append_child("volTypes"); + /*auto root_node = */ document.append_child("volTypes"); } void xlsx_consumer::read_worksheet(const pugi::xml_node root, const std::string &rel_id) @@ -1344,14 +1287,14 @@ void xlsx_consumer::read_comments(const pugi::xml_node root) { pugi::xml_document document; document.load_string(source_.read(path("[Content Types].xml")).c_str()); - auto root_node = document.append_child("comments"); + /*auto root_node = */ document.append_child("comments"); } void xlsx_consumer::read_drawings(const pugi::xml_node root) { pugi::xml_document document; document.load_string(source_.read(path("[Content Types].xml")).c_str()); - auto root_node = document.append_child("wsDr"); + /*auto root_node = */ document.append_child("wsDr"); } // Unknown Parts @@ -1360,14 +1303,14 @@ void xlsx_consumer::read_unknown_parts(const pugi::xml_node root) { pugi::xml_document document; document.load_string(source_.read(path("[Content Types].xml")).c_str()); - auto root_node = document.append_child("Hmm"); + /*auto root_node = */ document.append_child("Hmm"); } void xlsx_consumer::read_unknown_relationships(const pugi::xml_node root) { pugi::xml_document document; document.load_string(source_.read(path("[Content Types].xml")).c_str()); - auto root_node = document.append_child("Relationships"); + /*auto root_node = */ document.append_child("Relationships"); } } // namespace detail diff --git a/source/detail/xlsx_producer.cpp b/source/detail/xlsx_producer.cpp index 05f90532..745d0e57 100644 --- a/source/detail/xlsx_producer.cpp +++ b/source/detail/xlsx_producer.cpp @@ -191,7 +191,7 @@ void write_relationships(const std::vector &relationships, p relationship_node.append_attribute("Id").set_value(relationship.get_id().c_str()); relationship_node.append_attribute("Type").set_value(to_string(relationship.get_type()).c_str()); - relationship_node.append_attribute("Target").set_value(relationship.get_target_uri().to_string('/').c_str()); + relationship_node.append_attribute("Target").set_value(relationship.get_target().get_path().string().c_str()); if (relationship.get_target_mode() == xlnt::target_mode::external) { @@ -586,90 +586,43 @@ void xlsx_producer::write(std::vector &destination) void xlsx_producer::populate_archive() { write_manifest(); - path workbook_part; - for (auto &rel : source_.impl().manifest_.get_package_relationships()) + for (auto &rel : source_.impl().manifest_.get_relationships(path("/"))) { pugi::xml_document document; + bool write_document = true; switch (rel.get_type()) { case relationship::type::core_properties: - write_core_properties(document.root()); + write_core_properties(rel, document.root()); break; + case relationship::type::extended_properties: - write_extended_properties(document.root()); + write_extended_properties(rel, document.root()); break; + case relationship::type::custom_properties: - write_custom_properties(document.root()); + write_custom_properties(rel, document.root()); break; + case relationship::type::office_document: - write_workbook(document.root()); - workbook_part = rel.get_target_uri(); + write_workbook(rel, document.root()); break; + case relationship::type::thumbnail: - destination_.write_string( - std::string(source_.get_thumbnail().begin(), source_.get_thumbnail().end()), - rel.get_target_uri()); - continue; - } + write_thumbnail(rel); + write_document = false; + break; + + default: + break; + } - write_document_to_archive(document, rel.get_target_uri(), destination_); - } - - for (auto rel : source_.impl().manifest_.get_part_relationships(workbook_part)) - { - pugi::xml_document document; - - switch (rel.get_type()) - { - case relationship::type::calculation_chain: - write_calculation_chain(document.root()); - break; - case relationship::type::chartsheet: - write_chartsheet(document.root(), rel); - break; - case relationship::type::connections: - write_connections(document.root()); - break; - case relationship::type::custom_xml_mappings: - write_custom_xml_mappings(document.root()); - break; - case relationship::type::dialogsheet: - write_dialogsheet(document.root(), rel); - break; - case relationship::type::external_workbook_references: - write_external_workbook_references(document.root()); - break; - case relationship::type::metadata: - write_metadata(document.root()); - break; - case relationship::type::pivot_table: - write_pivot_table(document.root()); - break; - case relationship::type::shared_string_table: - write_shared_string_table(document.root()); - break; - case relationship::type::shared_workbook_revision_headers: - write_shared_workbook_revision_headers(document.root()); - break; - case relationship::type::styles: - write_styles(document.root()); - break; - case relationship::type::theme: - write_theme(document.root()); - break; - case relationship::type::volatile_dependencies: - write_volatile_dependencies(document.root()); - break; - case relationship::type::worksheet: - write_worksheet(document.root(), rel); - break; - } - - std::ostringstream document_stream; - document.save(document_stream); - destination_.write_string(document_stream.str(), rel.get_target_uri()); + if (write_document) + { + write_document_to_archive(document, rel.get_target().get_path(), destination_); + } } // Unknown Parts @@ -687,7 +640,7 @@ void xlsx_producer::write_manifest() auto types_node = content_types_document.append_child("Types"); types_node.append_attribute("xmlns").set_value("http://schemas.openxmlformats.org/package/2006/content-types"); - for (const auto &extension : source_.get_manifest().get_default_type_extensions()) + for (const auto &extension : source_.get_manifest().get_extensions_with_default_types()) { auto default_node = types_node.append_child("Default"); default_node.append_attribute("Extension").set_value(extension.c_str()); @@ -695,32 +648,31 @@ void xlsx_producer::write_manifest() default_node.append_attribute("ContentType").set_value(content_type.c_str()); } - for (const auto &part : source_.get_manifest().get_overriden_parts()) + for (const auto &part : source_.get_manifest().get_parts_with_overriden_types()) { auto override_node = types_node.append_child("Override"); - override_node.append_attribute("PartName").set_value(("/" + part.to_string('/')).c_str()); - auto content_type = source_.get_manifest().get_content_type_string(part); + override_node.append_attribute("PartName").set_value(part.string().c_str()); + auto content_type = source_.get_manifest().get_override_type(part); override_node.append_attribute("ContentType").set_value(content_type.c_str()); } - for (const auto &part : source_.get_manifest().get_parts_with_relationships()) + for (const auto &part : source_.get_manifest().get_parts()) { - auto part_rels = source_.get_manifest().get_part_relationships(part); + auto part_rels = source_.get_manifest().get_relationships(part); + + if (part_rels.empty()) continue; pugi::xml_document part_rels_document; write_relationships(part_rels, part_rels_document.root()); - path rels_path = part.parent().append("_rels").append(part.basename() + ".rels"); + path parent(part.parent().string().front() == '/' ? part.parent().string().substr(1) : part.parent().string()); + path rels_path(parent.append("_rels").append(part.filename() + ".rels").string()); write_document_to_archive(part_rels_document, rels_path, destination_); } write_document_to_archive(content_types_document, path("[Content_Types].xml"), destination_); - - pugi::xml_document package_rels_document; - write_relationships(source_.get_manifest().get_package_relationships(), package_rels_document); - write_document_to_archive(package_rels_document, path("_rels/.rels"), destination_); } -void xlsx_producer::write_extended_properties(pugi::xml_node root) +void xlsx_producer::write_extended_properties(const relationship &rel, pugi::xml_node root) { auto properties_node = root.append_child("Properties"); @@ -765,7 +717,7 @@ void xlsx_producer::write_extended_properties(pugi::xml_node root) } } -void xlsx_producer::write_core_properties(pugi::xml_node root) +void xlsx_producer::write_core_properties(const relationship &rel, pugi::xml_node root) { auto core_properties_node = root.append_child("cp:coreProperties"); @@ -788,14 +740,14 @@ void xlsx_producer::write_core_properties(pugi::xml_node root) core_properties_node.append_child("cp:category"); } -void xlsx_producer::write_custom_properties(pugi::xml_node root) +void xlsx_producer::write_custom_properties(const relationship &rel, pugi::xml_node root) { - auto properties_node = root.append_child("Properties"); + /*auto properties_node = */root.append_child("Properties"); } // Write SpreadsheetML-Specific Package Parts -void xlsx_producer::write_workbook(pugi::xml_node root) +void xlsx_producer::write_workbook(const relationship &rel, pugi::xml_node root) { std::size_t num_visible = 0; @@ -842,14 +794,13 @@ void xlsx_producer::write_workbook(pugi::xml_node root) auto sheets_node = workbook_node.append_child("sheets"); auto defined_names_node = workbook_node.append_child("definedNames"); - std::size_t index = 1; - auto wb_rel = source_.d_->manifest_.get_package_relationship(xlnt::relationship::type::office_document); + auto wb_rel = source_.d_->manifest_.get_relationship(path("/"), xlnt::relationship::type::office_document); for (const auto ws : source_) { auto sheet_rel_id = source_.d_->sheet_title_rel_id_map_[ws.get_title()]; - auto sheet_rel = source_.d_->manifest_.get_part_relationship(wb_rel.get_target_uri(), sheet_rel_id); + auto sheet_rel = source_.d_->manifest_.get_relationship(wb_rel.get_target().get_path(), sheet_rel_id); auto sheet_node = sheets_node.append_child("sheet"); sheet_node.append_attribute("name").set_value(ws.get_title().c_str()); @@ -893,51 +844,118 @@ void xlsx_producer::write_workbook(pugi::xml_node root) defined_name_node.text().set(target_string.c_str()); } */ + + for (const auto &child_rel : source_.get_manifest().get_relationships(rel.get_target().get_path())) + { + pugi::xml_document document; + + switch (child_rel.get_type()) + { + case relationship::type::calculation_chain: + write_calculation_chain(child_rel, document.root()); + break; + + case relationship::type::chartsheet: + write_chartsheet(child_rel, document.root()); + break; + + case relationship::type::connections: + write_connections(child_rel, document.root()); + break; + + case relationship::type::custom_xml_mappings: + write_custom_xml_mappings(child_rel, document.root()); + break; + + case relationship::type::dialogsheet: + write_dialogsheet(child_rel, document.root()); + break; + + case relationship::type::external_workbook_references: + write_external_workbook_references(child_rel, document.root()); + break; + + case relationship::type::metadata: + write_metadata(child_rel, document.root()); + break; + + case relationship::type::pivot_table: + write_pivot_table(child_rel, document.root()); + break; + case relationship::type::shared_string_table: + write_shared_string_table(child_rel, document.root()); + break; + case relationship::type::shared_workbook_revision_headers: + write_shared_workbook_revision_headers(child_rel, document.root()); + break; + + case relationship::type::styles: + write_styles(child_rel, document.root()); + break; + + case relationship::type::theme: + write_theme(child_rel, document.root()); + break; + + case relationship::type::volatile_dependencies: + write_volatile_dependencies(child_rel, document.root()); + break; + + case relationship::type::worksheet: + write_worksheet(child_rel, document.root()); + break; + + default: + break; + } + + write_document_to_archive(document, child_rel.get_target().get_path(), destination_); + } } // Write Workbook Relationship Target Parts -void xlsx_producer::write_calculation_chain(pugi::xml_node root) +void xlsx_producer::write_calculation_chain(const relationship &rel, pugi::xml_node root) { - auto calc_chain_node = root.append_child("calcChain"); + /*auto calc_chain_node = */root.append_child("calcChain"); } -void xlsx_producer::write_chartsheet(pugi::xml_node root, const relationship &rel) +void xlsx_producer::write_chartsheet(const relationship &rel, pugi::xml_node root) { - auto chartsheet_node = root.append_child("chartsheet"); + /*auto chartsheet_node = */root.append_child("chartsheet"); } -void xlsx_producer::write_connections(pugi::xml_node root) +void xlsx_producer::write_connections(const relationship &rel, pugi::xml_node root) { - auto connections_node = root.append_child("connections"); + /*auto connections_node = */root.append_child("connections"); } -void xlsx_producer::write_custom_xml_mappings(pugi::xml_node root) +void xlsx_producer::write_custom_xml_mappings(const relationship &rel, pugi::xml_node root) { - auto map_info_node = root.append_child("MapInfo"); + /*auto map_info_node = */root.append_child("MapInfo"); } -void xlsx_producer::write_dialogsheet(pugi::xml_node root, const relationship &rel) +void xlsx_producer::write_dialogsheet(const relationship &rel, pugi::xml_node root) { - auto dialogsheet_node = root.append_child("dialogsheet"); + /*auto dialogsheet_node = */root.append_child("dialogsheet"); } -void xlsx_producer::write_external_workbook_references(pugi::xml_node root) +void xlsx_producer::write_external_workbook_references(const relationship &rel, pugi::xml_node root) { - auto external_link_node = root.append_child("externalLink"); + /*auto external_link_node = */root.append_child("externalLink"); } -void xlsx_producer::write_metadata(pugi::xml_node root) +void xlsx_producer::write_metadata(const relationship &rel, pugi::xml_node root) { - auto metadata_node = root.append_child("metadata"); + /*auto metadata_node = */root.append_child("metadata"); } -void xlsx_producer::write_pivot_table(pugi::xml_node root) +void xlsx_producer::write_pivot_table(const relationship &rel, pugi::xml_node root) { - auto pivot_table_definition_node = root.append_child("pivotTableDefinition"); + /*auto pivot_table_definition_node = */root.append_child("pivotTableDefinition"); } -void xlsx_producer::write_shared_string_table(pugi::xml_node root) +void xlsx_producer::write_shared_string_table(const relationship &rel, pugi::xml_node root) { auto sst_node = root.append_child("sst"); @@ -997,22 +1015,22 @@ void xlsx_producer::write_shared_string_table(pugi::xml_node root) } } -void xlsx_producer::write_shared_workbook_revision_headers(pugi::xml_node root) +void xlsx_producer::write_shared_workbook_revision_headers(const relationship &rel, pugi::xml_node root) { - auto headers_node = root.append_child("headers"); + /*auto headers_node = */root.append_child("headers"); } -void xlsx_producer::write_shared_workbook(pugi::xml_node root) +void xlsx_producer::write_shared_workbook(const relationship &rel, pugi::xml_node root) { - auto revisions_node = root.append_child("revisions"); + /*auto revisions_node = */root.append_child("revisions"); } -void xlsx_producer::write_shared_workbook_user_data(pugi::xml_node root) +void xlsx_producer::write_shared_workbook_user_data(const relationship &rel, pugi::xml_node root) { - auto users_node = root.append_child("users"); + /*auto users_node = */root.append_child("users"); } -void xlsx_producer::write_styles(pugi::xml_node root) +void xlsx_producer::write_styles(const relationship &rel, pugi::xml_node root) { auto stylesheet_node = root.append_child("styleSheet"); stylesheet_node.append_attribute("xmlns").set_value("http://schemas.openxmlformats.org/spreadsheetml/2006/main"); @@ -1067,7 +1085,7 @@ void xlsx_producer::write_styles(pugi::xml_node root) } } -void xlsx_producer::write_theme(pugi::xml_node root) +void xlsx_producer::write_theme(const relationship &rel, pugi::xml_node root) { auto theme_node = root.append_child("a:theme"); theme_node.append_attribute("xmlns:a").set_value(constants::get_namespace("drawingml").c_str()); @@ -1403,12 +1421,12 @@ void xlsx_producer::write_theme(pugi::xml_node root) theme_node.append_child("a:extraClrSchemeLst"); } -void xlsx_producer::write_volatile_dependencies(pugi::xml_node root) +void xlsx_producer::write_volatile_dependencies(const relationship &rel, pugi::xml_node root) { - auto vol_types_node = root.append_child("volTypes"); + /*auto vol_types_node = */root.append_child("volTypes"); } -void xlsx_producer::write_worksheet(pugi::xml_node root, const relationship &rel) +void xlsx_producer::write_worksheet(const relationship &rel, pugi::xml_node root) { auto worksheet_node = root.append_child("worksheet"); worksheet_node.append_attribute("xmlns").set_value("http://schemas.openxmlformats.org/spreadsheetml/2006/main"); @@ -1702,9 +1720,9 @@ void xlsx_producer::write_worksheet(pugi::xml_node root, const relationship &rel } } - if (!source_.impl().manifest_.get_part_relationships(rel.get_target_uri()).empty()) + if (!source_.impl().manifest_.get_relationships(rel.get_target().get_path()).empty()) { - auto sheet_rels = source_.impl().manifest_.get_part_relationships(rel.get_target_uri()); + auto sheet_rels = source_.impl().manifest_.get_relationships(rel.get_target().get_path()); std::vector hyperlink_sheet_rels; for (const auto &sheet_rel : sheet_rels) @@ -1722,7 +1740,7 @@ void xlsx_producer::write_worksheet(pugi::xml_node root, const relationship &rel for (const auto &hyperlink_rel : hyperlink_sheet_rels) { auto hyperlink_node = hyperlinks_node.append_child("hyperlink"); - hyperlink_node.append_attribute("display").set_value(hyperlink_rel.get_target_uri().to_string('/').c_str()); + hyperlink_node.append_attribute("display").set_value(hyperlink_rel.get_target().get_path().string().c_str()); hyperlink_node.append_attribute("ref").set_value(hyperlink_references.at(hyperlink_rel.get_id()).c_str()); hyperlink_node.append_attribute("r:id").set_value(hyperlink_rel.get_id().c_str()); } @@ -1801,14 +1819,14 @@ void xlsx_producer::write_worksheet(pugi::xml_node root, const relationship &rel // Sheet Relationship Target Parts -void xlsx_producer::write_comments(pugi::xml_node root) +void xlsx_producer::write_comments(const relationship &rel, pugi::xml_node root) { - auto comments_node = root.append_child("comments"); + /*auto comments_node = */root.append_child("comments"); } -void xlsx_producer::write_drawings(pugi::xml_node root) +void xlsx_producer::write_drawings(const relationship &rel, pugi::xml_node root) { - auto ws_dr_node = root.append_child("wsDr"); + /*auto ws_dr_node = */root.append_child("wsDr"); } // Other Parts @@ -1825,5 +1843,12 @@ void xlsx_producer::write_unknown_relationships() { } +void xlsx_producer::write_thumbnail(const relationship &rel) +{ + const auto &thumbnail = source_.get_thumbnail(); + std::string thumbnail_string(thumbnail.begin(), thumbnail.end()); + destination_.write_string(thumbnail_string, rel.get_target().get_path()); +} + } // namespace detail } // namepsace xlnt diff --git a/source/detail/xlsx_producer.hpp b/source/detail/xlsx_producer.hpp index 41622e2d..543478c1 100644 --- a/source/detail/xlsx_producer.hpp +++ b/source/detail/xlsx_producer.hpp @@ -63,38 +63,39 @@ private: // Package Parts void write_manifest(); - void write_core_properties(pugi::xml_node root); - void write_extended_properties(pugi::xml_node root); - void write_custom_properties(pugi::xml_node root); + void write_core_properties(const relationship &rel, pugi::xml_node root); + void write_extended_properties(const relationship &rel, pugi::xml_node root); + void write_custom_properties(const relationship &rel, pugi::xml_node root); + void write_thumbnail(const relationship &rel); // SpreadsheetML-Specific Package Parts - void write_workbook(pugi::xml_node root); + void write_workbook(const relationship &rel, pugi::xml_node root); // Workbook Relationship Target Parts - void write_calculation_chain(pugi::xml_node root); - void write_connections(pugi::xml_node root); - void write_custom_xml_mappings(pugi::xml_node root); - void write_external_workbook_references(pugi::xml_node root); - void write_metadata(pugi::xml_node root); - void write_pivot_table(pugi::xml_node root); - void write_shared_string_table(pugi::xml_node root); - void write_shared_workbook_revision_headers(pugi::xml_node root); - void write_shared_workbook(pugi::xml_node root); - void write_shared_workbook_user_data(pugi::xml_node root); - void write_styles(pugi::xml_node root); - void write_theme(pugi::xml_node root); - void write_volatile_dependencies(pugi::xml_node root); + void write_calculation_chain(const relationship &rel, pugi::xml_node root); + void write_connections(const relationship &rel, pugi::xml_node root); + void write_custom_xml_mappings(const relationship &rel, pugi::xml_node root); + void write_external_workbook_references(const relationship &rel, pugi::xml_node root); + void write_metadata(const relationship &rel, pugi::xml_node root); + void write_pivot_table(const relationship &rel, pugi::xml_node root); + void write_shared_string_table(const relationship &rel, pugi::xml_node root); + void write_shared_workbook_revision_headers(const relationship &rel, pugi::xml_node root); + void write_shared_workbook(const relationship &rel, pugi::xml_node root); + void write_shared_workbook_user_data(const relationship &rel, pugi::xml_node root); + void write_styles(const relationship &rel, pugi::xml_node root); + void write_theme(const relationship &rel, pugi::xml_node root); + void write_volatile_dependencies(const relationship &rel, pugi::xml_node root); - void write_chartsheet(pugi::xml_node root, const relationship &rel); - void write_dialogsheet(pugi::xml_node root, const relationship &rel); - void write_worksheet(pugi::xml_node root, const relationship &rel); + void write_chartsheet(const relationship &rel, pugi::xml_node root); + void write_dialogsheet(const relationship &rel, pugi::xml_node root); + void write_worksheet(const relationship &rel, pugi::xml_node root); // Sheet Relationship Target Parts - void write_comments(pugi::xml_node root); - void write_drawings(pugi::xml_node root); + void write_comments(const relationship &rel, pugi::xml_node root); + void write_drawings(const relationship &rel, pugi::xml_node root); // Other Parts diff --git a/source/packaging/manifest.cpp b/source/packaging/manifest.cpp index cae62231..af260dd3 100644 --- a/source/packaging/manifest.cpp +++ b/source/packaging/manifest.cpp @@ -21,310 +21,169 @@ // // @license: http://www.opensource.org/licenses/mit-license.php // @author: see AUTHORS file + #include +#include #include #include -namespace { - -std::string to_string(xlnt::content_type type) -{ - return ""; -} - -xlnt::content_type from_string(const std::string &type_string) -{ - return xlnt::content_type::unknown; -} - -} - namespace xlnt { void manifest::clear() { - clear_types(); - clear_relationships(); - part_infos_.clear(); + default_content_types_.clear(); + override_content_types_.clear(); + relationships_.clear(); } -void manifest::clear_types() +bool manifest::has_relationship(const path &part, relationship::type type) const { - clear_default_types(); - clear_override_types(); + if (relationships_.find(part) == relationships_.end()) return false; + + for (const auto &rel : relationships_.at(part)) + { + if (rel.second.get_type() == type) return true; + } + + return false; } -void manifest::clear_default_types() +relationship manifest::get_relationship(const path &part, relationship::type type) const { - extension_content_type_map_.clear(); -} - -void manifest::clear_override_types() -{ - for (auto &info : part_infos_) - { - info.second.content_type.clear(); - } -} - -void manifest::clear_relationships() -{ - clear_package_relationships(); - clear_part_relationships(); -} - -void manifest::clear_package_relationships() -{ - package_relationships_.clear(); -} - -void manifest::clear_part_relationships() -{ - for (auto &info : part_infos_) - { - info.second.relationships.clear(); - } -} - -bool manifest::has_package_relationship(relationship::type type) const -{ - for (const auto &rel : package_relationships_) - { - if (rel.get_type() == type) - { - return true; - } - } - - return false; -} - -bool manifest::has_part_relationship(const path &part, relationship::type type) const -{ - for (const auto &rel : part_infos_.at(part).relationships) - { - if (rel.get_type() == type) - { - return true; - } - } - - return false; -} - -relationship manifest::get_package_relationship(relationship::type type) const -{ - for (const auto &rel : package_relationships_) - { - if (rel.get_type() == type) - { - return rel; - } - } + if (relationships_.find(part) == relationships_.end()) throw key_not_found(); + for (const auto &rel : relationships_.at(part)) + { + if (rel.second.get_type() == type) return rel.second; + } + throw key_not_found(); } -std::vector manifest::get_package_relationships(relationship::type type) const +std::vector manifest::get_relationships(const path &part, relationship::type type) const { std::vector matches; - for (const auto &rel : package_relationships_) - { - if (rel.get_type() == type) - { - matches.push_back(rel); - } - } + for (const auto &rel : relationships_.at(part)) + { + if (rel.second.get_type() == type) + { + matches.push_back(rel.second); + } + } return matches; } -relationship manifest::get_part_relationship(const path &part, relationship::type type) const +std::string manifest::get_content_type(const path &part) const { - for (const auto &rel : part_infos_.at(part).relationships) - { - if (rel.get_type() == type) - { - return rel; - } - } - - throw key_not_found(); -} - -std::vector manifest::get_part_relationships(const path &part, relationship::type type) const -{ - std::vector matches; - - for (const auto &rel : part_infos_.at(part).relationships) - { - if (rel.get_type() == type) - { - matches.push_back(rel); - } - } - - return matches; -} - -content_type manifest::get_content_type(const path &part) const -{ - return from_string(get_content_type_string(part)); -} - -std::string manifest::get_content_type_string(const path &part) const -{ - if (part_infos_.find(part) == part_infos_.end()) - { - auto extension = part.extension(); - - if (has_default_type(extension)) - { - return get_default_type(extension); - } - - throw key_not_found(); - } - - return part_infos_.at(part).content_type; + if (has_override_type(part)) + { + return get_override_type(part); + } + + if (has_default_type(part.extension())) + { + return get_default_type(part.extension()); + } + + throw key_not_found(); } void manifest::register_override_type(const path &part, const std::string &content_type) { - part_infos_[part] = { content_type, {} }; + override_content_types_[part] = content_type; } -std::string manifest::register_package_relationship(relationship::type type, const path &target_uri, target_mode mode) -{ - return register_package_relationship(type, target_uri, mode, next_package_relationship_id()); -} - -std::string manifest::register_package_relationship(relationship::type type, const path &target_uri, target_mode mode, const std::string &rel_id) -{ - if (has_package_relationship(rel_id)) - { - throw invalid_parameter(); - } - - relationship rel(rel_id, type, target_uri, mode); - package_relationships_.push_back(rel); - - return rel_id; -} - -bool manifest::has_package_relationship(const std::string &rel_id) const -{ - for (const auto &rel : package_relationships_) - { - if (rel.get_id() == rel_id) - { - return true; - } - } - - return false; -} - -std::vector manifest::get_overriden_parts() const +std::vector manifest::get_parts_with_overriden_types() const { std::vector overriden; - for (const auto &part : part_infos_) + for (const auto &part : override_content_types_) { - if (!part.second.content_type.empty()) - { - overriden.push_back(part.first); - } + overriden.push_back(part.first); } return overriden; } -std::vector manifest::get_package_relationships() const +std::vector manifest::get_relationships(const path &part) const { - return package_relationships_; + if (relationships_.find(part) == relationships_.end()) + { + return {}; + } + + std::vector relationships; + + for (const auto &rel : relationships_.at(part)) + { + relationships.push_back(rel.second); + } + + return relationships; } -relationship manifest::get_package_relationship(const std::string &rel_id) const +relationship manifest::get_relationship(const path &part, const std::string &rel_id) const { - for (const auto rel : package_relationships_) + if (relationships_.find(part) == relationships_.end()) { - if (rel.get_id() == rel_id) + throw key_not_found(); + } + + for (const auto &rel : relationships_.at(part)) + { + if (rel.second.get_id() == rel_id) { - return rel; + return rel.second; } } throw key_not_found(); } -std::vector manifest::get_part_relationships(const path &part) const +std::vector manifest::get_parts() const { - if (part_infos_.find(part) == part_infos_.end()) + std::unordered_set parts; + + for (const auto &part_rels : relationships_) { - throw key_not_found(); + parts.insert(part_rels.first); + + for (const auto &rel : part_rels.second) + { + if (rel.second.get_target_mode() == target_mode::internal) + { + parts.insert(rel.second.get_target().get_path()); + } + } } - return part_infos_.at(part).relationships; + return std::vector(parts.begin(), parts.end()); } -relationship manifest::get_part_relationship(const path &part, const std::string &rel_id) const +std::string manifest::register_relationship(const uri &source, relationship::type type, const uri &target, target_mode mode) { - if (part_infos_.find(part) == part_infos_.end()) - { - throw key_not_found(); - } - - for (const auto &rel : part_infos_.at(part).relationships) - { - if (rel.get_id() == rel_id) - { - return rel; - } - } - - throw key_not_found(); + return register_relationship(source, type, target, mode, next_relationship_id(source.get_path())); } -std::vector manifest::get_parts_with_relationships() const +std::string manifest::register_relationship(const uri &source, relationship::type type, const uri &target, target_mode mode, const std::string &rel_id) { - std::vector with_relationships; - - for (const auto &info : part_infos_) - { - if (!info.second.relationships.empty()) - { - with_relationships.push_back(info.first); - } - } - - return with_relationships; -} - -std::string manifest::register_part_relationship(const path &source_uri, relationship::type type, const path &target_uri, target_mode mode) -{ - return register_part_relationship(source_uri, type, target_uri, mode, next_relationship_id(source_uri)); -} - -std::string manifest::register_part_relationship(const path &source_uri, relationship::type type, const path &target_uri, target_mode mode, const std::string &rel_id) -{ - relationship rel(rel_id, type, path(target_uri), mode); - part_infos_.at(source_uri).relationships.push_back(rel); + relationships_[source.get_path()][rel_id] = relationship(rel_id, type, source, target, mode); return rel_id; } bool manifest::has_default_type(const std::string &extension) const { - return extension_content_type_map_.find(extension) != extension_content_type_map_.end(); + return default_content_types_.find(extension) != default_content_types_.end(); } -std::vector manifest::get_default_type_extensions() const +std::vector manifest::get_extensions_with_default_types() const { std::vector extensions; - for (const auto &extension_type_pair : extension_content_type_map_) + for (const auto &extension_type_pair : default_content_types_) { extensions.push_back(extension_type_pair.first); } @@ -334,54 +193,52 @@ std::vector manifest::get_default_type_extensions() const std::string manifest::get_default_type(const std::string &extension) const { - if (extension_content_type_map_.find(extension) == extension_content_type_map_.end()) + if (default_content_types_.find(extension) == default_content_types_.end()) { throw key_not_found(); } - return extension_content_type_map_.at(extension); + return default_content_types_.at(extension); } void manifest::register_default_type(const std::string &extension, const std::string &content_type) { - extension_content_type_map_[extension] = content_type; + default_content_types_[extension] = content_type; } void manifest::unregister_default_type(const std::string &extension) { - extension_content_type_map_.erase(extension); -} - -std::string manifest::next_package_relationship_id() const -{ - std::string r_id("rId"); - std::size_t index = 1; - - while (std::find_if(package_relationships_.begin(), package_relationships_.end(), - [&](const relationship &r) { return r.get_id() == r_id + std::to_string(index); }) - != package_relationships_.end()) - { - ++index; - } - - return r_id + std::to_string(index); + default_content_types_.erase(extension); } std::string manifest::next_relationship_id(const path &part) const { - std::string r_id("rId"); + if (relationships_.find(part) == relationships_.end()) return "rId1"; + std::size_t index = 1; + const auto &part_rels = relationships_.at(part); - const auto &part_rels = get_part_relationships(part); - - while (std::find_if(part_rels.begin(), part_rels.end(), - [&](const relationship &r) { return r.get_id() == r_id + std::to_string(index); }) - != part_rels.end()) + while (part_rels.find("rId" + std::to_string(index)) != part_rels.end()) { ++index; } - return r_id + std::to_string(index); + return "rId" + std::to_string(index); +} + +bool manifest::has_override_type(const xlnt::path &part) const +{ + return override_content_types_.find(part) != override_content_types_.end(); +} + +std::string manifest::get_override_type(const xlnt::path &part) const +{ + if (!has_override_type(part)) + { + throw key_not_found(); + } + + return override_content_types_.at(part); } } // namespace xlnt diff --git a/source/packaging/relationship.cpp b/source/packaging/relationship.cpp index 5d118cd3..41f296d1 100644 --- a/source/packaging/relationship.cpp +++ b/source/packaging/relationship.cpp @@ -30,7 +30,7 @@ relationship::relationship() { } -relationship::relationship(const std::string &id, type t, const path &source, const path &target, target_mode mode) +relationship::relationship(const std::string &id, type t, const uri &source, const uri &target, target_mode mode) : id_(id), type_(t), source_(source), @@ -44,17 +44,17 @@ std::string relationship::get_id() const return id_; } -target_mode relationship::get_mode() const +target_mode relationship::get_target_mode() const { return mode_; } -path relationship::get_source() const +uri relationship::get_source() const { return source_; } -path relationship::get_target() const +uri relationship::get_target() const { return target_; } @@ -68,7 +68,7 @@ bool relationship::operator==(const relationship &rhs) const { return type_ == rhs.type_ && id_ == rhs.id_ - && source_ == the.source_ + && source_ == rhs.source_ && target_ == rhs.target_ && mode_ == rhs.mode_; } diff --git a/source/packaging/tests/test_zip_file.hpp b/source/packaging/tests/test_zip_file.hpp index 776d9aa5..cd954d78 100644 --- a/source/packaging/tests/test_zip_file.hpp +++ b/source/packaging/tests/test_zip_file.hpp @@ -17,13 +17,13 @@ public: bool files_equal(const xlnt::path &left, const xlnt::path &right) { - if(left.to_string() == right.to_string()) + if(left.string() == right.string()) { return true; } - std::ifstream stream_left(left.to_string(), std::ios::binary); - std::ifstream stream_right(right.to_string(), std::ios::binary); + std::ifstream stream_left(left.string(), std::ios::binary); + std::ifstream stream_right(right.string(), std::ios::binary); while(stream_left && stream_right) { @@ -48,9 +48,9 @@ public: { temporary_file temp; - std::ifstream in_stream(existing_file.to_string(), std::ios::binary); + std::ifstream in_stream(existing_file.string(), std::ios::binary); xlnt::zip_file f(in_stream); - std::ofstream out_stream(temp.get_path().to_string(), std::ios::binary); + std::ofstream out_stream(temp.get_path().string(), std::ios::binary); f.save(out_stream); out_stream.close(); @@ -62,7 +62,7 @@ public: temporary_file temp_file; std::vector source_bytes; - std::ifstream in_stream(existing_file.to_string(), std::ios::binary); + std::ifstream in_stream(existing_file.string(), std::ios::binary); while(in_stream) { @@ -122,7 +122,7 @@ public: { xlnt::zip_file f(existing_file); auto info = f.getinfo(xlnt::path("text.txt")); - TS_ASSERT(info.filename.to_string() == "text.txt"); + TS_ASSERT(info.filename.string() == "text.txt"); } void test_infolist() diff --git a/source/packaging/uri.cpp b/source/packaging/uri.cpp new file mode 100644 index 00000000..c8748756 --- /dev/null +++ b/source/packaging/uri.cpp @@ -0,0 +1,28 @@ +#include + +namespace xlnt { + +uri::uri() +{ +} + +uri::uri(const std::string &uri_string) : path_(uri_string) +{ +} + +std::string uri::to_string() const +{ + return path_.string(); +} + +path uri::get_path() const +{ + return path_; +} + +bool operator==(const uri &left, const uri &right) +{ + return left.to_string() == right.to_string(); +} + +} // namespace xlnt \ No newline at end of file diff --git a/source/packaging/zip_file.cpp b/source/packaging/zip_file.cpp index 0b382886..04c35d72 100644 --- a/source/packaging/zip_file.cpp +++ b/source/packaging/zip_file.cpp @@ -185,11 +185,11 @@ void zip_file::load(std::istream &stream) void zip_file::load(const path &filename) { filename_ = filename; - std::ifstream stream(filename.to_string(), std::ios::binary); + std::ifstream stream(filename.string(), std::ios::binary); if (!stream.good()) { - throw invalid_file(filename.to_string()); + throw invalid_file(filename.string()); } reset(); @@ -197,7 +197,7 @@ void zip_file::load(const path &filename) if (buffer_.empty()) { - throw invalid_file(filename.to_string() + " - empty file"); + throw invalid_file(filename.string() + " - empty file"); } remove_comment(); @@ -220,7 +220,7 @@ void zip_file::load(const std::vector &bytes) void zip_file::save(const path &filename) { filename_ = filename; - std::ofstream stream(filename.to_string(), std::ios::binary); + std::ofstream stream(filename.string(), std::ios::binary); save(stream); } @@ -341,7 +341,7 @@ zip_info zip_file::getinfo(const path &name) start_read(); } - int index = mz_zip_reader_locate_file(archive_.get(), name.to_string('/').c_str(), nullptr, 0); + int index = mz_zip_reader_locate_file(archive_.get(), name.string().c_str(), nullptr, 0); if (index == -1) { @@ -462,19 +462,15 @@ void zip_file::write_file(const path &filename) if (filename.is_absolute()) { - arcname = path(); - bool first = true; + auto split = filename.split(); + + auto iter = split.begin() + 1; + arcname = path(); - for (auto part : filename) - { - if (first) - { - first = false; - continue; - } - - arcname.append(part); - } + while (iter != split.end()) + { + arcname.append(*iter++); + } } write_file(filename, arcname); @@ -482,7 +478,7 @@ void zip_file::write_file(const path &filename) void zip_file::write_file(const path &filename, const path &arcname) { - std::fstream file(filename.to_string(), std::ios::binary | std::ios::in); + std::fstream file(filename.string(), std::ios::binary | std::ios::in); std::stringstream ss; ss << file.rdbuf(); @@ -497,13 +493,13 @@ void zip_file::write_string(const std::string &bytes, const path &arcname) start_write(); } - mz_zip_writer_add_mem(archive_.get(), arcname.to_string('/').c_str(), + mz_zip_writer_add_mem(archive_.get(), arcname.string().c_str(), bytes.data(), bytes.size(), MZ_BEST_COMPRESSION); } void zip_file::write_string(const std::string &bytes, const zip_info &info) { - if (info.filename.to_string().empty() || info.date_time.year < 1980) + if (info.filename.string().empty() || info.date_time.year < 1980) { throw std::runtime_error("must specify a filename and valid date (year >= 1980"); } @@ -515,7 +511,7 @@ void zip_file::write_string(const std::string &bytes, const zip_info &info) auto crc = crc32buf(bytes.c_str(), bytes.size()); - mz_zip_writer_add_mem_ex(archive_.get(), info.filename.to_string('/').c_str(), bytes.data(), bytes.size(), + mz_zip_writer_add_mem_ex(archive_.get(), info.filename.string().c_str(), bytes.data(), bytes.size(), info.comment.c_str(), static_cast(info.comment.size()), MZ_BEST_COMPRESSION, 0, crc); } @@ -524,7 +520,7 @@ std::string zip_file::read(const zip_info &info) { std::size_t size; void *data_raw = mz_zip_reader_extract_file_to_heap(archive_.get(), - info.filename.to_string('/').c_str(), &size, 0); + info.filename.string().c_str(), &size, 0); if (data_raw == nullptr) { @@ -550,7 +546,7 @@ bool zip_file::has_file(const path &name) start_read(); } - int index = mz_zip_reader_locate_file(archive_.get(), name.to_string('/').c_str(), nullptr, 0); + int index = mz_zip_reader_locate_file(archive_.get(), name.string().c_str(), nullptr, 0); return index != -1; } diff --git a/source/styles/color.cpp b/source/styles/color.cpp index 376092e0..96bdc236 100644 --- a/source/styles/color.cpp +++ b/source/styles/color.cpp @@ -172,7 +172,7 @@ std::array rgb_color::decode_hex_string(const std::string &hex_ auto g = static_cast((x >> 8) & 0xff); auto b = static_cast(x & 0xff); - return { r, g, b, a }; + return { {r, g, b, a} }; } rgb_color::rgb_color(const std::string &hex_string) @@ -181,7 +181,7 @@ rgb_color::rgb_color(const std::string &hex_string) } rgb_color::rgb_color(std::uint8_t r, std::uint8_t g, std::uint8_t b, std::uint8_t a) - : rgba_({r, g, b, a}) + : rgba_({{r, g, b, a}}) { } diff --git a/source/utils/path.cpp b/source/utils/path.cpp index c9e092b1..9f71d5dc 100644 --- a/source/utils/path.cpp +++ b/source/utils/path.cpp @@ -46,16 +46,8 @@ char system_separator() bool is_root(const std::string &part) { - return part.size() == 2 && part.back() == ':' - && part.front() >= 'A' && part.front() <= 'Z'; -} - -std::string get_working_directory() -{ - TCHAR buffer[MAX_PATH]; - GetCurrentDirectory(MAX_PATH, buffer); - std::basic_string working_directory(buffer); - return std::string(working_directory.begin(), working_directory.end()); + return part == "/" || (part.size() == 2 && part.back() == ':' + && part.front() >= 'A' && part.front() <= 'Z'); } #else @@ -67,21 +59,7 @@ char system_separator() bool is_root(const std::string &part) { - return part.empty(); -} - -std::string get_working_directory() -{ - std::size_t buffer_size = 100; - std::vector buffer(buffer_size, '\0'); - - while (getcwd(&buffer[0], buffer_size) == nullptr) - { - buffer_size *= 2; - buffer.resize(buffer_size, '\0'); - } - - return std::string(&buffer[0]); + return part == "/"; } #endif @@ -110,23 +88,6 @@ std::vector split_path(const std::string &path, char delim) return split; } -std::vector split_path_universal(const std::string &path) -{ - auto initial = split_path(path, system_separator()); - - if (initial.size() == 1 && system_separator() == '\\') - { - auto alternative = split_path(path, '/'); - - if (alternative.size() > 1) - { - return alternative; - } - } - - return initial; -} - bool file_exists(const std::string &path) { struct stat info; @@ -143,9 +104,9 @@ bool directory_exists(const std::string path) namespace xlnt { -char path::separator() +char path::system_separator() { - return system_separator(); + return ::system_separator(); } path::path() @@ -153,11 +114,7 @@ path::path() } -path::path(const std::string &path_string) : parts_(split_path_universal(path_string)) -{ -} - -path::path(const std::string &path_string, char sep) : parts_(split_path(path_string, sep)) +path::path(const std::string &path_string) : internal_(path_string) { } @@ -170,91 +127,79 @@ bool path::is_relative() const bool path::is_absolute() const { - return !parts_.empty() && ::is_root(parts_.front()); + auto split_path = split(); + return !split_path.empty() && ::is_root(split_path.front()); } bool path::is_root() const { - return parts_.size() == 1 && ::is_root(parts_.front()); + return ::is_root(internal_); } path path::parent() const { - path result; - result.parts_ = parts_; - - if (result.parts_.size() > 1) - { - result.parts_.pop_back(); - } - else - { - return path(""); - } + if (is_root()) return *this; + auto split_path = split(); + + split_path.pop_back(); + + if (split_path.empty()) + { + return path(""); + } + + path result; + + for (const auto &component : split_path) + { + result = result.append(component); + } + return result; } -std::string path::basename() const +std::string path::filename() const { - return parts_.empty() ? "" : parts_.back(); + auto split_path = split(); + return split_path.empty() ? "" : split_path.back(); } std::string path::extension() const { - auto base = basename(); + auto base = filename(); auto last_dot = base.find_last_of('.'); - if (last_dot == std::string::npos) return ""; - - return base.substr(last_dot + 1); + return last_dot == std::string::npos ? "" : base.substr(last_dot + 1); } // conversion -std::string path::to_string(char sep) const +std::string path::string() const { - if (parts_.empty()) return ""; - - std::string result; - - for (const auto &part : parts_) - { - result.append(part); - result.push_back(sep); - } - - result.pop_back(); - - return result; + return internal_; } -path path::make_absolute(const path &base_path) const +std::vector path::split() const +{ + return split_path(internal_, guess_separator()); +} + +path path::resolve(const path &base_path) const { if (is_absolute()) { return *this; } - path copy = base_path; + path copy(base_path.internal_); - for (const auto &part : parts_) + for (const auto &part : split()) { - if (part == ".") - { - continue; - } - else if (part == ".." && copy.parts_.size() > 1) - { - copy.parts_.pop_back(); - } - else - { - copy.parts_.push_back(part); - } + copy = copy.append(part); } - - return copy; + + return copy; } // filesystem attributes @@ -266,120 +211,56 @@ bool path::exists() const bool path::is_directory() const { - return directory_exists(to_string()); + return directory_exists(string()); } bool path::is_file() const { - return file_exists(to_string()); + return file_exists(string()); } // filesystem std::string path::read_contents() const { - std::ifstream f(to_string()); + std::ifstream f(string()); std::ostringstream ss; ss << f.rdbuf(); return ss.str(); } -// mutators - -path &path::append(const std::string &to_append) -{ - parts_.push_back(to_append); - return *this; -} +// append path path::append(const std::string &to_append) const { - path copy(*this); - copy.append(to_append); - - return copy; -} - -path &path::append(const path &to_append) -{ - parts_.insert(parts_.end(), to_append.begin(), to_append.end()); - return *this; + path copy(internal_); + + if (!internal_.empty() && internal_.back() != guess_separator()) + { + copy.internal_.push_back(guess_separator()); + } + + copy.internal_.append(to_append); + + return copy; } path path::append(const path &to_append) const { - path copy(*this); - copy.append(to_append); - - return copy; + return append(to_append.string()); } -// iterators - -path::iterator path::begin() +char path::guess_separator() const { - return parts_.begin(); + if (system_separator() == '/') return '/'; + if (is_absolute()) return internal_.at(2); + return internal_.find('/') == std::string::npos ? '/' : '\\'; } -path::iterator path::end() +bool operator==(const path &left, const path &right) { - return parts_.end(); -} - -path::const_iterator path::begin() const -{ - return cbegin(); -} - -path::const_iterator path::end() const -{ - return cend(); -} - -path::const_iterator path::cbegin() const -{ - return parts_.cbegin(); -} - -path::const_iterator path::cend() const -{ - return parts_.cend(); -} - -path::reverse_iterator path::rbegin() -{ - return parts_.rbegin(); -} - -path::reverse_iterator path::rend() -{ - return parts_.rend(); -} - -path::const_reverse_iterator path::rbegin() const -{ - return crbegin(); -} - -path::const_reverse_iterator path::rend() const -{ - return crend(); -} - -path::const_reverse_iterator path::crbegin() const -{ - return parts_.crbegin(); -} - -path::const_reverse_iterator path::crend() const -{ - return parts_.crend(); -} - -std::string path::to_hash_string() const -{ - return to_string('/'); + return left.internal_ == right.internal_; } } // namespace xlnt diff --git a/source/utils/tests/test_path.hpp b/source/utils/tests/test_path.hpp index 072a0f00..98ffea1f 100644 --- a/source/utils/tests/test_path.hpp +++ b/source/utils/tests/test_path.hpp @@ -20,7 +20,7 @@ public: } TS_ASSERT(!temp.get_path().exists()); - std::ofstream stream(temp.get_path().to_string()); + std::ofstream stream(temp.get_path().string()); TS_ASSERT(temp.get_path().exists()); } }; diff --git a/source/workbook/tests/test_produce_xlsx.hpp b/source/workbook/tests/test_produce_xlsx.hpp index 4a3352e1..c4335606 100644 --- a/source/workbook/tests/test_produce_xlsx.hpp +++ b/source/workbook/tests/test_produce_xlsx.hpp @@ -16,7 +16,7 @@ public: { std::vector buffer; wb.save(buffer); - wb.save(xlnt::path("C:\\Users\\Thomas\\Desktop\\a.xlsx")); + wb.save(xlnt::path("a.xlsx")); xlnt::zip_file wb_archive(buffer); xlnt::zip_file file_archive(file); diff --git a/source/workbook/tests/test_workbook.hpp b/source/workbook/tests/test_workbook.hpp index 34350505..1b874542 100644 --- a/source/workbook/tests/test_workbook.hpp +++ b/source/workbook/tests/test_workbook.hpp @@ -228,8 +228,8 @@ public: xlnt::manifest m; TS_ASSERT(!m.has_default_type("xml")); TS_ASSERT_THROWS(m.get_default_type("xml"), xlnt::key_not_found); - TS_ASSERT(!m.has_package_relationship(xlnt::relationship::type::office_document)); - TS_ASSERT_THROWS(m.get_part_relationships(xlnt::path("xl/workbook.xml")), xlnt::key_not_found); + TS_ASSERT(!m.has_relationship(xlnt::path("/"), xlnt::relationship::type::office_document)); + TS_ASSERT_THROWS(m.get_relationships(xlnt::path("xl/workbook.xml")), xlnt::key_not_found); } void test_memory() diff --git a/source/workbook/workbook.cpp b/source/workbook/workbook.cpp index 6584f735..f44779fb 100644 --- a/source/workbook/workbook.cpp +++ b/source/workbook/workbook.cpp @@ -68,16 +68,16 @@ workbook workbook::minimal() wb.d_->manifest_.register_override_type(path("workbook.xml"), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"); - wb.d_->manifest_.register_package_relationship(relationship::type::office_document, - path("workbook.xml"), target_mode::internal); + wb.d_->manifest_.register_relationship(uri("/"), relationship::type::office_document, + uri("workbook.xml"), target_mode::internal); std::string title("Sheet"); wb.d_->worksheets_.push_back(detail::worksheet_impl(&wb, 1, title)); wb.d_->manifest_.register_override_type(path("sheet1.xml"), "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"); - auto ws_rel = wb.d_->manifest_.register_part_relationship(path("workbook.xml"), - relationship::type::worksheet, path("sheet1.xml"), target_mode::internal); + auto ws_rel = wb.d_->manifest_.register_relationship(uri("workbook.xml"), + relationship::type::worksheet, uri("sheet1.xml"), target_mode::internal); wb.d_->sheet_title_rel_id_map_[title] = ws_rel; return wb; @@ -90,8 +90,8 @@ workbook workbook::empty_excel() wb.d_->manifest_.register_override_type(path("xl/workbook.xml"), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"); - wb.d_->manifest_.register_package_relationship(relationship::type::office_document, - path("xl/workbook.xml"), target_mode::internal); + wb.d_->manifest_.register_relationship(uri("/"), relationship::type::office_document, + uri("xl/workbook.xml"), target_mode::internal); wb.d_->manifest_.register_default_type("rels", "application/vnd.openxmlformats-package.relationships+xml"); @@ -127,13 +127,13 @@ workbook workbook::empty_excel() wb.d_->manifest_.register_override_type(path("docProps/core.xml"), "application/vnd.openxmlformats-package.coreproperties+xml"); - wb.d_->manifest_.register_package_relationship(relationship::type::core_properties, - path("docProps/core.xml"), target_mode::internal); + wb.d_->manifest_.register_relationship(uri("/"), relationship::type::core_properties, + uri("docProps/core.xml"), target_mode::internal); wb.d_->manifest_.register_override_type(path("docProps/app.xml"), "application/vnd.openxmlformats-officedocument.extended-properties+xml"); - wb.d_->manifest_.register_package_relationship(relationship::type::extended_properties, - path("docProps/app.xml"), target_mode::internal); + wb.d_->manifest_.register_relationship(uri("/"), relationship::type::extended_properties, + uri("docProps/app.xml"), target_mode::internal); wb.set_application("Microsoft Excel"); wb.create_sheet(); @@ -278,15 +278,15 @@ worksheet workbook::create_sheet() auto sheet_id = d_->worksheets_.size() + 1; std::string sheet_filename = "sheet" + std::to_string(sheet_id) + ".xml"; - path sheet_path = constants::package_worksheets().append(sheet_filename); d_->worksheets_.push_back(detail::worksheet_impl(this, sheet_id, title)); - auto workbook_rel = d_->manifest_.get_package_relationship(relationship::type::office_document); - d_->manifest_.register_override_type(sheet_path, + auto workbook_rel = d_->manifest_.get_relationship(path("/"), relationship::type::office_document); + uri sheet_uri(constants::package_worksheets().append(sheet_filename).string()); + d_->manifest_.register_override_type(sheet_uri.get_path(), "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"); - auto ws_rel = d_->manifest_.register_part_relationship(workbook_rel.get_target_uri(), - relationship::type::worksheet, sheet_path, target_mode::internal); + auto ws_rel = d_->manifest_.register_relationship(workbook_rel.get_target(), + relationship::type::worksheet, sheet_uri, target_mode::internal); d_->sheet_title_rel_id_map_[title] = ws_rel; return worksheet(&d_->worksheets_.back()); @@ -611,14 +611,14 @@ const theme &workbook::get_theme() const void workbook::set_theme(const theme &value) { - auto workbook_rel = d_->manifest_.get_package_relationship(relationship::type::office_document); + auto workbook_rel = d_->manifest_.get_relationship(path("/"), relationship::type::office_document); - if (!d_->manifest_.has_part_relationship(workbook_rel.get_target_uri(), relationship::type::theme)) + if (!d_->manifest_.has_relationship(workbook_rel.get_target().get_path(), relationship::type::theme)) { d_->manifest_.register_override_type(constants::part_theme(), "application/vnd.openxmlformats-officedocument.theme+xml"); - d_->manifest_.register_part_relationship(workbook_rel.get_target_uri(), - relationship::type::theme, constants::part_theme(), target_mode::internal); + d_->manifest_.register_relationship(workbook_rel.get_target(), + relationship::type::theme, uri(constants::part_theme().string()), target_mode::internal); } d_->has_theme_ = true; @@ -642,14 +642,14 @@ std::vector workbook::get_named_ranges() const std::size_t workbook::add_format(const format &to_add) { - auto workbook_rel = d_->manifest_.get_package_relationship(relationship::type::office_document); + auto workbook_rel = d_->manifest_.get_relationship(path("/"), relationship::type::office_document); - if (!d_->manifest_.has_part_relationship(workbook_rel.get_target_uri(), relationship::type::styles)) + if (!d_->manifest_.has_relationship(workbook_rel.get_target().get_path(), relationship::type::styles)) { d_->manifest_.register_override_type(constants::part_styles(), "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"); - d_->manifest_.register_part_relationship(workbook_rel.get_target_uri(), - relationship::type::styles, constants::part_styles(), target_mode::internal); + d_->manifest_.register_relationship(workbook_rel.get_target(), + relationship::type::styles, uri(constants::part_styles().string()), target_mode::internal); } return d_->stylesheet_.add_format(to_add); @@ -657,14 +657,14 @@ std::size_t workbook::add_format(const format &to_add) std::size_t workbook::add_style(const style &to_add) { - auto workbook_rel = d_->manifest_.get_package_relationship(relationship::type::office_document); + auto workbook_rel = d_->manifest_.get_relationship(path("/"), relationship::type::office_document); - if (!d_->manifest_.has_part_relationship(workbook_rel.get_target_uri(), relationship::type::styles)) + if (!d_->manifest_.has_relationship(workbook_rel.get_target().get_path(), relationship::type::styles)) { d_->manifest_.register_override_type(constants::part_styles(), "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"); - d_->manifest_.register_part_relationship(workbook_rel.get_target_uri(), - relationship::type::styles, constants::part_styles(), target_mode::internal); + d_->manifest_.register_relationship(workbook_rel.get_target(), + relationship::type::styles, uri(constants::part_styles().string()), target_mode::internal); } return d_->stylesheet_.add_style(to_add); @@ -741,14 +741,14 @@ const std::vector &workbook::get_shared_strings() const void workbook::add_shared_string(const text &shared, bool allow_duplicates) { - auto workbook_rel = d_->manifest_.get_package_relationship(relationship::type::office_document); + auto workbook_rel = d_->manifest_.get_relationship(path("/"), relationship::type::office_document); - if (!d_->manifest_.has_part_relationship(workbook_rel.get_target_uri(), relationship::type::shared_string_table)) + if (!d_->manifest_.has_relationship(workbook_rel.get_target().get_path(), relationship::type::styles)) { d_->manifest_.register_override_type(constants::part_shared_strings(), "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"); - d_->manifest_.register_part_relationship(workbook_rel.get_target_uri(), - relationship::type::shared_string_table, constants::part_shared_strings(), target_mode::internal); + d_->manifest_.register_relationship(workbook_rel.get_target(), + relationship::type::shared_string_table, uri(constants::part_shared_strings().string()), target_mode::internal); } if (!allow_duplicates) @@ -776,9 +776,12 @@ bool workbook::contains(const std::string &sheet_title) const void workbook::set_thumbnail(const std::vector &thumbnail, const std::string &extension, const std::string &content_type) { - d_->manifest_.register_default_type(extension, content_type); - d_->manifest_.register_package_relationship(relationship::type::thumbnail, - path("docProps/thumbnail.jpeg"), target_mode::internal); + if (!d_->manifest_.has_relationship(path("/"), relationship::type::thumbnail)) + { + d_->manifest_.register_default_type(extension, content_type); + d_->manifest_.register_relationship(uri("/"), relationship::type::thumbnail, + uri("docProps/thumbnail.jpeg"), target_mode::internal); + } d_->thumbnail_.assign(thumbnail.begin(), thumbnail.end()); } diff --git a/source/worksheet/tests/test_worksheet.hpp b/source/worksheet/tests/test_worksheet.hpp index 7dbb756c..017da9fe 100644 --- a/source/worksheet/tests/test_worksheet.hpp +++ b/source/worksheet/tests/test_worksheet.hpp @@ -29,9 +29,8 @@ public: void test_invalid_cell() { - xlnt::workbook wb; - auto ws = wb.get_active_sheet(); - TS_ASSERT_THROWS(xlnt::cell_reference invalid(xlnt::column_t((xlnt::column_t::index_t)0), 0), xlnt::invalid_cell_reference); + TS_ASSERT_THROWS(xlnt::cell_reference(xlnt::column_t((xlnt::column_t::index_t)0), 0), + xlnt::invalid_cell_reference); } void test_worksheet_dimension() diff --git a/tests/data/xlsx/8_default-excel.xlsx b/tests/data/xlsx/8_default-excel.xlsx index 1fad86ee3870ed56247392b73178d493cc6c83d6..0d118e1d8e17a36b4e7436aad596c830d851c2d4 100644 GIT binary patch delta 699 zcmV;s0!01uqXG7#0kHQ732rMYI7I>g0E)5+cL9G| z-<&KL-SFN@IQicMb|Go+q$%yGptuT;gFl&KbczRevJ4fnKbs}b?>>P%Rsr|tJYEcZ zf8C8_L36y*Rr4ZQTmVcOEiqB}uia9lxM`i;qPFO}xkSfp%QPYkp9!wS&XLZYrm-$sPN~hy&G@Wd+e6q^2 zNuK?9-l6twgRZ$?&@lV4C8vvv!tJ_Peo8@aP4N+kh*jFMkV!!Q)Z-vz%z$$Oi2!z!WeKxI!31veDm zLUOwWn?EGcZr|Rt-D+hDJ|^efU(UItSnOpJeS&pDs}f~NiXu=(tOya2XKa=0SU@GoFy6l_Ij|={W{Pq6Q>mZ zU_z&3+$o#0K^)zCCq`r2wn;nhV}$$}eqB90_f=z|x?M3qRlyly7O2;@S_x*g)3uKt z>sl~~HV$loCys8L4RwAM7GDcR;J1grhR%)QrM}qk9PlVaB}1{3@-n}>UoTNLOS3#q zZ{lpWPUj@c$yNT=<@hn*MISEYG0p#p*8w^qo&Z&W&%R6b1(RV&6tkjCiUSF5D=Iie z0ssJtlQmFI0b7%VP(%VqKa*iU6_ejkDgpVE3Q;-%YLi7#D;x7s8@WjV004Ue000pH h0000000000000007n6%oJ^`1L%26W*T~7c20064?GnxPZ delta 676 zcmV;V0$cs|qXF}y0kHQ737OcfDMA7O0EMy%cL9H1Fw0-d_7B{0(b2(>g1*e&|C^5G zqMO@W2`B%XyDlW{oiwFA6%@DNaquToj85_3PL`oU;%Bqu`Q0aw$133JoX3lS@2|UQ zENG5*x@uk|i?feOqa`M4{vn$WM73R$jp9cfk}URtH6b5EWo-C^EPn#CRSVEQ1dQ%Q z&$B5?B>{hwlFLrRFcd`hNc=~Xl zU}?|X%E#^ts|>(wH>MS*ohGSVmsB|3mb^Xl@VpgO0@ zz_U>rjN@PfPm(sLxY7ss))q{GCys8L4Y6+N z7hgnkV0VYVhR%)trM}qk9I(Jg&4*$qWs}`MtQM$B<75`bH({Es;w(?D@^tpr<@hn* zMISE2G0lI7*HhvNP!;&>yHsD3!3GtxrA&$g37OcfDMA7O0ELq{P)-3@lZQ}50Y#I{ zP(T6plMPWi0cVp)Q7apa?nTc@0RRAd0ssIJ00000000000000002GssQ9c2blg&{h K23k)50000qNj&5L diff --git a/tests/helpers/path_helper.hpp b/tests/helpers/path_helper.hpp index ea55761b..60aaa754 100644 --- a/tests/helpers/path_helper.hpp +++ b/tests/helpers/path_helper.hpp @@ -59,7 +59,7 @@ public: static xlnt::path get_data_directory(const std::string &append = "") { return xlnt::path("../../tests/data") - .make_absolute(get_executable_directory()) + .resolve(get_executable_directory()) .append(xlnt::path(append)); } @@ -70,14 +70,14 @@ public: throw std::runtime_error("destination file already exists and overwrite==false"); } - std::ifstream src(source.to_string(), std::ios::binary); - std::ofstream dst(destination.to_string(), std::ios::binary); + std::ifstream src(source.string(), std::ios::binary); + std::ofstream dst(destination.string(), std::ios::binary); dst << src.rdbuf(); } static void delete_file(const xlnt::path &path) { - std::remove(path.to_string().c_str()); + std::remove(path.string().c_str()); } }; diff --git a/tests/helpers/temporary_file.hpp b/tests/helpers/temporary_file.hpp index a6f693d6..cd7c958a 100644 --- a/tests/helpers/temporary_file.hpp +++ b/tests/helpers/temporary_file.hpp @@ -40,13 +40,13 @@ public: { if(path_.exists()) { - std::remove(path_.to_string().c_str()); + std::remove(path_.string().c_str()); } } ~temporary_file() { - std::remove(path_.to_string().c_str()); + std::remove(path_.string().c_str()); } xlnt::path get_path() const { return path_; } diff --git a/tests/helpers/xml_helper.hpp b/tests/helpers/xml_helper.hpp index 3ffb9d2d..146ea28c 100644 --- a/tests/helpers/xml_helper.hpp +++ b/tests/helpers/xml_helper.hpp @@ -135,23 +135,47 @@ public: auto left_info = left_archive.infolist(); auto right_info = right_archive.infolist(); - if (left_info.size() != right_info.size()) return false; + if (left_info.size() != right_info.size()) + { + std::cout << "left has a different number of files than right" << std::endl; + + std::cout << "left has: "; + for (auto &info : left_info) + { + std::cout << info.filename.string() << ", "; + } + std::cout << std::endl; + + std::cout << "right has: "; + for (auto &info : left_info) + { + std::cout << info.filename.string() << ", "; + } + std::cout << std::endl; + } + + bool match = true; for (auto left_member : left_info) { - if (!right_archive.has_file(left_member)) return false; + if (!right_archive.has_file(left_member)) + { + match = false; + std::cout << "right is missing file: " << left_member.filename.string() << std::endl; + continue; + } auto left_member_contents = left_archive.read(left_member); auto right_member_contents = right_archive.read(left_member.filename); if (!strings_match(left_member_contents, right_member_contents)) { - std::cout << left_member.filename.to_string() << std::endl; - return false; + std::cout << left_member.filename.string() << std::endl; + match = false; } } - return true; + return match; } static bool workbooks_match(const xlnt::workbook &left, const xlnt::workbook &right)