From 9dce7b8f0ce2d5f15dba3d54889b828dfb282a2f Mon Sep 17 00:00:00 2001 From: Thomas Fussell Date: Fri, 30 Oct 2015 18:54:04 -0400 Subject: [PATCH] almost done. phew... --- include/xlnt/s11n/comment_serializer.hpp | 11 +- include/xlnt/s11n/excel_serializer.hpp | 115 ++++++++- include/xlnt/s11n/manifest_serializer.hpp | 8 +- include/xlnt/s11n/relationship_serializer.hpp | 6 +- .../xlnt/s11n/shared_strings_serializer.hpp | 4 +- include/xlnt/s11n/style_serializer.hpp | 229 ++++++++++-------- include/xlnt/s11n/theme_serializer.hpp | 6 +- include/xlnt/s11n/workbook_serializer.hpp | 9 +- include/xlnt/s11n/worksheet_serializer.hpp | 5 +- include/xlnt/s11n/xml_document.hpp | 37 ++- include/xlnt/s11n/xml_node.hpp | 40 +-- include/xlnt/s11n/xml_serializer.hpp | 5 +- include/xlnt/styles/color.hpp | 2 +- include/xlnt/workbook/workbook.hpp | 6 +- source/cell/cell.cpp | 2 +- source/detail/workbook_impl.hpp | 6 +- source/detail/xml_document_impl.hpp | 17 ++ source/detail/xml_node_impl.hpp | 16 ++ source/s11n/excel_serializer.cpp | 70 +++--- source/s11n/manifest_serializer.cpp | 64 ++++- source/s11n/relationship_serializer.cpp | 49 +++- source/s11n/shared_strings_serializer.cpp | 37 ++- source/s11n/style_serializer.cpp | 115 ++++++--- source/s11n/theme_serializer.cpp | 21 +- source/s11n/workbook_serializer.cpp | 106 ++++---- source/s11n/worksheet_serializer.cpp | 48 ++-- source/s11n/xml_document.cpp | 63 ++++- source/s11n/xml_node.cpp | 131 +++++----- source/s11n/xml_serializer.cpp | 83 ++----- source/workbook/manifest.cpp | 85 ++++++- source/workbook/workbook.cpp | 45 ++-- source/worksheet/range_reference.cpp | 6 + source/worksheet/worksheet.cpp | 10 + source/worksheet/worksheet_reader.cpp | 16 -- tests/helpers/helper.hpp | 31 ++- tests/test_props.hpp | 4 + tests/test_read.hpp | 19 +- tests/test_style_writer.hpp | 5 +- tests/test_theme.hpp | 6 +- tests/test_worksheet.hpp | 107 ++++---- tests/test_write.hpp | 129 +++++++--- tests/test_write_workbook.hpp | 78 ++++-- 42 files changed, 1211 insertions(+), 641 deletions(-) create mode 100644 source/detail/xml_document_impl.hpp create mode 100644 source/detail/xml_node_impl.hpp delete mode 100644 source/worksheet/worksheet_reader.cpp diff --git a/include/xlnt/s11n/comment_serializer.hpp b/include/xlnt/s11n/comment_serializer.hpp index 6ce09934..42066175 100644 --- a/include/xlnt/s11n/comment_serializer.hpp +++ b/include/xlnt/s11n/comment_serializer.hpp @@ -30,21 +30,20 @@ namespace xlnt { -class xml_node; +class xml_document; class comment_serializer { comment_serializer(worksheet sheet); - void read_comments(const xml_node &xml); - void read_comments_vml(const xml_node &xml); + void read_comments(const xml_document &xml); + void read_comments_vml(const xml_document &xml); - xml_node write_comments(); - xml_node write_comments_vml(); + xml_document write_comments() const; + xml_document write_comments_vml() const; private: worksheet sheet_; - std::vector comments_; }; } // namespace xlnt diff --git a/include/xlnt/s11n/excel_serializer.hpp b/include/xlnt/s11n/excel_serializer.hpp index d52ba22d..b4dddb6d 100644 --- a/include/xlnt/s11n/excel_serializer.hpp +++ b/include/xlnt/s11n/excel_serializer.hpp @@ -34,41 +34,148 @@ namespace xlnt { class workbook; +/// +/// Handles reading and writing workbooks from an actual XLSX archive +/// using other serializers. +/// class excel_serializer { public: + /// + /// + /// static const std::string central_directory_signature(); + + /// + /// + /// static std::string repair_central_directory(const std::string &original); + /// + /// Construct an excel_serializer which operates on wb. + /// excel_serializer(workbook &wb); + /// + /// Create a ZIP file in memory, load archive from filename, then populate workbook + /// with data from archive. + /// bool load_workbook(const std::string &filename, bool guess_types = false, bool data_only = false); - bool load_stream_workbook(std::istream &stream, bool guess_types = false, bool data_only = false); - bool load_virtual_workbook(const std::vector &bytes, bool guess_types = false, bool data_only = false); + + /// + /// Create a ZIP file in memory, load archive from stream, then populate workbook + /// with data from archive. + /// + bool load_stream_workbook(std::istream &stream, bool guess_types = false, bool data_only = false); + + + /// + /// Create a ZIP file in memory, load archive from bytes, then populate workbook + /// with data from archive. + /// + bool load_virtual_workbook(const std::vector &bytes, bool guess_types = false, bool data_only = false); + + /// + /// Create a ZIP file in memory, save workbook to this archive, then save archive + /// to filename. + /// bool save_workbook(const std::string &filename, bool as_template = false); + + /// + /// Create a ZIP file in memory, save workbook to this archive, then assign ZIP file + /// binary data to bytes. + /// bool save_virtual_workbook(std::vector &bytes, bool as_template = false); + + /// + /// Create a ZIP file in memory, save workbook to this archive, then copy ZIP file + /// binary data to stream. + /// bool save_stream_workbook(std::ostream &stream, bool as_template = false); private: + /// + /// Reads all files in archive and populates workbook with associated data + /// using other appropriate serializers such as workbook_serializer. + /// void read_data(bool guess_types, bool data_only); + + /// + /// Read xl/sharedStrings.xml from internal archive and add shared strings to workbook. + /// void read_shared_strings(); + + /// + /// + /// void read_images(); + + /// + /// + /// void read_charts(); + + /// + /// + /// void read_chartsheets(); + + /// + /// + /// void read_worksheets(); + + /// + /// + /// void read_external_links(); + /// + /// Write all files needed to create a valid XLSX file which represents all + /// data contained in workbook. + /// void write_data(bool as_template); + + /// + /// Write xl/sharedStrings.xml to internal archive based on shared strings in workbook. + /// void write_shared_strings(); + + /// + /// + /// void write_images(); + + /// + /// + /// void write_charts(); + + /// + /// + /// void write_chartsheets(); + + /// + /// + /// void write_worksheets(); + + /// + /// + /// void write_external_links(); - workbook &wb_; - std::vector shared_strings_; + /// + /// A reference to the workbook which is the object of read/write operations. + /// + workbook &workbook_; + + /// + /// The internal archive which holds files representing workbook_ during + /// read/write operations. + /// zip_file archive_; }; diff --git a/include/xlnt/s11n/manifest_serializer.hpp b/include/xlnt/s11n/manifest_serializer.hpp index 5babe4af..638eb60a 100644 --- a/include/xlnt/s11n/manifest_serializer.hpp +++ b/include/xlnt/s11n/manifest_serializer.hpp @@ -5,17 +5,17 @@ namespace xlnt { class manifest; -class workbook; class xml_document; -class zip_file; class manifest_serializer { public: manifest_serializer(manifest &m); - bool read_manifest(const xml_document &xml); - bool write_manifest(xml_document &xml); + void read_manifest(const xml_document &xml); + xml_document write_manifest() const; + + std::string determine_document_type() const; private: manifest &manifest_; diff --git a/include/xlnt/s11n/relationship_serializer.hpp b/include/xlnt/s11n/relationship_serializer.hpp index 2bd3996e..0dc1222f 100644 --- a/include/xlnt/s11n/relationship_serializer.hpp +++ b/include/xlnt/s11n/relationship_serializer.hpp @@ -6,13 +6,13 @@ namespace xlnt { class relationship; -class xml_document; +class zip_file; class relationship_serializer { public: - static bool read_relationships(const xml_document &xml, const std::string &dir, std::vector &relationships); - static bool write_relationships(const std::vector &relationships, const std::string &dir, xml_document &xml); + static std::vector read_relationships(zip_file &archive, const std::string &target); + static bool write_relationships(const std::vector &relationships, const std::string &target, zip_file &archive); }; } // namespace xlnt diff --git a/include/xlnt/s11n/shared_strings_serializer.hpp b/include/xlnt/s11n/shared_strings_serializer.hpp index a528d848..c9c56fb0 100644 --- a/include/xlnt/s11n/shared_strings_serializer.hpp +++ b/include/xlnt/s11n/shared_strings_serializer.hpp @@ -33,8 +33,8 @@ class xml_document; class shared_strings_serializer { public: - bool read_strings(const xml_document &xml, std::vector &strings); - bool write_strings(const std::vector &strings, xml_document &xml); + static bool read_shared_strings(const xml_document &xml, std::vector &strings); + static xml_document write_shared_strings(const std::vector &strings); }; } // namespace xlnt diff --git a/include/xlnt/s11n/style_serializer.hpp b/include/xlnt/s11n/style_serializer.hpp index 2895520e..20b4db82 100644 --- a/include/xlnt/s11n/style_serializer.hpp +++ b/include/xlnt/s11n/style_serializer.hpp @@ -56,6 +56,10 @@ public: /// with styles from an existing styles.xml. style_serializer(workbook &wb); + // + // Primary methods + // + /// /// Load all styles from xml_document into workbook given in constructor. /// @@ -65,28 +69,118 @@ public: /// Populate parameter xml with an XML tree representing the styles contained in the workbook /// given in the constructor. /// - bool write_stylesheet(xml_document &xml) const; + xml_document write_stylesheet() const; -private: - /// - /// Read a single style from the given node. In styles.xml, this is an "xf" element. - /// A style has an optional border id, fill id, font id, and number format id where - /// each id is an index in the corresponding list of borders, etc. A style also has - /// optional alignment and protection children. A style also defines whether each of - /// these is "applied". For example, a style with a defined font id, font=# but with - /// "applyFont=0" will not use the font in formatting. - /// - style read_style(const xml_node &style_node) const; +//TODO: These need to be public for unit tests. Could also make test class a friend? +//private: + + // + // Static element readers (i.e. readers that don't use internal state) + // /// /// Read and return an alignment from alignment_node. /// - alignment read_alignment(const xml_node &alignment_node) const; + static alignment read_alignment(const xml_node &alignment_node); /// /// Read and return a border side from side_node. /// - side read_side(const xml_node &side_node) const; + static side read_side(const xml_node &side_node); + + /// + /// Read and return a border from border_node. + /// + static border read_border(const xml_node &border_node); + + /// + /// Read and return a fill from fill_node. + /// + static fill read_fill(const xml_node &fill_node); + + /// + /// Read and return a font from font_node. + /// + static font read_font(const xml_node &font_node); + + /// + /// Read and return a number format from number_format_node. + /// + static number_format read_number_format(const xml_node &number_format_node); + + /// + /// Read and return a protection from protection_node. + /// + static protection read_protection(const xml_node &protection_node); + + /// + /// Read and return all indexed colors from indexed_colors_node. + /// + static std::vector read_indexed_colors(const xml_node &indexed_colors_node); + + /// + /// Read and return a color from color_node. + /// + static color read_color(const xml_node &color_node); + + /// + /// Read and return a conditional format, dxf, from conditional_format_node. + /// + static conditional_format read_conditional_format(const xml_node &conditional_formats_node); + + /// + /// Read and return a pair containing a name and corresponding style from named_style_node. + /// + static std::pair read_named_style(const xml_node &named_style_node); + + // + // Static element writers (i.e. writers that don't use internal state) + // + + /// + /// Build and return an xml tree representing alignment_. + /// + static xml_node write_alignment(const alignment &alignment_); + + /// + /// Build and return an xml tree representing border_. + /// + static xml_node write_border(const border &border_); + + /// + /// Build and return an xml tree representing conditional_format_. + /// + static xml_node write_conditional_format(const conditional_format &conditional_format_); + + /// + /// Build and return an xml tree representing fill_. + /// + static xml_node write_fill(const fill &fill_); + + /// + /// Build and return an xml tree representing font_. + /// + static xml_node write_font(const font &font_); + + /// + /// Build and return two xml trees, first=cell style and second=named style, representing named_style_. + /// + static std::pair write_named_style(const named_style &named_style_); + + /// + /// Build an xml tree representing all named styles in workbook into named_styles_node and cell_styles_node. + /// Returns true on success. + /// + static std::pair write_named_styles(); + + /// + /// Build and return an xml tree representing number_format_. + /// + static xml_node write_number_format(const number_format &number_format_); + + // + // Non-static element readers (i.e. readers that modify internal state) + // /// /// Read all borders from borders_node and add them to workbook. @@ -94,70 +188,24 @@ private: /// bool read_borders(const xml_node &borders_node); - /// - /// Read and return a border from border_node. - /// - border read_border(const xml_node &border_node) const; - /// /// Read all fills from fills_node and add them to workbook. /// Return true on success. /// bool read_fills(const xml_node &fills_node); - /// - /// Read and return a fill from fill_node. - /// - fill read_fill(const xml_node &fill_node) const; - /// /// Read all fonts from fonts_node and add them to workbook. /// Return true on success. /// bool read_fonts(const xml_node &fonts_node); - /// - /// Read and return a font from font_node. - /// - font read_font(const xml_node &font_node) const; - - /// - /// Read all borders from number_formats_node and add them to workbook. - /// Return true on success. - /// - bool read_number_formats(const xml_node &number_formats_node); - - /// - /// Read and return a number format from number_format_node. - /// - number_format read_number_format(const xml_node &number_format_node) const; - - /// - /// Read and return a protection from protection_node. - /// - protection read_protection(const xml_node &protection_node) const; - /// /// Read all colors from colors_node and add them to workbook. /// Return true on success. /// bool read_colors(const xml_node &colors_node); - /// - /// Read and return all indexed colors from indexed_colors_node. - /// - std::vector read_indexed_colors(const xml_node &indexed_colors_node) const; - - /// - /// Read and return a color from color_node. - /// - color read_color(const xml_node &color_node) const; - - /// - /// Read and return a conditional format, dxf, from conditional_format_node. - /// - conditional_format read_conditional_format(const xml_node &conditional_formats_node) const; - /// /// Read all named styles from named_style_node and cell_styles_node and add them to workbook. /// Return true on success. @@ -165,19 +213,24 @@ private: bool read_named_styles(const xml_node &named_styles_node); /// - /// Read and return a pair containing a name and corresponding style from named_style_node. + /// Read all borders from number_formats_node and add them to workbook. + /// Return true on success. /// - std::pair read_named_style(const xml_node &named_style_node) const; + bool read_number_formats(const xml_node &number_formats_node); /// - /// Build and return an xml tree representing alignment_. + /// Read a single style from the given node. In styles.xml, this is an "xf" element. + /// A style has an optional border id, fill id, font id, and number format id where + /// each id is an index in the corresponding list of borders, etc. A style also has + /// optional alignment and protection children. A style also defines whether each of + /// these is "applied". For example, a style with a defined font id, font=# but with + /// "applyFont=0" will not use the font in formatting. /// - xml_node write_alignment(const alignment &alignment_) const; + style read_style(const xml_node &style_node); - /// - /// Build and return an xml tree representing border_. - /// - xml_node write_border(const border &border_) const; + // + // Non-static element writers (i.e. writers that modify internal workbook) + // /// /// Build an xml tree representing all borders in workbook into borders_node. @@ -186,9 +239,10 @@ private: bool write_borders(xml_node &borders_node) const; /// - /// Build and return an xml tree representing fill_. + /// Build an xml tree representing all conditional formats in workbook into conditional_formats_node. + /// Returns true on success. /// - xml_node write_fill(const fill &fill_) const; + bool write_conditional_formats(xml_node &conditional_formats_node) const; /// /// Build an xml tree representing all fills in workbook into fills_node. @@ -196,38 +250,17 @@ private: /// bool write_fills(xml_node &fills_node) const; - /// - /// Build and return an xml tree representing font_. - /// - xml_node write_font(const font &font_) const; - /// /// Build an xml tree representing all fonts in workbook into fonts_node. /// Returns true on success. /// bool write_fonts(xml_node &fonts_node) const; - /// - /// Build and return an xml tree representing number_format_. - /// - xml_node write_number_format(const number_format &number_format_) const; - /// /// Build an xml tree representing all number formats in workbook into number_formats_node. /// Returns true on success. /// - bool write_number_formats(xml_node &fonts_node) const; - - /// - /// Build and return two xml trees, first=cell style and second=named style, representing named_style_. - /// - std::pair write_named_style(const named_style &named_style_) const; - - /// - /// Build an xml tree representing all named styles in workbook into named_styles_node and cell_styles_node. - /// Returns true on success. - /// - void write_named_styles(xml_node &named_styles_node, xml_node &cell_styles_node) const; + bool write_number_formats(xml_node fonts_node) const; /// /// Build an xml tree representing the given style into style_node. @@ -235,21 +268,11 @@ private: /// bool write_style(const style &style_, xml_node &style_node) const; - /// - /// Build and return an xml tree representing conditional_format_. - /// - xml_node write_conditional_format(const conditional_format &conditional_format_) const; - - /// - /// Build an xml tree representing all conditional formats in workbook into conditional_formats_node. - /// Returns true on success. - /// - bool write_conditional_formats(xml_node &conditional_formats_node) const; - +private: /// /// Set in the constructor, this workbook is used as the source or target for all writing or reading, respectively. /// - workbook &wb_; + workbook &workbook_; }; } // namespace xlnt diff --git a/include/xlnt/s11n/theme_serializer.hpp b/include/xlnt/s11n/theme_serializer.hpp index 6cb9803c..cde79541 100644 --- a/include/xlnt/s11n/theme_serializer.hpp +++ b/include/xlnt/s11n/theme_serializer.hpp @@ -33,8 +33,10 @@ class xml_document; class theme_serializer { public: - //theme read_theme(const xml_document &xml); - xml_document write_theme(const theme &theme_); + bool read_theme(const xml_document &xml, theme &t); + xml_document write_theme(const theme &t) const; + +private: }; } // namespace xlnt diff --git a/include/xlnt/s11n/workbook_serializer.hpp b/include/xlnt/s11n/workbook_serializer.hpp index fb3d24e9..d97c26b9 100644 --- a/include/xlnt/s11n/workbook_serializer.hpp +++ b/include/xlnt/s11n/workbook_serializer.hpp @@ -40,8 +40,7 @@ class xml_node; class workbook_serializer { public: - //TODO: does this go here? - static std::string determine_document_type(const manifest &manifest_); + using string_pair = std::pair; workbook_serializer(workbook &wb); @@ -53,15 +52,13 @@ public: xml_document write_properties_app() const; xml_document write_properties_core() const; - using string_pair = std::pair; - std::vector read_sheets(); std::vector detect_worksheets(); - bool write_named_ranges(xml_node &named_ranges_node); + xml_node write_named_ranges() const; private: - workbook &wb_; + workbook &workbook_; }; } // namespace xlnt diff --git a/include/xlnt/s11n/worksheet_serializer.hpp b/include/xlnt/s11n/worksheet_serializer.hpp index 0beb0ba7..0a7ebdda 100644 --- a/include/xlnt/s11n/worksheet_serializer.hpp +++ b/include/xlnt/s11n/worksheet_serializer.hpp @@ -34,15 +34,14 @@ class relationship; class workbook; class worksheet; class xml_document; -class xml_node; class worksheet_serializer { public: worksheet_serializer(worksheet sheet); - bool read_worksheet(const xml_document &xml, const std::vector &shared_strings, const relationship &rel); - bool write_worksheet(const std::vector &string_table, xml_document &xml); + bool read_worksheet(const xml_document &xml); + xml_document write_worksheet() const; private: worksheet sheet_; diff --git a/include/xlnt/s11n/xml_document.hpp b/include/xlnt/s11n/xml_document.hpp index b6dd3bfd..974e88ba 100644 --- a/include/xlnt/s11n/xml_document.hpp +++ b/include/xlnt/s11n/xml_document.hpp @@ -1,25 +1,48 @@ #pragma once +#include #include #include -#include - namespace xlnt { +namespace detail { +struct xml_document_impl; +} // namespace detail + +class xml_node; +class xml_serializer; class xml_document { public: + using string_pair = std::pair; + + xml_document(); + xml_document(const xml_document &other); + xml_document(xml_document &&other); + ~xml_document(); + + xml_document &operator=(const xml_document &other); + xml_document &operator=(xml_document &&other); + void set_encoding(const std::string &encoding); void add_namespace(const std::string &id, const std::string &uri); - xml_node &root(); - const xml_node &root() const; + xml_node add_child(const xml_node &child); + xml_node add_child(const std::string &child_name); + + xml_node get_root(); + const xml_node get_root() const; + + xml_node get_child(const std::string &child_name); + const xml_node get_child(const std::string &child_name) const; + + std::string to_string() const; + xml_document &from_string(const std::string &xml_string); private: - xml_node root_; - std::string encoding_; - std::vector namespaces_; + friend class xml_serializer; + std::unique_ptr d_; }; } // namespace xlnt diff --git a/include/xlnt/s11n/xml_node.hpp b/include/xlnt/s11n/xml_node.hpp index f35741b4..9b1b2c83 100644 --- a/include/xlnt/s11n/xml_node.hpp +++ b/include/xlnt/s11n/xml_node.hpp @@ -1,17 +1,26 @@ #pragma once +#include #include #include namespace xlnt { - -using string_pair = std::pair; - +namespace detail { +struct xml_node_impl; +} // namespace detail + +class xml_document; + class xml_node { public: + using string_pair = std::pair; + xml_node(); - explicit xml_node(const std::string &name); + xml_node(const xml_node &other); + ~xml_node(); + + xml_node &operator=(const xml_node &other); std::string get_name() const; void set_name(const std::string &name); @@ -20,24 +29,25 @@ public: std::string get_text() const; void set_text(const std::string &text); - const std::vector &get_children() const; + const std::vector get_children() const; bool has_child(const std::string &child_name) const; - xml_node &get_child(const std::string &child_name); - const xml_node &get_child(const std::string &child_name) const; - xml_node &add_child(const xml_node &child); - xml_node &add_child(const std::string &child_name); + xml_node get_child(const std::string &child_name); + const xml_node get_child(const std::string &child_name) const; + xml_node add_child(const xml_node &child); + xml_node add_child(const std::string &child_name); - const std::vector &get_attributes() const; + const std::vector get_attributes() const; bool has_attribute(const std::string &attribute_name) const; std::string get_attribute(const std::string &attribute_name) const; void add_attribute(const std::string &name, const std::string &value); + std::string to_string() const; + private: - std::string name_; - bool has_text_ = false; - std::string text_; - std::vector attributes_; - std::vector children_; + friend class xml_document; + friend class xml_serializer; + xml_node(const detail::xml_node_impl &d); + std::unique_ptr d_; }; } // namespace xlnt diff --git a/include/xlnt/s11n/xml_serializer.hpp b/include/xlnt/s11n/xml_serializer.hpp index 0d89d37d..c5d79664 100644 --- a/include/xlnt/s11n/xml_serializer.hpp +++ b/include/xlnt/s11n/xml_serializer.hpp @@ -3,14 +3,17 @@ #include namespace xlnt { - + class xml_document; +class xml_node; class xml_serializer { public: static std::string serialize(const xml_document &xml); static xml_document deserialize(const std::string &xml_string); + + static std::string serialize_node(const xml_node &xml); }; } // namespace xlnt diff --git a/include/xlnt/styles/color.hpp b/include/xlnt/styles/color.hpp index cf7c1123..9fb7a099 100644 --- a/include/xlnt/styles/color.hpp +++ b/include/xlnt/styles/color.hpp @@ -118,7 +118,7 @@ public: { if(type_ != type::rgb) { - throw std::runtime_error("not theme color"); + throw std::runtime_error("not rgb color"); } return rgb_string_; diff --git a/include/xlnt/workbook/workbook.hpp b/include/xlnt/workbook/workbook.hpp index 6542566e..91035834 100644 --- a/include/xlnt/workbook/workbook.hpp +++ b/include/xlnt/workbook/workbook.hpp @@ -248,7 +248,11 @@ public: const manifest &get_manifest() const; const std::vector &get_root_relationships() const; - + + void add_shared_string(const std::string &shared); + std::vector &get_shared_strings(); + const std::vector &get_shared_strings() const; + private: friend class worksheet; std::shared_ptr d_; diff --git a/source/cell/cell.cpp b/source/cell/cell.cpp index e69c01f5..613e2c3e 100644 --- a/source/cell/cell.cpp +++ b/source/cell/cell.cpp @@ -361,7 +361,7 @@ section parse_section(const std::string §ion_string) static const std::vector bracket_times = { "h", "hh", "m", "mm", "s", "ss" }; - for(int i = 0; i < section_string.size(); i++) + for(std::size_t i = 0; i < section_string.size(); i++) { if(!in_quotes && section_string[i] == '"') { diff --git a/source/detail/workbook_impl.hpp b/source/detail/workbook_impl.hpp index 509dee9b..173b7d2c 100644 --- a/source/detail/workbook_impl.hpp +++ b/source/detail/workbook_impl.hpp @@ -15,7 +15,8 @@ struct workbook_impl worksheets_(other.worksheets_), relationships_(other.relationships_), root_relationships_(other.root_relationships_), - drawings_(other.drawings_), + drawings_(other.drawings_), + shared_strings_(other.shared_strings_), properties_(other.properties_), guess_types_(other.guess_types_), data_only_(other.data_only_), @@ -40,6 +41,8 @@ struct workbook_impl std::copy(other.root_relationships_.begin(), other.root_relationships_.end(), std::back_inserter(root_relationships_)); drawings_.clear(); std::copy(other.drawings_.begin(), other.drawings_.end(), back_inserter(drawings_)); + shared_strings_.clear(); + std::copy(other.shared_strings_.begin(), other.shared_strings_.end(), std::back_inserter(shared_strings_)); properties_ = other.properties_; guess_types_ = other.guess_types_; data_only_ = other.data_only_; @@ -59,6 +62,7 @@ struct workbook_impl std::vector relationships_; std::vector root_relationships_; std::vector drawings_; + std::vector shared_strings_; document_properties properties_; diff --git a/source/detail/xml_document_impl.hpp b/source/detail/xml_document_impl.hpp new file mode 100644 index 00000000..d276fcaa --- /dev/null +++ b/source/detail/xml_document_impl.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include + +#include + +namespace xlnt { +namespace detail { + +struct xml_document_impl +{ + std::string encoding; + pugi::xml_document doc; +}; + +} // namespace detail +} // namespace xlnt diff --git a/source/detail/xml_node_impl.hpp b/source/detail/xml_node_impl.hpp new file mode 100644 index 00000000..f7374b60 --- /dev/null +++ b/source/detail/xml_node_impl.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include + +namespace xlnt { +namespace detail { + +struct xml_node_impl +{ + xml_node_impl() {} + explicit xml_node_impl(pugi::xml_node n) : node(n) {} + pugi::xml_node node; +}; + +} // namespace detail +} // namespace xlnt diff --git a/source/s11n/excel_serializer.cpp b/source/s11n/excel_serializer.cpp index f57b99ed..8d5520ab 100644 --- a/source/s11n/excel_serializer.cpp +++ b/source/s11n/excel_serializer.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -43,44 +44,49 @@ bool load_workbook(xlnt::zip_file &archive, bool guess_types, bool data_only, xl xlnt::manifest_serializer ms(wb.get_manifest()); ms.read_manifest(xlnt::xml_serializer::deserialize(archive.read(xlnt::constants::ArcContentTypes))); - if(xlnt::workbook_serializer::determine_document_type(wb.get_manifest()) != "excel") + if(ms.determine_document_type() != "excel") { throw xlnt::invalid_file_exception(""); } wb.clear(); - std::vector workbook_relationships; - xlnt::relationship_serializer::read_relationships(xlnt::xml_serializer::deserialize(archive.read("xl/_rels/workbook.xml.rels")), "", workbook_relationships); + auto workbook_relationships = xlnt::relationship_serializer::read_relationships(archive, xlnt::constants::ArcWorkbook); - for(auto relationship : workbook_relationships) + for(const auto &relationship : workbook_relationships) { wb.create_relationship(relationship.get_id(), relationship.get_target_uri(), relationship.get_type()); } auto xml = xlnt::xml_serializer::deserialize(archive.read(xlnt::constants::ArcWorkbook)); - auto &root_node = xml.root(); + auto root_node = xml.get_child("workbook"); - auto &workbook_pr_node = root_node.get_child("workbookPr"); + auto workbook_pr_node = root_node.get_child("workbookPr"); wb.get_properties().excel_base_date = (workbook_pr_node.has_attribute("date1904") && workbook_pr_node.get_attribute("date1904") != "0") ? xlnt::calendar::mac_1904 : xlnt::calendar::windows_1900; xlnt::shared_strings_serializer shared_strings_serializer_; std::vector shared_strings; - shared_strings_serializer_.read_strings(xlnt::xml_serializer::deserialize(archive.read(xlnt::constants::ArcSharedString)), shared_strings); + shared_strings_serializer_.read_shared_strings(xlnt::xml_serializer::deserialize(archive.read(xlnt::constants::ArcSharedString)), shared_strings); + + for(auto shared_string : shared_strings) + { + wb.add_shared_string(shared_string); + } xlnt::style_serializer style_reader_(wb); style_reader_.read_stylesheet(xlnt::xml_serializer::deserialize(archive.read(xlnt::constants::ArcStyles))); - auto &sheets_node = root_node.get_child("sheets"); + auto sheets_node = root_node.get_child("sheets"); for(auto sheet_node : sheets_node.get_children()) { auto rel = wb.get_relationship(sheet_node.get_attribute("r:id")); auto ws = wb.create_sheet(sheet_node.get_attribute("name"), rel); + auto ws_filename = "xl/" + rel.get_target_uri(); xlnt::worksheet_serializer worksheet_serializer(ws); - worksheet_serializer.read_worksheet(xlnt::xml_serializer::deserialize(archive.read(rel.get_target_uri())), shared_strings, rel); + worksheet_serializer.read_worksheet(xlnt::xml_serializer::deserialize(archive.read(ws_filename))); } return true; @@ -126,52 +132,45 @@ bool excel_serializer::load_workbook(const std::string &filename, bool guess_typ throw invalid_file_exception(filename); } - return ::load_workbook(archive_, guess_types, data_only, wb_); + return ::load_workbook(archive_, guess_types, data_only, workbook_); } bool excel_serializer::load_virtual_workbook(const std::vector &bytes, bool guess_types, bool data_only) { archive_.load(bytes); - return ::load_workbook(archive_, guess_types, data_only, wb_); + return ::load_workbook(archive_, guess_types, data_only, workbook_); } -excel_serializer::excel_serializer(workbook &wb) : wb_(wb) +excel_serializer::excel_serializer(workbook &wb) : workbook_(wb) { } -void excel_serializer::write_data(bool as_template) +void excel_serializer::write_data(bool /*as_template*/) { relationship_serializer relationship_serializer_; - xlnt::xml_document root_rels_xml; - relationship_serializer_.write_relationships(wb_.get_root_relationships(), "", root_rels_xml); - archive_.writestr(constants::ArcRootRels, xml_serializer::serialize(root_rels_xml)); - - xml_document workbook_rels_xml; - relationship_serializer_.write_relationships(wb_.get_relationships(), "", workbook_rels_xml); - archive_.writestr(constants::ArcWorkbookRels, xml_serializer::serialize(workbook_rels_xml)); + relationship_serializer_.write_relationships(workbook_.get_root_relationships(), "", archive_); + relationship_serializer_.write_relationships(workbook_.get_relationships(), constants::ArcWorkbook, archive_); xml_document properties_app_xml; - workbook_serializer workbook_serializer_(wb_); + workbook_serializer workbook_serializer_(workbook_); archive_.writestr(constants::ArcApp, xml_serializer::serialize(workbook_serializer_.write_properties_app())); archive_.writestr(constants::ArcCore, xml_serializer::serialize(workbook_serializer_.write_properties_core())); theme_serializer theme_serializer_; - xml_document theme_xml = theme_serializer_.write_theme(wb_.get_loaded_theme()); - archive_.writestr(constants::ArcTheme, xml_serializer::serialize(theme_xml)); + archive_.writestr(constants::ArcTheme, theme_serializer_.write_theme(workbook_.get_loaded_theme()).to_string()); + + xlnt::shared_strings_serializer shared_strings_serializer_; + archive_.writestr(constants::ArcSharedString, xml_serializer::serialize(shared_strings_serializer_.write_shared_strings(workbook_.get_shared_strings()))); archive_.writestr(constants::ArcWorkbook, xml_serializer::serialize(workbook_serializer_.write_workbook())); - style_serializer style_serializer_(wb_); - xml_document style_xml; - style_serializer_.write_stylesheet(style_xml); - archive_.writestr(constants::ArcStyles, xml_serializer::serialize(style_xml)); + style_serializer style_serializer_(workbook_); + archive_.writestr(constants::ArcStyles, style_serializer_.write_stylesheet().to_string()); - manifest_serializer manifest_serializer_(wb_.get_manifest()); - xml_document manifest_xml; - manifest_serializer_.write_manifest(manifest_xml); - archive_.writestr(constants::ArcContentTypes, xml_serializer::serialize(manifest_xml)); + manifest_serializer manifest_serializer_(workbook_.get_manifest()); + archive_.writestr(constants::ArcContentTypes, manifest_serializer_.write_manifest().to_string()); write_worksheets(); } @@ -180,17 +179,16 @@ void excel_serializer::write_worksheets() { std::size_t index = 0; - for(auto ws : wb_) + for(auto ws : workbook_) { - for(auto relationship : wb_.get_relationships()) + for(auto relationship : workbook_.get_relationships()) { if(relationship.get_type() == relationship::type::worksheet && workbook::index_from_ws_filename(relationship.get_target_uri()) == index) { worksheet_serializer serializer_(ws); - xml_document xml; - serializer_.write_worksheet(shared_strings_, xml); - archive_.writestr(relationship.get_target_uri(), xml_serializer::serialize(xml)); + std::string ws_filename = "xl/" + relationship.get_target_uri(); + archive_.writestr(ws_filename, serializer_.write_worksheet().to_string()); break; } } diff --git a/source/s11n/manifest_serializer.cpp b/source/s11n/manifest_serializer.cpp index 6650e6ec..5fe83edc 100644 --- a/source/s11n/manifest_serializer.cpp +++ b/source/s11n/manifest_serializer.cpp @@ -1,33 +1,81 @@ #include #include +#include #include #include "detail/constants.hpp" namespace xlnt { -bool manifest_serializer::write_manifest(xml_document &xml) +manifest_serializer::manifest_serializer(manifest &m) : manifest_(m) { + +} + +void manifest_serializer::read_manifest(const xml_document &xml) +{ + const auto root_node = xml.get_child("Types"); + + for(const auto child : root_node.get_children()) + { + if(child.get_name() == "Default") + { + manifest_.add_default_type(child.get_attribute("Extension"), child.get_attribute("ContentType")); + } + else if(child.get_name() == "Override") + { + manifest_.add_override_type(child.get_attribute("PartName"), child.get_attribute("ContentType")); + } + } +} + +xml_document manifest_serializer::write_manifest() const +{ + xml_document xml; + + auto root_node = xml.add_child("Types"); xml.add_namespace("", constants::Namespaces.at("content-types")); - auto root_node = xml.root(); - root_node.set_name("Types"); - - for(const auto &default_type : manifest_.get_default_types()) + for(const auto default_type : manifest_.get_default_types()) { auto type_node = root_node.add_child("Default"); type_node.add_attribute("Extension", default_type.get_extension()); - type_node.add_attribute("ContentType", default_type.get_extension()); + type_node.add_attribute("ContentType", default_type.get_content_type()); } - for(const auto &override_type : manifest_.get_override_types()) + for(const auto override_type : manifest_.get_override_types()) { auto type_node = root_node.add_child("Override"); type_node.add_attribute("PartName", override_type.get_part_name()); type_node.add_attribute("ContentType", override_type.get_content_type()); } - return true; + return xml; +} + +std::string manifest_serializer::determine_document_type() const +{ + if(!manifest_.has_override_type(constants::ArcWorkbook)) + { + return "unsupported"; + } + + std::string type = manifest_.get_override_type(constants::ArcWorkbook); + + if(type == "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml") + { + return "excel"; + } + else if(type == "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml") + { + return "powerpoint"; + } + else if(type == "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml") + { + return "word"; + } + + return "unsupported"; } } // namespace xlnt diff --git a/source/s11n/relationship_serializer.cpp b/source/s11n/relationship_serializer.cpp index 24a3304c..27cf0f3d 100644 --- a/source/s11n/relationship_serializer.cpp +++ b/source/s11n/relationship_serializer.cpp @@ -1,17 +1,42 @@ #include #include +#include #include #include #include "detail/constants.hpp" +namespace { + +std::string make_rels_name(const std::string &target) +{ + const char sep = '/'; + + if(target.empty() || target.back() == sep) + { + return target + "_rels/.rels"; + } + + auto sep_pos = target.find_last_of(sep); + auto first_part = target.substr(0, sep_pos + 1); + auto last_part = target.substr(sep_pos + 1); + + return first_part + "_rels/" + last_part + ".rels"; +} + +} + namespace xlnt { -bool relationship_serializer::read_relationships(const xml_document &xml, const std::string &dir, std::vector &relationships) +std::vector relationship_serializer::read_relationships(zip_file &archive, const std::string &target) { - auto root_node = xml.root(); - root_node.set_name("Relationships"); + xml_document xml; + xml.from_string(archive.read(make_rels_name(target))); + + auto root_node = xml.get_child("Relationships"); + + std::vector relationships; for(auto relationship_node : root_node.get_children()) { @@ -27,25 +52,21 @@ bool relationship_serializer::read_relationships(const xml_document &xml, const relationships.push_back(xlnt::relationship(type, id, target)); } - return true; + return relationships; } -bool relationship_serializer::write_relationships(const std::vector &relationships, const std::string &dir, xml_document &xml) +bool relationship_serializer::write_relationships(const std::vector &relationships, const std::string &target, zip_file &archive) { - xml.add_namespace("", constants::Namespaces.at("relationships")); + xml_document xml; - auto root_node = xml.root(); - root_node.set_name("Relationships"); + auto root_node = xml.add_child("Relationships"); + + xml.add_namespace("", constants::Namespaces.at("relationships")); for(const auto &relationship : relationships) { auto target = relationship.get_target_uri(); - if (dir != "" && target.substr(0, dir.size()) == dir) - { - target = target.substr(dir.size()); - } - auto relationship_node = root_node.add_child("Relationship"); relationship_node.add_attribute("Id", relationship.get_id()); @@ -58,6 +79,8 @@ bool relationship_serializer::write_relationships(const std::vector namespace xlnt { + +xml_document shared_strings_serializer::write_shared_strings(const std::vector &strings) +{ + xml_document xml; + + auto root_node = xml.add_child("sst"); + + xml.add_namespace("", "http://schemas.openxmlformats.org/spreadsheetml/2006/main"); + + root_node.add_attribute("uniqueCount", std::to_string(strings.size())); + + for(const auto &string : strings) + { + root_node.add_child("si").add_child("t").set_text(string); + } + + return xml; +} -bool shared_strings_serializer::read_strings(const xml_document &xml, std::vector &strings) +bool shared_strings_serializer::read_shared_strings(const xml_document &xml, std::vector &strings) { strings.clear(); - auto root_node = xml.root(); - root_node.set_name("sst"); + auto root_node = xml.get_child("sst"); auto unique_count = std::stoull(root_node.get_attribute("uniqueCount")); for(const auto &si_node : root_node.get_children()) { - strings.push_back(si_node.get_child("t").get_text()); + if(si_node.get_name() != "si") + { + continue; + } + + if(si_node.has_child("t")) + { + strings.push_back(si_node.get_child("t").get_text()); + } + else if(si_node.has_child("r")) + { + strings.push_back(si_node.get_child("r").get_child("t").get_text()); + } } if(unique_count != strings.size()) diff --git a/source/s11n/style_serializer.cpp b/source/s11n/style_serializer.cpp index e30cfb8f..04156288 100644 --- a/source/s11n/style_serializer.cpp +++ b/source/s11n/style_serializer.cpp @@ -132,11 +132,11 @@ xlnt::border_style border_style_from_string(const std::string &border_style_stri namespace xlnt { -style_serializer::style_serializer(workbook &wb) : wb_(wb) +style_serializer::style_serializer(workbook &wb) : workbook_(wb) { } -protection style_serializer::read_protection(const xml_node &protection_node) const +protection style_serializer::read_protection(const xml_node &protection_node) { protection prot; @@ -146,7 +146,7 @@ protection style_serializer::read_protection(const xml_node &protection_node) co return prot; } -alignment style_serializer::read_alignment(const xml_node &alignment_node) const +alignment style_serializer::read_alignment(const xml_node &alignment_node) { alignment align; @@ -219,7 +219,7 @@ alignment style_serializer::read_alignment(const xml_node &alignment_node) const return align; } -style style_serializer::read_style(const xml_node &style_node) const +style style_serializer::read_style(const xml_node &style_node) { style s; @@ -228,9 +228,9 @@ style style_serializer::read_style(const xml_node &style_node) const bool builtin_format = true; - for(auto num_fmt : wb_.get_number_formats()) + for(auto num_fmt : workbook_.get_number_formats()) { - if(num_fmt.get_id() == s.get_number_format_id()) + if(static_cast(num_fmt.get_id()) == s.get_number_format_id()) { s.number_format_ = num_fmt; builtin_format = false; @@ -245,15 +245,15 @@ style style_serializer::read_style(const xml_node &style_node) const s.apply_font(is_true(style_node.get_attribute("applyFont"))); s.font_id_ = style_node.has_attribute("fontId") ? std::stoull(style_node.get_attribute("fontId")) : 0; - s.font_ = wb_.get_font(s.font_id_); + s.font_ = workbook_.get_font(s.font_id_); s.apply_fill(is_true(style_node.get_attribute("applyFill"))); s.fill_id_ = style_node.has_attribute("fillId") ? std::stoull(style_node.get_attribute("fillId")) : 0; - s.fill_ = wb_.get_fill(s.fill_id_); + s.fill_ = workbook_.get_fill(s.fill_id_); s.apply_border(is_true(style_node.get_attribute("applyBorder"))); s.border_id_ = style_node.has_attribute("borderId") ? std::stoull(style_node.get_attribute("borderId")) : 0; - s.border_ = wb_.get_border(s.border_id_); + s.border_ = workbook_.get_border(s.border_id_); s.apply_protection(style_node.has_attribute("protection")); @@ -276,7 +276,7 @@ style style_serializer::read_style(const xml_node &style_node) const bool style_serializer::read_stylesheet(const xml_document &xml) { - auto stylesheet_node = xml.root(); + auto stylesheet_node = xml.get_child("styleSheet"); read_borders(stylesheet_node.get_child("borders")); read_fills(stylesheet_node.get_child("fills")); @@ -293,7 +293,7 @@ bool style_serializer::read_stylesheet(const xml_document &xml) continue; } - wb_.add_style(read_style(xf_node)); + workbook_.add_style(read_style(xf_node)); } return true; @@ -320,7 +320,7 @@ bool style_serializer::read_number_formats(const xml_node &number_formats_node) nf.set_format_string(format_string); nf.set_id(std::stoull(num_fmt_node.get_attribute("numFmtId"))); - wb_.add_number_format(nf); + workbook_.add_number_format(nf); } return true; @@ -330,13 +330,13 @@ bool style_serializer::read_fonts(const xml_node &fonts_node) { for(auto font_node : fonts_node.get_children()) { - wb_.add_font(read_font(font_node)); + workbook_.add_font(read_font(font_node)); } return true; } -font style_serializer::read_font(const xlnt::xml_node &font_node) const +font style_serializer::read_font(const xlnt::xml_node &font_node) { font new_font; @@ -390,7 +390,7 @@ bool style_serializer::read_colors(const xml_node &colors_node) for(const auto &indexed_color : indexed_colors) { - wb_.add_color(indexed_color); + workbook_.add_color(indexed_color); } } @@ -401,13 +401,13 @@ bool style_serializer::read_fills(const xml_node &fills_node) { for(auto fill_node : fills_node.get_children()) { - wb_.add_fill(read_fill(fill_node)); + workbook_.add_fill(read_fill(fill_node)); } return true; } -fill style_serializer::read_fill(const xml_node &fill_node) const +fill style_serializer::read_fill(const xml_node &fill_node) { fill new_fill; @@ -432,7 +432,7 @@ fill style_serializer::read_fill(const xml_node &fill_node) const return new_fill; } -side style_serializer::read_side(const xml_node &side_node) const +side style_serializer::read_side(const xml_node &side_node) { side new_side; @@ -453,13 +453,13 @@ bool style_serializer::read_borders(const xml_node &borders_node) { for(auto border_node : borders_node.get_children()) { - wb_.add_border(read_border(border_node)); + workbook_.add_border(read_border(border_node)); } return true; } -border style_serializer::read_border(const xml_node &border_node) const +border style_serializer::read_border(const xml_node &border_node) { border new_border; @@ -520,21 +520,56 @@ border style_serializer::read_border(const xml_node &border_node) const return new_border; } -bool style_serializer::write_stylesheet(xlnt::xml_document &xml) const +std::vector style_serializer::read_indexed_colors(const xml_node &indexed_colors_node) { + std::vector colors; + + for(auto color_node : indexed_colors_node.get_children()) + { + colors.push_back(read_color(color_node)); + } + + return colors; +} + +color style_serializer::read_color(const xml_node &color_node) +{ + if(color_node.has_attribute("rgb")) + { + return color(color::type::rgb, color_node.get_attribute("rgb")); + } + else if(color_node.has_attribute("theme")) + { + return color(color::type::theme, std::stoull(color_node.get_attribute("theme"))); + } + else if(color_node.has_attribute("indexed")) + { + return color(color::type::indexed, std::stoull(color_node.get_attribute("indexed"))); + } + else if(color_node.has_attribute("auto")) + { + return color(color::type::auto_, std::stoull(color_node.get_attribute("auto"))); + } + + throw std::runtime_error("bad color"); + return color(); +} + +xml_document style_serializer::write_stylesheet() const +{ + xml_document xml; + + auto style_sheet_node = xml.add_child("styleSheet"); xml.add_namespace("", "http://schemas.openxmlformats.org/spreadsheetml/2006/main"); xml.add_namespace("mc", "http://schemas.openxmlformats.org/markup-compatibility/2006"); + style_sheet_node.add_attribute("mc:Ignorable", "x14ac"); xml.add_namespace("x14ac", "http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac"); - auto &style_sheet_node = xml.root(); - style_sheet_node.set_name("styleSheet"); - style_sheet_node.add_attribute("mc:Ignorable", "x14ac"); - auto num_fmts_node = style_sheet_node.add_child("numFmts"); - auto num_fmts = wb_.get_number_formats(); + auto num_fmts = workbook_.get_number_formats(); num_fmts_node.add_attribute("count", std::to_string(num_fmts.size())); - for(auto &num_fmt : num_fmts) + for(const auto &num_fmt : num_fmts) { auto num_fmt_node = num_fmts_node.add_child("numFmt"); num_fmt_node.add_attribute("numFmtId", std::to_string(num_fmt.get_id())); @@ -542,7 +577,7 @@ bool style_serializer::write_stylesheet(xlnt::xml_document &xml) const } auto fonts_node = style_sheet_node.add_child("fonts"); - auto fonts = wb_.get_fonts(); + auto fonts = workbook_.get_fonts(); if(fonts.empty()) { @@ -620,7 +655,7 @@ bool style_serializer::write_stylesheet(xlnt::xml_document &xml) const } auto fills_node = style_sheet_node.add_child("fills"); - const auto &fills = wb_.get_fills(); + const auto &fills = workbook_.get_fills(); fills_node.add_attribute("count", std::to_string(fills.size())); for(auto &fill_ : fills) @@ -635,7 +670,7 @@ bool style_serializer::write_stylesheet(xlnt::xml_document &xml) const if(fill_.has_foreground_color()) { - auto &fg_color_node = pattern_fill_node.add_child("fgColor"); + auto fg_color_node = pattern_fill_node.add_child("fgColor"); switch(fill_.get_foreground_color().get_type()) { @@ -689,7 +724,7 @@ bool style_serializer::write_stylesheet(xlnt::xml_document &xml) const } auto borders_node = style_sheet_node.add_child("borders"); - const auto &borders = wb_.get_borders(); + const auto &borders = workbook_.get_borders(); borders_node.add_attribute("count", std::to_string(borders.size())); for(const auto &border_ : borders) @@ -764,17 +799,17 @@ bool style_serializer::write_stylesheet(xlnt::xml_document &xml) const } } - auto &cell_style_xfs_node = style_sheet_node.add_child("cellStyleXfs"); + auto cell_style_xfs_node = style_sheet_node.add_child("cellStyleXfs"); cell_style_xfs_node.add_attribute("count", "1"); - auto &style_xf_node = cell_style_xfs_node.add_child("xf"); + auto style_xf_node = cell_style_xfs_node.add_child("xf"); style_xf_node.add_attribute("numFmtId", "0"); style_xf_node.add_attribute("fontId", "0"); style_xf_node.add_attribute("fillId", "0"); style_xf_node.add_attribute("borderId", "0"); auto cell_xfs_node = style_sheet_node.add_child("cellXfs"); - const auto &styles = wb_.get_styles(); + const auto &styles = workbook_.get_styles(); cell_xfs_node.add_attribute("count", std::to_string(styles.size())); for(auto &style : styles) @@ -876,7 +911,7 @@ bool style_serializer::write_stylesheet(xlnt::xml_document &xml) const auto colors_node = style_sheet_node.add_child("colors"); auto indexed_colors_node = colors_node.add_child("indexedColors"); - for(auto &c : wb_.get_colors()) + for(auto &c : workbook_.get_colors()) { indexed_colors_node.add_child("rgbColor").add_attribute("rgb", c.get_rgb_string()); } @@ -887,16 +922,16 @@ bool style_serializer::write_stylesheet(xlnt::xml_document &xml) const ext_node.add_attribute("xmlns:x14", "http://schemas.microsoft.com/office/spreadsheetml/2009/9/main"); ext_node.add_child("x14:slicerStyles").add_attribute("defaultSlicerStyle", "SlicerStyleLight1"); - return true; + return xml; } -bool style_serializer::write_number_formats(xml_node &number_formats_node) const +bool style_serializer::write_number_formats(xml_node number_formats_node) const { - number_formats_node.add_attribute("count", std::to_string(wb_.get_number_formats().size())); + number_formats_node.add_attribute("count", std::to_string(workbook_.get_number_formats().size())); - for(const auto &num_fmt : wb_.get_number_formats()) + for(const auto &num_fmt : workbook_.get_number_formats()) { - auto &num_fmt_node = number_formats_node.add_child("numFmt"); + auto num_fmt_node = number_formats_node.add_child("numFmt"); num_fmt_node.add_attribute("numFmtId", std::to_string(num_fmt.get_id())); num_fmt_node.add_attribute("formatCode", num_fmt.get_format_string()); } diff --git a/source/s11n/theme_serializer.cpp b/source/s11n/theme_serializer.cpp index eff2f9e5..e43c3d4c 100644 --- a/source/s11n/theme_serializer.cpp +++ b/source/s11n/theme_serializer.cpp @@ -7,17 +7,16 @@ namespace xlnt { //I have no idea what this stuff is. I hope it was worth it. -xml_document theme_serializer::write_theme(const theme &theme_) +xml_document theme_serializer::write_theme(const theme &) const { xml_document xml; + auto theme_node = xml.add_child("a:theme"); xml.add_namespace("a", constants::Namespaces.at("drawingml")); - - auto &theme_node = xml.root(); - theme_node.set_name("a:theme"); theme_node.add_attribute("name", "Office Theme"); - auto &theme_elements_node = theme_node.add_child("a:themeElements"); - auto &clr_scheme_node = theme_elements_node.add_child("a:clrScheme"); + + auto theme_elements_node = theme_node.add_child("a:themeElements"); + auto clr_scheme_node = theme_elements_node.add_child("a:clrScheme"); clr_scheme_node.add_attribute("name", "Office"); struct scheme_element @@ -112,19 +111,19 @@ xml_document theme_serializer::write_theme(const theme &theme_) { if(scheme.typeface) { - auto &major_font_node = major_fonts_node.add_child(scheme.script); + auto major_font_node = major_fonts_node.add_child(scheme.script); major_font_node.add_attribute("typeface", scheme.major); - auto &minor_font_node = minor_fonts_node.add_child(scheme.script); + auto minor_font_node = minor_fonts_node.add_child(scheme.script); minor_font_node.add_attribute("typeface", scheme.minor); } else { - auto &major_font_node = major_fonts_node.add_child("a:font"); + auto major_font_node = major_fonts_node.add_child("a:font"); major_font_node.add_attribute("script", scheme.script); major_font_node.add_attribute("typeface", scheme.major); - auto &minor_font_node = minor_fonts_node.add_child("a:font"); + auto minor_font_node = minor_fonts_node.add_child("a:font"); minor_font_node.add_attribute("script", scheme.script); minor_font_node.add_attribute("typeface", scheme.minor); } @@ -141,7 +140,7 @@ xml_document theme_serializer::write_theme(const theme &theme_) auto grad_fill_list = grad_fill_node.add_child("a:gsLst"); auto gs_node = grad_fill_list.add_child("a:gs"); - gs_node.add_attribute("pos", 0); + gs_node.add_attribute("pos", "0"); auto scheme_color_node = gs_node.add_child("a:schemeClr"); scheme_color_node.add_attribute("val", "phClr"); scheme_color_node.add_child("a:tint").add_attribute("val", "50000"); diff --git a/source/s11n/workbook_serializer.cpp b/source/s11n/workbook_serializer.cpp index 45347069..59044ca7 100644 --- a/source/s11n/workbook_serializer.cpp +++ b/source/s11n/workbook_serializer.cpp @@ -50,10 +50,14 @@ std::string datetime_to_w3cdtf(const xlnt::datetime &dt) } // namespace namespace xlnt { - - /* -std::vector> workbook_serializer::read_sheets(zip_file &archive) + +workbook_serializer::workbook_serializer(workbook &wb) : workbook_(wb) { +} + +std::vector workbook_serializer::read_sheets() +{ + /* std::string ns; for(auto child : doc.children()) @@ -72,9 +76,9 @@ std::vector> workbook_serializer::read_sheet auto root_node = doc.get_child(with_ns("workbook")); auto sheets_node = root_node.get_child(with_ns("sheets")); - - std::vector> sheets; - + */ + std::vector sheets; + /* // store temp because pugixml iteration uses the internal char array multiple times auto sheet_element_name = with_ns("sheet"); @@ -84,15 +88,14 @@ std::vector> workbook_serializer::read_sheet std::string name = sheet_node.attribute("name").as_string(); sheets.push_back(std::make_pair(id, name)); } - + */ return sheets; } - */ void workbook_serializer::read_properties_core(const xml_document &xml) { - auto &props = wb_.get_properties(); - auto root_node = xml.root(); + auto &props = workbook_.get_properties(); + auto root_node = xml.get_child("dc:coreProperties"); props.excel_base_date = calendar::windows_1900; @@ -116,44 +119,19 @@ void workbook_serializer::read_properties_core(const xml_document &xml) } } -std::string workbook_serializer::determine_document_type(const manifest &manifest) -{ - if(!manifest.has_override_type(constants::ArcWorkbook)) - { - return "unsupported"; - } - - std::string type = manifest.get_override_type(constants::ArcWorkbook); - - if(type == "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml") - { - return "excel"; - } - else if(type == "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml") - { - return "powerpoint"; - } - else if(type == "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml") - { - return "word"; - } - - return "unsupported"; -} - /// /// Return a list of worksheets. /// content types has a list of paths but no titles /// workbook has a list of titles and relIds but no paths /// workbook_rels has a list of relIds and paths but no titles /// -std::vector workbook_serializer::detect_worksheets() +std::vector workbook_serializer::detect_worksheets() { static const std::string ValidWorksheet = "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"; std::vector valid_sheets; - for(const auto &content_type : wb_.get_manifest().get_override_types()) + for(const auto &content_type : workbook_.get_manifest().get_override_types()) { if(content_type.get_content_type() == ValidWorksheet) { @@ -161,7 +139,7 @@ std::vector workbook_serializer::detect_worksheets() } } - auto &workbook_relationships = wb_.get_relationships(); + auto &workbook_relationships = workbook_.get_relationships(); std::vector> result; for(const auto &ws : read_sheets()) @@ -180,19 +158,18 @@ std::vector workbook_serializer::detect_worksheets() xml_document workbook_serializer::write_properties_core() const { - auto &props = wb_.get_properties(); + auto &props = workbook_.get_properties(); xml_document xml; + auto root_node = xml.add_child("cp:coreProperties"); + xml.add_namespace("cp", "http://schemas.openxmlformats.org/package/2006/metadata/core-properties"); xml.add_namespace("dc", "http://purl.org/dc/elements/1.1/"); xml.add_namespace("dcmitype", "http://purl.org/dc/dcmitype/"); xml.add_namespace("dcterms", "http://purl.org/dc/terms/"); xml.add_namespace("xsi", "http://www.w3.org/2001/XMLSchema-instance"); - auto &root_node = xml.root(); - root_node.set_name("cp:coreProperties"); - root_node.add_child("dc:creator").set_text(props.creator); root_node.add_child("cp:lastModifiedBy").set_text(props.last_modified_by); root_node.add_child("dcterms:created").set_text(datetime_to_w3cdtf(props.created)); @@ -212,11 +189,10 @@ xml_document workbook_serializer::write_properties_app() const { xml_document xml; - xml.add_namespace("xmlns", "http://schemas.openxmlformats.org/officeDocument/2006/extended-properties"); - xml.add_namespace("xmlns:vt", "http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"); + auto root_node = xml.add_child("Properties"); - auto &root_node = xml.root(); - root_node.set_name("Properties"); + xml.add_namespace("", "http://schemas.openxmlformats.org/officeDocument/2006/extended-properties"); + xml.add_namespace("vt", "http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"); root_node.add_child("Application").set_text("Microsoft Excel"); root_node.add_child("DocSecurity").set_text("0"); @@ -232,14 +208,14 @@ xml_document workbook_serializer::write_properties_app() const heading_pairs_vector_node.add_attribute("baseType", "variant"); heading_pairs_vector_node.add_attribute("size", "2"); heading_pairs_vector_node.add_child("vt:variant").add_child("vt:lpstr").set_text("Worksheets"); - heading_pairs_vector_node.add_child("vt:variant").add_child("vt:i4").set_text(std::to_string(wb_.get_sheet_names().size())); + heading_pairs_vector_node.add_child("vt:variant").add_child("vt:i4").set_text(std::to_string(workbook_.get_sheet_names().size())); auto titles_of_parts_node = root_node.add_child("TitlesOfParts"); auto titles_of_parts_vector_node = titles_of_parts_node.add_child("vt:vector"); titles_of_parts_vector_node.add_attribute("baseType", "lpstr"); - titles_of_parts_vector_node.add_attribute("size", std::to_string(wb_.get_sheet_names().size())); + titles_of_parts_vector_node.add_attribute("size", std::to_string(workbook_.get_sheet_names().size())); - for(auto ws : wb_) + for(auto ws : workbook_) { titles_of_parts_vector_node.add_child("vt:lpstr").set_text(ws.get_title()); } @@ -251,7 +227,7 @@ xml_document workbook_serializer::write_workbook() const { std::size_t num_visible = 0; - for(auto ws : wb_) + for(auto ws : workbook_) { if(ws.get_page_setup().get_sheet_state() == xlnt::page_setup::sheet_state::visible) { @@ -266,22 +242,21 @@ xml_document workbook_serializer::write_workbook() const xml_document xml; - xml.add_namespace("xmlns", "http://schemas.openxmlformats.org/spreadsheetml/2006/main"); - xml.add_namespace("xmlns:r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships"); + auto root_node = xml.add_child("workbook"); - auto &root_node = xml.root(); - root_node.set_name("workbook"); + xml.add_namespace("", "http://schemas.openxmlformats.org/spreadsheetml/2006/main"); + xml.add_namespace("r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships"); - auto &file_version_node = root_node.add_child("fileVersion"); + auto file_version_node = root_node.add_child("fileVersion"); file_version_node.add_attribute("appName", "xl"); file_version_node.add_attribute("lastEdited", "4"); file_version_node.add_attribute("lowestEdited", "4"); file_version_node.add_attribute("rupBuild", "4505"); - auto &workbook_pr_node = root_node.add_child("workbookPr"); + auto workbook_pr_node = root_node.add_child("workbookPr"); workbook_pr_node.add_attribute("codeName", "ThisWorkbook"); workbook_pr_node.add_attribute("defaultThemeVersion", "124226"); - workbook_pr_node.add_attribute("date1904", wb_.get_properties().excel_base_date == calendar::mac_1904 ? "1" : "0"); + workbook_pr_node.add_attribute("date1904", workbook_.get_properties().excel_base_date == calendar::mac_1904 ? "1" : "0"); auto book_views_node = root_node.add_child("bookViews"); auto workbook_view_node = book_views_node.add_child("workbookView"); @@ -298,10 +273,11 @@ xml_document workbook_serializer::write_workbook() const auto sheets_node = root_node.add_child("sheets"); auto defined_names_node = root_node.add_child("definedNames"); - for(auto relationship : wb_.get_relationships()) + for(const auto &relationship : workbook_.get_relationships()) { if(relationship.get_type() == relationship::type::worksheet) { + //TODO: this is ugly std::string sheet_index_string = relationship.get_target_uri(); sheet_index_string = sheet_index_string.substr(0, sheet_index_string.find('.')); sheet_index_string = sheet_index_string.substr(sheet_index_string.find_last_of('/')); @@ -312,16 +288,16 @@ xml_document workbook_serializer::write_workbook() const sheet_index_string = sheet_index_string.substr(static_cast(first_digit + 1)); std::size_t sheet_index = static_cast(std::stoll(sheet_index_string) - 1); - auto ws = wb_.get_sheet_by_index(sheet_index); + auto ws = workbook_.get_sheet_by_index(sheet_index); auto sheet_node = sheets_node.add_child("sheet"); sheet_node.add_attribute("name", ws.get_title()); - sheet_node.add_attribute("r:id", relationship.get_id()); sheet_node.add_attribute("sheetId", std::to_string(sheet_index + 1)); + sheet_node.add_attribute("r:id", relationship.get_id()); if(ws.has_auto_filter()) { - auto &defined_name_node = defined_names_node.add_child("definedName"); + auto defined_name_node = defined_names_node.add_child("definedName"); defined_name_node.add_attribute("name", "_xlnm._FilterDatabase"); defined_name_node.add_attribute("hidden", "1"); defined_name_node.add_attribute("localSheetId", "0"); @@ -339,14 +315,16 @@ xml_document workbook_serializer::write_workbook() const return xml; } -bool workbook_serializer::write_named_ranges(xlnt::xml_node &named_ranges_node) +xml_node workbook_serializer::write_named_ranges() const { - for(auto &named_range : wb_.get_named_ranges()) + xlnt::xml_node named_ranges_node; + + for(auto &named_range : workbook_.get_named_ranges()) { named_ranges_node.add_child(named_range.get_name()); } - return true; + return named_ranges_node; } } // namespace xlnt diff --git a/source/s11n/worksheet_serializer.cpp b/source/s11n/worksheet_serializer.cpp index 3abdaf60..f6d8087e 100644 --- a/source/s11n/worksheet_serializer.cpp +++ b/source/s11n/worksheet_serializer.cpp @@ -21,10 +21,14 @@ bool is_integral(long double d) } // namepsace namespace xlnt { - -bool worksheet_serializer::read_worksheet(const xml_document &xml, const std::vector &string_table, const relationship &rel) + +worksheet_serializer::worksheet_serializer(worksheet sheet) : sheet_(sheet) { - auto &root_node = xml.root(); +} + +bool worksheet_serializer::read_worksheet(const xml_document &xml) +{ + auto &root_node = xml.get_child("worksheet"); auto &dimension_node = root_node.get_child("dimension"); std::string dimension = dimension_node.get_attribute("ref"); @@ -53,6 +57,8 @@ bool worksheet_serializer::read_worksheet(const xml_document &xml, const std::ve } } + auto &shared_strings = sheet_.get_parent().get_shared_strings(); + for(auto row_node : sheet_data_node.get_children()) { if(row_node.get_name() != "row") @@ -62,7 +68,7 @@ bool worksheet_serializer::read_worksheet(const xml_document &xml, const std::ve auto row_index = static_cast(std::stoull(row_node.get_attribute("r"))); - if(row_node.get_attribute("ht") != nullptr) + if(row_node.has_attribute("ht")) { sheet_.get_row_properties(row_index).height = std::stold(row_node.get_attribute("ht")); } @@ -111,10 +117,10 @@ bool worksheet_serializer::read_worksheet(const xml_document &xml, const std::ve bool has_value = cell_node.has_child("v"); std::string value_string = has_value ? cell_node.get_child("v").get_text() : ""; - bool has_type = cell_node.get_attribute("t") != nullptr; + bool has_type = cell_node.has_attribute("t"); std::string type = has_type ? cell_node.get_attribute("t") : ""; - bool has_style = cell_node.get_attribute("s") != nullptr; + bool has_style = cell_node.has_attribute("s"); int style_id = has_style ? std::stoull(cell_node.get_attribute("s")) : 0; bool has_formula = cell_node.has_child("f"); @@ -136,7 +142,7 @@ bool worksheet_serializer::read_worksheet(const xml_document &xml, const std::ve else if(has_type && type == "s" && !has_formula) // shared string { auto shared_string_index = std::stoull(value_string); - auto shared_string = string_table.at(shared_string_index); + auto shared_string = shared_strings.at(shared_string_index); cell.set_value(shared_string); } else if(has_type && type == "b") // boolean @@ -205,34 +211,35 @@ bool worksheet_serializer::read_worksheet(const xml_document &xml, const std::ve return true; } -bool worksheet_serializer::write_worksheet(const std::vector &string_table, xml_document &xml) +xml_document worksheet_serializer::write_worksheet() const { sheet_.get_cell("A1"); + xml_document xml; + + auto root_node = xml.add_child("worksheet"); + xml.add_namespace("", constants::Namespaces.at("spreadsheetml")); xml.add_namespace("r", constants::Namespaces.at("r")); - - auto &root_node = xml.root(); - root_node.set_name("worksheet"); - auto &sheet_pr_node = root_node.add_child("sheetPr"); + auto sheet_pr_node = root_node.add_child("sheetPr"); if(!sheet_.get_page_setup().is_default()) { - auto &page_set_up_pr_node = sheet_pr_node.add_child("pageSetUpPr"); + auto page_set_up_pr_node = sheet_pr_node.add_child("pageSetUpPr"); page_set_up_pr_node.add_attribute("fitToPage", sheet_.get_page_setup().fit_to_page() ? "1" : "0"); } - auto &outline_pr_node = sheet_pr_node.add_child("outlinePr"); + auto outline_pr_node = sheet_pr_node.add_child("outlinePr"); outline_pr_node.add_attribute("summaryBelow", "1"); outline_pr_node.add_attribute("summaryRight", "1"); - auto &dimension_node = root_node.add_child("dimension"); + auto dimension_node = root_node.add_child("dimension"); dimension_node.add_attribute("ref", sheet_.calculate_dimension().to_string()); - auto &sheet_views_node = root_node.add_child("sheetViews"); - auto &sheet_view_node = sheet_views_node.add_child("sheetView"); + auto sheet_views_node = root_node.add_child("sheetViews"); + auto sheet_view_node = sheet_views_node.add_child("sheetView"); sheet_view_node.add_attribute("workbookViewId", "0"); std::string active_pane = "bottomRight"; @@ -325,6 +332,7 @@ bool worksheet_serializer::write_worksheet(const std::vector &strin std::unordered_map hyperlink_references; auto sheet_data_node = root_node.add_child("sheetData"); + const auto &shared_strings = sheet_.get_parent().get_shared_strings(); for(auto row : sheet_.rows()) { @@ -395,9 +403,9 @@ bool worksheet_serializer::write_worksheet(const std::vector &strin int match_index = -1; - for(std::size_t i = 0; i < string_table.size(); i++) + for(std::size_t i = 0; i < shared_strings.size(); i++) { - if(string_table[i] == cell.get_value()) + if(shared_strings[i] == cell.get_value()) { match_index = static_cast(i); break; @@ -545,7 +553,7 @@ bool worksheet_serializer::write_worksheet(const std::vector &strin odd_footer_node.set_text(footer_text); } - return true; + return xml; } } // namespace xlnt diff --git a/source/s11n/xml_document.cpp b/source/s11n/xml_document.cpp index b316e6f1..ecc9f508 100644 --- a/source/s11n/xml_document.cpp +++ b/source/s11n/xml_document.cpp @@ -1,25 +1,78 @@ #include +#include +#include + +#include +#include namespace xlnt { + +xml_document::xml_document() : d_(new detail::xml_document_impl()) +{ +} + +xml_document::xml_document(const xml_document &other) : xml_document() +{ + d_->doc.append_copy(other.d_->doc.root()); +} + +xml_document::~xml_document() +{ +} void xml_document::set_encoding(const std::string &encoding) { - + d_->encoding = encoding; } void xml_document::add_namespace(const std::string &id, const std::string &uri) { + d_->doc.first_child().append_attribute((id.empty() ? "xmlns" : "xmlns:" + id).c_str()).set_value(uri.c_str()); +} + +xml_node xml_document::add_child(const xml_node &child) +{ + auto child_node = d_->doc.root().append_copy(child.d_->node); + return xml_node(detail::xml_node_impl(child_node)); +} + +xml_node xml_document::add_child(const std::string &child_name) +{ + auto child = d_->doc.root().append_child(child_name.c_str()); + return xml_node(detail::xml_node_impl(child)); +} + +xml_node xml_document::get_root() +{ + return xml_node(detail::xml_node_impl(d_->doc.root())); +} + +const xml_node xml_document::get_root() const +{ + return xml_node(detail::xml_node_impl(d_->doc.root())); +} + +std::string xml_document::to_string() const +{ + return xml_serializer::serialize(*this); +} + +xml_document &xml_document::from_string(const std::string &xml_string) +{ + auto doc = xml_serializer::deserialize(xml_string); + std::swap(doc.d_, d_); + return *this; } -xml_node &xml_document::root() +xml_node xml_document::get_child(const std::string &child_name) { - return root_; + return xml_node(detail::xml_node_impl(d_->doc.child(child_name.c_str()))); } -const xml_node &xml_document::root() const +const xml_node xml_document::get_child(const std::string &child_name) const { - return root_; + return xml_node(detail::xml_node_impl(d_->doc.child(child_name.c_str()))); } } // namespace xlnt \ No newline at end of file diff --git a/source/s11n/xml_node.cpp b/source/s11n/xml_node.cpp index 3b655917..4760d4bf 100644 --- a/source/s11n/xml_node.cpp +++ b/source/s11n/xml_node.cpp @@ -1,120 +1,139 @@ #include +#include + +#include namespace xlnt { -xml_node::xml_node() +xml_node::xml_node() : d_(new detail::xml_node_impl) { } -xml_node::xml_node(const std::string &name) +xml_node::xml_node(const detail::xml_node_impl &d) : xml_node() { - set_name(name); + d_->node = d.node; +} + +xml_node::~xml_node() +{ +} + +xml_node::xml_node(const xml_node &other) : xml_node() +{ + d_->node = other.d_->node; +} + +xml_node &xml_node::operator=(const xlnt::xml_node &other) +{ + d_->node = other.d_->node; + + return *this; } std::string xml_node::get_name() const { - return name_; + return d_->node.name(); } void xml_node::set_name(const std::string &name) { - name_ = name; + d_->node.set_name(name.c_str()); } bool xml_node::has_text() const { - return has_text_; + return d_->node.text() != nullptr; } std::string xml_node::get_text() const { - return text_; + return d_->node.text().as_string(); } void xml_node::set_text(const std::string &text) { - text_ = text; - has_text_ = true; + d_->node.text().set(text.c_str()); } -const std::vector &xml_node::get_children() const +const std::vector xml_node::get_children() const { - return children_; -} - -xml_node &xml_node::add_child(const xml_node &child) -{ - has_text_ = false; - text_.clear(); + std::vector children; - children_.push_back(child); - return children_.back(); + for(auto child : d_->node.children()) + { + children.push_back(xml_node(detail::xml_node_impl(child))); + } + + return children; } -xml_node &xml_node::add_child(const std::string &child_name) +xml_node xml_node::add_child(const xml_node &child) { - return add_child(xml_node(child_name)); + auto child_node = xml_node(detail::xml_node_impl(d_->node.append_child(child.get_name().c_str()))); + + for(auto attr : child.get_attributes()) + { + child_node.add_attribute(attr.first, attr.second); + } + + for(auto child_child : child.get_children()) + { + child_node.add_child(child_child); + } + + return child_node; } -const std::vector &xml_node::get_attributes() const +xml_node xml_node::add_child(const std::string &child_name) { - return attributes_; + return xml_node(detail::xml_node_impl(d_->node.append_child(child_name.c_str()))); +} + +const std::vector xml_node::get_attributes() const +{ + std::vector attributes; + + for(auto attr : d_->node.attributes()) + { + attributes.push_back(std::make_pair(attr.name(), attr.value())); + } + + return attributes; } void xml_node::add_attribute(const std::string &name, const std::string &value) { - attributes_.push_back(std::make_pair(name, value)); + d_->node.append_attribute(name.c_str()).set_value(value.c_str()); } bool xml_node::has_attribute(const std::string &attribute_name) const { - return std::find_if(attributes_.begin(), attributes_.end(), - [&](const string_pair &p) { return p.first == attribute_name; }) != attributes_.end(); + return d_->node.attribute(attribute_name.c_str()) != nullptr; } std::string xml_node::get_attribute(const std::string &attribute_name) const { - auto match = std::find_if(attributes_.begin(), attributes_.end(), - [&](const string_pair &p) { return p.first == attribute_name; }); - - if(match == attributes_.end()) - { - throw std::runtime_error("attribute doesn't exist: " + attribute_name); - } - - return match->second; + return d_->node.attribute(attribute_name.c_str()).value(); } bool xml_node::has_child(const std::string &child_name) const { - return std::find_if(children_.begin(), children_.end(), - [&](const xml_node &n) { return n.get_name() == child_name; }) != children_.end(); + return d_->node.child(child_name.c_str()) != nullptr; } -xml_node &xml_node::get_child(const std::string &child_name) +xml_node xml_node::get_child(const std::string &child_name) { - auto match = std::find_if(children_.begin(), children_.end(), - [&](const xml_node &n) { return n.get_name() == child_name; }); - - if(match == children_.end()) - { - throw std::runtime_error("child doesn't exist: " + child_name); - } - - return *match; + return xml_node(detail::xml_node_impl(d_->node.child(child_name.c_str()))); } -const xml_node &xml_node::get_child(const std::string &child_name) const +const xml_node xml_node::get_child(const std::string &child_name) const { - auto match = std::find_if(children_.begin(), children_.end(), - [&](const xml_node &n) { return n.get_name() == child_name; }); + return xml_node(detail::xml_node_impl(d_->node.child(child_name.c_str()))); +} - if(match == children_.end()) - { - throw std::runtime_error("child doesn't exist: " + child_name); - } - - return *match; +std::string xml_node::to_string() const +{ + return xml_serializer::serialize_node(*this); } } // namespace xlnt diff --git a/source/s11n/xml_serializer.cpp b/source/s11n/xml_serializer.cpp index f721c6ec..59ec2e60 100644 --- a/source/s11n/xml_serializer.cpp +++ b/source/s11n/xml_serializer.cpp @@ -5,65 +5,31 @@ #include #include "detail/include_pugixml.hpp" - -namespace { - -void serialize_node(const xlnt::xml_node &source, pugi::xml_node node) -{ - for(const auto &attribute : source.get_attributes()) - { - node.append_attribute(attribute.first.c_str()).set_value(attribute.second.c_str()); - } - - if(source.has_text()) - { - node.text().set(source.get_text().c_str()); - } - else - { - for(const auto &child : source.get_children()) - { - pugi::xml_node child_node = node.append_child(child.get_name().c_str()); - serialize_node(child, child_node); - } - } -} - -xlnt::xml_node deserialize_node(const pugi::xml_node source) -{ - xlnt::xml_node node(source.name()); - - for(const auto attribute : source.attributes()) - { - node.add_attribute(attribute.name(), attribute.as_string()); - } - - if(source.text() != nullptr) - { - node.set_text(source.text().as_string()); - } - else - { - for(const auto child : source.children()) - { - node.add_child(deserialize_node(child)); - } - } - - return node; -} - -} // namespace +#include "detail/xml_document_impl.hpp" +#include "detail/xml_node_impl.hpp" namespace xlnt { std::string xml_serializer::serialize(const xml_document &xml) { - pugi::xml_document doc; + std::ostringstream ss; + xml.d_->doc.save(ss, " ", pugi::format_default, pugi::encoding_utf8); - auto root = doc.root(); - root.set_name(xml.root().get_name().c_str()); - serialize_node(xml.root(), root); + return ss.str(); +} + +xml_document xml_serializer::deserialize(const std::string &xml_string) +{ + xml_document doc; + doc.d_->doc.load(xml_string.c_str()); + + return doc; +} + +std::string xml_serializer::serialize_node(const xml_node &xml) +{ + pugi::xml_document doc; + doc.append_copy(xml.d_->node); std::ostringstream ss; doc.save(ss); @@ -71,15 +37,4 @@ std::string xml_serializer::serialize(const xml_document &xml) return ss.str(); } -xml_document xml_serializer::deserialize(const std::string &xml_string) -{ - pugi::xml_document doc; - doc.load(xml_string.c_str()); - - xml_document result; - result.root() = deserialize_node(doc.root()); - - return result; -} - } // namespace xlnt diff --git a/source/workbook/manifest.cpp b/source/workbook/manifest.cpp index 01d64837..875f8fdb 100644 --- a/source/workbook/manifest.cpp +++ b/source/workbook/manifest.cpp @@ -1,7 +1,78 @@ #include +namespace { + +bool match_path(const std::string &path, const std::string &comparand) +{ + if(path == comparand) + { + return true; + } + + if(path[0] != '/' && path[0] != '.' && comparand[0] == '/') + { + return match_path("/" + path, comparand); + } + else if(comparand[0] != '/' && comparand[0] != '.' && path[0] == '/') + { + return match_path(path, "/" + comparand); + } + + return false; +} + +} // namespace + namespace xlnt { +default_type::default_type() +{ +} + +default_type::default_type(const std::string &extension, const std::string &content_type) : + extension_(extension), + content_type_(content_type) +{ +} + +default_type::default_type(const default_type &other) : + extension_(other.extension_), + content_type_(other.content_type_) +{ +} + +default_type &default_type::operator=(const default_type &other) +{ + extension_ = other.extension_; + content_type_ = other.content_type_; + + return *this; +} + +override_type::override_type() +{ +} + +override_type::override_type(const std::string &part_name, const std::string &content_type) : + part_name_(part_name), + content_type_(content_type) +{ +} + +override_type::override_type(const override_type &other) : + part_name_(other.part_name_), + content_type_(other.content_type_) +{ +} + +override_type &override_type::operator=(const override_type &other) +{ + part_name_ = other.part_name_; + content_type_ = other.content_type_; + + return *this; +} + bool manifest::has_default_type(const std::string &extension) const { return std::find_if(default_types_.begin(), default_types_.end(), @@ -11,7 +82,17 @@ bool manifest::has_default_type(const std::string &extension) const bool manifest::has_override_type(const std::string &part_name) const { return std::find_if(override_types_.begin(), override_types_.end(), - [&](const override_type &d) { return d.get_part_name() == part_name; }) != override_types_.end(); + [&](const override_type &d) { return match_path(d.get_part_name(), part_name); }) != override_types_.end(); +} + +void manifest::add_default_type(const std::string &extension, const std::string &content_type) +{ + default_types_.push_back(default_type(extension, content_type)); +} + +void manifest::add_override_type(const std::string &part_name, const std::string &content_type) +{ + override_types_.push_back(override_type(part_name, content_type)); } std::string manifest::get_default_type(const std::string &extension) const @@ -30,7 +111,7 @@ std::string manifest::get_default_type(const std::string &extension) const std::string manifest::get_override_type(const std::string &part_name) const { auto match = std::find_if(override_types_.begin(), override_types_.end(), - [&](const override_type &d) { return d.get_part_name() == part_name; }); + [&](const override_type &d) { return match_path(d.get_part_name(), part_name); }); if(match == override_types_.end()) { diff --git a/source/workbook/workbook.cpp b/source/workbook/workbook.cpp index eb04670c..9dde7946 100644 --- a/source/workbook/workbook.cpp +++ b/source/workbook/workbook.cpp @@ -619,7 +619,7 @@ const number_format &workbook::get_number_format(std::size_t style_id) const for(const auto &number_format_ : d_->number_formats_) { - if(number_format_.get_id() == number_format_id) + if(static_cast(number_format_.get_id()) == number_format_id) { return number_format_; } @@ -631,15 +631,15 @@ const number_format &workbook::get_number_format(std::size_t style_id) const return d_->number_formats_.back(); } -const font &workbook::get_font(std::size_t style_id) const +const font &workbook::get_font(std::size_t font_id) const { - return d_->fonts_[d_->styles_[style_id].font_id_]; + return d_->fonts_[font_id]; } std::size_t workbook::set_font(const font &font_, std::size_t style_id) { auto match = std::find(d_->fonts_.begin(), d_->fonts_.end(), font_); - auto font_index = 0; + std::size_t font_index = 0; if(match == d_->fonts_.end()) { @@ -674,22 +674,22 @@ std::size_t workbook::set_font(const font &font_, std::size_t style_id) return d_->styles_.size() - 1; } -const fill &workbook::get_fill(std::size_t style_id) const +const fill &workbook::get_fill(std::size_t fill_id) const { - return d_->fills_[d_->styles_[style_id].fill_id_]; + return d_->fills_[fill_id]; } -std::size_t workbook::set_fill(const fill &fill_, std::size_t style_id) +std::size_t workbook::set_fill(const fill &/*fill_*/, std::size_t style_id) { return style_id; } -const border &workbook::get_border(std::size_t style_id) const +const border &workbook::get_border(std::size_t border_id) const { - return d_->borders_[d_->styles_[style_id].border_id_]; + return d_->borders_[border_id]; } -std::size_t workbook::set_border(const border &border_, std::size_t style_id) +std::size_t workbook::set_border(const border &/*border_*/, std::size_t style_id) { return style_id; } @@ -699,7 +699,7 @@ const alignment &workbook::get_alignment(std::size_t style_id) const return d_->styles_[style_id].alignment_; } -std::size_t workbook::set_alignment(const alignment &alignment_, std::size_t style_id) +std::size_t workbook::set_alignment(const alignment &/*alignment_*/, std::size_t style_id) { return style_id; } @@ -709,7 +709,7 @@ const protection &workbook::get_protection(std::size_t style_id) const return d_->styles_[style_id].protection_; } -std::size_t workbook::set_protection(const protection &protection_, std::size_t style_id) +std::size_t workbook::set_protection(const protection &/*protection_*/, std::size_t style_id) { return style_id; } @@ -861,12 +861,27 @@ const std::vector &workbook::get_root_relationships() const { if(d_->root_relationships_.empty()) { - d_->root_relationships_.push_back(relationship(relationship::type::extended_properties, "rId3", "docProps/app.xml")); - d_->root_relationships_.push_back(relationship(relationship::type::core_properties, "rId2", "docProps/core.xml")); - d_->root_relationships_.push_back(relationship(relationship::type::office_document, "rId1", "xl/workbook.xml")); + d_->root_relationships_.push_back(relationship(relationship::type::core_properties, "rId1", "docProps/core.xml")); + d_->root_relationships_.push_back(relationship(relationship::type::extended_properties, "rId2", "docProps/app.xml")); + d_->root_relationships_.push_back(relationship(relationship::type::office_document, "rId3", "xl/workbook.xml")); } return d_->root_relationships_; } +std::vector &workbook::get_shared_strings() +{ + return d_->shared_strings_; +} + +const std::vector &workbook::get_shared_strings() const +{ + return d_->shared_strings_; +} + +void workbook::add_shared_string(const std::string &shared) +{ + d_->shared_strings_.push_back(shared); +} + } // namespace xlnt diff --git a/source/worksheet/range_reference.cpp b/source/worksheet/range_reference.cpp index 4059c831..61010ea2 100644 --- a/source/worksheet/range_reference.cpp +++ b/source/worksheet/range_reference.cpp @@ -83,4 +83,10 @@ bool range_reference::operator==(const range_reference &comparand) const && comparand.bottom_right_ == bottom_right_; } +bool range_reference::operator!=(const range_reference &comparand) const +{ + return comparand.top_left_ != top_left_ + || comparand.bottom_right_ != bottom_right_; +} + } diff --git a/source/worksheet/worksheet.cpp b/source/worksheet/worksheet.cpp index 27a7ee05..c22deef8 100644 --- a/source/worksheet/worksheet.cpp +++ b/source/worksheet/worksheet.cpp @@ -73,6 +73,11 @@ std::vector worksheet::get_merged_ranges() const return d_->merged_cells_; } +margins &worksheet::get_page_margins() +{ + return d_->page_margins_; +} + const margins &worksheet::get_page_margins() const { return d_->page_margins_; @@ -103,6 +108,11 @@ void worksheet::unset_auto_filter() d_->auto_filter_ = range_reference(1, 1, 1, 1); } +page_setup &worksheet::get_page_setup() +{ + return d_->page_setup_; +} + const page_setup &worksheet::get_page_setup() const { return d_->page_setup_; diff --git a/source/worksheet/worksheet_reader.cpp b/source/worksheet/worksheet_reader.cpp deleted file mode 100644 index 22ee4cde..00000000 --- a/source/worksheet/worksheet_reader.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include "detail/include_pugixml.hpp" - -namespace { - -} // namespace - -namespace xlnt { -} // namespace xlnt diff --git a/tests/helpers/helper.hpp b/tests/helpers/helper.hpp index a03d72c2..cab0c3ff 100644 --- a/tests/helpers/helper.hpp +++ b/tests/helpers/helper.hpp @@ -31,15 +31,27 @@ public: operator bool() const { return difference == difference_type::equivalent; } }; + static comparison_result compare_xml(const xlnt::xml_document &expected, const xlnt::xml_document &observed) + { + return compare_xml(expected.get_root(), observed.get_root()); + } + static comparison_result compare_xml(const std::string &expected, const xlnt::xml_document &observed) { - std::ifstream f(expected); - std::ostringstream s; - f >> s.rdbuf(); + std::string expected_contents = expected; - auto expected_xml = xlnt::xml_serializer::deserialize(s.str()); + if(PathHelper::FileExists(expected)) + { + std::ifstream f(expected); + std::ostringstream s; + f >> s.rdbuf(); + + expected_contents = s.str(); + } - return compare_xml(expected_xml.root(), observed.root()); + auto expected_xml = xlnt::xml_serializer::deserialize(expected_contents); + + return compare_xml(expected_xml.get_root(), observed.get_root()); } static comparison_result compare_xml(const std::string &left_contents, const std::string &right_contents) @@ -47,7 +59,7 @@ public: auto left_doc = xlnt::xml_serializer::deserialize(left_contents); auto right_doc = xlnt::xml_serializer::deserialize(right_contents); - return compare_xml(left_doc.root(), right_doc.root()); + return compare_xml(left_doc.get_root(), right_doc.get_root()); } static comparison_result compare_xml(const xlnt::xml_node &left, const xlnt::xml_node &right) @@ -100,13 +112,14 @@ public: return {difference_type::text_values_differ, "((empty))", right_temp}; } - auto right_child_iter = right.get_children().begin(); + auto right_children = right.get_children(); + auto right_child_iter = right_children.begin(); for(auto left_child : left.get_children()) { left_temp = left_child.get_name(); - if(right_child_iter == right.get_children().end()) + if(right_child_iter == right_children.end()) { return {difference_type::child_order_differs, left_temp, "((end))"}; } @@ -122,7 +135,7 @@ public: } } - if(right_child_iter != right.get_children().end()) + if(right_child_iter != right_children.end()) { right_temp = right_child_iter->get_name(); return {difference_type::child_order_differs, "((end))", right_temp}; diff --git a/tests/test_props.hpp b/tests/test_props.hpp index b8754c2f..8bb69d85 100644 --- a/tests/test_props.hpp +++ b/tests/test_props.hpp @@ -83,6 +83,8 @@ public: prop.last_modified_by = "SOMEBODY"; prop.created = xlnt::datetime(2010, 4, 1, 20, 30, 00); prop.modified = xlnt::datetime(2010, 4, 5, 14, 5, 30); + TS_FAIL(""); + return; xlnt::workbook_serializer serializer(wb); xlnt::xml_document xml = serializer.write_properties_core(); TS_ASSERT(Helper::compare_xml(PathHelper::GetDataDirectory() + "/writer/expected/core.xml", xml)); @@ -95,6 +97,8 @@ public: wb.create_sheet(); xlnt::workbook_serializer serializer(wb); xlnt::xml_document xml = serializer.write_properties_app(); + TS_FAIL(""); + return; TS_ASSERT(Helper::compare_xml(PathHelper::GetDataDirectory() + "/writer/expected/app.xml", xml)); } }; diff --git a/tests/test_read.hpp b/tests/test_read.hpp index 7ffbbec6..c8db121e 100644 --- a/tests/test_read.hpp +++ b/tests/test_read.hpp @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -331,11 +332,15 @@ public: { auto path = PathHelper::GetDataDirectory("/reader/bug137.xlsx"); xlnt::zip_file archive(path); + std::vector> expected = { {"xl/worksheets/sheet1.xml", "Sheet1"} }; - TS_ASSERT_EQUALS(xlnt::detect_worksheets(archive), expected); + + xlnt::workbook wb; + xlnt::workbook_serializer serializer(wb); + TS_ASSERT_EQUALS(serializer.detect_worksheets(), expected); } { @@ -348,7 +353,9 @@ public: {"xl/worksheets/sheet2.xml", "moredata"} }; - TS_ASSERT_EQUALS(xlnt::detect_worksheets(archive), expected); + xlnt::workbook wb; + xlnt::workbook_serializer serializer(wb); + TS_ASSERT_EQUALS(serializer.detect_worksheets(), expected); } { @@ -362,7 +369,9 @@ public: {"xl/worksheets/sheet.xml", "Sheet3"} }; - TS_ASSERT_EQUALS(xlnt::detect_worksheets(archive), expected); + xlnt::workbook wb; + xlnt::workbook_serializer serializer(wb); + TS_ASSERT_EQUALS(serializer.detect_worksheets(), expected); } } @@ -381,7 +390,7 @@ public: auto path = PathHelper::GetDataDirectory("/reader/bug137.xlsx"); xlnt::zip_file archive(path); - TS_ASSERT_EQUALS(xlnt::read_relationships(archive, "xl/workbook.xml"), expected); + TS_ASSERT_EQUALS(xlnt::relationship_serializer::read_relationships(archive, "xl/workbook.xml"), expected); } { @@ -400,7 +409,7 @@ public: auto path = PathHelper::GetDataDirectory("/reader/bug304.xlsx"); xlnt::zip_file archive(path); - TS_ASSERT_EQUALS(xlnt::read_relationships(archive, "xl/workbook.xml"), expected); + TS_ASSERT_EQUALS(xlnt::relationship_serializer::read_relationships(archive, "xl/workbook.xml"), expected); } } diff --git a/tests/test_style_writer.hpp b/tests/test_style_writer.hpp index c6e357b1..6e2bd0d4 100644 --- a/tests/test_style_writer.hpp +++ b/tests/test_style_writer.hpp @@ -13,14 +13,15 @@ public: xlnt::workbook wb; wb.add_number_format(xlnt::number_format("YYYY")); xlnt::style_serializer writer(wb); - auto xml = writer.write_number_formats(); + xlnt::xml_document observed; + writer.write_number_formats(observed.add_child("numFmts")); std::string expected = "" " " " " " " ""; - auto diff = Helper::compare_xml(xml, expected); + auto diff = Helper::compare_xml(expected, observed); TS_ASSERT(diff); } /* diff --git a/tests/test_theme.hpp b/tests/test_theme.hpp index 5d24791a..80143355 100644 --- a/tests/test_theme.hpp +++ b/tests/test_theme.hpp @@ -14,9 +14,9 @@ class test_theme : public CxxTest::TestSuite public: void test_write_theme() { - xlnt::theme_serializer serializer; xlnt::workbook wb; - auto content = serializer.write_theme(wb.get_loaded_theme()); - TS_ASSERT(Helper::compare_xml(PathHelper::GetDataDirectory() + "/writer/expected/theme1.xml", content)); + xlnt::theme_serializer serializer; + auto xml = serializer.write_theme(wb.get_loaded_theme()); + TS_ASSERT(Helper::compare_xml(PathHelper::GetDataDirectory() + "/writer/expected/theme1.xml", xml)); } }; diff --git a/tests/test_worksheet.hpp b/tests/test_worksheet.hpp index 9a35fc6e..27d6e443 100644 --- a/tests/test_worksheet.hpp +++ b/tests/test_worksheet.hpp @@ -3,6 +3,7 @@ #include #include +#include #include class test_worksheet : public CxxTest::TestSuite @@ -639,10 +640,8 @@ public: { xlnt::worksheet ws(wb_); - auto xml_string = xlnt::write_worksheet(ws, {}); - - pugi::xml_document doc; - doc.load(xml_string.c_str()); + xlnt::worksheet_serializer serializer(ws); + auto observed = serializer.write_worksheet(); auto expected_string = "" @@ -660,10 +659,10 @@ public: " " ""; - pugi::xml_document expected_doc; - expected_doc.load(expected_string); + xlnt::xml_document expected; + expected.from_string(expected_string); - TS_ASSERT(Helper::compare_xml(expected_doc, doc)); + TS_ASSERT(Helper::compare_xml(expected, observed)); } void test_page_margins() @@ -677,10 +676,8 @@ public: ws.get_page_margins().set_header(1.5); ws.get_page_margins().set_footer(1.5); - auto xml_string = xlnt::write_worksheet(ws, {}); - - pugi::xml_document doc; - doc.load(xml_string.c_str()); + xlnt::worksheet_serializer serializer(ws); + auto observed = serializer.write_worksheet(); auto expected_string = "" @@ -698,10 +695,10 @@ public: " " ""; - pugi::xml_document expected_doc; - expected_doc.load(expected_string); - - TS_ASSERT(Helper::compare_xml(expected_doc, doc)); + xlnt::xml_document expected; + expected.from_string(expected_string); + + TS_ASSERT(Helper::compare_xml(expected, observed)); } void test_merge() @@ -737,18 +734,17 @@ public: ws.get_cell("A1").set_value("Cell A1"); ws.get_cell("B1").set_value("Cell B1"); - auto xml_string = xlnt::write_worksheet(ws, string_table); + { + xlnt::worksheet_serializer serializer(ws); + auto observed = serializer.write_worksheet(); - pugi::xml_document doc; - doc.load(xml_string.c_str()); - - pugi::xml_document expected_doc; - expected_doc.load(expected_string1); - - TS_ASSERT(Helper::compare_xml(expected_doc, doc)); + xlnt::xml_document expected; + expected.from_string(expected_string1); + + TS_ASSERT(Helper::compare_xml(expected, observed)); + } ws.merge_cells("A1:B1"); - xml_string = xlnt::write_worksheet(ws, string_table); auto expected_string2 = "" @@ -776,13 +772,17 @@ public: " " ""; - doc.load(xml_string.c_str()); - expected_doc.load(expected_string2); - - TS_ASSERT(Helper::compare_xml(expected_doc, doc)); + { + xlnt::worksheet_serializer serializer(ws); + auto observed = serializer.write_worksheet(); + + xlnt::xml_document expected; + expected.from_string(expected_string2); + + TS_ASSERT(Helper::compare_xml(expected, observed)); + } ws.unmerge_cells("A1:B1"); - xml_string = xlnt::write_worksheet(ws, string_table); auto expected_string3 = "" @@ -807,10 +807,15 @@ public: " " ""; - doc.load(xml_string.c_str()); - expected_doc.load(expected_string3); - - TS_ASSERT(Helper::compare_xml(expected_doc, doc)); + { + xlnt::worksheet_serializer serializer(ws); + auto observed = serializer.write_worksheet(); + + xlnt::xml_document expected; + expected.from_string(expected_string3); + + TS_ASSERT(Helper::compare_xml(expected, observed)); + } } void test_printer_settings() @@ -825,11 +830,9 @@ public: ws.get_page_setup().set_horizontal_centered(true); ws.get_page_setup().set_vertical_centered(true); - auto xml_string = xlnt::write_worksheet(ws, {}); + xlnt::worksheet_serializer serializer(ws); + auto observed = serializer.write_worksheet(); - pugi::xml_document doc; - doc.load(xml_string.c_str()); - auto expected_string = "" "" @@ -850,10 +853,10 @@ public: " " ""; - pugi::xml_document expected_doc; - expected_doc.load(expected_string); + xlnt::xml_document expected; + expected.from_string(expected_string); - TS_ASSERT(Helper::compare_xml(expected_doc, doc)); + TS_ASSERT(Helper::compare_xml(expected, observed)); } void test_header_footer() @@ -901,13 +904,15 @@ public: " " ""; - pugi::xml_document expected_doc; - pugi::xml_document observed_doc; + xlnt::xml_document expected; + expected.from_string(expected_xml_string); - expected_doc.load(expected_xml_string.c_str()); - observed_doc.load(xlnt::write_worksheet(ws, {}).c_str()); - - TS_ASSERT(Helper::compare_xml(expected_doc, observed_doc)); + { + xlnt::worksheet_serializer serializer(ws); + auto observed = serializer.write_worksheet(); + + TS_ASSERT(Helper::compare_xml(expected, observed)); + } ws = wb_.create_sheet(); @@ -927,10 +932,12 @@ public: " " ""; - expected_doc.load(expected_xml_string.c_str()); - observed_doc.load(xlnt::write_worksheet(ws, {}).c_str()); - - TS_ASSERT(Helper::compare_xml(expected_doc, observed_doc)); + { + xlnt::worksheet_serializer serializer(ws); + auto observed = serializer.write_worksheet(); + + TS_ASSERT(Helper::compare_xml(expected, observed)); + } } void test_page_setup() diff --git a/tests/test_write.hpp b/tests/test_write.hpp index b0e0de34..66b6ecec 100644 --- a/tests/test_write.hpp +++ b/tests/test_write.hpp @@ -3,7 +3,12 @@ #include #include -#include +#include +#include +#include +#include +#include + #include "helpers/temporary_file.hpp" #include "helpers/path_helper.hpp" #include "helpers/helper.hpp" @@ -41,32 +46,45 @@ public: void test_write_workbook_rels() { xlnt::workbook wb; - auto content = xlnt::write_workbook_rels(wb); + xlnt::zip_file archive; + xlnt::relationship_serializer::write_relationships(wb.get_relationships(), "xl/workbook.xml", archive); + auto content = xlnt::xml_serializer::deserialize(archive.read("xl/_rels/workbook.xml.rels")); - TS_ASSERT(Helper::EqualsFileContent(PathHelper::GetDataDirectory() + "/writer/expected/workbook.xml.rels", content)); + TS_ASSERT(Helper::compare_xml(PathHelper::GetDataDirectory() + "/writer/expected/workbook.xml.rels", content)); } void test_write_workbook() { xlnt::workbook wb; - auto content = xlnt::write_workbook(wb); + xlnt::workbook_serializer serializer(wb); + auto content = serializer.write_workbook(); - TS_ASSERT(Helper::EqualsFileContent(PathHelper::GetDataDirectory() + "/writer/expected/workbook.xml", content)); + TS_ASSERT(Helper::compare_xml(PathHelper::GetDataDirectory() + "/writer/expected/workbook.xml", content)); } void test_write_string_table() { - std::vector table = {"hello", "world", "nice"}; - auto content = xlnt::write_shared_strings(table); - TS_ASSERT(Helper::EqualsFileContent(PathHelper::GetDataDirectory() + "/writer/expected/sharedStrings.xml", content)); + 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"); + + auto content = xlnt::shared_strings_serializer::write_shared_strings(wb.get_shared_strings()); + + TS_ASSERT(Helper::compare_xml(PathHelper::GetDataDirectory() + "/writer/expected/sharedStrings.xml", content)); } void test_write_worksheet() { auto ws = wb_.create_sheet(); ws.get_cell("F42").set_value("hello"); - auto content = xlnt::write_worksheet(ws, {"hello"}); - TS_ASSERT(Helper::EqualsFileContent(PathHelper::GetDataDirectory() + "/writer/expected/sheet1.xml", content)); + + xlnt::worksheet_serializer serializer(ws); + auto observed = serializer.write_worksheet(); + + TS_ASSERT(Helper::compare_xml(PathHelper::GetDataDirectory() + "/writer/expected/sheet1.xml", observed)); } void test_write_hidden_worksheet() @@ -74,8 +92,11 @@ public: auto ws = wb_.create_sheet(); ws.get_page_setup().set_sheet_state(xlnt::page_setup::sheet_state::hidden); ws.get_cell("F42").set_value("hello"); - auto content = xlnt::write_worksheet(ws, {"hello"}); - TS_ASSERT(Helper::EqualsFileContent(PathHelper::GetDataDirectory() + "/writer/expected/sheet1.xml", content)); + + xlnt::worksheet_serializer serializer(ws); + auto observed = serializer.write_worksheet(); + + TS_ASSERT(Helper::compare_xml(PathHelper::GetDataDirectory() + "/writer/expected/sheet1.xml", observed)); } void test_write_bool() @@ -83,8 +104,11 @@ public: auto ws = wb_.create_sheet(); ws.get_cell("F42").set_value(false); ws.get_cell("F43").set_value(true); - auto content = xlnt::write_worksheet(ws, {}); - TS_ASSERT(Helper::EqualsFileContent(PathHelper::GetDataDirectory() + "/writer/expected/sheet1_bool.xml", content)); + + xlnt::worksheet_serializer serializer(ws); + auto observed = serializer.write_worksheet(); + + TS_ASSERT(Helper::compare_xml(PathHelper::GetDataDirectory() + "/writer/expected/sheet1_bool.xml", observed)); } void test_write_formula() @@ -93,8 +117,11 @@ public: ws.get_cell("F1").set_value(10); ws.get_cell("F2").set_value(32); ws.get_cell("F3").set_formula("F1+F2"); - auto content = xlnt::write_worksheet(ws, {}); - TS_ASSERT(Helper::EqualsFileContent(PathHelper::GetDataDirectory() + "/writer/expected/sheet1_formula.xml", content)); + + xlnt::worksheet_serializer serializer(ws); + auto observed = serializer.write_worksheet(); + + TS_ASSERT(Helper::compare_xml(PathHelper::GetDataDirectory() + "/writer/expected/sheet1_formula.xml", observed)); } void test_write_height() @@ -102,8 +129,11 @@ public: auto ws = wb_.create_sheet(); ws.get_cell("F1").set_value(10); ws.get_row_properties(ws.get_cell("F1").get_row()).height = 30; - auto content = xlnt::write_worksheet(ws, {}); - TS_ASSERT(Helper::EqualsFileContent(PathHelper::GetDataDirectory() + "/writer/expected/sheet1_height.xml", content)); + + xlnt::worksheet_serializer serializer(ws); + auto observed = serializer.write_worksheet(); + + TS_ASSERT(Helper::compare_xml(PathHelper::GetDataDirectory() + "/writer/expected/sheet1_height.xml", observed)); } void test_write_hyperlink() @@ -111,8 +141,11 @@ public: auto ws = wb_.create_sheet(); ws.get_cell("A1").set_value("test"); ws.get_cell("A1").set_hyperlink("http://test.com"); - auto content = xlnt::write_worksheet(ws, {"test"}); - TS_ASSERT(Helper::EqualsFileContent(PathHelper::GetDataDirectory() + "/writer/expected/sheet1_hyperlink.xml", content)); + + xlnt::worksheet_serializer serializer(ws); + auto observed = serializer.write_worksheet(); + + TS_ASSERT(Helper::compare_xml(PathHelper::GetDataDirectory() + "/writer/expected/sheet1_hyperlink.xml", observed)); } void test_write_hyperlink_rels() @@ -125,8 +158,12 @@ public: ws.get_cell("A2").set_value("test"); ws.get_cell("A2").set_hyperlink("http://test2.com/"); TS_ASSERT_EQUALS(2, ws.get_relationships().size()); - auto content = xlnt::write_worksheet_rels(ws); - TS_ASSERT(Helper::EqualsFileContent(PathHelper::GetDataDirectory() + "/writer/expected/sheet1_hyperlink.xml.rels", content)); + + xlnt::zip_file archive; + xlnt::relationship_serializer::write_relationships(ws.get_relationships(), "xl/worksheets/sheet1.xml", archive); + auto content = xlnt::xml_serializer::deserialize(archive.read("xl/worksheets/_rels/sheet1.xml.rels")); + + TS_ASSERT(Helper::compare_xml(PathHelper::GetDataDirectory() + "/writer/expected/sheet1_hyperlink.xml.rels", content)); } void _test_write_hyperlink_image_rels() @@ -149,11 +186,16 @@ public: auto ws = wb.get_sheet_by_index(0); ws.get_cell("F42").set_value("hello"); ws.auto_filter("A1:F1"); - auto content = xlnt::write_worksheet(ws, {"hello"}); - TS_ASSERT(Helper::EqualsFileContent(PathHelper::GetDataDirectory() + "/writer/expected/sheet1_auto_filter.xml", content)); - content = xlnt::write_workbook(wb); - TS_ASSERT(Helper::EqualsFileContent(PathHelper::GetDataDirectory() + "/writer/expected/workbook_auto_filter.xml", content)); + xlnt::worksheet_serializer ws_serializer(ws); + auto observed = ws_serializer.write_worksheet(); + + TS_ASSERT(Helper::compare_xml(PathHelper::GetDataDirectory() + "/writer/expected/sheet1_auto_filter.xml", observed)); + + xlnt::workbook_serializer wb_serializer(wb); + auto observed2 = wb_serializer.write_workbook(); + + TS_ASSERT(Helper::compare_xml(PathHelper::GetDataDirectory() + "/writer/expected/workbook_auto_filter.xml", observed2)); } void test_write_auto_filter_filter_column() @@ -171,8 +213,11 @@ public: auto ws = wb_.create_sheet(); ws.get_cell("F42").set_value("hello"); ws.freeze_panes("A4"); - auto content = xlnt::write_worksheet(ws, {"hello"}); - TS_ASSERT(Helper::EqualsFileContent(PathHelper::GetDataDirectory() + "/writer/expected/sheet1_freeze_panes_horiz.xml", content)); + + xlnt::worksheet_serializer serializer(ws); + auto observed = serializer.write_worksheet(); + + TS_ASSERT(Helper::compare_xml(PathHelper::GetDataDirectory() + "/writer/expected/sheet1_freeze_panes_horiz.xml", observed)); } void test_freeze_panes_vert() @@ -180,8 +225,11 @@ public: auto ws = wb_.create_sheet(); ws.get_cell("F42").set_value("hello"); ws.freeze_panes("D1"); - auto content = xlnt::write_worksheet(ws, {"hello"}); - TS_ASSERT(Helper::EqualsFileContent(PathHelper::GetDataDirectory() + "/writer/expected/sheet1_freeze_panes_vert.xml", content)); + + xlnt::worksheet_serializer serializer(ws); + auto observed = serializer.write_worksheet(); + + TS_ASSERT(Helper::compare_xml(PathHelper::GetDataDirectory() + "/writer/expected/sheet1_freeze_panes_vert.xml", observed)); } void test_freeze_panes_both() @@ -189,24 +237,33 @@ public: auto ws = wb_.create_sheet(); ws.get_cell("F42").set_value("hello"); ws.freeze_panes("D4"); - auto content = xlnt::write_worksheet(ws, {"hello"}); - TS_ASSERT(Helper::EqualsFileContent(PathHelper::GetDataDirectory() + "/writer/expected/sheet1_freeze_panes_both.xml", content)); + + xlnt::worksheet_serializer serializer(ws); + auto observed = serializer.write_worksheet(); + + TS_ASSERT(Helper::compare_xml(PathHelper::GetDataDirectory() + "/writer/expected/sheet1_freeze_panes_both.xml", observed)); } void test_long_number() { auto ws = wb_.create_sheet(); ws.get_cell("A1").set_value(9781231231230LL); - auto content = xlnt::write_worksheet(ws, {}); - TS_ASSERT(Helper::EqualsFileContent(PathHelper::GetDataDirectory() + "/writer/expected/long_number.xml", content)); + + xlnt::worksheet_serializer serializer(ws); + auto observed = serializer.write_worksheet(); + + TS_ASSERT(Helper::compare_xml(PathHelper::GetDataDirectory() + "/writer/expected/long_number.xml", observed)); } void test_short_number() { auto ws = wb_.create_sheet(); ws.get_cell("A1").set_value(1234567890); - auto content = xlnt::write_worksheet(ws, {}); - TS_ASSERT(Helper::EqualsFileContent(PathHelper::GetDataDirectory() + "/writer/expected/short_number.xml", content)); + + xlnt::worksheet_serializer serializer(ws); + auto observed = serializer.write_worksheet(); + + TS_ASSERT(Helper::compare_xml(PathHelper::GetDataDirectory() + "/writer/expected/short_number.xml", observed)); } void _test_write_images() diff --git a/tests/test_write_workbook.hpp b/tests/test_write_workbook.hpp index bda0b867..632ca01d 100644 --- a/tests/test_write_workbook.hpp +++ b/tests/test_write_workbook.hpp @@ -4,8 +4,7 @@ #include #include -#include -#include +#include #include "helpers/path_helper.hpp" @@ -19,8 +18,10 @@ public: ws.get_cell("F42").set_value("hello"); ws.get_auto_filter() = "A1:F1"; - auto content = xlnt::write_workbook(wb); - auto diff = Helper::compare_xml(PathHelper::read_file("workbook_auto_filter.xml"), content); + xlnt::workbook_serializer serializer(wb); + auto observed = serializer.write_workbook(); + + auto diff = Helper::compare_xml(PathHelper::read_file("workbook_auto_filter.xml"), observed); TS_ASSERT(!diff); } @@ -30,8 +31,11 @@ public: auto ws = wb.create_sheet(); ws.set_sheet_state(xlnt::page_setup::sheet_state::hidden); wb.create_sheet(); - auto xml = xlnt::write_workbook(wb); - std::string expected = + + xlnt::workbook_serializer serializer(wb); + auto observed = serializer.write_workbook(); + + std::string expected_string = "" " " " " @@ -44,7 +48,11 @@ public: " " " " ""; - auto diff = Helper::compare_xml(xml, expected); + + xlnt::xml_document expected; + expected.from_string(expected_string); + + auto diff = Helper::compare_xml(expected, observed); TS_ASSERT(!diff); } @@ -53,40 +61,56 @@ public: xlnt::workbook wb; auto ws = wb.get_active_sheet(); ws.set_sheet_state(xlnt::page_setup::sheet_state::hidden); - TS_ASSERT_THROWS(xlnt::write_workbook(wb), xlnt::value_error); + + xlnt::workbook_serializer serializer(wb); + + TS_ASSERT_THROWS(serializer.write_workbook(), xlnt::value_error); } void test_write_empty_workbook() { xlnt::workbook wb; TemporaryFile file; - xlnt::save_workbook(wb, file.GetFilename()); + + xlnt::excel_serializer serializer(wb); + serializer.save_workbook(file.GetFilename()); + TS_ASSERT(PathHelper::FileExists(file.GetFilename())); } void test_write_virtual_workbook() { - xlnt::workbook old_wb; - auto saved_wb = xlnt::save_virtual_workbook(old_wb); - auto new_wb = xlnt::excel_reader::load_workbook(saved_wb); + xlnt::workbook old_wb, new_wb; + + xlnt::excel_serializer serializer(old_wb); + std::vector wb_bytes; + serializer.save_virtual_workbook(wb_bytes); + + xlnt::excel_serializer deserializer(new_wb); + deserializer.load_virtual_workbook(wb_bytes); + TS_ASSERT(new_wb != nullptr); } void test_write_workbook_rels() { xlnt::workbook wb; - auto content = xlnt::write_workbook_rels(wb); + xlnt::zip_file archive; + xlnt::relationship_serializer::write_relationships(wb.get_relationships(), "xl/workbook.xml", archive); + xlnt::xml_document observed; + observed.from_string(archive.read("xl/workbook/_rels/workbook.xml.rels")); auto filename = "workbook.xml.rels"; - auto diff = Helper::compare_xml(PathHelper::read_file(filename), content); + auto diff = Helper::compare_xml(PathHelper::read_file(filename), observed); TS_ASSERT(!diff); } void test_write_workbook_() { xlnt::workbook wb; - auto content = xlnt::write_workbook(wb); + xlnt::workbook_serializer serializer(wb); + auto observed = serializer.write_workbook(); auto filename = PathHelper::GetDataDirectory("/workbook.xml"); - auto diff = Helper::compare_xml(PathHelper::read_file(filename), content); + auto diff = Helper::compare_xml(PathHelper::read_file(filename), observed); TS_ASSERT(!diff); } @@ -95,12 +119,15 @@ public: xlnt::workbook wb; auto ws = wb.create_sheet(); wb.create_named_range("test_range", ws, "A1:B5"); - auto xml = xlnt::write_defined_names(wb); + xlnt::workbook_serializer serializer(wb); + auto observed_node = serializer.write_named_ranges(); + xlnt::xml_document observed; + observed.add_child(observed_node); std::string expected = "" "'Sheet'!$A$1:$B$5" ""; - auto diff = Helper::compare_xml(xml, expected); + auto diff = Helper::compare_xml(expected, observed); TS_ASSERT(!diff); } @@ -115,7 +142,9 @@ public: xlnt::workbook wb; wb.set_code_name("MyWB"); - auto content = xlnt::write_workbook(wb); + xlnt::workbook_serializer serializer(wb); + auto observed = serializer.write_workbook(); + std::string expected = "" " " @@ -128,21 +157,26 @@ public: " " " " ""; - auto diff = Helper::compare_xml(content, expected); + auto diff = Helper::compare_xml(expected, observed); TS_ASSERT(!diff); } void test_write_root_rels() { xlnt::workbook wb; - auto xml = xlnt::write_root_rels(wb); + xlnt::zip_file archive; + xlnt::relationship_serializer::write_relationships(wb.get_root_relationships(), "", archive); + xlnt::xml_document observed; + observed.from_string(archive.read("_rels/.rels")); + std::string expected = "" " " " " " " ""; - auto diff = Helper::compare_xml(xml, expected); + + auto diff = Helper::compare_xml(expected, observed); TS_ASSERT(!diff); }