diff --git a/include/xlnt/worksheet/row_properties.hpp b/include/xlnt/worksheet/row_properties.hpp index d78c4313..a2e92e25 100644 --- a/include/xlnt/worksheet/row_properties.hpp +++ b/include/xlnt/worksheet/row_properties.hpp @@ -63,6 +63,13 @@ public: /// The index to the style used by all cells in this row /// optional style; + + /// + /// 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. + /// + optional spans; }; inline bool operator==(const row_properties &lhs, const row_properties &rhs) @@ -72,7 +79,8 @@ inline bool operator==(const row_properties &lhs, const row_properties &rhs) && lhs.custom_height == rhs.custom_height && lhs.hidden == rhs.hidden && lhs.custom_format == rhs.custom_format - && lhs.style == rhs.style; + && lhs.style == rhs.style + && lhs.spans == rhs.spans; } } // namespace xlnt diff --git a/source/detail/serialization/xlsx_consumer.cpp b/source/detail/serialization/xlsx_consumer.cpp index 9a05603f..70ea0446 100644 --- a/source/detail/serialization/xlsx_consumer.cpp +++ b/source/detail/serialization/xlsx_consumer.cpp @@ -221,9 +221,13 @@ cell xlsx_consumer::read_cell() row_properties.dy_descent = parser().attribute(qn("x14ac", "dyDescent")); } + if (parser().attribute_present("spans")) { + row_properties.spans = parser().attribute("spans"); + } + skip_attributes({"customFormat", "s", "customFont", "outlineLevel", "collapsed", "thickTop", "thickBot", - "ph", "spans"}); + "ph"}); } if (!in_element(qn("spreadsheetml", "row"))) @@ -713,9 +717,13 @@ void xlsx_consumer::read_worksheet_sheetdata() row_properties.custom_format.set(parser().attribute("customFormat")); } + if (parser().attribute_present("spans")) { + row_properties.spans = parser().attribute("spans"); + } + skip_attributes({"customFont", "outlineLevel", "collapsed", "thickTop", "thickBot", - "ph", "spans"}); + "ph"}); while (in_element(qn("spreadsheetml", "row"))) { diff --git a/source/detail/serialization/xlsx_producer.cpp b/source/detail/serialization/xlsx_producer.cpp index f8172c16..202a1d0d 100644 --- a/source/detail/serialization/xlsx_producer.cpp +++ b/source/detail/serialization/xlsx_producer.cpp @@ -2439,6 +2439,10 @@ void xlsx_producer::write_worksheet(const relationship &rel) // See note for CT_Row, span attribute about block optimization if (first_row_in_block) { + // reset block column range + first_block_column = constants::max_column(); + last_block_column = constants::min_column(); + first_check_row = row; // round up to the next multiple of 16 last_check_row = ((row / 16) + 1) * 16; @@ -2448,8 +2452,8 @@ void xlsx_producer::write_worksheet(const relationship &rel) { for (auto column = dimension.top_left().column(); column <= dimension.bottom_right().column(); ++column) { - if (!ws.has_cell(cell_reference(column, row))) continue; - auto cell = ws.cell(cell_reference(column, row)); + if (!ws.has_cell(cell_reference(column, check_row))) continue; + auto cell = ws.cell(cell_reference(column, check_row)); if (cell.garbage_collectible()) continue; first_block_column = std::min(first_block_column, cell.column()); diff --git a/source/worksheet/worksheet.cpp b/source/worksheet/worksheet.cpp index 23c9ea7c..2d4c8881 100644 --- a/source/worksheet/worksheet.cpp +++ b/source/worksheet/worksheet.cpp @@ -577,7 +577,7 @@ column_t worksheet::highest_column_or_props() const range_reference worksheet::calculate_dimension() const { - return range_reference(lowest_column(), lowest_row(), + return range_reference(lowest_column(), lowest_row_or_props(), highest_column(), highest_row_or_props()); } diff --git a/tests/data/Issue353_first_row_empty_w_properties.xlsx b/tests/data/Issue353_first_row_empty_w_properties.xlsx new file mode 100644 index 00000000..65cdbe87 Binary files /dev/null and b/tests/data/Issue353_first_row_empty_w_properties.xlsx differ diff --git a/tests/workbook/workbook_test_suite.cpp b/tests/workbook/workbook_test_suite.cpp index 9f155087..f883e359 100644 --- a/tests/workbook/workbook_test_suite.cpp +++ b/tests/workbook/workbook_test_suite.cpp @@ -67,6 +67,7 @@ public: register_test(test_id_gen); register_test(test_load_file); register_test(test_Issue279); + register_test(test_Issue353); } void test_active_sheet() @@ -495,5 +496,18 @@ public: //save a copy file wb.save("temp.xlsx"); } + + void test_Issue353() + { + xlnt::workbook wb1; + wb1.load(path_helper::test_file("Issue353_first_row_empty_w_properties.xlsx")); + wb1.save("temp_issue353.xlsx"); + + xlnt::workbook wb2; + wb2.load("temp_issue353.xlsx"); + auto ws = wb2.active_sheet(); + xlnt_assert_equals(ws.row_properties(1).spans.get(), "1:8"); + xlnt_assert_equals(ws.row_properties(17).spans.get(), "2:7"); + } }; static workbook_test_suite x; \ No newline at end of file