finish full header/footer implementation, add support for page breaks, clean up row/column properties interface

pull/101/head
Thomas Fussell 2016-12-22 18:57:22 -05:00
parent cd7c11a4ea
commit 008461bf60
14 changed files with 848 additions and 60 deletions

View File

@ -38,17 +38,22 @@ public:
/// <summary>
///
/// </summary>
long double width;
optional<double> width;
/// <summary>
///
/// </summary>
std::size_t style;
bool custom_width = false;
/// <summary>
///
/// </summary>
bool custom;
optional<std::size_t> style;
/// <summary>
///
/// </summary>
bool hidden = false;
};
} // namespace xlnt

View File

@ -37,27 +37,22 @@ public:
/// <summary>
///
/// </summary>
long double height;
optional<double> height;
/// <summary>
///
/// </summary>
bool visible;
bool custom_height = false;
/// <summary>
///
/// </summary>
int outline_level;
bool hidden = false;
/// <summary>
///
/// </summary>
bool collapsed;
/// <summary>
///
/// </summary>
int style_index;
optional<std::size_t> style;
};
} // namespace xlnt

View File

@ -31,6 +31,16 @@
namespace xlnt {
/// <summary>
///
/// </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.
@ -166,6 +176,16 @@ public:
return default_grid_color_;
}
void type(sheet_view_type new_type)
{
type_ = new_type;
}
sheet_view_type type() const
{
return type_;
}
bool operator==(const sheet_view &rhs) const
{
return id_ == rhs.id_ && show_grid_lines_ == rhs.show_grid_lines_
@ -189,6 +209,11 @@ private:
/// </summary>
bool default_grid_color_ = true;
/// <summary>
///
/// </summary>
sheet_view_type type_ = sheet_view_type::normal;
/// <summary>
///
/// </summary>

View File

@ -686,6 +686,11 @@ public:
/// </summary>
void add_view(const sheet_view &new_view);
std::vector<row_t> &row_breaks();
const std::vector<row_t> &row_breaks() const;
std::vector<column_t> &column_breaks();
const std::vector<column_t> &column_breaks() const;
private:
friend class cell;
friend class const_range_iterator;

View File

@ -84,6 +84,8 @@ struct worksheet_impl
print_title_rows_ = other.print_title_rows_;
print_area_ = other.print_area_;
views_ = other.views_;
column_breaks_ = other.column_breaks_;
row_breaks_ = other.row_breaks_;
}
workbook *parent_;
@ -110,6 +112,9 @@ struct worksheet_impl
optional<range_reference> print_area_;
std::vector<sheet_view> views_;
std::vector<column_t> column_breaks_;
std::vector<row_t> row_breaks_;
};
} // namespace detail

View File

@ -56,6 +56,398 @@ struct hash<xml::qname>
namespace {
std::array<xlnt::optional<xlnt::formatted_text>, 3> parse_header_footer(const std::string &hf_string)
{
std::array<xlnt::optional<xlnt::formatted_text>, 3> result;
if (hf_string.empty())
{
return result;
}
enum class hf_code
{
left_section, // &L
center_section, // &C
right_section, // &R
current_page_number, // &P
total_page_number, // &N
font_size, // &#
text_font_color, // &KRRGGBB or &KTTSNN
text_strikethrough, // &S
text_superscript, // &X
text_subscript, // &Y
date, // &D
time, // &T
picture_as_background, // &G
text_single_underline, // &U
text_double_underline, // &E
workbook_file_path, // &Z
workbook_file_name, // &F
sheet_tab_name, // &A
add_to_page_number, // &+
subtract_from_page_number, // &-
text_font_name, // &"font name,font type"
bold_font_style, // &B
italic_font_style, // &I
outline_style, // &O
shadow_style, // &H
text // everything else
};
struct hf_token
{
hf_code code = hf_code::text;
std::string value;
};
std::vector<hf_token> tokens;
std::size_t position = 0;
while (position < hf_string.size())
{
hf_token token;
auto next_ampersand = hf_string.find('&', position + 1);
token.value = hf_string.substr(position, next_ampersand - position);
auto next_position = next_ampersand;
if (hf_string[position] == '&')
{
token.value.clear();
next_position = position + 2;
auto first_code_char = hf_string[position + 1];
if (first_code_char == '"')
{
auto end_quote_index = hf_string.find('"', position + 2);
next_position = end_quote_index + 1;
token.value = hf_string.substr(position + 2, end_quote_index - position - 2); // remove quotes
token.code = hf_code::text_font_name;
}
else if (first_code_char == '&')
{
token.value = "&&"; // escaped ampersand
}
else if (first_code_char == 'L')
{
token.code = hf_code::left_section;
}
else if (first_code_char == 'C')
{
token.code = hf_code::center_section;
}
else if (first_code_char == 'R')
{
token.code = hf_code::right_section;
}
else if (first_code_char == 'P')
{
token.code = hf_code::current_page_number;
}
else if (first_code_char == 'N')
{
token.code = hf_code::total_page_number;
}
else if (std::string("0123456789").find(hf_string[position + 1]) != std::string::npos)
{
token.code = hf_code::font_size;
next_position = hf_string.find_first_not_of("0123456789", position + 1);
token.value = hf_string.substr(position + 1, next_position - position - 1);
}
else if (first_code_char == 'K')
{
if (hf_string[position + 4] == '+' || hf_string[position + 4] == '-')
{
token.value = hf_string.substr(position + 2, 5);
next_position = position + 7;
}
else
{
token.value = hf_string.substr(position + 2, 6);
next_position = position + 8;
}
token.code = hf_code::text_font_color;
}
else if (first_code_char == 'S')
{
token.code = hf_code::text_strikethrough;
}
else if (first_code_char == 'X')
{
token.code = hf_code::text_superscript;
}
else if (first_code_char == 'Y')
{
token.code = hf_code::text_subscript;
}
else if (first_code_char == 'D')
{
token.code = hf_code::date;
}
else if (first_code_char == 'T')
{
token.code = hf_code::time;
}
else if (first_code_char == 'G')
{
token.code = hf_code::picture_as_background;
}
else if (first_code_char == 'U')
{
token.code = hf_code::text_single_underline;
}
else if (first_code_char == 'E')
{
token.code = hf_code::text_double_underline;
}
else if (first_code_char == 'Z')
{
token.code = hf_code::workbook_file_path;
}
else if (first_code_char == 'F')
{
token.code = hf_code::workbook_file_name;
}
else if (first_code_char == 'A')
{
token.code = hf_code::sheet_tab_name;
}
else if (first_code_char == '+')
{
token.code = hf_code::add_to_page_number;
}
else if (first_code_char == '-')
{
token.code = hf_code::subtract_from_page_number;
}
else if (first_code_char == 'B')
{
token.code = hf_code::bold_font_style;
}
else if (first_code_char == 'I')
{
token.code = hf_code::italic_font_style;
}
else if (first_code_char == 'O')
{
token.code = hf_code::outline_style;
}
else if (first_code_char == 'H')
{
token.code = hf_code::shadow_style;
}
}
position = next_position;
tokens.push_back(token);
}
const auto parse_section = [&tokens,&result](hf_code code)
{
std::vector<hf_code> end_codes
{
hf_code::left_section,
hf_code::center_section,
hf_code::right_section
};
end_codes.erase(std::find(end_codes.begin(), end_codes.end(), code));
std::size_t start_index = 0;
while (start_index < tokens.size() && tokens[start_index].code != code)
{
++start_index;
}
if (start_index == tokens.size())
{
return;
}
++start_index; // skip the section code
std::size_t end_index = start_index;
while (end_index < tokens.size()
&& std::find(end_codes.begin(), end_codes.end(), tokens[end_index].code) == end_codes.end())
{
++end_index;
}
xlnt::formatted_text current_text;
xlnt::text_run current_run;
// todo: all this nice parsing and the codes are just being turned back into text representations
// It would be nice to create an interface for the library to read and write these codes
for (auto i = start_index; i < end_index; ++i)
{
const auto &current_token = tokens[i];
if (current_token.code == hf_code::text)
{
current_run.string(current_run.string() + current_token.value);
continue;
}
if (!current_run.string().empty())
{
current_text.add_run(current_run);
current_run = xlnt::text_run();
}
switch (current_token.code)
{
case hf_code::text:
break; // already handled above
case hf_code::left_section:
break; // used below
case hf_code::center_section:
break; // used below
case hf_code::right_section:
break; // used below
case hf_code::current_page_number:
current_run.string(current_run.string() + "&P");
break;
case hf_code::total_page_number:
current_run.string(current_run.string() + "&N");
break;
case hf_code::font_size:
current_run.size(static_cast<std::size_t>(std::stoi(current_token.value)));
break;
case hf_code::text_font_color:
if (current_token.value.size() == 6)
{
current_run.color(xlnt::rgb_color(current_token.value));
}
break;
case hf_code::text_strikethrough:
break;
case hf_code::text_superscript:
break;
case hf_code::text_subscript:
break;
case hf_code::date:
current_run.string(current_run.string() + "&D");
break;
case hf_code::time:
current_run.string(current_run.string() + "&T");
break;
case hf_code::picture_as_background:
current_run.string(current_run.string() + "&G");
break;
case hf_code::text_single_underline:
break;
case hf_code::text_double_underline:
break;
case hf_code::workbook_file_path:
current_run.string(current_run.string() + "&Z");
break;
case hf_code::workbook_file_name:
current_run.string(current_run.string() + "&F");
break;
case hf_code::sheet_tab_name:
current_run.string(current_run.string() + "&A");
break;
case hf_code::add_to_page_number:
break;
case hf_code::subtract_from_page_number:
break;
case hf_code::text_font_name:
{
auto comma_index = current_token.value.find(',');
auto font_name = current_token.value.substr(0, comma_index);
if (font_name != "-")
{
current_run.font(font_name);
}
if (comma_index != std::string::npos)
{
auto font_type = current_token.value.substr(comma_index + 1);
if (font_type == "Bold")
{
current_run.bold(true);
}
else if (font_type == "Italic")
{
}
else if (font_type == "BoldItalic")
{
current_run.bold(true);
}
}
}
break;
case hf_code::bold_font_style:
current_run.bold(true);
break;
case hf_code::italic_font_style:
break;
case hf_code::outline_style:
break;
case hf_code::shadow_style:
break;
}
}
if (!current_run.string().empty())
{
current_text.add_run(current_run);
}
auto location_index = static_cast<std::size_t>(code == hf_code::left_section ? 0
: code == hf_code::center_section ? 1 : 2);
if (!current_text.plain_text().empty())
{
result[location_index] = current_text;
}
};
parse_section(hf_code::left_section);
parse_section(hf_code::center_section);
parse_section(hf_code::right_section);
return result;
}
} // namespace
namespace {
#ifndef NDEBUG
#define THROW_ON_INVALID_XML
#endif
@ -1504,6 +1896,11 @@ void xlsx_consumer::read_worksheet(const std::string &rel_id)
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",
@ -1579,16 +1976,29 @@ void xlsx_consumer::read_worksheet(const std::string &rel_id)
{
expect_start_element(xml::qname(xmlns, "col"), xml::content::simple);
skip_attributes({"bestFit", "collapsed", "hidden", "outlineLevel"});
skip_attributes({"bestFit", "collapsed", "outlineLevel"});
auto min = static_cast<column_t::index_t>(std::stoull(parser().attribute("min")));
auto max = static_cast<column_t::index_t>(std::stoull(parser().attribute("max")));
auto width = std::stold(parser().attribute("width"));
auto column_style = parser().attribute_present("style")
? parser().attribute<std::size_t>("style") : static_cast<std::size_t>(0);
optional<double> width;
if (parser().attribute_present("width"))
{
width = parser().attribute<double>("width");
}
optional<std::size_t> column_style;
if (parser().attribute_present("style"))
{
column_style = parser().attribute<std::size_t>("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(xml::qname(xmlns, "col"));
@ -1596,10 +2006,18 @@ void xlsx_consumer::read_worksheet(const std::string &rel_id)
{
column_properties props;
props.width = width;
props.style = column_style;
props.custom = custom;
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);
}
}
@ -1613,13 +2031,22 @@ void xlsx_consumer::read_worksheet(const std::string &rel_id)
if (parser().attribute_present("ht"))
{
ws.row_properties(row_index).height = parser().attribute<long double>("ht");
ws.row_properties(row_index).height = parser().attribute<double>("ht");
}
if (parser().attribute_present("customHeight"))
{
ws.row_properties(row_index).custom_height = is_true(parser().attribute("customHeight"));
}
if (parser().attribute_present("hidden") && is_true(parser().attribute("hidden")))
{
ws.row_properties(row_index).hidden = true;
}
skip_attributes({xml::qname(xmlns_x14ac, "dyDescent")});
skip_attributes({"customFormat", "s", "customFont", "hidden",
"outlineLevel", "collapsed", "thickTop", "thickBot", "ph",
"spans", "customHeight"});
skip_attributes({"customFormat", "s", "customFont", "outlineLevel",
"collapsed", "thickTop", "thickBot", "ph", "spans"});
while (in_element(xml::qname(xmlns, "row")))
{
@ -1824,15 +2251,148 @@ void xlsx_consumer::read_worksheet(const std::string &rel_id)
}
else if (current_worksheet_element == xml::qname(xmlns, "headerFooter")) // CT_HeaderFooter 0-1
{
skip_remaining_content(current_worksheet_element);
header_footer hf;
hf.align_with_margins(!parser().attribute_present("alignWithMargins")
|| is_true(parser().attribute("alignWithMargins")));
hf.scale_with_doc(!parser().attribute_present("alignWithMargins")
|| is_true(parser().attribute("alignWithMargins")));
auto different_odd_even = parser().attribute_present("differentOddEven")
&& is_true(parser().attribute("differentOddEven"));
auto different_first = parser().attribute_present("differentFirst")
&& is_true(parser().attribute("differentFirst"));
optional<std::array<optional<formatted_text>, 3>> odd_header;
optional<std::array<optional<formatted_text>, 3>> odd_footer;
optional<std::array<optional<formatted_text>, 3>> even_header;
optional<std::array<optional<formatted_text>, 3>> even_footer;
optional<std::array<optional<formatted_text>, 3>> first_header;
optional<std::array<optional<formatted_text>, 3>> first_footer;
while (in_element(current_worksheet_element))
{
auto current_hf_element = expect_start_element(xml::content::simple);
if (current_hf_element == xml::qname(xmlns, "oddHeader"))
{
odd_header = parse_header_footer(read_text());
}
else if (current_hf_element == xml::qname(xmlns, "oddFooter"))
{
odd_footer = parse_header_footer(read_text());
}
else if (current_hf_element == xml::qname(xmlns, "evenHeader"))
{
even_header = parse_header_footer(read_text());
}
else if (current_hf_element == xml::qname(xmlns, "evenFooter"))
{
even_footer = parse_header_footer(read_text());
}
else if (current_hf_element == xml::qname(xmlns, "firstHeader"))
{
first_header = parse_header_footer(read_text());
}
else if (current_hf_element == xml::qname(xmlns, "firstFooter"))
{
first_footer = parse_header_footer(read_text());
}
else
{
unexpected_element(current_hf_element);
}
expect_end_element(current_hf_element);
}
for (std::size_t i = 0; i < 3; ++i)
{
auto loc = i == 0 ? header_footer::location::left
: i == 1 ? header_footer::location::center
: header_footer::location::right;
if (different_odd_even)
{
if (odd_header.is_set() && odd_header.get().at(i).is_set()
&& even_header.is_set() && even_header.get().at(i).is_set())
{
hf.odd_even_header(loc, odd_header.get().at(i).get(), even_header.get().at(i).get());
}
if (odd_footer.is_set() && odd_footer.get().at(i).is_set()
&& even_footer.is_set() && even_footer.get().at(i).is_set())
{
hf.odd_even_footer(loc, odd_footer.get().at(i).get(), even_footer.get().at(i).get());
}
}
else
{
if (odd_header.is_set() && odd_header.get().at(i).is_set())
{
hf.header(loc, odd_header.get().at(i).get());
}
if (odd_footer.is_set() && odd_footer.get().at(i).is_set())
{
hf.footer(loc, odd_footer.get().at(i).get());
}
}
if (different_first)
{
}
}
ws.header_footer(hf);
}
else if (current_worksheet_element == xml::qname(xmlns, "rowBreaks")) // CT_PageBreak 0-1
{
skip_remaining_content(current_worksheet_element);
auto count = parser().attribute_present("count") ? parser().attribute<std::size_t>("count") : 0;
auto manual_break_count = parser().attribute_present("manualBreakCount") ? parser().attribute<std::size_t>("manualBreakCount") : 0;
while (in_element(xml::qname(xmlns, "rowBreaks")))
{
expect_start_element(xml::qname(xmlns, "brk"), xml::content::simple);
if (parser().attribute_present("id"))
{
ws.row_breaks().push_back(parser().attribute<row_t>("id"));
--count;
}
if (parser().attribute_present("man") && is_true(parser().attribute("man")))
{
--manual_break_count;
}
skip_attributes({"min", "max", "pt"});
expect_end_element(xml::qname(xmlns, "brk"));
}
}
else if (current_worksheet_element == xml::qname(xmlns, "colBreaks")) // CT_PageBreak 0-1
{
skip_remaining_content(current_worksheet_element);
auto count = parser().attribute_present("count") ? parser().attribute<std::size_t>("count") : 0;
auto manual_break_count = parser().attribute_present("manualBreakCount") ? parser().attribute<std::size_t>("manualBreakCount") : 0;
while (in_element(xml::qname(xmlns, "colBreaks")))
{
expect_start_element(xml::qname(xmlns, "brk"), xml::content::simple);
if (parser().attribute_present("id"))
{
ws.column_breaks().push_back(parser().attribute<column_t::index_t>("id"));
--count;
}
if (parser().attribute_present("man") && is_true(parser().attribute("man")))
{
--manual_break_count;
}
skip_attributes({"min", "max", "pt"});
expect_end_element(xml::qname(xmlns, "brk"));
}
}
else if (current_worksheet_element == xml::qname(xmlns, "customProperties")) // CT_CustomProperties 0-1
{
@ -1858,6 +2418,10 @@ void xlsx_consumer::read_worksheet(const std::string &rel_id)
{
skip_remaining_content(current_worksheet_element);
}
else if (current_worksheet_element == xml::qname(xmlns, "extLst"))
{
skip_remaining_content(current_worksheet_element);
}
else
{
unexpected_element(current_worksheet_element);

View File

@ -1837,6 +1837,11 @@ void xlsx_producer::write_worksheet(const relationship &rel)
serializer().attribute("tabSelected", write_bool(view.id() == 0));
serializer().attribute("workbookViewId", view.id());
if (view.type() != sheet_view_type::normal)
{
serializer().attribute("view", view.type() == sheet_view_type::page_break_preview ? "pageBreakPreview" : "pageLayout");
}
if (view.has_pane())
{
const auto &current_pane = view.pane();
@ -1919,9 +1924,27 @@ void xlsx_producer::write_worksheet(const relationship &rel)
serializer().start_element(xmlns, "col");
serializer().attribute("min", column.index);
serializer().attribute("max", column.index);
serializer().attribute("width", props.width);
serializer().attribute("style", props.style);
serializer().attribute("customWidth", write_bool(props.custom));
if (props.width.is_set())
{
serializer().attribute("width", props.width.get());
}
if (props.custom_width)
{
serializer().attribute("customWidth", write_bool(true));
}
if (props.style.is_set())
{
serializer().attribute("style", props.style.get());
}
if (props.hidden)
{
serializer().attribute("hidden", write_bool(true));
}
serializer().end_element(xmlns, "col");
}
@ -1971,17 +1994,31 @@ void xlsx_producer::write_worksheet(const relationship &rel)
if (ws.has_row_properties(row.front().row()))
{
serializer().attribute("customHeight", "1");
auto height = ws.row_properties(row.front().row()).height;
const auto &props = ws.row_properties(row.front().row());
if (props.custom_height)
{
serializer().attribute("customHeight", write_bool(true));
}
if (std::fabs(height - std::floor(height)) == 0.L)
{
serializer().attribute("ht", std::to_string(static_cast<long long int>(height)) + ".0");
}
else
{
serializer().attribute("ht", height);
}
if (props.height.is_set())
{
auto height = props.height.get();
if (std::fabs(height - std::floor(height)) == 0.0)
{
serializer().attribute("ht", std::to_string(static_cast<int>(height)) + ".0");
}
else
{
serializer().attribute("ht", height);
}
}
if (props.hidden)
{
serializer().attribute("hidden", write_bool(true));
}
}
for (auto cell : row)
@ -2222,7 +2259,56 @@ void xlsx_producer::write_worksheet(const relationship &rel)
{ header_footer::location::right, "&R" },
};
return location_code_map.at(where) + t.plain_text();
auto encoded = location_code_map.at(where);
for (const auto &run : t.runs())
{
if (run.string().empty()) continue;
if (run.has_formatting())
{
if (run.has_font())
{
encoded.push_back('&');
encoded.push_back('"');
encoded.append(run.font());
encoded.push_back(',');
if (run.bold())
{
encoded.append("Bold");
}
else
{
encoded.append("Regular");
}
//todo: BoldItalic?
encoded.push_back('"');
}
else if (run.bold())
{
encoded.append("&B");
}
if (run.has_size())
{
encoded.push_back('&');
encoded.append(std::to_string(run.size()));
}
if (run.has_color())
{
encoded.push_back('&');
encoded.push_back('K');
encoded.append(run.color().rgb().hex_string().substr(2));
}
}
encoded.append(run.string());
}
return encoded;
};
const auto locations =
@ -2308,6 +2394,44 @@ void xlsx_producer::write_worksheet(const relationship &rel)
serializer().end_element(xmlns, "headerFooter");
}
if (!ws.row_breaks().empty())
{
serializer().start_element(xmlns, "rowBreaks");
serializer().attribute("count", ws.row_breaks().size());
serializer().attribute("manualBreakCount", ws.row_breaks().size());
for (auto break_id : ws.row_breaks())
{
serializer().start_element(xmlns, "brk");
serializer().attribute("id", break_id);
serializer().attribute("max", 16383);
serializer().attribute("man", 1);
serializer().end_element(xmlns, "brk");
}
serializer().end_element(xmlns, "rowBreaks");
}
if (!ws.column_breaks().empty())
{
serializer().start_element(xmlns, "colBreaks");
serializer().attribute("count", ws.column_breaks().size());
serializer().attribute("manualBreakCount", ws.column_breaks().size());
for (auto break_id : ws.column_breaks())
{
serializer().start_element(xmlns, "brk");
serializer().attribute("id", break_id.index);
serializer().attribute("max", 1048575);
serializer().attribute("man", 1);
serializer().end_element(xmlns, "brk");
}
serializer().end_element(xmlns, "colBreaks");
}
if (!worksheet_rels.empty())
{
for (const auto &child_rel : worksheet_rels)

View File

@ -88,4 +88,36 @@ public:
TS_ASSERT_EQUALS(wb.active_sheet().cell("A1").hyperlink(),
"https://fr.wikipedia.org/wiki/Ille-et-Vilaine");
}
void test_read_headers_and_footers()
{
xlnt::workbook wb;
wb.load("data/21_headers_and_footers.xlsx");
auto ws = wb.active_sheet();
TS_ASSERT_EQUALS(ws.cell("A1").value<std::string>(), "header");
TS_ASSERT_EQUALS(ws.cell("A2").value<std::string>(), "and");
TS_ASSERT_EQUALS(ws.cell("A3").value<std::string>(), "footer");
TS_ASSERT_EQUALS(ws.cell("A4").value<std::string>(), "page1");
TS_ASSERT_EQUALS(ws.cell("A43").value<std::string>(), "page2");
TS_ASSERT(ws.has_header_footer());
TS_ASSERT(ws.header_footer().align_with_margins());
TS_ASSERT(ws.header_footer().scale_with_doc());
TS_ASSERT(!ws.header_footer().different_first());
TS_ASSERT(!ws.header_footer().different_odd_even());
TS_ASSERT(ws.header_footer().has_header(xlnt::header_footer::location::left));
TS_ASSERT_EQUALS(ws.header_footer().header(xlnt::header_footer::location::left).plain_text(), "left header");
TS_ASSERT(ws.header_footer().has_header(xlnt::header_footer::location::center));
TS_ASSERT_EQUALS(ws.header_footer().header(xlnt::header_footer::location::center).plain_text(), "center header");
TS_ASSERT(ws.header_footer().has_header(xlnt::header_footer::location::right));
TS_ASSERT_EQUALS(ws.header_footer().header(xlnt::header_footer::location::right).plain_text(), "right header");
TS_ASSERT(ws.header_footer().has_footer(xlnt::header_footer::location::left));
TS_ASSERT_EQUALS(ws.header_footer().footer(xlnt::header_footer::location::left).plain_text(), "left && footer");
TS_ASSERT(ws.header_footer().has_footer(xlnt::header_footer::location::center));
TS_ASSERT_EQUALS(ws.header_footer().footer(xlnt::header_footer::location::center).plain_text(), "center footer");
TS_ASSERT(ws.header_footer().has_footer(xlnt::header_footer::location::right));
TS_ASSERT_EQUALS(ws.header_footer().footer(xlnt::header_footer::location::right).plain_text(), "right footer");
}
};

View File

@ -20,14 +20,14 @@ public:
{
std::vector<std::uint8_t> original_buffer;
original.save(original_buffer);
original.save("b.xlsx");
original.save("round_trip_in.xlsx");
xlnt::workbook resulting_workbook;
resulting_workbook.load(original_buffer);
std::vector<std::uint8_t> resulting_buffer;
resulting_workbook.save(resulting_buffer);
resulting_workbook.save("a.xlsx");
resulting_workbook.save("round_trip_out.xlsx");
return xml_helper::xlsx_archives_match(original_buffer, resulting_buffer);
}
@ -52,7 +52,7 @@ public:
std::vector<std::uint8_t> buffer;
original_workbook.save(buffer);
original_workbook.save("a.xlsx");
original_workbook.save("round_trip_out.xlsx");
return xml_helper::xlsx_archives_match(original_data, buffer);
}
@ -108,11 +108,24 @@ public:
void test_round_trip_all_styles_rw()
{
auto path = path_helper::get_data_directory("13_all_styles.xlsx");
xlnt::workbook original_workbook;
original_workbook.load(path);
std::vector<std::uint8_t> buffer;
original_workbook.save(buffer);
TS_ASSERT(round_trip_matches_rw(path));
}
void test_round_trip_headers_footers()
{
auto path = path_helper::get_data_directory("21_headers_and_footers.xlsx");
TS_ASSERT(round_trip_matches_rw(path));
}
void test_round_trip_row_and_col_props()
{
auto path = path_helper::get_data_directory("22_row_and_col_properties.xlsx");
TS_ASSERT(round_trip_matches_rw(path));
}
void test_round_trip_page_breaks()
{
auto path = path_helper::get_data_directory("23_page_breaks.xlsx");
TS_ASSERT(round_trip_matches_rw(path));
}
};

