diff --git a/include/xlnt/packaging/manifest.hpp b/include/xlnt/packaging/manifest.hpp index 82ae89c8..5e7659a7 100644 --- a/include/xlnt/packaging/manifest.hpp +++ b/include/xlnt/packaging/manifest.hpp @@ -115,106 +115,82 @@ public: /// 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; + /// /// 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; + /// + /// 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; + 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_part_content_type(const path &part) const; + content_type get_content_type(const path &part) const; /// /// Given the path to a part, returns the content type of the part as a string. /// - std::string get_part_content_type_string(const path &part) const; + std::string get_content_type_string(const path &part) const; - /// - /// Registers a part of the given type at the standard path and creates a - /// relationship with the new part as a target of "source". The type of the - /// relationship is inferred based on the provided types. - /// - void register_part(const path &part, const path &parent, const std::string &content_type, relationship::type relation); - - /// - /// - /// - void register_part(const path &parent, const relationship &rel, const std::string &content_type); - - /// - /// Registers a package part of the given type at the standard path and creates a - /// relationship with the package root as the source. The type of the - /// relationship is inferred based on the provided type. - /// - void register_package_part(const path &part, const std::string &content_type, relationship::type relation); + 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_part(const path &part); + void unregister_override_type(const path &part); /// /// Returns true if the part at the given path has been registered in this manifest. /// - bool has_part(const path &part) const; + bool has_override_type(const path &part) const; - /// - /// Returns the path of every registered part in this manifest. - /// - std::vector get_parts() const; - - /// - /// Returns every registered relationship with the package root as the source. - /// - std::vector get_package_relationships() const; - - /// - /// Returns the package relationship with the given ID. - /// - relationship get_package_relationship(const std::string &rel_id); - - /// - /// Returns every registered relationship with the part of the given type - /// as the source. - /// - std::vector get_part_relationships(const path &part) const; - - /// - /// Returns the registered relationship with the part of the given type - /// as the source and the ID matching the provided ID. - /// - relationship get_part_relationship(const path &part, const std::string &rel_id) const; + std::vector get_overriden_parts() const; /// /// /// - std::string register_external_package_relationship(relationship::type type, const std::string &target_uri); + std::string register_package_relationship(relationship::type type, const path &target_uri, target_mode mode); /// /// /// - std::string register_external_package_relationship(relationship::type type, const std::string &target_uri, const std::string &rel_id); + std::string register_package_relationship(relationship::type type, const path &target_uri, target_mode mode, const std::string &rel_id); /// /// /// - std::string register_external_relationship(const path &source_part, relationship::type type, const std::string &target_uri); + std::string register_part_relationship(const path &source_uri, relationship::type type, const path &target_uri, target_mode mode); /// /// /// - std::string register_external_relationship(const path &source_part, relationship::type type, const std::string &target_uri, const std::string &rel_id); + std::string register_part_relationship(const path &source_uri, relationship::type type, const path &target_uri, target_mode mode, const std::string &rel_id); /// /// Returns true if a default content type for the extension has been registered. @@ -226,6 +202,8 @@ public: /// std::vector get_default_type_extensions() const; + std::vector get_parts_with_relationships() const; + /// /// Returns the registered default content type for parts of the given extension. /// @@ -248,7 +226,7 @@ private: std::vector relationships; }; - std::string next_package_relationship_id() const;\ + std::string next_package_relationship_id() const; std::string next_relationship_id(const path &part) const; diff --git a/include/xlnt/packaging/relationship.hpp b/include/xlnt/packaging/relationship.hpp index e60dc560..05c88e08 100644 --- a/include/xlnt/packaging/relationship.hpp +++ b/include/xlnt/packaging/relationship.hpp @@ -85,7 +85,6 @@ enum class XLNT_CLASS relationship_type styles, table_definition, volatile_dependencies, - workbook, worksheet, hyperlink, diff --git a/include/xlnt/workbook/workbook.hpp b/include/xlnt/workbook/workbook.hpp index b7acd4fd..9858e0f0 100644 --- a/include/xlnt/workbook/workbook.hpp +++ b/include/xlnt/workbook/workbook.hpp @@ -373,10 +373,10 @@ public: // serialization - void save(std::vector &data); - void save(const std::string &filename); - void save(const xlnt::path &filename); - void save(std::ostream &stream); + void save(std::vector &data) const; + void save(const std::string &filename) const; + void save(const xlnt::path &filename) const; + void save(std::ostream &stream) const; void load(const std::vector &data); void load(const std::string &filename); @@ -423,7 +423,8 @@ public: // thumbnail - void set_thumbnail(const std::vector &thumbnail); + void set_thumbnail(const std::vector &thumbnail, + const std::string &extension, const std::string &content_type); const std::vector &get_thumbnail() const; // operators diff --git a/source/cell/cell.cpp b/source/cell/cell.cpp index 39a95e6c..9815afb2 100644 --- a/source/cell/cell.cpp +++ b/source/cell/cell.cpp @@ -77,41 +77,63 @@ std::pair cast_time(const std::string &s) { xlnt::time result; - try + std::vector time_components; + std::size_t prev = 0; + auto colon_index = s.find(':'); + + while (colon_index != std::string::npos) { - auto last_colon = s.find_last_of(':'); - if (last_colon == std::string::npos) return{ false, result }; - double seconds = std::stod(s.substr(last_colon + 1)); - result.second = static_cast(seconds); - result.microsecond = static_cast((seconds - static_cast(result.second)) * 1e6); - - auto first_colon = s.find_first_of(':'); - - if (first_colon == last_colon) - { - auto decimal_pos = s.find('.'); - if (decimal_pos != std::string::npos) - { - result.minute = std::stoi(s.substr(0, first_colon)); - } - else - { - result.hour = std::stoi(s.substr(0, first_colon)); - result.minute = result.second; - result.second = 0; - } - } - else - { - result.hour = std::stoi(s.substr(0, first_colon)); - result.minute = std::stoi(s.substr(first_colon + 1, last_colon - first_colon - 1)); - } + time_components.push_back(s.substr(prev, colon_index - prev)); + prev = colon_index + 1; + colon_index = s.find(':', colon_index + 1); } - catch (std::invalid_argument) + + time_components.push_back(s.substr(prev, colon_index - prev)); + + if (time_components.size() < 2 || time_components.size() > 3) { return{ false, result }; } + std::vector numeric_components; + + for (auto component : time_components) + { + if (component.empty() || (component.substr(0, component.find('.')).size() > 2)) + { + return{ false, result }; + } + + for (auto d : component) + { + if (!(d >= '0' && d <= '9') && d != '.') + { + return{ false, result }; + } + } + + auto without_leading_zero = component.front() == '0' ? component.substr(1) : component; + auto numeric = std::stod(without_leading_zero); + + numeric_components.push_back(numeric); + } + + result.hour = static_cast(numeric_components[0]); + result.minute = static_cast(numeric_components[1]); + + if (result.minute != numeric_components[1]) + { + result.minute = result.hour; + result.hour = 0; + result.second = static_cast(numeric_components[1]); + result.microsecond = static_cast((numeric_components[1] - result.second) * 1E6); + } + else if (numeric_components.size() > 2) + { + result.second = static_cast(numeric_components[2]); + result.microsecond = static_cast((numeric_components[2] - result.second) * 1E6); + } + return{ true, result }; } @@ -453,9 +475,9 @@ void cell::set_hyperlink(const std::string &hyperlink) void cell::set_formula(const std::string &formula) { - if (formula.length() == 0) + if (formula.empty()) { - throw invalid_data_type(); + throw invalid_parameter(); } if (formula[0] == '=') @@ -477,7 +499,7 @@ std::string cell::get_formula() const { if (d_->formula_.empty()) { - throw invalid_data_type(); + throw invalid_attribute(); } return d_->formula_; diff --git a/source/cell/tests/test_cell.hpp b/source/cell/tests/test_cell.hpp index 211ebc47..d625956b 100644 --- a/source/cell/tests/test_cell.hpp +++ b/source/cell/tests/test_cell.hpp @@ -139,8 +139,8 @@ public: auto cell = ws.get_cell(xlnt::cell_reference(1, 1)); TS_ASSERT(!cell.has_formula()); - TS_ASSERT_THROWS(cell.set_formula(""), std::runtime_error); - TS_ASSERT_THROWS(cell.get_formula(), std::runtime_error); + TS_ASSERT_THROWS(cell.set_formula(""), xlnt::invalid_parameter); + TS_ASSERT_THROWS(cell.get_formula(), xlnt::invalid_attribute); cell.set_formula("=42"); TS_ASSERT(cell.has_formula()); TS_ASSERT_EQUALS(cell.get_formula(), "42"); diff --git a/source/detail/stylesheet.hpp b/source/detail/stylesheet.hpp index 820f491c..b2697844 100644 --- a/source/detail/stylesheet.hpp +++ b/source/detail/stylesheet.hpp @@ -37,7 +37,7 @@ namespace { template -std::size_t search_vector(const std::vector &items, const T &to_find) +std::size_t index(const std::vector &items, const T &to_find) { auto match = std::find(items.begin(), items.end(), to_find); @@ -49,6 +49,13 @@ std::size_t search_vector(const std::vector &items, const T &to_find) return std::distance(items.begin(), match); } +template +bool contains(const std::vector &items, const T &to_find) +{ + auto match = std::find(items.begin(), items.end(), to_find); + return match != items.end(); +} + } // namespace namespace xlnt { @@ -58,7 +65,10 @@ struct stylesheet { ~stylesheet() {} - std::size_t index(const format &f) { return search_vector(formats, f); } + std::size_t index(const format &f) + { + return ::index(formats, f); + } std::size_t index(const std::string &style_name) { @@ -72,11 +82,6 @@ struct stylesheet return std::distance(styles.begin(), match); } - - std::size_t index(const border &b) { return search_vector(borders, b); } - std::size_t index(const fill &f) { return search_vector(fills, f); } - std::size_t index(const font &f) { return search_vector(fonts, f); } - std::size_t index(const number_format &f) { return search_vector(number_formats, f); } std::size_t add_format(const format &f) { @@ -97,43 +102,24 @@ struct stylesheet formats.push_back(f); format_styles.push_back("Normal"); - try - { - search_vector(borders, f.get_border()); - } - catch(std::out_of_range) + if (!contains(borders, f.get_border())) { borders.push_back(f.get_border()); } - try - { - search_vector(fills, f.get_fill()); - } - catch(std::out_of_range) - { - fills.push_back(f.get_fill()); - } + if (!contains(fills, f.get_fill())) + { + fills.push_back(f.get_fill()); + } + + if (!contains(fonts, f.get_font())) + { + fonts.push_back(f.get_font()); + } - try + if (f.get_number_format().get_id() >= 164 && !contains(number_formats, f.get_number_format())) { - search_vector(fonts, f.get_font()); - } - catch(std::out_of_range) - { - fonts.push_back(f.get_font()); - } - - if (f.get_number_format().get_id() >= 164) - { - try - { - search_vector(number_formats, f.get_number_format()); - } - catch(std::out_of_range) - { - number_formats.push_back(f.get_number_format()); - } + number_formats.push_back(f.get_number_format()); } return formats.size() - 1; diff --git a/source/detail/workbook_impl.hpp b/source/detail/workbook_impl.hpp index 34f3dcac..7379a1af 100644 --- a/source/detail/workbook_impl.hpp +++ b/source/detail/workbook_impl.hpp @@ -22,8 +22,9 @@ // @author: see AUTHORS file #pragma once -#include #include +#include +#include #include #include @@ -92,7 +93,8 @@ struct workbook_impl links_up_to_date_(other.links_up_to_date_), shared_doc_(other.shared_doc_), hyperlinks_changed_(other.hyperlinks_changed_), - app_version_(other.app_version_) + app_version_(other.app_version_), + sheet_title_rel_id_map_(other.sheet_title_rel_id_map_) { } @@ -131,6 +133,8 @@ struct workbook_impl hyperlinks_changed_ = other.hyperlinks_changed_; app_version_ = other.app_version_; + sheet_title_rel_id_map_ = other.sheet_title_rel_id_map_; + return *this; } @@ -173,6 +177,8 @@ struct workbook_impl bool shared_doc_; bool hyperlinks_changed_; std::string app_version_; + + std::unordered_map sheet_title_rel_id_map_; }; } // namespace detail diff --git a/source/detail/xlsx_consumer.cpp b/source/detail/xlsx_consumer.cpp index 7bb2087c..45c9165b 100644 --- a/source/detail/xlsx_consumer.cpp +++ b/source/detail/xlsx_consumer.cpp @@ -708,18 +708,21 @@ xlsx_consumer::xlsx_consumer(workbook &destination) : destination_(destination) void xlsx_consumer::read(const path &source) { + destination_.clear(); source_.load(source); populate_workbook(); } void xlsx_consumer::read(std::istream &source) { + destination_.clear(); source_.load(source); populate_workbook(); } void xlsx_consumer::read(const std::vector &source) { + destination_.clear(); source_.load(source); populate_workbook(); } @@ -729,10 +732,8 @@ void xlsx_consumer::read(const std::vector &source) void xlsx_consumer::populate_workbook() { auto &manifest = destination_.get_manifest(); - manifest.clear(); - destination_.d_->worksheets_.clear(); - read_manifest(); + path workbook_part; for (const auto &rel : manifest.get_package_relationships()) { @@ -751,13 +752,14 @@ void xlsx_consumer::populate_workbook() read_custom_property(document.root()); break; case relationship::type::office_document: - check_document_type(manifest.get_part_content_type_string(rel.get_target_uri())); + check_document_type(manifest.get_content_type_string(rel.get_target_uri())); + workbook_part = rel.get_target_uri(); read_workbook(document.root()); break; } } - for (const auto &rel : manifest.get_part_relationships(constants::part_workbook())) + 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()); @@ -767,18 +769,12 @@ void xlsx_consumer::populate_workbook() case relationship::type::calculation_chain: read_calculation_chain(document.root()); break; - case relationship::type::chartsheet: - read_chartsheet(document.root(), rel.get_id()); - break; case relationship::type::connections: read_connections(document.root()); break; case relationship::type::custom_xml_mappings: read_custom_xml_mappings(document.root()); break; - case relationship::type::dialogsheet: - read_dialogsheet(document.root(), rel.get_id()); - break; case relationship::type::external_workbook_references: read_external_workbook_references(document.root()); break; @@ -795,7 +791,7 @@ void xlsx_consumer::populate_workbook() read_shared_workbook_revision_headers(document.root()); break; case relationship::type::styles: - read_styles(document.root()); + read_stylesheet(document.root()); break; case relationship::type::theme: read_theme(document.root()); @@ -803,6 +799,22 @@ void xlsx_consumer::populate_workbook() case relationship::type::volatile_dependencies: read_volatile_dependencies(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::chartsheet: + read_chartsheet(document.root(), rel.get_id()); + break; + case relationship::type::dialogsheet: + read_dialogsheet(document.root(), rel.get_id()); + break; case relationship::type::worksheet: read_worksheet(document.root(), rel.get_id()); break; @@ -818,38 +830,15 @@ void xlsx_consumer::populate_workbook() void read_unknown_parts(); void read_unknown_relationships(); - - auto current_iter = destination_.d_->worksheets_.begin(); - for (std::size_t current_index = 0; current_index < destination_.get_sheet_titles().size(); ++current_index) - { - auto current_id = current_iter->id_; - - for (const auto j : worksheet_rel_title_id_index_map_) - { - if (std::get<1>(j.second) == current_id) - { - if (std::get<2>(j.second) == current_index) break; - - auto switch_iter = destination_.d_->worksheets_.begin(); - - for (std::size_t k = 0; k < std::get<2>(j.second); ++k) - { - ++switch_iter; - } - - std::swap(*switch_iter, *current_iter); - } - } - - ++current_iter; - } } // Package Parts void xlsx_consumer::read_manifest() { - auto package_rels = read_relationships(path("_rels/.rels"), source_); + path package_rels_path("_rels/.rels"); + if (!source_.has_file(package_rels_path)) throw invalid_file("missing package rels"); + auto package_rels = read_relationships(package_rels_path, source_); pugi::xml_document document; document.load_string(source_.read(path("[Content_Types].xml")).c_str()); @@ -857,8 +846,6 @@ void xlsx_consumer::read_manifest() const auto root_node = document.child("Types"); auto &manifest = destination_.get_manifest(); - std::unordered_map> part_types; - auto make_relative = [](const path &p) { path copy; @@ -881,30 +868,32 @@ void xlsx_consumer::read_manifest() else if (child.name() == std::string("Override")) { path part(child.attribute("PartName").value()); - part_types[make_relative(part)] = child.attribute("ContentType").value(); + manifest.register_override_type(part, child.attribute("ContentType").value()); } } for (const auto &package_rel : package_rels) { - manifest.register_package_part(package_rel.get_target_uri(), - part_types.at(package_rel.get_target_uri()), package_rel.get_type()); + manifest.register_package_relationship(package_rel.get_type(), + package_rel.get_target_uri(), + package_rel.get_target_mode(), + package_rel.get_id()); } - for (const auto &relationship_source : part_types) + for (const auto &relationship_source : source_.infolist()) { - auto rels_name = relationship_source.first.parent() - .append("_rels") - .append(relationship_source.first.basename() + ".rels"); - auto part_rels = read_relationships(rels_name, source_); + if (relationship_source.filename == path("_rels/.rels") + || relationship_source.filename.extension() != ".rels") continue; - if (part_rels.empty()) continue; + auto part_rels = read_relationships(relationship_source.filename, source_); + auto source_uri = relationship_source.filename.parent().parent() + .append(relationship_source.filename.basename()); for (const auto part_rel : part_rels) { - auto part = relationship_source.first.parent().append(part_rel.get_target_uri()); - relationship new_part_rel(part_rel.get_id(), part_rel.get_type(), part, target_mode::internal); - manifest.register_part(relationship_source.first, new_part_rel, part_types.at(part)); + 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()); } } } @@ -965,14 +954,16 @@ void xlsx_consumer::read_workbook(const pugi::xml_node root) } } + std::size_t index = 0; for (auto sheet_node : workbook_node.child("sheets").children("sheet")) { std::string rel_id(sheet_node.attribute("r:id").value()); std::string title(sheet_node.attribute("name").value()); auto id = string_to_size_t(sheet_node.attribute("sheetId").value()); - worksheet_rel_title_id_index_map_[rel_id] = - { title, id, worksheet_rel_title_id_index_map_.size() }; + sheet_title_id_map_[rel_id] = id; + sheet_title_index_map_[rel_id] = index++; + destination_.d_->sheet_title_rel_id_map_[title] = rel_id; } } @@ -1135,21 +1126,18 @@ void xlsx_consumer::read_shared_workbook_user_data(const pugi::xml_node root) auto root_node = document.append_child("users"); } -void xlsx_consumer::read_styles(const pugi::xml_node root) +void xlsx_consumer::read_stylesheet(const pugi::xml_node root) { - pugi::xml_document document; - - auto stylesheet_node = document.child("styleSheet"); - + auto stylesheet_node = root.child("styleSheet"); auto &stylesheet = destination_.impl().stylesheet_; - ::read_borders(stylesheet_node.child("borders"), stylesheet.borders); - ::read_fills(stylesheet_node.child("fills"), stylesheet.fills); - ::read_fonts(stylesheet_node.child("fonts"), stylesheet.fonts); - ::read_number_formats(stylesheet_node.child("numFmts"), stylesheet.number_formats); - ::read_colors(stylesheet_node.child("colors"), stylesheet.colors); - ::read_styles(stylesheet_node.child("cellStyles"), stylesheet_node.child("cellStyleXfs"), stylesheet, stylesheet.styles, stylesheet.style_name_map); - ::read_formats(stylesheet_node.child("cellXfs"), stylesheet, stylesheet.formats, stylesheet.format_styles); + read_borders(stylesheet_node.child("borders"), stylesheet.borders); + read_fills(stylesheet_node.child("fills"), stylesheet.fills); + read_fonts(stylesheet_node.child("fonts"), stylesheet.fonts); + read_number_formats(stylesheet_node.child("numFmts"), stylesheet.number_formats); + read_colors(stylesheet_node.child("colors"), stylesheet.colors); + read_styles(stylesheet_node.child("cellStyles"), stylesheet_node.child("cellStyleXfs"), stylesheet, stylesheet.styles, stylesheet.style_name_map); + read_formats(stylesheet_node.child("cellXfs"), stylesheet, stylesheet.formats, stylesheet.format_styles); } void xlsx_consumer::read_theme(const pugi::xml_node root) @@ -1167,10 +1155,24 @@ void xlsx_consumer::read_volatile_dependencies(const pugi::xml_node root) void xlsx_consumer::read_worksheet(const pugi::xml_node root, const std::string &rel_id) { - auto title = std::get<0>(worksheet_rel_title_id_index_map_[rel_id]); - auto id = std::get<1>(worksheet_rel_title_id_index_map_[rel_id]); + auto title = std::find_if(destination_.d_->sheet_title_rel_id_map_.begin(), + destination_.d_->sheet_title_rel_id_map_.end(), + [&](const std::pair &p) + { + return p.second == rel_id; + })->first; - destination_.d_->worksheets_.emplace_back(&destination_, id, title); + auto id = sheet_title_id_map_[title]; + auto index = sheet_title_index_map_[title]; + + auto insertion_iter = destination_.d_->worksheets_.begin(); + while (insertion_iter != destination_.d_->worksheets_.end() + && sheet_title_index_map_[insertion_iter->title_] < index) + { + ++insertion_iter; + } + + destination_.d_->worksheets_.emplace(insertion_iter, &destination_, id, title); auto ws = destination_.get_sheet_by_id(id); diff --git a/source/detail/xlsx_consumer.hpp b/source/detail/xlsx_consumer.hpp index a6bd31ee..bd8c4adf 100644 --- a/source/detail/xlsx_consumer.hpp +++ b/source/detail/xlsx_consumer.hpp @@ -86,7 +86,7 @@ private: void read_shared_workbook_revision_headers(const pugi::xml_node root); void read_shared_workbook(const pugi::xml_node root); void read_shared_workbook_user_data(const pugi::xml_node root); - void read_styles(const pugi::xml_node root); + void read_stylesheet(const pugi::xml_node root); void read_theme(const pugi::xml_node root); void read_volatile_dependencies(const pugi::xml_node root); @@ -110,11 +110,8 @@ private: /// zip_file source_; - /// - /// Worksheet titles from workbook.xml are saved here so we know what its - /// title is when readind the associated worksheet XML. - /// - std::unordered_map> worksheet_rel_title_id_index_map_; + std::unordered_map sheet_title_id_map_; + std::unordered_map sheet_title_index_map_; /// /// A reference to the workbook which is being read. diff --git a/source/detail/xlsx_producer.cpp b/source/detail/xlsx_producer.cpp index 0b72328f..05f90532 100644 --- a/source/detail/xlsx_producer.cpp +++ b/source/detail/xlsx_producer.cpp @@ -180,14 +180,14 @@ std::string to_string(xlnt::horizontal_alignment horizontal_alignment) } } -void write_relationships(const std::vector &relationships, pugi::xml_document &document) +void write_relationships(const std::vector &relationships, pugi::xml_node root) { - auto root_node = document.append_child("Relationships"); - root_node.append_attribute("xmlns").set_value(xlnt::constants::get_namespace("relationships").c_str()); + auto relationships_node = root.append_child("Relationships"); + relationships_node.append_attribute("xmlns").set_value(xlnt::constants::get_namespace("relationships").c_str()); for (const auto &relationship : relationships) { - auto relationship_node = root_node.append_child("Relationship"); + auto relationship_node = relationships_node.append_child("Relationship"); 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()); @@ -565,104 +565,113 @@ xlsx_producer::xlsx_producer(const workbook &target) : source_(target) void xlsx_producer::write(const path &destination) { - xlnt::zip_file archive; - populate_archive(destination_); - archive.save(destination); + populate_archive(); + destination_.save(destination); } void xlsx_producer::write(std::ostream &destination) { - xlnt::zip_file archive; - populate_archive(destination_); - archive.save(destination); + populate_archive(); + destination_.save(destination); } void xlsx_producer::write(std::vector &destination) { - xlnt::zip_file archive; - populate_archive(destination_); - archive.save(destination); + populate_archive(); + destination_.save(destination); } // Part Writing Methods -void xlsx_producer::populate_archive(zip_file &destination_) +void xlsx_producer::populate_archive() { - write_package_relationships(); - write_content_types(); - write_workbook(); - write_workbook_relationships(); + write_manifest(); + path workbook_part; for (auto &rel : source_.impl().manifest_.get_package_relationships()) { + pugi::xml_document document; + switch (rel.get_type()) { case relationship::type::core_properties: - write_core_properties(); + write_core_properties(document.root()); break; case relationship::type::extended_properties: - write_app_properties(); + write_extended_properties(document.root()); break; case relationship::type::custom_properties: - write_custom_property(); + write_custom_properties(document.root()); break; + case relationship::type::office_document: + write_workbook(document.root()); + workbook_part = rel.get_target_uri(); + break; + case relationship::type::thumbnail: + destination_.write_string( + std::string(source_.get_thumbnail().begin(), source_.get_thumbnail().end()), + rel.get_target_uri()); + continue; } + + write_document_to_archive(document, rel.get_target_uri(), destination_); } - for (auto rel : source_.impl().manifest_.get_part_relationships(constants::part_workbook())) + 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(); + write_calculation_chain(document.root()); break; case relationship::type::chartsheet: - write_chartsheet(rel); + write_chartsheet(document.root(), rel); break; case relationship::type::connections: - write_connections(); + write_connections(document.root()); break; case relationship::type::custom_xml_mappings: - write_custom_xml_mappings(); + write_custom_xml_mappings(document.root()); break; case relationship::type::dialogsheet: - write_dialogsheet(rel); + write_dialogsheet(document.root(), rel); break; case relationship::type::external_workbook_references: - write_external_workbook_references(); + write_external_workbook_references(document.root()); break; case relationship::type::metadata: - write_metadata(); + write_metadata(document.root()); break; case relationship::type::pivot_table: - write_pivot_table(); + write_pivot_table(document.root()); break; case relationship::type::shared_string_table: - write_shared_string_table(); + write_shared_string_table(document.root()); break; case relationship::type::shared_workbook_revision_headers: - write_shared_workbook_revision_headers(); + write_shared_workbook_revision_headers(document.root()); break; case relationship::type::styles: - write_styles(); + write_styles(document.root()); break; case relationship::type::theme: - write_theme(); + write_theme(document.root()); break; case relationship::type::volatile_dependencies: - write_volatile_dependencies(); + write_volatile_dependencies(document.root()); break; case relationship::type::worksheet: - write_worksheet(rel); + write_worksheet(document.root(), rel); break; } + + std::ostringstream document_stream; + document.save(document_stream); + destination_.write_string(document_stream.str(), rel.get_target_uri()); } - // Sheet Relationship Target Parts - - void write_comments(); - void write_drawings(); - // Unknown Parts void write_unknown_parts(); @@ -671,30 +680,11 @@ void xlsx_producer::populate_archive(zip_file &destination_) // Package Parts -void xlsx_producer::write_package_relationships() +void xlsx_producer::write_manifest() { - pugi::xml_document document; + pugi::xml_document content_types_document; - auto relationships_node = document.append_child("Relationships"); - relationships_node.append_attribute("xmlns").set_value("http://schemas.openxmlformats.org/package/2006/relationships"); - - for (const auto &relationship : source_.impl().manifest_.get_package_relationships()) - { - auto relationship_node = relationships_node.append_child("Relationship"); - - 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()); - } - - write_document_to_archive(document, xlnt::constants::part_root_relationships(), destination_); -} - -void xlsx_producer::write_content_types() -{ - pugi::xml_document document; - - auto types_node = document.append_child("Types"); + 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()) @@ -705,44 +695,57 @@ void xlsx_producer::write_content_types() default_node.append_attribute("ContentType").set_value(content_type.c_str()); } - for (const auto &part : source_.get_manifest().get_parts()) + for (const auto &part : source_.get_manifest().get_overriden_parts()) { 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_part_content_type_string(part); + 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("ContentType").set_value(content_type.c_str()); } - write_document_to_archive(document, xlnt::constants::part_content_types(), destination_); + for (const auto &part : source_.get_manifest().get_parts_with_relationships()) + { + auto part_rels = source_.get_manifest().get_part_relationships(part); + + 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"); + 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_app_properties() +void xlsx_producer::write_extended_properties(pugi::xml_node root) { - pugi::xml_document document; - auto root_node = document.append_child("Properties"); + auto properties_node = root.append_child("Properties"); - root_node.append_attribute("xmlns").set_value("http://schemas.openxmlformats.org/officeDocument/2006/extended-properties"); - root_node.append_attribute("xmlns:vt").set_value("http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"); + properties_node.append_attribute("xmlns").set_value("http://schemas.openxmlformats.org/officeDocument/2006/extended-properties"); + properties_node.append_attribute("xmlns:vt").set_value("http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"); - root_node.append_child("Application").text().set(source_.get_application().c_str()); - root_node.append_child("DocSecurity").text().set(std::to_string(source_.get_doc_security()).c_str()); - root_node.append_child("ScaleCrop").text().set(source_.get_scale_crop() ? "true" : "false"); + properties_node.append_child("Application").text().set(source_.get_application().c_str()); + properties_node.append_child("DocSecurity").text().set(std::to_string(source_.get_doc_security()).c_str()); + properties_node.append_child("ScaleCrop").text().set(source_.get_scale_crop() ? "true" : "false"); - auto company_node = root_node.append_child("Company"); + auto company_node = properties_node.append_child("Company"); if (!source_.get_company().empty()) { company_node.text().set(source_.get_company().c_str()); } - root_node.append_child("LinksUpToDate").text().set(source_.links_up_to_date() ? "true" : "false"); - root_node.append_child("SharedDoc").text().set(source_.is_shared_doc() ? "true" : "false"); - root_node.append_child("HyperlinksChanged").text().set(source_.hyperlinks_changed() ? "true" : "false"); - root_node.append_child("AppVersion").text().set(source_.get_app_version().c_str()); + properties_node.append_child("LinksUpToDate").text().set(source_.links_up_to_date() ? "true" : "false"); + properties_node.append_child("SharedDoc").text().set(source_.is_shared_doc() ? "true" : "false"); + properties_node.append_child("HyperlinksChanged").text().set(source_.hyperlinks_changed() ? "true" : "false"); + properties_node.append_child("AppVersion").text().set(source_.get_app_version().c_str()); // TODO what is this stuff? - auto heading_pairs_node = root_node.append_child("HeadingPairs"); + auto heading_pairs_node = properties_node.append_child("HeadingPairs"); auto heading_pairs_vector_node = heading_pairs_node.append_child("vt:vector"); heading_pairs_vector_node.append_attribute("size").set_value("2"); heading_pairs_vector_node.append_attribute("baseType").set_value("variant"); @@ -751,7 +754,7 @@ void xlsx_producer::write_app_properties() .append_child("vt:i4") .text().set(std::to_string(source_.get_sheet_titles().size()).c_str()); - auto titles_of_parts_node = root_node.append_child("TitlesOfParts"); + auto titles_of_parts_node = properties_node.append_child("TitlesOfParts"); auto titles_of_parts_vector_node = titles_of_parts_node.append_child("vt:vector"); titles_of_parts_vector_node.append_attribute("size").set_value(std::to_string(source_.get_sheet_titles().size()).c_str()); titles_of_parts_vector_node.append_attribute("baseType").set_value("lpstr"); @@ -760,49 +763,40 @@ void xlsx_producer::write_app_properties() { titles_of_parts_vector_node.append_child("vt:lpstr").text().set(ws.get_title().c_str()); } - - write_document_to_archive(document, xlnt::constants::part_app(), destination_); } -void xlsx_producer::write_core_properties() +void xlsx_producer::write_core_properties(pugi::xml_node root) { - pugi::xml_document document; - auto root_node = document.append_child("cp:coreProperties"); + auto core_properties_node = root.append_child("cp:coreProperties"); - root_node.append_attribute("xmlns:cp").set_value("http://schemas.openxmlformats.org/package/2006/metadata/core-properties"); - root_node.append_attribute("xmlns:dc").set_value("http://purl.org/dc/elements/1.1/"); - root_node.append_attribute("xmlns:dcmitype").set_value("http://purl.org/dc/dcmitype/"); - root_node.append_attribute("xmlns:dcterms").set_value("http://purl.org/dc/terms/"); - root_node.append_attribute("xmlns:xsi").set_value("http://www.w3.org/2001/XMLSchema-instance"); + core_properties_node.append_attribute("xmlns:cp").set_value("http://schemas.openxmlformats.org/package/2006/metadata/core-properties"); + core_properties_node.append_attribute("xmlns:dc").set_value("http://purl.org/dc/elements/1.1/"); + core_properties_node.append_attribute("xmlns:dcmitype").set_value("http://purl.org/dc/dcmitype/"); + core_properties_node.append_attribute("xmlns:dcterms").set_value("http://purl.org/dc/terms/"); + core_properties_node.append_attribute("xmlns:xsi").set_value("http://www.w3.org/2001/XMLSchema-instance"); - root_node.append_child("dc:creator").text().set(source_.get_creator().c_str()); - root_node.append_child("cp:lastModifiedBy").text().set(source_.get_last_modified_by().c_str()); - root_node.append_child("dcterms:created").text().set(datetime_to_w3cdtf(source_.get_created()).c_str()); - root_node.child("dcterms:created").append_attribute("xsi:type").set_value("dcterms:W3CDTF"); - root_node.append_child("dcterms:modified").text().set(datetime_to_w3cdtf(source_.get_modified()).c_str()); - root_node.child("dcterms:modified").append_attribute("xsi:type").set_value("dcterms:W3CDTF"); - root_node.append_child("dc:title").text().set(source_.get_title().c_str()); - root_node.append_child("dc:description"); - root_node.append_child("dc:subject"); - root_node.append_child("cp:keywords"); - root_node.append_child("cp:category"); - - write_document_to_archive(document, xlnt::constants::part_core(), destination_); + core_properties_node.append_child("dc:creator").text().set(source_.get_creator().c_str()); + core_properties_node.append_child("cp:lastModifiedBy").text().set(source_.get_last_modified_by().c_str()); + core_properties_node.append_child("dcterms:created").text().set(datetime_to_w3cdtf(source_.get_created()).c_str()); + core_properties_node.child("dcterms:created").append_attribute("xsi:type").set_value("dcterms:W3CDTF"); + core_properties_node.append_child("dcterms:modified").text().set(datetime_to_w3cdtf(source_.get_modified()).c_str()); + core_properties_node.child("dcterms:modified").append_attribute("xsi:type").set_value("dcterms:W3CDTF"); + core_properties_node.append_child("dc:title").text().set(source_.get_title().c_str()); + core_properties_node.append_child("dc:description"); + core_properties_node.append_child("dc:subject"); + core_properties_node.append_child("cp:keywords"); + core_properties_node.append_child("cp:category"); } -void xlsx_producer::write_custom_file_properties() +void xlsx_producer::write_custom_properties(pugi::xml_node root) { - pugi::xml_document document; - auto root_node = document.append_child("customFileProperties"); - write_document_to_archive(document, xlnt::constants::part_core(), destination_); + auto properties_node = root.append_child("Properties"); } // Write SpreadsheetML-Specific Package Parts -void xlsx_producer::write_workbook() +void xlsx_producer::write_workbook(pugi::xml_node root) { - pugi::xml_document document; - std::size_t num_visible = 0; for (auto ws : source_) @@ -818,23 +812,23 @@ void xlsx_producer::write_workbook() throw xlnt::no_visible_worksheets(); } - auto root_node = document.append_child("workbook"); + auto workbook_node = root.append_child("workbook"); - root_node.append_attribute("xmlns").set_value("http://schemas.openxmlformats.org/spreadsheetml/2006/main"); - root_node.append_attribute("xmlns:r").set_value("http://schemas.openxmlformats.org/officeDocument/2006/relationships"); + workbook_node.append_attribute("xmlns").set_value("http://schemas.openxmlformats.org/spreadsheetml/2006/main"); + workbook_node.append_attribute("xmlns:r").set_value("http://schemas.openxmlformats.org/officeDocument/2006/relationships"); - auto file_version_node = root_node.append_child("fileVersion"); + auto file_version_node = workbook_node.append_child("fileVersion"); file_version_node.append_attribute("appName").set_value("xl"); file_version_node.append_attribute("lastEdited").set_value("4"); file_version_node.append_attribute("lowestEdited").set_value("4"); file_version_node.append_attribute("rupBuild").set_value("4505"); - auto workbook_pr_node = root_node.append_child("workbookPr"); + auto workbook_pr_node = workbook_node.append_child("workbookPr"); workbook_pr_node.append_attribute("codeName").set_value("ThisWorkbook"); workbook_pr_node.append_attribute("defaultThemeVersion").set_value("124226"); workbook_pr_node.append_attribute("date1904").set_value(source_.get_base_date() == calendar::mac_1904 ? "1" : "0"); - auto book_views_node = root_node.append_child("bookViews"); + auto book_views_node = workbook_node.append_child("bookViews"); auto workbook_view_node = book_views_node.append_child("workbookView"); workbook_view_node.append_attribute("activeTab").set_value("0"); workbook_view_node.append_attribute("autoFilterDateGrouping").set_value("1"); @@ -846,45 +840,41 @@ void xlsx_producer::write_workbook() workbook_view_node.append_attribute("tabRatio").set_value("600"); workbook_view_node.append_attribute("visibility").set_value("visible"); - auto sheets_node = root_node.append_child("sheets"); - auto defined_names_node = root_node.append_child("definedNames"); + 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); + for (const auto ws : source_) { - path target("worksheets/sheet" + std::to_string(index++) + ".xml"); + 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); - for (const auto &rel : source_.impl().manifest_.get_part_relationships(constants::part_workbook())) + auto sheet_node = sheets_node.append_child("sheet"); + sheet_node.append_attribute("name").set_value(ws.get_title().c_str()); + sheet_node.append_attribute("sheetId").set_value(std::to_string(ws.get_id()).c_str()); + + if (ws.get_sheet_state() == xlnt::sheet_state::hidden) { - if (rel.get_target_uri().to_string('/') != target.to_string('/')) continue; + sheet_node.append_attribute("state").set_value("hidden"); + } - auto sheet_node = sheets_node.append_child("sheet"); - sheet_node.append_attribute("name").set_value(ws.get_title().c_str()); - sheet_node.append_attribute("sheetId").set_value(std::to_string(ws.get_id()).c_str()); + sheet_node.append_attribute("r:id").set_value(sheet_rel_id.c_str()); - if (ws.get_sheet_state() == xlnt::sheet_state::hidden) - { - sheet_node.append_attribute("state").set_value("hidden"); - } - - sheet_node.append_attribute("r:id").set_value(rel.get_id().c_str()); - - if (ws.has_auto_filter()) - { - auto defined_name_node = defined_names_node.append_child("definedName"); - defined_name_node.append_attribute("name").set_value("_xlnm._FilterDatabase"); - defined_name_node.append_attribute("hidden").set_value("1"); - defined_name_node.append_attribute("localSheetId").set_value("0"); - std::string name = - "'" + ws.get_title() + "'!" + range_reference::make_absolute(ws.get_auto_filter()).to_string(); - defined_name_node.text().set(name.c_str()); - } - - break; + if (ws.has_auto_filter()) + { + auto defined_name_node = defined_names_node.append_child("definedName"); + defined_name_node.append_attribute("name").set_value("_xlnm._FilterDatabase"); + defined_name_node.append_attribute("hidden").set_value("1"); + defined_name_node.append_attribute("localSheetId").set_value("0"); + std::string name = + "'" + ws.get_title() + "'!" + range_reference::make_absolute(ws.get_auto_filter()).to_string(); + defined_name_node.text().set(name.c_str()); } } - auto calc_pr_node = root_node.append_child("calcPr"); + auto calc_pr_node = workbook_node.append_child("calcPr"); calc_pr_node.append_attribute("calcId").set_value("124519"); calc_pr_node.append_attribute("calcMode").set_value("auto"); calc_pr_node.append_attribute("fullCalcOnLoad").set_value("1"); @@ -903,110 +893,68 @@ void xlsx_producer::write_workbook() defined_name_node.text().set(target_string.c_str()); } */ - - write_document_to_archive(document, xlnt::path("workbook.xml"), destination_); -} - -void xlsx_producer::write_workbook_relationships() -{ - pugi::xml_document document; - - auto relationships_node = document.append_child("Relationships"); - relationships_node.append_attribute("xmlns").set_value("http://schemas.openxmlformats.org/package/2006/relationships"); - - auto relationship_node = relationships_node.append_child("Relationship"); - relationship_node.append_attribute("Id").set_value("rId1"); - relationship_node.append_attribute("Type").set_value("http://purl.oclc.org/ooxml/officeDocument/relationships/worksheet"); - relationship_node.append_attribute("Target").set_value("sheet1.xml"); - - write_document_to_archive(document, xlnt::path("_rels/workbook.xml.rels"), destination_); } // Write Workbook Relationship Target Parts -void xlsx_producer::write_calculation_chain() +void xlsx_producer::write_calculation_chain(pugi::xml_node root) { - pugi::xml_document document; - auto root_node = document.append_child("calcChain"); - write_document_to_archive(document, xlnt::constants::part_core(), destination_); + auto calc_chain_node = root.append_child("calcChain"); } -void xlsx_producer::write_chartsheet(const relationship &rel) +void xlsx_producer::write_chartsheet(pugi::xml_node root, const relationship &rel) { - pugi::xml_document document; - auto root_node = document.append_child("chartsheet"); - write_document_to_archive(document, xlnt::constants::part_core(), destination_); + auto chartsheet_node = root.append_child("chartsheet"); } -void xlsx_producer::write_connections() +void xlsx_producer::write_connections(pugi::xml_node root) { - pugi::xml_document document; - auto root_node = document.append_child("connections"); - write_document_to_archive(document, xlnt::constants::part_core(), destination_); + auto connections_node = root.append_child("connections"); } -void xlsx_producer::write_custom_property() +void xlsx_producer::write_custom_xml_mappings(pugi::xml_node root) { - pugi::xml_document document; - auto root_node = document.append_child("customProperty"); - write_document_to_archive(document, xlnt::constants::part_core(), destination_); + auto map_info_node = root.append_child("MapInfo"); } -void xlsx_producer::write_custom_xml_mappings() +void xlsx_producer::write_dialogsheet(pugi::xml_node root, const relationship &rel) { - pugi::xml_document document; - auto root_node = document.append_child("connections"); - write_document_to_archive(document, xlnt::constants::part_core(), destination_); + auto dialogsheet_node = root.append_child("dialogsheet"); } -void xlsx_producer::write_dialogsheet(const relationship &rel) +void xlsx_producer::write_external_workbook_references(pugi::xml_node root) { - pugi::xml_document document; - auto root_node = document.append_child("dialogsheet"); - write_document_to_archive(document, xlnt::constants::part_core(), destination_); + auto external_link_node = root.append_child("externalLink"); } -void xlsx_producer::write_external_workbook_references() +void xlsx_producer::write_metadata(pugi::xml_node root) { - pugi::xml_document document; - auto root_node = document.append_child("externalLink"); - write_document_to_archive(document, xlnt::constants::part_core(), destination_); + auto metadata_node = root.append_child("metadata"); } -void xlsx_producer::write_metadata() +void xlsx_producer::write_pivot_table(pugi::xml_node root) { - pugi::xml_document document; - auto root_node = document.append_child("metadata"); - write_document_to_archive(document, xlnt::constants::part_core(), destination_); + auto pivot_table_definition_node = root.append_child("pivotTableDefinition"); } -void xlsx_producer::write_pivot_table() +void xlsx_producer::write_shared_string_table(pugi::xml_node root) { - pugi::xml_document document; - auto root_node = document.append_child("pivotTableDefinition"); - write_document_to_archive(document, xlnt::constants::part_core(), destination_); -} + auto sst_node = root.append_child("sst"); -void xlsx_producer::write_shared_string_table() -{ - pugi::xml_document document; + sst_node.append_attribute("xmlns").set_value("http://schemas.openxmlformats.org/spreadsheetml/2006/main"); - auto root_node = document.append_child("sst"); - - root_node.append_attribute("xmlns").set_value("http://schemas.openxmlformats.org/spreadsheetml/2006/main"); - - root_node.append_attribute("count").set_value(std::to_string(source_.get_shared_strings().size()).c_str()); - root_node.append_attribute("uniqueCount").set_value(std::to_string(source_.get_shared_strings().size()).c_str()); + sst_node.append_attribute("count").set_value(std::to_string(source_.get_shared_strings().size()).c_str()); + sst_node.append_attribute("uniqueCount").set_value(std::to_string(source_.get_shared_strings().size()).c_str()); for (const auto &string : source_.get_shared_strings()) { if (string.get_runs().size() == 1 && !string.get_runs().at(0).has_formatting()) { - root_node.append_child("si").append_child("t").text().set(string.get_plain_string().c_str()); + sst_node.append_child("si").append_child("t").text().set(string.get_plain_string().c_str()); } else { - auto string_item_node = root_node.append_child("si"); + auto string_item_node = sst_node.append_child("si"); for (const auto &run : string.get_runs()) { @@ -1047,95 +995,81 @@ void xlsx_producer::write_shared_string_table() } } } - - write_document_to_archive(document, xlnt::constants::part_core(), destination_); } -void xlsx_producer::write_shared_workbook_revision_headers() +void xlsx_producer::write_shared_workbook_revision_headers(pugi::xml_node root) { - pugi::xml_document document; - auto root_node = document.append_child("headers"); - write_document_to_archive(document, xlnt::constants::part_core(), destination_); + auto headers_node = root.append_child("headers"); } -void xlsx_producer::write_shared_workbook() +void xlsx_producer::write_shared_workbook(pugi::xml_node root) { - pugi::xml_document document; - auto root_node = document.append_child("revisions"); - write_document_to_archive(document, xlnt::constants::part_core(), destination_); + auto revisions_node = root.append_child("revisions"); } -void xlsx_producer::write_shared_workbook_user_data() +void xlsx_producer::write_shared_workbook_user_data(pugi::xml_node root) { - pugi::xml_document document; - auto root_node = document.append_child("users"); - write_document_to_archive(document, xlnt::constants::part_core(), destination_); + auto users_node = root.append_child("users"); } -void xlsx_producer::write_styles() +void xlsx_producer::write_styles(pugi::xml_node root) { - pugi::xml_document document; - - auto root_node = document.append_child("styleSheet"); - root_node.append_attribute("xmlns").set_value("http://schemas.openxmlformats.org/spreadsheetml/2006/main"); - root_node.append_attribute("xmlns:mc").set_value("http://schemas.openxmlformats.org/markup-compatibility/2006"); - root_node.append_attribute("mc:Ignorable").set_value("x14ac"); - root_node.append_attribute("xmlns:x14ac").set_value("http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac"); + auto stylesheet_node = root.append_child("styleSheet"); + stylesheet_node.append_attribute("xmlns").set_value("http://schemas.openxmlformats.org/spreadsheetml/2006/main"); + stylesheet_node.append_attribute("xmlns:mc").set_value("http://schemas.openxmlformats.org/markup-compatibility/2006"); + stylesheet_node.append_attribute("mc:Ignorable").set_value("x14ac"); + stylesheet_node.append_attribute("xmlns:x14ac").set_value("http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac"); const auto &stylesheet = source_.impl().stylesheet_; if (!stylesheet.number_formats.empty()) { - auto number_formats_node = root_node.append_child("numFmts"); + auto number_formats_node = stylesheet_node.append_child("numFmts"); write_number_formats(stylesheet.number_formats, number_formats_node); } if (!stylesheet.fonts.empty()) { - auto fonts_node = root_node.append_child("fonts"); + auto fonts_node = stylesheet_node.append_child("fonts"); write_fonts(stylesheet.fonts, fonts_node); } if (!stylesheet.fills.empty()) { - auto fills_node = root_node.append_child("fills"); + auto fills_node = stylesheet_node.append_child("fills"); write_fills(stylesheet.fills, fills_node); } if (!stylesheet.borders.empty()) { - auto borders_node = root_node.append_child("borders"); + auto borders_node = stylesheet_node.append_child("borders"); write_borders(stylesheet.borders, borders_node); } - auto cell_style_xfs_node = root_node.append_child("cellStyleXfs"); + auto cell_style_xfs_node = stylesheet_node.append_child("cellStyleXfs"); - auto cell_xfs_node = root_node.append_child("cellXfs"); + auto cell_xfs_node = stylesheet_node.append_child("cellXfs"); write_formats(stylesheet, cell_xfs_node); - auto cell_styles_node = root_node.append_child("cellStyles"); + auto cell_styles_node = stylesheet_node.append_child("cellStyles"); ::write_styles(stylesheet, cell_styles_node, cell_style_xfs_node); - auto dxfs_node = root_node.append_child("dxfs"); + auto dxfs_node = stylesheet_node.append_child("dxfs"); write_dxfs(dxfs_node); - auto table_styles_node = root_node.append_child("tableStyles"); + auto table_styles_node = stylesheet_node.append_child("tableStyles"); write_table_styles(table_styles_node); if (!stylesheet.colors.empty()) { - auto colors_node = root_node.append_child("colors"); + auto colors_node = stylesheet_node.append_child("colors"); write_colors(stylesheet.colors, colors_node); } - - write_document_to_archive(document, xlnt::constants::part_core(), destination_); } -void xlsx_producer::write_theme() +void xlsx_producer::write_theme(pugi::xml_node root) { - pugi::xml_document document; - - auto theme_node = document.append_child("a:theme"); + auto theme_node = root.append_child("a:theme"); theme_node.append_attribute("xmlns:a").set_value(constants::get_namespace("drawingml").c_str()); theme_node.append_attribute("name").set_value("Office Theme"); @@ -1467,191 +1401,16 @@ void xlsx_producer::write_theme() theme_node.append_child("a:objectDefaults"); theme_node.append_child("a:extraClrSchemeLst"); - - write_document_to_archive(document, xlnt::constants::part_core(), destination_); } -void xlsx_producer::write_volatile_dependencies() +void xlsx_producer::write_volatile_dependencies(pugi::xml_node root) { - pugi::xml_document document; - auto root_node = document.append_child("volTypes"); - write_document_to_archive(document, xlnt::constants::part_core(), destination_); + auto vol_types_node = root.append_child("volTypes"); } -/* -bool xlsx_producer::read_worksheet(const relationship &rel) +void xlsx_producer::write_worksheet(pugi::xml_node root, const relationship &rel) { - const auto ws = source_.get_active_sheet(); - auto root_node = xml.child("worksheet"); - - auto dimension_node = root_node.child("dimension"); - std::string dimension(dimension_node.attribute("ref").value()); - auto full_range = xlnt::range_reference(dimension); - auto sheet_data_node = root_node.child("sheetData"); - - if (root_node.child("mergeCells")) - { - auto merge_cells_node = root_node.child("mergeCells"); - auto count = std::stoull(merge_cells_node.attribute("count").value()); - - for (auto merge_cell_node : merge_cells_node.children("mergeCell")) - { - sheet_.merge_cells(range_reference(merge_cell_node.attribute("ref").value())); - count--; - } - - if (count != 0) - { - throw std::runtime_error("mismatch between count and actual number of merged cells"); - } - } - - auto &shared_strings = sheet_.get_workbook().get_shared_strings(); - - for (auto row_node : sheet_data_node.children("row")) - { - auto row_index = static_cast(std::stoull(row_node.attribute("r").value())); - - if (row_node.attribute("ht")) - { - sheet_.get_row_properties(row_index).height = std::stold(row_node.attribute("ht").value()); - } - - std::string span_string = row_node.attribute("spans").value(); - auto colon_index = span_string.find(':'); - - column_t min_column = 0; - column_t max_column = 0; - - if (colon_index != std::string::npos) - { - min_column = static_cast(std::stoll(span_string.substr(0, colon_index))); - max_column = static_cast(std::stoll(span_string.substr(colon_index + 1))); - } - else - { - min_column = full_range.get_top_left().get_column_index(); - max_column = full_range.get_bottom_right().get_column_index(); - } - - for (column_t i = min_column; i <= max_column; i++) - { - std::string address = i.column_string() + std::to_string(row_index); - - pugi::xml_node cell_node; - bool cell_found = false; - - for (auto &check_cell_node : row_node.children("c")) - { - if (check_cell_node.attribute("r") && check_cell_node.attribute("r").value() == address) - { - cell_node = check_cell_node; - cell_found = true; - break; - } - } - - if (cell_found) - { - bool has_value = cell_node.child("v") != nullptr; - std::string value_string = has_value ? cell_node.child("v").text().get() : ""; - - bool has_type = cell_node.attribute("t") != nullptr; - std::string type = has_type ? cell_node.attribute("t").value() : ""; - - bool has_format = cell_node.attribute("s") != nullptr; - auto format_id = static_cast(has_format ? std::stoull(cell_node.attribute("s").value()) : 0LL); - - bool has_formula = cell_node.child("f") != nullptr; - bool has_shared_formula = has_formula && cell_node.child("f").attribute("t") != nullptr - && cell_node.child("f").attribute("t").value() == std::string("shared"); - - auto cell = sheet_.get_cell(address); - - if (has_formula && !has_shared_formula && !sheet_.get_workbook().get_data_only()) - { - std::string formula = cell_node.child("f").text().get(); - cell.set_formula(formula); - } - - if (has_type && type == "inlineStr") // inline string - { - std::string inline_string = cell_node.child("is").child("t").text().get(); - cell.set_value(inline_string); - } - else if (has_type && type == "s" && !has_formula) // shared string - { - auto shared_string_index = static_cast(std::stoull(value_string)); - auto shared_string = shared_strings.at(shared_string_index); - cell.set_value(shared_string); - } - else if (has_type && type == "b") // boolean - { - cell.set_value(value_string != "0"); - } - else if (has_type && type == "str") - { - cell.set_value(value_string); - } - else if (has_value && !value_string.empty()) - { - if (!value_string.empty() && value_string[0] == '#') - { - cell.set_error(value_string); - } - else - { - cell.set_value(std::stold(value_string)); - } - } - - if (has_format) - { - cell.set_format(stylesheet.formats.at(format_id)); - } - } - } - } - - auto cols_node = root_node.child("cols"); - - for (auto col_node : cols_node.children("col")) - { - auto min = static_cast(std::stoull(col_node.attribute("min").value())); - auto max = static_cast(std::stoull(col_node.attribute("max").value())); - auto width = std::stold(col_node.attribute("width").value()); - bool custom = col_node.attribute("customWidth").value() == std::string("1"); - auto column_style = static_cast(col_node.attribute("style") ? std::stoull(col_node.attribute("style").value()) : 0); - - for (auto column = min; column <= max; column++) - { - if (!sheet_.has_column_properties(column)) - { - sheet_.add_column_properties(column, column_properties()); - } - - sheet_.get_column_properties(min).width = width; - sheet_.get_column_properties(min).style = column_style; - sheet_.get_column_properties(min).custom = custom; - } - } - - if (root_node.child("autoFilter")) - { - auto auto_filter_node = root_node.child("autoFilter"); - xlnt::range_reference ref(auto_filter_node.attribute("ref").value()); - sheet_.auto_filter(ref); - } - - return true; -} -*/ - -void xlsx_producer::write_worksheet(const relationship &rel) -{ - pugi::xml_document document; - - auto worksheet_node = document.append_child("worksheet"); + auto worksheet_node = root.append_child("worksheet"); worksheet_node.append_attribute("xmlns").set_value("http://schemas.openxmlformats.org/spreadsheetml/2006/main"); worksheet_node.append_attribute("xmlns:r").set_value("http://schemas.openxmlformats.org/package/2006/relationships"); @@ -1661,7 +1420,14 @@ void xlsx_producer::write_worksheet(const relationship &rel) outline_pr_node.append_attribute("summaryBelow").set_value("1"); outline_pr_node.append_attribute("summaryRight").set_value("1"); - auto ws = source_.get_sheet_by_index(0); + auto title = std::find_if(source_.d_->sheet_title_rel_id_map_.begin(), + source_.d_->sheet_title_rel_id_map_.end(), + [&](const std::pair &p) + { + return p.second == rel.get_id(); + })->first; + + auto ws = source_.get_sheet_by_title(title); if (!ws.get_page_setup().is_default()) { @@ -1821,11 +1587,6 @@ void xlsx_producer::write_worksheet(const relationship &rel) { if (!cell.garbage_collectible()) { - if (cell.has_hyperlink()) - { -// hyperlink_references[cell.get_hyperlink().get_id()] = cell.get_reference().to_string(); - } - auto cell_node = row_node.append_child("c"); cell_node.append_attribute("r").set_value(cell.get_reference().to_string().c_str()); @@ -1943,14 +1704,28 @@ void xlsx_producer::write_worksheet(const relationship &rel) if (!source_.impl().manifest_.get_part_relationships(rel.get_target_uri()).empty()) { - auto hyperlinks_node = worksheet_node.append_child("hyperlinks"); + auto sheet_rels = source_.impl().manifest_.get_part_relationships(rel.get_target_uri()); + std::vector hyperlink_sheet_rels; - for (const auto &relationship : source_.impl().manifest_.get_part_relationships(rel.get_target_uri())) + for (const auto &sheet_rel : sheet_rels) { - auto hyperlink_node = hyperlinks_node.append_child("hyperlink"); - hyperlink_node.append_attribute("display").set_value(relationship.get_target_uri().to_string('/').c_str()); - hyperlink_node.append_attribute("ref").set_value(hyperlink_references.at(relationship.get_id()).c_str()); - hyperlink_node.append_attribute("r:id").set_value(relationship.get_id().c_str()); + if (sheet_rel.get_type() == relationship::type::hyperlink) + { + hyperlink_sheet_rels.push_back(sheet_rel); + } + } + + if (!hyperlink_sheet_rels.empty()) + { + auto hyperlinks_node = worksheet_node.append_child("hyperlinks"); + + 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("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()); + } } } @@ -2022,40 +1797,32 @@ void xlsx_producer::write_worksheet(const relationship &rel) "Text &P of &N"; odd_footer_node.text().set(footer_text.c_str()); } - - write_document_to_archive(document, rel.get_target_uri(), destination_); } // Sheet Relationship Target Parts -void xlsx_producer::write_comments() +void xlsx_producer::write_comments(pugi::xml_node root) { - pugi::xml_document document; - auto root_node = document.append_child("comments"); - write_document_to_archive(document, xlnt::constants::part_core(), destination_); + auto comments_node = root.append_child("comments"); } -void xlsx_producer::write_drawings() +void xlsx_producer::write_drawings(pugi::xml_node root) { - pugi::xml_document document; - auto root_node = document.append_child("wsDr"); - write_document_to_archive(document, xlnt::constants::part_core(), destination_); + auto ws_dr_node = root.append_child("wsDr"); } -// Unknown Parts +// Other Parts + +void xlsx_producer::write_custom_property() +{ +} void xlsx_producer::write_unknown_parts() { - pugi::xml_document document; - auto root_node = document.append_child("Hmm"); - write_document_to_archive(document, xlnt::constants::part_core(), destination_); } void xlsx_producer::write_unknown_relationships() { - pugi::xml_document document; - auto root_node = document.append_child("Relationships"); - write_document_to_archive(document, xlnt::constants::part_core(), destination_); } } // namespace detail diff --git a/source/detail/xlsx_producer.hpp b/source/detail/xlsx_producer.hpp index 45504a31..41622e2d 100644 --- a/source/detail/xlsx_producer.hpp +++ b/source/detail/xlsx_producer.hpp @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -57,50 +58,47 @@ private: /// Write all files needed to create a valid XLSX file which represents all /// data contained in workbook. /// - void populate_archive(zip_file &archive); + void populate_archive(); // Package Parts - void write_package_relationships(); - void write_content_types(); - void write_app_properties(); - void write_core_properties(); - void write_custom_file_properties(); + 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); // SpreadsheetML-Specific Package Parts - void write_workbook(); - void write_workbook_relationships(); + void write_workbook(pugi::xml_node root); // Workbook Relationship Target Parts - void write_calculation_chain(); - void write_connections(); - void write_custom_property(); - void write_custom_xml_mappings(); - void write_external_workbook_references(); - void write_metadata(); - void write_pivot_table(); - void write_shared_string_table(); - void write_shared_workbook_revision_headers(); - void write_shared_workbook(); - void write_shared_workbook_user_data(); - void write_styles(); - void write_theme(); - void write_volatile_dependencies(); + 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_chartsheet(const relationship &rel); - void write_dialogsheet(const relationship &rel); - bool read_worksheet(const relationship &rel); - void write_worksheet(const relationship &rel); + 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); // Sheet Relationship Target Parts - void write_comments(); - void write_drawings(); + void write_comments(pugi::xml_node root); + void write_drawings(pugi::xml_node root); - // Unknown Parts + // Other Parts + void write_custom_property(); void write_unknown_parts(); void write_unknown_relationships(); diff --git a/source/packaging/manifest.cpp b/source/packaging/manifest.cpp index d8d0380f..cae62231 100644 --- a/source/packaging/manifest.cpp +++ b/source/packaging/manifest.cpp @@ -169,99 +169,77 @@ std::vector manifest::get_part_relationships(const path &part, rel return matches; } -content_type manifest::get_part_content_type(const path &part) const +content_type manifest::get_content_type(const path &part) const { - return from_string(get_part_content_type_string(part)); + return from_string(get_content_type_string(part)); } -std::string manifest::get_part_content_type_string(const path &part) const +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; } -void manifest::register_part(const path &part, const path &parent, const std::string &content_type, relationship::type relation) +void manifest::register_override_type(const path &part, const std::string &content_type) { part_infos_[part] = { content_type, {} }; - - relationship rel(next_package_relationship_id(), relation, part, target_mode::internal); - part_infos_[parent].relationships.push_back(rel); } -void manifest::register_part(const path &parent, const relationship &rel, const std::string &content_type) +std::string manifest::register_package_relationship(relationship::type type, const path &target_uri, target_mode mode) { - part_infos_[rel.get_target_uri()] = { content_type,{} }; - part_infos_[parent].relationships.push_back(rel); + return register_package_relationship(type, target_uri, mode, next_package_relationship_id()); } -void manifest::register_package_part(const path &part, const std::string &content_type, relationship::type relation) +std::string manifest::register_package_relationship(relationship::type type, const path &target_uri, target_mode mode, const std::string &rel_id) { - part_infos_[part] = { content_type, {} }; + if (has_package_relationship(rel_id)) + { + throw invalid_parameter(); + } - relationship rel(next_package_relationship_id(), relation, part, target_mode::internal); + relationship rel(rel_id, type, target_uri, mode); package_relationships_.push_back(rel); + + return rel_id; } -void manifest::unregister_part(const path &part) +bool manifest::has_package_relationship(const std::string &rel_id) const { - if (part_infos_.find(part) != part_infos_.end()) + for (const auto &rel : package_relationships_) { - part_infos_.erase(part); - } - else - { - throw key_not_found(); - } - - auto package_rels_iter = package_relationships_.begin(); - - while (package_rels_iter != package_relationships_.end()) - { - if (package_rels_iter->get_target_uri() == part) + if (rel.get_id() == rel_id) { - package_rels_iter = package_relationships_.erase(package_rels_iter); - continue; - } - - ++package_rels_iter; - } - - for (auto ¤t_part : part_infos_) - { - auto rels_iter = current_part.second.relationships.begin(); - - while (rels_iter != current_part.second.relationships.end()) - { - if (rels_iter->get_target_uri() == part) - { - rels_iter = current_part.second.relationships.erase(rels_iter); - continue; - } - - ++rels_iter; + return true; } } + + return false; } -std::vector manifest::get_parts() const +std::vector manifest::get_overriden_parts() const { - std::vector parts; + std::vector overriden; for (const auto &part : part_infos_) { - parts.push_back(part.first); + if (!part.second.content_type.empty()) + { + overriden.push_back(part.first); + } } - return parts; -} - -bool manifest::has_part(const path &part) const -{ - return part_infos_.find(part) != part_infos_.end(); + return overriden; } std::vector manifest::get_package_relationships() const @@ -269,7 +247,7 @@ std::vector manifest::get_package_relationships() const return package_relationships_; } -relationship manifest::get_package_relationship(const std::string &rel_id) +relationship manifest::get_package_relationship(const std::string &rel_id) const { for (const auto rel : package_relationships_) { @@ -284,14 +262,12 @@ relationship manifest::get_package_relationship(const std::string &rel_id) std::vector manifest::get_part_relationships(const path &part) const { - std::vector matches; - - for (const auto &rel : part_infos_.at(part).relationships) + if (part_infos_.find(part) == part_infos_.end()) { - matches.push_back(rel); + throw key_not_found(); } - return matches; + return part_infos_.at(part).relationships; } relationship manifest::get_part_relationship(const path &part, const std::string &rel_id) const @@ -312,27 +288,30 @@ relationship manifest::get_part_relationship(const path &part, const std::string throw key_not_found(); } -std::string manifest::register_external_package_relationship(relationship::type type, const std::string &target_uri) +std::vector manifest::get_parts_with_relationships() const { - return register_external_package_relationship(type, target_uri, next_package_relationship_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_external_package_relationship(relationship::type type, const std::string &target_uri, const std::string &rel_id) +std::string manifest::register_part_relationship(const path &source_uri, relationship::type type, const path &target_uri, target_mode mode) { - relationship rel(rel_id, type, path(target_uri), target_mode::external); - package_relationships_.push_back(rel); - return rel_id; + return register_part_relationship(source_uri, type, target_uri, mode, next_relationship_id(source_uri)); } -std::string manifest::register_external_relationship(const path &source_part, relationship::type type, const std::string &target_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) { - return register_external_relationship(source_part, type, target_uri, next_relationship_id(source_part)); -} - -std::string manifest::register_external_relationship(const path &source_part, relationship::type type, const std::string &target_uri, const std::string &rel_id) -{ - relationship rel(rel_id, type, path(target_uri), target_mode::external); - part_infos_.at(source_part).relationships.push_back(rel); + relationship rel(rel_id, type, path(target_uri), mode); + part_infos_.at(source_uri).relationships.push_back(rel); return rel_id; } diff --git a/source/packaging/tests/test_core.hpp b/source/packaging/tests/test_core.hpp deleted file mode 100644 index 1410f6e8..00000000 --- a/source/packaging/tests/test_core.hpp +++ /dev/null @@ -1,82 +0,0 @@ -#pragma once - -#include -#include - -#include -#include -#include -#include - -class test_core : public CxxTest::TestSuite -{ -public: - void test_read_properties_core() - { - xlnt::workbook wb; - wb.load(path_helper::get_data_directory("genuine/empty.xlsx")); - - TS_ASSERT_EQUALS(wb.get_creator(), "*.*"); - TS_ASSERT_EQUALS(wb.get_last_modified_by(), "Charlie Clark"); - TS_ASSERT_EQUALS(wb.get_created(), xlnt::datetime(2010, 4, 9, 20, 43, 12)); - TS_ASSERT_EQUALS(wb.get_modified(), xlnt::datetime(2014, 1, 2, 14, 53, 6)); - } - - void test_read_sheets_titles() - { - const std::vector expected_titles = {"Sheet1 - Text", "Sheet2 - Numbers", "Sheet3 - Formulas", "Sheet4 - Dates"}; - - xlnt::workbook wb; - wb.load(path_helper::get_data_directory("genuine/empty.xlsx")); - - TS_ASSERT_EQUALS(wb.get_sheet_titles(), expected_titles); - } - - void test_read_properties_core_libre() - { - xlnt::workbook wb; - wb.load(path_helper::get_data_directory("genuine/empty_libre.xlsx")); - TS_ASSERT_EQUALS(wb.get_base_date(), xlnt::calendar::windows_1900); - } - - void test_read_sheets_titles_libre() - { - const std::vector expected_titles = {"Sheet1 - Text", "Sheet2 - Numbers", "Sheet3 - Formulas", "Sheet4 - Dates"}; - - xlnt::workbook wb; - wb.load(path_helper::get_data_directory("genuine/empty_libre.xlsx")); - auto title_iter = expected_titles.begin(); - - for(auto ws : wb) - { - TS_ASSERT_EQUALS(ws.get_title(), *(title_iter++)); - } - } - - void test_write_properties_core() - { - xlnt::workbook wb; - wb.set_creator("TEST_USER"); - wb.set_last_modified_by("SOMEBODY"); - wb.set_created(xlnt::datetime(2010, 4, 1, 20, 30, 00)); - wb.set_modified(xlnt::datetime(2010, 4, 5, 14, 5, 30)); - - TS_ASSERT(xml_helper::file_matches_workbook_part( - path_helper::get_data_directory("writer/expected/core.xml"), - wb, xlnt::constants::part_core())); - } - - void test_write_properties_app() - { - xlnt::workbook wb; - wb.set_application("Microsoft Excel"); - wb.set_app_version("12.0000"); - wb.set_company("Company"); - wb.create_sheet(); - wb.create_sheet(); - - TS_ASSERT(xml_helper::file_matches_workbook_part( - path_helper::get_data_directory("writer/expected/core.xml"), - wb, xlnt::constants::part_core())); - } -}; diff --git a/source/packaging/tests/test_zip_file.hpp b/source/packaging/tests/test_zip_file.hpp new file mode 100644 index 00000000..776d9aa5 --- /dev/null +++ b/source/packaging/tests/test_zip_file.hpp @@ -0,0 +1,225 @@ +#include +#include +#include + +#include +#include "helpers/path_helper.hpp" +#include "helpers/temporary_file.hpp" + +class test_zip_file : public CxxTest::TestSuite +{ +public: + test_zip_file() + { + existing_file = path_helper::get_data_directory("xlsx/4_not-package.xlsx"); + expected_string = "not-empty"; + } + + bool files_equal(const xlnt::path &left, const xlnt::path &right) + { + if(left.to_string() == right.to_string()) + { + return true; + } + + std::ifstream stream_left(left.to_string(), std::ios::binary); + std::ifstream stream_right(right.to_string(), std::ios::binary); + + while(stream_left && stream_right) + { + if(stream_left.get() != stream_right.get()) + { + return false; + } + } + + return true; + } + + void test_load_file() + { + temporary_file temp_file; + xlnt::zip_file f(existing_file); + f.save(temp_file.get_path()); + TS_ASSERT(files_equal(existing_file, temp_file.get_path())); + } + + void test_load_stream() + { + temporary_file temp; + + std::ifstream in_stream(existing_file.to_string(), std::ios::binary); + xlnt::zip_file f(in_stream); + std::ofstream out_stream(temp.get_path().to_string(), std::ios::binary); + f.save(out_stream); + out_stream.close(); + + TS_ASSERT(files_equal(existing_file, temp.get_path())); + } + + void test_load_bytes() + { + temporary_file temp_file; + + std::vector source_bytes; + std::ifstream in_stream(existing_file.to_string(), std::ios::binary); + + while(in_stream) + { + source_bytes.push_back(static_cast(in_stream.get())); + } + + xlnt::zip_file f(source_bytes); + f.save(temp_file.get_path()); + + xlnt::zip_file f2; + f2.load(temp_file.get_path()); + std::vector result_bytes; + f2.save(result_bytes); + + TS_ASSERT(source_bytes == result_bytes); + } + + void test_reset() + { + xlnt::zip_file f(existing_file); + + TS_ASSERT(!f.namelist().empty()); + + try + { + f.read(xlnt::path("text.txt")); + } + catch(std::exception e) + { + TS_ASSERT(false); + } + + f.reset(); + + TS_ASSERT(f.namelist().empty()); + + try + { + f.read(xlnt::path("doesnt-exist.txt")); + TS_ASSERT(false); + } + catch(std::exception e) + { + } + + f.write_string("b", xlnt::path("a")); + f.reset(); + + TS_ASSERT(f.namelist().empty()); + + f.write_string("b", xlnt::path("a")); + + TS_ASSERT_DIFFERS(f.getinfo(xlnt::path("a")).file_size, 0); + } + + void test_getinfo() + { + xlnt::zip_file f(existing_file); + auto info = f.getinfo(xlnt::path("text.txt")); + TS_ASSERT(info.filename.to_string() == "text.txt"); + } + + void test_infolist() + { + xlnt::zip_file f(existing_file); + TS_ASSERT_EQUALS(f.infolist().size(), 1); + } + + void test_namelist() + { + xlnt::zip_file f(existing_file); + TS_ASSERT_EQUALS(f.namelist().size(), 1); + } + + void test_open_by_name() + { + xlnt::zip_file f(existing_file); + std::stringstream ss; + ss << f.open(xlnt::path("text.txt")).rdbuf(); + std::string result = ss.str(); + TS_ASSERT(result == expected_string); + } + + void test_open_by_info() + { + xlnt::zip_file f(existing_file); + std::stringstream ss; + ss << f.open(xlnt::path("text.txt")).rdbuf(); + std::string result = ss.str(); + TS_ASSERT(result == expected_string); + } + + + void test_read() + { + xlnt::zip_file f(existing_file); + TS_ASSERT(f.read(xlnt::path("text.txt")) == expected_string); + TS_ASSERT(f.read(f.getinfo(xlnt::path("text.txt"))) == expected_string); + } + + void test_testzip() + { + xlnt::zip_file f(existing_file); + TS_ASSERT(!f.check_crc()); + } + + void test_write_file() + { + temporary_file temp_file; + + xlnt::zip_file f; + auto text_file = path_helper::get_data_directory("xlsx/2_text.xlsx"); + f.write_file(text_file); + f.write_file(text_file, xlnt::path("a.txt")); + f.save(temp_file.get_path()); + + xlnt::zip_file f2(temp_file.get_path()); + + for(auto &info : f2.infolist()) + { + TS_ASSERT(f2.read(info) == expected_string); + } + } + + void test_write_string() + { + xlnt::zip_file f; + f.write_string("a\na", xlnt::path("a.txt")); + xlnt::zip_info info; + info.filename = xlnt::path("b.txt"); + info.date_time.year = 2014; + f.write_string("b\nb", info); + + temporary_file temp_file; + f.save(temp_file.get_path()); + + xlnt::zip_file f2(temp_file.get_path()); + TS_ASSERT(f2.read(xlnt::path("a.txt")) == "a\na"); + TS_ASSERT(f2.read(f2.getinfo(xlnt::path("b.txt"))) == "b\nb"); + } + + void test_comment() + { + xlnt::zip_file f; + f.comment = "comment"; + temporary_file temp_file; + f.save(temp_file.get_path()); + + xlnt::zip_file f2(temp_file.get_path()); + TS_ASSERT(f2.comment == "comment"); + + xlnt::zip_file f3; + std::vector bytes { 1, 2, 3 }; + TS_ASSERT_THROWS(f3.load(bytes), std::runtime_error); + } + +private: + xlnt::path existing_file; + std::string expected_string; +}; diff --git a/source/packaging/zip_file.cpp b/source/packaging/zip_file.cpp index f13fef1e..0b382886 100644 --- a/source/packaging/zip_file.cpp +++ b/source/packaging/zip_file.cpp @@ -31,6 +31,7 @@ #include #include +#include namespace { @@ -164,8 +165,19 @@ zip_file::~zip_file() void zip_file::load(std::istream &stream) { + if (!stream.good()) + { + throw invalid_file("((std::istream))"); + } + reset(); buffer_.assign(std::istreambuf_iterator(stream), std::istreambuf_iterator()); + + if (buffer_.empty()) + { + throw invalid_file("((stream)) - empty file"); + } + remove_comment(); start_read(); } @@ -174,11 +186,31 @@ void zip_file::load(const path &filename) { filename_ = filename; std::ifstream stream(filename.to_string(), std::ios::binary); - load(stream); + + if (!stream.good()) + { + throw invalid_file(filename.to_string()); + } + + reset(); + buffer_.assign(std::istreambuf_iterator(stream), std::istreambuf_iterator()); + + if (buffer_.empty()) + { + throw invalid_file(filename.to_string() + " - empty file"); + } + + remove_comment(); + start_read(); } void zip_file::load(const std::vector &bytes) { + if (bytes.empty()) + { + throw invalid_file("((bytes))"); + } + reset(); buffer_.assign(bytes.begin(), bytes.end()); remove_comment(); diff --git a/source/styles/tests/test_stylesheet.hpp b/source/styles/tests/test_stylesheet.hpp deleted file mode 100644 index 23e3b3f1..00000000 --- a/source/styles/tests/test_stylesheet.hpp +++ /dev/null @@ -1,285 +0,0 @@ -#pragma once - -#include -#include -#include - -#include -#include -#include -#include -#include - -class test_stylesheet : public CxxTest::TestSuite -{ -public: - void test_from_simple() - { - xlnt::workbook wb; - wb.load(path_helper::get_data_directory("reader/styles/simple-styles.xml")); - } - - void test_from_complex() - { - xlnt::workbook wb; - wb.load(path_helper::get_data_directory("reader/styles/complex-styles.xml")); - } - - void test_add_existing_style() - { - xlnt::workbook wb; - - auto &s = wb.create_style("test"); - - wb.add_style(s); - - xlnt::style copy; - copy = s; - - TS_ASSERT_EQUALS(s, copy); - } - -/* - void _test_unprotected_cell() - { - datadir.chdir(); - src = open ("worksheet_unprotected_style.xml"); - xml = src.read(); - node = fromstring(xml); - stylesheet = Stylesheet.from_tree(node); - - styles = stylesheet.cell_styles; - assert len(styles) == 3; - // default is cells are locked - assert styles[1] == StyleArray([4,0,0,0,0,0,0,0,0]); - assert styles[2] == StyleArray([3,0,0,0,1,0,0,0,0]); - } - - void _test_read_cell_style() - { - datadir.chdir(); - src = open("empty-workbook-styles.xml"); - xml = src.read(); - node = fromstring(xml); - stylesheet = Stylesheet.from_tree(node); - - styles = stylesheet.cell_styles; - assert len(styles) == 2; - assert styles[1] == StyleArray([0,0,0,9,0,0,0,0,1]; - } - - void _test_read_xf_no_number_format() - { - datadir.chdir(); - src = open("no_number_format.xml"); - xml = src.read(); - node = fromstring(xml); - stylesheet = Stylesheet.from_tree(node); - - styles = stylesheet.cell_styles; - assert len(styles) == 3; - assert styles[1] == StyleArray([1,0,1,0,0,0,0,0,0]); - assert styles[2] == StyleArray([0,0,0,14,0,0,0,0,0]); - } - - void _test_none_values() - { - datadir.chdir(); - src = open("none_value_styles.xml"); - xml = src.read(); - node = fromstring(xml); - stylesheet = Stylesheet.from_tree(node); - - fonts = stylesheet.fonts; - assert fonts[0].scheme is None; - assert fonts[0].vertAlign is None; - assert fonts[1].u is None; - } - - void _test_alignment() - { - datadir.chdir(); - src = open("alignment_styles.xml"); - xml = src.read(); - node = fromstring(xml); - stylesheet = Stylesheet.from_tree(node); - - styles = stylesheet.cell_styles; - assert len(styles) == 3; - assert styles[2] == StyleArray([0,0,0,0,0,2,0,0,0]); - - assert stylesheet.alignments == [ - Alignment(), - Alignment(textRotation=180), - Alignment(vertical='top', textRotation=255), - ]; - } - - void _test_rgb_colors() - { - datadir.chdir(); - src = open("rgb_colors.xml"); - xml = src.read(); - node = fromstring(xml); - stylesheet = Stylesheet.from_tree(node); - - assert len(stylesheet.colors.index) == 64; - assert stylesheet.colors.index[0] == "00000000"; - assert stylesheet.colors.index[-1] == "00333333"; - } - - - void _test_custom_number_formats() - { - datadir.chdir(); - src = open("styles_number_formats.xml", "rb"); - xml = src.read(); - node = fromstring(xml); - stylesheet = Stylesheet.from_tree(node); - - assert stylesheet.number_formats == [ - '_ * #,##0.00_ ;_ * \-#,##0.00_ ;_ * "-"??_ ;_ @_ ', - "#,##0.00_ ", - "yyyy/m/d;@", - "0.00000_ " - ]; - } - - void _test_assign_number_formats() - { - node = fromstring( - "" - "" - " " - "" - "" - "" - " " - "" - "" - ""); - stylesheet = Stylesheet.from_tree(node); - styles = stylesheet.cell_styles; - - assert styles[0] == StyleArray([2, 0, 0, 164, 0, 1, 0, 0, 0]); - } - - - void _test_named_styles() - { - datadir.chdir(); - src = open("complex-styles.xml"); - xml = src.read(); - node = fromstring(xml); - stylesheet = Stylesheet.from_tree(node); - - followed = stylesheet.named_styles['Followed Hyperlink']; - assert followed.name == "Followed Hyperlink"; - assert followed.font == stylesheet.fonts[2]; - assert followed.fill == DEFAULT_EMPTY_FILL; - assert followed.border == Border(); - - link = stylesheet.named_styles['Hyperlink']; - assert link.name == "Hyperlink"; - assert link.font == stylesheet.fonts[1]; - assert link.fill == DEFAULT_EMPTY_FILL; - assert link.border == Border(); - - normal = stylesheet.named_styles['Normal']; - assert normal.name == "Normal"; - assert normal.font == stylesheet.fonts[0]; - assert normal.fill == DEFAULT_EMPTY_FILL; - assert normal.border == Border(); - } - - void _test_no_styles() - { - wb1 = wb2 = Workbook(); - archive = ZipFile(BytesIO(), "a"); - apply_stylesheet(archive, wb1); - assert wb1._cell_styles == wb2._cell_styles; - assert wb2._named_styles == wb2._named_styles; - } - - void _test_write_worksheet() - { - wb = Workbook() - node = write_stylesheet(wb); - xml = tostring(node); - - const std::string expected = - "" - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - "" - ""; - - diff = compare_xml(xml, expected); - assert diff is None, diff; - } - - void _test_simple_styles() { - wb = Workbook(); - wb.guess_types = True; - ws = wb.active; - now = datetime.date.today(); - for idx, v in enumerate(['12.34%', now, 'This is a test', '31.31415', None], 1) - { - ws.append([v]); - _ = ws.cell(column=1, row=idx).style_id; - } - - // set explicit formats - ws['D9'].number_format = numbers.FORMAT_NUMBER_00; - ws['D9'].protection = Protection(locked=True); - ws['D9'].style_id; - ws['E1'].protection = Protection(hidden=True); - ws['E1'].style_id; - - assert len(wb._cell_styles) == 5; - stylesheet = write_stylesheet(wb); - - datadir.chdir(); - reference_file = open('simple-styles.xml'); - expected = reference_file.read(); - xml = tostring(stylesheet); - diff = compare_xml(xml, expected); - assert diff is None, diff; - } - */ -}; diff --git a/source/utils/path.cpp b/source/utils/path.cpp index 85735f6d..c9e092b1 100644 --- a/source/utils/path.cpp +++ b/source/utils/path.cpp @@ -187,6 +187,10 @@ path path::parent() const { result.parts_.pop_back(); } + else + { + return path(""); + } return result; } diff --git a/source/utils/tests/test_zip_file.hpp b/source/utils/tests/test_zip_file.hpp deleted file mode 100644 index f5190625..00000000 --- a/source/utils/tests/test_zip_file.hpp +++ /dev/null @@ -1,254 +0,0 @@ -#include -#include -#include - -#include -#include "helpers/path_helper.hpp" -#include "helpers/temporary_file.hpp" - -class test_zip_file : public CxxTest::TestSuite -{ -public: - test_zip_file() - { - existing_file = path_helper::get_data_directory("genuine/empty.xlsx"); - expected_content_types_string = "\r\n"; - expected_atxt_string = "\nThis is cell A1 in Sheet 1This is cell G5"; - expected_printdir_string = - " Length Date Time Name\n" - "--------- ---------- ----- ----\n" - " 1704 01/01/1980 00:00 [Content_Types].xml\n" - " 588 01/01/1980 00:00 _rels/.rels\n" - " 917 01/01/1980 00:00 docProps/app.xml\n" - " 609 01/01/1980 00:00 docProps/core.xml\n" - " 1254 01/01/1980 00:00 xl/_rels/workbook.xml.rels\n" - " 169 01/01/1980 00:00 xl/calcChain.xml\n" - " 233 01/01/1980 00:00 xl/sharedStrings.xml\n" - " 1724 01/01/1980 00:00 xl/styles.xml\n" - " 6995 01/01/1980 00:00 xl/theme/theme1.xml\n" - " 898 01/01/1980 00:00 xl/workbook.xml\n" - " 1068 07/21/2016 20:27 xl/worksheets/sheet1.xml\n" - " 4427 01/01/1980 00:00 xl/worksheets/sheet2.xml\n" - " 1032 01/01/1980 00:00 xl/worksheets/sheet3.xml\n" - " 1231 01/01/1980 00:00 xl/worksheets/sheet4.xml\n" - "--------- -------\n" - " 22849 14 files\n"; - } - - bool files_equal(const xlnt::path &left, const xlnt::path &right) - { - if(left.to_string() == right.to_string()) - { - return true; - } - - std::ifstream stream_left(left.to_string(), std::ios::binary); - std::ifstream stream_right(right.to_string(), std::ios::binary); - - while(stream_left && stream_right) - { - if(stream_left.get() != stream_right.get()) - { - return false; - } - } - - return true; - } - - void test_load_file() - { - temporary_file temp_file; - xlnt::zip_file f(existing_file); - f.save(temp_file.get_path()); - TS_ASSERT(files_equal(existing_file, temp_file.get_path())); - } - - void test_load_stream() - { - temporary_file temp; - - std::ifstream in_stream(existing_file.to_string(), std::ios::binary); - xlnt::zip_file f(in_stream); - std::ofstream out_stream(temp.get_path().to_string(), std::ios::binary); - f.save(out_stream); - out_stream.close(); - - TS_ASSERT(files_equal(existing_file, temp.get_path())); - } - - void test_load_bytes() - { - temporary_file temp_file; - - std::vector source_bytes; - std::ifstream in_stream(existing_file.to_string(), std::ios::binary); - - while(in_stream) - { - source_bytes.push_back(static_cast(in_stream.get())); - } - - xlnt::zip_file f(source_bytes); - f.save(temp_file.get_path()); - - xlnt::zip_file f2; - f2.load(temp_file.get_path()); - std::vector result_bytes; - f2.save(result_bytes); - - TS_ASSERT(source_bytes == result_bytes); - } - - void test_reset() - { - xlnt::zip_file f(existing_file); - - TS_ASSERT(!f.namelist().empty()); - - try - { - f.read(xlnt::path("[Content_Types].xml")); - } - catch(std::exception e) - { - TS_ASSERT(false); - } - - f.reset(); - - TS_ASSERT(f.namelist().empty()); - - try - { - f.read(xlnt::path("[Content_Types].xml")); - TS_ASSERT(false); - } - catch(std::exception e) - { - } - - f.write_string("b", xlnt::path("a")); - f.reset(); - - TS_ASSERT(f.namelist().empty()); - - f.write_string("b", xlnt::path("a")); - - TS_ASSERT_DIFFERS(f.getinfo(xlnt::path("a")).file_size, 0); - } - - void test_getinfo() - { - xlnt::zip_file f(existing_file); - auto info = f.getinfo(xlnt::path("[Content_Types].xml")); - TS_ASSERT(info.filename.to_string() == "[Content_Types].xml"); - } - - void test_infolist() - { - xlnt::zip_file f(existing_file); - TS_ASSERT_EQUALS(f.infolist().size(), 14); - } - - void test_namelist() - { - xlnt::zip_file f(existing_file); - TS_ASSERT_EQUALS(f.namelist().size(), 14); - } - - void test_open_by_name() - { - xlnt::zip_file f(existing_file); - std::stringstream ss; - ss << f.open(xlnt::path("[Content_Types].xml")).rdbuf(); - std::string result = ss.str(); - TS_ASSERT(result == expected_content_types_string); - } - - void test_open_by_info() - { - xlnt::zip_file f(existing_file); - std::stringstream ss; - ss << f.open(xlnt::path("[Content_Types].xml")).rdbuf(); - std::string result = ss.str(); - TS_ASSERT(result == expected_content_types_string); - } - - - void test_read() - { - xlnt::zip_file f(existing_file); - TS_ASSERT(f.read(xlnt::path("[Content_Types].xml")) == expected_content_types_string); - TS_ASSERT(f.read(f.getinfo(xlnt::path("[Content_Types].xml"))) == expected_content_types_string); - } - - void test_testzip() - { - xlnt::zip_file f(existing_file); - TS_ASSERT(!f.check_crc()); - } - - void test_write_file() - { - temporary_file temp_file; - - xlnt::zip_file f; - auto text_file = path_helper::get_data_directory("reader/sharedStrings.xml"); - f.write_file(text_file); - f.write_file(text_file, xlnt::path("sharedStrings2.xml")); - f.save(temp_file.get_path()); - - xlnt::zip_file f2(temp_file.get_path()); - - for(auto &info : f2.infolist()) - { - if(info.filename.to_string() == "sharedStrings2.xml") - { - TS_ASSERT(f2.read(info) == expected_atxt_string); - } - else if(info.filename.basename() == "sharedStrings.xml") - { - TS_ASSERT(f2.read(info) == expected_atxt_string); - } - } - } - - void test_write_string() - { - xlnt::zip_file f; - f.write_string("a\na", xlnt::path("a.txt")); - xlnt::zip_info info; - info.filename = xlnt::path("b.txt"); - info.date_time.year = 2014; - f.write_string("b\nb", info); - - temporary_file temp_file; - f.save(temp_file.get_path()); - - xlnt::zip_file f2(temp_file.get_path()); - TS_ASSERT(f2.read(xlnt::path("a.txt")) == "a\na"); - TS_ASSERT(f2.read(f2.getinfo(xlnt::path("b.txt"))) == "b\nb"); - } - - void test_comment() - { - xlnt::zip_file f; - f.comment = "comment"; - temporary_file temp_file; - f.save(temp_file.get_path()); - - xlnt::zip_file f2(temp_file.get_path()); - TS_ASSERT(f2.comment == "comment"); - - xlnt::zip_file f3; - std::vector bytes { 1, 2, 3 }; - TS_ASSERT_THROWS(f3.load(bytes), std::runtime_error); - } - -private: - xlnt::path existing_file; - std::string expected_content_types_string; - std::string expected_atxt_string; - std::string expected_printdir_string; -}; diff --git a/source/workbook/tests/test_consume_xlsx.hpp b/source/workbook/tests/test_consume_xlsx.hpp new file mode 100644 index 00000000..6ab8db1e --- /dev/null +++ b/source/workbook/tests/test_consume_xlsx.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include +#include + +class test_consume_xlsx : public CxxTest::TestSuite +{ +}; diff --git a/source/workbook/tests/test_produce_xlsx.hpp b/source/workbook/tests/test_produce_xlsx.hpp new file mode 100644 index 00000000..4a3352e1 --- /dev/null +++ b/source/workbook/tests/test_produce_xlsx.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include +#include + +class test_produce_xlsx : public CxxTest::TestSuite +{ +public: + bool workbook_matches_file(xlnt::workbook &wb, const xlnt::path &file) + { + std::vector buffer; + wb.save(buffer); + wb.save(xlnt::path("C:\\Users\\Thomas\\Desktop\\a.xlsx")); + xlnt::zip_file wb_archive(buffer); + + xlnt::zip_file file_archive(file); + + return xml_helper::archives_match(wb_archive, file_archive); + } + + void test_empty_excel() + { + xlnt::workbook wb = xlnt::workbook::empty_excel(); + TS_ASSERT(workbook_matches_file(wb, path_helper::get_data_directory("xlsx/8_default-excel.xlsx"))); + } +}; diff --git a/source/workbook/tests/test_read.hpp b/source/workbook/tests/test_read.hpp deleted file mode 100644 index 802bdb0f..00000000 --- a/source/workbook/tests/test_read.hpp +++ /dev/null @@ -1,438 +0,0 @@ -#pragma once - -#include -#include -#include - -#include -#include -#include -#include - -class test_read : public CxxTest::TestSuite -{ -public: - - xlnt::workbook standard_workbook() - { - xlnt::workbook wb; - auto path = path_helper::get_data_directory("genuine/empty.xlsx"); - wb.load(path); - - return wb; - } - - void test_read_standard_workbook() - { - TS_ASSERT_THROWS_NOTHING(standard_workbook()); - } - - void test_read_standard_workbook_from_fileobj() - { - auto path = path_helper::get_data_directory("genuine/empty.xlsx"); - std::ifstream fo(path.to_string(), std::ios::binary); - - xlnt::workbook wb; - wb.load(fo); - - TS_ASSERT(wb.get_sheet_titles().size() == 1); - } - - void test_read_worksheet() - { - auto wb = standard_workbook(); - auto sheet2 = wb.get_sheet_by_title("Sheet2 - Numbers"); - - TS_ASSERT_DIFFERS(sheet2, nullptr); - TS_ASSERT_EQUALS("This is cell G5", sheet2.get_cell("G5").get_value()); - TS_ASSERT_EQUALS(18, sheet2.get_cell("D18").get_value()); - TS_ASSERT_EQUALS(true, sheet2.get_cell("G9").get_value()); - TS_ASSERT_EQUALS(false, sheet2.get_cell("G10").get_value()); - } - - void test_read_nostring_workbook() - { - auto path = path_helper::get_data_directory("genuine/empty-no-string.xlsx"); - - xlnt::workbook wb; - TS_ASSERT_THROWS_NOTHING(wb.load(path)); - } - - void test_read_empty_file() - { - xlnt::workbook wb; - TS_ASSERT_THROWS(wb.load(path_helper::get_data_directory("reader/null_file.xlsx")), xlnt::invalid_file); - } - - void test_read_empty_archive() - { - xlnt::workbook wb; - TS_ASSERT_THROWS(wb.load(path_helper::get_data_directory("reader/null_archive.xlsx")), xlnt::invalid_file); - } - - void test_read_workbook_with_no_properties() - { - xlnt::workbook wb; - TS_ASSERT_THROWS_NOTHING(wb.load(path_helper::get_data_directory("genuine/empty_with_no_properties.xlsx"))); - } - - xlnt::workbook workbook_with_styles() - { - xlnt::workbook wb; - wb.load(path_helper::get_data_directory("genuine/empty-with-styles.xlsx")); - - return wb; - } - - void test_read_workbook_with_styles_general() - { - auto wb = workbook_with_styles(); - auto ws = wb["Sheet1"]; - auto format = ws.get_cell("A1").get_number_format(); - auto expected = xlnt::number_format::general(); - TS_ASSERT_EQUALS(format, expected); - } - - void test_read_workbook_with_styles_date() - { - auto wb = workbook_with_styles(); - auto ws = wb["Sheet1"]; - auto format = ws.get_cell("A2").get_number_format(); - auto expected = xlnt::number_format::date_xlsx14(); - TS_ASSERT_EQUALS(format, expected); - } - - void test_read_workbook_with_styles_number() - { - auto wb = workbook_with_styles(); - auto ws = wb["Sheet1"]; - auto code = ws.get_cell("A3").get_number_format(); - auto expected = xlnt::number_format::number_00(); - TS_ASSERT_EQUALS(code, expected); - } - - void test_read_workbook_with_styles_time() - { - auto wb = workbook_with_styles(); - auto ws = wb["Sheet1"]; - auto code = ws.get_cell("A4").get_number_format(); - auto expected = xlnt::number_format::date_time3(); - TS_ASSERT_EQUALS(code, expected); - } - - void test_read_workbook_with_styles_percentage() - { - auto wb = workbook_with_styles(); - auto ws = wb["Sheet1"]; - auto code = ws.get_cell("A5").get_number_format(); - auto expected = xlnt::number_format::percentage_00(); - TS_ASSERT_EQUALS(code, expected); - } - - void test_read_charset_excel() - { - xlnt::workbook wb; - auto path = path_helper::get_data_directory("reader/charset-excel.xlsx"); - wb.load(path); - - auto ws = wb["Sheet1"]; - auto val = ws.get_cell("A1").get_value(); - TS_ASSERT_EQUALS(val, "Direnç"); - } - - void test_read_shared_strings_max_range() - { - xlnt::workbook wb; - const auto path = path_helper::get_data_directory("reader/shared_strings-max_range.xlsx"); - wb.load(path); - - auto ws = wb["Sheet1"]; - auto val = ws.get_cell("A1").get_value(); - TS_ASSERT_EQUALS(val, "Donald"); - } - - void test_read_shared_strings_multiple_r_nodes() - { - auto path = path_helper::get_data_directory("reader/shared_strings-multiple_r_nodes.xlsx"); - - xlnt::workbook wb; - wb.load(path); - - auto ws = wb["Sheet1"]; - auto val = ws.get_cell("A1").get_value(); - TS_ASSERT_EQUALS(val, "abcdef"); - } - - xlnt::workbook date_mac_1904() - { - xlnt::workbook wb; - wb.load(path_helper::get_data_directory("reader/date_1904.xlsx")); - - return wb; - } - - xlnt::workbook date_std_1900() - { - xlnt::workbook wb; - wb.load(path_helper::get_data_directory("reader/date_1900.xlsx")); - - return wb; - } - - void test_read_win_base_date() - { - auto wb = date_std_1900(); - TS_ASSERT_EQUALS(wb.get_base_date(), xlnt::calendar::windows_1900); - } - - void test_read_mac_base_date() - { - auto wb = date_mac_1904(); - TS_ASSERT_EQUALS(wb.get_base_date(), xlnt::calendar::mac_1904); - } - - void test_read_date_style_win() - { - auto wb = date_std_1900(); - auto ws = wb["Sheet1"]; - TS_ASSERT_EQUALS(ws.get_cell("A1").get_number_format(), xlnt::number_format::date_xlsx14()); - } - - void test_read_date_style_mac() - { - auto wb = date_mac_1904(); - auto ws = wb["Sheet1"]; - TS_ASSERT_EQUALS(ws.get_cell("A1").get_number_format(), xlnt::number_format::date_xlsx14()); - } - - void test_read_compare_mac_win_dates() - { - auto wb_mac = date_mac_1904(); - auto ws_mac = wb_mac["Sheet1"]; - auto wb_win = date_std_1900(); - auto ws_win = wb_win["Sheet1"]; - xlnt::datetime dt(2011, 10, 31); - TS_ASSERT_EQUALS(ws_mac.get_cell("A1").get_value(), dt); - TS_ASSERT_EQUALS(ws_win.get_cell("A1").get_value(), dt); - TS_ASSERT_EQUALS(ws_mac.get_cell("A1").get_value(), ws_win.get_cell("A1").get_value()); - } - - void test_read_no_theme() - { - auto path = path_helper::get_data_directory("genuine/libreoffice_nrt.xlsx"); - - xlnt::workbook wb; - TS_ASSERT_THROWS_NOTHING(wb.load(path)); - } - - void _test_read_complex_formulae() - { - /* - auto path = PathHelper::GetDataDirectory("reader/formulae.xlsx"); - auto wb = xlnt::reader::load_workbook(path); - auto ws = wb.get_active_sheet(); - - // Test normal forumlae - TS_ASSERT(!ws.get_cell("A1").has_formula()); - TS_ASSERT(!ws.get_cell("A2").has_formula()); - TS_ASSERT(ws.get_cell("A3").has_formula()); - TS_ASSERT(ws.get_formula_attributes().find("A3") == ws.get_formula_attributes().end()); - TS_ASSERT(ws.get_cell("A3").get_formula() == "12345"); - TS_ASSERT(ws.get_cell("A4").has_formula()); - TS_ASSERT(ws.get_formula_attributes().find("A3") == ws.get_formula_attributes().end()); - ws.get_cell("A4").set_formula("A2+A3"); - TS_ASSERT(ws.get_cell("A5").has_formula()); - TS_ASSERT(ws.get_formula_attributes().find("A5") == ws.get_formula_attributes().end()); - ws.get_cell("A5").set_formula("SUM(A2:A4)"); - - // Test unicode - std::string expected = "=IF(ISBLANK(B16), \"D\xFCsseldorf\", B16)"; - TS_ASSERT(ws.get_cell("A16").get_formula() == expected); - - // Test shared forumlae - TS_ASSERT(ws.get_cell("B7").get_data_type() == "f"); - TS_ASSERT(ws.formula_attributes["B7"]["t"] == "shared"); - TS_ASSERT(ws.formula_attributes["B7"]["si"] == "0"); - TS_ASSERT(ws.formula_attributes["B7"]["ref"] == "B7:E7"); - TS_ASSERT(ws.get_cell("B7").value == "=B4*2"); - TS_ASSERT(ws.get_cell("C7").get_data_type() == "f"); - TS_ASSERT(ws.formula_attributes["C7"]["t"] == "shared"); - TS_ASSERT(ws.formula_attributes["C7"]["si"] == "0"); - TS_ASSERT("ref" not in ws.formula_attributes["C7"]); - TS_ASSERT(ws.get_cell("C7").value == "="); - TS_ASSERT(ws.get_cell("D7").get_data_type() == "f"); - TS_ASSERT(ws.formula_attributes["D7"]["t"] == "shared"); - TS_ASSERT(ws.formula_attributes["D7"]["si"] == "0"); - TS_ASSERT("ref" not in ws.formula_attributes["D7"]); - TS_ASSERT(ws.get_cell("D7").value == "="); - TS_ASSERT(ws.get_cell("E7").get_data_type() == "f"); - TS_ASSERT(ws.formula_attributes["E7"]["t"] == "shared"); - TS_ASSERT(ws.formula_attributes["E7"]["si"] == "0"); - TS_ASSERT("ref" not in ws.formula_attributes["E7"]); - TS_ASSERT(ws.get_cell("E7").value == "="); - - // Test array forumlae - TS_ASSERT(ws.get_cell("C10").get_data_type() == "f"); - TS_ASSERT("ref" not in ws.formula_attributes["C10"]["ref"]); - TS_ASSERT(ws.formula_attributes["C10"]["t"] == "array"); - TS_ASSERT("si" not in ws.formula_attributes["C10"]); - TS_ASSERT(ws.formula_attributes["C10"]["ref"] == "C10:C14"); - TS_ASSERT(ws.get_cell("C10").value == "=SUM(A10:A14*B10:B14)"); - TS_ASSERT(ws.get_cell("C11").get_data_type() != "f"); - */ - } - - void test_data_only() - { - auto path = path_helper::get_data_directory("reader/formulae.xlsx"); - - xlnt::workbook wb; - wb.set_data_only(true); - wb.load(path); - - auto ws = wb.get_active_sheet(); - - TS_ASSERT(ws.get_formula_attributes().empty()); - TS_ASSERT(ws.get_workbook().get_data_only()); - TS_ASSERT(ws.get_cell("A2").get_data_type() == xlnt::cell::type::numeric); - TS_ASSERT(ws.get_cell("A2").get_value() == 12345); - TS_ASSERT(!ws.get_cell("A2").has_formula()); - TS_ASSERT(ws.get_cell("A3").get_data_type() == xlnt::cell::type::numeric); - TS_ASSERT(ws.get_cell("A3").get_value() == 12345); - TS_ASSERT(!ws.get_cell("A3").has_formula()); - TS_ASSERT(ws.get_cell("A4").get_data_type() == xlnt::cell::type::numeric); - TS_ASSERT(ws.get_cell("A4").get_value() == 24690); - TS_ASSERT(!ws.get_cell("A4").has_formula()); - TS_ASSERT(ws.get_cell("A5").get_data_type() == xlnt::cell::type::numeric); - TS_ASSERT(ws.get_cell("A5").get_value() == 49380); - TS_ASSERT(!ws.get_cell("A5").has_formula()); - } - - void test_read_content_types() - { - std::vector> expected = - { - {"/xl/workbook.xml", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"}, - {"/xl/worksheets/sheet1.xml", "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"}, - {"/xl/chartsheets/sheet1.xml", "application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml"}, - {"/xl/worksheets/sheet2.xml", "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"}, - {"/xl/theme/theme1.xml", "application/vnd.openxmlformats-officedocument.theme+xml"}, - {"/xl/styles.xml", "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"}, - {"/xl/sharedStrings.xml", "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"}, - {"/xl/drawings/drawing1.xml", "application/vnd.openxmlformats-officedocument.drawing+xml"}, - {"/xl/charts/chart1.xml", "application/vnd.openxmlformats-officedocument.drawingml.chart+xml"}, - {"/xl/drawings/drawing2.xml", "application/vnd.openxmlformats-officedocument.drawing+xml"}, - {"/xl/charts/chart2.xml", "application/vnd.openxmlformats-officedocument.drawingml.chart+xml"}, - {"/xl/calcChain.xml", "application/vnd.openxmlformats-officedocument.spreadsheetml.calcChain+xml"}, - {"/docProps/core.xml", "application/vnd.openxmlformats-package.core-properties+xml"}, - {"/docProps/app.xml", "application/vnd.openxmlformats-officedocument.extended-properties+xml"} - }; - - auto path = path_helper::get_data_directory("reader/contains_chartsheets.xlsx"); - - xlnt::workbook wb; - wb.load(path); - - auto &result = wb.get_manifest().get_parts(); - - if(result.size() != expected.size()) - { - TS_ASSERT_EQUALS(result.size(), expected.size()); - return; - } - - for(std::size_t i = 0; i < expected.size(); i++) - { - TS_ASSERT(wb.get_manifest().has_part(xlnt::path(expected[i].first))); - TS_ASSERT_EQUALS(wb.get_manifest().get_part_content_type_string(xlnt::path(expected[i].first)), expected[i].second); - } - } - - void test_guess_types() - { - bool guess; - xlnt::cell::type dtype; - std::vector> test_cases = {{true, xlnt::cell::type::numeric}, {false, xlnt::cell::type::string}}; - - for(const auto &expected : test_cases) - { - std::tie(guess, dtype) = expected; - auto path = path_helper::get_data_directory("genuine/guess_types.xlsx"); - - xlnt::workbook wb; - wb.set_guess_types(guess); - wb.load(path); - - auto ws = wb.get_active_sheet(); - TS_ASSERT(ws.get_cell("D2").get_data_type() == dtype); - } - } - - void test_read_autofilter() - { - auto path = path_helper::get_data_directory("reader/bug275.xlsx"); - - xlnt::workbook wb; - wb.load(path); - - auto ws = wb.get_active_sheet(); - TS_ASSERT_EQUALS(ws.get_auto_filter().to_string(), "A1:B6"); - } - - void test_bad_formats_xlsb() - { - auto path = path_helper::get_data_directory("genuine/a.xlsb"); - xlnt::workbook wb; - TS_ASSERT_THROWS(wb.load(path), xlnt::invalid_file); - } - - void test_bad_formats_xls() - { - auto path = path_helper::get_data_directory("genuine/a.xls"); - xlnt::workbook wb; - TS_ASSERT_THROWS(wb.load(path), xlnt::invalid_file); - } - - void test_bad_formats_no() - { - auto path = path_helper::get_data_directory("genuine/a.no-format"); - xlnt::workbook wb; - TS_ASSERT_THROWS(wb.load(path), xlnt::invalid_file); - } - - - void test_read_shared_strings_with_runs() - { - xlnt::workbook wb; - wb.load("reader/genuine/a.xlsx"); - - const auto &strings = wb.get_shared_strings(); - - TS_ASSERT_EQUALS(strings.size(), 1); - TS_ASSERT_EQUALS(strings.front().get_runs().size(), 1); - TS_ASSERT_EQUALS(strings.front().get_runs().front().get_size(), 13); - TS_ASSERT_EQUALS(strings.front().get_runs().front().get_color(), "color"); - TS_ASSERT_EQUALS(strings.front().get_runs().front().get_font(), "font"); - TS_ASSERT_EQUALS(strings.front().get_runs().front().get_family(), 12); - TS_ASSERT_EQUALS(strings.front().get_runs().front().get_scheme(), "scheme"); - } - - void test_read_inlinestr() - { - xlnt::workbook wb; - wb.load(path_helper::get_data_directory("genuine/empty.xlsx")); - TS_ASSERT_EQUALS(wb.get_sheet_by_index(0).get_cell("A1").get_value(), "This is cell A1 in Sheet 1"); - } - - void test_determine_document_type() - { - xlnt::workbook wb; - TS_ASSERT_THROWS(wb.load(path_helper::get_data_directory("1_empty.txt")), xlnt::invalid_file); - TS_ASSERT_THROWS(wb.load(path_helper::get_data_directory("2_not-empty.txt")), xlnt::invalid_file); - TS_ASSERT_THROWS(wb.load(path_helper::get_data_directory("3_empty.zip")), xlnt::invalid_file); - TS_ASSERT_THROWS(wb.load(path_helper::get_data_directory("4_not-package.zip")), xlnt::invalid_file); - TS_ASSERT_THROWS(wb.load(path_helper::get_data_directory("5_visio.vsdx")), xlnt::invalid_file); - TS_ASSERT_THROWS(wb.load(path_helper::get_data_directory("6_document.docx")), xlnt::invalid_file); - TS_ASSERT_THROWS(wb.load(path_helper::get_data_directory("7_presentation.pptx")), xlnt::invalid_file); - } -}; diff --git a/source/workbook/tests/test_round_trip.hpp b/source/workbook/tests/test_round_trip.hpp new file mode 100644 index 00000000..0d3dba15 --- /dev/null +++ b/source/workbook/tests/test_round_trip.hpp @@ -0,0 +1,82 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include +#include + +class test_round_trip : public CxxTest::TestSuite +{ +public: + bool round_trip_matches_wr(const xlnt::workbook &original) + { + std::vector serialized; + original.save(serialized); + + xlnt::workbook deserialized; + deserialized.load(serialized); + + return xml_helper::workbooks_match(original, deserialized); + } + + bool round_trip_matches_rw(const xlnt::path &file) + { + xlnt::zip_file original_archive(file); + + xlnt::workbook deserialized; + deserialized.load(file); + + std::vector serialized; + deserialized.save(serialized); + + xlnt::zip_file resulting_archive(serialized); + + return xml_helper::archives_match(original_archive, resulting_archive); + } + + void test_round_trip_minimal_wr() + { + TS_SKIP(""); + xlnt::workbook wb = xlnt::workbook::minimal(); + TS_ASSERT(round_trip_matches_wr(wb)); + } + + void test_round_trip_empty_excel_wr() + { + TS_SKIP(""); + xlnt::workbook wb = xlnt::workbook::empty_excel(); + TS_ASSERT(round_trip_matches_wr(wb)); + } + + void test_round_trip_empty_libre_office_wr() + { + TS_SKIP(""); + xlnt::workbook wb = xlnt::workbook::empty_libre_office(); + TS_ASSERT(round_trip_matches_wr(wb)); + } + + void test_round_trip_empty_pages_wr() + { + TS_SKIP(""); + xlnt::workbook wb = xlnt::workbook::empty_numbers(); + TS_ASSERT(round_trip_matches_wr(wb)); + } + + void test_round_trip_empty_excel_rw() + { + TS_SKIP(""); + auto path = path_helper::get_data_directory("xlsx/8_default-excel.xlsx"); + TS_ASSERT(round_trip_matches_rw(path)); + } + + void test_round_trip_empty_libre_rw() + { + TS_SKIP(""); + auto path = path_helper::get_data_directory("xlsx/9_default-libre-office.xlsx"); + TS_ASSERT(round_trip_matches_rw(path)); + } +}; diff --git a/source/workbook/tests/test_style_reader.hpp b/source/workbook/tests/test_style_reader.hpp deleted file mode 100644 index 1f7d993e..00000000 --- a/source/workbook/tests/test_style_reader.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include -#include - -#include -#include - -class test_style_reader : public CxxTest::TestSuite -{ -public: - void test_complex_formatting() - { - xlnt::workbook wb; - wb.load(path_helper::get_data_directory("reader/formatting.xlsx")); - - // border_style - auto ws = wb.get_active_sheet(); - auto e30 = ws.get_cell("E30"); - TS_ASSERT_EQUALS(e30.get_border().get_side(xlnt::border::side::top).get_color().get_indexed().get_index(), 10); - TS_ASSERT_EQUALS(e30.get_border().get_side(xlnt::border::side::top).get_style(), xlnt::border_style::thin); - - // underline_style - auto f30 = ws.get_cell("F30"); - TS_ASSERT_EQUALS(e30.get_font().get_underline(), xlnt::font::underline_style::none); - TS_ASSERT_EQUALS(f30.get_font().get_underline(), xlnt::font::underline_style::single); - - // gradient fill - auto e21 = ws.get_cell("E21"); - TS_ASSERT_EQUALS(e21.get_fill().get_type(), xlnt::fill::type::gradient); - TS_ASSERT_EQUALS(e21.get_fill().get_gradient_fill().get_type(), xlnt::gradient_fill::type::linear); - TS_ASSERT_EQUALS(e21.get_fill().get_gradient_fill().get_stops().size(), 2); - TS_ASSERT_EQUALS(e21.get_fill().get_gradient_fill().get_stops().at(0).get_rgb().get_hex_string(), "ffff0000"); - TS_ASSERT_EQUALS(e21.get_fill().get_gradient_fill().get_stops().at(1).get_rgb().get_hex_string(), "ff0000ff"); - } -}; diff --git a/source/workbook/tests/test_style_writer.hpp b/source/workbook/tests/test_style_writer.hpp deleted file mode 100644 index c616fe5b..00000000 --- a/source/workbook/tests/test_style_writer.hpp +++ /dev/null @@ -1,365 +0,0 @@ -#pragma once - -#include -#include - -#include - -class test_style_writer : public CxxTest::TestSuite -{ -public: - bool style_xml_matches(const std::string &expected_string, xlnt::workbook &wb) - { - std::vector bytes; - wb.save(bytes); - - xlnt::zip_file archive; - archive.load(bytes); - - pugi::xml_document observed; - observed.load(archive.read(xlnt::constants::part_styles()).c_str()); - - return xml_helper::string_matches_document(expected_string, observed); - } - - void test_write_custom_number_format() - { - xlnt::workbook wb; - wb.get_active_sheet().get_cell("A1").set_number_format(xlnt::number_format("YYYY")); - auto expected = - "" - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - ""; - TS_ASSERT(style_xml_matches(expected, wb)); - } - - void test_simple_styles() - { - xlnt::workbook wb; - wb.set_guess_types(true); - auto ws = wb.get_active_sheet(); - ws.get_cell("A1").set_value("12.34%"); - auto now = xlnt::date::today(); - ws.get_cell("A2").set_value(now); - ws.get_cell("A3").set_value("This is a test"); - ws.get_cell("A4").set_value("31.31415"); - ws.get_cell("A5"); - - ws.get_cell("D9").set_number_format(xlnt::number_format::number_00()); - xlnt::protection locked(true, false); - ws.get_cell("D9").set_protection(locked); - xlnt::protection hidden(true, true); - ws.get_cell("E1").set_protection(hidden); - - xlnt::border b; - xlnt::border::border_property prop; - prop.set_style(xlnt::border_style::dashdot); - prop.set_color(xlnt::rgb_color("ffff0000")); - b.set_side(xlnt::border::side::top, prop); - ws.get_cell("D10").set_border(b); - - auto expected = path_helper::get_data_directory("writer/expected/simple-styles.xml"); - TS_ASSERT(style_xml_matches(expected.read_contents(), wb)); - } - - void test_empty_workbook() - { - xlnt::workbook wb; - std::string expected = - "" - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - ""; - TS_ASSERT(style_xml_matches(expected, wb)); - } - - void test_complex_font() - { - xlnt::font f; - f.set_bold(true); - f.set_color(xlnt::color::red()); - f.set_family(3); - f.set_italic(true); - f.set_name("Consolas"); - f.set_scheme("major"); - f.set_size(21); - f.set_strikethrough(true); - f.set_underline(xlnt::font::underline_style::double_accounting); - - xlnt::workbook wb; - wb.get_active_sheet().get_cell("A1").set_font(f); - - std::string expected = - "" - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - ""; - - TS_ASSERT(style_xml_matches(expected, wb)); - } - - void test_alignment() - { - xlnt::workbook wb; - xlnt::alignment a; - a.set_horizontal(xlnt::horizontal_alignment::center_continuous); - a.set_vertical(xlnt::vertical_alignment::justify); - a.set_wrap_text(true); - a.set_shrink_to_fit(true); - wb.get_active_sheet().get_cell("A1").set_alignment(a); - - std::string expected = - "" - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - ""; - TS_ASSERT(style_xml_matches(expected, wb)); - } - - void test_fill() - { - xlnt::workbook wb; - - xlnt::fill pattern_fill = xlnt::fill::pattern(xlnt::pattern_fill::type::solid); - pattern_fill.get_pattern_fill().set_foreground_color(xlnt::color::red()); - pattern_fill.get_pattern_fill().set_background_color(xlnt::color::blue()); - wb.get_active_sheet().get_cell("A1").set_fill(pattern_fill); - - xlnt::fill gradient_fill_linear = xlnt::fill::gradient(xlnt::gradient_fill::type::linear); - gradient_fill_linear.get_gradient_fill().set_degree(90); - wb.get_active_sheet().get_cell("A1").set_fill(gradient_fill_linear); - - xlnt::fill gradient_fill_path = xlnt::fill::gradient(xlnt::gradient_fill::type::path); - gradient_fill_path.get_gradient_fill().set_gradient_left(1); - gradient_fill_path.get_gradient_fill().set_gradient_right(2); - gradient_fill_path.get_gradient_fill().set_gradient_top(3); - gradient_fill_path.get_gradient_fill().set_gradient_bottom(4); - gradient_fill_path.get_gradient_fill().add_stop(0, xlnt::color::red()); - gradient_fill_path.get_gradient_fill().add_stop(1, xlnt::color::blue()); - wb.get_active_sheet().get_cell("A1").set_fill(gradient_fill_path); - - std::string expected = - "" - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - ""; - TS_ASSERT(style_xml_matches(expected, wb)); - } -}; diff --git a/source/workbook/tests/test_theme.hpp b/source/workbook/tests/test_theme.hpp index e4eb7a04..46a70a34 100644 --- a/source/workbook/tests/test_theme.hpp +++ b/source/workbook/tests/test_theme.hpp @@ -9,14 +9,4 @@ class test_theme : public CxxTest::TestSuite { -public: - void test_write_theme() - { - xlnt::workbook wb; - wb.set_theme(xlnt::theme()); - - TS_ASSERT(xml_helper::file_matches_workbook_part( - path_helper::get_data_directory("writer/expected/theme1.xml"), - wb, xlnt::constants::part_theme())); - } }; diff --git a/source/workbook/tests/test_workbook.hpp b/source/workbook/tests/test_workbook.hpp index 0947a4ca..34350505 100644 --- a/source/workbook/tests/test_workbook.hpp +++ b/source/workbook/tests/test_workbook.hpp @@ -173,40 +173,6 @@ public: TS_ASSERT_THROWS(wb.remove_named_range("test_nr2"), std::runtime_error); } - void test_write_regular_date() - { - const xlnt::datetime today(2010, 1, 18, 14, 15, 20, 1600); - - xlnt::workbook book; - auto sheet = book.get_active_sheet(); - sheet.get_cell("A1").set_value(today); - temporary_file temp_file; - book.save(temp_file.get_path()); - - xlnt::workbook test_book; - test_book.load(temp_file.get_path()); - auto test_sheet = test_book.get_active_sheet(); - - TS_ASSERT_EQUALS(test_sheet.get_cell("A1").get_value(), today); - } - - void test_write_regular_float() - { - long double float_value = 1.0L / 3.0L; - - xlnt::workbook book; - auto sheet = book.get_active_sheet(); - sheet.get_cell("A1").set_value(float_value); - temporary_file temp_file; - book.save(temp_file.get_path()); - - xlnt::workbook test_book; - test_book.load(temp_file.get_path()); - auto test_sheet = test_book.get_active_sheet(); - - TS_ASSERT_EQUALS(test_sheet.get_cell("A1").get_value(), float_value); - } - void test_post_increment_iterator() { xlnt::workbook wb; @@ -262,7 +228,7 @@ 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_part(xlnt::path("xl/workbook.xml"))); + 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); } diff --git a/source/workbook/tests/test_write.hpp b/source/workbook/tests/test_write.hpp deleted file mode 100644 index 7ffff15c..00000000 --- a/source/workbook/tests/test_write.hpp +++ /dev/null @@ -1,327 +0,0 @@ -#pragma once - -#include -#include - -#include -#include -#include -#include - -class test_write : public CxxTest::TestSuite -{ -public: - void test_write_empty_workbook() - { - xlnt::workbook wbk; - wbk.get_active_sheet().get_cell("A2").set_value("xlnt"); - wbk.get_active_sheet().get_cell("B5").set_value(88); - wbk.get_active_sheet().get_cell("B5").set_number_format(xlnt::number_format::percentage_00()); - wbk.save(temp_file.get_path()); - - if(temp_file.get_path().exists()) - { - path_helper::delete_file(temp_file.get_path()); - } - - TS_ASSERT(!temp_file.get_path().exists()); - wb_.save(temp_file.get_path()); - TS_ASSERT(temp_file.get_path().exists()); - } - - void test_write_virtual_workbook() - { - xlnt::workbook old_wb; - std::vector saved_wb; - old_wb.save(saved_wb); - xlnt::workbook new_wb; - new_wb.load(saved_wb); - TS_ASSERT_EQUALS(old_wb, new_wb); - } - - void test_write_workbook_rels() - { - xlnt::workbook wb; - wb.add_shared_string(xlnt::text()); - - TS_ASSERT(xml_helper::file_matches_workbook_part( - path_helper::get_data_directory("writer/expected/workbook.xml.rels"), - wb, xlnt::path("xl/_rels/workbook.xml.rels"))); - } - - void test_write_workbook() - { - xlnt::workbook wb; - - TS_ASSERT(xml_helper::file_matches_workbook_part( - path_helper::get_data_directory("writer/expected/workbook.xml"), - wb, xlnt::path("xl/workbook.xml"))); - } - - void test_write_string_table() - { - xlnt::workbook wb; - auto ws = wb.get_active_sheet(); - - ws.get_cell("A1").set_value("hello"); - ws.get_cell("A2").set_value("world"); - ws.get_cell("A3").set_value("nice"); - - TS_ASSERT(xml_helper::file_matches_workbook_part( - path_helper::get_data_directory("writer/expected/sharedStrings.xml"), - wb, xlnt::path("xl/sharedStrings.xml"))); - } - - void test_write_worksheet() - { - xlnt::workbook wb; - auto ws = wb.get_active_sheet(); - - ws.get_cell("F42").set_value("hello"); - - TS_ASSERT(xml_helper::file_matches_workbook_part( - path_helper::get_data_directory("writer/expected/sheet1.xml"), - wb, xlnt::path("xl/worksheets/sheet1.xml"))); - } - - void test_write_hidden_worksheet() - { - xlnt::workbook wb; - auto ws = wb.get_active_sheet(); - - ws.get_page_setup().set_sheet_state(xlnt::sheet_state::hidden); - ws.get_cell("F42").set_value("hello"); - - TS_ASSERT(xml_helper::file_matches_workbook_part( - path_helper::get_data_directory("writer/expected/sheet1.xml"), - wb, xlnt::path("xl/worksheets/sheet1.xml"))); - } - - void test_write_bool() - { - xlnt::workbook wb; - auto ws = wb.get_active_sheet(); - - ws.get_cell("F42").set_value(false); - ws.get_cell("F43").set_value(true); - - TS_ASSERT(xml_helper::file_matches_workbook_part( - path_helper::get_data_directory("writer/expected/sheet1_bool.xml"), - wb, xlnt::path("xl/worksheets/sheet1.xml"))); - } - - void test_write_formula() - { - xlnt::workbook wb; - auto ws = wb.get_active_sheet(); - - ws.get_cell("F1").set_value(10); - ws.get_cell("F2").set_value(32); - ws.get_cell("F3").set_formula("F1+F2"); - - TS_ASSERT(xml_helper::file_matches_workbook_part( - path_helper::get_data_directory("writer/expected/sheet1_bool.xml"), - wb, xlnt::path("writer/expected/sheet1_formula.xml"))); - } - - void test_write_height() - { - xlnt::workbook wb; - auto ws = wb.get_active_sheet(); - - ws.get_cell("F1").set_value(10); - ws.get_row_properties(ws.get_cell("F1").get_row()).height = 30; - - TS_ASSERT(xml_helper::file_matches_workbook_part( - path_helper::get_data_directory("writer/expected/sheet1_height.xml"), - wb, xlnt::path("xl/worksheets/sheet1.xml"))); - } - - void test_write_hyperlink() - { - xlnt::workbook wb; - auto ws = wb.get_active_sheet(); - - ws.get_cell("A1").set_value("test"); - ws.get_cell("A1").set_hyperlink("http://test.com"); - - TS_ASSERT(xml_helper::file_matches_workbook_part( - path_helper::get_data_directory("writer/expected/sheet1_hyperlink.xml"), - wb, xlnt::path("xl/worksheets/sheet1.xml"))); - } - - void test_write_hyperlink_rels() - { - xlnt::workbook wb; - auto ws = wb.get_active_sheet(); - - ws.get_cell("A1").set_value("test"); - ws.get_cell("A1").set_hyperlink("http://test.com/"); - ws.get_cell("A2").set_value("test"); - ws.get_cell("A2").set_hyperlink("http://test2.com/"); - - TS_ASSERT(xml_helper::file_matches_workbook_part( - path_helper::get_data_directory("writer/expected/sheet1_hyperlink.xml.rels"), - wb, xlnt::path("xl/worksheets/_rels/sheet1.xml.rels"))); - } - - void _test_write_hyperlink_image_rels() - { - TS_SKIP("not implemented"); - } - - void test_hyperlink_value() - { - xlnt::workbook wb; - auto ws = wb.get_active_sheet(); - - ws.get_cell("A1").set_hyperlink("http://test.com"); - TS_ASSERT_EQUALS("http://test.com", ws.get_cell("A1").get_value()); - ws.get_cell("A1").set_value("test"); - TS_ASSERT_EQUALS("test", ws.get_cell("A1").get_value()); - } - - void test_write_auto_filter() - { - xlnt::workbook wb; - auto ws = wb.get_active_sheet(); - - ws.get_cell("F42").set_value("hello"); - ws.auto_filter("A1:F1"); - - TS_ASSERT(xml_helper::file_matches_workbook_part( - path_helper::get_data_directory("writer/expected/sheet1_auto_filter.xml"), - wb, xlnt::path("xl/worksheets/sheet1.xml"))); - - TS_ASSERT(xml_helper::file_matches_workbook_part( - path_helper::get_data_directory("writer/expected/workbook_auto_filter.xml"), - wb, xlnt::path("xl/workbook.xml"))); - } - - void test_write_auto_filter_filter_column() - { - - } - - void test_write_auto_filter_sort_condition() - { - - } - - void test_freeze_panes_horiz() - { - xlnt::workbook wb; - auto ws = wb.get_active_sheet(); - - ws.get_cell("F42").set_value("hello"); - ws.freeze_panes("A4"); - - TS_ASSERT(xml_helper::file_matches_workbook_part( - path_helper::get_data_directory("writer/expected/sheet1_freeze_panes_horiz.xml"), - wb, xlnt::path("xl/worksheets/sheet1.xml"))); - } - - void test_freeze_panes_vert() - { - xlnt::workbook wb; - auto ws = wb.get_active_sheet(); - - ws.get_cell("F42").set_value("hello"); - ws.freeze_panes("D1"); - - TS_ASSERT(xml_helper::file_matches_workbook_part( - path_helper::get_data_directory("writer/expected/sheet1_freeze_panes_vert.xml"), - wb, xlnt::path("xl/worksheets/sheet1.xml"))); - } - - void test_freeze_panes_both() - { - xlnt::workbook wb; - auto ws = wb.get_active_sheet(); - ws.get_cell("F42").set_value("hello"); - ws.freeze_panes("D4"); - - TS_ASSERT(xml_helper::file_matches_workbook_part( - path_helper::get_data_directory("writer/expected/sheet1_freeze_panes_both.xml"), - wb, xlnt::path("xl/worksheets/sheet1.xml"))); - } - - void test_long_number() - { - xlnt::workbook wb; - auto ws = wb.get_active_sheet(); - ws.get_cell("A1").set_value(9781231231230LL); - - TS_ASSERT(xml_helper::file_matches_workbook_part( - path_helper::get_data_directory("writer/expected/long_number.xml"), - wb, xlnt::path("xl/worksheets/sheet1.xml"))); - } - - void test_short_number() - { - xlnt::workbook wb; - auto ws = wb.get_active_sheet(); - ws.get_cell("A1").set_value(1234567890); - - TS_ASSERT(xml_helper::file_matches_workbook_part( - path_helper::get_data_directory("writer/expected/short_number.xml"), - wb_, xlnt::path("xl/worksheets/sheet1.xml"))); - } - - void test_write_page_setup() - { - xlnt::workbook wb; - auto ws = wb.get_active_sheet(); - - auto &page_setup = ws.get_page_setup(); - TS_ASSERT(page_setup.is_default()); - - page_setup.set_break(xlnt::page_break::column); - page_setup.set_fit_to_height(true); - page_setup.set_fit_to_page(true); - page_setup.set_fit_to_width(true); - page_setup.set_horizontal_centered(true); - page_setup.set_orientation(xlnt::orientation::landscape); - page_setup.set_paper_size(xlnt::paper_size::a5); - page_setup.set_scale(4.0); - page_setup.set_sheet_state(xlnt::sheet_state::visible); - page_setup.set_vertical_centered(true); - - TS_ASSERT(!page_setup.is_default()); - - std::vector bytes; - wb.save(bytes); - - xlnt::zip_file archive; - archive.load(bytes); - auto worksheet_xml_string = archive.read(xlnt::path("xl/worksheets/sheet1.xml")); - - pugi::xml_document worksheet_xml; - worksheet_xml.load(worksheet_xml_string.c_str()); - - std::string expected = - "" - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - ""; - - TS_ASSERT(xml_helper::string_matches_document(expected, worksheet_xml)); - } - - private: - temporary_file temp_file; - xlnt::workbook wb_; -}; diff --git a/source/workbook/tests/test_write_workbook.hpp b/source/workbook/tests/test_write_workbook.hpp deleted file mode 100644 index ff0d4934..00000000 --- a/source/workbook/tests/test_write_workbook.hpp +++ /dev/null @@ -1,219 +0,0 @@ -#pragma once - -#include -#include - -#include -#include -#include - -class test_write_workbook : public CxxTest::TestSuite -{ -public: - void test_write_auto_filter() - { - xlnt::workbook wb; - auto ws = wb.get_active_sheet(); - - ws.get_cell("F42").set_value("hello"); - ws.auto_filter("A1:F1"); - - TS_ASSERT(xml_helper::file_matches_workbook_part( - path_helper::get_data_directory("writer/expected/workbook_auto_filter.xml"), - wb, xlnt::path("xl/workbook.xml"))); - } - - void test_write_hidden_worksheet() - { - xlnt::workbook wb; - auto ws = wb.get_active_sheet(); - - ws.set_sheet_state(xlnt::sheet_state::hidden); - wb.create_sheet(); - - std::string expected_string = - "" - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - ""; - - TS_ASSERT(xml_helper::string_matches_workbook_part(expected_string, - wb, xlnt::path("xl/workbook.xml"))); - } - - void test_write_hidden_single_worksheet() - { - xlnt::workbook wb; - auto ws = wb.get_active_sheet(); - ws.set_sheet_state(xlnt::sheet_state::hidden); - std::vector trash; - TS_ASSERT_THROWS(wb.save(trash), xlnt::no_visible_worksheets); - } - - void test_write_empty_workbook() - { - xlnt::workbook wb; - temporary_file file; - - TS_ASSERT(!file.get_path().exists()) - wb.save(file.get_path()); - TS_ASSERT(file.get_path().exists()); - } - - void test_write_virtual_workbook() - { - xlnt::workbook old_wb, new_wb; - - std::vector wb_bytes; - old_wb.save(wb_bytes); - new_wb.load(wb_bytes); - - // TODO more tests! - } - - void test_write_workbook_rels() - { - xlnt::workbook wb; - auto ws = wb.get_active_sheet(); - ws.get_cell("A1").set_value("string"); - - TS_ASSERT(xml_helper::file_matches_workbook_part( - path_helper::get_data_directory("writer/expected/workbook.xml.rels"), - wb, xlnt::path("xl/_rels/workbook.xml.rels"))); - } - - void test_write_workbook_part() - { - xlnt::workbook wb; - - auto filename = path_helper::get_data_directory("writer/expected/workbook.xml"); - TS_ASSERT(xml_helper::file_matches_workbook_part(filename, wb, xlnt::path("xl/_rels/workbook.xml.rels"))); - } - - void _test_write_named_range() - { - /* - xlnt::workbook wb; - auto ws = wb.get_active_sheet(); - wb.create_named_range("test_range", ws, "$A$1:$B$5"); - xlnt::workbook_serializer serializer(wb); - pugi::xml_document xml; - auto root = xml.root().append_child("root"); - serializer.write_named_ranges(root); - std::string expected = - "" - "'Sheet'!$A$1:$B$5" - ""; - - TS_ASSERT(xml_helper::string_matches_document(expected, xml)); - */ - } - - void test_read_workbook_code_name() - { -// with open(tmpl, "rb") as expected: -// TS_ASSERT(read_workbook_code_name(expected.read()) == code_name - } - - void test_write_workbook_code_name() - { - xlnt::workbook wb; - wb.set_code_name("MyWB"); - - std::string expected = - "" - " " - " " - " " - " " - " " - " " - " " - " " - " " - ""; - - TS_ASSERT(xml_helper::string_matches_workbook_part(expected, wb, xlnt::path("xl/workbook.xml"))); - } - - void test_write_root_rels() - { - xlnt::workbook wb; - - std::string expected = - "" - " " - " " - " " - ""; - - TS_ASSERT(xml_helper::string_matches_workbook_part(expected, wb, xlnt::path("_rels/.rels"))); - } - - void test_write_shared_strings_with_runs() - { - xlnt::workbook wb; - auto ws = wb.get_active_sheet(); - auto cell = ws.get_cell("A1"); - xlnt::text_run run; - run.set_color("color"); - run.set_family(12); - run.set_font("font"); - run.set_scheme("scheme"); - run.set_size(13); - run.set_string("string"); - xlnt::text text; - text.add_run(run); - cell.set_value(text); - - std::string expected = - "" - " " - " " - " " - " " - " " - " " - " " - " " - " " - " string" - " " - " " - ""; - - TS_ASSERT(xml_helper::string_matches_workbook_part(expected, wb, - xlnt::constants::part_shared_strings())); - } - - void test_write_worksheet_order() - { - auto path = path_helper::get_data_directory("genuine/tab_order.xlsx"); - - // Load an original workbook produced by Excel - xlnt::workbook wb_src; - wb_src.load(path); - - // Save it to a new file, unmodified - temporary_file file; - wb_src.save(file.get_path()); - TS_ASSERT(file.get_path().exists()); - - // Load it again - xlnt::workbook wb_dst; - wb_dst.load(file.get_path()); - - TS_ASSERT_EQUALS(wb_src.get_sheet_titles(), wb_dst.get_sheet_titles()); - } - -private: - xlnt::workbook wb_; -}; diff --git a/source/workbook/workbook.cpp b/source/workbook/workbook.cpp index bd1c8fb3..6584f735 100644 --- a/source/workbook/workbook.cpp +++ b/source/workbook/workbook.cpp @@ -63,13 +63,22 @@ workbook workbook::minimal() auto impl = new detail::workbook_impl(); workbook wb(impl); - wb.d_->worksheets_.push_back(detail::worksheet_impl(&wb, 1, "Sheet")); + wb.d_->manifest_.register_default_type("rels", + "application/vnd.openxmlformats-package.relationships+xml"); - wb.d_->manifest_.register_package_part(path("workbook.xml"), - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml", - relationship::type::workbook); - wb.d_->manifest_.register_part(path("sheet1.xml"), path("workbook.xml"), - "worksheet", relationship::type::worksheet); + 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); + + 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); + wb.d_->sheet_title_rel_id_map_[title] = ws_rel; return wb; } @@ -79,15 +88,52 @@ workbook workbook::empty_excel() auto impl = new detail::workbook_impl(); xlnt::workbook wb(impl); - wb.d_->manifest_.register_package_part(path("docProps/core.xml"), - "application/vnd.openxmlformats-package.coreproperties+xml", - relationship::type::core_properties); - wb.d_->manifest_.register_package_part(path("docProps/app.xml"), - "application/vnd.openxmlformats-officedocument.extended-properties+xml", - relationship::type::extended_properties); - wb.d_->manifest_.register_package_part(path("xl/workbook.xml"), - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml", - relationship::type::workbook); + 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_default_type("rels", + "application/vnd.openxmlformats-package.relationships+xml"); + wb.d_->manifest_.register_default_type("xml", "application/xml"); + + const std::vector thumbnail = { + 0xff,0xd8,0xff,0xe0,0x00,0x10,0x4a,0x46,0x49,0x46,0x00,0x01,0x01,0x01, + 0x00,0x60,0x00,0x60,0x00,0x00,0xff,0xe1,0x00,0x16,0x45,0x78,0x69,0x66, + 0x00,0x00,0x49,0x49,0x2a,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0xff,0xdb,0x00,0x43,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01, + 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, + 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, + 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, + 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, + 0x01,0xff,0xdb,0x00,0x43,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, + 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, + 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, + 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, + 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, + 0xff,0xc0,0x00,0x11,0x08,0x00,0x01,0x00,0x01,0x03,0x01,0x22,0x00,0x02, + 0x11,0x01,0x03,0x11,0x01,0xff,0xc4,0x00,0x15,0x00,0x01,0x01,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a, + 0xff,0xc4,0x00,0x14,0x10,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xc4,0x00,0x14,0x01,0x01, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0xff,0xc4,0x00,0x14,0x11,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xda,0x00,0x0c, + 0x03,0x01,0x00,0x02,0x11,0x03,0x11,0x00,0x3f,0x00,0xbf,0x80,0x01,0xff, + 0xd9 + }; + + wb.set_thumbnail(thumbnail, "jpeg", "image/jpeg"); + + 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_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.set_application("Microsoft Excel"); wb.create_sheet(); @@ -236,10 +282,12 @@ worksheet workbook::create_sheet() d_->worksheets_.push_back(detail::worksheet_impl(this, sheet_id, title)); - auto workbook_rel = d_->manifest_.get_package_relationship(relationship::type::workbook); - d_->manifest_.register_part(sheet_path, workbook_rel.get_target_uri(), - "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml", - relationship::type::worksheet); + auto workbook_rel = d_->manifest_.get_package_relationship(relationship::type::office_document); + d_->manifest_.register_override_type(sheet_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); + d_->sheet_title_rel_id_map_[title] = ws_rel; return worksheet(&d_->worksheets_.back()); } @@ -343,24 +391,24 @@ void workbook::load(const path &filename) consumer.read(filename); } -void workbook::save(std::vector &data) +void workbook::save(std::vector &data) const { detail::xlsx_producer producer(*this); producer.write(data); } -void workbook::save(const std::string &filename) +void workbook::save(const std::string &filename) const { return save(path(filename)); } -void workbook::save(const path &filename) +void workbook::save(const path &filename) const { detail::xlsx_producer producer(*this); producer.write(filename); } -void workbook::save(std::ostream &stream) +void workbook::save(std::ostream &stream) const { detail::xlsx_producer producer(*this); producer.write(stream); @@ -563,11 +611,14 @@ const theme &workbook::get_theme() const void workbook::set_theme(const theme &value) { - if (!d_->manifest_.has_part(constants::part_theme())) + auto workbook_rel = d_->manifest_.get_package_relationship(relationship::type::office_document); + + if (!d_->manifest_.has_part_relationship(workbook_rel.get_target_uri(), relationship::type::theme)) { - auto workbook_rel = d_->manifest_.get_package_relationship(relationship::type::workbook); - d_->manifest_.register_part(constants::part_theme(), workbook_rel.get_target_uri(), - "theme", 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_->has_theme_ = true; @@ -591,12 +642,14 @@ std::vector workbook::get_named_ranges() const std::size_t workbook::add_format(const format &to_add) { - if (!d_->manifest_.has_part(constants::part_styles())) + auto workbook_rel = d_->manifest_.get_package_relationship(relationship::type::office_document); + + if (!d_->manifest_.has_part_relationship(workbook_rel.get_target_uri(), relationship::type::styles)) { - auto workbook_rel = d_->manifest_.get_package_relationship(relationship::type::workbook); - d_->manifest_.register_part(constants::part_styles(), workbook_rel.get_target_uri(), - "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml", - 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); } return d_->stylesheet_.add_format(to_add); @@ -604,12 +657,14 @@ std::size_t workbook::add_format(const format &to_add) std::size_t workbook::add_style(const style &to_add) { - if (!d_->manifest_.has_part(constants::part_styles())) + auto workbook_rel = d_->manifest_.get_package_relationship(relationship::type::office_document); + + if (!d_->manifest_.has_part_relationship(workbook_rel.get_target_uri(), relationship::type::styles)) { - auto workbook_rel = d_->manifest_.get_package_relationship(relationship::type::workbook); - d_->manifest_.register_part(constants::part_styles(), workbook_rel.get_target_uri(), - "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml", - 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); } return d_->stylesheet_.add_style(to_add); @@ -686,12 +741,14 @@ const std::vector &workbook::get_shared_strings() const void workbook::add_shared_string(const text &shared, bool allow_duplicates) { - if (!d_->manifest_.has_part(constants::part_shared_strings())) + auto workbook_rel = d_->manifest_.get_package_relationship(relationship::type::office_document); + + if (!d_->manifest_.has_part_relationship(workbook_rel.get_target_uri(), relationship::type::shared_string_table)) { - auto workbook_rel = d_->manifest_.get_package_relationship(relationship::type::workbook); - d_->manifest_.register_part(constants::part_shared_strings(), workbook_rel.get_target_uri(), - "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml", - relationship::type::shared_string_table); + 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); } if (!allow_duplicates) @@ -716,8 +773,13 @@ bool workbook::contains(const std::string &sheet_title) const return false; } -void workbook::set_thumbnail(const std::vector &thumbnail) +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); + d_->thumbnail_.assign(thumbnail.begin(), thumbnail.end()); } diff --git a/source/worksheet/tests/test_worksheet.hpp b/source/worksheet/tests/test_worksheet.hpp index 9c18b116..7dbb756c 100644 --- a/source/worksheet/tests/test_worksheet.hpp +++ b/source/worksheet/tests/test_worksheet.hpp @@ -520,47 +520,32 @@ public: void test_print_titles_old() { xlnt::workbook wb; + + auto ws = wb.create_sheet(); + ws.add_print_title(3); + TS_ASSERT_EQUALS(ws.get_print_titles(), "Sheet1!1:3"); - { - wb.clear(); - auto ws = wb.create_sheet(); - ws.add_print_title(3); - TS_ASSERT_EQUALS(ws.get_print_titles(), "Sheet!1:3"); - } - - { - wb.clear(); - auto ws = wb.create_sheet(); - ws.add_print_title(4, "cols"); - TS_ASSERT_EQUALS(ws.get_print_titles(), "Sheet!A:D"); - } + auto ws2 = wb.create_sheet(); + ws2.add_print_title(4, "cols"); + TS_ASSERT_EQUALS(ws2.get_print_titles(), "Sheet2!A:D"); } void test_print_titles_new() { xlnt::workbook wb; - { - wb.clear(); - auto ws = wb.create_sheet(); - ws.set_print_title_rows("1:4"); - TS_ASSERT_EQUALS(ws.get_print_titles(), "Sheet!1:4"); - } - - { - wb.clear(); - auto ws = wb.create_sheet(); - ws.set_print_title_cols("A:F"); - TS_ASSERT_EQUALS(ws.get_print_titles(), "Sheet!A:F"); - } + auto ws = wb.create_sheet(); + ws.set_print_title_rows("1:4"); + TS_ASSERT_EQUALS(ws.get_print_titles(), "Sheet1!1:4"); - { - wb.clear(); - auto ws = wb.create_sheet(); - ws.set_print_title_rows("1:2"); - ws.set_print_title_cols("C:D"); - TS_ASSERT_EQUALS(ws.get_print_titles(), "Sheet!1:2,Sheet!C:D"); - } + auto ws2 = wb.create_sheet(); + ws2.set_print_title_cols("A:F"); + TS_ASSERT_EQUALS(ws2.get_print_titles(), "Sheet2!A:F"); + + auto ws3 = wb.create_sheet(); + ws3.set_print_title_rows("1:2"); + ws3.set_print_title_cols("C:D"); + TS_ASSERT_EQUALS(ws3.get_print_titles(), "Sheet3!1:2,Sheet3!C:D"); } void test_print_area() diff --git a/tests/data/2_not-empty.txt b/tests/data/2_not-empty.txt deleted file mode 100644 index 041ecf26..00000000 --- a/tests/data/2_not-empty.txt +++ /dev/null @@ -1 +0,0 @@ -not-empty diff --git a/tests/data/4_not-package.zip b/tests/data/4_not-package.zip deleted file mode 100644 index d80e9910..00000000 Binary files a/tests/data/4_not-package.zip and /dev/null differ diff --git a/tests/data/5_visio.zip b/tests/data/5_visio.zip deleted file mode 100644 index 32b6589e..00000000 Binary files a/tests/data/5_visio.zip and /dev/null differ diff --git a/tests/data/complex-styles.xlsx b/tests/data/complex-styles.xlsx deleted file mode 100644 index 0d43e28d..00000000 Binary files a/tests/data/complex-styles.xlsx and /dev/null differ diff --git a/tests/data/writer/expected/Content_types_vba.xml b/tests/data/content_types/Content_types_vba.xml similarity index 100% rename from tests/data/writer/expected/Content_types_vba.xml rename to tests/data/content_types/Content_types_vba.xml diff --git a/tests/data/writer/expected/[Content_Types].xml b/tests/data/content_types/[Content_Types].xml similarity index 100% rename from tests/data/writer/expected/[Content_Types].xml rename to tests/data/content_types/[Content_Types].xml diff --git a/tests/data/writer/expected/core.xml b/tests/data/core_properties/core.xml similarity index 100% rename from tests/data/writer/expected/core.xml rename to tests/data/core_properties/core.xml diff --git a/tests/data/writer/app.xml b/tests/data/extended_properties/app.xml similarity index 100% rename from tests/data/writer/app.xml rename to tests/data/extended_properties/app.xml diff --git a/tests/data/genuine/NameWithValueBug.xlsx b/tests/data/genuine/NameWithValueBug.xlsx deleted file mode 100644 index 0f8a5dc3..00000000 Binary files a/tests/data/genuine/NameWithValueBug.xlsx and /dev/null differ diff --git a/tests/data/genuine/empty-no-string.xlsx b/tests/data/genuine/empty-no-string.xlsx deleted file mode 100644 index 39490ac3..00000000 Binary files a/tests/data/genuine/empty-no-string.xlsx and /dev/null differ diff --git a/tests/data/genuine/empty-with-styles.xlsx b/tests/data/genuine/empty-with-styles.xlsx deleted file mode 100644 index 250e9b43..00000000 Binary files a/tests/data/genuine/empty-with-styles.xlsx and /dev/null differ diff --git a/tests/data/genuine/empty.xlsx b/tests/data/genuine/empty.xlsx deleted file mode 100644 index 1b9e826b..00000000 Binary files a/tests/data/genuine/empty.xlsx and /dev/null differ diff --git a/tests/data/genuine/empty_libre.xlsx b/tests/data/genuine/empty_libre.xlsx deleted file mode 100644 index 9b66aa08..00000000 Binary files a/tests/data/genuine/empty_libre.xlsx and /dev/null differ diff --git a/tests/data/genuine/empty_no_dimensions.xlsx b/tests/data/genuine/empty_no_dimensions.xlsx deleted file mode 100644 index e402883c..00000000 Binary files a/tests/data/genuine/empty_no_dimensions.xlsx and /dev/null differ diff --git a/tests/data/genuine/empty_with_no_properties.xlsx b/tests/data/genuine/empty_with_no_properties.xlsx deleted file mode 100644 index 8eaa8729..00000000 Binary files a/tests/data/genuine/empty_with_no_properties.xlsx and /dev/null differ diff --git a/tests/data/genuine/guess_types.xlsx b/tests/data/genuine/guess_types.xlsx deleted file mode 100644 index 5cfa19ff..00000000 Binary files a/tests/data/genuine/guess_types.xlsx and /dev/null differ diff --git a/tests/data/genuine/libreoffice_nrt.xlsx b/tests/data/genuine/libreoffice_nrt.xlsx deleted file mode 100644 index cdf0724d..00000000 Binary files a/tests/data/genuine/libreoffice_nrt.xlsx and /dev/null differ diff --git a/tests/data/genuine/number_empty_shared_strings.xlsx b/tests/data/genuine/number_empty_shared_strings.xlsx deleted file mode 100644 index 4caadb75..00000000 Binary files a/tests/data/genuine/number_empty_shared_strings.xlsx and /dev/null differ diff --git a/tests/data/genuine/tab_order.xlsx b/tests/data/genuine/tab_order.xlsx deleted file mode 100644 index f84e1b2a..00000000 Binary files a/tests/data/genuine/tab_order.xlsx and /dev/null differ diff --git a/tests/data/writer/expected/.rels b/tests/data/package_rels/.rels similarity index 100% rename from tests/data/writer/expected/.rels rename to tests/data/package_rels/.rels diff --git a/tests/data/reader/bigfoot.xlsx b/tests/data/reader/bigfoot.xlsx deleted file mode 100644 index 799fcb42..00000000 Binary files a/tests/data/reader/bigfoot.xlsx and /dev/null differ diff --git a/tests/data/reader/bug137.xlsx b/tests/data/reader/bug137.xlsx deleted file mode 100644 index cde2874a..00000000 Binary files a/tests/data/reader/bug137.xlsx and /dev/null differ diff --git a/tests/data/reader/bug275.xlsx b/tests/data/reader/bug275.xlsx deleted file mode 100644 index 0c4e5f0a..00000000 Binary files a/tests/data/reader/bug275.xlsx and /dev/null differ diff --git a/tests/data/reader/bug304.xlsx b/tests/data/reader/bug304.xlsx deleted file mode 100644 index a0747277..00000000 Binary files a/tests/data/reader/bug304.xlsx and /dev/null differ diff --git a/tests/data/reader/charset-excel.xlsx b/tests/data/reader/charset-excel.xlsx deleted file mode 100644 index fb0d7060..00000000 Binary files a/tests/data/reader/charset-excel.xlsx and /dev/null differ diff --git a/tests/data/reader/contains_chartsheets.xlsx b/tests/data/reader/contains_chartsheets.xlsx deleted file mode 100644 index 985ebef6..00000000 Binary files a/tests/data/reader/contains_chartsheets.xlsx and /dev/null differ diff --git a/tests/data/reader/date_1900.xlsx b/tests/data/reader/date_1900.xlsx deleted file mode 100644 index d4f94621..00000000 Binary files a/tests/data/reader/date_1900.xlsx and /dev/null differ diff --git a/tests/data/reader/date_1904.xlsx b/tests/data/reader/date_1904.xlsx deleted file mode 100644 index d18e7c48..00000000 Binary files a/tests/data/reader/date_1904.xlsx and /dev/null differ diff --git a/tests/data/reader/formatting.xlsx b/tests/data/reader/formatting.xlsx deleted file mode 100644 index 25a41c57..00000000 Binary files a/tests/data/reader/formatting.xlsx and /dev/null differ diff --git a/tests/data/reader/formulae.xlsx b/tests/data/reader/formulae.xlsx deleted file mode 100644 index f1195eec..00000000 Binary files a/tests/data/reader/formulae.xlsx and /dev/null differ diff --git a/tests/data/reader/null_archive.xlsx b/tests/data/reader/null_archive.xlsx deleted file mode 100644 index 967263ab..00000000 Binary files a/tests/data/reader/null_archive.xlsx and /dev/null differ diff --git a/tests/data/reader/sharedStrings.xml b/tests/data/reader/sharedStrings.xml deleted file mode 100644 index 32bb79f1..00000000 --- a/tests/data/reader/sharedStrings.xml +++ /dev/null @@ -1,2 +0,0 @@ - -This is cell A1 in Sheet 1This is cell G5 \ No newline at end of file diff --git a/tests/data/reader/shared_strings-max_range.xlsx b/tests/data/reader/shared_strings-max_range.xlsx deleted file mode 100644 index 314f78d8..00000000 Binary files a/tests/data/reader/shared_strings-max_range.xlsx and /dev/null differ diff --git a/tests/data/reader/shared_strings-multiple_r_nodes.xlsx b/tests/data/reader/shared_strings-multiple_r_nodes.xlsx deleted file mode 100644 index d35130e5..00000000 Binary files a/tests/data/reader/shared_strings-multiple_r_nodes.xlsx and /dev/null differ diff --git a/tests/data/reader/styles/simple-styles.xml b/tests/data/reader/styles/simple-styles.xml deleted file mode 100644 index aced6d79..00000000 --- a/tests/data/reader/styles/simple-styles.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tests/data/reader/vba+comments.xlsm b/tests/data/reader/vba+comments.xlsm deleted file mode 100644 index e8bc9b84..00000000 Binary files a/tests/data/reader/vba+comments.xlsm and /dev/null differ diff --git a/tests/data/reader/vba-comments-saved.xlsm b/tests/data/reader/vba-comments-saved.xlsm deleted file mode 100644 index 94a9496f..00000000 Binary files a/tests/data/reader/vba-comments-saved.xlsm and /dev/null differ diff --git a/tests/data/reader/vba-test.xlsm b/tests/data/reader/vba-test.xlsm deleted file mode 100644 index 13db7974..00000000 Binary files a/tests/data/reader/vba-test.xlsm and /dev/null differ diff --git a/tests/data/writer/expected/sharedStrings.xml b/tests/data/shared_strings/sharedStrings.xml similarity index 100% rename from tests/data/writer/expected/sharedStrings.xml rename to tests/data/shared_strings/sharedStrings.xml diff --git a/tests/data/reader/styles/alignment_styles.xml b/tests/data/styles/alignment_styles.xml similarity index 100% rename from tests/data/reader/styles/alignment_styles.xml rename to tests/data/styles/alignment_styles.xml diff --git a/tests/data/reader/styles/complex-styles.xml b/tests/data/styles/complex-styles.xml similarity index 100% rename from tests/data/reader/styles/complex-styles.xml rename to tests/data/styles/complex-styles.xml diff --git a/tests/data/reader/styles/dxf_style.xml b/tests/data/styles/dxf_style.xml similarity index 100% rename from tests/data/reader/styles/dxf_style.xml rename to tests/data/styles/dxf_style.xml diff --git a/tests/data/reader/styles/empty-workbook-styles.xml b/tests/data/styles/empty-workbook-styles.xml similarity index 100% rename from tests/data/reader/styles/empty-workbook-styles.xml rename to tests/data/styles/empty-workbook-styles.xml diff --git a/tests/data/reader/styles/no_number_format.xml b/tests/data/styles/no_number_format.xml similarity index 100% rename from tests/data/reader/styles/no_number_format.xml rename to tests/data/styles/no_number_format.xml diff --git a/tests/data/reader/styles/none_value_styles.xml b/tests/data/styles/none_value_styles.xml similarity index 100% rename from tests/data/reader/styles/none_value_styles.xml rename to tests/data/styles/none_value_styles.xml diff --git a/tests/data/reader/styles/rgb_colors.xml b/tests/data/styles/rgb_colors.xml old mode 100755 new mode 100644 similarity index 100% rename from tests/data/reader/styles/rgb_colors.xml rename to tests/data/styles/rgb_colors.xml diff --git a/tests/data/writer/expected/simple-styles.xml b/tests/data/styles/simple-styles.xml similarity index 100% rename from tests/data/writer/expected/simple-styles.xml rename to tests/data/styles/simple-styles.xml diff --git a/tests/data/writer/expected/styles.xml b/tests/data/styles/styles.xml similarity index 100% rename from tests/data/writer/expected/styles.xml rename to tests/data/styles/styles.xml diff --git a/tests/data/reader/styles/styles_number_formats.xml b/tests/data/styles/styles_number_formats.xml similarity index 100% rename from tests/data/reader/styles/styles_number_formats.xml rename to tests/data/styles/styles_number_formats.xml diff --git a/tests/data/writer/expected/theme1.xml b/tests/data/theme/theme1.xml similarity index 100% rename from tests/data/writer/expected/theme1.xml rename to tests/data/theme/theme1.xml diff --git a/tests/data/reader/bug328_hyperlinks.xml b/tests/data/workbook/bug328_hyperlinks.xml similarity index 100% rename from tests/data/reader/bug328_hyperlinks.xml rename to tests/data/workbook/bug328_hyperlinks.xml diff --git a/tests/data/writer/expected/workbook.xml b/tests/data/workbook/workbook.xml similarity index 100% rename from tests/data/writer/expected/workbook.xml rename to tests/data/workbook/workbook.xml diff --git a/tests/data/writer/expected/workbook_auto_filter.xml b/tests/data/workbook/workbook_auto_filter.xml similarity index 100% rename from tests/data/writer/expected/workbook_auto_filter.xml rename to tests/data/workbook/workbook_auto_filter.xml diff --git a/tests/data/writer/expected/workbook.xml.rels b/tests/data/workbook_rels/workbook.xml.rels similarity index 100% rename from tests/data/writer/expected/workbook.xml.rels rename to tests/data/workbook_rels/workbook.xml.rels diff --git a/tests/data/reader/bug393-worksheet.xml b/tests/data/worksheets/bug393-worksheet.xml similarity index 100% rename from tests/data/reader/bug393-worksheet.xml rename to tests/data/worksheets/bug393-worksheet.xml diff --git a/tests/data/writer/expected/decimal.xml b/tests/data/worksheets/decimal.xml similarity index 100% rename from tests/data/writer/expected/decimal.xml rename to tests/data/worksheets/decimal.xml diff --git a/tests/data/reader/empty_rows.xml b/tests/data/worksheets/empty_rows.xml similarity index 100% rename from tests/data/reader/empty_rows.xml rename to tests/data/worksheets/empty_rows.xml diff --git a/tests/data/writer/expected/long_number.xml b/tests/data/worksheets/long_number.xml similarity index 100% rename from tests/data/writer/expected/long_number.xml rename to tests/data/worksheets/long_number.xml diff --git a/tests/data/reader/merged-ranges.xml b/tests/data/worksheets/merged-ranges.xml similarity index 100% rename from tests/data/reader/merged-ranges.xml rename to tests/data/worksheets/merged-ranges.xml diff --git a/tests/data/writer/expected/sheet1.xml b/tests/data/worksheets/sheet1.xml similarity index 100% rename from tests/data/writer/expected/sheet1.xml rename to tests/data/worksheets/sheet1.xml diff --git a/tests/data/writer/expected/sheet1_auto_filter.xml b/tests/data/worksheets/sheet1_auto_filter.xml similarity index 100% rename from tests/data/writer/expected/sheet1_auto_filter.xml rename to tests/data/worksheets/sheet1_auto_filter.xml diff --git a/tests/data/writer/expected/sheet1_auto_filter_filter_column.xml b/tests/data/worksheets/sheet1_auto_filter_filter_column.xml similarity index 100% rename from tests/data/writer/expected/sheet1_auto_filter_filter_column.xml rename to tests/data/worksheets/sheet1_auto_filter_filter_column.xml diff --git a/tests/data/writer/expected/sheet1_auto_filter_sort_condition.xml b/tests/data/worksheets/sheet1_auto_filter_sort_condition.xml similarity index 100% rename from tests/data/writer/expected/sheet1_auto_filter_sort_condition.xml rename to tests/data/worksheets/sheet1_auto_filter_sort_condition.xml diff --git a/tests/data/writer/expected/sheet1_bool.xml b/tests/data/worksheets/sheet1_bool.xml similarity index 100% rename from tests/data/writer/expected/sheet1_bool.xml rename to tests/data/worksheets/sheet1_bool.xml diff --git a/tests/data/writer/expected/sheet1_formula.xml b/tests/data/worksheets/sheet1_formula.xml similarity index 100% rename from tests/data/writer/expected/sheet1_formula.xml rename to tests/data/worksheets/sheet1_formula.xml diff --git a/tests/data/writer/expected/sheet1_freeze_panes_both.xml b/tests/data/worksheets/sheet1_freeze_panes_both.xml similarity index 100% rename from tests/data/writer/expected/sheet1_freeze_panes_both.xml rename to tests/data/worksheets/sheet1_freeze_panes_both.xml diff --git a/tests/data/writer/expected/sheet1_freeze_panes_horiz.xml b/tests/data/worksheets/sheet1_freeze_panes_horiz.xml similarity index 100% rename from tests/data/writer/expected/sheet1_freeze_panes_horiz.xml rename to tests/data/worksheets/sheet1_freeze_panes_horiz.xml diff --git a/tests/data/writer/expected/sheet1_freeze_panes_vert.xml b/tests/data/worksheets/sheet1_freeze_panes_vert.xml similarity index 100% rename from tests/data/writer/expected/sheet1_freeze_panes_vert.xml rename to tests/data/worksheets/sheet1_freeze_panes_vert.xml diff --git a/tests/data/writer/expected/sheet1_height.xml b/tests/data/worksheets/sheet1_height.xml similarity index 100% rename from tests/data/writer/expected/sheet1_height.xml rename to tests/data/worksheets/sheet1_height.xml diff --git a/tests/data/writer/expected/sheet1_hyperlink.xml b/tests/data/worksheets/sheet1_hyperlink.xml similarity index 100% rename from tests/data/writer/expected/sheet1_hyperlink.xml rename to tests/data/worksheets/sheet1_hyperlink.xml diff --git a/tests/data/writer/expected/sheet1_hyperlink.xml.rels b/tests/data/worksheets/sheet1_hyperlink.xml.rels similarity index 100% rename from tests/data/writer/expected/sheet1_hyperlink.xml.rels rename to tests/data/worksheets/sheet1_hyperlink.xml.rels diff --git a/tests/data/writer/expected/sheet1_style.xml b/tests/data/worksheets/sheet1_style.xml similarity index 100% rename from tests/data/writer/expected/sheet1_style.xml rename to tests/data/worksheets/sheet1_style.xml diff --git a/tests/data/reader/sheet2.xml b/tests/data/worksheets/sheet2.xml similarity index 100% rename from tests/data/reader/sheet2.xml rename to tests/data/worksheets/sheet2.xml diff --git a/tests/data/reader/sheet2_invalid_dimension.xml b/tests/data/worksheets/sheet2_invalid_dimension.xml similarity index 100% rename from tests/data/reader/sheet2_invalid_dimension.xml rename to tests/data/worksheets/sheet2_invalid_dimension.xml diff --git a/tests/data/reader/sheet2_no_dimension.xml b/tests/data/worksheets/sheet2_no_dimension.xml similarity index 100% rename from tests/data/reader/sheet2_no_dimension.xml rename to tests/data/worksheets/sheet2_no_dimension.xml diff --git a/tests/data/reader/sheet2_no_span.xml b/tests/data/worksheets/sheet2_no_span.xml similarity index 100% rename from tests/data/reader/sheet2_no_span.xml rename to tests/data/worksheets/sheet2_no_span.xml diff --git a/tests/data/writer/expected/short_number.xml b/tests/data/worksheets/short_number.xml similarity index 100% rename from tests/data/writer/expected/short_number.xml rename to tests/data/worksheets/short_number.xml diff --git a/tests/data/reader/worksheet_formula.xml b/tests/data/worksheets/worksheet_formula.xml similarity index 100% rename from tests/data/reader/worksheet_formula.xml rename to tests/data/worksheets/worksheet_formula.xml diff --git a/tests/data/reader/styles/worksheet_unprotected_style.xml b/tests/data/worksheets/worksheet_unprotected_style.xml similarity index 100% rename from tests/data/reader/styles/worksheet_unprotected_style.xml rename to tests/data/worksheets/worksheet_unprotected_style.xml diff --git a/tests/data/writer/Content_types_vba.xml b/tests/data/writer/Content_types_vba.xml deleted file mode 100644 index da731852..00000000 --- a/tests/data/writer/Content_types_vba.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/tests/data/writer/expected/app.xml b/tests/data/writer/expected/app.xml deleted file mode 100644 index 856f8466..00000000 --- a/tests/data/writer/expected/app.xml +++ /dev/null @@ -1,2 +0,0 @@ - -Microsoft Excel0falseCompanyfalsefalsefalse12.0000Worksheets3SheetSheet1Sheet2 \ No newline at end of file diff --git a/tests/data/writer/expected/font.xml b/tests/data/writer/expected/font.xml deleted file mode 100644 index 00beff8a..00000000 --- a/tests/data/writer/expected/font.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/tests/data/writer/font.xml b/tests/data/writer/font.xml deleted file mode 100644 index 00beff8a..00000000 --- a/tests/data/writer/font.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/tests/data/writer/styles.xml b/tests/data/writer/styles.xml deleted file mode 100644 index c3a69218..00000000 --- a/tests/data/writer/styles.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tests/data/1_empty.txt b/tests/data/xlsx/1_blank.xlsx similarity index 100% rename from tests/data/1_empty.txt rename to tests/data/xlsx/1_blank.xlsx diff --git a/tests/data/xlsx/2_text.xlsx b/tests/data/xlsx/2_text.xlsx new file mode 100644 index 00000000..be394c40 --- /dev/null +++ b/tests/data/xlsx/2_text.xlsx @@ -0,0 +1 @@ +not-empty \ No newline at end of file diff --git a/tests/data/3_empty.zip b/tests/data/xlsx/3_empty-zip.xlsx similarity index 100% rename from tests/data/3_empty.zip rename to tests/data/xlsx/3_empty-zip.xlsx diff --git a/tests/data/xlsx/4_not-package.xlsx b/tests/data/xlsx/4_not-package.xlsx new file mode 100644 index 00000000..909c7b62 Binary files /dev/null and b/tests/data/xlsx/4_not-package.xlsx differ diff --git a/tests/data/5_visio.vsdx b/tests/data/xlsx/5_visio.xlsx similarity index 100% rename from tests/data/5_visio.vsdx rename to tests/data/xlsx/5_visio.xlsx diff --git a/tests/data/6_document.docx b/tests/data/xlsx/6_document.xlsx similarity index 100% rename from tests/data/6_document.docx rename to tests/data/xlsx/6_document.xlsx diff --git a/tests/data/7_presentation.pptx b/tests/data/xlsx/7_presentation.xlsx similarity index 100% rename from tests/data/7_presentation.pptx rename to tests/data/xlsx/7_presentation.xlsx diff --git a/tests/data/8_excel-empty.xlsx b/tests/data/xlsx/8_default-excel.xlsx similarity index 100% rename from tests/data/8_excel-empty.xlsx rename to tests/data/xlsx/8_default-excel.xlsx diff --git a/tests/data/xlsx/9_default-libre-office.xlsx b/tests/data/xlsx/9_default-libre-office.xlsx new file mode 100644 index 00000000..64c70e5a Binary files /dev/null and b/tests/data/xlsx/9_default-libre-office.xlsx differ diff --git a/tests/helpers/xml_helper.hpp b/tests/helpers/xml_helper.hpp index ae28da10..3ffb9d2d 100644 --- a/tests/helpers/xml_helper.hpp +++ b/tests/helpers/xml_helper.hpp @@ -129,6 +129,45 @@ public: return documents_match(left_xml, right_xml); } + + static bool archives_match(xlnt::zip_file &left_archive, xlnt::zip_file &right_archive) + { + auto left_info = left_archive.infolist(); + auto right_info = right_archive.infolist(); + + if (left_info.size() != right_info.size()) return false; + + for (auto left_member : left_info) + { + if (!right_archive.has_file(left_member)) return false; + + 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; + } + } + + return true; + } + + static bool workbooks_match(const xlnt::workbook &left, const xlnt::workbook &right) + { + std::vector buffer; + + left.save(buffer); + xlnt::zip_file left_archive(buffer); + + buffer.clear(); + + right.save(buffer); + xlnt::zip_file right_archive(buffer); + + return archives_match(left_archive, right_archive); + } static comparison_result compare_xml_nodes(const pugi::xml_node &left, const pugi::xml_node &right) {