fix empty row height/cell width, #235

This commit is contained in:
Thomas Fussell 2017-10-30 19:36:24 -04:00
parent dc4befd867
commit 470c655df6
6 changed files with 325 additions and 171 deletions

View File

@ -370,11 +370,21 @@ public:
/// </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>
@ -385,11 +395,21 @@ public:
/// </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 non-empty cells in the worksheet.
/// </summary>

View File

@ -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<xlnt::row_t>(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 (ws.has_row_properties(row.front().row()))
if (any_non_null)
{
const auto &props = ws.row_properties(row.front().row());
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))
{
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;
// record data about the cell needed later
if (cell.has_comment())
for (auto column = dimension.top_left().column(); column <= dimension.bottom_right().column(); ++column)
{
cells_with_comments.push_back(cell.reference());
}
if (!ws.has_cell(cell_reference(column, row))) continue;
if (cell.has_hyperlink())
{
hyperlink_references[cell.reference().to_string()] = reverse_hyperlink_references[cell.hyperlink()];
}
auto cell = ws.cell(cell_reference(column, row));
write_start_element(xmlns, "c");
if (cell.garbage_collectible()) continue;
// begin cell attributes
// record data about the cell needed later
write_attribute("r", cell.reference().to_string());
if (cell.has_format())
{
write_attribute("s", cell.format().d_->id);
}
switch (cell.data_type())
{
case cell::type::empty:
break;
case cell::type::boolean:
write_attribute("t", "b");
break;
case cell::type::date:
write_attribute("t", "d");
break;
case cell::type::error:
write_attribute("t", "e");
break;
case cell::type::inline_string:
write_attribute("t", "inlineStr");
break;
case cell::type::number:
write_attribute("t", "n");
break;
case cell::type::shared_string:
write_attribute("t", "s");
break;
case cell::type::formula_string:
write_attribute("t", "str");
break;
}
//write_attribute("cm", "");
//write_attribute("vm", "");
//write_attribute("ph", "");
// begin child elements
if (cell.has_formula())
{
write_element(xmlns, "f", cell.formula());
}
switch (cell.data_type())
{
case cell::type::empty:
break;
case cell::type::boolean:
write_element(xmlns, "v", write_bool(cell.value<bool>()));
break;
case cell::type::date:
write_element(xmlns, "v", cell.value<std::string>());
break;
case cell::type::error:
write_element(xmlns, "v", cell.value<std::string>());
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<std::string>());
write_end_element(xmlns, "is");
break;
case cell::type::number:
write_start_element(xmlns, "v");
if (is_integral(cell.value<double>()))
if (cell.has_comment())
{
write_characters(static_cast<std::int64_t>(cell.value<double>()));
}
else
{
std::stringstream ss;
ss.precision(20);
ss << cell.value<double>();
write_characters(ss.str());
cells_with_comments.push_back(cell.reference());
}
write_end_element(xmlns, "v");
break;
if (cell.has_hyperlink())
{
hyperlink_references[cell.reference().to_string()] = reverse_hyperlink_references[cell.hyperlink()];
}
case cell::type::shared_string:
write_element(xmlns, "v", static_cast<std::size_t>(cell.d_->value_numeric_));
break;
write_start_element(xmlns, "c");
case cell::type::formula_string:
write_element(xmlns, "v", cell.value<std::string>());
break;
// begin cell attributes
write_attribute("r", cell.reference().to_string());
if (cell.has_format())
{
write_attribute("s", cell.format().d_->id);
}
switch (cell.data_type())
{
case cell::type::empty:
break;
case cell::type::boolean:
write_attribute("t", "b");
break;
case cell::type::date:
write_attribute("t", "d");
break;
case cell::type::error:
write_attribute("t", "e");
break;
case cell::type::inline_string:
write_attribute("t", "inlineStr");
break;
case cell::type::number:
write_attribute("t", "n");
break;
case cell::type::shared_string:
write_attribute("t", "s");
break;
case cell::type::formula_string:
write_attribute("t", "str");
break;
}
//write_attribute("cm", "");
//write_attribute("vm", "");
//write_attribute("ph", "");
// begin child elements
if (cell.has_formula())
{
write_element(xmlns, "f", cell.formula());
}
switch (cell.data_type())
{
case cell::type::empty:
break;
case cell::type::boolean:
write_element(xmlns, "v", write_bool(cell.value<bool>()));
break;
case cell::type::date:
write_element(xmlns, "v", cell.value<std::string>());
break;
case cell::type::error:
write_element(xmlns, "v", cell.value<std::string>());
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<std::string>());
write_end_element(xmlns, "is");
break;
case cell::type::number:
write_start_element(xmlns, "v");
if (is_integral(cell.value<double>()))
{
write_characters(static_cast<std::int64_t>(cell.value<double>()));
}
else
{
std::stringstream ss;
ss.precision(20);
ss << cell.value<double>();
write_characters(ss.str());
}
write_end_element(xmlns, "v");
break;
case cell::type::shared_string:
write_element(xmlns, "v", static_cast<std::size_t>(cell.d_->value_numeric_));
break;
case cell::type::formula_string:
write_element(xmlns, "v", cell.value<std::string>());
break;
}
write_end_element(xmlns, "c");
}
write_end_element(xmlns, "c");
}
write_end_element(xmlns, "row");

View File

@ -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());

View File

@ -395,22 +395,27 @@ public:
wb.load(path_helper::test_file("13_custom_heights_widths.xlsx"));
auto ws = wb.active_sheet();
xlnt_assert_equals(ws.cell("A1").value<std::string>(), "170xd");
xlnt_assert_equals(ws.cell("B1").value<std::string>(), "40xd");
xlnt_assert_equals(ws.cell("C1").value<std::string>(), "dxd");
xlnt_assert_equals(ws.cell("A2").value<std::string>(), "170x30");
xlnt_assert_equals(ws.cell("B2").value<std::string>(), "40x30");
xlnt_assert_equals(ws.cell("C2").value<std::string>(), "dx30");
xlnt_assert_equals(ws.cell("A3").value<std::string>(), "170x10");
xlnt_assert_equals(ws.cell("B3").value<std::string>(), "40x10");
xlnt_assert_equals(ws.cell("C3").value<std::string>(), "dx10");
xlnt_assert_equals(ws.cell("A1").value<std::string>(), "A1");
xlnt_assert_equals(ws.cell("B1").value<std::string>(), "B1");
xlnt_assert_equals(ws.cell("D1").value<std::string>(), "D1");
xlnt_assert_equals(ws.cell("A2").value<std::string>(), "A2");
xlnt_assert_equals(ws.cell("B2").value<std::string>(), "B2");
xlnt_assert_equals(ws.cell("D2").value<std::string>(), "D2");
xlnt_assert_equals(ws.cell("A4").value<std::string>(), "A4");
xlnt_assert_equals(ws.cell("B4").value<std::string>(), "B4");
xlnt_assert_equals(ws.cell("D4").value<std::string>(), "D4");
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.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 @@ public:
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.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(2).height = 30;
ws.row_properties(3).height = 10;
ws.row_properties(1).height = 100;
ws.row_properties(1).custom_height = true;
ws.column_properties("A").width = 27.617745535714285;
ws.column_properties("B").width = 5.9497767857142856;
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")));

View File

@ -68,10 +68,14 @@ public:
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 @@ public:
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 @@ public:
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_min_row()
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_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 @@ public:
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;