summaryrefslogtreecommitdiffstats
path: root/src/orcus_test_gnumeric.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:48:59 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:48:59 +0000
commitc484829272cd13a738e35412498e12f2c9a194ac (patch)
treea1f5ec09629ee895bd3963fa8820b45f2f4c574b /src/orcus_test_gnumeric.cpp
parentInitial commit. (diff)
downloadliborcus-c484829272cd13a738e35412498e12f2c9a194ac.tar.xz
liborcus-c484829272cd13a738e35412498e12f2c9a194ac.zip
Adding upstream version 0.19.2.upstream/0.19.2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/orcus_test_gnumeric.cpp')
-rw-r--r--src/orcus_test_gnumeric.cpp1386
1 files changed, 1386 insertions, 0 deletions
diff --git a/src/orcus_test_gnumeric.cpp b/src/orcus_test_gnumeric.cpp
new file mode 100644
index 0000000..f2ba3b6
--- /dev/null
+++ b/src/orcus_test_gnumeric.cpp
@@ -0,0 +1,1386 @@
+/* -*- 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_test_global.hpp"
+#include "filesystem_env.hpp"
+
+#include <orcus/orcus_gnumeric.hpp>
+#include <orcus/format_detection.hpp>
+#include <orcus/stream.hpp>
+#include <orcus/spreadsheet/factory.hpp>
+#include <orcus/spreadsheet/document.hpp>
+#include <orcus/spreadsheet/sheet.hpp>
+#include <orcus/spreadsheet/auto_filter.hpp>
+#include <orcus/spreadsheet/shared_strings.hpp>
+#include <orcus/spreadsheet/styles.hpp>
+#include <orcus/types.hpp>
+
+#include <ixion/address.hpp>
+#include <ixion/model_context.hpp>
+#include <iostream>
+#include <sstream>
+
+using namespace orcus;
+namespace ss = orcus::spreadsheet;
+
+namespace {
+
+std::vector<fs::path> dirs = {
+ SRCDIR"/test/gnumeric/raw-values-1/",
+ SRCDIR"/test/gnumeric/cell-value-types/",
+ SRCDIR"/test/gnumeric/formula-cells/",
+ SRCDIR"/test/gnumeric/named-expression/",
+ SRCDIR"/test/gnumeric/named-expression-sheet-local/",
+};
+
+std::unique_ptr<ss::document> load_doc(const fs::path& filepath)
+{
+ ss::range_size_t ss{1048576, 16384};
+ auto doc = std::make_unique<ss::document>(ss);
+ ss::import_factory factory(*doc);
+ orcus_gnumeric app(&factory);
+ app.read_file(filepath.string());
+
+ // Gnumeric doc doesn't cache formula results.
+ doc->recalc_formula_cells();
+
+ return doc;
+}
+
+void test_gnumeric_detection()
+{
+ ORCUS_TEST_FUNC_SCOPE;
+
+ for (const auto& dir : dirs)
+ {
+ fs::path filepath = dir / "input.gnumeric";
+ file_content fc(filepath.string());
+ assert(!fc.empty());
+
+ format_t detected = detect(fc.str());
+ assert(detected == format_t::gnumeric);
+ }
+}
+
+void test_gnumeric_create_filter()
+{
+ ORCUS_TEST_FUNC_SCOPE;
+
+ ss::range_size_t ssize{1048576, 16384};
+ std::unique_ptr<ss::document> doc = std::make_unique<ss::document>(ssize);
+ ss::import_factory factory(*doc);
+
+ auto f = create_filter(format_t::gnumeric, &factory);
+ assert(f);
+ assert(f->get_name() == "gnumeric");
+}
+
+void test_gnumeric_import()
+{
+ ORCUS_TEST_FUNC_SCOPE;
+
+ for (const auto& dir : dirs)
+ {
+ std::cout << "checking " << dir << "..." << std::endl;
+
+ // Read the input.gnumeric document.
+ fs::path filepath = dir / "input.gnumeric";
+ auto doc = load_doc(filepath);
+
+ // Dump the content of the model.
+ std::ostringstream os;
+ doc->dump_check(os);
+ std::string check = os.str();
+
+ // Check that against known control.
+ filepath = dir / "check.txt";
+ file_content control(filepath.string());
+
+ assert(!check.empty());
+ assert(!control.empty());
+
+ test::verify_content(__FILE__, __LINE__, control.str(), check);
+ }
+}
+
+void test_gnumeric_column_widths_row_heights()
+{
+ ORCUS_TEST_FUNC_SCOPE;
+
+ fs::path filepath = SRCDIR"/test/gnumeric/column-width-row-height/input.gnumeric";
+ auto doc = load_doc(filepath);
+
+ assert(doc->get_sheet_count() == 1);
+ const ss::sheet* sh = doc->get_sheet(0);
+ assert(sh);
+
+ {
+ // column, column width (twips), column span
+ const std::tuple<ss::col_t, ss::col_width_t, ss::col_t> expected[] = {
+ { 0, 99 * 20, 1 },
+ { 1, 57 * 20, 1 },
+ { 2, 84 * 20, 1 },
+ { 3, 111 * 20, 1 },
+ { 4, ss::get_default_column_width(), 1 },
+ { 5, 69 * 20, 3 },
+ { 10, 120 * 20, 2 },
+ };
+
+ for (const auto& [col, cw_expected, span_expected] : expected)
+ {
+ ss::col_t col_start, col_end;
+ ss::col_width_t cw = sh->get_col_width(col, &col_start, &col_end);
+ ss::col_t span = col_end - col_start;
+
+ std::cout << "column: " << col << "; expected=" << cw_expected << "; actual=" << cw
+ << "; span-expected=" << span_expected << "; span-actual=" << span << std::endl;
+
+ assert(cw == cw_expected);
+ assert(span == span_expected);
+ }
+ }
+
+ {
+ // row, row height (twips), row span
+ const std::tuple<ss::row_t, ss::row_height_t, ss::row_t> expected[] = {
+ { 0, 18 * 20, 3 },
+ { 3, 30 * 20, 1 },
+ { 4, 42 * 20, 1 },
+ { 5, 51 * 20, 1 },
+ { 6, ss::get_default_row_height(), 1 },
+ { 7, 27 * 20, 3 },
+ { 10, ss::get_default_row_height(), 2 },
+ { 12, 36 * 20, 2 },
+ };
+
+ for (const auto& [row, rh_expected, span_expected] : expected)
+ {
+ ss::row_t row_start, row_end;
+ ss::row_height_t rh = sh->get_row_height(row, &row_start, &row_end);
+ ss::row_t span = row_end - row_start;
+
+ std::cout << "row: " << row << "; expected=" << rh_expected << "; actual=" << rh
+ << "; span-expected=" << span_expected << "; span-actual=" << span << std::endl;
+
+ assert(rh == rh_expected);
+ assert(span == span_expected);
+ }
+ }
+}
+
+void test_gnumeric_auto_filter()
+{
+ ORCUS_TEST_FUNC_SCOPE;
+
+ fs::path filepath = SRCDIR"/test/gnumeric/table/autofilter.gnumeric";
+ auto doc = load_doc(filepath);
+
+ assert(doc->get_sheet_count() == 1);
+ const ss::sheet* sh = doc->get_sheet(0);
+ assert(sh);
+
+ const ss::auto_filter_t* af = sh->get_auto_filter_data();
+ assert(af);
+ ixion::abs_range_t b2_c11{0, 1, 1, 10, 2};
+ assert(af->range == b2_c11);
+ assert(af->columns.size() == 2);
+
+ auto it = af->columns.begin();
+ assert(it->first == 0);
+ {
+ const ss::auto_filter_column_t& afc = it->second;
+ assert(afc.match_values.size() == 1);
+ assert(*afc.match_values.begin() == "A");
+ }
+
+ ++it;
+ assert(it->first == 1);
+ {
+ const ss::auto_filter_column_t& afc = it->second;
+ assert(afc.match_values.size() == 1);
+ assert(*afc.match_values.begin() == "1");
+ }
+}
+
+void test_gnumeric_hidden_rows_columns()
+{
+ ORCUS_TEST_FUNC_SCOPE;
+
+ fs::path filepath = SRCDIR"/test/gnumeric/hidden-rows-columns/input.gnumeric";
+ auto doc = load_doc(filepath);
+
+ ss::sheet* sh = doc->get_sheet("Hidden Rows");
+ assert(sh);
+
+ ss::row_t row_start = -1, row_end = -1;
+
+ // Row 1 is visible.
+ assert(!sh->is_row_hidden(0, &row_start, &row_end));
+ assert(row_start == 0);
+ assert(row_end == 1); // the end position is non-inclusive.
+
+ // Rows 2-3 are hidden.
+ assert(sh->is_row_hidden(1, &row_start, &row_end));
+ assert(row_start == 1);
+ assert(row_end == 3); // the end position is non-inclusive.
+
+ // Row 4 is visible.
+ assert(!sh->is_row_hidden(3, &row_start, &row_end));
+ assert(row_start == 3);
+ assert(row_end == 4); // the end position is non-inclusive.
+
+ // Row 5 is hidden.
+ assert(sh->is_row_hidden(4, &row_start, &row_end));
+ assert(row_start == 4);
+ assert(row_end == 5); // the end position is non-inclusive.
+
+ // Rows 6-8 are visible.
+ assert(!sh->is_row_hidden(5, &row_start, &row_end));
+ assert(row_start == 5);
+ assert(row_end == 8); // the end position is non-inclusive.
+
+ // Row 9 is hidden.
+ assert(sh->is_row_hidden(8, &row_start, &row_end));
+ assert(row_start == 8);
+ assert(row_end == 9); // the end position is non-inclusive.
+
+ // The rest of the rows are visible.
+ assert(!sh->is_row_hidden(9, &row_start, &row_end));
+ assert(row_start == 9);
+ assert(row_end == doc->get_sheet_size().rows); // the end position is non-inclusive.
+
+ sh = doc->get_sheet("Hidden Columns");
+ assert(sh);
+
+ ss::col_t col_start = -1, col_end = -1;
+
+ // Columns A-B are visible.
+ assert(!sh->is_col_hidden(0, &col_start, &col_end));
+ assert(col_start == 0);
+ assert(col_end == 2); // non-inclusive
+
+ // Columns C-E are hidden.
+ assert(sh->is_col_hidden(2, &col_start, &col_end));
+ assert(col_start == 2);
+ assert(col_end == 6); // non-inclusive
+
+ // Columns G-J are visible.
+ assert(!sh->is_col_hidden(6, &col_start, &col_end));
+ assert(col_start == 6);
+ assert(col_end == 10); // non-inclusive
+
+ // Column K is hidden.
+ assert(sh->is_col_hidden(10, &col_start, &col_end));
+ assert(col_start == 10);
+ assert(col_end == 11); // non-inclusive
+
+ // The rest of the columns are all visible.
+ assert(!sh->is_col_hidden(11, &col_start, &col_end));
+ assert(col_start == 11);
+ assert(col_end == doc->get_sheet_size().columns); // non-inclusive
+}
+
+void test_gnumeric_merged_cells()
+{
+ ORCUS_TEST_FUNC_SCOPE;
+
+ fs::path filepath = SRCDIR"/test/gnumeric/merged-cells/input.gnumeric";
+ auto doc = load_doc(filepath);
+
+ const ss::sheet* sheet1 = doc->get_sheet("Sheet1");
+ assert(sheet1);
+
+ ss::range_t merge_range = sheet1->get_merge_cell_range(0, 1);
+ assert(merge_range.first.column == 1);
+ assert(merge_range.last.column == 2);
+ assert(merge_range.first.row == 0);
+ assert(merge_range.last.row == 0);
+
+ merge_range = sheet1->get_merge_cell_range(0, 3);
+ assert(merge_range.first.column == 3);
+ assert(merge_range.last.column == 5);
+ assert(merge_range.first.row == 0);
+ assert(merge_range.last.row == 0);
+
+ merge_range = sheet1->get_merge_cell_range(1, 0);
+ assert(merge_range.first.column == 0);
+ assert(merge_range.last.column == 0);
+ assert(merge_range.first.row == 1);
+ assert(merge_range.last.row == 2);
+
+ merge_range = sheet1->get_merge_cell_range(3, 0);
+ assert(merge_range.first.column == 0);
+ assert(merge_range.last.column == 0);
+ assert(merge_range.first.row == 3);
+ assert(merge_range.last.row == 5);
+
+ merge_range = sheet1->get_merge_cell_range(2, 2);
+ assert(merge_range.first.column == 2);
+ assert(merge_range.last.column == 5);
+ assert(merge_range.first.row == 2);
+ assert(merge_range.last.row == 5);
+}
+
+void test_gnumeric_text_alignment()
+{
+ ORCUS_TEST_FUNC_SCOPE;
+
+ fs::path filepath = SRCDIR"/test/gnumeric/text-alignment/input.gnumeric";
+ auto doc = load_doc(filepath);
+
+ ss::styles& styles = doc->get_styles();
+
+ ss::sheet* sh = doc->get_sheet(0);
+ assert(sh);
+
+ struct check
+ {
+ ss::row_t row;
+ ss::col_t col;
+ bool apply_align;
+ ss::hor_alignment_t hor_align;
+ ss::ver_alignment_t ver_align;
+ };
+
+ std::vector<check> checks =
+ {
+ { 1, 2, true, ss::hor_alignment_t::unknown, ss::ver_alignment_t::bottom }, // C2
+ { 2, 2, true, ss::hor_alignment_t::left, ss::ver_alignment_t::bottom }, // C3
+ { 3, 2, true, ss::hor_alignment_t::center, ss::ver_alignment_t::bottom }, // C4
+ { 4, 2, true, ss::hor_alignment_t::right, ss::ver_alignment_t::bottom }, // C5
+ { 5, 2, true, ss::hor_alignment_t::left, ss::ver_alignment_t::bottom }, // C6
+ { 6, 2, true, ss::hor_alignment_t::left, ss::ver_alignment_t::bottom }, // C7
+ { 7, 2, true, ss::hor_alignment_t::right, ss::ver_alignment_t::bottom }, // C8
+ { 8, 2, true, ss::hor_alignment_t::right, ss::ver_alignment_t::bottom }, // C9
+ { 9, 2, true, ss::hor_alignment_t::unknown, ss::ver_alignment_t::middle }, // C10
+ { 10, 2, true, ss::hor_alignment_t::left, ss::ver_alignment_t::middle }, // C11
+ { 11, 2, true, ss::hor_alignment_t::center, ss::ver_alignment_t::middle }, // C12
+ { 12, 2, true, ss::hor_alignment_t::right, ss::ver_alignment_t::middle }, // C13
+ { 13, 2, true, ss::hor_alignment_t::left, ss::ver_alignment_t::middle }, // C14
+ { 14, 2, true, ss::hor_alignment_t::left, ss::ver_alignment_t::middle }, // C15
+ { 15, 2, true, ss::hor_alignment_t::right, ss::ver_alignment_t::middle }, // C16
+ { 16, 2, true, ss::hor_alignment_t::right, ss::ver_alignment_t::middle }, // C17
+ { 17, 2, true, ss::hor_alignment_t::unknown, ss::ver_alignment_t::top }, // C18
+ { 18, 2, true, ss::hor_alignment_t::left, ss::ver_alignment_t::top }, // C19
+ { 19, 2, true, ss::hor_alignment_t::center, ss::ver_alignment_t::top }, // C20
+ { 20, 2, true, ss::hor_alignment_t::right, ss::ver_alignment_t::top }, // C21
+ { 21, 2, true, ss::hor_alignment_t::left, ss::ver_alignment_t::top }, // C22
+ { 22, 2, true, ss::hor_alignment_t::left, ss::ver_alignment_t::top }, // C23
+ { 23, 2, true, ss::hor_alignment_t::right, ss::ver_alignment_t::top }, // C24
+ { 24, 2, true, ss::hor_alignment_t::right, ss::ver_alignment_t::top }, // C25
+ { 25, 2, true, ss::hor_alignment_t::unknown, ss::ver_alignment_t::justified }, // C26
+ { 26, 2, true, ss::hor_alignment_t::justified, ss::ver_alignment_t::bottom }, // C27
+ { 27, 2, true, ss::hor_alignment_t::distributed, ss::ver_alignment_t::distributed }, // C28
+ };
+
+ for (const check& c : checks)
+ {
+ std::cout << "row=" << c.row << "; col=" << c.col << std::endl;
+ size_t xf = sh->get_cell_format(c.row, c.col);
+
+ const ss::cell_format_t* cf = styles.get_cell_format(xf);
+ assert(cf);
+ assert(c.apply_align == cf->apply_alignment);
+
+ if (!cf->apply_alignment)
+ continue;
+
+ assert(c.hor_align == cf->hor_align);
+ assert(c.ver_align == cf->ver_align);
+ }
+}
+
+void test_gnumeric_cell_properties_wrap_and_shrink()
+{
+ ORCUS_TEST_FUNC_SCOPE;
+
+ // NB : Gnumeric doesn't appear to support shrink-to-fit, so we only check
+ // wrap-text for now. When Gnumeric supports shrink-to-fit, re-generate the
+ // test file from test/xls-xml/cell-properties/wrap-and-shrink.xml.
+
+ fs::path filepath = SRCDIR"/test/gnumeric/cell-properties/wrap-and-shrink.gnumeric";
+ auto doc = load_doc(filepath);
+
+ const ss::styles& styles = doc->get_styles();
+ const ss::sheet* sh = doc->get_sheet(0);
+ assert(sh);
+
+ std::size_t xfid = sh->get_cell_format(0, 1); // B1
+ const ss::cell_format_t* xf = styles.get_cell_format(xfid);
+ assert(xf);
+ assert(xf->wrap_text);
+ assert(!*xf->wrap_text);
+// assert(xf->shrink_to_fit);
+// assert(!*xf->shrink_to_fit);
+
+ xfid = sh->get_cell_format(1, 1); // B2
+ xf = styles.get_cell_format(xfid);
+ assert(xf);
+ assert(xf->wrap_text);
+ assert(*xf->wrap_text);
+// assert(xf->shrink_to_fit);
+// assert(!*xf->shrink_to_fit);
+
+ xfid = sh->get_cell_format(2, 1); // B3
+ xf = styles.get_cell_format(xfid);
+ assert(xf);
+ assert(xf->wrap_text);
+ assert(!*xf->wrap_text);
+// assert(xf->shrink_to_fit);
+// assert(*xf->shrink_to_fit);
+}
+
+void test_gnumeric_background_fill()
+{
+ ORCUS_TEST_FUNC_SCOPE;
+
+ fs::path filepath = SRCDIR"/test/gnumeric/background-color/standard.gnumeric";
+ auto doc = load_doc(filepath);
+
+ ss::styles& styles = doc->get_styles();
+
+ ss::sheet* sh = doc->get_sheet(0);
+ assert(sh);
+
+ struct check
+ {
+ ss::row_t row;
+ ss::col_t col;
+ ss::fill_pattern_t pattern_type;
+ ss::color_t fg_color;
+ };
+
+ std::vector<check> checks =
+ {
+ { 1, 0, ss::fill_pattern_t::solid, { 255, 192, 0, 0 } }, // A2 - dark red
+ { 2, 0, ss::fill_pattern_t::solid, { 255, 255, 0, 0 } }, // A3 - red
+ { 3, 0, ss::fill_pattern_t::solid, { 255, 255, 192, 0 } }, // A4 - orange
+ { 4, 0, ss::fill_pattern_t::solid, { 255, 255, 255, 0 } }, // A5 - yellow
+ { 5, 0, ss::fill_pattern_t::solid, { 255, 146, 208, 80 } }, // A6 - light green
+ { 6, 0, ss::fill_pattern_t::solid, { 255, 0, 176, 80 } }, // A7 - green
+ { 7, 0, ss::fill_pattern_t::solid, { 255, 0, 176, 240 } }, // A8 - light blue
+ { 8, 0, ss::fill_pattern_t::solid, { 255, 0, 112, 192 } }, // A9 - blue
+ { 9, 0, ss::fill_pattern_t::solid, { 255, 0, 32, 96 } }, // A10 - dark blue
+ { 10, 0, ss::fill_pattern_t::solid, { 255, 112, 48, 160 } }, // A11 - purple
+ };
+
+ ss::color_t color_white(255, 255, 255, 255);
+
+ for (const check& c : checks)
+ {
+ size_t xf = sh->get_cell_format(c.row, c.col);
+
+ const ss::cell_format_t* cf = styles.get_cell_format(xf);
+ assert(cf);
+
+ const ss::fill_t* fill_data = styles.get_fill(cf->fill);
+ assert(fill_data);
+ assert(fill_data->pattern_type == c.pattern_type);
+ assert(fill_data->fg_color == c.fg_color);
+
+ // The font colors are all white in the colored cells.
+ const ss::font_t* font_data = styles.get_font(cf->font);
+ assert(font_data);
+
+ assert(font_data->color == color_white);
+ }
+}
+
+void test_gnumeric_colored_text()
+{
+ ORCUS_TEST_FUNC_SCOPE;
+
+ fs::path filepath = SRCDIR"/test/gnumeric/colored-text/input.gnumeric";
+ auto doc = load_doc(filepath);
+
+ const ss::sheet* sheet1 = doc->get_sheet("ColoredText");
+ assert(sheet1);
+
+ const ss::shared_strings& ss = doc->get_shared_strings();
+
+ const ss::styles& styles = doc->get_styles();
+
+ // Column A contains colored cells.
+
+ struct check
+ {
+ ss::row_t row;
+ ss::color_elem_t red;
+ ss::color_elem_t green;
+ ss::color_elem_t blue;
+ std::string text;
+ };
+
+ std::vector<check> checks = {
+ { 1, 0xC0, 0x00, 0x00, "Dark Red" },
+ { 2, 0xFF, 0x00, 0x00, "Red" },
+ { 3, 0xFF, 0xC0, 0x00, "Orange" },
+ { 4, 0xFF, 0xFF, 0x00, "Yellow" },
+ { 5, 0x92, 0xD0, 0x50, "Light Green" },
+ { 6, 0x00, 0xB0, 0x50, "Green" },
+ { 7, 0x00, 0xB0, 0xF0, "Light Blue" },
+ { 8, 0x00, 0x70, 0xC0, "Blue" },
+ { 9, 0x00, 0x20, 0x60, "Dark Blue" },
+ { 10, 0x70, 0x30, 0xA0, "Purple" },
+ };
+
+ for (const check& c : checks)
+ {
+ size_t xfi = sheet1->get_cell_format(c.row, 0);
+ const ss::cell_format_t* xf = styles.get_cell_format(xfi);
+ assert(xf);
+
+ const ss::font_t* font = styles.get_font(xf->font);
+ assert(font);
+ assert(font->color);
+ assert(font->color.value().red == c.red);
+ assert(font->color.value().green == c.green);
+ assert(font->color.value().blue == c.blue);
+
+ size_t si = sheet1->get_string_identifier(c.row, 0);
+ const std::string* s = ss.get_string(si);
+ assert(s);
+ assert(*s == c.text);
+ }
+
+ {
+ // Cell B2 contains mix-colored text.
+ size_t si = sheet1->get_string_identifier(1, 1);
+ const std::string* s = ss.get_string(si);
+ assert(s);
+ assert(*s == "Red and Blue");
+ const spreadsheet::format_runs_t* fmt_runs = ss.get_format_runs(si);
+ assert(fmt_runs);
+
+ // There should be 2 segments that are color-formatted.
+ assert(fmt_runs->size() == 2);
+
+ // The 'Red' segment should be in red color.
+ const spreadsheet::format_run* fmt = &fmt_runs->at(0);
+ assert(fmt->color.alpha == 0xFF);
+ assert(fmt->color.red == 0xFF);
+ assert(fmt->color.green == 0);
+ assert(fmt->color.blue == 0);
+ assert(fmt->pos == 0);
+ assert(fmt->size == 3);
+
+ // The 'Blue' segment should be in blue color.
+ fmt = &fmt_runs->at(1);
+ assert(fmt->color.alpha == 0xFF);
+ assert(fmt->color.red == 0);
+ assert(fmt->color.green == 0x00);
+ assert(fmt->color.blue == 0xFF);
+ assert(fmt->pos == 8);
+ assert(fmt->size == 4);
+ }
+
+ {
+ // Cell B3 too
+ size_t si = sheet1->get_string_identifier(2, 1);
+ const std::string* s = ss.get_string(si);
+ assert(s);
+ assert(*s == "Green and Orange");
+ const spreadsheet::format_runs_t* fmt_runs = ss.get_format_runs(si);
+ assert(fmt_runs);
+
+ assert(fmt_runs->size() == 2);
+
+ // 'Green' segment
+ const spreadsheet::format_run* fmt = &fmt_runs->at(0);
+ assert(fmt->color.alpha == 0xFF);
+ assert(fmt->color.red == 0);
+ assert(fmt->color.green == 0xFF);
+ assert(fmt->color.blue == 0);
+ assert(fmt->pos == 0);
+ assert(fmt->size == 5);
+
+ // 'Orange' segment
+ fmt = &fmt_runs->at(1);
+ assert(fmt->color.alpha == 0xFF);
+ assert(fmt->color.red == 0xFF);
+ assert(fmt->color.green == 0x99);
+ assert(fmt->color.blue == 0);
+ assert(fmt->pos == 10);
+ assert(fmt->size == 6);
+ }
+}
+
+void test_gnumeric_text_formats()
+{
+ ORCUS_TEST_FUNC_SCOPE;
+
+ fs::path filepath = SRCDIR"/test/gnumeric/text-formats/input.gnumeric";
+ auto doc = load_doc(filepath);
+ assert(doc);
+
+ const auto& styles_pool = doc->get_styles();
+
+ auto get_font = [&styles_pool](const ss::sheet& sh, ss::row_t row, ss::col_t col)
+ {
+ std::size_t xf = sh.get_cell_format(row, col);
+
+ const ss::cell_format_t* cell_format = styles_pool.get_cell_format(xf);
+ assert(cell_format);
+
+ const ss::font_t* font = styles_pool.get_font(cell_format->font);
+ assert(font);
+
+ return font;
+ };
+
+ auto check_cell_bold = [&get_font](const ss::sheet& sh, ss::row_t row, ss::col_t col, bool expected)
+ {
+ const ss::font_t* font = get_font(sh, row, col);
+
+ if (expected)
+ {
+ if (font->bold && *font->bold)
+ return true;
+
+ std::cerr << "expected to be bold but it is not "
+ << "(sheet=" << sh.get_index() << "; row=" << row << "; column=" << col << ")"
+ << std::endl;
+
+ return false;
+ }
+ else
+ {
+ if (!font->bold || !*font->bold)
+ return true;
+
+ std::cerr << "expected to be non-bold but it is bold "
+ << "(sheet=" << sh.get_index() << "; row=" << row << "; column=" << col << ")"
+ << std::endl;
+
+ return false;
+ }
+ };
+
+ auto check_cell_italic = [&get_font](const ss::sheet& sh, ss::row_t row, ss::col_t col, bool expected)
+ {
+ const ss::font_t* font = get_font(sh, row, col);
+
+ if (expected)
+ {
+ if (font->italic && *font->italic)
+ return true;
+
+ std::cerr << "expected to be italic but it is not "
+ << "(sheet=" << sh.get_index() << "; row=" << row << "; column=" << col << ")"
+ << std::endl;
+
+ return false;
+ }
+ else
+ {
+ if (!font->italic || !*font->italic)
+ return true;
+
+ std::cerr << "expected to be non-italic but it is italic "
+ << "(sheet=" << sh.get_index() << "; row=" << row << "; column=" << col << ")"
+ << std::endl;
+
+ return false;
+ }
+ };
+
+ auto check_cell_text = [&doc](const ss::sheet& sh, ss::row_t row, ss::col_t col, std::string_view expected)
+ {
+ const auto& sstrings = doc->get_shared_strings();
+
+ std::size_t si = sh.get_string_identifier(row, col);
+ const std::string* s = sstrings.get_string(si);
+ if (!s)
+ {
+ std::cerr << "expected='" << expected << "'; actual=<none> "
+ << "(sheet=" << sh.get_index() << "; row=" << row << "; column=" << col << ")"
+ << std::endl;
+
+ return false;
+ }
+
+ if (*s == expected)
+ return true;
+
+ std::cerr << "expected='" << expected << "'; actual='" << *s << "' "
+ << "(sheet=" << sh.get_index() << "; row=" << row << "; column=" << col << ")"
+ << std::endl;
+
+ return false;
+ };
+
+ const ss::sheet* sheet1 = doc->get_sheet("Text Properties");
+ assert(sheet1);
+
+ ss::row_t row = 0;
+ ss::col_t col = 0;
+
+ // A1 - unformatted
+ assert(check_cell_text(*sheet1, row, col, "Normal Text"));
+ assert(check_cell_bold(*sheet1, row, col, false));
+ assert(check_cell_italic(*sheet1, row, col, false));
+
+ // A2 - bold
+ row = 1;
+ assert(check_cell_text(*sheet1, row, col, "Bold Text"));
+ assert(check_cell_bold(*sheet1, row, col, true));
+ assert(check_cell_italic(*sheet1, row, col, false));
+
+ // A3 - italic
+ row = 2;
+ assert(check_cell_text(*sheet1, row, col, "Italic Text"));
+ assert(check_cell_bold(*sheet1, row, col, false));
+ assert(check_cell_italic(*sheet1, row, col, true));
+
+ // A4 - bold and italic
+ row = 3;
+ assert(check_cell_text(*sheet1, row, col, "Bold and Italic Text"));
+ assert(check_cell_bold(*sheet1, row, col, true));
+ assert(check_cell_italic(*sheet1, row, col, true));
+
+ // A5 - bold and italic mixed - base cell is unformatted and text contains
+ // format runs.
+ row = 4;
+ assert(check_cell_text(*sheet1, row, col, "Bold and Italic mixed"));
+ assert(check_cell_bold(*sheet1, row, col, false));
+ assert(check_cell_italic(*sheet1, row, col, false));
+
+ std::size_t si = sheet1->get_string_identifier(row, col);
+ const ss::format_runs_t* runs = doc->get_shared_strings().get_format_runs(si);
+ assert(runs);
+ assert(runs->size() == 2u);
+
+ // Bold and ...
+ // ^^^^
+ assert(runs->at(0).pos == 0);
+ assert(runs->at(0).size == 4);
+ assert(runs->at(0).bold);
+ assert(!runs->at(0).italic);
+
+ // Bold and Italic
+ // ^^^^^^
+ assert(runs->at(1).pos == 9);
+ assert(runs->at(1).size == 6);
+ assert(!runs->at(1).bold);
+ assert(runs->at(1).italic);
+
+ // A6
+ row = 5;
+ assert(check_cell_text(*sheet1, row, col, "Bold base with non-bold part"));
+ assert(check_cell_bold(*sheet1, row, col, true));
+ assert(check_cell_italic(*sheet1, row, col, false));
+#if 0 // FIXME: see #183
+ si = sheet1->get_string_identifier(row, col);
+ runs = doc->get_shared_strings().get_format_runs(si);
+ assert(runs);
+ assert(runs->size() == 1u);
+#endif
+
+ // Rest of the cells are imported as unformatted for now, until we support
+ // more format properties. See #182.
+ row = 6;
+ assert(check_cell_text(*sheet1, row, col, "Only partially underlined"));
+
+ {
+ row = 7;
+ assert(check_cell_text(*sheet1, row, col, "All Underlined"));
+ const ss::font_t* font = get_font(*sheet1, row, col);
+ assert(font->underline_style);
+ assert(*font->underline_style == ss::underline_t::single_line);
+ }
+
+ {
+ row = 8;
+ assert(check_cell_text(*sheet1, row, col, "Bold and Underlined"));
+ const ss::font_t* font = get_font(*sheet1, row, col);
+ assert(font->underline_style);
+ assert(*font->underline_style == ss::underline_t::single_line);
+ assert(font->bold);
+ assert(*font->bold);
+ }
+
+ {
+ row = 9;
+ assert(check_cell_text(*sheet1, row, col, "All Strikethrough"));
+ const ss::font_t* font = get_font(*sheet1, row, col);
+ assert(font->strikethrough_style);
+ assert(*font->strikethrough_style == ss::strikethrough_style_t::solid);
+
+ assert(font->strikethrough_type);
+ assert(*font->strikethrough_type == ss::strikethrough_type_t::single_type);
+
+ assert(font->strikethrough_width);
+ assert(*font->strikethrough_width == ss::strikethrough_width_t::width_auto);
+ }
+
+ row = 10;
+ assert(check_cell_text(*sheet1, row, col, "Partial strikethrough"));
+ row = 11;
+ assert(check_cell_text(*sheet1, row, col, "Superscript"));
+ row = 12;
+ assert(check_cell_text(*sheet1, row, col, "Subscript"));
+ row = 13;
+ assert(check_cell_text(*sheet1, row, col, "x2 + y2 = 102"));
+ row = 14;
+ assert(check_cell_text(*sheet1, row, col, "xi = yi + zi"));
+
+ {
+ const ss::sheet* sheet2 = doc->get_sheet("Fonts");
+ assert(sheet2);
+
+ struct check
+ {
+ ss::row_t row;
+ std::string_view font_name;
+ double font_unit;
+ };
+
+ check checks[] = {
+ { 0, "Sans", 12.0 },
+ { 1, "FreeSans", 18.0 },
+ { 2, "Serif", 14.0 },
+ { 3, "Monospace", 9.0 },
+ { 4, "DejaVu Sans Mono", 11.0 },
+ };
+
+ for (const auto& c : checks)
+ {
+ std::size_t xf = sheet2->get_cell_format(c.row, 0);
+ const ss::cell_format_t* cell_format = styles_pool.get_cell_format(xf);
+ assert(cell_format);
+ const ss::font_t* font = styles_pool.get_font(cell_format->font);
+ assert(font);
+ assert(font->name == c.font_name);
+ assert(font->size == c.font_unit);
+
+ // Columns A and B should have the same font.
+ xf = sheet2->get_cell_format(c.row, 1);
+ cell_format = styles_pool.get_cell_format(xf);
+ assert(cell_format);
+ font = styles_pool.get_font(cell_format->font);
+ assert(font);
+ assert(font->name == c.font_name);
+ assert(font->size == c.font_unit);
+ }
+ }
+
+ {
+ const ss::sheet* sheet3 = doc->get_sheet("Mixed Fonts");
+ assert(sheet3);
+
+ // A1
+ row = 0;
+ col = 0;
+ assert(check_cell_text(*sheet3, row, col, "C++ has class and struct as keywords."));
+
+ // Base cell has Serif 12-pt font applied
+ auto xf = sheet3->get_cell_format(row, col);
+ const ss::cell_format_t* fmt = styles_pool.get_cell_format(xf);
+ assert(fmt);
+ const ss::font_t* font = styles_pool.get_font(fmt->font);
+ assert(font);
+ assert(font->name == "Serif");
+ assert(font->size == 12.0f);
+
+ // two segments where Monospace font is applied
+ si = sheet3->get_string_identifier(row, col);
+ runs = doc->get_shared_strings().get_format_runs(si);
+ assert(runs);
+ assert(runs->size() == 2u);
+
+ // C++ has class ...
+ // ^^^^^
+ assert(runs->at(0).pos == 8);
+ assert(runs->at(0).size == 5);
+ assert(runs->at(0).font == "Monospace");
+
+ // ... and struct as ...
+ // ^^^^^^
+ assert(runs->at(1).pos == 18);
+ assert(runs->at(1).size == 6);
+ assert(runs->at(1).font == "Monospace");
+
+ // A2
+ row = 1;
+ assert(check_cell_text(*sheet3, row, col, "Text with 12-point font, 24-point font, and 36-point font mixed."));
+ si = sheet3->get_string_identifier(row, col);
+ runs = doc->get_shared_strings().get_format_runs(si);
+ assert(runs);
+ assert(runs->size() == 6u);
+
+ // with 12-point font, ...
+ // ^^
+ assert(runs->at(0).pos == 10);
+ assert(runs->at(0).size == 2);
+ assert(runs->at(0).font_size == 12.0f);
+ assert(runs->at(0).color == ss::color_t(0xFF, 0xFF, 0, 0)); // red
+
+ // with 12-point font, ...
+ // ^^^^^^
+ assert(runs->at(1).pos == 12);
+ assert(runs->at(1).size == 6);
+ assert(runs->at(1).font_size == 12.0f);
+
+ // 24-point font,
+ // ^^
+ assert(runs->at(2).pos == 25);
+ assert(runs->at(2).size == 2);
+ assert(runs->at(2).font_size == 24.0f);
+ assert(runs->at(2).color == ss::color_t(0xFF, 0xFF, 0, 0)); // red
+
+ // 24-point font,
+ // ^^^^^^
+ assert(runs->at(3).pos == 27);
+ assert(runs->at(3).size == 6);
+ assert(runs->at(3).font_size == 24.0f);
+
+ // and 36-point font
+ // ^^
+ assert(runs->at(4).pos == 44);
+ assert(runs->at(4).size == 2);
+ assert(runs->at(4).font_size == 36.0f);
+ assert(runs->at(4).color == ss::color_t(0xFF, 0xFF, 0, 0)); // red
+
+ // and 36-point font
+ // ^^^^^^
+ assert(runs->at(5).pos == 46);
+ assert(runs->at(5).size == 6);
+ assert(runs->at(5).font_size == 36.0f);
+ }
+}
+
+void test_gnumeric_cell_borders_single_cells()
+{
+ ORCUS_TEST_FUNC_SCOPE;
+
+ fs::path filepath = SRCDIR"/test/gnumeric/borders/single-cells.gnumeric";
+ auto doc = load_doc(filepath);
+ assert(doc);
+
+ ss::styles& styles = doc->get_styles();
+
+ ss::sheet* sh = doc->get_sheet(0);
+ assert(sh);
+
+ struct check
+ {
+ ss::row_t row;
+ ss::col_t col;
+ ss::border_style_t style;
+ };
+
+ std::vector<check> checks =
+ {
+ { 3, 1, ss::border_style_t::hair },
+ { 5, 1, ss::border_style_t::dotted },
+ { 7, 1, ss::border_style_t::dash_dot_dot },
+ { 9, 1, ss::border_style_t::dash_dot },
+ { 11, 1, ss::border_style_t::dashed },
+ { 13, 1, ss::border_style_t::thin },
+ { 1, 3, ss::border_style_t::medium_dash_dot_dot },
+ { 3, 3, ss::border_style_t::slant_dash_dot },
+ { 5, 3, ss::border_style_t::medium_dash_dot },
+ { 7, 3, ss::border_style_t::medium_dashed },
+ { 9, 3, ss::border_style_t::medium },
+ { 11, 3, ss::border_style_t::thick },
+ { 13, 3, ss::border_style_t::double_border },
+ };
+
+ for (const check& c : checks)
+ {
+ std::cout << "(row: " << c.row << "; col: " << c.col << "; expected: " << int(c.style) << ")" << std::endl;
+ size_t xf = sh->get_cell_format(c.row, c.col);
+ const ss::cell_format_t* cf = styles.get_cell_format(xf);
+ assert(cf);
+ assert(cf->apply_border);
+
+ const ss::border_t* border = styles.get_border(cf->border);
+ assert(border);
+ assert(border->top.style == c.style);
+ assert(border->bottom.style == c.style);
+ assert(border->left.style == c.style);
+ assert(border->right.style == c.style);
+ }
+}
+
+void test_gnumeric_cell_borders_directions()
+{
+ ORCUS_TEST_FUNC_SCOPE;
+
+ fs::path filepath = SRCDIR"/test/gnumeric/borders/directions.gnumeric";
+ auto doc = load_doc(filepath);
+ assert(doc);
+
+ ss::styles& styles = doc->get_styles();
+
+ ss::sheet* sh = doc->get_sheet(0);
+ assert(sh);
+
+ struct check
+ {
+ ss::row_t row;
+ ss::col_t col;
+ ss::border_direction_t dir;
+ };
+
+ std::vector<check> checks =
+ {
+ { 1, 1, ss::border_direction_t::top },
+ { 3, 1, ss::border_direction_t::left },
+ { 5, 1, ss::border_direction_t::right },
+ { 7, 1, ss::border_direction_t::bottom },
+ { 9, 1, ss::border_direction_t::diagonal_tl_br },
+ { 11, 1, ss::border_direction_t::diagonal_bl_tr },
+ { 13, 1, ss::border_direction_t::diagonal },
+ };
+
+ const ss::color_t black{255, 0, 0, 0};
+
+ for (const check& c : checks)
+ {
+ size_t xf = sh->get_cell_format(c.row, c.col);
+ const ss::cell_format_t* cf = styles.get_cell_format(xf);
+ assert(cf);
+ assert(cf->apply_border);
+
+ const ss::border_t* border = styles.get_border(cf->border);
+ assert(border);
+
+ switch (c.dir)
+ {
+ case ss::border_direction_t::top:
+ assert(border->top.style);
+ assert(*border->top.style == ss::border_style_t::thin);
+ assert(border->top.border_color);
+ assert(*border->top.border_color == black);
+ assert(!border->top.border_width);
+ assert(!border->bottom.style);
+ assert(!border->bottom.border_color);
+ assert(!border->bottom.border_width);
+ assert(!border->left.style);
+ assert(!border->left.border_color);
+ assert(!border->left.border_width);
+ assert(!border->right.style);
+ assert(!border->right.border_color);
+ assert(!border->right.border_width);
+ assert(!border->diagonal.style);
+ assert(!border->diagonal.border_color);
+ assert(!border->diagonal.border_width);
+ assert(!border->diagonal_bl_tr.style);
+ assert(!border->diagonal_bl_tr.border_color);
+ assert(!border->diagonal_bl_tr.border_width);
+ assert(!border->diagonal_tl_br.style);
+ assert(!border->diagonal_tl_br.border_color);
+ assert(!border->diagonal_tl_br.border_width);
+ break;
+ case ss::border_direction_t::left:
+ assert(!border->top.style);
+ assert(!border->top.border_color);
+ assert(!border->top.border_width);
+ assert(!border->bottom.style);
+ assert(!border->bottom.border_color);
+ assert(!border->bottom.border_width);
+ assert(border->left.style);
+ assert(*border->left.style == ss::border_style_t::thin);
+ assert(border->left.border_color);
+ assert(*border->left.border_color == black);
+ assert(!border->left.border_width);
+ assert(!border->right.style);
+ assert(!border->right.border_color);
+ assert(!border->right.border_width);
+ assert(!border->diagonal.style);
+ assert(!border->diagonal.border_color);
+ assert(!border->diagonal.border_width);
+ assert(!border->diagonal_bl_tr.style);
+ assert(!border->diagonal_bl_tr.border_color);
+ assert(!border->diagonal_bl_tr.border_width);
+ assert(!border->diagonal_tl_br.style);
+ assert(!border->diagonal_tl_br.border_color);
+ assert(!border->diagonal_tl_br.border_width);
+ break;
+ case ss::border_direction_t::right:
+ assert(!border->top.style);
+ assert(!border->top.border_color);
+ assert(!border->top.border_width);
+ assert(!border->bottom.style);
+ assert(!border->bottom.border_color);
+ assert(!border->bottom.border_width);
+ assert(!border->left.style);
+ assert(!border->left.border_color);
+ assert(!border->left.border_width);
+ assert(border->right.style);
+ assert(*border->right.style == ss::border_style_t::thin);
+ assert(border->right.border_color);
+ assert(*border->right.border_color == black);
+ assert(!border->right.border_width);
+ assert(!border->diagonal.style);
+ assert(!border->diagonal.border_color);
+ assert(!border->diagonal.border_width);
+ assert(!border->diagonal_bl_tr.style);
+ assert(!border->diagonal_bl_tr.border_color);
+ assert(!border->diagonal_bl_tr.border_width);
+ assert(!border->diagonal_tl_br.style);
+ assert(!border->diagonal_tl_br.border_color);
+ assert(!border->diagonal_tl_br.border_width);
+ break;
+ case ss::border_direction_t::bottom:
+ assert(!border->top.style);
+ assert(!border->top.border_color);
+ assert(!border->top.border_width);
+ assert(border->bottom.style);
+ assert(*border->bottom.style == ss::border_style_t::thin);
+ assert(border->bottom.border_color);
+ assert(*border->bottom.border_color == black);
+ assert(!border->bottom.border_width);
+ assert(!border->left.style);
+ assert(!border->left.border_color);
+ assert(!border->left.border_width);
+ assert(!border->right.style);
+ assert(!border->right.border_color);
+ assert(!border->right.border_width);
+ assert(!border->diagonal.style);
+ assert(!border->diagonal.border_color);
+ assert(!border->diagonal.border_width);
+ assert(!border->diagonal_bl_tr.style);
+ assert(!border->diagonal_bl_tr.border_color);
+ assert(!border->diagonal_bl_tr.border_width);
+ assert(!border->diagonal_tl_br.style);
+ assert(!border->diagonal_tl_br.border_color);
+ assert(!border->diagonal_tl_br.border_width);
+ break;
+ case ss::border_direction_t::diagonal:
+ assert(!border->top.style);
+ assert(!border->top.border_color);
+ assert(!border->top.border_width);
+ assert(!border->bottom.style);
+ assert(!border->bottom.border_color);
+ assert(!border->bottom.border_width);
+ assert(!border->left.style);
+ assert(!border->left.border_color);
+ assert(!border->left.border_width);
+ assert(!border->right.style);
+ assert(!border->right.border_color);
+ assert(!border->right.border_width);
+ assert(!border->diagonal.style);
+ assert(!border->diagonal.border_color);
+ assert(!border->diagonal.border_width);
+ assert(border->diagonal_bl_tr.style);
+ assert(*border->diagonal_bl_tr.style == ss::border_style_t::thin);
+ assert(border->diagonal_bl_tr.border_color);
+ assert(*border->diagonal_bl_tr.border_color == black);
+ assert(!border->diagonal_bl_tr.border_width);
+ assert(border->diagonal_tl_br.style);
+ assert(*border->diagonal_tl_br.style == ss::border_style_t::thin);
+ assert(border->diagonal_tl_br.border_color);
+ assert(*border->diagonal_tl_br.border_color == black);
+ assert(!border->diagonal_tl_br.border_width);
+ break;
+ case ss::border_direction_t::diagonal_tl_br:
+ assert(!border->top.style);
+ assert(!border->top.border_color);
+ assert(!border->top.border_width);
+ assert(!border->bottom.style);
+ assert(!border->bottom.border_color);
+ assert(!border->bottom.border_width);
+ assert(!border->left.style);
+ assert(!border->left.border_color);
+ assert(!border->left.border_width);
+ assert(!border->right.style);
+ assert(!border->right.border_color);
+ assert(!border->right.border_width);
+ assert(!border->diagonal.style);
+ assert(!border->diagonal.border_color);
+ assert(!border->diagonal.border_width);
+ assert(!border->diagonal_bl_tr.style);
+ assert(!border->diagonal_bl_tr.border_color);
+ assert(!border->diagonal_bl_tr.border_width);
+ assert(border->diagonal_tl_br.style);
+ assert(*border->diagonal_tl_br.style == ss::border_style_t::thin);
+ assert(border->diagonal_tl_br.border_color);
+ assert(*border->diagonal_tl_br.border_color == black);
+ assert(!border->diagonal_tl_br.border_width);
+ break;
+ case ss::border_direction_t::diagonal_bl_tr:
+ assert(!border->top.style);
+ assert(!border->top.border_color);
+ assert(!border->top.border_width);
+ assert(!border->bottom.style);
+ assert(!border->bottom.border_color);
+ assert(!border->bottom.border_width);
+ assert(!border->left.style);
+ assert(!border->left.border_color);
+ assert(!border->left.border_width);
+ assert(!border->right.style);
+ assert(!border->right.border_color);
+ assert(!border->right.border_width);
+ assert(!border->diagonal.style);
+ assert(!border->diagonal.border_color);
+ assert(!border->diagonal.border_width);
+ assert(border->diagonal_bl_tr.style);
+ assert(*border->diagonal_bl_tr.style == ss::border_style_t::thin);
+ assert(border->diagonal_bl_tr.border_color);
+ assert(*border->diagonal_bl_tr.border_color == black);
+ assert(!border->diagonal_bl_tr.border_width);
+ assert(!border->diagonal_tl_br.style);
+ assert(!border->diagonal_tl_br.border_color);
+ assert(!border->diagonal_tl_br.border_width);
+ break;
+ default:
+ assert(!"unhandled direction!");
+ }
+ }
+}
+
+void test_gnumeric_cell_borders_colors()
+{
+ ORCUS_TEST_FUNC_SCOPE;
+
+ using ss::color_t;
+ using ss::border_style_t;
+
+ fs::path filepath = SRCDIR"/test/gnumeric/borders/colors.gnumeric";
+ auto doc = load_doc(filepath);
+ assert(doc);
+
+ ss::styles& styles = doc->get_styles();
+
+ ss::sheet* sh = doc->get_sheet(0);
+ assert(sh);
+
+ struct check
+ {
+ ss::row_t row;
+ ss::col_t col;
+ color_t color;
+ };
+
+ std::vector<check> checks =
+ {
+ { 2, 1, color_t(0xFF, 0xFF, 0, 0) }, // B3 - red
+ { 3, 1, color_t(0xFF, 0, 0x70, 0xC0) }, // B4 - blue
+ { 4, 1, color_t(0xFF, 0, 0xB0, 0x50) }, // B5 - green
+ };
+
+ for (const check& c : checks)
+ {
+ size_t xf = sh->get_cell_format(c.row, c.col); // B3
+
+ const ss::cell_format_t* cf = styles.get_cell_format(xf);
+ assert(cf);
+ assert(cf->apply_border);
+
+ const ss::border_t* border = styles.get_border(cf->border);
+ assert(border);
+
+ assert(!border->left.style);
+ assert(border->right.style);
+ assert(*border->right.style == border_style_t::thick);
+ assert(!border->top.style);
+ assert(!border->bottom.style);
+
+ assert(border->right.border_color == c.color);
+ }
+
+ // B7 contains yellow left border, purple right border, and light blue
+ // diagonal borders.
+
+ size_t xf = sh->get_cell_format(6, 1); // B7
+
+ const ss::cell_format_t* cf = styles.get_cell_format(xf);
+ assert(cf);
+ assert(cf->apply_border);
+
+ const ss::border_t* border = styles.get_border(cf->border);
+ assert(border);
+
+ assert(border->left.style == border_style_t::thick);
+ assert(border->left.border_color == color_t(0xFF, 0xFF, 0xFF, 0)); // yellow
+
+ assert(border->right.style == border_style_t::thick);
+ assert(border->right.border_color == color_t(0xFF, 0x70, 0x30, 0xA0)); // purple
+
+ assert(border->diagonal_bl_tr.style == border_style_t::thick);
+ assert(border->diagonal_bl_tr.border_color == color_t(0xFF, 0x00, 0xB0, 0xF0)); // light blue
+
+ assert(border->diagonal_tl_br.style == border_style_t::thick);
+ assert(border->diagonal_tl_br.border_color == color_t(0xFF, 0x00, 0xB0, 0xF0)); // light blue
+
+ // B7 also contains multi-line string. Test that as well.
+ ixion::model_context& model = doc->get_model_context();
+ ixion::string_id_t sid = model.get_string_identifier(ixion::abs_address_t(0,6,1));
+ const std::string* s = model.get_string(sid);
+ assert(s);
+ assert(*s == "<- Yellow\nPurple ->\nLight Blue \\");
+}
+
+void test_gnumeric_number_format()
+{
+ ORCUS_TEST_FUNC_SCOPE;
+
+ fs::path filepath = SRCDIR"/test/gnumeric/number-formats/input.gnumeric";
+ auto doc = load_doc(filepath);
+ assert(doc);
+
+ const spreadsheet::styles& styles = doc->get_styles();
+
+ const spreadsheet::sheet* sh = doc->get_sheet(0);
+ assert(sh);
+
+ struct check
+ {
+ ss::row_t row;
+ ss::col_t col;
+ std::string_view expected;
+ };
+
+ std::vector<check> checks =
+ {
+ { 1, 1, "[$-F800]dddd\\,\\ mmmm\\ dd\\,\\ yyyy" },
+ { 2, 1, "[$-409]mmmm\\ d\\,\\ yyyy;@" },
+ { 3, 1, "m/d/yy;@" },
+ { 4, 1, "m/d/yy h:mm" }, // General Date
+ };
+
+ for (const check& c : checks)
+ {
+ size_t xf = sh->get_cell_format(c.row, c.col);
+ const spreadsheet::cell_format_t* cf = styles.get_cell_format(xf);
+ assert(cf);
+
+ const spreadsheet::number_format_t* nf = styles.get_number_format(cf->number_format);
+ assert(nf);
+ std::cout << "row=" << c.row << "; col=" << c.col << "; expected='"
+ << c.expected << "'; actual='" << (nf->format_string ? *nf->format_string : "") << "'" << std::endl;
+ assert(nf->format_string == c.expected);
+ }
+}
+
+} // anonymous namespace
+
+int main()
+{
+ test_gnumeric_detection();
+ test_gnumeric_create_filter();
+ test_gnumeric_import();
+ test_gnumeric_column_widths_row_heights();
+ test_gnumeric_auto_filter();
+ test_gnumeric_hidden_rows_columns();
+ test_gnumeric_merged_cells();
+ test_gnumeric_text_alignment();
+ test_gnumeric_cell_properties_wrap_and_shrink();
+ test_gnumeric_background_fill();
+ test_gnumeric_colored_text();
+ test_gnumeric_text_formats();
+ test_gnumeric_cell_borders_single_cells();
+ test_gnumeric_cell_borders_directions();
+ test_gnumeric_cell_borders_colors();
+ test_gnumeric_number_format();
+
+ return EXIT_SUCCESS;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */