start implementing complex number formats, test builtin formats

This commit is contained in:
Thomas Fussell 2016-07-05 21:27:35 -04:00
parent 627e6d438e
commit 9f1ac60d20
14 changed files with 512 additions and 505 deletions

View File

@ -36,28 +36,21 @@ namespace xlnt {
class XLNT_CLASS protection : public hashable class XLNT_CLASS protection : public hashable
{ {
public: public:
enum class type
{
inherit,
protected_,
unprotected
};
protection(); protection();
protection(type locked); protection(bool locked, bool hidden);
type get_locked() const; bool get_locked() const;
void set_locked(type locked_type); void set_locked(bool locked);
type get_hidden() const; bool get_hidden() const;
void set_hidden(type hidden_type); void set_hidden(bool hidden);
protected: protected:
std::string to_hash_string() const override; std::string to_hash_string() const override;
private: private:
type locked_; bool locked_;
type hidden_; bool hidden_;
}; };
} // namespace xlnt } // namespace xlnt

View File

@ -420,14 +420,11 @@ public:
TS_ASSERT(!cell.has_format()); TS_ASSERT(!cell.has_format());
xlnt::protection prot; cell.set_protection(xlnt::protection(false, true));
prot.set_locked(xlnt::protection::type::protected_);
cell.set_protection(prot);
TS_ASSERT(cell.has_format()); TS_ASSERT(cell.has_format());
TS_ASSERT(cell.get_format().protection_applied()); TS_ASSERT(cell.get_format().protection_applied());
TS_ASSERT_EQUALS(cell.get_protection(), prot); TS_ASSERT_EQUALS(cell.get_protection(), xlnt::protection(false, true));
TS_ASSERT(cell.has_format()); TS_ASSERT(cell.has_format());
cell.clear_format(); cell.clear_format();

View File

@ -465,10 +465,40 @@ void number_format_parser::finalize()
bool leading_zero = false; bool leading_zero = false;
std::size_t minutes_index = 0; std::size_t minutes_index = 0;
bool integer_part = false;
bool fractional_part = false;
std::size_t integer_part_index = 0;
bool percentage = false;
bool exponent = false;
std::size_t exponent_index = 0;
for (std::size_t i = 0; i < code.parts.size(); ++i) for (std::size_t i = 0; i < code.parts.size(); ++i)
{ {
const auto &part = code.parts[i]; const auto &part = code.parts[i];
if (part.placeholders.type == format_placeholders::placeholders_type::integer_part)
{
integer_part = true;
integer_part_index = i;
}
else if (part.placeholders.type == format_placeholders::placeholders_type::fractional_part)
{
fractional_part = true;
}
else if (part.placeholders.type == format_placeholders::placeholders_type::scientific_exponent_plus
|| part.placeholders.type == format_placeholders::placeholders_type::scientific_exponent_minus)
{
exponent = true;
exponent_index = i;
}
if (part.placeholders.percentage)
{
percentage = true;
}
if (part.type == template_part::template_type::month_number if (part.type == template_part::template_type::month_number
|| part.type == template_part::template_type::month_number_leading_zero) || part.type == template_part::template_type::month_number_leading_zero)
{ {
@ -514,6 +544,30 @@ void number_format_parser::finalize()
template_part::template_type::minute_leading_zero : template_part::template_type::minute_leading_zero :
template_part::template_type::minute; template_part::template_type::minute;
} }
if (integer_part && !fractional_part)
{
code.parts[integer_part_index].placeholders.type = format_placeholders::placeholders_type::integer_only;
}
if (integer_part && fractional_part && percentage)
{
code.parts[integer_part_index].placeholders.percentage = true;
}
if (exponent)
{
const auto &next = code.parts[exponent_index + 1];
auto temp = code.parts[exponent_index].placeholders.type;
code.parts[exponent_index].placeholders = next.placeholders;
code.parts[exponent_index].placeholders.type = temp;
code.parts.erase(code.parts.begin() + exponent_index + 1);
for (std::size_t i = 0; i < code.parts.size(); ++i)
{
code.parts[i].placeholders.scientific = true;
}
}
} }
validate(); validate();
@ -608,15 +662,18 @@ number_format_token number_format_parser::parse_next_token()
do do
{ {
token.string.push_back(current_char); token.string.push_back(current_char);
current_char = format_string_[position_]; current_char = format_string_[position_++];
if (current_char != '.')
{
++position_;
}
} }
while (current_char == '0' || current_char == '#' || current_char == '?' || current_char == ','); while (current_char == '0' || current_char == '#' || current_char == '?' || current_char == ',');
--position_;
if (current_char == '%')
{
token.string.push_back('%');
++position_;
}
break; break;
case 'y': case 'y':
@ -693,6 +750,17 @@ number_format_token number_format_parser::parse_next_token()
token.string.push_back(current_char); token.string.push_back(current_char);
break; break;
case 'E':
token.type = number_format_token::token_type::number;
token.string.push_back(current_char);
current_char = format_string_[position_++];
if (current_char == '+' || current_char == '-')
{
token.string.push_back(current_char);
break;
}
default: default:
throw std::runtime_error("unexpected character"); throw std::runtime_error("unexpected character");
} }
@ -736,6 +804,22 @@ format_placeholders number_format_parser::parse_placeholders(const std::string &
{ {
p.type = format_placeholders::placeholders_type::fractional_part; p.type = format_placeholders::placeholders_type::fractional_part;
} }
else if (placeholders_string.front() == 'E')
{
p.type = placeholders_string[1] == '+'
? format_placeholders::placeholders_type::scientific_exponent_plus
: format_placeholders::placeholders_type::scientific_exponent_minus;
return p;
}
else
{
p.type = format_placeholders::placeholders_type::integer_part;
}
if (placeholders_string.back() == '%')
{
p.percentage = true;
}
std::vector<std::size_t> comma_indices; std::vector<std::size_t> comma_indices;
@ -983,10 +1067,28 @@ std::string number_formatter::fill_placeholders(const format_placeholders &p, lo
return result; return result;
} }
if (p.percentage)
{
number *= 100;
}
auto integer_part = static_cast<int>(number); auto integer_part = static_cast<int>(number);
if (p.type != format_placeholders::placeholders_type::fractional_part) if (p.type == format_placeholders::placeholders_type::integer_only
|| p.type == format_placeholders::placeholders_type::integer_part)
{ {
if (p.scientific)
{
auto fractional_part = number;
while (fractional_part > 10)
{
fractional_part /= 10;
}
integer_part = static_cast<int>(fractional_part);
}
auto result = std::to_string(integer_part); auto result = std::to_string(integer_part);
while (result.size() < p.num_zeros) while (result.size() < p.num_zeros)
@ -1017,14 +1119,32 @@ std::string number_formatter::fill_placeholders(const format_placeholders &p, lo
result = std::string(temp.rbegin(), temp.rend()); result = std::string(temp.rbegin(), temp.rend());
} }
if (p.percentage && p.type == format_placeholders::placeholders_type::integer_only)
{
result.push_back('%');
}
return result; return result;
} }
else else if (p.type == format_placeholders::placeholders_type::fractional_part)
{ {
auto fractional_part = number - integer_part; auto fractional_part = number - integer_part;
auto result = std::to_string(fractional_part).substr(1);
while (result.back() == '0') if (p.scientific)
{
fractional_part = number;
while (fractional_part > 10)
{
fractional_part /= 10;
}
fractional_part -= static_cast<int>(fractional_part);
}
auto result = fractional_part == 0 ? std::string(".") : std::to_string(fractional_part).substr(1);
while (result.back() == '0' || result.size() > (p.num_zeros + p.num_optionals + 1))
{ {
result.pop_back(); result.pop_back();
} }
@ -1039,8 +1159,45 @@ std::string number_formatter::fill_placeholders(const format_placeholders &p, lo
result.push_back(' '); result.push_back(' ');
} }
if (p.percentage)
{
result.push_back('%');
}
return result; return result;
} }
else if (p.type == format_placeholders::placeholders_type::scientific_exponent_minus
|| p.type == format_placeholders::placeholders_type::scientific_exponent_plus)
{
auto exponent = number != 0 ? static_cast<int>(std::log10(integer_part)) : 0;
auto result = std::to_string(exponent);
while (result.back() == '0' || result.size() > (p.num_zeros + p.num_optionals))
{
result.pop_back();
}
while (result.size() < p.num_zeros)
{
result = "0" + result;
}
while (result.size() < p.num_zeros + p.num_spaces)
{
result = " " + result;
}
if (p.percentage)
{
result.push_back('%');
}
result = (p.type == format_placeholders::placeholders_type::scientific_exponent_plus ? "E+" : "E") + result;
return result;
}
return "";
} }
std::string number_formatter::format_number(const format_code &format, long double number) std::string number_formatter::format_number(const format_code &format, long double number)
@ -1188,6 +1345,7 @@ std::string number_formatter::format_number(const format_code &format, long doub
std::string number_formatter::format_text(const format_code &format, const std::string &text) std::string number_formatter::format_text(const format_code &format, const std::string &text)
{ {
std::string result; std::string result;
bool any_text_part = false;
for (const auto &part : format.parts) for (const auto &part : format.parts)
{ {
@ -1195,6 +1353,7 @@ std::string number_formatter::format_text(const format_code &format, const std::
{ {
case template_part::template_type::text: case template_part::template_type::text:
result.append(part.string); result.append(part.string);
any_text_part = true;
break; break;
case template_part::template_type::general: case template_part::template_type::general:
@ -1202,6 +1361,7 @@ std::string number_formatter::format_text(const format_code &format, const std::
|| part.placeholders.type == format_placeholders::placeholders_type::text) || part.placeholders.type == format_placeholders::placeholders_type::text)
{ {
result.append(text); result.append(text);
any_text_part = true;
} }
break; break;
@ -1211,6 +1371,11 @@ std::string number_formatter::format_text(const format_code &format, const std::
} }
} }
if (!format.parts.empty() && !any_text_part)
{
return text;
}
return result; return result;
} }

View File

@ -236,16 +236,20 @@ struct format_placeholders
bad, bad,
general, general,
text, text,
fractional_part, integer_only,
integer_part, integer_part,
fractional_part,
fraction_integer, fraction_integer,
fraction_numerator, fraction_numerator,
fraction_denominator, fraction_denominator,
scientific_significand, scientific_significand,
scientific_exponent scientific_exponent_plus,
scientific_exponent_minus
} type = placeholders_type::bad; } type = placeholders_type::bad;
bool use_comma_separator = false; bool use_comma_separator = false;
bool percentage = false;
bool scientific = false;
std::size_t num_zeros = 0; // 0 std::size_t num_zeros = 0; // 0
std::size_t num_optionals = 0; // # std::size_t num_optionals = 0; // #

View File

@ -93,36 +93,6 @@ std::size_t string_to_size_t(const std::string &s)
// enum serialization // enum serialization
// //
// protection::type serialization
xlnt::protection::type protection_type_from_string(const std::string &type_string)
{
auto lower = string_lower(type_string);
if (lower == "inherit") return xlnt::protection::type::inherit;
if (is_true(lower)) return xlnt::protection::type::protected_;
if (!is_false(lower))
{
throw std::runtime_error("bad enum " + type_string);
}
return xlnt::protection::type::unprotected;
};
std::string protection_type_to_string(xlnt::protection::type type)
{
switch (type)
{
case xlnt::protection::type::inherit: return "inherit";
case xlnt::protection::type::protected_: return "true";
case xlnt::protection::type::unprotected: return "false";
}
throw std::runtime_error("bad enum " + std::to_string(static_cast<std::size_t>(type)));
}
// font::underline_style serialization // font::underline_style serialization
const std::unordered_map<std::string, xlnt::font::underline_style> &get_string_underline_style_map() const std::unordered_map<std::string, xlnt::font::underline_style> &get_string_underline_style_map()
@ -398,10 +368,25 @@ xlnt::protection read_protection(const pugi::xml_node protection_node)
{ {
xlnt::protection prot; xlnt::protection prot;
prot.set_locked(protection_type_from_string(protection_node.attribute("locked").value())); if (is_true(protection_node.attribute("locked").value()))
prot.set_hidden(protection_type_from_string(protection_node.attribute("hidden").value())); {
prot.set_locked(true);
}
else if (!is_false(protection_node.attribute("locked").value()))
{
throw std::runtime_error("bad protection value");
}
return std::move(prot); if (is_true(protection_node.attribute("hidden").value()))
{
prot.set_hidden(true);
}
else if (!is_false(protection_node.attribute("hidden").value()))
{
throw std::runtime_error("bad protection value");
}
return prot;
} }
xlnt::alignment read_alignment(const pugi::xml_node alignment_node) xlnt::alignment read_alignment(const pugi::xml_node alignment_node)
@ -1030,12 +1015,14 @@ bool write_base_format(const xlnt::base_format &xf, const xlnt::detail::styleshe
if (xf.get_alignment().has_vertical()) if (xf.get_alignment().has_vertical())
{ {
alignment_node.append_attribute("vertical").set_value(vertical_alignment_to_string(xf.get_alignment().get_vertical()).c_str()); auto vertical = vertical_alignment_to_string(xf.get_alignment().get_vertical());
alignment_node.append_attribute("vertical").set_value(vertical.c_str());
} }
if (xf.get_alignment().has_horizontal()) if (xf.get_alignment().has_horizontal())
{ {
alignment_node.append_attribute("horizontal").set_value(horizontal_alignment_to_string(xf.get_alignment().get_horizontal()).c_str()); auto horizontal = horizontal_alignment_to_string(xf.get_alignment().get_horizontal());
alignment_node.append_attribute("horizontal").set_value(horizontal.c_str());
} }
if (xf.get_alignment().get_wrap_text()) if (xf.get_alignment().get_wrap_text())
@ -1052,9 +1039,8 @@ bool write_base_format(const xlnt::base_format &xf, const xlnt::detail::styleshe
if (xf.protection_applied()) if (xf.protection_applied())
{ {
auto protection_node = xf_node.append_child("protection"); auto protection_node = xf_node.append_child("protection");
protection_node.append_attribute("locked").set_value(xf.get_protection().get_locked() ? "1" : "0");
protection_node.append_attribute("locked").set_value(protection_type_to_string(xf.get_protection().get_locked()).c_str()); protection_node.append_attribute("hidden").set_value(xf.get_protection().get_hidden() ? "1" : "0");
protection_node.append_attribute("hidden").set_value(protection_type_to_string(xf.get_protection().get_hidden()).c_str());
} }
return true; return true;
@ -1139,17 +1125,6 @@ bool write_colors(const std::vector<xlnt::color> &colors, pugi::xml_node &colors
return true; return true;
} }
bool write_ext_list(pugi::xml_node &ext_list_node)
{
auto ext_node = ext_list_node.append_child("ext");
ext_node.append_attribute("uri").set_value("{EB79DEF2-80B8-43e5-95BD-54CBDDF9020C}");
ext_node.append_attribute("xmlns:x14").set_value("http://schemas.microsoft.com/office/spreadsheetml/2009/9/main");
ext_node.append_child("x14:slicerStyles").append_attribute("defaultSlicerStyle").set_value("SlicerStyleLight1");
return true;
}
bool write_number_formats(const std::vector<xlnt::number_format> &number_formats, pugi::xml_node &number_formats_node) bool write_number_formats(const std::vector<xlnt::number_format> &number_formats, pugi::xml_node &number_formats_node)
{ {
number_formats_node.append_attribute("count").set_value(std::to_string(number_formats.size()).c_str()); number_formats_node.append_attribute("count").set_value(std::to_string(number_formats.size()).c_str());
@ -1239,9 +1214,6 @@ bool style_serializer::write_stylesheet(pugi::xml_document &doc)
write_colors(stylesheet_.colors, colors_node); write_colors(stylesheet_.colors, colors_node);
} }
auto ext_list_node = root_node.append_child("extLst");
write_ext_list(ext_list_node);
return true; return true;
} }

View File

@ -95,7 +95,7 @@ struct stylesheet
} }
formats.push_back(f); formats.push_back(f);
format_styles.push_back(""); format_styles.push_back("Normal");
try try
{ {

View File

@ -162,8 +162,11 @@ const protection &base_format::get_protection() const
void base_format::set_protection(const xlnt::protection &new_protection) void base_format::set_protection(const xlnt::protection &new_protection)
{ {
if (!new_protection.get_locked() || new_protection.get_hidden())
{
protection_ = new_protection; protection_ = new_protection;
protection_applied(true); protection_applied(true);
}
} }
std::string base_format::to_hash_string() const std::string base_format::to_hash_string() const

View File

@ -44,10 +44,10 @@ const std::unordered_map<std::size_t, std::string> &builtin_formats()
{ 2, "0.00" }, { 2, "0.00" },
{ 3, "#,##0" }, { 3, "#,##0" },
{ 4, "#,##0.00" }, { 4, "#,##0.00" },
{ 5, "\"$\"#,##0_);(\"$\"#,##0)" }, { 5, "#,##0;-#,##0" },
{ 6, "\"$\"#,##0_);[Red](\"$\"#,##0)" }, { 6, "#,##0;[Red]-#,##0" },
{ 7, "\"$\"#,##0.00_);(\"$\"#,##0.00)" }, { 7, "#,##0.00;-#,##0.00" },
{ 8, "\"$\"#,##0.00_);[Red](\"$\"#,##0.00)" }, { 8, "#,##0.00;[Red]-#,##0.00" },
{ 9, "0%" }, { 9, "0%" },
{ 10, "0.00%" }, { 10, "0.00%" },
{ 11, "0.00E+00" }, { 11, "0.00E+00" },

View File

@ -26,41 +26,42 @@
namespace xlnt { namespace xlnt {
protection::protection() : protection(type::unprotected) protection::protection() : protection(true, false)
{ {
} }
protection::protection(type t) : locked_(t), hidden_(type::unprotected) protection::protection(bool locked, bool hidden)
: locked_(locked),
hidden_(hidden)
{ {
} }
protection::type protection::get_locked() const bool protection::get_locked() const
{ {
return locked_; return locked_;
} }
void protection::set_locked(type locked_type) void protection::set_locked(bool locked)
{ {
locked_ = locked_type; locked_ = locked;
} }
bool protection::get_hidden() const
protection::type protection::get_hidden() const
{ {
return hidden_; return hidden_;
} }
void protection::set_hidden(type hidden_type) void protection::set_hidden(bool hidden)
{ {
hidden_ = hidden_type; hidden_ = hidden;
} }
std::string protection::to_hash_string() const std::string protection::to_hash_string() const
{ {
std::string hash_string = "protection"; std::string hash_string = "protection";
hash_string.append(std::to_string(static_cast<std::size_t>(locked_))); hash_string.append(locked_ ? "1" : "0");
hash_string.append(std::to_string(static_cast<std::size_t>(hidden_))); hash_string.append(hidden_ ? "1" : "0");
return hash_string; return hash_string;
} }

View File

@ -364,45 +364,154 @@ public:
TS_ASSERT_THROWS(nf.format(1.2, xlnt::calendar::windows_1900), std::runtime_error); TS_ASSERT_THROWS(nf.format(1.2, xlnt::calendar::windows_1900), std::runtime_error);
} }
void test_builtin_formats() void format_and_test(const xlnt::number_format &nf, const std::array<std::string, 4> &expect)
{ {
TS_ASSERT_EQUALS(xlnt::number_format::text().format("a"), "a"); long double positive = 42503.1234;
TS_ASSERT_EQUALS(xlnt::number_format::number().format(1, xlnt::calendar::windows_1900), "1"); long double negative = -1 * positive;
TS_ASSERT_EQUALS(xlnt::number_format::number_comma_separated1().format(12345.67, xlnt::calendar::windows_1900), "12,345.67"); long double zero = 0;
const std::string text = "text";
xlnt::calendar calendar = xlnt::calendar::windows_1900;
/* TS_ASSERT_EQUALS(nf.format(positive, calendar), expect[0]);
auto datetime = xlnt::datetime(2016, 6, 24, 0, 45, 58); TS_ASSERT_EQUALS(nf.format(negative, calendar), expect[1]);
auto datetime_number = datetime.to_number(xlnt::calendar::windows_1900); TS_ASSERT_EQUALS(nf.format(zero, calendar), expect[2]);
TS_ASSERT_EQUALS(nf.format(text), expect[3]);
}
TS_ASSERT_EQUALS(xlnt::number_format::percentage().format(datetime_number, xlnt::calendar::windows_1900), "1"); // General
TS_ASSERT_EQUALS(xlnt::number_format::percentage_00().format(datetime_number, xlnt::calendar::windows_1900), "1"); void test_builtin_format_0()
TS_ASSERT_EQUALS(xlnt::number_format::date_yyyymmdd2().format(datetime_number, xlnt::calendar::windows_1900), "1"); {
TS_ASSERT_EQUALS(xlnt::number_format::date_yyyymmdd().format(datetime_number, xlnt::calendar::windows_1900), "1"); format_and_test(xlnt::number_format::general(), {{"42503.1234", "-42503.1234", "0", "text"}});
TS_ASSERT_EQUALS(xlnt::number_format::date_ddmmyyyy().format(datetime_number, xlnt::calendar::windows_1900), "1"); }
TS_ASSERT_EQUALS(xlnt::number_format::date_dmyslash().format(datetime_number, xlnt::calendar::windows_1900), "1");
TS_ASSERT_EQUALS(xlnt::number_format::date_dmyminus().format(datetime_number, xlnt::calendar::windows_1900), "1");
TS_ASSERT_EQUALS(xlnt::number_format::date_dmminus().format(datetime_number, xlnt::calendar::windows_1900), "1");
TS_ASSERT_EQUALS(xlnt::number_format::date_myminus().format(datetime_number, xlnt::calendar::windows_1900), "1");
TS_ASSERT_EQUALS(xlnt::number_format::date_xlsx14().format(datetime_number, xlnt::calendar::windows_1900), "1");
TS_ASSERT_EQUALS(xlnt::number_format::date_xlsx15().format(datetime_number, xlnt::calendar::windows_1900), "1");
TS_ASSERT_EQUALS(xlnt::number_format::date_xlsx16().format(datetime_number, xlnt::calendar::windows_1900), "1");
TS_ASSERT_EQUALS(xlnt::number_format::date_xlsx17().format(datetime_number, xlnt::calendar::windows_1900), "1");
TS_ASSERT_EQUALS(xlnt::number_format::date_xlsx22().format(datetime_number, xlnt::calendar::windows_1900), "1");
TS_ASSERT_EQUALS(xlnt::number_format::date_datetime().format(datetime_number, xlnt::calendar::windows_1900), "1");
TS_ASSERT_EQUALS(xlnt::number_format::date_time1().format(datetime_number, xlnt::calendar::windows_1900), "1");
TS_ASSERT_EQUALS(xlnt::number_format::date_time2().format(datetime_number, xlnt::calendar::windows_1900), "1");
TS_ASSERT_EQUALS(xlnt::number_format::date_time3().format(datetime_number, xlnt::calendar::windows_1900), "1");
TS_ASSERT_EQUALS(xlnt::number_format::date_time4().format(datetime_number, xlnt::calendar::windows_1900), "1");
TS_ASSERT_EQUALS(xlnt::number_format::date_time5().format(datetime_number, xlnt::calendar::windows_1900), "1");
TS_ASSERT_EQUALS(xlnt::number_format::date_time6().format(datetime_number, xlnt::calendar::windows_1900), "1");
TS_ASSERT_EQUALS(xlnt::number_format::date_time7().format(datetime_number, xlnt::calendar::windows_1900), "1");
TS_ASSERT_EQUALS(xlnt::number_format::date_time8().format(datetime_number, xlnt::calendar::windows_1900), "1");
TS_ASSERT_EQUALS(xlnt::number_format::date_timedelta().format(datetime_number, xlnt::calendar::windows_1900), "1");
TS_ASSERT_EQUALS(xlnt::number_format::date_yyyymmddslash().format(datetime_number, xlnt::calendar::windows_1900), "1");
TS_ASSERT_EQUALS(xlnt::number_format::currency_usd_simple().format(1.23, xlnt::calendar::windows_1900), "1"); // 0
TS_ASSERT_EQUALS(xlnt::number_format::currency_usd().format(1.23, xlnt::calendar::windows_1900), "1"); void test_builtin_format_1()
TS_ASSERT_EQUALS(xlnt::number_format::currency_eur_simple().format(1.23, xlnt::calendar::windows_1900), "1"); {
*/ format_and_test(xlnt::number_format::number(), {{"42503", "-42503", "0", "text"}});
}
// 0.00
void test_builtin_format_2()
{
format_and_test(xlnt::number_format::number_00(), {{"42503.12", "-42503.12", "0.00", "text"}});
}
// #,##0
void test_builtin_format_3()
{
format_and_test(xlnt::number_format::from_builtin_id(3), {{"42,503", "-42,503", "0", "text"}});
}
// #,##0.00
void test_builtin_format_4()
{
format_and_test(xlnt::number_format::from_builtin_id(4), {{"42,503.12", "-42,503.12", "0.00", "text"}});
}
// #,##0;-#,##0
void test_builtin_format_5()
{
format_and_test(xlnt::number_format::from_builtin_id(5), {{"42,503", "-42,503", "0", "text"}});
}
// #,##0;[Red]-#,##0
void test_builtin_format_6()
{
format_and_test(xlnt::number_format::from_builtin_id(6), {{"42,503", "-42,503", "0", "text"}});
}
// #,##0.00;-#,##0.00
void test_builtin_format_7()
{
format_and_test(xlnt::number_format::from_builtin_id(7), {{"42,503.12", "-42,503.12", "0.00", "text"}});
}
// #,##0.00;[Red]-#,##0.00
void test_builtin_format_8()
{
format_and_test(xlnt::number_format::from_builtin_id(8), {{"42,503.12", "-42,503.12", "0.00", "text"}});
}
// 0%
void test_builtin_format_9()
{
format_and_test(xlnt::number_format::percentage(), {{"4250312%", "-4250312%", "0%", "text"}});
}
// 0.00%
void test_builtin_format_10()
{
format_and_test(xlnt::number_format::percentage_00(), {{"4250312.34%", "-4250312.34%", "0.00%", "text"}});
}
// 0.00E+00
void test_builtin_format_11()
{
format_and_test(xlnt::number_format::from_builtin_id(11), {{"4.25E+04", "-4.25E+04", "0.00E+00", "text"}});
}
// # ?/?
void _test_builtin_format_12()
{
format_and_test(xlnt::number_format::from_builtin_id(12), {{"42503 1/8", "-42503 1/8", "0", "text"}});
}
// # ??/??
void _test_builtin_format_13()
{
format_and_test(xlnt::number_format::from_builtin_id(13), {{"42503 10/81", "-42503 10/81", "0", "text"}});
}
// mm-dd-yy
void _test_builtin_format_14()
{
format_and_test(xlnt::number_format::from_builtin_id(14), {{"05-13-16", "###########", "01-00-00", "text"}});
}
// d-mmm-yy
void _test_builtin_format_15()
{
format_and_test(xlnt::number_format::from_builtin_id(15), {{"13-May-16", "###########", "0-Jan-00", "text"}});
}
// d-mmm
void _test_builtin_format_16()
{
format_and_test(xlnt::number_format::from_builtin_id(16), {{"13-May", "###########", "0-Jan", "text"}});
}
// mmm-yy
void _test_builtin_format_17()
{
format_and_test(xlnt::number_format::from_builtin_id(17), {{"May-16", "###########", "Jan-00", "text"}});
}
// h:mm AM/PM
void _test_builtin_format_18()
{
format_and_test(xlnt::number_format::from_builtin_id(18), {{"2:57 AM", "###########", "12:00 AM", "text"}});
}
// h:mm:ss AM/PM
void _test_builtin_format_19()
{
format_and_test(xlnt::number_format::from_builtin_id(19), {{"2:57:42 AM", "###########", "12:00:00 AM", "text"}});
}
// h:mm
void _test_builtin_format_20()
{
format_and_test(xlnt::number_format::from_builtin_id(20), {{"2:57", "###########", "0:00", "text"}});
}
// h:mm:ss
void _test_builtin_format_21()
{
format_and_test(xlnt::number_format::from_builtin_id(21), {{"2:57:42", "###########", "0:00:00", "text"}});
}
// m/d/yy h:mm
void _test_builtin_format_22()
{
format_and_test(xlnt::number_format::from_builtin_id(22), {{"5/13/16 2:57", "###########", "1/0/00 0:00", "text"}});
} }
}; };

View File

@ -14,65 +14,6 @@
class test_stylesheet : public CxxTest::TestSuite class test_stylesheet : public CxxTest::TestSuite
{ {
public: public:
void test_ctor()
{
xlnt::workbook wb;
xlnt::excel_serializer excel_serializer(wb);
xlnt::style_serializer style_serializer(excel_serializer.get_stylesheet());
xlnt::zip_file archive;
pugi::xml_document stylesheet_doc;
style_serializer.write_stylesheet(stylesheet_doc);
std::ostringstream ss;
stylesheet_doc.save(ss);
auto stylesheet_xml = ss.str();
const std::string expected =
"<styleSheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" xmlns:x14ac=\"http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac\" mc:Ignorable=\"x14ac\">"
" <fonts count=\"1\">"
" <font>"
" <sz val=\"12\"/>"
" <color theme=\"1\"/>"
" <name val=\"Calibri\"/>"
" <family val=\"2\"/>"
" <scheme val=\"minor\"/>"
" </font>"
" </fonts>"
" <fills count=\"1\">"
" <fill>"
" <patternFill patternType=\"none\"/>"
" </fill>"
" </fills>"
" <borders count=\"1\">"
" <border>"
" <left/>"
" <right/>"
" <top/>"
" <bottom/>"
" <diagonal/>"
" </border>"
" </borders>"
" <cellStyleXfs count=\"1\">"
" <xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\"/>"
" </cellStyleXfs>"
" <cellXfs count=\"1\">"
" <xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\" xfId=\"0\"/>"
" </cellXfs>"
" <cellStyles count=\"1\">"
" <cellStyle name=\"Normal\" xfId=\"0\" builtinId=\"0\"/>"
" </cellStyles>"
" <dxfs count=\"0\"/>"
" <tableStyles count=\"0\" defaultTableStyle=\"TableStyleMedium9\" defaultPivotStyle=\"PivotStyleMedium7\"/>"
" <extLst>"
" <ext xmlns:x14=\"http://schemas.microsoft.com/office/spreadsheetml/2009/9/main\" uri=\"{EB79DEF2-80B8-43e5-95BD-54CBDDF9020C}\">"
" <x14:slicerStyles defaultSlicerStyle=\"SlicerStyleLight1\"/>"
" </ext>"
" </extLst>"
"</styleSheet>";
TS_ASSERT(Helper::compare_xml(expected, stylesheet_xml));
}
void test_from_simple() void test_from_simple()
{ {
pugi::xml_document doc; pugi::xml_document doc;

View File

@ -6,6 +6,7 @@
#include <detail/style_serializer.hpp> #include <detail/style_serializer.hpp>
#include <detail/stylesheet.hpp> #include <detail/stylesheet.hpp>
#include <detail/workbook_impl.hpp> #include <detail/workbook_impl.hpp>
#include <helpers/path_helper.hpp>
class test_style_writer : public CxxTest::TestSuite class test_style_writer : public CxxTest::TestSuite
{ {
@ -27,262 +28,80 @@ public:
auto diff = Helper::compare_xml(expected_doc.child("numFmts"), observed.child("styleSheet").child("numFmts")); auto diff = Helper::compare_xml(expected_doc.child("numFmts"), observed.child("styleSheet").child("numFmts"));
TS_ASSERT(diff); TS_ASSERT(diff);
} }
/*
class TestStyleWriter(object):
void setup(self): void test_simple_styles()
self.workbook = Workbook() {
self.worksheet = self.workbook.create_sheet() xlnt::workbook wb;
wb.set_guess_types(true);
auto ws = wb.get_active_sheet();
ws.get_cell("A1").set_value("12.34%");
auto now = xlnt::date::today();
ws.get_cell("A2").set_value(now);
ws.get_cell("A3").set_value("This is a test");
ws.get_cell("A4").set_value("31.31415");
ws.get_cell("A5");
void _test_no_style(self): ws.get_cell("D9").set_number_format(xlnt::number_format::number_00());
w = StyleWriter(self.workbook) xlnt::protection locked(true, false);
assert len(w.wb._cell_styles) == 1 # there is always the empty (defaul) style ws.get_cell("D9").set_protection(locked);
xlnt::protection hidden(true, true);
ws.get_cell("E1").set_protection(hidden);
void _test_nb_style(self): xlnt::excel_serializer e(wb);
for i in range(1, 6): xlnt::style_serializer serializer(e.get_stylesheet());
cell = self.worksheet.cell(row=1, column=i) pugi::xml_document xml;
cell.font = Font(size=i) serializer.write_stylesheet(xml);
_ = cell.style_id
w = StyleWriter(self.workbook)
assert len(w.wb._cell_styles) == 6 # 5 + the default
cell = self.worksheet.cell('A10') TS_ASSERT(Helper::compare_xml(PathHelper::GetDataDirectory("/writer/expected/simple-styles.xml"), xml));
cell.border=Border(top=Side(border_style=borders.BORDER_THIN)) }
_ = cell.style_id
w = StyleWriter(self.workbook)
assert len(w.wb._cell_styles) == 7
void test_empty_workbook()
void _test_default_xfs(self): {
w = StyleWriter(self.workbook) xlnt::workbook wb;
fonts = nft = borders = fills = DummyElement() xlnt::excel_serializer e(wb);
w._write_cell_styles() xlnt::style_serializer serializer(e.get_stylesheet());
xml = tostring(w._root) auto expected =
expected = """ "<styleSheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" xmlns:x14ac=\"http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac\" mc:Ignorable=\"x14ac\">"
<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"> " <fonts count=\"1\">"
<cellXfs count="1"> " <font>"
<xf borderId="0" fillId="0" fontId="0" numFmtId="0" xfId="0"/> " <sz val=\"12\"/>"
</cellXfs> " <color theme=\"1\"/>"
</styleSheet> " <name val=\"Calibri\"/>"
""" " <family val=\"2\"/>"
diff = compare_xml(xml, expected) " <scheme val=\"minor\"/>"
assert diff is None, diff " </font>"
" </fonts>"
" <fills count=\"2\">"
void _test_xfs_number_format(self): " <fill>"
for idx, nf in enumerate(["0.0%", "0.00%", "0.000%"], 1): " <patternFill patternType=\"none\"/>"
cell = self.worksheet.cell(row=idx, column=1) " </fill>"
cell.number_format = nf " <fill>"
_ = cell.style_id # add to workbook styles " <patternFill patternType=\"gray125\"/>"
w = StyleWriter(self.workbook) " </fill>"
w._write_cell_styles() " </fills>"
" <borders count=\"1\">"
expected = """ " <border>"
<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"> " <left/>"
<cellXfs count="4"> " <right/>"
<xf borderId="0" fillId="0" fontId="0" numFmtId="0" xfId="0"/> " <top/>"
<xf borderId="0" fillId="0" fontId="0" numFmtId="164" xfId="0"/> " <bottom/>"
<xf borderId="0" fillId="0" fontId="0" numFmtId="10" xfId="0"/> " <diagonal/>"
<xf borderId="0" fillId="0" fontId="0" numFmtId="165" xfId="0"/> " </border>"
</cellXfs> " </borders>"
</styleSheet> " <cellStyleXfs count=\"1\">"
""" " <xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\"/>"
" </cellStyleXfs>"
xml = tostring(w._root) " <cellXfs count=\"1\">"
diff = compare_xml(xml, expected) " <xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\" xfId=\"0\"/>"
assert diff is None, diff " </cellXfs>"
" <cellStyles count=\"1\">"
" <cellStyle name=\"Normal\" xfId=\"0\" builtinId=\"0\"/>"
void _test_xfs_fonts(self): " </cellStyles>"
cell = self.worksheet.cell('A1') " <dxfs count=\"0\"/>"
cell.font = Font(size=12, bold=True) " <tableStyles count=\"0\" defaultTableStyle=\"TableStyleMedium9\" defaultPivotStyle=\"PivotStyleMedium7\"/>"
_ = cell.style_id # update workbook styles "</styleSheet>";
w = StyleWriter(self.workbook) pugi::xml_document xml;
serializer.write_stylesheet(xml);
w._write_cell_styles() TS_ASSERT(Helper::compare_xml(expected, xml));
xml = tostring(w._root) }
expected = """
<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
<cellXfs count="2">
<xf borderId="0" fillId="0" fontId="0" numFmtId="0" xfId="0"/>
<xf borderId="0" fillId="0" fontId="1" numFmtId="0" xfId="0"/>
</cellXfs>
</styleSheet>
"""
diff = compare_xml(xml, expected)
assert diff is None, diff
void _test_xfs_fills(self):
cell = self.worksheet.cell('A1')
cell.fill = fill=PatternFill(fill_type='solid',
start_color=Color(colors.DARKYELLOW))
_ = cell.style_id # update workbook styles
w = StyleWriter(self.workbook)
w._write_cell_styles()
xml = tostring(w._root)
expected = """
<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
<cellXfs count="2">
<xf borderId="0" fillId="0" fontId="0" numFmtId="0" xfId="0"/>
<xf borderId="0" fillId="2" fontId="0" numFmtId="0" xfId="0"/>
</cellXfs>
</styleSheet>
"""
diff = compare_xml(xml, expected)
assert diff is None, diff
void _test_xfs_borders(self):
cell = self.worksheet.cell('A1')
cell.border=Border(top=Side(border_style=borders.BORDER_THIN,
color=Color(colors.DARKYELLOW)))
_ = cell.style_id # update workbook styles
w = StyleWriter(self.workbook)
w._write_cell_styles()
xml = tostring(w._root)
expected = """
<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
<cellXfs count="2">
<xf borderId="0" fillId="0" fontId="0" numFmtId="0" xfId="0"/>
<xf borderId="1" fillId="0" fontId="0" numFmtId="0" xfId="0"/>
</cellXfs>
</styleSheet>
"""
diff = compare_xml(xml, expected)
assert diff is None, diff
void _test_protection(self):
cell = self.worksheet.cell('A1')
cell.protection = Protection(locked=True, hidden=True)
_ = cell.style_id
w = StyleWriter(self.workbook)
w._write_cell_styles()
xml = tostring(w._root)
expected = """
<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
<cellXfs count="2">
<xf borderId="0" fillId="0" fontId="0" numFmtId="0" xfId="0"/>
<xf applyProtection="1" borderId="0" fillId="0" fontId="0" numFmtId="0" xfId="0">
<protection hidden="1" locked="1"/>
</xf>
</cellXfs>
</styleSheet>
"""
diff = compare_xml(xml, expected)
assert diff is None, diff
void _test_named_styles(self):
writer = StyleWriter(self.workbook)
writer._write_named_styles()
xml = tostring(writer._root)
expected = """
<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
<cellStyleXfs count="1">
<xf borderId="0" fillId="0" fontId="0" numFmtId="0"></xf>
</cellStyleXfs>
</styleSheet>
"""
diff = compare_xml(xml, expected)
assert diff is None, diff
void _test_style_names(self):
writer = StyleWriter(self.workbook)
writer._write_style_names()
xml = tostring(writer._root)
expected = """
<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
<cellStyles count="1">
<cellStyle name="Normal" xfId="0" builtinId="0" hidden="0"/>
</cellStyles>
</styleSheet>
"""
diff = compare_xml(xml, expected)
assert diff is None, diff
void _test_simple_styles(datadir):
wb = Workbook(guess_types=True)
ws = wb.active
now = datetime.datetime.now()
for idx, v in enumerate(['12.34%', now, 'This is a test', '31.31415', None], 1):
ws.append([v])
_ = ws.cell(column=1, row=idx).style_id
# set explicit formats
ws['D9'].number_format = numbers.FORMAT_NUMBER_00
ws['D9'].protection = Protection(locked=True)
ws['D9'].style_id
ws['E1'].protection = Protection(hidden=True)
ws['E1'].style_id
assert len(wb._cell_styles) == 5
writer = StyleWriter(wb)
datadir.chdir()
with open('simple-styles.xml') as reference_file:
expected = reference_file.read()
xml = writer.write_table()
diff = compare_xml(xml, expected)
assert diff is None, diff
void _test_empty_workbook():
wb = Workbook()
writer = StyleWriter(wb)
expected = """
<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
<numFmts count="0"/>
<fonts count="1">
<font>
<name val="Calibri"/>
<family val="2"/>
<color theme="1"/>
<sz val="11"/>
<scheme val="minor"/>
</font>
</fonts>
<fills count="2">
<fill>
<patternFill />
</fill>
<fill>
<patternFill patternType="gray125"/>
</fill>
</fills>
<borders count="1">
<border>
<left/>
<right/>
<top/>
<bottom/>
<diagonal/>
</border>
</borders>
<cellStyleXfs count="1">
<xf borderId="0" fillId="0" fontId="0" numFmtId="0"/>
</cellStyleXfs>
<cellXfs count="1">
<xf borderId="0" fillId="0" fontId="0" numFmtId="0" xfId="0"/>
</cellXfs>
<cellStyles count="1">
<cellStyle builtinId="0" name="Normal" xfId="0" hidden="0"/>
</cellStyles>
<dxfs count="0"/>
<tableStyles count="0" defaultPivotStyle="PivotStyleLight16" defaultTableStyle="TableStyleMedium9"/>
</styleSheet>
"""
xml = writer.write_table()
diff = compare_xml(xml, expected)
assert diff is None, diff
*/
}; };

View File

@ -91,6 +91,11 @@ workbook::workbook() : d_(new detail::workbook_impl())
add_format(format()); add_format(format());
create_style("Normal"); create_style("Normal");
d_->stylesheet_.format_styles.front() = "Normal"; d_->stylesheet_.format_styles.front() = "Normal";
xlnt::fill gray125;
gray125.set_type(xlnt::fill::type::pattern);
gray125.set_pattern_type(xlnt::fill::pattern_type::gray125);
d_->stylesheet_.fills.push_back(gray125);
} }
workbook::workbook(encoding e) : workbook() workbook::workbook(encoding e) : workbook()

