diff --git a/include/orcus/orcus_import_ods.hpp b/include/orcus/orcus_import_ods.hpp index c5625471393d8f1eb0f1c55c7a1063b8e32de6cc..fa18e3f5b29c986d7ff6680c0b5a06664bb65095 100644 --- a/include/orcus/orcus_import_ods.hpp +++ b/include/orcus/orcus_import_ods.hpp @@ -14,6 +14,7 @@ namespace orcus { namespace spreadsheet { namespace iface { class import_styles; + class import_conditional_format; }} class ORCUS_DLLPUBLIC import_ods @@ -24,7 +25,8 @@ private: import_ods& operator=(const import_ods&); // deleted public: - static void read_styles(const char* p, size_t n, spreadsheet::iface::import_styles* data); + static void read_styles(const char* p, size_t n, spreadsheet::iface::import_styles* data, + spreadsheet::iface::import_conditional_format* conditional_format = nullptr); }; } diff --git a/include/orcus/spreadsheet/Makefile.am b/include/orcus/spreadsheet/Makefile.am index 6838cc1decbca9256494a61b552f2799fb24fcff..6af9e40318868e0db7ab155a4cace9bc14ab93bd 100644 --- a/include/orcus/spreadsheet/Makefile.am +++ b/include/orcus/spreadsheet/Makefile.am @@ -9,6 +9,7 @@ if BUILD_SPREADSHEET_MODEL liborcus_HEADERS += \ auto_filter.hpp \ + conditional_format.hpp document.hpp \ factory.hpp \ global_settings.hpp \ diff --git a/include/orcus/spreadsheet/conditional_format.hpp b/include/orcus/spreadsheet/conditional_format.hpp new file mode 100644 index 0000000000000000000000000000000000000000..e486e6586ac09f04d14ad875b6e3359ca02e1ed9 --- /dev/null +++ b/include/orcus/spreadsheet/conditional_format.hpp @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef __ORCUS_SPREADSHEET_CONDITIONAL_FORMAT_HPP__ +#define __ORCUS_SPREADSHEET_CONDITIONAL_FORMAT_HPP__ + +#include "orcus/spreadsheet/import_interface.hpp" +#include "orcus/spreadsheet/styles.hpp" +#include "orcus/pstring.hpp" +#include "orcus/env.hpp" +#include +#include + +namespace orcus { + +class string_pool; + +namespace spreadsheet { + +struct ORCUS_SPM_DLLPUBLIC condition_t +{ + pstring mapped_name; + condition_operator_t condition_operator; + conditional_format_t condition_type; + color_t color; + pstring formula; + std::vector values; + + condition_t(); + void reset(); +}; + +class ORCUS_SPM_DLLPUBLIC import_conditional_format : public iface::import_conditional_format +{ +public: + ORCUS_DLLPUBLIC virtual ~import_conditional_format(); + import_conditional_format(string_pool& sp); + + virtual void set_color(color_elem_t alpha, color_elem_t red, + color_elem_t green, color_elem_t blue); + virtual void set_style_name(const char* p, size_t n); + virtual void set_values(std::vector v); + virtual void set_formula(const char* p, size_t n); + virtual void set_condition_type(orcus::spreadsheet::condition_type_t type); + virtual void set_date(orcus::spreadsheet::condition_date_t date); + virtual size_t commit_condition(); + virtual void set_icon_name(const char* p, size_t n); + virtual void set_databar_gradient(bool gradient); + virtual void set_databar_axis(orcus::spreadsheet::databar_axis_t axis); + virtual void set_databar_color_positive(color_elem_t alpha, color_elem_t red, + color_elem_t green, color_elem_t blue); + virtual void set_databar_color_negative(color_elem_t alpha, color_elem_t red, + color_elem_t green, color_elem_t blue); + virtual void set_min_databar_length(double length); + virtual void set_max_databar_length(double length); + virtual void set_show_value(bool show); + virtual void set_iconset_reverse(bool reverse); + virtual void set_xf_id(size_t xf); + virtual void set_operator(orcus::spreadsheet::condition_operator_t operator_type); + virtual void set_type(orcus::spreadsheet::conditional_format_t type); + virtual void commit_entry(); + virtual void set_range(const char* p, size_t n); + virtual void set_range(orcus::spreadsheet::row_t row_start, orcus::spreadsheet::col_t col_start, + orcus::spreadsheet::row_t row_end, orcus::spreadsheet::col_t col_end); + virtual void commit_format(); + + const condition_t* get_conditional_format(size_t index); + + +private: + string_pool& m_string_pool; + condition_t m_cur_conditional_format; + ::std::vector m_conditional_formats; +}; + +}} + + +#endif +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/include/orcus/spreadsheet/import_interface.hpp b/include/orcus/spreadsheet/import_interface.hpp index ff833fccbb0136f6a1b17cb8ce57daef988e633c..683360193f47deb53303aa3de553b880f814194b 100644 --- a/include/orcus/spreadsheet/import_interface.hpp +++ b/include/orcus/spreadsheet/import_interface.hpp @@ -13,6 +13,7 @@ #include "orcus/spreadsheet/types.hpp" #include "orcus/types.hpp" #include "orcus/env.hpp" +#include // NB: This header must not depend on ixion, as it needs to be usable for // those clients that provide their own formula engine. Other headers in @@ -141,6 +142,7 @@ public: virtual void set_xf_border(size_t index) = 0; virtual void set_xf_protection(size_t index) = 0; virtual void set_xf_number_format(size_t index) = 0; + virtual void set_xf_condition(size_t index) = 0; virtual void set_xf_style_xf(size_t index) = 0; virtual void set_xf_apply_alignment(bool b) = 0; virtual void set_xf_horizontal_alignment(orcus::spreadsheet::hor_alignment_t align) = 0; @@ -278,6 +280,15 @@ public: virtual void set_color(color_elem_t alpha, color_elem_t red, color_elem_t green, color_elem_t blue) = 0; + /* + * Sets the name of the style that has to be applied + * Used for type == condition + */ + virtual void set_style_name(const char* p, size_t n) = 0; + + /* sets the values required for conditions*/ + virtual void set_values(std::vector v) = 0; + /** * Sets the formula, value or string of the current condition. */ @@ -297,7 +308,7 @@ public: /** * commits the current condition to the current entry. */ - virtual void commit_condition() = 0; + virtual size_t commit_condition() = 0; /** * Name of the icons to use in the current entry. diff --git a/include/orcus/spreadsheet/styles.hpp b/include/orcus/spreadsheet/styles.hpp index de3e96b555a0bbf97cb29803004db4265c36912c..8d6de2346b691e72102bfbbe2a70eb95a9c3e066 100644 --- a/include/orcus/spreadsheet/styles.hpp +++ b/include/orcus/spreadsheet/styles.hpp @@ -116,6 +116,7 @@ struct ORCUS_SPM_DLLPUBLIC cell_format_t size_t protection; /// protection ID size_t number_format; /// number format ID size_t style_xf; /// style XF ID (used only for cell format) + size_t condition; /// condition ID hor_alignment_t hor_align; ver_alignment_t ver_align; bool apply_num_format:1; @@ -124,6 +125,7 @@ struct ORCUS_SPM_DLLPUBLIC cell_format_t bool apply_border:1; bool apply_alignment:1; bool apply_protection:1; + bool apply_condition:1; cell_format_t(); void reset(); @@ -199,6 +201,7 @@ public: virtual void set_xf_border(size_t index); virtual void set_xf_protection(size_t index); virtual void set_xf_number_format(size_t index); + virtual void set_xf_condition(size_t index); virtual void set_xf_style_xf(size_t index); virtual void set_xf_apply_alignment(bool b); virtual void set_xf_horizontal_alignment(orcus::spreadsheet::hor_alignment_t align); diff --git a/src/liborcus/Makefile.am b/src/liborcus/Makefile.am index e24a6dccf1cb60e002e8a732742cd265212fe3cd..746f3b98f00f62c00d0b7263f23434caa43bd1e4 100644 --- a/src/liborcus/Makefile.am +++ b/src/liborcus/Makefile.am @@ -195,6 +195,8 @@ liborcus_@ORCUS_API_VERSION@_la_SOURCES += \ odf_styles.cpp \ odf_styles_context.hpp \ odf_styles_context.cpp \ + odf_number_formatting_context.hpp \ + odf_number_formatting_context.cpp \ odf_token_constants.hpp \ odf_token_constants.inl \ odf_tokens.hpp \ diff --git a/src/liborcus/odf_number_formatting_context.cpp b/src/liborcus/odf_number_formatting_context.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f7947e67e9076fa61c71c85788681c4db3b36018 --- /dev/null +++ b/src/liborcus/odf_number_formatting_context.cpp @@ -0,0 +1,1002 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "odf_number_formatting_context.hpp" +#include "odf_namespace_types.hpp" +#include "odf_token_constants.hpp" +#include "odf_helper.hpp" +#include "orcus/measurement.hpp" +#include "orcus/spreadsheet/import_interface.hpp" +#include +#include + +#include +#include + +using namespace std; + +namespace orcus { + +namespace { + +class number_style_attr_parser : std::unary_function +{ + pstring m_country_code; + pstring m_style_name; + pstring m_language; + bool m_volatile; + +public: + + number_style_attr_parser(): + m_volatile(false) + {} + + void operator() (const xml_token_attr_t& attr) + { + if (attr.ns == NS_odf_number) + { + switch(attr.name) + { + case XML_country: + m_country_code = attr.value; + break; + case XML_language: + m_language = attr.value; + break; + default: + ; + } + } + else if (attr.ns == NS_odf_style) + { + switch (attr.name) + { + case XML_name: + m_style_name = attr.value; + break; + case XML_volatile: + m_volatile = attr.value == "true"; + break; + default: + ; + } + } + } + + const pstring& get_style_name() const { return m_style_name;} + const pstring& get_country_code() const { return m_country_code;} + bool is_volatile() const { return m_volatile;} + const pstring& get_language() const { return m_language;} +}; + +class number_attr_parser : std::unary_function +{ + size_t m_decimal_places; + size_t m_min_int_digits; + bool m_grouping; + bool m_has_decimal_places; + +public: + + number_attr_parser() : + m_grouping(false), + m_has_decimal_places(false) + {} + + void operator() (const xml_token_attr_t& attr) + { + switch (attr.name) + { + case XML_decimal_places: + { + m_decimal_places = boost::lexical_cast(attr.value.str()); + if (m_decimal_places > 0) + m_has_decimal_places = true; + } + break; + case XML_grouping: + m_grouping = attr.value == "true"; + break; + case XML_min_integer_digits: + m_min_int_digits = boost::lexical_cast(attr.value.str()); + break; + default: + ; + } + } + + const size_t& get_decimal_places() const { return m_decimal_places;} + const bool is_grouped() const { return m_grouping;} + const size_t& get_min_int_digits() const { return m_min_int_digits;} + const bool has_decimal_places() const { return m_has_decimal_places;} +}; + +class scientific_number_attr_parser : std::unary_function +{ + size_t m_decimal_places; + bool m_grouping; + size_t m_min_exp_digits; + size_t m_min_int_digits; + +public: + + scientific_number_attr_parser() : + m_grouping(false) + {} + + void operator() (const xml_token_attr_t& attr) + { + switch(attr.name) + { + case XML_decimal_places: + m_decimal_places = boost::lexical_cast(attr.value.str()); + break; + case XML_grouping: + m_grouping = attr.value == "true"; + break; + case XML_min_exponent_digits: + m_min_exp_digits = boost::lexical_cast(attr.value.str()); + break; + case XML_min_integer_digits: + m_min_int_digits = boost::lexical_cast(attr.value.str()); + break; + default: + ; + } + } + + const size_t& get_decimal_places() const { return m_decimal_places;} + const bool is_grouped() const { return m_grouping;} + const size_t& get_min_exp_digits() const { return m_min_exp_digits;} + const size_t& get_min_int_digits() const { return m_min_int_digits;} +}; + +class percentage_style_attr_parser : std::unary_function +{ + pstring m_style_name; + bool m_volatile; + +public: + percentage_style_attr_parser() : + m_volatile(false) + {} + + void operator() (const xml_token_attr_t& attr) + { + if (attr.ns == NS_odf_style) + { + switch (attr.name) + { + case XML_name: + m_style_name = attr.value; + break; + case XML_volatile: + m_volatile = attr.value == "true"; + break; + default: + ; + } + } + } + + const pstring& get_style_name() const { return m_style_name;} + const bool is_volatile() const { return m_volatile;} +}; + +class currency_style_attr_parser : std::unary_function +{ + pstring m_style_name; + bool m_volatile; + +public: + currency_style_attr_parser() : + m_volatile(false) + {} + + void operator() (const xml_token_attr_t& attr) + { + if (attr.ns == NS_odf_style) + { + switch (attr.name) + { + case XML_name: + m_style_name = attr.value; + break; + case XML_volatile: + m_volatile = attr.value == "true"; + break; + default: + ; + } + } + } + + const pstring& get_style_name() const { return m_style_name;} + const bool is_volatile() const { return m_volatile;} +}; + + +class date_style_attr_parser : std::unary_function +{ + pstring m_style_name; + bool m_volatile; + +public: + date_style_attr_parser(): + m_volatile(false) + {} + + void operator() (const xml_token_attr_t& attr) + { + if (attr.ns == NS_odf_number) + { + switch (attr.name) + { + case XML_volatile: + m_volatile = attr.value == "true"; + break; + default: + ; + } + } + else if (attr.ns == NS_odf_style) + { + switch (attr.name) + { + case XML_name: + m_style_name = attr.value; + break; + default: + ; + } + } + } + const pstring& get_style_name() const { return m_style_name;} + const bool is_volatile() const { return m_volatile;} +}; + +class day_attr_parser : std::unary_function +{ + bool m_style_name; + +public: + day_attr_parser(): + m_style_name(false) + {} + + void operator() (const xml_token_attr_t& attr) + { + if (attr.ns == NS_odf_number) + if (attr.name == XML_style) + m_style_name = attr.value == "long"; + } + + const bool has_long() const { return m_style_name;} + +}; + +class year_attr_parser : std::unary_function +{ + bool m_style_name; + +public: + year_attr_parser(): + m_style_name(false) + {} + + void operator() (const xml_token_attr_t& attr) + { + if (attr.ns == NS_odf_number) + if (attr.name == XML_style) + m_style_name = attr.value =="long"; + } + + const bool has_long() const { return m_style_name;} + +}; + +class month_attr_parser : std::unary_function +{ + bool m_style_name; + bool m_textual; + +public: + month_attr_parser(): + m_textual(false) + {} + + void operator() (const xml_token_attr_t& attr) + { + if (attr.ns == NS_odf_number) + if (attr.name == XML_style) + m_style_name = attr.value == "long"; + if (attr.name == XML_textual) + m_textual = attr.value == "true"; + } + + const bool has_long() const { return m_style_name;} + const bool is_textual() const { return m_textual;} +}; + + +class time_style_attr_parser : std::unary_function +{ + pstring m_style_name; + bool m_volatile; + +public: + time_style_attr_parser(): + m_volatile(false) + {} + + void operator() (const xml_token_attr_t& attr) + { + if (attr.ns == NS_odf_number) + { + switch (attr.name) + { + case XML_volatile: + m_volatile = attr.value == "true"; + break; + default: + ; + } + } + else if (attr.ns == NS_odf_style) + { + switch (attr.name) + { + case XML_name: + m_style_name = attr.value; + break; + default: + ; + } + } + } + + const pstring& get_style_name() const { return m_style_name;} + const bool is_volatile() const { return m_volatile;} +}; + +class hours_attr_parser : std::unary_function +{ + bool m_style_name; + +public: + hours_attr_parser(): + m_style_name(false) + {} + + void operator() (const xml_token_attr_t& attr) + { + if (attr.ns == NS_odf_number) + if (attr.name == XML_style) + m_style_name = attr.value == "long"; + } + + const bool has_long() const { return m_style_name;} +}; + +class minutes_attr_parser : std::unary_function +{ + bool m_style_name; + +public: + minutes_attr_parser(): + m_style_name(false) + {} + + void operator() (const xml_token_attr_t& attr) + { + if (attr.ns == NS_odf_number) + if (attr.name == XML_style) + m_style_name = attr.value == "long"; + } + + const bool has_long() const { return m_style_name;} +}; + +class seconds_attr_parser : std::unary_function +{ + size_t m_decimal_places; + bool m_style_name; + bool m_has_decimal_places; + +public: + seconds_attr_parser(): + m_style_name(false), + m_has_decimal_places(false) + {} + + void operator() (const xml_token_attr_t& attr) + { + if (attr.ns == NS_odf_number) + if (attr.name == XML_style) + m_style_name = attr.value == "long"; + if (attr.name == XML_decimal_places) + { + m_decimal_places = boost::lexical_cast(attr.value.str()); + m_has_decimal_places = true; + } + } + + const bool has_long() const { return m_style_name;} + const size_t& get_decimal_places() const { return m_decimal_places;} + const bool has_decimal_places() const { return m_has_decimal_places;} +}; + + +class fraction_attr_parser : std::unary_function +{ + size_t m_min_int_digits; + size_t m_min_deno_digits; + size_t m_min_num_digits; + pstring m_deno_value; + + bool m_predefined_deno; + +public: + fraction_attr_parser(): + m_predefined_deno(false) + {} + + void operator() (const xml_token_attr_t& attr) + { + switch(attr.name) + { + case XML_min_integer_digits: + m_min_int_digits = boost::lexical_cast(attr.value.str()); + break; + case XML_min_numerator_digits: + m_min_num_digits = boost::lexical_cast(attr.value.str()); + break; + case XML_min_denominator_digits: + m_min_deno_digits = boost::lexical_cast(attr.value.str()); + break; + case XML_denominator_value: + { + m_deno_value = attr.value; + m_predefined_deno = true; + } + break; + default: + ; + } + } + + const size_t& get_min_int_digits() const { return m_min_int_digits;} + const size_t& get_min_num_digits() const { return m_min_num_digits;} + const size_t& get_min_deno_digits() const { return m_min_deno_digits;} + const pstring& get_deno_value() const { return m_deno_value;} + const bool has_predefined_deno() const { return m_predefined_deno;} +}; + +class boolean_style_attr_parser : std::unary_function +{ + pstring m_style_name; + bool m_volatile; + +public: + boolean_style_attr_parser(): + m_volatile(false) + {} + + void operator() (const xml_token_attr_t& attr) + { + if (attr.ns == NS_odf_number) + { + switch (attr.name) + { + case XML_volatile: + m_volatile = attr.value == "true"; + break; + default: + ; + } + } + else if (attr.ns == NS_odf_style) + { + switch (attr.name) + { + case XML_name: + m_style_name = attr.value; + break; + default: + ; + } + } + } + + const pstring& get_style_name() const { return m_style_name;} + const bool is_volatile() const { return m_volatile;} +}; + +class text_style_attr_parser : std::unary_function +{ + pstring m_style_name; + bool m_volatile; + +public: + text_style_attr_parser(): + m_volatile(false) + {} + + void operator() (const xml_token_attr_t& attr) + { + if (attr.ns == NS_odf_number) + { + switch (attr.name) + { + case XML_volatile: + m_volatile = attr.value == "true"; + break; + default: + ; + } + } + else if (attr.ns == NS_odf_style) + { + switch (attr.name) + { + case XML_name: + m_style_name = attr.value; + break; + default: + ; + } + } + } + + const pstring& get_style_name() const { return m_style_name;} + const bool is_volatile() const { return m_volatile;} +}; + +class text_properties_attr_parser : std::unary_function +{ + pstring m_color; + bool color_absent; + +public: + text_properties_attr_parser(): + color_absent(true) + {} + + void operator() (const xml_token_attr_t& attr) + { + if (attr.ns == NS_odf_fo) + { + switch (attr.name) + { + case XML_color: + { + if (attr.value == "#000000") + m_color = "BLACK"; + if (attr.value == "#ff0000") + m_color = "RED"; + if (attr.value == "#00ff00") + m_color = "GREEN"; + if (attr.value == "#0000ff") + m_color = "BLUE"; + if (attr.value == "#ffff00") + m_color = "YELLOW"; + if (attr.value == "#00ffff") + m_color = "CYAN"; + if (attr.value == "#ff00ff") + m_color = "MAGENTA"; + if (attr.value == "$ffffff") + m_color = "WHITE"; + else + color_absent = false; + } + } + } + } + + const pstring& get_color() const { return m_color;} + const bool has_color() const { return !color_absent;} +}; + +class map_attr_parser : std::unary_function +{ + string m_value; + string m_sign; + bool m_has_map; + +public: + map_attr_parser(): + m_has_map(false) + {} + + void operator() (const xml_token_attr_t& attr) + { + if (attr.ns == NS_odf_style) + { + if (attr.name == XML_condition) + { + for (size_t i = 0; i < attr.value.size(); i++) + { + if (attr.value[i] == '<' || attr.value[i] == '>' || attr.value[i] == '=') + m_sign = m_sign + attr.value[i]; + if (isdigit(attr.value[i])) + m_value = m_value + attr.value[i]; + } + m_has_map = true; + } + } + } + const string& get_value() const { return m_value;} + const string& get_sign() const { return m_sign;} + const bool has_map() const { return m_has_map;} +}; + +} + +number_formatting_context::number_formatting_context( + session_context& session_cxt, const tokens& tk, odf_styles_map_type& styles, + spreadsheet::iface::import_styles* iface_styles, number_formatting_style* number_format_style) : + xml_context_base(session_cxt, tk), + mp_styles(iface_styles), + m_styles(styles), + m_current_style(number_format_style) +{} + +bool number_formatting_context::can_handle_element(xmlns_id_t ns, xml_token_t name) const +{ + return true; +} + +xml_context_base* number_formatting_context::create_child_context(xmlns_id_t ns, xml_token_t name) +{ + return NULL; +} + +void number_formatting_context::end_child_context(xmlns_id_t ns, xml_token_t name, xml_context_base* child) +{ +} + +void number_formatting_context::start_element(xmlns_id_t ns, xml_token_t name, const std::vector& attrs) +{ + if (ns == NS_odf_number) + { + switch(name) + { + case XML_number_style: + { + number_style_attr_parser func; + func = std::for_each(attrs.begin(), attrs.end(), func); + m_current_style->name = func.get_style_name(); + m_current_style->is_volatile = func.is_volatile(); + } + break; + case XML_number: + { + number_attr_parser func; + func = std::for_each(attrs.begin(), attrs.end(), func); + if (func.is_grouped()) + { + if (func.get_min_int_digits() < 4) + { + m_current_style->number_formatting_code += "#,"; + for (size_t i = 0; i < 3 - func.get_min_int_digits(); i++) + { + m_current_style->number_formatting_code += "#"; + } + for (size_t i = 0; i < func.get_min_int_digits(); i++) + { + m_current_style->number_formatting_code += "0"; + } + } + else + { + std:: string temporary_code; + for(size_t i = 0; i < func.get_min_int_digits(); i++) + { + if (i % 3 == 0 && i != 0) + temporary_code += ","; + temporary_code += "0"; + } + std::reverse(temporary_code.begin(), temporary_code.end()); + m_current_style->number_formatting_code += temporary_code; + } + } + else + { + if (func.get_min_int_digits() == 0) + m_current_style->number_formatting_code += "#"; + + for (size_t i = 0; i < func.get_min_int_digits(); i++) + { + m_current_style->number_formatting_code += "0"; + } + } + if (func.has_decimal_places()) + { + m_current_style->number_formatting_code += "."; + for(size_t i = 0; i < func.get_decimal_places() ; i++) + m_current_style->number_formatting_code += "0"; + } + } + break; + case XML_currency_style: + { + currency_style_attr_parser func; + func = std::for_each(attrs.begin(), attrs.end(), func); + m_current_style->name = func.get_style_name(); + m_current_style->is_volatile = func.is_volatile(); + } + break; + case XML_percentage_style: + { + percentage_style_attr_parser func; + func = std::for_each(attrs.begin(), attrs.end(), func); + m_current_style->name = func.get_style_name(); + m_current_style->is_volatile = func.is_volatile(); + } + break; + case XML_scientific_number: + { + scientific_number_attr_parser func; + func = std::for_each(attrs.begin(), attrs.end(), func); + + if (func.is_grouped()) + { + if (func.get_min_int_digits() < 4) + { + m_current_style->number_formatting_code += "#,"; + for (size_t i = 0; i < 3 - func.get_min_int_digits(); i++) + { + m_current_style->number_formatting_code += "#"; + } + for (size_t i = 0; i < func.get_min_int_digits(); i++) + { + m_current_style->number_formatting_code += "0"; + } + } + else + { + std:: string temporary_code; + for(size_t i = 0; i < func.get_min_int_digits(); i++) + { + if (i % 3 == 0 && i != 0) + temporary_code += ","; + temporary_code += "0"; + } + std::reverse(temporary_code.begin(), temporary_code.end()); + m_current_style->number_formatting_code += temporary_code; + } + } + else + { + if (func.get_min_int_digits() == 0) + m_current_style->number_formatting_code += "#"; + + for (size_t i = 0; i < func.get_min_int_digits(); i++) + { + m_current_style->number_formatting_code += "0"; + } + } + + m_current_style->number_formatting_code += "."; + for(size_t i = 0; i < func.get_decimal_places() ; i++) + m_current_style->number_formatting_code += "0"; + + m_current_style->number_formatting_code += "E+"; + for(size_t i = 0; i < func.get_min_exp_digits() ; i++) + m_current_style->number_formatting_code += "0"; + } + break; + case XML_boolean_style: + { + boolean_style_attr_parser func; + func = std::for_each(attrs.begin(), attrs.end(), func); + m_current_style->name = func.get_style_name(); + m_current_style->is_volatile = func.is_volatile(); + } + break; + case XML_boolean: + { + m_current_style->number_formatting_code += "BOOLEAN"; + } + break; + case XML_fraction: + { + fraction_attr_parser func; + func = std::for_each(attrs.begin(), attrs.end(), func); + + for (size_t i = 0; i < func.get_min_int_digits(); i++) + m_current_style->number_formatting_code += "#"; + + if (func.get_min_int_digits() != 0) + m_current_style->number_formatting_code += " "; + + for (size_t i = 0; i < func.get_min_num_digits(); i++) + m_current_style->number_formatting_code += "?"; + + m_current_style->number_formatting_code += "/"; + if (func.has_predefined_deno()) + m_current_style->number_formatting_code += func.get_deno_value(); + else + for(size_t i = 0; i < func.get_min_deno_digits(); i++) + m_current_style->number_formatting_code += "?"; + } + break; + case XML_date_style: + { + date_style_attr_parser func; + func = std::for_each(attrs.begin(), attrs.end(), func); + m_current_style->name = func.get_style_name(); + m_current_style->is_volatile = func.is_volatile(); + } + break; + case XML_day: + { + day_attr_parser func; + func = std::for_each(attrs.begin(), attrs.end(), func); + m_current_style->number_formatting_code += "D"; + if (func.has_long()) + m_current_style->number_formatting_code += "D"; + } + break; + case XML_month: + { + month_attr_parser func; + func = std::for_each(attrs.begin(), attrs.end(), func); + m_current_style->number_formatting_code += "M"; + if (func.has_long()) + m_current_style->number_formatting_code += "M"; + if (func.is_textual()) + m_current_style->number_formatting_code += "M"; + if (func.has_long() && func.is_textual()) + m_current_style->number_formatting_code += "M"; + } + break; + case XML_year: + { + year_attr_parser func; + func = std::for_each(attrs.begin(), attrs.end(), func); + m_current_style->number_formatting_code += "YY"; + if (func.has_long()) + m_current_style->number_formatting_code += "YY"; + } + break; + case XML_time_style: + { + time_style_attr_parser func; + func = std::for_each(attrs.begin(), attrs.end(), func); + m_current_style->name = func.get_style_name(); + m_current_style->is_volatile = func.is_volatile(); + } + break; + case XML_hours: + { + hours_attr_parser func; + func = std::for_each(attrs.begin(), attrs.end(), func); + m_current_style->number_formatting_code += "H"; + if (func.has_long()) + m_current_style->number_formatting_code += "H"; + } + break; + case XML_minutes: + { + minutes_attr_parser func; + func = std::for_each(attrs.begin(), attrs.end(), func); + m_current_style->number_formatting_code += "M"; + if (func.has_long()) + m_current_style->number_formatting_code += "M"; + } + break; + case XML_seconds: + { + seconds_attr_parser func; + func = std::for_each(attrs.begin(), attrs.end(), func); + m_current_style->number_formatting_code += "S"; + if (func.has_long()) + m_current_style->number_formatting_code += "S"; + if (func.has_decimal_places()) + for (size_t i = 0; i < func.get_decimal_places(); i++) + m_current_style->number_formatting_code += "S"; + } + break; + case XML_am_pm: + { + m_current_style->number_formatting_code += " AM/PM"; + } + break; + case XML_text_style: + { + text_style_attr_parser func; + func = std::for_each(attrs.begin(), attrs.end(), func); + m_current_style->name = func.get_style_name(); + m_current_style->is_volatile = func.is_volatile(); + } + break; + case XML_text_content: + { + m_current_style->number_formatting_code += "@"; + } + break; + default: + ; + } + } + if (ns == NS_odf_style) + { + switch (name) + { + case XML_text_properties: + { + text_properties_attr_parser func; + func = std::for_each(attrs.begin(), attrs.end(), func); + if (func.has_color()) + m_current_style->number_formatting_code = m_current_style->number_formatting_code + "[" + func.get_color() + "]"; + } + break; + case XML_map: + { + map_attr_parser func; + func = std::for_each(attrs.begin(), attrs.end(), func); + if (func.has_map()) + { + m_current_style->number_formatting_code = "[" + func.get_sign() + func.get_value() + "]" + + m_current_style->number_formatting_code; + } + } + break; + default: + ; + } + } +} + +bool number_formatting_context::end_element(xmlns_id_t ns, xml_token_t name) +{ + if (ns == NS_odf_number) + { + if (name == XML_number_style || name == XML_currency_style || name == XML_percentage_style + || name == XML_text_style || name == XML_boolean_style || name == XML_date_style + || name == XML_time_style) + { + if (m_current_style->is_volatile) + { + m_current_style->number_formatting_code += ";"; + } + else + { + mp_styles->set_number_format_code(m_current_style->number_formatting_code.c_str(), + m_current_style->number_formatting_code.size()); + mp_styles->set_xf_number_format(mp_styles->commit_number_format()); + + mp_styles->set_cell_style_name( m_current_style->name.get(), m_current_style->name.size()); + mp_styles->set_cell_style_xf(mp_styles->commit_cell_style_xf()); + mp_styles->commit_cell_style(); + return true; + } + } + else if (name == XML_currency_symbol) + m_current_style->number_formatting_code = m_current_style->number_formatting_code + "[$" + + m_current_style->character_stream + "]"; + + else if (name == XML_text) + m_current_style->number_formatting_code += m_current_style->character_stream; + } + m_current_style->character_stream.clear(); + return false; +} + + +void number_formatting_context::characters(const pstring& str, bool transient) +{ + if (str != "\n") + m_current_style->character_stream = str; +} + + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/src/liborcus/odf_number_formatting_context.hpp b/src/liborcus/odf_number_formatting_context.hpp new file mode 100644 index 0000000000000000000000000000000000000000..7977416dfce4c277063f5e8576dcad0975fa508b --- /dev/null +++ b/src/liborcus/odf_number_formatting_context.hpp @@ -0,0 +1,51 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef ODF_NUMBER_FORMATTING_CONTEXT_HPP +#define ODF_NUMBER_FORMATTING_CONTEXT_HPP + +#include "xml_context_base.hpp" +#include "odf_styles.hpp" +#include "odf_styles_context.hpp" +#include "orcus/global.hpp" + +namespace orcus { + +namespace spreadsheet { namespace iface { + class import_styles; +}} + + +/** + * Context that handles scope. + */ +class number_formatting_context : public xml_context_base +{ +public: + number_formatting_context( + session_context& session_cxt, const tokens& tk, odf_styles_map_type& styles, spreadsheet::iface::import_styles* iface_styles, number_formatting_style*); + + virtual bool can_handle_element(xmlns_id_t ns, xml_token_t name) const; + virtual xml_context_base* create_child_context(xmlns_id_t ns, xml_token_t name); + virtual void end_child_context(xmlns_id_t ns, xml_token_t name, xml_context_base* child); + virtual void start_element(xmlns_id_t ns, xml_token_t name, const std::vector& attrs); + virtual bool end_element(xmlns_id_t ns, xml_token_t name); + virtual void characters(const pstring& str, bool transient); + +private: + spreadsheet::iface::import_styles* mp_styles; + odf_styles_map_type& m_styles; + std::unique_ptr mp_child; + + style_value_converter m_converter; + number_formatting_style* m_current_style; +}; + +} + +#endif +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/src/liborcus/odf_styles.cpp b/src/liborcus/odf_styles.cpp index 15b2448f353ce76879e7b5aefc3b74aba63047df..cbbf3f8bec4b0cbf9fc3c5e41e342eb39d080967 100644 --- a/src/liborcus/odf_styles.cpp +++ b/src/liborcus/odf_styles.cpp @@ -75,5 +75,12 @@ odf_style::~odf_style() } } +number_formatting_style::number_formatting_style(const pstring& style_name, const bool volatile_style) +{ + name = style_name; + is_volatile = volatile_style; +} + + } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/src/liborcus/odf_styles.hpp b/src/liborcus/odf_styles.hpp index 62a392b69ecfa41dea5bb6912571350bfcff6e55..eda8e7e53edd807108db28a61a91ff21bd1ce390 100644 --- a/src/liborcus/odf_styles.hpp +++ b/src/liborcus/odf_styles.hpp @@ -49,6 +49,7 @@ struct odf_style size_t fill; size_t border; size_t protection; + size_t conditional_format; size_t xf; bool automatic_style; @@ -97,6 +98,22 @@ struct odf_style ~odf_style(); }; +struct number_formatting_style +{ + size_t number_formatting; + + pstring name; + std::string number_formatting_code; + bool is_volatile; + pstring character_stream; + + number_formatting_style(): + is_volatile(false) + {} + + number_formatting_style(const pstring& style_name, const bool volatile_style); +}; + typedef std::map> odf_styles_map_type; } diff --git a/src/liborcus/odf_styles_context.cpp b/src/liborcus/odf_styles_context.cpp index c0847fbd8580797c8db7edd2ed72180f05feb34a..7f5002e3b4a80501e75da8327a9e3f8bb0e8866c 100644 --- a/src/liborcus/odf_styles_context.cpp +++ b/src/liborcus/odf_styles_context.cpp @@ -11,8 +11,9 @@ #include "odf_helper.hpp" #include "orcus/measurement.hpp" #include "orcus/spreadsheet/import_interface.hpp" - +#include "odf_number_formatting_context.hpp" #include +#include using namespace std; @@ -386,6 +387,129 @@ public: }; +class map_prop_attr_parser : std::unary_function +{ + spreadsheet::conditional_format_t m_condition; + spreadsheet::condition_operator_t m_operator; + pstring m_style_name; + pstring m_formula; + std::vector m_value; + + bool m_has_style_name; + bool m_has_condition; + bool m_has_formula; + +public: + + map_prop_attr_parser(): + m_has_style_name(false), + m_has_condition(false), + m_has_formula(false) + {} + + void operator() (const xml_token_attr_t& attr) + { + if (attr.ns == NS_odf_style) + { + switch (attr.name) + { + case XML_condition: + { + std::string condition; + std::string operator_expression; + std::string numerical_value; + std::string formula; + + for (size_t i = 0; i < attr.value.size(); i++) + { + if (isalpha(attr.value[i]) || attr.value[i] == '-') + condition += attr.value.str()[i]; + else if (isdigit(attr.value[i])) + numerical_value += attr.value.str()[i]; + else if (attr.value[i] == ',') + { + m_value.push_back(boost::lexical_cast(numerical_value)); + numerical_value.clear(); + } + else if (attr.value[i] == '<' || attr.value[i] == '>' || attr.value[i] == '=' || attr.value[i] == '!') + operator_expression += attr.value.str()[i]; + else if (attr.value[i] == '(') + { + for (size_t j = i+1; attr.value[j] != ')'; j++) + formula += attr.value.str()[j]; + } + } + if (!numerical_value.empty()) + m_value.push_back(boost::lexical_cast(numerical_value)); // to push the remaining numerical value + + if (condition == "cell-content") + { + m_condition = spreadsheet::conditional_format_t::condition; + m_has_condition = true; + } + else if (condition.substr(0, 15) == "is-true-formula") + { + m_condition = spreadsheet::conditional_format_t::formula; + } + else if (condition == "cell-content-is-between") + { + m_condition = spreadsheet::conditional_format_t::condition; + m_operator = spreadsheet::condition_operator_t::between; + m_has_condition = true; + } + else if (condition == "cell-content-is-not-between") + { + m_condition = spreadsheet::conditional_format_t::condition; + m_operator = spreadsheet::condition_operator_t::not_between; + m_has_condition = true; + } + else + { + m_condition = spreadsheet::conditional_format_t::unknown; + m_operator = spreadsheet::condition_operator_t::unknown; + } + + if (condition.substr(0, 15) == "is-true-formula") + { + m_formula = pstring(formula.c_str(), formula.size()); + m_has_formula = true; + } + + if (operator_expression == "<") + m_operator = spreadsheet::condition_operator_t::less; + else if (operator_expression == "<=") + m_operator = spreadsheet::condition_operator_t::less_equal; + else if (operator_expression == ">") + m_operator = spreadsheet::condition_operator_t::greater; + else if (operator_expression == ">=") + m_operator = spreadsheet::condition_operator_t::greater_equal; + else if (operator_expression == "=") + m_operator = spreadsheet::condition_operator_t::equal; + else if (operator_expression == "!=") + m_operator = spreadsheet::condition_operator_t::not_equal; + } + break; + case XML_apply_style_name: + { + m_style_name = attr.value; + m_has_style_name = true; + } + break; + default: + ; + } + } + } + const spreadsheet::conditional_format_t get_condition() const { return m_condition;} + const spreadsheet::condition_operator_t get_operator() const { return m_operator;} + const pstring& get_style_name() const { return m_style_name;} + const pstring& get_formula() const { return m_formula;} + const vector& get_values() const { return m_value;} + const bool has_style_name() const { return m_has_style_name;} + const bool has_condition() const { return m_has_condition;} + const bool has_formula() const { return m_has_formula;} +}; + } style_value_converter::style_value_converter() @@ -420,9 +544,10 @@ odf_style_family style_value_converter::to_style_family(const pstring& val) cons styles_context::styles_context( session_context& session_cxt, const tokens& tk, odf_styles_map_type& styles, - spreadsheet::iface::import_styles* iface_styles) : + spreadsheet::iface::import_styles* iface_styles, spreadsheet::iface::import_conditional_format* cond_format): xml_context_base(session_cxt, tk), mp_styles(iface_styles), + mp_cond_format(cond_format), m_styles(styles), m_automatic_styles(false) { @@ -431,16 +556,28 @@ styles_context::styles_context( bool styles_context::can_handle_element(xmlns_id_t ns, xml_token_t name) const { + if (ns == NS_odf_number) + return false; + return true; } xml_context_base* styles_context::create_child_context(xmlns_id_t ns, xml_token_t name) { - return NULL; + number_formatting_style* number_formatting = new number_formatting_style; + if (ns == NS_odf_number ) + { + mp_child.reset(new number_formatting_context(get_session_context(), get_tokens(), m_styles, mp_styles, number_formatting)); + mp_child->transfer_common(*this); + return mp_child.get(); + } + + return nullptr; } void styles_context::end_child_context(xmlns_id_t ns, xml_token_t name, xml_context_base* child) { + return; } void styles_context::start_element(xmlns_id_t ns, xml_token_t name, const std::vector& attrs) @@ -634,6 +771,31 @@ void styles_context::start_element(xmlns_id_t ns, xml_token_t name, const std::v } } break; + case XML_map: + { + xml_element_expected(parent, NS_odf_style, XML_style); + map_prop_attr_parser func; + func = std::for_each(attrs.begin(), attrs.end(), func); + + if (func.has_condition()) + { + mp_cond_format->set_type(func.get_condition()); + mp_cond_format->set_values(func.get_values()); + mp_cond_format->set_operator(func.get_operator()); + } + if (func.has_formula()) + { + mp_cond_format->set_type(func.get_condition()); + mp_cond_format->set_formula(func.get_formula().get(), func.get_formula().size()); + } + if (func.has_style_name()) + mp_cond_format->set_style_name(func.get_style_name().get(), func.get_style_name().size()); + + size_t cond_format_id = mp_cond_format->commit_condition(); + mp_styles->set_xf_condition(cond_format_id); + + } + break; default: warn_unhandled(); } diff --git a/src/liborcus/odf_styles_context.hpp b/src/liborcus/odf_styles_context.hpp index fe9923ecf879d4219b44154381c92f07e9bf6f9f..59a21d5d31e1dcdf6480cd9eb26c1ca2eed42bcf 100644 --- a/src/liborcus/odf_styles_context.hpp +++ b/src/liborcus/odf_styles_context.hpp @@ -19,6 +19,7 @@ namespace orcus { namespace spreadsheet { namespace iface { class import_styles; + class import_conditional_format; }} class style_value_converter @@ -39,7 +40,8 @@ class styles_context : public xml_context_base { public: styles_context( - session_context& session_cxt, const tokens& tk, odf_styles_map_type& styles, spreadsheet::iface::import_styles* iface_styles); + session_context& session_cxt, const tokens& tk, odf_styles_map_type& styles, spreadsheet::iface::import_styles* iface_styles, + spreadsheet::iface::import_conditional_format* cond_format = nullptr); virtual bool can_handle_element(xmlns_id_t ns, xml_token_t name) const; virtual xml_context_base* create_child_context(xmlns_id_t ns, xml_token_t name); @@ -53,7 +55,9 @@ private: private: spreadsheet::iface::import_styles* mp_styles; + spreadsheet::iface::import_conditional_format* mp_cond_format; odf_styles_map_type& m_styles; + std::unique_ptr mp_child; style_value_converter m_converter; diff --git a/src/liborcus/odf_styles_context_test.cpp b/src/liborcus/odf_styles_context_test.cpp index a283b23c09f50e4e76e7c2a0ac119d64ff46dd10..4bdaec06c8c59da6e5d557e4b5206b74738fa8cd 100644 --- a/src/liborcus/odf_styles_context_test.cpp +++ b/src/liborcus/odf_styles_context_test.cpp @@ -1,5 +1,5 @@ #include - +#include #include #include @@ -28,17 +28,8 @@ const orcus::spreadsheet::cell_style_t* find_cell_style_by_name(const orcus::pst } -int main() +void test_odf_fill(orcus::spreadsheet::import_styles &styles) { - orcus::string_pool string_pool; - const char* path = SRCDIR"/test/ods/styles/cell-styles.xml"; - std::string content = orcus::load_file_content(path); - orcus::spreadsheet::import_styles styles(string_pool); - orcus::import_ods::read_styles(content.c_str(), content.size(), &styles); - -/* Test for Cell Fill - ===================================================== -*/ const orcus::spreadsheet::cell_style_t* style = find_cell_style_by_name("Name1", &styles); assert(style->parent_name == "Text"); size_t xf = style->xf; @@ -54,16 +45,16 @@ int main() assert(cell_fill->bg_color.red == 0xfe); assert(cell_fill->bg_color.green == 0xff); assert(cell_fill->bg_color.blue == 0xcc); +} -/* Test for Border Styles - ===================================================== -*/ +void test_odf_border(orcus::spreadsheet::import_styles &styles) +{ assert(styles.get_border_count() == 8); /* Test that border style applies to all the sides when not specified */ - style = find_cell_style_by_name("Name1", &styles); - xf = style->xf; - cell_format = styles.get_cell_style_format(xf); + const orcus::spreadsheet::cell_style_t* style = find_cell_style_by_name("Name1", &styles); + size_t xf = style->xf; + const orcus::spreadsheet::cell_format_t* cell_format = styles.get_cell_style_format(xf); size_t border = cell_format->border; assert(cell_format); @@ -109,14 +100,14 @@ int main() assert(cell_border->diagonal_bl_tr.border_color.red == 0xff); assert(cell_border->diagonal_tl_br.border_color.green == 0x00); assert(cell_border->diagonal_tl_br.border_width.value == 0.74); +} -/* Test for Cell Protection - ======================================================== -*/ +void test_odf_cell_protection(orcus::spreadsheet::import_styles& styles) +{ /* Test that Cell is only protected and not hidden , Print Content is true */ - style = find_cell_style_by_name("Name5", &styles); - xf = style->xf; - cell_format = styles.get_cell_style_format(xf); + const orcus::spreadsheet::cell_style_t* style = find_cell_style_by_name("Name5", &styles); + size_t xf = style->xf; + const orcus::spreadsheet::cell_format_t* cell_format = styles.get_cell_style_format(xf); size_t protection = cell_format->protection; assert(cell_format); @@ -151,13 +142,13 @@ int main() assert(cell_protection->hidden == false); assert(cell_protection->print_content == true); assert(cell_protection->formula_hidden == false); +} -/* Test for Font and underline - ================================================== -*/ - style = find_cell_style_by_name("Name8", &styles); - xf = style->xf; - cell_format = styles.get_cell_style_format(xf); +void test_odf_font(orcus::spreadsheet::import_styles& styles) +{ + const orcus::spreadsheet::cell_style_t* style = find_cell_style_by_name("Name8", &styles); + size_t xf = style->xf; + const orcus::spreadsheet::cell_format_t* cell_format = styles.get_cell_style_format(xf); size_t font = cell_format->font; assert(cell_format); @@ -192,6 +183,143 @@ int main() assert(cell_font->underline_color.red == (int)0x18); assert(cell_font->underline_color.green == (int)0x56); assert(cell_font->underline_color.blue == (int)0xff); +} + +void test_odf_number_formatting(orcus::spreadsheet::import_styles& styles) +{ + const orcus::spreadsheet::cell_style_t* style = find_cell_style_by_name("Name10", &styles); + size_t xf = style->xf; + const orcus::spreadsheet::cell_format_t* cell_format = styles.get_cell_style_format(xf); + assert(cell_format); + + size_t number_format = cell_format->number_format; + const orcus::spreadsheet::number_format_t* cell_number_format = styles.get_number_format(number_format); + assert(cell_number_format->format_string.str() == "#.000000"); + + style = find_cell_style_by_name("Name11", &styles); + xf = style->xf; + cell_format = styles.get_cell_style_format(xf); + assert(cell_format); + + number_format = cell_format->number_format; + cell_number_format = styles.get_number_format(number_format); + assert(cell_number_format->format_string.str() == "[$₹]#,##0.00;[RED]-[$₹]#,##0.00"); + + style = find_cell_style_by_name("Name12", &styles); + xf = style->xf; + cell_format = styles.get_cell_style_format(xf); + assert(cell_format); + + number_format = cell_format->number_format; + cell_number_format = styles.get_number_format(number_format); + assert(cell_number_format->format_string.str() == "0.00%"); + + style = find_cell_style_by_name("Name13", &styles); + xf = style->xf; + cell_format = styles.get_cell_style_format(xf); + assert(cell_format); + + number_format = cell_format->number_format; + cell_number_format = styles.get_number_format(number_format); + assert(cell_number_format->format_string.str() == "#.00E+00"); + + style = find_cell_style_by_name("Name15", &styles); + xf = style->xf; + cell_format = styles.get_cell_style_format(xf); + assert(cell_format); + + number_format = cell_format->number_format; + cell_number_format = styles.get_number_format(number_format); + assert(cell_number_format->format_string.str() == "BOOLEAN"); + + style = find_cell_style_by_name("Name16", &styles); + xf = style->xf; + cell_format = styles.get_cell_style_format(xf); + assert(cell_format); + + number_format = cell_format->number_format; + cell_number_format = styles.get_number_format(number_format); + assert(cell_number_format->format_string.str() == "#### ?/11"); + + style = find_cell_style_by_name("Name17", &styles); + xf = style->xf; + cell_format = styles.get_cell_style_format(xf); + assert(cell_format); + + number_format = cell_format->number_format; + cell_number_format = styles.get_number_format(number_format); + assert(cell_number_format->format_string.str() == "MM/DD/YY"); + + style = find_cell_style_by_name("Name18", &styles); + xf = style->xf; + cell_format = styles.get_cell_style_format(xf); + assert(cell_format); + + number_format = cell_format->number_format; + cell_number_format = styles.get_number_format(number_format); + std::cerr<format_string.str(); + assert(cell_number_format->format_string.str() == "HH:MM:SS AM/PM"); + + style = find_cell_style_by_name("Name19", &styles); + xf = style->xf; + cell_format = styles.get_cell_style_format(xf); + assert(cell_format); + + number_format = cell_format->number_format; + cell_number_format = styles.get_number_format(number_format); + assert(cell_number_format->format_string.str() == "[>=0]0.00;[RED]-0.00"); + +} + +void test_odf_conditional_format(orcus::spreadsheet::import_styles& styles, + orcus::spreadsheet::import_conditional_format& cond_format) +{ + const orcus::spreadsheet::cell_style_t* style = find_cell_style_by_name("Name20", &styles); + size_t xf = style->xf; + const orcus::spreadsheet::cell_format_t* cell_format = styles.get_cell_style_format(xf); + + size_t conditional_format = cell_format->condition; + const orcus::spreadsheet::condition_t* cell_conditional_format = cond_format.get_conditional_format(conditional_format); + assert(cell_conditional_format->mapped_name == "Result2"); + assert(cell_conditional_format->condition_operator == orcus::spreadsheet::condition_operator_t::greater); + assert(cell_conditional_format->condition_type == orcus::spreadsheet::conditional_format_t::condition); + assert(cell_conditional_format->values[0] == 5); + + style = find_cell_style_by_name("Name21", &styles); + xf = style->xf; + cell_format = styles.get_cell_style_format(xf); + + conditional_format = cell_format->condition; + cell_conditional_format = cond_format.get_conditional_format(conditional_format); + assert(cell_conditional_format->mapped_name == "Default"); + assert(cell_conditional_format->condition_type == orcus::spreadsheet::conditional_format_t::condition); + assert(cell_conditional_format->condition_operator == orcus::spreadsheet::condition_operator_t::between); + assert(cell_conditional_format->values[0] == 10); + assert(cell_conditional_format->values[1] == 20); + +} +int main() +{ + orcus::string_pool string_pool; + const char* path = SRCDIR"/test/ods/styles/cell-styles.xml"; + std::string content = orcus::load_file_content(path); + orcus::spreadsheet::import_styles styles(string_pool); + orcus::spreadsheet::import_conditional_format cond_format(string_pool); + orcus::import_ods::read_styles(content.c_str(), content.size(), &styles, &cond_format); + + test_odf_fill(styles); + test_odf_border(styles); + test_odf_cell_protection(styles); + test_odf_font(styles); + test_odf_conditional_format(styles, cond_format); + + orcus::string_pool string_pool2; + path = SRCDIR"/test/ods/styles/number-format.xml"; + std::string content2 = orcus::load_file_content(path); + orcus::spreadsheet::import_styles styles2(string_pool2); + orcus::import_ods::read_styles(content2.c_str(), content2.size(), &styles2); + + test_odf_number_formatting(styles2); return 0; } diff --git a/src/liborcus/orcus_import_ods.cpp b/src/liborcus/orcus_import_ods.cpp index 2d76dbb780787f7fda1d598550de76782d482cc8..b352e76d98d7d6b68142d70d14fd57197e596775 100644 --- a/src/liborcus/orcus_import_ods.cpp +++ b/src/liborcus/orcus_import_ods.cpp @@ -18,11 +18,11 @@ #include "session_context.hpp" #include "xml_stream_parser.hpp" -#include "xml_simple_stream_handler.hpp" namespace orcus { -void import_ods::read_styles(const char* p, size_t n, spreadsheet::iface::import_styles* styles) +void import_ods::read_styles(const char* p, size_t n, spreadsheet::iface::import_styles* styles, + spreadsheet::iface::import_conditional_format* cond_format) { if(!styles) return; @@ -32,9 +32,9 @@ void import_ods::read_styles(const char* p, size_t n, spreadsheet::iface::import session_context cxt; odf_styles_map_type styles_map; - auto context = orcus::make_unique(cxt, odf_tokens, styles_map, styles); + auto context = orcus::make_unique(cxt, odf_tokens, styles_map, styles, cond_format); - xml_simple_stream_handler stream_handler(context.release()); + xml_stream_handler stream_handler(context.release()); xmlns_repository ns_repo; ns_repo.add_predefined_values(NS_odf_all); diff --git a/src/liborcus/xml_stream_handler.hpp b/src/liborcus/xml_stream_handler.hpp index 25c7de3412f20e8df2e27cad891bb2cd0ecc165d..cf0dc38f2ed8d84faec1a10f310ff26f683180c9 100644 --- a/src/liborcus/xml_stream_handler.hpp +++ b/src/liborcus/xml_stream_handler.hpp @@ -31,7 +31,7 @@ class xml_stream_handler xml_stream_handler(); // disabled public: xml_stream_handler(xml_context_base* root_context); - virtual ~xml_stream_handler() = 0; + virtual ~xml_stream_handler() ; virtual void start_document(); virtual void end_document(); diff --git a/src/spreadsheet/Makefile.am b/src/spreadsheet/Makefile.am index bb488b911a8212b8d29109e841d76318af9220e5..9867639d5ec11b7d863c1d739bf0a09210a6a03a 100644 --- a/src/spreadsheet/Makefile.am +++ b/src/spreadsheet/Makefile.am @@ -13,6 +13,7 @@ endif lib_LTLIBRARIES = liborcus-spreadsheet-model-@ORCUS_API_VERSION@.la liborcus_spreadsheet_model_@ORCUS_API_VERSION@_la_SOURCES = \ auto_filter.cpp \ + conditional_format.cpp\ data_table.hpp \ data_table.cpp \ document.cpp \ diff --git a/src/spreadsheet/conditional_format.cpp b/src/spreadsheet/conditional_format.cpp new file mode 100644 index 0000000000000000000000000000000000000000..384b797434e414a541ce8d796db4d35b241c1339 --- /dev/null +++ b/src/spreadsheet/conditional_format.cpp @@ -0,0 +1,102 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +#include "orcus/spreadsheet/conditional_format.hpp" +#include "orcus/string_pool.hpp" + +#include +#include + +namespace orcus { + +class string_pool; + +namespace spreadsheet { + +void condition_t::reset() +{ + *this = condition_t(); +} + +import_conditional_format::import_conditional_format(string_pool& sp) : m_string_pool(sp) {} + +import_conditional_format::~import_conditional_format() +{ +} + +condition_t::condition_t() +{} + +void import_conditional_format::set_color(color_elem_t alpha, color_elem_t red, color_elem_t green, color_elem_t blue) +{ + m_cur_conditional_format.color = color_t (alpha, red, green, blue); +} + +void import_conditional_format::set_style_name(const char* p, size_t n) +{ + m_cur_conditional_format.mapped_name = pstring(p, n); +} + +void import_conditional_format::set_formula(const char* p, size_t n) +{ + m_cur_conditional_format.formula = pstring(p, n); +} + +void import_conditional_format::set_condition_type(orcus::spreadsheet::condition_type_t type) {} + +void import_conditional_format::set_operator(orcus::spreadsheet::condition_operator_t operator_type) +{ + m_cur_conditional_format.condition_operator = operator_type; +} + +size_t import_conditional_format::commit_condition() +{ + m_conditional_formats.push_back(m_cur_conditional_format); + m_cur_conditional_format.reset(); + return m_conditional_formats.size() - 1; +} + +void import_conditional_format::set_type(orcus::spreadsheet::conditional_format_t type) +{ + m_cur_conditional_format.condition_type = type; +} + +void import_conditional_format::set_values(std::vector v) +{ + m_cur_conditional_format.values = v; +} + +const condition_t* import_conditional_format::get_conditional_format(size_t index) +{ + if (index >= m_conditional_formats.size()) + return NULL; + + return &m_conditional_formats[index]; +} + +void import_conditional_format::set_date(orcus::spreadsheet::condition_date_t) {} +void import_conditional_format::set_icon_name(const char* p, size_t n) {} + +void import_conditional_format::set_databar_gradient(bool gradient) {} +void import_conditional_format::set_databar_axis(orcus::spreadsheet::databar_axis_t axis) {} +void import_conditional_format::set_databar_color_positive(color_elem_t alpha, color_elem_t red, + color_elem_t green, color_elem_t blue) {} +void import_conditional_format::set_databar_color_negative(color_elem_t alpha, color_elem_t red, + color_elem_t green, color_elem_t blue) {} +void import_conditional_format::set_min_databar_length(double length) {} +void import_conditional_format::set_max_databar_length(double length) {} +void import_conditional_format::set_show_value(bool show) {} +void import_conditional_format::set_iconset_reverse(bool reverse) {} +void import_conditional_format::set_xf_id(size_t xf) {} +void import_conditional_format::commit_entry() {} +void import_conditional_format::set_range(const char* p, size_t n) {} +void import_conditional_format::set_range(orcus::spreadsheet::row_t row_start, orcus::spreadsheet::col_t col_start, + orcus::spreadsheet::row_t row_end, orcus::spreadsheet::col_t col_end) {} +void import_conditional_format::commit_format() {} + +}} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/src/spreadsheet/styles.cpp b/src/spreadsheet/styles.cpp index 2784e1d56795edcdb557a6b632cb446fc3c6074c..f6279f9f844967e9dd9d8d633079edc87563d366 100644 --- a/src/spreadsheet/styles.cpp +++ b/src/spreadsheet/styles.cpp @@ -406,6 +406,11 @@ void import_styles::set_xf_number_format(size_t index) m_cur_cell_format.number_format = index; } +void import_styles::set_xf_condition(size_t index) +{ + m_cur_cell_format.condition = index; +} + void import_styles::set_xf_style_xf(size_t index) { m_cur_cell_format.style_xf = index; diff --git a/test/ods/styles/cell-styles.xml b/test/ods/styles/cell-styles.xml index 070f7ee6725d5cf677430af84eaf4cfb5f549a92..c183063dfafe64cc09871cf5aac8452dad024b49 100644 --- a/test/ods/styles/cell-styles.xml +++ b/test/ods/styles/cell-styles.xml @@ -27,4 +27,10 @@ + + + + + + diff --git a/test/ods/styles/number-format.xml b/test/ods/styles/number-format.xml new file mode 100644 index 0000000000000000000000000000000000000000..ecc1fbc9aed170de23247812d0ee402bf674b803 --- /dev/null +++ b/test/ods/styles/number-format.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + - + + + + + + % + + + + + + + December + + + + + + + + + + / + + / + + + + + : + + : + + + + + + + + + + - + + + +