From acfb5c642bae1884ac65ac22b07c7c209670209d Mon Sep 17 00:00:00 2001 From: Thomas Fussell Date: Fri, 20 Nov 2015 20:41:32 -0500 Subject: [PATCH] keep cleaning up iterators, documenting classes, and moving implementations to source files --- include/xlnt/cell/cell.hpp | 6 +- include/xlnt/cell/cell_reference.hpp | 2 +- .../xlnt/cell/{types.hpp => index_types.hpp} | 0 include/xlnt/packaging/default_type.hpp | 5 + .../xlnt/packaging/document_properties.hpp | 2 +- include/xlnt/packaging/manifest.hpp | 4 + include/xlnt/packaging/override_type.hpp | 4 + include/xlnt/packaging/relationship.hpp | 123 +-- .../xlnt/serialization/comment_serializer.hpp | 3 + .../serialization/manifest_serializer.hpp | 3 + .../shared_strings_serializer.hpp | 3 + .../xlnt/serialization/theme_serializer.hpp | 3 + .../serialization/workbook_serializer.hpp | 3 + .../serialization/worksheet_serializer.hpp | 3 + include/xlnt/serialization/xml_document.hpp | 3 + include/xlnt/serialization/xml_node.hpp | 3 + include/xlnt/serialization/xml_serializer.hpp | 3 + include/xlnt/styles/alignment.hpp | 79 +- include/xlnt/styles/border.hpp | 43 +- include/xlnt/styles/color.hpp | 8 +- include/xlnt/styles/diagonal_direction.hpp | 43 + include/xlnt/styles/fill.hpp | 3 + include/xlnt/styles/font.hpp | 3 + include/xlnt/styles/horizontal_alignment.hpp | 44 + include/xlnt/styles/named_style.hpp | 3 + include/xlnt/styles/number_format.hpp | 37 +- include/xlnt/styles/protection.hpp | 3 + include/xlnt/styles/side.hpp | 3 + include/xlnt/styles/style.hpp | 3 + include/xlnt/styles/vertical_alignment.hpp | 42 + include/xlnt/utils/attribute_error.hpp | 2 +- .../xlnt/utils/cell_coordinates_exception.hpp | 2 +- include/xlnt/utils/datetime.hpp | 3 + include/xlnt/utils/exceptions.hpp | 2 + include/xlnt/utils/hash_combine.hpp | 3 + include/xlnt/utils/invalid_file_exception.hpp | 2 +- include/xlnt/utils/missing_number_format.hpp | 2 +- include/xlnt/utils/named_range_exception.hpp | 2 +- include/xlnt/workbook/document_security.hpp | 2 +- include/xlnt/workbook/external_book.hpp | 3 + include/xlnt/workbook/named_range.hpp | 5 + include/xlnt/workbook/theme.hpp | 4 + include/xlnt/worksheet/cell_vector.hpp | 102 +-- include/xlnt/worksheet/column_properties.hpp | 4 + include/xlnt/worksheet/footer.hpp | 34 +- include/xlnt/worksheet/header.hpp | 34 +- include/xlnt/worksheet/header_footer.hpp | 2 +- include/xlnt/worksheet/major_order.hpp | 3 + include/xlnt/worksheet/orientation.hpp | 3 + include/xlnt/worksheet/page_break.hpp | 3 + include/xlnt/worksheet/page_margins.hpp | 3 + include/xlnt/worksheet/page_setup.hpp | 3 + include/xlnt/worksheet/pane.hpp | 3 + include/xlnt/worksheet/paper_size.hpp | 3 + include/xlnt/worksheet/range.hpp | 105 +-- include/xlnt/worksheet/range_iterator_2d.hpp | 80 ++ include/xlnt/worksheet/range_reference.hpp | 78 +- include/xlnt/worksheet/row_properties.hpp | 3 + include/xlnt/worksheet/selection.hpp | 3 + include/xlnt/worksheet/sheet_protection.hpp | 8 +- include/xlnt/worksheet/sheet_state.hpp | 4 + include/xlnt/worksheet/sheet_view.hpp | 4 + include/xlnt/worksheet/worksheet.hpp | 28 +- .../xlnt/worksheet/worksheet_properties.hpp | 3 + include/xlnt/xlnt.hpp | 2 +- source/cell/cell.cpp | 785 +---------------- source/cell/{types.cpp => index_types.cpp} | 2 +- source/detail/cell_impl.hpp | 2 +- source/detail/constants.hpp | 2 +- source/packaging/relationship.cpp | 115 +++ source/serialization/style_serializer.cpp | 40 +- source/styles/alignment.cpp | 86 ++ source/styles/border.cpp | 36 + source/styles/number_format.cpp | 813 ++++++++++++++++++ source/worksheet/cell_vector.cpp | 125 ++- source/worksheet/footer.cpp | 34 + source/worksheet/header.cpp | 34 + source/worksheet/range.cpp | 148 +++- source/worksheet/range_reference.cpp | 58 ++ source/worksheet/sheet_protection.cpp | 19 +- source/worksheet/tests/test_worksheet.hpp | 12 +- source/worksheet/worksheet.cpp | 68 +- 82 files changed, 2073 insertions(+), 1340 deletions(-) rename include/xlnt/cell/{types.hpp => index_types.hpp} (100%) create mode 100644 include/xlnt/styles/diagonal_direction.hpp create mode 100644 include/xlnt/styles/horizontal_alignment.hpp create mode 100644 include/xlnt/styles/vertical_alignment.hpp create mode 100644 include/xlnt/worksheet/range_iterator_2d.hpp rename source/cell/{types.cpp => index_types.cpp} (99%) create mode 100644 source/styles/alignment.cpp create mode 100644 source/styles/border.cpp create mode 100644 source/worksheet/footer.cpp create mode 100644 source/worksheet/header.cpp diff --git a/include/xlnt/cell/cell.hpp b/include/xlnt/cell/cell.hpp index 94d31017..fc79ee13 100644 --- a/include/xlnt/cell/cell.hpp +++ b/include/xlnt/cell/cell.hpp @@ -27,9 +27,9 @@ #include #include -#include -#include -#include +#include // for XLNT_CLASS, XLNT_FUNCTION +#include // for cell_type +#include // for column_t, row_t namespace xlnt { diff --git a/include/xlnt/cell/cell_reference.hpp b/include/xlnt/cell/cell_reference.hpp index 9cdc46a1..f01c0203 100644 --- a/include/xlnt/cell/cell_reference.hpp +++ b/include/xlnt/cell/cell_reference.hpp @@ -27,7 +27,7 @@ #include #include -#include +#include namespace xlnt { diff --git a/include/xlnt/cell/types.hpp b/include/xlnt/cell/index_types.hpp similarity index 100% rename from include/xlnt/cell/types.hpp rename to include/xlnt/cell/index_types.hpp diff --git a/include/xlnt/packaging/default_type.hpp b/include/xlnt/packaging/default_type.hpp index 64347503..68977c6b 100644 --- a/include/xlnt/packaging/default_type.hpp +++ b/include/xlnt/packaging/default_type.hpp @@ -29,6 +29,11 @@ namespace xlnt { +/// +/// Default types in an OOXML package are identified by their extension. +/// All files in the package with this extension will be assigned the given +/// content type unless an override type for the exact file is provided. +/// class XLNT_CLASS default_type { public: diff --git a/include/xlnt/packaging/document_properties.hpp b/include/xlnt/packaging/document_properties.hpp index 2373cf13..352fad63 100644 --- a/include/xlnt/packaging/document_properties.hpp +++ b/include/xlnt/packaging/document_properties.hpp @@ -31,7 +31,7 @@ namespace xlnt { /// -/// High-level properties of the document. +/// High-level properties of the OOXML document. /// class XLNT_CLASS document_properties { diff --git a/include/xlnt/packaging/manifest.hpp b/include/xlnt/packaging/manifest.hpp index 4dae1035..ebfc18b5 100644 --- a/include/xlnt/packaging/manifest.hpp +++ b/include/xlnt/packaging/manifest.hpp @@ -32,6 +32,10 @@ namespace xlnt { +/// +/// The manifest keeps track of all files the OOXML package and +/// their type. +/// class XLNT_CLASS manifest { public: diff --git a/include/xlnt/packaging/override_type.hpp b/include/xlnt/packaging/override_type.hpp index 7aeb89a0..2a281625 100644 --- a/include/xlnt/packaging/override_type.hpp +++ b/include/xlnt/packaging/override_type.hpp @@ -29,6 +29,10 @@ namespace xlnt { +/// +/// An override_type applies a different content type to a file which +/// already has a default content type for its extension. +/// class XLNT_CLASS override_type { public: diff --git a/include/xlnt/packaging/relationship.hpp b/include/xlnt/packaging/relationship.hpp index 8562025b..db73669a 100644 --- a/include/xlnt/packaging/relationship.hpp +++ b/include/xlnt/packaging/relationship.hpp @@ -67,134 +67,41 @@ class XLNT_CLASS relationship custom_xml }; - static type type_from_string(const std::string &type_string) - { - if (type_string == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties") - { - return type::extended_properties; - } - else if (type_string == "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties") - { - return type::core_properties; - } - else if (type_string == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument") - { - return type::office_document; - } - else if (type_string == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet") - { - return type::worksheet; - } - else if (type_string == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings") - { - return type::shared_strings; - } - else if (type_string == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles") - { - return type::styles; - } - else if (type_string == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme") - { - return type::theme; - } - else if (type_string == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink") - { - return type::hyperlink; - } - else if (type_string == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chartsheet") - { - return type::chartsheet; - } - else if (type_string == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/customXml") - { - return type::custom_xml; - } - - return type::invalid; - } - - static std::string type_to_string(type t) - { - switch (t) - { - case type::extended_properties: - return "http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties"; - case type::core_properties: - return "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties"; - case type::office_document: - return "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"; - case type::worksheet: - return "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet"; - case type::shared_strings: - return "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings"; - case type::styles: - return "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles"; - case type::theme: - return "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme"; - case type::hyperlink: - return "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink"; - case type::chartsheet: - return "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chartsheet"; - case type::custom_xml: - return "http://schemas.openxmlformats.org/officeDocument/2006/relationships/customXml"; - default: - return "??"; - } - } + static type type_from_string(const std::string &type_string); + static std::string type_to_string(type t); + relationship(); - relationship(const std::string &t, const std::string &r_id = "", const std::string &target_uri = "") - : relationship(type_from_string(t), r_id, target_uri) - { - } + + relationship(const std::string &t, const std::string &r_id = "", const std::string &target_uri = ""); + relationship(type t, const std::string &r_id = "", const std::string &target_uri = ""); /// /// gets a string that identifies the relationship. /// - std::string get_id() const - { - return id_; - } + std::string get_id() const; /// /// gets the URI of the package or part that owns the relationship. /// - std::string get_source_uri() const - { - return source_uri_; - } + std::string get_source_uri() const; /// /// gets a value that indicates whether the target of the relationship is or External to the Package. /// - target_mode get_target_mode() const - { - return target_mode_; - } + target_mode get_target_mode() const; /// /// gets the URI of the target resource of the relationship. /// - std::string get_target_uri() const - { - return target_uri_; - } + std::string get_target_uri() const; - type get_type() const - { - return type_; - } - std::string get_type_string() const - { - return type_to_string(type_); - } - - friend bool operator==(const relationship &left, const relationship &right) - { - return left.type_ == right.type_ && left.id_ == right.id_ && left.source_uri_ == right.source_uri_ && - left.target_uri_ == right.target_uri_ && left.target_mode_ == right.target_mode_; - } + type get_type() const; + + std::string get_type_string() const; + + bool operator==(const relationship &rhs) const; private: type type_; diff --git a/include/xlnt/serialization/comment_serializer.hpp b/include/xlnt/serialization/comment_serializer.hpp index 7c6509a5..1bcb04ad 100644 --- a/include/xlnt/serialization/comment_serializer.hpp +++ b/include/xlnt/serialization/comment_serializer.hpp @@ -33,6 +33,9 @@ namespace xlnt { class xml_document; +/// +/// Manages converting comments to and from XML. +/// class XLNT_CLASS comment_serializer { comment_serializer(worksheet sheet); diff --git a/include/xlnt/serialization/manifest_serializer.hpp b/include/xlnt/serialization/manifest_serializer.hpp index bad062cc..2e9844ed 100644 --- a/include/xlnt/serialization/manifest_serializer.hpp +++ b/include/xlnt/serialization/manifest_serializer.hpp @@ -32,6 +32,9 @@ namespace xlnt { class manifest; class xml_document; +/// +/// Manages converting a manifest to and from XML. +/// class XLNT_CLASS manifest_serializer { public: diff --git a/include/xlnt/serialization/shared_strings_serializer.hpp b/include/xlnt/serialization/shared_strings_serializer.hpp index a23d56ef..a48ed022 100644 --- a/include/xlnt/serialization/shared_strings_serializer.hpp +++ b/include/xlnt/serialization/shared_strings_serializer.hpp @@ -32,6 +32,9 @@ namespace xlnt { class xml_document; +/// +/// Manages converting a set of shared strings to and from XML. +/// class XLNT_CLASS shared_strings_serializer { public: diff --git a/include/xlnt/serialization/theme_serializer.hpp b/include/xlnt/serialization/theme_serializer.hpp index 33ba0989..939fbbc2 100644 --- a/include/xlnt/serialization/theme_serializer.hpp +++ b/include/xlnt/serialization/theme_serializer.hpp @@ -32,6 +32,9 @@ namespace xlnt { class theme; class xml_document; +/// +/// Manages converting a theme to and from XML. +/// class XLNT_CLASS theme_serializer { public: diff --git a/include/xlnt/serialization/workbook_serializer.hpp b/include/xlnt/serialization/workbook_serializer.hpp index cdf11efa..9874f24a 100644 --- a/include/xlnt/serialization/workbook_serializer.hpp +++ b/include/xlnt/serialization/workbook_serializer.hpp @@ -39,6 +39,9 @@ class zip_file; class xml_document; class xml_node; +/// +/// Manages converting workbook to and from XML. +/// class XLNT_CLASS workbook_serializer { public: diff --git a/include/xlnt/serialization/worksheet_serializer.hpp b/include/xlnt/serialization/worksheet_serializer.hpp index 2971c039..2e3e075a 100644 --- a/include/xlnt/serialization/worksheet_serializer.hpp +++ b/include/xlnt/serialization/worksheet_serializer.hpp @@ -36,6 +36,9 @@ class workbook; class worksheet; class xml_document; +/// +/// Manages converting a worksheet to and from XML. +/// class XLNT_CLASS worksheet_serializer { public: diff --git a/include/xlnt/serialization/xml_document.hpp b/include/xlnt/serialization/xml_document.hpp index 8c964a7a..a41216f0 100644 --- a/include/xlnt/serialization/xml_document.hpp +++ b/include/xlnt/serialization/xml_document.hpp @@ -34,6 +34,9 @@ namespace detail { struct xml_document_impl; } class xml_node; class xml_serializer; +/// +/// Abstracts an XML document from a particular implementation. +/// class XLNT_CLASS xml_document { public: diff --git a/include/xlnt/serialization/xml_node.hpp b/include/xlnt/serialization/xml_node.hpp index bd5369fe..ea8648bc 100644 --- a/include/xlnt/serialization/xml_node.hpp +++ b/include/xlnt/serialization/xml_node.hpp @@ -33,6 +33,9 @@ namespace detail { struct xml_node_impl; } class xml_document; +/// +/// Abstracts an XML node from a particular implementation. +/// class XLNT_CLASS xml_node { public: diff --git a/include/xlnt/serialization/xml_serializer.hpp b/include/xlnt/serialization/xml_serializer.hpp index a455c641..b7130bee 100644 --- a/include/xlnt/serialization/xml_serializer.hpp +++ b/include/xlnt/serialization/xml_serializer.hpp @@ -31,6 +31,9 @@ namespace xlnt { class xml_document; class xml_node; +/// +/// Converts XML documents to and from raw strings. +/// class XLNT_CLASS xml_serializer { public: diff --git a/include/xlnt/styles/alignment.hpp b/include/xlnt/styles/alignment.hpp index 3fa3bfb2..8c62012a 100644 --- a/include/xlnt/styles/alignment.hpp +++ b/include/xlnt/styles/alignment.hpp @@ -25,6 +25,8 @@ #include #include +#include +#include namespace xlnt { @@ -34,82 +36,25 @@ namespace xlnt { class XLNT_CLASS alignment { public: - enum class horizontal_alignment - { - none, - general, - left, - right, - center, - center_continuous, - justify - }; + bool get_wrap_text() const; - enum class vertical_alignment - { - none, - bottom, - top, - center, - justify - }; + void set_wrap_text(bool wrap_text); - bool get_wrap_text() const - { - return wrap_text_; - } + bool has_horizontal() const; - void set_wrap_text(bool wrap_text) - { - wrap_text_ = wrap_text; - } + horizontal_alignment get_horizontal() const; - bool has_horizontal() const - { - return horizontal_ != horizontal_alignment::none; - } + void set_horizontal(horizontal_alignment horizontal); - horizontal_alignment get_horizontal() const - { - return horizontal_; - } + bool has_vertical() const; - void set_horizontal(horizontal_alignment horizontal) - { - horizontal_ = horizontal; - } + vertical_alignment get_vertical() const; - bool has_vertical() const - { - return vertical_ != vertical_alignment::none; - } + void set_vertical(vertical_alignment vertical); - vertical_alignment get_vertical() const - { - return vertical_; - } + bool operator==(const alignment &other) const; - void set_vertical(vertical_alignment vertical) - { - vertical_ = vertical; - } - - bool operator==(const alignment &other) const - { - return hash() == other.hash(); - } - - std::size_t hash() const - { - std::size_t seed = 0; - hash_combine(seed, wrap_text_); - hash_combine(seed, shrink_to_fit_); - hash_combine(seed, static_cast(horizontal_)); - hash_combine(seed, static_cast(vertical_)); - hash_combine(seed, text_rotation_); - hash_combine(seed, indent_); - return seed; - } + std::size_t hash() const; private: horizontal_alignment horizontal_ = horizontal_alignment::none; diff --git a/include/xlnt/styles/border.hpp b/include/xlnt/styles/border.hpp index 9195c520..21da4a18 100644 --- a/include/xlnt/styles/border.hpp +++ b/include/xlnt/styles/border.hpp @@ -27,19 +27,15 @@ #include #include +#include #include #include namespace xlnt { -enum class XLNT_CLASS diagonal_direction -{ - none, - up, - down, - both -}; - +/// +/// Describes the border style of a particular cell. +/// class XLNT_CLASS border { public: @@ -70,36 +66,9 @@ class XLNT_CLASS border diagonal_direction diagonal_direction_ = diagonal_direction::none; - bool operator==(const border &other) const - { - return hash() == other.hash(); - } + bool operator==(const border &other) const; - std::size_t hash() const - { - std::size_t seed = 0; - - hash_combine(seed, start_assigned); - if (start_assigned) hash_combine(seed, start.hash()); - hash_combine(seed, end_assigned); - if (end_assigned) hash_combine(seed, end.hash()); - hash_combine(seed, left_assigned); - if (left_assigned) hash_combine(seed, left.hash()); - hash_combine(seed, right_assigned); - if (right_assigned) hash_combine(seed, right.hash()); - hash_combine(seed, top_assigned); - if (top_assigned) hash_combine(seed, top.hash()); - hash_combine(seed, bottom_assigned); - if (bottom_assigned) hash_combine(seed, bottom.hash()); - hash_combine(seed, diagonal_assigned); - if (diagonal_assigned) hash_combine(seed, diagonal.hash()); - hash_combine(seed, vertical_assigned); - if (vertical_assigned) hash_combine(seed, vertical.hash()); - hash_combine(seed, horizontal_assigned); - if (horizontal_assigned) hash_combine(seed, horizontal.hash()); - - return seed; - } + std::size_t hash() const; }; } // namespace xlnt diff --git a/include/xlnt/styles/color.hpp b/include/xlnt/styles/color.hpp index 371132a4..774f901d 100644 --- a/include/xlnt/styles/color.hpp +++ b/include/xlnt/styles/color.hpp @@ -30,9 +30,15 @@ namespace xlnt { +/// +/// Colors can be applied to many parts of a cell's style. +/// class XLNT_CLASS color { - public: +public: + /// + /// Some colors are references to colors rather than having a particular RGB value. + /// enum class type { indexed, diff --git a/include/xlnt/styles/diagonal_direction.hpp b/include/xlnt/styles/diagonal_direction.hpp new file mode 100644 index 00000000..401de600 --- /dev/null +++ b/include/xlnt/styles/diagonal_direction.hpp @@ -0,0 +1,43 @@ +// Copyright (c) 2014-2015 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 + +namespace xlnt { + +/// +/// Cells can have borders that go from the top-left to bottom-right +/// or from the top-right to bottom-left, or both, or neither. +/// Used by style->border. +/// +enum class XLNT_CLASS diagonal_direction +{ + none, + up, + down, + both +}; + +} // namespace xlnt diff --git a/include/xlnt/styles/fill.hpp b/include/xlnt/styles/fill.hpp index e03eca1d..cdef1932 100644 --- a/include/xlnt/styles/fill.hpp +++ b/include/xlnt/styles/fill.hpp @@ -29,6 +29,9 @@ namespace xlnt { +/// +/// Describes the fill style of a particular cell. +/// class XLNT_CLASS fill { public: diff --git a/include/xlnt/styles/font.hpp b/include/xlnt/styles/font.hpp index 9b6ba67a..4b07c8fb 100644 --- a/include/xlnt/styles/font.hpp +++ b/include/xlnt/styles/font.hpp @@ -33,6 +33,9 @@ namespace xlnt { class style; +/// +/// Describes the font style of a particular cell. +/// class XLNT_CLASS font { public: diff --git a/include/xlnt/styles/horizontal_alignment.hpp b/include/xlnt/styles/horizontal_alignment.hpp new file mode 100644 index 00000000..4428f253 --- /dev/null +++ b/include/xlnt/styles/horizontal_alignment.hpp @@ -0,0 +1,44 @@ +// Copyright (c) 2014-2015 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 + +namespace xlnt { + +/// +/// Text can be aligned horizontally in these enumerated ways. +/// +enum class XLNT_CLASS horizontal_alignment +{ + none, + general, + left, + right, + center, + center_continuous, + justify +}; + +} // namespace xlnt diff --git a/include/xlnt/styles/named_style.hpp b/include/xlnt/styles/named_style.hpp index d1279645..6666df5c 100644 --- a/include/xlnt/styles/named_style.hpp +++ b/include/xlnt/styles/named_style.hpp @@ -28,6 +28,9 @@ namespace xlnt { +/// +/// A named style is simply a style that can be referred to by name rather than by index. +/// class XLNT_CLASS named_style { public: diff --git a/include/xlnt/styles/number_format.hpp b/include/xlnt/styles/number_format.hpp index f86df863..3017a48a 100644 --- a/include/xlnt/styles/number_format.hpp +++ b/include/xlnt/styles/number_format.hpp @@ -31,6 +31,11 @@ namespace xlnt { +enum class calendar; + +/// +/// Describes the number formatting applied to text and numbers within a certain cell. +/// class XLNT_CLASS number_format { public: @@ -81,33 +86,19 @@ class XLNT_CLASS number_format std::string get_format_string() const; - bool has_id() const - { - return id_set_; - } - - void set_id(std::size_t id) - { - id_ = id; - id_set_ = true; - } + bool has_id() const; + void set_id(std::size_t id); - std::size_t get_id() const - { - if(!id_set_) - { - throw std::runtime_error("number format doesn't have an id"); - } - - return id_; - } + std::size_t get_id() const; std::size_t hash() const; + + std::string format(const std::string &text) const; + std::string format(long double number, calendar base_date) const; + + bool is_date_format() const; - bool operator==(const number_format &other) const - { - return hash() == other.hash(); - } + bool operator==(const number_format &other) const; private: bool id_set_; diff --git a/include/xlnt/styles/protection.hpp b/include/xlnt/styles/protection.hpp index 3d9826e5..c3829681 100644 --- a/include/xlnt/styles/protection.hpp +++ b/include/xlnt/styles/protection.hpp @@ -30,6 +30,9 @@ namespace xlnt { +/// +/// Describes the protection style of a particular cell. +/// class XLNT_CLASS protection { public: diff --git a/include/xlnt/styles/side.hpp b/include/xlnt/styles/side.hpp index f4a66757..2ae7015e 100644 --- a/include/xlnt/styles/side.hpp +++ b/include/xlnt/styles/side.hpp @@ -48,6 +48,9 @@ enum class XLNT_CLASS border_style thin }; +/// +/// Describes the one of the sides of a border of a particular cell. +/// class XLNT_CLASS side { public: diff --git a/include/xlnt/styles/style.hpp b/include/xlnt/styles/style.hpp index 64a90413..0c62f90e 100644 --- a/include/xlnt/styles/style.hpp +++ b/include/xlnt/styles/style.hpp @@ -35,6 +35,9 @@ namespace xlnt { class workbook; +/// +/// Describes the entirety of the styling of a particular cell. +/// class XLNT_CLASS style { public: diff --git a/include/xlnt/styles/vertical_alignment.hpp b/include/xlnt/styles/vertical_alignment.hpp new file mode 100644 index 00000000..ac53d04f --- /dev/null +++ b/include/xlnt/styles/vertical_alignment.hpp @@ -0,0 +1,42 @@ +// Copyright (c) 2014-2015 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 + +namespace xlnt { + +/// +/// Text can be aligned vertically in these enumerated ways. +/// +enum class XLNT_CLASS vertical_alignment +{ + none, + bottom, + top, + center, + justify +}; + +} // namespace xlnt diff --git a/include/xlnt/utils/attribute_error.hpp b/include/xlnt/utils/attribute_error.hpp index 4d4877b6..dfaac3f0 100644 --- a/include/xlnt/utils/attribute_error.hpp +++ b/include/xlnt/utils/attribute_error.hpp @@ -25,7 +25,7 @@ #include -#include +#include namespace xlnt { diff --git a/include/xlnt/utils/cell_coordinates_exception.hpp b/include/xlnt/utils/cell_coordinates_exception.hpp index 9f362d76..f76f0695 100644 --- a/include/xlnt/utils/cell_coordinates_exception.hpp +++ b/include/xlnt/utils/cell_coordinates_exception.hpp @@ -27,7 +27,7 @@ #include #include -#include +#include namespace xlnt { diff --git a/include/xlnt/utils/datetime.hpp b/include/xlnt/utils/datetime.hpp index e2248ba8..e26eb8a2 100644 --- a/include/xlnt/utils/datetime.hpp +++ b/include/xlnt/utils/datetime.hpp @@ -29,6 +29,9 @@ namespace xlnt { +/// +/// A datetime is a combination of a date and a time. +/// struct XLNT_CLASS datetime { /// diff --git a/include/xlnt/utils/exceptions.hpp b/include/xlnt/utils/exceptions.hpp index a790e0cc..cfb4c6f7 100644 --- a/include/xlnt/utils/exceptions.hpp +++ b/include/xlnt/utils/exceptions.hpp @@ -23,6 +23,8 @@ // @author: see AUTHORS file #pragma once +// An shortcut for including all possible exceptions in xlnt. + #include #include #include diff --git a/include/xlnt/utils/hash_combine.hpp b/include/xlnt/utils/hash_combine.hpp index e8ae0933..28db6188 100644 --- a/include/xlnt/utils/hash_combine.hpp +++ b/include/xlnt/utils/hash_combine.hpp @@ -35,6 +35,9 @@ namespace xlnt { // TODO: Can this be in source->detail, or must it be in a header? +/// +/// Return a value by combining the hashed value of v (using std::hash) with seed. +/// template inline void XLNT_FUNCTION hash_combine(std::size_t &seed, const T &v) { diff --git a/include/xlnt/utils/invalid_file_exception.hpp b/include/xlnt/utils/invalid_file_exception.hpp index 458fe4a1..70c48f1c 100644 --- a/include/xlnt/utils/invalid_file_exception.hpp +++ b/include/xlnt/utils/invalid_file_exception.hpp @@ -31,7 +31,7 @@ namespace xlnt { /// -/// Error for trying to open a non-ooxml file. +/// Error for trying to open a non-OOXML file. /// class XLNT_CLASS invalid_file_exception : public std::runtime_error { diff --git a/include/xlnt/utils/missing_number_format.hpp b/include/xlnt/utils/missing_number_format.hpp index fa043f3a..39e52507 100644 --- a/include/xlnt/utils/missing_number_format.hpp +++ b/include/xlnt/utils/missing_number_format.hpp @@ -30,7 +30,7 @@ namespace xlnt { /// -/// Error when a references number format is not in the stylesheet. +/// Error when a referenced number format is not in the stylesheet. /// class XLNT_CLASS missing_number_format : public std::runtime_error { diff --git a/include/xlnt/utils/named_range_exception.hpp b/include/xlnt/utils/named_range_exception.hpp index 8417844f..c9c2a436 100644 --- a/include/xlnt/utils/named_range_exception.hpp +++ b/include/xlnt/utils/named_range_exception.hpp @@ -30,7 +30,7 @@ namespace xlnt { /// -/// Error for badly formatted named ranges. +/// Error for incorrectly formatted named ranges. /// class XLNT_CLASS named_range_exception : public std::runtime_error { diff --git a/include/xlnt/workbook/document_security.hpp b/include/xlnt/workbook/document_security.hpp index 0293b476..2979a28f 100644 --- a/include/xlnt/workbook/document_security.hpp +++ b/include/xlnt/workbook/document_security.hpp @@ -30,7 +30,7 @@ namespace xlnt { /// -/// Security information about the document. +/// Security information about the OOXML document. /// class XLNT_CLASS document_security { diff --git a/include/xlnt/workbook/external_book.hpp b/include/xlnt/workbook/external_book.hpp index e5ea3017..31ae8b55 100644 --- a/include/xlnt/workbook/external_book.hpp +++ b/include/xlnt/workbook/external_book.hpp @@ -27,6 +27,9 @@ namespace xlnt { +/// +/// A reference to an external workbook for use in formulae. +/// class XLNT_CLASS external_book { }; diff --git a/include/xlnt/workbook/named_range.hpp b/include/xlnt/workbook/named_range.hpp index 023915f3..798301d5 100644 --- a/include/xlnt/workbook/named_range.hpp +++ b/include/xlnt/workbook/named_range.hpp @@ -33,8 +33,13 @@ namespace xlnt { class range_reference; class worksheet; +//TODO: why is this not in a class? std::vector> XLNT_FUNCTION split_named_range(const std::string &named_range_string); +/// +/// A 2D range of cells in a worksheet that is referred to by name. +/// ws->get_range("A1:B2") could be replaced by ws->get_range("range1") +/// class XLNT_CLASS named_range { public: diff --git a/include/xlnt/workbook/theme.hpp b/include/xlnt/workbook/theme.hpp index 5c6885e5..d57f2e8b 100644 --- a/include/xlnt/workbook/theme.hpp +++ b/include/xlnt/workbook/theme.hpp @@ -26,6 +26,10 @@ namespace xlnt { +/// +/// A theme is a combination of fonts, colors, and effects. +/// This isn't really supported yet. +/// class XLNT_CLASS theme { }; diff --git a/include/xlnt/worksheet/cell_vector.hpp b/include/xlnt/worksheet/cell_vector.hpp index 3cad0559..2553e5d6 100644 --- a/include/xlnt/worksheet/cell_vector.hpp +++ b/include/xlnt/worksheet/cell_vector.hpp @@ -35,78 +35,33 @@ namespace xlnt { class cell; class range_reference; +/// +/// A cell vector is a linear (1D) range of cells, either vertical or horizontal +/// depending on the major order specified in the constructor. +/// class XLNT_CLASS cell_vector { public: - template - class XLNT_CLASS common_iterator : public std::iterator + class XLNT_CLASS iterator : public std::iterator { public: - common_iterator(worksheet ws, const cell_reference &start_cell, major_order order = major_order::row) - : ws_(ws), current_cell_(start_cell), range_(start_cell.to_range()), order_(order) - { - } + iterator(worksheet ws, const cell_reference &start_cell, major_order order = major_order::row); - common_iterator(const common_iterator &other) - { - *this = other; - } + iterator(const iterator &other); cell operator*(); - bool operator==(const common_iterator &other) const - { - return ws_ == other.ws_ && current_cell_ == other.current_cell_ && order_ == other.order_; - } + bool operator==(const iterator &other) const; - bool operator!=(const common_iterator &other) const - { - return !(*this == other); - } + bool operator!=(const iterator &other) const; - common_iterator &operator--() - { - if (order_ == major_order::row) - { - current_cell_.set_column_index(current_cell_.get_column_index() - 1); - } - else - { - current_cell_.set_row(current_cell_.get_row() - 1); - } + iterator &operator--(); + + iterator operator--(int); - return *this; - } + iterator &operator++(); - common_iterator operator--(int) - { - iterator old = *this; - --*this; - return old; - } - - common_iterator &operator++() - { - if (order_ == major_order::row) - { - current_cell_.set_column_index(current_cell_.get_column_index() + 1); - } - else - { - current_cell_.set_row(current_cell_.get_row() + 1); - } - - return *this; - } - - common_iterator operator++(int) - { - iterator old = *this; - ++*this; - return old; - } - - friend class common_iterator; + iterator operator++(int); private: worksheet ws_; @@ -114,9 +69,34 @@ class XLNT_CLASS cell_vector range_reference range_; major_order order_; }; + + class XLNT_CLASS const_iterator : public std::iterator + { + public: + const_iterator(worksheet ws, const cell_reference &start_cell, major_order order = major_order::row); - using iterator = common_iterator; - using const_iterator = common_iterator; + const_iterator(const const_iterator &other); + + const cell operator*(); + + bool operator==(const const_iterator &other) const; + + bool operator!=(const const_iterator &other) const; + + const_iterator &operator--(); + + const_iterator operator--(int); + + const_iterator &operator++(); + + const_iterator operator++(int); + + private: + worksheet ws_; + cell_reference current_cell_; + range_reference range_; + major_order order_; + }; cell_vector(worksheet ws, const range_reference &ref, major_order order = major_order::row); diff --git a/include/xlnt/worksheet/column_properties.hpp b/include/xlnt/worksheet/column_properties.hpp index d5b09b0c..c833d133 100644 --- a/include/xlnt/worksheet/column_properties.hpp +++ b/include/xlnt/worksheet/column_properties.hpp @@ -27,6 +27,10 @@ namespace xlnt { +/// +/// Properties applied to a column in a worksheet. +/// Columns can have a size and a style. +/// class XLNT_CLASS column_properties { public: diff --git a/include/xlnt/worksheet/footer.hpp b/include/xlnt/worksheet/footer.hpp index 90b67ca3..69cdebdb 100644 --- a/include/xlnt/worksheet/footer.hpp +++ b/include/xlnt/worksheet/footer.hpp @@ -14,30 +14,16 @@ class XLNT_CLASS footer { public: footer(); - void set_text(const std::string &text) - { - default_ = false; - text_ = text; - } - void set_font_name(const std::string &font_name) - { - default_ = false; - font_name_ = font_name; - } - void set_font_size(std::size_t font_size) - { - default_ = false; - font_size_ = font_size; - } - void set_font_color(const std::string &font_color) - { - default_ = false; - font_color_ = font_color; - } - bool is_default() const - { - return default_; - } + + void set_text(const std::string &text); + + void set_font_name(const std::string &font_name); + + void set_font_size(std::size_t font_size); + + void set_font_color(const std::string &font_color); + + bool is_default() const; private: bool default_; diff --git a/include/xlnt/worksheet/header.hpp b/include/xlnt/worksheet/header.hpp index 60c3246e..c43d1ed1 100644 --- a/include/xlnt/worksheet/header.hpp +++ b/include/xlnt/worksheet/header.hpp @@ -14,30 +14,16 @@ class XLNT_CLASS header { public: header(); - void set_text(const std::string &text) - { - default_ = false; - text_ = text; - } - void set_font_name(const std::string &font_name) - { - default_ = false; - font_name_ = font_name; - } - void set_font_size(std::size_t font_size) - { - default_ = false; - font_size_ = font_size; - } - void set_font_color(const std::string &font_color) - { - default_ = false; - font_color_ = font_color; - } - bool is_default() const - { - return default_; - } + + void set_text(const std::string &text); + + void set_font_name(const std::string &font_name); + + void set_font_size(std::size_t font_size); + + void set_font_color(const std::string &font_color); + + bool is_default() const; private: bool default_; diff --git a/include/xlnt/worksheet/header_footer.hpp b/include/xlnt/worksheet/header_footer.hpp index c5b031fe..0cc35081 100644 --- a/include/xlnt/worksheet/header_footer.hpp +++ b/include/xlnt/worksheet/header_footer.hpp @@ -10,7 +10,7 @@ namespace xlnt { /// -/// Worksheet header and footer +/// Worksheet header and footer for all six possible positions. /// class XLNT_CLASS header_footer { diff --git a/include/xlnt/worksheet/major_order.hpp b/include/xlnt/worksheet/major_order.hpp index 745b2e13..a8b67956 100644 --- a/include/xlnt/worksheet/major_order.hpp +++ b/include/xlnt/worksheet/major_order.hpp @@ -26,6 +26,9 @@ namespace xlnt { +/// +/// Defines whether iterating a range returns columns or rows sequentially. +/// enum class XLNT_CLASS major_order { column, diff --git a/include/xlnt/worksheet/orientation.hpp b/include/xlnt/worksheet/orientation.hpp index 9c26600d..e96dcb93 100644 --- a/include/xlnt/worksheet/orientation.hpp +++ b/include/xlnt/worksheet/orientation.hpp @@ -27,6 +27,9 @@ namespace xlnt { +/// +/// The orientation of the worksheet when it is printed. +/// enum class XLNT_CLASS orientation { portrait, diff --git a/include/xlnt/worksheet/page_break.hpp b/include/xlnt/worksheet/page_break.hpp index 8283df70..c25f0ea8 100644 --- a/include/xlnt/worksheet/page_break.hpp +++ b/include/xlnt/worksheet/page_break.hpp @@ -27,6 +27,9 @@ namespace xlnt { +/// +/// The types of page breaks. +/// enum class XLNT_CLASS page_break { none = 0, diff --git a/include/xlnt/worksheet/page_margins.hpp b/include/xlnt/worksheet/page_margins.hpp index 5a9243db..7a0800f0 100644 --- a/include/xlnt/worksheet/page_margins.hpp +++ b/include/xlnt/worksheet/page_margins.hpp @@ -27,6 +27,9 @@ namespace xlnt { +/// +/// Describes the margins around a worksheet for printing. +/// class XLNT_CLASS page_margins { public: diff --git a/include/xlnt/worksheet/page_setup.hpp b/include/xlnt/worksheet/page_setup.hpp index edeeb0b5..cdd1d828 100644 --- a/include/xlnt/worksheet/page_setup.hpp +++ b/include/xlnt/worksheet/page_setup.hpp @@ -31,6 +31,9 @@ namespace xlnt { +/// +/// Describes how a worksheet will be converted into a page during printing. +/// struct XLNT_CLASS page_setup { public: diff --git a/include/xlnt/worksheet/pane.hpp b/include/xlnt/worksheet/pane.hpp index 5bd705f1..bffe58b8 100644 --- a/include/xlnt/worksheet/pane.hpp +++ b/include/xlnt/worksheet/pane.hpp @@ -27,6 +27,9 @@ namespace xlnt { +/// +/// A fixed portion of a worksheet. +/// class XLNT_CLASS pane { }; diff --git a/include/xlnt/worksheet/paper_size.hpp b/include/xlnt/worksheet/paper_size.hpp index 448761ca..de322f86 100644 --- a/include/xlnt/worksheet/paper_size.hpp +++ b/include/xlnt/worksheet/paper_size.hpp @@ -27,6 +27,9 @@ namespace xlnt { +/// +/// The possible paper sizes for printing. +/// enum class XLNT_CLASS paper_size { letter = 1, diff --git a/include/xlnt/worksheet/range.hpp b/include/xlnt/worksheet/range.hpp index e1b06c08..4c0f52e0 100644 --- a/include/xlnt/worksheet/range.hpp +++ b/include/xlnt/worksheet/range.hpp @@ -30,95 +30,22 @@ #include #include #include +#include #include #include namespace xlnt { +/// +/// A range is a 2D collection of cells with defined extens that can be iterated upon. +/// class XLNT_CLASS range { public: - template - class XLNT_CLASS common_iterator : public std::iterator - { - public: - common_iterator(worksheet ws, const range_reference &start_cell, major_order order = major_order::row) - : ws_(ws), current_cell_(start_cell.get_top_left()), range_(start_cell), order_(order) - { - } - - common_iterator(const common_iterator &other) - { - *this = other; - } - - cell_vector operator*(); - - bool operator==(const common_iterator &other) const - { - return ws_ == other.ws_ && current_cell_ == other.current_cell_ && order_ == other.order_; - } - - bool operator!=(const common_iterator &other) const - { - return !(*this == other); - } - - common_iterator &operator--() - { - if (order_ == major_order::row) - { - current_cell_.set_row(current_cell_.get_row() - 1); - } - else - { - current_cell_.set_column_index(current_cell_.get_column_index() - 1); - } - - return *this; - } - - common_iterator operator--(int) - { - iterator old = *this; - --*this; - return old; - } - - common_iterator &operator++() - { - if (order_ == major_order::row) - { - current_cell_.set_row(current_cell_.get_row() + 1); - } - else - { - current_cell_.set_column_index(current_cell_.get_column_index() + 1); - } - - return *this; - } - - common_iterator operator++(int) - { - iterator old = *this; - ++*this; - return old; - } - - friend class common_iterator; - - private: - worksheet ws_; - cell_reference current_cell_; - range_reference range_; - major_order order_; - }; - - using iterator = common_iterator; - using const_iterator = common_iterator; - - range(worksheet ws, const range_reference &reference, major_order order = major_order::row); + using iterator = range_iterator_2d; + using const_iterator = const_range_iterator_2d; + + range(worksheet ws, const range_reference &reference, major_order order = major_order::row, bool skip_null = false); ~range(); @@ -128,10 +55,7 @@ class XLNT_CLASS range bool operator==(const range &comparand) const; - bool operator!=(const range &comparand) const - { - return !(*this == comparand); - } + bool operator!=(const range &comparand) const; cell_vector get_vector(std::size_t vector_index); @@ -150,14 +74,8 @@ class XLNT_CLASS range iterator begin(); iterator end(); - const_iterator begin() const - { - return cbegin(); - } - const_iterator end() const - { - return cend(); - } + const_iterator begin() const; + const_iterator end() const; const_iterator cbegin() const; const_iterator cend() const; @@ -166,6 +84,7 @@ class XLNT_CLASS range worksheet ws_; range_reference ref_; major_order order_; + bool skip_null_; }; } // namespace xlnt diff --git a/include/xlnt/worksheet/range_iterator_2d.hpp b/include/xlnt/worksheet/range_iterator_2d.hpp new file mode 100644 index 00000000..0d39ad1a --- /dev/null +++ b/include/xlnt/worksheet/range_iterator_2d.hpp @@ -0,0 +1,80 @@ +#pragma once + +#include +#include +#include +#include + +namespace xlnt { + +class cell_vector; +class worksheet; + +namespace detail { struct worksheet_impl; } + +/// +/// An iterator used by worksheet and range for traversing +/// a 2D grid of cells by row/column then across that row/column. +/// +class XLNT_CLASS range_iterator_2d : public std::iterator +{ + public: + range_iterator_2d(worksheet &ws, const range_reference &start_cell, major_order order = major_order::row); + + range_iterator_2d(const range_iterator_2d &other); + + cell_vector operator*(); + + bool operator==(const range_iterator_2d &other) const; + + bool operator!=(const range_iterator_2d &other) const; + + range_iterator_2d &operator--(); + + range_iterator_2d operator--(int); + + range_iterator_2d &operator++(); + + range_iterator_2d operator++(int); + + private: + detail::worksheet_impl *ws_; + cell_reference current_cell_; + range_reference range_; + major_order order_; +}; + + +/// +/// A const version of range_iterator_2d which does not allow modification +/// to the dereferenced cell_vector. +/// +class XLNT_CLASS const_range_iterator_2d : public std::iterator +{ + public: + const_range_iterator_2d(const worksheet &ws, const range_reference &start_cell, major_order order = major_order::row); + + const_range_iterator_2d(const const_range_iterator_2d &other); + + const cell_vector operator*(); + + bool operator==(const const_range_iterator_2d &other) const; + + bool operator!=(const const_range_iterator_2d &other) const; + + const_range_iterator_2d &operator--(); + + const_range_iterator_2d operator--(int); + + const_range_iterator_2d &operator++(); + + const_range_iterator_2d operator++(int); + + private: + detail::worksheet_impl *ws_; + cell_reference current_cell_; + range_reference range_; + major_order order_; +}; + +} // namespace xlnt diff --git a/include/xlnt/worksheet/range_reference.hpp b/include/xlnt/worksheet/range_reference.hpp index 39cde98e..f1e347cf 100644 --- a/include/xlnt/worksheet/range_reference.hpp +++ b/include/xlnt/worksheet/range_reference.hpp @@ -27,6 +27,10 @@ namespace xlnt { +/// +/// A range_reference describes a rectangular area of a worksheet with positive +/// width and height defined by a top-left and bottom-right corner. +/// class XLNT_CLASS range_reference { public: @@ -48,69 +52,43 @@ class XLNT_CLASS range_reference std::size_t get_height() const; - cell_reference get_top_left() const - { - return top_left_; - } - cell_reference get_bottom_right() const - { - return bottom_right_; - } - cell_reference &get_top_left() - { - return top_left_; - } - cell_reference &get_bottom_right() - { - return bottom_right_; - } + cell_reference get_top_left() const; + + cell_reference get_bottom_right() const; + + cell_reference &get_top_left(); + + cell_reference &get_bottom_right(); range_reference make_offset(int column_offset, int row_offset) const; std::string to_string() const; bool operator==(const range_reference &comparand) const; - bool operator==(const std::string &reference_string) const - { - return *this == range_reference(reference_string); - } - bool operator==(const char *reference_string) const - { - return *this == std::string(reference_string); - } + + bool operator==(const std::string &reference_string) const; + + bool operator==(const char *reference_string) const; + bool operator!=(const range_reference &comparand) const; - bool operator!=(const std::string &reference_string) const - { - return *this != range_reference(reference_string); - } - bool operator!=(const char *reference_string) const - { - return *this != std::string(reference_string); - } + + bool operator!=(const std::string &reference_string) const; + + bool operator!=(const char *reference_string) const; + + XLNT_FUNCTION friend bool operator==(const std::string &reference_string, const range_reference &ref); + + XLNT_FUNCTION friend bool operator==(const char *reference_string, const range_reference &ref); + + XLNT_FUNCTION friend bool operator!=(const std::string &reference_string, const range_reference &ref); + + XLNT_FUNCTION friend bool operator!=(const char *reference_string, const range_reference &ref); private: cell_reference top_left_; cell_reference bottom_right_; }; -inline bool XLNT_FUNCTION operator==(const std::string &reference_string, const range_reference &ref) -{ - return ref == reference_string; -} -inline bool XLNT_FUNCTION operator==(const char *reference_string, const range_reference &ref) -{ - return ref == reference_string; -} - -inline bool XLNT_FUNCTION operator!=(const std::string &reference_string, const range_reference &ref) -{ - return ref != reference_string; -} - -inline bool XLNT_FUNCTION operator!=(const char *reference_string, const range_reference &ref) -{ - return ref != reference_string; -} } // namespace xlnt diff --git a/include/xlnt/worksheet/row_properties.hpp b/include/xlnt/worksheet/row_properties.hpp index e940a1ad..f4237555 100644 --- a/include/xlnt/worksheet/row_properties.hpp +++ b/include/xlnt/worksheet/row_properties.hpp @@ -27,6 +27,9 @@ namespace xlnt { +/// +/// The properties of a row in a worksheet. +/// class XLNT_CLASS row_properties { public: diff --git a/include/xlnt/worksheet/selection.hpp b/include/xlnt/worksheet/selection.hpp index b567360d..44e06e5e 100644 --- a/include/xlnt/worksheet/selection.hpp +++ b/include/xlnt/worksheet/selection.hpp @@ -26,6 +26,9 @@ namespace xlnt { +/// +/// The selected area of a worksheet. +/// class XLNT_CLASS selection { }; diff --git a/include/xlnt/worksheet/sheet_protection.hpp b/include/xlnt/worksheet/sheet_protection.hpp index 552c0165..b1aacb10 100644 --- a/include/xlnt/worksheet/sheet_protection.hpp +++ b/include/xlnt/worksheet/sheet_protection.hpp @@ -29,16 +29,16 @@ namespace xlnt { +/// +/// Protection applied to a particular worksheet to prevent it from being modified. +/// class XLNT_CLASS sheet_protection { public: static std::string hash_password(const std::string &password); void set_password(const std::string &password); - std::string get_hashed_password() const - { - return hashed_password_; - } + std::string get_hashed_password() const; private: std::string hashed_password_; diff --git a/include/xlnt/worksheet/sheet_state.hpp b/include/xlnt/worksheet/sheet_state.hpp index 423ea03e..b119296c 100644 --- a/include/xlnt/worksheet/sheet_state.hpp +++ b/include/xlnt/worksheet/sheet_state.hpp @@ -27,6 +27,10 @@ namespace xlnt { +/// +/// Defines how a worksheet appears in the workbook. +/// A workbook must have at least one sheet which is visible at all times. +/// enum class XLNT_CLASS sheet_state { visible, diff --git a/include/xlnt/worksheet/sheet_view.hpp b/include/xlnt/worksheet/sheet_view.hpp index c4577bc5..946dbfed 100644 --- a/include/xlnt/worksheet/sheet_view.hpp +++ b/include/xlnt/worksheet/sheet_view.hpp @@ -27,6 +27,10 @@ namespace xlnt { +/// +/// Describes a view of a worksheet. +/// Worksheets can have multiple views which show the data differently. +/// class XLNT_CLASS sheet_view { }; diff --git a/include/xlnt/worksheet/worksheet.hpp b/include/xlnt/worksheet/worksheet.hpp index 17ff223d..ebffb74b 100644 --- a/include/xlnt/worksheet/worksheet.hpp +++ b/include/xlnt/worksheet/worksheet.hpp @@ -23,23 +23,24 @@ // @author: see AUTHORS file #pragma once -#include #include #include #include #include #include -#include +#include #include #include #include #include +#include namespace xlnt { class cell; class cell_reference; +class cell_vector; class column_properties; class comment; class range; @@ -53,11 +54,15 @@ struct date; namespace detail { struct worksheet_impl; } /// -/// A worksheet is a 2D array of cells. +/// A worksheet is a 2D array of cells starting with cell A1 in the top-left corner +/// and extending indefinitely down and right as needed. /// class XLNT_CLASS worksheet { - public: +public: + using iterator = range_iterator_2d; + using const_iterator = const_range_iterator_2d; + worksheet(); worksheet(const worksheet &rhs); worksheet(workbook &parent_workbook, const std::string &title = std::string()); @@ -92,7 +97,6 @@ class XLNT_CLASS worksheet range rows(int row_offset, int column_offset) const; range rows(const std::string &range_string, int row_offset, int column_offset) const; range columns() const; - std::list get_cell_collection(); // properties column_properties &get_column_properties(column_t column); @@ -198,9 +202,23 @@ class XLNT_CLASS worksheet void set_sheet_state(sheet_state state); + iterator begin(); + iterator end(); + + const_iterator begin() const; + const_iterator end() const; + + const_iterator cbegin() const; + const_iterator cend() const; + + range iter_cells(bool skip_null); + private: friend class workbook; friend class cell; + friend class range_iterator_2d; + friend class const_range_iterator_2d; + worksheet(detail::worksheet_impl *d); detail::worksheet_impl *d_; }; diff --git a/include/xlnt/worksheet/worksheet_properties.hpp b/include/xlnt/worksheet/worksheet_properties.hpp index c54b2108..99954f00 100644 --- a/include/xlnt/worksheet/worksheet_properties.hpp +++ b/include/xlnt/worksheet/worksheet_properties.hpp @@ -27,6 +27,9 @@ namespace xlnt { +/// +/// ? +/// class XLNT_CLASS worksheet_properties { }; diff --git a/include/xlnt/xlnt.hpp b/include/xlnt/xlnt.hpp index d8b72340..74c0e80d 100644 --- a/include/xlnt/xlnt.hpp +++ b/include/xlnt/xlnt.hpp @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/source/cell/cell.cpp b/source/cell/cell.cpp index 0bc4421e..6b3ddcd3 100644 --- a/source/cell/cell.cpp +++ b/source/cell/cell.cpp @@ -21,767 +21,6 @@ #include #include -namespace { - -enum class condition_type -{ - less_than, - less_or_equal, - equal, - greater_than, - greater_or_equal, - invalid -}; - -struct section -{ - bool has_value = false; - std::string value; - bool has_color = false; - std::string color; - bool has_condition = false; - condition_type condition = condition_type::invalid; - std::string condition_value; - bool has_locale = false; - std::string locale; - - section &operator=(const section &other) - { - has_value = other.has_value; - value = other.value; - has_color = other.has_color; - color = other.color; - has_condition = other.has_condition; - condition = other.condition; - condition_value = other.condition_value; - has_locale = other.has_locale; - locale = other.locale; - - return *this; - } -}; - -struct format_sections -{ - section first; - section second; - section third; - section fourth; -}; - -// copied from named_range.cpp, keep in sync -/// -/// Return a vector containing string split at each delim. -/// -/// -/// This should maybe be in a utility header so it can be used elsewhere. -/// -std::vector split_string(const std::string &string, char delim) -{ - std::vector split; - std::string::size_type previous_index = 0; - auto separator_index = string.find(delim); - - while (separator_index != std::string::npos) - { - auto part = string.substr(previous_index, separator_index - previous_index); - split.push_back(part); - - previous_index = separator_index + 1; - separator_index = string.find(delim, previous_index); - } - - split.push_back(string.substr(previous_index)); - - return split; -} - -std::vector split_string_any(const std::string &string, const std::string &delims) -{ - std::vector split; - std::string::size_type previous_index = 0; - auto separator_index = string.find_first_of(delims); - - while (separator_index != std::string::npos) - { - auto part = string.substr(previous_index, separator_index - previous_index); - - if (!part.empty()) - { - split.push_back(part); - } - - previous_index = separator_index + 1; - separator_index = string.find_first_of(delims, previous_index); - } - - split.push_back(string.substr(previous_index)); - - return split; -} - -bool is_date_format(const std::string &format_string) -{ - auto not_in = format_string.find_first_not_of("/-:, mMyYdDhHsS"); - return not_in == std::string::npos; -} - -bool is_valid_color(const std::string &color) -{ - static const std::vector *colors = - new std::vector( - { - "Black", - "Green" - "White", - "Blue", - "Magenta", - "Yellow", - "Cyan", - "Red" - }); - - auto compare_color = [&](const std::string &other) { - if (color.size() != other.size()) return false; - - for (std::size_t i = 0; i < color.size(); i++) - { - if (std::toupper(color[i]) != std::toupper(other[i])) - { - return false; - } - } - - return true; - }; - - return std::find_if(colors->begin(), colors->end(), compare_color) != colors->end(); -} - -bool parse_condition(const std::string &string, section &s) -{ - s.has_condition = false; - s.condition = condition_type::invalid; - s.condition_value.clear(); - - if (string[0] == '<') - { - s.has_condition = true; - - if (string[1] == '=') - { - s.condition = condition_type::less_or_equal; - s.condition_value = string.substr(2); - } - else - { - s.condition = condition_type::less_than; - s.condition_value = string.substr(1); - } - } - if (string[0] == '>') - { - s.has_condition = true; - - if (string[1] == '=') - { - s.condition = condition_type::greater_or_equal; - s.condition_value = string.substr(2); - } - else - { - s.condition = condition_type::greater_than; - s.condition_value = string.substr(1); - } - } - else if (string[0] == '=') - { - s.has_condition = true; - s.condition = condition_type::equal; - s.condition_value = string.substr(1); - } - - return s.has_condition; -} - -bool is_hex(char c) -{ - if (c >= 'A' || c <= 'F') return true; - if (c >= '0' || c <= '9') return true; - return false; -} - -const std::unordered_map known_locales() -{ - const std::unordered_map *all = - new std::unordered_map( - { - { 0x401, "Arabic - Saudi Arabia" }, - { 0x402, "Bulgarian" }, - { 0x403, "Catalan" }, - { 0x404, "Chinese - Taiwan" }, - { 0x405, "Czech" }, - { 0x406, "Danish" }, - { 0x407, "German - Germany" }, - { 0x408, "Greek" }, - { 0x409, "English - United States" }, - { 0x410, "Italian - Italy" }, - { 0x411, "Japanese" }, - { 0x412, "Korean" }, - { 0x413, "Dutch - Netherlands" }, - { 0x414, "Norwegian - Bokml" }, - { 0x415, "Polish" }, - { 0x416, "Portuguese - Brazil" }, - { 0x417, "Raeto-Romance" }, - { 0x418, "Romanian - Romania" }, - { 0x419, "Russian" }, - { 0x420, "Urdu" }, - { 0x421, "Indonesian" }, - { 0x422, "Ukrainian" }, - { 0x423, "Belarusian" }, - { 0x424, "Slovenian" }, - { 0x425, "Estonian" }, - { 0x426, "Latvian" }, - { 0x427, "Lithuanian" }, - { 0x428, "Tajik" }, - { 0x429, "Farsi - Persian" }, - { 0x430, "Sesotho (Sutu)" }, - { 0x431, "Tsonga" }, - { 0x432, "Setsuana" }, - { 0x433, "Venda" }, - { 0x434, "Xhosa" }, - { 0x435, "Zulu" }, - { 0x436, "Afrikaans" }, - { 0x437, "Georgian" }, - { 0x438, "Faroese" }, - { 0x439, "Hindi" }, - { 0x440, "Kyrgyz - Cyrillic" }, - { 0x441, "Swahili" }, - { 0x442, "Turkmen" }, - { 0x443, "Uzbek - Latin" }, - { 0x444, "Tatar" }, - { 0x445, "Bengali - India" }, - { 0x446, "Punjabi" }, - { 0x447, "Gujarati" }, - { 0x448, "Oriya" }, - { 0x449, "Tamil" }, - { 0x450, "Mongolian" }, - { 0x451, "Tibetan" }, - { 0x452, "Welsh" }, - { 0x453, "Khmer" }, - { 0x454, "Lao" }, - { 0x455, "Burmese" }, - { 0x456, "Galician" }, - { 0x457, "Konkani" }, - { 0x458, "Manipuri" }, - { 0x459, "Sindhi" }, - { 0x460, "Kashmiri" }, - { 0x461, "Nepali" }, - { 0x462, "Frisian - Netherlands" }, - { 0x464, "Filipino" }, - { 0x465, "Divehi; Dhivehi; Maldivian" }, - { 0x466, "Edo" }, - { 0x470, "Igbo - Nigeria" }, - { 0x474, "Guarani - Paraguay" }, - { 0x476, "Latin" }, - { 0x477, "Somali" }, - { 0x481, "Maori" }, - { 0x801, "Arabic - Iraq" }, - { 0x804, "Chinese - China" }, - { 0x807, "German - Switzerland" }, - { 0x809, "English - Great Britain" }, - { 0x810, "Italian - Switzerland" }, - { 0x813, "Dutch - Belgium" }, - { 0x814, "Norwegian - Nynorsk" }, - { 0x816, "Portuguese - Portugal" }, - { 0x818, "Romanian - Moldova" }, - { 0x819, "Russian - Moldova" }, - { 0x843, "Uzbek - Cyrillic" }, - { 0x845, "Bengali - Bangladesh" }, - { 0x850, "Mongolian" }, - { 0x1001, "Arabic - Libya" }, - { 0x1004, "Chinese - Singapore" }, - { 0x1007, "German - Luxembourg" }, - { 0x1009, "English - Canada" }, - { 0x1401, "Arabic - Algeria" }, - { 0x1404, "Chinese - Macau SAR" }, - { 0x1407, "German - Liechtenstein" }, - { 0x1409, "English - New Zealand" }, - { 0x1801, "Arabic - Morocco" }, - { 0x1809, "English - Ireland" }, - { 0x2001, "Arabic - Oman" }, - { 0x2009, "English - Jamaica" }, - { 0x2401, "Arabic - Yemen" }, - { 0x2409, "English - Caribbean" }, - { 0x2801, "Arabic - Syria" }, - { 0x2809, "English - Belize" }, - { 0x3001, "Arabic - Lebanon" }, - { 0x3009, "English - Zimbabwe" }, - { 0x3401, "Arabic - Kuwait" }, - { 0x3409, "English - Phillippines" }, - { 0x3801, "Arabic - United Arab Emirates" }, - { 0x4001, "Arabic - Qatar" } - }); - - return *all; -} - -bool is_valid_locale(const std::string &locale_string) -{ - std::string country = locale_string.substr(locale_string.find('-') + 1); - - if (country.empty()) - { - return false; - } - - for (auto c : country) - { - if (!is_hex(static_cast(std::toupper(c)))) - { - return false; - } - } - - auto index = std::stoi(country, 0, 16); - - auto known_locales_ = known_locales(); - - if (known_locales_.find(index) == known_locales_.end()) - { - return false; - } - - std::string beginning = locale_string.substr(0, locale_string.find('-')); - - if (beginning.empty() || beginning[0] != '$') - { - return false; - } - - if (beginning.size() == 1) - { - return true; - } - - beginning = beginning.substr(1); - - return true; -} - -section parse_section(const std::string §ion_string) -{ - section s; - - std::string format_part; - std::string bracket_part; - - std::vector bracket_parts; - - bool in_quotes = false; - bool in_brackets = false; - - const std::vector bracket_times = { "h", "hh", "m", "mm", "s", "ss" }; - - for (std::size_t i = 0; i < section_string.size(); i++) - { - if (!in_quotes && section_string[i] == '"') - { - format_part.push_back(section_string[i]); - in_quotes = true; - } - else if (in_quotes && section_string[i] == '"') - { - format_part.push_back(section_string[i]); - - if (i < section_string.size() - 1 && section_string[i + 1] != '"') - { - in_quotes = false; - } - } - else if (!in_brackets && section_string[i] == '[') - { - in_brackets = true; - - for (auto bracket_time : bracket_times) - { - if (i < section_string.size() - bracket_time.size() && - section_string.substr(i + 1, bracket_time.size()) == bracket_time) - { - in_brackets = false; - break; - } - } - } - else if (in_brackets) - { - if (section_string[i] == ']') - { - in_brackets = false; - - if (is_valid_color(bracket_part)) - { - if (s.color.empty()) - { - s.color = bracket_part; - s.has_color = true; - } - else - { - throw std::runtime_error("two colors"); - } - } - else if (is_valid_locale(bracket_part)) - { - if (s.locale.empty()) - { - s.locale = bracket_part; - s.has_locale = true; - } - else - { - throw std::runtime_error("two locales"); - } - } - else if (s.has_condition || !parse_condition(bracket_part, s)) - { - throw std::runtime_error("invalid bracket format"); - } - - bracket_part.clear(); - } - else - { - bracket_part.push_back(section_string[i]); - } - } - else - { - format_part.push_back(section_string[i]); - } - } - - s.value = format_part; - s.has_value = true; - - return s; -} - -format_sections parse_format_sections(const std::string &combined) -{ - format_sections result = {}; - - auto split = split_string(combined, ';'); - - if (split.empty()) - { - throw std::runtime_error("empty string"); - } - - result.first = parse_section(split[0]); - - if (!result.first.has_condition) - { - result.second = result.first; - result.third = result.first; - } - - if (split.size() > 1) - { - result.second = parse_section(split[1]); - } - - if (split.size() > 2) - { - if (result.first.has_condition && !result.second.has_condition) - { - throw std::runtime_error("first two sections should have conditions"); - } - - result.third = parse_section(split[2]); - - if (result.third.has_condition) - { - throw std::runtime_error("third section shouldn't have a condition"); - } - } - - if (split.size() > 3) - { - if (result.first.has_condition) - { - throw std::runtime_error("too many parts"); - } - - result.fourth = parse_section(split[3]); - } - - if (split.size() > 4) - { - throw std::runtime_error("too many parts"); - } - - return result; -} - -std::string format_section(long double number, const section &format, xlnt::calendar base_date) -{ - const std::string unquoted = "$+(:^'{<=-/)!&~}> "; - std::string format_temp = format.value; - std::string result; - - if (is_date_format(format.value)) - { - const std::string date_unquoted = ",-/: "; - - const std::vector dates = { "m", "mm", "mmm", "mmmmm", "mmmmmm", "d", "dd", "ddd", "dddd", "yy", - "yyyy", "h", "[h]", "hh", "m", "[m]", "mm", "s", "[s]", "ss", "AM/PM", - "am/pm", "A/P", "a/p" }; - - const std::vector MonthNames = { "January", "February", "March", - "April", "May", "June", "July", "August", "September", "October", "November", "December" }; - - - auto split = split_string_any(format.value, date_unquoted); - std::string::size_type index = 0, prev = 0; - auto d = xlnt::datetime::from_number(number, base_date); - bool processed_month = false; - - auto lower_string = [](const std::string &s) { - std::string lower; - lower.resize(s.size()); - for (std::size_t i = 0; i < s.size(); i++) - lower[i] = static_cast(std::tolower(s[i])); - return lower; - }; - - for (auto part : split) - { - while (format.value.substr(index, part.size()) != part) - { - index++; - } - - auto between = format.value.substr(prev, index - prev); - result.append(between); - - part = lower_string(part); - - if (part == "m" && !processed_month) - { - result.append(std::to_string(d.month)); - processed_month = true; - } - else if (part == "mm" && !processed_month) - { - if (d.month < 10) - { - result.append("0"); - } - - result.append(std::to_string(d.month)); - processed_month = true; - } - else if (part == "mmm" && !processed_month) - { - result.append(MonthNames.at(static_cast(d.month - 1)).substr(0, 3)); - processed_month = true; - } - else if (part == "mmmm" && !processed_month) - { - result.append(MonthNames.at(static_cast(d.month - 1))); - processed_month = true; - } - else if (part == "d") - { - result.append(std::to_string(d.day)); - } - else if (part == "dd") - { - if (d.day < 10) - { - result.append("0"); - } - - result.append(std::to_string(d.day)); - } - else if (part == "yyyy") - { - result.append(std::to_string(d.year)); - } - - else if (part == "h") - { - result.append(std::to_string(d.hour)); - processed_month = true; - } - else if (part == "hh") - { - if (d.hour < 10) - { - result.append("0"); - } - - result.append(std::to_string(d.hour)); - processed_month = true; - } - else if (part == "m") - { - result.append(std::to_string(d.minute)); - } - else if (part == "mm") - { - if (d.minute < 10) - { - result.append("0"); - } - - result.append(std::to_string(d.minute)); - } - else if (part == "s") - { - result.append(std::to_string(d.second)); - } - else if (part == "ss") - { - if (d.second < 10) - { - result.append("0"); - } - - result.append(std::to_string(d.second)); - } - else if (part == "am/pm" || part == "a/p") - { - if (d.hour < 12) - { - result.append("AM"); - } - else - { - result.append("PM"); - } - } - - index += part.size(); - prev = index; - } - - if (index < format.value.size()) - { - result.append(format.value.substr(index)); - } - } - else if (format.value == "General" || format.value == "0") - { - if (number == static_cast(number)) - { - result = std::to_string(static_cast(number)); - } - else - { - result = std::to_string(number); - } - } - else if (format.value.substr(0, 8) == "#,##0.00" || format.value.substr(0, 9) == "-#,##0.00") - { - if (format.value[0] == '-') - { - result = "-"; - } - - if (format.has_locale && format.locale == "$$-1009") - { - result += "CA$"; - } - else if (format.has_locale && format.locale == "$€-407") - { - result += "€"; - } - else - { - result += "$"; - } - - result += std::to_string(number < 0 ? -number : number); - - auto decimal_pos = result.find('.'); - - if (decimal_pos != std::string::npos) - { - result[decimal_pos] = ','; - decimal_pos += 3; - - while (decimal_pos < result.size()) - { - result.pop_back(); - } - } - } - - return result; -} - -std::string format_section(const std::string &text, const section &format) -{ - auto arobase_index = format.value.find('@'); - - std::string first_part, middle_part, last_part; - - if (arobase_index != std::string::npos) - { - first_part = format.value.substr(0, arobase_index); - middle_part = text; - last_part = format.value.substr(arobase_index + 1); - } - else - { - first_part = format.value; - } - - auto unquote = [](std::string &s) { - if (!s.empty()) - { - if (s.front() != '"' || s.back() != '"') return false; - s = s.substr(0, s.size() - 2); - } - - return true; - }; - - if (!unquote(first_part) || !unquote(last_part)) - { - throw std::runtime_error(std::string("additional text must be enclosed in quotes: ") + format.value); - } - - return first_part + middle_part + last_part; -} - -std::string format_number(long double number, const std::string &format, xlnt::calendar base_date) -{ - auto sections = parse_format_sections(format); - - if (number > 0) - { - return format_section(number, sections.first, base_date); - } - else if (number < 0) - { - return format_section(number, sections.second, base_date); - } - - // number == 0 - return format_section(number, sections.third, base_date); -} - -std::string format_text(const std::string &text, const std::string &format) -{ - if (format == "General") return text; - auto sections = parse_format_sections(format); - return format_section(text, sections.fourth); -} -} - namespace xlnt { const std::unordered_map &cell::error_codes() @@ -1012,25 +251,7 @@ bool cell::is_merged() const bool cell::is_date() const { - if (get_data_type() == type::numeric) - { - auto number_format = get_number_format().get_format_string(); - - if (number_format != "General") - { - try - { - auto sections = parse_format_sections(number_format); - return is_date_format(sections.first.value); - } - catch (std::exception) - { - return false; - } - } - } - - return false; + return get_data_type() == type::numeric && get_number_format().is_date_format(); } cell_reference cell::get_reference() const @@ -1448,11 +669,11 @@ std::string cell::to_string() const case cell::type::null: return ""; case cell::type::numeric: - return format_number(get_value(), nf.get_format_string(), get_base_date()); + return get_number_format().format(get_value(), get_base_date()); case cell::type::string: case cell::type::formula: case cell::type::error: - return format_text(get_value(), nf.get_format_string()); + return get_number_format().format(get_value()); case cell::type::boolean: return get_value() == 0 ? "FALSE" : "TRUE"; default: diff --git a/source/cell/types.cpp b/source/cell/index_types.cpp similarity index 99% rename from source/cell/types.cpp rename to source/cell/index_types.cpp index c5d0af5f..7dbc8c1d 100644 --- a/source/cell/types.cpp +++ b/source/cell/index_types.cpp @@ -1,6 +1,6 @@ #include -#include +#include #include #include diff --git a/source/detail/cell_impl.hpp b/source/detail/cell_impl.hpp index 5e2e63d6..49beea12 100644 --- a/source/detail/cell_impl.hpp +++ b/source/detail/cell_impl.hpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include #include #include diff --git a/source/detail/constants.hpp b/source/detail/constants.hpp index 9c1676c6..5eb27c47 100644 --- a/source/detail/constants.hpp +++ b/source/detail/constants.hpp @@ -3,7 +3,7 @@ #include #include -#include +#include namespace xlnt { diff --git a/source/packaging/relationship.cpp b/source/packaging/relationship.cpp index 8a030d6d..f48555f4 100644 --- a/source/packaging/relationship.cpp +++ b/source/packaging/relationship.cpp @@ -16,4 +16,119 @@ relationship::relationship() { } +relationship::type relationship::type_from_string(const std::string &type_string) +{ + if (type_string == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties") + { + return type::extended_properties; + } + else if (type_string == "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties") + { + return type::core_properties; + } + else if (type_string == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument") + { + return type::office_document; + } + else if (type_string == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet") + { + return type::worksheet; + } + else if (type_string == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings") + { + return type::shared_strings; + } + else if (type_string == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles") + { + return type::styles; + } + else if (type_string == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme") + { + return type::theme; + } + else if (type_string == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink") + { + return type::hyperlink; + } + else if (type_string == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chartsheet") + { + return type::chartsheet; + } + else if (type_string == "http://schemas.openxmlformats.org/officeDocument/2006/relationships/customXml") + { + return type::custom_xml; + } + + return type::invalid; +} + +std::string relationship::type_to_string(type t) +{ + switch (t) + { + case type::extended_properties: + return "http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties"; + case type::core_properties: + return "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties"; + case type::office_document: + return "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"; + case type::worksheet: + return "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet"; + case type::shared_strings: + return "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings"; + case type::styles: + return "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles"; + case type::theme: + return "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme"; + case type::hyperlink: + return "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink"; + case type::chartsheet: + return "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chartsheet"; + case type::custom_xml: + return "http://schemas.openxmlformats.org/officeDocument/2006/relationships/customXml"; + default: + return "??"; + } +} + +relationship::relationship(const std::string &t, const std::string &r_id, const std::string &target_uri) + : relationship(type_from_string(t), r_id, target_uri) +{ +} + +std::string relationship::get_id() const +{ + return id_; +} + +std::string relationship::get_source_uri() const +{ + return source_uri_; +} + +target_mode relationship::get_target_mode() const +{ + return target_mode_; +} + +std::string relationship::get_target_uri() const +{ + return target_uri_; +} + +relationship::type relationship::get_type() const +{ + return type_; +} +std::string relationship::get_type_string() const +{ + return type_to_string(type_); +} + +bool relationship::operator==(const relationship &rhs) const +{ + return type_ == rhs.type_ && id_ == rhs.id_ && source_uri_ == rhs.source_uri_ && + target_uri_ == rhs.target_uri_ && target_mode_ == rhs.target_mode_; +} + } // namespace xlnt diff --git a/source/serialization/style_serializer.cpp b/source/serialization/style_serializer.cpp index e8ccabb5..8a312504 100644 --- a/source/serialization/style_serializer.cpp +++ b/source/serialization/style_serializer.cpp @@ -160,19 +160,19 @@ alignment style_serializer::read_alignment(const xml_node &alignment_node) if (vertical == "bottom") { - align.set_vertical(alignment::vertical_alignment::bottom); + align.set_vertical(vertical_alignment::bottom); } else if (vertical == "center") { - align.set_vertical(alignment::vertical_alignment::center); + align.set_vertical(vertical_alignment::center); } else if (vertical == "justify") { - align.set_vertical(alignment::vertical_alignment::justify); + align.set_vertical(vertical_alignment::justify); } else if (vertical == "top") { - align.set_vertical(alignment::vertical_alignment::top); + align.set_vertical(vertical_alignment::top); } else { @@ -188,27 +188,27 @@ alignment style_serializer::read_alignment(const xml_node &alignment_node) if (horizontal == "left") { - align.set_horizontal(alignment::horizontal_alignment::left); + align.set_horizontal(horizontal_alignment::left); } else if (horizontal == "center") { - align.set_horizontal(alignment::horizontal_alignment::center); + align.set_horizontal(horizontal_alignment::center); } else if (horizontal == "center-continuous") { - align.set_horizontal(alignment::horizontal_alignment::center_continuous); + align.set_horizontal(horizontal_alignment::center_continuous); } else if (horizontal == "right") { - align.set_horizontal(alignment::horizontal_alignment::right); + align.set_horizontal(horizontal_alignment::right); } else if (horizontal == "justify") { - align.set_horizontal(alignment::horizontal_alignment::justify); + align.set_horizontal(horizontal_alignment::justify); } else if (horizontal == "general") { - align.set_horizontal(alignment::horizontal_alignment::general); + align.set_horizontal(horizontal_alignment::general); } else { @@ -885,16 +885,16 @@ xml_document style_serializer::write_stylesheet() const { switch (style.alignment_.get_vertical()) { - case alignment::vertical_alignment::bottom: + case vertical_alignment::bottom: alignment_node.add_attribute("vertical", "bottom"); break; - case alignment::vertical_alignment::center: + case vertical_alignment::center: alignment_node.add_attribute("vertical", "center"); break; - case alignment::vertical_alignment::justify: + case vertical_alignment::justify: alignment_node.add_attribute("vertical", "justify"); break; - case alignment::vertical_alignment::top: + case vertical_alignment::top: alignment_node.add_attribute("vertical", "top"); break; default: @@ -906,22 +906,22 @@ xml_document style_serializer::write_stylesheet() const { switch (style.alignment_.get_horizontal()) { - case alignment::horizontal_alignment::center: + case horizontal_alignment::center: alignment_node.add_attribute("horizontal", "center"); break; - case alignment::horizontal_alignment::center_continuous: + case horizontal_alignment::center_continuous: alignment_node.add_attribute("horizontal", "center_continuous"); break; - case alignment::horizontal_alignment::general: + case horizontal_alignment::general: alignment_node.add_attribute("horizontal", "general"); break; - case alignment::horizontal_alignment::justify: + case horizontal_alignment::justify: alignment_node.add_attribute("horizontal", "justify"); break; - case alignment::horizontal_alignment::left: + case horizontal_alignment::left: alignment_node.add_attribute("horizontal", "left"); break; - case alignment::horizontal_alignment::right: + case horizontal_alignment::right: alignment_node.add_attribute("horizontal", "right"); break; default: diff --git a/source/styles/alignment.cpp b/source/styles/alignment.cpp new file mode 100644 index 00000000..bc9cb3d7 --- /dev/null +++ b/source/styles/alignment.cpp @@ -0,0 +1,86 @@ +// Copyright (c) 2014-2015 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 + +#include + +namespace xlnt { + +bool alignment::get_wrap_text() const +{ + return wrap_text_; +} + +void alignment::set_wrap_text(bool wrap_text) +{ + wrap_text_ = wrap_text; +} + +bool alignment::has_horizontal() const +{ + return horizontal_ != horizontal_alignment::none; +} + +horizontal_alignment alignment::get_horizontal() const +{ + return horizontal_; +} + +void alignment::set_horizontal(horizontal_alignment horizontal) +{ + horizontal_ = horizontal; +} + +bool alignment::has_vertical() const +{ + return vertical_ != vertical_alignment::none; +} + +vertical_alignment alignment::get_vertical() const +{ + return vertical_; +} + +void alignment::set_vertical(vertical_alignment vertical) +{ + vertical_ = vertical; +} + +bool alignment::operator==(const alignment &other) const +{ + return hash() == other.hash(); +} + +std::size_t alignment::hash() const +{ + std::size_t seed = 0; + hash_combine(seed, wrap_text_); + hash_combine(seed, shrink_to_fit_); + hash_combine(seed, static_cast(horizontal_)); + hash_combine(seed, static_cast(vertical_)); + hash_combine(seed, text_rotation_); + hash_combine(seed, indent_); + return seed; +} + +} // namespace xlnt diff --git a/source/styles/border.cpp b/source/styles/border.cpp new file mode 100644 index 00000000..21275d22 --- /dev/null +++ b/source/styles/border.cpp @@ -0,0 +1,36 @@ +#include + +namespace xlnt { + +bool border::operator==(const border &other) const +{ + return hash() == other.hash(); +} + +std::size_t border::hash() const +{ + std::size_t seed = 0; + + hash_combine(seed, start_assigned); + if (start_assigned) hash_combine(seed, start.hash()); + hash_combine(seed, end_assigned); + if (end_assigned) hash_combine(seed, end.hash()); + hash_combine(seed, left_assigned); + if (left_assigned) hash_combine(seed, left.hash()); + hash_combine(seed, right_assigned); + if (right_assigned) hash_combine(seed, right.hash()); + hash_combine(seed, top_assigned); + if (top_assigned) hash_combine(seed, top.hash()); + hash_combine(seed, bottom_assigned); + if (bottom_assigned) hash_combine(seed, bottom.hash()); + hash_combine(seed, diagonal_assigned); + if (diagonal_assigned) hash_combine(seed, diagonal.hash()); + hash_combine(seed, vertical_assigned); + if (vertical_assigned) hash_combine(seed, vertical.hash()); + hash_combine(seed, horizontal_assigned); + if (horizontal_assigned) hash_combine(seed, horizontal.hash()); + + return seed; +} + +} // namespace xlnt diff --git a/source/styles/number_format.cpp b/source/styles/number_format.cpp index 18edd7cc..a17543e0 100644 --- a/source/styles/number_format.cpp +++ b/source/styles/number_format.cpp @@ -1,6 +1,7 @@ #include #include +#include #include #include @@ -65,6 +66,764 @@ const std::unordered_map &builtin_formats() return *formats; } +enum class condition_type +{ + less_than, + less_or_equal, + equal, + greater_than, + greater_or_equal, + invalid +}; + +struct section +{ + bool has_value = false; + std::string value; + bool has_color = false; + std::string color; + bool has_condition = false; + condition_type condition = condition_type::invalid; + std::string condition_value; + bool has_locale = false; + std::string locale; + + section &operator=(const section &other) + { + has_value = other.has_value; + value = other.value; + has_color = other.has_color; + color = other.color; + has_condition = other.has_condition; + condition = other.condition; + condition_value = other.condition_value; + has_locale = other.has_locale; + locale = other.locale; + + return *this; + } +}; + +struct format_sections +{ + section first; + section second; + section third; + section fourth; +}; + +// copied from named_range.cpp, keep in sync +/// +/// Return a vector containing string split at each delim. +/// +/// +/// This should maybe be in a utility header so it can be used elsewhere. +/// +std::vector split_string(const std::string &string, char delim) +{ + std::vector split; + std::string::size_type previous_index = 0; + auto separator_index = string.find(delim); + + while (separator_index != std::string::npos) + { + auto part = string.substr(previous_index, separator_index - previous_index); + split.push_back(part); + + previous_index = separator_index + 1; + separator_index = string.find(delim, previous_index); + } + + split.push_back(string.substr(previous_index)); + + return split; +} + +std::vector split_string_any(const std::string &string, const std::string &delims) +{ + std::vector split; + std::string::size_type previous_index = 0; + auto separator_index = string.find_first_of(delims); + + while (separator_index != std::string::npos) + { + auto part = string.substr(previous_index, separator_index - previous_index); + + if (!part.empty()) + { + split.push_back(part); + } + + previous_index = separator_index + 1; + separator_index = string.find_first_of(delims, previous_index); + } + + split.push_back(string.substr(previous_index)); + + return split; +} + +bool is_date_format(const std::string &format_string) +{ + auto not_in = format_string.find_first_not_of("/-:, mMyYdDhHsS"); + return not_in == std::string::npos; +} + +bool is_valid_color(const std::string &color) +{ + static const std::vector *colors = + new std::vector( + { + "Black", + "Green" + "White", + "Blue", + "Magenta", + "Yellow", + "Cyan", + "Red" + }); + + auto compare_color = [&](const std::string &other) { + if (color.size() != other.size()) return false; + + for (std::size_t i = 0; i < color.size(); i++) + { + if (std::toupper(color[i]) != std::toupper(other[i])) + { + return false; + } + } + + return true; + }; + + return std::find_if(colors->begin(), colors->end(), compare_color) != colors->end(); +} + +bool parse_condition(const std::string &string, section &s) +{ + s.has_condition = false; + s.condition = condition_type::invalid; + s.condition_value.clear(); + + if (string[0] == '<') + { + s.has_condition = true; + + if (string[1] == '=') + { + s.condition = condition_type::less_or_equal; + s.condition_value = string.substr(2); + } + else + { + s.condition = condition_type::less_than; + s.condition_value = string.substr(1); + } + } + if (string[0] == '>') + { + s.has_condition = true; + + if (string[1] == '=') + { + s.condition = condition_type::greater_or_equal; + s.condition_value = string.substr(2); + } + else + { + s.condition = condition_type::greater_than; + s.condition_value = string.substr(1); + } + } + else if (string[0] == '=') + { + s.has_condition = true; + s.condition = condition_type::equal; + s.condition_value = string.substr(1); + } + + return s.has_condition; +} + +bool is_hex(char c) +{ + if (c >= 'A' || c <= 'F') return true; + if (c >= '0' || c <= '9') return true; + return false; +} + +const std::unordered_map known_locales() +{ + const std::unordered_map *all = + new std::unordered_map( + { + { 0x401, "Arabic - Saudi Arabia" }, + { 0x402, "Bulgarian" }, + { 0x403, "Catalan" }, + { 0x404, "Chinese - Taiwan" }, + { 0x405, "Czech" }, + { 0x406, "Danish" }, + { 0x407, "German - Germany" }, + { 0x408, "Greek" }, + { 0x409, "English - United States" }, + { 0x410, "Italian - Italy" }, + { 0x411, "Japanese" }, + { 0x412, "Korean" }, + { 0x413, "Dutch - Netherlands" }, + { 0x414, "Norwegian - Bokml" }, + { 0x415, "Polish" }, + { 0x416, "Portuguese - Brazil" }, + { 0x417, "Raeto-Romance" }, + { 0x418, "Romanian - Romania" }, + { 0x419, "Russian" }, + { 0x420, "Urdu" }, + { 0x421, "Indonesian" }, + { 0x422, "Ukrainian" }, + { 0x423, "Belarusian" }, + { 0x424, "Slovenian" }, + { 0x425, "Estonian" }, + { 0x426, "Latvian" }, + { 0x427, "Lithuanian" }, + { 0x428, "Tajik" }, + { 0x429, "Farsi - Persian" }, + { 0x430, "Sesotho (Sutu)" }, + { 0x431, "Tsonga" }, + { 0x432, "Setsuana" }, + { 0x433, "Venda" }, + { 0x434, "Xhosa" }, + { 0x435, "Zulu" }, + { 0x436, "Afrikaans" }, + { 0x437, "Georgian" }, + { 0x438, "Faroese" }, + { 0x439, "Hindi" }, + { 0x440, "Kyrgyz - Cyrillic" }, + { 0x441, "Swahili" }, + { 0x442, "Turkmen" }, + { 0x443, "Uzbek - Latin" }, + { 0x444, "Tatar" }, + { 0x445, "Bengali - India" }, + { 0x446, "Punjabi" }, + { 0x447, "Gujarati" }, + { 0x448, "Oriya" }, + { 0x449, "Tamil" }, + { 0x450, "Mongolian" }, + { 0x451, "Tibetan" }, + { 0x452, "Welsh" }, + { 0x453, "Khmer" }, + { 0x454, "Lao" }, + { 0x455, "Burmese" }, + { 0x456, "Galician" }, + { 0x457, "Konkani" }, + { 0x458, "Manipuri" }, + { 0x459, "Sindhi" }, + { 0x460, "Kashmiri" }, + { 0x461, "Nepali" }, + { 0x462, "Frisian - Netherlands" }, + { 0x464, "Filipino" }, + { 0x465, "Divehi; Dhivehi; Maldivian" }, + { 0x466, "Edo" }, + { 0x470, "Igbo - Nigeria" }, + { 0x474, "Guarani - Paraguay" }, + { 0x476, "Latin" }, + { 0x477, "Somali" }, + { 0x481, "Maori" }, + { 0x801, "Arabic - Iraq" }, + { 0x804, "Chinese - China" }, + { 0x807, "German - Switzerland" }, + { 0x809, "English - Great Britain" }, + { 0x810, "Italian - Switzerland" }, + { 0x813, "Dutch - Belgium" }, + { 0x814, "Norwegian - Nynorsk" }, + { 0x816, "Portuguese - Portugal" }, + { 0x818, "Romanian - Moldova" }, + { 0x819, "Russian - Moldova" }, + { 0x843, "Uzbek - Cyrillic" }, + { 0x845, "Bengali - Bangladesh" }, + { 0x850, "Mongolian" }, + { 0x1001, "Arabic - Libya" }, + { 0x1004, "Chinese - Singapore" }, + { 0x1007, "German - Luxembourg" }, + { 0x1009, "English - Canada" }, + { 0x1401, "Arabic - Algeria" }, + { 0x1404, "Chinese - Macau SAR" }, + { 0x1407, "German - Liechtenstein" }, + { 0x1409, "English - New Zealand" }, + { 0x1801, "Arabic - Morocco" }, + { 0x1809, "English - Ireland" }, + { 0x2001, "Arabic - Oman" }, + { 0x2009, "English - Jamaica" }, + { 0x2401, "Arabic - Yemen" }, + { 0x2409, "English - Caribbean" }, + { 0x2801, "Arabic - Syria" }, + { 0x2809, "English - Belize" }, + { 0x3001, "Arabic - Lebanon" }, + { 0x3009, "English - Zimbabwe" }, + { 0x3401, "Arabic - Kuwait" }, + { 0x3409, "English - Phillippines" }, + { 0x3801, "Arabic - United Arab Emirates" }, + { 0x4001, "Arabic - Qatar" } + }); + + return *all; +} + +bool is_valid_locale(const std::string &locale_string) +{ + std::string country = locale_string.substr(locale_string.find('-') + 1); + + if (country.empty()) + { + return false; + } + + for (auto c : country) + { + if (!is_hex(static_cast(std::toupper(c)))) + { + return false; + } + } + + auto index = std::stoi(country, 0, 16); + + auto known_locales_ = known_locales(); + + if (known_locales_.find(index) == known_locales_.end()) + { + return false; + } + + std::string beginning = locale_string.substr(0, locale_string.find('-')); + + if (beginning.empty() || beginning[0] != '$') + { + return false; + } + + if (beginning.size() == 1) + { + return true; + } + + beginning = beginning.substr(1); + + return true; +} + +section parse_section(const std::string §ion_string) +{ + section s; + + std::string format_part; + std::string bracket_part; + + std::vector bracket_parts; + + bool in_quotes = false; + bool in_brackets = false; + + const std::vector bracket_times = { "h", "hh", "m", "mm", "s", "ss" }; + + for (std::size_t i = 0; i < section_string.size(); i++) + { + if (!in_quotes && section_string[i] == '"') + { + format_part.push_back(section_string[i]); + in_quotes = true; + } + else if (in_quotes && section_string[i] == '"') + { + format_part.push_back(section_string[i]); + + if (i < section_string.size() - 1 && section_string[i + 1] != '"') + { + in_quotes = false; + } + } + else if (!in_brackets && section_string[i] == '[') + { + in_brackets = true; + + for (auto bracket_time : bracket_times) + { + if (i < section_string.size() - bracket_time.size() && + section_string.substr(i + 1, bracket_time.size()) == bracket_time) + { + in_brackets = false; + break; + } + } + } + else if (in_brackets) + { + if (section_string[i] == ']') + { + in_brackets = false; + + if (is_valid_color(bracket_part)) + { + if (s.color.empty()) + { + s.color = bracket_part; + s.has_color = true; + } + else + { + throw std::runtime_error("two colors"); + } + } + else if (is_valid_locale(bracket_part)) + { + if (s.locale.empty()) + { + s.locale = bracket_part; + s.has_locale = true; + } + else + { + throw std::runtime_error("two locales"); + } + } + else if (s.has_condition || !parse_condition(bracket_part, s)) + { + throw std::runtime_error("invalid bracket format"); + } + + bracket_part.clear(); + } + else + { + bracket_part.push_back(section_string[i]); + } + } + else + { + format_part.push_back(section_string[i]); + } + } + + s.value = format_part; + s.has_value = true; + + return s; +} + +format_sections parse_format_sections(const std::string &combined) +{ + format_sections result = {}; + + auto split = split_string(combined, ';'); + + if (split.empty()) + { + throw std::runtime_error("empty string"); + } + + result.first = parse_section(split[0]); + + if (!result.first.has_condition) + { + result.second = result.first; + result.third = result.first; + } + + if (split.size() > 1) + { + result.second = parse_section(split[1]); + } + + if (split.size() > 2) + { + if (result.first.has_condition && !result.second.has_condition) + { + throw std::runtime_error("first two sections should have conditions"); + } + + result.third = parse_section(split[2]); + + if (result.third.has_condition) + { + throw std::runtime_error("third section shouldn't have a condition"); + } + } + + if (split.size() > 3) + { + if (result.first.has_condition) + { + throw std::runtime_error("too many parts"); + } + + result.fourth = parse_section(split[3]); + } + + if (split.size() > 4) + { + throw std::runtime_error("too many parts"); + } + + return result; +} + +std::string format_section(long double number, const section &format, xlnt::calendar base_date) +{ + const std::string unquoted = "$+(:^'{<=-/)!&~}> "; + std::string format_temp = format.value; + std::string result; + + if (is_date_format(format.value)) + { + const std::string date_unquoted = ",-/: "; + + const std::vector dates = { "m", "mm", "mmm", "mmmmm", "mmmmmm", "d", "dd", "ddd", "dddd", "yy", + "yyyy", "h", "[h]", "hh", "m", "[m]", "mm", "s", "[s]", "ss", "AM/PM", + "am/pm", "A/P", "a/p" }; + + const std::vector MonthNames = { "January", "February", "March", + "April", "May", "June", "July", "August", "September", "October", "November", "December" }; + + + auto split = split_string_any(format.value, date_unquoted); + std::string::size_type index = 0, prev = 0; + auto d = xlnt::datetime::from_number(number, base_date); + bool processed_month = false; + + auto lower_string = [](const std::string &s) { + std::string lower; + lower.resize(s.size()); + for (std::size_t i = 0; i < s.size(); i++) + lower[i] = static_cast(std::tolower(s[i])); + return lower; + }; + + for (auto part : split) + { + while (format.value.substr(index, part.size()) != part) + { + index++; + } + + auto between = format.value.substr(prev, index - prev); + result.append(between); + + part = lower_string(part); + + if (part == "m" && !processed_month) + { + result.append(std::to_string(d.month)); + processed_month = true; + } + else if (part == "mm" && !processed_month) + { + if (d.month < 10) + { + result.append("0"); + } + + result.append(std::to_string(d.month)); + processed_month = true; + } + else if (part == "mmm" && !processed_month) + { + result.append(MonthNames.at(static_cast(d.month - 1)).substr(0, 3)); + processed_month = true; + } + else if (part == "mmmm" && !processed_month) + { + result.append(MonthNames.at(static_cast(d.month - 1))); + processed_month = true; + } + else if (part == "d") + { + result.append(std::to_string(d.day)); + } + else if (part == "dd") + { + if (d.day < 10) + { + result.append("0"); + } + + result.append(std::to_string(d.day)); + } + else if (part == "yyyy") + { + result.append(std::to_string(d.year)); + } + + else if (part == "h") + { + result.append(std::to_string(d.hour)); + processed_month = true; + } + else if (part == "hh") + { + if (d.hour < 10) + { + result.append("0"); + } + + result.append(std::to_string(d.hour)); + processed_month = true; + } + else if (part == "m") + { + result.append(std::to_string(d.minute)); + } + else if (part == "mm") + { + if (d.minute < 10) + { + result.append("0"); + } + + result.append(std::to_string(d.minute)); + } + else if (part == "s") + { + result.append(std::to_string(d.second)); + } + else if (part == "ss") + { + if (d.second < 10) + { + result.append("0"); + } + + result.append(std::to_string(d.second)); + } + else if (part == "am/pm" || part == "a/p") + { + if (d.hour < 12) + { + result.append("AM"); + } + else + { + result.append("PM"); + } + } + + index += part.size(); + prev = index; + } + + if (index < format.value.size()) + { + result.append(format.value.substr(index)); + } + } + else if (format.value == "General" || format.value == "0") + { + if (number == static_cast(number)) + { + result = std::to_string(static_cast(number)); + } + else + { + result = std::to_string(number); + } + } + else if (format.value.substr(0, 8) == "#,##0.00" || format.value.substr(0, 9) == "-#,##0.00") + { + if (format.value[0] == '-') + { + result = "-"; + } + + if (format.has_locale && format.locale == "$$-1009") + { + result += "CA$"; + } + else if (format.has_locale && format.locale == "$€-407") + { + result += "€"; + } + else + { + result += "$"; + } + + result += std::to_string(number < 0 ? -number : number); + + auto decimal_pos = result.find('.'); + + if (decimal_pos != std::string::npos) + { + result[decimal_pos] = ','; + decimal_pos += 3; + + while (decimal_pos < result.size()) + { + result.pop_back(); + } + } + } + + return result; +} + +std::string format_section(const std::string &text, const section &format) +{ + auto arobase_index = format.value.find('@'); + + std::string first_part, middle_part, last_part; + + if (arobase_index != std::string::npos) + { + first_part = format.value.substr(0, arobase_index); + middle_part = text; + last_part = format.value.substr(arobase_index + 1); + } + else + { + first_part = format.value; + } + + auto unquote = [](std::string &s) { + if (!s.empty()) + { + if (s.front() != '"' || s.back() != '"') return false; + s = s.substr(0, s.size() - 2); + } + + return true; + }; + + if (!unquote(first_part) || !unquote(last_part)) + { + throw std::runtime_error(std::string("additional text must be enclosed in quotes: ") + format.value); + } + + return first_part + middle_part + last_part; +} + +std::string format_number(long double number, const std::string &format, xlnt::calendar base_date) +{ + auto sections = parse_format_sections(format); + + if (number > 0) + { + return format_section(number, sections.first, base_date); + } + else if (number < 0) + { + return format_section(number, sections.second, base_date); + } + + // number == 0 + return format_section(number, sections.third, base_date); +} + +std::string format_text(const std::string &text, const std::string &format) +{ + if (format == "General") return text; + auto sections = parse_format_sections(format); + return format_section(text, sections.fourth); +} + } // namespace namespace xlnt { @@ -340,4 +1099,58 @@ void number_format::set_format_string(const std::string &format_string, std::siz id_set_ = true; } +bool number_format::has_id() const +{ + return id_set_; +} + +void number_format::set_id(std::size_t id) +{ + id_ = id; + id_set_ = true; +} + +std::size_t number_format::get_id() const +{ + if(!id_set_) + { + throw std::runtime_error("number format doesn't have an id"); + } + + return id_; +} + +bool number_format::is_date_format() const +{ + if (format_string_ != "General") + { + try + { + auto sections = parse_format_sections(format_string_); + return ::is_date_format(sections.first.value); + } + catch (std::exception) + { + return false; + } + } + + return false; +} + +std::string number_format::format(const std::string &text) const +{ + return format_text(text, format_string_); +} + +std::string number_format::format(long double number, calendar base_date) const +{ + return format_number(number, format_string_, base_date); +} + +bool number_format::operator==(const number_format &other) const +{ + return hash() == other.hash(); +} + } // namespace xlnt diff --git a/source/worksheet/cell_vector.cpp b/source/worksheet/cell_vector.cpp index 6f76220b..a724ceec 100644 --- a/source/worksheet/cell_vector.cpp +++ b/source/worksheet/cell_vector.cpp @@ -3,7 +3,130 @@ namespace xlnt { -template <> +cell_vector::iterator::iterator(worksheet ws, const cell_reference &start_cell, major_order order) + : ws_(ws), current_cell_(start_cell), range_(start_cell.to_range()), order_(order) +{ +} + +cell_vector::iterator::iterator(const iterator &other) +{ + *this = other; +} + +bool cell_vector::iterator::operator==(const iterator &other) const +{ + return ws_ == other.ws_ && current_cell_ == other.current_cell_ && order_ == other.order_; +} + +bool cell_vector::iterator::operator!=(const iterator &other) const +{ + return !(*this == other); +} + +cell_vector::iterator &cell_vector::iterator::operator--() +{ + if (order_ == major_order::row) + { + current_cell_.set_column_index(current_cell_.get_column_index() - 1); + } + else + { + current_cell_.set_row(current_cell_.get_row() - 1); + } + + return *this; +} + +cell_vector::iterator cell_vector::iterator::operator--(int) +{ + iterator old = *this; + --*this; + return old; +} + +cell_vector::iterator &cell_vector::iterator::operator++() +{ + if (order_ == major_order::row) + { + current_cell_.set_column_index(current_cell_.get_column_index() + 1); + } + else + { + current_cell_.set_row(current_cell_.get_row() + 1); + } + + return *this; +} + +cell_vector::iterator cell_vector::iterator::operator++(int) +{ + iterator old = *this; + ++*this; + return old; +} + +cell_vector::const_iterator::const_iterator(worksheet ws, const cell_reference &start_cell, major_order order) + : ws_(ws), current_cell_(start_cell), range_(start_cell.to_range()), order_(order) +{ +} + +cell_vector::const_iterator::const_iterator(const const_iterator &other) +{ + *this = other; +} + +bool cell_vector::const_iterator::operator==(const const_iterator &other) const +{ + return ws_ == other.ws_ && current_cell_ == other.current_cell_ && order_ == other.order_; +} + +bool cell_vector::const_iterator::operator!=(const const_iterator &other) const +{ + return !(*this == other); +} + +cell_vector::const_iterator &cell_vector::const_iterator::operator--() +{ + if (order_ == major_order::row) + { + current_cell_.set_column_index(current_cell_.get_column_index() - 1); + } + else + { + current_cell_.set_row(current_cell_.get_row() - 1); + } + + return *this; +} + +cell_vector::const_iterator cell_vector::const_iterator::operator--(int) +{ + const_iterator old = *this; + --*this; + return old; +} + +cell_vector::const_iterator &cell_vector::const_iterator::operator++() +{ + if (order_ == major_order::row) + { + current_cell_.set_column_index(current_cell_.get_column_index() + 1); + } + else + { + current_cell_.set_row(current_cell_.get_row() + 1); + } + + return *this; +} + +cell_vector::const_iterator cell_vector::const_iterator::operator++(int) +{ + const_iterator old = *this; + ++*this; + return old; +} + cell cell_vector::iterator::operator*() { return ws_[current_cell_]; diff --git a/source/worksheet/footer.cpp b/source/worksheet/footer.cpp new file mode 100644 index 00000000..31716bab --- /dev/null +++ b/source/worksheet/footer.cpp @@ -0,0 +1,34 @@ +#include + +namespace xlnt { + +void footer::set_text(const std::string &text) +{ + default_ = false; + text_ = text; +} + +void footer::set_font_name(const std::string &font_name) +{ + default_ = false; + font_name_ = font_name; +} + +void footer::set_font_size(std::size_t font_size) +{ + default_ = false; + font_size_ = font_size; +} + +void footer::set_font_color(const std::string &font_color) +{ + default_ = false; + font_color_ = font_color; +} + +bool footer::is_default() const +{ + return default_; +} + +} // namespace xlnt diff --git a/source/worksheet/header.cpp b/source/worksheet/header.cpp new file mode 100644 index 00000000..413f9630 --- /dev/null +++ b/source/worksheet/header.cpp @@ -0,0 +1,34 @@ +#include + +namespace xlnt { + +void header::set_text(const std::string &text) +{ + default_ = false; + text_ = text; +} + +void header::set_font_name(const std::string &font_name) +{ + default_ = false; + font_name_ = font_name; +} + +void header::set_font_size(std::size_t font_size) +{ + default_ = false; + font_size_ = font_size; +} + +void header::set_font_color(const std::string &font_color) +{ + default_ = false; + font_color_ = font_color; +} + +bool header::is_default() const +{ + return default_; +} + +} // namespace xlnt \ No newline at end of file diff --git a/source/worksheet/range.cpp b/source/worksheet/range.cpp index 490443e2..753015a1 100644 --- a/source/worksheet/range.cpp +++ b/source/worksheet/range.cpp @@ -5,8 +5,8 @@ namespace xlnt { -range::range(worksheet ws, const range_reference &reference, major_order order) - : ws_(ws), ref_(reference), order_(order) +range::range(worksheet ws, const range_reference &reference, major_order order, bool skip_null) + : ws_(ws), ref_(reference), order_(order), skip_null_(skip_null) { } @@ -133,7 +133,6 @@ range::const_iterator range::cend() const return const_iterator(ws_, range_reference(top_right, bottom_right), order_); } -template <> cell_vector range::iterator::operator*() { if (order_ == major_order::row) @@ -148,4 +147,147 @@ cell_vector range::iterator::operator*() return cell_vector(ws_, reference, order_); } +range_iterator_2d::range_iterator_2d(worksheet &ws, const range_reference &start_cell, major_order order) + : ws_(ws.d_), current_cell_(start_cell.get_top_left()), range_(start_cell), order_(order) +{ +} + +range_iterator_2d::range_iterator_2d(const range_iterator_2d &other) +{ + *this = other; +} + +bool range_iterator_2d::operator==(const range_iterator_2d &other) const +{ + return ws_ == other.ws_ && current_cell_ == other.current_cell_ && order_ == other.order_; +} + +bool range_iterator_2d::operator!=(const range_iterator_2d &other) const +{ + return !(*this == other); +} + +range_iterator_2d &range_iterator_2d::operator--() +{ + if (order_ == major_order::row) + { + current_cell_.set_row(current_cell_.get_row() - 1); + } + else + { + current_cell_.set_column_index(current_cell_.get_column_index() - 1); + } + + return *this; +} + +range_iterator_2d range_iterator_2d::operator--(int) +{ + range_iterator_2d old = *this; + --*this; + + return old; +} + +range_iterator_2d &range_iterator_2d::operator++() +{ + if (order_ == major_order::row) + { + current_cell_.set_row(current_cell_.get_row() + 1); + } + else + { + current_cell_.set_column_index(current_cell_.get_column_index() + 1); + } + + return *this; +} + +range_iterator_2d range_iterator_2d::operator++(int) +{ + range_iterator_2d old = *this; + ++*this; + + return old; +} + +const_range_iterator_2d::const_range_iterator_2d(const worksheet &ws, const range_reference &start_cell, major_order order) + : ws_(ws.d_), current_cell_(start_cell.get_top_left()), range_(start_cell), order_(order) +{ +} + +const_range_iterator_2d::const_range_iterator_2d(const const_range_iterator_2d &other) +{ + *this = other; +} + +bool const_range_iterator_2d::operator==(const const_range_iterator_2d &other) const +{ + return ws_ == other.ws_ && current_cell_ == other.current_cell_ && order_ == other.order_; +} + +bool const_range_iterator_2d::operator!=(const const_range_iterator_2d &other) const +{ + return !(*this == other); +} + +range::const_iterator &range::const_iterator::operator--() +{ + if (order_ == major_order::row) + { + current_cell_.set_row(current_cell_.get_row() - 1); + } + else + { + current_cell_.set_column_index(current_cell_.get_column_index() - 1); + } + + return *this; +} + +range::const_iterator range::const_iterator::operator--(int) +{ + const_range_iterator_2d old = *this; + --*this; + + return old; +} + +range::const_iterator &range::const_iterator::operator++() +{ + if (order_ == major_order::row) + { + current_cell_.set_row(current_cell_.get_row() + 1); + } + else + { + current_cell_.set_column_index(current_cell_.get_column_index() + 1); + } + + return *this; +} + +range::const_iterator range::const_iterator::operator++(int) +{ + const_range_iterator_2d old = *this; + ++*this; + + return old; +} + +bool range::operator!=(const range &comparand) const +{ + return !(*this == comparand); +} + +range::const_iterator range::begin() const +{ + return cbegin(); +} + +range::const_iterator range::end() const +{ + return cend(); +} + } // namespace xlnt diff --git a/source/worksheet/range_reference.cpp b/source/worksheet/range_reference.cpp index 007bdfbb..547cd14a 100644 --- a/source/worksheet/range_reference.cpp +++ b/source/worksheet/range_reference.cpp @@ -82,4 +82,62 @@ bool range_reference::operator!=(const range_reference &comparand) const { return comparand.top_left_ != top_left_ || comparand.bottom_right_ != bottom_right_; } + +cell_reference range_reference::get_top_left() const +{ + return top_left_; } +cell_reference range_reference::get_bottom_right() const +{ + return bottom_right_; +} +cell_reference &range_reference::get_top_left() +{ + return top_left_; +} +cell_reference &range_reference::get_bottom_right() +{ + return bottom_right_; +} + +bool range_reference::operator==(const std::string &reference_string) const +{ + return *this == range_reference(reference_string); +} + +bool range_reference::operator==(const char *reference_string) const +{ + return *this == std::string(reference_string); +} + +bool range_reference::operator!=(const std::string &reference_string) const +{ + return *this != range_reference(reference_string); +} + +bool range_reference::operator!=(const char *reference_string) const +{ + return *this != std::string(reference_string); +} + +XLNT_FUNCTION bool operator==(const std::string &reference_string, const range_reference &ref) +{ + return ref == reference_string; +} + +XLNT_FUNCTION bool operator==(const char *reference_string, const range_reference &ref) +{ + return ref == reference_string; +} + +XLNT_FUNCTION bool operator!=(const std::string &reference_string, const range_reference &ref) +{ + return ref != reference_string; +} + +XLNT_FUNCTION bool operator!=(const char *reference_string, const range_reference &ref) +{ + return ref != reference_string; +} + +} // namespace xlnt diff --git a/source/worksheet/sheet_protection.cpp b/source/worksheet/sheet_protection.cpp index ed629683..ce4c64c6 100644 --- a/source/worksheet/sheet_protection.cpp +++ b/source/worksheet/sheet_protection.cpp @@ -4,6 +4,18 @@ #include +namespace { + +template +std::string int_to_hex(T i) +{ + std::stringstream stream; + stream << std::hex << i; + return stream.str(); +} + +} // namespace + namespace xlnt { void sheet_protection::set_password(const std::string &password) @@ -11,12 +23,9 @@ void sheet_protection::set_password(const std::string &password) hashed_password_ = hash_password(password); } -template -std::string int_to_hex(T i) +std::string sheet_protection::get_hashed_password() const { - std::stringstream stream; - stream << std::hex << i; - return stream.str(); + return hashed_password_; } std::string sheet_protection::hash_password(const std::string &plaintext_password) diff --git a/source/worksheet/tests/test_worksheet.hpp b/source/worksheet/tests/test_worksheet.hpp index 6cef96c1..48295480 100644 --- a/source/worksheet/tests/test_worksheet.hpp +++ b/source/worksheet/tests/test_worksheet.hpp @@ -192,8 +192,16 @@ public: ws.garbage_collect(); - auto cell_collection = ws.get_cell_collection(); - std::set cells(cell_collection.begin(), cell_collection.end()); + std::set cells; + + for(auto row : ws) + { + for(auto cell : row) + { + if(!cell.garbage_collectible()) cells.insert(cell); + } + } + std::set expected = {ws.get_cell("B2"), ws.get_cell("C4"), ws.get_cell("D1")}; // Set difference diff --git a/source/worksheet/worksheet.cpp b/source/worksheet/worksheet.cpp index cf4258cb..d4f7450b 100644 --- a/source/worksheet/worksheet.cpp +++ b/source/worksheet/worksheet.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include #include #include #include @@ -173,19 +173,6 @@ void worksheet::garbage_collect() } } -std::list worksheet::get_cell_collection() -{ - std::list cells; - for (auto &c : d_->cell_map_) - { - for (auto &d : c.second) - { - cells.push_back(cell(&d.second)); - } - } - return cells; -} - std::string worksheet::get_title() const { if (d_ == nullptr) @@ -766,4 +753,57 @@ const row_properties &worksheet::get_row_properties(row_t row) const return d_->row_properties_.at(row); } +worksheet::iterator worksheet::begin() +{ + auto dimensions = calculate_dimension(); + cell_reference top_right(dimensions.get_bottom_right().get_column_index(), dimensions.get_top_left().get_row()); + range_reference row_range(dimensions.get_top_left(), top_right); + + return iterator(*this, row_range, major_order::row); +} + +worksheet::iterator worksheet::end() +{ + auto dimensions = calculate_dimension(); + auto past_end_row_index = dimensions.get_bottom_right().get_row() + 1; + cell_reference bottom_left(dimensions.get_top_left().get_column_index(), past_end_row_index); + cell_reference bottom_right(dimensions.get_bottom_right().get_column_index(), past_end_row_index); + + return iterator(*this, range_reference(bottom_left, bottom_right), major_order::row); +} + +worksheet::const_iterator worksheet::cbegin() const +{ + auto dimensions = calculate_dimension(); + cell_reference top_right(dimensions.get_bottom_right().get_column_index(), dimensions.get_top_left().get_row()); + range_reference row_range(dimensions.get_top_left(), top_right); + + return const_iterator(*this, row_range, major_order::row); +} + +worksheet::const_iterator worksheet::cend() const +{ + auto dimensions = calculate_dimension(); + auto past_end_row_index = dimensions.get_bottom_right().get_row() + 1; + cell_reference bottom_left(dimensions.get_top_left().get_column_index(), past_end_row_index); + cell_reference bottom_right(dimensions.get_bottom_right().get_column_index(), past_end_row_index); + + return const_iterator(*this, range_reference(bottom_left, bottom_right), major_order::row); +} + +worksheet::const_iterator worksheet::begin() const +{ + return cbegin(); +} + +worksheet::const_iterator worksheet::end() const +{ + return cend(); +} + +range worksheet::iter_cells(bool skip_null) +{ + return range(*this, calculate_dimension(), major_order::row, skip_null); +} + } // namespace xlnt