View File

@ -54,7 +54,7 @@ bool header_footer::different_odd_even() const
bool header_footer::different_first() const
{
return first_headers_.empty() || !first_footers_.empty();
return !first_headers_.empty() || !first_footers_.empty();
}
bool header_footer::scale_with_doc() const

View File

@ -815,16 +815,16 @@ std::vector<std::string> worksheet::formula_attributes() const
cell_reference worksheet::point_pos(int left, int top) const
{
static const auto DefaultColumnWidth = 51.85L;
static const auto DefaultRowHeight = 15.0L;
static const auto DefaultColumnWidth = 51.85;
static const auto DefaultRowHeight = 15.0;
auto points_to_pixels = [](long double value, long double dpi)
auto points_to_pixels = [](double value, double dpi)
{
return static_cast<int>(std::ceil(value * dpi / 72));
};
auto default_height = points_to_pixels(DefaultRowHeight, 96.0L);
auto default_width = points_to_pixels(DefaultColumnWidth, 96.0L);
auto default_height = points_to_pixels(DefaultRowHeight, 96.0);
auto default_width = points_to_pixels(DefaultColumnWidth, 96.0);
column_t current_column = 1;
row_t current_row = 1;
@ -836,13 +836,13 @@ cell_reference worksheet::point_pos(int left, int top) const
{
current_column++;
if (has_column_properties(current_column))
if (has_column_properties(current_column) && column_properties(current_column).width.is_set())
{
auto cdw = column_properties(current_column).width;
auto cdw = column_properties(current_column).width.get();
if (cdw >= 0)
{
left_pos += points_to_pixels(cdw, 96.0L);
left_pos += points_to_pixels(cdw, 96.0);
continue;
}
}
@ -854,13 +854,13 @@ cell_reference worksheet::point_pos(int left, int top) const
{
current_row++;
if (has_row_properties(current_row))
if (has_row_properties(current_row) && row_properties(current_row).height.is_set())
{
auto cdh = row_properties(current_row).height;
auto cdh = row_properties(current_row).height.get();
if (cdh >= 0)
{
top_pos += points_to_pixels(cdh, 96.0L);
top_pos += points_to_pixels(cdh, 96.0);
continue;
}
}
@ -1045,4 +1045,24 @@ void worksheet::header_footer(const class header_footer &hf)
d_->header_footer_ = hf;
}
std::vector<row_t> &worksheet::row_breaks()
{
return d_->row_breaks_;
}
const std::vector<row_t> &worksheet::row_breaks() const
{
return d_->row_breaks_;
}
std::vector<column_t> &worksheet::column_breaks()
{
return d_->column_breaks_;
}
const std::vector<column_t> &worksheet::column_breaks() const
{
return d_->column_breaks_;
}
} // namespace xlnt

Binary file not shown.

Binary file not shown.

Binary file not shown.