diff --git a/.appveyor.yml b/.appveyor.yml index 9e5b0ce5..374194cb 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -12,8 +12,8 @@ environment: init: [] install: [] before_build: -- git submodule update --init --recursive -- cmake -H. -Bbuild -G"Visual Studio 14 2015 Win64" -DSTATIC=%STATIC% +- git submodule update --init --recursive --remote +- cmake -H. -Bbuild -G"Visual Studio 14 2015 Win64" -DSTATIC=%STATIC% -DSAMPLES=ON build: project: build/xlnt_all.sln diff --git a/.gitmodules b/.gitmodules index a3a84d6d..cb9c07a2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -22,6 +22,8 @@ path = third-party/botan url = https://github.com/randombit/botan branch = master + [submodule "third-party/zlib"] path = third-party/zlib url = https://github.com/madler/zlib.git + branch = develop diff --git a/.travis.yml b/.travis.yml index 35c75ac4..852f5f14 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,6 +23,7 @@ matrix: - pip install --user git+git://github.com/eddyxu/cpp-coveralls.git env: - COMPILER=g++-4.9 + - COVERAGE=ON - os: linux compiler: gcc @@ -35,25 +36,12 @@ matrix: - valgrind env: - COMPILER=g++-5 + - COVERAGE=OFF script: - - cmake --version - mkdir build - cd build - - cmake -D SHARED=1 -D STATIC=0 -D WITH_TESTS=1 -D WITH_SAMPLES=1 -D CMAKE_CXX_COMPILER=$COMPILER .. - - cmake --build . --target xlnt.test + - cmake -D STATIC=ON -D SAMPLES=ON -D COVERAGE=$COVERAGE -D CMAKE_CXX_COMPILER=$COMPILER -D CMAKE_BUILD_TYPE=Debug .. + - cmake --build . - cd bin && ./xlnt.test - -after_success: - - if [ "$COMPILER" = "g++-4.9" ]; then sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.9 90 ; fi - - if [ "$COMPILER" = "g++-4.9" ]; then sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.9 90 ; fi - - if [ "$COMPILER" = "g++-4.9" ]; then sudo update-alternatives --install /usr/bin/gcov gcov /usr/bin/gcov-4.9 90 ; fi - - if [ "$COMPILER" = "g++-4.9" ]; then cd .. ; fi - - if [ "$COMPILER" = "g++-4.9" ]; then rm -rf ./* ; fi - - if [ "$COMPILER" = "g++-4.9" ]; then cmake -G "Unix Makefiles" -D DEBUG=1 -D COVERAGE=1 -D SHARED=0 -DCMAKE_BUILD_TYPE=Debug -D STATIC=1 .. ; fi - - if [ "$COMPILER" = "g++-4.9" ]; then make ; fi - - if [ "$COMPILER" = "g++-4.9" ]; then cd bin && ./xlnt.test ; fi - - if [ "$COMPILER" = "g++-4.9" ]; then cd ../ ; fi - - if [ "$COMPILER" = "g++-4.9" ]; then export OLDWD=$(pwd) ; fi - - if [ "$COMPILER" = "g++-4.9" ]; then cd "build/CMakeFiles/xlnt.static.dir$(pwd)" ; pwd ; fi - - if [ "$COMPILER" = "g++-4.9" ]; then coveralls --root $OLDWD --verbose -x ".cpp" --gcov-options '\-p' --exclude include --exclude third-party --exclude tests --exclude samples --exclude benchmarks -E ".*/source/.*/tests/.*" -E ".*/build/tests/runner-autogen.cpp.*" -E ".*CompilerIdCXX/.*" ; fi + - find . diff --git a/docs/index.rst b/docs/index.rst index e0153006..db081691 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -19,57 +19,107 @@ xlnt is a C++14 library for reading, writing, and modifying XLSX files as descri Summary of Features +++++++++++++++++++ ++---------------------------------------------------------------------+------+------+-------+ | Feature | Read | Edit | Write | -|---------------------------------------------------------------------|------|------|-------| ++=====================================================================+======+======+=======+ | Excel-style Workbook | ✓ | ✓ | ✓ | ++---------------------------------------------------------------------+------+------+-------+ | LibreOffice-style Workbook | ✓ | ✓ | ✓ | ++---------------------------------------------------------------------+------+------+-------+ | Numbers-style Workbook | ✓ | ✓ | ✓ | ++---------------------------------------------------------------------+------+------+-------+ | Encrypted Workbook (Excel 2007-2010) | ✓ | ✓ | | ++---------------------------------------------------------------------+------+------+-------+ | Encrypted Workbook (Excel 2013-2016) | ✓ | ✓ | | ++---------------------------------------------------------------------+------+------+-------+ | Excel Binary Workbook (.xlsb) | | | | ++---------------------------------------------------------------------+------+------+-------+ | Excel Macro-Enabled Workbook (.xlsm) | | | | ++---------------------------------------------------------------------+------+------+-------+ | Excel Macro-Enabled Template (.xltm) | | | | ++---------------------------------------------------------------------+------+------+-------+ | Document Properties | ✓ | ✓ | ✓ | ++---------------------------------------------------------------------+------+------+-------+ | Numeric Cell Values | ✓ | ✓ | ✓ | ++---------------------------------------------------------------------+------+------+-------+ | Inline String Cell Values | ✓ | ✓ | ✓ | ++---------------------------------------------------------------------+------+------+-------+ | Shared String Cell Values | ✓ | ✓ | ✓ | ++---------------------------------------------------------------------+------+------+-------+ | Shared String Text Run Formatting (e.g. varied fonts within a cell) | ✓ | ✓ | ✓ | ++---------------------------------------------------------------------+------+------+-------+ | Hyperlink Cell Values | | | | ++---------------------------------------------------------------------+------+------+-------+ | Formula Cell Values | | | | ++---------------------------------------------------------------------+------+------+-------+ | Formula Evaluation | | | | ++---------------------------------------------------------------------+------+------+-------+ | Page Margins | ✓ | ✓ | ✓ | ++---------------------------------------------------------------------+------+------+-------+ | Page Setup | | | | ++---------------------------------------------------------------------+------+------+-------+ | Print Area | | | | -| Comments | | | | ++---------------------------------------------------------------------+------+------+-------+ +| Comments | ✓ | ✓ | | ++---------------------------------------------------------------------+------+------+-------+ | Header and Footer | | | | ++---------------------------------------------------------------------+------+------+-------+ | Custom Views | | | | ++---------------------------------------------------------------------+------+------+-------+ | Charts | | | | ++---------------------------------------------------------------------+------+------+-------+ | Chartsheets | | | | ++---------------------------------------------------------------------+------+------+-------+ | Dialogsheets | | | | ++---------------------------------------------------------------------+------+------+-------+ | Themes | ✓ | | ✓ | ++---------------------------------------------------------------------+------+------+-------+ | Cell Styles | ✓ | ✓ | ✓ | ++---------------------------------------------------------------------+------+------+-------+ | Cell Formats | ✓ | ✓ | ✓ | ++---------------------------------------------------------------------+------+------+-------+ | Formatting->Alignment (e.g. right align) | ✓ | ✓ | ✓ | ++---------------------------------------------------------------------+------+------+-------+ | Formatting->Border (e.g. red cell outline) | ✓ | ✓ | ✓ | ++---------------------------------------------------------------------+------+------+-------+ | Formatting->Fill (e.g. green cell background) | ✓ | ✓ | ✓ | ++---------------------------------------------------------------------+------+------+-------+ | Formatting->Font (e.g. blue cell text) | ✓ | ✓ | ✓ | ++---------------------------------------------------------------------+------+------+-------+ | Formatting->Number Format (e.g. show 2 decimals) | ✓ | ✓ | ✓ | ++---------------------------------------------------------------------+------+------+-------+ | Formatting->Protection (e.g. hide formulas) | ✓ | ✓ | ✓ | ++---------------------------------------------------------------------+------+------+-------+ | Column Styles | | | | ++---------------------------------------------------------------------+------+------+-------+ | Row Styles | | | | ++---------------------------------------------------------------------+------+------+-------+ | Sheet Styles | | | | ++---------------------------------------------------------------------+------+------+-------+ | Conditional Formatting | | | | ++---------------------------------------------------------------------+------+------+-------+ | Tables | | | | ++---------------------------------------------------------------------+------+------+-------+ | Table Formatting | | | | ++---------------------------------------------------------------------+------+------+-------+ | Pivot Tables | | | | ++---------------------------------------------------------------------+------+------+-------+ | XLSX Thumbnail | ✓ | | ✓ | ++---------------------------------------------------------------------+------+------+-------+ | Custom OOXML Properties | | | | ++---------------------------------------------------------------------+------+------+-------+ | Custom OOXML Parts | | | | ++---------------------------------------------------------------------+------+------+-------+ | Drawing | | | | ++---------------------------------------------------------------------+------+------+-------+ | Text Box | | | | ++---------------------------------------------------------------------+------+------+-------+ | WordArt | | | | ++---------------------------------------------------------------------+------+------+-------+ | Embedded Content (e.g. images) | | | | ++---------------------------------------------------------------------+------+------+-------+ | Excel VBA | | | | ++---------------------------------------------------------------------+------+------+-------+ Sample code: ++++++++++++ diff --git a/include/xlnt/cell/cell_reference.hpp b/include/xlnt/cell/cell_reference.hpp index 3186dc9a..ad0df249 100644 --- a/include/xlnt/cell/cell_reference.hpp +++ b/include/xlnt/cell/cell_reference.hpp @@ -86,12 +86,6 @@ public: /// cell_reference(const std::string &reference_string); - /// - /// Constructs a cell_reference from a string reprenting a column (e.g. A) and - /// a 1-indexed row. - /// - cell_reference(const std::string &column, row_t row); - /// /// Constructs a cell_reference from a 1-indexed column index and row index. /// diff --git a/include/xlnt/cell/index_types.hpp b/include/xlnt/cell/index_types.hpp index cbd339b2..771397ae 100644 --- a/include/xlnt/cell/index_types.hpp +++ b/include/xlnt/cell/index_types.hpp @@ -81,12 +81,12 @@ public: /// /// Construct a column from a string. /// - explicit column_t(const std::string &column_string); + column_t(const std::string &column_string); /// /// Construct a column from a string. /// - explicit column_t(const char *column_string); + column_t(const char *column_string); /// /// Copy constructor diff --git a/include/xlnt/styles/alignment.hpp b/include/xlnt/styles/alignment.hpp index 82e0a2ba..2e697674 100644 --- a/include/xlnt/styles/alignment.hpp +++ b/include/xlnt/styles/alignment.hpp @@ -27,7 +27,6 @@ #include #include #include -#include #include namespace xlnt { @@ -35,7 +34,7 @@ namespace xlnt { /// /// Alignment options for use in cell formats. /// -class XLNT_API alignment : public hashable +class XLNT_API alignment { public: optional shrink() const; @@ -62,8 +61,15 @@ public: alignment &vertical(vertical_alignment vertical); -protected: - std::string to_hash_string() const override; + /// + /// Returns true if left is exactly equal to right. + /// + friend bool operator==(const alignment &left, const alignment &right); + + /// + /// Returns true if left is not exactly equal to right. + /// + friend bool operator!=(const alignment &left, const alignment &right) { return !(left == right); } private: optional shrink_to_fit_; diff --git a/include/xlnt/styles/border.hpp b/include/xlnt/styles/border.hpp index 0e7712de..e8718a46 100644 --- a/include/xlnt/styles/border.hpp +++ b/include/xlnt/styles/border.hpp @@ -33,7 +33,6 @@ #include #include #include -#include #include namespace xlnt { @@ -56,7 +55,7 @@ namespace xlnt { /// /// Describes the border style of a particular cell. /// -class XLNT_API border : public hashable +class XLNT_API border { public: class XLNT_API border_property @@ -68,6 +67,16 @@ public: optional style() const; border_property &style(border_style style); + /// + /// Returns true if left is exactly equal to right. + /// + friend bool operator==(const border_property &left, const border_property &right); + + /// + /// Returns true if left is not exactly equal to right. + /// + friend bool operator!=(const border_property &left, const border_property &right) { return !(left == right); } + private: optional color_; optional style_; @@ -82,9 +91,16 @@ public: optional diagonal() const; border &diagonal(diagonal_direction dir); - -protected: - std::string to_hash_string() const override; + + /// + /// Returns true if left is exactly equal to right. + /// + friend bool operator==(const border &left, const border &right); + + /// + /// Returns true if left is not exactly equal to right. + /// + friend bool operator!=(const border &left, const border &right) { return !(left == right); } private: optional start_; diff --git a/include/xlnt/styles/color.hpp b/include/xlnt/styles/color.hpp index ced7d0a5..320a3daa 100644 --- a/include/xlnt/styles/color.hpp +++ b/include/xlnt/styles/color.hpp @@ -87,7 +87,7 @@ private: /// /// Colors can be applied to many parts of a cell's style. /// -class XLNT_API color : public hashable +class XLNT_API color { public: /// @@ -136,9 +136,6 @@ public: bool operator==(const color &other) const; bool operator!=(const color &other) const { return !(*this == other); } -protected: - std::string to_hash_string() const override; - private: void assert_type(type t) const; diff --git a/include/xlnt/styles/fill.hpp b/include/xlnt/styles/fill.hpp index e7af02da..71ecf921 100644 --- a/include/xlnt/styles/fill.hpp +++ b/include/xlnt/styles/fill.hpp @@ -54,7 +54,7 @@ enum class XLNT_API pattern_fill_type gray0625 }; -class XLNT_API pattern_fill : public hashable +class XLNT_API pattern_fill { public: pattern_fill(); @@ -71,8 +71,15 @@ public: pattern_fill &background(const color &background); -protected: - std::string to_hash_string() const override; + /// + /// Returns true if left is exactly equal to right. + /// + friend bool operator==(const pattern_fill &left, const pattern_fill &right); + + /// + /// Returns true if left is not exactly equal to right. + /// + friend bool operator!=(const pattern_fill &left, const pattern_fill &right) { return !(left == right); } private: pattern_fill_type type_ = pattern_fill_type::none; @@ -87,7 +94,7 @@ enum class XLNT_API gradient_fill_type path }; -class XLNT_API gradient_fill : public hashable +class XLNT_API gradient_fill { public: gradient_fill(); @@ -134,9 +141,16 @@ public: gradient_fill &clear_stops(); std::unordered_map stops() const; + + /// + /// Returns true if left is exactly equal to right. + /// + friend bool operator==(const gradient_fill &left, const gradient_fill &right); -protected: - std::string to_hash_string() const override; + /// + /// Returns true if left is not exactly equal to right. + /// + friend bool operator!=(const gradient_fill &left, const gradient_fill &right) { return !(left == right); } private: gradient_fill_type type_ = gradient_fill_type::linear; @@ -160,7 +174,7 @@ enum class XLNT_API fill_type /// /// Describes the fill style of a particular cell. /// -class XLNT_API fill : public hashable +class XLNT_API fill { public: /// @@ -203,9 +217,16 @@ public: /// Throws an invalid_attribute exception if this is not a pattern fill. /// class pattern_fill pattern_fill() const; + + /// + /// Returns true if left is exactly equal to right. + /// + friend bool operator==(const fill &left, const fill &right); -protected: - std::string to_hash_string() const override; + /// + /// Returns true if left is not exactly equal to right. + /// + friend bool operator!=(const fill &left, const fill &right) { return !(left == right); } private: fill_type type_ = fill_type::pattern; diff --git a/include/xlnt/styles/font.hpp b/include/xlnt/styles/font.hpp index f1ab9a6e..dd4b3fd9 100644 --- a/include/xlnt/styles/font.hpp +++ b/include/xlnt/styles/font.hpp @@ -36,7 +36,7 @@ class style; /// /// Describes the font style of a particular cell. /// -class XLNT_API font : public hashable +class XLNT_API font { public: enum class underline_style @@ -91,9 +91,16 @@ public: font &scheme(const std::string &scheme); optional scheme() const; - -protected: - std::string to_hash_string() const override; + + /// + /// Returns true if left is exactly equal to right. + /// + friend bool operator==(const font &left, const font &right); + + /// + /// Returns true if left is not exactly equal to right. + /// + friend bool operator!=(const font &left, const font &right) { return !(left == right); } private: friend class style; diff --git a/include/xlnt/styles/number_format.hpp b/include/xlnt/styles/number_format.hpp index d9c204c5..cb36afa9 100644 --- a/include/xlnt/styles/number_format.hpp +++ b/include/xlnt/styles/number_format.hpp @@ -27,7 +27,6 @@ #include #include -#include namespace xlnt { @@ -36,7 +35,7 @@ enum class calendar; /// /// Describes the number formatting applied to text and numbers within a certain cell. /// -class XLNT_API number_format : public hashable +class XLNT_API number_format { public: static const number_format general(); @@ -88,8 +87,16 @@ public: bool is_date_format() const; -protected: - std::string to_hash_string() const override; + + /// + /// Returns true if left is exactly equal to right. + /// + friend bool operator==(const number_format &left, const number_format &right); + + /// + /// Returns true if left is not exactly equal to right. + /// + friend bool operator!=(const number_format &left, const number_format &right) { return !(left == right); } private: bool id_set_; diff --git a/include/xlnt/styles/protection.hpp b/include/xlnt/styles/protection.hpp index 8c7e776e..a342e170 100644 --- a/include/xlnt/styles/protection.hpp +++ b/include/xlnt/styles/protection.hpp @@ -26,7 +26,6 @@ #include #include -#include #include namespace xlnt { @@ -34,7 +33,7 @@ namespace xlnt { /// /// Describes the protection style of a particular cell. /// -class XLNT_API protection : public hashable +class XLNT_API protection { public: static protection unlocked_and_visible(); @@ -49,9 +48,16 @@ public: bool hidden() const; protection &hidden(bool hidden); + + /// + /// Returns true if left is exactly equal to right. + /// + friend bool operator==(const protection &left, const protection &right); -protected: - std::string to_hash_string() const override; + /// + /// Returns true if left is not exactly equal to right. + /// + friend bool operator!=(const protection &left, const protection &right) { return !(left == right); } private: bool locked_; diff --git a/include/xlnt/utils/exceptions.hpp b/include/xlnt/utils/exceptions.hpp index 8ebf87f0..322b5fcb 100644 --- a/include/xlnt/utils/exceptions.hpp +++ b/include/xlnt/utils/exceptions.hpp @@ -38,6 +38,8 @@ class XLNT_API exception : public std::runtime_error { public: exception(const std::string &message); + exception(const exception &) = default; + virtual ~exception(); void set_message(const std::string &message); @@ -107,6 +109,8 @@ class XLNT_API invalid_column_string_index : public exception { public: invalid_column_string_index(); + invalid_column_string_index(const invalid_column_string_index &) = default; + ~invalid_column_string_index(); }; /// @@ -117,6 +121,8 @@ class XLNT_API invalid_cell_reference : public exception public: invalid_cell_reference(column_t column, row_t row); invalid_cell_reference(const std::string &reference_string); + invalid_cell_reference(const invalid_cell_reference &) = default; + ~invalid_cell_reference(); }; /// @@ -126,6 +132,8 @@ class XLNT_API invalid_attribute : public exception { public: invalid_attribute(); + invalid_attribute(const invalid_attribute &) = default; + virtual ~invalid_attribute(); }; /// diff --git a/include/xlnt/utils/hashable.hpp b/include/xlnt/utils/hashable.hpp index 56ec9ab7..e69de29b 100644 --- a/include/xlnt/utils/hashable.hpp +++ b/include/xlnt/utils/hashable.hpp @@ -1,55 +0,0 @@ -// Copyright (c) 2014-2016 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 -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, WRISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE -// -// @license: http://www.opensource.org/licenses/mit-license.php -// @author: see AUTHORS file -#pragma once - -#include -#include - -#include - -namespace xlnt { - -class XLNT_API hashable -{ -public: - std::size_t hash() const; - bool operator==(const hashable &other) const; - -protected: - virtual std::string to_hash_string() const = 0; -}; - -} // namespace xlnt - -namespace std { - -template<> -struct hash -{ - size_t operator()(const xlnt::hashable &k) const - { - return k.hash(); - } -}; - -} // namepsace std diff --git a/include/xlnt/utils/utf8string.hpp b/include/xlnt/utils/utf8string.hpp index 1b8025b8..1d09eda4 100644 --- a/include/xlnt/utils/utf8string.hpp +++ b/include/xlnt/utils/utf8string.hpp @@ -24,7 +24,6 @@ #include #include -#include #include // for XLNT_API #include diff --git a/include/xlnt/worksheet/pane.hpp b/include/xlnt/worksheet/pane.hpp index 067ab4ad..27771b10 100644 --- a/include/xlnt/worksheet/pane.hpp +++ b/include/xlnt/worksheet/pane.hpp @@ -24,6 +24,7 @@ #pragma once #include +#include #include namespace xlnt { @@ -57,8 +58,8 @@ public: cell_reference top_left_cell; pane_state state; pane_corner active_pane; - int y_split; - int x_split; + row_t y_split; + column_t x_split; }; } // namespace xlnt diff --git a/include/xlnt/worksheet/worksheet.hpp b/include/xlnt/worksheet/worksheet.hpp index e87df722..9c10a7e5 100644 --- a/include/xlnt/worksheet/worksheet.hpp +++ b/include/xlnt/worksheet/worksheet.hpp @@ -223,10 +223,10 @@ public: range iter_cells(bool skip_null); - void add_print_title(int i); - void add_print_title(int i, const std::string &rows_or_cols); - void set_print_title_rows(const std::string &rows); - void set_print_title_cols(const std::string &rows); + void set_print_title_rows(row_t first_row, row_t last_row); + void set_print_title_rows(row_t last_row); + void set_print_title_cols(column_t first_column, column_t last_column); + void set_print_title_cols(column_t last_column); std::string get_print_titles() const; void set_print_area(const std::string &print_area); diff --git a/include/xlnt/xlnt_config.hpp b/include/xlnt/xlnt_config.hpp index 3d1d0b5f..28419e48 100644 --- a/include/xlnt/xlnt_config.hpp +++ b/include/xlnt/xlnt_config.hpp @@ -26,28 +26,6 @@ namespace xlnt { -/// -/// Enumeration of possible limit styles. -/// Excel places limitations on the number of rows and columns, -/// but we may wish to change those limits in some cases. Values -/// other than excel might prevent the file from being opened in Excel. -/// -enum class limit_style -{ - /// - /// limits used in openpyxl - /// - openpyxl, - /// - /// limits as determined by Excel - /// - excel, - /// - /// limits as high as possible based on system (i.e. 32-bit or 64-bit) - /// - maximum -}; - #ifndef XLNT_API #if !defined(XLNT_STATIC) && defined(_MSC_VER) #ifdef XLNT_EXPORT diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index 038449bf..98f01c8d 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -1,3 +1,4 @@ +cmake_minimum_required(VERSION 3.1) include(${CMAKE_CURRENT_SOURCE_DIR}/../cmake/common.cmake) project(${LIBRARY_NAME}.samples VERSION ${LIBRARY_VERSION} LANGUAGES CXX C) @@ -7,18 +8,22 @@ endif() include_directories(${LIBRARY_INCLUDE_DIR}) -add_executable(sample-basic ${CMAKE_CURRENT_SOURCE_DIR}/sample.cpp) -target_link_libraries(sample-basic ${LIBRARY_NAME}) - -add_executable(sample-decrypt ${CMAKE_CURRENT_SOURCE_DIR}/decrypt.cpp) -target_link_libraries(sample-decrypt ${LIBRARY_NAME}) +file(GLOB SAMPLE_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) if(NOT MSVC) set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) - target_link_libraries(sample-basic Threads::Threads) - target_link_libraries(sample-decrypt Threads::Threads) endif() +foreach(SAMPLE_SOURCE IN ITEMS ${SAMPLE_SOURCES}) + get_filename_component(SAMPLE_NAME ${SAMPLE_SOURCE} NAME_WE) + set(SAMPLE_EXECUTABLE sample-${SAMPLE_NAME}) + add_executable(${SAMPLE_EXECUTABLE} ${SAMPLE_SOURCE}) + target_link_libraries(${SAMPLE_EXECUTABLE} ${LIBRARY_NAME}) + if(NOT MSVC) + target_link_libraries(${SAMPLE_EXECUTABLE} Threads::Threads) + endif() +endforeach() + file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data DESTINATION ${CMAKE_BINARY_DIR}/bin) diff --git a/samples/add_comments.cpp b/samples/disabled/add_comments.cpp similarity index 100% rename from samples/add_comments.cpp rename to samples/disabled/add_comments.cpp diff --git a/samples/basic_conditional_formatting.cpp b/samples/disabled/basic_conditional_formatting.cpp similarity index 100% rename from samples/basic_conditional_formatting.cpp rename to samples/disabled/basic_conditional_formatting.cpp diff --git a/samples/comment_error.cpp b/samples/disabled/comment_error.cpp similarity index 100% rename from samples/comment_error.cpp rename to samples/disabled/comment_error.cpp diff --git a/samples/copy_style.cpp b/samples/disabled/copy_style.cpp similarity index 100% rename from samples/copy_style.cpp rename to samples/disabled/copy_style.cpp diff --git a/samples/create.cpp b/samples/disabled/create.cpp similarity index 100% rename from samples/create.cpp rename to samples/disabled/create.cpp diff --git a/samples/default_styles.cpp b/samples/disabled/default_styles.cpp similarity index 100% rename from samples/default_styles.cpp rename to samples/disabled/default_styles.cpp diff --git a/samples/font_legacy_colors.cpp b/samples/disabled/font_legacy_colors.cpp similarity index 100% rename from samples/font_legacy_colors.cpp rename to samples/disabled/font_legacy_colors.cpp diff --git a/samples/font_rgb_colors.cpp b/samples/disabled/font_rgb_colors.cpp similarity index 100% rename from samples/font_rgb_colors.cpp rename to samples/disabled/font_rgb_colors.cpp diff --git a/samples/group_columns.cpp b/samples/disabled/group_columns.cpp similarity index 100% rename from samples/group_columns.cpp rename to samples/disabled/group_columns.cpp diff --git a/samples/header_footer.cpp b/samples/disabled/header_footer.cpp similarity index 100% rename from samples/header_footer.cpp rename to samples/disabled/header_footer.cpp diff --git a/samples/image.cpp b/samples/disabled/image.cpp similarity index 100% rename from samples/image.cpp rename to samples/disabled/image.cpp diff --git a/samples/merge_unmerge.cpp b/samples/disabled/merge_unmerge.cpp similarity index 100% rename from samples/merge_unmerge.cpp rename to samples/disabled/merge_unmerge.cpp diff --git a/samples/page_setup.cpp b/samples/disabled/page_setup.cpp similarity index 100% rename from samples/page_setup.cpp rename to samples/disabled/page_setup.cpp diff --git a/samples/parse_formulae.cpp b/samples/disabled/parse_formulae.cpp similarity index 100% rename from samples/parse_formulae.cpp rename to samples/disabled/parse_formulae.cpp diff --git a/samples/print_options.cpp b/samples/disabled/print_options.cpp similarity index 100% rename from samples/print_options.cpp rename to samples/disabled/print_options.cpp diff --git a/samples/read.cpp b/samples/disabled/read.cpp similarity index 100% rename from samples/read.cpp rename to samples/disabled/read.cpp diff --git a/samples/row_column_style.cpp b/samples/disabled/row_column_style.cpp similarity index 100% rename from samples/row_column_style.cpp rename to samples/disabled/row_column_style.cpp diff --git a/samples/style_application.cpp b/samples/disabled/style_application.cpp similarity index 100% rename from samples/style_application.cpp rename to samples/disabled/style_application.cpp diff --git a/samples/style_side_effects.cpp b/samples/disabled/style_side_effects.cpp similarity index 100% rename from samples/style_side_effects.cpp rename to samples/disabled/style_side_effects.cpp diff --git a/samples/using_formulae.cpp b/samples/disabled/using_formulae.cpp similarity index 100% rename from samples/using_formulae.cpp rename to samples/disabled/using_formulae.cpp diff --git a/samples/using_number_formats.cpp b/samples/disabled/using_number_formats.cpp similarity index 100% rename from samples/using_number_formats.cpp rename to samples/disabled/using_number_formats.cpp diff --git a/samples/validate_cells.cpp b/samples/disabled/validate_cells.cpp similarity index 100% rename from samples/validate_cells.cpp rename to samples/disabled/validate_cells.cpp diff --git a/samples/worksheet_properties.cpp b/samples/disabled/worksheet_properties.cpp similarity index 100% rename from samples/worksheet_properties.cpp rename to samples/disabled/worksheet_properties.cpp diff --git a/samples/write.cpp b/samples/disabled/write.cpp similarity index 100% rename from samples/write.cpp rename to samples/disabled/write.cpp diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index e62d4193..7e925dbe 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -48,7 +48,15 @@ endforeach() # Platform specific settings if(COVERAGE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") +endif() + +if(MSVC) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") +elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra") +elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Weverything -Wno-c++98-compat -Wno-padded") endif() if(APPLE) @@ -68,7 +76,6 @@ include_directories(${XLNT_INCLUDE_DIR} ${THIRD_PARTY_DIR}/utfcpp/source ${THIRD_PARTY_DIR}/pole ${THIRD_PARTY_DIR}/botan - ${THIRD_PARTY_DIR}/partio ${THIRD_PARTY_DIR}/zlib) file(GLOB ROOT_HEADERS ${XLNT_INCLUDE_DIR}/xlnt/*.hpp) @@ -92,7 +99,7 @@ file(GLOB WORKBOOK_HEADERS ${XLNT_INCLUDE_DIR}/xlnt/workbook/*.hpp) file(GLOB WORKBOOK_SOURCES ${XLNT_SOURCE_DIR}/workbook/*.cpp) file(GLOB WORKSHEET_HEADERS ${XLNT_INCLUDE_DIR}/xlnt/worksheet/*.hpp) file(GLOB WORKSHEET_SOURCES ${XLNT_SOURCE_DIR}/worksheet/*.cpp) -file(GLOB DETAIL_HEADERS ${XLNT_INCLUDE_DIR}/detail/*.hpp) +file(GLOB DETAIL_HEADERS ${XLNT_SOURCE_DIR}/detail/*.hpp) file(GLOB DETAIL_SOURCES ${XLNT_SOURCE_DIR}/detail/*.cpp) set(XLNT_HEADERS ${ROOT_HEADERS} ${CELL_HEADERS} ${CHARTS_HEADERS} diff --git a/source/cell/cell.cpp b/source/cell/cell.cpp index eb8c3768..d46b4243 100644 --- a/source/cell/cell.cpp +++ b/source/cell/cell.cpp @@ -522,7 +522,7 @@ void cell::set_error(const std::string &error) cell cell::offset(int column, int row) { - return get_worksheet().get_cell(cell_reference(d_->column_ + column, d_->row_ + row)); + return get_worksheet().get_cell(get_reference().make_offset(column, row)); } worksheet cell::get_worksheet() diff --git a/source/cell/cell_reference.cpp b/source/cell/cell_reference.cpp index b1a9e619..7b727339 100644 --- a/source/cell/cell_reference.cpp +++ b/source/cell/cell_reference.cpp @@ -61,11 +61,6 @@ cell_reference::cell_reference(const char *reference_string) { } -cell_reference::cell_reference(const std::string &column, row_t row) - : cell_reference(column_t(column), row) -{ -} - cell_reference::cell_reference(column_t column_index, row_t row) : column_(column_index), row_(row), absolute_row_(false), absolute_column_(false) { @@ -268,8 +263,10 @@ bool cell_reference::operator!=(const char *reference_string) const cell_reference cell_reference::make_offset(int column_offset, int row_offset) const { //TODO: check for overflow/underflow - return cell_reference(static_cast(static_cast(column_.index) + column_offset), - static_cast(static_cast(row_) + row_offset)); + auto relative_column = static_cast(static_cast(column_.index) + column_offset); + auto relative_row = static_cast(static_cast(row_) + row_offset); + return cell_reference(relative_column, relative_row); + } bool cell_reference::operator==(const cell_reference &comparand) const diff --git a/source/cell/index_types.cpp b/source/cell/index_types.cpp index 1880c38c..5323a272 100644 --- a/source/cell/index_types.cpp +++ b/source/cell/index_types.cpp @@ -114,7 +114,7 @@ bool column_t::operator==(const column_t &other) const { return index == other.i bool column_t::operator!=(const column_t &other) const { return !(*this == other); } -bool column_t::operator==(int other) const { return *this == column_t(other); } +bool column_t::operator==(int other) const { return *this == column_t(static_cast(other)); } bool column_t::operator==(index_t other) const { return *this == column_t(other); } diff --git a/source/cell/text_run.cpp b/source/cell/text_run.cpp index 71be04d9..6e8e96cb 100644 --- a/source/cell/text_run.cpp +++ b/source/cell/text_run.cpp @@ -51,7 +51,7 @@ bool text_run::has_formatting() const bool text_run::has_size() const { - return (bool)size_; + return size_.is_set(); } std::size_t text_run::get_size() const @@ -66,7 +66,7 @@ void text_run::set_size(std::size_t size) bool text_run::has_color() const { - return (bool)color_; + return color_.is_set(); } color text_run::get_color() const @@ -81,7 +81,7 @@ void text_run::set_color(const color &new_color) bool text_run::has_font() const { - return (bool)font_; + return font_.is_set(); } std::string text_run::get_font() const @@ -96,7 +96,7 @@ void text_run::set_font(const std::string &font) bool text_run::has_family() const { - return (bool)family_; + return family_.is_set(); } std::size_t text_run::get_family() const @@ -111,7 +111,7 @@ void text_run::set_family(std::size_t family) bool text_run::has_scheme() const { - return (bool)scheme_; + return scheme_.is_set(); } std::string text_run::get_scheme() const @@ -126,7 +126,7 @@ void text_run::set_scheme(const std::string &scheme) bool text_run::bold_set() const { - return (bool)bold_; + return bold_.is_set(); } bool text_run::is_bold() const diff --git a/source/detail/constants.cpp b/source/detail/constants.cpp index d36bc4e1..eb379ccf 100644 --- a/source/detail/constants.cpp +++ b/source/detail/constants.cpp @@ -27,12 +27,12 @@ namespace xlnt { -const row_t constants::min_row() +row_t constants::min_row() { return 1; } -const row_t constants::max_row() +row_t constants::max_row() { return std::numeric_limits::max(); } diff --git a/source/detail/constants.hpp b/source/detail/constants.hpp index 7183cba0..00b39e39 100644 --- a/source/detail/constants.hpp +++ b/source/detail/constants.hpp @@ -35,12 +35,12 @@ struct XLNT_API constants /// /// Returns the lowest allowable row index in a worksheet. /// - static const row_t min_row(); + static row_t min_row(); /// /// Returns the largest allowable row index in a worksheet. /// - static const row_t max_row(); + static row_t max_row(); /// /// Returns the lowest allowable column index in a worksheet. diff --git a/source/detail/excel_thumbnail.hpp b/source/detail/excel_thumbnail.hpp index 649d452d..8e6fdf9b 100644 --- a/source/detail/excel_thumbnail.hpp +++ b/source/detail/excel_thumbnail.hpp @@ -5,6 +5,8 @@ namespace xlnt { +const std::vector &excel_thumbnail(); + const std::vector &excel_thumbnail() { const auto *data = new std::vector{ diff --git a/source/detail/include_libstudxml.hpp b/source/detail/include_libstudxml.hpp index 6ffaa583..eb88eb3d 100644 --- a/source/detail/include_libstudxml.hpp +++ b/source/detail/include_libstudxml.hpp @@ -25,4 +25,4 @@ #include #include #include -#include \ No newline at end of file +#include diff --git a/source/detail/number_formatter.cpp b/source/detail/number_formatter.cpp index b5f43232..17a41cd2 100644 --- a/source/detail/number_formatter.cpp +++ b/source/detail/number_formatter.cpp @@ -25,125 +25,144 @@ #include #include +#include -namespace xlnt { -namespace detail { +namespace { const std::unordered_map known_locales() { const std::unordered_map *all = new std::unordered_map( - { - { 0x401, "Arabic - Saudi Arabia" }, - { 0x402, "Bulgarian" }, - { 0x403, "Catalan" }, - { 0x404, "Chinese - Taiwan" }, - { 0x405, "Czech" }, - { 0x406, "Danish" }, - { 0x407, "German - Germany" }, - { 0x408, "Greek" }, - { 0x409, "English - United States" }, - { 0x410, "Italian - Italy" }, - { 0x411, "Japanese" }, - { 0x412, "Korean" }, - { 0x413, "Dutch - Netherlands" }, - { 0x414, "Norwegian - Bokml" }, - { 0x415, "Polish" }, - { 0x416, "Portuguese - Brazil" }, - { 0x417, "Raeto-Romance" }, - { 0x418, "Romanian - Romania" }, - { 0x419, "Russian" }, - { 0x420, "Urdu" }, - { 0x421, "Indonesian" }, - { 0x422, "Ukrainian" }, - { 0x423, "Belarusian" }, - { 0x424, "Slovenian" }, - { 0x425, "Estonian" }, - { 0x426, "Latvian" }, - { 0x427, "Lithuanian" }, - { 0x428, "Tajik" }, - { 0x429, "Farsi - Persian" }, - { 0x430, "Sesotho (Sutu)" }, - { 0x431, "Tsonga" }, - { 0x432, "Setsuana" }, - { 0x433, "Venda" }, - { 0x434, "Xhosa" }, - { 0x435, "Zulu" }, - { 0x436, "Afrikaans" }, - { 0x437, "Georgian" }, - { 0x438, "Faroese" }, - { 0x439, "Hindi" }, - { 0x440, "Kyrgyz - Cyrillic" }, - { 0x441, "Swahili" }, - { 0x442, "Turkmen" }, - { 0x443, "Uzbek - Latin" }, - { 0x444, "Tatar" }, - { 0x445, "Bengali - India" }, - { 0x446, "Punjabi" }, - { 0x447, "Gujarati" }, - { 0x448, "Oriya" }, - { 0x449, "Tamil" }, - { 0x450, "Mongolian" }, - { 0x451, "Tibetan" }, - { 0x452, "Welsh" }, - { 0x453, "Khmer" }, - { 0x454, "Lao" }, - { 0x455, "Burmese" }, - { 0x456, "Galician" }, - { 0x457, "Konkani" }, - { 0x458, "Manipuri" }, - { 0x459, "Sindhi" }, - { 0x460, "Kashmiri" }, - { 0x461, "Nepali" }, - { 0x462, "Frisian - Netherlands" }, - { 0x464, "Filipino" }, - { 0x465, "Divehi; Dhivehi; Maldivian" }, - { 0x466, "Edo" }, - { 0x470, "Igbo - Nigeria" }, - { 0x474, "Guarani - Paraguay" }, - { 0x476, "Latin" }, - { 0x477, "Somali" }, - { 0x481, "Maori" }, - { 0x801, "Arabic - Iraq" }, - { 0x804, "Chinese - China" }, - { 0x807, "German - Switzerland" }, - { 0x809, "English - Great Britain" }, - { 0x810, "Italian - Switzerland" }, - { 0x813, "Dutch - Belgium" }, - { 0x814, "Norwegian - Nynorsk" }, - { 0x816, "Portuguese - Portugal" }, - { 0x818, "Romanian - Moldova" }, - { 0x819, "Russian - Moldova" }, - { 0x843, "Uzbek - Cyrillic" }, - { 0x845, "Bengali - Bangladesh" }, - { 0x850, "Mongolian" }, - { 0x1001, "Arabic - Libya" }, - { 0x1004, "Chinese - Singapore" }, - { 0x1007, "German - Luxembourg" }, - { 0x1009, "English - Canada" }, - { 0x1401, "Arabic - Algeria" }, - { 0x1404, "Chinese - Macau SAR" }, - { 0x1407, "German - Liechtenstein" }, - { 0x1409, "English - New Zealand" }, - { 0x1801, "Arabic - Morocco" }, - { 0x1809, "English - Ireland" }, - { 0x2001, "Arabic - Oman" }, - { 0x2009, "English - Jamaica" }, - { 0x2401, "Arabic - Yemen" }, - { 0x2409, "English - Caribbean" }, - { 0x2801, "Arabic - Syria" }, - { 0x2809, "English - Belize" }, - { 0x3001, "Arabic - Lebanon" }, - { 0x3009, "English - Zimbabwe" }, - { 0x3401, "Arabic - Kuwait" }, - { 0x3409, "English - Phillippines" }, - { 0x3801, "Arabic - United Arab Emirates" }, - { 0x4001, "Arabic - Qatar" } - }); + { + { 0x401, "Arabic - Saudi Arabia" }, + { 0x402, "Bulgarian" }, + { 0x403, "Catalan" }, + { 0x404, "Chinese - Taiwan" }, + { 0x405, "Czech" }, + { 0x406, "Danish" }, + { 0x407, "German - Germany" }, + { 0x408, "Greek" }, + { 0x409, "English - United States" }, + { 0x410, "Italian - Italy" }, + { 0x411, "Japanese" }, + { 0x412, "Korean" }, + { 0x413, "Dutch - Netherlands" }, + { 0x414, "Norwegian - Bokml" }, + { 0x415, "Polish" }, + { 0x416, "Portuguese - Brazil" }, + { 0x417, "Raeto-Romance" }, + { 0x418, "Romanian - Romania" }, + { 0x419, "Russian" }, + { 0x420, "Urdu" }, + { 0x421, "Indonesian" }, + { 0x422, "Ukrainian" }, + { 0x423, "Belarusian" }, + { 0x424, "Slovenian" }, + { 0x425, "Estonian" }, + { 0x426, "Latvian" }, + { 0x427, "Lithuanian" }, + { 0x428, "Tajik" }, + { 0x429, "Farsi - Persian" }, + { 0x430, "Sesotho (Sutu)" }, + { 0x431, "Tsonga" }, + { 0x432, "Setsuana" }, + { 0x433, "Venda" }, + { 0x434, "Xhosa" }, + { 0x435, "Zulu" }, + { 0x436, "Afrikaans" }, + { 0x437, "Georgian" }, + { 0x438, "Faroese" }, + { 0x439, "Hindi" }, + { 0x440, "Kyrgyz - Cyrillic" }, + { 0x441, "Swahili" }, + { 0x442, "Turkmen" }, + { 0x443, "Uzbek - Latin" }, + { 0x444, "Tatar" }, + { 0x445, "Bengali - India" }, + { 0x446, "Punjabi" }, + { 0x447, "Gujarati" }, + { 0x448, "Oriya" }, + { 0x449, "Tamil" }, + { 0x450, "Mongolian" }, + { 0x451, "Tibetan" }, + { 0x452, "Welsh" }, + { 0x453, "Khmer" }, + { 0x454, "Lao" }, + { 0x455, "Burmese" }, + { 0x456, "Galician" }, + { 0x457, "Konkani" }, + { 0x458, "Manipuri" }, + { 0x459, "Sindhi" }, + { 0x460, "Kashmiri" }, + { 0x461, "Nepali" }, + { 0x462, "Frisian - Netherlands" }, + { 0x464, "Filipino" }, + { 0x465, "Divehi; Dhivehi; Maldivian" }, + { 0x466, "Edo" }, + { 0x470, "Igbo - Nigeria" }, + { 0x474, "Guarani - Paraguay" }, + { 0x476, "Latin" }, + { 0x477, "Somali" }, + { 0x481, "Maori" }, + { 0x801, "Arabic - Iraq" }, + { 0x804, "Chinese - China" }, + { 0x807, "German - Switzerland" }, + { 0x809, "English - Great Britain" }, + { 0x810, "Italian - Switzerland" }, + { 0x813, "Dutch - Belgium" }, + { 0x814, "Norwegian - Nynorsk" }, + { 0x816, "Portuguese - Portugal" }, + { 0x818, "Romanian - Moldova" }, + { 0x819, "Russian - Moldova" }, + { 0x843, "Uzbek - Cyrillic" }, + { 0x845, "Bengali - Bangladesh" }, + { 0x850, "Mongolian" }, + { 0x1001, "Arabic - Libya" }, + { 0x1004, "Chinese - Singapore" }, + { 0x1007, "German - Luxembourg" }, + { 0x1009, "English - Canada" }, + { 0x1401, "Arabic - Algeria" }, + { 0x1404, "Chinese - Macau SAR" }, + { 0x1407, "German - Liechtenstein" }, + { 0x1409, "English - New Zealand" }, + { 0x1801, "Arabic - Morocco" }, + { 0x1809, "English - Ireland" }, + { 0x2001, "Arabic - Oman" }, + { 0x2009, "English - Jamaica" }, + { 0x2401, "Arabic - Yemen" }, + { 0x2409, "English - Caribbean" }, + { 0x2801, "Arabic - Syria" }, + { 0x2809, "English - Belize" }, + { 0x3001, "Arabic - Lebanon" }, + { 0x3009, "English - Zimbabwe" }, + { 0x3401, "Arabic - Kuwait" }, + { 0x3409, "English - Phillippines" }, + { 0x3801, "Arabic - United Arab Emirates" }, + { 0x4001, "Arabic - Qatar" } + } + ); return *all; } +[[ noreturn ]] void unhandled_case_error() +{ + throw xlnt::exception("unhandled"); +} + +void unhandled_case(bool error) +{ + if (error) + { + unhandled_case_error(); + } +} + +} // namespace + +namespace xlnt { +namespace detail { + bool format_condition::satisfied_by(long double number) const { switch (type) @@ -157,10 +176,9 @@ bool format_condition::satisfied_by(long double number) const case condition_type::less_than: return number < value; case condition_type::not_equal: - return number != value; + return std::fabs(number - value) >= std::numeric_limits::min(); case condition_type::equal: - default: - return number == value; + return std::fabs(number - value) < std::numeric_limits::min(); } } @@ -336,6 +354,10 @@ void number_format_parser::parse() part.type = template_part::template_type::elapsed_seconds; break; } + + unhandled_case(true); + break; + case 'm': if (token.string == "m") { @@ -362,6 +384,10 @@ void number_format_parser::parse() part.type = template_part::template_type::month_letter; break; } + + unhandled_case(true); + break; + case 'd': if (token.string == "d") { @@ -383,6 +409,10 @@ void number_format_parser::parse() part.type = template_part::template_type::day_name; break; } + + unhandled_case(true); + break; + case 'y': if (token.string == "yy") { @@ -394,6 +424,10 @@ void number_format_parser::parse() part.type = template_part::template_type::year_long; break; } + + unhandled_case(true); + break; + case 'h': if (token.string == "h") { @@ -405,6 +439,10 @@ void number_format_parser::parse() part.type = template_part::template_type::hour_leading_zero; break; } + + unhandled_case(true); + break; + case 's': if (token.string == "s") { @@ -416,6 +454,10 @@ void number_format_parser::parse() part.type = template_part::template_type::second_leading_zero; break; } + + unhandled_case(true); + break; + case 'A': section.twelve_hour = true; @@ -429,6 +471,13 @@ void number_format_parser::parse() part.type = template_part::template_type::a_p; break; } + + unhandled_case(true); + break; + + default: + unhandled_case(true); + break; } section.parts.push_back(part); @@ -445,8 +494,6 @@ void number_format_parser::parse() token = parse_next_token(); } - - throw std::runtime_error("bad format"); } void number_format_parser::finalize() @@ -805,11 +852,41 @@ number_format_token number_format_parser::parse_next_token() break; case '(': + token.type = number_format_token::token_type::text; + token.string.push_back(current_char); + + break; + case ')': + token.type = number_format_token::token_type::text; + token.string.push_back(current_char); + + break; + case '-': + token.type = number_format_token::token_type::text; + token.string.push_back(current_char); + + break; + case '+': + token.type = number_format_token::token_type::text; + token.string.push_back(current_char); + + break; + case ':': + token.type = number_format_token::token_type::text; + token.string.push_back(current_char); + + break; + case ' ': + token.type = number_format_token::token_type::text; + token.string.push_back(current_char); + + break; + case '/': token.type = number_format_token::token_type::text; token.string.push_back(current_char); @@ -832,6 +909,8 @@ number_format_token number_format_parser::parse_next_token() break; } + break; + default: throw std::runtime_error("unexpected character"); } @@ -951,6 +1030,10 @@ format_color number_format_parser::color_from_string(const std::string &color) return static_cast(color_number); } } + + unhandled_case(true); + break; + case 'B': if (color == "Black") { @@ -960,34 +1043,60 @@ format_color number_format_parser::color_from_string(const std::string &color) { return format_color::blue; } + + unhandled_case(true); + break; + case 'G': if (color == "Green") { return format_color::green; } + + unhandled_case(true); + break; + case 'W': if (color == "White") { return format_color::white; } + + unhandled_case(true); + break; + case 'M': if (color == "Magenta") { return format_color::magenta; } + + unhandled_case(true); + break; + case 'Y': if (color == "Yellow") { return format_color::yellow; } + + unhandled_case(true); + break; + case 'R': if (color == "Red") { return format_color::red; } + + unhandled_case(true); + break; + default: - throw std::runtime_error("bad color: " + color); + unhandled_case(true); } + + unhandled_case_error(); } std::pair number_format_parser::locale_from_string(const std::string &locale_string) @@ -1199,7 +1308,8 @@ std::string number_formatter::fill_placeholders(const format_placeholders &p, lo else if (p.type == format_placeholders::placeholders_type::fractional_part) { auto fractional_part = number - integer_part; - result = fractional_part == 0 ? std::string(".") : std::to_string(fractional_part).substr(1); + result = std::fabs(fractional_part) < std::numeric_limits::min() + ? std::string(".") : std::to_string(fractional_part).substr(1); while (result.back() == '0' || result.size() > (p.num_zeros + p.num_optionals + p.num_spaces + 1)) { diff --git a/source/detail/vector_streambuf.hpp b/source/detail/vector_streambuf.hpp index 8189cebd..cf928a8c 100644 --- a/source/detail/vector_streambuf.hpp +++ b/source/detail/vector_streambuf.hpp @@ -48,39 +48,32 @@ public: private: int_type underflow() { - return (position_ == static_cast(data_.size())) - ? traits_type::eof() : traits_type::to_int_type(data_[position_]); + if (position_ == data_.size()) + { + return traits_type::eof(); + } + + return traits_type::to_int_type(static_cast(data_[position_])); } int_type uflow() { - if (position_ == static_cast(data_.size())) + if (position_ == data_.size()) { return traits_type::eof(); } - auto previous = position_; - position_ += 1; - - return traits_type::to_int_type(data_[previous]); - } - - int_type pbackfail(int_type ch) - { - if (position_ == std::streampos(0) || (ch != traits_type::eof() && ch != data_[position_ - std::streampos(1)])) - { - return traits_type::eof(); - } - - auto old_pos = position_; - position_ -= 1; - - return traits_type::to_int_type(data_[position_]); + return traits_type::to_int_type(static_cast(data_[position_++])); } std::streamsize showmanyc() { - return position_ < data_.size() ? data_.size() - position_ : -1; + if (position_ == data_.size()) + { + return static_cast(-1); + } + + return static_cast(data_.size() - position_); } std::streampos seekoff(std::streamoff off, @@ -96,23 +89,56 @@ private: position_ = data_.size(); } - position_ += off; + if (off < 0) + { + if (static_cast(-off) > position_) + { + position_ = 0; + return static_cast(-1); + } + else + { + position_ -= static_cast(-off); + } + } + else if (off > 0) + { + if (static_cast(off) + position_ > data_.size()) + { + position_ = data_.size(); + return static_cast(-1); + } + else + { + position_ += static_cast(off); + } + } - if (position_ < 0) return -1; - if (position_ > data_.size()) return -1; - - return position_; + return static_cast(position_); } std::streampos seekpos(std::streampos sp, std::ios_base::openmode which = std::ios_base::in | std::ios_base::out) { - return position_ = sp; + if (sp < 0) + { + position_ = 0; + } + else if (static_cast(sp) > data_.size()) + { + position_ = data_.size(); + } + else + { + position_ = static_cast(sp); + } + + return static_cast(position_); } private: const std::vector &data_; - std::streampos position_; + std::size_t position_; }; /// @@ -141,59 +167,91 @@ private: position_ = data_.size() - 1; } - return traits_type::to_int_type(data_[position_]); + return traits_type::to_int_type(static_cast(data_[position_])); } std::streamsize xsputn(const char *s, std::streamsize n) { if (data_.empty()) { - data_.resize(n); + data_.resize(static_cast(n)); } else { auto position_size = data_.size(); - auto required_size = static_cast(position_ + static_cast(n)); + auto required_size = static_cast(position_ + static_cast(n)); data_.resize(std::max(position_size, required_size)); } - std::copy(s, s + n, data_.begin() + position_); - position_ += n; + std::copy(s, s + n, data_.begin() + static_cast(position_)); + position_ += static_cast(n); return n; } - std::streampos seekoff(std::streamoff off, std::ios_base::seekdir way, + std::streampos seekoff(std::streamoff off, + std::ios_base::seekdir way, std::ios_base::openmode which = std::ios_base::in | std::ios_base::out) { if (way == std::ios_base::beg) { - position_ = off; - } - else if (way == std::ios_base::cur) - { - position_ += off; + position_ = 0; } else if (way == std::ios_base::end) { position_ = data_.size(); } - return (position_ < 0 || position_ > data_.size()) - ? std::streampos(-1) : position_; + if (off < 0) + { + if (static_cast(-off) > position_) + { + position_ = 0; + return static_cast(-1); + } + else + { + position_ -= static_cast(-off); + } + } + else if (off > 0) + { + if (static_cast(off) + position_ > data_.size()) + { + position_ = data_.size(); + return static_cast(-1); + } + else + { + position_ += static_cast(off); + } + } + + return static_cast(position_); } std::streampos seekpos(std::streampos sp, std::ios_base::openmode which = std::ios_base::in | std::ios_base::out) { - position_ = sp; - return (position_ < 0 || position_ > data_.size()) - ? std::streampos(-1) : position_; + if (sp < 0) + { + position_ = 0; + } + else if (static_cast(sp) > data_.size()) + { + position_ = data_.size(); + } + else + { + position_ = static_cast(sp); + } + + return static_cast(position_); } private: std::vector &data_; - std::streampos position_; + std::size_t position_; }; } // namespace detail diff --git a/source/detail/xlsx_consumer.cpp b/source/detail/xlsx_consumer.cpp index 9efadcc4..8b84cbc1 100644 --- a/source/detail/xlsx_consumer.cpp +++ b/source/detail/xlsx_consumer.cpp @@ -99,13 +99,13 @@ xlnt::color read_color(xml::parser &parser) return result; } -std::vector read_relationships(const xlnt::path &part, Partio::ZipFileReader &archive) +std::vector read_relationships(const xlnt::path &part, xlnt::detail::ZipFileReader &archive) { std::vector relationships; - if (!archive.Has_File(part.string())) return relationships; + if (!archive.has_file(part.string())) return relationships; - std::unique_ptr rels_stream(archive.Get_File(part.string(), true)); - xml::parser parser(*rels_stream, part.string()); + auto &rels_stream = archive.open(part.string()); + xml::parser parser(rels_stream, part.string()); xlnt::uri source(part.string()); @@ -152,7 +152,7 @@ xlsx_consumer::xlsx_consumer(workbook &target) void xlsx_consumer::read(std::istream &source) { - archive_.reset(new Partio::ZipFileReader(source)); + archive_.reset(new ZipFileReader(source)); populate_workbook(); } @@ -170,8 +170,8 @@ void xlsx_consumer::populate_workbook() for (const auto &rel : manifest.get_relationships(path("/"))) { - std::unique_ptr parser_stream(archive_->Get_File(rel.get_target().get_path().string(), true)); - xml::parser parser(*parser_stream, rel.get_target().get_path().string()); + xml::parser parser(archive_->open(rel.get_target().get_path().string()), + rel.get_target().get_path().string()); parser_ = &parser; switch (rel.get_type()) @@ -235,11 +235,10 @@ void xlsx_consumer::populate_workbook() for (const auto &rel : manifest.get_relationships(workbook_rel.get_target().get_path())) { path part_path(rel.get_source().get_path().parent().append(rel.get_target().get_path())); - std::unique_ptr parser_stream(archive_->Get_File(part_path.string(), true)); auto using_namespaces = rel.get_type() == relationship::type::styles; auto receive = xml::parser::receive_default | (using_namespaces ? xml::parser::receive_namespace_decls : 0); - xml::parser parser(*parser_stream, part_path.string(), receive); + xml::parser parser(archive_->open(part_path.string()), part_path.string(), receive); parser_ = &parser; switch (rel.get_type()) @@ -268,9 +267,8 @@ void xlsx_consumer::populate_workbook() for (const auto &rel : manifest.get_relationships(workbook_rel.get_target().get_path())) { path part_path(rel.get_source().get_path().parent().append(rel.get_target().get_path())); - std::unique_ptr parser_stream(archive_->Get_File(part_path.string(), true)); auto receive = xml::parser::receive_default | xml::parser::receive_namespace_decls; - xml::parser parser(*parser_stream, rel.get_target().get_path().string(), receive); + xml::parser parser(archive_->open(part_path.string()), rel.get_target().get_path().string(), receive); parser_ = &parser; switch (rel.get_type()) @@ -306,21 +304,17 @@ void xlsx_consumer::read_manifest() { path package_rels_path("_rels/.rels"); - if (!archive_->Has_File(package_rels_path.string())) + if (!archive_->has_file(package_rels_path.string())) { throw invalid_file("missing package rels"); } auto package_rels = read_relationships(package_rels_path, *archive_); - - std::unique_ptr parser_stream(archive_->Get_File("[Content_Types].xml", true)); - //std::string stream_string((std::istreambuf_iterator(*parser_stream)), std::istreambuf_iterator()); - xml::parser parser(*parser_stream, "[Content_Types].xml"); - auto &manifest = target_.get_manifest(); static const auto xmlns = constants::get_namespace("content-types"); + xml::parser parser(archive_->open("[Content_Types].xml"), "[Content_Types].xml"); parser.next_expect(xml::parser::event_type::start_element, xmlns, "Types"); parser.content(xml::content::complex); @@ -355,10 +349,7 @@ void xlsx_consumer::read_manifest() package_rel.get_id()); } - std::vector file_list; - archive_->Get_File_List(file_list); - - for (const auto &relationship_source_string : file_list) + for (const auto &relationship_source_string : archive_->files()) { auto relationship_source = path(relationship_source_string); @@ -2013,9 +2004,8 @@ void xlsx_consumer::read_worksheet(const std::string &rel_id) part_path = std::accumulate(split_part_path.begin(), split_part_path.end(), path(""), [](const path &a, const std::string &b) { return a.append(b); }); - std::unique_ptr parser_stream(archive_->Get_File(part_path.string(), true)); auto receive = xml::parser::receive_default; - xml::parser parser(*parser_stream, rel.get_target().get_path().string(), receive); + xml::parser parser(archive_->open(part_path.string()), rel.get_target().get_path().string(), receive); parser_ = &parser; switch (rel.get_type()) diff --git a/source/detail/xlsx_consumer.hpp b/source/detail/xlsx_consumer.hpp index c752b661..3d40d3ef 100644 --- a/source/detail/xlsx_consumer.hpp +++ b/source/detail/xlsx_consumer.hpp @@ -30,10 +30,7 @@ #include #include - -namespace Partio { -class ZipFileReader; -} +#include namespace xlnt { @@ -44,6 +41,8 @@ class worksheet; namespace detail { +class ZipFileReader; + /// /// Handles writing a workbook into an XLSX file. /// @@ -218,7 +217,7 @@ private: /// /// The ZIP file containing the files that make up the OOXML package. /// - std::unique_ptr archive_; + std::unique_ptr archive_; /// /// Map of sheet titles to relationship IDs. diff --git a/source/detail/xlsx_crypto.cpp b/source/detail/xlsx_crypto.cpp index 93d4be92..94cd264b 100644 --- a/source/detail/xlsx_crypto.cpp +++ b/source/detail/xlsx_crypto.cpp @@ -33,551 +33,556 @@ namespace xlnt { namespace detail { -static const std::size_t segment_length = 4096; - -enum class cipher_algorithm +struct crypto_helper { - aes, - rc2, - rc4, - des, - desx, - triple_des, - triple_des_112 + static const std::size_t segment_length = 4096; + + enum class cipher_algorithm + { + aes, + rc2, + rc4, + des, + desx, + triple_des, + triple_des_112 + }; + + enum class cipher_chaining + { + ecb, // electronic code book + cbc, // cipher block chaining + cfb // cipher feedback chaining + }; + + enum class cipher_direction + { + encryption, + decryption + }; + + enum class hash_algorithm + { + sha1, + sha256, + sha384, + sha512, + md5, + md4, + md2, + ripemd128, + ripemd160, + whirlpool + }; + + static std::vector aes(const std::vector &key, + const std::vector &iv, + const std::vector &encrypted, + cipher_chaining chaining, cipher_direction direction) + { + std::string cipher_name("AES-"); + cipher_name.append(std::to_string(key.size() * 8)); + cipher_name.append(chaining == cipher_chaining::ecb + ? "/ECB/NoPadding" : "/CBC/NoPadding"); + + auto botan_direction = direction == cipher_direction::decryption + ? Botan::DECRYPTION : Botan::ENCRYPTION; + Botan::Pipe pipe(Botan::get_cipher(cipher_name, key, iv, botan_direction)); + pipe.process_msg(encrypted); + auto decrypted = pipe.read_all(); + + return std::vector(decrypted.begin(), decrypted.end()); + } + + static std::vector decode_base64(const std::string &encoded) + { + Botan::Pipe pipe(new Botan::Base64_Decoder); + pipe.process_msg(encoded); + auto decoded = pipe.read_all(); + + return std::vector(decoded.begin(), decoded.end()); + }; + + static std::vector hash(hash_algorithm algorithm, + const std::vector &input) + { + Botan::Pipe pipe(new Botan::Hash_Filter( + algorithm == hash_algorithm::sha512 ? "SHA-512" : "SHA-1")); + pipe.process_msg(input); + auto hash = pipe.read_all(); + + return std::vector(hash.begin(), hash.end()); + }; + + static std::vector get_file(POLE::Storage &storage, const std::string &name) + { + POLE::Stream stream(&storage, name.c_str()); + if (stream.fail()) return {}; + std::vector bytes(stream.size(), 0); + stream.read(bytes.data(), static_cast(bytes.size())); + return bytes; + } + + template + static auto read_int(std::size_t &index, const std::vector &raw_data) + { + auto result = *reinterpret_cast(&raw_data[index]); + index += sizeof(T); + + return result; + }; + + struct standard_encryption_info + { + const std::size_t spin_count = 50000; + std::size_t block_size; + std::size_t key_bits; + std::size_t key_bytes; + std::size_t hash_size; + cipher_algorithm cipher; + cipher_chaining chaining; + const hash_algorithm hash = hash_algorithm::sha1; + std::vector salt_value; + std::vector verifier_hash_input; + std::vector verifier_hash_value; + std::vector encrypted_key_value; + }; + + static std::vector decrypt_xlsx_standard(const std::vector &encryption_info, + const std::string &password, const std::vector &encrypted_package) + { + std::size_t offset = 0; + + standard_encryption_info info; + + auto header_length = read_int(offset, encryption_info); + auto index_at_start = offset; + /*auto skip_flags = */read_int(offset, encryption_info); + /*auto size_extra = */read_int(offset, encryption_info); + auto alg_id = read_int(offset, encryption_info); + + if (alg_id == 0 || alg_id == 0x0000660E || alg_id == 0x0000660F || alg_id == 0x00006610) + { + info.cipher = cipher_algorithm::aes; + } + else + { + throw xlnt::exception("invalid cipher algorithm"); + } + + auto alg_id_hash = read_int(offset, encryption_info); + if (alg_id_hash != 0x00008004 && alg_id_hash == 0) + { + throw xlnt::exception("invalid hash algorithm"); + } + + info.key_bits = read_int(offset, encryption_info); + info.key_bytes = info.key_bits / 8; + + auto provider_type = read_int(offset, encryption_info); + if (provider_type != 0 && provider_type != 0x00000018) + { + throw xlnt::exception("invalid provider type"); + } + + read_int(offset, encryption_info); // reserved 1 + if (read_int(offset, encryption_info) != 0) // reserved 2 + { + throw xlnt::exception("invalid header"); + } + + const auto csp_name_length = header_length - (offset - index_at_start); + std::vector csp_name_wide( + reinterpret_cast(&*(encryption_info.begin() + static_cast(offset))), + reinterpret_cast(&*(encryption_info.begin() + static_cast(offset + csp_name_length)))); + std::string csp_name(csp_name_wide.begin(), csp_name_wide.end() - 1); // without trailing null + if (csp_name != "Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)" + && csp_name != "Microsoft Enhanced RSA and AES Cryptographic Provider") + { + throw xlnt::exception("invalid cryptographic provider"); + } + offset += csp_name_length; + + const auto salt_size = read_int(offset, encryption_info); + std::vector salt(encryption_info.begin() + static_cast(offset), + encryption_info.begin() + static_cast(offset + salt_size)); + offset += salt_size; + + static const auto verifier_size = std::size_t(16); + std::vector verifier_hash_input(encryption_info.begin() + static_cast(offset), + encryption_info.begin() + static_cast(offset + verifier_size)); + offset += verifier_size; + + const auto verifier_hash_size = read_int(offset, encryption_info); + std::vector verifier_hash_value(encryption_info.begin() + static_cast(offset), + encryption_info.begin() + static_cast(offset + verifier_hash_size)); + offset += verifier_hash_size; + + // begin key generation algorithm + + // H_0 = H(salt + password) + auto salt_plus_password = salt; + std::vector password_wide(password.begin(), password.end()); + std::for_each(password_wide.begin(), password_wide.end(), + [&salt_plus_password](std::uint16_t c) + { + salt_plus_password.insert(salt_plus_password.end(), + reinterpret_cast(&c), + reinterpret_cast(&c) + sizeof(std::uint16_t)); + }); + std::vector h_0 = hash(info.hash, salt_plus_password); + + // H_n = H(iterator + H_n-1) + std::vector iterator_plus_h_n(4, 0); + iterator_plus_h_n.insert(iterator_plus_h_n.end(), h_0.begin(), h_0.end()); + std::uint32_t &iterator = *reinterpret_cast(iterator_plus_h_n.data()); + std::vector h_n; + for (iterator = 0; iterator < info.spin_count; ++iterator) + { + h_n = hash(info.hash, iterator_plus_h_n); + std::copy(h_n.begin(), h_n.end(), iterator_plus_h_n.begin() + 4); + } + + // H_final = H(H_n + block) + auto h_n_plus_block = h_n; + const std::uint32_t block_number = 0; + h_n_plus_block.insert(h_n_plus_block.end(), + reinterpret_cast(&block_number), + reinterpret_cast(&block_number) + sizeof(std::uint32_t)); + auto h_final = hash(info.hash, h_n_plus_block); + + // X1 = H(h_final ^ 0x36) + std::vector buffer(64, 0x36); + for (std::size_t i = 0; i < h_final.size(); ++i) + { + buffer[i] = static_cast(0x36 ^ h_final[i]); + } + auto X1 = hash(info.hash, buffer); + + // X2 = H(h_final ^ 0x5C) + buffer.assign(64, 0x5c); + for (std::size_t i = 0; i < h_final.size(); ++i) + { + buffer[i] = static_cast(0x5c ^ h_final[i]); + } + auto X2 = hash(info.hash, buffer); + + auto X3 = X1; + X3.insert(X3.end(), X2.begin(), X2.end()); + + auto key_derived = std::vector(X3.begin(), X3.begin() + static_cast(info.key_bytes)); + + //todo: verify here + + std::size_t package_offset = 0; + auto decrypted_size = read_int(package_offset, encrypted_package); + auto decrypted = aes(key_derived, {}, std::vector( + encrypted_package.begin() + 8, encrypted_package.end()), + cipher_chaining::ecb, cipher_direction::decryption); + decrypted.resize(decrypted_size); + + return decrypted; + } + + struct agile_encryption_info + { + // key data + struct + { + std::size_t salt_size; + std::size_t block_size; + std::size_t key_bits; + std::size_t hash_size; + std::string cipher_algorithm; + std::string cipher_chaining; + std::string hash_algorithm; + std::vector salt_value; + } key_data; + + struct + { + std::vector hmac_key; + std::vector hmac_value; + } data_integrity; + + struct + { + std::size_t spin_count; + std::size_t salt_size; + std::size_t block_size; + std::size_t key_bits; + std::size_t hash_size; + std::string cipher_algorithm; + std::string cipher_chaining; + hash_algorithm hash; + std::vector salt_value; + std::vector verifier_hash_input; + std::vector verifier_hash_value; + std::vector encrypted_key_value; + } key_encryptor; + }; + + static std::vector decrypt_xlsx_agile(const std::vector &encryption_info, + const std::string &password, const std::vector &encrypted_package) + { + static const auto xmlns = std::string("http://schemas.microsoft.com/office/2006/encryption"); + static const auto xmlns_p = std::string("http://schemas.microsoft.com/office/2006/keyEncryptor/password"); + static const auto xmlns_c = std::string("http://schemas.microsoft.com/office/2006/keyEncryptor/certificate"); + + agile_encryption_info result; + + xml::parser parser(encryption_info.data(), encryption_info.size(), "EncryptionInfo"); + + parser.next_expect(xml::parser::event_type::start_element, xmlns, "encryption"); + + parser.next_expect(xml::parser::event_type::start_element, xmlns, "keyData"); + result.key_data.salt_size = parser.attribute("saltSize"); + result.key_data.block_size = parser.attribute("blockSize"); + result.key_data.key_bits = parser.attribute("keyBits"); + result.key_data.hash_size = parser.attribute("hashSize"); + result.key_data.cipher_algorithm = parser.attribute("cipherAlgorithm"); + result.key_data.cipher_chaining = parser.attribute("cipherChaining"); + result.key_data.hash_algorithm = parser.attribute("hashAlgorithm"); + result.key_data.salt_value = decode_base64(parser.attribute("saltValue")); + parser.next_expect(xml::parser::event_type::end_element, xmlns, "keyData"); + + parser.next_expect(xml::parser::event_type::start_element, xmlns, "dataIntegrity"); + result.data_integrity.hmac_key = decode_base64(parser.attribute("encryptedHmacKey")); + result.data_integrity.hmac_value = decode_base64(parser.attribute("encryptedHmacValue")); + parser.next_expect(xml::parser::event_type::end_element, xmlns, "dataIntegrity"); + + parser.next_expect(xml::parser::event_type::start_element, xmlns, "keyEncryptors"); + parser.next_expect(xml::parser::event_type::start_element, xmlns, "keyEncryptor"); + parser.attribute("uri"); + bool any_password_key = false; + + while (parser.peek() != xml::parser::event_type::end_element) + { + parser.next_expect(xml::parser::event_type::start_element); + + if (parser.namespace_() == xmlns_p && parser.name() == "encryptedKey") + { + any_password_key = true; + result.key_encryptor.spin_count = parser.attribute("spinCount"); + result.key_encryptor.salt_size = parser.attribute("saltSize"); + result.key_encryptor.block_size = parser.attribute("blockSize"); + result.key_encryptor.key_bits = parser.attribute("keyBits"); + result.key_encryptor.hash_size = parser.attribute("hashSize"); + result.key_encryptor.cipher_algorithm = parser.attribute("cipherAlgorithm"); + result.key_encryptor.cipher_chaining = parser.attribute("cipherChaining"); + + auto hash_algorithm_string = parser.attribute("hashAlgorithm"); + if (hash_algorithm_string == "SHA512") + { + result.key_encryptor.hash = hash_algorithm::sha512; + } + else if (hash_algorithm_string == "SHA1") + { + result.key_encryptor.hash = hash_algorithm::sha1; + } + else if (hash_algorithm_string == "SHA256") + { + result.key_encryptor.hash = hash_algorithm::sha256; + } + else if (hash_algorithm_string == "SHA384") + { + result.key_encryptor.hash = hash_algorithm::sha384; + } + + result.key_encryptor.salt_value = decode_base64(parser.attribute("saltValue")); + result.key_encryptor.verifier_hash_input = decode_base64(parser.attribute("encryptedVerifierHashInput")); + result.key_encryptor.verifier_hash_value = decode_base64(parser.attribute("encryptedVerifierHashValue")); + result.key_encryptor.encrypted_key_value = decode_base64(parser.attribute("encryptedKeyValue")); + } + else + { + throw xlnt::unsupported("other encryption key types not supported"); + } + + parser.next_expect(xml::parser::event_type::end_element); + } + + if (!any_password_key) + { + throw "no password key in keyEncryptors"; + } + + parser.next_expect(xml::parser::event_type::end_element, xmlns, "keyEncryptor"); + parser.next_expect(xml::parser::event_type::end_element, xmlns, "keyEncryptors"); + + parser.next_expect(xml::parser::event_type::end_element, xmlns, "encryption"); + + + // begin key generation algorithm + + // H_0 = H(salt + password) + auto salt_plus_password = result.key_encryptor.salt_value; + std::vector password_wide(password.begin(), password.end()); + + std::for_each(password_wide.begin(), password_wide.end(), + [&salt_plus_password](std::uint16_t c) + { + salt_plus_password.insert(salt_plus_password.end(), + reinterpret_cast(&c), + reinterpret_cast(&c) + sizeof(std::uint16_t)); + }); + + auto h_0 = hash(result.key_encryptor.hash, salt_plus_password); + + // H_n = H(iterator + H_n-1) + std::vector iterator_plus_h_n(4, 0); + iterator_plus_h_n.insert(iterator_plus_h_n.end(), h_0.begin(), h_0.end()); + std::uint32_t &iterator = *reinterpret_cast(iterator_plus_h_n.data()); + std::vector h_n; + + for (iterator = 0; iterator < result.key_encryptor.spin_count; ++iterator) + { + h_n = hash(result.key_encryptor.hash, iterator_plus_h_n); + std::copy(h_n.begin(), h_n.end(), iterator_plus_h_n.begin() + 4); + } + + static const std::size_t block_size = 8; + + auto calculate_block = [&result]( + const std::vector &raw_key, + const std::array &block, + const std::vector &encrypted) + { + auto combined = raw_key; + combined.insert(combined.end(), block.begin(), block.end()); + auto key = hash(result.key_encryptor.hash, combined); + key.resize(result.key_encryptor.key_bits / 8); + return aes(key, result.key_encryptor.salt_value, encrypted, + cipher_chaining::cbc, cipher_direction::decryption); + }; + + const std::array input_block_key + = { {0xfe, 0xa7, 0xd2, 0x76, 0x3b, 0x4b, 0x9e, 0x79} }; + auto hash_input = calculate_block(h_n, input_block_key, + result.key_encryptor.verifier_hash_input); + auto calculated_verifier = hash(result.key_encryptor.hash, hash_input); + + const std::array verifier_block_key + = { {0xd7, 0xaa, 0x0f, 0x6d, 0x30, 0x61, 0x34, 0x4e} }; + auto expected_verifier = calculate_block(h_n, verifier_block_key, + result.key_encryptor.verifier_hash_value); + + if (calculated_verifier.size() != expected_verifier.size() + || std::mismatch(calculated_verifier.begin(), calculated_verifier.end(), + expected_verifier.begin(), expected_verifier.end()) + != std::make_pair(calculated_verifier.end(), expected_verifier.end())) + { + throw xlnt::exception("bad password"); + } + + const std::array key_value_block_key + = { {0x14, 0x6e, 0x0b, 0xe7, 0xab, 0xac, 0xd0, 0xd6} }; + auto key = calculate_block(h_n, key_value_block_key, + result.key_encryptor.encrypted_key_value); + + auto salt_size = result.key_data.salt_size; + auto salt_with_block_key = result.key_data.salt_value; + salt_with_block_key.resize(salt_size + sizeof(std::uint32_t), 0); + + auto &segment = *reinterpret_cast(salt_with_block_key.data() + salt_size); + auto total_size = static_cast(*reinterpret_cast(encrypted_package.data())); + + std::vector encrypted_segment(segment_length, 0); + std::vector decrypted_package; + decrypted_package.reserve(encrypted_package.size() - 8); + + for (std::size_t i = 8; i < encrypted_package.size(); i += segment_length) + { + auto iv = hash(result.key_encryptor.hash, salt_with_block_key); + iv.resize(16); + + auto decrypted_segment = aes(key, iv, std::vector( + encrypted_package.begin() + static_cast(i), + encrypted_package.begin() + static_cast(i) + segment_length), + cipher_chaining::cbc, cipher_direction::decryption); + + decrypted_package.insert(decrypted_package.end(), + decrypted_segment.begin(), decrypted_segment.end()); + + ++segment; + } + + decrypted_package.resize(total_size); + + return decrypted_package; + } + + static std::vector decrypt_xlsx(const std::vector &bytes, const std::string &password) + { + if (bytes.empty()) + { + throw xlnt::exception("empty file"); + } + + std::vector as_chars(bytes.begin(), bytes.end()); + POLE::Storage storage(as_chars.data(), static_cast(bytes.size())); + + if (!storage.open()) + { + throw xlnt::exception("not an ole compound file"); + } + + auto encrypted_package = get_file(storage, "EncryptedPackage"); + auto encryption_info = get_file(storage, "EncryptionInfo"); + + std::size_t index = 0; + + auto version_major = read_int(index, encryption_info); + auto version_minor = read_int(index, encryption_info); + auto encryption_flags = read_int(index, encryption_info); + + // get rid of header + encryption_info.erase(encryption_info.begin(), + encryption_info.begin() + static_cast(index)); + + // version 4.4 is agile + if (version_major == 4 && version_minor == 4) + { + if (encryption_flags != 0x40) + { + throw xlnt::exception("bad header"); + } + + return decrypt_xlsx_agile(encryption_info, password, encrypted_package); + } + + // not agile, only try to decrypt versions 3.2 and 4.2 + if (version_minor != 2 + || (version_major != 2 && version_major != 3 && version_major != 4)) + { + throw xlnt::exception("unsupported encryption version"); + } + + if ((encryption_flags & 0b00000011) != 0) // Reserved1 and Reserved2, MUST be 0 + { + throw xlnt::exception("bad header"); + } + + if ((encryption_flags & 0b00000100) == 0 // fCryptoAPI + || (encryption_flags & 0b00010000) != 0) // fExternal + { + throw xlnt::exception("extensible encryption is not supported"); + } + + if ((encryption_flags & 0b00100000) == 0) // fAES + { + throw xlnt::exception("not an OOXML document"); + } + + return decrypt_xlsx_standard(encryption_info, password, encrypted_package); + } }; -enum class cipher_chaining -{ - ecb, // electronic code book - cbc, // cipher block chaining - cfb // cipher feedback chaining -}; - -enum class cipher_direction -{ - encryption, - decryption -}; - -enum class hash_algorithm -{ - sha1, - sha256, - sha384, - sha512, - md5, - md4, - md2, - ripemd128, - ripemd160, - whirlpool -}; - -std::vector aes(const std::vector &key, - const std::vector &iv, - const std::vector &encrypted, - cipher_chaining chaining, cipher_direction direction) -{ - std::string cipher_name("AES-"); - cipher_name.append(std::to_string(key.size() * 8)); - cipher_name.append(chaining == cipher_chaining::ecb - ? "/ECB/NoPadding" : "/CBC/NoPadding"); - - auto botan_direction = direction == cipher_direction::decryption - ? Botan::DECRYPTION : Botan::ENCRYPTION; - Botan::Pipe pipe(Botan::get_cipher(cipher_name, key, iv, botan_direction)); - pipe.process_msg(encrypted); - auto decrypted = pipe.read_all(); - - return std::vector(decrypted.begin(), decrypted.end()); -} - -std::vector decode_base64(const std::string &encoded) -{ - Botan::Pipe pipe(new Botan::Base64_Decoder); - pipe.process_msg(encoded); - auto decoded = pipe.read_all(); - - return std::vector(decoded.begin(), decoded.end()); -}; - -std::vector hash(hash_algorithm algorithm, - const std::vector &input) -{ - Botan::Pipe pipe(new Botan::Hash_Filter( - algorithm == hash_algorithm::sha512 ? "SHA-512" : "SHA-1")); - pipe.process_msg(input); - auto hash = pipe.read_all(); - - return std::vector(hash.begin(), hash.end()); -}; - -std::vector get_file(POLE::Storage &storage, const std::string &name) -{ - POLE::Stream stream(&storage, name.c_str()); - if (stream.fail()) return {}; - std::vector bytes(stream.size(), 0); - stream.read(bytes.data(), static_cast(bytes.size())); - return bytes; -} - -template -auto read_int(std::size_t &index, const std::vector &raw_data) -{ - auto result = *reinterpret_cast(&raw_data[index]); - index += sizeof(T); - - return result; -}; - -struct standard_encryption_info -{ - const std::size_t spin_count = 50000; - std::size_t block_size; - std::size_t key_bits; - std::size_t key_bytes; - std::size_t hash_size; - cipher_algorithm cipher; - cipher_chaining chaining; - const hash_algorithm hash = hash_algorithm::sha1; - std::vector salt_value; - std::vector verifier_hash_input; - std::vector verifier_hash_value; - std::vector encrypted_key_value; -}; - -std::vector decrypt_xlsx_standard(const std::vector &encryption_info, - const std::string &password, const std::vector &encrypted_package) -{ - std::size_t offset = 0; - - standard_encryption_info info; - - auto header_length = read_int(offset, encryption_info); - auto index_at_start = offset; - /*auto skip_flags = */read_int(offset, encryption_info); - /*auto size_extra = */read_int(offset, encryption_info); - auto alg_id = read_int(offset, encryption_info); - - if (alg_id == 0 || alg_id == 0x0000660E || alg_id == 0x0000660F || alg_id == 0x00006610) - { - info.cipher = cipher_algorithm::aes; - } - else - { - throw xlnt::exception("invalid cipher algorithm"); - } - - auto alg_id_hash = read_int(offset, encryption_info); - if (alg_id_hash != 0x00008004 && alg_id_hash == 0) - { - throw xlnt::exception("invalid hash algorithm"); - } - - info.key_bits = read_int(offset, encryption_info); - info.key_bytes = info.key_bits / 8; - - auto provider_type = read_int(offset, encryption_info); - if (provider_type != 0 && provider_type != 0x00000018) - { - throw xlnt::exception("invalid provider type"); - } - - read_int(offset, encryption_info); // reserved 1 - if (read_int(offset, encryption_info) != 0) // reserved 2 - { - throw xlnt::exception("invalid header"); - } - - const auto csp_name_length = header_length - (offset - index_at_start); - std::vector csp_name_wide( - reinterpret_cast(&*(encryption_info.begin() + offset)), - reinterpret_cast(&*(encryption_info.begin() + offset + csp_name_length))); - std::string csp_name(csp_name_wide.begin(), csp_name_wide.end() - 1); // without trailing null - if (csp_name != "Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)" - && csp_name != "Microsoft Enhanced RSA and AES Cryptographic Provider") - { - throw xlnt::exception("invalid cryptographic provider"); - } - offset += csp_name_length; - - const auto salt_size = read_int(offset, encryption_info); - std::vector salt(encryption_info.begin() + offset, - encryption_info.begin() + offset + salt_size); - offset += salt_size; - - static const auto verifier_size = std::size_t(16); - std::vector verifier_hash_input(encryption_info.begin() + offset, - encryption_info.begin() + offset + verifier_size); - offset += verifier_size; - - const auto verifier_hash_size = read_int(offset, encryption_info); - std::vector verifier_hash_value(encryption_info.begin() + offset, - encryption_info.begin() + offset + 32); - offset += verifier_hash_size; - - // begin key generation algorithm - - // H_0 = H(salt + password) - auto salt_plus_password = salt; - std::vector password_wide(password.begin(), password.end()); - std::for_each(password_wide.begin(), password_wide.end(), - [&salt_plus_password](std::uint16_t c) - { - salt_plus_password.insert(salt_plus_password.end(), - reinterpret_cast(&c), - reinterpret_cast(&c) + sizeof(std::uint16_t)); - }); - std::vector h_0 = hash(info.hash, salt_plus_password); - - // H_n = H(iterator + H_n-1) - std::vector iterator_plus_h_n(4, 0); - iterator_plus_h_n.insert(iterator_plus_h_n.end(), h_0.begin(), h_0.end()); - std::uint32_t &iterator = *reinterpret_cast(iterator_plus_h_n.data()); - std::vector h_n; - for (iterator = 0; iterator < info.spin_count; ++iterator) - { - h_n = hash(info.hash, iterator_plus_h_n); - std::copy(h_n.begin(), h_n.end(), iterator_plus_h_n.begin() + 4); - } - - // H_final = H(H_n + block) - auto h_n_plus_block = h_n; - const std::uint32_t block_number = 0; - h_n_plus_block.insert(h_n_plus_block.end(), - reinterpret_cast(&block_number), - reinterpret_cast(&block_number) + sizeof(std::uint32_t)); - auto h_final = hash(info.hash, h_n_plus_block); - - // X1 = H(h_final ^ 0x36) - std::vector buffer(64, 0x36); - for (std::size_t i = 0; i < h_final.size(); ++i) - { - buffer[i] = static_cast(0x36 ^ h_final[i]); - } - auto X1 = hash(info.hash, buffer); - - // X2 = H(h_final ^ 0x5C) - buffer.assign(64, 0x5c); - for (std::size_t i = 0; i < h_final.size(); ++i) - { - buffer[i] = static_cast(0x5c ^ h_final[i]); - } - auto X2 = hash(info.hash, buffer); - - auto X3 = X1; - X3.insert(X3.end(), X2.begin(), X2.end()); - - auto key_derived = std::vector(X3.begin(), X3.begin() + info.key_bytes); - - //todo: verify here - - std::size_t package_offset = 0; - auto decrypted_size = read_int(package_offset, encrypted_package); - auto decrypted = aes(key_derived, {}, std::vector( - encrypted_package.begin() + 8, encrypted_package.end()), - cipher_chaining::ecb, cipher_direction::decryption); - decrypted.resize(decrypted_size); - - return decrypted; -} - - -struct agile_encryption_info -{ - // key data - struct - { - std::size_t salt_size; - std::size_t block_size; - std::size_t key_bits; - std::size_t hash_size; - std::string cipher_algorithm; - std::string cipher_chaining; - std::string hash_algorithm; - std::vector salt_value; - } key_data; - - struct - { - std::vector hmac_key; - std::vector hmac_value; - } data_integrity; - - struct - { - std::size_t spin_count; - std::size_t salt_size; - std::size_t block_size; - std::size_t key_bits; - std::size_t hash_size; - std::string cipher_algorithm; - std::string cipher_chaining; - hash_algorithm hash; - std::vector salt_value; - std::vector verifier_hash_input; - std::vector verifier_hash_value; - std::vector encrypted_key_value; - } key_encryptor; -}; - -std::vector decrypt_xlsx_agile(const std::vector &encryption_info, - const std::string &password, const std::vector &encrypted_package) -{ - static const auto xmlns = std::string("http://schemas.microsoft.com/office/2006/encryption"); - static const auto xmlns_p = std::string("http://schemas.microsoft.com/office/2006/keyEncryptor/password"); - static const auto xmlns_c = std::string("http://schemas.microsoft.com/office/2006/keyEncryptor/certificate"); - - agile_encryption_info result; - - xml::parser parser(encryption_info.data(), encryption_info.size(), "EncryptionInfo"); - - parser.next_expect(xml::parser::event_type::start_element, xmlns, "encryption"); - - parser.next_expect(xml::parser::event_type::start_element, xmlns, "keyData"); - result.key_data.salt_size = parser.attribute("saltSize"); - result.key_data.block_size = parser.attribute("blockSize"); - result.key_data.key_bits = parser.attribute("keyBits"); - result.key_data.hash_size = parser.attribute("hashSize"); - result.key_data.cipher_algorithm = parser.attribute("cipherAlgorithm"); - result.key_data.cipher_chaining = parser.attribute("cipherChaining"); - result.key_data.hash_algorithm = parser.attribute("hashAlgorithm"); - result.key_data.salt_value = decode_base64(parser.attribute("saltValue")); - parser.next_expect(xml::parser::event_type::end_element, xmlns, "keyData"); - - parser.next_expect(xml::parser::event_type::start_element, xmlns, "dataIntegrity"); - result.data_integrity.hmac_key = decode_base64(parser.attribute("encryptedHmacKey")); - result.data_integrity.hmac_value = decode_base64(parser.attribute("encryptedHmacValue")); - parser.next_expect(xml::parser::event_type::end_element, xmlns, "dataIntegrity"); - - parser.next_expect(xml::parser::event_type::start_element, xmlns, "keyEncryptors"); - parser.next_expect(xml::parser::event_type::start_element, xmlns, "keyEncryptor"); - parser.attribute("uri"); - bool any_password_key = false; - - while (parser.peek() != xml::parser::event_type::end_element) - { - parser.next_expect(xml::parser::event_type::start_element); - - if (parser.namespace_() == xmlns_p && parser.name() == "encryptedKey") - { - any_password_key = true; - result.key_encryptor.spin_count = parser.attribute("spinCount"); - result.key_encryptor.salt_size = parser.attribute("saltSize"); - result.key_encryptor.block_size = parser.attribute("blockSize"); - result.key_encryptor.key_bits = parser.attribute("keyBits"); - result.key_encryptor.hash_size = parser.attribute("hashSize"); - result.key_encryptor.cipher_algorithm = parser.attribute("cipherAlgorithm"); - result.key_encryptor.cipher_chaining = parser.attribute("cipherChaining"); - - auto hash_algorithm_string = parser.attribute("hashAlgorithm"); - if (hash_algorithm_string == "SHA512") - { - result.key_encryptor.hash = hash_algorithm::sha512; - } - else if (hash_algorithm_string == "SHA1") - { - result.key_encryptor.hash = hash_algorithm::sha1; - } - else if (hash_algorithm_string == "SHA256") - { - result.key_encryptor.hash = hash_algorithm::sha256; - } - else if (hash_algorithm_string == "SHA384") - { - result.key_encryptor.hash = hash_algorithm::sha384; - } - - result.key_encryptor.salt_value = decode_base64(parser.attribute("saltValue")); - result.key_encryptor.verifier_hash_input = decode_base64(parser.attribute("encryptedVerifierHashInput")); - result.key_encryptor.verifier_hash_value = decode_base64(parser.attribute("encryptedVerifierHashValue")); - result.key_encryptor.encrypted_key_value = decode_base64(parser.attribute("encryptedKeyValue")); - } - else - { - throw xlnt::unsupported("other encryption key types not supported"); - } - - parser.next_expect(xml::parser::event_type::end_element); - } - - if (!any_password_key) - { - throw "no password key in keyEncryptors"; - } - - parser.next_expect(xml::parser::event_type::end_element, xmlns, "keyEncryptor"); - parser.next_expect(xml::parser::event_type::end_element, xmlns, "keyEncryptors"); - - parser.next_expect(xml::parser::event_type::end_element, xmlns, "encryption"); - - - // begin key generation algorithm - - // H_0 = H(salt + password) - auto salt_plus_password = result.key_encryptor.salt_value; - std::vector password_wide(password.begin(), password.end()); - - std::for_each(password_wide.begin(), password_wide.end(), - [&salt_plus_password](std::uint16_t c) - { - salt_plus_password.insert(salt_plus_password.end(), - reinterpret_cast(&c), - reinterpret_cast(&c) + sizeof(std::uint16_t)); - }); - - auto h_0 = hash(result.key_encryptor.hash, salt_plus_password); - - // H_n = H(iterator + H_n-1) - std::vector iterator_plus_h_n(4, 0); - iterator_plus_h_n.insert(iterator_plus_h_n.end(), h_0.begin(), h_0.end()); - std::uint32_t &iterator = *reinterpret_cast(iterator_plus_h_n.data()); - std::vector h_n; - - for (iterator = 0; iterator < result.key_encryptor.spin_count; ++iterator) - { - h_n = hash(result.key_encryptor.hash, iterator_plus_h_n); - std::copy(h_n.begin(), h_n.end(), iterator_plus_h_n.begin() + 4); - } - - static const std::size_t block_size = 8; - - auto calculate_block = [&result]( - const std::vector &raw_key, - const std::array &block, - const std::vector &encrypted) - { - auto combined = raw_key; - combined.insert(combined.end(), block.begin(), block.end()); - auto key = hash(result.key_encryptor.hash, combined); - key.resize(result.key_encryptor.key_bits / 8); - return aes(key, result.key_encryptor.salt_value, encrypted, - cipher_chaining::cbc, cipher_direction::decryption); - }; - - const std::array input_block_key - = { {0xfe, 0xa7, 0xd2, 0x76, 0x3b, 0x4b, 0x9e, 0x79} }; - auto hash_input = calculate_block(h_n, input_block_key, - result.key_encryptor.verifier_hash_input); - auto calculated_verifier = hash(result.key_encryptor.hash, hash_input); - - const std::array verifier_block_key - = { {0xd7, 0xaa, 0x0f, 0x6d, 0x30, 0x61, 0x34, 0x4e} }; - auto expected_verifier = calculate_block(h_n, verifier_block_key, - result.key_encryptor.verifier_hash_value); - - if (calculated_verifier.size() != expected_verifier.size() - || std::mismatch(calculated_verifier.begin(), calculated_verifier.end(), - expected_verifier.begin(), expected_verifier.end()) - != std::make_pair(calculated_verifier.end(), expected_verifier.end())) - { - throw xlnt::exception("bad password"); - } - - const std::array key_value_block_key - = { {0x14, 0x6e, 0x0b, 0xe7, 0xab, 0xac, 0xd0, 0xd6} }; - auto key = calculate_block(h_n, key_value_block_key, - result.key_encryptor.encrypted_key_value); - - auto salt_size = result.key_data.salt_size; - auto salt_with_block_key = result.key_data.salt_value; - salt_with_block_key.resize(salt_size + sizeof(std::uint32_t), 0); - - auto &segment = *reinterpret_cast(salt_with_block_key.data() + salt_size); - auto total_size = static_cast(*reinterpret_cast(encrypted_package.data())); - - std::vector encrypted_segment(segment_length, 0); - std::vector decrypted_package; - decrypted_package.reserve(encrypted_package.size() - 8); - - for (std::size_t i = 8; i < encrypted_package.size(); i += segment_length) - { - auto iv = hash(result.key_encryptor.hash, salt_with_block_key); - iv.resize(16); - - auto decrypted_segment = aes(key, iv, std::vector( - encrypted_package.begin() + i, encrypted_package.begin() + i + segment_length), - cipher_chaining::cbc, cipher_direction::decryption); - decrypted_package.insert(decrypted_package.end(), - decrypted_segment.begin(), decrypted_segment.end()); - - ++segment; - } - - decrypted_package.resize(total_size); - - return decrypted_package; -} - -std::vector decrypt_xlsx(const std::vector &bytes, const std::string &password) -{ - if (bytes.empty()) - { - throw xlnt::exception("empty file"); - } - - std::vector as_chars(bytes.begin(), bytes.end()); - POLE::Storage storage(as_chars.data(), static_cast(bytes.size())); - - if (!storage.open()) - { - throw xlnt::exception("not an ole compound file"); - } - - auto encrypted_package = get_file(storage, "EncryptedPackage"); - auto encryption_info = get_file(storage, "EncryptionInfo"); - - std::size_t index = 0; - - auto version_major = read_int(index, encryption_info); - auto version_minor = read_int(index, encryption_info); - auto encryption_flags = read_int(index, encryption_info); - - // get rid of header - encryption_info.erase(encryption_info.begin(), encryption_info.begin() + index); - - // version 4.4 is agile - if (version_major == 4 && version_minor == 4) - { - if (encryption_flags != 0x40) - { - throw xlnt::exception("bad header"); - } - - return decrypt_xlsx_agile(encryption_info, password, encrypted_package); - } - - // not agile, only try to decrypt versions 3.2 and 4.2 - if (version_minor != 2 - || (version_major != 2 && version_major != 3 && version_major != 4)) - { - throw xlnt::exception("unsupported encryption version"); - } - - if ((encryption_flags & 0b00000011) != 0) // Reserved1 and Reserved2, MUST be 0 - { - throw xlnt::exception("bad header"); - } - - if ((encryption_flags & 0b00000100) == 0 // fCryptoAPI - || (encryption_flags & 0b00010000) != 0) // fExternal - { - throw xlnt::exception("extensible encryption is not supported"); - } - - if ((encryption_flags & 0b00100000) == 0) // fAES - { - throw xlnt::exception("not an OOXML document"); - } - - return decrypt_xlsx_standard(encryption_info, password, encrypted_package); -} - void xlsx_consumer::read(std::istream &source, const std::string &password) { std::vector data((std::istreambuf_iterator(source)), (std::istreambuf_iterator())); - const auto decrypted = decrypt_xlsx(data, password); + const auto decrypted = crypto_helper::decrypt_xlsx(data, password); vector_istreambuf decrypted_buffer(decrypted); std::istream decrypted_stream(&decrypted_buffer); read(decrypted_stream); diff --git a/source/detail/xlsx_producer.cpp b/source/detail/xlsx_producer.cpp index e76639d2..7bf00f9f 100644 --- a/source/detail/xlsx_producer.cpp +++ b/source/detail/xlsx_producer.cpp @@ -24,10 +24,12 @@ #include // for std::accumulate #include -#include -#include #include +#include +#include #include +#include +#include #include #include #include @@ -84,7 +86,7 @@ xlsx_producer::xlsx_producer(const workbook &target) : source_(target) void xlsx_producer::write(std::ostream &destination) { - Partio::ZipFileWriter archive(destination); + ZipFileWriter archive(destination); archive_ = &archive; populate_archive(); } @@ -100,6 +102,13 @@ void xlsx_producer::populate_archive() for (auto &rel : root_rels) { + // thumbnail is binary content so we don't want to open an xml serializer stream + if (rel.get_type() == relationship::type::thumbnail) + { + write_thumbnail(rel); + continue; + } + begin_part(rel.get_target().get_path()); switch (rel.get_type()) @@ -119,10 +128,6 @@ void xlsx_producer::populate_archive() case relationship::type::office_document: write_workbook(rel); break; - - case relationship::type::thumbnail: - write_thumbnail(rel); - break; default: break; @@ -154,7 +159,7 @@ void xlsx_producer::begin_part(const path &part) { end_part(); - current_part_stream_.reset(archive_->Add_File(part.string(), true)); + current_part_stream_.reset(archive_->open(part.string())); current_part_serializer_.reset(new xml::serializer(*current_part_stream_, part.string())); } @@ -2498,11 +2503,12 @@ void xlsx_producer::write_unknown_relationships() void xlsx_producer::write_thumbnail(const relationship &rel) { + end_part(); + const auto &thumbnail = source_.get_thumbnail(); - std::unique_ptr thumbnail_stream( - archive_->Add_File(rel.get_target().get_path().string(), true)); - std::for_each(thumbnail.begin(), thumbnail.end(), - [&thumbnail_stream](std::uint8_t b) { *thumbnail_stream << b; }); + current_part_stream_.reset(archive_->open(rel.get_target().get_path().string())); + vector_istreambuf thumbnail_buffer(thumbnail); + *current_part_stream_ << &thumbnail_buffer; } xml::serializer &xlsx_producer::serializer() diff --git a/source/detail/xlsx_producer.hpp b/source/detail/xlsx_producer.hpp index f7369b05..dbe43309 100644 --- a/source/detail/xlsx_producer.hpp +++ b/source/detail/xlsx_producer.hpp @@ -28,11 +28,6 @@ #include #include -#include - -namespace Partio { -class ZipFileWriter; -} namespace xml { class serializer; @@ -49,6 +44,8 @@ class worksheet; namespace detail { +class ZipFileWriter; + /// /// Handles writing a workbook into an XLSX file. /// @@ -138,7 +135,7 @@ private: /// const workbook &source_; - Partio::ZipFileWriter *archive_; + ZipFileWriter *archive_; std::unique_ptr current_part_stream_; std::unique_ptr current_part_serializer_; }; diff --git a/source/detail/zip.cpp b/source/detail/zip.cpp index 6b7c1333..5441e3b5 100644 --- a/source/detail/zip.cpp +++ b/source/detail/zip.cpp @@ -33,10 +33,6 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. */ -extern "C"{ -#include -} - #include #include #include @@ -46,123 +42,129 @@ extern "C"{ #include #include +#include + #include -namespace Partio{ +namespace xlnt { +namespace detail { template -inline void Swap_Endianity(T& x) +inline T read_int(std::istream &stream) { - assert(sizeof(T)<=8); - if(sizeof(T)>1) { - T old=x; - for(unsigned int k=1;k<=sizeof(T);k++) ((char*)&x)[k-1]=((char*)&old)[sizeof(T)-k]; - } + T value; + stream.read(reinterpret_cast(&value), sizeof(T)); + return value; } template -inline void Read_Primitive(std::istream& stream,T& x) +inline void write_int(std::ostream &stream, T value) { - stream.read(&(char&)x,sizeof(T)); + stream.write(reinterpret_cast(&value), sizeof(T)); } -template -inline void Write_Primitive(std::ostream& stream,const T& x) +struct zip_file_header { - stream.write(&(char&)x,sizeof(T)); -} - -//##################################################################### -// class ZipFileHeader -//##################################################################### -struct ZipFileHeader -{ - unsigned short version; - unsigned short flags; - unsigned short compression_type; - unsigned short stamp_date,stamp_time; - unsigned int crc; - unsigned int compressed_size,uncompressed_size; + std::uint16_t version = 20; + std::uint16_t flags = 0; + std::uint16_t compression_type = 8; + std::uint16_t stamp_date,stamp_time = 0; + std::uint32_t crc = 0; + std::uint32_t compressed_size = 0; + std::uint32_t uncompressed_size = 0; std::string filename; - unsigned int header_offset; // local header offset + std::string comment; + std::vector extra; + std::uint32_t header_offset = 0; // local header offset - ZipFileHeader() - {} + zip_file_header() + { + } - ZipFileHeader(const std::string& filename_input) - :version(20),flags(0),compression_type(8),stamp_date(0),stamp_time(0),crc(0), - compressed_size(0),uncompressed_size(0),filename(filename_input),header_offset(0) - {} + bool read(std::istream& istream,const bool global) + { + auto sig = read_int(istream); - bool Read(std::istream& istream,const bool global) - {unsigned int sig; - unsigned short version,flags; - // read and check for local/global magic - if(global){ - Read_Primitive(istream,sig); - if(sig!=0x02014b50){std::cerr<<"Did not find global header signature"<(istream); + } + else if(sig!=0x04034b50) + { + std::cerr<<"Did not find local header signature"<(istream); + flags = read_int(istream); + compression_type = read_int(istream); + stamp_date = read_int(istream); + stamp_time = read_int(istream); + crc = read_int(istream); + compressed_size = read_int(istream); + uncompressed_size = read_int(istream); + + auto filename_length = read_int(istream); + auto extra_length = read_int(istream); + + std::uint16_t comment_length = 0; + + if(global) + { + comment_length = read_int(istream); + /*std::uint16_t disk_number_start = */read_int(istream); + /*std::uint16_t int_file_attrib = */read_int(istream); + /*std::uint32_t ext_file_attrib = */read_int(istream); + header_offset = read_int(istream); + } + + filename.resize(filename_length, '\0'); + istream.read(&filename[0], filename_length); + + extra.resize(extra_length, 0); + istream.read(reinterpret_cast(extra.data()), extra_length); + + if (global) + { + comment.resize(comment_length, '\0'); + istream.read(&comment[0], comment_length); + } + + return true; + } void Write(std::ostream& ostream,const bool global) const {if(global){ - Write_Primitive(ostream,(unsigned int)0x02014b50); // header sig - Write_Primitive(ostream,(unsigned short)00);} // version made by - else Write_Primitive(ostream,(unsigned int)0x04034b50); - Write_Primitive(ostream,version); - Write_Primitive(ostream,flags); - Write_Primitive(ostream,compression_type); - Write_Primitive(ostream,stamp_date); - Write_Primitive(ostream,stamp_time); - Write_Primitive(ostream,crc); - Write_Primitive(ostream,compressed_size); - Write_Primitive(ostream,uncompressed_size); - Write_Primitive(ostream,(unsigned short)filename.length()); - Write_Primitive(ostream,(unsigned short)0); // extra lengthx + write_int(ostream,(unsigned int)0x02014b50); // header sig + write_int(ostream,(unsigned short)00);} // version made by + else write_int(ostream,(unsigned int)0x04034b50); + write_int(ostream,version); + write_int(ostream,flags); + write_int(ostream,compression_type); + write_int(ostream,stamp_date); + write_int(ostream,stamp_time); + write_int(ostream,crc); + write_int(ostream,compressed_size); + write_int(ostream,uncompressed_size); + write_int(ostream,(unsigned short)filename.length()); + write_int(ostream,(unsigned short)0); // extra lengthx if(global){ - Write_Primitive(ostream,(unsigned short)0); // filecomment - Write_Primitive(ostream,(unsigned short)0); // disk# start - Write_Primitive(ostream,(unsigned short)0); // internal file - Write_Primitive(ostream,(unsigned int)0); // ext final - Write_Primitive(ostream,(unsigned int)header_offset);} // rel offset - for(unsigned int i=0;iheader_offset); header->Write(ostream,false); ostream.seekp(final_position);} - else{Write_Primitive(ostream,crc);Write_Primitive(ostream,uncompressed_size);}} + else{write_int(ostream,crc);write_int(ostream,uncompressed_size);}} if(!header) delete &ostream;} protected: @@ -329,174 +325,189 @@ protected: {if(c!=EOF){*pptr()=c;pbump(1);} if(process(false)==EOF) return EOF; return c;} - -//##################################################################### }; -//##################################################################### -// Class ZIP_FILE_ISTREAM -//##################################################################### -// Class needed because istream cannot own its streambuf + class ZIP_FILE_ISTREAM:public std::istream { ZipStreambufDecompress buf; + public: - ZIP_FILE_ISTREAM(std::istream& istream,ZipFileHeader header) + ZIP_FILE_ISTREAM(std::istream& istream,zip_file_header header) :std::istream(&buf),buf(istream,header) - {} + { + } virtual ~ZIP_FILE_ISTREAM() - {} - -//##################################################################### + { + } }; -//##################################################################### -// Class ZIP_FILE_OSTREAM -//##################################################################### -// Class needed because ostream cannot own its streambuf + class ZIP_FILE_OSTREAM:public std::ostream { ZipStreambufCompress buf; + public: - ZIP_FILE_OSTREAM(ZipFileHeader* header,std::ostream& ostream) + ZIP_FILE_OSTREAM(zip_file_header* header,std::ostream& ostream) :std::ostream(&buf),buf(header,ostream) - {} + { + } virtual ~ZIP_FILE_OSTREAM() - {} - -//##################################################################### + { + } }; -//##################################################################### -// Function ZipFileWriter -//##################################################################### -ZipFileWriter:: -ZipFileWriter(std::ostream& stream) : ostream(stream) + +ZipFileWriter::ZipFileWriter(std::ostream& stream) : target_stream_(stream) { - if(!ostream) throw std::runtime_error("ZIP: Invalid file handle"); + if(!target_stream_) throw std::runtime_error("ZIP: Invalid file handle"); } -//##################################################################### -// Function ZipFileWriter -//##################################################################### -ZipFileWriter:: -~ZipFileWriter() + +ZipFileWriter::~ZipFileWriter() { // Write all file headers - std::ios::streampos final_position=ostream.tellp(); - for(unsigned int i=0;iWrite(ostream,true);delete files[i];} - std::ios::streampos central_end=ostream.tellp(); + std::ios::streampos final_position=target_stream_.tellp(); + for(unsigned int i=0;i::iterator i=filename_to_header.begin(); - for(;i!=filename_to_header.end();++i) - delete i->second; } -//##################################################################### -// Function Find_And_Read_Central_Header -//##################################################################### -bool ZipFileReader:: -Find_And_Read_Central_Header() + +bool ZipFileReader::read_central_header() { // Find the header // NOTE: this assumes the zip file header is the last thing written to file... - istream.seekg(0,std::ios_base::end); - std::ios::streampos end_position=istream.tellg(); - unsigned int max_comment_size=0xffff; // max size of header - unsigned int read_size_before_comment=22; + source_stream_.seekg(0,std::ios_base::end); + std::ios::streampos end_position=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; + if(read_start>end_position) read_start=end_position; - istream.seekg(end_position-read_start); - char *buf=new char[read_start]; + source_stream_.seekg(end_position-read_start); + std::vector buf(static_cast(read_start), '\0'); if(read_start<=0){std::cerr<<"ZIP: Invalid read buffer size"<(header_index))); + + /*auto word = */read_int(source_stream_); + auto disk_number1 = read_int(source_stream_); + auto disk_number2 = read_int(source_stream_); + + if(disk_number1 != disk_number2 || disk_number1 != 0) + { + std::cerr<<"ZIP: multiple disk zip files are not supported"<(source_stream_); // one entry in center in this disk + auto num_files_this_disk = read_int(source_stream_); // one entry in center + + if(num_files != num_files_this_disk) + { + std::cerr<<"ZIP: multi disk zip files are not supported"<(source_stream_); // size of header + auto header_offset = read_int(source_stream_); // offset to header + // go to header and read all file headers - istream.seekg(header_offset); - for(int i=0;iRead(istream,true); - if(valid) filename_to_header[header->filename]=header;} + source_stream_.seekg(header_offset); + + for (std::uint16_t i = 0; i < num_files; ++i) + { + zip_file_header header; + + if (header.read(source_stream_, true)) + { + file_headers_[header.filename] = header; + } + } + return true; } -//##################################################################### -// Function Get_File -//##################################################################### -std::istream* ZipFileReader::Get_File(const std::string& filename,const bool binary) + +std::istream &ZipFileReader::open(const std::string &filename) { - std::map::iterator i=filename_to_header.find(filename); - if(i!=filename_to_header.end()){ - ZipFileHeader* header=i->second; - istream.seekg((*header).header_offset);return new ZIP_FILE_ISTREAM(istream,*header); + if (!has_file(filename)) + { + throw "not found"; } - return 0; -} -//##################################################################### -// Function Get_File_List -//##################################################################### -void ZipFileReader::Get_File_List(std::vector& filenames) const -{ - filenames.clear(); - std::map::const_iterator i=filename_to_header.begin(); - for(;i!=filename_to_header.end();++i) - filenames.push_back(i->first); -} -//##################################################################### -// Function Has_File -//##################################################################### -bool ZipFileReader::Has_File(const std::string &filename) const -{ - return filename_to_header.find(filename) != filename_to_header.end(); + + auto header = file_headers_.at(filename); + source_stream_.seekg(header.header_offset); + read_stream_.reset(new ZIP_FILE_ISTREAM(source_stream_, header)); + + return *read_stream_; } -} // namespace Partio +std::vector ZipFileReader::files() const +{ + std::vector filenames; + std::transform(file_headers_.begin(), file_headers_.end(), std::back_inserter(filenames), + [](const std::pair &h) { return h.first; }); + return filenames; +} + +bool ZipFileReader::has_file(const std::string &filename) const +{ + return file_headers_.count(filename) != 0; +} + +} // namespace detail +} // namespace xlnt diff --git a/source/detail/zip.hpp b/source/detail/zip.hpp index 7c44ad38..1c312c2f 100644 --- a/source/detail/zip.hpp +++ b/source/detail/zip.hpp @@ -33,56 +33,46 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. */ -#ifndef __ZIP__ -#define __ZIP__ +#pragma once -#include #include -#include -#include +#include +#include #include -namespace Partio{ -struct ZipFileHeader; -//##################################################################### -// Functions Gzip_Out/Gzip_In - Create streams that read/write .gz -//##################################################################### -std::istream* Gzip_In(const std::string& filename,std::ios::openmode mode); -std::ostream* Gzip_Out(const std::string& filename,std::ios::openmode mode); -//##################################################################### -// Class ZipFileWriter -//##################################################################### +namespace xlnt { +namespace detail { + +struct zip_file_header; + class ZipFileWriter { - std::ostream &ostream; - std::vector files; public: - -//##################################################################### ZipFileWriter(std::ostream &filename); virtual ~ZipFileWriter(); - std::ostream* Add_File(const std::string& filename,const bool binary=true); -//##################################################################### + std::ostream* open(const std::string& filename); + +private: + std::ostream &target_stream_; + std::vector file_headers_; }; -//##################################################################### -// Class ZipFileReader -//##################################################################### class ZipFileReader { - std::istream &istream; public: - std::map filename_to_header; - -//##################################################################### ZipFileReader(std::istream &stream); virtual ~ZipFileReader(); - std::istream* Get_File(const std::string& filename,const bool binary=true); - void Get_File_List(std::vector& filenames) const; - bool Has_File(const std::string &filename) const; + std::istream &open(const std::string &filename); + std::vector files() const; + bool has_file(const std::string &filename) const; + private: - bool Find_And_Read_Central_Header(); -//##################################################################### + bool read_central_header(); + + std::unordered_map file_headers_; + std::istream &source_stream_; + std::unique_ptr read_stream_; }; -} -#endif + +} // namespace detail +} // namespace xlnt diff --git a/source/packaging/uri.cpp b/source/packaging/uri.cpp index c8748756..079b95c1 100644 --- a/source/packaging/uri.cpp +++ b/source/packaging/uri.cpp @@ -25,4 +25,4 @@ bool operator==(const uri &left, const uri &right) return left.to_string() == right.to_string(); } -} // namespace xlnt \ No newline at end of file +} // namespace xlnt diff --git a/source/styles/alignment.cpp b/source/styles/alignment.cpp index 61e16fe4..d6e30dbb 100644 --- a/source/styles/alignment.cpp +++ b/source/styles/alignment.cpp @@ -92,18 +92,87 @@ optional alignment::rotation() const return text_rotation_; } -std::string alignment::to_hash_string() const +bool operator==(const alignment &left, const alignment &right) { - std::string hash_string; - - hash_string.append(wrap_text_ ? "1" : "0"); - hash_string.append(shrink_to_fit_ ? "1" : "0"); - hash_string.append(std::to_string(static_cast(horizontal_))); - hash_string.append(std::to_string(static_cast(vertical_))); - hash_string.append(std::to_string(text_rotation_)); - hash_string.append(std::to_string(indent_)); + if (left.horizontal().is_set() != right.horizontal().is_set()) + { + return false; + } - return hash_string; + if (left.horizontal().is_set()) + { + if (left.horizontal().get() != right.horizontal().get()) + { + return false; + } + } + + if (left.indent().is_set() != right.indent().is_set()) + { + return false; + } + + if (left.indent().is_set()) + { + if (left.indent().get() != right.indent().get()) + { + return false; + } + } + + if (left.rotation().is_set() != right.rotation().is_set()) + { + return false; + } + + if (left.rotation().is_set()) + { + if (left.rotation().get() != right.rotation().get()) + { + return false; + } + } + + if (left.shrink().is_set() != right.shrink().is_set()) + { + return false; + } + + if (left.shrink().is_set()) + { + if (left.shrink().get() != right.shrink().get()) + { + return false; + } + } + + if (left.vertical().is_set() != right.vertical().is_set()) + { + return false; + } + + if (left.vertical().is_set()) + { + if (left.vertical().get() != right.vertical().get()) + { + return false; + } + } + + if (left.wrap().is_set() != right.wrap().is_set()) + { + return false; + } + + if (left.wrap().is_set()) + { + if (left.wrap().get() != right.wrap().get()) + { + return false; + } + } + + return true; } } // namespace xlnt diff --git a/source/styles/border.cpp b/source/styles/border.cpp index cf354b9a..4ea1dded 100644 --- a/source/styles/border.cpp +++ b/source/styles/border.cpp @@ -49,6 +49,38 @@ border::border_property &border::border_property::style(border_style s) return *this; } +bool operator==(const border::border_property &left, + const border::border_property &right) +{ + if (left.style().is_set() != right.style().is_set()) + { + return false; + } + + if (left.style().is_set()) + { + if (left.style().get() != right.style().get()) + { + return false; + } + } + + if (left.color().is_set() != right.color().is_set()) + { + return false; + } + + if (left.color().is_set()) + { + if (left.color().get() != right.color().get()) + { + return false; + } + } + + return true; +} + border::border() { } @@ -112,45 +144,25 @@ optional border::diagonal() const return diagonal_direction_; } -std::string border::to_hash_string() const +bool operator==(const border &left, const border &right) { - std::string hash_string; - - for (const auto &side_type : all_sides()) - { - hash_string.append(std::to_string(static_cast(side_type))); - - if (side(side_type)) - { - const auto side_properties = *side(side_type); - - if (side_properties.style()) - { - hash_string.append(std::to_string(static_cast(*side_properties.style()))); - } - else - { - hash_string.push_back(' '); - } - - if (side_properties.color()) - { - hash_string.append(std::to_string(std::hash()(*side_properties.color()))); - } - else - { - hash_string.push_back(' '); - } - } - else - { - hash_string.push_back(' '); - } - - hash_string.push_back(' '); - } - - return hash_string; + for (auto side : border::all_sides()) + { + if (left.side(side).is_set() != right.side(side).is_set()) + { + return false; + } + + if (left.side(side).is_set()) + { + if (left.side(side).get() != right.side(side).get()) + { + return false; + } + } + } + + return true; } } // namespace xlnt diff --git a/source/styles/color.cpp b/source/styles/color.cpp index d91c66a9..94cbbfa7 100644 --- a/source/styles/color.cpp +++ b/source/styles/color.cpp @@ -206,32 +206,6 @@ void color::set_tint(double tint) tint_ = tint; } -std::string color::to_hash_string() const -{ - std::string hash_string = "color"; - - hash_string.append(std::to_string(static_cast(type_))); - hash_string.append(std::to_string(static_cast(tint_))); - - if (type_ == type::indexed) - { - hash_string.append("indexed"); - hash_string.append(std::to_string(indexed_.get_index())); - } - else if (type_ == type::theme) - { - hash_string.append("theme"); - hash_string.append(std::to_string(theme_.get_index())); - } - else if (type_ == type::rgb) - { - hash_string.append("rgb"); - hash_string.append(rgb_.get_hex_string()); - } - - return hash_string; -} - void color::assert_type(type t) const { if (t != type_) diff --git a/source/styles/fill.cpp b/source/styles/fill.cpp index 02578140..7152d13e 100644 --- a/source/styles/fill.cpp +++ b/source/styles/fill.cpp @@ -72,25 +72,40 @@ pattern_fill &pattern_fill::background(const color &new_background) return *this; } -std::string pattern_fill::to_hash_string() const +bool operator==(const pattern_fill &left, const pattern_fill &right) { - std::string hash_string = "pattern_fill"; + if (left.background().is_set() != right.background().is_set()) + { + return false; + } + + if (left.background().is_set()) + { + if (left.background().get() != right.background().get()) + { + return false; + } + } - hash_string.append(background_ ? "1" : "0"); - - if (background_) - { - hash_string.append(std::to_string(background_->hash())); - } - - hash_string.append(background_ ? "1" : "0"); - - if (background_) - { - hash_string.append(std::to_string(background_->hash())); - } - - return hash_string; + if (left.foreground().is_set() != right.foreground().is_set()) + { + return false; + } + + if (left.foreground().is_set()) + { + if (left.foreground().get() != right.foreground().get()) + { + return false; + } + } + + if (left.type() != right.type()) + { + return false; + } + + return true; } // gradient_fill @@ -166,21 +181,6 @@ gradient_fill &gradient_fill::bottom(double value) return *this; } -std::string gradient_fill::to_hash_string() const -{ - std::string hash_string = "gradient_fill"; - - hash_string.append(std::to_string(static_cast(type_))); - hash_string.append(std::to_string(stops_.size())); - hash_string.append(std::to_string(degree_)); - hash_string.append(std::to_string(left_)); - hash_string.append(std::to_string(right_)); - hash_string.append(std::to_string(top_)); - hash_string.append(std::to_string(bottom_)); - - return hash_string; -} - gradient_fill &gradient_fill::add_stop(double position, color stop_color) { stops_[position] = stop_color; @@ -198,6 +198,46 @@ std::unordered_map gradient_fill::stops() const return stops_; } +bool operator==(const gradient_fill &left, const gradient_fill &right) +{ + if (left.type() != right.type()) + { + return false; + } + + if (left.degree() != right.degree()) + { + return false; + } + + if (left.bottom() != right.bottom()) + { + return false; + } + + if (left.right() != right.right()) + { + return false; + } + + if (left.top() != right.top()) + { + return false; + } + + if (left.left() != right.left()) + { + return false; + } + + if (left.stops() != right.stops()) + { + return false; + } + + return true; +} + // fill fill fill::solid(const color &fill_color) @@ -249,27 +289,19 @@ pattern_fill fill::pattern_fill() const return pattern_; } -std::string fill::to_hash_string() const +bool operator==(const fill &left, const fill &right) { - std::string hash_string = "fill"; + if (left.type() != right.type()) + { + return false; + } + + if (left.type() == fill_type::gradient) + { + return left.gradient_fill() == right.gradient_fill(); + } - hash_string.append(std::to_string(static_cast(type_))); - - switch (type_) - { - case fill_type::pattern: - hash_string.append(std::to_string(pattern_.hash())); - break; - - case fill_type::gradient: - hash_string.append(std::to_string(gradient_.hash())); - break; - - default: - break; - } // switch (type_) - - return hash_string; + return left.pattern_fill() == right.pattern_fill(); } } // namespace xlnt diff --git a/source/styles/font.cpp b/source/styles/font.cpp index f94dfb59..02bcf746 100644 --- a/source/styles/font.cpp +++ b/source/styles/font.cpp @@ -153,22 +153,83 @@ optional font::scheme() const return scheme_; } -std::string font::to_hash_string() const +bool operator==(const font &left, const font &right) { - std::string hash_string = "font"; + if (left.bold() != right.bold()) + { + return false; + } - hash_string.append(std::to_string(bold_)); - hash_string.append(std::to_string(italic_)); - hash_string.append(std::to_string(superscript_)); - hash_string.append(std::to_string(subscript_)); - hash_string.append(std::to_string(strikethrough_)); - hash_string.append(name_); - hash_string.append(std::to_string(size_)); - hash_string.append(std::to_string(static_cast(underline_))); - hash_string.append(family_ ? std::to_string(*family_) : ""); - hash_string.append(scheme_ ? *scheme_ : ""); + if (left.color().is_set() != right.color().is_set()) + { + return false; + } + + if (left.color().is_set()) + { + if (left.color().get() != right.color().get()) + { + return false; + } + } - return hash_string; + if (left.family().is_set() != right.family().is_set()) + { + return false; + } + + if (left.family().is_set()) + { + if (left.family().get() != right.family().get()) + { + return false; + } + } + + if (left.italic() != right.italic()) + { + return false; + } + + if (left.name() != right.name()) + { + return false; + } + + if (left.scheme().is_set() != right.scheme().is_set()) + { + return false; + } + + if (left.scheme().is_set()) + { + if (left.scheme().get() != right.scheme().get()) + { + return false; + } + } + + if (left.size() != right.size()) + { + return false; + } + + if (left.strikethrough() != right.strikethrough()) + { + return false; + } + + if (left.superscript() != right.superscript()) + { + return false; + } + + if (left.underline() != right.underline()) + { + return false; + } + + return true; } } // namespace xlnt diff --git a/source/styles/number_format.cpp b/source/styles/number_format.cpp index eb427d15..25fbee6c 100644 --- a/source/styles/number_format.cpp +++ b/source/styles/number_format.cpp @@ -273,17 +273,6 @@ std::string number_format::get_format_string() const return format_string_; } -std::string number_format::to_hash_string() const -{ - std::string hash_string("number_format"); - hash_string.append(format_string_); - hash_string.append("|||"); - hash_string.append(std::to_string(id_)); - - return hash_string; -} - - void number_format::set_format_string(const std::string &format_string) { format_string_ = format_string; @@ -364,4 +353,11 @@ std::string number_format::format(long double number, calendar base_date) const return detail::number_formatter(format_string_, base_date).format_number(number); } +bool operator==(const number_format &left, const number_format &right) +{ + return left.id_set_ == right.id_set_ + && left.id_ == right.id_ + && left.format_string_ == right.format_string_; +} + } // namespace xlnt diff --git a/source/styles/protection.cpp b/source/styles/protection.cpp index e6dd07b9..418b0357 100644 --- a/source/styles/protection.cpp +++ b/source/styles/protection.cpp @@ -52,14 +52,9 @@ protection &protection::hidden(bool hidden) return *this; } -std::string protection::to_hash_string() const +bool operator==(const protection &left, const protection &right) { - std::string hash_string = "protection"; - - hash_string.append(locked_ ? "1" : "0"); - hash_string.append(hidden_ ? "1" : "0"); - - return hash_string; + return left.locked_ == right.locked_ && left.hidden_ == right.hidden_; } } // namespace xlnt diff --git a/source/styles/tests/test_fill.hpp b/source/styles/tests/test_fill.hpp index f14dc2da..8e84b406 100644 --- a/source/styles/tests/test_fill.hpp +++ b/source/styles/tests/test_fill.hpp @@ -43,14 +43,14 @@ public: TS_ASSERT(const_fill.pattern_fill().background()); } - void test_hash() + void test_comparison() { xlnt::fill pattern_fill = xlnt::pattern_fill().type(xlnt::pattern_fill_type::solid); xlnt::fill gradient_fill_linear = xlnt::gradient_fill().type(xlnt::gradient_fill_type::linear); xlnt::fill gradient_fill_path = xlnt::gradient_fill().type(xlnt::gradient_fill_type::path); - TS_ASSERT_DIFFERS(pattern_fill.hash(), gradient_fill_linear.hash()); - TS_ASSERT_DIFFERS(gradient_fill_linear.hash(), gradient_fill_path.hash()); - TS_ASSERT_DIFFERS(gradient_fill_path.hash(), pattern_fill.hash()); + TS_ASSERT_DIFFERS(pattern_fill, gradient_fill_linear); + TS_ASSERT_DIFFERS(gradient_fill_linear, gradient_fill_path); + TS_ASSERT_DIFFERS(gradient_fill_path, pattern_fill); } }; diff --git a/source/utils/exceptions.cpp b/source/utils/exceptions.cpp index 18e0cc7a..5deea26a 100644 --- a/source/utils/exceptions.cpp +++ b/source/utils/exceptions.cpp @@ -32,6 +32,10 @@ exception::exception(const std::string &message) set_message(message); } +exception::~exception() +{ +} + void exception::set_message(const std::string &message) { message_ = message; @@ -47,6 +51,10 @@ invalid_column_string_index::invalid_column_string_index() { } +invalid_column_string_index::~invalid_column_string_index() +{ +} + invalid_data_type::invalid_data_type() : exception("data type error") { @@ -68,6 +76,10 @@ invalid_cell_reference::invalid_cell_reference(const std::string &coord_string) { } +invalid_cell_reference::~invalid_cell_reference() +{ +} + illegal_character::illegal_character(char c) : exception(std::string("illegal character: (") + std::to_string(static_cast(c)) + ")") { @@ -83,6 +95,10 @@ invalid_attribute::invalid_attribute() { } +invalid_attribute::~invalid_attribute() +{ +} + key_not_found::key_not_found() : exception("key not found in container") { diff --git a/source/utils/hashable.cpp b/source/utils/hashable.cpp index d43573a9..e69de29b 100644 --- a/source/utils/hashable.cpp +++ b/source/utils/hashable.cpp @@ -1,39 +0,0 @@ -// Copyright (c) 2014-2016 Thomas Fussell -// Copyright (c) 2010-2015 openpyxl -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, WRISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE -// -// @license: http://www.opensource.org/licenses/mit-license.php -// @author: see AUTHORS file -#include - -namespace xlnt { - -std::size_t hashable::hash() const -{ - static std::hash hasher; - return hasher.operator()(to_hash_string()); -} - -bool hashable::operator==(const xlnt::hashable &other) const -{ - return hash() == other.hash(); -} - -} // namespace xlnt diff --git a/source/utils/utf8string.cpp b/source/utils/utf8string.cpp index fd88aea2..e0d664a8 100644 --- a/source/utils/utf8string.cpp +++ b/source/utils/utf8string.cpp @@ -20,6 +20,12 @@ // // @license: http://www.opensource.org/licenses/mit-license.php // @author: see AUTHORS file + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wshadow" +#include +#pragma clang diagnostic pop + #include namespace xlnt { diff --git a/source/workbook/tests/test_consume_xlsx.hpp b/source/workbook/tests/test_consume_xlsx.hpp index 4844e3cd..4fb81bed 100644 --- a/source/workbook/tests/test_consume_xlsx.hpp +++ b/source/workbook/tests/test_consume_xlsx.hpp @@ -70,6 +70,10 @@ public: #ifdef WIN32 xlnt::workbook wb; wb.load(L"data\\19_unicode_Λ.xlsx"); +#else + xlnt::workbook wb; + wb.load("data/19_unicode_Λ.xlsx"); #endif + TS_ASSERT_EQUALS(wb.get_active_sheet().get_cell("A1").get_value(), "unicode!"); } }; diff --git a/source/workbook/workbook.cpp b/source/workbook/workbook.cpp index 2e2b299e..2aa900fb 100644 --- a/source/workbook/workbook.cpp +++ b/source/workbook/workbook.cpp @@ -637,7 +637,7 @@ std::size_t workbook::get_index(worksheet ws) throw invalid_parameter(); } - return std::distance(begin(), match); + return static_cast(std::distance(begin(), match)); } void workbook::create_named_range(const std::string &name, worksheet range_owner, const std::string &reference_string) diff --git a/source/worksheet/tests/test_worksheet.hpp b/source/worksheet/tests/test_worksheet.hpp index 5c491a80..3ccbfb45 100644 --- a/source/worksheet/tests/test_worksheet.hpp +++ b/source/worksheet/tests/test_worksheet.hpp @@ -521,11 +521,11 @@ public: xlnt::workbook wb; auto ws = wb.get_active_sheet(); - ws.add_print_title(3); + ws.set_print_title_rows(3); TS_ASSERT_EQUALS(ws.get_print_titles(), "Sheet1!1:3"); auto ws2 = wb.create_sheet(); - ws2.add_print_title(4, "cols"); + ws2.set_print_title_cols(4); TS_ASSERT_EQUALS(ws2.get_print_titles(), "Sheet2!A:D"); } @@ -534,17 +534,17 @@ public: xlnt::workbook wb; auto ws = wb.get_active_sheet(); - ws.set_print_title_rows("1:4"); + ws.set_print_title_rows(4); TS_ASSERT_EQUALS(ws.get_print_titles(), "Sheet1!1:4"); auto ws2 = wb.create_sheet(); - ws2.set_print_title_cols("A:F"); + ws2.set_print_title_cols("F"); TS_ASSERT_EQUALS(ws2.get_print_titles(), "Sheet2!A:F"); auto ws3 = wb.create_sheet(); - ws3.set_print_title_rows("1:2"); - ws3.set_print_title_cols("C:D"); - TS_ASSERT_EQUALS(ws3.get_print_titles(), "Sheet3!1:2,Sheet3!C:D"); + ws3.set_print_title_rows(2, 3); + ws3.set_print_title_cols("C", "D"); + TS_ASSERT_EQUALS(ws3.get_print_titles(), "Sheet3!2:3,Sheet3!C:D"); } void test_print_area() diff --git a/source/worksheet/worksheet.cpp b/source/worksheet/worksheet.cpp index 0b1e29c6..f49cead0 100644 --- a/source/worksheet/worksheet.cpp +++ b/source/worksheet/worksheet.cpp @@ -624,7 +624,7 @@ void worksheet::append(const std::unordered_map &cells) for (auto cell : cells) { - get_cell(cell_reference(static_cast(cell.first), row)).set_value(cell.second); + get_cell(cell_reference(static_cast(cell.first), row)).set_value(cell.second); } } @@ -985,31 +985,24 @@ range worksheet::iter_cells(bool skip_null) return range(*this, calculate_dimension(), major_order::row, skip_null); } -void worksheet::add_print_title(int i) +void worksheet::set_print_title_rows(row_t last_row) { - add_print_title(i, "rows"); + set_print_title_rows(1, last_row); } -void worksheet::add_print_title(int i, const std::string &rows_or_cols) +void worksheet::set_print_title_rows(row_t first_row, row_t last_row) { - if(rows_or_cols == "cols") - { - set_print_title_cols("A:" + column_t::column_string_from_index(i)); - } - else - { - set_print_title_rows("1:" + std::to_string(i)); - } + d_->print_title_rows_ = std::to_string(first_row) + ":" + std::to_string(last_row); } -void worksheet::set_print_title_rows(const std::string &rows) +void worksheet::set_print_title_cols(column_t last_column) { - d_->print_title_rows_ = rows; + set_print_title_cols(1, last_column); } -void worksheet::set_print_title_cols(const std::string &cols) +void worksheet::set_print_title_cols(column_t first_column, column_t last_column) { - d_->print_title_cols_ = cols; + d_->print_title_cols_ = first_column.column_string() + ":" + last_column.column_string(); } std::string worksheet::get_print_titles() const diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ce4a6661..f1eaf6ee 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -26,15 +26,21 @@ file(GLOB UTILS_TESTS ${LIBRARY_SOURCE_DIR}/utils/tests/test_*.hpp) file(GLOB WORKBOOK_TESTS ${LIBRARY_SOURCE_DIR}/workbook/tests/test_*.hpp) file(GLOB WORKSHEET_TESTS ${LIBRARY_SOURCE_DIR}/worksheet/tests/test_*.hpp) -set(ZIP ${LIBRARY_SOURCE_DIR}/detail/zip.cpp) +set(XLNT_ZIP + ${LIBRARY_SOURCE_DIR}/detail/zip.cpp + ${LIBRARY_SOURCE_DIR}/detail/zip.hpp) set(TESTS ${CELL_TESTS} ${CHARTS_TESTS} ${CHARTSHEET_TESTS} ${DRAWING_TESTS} ${FORMULA_TESTS} ${PACKAGING_TESTS} ${STYLES_TESTS} ${UTILS_TESTS} ${WORKBOOK_TESTS} ${WORKSHEET_TESTS}) -set(PUGIXML ${THIRD_PARTY_DIR}/pugixml/src/pugixml.cpp) -file(GLOB TEST_HELPERS_HEADERS ${XLNT_TESTS_DIR}/helpers/*.hpp) -file(GLOB TEST_HELPERS_SOURCES ${XLNT_TESTS_DIR}/helpers/*.cpp) +set(PUGIXML + ${THIRD_PARTY_DIR}/pugixml/src/pugixml.cpp + ${THIRD_PARTY_DIR}/pugixml/src/pugixml.hpp + ${THIRD_PARTY_DIR}/pugixml/src/pugiconfig.hpp) + +file(GLOB TEST_HELPERS_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/helpers/*.hpp) +file(GLOB TEST_HELPERS_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/helpers/*.cpp) SET(TEST_HELPERS ${TEST_HELPERS_HEADERS} ${TEST_HELPERS_SOURCES}) @@ -42,9 +48,11 @@ file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/tests") set(RUNNER "${CMAKE_CURRENT_BINARY_DIR}/runner-autogen.cpp") set_source_files_properties(${RUNNER} PROPERTIES GENERATED TRUE) -add_executable(${PROJECT_NAME} ${TEST_HELPERS} ${TESTS} ${RUNNER} ${PUGIXML} $ ${ZIP}) +add_executable(${PROJECT_NAME} ${TEST_HELPERS} ${TESTS} ${RUNNER} ${PUGIXML} $ ${XLNT_ZIP}) source_group(helpers FILES ${TEST_HELPERS}) +source_group(xlnt\\detail FILES ${XLNT_ZIP}) +source_group(third-party\\pugixml FILES ${PUGIXML}) source_group(tests\\cell FILES ${CELL_TESTS}) source_group(tests\\charts FILES ${CHARTS_TESTS}) source_group(tests\\chartsheet FILES ${CHARTSHEET_TESTS}) diff --git a/tests/data/19_unicode_Λ.xlsx b/tests/data/19_unicode_Λ.xlsx index d8f0f1f6..98bf2719 100644 Binary files a/tests/data/19_unicode_Λ.xlsx and b/tests/data/19_unicode_Λ.xlsx differ diff --git a/tests/helpers/xml_helper.hpp b/tests/helpers/xml_helper.hpp index d6632de3..28de0f1d 100644 --- a/tests/helpers/xml_helper.hpp +++ b/tests/helpers/xml_helper.hpp @@ -276,7 +276,7 @@ public: std::vector bytes; wb.save(bytes); std::istringstream file_stream(std::string(bytes.begin(), bytes.end())); - Partio::ZipFileReader archive(file_stream); + xlnt::detail::ZipFileReader archive(file_stream); return string_matches_archive_member(expected, archive, part, content_type); } @@ -287,33 +287,32 @@ public: std::vector bytes; wb.save(bytes); std::istringstream file_stream(std::string(bytes.begin(), bytes.end())); - Partio::ZipFileReader archive(file_stream); + xlnt::detail::ZipFileReader archive(file_stream); return file_matches_archive_member(expected, archive, part, content_type); } static bool string_matches_archive_member(const std::string &expected, - Partio::ZipFileReader &archive, + xlnt::detail::ZipFileReader &archive, const xlnt::path &member, const std::string &content_type) { - auto stream = archive.Get_File(member.string(), true); - std::string contents((std::istreambuf_iterator(*stream)), (std::istreambuf_iterator())); - delete stream; + auto &stream = archive.open(member.string()); + std::string contents((std::istreambuf_iterator(stream)), (std::istreambuf_iterator())); return compare_files(expected, contents, content_type); } static bool file_matches_archive_member(const xlnt::path &file, - Partio::ZipFileReader &archive, + xlnt::detail::ZipFileReader &archive, const xlnt::path &member, const std::string &content_type) { - if (!archive.Has_File(member.string())) return false; + if (!archive.has_file(member.string())) return false; std::vector member_data; xlnt::detail::vector_ostreambuf member_data_buffer(member_data); std::ostream member_data_stream(&member_data_buffer); - std::unique_ptr member_stream(archive.Get_File(member.string(), true)); - member_data_stream << member_stream->rdbuf(); + auto &member_stream = archive.open(member.string()); + member_data_stream << member_stream.rdbuf(); std::string contents(member_data.begin(), member_data.end()); return compare_files(file.read_contents(), contents, content_type); } @@ -337,15 +336,15 @@ public: { xlnt::detail::vector_istreambuf left_buffer(left); std::istream left_stream(&left_buffer); - Partio::ZipFileReader left_archive(left_stream); + xlnt::detail::ZipFileReader left_archive(left_stream); - const auto left_info = left_archive.filename_to_header; + const auto left_info = left_archive.files(); xlnt::detail::vector_istreambuf right_buffer(right); std::istream right_stream(&right_buffer); - Partio::ZipFileReader right_archive(right_stream); + xlnt::detail::ZipFileReader right_archive(right_stream); - const auto right_info = right_archive.filename_to_header; + const auto right_info = right_archive.files(); if (left_info.size() != right_info.size()) { @@ -354,14 +353,14 @@ public: std::cout << "left has: "; for (auto &info : left_info) { - std::cout << info.first << ", "; + std::cout << info << ", "; } std::cout << std::endl; std::cout << "right has: "; for (auto &info : right_info) { - std::cout << info.first << ", "; + std::cout << info << ", "; } std::cout << std::endl; } @@ -379,33 +378,33 @@ public: for (auto left_member : left_info) { - if (!right_archive.Has_File(left_member.first)) + if (!right_archive.has_file(left_member)) { match = false; - std::cout << "right is missing file: " << left_member.first << std::endl; + std::cout << "right is missing file: " << left_member << std::endl; continue; } - std::unique_ptr left_member_stream(left_archive.Get_File(left_member.first)); + auto &left_member_stream = left_archive.open(left_member); std::vector left_contents_raw; xlnt::detail::vector_ostreambuf left_contents_buffer(left_contents_raw); std::ostream left_contents_stream(&left_contents_buffer); - left_contents_stream << left_member_stream->rdbuf(); + left_contents_stream << left_member_stream.rdbuf(); std::string left_member_contents(left_contents_raw.begin(), left_contents_raw.end()); - std::unique_ptr right_member_stream(left_archive.Get_File(left_member.first)); + auto &right_member_stream = left_archive.open(left_member); std::vector right_contents_raw; xlnt::detail::vector_ostreambuf right_contents_buffer(right_contents_raw); std::ostream right_contents_stream(&right_contents_buffer); - right_contents_stream << right_member_stream->rdbuf(); + right_contents_stream << right_member_stream.rdbuf(); std::string right_member_contents(right_contents_raw.begin(), right_contents_raw.end()); std::string left_content_type, right_content_type; - if (left_member.first != "[Content_Types].xml") + if (left_member != "[Content_Types].xml") { - left_content_type = left_manifest.get_content_type(xlnt::path(left_member.first)); - right_content_type = right_manifest.get_content_type(xlnt::path(left_member.first)); + left_content_type = left_manifest.get_content_type(xlnt::path(left_member)); + right_content_type = right_manifest.get_content_type(xlnt::path(left_member)); } else { @@ -415,7 +414,7 @@ public: if (left_content_type != right_content_type) { std::cout << "content types differ: " - << left_member.first + << left_member << " " << left_content_type << " " @@ -425,7 +424,7 @@ public: } else if (!compare_files(left_member_contents, right_member_contents, left_content_type)) { - std::cout << left_member.first << std::endl; + std::cout << left_member << std::endl; match = false; } } diff --git a/third-party/CMakeLists.txt b/third-party/CMakeLists.txt index 52ac4757..cd06abe7 100644 --- a/third-party/CMakeLists.txt +++ b/third-party/CMakeLists.txt @@ -13,31 +13,48 @@ set(LIBSTUDXML ${CMAKE_CURRENT_SOURCE_DIR}/libstudxml/xml/parser.cxx ${CMAKE_CURRENT_SOURCE_DIR}/libstudxml/xml/qname.cxx ${CMAKE_CURRENT_SOURCE_DIR}/libstudxml/xml/serializer.cxx - ${CMAKE_CURRENT_SOURCE_DIR}/libstudxml/xml/value-traits.cxx + ${CMAKE_CURRENT_SOURCE_DIR}/libstudxml/xml/value-traits.cxx + ${CMAKE_CURRENT_SOURCE_DIR}/libstudxml/xml/content + ${CMAKE_CURRENT_SOURCE_DIR}/libstudxml/xml/exception + ${CMAKE_CURRENT_SOURCE_DIR}/libstudxml/xml/forward + ${CMAKE_CURRENT_SOURCE_DIR}/libstudxml/xml/parser + ${CMAKE_CURRENT_SOURCE_DIR}/libstudxml/xml/qname + ${CMAKE_CURRENT_SOURCE_DIR}/libstudxml/xml/serializer + ${CMAKE_CURRENT_SOURCE_DIR}/libstudxml/xml/value-traits) + +set(EXPAT ${CMAKE_CURRENT_SOURCE_DIR}/libstudxml/xml/details/expat/xmlparse.c - ${CMAKE_CURRENT_SOURCE_DIR}/libstudxml/xml/details/expat/xmlrole.c + ${CMAKE_CURRENT_SOURCE_DIR}/libstudxml/xml/details/expat/xmlrole.c ${CMAKE_CURRENT_SOURCE_DIR}/libstudxml/xml/details/expat/xmltok_impl.c - ${CMAKE_CURRENT_SOURCE_DIR}/libstudxml/xml/details/expat/xmltok_ns.c + ${CMAKE_CURRENT_SOURCE_DIR}/libstudxml/xml/details/expat/xmltok_ns.c ${CMAKE_CURRENT_SOURCE_DIR}/libstudxml/xml/details/expat/xmltok.c - ${CMAKE_CURRENT_SOURCE_DIR}/libstudxml/xml/details/genx/char-props.c - ${CMAKE_CURRENT_SOURCE_DIR}/libstudxml/xml/details/genx/genx.c) -set(POLE pole/pole.cpp) -set(BOTAN ${CMAKE_CURRENT_SOURCE_DIR}/botan/botan_all.cpp) -set(ZLIB ${CMAKE_CURRENT_SOURCE_DIR}/zlib/adler32.c - ${CMAKE_CURRENT_SOURCE_DIR}/zlib/compress.c - ${CMAKE_CURRENT_SOURCE_DIR}/zlib/crc32.c - ${CMAKE_CURRENT_SOURCE_DIR}/zlib/deflate.c - ${CMAKE_CURRENT_SOURCE_DIR}/zlib/gzclose.c - ${CMAKE_CURRENT_SOURCE_DIR}/zlib/gzlib.c - ${CMAKE_CURRENT_SOURCE_DIR}/zlib/gzread.c - ${CMAKE_CURRENT_SOURCE_DIR}/zlib/gzwrite.c - ${CMAKE_CURRENT_SOURCE_DIR}/zlib/infback.c - ${CMAKE_CURRENT_SOURCE_DIR}/zlib/inffast.c - ${CMAKE_CURRENT_SOURCE_DIR}/zlib/inflate.c - ${CMAKE_CURRENT_SOURCE_DIR}/zlib/inftrees.c - ${CMAKE_CURRENT_SOURCE_DIR}/zlib/trees.c - ${CMAKE_CURRENT_SOURCE_DIR}/zlib/uncompr.c - ${CMAKE_CURRENT_SOURCE_DIR}/zlib/zutil.c) + ${CMAKE_CURRENT_SOURCE_DIR}/libstudxml/xml/details/expat/ascii.h + ${CMAKE_CURRENT_SOURCE_DIR}/libstudxml/xml/details/expat/asciitab.h + ${CMAKE_CURRENT_SOURCE_DIR}/libstudxml/xml/details/expat/config.h + ${CMAKE_CURRENT_SOURCE_DIR}/libstudxml/xml/details/expat/expat_external.h + ${CMAKE_CURRENT_SOURCE_DIR}/libstudxml/xml/details/expat/expat.h + ${CMAKE_CURRENT_SOURCE_DIR}/libstudxml/xml/details/expat/iasciitab.h + ${CMAKE_CURRENT_SOURCE_DIR}/libstudxml/xml/details/expat/internal.h + ${CMAKE_CURRENT_SOURCE_DIR}/libstudxml/xml/details/expat/latin1tab.h + ${CMAKE_CURRENT_SOURCE_DIR}/libstudxml/xml/details/expat/nametab.h + ${CMAKE_CURRENT_SOURCE_DIR}/libstudxml/xml/details/expat/utf8tab.h + ${CMAKE_CURRENT_SOURCE_DIR}/libstudxml/xml/details/expat/xmlrole.h + ${CMAKE_CURRENT_SOURCE_DIR}/libstudxml/xml/details/expat/xmltok_impl.h + ${CMAKE_CURRENT_SOURCE_DIR}/libstudxml/xml/details/expat/xmltok.h) + +set(GENX + ${CMAKE_CURRENT_SOURCE_DIR}/libstudxml/xml/details/genx/char-props.c + ${CMAKE_CURRENT_SOURCE_DIR}/libstudxml/xml/details/genx/genx.c + ${CMAKE_CURRENT_SOURCE_DIR}/libstudxml/xml/details/genx/genx.h) + +set(POLE + ${CMAKE_CURRENT_SOURCE_DIR}/pole/pole.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/pole/pole.h) + +set(BOTAN + ${CMAKE_CURRENT_SOURCE_DIR}/botan/botan_all.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/botan/botan_all_internal.h + ${CMAKE_CURRENT_SOURCE_DIR}/botan/botan_all.h) if(MSVC) set_source_files_properties(${BOTAN} PROPERTIES COMPILE_FLAGS "/wd\"4244\"") @@ -52,9 +69,40 @@ endif() add_custom_command(OUTPUT ${BOTAN} COMMAND python configure.py --minimized-build --enable-modules=sha1,aes,filters,codec_filt,cbc,ecb,sha2_32,sha2_64 --without-sphinx --disable-shared --amalgamation --cpu=${CPU} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/botan - COMMENT "Generating botan amalgamation ${BOTAN}") + COMMENT "Generating botan amalgamation") -add_library(xlnt.third-party OBJECT ${LIBSTUDXML} ${POLE} ${BOTAN} ${ZLIB}) +set(ZLIB ${CMAKE_CURRENT_SOURCE_DIR}/zlib/adler32.c + ${CMAKE_CURRENT_SOURCE_DIR}/zlib/compress.c + ${CMAKE_CURRENT_SOURCE_DIR}/zlib/crc32.c + ${CMAKE_CURRENT_SOURCE_DIR}/zlib/deflate.c + ${CMAKE_CURRENT_SOURCE_DIR}/zlib/gzclose.c + ${CMAKE_CURRENT_SOURCE_DIR}/zlib/gzlib.c + ${CMAKE_CURRENT_SOURCE_DIR}/zlib/gzread.c + ${CMAKE_CURRENT_SOURCE_DIR}/zlib/gzwrite.c + ${CMAKE_CURRENT_SOURCE_DIR}/zlib/infback.c + ${CMAKE_CURRENT_SOURCE_DIR}/zlib/inffast.c + ${CMAKE_CURRENT_SOURCE_DIR}/zlib/inflate.c + ${CMAKE_CURRENT_SOURCE_DIR}/zlib/inftrees.c + ${CMAKE_CURRENT_SOURCE_DIR}/zlib/trees.c + ${CMAKE_CURRENT_SOURCE_DIR}/zlib/uncompr.c + ${CMAKE_CURRENT_SOURCE_DIR}/zlib/zutil.c + ${CMAKE_CURRENT_SOURCE_DIR}/zlib/crc32.h + ${CMAKE_CURRENT_SOURCE_DIR}/zlib/deflate.h + ${CMAKE_CURRENT_SOURCE_DIR}/zlib/gzguts.h + ${CMAKE_CURRENT_SOURCE_DIR}/zlib/inffast.h + ${CMAKE_CURRENT_SOURCE_DIR}/zlib/inffixed.h + ${CMAKE_CURRENT_SOURCE_DIR}/zlib/inflate.h + ${CMAKE_CURRENT_SOURCE_DIR}/zlib/inftrees.h + ${CMAKE_CURRENT_SOURCE_DIR}/zlib/trees.h + ${CMAKE_CURRENT_SOURCE_DIR}/zlib/zconf.h + ${CMAKE_CURRENT_SOURCE_DIR}/zlib/zlib.h + ${CMAKE_CURRENT_SOURCE_DIR}/zlib/zutil.h) + +if(NOT MSVC) + set_source_files_properties(${ZLIB} PROPERTIES COMPILE_FLAGS "-Wno-implicit-function-declaration") +endif() + +add_library(xlnt.third-party OBJECT ${LIBSTUDXML} ${GENX} ${EXPAT} ${POLE} ${BOTAN} ${ZLIB}) target_compile_definitions(xlnt.third-party PRIVATE LIBSTUDXML_STATIC_LIB=1) if(NOT STATIC) @@ -69,5 +117,7 @@ endif() source_group(botan FILES ${BOTAN}) source_group(libstudxml FILES ${LIBSTUDXML}) -source_group(pole FILES ${LIBSTUDXML}) +source_group(libstudxml\\genx FILES ${GENX}) +source_group(libstudxml\\expat FILES ${EXPAT}) +source_group(pole FILES ${POLE}) source_group(zlib FILES ${ZLIB}) diff --git a/third-party/botan b/third-party/botan index 36e0ea1e..73c2605f 160000 --- a/third-party/botan +++ b/third-party/botan @@ -1 +1 @@ -Subproject commit 36e0ea1e407027ac48e82b56016a6813ff6a1082 +Subproject commit 73c2605f50e6192bf6cb560c51d32bc53b4c5597 diff --git a/third-party/libstudxml b/third-party/libstudxml index 54d64e7c..132522ca 160000 --- a/third-party/libstudxml +++ b/third-party/libstudxml @@ -1 +1 @@ -Subproject commit 54d64e7ca405e06d2328e6207bb4752e20577e50 +Subproject commit 132522ca4c895e9b07d7e323d5529474806e5829 diff --git a/third-party/pole/pole.h b/third-party/pole/pole.h index 3b005e1e..69f54438 100644 --- a/third-party/pole/pole.h +++ b/third-party/pole/pole.h @@ -355,4 +355,4 @@ namespace POLE } // namespace POLE -#endif // POLE_H \ No newline at end of file +#endif // POLE_H diff --git a/third-party/pugixml b/third-party/pugixml index dfe9360c..a832e8a5 160000 --- a/third-party/pugixml +++ b/third-party/pugixml @@ -1 +1 @@ -Subproject commit dfe9360cdf038c0ecf53d45bfc75cf8fd34604b8 +Subproject commit a832e8a5eff11f58a00ca41ec51ff3895b0da165 diff --git a/third-party/utfcpp b/third-party/utfcpp index f029fcc2..a5ad5ec9 160000 --- a/third-party/utfcpp +++ b/third-party/utfcpp @@ -1 +1 @@ -Subproject commit f029fcc2fbc7cd979925f198f7e6ca8170d45000 +Subproject commit a5ad5ec9d936d63e9c010d1054f8b11fed0fabbc diff --git a/third-party/zlib b/third-party/zlib index 50893291..94575859 160000 --- a/third-party/zlib +++ b/third-party/zlib @@ -1 +1 @@ -Subproject commit 50893291621658f355bc5b4d450a8d06a563053d +Subproject commit 94575859cf7f657f0f31aff4c50761fe3f182699