View File

@ -1,15 +1,15 @@
<?xml version="1.0"?> <?xml version="1.0" ?>
<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"> <styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="x14ac" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac">
<numFmts count="1"> <numFmts count="1">
<numFmt formatCode="yyyy-mm-dd" numFmtId="165"/> <numFmt numFmtId="164" formatCode="yyyy-mm-dd"/>
</numFmts> </numFmts>
<fonts count="1"> <fonts count="1">
<font> <font>
<sz val="11"/> <sz val="12" />
<color theme="1"/> <color theme="1" />
<name val="Calibri"/> <name val="Calibri" />
<family val="2"/> <family val="2" />
<scheme val="minor"/> <scheme val="minor" />
</font> </font>
</fonts> </fonts>
<fills count="2"> <fills count="2">
@ -30,22 +30,20 @@
</border> </border>
</borders> </borders>
<cellStyleXfs count="1"> <cellStyleXfs count="1">
<xf borderId="0" fillId="0" fontId="0" numFmtId="0"/> <xf numFmtId="0" fontId="0" fillId="0" borderId="0"/>
</cellStyleXfs> </cellStyleXfs>
<cellXfs count="6"> <cellXfs count="5">
<xf borderId="0" fillId="0" fontId="0" numFmtId="0" xfId="0"/> <xf borderId="0" fillId="0" fontId="0" numFmtId="0" xfId="0"/>
<xf applyNumberFormat="1" borderId="0" fillId="0" fontId="0" numFmtId="9" xfId="0"/> <xf borderId="0" fillId="0" fontId="0" numFmtId="9" xfId="0"/>
<xf applyNumberFormat="1" borderId="0" fillId="0" fontId="0" numFmtId="165" xfId="0"/> <xf borderId="0" fillId="0" fontId="0" numFmtId="164" xfId="0"/>
<xf applyNumberFormat="1" applyProtection="1" borderId="0" fillId="0" fontId="0" numFmtId="2" xfId="0"> <xf borderId="0" fillId="0" fontId="0" numFmtId="2" xfId="0"/>
<protection locked="0"/>
</xf>
<xf applyProtection="1" borderId="0" fillId="0" fontId="0" numFmtId="0" xfId="0"> <xf applyProtection="1" borderId="0" fillId="0" fontId="0" numFmtId="0" xfId="0">
<protection hidden="0"/> <protection hidden="1" locked="1"/>
</xf> </xf>
</cellXfs> </cellXfs>
<cellStyles count="1"> <cellStyles count="1">
<cellStyle builtinId="0" name="Normal" xfId="0"/> <cellStyle name="Normal" xfId="0" builtinId="0"/>
</cellStyles> </cellStyles>
<dxfs count="0"/> <dxfs count="0" />
<tableStyles count="0" defaultPivotStyle="PivotStyleLight16" defaultTableStyle="TableStyleMedium9"/> <tableStyles count="0" defaultTableStyle="TableStyleMedium9" defaultPivotStyle="PivotStyleLight16"/>
</styleSheet> </styleSheet>