diff --git a/xlnt/include/xlnt/cell/cell.hpp b/xlnt/include/xlnt/cell/cell.hpp index e65eab0..9ff5fb9 100644 --- a/xlnt/include/xlnt/cell/cell.hpp +++ b/xlnt/include/xlnt/cell/cell.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -47,6 +47,8 @@ class font; class format; class number_format; class protection; +class range; +class relationship; class style; class workbook; class worksheet; @@ -238,6 +240,11 @@ class XLNT_API cell /// column_t column() const; + /// + /// Returns the numeric index (A == 1) of the column of this cell. + /// + column_t::index_t column_index() const; + /// /// Returns the row of this cell. /// @@ -251,25 +258,25 @@ class XLNT_API cell // hyperlink /// - /// Returns the URL of this cell's hyperlink. + /// Returns the relationship of this cell's hyperlink. /// - std::string hyperlink() const; - - /// - /// Adds a hyperlink to this cell pointing to the URL of the given value. - /// - void hyperlink(const std::string &url); + class hyperlink hyperlink() const; /// /// Adds a hyperlink to this cell pointing to the URI of the given value and sets /// the text value of the cell to the given parameter. /// - void hyperlink(const std::string &url, const std::string &display); + void hyperlink(const std::string &url, const std::string &display = ""); /// /// Adds an internal hyperlink to this cell pointing to the given cell. /// - void hyperlink(xlnt::cell target); + void hyperlink(xlnt::cell target, const std::string& display = ""); + + /// + /// Adds an internal hyperlink to this cell pointing to the given range. + /// + void hyperlink(xlnt::range target, const std::string& display = ""); /// /// Returns true if this cell has a hyperlink set. @@ -601,9 +608,9 @@ class XLNT_API cell bool operator==(const cell &comparand) const; /// - /// Returns true if this cell is uninitialized. + /// Returns false if this cell the same cell as comparand (compared by reference). /// - bool operator==(std::nullptr_t) const; + bool operator!=(const cell &comparand) const; private: friend class style; @@ -639,6 +646,11 @@ class XLNT_API cell /// XLNT_API bool operator==(std::nullptr_t, const cell &cell); +/// +/// Returns true if this cell is uninitialized. +/// +XLNT_API bool operator==(const cell &cell, std::nullptr_t); + /// /// Convenience function for writing cell to an ostream. /// Uses cell::to_string() internally. diff --git a/xlnt/include/xlnt/cell/cell_reference.hpp b/xlnt/include/xlnt/cell/cell_reference.hpp index b9472f7..57fd54b 100644 --- a/xlnt/include/xlnt/cell/cell_reference.hpp +++ b/xlnt/include/xlnt/cell/cell_reference.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/include/xlnt/cell/cell_type.hpp b/xlnt/include/xlnt/cell/cell_type.hpp index cee0e48..dac6ad2 100644 --- a/xlnt/include/xlnt/cell/cell_type.hpp +++ b/xlnt/include/xlnt/cell/cell_type.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/xlnt/include/xlnt/cell/comment.hpp b/xlnt/include/xlnt/cell/comment.hpp index 9b3a7c8..124d22d 100644 --- a/xlnt/include/xlnt/cell/comment.hpp +++ b/xlnt/include/xlnt/cell/comment.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/include/xlnt/cell/index_types.hpp b/xlnt/include/xlnt/cell/index_types.hpp index 63dbd8a..129a5ee 100644 --- a/xlnt/include/xlnt/cell/index_types.hpp +++ b/xlnt/include/xlnt/cell/index_types.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/include/xlnt/cell/rich_text.hpp b/xlnt/include/xlnt/cell/rich_text.hpp index 3784958..31cb279 100644 --- a/xlnt/include/xlnt/cell/rich_text.hpp +++ b/xlnt/include/xlnt/cell/rich_text.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2017 Thomas Fussell +// Copyright (c) 2016-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -48,6 +48,11 @@ class XLNT_API rich_text /// rich_text(const std::string &plain_text); + /// + /// Constructs a rich text object from other + /// + rich_text(const rich_text &other); + /// /// Constructs a rich text object with the given text and font. /// @@ -67,7 +72,7 @@ class XLNT_API rich_text /// Clears any runs in this text and adds a single run with default formatting and /// the given string as its textual content. /// - void plain_text(const std::string &s); + void plain_text(const std::string &s, bool preserve_space); /// /// Combines the textual content of each text run in order and returns the result. @@ -89,6 +94,11 @@ class XLNT_API rich_text /// void add_run(const rich_text_run &t); + /// + /// Copies rich text object from other + /// + rich_text& operator=(const rich_text &rhs); + /// /// Returns true if the runs that make up this text are identical to those in rhs. /// @@ -97,7 +107,7 @@ class XLNT_API rich_text /// /// Returns true if the runs that make up this text are identical to those in rhs. /// - bool operator!=(const rich_text &rhs) const; + bool operator!=(const rich_text &rhs) const; /// /// Returns true if this text has a single unformatted run with text equal to rhs. @@ -116,4 +126,20 @@ class XLNT_API rich_text std::vector runs_; }; +class XLNT_API rich_text_hash +{ +public: + std::size_t operator()(const rich_text& k) const + { + std::size_t res = 0; + + for (auto r : k.runs()) + { + res ^= std::hash()(r.first); + } + + return res; + } +}; + } // namespace xlnt diff --git a/xlnt/include/xlnt/cell/rich_text_run.hpp b/xlnt/include/xlnt/cell/rich_text_run.hpp index 432097e..dc31fe5 100644 --- a/xlnt/include/xlnt/cell/rich_text_run.hpp +++ b/xlnt/include/xlnt/cell/rich_text_run.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2017 Thomas Fussell +// Copyright (c) 2016-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -34,10 +34,11 @@ namespace xlnt { /// /// Typedef a rich_text_run as a pair of string and optional font. /// -struct rich_text_run +struct XLNT_API rich_text_run { std::string first; optional second; + bool preserve_space; bool operator==(const rich_text_run &other) const; diff --git a/xlnt/include/xlnt/packaging/manifest.hpp b/xlnt/include/xlnt/packaging/manifest.hpp index 5aa2eae..03d19de 100644 --- a/xlnt/include/xlnt/packaging/manifest.hpp +++ b/xlnt/include/xlnt/packaging/manifest.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -172,6 +172,8 @@ class XLNT_API manifest /// void unregister_override_type(const path &part); + bool operator==(const manifest &other) const; + private: /// /// Returns the lowest rId for the given part that hasn't already been registered. diff --git a/xlnt/include/xlnt/packaging/relationship.hpp b/xlnt/include/xlnt/packaging/relationship.hpp index 3b6c192..2dcbb4a 100644 --- a/xlnt/include/xlnt/packaging/relationship.hpp +++ b/xlnt/include/xlnt/packaging/relationship.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -115,7 +115,7 @@ class XLNT_API relationship /// /// Returns a string of the form rId# that identifies the relationship. /// - std::string id() const; + const std::string& id() const; /// /// Returns the type of this relationship. @@ -130,12 +130,12 @@ class XLNT_API relationship /// /// Returns the URI of the package part this relationship points to. /// - uri source() const; + const uri &source() const; /// /// Returns the URI of the package part this relationship points to. /// - uri target() const; + const uri &target() const; /// /// Returns true if and only if rhs is equal to this relationship. diff --git a/xlnt/include/xlnt/packaging/uri.hpp b/xlnt/include/xlnt/packaging/uri.hpp index d1e5ee9..66d6358 100644 --- a/xlnt/include/xlnt/packaging/uri.hpp +++ b/xlnt/include/xlnt/packaging/uri.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -123,7 +123,7 @@ class XLNT_API uri /// Returns the path of this URI. /// E.g. the path of http://example.com/document is "/document" /// - class path path() const; + const class path& path() const; /// /// Returns true if this URI has a non-null query string section. diff --git a/xlnt/include/xlnt/styles/alignment.hpp b/xlnt/include/xlnt/styles/alignment.hpp index d2972bc..f8a777a 100644 --- a/xlnt/include/xlnt/styles/alignment.hpp +++ b/xlnt/include/xlnt/styles/alignment.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/xlnt/include/xlnt/styles/border.hpp b/xlnt/include/xlnt/styles/border.hpp index 1f54849..0c953aa 100644 --- a/xlnt/include/xlnt/styles/border.hpp +++ b/xlnt/include/xlnt/styles/border.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/xlnt/include/xlnt/styles/color.hpp b/xlnt/include/xlnt/styles/color.hpp index 2905b2e..2402145 100644 --- a/xlnt/include/xlnt/styles/color.hpp +++ b/xlnt/include/xlnt/styles/color.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -101,7 +101,7 @@ class XLNT_API rgb_color /// /// Constructs an RGB color from red, green, and blue values in the range 0 to 255 - /// plus an optional alpha which defaults to fully opaque. + /// plus an optional alpha which defaults to fully opaque. /// rgb_color(std::uint8_t r, std::uint8_t g, std::uint8_t b, std::uint8_t a = 255); @@ -116,17 +116,17 @@ class XLNT_API rgb_color std::uint8_t red() const; /// - /// Returns a byte representing the red component of this color + /// Returns a byte representing the red component of this color /// std::uint8_t green() const; /// - /// Returns a byte representing the blue component of this color + /// Returns a byte representing the blue component of this color /// std::uint8_t blue() const; /// - /// Returns a byte representing the alpha component of this color + /// Returns a byte representing the alpha component of this color /// std::uint8_t alpha() const; @@ -136,7 +136,7 @@ class XLNT_API rgb_color std::array rgb() const; /// - /// Returns the red, green, blue, and alpha components of this color separately in an array in that order. + /// Returns the red, green, blue, and alpha components of this color separately in an array in that order. /// std::array rgba() const; @@ -174,42 +174,42 @@ class XLNT_API color static const color white(); /// - /// Returns the color \#ff0000 + /// Returns the color \#ff0000 /// static const color red(); /// - /// Returns the color \#8b0000 + /// Returns the color \#8b0000 /// static const color darkred(); /// - /// Returns the color \#00ff00 + /// Returns the color \#00ff00 /// static const color blue(); /// - /// Returns the color \#008b00 + /// Returns the color \#008b00 /// static const color darkblue(); /// - /// Returns the color \#0000ff + /// Returns the color \#0000ff /// static const color green(); /// - /// Returns the color \#00008b + /// Returns the color \#00008b /// static const color darkgreen(); /// - /// Returns the color \#ffff00 + /// Returns the color \#ffff00 /// static const color yellow(); /// - /// Returns the color \#cccc00 + /// Returns the color \#cccc00 /// static const color darkyellow(); @@ -249,22 +249,40 @@ class XLNT_API color void auto_(bool value); /// - /// Returns the internal indexed color representing this color. If this is not an RGB color, - /// an invalid_attribute exception will be thrown. + /// Returns the internal indexed color representing this color. If this is not an RGB color, + /// an invalid_attribute exception will be thrown. + /// + const rgb_color& rgb() const; + + /// + /// Returns the internal indexed color representing this color. If this is not an RGB color, + /// an invalid_attribute exception will be thrown. /// - rgb_color rgb() const; + rgb_color &rgb(); /// /// Returns the internal indexed color representing this color. If this is not an indexed color, - /// an invalid_attribute exception will be thrown. + /// an invalid_attribute exception will be thrown. /// - indexed_color indexed() const; + const indexed_color& indexed() const; /// - /// Returns the internal indexed color representing this color. If this is not a theme color, - /// an invalid_attribute exception will be thrown. + /// Returns the internal indexed color representing this color. If this is not an indexed color, + /// an invalid_attribute exception will be thrown. /// - theme_color theme() const; + indexed_color &indexed(); + + /// + /// Returns the internal indexed color representing this color. If this is not a theme color, + /// an invalid_attribute exception will be thrown. + /// + const theme_color& theme() const; + + /// + /// Returns the internal indexed color representing this color. If this is not a theme color, + /// an invalid_attribute exception will be thrown. + /// + theme_color& theme(); /// /// Returns the tint of this color. @@ -276,15 +294,15 @@ class XLNT_API color /// void tint(double tint); - /// - /// Returns true if this color is equivalent to other - /// - bool operator==(const color &other) const; + /// + /// Returns true if this color is equivalent to other + /// + bool operator==(const color &other) const; /// /// Returns true if this color is not equivalent to other /// - bool operator!=(const color &other) const; + bool operator!=(const color &other) const; private: /// @@ -303,12 +321,12 @@ class XLNT_API color rgb_color rgb_; /// - /// The internal RGB color. Only valid when this color has a type of indexed + /// The internal RGB color. Only valid when this color has a type of indexed /// indexed_color indexed_; /// - /// The internal RGB color. Only valid when this color has a type of theme + /// The internal RGB color. Only valid when this color has a type of theme /// theme_color theme_; diff --git a/xlnt/include/xlnt/styles/conditional_format.hpp b/xlnt/include/xlnt/styles/conditional_format.hpp index a43a6d4..0260b6b 100644 --- a/xlnt/include/xlnt/styles/conditional_format.hpp +++ b/xlnt/include/xlnt/styles/conditional_format.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -53,6 +53,11 @@ class XLNT_API condition static condition text_contains(const std::string &start); static condition text_does_not_contain(const std::string &start); + bool operator==(const condition& rhs) const + { + return text_comparand_ == rhs.text_comparand_; + } + private: friend class detail::xlsx_producer; diff --git a/xlnt/include/xlnt/styles/fill.hpp b/xlnt/include/xlnt/styles/fill.hpp index 0479ad4..d456fcf 100644 --- a/xlnt/include/xlnt/styles/fill.hpp +++ b/xlnt/include/xlnt/styles/fill.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/xlnt/include/xlnt/styles/font.hpp b/xlnt/include/xlnt/styles/font.hpp index 2cb9f42..195271e 100644 --- a/xlnt/include/xlnt/styles/font.hpp +++ b/xlnt/include/xlnt/styles/font.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -68,12 +68,22 @@ class XLNT_API font bool bold() const; /// - /// Sets the bold state of the font to bold and returns a reference to the font. + /// Sets the vertical alignment of the font to subscript and returns a reference to the font. + /// + font &subscript(bool value); + + /// + /// Returns true if this font has a vertical alignment of subscript. /// - font &superscript(bool superscript); + bool subscript() const; /// - /// Returns true if this font has a superscript applied. + /// Sets the vertical alignment of the font to superscript and returns a reference to the font. + /// + font &superscript(bool value); + + /// + /// Returns true if this font has a vertical alignment of superscript. /// bool superscript() const; @@ -160,7 +170,7 @@ class XLNT_API font /// /// Returns the name of the font face. /// - std::string name() const; + const std::string& name() const; /// /// Returns true if this font has a color applied. @@ -222,7 +232,7 @@ class XLNT_API font /// /// Returns the scheme of this font. /// - std::string scheme() const; + const std::string& scheme() const; /// /// Returns true if left is exactly equal to right. @@ -232,7 +242,10 @@ class XLNT_API font /// /// Returns true if left is not exactly equal to right. /// - bool operator!=(const font &other) const; + bool operator!=(const font &other) const + { + return !operator==(other); + } private: friend class style; diff --git a/xlnt/include/xlnt/styles/format.hpp b/xlnt/include/xlnt/styles/format.hpp index 0a549cf..252f7cb 100644 --- a/xlnt/include/xlnt/styles/format.hpp +++ b/xlnt/include/xlnt/styles/format.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -63,7 +63,7 @@ class XLNT_API format /// to true, determines whether the alignment should be enabled for cells using /// this format. /// - format alignment(const xlnt::alignment &new_alignment, bool applied = true); + format alignment(const xlnt::alignment &new_alignment, optional applied = {}); /// /// Returns true if the alignment of this format should be applied to cells @@ -81,7 +81,7 @@ class XLNT_API format /// to true, determines whether the border should be enabled for cells using /// this format. /// - format border(const xlnt::border &new_border, bool applied = true); + format border(const xlnt::border &new_border, optional applied = {}); /// /// Returns true if the border set for this format should be applied to cells using the format. @@ -98,7 +98,7 @@ class XLNT_API format /// to true, determines whether the border should be enabled for cells using /// this format. /// - format fill(const xlnt::fill &new_fill, bool applied = true); + format fill(const xlnt::fill &new_fill, optional applied = {}); /// /// Returns true if the fill set for this format should be applied to cells using the format. @@ -115,7 +115,7 @@ class XLNT_API format /// to true, determines whether the font should be enabled for cells using /// this format. /// - format font(const xlnt::font &new_font, bool applied = true); + format font(const xlnt::font &new_font, optional applied = {}); /// /// Returns true if the font set for this format should be applied to cells using the format. @@ -132,7 +132,7 @@ class XLNT_API format /// to true, determines whether the number format should be enabled for cells using /// this format. /// - format number_format(const xlnt::number_format &new_number_format, bool applied = true); + format number_format(const xlnt::number_format &new_number_format, optional applied = {}); /// /// Returns true if the number_format set for this format should be applied to cells using the format. @@ -154,7 +154,7 @@ class XLNT_API format /// to true, determines whether the protection should be enabled for cells using /// this format. /// - format protection(const xlnt::protection &new_protection, bool applied = true); + format protection(const xlnt::protection &new_protection, optional applied = {}); /// /// Returns true if the pivot table interface is enabled for this format. diff --git a/xlnt/include/xlnt/styles/number_format.hpp b/xlnt/include/xlnt/styles/number_format.hpp index 8d0574a..b967665 100644 --- a/xlnt/include/xlnt/styles/number_format.hpp +++ b/xlnt/include/xlnt/styles/number_format.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/xlnt/include/xlnt/styles/protection.hpp b/xlnt/include/xlnt/styles/protection.hpp index 9b9d8f1..42a07a3 100644 --- a/xlnt/include/xlnt/styles/protection.hpp +++ b/xlnt/include/xlnt/styles/protection.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/xlnt/include/xlnt/styles/style.hpp b/xlnt/include/xlnt/styles/style.hpp index d75fcb7..4ba5725 100644 --- a/xlnt/include/xlnt/styles/style.hpp +++ b/xlnt/include/xlnt/styles/style.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -123,7 +123,7 @@ class XLNT_API style /// to true, determines whether the alignment should be enabled for cells using /// this style. /// - style alignment(const xlnt::alignment &new_alignment, bool applied = true); + style alignment(const xlnt::alignment &new_alignment, optional applied = {}); /// /// Returns the border of this style. @@ -140,7 +140,7 @@ class XLNT_API style /// to true, determines whether the border should be enabled for cells using /// this style. /// - style border(const xlnt::border &new_border, bool applied = true); + style border(const xlnt::border &new_border, optional applied = {}); /// /// Returns the fill of this style. @@ -157,7 +157,7 @@ class XLNT_API style /// to true, determines whether the border should be enabled for cells using /// this style. /// - style fill(const xlnt::fill &new_fill, bool applied = true); + style fill(const xlnt::fill &new_fill, optional applied = {}); /// /// Returns the font of this style. @@ -174,7 +174,7 @@ class XLNT_API style /// to true, determines whether the font should be enabled for cells using /// this style. /// - style font(const xlnt::font &new_font, bool applied = true); + style font(const xlnt::font &new_font, optional applied = {}); /// /// Returns the number_format of this style. @@ -191,7 +191,7 @@ class XLNT_API style /// to true, determines whether the number format should be enabled for cells using /// this style. /// - style number_format(const xlnt::number_format &new_number_format, bool applied = true); + style number_format(const xlnt::number_format &new_number_format, optional applied = {}); /// /// Returns the protection of this style. @@ -208,7 +208,7 @@ class XLNT_API style /// to true, determines whether the protection should be enabled for cells using /// this style. /// - style protection(const xlnt::protection &new_protection, bool applied = true); + style protection(const xlnt::protection &new_protection, optional applied = {}); /// /// Returns true if the pivot table interface is enabled for this style. diff --git a/xlnt/include/xlnt/utils/calendar.hpp b/xlnt/include/xlnt/utils/calendar.hpp index 88de229..ade6fab 100644 --- a/xlnt/include/xlnt/utils/calendar.hpp +++ b/xlnt/include/xlnt/utils/calendar.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/include/xlnt/utils/date.hpp b/xlnt/include/xlnt/utils/date.hpp index 73be2f6..8eb9d1a 100644 --- a/xlnt/include/xlnt/utils/date.hpp +++ b/xlnt/include/xlnt/utils/date.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/include/xlnt/utils/datetime.hpp b/xlnt/include/xlnt/utils/datetime.hpp index 24ca765..646b2bd 100644 --- a/xlnt/include/xlnt/utils/datetime.hpp +++ b/xlnt/include/xlnt/utils/datetime.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/include/xlnt/utils/exceptions.hpp b/xlnt/include/xlnt/utils/exceptions.hpp index 3b108e7..0618a0f 100644 --- a/xlnt/include/xlnt/utils/exceptions.hpp +++ b/xlnt/include/xlnt/utils/exceptions.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -120,7 +120,7 @@ class XLNT_API missing_number_format : public exception /// /// Default constructor. /// - missing_number_format(); + missing_number_format() = default; /// /// Default copy constructor. diff --git a/xlnt/include/xlnt/utils/optional.hpp b/xlnt/include/xlnt/utils/optional.hpp index 655923d..8677f24 100644 --- a/xlnt/include/xlnt/utils/optional.hpp +++ b/xlnt/include/xlnt/utils/optional.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2017 Thomas Fussell +// Copyright (c) 2016-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -25,6 +25,7 @@ #include #include +#include namespace xlnt { @@ -34,39 +35,207 @@ namespace xlnt { /// within the optional class. /// template -class XLNT_API optional +class optional { +#if defined(_MSC_VER) && _MSC_VER <= 1900 // v14, visual studio 2015 +#define XLNT_NOEXCEPT_VALUE_COMPAT(...) (false) +#else +#define XLNT_NOEXCEPT_VALUE_COMPAT(...) (__VA_ARGS__) + using ctor_copy_T_noexcept = typename std::conditional{}, std::true_type, std::false_type>::type; + using ctor_move_T_noexcept = typename std::conditional{}, std::true_type, std::false_type>::type; + using copy_ctor_noexcept = ctor_copy_T_noexcept; + using move_ctor_noexcept = ctor_move_T_noexcept; + using set_copy_noexcept_t = typename std::conditional{} && std::is_nothrow_assignable{}, std::true_type, std::false_type>::type; + using set_move_noexcept_t = typename std::conditional{} && std::is_nothrow_move_assignable{}, std::true_type, std::false_type>::type; + using clear_noexcept_t = typename std::conditional{}, std::true_type, std::false_type>::type; +#endif + public: /// /// Default contructor. is_set() will be false initially. /// - optional() : has_value_(false), value_(T()) + optional() noexcept + : has_value_(false) + { + } + + /// + /// Constructs this optional with a value. + /// noexcept if T copy ctor is noexcept + /// + optional(const T &value) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(ctor_copy_T_noexcept{})) + : has_value_(true) { + new (&storage_) T(value); } /// /// Constructs this optional with a value. + /// noexcept if T move ctor is noexcept + /// + optional(T &&value) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(ctor_move_T_noexcept{})) + : has_value_(true) + { + new (&storage_) T(std::move(value)); + } + + /// + /// Copy constructs this optional from other + /// noexcept if T copy ctor is noexcept + /// + optional(const optional &other) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(copy_ctor_noexcept{})) + : has_value_(other.has_value_) + { + if (has_value_) + { + new (&storage_) T(other.value_ref()); + } + } + + /// + /// Move constructs this optional from other. Clears the value from other if set + /// noexcept if T move ctor is noexcept /// - explicit optional(const T &value) : has_value_(true), value_(value) + optional(optional &&other) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(move_ctor_noexcept{})) + : has_value_(other.has_value_) { + if (has_value_) + { + new (&storage_) T(std::move(other.value_ref())); + other.clear(); + } + } + + /// + /// Copy assignment of this optional from other + /// noexcept if set and clear are noexcept for T& + /// + optional &operator=(const optional &other) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(set_copy_noexcept_t{} && clear_noexcept_t{})) + { + if (other.has_value_) + { + set(other.value_ref()); + } + else + { + clear(); + } + return *this; + } + + /// + /// Move assignment of this optional from other + /// noexcept if set and clear are noexcept for T&& + /// + optional &operator=(optional &&other) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(set_move_noexcept_t{} && clear_noexcept_t{})) + { + if (other.has_value_) + { + set(std::move(other.value_ref())); + other.clear(); + } + else + { + clear(); + } + return *this; + } + + /// + /// Destructor cleans up the T instance if set + /// + ~optional() noexcept // note:: unconditional because msvc freaks out otherwise + { + clear(); } /// /// Returns true if this object currently has a value set. This should /// be called before accessing the value with optional::get(). /// - bool is_set() const + bool is_set() const noexcept { return has_value_; } /// - /// Sets the value to value. + /// Copies the value into the stored value /// - void set(const T &value) + void set(const T &value) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(set_copy_noexcept_t{})) { - has_value_ = true; - value_ = value; +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif + if (has_value_) + { + value_ref() = value; + } + else + { + new (&storage_) T(value); + has_value_ = true; + } +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif + } + + /// + /// Moves the value into the stored value + /// + void set(T &&value) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(set_move_noexcept_t{})) + { + // note seperate overload for two reasons (as opposed to perfect forwarding) + // 1. have to deal with implicit conversions internally with perfect forwarding + // 2. have to deal with the noexcept specfiers for all the different variations + // overload is just far and away the simpler solution +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif + if (has_value_) + { + value_ref() = std::move(value); + } + else + { + new (&storage_) T(std::move(value)); + has_value_ = true; + } +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif + } + + /// + /// Assignment operator overload. Equivalent to setting the value using optional::set. + /// + optional &operator=(const T &rhs) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(set_copy_noexcept_t{})) + { + set(rhs); + return *this; + } + + /// + /// Assignment operator overload. Equivalent to setting the value using optional::set. + /// + optional &operator=(T &&rhs) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(set_move_noexcept_t{})) + { + set(std::move(rhs)); + return *this; + } + + /// + /// After this is called, is_set() will return false until a new value is provided. + /// + void clear() noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(clear_noexcept_t{})) + { + if (has_value_) + { + reinterpret_cast(&storage_)->~T(); + } + has_value_ = false; } /// @@ -80,7 +249,7 @@ class XLNT_API optional throw invalid_attribute(); } - return value_; + return value_ref(); } /// @@ -94,44 +263,55 @@ class XLNT_API optional throw invalid_attribute(); } - return value_; + return value_ref(); } /// - /// Resets the internal value using its default constructor. After this is - /// called, is_set() will return false until a new value is provided. + /// Returns true if neither this nor other have a value + /// or both have a value and those values are equal according to + /// their equality operator. /// - void clear() + bool operator==(const optional &other) const noexcept { - has_value_ = false; - value_ = T(); + if (has_value_ != other.has_value_) + { + return false; + } + if (!has_value_) + { + return true; + } + return value_ref() == other.value_ref(); } /// - /// Assignment operator. Equivalent to setting the value using optional::set. + /// Returns false if neither this nor other have a value + /// or both have a value and those values are equal according to + /// their equality operator. /// - optional &operator=(const T &rhs) + bool operator!=(const optional &other) const noexcept { - has_value_ = true; - value_ = rhs; + return !operator==(other); + } - return *this; +private: + // helpers for getting a T out of storage + T &value_ref() noexcept + { + return *reinterpret_cast(&storage_); } - /// - /// Returns true if neither this nor other have a value - /// or both have a value and those values are equal according to - /// their equality operator. - /// - bool operator==(const optional &other) const + const T &value_ref() const noexcept { - return has_value_ == other.has_value_ - && (!has_value_ || (has_value_ && value_ == other.value_)); + return *reinterpret_cast(&storage_); } -private: bool has_value_; - T value_; + typename std::aligned_storage::type storage_; }; +#ifdef XLNT_NOEXCEPT_VALUE_COMPAT +#undef XLNT_NOEXCEPT_VALUE_COMPAT +#endif + } // namespace xlnt diff --git a/xlnt/include/xlnt/utils/path.hpp b/xlnt/include/xlnt/utils/path.hpp index 4a392c1..ba16526 100644 --- a/xlnt/include/xlnt/utils/path.hpp +++ b/xlnt/include/xlnt/utils/path.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -107,7 +107,7 @@ class XLNT_API path /// Create a string representing this path separated by the provided /// separator or the system-default separator if not provided. /// - std::string string() const; + const std::string& string() const; #ifdef _MSC_VER /// @@ -176,6 +176,11 @@ class XLNT_API path /// bool operator==(const path &other) const; + /// + /// Returns true if left path is equal to right path. + /// + bool operator!=(const path &other) const; + private: /// /// Returns the character that separates directories in the path. diff --git a/xlnt/include/xlnt/utils/scoped_enum_hash.hpp b/xlnt/include/xlnt/utils/scoped_enum_hash.hpp index 6bac514..bf18432 100644 --- a/xlnt/include/xlnt/utils/scoped_enum_hash.hpp +++ b/xlnt/include/xlnt/utils/scoped_enum_hash.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2017 Thomas Fussell +// Copyright (c) 2016-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/include/xlnt/utils/time.hpp b/xlnt/include/xlnt/utils/time.hpp index 36d6f42..247c28b 100644 --- a/xlnt/include/xlnt/utils/time.hpp +++ b/xlnt/include/xlnt/utils/time.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/include/xlnt/utils/timedelta.hpp b/xlnt/include/xlnt/utils/timedelta.hpp index 797e6d9..80aa7df 100644 --- a/xlnt/include/xlnt/utils/timedelta.hpp +++ b/xlnt/include/xlnt/utils/timedelta.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/include/xlnt/utils/variant.hpp b/xlnt/include/xlnt/utils/variant.hpp index 4fe6417..a7c4c30 100644 --- a/xlnt/include/xlnt/utils/variant.hpp +++ b/xlnt/include/xlnt/utils/variant.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017 Thomas Fussell +// Copyright (c) 2017-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -162,6 +162,8 @@ class XLNT_API variant /// type value_type() const; + bool operator==(const variant &rhs) const; + private: type type_; std::vector vector_value_; diff --git a/xlnt/include/xlnt/workbook/calculation_properties.hpp b/xlnt/include/xlnt/workbook/calculation_properties.hpp index b1f85c6..2202664 100644 --- a/xlnt/include/xlnt/workbook/calculation_properties.hpp +++ b/xlnt/include/xlnt/workbook/calculation_properties.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2017 Thomas Fussell +// Copyright (c) 2016-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -46,4 +46,10 @@ class XLNT_API calculation_properties bool concurrent_calc; }; +inline bool operator==(const calculation_properties &lhs, const calculation_properties &rhs) +{ + return lhs.calc_id == rhs.calc_id + && lhs.concurrent_calc == rhs.concurrent_calc; +} + } // namespace xlnt diff --git a/xlnt/include/xlnt/workbook/document_security.hpp b/xlnt/include/xlnt/workbook/document_security.hpp index ac368a4..b6d5624 100644 --- a/xlnt/include/xlnt/workbook/document_security.hpp +++ b/xlnt/include/xlnt/workbook/document_security.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -67,7 +67,7 @@ class XLNT_API document_security /// /// Constructs a new document security object with default values. /// - document_security(); + document_security() = default; /// /// If true, the workbook is locked for revisions. diff --git a/xlnt/include/xlnt/workbook/external_book.hpp b/xlnt/include/xlnt/workbook/external_book.hpp index e410954..061c421 100644 --- a/xlnt/include/xlnt/workbook/external_book.hpp +++ b/xlnt/include/xlnt/workbook/external_book.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/xlnt/include/xlnt/workbook/metadata_property.hpp b/xlnt/include/xlnt/workbook/metadata_property.hpp index 4a3dc22..c380079 100644 --- a/xlnt/include/xlnt/workbook/metadata_property.hpp +++ b/xlnt/include/xlnt/workbook/metadata_property.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017 Thomas Fussell +// Copyright (c) 2017-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/include/xlnt/workbook/named_range.hpp b/xlnt/include/xlnt/workbook/named_range.hpp index 8015668..bc90b20 100644 --- a/xlnt/include/xlnt/workbook/named_range.hpp +++ b/xlnt/include/xlnt/workbook/named_range.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -76,6 +76,8 @@ class XLNT_API named_range /// named_range &operator=(const named_range &other); + bool operator==(const named_range &rhs) const; + private: /// /// The name of this named range. diff --git a/xlnt/include/xlnt/workbook/streaming_workbook_reader.hpp b/xlnt/include/xlnt/workbook/streaming_workbook_reader.hpp index 012746c..f5538fd 100644 --- a/xlnt/include/xlnt/workbook/streaming_workbook_reader.hpp +++ b/xlnt/include/xlnt/workbook/streaming_workbook_reader.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/xlnt/include/xlnt/workbook/streaming_workbook_writer.hpp b/xlnt/include/xlnt/workbook/streaming_workbook_writer.hpp index 7ed8f7f..8c7b1b1 100644 --- a/xlnt/include/xlnt/workbook/streaming_workbook_writer.hpp +++ b/xlnt/include/xlnt/workbook/streaming_workbook_writer.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/xlnt/include/xlnt/workbook/theme.hpp b/xlnt/include/xlnt/workbook/theme.hpp index 9d05873..96b0434 100644 --- a/xlnt/include/xlnt/workbook/theme.hpp +++ b/xlnt/include/xlnt/workbook/theme.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -33,6 +33,11 @@ namespace xlnt { /// class XLNT_API theme { +public: + bool operator==(const theme&) const + { + return true; + } }; } // namespace xlnt diff --git a/xlnt/include/xlnt/workbook/workbook.hpp b/xlnt/include/xlnt/workbook/workbook.hpp index fd2dcc5..3ba2685 100644 --- a/xlnt/include/xlnt/workbook/workbook.hpp +++ b/xlnt/include/xlnt/workbook/workbook.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -33,6 +34,7 @@ #include #include +#include namespace xlnt { @@ -130,6 +132,26 @@ class XLNT_API workbook /// workbook(); + /// + /// load the xlsx file at path + /// + workbook(const xlnt::path &file); + + /// + /// load the encrpyted xlsx file at path + /// + workbook(const xlnt::path &file, const std::string& password); + + /// + /// construct the workbook from any data stream where the data is the binary form of a workbook + /// + workbook(std::istream & data); + + /// + /// construct the workbook from any data stream where the data is the binary form of an encrypted workbook + /// + workbook(std::istream &data, const std::string& password); + /// /// Move constructor. Constructs a workbook from existing workbook, other. /// @@ -390,6 +412,16 @@ class XLNT_API workbook /// void title(const std::string &title); + /// + /// Sets the absolute path of this workbook to path. + /// + void abs_path(const std::string &path); + + /// + /// Sets the ArchID flags of this workbook to flags. + /// + void arch_id_flags(const std::size_t flags); + // Named Ranges /// @@ -670,10 +702,10 @@ class XLNT_API workbook /// class style create_style(const std::string &name); - /// - /// Creates a new style and returns it. - /// - class style create_builtin_style(std::size_t builtin_id); + /// + /// Creates a new style and returns it. + /// + class style create_builtin_style(std::size_t builtin_id); /// /// Clear all named styles from cells and remove the styles from @@ -682,6 +714,31 @@ class XLNT_API workbook /// void clear_styles(); + /// + /// Sets the default slicer style to the given value. + /// + void default_slicer_style(const std::string &value); + + /// + /// Returns the default slicer style. + /// + std::string default_slicer_style() const; + + /// + /// Enables knownFonts in stylesheet. + /// + void enable_known_fonts(); + + /// + /// Disables knownFonts in stylesheet. + /// + void disable_known_fonts(); + + /// + /// Returns true if knownFonts are enabled in the stylesheet. + /// + bool known_fonts_enabled() const; + // Manifest /// @@ -704,17 +761,27 @@ class XLNT_API workbook /// std::size_t add_shared_string(const rich_text &shared, bool allow_duplicates = false); + /// + /// Returns a reference to the shared string ordered by id + /// + const std::map &shared_strings_by_id() const; + + /// + /// Returns a reference to the shared string related to the specified index + /// + const rich_text &shared_strings(std::size_t index) const; + /// /// Returns a reference to the shared strings being used by cells /// in this workbook. /// - std::vector &shared_strings(); + std::unordered_map &shared_strings(); /// /// Returns a reference to the shared strings being used by cells /// in this workbook. /// - const std::vector &shared_strings() const; + const std::unordered_map &shared_strings() const; // Thumbnail @@ -839,6 +906,11 @@ class XLNT_API workbook /// void swap(workbook &other); + /// + /// Sheet 1 should be rId1, sheet 2 should be rId2, etc. + /// + void reorder_relationships(); + /// /// An opaque pointer to a structure that holds all of the data relating to this workbook. /// diff --git a/xlnt/include/xlnt/workbook/workbook_view.hpp b/xlnt/include/xlnt/workbook/workbook_view.hpp index d74c9a8..6d1efc6 100644 --- a/xlnt/include/xlnt/workbook/workbook_view.hpp +++ b/xlnt/include/xlnt/workbook/workbook_view.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -102,4 +102,17 @@ class XLNT_API workbook_view optional y_window; }; +inline bool operator==(const workbook_view &lhs, const workbook_view &rhs) +{ + return lhs.active_tab == rhs.active_tab + && lhs.auto_filter_date_grouping == rhs.auto_filter_date_grouping + && lhs.first_sheet == rhs.first_sheet + && lhs.minimized == rhs.minimized + && lhs.show_horizontal_scroll == rhs.show_horizontal_scroll + && lhs.show_sheet_tabs == rhs.show_sheet_tabs + && lhs.show_vertical_scroll == rhs.show_vertical_scroll + && lhs.tab_ratio == rhs.tab_ratio + && lhs.visible == rhs.visible; +} + } // namespace xlnt diff --git a/xlnt/include/xlnt/workbook/worksheet_iterator.hpp b/xlnt/include/xlnt/workbook/worksheet_iterator.hpp index f73348e..3e0165f 100644 --- a/xlnt/include/xlnt/workbook/worksheet_iterator.hpp +++ b/xlnt/include/xlnt/workbook/worksheet_iterator.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -37,18 +37,22 @@ class worksheet; // because one needs to point at a const workbook and the other needs // to point at a non-const workbook stored as a member variable, respectively. -/// -/// Alias the parent class of this iterator to increase clarity. -/// -using ws_iter_type = std::iterator; /// /// An iterator which is used to iterate over the worksheets in a workbook. /// -class XLNT_API worksheet_iterator : public ws_iter_type +class XLNT_API worksheet_iterator { public: + /// + /// iterator tags required for use with standard algorithms and adapters + /// + using iterator_category = std::bidirectional_iterator_tag; + using value_type = worksheet; + using difference_type = std::ptrdiff_t; + using pointer = worksheet *; + using reference = worksheet; // intentionally value + /// /// Constructs a worksheet iterator from a workbook and sheet index. /// @@ -57,19 +61,41 @@ class XLNT_API worksheet_iterator : public ws_iter_type /// /// Copy constructs a worksheet iterator from another iterator. /// - worksheet_iterator(const worksheet_iterator &); + worksheet_iterator(const worksheet_iterator &) = default; + + /// + /// Copy assigns the iterator so that it points to the same worksheet in the same workbook. + /// + worksheet_iterator &operator=(const worksheet_iterator &) = default; + + /// + /// Move constructs a worksheet iterator from a temporary iterator. + /// + worksheet_iterator(worksheet_iterator &&) = default; + + /// + /// Move assign the iterator from a temporary iterator + /// + worksheet_iterator &operator=(worksheet_iterator &&) = default; + + /// + /// Default destructor + /// + ~worksheet_iterator() = default; /// - /// Assigns the iterator so that it points to the same worksheet in the same workbook. + /// Dereferences the iterator to return the worksheet it is pointing to. + /// If the iterator points to one-past-the-end of the workbook, an invalid_parameter + /// exception will be thrown. /// - worksheet_iterator &operator=(const worksheet_iterator &); + reference operator*(); /// /// Dereferences the iterator to return the worksheet it is pointing to. /// If the iterator points to one-past-the-end of the workbook, an invalid_parameter /// exception will be thrown. /// - worksheet operator*(); + const reference operator*() const; /// /// Returns true if this iterator points to the same worksheet as comparand. @@ -93,11 +119,23 @@ class XLNT_API worksheet_iterator : public ws_iter_type /// worksheet_iterator &operator++(); + /// + /// Post-decrement the iterator's internal workseet index. Returns a copy of the + /// iterator as it was before being incremented. + /// + worksheet_iterator operator--(int); + + /// + /// Pre-decrement the iterator's internal workseet index. Returns a refernce + /// to the same iterator. + /// + worksheet_iterator &operator--(); + private: /// /// The target workbook of this iterator. /// - workbook &wb_; + workbook *wb_; /// /// The index of the worksheet in wb_ this iterator is currently pointing to. @@ -106,18 +144,21 @@ class XLNT_API worksheet_iterator : public ws_iter_type }; -/// -/// Alias the parent class of this iterator to increase clarity. -/// -using c_ws_iter_type = std::iterator; - /// /// An iterator which is used to iterate over the worksheets in a const workbook. /// -class XLNT_API const_worksheet_iterator : public c_ws_iter_type +class XLNT_API const_worksheet_iterator { public: + /// + /// iterator tags required for use with standard algorithms and adapters + /// + using iterator_category = std::bidirectional_iterator_tag; + using value_type = const worksheet; + using difference_type = std::ptrdiff_t; + using pointer = const worksheet *; + using reference = const worksheet; // intentionally value + /// /// Constructs a worksheet iterator from a workbook and sheet index. /// @@ -126,19 +167,34 @@ class XLNT_API const_worksheet_iterator : public c_ws_iter_type /// /// Copy constructs a worksheet iterator from another iterator. /// - const_worksheet_iterator(const const_worksheet_iterator &); + const_worksheet_iterator(const const_worksheet_iterator &) = default; + + /// + /// Copy assigns the iterator so that it points to the same worksheet in the same workbook. + /// + const_worksheet_iterator &operator=(const const_worksheet_iterator &) = default; /// - /// Assigns the iterator so that it points to the same worksheet in the same workbook. + /// Move constructs a worksheet iterator from a temporary iterator. /// - const_worksheet_iterator &operator=(const const_worksheet_iterator &); + const_worksheet_iterator(const_worksheet_iterator &&) = default; + + /// + /// Move assigns the iterator from a temporary iterator + /// + const_worksheet_iterator &operator=(const_worksheet_iterator &&) = default; + + /// + /// Default destructor + /// + ~const_worksheet_iterator() = default; /// /// Dereferences the iterator to return the worksheet it is pointing to. /// If the iterator points to one-past-the-end of the workbook, an invalid_parameter /// exception will be thrown. /// - const worksheet operator*(); + const reference operator*() const; /// /// Returns true if this iterator points to the same worksheet as comparand. @@ -162,11 +218,23 @@ class XLNT_API const_worksheet_iterator : public c_ws_iter_type /// const_worksheet_iterator &operator++(); + /// + /// Post-decrement the iterator's internal workseet index. Returns a copy of the + /// iterator as it was before being incremented. + /// + const_worksheet_iterator operator--(int); + + /// + /// Pre-decrement the iterator's internal workseet index. Returns a refernce + /// to the same iterator. + /// + const_worksheet_iterator &operator--(); + private: /// /// The target workbook of this iterator. /// - const workbook &wb_; + const workbook *wb_; /// /// The index of the worksheet in wb_ this iterator is currently pointing to. diff --git a/xlnt/include/xlnt/worksheet/cell_iterator.hpp b/xlnt/include/xlnt/worksheet/cell_iterator.hpp index 71d6f94..415dee6 100644 --- a/xlnt/include/xlnt/worksheet/cell_iterator.hpp +++ b/xlnt/include/xlnt/worksheet/cell_iterator.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -40,18 +40,21 @@ class cell; class cell_reference; class range_reference; -/// -/// Alias the parent class of this iterator to increase clarity. -/// -using c_iter_type = std::iterator; - /// /// A cell iterator iterates over a 1D range by row or by column. /// -class XLNT_API cell_iterator : public c_iter_type +class XLNT_API cell_iterator { public: + /// + /// iterator tags required for use with standard algorithms and adapters + /// + using iterator_category = std::bidirectional_iterator_tag; + using value_type = cell; + using difference_type = std::ptrdiff_t; + using pointer = cell *; + using reference = cell; // intentionally value + /// /// Constructs a cell_iterator from a worksheet, range, and iteration settings. /// @@ -61,17 +64,38 @@ class XLNT_API cell_iterator : public c_iter_type /// /// Constructs a cell_iterator as a copy of an existing cell_iterator. /// - cell_iterator(const cell_iterator &other); + cell_iterator(const cell_iterator &) = default; /// /// Assigns this iterator to match the data in rhs. /// - cell_iterator &operator=(const cell_iterator &rhs) = default; + cell_iterator &operator=(const cell_iterator &) = default; + + /// + /// Constructs a cell_iterator by moving from a cell_iterator temporary + /// + cell_iterator(cell_iterator &&) = default; + + /// + /// Assigns this iterator to from a cell_iterator temporary + /// + cell_iterator &operator=(cell_iterator &&) = default; + + /// + /// destructor for const_cell_iterator + /// + ~cell_iterator() = default; /// /// Dereferences this iterator to return the cell it points to. /// - cell operator*(); + reference operator*(); + + /// + /// Dereferences this iterator to return the cell it points to. + /// + const reference operator*() const; + /// /// Returns true if this iterator is equivalent to other. @@ -135,7 +159,7 @@ class XLNT_API cell_iterator : public c_iter_type /// If true, cells that don't exist in the worksheet will be skipped during iteration. /// bool skip_null_; - + /// /// If true, when on the last column, the cursor will continue to the next row /// (and vice versa when iterating in column-major order) until reaching the @@ -144,18 +168,21 @@ class XLNT_API cell_iterator : public c_iter_type bool wrap_; }; -/// -/// Alias the parent class of this iterator to increase clarity. -/// -using cc_iter_type = std::iterator; - /// /// A cell iterator iterates over a 1D range by row or by column. /// -class XLNT_API const_cell_iterator : public cc_iter_type +class XLNT_API const_cell_iterator { public: + /// + /// iterator tags required for use with standard algorithms and adapters + /// + using iterator_category = std::bidirectional_iterator_tag; + using value_type = const cell; + using difference_type = std::ptrdiff_t; + using pointer = const cell *; + using reference = const cell; // intentionally value + /// /// Constructs a cell_iterator from a worksheet, range, and iteration settings. /// @@ -163,19 +190,34 @@ class XLNT_API const_cell_iterator : public cc_iter_type const range_reference &limits, major_order order, bool skip_null, bool wrap); /// - /// Constructs a cell_iterator as a copy of an existing cell_iterator. + /// Constructs a const_cell_iterator as a copy of an existing cell_iterator. /// - const_cell_iterator(const const_cell_iterator &other); + const_cell_iterator(const const_cell_iterator &) = default; /// /// Assigns this iterator to match the data in rhs. /// const_cell_iterator &operator=(const const_cell_iterator &) = default; + /// + /// Constructs a const_cell_iterator by moving from a const_cell_iterator temporary + /// + const_cell_iterator(const_cell_iterator &&) = default; + + /// + /// Assigns this iterator to from a const_cell_iterator temporary + /// + const_cell_iterator &operator=(const_cell_iterator &&) = default; + + /// + /// destructor for const_cell_iterator + /// + ~const_cell_iterator() = default; + /// /// Dereferences this iterator to return the cell it points to. /// - const cell operator*() const; + const reference operator*() const; /// /// Returns true if this iterator is equivalent to other. diff --git a/xlnt/include/xlnt/worksheet/cell_vector.hpp b/xlnt/include/xlnt/worksheet/cell_vector.hpp index 33c741c..cb83924 100644 --- a/xlnt/include/xlnt/worksheet/cell_vector.hpp +++ b/xlnt/include/xlnt/worksheet/cell_vector.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/include/xlnt/worksheet/column_properties.hpp b/xlnt/include/xlnt/worksheet/column_properties.hpp index 5619c02..3254993 100644 --- a/xlnt/include/xlnt/worksheet/column_properties.hpp +++ b/xlnt/include/xlnt/worksheet/column_properties.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -51,10 +51,25 @@ class XLNT_API column_properties /// optional style; + /// + /// Is this column sized to fit its content as best it can + /// serialise if true + /// + bool best_fit = false; + /// /// If true, this column will be hidden /// bool hidden = false; }; +inline bool operator==(const column_properties &lhs, const column_properties &rhs) +{ + return lhs.width == rhs.width + && lhs.custom_width == rhs.custom_width + && lhs.style == rhs.style + && lhs.best_fit == rhs.best_fit + && lhs.hidden == rhs.hidden; +} + } // namespace xlnt diff --git a/xlnt/include/xlnt/worksheet/header_footer.hpp b/xlnt/include/xlnt/worksheet/header_footer.hpp index bb139af..3b70b1f 100644 --- a/xlnt/include/xlnt/worksheet/header_footer.hpp +++ b/xlnt/include/xlnt/worksheet/header_footer.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -312,6 +312,8 @@ class XLNT_API header_footer /// rich_text even_footer(location where) const; + bool operator==(const header_footer &rhs) const; + private: bool align_with_margins_ = false; bool different_odd_even_ = false; diff --git a/xlnt/include/xlnt/worksheet/major_order.hpp b/xlnt/include/xlnt/worksheet/major_order.hpp index 30b62cd..46f0a20 100644 --- a/xlnt/include/xlnt/worksheet/major_order.hpp +++ b/xlnt/include/xlnt/worksheet/major_order.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/include/xlnt/worksheet/page_margins.hpp b/xlnt/include/xlnt/worksheet/page_margins.hpp index 23b426f..7f38f8b 100644 --- a/xlnt/include/xlnt/worksheet/page_margins.hpp +++ b/xlnt/include/xlnt/worksheet/page_margins.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -99,6 +99,8 @@ class XLNT_API page_margins /// void footer(double footer); + bool operator==(const page_margins &rhs) const; + private: /// /// The top margin diff --git a/xlnt/include/xlnt/worksheet/page_setup.hpp b/xlnt/include/xlnt/worksheet/page_setup.hpp index ae398bf..03d6a01 100644 --- a/xlnt/include/xlnt/worksheet/page_setup.hpp +++ b/xlnt/include/xlnt/worksheet/page_setup.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -25,6 +25,7 @@ #pragma once #include +#include namespace xlnt { @@ -33,6 +34,7 @@ namespace xlnt { /// enum class XLNT_API orientation { + default_orientation, portrait, landscape }; @@ -117,16 +119,6 @@ struct XLNT_API page_setup /// void paper_size(xlnt::paper_size paper_size); - /// - /// Returns the orientation of the worksheet using this page setup. - /// - xlnt::orientation orientation() const; - - /// - /// Sets the orientation of the page. - /// - void orientation(xlnt::orientation orientation); - /// /// Returns true if this worksheet should be scaled to fit on a single page during printing. /// @@ -158,36 +150,29 @@ struct XLNT_API page_setup void fit_to_width(bool fit_to_width); /// - /// Sets whether the worksheet should be centered horizontall on the page if it takes - /// up less than a full page. + /// Sets the factor by which the page should be scaled during printing. /// - void horizontal_centered(bool horizontal_centered); + void scale(double scale); /// - /// Returns whether horizontal centering has been enabled. + /// Returns the factor by which the page should be scaled during printing. /// - bool horizontal_centered() const; + double scale() const; /// - /// Sets whether the worksheet should be vertically centered on the page if it takes - /// up less than a full page. + /// The orientation /// - void vertical_centered(bool vertical_centered); - + xlnt::optional orientation_; /// - /// Returns whether vertical centering has been enabled. + /// The horizontal dpi /// - bool vertical_centered() const; - + xlnt::optional horizontal_dpi_; /// - /// Sets the factor by which the page should be scaled during printing. + /// The vertical dpi /// - void scale(double scale); + xlnt::optional vertical_dpi_; - /// - /// Returns the factor by which the page should be scaled during printing. - /// - double scale() const; + bool operator==(const page_setup &rhs) const; private: /// @@ -205,11 +190,6 @@ struct XLNT_API page_setup /// xlnt::paper_size paper_size_; - /// - /// The orientation - /// - xlnt::orientation orientation_; - /// /// Whether or not to fit to page /// @@ -225,20 +205,10 @@ struct XLNT_API page_setup /// bool fit_to_width_; - /// - /// Whether or not to center the content horizontally - /// - bool horizontal_centered_; - - /// - /// Whether or not to center the conent vertically - /// - bool vertical_centered_; - /// /// The amount to scale the worksheet /// double scale_; }; -} // namespace xlnt +} // namespace xlnt \ No newline at end of file diff --git a/xlnt/include/xlnt/worksheet/pane.hpp b/xlnt/include/xlnt/worksheet/pane.hpp index 7ed8d17..e8bb3c7 100644 --- a/xlnt/include/xlnt/worksheet/pane.hpp +++ b/xlnt/include/xlnt/worksheet/pane.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/xlnt/include/xlnt/worksheet/range.hpp b/xlnt/include/xlnt/worksheet/range.hpp index 6c5272c..02b88a1 100644 --- a/xlnt/include/xlnt/worksheet/range.hpp +++ b/xlnt/include/xlnt/worksheet/range.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -117,6 +117,11 @@ class XLNT_API range /// const class cell cell(const cell_reference &ref) const; + /// + /// The worksheet this range targets + /// + const worksheet & target_worksheet() const; + /// /// Returns the reference defining the bounds of this range. /// @@ -285,11 +290,11 @@ class XLNT_API range /// bool operator!=(const range &comparand) const; -private: + private: /// /// The worksheet this range is within /// - worksheet ws_; + class worksheet ws_; /// /// The reference of this range diff --git a/xlnt/include/xlnt/worksheet/range_iterator.hpp b/xlnt/include/xlnt/worksheet/range_iterator.hpp index 1b47a51..c6f4397 100644 --- a/xlnt/include/xlnt/worksheet/range_iterator.hpp +++ b/xlnt/include/xlnt/worksheet/range_iterator.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -35,19 +35,22 @@ namespace xlnt { class cell_vector; -/// -/// Alias the parent class of this iterator to increase clarity. -/// -using r_iter_type = std::iterator; - /// /// An iterator used by worksheet and range for traversing /// a 2D grid of cells by row/column then across that row/column. /// -class XLNT_API range_iterator : public r_iter_type +class XLNT_API range_iterator { public: + /// + /// iterator tags required for use with standard algorithms and adapters + /// + using iterator_category = std::bidirectional_iterator_tag; + using value_type = cell_vector; + using difference_type = std::ptrdiff_t; + using pointer = cell_vector *; + using reference = cell_vector; // intentionally value + /// /// Constructs a range iterator on a worksheet, cell pointing to the current /// row or column, range bounds, an order, and whether or not to skip null column/rows. @@ -56,19 +59,39 @@ class XLNT_API range_iterator : public r_iter_type const range_reference &bounds, major_order order, bool skip_null); /// - /// Copy constructor. + /// Default copy constructor. + /// + range_iterator(const range_iterator &) = default; + + /// + /// Default assignment operator. + /// + range_iterator &operator=(const range_iterator &) = default; + + /// + /// Default move constructor. + /// + range_iterator(range_iterator &&) = default; + + /// + /// Default move assignment operator. /// - range_iterator(const range_iterator &other); + range_iterator &operator=(range_iterator &&) = default; + + /// + /// Default destructor + /// + ~range_iterator() = default; /// /// Dereference the iterator to return a column or row. /// - cell_vector operator*() const; + reference operator*(); /// - /// Default assignment operator. + /// Dereference the iterator to return a column or row. /// - range_iterator &operator=(const range_iterator &) = default; + const reference operator*() const; /// /// Returns true if this iterator is equivalent to other. @@ -127,19 +150,22 @@ class XLNT_API range_iterator : public r_iter_type bool skip_null_; }; -/// -/// Alias the parent class of this iterator to increase clarity. -/// -using cr_iter_type = std::iterator; - /// /// A const version of range_iterator which does not allow modification /// to the dereferenced cell_vector. /// -class XLNT_API const_range_iterator : public cr_iter_type +class XLNT_API const_range_iterator { public: + /// + /// this iterator meets the interface requirements of bidirection_iterator + /// + using iterator_category = std::bidirectional_iterator_tag; + using value_type = const cell_vector; + using difference_type = std::ptrdiff_t; + using pointer = const cell_vector *; + using reference = const cell_vector; // intentionally value + /// /// Constructs a range iterator on a worksheet, cell pointing to the current /// row or column, range bounds, an order, and whether or not to skip null column/rows. @@ -148,19 +174,34 @@ class XLNT_API const_range_iterator : public cr_iter_type const range_reference &bounds, major_order order, bool skip_null); /// - /// Copy constructor. + /// Default copy constructor. /// - const_range_iterator(const const_range_iterator &other); + const_range_iterator(const const_range_iterator &) = default; /// - /// Dereferennce the iterator to return the current column/row. + /// Default assignment operator. /// - const cell_vector operator*() const; + const_range_iterator &operator=(const const_range_iterator &) = default; /// - /// Default assignment operator. + /// Default move constructor. /// - const_range_iterator &operator=(const const_range_iterator &) = default; + const_range_iterator(const_range_iterator &&) = default; + + /// + /// Default move assignment operator. + /// + const_range_iterator &operator=(const_range_iterator &&) = default; + + /// + /// Default destructor + /// + ~const_range_iterator() = default; + + /// + /// Dereferennce the iterator to return the current column/row. + /// + const reference operator*() const; /// /// Returns true if this iterator is equivalent to other. diff --git a/xlnt/include/xlnt/worksheet/range_reference.hpp b/xlnt/include/xlnt/worksheet/range_reference.hpp index 44f6b76..93643f6 100644 --- a/xlnt/include/xlnt/worksheet/range_reference.hpp +++ b/xlnt/include/xlnt/worksheet/range_reference.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -56,12 +56,7 @@ class XLNT_API range_reference /// top_left:bottom_right. /// explicit range_reference(const char *range_string); - - /// - /// Constructs a range reference from a pair of cell references. - /// - explicit range_reference(const std::pair &reference_pair); - + /// /// Constructs a range reference from cell references indicating top /// left and bottom right coordinates of the range. @@ -74,6 +69,8 @@ class XLNT_API range_reference range_reference(column_t column_index_start, row_t row_index_start, column_t column_index_end, row_t row_index_end); + range_reference(const range_reference &ref); + /// /// Returns true if the range has a width and height of 1 cell. /// @@ -154,6 +151,11 @@ class XLNT_API range_reference /// bool operator!=(const char *reference_string) const; + /// + /// Assigns the extents of the provided range to this range. + /// + range_reference &operator=(const range_reference &ref); + private: /// /// The top left cell in the range diff --git a/xlnt/include/xlnt/worksheet/row_properties.hpp b/xlnt/include/xlnt/worksheet/row_properties.hpp index 911492e..d78c431 100644 --- a/xlnt/include/xlnt/worksheet/row_properties.hpp +++ b/xlnt/include/xlnt/worksheet/row_properties.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -35,10 +35,15 @@ class XLNT_API row_properties { public: /// - /// Optional height + /// Row height /// optional height; + /// + /// Distance in pixels from the bottom of the cell to the baseline of the cell content + /// + optional dy_descent; + /// /// Whether or not the height is different from the default /// @@ -49,10 +54,25 @@ class XLNT_API row_properties /// bool hidden = false; + /// + /// True if row style should be applied + /// + optional custom_format; + /// /// The index to the style used by all cells in this row /// optional style; }; +inline bool operator==(const row_properties &lhs, const row_properties &rhs) +{ + return lhs.height == rhs.height + && lhs.dy_descent == rhs.dy_descent + && lhs.custom_height == rhs.custom_height + && lhs.hidden == rhs.hidden + && lhs.custom_format == rhs.custom_format + && lhs.style == rhs.style; +} + } // namespace xlnt diff --git a/xlnt/include/xlnt/worksheet/selection.hpp b/xlnt/include/xlnt/worksheet/selection.hpp index e467c99..f88519d 100644 --- a/xlnt/include/xlnt/worksheet/selection.hpp +++ b/xlnt/include/xlnt/worksheet/selection.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -36,6 +36,27 @@ namespace xlnt { class XLNT_API selection { public: + /// + /// default ctor + /// + explicit selection() = default; + + /// + /// ctor when no range selected + /// sqref == active_cell + /// + explicit selection(pane_corner quadrant, cell_reference active_cell) + : active_cell_(active_cell), sqref_(range_reference(active_cell, active_cell)), pane_(quadrant) + {} + + /// + /// ctor with selected range + /// sqref must contain active_cell + /// + explicit selection(pane_corner quadrant, cell_reference active_cell, range_reference selected) + : active_cell_(active_cell), sqref_(selected), pane_(quadrant) + {} + /// /// Returns true if this selection has a defined active cell. /// @@ -60,12 +81,36 @@ class XLNT_API selection active_cell_ = ref; } + /// + /// Returns true if this selection has a defined sqref. + /// + bool has_sqref() const + { + return sqref_.is_set(); + } + /// /// Returns the range encompassed by this selection. /// range_reference sqref() const { - return sqref_; + return sqref_.get(); + } + + /// + /// Sets the range encompassed by this selection. + /// + void sqref(const range_reference &ref) + { + sqref_ = ref; + } + + /// + /// Sets the range encompassed by this selection. + /// + void sqref(const std::string &ref) + { + sqref(range_reference(ref)); } /// @@ -97,17 +142,18 @@ class XLNT_API selection private: /// - /// The active cell + /// The last selected cell in the selection /// optional active_cell_; /// - /// The range + /// The last selected block in the selection + /// contains active_cell_, normally == to active_cell_ /// - range_reference sqref_; + optional sqref_; /// - /// The quadrant + /// The corner of the worksheet that this selection extends to /// pane_corner pane_; }; diff --git a/xlnt/include/xlnt/worksheet/sheet_protection.hpp b/xlnt/include/xlnt/worksheet/sheet_protection.hpp index 67d8e50..e3e783e 100644 --- a/xlnt/include/xlnt/worksheet/sheet_protection.hpp +++ b/xlnt/include/xlnt/worksheet/sheet_protection.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/xlnt/include/xlnt/worksheet/sheet_view.hpp b/xlnt/include/xlnt/worksheet/sheet_view.hpp index 0c91268..2c04bd9 100644 --- a/xlnt/include/xlnt/worksheet/sheet_view.hpp +++ b/xlnt/include/xlnt/worksheet/sheet_view.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -192,6 +192,30 @@ class XLNT_API sheet_view return type_; } + /// + /// has a top left cell? + /// + bool has_top_left_cell() const + { + return top_left_cell_.is_set(); + } + + /// + /// Sets the top left cell of this view. + /// + void top_left_cell(const cell_reference& ref) + { + top_left_cell_.set(ref); + } + + /// + /// Returns the top left cell of this view. + /// + cell_reference top_left_cell() const + { + return top_left_cell_.get(); + } + /// /// Returns true if this view is equal to rhs based on its id, grid lines setting, /// default grid color, pane, and selections. @@ -202,7 +226,8 @@ class XLNT_API sheet_view && show_grid_lines_ == rhs.show_grid_lines_ && default_grid_color_ == rhs.default_grid_color_ && pane_ == rhs.pane_ - && selections_ == rhs.selections_; + && selections_ == rhs.selections_ + && top_left_cell_ == rhs.top_left_cell_; } private: @@ -231,6 +256,11 @@ class XLNT_API sheet_view /// optional pane_; + /// + /// The top left cell + /// + optional top_left_cell_; + /// /// The collection of selections /// diff --git a/xlnt/include/xlnt/worksheet/worksheet.hpp b/xlnt/include/xlnt/worksheet/worksheet.hpp index 018d8fc..ed46099 100644 --- a/xlnt/include/xlnt/worksheet/worksheet.hpp +++ b/xlnt/include/xlnt/worksheet/worksheet.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -54,7 +54,9 @@ class range_iterator; class range_reference; class relationship; class row_properties; +class sheet_format_properties; class workbook; +class phonetic_pr; struct date; @@ -555,6 +557,21 @@ class XLNT_API worksheet /// void reserve(std::size_t n); + /// + /// Returns true if this sheet has phonetic properties + /// + bool has_phonetic_properties() const; + + /// + /// Returns the phonetic properties of this sheet. + /// + const phonetic_pr &phonetic_properties() const; + + /// + /// Sets the phonetic properties of this sheet to phonetic_props + /// + void phonetic_properties(const phonetic_pr& phonetic_props); + /// /// Returns true if this sheet has a header/footer. /// @@ -653,7 +670,7 @@ class XLNT_API worksheet /// /// Returns the view at the given index. /// - sheet_view view(std::size_t index = 0) const; + sheet_view &view(std::size_t index = 0) const; /// /// Adds new_view to the set of available views for this sheet. @@ -708,6 +725,26 @@ class XLNT_API worksheet /// xlnt::conditional_format conditional_format(const range_reference &ref, const condition &when); + /// + /// Returns the path of this worksheet in the containing package. + /// + xlnt::path path() const; + + /// + /// Returns the relationship from the parent workbook to this worksheet. + /// + relationship referring_relationship() const; + + /// + /// Returns the current formatting properties. + /// + sheet_format_properties format_properties() const; + + /// + /// Sets the format properties to the given properties. + /// + void format_properties(const sheet_format_properties &properties); + private: friend class cell; friend class const_range_iterator; diff --git a/xlnt/include/xlnt/xlnt.hpp b/xlnt/include/xlnt/xlnt.hpp index 7a4daf3..01fe498 100644 --- a/xlnt/include/xlnt/xlnt.hpp +++ b/xlnt/include/xlnt/xlnt.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include diff --git a/xlnt/include/xlnt/xlnt_config.hpp b/xlnt/include/xlnt/xlnt_config.hpp index 399657f..5c8ca97 100644 --- a/xlnt/include/xlnt/xlnt_config.hpp +++ b/xlnt/include/xlnt/xlnt_config.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/source/CMakeLists.txt b/xlnt/source/CMakeLists.txt index ded96a8..5c924e8 100644 --- a/xlnt/source/CMakeLists.txt +++ b/xlnt/source/CMakeLists.txt @@ -1,11 +1,9 @@ cmake_minimum_required(VERSION 3.1) project(xlnt VERSION 1.2) -# Require C99 and C++11 compilers -set(CMAKE_C_STANDARD 99) -set(CMAKE_C_STANDARD_REQUIRED ON) -set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD ${XLNT_CXX_LANG}) set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CXX_EXTENSIONS OFF) # Project metadata set(PROJECT_VENDOR "Thomas Fussell") @@ -37,12 +35,18 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unknown-pragmas") # ignore MSVC and Clang pragmas elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Weverything") # all warnings - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror") # warnings are errors + # blacklist warnings that are not relevant set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-c++98-compat") # ignore warnings about C++98 compatibility set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-c++98-compat-pedantic") # ignore pedantic warnings about C++98 compatibility set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-padded") # ignore padding warnings set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-documentation-unknown-command") # ignore unknown commands in Javadoc-style comments set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unknown-pragmas") # ignore Windows and GCC pragmas + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unknown-warning-option") # ignore Windows and GCC pragmas + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-float-equal") # don't warn on uses of == for fp types + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-newline-eof") # no longer an issue with post-c++11 standards which mandate include add a newline if neccesary + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-covered-switch-default") # default is often added to switches for completeness or to cover future alternatives + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-exit-time-destructors") # this is just a warning to notify that the destructor will run during exit + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-missing-braces") # Wmissing-field-initializers has less false positives endif() if(STATIC_CRT) @@ -151,6 +155,9 @@ else() target_compile_definitions(xlnt PUBLIC XLNT_STATIC=1) endif() +# requires cmake 3.8+ +#target_compile_features(xlnt PUBLIC cxx_std_${XLNT_CXX_LANG}) + # Includes target_include_directories(xlnt PUBLIC ${XLNT_INCLUDE_DIR}) target_include_directories(xlnt PRIVATE ${XLNT_SOURCE_DIR}) @@ -173,23 +180,23 @@ if(MSVC) set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/detail/cryptography/aes.cpp PROPERTIES COMPILE_FLAGS "/wd\"4996\"") -endif() - -# Platform- and file-specific settings, Clang -if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/detail/serialization/miniz.cpp - PROPERTIES - COMPILE_FLAGS "-Wno-undef") - set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/detail/serialization/zstream.cpp - PROPERTIES - COMPILE_FLAGS "-Wno-undef -Wno-shorten-64-to-32") -endif() - -# Platform- and file-specific settings, GCC -if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") - set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/detail/serialization/miniz.cpp - PROPERTIES - COMPILE_FLAGS "-Wno-strict-aliasing") +else() + # Platform- and file-specific settings, Clang + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/detail/serialization/miniz.cpp + PROPERTIES + COMPILE_FLAGS "-Wno-undef") + set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/detail/serialization/zstream.cpp + PROPERTIES + COMPILE_FLAGS "-Wno-undef -Wno-shorten-64-to-32") + endif() + + # Platform- and file-specific settings, GCC + if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") + set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/detail/serialization/miniz.cpp + PROPERTIES + COMPILE_FLAGS "-Wno-strict-aliasing") + endif() endif() # Group files into pseudo-folders in IDEs diff --git a/xlnt/source/cell/cell.cpp b/xlnt/source/cell/cell.cpp index 6a0545a..667212d 100644 --- a/xlnt/source/cell/cell.cpp +++ b/xlnt/source/cell/cell.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -28,12 +28,15 @@ #include #include +#include #include #include #include #include #include +#include #include +#include #include #include #include @@ -195,7 +198,7 @@ cell::cell(detail::cell_impl *d) bool cell::garbage_collectible() const { - return !(has_value() || is_merged() || has_formula() || has_format()); + return !(has_value() || is_merged() || has_formula() || has_format() || has_hyperlink()); } void cell::value(std::nullptr_t) @@ -311,6 +314,11 @@ column_t cell::column() const return d_->column_; } +column_t::index_t cell::column_index() const +{ + return d_->column_.index; +} + void cell::merged(bool merged) { d_->is_merged_ = merged; @@ -333,57 +341,104 @@ cell_reference cell::reference() const return {d_->column_, d_->row_}; } -bool cell::operator==(std::nullptr_t) const -{ - return d_ == nullptr; -} - bool cell::operator==(const cell &comparand) const { return d_ == comparand.d_; } -cell &cell::operator=(const cell &rhs) +bool cell::operator!=(const cell &comparand) const { - d_->column_ = rhs.d_->column_; - d_->format_ = rhs.d_->format_; - d_->formula_ = rhs.d_->formula_; - d_->hyperlink_ = rhs.d_->hyperlink_; - d_->is_merged_ = rhs.d_->is_merged_; - d_->parent_ = rhs.d_->parent_; - d_->row_ = rhs.d_->row_; - d_->type_ = rhs.d_->type_; - d_->value_numeric_ = rhs.d_->value_numeric_; - d_->value_text_ = rhs.d_->value_text_; - - return *this; + return d_ != comparand.d_; } -std::string cell::hyperlink() const +cell &cell::operator=(const cell &rhs) = default; + +hyperlink cell::hyperlink() const { - return d_->hyperlink_.get(); + return xlnt::hyperlink(&d_->hyperlink_.get()); } -void cell::hyperlink(const std::string &hyperlink) +void cell::hyperlink(const std::string &url, const std::string &display) { - if (hyperlink.length() == 0 - || std::find(hyperlink.begin(), hyperlink.end(), ':') == hyperlink.end()) + if (url.empty() || std::find(url.begin(), url.end(), ':') == url.end()) { throw invalid_parameter(); } - d_->hyperlink_ = hyperlink; + auto ws = worksheet(); + auto &manifest = ws.workbook().manifest(); + + d_->hyperlink_ = detail::hyperlink_impl(); + + // check for existing relationships + auto relationships = manifest.relationships(ws.path(), relationship_type::hyperlink); + auto relation = std::find_if(relationships.cbegin(), relationships.cend(), + [&url](xlnt::relationship rel) { return rel.target().path().string() == url; }); + if (relation != relationships.end()) + { + d_->hyperlink_.get().relationship = *relation; + } + else + { // register a new relationship + auto rel_id = manifest.register_relationship( + uri(ws.path().string()), + relationship_type::hyperlink, + uri(url), + target_mode::external); + // TODO: make manifest::register_relationship return the created relationship instead of rel id + d_->hyperlink_.get().relationship = manifest.relationship(ws.path(), rel_id); + } + // if a value is already present, the display string is ignored + if (has_value()) + { + d_->hyperlink_.get().display.set(to_string()); + } + else + { + d_->hyperlink_.get().display.set(display.empty() ? url : display); + value(hyperlink().display()); + } } -void cell::hyperlink(const std::string &url, const std::string &display) +void cell::hyperlink(xlnt::cell target, const std::string& display) { - hyperlink(url); - value(display); + // TODO: should this computed value be a method on a cell? + const auto cell_address = target.worksheet().title() + "!" + target.reference().to_string(); + + d_->hyperlink_ = detail::hyperlink_impl(); + d_->hyperlink_.get().relationship = xlnt::relationship("", relationship_type::hyperlink, + uri(""), uri(cell_address), target_mode::internal); + // if a value is already present, the display string is ignored + if (has_value()) + { + d_->hyperlink_.get().display.set(to_string()); + } + else + { + d_->hyperlink_.get().display.set(display.empty() ? cell_address : display); + value(hyperlink().display()); + } } -void cell::hyperlink(xlnt::cell /*target*/) +void cell::hyperlink(xlnt::range target, const std::string &display) { - //todo: implement + // TODO: should this computed value be a method on a cell? + const auto range_address = target.target_worksheet().title() + "!" + target.reference().to_string(); + + d_->hyperlink_ = detail::hyperlink_impl(); + d_->hyperlink_.get().relationship = xlnt::relationship("", relationship_type::hyperlink, + uri(""), uri(range_address), target_mode::internal); + + // if a value is already present, the display string is ignored + if (has_value()) + { + d_->hyperlink_.get().display.set(to_string()); + } + else + { + d_->hyperlink_.get().display.set(display.empty() ? range_address : display); + value(hyperlink().display()); + } } void cell::formula(const std::string &formula) @@ -402,8 +457,6 @@ void cell::formula(const std::string &formula) d_->formula_ = formula; } - data_type(type::number); - worksheet().register_calc_chain_in_manifest(); } @@ -426,6 +479,15 @@ void cell::clear_formula() } } +std::string cell::error() const +{ + if (d_->type_ != type::error) + { + throw xlnt::exception("called error() when cell type is not error"); + } + return value(); +} + void cell::error(const std::string &error) { if (error.length() == 0 || error[0] != '#') @@ -433,7 +495,7 @@ void cell::error(const std::string &error) throw invalid_data_type(); } - d_->value_text_.plain_text(error); + d_->value_text_.plain_text(error, false); d_->type_ = type::error; } @@ -598,37 +660,37 @@ XLNT_API timedelta cell::value() const void cell::alignment(const class alignment &alignment_) { auto new_format = has_format() ? modifiable_format() : workbook().create_format(); - format(new_format.alignment(alignment_, true)); + format(new_format.alignment(alignment_, optional(true))); } void cell::border(const class border &border_) { auto new_format = has_format() ? modifiable_format() : workbook().create_format(); - format(new_format.border(border_, true)); + format(new_format.border(border_, optional(true))); } void cell::fill(const class fill &fill_) { auto new_format = has_format() ? modifiable_format() : workbook().create_format(); - format(new_format.fill(fill_, true)); + format(new_format.fill(fill_, optional(true))); } void cell::font(const class font &font_) { auto new_format = has_format() ? modifiable_format() : workbook().create_format(); - format(new_format.font(font_, true)); + format(new_format.font(font_, optional(true))); } void cell::number_format(const class number_format &number_format_) { auto new_format = has_format() ? modifiable_format() : workbook().create_format(); - format(new_format.number_format(number_format_, true)); + format(new_format.number_format(number_format_, optional(true))); } void cell::protection(const class protection &protection_) { auto new_format = has_format() ? modifiable_format() : workbook().create_format(); - format(new_format.protection(protection_, true)); + format(new_format.protection(protection_, optional(true))); } template <> @@ -642,7 +704,7 @@ XLNT_API rich_text cell::value() const { if (data_type() == cell::type::shared_string) { - return workbook().shared_strings().at(static_cast(d_->value_numeric_)); + return workbook().shared_strings(static_cast(d_->value_numeric_)); } return d_->value_text_; @@ -697,6 +759,16 @@ calendar cell::base_date() const return workbook().base_date(); } +bool operator==(std::nullptr_t, const cell &cell) +{ + return cell.data_type() == cell::type::empty; +} + +bool operator==(const cell &cell, std::nullptr_t) +{ + return nullptr == cell; +} + XLNT_API std::ostream &operator<<(std::ostream &stream, const xlnt::cell &cell) { return stream << cell.to_string(); @@ -756,8 +828,11 @@ void cell::value(const std::string &value_string, bool infer_type) void cell::clear_format() { - format().d_->references -= format().d_->references > 0 ? 1 : 0; - d_->format_.clear(); + if (d_->format_.is_set()) + { + format().d_->references -= format().d_->references > 0 ? 1 : 0; + d_->format_.clear(); + } } void cell::clear_style() @@ -786,19 +861,19 @@ style cell::style() throw invalid_attribute(); } - auto f = format(); + auto f = format(); return f.style(); } const style cell::style() const { - if (!has_format() || !format().has_style()) - { - throw invalid_attribute(); - } + if (!has_format() || !format().has_style()) + { + throw invalid_attribute(); + } - return format().style(); + return format().style(); } bool cell::has_style() const diff --git a/xlnt/source/cell/cell_reference.cpp b/xlnt/source/cell/cell_reference.cpp index 42bea3f..ea4ad83 100644 --- a/xlnt/source/cell/cell_reference.cpp +++ b/xlnt/source/cell/cell_reference.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/xlnt/source/cell/comment.cpp b/xlnt/source/cell/comment.cpp index 0eb7aa2..16722d2 100644 --- a/xlnt/source/cell/comment.cpp +++ b/xlnt/source/cell/comment.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -38,7 +38,7 @@ comment::comment(const rich_text &text, const std::string &author) comment::comment(const std::string &text, const std::string &author) : text_(), author_(author) { - text_.plain_text(text); + text_.plain_text(text, false); } rich_text comment::text() const diff --git a/xlnt/source/cell/index_types.cpp b/xlnt/source/cell/index_types.cpp index c330421..1d04031 100644 --- a/xlnt/source/cell/index_types.cpp +++ b/xlnt/source/cell/index_types.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/xlnt/source/cell/rich_text.cpp b/xlnt/source/cell/rich_text.cpp index 44bb558..3cf1329 100644 --- a/xlnt/source/cell/rich_text.cpp +++ b/xlnt/source/cell/rich_text.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -26,16 +26,35 @@ #include #include +namespace { +bool has_trailing_whitespace(const std::string &s) +{ + return !s.empty() && (s.front() == ' ' || s.back() == ' '); +}; +} + namespace xlnt { rich_text::rich_text(const std::string &plain_text) - : rich_text({plain_text, optional()}) + : rich_text(rich_text_run{plain_text, optional(), has_trailing_whitespace(plain_text)}) { } rich_text::rich_text(const std::string &plain_text, const class font &text_font) - : rich_text({plain_text, optional(text_font)}) + : rich_text(rich_text_run{plain_text, optional(text_font), has_trailing_whitespace(plain_text)}) +{ +} + +rich_text::rich_text(const rich_text &other) +{ + *this = other; +} + +rich_text &rich_text::operator=(const rich_text &rhs) { + runs_.clear(); + runs_ = rhs.runs_; + return *this; } rich_text::rich_text(const rich_text_run &single_run) @@ -48,14 +67,19 @@ void rich_text::clear() runs_.clear(); } -void rich_text::plain_text(const std::string &s) +void rich_text::plain_text(const std::string &s, bool preserve_space = false) { clear(); - add_run(rich_text_run{s, {}}); + add_run(rich_text_run{s, {}, preserve_space}); } std::string rich_text::plain_text() const { + if (runs_.size() == 1) + { + return runs_.begin()->first; + } + return std::accumulate(runs_.begin(), runs_.end(), std::string(), [](const std::string &a, const rich_text_run &run) { return a + run.first; }); } @@ -65,6 +89,11 @@ std::vector rich_text::runs() const return runs_; } +void rich_text::runs(const std::vector &new_runs) +{ + runs_ = new_runs; +} + void rich_text::add_run(const rich_text_run &t) { runs_.push_back(t); diff --git a/xlnt/source/cell/rich_text_run.cpp b/xlnt/source/cell/rich_text_run.cpp index 79b2456..2d717d0 100644 --- a/xlnt/source/cell/rich_text_run.cpp +++ b/xlnt/source/cell/rich_text_run.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/source/detail/binary.hpp b/xlnt/source/detail/binary.hpp index e908870..cda8e56 100644 --- a/xlnt/source/detail/binary.hpp +++ b/xlnt/source/detail/binary.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/source/detail/constants.cpp b/xlnt/source/detail/constants.cpp index 759511e..9ccb4cd 100644 --- a/xlnt/source/detail/constants.cpp +++ b/xlnt/source/detail/constants.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/source/detail/constants.hpp b/xlnt/source/detail/constants.hpp index b88c5c6..719fcd1 100644 --- a/xlnt/source/detail/constants.hpp +++ b/xlnt/source/detail/constants.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/source/detail/cryptography/aes.cpp b/xlnt/source/detail/cryptography/aes.cpp index d88c66b..87a2cf7 100644 --- a/xlnt/source/detail/cryptography/aes.cpp +++ b/xlnt/source/detail/cryptography/aes.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017 Thomas Fussell +// Copyright (c) 2017-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/source/detail/cryptography/aes.hpp b/xlnt/source/detail/cryptography/aes.hpp index e1657bd..6c0af31 100644 --- a/xlnt/source/detail/cryptography/aes.hpp +++ b/xlnt/source/detail/cryptography/aes.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017 Thomas Fussell +// Copyright (c) 2017-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/source/detail/cryptography/base64.cpp b/xlnt/source/detail/cryptography/base64.cpp index 90c7a83..ddc9322 100644 --- a/xlnt/source/detail/cryptography/base64.cpp +++ b/xlnt/source/detail/cryptography/base64.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2017 Thomas Fussell +// Copyright (C) 2017-2018 Thomas Fussell // Copyright (C) 2013 Tomas Kislan // Copyright (C) 2013 Adam Rudd // diff --git a/xlnt/source/detail/cryptography/base64.hpp b/xlnt/source/detail/cryptography/base64.hpp index cbe5ac8..7a33922 100644 --- a/xlnt/source/detail/cryptography/base64.hpp +++ b/xlnt/source/detail/cryptography/base64.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017 Thomas Fussell +// Copyright (c) 2017-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/source/detail/cryptography/cipher.hpp b/xlnt/source/detail/cryptography/cipher.hpp index 408af36..4410e81 100644 --- a/xlnt/source/detail/cryptography/cipher.hpp +++ b/xlnt/source/detail/cryptography/cipher.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017 Thomas Fussell +// Copyright (c) 2017-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/source/detail/cryptography/compound_document.cpp b/xlnt/source/detail/cryptography/compound_document.cpp index 3815f17..5026ea7 100644 --- a/xlnt/source/detail/cryptography/compound_document.cpp +++ b/xlnt/source/detail/cryptography/compound_document.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2016-2017 Thomas Fussell +// Copyright (C) 2016-2018 Thomas Fussell // Copyright (C) 2002-2007 Ariya Hidayat (ariya@kde.org). // // Redistribution and use in source and binary forms, with or without diff --git a/xlnt/source/detail/cryptography/encryption_info.cpp b/xlnt/source/detail/cryptography/encryption_info.cpp index 61b1514..968852d 100644 --- a/xlnt/source/detail/cryptography/encryption_info.cpp +++ b/xlnt/source/detail/cryptography/encryption_info.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017 Thomas Fussell +// Copyright (c) 2017-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/source/detail/cryptography/encryption_info.hpp b/xlnt/source/detail/cryptography/encryption_info.hpp index 4f1dff5..98431a4 100644 --- a/xlnt/source/detail/cryptography/encryption_info.hpp +++ b/xlnt/source/detail/cryptography/encryption_info.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017 Thomas Fussell +// Copyright (c) 2017-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/source/detail/cryptography/hash.cpp b/xlnt/source/detail/cryptography/hash.cpp index 0e9ef08..440362d 100644 --- a/xlnt/source/detail/cryptography/hash.cpp +++ b/xlnt/source/detail/cryptography/hash.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017 Thomas Fussell +// Copyright (c) 2017-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/source/detail/cryptography/hash.hpp b/xlnt/source/detail/cryptography/hash.hpp index eeb4a3c..c1ebd36 100644 --- a/xlnt/source/detail/cryptography/hash.hpp +++ b/xlnt/source/detail/cryptography/hash.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017 Thomas Fussell +// Copyright (c) 2017-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/source/detail/cryptography/sha.cpp b/xlnt/source/detail/cryptography/sha.cpp index 67eefbd..02cfd8b 100644 --- a/xlnt/source/detail/cryptography/sha.cpp +++ b/xlnt/source/detail/cryptography/sha.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017 Thomas Fussell +// Copyright (c) 2017-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/source/detail/cryptography/sha.hpp b/xlnt/source/detail/cryptography/sha.hpp index fd3108b..6ac84a7 100644 --- a/xlnt/source/detail/cryptography/sha.hpp +++ b/xlnt/source/detail/cryptography/sha.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017 Thomas Fussell +// Copyright (c) 2017-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/source/detail/cryptography/value_traits.hpp b/xlnt/source/detail/cryptography/value_traits.hpp index 00ab7ba..3f49364 100644 --- a/xlnt/source/detail/cryptography/value_traits.hpp +++ b/xlnt/source/detail/cryptography/value_traits.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017 Thomas Fussell +// Copyright (c) 2017-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/source/detail/cryptography/xlsx_crypto_consumer.cpp b/xlnt/source/detail/cryptography/xlsx_crypto_consumer.cpp index a614523..6370ba0 100644 --- a/xlnt/source/detail/cryptography/xlsx_crypto_consumer.cpp +++ b/xlnt/source/detail/cryptography/xlsx_crypto_consumer.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/source/detail/cryptography/xlsx_crypto_consumer.hpp b/xlnt/source/detail/cryptography/xlsx_crypto_consumer.hpp index f8d5809..78b3e74 100644 --- a/xlnt/source/detail/cryptography/xlsx_crypto_consumer.hpp +++ b/xlnt/source/detail/cryptography/xlsx_crypto_consumer.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/source/detail/cryptography/xlsx_crypto_producer.cpp b/xlnt/source/detail/cryptography/xlsx_crypto_producer.cpp index 2916b15..d9af4cf 100644 --- a/xlnt/source/detail/cryptography/xlsx_crypto_producer.cpp +++ b/xlnt/source/detail/cryptography/xlsx_crypto_producer.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/source/detail/cryptography/xlsx_crypto_producer.hpp b/xlnt/source/detail/cryptography/xlsx_crypto_producer.hpp index 68a03d2..49244ec 100644 --- a/xlnt/source/detail/cryptography/xlsx_crypto_producer.hpp +++ b/xlnt/source/detail/cryptography/xlsx_crypto_producer.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/source/detail/default_case.hpp b/xlnt/source/detail/default_case.hpp index f1f8953..e7c147e 100644 --- a/xlnt/source/detail/default_case.hpp +++ b/xlnt/source/detail/default_case.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2017 Thomas Fussell +// Copyright (c) 2016-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/source/detail/external/include_libstudxml.hpp b/xlnt/source/detail/external/include_libstudxml.hpp index ec8493a..ec1c18e 100644 --- a/xlnt/source/detail/external/include_libstudxml.hpp +++ b/xlnt/source/detail/external/include_libstudxml.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/source/detail/external/include_windows.hpp b/xlnt/source/detail/external/include_windows.hpp index 737321a..62e77b4 100644 --- a/xlnt/source/detail/external/include_windows.hpp +++ b/xlnt/source/detail/external/include_windows.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/source/detail/header_footer/header_footer_code.cpp b/xlnt/source/detail/header_footer/header_footer_code.cpp index efbe7b9..f97ef65 100644 --- a/xlnt/source/detail/header_footer/header_footer_code.cpp +++ b/xlnt/source/detail/header_footer/header_footer_code.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -22,6 +22,7 @@ // @author: see AUTHORS file #include +#include namespace xlnt { namespace detail { @@ -359,6 +360,11 @@ std::array, 3> decode_header_footer(const std::s case hf_code::text_single_underline: { + if (!current_run.second.is_set()) + { + current_run.second = xlnt::font(); + } + current_run.second.get().underline(font::underline_style::single); break; } @@ -527,9 +533,24 @@ std::string encode_header_footer(const rich_text &t, header_footer::location whe if (run.second.get().has_size()) { encoded.push_back('&'); - encoded.append(std::to_string(run.second.get().size())); + encoded.append(serialize_number_to_string(run.second.get().size())); + } + if (run.second.get().underlined()) + { + switch (run.second.get().underline()) + { + case font::underline_style::single: + case font::underline_style::single_accounting: + encoded.append("&U"); + break; + case font::underline_style::double_: + case font::underline_style::double_accounting: + encoded.append("&E"); + break; + case font::underline_style::none: + break; + } } - if (run.second.get().has_color()) { encoded.push_back('&'); diff --git a/xlnt/source/detail/header_footer/header_footer_code.hpp b/xlnt/source/detail/header_footer/header_footer_code.hpp index 169eb25..96ae506 100644 --- a/xlnt/source/detail/header_footer/header_footer_code.hpp +++ b/xlnt/source/detail/header_footer/header_footer_code.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/source/detail/implementations/cell_impl.cpp b/xlnt/source/detail/implementations/cell_impl.cpp index 900da4e..8be6324 100644 --- a/xlnt/source/detail/implementations/cell_impl.cpp +++ b/xlnt/source/detail/implementations/cell_impl.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -21,9 +21,10 @@ // // @license: http://www.opensource.org/licenses/mit-license.php // @author: see AUTHORS file + #include -#include "cell_impl.hpp" +#include namespace xlnt { namespace detail { diff --git a/xlnt/source/detail/implementations/cell_impl.hpp b/xlnt/source/detail/implementations/cell_impl.hpp index fcf4a33..6475677 100644 --- a/xlnt/source/detail/implementations/cell_impl.hpp +++ b/xlnt/source/detail/implementations/cell_impl.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -27,20 +27,26 @@ #include #include -#include #include +#include +#include #include +#include +#include namespace xlnt { namespace detail { -struct format_impl; struct worksheet_impl; struct cell_impl { cell_impl(); - + cell_impl(const cell_impl &other) = default; + cell_impl(cell_impl &&other) = default; + cell_impl &operator=(const cell_impl &other) = default; + cell_impl &operator=(cell_impl &&other) = default; + cell_type type_; worksheet_impl *parent_; @@ -54,10 +60,25 @@ struct cell_impl double value_numeric_; optional formula_; - optional hyperlink_; + optional hyperlink_; optional format_; optional comment_; }; +inline bool operator==(const cell_impl &lhs, const cell_impl &rhs) +{ + // not comparing parent + return lhs.type_ == rhs.type_ + && lhs.column_ == rhs.column_ + && lhs.row_ == rhs.row_ + && lhs.is_merged_ == rhs.is_merged_ + && lhs.value_text_ == rhs.value_text_ + && lhs.value_numeric_ == rhs.value_numeric_ + && lhs.formula_ == rhs.formula_ + && lhs.hyperlink_ == rhs.hyperlink_ + && (lhs.format_.is_set() == rhs.format_.is_set() && (!lhs.format_.is_set() || *lhs.format_.get() == *rhs.format_.get())) + && (lhs.comment_.is_set() == rhs.comment_.is_set() && (!lhs.comment_.is_set() || *lhs.comment_.get() == *rhs.comment_.get())); +} + } // namespace detail } // namespace xlnt diff --git a/xlnt/source/detail/implementations/conditional_format_impl.hpp b/xlnt/source/detail/implementations/conditional_format_impl.hpp index 7ea9f3a..2542d97 100644 --- a/xlnt/source/detail/implementations/conditional_format_impl.hpp +++ b/xlnt/source/detail/implementations/conditional_format_impl.hpp @@ -20,8 +20,21 @@ struct worksheet_impl; struct conditional_format_impl { stylesheet *parent; + worksheet_impl *target_sheet; + + bool operator==(const conditional_format_impl& rhs) const + { + // not comparing parent or target sheet + return target_range == rhs.target_range + && priority == rhs.priority + && differential_format_id == rhs.differential_format_id + && when == rhs.when + && border_id == rhs.border_id + && fill_id == rhs.fill_id + && font_id == rhs.font_id; + } + range_reference target_range; - worksheet_impl *target_sheet; std::size_t priority; std::size_t differential_format_id; diff --git a/xlnt/source/detail/implementations/format_impl.hpp b/xlnt/source/detail/implementations/format_impl.hpp index 8b8e377..d203d56 100644 --- a/xlnt/source/detail/implementations/format_impl.hpp +++ b/xlnt/source/detail/implementations/format_impl.hpp @@ -30,22 +30,22 @@ struct format_impl std::size_t id; optional alignment_id; - bool alignment_applied = false; + optional alignment_applied; optional border_id; - bool border_applied = false; + optional border_applied; optional fill_id; - bool fill_applied = false; + optional fill_applied; optional font_id; - bool font_applied = false; + optional font_applied; optional number_format_id; - bool number_format_applied = false; + optional number_format_applied; optional protection_id; - bool protection_applied = false; + optional protection_applied; bool pivot_button_ = false; bool quote_prefix_ = false; diff --git a/xlnt/source/detail/implementations/style_impl.hpp b/xlnt/source/detail/implementations/style_impl.hpp index 49153ae..c05ec88 100644 --- a/xlnt/source/detail/implementations/style_impl.hpp +++ b/xlnt/source/detail/implementations/style_impl.hpp @@ -22,6 +22,30 @@ struct style_impl { stylesheet *parent; + bool operator==(const style_impl& rhs) const + { + return name == rhs.name + && formatting_record_id == rhs.formatting_record_id + && custom_builtin == rhs.custom_builtin + && hidden_style == rhs.hidden_style + && builtin_id == rhs.builtin_id + && outline_style == rhs.outline_style + && alignment_id == rhs.alignment_id + && alignment_applied == rhs.alignment_applied + && border_id == rhs.border_id + && border_applied == rhs.border_applied + && fill_id == rhs.fill_id + && fill_applied == rhs.fill_applied + && font_id == rhs.font_id + && font_applied == rhs.font_applied + && number_format_id == rhs.number_format_id + && number_format_applied == number_format_applied + && protection_id == rhs.protection_id + && protection_applied == rhs.protection_applied + && pivot_button_ == rhs.pivot_button_ + && quote_prefix_ == rhs.quote_prefix_; + } + std::string name; std::size_t formatting_record_id; @@ -32,22 +56,22 @@ struct style_impl optional outline_style; optional alignment_id; - bool alignment_applied = false; + optional alignment_applied; optional border_id; - bool border_applied = false; + optional border_applied; optional fill_id; - bool fill_applied = false; + optional fill_applied; optional font_id; - bool font_applied = false; + optional font_applied; optional number_format_id; - bool number_format_applied = false; + optional number_format_applied; optional protection_id; - bool protection_applied = false; + optional protection_applied; bool pivot_button_ = false; bool quote_prefix_ = false; diff --git a/xlnt/source/detail/implementations/stylesheet.hpp b/xlnt/source/detail/implementations/stylesheet.hpp index 3f9e2d8..8ccc597 100644 --- a/xlnt/source/detail/implementations/stylesheet.hpp +++ b/xlnt/source/detail/implementations/stylesheet.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -401,23 +401,21 @@ struct stylesheet { auto iter = format_impls.begin(); bool added = false; + pattern.references = 0; auto id = find_or_add(format_impls, pattern, &added); std::advance(iter, static_cast::difference_type>(id)); auto &result = *iter; - if (added) - { - result.references = 0; - } - result.parent = this; result.id = id; result.references++; if (id != pattern.id) { - pattern.references -= pattern.references > 0 ? 1 : 0; + iter = format_impls.begin(); + std::advance(iter, static_cast::difference_type>(pattern.id)); + iter->references -= iter->references > 0 ? 1 : 0; garbage_collect(); } @@ -432,7 +430,7 @@ struct stylesheet return find_or_create(new_format); } - format_impl *find_or_create_with(format_impl *pattern, const alignment &new_alignment, bool applied) + format_impl *find_or_create_with(format_impl *pattern, const alignment &new_alignment, optional applied) { format_impl new_format = *pattern; new_format.alignment_id = find_or_add(alignments, new_alignment); @@ -441,7 +439,7 @@ struct stylesheet return find_or_create(new_format); } - format_impl *find_or_create_with(format_impl *pattern, const border &new_border, bool applied) + format_impl *find_or_create_with(format_impl *pattern, const border &new_border, optional applied) { format_impl new_format = *pattern; new_format.border_id = find_or_add(borders, new_border); @@ -450,7 +448,7 @@ struct stylesheet return find_or_create(new_format); } - format_impl *find_or_create_with(format_impl *pattern, const fill &new_fill, bool applied) + format_impl *find_or_create_with(format_impl *pattern, const fill &new_fill, optional applied) { format_impl new_format = *pattern; new_format.fill_id = find_or_add(fills, new_fill); @@ -459,7 +457,7 @@ struct stylesheet return find_or_create(new_format); } - format_impl *find_or_create_with(format_impl *pattern, const font &new_font, bool applied) + format_impl *find_or_create_with(format_impl *pattern, const font &new_font, optional applied) { format_impl new_format = *pattern; new_format.font_id = find_or_add(fonts, new_font); @@ -468,7 +466,7 @@ struct stylesheet return find_or_create(new_format); } - format_impl *find_or_create_with(format_impl *pattern, const number_format &new_number_format, bool applied) + format_impl *find_or_create_with(format_impl *pattern, const number_format &new_number_format, optional applied) { format_impl new_format = *pattern; if (new_number_format.id() >= 164) @@ -481,7 +479,7 @@ struct stylesheet return find_or_create(new_format); } - format_impl *find_or_create_with(format_impl *pattern, const protection &new_protection, bool applied) + format_impl *find_or_create_with(format_impl *pattern, const protection &new_protection, optional applied) { format_impl new_format = *pattern; new_format.protection_id = find_or_add(protections, new_protection); @@ -529,13 +527,34 @@ struct stylesheet } workbook *parent; + + bool operator==(const stylesheet& rhs) const + { + // no equality on parent as there is only 1 stylesheet per borkbook hence would always be false + return garbage_collection_enabled == rhs.garbage_collection_enabled + && known_fonts_enabled == rhs.known_fonts_enabled + && conditional_format_impls == rhs.conditional_format_impls + && format_impls == rhs.format_impls + && style_impls == rhs.style_impls + && style_names == rhs.style_names + && default_slicer_style == rhs.default_slicer_style + && alignments == rhs.alignments + && borders == rhs.borders + && fills == rhs.fills + && fonts == rhs.fonts + && number_formats == rhs.number_formats + && protections == rhs.protections + && colors == rhs.colors; + } bool garbage_collection_enabled = true; + bool known_fonts_enabled = false; std::list conditional_format_impls; std::list format_impls; std::unordered_map style_impls; std::vector style_names; + optional default_slicer_style; std::vector alignments; std::vector borders; diff --git a/xlnt/source/detail/implementations/workbook_impl.hpp b/xlnt/source/detail/implementations/workbook_impl.hpp index 64d15cf..13dd194 100644 --- a/xlnt/source/detail/implementations/workbook_impl.hpp +++ b/xlnt/source/detail/implementations/workbook_impl.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -29,6 +29,7 @@ #include #include +#include #include #include #include @@ -46,14 +47,15 @@ struct worksheet_impl; struct workbook_impl { - workbook_impl() : base_date_(calendar::windows_1900) - { - } + workbook_impl() : base_date_(calendar::windows_1900) + { + } workbook_impl(const workbook_impl &other) : active_sheet_index_(other.active_sheet_index_), worksheets_(other.worksheets_), - shared_strings_(other.shared_strings_), + shared_strings_ids_(other.shared_strings_ids_), + shared_strings_values_(other.shared_strings_values_), stylesheet_(other.stylesheet_), manifest_(other.manifest_), theme_(other.theme_), @@ -71,15 +73,15 @@ struct workbook_impl active_sheet_index_ = other.active_sheet_index_; worksheets_.clear(); std::copy(other.worksheets_.begin(), other.worksheets_.end(), back_inserter(worksheets_)); - shared_strings_.clear(); - std::copy(other.shared_strings_.begin(), other.shared_strings_.end(), std::back_inserter(shared_strings_)); - theme_ = other.theme_; + shared_strings_ids_ = other.shared_strings_ids_; + shared_strings_values_ = other.shared_strings_values_; + theme_ = other.theme_; manifest_ = other.manifest_; - sheet_title_rel_id_map_ = other.sheet_title_rel_id_map_; - view_ = other.view_; - code_name_ = other.code_name_; - file_version_ = other.file_version_; + sheet_title_rel_id_map_ = other.sheet_title_rel_id_map_; + view_ = other.view_; + code_name_ = other.code_name_; + file_version_ = other.file_version_; core_properties_ = other.core_properties_; extended_properties_ = other.extended_properties_; @@ -88,16 +90,41 @@ struct workbook_impl return *this; } + bool operator==(const workbook_impl &other) + { + return active_sheet_index_ == other.active_sheet_index_ + && worksheets_ == other.worksheets_ + && shared_strings_ids_ == other.shared_strings_ids_ + && stylesheet_ == other.stylesheet_ + && base_date_ == other.base_date_ + && title_ == other.title_ + && manifest_ == other.manifest_ + && theme_ == other.theme_ + && images_ == other.images_ + && core_properties_ == other.core_properties_ + && extended_properties_ == other.extended_properties_ + && custom_properties_ == other.custom_properties_ + && sheet_title_rel_id_map_ == other.sheet_title_rel_id_map_ + && view_ == other.view_ + && code_name_ == other.code_name_ + && file_version_ == other.file_version_ + && calculation_properties_ == other.calculation_properties_ + && abs_path_ == other.abs_path_ + && arch_id_flags_ == other.arch_id_flags_ + && extensions_ == other.extensions_; + } + optional active_sheet_index_; std::list worksheets_; - std::vector shared_strings_; + std::unordered_map shared_strings_ids_; + std::map shared_strings_values_; optional stylesheet_; calendar base_date_; optional title_; - + manifest manifest_; optional theme_; std::unordered_map> images_; @@ -106,7 +133,7 @@ struct workbook_impl std::vector> extended_properties_; std::vector> custom_properties_; - std::unordered_map sheet_title_rel_id_map_; + std::unordered_map sheet_title_rel_id_map_; optional view_; optional code_name_; @@ -117,10 +144,21 @@ struct workbook_impl std::size_t last_edited; std::size_t lowest_edited; std::size_t rup_build; + + bool operator==(const file_version_t& rhs) const + { + return app_name == rhs.app_name + && last_edited == rhs.last_edited + && lowest_edited == rhs.lowest_edited + && rup_build == rhs.rup_build; + } }; - + optional file_version_; optional calculation_properties_; + optional abs_path_; + optional arch_id_flags_; + optional extensions_; }; } // namespace detail diff --git a/xlnt/source/detail/implementations/worksheet_impl.hpp b/xlnt/source/detail/implementations/worksheet_impl.hpp index 9c6ec27..cb3be2f 100644 --- a/xlnt/source/detail/implementations/worksheet_impl.hpp +++ b/xlnt/source/detail/implementations/worksheet_impl.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -27,14 +27,19 @@ #include #include -#include +#include #include -#include -#include -#include #include #include +#include +#include +#include #include +#include +#include +#include +#include +#include namespace xlnt { @@ -62,23 +67,16 @@ struct worksheet_impl id_ = other.id_; title_ = other.title_; + format_properties_ = other.format_properties_; column_properties_ = other.column_properties_; row_properties_ = other.row_properties_; cell_map_ = other.cell_map_; - - for (auto &row : cell_map_) - { - for (auto &cell : row.second) - { - cell.second.parent_ = this; - } - } - page_setup_ = other.page_setup_; auto_filter_ = other.auto_filter_; page_margins_ = other.page_margins_; merged_cells_ = other.merged_cells_; named_ranges_ = other.named_ranges_; + phonetic_properties_ = other.phonetic_properties_; header_footer_ = other.header_footer_; print_title_cols_ = other.print_title_cols_; print_title_rows_ = other.print_title_rows_; @@ -86,13 +84,53 @@ struct worksheet_impl views_ = other.views_; column_breaks_ = other.column_breaks_; row_breaks_ = other.row_breaks_; + extension_list_ = other.extension_list_; + sheet_properties_ = other.sheet_properties_; + print_options_ = other.print_options_; + + for (auto &row : cell_map_) + { + for (auto &cell : row.second) + { + cell.second.parent_ = this; + } + } } workbook *parent_; + bool operator==(const worksheet_impl& rhs) const + { + return id_ == rhs.id_ + && title_ == rhs.title_ + && format_properties_ == rhs.format_properties_ + && column_properties_ == rhs.column_properties_ + && row_properties_ == rhs.row_properties_ + && cell_map_ == rhs.cell_map_ + && page_setup_ == rhs.page_setup_ + && auto_filter_ == rhs.auto_filter_ + && page_margins_ == rhs.page_margins_ + && merged_cells_ == rhs.merged_cells_ + && named_ranges_ == rhs.named_ranges_ + && phonetic_properties_ == rhs.phonetic_properties_ + && header_footer_ == rhs.header_footer_ + && print_title_cols_ == rhs.print_title_cols_ + && print_title_rows_ == rhs.print_title_rows_ + && print_area_ == rhs.print_area_ + && views_ == rhs.views_ + && column_breaks_ == rhs.column_breaks_ + && row_breaks_ == rhs.row_breaks_ + && comments_ == rhs.comments_ + && print_options_ == rhs.print_options_ + && sheet_properties_ == rhs.sheet_properties_ + && extension_list_ == rhs.extension_list_; + } + std::size_t id_; std::string title_; + sheet_format_properties format_properties_; + std::unordered_map column_properties_; std::unordered_map row_properties_; @@ -104,6 +142,7 @@ struct worksheet_impl std::vector merged_cells_; std::unordered_map named_ranges_; + optional phonetic_properties_; optional header_footer_; std::string print_title_cols_; @@ -117,6 +156,10 @@ struct worksheet_impl std::vector row_breaks_; std::unordered_map comments_; + optional print_options_; + optional sheet_properties_; + + optional extension_list_; }; } // namespace detail diff --git a/xlnt/source/detail/number_format/number_formatter.cpp b/xlnt/source/detail/number_format/number_formatter.cpp index 8ef8453..5b0d5d4 100644 --- a/xlnt/source/detail/number_format/number_formatter.cpp +++ b/xlnt/source/detail/number_format/number_formatter.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/source/detail/number_format/number_formatter.hpp b/xlnt/source/detail/number_format/number_formatter.hpp index 02ede6b..4c3e149 100644 --- a/xlnt/source/detail/number_format/number_formatter.hpp +++ b/xlnt/source/detail/number_format/number_formatter.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/source/detail/serialization/custom_value_traits.cpp b/xlnt/source/detail/serialization/custom_value_traits.cpp index e31be3f..2eaf849 100644 --- a/xlnt/source/detail/serialization/custom_value_traits.cpp +++ b/xlnt/source/detail/serialization/custom_value_traits.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2017 Thomas Fussell +// Copyright (c) 2016-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -343,5 +343,19 @@ std::string to_string(pane_state state) default_case("frozen"); } +std::string to_string(orientation orientation) +{ + switch (orientation) + { + case orientation::default_orientation: + return "default"; + case orientation::landscape: + return "landscape"; + case orientation::portrait: + return "portrait"; + } + default_case("default"); +} + } // namespace detail } // namespace xlnt diff --git a/xlnt/source/detail/serialization/custom_value_traits.hpp b/xlnt/source/detail/serialization/custom_value_traits.hpp index a5ac300..aa283a0 100644 --- a/xlnt/source/detail/serialization/custom_value_traits.hpp +++ b/xlnt/source/detail/serialization/custom_value_traits.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2017 Thomas Fussell +// Copyright (c) 2016-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -23,7 +23,9 @@ #pragma once +#include #include +#include #include #include @@ -35,6 +37,7 @@ #include #include #include +#include #include namespace xlnt { @@ -74,6 +77,8 @@ std::string to_string(target_mode mode); std::string to_string(pane_state state); +std::string to_string(orientation state); + template static T from_string(const std::string &string_value); @@ -168,27 +173,57 @@ relationship_type from_string(const std::string &string) template<> pattern_fill_type from_string(const std::string &string) { - if (string == "darkdown") return pattern_fill_type::darkdown; - else if (string == "darkgray") return pattern_fill_type::darkgray; - else if (string == "darkgrid") return pattern_fill_type::darkgrid; - else if (string == "darkhorizontal") return pattern_fill_type::darkhorizontal; - else if (string == "darktrellis") return pattern_fill_type::darktrellis; - else if (string == "darkup") return pattern_fill_type::darkup; - else if (string == "darkvertical") return pattern_fill_type::darkvertical; - else if (string == "gray0625") return pattern_fill_type::gray0625; - else if (string == "gray125") return pattern_fill_type::gray125; - else if (string == "lightdown") return pattern_fill_type::lightdown; - else if (string == "lightgray") return pattern_fill_type::lightgray; - else if (string == "lightgrid") return pattern_fill_type::lightgrid; - else if (string == "lighthorizontal") return pattern_fill_type::lighthorizontal; - else if (string == "lighttrellis") return pattern_fill_type::lighttrellis; - else if (string == "lightup") return pattern_fill_type::lightup; - else if (string == "lightvertical") return pattern_fill_type::lightvertical; - else if (string == "mediumgray") return pattern_fill_type::mediumgray; - else if (string == "none") return pattern_fill_type::none; - else if (string == "solid") return pattern_fill_type::solid; - - default_case(pattern_fill_type::none); +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wexit-time-destructors" + static std::unordered_map patternFill { + {"darkdown", pattern_fill_type::darkdown }, + { "darkgray", pattern_fill_type::darkgray }, + { "darkgrid", pattern_fill_type::darkgrid }, + { "darkhorizontal", pattern_fill_type::darkhorizontal }, + { "darktrellis", pattern_fill_type::darktrellis }, + { "darkup", pattern_fill_type::darkup }, + { "darkvertical", pattern_fill_type::darkvertical }, + { "gray0625", pattern_fill_type::gray0625 }, + { "gray125", pattern_fill_type::gray125 }, + { "lightdown", pattern_fill_type::lightdown }, + { "lightgray", pattern_fill_type::lightgray }, + { "lightgrid", pattern_fill_type::lightgrid }, + { "lighthorizontal", pattern_fill_type::lighthorizontal }, + { "lighttrellis", pattern_fill_type::lighttrellis }, + { "lightup", pattern_fill_type::lightup }, + { "lightvertical", pattern_fill_type::lightvertical }, + { "mediumgray", pattern_fill_type::mediumgray }, + { "none", pattern_fill_type::none }, + { "solid", pattern_fill_type::solid } + }; +#pragma clang diagnostic pop + + auto toLower = [](std::string str) { + auto bg{ std::begin (str) }; + auto en{ std::end (str) }; + std::transform(bg, en, bg, + [](char c) { + // static cast to avoid int -> char narrowing warning + return static_cast(tolower(c)); + }); + + return str; + }; + + auto patternLookup = [](const std::string& key) { + auto entry { patternFill.find (key) }; + if (entry != std::end (patternFill)) { + return entry->second; + } + else { + // Note: there won't be an error if there is an unsupported pattern + return pattern_fill_type::none; + } + }; + + std::string lowerString {toLower (string) }; + + return patternLookup (lowerString); } template<> @@ -362,6 +397,15 @@ pane_corner from_string(const std::string &string) default_case(pane_corner::bottom_left); } +template <> +orientation from_string(const std::string &string) +{ + if (string == "default") return orientation::default_orientation; + else if (string == "landscape") return orientation::landscape; + else if (string == "portrait") return orientation::portrait; + default_case(orientation::default); +} + } // namespace detail } // namespace xlnt @@ -550,6 +594,20 @@ struct value_traits } }; +template <> +struct value_traits +{ + static xlnt::orientation parse(std::string string, const parser &) + { + return xlnt::detail::from_string(string); + } + + static std::string serialize(xlnt::orientation orientation, const serializer &) + { + return xlnt::detail::to_string(orientation); + } +}; + } // namespace xml diff --git a/xlnt/source/detail/serialization/excel_thumbnail.hpp b/xlnt/source/detail/serialization/excel_thumbnail.hpp index c3699fe..2640e68 100644 --- a/xlnt/source/detail/serialization/excel_thumbnail.hpp +++ b/xlnt/source/detail/serialization/excel_thumbnail.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2017 Thomas Fussell +// Copyright (c) 2016-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/source/detail/serialization/miniz.cpp b/xlnt/source/detail/serialization/miniz.cpp index 290ff88..f6bf233 100644 --- a/xlnt/source/detail/serialization/miniz.cpp +++ b/xlnt/source/detail/serialization/miniz.cpp @@ -2095,7 +2095,7 @@ void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int for (y = 0; y < h; ++y) { tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); - tdefl_compress_buffer(pComp, (mz_uint8 *)pImage + (flip ? (h - 1 - y) : y) * bpl, bpl, TDEFL_NO_FLUSH); + tdefl_compress_buffer(pComp, (const mz_uint8 *)pImage + (flip ? (h - 1 - y) : y) * bpl, bpl, TDEFL_NO_FLUSH); } if (tdefl_compress_buffer(pComp, nullptr, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE) { diff --git a/xlnt/source/detail/serialization/open_stream.cpp b/xlnt/source/detail/serialization/open_stream.cpp index 290adcb..fcb6c33 100644 --- a/xlnt/source/detail/serialization/open_stream.cpp +++ b/xlnt/source/detail/serialization/open_stream.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017 Thomas Fussell +// Copyright (c) 2017-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/source/detail/serialization/open_stream.hpp b/xlnt/source/detail/serialization/open_stream.hpp index b69aa20..00f9a71 100644 --- a/xlnt/source/detail/serialization/open_stream.hpp +++ b/xlnt/source/detail/serialization/open_stream.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017 Thomas Fussell +// Copyright (c) 2017-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/source/detail/serialization/vector_streambuf.cpp b/xlnt/source/detail/serialization/vector_streambuf.cpp index 24abae7..95b48e7 100644 --- a/xlnt/source/detail/serialization/vector_streambuf.cpp +++ b/xlnt/source/detail/serialization/vector_streambuf.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2017 Thomas Fussell +// Copyright (c) 2016-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/source/detail/serialization/vector_streambuf.hpp b/xlnt/source/detail/serialization/vector_streambuf.hpp index 482e2fc..2e50761 100644 --- a/xlnt/source/detail/serialization/vector_streambuf.hpp +++ b/xlnt/source/detail/serialization/vector_streambuf.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2017 Thomas Fussell +// Copyright (c) 2016-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/source/detail/serialization/xlsx_consumer.cpp b/xlnt/source/detail/serialization/xlsx_consumer.cpp index c7b8bc8..2f29e19 100644 --- a/xlnt/source/detail/serialization/xlsx_consumer.cpp +++ b/xlnt/source/detail/serialization/xlsx_consumer.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -26,21 +26,22 @@ #include #include -#include -#include -#include -#include -#include -#include -#include #include #include +#include #include #include #include #include #include #include +#include +#include +#include +#include +#include +#include +#include namespace std { @@ -124,6 +125,23 @@ struct number_converter double result; }; +using style_id_pair = std::pair; + +/// +/// Try to find given xfid value in the styles vector and, if succeeded, set's the optional style. +/// +void set_style_by_xfid(const std::vector &styles, + std::size_t xfid, xlnt::optional &style) +{ + for (auto &item : styles) + { + if (item.second == xfid) + { + style = item.first.name; + } + } +} + } // namespace /* @@ -181,25 +199,31 @@ cell xlsx_consumer::read_cell() { expect_start_element(qn("spreadsheetml", "row"), xml::content::complex); // CT_Row auto row_index = static_cast(std::stoul(parser().attribute("r"))); + auto &row_properties = ws.row_properties(row_index); if (parser().attribute_present("ht")) { - ws.row_properties(row_index).height = parser().attribute("ht"); + row_properties.height = parser().attribute("ht"); } if (parser().attribute_present("customHeight")) { - ws.row_properties(row_index).custom_height = is_true(parser().attribute("customHeight")); + row_properties.custom_height = is_true(parser().attribute("customHeight")); } if (parser().attribute_present("hidden") && is_true(parser().attribute("hidden"))) { - ws.row_properties(row_index).hidden = true; + row_properties.hidden = true; } - skip_attributes({ qn("x14ac", "dyDescent") }); - skip_attributes({ "customFormat", "s", "customFont", + + if (parser().attribute_present(qn("x14ac", "dyDescent"))) + { + row_properties.dy_descent = parser().attribute(qn("x14ac", "dyDescent")); + } + + skip_attributes({"customFormat", "s", "customFont", "outlineLevel", "collapsed", "thickTop", "thickBot", - "ph", "spans" }); + "ph", "spans"}); } if (!in_element(qn("spreadsheetml", "row"))) @@ -209,7 +233,8 @@ cell xlsx_consumer::read_cell() expect_start_element(qn("spreadsheetml", "c"), xml::content::complex); - auto cell = streaming_ ? xlnt::cell(streaming_cell_.get()) + auto cell = streaming_ + ? xlnt::cell(streaming_cell_.get()) : ws.cell(cell_reference(parser().attribute("r"))); auto reference = cell_reference(parser().attribute("r")); cell.d_->parent_ = current_worksheet_; @@ -221,7 +246,7 @@ cell xlsx_consumer::read_cell() if (parser().attribute_present("s")) { - cell.format(target_.format(std::stoull(parser().attribute("s")))); + cell.format(target_.format(static_cast(std::stoull(parser().attribute("s"))))); } auto has_value = false; @@ -249,8 +274,8 @@ cell xlsx_consumer::read_cell() has_shared_formula = parser().attribute("t") == "shared"; } - skip_attributes( - { "aca", "ref", "dt2D", "dtr", "del1", "del2", "r1", "r2", "ca", "si", "bx" }); + skip_attributes({"aca", "ref", "dt2D", "dtr", "del1", + "del2", "r1", "r2", "ca", "si", "bx"}); formula_value_string = read_text(); } @@ -342,13 +367,13 @@ std::string xlsx_consumer::read_worksheet_begin(const std::string &rel_id) auto title = std::find_if(target_.d_->sheet_title_rel_id_map_.begin(), target_.d_->sheet_title_rel_id_map_.end(), [&](const std::pair &p) { - return p.second == rel_id; - })->first; + return p.second == rel_id; + })->first; auto ws = worksheet(current_worksheet_); expect_start_element(qn("spreadsheetml", "worksheet"), xml::content::complex); // CT_Worksheet - skip_attributes({ qn("mc", "Ignorable") }); + skip_attributes({qn("mc", "Ignorable")}); while (in_element(qn("spreadsheetml", "worksheet"))) { @@ -356,6 +381,44 @@ std::string xlsx_consumer::read_worksheet_begin(const std::string &rel_id) if (current_worksheet_element == qn("spreadsheetml", "sheetPr")) // CT_SheetPr 0-1 { + sheet_pr props; + if (parser().attribute_present("syncHorizontal")) + { // optional, boolean, false + props.sync_horizontal.set(parser().attribute("syncHorizontal")); + } + if (parser().attribute_present("syncVertical")) + { // optional, boolean, false + props.sync_vertical.set(parser().attribute("syncVertical")); + } + if (parser().attribute_present("syncRef")) + { // optional, ST_Ref, false + props.sync_ref.set(cell_reference(parser().attribute("syncRef"))); + } + if (parser().attribute_present("transitionEvaluation")) + { // optional, boolean, false + props.transition_evaluation.set(parser().attribute("transitionEvaluation")); + } + if (parser().attribute_present("transitionEntry")) + { // optional, boolean, false + props.transition_entry.set(parser().attribute("transitionEntry")); + } + if (parser().attribute_present("published")) + { // optional, boolean, true + props.published.set(parser().attribute("published")); + } + if (parser().attribute_present("codeName")) + { // optional, string + props.code_name.set(parser().attribute("codeName")); + } + if (parser().attribute_present("filterMode")) + { // optional, boolean, false + props.filter_mode.set(parser().attribute("filterMode")); + } + if (parser().attribute_present("enableFormatConditionsCalculation")) + { // optional, boolean, true + props.enable_format_condition_calculation.set(parser().attribute("enableFormatConditionsCalculation")); + } + ws.d_->sheet_properties_.set(props); while (in_element(current_worksheet_element)) { auto sheet_pr_child_element = expect_start_element(xml::content::simple); @@ -383,16 +446,6 @@ std::string xlsx_consumer::read_worksheet_begin(const std::string &rel_id) expect_end_element(sheet_pr_child_element); } - - skip_attribute("syncHorizontal"); // optional, boolean, false - skip_attribute("syncVertical"); // optional, boolean, false - skip_attribute("syncRef"); // optional, ST_Ref, false - skip_attribute("transitionEvaluation"); // optional, boolean, false - skip_attribute("transitionEntry"); // optional, boolean, false - skip_attribute("published"); // optional, boolean, true - skip_attribute("codeName"); // optional, string - skip_attribute("filterMode"); // optional, boolean, false - skip_attribute("enableFormatConditionsCalculation"); // optional, boolean, true } else if (current_worksheet_element == qn("spreadsheetml", "dimension")) // CT_SheetDimension 0-1 { @@ -411,22 +464,33 @@ std::string xlsx_consumer::read_worksheet_begin(const std::string &rel_id) { new_view.show_grid_lines(is_true(parser().attribute("showGridLines"))); } + if (parser().attribute_present("topLeftCell")) + { + new_view.top_left_cell(cell_reference(parser().attribute("topLeftCell"))); + } if (parser().attribute_present("defaultGridColor")) // default="true" { new_view.default_grid_color(is_true(parser().attribute("defaultGridColor"))); } - if (parser().attribute_present("view") && parser().attribute("view") != "normal") + if (parser().attribute_present("view") + && parser().attribute("view") != "normal") { new_view.type(parser().attribute("view") == "pageBreakPreview" - ? sheet_view_type::page_break_preview - : sheet_view_type::page_layout); + ? sheet_view_type::page_break_preview + : sheet_view_type::page_layout); } - skip_attributes({ "windowProtection", "showFormulas", "showRowColHeaders", "showZeros", "rightToLeft", - "tabSelected", "showRuler", "showOutlineSymbols", "showWhiteSpace", "view", "topLeftCell", - "colorId", "zoomScale", "zoomScaleNormal", "zoomScaleSheetLayoutView", "zoomScalePageLayoutView" }); + if (parser().attribute_present("tabSelected") + && is_true(parser().attribute("tabSelected"))) + { + target_.d_->view_.get().active_tab = ws.id() - 1; + } + + skip_attributes({"windowProtection", "showFormulas", "showRowColHeaders", "showZeros", "rightToLeft", "showRuler", "showOutlineSymbols", "showWhiteSpace", + "view", "topLeftCell", "colorId", "zoomScale", "zoomScaleNormal", "zoomScaleSheetLayoutView", + "zoomScalePageLayoutView"}); while (in_element(qn("spreadsheetml", "sheetView"))) { @@ -471,7 +535,13 @@ std::string xlsx_consumer::read_worksheet_begin(const std::string &rel_id) { current_selection.active_cell(parser().attribute("activeCell")); } - + + if (parser().attribute_present("sqref")) + { + const auto sqref = range_reference(parser().attribute("sqref")); + current_selection.sqref(sqref); + } + current_selection.pane(pane_corner::top_left); new_view.add_selection(current_selection); @@ -501,7 +571,29 @@ std::string xlsx_consumer::read_worksheet_begin(const std::string &rel_id) } else if (current_worksheet_element == qn("spreadsheetml", "sheetFormatPr")) // CT_SheetFormatPr 0-1 { - skip_remaining_content(current_worksheet_element); + if (parser().attribute_present("baseColWidth")) + { + ws.d_->format_properties_.base_col_width = + parser().attribute("baseColWidth"); + } + if (parser().attribute_present("defaultColWidth")) + { + ws.d_->format_properties_.default_column_width = + parser().attribute("defaultColWidth"); + } + if (parser().attribute_present("defaultRowHeight")) + { + ws.d_->format_properties_.default_row_height = + parser().attribute("defaultRowHeight"); + } + + if (parser().attribute_present(qn("x14ac", "dyDescent"))) + { + ws.d_->format_properties_.dy_descent = + parser().attribute(qn("x14ac", "dyDescent")); + } + + skip_attributes(); } else if (current_worksheet_element == qn("spreadsheetml", "cols")) // CT_Cols 0+ { @@ -509,29 +601,37 @@ std::string xlsx_consumer::read_worksheet_begin(const std::string &rel_id) { expect_start_element(qn("spreadsheetml", "col"), xml::content::simple); - skip_attributes({ "bestFit", "collapsed", "outlineLevel" }); + skip_attributes(std::vector{"collapsed", "outlineLevel"}); auto min = static_cast(std::stoull(parser().attribute("min"))); auto max = static_cast(std::stoull(parser().attribute("max"))); - optional width; - - if (parser().attribute_present("width")) - { - width = (parser().attribute("width") * 7 - 5) / 7; - } - - optional column_style; - - if (parser().attribute_present("style")) - { - column_style = parser().attribute("style"); - } + // avoid uninitialised warnings in GCC by using a lambda to make the conditional initialisation + optional width = [](xml::parser &p) -> xlnt::optional { + if (p.attribute_present("width")) + { + return (p.attribute("width") * 7 - 5) / 7; + } + return xlnt::optional(); + }(parser()); + // avoid uninitialised warnings in GCC by using a lambda to make the conditional initialisation + optional column_style = [](xml::parser &p) -> xlnt::optional { + if (p.attribute_present("style")) + { + return p.attribute("style"); + } + return xlnt::optional(); + }(parser()); auto custom = parser().attribute_present("customWidth") - ? is_true(parser().attribute("customWidth")) : false; + ? is_true(parser().attribute("customWidth")) + : false; auto hidden = parser().attribute_present("hidden") - ? is_true(parser().attribute("hidden")) : false; + ? is_true(parser().attribute("hidden")) + : false; + auto best_fit = parser().attribute_present("bestFit") + ? is_true(parser().attribute("bestFit")) + : false; expect_end_element(qn("spreadsheetml", "col")); @@ -551,6 +651,7 @@ std::string xlsx_consumer::read_worksheet_begin(const std::string &rel_id) props.hidden = hidden; props.custom_width = custom; + props.best_fit = best_fit; ws.add_column_properties(column, props); } } @@ -581,26 +682,40 @@ void xlsx_consumer::read_worksheet_sheetdata() { expect_start_element(qn("spreadsheetml", "row"), xml::content::complex); // CT_Row auto row_index = parser().attribute("r"); + auto &row_properties = ws.row_properties(row_index); if (parser().attribute_present("ht")) { - ws.row_properties(row_index).height = parser().attribute("ht"); + row_properties.height = parser().attribute("ht"); } if (parser().attribute_present("customHeight")) { - ws.row_properties(row_index).custom_height = is_true(parser().attribute("customHeight")); + row_properties.custom_height = is_true(parser().attribute("customHeight")); } if (parser().attribute_present("hidden") && is_true(parser().attribute("hidden"))) { - ws.row_properties(row_index).hidden = true; + row_properties.hidden = true; } - skip_attributes({ qn("x14ac", "dyDescent") }); - skip_attributes({ "customFormat", "s", "customFont", + if (parser().attribute_present(qn("x14ac", "dyDescent"))) + { + row_properties.dy_descent = parser().attribute(qn("x14ac", "dyDescent")); + } + + if (parser().attribute_present("s")) + { + row_properties.style.set(static_cast(std::stoull(parser().attribute("s")))); + } + if (parser().attribute_present("customFormat")) + { + row_properties.custom_format.set(parser().attribute("customFormat")); + } + + skip_attributes({"customFont", "outlineLevel", "collapsed", "thickTop", "thickBot", - "ph", "spans" }); + "ph", "spans"}); while (in_element(qn("spreadsheetml", "row"))) { @@ -612,7 +727,7 @@ void xlsx_consumer::read_worksheet_sheetdata() if (parser().attribute_present("s")) { - cell.format(target_.format(std::stoull(parser().attribute("s")))); + cell.format(target_.format(static_cast(std::stoull(parser().attribute("s"))))); } auto has_value = false; @@ -641,7 +756,7 @@ void xlsx_consumer::read_worksheet_sheetdata() } skip_attributes( - { "aca", "ref", "dt2D", "dtr", "del1", "del2", "r1", "r2", "ca", "si", "bx" }); + {"aca", "ref", "dt2D", "dtr", "del1", "del2", "r1", "r2", "ca", "si", "bx"}); formula_value_string = read_text(); } @@ -696,7 +811,6 @@ void xlsx_consumer::read_worksheet_sheetdata() cell.error(value_string); } } - } expect_end_element(qn("spreadsheetml", "row")); @@ -774,7 +888,16 @@ worksheet xlsx_consumer::read_worksheet_end(const std::string &rel_id) } else if (current_worksheet_element == qn("spreadsheetml", "phoneticPr")) // CT_PhoneticPr 0-1 { - skip_remaining_content(current_worksheet_element); + phonetic_pr phonetic_properties(parser().attribute("fontId")); + if (parser().attribute_present("type")) + { + phonetic_properties.type(phonetic_pr::type_from_string(parser().attribute("type"))); + } + if (parser().attribute_present("alignment")) + { + phonetic_properties.alignment(phonetic_pr::alignment_from_string(parser().attribute("alignment"))); + } + current_worksheet_->phonetic_properties_.set(phonetic_properties); } else if (current_worksheet_element == qn("spreadsheetml", "conditionalFormatting")) // CT_ConditionalFormatting 0+ { @@ -786,8 +909,9 @@ worksheet xlsx_consumer::read_worksheet_end(const std::string &rel_id) } else if (current_worksheet_element == qn("spreadsheetml", "hyperlinks")) // CT_Hyperlinks 0-1 { - while (in_element(qn("spreadsheetml", "hyperlinks"))) + while (in_element(current_worksheet_element)) { + // CT_Hyperlink expect_start_element(qn("spreadsheetml", "hyperlink"), xml::content::simple); auto cell = ws.cell(parser().attribute("ref")); @@ -800,16 +924,66 @@ worksheet xlsx_consumer::read_worksheet_end(const std::string &rel_id) if (hyperlink_rel != hyperlinks.end()) { - cell.hyperlink(hyperlink_rel->target().path().string()); + auto url = hyperlink_rel->target().path().string(); + + if (cell.has_value()) + { + cell.hyperlink(url, cell.value()); + } + else + { + cell.hyperlink(url); + } } } + else if (parser().attribute_present("location")) + { + auto hyperlink = hyperlink_impl(); + + auto location = parser().attribute("location"); + hyperlink.relationship = relationship("", relationship_type::hyperlink, + uri(""), uri(location), target_mode::internal); + + if (parser().attribute_present("display")) + { + hyperlink.display = parser().attribute("display"); + } + + if (parser().attribute_present("tooltip")) + { + hyperlink.tooltip = parser().attribute("tooltip"); + } + + cell.d_->hyperlink_ = hyperlink; + } - skip_attributes({ "location", "tooltip", "display" }); expect_end_element(qn("spreadsheetml", "hyperlink")); } } else if (current_worksheet_element == qn("spreadsheetml", "printOptions")) // CT_PrintOptions 0-1 { + print_options opts; + if (parser().attribute_present("gridLines")) + { + opts.print_grid_lines.set(parser().attribute("gridLines")); + } + if (parser().attribute_present("gridLinesSet")) + { + opts.print_grid_lines.set(parser().attribute("gridLinesSet")); + } + if (parser().attribute_present("headings")) + { + opts.print_grid_lines.set(parser().attribute("headings")); + } + if (parser().attribute_present("horizontalCentered")) + { + opts.print_grid_lines.set(parser().attribute("horizontalCentered")); + } + if (parser().attribute_present("verticalCentered")) + { + opts.print_grid_lines.set(parser().attribute("verticalCentered")); + } + ws.d_->print_options_.set(opts); skip_remaining_content(current_worksheet_element); } else if (current_worksheet_element == qn("spreadsheetml", "pageMargins")) // CT_PageMargins 0-1 @@ -827,20 +1001,34 @@ worksheet xlsx_consumer::read_worksheet_end(const std::string &rel_id) } else if (current_worksheet_element == qn("spreadsheetml", "pageSetup")) // CT_PageSetup 0-1 { + page_setup setup; + if (parser().attribute_present("orientation")) + { + setup.orientation_.set(parser().attribute("orientation")); + } + if (parser().attribute_present("horizontalDpi")) + { + setup.horizontal_dpi_.set(parser().attribute("horizontalDpi")); + } + if (parser().attribute_present("verticalDpi")) + { + setup.vertical_dpi_.set(parser().attribute("verticalDpi")); + } + ws.page_setup(setup); skip_remaining_content(current_worksheet_element); } else if (current_worksheet_element == qn("spreadsheetml", "headerFooter")) // CT_HeaderFooter 0-1 { header_footer hf; - hf.align_with_margins( - !parser().attribute_present("alignWithMargins") || is_true(parser().attribute("alignWithMargins"))); - hf.scale_with_doc( - !parser().attribute_present("alignWithMargins") || is_true(parser().attribute("alignWithMargins"))); - auto different_odd_even = - parser().attribute_present("differentOddEven") && is_true(parser().attribute("differentOddEven")); - auto different_first = - parser().attribute_present("differentFirst") && is_true(parser().attribute("differentFirst")); + hf.align_with_margins(!parser().attribute_present("alignWithMargins") + || is_true(parser().attribute("alignWithMargins"))); + hf.scale_with_doc(!parser().attribute_present("alignWithMargins") + || is_true(parser().attribute("alignWithMargins"))); + auto different_odd_even = parser().attribute_present("differentOddEven") + && is_true(parser().attribute("differentOddEven")); + auto different_first = parser().attribute_present("differentFirst") + && is_true(parser().attribute("differentFirst")); optional, 3>> odd_header; optional, 3>> odd_footer; @@ -890,17 +1078,21 @@ worksheet xlsx_consumer::read_worksheet_end(const std::string &rel_id) for (std::size_t i = 0; i < 3; ++i) { auto loc = i == 0 ? header_footer::location::left - : i == 1 ? header_footer::location::center : header_footer::location::right; + : i == 1 ? header_footer::location::center : header_footer::location::right; if (different_odd_even) { - if (odd_header.is_set() && odd_header.get().at(i).is_set() && even_header.is_set() + if (odd_header.is_set() + && odd_header.get().at(i).is_set() + && even_header.is_set() && even_header.get().at(i).is_set()) { hf.odd_even_header(loc, odd_header.get().at(i).get(), even_header.get().at(i).get()); } - if (odd_footer.is_set() && odd_footer.get().at(i).is_set() && even_footer.is_set() + if (odd_footer.is_set() + && odd_footer.get().at(i).is_set() + && even_footer.is_set() && even_footer.get().at(i).is_set()) { hf.odd_even_footer(loc, odd_footer.get().at(i).get(), even_footer.get().at(i).get()); @@ -930,7 +1122,8 @@ worksheet xlsx_consumer::read_worksheet_end(const std::string &rel_id) { auto count = parser().attribute_present("count") ? parser().attribute("count") : 0; auto manual_break_count = parser().attribute_present("manualBreakCount") - ? parser().attribute("manualBreakCount") : 0; + ? parser().attribute("manualBreakCount") + : 0; while (in_element(qn("spreadsheetml", "rowBreaks"))) { @@ -947,7 +1140,7 @@ worksheet xlsx_consumer::read_worksheet_end(const std::string &rel_id) --manual_break_count; } - skip_attributes({ "min", "max", "pt" }); + skip_attributes({"min", "max", "pt"}); expect_end_element(qn("spreadsheetml", "brk")); } } @@ -973,7 +1166,7 @@ worksheet xlsx_consumer::read_worksheet_end(const std::string &rel_id) --manual_break_count; } - skip_attributes({ "min", "max", "pt" }); + skip_attributes({"min", "max", "pt"}); expect_end_element(qn("spreadsheetml", "brk")); } } @@ -1003,7 +1196,8 @@ worksheet xlsx_consumer::read_worksheet_end(const std::string &rel_id) } else if (current_worksheet_element == qn("spreadsheetml", "extLst")) { - skip_remaining_content(current_worksheet_element); + ext_list extensions(parser(), current_worksheet_element.namespace_()); + ws.d_->extension_list_.set(extensions); } else { @@ -1017,8 +1211,8 @@ worksheet xlsx_consumer::read_worksheet_end(const std::string &rel_id) if (manifest.has_relationship(sheet_path, xlnt::relationship_type::comments)) { - auto comments_part = manifest.canonicalize({ workbook_rel, sheet_rel, - manifest.relationship(sheet_path, xlnt::relationship_type::comments) }); + auto comments_part = manifest.canonicalize({workbook_rel, sheet_rel, + manifest.relationship(sheet_path, xlnt::relationship_type::comments)}); auto receive = xml::parser::receive_default; auto comments_part_streambuf = archive_->open(comments_part); @@ -1030,8 +1224,8 @@ worksheet xlsx_consumer::read_worksheet_end(const std::string &rel_id) if (manifest.has_relationship(sheet_path, xlnt::relationship_type::vml_drawing)) { - auto vml_drawings_part = manifest.canonicalize({ workbook_rel, sheet_rel, - manifest.relationship(sheet_path, xlnt::relationship_type::vml_drawing) }); + auto vml_drawings_part = manifest.canonicalize({workbook_rel, sheet_rel, + manifest.relationship(sheet_path, xlnt::relationship_type::vml_drawing)}); auto vml_drawings_part_streambuf = archive_->open(comments_part); std::istream vml_drawings_part_stream(comments_part_streambuf.get()); @@ -1058,8 +1252,7 @@ bool xlsx_consumer::has_cell() std::vector xlsx_consumer::read_relationships(const path &part) { - const auto part_rels_path = part.parent().append("_rels") - .append(part.filename() + ".rels").relative_to(path("/")); + const auto part_rels_path = part.parent().append("_rels").append(part.filename() + ".rels").relative_to(path("/")); std::vector relationships; if (!archive_->has_file(part_rels_path)) return relationships; @@ -1267,8 +1460,8 @@ void xlsx_consumer::populate_workbook(bool streaming) } } - read_part({ manifest().relationship(root_path, - relationship_type::office_document) }); + read_part({manifest().relationship(root_path, + relationship_type::office_document)}); } // Package Parts @@ -1365,14 +1558,16 @@ void xlsx_consumer::read_custom_properties() void xlsx_consumer::read_office_document(const std::string &content_type) // CT_Workbook { - if (content_type != "application/vnd." - "openxmlformats-officedocument.spreadsheetml.sheet.main+xml" - && content_type != "application/vnd." - "openxmlformats-officedocument.spreadsheetml.template.main+xml") + if (content_type != + "application/vnd." + "openxmlformats-officedocument.spreadsheetml.sheet.main+xml" + && content_type != + "application/vnd." + "openxmlformats-officedocument.spreadsheetml.template.main+xml") { throw xlnt::invalid_file(content_type); } - + target_.d_->calculation_properties_.clear(); expect_start_element(qn("workbook", "workbook"), xml::content::complex); @@ -1414,11 +1609,37 @@ void xlsx_consumer::read_office_document(const std::string &content_type) // CT_ { skip_remaining_content(current_workbook_element); } + else if (current_workbook_element == qn("mc", "AlternateContent")) + { + while (in_element(qn("mc", "AlternateContent"))) + { + auto alternate_content_element = expect_start_element(xml::content::complex); + + if (alternate_content_element == qn("mc", "Choice") + && parser().attribute_present("Requires") + && parser().attribute("Requires") == "x15") + { + auto x15_element = expect_start_element(xml::content::simple); + + if (x15_element == qn("x15ac", "absPath")) + { + target_.d_->abs_path_ = parser().attribute("url"); + } + + skip_remaining_content(x15_element); + expect_end_element(x15_element); + } + + skip_remaining_content(alternate_content_element); + expect_end_element(alternate_content_element); + } + } else if (current_workbook_element == qn("workbook", "workbookPr")) // CT_WorkbookPr 0-1 { target_.base_date(parser().attribute_present("date1904") // optional, bool=false - && is_true(parser().attribute("date1904")) - ? calendar::mac_1904 : calendar::windows_1900); + && is_true(parser().attribute("date1904")) + ? calendar::mac_1904 + : calendar::windows_1900); skip_attribute("showObjects"); // optional, ST_Objects="all" skip_attribute("showBorderUnselectedTables"); // optional, bool=true skip_attribute("filterPrivacy"); // optional, bool=false @@ -1447,8 +1668,8 @@ void xlsx_consumer::read_office_document(const std::string &content_type) // CT_ while (in_element(qn("workbook", "bookViews"))) { expect_start_element(qn("workbook", "workbookView"), xml::content::simple); - skip_attributes({"activeTab", "firstSheet", - "showHorizontalScroll", "showSheetTabs", "showVerticalScroll"}); + skip_attributes({"firstSheet", "showHorizontalScroll", + "showSheetTabs", "showVerticalScroll"}); workbook_view view; @@ -1477,6 +1698,11 @@ void xlsx_consumer::read_office_document(const std::string &content_type) // CT_ view.tab_ratio = parser().attribute("tabRatio"); } + if (parser().attribute_present("activeTab")) + { + view.active_tab = parser().attribute("activeTab"); + } + target_.view(view); skip_attributes(); @@ -1561,11 +1787,28 @@ void xlsx_consumer::read_office_document(const std::string &content_type) // CT_ } else if (current_workbook_element == qn("workbook", "extLst")) // CT_ExtensionList 0-1 { - skip_remaining_content(current_workbook_element); - } - else if (current_workbook_element == qn("mc", "AlternateContent")) - { - skip_remaining_content(current_workbook_element); + while (in_element(qn("workbook", "extLst"))) + { + auto extension_element = expect_start_element(xml::content::complex); + + if (extension_element == qn("workbook", "ext") + && parser().attribute_present("uri") + && parser().attribute("uri") == "{7523E5D3-25F3-A5E0-1632-64F254C22452}") + { + auto arch_id_extension_element = expect_start_element(xml::content::simple); + + if (arch_id_extension_element == qn("mx", "ArchID")) + { + target_.d_->arch_id_flags_ = parser().attribute("Flags"); + } + + skip_remaining_content(arch_id_extension_element); + expect_end_element(arch_id_extension_element); + } + + skip_remaining_content(extension_element); + expect_end_element(extension_element); + } } else { @@ -1607,7 +1850,8 @@ void xlsx_consumer::read_office_document(const std::string &content_type) // CT_ target_.d_->sheet_title_rel_id_map_.end(), [&](const std::pair &p) { return p.second == worksheet_rel.id(); - })->first; + }) + ->first; auto id = sheet_title_id_map_[title]; auto index = sheet_title_index_map_[title]; @@ -1622,7 +1866,7 @@ void xlsx_consumer::read_office_document(const std::string &content_type) // CT_ if (!streaming_) { - read_part({ workbook_rel, worksheet_rel }); + read_part({workbook_rel, worksheet_rel}); } } } @@ -1675,18 +1919,17 @@ void xlsx_consumer::read_shared_string_table() unique_count = parser().attribute("uniqueCount"); } - auto &strings = target_.shared_strings(); - while (in_element(qn("spreadsheetml", "sst"))) { expect_start_element(qn("spreadsheetml", "si"), xml::content::complex); - strings.push_back(read_rich_text(qn("spreadsheetml", "si"))); + auto rt = read_rich_text(qn("spreadsheetml", "si")); + target_.add_shared_string(rt); expect_end_element(qn("spreadsheetml", "si")); } expect_end_element(qn("spreadsheetml", "sst")); - if (has_unique_count && unique_count != strings.size()) + if (has_unique_count && unique_count != target_.shared_strings().size()) { throw invalid_file("sizes don't match"); } @@ -1870,7 +2113,11 @@ void xlsx_consumer::read_stylesheet() { auto &fonts = stylesheet.fonts; auto count = parser().attribute("count"); - skip_attributes({qn("x14ac", "knownFonts")}); + + if (parser().attribute_present(qn("x14ac", "knownFonts"))) + { + target_.enable_known_fonts(); + } while (in_element(qn("spreadsheetml", "fonts"))) { @@ -1916,7 +2163,16 @@ void xlsx_consumer::read_stylesheet() } else if (font_property_element == qn("spreadsheetml", "vertAlign")) { - new_font.superscript(parser().attribute("val") == "superscript"); + auto vert_align = parser().attribute("val"); + + if (vert_align == "superscript") + { + new_font.superscript(true); + } + else if (vert_align == "subscript") + { + new_font.subscript(true); + } } else if (font_property_element == qn("spreadsheetml", "strike")) { @@ -2074,36 +2330,52 @@ void xlsx_consumer::read_stylesheet() expect_start_element(qn("spreadsheetml", "xf"), xml::content::complex); auto &record = *(!in_style_records - ? format_records.emplace(format_records.end()) - : style_records.emplace(style_records.end())); + ? format_records.emplace(format_records.end()) + : style_records.emplace(style_records.end())); - record.first.border_applied = parser().attribute_present("applyBorder") - && is_true(parser().attribute("applyBorder")); + if (parser().attribute_present("applyBorder")) + { + record.first.border_applied = is_true(parser().attribute("applyBorder")); + } record.first.border_id = parser().attribute_present("borderId") - ? parser().attribute("borderId") : 0; + ? parser().attribute("borderId") + : 0; - record.first.fill_applied = parser().attribute_present("applyFill") - && is_true(parser().attribute("applyFill")); + if (parser().attribute_present("applyFill")) + { + record.first.fill_applied = is_true(parser().attribute("applyFill")); + } record.first.fill_id = parser().attribute_present("fillId") - ? parser().attribute("fillId") : 0; + ? parser().attribute("fillId") + : 0; - record.first.font_applied = parser().attribute_present("applyFont") - && is_true(parser().attribute("applyFont")); + if (parser().attribute_present("applyFont")) + { + record.first.font_applied = is_true(parser().attribute("applyFont")); + } record.first.font_id = parser().attribute_present("fontId") - ? parser().attribute("fontId") : 0; + ? parser().attribute("fontId") + : 0; - record.first.number_format_applied = parser().attribute_present("applyNumberFormat") - && is_true(parser().attribute("applyNumberFormat")); + if (parser().attribute_present("applyNumberFormat")) + { + record.first.number_format_applied = is_true(parser().attribute("applyNumberFormat")); + } record.first.number_format_id = parser().attribute_present("numFmtId") - ? parser().attribute("numFmtId") : 0; + ? parser().attribute("numFmtId") + : 0; auto apply_alignment_present = parser().attribute_present("applyAlignment"); - record.first.alignment_applied = apply_alignment_present - && is_true(parser().attribute("applyAlignment")); + if (apply_alignment_present) + { + record.first.alignment_applied = is_true(parser().attribute("applyAlignment")); + } auto apply_protection_present = parser().attribute_present("applyProtection"); - record.first.protection_applied = apply_protection_present - && is_true(parser().attribute("applyProtection")); + if (apply_protection_present) + { + record.first.protection_applied = is_true(parser().attribute("applyProtection")); + } record.first.pivot_button_ = parser().attribute_present("pivotButton") && is_true(parser().attribute("pivotButton")); @@ -2227,7 +2499,25 @@ void xlsx_consumer::read_stylesheet() } else if (current_style_element == qn("spreadsheetml", "extLst")) { - skip_remaining_content(current_style_element); + while (in_element(qn("spreadsheetml", "extLst"))) + { + expect_start_element(qn("spreadsheetml", "ext"), xml::content::complex); + + const auto uri = parser().attribute("uri"); + + if (uri == "{EB79DEF2-80B8-43e5-95BD-54CBDDF9020C}") // slicerStyles + { + expect_start_element(qn("x14", "slicerStyles"), xml::content::simple); + stylesheet.default_slicer_style = parser().attribute("defaultSlicerStyle"); + expect_end_element(qn("x14", "slicerStyles")); + } + else + { + skip_remaining_content(qn("spreadsheetml", "ext")); + } + + expect_end_element(qn("spreadsheetml", "ext")); + } } else if (current_style_element == qn("spreadsheetml", "colors")) // CT_Colors 0-1 { @@ -2326,7 +2616,7 @@ void xlsx_consumer::read_stylesheet() new_format.pivot_button_ = record.first.pivot_button_; new_format.quote_prefix_ = record.first.quote_prefix_; - new_format.style = styles.at(record.second).first.name; + set_style_by_xfid(styles, record.second, new_format.style); } } @@ -2364,7 +2654,7 @@ void xlsx_consumer::read_comments(worksheet ws) expect_start_element(qn("spreadsheetml", "comments"), xml::content::complex); // name space can be ignored - skip_attribute(qn("mc","Ignorable")); + skip_attribute(qn("mc", "Ignorable")); expect_start_element(qn("spreadsheetml", "authors"), xml::content::complex); while (in_element(qn("spreadsheetml", "authors"))) @@ -2391,12 +2681,12 @@ void xlsx_consumer::read_comments(worksheet ws) expect_end_element(qn("spreadsheetml", "text")); - if (in_element(xml::qname(qn("spreadsheetml", "comment")))) - { - expect_start_element(qn("mc", "AlternateContent"), xml::content::complex); - skip_remaining_content(qn("mc", "AlternateContent")); - expect_end_element(qn("mc", "AlternateContent")); - } + if (in_element(xml::qname(qn("spreadsheetml", "comment")))) + { + expect_start_element(qn("mc", "AlternateContent"), xml::content::complex); + skip_remaining_content(qn("mc", "AlternateContent")); + expect_end_element(qn("mc", "AlternateContent")); + } expect_end_element(qn("spreadsheetml", "comment")); } @@ -2597,12 +2887,16 @@ rich_text xlsx_consumer::read_rich_text(const xml::qname &parent) while (in_element(parent)) { auto text_element = expect_start_element(xml::content::mixed); + const auto xml_space = qn("xml", "space"); + const auto preserve_space = parser().attribute_present(xml_space) + ? parser().attribute(xml_space) == "preserve" + : false; skip_attributes(); auto text = read_text(); if (text_element == xml::qname(xmlns, "t")) { - t.plain_text(text); + t.plain_text(text, preserve_space); } else if (text_element == xml::qname(xmlns, "r")) { @@ -2648,12 +2942,14 @@ rich_text xlsx_consumer::read_rich_text(const xml::qname &parent) else if (current_run_property_element == xml::qname(xmlns, "b")) { run.second.get().bold(parser().attribute_present("val") - ? is_true(parser().attribute("val")) : true); + ? is_true(parser().attribute("val")) + : true); } else if (current_run_property_element == xml::qname(xmlns, "i")) { run.second.get().italic(parser().attribute_present("val") - ? is_true(parser().attribute("val")) : true); + ? is_true(parser().attribute("val")) + : true); } else if (current_run_property_element == xml::qname(xmlns, "u")) { @@ -2748,4 +3044,4 @@ manifest &xlsx_consumer::manifest() } } // namespace detail -} // namepsace xlnt +} // namespace xlnt diff --git a/xlnt/source/detail/serialization/xlsx_consumer.hpp b/xlnt/source/detail/serialization/xlsx_consumer.hpp index 4729c94..25aa789 100644 --- a/xlnt/source/detail/serialization/xlsx_consumer.hpp +++ b/xlnt/source/detail/serialization/xlsx_consumer.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/xlnt/source/detail/serialization/xlsx_producer.cpp b/xlnt/source/detail/serialization/xlsx_producer.cpp index 5491ccb..ec0d890 100644 --- a/xlnt/source/detail/serialization/xlsx_producer.cpp +++ b/xlnt/source/detail/serialization/xlsx_producer.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -34,9 +34,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -44,14 +46,6 @@ namespace { -/// -/// Returns true if d is exactly equal to an integer. -/// -bool is_integral(double d) -{ - return std::fabs(d - static_cast(static_cast(d))) == 0.0; -} - std::vector> core_property_namespace(xlnt::core_property type) { using xlnt::core_property; @@ -461,11 +455,22 @@ void xlsx_producer::write_workbook(const relationship &rel) static const auto &xmlns = constants::ns("workbook"); static const auto &xmlns_r = constants::ns("r"); static const auto &xmlns_s = constants::ns("spreadsheetml"); + static const auto &xmlns_mx = constants::ns("mx"); + static const auto &xmlns_x15ac = constants::ns("x15ac"); + static const auto &xmlns_x15 = constants::ns("x15"); + static const auto &xmlns_mc = constants::ns("mc"); write_start_element(xmlns, "workbook"); write_namespace(xmlns, ""); write_namespace(xmlns_r, "r"); + if (source_.d_->abs_path_.is_set()) + { + write_namespace(xmlns_mc, "mc"); + write_namespace(xmlns_x15, "x15"); + write_attribute(xml::qname(xmlns_mc, "Ignorable"), "x15"); + } + if (source_.has_file_version()) { write_start_element(xmlns, "fileVersion"); @@ -492,6 +497,20 @@ void xlsx_producer::write_workbook(const relationship &rel) write_end_element(xmlns, "workbookPr"); + if (source_.d_->abs_path_.is_set()) + { + write_start_element(xmlns_mc, "AlternateContent"); + write_namespace(xmlns_mc, "mc"); + write_start_element(xmlns_mc, "Choice"); + write_attribute("Requires", "x15"); + write_start_element(xmlns_x15ac, "absPath"); + write_namespace(xmlns_x15ac, "x15ac"); + write_attribute("url", source_.d_->abs_path_.get()); + write_end_element(xmlns_x15ac, "absPath"); + write_end_element(xmlns_mc, "Choice"); + write_end_element(xmlns_mc, "AlternateContent"); + } + if (source_.has_view()) { write_start_element(xmlns, "bookViews"); @@ -499,7 +518,7 @@ void xlsx_producer::write_workbook(const relationship &rel) const auto &view = source_.view(); - if (view.active_tab.is_set()) + if (view.active_tab.is_set() && view.active_tab.get() != std::size_t(0)) { write_attribute("activeTab", view.active_tab.get()); } @@ -633,6 +652,19 @@ void xlsx_producer::write_workbook(const relationship &rel) write_end_element(xmlns, "definedNames"); } + if (source_.d_->arch_id_flags_.is_set()) + { + write_start_element(xmlns, "extLst"); + write_start_element(xmlns, "ext"); + write_namespace(xmlns_mx, "mx"); + write_attribute("uri", "{7523E5D3-25F3-A5E0-1632-64F254C22452}"); + write_start_element(xmlns_mx, "ArchID"); + write_attribute("Flags", source_.d_->arch_id_flags_.get()); + write_end_element(xmlns_mx, "ArchID"); + write_end_element(xmlns, "ext"); + write_end_element(xmlns, "extLst"); + } + write_end_element(xmlns, "workbook"); auto workbook_rels = source_.manifest().relationships(rel.target().path()); @@ -818,20 +850,17 @@ void xlsx_producer::write_shared_string_table(const relationship & /*rel*/) #pragma clang diagnostic pop write_attribute("count", string_count); - write_attribute("uniqueCount", source_.shared_strings().size()); - - auto has_trailing_whitespace = [](const std::string &s) - { - return !s.empty() && (s.front() == ' ' || s.back() == ' '); - }; + write_attribute("uniqueCount", source_.shared_strings_by_id().size()); - for (const auto &string : source_.shared_strings()) + for (const auto &string : source_.shared_strings_by_id()) { - if (string.runs().size() == 1 && !string.runs().at(0).second.is_set()) + if (string.second.runs().size() == 1 && !string.second.runs().at(0).second.is_set()) { write_start_element(xmlns, "si"); write_start_element(xmlns, "t"); - write_characters(string.plain_text(), has_trailing_whitespace(string.plain_text())); + + write_characters(string.second.plain_text(), string.second.runs().front().preserve_space); + write_end_element(xmlns, "t"); write_end_element(xmlns, "si"); @@ -840,7 +869,7 @@ void xlsx_producer::write_shared_string_table(const relationship & /*rel*/) write_start_element(xmlns, "si"); - for (const auto &run : string.runs()) + for (const auto &run : string.second.runs()) { write_start_element(xmlns, "r"); @@ -893,7 +922,7 @@ void xlsx_producer::write_shared_string_table(const relationship & /*rel*/) } write_start_element(xmlns, "t"); - write_characters(run.first, has_trailing_whitespace(run.first)); + write_characters(run.first, run.preserve_space); write_end_element(xmlns, "t"); write_end_element(xmlns, "r"); } @@ -931,34 +960,50 @@ void xlsx_producer::write_font(const font &f) if (f.bold()) { write_start_element(xmlns, "b"); - write_attribute("val", write_bool(true)); write_end_element(xmlns, "b"); } if (f.italic()) { write_start_element(xmlns, "i"); - write_attribute("val", write_bool(true)); write_end_element(xmlns, "i"); } - if (f.underlined()) - { - write_start_element(xmlns, "u"); - write_attribute("val", f.underline()); - write_end_element(xmlns, "u"); - } - if (f.strikethrough()) { write_start_element(xmlns, "strike"); - write_attribute("val", write_bool(true)); write_end_element(xmlns, "strike"); } - write_start_element(xmlns, "sz"); - write_attribute("val", f.size()); - write_end_element(xmlns, "sz"); + if (f.underlined()) + { + write_start_element(xmlns, "u"); + if (f.underline() != font::underline_style::single) + { + write_attribute("val", f.underline()); + } + write_end_element(xmlns, "u"); + } + + if (f.superscript()) + { + write_start_element(xmlns, "vertAlign"); + write_attribute("val", "superscript"); + write_end_element(xmlns, "vertAlign"); + } + else if (f.subscript()) + { + write_start_element(xmlns, "vertAlign"); + write_attribute("val", "subscript"); + write_end_element(xmlns, "vertAlign"); + } + + if (f.has_size()) + { + write_start_element(xmlns, "sz"); + write_attribute("val", f.size()); + write_end_element(xmlns, "sz"); + } if (f.has_color()) { @@ -967,9 +1012,12 @@ void xlsx_producer::write_font(const font &f) write_end_element(xmlns, "color"); } - write_start_element(xmlns, "name"); - write_attribute("val", f.name()); - write_end_element(xmlns, "name"); + if (f.has_name()) + { + write_start_element(xmlns, "name"); + write_attribute("val", f.name()); + write_end_element(xmlns, "name"); + } if (f.has_family()) { @@ -1113,13 +1161,33 @@ void xlsx_producer::write_border(const border ¤t_border) void xlsx_producer::write_styles(const relationship & /*rel*/) { - static const auto &xmlns = constants::ns("spreadsheetml"); + static const auto &xmlns = constants::ns("spreadsheetml"); + static const auto &xmlns_mc = constants::ns("mc"); + static const auto &xmlns_x14 = constants::ns("x14"); + static const auto &xmlns_x14ac = constants::ns("x14ac"); write_start_element(xmlns, "styleSheet"); write_namespace(xmlns, ""); const auto &stylesheet = source_.impl().stylesheet_.get(); + auto using_namespace = [&stylesheet](const std::string &ns) + { + if (ns == "x14ac") + { + return stylesheet.known_fonts_enabled; + } + + return false; + }; + + if (using_namespace("x14ac")) + { + write_namespace(xmlns_mc, "mc"); + write_namespace(xmlns_x14ac, "x14ac"); + write_attribute(xml::qname(xmlns_mc, "Ignorable"), "x14ac"); + } + // Number Formats if (!stylesheet.number_formats.empty()) @@ -1156,6 +1224,31 @@ void xlsx_producer::write_styles(const relationship & /*rel*/) write_start_element(xmlns, "fonts"); write_attribute("count", fonts.size()); + if (stylesheet.known_fonts_enabled) + { + auto is_known_font = [](const font &f) + { + const auto &known_fonts = *new std::vector + { + font().name("Calibri").family(2).size(12).color(theme_color(1)).scheme("minor") + }; + + return std::find(known_fonts.begin(), known_fonts.end(), f) != known_fonts.end(); + }; + + std::size_t num_known_fonts = 0; + + for (const auto ¤t_font : fonts) + { + if (is_known_font(current_font)) + { + num_known_fonts += 1; + } + } + + write_attribute(xml::qname(xmlns_x14ac, "knownFonts"), num_known_fonts); + } + for (const auto ¤t_font : fonts) { write_font(current_font); @@ -1209,39 +1302,67 @@ void xlsx_producer::write_styles(const relationship & /*rel*/) const auto ¤t_style_impl = stylesheet.style_impls.at(current_style_name); write_start_element(xmlns, "xf"); - write_attribute("numFmtId", current_style_impl.number_format_id.get()); - write_attribute("fontId", current_style_impl.font_id.get()); - write_attribute("fillId", current_style_impl.fill_id.get()); - write_attribute("borderId", current_style_impl.border_id.get()); - if (current_style_impl.number_format_applied) + if (current_style_impl.number_format_id.is_set()) + { + write_attribute("numFmtId", current_style_impl.number_format_id.get()); + } + + if (current_style_impl.font_id.is_set()) + { + write_attribute("fontId", current_style_impl.font_id.get()); + } + + if (current_style_impl.fill_id.is_set()) + { + write_attribute("fillId", current_style_impl.fill_id.get()); + } + + if (current_style_impl.border_id.is_set()) { - write_attribute("applyNumberFormat", write_bool(true)); + write_attribute("borderId", current_style_impl.border_id.get()); } - if (current_style_impl.fill_applied) + if (current_style_impl.number_format_id.is_set() + && current_style_impl.number_format_applied.is_set()) { - write_attribute("applyFill", write_bool(true)); + write_attribute("applyNumberFormat", + write_bool(current_style_impl.number_format_applied.get())); } - if (current_style_impl.font_applied) + if (current_style_impl.fill_id.is_set() + && current_style_impl.fill_applied.is_set()) { - write_attribute("applyFont", write_bool(true)); + write_attribute("applyFill", + write_bool(current_style_impl.fill_applied.get())); } - if (current_style_impl.border_applied) + if (current_style_impl.font_id.is_set() + && current_style_impl.font_applied.is_set()) { - write_attribute("applyBorder", write_bool(true)); + write_attribute("applyFont", + write_bool(current_style_impl.font_applied.get())); } - if (current_style_impl.alignment_applied) + if (current_style_impl.border_id.is_set() + && current_style_impl.border_applied.is_set()) { - write_attribute("applyAlignment", write_bool(true)); + write_attribute("applyBorder", + write_bool(current_style_impl.border_applied.get())); } - if (current_style_impl.protection_applied) + if (current_style_impl.alignment_id.is_set() + && current_style_impl.alignment_applied.is_set()) { - write_attribute("applyProtection", write_bool(true)); + write_attribute("applyAlignment", + write_bool(current_style_impl.alignment_applied.get())); + } + + if (current_style_impl.protection_id.is_set() + && current_style_impl.protection_applied.is_set()) + { + write_attribute("applyProtection", + write_bool(current_style_impl.protection_applied.get())); } if (current_style_impl.pivot_button_) @@ -1318,48 +1439,69 @@ void xlsx_producer::write_styles(const relationship & /*rel*/) { write_start_element(xmlns, "xf"); - write_attribute("numFmtId", current_format_impl.number_format_id.get()); - write_attribute("fontId", current_format_impl.font_id.get()); + if (current_format_impl.number_format_id.is_set()) + { + write_attribute("numFmtId", current_format_impl.number_format_id.get()); + } - if (current_format_impl.style.is_set()) + if (current_format_impl.font_id.is_set()) { - write_attribute("fillId", stylesheet.style_impls.at(current_format_impl.style.get()).fill_id.get()); + write_attribute("fontId", current_format_impl.font_id.get()); } - else + + if (current_format_impl.fill_id.is_set()) { write_attribute("fillId", current_format_impl.fill_id.get()); } - write_attribute("borderId", current_format_impl.border_id.get()); + if (current_format_impl.border_id.is_set()) + { + write_attribute("borderId", current_format_impl.border_id.get()); + } - if (current_format_impl.number_format_applied) + if (current_format_impl.style.is_set()) + { + write_attribute("xfId", stylesheet.style_index(current_format_impl.style.get())); + } + + if (current_format_impl.number_format_id.is_set() + && current_format_impl.number_format_applied.is_set()) { - write_attribute("applyNumberFormat", write_bool(true)); + write_attribute("applyNumberFormat", + write_bool(current_format_impl.number_format_applied.get())); } - if (current_format_impl.fill_applied) + if (current_format_impl.fill_id.is_set() + && current_format_impl.fill_applied.is_set()) { - write_attribute("applyFill", write_bool(true)); + write_attribute("applyFill", + write_bool(current_format_impl.fill_applied.get())); } - if (current_format_impl.font_applied) + if (current_format_impl.font_id.is_set() + && current_format_impl.font_applied.is_set()) { - write_attribute("applyFont", write_bool(true)); + write_attribute("applyFont", + write_bool(current_format_impl.font_applied.get())); } - if (current_format_impl.border_applied) + if (current_format_impl.border_id.is_set() + && current_format_impl.border_applied.is_set()) { - write_attribute("applyBorder", write_bool(true)); + write_attribute("applyBorder", + write_bool(current_format_impl.border_applied.get())); } - if (current_format_impl.alignment_applied) + if (current_format_impl.alignment_applied.is_set()) { - write_attribute("applyAlignment", write_bool(true)); + write_attribute("applyAlignment", + write_bool(current_format_impl.alignment_applied.get())); } - if (current_format_impl.protection_applied) + if (current_format_impl.protection_applied.is_set()) { - write_attribute("applyProtection", write_bool(true)); + write_attribute("applyProtection", + write_bool(current_format_impl.protection_applied.get())); } if (current_format_impl.pivot_button_) @@ -1372,25 +1514,20 @@ void xlsx_producer::write_styles(const relationship & /*rel*/) write_attribute("quotePrefix", write_bool(true)); } - if (current_format_impl.style.is_set()) - { - write_attribute("xfId", stylesheet.style_index(current_format_impl.style.get())); - } - if (current_format_impl.alignment_id.is_set()) { const auto ¤t_alignment = stylesheet.alignments[current_format_impl.alignment_id.get()]; write_start_element(xmlns, "alignment"); - if (current_alignment.vertical().is_set()) + if (current_alignment.horizontal().is_set()) { - write_attribute("vertical", current_alignment.vertical().get()); + write_attribute("horizontal", current_alignment.horizontal().get()); } - if (current_alignment.horizontal().is_set()) + if (current_alignment.vertical().is_set()) { - write_attribute("horizontal", current_alignment.horizontal().get()); + write_attribute("vertical", current_alignment.vertical().get()); } if (current_alignment.rotation().is_set()) @@ -1521,6 +1658,26 @@ void xlsx_producer::write_styles(const relationship & /*rel*/) write_end_element(xmlns, "colors"); } + auto using_extensions = stylesheet.default_slicer_style.is_set(); + + if (using_extensions) + { + write_start_element(xmlns, "extLst"); + + if (stylesheet.default_slicer_style.is_set()) + { + write_start_element(xmlns, "ext"); + write_namespace(xmlns_x14, "x14"); + write_attribute("uri", "{EB79DEF2-80B8-43e5-95BD-54CBDDF9020C}"); // slicerStyles URI + write_start_element(xmlns_x14, "slicerStyles"); + write_attribute("defaultSlicerStyle", stylesheet.default_slicer_style.get()); + write_end_element(xmlns_x14, "slicerStyles"); + write_end_element(xmlns, "ext"); + } + + write_end_element(xmlns, "extLst"); + } + write_end_element(xmlns, "styleSheet"); } @@ -1997,6 +2154,8 @@ void xlsx_producer::write_worksheet(const relationship &rel) { static const auto &xmlns = constants::ns("spreadsheetml"); static const auto &xmlns_r = constants::ns("r"); + static const auto &xmlns_mc = constants::ns("mc"); + static const auto &xmlns_x14ac = constants::ns("x14ac"); auto worksheet_part = rel.source().path().parent().append(rel.target().path()); auto worksheet_rels = source_.manifest().relationships(worksheet_part); @@ -2012,9 +2171,74 @@ void xlsx_producer::write_worksheet(const relationship &rel) write_namespace(xmlns, ""); write_namespace(xmlns_r, "r"); - if (ws.has_page_setup()) + auto using_namespace = [&ws](const std::string &ns) + { + if (ns == "x14ac") + { + if (ws.format_properties().dy_descent.is_set()) + { + return true; + } + + for (auto row = ws.lowest_row(); row <= ws.highest_row(); ++row) + { + if (ws.has_row_properties(row) && ws.row_properties(row).dy_descent.is_set()) + { + return true; + } + } + } + + return false; + }; + + if (using_namespace("x14ac")) + { + write_namespace(xmlns_mc, "mc"); + write_namespace(xmlns_x14ac, "x14ac"); + write_attribute(xml::qname(xmlns_mc, "Ignorable"), "x14ac"); + } + + if (ws.d_->sheet_properties_.is_set()) { write_start_element(xmlns, "sheetPr"); + auto &props = ws.d_->sheet_properties_.get(); + if (props.sync_horizontal.is_set()) + { + write_attribute("syncHorizontal", props.sync_horizontal.get()); + } + if (props.sync_vertical.is_set()) + { + write_attribute("syncVertical", props.sync_vertical.get()); + } + if (props.sync_ref.is_set()) + { + write_attribute("syncRef", props.sync_ref.get().to_string()); + } + if (props.transition_evaluation.is_set()) + { + write_attribute("transitionEvaluation", props.transition_evaluation.get()); + } + if (props.transition_entry.is_set()) + { + write_attribute("transitionEntry", props.transition_entry.get()); + } + if (props.published.is_set()) + { + write_attribute("published", props.published.get()); + } + if (props.code_name.is_set()) + { + write_attribute("codeName", props.code_name.get()); + } + if (props.filter_mode.is_set()) + { + write_attribute("filterMode", props.filter_mode.get()); + } + if (props.enable_format_condition_calculation.is_set()) + { + write_attribute("enableFormatConditionsCalculation", props.enable_format_condition_calculation.get()); + } write_start_element(xmlns, "outlinePr"); write_attribute("summaryBelow", "1"); @@ -2040,16 +2264,26 @@ void xlsx_producer::write_worksheet(const relationship &rel) write_start_element(xmlns, "sheetViews"); write_start_element(xmlns, "sheetView"); + const auto wb_view = source_.view(); const auto view = ws.view(); - write_attribute("tabSelected", write_bool(view.id() == 0)); - write_attribute("workbookViewId", view.id()); + if ((wb_view.active_tab.is_set() && (ws.id() - 1) == wb_view.active_tab.get()) + || (!wb_view.active_tab.is_set() && ws.id() == 1)) + { + write_attribute("tabSelected", write_bool(true)); + } if (view.type() != sheet_view_type::normal) { - write_attribute( - "view", view.type() == sheet_view_type::page_break_preview ? "pageBreakPreview" : "pageLayout"); + write_attribute("view", view.type() == sheet_view_type::page_break_preview + ? "pageBreakPreview" : "pageLayout"); } + if (view.has_top_left_cell()) + { + write_attribute("topLeftCell", view.top_left_cell().to_string()); + } + + write_attribute("workbookViewId", view.id()); if (view.has_pane()) { @@ -2084,14 +2318,16 @@ void xlsx_producer::write_worksheet(const relationship &rel) if (current_selection.has_active_cell()) { write_attribute("activeCell", current_selection.active_cell().to_string()); - write_attribute("sqref", current_selection.active_cell().to_string()); - } - /* - if (current_selection.sqref() != "A1:A1") - { - write_attribute("sqref", current_selection.sqref().to_string()); - } - */ + } + + if (current_selection.has_sqref()) + { + const auto sqref = current_selection.sqref(); + write_attribute("sqref", sqref.is_single_cell() + ? sqref.top_left().to_string() + : sqref.to_string()); + } + if (current_selection.pane() != pane_corner::top_left) { write_attribute("pane", current_selection.pane()); @@ -2105,93 +2341,125 @@ void xlsx_producer::write_worksheet(const relationship &rel) } write_start_element(xmlns, "sheetFormatPr"); - write_attribute("baseColWidth", "10"); - write_attribute("defaultRowHeight", "16"); - write_end_element(xmlns, "sheetFormatPr"); + const auto &format_properties = ws.d_->format_properties_; - bool has_column_properties = false; + if (format_properties.base_col_width.is_set()) + { + write_attribute("baseColWidth", + format_properties.base_col_width.get()); + } + if (format_properties.default_column_width.is_set()) + { + write_attribute("defaultColWidth", + format_properties.default_column_width.get()); + } + if (format_properties.default_row_height.is_set()) + { + write_attribute("defaultRowHeight", + format_properties.default_row_height.get()); + } - for (auto column = ws.lowest_column(); column <= ws.highest_column(); column++) + if (format_properties.dy_descent.is_set()) { - if (ws.has_column_properties(column)) - { - has_column_properties = true; - break; - } + write_attribute(xml::qname(xmlns_x14ac, "dyDescent"), + format_properties.dy_descent.get()); } - if (has_column_properties) + write_end_element(xmlns, "sheetFormatPr"); + + bool has_column_properties = false; + const auto first_column = ws.lowest_column_or_props(); + const auto last_column = ws.highest_column_or_props(); + + for (auto column = first_column; column <= last_column; column++) { - write_start_element(xmlns, "cols"); + if (!ws.has_column_properties(column)) continue; - for (auto column = ws.lowest_column_or_props(); column <= ws.highest_column_or_props(); column++) + if(!has_column_properties) { - if (!ws.has_column_properties(column)) continue; + write_start_element(xmlns, "cols"); + has_column_properties = true; + } - const auto &props = ws.column_properties(column); + const auto &props = ws.column_properties(column); - write_start_element(xmlns, "col"); - write_attribute("min", column.index); - write_attribute("max", column.index); + write_start_element(xmlns, "col"); + write_attribute("min", column.index); + write_attribute("max", column.index); - if (props.width.is_set()) - { - write_attribute("width", (props.width.get() * 7 + 5) / 7); - } + if (props.width.is_set()) + { + double width = (props.width.get() * 7 + 5) / 7; + write_attribute("width", serialize_number_to_string(width)); + } - if (props.custom_width) - { - write_attribute("customWidth", write_bool(true)); - } + if (props.best_fit) + { + write_attribute("bestFit", write_bool(true)); + } - if (props.style.is_set()) - { - write_attribute("style", props.style.get()); - } + if (props.style.is_set()) + { + write_attribute("style", props.style.get()); + } - if (props.hidden) - { - write_attribute("hidden", write_bool(true)); - } + if (props.hidden) + { + write_attribute("hidden", write_bool(true)); + } - write_end_element(xmlns, "col"); + if (props.custom_width) + { + write_attribute("customWidth", write_bool(true)); } - write_end_element(xmlns, "cols"); + write_end_element(xmlns, "col"); } - const auto hyperlink_rels = source_.manifest().relationships(worksheet_part, relationship_type::hyperlink); - std::unordered_map reverse_hyperlink_references; - - for (auto hyperlink_rel : hyperlink_rels) + if (has_column_properties) { - reverse_hyperlink_references[hyperlink_rel.target().path().string()] = rel.id(); + write_end_element(xmlns, "cols"); } - std::unordered_map hyperlink_references; + std::vector> hyperlinks; std::vector cells_with_comments; write_start_element(xmlns, "sheetData"); + auto first_row = ws.lowest_row_or_props(); + auto last_row = ws.highest_row_or_props(); + auto first_block_column = constants::max_column(); + auto last_block_column = constants::min_column(); - for (auto row = ws.lowest_row_or_props(); row <= ws.highest_row_or_props(); ++row) + for (auto row = first_row; row <= last_row; ++row) { - auto first_column = constants::max_column(); - auto last_column = constants::min_column(); - bool any_non_null = false; + auto first_check_row = row; + auto last_check_row = row; + auto first_row_in_block = row == first_row || row % 16 == 1; - for (auto column = dimension.top_left().column(); column <= dimension.bottom_right().column(); ++column) + // See note for CT_Row, span attribute about block optimization + if (first_row_in_block) { - if (!ws.has_cell(cell_reference(column, row))) continue; + first_check_row = row; + // round up to the next multiple of 16 + last_check_row = ((row / 16) + 1) * 16; + } - auto cell = ws.cell(cell_reference(column, row)); + for (auto check_row = first_check_row; check_row <= last_check_row; ++check_row) + { + for (auto column = dimension.top_left().column(); column <= dimension.bottom_right().column(); ++column) + { + if (!ws.has_cell(cell_reference(column, row))) continue; + auto cell = ws.cell(cell_reference(column, row)); + if (cell.garbage_collectible()) continue; - first_column = std::min(first_column, cell.column()); - last_column = std::max(last_column, cell.column()); + first_block_column = std::min(first_block_column, cell.column()); + last_block_column = std::max(last_block_column, cell.column()); - if (!cell.garbage_collectible()) - { - any_non_null = true; + if (row == check_row) + { + any_non_null = true; + } } } @@ -2200,39 +2468,43 @@ void xlsx_producer::write_worksheet(const relationship &rel) write_start_element(xmlns, "row"); write_attribute("r", row); - if (any_non_null) - { - auto span_string = std::to_string(first_column.index) + ":" + std::to_string(last_column.index); - write_attribute("spans", span_string); - } + auto span_string = std::to_string(first_block_column.index) + ":" + + std::to_string(last_block_column.index); + write_attribute("spans", span_string); if (ws.has_row_properties(row)) { const auto &props = ws.row_properties(row); - if (props.custom_height) + if (props.style.is_set()) { - write_attribute("customHeight", write_bool(true)); + write_attribute("s", props.style.get()); + } + if (props.custom_format.is_set()) + { + write_attribute("customFormat", write_bool(props.custom_format.get())); } if (props.height.is_set()) { auto height = props.height.get(); - - if (std::fabs(height - std::floor(height)) == 0.0) - { - write_attribute("ht", std::to_string(static_cast(height)) + ".0"); - } - else - { - write_attribute("ht", height); - } + write_attribute("ht", serialize_number_to_string(height)); } if (props.hidden) { write_attribute("hidden", write_bool(true)); } + + if (props.custom_height) + { + write_attribute("customHeight", write_bool(true)); + } + + if (props.dy_descent.is_set()) + { + write_attribute(xml::qname(xmlns_x14ac, "dyDescent"), props.dy_descent.get()); + } } if (any_non_null) @@ -2254,7 +2526,7 @@ void xlsx_producer::write_worksheet(const relationship &rel) if (cell.has_hyperlink()) { - hyperlink_references[cell.reference().to_string()] = reverse_hyperlink_references[cell.hyperlink()]; + hyperlinks.push_back(std::make_pair(cell.reference().to_string(), cell.hyperlink())); } write_start_element(xmlns, "c"); @@ -2289,8 +2561,8 @@ void xlsx_producer::write_worksheet(const relationship &rel) write_attribute("t", "inlineStr"); break; - case cell::type::number: - write_attribute("t", "n"); + case cell::type::number: // default, don't write it + //write_attribute("t", "n"); break; case cell::type::shared_string: @@ -2339,19 +2611,7 @@ void xlsx_producer::write_worksheet(const relationship &rel) case cell::type::number: write_start_element(xmlns, "v"); - - if (is_integral(cell.value())) - { - write_characters(static_cast(cell.value())); - } - else - { - std::stringstream ss; - ss.precision(20); - ss << cell.value(); - write_characters(ss.str()); - } - + write_characters(serialize_number_to_string(cell.value())); write_end_element(xmlns, "v"); break; @@ -2438,29 +2698,73 @@ void xlsx_producer::write_worksheet(const relationship &rel) } } - if (!hyperlink_rels.empty()) + if (!hyperlinks.empty()) { write_start_element(xmlns, "hyperlinks"); - for (const auto &hyperlink : hyperlink_references) + for (const auto &hyperlink : hyperlinks) { write_start_element(xmlns, "hyperlink"); write_attribute("ref", hyperlink.first); - write_attribute(xml::qname(xmlns_r, "id"), hyperlink.second); + if (hyperlink.second.external()) + { + write_attribute(xml::qname(xmlns_r, "id"), + hyperlink.second.relationship().id()); + } + else + { + write_attribute("location", hyperlink.second.target_range()); + write_attribute("display", hyperlink.second.display()); + } write_end_element(xmlns, "hyperlink"); } write_end_element(xmlns, "hyperlinks"); } - if (ws.has_page_setup()) + if (ws.d_->print_options_.is_set()) { + auto &opts = ws.d_->print_options_.get(); write_start_element(xmlns, "printOptions"); - write_attribute("horizontalCentered", write_bool(ws.page_setup().horizontal_centered())); - write_attribute("verticalCentered", write_bool(ws.page_setup().vertical_centered())); + if (opts.print_grid_lines.is_set()) + { + write_attribute("gridLines", write_bool(opts.print_grid_lines.get())); + } + if (opts.grid_lines_set.is_set()) + { + write_attribute("gridLineSet", write_bool(opts.grid_lines_set.get())); + } + if (opts.print_headings.is_set()) + { + write_attribute("headings", write_bool(opts.print_headings.get())); + } + if (opts.horizontal_centered.is_set()) + { + write_attribute("horizontalCentered", write_bool(opts.horizontal_centered.get())); + } + if (opts.vertical_centered.is_set()) + { + write_attribute("verticalCentered", write_bool(opts.vertical_centered.get())); + } write_end_element(xmlns, "printOptions"); } + if (ws.has_phonetic_properties()) + { + write_start_element(xmlns, phonetic_pr::Serialised_ID()); + const auto &ph_props = ws.phonetic_properties(); + write_attribute("fontId", ph_props.font_id()); + if (ph_props.has_type()) + { + write_attribute("type", phonetic_pr::type_as_string(ph_props.type())); + } + if (ph_props.has_alignment()) + { + write_attribute("alignment", phonetic_pr::alignment_as_string(ph_props.alignment())); + } + write_end_element(xmlns, phonetic_pr::Serialised_ID()); + } + if (ws.has_page_margins()) { write_start_element(xmlns, "pageMargins"); @@ -2499,11 +2803,21 @@ void xlsx_producer::write_worksheet(const relationship &rel) if (ws.has_page_setup()) { write_start_element(xmlns, "pageSetup"); - write_attribute( - "orientation", ws.page_setup().orientation() == xlnt::orientation::landscape ? "landscape" : "portrait"); - write_attribute("paperSize", static_cast(ws.page_setup().paper_size())); + if (ws.page_setup().orientation_.is_set()) + { + write_attribute("orientation", ws.page_setup().orientation_.get()); + } + if (ws.page_setup().horizontal_dpi_.is_set()) + { + write_attribute("horizontalDpi", ws.page_setup().horizontal_dpi_.get()); + } + if (ws.page_setup().vertical_dpi_.is_set()) + { + write_attribute("verticalDpi", ws.page_setup().vertical_dpi_.get()); + } + /*write_attribute("paperSize", static_cast(ws.page_setup().paper_size())); write_attribute("fitToHeight", write_bool(ws.page_setup().fit_to_height())); - write_attribute("fitToWidth", write_bool(ws.page_setup().fit_to_width())); + write_attribute("fitToWidth", write_bool(ws.page_setup().fit_to_width()));*/ write_end_element(xmlns, "pageSetup"); } @@ -2659,6 +2973,11 @@ void xlsx_producer::write_worksheet(const relationship &rel) } } + if (ws.d_->extension_list_.is_set()) + { + ws.d_->extension_list_.get().serialize(*current_part_serializer_, xmlns); + } + write_end_element(xmlns, "worksheet"); if (!worksheet_rels.empty()) diff --git a/xlnt/source/detail/serialization/xlsx_producer.hpp b/xlnt/source/detail/serialization/xlsx_producer.hpp index a352ac4..9605094 100644 --- a/xlnt/source/detail/serialization/xlsx_producer.hpp +++ b/xlnt/source/detail/serialization/xlsx_producer.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/source/detail/serialization/zstream.cpp b/xlnt/source/detail/serialization/zstream.cpp index 9888d31..be72bd5 100644 --- a/xlnt/source/detail/serialization/zstream.cpp +++ b/xlnt/source/detail/serialization/zstream.cpp @@ -366,7 +366,7 @@ class zip_streambuf_compress : public std::streambuf deflateEnd(&strm); if (header) { - std::ios::streampos final_position = ostream.tellp(); + auto final_position = ostream.tellp(); header->uncompressed_size = uncompressed_size; header->crc = crc; ostream.seekp(header->header_offset); @@ -457,14 +457,14 @@ ozstream::ozstream(std::ostream &stream) ozstream::~ozstream() { // Write all file headers - std::ios::streampos final_position = destination_stream_.tellp(); + auto final_position = destination_stream_.tellp(); for (const auto &header : file_headers_) { write_header(header, destination_stream_, true); } - std::ios::streampos central_end = destination_stream_.tellp(); + auto central_end = destination_stream_.tellp(); // Write end of central write_int(destination_stream_, static_cast(0x06054b50)); // end of central @@ -507,12 +507,12 @@ bool izstream::read_central_header() // Find the header // NOTE: this assumes the zip file header is the last thing written to file... source_stream_.seekg(0, std::ios_base::end); - std::ios::streampos end_position = source_stream_.tellg(); + auto end_position = static_cast(source_stream_.tellg()); auto max_comment_size = std::uint32_t(0xffff); // max size of header auto read_size_before_comment = std::uint32_t(22); - std::ios::streamoff read_start = max_comment_size + read_size_before_comment; + std::uint32_t read_start = max_comment_size + read_size_before_comment; if (read_start > end_position) { diff --git a/xlnt/source/detail/unicode.cpp b/xlnt/source/detail/unicode.cpp index 84f07c7..f93c17a 100644 --- a/xlnt/source/detail/unicode.cpp +++ b/xlnt/source/detail/unicode.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/source/detail/unicode.hpp b/xlnt/source/detail/unicode.hpp index 9676086..bd83df7 100644 --- a/xlnt/source/detail/unicode.hpp +++ b/xlnt/source/detail/unicode.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/source/packaging/manifest.cpp b/xlnt/source/packaging/manifest.cpp index 13e8590..d11c29e 100644 --- a/xlnt/source/packaging/manifest.cpp +++ b/xlnt/source/packaging/manifest.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -78,16 +78,25 @@ path manifest::canonicalize(const std::vector &rels) const return result; } -bool manifest::has_relationship(const path &part, relationship_type type) const +bool manifest::has_relationship(const path &path, relationship_type type) const { - if (relationships_.find(part) == relationships_.end()) return false; - - for (const auto &rel : relationships_.at(part)) + auto rels = relationships_.find(path); + if (rels == relationships_.end()) { - if (rel.second.type() == type) return true; + return false; } + return rels->second.end() != std::find_if(rels->second.begin(), rels->second.end(), + [type](const std::pair &rel) { return rel.second.type() == type; }); +} - return false; +bool manifest::has_relationship(const path &path, const std::string &rel_id) const +{ + auto rels = relationships_.find(path); + if (rels == relationships_.end()) + { + return false; + } + return rels->second.find(rel_id) != rels->second.end(); } relationship manifest::relationship(const path &part, relationship_type type) const @@ -328,4 +337,11 @@ std::string manifest::override_type(const xlnt::path &part) const return override_content_types_.at(part); } +bool manifest::operator==(const manifest &other) const +{ + return default_content_types_ == other.default_content_types_ + && override_content_types_ == other.override_content_types_ + && relationships_ == other.relationships_; +} + } // namespace xlnt diff --git a/xlnt/source/packaging/relationship.cpp b/xlnt/source/packaging/relationship.cpp index 8ade398..9c104e0 100644 --- a/xlnt/source/packaging/relationship.cpp +++ b/xlnt/source/packaging/relationship.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -36,7 +36,7 @@ relationship::relationship( { } -std::string relationship::id() const +const std::string& relationship::id() const { return id_; } @@ -46,12 +46,12 @@ target_mode relationship::target_mode() const return mode_; } -uri relationship::source() const +const uri &relationship::source() const { return source_; } -uri relationship::target() const +const uri &relationship::target() const { return target_; } @@ -63,8 +63,16 @@ relationship_type relationship::type() const bool relationship::operator==(const relationship &rhs) const { - return type_ == rhs.type_ && id_ == rhs.id_ && source_ == rhs.source_ && target_ == rhs.target_ + return type_ == rhs.type_ + && id_ == rhs.id_ + && source_ == rhs.source_ + && target_ == rhs.target_ && mode_ == rhs.mode_; } +bool relationship::operator!=(const relationship &rhs) const +{ + return !(*this == rhs); +} + } // namespace xlnt diff --git a/xlnt/source/packaging/uri.cpp b/xlnt/source/packaging/uri.cpp index 10b4377..850234a 100644 --- a/xlnt/source/packaging/uri.cpp +++ b/xlnt/source/packaging/uri.cpp @@ -16,7 +16,7 @@ std::string uri::to_string() const return path_.string(); } -path uri::path() const +const path& uri::path() const { return path_; } diff --git a/xlnt/source/styles/alignment.cpp b/xlnt/source/styles/alignment.cpp index 5a155ec..b6ff999 100644 --- a/xlnt/source/styles/alignment.cpp +++ b/xlnt/source/styles/alignment.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/xlnt/source/styles/border.cpp b/xlnt/source/styles/border.cpp index 75d3c0d..b443d2c 100644 --- a/xlnt/source/styles/border.cpp +++ b/xlnt/source/styles/border.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/xlnt/source/styles/color.cpp b/xlnt/source/styles/color.cpp index f776c9d..141616f 100644 --- a/xlnt/source/styles/color.cpp +++ b/xlnt/source/styles/color.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -32,14 +32,14 @@ namespace { std::array decode_hex_string(const std::string &hex_string) { - auto x = std::strtoul(hex_string.c_str(), nullptr, 16); + auto x = std::strtoul(hex_string.c_str(), nullptr, 16); - auto a = static_cast(x >> 24); - auto r = static_cast((x >> 16) & 0xff); - auto g = static_cast((x >> 8) & 0xff); - auto b = static_cast(x & 0xff); + auto a = static_cast(x >> 24); + auto r = static_cast((x >> 16) & 0xff); + auto g = static_cast((x >> 8) & 0xff); + auto b = static_cast(x & 0xff); - return { { r, g, b, a } }; + return { { r, g, b, a } }; } } // namespace @@ -54,7 +54,12 @@ indexed_color::indexed_color(std::size_t index) : index_(index) std::size_t indexed_color::index() const { - return index_; + return index_; +} + +void indexed_color::index(std::size_t index) +{ + index_ = index; } // theme_color implementation @@ -65,14 +70,19 @@ theme_color::theme_color(std::size_t index) : index_(index) std::size_t theme_color::index() const { - return index_; + return index_; +} + +void theme_color::index(std::size_t index) +{ + index_ = index; } // rgb_color implementation std::string rgb_color::hex_string() const { - static const char *digits = "0123456789abcdef"; + static const char *digits = "0123456789ABCDEF"; std::string hex_string(8, '0'); auto out_iter = hex_string.begin(); @@ -100,84 +110,84 @@ rgb_color::rgb_color(std::uint8_t r, std::uint8_t g, std::uint8_t b, std::uint8_ std::uint8_t rgb_color::red() const { - return rgba_[0]; + return rgba_[0]; } std::uint8_t rgb_color::green() const { - return rgba_[1]; + return rgba_[1]; } std::uint8_t rgb_color::blue() const { - return rgba_[2]; + return rgba_[2]; } std::uint8_t rgb_color::alpha() const { - return rgba_[3]; + return rgba_[3]; } std::array rgb_color::rgb() const { - return {{red(), green(), blue()}}; + return {{red(), green(), blue()}}; } std::array rgb_color::rgba() const { - return rgba_; + return rgba_; } // color implementation const color color::black() { - return color(rgb_color("ff000000")); + return color(rgb_color("ff000000")); } const color color::white() { - return color(rgb_color("ffffffff")); + return color(rgb_color("ffffffff")); } const color color::red() { - return color(rgb_color("ffff0000")); + return color(rgb_color("ffff0000")); } const color color::darkred() { - return color(rgb_color("ff8b0000")); + return color(rgb_color("ff8b0000")); } const color color::blue() { - return color(rgb_color("ff0000ff")); + return color(rgb_color("ff0000ff")); } const color color::darkblue() { - return color(rgb_color("ff00008b")); + return color(rgb_color("ff00008b")); } const color color::green() { - return color(rgb_color("ff00ff00")); + return color(rgb_color("ff00ff00")); } const color color::darkgreen() { - return color(rgb_color("ff008b00")); + return color(rgb_color("ff008b00")); } const color color::yellow() { - return color(rgb_color("ffffff00")); + return color(rgb_color("ffffff00")); } const color color::darkyellow() { - return color(rgb_color("ffcccc00")); + return color(rgb_color("ffcccc00")); } color::color() : color(indexed_color(0)) @@ -185,57 +195,75 @@ color::color() : color(indexed_color(0)) } color::color(const rgb_color &rgb) - : type_(color_type::rgb), - rgb_(rgb), - indexed_(0), - theme_(0) + : type_(color_type::rgb), + rgb_(rgb), + indexed_(0), + theme_(0) { } color::color(const indexed_color &indexed) - : type_(color_type::indexed), - rgb_(rgb_color(0, 0, 0, 0)), - indexed_(indexed), - theme_(0) + : type_(color_type::indexed), + rgb_(rgb_color(0, 0, 0, 0)), + indexed_(indexed), + theme_(0) { } color::color(const theme_color &theme) - : type_(color_type::theme), - rgb_(rgb_color(0, 0, 0, 0)), - indexed_(0), - theme_(theme) + : type_(color_type::theme), + rgb_(rgb_color(0, 0, 0, 0)), + indexed_(0), + theme_(theme) { } color_type color::type() const { - return type_; + return type_; } bool color::auto_() const { - return auto__; + return auto__; } void color::auto_(bool value) { - auto__ = value; + auto__ = value; +} + +const indexed_color& color::indexed() const +{ + assert_type(color_type::indexed); + return indexed_; } -indexed_color color::indexed() const +indexed_color &color::indexed() { - assert_type(color_type::indexed); - return indexed_; + assert_type(color_type::indexed); + return indexed_; } -theme_color color::theme() const +const theme_color& color::theme() const { - assert_type(color_type::theme); - return theme_; + assert_type(color_type::theme); + return theme_; +} + +theme_color &color::theme() +{ + assert_type(color_type::theme); + return theme_; +} + +const rgb_color& color::rgb() const +{ + assert_type(color_type::rgb); + return rgb_; } -rgb_color color::rgb() const +rgb_color &color::rgb() { assert_type(color_type::rgb); return rgb_; @@ -248,7 +276,7 @@ void color::tint(double tint) double color::tint() const { - return tint_; + return tint_; } void color::assert_type(color_type t) const @@ -281,7 +309,7 @@ bool color::operator==(const xlnt::color &other) const bool color::operator!=(const color &other) const { - return !(*this == other); + return !(*this == other); } } // namespace xlnt diff --git a/xlnt/source/styles/conditional_format.cpp b/xlnt/source/styles/conditional_format.cpp index c958bcf..9a5852d 100644 --- a/xlnt/source/styles/conditional_format.cpp +++ b/xlnt/source/styles/conditional_format.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -81,6 +81,11 @@ bool conditional_format::operator!=(const conditional_format &other) const return !(*this == other); } +bool conditional_format::has_border() const +{ + return d_->border_id.is_set(); +} + xlnt::border conditional_format::border() const { return d_->parent->borders.at(d_->border_id.get()); @@ -92,6 +97,11 @@ conditional_format conditional_format::border(const xlnt::border &new_border) return *this; } +bool conditional_format::has_fill() const +{ + return d_->fill_id.is_set(); +} + xlnt::fill conditional_format::fill() const { return d_->parent->fills.at(d_->fill_id.get()); @@ -103,6 +113,11 @@ conditional_format conditional_format::fill(const xlnt::fill &new_fill) return *this; } +bool conditional_format::has_font() const +{ + return d_->font_id.is_set(); +} + xlnt::font conditional_format::font() const { return d_->parent->fonts.at(d_->font_id.get()); diff --git a/xlnt/source/styles/fill.cpp b/xlnt/source/styles/fill.cpp index 0ae4533..c3628ae 100644 --- a/xlnt/source/styles/fill.cpp +++ b/xlnt/source/styles/fill.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/xlnt/source/styles/font.cpp b/xlnt/source/styles/font.cpp index 4d30083..6bd3178 100644 --- a/xlnt/source/styles/font.cpp +++ b/xlnt/source/styles/font.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -26,11 +26,18 @@ #include +namespace { +const std::string &Default_Name() +{ + static const std::string Default("Calibri"); + return Default; +} +constexpr double Default_Size = 12.0; +} // namespace + namespace xlnt { font::font() - : name_("Calibri"), - size_(12.0) { } @@ -45,9 +52,9 @@ bool font::bold() const return bold_; } -font &font::superscript(bool superscript) +font &font::superscript(bool value) { - superscript_ = superscript; + superscript_ = value; return *this; } @@ -56,6 +63,17 @@ bool font::superscript() const return superscript_; } +font &font::subscript(bool value) +{ + subscript_ = value; + return *this; +} + +bool font::subscript() const +{ + return subscript_; +} + font &font::italic(bool italic) { italic_ = italic; @@ -129,7 +147,11 @@ font &font::size(double size) double font::size() const { - return size_.get(); + if (size_.is_set()) + { + return size_.get(); + } + return Default_Size; } bool font::has_name() const @@ -143,9 +165,13 @@ font &font::name(const std::string &name) return *this; } -std::string font::name() const +const std::string &font::name() const { - return name_.get(); + if (name_.is_set()) + { + return name_.get(); + } + return Default_Name(); } bool font::has_color() const @@ -181,6 +207,11 @@ font &font::charset(std::size_t charset) return *this; } +std::size_t font::charset() const +{ + return charset_.get(); +} + bool font::has_scheme() const { return scheme_.is_set(); @@ -202,86 +233,39 @@ std::size_t font::family() const return family_.get(); } -std::string font::scheme() const +const std::string &font::scheme() const { return scheme_.get(); } bool font::operator==(const font &other) const { - if (bold() != other.bold()) - { - return false; - } - - if (has_color() != other.has_color()) - { - return false; - } - - if (has_color()) - { - if (color() != other.color()) - { - return false; - } - } - - if (has_family() != other.has_family()) - { - return false; - } - - if (has_family()) - { - if (family() != other.family()) - { - return false; - } - } - - if (italic() != other.italic()) - { - return false; - } - - if (name() != other.name()) - { - return false; - } - - if (has_scheme() != other.has_scheme()) - { - return false; - } - - if (has_scheme()) - { - if (scheme() != other.scheme()) - { - return false; - } - } - - if (std::fabs(size() - other.size()) != 0.0) - { - return false; - } - - if (strikethrough() != other.strikethrough()) - { - return false; - } - - if (superscript() != other.superscript()) - { - return false; - } - - if (underline() != other.underline()) - { - return false; - } + // name + if (has_name() != other.has_name()) return false; + if (has_name() && name() != other.name()) return false; + // size + if (has_size() != other.has_size()) return false; + if (has_size() && std::fabs(size() - other.size()) != 0.0) return false; + // family + if (has_family() != other.has_family()) return false; + if (has_family() && family() != other.family()) return false; + // scheme + if (has_scheme() != other.has_scheme()) return false; + if (has_scheme() && scheme() != other.scheme()) return false; + // color + if (has_color() != other.has_color()) return false; + if (has_color() && color() != other.color()) return false; + // charset + if (has_charset() != other.has_charset()) return false; + if (has_charset() && charset() != other.charset()) return false; + // modifiers + if (bold() != other.bold()) return false; + if (italic() != other.italic()) return false; + if (strikethrough() != other.strikethrough()) return false; + if (superscript() != other.superscript()) return false; + if (subscript() != other.subscript()) return false; + if (underline() != other.underline()) return false; + if (shadow() != other.shadow()) return false; return true; } diff --git a/xlnt/source/styles/format.cpp b/xlnt/source/styles/format.cpp index bcd93a6..0074594 100644 --- a/xlnt/source/styles/format.cpp +++ b/xlnt/source/styles/format.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -81,7 +81,7 @@ xlnt::alignment format::alignment() const return d_->parent->alignments.at(d_->alignment_id.get()); } -format format::alignment(const xlnt::alignment &new_alignment, bool applied) +format format::alignment(const xlnt::alignment &new_alignment, optional applied) { d_ = d_->parent->find_or_create_with(d_, new_alignment, applied); return format(d_); @@ -92,7 +92,7 @@ xlnt::border format::border() const return d_->parent->borders.at(d_->border_id.get()); } -format format::border(const xlnt::border &new_border, bool applied) +format format::border(const xlnt::border &new_border, optional applied) { d_ = d_->parent->find_or_create_with(d_, new_border, applied); return format(d_); @@ -103,7 +103,7 @@ xlnt::fill format::fill() const return d_->parent->fills.at(d_->fill_id.get()); } -format format::fill(const xlnt::fill &new_fill, bool applied) +format format::fill(const xlnt::fill &new_fill, optional applied) { d_ = d_->parent->find_or_create_with(d_, new_fill, applied); return format(d_); @@ -114,7 +114,7 @@ xlnt::font format::font() const return d_->parent->fonts.at(d_->font_id.get()); } -format format::font(const xlnt::font &new_font, bool applied) +format format::font(const xlnt::font &new_font, optional applied) { d_ = d_->parent->find_or_create_with(d_, new_font, applied); return format(d_); @@ -131,7 +131,7 @@ xlnt::number_format format::number_format() const [&](const xlnt::number_format nf) { return nf.id() == d_->number_format_id.get(); }); } -format format::number_format(const xlnt::number_format &new_number_format, bool applied) +format format::number_format(const xlnt::number_format &new_number_format, optional applied) { auto copy = new_number_format; @@ -150,7 +150,7 @@ xlnt::protection format::protection() const return d_->parent->protections.at(d_->protection_id.get()); } -format format::protection(const xlnt::protection &new_protection, bool applied) +format format::protection(const xlnt::protection &new_protection, optional applied) { d_ = d_->parent->find_or_create_with(d_, new_protection, applied); return format(d_); @@ -158,32 +158,44 @@ format format::protection(const xlnt::protection &new_protection, bool applied) bool format::alignment_applied() const { - return d_->alignment_applied; + return d_->alignment_applied.is_set() + ? d_->alignment_applied.get() + : d_->alignment_id.is_set(); } bool format::border_applied() const { - return d_->border_applied; + return d_->border_applied.is_set() + ? d_->border_applied.get() + : d_->border_id.is_set(); } bool format::fill_applied() const { - return d_->fill_applied; + return d_->fill_applied.is_set() + ? d_->fill_applied.get() + : d_->fill_id.is_set(); } bool format::font_applied() const { - return d_->font_applied; + return d_->font_applied.is_set() + ? d_->font_applied.get() + : d_->font_id.is_set(); } bool format::number_format_applied() const { - return d_->number_format_applied; + return d_->number_format_applied.is_set() + ? d_->number_format_applied.get() + : d_->number_format_id.is_set(); } bool format::protection_applied() const { - return d_->protection_applied; + return d_->protection_applied.is_set() + ? d_->protection_applied.get() + : d_->protection_id.is_set(); } bool format::pivot_button() const diff --git a/xlnt/source/styles/number_format.cpp b/xlnt/source/styles/number_format.cpp index 270a687..e9e4740 100644 --- a/xlnt/source/styles/number_format.cpp +++ b/xlnt/source/styles/number_format.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/xlnt/source/styles/protection.cpp b/xlnt/source/styles/protection.cpp index f626e85..3d1fd32 100644 --- a/xlnt/source/styles/protection.cpp +++ b/xlnt/source/styles/protection.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -26,6 +26,26 @@ namespace xlnt { +protection protection::unlocked_and_visible() +{ + return protection(); +} + +protection protection::locked_and_visible() +{ + return protection().locked(true); +} + +protection protection::unlocked_and_hidden() +{ + return protection().hidden(true); +} + +protection protection::locked_and_hidden() +{ + return protection().locked(true).hidden(true); +} + protection::protection() : locked_(false), hidden_(false) { diff --git a/xlnt/source/styles/style.cpp b/xlnt/source/styles/style.cpp index 2ac46ac..4dd5418 100644 --- a/xlnt/source/styles/style.cpp +++ b/xlnt/source/styles/style.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -66,6 +66,11 @@ std::size_t style::builtin_id() const return d_->builtin_id.get(); } +bool style::builtin() const +{ + return d_->builtin_id.is_set(); +} + std::string style::name() const { return d_->name; @@ -87,17 +92,17 @@ bool style::operator==(const style &other) const return name() == other.name(); } -xlnt::alignment style::alignment() const +bool style::operator!=(const style &other) const { - return d_->parent->alignments.at(d_->alignment_id.get()); + return !operator==(other); } -bool style::alignment_applied() const +xlnt::alignment style::alignment() const { - return d_->alignment_applied; + return d_->parent->alignments.at(d_->alignment_id.get()); } -style style::alignment(const xlnt::alignment &new_alignment, bool applied) +style style::alignment(const xlnt::alignment &new_alignment, optional applied) { d_->alignment_id = d_->parent->find_or_add(d_->parent->alignments, new_alignment); d_->alignment_applied = applied; @@ -110,12 +115,7 @@ xlnt::border style::border() const return d_->parent->borders.at(d_->border_id.get()); } -bool style::border_applied() const -{ - return d_->border_applied; -} - -style style::border(const xlnt::border &new_border, bool applied) +style style::border(const xlnt::border &new_border, optional applied) { d_->border_id = d_->parent->find_or_add(d_->parent->borders, new_border); d_->border_applied = applied; @@ -128,12 +128,7 @@ xlnt::fill style::fill() const return d_->parent->fills.at(d_->fill_id.get()); } -bool style::fill_applied() const -{ - return d_->fill_applied; -} - -style style::fill(const xlnt::fill &new_fill, bool applied) +style style::fill(const xlnt::fill &new_fill, optional applied) { d_->fill_id = d_->parent->find_or_add(d_->parent->fills, new_fill); d_->fill_applied = applied; @@ -146,12 +141,7 @@ xlnt::font style::font() const return d_->parent->fonts.at(d_->font_id.get()); } -bool style::font_applied() const -{ - return d_->font_applied; -} - -style style::font(const xlnt::font &new_font, bool applied) +style style::font(const xlnt::font &new_font, optional applied) { d_->font_id = d_->parent->find_or_add(d_->parent->fonts, new_font); d_->font_applied = applied; @@ -172,12 +162,7 @@ xlnt::number_format style::number_format() const return *match; } -bool style::number_format_applied() const -{ - return d_->number_format_applied; -} - -style style::number_format(const xlnt::number_format &new_number_format, bool applied) +style style::number_format(const xlnt::number_format &new_number_format, optional applied) { auto copy = new_number_format; @@ -203,12 +188,7 @@ xlnt::protection style::protection() const return d_->parent->protections.at(d_->protection_id.get()); } -bool style::protection_applied() const -{ - return d_->protection_applied; -} - -style style::protection(const xlnt::protection &new_protection, bool applied) +style style::protection(const xlnt::protection &new_protection, optional applied) { d_->protection_id = d_->parent->find_or_add(d_->parent->protections, new_protection); d_->protection_applied = applied; @@ -216,6 +196,48 @@ style style::protection(const xlnt::protection &new_protection, bool applied) return *this; } +bool style::alignment_applied() const +{ + return d_->alignment_applied.is_set() + ? d_->alignment_applied.get() + : d_->alignment_id.is_set(); +} + +bool style::border_applied() const +{ + return d_->border_applied.is_set() + ? d_->border_applied.get() + : d_->border_id.is_set(); +} + +bool style::fill_applied() const +{ + return d_->fill_applied.is_set() + ? d_->fill_applied.get() + : d_->fill_id.is_set(); +} + +bool style::font_applied() const +{ + return d_->font_applied.is_set() + ? d_->font_applied.get() + : d_->font_id.is_set(); +} + +bool style::number_format_applied() const +{ + return d_->number_format_applied.is_set() + ? d_->number_format_applied.get() + : d_->number_format_id.is_set(); +} + +bool style::protection_applied() const +{ + return d_->protection_applied.is_set() + ? d_->protection_applied.get() + : d_->protection_id.is_set(); +} + bool style::pivot_button() const { return d_->pivot_button_; diff --git a/xlnt/source/utils/date.cpp b/xlnt/source/utils/date.cpp index 80dfccd..df98a35 100644 --- a/xlnt/source/utils/date.cpp +++ b/xlnt/source/utils/date.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/source/utils/datetime.cpp b/xlnt/source/utils/datetime.cpp index c4955f4..2f15184 100644 --- a/xlnt/source/utils/datetime.cpp +++ b/xlnt/source/utils/datetime.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/source/utils/exceptions.cpp b/xlnt/source/utils/exceptions.cpp index 6cb7a86..78d3db9 100644 --- a/xlnt/source/utils/exceptions.cpp +++ b/xlnt/source/utils/exceptions.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/xlnt/source/utils/path.cpp b/xlnt/source/utils/path.cpp index 2d22888..738d71f 100644 --- a/xlnt/source/utils/path.cpp +++ b/xlnt/source/utils/path.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -23,6 +23,8 @@ #include #include +#include +#include #include #ifdef __APPLE__ @@ -137,8 +139,24 @@ path::path() } path::path(const std::string &path_string) +{ + std::remove_copy(path_string.begin(), path_string.end(), std::back_inserter(internal_), '\"'); +} + +path::path(const std::string &path_string, char sep) : internal_(path_string) { + char curr_sep = guess_separator(); + if (curr_sep != sep) + { + for (char& c : internal_) // simple find and replace + { + if (c == curr_sep) + { + c = sep; + } + } + } } // general attributes @@ -205,7 +223,7 @@ std::pair path::split_extension() const // conversion -std::string path::string() const +const std::string& path::string() const { return internal_; } @@ -331,4 +349,9 @@ bool path::operator==(const path &other) const return internal_ == other.internal_; } +bool path::operator!=(const path &other) const +{ + return !operator==(other); +} + } // namespace xlnt diff --git a/xlnt/source/utils/time.cpp b/xlnt/source/utils/time.cpp index 147e41f..4211459 100644 --- a/xlnt/source/utils/time.cpp +++ b/xlnt/source/utils/time.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/source/utils/timedelta.cpp b/xlnt/source/utils/timedelta.cpp index 88391a0..c8fc628 100644 --- a/xlnt/source/utils/timedelta.cpp +++ b/xlnt/source/utils/timedelta.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/source/utils/variant.cpp b/xlnt/source/utils/variant.cpp index 79873ca..4c420ee 100644 --- a/xlnt/source/utils/variant.cpp +++ b/xlnt/source/utils/variant.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017 Thomas Fussell +// Copyright (c) 2017-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -27,9 +27,8 @@ namespace xlnt { variant::variant() -{ - -} + : type_(type::null) +{} variant::variant(const std::string &value) : type_(type::lpstr), @@ -41,7 +40,7 @@ variant::variant(const char *value) : variant(std::string(value)) { } -variant::variant(int value) +variant::variant(int32_t value) : type_(type::i4), i4_value_(value) { @@ -125,6 +124,28 @@ variant::variant(const std::vector &value) } } +bool variant::operator==(const variant &rhs) const +{ + if (type_ != rhs.type_) + { + return false; + } + switch (type_) + { + case type::vector: + return vector_value_ == rhs.vector_value_; + case type::i4: + case type::boolean: + return i4_value_ == rhs.i4_value_; + case type::date: + case type::lpstr: + return lpstr_value_ == rhs.lpstr_value_; + case type::null: + return true; + } + return false; +} + bool variant::is(type t) const { return type_ == t; diff --git a/xlnt/source/workbook/named_range.cpp b/xlnt/source/workbook/named_range.cpp index cdcf702..9bf1973 100644 --- a/xlnt/source/workbook/named_range.cpp +++ b/xlnt/source/workbook/named_range.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -123,4 +123,10 @@ named_range &named_range::operator=(const named_range &other) return *this; } +bool named_range::operator==(const named_range &rhs) const +{ + return name_ == rhs.name_ + && targets_ == rhs.targets_; +} + } // namespace xlnt diff --git a/xlnt/source/workbook/streaming_workbook_reader.cpp b/xlnt/source/workbook/streaming_workbook_reader.cpp index 1b9dec3..b5f22e9 100644 --- a/xlnt/source/workbook/streaming_workbook_reader.cpp +++ b/xlnt/source/workbook/streaming_workbook_reader.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017 Thomas Fussell +// Copyright (c) 2017-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/source/workbook/streaming_workbook_writer.cpp b/xlnt/source/workbook/streaming_workbook_writer.cpp index eb53487..e1a7d6b 100644 --- a/xlnt/source/workbook/streaming_workbook_writer.cpp +++ b/xlnt/source/workbook/streaming_workbook_writer.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017 Thomas Fussell +// Copyright (c) 2017-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal diff --git a/xlnt/source/workbook/workbook.cpp b/xlnt/source/workbook/workbook.cpp index adb97d5..fcea0b1 100644 --- a/xlnt/source/workbook/workbook.cpp +++ b/xlnt/source/workbook/workbook.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -28,16 +28,6 @@ #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include #include #include @@ -61,12 +51,22 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include namespace { using xlnt::detail::open_stream; -template +template std::vector keys(const std::vector> &container) { auto result = std::vector(); @@ -80,7 +80,7 @@ std::vector keys(const std::vector> &container) return result; } -template +template bool contains(const std::vector> &container, const T key) { for (const auto &iter : container) @@ -374,6 +374,16 @@ variant workbook::custom_property(const std::string &property_name) const throw xlnt::exception("workbook doesn't have custom property"); } +void workbook::abs_path(const std::string &path) +{ + d_->abs_path_ = path; +} + +void workbook::arch_id_flags(const std::size_t flags) +{ + d_->arch_id_flags_ = flags; +} + workbook workbook::empty() { auto impl = new detail::workbook_impl(); @@ -408,6 +418,7 @@ workbook workbook::empty() wb.d_->file_version_ = file_version; xlnt::workbook_view wb_view; + wb_view.active_tab = 0; wb_view.x_window = 0; wb_view.y_window = 460; wb_view.window_width = 28800; @@ -429,6 +440,10 @@ workbook workbook::empty() sheet_view view; ws.add_view(view); + auto &format_properties = ws.d_->format_properties_; + format_properties.base_col_width = 10.0; + format_properties.default_row_height = 16.0; + wb.theme(xlnt::theme()); wb.d_->stylesheet_ = detail::stylesheet(); @@ -436,35 +451,39 @@ workbook workbook::empty() stylesheet.parent = &wb; auto default_border = border() - .side(border_side::bottom, border::border_property()) - .side(border_side::top, border::border_property()) - .side(border_side::start, border::border_property()) - .side(border_side::end, border::border_property()) - .side(border_side::diagonal, border::border_property()); + .side(border_side::bottom, border::border_property()) + .side(border_side::top, border::border_property()) + .side(border_side::start, border::border_property()) + .side(border_side::end, border::border_property()) + .side(border_side::diagonal, border::border_property()); wb.d_->stylesheet_.get().borders.push_back(default_border); auto default_fill = fill(pattern_fill() - .type(pattern_fill_type::none)); + .type(pattern_fill_type::none)); stylesheet.fills.push_back(default_fill); auto gray125_fill = pattern_fill() - .type(pattern_fill_type::gray125); + .type(pattern_fill_type::gray125); stylesheet.fills.push_back(gray125_fill); auto default_font = font() - .name("Calibri") - .size(12) - .scheme("minor") - .family(2) - .color(theme_color(1)); + .name("Calibri") + .size(12) + .scheme("minor") + .family(2) + .color(theme_color(1)); stylesheet.fonts.push_back(default_font); - wb.create_builtin_style(0); + wb.create_builtin_style(0) + .border(default_border) + .fill(default_fill) + .font(default_font) + .number_format(xlnt::number_format::general()); wb.create_format(true) - .border(default_border, false) - .fill(default_fill, false) - .font(default_font, false) - .number_format(xlnt::number_format::general(), false) + .border(default_border) + .fill(default_fill) + .font(default_font) + .number_format(xlnt::number_format::general()) .style("Normal"); xlnt::calculation_properties calc_props; @@ -481,6 +500,30 @@ workbook::workbook() swap(wb_template); } +workbook::workbook(const xlnt::path &file) +{ + *this = empty(); + load(file); +} + +workbook::workbook(const xlnt::path &file, const std::string &password) +{ + *this = empty(); + load(file, password); +} + +workbook::workbook(std::istream &data) +{ + *this = empty(); + load(data); +} + +workbook::workbook(std::istream &data, const std::string &password) +{ + *this = empty(); + load(data, password); +} + workbook::workbook(detail::workbook_impl *impl) : d_(impl) { @@ -507,7 +550,7 @@ void workbook::register_package_part(relationship_type type) void workbook::register_workbook_part(relationship_type type) { auto wb_rel = manifest().relationship(path("/"), relationship_type::office_document); - auto wb_path = manifest().canonicalize({ wb_rel }); + auto wb_path = manifest().canonicalize({wb_rel}); if (!manifest().has_relationship(wb_path, type)) { @@ -695,22 +738,35 @@ worksheet workbook::create_sheet() std::string title = "Sheet1"; int index = 1; + // make a unique sheet name. Sheet<1...n> while (contains(title)) { title = "Sheet" + std::to_string(++index); } - + // unique sheet id size_t sheet_id = 1; for (const auto ws : *this) { sheet_id = std::max(sheet_id, ws.id() + 1); } - std::string sheet_filename = "sheet" + std::to_string(sheet_id) + ".xml"; - d_->worksheets_.push_back(detail::worksheet_impl(this, sheet_id, title)); - + // unique sheet file name auto workbook_rel = d_->manifest_.relationship(path("/"), relationship_type::office_document); - uri relative_sheet_uri(path("worksheets").append(sheet_filename).string()); + auto workbook_files = d_->manifest_.relationships(workbook_rel.target().path()); + auto rel_vec_contains = [&workbook_files](const xlnt::path &new_file_id) { + return workbook_files.end() != std::find_if(workbook_files.begin(), workbook_files.end(), [&new_file_id](const xlnt::relationship &rel) { + return rel.target().path() == new_file_id; + }); + }; + + size_t file_id = sheet_id; + xlnt::path sheet_relative_path; + do + { + sheet_relative_path = path("worksheets").append("sheet" + std::to_string(file_id++) + ".xml"); + } while (rel_vec_contains(sheet_relative_path)); + + uri relative_sheet_uri(sheet_relative_path.string()); auto absolute_sheet_path = path("/xl").append(relative_sheet_uri.path()); d_->manifest_.register_override_type( absolute_sheet_path, "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"); @@ -719,6 +775,7 @@ worksheet workbook::create_sheet() d_->sheet_title_rel_id_map_[title] = ws_rel; update_sheet_properties(); + reorder_relationships(); return worksheet(&d_->worksheets_.back()); } @@ -960,8 +1017,8 @@ void workbook::load(const std::wstring &filename, const std::string &password) void workbook::remove_sheet(worksheet ws) { - auto match_iter = std::find_if( - d_->worksheets_.begin(), d_->worksheets_.end(), [=](detail::worksheet_impl &comp) { return &comp == ws.d_; }); + auto match_iter = std::find_if(d_->worksheets_.begin(), d_->worksheets_.end(), + [=](detail::worksheet_impl &comp) { return &comp == ws.d_; }); if (match_iter == d_->worksheets_.end()) { @@ -981,7 +1038,8 @@ void workbook::remove_sheet(worksheet ws) for (auto &title_rel_id_pair : d_->sheet_title_rel_id_map_) { title_rel_id_pair.second = rel_id_map.count(title_rel_id_pair.second) > 0 - ? rel_id_map[title_rel_id_pair.second] : title_rel_id_pair.second; + ? rel_id_map[title_rel_id_pair.second] + : title_rel_id_pair.second; } update_sheet_properties(); @@ -1013,10 +1071,10 @@ worksheet workbook::create_sheet_with_rel(const std::string &title, const relati auto workbook_rel = d_->manifest_.relationship(path("/"), relationship_type::office_document); auto sheet_absoulute_path = workbook_rel.target().path().parent().append(rel.target().path()); - d_->manifest_.register_override_type( - sheet_absoulute_path, "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"); - auto ws_rel = d_->manifest_.register_relationship( - workbook_rel.target(), relationship_type::worksheet, rel.target(), target_mode::internal); + d_->manifest_.register_override_type(sheet_absoulute_path, + "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"); + auto ws_rel = d_->manifest_.register_relationship(workbook_rel.target(), + relationship_type::worksheet, rel.target(), target_mode::internal); d_->sheet_title_rel_id_map_[title] = ws_rel; update_sheet_properties(); @@ -1089,12 +1147,12 @@ void workbook::clear() bool workbook::operator==(const workbook &rhs) const { - return d_.get() == rhs.d_.get(); + return *d_ == *rhs.d_; } bool workbook::operator!=(const workbook &rhs) const { - return d_.get() != rhs.d_.get(); + return !operator==(rhs); } void workbook::swap(workbook &right) @@ -1158,9 +1216,7 @@ workbook::workbook(const workbook &other) d_->stylesheet_.get().parent = this; } -workbook::~workbook() -{ -} +workbook::~workbook() = default; bool workbook::has_theme() const { @@ -1209,6 +1265,31 @@ void workbook::clear_styles() apply_to_cells([](cell c) { c.clear_style(); }); } +void workbook::default_slicer_style(const std::string &value) +{ + d_->stylesheet_.get().default_slicer_style = value; +} + +std::string workbook::default_slicer_style() const +{ + return d_->stylesheet_.get().default_slicer_style.get(); +} + +void workbook::enable_known_fonts() +{ + d_->stylesheet_.get().known_fonts_enabled = true; +} + +void workbook::disable_known_fonts() +{ + d_->stylesheet_.get().known_fonts_enabled = false; +} + +bool workbook::known_fonts_enabled() const +{ + return d_->stylesheet_.get().known_fonts_enabled; +} + void workbook::clear_formats() { apply_to_cells([](cell c) { c.clear_format(); }); @@ -1251,39 +1332,53 @@ const manifest &workbook::manifest() const return d_->manifest_; } -std::vector &workbook::shared_strings() +const std::map &workbook::shared_strings_by_id() const { - return d_->shared_strings_; + return d_->shared_strings_values_; } -const std::vector &workbook::shared_strings() const +const rich_text& workbook::shared_strings(std::size_t index) const { - return d_->shared_strings_; + auto it = d_->shared_strings_values_.find(index); + + if (it != d_->shared_strings_values_.end()) + { + return it->second; + } + + static rich_text empty; + return empty; +} + +std::unordered_map &workbook::shared_strings() +{ + return d_->shared_strings_ids_; +} + +const std::unordered_map &workbook::shared_strings() const +{ + return d_->shared_strings_ids_; } std::size_t workbook::add_shared_string(const rich_text &shared, bool allow_duplicates) { register_workbook_part(relationship_type::shared_string_table); - auto index = std::size_t(0); - if (!allow_duplicates) { - // TODO: inefficient, use a set or something? - for (auto &s : d_->shared_strings_) - { - if (s == shared) - { - return index; - } + auto it = d_->shared_strings_ids_.find(shared); - ++index; + if (it != d_->shared_strings_ids_.end()) + { + return it->second; } } - d_->shared_strings_.push_back(shared); + auto sz = d_->shared_strings_ids_.size(); + d_->shared_strings_ids_[shared] = sz; + d_->shared_strings_values_[sz] = shared; - return index; + return sz; } bool workbook::contains(const std::string &sheet_title) const @@ -1323,7 +1418,7 @@ style workbook::create_style(const std::string &name) style workbook::create_builtin_style(const std::size_t builtin_id) { - return d_->stylesheet_.get().create_builtin_style(builtin_id); + return d_->stylesheet_.get().create_builtin_style(builtin_id); } style workbook::style(const std::string &name) @@ -1496,4 +1591,84 @@ void workbook::update_sheet_properties() } } +namespace { +// true if a sheet index is != worksheet relationship index +bool needs_reorder(const std::unordered_map &title_to_rels, + const std::vector &titles, + std::vector &relation_ids) +{ + bool all_match = true; + for (std::size_t title_index = 0; title_index < titles.size(); ++title_index) + { + const auto &rel = title_to_rels.at(titles[title_index]); + relation_ids.push_back(rel); + const auto expected_rel_id = "rId" + std::to_string(title_index + 1); + if (rel != expected_rel_id) + { + all_match = false; + } + } + return !all_match; // if all are as expected, reorder not required +}; + +struct rel_id_sorter +{ + // true if lhs < rhs + bool operator()(const xlnt::relationship &lhs, const xlnt::relationship &rhs) + { + // format is rTd + if (lhs.id().size() < rhs.id().size()) // a number with more digits will be larger + { + return true; + } + return lhs.id() < rhs.id(); + } +}; +} // namespace + +void workbook::reorder_relationships() +{ + const auto titles = sheet_titles(); + // the relation ID corresponding to the title at the same index is copied into here + std::vector worksheet_rel_ids; + worksheet_rel_ids.reserve(titles.size()); + if (!needs_reorder(d_->sheet_title_rel_id_map_, titles, worksheet_rel_ids)) + { + return; + } + // copy of existing relations + const auto wb_rel_target = manifest().relationship(path("/"), relationship_type::office_document).target(); + auto rel_copy = manifest().relationships(wb_rel_target.path()); + std::sort(rel_copy.begin(), rel_copy.end(), rel_id_sorter{}); + // clear existing relations + for (const auto &rel : rel_copy) + { + manifest().unregister_relationship(wb_rel_target, rel.id()); + } + // create new relations + std::size_t index = 0; + auto new_id = [&index]() { return "rId" + std::to_string(++index); }; // ids start from 1 + // worksheets first + while (index < worksheet_rel_ids.size()) + { + auto rel_it = std::find_if(rel_copy.begin(), rel_copy.end(), + [&](const relationship &rel) { return rel.id() == worksheet_rel_ids[index]; }); + + std::string rel_id = new_id(); + d_->sheet_title_rel_id_map_.at(titles[index - 1]) = rel_id; // update title -> relation mapping + manifest().register_relationship(relationship(rel_id, rel_it->type(), + rel_it->source(), rel_it->target(), rel_it->target_mode())); + } + // then all the other relations in the same order they started (just new indices) + for (const auto &old_rel : rel_copy) + { + if (old_rel.type() == relationship_type::worksheet) + { + continue; + } + manifest().register_relationship(relationship(new_id(), old_rel.type(), + old_rel.source(), old_rel.target(), old_rel.target_mode())); + } +} + } // namespace xlnt diff --git a/xlnt/source/workbook/worksheet_iterator.cpp b/xlnt/source/workbook/worksheet_iterator.cpp index 424e1a4..b1432e3 100644 --- a/xlnt/source/workbook/worksheet_iterator.cpp +++ b/xlnt/source/workbook/worksheet_iterator.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -28,18 +28,18 @@ namespace xlnt { worksheet_iterator::worksheet_iterator(workbook &wb, std::size_t index) - : wb_(wb), index_(index) + : wb_(&wb), index_(index) { } -worksheet_iterator::worksheet_iterator(const worksheet_iterator &rhs) - : wb_(rhs.wb_), index_(rhs.index_) +worksheet_iterator::reference worksheet_iterator::operator*() { + return (*wb_)[index_]; } -worksheet worksheet_iterator::operator*() +const worksheet_iterator::reference worksheet_iterator::operator*() const { - return wb_[index_]; + return (*wb_)[index_]; } worksheet_iterator &worksheet_iterator::operator++() @@ -50,40 +50,42 @@ worksheet_iterator &worksheet_iterator::operator++() worksheet_iterator worksheet_iterator::operator++(int) { - worksheet_iterator old(wb_, index_); + worksheet_iterator old(*wb_, index_); ++*this; return old; } -bool worksheet_iterator::operator==(const worksheet_iterator &comparand) const +worksheet_iterator &worksheet_iterator::operator--() { - return index_ == comparand.index_ && wb_ == comparand.wb_; + --index_; + return *this; } -bool worksheet_iterator::operator!=(const worksheet_iterator &comparand) const +worksheet_iterator worksheet_iterator::operator--(int) { - return !(*this == comparand); + worksheet_iterator old(*wb_, index_); + --(*this); + return old; } -worksheet_iterator &worksheet_iterator::operator=(const worksheet_iterator &other) +bool worksheet_iterator::operator==(const worksheet_iterator &comparand) const { - index_ = other.index_; - return *this; + return index_ == comparand.index_ && wb_ == comparand.wb_; } -const_worksheet_iterator::const_worksheet_iterator(const workbook &wb, std::size_t index) - : wb_(wb), index_(index) +bool worksheet_iterator::operator!=(const worksheet_iterator &comparand) const { + return !(*this == comparand); } -const_worksheet_iterator::const_worksheet_iterator(const const_worksheet_iterator &rhs) - : wb_(rhs.wb_), index_(rhs.index_) +const_worksheet_iterator::const_worksheet_iterator(const workbook &wb, std::size_t index) + : wb_(&wb), index_(index) { } -const worksheet const_worksheet_iterator::operator*() +const const_worksheet_iterator::reference const_worksheet_iterator::operator*() const { - return wb_.sheet_by_index(index_); + return wb_->sheet_by_index(index_); } const_worksheet_iterator &const_worksheet_iterator::operator++() @@ -94,11 +96,24 @@ const_worksheet_iterator &const_worksheet_iterator::operator++() const_worksheet_iterator const_worksheet_iterator::operator++(int) { - const_worksheet_iterator old(wb_, index_); + const_worksheet_iterator old(*wb_, index_); ++*this; return old; } +const_worksheet_iterator &const_worksheet_iterator::operator--() +{ + --index_; + return *this; +} + +const_worksheet_iterator const_worksheet_iterator::operator--(int) +{ + const_worksheet_iterator old(*wb_, index_); + --(*this); + return old; +} + bool const_worksheet_iterator::operator==(const const_worksheet_iterator &comparand) const { return index_ == comparand.index_ && wb_ == comparand.wb_; diff --git a/xlnt/source/worksheet/cell_iterator.cpp b/xlnt/source/worksheet/cell_iterator.cpp index 59ee0e3..e4c334e 100644 --- a/xlnt/source/worksheet/cell_iterator.cpp +++ b/xlnt/source/worksheet/cell_iterator.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -58,16 +58,6 @@ const_cell_iterator::const_cell_iterator(worksheet ws, const cell_reference &cur } } -cell_iterator::cell_iterator(const cell_iterator &other) -{ - *this = other; -} - -const_cell_iterator::const_cell_iterator(const const_cell_iterator &other) -{ - *this = other; -} - bool cell_iterator::operator==(const cell_iterator &other) const { return ws_ == other.ws_ @@ -275,14 +265,18 @@ const_cell_iterator const_cell_iterator::operator++(int) return old; } -cell cell_iterator::operator*() +cell_iterator::reference cell_iterator::operator*() { return ws_.cell(cursor_); } -const cell const_cell_iterator::operator*() const +const cell_iterator::reference cell_iterator::operator*() const { return ws_.cell(cursor_); } +const const_cell_iterator::reference const_cell_iterator::operator*() const +{ + return ws_.cell(cursor_); +} } // namespace xlnt diff --git a/xlnt/source/worksheet/cell_vector.cpp b/xlnt/source/worksheet/cell_vector.cpp index 5260c47..6c08de9 100644 --- a/xlnt/source/worksheet/cell_vector.cpp +++ b/xlnt/source/worksheet/cell_vector.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -48,7 +48,7 @@ cell_vector::iterator cell_vector::begin() cell_vector::iterator cell_vector::end() { auto past_end = cursor_; - + if (order_ == major_order::row) { past_end.column_index(bounds_.bottom_right().column_index() + 1); @@ -69,7 +69,7 @@ cell_vector::const_iterator cell_vector::cbegin() const cell_vector::const_iterator cell_vector::cend() const { auto past_end = cursor_; - + if (order_ == major_order::row) { past_end.column_index(bounds_.bottom_right().column_index() + 1); diff --git a/xlnt/source/worksheet/header_footer.cpp b/xlnt/source/worksheet/header_footer.cpp index 47e0594..b852da6 100644 --- a/xlnt/source/worksheet/header_footer.cpp +++ b/xlnt/source/worksheet/header_footer.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -295,4 +295,17 @@ rich_text header_footer::even_footer(location where) const return even_footers_.at(where); } +bool header_footer::operator==(const header_footer &rhs) const +{ + return align_with_margins_ == rhs.align_with_margins_ + && different_odd_even_ == rhs.different_odd_even_ + && scale_with_doc_ == rhs.scale_with_doc_ + && odd_headers_ == rhs.odd_headers_ + && even_headers_ == rhs.even_headers_ + && first_headers_ == rhs.first_headers_ + && odd_footers_ == rhs.odd_footers_ + && even_footers_ == rhs.even_footers_ + && first_footers_ == rhs.first_footers_; +} + } // namespace xlnt diff --git a/xlnt/source/worksheet/page_margins.cpp b/xlnt/source/worksheet/page_margins.cpp index ae5509b..77faec5 100644 --- a/xlnt/source/worksheet/page_margins.cpp +++ b/xlnt/source/worksheet/page_margins.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -89,4 +89,13 @@ void page_margins::footer(double footer) footer_ = footer; } +bool page_margins::operator==(const page_margins &rhs) const +{ + return top_ == rhs.top_ + && left_ == rhs.left_ + && right_ == rhs.right_ + && header_ == rhs.header_ + && footer_ == rhs.footer_; +} + } // namespace xlnt diff --git a/xlnt/source/worksheet/page_setup.cpp b/xlnt/source/worksheet/page_setup.cpp index 1004cd1..c832d5f 100644 --- a/xlnt/source/worksheet/page_setup.cpp +++ b/xlnt/source/worksheet/page_setup.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -29,12 +29,9 @@ page_setup::page_setup() : break_(xlnt::page_break::none), sheet_state_(xlnt::sheet_state::visible), paper_size_(xlnt::paper_size::letter), - orientation_(xlnt::orientation::portrait), fit_to_page_(false), fit_to_height_(false), fit_to_width_(false), - horizontal_centered_(false), - vertical_centered_(false), scale_(1) { } @@ -69,16 +66,6 @@ void page_setup::paper_size(xlnt::paper_size paper_size) paper_size_ = paper_size; } -orientation page_setup::orientation() const -{ - return orientation_; -} - -void page_setup::orientation(xlnt::orientation orientation) -{ - orientation_ = orientation; -} - bool page_setup::fit_to_page() const { return fit_to_page_; @@ -109,26 +96,6 @@ void page_setup::fit_to_width(bool fit_to_width) fit_to_width_ = fit_to_width; } -void page_setup::horizontal_centered(bool horizontal_centered) -{ - horizontal_centered_ = horizontal_centered; -} - -bool page_setup::horizontal_centered() const -{ - return horizontal_centered_; -} - -void page_setup::vertical_centered(bool vertical_centered) -{ - vertical_centered_ = vertical_centered; -} - -bool page_setup::vertical_centered() const -{ - return vertical_centered_; -} - void page_setup::scale(double scale) { scale_ = scale; @@ -139,4 +106,15 @@ double page_setup::scale() const return scale_; } +bool page_setup::operator==(const page_setup &rhs) const +{ + return break_ == rhs.break_ + && sheet_state_ == rhs.sheet_state_ + && paper_size_ == rhs.paper_size_ + && fit_to_page_ == rhs.fit_to_page_ + && fit_to_height_ == rhs.fit_to_height_ + && fit_to_width_ == rhs.fit_to_width_ + && scale_ == rhs.scale_; +} + } // namespace xlnt diff --git a/xlnt/source/worksheet/range.cpp b/xlnt/source/worksheet/range.cpp index 0bc9310..7649edc 100644 --- a/xlnt/source/worksheet/range.cpp +++ b/xlnt/source/worksheet/range.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -31,7 +31,7 @@ namespace xlnt { -range::range(worksheet ws, const range_reference &reference, major_order order, bool skip_null) +range::range(class worksheet ws, const range_reference &reference, major_order order, bool skip_null) : ws_(ws), ref_(reference), order_(order), @@ -39,9 +39,7 @@ range::range(worksheet ws, const range_reference &reference, major_order order, { } -range::~range() -{ -} +range::~range() = default; void range::clear_cells() { @@ -71,6 +69,16 @@ cell_vector range::operator[](std::size_t index) return vector(index); } +const cell_vector range::operator[](std::size_t index) const +{ + return vector(index); +} + +const worksheet &range::target_worksheet() const +{ + return ws_; +} + range_reference range::reference() const { return ref_; @@ -109,6 +117,22 @@ cell_vector range::vector(std::size_t vector_index) return cell_vector(ws_, cursor, ref_, order_, skip_null_, false); } +const cell_vector range::vector(std::size_t vector_index) const +{ + auto cursor = ref_.top_left(); + + if (order_ == major_order::row) + { + cursor.row(cursor.row() + static_cast(vector_index)); + } + else + { + cursor.column_index(cursor.column_index() + static_cast(vector_index)); + } + + return cell_vector(ws_, cursor, ref_, order_, skip_null_, false); +} + bool range::contains(const cell_reference &ref) { return ref_.top_left().column_index() <= ref.column_index() @@ -185,6 +209,11 @@ cell range::cell(const cell_reference &ref) return (*this)[ref.row() - 1][ref.column().index - 1]; } +const cell range::cell(const cell_reference &ref) const +{ + return (*this)[ref.row() - 1][ref.column().index - 1]; +} + cell_vector range::front() { return *begin(); diff --git a/xlnt/source/worksheet/range_iterator.cpp b/xlnt/source/worksheet/range_iterator.cpp index 642f6b8..2c7a3a1 100644 --- a/xlnt/source/worksheet/range_iterator.cpp +++ b/xlnt/source/worksheet/range_iterator.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -28,7 +28,12 @@ namespace xlnt { -cell_vector range_iterator::operator*() const +range_iterator::reference range_iterator::operator*() +{ + return cell_vector(ws_, cursor_, bounds_, order_, skip_null_, false); +} + +const range_iterator::reference range_iterator::operator*() const { return cell_vector(ws_, cursor_, bounds_, order_, skip_null_, false); } @@ -47,11 +52,6 @@ range_iterator::range_iterator(worksheet &ws, const cell_reference &cursor, } } -range_iterator::range_iterator(const range_iterator &other) -{ - *this = other; -} - bool range_iterator::operator==(const range_iterator &other) const { return ws_ == other.ws_ @@ -168,11 +168,6 @@ const_range_iterator::const_range_iterator(const worksheet &ws, const cell_refer } } -const_range_iterator::const_range_iterator(const const_range_iterator &other) -{ - *this = other; -} - bool const_range_iterator::operator==(const const_range_iterator &other) const { return ws_ == other.ws_ @@ -274,7 +269,7 @@ const_range_iterator const_range_iterator::operator++(int) return old; } -const cell_vector const_range_iterator::operator*() const +const const_range_iterator::reference const_range_iterator::operator*() const { return cell_vector(ws_, cursor_, bounds_, order_, skip_null_, false); } diff --git a/xlnt/source/worksheet/range_reference.cpp b/xlnt/source/worksheet/range_reference.cpp index 8fe9eb9..7116c07 100644 --- a/xlnt/source/worksheet/range_reference.cpp +++ b/xlnt/source/worksheet/range_reference.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -46,6 +46,12 @@ range_reference::range_reference(const char *range_string) { } +range_reference::range_reference(const range_reference &ref) +{ + top_left_ = ref.top_left_; + bottom_right_ = ref.bottom_right_; +} + range_reference::range_reference(const std::string &range_string) : top_left_("A1"), bottom_right_("A1") { @@ -178,4 +184,11 @@ XLNT_API bool operator!=(const char *reference_string, const range_reference &re return ref != reference_string; } +range_reference &range_reference::operator=(const range_reference &ref) +{ + top_left_ = ref.top_left_; + bottom_right_ = ref.bottom_right_; + return *this; +} + } // namespace xlnt diff --git a/xlnt/source/worksheet/sheet_protection.cpp b/xlnt/source/worksheet/sheet_protection.cpp index 5757ebf..c42b007 100644 --- a/xlnt/source/worksheet/sheet_protection.cpp +++ b/xlnt/source/worksheet/sheet_protection.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/xlnt/source/worksheet/worksheet.cpp b/xlnt/source/worksheet/worksheet.cpp index 830d3eb..5ab28fa 100644 --- a/xlnt/source/worksheet/worksheet.cpp +++ b/xlnt/source/worksheet/worksheet.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // Copyright (c) 2010-2015 openpyxl // // Permission is hereby granted, free of charge, to any person obtaining a copy @@ -26,10 +26,6 @@ #include #include -#include -#include -#include -#include #include #include #include @@ -46,6 +42,10 @@ #include #include #include +#include +#include +#include +#include namespace { @@ -98,7 +98,7 @@ void worksheet::create_named_range(const std::string &name, const range_referenc throw invalid_parameter(); //("named range name must be outside the range A1-XFD1048576"); } } - catch (xlnt::invalid_cell_reference) + catch (xlnt::invalid_cell_reference&) { // name is not a valid reference, that's good } @@ -242,25 +242,31 @@ std::string worksheet::title() const void worksheet::title(const std::string &title) { - if (title.length() > 31) + // do no work if we don't need to + if (d_->title_ == title) + { + return; + } + // excel limits worksheet titles to 31 characters + if (title.empty() || title.length() > 31) { throw invalid_sheet_title(title); } - + // invalid characters in a worksheet name if (title.find_first_of("*:/\\?[]") != std::string::npos) { throw invalid_sheet_title(title); } - - auto same_title = std::find_if(workbook().begin(), workbook().end(), - [&](worksheet ws) { return ws.title() == title; }); - - if (same_title != workbook().end() && *same_title != *this) + // try and insert the new name into the worksheets map + // if the insert fails, we have a duplicate sheet name + auto insert_result = workbook().d_->sheet_title_rel_id_map_.insert( + std::make_pair(title, workbook().d_->sheet_title_rel_id_map_[d_->title_])); + if (!insert_result.second) // insert failed, duplication detected { throw invalid_sheet_title(title); } - - workbook().d_->sheet_title_rel_id_map_[title] = workbook().d_->sheet_title_rel_id_map_[d_->title_]; + // if the insert succeeded (i.e. wasn't a duplicate sheet name) + // update the worksheet title and remove the old relation workbook().d_->sheet_title_rel_id_map_.erase(d_->title_); d_->title_ = title; @@ -284,13 +290,17 @@ void worksheet::freeze_panes(xlnt::cell top_left_cell) void worksheet::freeze_panes(const cell_reference &ref) { + if (ref == "A1") + { + unfreeze_panes(); + return; + } if (!has_view()) { d_->views_.push_back(sheet_view()); } auto &primary_view = d_->views_.front(); - if (!primary_view.has_pane()) { primary_view.pane(pane()); @@ -300,40 +310,23 @@ void worksheet::freeze_panes(const cell_reference &ref) primary_view.pane().state = pane_state::frozen; primary_view.clear_selections(); - primary_view.add_selection(selection()); - - if (ref == "A1") - { - unfreeze_panes(); - } - else if (ref.column() == "A") + if (ref.column() == "A") // no column is frozen { - primary_view.add_selection(selection()); - primary_view.selection(0).pane(pane_corner::bottom_left); - primary_view.selection(0).active_cell(ref.make_offset(0, -1)); // cell above - primary_view.selection(1).active_cell(ref); + primary_view.add_selection(selection(pane_corner::bottom_left, ref)); primary_view.pane().active_pane = pane_corner::bottom_left; primary_view.pane().y_split = ref.row() - 1; } - else if (ref.row() == 1) + else if (ref.row() == 1) // no row is frozen { - primary_view.add_selection(selection()); - primary_view.selection(0).pane(pane_corner::top_right); - primary_view.selection(0).active_cell(ref.make_offset(-1, 0)); // cell to the left - primary_view.selection(1).active_cell(ref); + primary_view.add_selection(selection(pane_corner::top_right, ref)); primary_view.pane().active_pane = pane_corner::top_right; primary_view.pane().x_split = ref.column_index() - 1; } - else + else // column and row is frozen { - primary_view.add_selection(selection()); - primary_view.add_selection(selection()); - primary_view.selection(0).pane(pane_corner::top_right); - primary_view.selection(0).active_cell(ref.make_offset(0, -1)); // cell above - primary_view.selection(1).pane(pane_corner::bottom_left); - primary_view.selection(1).active_cell(ref.make_offset(-1, 0)); // cell to the left - primary_view.selection(2).pane(pane_corner::bottom_right); - primary_view.selection(2).active_cell(ref); + primary_view.add_selection(selection(pane_corner::top_right, cell_reference(ref.column(), 1))); + primary_view.add_selection(selection(pane_corner::bottom_left, cell_reference(1, ref.row()))); + primary_view.add_selection(selection(pane_corner::bottom_right, ref)); primary_view.pane().active_pane = pane_corner::bottom_right; primary_view.pane().x_split = ref.column_index() - 1; primary_view.pane().y_split = ref.row() - 1; @@ -361,11 +354,12 @@ void worksheet::active_cell(const cell_reference &ref) if (!primary_view.has_selections()) { - primary_view.add_selection(selection()); + primary_view.add_selection(selection(pane_corner::bottom_right, ref)); + } + else + { + primary_view.selection(0).active_cell(ref); } - - auto &primary_selection = primary_view.selection(0); - primary_selection.active_cell(ref); } bool worksheet::has_active_cell() const @@ -608,7 +602,8 @@ column_t worksheet::highest_column_or_props() const range_reference worksheet::calculate_dimension() const { - return range_reference(lowest_column(), lowest_row(), highest_column(), highest_row()); + return range_reference(lowest_column(), lowest_row(), + highest_column(), highest_row_or_props()); } range worksheet::range(const std::string &reference_string) @@ -756,6 +751,7 @@ void worksheet::clear_cell(const cell_reference &ref) void worksheet::clear_row(row_t row) { d_->cell_map_.erase(row); + d_->row_properties_.erase(row); // TODO: garbage collect newly unreferenced resources such as styles? } @@ -1011,7 +1007,7 @@ bool worksheet::has_view() const return !d_->views_.empty(); } -sheet_view worksheet::view(std::size_t index) const +sheet_view &worksheet::view(std::size_t index) const { return d_->views_.at(index); } @@ -1031,6 +1027,21 @@ void worksheet::register_calc_chain_in_manifest() workbook().register_workbook_part(relationship_type::calculation_chain); } +bool worksheet::has_phonetic_properties() const +{ + return d_->phonetic_properties_.is_set(); +} + +const phonetic_pr& worksheet::phonetic_properties() const +{ + return d_->phonetic_properties_.get(); +} + +void worksheet::phonetic_properties(const phonetic_pr& phonetic_props) +{ + d_->phonetic_properties_.set(phonetic_props); +} + bool worksheet::has_header_footer() const { return d_->header_footer_.is_set(); @@ -1085,7 +1096,7 @@ double worksheet::row_height(row_t row) const { static const auto DefaultRowHeight = 15.0; - if (has_row_properties(row)) + if (has_row_properties(row) && row_properties(row).height.is_set()) { return row_properties(row).height.get(); } @@ -1107,7 +1118,33 @@ void worksheet::parent(xlnt::workbook &wb) conditional_format worksheet::conditional_format(const range_reference &ref, const condition &when) { - return workbook().d_->stylesheet_.get().add_conditional_format_rule(d_, ref, when); + return workbook().d_->stylesheet_.get().add_conditional_format_rule(d_, ref, when); +} + +path worksheet::path() const +{ + auto rel = referring_relationship(); + return xlnt::path(rel.source().path().parent().append(rel.target().path())); +} + +relationship worksheet::referring_relationship() const +{ + auto &manifest = workbook().manifest(); + auto wb_rel = manifest.relationship(xlnt::path("/"), + relationship_type::office_document); + auto ws_rel = manifest.relationship(wb_rel.target().path(), + workbook().d_->sheet_title_rel_id_map_.at(title())); + return ws_rel; +} + +sheet_format_properties worksheet::format_properties() const +{ + return d_->format_properties_; +} + +void worksheet::format_properties(const sheet_format_properties &properties) +{ + d_->format_properties_ = properties; } } // namespace xlnt diff --git a/xlnt/tests/CMakeLists.txt b/xlnt/tests/CMakeLists.txt index 20add02..74c2435 100644 --- a/xlnt/tests/CMakeLists.txt +++ b/xlnt/tests/CMakeLists.txt @@ -1,9 +1,9 @@ cmake_minimum_required(VERSION 3.1) project(xlnt.test) -# Require C++11 compiler -set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD ${XLNT_CXX_LANG}) set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CXX_EXTENSIONS OFF) if(NOT COMBINED_PROJECT) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../source ${CMAKE_CURRENT_BINARY_DIR}/source) @@ -14,12 +14,12 @@ if(STATIC_CRT) ucm_set_runtime(STATIC) endif() -file(GLOB CELL_TESTS ${CMAKE_CURRENT_SOURCE_DIR}/cell/*.hpp) -file(GLOB PACKAGING_TESTS ${CMAKE_CURRENT_SOURCE_DIR}/packaging/*.hpp) -file(GLOB STYLES_TESTS ${CMAKE_CURRENT_SOURCE_DIR}/styles/*.hpp) -file(GLOB UTILS_TESTS ${CMAKE_CURRENT_SOURCE_DIR}/utils/*.hpp) -file(GLOB WORKBOOK_TESTS ${CMAKE_CURRENT_SOURCE_DIR}/workbook/*.hpp) -file(GLOB WORKSHEET_TESTS ${CMAKE_CURRENT_SOURCE_DIR}/worksheet/*.hpp) +file(GLOB CELL_TESTS ${CMAKE_CURRENT_SOURCE_DIR}/cell/*.cpp) +file(GLOB PACKAGING_TESTS ${CMAKE_CURRENT_SOURCE_DIR}/packaging/*.cpp) +file(GLOB STYLES_TESTS ${CMAKE_CURRENT_SOURCE_DIR}/styles/*.cpp) +file(GLOB UTILS_TESTS ${CMAKE_CURRENT_SOURCE_DIR}/utils/*.cpp) +file(GLOB WORKBOOK_TESTS ${CMAKE_CURRENT_SOURCE_DIR}/workbook/*.cpp) +file(GLOB WORKSHEET_TESTS ${CMAKE_CURRENT_SOURCE_DIR}/worksheet/*.cpp) set(TESTS ${CELL_TESTS} @@ -48,6 +48,8 @@ target_include_directories(xlnt.test set(XLNT_TEST_DATA_DIR ${CMAKE_CURRENT_SOURCE_DIR}/data) target_compile_definitions(xlnt.test PRIVATE XLNT_TEST_DATA_DIR=${XLNT_TEST_DATA_DIR}) +# requires cmake 3.8+ +#target_compile_features(xlnt.test PRIVATE cxx_std_${XLNT_CXX_LANG}) if(MSVC) # bigobj because there are so many headers in one source file diff --git a/xlnt/tests/data/10_comments_hyperlinks_formulae.xlsx b/xlnt/tests/data/10_comments_hyperlinks_formulae.xlsx index 0e5a7f2..c989dc5 100644 Binary files a/xlnt/tests/data/10_comments_hyperlinks_formulae.xlsx and b/xlnt/tests/data/10_comments_hyperlinks_formulae.xlsx differ diff --git a/xlnt/tests/data/13_custom_heights_widths.xlsx b/xlnt/tests/data/13_custom_heights_widths.xlsx index 875edd5..005feb0 100644 Binary files a/xlnt/tests/data/13_custom_heights_widths.xlsx and b/xlnt/tests/data/13_custom_heights_widths.xlsx differ diff --git "a/xlnt/tests/data/9_unicode_\316\233.xlsx" "b/xlnt/tests/data/9_unicode_\316\233.xlsx" index affa532..39977a1 100644 Binary files "a/xlnt/tests/data/9_unicode_\316\233.xlsx" and "b/xlnt/tests/data/9_unicode_\316\233.xlsx" differ diff --git a/xlnt/tests/helpers/assertions.hpp b/xlnt/tests/helpers/assertions.hpp index 54f700a..531f6af 100644 --- a/xlnt/tests/helpers/assertions.hpp +++ b/xlnt/tests/helpers/assertions.hpp @@ -2,29 +2,60 @@ #include #include +#include -#define xlnt_assert(expression) do\ - {\ - try { if (expression) break; }\ - catch (...) {}\ - throw xlnt::exception("test failed");\ +#define XLNT_STRINGIFYX(x) #x +#define XLNT_STRINGIFY(x) XLNT_STRINGIFYX(x) + +#define xlnt_assert(expression) \ + do \ + { \ + try \ + { \ + if (expression) break; \ + } \ + catch (std::exception & ex) \ + { \ + throw ex; \ + } \ + catch (...) \ + { \ + } \ + throw xlnt::exception( \ + "assert failed at L:" XLNT_STRINGIFY(__LINE__) "\n" XLNT_STRINGIFY(expression)); \ } while (false) -#define xlnt_assert_throws_nothing(expression) do\ - {\ - try { expression; break; }\ - catch (...) {}\ - throw xlnt::exception("test failed");\ +#define xlnt_assert_throws_nothing(expression) \ + do \ + { \ + try \ + { \ + expression; \ + break; \ + } \ + catch (...) \ + { \ + } \ + throw xlnt::exception("assert throws nothing failed at L:" XLNT_STRINGIFY(__LINE__) "\n" XLNT_STRINGIFY(expression)); \ } while (false) -#define xlnt_assert_throws(expression, exception_type) do\ - {\ - try { expression; }\ - catch (exception_type) { break; }\ - catch (...) {}\ - throw xlnt::exception("test failed");\ +#define xlnt_assert_throws(expression, exception_type) \ + do \ + { \ + try \ + { \ + expression; \ + } \ + catch (exception_type) \ + { \ + break; \ + } \ + catch (...) \ + { \ + } \ + throw xlnt::exception("assert throws failed at L:" XLNT_STRINGIFY(__LINE__) "\n" XLNT_STRINGIFY(expression)); \ } while (false) -#define xlnt_assert_equals(left, right) xlnt_assert(left == right) -#define xlnt_assert_differs(left, right) xlnt_assert(left != right) -#define xlnt_assert_delta(left, right, delta) xlnt_assert(std::abs(left - right) <= delta) +#define xlnt_assert_equals(left, right) xlnt_assert((left) == (right)) +#define xlnt_assert_differs(left, right) xlnt_assert((left) != (right)) +#define xlnt_assert_delta(left, right, delta) xlnt_assert(std::abs((left) - (right)) <= (delta)) diff --git a/xlnt/tests/helpers/test_suite.hpp b/xlnt/tests/helpers/test_suite.hpp index 7de32f6..89062c8 100644 --- a/xlnt/tests/helpers/test_suite.hpp +++ b/xlnt/tests/helpers/test_suite.hpp @@ -8,6 +8,11 @@ #include #include +#include +//#include +//#include +#include +#include struct test_status { @@ -17,48 +22,21 @@ struct test_status std::vector failures; }; -std::string build_name(const std::string &pretty, const std::string &method) -{ - return pretty.substr(0, pretty.find("::") + 2) + method; -} +std::string build_name(const std::string &pretty, const std::string &method); #define register_test(test) register_test_internal([this]() { test(); }, build_name(__FUNCTION__, #test)); class test_suite { public: - test_status go() - { - test_status status; - - for (auto test : tests) - { - try - { - test.first(); - std::cout << "."; - status.tests_passed++; - } - catch (...) - { - std::cout << "*"; - status.tests_failed++; - status.failures.push_back(test.second); - } - - std::cout.flush(); - status.tests_run++; - } - - return status; - } + static test_status go(); protected: - void register_test_internal(std::function t, const std::string &function) + static void register_test_internal(std::function t, const std::string &function) { - tests.push_back(std::make_pair(t, function)); + tests().push_back(std::make_pair(t, function)); } private: - std::vector, std::string>> tests; + static std::vector, std::string>> &tests(); }; diff --git a/xlnt/tests/helpers/timing.hpp b/xlnt/tests/helpers/timing.hpp index 034cea6..b2d9186 100644 --- a/xlnt/tests/helpers/timing.hpp +++ b/xlnt/tests/helpers/timing.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017 Thomas Fussell +// Copyright (c) 2017-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -26,7 +26,7 @@ namespace xlnt { namespace benchmarks { -std::size_t current_time() +inline std::size_t current_time() { auto now = std::chrono::system_clock::now(); auto time_since_epoch = now.time_since_epoch(); diff --git a/xlnt/tests/helpers/xml_helper.hpp b/xlnt/tests/helpers/xml_helper.hpp index 3ceaf16..9a90e9e 100644 --- a/xlnt/tests/helpers/xml_helper.hpp +++ b/xlnt/tests/helpers/xml_helper.hpp @@ -1,26 +1,40 @@ #pragma once #include +#include +#include +#include #include #include #include -#include -#include class xml_helper { public: static bool compare_files(const std::string &left, - const std::string &right, const std::string &content_type) + const std::string &right, const std::string &content_type) { // content types are stored in unordered maps, too complicated to compare - if (content_type == "[Content_Types].xml") return true; + if (content_type == "[Content_Types].xml") + { + return true; + } + // calcChain is optional - if (content_type == "application/vnd.openxmlformats-officedocument.spreadsheetml.calcChain+xml") return true; + if (content_type == "application/vnd.openxmlformats-officedocument.spreadsheetml.calcChain+xml") + { + return true; + } + + // compared already + if (content_type == "application/vnd.openxmlformats-package.relationships+xml") + { + return true; + } auto is_xml = (content_type.substr(0, 12) == "application/" - && content_type.substr(content_type.size() - 4) == "+xml") + && content_type.substr(content_type.size() - 4) == "+xml") || content_type == "application/xml" || content_type == "[Content_Types].xml" || content_type == "application/vnd.openxmlformats-officedocument.vmlDrawing"; @@ -49,8 +63,7 @@ class xml_helper bool difference = false; auto right_iter = right_parser.begin(); - auto is_whitespace = [](const std::string &v) - { + auto is_whitespace = [](const std::string &v) { return v.find_first_not_of("\n\r\t ") == std::string::npos; }; @@ -184,49 +197,99 @@ class xml_helper ++right_iter; } - if (difference && !suppress_debug_info) - { - std::cout << "documents don't match" << std::endl; + if (difference && !suppress_debug_info) + { + std::cout << "documents don't match" << std::endl; - std::cout << "left:" << std::endl; + std::cout << "left:" << std::endl; for (auto c : left) { std::cout << c << std::flush; } - std::cout << std::endl; + std::cout << std::endl; - std::cout << "right:" << std::endl; + std::cout << "right:" << std::endl; for (auto c : right) { std::cout << c << std::flush; } - std::cout << std::endl; - } + std::cout << std::endl; + } - return !difference; + return !difference; } - static bool xlsx_archives_match(const std::vector &left, + static bool compare_relationships(const xlnt::manifest &left, + const xlnt::manifest &right) + { + std::unordered_set parts; + + for (const auto &part : left.parts()) + { + parts.insert(part.string()); + + auto left_rels = left.relationships(part); + auto right_rels = right.relationships(part); + + if (left_rels.size() != right_rels.size()) + { + return false; + } + + std::unordered_map left_rels_map; + + for (const auto &rel : left_rels) + { + left_rels_map[rel.id()] = rel; + } + + for (const auto &right_rel : right_rels) + { + if (left_rels_map.count(right_rel.id()) != 1) + { + return false; + } + + const auto &left_rel = left_rels_map.at(right_rel.id()); + + if (left_rel != right_rel) + { + return false; + } + } + } + + for (const auto &part : right.parts()) + { + if (parts.count(part.string()) != 1) + { + return false; + } + } + + return true; + } + + static bool xlsx_archives_match(const std::vector &left, const std::vector &right) - { + { xlnt::detail::vector_istreambuf left_buffer(left); std::istream left_stream(&left_buffer); xlnt::detail::izstream left_archive(left_stream); - const auto left_info = left_archive.files(); + const auto left_info = left_archive.files(); xlnt::detail::vector_istreambuf right_buffer(right); std::istream right_stream(&right_buffer); xlnt::detail::izstream right_archive(right_stream); - const auto right_info = right_archive.files(); + const auto right_info = right_archive.files(); auto difference_is_missing_calc_chain = false; if (std::abs(int(left_info.size()) - int(right_info.size())) == 1) { - auto is_calc_chain = [](const xlnt::path &p) - { + auto is_calc_chain = [](const xlnt::path &p) { return p.filename() == "calcChain.xml"; }; @@ -241,7 +304,7 @@ class xml_helper } } - if (left_info.size() != right_info.size() && ! difference_is_missing_calc_chain) + if (left_info.size() != right_info.size() && !difference_is_missing_calc_chain) { std::cout << "left has a different number of files than right" << std::endl; @@ -271,9 +334,43 @@ class xml_helper auto &left_manifest = left_workbook.manifest(); auto &right_manifest = right_workbook.manifest(); - for (auto left_member : left_info) - { - if (!right_archive.has_file(left_member)) + if (!compare_relationships(left_manifest, right_manifest)) + { + std::cout << "relationship mismatch\n" + << "Left:\n"; + for (const auto &part : left_manifest.parts()) + { + std::cout << "-part: " << part.string() << '\n'; + auto rels = left_manifest.relationships(part); + for (auto &rel : rels) + { + std::cout << rel.id() << ':' + << static_cast(rel.type()) + << ':' << static_cast(rel.target_mode()) + << ':' << rel.source().path().string() + << ':' << rel.target().path().string() << '\n'; + } + } + std::cout << "\nRight:\n"; + for (const auto &part : right_manifest.parts()) + { + std::cout << "-part: " << part.string() << '\n'; + auto rels = right_manifest.relationships(part); + for (auto &rel : rels) + { + std::cout << rel.id() + << ':' << static_cast(rel.type()) + << ':' << static_cast(rel.target_mode()) + << ':' << rel.source().path().string() + << ':' << rel.target().path().string() << '\n'; + } + } + return false; + } + + for (auto left_member : left_info) + { + if (!right_archive.has_file(left_member)) { if (difference_is_missing_calc_chain) { @@ -287,32 +384,34 @@ class xml_helper } auto left_content_type = left_member.string() == "[Content_Types].xml" - ? "[Content_Types].xml" : left_manifest.content_type(left_member); + ? "[Content_Types].xml" + : left_manifest.content_type(left_member); auto right_content_type = left_member.string() == "[Content_Types].xml" - ? "[Content_Types].xml" : right_manifest.content_type(left_member); + ? "[Content_Types].xml" + : right_manifest.content_type(left_member); if (left_content_type != right_content_type) { std::cout << "content types differ: " - << left_member.string() - << " " - << left_content_type - << " " - << right_content_type - << std::endl; + << left_member.string() + << " " + << left_content_type + << " " + << right_content_type + << std::endl; match = false; break; } if (!compare_files(left_archive.read(left_member), - left_archive.read(left_member), left_content_type)) - { - std::cout << left_member.string() << std::endl; + right_archive.read(left_member), left_content_type)) + { + std::cout << left_member.string() << std::endl; match = false; break; - } - } + } + } - return match; - } + return match; + } }; diff --git a/xlnt/tests/runner.cpp b/xlnt/tests/runner.cpp index b01ea24..b2f31a5 100644 --- a/xlnt/tests/runner.cpp +++ b/xlnt/tests/runner.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 Thomas Fussell +// Copyright (c) 2014-2018 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -22,85 +22,27 @@ // @author: see AUTHORS file #include +#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -#include - -test_status overall_status; - -template -void run_tests() +void print_summary(const test_status& results) { - auto status = T{}.go(); + std::cout << "\n\n"; - overall_status.tests_run += status.tests_run; - overall_status.tests_passed += status.tests_passed; - overall_status.tests_failed += status.tests_failed; + std::cout << "Run: " << results.tests_run << '\n'; + std::cout << "Passed: " << results.tests_passed << '\n'; + std::cout << "Failed: " << results.tests_failed << '\n' << '\n'; - std::copy(status.failures.begin(), status.failures.end(), std::back_inserter(overall_status.failures)); -} - -void print_summary() -{ - std::cout << std::endl; - - for (auto failure : overall_status.failures) + for (auto failure : results.failures) { - std::cout << failure << " failed" << std::endl; + std::cout << failure << "\n\n"; } } int main() { - // cell - run_tests(); - run_tests(); - run_tests(); - - // styles - run_tests(); - run_tests(); - run_tests(); - run_tests(); - - // utils - run_tests(); - run_tests(); - run_tests(); - run_tests(); - - // workbook - run_tests(); - run_tests(); - run_tests(); - - // worksheet - run_tests(); - run_tests(); - run_tests(); + test_status overall_status = test_suite::go(); - print_summary(); + print_summary(overall_status); return static_cast(overall_status.tests_failed); }