diff --git a/include/xlnt/workbook/streaming_workbook_reader.hpp b/include/xlnt/workbook/streaming_workbook_reader.hpp index 1e98f1ac..7bd56e54 100644 --- a/include/xlnt/workbook/streaming_workbook_reader.hpp +++ b/include/xlnt/workbook/streaming_workbook_reader.hpp @@ -58,12 +58,16 @@ public: /// void close(); + bool has_cell(); + /// /// Reads the next cell in the current worksheet and optionally returns it if /// the last cell in the sheet has not yet been read. /// cell read_cell(); + bool has_worksheet(); + /// /// Beings reading of the next worksheet in the workbook and optionally /// returns its title if the last worksheet has not yet been read. diff --git a/source/detail/serialization/xlsx_consumer.cpp b/source/detail/serialization/xlsx_consumer.cpp index 89161fd7..933e28e2 100644 --- a/source/detail/serialization/xlsx_consumer.cpp +++ b/source/detail/serialization/xlsx_consumer.cpp @@ -129,7 +129,8 @@ namespace detail { xlsx_consumer::xlsx_consumer(workbook &target) : target_(target), - parser_(nullptr) + parser_(nullptr), + stream_cell_(nullptr) { } @@ -150,9 +151,252 @@ cell xlsx_consumer::read_cell() return cell(nullptr); } -std::string xlsx_consumer::begin_worksheet() +std::string xlsx_consumer::read_worksheet_begin() { - return ""; + if (worksheet_queue_.empty()) + { + return ""; + } + + const auto back = worksheet_queue_.back(); + const auto rel_id = back.id(); + worksheet_queue_.pop_back(); + + auto title = std::find_if(target_.d_->sheet_title_rel_id_map_.begin(), + target_.d_->sheet_title_rel_id_map_.end(), + [&](const std::pair &p) { + return p.second == rel_id; + })->first; + + auto id = sheet_title_id_map_[title]; + auto index = sheet_title_index_map_[title]; + + auto insertion_iter = target_.d_->worksheets_.begin(); + while (insertion_iter != target_.d_->worksheets_.end() && sheet_title_index_map_[insertion_iter->title_] < index) + { + ++insertion_iter; + } + + target_.d_->worksheets_.emplace(insertion_iter, &target_, id, title); + + auto ws = target_.sheet_by_id(id); + + expect_start_element(qn("spreadsheetml", "worksheet"), xml::content::complex); // CT_Worksheet + skip_attributes({ qn("mc", "Ignorable") }); + read_namespaces(); + + xlnt::range_reference full_range; + auto &manifest = target_.manifest(); + + const auto workbook_rel = manifest.relationship(path("/"), relationship_type::office_document); + const auto sheet_rel = manifest.relationship(workbook_rel.target().path(), rel_id); + path sheet_path(sheet_rel.source().path().parent().append(sheet_rel.target().path())); + auto hyperlinks = manifest.relationships(sheet_path, xlnt::relationship_type::hyperlink); + + while (in_element(qn("spreadsheetml", "worksheet"))) + { + auto current_worksheet_element = expect_start_element(xml::content::complex); + + if (current_worksheet_element == qn("spreadsheetml", "sheetPr")) // CT_SheetPr 0-1 + { + while (in_element(current_worksheet_element)) + { + auto sheet_pr_child_element = expect_start_element(xml::content::simple); + + if (sheet_pr_child_element == qn("spreadsheetml", "tabColor")) // CT_Color 0-1 + { + read_color(); + } + else if (sheet_pr_child_element == qn("spreadsheetml", "outlinePr")) // CT_OutlinePr 0-1 + { + skip_attribute("applyStyles"); // optional, boolean, false + skip_attribute("summaryBelow"); // optional, boolean, true + skip_attribute("summaryRight"); // optional, boolean, true + skip_attribute("showOutlineSymbols"); // optional, boolean, true + } + else if (sheet_pr_child_element == qn("spreadsheetml", "pageSetUpPr")) // CT_PageSetUpPr 0-1 + { + skip_attribute("autoPageBreaks"); // optional, boolean, true + skip_attribute("fitToPage"); // optional, boolean, false + } + else + { + unexpected_element(sheet_pr_child_element); + } + + expect_end_element(sheet_pr_child_element); + } + + skip_attribute("syncHorizontal"); // optional, boolean, false + skip_attribute("syncVertical"); // optional, boolean, false + skip_attribute("syncRef"); // optional, ST_Ref, false + skip_attribute("transitionEvaluation"); // optional, boolean, false + skip_attribute("transitionEntry"); // optional, boolean, false + skip_attribute("published"); // optional, boolean, true + skip_attribute("codeName"); // optional, string + skip_attribute("filterMode"); // optional, boolean, false + skip_attribute("enableFormatConditionsCalculation"); // optional, boolean, true + } + else if (current_worksheet_element == qn("spreadsheetml", "dimension")) // CT_SheetDimension 0-1 + { + full_range = xlnt::range_reference(parser().attribute("ref")); + } + else if (current_worksheet_element == qn("spreadsheetml", "sheetViews")) // CT_SheetViews 0-1 + { + while (in_element(current_worksheet_element)) + { + expect_start_element(qn("spreadsheetml", "sheetView"), xml::content::complex); // CT_SheetView 1+ + + sheet_view new_view; + new_view.id(parser().attribute("workbookViewId")); + + if (parser().attribute_present("showGridLines")) // default="true" + { + new_view.show_grid_lines(is_true(parser().attribute("showGridLines"))); + } + + if (parser().attribute_present("defaultGridColor")) // default="true" + { + new_view.default_grid_color(is_true(parser().attribute("defaultGridColor"))); + } + + if (parser().attribute_present("view") && parser().attribute("view") != "normal") + { + new_view.type(parser().attribute("view") == "pageBreakPreview" ? sheet_view_type::page_break_preview + : sheet_view_type::page_layout); + } + + skip_attributes({ "windowProtection", "showFormulas", "showRowColHeaders", "showZeros", "rightToLeft", + "tabSelected", "showRuler", "showOutlineSymbols", "showWhiteSpace", "view", "topLeftCell", + "colorId", "zoomScale", "zoomScaleNormal", "zoomScaleSheetLayoutView", "zoomScalePageLayoutView" }); + + while (in_element(qn("spreadsheetml", "sheetView"))) + { + auto sheet_view_child_element = expect_start_element(xml::content::simple); + + if (sheet_view_child_element == qn("spreadsheetml", "pane")) // CT_Pane 0-1 + { + pane new_pane; + + if (parser().attribute_present("topLeftCell")) + { + new_pane.top_left_cell = cell_reference(parser().attribute("topLeftCell")); + } + + if (parser().attribute_present("xSplit")) + { + new_pane.x_split = parser().attribute("xSplit"); + } + + if (parser().attribute_present("ySplit")) + { + new_pane.y_split = parser().attribute("ySplit"); + } + + if (parser().attribute_present("activePane")) + { + new_pane.active_pane = parser().attribute("activePane"); + } + + if (parser().attribute_present("state")) + { + new_pane.state = parser().attribute("state"); + } + + new_view.pane(new_pane); + } + else if (sheet_view_child_element == qn("spreadsheetml", "selection")) // CT_Selection 0-4 + { + skip_remaining_content(sheet_view_child_element); + } + else if (sheet_view_child_element == qn("spreadsheetml", "pivotSelection")) // CT_PivotSelection 0-4 + { + skip_remaining_content(sheet_view_child_element); + } + else if (sheet_view_child_element == qn("spreadsheetml", "extLst")) // CT_ExtensionList 0-1 + { + skip_remaining_content(sheet_view_child_element); + } + else + { + unexpected_element(sheet_view_child_element); + } + + expect_end_element(sheet_view_child_element); + } + + expect_end_element(qn("spreadsheetml", "sheetView")); + + ws.d_->views_.push_back(new_view); + } + } + else if (current_worksheet_element == qn("spreadsheetml", "sheetFormatPr")) // CT_SheetFormatPr 0-1 + { + skip_remaining_content(current_worksheet_element); + } + else if (current_worksheet_element == qn("spreadsheetml", "cols")) // CT_Cols 0+ + { + while (in_element(qn("spreadsheetml", "cols"))) + { + expect_start_element(qn("spreadsheetml", "col"), xml::content::simple); + + skip_attributes({ "bestFit", "collapsed", "outlineLevel" }); + + auto min = static_cast(std::stoull(parser().attribute("min"))); + auto max = static_cast(std::stoull(parser().attribute("max"))); + + optional width; + + if (parser().attribute_present("width")) + { + width = parser().attribute("width"); + } + + optional column_style; + + if (parser().attribute_present("style")) + { + column_style = parser().attribute("style"); + } + + auto custom = + parser().attribute_present("customWidth") ? is_true(parser().attribute("customWidth")) : false; + auto hidden = parser().attribute_present("hidden") ? is_true(parser().attribute("hidden")) : false; + + expect_end_element(qn("spreadsheetml", "col")); + + for (auto column = min; column <= max; column++) + { + column_properties props; + + if (width.is_set()) + { + props.width = width.get(); + } + + if (column_style.is_set()) + { + props.style = column_style.get(); + } + + props.hidden = hidden; + props.custom_width = custom; + ws.add_column_properties(column, props); + } + } + } + else if (current_worksheet_element == qn("spreadsheetml", "sheetData")) // CT_SheetData 1 + { + in_sheet_data_ = true; + } + } + + if (!in_sheet_data_) + { + read_worksheet_end(); + } + + return title; } worksheet xlsx_consumer::end_worksheet() @@ -165,6 +409,16 @@ xml::parser &xlsx_consumer::parser() return *parser_; } +bool xlsx_consumer::has_cell() +{ + return stream_cell_ != nullptr; +} + +bool xlsx_consumer::has_worksheet() +{ + return !worksheet_queue_.empty(); +} + std::vector xlsx_consumer::read_relationships(const path &part) { const auto part_rels_path = part.parent().append("_rels") @@ -1434,238 +1688,17 @@ void xlsx_consumer::read_volatile_dependencies() } // CT_Worksheet -void xlsx_consumer::read_worksheet(const std::string &rel_id) +void xlsx_consumer::read_worksheet_end() { -/* - static const auto &xmlns = constants::namespace_("spreadsheetml"); - static const auto &xmlns_mc = constants::namespace_("mc"); - static const auto &xmlns_x14ac = constants::namespace_("x14ac"); - static const auto &xmlns_r = constants::namespace_("r"); -*/ - auto title = std::find_if(target_.d_->sheet_title_rel_id_map_.begin(), - target_.d_->sheet_title_rel_id_map_.end(), - [&](const std::pair &p) { - return p.second == rel_id; - })->first; - - auto id = sheet_title_id_map_[title]; - auto index = sheet_title_index_map_[title]; - - auto insertion_iter = target_.d_->worksheets_.begin(); - while (insertion_iter != target_.d_->worksheets_.end() && sheet_title_index_map_[insertion_iter->title_] < index) - { - ++insertion_iter; - } - - target_.d_->worksheets_.emplace(insertion_iter, &target_, id, title); - - auto ws = target_.sheet_by_id(id); - - expect_start_element(qn("spreadsheetml", "worksheet"), xml::content::complex); // CT_Worksheet - skip_attributes({qn("mc", "Ignorable")}); - read_namespaces(); - - xlnt::range_reference full_range; - auto &manifest = target_.manifest(); - - const auto workbook_rel = manifest.relationship(path("/"), relationship_type::office_document); - const auto sheet_rel = manifest.relationship(workbook_rel.target().path(), rel_id); - path sheet_path(sheet_rel.source().path().parent().append(sheet_rel.target().path())); - auto hyperlinks = manifest.relationships(sheet_path, xlnt::relationship_type::hyperlink); + in_sheet_data_ = false; + auto ws = worksheet(stream_worksheet_); + std::vector hyperlinks; while (in_element(qn("spreadsheetml", "worksheet"))) { auto current_worksheet_element = expect_start_element(xml::content::complex); - if (current_worksheet_element == qn("spreadsheetml", "sheetPr")) // CT_SheetPr 0-1 - { - while (in_element(current_worksheet_element)) - { - auto sheet_pr_child_element = expect_start_element(xml::content::simple); - - if (sheet_pr_child_element == qn("spreadsheetml", "tabColor")) // CT_Color 0-1 - { - read_color(); - } - else if (sheet_pr_child_element == qn("spreadsheetml", "outlinePr")) // CT_OutlinePr 0-1 - { - skip_attribute("applyStyles"); // optional, boolean, false - skip_attribute("summaryBelow"); // optional, boolean, true - skip_attribute("summaryRight"); // optional, boolean, true - skip_attribute("showOutlineSymbols"); // optional, boolean, true - } - else if (sheet_pr_child_element == qn("spreadsheetml", "pageSetUpPr")) // CT_PageSetUpPr 0-1 - { - skip_attribute("autoPageBreaks"); // optional, boolean, true - skip_attribute("fitToPage"); // optional, boolean, false - } - else - { - unexpected_element(sheet_pr_child_element); - } - - expect_end_element(sheet_pr_child_element); - } - - skip_attribute("syncHorizontal"); // optional, boolean, false - skip_attribute("syncVertical"); // optional, boolean, false - skip_attribute("syncRef"); // optional, ST_Ref, false - skip_attribute("transitionEvaluation"); // optional, boolean, false - skip_attribute("transitionEntry"); // optional, boolean, false - skip_attribute("published"); // optional, boolean, true - skip_attribute("codeName"); // optional, string - skip_attribute("filterMode"); // optional, boolean, false - skip_attribute("enableFormatConditionsCalculation"); // optional, boolean, true - } - else if (current_worksheet_element == qn("spreadsheetml", "dimension")) // CT_SheetDimension 0-1 - { - full_range = xlnt::range_reference(parser().attribute("ref")); - } - else if (current_worksheet_element == qn("spreadsheetml", "sheetViews")) // CT_SheetViews 0-1 - { - while (in_element(current_worksheet_element)) - { - expect_start_element(qn("spreadsheetml", "sheetView"), xml::content::complex); // CT_SheetView 1+ - - sheet_view new_view; - new_view.id(parser().attribute("workbookViewId")); - - if (parser().attribute_present("showGridLines")) // default="true" - { - new_view.show_grid_lines(is_true(parser().attribute("showGridLines"))); - } - - if (parser().attribute_present("defaultGridColor")) // default="true" - { - new_view.default_grid_color(is_true(parser().attribute("defaultGridColor"))); - } - - if (parser().attribute_present("view") && parser().attribute("view") != "normal") - { - new_view.type(parser().attribute("view") == "pageBreakPreview" ? sheet_view_type::page_break_preview - : sheet_view_type::page_layout); - } - - skip_attributes({"windowProtection", "showFormulas", "showRowColHeaders", "showZeros", "rightToLeft", - "tabSelected", "showRuler", "showOutlineSymbols", "showWhiteSpace", "view", "topLeftCell", - "colorId", "zoomScale", "zoomScaleNormal", "zoomScaleSheetLayoutView", "zoomScalePageLayoutView"}); - - while (in_element(qn("spreadsheetml", "sheetView"))) - { - auto sheet_view_child_element = expect_start_element(xml::content::simple); - - if (sheet_view_child_element == qn("spreadsheetml", "pane")) // CT_Pane 0-1 - { - pane new_pane; - - if (parser().attribute_present("topLeftCell")) - { - new_pane.top_left_cell = cell_reference(parser().attribute("topLeftCell")); - } - - if (parser().attribute_present("xSplit")) - { - new_pane.x_split = parser().attribute("xSplit"); - } - - if (parser().attribute_present("ySplit")) - { - new_pane.y_split = parser().attribute("ySplit"); - } - - if (parser().attribute_present("activePane")) - { - new_pane.active_pane = parser().attribute("activePane"); - } - - if (parser().attribute_present("state")) - { - new_pane.state = parser().attribute("state"); - } - - new_view.pane(new_pane); - } - else if (sheet_view_child_element == qn("spreadsheetml", "selection")) // CT_Selection 0-4 - { - skip_remaining_content(sheet_view_child_element); - } - else if (sheet_view_child_element == qn("spreadsheetml", "pivotSelection")) // CT_PivotSelection 0-4 - { - skip_remaining_content(sheet_view_child_element); - } - else if (sheet_view_child_element == qn("spreadsheetml", "extLst")) // CT_ExtensionList 0-1 - { - skip_remaining_content(sheet_view_child_element); - } - else - { - unexpected_element(sheet_view_child_element); - } - - expect_end_element(sheet_view_child_element); - } - - expect_end_element(qn("spreadsheetml", "sheetView")); - - ws.d_->views_.push_back(new_view); - } - } - else if (current_worksheet_element == qn("spreadsheetml", "sheetFormatPr")) // CT_SheetFormatPr 0-1 - { - skip_remaining_content(current_worksheet_element); - } - else if (current_worksheet_element == qn("spreadsheetml", "cols")) // CT_Cols 0+ - { - while (in_element(qn("spreadsheetml", "cols"))) - { - expect_start_element(qn("spreadsheetml", "col"), xml::content::simple); - - skip_attributes({"bestFit", "collapsed", "outlineLevel"}); - - auto min = static_cast(std::stoull(parser().attribute("min"))); - auto max = static_cast(std::stoull(parser().attribute("max"))); - - optional width; - - if (parser().attribute_present("width")) - { - width = parser().attribute("width"); - } - - optional column_style; - - if (parser().attribute_present("style")) - { - column_style = parser().attribute("style"); - } - - auto custom = - parser().attribute_present("customWidth") ? is_true(parser().attribute("customWidth")) : false; - auto hidden = parser().attribute_present("hidden") ? is_true(parser().attribute("hidden")) : false; - - expect_end_element(qn("spreadsheetml", "col")); - - for (auto column = min; column <= max; column++) - { - column_properties props; - - if (width.is_set()) - { - props.width = width.get(); - } - - if (column_style.is_set()) - { - props.style = column_style.get(); - } - - props.hidden = hidden; - props.custom_width = custom; - ws.add_column_properties(column, props); - } - } - } - else if (current_worksheet_element == qn("spreadsheetml", "sheetData")) // CT_SheetData 1 + if (current_worksheet_element == qn("spreadsheetml", "sheetData")) // CT_SheetData 1 { while (in_element(qn("spreadsheetml", "sheetData"))) { @@ -2089,10 +2122,15 @@ void xlsx_consumer::read_worksheet(const std::string &rel_id) expect_end_element(qn("spreadsheetml", "worksheet")); + auto &manifest = target_.manifest(); + + xlnt::path sheet_path; + relationship workbook_rel, sheet_rel; + if (manifest.has_relationship(sheet_path, xlnt::relationship_type::comments)) { - auto comments_part = manifest.canonicalize( - {workbook_rel, sheet_rel, manifest.relationship(sheet_path, xlnt::relationship_type::comments)}); + auto comments_part = manifest.canonicalize({workbook_rel, sheet_rel, + manifest.relationship(sheet_path, xlnt::relationship_type::comments)}); auto receive = xml::parser::receive_default; auto comments_part_streambuf = archive_->open(comments_part); @@ -2104,8 +2142,8 @@ void xlsx_consumer::read_worksheet(const std::string &rel_id) if (manifest.has_relationship(sheet_path, xlnt::relationship_type::vml_drawing)) { - auto vml_drawings_part = manifest.canonicalize( - {workbook_rel, sheet_rel, manifest.relationship(sheet_path, xlnt::relationship_type::vml_drawing)}); + auto vml_drawings_part = manifest.canonicalize({workbook_rel, sheet_rel, + manifest.relationship(sheet_path, xlnt::relationship_type::vml_drawing)}); auto vml_drawings_part_streambuf = archive_->open(comments_part); std::istream vml_drawings_part_stream(comments_part_streambuf.get()); diff --git a/source/detail/serialization/xlsx_consumer.hpp b/source/detail/serialization/xlsx_consumer.hpp index 225e5f22..c34f048a 100644 --- a/source/detail/serialization/xlsx_consumer.hpp +++ b/source/detail/serialization/xlsx_consumer.hpp @@ -45,6 +45,7 @@ template class optional; class path; class relationship; +class streaming_workbook_reader; class variant; class workbook; class worksheet; @@ -52,6 +53,8 @@ class worksheet; namespace detail { class izstream; +struct cell_impl; +struct worksheet_impl; /// /// Handles writing a workbook into an XLSX file. @@ -61,8 +64,18 @@ class xlsx_consumer public: xlsx_consumer(workbook &destination); + void read(std::istream &source); + + void read(std::istream &source, const std::string &password); + +private: + friend class streaming_workbook_reader; + + void open(std::istream &source); + bool has_cell(); + /// /// Reads the next cell in the current worksheet and optionally returns it if /// the last cell in the sheet has not yet been read. An exception will be thrown @@ -70,13 +83,6 @@ public: /// cell read_cell(); - /// - /// Beings reading of the next worksheet in the workbook and optionally - /// returns its title if the last worksheet has not yet been read. An - /// exception will be thrown if this is not open as a streaming consumer. - /// - std::string begin_worksheet(); - /// /// Ends reading of the current worksheet in the workbook and optionally /// returns a worksheet object corresponding to the worksheet with the title @@ -85,11 +91,8 @@ public: /// worksheet end_worksheet(); - void read(std::istream &source); + bool has_worksheet(); - void read(std::istream &source, const std::string &password); - -private: /// /// Read all the files needed from the XLSX archive and initialize all of /// the data in the workbook to match. @@ -208,6 +211,16 @@ private: /// void read_worksheet(const std::string &title); + /// + /// xl/sheets/*.xml + /// + std::string read_worksheet_begin(); + + /// + /// xl/sheets/*.xml + /// + void read_worksheet_end(); + // Sheet Relationship Target Parts /// @@ -398,6 +411,14 @@ private: std::vector stack_; bool preserve_space_ = false; + + std::vector worksheet_queue_; + + detail::cell_impl *stream_cell_; + + detail::worksheet_impl *stream_worksheet_; + + bool in_sheet_data_; }; } // namespace detail diff --git a/source/workbook/streaming_workbook_reader.cpp b/source/workbook/streaming_workbook_reader.cpp index 84f3d5f0..462f172d 100644 --- a/source/workbook/streaming_workbook_reader.cpp +++ b/source/workbook/streaming_workbook_reader.cpp @@ -32,6 +32,45 @@ #include #include + +namespace { + +//TODO: (important) this is duplicated from workbook.cpp, find a common place to keep it +#ifdef _MSC_VER +void open_stream(std::ifstream &stream, const std::wstring &path) +{ + stream.open(path, std::ios::binary); +} + +void open_stream(std::ofstream &stream, const std::wstring &path) +{ + stream.open(path, std::ios::binary); +} + +void open_stream(std::ifstream &stream, const std::string &path) +{ + open_stream(stream, xlnt::path(path).wstring()); +} + +void open_stream(std::ofstream &stream, const std::string &path) +{ + open_stream(stream, xlnt::path(path).wstring()); +} +#else +void open_stream(std::ifstream &stream, const std::string &path) +{ + stream.open(path, std::ios::binary); +} + +void open_stream(std::ofstream &stream, const std::string &path) +{ + stream.open(path, std::ios::binary); +} +#endif + +} // namespace + + namespace xlnt { streaming_workbook_reader::~streaming_workbook_reader() @@ -41,10 +80,15 @@ streaming_workbook_reader::~streaming_workbook_reader() void streaming_workbook_reader::close() { - if (consumer_) - { - consumer_.reset(nullptr); - } + if (consumer_) + { + consumer_.reset(nullptr); + } +} + +bool streaming_workbook_reader::has_cell() +{ + return consumer_->has_cell(); } cell streaming_workbook_reader::read_cell() @@ -52,9 +96,14 @@ cell streaming_workbook_reader::read_cell() return consumer_->read_cell(); } +bool streaming_workbook_reader::has_worksheet() +{ + return consumer_->has_worksheet(); +} + std::string streaming_workbook_reader::begin_worksheet() { - return consumer_->begin_worksheet(); + return consumer_->read_worksheet_begin(); } worksheet streaming_workbook_reader::end_worksheet() @@ -71,21 +120,22 @@ void streaming_workbook_reader::open(const std::vector &data) void streaming_workbook_reader::open(const std::string &filename) { - std::ifstream file_stream(filename, std::ios::binary); - open(file_stream); + std::ifstream file_stream; + open_stream(file_stream, filename); } #ifdef _MSC_VER void streaming_workbook_reader::open(const std::wstring &filename) { - std::ifstream file_stream(filename, std::ios::binary); - open(file_stream); + std::ifstream file_stream; + open_stream(file_stream, filename); } #endif void streaming_workbook_reader::open(const xlnt::path &filename) { - open(filename.string()); + std::ifstream file_stream; + open_stream(file_stream, filename.string()); } void streaming_workbook_reader::open(std::istream &stream) diff --git a/tests/workbook/serialization_test_suite.hpp b/tests/workbook/serialization_test_suite.hpp index aa2d6def..8449d016 100644 --- a/tests/workbook/serialization_test_suite.hpp +++ b/tests/workbook/serialization_test_suite.hpp @@ -31,6 +31,8 @@ #include #include #include +#include +#include #include class serialization_test_suite : public test_suite @@ -461,4 +463,42 @@ public: xlnt_assert(round_trip_matches_rw(path, password)); } } + + void test_streaming_read() + { + const auto path = path_helper::test_file("4_every_style.xlsx"); + xlnt::streaming_workbook_reader reader; + + reader.open(path); + + while (reader.has_worksheet()) + { + reader.begin_worksheet(); + + while (reader.has_cell()) + { + const auto cell = reader.read_cell(); + std::cout << cell.reference().to_string() << std::endl; + } + + const auto ws = reader.end_worksheet(); + } + } + + void test_streaming_write() + { + const auto path = std::string("stream-out.xlsx"); + xlnt::streaming_workbook_writer writer; + + writer.open(path); + + writer.add_sheet("stream"); + + auto b2 = writer.add_cell("B2"); + b2.value("B2!"); + + auto c3 = writer.add_cell("C3"); + b2.value("should not change"); + c3.value("C3!"); + } };