diff --git a/include/xlnt/worksheet/worksheet.hpp b/include/xlnt/worksheet/worksheet.hpp
index aca470e71..018d8fc08 100644
--- a/include/xlnt/worksheet/worksheet.hpp
+++ b/include/xlnt/worksheet/worksheet.hpp
@@ -370,11 +370,21 @@ class XLNT_API worksheet
///
row_t lowest_row() const;
+ ///
+ /// Returns the row of the first non-empty cell or lowest row with properties in the worksheet.
+ ///
+ row_t lowest_row_or_props() const;
+
///
/// Returns the row of the last non-empty cell in the worksheet.
///
row_t highest_row() const;
+ ///
+ /// Returns the row of the last non-empty cell or highest row with properties in the worksheet.
+ ///
+ row_t highest_row_or_props() const;
+
///
/// Returns the row directly below the last non-empty cell in the worksheet.
///
@@ -385,11 +395,21 @@ class XLNT_API worksheet
///
column_t lowest_column() const;
+ ///
+ /// Returns the column of the first non-empty cell or lowest column with properties in the worksheet.
+ ///
+ column_t lowest_column_or_props() const;
+
///
/// Returns the column of the last non-empty cell in the worksheet.
///
column_t highest_column() const;
+ ///
+ /// Returns the column of the last non-empty cell or highest column with properties in the worksheet.
+ ///
+ column_t highest_column_or_props() const;
+
///
/// Returns a range_reference pointing to the full range of non-empty cells in the worksheet.
///
diff --git a/source/detail/serialization/xlsx_producer.cpp b/source/detail/serialization/xlsx_producer.cpp
index dfbe3bb81..fd660d5d0 100644
--- a/source/detail/serialization/xlsx_producer.cpp
+++ b/source/detail/serialization/xlsx_producer.cpp
@@ -2030,8 +2030,9 @@ void xlsx_producer::write_worksheet(const relationship &rel)
write_start_element(xmlns, "dimension");
const auto dimension = ws.calculate_dimension();
- write_attribute(
- "ref", dimension.is_single_cell() ? dimension.top_left().to_string() : dimension.to_string());
+ write_attribute("ref", dimension.is_single_cell()
+ ? dimension.top_left().to_string()
+ : dimension.to_string());
write_end_element(xmlns, "dimension");
if (ws.has_view())
@@ -2123,7 +2124,7 @@ void xlsx_producer::write_worksheet(const relationship &rel)
{
write_start_element(xmlns, "cols");
- for (auto column = ws.lowest_column(); column <= ws.highest_column(); column++)
+ for (auto column = ws.lowest_column_or_props(); column <= ws.highest_column_or_props(); column++)
{
if (!ws.has_column_properties(column)) continue;
@@ -2172,16 +2173,20 @@ void xlsx_producer::write_worksheet(const relationship &rel)
write_start_element(xmlns, "sheetData");
- for (auto row : ws.rows())
+ for (auto row = ws.lowest_row_or_props(); row <= ws.highest_row_or_props(); ++row)
{
- auto min = static_cast(row.length());
- xlnt::row_t max = 0;
+ auto first_column = constants::max_column();
+ auto last_column = constants::min_column();
bool any_non_null = false;
- for (auto cell : row)
+ for (auto column = dimension.top_left().column(); column <= dimension.bottom_right().column(); ++column)
{
- min = std::min(min, cell.column().index);
- max = std::max(max, cell.column().index);
+ if (!ws.has_cell(cell_reference(column, row))) continue;
+
+ auto cell = ws.cell(cell_reference(column, row));
+
+ first_column = std::min(first_column, cell.column());
+ last_column = std::max(last_column, cell.column());
if (!cell.garbage_collectible())
{
@@ -2189,19 +2194,20 @@ void xlsx_producer::write_worksheet(const relationship &rel)
}
}
- if (!any_non_null)
- {
- continue;
- }
+ if (!any_non_null && !ws.has_row_properties(row)) continue;
write_start_element(xmlns, "row");
+ write_attribute("r", row);
- write_attribute("r", row.front().row());
- write_attribute("spans", std::to_string(min) + ":" + std::to_string(max));
+ if (any_non_null)
+ {
+ auto span_string = std::to_string(first_column.index) + ":" + std::to_string(last_column.index);
+ write_attribute("spans", span_string);
+ }
- if (ws.has_row_properties(row.front().row()))
+ if (ws.has_row_properties(row))
{
- const auto &props = ws.row_properties(row.front().row());
+ const auto &props = ws.row_properties(row);
if (props.custom_height)
{
@@ -2228,130 +2234,137 @@ void xlsx_producer::write_worksheet(const relationship &rel)
}
}
- for (auto cell : row) // CT_Cell
+ if (any_non_null)
{
- if (cell.garbage_collectible()) continue;
+ for (auto column = dimension.top_left().column(); column <= dimension.bottom_right().column(); ++column)
+ {
+ if (!ws.has_cell(cell_reference(column, row))) continue;
- // record data about the cell needed later
+ auto cell = ws.cell(cell_reference(column, row));
- if (cell.has_comment())
- {
- cells_with_comments.push_back(cell.reference());
- }
+ if (cell.garbage_collectible()) continue;
- if (cell.has_hyperlink())
- {
- hyperlink_references[cell.reference().to_string()] = reverse_hyperlink_references[cell.hyperlink()];
- }
+ // record data about the cell needed later
- write_start_element(xmlns, "c");
+ if (cell.has_comment())
+ {
+ cells_with_comments.push_back(cell.reference());
+ }
- // begin cell attributes
+ if (cell.has_hyperlink())
+ {
+ hyperlink_references[cell.reference().to_string()] = reverse_hyperlink_references[cell.hyperlink()];
+ }
- write_attribute("r", cell.reference().to_string());
+ write_start_element(xmlns, "c");
- if (cell.has_format())
- {
- write_attribute("s", cell.format().d_->id);
- }
+ // begin cell attributes
- switch (cell.data_type())
- {
- case cell::type::empty:
- break;
+ write_attribute("r", cell.reference().to_string());
- case cell::type::boolean:
- write_attribute("t", "b");
- break;
+ if (cell.has_format())
+ {
+ write_attribute("s", cell.format().d_->id);
+ }
- case cell::type::date:
- write_attribute("t", "d");
- break;
+ switch (cell.data_type())
+ {
+ case cell::type::empty:
+ break;
- case cell::type::error:
- write_attribute("t", "e");
- break;
+ case cell::type::boolean:
+ write_attribute("t", "b");
+ break;
- case cell::type::inline_string:
- write_attribute("t", "inlineStr");
- break;
+ case cell::type::date:
+ write_attribute("t", "d");
+ break;
- case cell::type::number:
- write_attribute("t", "n");
- break;
+ case cell::type::error:
+ write_attribute("t", "e");
+ break;
- case cell::type::shared_string:
- write_attribute("t", "s");
- break;
+ case cell::type::inline_string:
+ write_attribute("t", "inlineStr");
+ break;
- case cell::type::formula_string:
- write_attribute("t", "str");
- break;
- }
+ case cell::type::number:
+ write_attribute("t", "n");
+ break;
- //write_attribute("cm", "");
- //write_attribute("vm", "");
- //write_attribute("ph", "");
+ case cell::type::shared_string:
+ write_attribute("t", "s");
+ break;
- // begin child elements
+ case cell::type::formula_string:
+ write_attribute("t", "str");
+ break;
+ }
- if (cell.has_formula())
- {
- write_element(xmlns, "f", cell.formula());
- }
+ //write_attribute("cm", "");
+ //write_attribute("vm", "");
+ //write_attribute("ph", "");
- switch (cell.data_type())
- {
- case cell::type::empty:
- break;
+ // begin child elements
- case cell::type::boolean:
- write_element(xmlns, "v", write_bool(cell.value()));
- break;
+ if (cell.has_formula())
+ {
+ write_element(xmlns, "f", cell.formula());
+ }
- case cell::type::date:
- write_element(xmlns, "v", cell.value());
- break;
+ switch (cell.data_type())
+ {
+ case cell::type::empty:
+ break;
- case cell::type::error:
- write_element(xmlns, "v", cell.value());
- break;
+ case cell::type::boolean:
+ write_element(xmlns, "v", write_bool(cell.value()));
+ break;
- case cell::type::inline_string:
- write_start_element(xmlns, "is");
- // TODO: make a write_rich_text method and use that here
- write_element(xmlns, "t", cell.value());
- write_end_element(xmlns, "is");
- break;
+ case cell::type::date:
+ write_element(xmlns, "v", cell.value());
+ break;
- case cell::type::number:
- write_start_element(xmlns, "v");
+ case cell::type::error:
+ write_element(xmlns, "v", cell.value());
+ break;
- if (is_integral(cell.value()))
- {
- write_characters(static_cast(cell.value()));
- }
- else
- {
- std::stringstream ss;
- ss.precision(20);
- ss << cell.value();
- write_characters(ss.str());
- }
+ case cell::type::inline_string:
+ write_start_element(xmlns, "is");
+ // TODO: make a write_rich_text method and use that here
+ write_element(xmlns, "t", cell.value());
+ write_end_element(xmlns, "is");
+ break;
- write_end_element(xmlns, "v");
- break;
+ case cell::type::number:
+ write_start_element(xmlns, "v");
- case cell::type::shared_string:
- write_element(xmlns, "v", static_cast(cell.d_->value_numeric_));
- break;
+ if (is_integral(cell.value()))
+ {
+ write_characters(static_cast(cell.value()));
+ }
+ else
+ {
+ std::stringstream ss;
+ ss.precision(20);
+ ss << cell.value();
+ write_characters(ss.str());
+ }
- case cell::type::formula_string:
- write_element(xmlns, "v", cell.value());
- break;
- }
+ write_end_element(xmlns, "v");
+ break;
+
+ case cell::type::shared_string:
+ write_element(xmlns, "v", static_cast(cell.d_->value_numeric_));
+ break;
- write_end_element(xmlns, "c");
+ case cell::type::formula_string:
+ write_element(xmlns, "v", cell.value());
+ break;
+ }
+
+ write_end_element(xmlns, "c");
+ }
}
write_end_element(xmlns, "row");
diff --git a/source/worksheet/worksheet.cpp b/source/worksheet/worksheet.cpp
index 7078cba27..830d3eb46 100644
--- a/source/worksheet/worksheet.cpp
+++ b/source/worksheet/worksheet.cpp
@@ -481,7 +481,7 @@ column_t worksheet::lowest_column() const
return constants::min_column();
}
- column_t lowest = constants::max_column();
+ auto lowest = constants::max_column();
for (auto &row : d_->cell_map_)
{
@@ -494,6 +494,23 @@ column_t worksheet::lowest_column() const
return lowest;
}
+column_t worksheet::lowest_column_or_props() const
+{
+ auto lowest = lowest_column();
+
+ if (d_->cell_map_.empty() && !d_->column_properties_.empty())
+ {
+ lowest = d_->column_properties_.begin()->first;
+ }
+
+ for (auto &props : d_->column_properties_)
+ {
+ lowest = std::min(lowest, props.first);
+ }
+
+ return lowest;
+}
+
row_t worksheet::lowest_row() const
{
if (d_->cell_map_.empty())
@@ -501,7 +518,7 @@ row_t worksheet::lowest_row() const
return constants::min_row();
}
- row_t lowest = constants::max_row();
+ auto lowest = constants::max_row();
for (auto &row : d_->cell_map_)
{
@@ -511,9 +528,26 @@ row_t worksheet::lowest_row() const
return lowest;
}
+row_t worksheet::lowest_row_or_props() const
+{
+ auto lowest = lowest_row();
+
+ if (d_->cell_map_.empty() && !d_->row_properties_.empty())
+ {
+ lowest = d_->row_properties_.begin()->first;
+ }
+
+ for (auto &props : d_->row_properties_)
+ {
+ lowest = std::min(lowest, props.first);
+ }
+
+ return lowest;
+}
+
row_t worksheet::highest_row() const
{
- row_t highest = constants::min_row();
+ auto highest = constants::min_row();
for (auto &row : d_->cell_map_)
{
@@ -523,9 +557,26 @@ row_t worksheet::highest_row() const
return highest;
}
+row_t worksheet::highest_row_or_props() const
+{
+ auto highest = highest_row();
+
+ if (d_->cell_map_.empty() && !d_->row_properties_.empty())
+ {
+ highest = d_->row_properties_.begin()->first;
+ }
+
+ for (auto &props : d_->row_properties_)
+ {
+ highest = std::max(highest, props.first);
+ }
+
+ return highest;
+}
+
column_t worksheet::highest_column() const
{
- column_t highest = constants::min_column();
+ auto highest = constants::min_column();
for (auto &row : d_->cell_map_)
{
@@ -538,6 +589,23 @@ column_t worksheet::highest_column() const
return highest;
}
+column_t worksheet::highest_column_or_props() const
+{
+ auto highest = highest_column();
+
+ if (d_->cell_map_.empty() && !d_->column_properties_.empty())
+ {
+ highest = d_->column_properties_.begin()->first;
+ }
+
+ for (auto &props : d_->column_properties_)
+ {
+ highest = std::max(highest, props.first);
+ }
+
+ return highest;
+}
+
range_reference worksheet::calculate_dimension() const
{
return range_reference(lowest_column(), lowest_row(), highest_column(), highest_row());
diff --git a/tests/data/13_custom_heights_widths.xlsx b/tests/data/13_custom_heights_widths.xlsx
index 13d7a54d5..875edd538 100644
Binary files a/tests/data/13_custom_heights_widths.xlsx and b/tests/data/13_custom_heights_widths.xlsx differ
diff --git a/tests/workbook/serialization_test_suite.hpp b/tests/workbook/serialization_test_suite.hpp
index 89ccbf00a..da4db5aef 100644
--- a/tests/workbook/serialization_test_suite.hpp
+++ b/tests/workbook/serialization_test_suite.hpp
@@ -395,22 +395,27 @@ class serialization_test_suite : public test_suite
wb.load(path_helper::test_file("13_custom_heights_widths.xlsx"));
auto ws = wb.active_sheet();
- xlnt_assert_equals(ws.cell("A1").value(), "170xd");
- xlnt_assert_equals(ws.cell("B1").value(), "40xd");
- xlnt_assert_equals(ws.cell("C1").value(), "dxd");
- xlnt_assert_equals(ws.cell("A2").value(), "170x30");
- xlnt_assert_equals(ws.cell("B2").value(), "40x30");
- xlnt_assert_equals(ws.cell("C2").value(), "dx30");
- xlnt_assert_equals(ws.cell("A3").value(), "170x10");
- xlnt_assert_equals(ws.cell("B3").value(), "40x10");
- xlnt_assert_equals(ws.cell("C3").value(), "dx10");
-
- xlnt_assert(!ws.row_properties(1).height.is_set());
- xlnt_assert_equals(ws.row_properties(2).height.get(), 30);
- xlnt_assert_equals(ws.row_properties(3).height.get(), 10);
- xlnt_assert_delta(ws.column_properties("A").width.get(), 27.617745535714285, 1.0E-9);
- xlnt_assert_delta(ws.column_properties("B").width.get(), 5.9497767857142856, 1.0E-9);
- xlnt_assert(!ws.column_properties("C").width.is_set());
+ xlnt_assert_equals(ws.cell("A1").value(), "A1");
+ xlnt_assert_equals(ws.cell("B1").value(), "B1");
+ xlnt_assert_equals(ws.cell("D1").value(), "D1");
+ xlnt_assert_equals(ws.cell("A2").value(), "A2");
+ xlnt_assert_equals(ws.cell("B2").value(), "B2");
+ xlnt_assert_equals(ws.cell("D2").value(), "D2");
+ xlnt_assert_equals(ws.cell("A4").value(), "A4");
+ xlnt_assert_equals(ws.cell("B4").value(), "B4");
+ xlnt_assert_equals(ws.cell("D4").value(), "D4");
+
+ xlnt_assert_equals(ws.row_properties(1).height.get(), 100);
+ xlnt_assert(!ws.row_properties(2).height.is_set());
+ xlnt_assert_equals(ws.row_properties(3).height.get(), 100);
+ xlnt_assert(!ws.row_properties(4).height.is_set());
+ xlnt_assert_equals(ws.row_properties(5).height.get(), 100);
+
+ xlnt_assert_delta(ws.column_properties("A").width.get(), 15.949776785714286, 1.0E-9);
+ xlnt_assert(!ws.column_properties("B").width.is_set());
+ xlnt_assert_delta(ws.column_properties("C").width.get(), 15.949776785714286, 1.0E-9);
+ xlnt_assert(!ws.column_properties("D").width.is_set());
+ xlnt_assert_delta(ws.column_properties("E").width.get(), 15.949776785714286, 1.0E-9);
}
void test_write_custom_heights_widths()
@@ -418,21 +423,33 @@ class serialization_test_suite : public test_suite
xlnt::workbook wb;
auto ws = wb.active_sheet();
- ws.cell("A1").value("170xd");
- ws.cell("B1").value("40xd");
- ws.cell("C1").value("dxd");
- ws.cell("A2").value("170x30");
- ws.cell("B2").value("40x30");
- ws.cell("C2").value("dx30");
- ws.cell("A3").value("170x10");
- ws.cell("B3").value("40x10");
- ws.cell("C3").value("dx10");
-
- ws.row_properties(2).height = 30;
- ws.row_properties(3).height = 10;
-
- ws.column_properties("A").width = 27.617745535714285;
- ws.column_properties("B").width = 5.9497767857142856;
+ ws.cell("A1").value("A1");
+ ws.cell("B1").value("B1");
+ ws.cell("D1").value("D1");
+ ws.cell("A2").value("A2");
+ ws.cell("B2").value("B2");
+ ws.cell("D2").value("D2");
+ ws.cell("A4").value("A4");
+ ws.cell("B4").value("B4");
+ ws.cell("D4").value("D4");
+
+ ws.row_properties(1).height = 100;
+ ws.row_properties(1).custom_height = true;
+
+ ws.row_properties(3).height = 100;
+ ws.row_properties(3).custom_height = true;
+
+ ws.row_properties(5).height = 100;
+ ws.row_properties(5).custom_height = true;
+
+ ws.column_properties("A").width = 15.949776785714286;
+ ws.column_properties("A").custom_width = true;
+
+ ws.column_properties("C").width = 15.949776785714286;
+ ws.column_properties("C").custom_width = true;
+
+ ws.column_properties("E").width = 15.949776785714286;
+ ws.column_properties("E").custom_width = true;
wb.save("temp.xlsx");
xlnt_assert(workbook_matches_file(wb, path_helper::test_file("13_custom_heights_widths.xlsx")));
diff --git a/tests/worksheet/worksheet_test_suite.hpp b/tests/worksheet/worksheet_test_suite.hpp
index 5f308e141..be0b7c1b8 100644
--- a/tests/worksheet/worksheet_test_suite.hpp
+++ b/tests/worksheet/worksheet_test_suite.hpp
@@ -68,10 +68,14 @@ class worksheet_test_suite : public test_suite
register_test(test_freeze_panes_horiz);
register_test(test_freeze_panes_vert);
register_test(test_freeze_panes_both);
- register_test(test_min_column);
- register_test(test_max_column);
- register_test(test_min_row);
- register_test(test_max_row);
+ register_test(test_lowest_column);
+ register_test(test_lowest_column_or_props);
+ register_test(test_highest_column);
+ register_test(test_highest_column_or_props);
+ register_test(test_lowest_row);
+ register_test(test_lowest_row_or_props);
+ register_test(test_highest_row);
+ register_test(test_highest_row_or_props);
register_test(test_const_iterators);
register_test(test_const_reverse_iterators);
register_test(test_column_major_iterators);
@@ -503,14 +507,22 @@ class worksheet_test_suite : public test_suite
xlnt_assert_equals(view.pane().y_split, 3);
}
- void test_min_column()
+ void test_lowest_column()
{
xlnt::workbook wb;
auto ws = wb.active_sheet();
xlnt_assert_equals(ws.lowest_column(), 1);
}
- void test_max_column()
+ void test_lowest_column_or_props()
+ {
+ xlnt::workbook wb;
+ auto ws = wb.active_sheet();
+ ws.column_properties("J").width = 14.3;
+ xlnt_assert_equals(ws.lowest_column_or_props(), "J");
+ }
+
+ void test_highest_column()
{
xlnt::workbook wb;
auto ws = wb.active_sheet();
@@ -518,17 +530,33 @@ class worksheet_test_suite : public test_suite
ws[xlnt::cell_reference("F2")].value(32);
ws[xlnt::cell_reference("F3")].formula("=F1+F2");
ws[xlnt::cell_reference("A4")].formula("=A1+A2+A3");
- xlnt_assert_equals(ws.highest_column(), 6);
+ xlnt_assert_equals(ws.highest_column(), "F");
+ }
+
+ void test_highest_column_or_props()
+ {
+ xlnt::workbook wb;
+ auto ws = wb.active_sheet();
+ ws.column_properties("J").width = 14.3;
+ xlnt_assert_equals(ws.highest_column_or_props(), "J");
}
- void test_min_row()
+ void test_lowest_row()
{
xlnt::workbook wb;
auto ws = wb.active_sheet();
xlnt_assert_equals(ws.lowest_row(), 1);
}
- void test_max_row()
+ void test_lowest_row_or_props()
+ {
+ xlnt::workbook wb;
+ auto ws = wb.active_sheet();
+ ws.row_properties(11).height = 14.3;
+ xlnt_assert_equals(ws.lowest_row_or_props(), 11);
+ }
+
+ void test_highest_row()
{
xlnt::workbook wb;
auto ws = wb.active_sheet();
@@ -536,6 +564,14 @@ class worksheet_test_suite : public test_suite
xlnt_assert_equals(ws.highest_row(), 4);
}
+ void test_highest_row_or_props()
+ {
+ xlnt::workbook wb;
+ auto ws = wb.active_sheet();
+ ws.row_properties(11).height = 14.3;
+ xlnt_assert_equals(ws.highest_row_or_props(), 11);
+ }
+
void test_const_iterators()
{
xlnt::workbook wb;