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