diff --git a/benchmarks/styles.cpp b/benchmarks/styles.cpp index b9d26500..58071a9c 100644 --- a/benchmarks/styles.cpp +++ b/benchmarks/styles.cpp @@ -22,7 +22,9 @@ // @author: see AUTHORS file #include +#include #include +#include #include #include @@ -41,119 +43,135 @@ std::size_t random_index(std::size_t max) return dis(gen); } -std::vector generate_all_styles(xlnt::workbook &wb) +void generate_all_formats(xlnt::workbook &wb, std::vector& formats) { - std::vector styles; + const auto vertical_alignments = std::vector + { + xlnt::vertical_alignment::center, + xlnt::vertical_alignment::justify, + xlnt::vertical_alignment::top, + xlnt::vertical_alignment::bottom + }; - const auto vertical_alignments = std::vector - { - xlnt::vertical_alignment::center, - xlnt::vertical_alignment::justify, - xlnt::vertical_alignment::top, - xlnt::vertical_alignment::bottom - }; + const auto horizontal_alignments = std::vector + { + xlnt::horizontal_alignment::center, + xlnt::horizontal_alignment::center_continuous, + xlnt::horizontal_alignment::general, + xlnt::horizontal_alignment::justify, + xlnt::horizontal_alignment::left, + xlnt::horizontal_alignment::right + }; - const auto horizontal_alignments = std::vector - { - xlnt::horizontal_alignment::center, - xlnt::horizontal_alignment::center_continuous, - xlnt::horizontal_alignment::general, - xlnt::horizontal_alignment::justify, - xlnt::horizontal_alignment::left, - xlnt::horizontal_alignment::right - }; + const auto font_names = std::vector + { + "Calibri", + "Tahoma", + "Arial", + "Times New Roman" + }; - const auto font_names = std::vector - { - "Calibri", - "Tahoma", - "Arial", - "Times New Roman" - }; + const auto font_sizes = std::vector + { + 11., + 13., + 15., + 17., + 19., + 21., + 23., + 25., + 27., + 29., + 31., + 33., + 35. + }; - const auto font_sizes = std::vector - { - 11., - 13., - 15., - 17., - 19., - 21., - 23., - 25., - 27., - 29., - 31., - 33., - 35. - }; + const auto underline_options = std::vector + { + xlnt::font::underline_style::single, + xlnt::font::underline_style::none + }; - const auto underline_options = std::vector - { - xlnt::font::underline_style::single, - xlnt::font::underline_style::none - }; + for (auto vertical_alignment : vertical_alignments) + { + for (auto horizontal_alignment : horizontal_alignments) + { + for (auto name : font_names) + { + for (auto size : font_sizes) + { + for (auto bold : { true, false }) + { + for (auto underline : underline_options) + { + for (auto italic : { true, false }) + { + auto fmt = wb.create_format(); - std::size_t index = 0; + xlnt::font f; + f.name(name); + f.size(size); + f.italic(italic); + f.underline(underline); + f.bold(bold); + fmt.font(f); - for (auto vertical_alignment : vertical_alignments) - { - for (auto horizontal_alignment : horizontal_alignments) - { - for (auto name : font_names) - { - for (auto size : font_sizes) - { - for (auto bold : { true, false }) - { - for (auto underline : underline_options) - { - for (auto italic : { true, false }) - { - auto s = wb.create_style(std::to_string(index++)); + xlnt::alignment a; + a.vertical(vertical_alignment); + a.horizontal(horizontal_alignment); + fmt.alignment(a); - xlnt::font f; - f.name(name); - f.size(size); - f.italic(italic); - f.underline(underline); - f.bold(bold); - s.font(f); - - xlnt::alignment a; - a.vertical(vertical_alignment); - a.horizontal(horizontal_alignment); - s.alignment(a); - - styles.push_back(s); - } - } - } - } - } - } - } - - return styles; + formats.push_back(fmt); + } + } + } + } + } + } + } } -xlnt::workbook non_optimized_workbook(int n) + +xlnt::workbook non_optimized_workbook_formats(int rows_number, int columns_number) { - xlnt::workbook wb; - auto styles = generate_all_styles(wb); + using xlnt::benchmarks::current_time; - for(int idx = 1; idx < n; idx++) - { - auto worksheet = wb[random_index(wb.sheet_count())]; - auto cell = worksheet.cell(xlnt::cell_reference(1, (xlnt::row_t)idx)); - cell.value(0); - cell.style(styles.at(random_index(styles.size()))); - } + xlnt::workbook wb; + std::vector formats; + auto start = current_time(); - return wb; + generate_all_formats(wb, formats); + + auto elapsed = current_time() - start; + + std::cout << "elapsed " << elapsed / 1000.0 << ". generate_all_formats. number of unique formats " << formats.size() << std::endl; + + start = current_time(); + auto worksheet = wb[random_index(wb.sheet_count())]; + auto cells_proceeded = 0; + for (int row_idx = 1; row_idx <= rows_number; row_idx++) + { + for (int col_idx = 1; col_idx <= columns_number; col_idx++) + { + auto cell = worksheet.cell(xlnt::cell_reference((xlnt::column_t)col_idx, (xlnt::row_t)row_idx)); + std::ostringstream string_stm; + string_stm << "Col: " << col_idx << "Row: " << row_idx; + cell.value(string_stm.str()); + cell.format(formats.at(random_index(formats.size()))); + cells_proceeded++; + } + } + + elapsed = current_time() - start; + + std::cout << "elapsed " << elapsed / 1000.0 << ". set values and formats for cells. cells proceeded " << cells_proceeded << std::endl; + + return wb; } -void to_profile(xlnt::workbook &wb, const std::string &f, int n) +void to_save_profile(xlnt::workbook &wb, const std::string &f) { using xlnt::benchmarks::current_time; @@ -161,17 +179,72 @@ void to_profile(xlnt::workbook &wb, const std::string &f, int n) wb.save(f); auto elapsed = current_time() - start; - std::cout << "took " << elapsed / 1000.0 << "s for " << n << " styles" << std::endl; + std::cout << "elapsed " << elapsed / 1000.0 << ". save workbook." << std::endl; +} + +void to_load_profile(xlnt::workbook &wb, const std::string &f) +{ + using xlnt::benchmarks::current_time; + + auto start = current_time(); + wb.load(f); + auto elapsed = current_time() - start; + + std::cout << "elapsed " << elapsed / 1000.0 << ". load workbook." << std::endl; +} + +void read_formats_profile(xlnt::workbook &wb, int rows_number, int columns_number) +{ + using xlnt::benchmarks::current_time; + + std::vector values; + std::vector formats; + auto start = current_time(); + auto worksheet = wb[random_index(wb.sheet_count())]; + for (int row_idx = 1; row_idx <= rows_number; row_idx++) + { + for (int col_idx = 1; col_idx <= columns_number; col_idx++) + { + auto cell = worksheet.cell(xlnt::cell_reference((xlnt::column_t)col_idx, (xlnt::row_t)row_idx)); + values.push_back(cell.value()); + formats.push_back(cell.format()); + } + } + + auto elapsed = current_time() - start; + + std::cout << "elapsed " << elapsed / 1000.0 << ". read values and formats for cells. values count " << values.size() + << ". formats count " << formats.size() << std::endl; } } // namespace -int main() +int main(int argc, char * argv[]) { - int n = 10000; - auto wb = non_optimized_workbook(n); - std::string f = "temp.xlsx"; - to_profile(wb, f, n); + int rows_number = 1000; + int columns_number = 10; + + try + { + if (argc > 1) + rows_number = std::stoi(argv[1]); + + if (argc > 2) + columns_number = std::stoi(argv[2]); + + std::cout << "started. number of rows " << rows_number << ", number of columns " << columns_number << std::endl; + auto wb = non_optimized_workbook_formats(rows_number, columns_number); + auto f = "temp-formats.xlsx"; + to_save_profile(wb, f); + + xlnt::workbook load_formats_wb; + to_load_profile(load_formats_wb, f); + read_formats_profile(load_formats_wb, rows_number, columns_number); + } + catch(std::exception& ex) + { + std::cout << "failed. " << ex.what() << std::endl; + } return 0; } diff --git a/include/xlnt/cell/rich_text.hpp b/include/xlnt/cell/rich_text.hpp index 85bbc29a..31cb2790 100644 --- a/include/xlnt/cell/rich_text.hpp +++ b/include/xlnt/cell/rich_text.hpp @@ -48,6 +48,11 @@ public: /// rich_text(const std::string &plain_text); + /// + /// Constructs a rich text object from other + /// + rich_text(const rich_text &other); + /// /// Constructs a rich text object with the given text and font. /// @@ -89,6 +94,11 @@ public: /// void add_run(const rich_text_run &t); + /// + /// Copies rich text object from other + /// + rich_text& operator=(const rich_text &rhs); + /// /// Returns true if the runs that make up this text are identical to those in rhs. /// @@ -97,7 +107,7 @@ public: /// /// Returns true if the runs that make up this text are identical to those in rhs. /// - bool operator!=(const rich_text &rhs) const; + bool operator!=(const rich_text &rhs) const; /// /// Returns true if this text has a single unformatted run with text equal to rhs. @@ -116,4 +126,20 @@ private: std::vector runs_; }; +class XLNT_API rich_text_hash +{ +public: + std::size_t operator()(const rich_text& k) const + { + std::size_t res = 0; + + for (auto r : k.runs()) + { + res ^= std::hash()(r.first); + } + + return res; + } +}; + } // namespace xlnt diff --git a/include/xlnt/styles/color.hpp b/include/xlnt/styles/color.hpp index db35e6ef..24021452 100644 --- a/include/xlnt/styles/color.hpp +++ b/include/xlnt/styles/color.hpp @@ -101,7 +101,7 @@ public: /// /// Constructs an RGB color from red, green, and blue values in the range 0 to 255 - /// plus an optional alpha which defaults to fully opaque. + /// plus an optional alpha which defaults to fully opaque. /// rgb_color(std::uint8_t r, std::uint8_t g, std::uint8_t b, std::uint8_t a = 255); @@ -116,17 +116,17 @@ public: std::uint8_t red() const; /// - /// Returns a byte representing the red component of this color + /// Returns a byte representing the red component of this color /// std::uint8_t green() const; /// - /// Returns a byte representing the blue component of this color + /// Returns a byte representing the blue component of this color /// std::uint8_t blue() const; /// - /// Returns a byte representing the alpha component of this color + /// Returns a byte representing the alpha component of this color /// std::uint8_t alpha() const; @@ -136,7 +136,7 @@ public: std::array rgb() const; /// - /// Returns the red, green, blue, and alpha components of this color separately in an array in that order. + /// Returns the red, green, blue, and alpha components of this color separately in an array in that order. /// std::array rgba() const; @@ -174,42 +174,42 @@ public: static const color white(); /// - /// Returns the color \#ff0000 + /// Returns the color \#ff0000 /// static const color red(); /// - /// Returns the color \#8b0000 + /// Returns the color \#8b0000 /// static const color darkred(); /// - /// Returns the color \#00ff00 + /// Returns the color \#00ff00 /// static const color blue(); /// - /// Returns the color \#008b00 + /// Returns the color \#008b00 /// static const color darkblue(); /// - /// Returns the color \#0000ff + /// Returns the color \#0000ff /// static const color green(); /// - /// Returns the color \#00008b + /// Returns the color \#00008b /// static const color darkgreen(); /// - /// Returns the color \#ffff00 + /// Returns the color \#ffff00 /// static const color yellow(); /// - /// Returns the color \#cccc00 + /// Returns the color \#cccc00 /// static const color darkyellow(); @@ -249,8 +249,8 @@ public: void auto_(bool value); /// - /// Returns the internal indexed color representing this color. If this is not an RGB color, - /// an invalid_attribute exception will be thrown. + /// Returns the internal indexed color representing this color. If this is not an RGB color, + /// an invalid_attribute exception will be thrown. /// const rgb_color& rgb() const; @@ -262,7 +262,7 @@ public: /// /// Returns the internal indexed color representing this color. If this is not an indexed color, - /// an invalid_attribute exception will be thrown. + /// an invalid_attribute exception will be thrown. /// const indexed_color& indexed() const; @@ -273,8 +273,8 @@ public: indexed_color &indexed(); /// - /// Returns the internal indexed color representing this color. If this is not a theme color, - /// an invalid_attribute exception will be thrown. + /// Returns the internal indexed color representing this color. If this is not a theme color, + /// an invalid_attribute exception will be thrown. /// const theme_color& theme() const; @@ -294,15 +294,15 @@ public: /// void tint(double tint); - /// - /// Returns true if this color is equivalent to other - /// - bool operator==(const color &other) const; + /// + /// Returns true if this color is equivalent to other + /// + bool operator==(const color &other) const; /// /// Returns true if this color is not equivalent to other /// - bool operator!=(const color &other) const; + bool operator!=(const color &other) const; private: /// @@ -321,12 +321,12 @@ private: rgb_color rgb_; /// - /// The internal RGB color. Only valid when this color has a type of indexed + /// The internal RGB color. Only valid when this color has a type of indexed /// indexed_color indexed_; /// - /// The internal RGB color. Only valid when this color has a type of theme + /// The internal RGB color. Only valid when this color has a type of theme /// theme_color theme_; diff --git a/include/xlnt/utils/optional.hpp b/include/xlnt/utils/optional.hpp index 8248ebd2..8677f24b 100644 --- a/include/xlnt/utils/optional.hpp +++ b/include/xlnt/utils/optional.hpp @@ -314,4 +314,4 @@ private: #undef XLNT_NOEXCEPT_VALUE_COMPAT #endif -} // namespace xlnt \ No newline at end of file +} // namespace xlnt diff --git a/include/xlnt/workbook/workbook.hpp b/include/xlnt/workbook/workbook.hpp index 7b98f622..d92cf2b2 100644 --- a/include/xlnt/workbook/workbook.hpp +++ b/include/xlnt/workbook/workbook.hpp @@ -33,6 +33,7 @@ #include #include +#include namespace xlnt { @@ -700,10 +701,10 @@ public: /// class style create_style(const std::string &name); - /// - /// Creates a new style and returns it. - /// - class style create_builtin_style(std::size_t builtin_id); + /// + /// Creates a new style and returns it. + /// + class style create_builtin_style(std::size_t builtin_id); /// /// Clear all named styles from cells and remove the styles from @@ -759,17 +760,27 @@ public: /// std::size_t add_shared_string(const rich_text &shared, bool allow_duplicates = false); - /// - /// Returns a reference to the shared strings being used by cells - /// in this workbook. - /// - std::vector &shared_strings(); + /// + /// Returns a reference to the shared string ordered by id + /// + const std::unordered_map &shared_strings_by_id() const; + + /// + /// Returns a reference to the shared string related to the specified index + /// + const rich_text &shared_strings(std::size_t index) const; /// /// Returns a reference to the shared strings being used by cells /// in this workbook. /// - const std::vector &shared_strings() const; + std::unordered_map &shared_strings(); + + /// + /// Returns a reference to the shared strings being used by cells + /// in this workbook. + /// + const std::unordered_map &shared_strings() const; // Thumbnail diff --git a/source/cell/cell.cpp b/source/cell/cell.cpp index bc5f558f..667212d8 100644 --- a/source/cell/cell.cpp +++ b/source/cell/cell.cpp @@ -704,7 +704,7 @@ XLNT_API rich_text cell::value() const { if (data_type() == cell::type::shared_string) { - return workbook().shared_strings().at(static_cast(d_->value_numeric_)); + return workbook().shared_strings(static_cast(d_->value_numeric_)); } return d_->value_text_; @@ -861,19 +861,19 @@ style cell::style() throw invalid_attribute(); } - auto f = format(); + auto f = format(); return f.style(); } const style cell::style() const { - if (!has_format() || !format().has_style()) - { - throw invalid_attribute(); - } + if (!has_format() || !format().has_style()) + { + throw invalid_attribute(); + } - return format().style(); + return format().style(); } bool cell::has_style() const diff --git a/source/cell/rich_text.cpp b/source/cell/rich_text.cpp index 6cda69db..3cf13290 100644 --- a/source/cell/rich_text.cpp +++ b/source/cell/rich_text.cpp @@ -45,6 +45,18 @@ rich_text::rich_text(const std::string &plain_text, const class font &text_font) { } +rich_text::rich_text(const rich_text &other) +{ + *this = other; +} + +rich_text &rich_text::operator=(const rich_text &rhs) +{ + runs_.clear(); + runs_ = rhs.runs_; + return *this; +} + rich_text::rich_text(const rich_text_run &single_run) { add_run(single_run); @@ -63,6 +75,11 @@ void rich_text::plain_text(const std::string &s, bool preserve_space = false) std::string rich_text::plain_text() const { + if (runs_.size() == 1) + { + return runs_.begin()->first; + } + return std::accumulate(runs_.begin(), runs_.end(), std::string(), [](const std::string &a, const rich_text_run &run) { return a + run.first; }); } diff --git a/source/detail/header_footer/header_footer_code.cpp b/source/detail/header_footer/header_footer_code.cpp index d598ff26..f97ef659 100644 --- a/source/detail/header_footer/header_footer_code.cpp +++ b/source/detail/header_footer/header_footer_code.cpp @@ -547,7 +547,6 @@ std::string encode_header_footer(const rich_text &t, header_footer::location whe case font::underline_style::double_accounting: encoded.append("&E"); break; - default: case font::underline_style::none: break; } diff --git a/source/detail/implementations/workbook_impl.hpp b/source/detail/implementations/workbook_impl.hpp index 38d099cb..6edd4332 100644 --- a/source/detail/implementations/workbook_impl.hpp +++ b/source/detail/implementations/workbook_impl.hpp @@ -47,14 +47,15 @@ struct worksheet_impl; struct workbook_impl { - workbook_impl() : base_date_(calendar::windows_1900) - { - } + workbook_impl() : base_date_(calendar::windows_1900) + { + } workbook_impl(const workbook_impl &other) : active_sheet_index_(other.active_sheet_index_), worksheets_(other.worksheets_), - shared_strings_(other.shared_strings_), + shared_strings_ids_(other.shared_strings_ids_), + shared_strings_values_(other.shared_strings_values_), stylesheet_(other.stylesheet_), manifest_(other.manifest_), theme_(other.theme_), @@ -72,15 +73,15 @@ struct workbook_impl active_sheet_index_ = other.active_sheet_index_; worksheets_.clear(); std::copy(other.worksheets_.begin(), other.worksheets_.end(), back_inserter(worksheets_)); - shared_strings_.clear(); - std::copy(other.shared_strings_.begin(), other.shared_strings_.end(), std::back_inserter(shared_strings_)); - theme_ = other.theme_; + shared_strings_ids_ = other.shared_strings_ids_; + shared_strings_values_ = other.shared_strings_values_; + theme_ = other.theme_; manifest_ = other.manifest_; - sheet_title_rel_id_map_ = other.sheet_title_rel_id_map_; - view_ = other.view_; - code_name_ = other.code_name_; - file_version_ = other.file_version_; + sheet_title_rel_id_map_ = other.sheet_title_rel_id_map_; + view_ = other.view_; + code_name_ = other.code_name_; + file_version_ = other.file_version_; core_properties_ = other.core_properties_; extended_properties_ = other.extended_properties_; @@ -116,13 +117,14 @@ struct workbook_impl optional active_sheet_index_; std::list worksheets_; - std::vector shared_strings_; + std::unordered_map shared_strings_ids_; + std::unordered_map shared_strings_values_; optional stylesheet_; calendar base_date_; optional title_; - + manifest manifest_; optional theme_; std::unordered_map> images_; @@ -131,11 +133,12 @@ struct workbook_impl std::vector> extended_properties_; std::vector> custom_properties_; - std::unordered_map sheet_title_rel_id_map_; + std::unordered_map sheet_title_rel_id_map_; optional view_; optional code_name_; +<<<<<<< Updated upstream struct file_version_t { std::string app_name; @@ -152,6 +155,16 @@ struct workbook_impl } }; +======= + struct file_version_t + { + std::string app_name; + std::size_t last_edited; + std::size_t lowest_edited; + std::size_t rup_build; + }; + +>>>>>>> Stashed changes optional file_version_; optional calculation_properties_; optional abs_path_; diff --git a/source/detail/serialization/xlsx_consumer.cpp b/source/detail/serialization/xlsx_consumer.cpp index a634bf7b..91dfc91b 100644 --- a/source/detail/serialization/xlsx_consumer.cpp +++ b/source/detail/serialization/xlsx_consumer.cpp @@ -246,7 +246,11 @@ cell xlsx_consumer::read_cell() if (parser().attribute_present("s")) { +<<<<<<< Updated upstream cell.format(target_.format(static_cast(std::stoull(parser().attribute("s"))))); +======= + cell.format(target_.format(std::stoull(parser().attribute("s")))); +>>>>>>> Stashed changes } auto has_value = false; @@ -536,12 +540,15 @@ std::string xlsx_consumer::read_worksheet_begin(const std::string &rel_id) current_selection.active_cell(parser().attribute("activeCell")); } +<<<<<<< Updated upstream if (parser().attribute_present("sqref")) { const auto sqref = range_reference(parser().attribute("sqref")); current_selection.sqref(sqref); } +======= +>>>>>>> Stashed changes current_selection.pane(pane_corner::top_left); new_view.add_selection(current_selection); @@ -727,7 +734,11 @@ void xlsx_consumer::read_worksheet_sheetdata() if (parser().attribute_present("s")) { +<<<<<<< Updated upstream cell.format(target_.format(static_cast(std::stoull(parser().attribute("s"))))); +======= + cell.format(target_.format(std::stoull(parser().attribute("s")))); +>>>>>>> Stashed changes } auto has_value = false; @@ -1021,14 +1032,14 @@ worksheet xlsx_consumer::read_worksheet_end(const std::string &rel_id) { header_footer hf; - hf.align_with_margins( - !parser().attribute_present("alignWithMargins") || is_true(parser().attribute("alignWithMargins"))); - hf.scale_with_doc( - !parser().attribute_present("alignWithMargins") || is_true(parser().attribute("alignWithMargins"))); - auto different_odd_even = - parser().attribute_present("differentOddEven") && is_true(parser().attribute("differentOddEven")); - auto different_first = - parser().attribute_present("differentFirst") && is_true(parser().attribute("differentFirst")); + hf.align_with_margins(!parser().attribute_present("alignWithMargins") + || is_true(parser().attribute("alignWithMargins"))); + hf.scale_with_doc(!parser().attribute_present("alignWithMargins") + || is_true(parser().attribute("alignWithMargins"))); + auto different_odd_even = parser().attribute_present("differentOddEven") + && is_true(parser().attribute("differentOddEven")); + auto different_first = parser().attribute_present("differentFirst") + && is_true(parser().attribute("differentFirst")); optional, 3>> odd_header; optional, 3>> odd_footer; @@ -1082,13 +1093,17 @@ worksheet xlsx_consumer::read_worksheet_end(const std::string &rel_id) if (different_odd_even) { - if (odd_header.is_set() && odd_header.get().at(i).is_set() && even_header.is_set() + if (odd_header.is_set() + && odd_header.get().at(i).is_set() + && even_header.is_set() && even_header.get().at(i).is_set()) { hf.odd_even_header(loc, odd_header.get().at(i).get(), even_header.get().at(i).get()); } - if (odd_footer.is_set() && odd_footer.get().at(i).is_set() && even_footer.is_set() + if (odd_footer.is_set() + && odd_footer.get().at(i).is_set() + && even_footer.is_set() && even_footer.get().at(i).is_set()) { hf.odd_even_footer(loc, odd_footer.get().at(i).get(), even_footer.get().at(i).get()); @@ -1915,18 +1930,17 @@ void xlsx_consumer::read_shared_string_table() unique_count = parser().attribute("uniqueCount"); } - auto &strings = target_.shared_strings(); - while (in_element(qn("spreadsheetml", "sst"))) { expect_start_element(qn("spreadsheetml", "si"), xml::content::complex); - strings.push_back(read_rich_text(qn("spreadsheetml", "si"))); + auto rt = read_rich_text(qn("spreadsheetml", "si")); + target_.add_shared_string(rt); expect_end_element(qn("spreadsheetml", "si")); } expect_end_element(qn("spreadsheetml", "sst")); - if (has_unique_count && unique_count != strings.size()) + if (has_unique_count && unique_count != target_.shared_strings().size()) { throw invalid_file("sizes don't match"); } diff --git a/source/detail/serialization/xlsx_producer.cpp b/source/detail/serialization/xlsx_producer.cpp index 5d854d7d..ec0d890e 100644 --- a/source/detail/serialization/xlsx_producer.cpp +++ b/source/detail/serialization/xlsx_producer.cpp @@ -850,15 +850,17 @@ void xlsx_producer::write_shared_string_table(const relationship & /*rel*/) #pragma clang diagnostic pop write_attribute("count", string_count); - write_attribute("uniqueCount", source_.shared_strings().size()); + write_attribute("uniqueCount", source_.shared_strings_by_id().size()); - for (const auto &string : source_.shared_strings()) + for (const auto &string : source_.shared_strings_by_id()) { - if (string.runs().size() == 1 && !string.runs().at(0).second.is_set()) + if (string.second.runs().size() == 1 && !string.second.runs().at(0).second.is_set()) { write_start_element(xmlns, "si"); write_start_element(xmlns, "t"); - write_characters(string.plain_text(), string.runs().front().preserve_space); + + write_characters(string.second.plain_text(), string.second.runs().front().preserve_space); + write_end_element(xmlns, "t"); write_end_element(xmlns, "si"); @@ -867,7 +869,7 @@ void xlsx_producer::write_shared_string_table(const relationship & /*rel*/) write_start_element(xmlns, "si"); - for (const auto &run : string.runs()) + for (const auto &run : string.second.runs()) { write_start_element(xmlns, "r"); @@ -2164,7 +2166,7 @@ void xlsx_producer::write_worksheet(const relationship &rel) })->first; auto ws = source_.sheet_by_title(title); - + write_start_element(xmlns, "worksheet"); write_namespace(xmlns, ""); write_namespace(xmlns_r, "r"); @@ -2372,7 +2374,7 @@ void xlsx_producer::write_worksheet(const relationship &rel) for (auto column = first_column; column <= last_column; column++) { if (!ws.has_column_properties(column)) continue; - + if(!has_column_properties) { write_start_element(xmlns, "cols"); diff --git a/source/styles/color.cpp b/source/styles/color.cpp index 01561eec..141616fc 100644 --- a/source/styles/color.cpp +++ b/source/styles/color.cpp @@ -32,14 +32,14 @@ namespace { std::array decode_hex_string(const std::string &hex_string) { - auto x = std::strtoul(hex_string.c_str(), nullptr, 16); + auto x = std::strtoul(hex_string.c_str(), nullptr, 16); - auto a = static_cast(x >> 24); - auto r = static_cast((x >> 16) & 0xff); - auto g = static_cast((x >> 8) & 0xff); - auto b = static_cast(x & 0xff); + auto a = static_cast(x >> 24); + auto r = static_cast((x >> 16) & 0xff); + auto g = static_cast((x >> 8) & 0xff); + auto b = static_cast(x & 0xff); - return { { r, g, b, a } }; + return { { r, g, b, a } }; } } // namespace @@ -54,7 +54,7 @@ indexed_color::indexed_color(std::size_t index) : index_(index) std::size_t indexed_color::index() const { - return index_; + return index_; } void indexed_color::index(std::size_t index) @@ -70,7 +70,7 @@ theme_color::theme_color(std::size_t index) : index_(index) std::size_t theme_color::index() const { - return index_; + return index_; } void theme_color::index(std::size_t index) @@ -110,84 +110,84 @@ rgb_color::rgb_color(std::uint8_t r, std::uint8_t g, std::uint8_t b, std::uint8_ std::uint8_t rgb_color::red() const { - return rgba_[0]; + return rgba_[0]; } std::uint8_t rgb_color::green() const { - return rgba_[1]; + return rgba_[1]; } std::uint8_t rgb_color::blue() const { - return rgba_[2]; + return rgba_[2]; } std::uint8_t rgb_color::alpha() const { - return rgba_[3]; + return rgba_[3]; } std::array rgb_color::rgb() const { - return {{red(), green(), blue()}}; + return {{red(), green(), blue()}}; } std::array rgb_color::rgba() const { - return rgba_; + return rgba_; } // color implementation const color color::black() { - return color(rgb_color("ff000000")); + return color(rgb_color("ff000000")); } const color color::white() { - return color(rgb_color("ffffffff")); + return color(rgb_color("ffffffff")); } const color color::red() { - return color(rgb_color("ffff0000")); + return color(rgb_color("ffff0000")); } const color color::darkred() { - return color(rgb_color("ff8b0000")); + return color(rgb_color("ff8b0000")); } const color color::blue() { - return color(rgb_color("ff0000ff")); + return color(rgb_color("ff0000ff")); } const color color::darkblue() { - return color(rgb_color("ff00008b")); + return color(rgb_color("ff00008b")); } const color color::green() { - return color(rgb_color("ff00ff00")); + return color(rgb_color("ff00ff00")); } const color color::darkgreen() { - return color(rgb_color("ff008b00")); + return color(rgb_color("ff008b00")); } const color color::yellow() { - return color(rgb_color("ffffff00")); + return color(rgb_color("ffffff00")); } const color color::darkyellow() { - return color(rgb_color("ffcccc00")); + return color(rgb_color("ffcccc00")); } color::color() : color(indexed_color(0)) @@ -195,48 +195,48 @@ color::color() : color(indexed_color(0)) } color::color(const rgb_color &rgb) - : type_(color_type::rgb), - rgb_(rgb), - indexed_(0), - theme_(0) + : type_(color_type::rgb), + rgb_(rgb), + indexed_(0), + theme_(0) { } color::color(const indexed_color &indexed) - : type_(color_type::indexed), - rgb_(rgb_color(0, 0, 0, 0)), - indexed_(indexed), - theme_(0) + : type_(color_type::indexed), + rgb_(rgb_color(0, 0, 0, 0)), + indexed_(indexed), + theme_(0) { } color::color(const theme_color &theme) - : type_(color_type::theme), - rgb_(rgb_color(0, 0, 0, 0)), - indexed_(0), - theme_(theme) + : type_(color_type::theme), + rgb_(rgb_color(0, 0, 0, 0)), + indexed_(0), + theme_(theme) { } color_type color::type() const { - return type_; + return type_; } bool color::auto_() const { - return auto__; + return auto__; } void color::auto_(bool value) { - auto__ = value; + auto__ = value; } const indexed_color& color::indexed() const { - assert_type(color_type::indexed); - return indexed_; + assert_type(color_type::indexed); + return indexed_; } indexed_color &color::indexed() @@ -247,8 +247,8 @@ indexed_color &color::indexed() const theme_color& color::theme() const { - assert_type(color_type::theme); - return theme_; + assert_type(color_type::theme); + return theme_; } theme_color &color::theme() @@ -276,7 +276,7 @@ void color::tint(double tint) double color::tint() const { - return tint_; + return tint_; } void color::assert_type(color_type t) const @@ -309,7 +309,7 @@ bool color::operator==(const xlnt::color &other) const bool color::operator!=(const color &other) const { - return !(*this == other); + return !(*this == other); } } // namespace xlnt diff --git a/source/utils/path.cpp b/source/utils/path.cpp index 34c36cf0..738d71f9 100644 --- a/source/utils/path.cpp +++ b/source/utils/path.cpp @@ -23,6 +23,8 @@ #include #include +#include +#include #include #ifdef __APPLE__ @@ -137,8 +139,8 @@ path::path() } path::path(const std::string &path_string) - : internal_(path_string) { + std::remove_copy(path_string.begin(), path_string.end(), std::back_inserter(internal_), '\"'); } path::path(const std::string &path_string, char sep) diff --git a/source/workbook/workbook.cpp b/source/workbook/workbook.cpp index c4739f03..6c621cc6 100644 --- a/source/workbook/workbook.cpp +++ b/source/workbook/workbook.cpp @@ -473,11 +473,15 @@ workbook workbook::empty() .color(theme_color(1)); stylesheet.fonts.push_back(default_font); +<<<<<<< Updated upstream wb.create_builtin_style(0) .border(default_border) .fill(default_fill) .font(default_font) .number_format(xlnt::number_format::general()); +======= + wb.create_builtin_style(0); +>>>>>>> Stashed changes wb.create_format(true) .border(default_border) @@ -1017,8 +1021,8 @@ void workbook::load(const std::wstring &filename, const std::string &password) void workbook::remove_sheet(worksheet ws) { - auto match_iter = std::find_if( - d_->worksheets_.begin(), d_->worksheets_.end(), [=](detail::worksheet_impl &comp) { return &comp == ws.d_; }); + auto match_iter = std::find_if(d_->worksheets_.begin(), d_->worksheets_.end(), + [=](detail::worksheet_impl &comp) { return &comp == ws.d_; }); if (match_iter == d_->worksheets_.end()) { @@ -1071,10 +1075,10 @@ worksheet workbook::create_sheet_with_rel(const std::string &title, const relati auto workbook_rel = d_->manifest_.relationship(path("/"), relationship_type::office_document); auto sheet_absoulute_path = workbook_rel.target().path().parent().append(rel.target().path()); - d_->manifest_.register_override_type( - sheet_absoulute_path, "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"); - auto ws_rel = d_->manifest_.register_relationship( - workbook_rel.target(), relationship_type::worksheet, rel.target(), target_mode::internal); + d_->manifest_.register_override_type(sheet_absoulute_path, + "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"); + auto ws_rel = d_->manifest_.register_relationship(workbook_rel.target(), + relationship_type::worksheet, rel.target(), target_mode::internal); d_->sheet_title_rel_id_map_[title] = ws_rel; update_sheet_properties(); @@ -1332,39 +1336,53 @@ const manifest &workbook::manifest() const return d_->manifest_; } -std::vector &workbook::shared_strings() +const std::unordered_map &workbook::shared_strings_by_id() const { - return d_->shared_strings_; + return d_->shared_strings_values_; } -const std::vector &workbook::shared_strings() const +const rich_text& workbook::shared_strings(std::size_t index) const { - return d_->shared_strings_; + auto it = d_->shared_strings_values_.find(index); + + if (it != d_->shared_strings_values_.end()) + { + return it->second; + } + + static rich_text empty; + return empty; +} + +std::unordered_map &workbook::shared_strings() +{ + return d_->shared_strings_ids_; +} + +const std::unordered_map &workbook::shared_strings() const +{ + return d_->shared_strings_ids_; } std::size_t workbook::add_shared_string(const rich_text &shared, bool allow_duplicates) { register_workbook_part(relationship_type::shared_string_table); - auto index = std::size_t(0); - if (!allow_duplicates) { - // TODO: inefficient, use a set or something? - for (auto &s : d_->shared_strings_) - { - if (s == shared) - { - return index; - } + auto it = d_->shared_strings_ids_.find(shared); - ++index; + if (it != d_->shared_strings_ids_.end()) + { + return it->second; } } - d_->shared_strings_.push_back(shared); + auto sz = d_->shared_strings_ids_.size(); + d_->shared_strings_ids_[shared] = sz; + d_->shared_strings_values_[sz] = shared; - return index; + return sz; } bool workbook::contains(const std::string &sheet_title) const diff --git a/tests/workbook/serialization_test_suite.cpp b/tests/workbook/serialization_test_suite.cpp index 744103ee..708ac5a2 100644 --- a/tests/workbook/serialization_test_suite.cpp +++ b/tests/workbook/serialization_test_suite.cpp @@ -110,6 +110,7 @@ public: const auto path = path_helper::test_file("3_default.xlsx"); xlnt_assert(workbook_matches_file(wb, path)); } +<<<<<<< Updated upstream:tests/workbook/serialization_test_suite.cpp void test_produce_simple_excel() { @@ -245,6 +246,103 @@ public: sheet1.cell("A5").hyperlink("https://google.com/"); sheet1.cell("A5").format(hyperlink_format); +======= + + void test_produce_simple_excel() + { + xlnt::workbook wb; + auto ws = wb.active_sheet(); + + auto bold_font = xlnt::font().bold(true); + + ws.cell("A1").value("Type"); + ws.cell("A1").font(bold_font); + + ws.cell("B1").value("Value"); + ws.cell("B1").font(bold_font); + + ws.cell("A2").value("null"); + ws.cell("B2").value(nullptr); + + ws.cell("A3").value("bool (true)"); + ws.cell("B3").value(true); + + ws.cell("A4").value("bool (false)"); + ws.cell("B4").value(false); + + ws.cell("A5").value("number (int)"); + ws.cell("B5").value(std::numeric_limits::max()); + + ws.cell("A5").value("number (unsigned int)"); + ws.cell("B5").value(std::numeric_limits::max()); + + ws.cell("A6").value("number (long long int)"); + ws.cell("B6").value(std::numeric_limits::max()); + + ws.cell("A6").value("number (unsigned long long int)"); + ws.cell("B6").value(std::numeric_limits::max()); + + ws.cell("A13").value("number (float)"); + ws.cell("B13").value(std::numeric_limits::max()); + + ws.cell("A14").value("number (double)"); + ws.cell("B14").value(std::numeric_limits::max()); + + ws.cell("A16").value("text (char *)"); + ws.cell("B16").value("string"); + + ws.cell("A17").value("text (std::string)"); + ws.cell("B17").value(std::string("string")); + + ws.cell("A18").value("date"); + ws.cell("B18").value(xlnt::date(2016, 2, 3)); + + ws.cell("A19").value("time"); + ws.cell("B19").value(xlnt::time(1, 2, 3, 4)); + + ws.cell("A20").value("datetime"); + ws.cell("B20").value(xlnt::datetime(2016, 2, 3, 1, 2, 3, 4)); + + ws.cell("A21").value("timedelta"); + ws.cell("B21").value(xlnt::timedelta(1, 2, 3, 4, 5)); + + ws.freeze_panes("B2"); + + std::vector temp_buffer; + wb.save(temp_buffer); + xlnt_assert(!temp_buffer.empty()); + } + + void test_save_after_sheet_deletion() + { + xlnt::workbook workbook; + + xlnt_assert_equals(workbook.sheet_titles().size(), 1); + + auto sheet = workbook.create_sheet(); + sheet.title("XXX1"); + xlnt_assert_equals(workbook.sheet_titles().size(), 2); + + workbook.remove_sheet(workbook.sheet_by_title("XXX1")); + xlnt_assert_equals(workbook.sheet_titles().size(), 1); + + std::vector temp_buffer; + xlnt_assert_throws_nothing(workbook.save(temp_buffer)); + xlnt_assert(!temp_buffer.empty()); + } + + void test_write_comments_hyperlinks_formulae() + { + xlnt::workbook wb; + auto sheet1 = wb.active_sheet(); + auto comment_font = xlnt::font().bold(true).size(10).color(xlnt::indexed_color(81)).name("Calibri"); + + sheet1.cell("A1").value("Sheet1!A1"); + sheet1.cell("A1").comment("Sheet1 comment", comment_font, "Microsoft Office User"); + + sheet1.cell("A2").value("Sheet1!A2"); + sheet1.cell("A2").comment("Sheet1 comment2", comment_font, "Microsoft Office User"); +>>>>>>> Stashed changes:tests/workbook/serialization_test_suite.hpp sheet1.cell("A6").hyperlink(sheet1.cell("A1")); sheet1.cell("A6").format(hyperlink_format); @@ -257,6 +355,7 @@ public: sheet1.cell("C2").value("a"); sheet1.cell("C3").value("b"); +<<<<<<< Updated upstream:tests/workbook/serialization_test_suite.cpp for (auto i = 1; i <= 7; ++i) { sheet1.row_properties(i).dy_descent = 0.2; @@ -270,6 +369,11 @@ public: // comments sheet2.cell("A1").value("Sheet2!A1"); sheet2.cell("A1").comment("Sheet2 comment", comment_font, "Microsoft Office User"); +======= + auto sheet2 = wb.create_sheet(); + sheet2.cell("A1").value("Sheet2!A1"); + sheet2.cell("A2").comment("Sheet2 comment", comment_font, "Microsoft Office User"); +>>>>>>> Stashed changes:tests/workbook/serialization_test_suite.hpp sheet2.cell("A2").value("Sheet2!A2"); sheet2.cell("A2").comment("Sheet2 comment2", comment_font, "Microsoft Office User"); @@ -360,11 +464,17 @@ public: { #ifdef _MSC_VER xlnt::workbook wb; +<<<<<<< Updated upstream:tests/workbook/serialization_test_suite.cpp // L"/9_unicode_Λ.xlsx" doesn't use wchar_t(0x039B) for the capital lambda... // L"/9_unicode_\u039B.xlsx" gives the corrct output const auto path = LSTRING_LITERAL(XLNT_TEST_DATA_DIR) L"/9_unicode_\u039B.xlsx"; // L"/9_unicode_Λ.xlsx" wb.load(path); xlnt_assert_equals(wb.active_sheet().cell("A1").value(), u8"un\u00EFc\u00F4d\u0117!"); // u8"unïcôdė!" +======= + std::wstring path = LSTRING_LITERAL(XLNT_TEST_DATA_DIR) L"/9_unicode_Λ.xlsx"; + wb.load(path_normalized); + xlnt_assert_equals(wb.active_sheet().cell("A1").value(), u8"unicodê!"); +>>>>>>> Stashed changes:tests/workbook/serialization_test_suite.hpp #endif #ifndef __MINGW32__ @@ -560,8 +670,13 @@ public: ws.column_properties("E").width = width; ws.column_properties("E").custom_width = true; +<<<<<<< Updated upstream:tests/workbook/serialization_test_suite.cpp xlnt_assert(workbook_matches_file(wb, path_helper::test_file("13_custom_heights_widths.xlsx"))); +======= + wb.save("temp.xlsx"); + xlnt_assert(workbook_matches_file(wb, path_helper::test_file("13_custom_heights_widths.xlsx"))); +>>>>>>> Stashed changes:tests/workbook/serialization_test_suite.hpp } /// @@ -709,4 +824,4 @@ public: c3.value("C3!"); } }; -static serialization_test_suite x; \ No newline at end of file +static serialization_test_suite x;