diff --git a/include/xlnt/packaging/app_properties.hpp b/include/xlnt/packaging/app_properties.hpp new file mode 100644 index 00000000..cf3e2383 --- /dev/null +++ b/include/xlnt/packaging/app_properties.hpp @@ -0,0 +1,48 @@ +// Copyright (c) 2014-2016 Thomas Fussell +// Copyright (c) 2010-2015 openpyxl +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, WRISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE +// +// @license: http://www.opensource.org/licenses/mit-license.php +// @author: see AUTHORS file +#pragma once + +#include + +#include + +namespace xlnt { + +/// +/// High-level properties of the OOXML document regarding the app that created it. +/// +class XLNT_CLASS app_properties +{ +public: + std::string application = "libxlnt"; + int doc_security = 0; + bool scale_crop = false; + std::string company = ""; + bool links_up_to_date = false; + bool shared_doc = false; + bool hyperlinks_changed = false; + std::string app_version = "0.9"; +}; + +} // namespace xlnt diff --git a/include/xlnt/workbook/workbook.hpp b/include/xlnt/workbook/workbook.hpp index 9ba429ca..ad25bb55 100644 --- a/include/xlnt/workbook/workbook.hpp +++ b/include/xlnt/workbook/workbook.hpp @@ -35,6 +35,7 @@ namespace xlnt { class alignment; +class app_properties; class border; class color; class const_worksheet_iterator; @@ -136,6 +137,9 @@ public: document_properties &get_properties(); const document_properties &get_properties() const; + app_properties &get_app_properties(); + const app_properties &get_app_properties() const; + // named ranges std::vector get_named_ranges() const; void create_named_range(const std::string &name, worksheet worksheet, const range_reference &reference); diff --git a/source/detail/workbook_impl.hpp b/source/detail/workbook_impl.hpp index 5f732f79..38b9657a 100644 --- a/source/detail/workbook_impl.hpp +++ b/source/detail/workbook_impl.hpp @@ -40,6 +40,7 @@ struct workbook_impl drawings_(other.drawings_), shared_strings_(other.shared_strings_), properties_(other.properties_), + app_properties_(other.app_properties_), guess_types_(other.guess_types_), data_only_(other.data_only_), read_only_(other.read_only_), @@ -68,6 +69,7 @@ struct workbook_impl shared_strings_.clear(); std::copy(other.shared_strings_.begin(), other.shared_strings_.end(), std::back_inserter(shared_strings_)); properties_ = other.properties_; + app_properties_ = other.app_properties_; guess_types_ = other.guess_types_; data_only_ = other.data_only_; read_only_ = other.read_only_; @@ -90,6 +92,7 @@ struct workbook_impl std::vector shared_strings_; document_properties properties_; + app_properties app_properties_; bool guess_types_; bool data_only_; diff --git a/source/packaging/tests/test_core.hpp b/source/packaging/tests/test_core.hpp index 13963824..ca56b594 100644 --- a/source/packaging/tests/test_core.hpp +++ b/source/packaging/tests/test_core.hpp @@ -3,13 +3,14 @@ #include #include +#include #include #include #include #include -class test_props : public CxxTest::TestSuite +class test_core : public CxxTest::TestSuite { public: void test_read_properties_core() @@ -86,6 +87,8 @@ public: void test_write_properties_app() { xlnt::workbook wb; + wb.get_app_properties().application = "Microsoft Excel"; + wb.get_app_properties().app_version = "12.0000"; wb.create_sheet(); wb.create_sheet(); xlnt::workbook_serializer serializer(wb); diff --git a/source/serialization/excel_serializer.cpp b/source/serialization/excel_serializer.cpp index c701dbaf..320742ce 100644 --- a/source/serialization/excel_serializer.cpp +++ b/source/serialization/excel_serializer.cpp @@ -94,6 +94,14 @@ bool load_workbook(xlnt::zip_file &archive, bool guess_types, bool data_only, xl workbook_serializer_.read_properties_core(core_properties_xml); } + if(archive.has_file(xlnt::constants::ArcApp())) + { + xlnt::workbook_serializer workbook_serializer_(wb); + xlnt::xml_document app_properties_xml; + app_properties_xml.from_string(archive.read(xlnt::constants::ArcApp())); + workbook_serializer_.read_properties_app(app_properties_xml); + } + xlnt::relationship_serializer relationship_serializer_(archive); auto workbook_relationships = relationship_serializer_.read_relationships(xlnt::constants::ArcWorkbook()); diff --git a/source/serialization/style_serializer.cpp b/source/serialization/style_serializer.cpp index f79bbd8a..af20ef2c 100644 --- a/source/serialization/style_serializer.cpp +++ b/source/serialization/style_serializer.cpp @@ -34,6 +34,24 @@ namespace { +bool equals_case_insensitive(const std::string &left, const std::string &right) +{ + if (left.size() != right.size()) + { + return false; + } + + for (std::size_t i = 0; i < left.size(); i++) + { + if (std::tolower(left[i]) != std::tolower(right[i])) + { + return false; + } + } + + return true; +} + bool is_true(const std::string &bool_string) { return bool_string == "1"; @@ -41,11 +59,11 @@ bool is_true(const std::string &bool_string) xlnt::protection::type protection_type_from_string(const std::string &type_string) { - if (type_string == "true") + if (equals_case_insensitive(type_string, "true")) { return xlnt::protection::type::protected_; } - else if (type_string == "inherit") + else if (equals_case_insensitive(type_string, "inherit")) { return xlnt::protection::type::inherit; } @@ -55,23 +73,23 @@ xlnt::protection::type protection_type_from_string(const std::string &type_strin xlnt::font::underline_style underline_style_from_string(const std::string &underline_string) { - if (underline_string == "none") + if (equals_case_insensitive(underline_string, "none")) { return xlnt::font::underline_style::none; } - else if (underline_string == "single") + else if (equals_case_insensitive(underline_string, "single")) { return xlnt::font::underline_style::single; } - else if (underline_string == "single-accounting") + else if (equals_case_insensitive(underline_string, "single-accounting")) { return xlnt::font::underline_style::single_accounting; } - else if (underline_string == "double") + else if (equals_case_insensitive(underline_string, "double")) { return xlnt::font::underline_style::double_; } - else if (underline_string == "double-accounting") + else if (equals_case_insensitive(underline_string, "double-accounting")) { return xlnt::font::underline_style::double_accounting; } @@ -81,73 +99,75 @@ xlnt::font::underline_style underline_style_from_string(const std::string &under xlnt::fill::pattern_type pattern_fill_type_from_string(const std::string &fill_type) { - if (fill_type == "none") return xlnt::fill::pattern_type::none; - if (fill_type == "solid") return xlnt::fill::pattern_type::solid; - if (fill_type == "gray125") return xlnt::fill::pattern_type::gray125; + if (equals_case_insensitive(fill_type, "none")) return xlnt::fill::pattern_type::none; + if (equals_case_insensitive(fill_type, "solid")) return xlnt::fill::pattern_type::solid; + if (equals_case_insensitive(fill_type, "gray125")) return xlnt::fill::pattern_type::gray125; return xlnt::fill::pattern_type::none; }; xlnt::border_style border_style_from_string(const std::string &border_style_string) { - if (border_style_string == "none") + if (equals_case_insensitive(border_style_string, "none")) { return xlnt::border_style::none; } - else if (border_style_string == "dashdot") + else if (equals_case_insensitive(border_style_string, "dashdot")) { return xlnt::border_style::dashdot; } - else if (border_style_string == "dashdotdot") + else if (equals_case_insensitive(border_style_string, "dashdotdot")) { return xlnt::border_style::dashdotdot; } - else if (border_style_string == "dashed") + else if (equals_case_insensitive(border_style_string, "dashed")) { return xlnt::border_style::dashed; } - else if (border_style_string == "dotted") + else if (equals_case_insensitive(border_style_string, "dotted")) { return xlnt::border_style::dotted; } - else if (border_style_string == "double") + else if (equals_case_insensitive(border_style_string, "double")) { return xlnt::border_style::double_; } - else if (border_style_string == "hair") + else if (equals_case_insensitive(border_style_string, "hair")) { return xlnt::border_style::hair; } - else if (border_style_string == "medium") + else if (equals_case_insensitive(border_style_string, "medium")) { return xlnt::border_style::medium; } - else if (border_style_string == "mediumdashdot") + else if (equals_case_insensitive(border_style_string, "mediumdashdot")) { return xlnt::border_style::mediumdashdot; } - else if (border_style_string == "mediumdashdotdot") + else if (equals_case_insensitive(border_style_string, "mediumdashdotdot")) { return xlnt::border_style::mediumdashdotdot; } - else if (border_style_string == "mediumdashed") + else if (equals_case_insensitive(border_style_string, "mediumdashed")) { return xlnt::border_style::mediumdashed; } - else if (border_style_string == "slantdashdot") + else if (equals_case_insensitive(border_style_string, "slantdashdot")) { return xlnt::border_style::slantdashdot; } - else if (border_style_string == "thick") + else if (equals_case_insensitive(border_style_string, "thick")) { return xlnt::border_style::thick; } - else if (border_style_string == "thin") + else if (equals_case_insensitive(border_style_string, "thin")) { return xlnt::border_style::thin; } else { - throw std::runtime_error("unknown border style"); + std::string message = "unknown border style: "; + message.append(border_style_string); + throw std::runtime_error(border_style_string); } } @@ -696,6 +716,9 @@ xml_document style_serializer::write_stylesheet() const case color::type::indexed: fg_color_node.add_attribute("indexed", std::to_string(fill_.get_foreground_color()->get_index())); break; + case color::type::rgb: + fg_color_node.add_attribute("rgb", fill_.get_foreground_color()->get_rgb_string()); + break; default: throw std::runtime_error("bad type"); } @@ -716,6 +739,9 @@ xml_document style_serializer::write_stylesheet() const case color::type::indexed: bg_color_node.add_attribute("indexed", std::to_string(fill_.get_background_color()->get_index())); break; + case color::type::rgb: + bg_color_node.add_attribute("rgb", fill_.get_background_color()->get_rgb_string()); + break; default: throw std::runtime_error("bad type"); } @@ -846,6 +872,10 @@ xml_document style_serializer::write_stylesheet() const { color_node.add_attribute("theme", std::to_string(current_side->get_color()->get_theme())); } + else if (current_side->get_color()->get_type() == color::type::rgb) + { + color_node.add_attribute("rgb", current_side->get_color()->get_rgb_string()); + } else { throw std::runtime_error("invalid color type"); diff --git a/source/serialization/workbook_serializer.cpp b/source/serialization/workbook_serializer.cpp index 36bc316f..97e080b7 100644 --- a/source/serialization/workbook_serializer.cpp +++ b/source/serialization/workbook_serializer.cpp @@ -23,6 +23,7 @@ // @author: see AUTHORS file #include +#include #include #include #include @@ -110,6 +111,52 @@ void workbook_serializer::read_properties_core(const xml_document &xml) } } +void workbook_serializer::read_properties_app(const xml_document &xml) +{ + auto &props = workbook_.get_app_properties(); + auto root_node = xml.get_child("Properties"); + + if(root_node.has_child("Application")) + { + props.application = root_node.get_child("Application").get_text(); + } + + if(root_node.has_child("DocSecurity")) + { + props.doc_security = std::stoi(root_node.get_child("DocSecurity").get_text()); + } + + if(root_node.has_child("ScaleCrop")) + { + props.scale_crop = root_node.get_child("ScaleCrop").get_text() == "true"; + } + + if(root_node.has_child("Company")) + { + props.company = root_node.get_child("Company").get_text(); + } + + if(root_node.has_child("ScaleCrop")) + { + props.links_up_to_date = root_node.get_child("ScaleCrop").get_text() == "true"; + } + + if(root_node.has_child("SharedDoc")) + { + props.shared_doc = root_node.get_child("SharedDoc").get_text() == "true"; + } + + if(root_node.has_child("HyperlinksChanged")) + { + props.hyperlinks_changed = root_node.get_child("HyperlinksChanged").get_text() == "true"; + } + + if(root_node.has_child("AppVersion")) + { + props.app_version = root_node.get_child("AppVersion").get_text(); + } +} + xml_document workbook_serializer::write_properties_core() const { auto &props = workbook_.get_properties(); @@ -147,16 +194,27 @@ xml_document workbook_serializer::write_properties_app() const xml.add_namespace("", "http://schemas.openxmlformats.org/officeDocument/2006/extended-properties"); xml.add_namespace("vt", "http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"); + + auto &properties = workbook_.get_app_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"); + root_node.add_child("Application").set_text(properties.application); + root_node.add_child("DocSecurity").set_text(std::to_string(properties.doc_security)); + root_node.add_child("ScaleCrop").set_text(properties.scale_crop ? "true" : "false"); + + auto company_node = root_node.add_child("Company"); + + if (!properties.company.empty()) + { + company_node.set_text(properties.company); + } + + root_node.add_child("LinksUpToDate").set_text(properties.links_up_to_date ? "true" : "false"); + root_node.add_child("SharedDoc").set_text(properties.shared_doc ? "true" : "false"); + root_node.add_child("HyperlinksChanged").set_text(properties.hyperlinks_changed ? "true" : "false"); + root_node.add_child("AppVersion").set_text(properties.app_version); + // TODO what is this stuff? + 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"); diff --git a/source/workbook/workbook.cpp b/source/workbook/workbook.cpp index da01e810..23f5ed59 100644 --- a/source/workbook/workbook.cpp +++ b/source/workbook/workbook.cpp @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -499,6 +500,17 @@ const document_properties &workbook::get_properties() const return d_->properties_; } +app_properties &workbook::get_app_properties() +{ + return d_->app_properties_; +} + +const app_properties &workbook::get_app_properties() const +{ + return d_->app_properties_; +} + + void swap(workbook &left, workbook &right) { using std::swap;