diff --git a/build/genie/genie.lua b/build/genie/genie.lua index 49c69b09..025b4b48 100644 --- a/build/genie/genie.lua +++ b/build/genie/genie.lua @@ -21,7 +21,7 @@ project "xlnt.test" "../../tests/runner-autogen.cpp" } links { "xlnt", "miniz" } - prebuildcommands { "../generate-tests.sh" } + prebuildcommands { "../../generate-tests.sh" } flags { "Unicode" } configuration "windows" defines { "WIN32" } diff --git a/include/xlnt/s11n/excel_serializer.hpp b/include/xlnt/s11n/excel_serializer.hpp index 3eb4628f..d52ba22d 100644 --- a/include/xlnt/s11n/excel_serializer.hpp +++ b/include/xlnt/s11n/excel_serializer.hpp @@ -37,7 +37,7 @@ class workbook; class excel_serializer { public: - static std::string central_directory_signature(); + static const std::string central_directory_signature(); static std::string repair_central_directory(const std::string &original); excel_serializer(workbook &wb); @@ -46,9 +46,9 @@ public: 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); - bool save_workbook(workbook &wb, const std::string &filename, bool as_template = false); - bool save_virtual_workbook(xlnt::workbook &wb, std::vector &bytes, bool as_template = false); - bool save_stream_workbook(xlnt::workbook &wb, std::ostream &stream, bool as_template = false); + bool save_workbook(const std::string &filename, bool as_template = false); + bool save_virtual_workbook(std::vector &bytes, bool as_template = false); + bool save_stream_workbook(std::ostream &stream, bool as_template = false); private: void read_data(bool guess_types, bool data_only); diff --git a/include/xlnt/s11n/manifest_serializer.hpp b/include/xlnt/s11n/manifest_serializer.hpp index 41697c89..5babe4af 100644 --- a/include/xlnt/s11n/manifest_serializer.hpp +++ b/include/xlnt/s11n/manifest_serializer.hpp @@ -14,7 +14,7 @@ class manifest_serializer public: manifest_serializer(manifest &m); - bool read_mainfest(const xml_document &xml); + bool read_manifest(const xml_document &xml); bool write_manifest(xml_document &xml); private: diff --git a/include/xlnt/s11n/relationship_serializer.hpp b/include/xlnt/s11n/relationship_serializer.hpp index 8df03c90..2bd3996e 100644 --- a/include/xlnt/s11n/relationship_serializer.hpp +++ b/include/xlnt/s11n/relationship_serializer.hpp @@ -10,8 +10,9 @@ class xml_document; class relationship_serializer { - bool read_relationships(const xml_document &xml, const std::string &dir, std::vector &relationships); - bool write_relationships(const std::vector &relationships, const std::string &dir, xml_document &xml); +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); }; } // namespace xlnt diff --git a/include/xlnt/s11n/theme_serializer.hpp b/include/xlnt/s11n/theme_serializer.hpp index 6db65bc5..6cb9803c 100644 --- a/include/xlnt/s11n/theme_serializer.hpp +++ b/include/xlnt/s11n/theme_serializer.hpp @@ -26,11 +26,14 @@ #include namespace xlnt { + +class theme; +class xml_document; class theme_serializer { public: - theme read_theme(const xml_document &xml); + //theme read_theme(const xml_document &xml); xml_document write_theme(const theme &theme_); }; diff --git a/include/xlnt/s11n/workbook_serializer.hpp b/include/xlnt/s11n/workbook_serializer.hpp index 1400d18e..54311811 100644 --- a/include/xlnt/s11n/workbook_serializer.hpp +++ b/include/xlnt/s11n/workbook_serializer.hpp @@ -35,10 +35,14 @@ class worksheet; class workbook; class zip_file; class xml_document; +class xml_node; class workbook_serializer { public: + //TODO: does this go here? + static std::string determine_document_type(const manifest &manifest_); + workbook_serializer(workbook &wb); void read_workbook(const xml_document &xml); @@ -48,17 +52,14 @@ public: xml_document write_workbook() const; xml_document write_properties_app() const; xml_document write_properties_core() const; - + private: - //workbook_view, sheets, sheet, defined_names - std::string determine_document_type(const manifest &manifest_); - using string_pair = std::pair; std::vector read_sheets(zip_file &archive); std::vector detect_worksheets(zip_file &archive); - std::string write_defined_names(const workbook &wb); + bool write_named_ranges(xml_node &named_ranges_node); workbook &wb_; }; diff --git a/include/xlnt/s11n/worksheet_serializer.hpp b/include/xlnt/s11n/worksheet_serializer.hpp index 31eacd34..0beb0ba7 100644 --- a/include/xlnt/s11n/worksheet_serializer.hpp +++ b/include/xlnt/s11n/worksheet_serializer.hpp @@ -41,9 +41,8 @@ class worksheet_serializer public: worksheet_serializer(worksheet sheet); - bool read_worksheet(const xml_document &xml, const std::vector &shared_strings, const relationship &rel, worksheet ws); - std::string read_dimension(const xml_node &dimension_node); - bool write_worksheet(const worksheet ws, const std::vector &string_table, xml_document &xml); + 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); private: worksheet sheet_; diff --git a/include/xlnt/s11n/xml_node.hpp b/include/xlnt/s11n/xml_node.hpp index 8534b084..f35741b4 100644 --- a/include/xlnt/s11n/xml_node.hpp +++ b/include/xlnt/s11n/xml_node.hpp @@ -22,6 +22,7 @@ public: 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); diff --git a/include/xlnt/styles/font.hpp b/include/xlnt/styles/font.hpp index 7b471bbd..bf64c683 100644 --- a/include/xlnt/styles/font.hpp +++ b/include/xlnt/styles/font.hpp @@ -70,6 +70,8 @@ public: color get_color() const { return color_; } bool has_family() const { return has_family_; } + int get_family() const { return family_; } + bool has_scheme() const { return has_scheme_; } std::size_t hash() const diff --git a/include/xlnt/styles/side.hpp b/include/xlnt/styles/side.hpp index 4d4cb5ac..4939b8d8 100644 --- a/include/xlnt/styles/side.hpp +++ b/include/xlnt/styles/side.hpp @@ -50,12 +50,6 @@ enum class border_style class side { public: - enum class color_type - { - theme, - indexed - }; - side(); std::size_t hash() const diff --git a/include/xlnt/workbook/workbook.hpp b/include/xlnt/workbook/workbook.hpp index c5ea9518..6542566e 100644 --- a/include/xlnt/workbook/workbook.hpp +++ b/include/xlnt/workbook/workbook.hpp @@ -49,6 +49,7 @@ class range; class range_reference; class relationship; class style; +class theme; class worksheet; class zip_file; @@ -199,7 +200,7 @@ public: void create_relationship(const std::string &id, const std::string &target, relationship::type type); relationship get_relationship(const std::string &id) const; - std::vector get_relationships() const; + const std::vector &get_relationships() const; void add_alignment(const alignment &a); void add_border(const border &b); @@ -237,8 +238,8 @@ public: void set_code_name(const std::string &code_name); - bool has_loaded_theme(); - std::string get_loaded_theme(); + bool has_loaded_theme() const; + const theme &get_loaded_theme() const; const style &get_style(std::size_t style_id) const; std::size_t add_style(const style &style_); @@ -246,6 +247,8 @@ public: manifest &get_manifest(); const manifest &get_manifest() const; + const std::vector &get_root_relationships() const; + private: friend class worksheet; std::shared_ptr d_; diff --git a/include/xlnt/worksheet/worksheet.hpp b/include/xlnt/worksheet/worksheet.hpp index 9be5f5a5..13655035 100644 --- a/include/xlnt/worksheet/worksheet.hpp +++ b/include/xlnt/worksheet/worksheet.hpp @@ -223,7 +223,7 @@ public: // relationships relationship create_relationship(relationship::type type, const std::string &target_uri); - std::vector get_relationships(); + const std::vector &get_relationships() const; // charts //void add_chart(chart chart); @@ -262,7 +262,9 @@ public: const range operator()(const cell_reference &top_left, const cell_reference &bottom_right) const; // page + page_setup &get_page_setup(); const page_setup &get_page_setup() const; + margins &get_page_margins(); const margins &get_page_margins() const; // auto filter diff --git a/include/xlnt/xlnt.hpp b/include/xlnt/xlnt.hpp index 8343e044..39d2f4ed 100644 --- a/include/xlnt/xlnt.hpp +++ b/include/xlnt/xlnt.hpp @@ -42,11 +42,9 @@ const std::string download_url = "https://github.com/tfussell/xlnt/archive/maste #include "common/relationship.hpp" #include "common/string_table.hpp" #include "common/zip_file.hpp" -#include "reader/excel_reader.hpp" #include "workbook/document_properties.hpp" #include "workbook/named_range.hpp" #include "workbook/workbook.hpp" #include "worksheet/range.hpp" #include "worksheet/range_reference.hpp" #include "worksheet/worksheet.hpp" -#include "writer/workbook_writer.hpp" diff --git a/source/detail/workbook_impl.hpp b/source/detail/workbook_impl.hpp index 9ba12776..509dee9b 100644 --- a/source/detail/workbook_impl.hpp +++ b/source/detail/workbook_impl.hpp @@ -13,7 +13,8 @@ struct workbook_impl workbook_impl(const workbook_impl &other) : active_sheet_index_(other.active_sheet_index_), worksheets_(other.worksheets_), - relationships_(other.relationships_), + relationships_(other.relationships_), + root_relationships_(other.root_relationships_), drawings_(other.drawings_), properties_(other.properties_), guess_types_(other.guess_types_), @@ -35,6 +36,8 @@ struct workbook_impl std::copy(other.worksheets_.begin(), other.worksheets_.end(), back_inserter(worksheets_)); relationships_.clear(); std::copy(other.relationships_.begin(), other.relationships_.end(), std::back_inserter(relationships_)); + root_relationships_.clear(); + 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_)); properties_ = other.properties_; @@ -54,6 +57,7 @@ struct workbook_impl std::size_t active_sheet_index_; std::vector worksheets_; std::vector relationships_; + std::vector root_relationships_; std::vector drawings_; document_properties properties_; @@ -72,6 +76,8 @@ struct workbook_impl std::vector number_formats_; manifest manifest_; + + theme theme_; }; } // namespace detail diff --git a/source/s11n/excel_serializer.cpp b/source/s11n/excel_serializer.cpp index ada9b873..f57b99ed 100644 --- a/source/s11n/excel_serializer.cpp +++ b/source/s11n/excel_serializer.cpp @@ -1,7 +1,20 @@ #include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include +#include + +#include namespace { @@ -22,99 +35,69 @@ std::string::size_type find_string_in_string(const std::string &string, const st return possible_match_index; } -xlnt::workbook load_workbook(xlnt::zip_file &archive, bool guess_types, bool data_only) +bool load_workbook(xlnt::zip_file &archive, bool guess_types, bool data_only, xlnt::workbook &wb) { - xlnt::workbook wb; - wb.set_guess_types(guess_types); wb.set_data_only(data_only); - auto content_types = xlnt::read_content_types(archive); - auto type = xlnt::determine_document_type(content_types); + xlnt::manifest_serializer ms(wb.get_manifest()); + ms.read_manifest(xlnt::xml_serializer::deserialize(archive.read(xlnt::constants::ArcContentTypes))); - if(type != "excel") + if(xlnt::workbook_serializer::determine_document_type(wb.get_manifest()) != "excel") { throw xlnt::invalid_file_exception(""); } wb.clear(); - auto workbook_relationships = read_relationships(archive, "xl/workbook.xml"); + std::vector workbook_relationships; + xlnt::relationship_serializer::read_relationships(xlnt::xml_serializer::deserialize(archive.read("xl/_rels/workbook.xml.rels")), "", workbook_relationships); for(auto relationship : workbook_relationships) { wb.create_relationship(relationship.get_id(), relationship.get_target_uri(), relationship.get_type()); } - pugi::xml_document doc; - doc.load(archive.read("xl/workbook.xml").c_str()); + auto xml = xlnt::xml_serializer::deserialize(archive.read(xlnt::constants::ArcWorkbook)); - auto root_node = doc.child("workbook"); + auto &root_node = xml.root(); - auto workbook_pr_node = root_node.child("workbookPr"); - wb.get_properties().excel_base_date = (workbook_pr_node.attribute("date1904") != nullptr && workbook_pr_node.attribute("date1904").as_int() != 0) ? xlnt::calendar::mac_1904 : xlnt::calendar::windows_1900; + 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; - auto sheets_node = root_node.child("sheets"); + 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); - xlnt::shared_strings_reader shared_strings_reader_; - auto shared_strings = shared_strings_reader_.read_strings(archive); + xlnt::style_serializer style_reader_(wb); + style_reader_.read_stylesheet(xlnt::xml_serializer::deserialize(archive.read(xlnt::constants::ArcStyles))); - xlnt::style_reader style_reader_(wb); - style_reader_.read_styles(archive); + auto &sheets_node = root_node.get_child("sheets"); - for(const auto &border_ : style_reader_.get_borders()) + for(auto sheet_node : sheets_node.get_children()) { - wb.add_border(border_); - } - - for(const auto &fill_ : style_reader_.get_fills()) - { - wb.add_fill(fill_); - } - - for(const auto &font_ : style_reader_.get_fonts()) - { - wb.add_font(font_); - } - - for(const auto &number_format_ : style_reader_.get_number_formats()) - { - wb.add_number_format(number_format_); - } - - for(auto &color_rgb : style_reader_.get_colors()) - { - wb.add_color(xlnt::color(xlnt::color::type::rgb, color_rgb)); - } - - for(const auto &style : style_reader_.get_styles()) - { - wb.add_style(style); - } - - for(auto sheet_node : sheets_node.children("sheet")) - { - auto rel = wb.get_relationship(sheet_node.attribute("r:id").as_string()); - auto ws = wb.create_sheet(sheet_node.attribute("name").as_string(), rel); + auto rel = wb.get_relationship(sheet_node.get_attribute("r:id")); + auto ws = wb.create_sheet(sheet_node.get_attribute("name"), rel); - xlnt::read_worksheet(ws, archive, rel, shared_strings); + xlnt::worksheet_serializer worksheet_serializer(ws); + worksheet_serializer.read_worksheet(xlnt::xml_serializer::deserialize(archive.read(rel.get_target_uri())), shared_strings, rel); } - return wb; + return true; } } // namespace namespace xlnt { -std::string excel_reader::CentralDirectorySignature() +const std::string excel_serializer::central_directory_signature() { return "\x50\x4b\x05\x06"; } -std::string excel_reader::repair_central_directory(const std::string &original) +std::string excel_serializer::repair_central_directory(const std::string &original) { - auto pos = find_string_in_string(original, CentralDirectorySignature()); + auto pos = find_string_in_string(original, central_directory_signature()); if(pos != std::string::npos) { @@ -124,104 +107,76 @@ std::string excel_reader::repair_central_directory(const std::string &original) return original; } -workbook excel_reader::load_workbook(std::istream &stream, bool guess_types, bool data_only) +bool excel_serializer::load_stream_workbook(std::istream &stream, bool guess_types, bool data_only) { std::vector bytes((std::istream_iterator(stream)), std::istream_iterator()); - - return load_workbook(bytes, guess_types, data_only); + + return load_virtual_workbook(bytes, guess_types, data_only); } -workbook excel_reader::load_workbook(const std::string &filename, bool guess_types, bool data_only) +bool excel_serializer::load_workbook(const std::string &filename, bool guess_types, bool data_only) { - xlnt::zip_file archive; - try { - archive.load(filename); + archive_.load(filename); } catch(std::runtime_error) { throw invalid_file_exception(filename); } - return ::load_workbook(archive, guess_types, data_only); + return ::load_workbook(archive_, guess_types, data_only, wb_); } -xlnt::workbook excel_reader::load_workbook(const std::vector &bytes, bool guess_types, bool data_only) +bool excel_serializer::load_virtual_workbook(const std::vector &bytes, bool guess_types, bool data_only) { - xlnt::zip_file archive; - archive.load(bytes); + archive_.load(bytes); - return ::load_workbook(archive, guess_types, data_only); + return ::load_workbook(archive_, guess_types, data_only, wb_); } -excel_writer::excel_writer(workbook &wb) : wb_(wb) +excel_serializer::excel_serializer(workbook &wb) : wb_(wb) { } -void excel_writer::save(const std::string &filename, bool as_template) +void excel_serializer::write_data(bool as_template) { - zip_file archive; - write_data(archive, as_template); - archive.save(filename); + 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)); + + xml_document properties_app_xml; + workbook_serializer workbook_serializer_(wb_); + 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::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)); + + 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)); + + write_worksheets(); } -void excel_writer::write_data(zip_file &archive, bool as_template) -{ - archive.writestr(constants::ArcRootRels, write_root_rels(wb_)); - archive.writestr(constants::ArcWorkbookRels, write_workbook_rels(wb_)); - archive.writestr(constants::ArcApp, write_properties_app(wb_)); - archive.writestr(constants::ArcCore, write_properties_core(wb_.get_properties())); - - if(wb_.has_loaded_theme()) - { - archive.writestr(constants::ArcTheme, wb_.get_loaded_theme()); - } - else - { - archive.writestr(constants::ArcTheme, write_theme()); - } - - archive.writestr(constants::ArcWorkbook, write_workbook(wb_)); - - auto shared_strings = extract_all_strings(wb_); - - write_charts(archive); - write_images(archive); - write_shared_strings(archive, shared_strings); - write_worksheets(archive, shared_strings); - write_chartsheets(archive); - write_external_links(archive); - - style_writer style_writer_(wb_); - archive.writestr(constants::ArcStyles, style_writer_.write_table()); - - auto manifest = write_content_types(wb_, as_template); - archive.writestr(constants::ArcContentTypes, manifest); -} - -void excel_writer::write_shared_strings(xlnt::zip_file &archive, const std::vector &shared_strings) -{ - archive.writestr(constants::ArcSharedString, ::xlnt::write_shared_strings(shared_strings)); -} - -void excel_writer::write_images(zip_file &/*archive*/) -{ - -} - -void excel_writer::write_charts(zip_file &/*archive*/) -{ - -} - -void excel_writer::write_chartsheets(zip_file &/*archive*/) -{ - -} - -void excel_writer::write_worksheets(zip_file &archive, const std::vector &shared_strings) +void excel_serializer::write_worksheets() { std::size_t index = 0; @@ -232,7 +187,10 @@ void excel_writer::write_worksheets(zip_file &archive, const std::vector save_virtual_workbook(xlnt::workbook &wb, bool as_template) +bool excel_serializer::save_workbook(const std::string &filename, bool as_template) { - zip_file archive; - excel_writer writer(wb); - writer.write_data(archive, as_template); - std::vector buffer; - archive.save(buffer); + write_data(as_template); + archive_.save(filename); + + return true; +} + +bool excel_serializer::save_virtual_workbook(std::vector &bytes, bool as_template) +{ + write_data(as_template); + archive_.save(bytes); - return buffer; + return true; } } // namespace xlnt diff --git a/source/s11n/relationship_serializer.cpp b/source/s11n/relationship_serializer.cpp index 28ea0080..24a3304c 100644 --- a/source/s11n/relationship_serializer.cpp +++ b/source/s11n/relationship_serializer.cpp @@ -7,10 +7,27 @@ #include "detail/constants.hpp" namespace xlnt { - + bool relationship_serializer::read_relationships(const xml_document &xml, const std::string &dir, std::vector &relationships) { - return false; + auto root_node = xml.root(); + root_node.set_name("Relationships"); + + for(auto relationship_node : root_node.get_children()) + { + if(relationship_node.get_name() != "Relationship") + { + continue; + } + + std::string id = relationship_node.get_attribute("Id"); + std::string type = relationship_node.get_attribute("Type"); + std::string target = relationship_node.get_attribute("Target"); + + relationships.push_back(xlnt::relationship(type, id, target)); + } + + return true; } bool relationship_serializer::write_relationships(const std::vector &relationships, const std::string &dir, xml_document &xml) @@ -29,15 +46,15 @@ bool relationship_serializer::write_relationships(const std::vector(num_fmts.size())); + num_fmts_node.add_attribute("count", std::to_string(num_fmts.size())); for(auto &num_fmt : num_fmts) { auto num_fmt_node = num_fmts_node.add_child("numFmt"); - num_fmt_node.add_attribute("numFmtId", num_fmt.get_id()); + num_fmt_node.add_attribute("numFmtId", std::to_string(num_fmt.get_id())); num_fmt_node.add_attribute("formatCode", num_fmt.get_format_string()); } auto fonts_node = style_sheet_node.add_child("fonts"); auto fonts = wb_.get_fonts(); + if(fonts.empty()) { fonts.push_back(font()); } - fonts_node.add_attribute("count", static_cast(fonts.size())); - fonts_node.add_attribute("x14ac:knownFonts", 1); + + fonts_node.add_attribute("count", std::to_string(fonts.size())); + //TODO: what does this do? + //fonts_node.add_attribute("x14ac:knownFonts", "1"); for(auto &f : fonts) { @@ -556,13 +560,13 @@ bool style_serializer::write_stylesheet(xlnt::xml_document &xml) const if(f.is_bold()) { auto bold_node = font_node.add_child("b"); - bold_node.add_attribute("val", 1); + bold_node.add_attribute("val", "1"); } if(f.is_italic()) { auto bold_node = font_node.add_child("i"); - bold_node.add_attribute("val", 1); + bold_node.add_attribute("val", "1"); } if(f.is_underline()) @@ -582,21 +586,21 @@ bool style_serializer::write_stylesheet(xlnt::xml_document &xml) const if(f.is_strikethrough()) { auto bold_node = font_node.add_child("strike"); - bold_node.add_attribute("val", 1); + bold_node.add_attribute("val", "1"); } auto size_node = font_node.add_child("sz"); - size_node.add_attribute("val", f.get_size()); + size_node.add_attribute("val", std::to_string(f.get_size())); auto color_node = font_node.add_child("color"); if(f.get_color().get_type() == color::type::indexed) { - color_node.add_attribute("indexed", static_cast(f.get_color().get_index())); + color_node.add_attribute("indexed", std::to_string(f.get_color().get_index())); } else if(f.get_color().get_type() == color::type::theme) { - color_node.add_attribute("theme", static_cast(f.get_color().get_theme())); + color_node.add_attribute("theme", std::to_string(f.get_color().get_theme())); } auto name_node = font_node.add_child("name"); @@ -605,7 +609,7 @@ bool style_serializer::write_stylesheet(xlnt::xml_document &xml) const if(f.has_family()) { auto family_node = font_node.add_child("family"); - family_node.add_attribute("val", 2); + family_node.add_attribute("val", std::to_string(f.get_family())); } if(f.has_scheme()) @@ -617,7 +621,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(); - fills_node.add_attribute("count", static_cast(fills.size())); + fills_node.add_attribute("count", std::to_string(fills.size())); for(auto &fill_ : fills) { @@ -631,13 +635,13 @@ 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()) { - case color::type::auto_: fg_color_node.add_attribute("auto", fill_.get_foreground_color().get_auto()); break; - case color::type::theme: fg_color_node.add_attribute("theme", fill_.get_foreground_color().get_theme()); break; - case color::type::indexed: fg_color_node.add_attribute("indexed", fill_.get_foreground_color().get_index()); break; + case color::type::auto_: fg_color_node.add_attribute("auto", std::to_string(fill_.get_foreground_color().get_auto())); break; + case color::type::theme: fg_color_node.add_attribute("theme", std::to_string(fill_.get_foreground_color().get_theme())); break; + case color::type::indexed: fg_color_node.add_attribute("indexed", std::to_string(fill_.get_foreground_color().get_index())); break; default: throw std::runtime_error("bad type"); } } @@ -648,9 +652,9 @@ bool style_serializer::write_stylesheet(xlnt::xml_document &xml) const switch(fill_.get_background_color().get_type()) { - case color::type::auto_: bg_color_node.add_attribute("auto", fill_.get_background_color().get_auto()); break; - case color::type::theme: bg_color_node.add_attribute("theme", fill_.get_background_color().get_theme()); break; - case color::type::indexed: bg_color_node.add_attribute("indexed", fill_.get_background_color().get_index()); break; + case color::type::auto_: bg_color_node.add_attribute("auto", std::to_string(fill_.get_background_color().get_auto())); break; + case color::type::theme: bg_color_node.add_attribute("theme", std::to_string(fill_.get_background_color().get_theme())); break; + case color::type::indexed: bg_color_node.add_attribute("indexed", std::to_string(fill_.get_background_color().get_index())); break; default: throw std::runtime_error("bad type"); } } @@ -666,27 +670,27 @@ bool style_serializer::write_stylesheet(xlnt::xml_document &xml) const if(fill_.get_gradient_type_string() == "linear") { - gradient_fill_node.add_attribute("degree", fill_.get_rotation()); + gradient_fill_node.add_attribute("degree", std::to_string(fill_.get_rotation())); } else if(fill_.get_gradient_type_string() == "path") { - gradient_fill_node.add_attribute("left", fill_.get_gradient_left()); - gradient_fill_node.add_attribute("right", fill_.get_gradient_right()); - gradient_fill_node.add_attribute("top", fill_.get_gradient_top()); - gradient_fill_node.add_attribute("bottom", fill_.get_gradient_bottom()); + gradient_fill_node.add_attribute("left", std::to_string(fill_.get_gradient_left())); + gradient_fill_node.add_attribute("right", std::to_string(fill_.get_gradient_right())); + gradient_fill_node.add_attribute("top", std::to_string(fill_.get_gradient_top())); + gradient_fill_node.add_attribute("bottom", std::to_string(fill_.get_gradient_bottom())); auto start_node = gradient_fill_node.add_child("stop"); - start_node.add_attribute("position", 0); + start_node.add_attribute("position", "0"); auto end_node = gradient_fill_node.add_child("stop"); - end_node.add_attribute("position", 1); + end_node.add_attribute("position", "1"); } } } auto borders_node = style_sheet_node.add_child("borders"); const auto &borders = wb_.get_borders(); - borders_node.add_attribute("count", static_cast(borders.size())); + borders_node.add_attribute("count", std::to_string(borders.size())); for(const auto &border_ : borders) { @@ -715,39 +719,41 @@ bool style_serializer::write_stylesheet(xlnt::xml_document &xml) const if(side_.is_style_assigned()) { - auto style_attribute = side_node.add_attribute("style"); + std::string style_string; switch(side_.get_style()) { - case border_style::none: style_attribute.set_value("none"); break; - case border_style::dashdot : style_attribute.set_value("dashdot"); break; - case border_style::dashdotdot : style_attribute.set_value("dashdotdot"); break; - case border_style::dashed : style_attribute.set_value("dashed"); break; - case border_style::dotted : style_attribute.set_value("dotted"); break; - case border_style::double_ : style_attribute.set_value("double"); break; - case border_style::hair : style_attribute.set_value("hair"); break; - case border_style::medium : style_attribute.set_value("thin"); break; - case border_style::mediumdashdot: style_attribute.set_value("mediumdashdot"); break; - case border_style::mediumdashdotdot: style_attribute.set_value("mediumdashdotdot"); break; - case border_style::mediumdashed: style_attribute.set_value("mediumdashed"); break; - case border_style::slantdashdot: style_attribute.set_value("slantdashdot"); break; - case border_style::thick: style_attribute.set_value("thick"); break; - case border_style::thin: style_attribute.set_value("thin"); break; + case border_style::none: style_string = "none"; break; + case border_style::dashdot: style_string = "dashdot"; break; + case border_style::dashdotdot: style_string = "dashdotdot"; break; + case border_style::dashed: style_string = "dashed"; break; + case border_style::dotted: style_string = "dotted"; break; + case border_style::double_: style_string = "double"; break; + case border_style::hair: style_string = "hair"; break; + case border_style::medium: style_string = "thin"; break; + case border_style::mediumdashdot: style_string = "mediumdashdot"; break; + case border_style::mediumdashdotdot: style_string = "mediumdashdotdot"; break; + case border_style::mediumdashed: style_string = "mediumdashed"; break; + case border_style::slantdashdot: style_string = "slantdashdot"; break; + case border_style::thick: style_string = "thick"; break; + case border_style::thin: style_string = "thin"; break; default: throw std::runtime_error("invalid style"); } + + side_node.add_attribute("style", style_string); } if(side_.is_color_assigned()) { auto color_node = side_node.add_child("color"); - if(side_.get_color_type() == side::color_type::indexed) + if(side_.get_color().get_type() == color::type::indexed) { - color_node.add_attribute("indexed", (int)side_.get_color()); + color_node.add_attribute("indexed", std::to_string(side_.get_color().get_index())); } - else if(side_.get_color_type() == side::color_type::theme) + else if(side_.get_color().get_type() == color::type::theme) { - color_node.add_attribute("indexed", (int)side_.get_color()); + color_node.add_attribute("theme", std::to_string(side_.get_color().get_theme())); } else { @@ -758,40 +764,41 @@ bool style_serializer::write_stylesheet(xlnt::xml_document &xml) const } } - 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"); - 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_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"); + 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(); - cell_xfs_node.add_attribute("count", static_cast(styles.size())); + cell_xfs_node.add_attribute("count", std::to_string(styles.size())); for(auto &style : styles) { auto xf_node = cell_xfs_node.add_child("xf"); - xf_node.add_attribute("numFmtId", style.get_number_format().get_id()); - xf_node.add_attribute("fontId", (int)style.get_font_id()); + xf_node.add_attribute("numFmtId", std::to_string(style.get_number_format().get_id())); + xf_node.add_attribute("fontId", std::to_string(style.get_font_id())); if(style.fill_apply_) { - xf_node.add_attribute("fillId", (int)style.get_fill_id()); + xf_node.add_attribute("fillId", std::to_string(style.get_fill_id())); } if(style.border_apply_) { - xf_node.add_attribute("borderId", (int)style.get_border_id()); + xf_node.add_attribute("borderId", std::to_string(style.get_border_id())); } - xf_node.add_attribute("applyNumberFormat", style.number_format_apply_ ? 1 : 0); - xf_node.add_attribute("applyFont", style.font_apply_ ? 1 : 0); - xf_node.add_attribute("applyFill", style.fill_apply_ ? 1 : 0); - xf_node.add_attribute("applyBorder", style.border_apply_ ? 1 : 0); - xf_node.add_attribute("applyAlignment", style.alignment_apply_ ? 1 : 0); - xf_node.add_attribute("applyProtection", style.protection_apply_ ? 1 : 0); + xf_node.add_attribute("applyNumberFormat", style.number_format_apply_ ? "1" : "0"); + xf_node.add_attribute("applyFont", style.font_apply_ ? "1" : "0"); + xf_node.add_attribute("applyFill", style.fill_apply_ ? "1" : "0"); + xf_node.add_attribute("applyBorder", style.border_apply_ ? "1" : "0"); + xf_node.add_attribute("applyAlignment", style.alignment_apply_ ? "1" : "0"); + xf_node.add_attribute("applyProtection", style.protection_apply_ ? "1" : "0"); if(style.alignment_apply_) { @@ -847,22 +854,22 @@ bool style_serializer::write_stylesheet(xlnt::xml_document &xml) const if(style.alignment_.get_wrap_text()) { - alignment_node.add_attribute("wrapText", 1); + alignment_node.add_attribute("wrapText", "1"); } } } auto cell_styles_node = style_sheet_node.add_child("cellStyles"); - cell_styles_node.add_attribute("count", 1); + cell_styles_node.add_attribute("count", "1"); auto cell_style_node = cell_styles_node.add_child("cellStyle"); cell_style_node.add_attribute("name", "Normal"); - cell_style_node.add_attribute("xfId", 0); - cell_style_node.add_attribute("builtinId", 0); + cell_style_node.add_attribute("xfId", "0"); + cell_style_node.add_attribute("builtinId", "0"); - style_sheet_node.add_child("dxfs").add_attribute("count", 0); + style_sheet_node.add_child("dxfs").add_attribute("count", "0"); auto table_styles_node = style_sheet_node.add_child("tableStyles"); - table_styles_node.add_attribute("count", 0); + table_styles_node.add_attribute("count", "0"); table_styles_node.add_attribute("defaultTableStyle", "TableStyleMedium2"); table_styles_node.add_attribute("defaultPivotStyle", "PivotStyleMedium9"); diff --git a/source/s11n/theme_serializer.cpp b/source/s11n/theme_serializer.cpp new file mode 100644 index 00000000..eff2f9e5 --- /dev/null +++ b/source/s11n/theme_serializer.cpp @@ -0,0 +1,356 @@ +#include +#include +#include + +#include + +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 xml; + + 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"); + clr_scheme_node.add_attribute("name", "Office"); + + struct scheme_element + { + std::string name; + std::string sub_element_name; + std::string val; + }; + + std::vector scheme_elements = + { + {"a:dk1", "a:sysClr", "windowText"}, + {"a:lt1", "a:sysClr", "window"}, + {"a:dk2", "a:srgbClr", "1F497D"}, + {"a:lt2", "a:srgbClr", "EEECE1"}, + {"a:accent1", "a:srgbClr", "4F81BD"}, + {"a:accent2", "a:srgbClr", "C0504D"}, + {"a:accent3", "a:srgbClr", "9BBB59"}, + {"a:accent4", "a:srgbClr", "8064A2"}, + {"a:accent5", "a:srgbClr", "4BACC6"}, + {"a:accent6", "a:srgbClr", "F79646"}, + {"a:hlink", "a:srgbClr", "0000FF"}, + {"a:folHlink", "a:srgbClr", "800080"}, + }; + + for(auto element : scheme_elements) + { + auto element_node = clr_scheme_node.add_child(element.name); + element_node.add_child(element.sub_element_name).add_attribute("val", element.val); + + if(element.name == "a:dk1") + { + element_node.get_child(element.sub_element_name).add_attribute("lastClr", "000000"); + } + else if(element.name == "a:lt1") + { + element_node.get_child(element.sub_element_name).add_attribute("lastClr", "FFFFFF"); + } + } + + struct font_scheme + { + bool typeface; + std::string script; + std::string major; + std::string minor; + }; + + std::vector font_schemes = + { + {true, "a:latin", "Cambria", "Calibri"}, + {true, "a:ea", "", ""}, + {true, "a:cs", "", ""}, + {false, "Jpan", "\xef\xbc\xad\xef\xbc\xb3 \xef\xbc\xb0\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf", "\xef\xbc\xad\xef\xbc\xb3 \xef\xbc\xb0\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf"}, + {false, "Hang", "\xeb\xa7\x91\xec\x9d\x80 \xea\xb3\xa0\xeb\x94\x95", "\xeb\xa7\x91\xec\x9d\x80 \xea\xb3\xa0\xeb\x94\x95"}, + {false, "Hans", "\xe5\xae\x8b\xe4\xbd\x93", "\xe5\xae\x8b\xe4\xbd\x93"}, + {false, "Hant", "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94", "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94"}, + {false, "Arab", "Times New Roman", "Arial"}, + {false, "Hebr", "Times New Roman", "Arial"}, + {false, "Thai", "Tahoma", "Tahoma"}, + {false, "Ethi", "Nyala", "Nyala"}, + {false, "Beng", "Vrinda", "Vrinda"}, + {false, "Gujr", "Shruti", "Shruti"}, + {false, "Khmr", "MoolBoran", "DaunPenh"}, + {false, "Knda", "Tunga", "Tunga"}, + {false, "Guru", "Raavi", "Raavi"}, + {false, "Cans", "Euphemia", "Euphemia"}, + {false, "Cher", "Plantagenet Cherokee", "Plantagenet Cherokee"}, + {false, "Yiii", "Microsoft Yi Baiti", "Microsoft Yi Baiti"}, + {false, "Tibt", "Microsoft Himalaya", "Microsoft Himalaya"}, + {false, "Thaa", "MV Boli", "MV Boli"}, + {false, "Deva", "Mangal", "Mangal"}, + {false, "Telu", "Gautami", "Gautami"}, + {false, "Taml", "Latha", "Latha"}, + {false, "Syrc", "Estrangelo Edessa", "Estrangelo Edessa"}, + {false, "Orya", "Kalinga", "Kalinga"}, + {false, "Mlym", "Kartika", "Kartika"}, + {false, "Laoo", "DokChampa", "DokChampa"}, + {false, "Sinh", "Iskoola Pota", "Iskoola Pota"}, + {false, "Mong", "Mongolian Baiti", "Mongolian Baiti"}, + {false, "Viet", "Times New Roman", "Arial"}, + {false, "Uigh", "Microsoft Uighur", "Microsoft Uighur"} + }; + + auto font_scheme_node = theme_elements_node.add_child("a:fontScheme"); + font_scheme_node.add_attribute("name", "Office"); + + auto major_fonts_node = font_scheme_node.add_child("a:majorFont"); + auto minor_fonts_node = font_scheme_node.add_child("a:minorFont"); + + for(auto scheme : font_schemes) + { + if(scheme.typeface) + { + 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); + minor_font_node.add_attribute("typeface", scheme.minor); + } + else + { + 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"); + minor_font_node.add_attribute("script", scheme.script); + minor_font_node.add_attribute("typeface", scheme.minor); + } + } + + auto format_scheme_node = theme_elements_node.add_child("a:fmtScheme"); + format_scheme_node.add_attribute("name", "Office"); + + auto fill_style_list_node = format_scheme_node.add_child("a:fillStyleLst"); + fill_style_list_node.add_child("a:solidFill").add_child("a:schemeClr").add_attribute("val", "phClr"); + + auto grad_fill_node = fill_style_list_node.add_child("a:gradFill"); + grad_fill_node.add_attribute("rotWithShape", "1"); + + 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); + 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"); + scheme_color_node.add_child("a:satMod").add_attribute("val", "300000"); + + gs_node = grad_fill_list.add_child("a:gs"); + gs_node.add_attribute("pos", "35000"); + 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", "37000"); + scheme_color_node.add_child("a:satMod").add_attribute("val", "300000"); + + gs_node = grad_fill_list.add_child("a:gs"); + gs_node.add_attribute("pos", "100000"); + 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", "15000"); + scheme_color_node.add_child("a:satMod").add_attribute("val", "350000"); + + auto lin_node = grad_fill_node.add_child("a:lin"); + lin_node.add_attribute("ang", "16200000"); + lin_node.add_attribute("scaled", "1"); + + grad_fill_node = fill_style_list_node.add_child("a:gradFill"); + grad_fill_node.add_attribute("rotWithShape", "1"); + + grad_fill_list = grad_fill_node.add_child("a:gsLst"); + gs_node = grad_fill_list.add_child("a:gs"); + gs_node.add_attribute("pos", "0"); + scheme_color_node = gs_node.add_child("a:schemeClr"); + scheme_color_node.add_attribute("val", "phClr"); + scheme_color_node.add_child("a:shade").add_attribute("val", "51000"); + scheme_color_node.add_child("a:satMod").add_attribute("val", "130000"); + + gs_node = grad_fill_list.add_child("a:gs"); + gs_node.add_attribute("pos", "80000"); + scheme_color_node = gs_node.add_child("a:schemeClr"); + scheme_color_node.add_attribute("val", "phClr"); + scheme_color_node.add_child("a:shade").add_attribute("val", "93000"); + scheme_color_node.add_child("a:satMod").add_attribute("val", "130000"); + + gs_node = grad_fill_list.add_child("a:gs"); + gs_node.add_attribute("pos", "100000"); + scheme_color_node = gs_node.add_child("a:schemeClr"); + scheme_color_node.add_attribute("val", "phClr"); + scheme_color_node.add_child("a:shade").add_attribute("val", "94000"); + scheme_color_node.add_child("a:satMod").add_attribute("val", "135000"); + + lin_node = grad_fill_node.add_child("a:lin"); + lin_node.add_attribute("ang", "16200000"); + lin_node.add_attribute("scaled", "0"); + + auto line_style_list_node = format_scheme_node.add_child("a:lnStyleLst"); + + auto ln_node = line_style_list_node.add_child("a:ln"); + ln_node.add_attribute("w", "9525"); + ln_node.add_attribute("cap", "flat"); + ln_node.add_attribute("cmpd", "sng"); + ln_node.add_attribute("algn", "ctr"); + + auto solid_fill_node = ln_node.add_child("a:solidFill"); + scheme_color_node = solid_fill_node.add_child("a:schemeClr"); + scheme_color_node.add_attribute("val", "phClr"); + scheme_color_node.add_child("a:shade").add_attribute("val", "95000"); + scheme_color_node.add_child("a:satMod").add_attribute("val", "105000"); + ln_node.add_child("a:prstDash").add_attribute("val", "solid"); + + ln_node = line_style_list_node.add_child("a:ln"); + ln_node.add_attribute("w", "25400"); + ln_node.add_attribute("cap", "flat"); + ln_node.add_attribute("cmpd", "sng"); + ln_node.add_attribute("algn", "ctr"); + + solid_fill_node = ln_node.add_child("a:solidFill"); + scheme_color_node = solid_fill_node.add_child("a:schemeClr"); + scheme_color_node.add_attribute("val", "phClr"); + ln_node.add_child("a:prstDash").add_attribute("val", "solid"); + + ln_node = line_style_list_node.add_child("a:ln"); + ln_node.add_attribute("w", "38100"); + ln_node.add_attribute("cap", "flat"); + ln_node.add_attribute("cmpd", "sng"); + ln_node.add_attribute("algn", "ctr"); + + solid_fill_node = ln_node.add_child("a:solidFill"); + scheme_color_node = solid_fill_node.add_child("a:schemeClr"); + scheme_color_node.add_attribute("val", "phClr"); + ln_node.add_child("a:prstDash").add_attribute("val", "solid"); + + auto effect_style_list_node = format_scheme_node.add_child("a:effectStyleLst"); + auto effect_style_node = effect_style_list_node.add_child("a:effectStyle"); + auto effect_list_node = effect_style_node.add_child("a:effectLst"); + auto outer_shadow_node = effect_list_node.add_child("a:outerShdw"); + outer_shadow_node.add_attribute("blurRad", "40000"); + outer_shadow_node.add_attribute("dist", "20000"); + outer_shadow_node.add_attribute("dir", "5400000"); + outer_shadow_node.add_attribute("rotWithShape", "0"); + auto srgb_clr_node = outer_shadow_node.add_child("a:srgbClr"); + srgb_clr_node.add_attribute("val", "000000"); + srgb_clr_node.add_child("a:alpha").add_attribute("val", "38000"); + + effect_style_node = effect_style_list_node.add_child("a:effectStyle"); + effect_list_node = effect_style_node.add_child("a:effectLst"); + outer_shadow_node = effect_list_node.add_child("a:outerShdw"); + outer_shadow_node.add_attribute("blurRad", "40000"); + outer_shadow_node.add_attribute("dist", "23000"); + outer_shadow_node.add_attribute("dir", "5400000"); + outer_shadow_node.add_attribute("rotWithShape", "0"); + srgb_clr_node = outer_shadow_node.add_child("a:srgbClr"); + srgb_clr_node.add_attribute("val", "000000"); + srgb_clr_node.add_child("a:alpha").add_attribute("val", "35000"); + + effect_style_node = effect_style_list_node.add_child("a:effectStyle"); + effect_list_node = effect_style_node.add_child("a:effectLst"); + outer_shadow_node = effect_list_node.add_child("a:outerShdw"); + outer_shadow_node.add_attribute("blurRad", "40000"); + outer_shadow_node.add_attribute("dist", "23000"); + outer_shadow_node.add_attribute("dir", "5400000"); + outer_shadow_node.add_attribute("rotWithShape", "0"); + srgb_clr_node = outer_shadow_node.add_child("a:srgbClr"); + srgb_clr_node.add_attribute("val", "000000"); + srgb_clr_node.add_child("a:alpha").add_attribute("val", "35000"); + auto scene3d_node = effect_style_node.add_child("a:scene3d"); + auto camera_node = scene3d_node.add_child("a:camera"); + camera_node.add_attribute("prst", "orthographicFront"); + auto rot_node = camera_node.add_child("a:rot"); + rot_node.add_attribute("lat", "0"); + rot_node.add_attribute("lon", "0"); + rot_node.add_attribute("rev", "0"); + auto light_rig_node = scene3d_node.add_child("a:lightRig"); + light_rig_node.add_attribute("rig", "threePt"); + light_rig_node.add_attribute("dir", "t"); + rot_node = light_rig_node.add_child("a:rot"); + rot_node.add_attribute("lat", "0"); + rot_node.add_attribute("lon", "0"); + rot_node.add_attribute("rev", "1200000"); + + auto bevel_node = effect_style_node.add_child("a:sp3d").add_child("a:bevelT"); + bevel_node.add_attribute("w", "63500"); + bevel_node.add_attribute("h", "25400"); + + auto bg_fill_style_list_node = format_scheme_node.add_child("a:bgFillStyleLst"); + + bg_fill_style_list_node.add_child("a:solidFill").add_child("a:schemeClr").add_attribute("val", "phClr"); + + grad_fill_node = bg_fill_style_list_node.add_child("a:gradFill"); + grad_fill_node.add_attribute("rotWithShape", "1"); + + grad_fill_list = grad_fill_node.add_child("a:gsLst"); + gs_node = grad_fill_list.add_child("a:gs"); + gs_node.add_attribute("pos", "0"); + 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", "40000"); + scheme_color_node.add_child("a:satMod").add_attribute("val", "350000"); + + gs_node = grad_fill_list.add_child("a:gs"); + gs_node.add_attribute("pos", "40000"); + 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", "45000"); + scheme_color_node.add_child("a:shade").add_attribute("val", "99000"); + scheme_color_node.add_child("a:satMod").add_attribute("val", "350000"); + + gs_node = grad_fill_list.add_child("a:gs"); + gs_node.add_attribute("pos", "100000"); + scheme_color_node = gs_node.add_child("a:schemeClr"); + scheme_color_node.add_attribute("val", "phClr"); + scheme_color_node.add_child("a:shade").add_attribute("val", "20000"); + scheme_color_node.add_child("a:satMod").add_attribute("val", "255000"); + + auto path_node = grad_fill_node.add_child("a:path"); + path_node.add_attribute("path", "circle"); + auto fill_to_rect_node = path_node.add_child("a:fillToRect"); + fill_to_rect_node.add_attribute("l", "50000"); + fill_to_rect_node.add_attribute("t", "-80000"); + fill_to_rect_node.add_attribute("r", "50000"); + fill_to_rect_node.add_attribute("b", "180000"); + + grad_fill_node = bg_fill_style_list_node.add_child("a:gradFill"); + grad_fill_node.add_attribute("rotWithShape", "1"); + + grad_fill_list = grad_fill_node.add_child("a:gsLst"); + gs_node = grad_fill_list.add_child("a:gs"); + gs_node.add_attribute("pos", "0"); + 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", "80000"); + scheme_color_node.add_child("a:satMod").add_attribute("val", "300000"); + + gs_node = grad_fill_list.add_child("a:gs"); + gs_node.add_attribute("pos", "100000"); + scheme_color_node = gs_node.add_child("a:schemeClr"); + scheme_color_node.add_attribute("val", "phClr"); + scheme_color_node.add_child("a:shade").add_attribute("val", "30000"); + scheme_color_node.add_child("a:satMod").add_attribute("val", "200000"); + + path_node = grad_fill_node.add_child("a:path"); + path_node.add_attribute("path", "circle"); + fill_to_rect_node = path_node.add_child("a:fillToRect"); + fill_to_rect_node.add_attribute("l", "50000"); + fill_to_rect_node.add_attribute("t", "50000"); + fill_to_rect_node.add_attribute("r", "50000"); + fill_to_rect_node.add_attribute("b", "50000"); + + theme_node.add_child("a:objectDefaults"); + theme_node.add_child("a:extraClrSchemeLst"); + + return xml; +} + +} // namespace xlnt diff --git a/source/s11n/workbook_serializer.cpp b/source/s11n/workbook_serializer.cpp index ed8834d2..e7ca1cfc 100644 --- a/source/s11n/workbook_serializer.cpp +++ b/source/s11n/workbook_serializer.cpp @@ -1,11 +1,15 @@ #include #include +#include #include #include #include #include #include +#include #include +#include +#include #include "detail/constants.hpp" @@ -66,15 +70,15 @@ std::vector> workbook_serializer::read_sheet auto with_ns = [&](const std::string &base) { return ns.empty() ? base : ns + ":" + base; }; - auto root_node = doc.child(with_ns("workbook").c_str()); - auto sheets_node = root_node.child(with_ns("sheets").c_str()); + auto root_node = doc.get_child(with_ns("workbook")); + auto sheets_node = root_node.get_child(with_ns("sheets")); std::vector> sheets; // store temp because pugixml iteration uses the internal char array multiple times auto sheet_element_name = with_ns("sheet"); - for(auto sheet_node : sheets_node.children(sheet_element_name.c_str())) + for(auto sheet_node : sheets_node.children(sheet_element_name)) { std::string id = sheet_node.attribute("r:id").as_string(); std::string name = sheet_node.attribute("name").as_string(); @@ -112,53 +116,6 @@ void workbook_serializer::read_properties_core(const xml_document &xml) } } -std::string workbook_serializer::read_dimension(const std::string &xml_string) -{ - pugi::xml_document doc; - doc.load(xml_string.c_str()); - auto root_node = doc.child("worksheet"); - auto dimension_node = root_node.child("dimension"); - std::string dimension = dimension_node.attribute("ref").as_string(); - return dimension; -} - -std::vector workbook_serializer::read_relationships(zip_file &archive, const std::string &filename) -{ - auto filename_separator_index = filename.find_last_of('/'); - auto basename = filename.substr(filename_separator_index + 1); - auto dirname = filename.substr(0, filename_separator_index); - auto rels_filename = dirname + "/_rels/" + basename + ".rels"; - - pugi::xml_document doc; - auto content = archive.read(rels_filename); - doc.load(content.c_str()); - - auto root_node = doc.child("Relationships"); - - std::vector relationships; - - for(auto relationship : root_node.children("Relationship")) - { - std::string id = relationship.attribute("Id").as_string(); - std::string type = relationship.attribute("Type").as_string(); - std::string target = relationship.attribute("Target").as_string(); - - if(target[0] != '/' && target.substr(0, 2) != "..") - { - target = dirname + "/" + target; - } - - if(target[0] == '/') - { - target = target.substr(1); - } - - relationships.push_back(xlnt::relationship(type, id, target)); - } - - return relationships; -} - std::string workbook_serializer::determine_document_type(const manifest &manifest) { if(!manifest.has_override_type(constants::ArcWorkbook)) @@ -184,22 +141,27 @@ std::string workbook_serializer::determine_document_type(const manifest &manifes 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(zip_file &archive) { static const std::string ValidWorksheet = "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"; - auto content_types = read_content_types(archive); std::vector valid_sheets; - for(const auto &content_type : content_types) + for(const auto &content_type : wb_.get_manifest().get_override_types()) { - if(content_type.second == ValidWorksheet) + if(content_type.get_content_type() == ValidWorksheet) { - valid_sheets.push_back(content_type.first); + valid_sheets.push_back(content_type.get_part_name()); } } - auto workbook_relationships = read_relationships(archive, "xl/workbook.xml"); + auto &workbook_relationships = wb_.get_relationships(); std::vector> result; for(const auto &ws : read_sheets(archive)) @@ -216,456 +178,80 @@ std::vector> workbook_serializer::detect_wor return result; } -std::string workbook_serializer::write_shared_strings(const std::vector &string_table) +xml_document workbook_serializer::write_properties_core() const { - pugi::xml_document doc; - auto root_node = doc.append_child("sst"); - root_node.append_attribute("xmlns").set_value("http://schemas.openxmlformats.org/spreadsheetml/2006/main"); - root_node.append_attribute("uniqueCount").set_value((int)string_table.size()); + auto &props = wb_.get_properties(); - for(auto string : string_table) + xml_document xml; + + 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)); + root_node.get_child("dcterms:created").add_attribute("xsi:type", "dcterms:W3CDTF"); + root_node.add_child("dcterms:modified").set_text(datetime_to_w3cdtf(props.modified)); + root_node.get_child("dcterms:modified").add_attribute("xsi:type", "dcterms:W3CDTF"); + root_node.add_child("dc:title").set_text(props.title); + root_node.add_child("dc:description"); + root_node.add_child("dc:subject"); + root_node.add_child("cp:keywords"); + root_node.add_child("cp:category"); + + return xml; +} + +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.root(); + root_node.set_name("Properties"); + + root_node.add_child("Application").set_text("Microsoft Excel"); + root_node.add_child("DocSecurity").set_text("0"); + root_node.add_child("ScaleCrop").set_text("false"); + root_node.add_child("Company"); + root_node.add_child("LinksUpToDate").set_text("false"); + root_node.add_child("SharedDoc").set_text("false"); + root_node.add_child("HyperlinksChanged").set_text("false"); + root_node.add_child("AppVersion").set_text("12.0000"); + + auto heading_pairs_node = root_node.add_child("HeadingPairs"); + auto heading_pairs_vector_node = heading_pairs_node.add_child("vt:vector"); + 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())); + + 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())); + + for(auto ws : wb_) { - root_node.append_child("si").append_child("t").text().set(string.c_str()); + titles_of_parts_vector_node.add_child("vt:lpstr").set_text(ws.get_title()); } - - std::stringstream ss; - doc.save(ss); - - return ss.str(); + + return xml; } -std::string workbook_serializer::write_properties_core(const document_properties &prop) -{ - pugi::xml_document doc; - auto root_node = doc.append_child("cp:coreProperties"); - root_node.append_attribute("xmlns:cp").set_value("http://schemas.openxmlformats.org/package/2006/metadata/core-properties"); - root_node.append_attribute("xmlns:dc").set_value("http://purl.org/dc/elements/1.1/"); - root_node.append_attribute("xmlns:dcmitype").set_value("http://purl.org/dc/dcmitype/"); - root_node.append_attribute("xmlns:dcterms").set_value("http://purl.org/dc/terms/"); - root_node.append_attribute("xmlns:xsi").set_value("http://www.w3.org/2001/XMLSchema-instance"); - - root_node.append_child("dc:creator").text().set(prop.creator.c_str()); - root_node.append_child("cp:lastModifiedBy").text().set(prop.last_modified_by.c_str()); - root_node.append_child("dcterms:created").text().set(datetime_to_w3cdtf(prop.created).c_str()); - root_node.child("dcterms:created").append_attribute("xsi:type").set_value("dcterms:W3CDTF"); - root_node.append_child("dcterms:modified").text().set(datetime_to_w3cdtf(prop.modified).c_str()); - root_node.child("dcterms:modified").append_attribute("xsi:type").set_value("dcterms:W3CDTF"); - root_node.append_child("dc:title").text().set(prop.title.c_str()); - root_node.append_child("dc:description"); - root_node.append_child("dc:subject"); - root_node.append_child("cp:keywords"); - root_node.append_child("cp:category"); - - std::stringstream ss; - doc.save(ss); - - return ss.str(); -} - -std::string workbook_serializer::write_worksheet_rels(worksheet ws) -{ - return write_relationships(ws.get_relationships(), ""); -} - -std::string workbook_serializer::write_theme() -{ - pugi::xml_document doc; - auto theme_node = doc.append_child("a:theme"); - theme_node.append_attribute("xmlns:a").set_value(constants::Namespaces.at("drawingml").c_str()); - theme_node.append_attribute("name").set_value("Office Theme"); - auto theme_elements_node = theme_node.append_child("a:themeElements"); - auto clr_scheme_node = theme_elements_node.append_child("a:clrScheme"); - clr_scheme_node.append_attribute("name").set_value("Office"); - - struct scheme_element - { - std::string name; - std::string sub_element_name; - std::string val; - }; - - std::vector scheme_elements = - { - {"a:dk1", "a:sysClr", "windowText"}, - {"a:lt1", "a:sysClr", "window"}, - {"a:dk2", "a:srgbClr", "1F497D"}, - {"a:lt2", "a:srgbClr", "EEECE1"}, - {"a:accent1", "a:srgbClr", "4F81BD"}, - {"a:accent2", "a:srgbClr", "C0504D"}, - {"a:accent3", "a:srgbClr", "9BBB59"}, - {"a:accent4", "a:srgbClr", "8064A2"}, - {"a:accent5", "a:srgbClr", "4BACC6"}, - {"a:accent6", "a:srgbClr", "F79646"}, - {"a:hlink", "a:srgbClr", "0000FF"}, - {"a:folHlink", "a:srgbClr", "800080"}, - }; - - for(auto element : scheme_elements) - { - auto element_node = clr_scheme_node.append_child(element.name.c_str()); - element_node.append_child(element.sub_element_name.c_str()).append_attribute("val").set_value(element.val.c_str()); - - if(element.name == "a:dk1") - { - element_node.child(element.sub_element_name.c_str()).append_attribute("lastClr").set_value("000000"); - } - else if(element.name == "a:lt1") - { - element_node.child(element.sub_element_name.c_str()).append_attribute("lastClr").set_value("FFFFFF"); - } - } - - struct font_scheme - { - bool typeface; - std::string script; - std::string major; - std::string minor; - }; - - std::vector font_schemes = - { - {true, "a:latin", "Cambria", "Calibri"}, - {true, "a:ea", "", ""}, - {true, "a:cs", "", ""}, - {false, "Jpan", "\xef\xbc\xad\xef\xbc\xb3 \xef\xbc\xb0\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf", "\xef\xbc\xad\xef\xbc\xb3 \xef\xbc\xb0\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf"}, - {false, "Hang", "\xeb\xa7\x91\xec\x9d\x80 \xea\xb3\xa0\xeb\x94\x95", "\xeb\xa7\x91\xec\x9d\x80 \xea\xb3\xa0\xeb\x94\x95"}, - {false, "Hans", "\xe5\xae\x8b\xe4\xbd\x93", "\xe5\xae\x8b\xe4\xbd\x93"}, - {false, "Hant", "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94", "\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94"}, - {false, "Arab", "Times New Roman", "Arial"}, - {false, "Hebr", "Times New Roman", "Arial"}, - {false, "Thai", "Tahoma", "Tahoma"}, - {false, "Ethi", "Nyala", "Nyala"}, - {false, "Beng", "Vrinda", "Vrinda"}, - {false, "Gujr", "Shruti", "Shruti"}, - {false, "Khmr", "MoolBoran", "DaunPenh"}, - {false, "Knda", "Tunga", "Tunga"}, - {false, "Guru", "Raavi", "Raavi"}, - {false, "Cans", "Euphemia", "Euphemia"}, - {false, "Cher", "Plantagenet Cherokee", "Plantagenet Cherokee"}, - {false, "Yiii", "Microsoft Yi Baiti", "Microsoft Yi Baiti"}, - {false, "Tibt", "Microsoft Himalaya", "Microsoft Himalaya"}, - {false, "Thaa", "MV Boli", "MV Boli"}, - {false, "Deva", "Mangal", "Mangal"}, - {false, "Telu", "Gautami", "Gautami"}, - {false, "Taml", "Latha", "Latha"}, - {false, "Syrc", "Estrangelo Edessa", "Estrangelo Edessa"}, - {false, "Orya", "Kalinga", "Kalinga"}, - {false, "Mlym", "Kartika", "Kartika"}, - {false, "Laoo", "DokChampa", "DokChampa"}, - {false, "Sinh", "Iskoola Pota", "Iskoola Pota"}, - {false, "Mong", "Mongolian Baiti", "Mongolian Baiti"}, - {false, "Viet", "Times New Roman", "Arial"}, - {false, "Uigh", "Microsoft Uighur", "Microsoft Uighur"} - }; - - auto font_scheme_node = theme_elements_node.append_child("a:fontScheme"); - font_scheme_node.append_attribute("name").set_value("Office"); - - auto major_fonts_node = font_scheme_node.append_child("a:majorFont"); - auto minor_fonts_node = font_scheme_node.append_child("a:minorFont"); - - for(auto scheme : font_schemes) - { - pugi::xml_node major_font_node, minor_font_node; - - if(scheme.typeface) - { - major_font_node = major_fonts_node.append_child(scheme.script.c_str()); - minor_font_node = minor_fonts_node.append_child(scheme.script.c_str()); - } - else - { - major_font_node = major_fonts_node.append_child("a:font"); - major_font_node.append_attribute("script").set_value(scheme.script.c_str()); - minor_font_node = minor_fonts_node.append_child("a:font"); - minor_font_node.append_attribute("script").set_value(scheme.script.c_str()); - } - - major_font_node.append_attribute("typeface").set_value(scheme.major.c_str()); - minor_font_node.append_attribute("typeface").set_value(scheme.minor.c_str()); - } - - auto format_scheme_node = theme_elements_node.append_child("a:fmtScheme"); - format_scheme_node.append_attribute("name").set_value("Office"); - - auto fill_style_list_node = format_scheme_node.append_child("a:fillStyleLst"); - fill_style_list_node.append_child("a:solidFill").append_child("a:schemeClr").append_attribute("val").set_value("phClr"); - - auto grad_fill_node = fill_style_list_node.append_child("a:gradFill"); - grad_fill_node.append_attribute("rotWithShape").set_value(1); - - auto grad_fill_list = grad_fill_node.append_child("a:gsLst"); - auto gs_node = grad_fill_list.append_child("a:gs"); - gs_node.append_attribute("pos").set_value(0); - auto scheme_color_node = gs_node.append_child("a:schemeClr"); - scheme_color_node.append_attribute("val").set_value("phClr"); - scheme_color_node.append_child("a:tint").append_attribute("val").set_value(50000); - scheme_color_node.append_child("a:satMod").append_attribute("val").set_value(300000); - - gs_node = grad_fill_list.append_child("a:gs"); - gs_node.append_attribute("pos").set_value(35000); - scheme_color_node = gs_node.append_child("a:schemeClr"); - scheme_color_node.append_attribute("val").set_value("phClr"); - scheme_color_node.append_child("a:tint").append_attribute("val").set_value(37000); - scheme_color_node.append_child("a:satMod").append_attribute("val").set_value(300000); - - gs_node = grad_fill_list.append_child("a:gs"); - gs_node.append_attribute("pos").set_value(100000); - scheme_color_node = gs_node.append_child("a:schemeClr"); - scheme_color_node.append_attribute("val").set_value("phClr"); - scheme_color_node.append_child("a:tint").append_attribute("val").set_value(15000); - scheme_color_node.append_child("a:satMod").append_attribute("val").set_value(350000); - - auto lin_node = grad_fill_node.append_child("a:lin"); - lin_node.append_attribute("ang").set_value(16200000); - lin_node.append_attribute("scaled").set_value(1); - - grad_fill_node = fill_style_list_node.append_child("a:gradFill"); - grad_fill_node.append_attribute("rotWithShape").set_value(1); - - grad_fill_list = grad_fill_node.append_child("a:gsLst"); - gs_node = grad_fill_list.append_child("a:gs"); - gs_node.append_attribute("pos").set_value(0); - scheme_color_node = gs_node.append_child("a:schemeClr"); - scheme_color_node.append_attribute("val").set_value("phClr"); - scheme_color_node.append_child("a:shade").append_attribute("val").set_value(51000); - scheme_color_node.append_child("a:satMod").append_attribute("val").set_value(130000); - - gs_node = grad_fill_list.append_child("a:gs"); - gs_node.append_attribute("pos").set_value(80000); - scheme_color_node = gs_node.append_child("a:schemeClr"); - scheme_color_node.append_attribute("val").set_value("phClr"); - scheme_color_node.append_child("a:shade").append_attribute("val").set_value(93000); - scheme_color_node.append_child("a:satMod").append_attribute("val").set_value(130000); - - gs_node = grad_fill_list.append_child("a:gs"); - gs_node.append_attribute("pos").set_value(100000); - scheme_color_node = gs_node.append_child("a:schemeClr"); - scheme_color_node.append_attribute("val").set_value("phClr"); - scheme_color_node.append_child("a:shade").append_attribute("val").set_value(94000); - scheme_color_node.append_child("a:satMod").append_attribute("val").set_value(135000); - - lin_node = grad_fill_node.append_child("a:lin"); - lin_node.append_attribute("ang").set_value(16200000); - lin_node.append_attribute("scaled").set_value(0); - - auto line_style_list_node = format_scheme_node.append_child("a:lnStyleLst"); - - auto ln_node = line_style_list_node.append_child("a:ln"); - ln_node.append_attribute("w").set_value(9525); - ln_node.append_attribute("cap").set_value("flat"); - ln_node.append_attribute("cmpd").set_value("sng"); - ln_node.append_attribute("algn").set_value("ctr"); - - auto solid_fill_node = ln_node.append_child("a:solidFill"); - scheme_color_node = solid_fill_node.append_child("a:schemeClr"); - scheme_color_node.append_attribute("val").set_value("phClr"); - scheme_color_node.append_child("a:shade").append_attribute("val").set_value(95000); - scheme_color_node.append_child("a:satMod").append_attribute("val").set_value(105000); - ln_node.append_child("a:prstDash").append_attribute("val").set_value("solid"); - - ln_node = line_style_list_node.append_child("a:ln"); - ln_node.append_attribute("w").set_value(25400); - ln_node.append_attribute("cap").set_value("flat"); - ln_node.append_attribute("cmpd").set_value("sng"); - ln_node.append_attribute("algn").set_value("ctr"); - - solid_fill_node = ln_node.append_child("a:solidFill"); - scheme_color_node = solid_fill_node.append_child("a:schemeClr"); - scheme_color_node.append_attribute("val").set_value("phClr"); - ln_node.append_child("a:prstDash").append_attribute("val").set_value("solid"); - - ln_node = line_style_list_node.append_child("a:ln"); - ln_node.append_attribute("w").set_value(38100); - ln_node.append_attribute("cap").set_value("flat"); - ln_node.append_attribute("cmpd").set_value("sng"); - ln_node.append_attribute("algn").set_value("ctr"); - - solid_fill_node = ln_node.append_child("a:solidFill"); - scheme_color_node = solid_fill_node.append_child("a:schemeClr"); - scheme_color_node.append_attribute("val").set_value("phClr"); - ln_node.append_child("a:prstDash").append_attribute("val").set_value("solid"); - - auto effect_style_list_node = format_scheme_node.append_child("a:effectStyleLst"); - auto effect_style_node = effect_style_list_node.append_child("a:effectStyle"); - auto effect_list_node = effect_style_node.append_child("a:effectLst"); - auto outer_shadow_node = effect_list_node.append_child("a:outerShdw"); - outer_shadow_node.append_attribute("blurRad").set_value(40000); - outer_shadow_node.append_attribute("dist").set_value(20000); - outer_shadow_node.append_attribute("dir").set_value(5400000); - outer_shadow_node.append_attribute("rotWithShape").set_value(0); - auto srgb_clr_node = outer_shadow_node.append_child("a:srgbClr"); - srgb_clr_node.append_attribute("val").set_value("000000"); - srgb_clr_node.append_child("a:alpha").append_attribute("val").set_value(38000); - - effect_style_node = effect_style_list_node.append_child("a:effectStyle"); - effect_list_node = effect_style_node.append_child("a:effectLst"); - outer_shadow_node = effect_list_node.append_child("a:outerShdw"); - outer_shadow_node.append_attribute("blurRad").set_value(40000); - outer_shadow_node.append_attribute("dist").set_value(23000); - outer_shadow_node.append_attribute("dir").set_value(5400000); - outer_shadow_node.append_attribute("rotWithShape").set_value(0); - srgb_clr_node = outer_shadow_node.append_child("a:srgbClr"); - srgb_clr_node.append_attribute("val").set_value("000000"); - srgb_clr_node.append_child("a:alpha").append_attribute("val").set_value(35000); - - effect_style_node = effect_style_list_node.append_child("a:effectStyle"); - effect_list_node = effect_style_node.append_child("a:effectLst"); - outer_shadow_node = effect_list_node.append_child("a:outerShdw"); - outer_shadow_node.append_attribute("blurRad").set_value(40000); - outer_shadow_node.append_attribute("dist").set_value(23000); - outer_shadow_node.append_attribute("dir").set_value(5400000); - outer_shadow_node.append_attribute("rotWithShape").set_value(0); - srgb_clr_node = outer_shadow_node.append_child("a:srgbClr"); - srgb_clr_node.append_attribute("val").set_value("000000"); - srgb_clr_node.append_child("a:alpha").append_attribute("val").set_value(35000); - auto scene3d_node = effect_style_node.append_child("a:scene3d"); - auto camera_node = scene3d_node.append_child("a:camera"); - camera_node.append_attribute("prst").set_value("orthographicFront"); - auto rot_node = camera_node.append_child("a:rot"); - rot_node.append_attribute("lat").set_value(0); - rot_node.append_attribute("lon").set_value(0); - rot_node.append_attribute("rev").set_value(0); - auto light_rig_node = scene3d_node.append_child("a:lightRig"); - light_rig_node.append_attribute("rig").set_value("threePt"); - light_rig_node.append_attribute("dir").set_value("t"); - rot_node = light_rig_node.append_child("a:rot"); - rot_node.append_attribute("lat").set_value(0); - rot_node.append_attribute("lon").set_value(0); - rot_node.append_attribute("rev").set_value(1200000); - - auto bevel_node = effect_style_node.append_child("a:sp3d").append_child("a:bevelT"); - bevel_node.append_attribute("w").set_value(63500); - bevel_node.append_attribute("h").set_value(25400); - - auto bg_fill_style_list_node = format_scheme_node.append_child("a:bgFillStyleLst"); - - bg_fill_style_list_node.append_child("a:solidFill").append_child("a:schemeClr").append_attribute("val").set_value("phClr"); - - grad_fill_node = bg_fill_style_list_node.append_child("a:gradFill"); - grad_fill_node.append_attribute("rotWithShape").set_value(1); - - grad_fill_list = grad_fill_node.append_child("a:gsLst"); - gs_node = grad_fill_list.append_child("a:gs"); - gs_node.append_attribute("pos").set_value(0); - scheme_color_node = gs_node.append_child("a:schemeClr"); - scheme_color_node.append_attribute("val").set_value("phClr"); - scheme_color_node.append_child("a:tint").append_attribute("val").set_value(40000); - scheme_color_node.append_child("a:satMod").append_attribute("val").set_value(350000); - - gs_node = grad_fill_list.append_child("a:gs"); - gs_node.append_attribute("pos").set_value(40000); - scheme_color_node = gs_node.append_child("a:schemeClr"); - scheme_color_node.append_attribute("val").set_value("phClr"); - scheme_color_node.append_child("a:tint").append_attribute("val").set_value(45000); - scheme_color_node.append_child("a:shade").append_attribute("val").set_value(99000); - scheme_color_node.append_child("a:satMod").append_attribute("val").set_value(350000); - - gs_node = grad_fill_list.append_child("a:gs"); - gs_node.append_attribute("pos").set_value(100000); - scheme_color_node = gs_node.append_child("a:schemeClr"); - scheme_color_node.append_attribute("val").set_value("phClr"); - scheme_color_node.append_child("a:shade").append_attribute("val").set_value(20000); - scheme_color_node.append_child("a:satMod").append_attribute("val").set_value(255000); - - auto path_node = grad_fill_node.append_child("a:path"); - path_node.append_attribute("path").set_value("circle"); - auto fill_to_rect_node = path_node.append_child("a:fillToRect"); - fill_to_rect_node.append_attribute("l").set_value(50000); - fill_to_rect_node.append_attribute("t").set_value(-80000); - fill_to_rect_node.append_attribute("r").set_value(50000); - fill_to_rect_node.append_attribute("b").set_value(180000); - - grad_fill_node = bg_fill_style_list_node.append_child("a:gradFill"); - grad_fill_node.append_attribute("rotWithShape").set_value(1); - - grad_fill_list = grad_fill_node.append_child("a:gsLst"); - gs_node = grad_fill_list.append_child("a:gs"); - gs_node.append_attribute("pos").set_value(0); - scheme_color_node = gs_node.append_child("a:schemeClr"); - scheme_color_node.append_attribute("val").set_value("phClr"); - scheme_color_node.append_child("a:tint").append_attribute("val").set_value(80000); - scheme_color_node.append_child("a:satMod").append_attribute("val").set_value(300000); - - gs_node = grad_fill_list.append_child("a:gs"); - gs_node.append_attribute("pos").set_value(100000); - scheme_color_node = gs_node.append_child("a:schemeClr"); - scheme_color_node.append_attribute("val").set_value("phClr"); - scheme_color_node.append_child("a:shade").append_attribute("val").set_value(30000); - scheme_color_node.append_child("a:satMod").append_attribute("val").set_value(200000); - - path_node = grad_fill_node.append_child("a:path"); - path_node.append_attribute("path").set_value("circle"); - fill_to_rect_node = path_node.append_child("a:fillToRect"); - fill_to_rect_node.append_attribute("l").set_value(50000); - fill_to_rect_node.append_attribute("t").set_value(50000); - fill_to_rect_node.append_attribute("r").set_value(50000); - fill_to_rect_node.append_attribute("b").set_value(50000); - - theme_node.append_child("a:objectDefaults"); - theme_node.append_child("a:extraClrSchemeLst"); - - std::stringstream ss; - doc.print(ss); - return ss.str(); -} - -std::string write_properties_app(const workbook &wb) -{ - pugi::xml_document doc; - auto root_node = doc.append_child("Properties"); - root_node.append_attribute("xmlns").set_value("http://schemas.openxmlformats.org/officeDocument/2006/extended-properties"); - root_node.append_attribute("xmlns:vt").set_value("http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"); - - root_node.append_child("Application").text().set("Microsoft Excel"); - root_node.append_child("DocSecurity").text().set("0"); - root_node.append_child("ScaleCrop").text().set("false"); - root_node.append_child("Company"); - root_node.append_child("LinksUpToDate").text().set("false"); - root_node.append_child("SharedDoc").text().set("false"); - root_node.append_child("HyperlinksChanged").text().set("false"); - root_node.append_child("AppVersion").text().set("12.0000"); - - auto heading_pairs_node = root_node.append_child("HeadingPairs"); - auto heading_pairs_vector_node = heading_pairs_node.append_child("vt:vector"); - heading_pairs_vector_node.append_attribute("baseType").set_value("variant"); - heading_pairs_vector_node.append_attribute("size").set_value("2"); - heading_pairs_vector_node.append_child("vt:variant").append_child("vt:lpstr").text().set("Worksheets"); - heading_pairs_vector_node.append_child("vt:variant").append_child("vt:i4").text().set(std::to_string(wb.get_sheet_names().size()).c_str()); - - auto titles_of_parts_node = root_node.append_child("TitlesOfParts"); - auto titles_of_parts_vector_node = titles_of_parts_node.append_child("vt:vector"); - titles_of_parts_vector_node.append_attribute("baseType").set_value("lpstr"); - titles_of_parts_vector_node.append_attribute("size").set_value(std::to_string(wb.get_sheet_names().size()).c_str()); - - for(auto ws : wb) - { - titles_of_parts_vector_node.append_child("vt:lpstr").text().set(ws.get_title().c_str()); - } - - std::stringstream ss; - doc.save(ss); - - return ss.str(); -} - -std::string write_root_rels(const workbook &) -{ - std::vector relationships; - - relationships.push_back(relationship(relationship::type::extended_properties, "rId3", "docProps/app.xml")); - relationships.push_back(relationship(relationship::type::core_properties, "rId2", "docProps/core.xml")); - relationships.push_back(relationship(relationship::type::office_document, "rId1", "xl/workbook.xml")); - - return write_relationships(relationships, ""); -} - -std::string write_workbook(const workbook &wb) +xml_document workbook_serializer::write_workbook() const { std::size_t num_visible = 0; - for(auto ws : wb) + for(auto ws : wb_) { if(ws.get_page_setup().get_sheet_state() == xlnt::page_setup::sheet_state::visible) { @@ -675,41 +261,44 @@ std::string write_workbook(const workbook &wb) if(num_visible == 0) { - throw value_error(); + throw xlnt::value_error(); } - pugi::xml_document doc; - auto root_node = doc.append_child("workbook"); - root_node.append_attribute("xmlns").set_value("http://schemas.openxmlformats.org/spreadsheetml/2006/main"); - root_node.append_attribute("xmlns:r").set_value("http://schemas.openxmlformats.org/officeDocument/2006/relationships"); + xml_document xml; - auto file_version_node = root_node.append_child("fileVersion"); - file_version_node.append_attribute("appName").set_value("xl"); - file_version_node.append_attribute("lastEdited").set_value("4"); - file_version_node.append_attribute("lowestEdited").set_value("4"); - file_version_node.append_attribute("rupBuild").set_value("4505"); + xml.add_namespace("xmlns", "http://schemas.openxmlformats.org/spreadsheetml/2006/main"); + xml.add_namespace("xmlns:r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships"); - auto workbook_pr_node = root_node.append_child("workbookPr"); - workbook_pr_node.append_attribute("codeName").set_value("ThisWorkbook"); - workbook_pr_node.append_attribute("defaultThemeVersion").set_value("124226"); - workbook_pr_node.append_attribute("date1904").set_value(wb.get_properties().excel_base_date == calendar::mac_1904 ? 1 : 0); + auto &root_node = xml.root(); + root_node.set_name("workbook"); - auto book_views_node = root_node.append_child("bookViews"); - auto workbook_view_node = book_views_node.append_child("workbookView"); - workbook_view_node.append_attribute("activeTab").set_value("0"); - workbook_view_node.append_attribute("autoFilterDateGrouping").set_value("1"); - workbook_view_node.append_attribute("firstSheet").set_value("0"); - workbook_view_node.append_attribute("minimized").set_value("0"); - workbook_view_node.append_attribute("showHorizontalScroll").set_value("1"); - workbook_view_node.append_attribute("showSheetTabs").set_value("1"); - workbook_view_node.append_attribute("showVerticalScroll").set_value("1"); - workbook_view_node.append_attribute("tabRatio").set_value("600"); - workbook_view_node.append_attribute("visibility").set_value("visible"); + 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 sheets_node = root_node.append_child("sheets"); - auto defined_names_node = root_node.append_child("definedNames"); + 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"); - for(auto relationship : wb.get_relationships()) + auto book_views_node = root_node.add_child("bookViews"); + auto workbook_view_node = book_views_node.add_child("workbookView"); + workbook_view_node.add_attribute("activeTab", "0"); + workbook_view_node.add_attribute("autoFilterDateGrouping", "1"); + workbook_view_node.add_attribute("firstSheet", "0"); + workbook_view_node.add_attribute("minimized", "0"); + workbook_view_node.add_attribute("showHorizontalScroll", "1"); + workbook_view_node.add_attribute("showSheetTabs", "1"); + workbook_view_node.add_attribute("showVerticalScroll", "1"); + workbook_view_node.add_attribute("tabRatio", "600"); + workbook_view_node.add_attribute("visibility", "visible"); + + auto sheets_node = root_node.add_child("sheets"); + auto defined_names_node = root_node.add_child("definedNames"); + + for(auto relationship : wb_.get_relationships()) { if(relationship.get_type() == relationship::type::worksheet) { @@ -723,55 +312,41 @@ std::string write_workbook(const workbook &wb) 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 = wb_.get_sheet_by_index(sheet_index); - auto sheet_node = sheets_node.append_child("sheet"); - sheet_node.append_attribute("name").set_value(ws.get_title().c_str()); - sheet_node.append_attribute("r:id").set_value(relationship.get_id().c_str()); - sheet_node.append_attribute("sheetId").set_value(std::to_string(sheet_index + 1).c_str()); + 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)); if(ws.has_auto_filter()) { - auto defined_name_node = defined_names_node.append_child("definedName"); - defined_name_node.append_attribute("name").set_value("_xlnm._FilterDatabase"); - defined_name_node.append_attribute("hidden").set_value(1); - defined_name_node.append_attribute("localSheetId").set_value(0); + 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"); std::string name = "'" + ws.get_title() + "'!" + range_reference::make_absolute(ws.get_auto_filter()).to_string(); - defined_name_node.text().set(name.c_str()); + defined_name_node.set_text(name); } } } - auto calc_pr_node = root_node.append_child("calcPr"); - calc_pr_node.append_attribute("calcId").set_value("124519"); - calc_pr_node.append_attribute("calcMode").set_value("auto"); - calc_pr_node.append_attribute("fullCalcOnLoad").set_value("1"); + auto calc_pr_node = root_node.add_child("calcPr"); + calc_pr_node.add_attribute("calcId", "124519"); + calc_pr_node.add_attribute("calcMode", "auto"); + calc_pr_node.add_attribute("fullCalcOnLoad", "1"); - std::stringstream ss; - doc.save(ss); - - return ss.str(); -} - -std::string workbook_serializer::write_workbook_rels(const workbook &wb) -{ - return write_relationships(wb.get_relationships(), "xl/"); + return xml; } -std::string workbook_serializer::write_defined_names(const xlnt::workbook &wb) +bool workbook_serializer::write_named_ranges(xlnt::xml_node &named_ranges_node) { - pugi::xml_document doc; - auto names = doc.root().append_child("names"); - - for(auto named_range : wb.get_named_ranges()) + for(auto &named_range : wb_.get_named_ranges()) { - names.append_child(named_range.get_name().c_str()); + named_ranges_node.add_child(named_range.get_name()); } - - std::ostringstream stream; - doc.save(stream); - - return stream.str(); + + return true; } } // namespace xlnt diff --git a/source/s11n/worksheet_serializer.cpp b/source/s11n/worksheet_serializer.cpp index 15488d13..3abdaf60 100644 --- a/source/s11n/worksheet_serializer.cpp +++ b/source/s11n/worksheet_serializer.cpp @@ -1,8 +1,11 @@ +#include + #include #include #include #include #include +#include #include #include @@ -18,10 +21,193 @@ bool is_integral(long double d) } // namepsace namespace xlnt { - -bool worksheet_serializer::write_worksheet(const worksheet ws, const std::vector &string_table, xml_document &xml) + +bool worksheet_serializer::read_worksheet(const xml_document &xml, const std::vector &string_table, const relationship &rel) { - ws.get_cell("A1"); + auto &root_node = xml.root(); + + auto &dimension_node = root_node.get_child("dimension"); + std::string dimension = dimension_node.get_attribute("ref"); + auto full_range = xlnt::range_reference(dimension); + auto sheet_data_node = root_node.get_child("sheetData"); + + if(root_node.has_child("mergeCells")) + { + auto merge_cells_node = root_node.get_child("mergeCells"); + auto count = std::stoull(merge_cells_node.get_attribute("count")); + + for(auto merge_cell_node : merge_cells_node.get_children()) + { + if(merge_cell_node.get_name() != "mergeCell") + { + continue; + } + + sheet_.merge_cells(merge_cell_node.get_attribute("ref")); + count--; + } + + if(count != 0) + { + throw std::runtime_error("mismatch between count and actual number of merged cells"); + } + } + + for(auto row_node : sheet_data_node.get_children()) + { + if(row_node.get_name() != "row") + { + continue; + } + + auto row_index = static_cast(std::stoull(row_node.get_attribute("r"))); + + if(row_node.get_attribute("ht") != nullptr) + { + sheet_.get_row_properties(row_index).height = std::stold(row_node.get_attribute("ht")); + } + + std::string span_string = row_node.get_attribute("spans"); + auto colon_index = span_string.find(':'); + + column_t min_column = 0; + column_t max_column = 0; + + if(colon_index != std::string::npos) + { + min_column = static_cast(std::stoll(span_string.substr(0, colon_index))); + max_column = static_cast(std::stoll(span_string.substr(colon_index + 1))); + } + else + { + min_column = static_cast(full_range.get_top_left().get_column_index()); + max_column = static_cast(full_range.get_bottom_right().get_column_index()); + } + + for(column_t i = min_column; i <= max_column; i++) + { + std::string address = xlnt::cell_reference::column_string_from_index(i) + std::to_string(row_index); + + xml_node cell_node; + bool cell_found = false; + + for(auto &check_cell_node : row_node.get_children()) + { + if(check_cell_node.get_name() != "c") + { + continue; + } + + if(check_cell_node.has_attribute("r") && check_cell_node.get_attribute("r") == address) + { + cell_node = check_cell_node; + cell_found = true; + break; + } + } + + if(cell_found) + { + 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; + std::string type = has_type ? cell_node.get_attribute("t") : ""; + + bool has_style = cell_node.get_attribute("s") != nullptr; + int style_id = has_style ? std::stoull(cell_node.get_attribute("s")) : 0; + + bool has_formula = cell_node.has_child("f"); + bool has_shared_formula = has_formula && cell_node.get_child("f").has_attribute("t") && cell_node.get_child("f").get_attribute("t") == "shared"; + + auto cell = sheet_.get_cell(address); + + if(has_formula && !has_shared_formula && !sheet_.get_parent().get_data_only()) + { + std::string formula = cell_node.get_child("f").get_text(); + cell.set_formula(formula); + } + + if(has_type && type == "inlineStr") // inline string + { + std::string inline_string = cell_node.get_child("is").get_child("t").get_text(); + cell.set_value(inline_string); + } + 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); + cell.set_value(shared_string); + } + else if(has_type && type == "b") // boolean + { + cell.set_value(value_string != "0"); + } + else if(has_type && type == "str") + { + cell.set_value(value_string); + } + else if(has_value && !value_string.empty()) + { + if(!value_string.empty() && value_string[0] == '#') + { + cell.set_error(value_string); + } + else + { + cell.set_value(std::stold(value_string)); + } + } + + if(has_style) + { + cell.set_style_id(style_id); + } + } + } + } + + auto &cols_node = root_node.get_child("cols"); + + for(auto col_node : cols_node.get_children()) + { + if(col_node.get_name() != "col") + { + continue; + } + + auto min = static_cast(std::stoull(col_node.get_attribute("min"))); + auto max = static_cast(std::stoull(col_node.get_attribute("max"))); + auto width = std::stold(col_node.get_attribute("width")); + auto column_style = std::stoull(col_node.get_attribute("style")); + bool custom = col_node.get_attribute("customWidth") == "1"; + + for(auto column = min; column <= max; column++) + { + if(!sheet_.has_column_properties(column)) + { + sheet_.add_column_properties(column, column_properties()); + } + + sheet_.get_column_properties(min).width = width; + sheet_.get_column_properties(min).style = column_style; + sheet_.get_column_properties(min).custom = custom; + } + } + + if(root_node.has_child("autoFilter")) + { + auto &auto_filter_node = root_node.get_child("autoFilter"); + xlnt::range_reference ref(auto_filter_node.get_attribute("ref")); + sheet_.auto_filter(ref); + } + + return true; +} + +bool worksheet_serializer::write_worksheet(const std::vector &string_table, xml_document &xml) +{ + sheet_.get_cell("A1"); xml.add_namespace("", constants::Namespaces.at("spreadsheetml")); xml.add_namespace("r", constants::Namespaces.at("r")); @@ -31,10 +217,10 @@ bool worksheet_serializer::write_worksheet(const worksheet ws, const std::vector auto &sheet_pr_node = root_node.add_child("sheetPr"); - if(!ws.get_page_setup().is_default()) + if(!sheet_.get_page_setup().is_default()) { auto &page_set_up_pr_node = sheet_pr_node.add_child("pageSetUpPr"); - page_set_up_pr_node.add_attribute("fitToPage", ws.get_page_setup().fit_to_page() ? "1" : "0"); + 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"); @@ -43,7 +229,7 @@ bool worksheet_serializer::write_worksheet(const worksheet ws, const std::vector outline_pr_node.add_attribute("summaryRight", "1"); auto &dimension_node = root_node.add_child("dimension"); - dimension_node.add_attribute("ref", ws.calculate_dimension().to_string()); + 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"); @@ -51,23 +237,23 @@ bool worksheet_serializer::write_worksheet(const worksheet ws, const std::vector std::string active_pane = "bottomRight"; - if(ws.has_frozen_panes()) + if(sheet_.has_frozen_panes()) { auto pane_node = sheet_view_node.add_child("pane"); - if(ws.get_frozen_panes().get_column_index() > 1) + if(sheet_.get_frozen_panes().get_column_index() > 1) { - pane_node.add_attribute("xSplit", std::to_string(ws.get_frozen_panes().get_column_index() - 1)); + pane_node.add_attribute("xSplit", std::to_string(sheet_.get_frozen_panes().get_column_index() - 1)); active_pane = "topRight"; } - if(ws.get_frozen_panes().get_row() > 1) + if(sheet_.get_frozen_panes().get_row() > 1) { - pane_node.add_attribute("ySplit", std::to_string(ws.get_frozen_panes().get_row() - 1)); + pane_node.add_attribute("ySplit", std::to_string(sheet_.get_frozen_panes().get_row() - 1)); active_pane = "bottomLeft"; } - if(ws.get_frozen_panes().get_row() > 1 && ws.get_frozen_panes().get_column_index() > 1) + if(sheet_.get_frozen_panes().get_row() > 1 && sheet_.get_frozen_panes().get_column_index() > 1) { auto top_right_node = sheet_view_node.add_child("selection"); top_right_node.add_attribute("pane", "topRight"); @@ -76,24 +262,24 @@ bool worksheet_serializer::write_worksheet(const worksheet ws, const std::vector active_pane = "bottomRight"; } - pane_node.add_attribute("topLeftCell", ws.get_frozen_panes().to_string()); + pane_node.add_attribute("topLeftCell", sheet_.get_frozen_panes().to_string()); pane_node.add_attribute("activePane", active_pane); pane_node.add_attribute("state", "frozen"); } auto selection_node = sheet_view_node.add_child("selection"); - if(ws.has_frozen_panes()) + if(sheet_.has_frozen_panes()) { - if(ws.get_frozen_panes().get_row() > 1 && ws.get_frozen_panes().get_column_index() > 1) + if(sheet_.get_frozen_panes().get_row() > 1 && sheet_.get_frozen_panes().get_column_index() > 1) { selection_node.add_attribute("pane", "bottomRight"); } - else if(ws.get_frozen_panes().get_row() > 1) + else if(sheet_.get_frozen_panes().get_row() > 1) { selection_node.add_attribute("pane", "bottomLeft"); } - else if(ws.get_frozen_panes().get_column_index() > 1) + else if(sheet_.get_frozen_panes().get_column_index() > 1) { selection_node.add_attribute("pane", "topRight"); } @@ -109,9 +295,9 @@ bool worksheet_serializer::write_worksheet(const worksheet ws, const std::vector bool has_column_properties = false; - for(auto column = ws.get_lowest_column(); column <= ws.get_highest_column(); column++) + for(auto column = sheet_.get_lowest_column(); column <= sheet_.get_highest_column(); column++) { - if(ws.has_column_properties(column)) + if(sheet_.has_column_properties(column)) { has_column_properties = true; break; @@ -122,9 +308,9 @@ bool worksheet_serializer::write_worksheet(const worksheet ws, const std::vector { auto cols_node = root_node.add_child("cols"); - for(auto column = ws.get_lowest_column(); column <= ws.get_highest_column(); column++) + for(auto column = sheet_.get_lowest_column(); column <= sheet_.get_highest_column(); column++) { - const auto &props = ws.get_column_properties(column); + const auto &props = sheet_.get_column_properties(column); auto col_node = cols_node.add_child("col"); @@ -140,7 +326,7 @@ bool worksheet_serializer::write_worksheet(const worksheet ws, const std::vector auto sheet_data_node = root_node.add_child("sheetData"); - for(auto row : ws.rows()) + for(auto row : sheet_.rows()) { row_t min = static_cast(row.num_cells()); row_t max = 0; @@ -167,10 +353,10 @@ bool worksheet_serializer::write_worksheet(const worksheet ws, const std::vector row_node.add_attribute("r", std::to_string(row.front().get_row())); row_node.add_attribute("spans", (std::to_string(min) + ":" + std::to_string(max))); - if(ws.has_row_properties(row.front().get_row())) + if(sheet_.has_row_properties(row.front().get_row())) { row_node.add_attribute("customHeight", "1"); - auto height = ws.get_row_properties(row.front().get_row()).height; + auto height = sheet_.get_row_properties(row.front().get_row()).height; if(height == std::floor(height)) { @@ -228,14 +414,14 @@ bool worksheet_serializer::write_worksheet(const worksheet ws, const std::vector { cell_node.add_attribute("t", "inlineStr"); auto inline_string_node = cell_node.add_child("is"); - inline_string_node.add_child("t").text().set(cell.get_value()); + inline_string_node.add_child("t").set_text(cell.get_value()); } } else { cell_node.add_attribute("t", "s"); auto value_node = cell_node.add_child("v"); - value_node.text().set(match_index); + value_node.set_text(std::to_string(match_index)); } } else @@ -246,22 +432,23 @@ bool worksheet_serializer::write_worksheet(const worksheet ws, const std::vector { cell_node.add_attribute("t", "b"); auto value_node = cell_node.add_child("v"); - value_node.text().set(cell.get_value() ? 1 : 0); + value_node.set_text(cell.get_value() ? "1" : "0"); } else if(cell.get_data_type() == cell::type::numeric) { if(cell.has_formula()) { - cell_node.add_child("f").text().set(cell.get_formula()); - cell_node.add_child("v").text().set(cell.to_string()); + cell_node.add_child("f").set_text(cell.get_formula()); + cell_node.add_child("v").set_text(cell.to_string()); continue; } cell_node.add_attribute("t", "n"); auto value_node = cell_node.add_child("v"); + if(is_integral(cell.get_value())) { - value_node.text().set(cell.get_value()); + value_node.set_text(std::to_string(cell.get_value())); } else { @@ -269,50 +456,49 @@ bool worksheet_serializer::write_worksheet(const worksheet ws, const std::vector ss.precision(20); ss << cell.get_value(); ss.str(); - value_node.text().set(ss.str()); + value_node.set_text(ss.str()); } } } else if(cell.has_formula()) { - cell_node.add_child("f").text().set(cell.get_formula()); + cell_node.add_child("f").set_text(cell.get_formula()); cell_node.add_child("v"); continue; } } - //if(cell.has_style()) + if(cell.has_style()) { - auto style_id = cell.get_style_id(); - cell_node.add_attribute("s", (int)style_id); + cell_node.add_attribute("s", std::to_string(cell.get_style_id())); } } } } - if(ws.has_auto_filter()) + if(sheet_.has_auto_filter()) { auto auto_filter_node = root_node.add_child("autoFilter"); - auto_filter_node.add_attribute("ref", ws.get_auto_filter().to_string()); + auto_filter_node.add_attribute("ref", sheet_.get_auto_filter().to_string()); } - if(!ws.get_merged_ranges().empty()) + if(!sheet_.get_merged_ranges().empty()) { auto merge_cells_node = root_node.add_child("mergeCells"); - merge_cells_node.add_attribute("count", (unsigned int)ws.get_merged_ranges().size()); + merge_cells_node.add_attribute("count", std::to_string(sheet_.get_merged_ranges().size())); - for(auto merged_range : ws.get_merged_ranges()) + for(auto merged_range : sheet_.get_merged_ranges()) { auto merge_cell_node = merge_cells_node.add_child("mergeCell"); merge_cell_node.add_attribute("ref", merged_range.to_string()); } } - if(!ws.get_relationships().empty()) + if(!sheet_.get_relationships().empty()) { auto hyperlinks_node = root_node.add_child("hyperlinks"); - for(auto relationship : ws.get_relationships()) + for(const auto &relationship : sheet_.get_relationships()) { auto hyperlink_node = hyperlinks_node.add_child("hyperlink"); hyperlink_node.add_attribute("display", relationship.get_target_uri()); @@ -321,48 +507,45 @@ bool worksheet_serializer::write_worksheet(const worksheet ws, const std::vector } } - if(!ws.get_page_setup().is_default()) + if(!sheet_.get_page_setup().is_default()) { auto print_options_node = root_node.add_child("printOptions"); - print_options_node.add_attribute("horizontalCentered", ws.get_page_setup().get_horizontal_centered() ? 1 : 0); - print_options_node.add_attribute("verticalCentered", ws.get_page_setup().get_vertical_centered() ? 1 : 0); + print_options_node.add_attribute("horizontalCentered", sheet_.get_page_setup().get_horizontal_centered() ? "1" : "0"); + print_options_node.add_attribute("verticalCentered", sheet_.get_page_setup().get_vertical_centered() ? "1" : "0"); } auto page_margins_node = root_node.add_child("pageMargins"); - page_margins_node.add_attribute("left", ws.get_page_margins().get_left()); - page_margins_node.add_attribute("right", ws.get_page_margins().get_right()); - page_margins_node.add_attribute("top", ws.get_page_margins().get_top()); - page_margins_node.add_attribute("bottom", ws.get_page_margins().get_bottom()); - page_margins_node.add_attribute("header", ws.get_page_margins().get_header()); - page_margins_node.add_attribute("footer", ws.get_page_margins().get_footer()); + page_margins_node.add_attribute("left", std::to_string(sheet_.get_page_margins().get_left())); + page_margins_node.add_attribute("right", std::to_string(sheet_.get_page_margins().get_right())); + page_margins_node.add_attribute("top", std::to_string(sheet_.get_page_margins().get_top())); + page_margins_node.add_attribute("bottom", std::to_string(sheet_.get_page_margins().get_bottom())); + page_margins_node.add_attribute("header", std::to_string(sheet_.get_page_margins().get_header())); + page_margins_node.add_attribute("footer", std::to_string(sheet_.get_page_margins().get_footer())); - if(!ws.get_page_setup().is_default()) + if(!sheet_.get_page_setup().is_default()) { auto page_setup_node = root_node.add_child("pageSetup"); - std::string orientation_string = ws.get_page_setup().get_orientation() == page_setup::orientation::landscape ? "landscape" : "portrait"; + std::string orientation_string = sheet_.get_page_setup().get_orientation() == page_setup::orientation::landscape ? "landscape" : "portrait"; page_setup_node.add_attribute("orientation", orientation_string); - page_setup_node.add_attribute("paperSize", (int)ws.get_page_setup().get_paper_size()); - page_setup_node.add_attribute("fitToHeight", ws.get_page_setup().fit_to_height() ? 1 : 0); - page_setup_node.add_attribute("fitToWidth", ws.get_page_setup().fit_to_width() ? 1 : 0); + page_setup_node.add_attribute("paperSize", std::to_string(static_cast(sheet_.get_page_setup().get_paper_size()))); + page_setup_node.add_attribute("fitToHeight", sheet_.get_page_setup().fit_to_height() ? "1" : "0"); + page_setup_node.add_attribute("fitToWidth", sheet_.get_page_setup().fit_to_width() ? "1" : "0"); } - if(!ws.get_header_footer().is_default()) + if(!sheet_.get_header_footer().is_default()) { auto header_footer_node = root_node.add_child("headerFooter"); auto odd_header_node = header_footer_node.add_child("oddHeader"); std::string header_text = "&L&\"Calibri,Regular\"&K000000Left Header Text&C&\"Arial,Regular\"&6&K445566Center Header Text&R&\"Arial,Bold\"&8&K112233Right Header Text"; - odd_header_node.text().set(header_text); + odd_header_node.set_text(header_text); auto odd_footer_node = header_footer_node.add_child("oddFooter"); std::string footer_text = "&L&\"Times New Roman,Regular\"&10&K445566Left Footer Text_x000D_And &D and &T&C&\"Times New Roman,Bold\"&12&K778899Center Footer Text &Z&F on &A&R&\"Times New Roman,Italic\"&14&KAABBCCRight Footer Text &P of &N"; - odd_footer_node.text().set(footer_text); + odd_footer_node.set_text(footer_text); } - std::stringstream ss; - doc.save(ss); - - return ss.str(); + return true; } } // namespace xlnt diff --git a/source/s11n/xml_node.cpp b/source/s11n/xml_node.cpp index 06239c83..3b655917 100644 --- a/source/s11n/xml_node.cpp +++ b/source/s11n/xml_node.cpp @@ -68,13 +68,13 @@ void xml_node::add_attribute(const std::string &name, const std::string &value) bool xml_node::has_attribute(const std::string &attribute_name) const { - return std::find(attributes_.begin(), attributes_.end(), + return std::find_if(attributes_.begin(), attributes_.end(), [&](const string_pair &p) { return p.first == attribute_name; }) != attributes_.end(); } std::string xml_node::get_attribute(const std::string &attribute_name) const { - auto match = std::find(attributes_.begin(), attributes_.end(), + auto match = std::find_if(attributes_.begin(), attributes_.end(), [&](const string_pair &p) { return p.first == attribute_name; }); if(match == attributes_.end()) @@ -87,16 +87,29 @@ std::string xml_node::get_attribute(const std::string &attribute_name) const bool xml_node::has_child(const std::string &child_name) const { - return std::find(children_.begin(), children_.end(), + return std::find_if(children_.begin(), children_.end(), [&](const xml_node &n) { return n.get_name() == child_name; }) != children_.end(); } +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; +} + const xml_node &xml_node::get_child(const std::string &child_name) const { - auto match = std::find(children_.begin(), children_.end(), + auto match = std::find_if(children_.begin(), children_.end(), [&](const xml_node &n) { return n.get_name() == child_name; }); - if(match == attributes_.end()) + if(match == children_.end()) { throw std::runtime_error("child doesn't exist: " + child_name); } diff --git a/source/workbook/workbook.cpp b/source/workbook/workbook.cpp index 92a54129..eb04670c 100644 --- a/source/workbook/workbook.cpp +++ b/source/workbook/workbook.cpp @@ -12,19 +12,21 @@ #include #include #include -#include +#include #include #include #include #include #include #include +#include #include +#include #include +#include #include #include #include -#include #include "detail/cell_impl.hpp" #include "detail/include_pugixml.hpp" @@ -243,22 +245,25 @@ range workbook::get_named_range(const std::string &name) bool workbook::load(std::istream &stream) { - *this = excel_reader::load_workbook(stream); + excel_serializer serializer_(*this); + serializer_.load_stream_workbook(stream); return true; } bool workbook::load(const std::vector &data) { - *this = excel_reader::load_workbook(data); + excel_serializer serializer_(*this); + serializer_.load_virtual_workbook(data); return true; } bool workbook::load(const std::string &filename) { - *this = excel_reader::load_workbook(filename); - + excel_serializer serializer_(*this); + serializer_.load_workbook(filename); + return true; } @@ -446,13 +451,18 @@ void workbook::clear() bool workbook::save(std::vector &data) { - data = save_virtual_workbook(*this); + excel_serializer serializer(*this); + serializer.save_virtual_workbook(data); + return true; } bool workbook::save(const std::string &filename) { - return save_workbook(*this, filename); + excel_serializer serializer(*this); + serializer.save_workbook(filename); + + return true; } bool workbook::operator==(std::nullptr_t) const @@ -465,7 +475,7 @@ bool workbook::operator==(const workbook &rhs) const return d_.get() == rhs.d_.get(); } -std::vector xlnt::workbook::get_relationships() const +const std::vector &xlnt::workbook::get_relationships() const { return d_->relationships_; } @@ -570,14 +580,14 @@ void workbook::set_code_name(const std::string &/*code_name*/) } -bool workbook::has_loaded_theme() +bool workbook::has_loaded_theme() const { return false; } -std::string workbook::get_loaded_theme() +const theme &workbook::get_loaded_theme() const { - return ""; + return d_->theme_; } std::vector workbook::get_named_ranges() const @@ -847,4 +857,16 @@ const manifest &workbook::get_manifest() const return d_->manifest_; } +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")); + } + + return d_->root_relationships_; +} + } // namespace xlnt diff --git a/source/worksheet/worksheet.cpp b/source/worksheet/worksheet.cpp index 7b3b4b2a..27a7ee05 100644 --- a/source/worksheet/worksheet.cpp +++ b/source/worksheet/worksheet.cpp @@ -330,7 +330,7 @@ const range worksheet::get_squared_range(column_t min_col, row_t min_row, column return get_range(reference); } -std::vector worksheet::get_relationships() +const std::vector &worksheet::get_relationships() const { return d_->relationships_; } diff --git a/source/worksheet/worksheet_reader.cpp b/source/worksheet/worksheet_reader.cpp index d201bf74..22ee4cde 100644 --- a/source/worksheet/worksheet_reader.cpp +++ b/source/worksheet/worksheet_reader.cpp @@ -1,7 +1,6 @@ #include #include #include -#include #include #include #include @@ -14,158 +13,4 @@ namespace { } // namespace namespace xlnt { - -void read_worksheet(worksheet ws, zip_file &archive, const relationship &rel, const std::vector &string_table) -{ - pugi::xml_document doc; - doc.load(archive.read(rel.get_target_uri()).c_str()); - - auto root_node = doc.child("worksheet"); - - auto dimension_node = root_node.child("dimension"); - std::string dimension = dimension_node.attribute("ref").as_string(); - auto full_range = xlnt::range_reference(dimension); - auto sheet_data_node = root_node.child("sheetData"); - auto merge_cells_node = root_node.child("mergeCells"); - - if(merge_cells_node != nullptr) - { - int count = merge_cells_node.attribute("count").as_int(); - - for(auto merge_cell_node : merge_cells_node.children("mergeCell")) - { - ws.merge_cells(merge_cell_node.attribute("ref").as_string()); - count--; - } - - if(count != 0) - { - throw std::runtime_error("mismatch between count and actual number of merged cells"); - } - } - - for(auto row_node : sheet_data_node.children("row")) - { - auto row_index = static_cast(row_node.attribute("r").as_uint()); - - if(row_node.attribute("ht") != nullptr) - { - ws.get_row_properties(row_index).height = row_node.attribute("ht").as_double(); - } - - std::string span_string = row_node.attribute("spans").as_string(); - auto colon_index = span_string.find(':'); - - column_t min_column = 0; - column_t max_column = 0; - - if(colon_index != std::string::npos) - { - min_column = static_cast(std::stoll(span_string.substr(0, colon_index))); - max_column = static_cast(std::stoll(span_string.substr(colon_index + 1))); - } - else - { - min_column = static_cast(full_range.get_top_left().get_column_index()); - max_column = static_cast(full_range.get_bottom_right().get_column_index()); - } - - for(column_t i = min_column; i <= max_column; i++) - { - std::string address = xlnt::cell_reference::column_string_from_index(i) + std::to_string(row_index); - auto cell_node = row_node.find_child_by_attribute("c", "r", address.c_str()); - - if(cell_node != nullptr) - { - bool has_value = cell_node.child("v") != nullptr; - std::string value_string = has_value ? cell_node.child("v").text().as_string() : ""; - - bool has_type = cell_node.attribute("t") != nullptr; - std::string type = has_type ? cell_node.attribute("t").as_string() : ""; - - bool has_style = cell_node.attribute("s") != nullptr; - int style_id = has_style ? cell_node.attribute("s").as_int() : 0; - - bool has_formula = cell_node.child("f") != nullptr; - bool has_shared_formula = has_formula && cell_node.child("f").attribute("t") != nullptr && std::string(cell_node.child("f").attribute("t").as_string()) == "shared"; - - auto cell = ws.get_cell(address); - - if(has_formula && !has_shared_formula && !ws.get_parent().get_data_only()) - { - std::string formula = cell_node.child("f").text().as_string(); - cell.set_formula(formula); - } - - if(has_type && type == "inlineStr") // inline string - { - std::string inline_string = cell_node.child("is").child("t").text().as_string(); - cell.set_value(inline_string); - } - 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); - cell.set_value(shared_string); - } - else if(has_type && type == "b") // boolean - { - cell.set_value(value_string != "0"); - } - else if(has_type && type == "str") - { - cell.set_value(value_string); - } - else if(has_value && !value_string.empty()) - { - if(!value_string.empty() && value_string[0] == '#') - { - cell.set_error(value_string); - } - else - { - cell.set_value(std::stold(value_string)); - } - } - - if(has_style) - { - cell.set_style_id(style_id); - } - } - } - } - - auto cols_node = root_node.child("cols"); - - for(auto col_node : cols_node.children("col")) - { - column_t min = col_node.attribute("min").as_uint(); - column_t max = col_node.attribute("max").as_uint(); - double width = col_node.attribute("width").as_double(); - std::size_t column_style = col_node.attribute("style").as_ullong(); - bool custom = col_node.attribute("customWidth").as_bool(); - - for(auto column = min; column <= max; column++) - { - if(!ws.has_column_properties(column)) - { - ws.add_column_properties(column, column_properties()); - } - - ws.get_column_properties(min).width = width; - ws.get_column_properties(min).style = column_style; - ws.get_column_properties(min).custom = custom; - } - } - - auto auto_filter_node = root_node.child("autoFilter"); - - if(auto_filter_node != nullptr) - { - xlnt::range_reference ref(auto_filter_node.attribute("ref").as_string()); - ws.auto_filter(ref); - } -} - } // namespace xlnt diff --git a/tests/test_cell.hpp b/tests/test_cell.hpp index 60f8a78c..52b0e931 100644 --- a/tests/test_cell.hpp +++ b/tests/test_cell.hpp @@ -19,11 +19,6 @@ #include #include -#include -#include - -#include - class test_cell : public CxxTest::TestSuite { private: @@ -37,7 +32,8 @@ public: void test_debug() { - auto wb = xlnt::excel_reader::load_workbook("/Users/thomas/Development/xlnt/samples/formatting.xlsx"); + xlnt::workbook wb; + wb.load("/Users/thomas/Development/xlnt/samples/formatting.xlsx"); wb.save("/Users/thomas/Development/xlnt/samples/formatting-rt.xlsx"); wb.save("/Users/thomas/Development/xlnt/samples/formatting-rt.zip"); } @@ -94,7 +90,6 @@ public: TS_ASSERT(cell.get_row() == 1); TS_ASSERT(cell.get_reference() == "A1"); TS_ASSERT(!cell.has_value()); - TS_ASSERT(cell.get_xf_index() == 0); TS_ASSERT(!cell.has_comment()); } @@ -179,7 +174,7 @@ public: auto ws = wb_guess_types.create_sheet(); auto cell = ws.get_cell(xlnt::cell_reference(1, 1)); - for(auto error_code : xlnt::cell::ErrorCodes) + for(auto error_code : xlnt::cell::error_codes()) { cell.set_value(error_code.first); TS_ASSERT(cell.get_data_type() == xlnt::cell::type::error); diff --git a/tests/test_props.hpp b/tests/test_props.hpp index fbd97e5e..e747c124 100644 --- a/tests/test_props.hpp +++ b/tests/test_props.hpp @@ -3,8 +3,7 @@ #include #include -#include -#include +#include #include "helpers/path_helper.hpp" #include "helpers/helper.hpp" @@ -16,7 +15,9 @@ public: { xlnt::zip_file archive(PathHelper::GetDataDirectory() + "/genuine/empty.xlsx"); auto content = archive.read("docProps/core.xml"); - auto prop = xlnt::read_properties_core(content); + xlnt::workbook wb; + xlnt::workbook_serializer serializer(wb); + auto prop = serializer.read_properties_core(content); TS_ASSERT_EQUALS(prop.creator, "*.*"); TS_ASSERT_EQUALS(prop.last_modified_by, "Charlie Clark"); TS_ASSERT_EQUALS(prop.created, xlnt::datetime(2010, 4, 9, 20, 43, 12)); diff --git a/tests/test_read.hpp b/tests/test_read.hpp index fcb00f27..56a2152a 100644 --- a/tests/test_read.hpp +++ b/tests/test_read.hpp @@ -5,10 +5,6 @@ #include -#include -#include -#include - #include "helpers/path_helper.hpp" class test_read : public CxxTest::TestSuite