Browse Source

xlnt自整理VS项目,与github上不同,git上面的太多编译错误。

jianfeng1.wang 2 years ago
parent
commit
0a35ffe797
100 changed files with 15873 additions and 0 deletions
  1. BIN
      xlnt/benchmarks/data/large.xlsx
  2. BIN
      xlnt/benchmarks/data/very_large.xlsx
  3. 23 0
      xlnt/benchmarks/disabled/bufzip.cpp
  4. 65 0
      xlnt/benchmarks/disabled/memory.cpp
  5. 131 0
      xlnt/benchmarks/disabled/profiling.cpp
  6. 45 0
      xlnt/benchmarks/disabled/reader.cpp
  7. 31 0
      xlnt/benchmarks/disabled/speed.cpp
  8. 36 0
      xlnt/benchmarks/microbenchmarks/CMakeLists.txt
  9. 207 0
      xlnt/benchmarks/microbenchmarks/double_to_string.cpp
  10. 223 0
      xlnt/benchmarks/microbenchmarks/string_to_double.cpp
  11. 58 0
      xlnt/benchmarks/spreadsheet-load.cpp
  12. 250 0
      xlnt/benchmarks/styles.cpp
  13. 90 0
      xlnt/benchmarks/writer.cpp
  14. 712 0
      xlnt/include/xlnt/cell/cell.hpp
  15. 272 0
      xlnt/include/xlnt/cell/cell_reference.hpp
  16. 58 0
      xlnt/include/xlnt/cell/cell_type.hpp
  17. 166 0
      xlnt/include/xlnt/cell/comment.hpp
  18. 70 0
      xlnt/include/xlnt/cell/hyperlink.hpp
  19. 309 0
      xlnt/include/xlnt/cell/index_types.hpp
  20. 46 0
      xlnt/include/xlnt/cell/phonetic_run.hpp
  21. 179 0
      xlnt/include/xlnt/cell/rich_text.hpp
  22. 48 0
      xlnt/include/xlnt/cell/rich_text_run.hpp
  23. 59 0
      xlnt/include/xlnt/drawing/spreadsheet_drawing.hpp
  24. 79 0
      xlnt/include/xlnt/packaging/ext_list.hpp
  25. 199 0
      xlnt/include/xlnt/packaging/manifest.hpp
  26. 178 0
      xlnt/include/xlnt/packaging/relationship.hpp
  27. 239 0
      xlnt/include/xlnt/packaging/uri.hpp
  28. 168 0
      xlnt/include/xlnt/styles/alignment.hpp
  29. 227 0
      xlnt/include/xlnt/styles/border.hpp
  30. 350 0
      xlnt/include/xlnt/styles/color.hpp
  31. 170 0
      xlnt/include/xlnt/styles/conditional_format.hpp
  32. 369 0
      xlnt/include/xlnt/styles/fill.hpp
  33. 324 0
      xlnt/include/xlnt/styles/font.hpp
  34. 235 0
      xlnt/include/xlnt/styles/format.hpp
  35. 274 0
      xlnt/include/xlnt/styles/number_format.hpp
  36. 107 0
      xlnt/include/xlnt/styles/protection.hpp
  37. 261 0
      xlnt/include/xlnt/styles/style.hpp
  38. 40 0
      xlnt/include/xlnt/utils/calendar.hpp
  39. 93 0
      xlnt/include/xlnt/utils/date.hpp
  40. 136 0
      xlnt/include/xlnt/utils/datetime.hpp
  41. 347 0
      xlnt/include/xlnt/utils/exceptions.hpp
  42. 194 0
      xlnt/include/xlnt/utils/numeric.hpp
  43. 337 0
      xlnt/include/xlnt/utils/optional.hpp
  44. 221 0
      xlnt/include/xlnt/utils/path.hpp
  45. 47 0
      xlnt/include/xlnt/utils/scoped_enum_hash.hpp
  46. 93 0
      xlnt/include/xlnt/utils/time.hpp
  47. 84 0
      xlnt/include/xlnt/utils/timedelta.hpp
  48. 195 0
      xlnt/include/xlnt/utils/variant.hpp
  49. 55 0
      xlnt/include/xlnt/workbook/calculation_properties.hpp
  50. 99 0
      xlnt/include/xlnt/workbook/document_security.hpp
  51. 38 0
      xlnt/include/xlnt/workbook/external_book.hpp
  52. 86 0
      xlnt/include/xlnt/workbook/metadata_property.hpp
  53. 93 0
      xlnt/include/xlnt/workbook/named_range.hpp
  54. 143 0
      xlnt/include/xlnt/workbook/streaming_workbook_reader.hpp
  55. 116 0
      xlnt/include/xlnt/workbook/streaming_workbook_writer.hpp
  56. 43 0
      xlnt/include/xlnt/workbook/theme.hpp
  57. 932 0
      xlnt/include/xlnt/workbook/workbook.hpp
  58. 118 0
      xlnt/include/xlnt/workbook/workbook_view.hpp
  59. 253 0
      xlnt/include/xlnt/workbook/worksheet_iterator.hpp
  60. 317 0
      xlnt/include/xlnt/worksheet/cell_iterator.hpp
  61. 216 0
      xlnt/include/xlnt/worksheet/cell_vector.hpp
  62. 75 0
      xlnt/include/xlnt/worksheet/column_properties.hpp
  63. 329 0
      xlnt/include/xlnt/worksheet/header_footer.hpp
  64. 39 0
      xlnt/include/xlnt/worksheet/major_order.hpp
  65. 136 0
      xlnt/include/xlnt/worksheet/page_margins.hpp
  66. 244 0
      xlnt/include/xlnt/worksheet/page_setup.hpp
  67. 98 0
      xlnt/include/xlnt/worksheet/pane.hpp
  68. 167 0
      xlnt/include/xlnt/worksheet/phonetic_pr.hpp
  69. 67 0
      xlnt/include/xlnt/worksheet/print_options.hpp
  70. 315 0
      xlnt/include/xlnt/worksheet/range.hpp
  71. 273 0
      xlnt/include/xlnt/worksheet/range_iterator.hpp
  72. 189 0
      xlnt/include/xlnt/worksheet/range_reference.hpp
  73. 86 0
      xlnt/include/xlnt/worksheet/row_properties.hpp
  74. 163 0
      xlnt/include/xlnt/worksheet/selection.hpp
  75. 67 0
      xlnt/include/xlnt/worksheet/sheet_format_properties.hpp
  76. 94 0
      xlnt/include/xlnt/worksheet/sheet_pr.hpp
  77. 63 0
      xlnt/include/xlnt/worksheet/sheet_protection.hpp
  78. 270 0
      xlnt/include/xlnt/worksheet/sheet_view.hpp
  79. 841 0
      xlnt/include/xlnt/worksheet/worksheet.hpp
  80. 92 0
      xlnt/include/xlnt/xlnt.hpp
  81. 43 0
      xlnt/include/xlnt/xlnt_config.hpp
  82. 31 0
      xlnt/libstudxml.sln
  83. 318 0
      xlnt/libstudxml.vcxproj
  84. 96 0
      xlnt/libstudxml.vcxproj.filters
  85. 343 0
      xlnt/python/python_streambuf.hpp
  86. 256 0
      xlnt/python/setup.py
  87. 452 0
      xlnt/python/xlntpyarrow.lib.cpp
  88. 86 0
      xlnt/python/xlntpyarrow/__init__.py
  89. BIN
      xlnt/samples/data/cafe.jpg
  90. BIN
      xlnt/samples/data/documentation-print.xlsx
  91. BIN
      xlnt/samples/data/encrypted.xlsx
  92. BIN
      xlnt/samples/data/penguin.jpg
  93. BIN
      xlnt/samples/data/sample1.xlsx
  94. 36 0
      xlnt/samples/decrypt.cpp
  95. 1 0
      xlnt/samples/disabled/add_comments.cpp
  96. 1 0
      xlnt/samples/disabled/basic_conditional_formatting.cpp
  97. 1 0
      xlnt/samples/disabled/comment_error.cpp
  98. 1 0
      xlnt/samples/disabled/copy_style.cpp
  99. 45 0
      xlnt/samples/disabled/create.cpp
  100. 1 0
      xlnt/samples/disabled/default_styles.cpp

BIN
xlnt/benchmarks/data/large.xlsx


BIN
xlnt/benchmarks/data/very_large.xlsx


+ 23 - 0
xlnt/benchmarks/disabled/bufzip.cpp

@@ -0,0 +1,23 @@
+#include <xlnt/xlnt.hpp>
+#include <xlnt/serialization/xml_document.hpp>
+#include <xlnt/serialization/xml_node.hpp>
+#include <xlnt/serialization/xml_serializer.hpp>
+
+void standard()
+{
+    xlnt::xml_document doc;
+
+    for (int i = 0; i < 1000000; i++) 
+    {
+	doc.add_child("test");
+    }
+
+    xlnt::zip_file archive;
+    archive.writestr("sheet.xml", doc.to_string());
+}
+
+int main()
+{
+    standard();
+    return 0;
+}

+ 65 - 0
xlnt/benchmarks/disabled/memory.cpp

@@ -0,0 +1,65 @@
+#include <cassert>
+
+#ifdef __APPLE__
+#include<mach/mach.h>
+#endif
+
+#include <xlnt/xlnt.hpp>
+
+#include "../tests/helpers/path_helper.hpp"
+
+int calc_memory_usage()
+{
+#ifdef __APPLE__
+    struct task_basic_info t_info;
+    mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT;
+
+    if (KERN_SUCCESS != task_info(mach_task_self(),
+				  TASK_BASIC_INFO, (task_info_t)&t_info, 
+				  &t_info_count))
+    {
+	return 0;
+    }
+
+    return t_info.virtual_size;
+#endif
+    return 0;
+}
+
+void test_memory_use()
+{
+    // Naive test that assumes memory use will never be more than 120 % of
+    // that for first 50 rows
+    auto current_folder = PathHelper::GetExecutableDirectory();
+    auto src = current_folder + "rks/files/very_large.xlsx";
+
+    xlnt::workbook wb;
+    wb.load(src);
+    auto ws = wb.get_active_sheet();
+
+    int initial_use = 0;
+    int n = 0;
+
+    for (auto line : ws.rows())
+    {
+        if (n % 50 == 0)
+        {
+            auto use = calc_memory_usage();
+
+            if (initial_use == 0)
+	    {
+                initial_use = use;
+	    }
+
+            assert(use / initial_use < 1.2);
+	    std::cout << n << " " << use << std::endl;
+	}
+
+        n++;
+    }
+}
+
+int main()
+{
+    test_memory_use();
+}

+ 131 - 0
xlnt/benchmarks/disabled/profiling.cpp

@@ -0,0 +1,131 @@
+from io import BytesIO
+from lxml.etree import xmlfile
+import os
+from random import randint
+
+from openpyxl import Workbook
+from openpyxl.xml.functions import XMLGenerator
+
+def make_worksheet():
+    wb = Workbook()
+    ws = wb.active
+    for i in range(1000):
+        ws.append(list(range(100)))
+    return ws
+
+
+def lxml_writer(ws=None):
+    from openpyxl.writer.lxml_worksheet import write_rows
+    if ws is None:
+        ws = make_worksheet()
+
+    out = BytesIO()
+    with xmlfile(out) as xf:
+        write_rows(xf, ws)
+    #with open("lxml_writer.xml", "wb") as dump:
+        #dump.write(out.getvalue())
+    #ws.parent.save("lxml_writer.xlsx")
+
+
+def make_dump_worksheet():
+    wb = Workbook(write_only=True)
+    ws = wb.create_sheet()
+    return ws
+
+def dump_writer(ws=None):
+    if ws is None:
+        ws = make_dump_worksheet()
+    for i in range(1000):
+        ws.append(list(range(100)))
+
+
+COLUMNS = 100
+ROWS = 1000
+BOLD = 1
+ITALIC = 2
+UNDERLINE = 4
+RED_BG = 8
+formatData = [[None] * COLUMNS for _ in range(ROWS)]
+
+def generate_format_data():
+    for row in range(ROWS):
+        for col in range(COLUMNS):
+            formatData[row][col] = randint(1, 15)
+
+
+def styled_sheet():
+    from openpyxl import Workbook
+    from openpyxl.styles import Font, Style, PatternFill, Color, colors
+
+    wb = Workbook()
+    ws = wb.active
+    ws.title = 'Test 1'
+
+    red_fill = PatternFill(fill_type='solid', fgColor=Color(colors.RED), bgColor=Color(colors.RED))
+    empty_fill = PatternFill()
+    styles = []
+    # pregenerate relevant styles
+    for row in range(ROWS):
+        _row = []
+        for col in range(COLUMNS):
+            cell = ws.cell(row=row+1, column=col+1)
+            cell.value = 1
+            font = {}
+            fill = PatternFill()
+            if formatData[row][col] & BOLD:
+                font['bold'] = True
+            if formatData[row][col] & ITALIC:
+                font['italic'] = True
+            if formatData[row][col] & UNDERLINE:
+                font['underline'] = 'single'
+            if formatData[row][col] & RED_BG:
+                fill = red_fill
+            cell.style = Style(font=Font(**font), fill=fill)
+
+    #wb.save(get_output_path('test_openpyxl_style_std_pregen.xlsx'))
+
+
+def read_workbook():
+    from openpyxl import load_workbook
+    folder = os.path.split(__file__)[0]
+    src = os.path.join(folder, "files", "very_large.xlsx")
+    wb = load_workbook(src)
+    return wb
+
+
+def rows(wb):
+    ws = wb.active
+    rows = ws.iter_rows()
+    for r, row in enumerate(rows):
+        for c, col in enumerate(row):
+            pass
+    print((r+1)* (c+1), "cells")
+
+
+def col_index1():
+    from openpyxl.cell import get_column_letter
+    for i in range(1, 18279):
+        c = get_column_letter(i)
+
+
+
+"""
+Sample use
+import cProfile
+ws = make_worksheet()
+cProfile.run("profiling.lxml_writer(ws)", sort="tottime")
+"""
+
+
+if __name__ == '__main__':
+    import cProfile
+    ws = make_worksheet()
+    #wb = read_workbook()
+    #cProfile.run("rows(wb)", sort="tottime")
+    #cProfile.run("make_worksheet()", sort="tottime")
+    #cProfile.run("lxml_writer(ws)", sort="tottime")
+    #generate_format_data()
+    #cProfile.run("styled_sheet()", sort="tottime")
+    #ws = make_dump_worksheet()
+    #cProfile.run("dump_writer(ws)", sort="tottime")
+    cProfile.run("col_index1()", sort="tottime")

+ 45 - 0
xlnt/benchmarks/disabled/reader.cpp

@@ -0,0 +1,45 @@
+import os
+import sys
+import timeit
+
+import openpyxl
+
+
+def reader(optimised):
+    """
+    Loop through all cells of a workbook
+    """
+    folder = os.path.split(__file__)[0]
+    src = os.path.join(folder, "files", "very_large.xlsx")
+    wb = openpyxl.load_workbook(src, use_iterators=optimised)
+    ws = wb.active
+    rows = ws.iter_rows()
+    for r, row in enumerate(rows):
+        for c, col in enumerate(row):
+            pass
+    print((r+1)* (c+1), "cells")
+
+def timer(fn):
+    """
+    Create a timeit call to a function and pass in keyword arguments.
+    The function is called twice, once using the standard workbook, then with the optimised one.
+    Time from the best of three is taken.
+    """
+    print("lxml", openpyxl.LXML)
+    result = []
+    for opt in (False, True,):
+        print("Workbook is {0}".format(opt and "optimised" or "not optimised"))
+        times = timeit.repeat("{0}({1})".format(fn.__name__, opt),
+                              setup="from __main__ import {0}".format(fn.__name__),
+                              number = 1,
+                              repeat = 3
+        )
+        print("{0:.2f}s".format(min(times)))
+        result.append(min(times))
+    std, opt = result
+    print("Optimised takes {0:.2%} time\n".format(opt/std))
+    return std, opt
+
+
+if __name__ == "__main__":
+    timer(reader)

+ 31 - 0
xlnt/benchmarks/disabled/speed.cpp

@@ -0,0 +1,31 @@
+"Benchmark some different implementations for cells"
+
+from openpyxl.compat import range
+
+from openpyxl.cell import Cell
+from openpyxl.cell.read_only import ReadOnlyCell
+from memory_profiler import memory_usage
+import time
+
+
+def standard():
+    c = Cell(None, "A", "0", None)
+
+def iterative():
+    c = ReadOnlyCell(None, None, None, 'n')
+
+def dictionary():
+    c = {'ws':'None', 'col':'A', 'row':0, 'value':1}
+
+
+if __name__ == '__main__':
+    initial_use = memory_usage(proc=-1, interval=1)[0]
+    for fn in (standard, iterative, dictionary):
+        t = time.time()
+        container = []
+        for i in range(1000000):
+            container.append(fn())
+        print("{0} {1} MB, {2:.2f}s".format(
+            fn.func_name,
+            memory_usage(proc=-1, interval=1)[0] - initial_use,
+            time.time() - t))

+ 36 - 0
xlnt/benchmarks/microbenchmarks/CMakeLists.txt

@@ -0,0 +1,36 @@
+# FetchContent added in cmake v3.11
+# https://cmake.org/cmake/help/v3.11/module/FetchContent.html
+# this file is behind a feature flag (XLNT_MICROBENCH_ENABLED) so the primary build is not affected
+cmake_minimum_required(VERSION 3.11)
+project(xlnt_ubench)
+
+# acquire google benchmark dependency
+# disable generation of the various test projects
+set(BENCHMARK_ENABLE_TESTING OFF)
+# gtest not required
+set(BENCHMARK_ENABLE_GTEST_TESTS OFF)
+
+include(FetchContent)
+FetchContent_Declare(
+	googlebenchmark
+	GIT_REPOSITORY 	https://github.com/google/benchmark
+	GIT_TAG			v1.5.0
+)
+# download if not already present
+FetchContent_GetProperties(googlebenchmark)
+if(NOT googlebenchmark_POPULATED)
+	FetchContent_Populate(googlebenchmark)
+	add_subdirectory(${googlebenchmark_SOURCE_DIR} ${googlebenchmark_BINARY_DIR})
+endif()
+# equivalent of add_subdirectory, now available for use
+FetchContent_MakeAvailable(googlebenchmark)
+
+
+add_executable(xlnt_ubench)
+target_sources(xlnt_ubench
+	PRIVATE
+		string_to_double.cpp
+		double_to_string.cpp
+)
+target_link_libraries(xlnt_ubench benchmark_main xlnt)
+target_compile_features(xlnt_ubench PRIVATE cxx_std_17)

+ 207 - 0
xlnt/benchmarks/microbenchmarks/double_to_string.cpp

@@ -0,0 +1,207 @@
+// A core part of the xlsx serialisation routine is taking doubles from memory and stringifying them
+// this has a few requirements
+// - expect strings in the form 1234.56 (i.e. no thousands seperator, '.' used for the decimal seperator)
+// - outputs up to 15 significant figures (excel only serialises numbers up to 15sf)
+
+#include "benchmark/benchmark.h"
+#include <locale>
+#include <random>
+#include <sstream>
+
+namespace {
+
+// setup a large quantity of random doubles as strings
+template <bool Decimal_Locale = true>
+class RandomFloats : public benchmark::Fixture
+{
+    static constexpr size_t Number_of_Elements = 1 << 20;
+    static_assert(Number_of_Elements > 1'000'000, "ensure a decent set of random values is generated");
+
+    std::vector<double> inputs;
+
+    size_t index = 0;
+    const char *locale_str = nullptr;
+
+public:
+    void SetUp(const ::benchmark::State &state)
+    {
+        if (Decimal_Locale)
+        {
+            locale_str = setlocale(LC_ALL, "C");
+        }
+        else
+        {
+            locale_str = setlocale(LC_ALL, "de-DE");
+        }
+        std::random_device rd; // obtain a seed for the random number engine
+        std::mt19937 gen(rd());
+        // doing full range is stupid (<double>::min/max()...), it just ends up generating very large numbers
+        // uniform is probably not the best distribution to use here, but it will do for now
+        std::uniform_real_distribution<double> dis(-1'000, 1'000);
+        // generate a large quantity of doubles to deserialise
+        inputs.reserve(Number_of_Elements);
+        for (int i = 0; i < Number_of_Elements; ++i)
+        {
+            double d = dis(gen);
+            inputs.push_back(d);
+        }
+    }
+
+    void TearDown(const ::benchmark::State &state)
+    {
+        // restore locale
+        setlocale(LC_ALL, locale_str);
+        // gbench is keeping the fixtures alive somewhere, need to clear the data after use
+        inputs = std::vector<double>{};
+    }
+
+    double &get_rand()
+    {
+        return inputs[++index & (Number_of_Elements - 1)];
+    }
+};
+
+/// Takes in a double and outputs a string form of that number which will
+/// serialise and deserialise without loss of precision
+std::string serialize_number_to_string(double num)
+{
+    // more digits and excel won't match
+    constexpr int Excel_Digit_Precision = 15; //sf
+    std::stringstream ss;
+    ss.precision(Excel_Digit_Precision);
+    ss << num;
+    return ss.str();
+}
+
+class number_serialiser
+{
+    static constexpr int Excel_Digit_Precision = 15; //sf
+    std::ostringstream ss;
+
+public:
+    explicit number_serialiser()
+    {
+        ss.precision(Excel_Digit_Precision);
+        ss.imbue(std::locale("C"));
+    }
+
+    std::string serialise(double d)
+    {
+        ss.str(""); // reset string buffer
+        ss.clear(); // reset any error flags
+        ss << d;
+        return ss.str();
+    }
+};
+
+class number_serialiser_mk2
+{
+    static constexpr int Excel_Digit_Precision = 15; //sf
+    bool should_convert_comma;
+
+    void convert_comma(char *buf, int len)
+    {
+        char *buf_end = buf + len;
+        char *decimal = std::find(buf, buf_end, ',');
+        if (decimal != buf_end)
+        {
+            *decimal = '.';
+        }
+    }
+
+public:
+    explicit number_serialiser_mk2()
+        : should_convert_comma(std::use_facet<std::numpunct<char>>(std::locale{}).decimal_point() == ',')
+    {
+    }
+
+    std::string serialise(double d)
+    {
+        char buf[Excel_Digit_Precision + 1]; // need space for trailing '\0'
+        int len = snprintf(buf, sizeof(buf), "%.15g", d);
+        if (should_convert_comma)
+        {
+            convert_comma(buf, len);
+        }
+        return std::string(buf, len);
+    }
+};
+
+using RandFloats = RandomFloats<true>;
+using RandFloatsComma = RandomFloats<false>;
+} // namespace
+
+BENCHMARK_F(RandFloats, string_from_double_sstream)
+(benchmark::State &state)
+{
+    while (state.KeepRunning())
+    {
+        benchmark::DoNotOptimize(
+            serialize_number_to_string(get_rand()));
+    }
+}
+
+BENCHMARK_F(RandFloats, string_from_double_sstream_cached)
+(benchmark::State &state)
+{
+    number_serialiser ser;
+    while (state.KeepRunning())
+    {
+        benchmark::DoNotOptimize(
+            ser.serialise(get_rand()));
+    }
+}
+
+BENCHMARK_F(RandFloats, string_from_double_snprintf)
+(benchmark::State &state)
+{
+    while (state.KeepRunning())
+    {
+        char buf[16];
+        int len = snprintf(buf, sizeof(buf), "%.15g", get_rand());
+
+        benchmark::DoNotOptimize(
+            std::string(buf, len));
+    }
+}
+
+BENCHMARK_F(RandFloats, string_from_double_snprintf_fixed)
+(benchmark::State &state)
+{
+    number_serialiser_mk2 ser;
+    while (state.KeepRunning())
+    {
+        benchmark::DoNotOptimize(
+            ser.serialise(get_rand()));
+    }
+}
+
+// locale names are different between OS's, and std::from_chars is only complete in MSVC
+#ifdef _MSC_VER
+
+#include <charconv>
+BENCHMARK_F(RandFloats, string_from_double_std_to_chars)
+(benchmark::State &state)
+{
+    while (state.KeepRunning())
+    {
+        char buf[16];
+        std::to_chars_result result = std::to_chars(buf, buf + std::size(buf), get_rand());
+
+        benchmark::DoNotOptimize(
+            std::string(buf, result.ptr));
+    }
+}
+
+BENCHMARK_F(RandFloatsComma, string_from_double_snprintf_fixed_comma)
+(benchmark::State &state)
+{
+    number_serialiser_mk2 ser;
+    while (state.KeepRunning())
+    {
+        benchmark::DoNotOptimize(
+            ser.serialise(get_rand()));
+    }
+}
+
+#endif

+ 223 - 0
xlnt/benchmarks/microbenchmarks/string_to_double.cpp

@@ -0,0 +1,223 @@
+// A core part of the xlsx parsing routine is taking strings from the xml parser and parsing these to a double
+// this has a few requirements
+// - expect strings in the form 1234.56 (i.e. no thousands seperator, '.' used for the decimal seperator)
+// - handles atleast 15 significant figures (excel only serialises numbers up to 15sf)
+
+#include <benchmark/benchmark.h>
+#include <locale>
+#include <random>
+#include <sstream>
+
+namespace {
+
+// setup a large quantity of random doubles as strings
+template <bool Decimal_Locale = true>
+class RandomFloatStrs : public benchmark::Fixture
+{
+    static constexpr size_t Number_of_Elements = 1 << 20;
+    static_assert(Number_of_Elements > 1'000'000, "ensure a decent set of random values is generated");
+
+    std::vector<std::string> inputs;
+
+    size_t index = 0;
+    const char *locale_str = nullptr;
+
+public:
+    void SetUp(const ::benchmark::State &state)
+    {
+        if (Decimal_Locale)
+        {
+            locale_str = setlocale(LC_ALL, "C");
+        }
+        else
+        {
+            locale_str = setlocale(LC_ALL, "de-DE");
+        }
+        std::random_device rd; // obtain a seed for the random number engine
+        std::mt19937 gen(rd());
+        // doing full range is stupid (<double>::min/max()...), it just ends up generating very large numbers
+        // uniform is probably not the best distribution to use here, but it will do for now
+        std::uniform_real_distribution<double> dis(-1'000, 1'000);
+        // generate a large quantity of doubles to deserialise
+        inputs.reserve(Number_of_Elements);
+        for (int i = 0; i < Number_of_Elements; ++i)
+        {
+            double d = dis(gen);
+            char buf[16];
+            snprintf(buf, 16, "%.15f", d);
+            inputs.push_back(std::string(buf));
+        }
+    }
+
+    void TearDown(const ::benchmark::State &state)
+    {
+        // restore locale
+        setlocale(LC_ALL, locale_str);
+        // gbench is keeping the fixtures alive somewhere, need to clear the data after use
+        inputs = std::vector<std::string>{};
+    }
+
+    std::string &get_rand()
+    {
+        return inputs[++index & (Number_of_Elements - 1)];
+    }
+};
+
+// method used by xlsx_consumer.cpp in commit - ba01de47a7d430764c20ec9ac9600eec0eb38bcf
+// std::istringstream with the locale set to "C"
+struct number_converter
+{
+    number_converter()
+    {
+        stream.imbue(std::locale("C"));
+    }
+
+    double stold(const std::string &s)
+    {
+        stream.str(s);
+        stream.clear();
+        stream >> result;
+        return result;
+    }
+
+    std::istringstream stream;
+    double result;
+};
+
+
+// to resolve the locale issue with strtod, a little preprocessing of the input is required
+struct number_converter_mk2
+{
+    explicit number_converter_mk2()
+        : should_convert_to_comma(std::use_facet<std::numpunct<char>>(std::locale{}).decimal_point() == ',')
+    {
+    }
+
+    double stold(std::string &s) const noexcept
+    {
+        assert(!s.empty());
+        if (should_convert_to_comma)
+        {
+            auto decimal_pt = std::find(s.begin(), s.end(), '.');
+            if (decimal_pt != s.end())
+            {
+                *decimal_pt = ',';
+            }
+        }
+        return strtod(s.c_str(), nullptr);
+    }
+
+    double stold(const std::string &s) const
+    {
+        assert(!s.empty());
+        if (!should_convert_to_comma)
+        {
+            return strtod(s.c_str(), nullptr);
+        }
+        std::string copy(s);
+        auto decimal_pt = std::find(copy.begin(), copy.end(), '.');
+        if (decimal_pt != copy.end())
+        {
+            *decimal_pt = ',';
+        }
+        return strtod(copy.c_str(), nullptr);
+    }
+
+private:
+    bool should_convert_to_comma = false;
+};
+
+using RandFloatStrs = RandomFloatStrs<true>;
+// german locale uses ',' as the seperator
+using RandFloatCommaStrs = RandomFloatStrs<false>;
+} // namespace
+
+BENCHMARK_F(RandFloatStrs, double_from_string_sstream)
+(benchmark::State &state)
+{
+    number_converter converter;
+    while (state.KeepRunning())
+    {
+        benchmark::DoNotOptimize(
+            converter.stold(get_rand()));
+    }
+}
+
+// using strotod
+// https://en.cppreference.com/w/cpp/string/byte/strtof
+// this naive usage is broken in the face of locales (fails condition 1)
+#include <cstdlib>
+BENCHMARK_F(RandFloatStrs, double_from_string_strtod)
+(benchmark::State &state)
+{
+    while (state.KeepRunning())
+    {
+        benchmark::DoNotOptimize(
+            strtod(get_rand().c_str(), nullptr));
+    }
+}
+
+BENCHMARK_F(RandFloatStrs, double_from_string_strtod_fixed)
+(benchmark::State &state)
+{
+    number_converter_mk2 converter;
+    while (state.KeepRunning())
+    {
+        benchmark::DoNotOptimize(
+            converter.stold(get_rand()));
+    }
+}
+
+BENCHMARK_F(RandFloatStrs, double_from_string_strtod_fixed_const_ref)
+(benchmark::State &state)
+{
+    number_converter_mk2 converter;
+    while (state.KeepRunning())
+    {
+        const std::string &inp = get_rand();
+        benchmark::DoNotOptimize(
+            converter.stold(inp));
+    }
+}
+
+// locale names are different between OS's, and std::from_chars is only complete in MSVC
+#ifdef _MSC_VER
+
+#include <charconv>
+BENCHMARK_F(RandFloatStrs, double_from_string_std_from_chars)
+(benchmark::State &state)
+{
+    while (state.KeepRunning())
+    {
+        const std::string &input = get_rand();
+        double output;
+        benchmark::DoNotOptimize(
+            std::from_chars(input.data(), input.data() + input.size(), output));
+    }
+}
+
+// not using the standard "C" locale with '.' seperator
+BENCHMARK_F(RandFloatCommaStrs, double_from_string_strtod_fixed_comma_ref)
+(benchmark::State &state)
+{
+    number_converter_mk2 converter;
+    while (state.KeepRunning())
+    {
+        benchmark::DoNotOptimize(
+            converter.stold(get_rand()));
+    }
+}
+
+BENCHMARK_F(RandFloatCommaStrs, double_from_string_strtod_fixed_comma_const_ref)
+(benchmark::State &state)
+{
+    number_converter_mk2 converter;
+    while (state.KeepRunning())
+    {
+        const std::string &inp = get_rand();
+        benchmark::DoNotOptimize(
+            converter.stold(inp));
+    }
+}
+
+#endif

+ 58 - 0
xlnt/benchmarks/spreadsheet-load.cpp

@@ -0,0 +1,58 @@
+#include <xlnt/xlnt.hpp>
+#include <chrono>
+#include <helpers/path_helper.hpp>
+
+namespace {
+using milliseconds_d = std::chrono::duration<double, std::milli>;
+
+void run_load_test(const xlnt::path &file, int runs = 10)
+{
+    std::cout << file.string() << "\n\n";
+
+    xlnt::workbook wb;
+    std::vector<std::chrono::steady_clock::duration> test_timings;
+
+    for (int i = 0; i < runs; ++i)
+    {
+        auto start = std::chrono::steady_clock::now();
+        wb.load(file);
+
+        auto end = std::chrono::steady_clock::now();
+        wb.clear();
+        test_timings.push_back(end - start);
+
+        std::cout << milliseconds_d(test_timings.back()).count() << " ms\n";
+    }
+}
+
+void run_save_test(const xlnt::path &file, int runs = 10)
+{
+    std::cout << file.string() << "\n\n";
+
+    xlnt::workbook wb;
+    wb.load(file);
+    const xlnt::path save_path(file.filename());
+
+    std::vector<std::chrono::steady_clock::duration> test_timings;
+
+    for (int i = 0; i < runs; ++i)
+    {
+        auto start = std::chrono::steady_clock::now();
+
+        wb.save(save_path);
+
+        auto end = std::chrono::steady_clock::now();
+        test_timings.push_back(end - start);
+        std::cout << milliseconds_d(test_timings.back()).count() << " ms\n";
+    }
+}
+} // namespace
+
+int main()
+{
+    run_load_test(path_helper::benchmark_file("large.xlsx"));
+    run_load_test(path_helper::benchmark_file("very_large.xlsx"));
+
+    run_save_test(path_helper::benchmark_file("large.xlsx"));
+    run_save_test(path_helper::benchmark_file("very_large.xlsx"));
+}

+ 250 - 0
xlnt/benchmarks/styles.cpp

@@ -0,0 +1,250 @@
+// Copyright (c) 2017-2018 Thomas Fussell
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// 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, ARISING 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 <chrono>
+#include <string>
+#include <iostream>
+#include <sstream> 
+#include <iterator>
+#include <random>
+
+#include <helpers/timing.hpp>
+#include <xlnt/xlnt.hpp>
+
+namespace {
+
+std::size_t random_index(std::size_t max)
+{
+    static std::random_device rd;
+    static std::mt19937 gen(rd());
+
+    std::uniform_int_distribution<> dis(0, static_cast<int>(max - 1));
+
+    return dis(gen);
+}
+
+void generate_all_formats(xlnt::workbook &wb, std::vector<xlnt::format>& formats)
+{
+	const auto vertical_alignments = std::vector<xlnt::vertical_alignment>
+	{
+		xlnt::vertical_alignment::center,
+		xlnt::vertical_alignment::justify,
+		xlnt::vertical_alignment::top,
+		xlnt::vertical_alignment::bottom
+	};
+
+	const auto horizontal_alignments = std::vector<xlnt::horizontal_alignment>
+	{
+		xlnt::horizontal_alignment::center,
+		xlnt::horizontal_alignment::center_continuous,
+		xlnt::horizontal_alignment::general,
+		xlnt::horizontal_alignment::justify,
+		xlnt::horizontal_alignment::left,
+		xlnt::horizontal_alignment::right
+	};
+
+	const auto font_names = std::vector<std::string>
+	{
+		"Calibri",
+		"Tahoma",
+		"Arial",
+		"Times New Roman"
+	};
+
+	const auto font_sizes = std::vector<double>
+	{
+		11.,
+		13.,
+		15.,
+		17.,
+		19.,
+		21.,
+		23.,
+		25.,
+		27.,
+		29.,
+		31.,
+		33.,
+		35.
+	};
+
+	const auto underline_options = std::vector<xlnt::font::underline_style>
+	{
+		xlnt::font::underline_style::single,
+		xlnt::font::underline_style::none
+	};
+
+	for (auto vertical_alignment : vertical_alignments)
+	{
+		for (auto horizontal_alignment : horizontal_alignments)
+		{
+			for (auto name : font_names)
+			{
+				for (auto size : font_sizes)
+				{
+					for (auto bold : { true, false })
+					{
+						for (auto underline : underline_options)
+						{
+							for (auto italic : { true, false })
+							{
+								auto fmt = wb.create_format();
+
+								xlnt::font f;
+								f.name(name);
+								f.size(size);
+								f.italic(italic);
+								f.underline(underline);
+								f.bold(bold);
+								fmt.font(f);
+
+								xlnt::alignment a;
+								a.vertical(vertical_alignment);
+								a.horizontal(horizontal_alignment);
+								fmt.alignment(a);
+
+								formats.push_back(fmt);
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+}
+
+
+xlnt::workbook non_optimized_workbook_formats(int rows_number, int columns_number)
+{
+	using xlnt::benchmarks::current_time;
+
+	xlnt::workbook wb;
+	std::vector<xlnt::format> formats;
+	auto start = current_time();
+
+	generate_all_formats(wb, formats);
+
+	auto elapsed = current_time() - start;
+
+	std::cout << "elapsed " << elapsed / 1000.0 << ". generate_all_formats. number of unique formats " << formats.size() << std::endl;
+
+	start = current_time();
+	auto worksheet = wb[random_index(wb.sheet_count())];
+	auto cells_proceeded = 0;
+	for (int row_idx = 1; row_idx <= rows_number; row_idx++)
+	{
+		for (int col_idx = 1; col_idx <= columns_number; col_idx++)
+		{
+			auto cell = worksheet.cell(xlnt::cell_reference((xlnt::column_t)col_idx, (xlnt::row_t)row_idx));
+			std::ostringstream string_stm;
+			string_stm << "Col: " << col_idx << "Row: " << row_idx;
+			cell.value(string_stm.str());
+			cell.format(formats.at(random_index(formats.size())));
+			cells_proceeded++;
+		}
+	}
+
+	elapsed = current_time() - start;
+
+	std::cout << "elapsed " << elapsed / 1000.0 << ". set values and formats for cells. cells proceeded " << cells_proceeded << std::endl;
+
+	return wb;
+}
+
+void to_save_profile(xlnt::workbook &wb, const std::string &f)
+{
+    using xlnt::benchmarks::current_time;
+
+    auto start = current_time();
+    wb.save(f);
+    auto elapsed = current_time() - start;
+
+    std::cout << "elapsed " << elapsed / 1000.0 << ". save workbook." << std::endl;
+}
+
+void to_load_profile(xlnt::workbook &wb, const std::string &f)
+{
+	using xlnt::benchmarks::current_time;
+	
+	auto start = current_time();	
+	wb.load(f);
+	auto elapsed = current_time() - start;
+
+	std::cout << "elapsed " << elapsed / 1000.0 << ". load workbook." << std::endl;
+}
+
+void read_formats_profile(xlnt::workbook &wb, int rows_number, int columns_number)
+{
+	using xlnt::benchmarks::current_time;
+
+	std::vector<std::string> values;
+	std::vector<xlnt::format> formats;
+	auto start = current_time();
+	auto worksheet = wb[random_index(wb.sheet_count())];
+	for (int row_idx = 1; row_idx <= rows_number; row_idx++)
+	{
+		for (int col_idx = 1; col_idx <= columns_number; col_idx++)
+		{
+			auto cell = worksheet.cell(xlnt::cell_reference((xlnt::column_t)col_idx, (xlnt::row_t)row_idx));
+			values.push_back(cell.value<std::string>());
+			formats.push_back(cell.format());
+		}
+	}
+
+	auto elapsed = current_time() - start;
+
+	std::cout << "elapsed " << elapsed / 1000.0 << ". read values and formats for cells. values count " << values.size() 
+		<< ". formats count " << formats.size() << std::endl;
+}
+
+} // namespace
+
+int main(int argc, char * argv[])
+{
+    int rows_number = 1000;
+	int columns_number = 10;
+
+	try 
+	{
+		if (argc > 1)
+			rows_number = std::stoi(argv[1]);
+
+		if (argc > 2)
+			columns_number = std::stoi(argv[2]);
+
+		std::cout << "started. number of rows " << rows_number << ", number of columns " << columns_number << std::endl;
+		auto wb = non_optimized_workbook_formats(rows_number, columns_number);
+		auto f = "temp-formats.xlsx";
+		to_save_profile(wb, f);
+
+		xlnt::workbook load_formats_wb;
+		to_load_profile(load_formats_wb, f);
+		read_formats_profile(load_formats_wb, rows_number, columns_number);
+	}
+	catch(std::exception& ex)
+	{
+		std::cout << "failed. " << ex.what() << std::endl;
+	}
+
+    return 0;
+}

+ 90 - 0
xlnt/benchmarks/writer.cpp

@@ -0,0 +1,90 @@
+// Copyright (c) 2017-2018 Thomas Fussell
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// 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, ARISING 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 <chrono>
+#include <iostream>
+
+#include <helpers/timing.hpp>
+#include <xlnt/xlnt.hpp>
+
+namespace {
+
+// Create a worksheet with variable width rows. Because data must be
+// serialised row by row it is often the width of the rows which is most
+// important.
+void writer(int cols, int rows)
+{
+    xlnt::workbook wb;
+    auto ws = wb.create_sheet();
+
+    for(int index = 0; index < rows; index++)
+    {
+        if (rows >= 10 && (index + 1) % (rows / 10) == 0)
+        {
+            std::string progress = std::string((index + 1) / (1 + rows / 10), '.');
+            std::cout << "\r" << progress;
+        }
+
+		for (int i = 0; i < cols; i++)
+		{
+			ws.cell(xlnt::cell_reference(i + 1, index + 1)).value(i);
+		}
+    }
+    std::cout << '\n';
+
+    auto filename = "benchmark.xlsx";
+    wb.save(filename);
+}
+
+// Create a timeit call to a function and pass in keyword arguments.
+// The function is called twice, once using the standard workbook, then with the optimised one.
+// Time from the best of three is taken.
+void timer(std::function<void(int, int)> fn, int cols, int rows)
+{
+    const auto repeat = std::size_t(3);
+    std::chrono::duration<double, std::milli> time{};
+    std::cout << cols << " cols " << rows << " rows" << std::endl;
+    fn(rows, cols); // 1 cold run
+
+    for(int i = 0; i < repeat; i++)
+    {
+        auto start = std::chrono::high_resolution_clock::now();
+        fn(cols, rows);
+        time += std::chrono::high_resolution_clock::now() - start;
+    }
+
+    std::cout << time.count() / repeat << " ms per iteration" << '\n' << '\n';
+}
+
+} // namespace
+
+int main()
+{
+    timer(&writer, 10000, 1);
+    timer(&writer, 1000, 10);
+    timer(&writer, 100, 100);
+    timer(&writer, 10, 1000);
+    timer(&writer, 1, 10000);
+
+    return 0;
+}

+ 712 - 0
xlnt/include/xlnt/cell/cell.hpp

@@ -0,0 +1,712 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <memory>
+#include <string>
+#include <unordered_map>
+
+#include <xlnt/xlnt_config.hpp>
+#include <xlnt/cell/cell_type.hpp>
+#include <xlnt/cell/index_types.hpp>
+#include <xlnt/cell/rich_text.hpp>
+
+namespace xlnt {
+
+enum class calendar;
+
+class alignment;
+class base_format;
+class border;
+class cell_reference;
+class comment;
+class fill;
+class font;
+class format;
+class number_format;
+class protection;
+class range;
+class relationship;
+class style;
+class workbook;
+class worksheet;
+class xlsx_consumer;
+class xlsx_producer;
+class phonetic_pr;
+
+struct date;
+struct datetime;
+struct time;
+struct timedelta;
+
+namespace detail {
+
+class xlsx_consumer;
+class xlsx_producer;
+
+struct cell_impl;
+
+} // namespace detail
+
+/// <summary>
+/// Describes a unit of data in a worksheet at a specific coordinate and its
+/// associated properties.
+/// </summary>
+/// <remarks>
+/// Properties of interest include style, type, value, and address.
+/// The Cell class is required to know its value and type, display options,
+/// and any other features of an Excel cell.Utilities for referencing
+/// cells using Excel's 'A1' column/row nomenclature are also provided.
+/// </remarks>
+class XLNT_API cell
+{
+public:
+    /// <summary>
+    /// Alias xlnt::cell_type to xlnt::cell::type since it looks nicer.
+    /// </summary>
+    using type = cell_type;
+
+    /// <summary>
+    /// Returns a map of error strings such as \#DIV/0! and their associated indices.
+    /// </summary>
+    static const std::unordered_map<std::string, int> &error_codes();
+
+    /// <summary>
+    /// Default copy constructor.
+    /// </summary>
+    cell(const cell &) = default;
+
+    // value
+
+    /// <summary>
+    /// Returns true if value has been set and has not been cleared using cell::clear_value().
+    /// </summary>
+    bool has_value() const;
+
+    /// <summary>
+    /// Returns the value of this cell as an instance of type T.
+    /// Overloads exist for most C++ fundamental types like bool, int, etc. as well
+    /// as for std::string and xlnt datetime types: date, time, datetime, and timedelta.
+    /// </summary>
+    template <typename T>
+    T value() const;
+
+    /// <summary>
+    /// Makes this cell have a value of type null.
+    /// All other cell attributes are retained.
+    /// </summary>
+    void clear_value();
+
+    /// <summary>
+    /// Sets the type of this cell to null.
+    /// </summary>
+    void value(std::nullptr_t);
+
+    /// <summary>
+    /// Sets the value of this cell to the given boolean value.
+    /// </summary>
+    void value(bool boolean_value);
+
+    /// <summary>
+    /// Sets the value of this cell to the given value.
+    /// </summary>
+    void value(int int_value);
+
+    /// <summary>
+    /// Sets the value of this cell to the given value.
+    /// </summary>
+    void value(unsigned int int_value);
+
+    /// <summary>
+    /// Sets the value of this cell to the given value.
+    /// </summary>
+    void value(long long int int_value);
+
+    /// <summary>
+    /// Sets the value of this cell to the given value.
+    /// </summary>
+    void value(unsigned long long int int_value);
+
+    /// <summary>
+    /// Sets the value of this cell to the given value.
+    /// </summary>
+    void value(float float_value);
+
+    /// <summary>
+    /// Sets the value of this cell to the given value.
+    /// </summary>
+    void value(double float_value);
+
+    /// <summary>
+    /// Sets the value of this cell to the given value.
+    /// </summary>
+    void value(const date &date_value);
+
+    /// <summary>
+    /// Sets the value of this cell to the given value.
+    /// </summary>
+    void value(const time &time_value);
+
+    /// <summary>
+    /// Sets the value of this cell to the given value.
+    /// </summary>
+    void value(const datetime &datetime_value);
+
+    /// <summary>
+    /// Sets the value of this cell to the given value.
+    /// </summary>
+    void value(const timedelta &timedelta_value);
+
+    /// <summary>
+    /// Sets the value of this cell to the given value.
+    /// </summary>
+    void value(const std::string &string_value);
+
+    /// <summary>
+    /// Sets the value of this cell to the given value.
+    /// </summary>
+    void value(const char *string_value);
+
+    /// <summary>
+    /// Sets the value of this cell to the given value.
+    /// </summary>
+    void value(const rich_text &text_value);
+
+    /// <summary>
+    /// Sets the value and formatting of this cell to that of other_cell.
+    /// </summary>
+    void value(const cell other_cell);
+
+    /// <summary>
+    /// Analyzes string_value to determine its type, convert it to that type,
+    /// and set the value of this cell to that converted value.
+    /// </summary>
+    void value(const std::string &string_value, bool infer_type);
+
+    /// <summary>
+    /// Returns the type of this cell.
+    /// </summary>
+    type data_type() const;
+
+    /// <summary>
+    /// Sets the type of this cell. This should usually be done indirectly
+    /// by setting the value of the cell to a value of that type.
+    /// </summary>
+    void data_type(type t);
+
+    // properties
+
+    /// <summary>
+    /// There's no reason to keep a cell which has no value and is not a placeholder.
+    /// Returns true if this cell has no value, style, isn't merged, etc.
+    /// </summary>
+    bool garbage_collectible() const;
+
+    /// <summary>
+    /// Returns true iff this cell's number format matches a date format.
+    /// </summary>
+    bool is_date() const;
+
+    // position
+
+    /// <summary>
+    /// Returns a cell_reference that points to the location of this cell.
+    /// </summary>
+    cell_reference reference() const;
+
+    /// <summary>
+    /// Returns the column of this cell.
+    /// </summary>
+    column_t column() const;
+
+    /// <summary>
+    /// Returns the numeric index (A == 1) of the column of this cell.
+    /// </summary>
+    column_t::index_t column_index() const;
+
+    /// <summary>
+    /// Returns the row of this cell.
+    /// </summary>
+    row_t row() const;
+
+    /// <summary>
+    /// Returns the location of this cell as an ordered pair (left, top).
+    /// </summary>
+    std::pair<int, int> anchor() const;
+
+    // hyperlink
+
+    /// <summary>
+    /// Returns the relationship of this cell's hyperlink.
+    /// </summary>
+    class hyperlink hyperlink() const;
+
+    /// <summary>
+    /// Adds a hyperlink to this cell pointing to the URI of the given value and sets
+    /// the text value of the cell to the given parameter.
+    /// </summary>
+    void hyperlink(const std::string &url, const std::string &display = "");
+
+    /// <summary>
+    /// Adds an internal hyperlink to this cell pointing to the given cell.
+    /// </summary>
+    void hyperlink(xlnt::cell target, const std::string &display = "");
+
+    /// <summary>
+    /// Adds an internal hyperlink to this cell pointing to the given range.
+    /// </summary>
+    void hyperlink(xlnt::range target, const std::string &display = "");
+
+    /// <summary>
+    /// Returns true if this cell has a hyperlink set.
+    /// </summary>
+    bool has_hyperlink() const;
+
+    // computed formatting
+
+    /// <summary>
+    /// Returns the alignment that should be used when displaying this cell
+    /// graphically based on the workbook default, the cell-level format,
+    /// and the named style applied to the cell in that order.
+    /// </summary>
+    class alignment computed_alignment() const;
+
+    /// <summary>
+    /// Returns the border that should be used when displaying this cell
+    /// graphically based on the workbook default, the cell-level format,
+    /// and the named style applied to the cell in that order.
+    /// </summary>
+    class border computed_border() const;
+
+    /// <summary>
+    /// Returns the fill that should be used when displaying this cell
+    /// graphically based on the workbook default, the cell-level format,
+    /// and the named style applied to the cell in that order.
+    /// </summary>
+    class fill computed_fill() const;
+
+    /// <summary>
+    /// Returns the font that should be used when displaying this cell
+    /// graphically based on the workbook default, the cell-level format,
+    /// and the named style applied to the cell in that order.
+    /// </summary>
+    class font computed_font() const;
+
+    /// <summary>
+    /// Returns the number format that should be used when displaying this cell
+    /// graphically based on the workbook default, the cell-level format,
+    /// and the named style applied to the cell in that order.
+    /// </summary>
+    class number_format computed_number_format() const;
+
+    /// <summary>
+    /// Returns the protection that should be used when displaying this cell
+    /// graphically based on the workbook default, the cell-level format,
+    /// and the named style applied to the cell in that order.
+    /// </summary>
+    class protection computed_protection() const;
+
+    // format
+
+    /// <summary>
+    /// Returns true if this cell has had a format applied to it.
+    /// </summary>
+    bool has_format() const;
+
+    /// <summary>
+    /// Returns the format applied to this cell. If this cell has no
+    /// format, an invalid_attribute exception will be thrown.
+    /// </summary>
+    const class format format() const;
+
+    /// <summary>
+    /// Applies the cell-level formatting of new_format to this cell.
+    /// </summary>
+    void format(const class format new_format);
+
+    /// <summary>
+    /// Removes the cell-level formatting from this cell.
+    /// This doesn't affect the style that may also be applied to the cell.
+    /// Throws an invalid_attribute exception if no format is applied.
+    /// </summary>
+    void clear_format();
+
+    /// <summary>
+    /// Returns the number format of this cell.
+    /// </summary>
+    class number_format number_format() const;
+
+    /// <summary>
+    /// Creates a new format in the workbook, sets its number_format
+    /// to the given format, and applies the format to this cell.
+    /// </summary>
+    void number_format(const class number_format &format);
+
+    /// <summary>
+    /// Returns the font applied to the text in this cell.
+    /// </summary>
+    class font font() const;
+
+    /// <summary>
+    /// Creates a new format in the workbook, sets its font
+    /// to the given font, and applies the format to this cell.
+    /// </summary>
+    void font(const class font &font_);
+
+    /// <summary>
+    /// Returns the fill applied to this cell.
+    /// </summary>
+    class fill fill() const;
+
+    /// <summary>
+    /// Creates a new format in the workbook, sets its fill
+    /// to the given fill, and applies the format to this cell.
+    /// </summary>
+    void fill(const class fill &fill_);
+
+    /// <summary>
+    /// Returns the border of this cell.
+    /// </summary>
+    class border border() const;
+
+    /// <summary>
+    /// Creates a new format in the workbook, sets its border
+    /// to the given border, and applies the format to this cell.
+    /// </summary>
+    void border(const class border &border_);
+
+    /// <summary>
+    /// Returns the alignment of the text in this cell.
+    /// </summary>
+    class alignment alignment() const;
+
+    /// <summary>
+    /// Creates a new format in the workbook, sets its alignment
+    /// to the given alignment, and applies the format to this cell.
+    /// </summary>
+    void alignment(const class alignment &alignment_);
+
+    /// <summary>
+    /// Returns the protection of this cell.
+    /// </summary>
+    class protection protection() const;
+
+    /// <summary>
+    /// Creates a new format in the workbook, sets its protection
+    /// to the given protection, and applies the format to this cell.
+    /// </summary>
+    void protection(const class protection &protection_);
+
+    // style
+
+    /// <summary>
+    /// Returns true if this cell has had a style applied to it.
+    /// </summary>
+    bool has_style() const;
+
+    /// <summary>
+    /// Returns a wrapper pointing to the named style applied to this cell.
+    /// </summary>
+    class style style();
+
+    /// <summary>
+    /// Returns a wrapper pointing to the named style applied to this cell.
+    /// </summary>
+    const class style style() const;
+
+    /// <summary>
+    /// Sets the named style applied to this cell to a style named style_name.
+    /// Equivalent to style(new_style.name()).
+    /// </summary>
+    void style(const class style &new_style);
+
+    /// <summary>
+    /// Sets the named style applied to this cell to a style named style_name.
+    /// If this style has not been previously created in the workbook, a
+    /// key_not_found exception will be thrown.
+    /// </summary>
+    void style(const std::string &style_name);
+
+    /// <summary>
+    /// Removes the named style from this cell.
+    /// An invalid_attribute exception will be thrown if this cell has no style.
+    /// This will not affect the cell format of the cell.
+    /// </summary>
+    void clear_style();
+
+    // formula
+
+    /// <summary>
+    /// Returns the string representation of the formula applied to this cell.
+    /// </summary>
+    std::string formula() const;
+
+    /// <summary>
+    /// Sets the formula of this cell to the given value.
+    /// This formula string should begin with '='.
+    /// </summary>
+    void formula(const std::string &formula);
+
+    /// <summary>
+    /// Removes the formula from this cell. After this is called, has_formula() will return false.
+    /// </summary>
+    void clear_formula();
+
+    /// <summary>
+    /// Returns true if this cell has had a formula applied to it.
+    /// </summary>
+    bool has_formula() const;
+
+    // printing
+
+    /// <summary>
+    /// Returns a string representing the value of this cell. If the data type is not a string,
+    /// it will be converted according to the number format.
+    /// </summary>
+    std::string to_string() const;
+
+    // merging
+
+    /// <summary>
+    /// Returns true iff this cell has been merged with one or more
+    /// surrounding cells.
+    /// </summary>
+    bool is_merged() const;
+
+    /// <summary>
+    /// Makes this a merged cell iff merged is true.
+    /// Generally, this shouldn't be called directly. Instead,
+    /// use worksheet::merge_cells on its parent worksheet.
+    /// </summary>
+    void merged(bool merged);
+
+    // phonetics
+
+    /// <summary>
+    /// Returns true if this cell is set to show phonetic information.
+    /// </summary>
+    bool phonetics_visible() const;
+
+    /// <summary>
+    /// Enables the display of phonetic information on this cell.
+    /// </summary>
+    void show_phonetics(bool phonetics);
+
+    /// <summary>
+    /// Returns the error string that is stored in this cell.
+    /// </summary>
+    std::string error() const;
+
+    /// <summary>
+    /// Directly assigns the value of this cell to be the given error.
+    /// </summary>
+    void error(const std::string &error);
+
+    /// <summary>
+    /// Returns a cell from this cell's parent workbook at
+    /// a relative offset given by the parameters.
+    /// </summary>
+    cell offset(int column, int row);
+
+    /// <summary>
+    /// Returns the worksheet that owns this cell.
+    /// </summary>
+    class worksheet worksheet();
+
+    /// <summary>
+    /// Returns the worksheet that owns this cell.
+    /// </summary>
+    const class worksheet worksheet() const;
+
+    /// <summary>
+    /// Returns the workbook of the worksheet that owns this cell.
+    /// </summary>
+    class workbook &workbook();
+
+    /// <summary>
+    /// Returns the workbook of the worksheet that owns this cell.
+    /// </summary>
+    const class workbook &workbook() const;
+
+    /// <summary>
+    /// Returns the base date of the parent workbook.
+    /// </summary>
+    calendar base_date() const;
+
+    /// <summary>
+    /// Returns to_check after verifying and fixing encoding, size, and illegal characters.
+    /// </summary>
+    std::string check_string(const std::string &to_check);
+
+    // comment
+
+    /// <summary>
+    /// Returns true if this cell has a comment applied.
+    /// </summary>
+    bool has_comment();
+
+    /// <summary>
+    /// Deletes the comment applied to this cell if it exists.
+    /// </summary>
+    void clear_comment();
+
+    /// <summary>
+    /// Gets the comment applied to this cell.
+    /// </summary>
+    class comment comment();
+
+    /// <summary>
+    /// Creates a new comment with the given text and optional author and
+    /// applies it to the cell.
+    /// </summary>
+    void comment(const std::string &text,
+        const std::string &author = "Microsoft Office User");
+
+    /// <summary>
+    /// Creates a new comment with the given text, formatting, and optional
+    /// author and applies it to the cell.
+    /// </summary>
+    void comment(const std::string &comment_text,
+        const class font &comment_font,
+        const std::string &author = "Microsoft Office User");
+
+    /// <summary>
+    /// Apply the comment provided as the only argument to the cell.
+    /// </summary>
+    void comment(const class comment &new_comment);
+
+    /// <summary>
+    /// Returns the width of this cell in pixels.
+    /// </summary>
+    double width() const;
+
+    /// <summary>
+    /// Returns the height of this cell in pixels.
+    /// </summary>
+    double height() const;
+
+    // operators
+
+    /// <summary>
+    /// Makes this cell interally point to rhs.
+    /// The cell data originally pointed to by this cell will be unchanged.
+    /// </summary>
+    cell &operator=(const cell &rhs);
+
+    /// <summary>
+    /// Returns true if this cell the same cell as comparand (compared by reference).
+    /// </summary>
+    bool operator==(const cell &comparand) const;
+
+    /// <summary>
+    /// Returns false if this cell the same cell as comparand (compared by reference).
+    /// </summary>
+    bool operator!=(const cell &comparand) const;
+
+private:
+    friend class style;
+    friend class worksheet;
+    friend class detail::xlsx_consumer;
+    friend class detail::xlsx_producer;
+    friend struct detail::cell_impl;
+
+    /// <summary>
+    /// Returns a non-const reference to the format of this cell.
+    /// This is for internal use only.
+    /// </summary>
+    class format modifiable_format();
+
+    /// <summary>
+    /// Delete the default zero-argument constructor.
+    /// </summary>
+    cell() = delete;
+
+    /// <summary>
+    /// Private constructor to create a cell from its implementation.
+    /// </summary>
+    cell(detail::cell_impl *d);
+
+    /// <summary>
+    /// A pointer to this cell's implementation.
+    /// </summary>
+    detail::cell_impl *d_;
+};
+
+/// <summary>
+/// Returns true if this cell is uninitialized.
+/// </summary>
+XLNT_API bool operator==(std::nullptr_t, const cell &cell);
+
+/// <summary>
+/// Returns true if this cell is uninitialized.
+/// </summary>
+XLNT_API bool operator==(const cell &cell, std::nullptr_t);
+
+/// <summary>
+/// Convenience function for writing cell to an ostream.
+/// Uses cell::to_string() internally.
+/// </summary>
+XLNT_API std::ostream &operator<<(std::ostream &stream, const xlnt::cell &cell);
+
+template <>
+bool cell::value<bool>() const;
+
+template <>
+int cell::value<int>() const;
+
+template <>
+unsigned int cell::value<unsigned int>() const;
+
+template <>
+long long int cell::value<long long int>() const;
+
+template <>
+unsigned long long cell::value<unsigned long long int>() const;
+
+template <>
+float cell::value<float>() const;
+
+template <>
+double cell::value<double>() const;
+
+template <>
+date cell::value<date>() const;
+
+template <>
+time cell::value<time>() const;
+
+template <>
+datetime cell::value<datetime>() const;
+
+template <>
+timedelta cell::value<timedelta>() const;
+
+template <>
+std::string cell::value<std::string>() const;
+
+template <>
+rich_text cell::value<rich_text>() const;
+
+} // namespace xlnt

+ 272 - 0
xlnt/include/xlnt/cell/cell_reference.hpp

@@ -0,0 +1,272 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <xlnt/xlnt_config.hpp>
+#include <xlnt/cell/index_types.hpp>
+
+#include <cstdint>
+#include <functional>
+#include <string>
+#include <tuple>
+#include <utility>
+
+namespace xlnt {
+
+class cell_reference;
+class range_reference;
+
+/// <summary>
+/// Functor for hashing a cell reference.
+/// Allows for use of std::unordered_set<cell_reference, cel_reference_hash> and similar.
+/// </summary>
+struct XLNT_API cell_reference_hash
+{
+    /// <summary>
+    /// Returns a hash representing a particular cell_reference.
+    /// </summary>
+    std::size_t operator()(const cell_reference &k) const;
+};
+
+/// <summary>
+/// An object used to refer to a cell.
+/// References have two parts, the column and the row.
+/// In Excel, the reference string A1 refers to the top-left-most cell. A cell_reference
+/// can be initialized from a string of this form or a 1-indexed ordered pair of the form
+/// column, row.
+/// </summary>
+class XLNT_API cell_reference
+{
+public:
+    /// <summary>
+    /// Splits a coordinate string like "A1" into an equivalent pair like {"A", 1}.
+    /// </summary>
+    static std::pair<std::string, row_t> split_reference(const std::string &reference_string);
+
+    /// <summary>
+    /// Splits a coordinate string like "A1" into an equivalent pair like {"A", 1}.
+    /// Reference parameters absolute_column and absolute_row will be set to true
+    /// if column part or row part are prefixed by a dollar-sign indicating they
+    /// are absolute, otherwise false.
+    /// </summary>
+    static std::pair<std::string, row_t> split_reference(
+        const std::string &reference_string, bool &absolute_column, bool &absolute_row);
+
+    // constructors
+
+    /// <summary>
+    /// Default constructor makes a reference to the top-left-most cell, "A1".
+    /// </summary>
+    cell_reference();
+
+    // TODO: should these be explicit? The implicit conversion is nice sometimes.
+
+    /// <summary>
+    /// Constructs a cell_reference from a string reprenting a cell coordinate (e.g. $B14).
+    /// </summary>
+    cell_reference(const char *reference_string);
+
+    /// <summary>
+    /// Constructs a cell_reference from a string reprenting a cell coordinate (e.g. $B14).
+    /// </summary>
+    cell_reference(const std::string &reference_string);
+
+    /// <summary>
+    /// Constructs a cell_reference from a 1-indexed column index and row index.
+    /// </summary>
+    cell_reference(column_t column, row_t row);
+
+    // absoluteness
+
+    /// <summary>
+    /// Converts a coordinate to an absolute coordinate string (e.g. B12 -> $B$12)
+    /// Defaulting to true, absolute_column and absolute_row can optionally control
+    /// whether the resulting cell_reference has an absolute column (e.g. B12 -> $B12)
+    /// and absolute row (e.g. B12 -> B$12) respectively.
+    /// </summary>
+    /// <remarks>
+    /// This is functionally equivalent to:
+    /// cell_reference copy(*this);
+    /// copy.column_absolute(absolute_column);
+    /// copy.row_absolute(absolute_row);
+    /// return copy;
+    /// </remarks>
+    cell_reference &make_absolute(bool absolute_column = true, bool absolute_row = true);
+
+    /// <summary>
+    /// Returns true if the reference refers to an absolute column, otherwise false.
+    /// </summary>
+    bool column_absolute() const;
+
+    /// <summary>
+    /// Makes this reference have an absolute column if absolute_column is true,
+    /// otherwise not absolute.
+    /// </summary>
+    void column_absolute(bool absolute_column);
+
+    /// <summary>
+    /// Returns true if the reference refers to an absolute row, otherwise false.
+    /// </summary>
+    bool row_absolute() const;
+
+    /// <summary>
+    /// Makes this reference have an absolute row if absolute_row is true,
+    /// otherwise not absolute.
+    /// </summary>
+    void row_absolute(bool absolute_row);
+
+    // getters/setters
+
+    /// <summary>
+    /// Returns a string that identifies the column of this reference
+    /// (e.g. second column from left is "B")
+    /// </summary>
+    column_t column() const;
+
+    /// <summary>
+    /// Sets the column of this reference from a string that identifies a particular column.
+    /// </summary>
+    void column(const std::string &column_string);
+
+    /// <summary>
+    /// Returns a 1-indexed numeric index of the column of this reference.
+    /// </summary>
+    column_t::index_t column_index() const;
+
+    /// <summary>
+    /// Sets the column of this reference from a 1-indexed number that identifies a particular column.
+    /// </summary>
+    void column_index(column_t column);
+
+    /// <summary>
+    /// Returns a 1-indexed numeric index of the row of this reference.
+    /// </summary>
+    row_t row() const;
+
+    /// <summary>
+    /// Sets the row of this reference from a 1-indexed number that identifies a particular row.
+    /// </summary>
+    void row(row_t row);
+
+    /// <summary>
+    /// Returns a cell_reference offset from this cell_reference by
+    /// the number of columns and rows specified by the parameters.
+    /// A negative value for column_offset or row_offset results
+    /// in a reference above or left of this cell_reference, respectively.
+    /// </summary>
+    cell_reference make_offset(int column_offset, int row_offset) const;
+
+    /// <summary>
+    /// Returns a string like "A1" for cell_reference(1, 1).
+    /// </summary>
+    std::string to_string() const;
+
+    /// <summary>
+    /// Returns a 1x1 range_reference containing only this cell_reference.
+    /// </summary>
+    range_reference to_range() const;
+
+    // operators
+
+    /// <summary>
+    /// I've always wanted to overload the comma operator.
+    /// cell_reference("A", 1), cell_reference("B", 1) will return
+    /// range_reference(cell_reference("A", 1), cell_reference("B", 1))
+    /// </summary>
+    range_reference operator,(const cell_reference &other) const;
+
+    /// <summary>
+    /// Returns true if this reference is identical to comparand including
+    /// in absoluteness of column and row.
+    /// </summary>
+    bool operator==(const cell_reference &comparand) const;
+
+    /// <summary>
+    /// Constructs a cell_reference from reference_string and return the result
+    /// of their comparison.
+    /// </summary>
+    bool operator==(const std::string &reference_string) const;
+
+    /// <summary>
+    /// Constructs a cell_reference from reference_string and return the result
+    /// of their comparison.
+    /// </summary>
+    bool operator==(const char *reference_string) const;
+
+    /// <summary>
+    /// Returns true if this reference is not identical to comparand including
+    /// in absoluteness of column and row.
+    /// </summary>
+    bool operator!=(const cell_reference &comparand) const;
+
+    /// <summary>
+    /// Constructs a cell_reference from reference_string and return the result
+    /// of their comparison.
+    /// </summary>
+    bool operator!=(const std::string &reference_string) const;
+
+    /// <summary>
+    /// Constructs a cell_reference from reference_string and return the result
+    /// of their comparison.
+    /// </summary>
+    bool operator!=(const char *reference_string) const;
+
+private:
+    /// <summary>
+    /// Index of the column. Important: this is one-indexed to conform
+    /// with Excel. Column "A", the first column, would have an index of 1.
+    /// </summary>
+    column_t column_;
+
+    /// <summary>
+    /// Index of the column. Important: this is one-indexed to conform
+    /// with Excel. Column "A", the first column, would have an index of 1.
+    /// </summary>
+    row_t row_;
+
+    /// <summary>
+    /// True if the reference's row is absolute. This looks like "A$1" in Excel.
+    /// </summary>
+    bool absolute_row_;
+
+    /// <summary>
+    /// True if the reference's column is absolute. This looks like "$A1" in Excel.
+    /// </summary>
+    bool absolute_column_;
+};
+
+} // namespace xlnt
+
+namespace std {
+template <>
+struct hash<xlnt::cell_reference>
+{
+    size_t operator()(const xlnt::cell_reference &x) const
+    {
+        static_assert(std::is_same<decltype(x.row()), std::uint32_t>::value, "this hash function expects both row and column to be 32-bit numbers");
+        static_assert(std::is_same<decltype(x.column_index()), std::uint32_t>::value, "this hash function expects both row and column to be 32-bit numbers");
+        return hash<std::uint64_t>{}(x.row() | static_cast<std::uint64_t>(x.column_index()) << 32);
+    }
+};
+} // namespace std

+ 58 - 0
xlnt/include/xlnt/cell/cell_type.hpp

@@ -0,0 +1,58 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <xlnt/xlnt_config.hpp>
+
+/// <summary>
+/// Enumerates the possible types a cell can be determined by it's current value.
+/// </summary>
+
+namespace xlnt {
+
+/// <summary>
+/// Enumerates the possible types a cell can be determined by it's current value.
+/// </summary>
+enum class XLNT_API cell_type
+{
+    /// no value
+    empty,
+    /// value is TRUE or FALSE
+    boolean,
+    /// value is an ISO 8601 formatted date
+    date,
+    /// value is a known error code such as \#VALUE!
+    error,
+    /// value is a string stored in the cell
+    inline_string,
+    /// value is a number
+    number,
+    /// value is a string shared with other cells to save space
+    shared_string,
+    /// value is the string result of a formula
+    formula_string
+};
+
+} // namespace xlnt

+ 166 - 0
xlnt/include/xlnt/cell/comment.hpp

@@ -0,0 +1,166 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <string>
+
+#include <xlnt/xlnt_config.hpp>
+#include <xlnt/cell/rich_text.hpp>
+
+namespace xlnt {
+
+/// <summary>
+/// A comment can be applied to a cell to provide extra information about its contents.
+/// </summary>
+class XLNT_API comment
+{
+public:
+    /// <summary>
+    /// Constructs a new blank comment.
+    /// </summary>
+    comment();
+
+    /// <summary>
+    /// Constructs a new comment with the given text and author.
+    /// </summary>
+    comment(const rich_text &text, const std::string &author);
+
+    /// <summary>
+    /// Constructs a new comment with the given unformatted text and author.
+    /// </summary>
+    comment(const std::string &text, const std::string &author);
+
+    /// <summary>
+    /// Returns the text that will be displayed for this comment.
+    /// </summary>
+    rich_text text() const;
+
+    /// <summary>
+    /// Returns the plain text that will be displayed for this comment without formatting information.
+    /// </summary>
+    std::string plain_text() const;
+
+    /// <summary>
+    /// Returns the author of this comment.
+    /// </summary>
+    std::string author() const;
+
+    /// <summary>
+    /// Makes this comment only visible when the associated cell is hovered.
+    /// </summary>
+    void hide();
+
+    /// <summary>
+    /// Makes this comment always visible.
+    /// </summary>
+    void show();
+
+    /// <summary>
+    /// Returns true if this comment is not hidden.
+    /// </summary>
+    bool visible() const;
+
+    /// <summary>
+    /// Sets the absolute position of this cell to the given coordinates.
+    /// </summary>
+    void position(int left, int top);
+
+    /// <summary>
+    /// Returns the distance from the left side of the sheet to the left side of the comment.
+    /// </summary>
+    int left() const;
+
+    /// <summary>
+    /// Returns the distance from the top of the sheet to the top of the comment.
+    /// </summary>
+    int top() const;
+
+    /// <summary>
+    /// Sets the size of the comment.
+    /// </summary>
+    void size(int width, int height);
+
+    /// <summary>
+    /// Returns the width of this comment.
+    /// </summary>
+    int width() const;
+
+    /// <summary>
+    /// Returns the height of this comment.
+    /// </summary>
+    int height() const;
+
+    /// <summary>
+    /// Return true if this comment is equivalent to other.
+    /// </summary>
+    bool operator==(const comment &other) const;
+
+    /// <summary>
+    /// Returns true if this comment is not equivalent to other.
+    /// </summary>
+    bool operator!=(const comment &other) const;
+
+private:
+    /// <summary>
+    /// The formatted textual content in this cell displayed directly after the author.
+    /// </summary>
+    rich_text text_;
+
+    /// <summary>
+    /// The name of the person that created this comment.
+    /// </summary>
+    std::string author_;
+
+    /// <summary>
+    /// True if this comment is not hidden.
+    /// </summary>
+    bool visible_ = false;
+
+    /// <summary>
+    /// The fill color
+    /// </summary>
+    std::string fill_;
+
+    /// <summary>
+    /// Distance from the left side of the sheet.
+    /// </summary>
+    int left_ = 0;
+
+    /// <summary>
+    /// Distance from the top of the sheet.
+    /// </summary>
+    int top_ = 0;
+
+    /// <summary>
+    /// Width of the comment box.
+    /// </summary>
+    int width_ = 200;
+
+    /// <summary>
+    /// Height of the comment box.
+    /// </summary>
+    int height_ = 100;
+};
+
+} // namespace xlnt

+ 70 - 0
xlnt/include/xlnt/cell/hyperlink.hpp

@@ -0,0 +1,70 @@
+// Copyright (c) 2018 Thomas Fussell
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// 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, ARISING 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 <xlnt/xlnt_config.hpp>
+#include <string>
+
+namespace xlnt {
+
+namespace detail {
+struct hyperlink_impl;
+}
+
+class cell;
+class range;
+class relationship;
+
+/// <summary>
+/// Describes a hyperlink pointing from a cell to another cell or a URL.
+/// </summary>
+class XLNT_API hyperlink
+{
+public:
+    bool external() const;
+    class relationship relationship() const;
+    // external target
+    std::string url() const;
+    // internal target
+    std::string target_range() const;
+
+    bool has_display() const;
+    void display(const std::string &value);
+    const std::string &display() const;
+
+    bool has_tooltip() const;
+    void tooltip(const std::string &value);
+    const std::string &tooltip() const;
+
+    bool has_location() const;
+    void location(const std::string &value);
+    const std::string &location() const;
+
+private:
+    friend class cell;
+    hyperlink(detail::hyperlink_impl *d);
+    detail::hyperlink_impl *d_;
+};
+
+} // namespace xlnt

+ 309 - 0
xlnt/include/xlnt/cell/index_types.hpp

@@ -0,0 +1,309 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <algorithm>
+#include <cstdint>
+#include <string>
+
+#include <xlnt/xlnt_config.hpp>
+
+// We might want to change these types for various optimizations in the future
+// so use typedefs.
+
+namespace xlnt {
+
+/// <summary>
+/// All rows should be referred to by an instance of this type.
+/// </summary>
+using row_t = std::uint32_t;
+
+/// <summary>
+/// Columns can be referred to as a string A,B,...Z,AA,AB,..,ZZ,AAA,...,ZZZ
+/// or as a 1-indexed index. This class encapsulates both of these forms of
+/// column referencing and allows for conversions between them.
+/// </summary>
+class XLNT_API column_t
+{
+public:
+    /// <summary>
+    /// Alias declaration for the internal numeric type of this column.
+    /// </summary>
+    using index_t = std::uint32_t;
+
+    /// <summary>
+    /// Convert a column letter into a column number (e.g. B -> 2)
+    /// </summary>
+    /// <remarks>
+    /// Excel only supports 1 - 3 letter column names from A->ZZZ, so we
+    /// restrict our column names to 1 - 3 characters, each in the range A - Z.
+    /// Strings outside this range and malformed strings will throw column_string_index_exception.
+    /// </remarks>
+    static index_t column_index_from_string(const std::string &column_string);
+
+    /// <summary>
+    /// Convert a column number into a column letter (3 -> 'C')
+    /// </summary>
+    /// <remarks>
+    /// Right shift the column, column_index, by 26 to find column letters in reverse
+    /// order. These indices are 1-based, and can be converted to ASCII
+    /// ordinals by adding 64.
+    /// </remarks>
+    static std::string column_string_from_index(index_t column_index);
+
+    /// <summary>
+    /// Default constructor. The column points to the "A" column.
+    /// </summary>
+    column_t();
+
+    /// <summary>
+    /// Constructs a column from a number.
+    /// </summary>
+    column_t(index_t column_index);
+
+    /// <summary>
+    /// Constructs a column from a string.
+    /// </summary>
+    column_t(const std::string &column_string);
+
+    /// <summary>
+    /// Constructs a column from a string.
+    /// </summary>
+    column_t(const char *column_string);
+
+    /// <summary>
+    /// Returns a string representation of this column index.
+    /// </summary>
+    std::string column_string() const;
+
+    /// <summary>
+    /// Sets this column to be equal to rhs and return reference to self.
+    /// </summary>
+    column_t &operator=(const std::string &rhs);
+
+    /// <summary>
+    /// Sets this column to be equal to rhs and return reference to self.
+    /// </summary>
+    column_t &operator=(const char *rhs);
+
+    /// <summary>
+    /// Returns true if this column refers to the same column as other.
+    /// </summary>
+    bool operator==(const column_t &other) const;
+
+    /// <summary>
+    /// Returns true if this column doesn't refer to the same column as other.
+    /// </summary>
+    bool operator!=(const column_t &other) const;
+
+    /// <summary>
+    /// Returns true if this column refers to the same column as other.
+    /// </summary>
+    bool operator==(int other) const;
+
+    /// <summary>
+    /// Returns true if this column refers to the same column as other.
+    /// </summary>
+    bool operator==(index_t other) const;
+
+    /// <summary>
+    /// Returns true if this column refers to the same column as other.
+    /// </summary>
+    bool operator==(const std::string &other) const;
+
+    /// <summary>
+    /// Returns true if this column refers to the same column as other.
+    /// </summary>
+    bool operator==(const char *other) const;
+
+    /// <summary>
+    /// Returns true if this column doesn't refer to the same column as other.
+    /// </summary>
+    bool operator!=(int other) const;
+
+    /// <summary>
+    /// Returns true if this column doesn't refer to the same column as other.
+    /// </summary>
+    bool operator!=(index_t other) const;
+
+    /// <summary>
+    /// Returns true if this column doesn't refer to the same column as other.
+    /// </summary>
+    bool operator!=(const std::string &other) const;
+
+    /// <summary>
+    /// Returns true if this column doesn't refer to the same column as other.
+    /// </summary>
+    bool operator!=(const char *other) const;
+
+    /// <summary>
+    /// Returns true if other is to the right of this column.
+    /// </summary>
+    bool operator>(const column_t &other) const;
+
+    /// <summary>
+    /// Returns true if other is to the right of or equal to this column.
+    /// </summary>
+    bool operator>=(const column_t &other) const;
+
+    /// <summary>
+    /// Returns true if other is to the left of this column.
+    /// </summary>
+    bool operator<(const column_t &other) const;
+
+    /// <summary>
+    /// Returns true if other is to the left of or equal to this column.
+    /// </summary>
+    bool operator<=(const column_t &other) const;
+
+    /// <summary>
+    /// Returns true if other is to the right of this column.
+    /// </summary>
+    bool operator>(const column_t::index_t &other) const;
+
+    /// <summary>
+    /// Returns true if other is to the right of or equal to this column.
+    /// </summary>
+    bool operator>=(const column_t::index_t &other) const;
+
+    /// <summary>
+    /// Returns true if other is to the left of this column.
+    /// </summary>
+    bool operator<(const column_t::index_t &other) const;
+
+    /// <summary>
+    /// Returns true if other is to the left of or equal to this column.
+    /// </summary>
+    bool operator<=(const column_t::index_t &other) const;
+
+    /// <summary>
+    /// Pre-increments this column, making it point to the column one to the right and returning a reference to it.
+    /// </summary>
+    column_t &operator++();
+
+    /// <summary>
+    /// Pre-deccrements this column, making it point to the column one to the left and returning a reference to it.
+    /// </summary>
+    column_t &operator--();
+
+    /// <summary>
+    /// Post-increments this column, making it point to the column one to the right and returning the old column.
+    /// </summary>
+    column_t operator++(int);
+
+    /// <summary>
+    /// Post-decrements this column, making it point to the column one to the left and returning the old column.
+    /// </summary>
+    column_t operator--(int);
+
+    /// <summary>
+    /// Returns the result of adding rhs to this column.
+    /// </summary>
+    friend XLNT_API column_t operator+(column_t lhs, const column_t &rhs);
+
+    /// <summary>
+    /// Returns the result of subtracing lhs by rhs column.
+    /// </summary>
+    friend XLNT_API column_t operator-(column_t lhs, const column_t &rhs);
+
+    /// <summary>
+    /// Adds rhs to this column and returns a reference to this column.
+    /// </summary>
+    column_t &operator+=(const column_t &rhs);
+
+    /// <summary>
+    /// Subtracts rhs from this column and returns a reference to this column.
+    /// </summary>
+    column_t &operator-=(const column_t &rhs);
+
+    /// <summary>
+    /// Returns true if other is to the right of this column.
+    /// </summary>
+    friend XLNT_API bool operator>(const column_t::index_t &left, const column_t &right);
+
+    /// <summary>
+    /// Returns true if other is to the right of or equal to this column.
+    /// </summary>
+    friend XLNT_API bool operator>=(const column_t::index_t &left, const column_t &right);
+
+    /// <summary>
+    /// Returns true if other is to the left of this column.
+    /// </summary>
+    friend XLNT_API bool operator<(const column_t::index_t &left, const column_t &right);
+
+    /// <summary>
+    /// Returns true if other is to the left of or equal to this column.
+    /// </summary>
+    friend XLNT_API bool operator<=(const column_t::index_t &left, const column_t &right);
+
+    /// <summary>
+    /// Swaps the columns that left and right refer to.
+    /// </summary>
+    friend XLNT_API void swap(column_t &left, column_t &right);
+
+    /// <summary>
+    /// Internal numeric value of this column index.
+    /// </summary>
+    index_t index;
+};
+
+enum class row_or_col_t : int
+{
+    row,
+    column
+};
+
+/// <summary>
+/// Functor for hashing a column.
+/// Allows for use of std::unordered_set<column_t, column_hash> and similar.
+/// </summary>
+struct XLNT_API column_hash
+{
+    /// <summary>
+    /// Returns the result of hashing column k.
+    /// </summary>
+    std::size_t operator()(const column_t &k) const;
+};
+
+} // namespace xlnt
+
+namespace std {
+
+/// <summary>
+/// Template specialization to allow xlnt::column_t to be used as a key in a std container.
+/// </summary>
+template <>
+struct hash<xlnt::column_t>
+{
+    /// <summary>
+    /// Returns the result of hashing column k.
+    /// </summary>
+    size_t operator()(const xlnt::column_t &k) const
+    {
+        static xlnt::column_hash hasher;
+        return hasher(k);
+    }
+};
+
+} // namespace std

+ 46 - 0
xlnt/include/xlnt/cell/phonetic_run.hpp

@@ -0,0 +1,46 @@
+// Copyright (c) 2016-2020
+//
+// 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, ARISING 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 <string>
+
+#include <xlnt/xlnt_config.hpp>
+
+namespace xlnt {
+
+/// <summary>
+/// Encapsulates a run of text that
+/// </summary>
+struct XLNT_API phonetic_run
+{
+    std::string text;
+    uint32_t start;
+    uint32_t end;
+    bool preserve_space;
+
+    bool operator==(const phonetic_run &other) const;
+    bool operator!=(const phonetic_run &other) const;
+};
+
+} // namespace xlnt

+ 179 - 0
xlnt/include/xlnt/cell/rich_text.hpp

@@ -0,0 +1,179 @@
+// Copyright (c) 2016-2021 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, ARISING 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 <string>
+#include <vector>
+
+#include <xlnt/xlnt_config.hpp>
+#include <xlnt/cell/phonetic_run.hpp>
+#include <xlnt/cell/rich_text_run.hpp>
+#include <xlnt/worksheet/phonetic_pr.hpp>
+
+namespace xlnt {
+
+/// <summary>
+/// Encapsulates zero or more formatted text runs where a text run
+/// is a string of text with the same defined formatting.
+/// </summary>
+class XLNT_API rich_text
+{
+public:
+    /// <summary>
+    /// Constructs an empty rich text object with no font and empty text.
+    /// </summary>
+    rich_text() = default;
+
+    /// <summary>
+    /// Constructs a rich text object with the given text and no font.
+    /// </summary>
+    rich_text(const std::string &plain_text);
+
+    /// <summary>
+    /// Constructs a rich text object from other
+    /// </summary>
+    rich_text(const rich_text &other);
+
+    /// <summary>
+    /// Constructs a rich text object with the given text and font.
+    /// </summary>
+    rich_text(const std::string &plain_text, const class font &text_font);
+
+    /// <summary>
+    /// Copy constructor.
+    /// </summary>
+    rich_text(const rich_text_run &single_run);
+
+    /// <summary>
+    /// Removes all text runs from this text.
+    /// </summary>
+    void clear();
+
+    /// <summary>
+    /// Clears any runs in this text and adds a single run with default formatting and
+    /// the given string as its textual content.
+    /// </summary>
+    void plain_text(const std::string &s, bool preserve_space);
+
+    /// <summary>
+    /// Combines the textual content of each text run in order and returns the result.
+    /// </summary>
+    std::string plain_text() const;
+
+    /// <summary>
+    /// Returns a copy of the individual runs that comprise this text.
+    /// </summary>
+    std::vector<rich_text_run> runs() const;
+
+    /// <summary>
+    /// Sets the runs of this text all at once.
+    /// </summary>
+    void runs(const std::vector<rich_text_run> &new_runs);
+
+    /// <summary>
+    /// Adds a new run to the end of the set of runs.
+    /// </summary>
+    void add_run(const rich_text_run &t);
+
+    /// <summary>
+    /// Returns a copy of the individual runs that comprise this text.
+    /// </summary>
+    std::vector<phonetic_run> phonetic_runs() const;
+
+    /// <summary>
+    /// Sets the runs of this text all at once.
+    /// </summary>
+    void phonetic_runs(const std::vector<phonetic_run> &new_phonetic_runs);
+
+    /// <summary>
+    /// Adds a new run to the end of the set of runs.
+    /// </summary>
+    void add_phonetic_run(const phonetic_run &t);
+
+    /// <summary>
+    /// Returns true if this text has phonetic properties
+    /// </summary>
+    bool has_phonetic_properties() const;
+
+    /// <summary>
+    /// Returns the phonetic properties of this text.
+    /// </summary>
+    const phonetic_pr &phonetic_properties() const;
+
+    /// <summary>
+    /// Sets the phonetic properties of this text to phonetic_props
+    /// </summary>
+    void phonetic_properties(const phonetic_pr &phonetic_props);
+
+    /// <summary>
+    /// Copies rich text object from other
+    /// </summary>
+    rich_text &operator=(const rich_text &rhs);
+
+    /// <summary>
+    /// Returns true if the runs that make up this text are identical to those in rhs.
+    /// </summary>
+    bool operator==(const rich_text &rhs) const;
+
+    /// <summary>
+    /// Returns true if the runs that make up this text are identical to those in rhs.
+    /// </summary>
+    bool operator!=(const rich_text &rhs) const;
+
+    /// <summary>
+    /// Returns true if this text has a single unformatted run with text equal to rhs.
+    /// </summary>
+    bool operator==(const std::string &rhs) const;
+
+    /// <summary>
+    /// Returns true if this text has a single unformatted run with text equal to rhs.
+    /// </summary>
+    bool operator!=(const std::string &rhs) const;
+
+private:
+    /// <summary>
+    /// The runs that make up this rich text.
+    /// </summary>
+    std::vector<rich_text_run> runs_;
+    std::vector<phonetic_run> phonetic_runs_;
+    optional<phonetic_pr> phonetic_properties_;
+};
+
+class XLNT_API rich_text_hash
+{
+public:
+    std::size_t operator()(const rich_text &k) const
+    {
+        std::size_t res = 0;
+
+        for (auto r : k.runs())
+        {
+            res ^= std::hash<std::string>()(r.first);
+        }
+
+        return res;
+    }
+};
+
+} // namespace xlnt

+ 48 - 0
xlnt/include/xlnt/cell/rich_text_run.hpp

@@ -0,0 +1,48 @@
+// Copyright (c) 2016-2021 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, ARISING 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 <string>
+
+#include <xlnt/xlnt_config.hpp>
+#include <xlnt/styles/font.hpp>
+#include <xlnt/utils/optional.hpp>
+
+namespace xlnt {
+
+/// <summary>
+/// Typedef a rich_text_run as a pair of string and optional font.
+/// </summary>
+struct XLNT_API rich_text_run
+{
+    std::string first;
+    optional<font> second;
+    bool preserve_space;
+
+    bool operator==(const rich_text_run &other) const;
+
+    bool operator!=(const rich_text_run &other) const;
+};
+
+} // namespace xlnt

+ 59 - 0
xlnt/include/xlnt/drawing/spreadsheet_drawing.hpp

@@ -0,0 +1,59 @@
+// Copyright (c) 2018
+//
+// 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, ARISING 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 <xlnt/xlnt_config.hpp>
+#include <string>
+#include <vector>
+
+namespace xml {
+class parser;
+class serializer;
+} // namespace xml
+
+namespace xlnt {
+
+class worksheet;
+
+namespace drawing {
+
+/// <summary>
+/// The spreadsheet_drawing class encapsulates the information
+/// captured from objects within the spreadsheetDrawing schema.
+/// </summary>
+class XLNT_API spreadsheet_drawing
+{
+public:
+    spreadsheet_drawing(xml::parser &parser);
+    void serialize(xml::serializer &serializer);
+
+    std::vector<std::string> get_embed_ids();
+
+private:
+    std::string serialized_value_;
+    std::vector<std::string> embed_ids_;
+};
+
+} // namespace drawing
+} // namespace xlnt

+ 79 - 0
xlnt/include/xlnt/packaging/ext_list.hpp

@@ -0,0 +1,79 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <xlnt/xlnt_config.hpp>
+#include <xlnt/packaging/uri.hpp>
+
+#include <string>
+#include <vector>
+
+namespace xml {
+class parser;
+class serializer;
+} // namespace xml
+
+namespace xlnt {
+
+/// <summary>
+/// A list of xml extensions that may or may not be understood by the parser
+/// preservation is required for round-tripping even if extension is not understood
+/// [serialised: extLst]
+/// </summary>
+class XLNT_API ext_list
+{
+public:
+    struct ext
+    {
+        ext(xml::parser &parser, const std::string &ns);
+        ext(const uri &ID, const std::string &serialised);
+        void serialise(xml::serializer &serialiser, const std::string &ns);
+
+        uri extension_ID_;
+        std::string serialised_value_;
+    };
+    ext_list() = default; // default ctor required by xlnt::optional
+    explicit ext_list(xml::parser &parser, const std::string &ns);
+    void serialize(xml::serializer &serialiser, const std::string &ns);
+
+    void add_extension(const uri &ID, const std::string &element);
+
+    bool has_extension(const uri &extension_uri) const;
+
+    const ext &extension(const uri &extension_uri) const;
+
+    const std::vector<ext> &extensions() const;
+
+    bool operator==(const ext_list &rhs) const;
+
+private:
+    std::vector<ext> extensions_;
+};
+
+inline bool operator==(const ext_list::ext &lhs, const ext_list::ext &rhs)
+{
+    return lhs.extension_ID_ == rhs.extension_ID_
+        && lhs.serialised_value_ == rhs.serialised_value_;
+}
+} // namespace xlnt

+ 199 - 0
xlnt/include/xlnt/packaging/manifest.hpp

@@ -0,0 +1,199 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <string>
+#include <unordered_map>
+
+#include <xlnt/xlnt_config.hpp>
+#include <xlnt/packaging/relationship.hpp>
+#include <xlnt/utils/path.hpp>
+
+namespace xlnt {
+
+/// <summary>
+/// The manifest keeps track of all files in the OOXML package and
+/// their type and relationships.
+/// </summary>
+class XLNT_API manifest
+{
+public:
+    /// <summary>
+    /// Unregisters all default and override type and all relationships and known parts.
+    /// </summary>
+    void clear();
+
+    /// <summary>
+    /// Returns the path to all internal package parts registered as a source
+    /// or target of a relationship.
+    /// </summary>
+    std::vector<path> parts() const;
+
+    // Relationships
+
+    /// <summary>
+    /// Returns true if the manifest contains a relationship with the given type with part as the source.
+    /// </summary>
+    bool has_relationship(const path &source, relationship_type type) const;
+
+    /// <summary>
+    /// Returns true if the manifest contains a relationship with the given type with part as the source.
+    /// </summary>
+    bool has_relationship(const path &source, const std::string &rel_id) const;
+
+    /// <summary>
+    /// Returns the relationship with "source" as the source and with a type of "type".
+    /// Throws a key_not_found exception if no such relationship is found.
+    /// </summary>
+    class relationship relationship(const path &source, relationship_type type) const;
+
+    /// <summary>
+    /// Returns the relationship with "source" as the source and with an ID of "rel_id".
+    /// Throws a key_not_found exception if no such relationship is found.
+    /// </summary>
+    class relationship relationship(const path &source, const std::string &rel_id) const;
+
+    /// <summary>
+    /// Returns all relationship with "source" as the source.
+    /// </summary>
+    std::vector<xlnt::relationship> relationships(const path &source) const;
+
+    /// <summary>
+    /// Returns all relationships with "source" as the source and with a type of "type".
+    /// </summary>
+    std::vector<xlnt::relationship> relationships(const path &source, relationship_type type) const;
+
+    /// <summary>
+    /// Returns the canonical path of the chain of relationships by traversing through rels
+    /// and forming the absolute combined path.
+    /// </summary>
+    path canonicalize(const std::vector<xlnt::relationship> &rels) const;
+
+    /// <summary>
+    /// Registers a new relationship by specifying all of the relationship properties explicitly.
+    /// </summary>
+    std::string register_relationship(const uri &source, relationship_type type, const uri &target, target_mode mode);
+
+    /// <summary>
+    /// Registers a new relationship already constructed elsewhere.
+    /// </summary>
+    std::string register_relationship(const class relationship &rel);
+
+    /// <summary>
+    /// Delete the relationship with the given id from source part. Returns a mapping
+    /// of relationship IDs since IDs are shifted down. For example, if there are three
+    /// relationships for part a.xml like [rId1, rId2, rId3] and rId2 is deleted, the
+    /// resulting map would look like [rId3->rId2].
+    /// </summary>
+    std::unordered_map<std::string, std::string> unregister_relationship(const uri &source, const std::string &rel_id);
+
+    // Content Types
+
+    /// <summary>
+    /// Given the path to a part, returns the content type of the part as a string.
+    /// </summary>
+    std::string content_type(const path &part) const;
+
+    // Default Content Types
+
+    /// <summary>
+    /// Returns true if a default content type for the extension has been registered.
+    /// </summary>
+    bool has_default_type(const std::string &extension) const;
+
+    /// <summary>
+    /// Returns a vector of all extensions with registered default content types.
+    /// </summary>
+    std::vector<std::string> extensions_with_default_types() const;
+
+    /// <summary>
+    /// Returns the registered default content type for parts of the given extension.
+    /// </summary>
+    std::string default_type(const std::string &extension) const;
+
+    /// <summary>
+    /// Associates the given extension with the given content type.
+    /// </summary>
+    void register_default_type(const std::string &extension, const std::string &type);
+
+    /// <summary>
+    /// Unregisters the default content type for the given extension.
+    /// </summary>
+    void unregister_default_type(const std::string &extension);
+
+    // Override Content Types
+
+    /// <summary>
+    /// Returns true if a content type overriding the default content type has been registered
+    /// for the given part.
+    /// </summary>
+    bool has_override_type(const path &part) const;
+
+    /// <summary>
+    /// Returns the override content type registered for the given part.
+    /// Throws key_not_found exception if no override type was found.
+    /// </summary>
+    std::string override_type(const path &part) const;
+
+    /// <summary>
+    /// Returns the path of every part in this manifest with an overriden content type.
+    /// </summary>
+    std::vector<path> parts_with_overriden_types() const;
+
+    /// <summary>
+    /// Overrides any default type registered for the part's extension with the given content type.
+    /// </summary>
+    void register_override_type(const path &part, const std::string &type);
+
+    /// <summary>
+    /// Unregisters the overriding content type of the given part.
+    /// </summary>
+    void unregister_override_type(const path &part);
+
+    bool operator==(const manifest &other) const;
+
+private:
+    /// <summary>
+    /// Returns the lowest rId for the given part that hasn't already been registered.
+    /// </summary>
+    std::string next_relationship_id(const path &part) const;
+
+    /// <summary>
+    /// The map of extensions to default content types.
+    /// </summary>
+    std::unordered_map<std::string, std::string> default_content_types_;
+
+    /// <summary>
+    /// The map of package parts to overriding content types.
+    /// </summary>
+    std::unordered_map<path, std::string> override_content_types_;
+
+    /// <summary>
+    /// The map of package parts to their registered relationships.
+    /// </summary>
+    std::unordered_map<path, std::unordered_map<std::string, xlnt::relationship>> relationships_;
+};
+
+} // namespace xlnt

+ 178 - 0
xlnt/include/xlnt/packaging/relationship.hpp

@@ -0,0 +1,178 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <string>
+
+#include <xlnt/xlnt_config.hpp>
+#include <xlnt/packaging/uri.hpp>
+#include <xlnt/utils/path.hpp>
+
+namespace xlnt {
+
+/// <summary>
+/// Specifies whether the target of a relationship is inside or outside the Package.
+/// </summary>
+enum class XLNT_API target_mode
+{
+    /// <summary>
+    /// The relationship references a resource that is external to the package.
+    /// </summary>
+    internal,
+    /// <summary>
+    /// The relationship references a part that is inside the package.
+    /// </summary>
+    external
+};
+
+/// <summary>
+/// All package relationships must be one of these defined types.
+/// </summary>
+enum class XLNT_API relationship_type
+{
+    unknown,
+
+    // Package parts
+    core_properties,
+    extended_properties,
+    custom_properties,
+    office_document,
+    thumbnail,
+    printer_settings,
+
+    // SpreadsheetML parts
+    calculation_chain,
+    chartsheet,
+    comments,
+    connections,
+    custom_property,
+    custom_xml_mappings,
+    dialogsheet,
+    drawings,
+    external_workbook_references,
+    pivot_table,
+    pivot_table_cache_definition,
+    pivot_table_cache_records,
+    query_table,
+    shared_string_table,
+    shared_workbook_revision_headers,
+    shared_workbook,
+    theme,
+    revision_log,
+    shared_workbook_user_data,
+    single_cell_table_definitions,
+    stylesheet,
+    table_definition,
+    vml_drawing,
+    volatile_dependencies,
+    worksheet,
+    vbaproject,
+
+    // Worksheet parts
+    hyperlink,
+    image
+};
+
+/// <summary>
+/// Represents an association between a source Package or part, and a target object which can be a part or external
+/// resource.
+/// </summary>
+class XLNT_API relationship
+{
+public:
+    /// <summary>
+    /// Constructs a new empty relationship.
+    /// </summary>
+    relationship();
+
+    /// <summary>
+    /// Constructs a new relationship by specifying all of its properties.
+    /// </summary>
+    relationship(const std::string &id, relationship_type t, const uri &source,
+        const uri &target, xlnt::target_mode mode);
+
+    /// <summary>
+    /// Returns a string of the form rId# that identifies the relationship.
+    /// </summary>
+    const std::string &id() const;
+
+    /// <summary>
+    /// Returns the type of this relationship.
+    /// </summary>
+    relationship_type type() const;
+
+    /// <summary>
+    /// Returns whether the target of the relationship is internal or external to the package.
+    /// </summary>
+    xlnt::target_mode target_mode() const;
+
+    /// <summary>
+    /// Returns the URI of the package part this relationship points to.
+    /// </summary>
+    const uri &source() const;
+
+    /// <summary>
+    /// Returns the URI of the package part this relationship points to.
+    /// </summary>
+    const uri &target() const;
+
+    /// <summary>
+    /// Returns true if and only if rhs is equal to this relationship.
+    /// </summary>
+    bool operator==(const relationship &rhs) const;
+
+    /// <summary>
+    /// Returns true if and only if rhs is not equal to this relationship.
+    /// </summary>
+    bool operator!=(const relationship &rhs) const;
+
+private:
+    /// <summary>
+    /// The id of this relationship in the format "rId#"
+    /// </summary>
+    std::string id_;
+
+    /// <summary>
+    /// The type of this relationship.
+    /// </summary>
+    relationship_type type_;
+
+    /// <summary>
+    /// The URI of the source of this relationship.
+    /// </summary>
+    uri source_;
+
+    /// <summary>
+    /// The URI of the target of this relationshp.
+    /// </summary>
+    uri target_;
+
+    /// <summary>
+    /// Whether the target of this relationship is internal or external.
+    /// </summary>
+    xlnt::target_mode mode_;
+};
+
+} // namespace xlnt

+ 239 - 0
xlnt/include/xlnt/packaging/uri.hpp

@@ -0,0 +1,239 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <string>
+
+#include <xlnt/xlnt_config.hpp>
+#include <xlnt/utils/path.hpp>
+
+namespace xlnt {
+
+/// <summary>
+/// Encapsulates a uniform resource identifier (URI) as described
+/// by RFC 3986.
+/// </summary>
+class XLNT_API uri
+{
+public:
+    /// <summary>
+    /// Constructs an empty URI.
+    /// </summary>
+    uri();
+
+    /// <summary>
+    /// Constructs a URI by combining base with relative.
+    /// </summary>
+    uri(const uri &base, const uri &relative);
+
+    /// <summary>
+    /// Constructs a URI by combining base with relative path.
+    /// </summary>
+    uri(const uri &base, const path &relative);
+
+    /// <summary>
+    /// Constructs a URI by parsing the given uri_string.
+    /// </summary>
+    uri(const std::string &uri_string);
+
+    /// <summary>
+    /// Returns true if this URI is relative.
+    /// </summary>
+    bool is_relative() const;
+
+    /// <summary>
+    /// Returns true if this URI is not relative (i.e. absolute).
+    /// </summary>
+    bool is_absolute() const;
+
+    /// <summary>
+    /// Returns the scheme of this URI.
+    /// E.g. the scheme of http://user:pass@example.com is "http"
+    /// </summary>
+    std::string scheme() const;
+
+    /// <summary>
+    /// Returns the authority of this URI.
+    /// E.g. the authority of http://user:pass@example.com:80/document is "user:pass@example.com:80"
+    /// </summary>
+    std::string authority() const;
+
+    /// <summary>
+    /// Returns true if an authentication section is specified for this URI.
+    /// </summary>
+    bool has_authentication() const;
+
+    /// <summary>
+    /// Returns the authentication of this URI.
+    /// E.g. the authentication of http://user:pass@example.com is "user:pass"
+    /// </summary>
+    std::string authentication() const;
+
+    /// <summary>
+    /// Returns the username of this URI.
+    /// E.g. the username of http://user:pass@example.com is "user"
+    /// </summary>
+    std::string username() const;
+
+    /// <summary>
+    /// Returns the password of this URI.
+    /// E.g. the password of http://user:pass@example.com is "pass"
+    /// </summary>
+    std::string password() const;
+
+    /// <summary>
+    /// Returns the host of this URI.
+    /// E.g. the host of http://example.com:80/document is "example.com"
+    /// </summary>
+    std::string host() const;
+
+    /// <summary>
+    /// Returns true if a non-default port is specified for this URI.
+    /// </summary>
+    bool has_port() const;
+
+    /// <summary>
+    /// Returns the port of this URI.
+    /// E.g. the port of https://example.com:443/document is "443"
+    /// </summary>
+    std::size_t port() const;
+
+    /// <summary>
+    /// Returns the path of this URI.
+    /// E.g. the path of http://example.com/document is "/document"
+    /// </summary>
+    const class path &path() const;
+
+    /// <summary>
+    /// Returns true if this URI has a non-null query string section.
+    /// </summary>
+    bool has_query() const;
+
+    /// <summary>
+    /// Returns the query string of this URI.
+    /// E.g. the query of http://example.com/document?v=1&x=3#abc is "v=1&x=3"
+    /// </summary>
+    std::string query() const;
+
+    /// <summary>
+    /// Returns true if this URI has a non-empty fragment section.
+    /// </summary>
+    bool has_fragment() const;
+
+    /// <summary>
+    /// Returns the fragment section of this URI.
+    /// E.g. the fragment of http://example.com/document#abc is "abc"
+    /// </summary>
+    std::string fragment() const;
+
+    /// <summary>
+    /// Returns a string representation of this URI.
+    /// </summary>
+    std::string to_string() const;
+
+    /// <summary>
+    /// If this URI is relative, an absolute URI will be returned by appending
+    /// the path to the given absolute base URI.
+    /// </summary>
+    uri make_absolute(const uri &base);
+
+    /// <summary>
+    /// If this URI is absolute, a relative URI will be returned by removing the
+    /// common base path from the given absolute base URI.
+    /// </summary>
+    uri make_reference(const uri &base);
+
+    /// <summary>
+    /// Returns true if this URI is equivalent to other.
+    /// </summary>
+    bool operator==(const uri &other) const;
+
+private:
+    /// <summary>
+    /// True if this URI is absolute.
+    /// </summary>
+    bool absolute_ = false;
+
+    /// <summary>
+    /// The scheme, like "http"
+    /// </summary>
+    std::string scheme_;
+
+    /// <summary>
+    /// True if this URI has an authentication section.
+    /// </summary>
+    bool has_authentication_ = false;
+
+    /// <summary>
+    /// The username
+    /// </summary>
+    std::string username_;
+
+    /// <summary>
+    /// The password
+    /// </summary>
+    std::string password_;
+
+    /// <summary>
+    /// The host
+    /// </summary>
+    std::string host_;
+
+    /// <summary>
+    /// True if this URI has a non-default port specified
+    /// </summary>
+    bool has_port_ = false;
+
+    /// <summary>
+    /// The numeric port
+    /// </summary>
+    std::size_t port_ = 0;
+
+    /// <summary>
+    /// True if this URI has a query section
+    /// </summary>
+    bool has_query_ = false;
+
+    /// <summary>
+    /// The query section
+    /// </summary>
+    std::string query_;
+
+    /// <summary>
+    /// True if this URI has a fragment section
+    /// </summary>
+    bool has_fragment_ = false;
+
+    /// <summary>
+    /// The fragment section
+    /// </summary>
+    std::string fragment_;
+
+    /// <summary>
+    /// The path section
+    /// </summary>
+    class path path_;
+};
+
+} // namespace xlnt

+ 168 - 0
xlnt/include/xlnt/styles/alignment.hpp

@@ -0,0 +1,168 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <xlnt/xlnt_config.hpp>
+#include <xlnt/utils/optional.hpp>
+
+namespace xlnt {
+
+/// <summary>
+/// Text can be aligned horizontally within a cell in these enumerated ways.
+/// </summary>
+enum class XLNT_API horizontal_alignment
+{
+    general,
+    left,
+    center,
+    right,
+    fill,
+    justify,
+    center_continuous,
+    distributed
+};
+
+/// <summary>
+/// Text can be aligned vertically within a cell in these enumerated ways.
+/// </summary>
+enum class XLNT_API vertical_alignment
+{
+    top,
+    center,
+    bottom,
+    justify,
+    distributed
+};
+
+/// <summary>
+/// Alignment options that determine how text should be displayed within a cell.
+/// </summary>
+class XLNT_API alignment
+{
+public:
+    /// <summary>
+    /// Returns true if shrink-to-fit has been enabled.
+    /// </summary>
+    bool shrink() const;
+
+    /// <summary>
+    /// Sets whether the font size should be reduced until all of the text fits in a cell without wrapping.
+    /// </summary>
+    alignment &shrink(bool shrink_to_fit);
+
+    /// <summary>
+    /// Returns true if text-wrapping has been enabled.
+    /// </summary>
+    bool wrap() const;
+
+    /// <summary>
+    /// Sets whether text in a cell should continue to multiple lines if it doesn't fit in one line.
+    /// </summary>
+    alignment &wrap(bool wrap_text);
+
+    /// <summary>
+    /// Returns the optional value of indentation width in number of spaces.
+    /// </summary>
+    optional<int> indent() const;
+
+    /// <summary>
+    /// Sets the indent size in number of spaces from the side of the cell. This will only
+    /// take effect when left or right horizontal alignment has also been set.
+    /// </summary>
+    alignment &indent(int indent_size);
+
+    /// <summary>
+    /// Returns the optional value of rotation for text in the cell in degrees.
+    /// </summary>
+    optional<int> rotation() const;
+
+    /// <summary>
+    /// Sets the rotation for text in the cell in degrees.
+    /// </summary>
+    alignment &rotation(int text_rotation);
+
+    /// <summary>
+    /// Returns the optional horizontal alignment.
+    /// </summary>
+    optional<horizontal_alignment> horizontal() const;
+
+    /// <summary>
+    /// Sets the horizontal alignment.
+    /// </summary>
+    alignment &horizontal(horizontal_alignment horizontal);
+
+    /// <summary>
+    /// Returns the optional vertical alignment.
+    /// </summary>
+    optional<vertical_alignment> vertical() const;
+
+    /// <summary>
+    /// Sets the vertical alignment.
+    /// </summary>
+    alignment &vertical(vertical_alignment vertical);
+
+    /// <summary>
+    /// Returns true if this alignment is equivalent to other.
+    /// </summary>
+    bool operator==(const alignment &other) const;
+
+    /// <summary>
+    /// Returns true if this alignment is not equivalent to other.
+    /// </summary>
+    bool operator!=(const alignment &other) const;
+
+private:
+    /// <summary>
+    /// Whether or not to shrink font size until it fits on one line
+    /// </summary>
+    bool shrink_to_fit_ = false;
+
+    /// <summary>
+    /// Whether or not to wrap text to the next line
+    /// </summary>
+    bool wrap_text_ = false;
+
+    /// <summary>
+    /// The indent in number of spaces from the side
+    /// </summary>
+    optional<int> indent_;
+
+    /// <summary>
+    /// The text roation in degrees
+    /// </summary>
+    optional<int> text_rotation_;
+
+    /// <summary>
+    /// The horizontal alignment
+    /// </summary>
+    optional<horizontal_alignment> horizontal_;
+
+    /// <summary>
+    /// The vertical alignment
+    /// </summary>
+    optional<vertical_alignment> vertical_;
+};
+
+} // namespace xlnt

+ 227 - 0
xlnt/include/xlnt/styles/border.hpp

@@ -0,0 +1,227 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <cstddef>
+#include <functional>
+#include <unordered_map>
+#include <vector>
+
+#include <xlnt/xlnt_config.hpp>
+#include <xlnt/styles/color.hpp>
+#include <xlnt/utils/optional.hpp>
+
+namespace xlnt {
+
+/// <summary>
+/// Enumerates the sides of a cell to which a border style can be applied.
+/// </summary>
+enum class XLNT_API border_side
+{
+    start,
+    end,
+    top,
+    bottom,
+    diagonal,
+    vertical,
+    horizontal
+};
+
+/// <summary>
+/// Enumerates the pattern of the border lines on a particular side.
+/// </summary>
+enum class XLNT_API border_style
+{
+    none,
+    dashdot,
+    dashdotdot,
+    dashed,
+    dotted,
+    double_,
+    hair,
+    medium,
+    mediumdashdot,
+    mediumdashdotdot,
+    mediumdashed,
+    slantdashdot,
+    thick,
+    thin
+};
+
+/// <summary>
+/// Cells can have borders that go from the top-left to bottom-right
+/// or from the top-right to bottom-left, or both, or neither.
+/// Used by style->border.
+/// </summary>
+enum class XLNT_API diagonal_direction
+{
+    neither,
+    up,
+    down,
+    both
+};
+
+} // namespace xlnt
+
+namespace xlnt {
+
+/// <summary>
+/// Describes the border style of a particular cell.
+/// </summary>
+class XLNT_API border
+{
+public:
+    /// <summary>
+    /// Each side of a cell can have a border_property applied to it to change
+    /// how it is displayed.
+    /// </summary>
+    class XLNT_API border_property
+    {
+    public:
+        /// <summary>
+        /// Returns the color of the side.
+        /// </summary>
+        optional<class color> color() const;
+
+        /// <summary>
+        /// Sets the color of the side and returns a reference to the side properties.
+        /// </summary>
+        border_property &color(const xlnt::color &c);
+
+        /// <summary>
+        /// Returns the style of the side.
+        /// </summary>
+        optional<border_style> style() const;
+
+        /// <summary>
+        /// Sets the style of the side and returns a reference to the side properties.
+        /// </summary>
+        border_property &style(border_style style);
+
+        /// <summary>
+        /// Returns true if left is exactly equal to right.
+        /// </summary>
+        bool operator==(const border_property &right) const;
+
+        /// <summary>
+        /// Returns true if left is not exactly equal to right.
+        /// </summary>
+        bool operator!=(const border_property &right) const;
+
+    private:
+        /// <summary>
+        /// The color of the side
+        /// </summary>
+        optional<class color> color_;
+
+        /// <summary>
+        /// The style of the side
+        /// </summary>
+        optional<border_style> style_;
+    };
+
+    /// <summary>
+    /// Returns a vector containing all of the border sides to be used for iteration.
+    /// </summary>
+    static const std::vector<border_side> &all_sides();
+
+    /// <summary>
+    /// Constructs a default border.
+    /// </summary>
+    border();
+
+    /// <summary>
+    /// Returns the border properties of the given side.
+    /// </summary>
+    optional<border_property> side(border_side s) const;
+
+    /// <summary>
+    /// Sets the border properties of the side s to prop.
+    /// </summary>
+    border &side(border_side s, const border_property &prop);
+
+    /// <summary>
+    /// Returns the diagonal direction of this border.
+    /// </summary>
+    optional<diagonal_direction> diagonal() const;
+
+    /// <summary>
+    /// Sets the diagonal direction of this border to dir.
+    /// </summary>
+    border &diagonal(diagonal_direction dir);
+
+    /// <summary>
+    /// Returns true if left is exactly equal to right.
+    /// </summary>
+    bool operator==(const border &right) const;
+
+    /// <summary>
+    /// Returns true if left is not exactly equal to right.
+    /// </summary>
+    bool operator!=(const border &right) const;
+
+private:
+    /// <summary>
+    /// Start side (i.e. left) border properties
+    /// </summary>
+    optional<border_property> start_;
+
+    /// <summary>
+    /// End side (i.e. right) border properties
+    /// </summary>
+    optional<border_property> end_;
+
+    /// <summary>
+    /// Top side border properties
+    /// </summary>
+    optional<border_property> top_;
+
+    /// <summary>
+    /// Bottom side border properties
+    /// </summary>
+    optional<border_property> bottom_;
+
+    /// <summary>
+    /// Vertical border properties
+    /// </summary>
+    optional<border_property> vertical_;
+
+    /// <summary>
+    /// Horizontal border properties
+    /// </summary>
+    optional<border_property> horizontal_;
+
+    /// <summary>
+    /// Diagonal border properties
+    /// </summary>
+    optional<border_property> diagonal_;
+
+    /// <summary>
+    /// Direction of diagonal border properties to be applied
+    /// </summary>
+    optional<diagonal_direction> diagonal_direction_;
+};
+
+} // namespace xlnt

+ 350 - 0
xlnt/include/xlnt/styles/color.hpp

@@ -0,0 +1,350 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <array>
+#include <string>
+
+#include <xlnt/xlnt_config.hpp>
+#include <xlnt/utils/optional.hpp>
+
+namespace xlnt {
+
+/// <summary>
+/// An indexed color encapsulates a simple index to a color in the indexedColors of the stylesheet.
+/// </summary>
+class XLNT_API indexed_color
+{
+public:
+    //TODO: should this be explicit?
+    /// <summary>
+    /// Constructs an indexed_color from an index.
+    /// </summary>
+    indexed_color(std::size_t index);
+
+    /// <summary>
+    /// Returns the index this color points to.
+    /// </summary>
+    std::size_t index() const;
+
+    /// <summary>
+    /// Sets the index to index.
+    /// </summary>
+    void index(std::size_t index);
+
+private:
+    /// <summary>
+    /// The index of this color
+    /// </summary>
+    std::size_t index_;
+};
+
+/// <summary>
+/// A theme color encapsulates a color derived from the theme.
+/// </summary>
+class XLNT_API theme_color
+{
+public:
+    /// <summary>
+    /// Constructs a theme_color from an index.
+    /// </summary>
+    theme_color(std::size_t index);
+
+    /// <summary>
+    /// Returns the index of the color in the theme this points to.
+    /// </summary>
+    std::size_t index() const;
+
+    /// <summary>
+    /// Sets the index of this color to index.
+    /// </summary>
+    void index(std::size_t index);
+
+private:
+    /// <summary>
+    /// The index of the color
+    /// </summary>
+    std::size_t index_;
+};
+
+/// <summary>
+/// An RGB color describes a color in terms of its red, green, blue, and alpha components.
+/// </summary>
+class XLNT_API rgb_color
+{
+public:
+    /// <summary>
+    /// Constructs an RGB color from a string in the form \#[aa]rrggbb
+    /// </summary>
+    rgb_color(const std::string &hex_string);
+
+    /// <summary>
+    /// Constructs an RGB color from red, green, and blue values in the range 0 to 255
+    /// plus an optional alpha which defaults to fully opaque.
+    /// </summary>
+    rgb_color(std::uint8_t r, std::uint8_t g, std::uint8_t b, std::uint8_t a = 255);
+
+    /// <summary>
+    /// Returns a string representation of this color in the form \#aarrggbb
+    /// </summary>
+    std::string hex_string() const;
+
+    /// <summary>
+    /// Returns a byte representing the red component of this color
+    /// </summary>
+    std::uint8_t red() const;
+
+    /// <summary>
+    /// Returns a byte representing the red component of this color
+    /// </summary>
+    std::uint8_t green() const;
+
+    /// <summary>
+    /// Returns a byte representing the blue component of this color
+    /// </summary>
+    std::uint8_t blue() const;
+
+    /// <summary>
+    /// Returns a byte representing the alpha component of this color
+    /// </summary>
+    std::uint8_t alpha() const;
+
+    /// <summary>
+    /// Returns the red, green, and blue components of this color separately in an array in that order.
+    /// </summary>
+    std::array<std::uint8_t, 3> rgb() const;
+
+    /// <summary>
+    /// Returns the red, green, blue, and alpha components of this color separately in an array in that order.
+    /// </summary>
+    std::array<std::uint8_t, 4> rgba() const;
+
+private:
+    /// <summary>
+    /// The four bytes of this color
+    /// </summary>
+    std::array<std::uint8_t, 4> rgba_;
+};
+
+/// <summary>
+/// Some colors are references to colors rather than having a particular RGB value.
+/// </summary>
+enum class color_type
+{
+    indexed,
+    theme,
+    rgb
+};
+
+/// <summary>
+/// Colors can be applied to many parts of a cell's style.
+/// </summary>
+class XLNT_API color
+{
+public:
+    /// <summary>
+    /// Returns the color \#000000
+    /// </summary>
+    static const color black();
+
+    /// <summary>
+    /// Returns the color \#ffffff
+    /// </summary>
+    static const color white();
+
+    /// <summary>
+    /// Returns the color \#ff0000
+    /// </summary>
+    static const color red();
+
+    /// <summary>
+    /// Returns the color \#8b0000
+    /// </summary>
+    static const color darkred();
+
+    /// <summary>
+    /// Returns the color \#00ff00
+    /// </summary>
+    static const color blue();
+
+    /// <summary>
+    /// Returns the color \#008b00
+    /// </summary>
+    static const color darkblue();
+
+    /// <summary>
+    /// Returns the color \#0000ff
+    /// </summary>
+    static const color green();
+
+    /// <summary>
+    /// Returns the color \#00008b
+    /// </summary>
+    static const color darkgreen();
+
+    /// <summary>
+    /// Returns the color \#ffff00
+    /// </summary>
+    static const color yellow();
+
+    /// <summary>
+    /// Returns the color \#cccc00
+    /// </summary>
+    static const color darkyellow();
+
+    /// <summary>
+    /// Constructs a default color
+    /// </summary>
+    color();
+
+    /// <summary>
+    /// Constructs a color from a given RGB color
+    /// </summary>
+    color(const rgb_color &rgb);
+
+    /// <summary>
+    /// Constructs a color from a given indexed color
+    /// </summary>
+    color(const indexed_color &indexed);
+
+    /// <summary>
+    /// Constructs a color from a given theme color
+    /// </summary>
+    color(const theme_color &theme);
+
+    /// <summary>
+    /// Returns the type of this color
+    /// </summary>
+    color_type type() const;
+
+    /// <summary>
+    /// Returns true if this color has been set to auto
+    /// </summary>
+    bool auto_() const;
+
+    /// <summary>
+    /// Sets the auto property of this color to value
+    /// </summary>
+    void auto_(bool value);
+
+    /// <summary>
+    /// Returns the internal indexed color representing this color. If this is not an RGB color,
+    /// an invalid_attribute exception will be thrown.
+    /// </summary>
+    const rgb_color &rgb() const;
+
+    /// <summary>
+    /// Returns the internal indexed color representing this color. If this is not an RGB color,
+    /// an invalid_attribute exception will be thrown.
+    /// </summary>
+    rgb_color &rgb();
+
+    /// <summary>
+    /// Returns the internal indexed color representing this color. If this is not an indexed color,
+    /// an invalid_attribute exception will be thrown.
+    /// </summary>
+    const indexed_color &indexed() const;
+
+    /// <summary>
+    /// Returns the internal indexed color representing this color. If this is not an indexed color,
+    /// an invalid_attribute exception will be thrown.
+    /// </summary>
+    indexed_color &indexed();
+
+    /// <summary>
+    /// Returns the internal indexed color representing this color. If this is not a theme color,
+    /// an invalid_attribute exception will be thrown.
+    /// </summary>
+    const theme_color &theme() const;
+
+    /// <summary>
+    /// Returns the internal indexed color representing this color. If this is not a theme color,
+    /// an invalid_attribute exception will be thrown.
+    /// </summary>
+    theme_color &theme();
+
+    /// <summary>
+    /// Returns true if tint is set
+    /// </summary>
+    bool has_tint() const;
+
+    /// <summary>
+    /// Returns the tint of this color.
+    /// </summary>
+    double tint() const;
+
+    /// <summary>
+    /// Sets the tint of this color to tint. Tints lighten or darken an existing color by multiplying the color with the tint.
+    /// </summary>
+    void tint(double tint);
+
+    /// <summary>
+    /// Returns true if this color is equivalent to other
+    /// </summary>
+    bool operator==(const color &other) const;
+
+    /// <summary>
+    /// Returns true if this color is not equivalent to other
+    /// </summary>
+    bool operator!=(const color &other) const;
+
+private:
+    /// <summary>
+    /// Throws an invalid_attribute exception if the given type is different from this color's type
+    /// </summary>
+    void assert_type(color_type t) const;
+
+    /// <summary>
+    /// The type of this color
+    /// </summary>
+    color_type type_;
+
+    /// <summary>
+    /// The internal RGB color. Only valid when this color has a type of rgb
+    /// </summary>
+    rgb_color rgb_;
+
+    /// <summary>
+    /// The internal RGB color. Only valid when this color has a type of indexed
+    /// </summary>
+    indexed_color indexed_;
+
+    /// <summary>
+    /// The internal RGB color. Only valid when this color has a type of theme
+    /// </summary>
+    theme_color theme_;
+
+    /// <summary>
+    /// The tint of this color
+    /// </summary>
+    optional<double> tint_;
+
+    /// <summary>
+    /// Whether or not this is an auto color
+    /// </summary>
+    bool auto_color = false;
+};
+
+} // namespace xlnt

+ 170 - 0
xlnt/include/xlnt/styles/conditional_format.hpp

@@ -0,0 +1,170 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <cstdint>
+#include <string>
+
+#include <xlnt/xlnt_config.hpp>
+#include <xlnt/utils/optional.hpp>
+
+namespace xlnt {
+
+class border;
+class fill;
+class font;
+
+namespace detail {
+
+struct conditional_format_impl;
+struct stylesheet;
+class xlsx_consumer;
+class xlsx_producer;
+
+} // namespace detail
+
+class XLNT_API condition
+{
+public:
+    static condition text_starts_with(const std::string &start);
+    static condition text_ends_with(const std::string &end);
+    static condition text_contains(const std::string &start);
+    static condition text_does_not_contain(const std::string &start);
+
+    bool operator==(const condition &rhs) const
+    {
+        return text_comparand_ == rhs.text_comparand_;
+    }
+
+private:
+    friend class detail::xlsx_producer;
+
+    enum class type
+    {
+        contains_text
+    } type_;
+
+    enum class condition_operator
+    {
+        starts_with,
+        ends_with,
+        contains,
+        does_not_contain
+    } operator_;
+
+    std::string text_comparand_;
+};
+
+/// <summary>
+/// Describes a conditional format that will be applied to all cells in the
+/// associated range that satisfy the condition. This can only be constructed
+/// using methods on worksheet or range.
+/// </summary>
+class XLNT_API conditional_format
+{
+public:
+    /// <summary>
+    /// Delete zero-argument constructor
+    /// </summary>
+    conditional_format() = delete;
+
+    /// <summary>
+    /// Default copy constructor. Constructs a format using the same PIMPL as other.
+    /// </summary>
+    conditional_format(const conditional_format &other) = default;
+
+    // Formatting (xf) components
+
+    /// <summary>
+    ///
+    /// </summary>
+    bool has_border() const;
+
+    /// <summary>
+    ///
+    /// </summary>
+    class border border() const;
+
+    /// <summary>
+    ///
+    /// </summary>
+    conditional_format border(const xlnt::border &new_border);
+
+    /// <summary>
+    ///
+    /// </summary>
+    bool has_fill() const;
+
+    /// <summary>
+    ///
+    /// </summary>
+    class fill fill() const;
+
+    /// <summary>
+    ///
+    /// </summary>
+    conditional_format fill(const xlnt::fill &new_fill);
+
+    /// <summary>
+    ///
+    /// </summary>
+    bool has_font() const;
+
+    /// <summary>
+    ///
+    /// </summary>
+    class font font() const;
+
+    /// <summary>
+    ///
+    /// </summary>
+    conditional_format font(const xlnt::font &new_font);
+
+    /// <summary>
+    /// Returns true if this format is equivalent to other.
+    /// </summary>
+    bool operator==(const conditional_format &other) const;
+
+    /// <summary>
+    /// Returns true if this format is not equivalent to other.
+    /// </summary>
+    bool operator!=(const conditional_format &other) const;
+
+private:
+    friend struct detail::stylesheet;
+    friend class detail::xlsx_consumer;
+
+    /// <summary>
+    ///
+    /// </summary>
+    conditional_format(detail::conditional_format_impl *d);
+
+    /// <summary>
+    ///
+    /// </summary>
+    detail::conditional_format_impl *d_;
+};
+
+} // namespace xlnt

+ 369 - 0
xlnt/include/xlnt/styles/fill.hpp

@@ -0,0 +1,369 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <unordered_map>
+
+#include <xlnt/xlnt_config.hpp>
+#include <xlnt/styles/color.hpp>
+#include <xlnt/utils/optional.hpp>
+
+namespace xlnt {
+
+/// <summary>
+/// The pattern of pixels upon which the corresponding pattern fill will be displayed
+/// </summary>
+enum class XLNT_API pattern_fill_type
+{
+    none,
+    solid,
+    mediumgray,
+    darkgray,
+    lightgray,
+    darkhorizontal,
+    darkvertical,
+    darkdown,
+    darkup,
+    darkgrid,
+    darktrellis,
+    lighthorizontal,
+    lightvertical,
+    lightdown,
+    lightup,
+    lightgrid,
+    lighttrellis,
+    gray125,
+    gray0625
+};
+
+/// <summary>
+/// Represents a fill which colors the cell based on a foreground and
+/// background color and a pattern.
+/// </summary>
+class XLNT_API pattern_fill
+{
+public:
+    /// <summary>
+    /// Constructs a default pattern fill with a none pattern and no colors.
+    /// </summary>
+    pattern_fill();
+
+    /// <summary>
+    /// Returns the pattern used by this fill
+    /// </summary>
+    pattern_fill_type type() const;
+
+    /// <summary>
+    /// Sets the pattern of this fill and returns a reference to it.
+    /// </summary>
+    pattern_fill &type(pattern_fill_type new_type);
+
+    /// <summary>
+    /// Returns the optional foreground color of this fill
+    /// </summary>
+    optional<color> foreground() const;
+
+    /// <summary>
+    /// Sets the foreground color and returns a reference to this pattern.
+    /// </summary>
+    pattern_fill &foreground(const color &foreground);
+
+    /// <summary>
+    /// Returns the optional background color of this fill
+    /// </summary>
+    optional<color> background() const;
+
+    /// <summary>
+    /// Sets the foreground color and returns a reference to this pattern.
+    /// </summary>
+    pattern_fill &background(const color &background);
+
+    /// <summary>
+    /// Returns true if this pattern fill is equivalent to other.
+    /// </summary>
+    bool operator==(const pattern_fill &other) const;
+
+    /// <summary>
+    /// Returns true if this pattern fill is not equivalent to other.
+    /// </summary>
+    bool operator!=(const pattern_fill &other) const;
+
+private:
+    /// <summary>
+    /// The type of this pattern_fill
+    /// </summary>
+    pattern_fill_type type_ = pattern_fill_type::none;
+
+    /// <summary>
+    /// The optional foreground color
+    /// </summary>
+    optional<color> foreground_;
+
+    /// <summary>
+    /// THe optional background color
+    /// </summary>
+    optional<color> background_;
+};
+
+/// <summary>
+/// Enumerates the types of gradient fills
+/// </summary>
+enum class XLNT_API gradient_fill_type
+{
+    linear,
+    path
+};
+
+/// <summary>
+/// Encapsulates a fill which transitions between colors at particular "stops".
+/// </summary>
+class XLNT_API gradient_fill
+{
+public:
+    /// <summary>
+    /// Constructs a default linear fill
+    /// </summary>
+    gradient_fill();
+
+    /// <summary>
+    /// Returns the type of this gradient fill
+    /// </summary>
+    gradient_fill_type type() const;
+
+    // Type
+
+    /// <summary>
+    /// Sets the type of this gradient fill
+    /// </summary>
+    gradient_fill &type(gradient_fill_type new_type);
+
+    // Degree
+
+    /// <summary>
+    /// Sets the angle of the gradient in degrees
+    /// </summary>
+    gradient_fill &degree(double degree);
+
+    /// <summary>
+    /// Returns the angle of the gradient
+    /// </summary>
+    double degree() const;
+
+    // Left
+
+    /// <summary>
+    /// Returns the distance from the left where the gradient starts.
+    /// </summary>
+    double left() const;
+
+    /// <summary>
+    /// Sets the distance from the left where the gradient starts.
+    /// </summary>
+    gradient_fill &left(double value);
+
+    // Right
+
+    /// <summary>
+    /// Returns the distance from the right where the gradient starts.
+    /// </summary>
+    double right() const;
+
+    /// <summary>
+    /// Sets the distance from the right where the gradient starts.
+    /// </summary>
+    gradient_fill &right(double value);
+
+    // Top
+
+    /// <summary>
+    /// Returns the distance from the top where the gradient starts.
+    /// </summary>
+    double top() const;
+
+    /// <summary>
+    /// Sets the distance from the top where the gradient starts.
+    /// </summary>
+    gradient_fill &top(double value);
+
+    // Bottom
+
+    /// <summary>
+    /// Returns the distance from the bottom where the gradient starts.
+    /// </summary>
+    double bottom() const;
+
+    /// <summary>
+    /// Sets the distance from the bottom where the gradient starts.
+    /// </summary>
+    gradient_fill &bottom(double value);
+
+    // Stops
+
+    /// <summary>
+    /// Adds a gradient stop at position with the given color.
+    /// </summary>
+    gradient_fill &add_stop(double position, color stop_color);
+
+    /// <summary>
+    /// Deletes all stops from the gradient.
+    /// </summary>
+    gradient_fill &clear_stops();
+
+    /// <summary>
+    /// Returns all of the gradient stops.
+    /// </summary>
+    std::unordered_map<double, color> stops() const;
+
+    /// <summary>
+    /// Returns true if the gradient is equivalent to other.
+    /// </summary>
+    bool operator==(const gradient_fill &other) const;
+
+    /// <summary>
+    /// Returns true if the gradient is not equivalent to other.
+    /// </summary>
+    bool operator!=(const gradient_fill &right) const;
+
+private:
+    /// <summary>
+    /// The type of gradient
+    /// </summary>
+    gradient_fill_type type_ = gradient_fill_type::linear;
+
+    /// <summary>
+    /// The angle of the gradient
+    /// </summary>
+    double degree_ = 0;
+
+    /// <summary>
+    /// THe left distance
+    /// </summary>
+    double left_ = 0;
+
+    /// <summary>
+    /// THe right distance
+    /// </summary>
+    double right_ = 0;
+
+    /// <summary>
+    /// The top distance
+    /// </summary>
+    double top_ = 0;
+
+    /// <summary>
+    /// The bottom distance
+    /// </summary>
+    double bottom_ = 0;
+
+    /// <summary>
+    /// The gradient stops and colors
+    /// </summary>
+    std::unordered_map<double, color> stops_;
+};
+
+/// <summary>
+/// Enumerates the possible fill types
+/// </summary>
+enum class XLNT_API fill_type
+{
+    pattern,
+    gradient
+};
+
+/// <summary>
+/// Describes the fill style of a particular cell.
+/// </summary>
+class XLNT_API fill
+{
+public:
+    /// <summary>
+    /// Helper method for the most common use case, setting the fill color of a cell to a single solid color.
+    /// The foreground and background colors of a fill are not the same as the foreground and background colors
+    /// of a cell. When setting a fill color in Excel, a new fill is created with the given color as the fill's
+    /// fgColor and index color number 64 as the bgColor. This method creates a fill in the same way.
+    /// </summary>
+    static fill solid(const color &fill_color);
+
+    /// <summary>
+    /// Constructs a fill initialized as a none-type pattern fill with no
+    /// foreground or background colors.
+    /// </summary>
+    fill();
+
+    /// <summary>
+    /// Constructs a fill initialized as a pattern fill based on the given pattern.
+    /// </summary>
+    fill(const pattern_fill &pattern);
+
+    /// <summary>
+    /// Constructs a fill initialized as a gradient fill based on the given gradient.
+    /// </summary>
+    fill(const gradient_fill &gradient);
+
+    /// <summary>
+    /// Returns the fill_type of this fill depending on how it was constructed.
+    /// </summary>
+    fill_type type() const;
+
+    /// <summary>
+    /// Returns the gradient fill represented by this fill.
+    /// Throws an invalid_attribute exception if this is not a gradient fill.
+    /// </summary>
+    class gradient_fill gradient_fill() const;
+
+    /// <summary>
+    /// Returns the pattern fill represented by this fill.
+    /// Throws an invalid_attribute exception if this is not a pattern fill.
+    /// </summary>
+    class pattern_fill pattern_fill() const;
+
+    /// <summary>
+    /// Returns true if left is exactly equal to right.
+    /// </summary>
+    bool operator==(const fill &other) const;
+
+    /// <summary>
+    /// Returns true if left is not exactly equal to right.
+    /// </summary>
+    bool operator!=(const fill &other) const;
+
+private:
+    /// <summary>
+    /// The type of this fill
+    /// </summary>
+    fill_type type_ = fill_type::pattern;
+
+    /// <summary>
+    /// The internal gradient fill if this is a gradient fill type
+    /// </summary>
+    xlnt::gradient_fill gradient_;
+
+    /// <summary>
+    /// The internal pattern fill if this is a pattern fill type
+    /// </summary>
+    xlnt::pattern_fill pattern_;
+};
+
+} // namespace xlnt

+ 324 - 0
xlnt/include/xlnt/styles/font.hpp

@@ -0,0 +1,324 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <string>
+
+#include <xlnt/xlnt_config.hpp>
+#include <xlnt/styles/color.hpp>
+#include <xlnt/utils/optional.hpp>
+
+namespace xlnt {
+
+class style;
+
+/// <summary>
+/// Describes the font style of a particular cell.
+/// </summary>
+class XLNT_API font
+{
+public:
+    /// <summary>
+    /// Text can be underlined in the enumerated ways
+    /// </summary>
+    enum class underline_style
+    {
+        none,
+        double_,
+        double_accounting,
+        single,
+        single_accounting
+    };
+
+    /// <summary>
+    /// Constructs a default font. Calibri, size 12
+    /// </summary>
+    font();
+
+    /// <summary>
+    /// Sets the bold state of the font to bold and returns a reference to the font.
+    /// </summary>
+    font &bold(bool bold);
+
+    /// <summary>
+    /// Returns the bold state of the font.
+    /// </summary>
+    bool bold() const;
+
+    /// <summary>
+    /// Sets the vertical alignment of the font to subscript and returns a reference to the font.
+    /// </summary>
+    font &subscript(bool value);
+
+    /// <summary>
+    /// Returns true if this font has a vertical alignment of subscript.
+    /// </summary>
+    bool subscript() const;
+
+    /// <summary>
+    /// Sets the vertical alignment of the font to superscript and returns a reference to the font.
+    /// </summary>
+    font &superscript(bool value);
+
+    /// <summary>
+    /// Returns true if this font has a vertical alignment of superscript.
+    /// </summary>
+    bool superscript() const;
+
+    /// <summary>
+    /// Sets the bold state of the font to bold and returns a reference to the font.
+    /// </summary>
+    font &italic(bool italic);
+
+    /// <summary>
+    /// Returns true if this font has italics applied.
+    /// </summary>
+    bool italic() const;
+
+    /// <summary>
+    /// Sets the bold state of the font to bold and returns a reference to the font.
+    /// </summary>
+    font &strikethrough(bool strikethrough);
+
+    /// <summary>
+    /// Returns true if this font has a strikethrough applied.
+    /// </summary>
+    bool strikethrough() const;
+
+    /// <summary>
+    /// Sets the bold state of the font to bold and returns a reference to the font.
+    /// </summary>
+    font &outline(bool outline);
+
+    /// <summary>
+    /// Returns true if this font has an outline applied.
+    /// </summary>
+    bool outline() const;
+
+    /// <summary>
+    /// Sets the shadow state of the font to shadow and returns a reference to the font.
+    /// </summary>
+    font &shadow(bool shadow);
+
+    /// <summary>
+    /// Returns true if this font has a shadow applied.
+    /// </summary>
+    bool shadow() const;
+
+    /// <summary>
+    /// Sets the underline state of the font to new_underline and returns a reference to the font.
+    /// </summary>
+    font &underline(underline_style new_underline);
+
+    /// <summary>
+    /// Returns true if this font has any type of underline applied.
+    /// </summary>
+    bool underlined() const;
+
+    /// <summary>
+    /// Returns the particular style of underline this font has applied.
+    /// </summary>
+    underline_style underline() const;
+
+    /// <summary>
+    /// Returns true if this font has a defined size.
+    /// </summary>
+    bool has_size() const;
+
+    /// <summary>
+    /// Sets the size of the font to size and returns a reference to the font.
+    /// </summary>
+    font &size(double size);
+
+    /// <summary>
+    /// Returns the size of the font.
+    /// </summary>
+    double size() const;
+
+    /// <summary>
+    /// Returns true if this font has a particular face applied (e.g. "Comic Sans").
+    /// </summary>
+    bool has_name() const;
+
+    /// <summary>
+    /// Sets the font face to name and returns a reference to the font.
+    /// </summary>
+    font &name(const std::string &name);
+
+    /// <summary>
+    /// Returns the name of the font face.
+    /// </summary>
+    const std::string &name() const;
+
+    /// <summary>
+    /// Returns true if this font has a color applied.
+    /// </summary>
+    bool has_color() const;
+
+    /// <summary>
+    /// Sets the color of the font to c and returns a reference to the font.
+    /// </summary>
+    font &color(const color &c);
+
+    /// <summary>
+    /// Returns the color that this font is using.
+    /// </summary>
+    xlnt::color color() const;
+
+    /// <summary>
+    /// Returns true if this font has a family defined.
+    /// </summary>
+    bool has_family() const;
+
+    /// <summary>
+    /// Sets the family index of the font to family and returns a reference to the font.
+    /// </summary>
+    font &family(std::size_t family);
+
+    /// <summary>
+    /// Returns the family index for the font.
+    /// </summary>
+    std::size_t family() const;
+
+    /// <summary>
+    /// Returns true if this font has a charset defined.
+    /// </summary>
+    bool has_charset() const;
+
+    // TODO: charset should be an enum, not a number
+
+    /// <summary>
+    /// Sets the charset of the font to charset and returns a reference to the font.
+    /// </summary>
+    font &charset(std::size_t charset);
+
+    /// <summary>
+    /// Returns the charset of the font.
+    /// </summary>
+    std::size_t charset() const;
+
+    /// <summary>
+    /// Returns true if this font has a scheme.
+    /// </summary>
+    bool has_scheme() const;
+
+    /// <summary>
+    /// Sets the scheme of the font to scheme and returns a reference to the font.
+    /// </summary>
+    font &scheme(const std::string &scheme);
+
+    /// <summary>
+    /// Returns the scheme of this font.
+    /// </summary>
+    const std::string &scheme() const;
+
+    /// <summary>
+    /// Returns true if left is exactly equal to right.
+    /// </summary>
+    bool operator==(const font &other) const;
+
+    /// <summary>
+    /// Returns true if left is not exactly equal to right.
+    /// </summary>
+    bool operator!=(const font &other) const
+    {
+        return !operator==(other);
+    }
+
+private:
+    friend class style;
+
+    /// <summary>
+    /// The name of the font
+    /// </summary>
+    optional<std::string> name_;
+
+    /// <summary>
+    /// size
+    /// </summary>
+    optional<double> size_;
+
+    /// <summary>
+    /// bold
+    /// </summary>
+    bool bold_ = false;
+
+    /// <summary>
+    /// italic
+    /// </summary>
+    bool italic_ = false;
+
+    /// <summary>
+    /// superscript
+    /// </summary>
+    bool superscript_ = false;
+
+    /// <summary>
+    /// subscript
+    /// </summary>
+    bool subscript_ = false;
+
+    /// <summary>
+    /// strikethrough
+    /// </summary>
+    bool strikethrough_ = false;
+
+    /// <summary>
+    /// outline
+    /// </summary>
+    bool outline_ = false;
+
+    /// <summary>
+    /// shadow
+    /// </summary>
+    bool shadow_ = false;
+
+    /// <summary>
+    /// underline style
+    /// </summary>
+    underline_style underline_ = underline_style::none;
+
+    /// <summary>
+    /// color
+    /// </summary>
+    optional<xlnt::color> color_;
+
+    /// <summary>
+    /// family
+    /// </summary>
+    optional<std::size_t> family_;
+
+    /// <summary>
+    /// charset
+    /// </summary>
+    optional<std::size_t> charset_;
+
+    /// <summary>
+    /// scheme
+    /// </summary>
+    optional<std::string> scheme_;
+};
+
+} // namespace xlnt

+ 235 - 0
xlnt/include/xlnt/styles/format.hpp

@@ -0,0 +1,235 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <cstddef>
+#include <string>
+
+#include <xlnt/xlnt_config.hpp>
+
+namespace xlnt {
+
+class alignment;
+class border;
+class cell;
+class fill;
+class font;
+class number_format;
+class protection;
+class style;
+
+template <typename T>
+class optional;
+
+namespace detail {
+
+struct format_impl;
+struct stylesheet;
+class xlsx_producer;
+class xlsx_consumer;
+
+} // namespace detail
+
+/// <summary>
+/// Describes the formatting of a particular cell.
+/// </summary>
+class XLNT_API format
+{
+public:
+    /// <summary>
+    /// Returns the alignment of this format.
+    /// </summary>
+    class alignment alignment() const;
+
+    /// <summary>
+    /// Sets the alignment of this format to new_alignment. Applied, which defaults
+    /// to true, determines whether the alignment should be enabled for cells using
+    /// this format.
+    /// </summary>
+    format alignment(const xlnt::alignment &new_alignment, xlnt::optional<bool> applied = {});
+
+    /// <summary>
+    /// Returns true if the alignment of this format should be applied to cells
+    /// using it.
+    /// </summary>
+    bool alignment_applied() const;
+
+    /// <summary>
+    /// Returns the border of this format.
+    /// </summary>
+    class border border() const;
+
+    /// <summary>
+    /// Sets the border of this format to new_border. Applied, which defaults
+    /// to true, determines whether the border should be enabled for cells using
+    /// this format.
+    /// </summary>
+    format border(const xlnt::border &new_border, xlnt::optional<bool> applied = {});
+
+    /// <summary>
+    /// Returns true if the border set for this format should be applied to cells using the format.
+    /// </summary>
+    bool border_applied() const;
+
+    /// <summary>
+    /// Returns the fill of this format.
+    /// </summary>
+    class fill fill() const;
+
+    /// <summary>
+    /// Sets the fill of this format to new_fill. Applied, which defaults
+    /// to true, determines whether the border should be enabled for cells using
+    /// this format.
+    /// </summary>
+    format fill(const xlnt::fill &new_fill, xlnt::optional<bool> applied = {});
+
+    /// <summary>
+    /// Returns true if the fill set for this format should be applied to cells using the format.
+    /// </summary>
+    bool fill_applied() const;
+
+    /// <summary>
+    /// Returns the font of this format.
+    /// </summary>
+    class font font() const;
+
+    /// <summary>
+    /// Sets the font of this format to new_font. Applied, which defaults
+    /// to true, determines whether the font should be enabled for cells using
+    /// this format.
+    /// </summary>
+    format font(const xlnt::font &new_font, xlnt::optional<bool> applied = {});
+
+    /// <summary>
+    /// Returns true if the font set for this format should be applied to cells using the format.
+    /// </summary>
+    bool font_applied() const;
+
+    /// <summary>
+    /// Returns the number format of this format.
+    /// </summary>
+    class number_format number_format() const;
+
+    /// <summary>
+    /// Sets the number format of this format to new_number_format. Applied, which defaults
+    /// to true, determines whether the number format should be enabled for cells using
+    /// this format.
+    /// </summary>
+    format number_format(const xlnt::number_format &new_number_format, xlnt::optional<bool> applied = {});
+
+    /// <summary>
+    /// Returns true if the number_format set for this format should be applied to cells using the format.
+    /// </summary>
+    bool number_format_applied() const;
+
+    /// <summary>
+    /// Returns the protection of this format.
+    /// </summary>
+    class protection protection() const;
+
+    /// <summary>
+    /// Returns true if the protection set for this format should be applied to cells using the format.
+    /// </summary>
+    bool protection_applied() const;
+
+    /// <summary>
+    /// Sets the protection of this format to new_protection. Applied, which defaults
+    /// to true, determines whether the protection should be enabled for cells using
+    /// this format.
+    /// </summary>
+    format protection(const xlnt::protection &new_protection, xlnt::optional<bool> applied = {});
+
+    /// <summary>
+    /// Returns true if the pivot table interface is enabled for this format.
+    /// </summary>
+    bool pivot_button() const;
+
+    /// <summary>
+    /// If show is true, a pivot table interface will be displayed for cells using
+    /// this format.
+    /// </summary>
+    void pivot_button(bool show);
+
+    /// <summary>
+    /// Returns true if this format should add a single-quote prefix for all text values.
+    /// </summary>
+    bool quote_prefix() const;
+
+    /// <summary>
+    /// If quote is true, enables a single-quote prefix for all text values in cells
+    /// using this format (e.g. "abc" will appear as "'abc"). The text will also not
+    /// be stored in sharedStrings when this is enabled.
+    /// </summary>
+    void quote_prefix(bool quote);
+
+    /// <summary>
+    /// Returns true if this format has a corresponding style applied.
+    /// </summary>
+    bool has_style() const;
+
+    /// <summary>
+    /// Removes the style from this format if it exists.
+    /// </summary>
+    void clear_style();
+
+    /// <summary>
+    /// Sets the style of this format to a style with the given name.
+    /// </summary>
+    format style(const std::string &name);
+
+    /// <summary>
+    /// Sets the style of this format to new_style.
+    /// </summary>
+    format style(const class style &new_style);
+
+    /// <summary>
+    /// Returns the style of this format. If it has no style, an invalid_parameter
+    /// exception will be thrown.
+    /// </summary>
+    class style style();
+
+    /// <summary>
+    /// Returns the style of this format. If it has no style, an invalid_parameters
+    /// exception will be thrown.
+    /// </summary>
+    const class style style() const;
+
+private:
+    friend struct detail::stylesheet;
+    friend class detail::xlsx_producer;
+    friend class detail::xlsx_consumer;
+    friend class cell;
+
+    /// <summary>
+    /// Constructs a format from an impl pointer.
+    /// </summary>
+    format(detail::format_impl *d);
+
+    /// <summary>
+    /// The internal implementation of this format
+    /// </summary>
+    detail::format_impl *d_;
+};
+
+} // namespace xlnt

+ 274 - 0
xlnt/include/xlnt/styles/number_format.hpp

@@ -0,0 +1,274 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <cstdint>
+#include <string>
+
+#include <xlnt/xlnt_config.hpp>
+#include <xlnt/utils/optional.hpp>
+
+namespace xlnt {
+
+enum class calendar;
+
+/// <summary>
+/// Describes the number formatting applied to text and numbers within a certain cell.
+/// </summary>
+class XLNT_API number_format
+{
+public:
+    /// <summary>
+    /// Number format "General"
+    /// </summary>
+    static const number_format general();
+
+    /// <summary>
+    /// Number format "@"
+    /// </summary>
+    static const number_format text();
+
+    /// <summary>
+    /// Number format "0"
+    /// </summary>
+    static const number_format number();
+
+    /// <summary>
+    /// Number format "00"
+    /// </summary>
+    static const number_format number_00();
+
+    /// <summary>
+    /// Number format "#,##0.00"
+    /// </summary>
+    static const number_format number_comma_separated1();
+
+    /// <summary>
+    /// Number format "0%"
+    /// </summary>
+    static const number_format percentage();
+
+    /// <summary>
+    /// Number format "0.00%"
+    /// </summary>
+    static const number_format percentage_00();
+
+    /// <summary>
+    /// Number format "yyyy-mm-dd"
+    /// </summary>
+    static const number_format date_yyyymmdd2();
+
+    /// <summary>
+    /// Number format "yy-mm-dd"
+    /// </summary>
+    static const number_format date_yymmdd();
+
+    /// <summary>
+    /// Number format "dd/mm/yy"
+    /// </summary>
+    static const number_format date_ddmmyyyy();
+
+    /// <summary>
+    /// Number format "d/m/yy"
+    /// </summary>
+    static const number_format date_dmyslash();
+
+    /// <summary>
+    /// Number format "d-m-yy"
+    /// </summary>
+    static const number_format date_dmyminus();
+
+    /// <summary>
+    /// Number format "d-m"
+    /// </summary>
+    static const number_format date_dmminus();
+
+    /// <summary>
+    /// Number format "m-yy"
+    /// </summary>
+    static const number_format date_myminus();
+
+    /// <summary>
+    /// Number format "mm-dd-yy"
+    /// </summary>
+    static const number_format date_xlsx14();
+
+    /// <summary>
+    /// Number format "d-mmm-yy"
+    /// </summary>
+    static const number_format date_xlsx15();
+
+    /// <summary>
+    /// Number format "d-mmm"
+    /// </summary>
+    static const number_format date_xlsx16();
+
+    /// <summary>
+    /// Number format "mmm-yy"
+    /// </summary>
+    static const number_format date_xlsx17();
+
+    /// <summary>
+    /// Number format "m/d/yy h:mm"
+    /// </summary>
+    static const number_format date_xlsx22();
+
+    /// <summary>
+    /// Number format "yyyy-mm-dd h:mm:ss"
+    /// </summary>
+    static const number_format date_datetime();
+
+    /// <summary>
+    /// Number format "h:mm AM/PM"
+    /// </summary>
+    static const number_format date_time1();
+
+    /// <summary>
+    /// Number format "h:mm:ss AM/PM"
+    /// </summary>
+    static const number_format date_time2();
+
+    /// <summary>
+    /// Number format "h:mm"
+    /// </summary>
+    static const number_format date_time3();
+
+    /// <summary>
+    /// Number format "h:mm:ss"
+    /// </summary>
+    static const number_format date_time4();
+
+    /// <summary>
+    /// Number format "mm:ss"
+    /// </summary>
+    static const number_format date_time5();
+
+    /// <summary>
+    /// Number format "h:mm:ss"
+    /// </summary>
+    static const number_format date_time6();
+
+    /// <summary>
+    /// Returns true if the given format ID corresponds to a known builtin format.
+    /// </summary>
+    static bool is_builtin_format(std::size_t builtin_id);
+
+    /// <summary>
+    /// Returns the format with the given ID. Thows an invalid_parameter exception
+    /// if builtin_id is not a valid ID.
+    /// </summary>
+    static const number_format &from_builtin_id(std::size_t builtin_id);
+
+    /// <summary>
+    /// Constructs a default number_format equivalent to "General"
+    /// </summary>
+    number_format();
+
+    /// <summary>
+    /// Constructs a number format equivalent to that returned from number_format::from_builtin_id(builtin_id).
+    /// </summary>
+    number_format(std::size_t builtin_id);
+
+    /// <summary>
+    /// Constructs a number format from a code string. If the string matches a builtin ID,
+    /// its ID will also be set to match the builtin ID.
+    /// </summary>
+    number_format(const std::string &code);
+
+    /// <summary>
+    /// Constructs a number format from a code string and custom ID. Custom ID should generally
+    /// be >= 164.
+    /// </summary>
+    number_format(const std::string &code, std::size_t custom_id);
+
+    /// <summary>
+    /// Sets the format code of this number format to format_code.
+    /// </summary>
+    void format_string(const std::string &format_code);
+
+    /// <summary>
+    /// Sets the format code of this number format to format_code and the ID to custom_id.
+    /// </summary>
+    void format_string(const std::string &format_code, std::size_t custom_id);
+
+    /// <summary>
+    /// Returns the format code this number format uses.
+    /// </summary>
+    std::string format_string() const;
+
+    /// <summary>
+    /// Returns true if this number format has an ID.
+    /// </summary>
+    bool has_id() const;
+
+    /// <summary>
+    /// Sets the ID of this number format to id.
+    /// </summary>
+    void id(std::size_t id);
+
+    /// <summary>
+    /// Returns the ID of this format.
+    /// </summary>
+    std::size_t id() const;
+
+    /// <summary>
+    /// Returns text formatted according to this number format's format code.
+    /// </summary>
+    std::string format(const std::string &text) const;
+
+    /// <summary>
+    /// Returns number formatted according to this number format's format code
+    /// with the given base date.
+    /// </summary>
+    std::string format(double number, calendar base_date) const;
+
+    /// <summary>
+    /// Returns true if this format code returns a number formatted as a date.
+    /// </summary>
+    bool is_date_format() const;
+
+    /// <summary>
+    /// Returns true if this format is equivalent to other.
+    /// </summary>
+    bool operator==(const number_format &other) const;
+
+    /// <summary>
+    /// Returns true if this format is not equivalent to other.
+    /// </summary>
+    bool operator!=(const number_format &other) const;
+
+private:
+    /// <summary>
+    /// The optional ID
+    /// </summary>
+    optional<std::size_t> id_;
+
+    /// <summary>
+    /// The format code
+    /// </summary>
+    std::string format_string_;
+};
+
+} // namespace xlnt

+ 107 - 0
xlnt/include/xlnt/styles/protection.hpp

@@ -0,0 +1,107 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <cstddef>
+
+#include <xlnt/xlnt_config.hpp>
+#include <xlnt/utils/optional.hpp>
+
+namespace xlnt {
+
+/// <summary>
+/// Describes the protection style of a particular cell.
+/// </summary>
+class XLNT_API protection
+{
+public:
+    /// <summary>
+    /// Returns an unlocked and unhidden protection object.
+    /// </summary>
+    static protection unlocked_and_visible();
+
+    /// <summary>
+    /// Returns a locked and unhidden protection object.
+    /// </summary>
+    static protection locked_and_visible();
+
+    /// <summary>
+    /// Returns an unlocked and hidden protection object.
+    /// </summary>
+    static protection unlocked_and_hidden();
+
+    /// <summary>
+    /// Returns a locked and hidden protection object.
+    /// </summary>
+    static protection locked_and_hidden();
+
+    /// <summary>
+    /// Constructs a default unlocked unhidden protection object.
+    /// </summary>
+    protection();
+
+    /// <summary>
+    /// Returns true if cells using this protection should be locked.
+    /// </summary>
+    bool locked() const;
+
+    /// <summary>
+    /// Sets the locked state of the protection to locked and returns a reference to the protection.
+    /// </summary>
+    protection &locked(bool locked);
+
+    /// <summary>
+    /// Returns true if cells using this protection should be hidden.
+    /// </summary>
+    bool hidden() const;
+
+    /// <summary>
+    /// Sets the hidden state of the protection to hidden and returns a reference to the protection.
+    /// </summary>
+    protection &hidden(bool hidden);
+
+    /// <summary>
+    /// Returns true if this protections is equivalent to right.
+    /// </summary>
+    bool operator==(const protection &other) const;
+
+    /// <summary>
+    /// Returns true if this protection is not equivalent to right.
+    /// </summary>
+    bool operator!=(const protection &other) const;
+
+private:
+    /// <summary>
+    /// Whether the cell using this protection is locked or not
+    /// </summary>
+    bool locked_;
+
+    /// <summary>
+    /// Whether the cell using this protection is hidden or not
+    /// </summary>
+    bool hidden_;
+};
+
+} // namespace xlnt

+ 261 - 0
xlnt/include/xlnt/styles/style.hpp

@@ -0,0 +1,261 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <cstdint>
+#include <string>
+
+#include <xlnt/xlnt_config.hpp>
+#include <xlnt/utils/optional.hpp>
+
+namespace xlnt {
+
+class alignment;
+class border;
+class cell;
+class fill;
+class font;
+class number_format;
+class protection;
+
+namespace detail {
+
+struct style_impl;
+struct stylesheet;
+class xlsx_consumer;
+
+} // namespace detail
+
+/// <summary>
+/// Describes a style which has a name and can be applied to multiple individual
+/// formats. In Excel this is a "Cell Style".
+/// </summary>
+class XLNT_API style
+{
+public:
+    /// <summary>
+    /// Delete zero-argument constructor
+    /// </summary>
+    style() = delete;
+
+    /// <summary>
+    /// Default copy constructor. Constructs a style using the same PIMPL as other.
+    /// </summary>
+    style(const style &other) = default;
+
+    /// <summary>
+    /// Returns the name of this style.
+    /// </summary>
+    std::string name() const;
+
+    /// <summary>
+    /// Sets the name of this style to name.
+    /// </summary>
+    style name(const std::string &name);
+
+    /// <summary>
+    /// Returns true if this style is hidden.
+    /// </summary>
+    bool hidden() const;
+
+    /// <summary>
+    /// Sets the hidden state of this style to value. A hidden style will not
+    /// be shown in the list of selectable styles in the UI, but will still
+    /// apply its formatting to cells using it.
+    /// </summary>
+    style hidden(bool value);
+
+    /// <summary>
+    /// Returns true if this is a builtin style that has been customized and
+    /// should therefore be persisted in the workbook.
+    /// </summary>
+    bool custom_builtin() const;
+
+    /// <summary>
+    /// Returns the index of the builtin style that this style is an instance
+    /// of or is a customized version thereof. If style::builtin() is false,
+    /// this will throw an invalid_attribute exception.
+    /// </summary>
+    std::size_t builtin_id() const;
+
+    /// <summary>
+    /// Returns true if this is a builtin style.
+    /// </summary>
+    bool builtin() const;
+
+    // Formatting (xf) components
+
+    /// <summary>
+    /// Returns the alignment of this style.
+    /// </summary>
+    class alignment alignment() const;
+
+    /// <summary>
+    /// Returns true if the alignment of this style should be applied to cells
+    /// using it.
+    /// </summary>
+    bool alignment_applied() const;
+
+    /// <summary>
+    /// Sets the alignment of this style to new_alignment. Applied, which defaults
+    /// to true, determines whether the alignment should be enabled for cells using
+    /// this style.
+    /// </summary>
+    style alignment(const xlnt::alignment &new_alignment, optional<bool> applied = {});
+
+    /// <summary>
+    /// Returns the border of this style.
+    /// </summary>
+    class border border() const;
+
+    /// <summary>
+    /// Returns true if the border set for this style should be applied to cells using the style.
+    /// </summary>
+    bool border_applied() const;
+
+    /// <summary>
+    /// Sets the border of this style to new_border. Applied, which defaults
+    /// to true, determines whether the border should be enabled for cells using
+    /// this style.
+    /// </summary>
+    style border(const xlnt::border &new_border, optional<bool> applied = {});
+
+    /// <summary>
+    /// Returns the fill of this style.
+    /// </summary>
+    class fill fill() const;
+
+    /// <summary>
+    /// Returns true if the fill set for this style should be applied to cells using the style.
+    /// </summary>
+    bool fill_applied() const;
+
+    /// <summary>
+    /// Sets the fill of this style to new_fill. Applied, which defaults
+    /// to true, determines whether the border should be enabled for cells using
+    /// this style.
+    /// </summary>
+    style fill(const xlnt::fill &new_fill, optional<bool> applied = {});
+
+    /// <summary>
+    /// Returns the font of this style.
+    /// </summary>
+    class font font() const;
+
+    /// <summary>
+    /// Returns true if the font set for this style should be applied to cells using the style.
+    /// </summary>
+    bool font_applied() const;
+
+    /// <summary>
+    /// Sets the font of this style to new_font. Applied, which defaults
+    /// to true, determines whether the font should be enabled for cells using
+    /// this style.
+    /// </summary>
+    style font(const xlnt::font &new_font, optional<bool> applied = {});
+
+    /// <summary>
+    /// Returns the number_format of this style.
+    /// </summary>
+    class number_format number_format() const;
+
+    /// <summary>
+    /// Returns true if the number_format set for this style should be applied to cells using the style.
+    /// </summary>
+    bool number_format_applied() const;
+
+    /// <summary>
+    /// Sets the number format of this style to new_number_format. Applied, which defaults
+    /// to true, determines whether the number format should be enabled for cells using
+    /// this style.
+    /// </summary>
+    style number_format(const xlnt::number_format &new_number_format, optional<bool> applied = {});
+
+    /// <summary>
+    /// Returns the protection of this style.
+    /// </summary>
+    class protection protection() const;
+
+    /// <summary>
+    /// Returns true if the protection set for this style should be applied to cells using the style.
+    /// </summary>
+    bool protection_applied() const;
+
+    /// <summary>
+    /// Sets the border of this style to new_protection. Applied, which defaults
+    /// to true, determines whether the protection should be enabled for cells using
+    /// this style.
+    /// </summary>
+    style protection(const xlnt::protection &new_protection, optional<bool> applied = {});
+
+    /// <summary>
+    /// Returns true if the pivot table interface is enabled for this style.
+    /// </summary>
+    bool pivot_button() const;
+
+    /// <summary>
+    /// If show is true, a pivot table interface will be displayed for cells using
+    /// this style.
+    /// </summary>
+    void pivot_button(bool show);
+
+    /// <summary>
+    /// Returns true if this style should add a single-quote prefix for all text values.
+    /// </summary>
+    bool quote_prefix() const;
+
+    /// <summary>
+    /// If quote is true, enables a single-quote prefix for all text values in cells
+    /// using this style (e.g. "abc" will appear as "'abc"). The text will also not
+    /// be stored in sharedStrings when this is enabled.
+    /// </summary>
+    void quote_prefix(bool quote);
+
+    /// <summary>
+    /// Returns true if this style is equivalent to other.
+    /// </summary>
+    bool operator==(const style &other) const;
+
+    /// <summary>
+    /// Returns true if this style is not equivalent to other.
+    /// </summary>
+    bool operator!=(const style &other) const;
+
+private:
+    friend struct detail::stylesheet;
+    friend class detail::xlsx_consumer;
+
+    /// <summary>
+    /// Constructs a style from an impl pointer.
+    /// </summary>
+    style(detail::style_impl *d);
+
+    /// <summary>
+    /// The internal implementation of this style
+    /// </summary>
+    detail::style_impl *d_;
+};
+
+} // namespace xlnt

+ 40 - 0
xlnt/include/xlnt/utils/calendar.hpp

@@ -0,0 +1,40 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <xlnt/xlnt_config.hpp>
+
+namespace xlnt {
+
+/// <summary>
+/// An enumeration of possible base dates.
+/// Dates in Excel are stored as days since this base date.
+/// </summary>
+enum class XLNT_API calendar
+{
+    windows_1900,
+    mac_1904
+};
+
+} // namespace xlnt

+ 93 - 0
xlnt/include/xlnt/utils/date.hpp

@@ -0,0 +1,93 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <string>
+
+#include <xlnt/xlnt_config.hpp>
+#include <xlnt/utils/calendar.hpp>
+
+namespace xlnt {
+
+/// <summary>
+/// A date is a specific day specified in terms of a year, month, and day.
+/// It can also be initialized as a number of days since a base date
+/// using date::from_number.
+/// </summary>
+struct XLNT_API date
+{
+    /// <summary>
+    /// Return the current date according to the system time.
+    /// </summary>
+    static date today();
+
+    /// <summary>
+    /// Return a date by adding days_since_base_year to base_date.
+    /// This includes leap years.
+    /// </summary>
+    static date from_number(int days_since_base_year, calendar base_date);
+
+    /// <summary>
+    /// Constructs a data from a given year, month, and day.
+    /// </summary>
+    date(int year_, int month_, int day_);
+
+    /// <summary>
+    /// Return the number of days between this date and base_date.
+    /// </summary>
+    int to_number(calendar base_date) const;
+
+    /// <summary>
+    /// Calculates and returns the day of the week that this date represents in the range
+    /// 0 to 6 where 0 represents Sunday.
+    /// </summary>
+    int weekday() const;
+
+    /// <summary>
+    /// Return true if this date is equal to comparand.
+    /// </summary>
+    bool operator==(const date &comparand) const;
+
+    /// <summary>
+    /// Return true if this date is equal to comparand.
+    /// </summary>
+    bool operator!=(const date &comparand) const;
+
+    /// <summary>
+    /// The year
+    /// </summary>
+    int year;
+
+    /// <summary>
+    /// The month
+    /// </summary>
+    int month;
+
+    /// <summary>
+    /// The day
+    /// </summary>
+    int day;
+};
+
+} // namespace xlnt

+ 136 - 0
xlnt/include/xlnt/utils/datetime.hpp

@@ -0,0 +1,136 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <string>
+
+#include <xlnt/xlnt_config.hpp>
+#include <xlnt/utils/calendar.hpp>
+
+namespace xlnt {
+
+struct date;
+struct time;
+
+/// <summary>
+/// A datetime is a combination of a date and a time.
+/// </summary>
+struct XLNT_API datetime
+{
+    /// <summary>
+    /// Returns the current date and time according to the system time.
+    /// </summary>
+    static datetime now();
+
+    /// <summary>
+    /// Returns the current date and time according to the system time.
+    /// This is equivalent to datetime::now().
+    /// </summary>
+    static datetime today();
+
+    /// <summary>
+    /// Returns a datetime from number by converting the integer part into
+    /// a date and the fractional part into a time according to date::from_number
+    /// and time::from_number.
+    /// </summary>
+    static datetime from_number(double number, calendar base_date);
+
+    /// <summary>
+    /// Returns a datetime equivalent to the ISO-formatted string iso_string.
+    /// </summary>
+    static datetime from_iso_string(const std::string &iso_string);
+
+    /// <summary>
+    /// Constructs a datetime from a date and a time.
+    /// </summary>
+    datetime(const date &d, const time &t);
+
+    /// <summary>
+    /// Constructs a datetime from a year, month, and day plus optional hour, minute, second, and microsecond.
+    /// </summary>
+    datetime(int year_, int month_, int day_, int hour_ = 0, int minute_ = 0, int second_ = 0, int microsecond_ = 0);
+
+    /// <summary>
+    /// Returns a string represenation of this date and time.
+    /// </summary>
+    std::string to_string() const;
+
+    /// <summary>
+    /// Returns an ISO-formatted string representation of this date and time.
+    /// </summary>
+    std::string to_iso_string() const;
+
+    /// <summary>
+    /// Returns this datetime as a number of seconds since 1900 or 1904 (depending on base_date provided).
+    /// </summary>
+    double to_number(calendar base_date) const;
+
+    /// <summary>
+    /// Returns true if this datetime is equivalent to comparand.
+    /// </summary>
+    bool operator==(const datetime &comparand) const;
+
+    /// <summary>
+    /// Calculates and returns the day of the week that this date represents in the range
+    /// 0 to 6 where 0 represents Sunday.
+    /// </summary>
+    int weekday() const;
+
+    /// <summary>
+    /// The year
+    /// </summary>
+    int year;
+
+    /// <summary>
+    /// The month
+    /// </summary>
+    int month;
+
+    /// <summary>
+    /// The day
+    /// </summary>
+    int day;
+
+    /// <summary>
+    /// The hour
+    /// </summary>
+    int hour;
+
+    /// <summary>
+    /// The minute
+    /// </summary>
+    int minute;
+
+    /// <summary>
+    /// The second
+    /// </summary>
+    int second;
+
+    /// <summary>
+    /// The microsecond
+    /// </summary>
+    int microsecond;
+};
+
+} // namespace xlnt

+ 347 - 0
xlnt/include/xlnt/utils/exceptions.hpp

@@ -0,0 +1,347 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <cstdint>
+#include <stdexcept>
+
+#include <xlnt/xlnt_config.hpp>
+#include <xlnt/cell/index_types.hpp>
+
+namespace xlnt {
+
+/// <summary>
+/// Parent type of all custom exceptions thrown in this library.
+/// </summary>
+class XLNT_API exception : public std::runtime_error
+{
+public:
+    /// <summary>
+    /// Constructs an exception with a message. This message will be
+    /// returned by std::exception::what(), an inherited member of this class.
+    /// </summary>
+    explicit exception(const std::string &message);
+
+    /// <summary>
+    /// Default copy constructor.
+    /// </summary>
+    exception(const exception &) = default;
+
+    /// <summary>
+    /// Destructor
+    /// </summary>
+    ~exception() override;
+
+    /// <summary>
+    /// Sets the message after the xlnt::exception is constructed. This can show
+    /// more specific information than std::exception::what().
+    /// </summary>
+    void message(const std::string &message);
+
+    /// <summary>
+    /// Gets the message containing extra information.
+    /// </summary>
+    std::string message();
+
+private:
+    /// <summary>
+    /// The exception message
+    /// </summary>
+    std::string message_;
+};
+
+/// <summary>
+/// Exception for a bad parameter value
+/// </summary>
+class XLNT_API invalid_parameter : public exception
+{
+public:
+    /// <summary>
+    /// Default constructor.
+    /// </summary>
+    invalid_parameter();
+
+    /// <summary>
+    /// Default copy constructor.
+    /// </summary>
+    invalid_parameter(const invalid_parameter &) = default;
+
+    /// <summary>
+    /// Destructor
+    /// </summary>
+    ~invalid_parameter() override;
+};
+
+/// <summary>
+/// Exception for bad sheet names.
+/// </summary>
+class XLNT_API invalid_sheet_title : public exception
+{
+public:
+    /// <summary>
+    /// Default constructor.
+    /// </summary>
+    explicit invalid_sheet_title(const std::string &title);
+
+    /// <summary>
+    /// Default copy constructor.
+    /// </summary>
+    invalid_sheet_title(const invalid_sheet_title &) = default;
+
+    /// <summary>
+    /// Destructor
+    /// </summary>
+    ~invalid_sheet_title() override;
+};
+
+/// <summary>
+/// Exception for trying to open a non-XLSX file.
+/// </summary>
+class XLNT_API invalid_file : public exception
+{
+public:
+    /// <summary>
+    /// Constructs an invalid_file exception thrown when attempt to access
+    /// the given filename.
+    /// </summary>
+    explicit invalid_file(const std::string &filename);
+
+    /// <summary>
+    /// Default copy constructor.
+    /// </summary>
+    invalid_file(const invalid_file &) = default;
+
+    /// <summary>
+    /// Destructor
+    /// </summary>
+    ~invalid_file() override;
+};
+
+/// <summary>
+/// The data submitted which cannot be used directly in Excel files. It
+/// must be removed or escaped.
+/// </summary>
+class XLNT_API illegal_character : public exception
+{
+public:
+    /// <summary>
+    /// Constructs an illegal_character exception thrown as a result of the given character.
+    /// </summary>
+    explicit illegal_character(char c);
+
+    /// <summary>
+    /// Default copy constructor.
+    /// </summary>
+    illegal_character(const illegal_character &) = default;
+
+    /// <summary>
+    /// Destructor
+    /// </summary>
+    ~illegal_character() override;
+};
+
+/// <summary>
+/// Exception for any data type inconsistencies.
+/// </summary>
+class XLNT_API invalid_data_type : public exception
+{
+public:
+    /// <summary>
+    /// Default constructor.
+    /// </summary>
+    invalid_data_type();
+
+    /// <summary>
+    /// Default copy constructor.
+    /// </summary>
+    invalid_data_type(const invalid_data_type &) = default;
+
+    /// <summary>
+    /// Destructor
+    /// </summary>
+    ~invalid_data_type() override;
+};
+
+/// <summary>
+/// Exception for bad column indices in A1-style cell references.
+/// </summary>
+class XLNT_API invalid_column_index : public exception
+{
+public:
+    /// <summary>
+    /// Default constructor.
+    /// </summary>
+    invalid_column_index();
+
+    /// <summary>
+    /// Default copy constructor.
+    /// </summary>
+    invalid_column_index(const invalid_column_index &) = default;
+
+    /// <summary>
+    /// Destructor
+    /// </summary>
+    ~invalid_column_index() override;
+};
+
+/// <summary>
+/// Exception for converting between numeric and A1-style cell references.
+/// </summary>
+class XLNT_API invalid_cell_reference : public exception
+{
+public:
+    /// <summary>
+    /// Constructs an invalid_cell_reference exception for the given column and row.
+    /// </summary>
+    invalid_cell_reference(column_t column, row_t row);
+
+    /// <summary>
+    /// Constructs an invalid_cell_reference exception for the given string.
+    /// </summary>
+    explicit invalid_cell_reference(const std::string &reference_string);
+
+    /// <summary>
+    /// Default copy constructor.
+    /// </summary>
+    invalid_cell_reference(const invalid_cell_reference &) = default;
+
+    /// <summary>
+    /// Destructor
+    /// </summary>
+    ~invalid_cell_reference() override;
+};
+
+/// <summary>
+/// Exception when setting a class's attribute to an invalid value
+/// </summary>
+class XLNT_API invalid_attribute : public exception
+{
+public:
+    /// <summary>
+    /// Default constructor.
+    /// </summary>
+    invalid_attribute();
+
+    /// <summary>
+    /// Default copy constructor.
+    /// </summary>
+    invalid_attribute(const invalid_attribute &) = default;
+
+    /// <summary>
+    /// Destructor
+    /// </summary>
+    ~invalid_attribute() override;
+};
+
+/// <summary>
+/// Exception for a key that doesn't exist in a container
+/// </summary>
+class XLNT_API key_not_found : public exception
+{
+public:
+    /// <summary>
+    /// Default constructor.
+    /// </summary>
+    key_not_found();
+
+    /// <summary>
+    /// Default copy constructor.
+    /// </summary>
+    key_not_found(const key_not_found &) = default;
+
+    /// <summary>
+    /// Destructor
+    /// </summary>
+    ~key_not_found() override;
+};
+
+/// <summary>
+/// Exception for a workbook with no visible worksheets
+/// </summary>
+class XLNT_API no_visible_worksheets : public exception
+{
+public:
+    /// <summary>
+    /// Default constructor.
+    /// </summary>
+    no_visible_worksheets();
+
+    /// <summary>
+    /// Default copy constructor.
+    /// </summary>
+    no_visible_worksheets(const no_visible_worksheets &) = default;
+
+    /// <summary>
+    /// Destructor
+    /// </summary>
+    ~no_visible_worksheets() override;
+};
+
+/// <summary>
+/// Debug exception for a switch that fell through to the default case
+/// </summary>
+class XLNT_API unhandled_switch_case : public exception
+{
+public:
+    /// <summary>
+    /// Default constructor.
+    /// </summary>
+    unhandled_switch_case();
+
+    /// <summary>
+    /// Default copy constructor.
+    /// </summary>
+    unhandled_switch_case(const unhandled_switch_case &) = default;
+
+    /// <summary>
+    /// Destructor
+    /// </summary>
+    ~unhandled_switch_case() override;
+};
+
+/// <summary>
+/// Exception for attempting to use a feature which is not supported
+/// </summary>
+class XLNT_API unsupported : public exception
+{
+public:
+    /// <summary>
+    /// Constructs an unsupported exception with a message describing the unsupported
+    /// feature.
+    /// </summary>
+    explicit unsupported(const std::string &message);
+
+    /// <summary>
+    /// Default copy constructor.
+    /// </summary>
+    unsupported(const unsupported &) = default;
+
+    /// <summary>
+    /// Destructor
+    /// </summary>
+    ~unsupported() override;
+};
+
+} // namespace xlnt

+ 194 - 0
xlnt/include/xlnt/utils/numeric.hpp

@@ -0,0 +1,194 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <xlnt/xlnt_config.hpp>
+#include <algorithm>
+#include <cassert>
+#include <cmath>
+#include <cstddef>
+#include <limits>
+#include <sstream>
+#include <type_traits>
+
+#undef min
+#undef max
+
+namespace xlnt {
+namespace detail {
+
+/// <summary>
+/// constexpr abs
+/// </summary>
+template <typename Number>
+constexpr Number abs(Number val)
+{
+    return (val < Number{0}) ? -val : val;
+}
+
+/// <summary>
+/// constexpr max
+/// </summary>
+template <typename NumberL, typename NumberR>
+constexpr typename std::common_type<NumberL, NumberR>::type (max)(NumberL lval, NumberR rval)
+{
+    return (lval < rval) ? rval : lval;
+}
+
+/// <summary>
+/// constexpr min
+/// </summary>
+template <typename NumberL, typename NumberR>
+constexpr typename std::common_type<NumberL, NumberR>::type (min)(NumberL lval, NumberR rval)
+{
+    return (lval < rval) ? lval : rval;
+}
+
+/// <summary>
+/// Floating point equality requires a bit of fuzzing due to the imprecise nature of fp calculation
+/// References:
+/// - Several blogs/articles were referenced with the following being the most useful
+/// -- https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
+/// -- http://realtimecollisiondetection.net/blog/?p=89
+/// - Testing Frameworks {Catch2, Boost, Google}, primarily for selecting the default scale factor
+/// -- None of these even remotely agree
+/// </summary>
+template <typename EpsilonType = float, // the type to extract epsilon from
+    typename LNumber, typename RNumber> // parameter types (deduced)
+bool float_equals(const LNumber &lhs, const RNumber &rhs,
+    int epsilon_scale = 20) // scale the "fuzzy" equality. Higher value gives a more tolerant comparison
+{
+    // a type that lhs and rhs can agree on
+    using common_t = typename std::common_type<LNumber, RNumber>::type;
+    // asserts for sane usage
+    static_assert(std::is_floating_point<LNumber>::value || std::is_floating_point<RNumber>::value,
+        "Using this function with two integers is just wasting time. Use ==");
+    static_assert(std::numeric_limits<EpsilonType>::epsilon() < EpsilonType{1},
+        "epsilon >= 1.0 will cause all comparisons to return true");
+
+    // NANs always compare false with themselves
+    if (std::isnan(lhs) || std::isnan(rhs))
+    {
+        return false;
+    }
+    // epsilon type defaults to float because even if both args are a higher precision type
+    // either or both could have been promoted by prior operations
+    // if a higher precision is required, the template type can be changed
+    constexpr common_t epsilon = static_cast<common_t>(std::numeric_limits<EpsilonType>::epsilon());
+    // the "epsilon" then needs to be scaled into the comparison range
+    // epsilon for numeric_limits is valid when abs(x) <1.0, scaling only needs to be upwards
+    // in particular, this prevents a lhs of 0 from requiring an exact comparison
+    // additionally, a scale factor is applied.
+    common_t scaled_fuzz = epsilon_scale * epsilon * max(max(xlnt::detail::abs<common_t>(lhs),
+                                                             xlnt::detail::abs<common_t>(rhs)), // |max| of parameters.
+                               common_t{1}); // clamp
+    return ((lhs + scaled_fuzz) >= rhs) && ((rhs + scaled_fuzz) >= lhs);
+}
+
+class number_serialiser
+{
+    static constexpr int Excel_Digit_Precision = 15; //sf
+    bool should_convert_comma;
+
+    static void convert_comma_to_pt(char *buf, int len)
+    {
+        char *buf_end = buf + len;
+        char *decimal = std::find(buf, buf_end, ',');
+        if (decimal != buf_end)
+        {
+            *decimal = '.';
+        }
+    }
+
+    static void convert_pt_to_comma(char *buf, size_t len)
+    {
+        char *buf_end = buf + len;
+        char *decimal = std::find(buf, buf_end, '.');
+        if (decimal != buf_end)
+        {
+            *decimal = ',';
+        }
+    }
+
+public:
+    explicit number_serialiser()
+        : should_convert_comma(localeconv()->decimal_point[0] == ',')
+    {
+    }
+
+    // for printing to file.
+    // This matches the output format of excel irrespective of current locale
+    std::string serialise(double d) const
+    {
+        char buf[30];
+        int len = snprintf(buf, sizeof(buf), "%.15g", d);
+        if (should_convert_comma)
+        {
+            convert_comma_to_pt(buf, len);
+        }
+        return std::string(buf, static_cast<size_t>(len));
+    }
+
+    // replacement for std::to_string / s*printf("%f", ...)
+    // behaves same irrespective of locale
+    std::string serialise_short(double d) const
+    {
+        char buf[30];
+        int len = snprintf(buf, sizeof(buf), "%f", d);
+        if (should_convert_comma)
+        {
+            convert_comma_to_pt(buf, len);
+        }
+        return std::string(buf, static_cast<size_t>(len));
+    }
+
+    double deserialise(const std::string &s, ptrdiff_t *len_converted) const
+    {
+        assert(!s.empty());
+        assert(len_converted != nullptr);
+        char *end_of_convert;
+        if (!should_convert_comma)
+        {
+            double d = strtod(s.c_str(), &end_of_convert);
+            *len_converted = end_of_convert - s.c_str();
+            return d;
+        }
+        char buf[30];
+        assert(s.size() < sizeof(buf));
+        auto copy_end = std::copy(s.begin(), s.end(), buf);
+        convert_pt_to_comma(buf, static_cast<size_t>(copy_end - buf));
+        double d = strtod(buf, &end_of_convert);
+        *len_converted = end_of_convert - buf;
+        return d;
+    }
+
+    double deserialise(const std::string &s) const
+    {
+        ptrdiff_t ignore;
+        return deserialise(s, &ignore);
+    }
+};
+
+} // namespace detail
+} // namespace xlnt

+ 337 - 0
xlnt/include/xlnt/utils/optional.hpp

@@ -0,0 +1,337 @@
+// Copyright (c) 2016-2021 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, ARISING 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 "xlnt/utils/exceptions.hpp"
+#include "xlnt/utils/numeric.hpp"
+#include "xlnt/xlnt_config.hpp"
+#include <type_traits>
+
+namespace xlnt {
+
+/// <summary>
+/// Many settings in xlnt are allowed to not have a value set. This class
+/// encapsulates a value which may or may not be set. Memory is allocated
+/// within the optional class.
+/// </summary>
+template <typename T>
+class optional
+{
+#if ((defined(_MSC_VER) && _MSC_VER <= 1900) || (defined(__GNUC__) && __GNUC__ < 5))
+// Disable enhanced type checking on Visual Studio <= 2015 and GCC <5
+#define XLNT_NOEXCEPT_VALUE_COMPAT(...) (false)
+#else
+#define XLNT_NOEXCEPT_VALUE_COMPAT(...) (__VA_ARGS__)
+    using ctor_copy_T_noexcept = typename std::conditional<std::is_nothrow_copy_constructible<T>{}, std::true_type, std::false_type>::type;
+    using ctor_move_T_noexcept = typename std::conditional<std::is_nothrow_move_constructible<T>{}, std::true_type, std::false_type>::type;
+    using copy_ctor_noexcept = ctor_copy_T_noexcept;
+    using move_ctor_noexcept = ctor_move_T_noexcept;
+    using set_copy_noexcept_t = typename std::conditional<std::is_nothrow_copy_constructible<T>{} && std::is_nothrow_assignable<T, T>{}, std::true_type, std::false_type>::type;
+    using set_move_noexcept_t = typename std::conditional<std::is_nothrow_move_constructible<T>{} && std::is_nothrow_move_assignable<T>{}, std::true_type, std::false_type>::type;
+    using clear_noexcept_t = typename std::conditional<std::is_nothrow_destructible<T>{}, std::true_type, std::false_type>::type;
+#endif
+    /// <summary>
+    /// Default equality operation, just uses operator==
+    /// </summary>
+    template <typename U = T, typename std::enable_if<!std::is_floating_point<U>::value>::type * = nullptr>
+    constexpr bool compare_equal(const U &lhs, const U &rhs) const
+    {
+        return lhs == rhs;
+    }
+
+    /// <summary>
+    /// equality operation for floating point numbers. Provides "fuzzy" equality
+    /// </summary>
+    template <typename U = T, typename std::enable_if<std::is_floating_point<U>::value>::type * = nullptr>
+    constexpr bool compare_equal(const U &lhs, const U &rhs) const
+    {
+        return detail::float_equals(lhs, rhs);
+    }
+
+public:
+    /// <summary>
+    /// Default contructor. is_set() will be false initially.
+    /// </summary>
+    optional() noexcept
+        : has_value_(false)
+    {
+    }
+
+    /// <summary>
+    /// Constructs this optional with a value.
+    /// noexcept if T copy ctor is noexcept
+    /// </summary>
+    optional(const T &value) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(ctor_copy_T_noexcept{}))
+        : has_value_(true)
+    {
+        new (&storage_) T(value);
+    }
+
+    /// <summary>
+    /// Constructs this optional with a value.
+    /// noexcept if T move ctor is noexcept
+    /// </summary>
+    optional(T &&value) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(ctor_move_T_noexcept{}))
+        : has_value_(true)
+    {
+        new (&storage_) T(std::move(value));
+    }
+
+    /// <summary>
+    /// Copy constructs this optional from other
+    /// noexcept if T copy ctor is noexcept
+    /// </summary>
+    optional(const optional &other) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(copy_ctor_noexcept{}))
+        : has_value_(other.has_value_)
+    {
+        if (has_value_)
+        {
+            new (&storage_) T(other.value_ref());
+        }
+    }
+
+    /// <summary>
+    /// Move constructs this optional from other. Clears the value from other if set
+    /// noexcept if T move ctor is noexcept
+    /// </summary>
+    optional(optional &&other) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(move_ctor_noexcept{}))
+        : has_value_(other.has_value_)
+    {
+        if (has_value_)
+        {
+            new (&storage_) T(std::move(other.value_ref()));
+            other.clear();
+        }
+    }
+
+    /// <summary>
+    /// Copy assignment of this optional from other
+    /// noexcept if set and clear are noexcept for T&
+    /// </summary>
+    optional &operator=(const optional &other) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(set_copy_noexcept_t{} && clear_noexcept_t{}))
+    {
+        if (other.has_value_)
+        {
+            set(other.value_ref());
+        }
+        else
+        {
+            clear();
+        }
+        return *this;
+    }
+
+    /// <summary>
+    /// Move assignment of this optional from other
+    /// noexcept if set and clear are noexcept for T&&
+    /// </summary>
+    optional &operator=(optional &&other) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(set_move_noexcept_t{} && clear_noexcept_t{}))
+    {
+        if (other.has_value_)
+        {
+            set(std::move(other.value_ref()));
+            other.clear();
+        }
+        else
+        {
+            clear();
+        }
+        return *this;
+    }
+
+    /// <summary>
+    /// Destructor cleans up the T instance if set
+    /// </summary>
+    ~optional() noexcept // note:: unconditional because msvc freaks out otherwise
+    {
+        clear();
+    }
+
+    /// <summary>
+    /// Returns true if this object currently has a value set. This should
+    /// be called before accessing the value with optional::get().
+    /// </summary>
+    bool is_set() const noexcept
+    {
+        return has_value_;
+    }
+
+    /// <summary>
+    /// Copies the value into the stored value
+    /// </summary>
+    void set(const T &value) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(set_copy_noexcept_t{}))
+    {
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
+#endif
+        if (has_value_)
+        {
+            value_ref() = value;
+        }
+        else
+        {
+            new (&storage_) T(value);
+            has_value_ = true;
+        }
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+    }
+
+    /// <summary>
+    /// Moves the value into the stored value
+    /// </summary>
+    void set(T &&value) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(set_move_noexcept_t{}))
+    {
+        // note seperate overload for two reasons (as opposed to perfect forwarding)
+        // 1. have to deal with implicit conversions internally with perfect forwarding
+        // 2. have to deal with the noexcept specfiers for all the different variations
+        // overload is just far and away the simpler solution
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
+#endif
+        if (has_value_)
+        {
+            value_ref() = std::move(value);
+        }
+        else
+        {
+            new (&storage_) T(std::move(value));
+            has_value_ = true;
+        }
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+    }
+
+    /// <summary>
+    /// Assignment operator overload. Equivalent to setting the value using optional::set.
+    /// </summary>
+    optional &operator=(const T &rhs) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(set_copy_noexcept_t{}))
+    {
+        set(rhs);
+        return *this;
+    }
+
+    /// <summary>
+    /// Assignment operator overload. Equivalent to setting the value using optional::set.
+    /// </summary>
+    optional &operator=(T &&rhs) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(set_move_noexcept_t{}))
+    {
+        set(std::move(rhs));
+        return *this;
+    }
+
+    /// <summary>
+    /// After this is called, is_set() will return false until a new value is provided.
+    /// </summary>
+    void clear() noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(clear_noexcept_t{}))
+    {
+        if (has_value_)
+        {
+            reinterpret_cast<T *>(&storage_)->~T();
+        }
+        has_value_ = false;
+    }
+
+    /// <summary>
+    /// Gets the value. If no value has been initialized in this object,
+    /// an xlnt::invalid_attribute exception will be thrown.
+    /// </summary>
+    T &get()
+    {
+        if (!has_value_)
+        {
+            throw invalid_attribute();
+        }
+
+        return value_ref();
+    }
+
+    /// <summary>
+    /// Gets the value. If no value has been initialized in this object,
+    /// an xlnt::invalid_attribute exception will be thrown.
+    /// </summary>
+    const T &get() const
+    {
+        if (!has_value_)
+        {
+            throw invalid_attribute();
+        }
+
+        return value_ref();
+    }
+
+    /// <summary>
+    /// Returns true if neither this nor other have a value
+    /// or both have a value and those values are equal according to
+    /// their equality operator.
+    /// </summary>
+    bool operator==(const optional<T> &other) const noexcept
+    {
+        if (has_value_ != other.has_value_)
+        {
+            return false;
+        }
+        if (!has_value_)
+        {
+            return true;
+        }
+        // equality is overloaded to provide fuzzy equality when T is a fp number
+        return compare_equal(value_ref(), other.value_ref());
+    }
+
+    /// <summary>
+    /// Returns false if neither this nor other have a value
+    /// or both have a value and those values are equal according to
+    /// their equality operator.
+    /// </summary>
+    bool operator!=(const optional<T> &other) const noexcept
+    {
+        return !operator==(other);
+    }
+
+private:
+    // helpers for getting a T out of storage
+    T &value_ref() noexcept
+    {
+        return *reinterpret_cast<T *>(&storage_);
+    }
+
+    const T &value_ref() const noexcept
+    {
+        return *reinterpret_cast<const T *>(&storage_);
+    }
+
+    bool has_value_;
+    typename std::aligned_storage<sizeof(T), alignof(T)>::type storage_;
+};
+
+#ifdef XLNT_NOEXCEPT_VALUE_COMPAT
+#undef XLNT_NOEXCEPT_VALUE_COMPAT
+#endif
+
+} // namespace xlnt

+ 221 - 0
xlnt/include/xlnt/utils/path.hpp

@@ -0,0 +1,221 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <string>
+#include <utility>
+#include <vector>
+
+#include <xlnt/xlnt_config.hpp>
+
+namespace xlnt {
+
+/// <summary>
+/// Encapsulates a path that points to location in a filesystem.
+/// </summary>
+class XLNT_API path
+{
+public:
+    /// <summary>
+    /// The system-specific path separator character (e.g. '/' or '\').
+    /// </summary>
+    static char system_separator();
+
+    /// <summary>
+    /// Construct an empty path.
+    /// </summary>
+    path();
+
+    /// <summary>
+    /// Counstruct a path from a string representing the path.
+    /// </summary>
+    explicit path(const std::string &path_string);
+
+    /// <summary>
+    /// Construct a path from a string with an explicit directory seprator.
+    /// </summary>
+    path(const std::string &path_string, char sep);
+
+    // general attributes
+
+    /// <summary>
+    /// Return true iff this path doesn't begin with / (or a drive letter on Windows).
+    /// </summary>
+    bool is_relative() const;
+
+    /// <summary>
+    /// Return true iff path::is_relative() is false.
+    /// </summary>
+    bool is_absolute() const;
+
+    /// <summary>
+    /// Return true iff this path is the root directory.
+    /// </summary>
+    bool is_root() const;
+
+    /// <summary>
+    /// Return a new path that points to the directory containing the current path
+    /// Return the path unchanged if this path is the absolute or relative root.
+    /// </summary>
+    path parent() const;
+
+    /// <summary>
+    /// Return the last component of this path.
+    /// </summary>
+    std::string filename() const;
+
+    /// <summary>
+    /// Return the part of the path following the last dot in the filename.
+    /// </summary>
+    std::string extension() const;
+
+    /// <summary>
+    /// Return a pair of strings resulting from splitting the filename on the last dot.
+    /// </summary>
+    std::pair<std::string, std::string> split_extension() const;
+
+    // conversion
+
+    /// <summary>
+    /// Create a string representing this path separated by the provided
+    /// separator or the system-default separator if not provided.
+    /// </summary>
+    std::vector<std::string> split() const;
+
+    /// <summary>
+    /// Create a string representing this path separated by the provided
+    /// separator or the system-default separator if not provided.
+    /// </summary>
+    const std::string &string() const;
+
+#ifdef _MSC_VER
+    /// <summary>
+    /// Create a wstring representing this path separated by the provided
+    /// separator or the system-default separator if not provided.
+    /// </summary>
+    std::wstring wstring() const;
+#endif
+
+    /// <summary>
+    /// If this path is relative, append each component of this path
+    /// to base_path and return the resulting absolute path. Otherwise,
+    /// the the current path will be returned and base_path will be ignored.
+    /// </summary>
+    path resolve(const path &base_path) const;
+
+    /// <summary>
+    /// The inverse of path::resolve. Creates a relative path from an absolute
+    /// path by removing the common root between base_path and this path.
+    /// If the current path is already relative, return it unchanged.
+    /// </summary>
+    path relative_to(const path &base_path) const;
+
+    // filesystem attributes
+
+    /// <summary>
+    /// Return true iff the file or directory pointed to by this path
+    /// exists on the filesystem.
+    /// </summary>
+    bool exists() const;
+
+    /// <summary>
+    /// Return true if the file or directory pointed to by this path
+    /// is a directory.
+    /// </summary>
+    bool is_directory() const;
+
+    /// <summary>
+    /// Return true if the file or directory pointed to by this path
+    /// is a regular file.
+    /// </summary>
+    bool is_file() const;
+
+    // filesystem
+
+    /// <summary>
+    /// Open the file pointed to by this path and return a string containing
+    /// the files contents.
+    /// </summary>
+    std::string read_contents() const;
+
+    // mutators
+
+    /// <summary>
+    /// Append the provided part to this path and return the result.
+    /// </summary>
+    path append(const std::string &to_append) const;
+
+    /// <summary>
+    /// Append the provided part to this path and return the result.
+    /// </summary>
+    path append(const path &to_append) const;
+
+    /// <summary>
+    /// Returns true if left path is equal to right path.
+    /// </summary>
+    bool operator==(const path &other) const;
+
+    /// <summary>
+    /// Returns true if left path is equal to right path.
+    /// </summary>
+    bool operator!=(const path &other) const;
+
+private:
+    /// <summary>
+    /// Returns the character that separates directories in the path.
+    /// On POSIX style filesystems, this is always '/'.
+    /// On Windows, this is the character that separates the drive letter from
+    /// the rest of the path for absolute paths with a drive letter, '/' if the path
+    /// is absolute and starts with '/', and '/' or '\' for relative paths
+    /// depending on which splits the path into more directory components.
+    /// </summary>
+    char guess_separator() const;
+
+    /// <summary>
+    /// A string that represents this path.
+    /// </summary>
+    std::string internal_;
+};
+
+} // namespace xlnt
+
+namespace std {
+
+/// <summary>
+/// Template specialization to allow xlnt:path to be used as a key in a std container.
+/// </summary>
+template <>
+struct hash<xlnt::path>
+{
+    /// <summary>
+    /// Returns a hashed represenation of the given path.
+    /// </summary>
+    size_t operator()(const xlnt::path &p) const
+    {
+        static hash<string> hasher;
+        return hasher(p.string());
+    }
+};
+
+} // namespace std

+ 47 - 0
xlnt/include/xlnt/utils/scoped_enum_hash.hpp

@@ -0,0 +1,47 @@
+// Copyright (c) 2016-2021 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, ARISING 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 <utility> // for std::hash
+
+namespace xlnt {
+
+/// <summary>
+/// Allows a scoped enum (aka "enum class") to be used as a key
+/// in a std::unordered_map.
+/// </summary>
+template <typename Enum>
+struct scoped_enum_hash
+{
+    /// <summary>
+    /// Cast the enumeration e to a std::size_t and hash that value using std::hash.
+    /// </summary>
+    std::size_t operator()(Enum e) const
+    {
+        static std::hash<std::size_t> hasher;
+        return hasher(static_cast<std::size_t>(e));
+    }
+};
+
+} // namespace xlnt

+ 93 - 0
xlnt/include/xlnt/utils/time.hpp

@@ -0,0 +1,93 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <string>
+
+#include <xlnt/xlnt_config.hpp>
+
+namespace xlnt {
+
+/// <summary>
+/// A time is a specific time of the day specified in terms of an hour,
+/// minute, second, and microsecond (0-999999).
+/// It can also be initialized as a fraction of a day using time::from_number.
+/// </summary>
+struct XLNT_API time
+{
+    /// <summary>
+    /// Return the current time according to the system time.
+    /// </summary>
+    static time now();
+
+    /// <summary>
+    /// Return a time from a number representing a fraction of a day.
+    /// The integer part of number will be ignored.
+    /// 0.5 would return time(12, 0, 0, 0) or noon, halfway through the day.
+    /// </summary>
+    static time from_number(double number);
+
+    /// <summary>
+    /// Constructs a time object from an optional hour, minute, second, and microsecond.
+    /// </summary>
+    explicit time(int hour_ = 0, int minute_ = 0, int second_ = 0, int microsecond_ = 0);
+
+    /// <summary>
+    /// Constructs a time object from a string representing the time.
+    /// </summary>
+    explicit time(const std::string &time_string);
+
+    /// <summary>
+    /// Returns a numeric representation of the time in the range 0-1 where the value
+    /// is equal to the fraction of the day elapsed.
+    /// </summary>
+    double to_number() const;
+
+    /// <summary>
+    /// Returns true if this time is equivalent to comparand.
+    /// </summary>
+    bool operator==(const time &comparand) const;
+
+    /// <summary>
+    /// The hour
+    /// </summary>
+    int hour;
+
+    /// <summary>
+    /// The minute
+    /// </summary>
+    int minute;
+
+    /// <summary>
+    /// The second
+    /// </summary>
+    int second;
+
+    /// <summary>
+    /// The microsecond
+    /// </summary>
+    int microsecond;
+};
+
+} // namespace xlnt

+ 84 - 0
xlnt/include/xlnt/utils/timedelta.hpp

@@ -0,0 +1,84 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <string>
+
+#include <xlnt/xlnt_config.hpp>
+
+namespace xlnt {
+
+/// <summary>
+/// Represents a span of time between two datetimes. This is
+/// not fully supported yet throughout the library.
+/// </summary>
+struct XLNT_API timedelta
+{
+    /// <summary>
+    /// Returns a timedelta from a number representing the factional number of days elapsed.
+    /// </summary>
+    static timedelta from_number(double number);
+
+    /// <summary>
+    /// Constructs a timedelta equal to zero.
+    /// </summary>
+    timedelta();
+
+    /// <summary>
+    /// Constructs a timedelta from a number of days, hours, minutes, seconds, and microseconds.
+    /// </summary>
+    timedelta(int days_, int hours_, int minutes_, int seconds_, int microseconds_);
+
+    /// <summary>
+    /// Returns a numeric representation of this timedelta as a fractional number of days.
+    /// </summary>
+    double to_number() const;
+
+    /// <summary>
+    /// The days
+    /// </summary>
+    int days;
+
+    /// <summary>
+    /// The hours
+    /// </summary>
+    int hours;
+
+    /// <summary>
+    /// The minutes
+    /// </summary>
+    int minutes;
+
+    /// <summary>
+    /// The seconds
+    /// </summary>
+    int seconds;
+
+    /// <summary>
+    /// The microseconds
+    /// </summary>
+    int microseconds;
+};
+
+} // namespace xlnt

+ 195 - 0
xlnt/include/xlnt/utils/variant.hpp

@@ -0,0 +1,195 @@
+// Copyright (c) 2017-2021 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, ARISING 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 <string>
+#include <vector>
+
+#include <xlnt/xlnt_config.hpp>
+
+namespace xlnt {
+
+struct datetime;
+
+/// <summary>
+/// Represents an object that can have variable type.
+/// </summary>
+class XLNT_API variant
+{
+public:
+    // TODO: implement remaining types?
+
+    /// <summary>
+    /// The possible types a variant can hold.
+    /// </summary>
+    enum class type
+    {
+        vector,
+        //array,
+        //blob,
+        //oblob,
+        //empty,
+        null,
+        //i1,
+        //i2,
+        i4,
+        //i8,
+        //integer,
+        //ui1,
+        //ui2,
+        //ui4,
+        //ui8,
+        //uint,
+        //r4,
+        //r8,
+        //decimal,
+        lpstr, // TODO: how does this differ from lpwstr?
+        //lpwstr,
+        //bstr,
+        date,
+        //filetime,
+        boolean,
+        //cy,
+        //error,
+        //stream,
+        //ostream,
+        //storage,
+        //ostorage,
+        //vstream,
+        //clsid
+    };
+
+    /// <summary>
+    /// Default constructor. Creates a null-type variant.
+    /// </summary>
+    variant();
+
+    /// <summary>
+    /// Creates a string-type variant with the given value.
+    /// </summary>
+    variant(const std::string &value);
+
+    /// <summary>
+    /// Creates a string-type variant with the given value.
+    /// </summary>
+    variant(const char *value);
+
+    /// <summary>
+    /// Creates a i4-type variant with the given value.
+    /// </summary>
+    variant(std::int32_t value);
+
+    /// <summary>
+    /// Creates a bool-type variant with the given value.
+    /// </summary>
+    variant(bool value);
+
+    /// <summary>
+    /// Creates a date-type variant with the given value.
+    /// </summary>
+    variant(const datetime &value);
+
+    /// <summary>
+    /// Creates a vector_i4-type variant with the given value.
+    /// </summary>
+    variant(const std::initializer_list<std::int32_t> &value);
+
+    /// <summary>
+    /// Creates a vector_i4-type variant with the given value.
+    /// </summary>
+    variant(const std::vector<std::int32_t> &value);
+
+    /// <summary>
+    /// Creates a vector_string-type variant with the given value.
+    /// </summary>
+    variant(const std::initializer_list<const char *> &value);
+
+    /// <summary>
+    /// Creates a vector_string-type variant with the given value.
+    /// </summary>
+    variant(const std::vector<const char *> &value);
+
+    /// <summary>
+    /// Creates a vector_string-type variant with the given value.
+    /// </summary>
+    variant(const std::initializer_list<std::string> &value);
+
+    /// <summary>
+    /// Creates a vector_string-type variant with the given value.
+    /// </summary>
+    variant(const std::vector<std::string> &value);
+
+    /// <summary>
+    /// Creates a vector_variant-type variant with the given value.
+    /// </summary>
+    variant(const std::vector<variant> &value);
+
+    /// <summary>
+    /// Returns true if this variant is of type t.
+    /// </summary>
+    bool is(type t) const;
+
+    /// <summary>
+    /// Returns the value of this variant as type T. An exception will
+    /// be thrown if the types are not convertible.
+    /// </summary>
+    template <typename T>
+    T get() const;
+
+    /// <summary>
+    /// Returns the type of this variant.
+    /// </summary>
+    type value_type() const;
+
+    bool operator==(const variant &rhs) const;
+
+private:
+    type type_;
+    std::vector<variant> vector_value_;
+    std::int32_t i4_value_;
+    std::string lpstr_value_;
+};
+
+template <>
+bool variant::get() const;
+
+template <>
+std::int32_t variant::get() const;
+
+template <>
+std::string variant::get() const;
+
+template <>
+datetime variant::get() const;
+
+template <>
+std::vector<std::int32_t> variant::get() const;
+
+template <>
+std::vector<std::string> variant::get() const;
+
+template <>
+std::vector<variant> variant::get() const;
+
+} // namespace xlnt

+ 55 - 0
xlnt/include/xlnt/workbook/calculation_properties.hpp

@@ -0,0 +1,55 @@
+// Copyright (c) 2016-2021 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, ARISING 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 <xlnt/xlnt_config.hpp>
+
+namespace xlnt {
+
+/// <summary>
+/// Workbook file properties relating to calculations.
+/// </summary>
+class XLNT_API calculation_properties
+{
+public:
+    /// <summary>
+    /// The version of calculation engine used to calculate cell formula values.
+    /// If this is older than the version of the Excel calculation engine opening
+    /// the workbook, cell values will be recalculated.
+    /// </summary>
+    std::size_t calc_id = 0;
+
+    /// <summary>
+    /// If this is true, concurrent calculation will be enabled for the workbook.
+    /// </summary>
+    bool concurrent_calc = false;
+};
+
+inline bool operator==(const calculation_properties &lhs, const calculation_properties &rhs)
+{
+    return lhs.calc_id == rhs.calc_id
+        && lhs.concurrent_calc == rhs.concurrent_calc;
+}
+
+} // namespace xlnt

+ 99 - 0
xlnt/include/xlnt/workbook/document_security.hpp

@@ -0,0 +1,99 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <string>
+
+#include <xlnt/xlnt_config.hpp>
+
+namespace xlnt {
+
+/// <summary>
+/// Properties governing how the data in a workbook should be protected.
+/// These values can be ignored by consumers.
+/// </summary>
+class XLNT_API document_security
+{
+public:
+    /// <summary>
+    /// Holds data describing the verifier that locks revisions or a workbook.
+    /// </summary>
+    struct lock_verifier
+    {
+        /// <summary>
+        /// The algorithm used to create and verify this lock.
+        /// </summary>
+        std::string hash_algorithm;
+
+        /// <summary>
+        /// The initial salt combined with the password used to prevent rainbow table attacks
+        /// </summary>
+        std::string salt;
+
+        /// <summary>
+        /// The actual hash value represented as a string
+        /// </summary>
+        std::string hash;
+
+        /// <summary>
+        /// The number of times the hash should be applied to the password combined with the salt.
+        /// This allows the difficulty of the hash to be increased as computing power increases.
+        /// </summary>
+        std::size_t spin_count;
+    };
+
+    /// <summary>
+    /// Constructs a new document security object with default values.
+    /// </summary>
+    document_security() = default;
+
+    /// <summary>
+    /// If true, the workbook is locked for revisions.
+    /// </summary>
+    bool lock_revision = false;
+
+    /// <summary>
+    /// If true, worksheets can't be moved, renamed, (un)hidden, inserted, or deleted.
+    /// </summary>
+    bool lock_structure = false;
+
+    /// <summary>
+    /// If true, workbook windows will be opened at the same position with the same size
+    /// every time they are loaded.
+    /// </summary>
+    bool lock_windows = false;
+
+    /// <summary>
+    /// The settings to allow the revision lock to be removed.
+    /// </summary>
+    lock_verifier revision_lock;
+
+    /// <summary>
+    /// The settings to allow the structure and windows lock to be removed.
+    /// </summary>
+    lock_verifier workbook_lock;
+};
+
+} // namespace xlnt

+ 38 - 0
xlnt/include/xlnt/workbook/external_book.hpp

@@ -0,0 +1,38 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <xlnt/xlnt_config.hpp>
+
+namespace xlnt {
+
+/// <summary>
+/// A reference to an external workbook for use in formulae.
+/// </summary>
+class XLNT_API external_book
+{
+};
+
+} // namespace xlnt

+ 86 - 0
xlnt/include/xlnt/workbook/metadata_property.hpp

@@ -0,0 +1,86 @@
+// Copyright (c) 2017-2021 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, ARISING 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 <xlnt/xlnt_config.hpp>
+
+namespace xlnt {
+
+/// <summary>
+/// Every core property in a workbook must be one of these types.
+/// </summary>
+enum class core_property
+{
+    category,
+    content_status,
+    created,
+    creator,
+    description,
+    identifier,
+    keywords,
+    language,
+    last_modified_by,
+    last_printed,
+    modified,
+    revision,
+    subject,
+    title,
+    version
+};
+
+/// <summary>
+/// Every extended property in a workbook must be one of these types.
+/// </summary>
+enum class extended_property
+{
+    application,
+    app_version,
+    characters,
+    characters_with_spaces,
+    company,
+    dig_sig,
+    doc_security,
+    heading_pairs,
+    hidden_slides,
+    h_links,
+    hyperlink_base,
+    hyperlinks_changed,
+    lines,
+    links_up_to_date,
+    manager,
+    m_m_clips,
+    notes,
+    pages,
+    paragraphs,
+    presentation_format,
+    scale_crop,
+    shared_doc,
+    slides,
+    template_,
+    titles_of_parts,
+    total_time,
+    words
+};
+
+} // namespace xlnt

+ 93 - 0
xlnt/include/xlnt/workbook/named_range.hpp

@@ -0,0 +1,93 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <string>
+#include <vector>
+
+#include <xlnt/xlnt_config.hpp>
+
+namespace xlnt {
+
+class range_reference;
+class worksheet;
+
+/// <summary>
+/// A 2D range of cells in a worksheet that is referred to by name.
+/// ws->range("A1:B2") could be replaced by ws->range("range1")
+/// </summary>
+class XLNT_API named_range
+{
+public:
+    /// <summary>
+    /// Type alias for the combination of sheet and range this named_range points to.
+    /// </summary>
+    using target = std::pair<worksheet, range_reference>;
+
+    /// <summary>
+    /// Constructs a named range that has no name and has no targets.
+    /// </summary>
+    named_range();
+
+    /// <summary>
+    /// Constructs a named range by copying its name and targets from other.
+    /// </summary>
+    named_range(const named_range &other);
+
+    /// <summary>
+    /// Constructs a named range with the given name and targets.
+    /// </summary>
+    named_range(const std::string &name, const std::vector<target> &targets);
+
+    /// <summary>
+    /// Returns the name of this range.
+    /// </summary>
+    std::string name() const;
+
+    /// <summary>
+    /// Returns the set of targets of this named range as a vector.
+    /// </summary>
+    const std::vector<target> &targets() const;
+
+    /// <summary>
+    /// Assigns the name and targets of this named_range to that of other.
+    /// </summary>
+    named_range &operator=(const named_range &other);
+
+    bool operator==(const named_range &rhs) const;
+
+private:
+    /// <summary>
+    /// The name of this named range.
+    /// </summary>
+    std::string name_;
+
+    /// <summary>
+    /// The targets of this named range.
+    /// </summary>
+    std::vector<target> targets_;
+};
+
+} // namespace xlnt

+ 143 - 0
xlnt/include/xlnt/workbook/streaming_workbook_reader.hpp

@@ -0,0 +1,143 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <functional>
+#include <iostream>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <xlnt/xlnt_config.hpp>
+
+namespace xml {
+class parser;
+}
+
+namespace xlnt {
+
+class cell;
+template <typename T>
+class optional;
+class path;
+class workbook;
+class worksheet;
+
+namespace detail {
+class xlsx_consumer;
+}
+
+/// <summary>
+/// workbook is the container for all other parts of the document.
+/// </summary>
+class XLNT_API streaming_workbook_reader
+{
+public:
+    streaming_workbook_reader();
+    ~streaming_workbook_reader();
+
+    /// <summary>
+    /// Closes currently open read stream. This will be called automatically
+    /// by the destructor if it hasn't already been called manually.
+    /// </summary>
+    void close();
+
+    bool has_cell();
+
+    /// <summary>
+    /// Reads the next cell in the current worksheet and optionally returns it if
+    /// the last cell in the sheet has not yet been read.
+    /// </summary>
+    cell read_cell();
+
+    bool has_worksheet(const std::string &name);
+
+    /// <summary>
+    /// Beings reading of the next worksheet in the workbook and optionally
+    /// returns its title if the last worksheet has not yet been read.
+    /// </summary>
+    void begin_worksheet(const std::string &name);
+
+    /// <summary>
+    /// Ends reading of the current worksheet in the workbook and optionally
+    /// returns a worksheet object corresponding to the worksheet with the title
+    /// returned by begin_worksheet().
+    /// </summary>
+    worksheet end_worksheet();
+
+    /// <summary>
+    /// Interprets byte vector data as an XLSX file and sets the content of this
+    /// workbook to match that file.
+    /// </summary>
+    void open(const std::vector<std::uint8_t> &data);
+
+    /// <summary>
+    /// Interprets file with the given filename as an XLSX file and sets
+    /// the content of this workbook to match that file.
+    /// </summary>
+    void open(const std::string &filename);
+
+#ifdef _MSC_VER
+    /// <summary>
+    /// Interprets file with the given filename as an XLSX file and sets
+    /// the content of this workbook to match that file.
+    /// </summary>
+    void open(const std::wstring &filename);
+#endif
+
+    /// <summary>
+    /// Interprets file with the given filename as an XLSX file and sets the
+    /// content of this workbook to match that file.
+    /// </summary>
+    void open(const path &filename);
+
+    /// <summary>
+    /// Interprets data in stream as an XLSX file and sets the content of this
+    /// workbook to match that file.
+    /// </summary>
+    void open(std::istream &stream);
+
+    /// <summary>
+    /// Holds the given streambuf internally, creates a std::istream backed
+    /// by the given buffer, and calls open(std::istream &) with that stream.
+    /// </summary>
+    void open(std::unique_ptr<std::streambuf> &&buffer);
+
+    /// <summary>
+    /// Returns a vector of the titles of sheets in the workbook in order.
+    /// </summary>
+    std::vector<std::string> sheet_titles();
+
+private:
+    std::string worksheet_rel_id_;
+    std::unique_ptr<detail::xlsx_consumer> consumer_;
+    std::unique_ptr<workbook> workbook_;
+    std::unique_ptr<std::istream> stream_;
+    std::unique_ptr<std::streambuf> stream_buffer_;
+    std::unique_ptr<std::istream> part_stream_;
+    std::unique_ptr<std::streambuf> part_stream_buffer_;
+    std::unique_ptr<xml::parser> parser_;
+};
+
+} // namespace xlnt

+ 116 - 0
xlnt/include/xlnt/workbook/streaming_workbook_writer.hpp

@@ -0,0 +1,116 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <cstddef>
+#include <iterator>
+#include <string>
+#include <vector>
+
+#include <xlnt/xlnt_config.hpp>
+
+namespace xml {
+class serializer;
+}
+
+namespace xlnt {
+
+class cell;
+class cell_reference;
+class worksheet;
+
+namespace detail {
+class xlsx_producer;
+} // namespace detail
+
+/// <summary>
+/// workbook is the container for all other parts of the document.
+/// </summary>
+class XLNT_API streaming_workbook_writer
+{
+public:
+    streaming_workbook_writer();
+    ~streaming_workbook_writer();
+
+    /// <summary>
+    /// Finishes writing of the remaining contents of the workbook and closes
+    /// currently open write stream. This will be called automatically by the
+    /// destructor if it hasn't already been called manually.
+    /// </summary>
+    void close();
+
+    /// <summary>
+    /// Writes a cell to the currently active worksheet at the position given by
+    /// ref and with the given value. ref should be to the right of or below
+    /// the previously written cell.
+    /// </summary>
+    cell add_cell(const cell_reference &ref);
+
+    /// <summary>
+    /// Ends writing of data to the current sheet and begins writing a new sheet
+    /// with the given title.
+    /// </summary>
+    worksheet add_worksheet(const std::string &title);
+
+    /// <summary>
+    /// Serializes the workbook into an XLSX file and saves the bytes into
+    /// byte vector data.
+    /// </summary>
+    void open(std::vector<std::uint8_t> &data);
+
+    /// <summary>
+    /// Serializes the workbook into an XLSX file and saves the data into a file
+    /// named filename.
+    /// </summary>
+    void open(const std::string &filename);
+
+#ifdef _MSC_VER
+    /// <summary>
+    /// Serializes the workbook into an XLSX file and saves the data into a file
+    /// named filename.
+    /// </summary>
+    void open(const std::wstring &filename);
+#endif
+
+    /// <summary>
+    /// Serializes the workbook into an XLSX file and saves the data into a file
+    /// named filename.
+    /// </summary>
+    void open(const xlnt::path &filename);
+
+    /// <summary>
+    /// Serializes the workbook into an XLSX file and saves the data into stream.
+    /// </summary>
+    void open(std::ostream &stream);
+
+    std::unique_ptr<xlnt::detail::xlsx_producer> producer_;
+    std::unique_ptr<workbook> workbook_;
+    std::unique_ptr<std::ostream> stream_;
+    std::unique_ptr<std::streambuf> stream_buffer_;
+    std::unique_ptr<std::ostream> part_stream_;
+    std::unique_ptr<std::streambuf> part_stream_buffer_;
+    std::unique_ptr<xml::serializer> serializer_;
+};
+
+} // namespace xlnt

+ 43 - 0
xlnt/include/xlnt/workbook/theme.hpp

@@ -0,0 +1,43 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <xlnt/xlnt_config.hpp>
+
+namespace xlnt {
+
+/// <summary>
+/// A theme is a combination of fonts, colors, and effects.
+/// This isn't really supported yet.
+/// </summary>
+class XLNT_API theme
+{
+public:
+    bool operator==(const theme &) const
+    {
+        return true;
+    }
+};
+
+} // namespace xlnt

+ 932 - 0
xlnt/include/xlnt/workbook/workbook.hpp

@@ -0,0 +1,932 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <functional>
+#include <iterator>
+#include <map>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include <xlnt/xlnt_config.hpp>
+#include <xlnt/cell/rich_text.hpp>
+
+namespace xlnt {
+
+enum class calendar;
+enum class core_property;
+enum class extended_property;
+enum class relationship_type;
+
+class alignment;
+class border;
+class calculation_properties;
+class cell;
+class cell_style;
+class color;
+class const_worksheet_iterator;
+class fill;
+class font;
+class format;
+class rich_text;
+class manifest;
+class metadata_property;
+class named_range;
+class number_format;
+class path;
+class pattern_fill;
+class protection;
+class range;
+class range_reference;
+class relationship;
+class streaming_workbook_reader;
+class style;
+class style_serializer;
+class theme;
+class variant;
+class workbook_view;
+class worksheet;
+class worksheet_iterator;
+class zip_file;
+
+struct datetime;
+
+namespace detail {
+
+struct stylesheet;
+struct workbook_impl;
+class xlsx_consumer;
+class xlsx_producer;
+
+} // namespace detail
+
+/// <summary>
+/// workbook is the container for all other parts of the document.
+/// </summary>
+class XLNT_API workbook
+{
+public:
+    /// <summary>
+    /// typedef for the iterator used for iterating through this workbook
+    /// (non-const) in a range-based for loop.
+    /// </summary>
+    using iterator = worksheet_iterator;
+
+    /// <summary>
+    /// typedef for the iterator used for iterating through this workbook
+    /// (const) in a range-based for loop.
+    /// </summary>
+    using const_iterator = const_worksheet_iterator;
+
+    /// <summary>
+    /// typedef for the iterator used for iterating through this workbook
+    /// (non-const) in a range-based for loop in reverse order using
+    /// std::make_reverse_iterator.
+    /// </summary>
+    using reverse_iterator = std::reverse_iterator<iterator>;
+
+    /// <summary>
+    /// typedef for the iterator used for iterating through this workbook
+    /// (const) in a range-based for loop in reverse order using
+    /// std::make_reverse_iterator.
+    /// </summary>
+    using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+
+    /// <summary>
+    /// Constructs and returns an empty workbook similar to a default.
+    /// Excel workbook
+    /// </summary>
+    static workbook empty();
+
+    // Constructors
+
+    /// <summary>
+    /// Default constructor. Constructs a workbook containing a single empty
+    /// worksheet using workbook::empty().
+    /// </summary>
+    workbook();
+
+    /// <summary>
+    /// load the xlsx file at path
+    /// </summary>
+    workbook(const xlnt::path &file);
+
+    /// <summary>
+    /// load the encrpyted xlsx file at path
+    /// </summary>
+    workbook(const xlnt::path &file, const std::string &password);
+
+    /// <summary>
+    /// construct the workbook from any data stream where the data is the binary form of a workbook
+    /// </summary>
+    workbook(std::istream &data);
+
+    /// <summary>
+    /// construct the workbook from any data stream where the data is the binary form of an encrypted workbook
+    /// </summary>
+    workbook(std::istream &data, const std::string &password);
+
+    /// <summary>
+    /// Move constructor. Constructs a workbook from existing workbook, other.
+    /// </summary>
+    workbook(workbook &&other);
+
+    /// <summary>
+    /// Copy constructor. Constructs this workbook from existing workbook, other.
+    /// </summary>
+    workbook(const workbook &other);
+
+    /// <summary>
+    /// Destroys this workbook, deallocating all internal storage space. Any pimpl
+    /// wrapper classes (e.g. cell) pointing into this workbook will be invalid
+    /// after this is executed.
+    /// </summary>
+    ~workbook();
+
+    // Worksheets
+
+    /// <summary>
+    /// Creates and returns a sheet after the last sheet in this workbook.
+    /// </summary>
+    worksheet create_sheet();
+
+    /// <summary>
+    /// Creates and returns a sheet at the specified index.
+    /// </summary>
+    worksheet create_sheet(std::size_t index);
+
+    /// <summary>
+    /// TODO: This should be private...
+    /// </summary>
+    worksheet create_sheet_with_rel(const std::string &title, const relationship &rel);
+
+    /// <summary>
+    /// Creates and returns a new sheet after the last sheet initializing it
+    /// with all of the data from the provided worksheet.
+    /// </summary>
+    worksheet copy_sheet(worksheet worksheet);
+
+    /// <summary>
+    /// Creates and returns a new sheet at the specified index initializing it
+    /// with all of the data from the provided worksheet.
+    /// </summary>
+    worksheet copy_sheet(worksheet worksheet, std::size_t index);
+
+    /// <summary>
+    /// Returns the worksheet that is determined to be active. An active
+    /// sheet is that which is initially shown by the spreadsheet editor.
+    /// </summary>
+    worksheet active_sheet();
+
+    /// <summary>
+    /// Sets the worksheet that is determined to be active. An active
+    /// sheet is that which is initially shown by the spreadsheet editor.
+    /// </summary>
+    void active_sheet(std::size_t index);
+
+    /// <summary>
+    /// Returns the worksheet with the given name. This may throw an exception
+    /// if the sheet isn't found. Use workbook::contains(const std::string &)
+    /// to make sure the sheet exists before calling this method.
+    /// </summary>
+    worksheet sheet_by_title(const std::string &title);
+
+    /// <summary>
+    /// Returns the worksheet with the given name. This may throw an exception
+    /// if the sheet isn't found. Use workbook::contains(const std::string &)
+    /// to make sure the sheet exists before calling this method.
+    /// </summary>
+    const worksheet sheet_by_title(const std::string &title) const;
+
+    /// <summary>
+    /// Returns the worksheet at the given index. This will throw an exception
+    /// if index is greater than or equal to the number of sheets in this workbook.
+    /// </summary>
+    worksheet sheet_by_index(std::size_t index);
+
+    /// <summary>
+    /// Returns the worksheet at the given index. This will throw an exception
+    /// if index is greater than or equal to the number of sheets in this workbook.
+    /// </summary>
+    const worksheet sheet_by_index(std::size_t index) const;
+
+    /// <summary>
+    /// Returns the worksheet with a sheetId of id. Sheet IDs are arbitrary numbers
+    /// that uniquely identify a sheet. Most users won't need this.
+    /// </summary>
+    worksheet sheet_by_id(std::size_t id);
+
+    /// <summary>
+    /// Returns the worksheet with a sheetId of id. Sheet IDs are arbitrary numbers
+    /// that uniquely identify a sheet. Most users won't need this.
+    /// </summary>
+    const worksheet sheet_by_id(std::size_t id) const;
+
+    /// <summary>
+    /// Returns the hidden identifier of the worksheet at the given index.
+    /// This will throw an exception if index is greater than or equal to the
+    /// number of sheets in this workbook.
+    /// </summary>
+    bool sheet_hidden_by_index(std::size_t index) const;
+
+    /// <summary>
+    /// Returns true if this workbook contains a sheet with the given title.
+    /// </summary>
+    bool contains(const std::string &title) const;
+
+    /// <summary>
+    /// Returns the index of the given worksheet. The worksheet must be owned by this workbook.
+    /// </summary>
+    std::size_t index(worksheet worksheet);
+
+    // remove worksheets
+
+    /// <summary>
+    /// Removes the given worksheet from this workbook.
+    /// </summary>
+    void remove_sheet(worksheet worksheet);
+
+    /// <summary>
+    /// Sets the contents of this workbook to be equivalent to that of
+    /// a workbook returned by workbook::empty().
+    /// </summary>
+    void clear();
+
+    // iterators
+
+    /// <summary>
+    /// Returns an iterator to the first worksheet in this workbook.
+    /// </summary>
+    iterator begin();
+
+    /// <summary>
+    /// Returns an iterator to the worksheet following the last worksheet of the workbook.
+    /// This worksheet acts as a placeholder; attempting to access it will cause an
+    /// exception to be thrown.
+    /// </summary>
+    iterator end();
+
+    /// <summary>
+    /// Returns a const iterator to the first worksheet in this workbook.
+    /// </summary>
+    const_iterator begin() const;
+
+    /// <summary>
+    /// Returns a const iterator to the worksheet following the last worksheet of the workbook.
+    /// This worksheet acts as a placeholder; attempting to access it will cause an
+    /// exception to be thrown.
+    /// </summary>
+    const_iterator end() const;
+
+    /// <summary>
+    /// Returns an iterator to the first worksheet in this workbook.
+    /// </summary>
+    const_iterator cbegin() const;
+
+    /// <summary>
+    /// Returns a const iterator to the worksheet following the last worksheet of the workbook.
+    /// This worksheet acts as a placeholder; attempting to access it will cause an
+    /// exception to be thrown.
+    /// </summary>
+    const_iterator cend() const;
+
+    /// <summary>
+    /// Applies the function "f" to every non-empty cell in every worksheet in this workbook.
+    /// </summary>
+    void apply_to_cells(std::function<void(cell)> f);
+
+    /// <summary>
+    /// Returns a temporary vector containing the titles of each sheet in the order
+    /// of the sheets in the workbook.
+    /// </summary>
+    std::vector<std::string> sheet_titles() const;
+
+    /// <summary>
+    /// Returns the number of sheets in this workbook.
+    /// </summary>
+    std::size_t sheet_count() const;
+
+    // Metadata Properties
+
+    /// <summary>
+    /// Returns true if the workbook has the core property with the given name.
+    /// </summary>
+    bool has_core_property(xlnt::core_property type) const;
+
+    /// <summary>
+    /// Returns a vector of the type of each core property that is set to
+    /// a particular value in this workbook.
+    /// </summary>
+    std::vector<xlnt::core_property> core_properties() const;
+
+    /// <summary>
+    /// Returns the value of the given core property.
+    /// </summary>
+    variant core_property(xlnt::core_property type) const;
+
+    /// <summary>
+    /// Sets the given core property to the provided value.
+    /// </summary>
+    void core_property(xlnt::core_property type, const variant &value);
+
+    /// <summary>
+    /// Returns true if the workbook has the extended property with the given name.
+    /// </summary>
+    bool has_extended_property(xlnt::extended_property type) const;
+
+    /// <summary>
+    /// Returns a vector of the type of each extended property that is set to
+    /// a particular value in this workbook.
+    /// </summary>
+    std::vector<xlnt::extended_property> extended_properties() const;
+
+    /// <summary>
+    /// Returns the value of the given extended property.
+    /// </summary>
+    variant extended_property(xlnt::extended_property type) const;
+
+    /// <summary>
+    /// Sets the given extended property to the provided value.
+    /// </summary>
+    void extended_property(xlnt::extended_property type, const variant &value);
+
+    /// <summary>
+    /// Returns true if the workbook has the custom property with the given name.
+    /// </summary>
+    bool has_custom_property(const std::string &property_name) const;
+
+    /// <summary>
+    /// Returns a vector of the name of each custom property that is set to
+    /// a particular value in this workbook.
+    /// </summary>
+    std::vector<std::string> custom_properties() const;
+
+    /// <summary>
+    /// Returns the value of the given custom property.
+    /// </summary>
+    variant custom_property(const std::string &property_name) const;
+
+    /// <summary>
+    /// Creates a new custom property in this workbook and sets it to the provided value.
+    /// </summary>
+    void custom_property(const std::string &property_name, const variant &value);
+
+    /// <summary>
+    /// Returns the base date used by this workbook. This will generally be windows_1900
+    /// except on Apple based systems when it will default to mac_1904 unless otherwise
+    /// set via `void workbook::base_date(calendar base_date)`.
+    /// </summary>
+    calendar base_date() const;
+
+    /// <summary>
+    /// Sets the base date style of this workbook. This is the date and time that
+    /// a numeric value of 0 represents.
+    /// </summary>
+    void base_date(calendar base_date);
+
+    /// <summary>
+    /// Returns true if this workbook has had its title set.
+    /// </summary>
+    bool has_title() const;
+
+    /// <summary>
+    /// Returns the title of this workbook.
+    /// </summary>
+    std::string title() const;
+
+    /// <summary>
+    /// Sets the title of this workbook to title.
+    /// </summary>
+    void title(const std::string &title);
+
+    /// <summary>
+    /// Sets the absolute path of this workbook to path.
+    /// </summary>
+    void abs_path(const std::string &path);
+
+    /// <summary>
+    /// Sets the ArchID flags of this workbook to flags.
+    /// </summary>
+    void arch_id_flags(const std::size_t flags);
+
+    // Named Ranges
+
+    /// <summary>
+    /// Returns a vector of the named ranges in this workbook.
+    /// </summary>
+    std::vector<xlnt::named_range> named_ranges() const;
+
+    /// <summary>
+    /// Creates a new names range.
+    /// </summary>
+    void create_named_range(const std::string &name, worksheet worksheet, const range_reference &reference);
+
+    /// <summary>
+    /// Creates a new names range.
+    /// </summary>
+    void create_named_range(const std::string &name, worksheet worksheet, const std::string &reference_string);
+
+    /// <summary>
+    /// Returns true if a named range of the given name exists in the workbook.
+    /// </summary>
+    bool has_named_range(const std::string &name) const;
+
+    /// <summary>
+    /// Returns the named range with the given name.
+    /// </summary>
+    class range named_range(const std::string &name);
+
+    /// <summary>
+    /// Deletes the named range with the given name.
+    /// </summary>
+    void remove_named_range(const std::string &name);
+
+    // Serialization/Deserialization
+
+    /// <summary>
+    /// Serializes the workbook into an XLSX file and saves the bytes into
+    /// byte vector data.
+    /// </summary>
+    void save(std::vector<std::uint8_t> &data) const;
+
+    /// <summary>
+    /// Serializes the workbook into an XLSX file encrypted with the given password
+    /// and saves the bytes into byte vector data.
+    /// </summary>
+    void save(std::vector<std::uint8_t> &data, const std::string &password) const;
+
+    /// <summary>
+    /// Serializes the workbook into an XLSX file and saves the data into a file
+    /// named filename.
+    /// </summary>
+    void save(const std::string &filename) const;
+
+    /// <summary>
+    /// Serializes the workbook into an XLSX file encrypted with the given password
+    /// and loads the bytes into a file named filename.
+    /// </summary>
+    void save(const std::string &filename, const std::string &password) const;
+
+#ifdef _MSC_VER
+    /// <summary>
+    /// Serializes the workbook into an XLSX file and saves the data into a file
+    /// named filename.
+    /// </summary>
+    void save(const std::wstring &filename) const;
+
+    /// <summary>
+    /// Serializes the workbook into an XLSX file encrypted with the given password
+    /// and loads the bytes into a file named filename.
+    /// </summary>
+    void save(const std::wstring &filename, const std::string &password) const;
+#endif
+
+    /// <summary>
+    /// Serializes the workbook into an XLSX file and saves the data into a file
+    /// named filename.
+    /// </summary>
+    void save(const xlnt::path &filename) const;
+
+    /// <summary>
+    /// Serializes the workbook into an XLSX file encrypted with the given password
+    /// and loads the bytes into a file named filename.
+    /// </summary>
+    void save(const xlnt::path &filename, const std::string &password) const;
+
+    /// <summary>
+    /// Serializes the workbook into an XLSX file and saves the data into stream.
+    /// </summary>
+    void save(std::ostream &stream) const;
+
+    /// <summary>
+    /// Serializes the workbook into an XLSX file encrypted with the given password
+    /// and loads the bytes into the given stream.
+    /// </summary>
+    void save(std::ostream &stream, const std::string &password) const;
+
+    /// <summary>
+    /// Interprets byte vector data as an XLSX file and sets the content of this
+    /// workbook to match that file.
+    /// </summary>
+    void load(const std::vector<std::uint8_t> &data);
+
+    /// <summary>
+    /// Interprets byte vector data as an XLSX file encrypted with the
+    /// given password and sets the content of this workbook to match that file.
+    /// </summary>
+    void load(const std::vector<std::uint8_t> &data, const std::string &password);
+
+    /// <summary>
+    /// Interprets file with the given filename as an XLSX file and sets
+    /// the content of this workbook to match that file.
+    /// </summary>
+    void load(const std::string &filename);
+
+    /// <summary>
+    /// Interprets file with the given filename as an XLSX file encrypted with the
+    /// given password and sets the content of this workbook to match that file.
+    /// </summary>
+    void load(const std::string &filename, const std::string &password);
+
+#ifdef _MSC_VER
+    /// <summary>
+    /// Interprets file with the given filename as an XLSX file and sets
+    /// the content of this workbook to match that file.
+    /// </summary>
+    void load(const std::wstring &filename);
+
+    /// <summary>
+    /// Interprets file with the given filename as an XLSX file encrypted with the
+    /// given password and sets the content of this workbook to match that file.
+    /// </summary>
+    void load(const std::wstring &filename, const std::string &password);
+#endif
+
+    /// <summary>
+    /// Interprets file with the given filename as an XLSX file and sets the
+    /// content of this workbook to match that file.
+    /// </summary>
+    void load(const xlnt::path &filename);
+
+    /// <summary>
+    /// Interprets file with the given filename as an XLSX file encrypted with the
+    /// given password and sets the content of this workbook to match that file.
+    /// </summary>
+    void load(const xlnt::path &filename, const std::string &password);
+
+    /// <summary>
+    /// Interprets data in stream as an XLSX file and sets the content of this
+    /// workbook to match that file.
+    /// </summary>
+    void load(std::istream &stream);
+
+    /// <summary>
+    /// Interprets data in stream as an XLSX file encrypted with the given password
+    /// and sets the content of this workbook to match that file.
+    /// </summary>
+    void load(std::istream &stream, const std::string &password);
+
+    // View
+
+    /// <summary>
+    /// Returns true if this workbook has a view.
+    /// </summary>
+    bool has_view() const;
+
+    /// <summary>
+    /// Returns the view.
+    /// </summary>
+    workbook_view view() const;
+
+    /// <summary>
+    /// Sets the view to view.
+    /// </summary>
+    void view(const workbook_view &view);
+
+    // Properties
+
+    /// <summary>
+    /// Returns true if a code name has been set for this workbook.
+    /// </summary>
+    bool has_code_name() const;
+
+    /// <summary>
+    /// Returns the code name that was set for this workbook.
+    /// </summary>
+    std::string code_name() const;
+
+    /// <summary>
+    /// Sets the code name of this workbook to code_name.
+    /// </summary>
+    void code_name(const std::string &code_name);
+
+    /// <summary>
+    /// Returns true if this workbook has a file version.
+    /// </summary>
+    bool has_file_version() const;
+
+    /// <summary>
+    /// Returns the AppName workbook file property.
+    /// </summary>
+    std::string app_name() const;
+
+    /// <summary>
+    /// Returns the LastEdited workbook file property.
+    /// </summary>
+    std::size_t last_edited() const;
+
+    /// <summary>
+    /// Returns the LowestEdited workbook file property.
+    /// </summary>
+    std::size_t lowest_edited() const;
+
+    /// <summary>
+    /// Returns the RupBuild workbook file property.
+    /// </summary>
+    std::size_t rup_build() const;
+
+    // Theme
+
+    /// <summary>
+    /// Returns true if this workbook has a theme defined.
+    /// </summary>
+    bool has_theme() const;
+
+    /// <summary>
+    /// Returns a const reference to this workbook's theme.
+    /// </summary>
+    const xlnt::theme &theme() const;
+
+    /// <summary>
+    /// Sets the theme to value.
+    /// </summary>
+    void theme(const class theme &value);
+
+    // Formats
+
+    /// <summary>
+    /// Returns the cell format at the given index. The index is the position of
+    /// the format in xl/styles.xml.
+    /// </summary>
+    xlnt::format format(std::size_t format_index);
+
+    /// <summary>
+    /// Returns the cell format at the given index. The index is the position of
+    /// the format in xl/styles.xml.
+    /// </summary>
+    const xlnt::format format(std::size_t format_index) const;
+
+    /// <summary>
+    /// Creates a new format and returns it.
+    /// </summary>
+    xlnt::format create_format(bool default_format = false);
+
+    /// <summary>
+    /// Clear all cell-level formatting and formats from the styelsheet. This leaves
+    /// all other styling in place (e.g. named styles).
+    /// </summary>
+    void clear_formats();
+
+    // Styles
+
+    /// <summary>
+    /// Returns true if this workbook has a style with a name of name.
+    /// </summary>
+    bool has_style(const std::string &name) const;
+
+    /// <summary>
+    /// Returns the named style with the given name.
+    /// </summary>
+    class style style(const std::string &name);
+
+    /// <summary>
+    /// Returns the named style with the given name.
+    /// </summary>
+    const class style style(const std::string &name) const;
+
+    /// <summary>
+    /// Creates a new style and returns it.
+    /// </summary>
+    class style create_style(const std::string &name);
+
+    /// <summary>
+    /// Creates a new style and returns it.
+    /// </summary>
+    class style create_builtin_style(std::size_t builtin_id);
+
+    /// <summary>
+    /// Clear all named styles from cells and remove the styles from
+    /// from the styelsheet. This leaves all other styling in place
+    /// (e.g. cell formats).
+    /// </summary>
+    void clear_styles();
+
+    /// <summary>
+    /// Sets the default slicer style to the given value.
+    /// </summary>
+    void default_slicer_style(const std::string &value);
+
+    /// <summary>
+    /// Returns the default slicer style.
+    /// </summary>
+    std::string default_slicer_style() const;
+
+    /// <summary>
+    /// Enables knownFonts in stylesheet.
+    /// </summary>
+    void enable_known_fonts();
+
+    /// <summary>
+    /// Disables knownFonts in stylesheet.
+    /// </summary>
+    void disable_known_fonts();
+
+    /// <summary>
+    /// Returns true if knownFonts are enabled in the stylesheet.
+    /// </summary>
+    bool known_fonts_enabled() const;
+
+    // Manifest
+
+    /// <summary>
+    /// Returns a reference to the workbook's internal manifest.
+    /// </summary>
+    class manifest &manifest();
+
+    /// <summary>
+    /// Returns a reference to the workbook's internal manifest.
+    /// </summary>
+    const class manifest &manifest() const;
+
+    // shared strings
+
+    /// <summary>
+    /// Append a shared string to the shared string collection in this workbook.
+    /// This should not generally be called unless you know what you're doing.
+    /// If allow_duplicates is false and the string is already in the collection,
+    /// it will not be added. Returns the index of the added string.
+    /// </summary>
+    std::size_t add_shared_string(const rich_text &shared, bool allow_duplicates = false);
+
+    /// <summary>
+    /// Returns a reference to the shared string related to the specified index
+    /// </summary>
+    const rich_text &shared_strings(std::size_t index) const;
+
+    /// <summary>
+    /// Returns a reference to the shared strings being used by cells
+    /// in this workbook.
+    /// </summary>
+    std::vector<rich_text> &shared_strings();
+
+    /// <summary>
+    /// Returns a reference to the shared strings being used by cells
+    /// in this workbook.
+    /// </summary>
+    const std::vector<rich_text> &shared_strings() const;
+
+    // Thumbnail
+
+    /// <summary>
+    /// Sets the workbook's thumbnail to the given vector of bytes, thumbnail,
+    /// with the given extension (e.g. jpg) and content_type (e.g. image/jpeg).
+    /// </summary>
+    void thumbnail(const std::vector<std::uint8_t> &thumbnail,
+        const std::string &extension, const std::string &content_type);
+
+    /// <summary>
+    /// Returns a vector of bytes representing the workbook's thumbnail.
+    /// </summary>
+    const std::vector<std::uint8_t> &thumbnail() const;
+
+    /// <summary>
+    /// Returns stored binary data.
+    /// </summary>
+    const std::unordered_map<std::string, std::vector<std::uint8_t>>& binaries() const;
+
+    // Calculation properties
+
+    /// <summary>
+    /// Returns true if this workbook has any calculation properties set.
+    /// </summary>
+    bool has_calculation_properties() const;
+
+    /// <summary>
+    /// Returns the calculation properties used in this workbook.
+    /// </summary>
+    class calculation_properties calculation_properties() const;
+
+    /// <summary>
+    /// Sets the calculation properties of this workbook to props.
+    /// </summary>
+    void calculation_properties(const class calculation_properties &props);
+
+    // Operators
+
+    /// <summary>
+    /// Set the contents of this workbook to be equal to those of "other".
+    /// Other is passed as value to allow for copy-swap idiom.
+    /// </summary>
+    workbook &operator=(workbook other);
+
+    /// <summary>
+    /// Return the worksheet with a title of "name".
+    /// </summary>
+    worksheet operator[](const std::string &name);
+
+    /// <summary>
+    /// Return the worksheet at "index".
+    /// </summary>
+    worksheet operator[](std::size_t index);
+
+    /// <summary>
+    /// Return true if this workbook internal implementation points to the same
+    /// memory as rhs's.
+    /// </summary>
+    bool operator==(const workbook &rhs) const;
+
+    /// <summary>
+    /// Return true if this workbook internal implementation doesn't point to the same
+    /// memory as rhs's.
+    /// </summary>
+    bool operator!=(const workbook &rhs) const;
+
+private:
+    friend class streaming_workbook_reader;
+    friend class worksheet;
+    friend class detail::xlsx_consumer;
+    friend class detail::xlsx_producer;
+
+    /// <summary>
+    /// Private constructor. Constructs a workbook from an implementation pointer.
+    /// Used by static constructor to resolve circular construction dependency.
+    /// </summary>
+    workbook(detail::workbook_impl *impl);
+
+    /// <summary>
+    /// Returns a reference to the workbook implementation structure. Provides
+    /// a nicer interface than constantly dereferencing workbook::d_.
+    /// </summary>
+    detail::workbook_impl &impl();
+
+    /// <summary>
+    /// Returns a reference to the workbook implementation structure. Provides
+    /// a nicer interface than constantly dereferencing workbook::d_.
+    /// </summary>
+    const detail::workbook_impl &impl() const;
+
+    /// <summary>
+    /// Adds a package-level part of the given type to the manifest if it doesn't
+    /// already exist. The part will have a path and content type of the default
+    /// for that particular relationship type.
+    /// </summary>
+    void register_package_part(relationship_type type);
+
+    /// <summary>
+    /// Adds a workbook-level part of the given type to the manifest if it doesn't
+    /// already exist. The part will have a path and content type of the default
+    /// for that particular relationship type. It will be a relationship target
+    /// of this workbook.
+    /// </summary>
+    void register_workbook_part(relationship_type type);
+
+    /// <summary>
+    /// Adds a worksheet-level part of the given type to the manifest if it doesn't
+    /// already exist. The part will have a path and content type of the default
+    /// for that particular relationship type. It will be a relationship target
+    /// of the given worksheet, ws.
+    /// </summary>
+    void register_worksheet_part(worksheet ws, relationship_type type);
+
+    /// <summary>
+    /// Removes calcChain part from manifest if no formulae remain in workbook.
+    /// </summary>
+    void garbage_collect_formulae();
+
+    /// <summary>
+    /// Update extended workbook properties titlesOfParts and headingPairs when sheets change.
+    /// </summary>
+    void update_sheet_properties();
+
+    /// <summary>
+    /// Swaps the data held in this workbook with workbook other.
+    /// </summary>
+    void swap(workbook &other);
+
+    /// <summary>
+    /// Sheet 1 should be rId1, sheet 2 should be rId2, etc.
+    /// </summary>
+    void reorder_relationships();
+
+    /// <summary>
+    /// An opaque pointer to a structure that holds all of the data relating to this workbook.
+    /// </summary>
+    std::unique_ptr<detail::workbook_impl> d_;
+};
+
+} // namespace xlnt

+ 118 - 0
xlnt/include/xlnt/workbook/workbook_view.hpp

@@ -0,0 +1,118 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <cstddef>
+
+#include <xlnt/xlnt_config.hpp>
+#include <xlnt/utils/optional.hpp>
+
+namespace xlnt {
+
+/// <summary>
+/// A workbook can be opened in multiple windows with different views.
+/// This class represents a particular view used by one window.
+/// </summary>
+class XLNT_API workbook_view
+{
+public:
+    /// <summary>
+    /// If true, dates will be grouped when presenting the user with filtering options.
+    /// </summary>
+    bool auto_filter_date_grouping = true;
+
+    /// <summary>
+    /// If true, the view will be minimized.
+    /// </summary>
+    bool minimized = false;
+
+    /// <summary>
+    /// If true, the horizontal scroll bar will be displayed.
+    /// </summary>
+    bool show_horizontal_scroll = true;
+
+    /// <summary>
+    /// If true, the sheet tabs will be displayed.
+    /// </summary>
+    bool show_sheet_tabs = true;
+
+    /// <summary>
+    /// If true, the vertical scroll bar will be displayed.
+    /// </summary>
+    bool show_vertical_scroll = true;
+
+    /// <summary>
+    /// If true, the workbook window will be visible.
+    /// </summary>
+    bool visible = true;
+
+    /// <summary>
+    /// The optional index to the active sheet in this view.
+    /// </summary>
+    optional<std::size_t> active_tab;
+
+    /// <summary>
+    /// The optional index to the first sheet in this view.
+    /// </summary>
+    optional<std::size_t> first_sheet;
+
+    /// <summary>
+    /// The optional ratio between the tabs bar and the horizontal scroll bar.
+    /// </summary>
+    optional<std::size_t> tab_ratio;
+
+    /// <summary>
+    /// The width of the workbook window in twips.
+    /// </summary>
+    optional<std::size_t> window_width;
+
+    /// <summary>
+    /// The height of the workbook window in twips.
+    /// </summary>
+    optional<std::size_t> window_height;
+
+    /// <summary>
+    /// The distance of the workbook window from the left side of the screen in twips.
+    /// </summary>
+    optional<int> x_window;
+
+    /// <summary>
+    /// The distance of the workbook window from the top of the screen in twips.
+    /// </summary>
+    optional<int> y_window;
+};
+
+inline bool operator==(const workbook_view &lhs, const workbook_view &rhs)
+{
+    return lhs.active_tab == rhs.active_tab
+        && lhs.auto_filter_date_grouping == rhs.auto_filter_date_grouping
+        && lhs.first_sheet == rhs.first_sheet
+        && lhs.minimized == rhs.minimized
+        && lhs.show_horizontal_scroll == rhs.show_horizontal_scroll
+        && lhs.show_sheet_tabs == rhs.show_sheet_tabs
+        && lhs.show_vertical_scroll == rhs.show_vertical_scroll
+        && lhs.tab_ratio == rhs.tab_ratio
+        && lhs.visible == rhs.visible;
+}
+
+} // namespace xlnt

+ 253 - 0
xlnt/include/xlnt/workbook/worksheet_iterator.hpp

@@ -0,0 +1,253 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <cstddef>
+#include <iterator>
+
+#include <xlnt/xlnt_config.hpp>
+
+namespace xlnt {
+
+class workbook;
+class worksheet;
+
+// Note: There are const and non-const implementations of this iterator
+// because one needs to point at a const workbook and the other needs
+// to point at a non-const workbook stored as a member variable, respectively.
+
+/// <summary>
+/// An iterator which is used to iterate over the worksheets in a workbook.
+/// </summary>
+class XLNT_API worksheet_iterator
+{
+public:
+    /// <summary>
+    /// iterator tags required for use with standard algorithms and adapters
+    /// </summary>
+    using iterator_category = std::bidirectional_iterator_tag;
+    using value_type = worksheet;
+    using difference_type = std::ptrdiff_t;
+    using pointer = worksheet *;
+    using reference = worksheet; // intentionally value
+
+    /// <summary>
+    /// Default Constructs a worksheet iterator
+    /// </summary>
+    worksheet_iterator() = default;
+
+    /// <summary>
+    /// Constructs a worksheet iterator from a workbook and sheet index.
+    /// </summary>
+    worksheet_iterator(workbook &wb, std::size_t index);
+
+    /// <summary>
+    /// Copy constructs a worksheet iterator from another iterator.
+    /// </summary>
+    worksheet_iterator(const worksheet_iterator &) = default;
+
+    /// <summary>
+    /// Copy assigns the iterator so that it points to the same worksheet in the same workbook.
+    /// </summary>
+    worksheet_iterator &operator=(const worksheet_iterator &) = default;
+
+    /// <summary>
+    /// Move constructs a worksheet iterator from a temporary iterator.
+    /// </summary>
+    worksheet_iterator(worksheet_iterator &&) = default;
+
+    /// <summary>
+    /// Move assign the iterator from a temporary iterator
+    /// </summary>
+    worksheet_iterator &operator=(worksheet_iterator &&) = default;
+
+    /// <summary>
+    /// Default destructor
+    /// </summary>
+    ~worksheet_iterator() = default;
+
+    /// <summary>
+    /// Dereferences the iterator to return the worksheet it is pointing to.
+    /// If the iterator points to one-past-the-end of the workbook, an invalid_parameter
+    /// exception will be thrown.
+    /// </summary>
+    reference operator*();
+
+    /// <summary>
+    /// Dereferences the iterator to return the worksheet it is pointing to.
+    /// If the iterator points to one-past-the-end of the workbook, an invalid_parameter
+    /// exception will be thrown.
+    /// </summary>
+    const reference operator*() const;
+
+    /// <summary>
+    /// Returns true if this iterator points to the same worksheet as comparand.
+    /// </summary>
+    bool operator==(const worksheet_iterator &comparand) const;
+
+    /// <summary>
+    /// Returns true if this iterator doesn't point to the same worksheet as comparand.
+    /// </summary>
+    bool operator!=(const worksheet_iterator &comparand) const;
+
+    /// <summary>
+    /// Post-increment the iterator's internal workseet index. Returns a copy of the
+    /// iterator as it was before being incremented.
+    /// </summary>
+    worksheet_iterator operator++(int);
+
+    /// <summary>
+    /// Pre-increment the iterator's internal workseet index. Returns a refernce
+    /// to the same iterator.
+    /// </summary>
+    worksheet_iterator &operator++();
+
+    /// <summary>
+    /// Post-decrement the iterator's internal workseet index. Returns a copy of the
+    /// iterator as it was before being incremented.
+    /// </summary>
+    worksheet_iterator operator--(int);
+
+    /// <summary>
+    /// Pre-decrement the iterator's internal workseet index. Returns a refernce
+    /// to the same iterator.
+    /// </summary>
+    worksheet_iterator &operator--();
+
+private:
+    /// <summary>
+    /// The target workbook of this iterator.
+    /// </summary>
+    workbook *wb_ = nullptr;
+
+    /// <summary>
+    /// The index of the worksheet in wb_ this iterator is currently pointing to.
+    /// </summary>
+    std::size_t index_ = 0;
+};
+
+/// <summary>
+/// An iterator which is used to iterate over the worksheets in a const workbook.
+/// </summary>
+class XLNT_API const_worksheet_iterator
+{
+public:
+    /// <summary>
+    /// iterator tags required for use with standard algorithms and adapters
+    /// </summary>
+    using iterator_category = std::bidirectional_iterator_tag;
+    using value_type = const worksheet;
+    using difference_type = std::ptrdiff_t;
+    using pointer = const worksheet *;
+    using reference = const worksheet; // intentionally value
+
+    /// <summary>
+    /// Default Constructs a worksheet iterator
+    /// </summary>
+    const_worksheet_iterator() = default;
+
+    /// <summary>
+    /// Constructs a worksheet iterator from a workbook and sheet index.
+    /// </summary>
+    const_worksheet_iterator(const workbook &wb, std::size_t index);
+
+    /// <summary>
+    /// Copy constructs a worksheet iterator from another iterator.
+    /// </summary>
+    const_worksheet_iterator(const const_worksheet_iterator &) = default;
+
+    /// <summary>
+    /// Copy assigns the iterator so that it points to the same worksheet in the same workbook.
+    /// </summary>
+    const_worksheet_iterator &operator=(const const_worksheet_iterator &) = default;
+
+    /// <summary>
+    /// Move constructs a worksheet iterator from a temporary iterator.
+    /// </summary>
+    const_worksheet_iterator(const_worksheet_iterator &&) = default;
+
+    /// <summary>
+    /// Move assigns the iterator from a temporary iterator
+    /// </summary>
+    const_worksheet_iterator &operator=(const_worksheet_iterator &&) = default;
+
+    /// <summary>
+    /// Default destructor
+    /// </summary>
+    ~const_worksheet_iterator() = default;
+
+    /// <summary>
+    /// Dereferences the iterator to return the worksheet it is pointing to.
+    /// If the iterator points to one-past-the-end of the workbook, an invalid_parameter
+    /// exception will be thrown.
+    /// </summary>
+    const reference operator*() const;
+
+    /// <summary>
+    /// Returns true if this iterator points to the same worksheet as comparand.
+    /// </summary>
+    bool operator==(const const_worksheet_iterator &comparand) const;
+
+    /// <summary>
+    /// Returns true if this iterator doesn't point to the same worksheet as comparand.
+    /// </summary>
+    bool operator!=(const const_worksheet_iterator &comparand) const;
+
+    /// <summary>
+    /// Post-increment the iterator's internal workseet index. Returns a copy of the
+    /// iterator as it was before being incremented.
+    /// </summary>
+    const_worksheet_iterator operator++(int);
+
+    /// <summary>
+    /// Pre-increment the iterator's internal workseet index. Returns a refernce
+    /// to the same iterator.
+    /// </summary>
+    const_worksheet_iterator &operator++();
+
+    /// <summary>
+    /// Post-decrement the iterator's internal workseet index. Returns a copy of the
+    /// iterator as it was before being incremented.
+    /// </summary>
+    const_worksheet_iterator operator--(int);
+
+    /// <summary>
+    /// Pre-decrement the iterator's internal workseet index. Returns a refernce
+    /// to the same iterator.
+    /// </summary>
+    const_worksheet_iterator &operator--();
+
+private:
+    /// <summary>
+    /// The target workbook of this iterator.
+    /// </summary>
+    const workbook *wb_ = nullptr;
+
+    /// <summary>
+    /// The index of the worksheet in wb_ this iterator is currently pointing to.
+    /// </summary>
+    std::size_t index_ = 0;
+};
+
+} // namespace xlnt

+ 317 - 0
xlnt/include/xlnt/worksheet/cell_iterator.hpp

@@ -0,0 +1,317 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <cstddef> // std::ptrdiff_t
+#include <iterator>
+
+#include <xlnt/xlnt_config.hpp>
+#include <xlnt/cell/cell_reference.hpp>
+#include <xlnt/worksheet/major_order.hpp>
+#include <xlnt/worksheet/range_reference.hpp>
+#include <xlnt/worksheet/worksheet.hpp>
+
+namespace xlnt {
+
+enum class major_order;
+
+class cell;
+class cell_reference;
+class range_reference;
+
+/// <summary>
+/// A cell iterator iterates over a 1D range by row or by column.
+/// </summary>
+class XLNT_API cell_iterator
+{
+public:
+    /// <summary>
+    /// iterator tags required for use with standard algorithms and adapters
+    /// </summary>
+    using iterator_category = std::bidirectional_iterator_tag;
+    using value_type = cell;
+    using difference_type = std::ptrdiff_t;
+    using pointer = cell *;
+    using reference = cell; // intentionally value
+
+    /// <summary>
+    /// Default constructs a cell_iterator
+    /// </summary>
+    cell_iterator() = default;
+
+    /// <summary>
+    /// Constructs a cell_iterator from a worksheet, range, and iteration settings.
+    /// </summary>
+    cell_iterator(worksheet ws, const cell_reference &start_cell,
+        const range_reference &limits, major_order order, bool skip_null, bool wrap);
+
+    /// <summary>
+    /// Constructs a cell_iterator as a copy of an existing cell_iterator.
+    /// </summary>
+    cell_iterator(const cell_iterator &) = default;
+
+    /// <summary>
+    /// Assigns this iterator to match the data in rhs.
+    /// </summary>
+    cell_iterator &operator=(const cell_iterator &) = default;
+
+    /// <summary>
+    /// Constructs a cell_iterator by moving from a cell_iterator temporary
+    /// </summary>
+    cell_iterator(cell_iterator &&) = default;
+
+    /// <summary>
+    /// Assigns this iterator to from a cell_iterator temporary
+    /// </summary>
+    cell_iterator &operator=(cell_iterator &&) = default;
+
+    /// <summary>
+    /// destructor for const_cell_iterator
+    /// </summary>
+    ~cell_iterator() = default;
+
+    /// <summary>
+    /// Dereferences this iterator to return the cell it points to.
+    /// </summary>
+    reference operator*();
+
+    /// <summary>
+    /// Dereferences this iterator to return the cell it points to.
+    /// </summary>
+    const reference operator*() const;
+
+    /// <summary>
+    /// Returns true if this iterator is equivalent to other.
+    /// </summary>
+    bool operator==(const cell_iterator &other) const;
+
+    /// <summary>
+    /// Returns true if this iterator isn't equivalent to other.
+    /// </summary>
+    bool operator!=(const cell_iterator &other) const;
+
+    /// <summary>
+    /// Pre-decrements the iterator to point to the previous cell and
+    /// returns a reference to the iterator.
+    /// </summary>
+    cell_iterator &operator--();
+
+    /// <summary>
+    /// Post-decrements the iterator to point to the previous cell and
+    /// return a copy of the iterator before the decrement.
+    /// </summary>
+    cell_iterator operator--(int);
+
+    /// <summary>
+    /// Pre-increments the iterator to point to the previous cell and
+    /// returns a reference to the iterator.
+    /// </summary>
+    cell_iterator &operator++();
+
+    /// <summary>
+    /// Post-increments the iterator to point to the previous cell and
+    /// return a copy of the iterator before the decrement.
+    /// </summary>
+    cell_iterator operator++(int);
+
+    /// <summary>
+    /// When iterating over a range that doesn't ignore null cells, operator*()
+    /// will throw when trying to access the cells that are null. This method
+    /// checks the existence of a cell.
+    /// </summary>
+    bool has_value() const;
+
+private:
+    /// <summary>
+    /// If true, cells that don't exist in the worksheet will be skipped during iteration.
+    /// </summary>
+    bool skip_null_ = false;
+
+    /// <summary>
+    /// If true, when on the last column, the cursor will continue to the next row
+    /// (and vice versa when iterating in column-major order) until reaching the
+    /// bottom right corner of the range.
+    /// </summary>
+    bool wrap_ = false;
+
+    /// <summary>
+    /// The order this iterator will move, by column or by row. Note that
+    /// this has the opposite meaning as in a range_iterator because after
+    /// getting a row-major range_iterator, the row-major cell_iterator will
+    /// iterate over a column and vice versa.
+    /// </summary>
+    major_order order_ = major_order::column;
+
+    /// <summary>
+    /// The worksheet this iterator will return cells from.
+    /// </summary>
+    worksheet ws_;
+
+    /// <summary>
+    /// The current cell the iterator points to
+    /// </summary>
+    cell_reference cursor_;
+
+    /// <summary>
+    /// The range of cells this iterator is restricted to
+    /// </summary>
+    range_reference bounds_;
+};
+
+/// <summary>
+/// A cell iterator iterates over a 1D range by row or by column.
+/// </summary>
+class XLNT_API const_cell_iterator
+{
+public:
+    /// <summary>
+    /// iterator tags required for use with standard algorithms and adapters
+    /// </summary>
+    using iterator_category = std::bidirectional_iterator_tag;
+    using value_type = const cell;
+    using difference_type = std::ptrdiff_t;
+    using pointer = const cell *;
+    using reference = const cell; // intentionally value
+
+    /// <summary>
+    /// Default constructs a cell_iterator
+    /// </summary>
+    const_cell_iterator() = default;
+
+    /// <summary>
+    /// Constructs a cell_iterator from a worksheet, range, and iteration settings.
+    /// </summary>
+    const_cell_iterator(worksheet ws, const cell_reference &start_cell,
+        const range_reference &limits, major_order order, bool skip_null, bool wrap);
+
+    /// <summary>
+    /// Constructs a const_cell_iterator as a copy of an existing cell_iterator.
+    /// </summary>
+    const_cell_iterator(const const_cell_iterator &) = default;
+
+    /// <summary>
+    /// Assigns this iterator to match the data in rhs.
+    /// </summary>
+    const_cell_iterator &operator=(const const_cell_iterator &) = default;
+
+    /// <summary>
+    /// Constructs a const_cell_iterator by moving from a const_cell_iterator temporary
+    /// </summary>
+    const_cell_iterator(const_cell_iterator &&) = default;
+
+    /// <summary>
+    /// Assigns this iterator to from a const_cell_iterator temporary
+    /// </summary>
+    const_cell_iterator &operator=(const_cell_iterator &&) = default;
+
+    /// <summary>
+    /// destructor for const_cell_iterator
+    /// </summary>
+    ~const_cell_iterator() = default;
+
+    /// <summary>
+    /// Dereferences this iterator to return the cell it points to.
+    /// </summary>
+    const reference operator*() const;
+
+    /// <summary>
+    /// Returns true if this iterator is equivalent to other.
+    /// </summary>
+    bool operator==(const const_cell_iterator &other) const;
+
+    /// <summary>
+    /// Returns true if this iterator isn't equivalent to other.
+    /// </summary>
+    bool operator!=(const const_cell_iterator &other) const;
+
+    /// <summary>
+    /// Pre-decrements the iterator to point to the previous cell and
+    /// returns a reference to the iterator.
+    /// </summary>
+    const_cell_iterator &operator--();
+
+    /// <summary>
+    /// Post-decrements the iterator to point to the previous cell and
+    /// return a copy of the iterator before the decrement.
+    /// </summary>
+    const_cell_iterator operator--(int);
+
+    /// <summary>
+    /// Pre-increments the iterator to point to the previous cell and
+    /// returns a reference to the iterator.
+    /// </summary>
+    const_cell_iterator &operator++();
+
+    /// <summary>
+    /// Post-increments the iterator to point to the previous cell and
+    /// return a copy of the iterator before the decrement.
+    /// </summary>
+    const_cell_iterator operator++(int);
+
+    /// <summary>
+    /// When iterating over a range that doesn't ignore null cells, operator*()
+    /// will throw when trying to access the cells that are null. This method
+    /// checks the existence of a cell.
+    /// </summary>
+    bool has_value() const;
+
+private:
+    /// <summary>
+    /// If true, cells that don't exist in the worksheet will be skipped during iteration.
+    /// </summary>
+    bool skip_null_ = false;
+
+    /// <summary>
+    /// If true, when on the last column, the cursor will continue to the next row
+    /// (and vice versa when iterating in column-major order) until reaching the
+    /// bottom right corner of the range.
+    /// </summary>
+    bool wrap_ = false;
+
+    /// <summary>
+    /// The order this iterator will move, by column or by row. Note that
+    /// this has the opposite meaning as in a range_iterator because after
+    /// getting a row-major range_iterator, the row-major cell_iterator will
+    /// iterate over a column and vice versa.
+    /// </summary>
+    major_order order_ = major_order::column;
+
+    /// <summary>
+    /// The worksheet this iterator will return cells from.
+    /// </summary>
+    worksheet ws_;
+
+    /// <summary>
+    /// The current cell the iterator points to
+    /// </summary>
+    cell_reference cursor_;
+
+    /// <summary>
+    /// The range of cells this iterator is restricted to
+    /// </summary>
+    range_reference bounds_;
+};
+
+} // namespace xlnt

+ 216 - 0
xlnt/include/xlnt/worksheet/cell_vector.hpp

@@ -0,0 +1,216 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <iterator>
+
+#include <xlnt/xlnt_config.hpp>
+#include <xlnt/cell/cell_reference.hpp>
+#include <xlnt/worksheet/cell_iterator.hpp>
+#include <xlnt/worksheet/major_order.hpp>
+#include <xlnt/worksheet/range_reference.hpp>
+#include <xlnt/worksheet/worksheet.hpp>
+
+namespace xlnt {
+
+class cell;
+class cell_iterator;
+class const_cell_iterator;
+class range_reference;
+
+/// <summary>
+/// A cell vector is a linear (1D) range of cells, either vertical or horizontal
+/// depending on the major order specified in the constructor.
+/// </summary>
+class XLNT_API cell_vector
+{
+public:
+    /// <summary>
+    /// Iterate over cells in a cell_vector with an iterator of this type.
+    /// </summary>
+    using iterator = cell_iterator;
+
+    /// <summary>
+    /// Iterate over const cells in a const cell_vector with an iterator of this type.
+    /// </summary>
+    using const_iterator = const_cell_iterator;
+
+    /// <summary>
+    /// Iterate over cells in a cell_vector in reverse oreder with an iterator
+    /// of this type.
+    /// </summary>
+    using reverse_iterator = std::reverse_iterator<iterator>;
+
+    /// <summary>
+    /// Iterate over const cells in a const cell_vector in reverse order with
+    /// an iterator of this type.
+    /// </summary>
+    using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+
+    /// <summary>
+    /// Constructs a cell vector pointing to a given range in a given worksheet.
+    /// order determines whether this vector is a row or a column. If skip_null is
+    /// true, iterating over this vector will skip empty cells.
+    /// </summary>
+    cell_vector(worksheet ws, const cell_reference &cursor,
+        const range_reference &ref, major_order order, bool skip_null, bool wrap);
+
+    /// <summary>
+    /// Returns true if every cell in this vector is null (i.e. the cell doesn't exist in the worksheet).
+    /// </summary>
+    bool empty() const;
+
+    /// <summary>
+    /// Returns the first cell in this vector.
+    /// </summary>
+    cell front();
+
+    /// <summary>
+    /// Returns the first cell in this vector.
+    /// </summary>
+    const cell front() const;
+
+    /// <summary>
+    /// Returns the last cell in this vector.
+    /// </summary>
+    cell back();
+
+    /// <summary>
+    /// Returns the last cell in this vector.
+    /// </summary>
+    const cell back() const;
+
+    /// <summary>
+    /// Returns the distance between the first and last cells in this vector.
+    /// </summary>
+    std::size_t length() const;
+
+    /// <summary>
+    /// Returns an iterator to the first cell in this vector.
+    /// </summary>
+    iterator begin();
+
+    /// <summary>
+    /// Returns an iterator to a cell one-past-the-end of this vector.
+    /// </summary>
+    iterator end();
+
+    /// <summary>
+    /// Returns an iterator to the first cell in this vector.
+    /// </summary>
+    const_iterator begin() const;
+
+    /// <summary>
+    /// Returns an iterator to the first cell in this vector.
+    /// </summary>
+    const_iterator cbegin() const;
+
+    /// <summary>
+    /// Returns an iterator to a cell one-past-the-end of this vector.
+    /// </summary>
+    const_iterator end() const;
+
+    /// <summary>
+    /// Returns an iterator to a cell one-past-the-end of this vector.
+    /// </summary>
+    const_iterator cend() const;
+
+    /// <summary>
+    /// Returns a reverse iterator to the first cell of this reversed vector.
+    /// </summary>
+    reverse_iterator rbegin();
+
+    /// <summary>
+    /// Returns a reverse iterator to to a cell one-past-the-end of this reversed vector.
+    /// </summary>
+    reverse_iterator rend();
+
+    /// <summary>
+    /// Returns a reverse iterator to the first cell of this reversed vector.
+    /// </summary>
+    const_reverse_iterator rbegin() const;
+
+    /// <summary>
+    /// Returns a reverse iterator to to a cell one-past-the-end of this reversed vector.
+    /// </summary>
+    const_reverse_iterator rend() const;
+
+    /// <summary>
+    /// Returns a reverse iterator to the first cell of this reversed vector.
+    /// </summary>
+    const_reverse_iterator crbegin() const;
+
+    /// <summary>
+    /// Returns a reverse iterator to to a cell one-past-the-end of this reversed vector.
+    /// </summary>
+    const_reverse_iterator crend() const;
+
+    /// <summary>
+    /// Returns the cell column_index distance away from the first cell in this vector.
+    /// </summary>
+    cell operator[](std::size_t column_index);
+
+    /// <summary>
+    /// Returns the cell column_index distance away from the first cell in this vector.
+    /// </summary>
+    const cell operator[](std::size_t column_index) const;
+
+private:
+    /// <summary>
+    /// The worksheet this vector points to cells from
+    /// </summary>
+    worksheet ws_;
+
+    /// <summary>
+    /// The reference of the first cell in this vector
+    /// </summary>
+    cell_reference cursor_;
+
+    /// <summary>
+    /// The range of cells this vector can point to
+    /// </summary>
+    range_reference bounds_;
+
+    /// <summary>
+    /// The direction that iteration over this vector will move. Note that
+    /// this has the opposite meaning as in a range_iterator because after
+    /// getting a row-major range_iterator, the row-major cell_iterator will
+    /// iterate over a column and vice versa.
+    /// </summary>
+    major_order order_;
+
+    /// <summary>
+    /// If true, cells that don't exist in the worksheet will be skipped during iteration.
+    /// </summary>
+    bool skip_null_;
+
+    /// <summary>
+    /// If true, when on the last column, the cursor will continue to the next row
+    /// (and vice versa when iterating in column-major order) until reaching the
+    /// bottom right corner of the range.
+    /// </summary>
+    bool wrap_;
+};
+
+} // namespace xlnt

+ 75 - 0
xlnt/include/xlnt/worksheet/column_properties.hpp

@@ -0,0 +1,75 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <xlnt/xlnt_config.hpp>
+
+namespace xlnt {
+
+/// <summary>
+/// Properties applied to a column in a worksheet.
+/// Columns can have a size and a style.
+/// </summary>
+class XLNT_API column_properties
+{
+public:
+    /// <summary>
+    /// The optional width of the column
+    /// </summary>
+    optional<double> width;
+
+    /// <summary>
+    /// If true, this is a custom width
+    /// </summary>
+    bool custom_width = false;
+
+    /// <summary>
+    /// The style index of this column. This shouldn't be used since style indices
+    /// aren't supposed to be used directly in xlnt. (TODO)
+    /// </summary>
+    optional<std::size_t> style;
+
+    /// <summary>
+    /// Is this column sized to fit its content as best it can
+    /// serialise if true
+    /// </summary>
+    bool best_fit = false;
+
+    /// <summary>
+    /// If true, this column will be hidden
+    /// </summary>
+    bool hidden = false;
+};
+
+inline bool operator==(const column_properties &lhs, const column_properties &rhs)
+{
+    return lhs.width == rhs.width
+        && lhs.custom_width == rhs.custom_width
+        && lhs.style == rhs.style
+        && lhs.best_fit == rhs.best_fit
+        && lhs.hidden == rhs.hidden;
+}
+
+} // namespace xlnt

+ 329 - 0
xlnt/include/xlnt/worksheet/header_footer.hpp

@@ -0,0 +1,329 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <cstdint>
+#include <string>
+#include <unordered_map>
+
+#include <xlnt/xlnt_config.hpp>
+#include <xlnt/cell/rich_text.hpp>
+#include <xlnt/utils/scoped_enum_hash.hpp>
+
+namespace xlnt {
+
+/// <summary>
+/// Represents the header and footer of a sheet in a workbook.
+/// </summary>
+class XLNT_API header_footer
+{
+public:
+    /// <summary>
+    /// Enumerates the three possible locations of a header or footer.
+    /// </summary>
+    enum class location
+    {
+        left,
+        center,
+        right
+    };
+
+    // General Properties
+
+    /// <summary>
+    /// True if any text has been added for a header at any location on any page.
+    /// </summary>
+    bool has_header() const;
+
+    /// <summary>
+    /// True if any text has been added for a footer at any location on any page.
+    /// </summary>
+    bool has_footer() const;
+
+    /// <summary>
+    /// True if headers and footers should align to the page margins.
+    /// </summary>
+    bool align_with_margins() const;
+
+    /// <summary>
+    /// Set to true if headers and footers should align to the page margins.
+    /// Set to false if headers and footers should align to the edge of the page.
+    /// </summary>
+    header_footer &align_with_margins(bool align);
+
+    /// <summary>
+    /// True if headers and footers differ based on page number.
+    /// </summary>
+    bool different_odd_even() const;
+
+    /// <summary>
+    /// True if headers and footers are different on the first page.
+    /// </summary>
+    bool different_first() const;
+
+    /// <summary>
+    /// True if headers and footers should scale to match the worksheet.
+    /// </summary>
+    bool scale_with_doc() const;
+
+    /// <summary>
+    /// Set to true if headers and footers should scale to match the worksheet.
+    /// </summary>
+    header_footer &scale_with_doc(bool scale);
+
+    // Normal Header
+
+    /// <summary>
+    /// True if any text has been added at the given location on any page.
+    /// </summary>
+    bool has_header(location where) const;
+
+    /// <summary>
+    /// Remove all headers from all pages.
+    /// </summary>
+    void clear_header();
+
+    /// <summary>
+    /// Remove header at the given location on any page.
+    /// </summary>
+    void clear_header(location where);
+
+    /// <summary>
+    /// Add a header at the given location with the given text.
+    /// </summary>
+    header_footer &header(location where, const std::string &text);
+
+    /// <summary>
+    /// Add a header at the given location with the given text.
+    /// </summary>
+    header_footer &header(location where, const rich_text &text);
+
+    /// <summary>
+    /// Get the text of the  header at the given location. If headers are
+    /// different on odd and even pages, the odd header will be returned.
+    /// </summary>
+    rich_text header(location where) const;
+
+    // First Page Header
+
+    /// <summary>
+    /// True if a header has been set for the first page at any location.
+    /// </summary>
+    bool has_first_page_header() const;
+
+    /// <summary>
+    /// True if a header has been set for the first page at the given location.
+    /// </summary>
+    bool has_first_page_header(location where) const;
+
+    /// <summary>
+    /// Remove all headers from the first page.
+    /// </summary>
+    void clear_first_page_header();
+
+    /// <summary>
+    /// Remove header from the first page at the given location.
+    /// </summary>
+    void clear_first_page_header(location where);
+
+    /// <summary>
+    /// Add a header on the first page at the given location with the given text.
+    /// </summary>
+    header_footer &first_page_header(location where, const rich_text &text);
+
+    /// <summary>
+    /// Get the text of the first page header at the given location. If no first
+    /// page header has been set, the general header for that location will
+    /// be returned.
+    /// </summary>
+    rich_text first_page_header(location where) const;
+
+    // Odd/Even Header
+
+    /// <summary>
+    /// True if different headers have been set for odd and even pages.
+    /// </summary>
+    bool has_odd_even_header() const;
+
+    /// <summary>
+    /// True if different headers have been set for odd and even pages at the given location.
+    /// </summary>
+    bool has_odd_even_header(location where) const;
+
+    /// <summary>
+    /// Remove odd/even headers at all locations.
+    /// </summary>
+    void clear_odd_even_header();
+
+    /// <summary>
+    /// Remove odd/even headers at the given location.
+    /// </summary>
+    void clear_odd_even_header(location where);
+
+    /// <summary>
+    /// Add a header for odd pages at the given location with the given text.
+    /// </summary>
+    header_footer &odd_even_header(location where, const rich_text &odd, const rich_text &even);
+
+    /// <summary>
+    /// Get the text of the odd page header at the given location. If no odd
+    /// page header has been set, the general header for that location will
+    /// be returned.
+    /// </summary>
+    rich_text odd_header(location where) const;
+
+    /// <summary>
+    /// Get the text of the even page header at the given location. If no even
+    /// page header has been set, the general header for that location will
+    /// be returned.
+    /// </summary>
+    rich_text even_header(location where) const;
+
+    // Normal Footer
+
+    /// <summary>
+    /// True if any text has been added at the given location on any page.
+    /// </summary>
+    bool has_footer(location where) const;
+
+    /// <summary>
+    /// Remove all footers from all pages.
+    /// </summary>
+    void clear_footer();
+
+    /// <summary>
+    /// Remove footer at the given location on any page.
+    /// </summary>
+    void clear_footer(location where);
+
+    /// <summary>
+    /// Add a footer at the given location with the given text.
+    /// </summary>
+    header_footer &footer(location where, const std::string &text);
+
+    /// <summary>
+    /// Add a footer at the given location with the given text.
+    /// </summary>
+    header_footer &footer(location where, const rich_text &text);
+
+    /// <summary>
+    /// Get the text of the  footer at the given location. If footers are
+    /// different on odd and even pages, the odd footer will be returned.
+    /// </summary>
+    rich_text footer(location where) const;
+
+    // First Page footer
+
+    /// <summary>
+    /// True if a footer has been set for the first page at any location.
+    /// </summary>
+    bool has_first_page_footer() const;
+
+    /// <summary>
+    /// True if a footer has been set for the first page at the given location.
+    /// </summary>
+    bool has_first_page_footer(location where) const;
+
+    /// <summary>
+    /// Remove all footers from the first page.
+    /// </summary>
+    void clear_first_page_footer();
+
+    /// <summary>
+    /// Remove footer from the first page at the given location.
+    /// </summary>
+    void clear_first_page_footer(location where);
+
+    /// <summary>
+    /// Add a footer on the first page at the given location with the given text.
+    /// </summary>
+    header_footer &first_page_footer(location where, const rich_text &text);
+
+    /// <summary>
+    /// Get the text of the first page footer at the given location. If no first
+    /// page footer has been set, the general footer for that location will
+    /// be returned.
+    /// </summary>
+    rich_text first_page_footer(location where) const;
+
+    // Odd/Even Footer
+
+    /// <summary>
+    /// True if different footers have been set for odd and even pages.
+    /// </summary>
+    bool has_odd_even_footer() const;
+
+    /// <summary>
+    /// True if different footers have been set for odd and even pages at the given location.
+    /// </summary>
+    bool has_odd_even_footer(location where) const;
+
+    /// <summary>
+    /// Remove odd/even footers at all locations.
+    /// </summary>
+    void clear_odd_even_footer();
+
+    /// <summary>
+    /// Remove odd/even footers at the given location.
+    /// </summary>
+    void clear_odd_even_footer(location where);
+
+    /// <summary>
+    /// Add a footer for odd pages at the given location with the given text.
+    /// </summary>
+    header_footer &odd_even_footer(location where, const rich_text &odd, const rich_text &even);
+
+    /// <summary>
+    /// Get the text of the odd page footer at the given location. If no odd
+    /// page footer has been set, the general footer for that location will
+    /// be returned.
+    /// </summary>
+    rich_text odd_footer(location where) const;
+
+    /// <summary>
+    /// Get the text of the even page footer at the given location. If no even
+    /// page footer has been set, the general footer for that location will
+    /// be returned.
+    /// </summary>
+    rich_text even_footer(location where) const;
+
+    bool operator==(const header_footer &rhs) const;
+
+private:
+    bool align_with_margins_ = false;
+    bool different_odd_even_ = false;
+    bool scale_with_doc_ = false;
+
+    using container = std::unordered_map<location, rich_text, scoped_enum_hash<location>>;
+
+    container odd_headers_;
+    container even_headers_;
+    container first_headers_;
+    container odd_footers_;
+    container even_footers_;
+    container first_footers_;
+};
+
+} // namespace xlnt

+ 39 - 0
xlnt/include/xlnt/worksheet/major_order.hpp

@@ -0,0 +1,39 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <xlnt/xlnt_config.hpp>
+
+namespace xlnt {
+
+/// <summary>
+/// Defines whether iterating a range returns columns or rows sequentially.
+/// </summary>
+enum class XLNT_API major_order
+{
+    column,
+    row
+};
+
+} // namespace xlnt

+ 136 - 0
xlnt/include/xlnt/worksheet/page_margins.hpp

@@ -0,0 +1,136 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <xlnt/xlnt_config.hpp>
+
+namespace xlnt {
+
+/// <summary>
+/// Describes the margins around a worksheet for printing.
+/// </summary>
+class XLNT_API page_margins
+{
+public:
+    /// <summary>
+    /// Constructs a page margins objects with Excel-default margins.
+    /// </summary>
+    page_margins();
+
+    /// <summary>
+    /// Returns the top margin
+    /// </summary>
+    double top() const;
+
+    /// <summary>
+    /// Sets the top margin to top
+    /// </summary>
+    void top(double top);
+
+    /// <summary>
+    /// Returns the left margin
+    /// </summary>
+    double left() const;
+
+    /// <summary>
+    /// Sets the left margin to left
+    /// </summary>
+    void left(double left);
+
+    /// <summary>
+    /// Returns the bottom margin
+    /// </summary>
+    double bottom() const;
+
+    /// <summary>
+    /// Sets the bottom margin to bottom
+    /// </summary>
+    void bottom(double bottom);
+
+    /// <summary>
+    /// Returns the right margin
+    /// </summary>
+    double right() const;
+
+    /// <summary>
+    /// Sets the right margin to right
+    /// </summary>
+    void right(double right);
+
+    /// <summary>
+    /// Returns the header margin
+    /// </summary>
+    double header() const;
+
+    /// <summary>
+    /// Sets the header margin to header
+    /// </summary>
+    void header(double header);
+
+    /// <summary>
+    /// Returns the footer margin
+    /// </summary>
+    double footer() const;
+
+    /// <summary>
+    /// Sets the footer margin to footer
+    /// </summary>
+    void footer(double footer);
+
+    bool operator==(const page_margins &rhs) const;
+
+private:
+    /// <summary>
+    /// The top margin
+    /// </summary>
+    double top_ = 1;
+
+    /// <summary>
+    /// The left margin
+    /// </summary>
+    double left_ = 0.75;
+
+    /// <summary>
+    /// The bottom margin
+    /// </summary>
+    double bottom_ = 1;
+
+    /// <summary>
+    /// The right margin
+    /// </summary>
+    double right_ = 0.75;
+
+    /// <summary>
+    /// The header margin
+    /// </summary>
+    double header_ = 0.5;
+
+    /// <summary>
+    /// The footer margin
+    /// </summary>
+    double footer_ = 0.5;
+};
+
+} // namespace xlnt

+ 244 - 0
xlnt/include/xlnt/worksheet/page_setup.hpp

@@ -0,0 +1,244 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <xlnt/xlnt_config.hpp>
+#include <xlnt/utils/optional.hpp>
+
+namespace xlnt {
+
+/// <summary>
+/// The orientation of the worksheet when it is printed.
+/// </summary>
+enum class XLNT_API orientation
+{
+    default_orientation,
+    portrait,
+    landscape
+};
+
+/// <summary>
+/// The types of page breaks.
+/// </summary>
+enum class XLNT_API page_break
+{
+    none = 0,
+    row = 1,
+    column = 2
+};
+
+/// <summary>
+/// The possible paper sizes for printing.
+/// </summary>
+enum class XLNT_API paper_size
+{
+    letter = 1,
+    letter_small = 2,
+    tabloid = 3,
+    ledger = 4,
+    legal = 5,
+    statement = 6,
+    executive = 7,
+    a3 = 8,
+    a4 = 9,
+    a4_small = 10,
+    a5 = 11
+};
+
+/// <summary>
+/// Defines how a worksheet appears in the workbook.
+/// A workbook must have at least one sheet which is visible at all times.
+/// </summary>
+enum class XLNT_API sheet_state
+{
+    visible,
+    hidden,
+    very_hidden
+};
+
+/// <summary>
+/// Describes how a worksheet will be converted into a page during printing.
+/// </summary>
+struct XLNT_API page_setup
+{
+public:
+    /// <summary>
+    /// Default constructor.
+    /// </summary>
+    page_setup();
+
+    /// <summary>
+    /// Returns the page break.
+    /// </summary>
+    xlnt::page_break page_break() const;
+
+    /// <summary>
+    /// Sets the page break to b.
+    /// </summary>
+    void page_break(xlnt::page_break b);
+
+    /// <summary>
+    /// Returns the current sheet state of this page setup.
+    /// </summary>
+    xlnt::sheet_state sheet_state() const;
+
+    /// <summary>
+    /// Sets the sheet state to sheet_state.
+    /// </summary>
+    void sheet_state(xlnt::sheet_state sheet_state);
+
+    /// <summary>
+    /// Returns the paper size which should be used to print the worksheet using this page setup.
+    /// </summary>
+    xlnt::paper_size paper_size() const;
+
+    /// <summary>
+    /// Sets the paper size of this page setup.
+    /// </summary>
+    void paper_size(xlnt::paper_size paper_size);
+
+    /// <summary>
+    /// Check if current paper setting has paper size setting
+    /// </summary>
+    bool has_paper_size() const;
+
+    /// <summary>
+    /// Returns true if this worksheet should be scaled to fit on a single page during printing.
+    /// </summary>
+    bool fit_to_page() const;
+
+    /// <summary>
+    /// If true, forces the worksheet to be scaled to fit on a single page during printing.
+    /// </summary>
+    void fit_to_page(bool fit_to_page);
+
+    /// <summary>
+    /// Returns true if the height of this worksheet should be scaled to fit on a printed page.
+    /// </summary>
+    bool fit_to_height() const;
+
+    /// <summary>
+    /// Sets whether the height of the page should be scaled to fit on a printed page.
+    /// </summary>
+    void fit_to_height(bool fit_to_height);
+
+    /// <summary>
+    /// Returns true if the width of this worksheet should be scaled to fit on a printed page.
+    /// </summary>
+    bool fit_to_width() const;
+
+    /// <summary>
+    /// Sets whether the width of the page should be scaled to fit on a printed page.
+    /// </summary>
+    void fit_to_width(bool fit_to_width);
+
+    /// <summary>
+    /// Sets the factor by which the page should be scaled during printing.
+    /// </summary>
+    void scale(double scale);
+
+    /// <summary>
+    /// Returns the factor by which the page should be scaled during printing.
+    /// </summary>
+    double scale() const;
+
+    /// <summary>
+    /// Check if current paper setting has scale setting
+    /// </summary>
+    bool has_scale() const;
+
+    /// <summary>
+    /// Gets reference relationship Id
+    /// </summary>
+    const std::string& rel_id() const;
+
+    /// <summary>
+    /// Sets reference relationship Id
+    /// </summary>
+    void rel_id(const std::string& val);
+
+    /// <summary>
+    /// Check if current paper setting has a reference relationship
+    /// </summary>
+    bool has_rel_id() const;
+
+    /// <summary>
+    /// The orientation
+    /// </summary>
+    xlnt::optional<xlnt::orientation> orientation_;
+    /// <summary>
+    /// The horizontal dpi
+    /// </summary>
+    xlnt::optional<std::size_t> horizontal_dpi_;
+    /// <summary>
+    /// The vertical dpi
+    /// </summary>
+    xlnt::optional<std::size_t> vertical_dpi_;
+
+    bool operator==(const page_setup &rhs) const;
+
+private:
+    /// <summary>
+    /// Relationship Id
+    /// </summary>
+    std::string rel_id_;
+
+    /// <summary>
+    /// The break
+    /// </summary>
+    xlnt::page_break break_;
+
+    /// <summary>
+    /// The sheet state
+    /// </summary>
+    xlnt::sheet_state sheet_state_;
+
+    /// <summary>
+    /// The paper size
+    /// </summary>
+    xlnt::optional<xlnt::paper_size> paper_size_;
+
+    /// <summary>
+    /// Whether or not to fit to page
+    /// </summary>
+    bool fit_to_page_;
+
+    /// <summary>
+    /// Whether or not to fit to height
+    /// </summary>
+    bool fit_to_height_;
+
+    /// <summary>
+    /// Whether or not to fit to width
+    /// </summary>
+    bool fit_to_width_;
+
+    /// <summary>
+    /// The amount to scale the worksheet
+    /// </summary>
+    xlnt::optional<double> scale_;
+};
+
+} // namespace xlnt

+ 98 - 0
xlnt/include/xlnt/worksheet/pane.hpp

@@ -0,0 +1,98 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <xlnt/xlnt_config.hpp>
+#include <xlnt/cell/cell_reference.hpp>
+#include <xlnt/cell/index_types.hpp>
+
+namespace xlnt {
+
+/// <summary>
+/// Enumeration of possible states of a pane
+/// </summary>
+enum class XLNT_API pane_state
+{
+    frozen,
+    frozen_split,
+    split
+};
+
+/// <summary>
+/// Enumeration of the four quadrants of a worksheet
+/// </summary>
+enum class XLNT_API pane_corner
+{
+    top_left,
+    top_right,
+    bottom_left,
+    bottom_right
+};
+
+/// <summary>
+/// A fixed portion of a worksheet.
+/// </summary>
+struct XLNT_API pane
+{
+    /// <summary>
+    /// The optional top left cell
+    /// </summary>
+    optional<cell_reference> top_left_cell;
+
+    /// <summary>
+    /// The state of the pane
+    /// </summary>
+    pane_state state = pane_state::split;
+
+    /// <summary>
+    /// The pane which contains the active cell
+    /// </summary>
+    pane_corner active_pane = pane_corner::top_left;
+
+    /// <summary>
+    /// The row where the split should take place
+    /// </summary>
+    row_t y_split = 1;
+
+    /// <summary>
+    /// The column where the split should take place
+    /// </summary>
+    column_t x_split = 1;
+
+    /// <summary>
+    /// Returns true if this pane is equal to rhs based on its top-left cell, state,
+    /// active pane, and x/y split location.
+    /// </summary>
+    bool operator==(const pane &rhs) const
+    {
+        return top_left_cell == rhs.top_left_cell
+            && state == rhs.state
+            && active_pane == rhs.active_pane
+            && y_split == rhs.y_split
+            && x_split == rhs.x_split;
+    }
+};
+
+} // namespace xlnt

+ 167 - 0
xlnt/include/xlnt/worksheet/phonetic_pr.hpp

@@ -0,0 +1,167 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <xlnt/xlnt_config.hpp>
+#include <xlnt/utils/optional.hpp>
+#include <cstdint>
+#include <ostream>
+
+namespace xlnt {
+
+/// <summary>
+/// Phonetic properties
+/// Element provides a collection of properties that affect display of East Asian Languages
+/// [Serialised phoneticPr]
+/// </summary>
+class XLNT_API phonetic_pr
+{
+public:
+    static std::string Serialised_ID();
+
+    /// <summary>
+    /// possible values for alignment property
+    /// </summary>
+    enum class align
+    {
+        center,
+        distributed,
+        left,
+        no_control
+    };
+
+    /// <summary>
+    /// possible values for type property
+    /// </summary>
+    enum class phonetic_type
+    {
+        full_width_katakana,
+        half_width_katakana,
+        hiragana,
+        no_conversion
+    };
+
+    /// <summary>
+    /// FontID represented by an unsigned 32-bit integer
+    /// </summary>
+    using font_id_t = std::uint32_t;
+
+    /// <summary>
+    /// Default ctor for phonetic properties
+    /// </summary>
+    phonetic_pr() = default;
+
+    /// <summary>
+    /// FontID ctor for phonetic properties
+    /// </summary>
+    explicit phonetic_pr(font_id_t font);
+
+    /// <summary>
+    /// adds the xml serialised representation of this element to the stream
+    /// </summary>
+    void serialise(std::ostream &output_stream) const;
+
+    /// <summary>
+    /// get the font index
+    /// </summary>
+    font_id_t font_id() const;
+
+    /// <summary>
+    /// set the font index
+    /// </summary>
+    void font_id(font_id_t font);
+
+    /// <summary>
+    /// is the phonetic type set
+    /// </summary>
+    bool has_type() const;
+
+    /// <summary>
+    /// returns the phonetic type
+    /// </summary>
+    phonetic_type type() const;
+
+    /// <summary>
+    /// sets the phonetic type
+    /// </summary>
+    void type(phonetic_type type);
+
+    /// <summary>
+    /// is the alignment set
+    /// </summary>
+    bool has_alignment() const;
+
+    /// <summary>
+    /// get the alignment
+    /// </summary>
+    align alignment() const;
+
+    /// <summary>
+    /// set the alignment
+    /// </summary>
+    void alignment(align align);
+
+    // serialisation
+    /// <summary>
+    /// string form of the type enum
+    /// </summary>
+    static const std::string &type_as_string(phonetic_type type);
+
+    /// <summary>
+    /// type enum from string
+    /// </summary>
+    static phonetic_type type_from_string(const std::string &str);
+
+    /// <summary>
+    /// string form of alignment enum
+    /// </summary>
+    static const std::string &alignment_as_string(xlnt::phonetic_pr::align type);
+
+    /// <summary>
+    /// alignment enum from string
+    /// </summary>
+    static align alignment_from_string(const std::string &str);
+
+    bool operator==(const phonetic_pr &rhs) const;
+
+private:
+    /// <summary>
+    /// zero based index into style sheet font record.
+    /// Default: 0
+    /// </summary>
+    font_id_t font_id_ = 0;
+
+    /// <summary>
+    /// Type of characters to use.
+    /// Default: full width katakana
+    /// </summary>
+    xlnt::optional<phonetic_type> type_;
+
+    /// <summary>
+    /// align across the cell(s).
+    /// Default: Left
+    /// </summary>
+    xlnt::optional<align> alignment_;
+};
+} // namespace xlnt

+ 67 - 0
xlnt/include/xlnt/worksheet/print_options.hpp

@@ -0,0 +1,67 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <xlnt/xlnt_config.hpp>
+#include <xlnt/utils/optional.hpp>
+
+namespace xlnt {
+
+struct XLNT_API print_options
+{
+    /// <summary>
+    /// if both grid_lines_set and this are true, grid lines are printed
+    /// </summary>
+    optional<bool> print_grid_lines;
+
+    /// <summary>
+    /// if both print grid lines and this are true, grid lines are printed
+    /// </summary>
+    optional<bool> grid_lines_set;
+
+    /// <summary>
+    /// print row and column headings
+    /// </summary>
+    optional<bool> print_headings;
+
+    /// <summary>
+    /// center on page horizontally
+    /// </summary>
+    optional<bool> horizontal_centered;
+
+    /// <summary>
+    /// center on page vertically
+    /// </summary>
+    optional<bool> vertical_centered;
+};
+
+inline bool operator==(const print_options &lhs, const print_options &rhs)
+{
+    return lhs.grid_lines_set == rhs.grid_lines_set
+        && lhs.horizontal_centered == rhs.horizontal_centered
+        && lhs.print_grid_lines == rhs.print_grid_lines
+        && lhs.print_headings == rhs.print_headings
+        && lhs.vertical_centered == rhs.vertical_centered;
+}
+} // namespace xlnt

+ 315 - 0
xlnt/include/xlnt/worksheet/range.hpp

@@ -0,0 +1,315 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <functional>
+#include <iterator>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <xlnt/xlnt_config.hpp>
+#include <xlnt/styles/alignment.hpp>
+#include <xlnt/styles/border.hpp>
+#include <xlnt/styles/conditional_format.hpp>
+#include <xlnt/styles/fill.hpp>
+#include <xlnt/styles/font.hpp>
+#include <xlnt/styles/number_format.hpp>
+#include <xlnt/styles/protection.hpp>
+#include <xlnt/worksheet/cell_vector.hpp>
+#include <xlnt/worksheet/major_order.hpp>
+#include <xlnt/worksheet/range_iterator.hpp>
+#include <xlnt/worksheet/range_reference.hpp>
+#include <xlnt/worksheet/worksheet.hpp>
+
+namespace xlnt {
+
+class const_range_iterator;
+class range_iterator;
+
+/// <summary>
+/// A range is a 2D collection of cells with defined extens that can be iterated upon.
+/// </summary>
+class XLNT_API range
+{
+public:
+    /// <summary>
+    /// Alias for the iterator type
+    /// </summary>
+    using iterator = range_iterator;
+
+    /// <summary>
+    /// Alias for the const iterator type
+    /// </summary>
+    using const_iterator = const_range_iterator;
+
+    /// <summary>
+    /// Alias for the reverse iterator type
+    /// </summary>
+    using reverse_iterator = std::reverse_iterator<iterator>;
+
+    /// <summary>
+    /// Alias for the const reverse iterator type
+    /// </summary>
+    using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+
+    /// <summary>
+    /// Constructs a range on the given worksheet.
+    /// </summary>
+    range(worksheet ws, const range_reference &reference,
+        major_order order = major_order::row, bool skip_null = false);
+
+    /// <summary>
+    /// Desctructor
+    /// </summary>
+    ~range();
+
+    /// <summary>
+    /// Default copy constructor.
+    /// </summary>
+    range(const range &) = default;
+
+    /// <summary>
+    /// Erases all cell data from the worksheet for cells within this range.
+    /// </summary>
+    void clear_cells();
+
+    /// <summary>
+    /// Returns a vector pointing to the n-th row or column in this range (depending
+    /// on major order).
+    /// </summary>
+    cell_vector vector(std::size_t n);
+
+    /// <summary>
+    /// Returns a vector pointing to the n-th row or column in this range (depending
+    /// on major order).
+    /// </summary>
+    const cell_vector vector(std::size_t n) const;
+
+    /// <summary>
+    /// Returns a cell in the range relative to its top left cell.
+    /// </summary>
+    class cell cell(const cell_reference &ref);
+
+    /// <summary>
+    /// Returns a cell in the range relative to its top left cell.
+    /// </summary>
+    const class cell cell(const cell_reference &ref) const;
+
+    /// <summary>
+    /// The worksheet this range targets
+    /// </summary>
+    const worksheet &target_worksheet() const;
+
+    /// <summary>
+    /// Returns the reference defining the bounds of this range.
+    /// </summary>
+    range_reference reference() const;
+
+    /// <summary>
+    /// Returns the number of rows or columns in this range (depending on major order).
+    /// </summary>
+    std::size_t length() const;
+
+    /// <summary>
+    /// Returns true if the given cell exists in the parent worksheet of this range.
+    /// </summary>
+    bool contains(const cell_reference &ref);
+
+    /// <summary>
+    /// Sets the alignment of all cells in the range to new_alignment and returns the range.
+    /// </summary>
+    range alignment(const xlnt::alignment &new_alignment);
+
+    /// <summary>
+    /// Sets the border of all cells in the range to new_border and returns the range.
+    /// </summary>
+    range border(const xlnt::border &new_border);
+
+    /// <summary>
+    /// Sets the fill of all cells in the range to new_fill and returns the range.
+    /// </summary>
+    range fill(const xlnt::fill &new_fill);
+
+    /// <summary>
+    /// Sets the font of all cells in the range to new_font and returns the range.
+    /// </summary>
+    range font(const xlnt::font &new_font);
+
+    /// <summary>
+    /// Sets the number format of all cells in the range to new_number_format and
+    /// returns the range.
+    /// </summary>
+    range number_format(const xlnt::number_format &new_number_format);
+
+    /// <summary>
+    /// Sets the protection of all cells in the range to new_protection and returns the range.
+    /// </summary>
+    range protection(const xlnt::protection &new_protection);
+
+    /// <summary>
+    /// Sets the named style applied to all cells in this range to a style named style_name.
+    /// </summary>
+    range style(const class style &new_style);
+
+    /// <summary>
+    /// Sets the named style applied to all cells in this range to a style named style_name.
+    /// If this style has not been previously created in the workbook, a
+    /// key_not_found exception will be thrown.
+    /// </summary>
+    range style(const std::string &style_name);
+
+    /// <summary>
+    ///
+    /// </summary>
+    xlnt::conditional_format conditional_format(const condition &when);
+
+    /// <summary>
+    /// Returns the first row or column in this range.
+    /// </summary>
+    cell_vector front();
+
+    /// <summary>
+    /// Returns the first row or column in this range.
+    /// </summary>
+    const cell_vector front() const;
+
+    /// <summary>
+    /// Returns the last row or column in this range.
+    /// </summary>
+    cell_vector back();
+
+    /// <summary>
+    /// Returns the last row or column in this range.
+    /// </summary>
+    const cell_vector back() const;
+
+    /// <summary>
+    /// Returns an iterator to the first row or column in this range.
+    /// </summary>
+    iterator begin();
+
+    /// <summary>
+    /// Returns an iterator to one past the last row or column in this range.
+    /// </summary>
+    iterator end();
+
+    /// <summary>
+    /// Returns an iterator to the first row or column in this range.
+    /// </summary>
+    const_iterator begin() const;
+
+    /// <summary>
+    /// Returns an iterator to one past the last row or column in this range.
+    /// </summary>
+    const_iterator end() const;
+
+    /// <summary>
+    /// Returns an iterator to the first row or column in this range.
+    /// </summary>
+    const_iterator cbegin() const;
+
+    /// <summary>
+    /// Returns an iterator to one past the last row or column in this range.
+    /// </summary>
+    const_iterator cend() const;
+
+    /// <summary>
+    /// Returns a reverse iterator to the first row or column in this range.
+    /// </summary>
+    reverse_iterator rbegin();
+
+    /// <summary>
+    /// Returns a reverse iterator to one past the last row or column in this range.
+    /// </summary>
+    reverse_iterator rend();
+
+    /// <summary>
+    /// Returns a reverse iterator to the first row or column in this range.
+    /// </summary>
+    const_reverse_iterator rbegin() const;
+
+    /// <summary>
+    /// Returns a reverse iterator to one past the last row or column in this range.
+    /// </summary>
+    const_reverse_iterator rend() const;
+
+    /// <summary>
+    /// Returns a reverse iterator to the first row or column in this range.
+    /// </summary>
+    const_reverse_iterator crbegin() const;
+
+    /// <summary>
+    /// Returns a reverse iterator to one past the last row or column in this range.
+    /// </summary>
+    const_reverse_iterator crend() const;
+
+    /// <summary>
+    /// Applies function f to all cells in the range
+    /// </summary>
+    void apply(std::function<void(class cell)> f);
+
+    /// <summary>
+    /// Returns the n-th row or column in this range.
+    /// </summary>
+    cell_vector operator[](std::size_t n);
+
+    /// <summary>
+    /// Returns the n-th row or column in this range.
+    /// </summary>
+    const cell_vector operator[](std::size_t n) const;
+
+    /// <summary>
+    /// Returns true if this range is equivalent to comparand.
+    /// </summary>
+    bool operator==(const range &comparand) const;
+
+    /// <summary>
+    /// Returns true if this range is not equivalent to comparand.
+    /// </summary>
+    bool operator!=(const range &comparand) const;
+
+private:
+    /// <summary>
+    /// The worksheet this range is within
+    /// </summary>
+    class worksheet ws_;
+
+    /// <summary>
+    /// The reference of this range
+    /// </summary>
+    range_reference ref_;
+
+    /// <summary>
+    /// Whether this range should be iterated by columns or rows first
+    /// </summary>
+    major_order order_;
+
+    /// <summary>
+    /// Whether null rows/columns and cells should be skipped during iteration
+    /// </summary>
+    bool skip_null_;
+};
+
+} // namespace xlnt

+ 273 - 0
xlnt/include/xlnt/worksheet/range_iterator.hpp

@@ -0,0 +1,273 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <cstddef> // std::ptrdiff_t
+
+#include <xlnt/xlnt_config.hpp>
+#include <xlnt/cell/cell_reference.hpp>
+#include <xlnt/worksheet/major_order.hpp>
+#include <xlnt/worksheet/range_reference.hpp>
+#include <xlnt/worksheet/worksheet.hpp>
+
+namespace xlnt {
+
+class cell_vector;
+
+/// <summary>
+/// An iterator used by worksheet and range for traversing
+/// a 2D grid of cells by row/column then across that row/column.
+/// </summary>
+class XLNT_API range_iterator
+{
+public:
+    /// <summary>
+    /// iterator tags required for use with standard algorithms and adapters
+    /// </summary>
+    using iterator_category = std::bidirectional_iterator_tag;
+    using value_type = cell_vector;
+    using difference_type = std::ptrdiff_t;
+    using pointer = cell_vector *;
+    using reference = cell_vector; // intentionally value
+
+    /// <summary>
+    /// Default constructs a range iterator
+    /// </summary>
+    range_iterator() = default;
+
+    /// <summary>
+    /// Constructs a range iterator on a worksheet, cell pointing to the current
+    /// row or column, range bounds, an order, and whether or not to skip null column/rows.
+    /// </summary>
+    range_iterator(worksheet &ws, const cell_reference &cursor,
+        const range_reference &bounds, major_order order, bool skip_null);
+
+    /// <summary>
+    /// Default copy constructor.
+    /// </summary>
+    range_iterator(const range_iterator &) = default;
+
+    /// <summary>
+    /// Default assignment operator.
+    /// </summary>
+    range_iterator &operator=(const range_iterator &) = default;
+
+    /// <summary>
+    /// Default move constructor.
+    /// </summary>
+    range_iterator(range_iterator &&) = default;
+
+    /// <summary>
+    /// Default move assignment operator.
+    /// </summary>
+    range_iterator &operator=(range_iterator &&) = default;
+
+    /// <summary>
+    /// Default destructor
+    /// </summary>
+    ~range_iterator() = default;
+
+    /// <summary>
+    /// Dereference the iterator to return a column or row.
+    /// </summary>
+    reference operator*();
+
+    /// <summary>
+    /// Dereference the iterator to return a column or row.
+    /// </summary>
+    const reference operator*() const;
+
+    /// <summary>
+    /// Returns true if this iterator is equivalent to other.
+    /// </summary>
+    bool operator==(const range_iterator &other) const;
+
+    /// <summary>
+    /// Returns true if this iterator is not equivalent to other.
+    /// </summary>
+    bool operator!=(const range_iterator &other) const;
+
+    /// <summary>
+    /// Pre-decrement the iterator to point to the previous row/column.
+    /// </summary>
+    range_iterator &operator--();
+
+    /// <summary>
+    /// Post-decrement the iterator to point to the previous row/column.
+    /// </summary>
+    range_iterator operator--(int);
+
+    /// <summary>
+    /// Pre-increment the iterator to point to the next row/column.
+    /// </summary>
+    range_iterator &operator++();
+
+    /// <summary>
+    /// Post-increment the iterator to point to the next row/column.
+    /// </summary>
+    range_iterator operator++(int);
+
+private:
+    /// <summary>
+    /// If true, empty rows and cells will be skipped when iterating with this iterator
+    /// </summary>
+    bool skip_null_ = false;
+
+    /// <summary>
+    /// Whether rows or columns should be iterated over first
+    /// </summary>
+    major_order order_ = major_order::column;
+
+    /// <summary>
+    /// The worksheet
+    /// </summary>
+    worksheet ws_;
+
+    /// <summary>
+    /// The first cell in the current row/column
+    /// </summary>
+    cell_reference cursor_;
+
+    /// <summary>
+    /// The bounds of the range
+    /// </summary>
+    range_reference bounds_;
+};
+
+/// <summary>
+/// A const version of range_iterator which does not allow modification
+/// to the dereferenced cell_vector.
+/// </summary>
+class XLNT_API const_range_iterator
+{
+public:
+    /// <summary>
+    /// this iterator meets the interface requirements of bidirection_iterator
+    /// </summary>
+    using iterator_category = std::bidirectional_iterator_tag;
+    using value_type = const cell_vector;
+    using difference_type = std::ptrdiff_t;
+    using pointer = const cell_vector *;
+    using reference = const cell_vector; // intentionally value
+
+    /// <summary>
+    /// Default constructs a range iterator
+    /// </summary>
+    const_range_iterator() = default;
+
+    /// <summary>
+    /// Constructs a range iterator on a worksheet, cell pointing to the current
+    /// row or column, range bounds, an order, and whether or not to skip null column/rows.
+    /// </summary>
+    const_range_iterator(const worksheet &ws, const cell_reference &cursor,
+        const range_reference &bounds, major_order order, bool skip_null);
+
+    /// <summary>
+    /// Default copy constructor.
+    /// </summary>
+    const_range_iterator(const const_range_iterator &) = default;
+
+    /// <summary>
+    /// Default assignment operator.
+    /// </summary>
+    const_range_iterator &operator=(const const_range_iterator &) = default;
+
+    /// <summary>
+    /// Default move constructor.
+    /// </summary>
+    const_range_iterator(const_range_iterator &&) = default;
+
+    /// <summary>
+    /// Default move assignment operator.
+    /// </summary>
+    const_range_iterator &operator=(const_range_iterator &&) = default;
+
+    /// <summary>
+    /// Default destructor
+    /// </summary>
+    ~const_range_iterator() = default;
+
+    /// <summary>
+    /// Dereferennce the iterator to return the current column/row.
+    /// </summary>
+    const reference operator*() const;
+
+    /// <summary>
+    /// Returns true if this iterator is equivalent to other.
+    /// </summary>
+    bool operator==(const const_range_iterator &other) const;
+
+    /// <summary>
+    /// Returns true if this iterator is not equivalent to other.
+    /// </summary>
+    bool operator!=(const const_range_iterator &other) const;
+
+    /// <summary>
+    /// Pre-decrement the iterator to point to the next row/column.
+    /// </summary>
+    const_range_iterator &operator--();
+
+    /// <summary>
+    /// Post-decrement the iterator to point to the next row/column.
+    /// </summary>
+    const_range_iterator operator--(int);
+
+    /// <summary>
+    /// Pre-increment the iterator to point to the next row/column.
+    /// </summary>
+    const_range_iterator &operator++();
+
+    /// <summary>
+    /// Post-increment the iterator to point to the next row/column.
+    /// </summary>
+    const_range_iterator operator++(int);
+
+private:
+    /// <summary>
+    /// If true, empty rows and cells will be skipped when iterating with this iterator
+    /// </summary>
+    bool skip_null_ = false;
+
+    /// <summary>
+    /// Determines whether iteration should move through rows or columns first
+    /// </summary>
+    major_order order_ = major_order::column;
+
+    /// <summary>
+    /// The implementation of the worksheet this iterator points to
+    /// </summary>
+    detail::worksheet_impl *ws_;
+
+    /// <summary>
+    /// The first cell in the current row or column this iterator points to
+    /// </summary>
+    cell_reference cursor_;
+
+    /// <summary>
+    /// The range this iterator starts and ends in
+    /// </summary>
+    range_reference bounds_;
+};
+
+} // namespace xlnt

+ 189 - 0
xlnt/include/xlnt/worksheet/range_reference.hpp

@@ -0,0 +1,189 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <xlnt/xlnt_config.hpp>
+#include <xlnt/cell/cell_reference.hpp>
+
+namespace xlnt {
+
+/// <summary>
+/// A range_reference describes a rectangular area of a worksheet with positive
+/// width and height defined by a top-left and bottom-right corner.
+/// </summary>
+class XLNT_API range_reference
+{
+public:
+    /// <summary>
+    /// Converts relative reference coordinates to absolute coordinates (B12 -> $B$12)
+    /// </summary>
+    static range_reference make_absolute(const range_reference &relative_reference);
+
+    /// <summary>
+    /// Constructs a range reference equal to A1:A1
+    /// </summary>
+    range_reference();
+
+    /// <summary>
+    /// Constructs a range reference equivalent to the provided range_string in the form
+    /// top_left:bottom_right.
+    /// </summary>
+    explicit range_reference(const std::string &range_string);
+
+    /// <summary>
+    /// Constructs a range reference equivalent to the provided range_string in the form
+    /// top_left:bottom_right.
+    /// </summary>
+    explicit range_reference(const char *range_string);
+
+    /// <summary>
+    /// Constructs a range reference from cell references indicating top
+    /// left and bottom right coordinates of the range.
+    /// </summary>
+    range_reference(const cell_reference &start, const cell_reference &end);
+
+    /// <summary>
+    /// Constructs a range reference from column and row indices.
+    /// </summary>
+    range_reference(column_t column_index_start, row_t row_index_start,
+        column_t column_index_end, row_t row_index_end);
+
+    /// <summary>
+    /// Returns true if the range has a width and height of 1 cell.
+    /// </summary>
+    bool is_single_cell() const;
+
+    /// <summary>
+    /// Returns the number of columns encompassed by this range.
+    /// </summary>
+    std::size_t width() const;
+
+    /// <summary>
+    /// Returns the number of rows encompassed by this range.
+    /// </summary>
+    std::size_t height() const;
+
+    /// <summary>
+    /// Returns the coordinate of the top left cell of this range.
+    /// </summary>
+    cell_reference top_left() const;
+
+    /// <summary>
+    /// Returns the coordinate of the top right cell of this range.
+    /// </summary>
+    cell_reference top_right() const;
+
+    /// <summary>
+    /// Returns the coordinate of the bottom left cell of this range.
+    /// </summary>
+    cell_reference bottom_left() const;
+
+    /// <summary>
+    /// Returns the coordinate of the bottom right cell of this range.
+    /// </summary>
+    cell_reference bottom_right() const;
+
+    /// <summary>
+    /// Returns a new range reference with the same width and height as this
+    /// range but shifted by the given number of columns and rows.
+    /// </summary>
+    range_reference make_offset(int column_offset, int row_offset) const;
+
+    /// <summary>
+    /// Returns a string representation of this range.
+    /// </summary>
+    std::string to_string() const;
+
+    /// <summary>
+    /// Returns true if the given cell reference is within the bounds of this range reference.
+    /// </summary>
+    bool contains(const cell_reference &ref) const;
+
+    /// <summary>
+    /// Returns true if this range is equivalent to the other range.
+    /// </summary>
+    bool operator==(const range_reference &comparand) const;
+
+    /// <summary>
+    /// Returns true if this range is equivalent to the string representation
+    /// of the other range.
+    /// </summary>
+    bool operator==(const std::string &reference_string) const;
+
+    /// <summary>
+    /// Returns true if this range is equivalent to the string representation
+    /// of the other range.
+    /// </summary>
+    bool operator==(const char *reference_string) const;
+
+    /// <summary>
+    /// Returns true if this range is not equivalent to the other range.
+    /// </summary>
+    bool operator!=(const range_reference &comparand) const;
+
+    /// <summary>
+    /// Returns true if this range is not equivalent to the string representation
+    /// of the other range.
+    /// </summary>
+    bool operator!=(const std::string &reference_string) const;
+
+    /// <summary>
+    /// Returns true if this range is not equivalent to the string representation
+    /// of the other range.
+    /// </summary>
+    bool operator!=(const char *reference_string) const;
+
+private:
+    /// <summary>
+    /// The top left cell in the range
+    /// </summary>
+    cell_reference top_left_;
+
+    /// <summary>
+    /// The bottom right cell in the range
+    /// </summary>
+    cell_reference bottom_right_;
+};
+
+/// <summary>
+/// Returns true if the string representation of the range is equivalent to ref.
+/// </summary>
+XLNT_API bool operator==(const std::string &reference_string, const range_reference &ref);
+
+/// <summary>
+/// Returns true if the string representation of the range is equivalent to ref.
+/// </summary>
+XLNT_API bool operator==(const char *reference_string, const range_reference &ref);
+
+/// <summary>
+/// Returns true if the string representation of the range is not equivalent to ref.
+/// </summary>
+XLNT_API bool operator!=(const std::string &reference_string, const range_reference &ref);
+
+/// <summary>
+/// Returns true if the string representation of the range is not equivalent to ref.
+/// </summary>
+XLNT_API bool operator!=(const char *reference_string, const range_reference &ref);
+
+} // namespace xlnt

+ 86 - 0
xlnt/include/xlnt/worksheet/row_properties.hpp

@@ -0,0 +1,86 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <xlnt/xlnt_config.hpp>
+
+namespace xlnt {
+
+/// <summary>
+/// The properties of a row in a worksheet.
+/// </summary>
+class XLNT_API row_properties
+{
+public:
+    /// <summary>
+    /// Row height
+    /// </summary>
+    optional<double> height;
+
+    /// <summary>
+    /// Distance in pixels from the bottom of the cell to the baseline of the cell content
+    /// </summary>
+    optional<double> dy_descent;
+
+    /// <summary>
+    /// Whether or not the height is different from the default
+    /// </summary>
+    bool custom_height = false;
+
+    /// <summary>
+    /// Whether or not the row should be hidden
+    /// </summary>
+    bool hidden = false;
+
+    /// <summary>
+    /// True if row style should be applied
+    /// </summary>
+    optional<bool> custom_format;
+
+    /// <summary>
+    /// The index to the style used by all cells in this row
+    /// </summary>
+    optional<std::size_t> style;
+
+    /// <summary>
+    /// The row column span, used as part of the row block optimisation.
+    /// This used for loading this attribute from existing excel files mainly for inspecting
+    /// and not used when saving, it is calculated in the xlsx_producer.
+    /// </summary>
+    optional<std::string> spans;
+};
+
+inline bool operator==(const row_properties &lhs, const row_properties &rhs)
+{
+    return lhs.height == rhs.height
+        && lhs.dy_descent == rhs.dy_descent
+        && lhs.custom_height == rhs.custom_height
+        && lhs.hidden == rhs.hidden
+        && lhs.custom_format == rhs.custom_format
+        && lhs.style == rhs.style
+        && lhs.spans == rhs.spans;
+}
+
+} // namespace xlnt

+ 163 - 0
xlnt/include/xlnt/worksheet/selection.hpp

@@ -0,0 +1,163 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <xlnt/xlnt_config.hpp>
+#include <xlnt/cell/cell_reference.hpp>
+#include <xlnt/worksheet/pane.hpp>
+#include <xlnt/worksheet/range_reference.hpp>
+
+namespace xlnt {
+
+/// <summary>
+/// The selected area of a worksheet.
+/// </summary>
+class XLNT_API selection
+{
+public:
+    /// <summary>
+    /// default ctor
+    /// </summary>
+    explicit selection() = default;
+
+    /// <summary>
+    /// ctor when no range selected
+    /// sqref == active_cell
+    /// </summary>
+    explicit selection(pane_corner quadrant, cell_reference active_cell)
+        : active_cell_(active_cell), sqref_(range_reference(active_cell, active_cell)), pane_(quadrant)
+    {
+    }
+
+    /// <summary>
+    /// ctor with selected range
+    /// sqref must contain active_cell
+    /// </summary>
+    explicit selection(pane_corner quadrant, cell_reference active_cell, range_reference selected)
+        : active_cell_(active_cell), sqref_(selected), pane_(quadrant)
+    {
+    }
+
+    /// <summary>
+    /// Returns true if this selection has a defined active cell.
+    /// </summary>
+    bool has_active_cell() const
+    {
+        return active_cell_.is_set();
+    }
+
+    /// <summary>
+    /// Returns the cell reference of the active cell.
+    /// </summary>
+    cell_reference active_cell() const
+    {
+        return active_cell_.get();
+    }
+
+    /// <summary>
+    /// Sets the active cell to that pointed to by ref.
+    /// </summary>
+    void active_cell(const cell_reference &ref)
+    {
+        active_cell_ = ref;
+    }
+
+    /// <summary>
+    /// Returns true if this selection has a defined sqref.
+    /// </summary>
+    bool has_sqref() const
+    {
+        return sqref_.is_set();
+    }
+
+    /// <summary>
+    /// Returns the range encompassed by this selection.
+    /// </summary>
+    range_reference sqref() const
+    {
+        return sqref_.get();
+    }
+
+    /// <summary>
+    /// Sets the range encompassed by this selection.
+    /// </summary>
+    void sqref(const range_reference &ref)
+    {
+        sqref_ = ref;
+    }
+
+    /// <summary>
+    /// Sets the range encompassed by this selection.
+    /// </summary>
+    void sqref(const std::string &ref)
+    {
+        sqref(range_reference(ref));
+    }
+
+    /// <summary>
+    /// Returns the sheet quadrant of this selection.
+    /// </summary>
+    pane_corner pane() const
+    {
+        return pane_;
+    }
+
+    /// <summary>
+    /// Sets the sheet quadrant of this selection to corner.
+    /// </summary>
+    void pane(pane_corner corner)
+    {
+        pane_ = corner;
+    }
+
+    /// <summary>
+    /// Returns true if this selection is equal to rhs based on its active cell,
+    /// sqref, and pane.
+    /// </summary>
+    bool operator==(const selection &rhs) const
+    {
+        return active_cell_ == rhs.active_cell_
+            && sqref_ == rhs.sqref_
+            && pane_ == rhs.pane_;
+    }
+
+private:
+    /// <summary>
+    /// The last selected cell in the selection
+    /// </summary>
+    optional<cell_reference> active_cell_;
+
+    /// <summary>
+    /// The last selected block in the selection
+    /// contains active_cell_, normally == to active_cell_
+    /// </summary>
+    optional<range_reference> sqref_;
+
+    /// <summary>
+    /// The corner of the worksheet that this selection extends to
+    /// </summary>
+    pane_corner pane_ = pane_corner::top_left;
+};
+
+} // namespace xlnt

+ 67 - 0
xlnt/include/xlnt/worksheet/sheet_format_properties.hpp

@@ -0,0 +1,67 @@
+// Copyright (c) 2018 Thomas Fussell
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// 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, ARISING 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 <xlnt/xlnt_config.hpp>
+#include <xlnt/utils/numeric.hpp>
+#include <xlnt/utils/optional.hpp>
+
+namespace xlnt {
+
+/// <summary>
+/// General worksheet formatting properties.
+/// </summary>
+class XLNT_API sheet_format_properties
+{
+public:
+    /// <summary>
+    /// The base column width
+    /// </summary>
+    optional<double> base_col_width;
+
+    /// <summary>
+    /// The default row height is required
+    /// </summary>
+    double default_row_height = 15.0;
+
+    /// <summary>
+    /// The default column width
+    /// </summary>
+    optional<double> default_column_width;
+
+    /// <summary>
+    /// x14ac extension, dyDescent property
+    /// </summary>
+    optional<double> dy_descent;
+};
+
+inline bool operator==(const sheet_format_properties &lhs, const sheet_format_properties &rhs)
+{
+    return lhs.base_col_width == rhs.base_col_width
+        && lhs.default_column_width == rhs.default_column_width
+        && detail::float_equals(lhs.default_row_height, rhs.default_row_height)
+        && lhs.dy_descent == rhs.dy_descent;
+}
+
+} // namespace xlnt

+ 94 - 0
xlnt/include/xlnt/worksheet/sheet_pr.hpp

@@ -0,0 +1,94 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <string>
+
+#include <xlnt/xlnt_config.hpp>
+#include <xlnt/cell/cell_reference.hpp>
+#include <xlnt/utils/optional.hpp>
+
+namespace xlnt {
+
+struct XLNT_API sheet_pr
+{
+    /// <summary>
+    /// is horizontally synced to the anchor point
+    /// </summary>
+    optional<bool> sync_horizontal;
+
+    /// <summary>
+    /// is vertically synced to the anchor point
+    /// </summary>
+    optional<bool> sync_vertical;
+
+    /// <summary>
+    /// Anchor point for worksheet's window
+    /// </summary>
+    optional<cell_reference> sync_ref;
+
+    /// <summary>
+    /// Lotus compatibility option
+    /// </summary>
+    optional<bool> transition_evaluation;
+
+    /// <summary>
+    /// Lotus compatibility option
+    /// </summary>
+    optional<bool> transition_entry;
+
+    /// <summary>
+    /// worksheet is published
+    /// </summary>
+    optional<bool> published;
+
+    /// <summary>
+    /// stable name of the sheet
+    /// </summary>
+    optional<std::string> code_name;
+
+    /// <summary>
+    /// worksheet has one or more autofilters or advanced filters on
+    /// </summary>
+    optional<bool> filter_mode;
+
+    /// <summary>
+    /// whether the conditional formatting calculations shall be evaluated
+    /// </summary>
+    optional<bool> enable_format_condition_calculation;
+};
+
+inline bool operator==(const sheet_pr &lhs, const sheet_pr &rhs)
+{
+    return lhs.sync_horizontal == rhs.sync_horizontal
+        && lhs.sync_vertical == rhs.sync_vertical
+        && lhs.sync_ref == rhs.sync_ref
+        && lhs.transition_evaluation == rhs.transition_evaluation
+        && lhs.transition_entry == rhs.transition_entry
+        && lhs.published == rhs.published
+        && lhs.code_name == rhs.code_name
+        && lhs.filter_mode == rhs.filter_mode
+        && lhs.enable_format_condition_calculation == rhs.enable_format_condition_calculation;
+}
+} // namespace xlnt

+ 63 - 0
xlnt/include/xlnt/worksheet/sheet_protection.hpp

@@ -0,0 +1,63 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <string>
+
+#include <xlnt/xlnt_config.hpp>
+
+namespace xlnt {
+
+// TOOD: does this really need its own class?
+
+/// <summary>
+/// Protection applied to a particular worksheet to prevent it from being modified.
+/// </summary>
+class XLNT_API sheet_protection
+{
+public:
+    /// <summary>
+    /// Calculates and returns the hash of the given protection password.
+    /// </summary>
+    static std::string hash_password(const std::string &password);
+
+    /// <summary>
+    /// Sets the protection password to password.
+    /// </summary>
+    void password(const std::string &password);
+
+    /// <summary>
+    /// Returns the hash of the password set for this sheet protection.
+    /// </summary>
+    std::string hashed_password() const;
+
+private:
+    /// <summary>
+    /// The hash of the password.
+    /// </summary>
+    std::string hashed_password_;
+};
+
+} // namespace xlnt

+ 270 - 0
xlnt/include/xlnt/worksheet/sheet_view.hpp

@@ -0,0 +1,270 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <xlnt/xlnt_config.hpp>
+#include <xlnt/utils/optional.hpp>
+#include <xlnt/worksheet/pane.hpp>
+#include <xlnt/worksheet/selection.hpp>
+
+namespace xlnt {
+
+/// <summary>
+/// Enumeration of possible types of sheet views
+/// </summary>
+enum class sheet_view_type
+{
+    normal,
+    page_break_preview,
+    page_layout
+};
+
+/// <summary>
+/// Describes a view of a worksheet.
+/// Worksheets can have multiple views which show the data differently.
+/// </summary>
+class XLNT_API sheet_view
+{
+public:
+    /// <summary>
+    /// Sets the ID of this view to new_id.
+    /// </summary>
+    void id(std::size_t new_id)
+    {
+        id_ = new_id;
+    }
+
+    /// <summary>
+    /// Returns the ID of this view.
+    /// </summary>
+    std::size_t id() const
+    {
+        return id_;
+    }
+
+    /// <summary>
+    /// Returns true if this view has a pane defined.
+    /// </summary>
+    bool has_pane() const
+    {
+        return pane_.is_set();
+    }
+
+    /// <summary>
+    /// Returns a reference to this view's pane.
+    /// </summary>
+    struct pane &pane()
+    {
+        return pane_.get();
+    }
+
+    /// <summary>
+    /// Returns a reference to this view's pane.
+    /// </summary>
+    const struct pane &pane() const
+    {
+        return pane_.get();
+    }
+
+    /// <summary>
+    /// Removes the defined pane from this view.
+    /// </summary>
+    void clear_pane()
+    {
+        pane_.clear();
+    }
+
+    /// <summary>
+    /// Sets the pane of this view to new_pane.
+    /// </summary>
+    void pane(const struct pane &new_pane)
+    {
+        pane_ = new_pane;
+    }
+
+    /// <summary>
+    /// Returns true if this view has any selections.
+    /// </summary>
+    bool has_selections() const
+    {
+        return !selections_.empty();
+    }
+
+    /// <summary>
+    /// Adds the given selection to the collection of selections.
+    /// </summary>
+    void add_selection(const class selection &new_selection)
+    {
+        selections_.push_back(new_selection);
+    }
+
+    /// <summary>
+    /// Removes all selections.
+    /// </summary>
+    void clear_selections()
+    {
+        selections_.clear();
+    }
+
+    /// <summary>
+    /// Returns the collection of selections as a vector.
+    /// </summary>
+    std::vector<xlnt::selection> selections() const
+    {
+        return selections_;
+    }
+
+    /// <summary>
+    /// Returns the selection at the given index.
+    /// </summary>
+    class xlnt::selection &selection(std::size_t index)
+    {
+        return selections_.at(index);
+    }
+
+    /// <summary>
+    /// If show is true, grid lines will be shown for sheets using this view.
+    /// </summary>
+    void show_grid_lines(bool show)
+    {
+        show_grid_lines_ = show;
+    }
+
+    /// <summary>
+    /// Returns true if grid lines will be shown for sheets using this view.
+    /// </summary>
+    bool show_grid_lines() const
+    {
+        return show_grid_lines_;
+    }
+
+    /// <summary>
+    /// If is_default is true, the default grid color will be used.
+    /// </summary>
+    void default_grid_color(bool is_default)
+    {
+        default_grid_color_ = is_default;
+    }
+
+    /// <summary>
+    /// Returns true if the default grid color will be used.
+    /// </summary>
+    bool default_grid_color() const
+    {
+        return default_grid_color_;
+    }
+
+    /// <summary>
+    /// Sets the type of this view.
+    /// </summary>
+    void type(sheet_view_type new_type)
+    {
+        type_ = new_type;
+    }
+
+    /// <summary>
+    /// Returns the type of this view.
+    /// </summary>
+    sheet_view_type type() const
+    {
+        return type_;
+    }
+
+    /// <summary>
+    /// has a  top left cell?
+    /// </summary>
+    bool has_top_left_cell() const
+    {
+        return top_left_cell_.is_set();
+    }
+
+    /// <summary>
+    /// Sets the top left cell of this view.
+    /// </summary>
+    void top_left_cell(const cell_reference &ref)
+    {
+        top_left_cell_.set(ref);
+    }
+
+    /// <summary>
+    /// Returns the top left cell of this view.
+    /// </summary>
+    cell_reference top_left_cell() const
+    {
+        return top_left_cell_.get();
+    }
+
+    /// <summary>
+    /// Returns true if this view is equal to rhs based on its id, grid lines setting,
+    /// default grid color, pane, and selections.
+    /// </summary>
+    bool operator==(const sheet_view &rhs) const
+    {
+        return id_ == rhs.id_
+            && show_grid_lines_ == rhs.show_grid_lines_
+            && default_grid_color_ == rhs.default_grid_color_
+            && pane_ == rhs.pane_
+            && selections_ == rhs.selections_
+            && top_left_cell_ == rhs.top_left_cell_;
+    }
+
+private:
+    /// <summary>
+    /// The id
+    /// </summary>
+    std::size_t id_ = 0;
+
+    /// <summary>
+    /// Whether or not to show grid lines
+    /// </summary>
+    bool show_grid_lines_ = true;
+
+    /// <summary>
+    /// Whether or not to use the default grid color
+    /// </summary>
+    bool default_grid_color_ = true;
+
+    /// <summary>
+    /// The type of this view
+    /// </summary>
+    sheet_view_type type_ = sheet_view_type::normal;
+
+    /// <summary>
+    /// The optional pane
+    /// </summary>
+    optional<xlnt::pane> pane_;
+
+    /// <summary>
+    /// The top left cell
+    /// </summary>
+    optional<cell_reference> top_left_cell_;
+
+    /// <summary>
+    /// The collection of selections
+    /// </summary>
+    std::vector<xlnt::selection> selections_;
+};
+
+} // namespace xlnt

+ 841 - 0
xlnt/include/xlnt/worksheet/worksheet.hpp

@@ -0,0 +1,841 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <iterator>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <xlnt/xlnt_config.hpp>
+#include <xlnt/cell/index_types.hpp>
+#include <xlnt/packaging/relationship.hpp>
+#include <xlnt/worksheet/page_margins.hpp>
+#include <xlnt/worksheet/page_setup.hpp>
+#include <xlnt/worksheet/sheet_view.hpp>
+
+namespace xlnt {
+
+class cell;
+class cell_reference;
+class cell_vector;
+class column_properties;
+class comment;
+class condition;
+class conditional_format;
+class const_range_iterator;
+class footer;
+class header;
+class range;
+class range_iterator;
+class range_reference;
+class relationship;
+class row_properties;
+class sheet_format_properties;
+class workbook;
+class phonetic_pr;
+
+struct date;
+
+namespace detail {
+
+class xlsx_consumer;
+class xlsx_producer;
+
+struct worksheet_impl;
+
+} // namespace detail
+
+/// <summary>
+/// A worksheet is a 2D array of cells starting with cell A1 in the top-left corner
+/// and extending indefinitely down and right as needed.
+/// </summary>
+class XLNT_API worksheet
+{
+public:
+    /// <summary>
+    /// Iterate over a non-const worksheet with an iterator of this type.
+    /// </summary>
+    using iterator = range_iterator;
+
+    /// <summary>
+    /// Iterate over a non-const worksheet with an iterator of this type.
+    /// </summary>
+    using const_iterator = const_range_iterator;
+
+    /// <summary>
+    /// Iterate in reverse over a non-const worksheet with an iterator of this type.
+    /// </summary>
+    using reverse_iterator = std::reverse_iterator<iterator>;
+
+    /// <summary>
+    /// Iterate in reverse order over a const worksheet with an iterator of this type.
+    /// </summary>
+    using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+
+    /// <summary>
+    /// Construct a null worksheet. No methods should be called on such a worksheet.
+    /// </summary>
+    worksheet();
+
+    /// <summary>
+    /// Copy constructor. This worksheet will point to the same memory as rhs's worksheet.
+    /// </summary>
+    worksheet(const worksheet &rhs);
+
+    /// <summary>
+    /// Returns a reference to the workbook this worksheet is owned by.
+    /// </summary>
+    class workbook &workbook();
+
+    /// <summary>
+    /// Returns a reference to the workbook this worksheet is owned by.
+    /// </summary>
+    const class workbook &workbook() const;
+
+    /// <summary>
+    /// Deletes data held in the worksheet that does not affect the internal data or display.
+    /// For example, unreference styles and empty cells will be removed.
+    /// </summary>
+    void garbage_collect();
+
+    // identification
+
+    /// <summary>
+    /// Returns the unique numeric identifier of this worksheet. This will sometimes but not necessarily
+    /// be the index of the worksheet in the workbook.
+    /// </summary>
+    std::size_t id() const;
+
+    /// <summary>
+    /// Set the unique numeric identifier. The id defaults to the lowest unused id in the workbook
+    /// so this should not be called without a good reason.
+    /// </summary>
+    void id(std::size_t id);
+
+    /// <summary>
+    /// Returns the title of this sheet.
+    /// </summary>
+    std::string title() const;
+
+    /// <summary>
+    /// Sets the title of this sheet.
+    /// </summary>
+    void title(const std::string &title);
+
+    // freeze panes
+
+    /// <summary>
+    /// Returns the top left corner of the region above and to the left of which panes are frozen.
+    /// </summary>
+    cell_reference frozen_panes() const;
+
+    /// <summary>
+    /// Freeze panes above and to the left of top_left_cell.
+    /// </summary>
+    void freeze_panes(cell top_left_cell);
+
+    /// <summary>
+    /// Freeze panes above and to the left of top_left_coordinate.
+    /// </summary>
+    void freeze_panes(const cell_reference &top_left_coordinate);
+
+    /// <summary>
+    /// Remove frozen panes. The data in those panes will be unaffected--this affects only the view.
+    /// </summary>
+    void unfreeze_panes();
+
+    /// <summary>
+    /// Returns true if this sheet has a frozen row, frozen column, or both.
+    /// </summary>
+    bool has_frozen_panes() const;
+
+    // container
+
+    /// <summary>
+    /// Returns true if this sheet has an initialized cell at the given reference.
+    /// </summary>
+    bool has_cell(const cell_reference &reference) const;
+
+    /// <summary>
+    /// Returns the cell at the given reference. If the cell doesn't exist, it
+    /// will be initialized to null before being returned.
+    /// </summary>
+    class cell cell(const cell_reference &reference);
+
+    /// <summary>
+    /// Returns the cell at the given reference. If the cell doesn't exist, an
+    /// invalid_parameter exception will be thrown.
+    /// </summary>
+    const class cell cell(const cell_reference &reference) const;
+
+    /// <summary>
+    /// Returns the cell at the given column and row. If the cell doesn't exist, it
+    /// will be initialized to null before being returned.
+    /// </summary>
+    class cell cell(column_t column, row_t row);
+
+    /// <summary>
+    /// Returns the cell at the given column and row. If the cell doesn't exist, an
+    /// invalid_parameter exception will be thrown.
+    /// </summary>
+    const class cell cell(column_t column, row_t row) const;
+
+    /// <summary>
+    /// Returns the range defined by reference string. If reference string is the name of
+    /// a previously-defined named range in the sheet, it will be returned.
+    /// </summary>
+    class range range(const std::string &reference_string);
+
+    /// <summary>
+    /// Returns the range defined by reference string. If reference string is the name of
+    /// a previously-defined named range in the sheet, it will be returned.
+    /// </summary>
+    const class range range(const std::string &reference_string) const;
+
+    /// <summary>
+    /// Returns the range defined by reference.
+    /// </summary>
+    class range range(const range_reference &reference);
+
+    /// <summary>
+    /// Returns the range defined by reference.
+    /// </summary>
+    const class range range(const range_reference &reference) const;
+
+    /// <summary>
+    /// Returns a range encompassing all cells in this sheet which will
+    /// be iterated upon in row-major order. If skip_null is true (default),
+    /// empty rows and cells will be skipped during iteration of the range.
+    /// </summary>
+    class range rows(bool skip_null = true);
+
+    /// <summary>
+    /// Returns a range encompassing all cells in this sheet which will
+    /// be iterated upon in row-major order. If skip_null is true (default),
+    /// empty rows and cells will be skipped during iteration of the range.
+    /// </summary>
+    const class range rows(bool skip_null = true) const;
+
+    /// <summary>
+    /// Returns a range ecompassing all cells in this sheet which will
+    /// be iterated upon in column-major order. If skip_null is true (default),
+    /// empty columns and cells will be skipped during iteration of the range.
+    /// </summary>
+    class range columns(bool skip_null = true);
+
+    /// <summary>
+    /// Returns a range ecompassing all cells in this sheet which will
+    /// be iterated upon in column-major order. If skip_null is true (default),
+    /// empty columns and cells will be skipped during iteration of the range.
+    /// </summary>
+    const class range columns(bool skip_null = true) const;
+
+    //TODO: finish implementing cell_iterator wrapping before uncommenting
+    //class cell_vector cells(bool skip_null = true);
+
+    //TODO: finish implementing cell_iterator wrapping before uncommenting
+    //const class cell_vector cells(bool skip_null = true) const;
+
+    /// <summary>
+    /// Clears memory used by the given cell.
+    /// </summary>
+    void clear_cell(const cell_reference &ref);
+
+    /// <summary>
+    /// Clears memory used by all cells in the given row.
+    /// </summary>
+    void clear_row(row_t row);
+
+    /// <summary>
+    /// Insert empty rows before the given row index
+    /// </summary>
+    void insert_rows(row_t row, std::uint32_t amount);
+
+    /// <summary>
+    /// Insert empty columns before the given column index
+    /// </summary>
+    void insert_columns(column_t column, std::uint32_t amount);
+
+    /// <summary>
+    /// Delete rows before the given row index
+    /// </summary>
+    void delete_rows(row_t row, std::uint32_t amount);
+
+    /// <summary>
+    /// Delete columns before the given column index
+    /// </summary>
+    void delete_columns(column_t column, std::uint32_t amount);
+
+    // properties
+
+    /// <summary>
+    /// Returns the column properties for the given column.
+    /// </summary>
+    xlnt::column_properties &column_properties(column_t column);
+
+    /// <summary>
+    /// Returns the column properties for the given column.
+    /// </summary>
+    const xlnt::column_properties &column_properties(column_t column) const;
+
+    /// <summary>
+    /// Returns true if column properties have been set for the given column.
+    /// </summary>
+    bool has_column_properties(column_t column) const;
+
+    /// <summary>
+    /// Sets column properties for the given column to props.
+    /// </summary>
+    void add_column_properties(column_t column, const class column_properties &props);
+
+    /// <summary>
+    /// Calculates the width of the given column. This will be the default column width if
+    /// a custom width is not set on this column's column_properties.
+    /// </summary>
+    double column_width(column_t column) const;
+
+    /// <summary>
+    /// Returns the row properties for the given row.
+    /// </summary>
+    xlnt::row_properties &row_properties(row_t row);
+
+    /// <summary>
+    /// Returns the row properties for the given row.
+    /// </summary>
+    const xlnt::row_properties &row_properties(row_t row) const;
+
+    /// <summary>
+    /// Returns true if row properties have been set for the given row.
+    /// </summary>
+    bool has_row_properties(row_t row) const;
+
+    /// <summary>
+    /// Sets row properties for the given row to props.
+    /// </summary>
+    void add_row_properties(row_t row, const class row_properties &props);
+
+    /// <summary>
+    /// Calculate the height of the given row. This will be the default row height if
+    /// a custom height is not set on this row's row_properties.
+    /// </summary>
+    double row_height(row_t row) const;
+
+    // positioning
+
+    /// <summary>
+    /// Returns a reference to the cell at the given point coordinates.
+    /// </summary>
+    cell_reference point_pos(int left, int top) const;
+
+    // named range
+
+    /// <summary>
+    /// Creates a new named range with the given name encompassing the string representing a range.
+    /// </summary>
+    void create_named_range(const std::string &name, const std::string &reference_string);
+
+    /// <summary>
+    /// Creates a new named range with the given name encompassing the given range reference.
+    /// </summary>
+    void create_named_range(const std::string &name, const range_reference &reference);
+
+    /// <summary>
+    /// Returns true if this worksheet contains a named range with the given name.
+    /// </summary>
+    bool has_named_range(const std::string &name) const;
+
+    /// <summary>
+    /// Returns the named range with the given name. Throws key_not_found
+    /// exception if the named range doesn't exist.
+    /// </summary>
+    class range named_range(const std::string &name);
+
+    /// <summary>
+    /// Returns the named range with the given name. Throws key_not_found
+    /// exception if the named range doesn't exist.
+    /// </summary>
+    const class range named_range(const std::string &name) const;
+
+    /// <summary>
+    /// Removes a named range with the given name.
+    /// </summary>
+    void remove_named_range(const std::string &name);
+
+    // extents
+
+    /// <summary>
+    /// Returns the row of the first non-empty cell in the worksheet.
+    /// </summary>
+    row_t lowest_row() const;
+
+    /// <summary>
+    /// Returns the row of the first non-empty cell or lowest row with properties in the worksheet.
+    /// </summary>
+    row_t lowest_row_or_props() const;
+
+    /// <summary>
+    /// Returns the row of the last non-empty cell in the worksheet.
+    /// </summary>
+    row_t highest_row() const;
+
+    /// <summary>
+    /// Returns the row of the last non-empty cell or highest row with properties in the worksheet.
+    /// </summary>
+    row_t highest_row_or_props() const;
+
+    /// <summary>
+    /// Returns the row directly below the last non-empty cell in the worksheet.
+    /// </summary>
+    row_t next_row() const;
+
+    /// <summary>
+    /// Returns the column of the first non-empty cell in the worksheet.
+    /// </summary>
+    column_t lowest_column() const;
+
+    /// <summary>
+    /// Returns the column of the first non-empty cell or lowest column with properties in the worksheet.
+    /// </summary>
+    column_t lowest_column_or_props() const;
+
+    /// <summary>
+    /// Returns the column of the last non-empty cell in the worksheet.
+    /// </summary>
+    column_t highest_column() const;
+
+    /// <summary>
+    /// Returns the column of the last non-empty cell or highest column with properties in the worksheet.
+    /// </summary>
+    column_t highest_column_or_props() const;
+
+    /// <summary>
+    /// Returns a range_reference pointing to the full range of cells in the worksheet.
+    /// If skip_null is true (default), empty cells (For example if the first row or column is empty)
+    /// will not be included in upper left bound of range.
+    /// </summary>
+    range_reference calculate_dimension(bool skip_null=true) const;
+
+    // cell merge
+
+    /// <summary>
+    /// Merges the cells within the range represented by the given string.
+    /// </summary>
+    void merge_cells(const std::string &reference_string);
+
+    /// <summary>
+    /// Merges the cells within the given range.
+    /// </summary>
+    void merge_cells(const range_reference &reference);
+
+    /// <summary>
+    /// Removes the merging of the cells in the range represented by the given string.
+    /// </summary>
+    void unmerge_cells(const std::string &reference_string);
+
+    /// <summary>
+    /// Removes the merging of the cells in the given range.
+    /// </summary>
+    void unmerge_cells(const range_reference &reference);
+
+    /// <summary>
+    /// Returns a vector of references of all merged ranges in the worksheet.
+    /// </summary>
+    std::vector<range_reference> merged_ranges() const;
+
+    // operators
+
+    /// <summary>
+    /// Returns true if this worksheet refers to the same worksheet as other.
+    /// </summary>
+    bool operator==(const worksheet &other) const;
+
+    /// <summary>
+    /// Returns true if this worksheet doesn't refer to the same worksheet as other.
+    /// </summary>
+    bool operator!=(const worksheet &other) const;
+
+    /// <summary>
+    /// Returns true if this worksheet is null.
+    /// </summary>
+    bool operator==(std::nullptr_t) const;
+
+    /// <summary>
+    /// Returns true if this worksheet is not null.
+    /// </summary>
+    bool operator!=(std::nullptr_t) const;
+
+    /// <summary>
+    /// Sets the internal pointer of this worksheet object to point to other.
+    /// </summary>
+    void operator=(const worksheet &other);
+
+    /// <summary>
+    /// Convenience method for worksheet::cell method.
+    /// </summary>
+    class cell operator[](const cell_reference &reference);
+
+    /// <summary>
+    /// Convenience method for worksheet::cell method.
+    /// </summary>
+    const class cell operator[](const cell_reference &reference) const;
+
+    /// <summary>
+    /// Returns true if this worksheet is equal to other. If reference is true, the comparison
+    /// will only check that both worksheets point to the same sheet in the same workbook.
+    /// </summary>
+    bool compare(const worksheet &other, bool reference) const;
+
+    // page
+
+    /// <summary>
+    /// Returns true if this worksheet has a page setup.
+    /// </summary>
+    bool has_page_setup() const;
+
+    /// <summary>
+    /// Returns the page setup for this sheet.
+    /// </summary>
+    xlnt::page_setup page_setup() const;
+
+    /// <summary>
+    /// Sets the page setup for this sheet to setup.
+    /// </summary>
+    void page_setup(const struct page_setup &setup);
+
+    /// <summary>
+    /// Returns true if this page has margins.
+    /// </summary>
+    bool has_page_margins() const;
+
+    /// <summary>
+    /// Returns the margins of this sheet.
+    /// </summary>
+    xlnt::page_margins page_margins() const;
+
+    /// <summary>
+    /// Sets the margins of this sheet to margins.
+    /// </summary>
+    void page_margins(const class page_margins &margins);
+
+    // auto filter
+
+    /// <summary>
+    /// Returns the current auto-filter of this sheet.
+    /// </summary>
+    range_reference auto_filter() const;
+
+    /// <summary>
+    /// Sets the auto-filter of this sheet to the range defined by range_string.
+    /// </summary>
+    void auto_filter(const std::string &range_string);
+
+    /// <summary>
+    /// Sets the auto-filter of this sheet to the given range.
+    /// </summary>
+    void auto_filter(const xlnt::range &range);
+
+    /// <summary>
+    /// Sets the auto-filter of this sheet to the range defined by reference.
+    /// </summary>
+    void auto_filter(const range_reference &reference);
+
+    /// <summary>
+    /// Clear the auto-filter from this sheet.
+    /// </summary>
+    void clear_auto_filter();
+
+    /// <summary>
+    /// Returns true if this sheet has an auto-filter set.
+    /// </summary>
+    bool has_auto_filter() const;
+
+    /// <summary>
+    /// Reserve n rows. This can be optionally called before adding many rows
+    /// to improve performance.
+    /// </summary>
+    void reserve(std::size_t n);
+
+    /// <summary>
+    /// Returns true if this sheet has phonetic properties
+    /// </summary>
+    bool has_phonetic_properties() const;
+
+    /// <summary>
+    /// Returns the phonetic properties of this sheet.
+    /// </summary>
+    const phonetic_pr &phonetic_properties() const;
+
+    /// <summary>
+    /// Sets the phonetic properties of this sheet to phonetic_props
+    /// </summary>
+    void phonetic_properties(const phonetic_pr &phonetic_props);
+
+    /// <summary>
+    /// Returns true if this sheet has a header/footer.
+    /// </summary>
+    bool has_header_footer() const;
+
+    /// <summary>
+    /// Returns the header/footer of this sheet.
+    /// </summary>
+    class header_footer header_footer() const;
+
+    /// <summary>
+    /// Sets the header/footer of this sheet to new_header_footer.
+    /// </summary>
+    void header_footer(const class header_footer &new_header_footer);
+
+    /// <summary>
+    /// Returns the sheet state of this sheet.
+    /// </summary>
+    xlnt::sheet_state sheet_state() const;
+
+    /// <summary>
+    /// Sets the sheet state of this sheet.
+    /// </summary>
+    void sheet_state(xlnt::sheet_state state);
+
+    /// <summary>
+    /// Returns an iterator to the first row in this sheet.
+    /// </summary>
+    iterator begin();
+
+    /// <summary>
+    /// Returns an iterator one past the last row in this sheet.
+    /// </summary>
+    iterator end();
+
+    /// <summary>
+    /// Return a constant iterator to the first row in this sheet.
+    /// </summary>
+    const_iterator begin() const;
+
+    /// <summary>
+    /// Returns a constant iterator to one past the last row in this sheet.
+    /// </summary>
+    const_iterator end() const;
+
+    /// <summary>
+    /// Return a constant iterator to the first row in this sheet.
+    /// </summary>
+    const_iterator cbegin() const;
+
+    /// <summary>
+    /// Returns a constant iterator to one past the last row in this sheet.
+    /// </summary>
+    const_iterator cend() const;
+
+    /// <summary>
+    /// Sets rows to repeat at top during printing.
+    /// </summary>
+    void print_title_rows(row_t start, row_t end);
+
+    /// <summary>
+    /// Get rows to repeat at top during printing.
+    /// </summary>
+    optional<std::pair<row_t, row_t>> print_title_rows() const;
+
+    /// <summary>
+    /// Sets columns to repeat at left during printing.
+    /// </summary>
+    void print_title_cols(column_t start, column_t end);
+    
+    /// <summary>
+    /// Get columns to repeat at left during printing.
+    /// </summary>
+    optional<std::pair<column_t, column_t>> print_title_cols() const;
+
+    /// <summary>
+    /// Returns true if the sheet has print titles defined.
+    /// </summary>
+    bool has_print_titles() const;
+
+    /// <summary>
+    /// Remove all print titles.
+    /// </summary>
+    void clear_print_titles();
+
+    /// <summary>
+    /// Sets the print area of this sheet to print_area.
+    /// </summary>
+    void print_area(const std::string &print_area);
+
+    /// <summary>
+    /// Clear the print area of this sheet.
+    /// </summary>
+    void clear_print_area();
+
+    /// <summary>
+    /// Returns the print area defined for this sheet.
+    /// </summary>
+    range_reference print_area() const;
+    
+    /// <summary>
+    /// Returns true if the print area is defined for this sheet.
+    /// </summary>
+    bool has_print_area() const;
+
+    /// <summary>
+    /// Returns true if this sheet has any number of views defined.
+    /// </summary>
+    bool has_view() const;
+
+    /// <summary>
+    /// Returns the view at the given index.
+    /// </summary>
+    sheet_view &view(std::size_t index = 0) const;
+
+    /// <summary>
+    /// Adds new_view to the set of available views for this sheet.
+    /// </summary>
+    void add_view(const sheet_view &new_view);
+
+    /// <summary>
+    /// Set the active cell on the default worksheet view to the given reference.
+    /// </summary>
+    void active_cell(const cell_reference &ref);
+
+    /// <summary>
+    /// Returns true if the worksheet has a view and the view has an active cell.
+    /// </summary>
+    bool has_active_cell() const;
+
+    /// <summary>
+    /// Returns the active cell on the default worksheet view.
+    /// </summary>
+    cell_reference active_cell() const;
+
+    // page breaks
+
+    /// <summary>
+    /// Remove all manual column and row page breaks (represented as dashed
+    /// blue lines in the page view in Excel).
+    /// </summary>
+    void clear_page_breaks();
+
+    /// <summary>
+    /// Returns vector where each element represents a row which will break a page below it.
+    /// </summary>
+    const std::vector<row_t> &page_break_rows() const;
+
+    /// <summary>
+    /// Add a page break at the given row.
+    /// </summary>
+    void page_break_at_row(row_t row);
+
+    /// <summary>
+    /// Returns vector where each element represents a column which will break a page to the right.
+    /// </summary>
+    const std::vector<column_t> &page_break_columns() const;
+
+    /// <summary>
+    /// Add a page break at the given column.
+    /// </summary>
+    void page_break_at_column(column_t column);
+
+    /// <summary>
+    /// Creates a conditional format for the given range with the given condition.
+    /// </summary>
+    xlnt::conditional_format conditional_format(const range_reference &ref, const condition &when);
+
+    /// <summary>
+    /// Returns the path of this worksheet in the containing package.
+    /// </summary>
+    xlnt::path path() const;
+
+    /// <summary>
+    /// Returns the relationship from the parent workbook to this worksheet.
+    /// </summary>
+    relationship referring_relationship() const;
+
+    /// <summary>
+    /// Returns the current formatting properties.
+    /// </summary>
+    sheet_format_properties format_properties() const;
+
+    /// <summary>
+    /// Sets the format properties to the given properties.
+    /// </summary>
+    void format_properties(const sheet_format_properties &properties);
+
+    /// <summary>
+    /// Returns true if this worksheet has a page setup.
+    /// </summary>
+    bool has_drawing() const;
+
+    /// <summary>
+    /// Returns true if this worksheet is empty.
+    /// A worksheet is considered empty if it doesn't have any cells.
+    /// </summary>
+    bool is_empty() const;
+
+private:
+    friend class cell;
+    friend class const_range_iterator;
+    friend class range_iterator;
+    friend class workbook;
+    friend class detail::xlsx_consumer;
+    friend class detail::xlsx_producer;
+
+    /// <summary>
+    /// Constructs a worksheet impl wrapper from d.
+    /// </summary>
+    worksheet(detail::worksheet_impl *d);
+
+    /// <summary>
+    /// Creates a comments part in the manifest as a relationship target of this sheet.
+    /// </summary>
+    void register_comments_in_manifest();
+
+    /// <summary>
+    /// Creates a calcChain part in the manifest if it doesn't already exist.
+    /// </summary>
+    void register_calc_chain_in_manifest();
+
+    /// <summary>
+    /// Removes calcChain part from manifest if no formulae remain in workbook.
+    /// </summary>
+    void garbage_collect_formulae();
+
+    /// <summary>
+    /// Sets the parent of this worksheet to wb.
+    /// </summary>
+    void parent(class workbook &wb);
+
+    /// <summary>
+    /// Move cells after index down or right by a given amount. The direction is decided by row_or_col.
+    /// If reverse is true, the cells will be moved up or left, depending on row_or_col.
+    /// </summary>
+    void move_cells(std::uint32_t index, std::uint32_t amount, row_or_col_t row_or_col, bool reverse = false);
+
+    /// <summary>
+    /// The pointer to this sheet's implementation.
+    /// </summary>
+    detail::worksheet_impl *d_;
+};
+
+} // namespace xlnt

+ 92 - 0
xlnt/include/xlnt/xlnt.hpp

@@ -0,0 +1,92 @@
+// Copyright (c) 2014-2021 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, ARISING 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 <xlnt/xlnt_config.hpp>
+
+// cell
+#include <xlnt/cell/cell.hpp>
+#include <xlnt/cell/cell_reference.hpp>
+#include <xlnt/cell/cell_type.hpp>
+#include <xlnt/cell/comment.hpp>
+#include <xlnt/cell/hyperlink.hpp>
+#include <xlnt/cell/index_types.hpp>
+#include <xlnt/cell/rich_text.hpp>
+#include <xlnt/cell/rich_text_run.hpp>
+
+// packaging
+#include <xlnt/packaging/manifest.hpp>
+#include <xlnt/packaging/relationship.hpp>
+#include <xlnt/packaging/uri.hpp>
+
+// styles
+#include <xlnt/styles/alignment.hpp>
+#include <xlnt/styles/border.hpp>
+#include <xlnt/styles/color.hpp>
+#include <xlnt/styles/fill.hpp>
+#include <xlnt/styles/font.hpp>
+#include <xlnt/styles/format.hpp>
+#include <xlnt/styles/number_format.hpp>
+#include <xlnt/styles/protection.hpp>
+#include <xlnt/styles/style.hpp>
+
+// utils
+#include <xlnt/utils/calendar.hpp>
+#include <xlnt/utils/date.hpp>
+#include <xlnt/utils/datetime.hpp>
+#include <xlnt/utils/exceptions.hpp>
+#include <xlnt/utils/path.hpp>
+#include <xlnt/utils/time.hpp>
+#include <xlnt/utils/timedelta.hpp>
+#include <xlnt/utils/variant.hpp>
+
+// workbook
+#include <xlnt/workbook/document_security.hpp>
+#include <xlnt/workbook/external_book.hpp>
+#include <xlnt/workbook/metadata_property.hpp>
+#include <xlnt/workbook/named_range.hpp>
+#include <xlnt/workbook/streaming_workbook_reader.hpp>
+#include <xlnt/workbook/streaming_workbook_writer.hpp>
+#include <xlnt/workbook/theme.hpp>
+#include <xlnt/workbook/workbook.hpp>
+#include <xlnt/workbook/worksheet_iterator.hpp>
+
+// worksheet
+#include <xlnt/worksheet/cell_iterator.hpp>
+#include <xlnt/worksheet/cell_vector.hpp>
+#include <xlnt/worksheet/column_properties.hpp>
+#include <xlnt/worksheet/header_footer.hpp>
+#include <xlnt/worksheet/major_order.hpp>
+#include <xlnt/worksheet/page_margins.hpp>
+#include <xlnt/worksheet/page_setup.hpp>
+#include <xlnt/worksheet/pane.hpp>
+#include <xlnt/worksheet/range.hpp>
+#include <xlnt/worksheet/range_iterator.hpp>
+#include <xlnt/worksheet/range_reference.hpp>
+#include <xlnt/worksheet/row_properties.hpp>
+#include <xlnt/worksheet/selection.hpp>
+#include <xlnt/worksheet/sheet_format_properties.hpp>
+#include <xlnt/worksheet/sheet_protection.hpp>
+#include <xlnt/worksheet/sheet_view.hpp>
+#include <xlnt/worksheet/worksheet.hpp>

+ 43 - 0
xlnt/include/xlnt/xlnt_config.hpp

@@ -0,0 +1,43 @@
+// Copyright (c) 2014-2021 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, ARISING 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
+
+#ifndef XLNT_API
+#if !defined(XLNT_STATIC) && defined(_MSC_VER)
+#ifdef XLNT_EXPORT
+#define XLNT_API __declspec(dllexport)
+#else
+#ifdef XLNT_SHARED
+// For clients of the library, supress warnings about DLL interfaces for standard library classes
+#pragma warning(disable : 4251)
+#pragma warning(disable : 4275)
+#define XLNT_API __declspec(dllimport)
+#else
+#define XLNT_API
+#endif
+#endif
+#else
+#define XLNT_API
+#endif
+#endif

+ 31 - 0
xlnt/libstudxml.sln

@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.4.33122.133
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libstudxml", "libstudxml.vcxproj", "{E16ADE97-86E3-39D3-B8E3-5C160FC2BD59}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Win32 = Debug|Win32
+		MinSizeRel|Win32 = MinSizeRel|Win32
+		Release|Win32 = Release|Win32
+		RelWithDebInfo|Win32 = RelWithDebInfo|Win32
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{E16ADE97-86E3-39D3-B8E3-5C160FC2BD59}.Debug|Win32.ActiveCfg = Debug|Win32
+		{E16ADE97-86E3-39D3-B8E3-5C160FC2BD59}.Debug|Win32.Build.0 = Debug|Win32
+		{E16ADE97-86E3-39D3-B8E3-5C160FC2BD59}.MinSizeRel|Win32.ActiveCfg = MinSizeRel|Win32
+		{E16ADE97-86E3-39D3-B8E3-5C160FC2BD59}.MinSizeRel|Win32.Build.0 = MinSizeRel|Win32
+		{E16ADE97-86E3-39D3-B8E3-5C160FC2BD59}.Release|Win32.ActiveCfg = Release|Win32
+		{E16ADE97-86E3-39D3-B8E3-5C160FC2BD59}.Release|Win32.Build.0 = Release|Win32
+		{E16ADE97-86E3-39D3-B8E3-5C160FC2BD59}.RelWithDebInfo|Win32.ActiveCfg = RelWithDebInfo|Win32
+		{E16ADE97-86E3-39D3-B8E3-5C160FC2BD59}.RelWithDebInfo|Win32.Build.0 = RelWithDebInfo|Win32
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {18D6C785-B2BB-38B3-9803-F3F951FCC3CC}
+	EndGlobalSection
+EndGlobal

+ 318 - 0
xlnt/libstudxml.vcxproj

@@ -0,0 +1,318 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="17.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <PreferredToolArchitecture>x86</PreferredToolArchitecture>
+  </PropertyGroup>
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="MinSizeRel|Win32">
+      <Configuration>MinSizeRel</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="RelWithDebInfo|Win32">
+      <Configuration>RelWithDebInfo</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{E16ADE97-86E3-39D3-B8E3-5C160FC2BD59}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <WindowsTargetPlatformVersion>10.0.22000.0</WindowsTargetPlatformVersion>
+    <Platform>Win32</Platform>
+    <ProjectName>libstudxml</ProjectName>
+    <VCProjectUpgraderObjectName>NoUpgrade</VCProjectUpgraderObjectName>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <CharacterSet>MultiByte</CharacterSet>
+    <PlatformToolset>v143</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <CharacterSet>MultiByte</CharacterSet>
+    <PlatformToolset>v143</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='MinSizeRel|Win32'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <CharacterSet>MultiByte</CharacterSet>
+    <PlatformToolset>v143</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|Win32'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <CharacterSet>MultiByte</CharacterSet>
+    <PlatformToolset>v143</PlatformToolset>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup>
+    <_ProjectFileVersion>10.0.20506.1</_ProjectFileVersion>
+    <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">libstudxml</TargetName>
+    <TargetExt Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.lib</TargetExt>
+    <TargetName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">libstudxml</TargetName>
+    <TargetExt Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.lib</TargetExt>
+    <TargetName Condition="'$(Configuration)|$(Platform)'=='MinSizeRel|Win32'">libstudxml</TargetName>
+    <TargetExt Condition="'$(Configuration)|$(Platform)'=='MinSizeRel|Win32'">.lib</TargetExt>
+    <TargetName Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|Win32'">libstudxml</TargetName>
+    <TargetExt Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|Win32'">.lib</TargetExt>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <AdditionalIncludeDirectories>third-party\libstudxml;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalOptions>%(AdditionalOptions) /utf-8</AdditionalOptions>
+      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+      <ExceptionHandling>Sync</ExceptionHandling>
+      <InlineFunctionExpansion>Disabled</InlineFunctionExpansion>
+      <Optimization>Disabled</Optimization>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+      <RuntimeTypeInfo>true</RuntimeTypeInfo>
+      <UseFullPaths>false</UseFullPaths>
+      <WarningLevel>Level3</WarningLevel>
+      <PreprocessorDefinitions>%(PreprocessorDefinitions);WIN32;_WINDOWS;LIBSTUDXML_STATIC_LIB=1;_CRT_SECURE_NO_WARNINGS=1;CMAKE_INTDIR="Debug"</PreprocessorDefinitions>
+      <ObjectFileName>$(IntDir)</ObjectFileName>
+    </ClCompile>
+    <ResourceCompile>
+      <PreprocessorDefinitions>%(PreprocessorDefinitions);WIN32;_DEBUG;_WINDOWS;LIBSTUDXML_STATIC_LIB=1;_CRT_SECURE_NO_WARNINGS=1;CMAKE_INTDIR=\"Debug\"</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>E:\DevCode\xlnt\third-party\libstudxml.build\..\libstudxml;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ResourceCompile>
+    <Midl>
+      <AdditionalIncludeDirectories>E:\DevCode\xlnt\third-party\libstudxml.build\..\libstudxml;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <OutputDirectory>$(ProjectDir)/$(IntDir)</OutputDirectory>
+      <HeaderFileName>%(Filename).h</HeaderFileName>
+      <TypeLibraryName>%(Filename).tlb</TypeLibraryName>
+      <InterfaceIdentifierFileName>%(Filename)_i.c</InterfaceIdentifierFileName>
+      <ProxyFileName>%(Filename)_p.c</ProxyFileName>
+    </Midl>
+    <Lib>
+      <AdditionalOptions>%(AdditionalOptions) /machine:X86</AdditionalOptions>
+    </Lib>
+    <PostBuildEvent>
+      <Command>copy $(OutDir)$(TargetName)$(TargetExt) $(TargetName)$(TargetExt) /y/a
+</Command>
+    </PostBuildEvent>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <AdditionalIncludeDirectories>third-party\libstudxml;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalOptions>%(AdditionalOptions) /utf-8</AdditionalOptions>
+      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+      <ExceptionHandling>Sync</ExceptionHandling>
+      <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
+      <Optimization>MaxSpeed</Optimization>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+      <RuntimeTypeInfo>true</RuntimeTypeInfo>
+      <UseFullPaths>false</UseFullPaths>
+      <WarningLevel>Level3</WarningLevel>
+      <PreprocessorDefinitions>%(PreprocessorDefinitions);WIN32;_WINDOWS;NDEBUG;LIBSTUDXML_STATIC_LIB=1;_CRT_SECURE_NO_WARNINGS=1;CMAKE_INTDIR="Release"</PreprocessorDefinitions>
+      <ObjectFileName>$(IntDir)</ObjectFileName>
+      <DebugInformationFormat>
+      </DebugInformationFormat>
+    </ClCompile>
+    <ResourceCompile>
+      <PreprocessorDefinitions>%(PreprocessorDefinitions);WIN32;_WINDOWS;NDEBUG;LIBSTUDXML_STATIC_LIB=1;_CRT_SECURE_NO_WARNINGS=1;CMAKE_INTDIR=\"Release\"</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>E:\DevCode\xlnt\third-party\libstudxml.build\..\libstudxml;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ResourceCompile>
+    <Midl>
+      <AdditionalIncludeDirectories>E:\DevCode\xlnt\third-party\libstudxml.build\..\libstudxml;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <OutputDirectory>$(ProjectDir)/$(IntDir)</OutputDirectory>
+      <HeaderFileName>%(Filename).h</HeaderFileName>
+      <TypeLibraryName>%(Filename).tlb</TypeLibraryName>
+      <InterfaceIdentifierFileName>%(Filename)_i.c</InterfaceIdentifierFileName>
+      <ProxyFileName>%(Filename)_p.c</ProxyFileName>
+    </Midl>
+    <Lib>
+      <AdditionalOptions>%(AdditionalOptions) /machine:X86</AdditionalOptions>
+    </Lib>
+    <PostBuildEvent>
+      <Command>copy $(OutDir)$(TargetName)$(TargetExt) $(TargetName)$(TargetExt) /y/a</Command>
+    </PostBuildEvent>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='MinSizeRel|Win32'">
+    <ClCompile>
+      <AdditionalIncludeDirectories>third-party\libstudxml;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalOptions>%(AdditionalOptions) /utf-8</AdditionalOptions>
+      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+      <ExceptionHandling>Sync</ExceptionHandling>
+      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+      <Optimization>MinSpace</Optimization>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+      <RuntimeTypeInfo>true</RuntimeTypeInfo>
+      <UseFullPaths>false</UseFullPaths>
+      <WarningLevel>Level3</WarningLevel>
+      <PreprocessorDefinitions>%(PreprocessorDefinitions);WIN32;_WINDOWS;NDEBUG;LIBSTUDXML_STATIC_LIB=1;_CRT_SECURE_NO_WARNINGS=1;CMAKE_INTDIR="MinSizeRel"</PreprocessorDefinitions>
+      <ObjectFileName>$(IntDir)</ObjectFileName>
+      <DebugInformationFormat>
+      </DebugInformationFormat>
+    </ClCompile>
+    <ResourceCompile>
+      <PreprocessorDefinitions>%(PreprocessorDefinitions);WIN32;_WINDOWS;NDEBUG;LIBSTUDXML_STATIC_LIB=1;_CRT_SECURE_NO_WARNINGS=1;CMAKE_INTDIR=\"MinSizeRel\"</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>E:\DevCode\xlnt\third-party\libstudxml.build\..\libstudxml;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ResourceCompile>
+    <Midl>
+      <AdditionalIncludeDirectories>E:\DevCode\xlnt\third-party\libstudxml.build\..\libstudxml;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <OutputDirectory>$(ProjectDir)/$(IntDir)</OutputDirectory>
+      <HeaderFileName>%(Filename).h</HeaderFileName>
+      <TypeLibraryName>%(Filename).tlb</TypeLibraryName>
+      <InterfaceIdentifierFileName>%(Filename)_i.c</InterfaceIdentifierFileName>
+      <ProxyFileName>%(Filename)_p.c</ProxyFileName>
+    </Midl>
+    <Lib>
+      <AdditionalOptions>%(AdditionalOptions) /machine:X86</AdditionalOptions>
+    </Lib>
+    <PostBuildEvent>
+      <Command>copy $(OutDir)$(TargetName)$(TargetExt) $(TargetName)$(TargetExt) /y/a</Command>
+    </PostBuildEvent>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|Win32'">
+    <ClCompile>
+      <AdditionalIncludeDirectories>third-party\libstudxml;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalOptions>%(AdditionalOptions) /utf-8</AdditionalOptions>
+      <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+      <ExceptionHandling>Sync</ExceptionHandling>
+      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+      <Optimization>MaxSpeed</Optimization>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+      <RuntimeTypeInfo>true</RuntimeTypeInfo>
+      <UseFullPaths>false</UseFullPaths>
+      <WarningLevel>Level3</WarningLevel>
+      <PreprocessorDefinitions>%(PreprocessorDefinitions);WIN32;_WINDOWS;NDEBUG;LIBSTUDXML_STATIC_LIB=1;_CRT_SECURE_NO_WARNINGS=1;CMAKE_INTDIR="RelWithDebInfo"</PreprocessorDefinitions>
+      <ObjectFileName>$(IntDir)</ObjectFileName>
+    </ClCompile>
+    <ResourceCompile>
+      <PreprocessorDefinitions>%(PreprocessorDefinitions);WIN32;_WINDOWS;NDEBUG;LIBSTUDXML_STATIC_LIB=1;_CRT_SECURE_NO_WARNINGS=1;CMAKE_INTDIR=\"RelWithDebInfo\"</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>E:\DevCode\xlnt\third-party\libstudxml.build\..\libstudxml;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ResourceCompile>
+    <Midl>
+      <AdditionalIncludeDirectories>E:\DevCode\xlnt\third-party\libstudxml.build\..\libstudxml;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <OutputDirectory>$(ProjectDir)/$(IntDir)</OutputDirectory>
+      <HeaderFileName>%(Filename).h</HeaderFileName>
+      <TypeLibraryName>%(Filename).tlb</TypeLibraryName>
+      <InterfaceIdentifierFileName>%(Filename)_i.c</InterfaceIdentifierFileName>
+      <ProxyFileName>%(Filename)_p.c</ProxyFileName>
+    </Midl>
+    <Lib>
+      <AdditionalOptions>%(AdditionalOptions) /machine:X86</AdditionalOptions>
+    </Lib>
+    <PostBuildEvent>
+      <Command>copy $(OutDir)$(TargetName)$(TargetExt) $(TargetName)$(TargetExt) /y/a</Command>
+    </PostBuildEvent>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <CustomBuild Include="E:\DevCode\xlnt\third-party\libstudxml.build\CMakeLists.txt">
+      <UseUtf8Encoding>Always</UseUtf8Encoding>
+      <Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Building Custom Rule E:/DevCode/xlnt/third-party/libstudxml.build/CMakeLists.txt</Message>
+      <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">setlocal
+"D:\Program Files\cmake-3.25.0\bin\cmake.exe" -SE:/DevCode/xlnt -BE:/DevCode/xlnt/build --check-stamp-file E:/DevCode/xlnt/build/source/third-party/libstudxml/CMakeFiles/generate.stamp
+if %errorlevel% neq 0 goto :cmEnd
+:cmEnd
+endlocal &amp; call :cmErrorLevel %errorlevel% &amp; goto :cmDone
+:cmErrorLevel
+exit /b %1
+:cmDone
+if %errorlevel% neq 0 goto :VCEnd</Command>
+      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalInputs)</AdditionalInputs>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">E:\DevCode\xlnt\build\source\third-party\libstudxml\CMakeFiles\generate.stamp</Outputs>
+      <LinkObjects Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</LinkObjects>
+      <Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Building Custom Rule E:/DevCode/xlnt/third-party/libstudxml.build/CMakeLists.txt</Message>
+      <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">setlocal
+"D:\Program Files\cmake-3.25.0\bin\cmake.exe" -SE:/DevCode/xlnt -BE:/DevCode/xlnt/build --check-stamp-file E:/DevCode/xlnt/build/source/third-party/libstudxml/CMakeFiles/generate.stamp
+if %errorlevel% neq 0 goto :cmEnd
+:cmEnd
+endlocal &amp; call :cmErrorLevel %errorlevel% &amp; goto :cmDone
+:cmErrorLevel
+exit /b %1
+:cmDone
+if %errorlevel% neq 0 goto :VCEnd</Command>
+      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalInputs)</AdditionalInputs>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">E:\DevCode\xlnt\build\source\third-party\libstudxml\CMakeFiles\generate.stamp</Outputs>
+      <LinkObjects Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkObjects>
+      <Message Condition="'$(Configuration)|$(Platform)'=='MinSizeRel|Win32'">Building Custom Rule E:/DevCode/xlnt/third-party/libstudxml.build/CMakeLists.txt</Message>
+      <Command Condition="'$(Configuration)|$(Platform)'=='MinSizeRel|Win32'">setlocal
+"D:\Program Files\cmake-3.25.0\bin\cmake.exe" -SE:/DevCode/xlnt -BE:/DevCode/xlnt/build --check-stamp-file E:/DevCode/xlnt/build/source/third-party/libstudxml/CMakeFiles/generate.stamp
+if %errorlevel% neq 0 goto :cmEnd
+:cmEnd
+endlocal &amp; call :cmErrorLevel %errorlevel% &amp; goto :cmDone
+:cmErrorLevel
+exit /b %1
+:cmDone
+if %errorlevel% neq 0 goto :VCEnd</Command>
+      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='MinSizeRel|Win32'">%(AdditionalInputs)</AdditionalInputs>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='MinSizeRel|Win32'">E:\DevCode\xlnt\build\source\third-party\libstudxml\CMakeFiles\generate.stamp</Outputs>
+      <LinkObjects Condition="'$(Configuration)|$(Platform)'=='MinSizeRel|Win32'">false</LinkObjects>
+      <Message Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|Win32'">Building Custom Rule E:/DevCode/xlnt/third-party/libstudxml.build/CMakeLists.txt</Message>
+      <Command Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|Win32'">setlocal
+"D:\Program Files\cmake-3.25.0\bin\cmake.exe" -SE:/DevCode/xlnt -BE:/DevCode/xlnt/build --check-stamp-file E:/DevCode/xlnt/build/source/third-party/libstudxml/CMakeFiles/generate.stamp
+if %errorlevel% neq 0 goto :cmEnd
+:cmEnd
+endlocal &amp; call :cmErrorLevel %errorlevel% &amp; goto :cmDone
+:cmErrorLevel
+exit /b %1
+:cmDone
+if %errorlevel% neq 0 goto :VCEnd</Command>
+      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|Win32'">%(AdditionalInputs)</AdditionalInputs>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|Win32'">E:\DevCode\xlnt\build\source\third-party\libstudxml\CMakeFiles\generate.stamp</Outputs>
+      <LinkObjects Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|Win32'">false</LinkObjects>
+    </CustomBuild>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="E:\DevCode\xlnt\third-party\libstudxml\xml\parser.cxx" />
+    <ClCompile Include="E:\DevCode\xlnt\third-party\libstudxml\xml\qname.cxx" />
+    <ClCompile Include="E:\DevCode\xlnt\third-party\libstudxml\xml\serializer.cxx" />
+    <ClCompile Include="E:\DevCode\xlnt\third-party\libstudxml\xml\value-traits.cxx" />
+    <None Include="E:\DevCode\xlnt\third-party\libstudxml\xml\content">
+    </None>
+    <None Include="E:\DevCode\xlnt\third-party\libstudxml\xml\exception">
+    </None>
+    <None Include="E:\DevCode\xlnt\third-party\libstudxml\xml\forward">
+    </None>
+    <None Include="E:\DevCode\xlnt\third-party\libstudxml\xml\parser">
+    </None>
+    <None Include="E:\DevCode\xlnt\third-party\libstudxml\xml\qname">
+    </None>
+    <None Include="E:\DevCode\xlnt\third-party\libstudxml\xml\serializer">
+    </None>
+    <None Include="E:\DevCode\xlnt\third-party\libstudxml\xml\value-traits">
+    </None>
+    <ClCompile Include="E:\DevCode\xlnt\third-party\libstudxml\xml\details\genx\char-props.c" />
+    <ClCompile Include="E:\DevCode\xlnt\third-party\libstudxml\xml\details\genx\genx.c" />
+    <ClInclude Include="E:\DevCode\xlnt\third-party\libstudxml\xml\details\genx\genx.h" />
+    <ClCompile Include="E:\DevCode\xlnt\third-party\libstudxml\xml\details\expat\xmlparse.c" />
+    <ClCompile Include="E:\DevCode\xlnt\third-party\libstudxml\xml\details\expat\xmlrole.c" />
+    <ClCompile Include="E:\DevCode\xlnt\third-party\libstudxml\xml\details\expat\xmltok.c" />
+    <ClInclude Include="E:\DevCode\xlnt\third-party\libstudxml\xml\details\expat\ascii.h" />
+    <ClInclude Include="E:\DevCode\xlnt\third-party\libstudxml\xml\details\expat\asciitab.h" />
+    <ClInclude Include="E:\DevCode\xlnt\third-party\libstudxml\xml\details\expat\config.h" />
+    <ClInclude Include="E:\DevCode\xlnt\third-party\libstudxml\xml\details\expat\expat_external.h" />
+    <ClInclude Include="E:\DevCode\xlnt\third-party\libstudxml\xml\details\expat\expat.h" />
+    <ClInclude Include="E:\DevCode\xlnt\third-party\libstudxml\xml\details\expat\iasciitab.h" />
+    <ClInclude Include="E:\DevCode\xlnt\third-party\libstudxml\xml\details\expat\internal.h" />
+    <ClInclude Include="E:\DevCode\xlnt\third-party\libstudxml\xml\details\expat\latin1tab.h" />
+    <ClInclude Include="E:\DevCode\xlnt\third-party\libstudxml\xml\details\expat\nametab.h" />
+    <ClInclude Include="E:\DevCode\xlnt\third-party\libstudxml\xml\details\expat\utf8tab.h" />
+    <ClInclude Include="E:\DevCode\xlnt\third-party\libstudxml\xml\details\expat\xmlrole.h" />
+    <ClInclude Include="E:\DevCode\xlnt\third-party\libstudxml\xml\details\expat\xmltok_impl.h" />
+    <ClInclude Include="E:\DevCode\xlnt\third-party\libstudxml\xml\details\expat\xmltok.h" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>

+ 96 - 0
xlnt/libstudxml.vcxproj.filters

@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="17.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <ClCompile Include="E:\DevCode\xlnt\third-party\libstudxml\xml\parser.cxx">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="E:\DevCode\xlnt\third-party\libstudxml\xml\qname.cxx">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="E:\DevCode\xlnt\third-party\libstudxml\xml\serializer.cxx">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="E:\DevCode\xlnt\third-party\libstudxml\xml\value-traits.cxx">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="E:\DevCode\xlnt\third-party\libstudxml\xml\details\genx\char-props.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="E:\DevCode\xlnt\third-party\libstudxml\xml\details\genx\genx.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="E:\DevCode\xlnt\third-party\libstudxml\xml\details\expat\xmlparse.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="E:\DevCode\xlnt\third-party\libstudxml\xml\details\expat\xmlrole.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="E:\DevCode\xlnt\third-party\libstudxml\xml\details\expat\xmltok.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="E:\DevCode\xlnt\third-party\libstudxml\xml\details\genx\genx.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="E:\DevCode\xlnt\third-party\libstudxml\xml\details\expat\ascii.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="E:\DevCode\xlnt\third-party\libstudxml\xml\details\expat\asciitab.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="E:\DevCode\xlnt\third-party\libstudxml\xml\details\expat\config.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="E:\DevCode\xlnt\third-party\libstudxml\xml\details\expat\expat_external.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="E:\DevCode\xlnt\third-party\libstudxml\xml\details\expat\expat.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="E:\DevCode\xlnt\third-party\libstudxml\xml\details\expat\iasciitab.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="E:\DevCode\xlnt\third-party\libstudxml\xml\details\expat\internal.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="E:\DevCode\xlnt\third-party\libstudxml\xml\details\expat\latin1tab.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="E:\DevCode\xlnt\third-party\libstudxml\xml\details\expat\nametab.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="E:\DevCode\xlnt\third-party\libstudxml\xml\details\expat\utf8tab.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="E:\DevCode\xlnt\third-party\libstudxml\xml\details\expat\xmlrole.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="E:\DevCode\xlnt\third-party\libstudxml\xml\details\expat\xmltok_impl.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="E:\DevCode\xlnt\third-party\libstudxml\xml\details\expat\xmltok.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+  </ItemGroup>
+  <ItemGroup>
+    <CustomBuild Include="E:\DevCode\xlnt\third-party\libstudxml.build\CMakeLists.txt" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="E:\DevCode\xlnt\third-party\libstudxml\xml\content" />
+    <None Include="E:\DevCode\xlnt\third-party\libstudxml\xml\exception" />
+    <None Include="E:\DevCode\xlnt\third-party\libstudxml\xml\forward" />
+    <None Include="E:\DevCode\xlnt\third-party\libstudxml\xml\parser" />
+    <None Include="E:\DevCode\xlnt\third-party\libstudxml\xml\qname" />
+    <None Include="E:\DevCode\xlnt\third-party\libstudxml\xml\serializer" />
+    <None Include="E:\DevCode\xlnt\third-party\libstudxml\xml\value-traits" />
+  </ItemGroup>
+  <ItemGroup>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{8D6EF416-E3F8-3FE9-A671-7F409000AAFF}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{7432471B-AF21-3BA5-8237-342EDE23679A}</UniqueIdentifier>
+    </Filter>
+  </ItemGroup>
+</Project>

+ 343 - 0
xlnt/python/python_streambuf.hpp

@@ -0,0 +1,343 @@
+#pragma once
+
+#include <cassert>
+#include <stdexcept>
+#include <iostream>
+#include <pybind11/pybind11.h>
+
+namespace xlnt {
+
+class python_streambuf : public std::basic_streambuf<char>
+{
+  private:
+    typedef std::basic_streambuf<char> base_t;
+
+  public:
+    /* The syntax
+        using base_t::char_type;
+       would be nicer but Visual Studio C++ 8 chokes on it
+    */
+    typedef base_t::char_type   char_type;
+    typedef base_t::int_type    int_type;
+    typedef base_t::pos_type    pos_type;
+    typedef base_t::off_type    off_type;
+    typedef base_t::traits_type traits_type;
+
+    // work around Visual C++ 7.1 problem
+    inline static int traits_type_eof()
+    {
+        return traits_type::eof();
+    }
+
+    /// The default size of the read and write buffer.
+    /** They are respectively used to buffer data read from and data written to
+        the Python file object. It can be modified from Python.
+    */
+    static std::size_t default_buffer_size;
+
+    /// Construct from a Python file object
+    /** if buffer_size is 0 the current default_buffer_size is used.
+    */
+    python_streambuf(
+      pybind11::object python_file_obj,
+      std::size_t buffer_size_ = 0)
+    :
+      py_read(python_file_obj.attr("read").cast<pybind11::function>()),
+      py_write(python_file_obj.attr("write").cast<pybind11::function>()),
+      py_seek(python_file_obj.attr("seek").cast<pybind11::function>()),
+      py_tell(python_file_obj.attr("tell").cast<pybind11::function>()),
+      buffer_size(buffer_size_ != 0 ? buffer_size_ : default_buffer_size),
+      write_buffer(0),
+      pos_of_read_buffer_end_in_py_file(0),
+      pos_of_write_buffer_end_in_py_file(buffer_size),
+      farthest_pptr(0)
+    {
+      assert(buffer_size != 0);
+
+      /* Some Python file objects (e.g. sys.stdout and sys.stdin)
+         have non-functional seek and tell. If so, assign None to
+         py_tell and py_seek.
+       */
+       if (!py_tell.is_none())
+       {
+         try
+         {
+           py_tell();
+         }
+         catch(...)
+         {
+           py_tell = pybind11::none();
+         }
+       }
+
+      if (!py_write.is_none())
+      {
+        // C-like string to make debugging easier
+        write_buffer = new char[buffer_size + 1];
+        write_buffer[buffer_size] = '\0';
+        setp(write_buffer, write_buffer + buffer_size);  // 27.5.2.4.5 (5)
+        farthest_pptr = pptr();
+      }
+      else
+      {
+        // The first attempt at output will result in a call to overflow
+        setp(0, 0);
+      }
+
+      if (!py_tell.is_none())
+      {
+        auto py_pos = py_tell().cast<pybind11::int_>();
+        pos_of_read_buffer_end_in_py_file = py_pos;
+        pos_of_write_buffer_end_in_py_file = py_pos;
+      }
+    }
+
+    /// Mundane destructor freeing the allocated resources
+    virtual ~python_streambuf() {
+      if (write_buffer) delete[] write_buffer;
+    }
+
+    /// C.f. C++ standard section 27.5.2.4.3
+    /** It is essential to override this virtual function for the stream
+        member function readsome to work correctly (c.f. 27.6.1.3, alinea 30)
+     */
+    virtual std::streamsize showmanyc() {
+      int_type const failure = traits_type::eof();
+      int_type status = underflow();
+      if (status == failure) return -1;
+      return egptr() - gptr();
+    }
+
+    /// C.f. C++ standard section 27.5.2.4.3
+    virtual int_type underflow() {
+      int_type const failure = traits_type::eof();
+      if (py_read.is_none()) {
+        throw std::invalid_argument(
+          "That Python file object has no 'read' attribute");
+      }
+      read_buffer = py_read(buffer_size).cast<pybind11::bytes>();
+      char *read_buffer_data = nullptr;
+      Py_ssize_t py_n_read = 0;
+      if (PyBytes_AsStringAndSize(read_buffer.ptr(), &read_buffer_data, &py_n_read) == -1) {
+        setg(0, 0, 0);
+        throw std::invalid_argument(
+          "The method 'read' of the Python file object "
+          "did not return a string.");
+      }
+      auto n_read = (off_type)py_n_read;
+      pos_of_read_buffer_end_in_py_file += n_read;
+      setg(read_buffer_data, read_buffer_data, read_buffer_data + n_read);
+      // ^^^27.5.2.3.1 (4)
+      if (n_read == 0) return failure;
+      return traits_type::to_int_type(read_buffer_data[0]);
+    }
+
+    /// C.f. C++ standard section 27.5.2.4.5
+    virtual int_type overflow(int_type c=traits_type_eof()) {
+      if (py_write.is_none()) {
+        throw std::invalid_argument(
+          "That Python file object has no 'write' attribute");
+      }
+      farthest_pptr = std::max(farthest_pptr, pptr());
+      auto n_written = (off_type)(farthest_pptr - pbase());
+      auto chunk = PyBytes_FromStringAndSize(pbase(), farthest_pptr - pbase());
+      py_write(chunk);
+      if (!traits_type::eq_int_type(c, traits_type::eof())) {
+	      auto ch = traits_type::to_char_type(c);
+        py_write(reinterpret_cast<char *>(&ch), 1);
+        n_written++;
+      }
+      if (n_written) {
+        pos_of_write_buffer_end_in_py_file += n_written;
+        setp(pbase(), epptr());
+        // ^^^ 27.5.2.4.5 (5)
+        farthest_pptr = pptr();
+      }
+      return traits_type::eq_int_type(
+        c, traits_type::eof()) ? traits_type::not_eof(c) : c;
+    }
+
+    /// Update the python file to reflect the state of this stream buffer
+    /** Empty the write buffer into the Python file object and set the seek
+        position of the latter accordingly (C++ standard section 27.5.2.4.2).
+        If there is no write buffer or it is empty, but there is a non-empty
+        read buffer, set the Python file object seek position to the
+        seek position in that read buffer.
+    */
+    virtual int sync() {
+      int result = 0;
+      farthest_pptr = std::max(farthest_pptr, pptr());
+      if (farthest_pptr && farthest_pptr > pbase()) {
+        off_type delta = pptr() - farthest_pptr;
+        int_type status = overflow();
+        if (traits_type::eq_int_type(status, traits_type::eof())) result = -1;
+        if (!py_seek.is_none())
+        {
+          py_seek(delta);
+        }
+      }
+      else if (gptr() && gptr() < egptr()) {
+        if (!py_seek.is_none())
+        {
+          py_seek(gptr() - egptr(), 1);
+        }
+      }
+      return result;
+    }
+
+    /// C.f. C++ standard section 27.5.2.4.2
+    /** This implementation is optimised to look whether the position is within
+        the buffers, so as to avoid calling Python seek or tell. It is
+        important for many applications that the overhead of calling into Python
+        is avoided as much as possible (e.g. parsers which may do a lot of
+        backtracking)
+    */
+    virtual
+    pos_type seekoff(off_type off, std::ios_base::seekdir way,
+                     std::ios_base::openmode which=  std::ios_base::in
+                                                   | std::ios_base::out)
+    {
+      /* In practice, "which" is either std::ios_base::in or out
+         since we end up here because either seekp or seekg was called
+         on the stream using this buffer. That simplifies the code
+         in a few places.
+      */
+      int const failure = off_type(-1);
+
+      if (py_seek.is_none()) {
+        throw std::invalid_argument(
+          "That Python file object has no 'seek' attribute");
+      }
+
+      // we need the read buffer to contain something!
+      if (which == std::ios_base::in && !gptr()) {
+        if (traits_type::eq_int_type(underflow(), traits_type::eof())) {
+          return failure;
+        }
+      }
+
+      // compute the whence parameter for Python seek
+      int whence;
+      switch (way) {
+        case std::ios_base::beg:
+          whence = 0;
+          break;
+        case std::ios_base::cur:
+          whence = 1;
+          break;
+        case std::ios_base::end:
+          whence = 2;
+          break;
+        default:
+          return failure;
+      }
+
+      // Let's have a go
+      auto result = seekoff_without_calling_python(off, way, which);
+      if (!result.second) {
+        // we need to call Python
+        if (which == std::ios_base::out) overflow();
+        if (way == std::ios_base::cur) {
+          if      (which == std::ios_base::in)  off -= egptr() - gptr();
+          else if (which == std::ios_base::out) off += pptr() - pbase();
+        }
+        py_seek(off, whence);
+        result.first = py_tell().cast<pybind11::int_>();
+        if (which == std::ios_base::in) underflow();
+      }
+      return result.first;
+    }
+
+    /// C.f. C++ standard section 27.5.2.4.2
+    virtual
+    pos_type seekpos(pos_type sp,
+                     std::ios_base::openmode which=  std::ios_base::in
+                                                   | std::ios_base::out)
+    {
+      return python_streambuf::seekoff(sp, std::ios_base::beg, which);
+    }
+
+  private:
+    pybind11::function py_read;
+    pybind11::function py_write;
+    pybind11::function py_seek;
+    pybind11::function py_tell;
+
+    std::size_t buffer_size;
+
+    /* This is actually a Python string and the actual read buffer is
+       its internal data, i.e. an array of characters. We use a Boost.Python
+       object so as to hold on it: as a result, the actual buffer can't
+       go away.
+    */
+    pybind11::bytes read_buffer;
+
+    /* A mere array of char's allocated on the heap at construction time and
+       de-allocated only at destruction time.
+    */
+    char *write_buffer = nullptr;
+
+    off_type pos_of_read_buffer_end_in_py_file,
+             pos_of_write_buffer_end_in_py_file;
+
+    // the farthest place the buffer has been written into
+    char *farthest_pptr = nullptr;
+
+
+    std::pair<off_type, bool> seekoff_without_calling_python(
+      off_type off,
+      std::ios_base::seekdir way,
+      std::ios_base::openmode which)
+    {
+      const auto failure = std::make_pair<off_type, bool>(off_type(), false);
+
+      // Buffer range and current position
+      off_type buf_begin, buf_end, buf_cur, upper_bound;
+      off_type pos_of_buffer_end_in_py_file;
+      if (which == std::ios_base::in) {
+        pos_of_buffer_end_in_py_file = pos_of_read_buffer_end_in_py_file;
+        buf_begin = reinterpret_cast<std::streamsize>(eback());
+        buf_cur = reinterpret_cast<std::streamsize>(gptr());
+        buf_end = reinterpret_cast<std::streamsize>(egptr());
+        upper_bound = buf_end;
+      }
+      else if (which == std::ios_base::out) {
+        pos_of_buffer_end_in_py_file = pos_of_write_buffer_end_in_py_file;
+        buf_begin = reinterpret_cast<std::streamsize>(pbase());
+        buf_cur = reinterpret_cast<std::streamsize>(pptr());
+        buf_end = reinterpret_cast<std::streamsize>(epptr());
+        farthest_pptr = std::max(farthest_pptr, pptr());
+        upper_bound = reinterpret_cast<std::streamsize>(farthest_pptr) + 1;
+      }
+      else {
+        throw xlnt::exception("unreachable");
+      }
+
+      // Sought position in "buffer coordinate"
+      off_type buf_sought;
+      if (way == std::ios_base::cur) {
+        buf_sought = buf_cur + off;
+      }
+      else if (way == std::ios_base::beg) {
+        buf_sought = buf_end + (off - pos_of_buffer_end_in_py_file);
+      }
+      else if (way == std::ios_base::end) {
+        return failure;
+      }
+      else {
+        throw xlnt::exception("unreachable");
+      }
+
+      // if the sought position is not in the buffer, give up
+      if (buf_sought < buf_begin || buf_sought >= upper_bound) return failure;
+
+      // we are in wonderland
+      if      (which == std::ios_base::in)  gbump(static_cast<int>(buf_sought - buf_cur));
+      else if (which == std::ios_base::out) pbump(static_cast<int>(buf_sought - buf_cur));
+      return std::make_pair<off_type, bool>(pos_of_buffer_end_in_py_file + (buf_sought - buf_end), true);
+    }
+};
+
+std::size_t python_streambuf::default_buffer_size = 1024;
+
+} // namespace xlnt

+ 256 - 0
xlnt/python/setup.py

@@ -0,0 +1,256 @@
+import glob
+import os
+import os.path as osp
+import re
+import shutil
+import sys
+
+from distutils.command.clean import clean as _clean
+from distutils import sysconfig
+
+from os.path import join as pjoin
+
+from setuptools import setup, Extension, Distribution
+from setuptools.command.build_ext import build_ext as _build_ext
+
+# Check if we're running 64-bit Python
+is_64_bit = sys.maxsize > 2**32
+
+class clean(_clean):
+    def run(self):
+        _clean.run(self)
+        for x in []:
+            try:
+                os.remove(x)
+            except OSError:
+                pass
+
+class build_ext(_build_ext):
+    def run(self):
+        self._run_cmake()
+
+    # adapted from cmake_build_ext in dynd-python
+    # github.com/libdynd/dynd-python
+
+    description = "Build the C-extensions for arrow"
+    user_options = ([('extra-cmake-args=', None, 'extra arguments for CMake'),
+                     ('build-type=', None, 'build type (Debug or Release)')] +
+                    _build_ext.user_options)
+
+    def initialize_options(self):
+        _build_ext.initialize_options(self)
+        self.extra_cmake_args = os.environ.get('XLNTPYARROW_CMAKE_OPTIONS', '')
+        self.build_type = os.environ.get('XLNTPYARROW_BUILD_TYPE', 'Debug')
+
+        self.cmake_cxxflags = os.environ.get('XLNTPYARROW_CXXFLAGS', '')
+
+        if sys.platform == 'win32':
+            # Cannot do debug builds in Windows unless Python itself is a debug
+            # build
+            if not hasattr(sys, 'gettotalrefcount'):
+                self.build_type = 'Release'
+
+    def _run_cmake(self):
+        # The directory containing this setup.py
+        source = osp.dirname(osp.abspath(__file__))
+
+        # The staging directory for the module being built
+        build_temp = pjoin(os.getcwd(), self.build_temp)
+        build_lib = os.path.join(os.getcwd(), self.build_lib)
+
+        # Change to the build directory
+        saved_cwd = os.getcwd()
+        if not os.path.isdir(self.build_temp):
+            self.mkpath(self.build_temp)
+        os.chdir(self.build_temp)
+
+        # Detect if we built elsewhere
+        if os.path.isfile('CMakeCache.txt'):
+            cachefile = open('CMakeCache.txt', 'r')
+            cachedir = re.search('CMAKE_CACHEFILE_DIR:INTERNAL=(.*)',
+                                 cachefile.read()).group(1)
+            cachefile.close()
+            if (cachedir != build_temp):
+                return
+
+        static_lib_option = ''
+
+        cmake_options = [
+            '-DPYTHON_EXECUTABLE=%s' % sys.executable,
+            static_lib_option,
+        ]
+
+        if len(self.cmake_cxxflags) > 0:
+            cmake_options.append('-DPYARROW_CXXFLAGS="{0}"'
+                                 .format(self.cmake_cxxflags))
+
+        cmake_options.append('-DCMAKE_BUILD_TYPE={0}'
+                             .format(self.build_type))
+
+        if 'CMAKE_GENERATOR' in os.environ:
+            cmake_options.append('-G{}'
+                .format(os.environ['CMAKE_GENERATOR']))
+
+        if sys.platform != 'win32':
+            cmake_options.append('-DCMAKE_INSTALL_PREFIX={0}'
+                                 .format(os.environ['PREFIX']))
+            cmake_command = (['cmake', self.extra_cmake_args] +
+                             cmake_options + [source])
+
+            print("-- Runnning cmake for xlntpyarrow")
+            self.spawn(cmake_command)
+            print("-- Finished cmake for xlntpyarrow")
+            args = ['make']
+            if os.environ.get('XLNTPYARROW_BUILD_VERBOSE', '0') == '1':
+                args.append('VERBOSE=1')
+
+            if 'XLNTPYARROW_PARALLEL' in os.environ:
+                args.append('-j{0}'.format(os.environ['XLNTPYARROW_PARALLEL']))
+
+            args.append('install')
+
+            print("-- Running cmake --build for xlntpyarrow")
+            self.spawn(args)
+            print("-- Finished cmake --build for xlntpyarrow")
+        else:
+            import shlex
+            if not is_64_bit:
+                raise RuntimeError('Not supported on 32-bit Windows')
+
+            cmake_options.append('-DCMAKE_INSTALL_PREFIX={0}'
+                                 .format(os.environ['LIBRARY_PREFIX']))
+            extra_cmake_args = shlex.split(self.extra_cmake_args)
+            cmake_command = (['cmake'] + extra_cmake_args +
+                             cmake_options + [source])
+
+            print("-- Runnning cmake for xlntpyarrow")
+            self.spawn(cmake_command)
+            print("-- Finished cmake for xlntpyarrow")
+            # Do the build
+            print("-- Running cmake --build for xlntpyarrow")
+            self.spawn(['cmake', '--build', '.', '--config', self.build_type, '--target', 'INSTALL'])
+            print("-- Finished cmake --build for xlntpyarrow")
+
+        if self.inplace:
+            # a bit hacky
+            build_lib = saved_cwd
+
+        # Move the libraries to the place expected by the Python
+        # build
+        shared_library_prefix = 'lib'
+        if sys.platform == 'darwin':
+            shared_library_suffix = '.dylib'
+        elif sys.platform == 'win32':
+            shared_library_suffix = '.dll'
+            shared_library_prefix = ''
+        else:
+            shared_library_suffix = '.so'
+
+        try:
+            os.makedirs(pjoin(build_lib, 'xlntpyarrow'))
+        except OSError:
+            pass
+
+        self._found_names = []
+        built_path = self.get_ext_built('lib')
+        if not os.path.exists(built_path):
+            raise RuntimeError('xlntpyarrow C-extension failed to build:',
+                               os.path.abspath(built_path))
+
+        ext_path = pjoin(build_lib, self._get_cmake_ext_path('lib'))
+        if os.path.exists(ext_path):
+            os.remove(ext_path)
+        self.mkpath(os.path.dirname(ext_path))
+        print('Moving built C-extension', built_path,
+              'to build path', ext_path)
+        shutil.move(self.get_ext_built('lib'), ext_path)
+        self._found_names.append('lib')
+
+        os.chdir(saved_cwd)
+
+    def _failure_permitted(self, name):
+        return False
+
+    def _get_inplace_dir(self):
+        pass
+
+    def _get_cmake_ext_path(self, name):
+        # Get the package directory from build_py
+        build_py = self.get_finalized_command('build_py')
+        package_dir = build_py.get_package_dir('xlntpyarrow')
+        # This is the name of the arrow C-extension
+        suffix = sysconfig.get_config_var('EXT_SUFFIX')
+        if suffix is None:
+            suffix = sysconfig.get_config_var('SO')
+        filename = name + suffix
+        return pjoin(package_dir, filename)
+
+    def get_ext_built(self, name):
+        if sys.platform == 'win32':
+            head, tail = os.path.split(name)
+            suffix = sysconfig.get_config_var('SO')
+            return pjoin(head, self.build_type, tail + suffix)
+        else:
+            suffix = sysconfig.get_config_var('SO')
+            print('suffix is', suffix)
+            return name + suffix
+
+    def get_names(self):
+        return self._found_names
+
+    def get_outputs(self):
+        # Just the C extensions
+        # regular_exts = _build_ext.get_outputs(self)
+        return [self._get_cmake_ext_path(name)
+                for name in self.get_names()]
+
+long_description = """
+xlntpyarrow allows Apache Arrow tables to be written to and read from an XLSX
+file efficiently using the C++ library xlnt.
+""".strip()
+
+classifiers = [
+    'Development Status :: 5 - Production/Stable',
+    'Environment :: Plugins',
+    'Intended Audience :: Science/Research',
+    'License :: OSI Approved :: MIT License',
+    'Natural Language :: English',
+    'Operating System :: Microsoft :: Windows',
+    'Operating System :: MacOS :: MacOS X',
+    'Operating System :: POSIX :: Linux',
+    'Programming Language :: C',
+    'Programming Language :: C++',
+    'Programming Language :: Python :: 2.7',
+    'Programming Language :: Python :: 3.6',
+    'Programming Language :: Python :: Implementation :: CPython',
+    'Topic :: Database',
+    'Topic :: Office/Business :: Financial :: Spreadsheet',
+    'Topic :: Scientific/Engineering :: Information Analysis',
+    'Topic :: Software Development :: Libraries :: Python Modules'
+]
+
+class BinaryDistribution(Distribution):
+    def has_ext_modules(foo):
+        return True
+
+setup(
+    name = 'xlntpyarrow',
+    version = '1.1.0',
+    description = 'Python library for converting Apache Arrow tables<->XLSX files',
+    long_description = long_description,
+    author = 'Thomas Fussell',
+    author_email = 'thomas.fussell@gmail.com',
+    maintainer = 'Thomas Fussell',
+    maintainer_email = 'thomas.fussell@gmail.com',
+    url = 'https://github.com/tfussell/xlnt',
+    download_url = 'https://github.com/tfussell/xlnt/releases',
+    packages = ['xlntpyarrow'],
+    ext_modules = [Extension('xlntpyarrow.lib', [])],
+    cmdclass = {
+        'clean': clean,
+        'build_ext': build_ext
+    },
+    classifiers = classifiers,
+    distclass = BinaryDistribution,
+    zip_safe = False
+)

+ 452 - 0
xlnt/python/xlntpyarrow.lib.cpp

@@ -0,0 +1,452 @@
+// Copyright (c) 2017-2018 Thomas Fussell
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// 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, ARISING 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 <exception>
+#include <arrow/api.h>
+#include <arrow/python/pyarrow.h>
+#include <pybind11/pybind11.h>
+#include <pybind11/stl.h>
+#include <xlnt/xlnt.hpp>
+#include <xlnt/workbook/streaming_workbook_reader.hpp>
+#include <python_streambuf.hpp>
+
+void import_pyarrow()
+{
+    static auto imported = false;
+
+    if (!imported)
+    {
+        if (arrow::py::import_pyarrow() != 0)
+        {
+            throw xlnt::exception("Import of pyarrow failed.");
+        }
+
+        imported = true;
+    }
+}
+
+arrow::ArrayBuilder *make_array_builder(arrow::Type::type type)
+{
+    auto pool = arrow::default_memory_pool();
+    auto builder = static_cast<arrow::ArrayBuilder *>(nullptr);
+
+    switch(type)
+    {
+    case arrow::Type::NA:
+        break;
+
+    case arrow::Type::UINT8:
+        builder = new arrow::TypeTraits<arrow::UInt8Type>::BuilderType(pool);
+        break;
+
+    case arrow::Type::INT8:
+        builder = new arrow::TypeTraits<arrow::Int8Type>::BuilderType(pool);
+        break;
+
+    case arrow::Type::UINT16:
+        builder = new arrow::TypeTraits<arrow::UInt16Type>::BuilderType(pool);
+        break;
+
+    case arrow::Type::INT16:
+        builder = new arrow::TypeTraits<arrow::Int16Type>::BuilderType(pool);
+        break;
+
+    case arrow::Type::UINT32:
+        builder = new arrow::TypeTraits<arrow::UInt32Type>::BuilderType(pool);
+        break;
+
+    case arrow::Type::INT32:
+        builder = new arrow::TypeTraits<arrow::Int32Type>::BuilderType(pool);
+        break;
+
+    case arrow::Type::UINT64:
+        builder = new arrow::TypeTraits<arrow::UInt64Type>::BuilderType(pool);
+        break;
+
+    case arrow::Type::INT64:
+        builder = new arrow::TypeTraits<arrow::Int64Type>::BuilderType(pool);
+        break;
+
+    case arrow::Type::DATE64:
+        builder = new arrow::TypeTraits<arrow::Date64Type>::BuilderType(pool);
+        break;
+
+    case arrow::Type::DATE32:
+        builder = new arrow::TypeTraits<arrow::Date32Type>::BuilderType(pool);
+        break;
+/*
+    case arrow::Type::TIMESTAMP:
+        builder = new arrow::TypeTraits<arrow::TimestampType>::BuilderType(pool);
+        break;
+
+    case arrow::Type::TIME32:
+        builder = new arrow::TypeTraits<arrow::Time32Type>::BuilderType(pool);
+        break;
+
+    case arrow::Type::TIME64:
+        builder = new arrow::TypeTraits<arrow::Time64Type>::BuilderType(pool);
+        break;
+*/
+    case arrow::Type::HALF_FLOAT:
+        builder = new arrow::TypeTraits<arrow::HalfFloatType>::BuilderType(pool);
+        break;
+
+    case arrow::Type::FLOAT:
+        builder = new arrow::TypeTraits<arrow::FloatType>::BuilderType(pool);
+        break;
+
+    case arrow::Type::DOUBLE:
+        builder = new arrow::TypeTraits<arrow::DoubleType>::BuilderType(pool);
+        break;
+/*
+    case arrow::Type::DECIMAL:
+        builder = new arrow::TypeTraits<arrow::DecimalType>::BuilderType(pool, type);
+        break;
+*/
+    case arrow::Type::BOOL:
+        builder = new arrow::TypeTraits<arrow::BooleanType>::BuilderType(pool);
+        break;
+
+    case arrow::Type::STRING:
+        builder = new arrow::TypeTraits<arrow::StringType>::BuilderType(pool);
+        break;
+
+    case arrow::Type::BINARY:
+        builder = new arrow::TypeTraits<arrow::BinaryType>::BuilderType(pool);
+        break;
+/*
+    case arrow::Type::FIXED_SIZE_BINARY:
+        builder = new arrow::TypeTraits<arrow::FixedSizeBinaryType>::BuilderType(pool);
+        break;
+
+    case arrow::Type::LIST:
+        builder = new arrow::TypeTraits<arrow::ListType>::BuilderType(pool);
+        break;
+
+    case arrow::Type::STRUCT:
+        builder = new arrow::TypeTraits<arrow::StructType>::BuilderType(pool);
+        break;
+
+    case arrow::Type::UNION:
+        builder = new arrow::TypeTraits<arrow::UnionType>::BuilderType(pool);
+        break;
+
+    case arrow::Type::DICTIONARY:
+        builder = new arrow::TypeTraits<arrow::DictionaryType>::BuilderType(pool);
+        break;
+*/
+    default:
+        throw xlnt::exception("not implemented");
+    }
+
+    return builder;
+}
+
+void open_file(xlnt::streaming_workbook_reader &reader, pybind11::object file)
+{
+    reader.open(std::unique_ptr<std::streambuf>(new xlnt::python_streambuf(file)));
+}
+
+template<typename T>
+T cell_value(xlnt::cell cell)
+{
+    return static_cast<T>(cell.value<double>());
+}
+
+// from https://stackoverflow.com/questions/1659440/32-bit-to-16-bit-floating-point-conversion
+std::uint16_t float_to_half(float f)
+{
+    auto x = static_cast<std::uint32_t>(f);
+    auto half = ((x >> 16) & 0x8000)
+        | ((((x & 0x7f800000) - 0x38000000) >> 13) & 0x7c00)
+        | ((x >> 13) & 0x03ff);
+
+    return half;
+}
+
+void append_cell_value(arrow::ArrayBuilder *builder, arrow::Type::type type, xlnt::cell cell)
+{
+    const status = arrow::Status::OK();
+
+    switch (type)
+    {
+    case arrow::Type::NA:
+        break;
+
+    case arrow::Type::BOOL:
+        status = static_cast<arrow::BooleanBuilder *>(builder)
+            ->Append(cell.value<bool>());
+        break;
+
+    case arrow::Type::UINT8:
+        status = static_cast<arrow::UInt8Builder *>(builder)
+            ->Append(cell_value<std::uint8_t>(cell));
+        break;
+
+    case arrow::Type::INT8:
+        status = static_cast<arrow::Int8Builder *>(builder)
+          ->Append(cell_value<std::uint8_t>(cell));
+        break;
+
+    case arrow::Type::UINT16:
+        status = static_cast<arrow::UInt16Builder *>(builder)
+            ->Append(cell_value<std::uint16_t>(cell));
+        break;
+
+    case arrow::Type::INT16:
+        status = static_cast<arrow::Int16Builder *>(builder)
+            ->Append(cell_value<std::int16_t>(cell));
+        break;
+
+    case arrow::Type::UINT32:
+        status = static_cast<arrow::UInt32Builder *>(builder)
+            ->Append(cell_value<std::uint32_t>(cell));
+        break;
+
+    case arrow::Type::INT32:
+        status = static_cast<arrow::Int32Builder *>(builder)
+            ->Append(cell_value<std::int32_t>(cell));
+        break;
+
+    case arrow::Type::UINT64:
+        status = static_cast<arrow::UInt64Builder *>(builder)
+            ->Append(cell_value<std::uint64_t>(cell));
+        break;
+
+    case arrow::Type::INT64:
+        status = static_cast<arrow::Int64Builder *>(builder)
+            ->Append(cell_value<std::int64_t>(cell));
+        break;
+
+    case arrow::Type::HALF_FLOAT:
+        status = static_cast<arrow::HalfFloatBuilder *>(builder)
+            ->Append(float_to_half(cell_value<float>(cell)));
+        break;
+
+    case arrow::Type::FLOAT:
+        status = static_cast<arrow::FloatBuilder *>(builder)
+            ->Append(cell_value<float>(cell));
+        break;
+
+    case arrow::Type::DOUBLE:
+        status = static_cast<arrow::DoubleBuilder *>(builder)
+            ->Append(cell_value<double>(cell));
+        break;
+
+    case arrow::Type::STRING:
+        status = static_cast<arrow::StringBuilder *>(builder)
+            ->Append(cell.value<std::string>());
+        break;
+
+    case arrow::Type::BINARY:
+        status = static_cast<arrow::BinaryBuilder *>(builder)
+            ->Append(cell.value<std::string>());
+        break;
+
+    case arrow::Type::FIXED_SIZE_BINARY:
+        status = static_cast<arrow::FixedSizeBinaryBuilder *>(builder)
+            ->Append(cell.value<std::string>());
+        break;
+
+    case arrow::Type::DATE32:
+        status = static_cast<arrow::Date32Builder *>(builder)
+            ->Append(cell_value<arrow::Date32Type::c_type>(cell));
+        break;
+
+    case arrow::Type::DATE64:
+        status = static_cast<arrow::Date64Builder *>(builder)
+            ->Append(cell_value<arrow::Date64Type::c_type>(cell));
+        break;
+
+    case arrow::Type::TIMESTAMP:
+        status = static_cast<arrow::TimestampBuilder *>(builder)
+            ->Append(cell_value<arrow::TimestampType::c_type>(cell));
+        break;
+
+    case arrow::Type::TIME32:
+        status = static_cast<arrow::Time32Builder *>(builder)
+            ->Append(cell_value<arrow::Time32Type::c_type>(cell));
+        break;
+
+    case arrow::Type::TIME64:
+        status = static_cast<arrow::Time64Builder *>(builder)
+            ->Append(cell_value<arrow::Time64Type::c_type>(cell));
+        break;
+/*
+    case arrow::Type::INTERVAL:
+        status = static_cast<arrow::IntervalBuilder *>(builder)
+            ->Append(cell_value<std::int64_t>(cell));
+        break;
+
+    case arrow::Type::DECIMAL:
+        status = static_cast<arrow::DecimalBuilder *>(builder)
+            ->Append(cell.value<std::string>());
+        break;
+
+    case arrow::Type::LIST:
+        status = static_cast<arrow::ListBuilder *>(builder)
+            ->Append(cell.value<std::string>());
+        break;
+
+    case arrow::Type::STRUCT:
+        status = static_cast<arrow::StructBuilder *>(builder)
+            ->Append(cell.value<std::string>());
+        break;
+
+    case arrow::Type::UNION:
+        status = static_cast<arrow::UnionBuilder *>(builder)
+            ->Append(cell.value<std::string>());
+        break;
+
+    case arrow::Type::DICTIONARY:
+        status = static_cast<arrow::DictionaryBuilder *>(builder)
+            ->Append(cell.value<std::string>());
+        break;
+*/
+    default:
+        throw xlnt::exception("not implemented");
+    }
+
+    if (status != arrow::Status::OK())
+    {
+        throw xlnt::exception("Append failed");
+    }
+}
+
+pybind11::handle read_batch(xlnt::streaming_workbook_reader &reader,
+    pybind11::object pyschema, int max_rows)
+{
+    import_pyarrow();
+
+    std::shared_ptr<arrow::Schema> schema;
+    arrow::py::unwrap_schema(pyschema.ptr(), &schema);
+
+    std::vector<arrow::Type::type> column_types;
+
+    for (auto i = 0; i < schema->num_fields(); ++i)
+    {
+        column_types.push_back(schema->field(i)->type()->id());
+    }
+
+    auto builders = std::vector<std::unique_ptr<arrow::ArrayBuilder>>();
+
+    for (auto type : column_types)
+    {
+        builders.emplace_back(make_array_builder(type));
+    }
+
+    auto row = std::int64_t(0);
+
+    while (row < max_rows)
+    {
+        if (!reader.has_cell()) break;
+
+        for (auto column = 0; column < schema->num_fields(); ++column)
+        {
+            if (!reader.has_cell()) break;
+
+            auto cell = reader.read_cell();
+            auto zero_indexed_column = cell.column().index - 1;
+            auto column_type = column_types.at(zero_indexed_column);
+            auto builder = builders.at(zero_indexed_column).get();
+
+            append_cell_value(builder, column_type, cell);
+        }
+
+        ++row;
+    }
+
+    auto columns = std::vector<std::shared_ptr<arrow::Array>>();
+
+    for (auto &builder : builders)
+    {
+        std::shared_ptr<arrow::Array> column;
+        builder->Finish(&column);
+        columns.emplace_back(column);
+    }
+
+    auto batch_pointer = std::make_shared<arrow::RecordBatch>(schema, row, columns);
+    auto batch_object = arrow::py::wrap_record_batch(batch_pointer);
+    auto batch_handle = pybind11::handle(batch_object); // don't need to incr. reference count, right?
+
+    return batch_handle;
+}
+
+PYBIND11_MODULE(lib, m)
+{
+    m.doc() = "streaming read/write interface for C++ XLSX library xlnt";
+
+    pybind11::class_<xlnt::streaming_workbook_reader>(m, "StreamingWorkbookReader")
+        .def(pybind11::init<>())
+        .def("has_cell", &xlnt::streaming_workbook_reader::has_cell)
+        .def("read_cell", &xlnt::streaming_workbook_reader::read_cell)
+        .def("has_worksheet", &xlnt::streaming_workbook_reader::has_worksheet)
+        .def("begin_worksheet", &xlnt::streaming_workbook_reader::begin_worksheet)
+        .def("end_worksheet", &xlnt::streaming_workbook_reader::end_worksheet)
+        .def("sheet_titles", &xlnt::streaming_workbook_reader::sheet_titles)
+        .def("open", &open_file)
+        .def("read_batch", &read_batch);
+
+    pybind11::class_<xlnt::worksheet>(m, "Worksheet");
+
+    pybind11::class_<xlnt::cell> cell(m, "Cell");
+    cell.def("value_string", [](xlnt::cell &cell)
+        {
+            return cell.value<std::string>();
+        })
+        .def("value_bool", [](xlnt::cell &cell)
+        {
+            return cell.value<bool>();
+        })
+        .def("value_unsigned_int", [](xlnt::cell &cell)
+        {
+            return cell.value<unsigned int>();
+        })
+        .def("value_double", [](xlnt::cell &cell)
+        {
+            return cell.value<double>();
+        })
+        .def("data_type", [](xlnt::cell &cell)
+            {
+                return cell.data_type();
+            })
+        .def("row", &xlnt::cell::row)
+        .def("column", [](xlnt::cell &cell)
+            {
+                return cell.column().index;
+            })
+        .def("format_is_date", [](xlnt::cell &cell)
+            {
+                return cell.has_format() && cell.number_format().is_date_format();
+            });
+
+    pybind11::enum_<xlnt::cell::type>(cell, "Type")
+        .value("Empty", xlnt::cell::type::empty)
+        .value("Boolean", xlnt::cell::type::boolean)
+        .value("Date", xlnt::cell::type::date)
+        .value("Error", xlnt::cell::type::error)
+        .value("InlineString", xlnt::cell::type::inline_string)
+        .value("Number", xlnt::cell::type::number)
+        .value("SharedString", xlnt::cell::type::shared_string)
+        .value("FormulaString", xlnt::cell::type::formula_string);
+}

+ 86 - 0
xlnt/python/xlntpyarrow/__init__.py

@@ -0,0 +1,86 @@
+import pyarrow as pa
+import xlntpyarrow.lib as xpa
+
+COLUMN_TYPE_FIELD = {
+    xpa.Cell.Type.Number: pa.float64,
+    xpa.Cell.Type.SharedString: pa.string,
+    xpa.Cell.Type.InlineString: pa.string,
+    xpa.Cell.Type.FormulaString: pa.string,
+    xpa.Cell.Type.Error: pa.string,
+    xpa.Cell.Type.Boolean: pa.bool_,
+    xpa.Cell.Type.Date: pa.date32,
+    xpa.Cell.Type.Empty: pa.string,
+}
+
+def cell_to_pyarrow_array(cell, type):
+    if cell.data_type() == xpa.Cell.Type.Number:
+        return pa.array([cell.value_double()], type)
+    elif cell.data_type() == xpa.Cell.Type.SharedString:
+        return pa.array([cell.value_string()], type)
+    elif cell.data_type() == xpa.Cell.Type.InlineString:
+        return pa.array([cell.value_string()], type)
+    elif cell.data_type() == xpa.Cell.Type.FormulaString:
+        return pa.array([cell.value_string()], type)
+    elif cell.data_type() == xpa.Cell.Type.Error:
+        return pa.array([cell.value_string()], type)
+    elif cell.data_type() == xpa.Cell.Type.Boolean:
+        return pa.array([cell.value_bool()], type)
+    elif cell.data_type() == xpa.Cell.Type.Date:
+        return pa.array([cell.value_unsigned_int()], type)
+    elif cell.data_type() == xpa.Cell.Type.Empty:
+        return pa.array([cell.value_string()], type)
+
+def xlsx2arrow(io, sheetname):
+    reader = xpa.StreamingWorkbookReader()
+    reader.open(io)
+
+    sheet_titles = reader.sheet_titles()
+    sheet_title = sheet_titles[0]
+
+    if sheetname is not None:
+        if isinstance(sheetname, int):
+            sheet_title = sheet_titles[sheetname]
+        elif isinstance(sheetname, str):
+            sheet_title = sheetname
+
+    reader.begin_worksheet(sheet_title)
+
+    column_names = []
+    fields = []
+    batches = []
+    schema = None
+    first_batch = []
+    max_column = 0
+
+    while reader.has_cell():
+        if schema is None:
+            cell = reader.read_cell()
+            type = cell.data_type()
+
+            if cell.row() == 1:
+                column_names.append(cell.value_string())
+                max_column = max(max_column, cell.column())
+                continue
+            elif cell.row() == 2:
+                column_name = column_names[cell.column() - 1]
+                if type == xpa.Cell.Type.Number and cell.format_is_date():
+                    fields.append(pa.field(column_name, pa.date32))
+                else:
+                    fields.append(pa.field(column_name, COLUMN_TYPE_FIELD[type]()))
+                first_batch.append(cell_to_pyarrow_array(cell, fields[-1].type))
+                if cell.column() == max_column:
+                    schema = pa.schema(fields)
+                    print(schema)
+                    batches.append(pa.RecordBatch.from_arrays(first_batch, column_names))
+                continue
+
+        batches.append(reader.read_batch(schema, 10000))
+
+    reader.end_worksheet()
+
+    return pa.Table.from_batches(batches)
+
+if __name__ == '__main__':
+    file = open('tmp.xlsx', 'rb')
+    table = xlsx2arrow(file, 'Sheet1')
+    print(table.to_pandas())

BIN
xlnt/samples/data/cafe.jpg


BIN
xlnt/samples/data/documentation-print.xlsx


BIN
xlnt/samples/data/encrypted.xlsx


BIN
xlnt/samples/data/penguin.jpg


BIN
xlnt/samples/data/sample1.xlsx


+ 36 - 0
xlnt/samples/decrypt.cpp

@@ -0,0 +1,36 @@
+// Copyright (c) 2017-2018 Thomas Fussell
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// 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, ARISING 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 <helpers/path_helper.hpp>
+#include <xlnt/xlnt.hpp>
+
+int main()
+{
+	xlnt::workbook wb;
+
+	const auto password = std::string("secret");
+	wb.load(path_helper::sample_file("encrypted.xlsx"), password);
+	wb.save("decrypted.xlsx");
+
+	return 0;
+}

+ 1 - 0
xlnt/samples/disabled/add_comments.cpp

@@ -0,0 +1 @@
+add_comments.cpp

+ 1 - 0
xlnt/samples/disabled/basic_conditional_formatting.cpp

@@ -0,0 +1 @@
+basic_conditional_formatting.cpp

+ 1 - 0
xlnt/samples/disabled/comment_error.cpp

@@ -0,0 +1 @@
+comment_error.cpp

+ 1 - 0
xlnt/samples/disabled/copy_style.cpp

@@ -0,0 +1 @@
+copy_style.cpp

+ 45 - 0
xlnt/samples/disabled/create.cpp

@@ -0,0 +1,45 @@
+#include <string>
+#include <unordered_map>
+#include <utility>
+
+#include <xlnt/xlnt.hpp>
+
+int main()
+{
+    const std::vector<std::pair<std::string, double>> amounts =
+    {
+        { "Anne", 17.31 },
+        { "Brent", 21.99 },
+        { "Catelyn", 94.47 },
+        { "Diedrich", 101.05 }
+    };
+    
+    xlnt::workbook wb;
+    auto ws = wb.get_active_sheet();
+    
+    ws.get_cell("A1").set_value("Name");
+    ws.get_cell("B1").set_value("Amount");
+    
+    std::size_t row = 2;
+    auto money_format = xlnt::number_format::from_builtin_id(44);
+    auto &style = wb.create_style("Currency");
+    style.set_builtin_id(4);
+    style.set_number_format(money_format);
+    
+    for (const auto &amount : amounts)
+    {
+        ws.get_cell(xlnt::cell_reference(1, row)).set_value(amount.first);
+        ws.get_cell(xlnt::cell_reference(2, row)).set_value(amount.second);
+        ws.get_cell(xlnt::cell_reference(2, row)).set_style("Currency");
+
+        row++;
+    }
+
+    std::string sum_formula = "=SUM(B2:B" + std::to_string(row - 1) + ")";
+    ws.get_cell(xlnt::cell_reference(2, row)).set_style("Currency");
+    ws.get_cell(xlnt::cell_reference(2, row)).set_formula(sum_formula);
+    
+    wb.save("create.xlsx");
+    
+    return 0;
+}

+ 1 - 0
xlnt/samples/disabled/default_styles.cpp

@@ -0,0 +1 @@
+default_styles.cpp

Some files were not shown because too many files changed in this diff