diff --git a/.appveyor.yml b/.appveyor.yml index a016f954..efd72a9d 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -14,6 +14,9 @@ environment: matrix: - STATIC: OFF - STATIC: ON + +matrix: + fast_finish: true init: [] @@ -23,10 +26,10 @@ before_build: - cmake -G "Visual Studio 14 2015" -D CMAKE_GENERATOR_PLATFORM=%platform% -D STATIC=%STATIC% -D SAMPLES=ON -D BENCHMARKS=ON .. build: - project: build/tests/xlnt.test.sln + project: build/xlnt_all.sln parallel: true verbosity: minimal test_script: - - cd tests\%configuration% - - xlnt.test.exe + - tests\%configuration%\xlnt.test.exe + - samples\%configuration%\sample-documentation.exe diff --git a/.travis.yml b/.travis.yml index 0e25f336..ff4c18ab 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,6 +30,7 @@ matrix: - COVERAGE=OFF - STATIC=OFF - SAMPLES=OFF + - BENCHMARKS=OFF # gcc-7, c++14, release build, static linking, samples + benchmarks compiled - os: linux @@ -47,7 +48,8 @@ matrix: - BUILD_TYPE=Release - COVERAGE=OFF - STATIC=ON - - SAMPLES=ON + - SAMPLES=OFF + - BENCHMARKS=OFF # gcc-8, c++17, release build, static linking, samples + benchmarks compiled - os: linux @@ -66,6 +68,7 @@ matrix: - COVERAGE=OFF - STATIC=ON - SAMPLES=ON + - BENCHMARKS=ON # =========== CLANG ============= # clang 4, c++11, release build, dynamic linking @@ -86,6 +89,7 @@ matrix: - COVERAGE=OFF - STATIC=OFF - SAMPLES=OFF + - BENCHMARKS=OFF # clang 5, c++14, release build, dynamic linking, samples + benchmarks compiled - os: linux @@ -104,7 +108,8 @@ matrix: - BUILD_TYPE=Release - COVERAGE=OFF - STATIC=ON - - SAMPLES=ON + - SAMPLES=OFF + - BENCHMARKS=OFF # clang 6, c++17, release build, static linking, samples compiled - os: linux @@ -124,6 +129,7 @@ matrix: - COVERAGE=OFF - STATIC=ON - SAMPLES=ON + - BENCHMARKS=ON # ============= CODE COVERAGE =============== # gcc-6, c++11, debug build, static linking, code coverage enabled @@ -159,17 +165,21 @@ install: - cmake --version script: - - | - if [[ "${BUILD_TYPE}" == "Release" && "${STATIC}" == "ON" ]]; - then BENCHMARKS="ON"; - else BENCHMARKS="OFF"; - fi - - mkdir build - cd build - cmake .. -DXLNT_CXX_LANG=${CXX_VER} -DSTATIC=$STATIC -DBENCHMARKS=$BENCHMARKS -DSAMPLES=$SAMPLES -DCOVERAGE=$COVERAGE -DCMAKE_BUILD_TYPE=$BUILD_TYPE - cmake --build . -- -j2 - ./tests/xlnt.test + - echo "samples" && echo 'travis_fold:start:samples' + - if [[ "${SAMPLES}" == "ON" ]]; then ./samples/sample-decrypt; fi + - if [[ "${SAMPLES}" == "ON" ]]; then ./samples/sample-img2xlsx "../samples/data/cafe.jpg" "img.xlsx"; fi + - if [[ "${SAMPLES}" == "ON" ]]; then ./samples/sample-documentation; fi + - echo 'travis_fold:end:samples' + - echo "benchmarks" && echo 'travis_fold:start:benchmarks' + - if [[ "${BENCHMARKS}" == "ON" ]]; then ./benchmarks/benchmark-styles; fi + - if [[ "${BENCHMARKS}" == "ON" ]]; then ./benchmarks/benchmark-writer; fi + - echo 'travis_fold:end:benchmarks' + after_success: - | diff --git a/benchmarks/writer.cpp b/benchmarks/writer.cpp index abe94e52..c69f138e 100644 --- a/benchmarks/writer.cpp +++ b/benchmarks/writer.cpp @@ -39,11 +39,10 @@ void writer(int cols, int rows) for(int index = 0; index < rows; index++) { - if ((index + 1) % (rows / 10) == 0) + if (rows >= 10 && (index + 1) % (rows / 10) == 0) { std::string progress = std::string((index + 1) / (1 + rows / 10), '.'); std::cout << "\r" << progress; - std::cout.flush(); } for (int i = 0; i < cols; i++) @@ -51,8 +50,7 @@ void writer(int cols, int rows) ws.cell(xlnt::cell_reference(i + 1, index + 1)).value(i); } } - - std::cout << std::endl; + std::cout << '\n'; auto filename = "benchmark.xlsx"; wb.save(filename); @@ -63,33 +61,30 @@ void writer(int cols, int rows) // Time from the best of three is taken. void timer(std::function fn, int cols, int rows) { - using xlnt::benchmarks::current_time; - const auto repeat = std::size_t(3); - auto time = std::numeric_limits::max(); - + std::chrono::duration time{}; std::cout << cols << " cols " << rows << " rows" << std::endl; + fn(rows, cols); // 1 cold run for(int i = 0; i < repeat; i++) { - auto start = current_time(); + auto start = std::chrono::high_resolution_clock::now(); fn(cols, rows); - time = std::min(current_time() - start, time); + time += std::chrono::high_resolution_clock::now() - start; } - std::cout << time / 1000.0 << std::endl; + std::cout << time.count() / repeat << " ms per iteration" << '\n' << '\n'; } } // namespace int main() { + timer(&writer, 10000, 1); + timer(&writer, 1000, 10); timer(&writer, 100, 100); - timer(&writer, 1000, 100); - timer(&writer, 4000, 100); - timer(&writer, 8192, 100); - timer(&writer, 10, 10000); - timer(&writer, 4000, 1000); + timer(&writer, 10, 1000); + timer(&writer, 1, 10000); return 0; } diff --git a/include/xlnt/cell/cell_reference.hpp b/include/xlnt/cell/cell_reference.hpp index 57fd54bc..fcd32c40 100644 --- a/include/xlnt/cell/cell_reference.hpp +++ b/include/xlnt/cell/cell_reference.hpp @@ -26,7 +26,7 @@ #include #include #include - +#include #include #include @@ -255,3 +255,17 @@ private: }; } // namespace xlnt + +namespace std +{ +template <> +struct hash +{ + size_t operator()(const xlnt::cell_reference &x) const + { + static_assert(std::is_same::value, "this hash function expects both row and column to be 32-bit numbers"); + static_assert(std::is_same::value, "this hash function expects both row and column to be 32-bit numbers"); + return hash{}(x.row() | static_cast(x.column_index()) << 32); + } +}; +} \ No newline at end of file diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index f8ad70da..fdeaab4d 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -41,3 +41,4 @@ foreach(SAMPLE_SOURCE IN ITEMS ${SAMPLE_SOURCES}) $) endif() endforeach() +# \ No newline at end of file diff --git a/samples/data/documentation-print.xlsx b/samples/data/documentation-print.xlsx new file mode 100644 index 00000000..48fc67fc Binary files /dev/null and b/samples/data/documentation-print.xlsx differ diff --git a/samples/documentation.cpp b/samples/documentation.cpp new file mode 100644 index 00000000..067300bf --- /dev/null +++ b/samples/documentation.cpp @@ -0,0 +1,226 @@ +// Copyright (c) 2017-2018 Thomas Fussell +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, WRISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE +// +// @license: http://www.opensource.org/licenses/mit-license.php +// @author: see AUTHORS file + +#include +#include "helpers/path_helper.hpp" + +// Readme +// from https://tfussell.gitbooks.io/xlnt/content/ and https://github.com/tfussell/xlnt/blob/master/README.md +void sample_readme_example1() +{ + xlnt::workbook wb; + xlnt::worksheet ws = wb.active_sheet(); + + ws.cell("A1").value(5); + ws.cell("B2").value("string data"); + ws.cell("C3").formula("=RAND()"); + + ws.merge_cells("C3:C4"); + ws.freeze_panes("B2"); + + wb.save("sample.xlsx"); +} + +// Simple - reading from an existing xlsx spread sheet. +// from https://tfussell.gitbooks.io/xlnt/content/docs/introduction/Examples.html +void sample_read_and_print_example() +{ + xlnt::workbook wb; + wb.load(path_helper::sample_file("documentation-print.xlsx")); // modified to use the test data directory + auto ws = wb.active_sheet(); + std::clog << "Processing spread sheet" << std::endl; + for (auto row : ws.rows(false)) + { + for (auto cell : row) + { + std::clog << cell.to_string() << std::endl; + } + } + std::clog << "Processing complete" << std::endl; +} + +// Simple - storing a spread sheet in a 2 dimensional C++ Vector for further processing +// from https://tfussell.gitbooks.io/xlnt/content/docs/introduction/Examples.html +void sample_read_into_vector_example() +{ + xlnt::workbook wb; + wb.load(path_helper::sample_file("documentation-print.xlsx")); // modified to use the test data directory + auto ws = wb.active_sheet(); + std::clog << "Processing spread sheet" << std::endl; + std::clog << "Creating a single vector which stores the whole spread sheet" << std::endl; + std::vector> theWholeSpreadSheet; + for (auto row : ws.rows(false)) + { + std::clog << "Creating a fresh vector for just this row in the spread sheet" << std::endl; + std::vector aSingleRow; + for (auto cell : row) + { + std::clog << "Adding this cell to the row" << std::endl; + aSingleRow.push_back(cell.to_string()); + } + std::clog << "Adding this entire row to the vector which stores the whole spread sheet" << std::endl; + theWholeSpreadSheet.push_back(aSingleRow); + } + std::clog << "Processing complete" << std::endl; + std::clog << "Reading the vector and printing output to the screen" << std::endl; + for (int rowInt = 0; rowInt < theWholeSpreadSheet.size(); rowInt++) + { + for (int colInt = 0; colInt < theWholeSpreadSheet.at(rowInt).size(); colInt++) + { + std::cout << theWholeSpreadSheet.at(rowInt).at(colInt) << std::endl; + } + } +} + +// Simple - writing values to a new xlsx spread sheet. +// from https://tfussell.gitbooks.io/xlnt/content/docs/introduction/Examples.html +void sample_write_sheet_to_file_example() +{ + // Creating a 2 dimensional vector which we will write values to + std::vector> wholeWorksheet; + //Looping through each row (100 rows as per the second argument in the for loop) + for (int outer = 0; outer < 100; outer++) + { + //Creating a fresh vector for a fresh row + std::vector singleRow; + //Looping through each of the columns (100 as per the second argument in the for loop) in this particular row + for (int inner = 0; inner < 100; inner++) + { + //Adding a single value in each cell of the row + std::string val = std::to_string(inner + 1); + singleRow.push_back(val); + } + //Adding the single row to the 2 dimensional vector + wholeWorksheet.push_back(singleRow); + std::clog << "Writing to row " << outer << " in the vector " << std::endl; + } + //Writing to the spread sheet + //Creating the output workbook + std::clog << "Creating workbook" << std::endl; + xlnt::workbook wbOut; + //Setting the destination output file name + std::string dest_filename = "output.xlsx"; + //Creating the output worksheet + xlnt::worksheet wsOut = wbOut.active_sheet(); + //Giving the output worksheet a title/name + wsOut.title("data"); + //We will now be looping through the 2 dimensional vector which we created above + //In this case we have two iterators one for the outer loop (row) and one for the inner loop (column) + std::clog << "Looping through vector and writing to spread sheet" << std::endl; + for (int fOut = 0; fOut < wholeWorksheet.size(); fOut++) + { + std::clog << "Row" << fOut << std::endl; + for (int fIn = 0; fIn < wholeWorksheet.at(fOut).size(); fIn++) + { + //Take notice of the difference between accessing the vector and accessing the work sheet + //As you may already know Excel spread sheets start at row 1 and column 1 (not row 0 and column 0 like you would expect from a C++ vector) + //In short the xlnt cell reference starts at column 1 row 1 (hence the + 1s below) and the vector reference starts at row 0 and column 0 + wsOut.cell(xlnt::cell_reference(fIn + 1, fOut + 1)).value(wholeWorksheet.at(fOut).at(fIn)); + //Further clarification to avoid confusion + //Cell reference arguments are (column number, row number); e.g. cell_reference(fIn + 1, fOut + 1) + //Vector arguments are (row number, column number); e.g. wholeWorksheet.at(fOut).at(fIn) + } + } + std::clog << "Finished writing spread sheet" << std::endl; + wbOut.save(dest_filename); +} + +// Number Formatting +// from https://tfussell.gitbooks.io/xlnt/content/docs/advanced/Formatting.html +void sample_number_formatting_example() +{ + xlnt::workbook wb; + auto cell = wb.active_sheet().cell("A1"); + cell.number_format(xlnt::number_format::percentage()); + cell.value(0.513); + std::cout << '\n' + << cell.to_string() << std::endl; +} +// Properties +// from https://tfussell.gitbooks.io/xlnt/content/docs/advanced/Properties.html +void sample_properties_example() +{ + xlnt::workbook wb; + wb.core_property(xlnt::core_property::category, "hors categorie"); + wb.core_property(xlnt::core_property::content_status, "good"); + wb.core_property(xlnt::core_property::created, xlnt::datetime(2017, 1, 15)); + wb.core_property(xlnt::core_property::creator, "me"); + wb.core_property(xlnt::core_property::description, "description"); + wb.core_property(xlnt::core_property::identifier, "id"); + wb.core_property(xlnt::core_property::keywords, {"wow", "such"}); + wb.core_property(xlnt::core_property::language, "Esperanto"); + wb.core_property(xlnt::core_property::last_modified_by, "someone"); + wb.core_property(xlnt::core_property::last_printed, xlnt::datetime(2017, 1, 15)); + wb.core_property(xlnt::core_property::modified, xlnt::datetime(2017, 1, 15)); + wb.core_property(xlnt::core_property::revision, "3"); + wb.core_property(xlnt::core_property::subject, "subject"); + wb.core_property(xlnt::core_property::title, "title"); + wb.core_property(xlnt::core_property::version, "1.0"); + wb.extended_property(xlnt::extended_property::application, "xlnt"); + wb.extended_property(xlnt::extended_property::app_version, "0.9.3"); + wb.extended_property(xlnt::extended_property::characters, 123); + wb.extended_property(xlnt::extended_property::characters_with_spaces, 124); + wb.extended_property(xlnt::extended_property::company, "Incorporated Inc."); + wb.extended_property(xlnt::extended_property::dig_sig, "?"); + wb.extended_property(xlnt::extended_property::doc_security, 0); + wb.extended_property(xlnt::extended_property::heading_pairs, true); + wb.extended_property(xlnt::extended_property::hidden_slides, false); + wb.extended_property(xlnt::extended_property::h_links, 0); + wb.extended_property(xlnt::extended_property::hyperlink_base, 0); + wb.extended_property(xlnt::extended_property::hyperlinks_changed, true); + wb.extended_property(xlnt::extended_property::lines, 42); + wb.extended_property(xlnt::extended_property::links_up_to_date, false); + wb.extended_property(xlnt::extended_property::manager, "johnny"); + wb.extended_property(xlnt::extended_property::m_m_clips, "?"); + wb.extended_property(xlnt::extended_property::notes, "note"); + wb.extended_property(xlnt::extended_property::pages, 19); + wb.extended_property(xlnt::extended_property::paragraphs, 18); + wb.extended_property(xlnt::extended_property::presentation_format, "format"); + wb.extended_property(xlnt::extended_property::scale_crop, true); + wb.extended_property(xlnt::extended_property::shared_doc, false); + wb.extended_property(xlnt::extended_property::slides, 17); + wb.extended_property(xlnt::extended_property::template_, "template!"); + wb.extended_property(xlnt::extended_property::titles_of_parts, {"title"}); + wb.extended_property(xlnt::extended_property::total_time, 16); + wb.extended_property(xlnt::extended_property::words, 101); + wb.custom_property("test", {1, 2, 3}); + wb.custom_property("Editor", "John Smith"); + wb.save("lots_of_properties.xlsx"); +} + +int main() +{ + sample_readme_example1(); + std::clog << '\n'; + sample_read_and_print_example(); + std::clog << '\n'; + sample_read_into_vector_example(); + std::clog << '\n'; + sample_write_sheet_to_file_example(); + std::clog << '\n'; + sample_number_formatting_example(); + std::clog << '\n'; + sample_properties_example(); + std::clog.flush(); + return 0; +} \ No newline at end of file diff --git a/samples/sample.cpp b/samples/sample.cpp deleted file mode 100644 index 7f1a89b0..00000000 --- a/samples/sample.cpp +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2017-2018 Thomas Fussell -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, WRISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE -// -// @license: http://www.opensource.org/licenses/mit-license.php -// @author: see AUTHORS file - -#include - -int main() -{ - xlnt::workbook wb; - xlnt::worksheet ws = wb.active_sheet(); - - ws.cell("A1").value(5); - ws.cell("B2").value("string data"); - ws.cell("C3").formula("=RAND()"); - - ws.merge_cells("C3:C4"); - ws.freeze_panes("B2"); - - wb.save("sample.xlsx"); - - return 0; -} diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 5c924e84..186e57fc 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -33,6 +33,7 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") # all warnings set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wextra") # extra warnings set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unknown-pragmas") # ignore MSVC and Clang pragmas + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-maybe-uninitialized") # GCC diagnostic with lots of false positives elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Weverything") # all warnings # blacklist warnings that are not relevant diff --git a/source/detail/implementations/stylesheet.hpp b/source/detail/implementations/stylesheet.hpp index 8ccc5976..0ec45161 100644 --- a/source/detail/implementations/stylesheet.hpp +++ b/source/detail/implementations/stylesheet.hpp @@ -178,33 +178,18 @@ struct stylesheet } template - std::size_t find_or_add(C &container, const T &item, bool *added = nullptr) + std::size_t find_or_add(C &container, const T &item) { - if (added != nullptr) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsign-conversion" + auto iter = std::find(container.begin(), container.end(), item); + if (iter != container.end()) { - *added = false; + return std::size_t(iter - container.begin()); } - - std::size_t i = 0; - - for (auto iter = container.begin(); iter != container.end(); ++iter) - { - if (*iter == item) - { - return i; - } - - ++i; - } - - if (added != nullptr) - { - *added = true; - } - - container.emplace(container.end(), item); - - return container.size() - 1; + iter = container.emplace(container.end(), item); + return std::size_t(iter - container.begin()); +#pragma GCC diagnostic pop } template @@ -235,7 +220,6 @@ struct stylesheet if (!garbage_collection_enabled) return; auto format_iter = format_impls.begin(); - while (format_iter != format_impls.end()) { auto &impl = *format_iter; @@ -399,12 +383,18 @@ struct stylesheet format_impl *find_or_create(format_impl &pattern) { - auto iter = format_impls.begin(); - bool added = false; pattern.references = 0; - auto id = find_or_add(format_impls, pattern, &added); - std::advance(iter, static_cast::difference_type>(id)); - + std::size_t id = 0; + auto iter = format_impls.begin(); + while (iter != format_impls.end() && !(*iter == pattern)) + { + ++id; + ++iter; + } + if (iter == format_impls.end()) + { + iter = format_impls.emplace(format_impls.end(), pattern); + } auto &result = *iter; result.parent = this; @@ -426,7 +416,10 @@ struct stylesheet { format_impl new_format = *pattern; new_format.style = style_name; - + if (pattern->references == 0) + { + *pattern = new_format; + } return find_or_create(new_format); } @@ -435,7 +428,10 @@ struct stylesheet format_impl new_format = *pattern; new_format.alignment_id = find_or_add(alignments, new_alignment); new_format.alignment_applied = applied; - + if (pattern->references == 0) + { + *pattern = new_format; + } return find_or_create(new_format); } @@ -444,7 +440,10 @@ struct stylesheet format_impl new_format = *pattern; new_format.border_id = find_or_add(borders, new_border); new_format.border_applied = applied; - + if (pattern->references == 0) + { + *pattern = new_format; + } return find_or_create(new_format); } @@ -453,7 +452,10 @@ struct stylesheet format_impl new_format = *pattern; new_format.fill_id = find_or_add(fills, new_fill); new_format.fill_applied = applied; - + if (pattern->references == 0) + { + *pattern = new_format; + } return find_or_create(new_format); } @@ -462,7 +464,10 @@ struct stylesheet format_impl new_format = *pattern; new_format.font_id = find_or_add(fonts, new_font); new_format.font_applied = applied; - + if (pattern->references == 0) + { + *pattern = new_format; + } return find_or_create(new_format); } @@ -475,7 +480,10 @@ struct stylesheet } new_format.number_format_id = new_number_format.id(); new_format.number_format_applied = applied; - + if (pattern->references == 0) + { + *pattern = new_format; + } return find_or_create(new_format); } @@ -484,7 +492,10 @@ struct stylesheet format_impl new_format = *pattern; new_format.protection_id = find_or_add(protections, new_protection); new_format.protection_applied = applied; - + if (pattern->references == 0) + { + *pattern = new_format; + } return find_or_create(new_format); } diff --git a/source/detail/implementations/worksheet_impl.hpp b/source/detail/implementations/worksheet_impl.hpp index cb3be2fb..7ae00ae4 100644 --- a/source/detail/implementations/worksheet_impl.hpp +++ b/source/detail/implementations/worksheet_impl.hpp @@ -88,12 +88,9 @@ struct worksheet_impl sheet_properties_ = other.sheet_properties_; print_options_ = other.print_options_; - for (auto &row : cell_map_) + for (auto &cell : cell_map_) { - for (auto &cell : row.second) - { - cell.second.parent_ = this; - } + cell.second.parent_ = this; } } @@ -134,7 +131,7 @@ struct worksheet_impl std::unordered_map column_properties_; std::unordered_map row_properties_; - std::unordered_map> cell_map_; + std::unordered_map cell_map_; optional page_setup_; optional auto_filter_; diff --git a/source/detail/serialization/xlsx_producer.cpp b/source/detail/serialization/xlsx_producer.cpp index a683811d..f8172c16 100644 --- a/source/detail/serialization/xlsx_producer.cpp +++ b/source/detail/serialization/xlsx_producer.cpp @@ -2180,7 +2180,8 @@ void xlsx_producer::write_worksheet(const relationship &rel) return true; } - for (auto row = ws.lowest_row(); row <= ws.highest_row(); ++row) + auto highest = ws.highest_row(); + for (auto row = ws.lowest_row(); row <= highest; ++row) { if (ws.has_row_properties(row) && ws.row_properties(row).dy_descent.is_set()) { diff --git a/source/styles/color.cpp b/source/styles/color.cpp index 5eabee70..20624e7d 100644 --- a/source/styles/color.cpp +++ b/source/styles/color.cpp @@ -310,7 +310,7 @@ bool color::operator==(const xlnt::color &other) const case color_type::theme: return theme_.index() == other.theme_.index(); case color_type::rgb: - return rgb_.hex_string() == other.rgb_.hex_string(); + return rgb_.rgba() == other.rgb_.rgba(); } return false; diff --git a/source/worksheet/worksheet.cpp b/source/worksheet/worksheet.cpp index 5ab28fa1..33386f84 100644 --- a/source/worksheet/worksheet.cpp +++ b/source/worksheet/worksheet.cpp @@ -196,32 +196,18 @@ const workbook &worksheet::workbook() const void worksheet::garbage_collect() { - auto cell_map_iter = d_->cell_map_.begin(); + auto cell_iter = d_->cell_map_.begin(); - while (cell_map_iter != d_->cell_map_.end()) + while (cell_iter != d_->cell_map_.end()) { - auto cell_iter = cell_map_iter->second.begin(); - - while (cell_iter != cell_map_iter->second.end()) + if (xlnt::cell(&cell_iter->second).garbage_collectible()) { - class cell current_cell(&cell_iter->second); - - if (current_cell.garbage_collectible()) - { - cell_iter = cell_map_iter->second.erase(cell_iter); - continue; - } - - cell_iter++; + cell_iter = d_->cell_map_.erase(cell_iter); } - - if (cell_map_iter->second.empty()) + else { - cell_map_iter = d_->cell_map_.erase(cell_map_iter); - continue; + ++cell_iter; } - - cell_map_iter++; } } @@ -391,25 +377,22 @@ cell_reference worksheet::active_cell() const cell worksheet::cell(const cell_reference &reference) { - auto &row = d_->cell_map_[reference.row()]; - auto match = row.find(reference.column_index()); - - if (match == row.end()) + auto match = d_->cell_map_.find(reference); + if (match == d_->cell_map_.end()) { - match = row.emplace(reference.column_index(), detail::cell_impl()).first; - auto &impl = match->second; - + auto impl = detail::cell_impl(); impl.parent_ = d_; impl.column_ = reference.column_index(); impl.row_ = reference.row(); - } + match = d_->cell_map_.emplace(reference, impl).first; + } return xlnt::cell(&match->second); } const cell worksheet::cell(const cell_reference &reference) const { - return xlnt::cell(&d_->cell_map_.at(reference.row()).at(reference.column_index())); + return xlnt::cell(&d_->cell_map_.at(reference)); } cell worksheet::cell(xlnt::column_t column, row_t row) @@ -424,13 +407,8 @@ const cell worksheet::cell(xlnt::column_t column, row_t row) const bool worksheet::has_cell(const cell_reference &reference) const { - const auto row = d_->cell_map_.find(reference.row()); - if (row == d_->cell_map_.cend()) return false; - - const auto col = row->second.find(reference.column_index()); - if (col == row->second.cend()) return false; - - return true; + const auto cell = d_->cell_map_.find(reference); + return cell != d_->cell_map_.cend(); } bool worksheet::has_row_properties(row_t row) const @@ -477,12 +455,9 @@ column_t worksheet::lowest_column() const auto lowest = constants::max_column(); - for (auto &row : d_->cell_map_) + for (auto &cell : d_->cell_map_) { - for (auto &c : row.second) - { - lowest = std::min(lowest, c.first); - } + lowest = std::min(lowest, cell.first.column()); } return lowest; @@ -514,9 +489,9 @@ row_t worksheet::lowest_row() const auto lowest = constants::max_row(); - for (auto &row : d_->cell_map_) + for (auto &cell : d_->cell_map_) { - lowest = std::min(lowest, row.first); + lowest = std::min(lowest, cell.first.row()); } return lowest; @@ -543,9 +518,9 @@ row_t worksheet::highest_row() const { auto highest = constants::min_row(); - for (auto &row : d_->cell_map_) + for (auto &cell : d_->cell_map_) { - highest = std::max(highest, row.first); + highest = std::max(highest, cell.first.row()); } return highest; @@ -572,12 +547,9 @@ column_t worksheet::highest_column() const { auto highest = constants::min_column(); - for (auto &row : d_->cell_map_) + for (auto &cell : d_->cell_map_) { - for (auto &c : row.second) - { - highest = std::max(highest, c.first); - } + highest = std::max(highest, cell.first.column()); } return highest; @@ -744,13 +716,22 @@ const cell_vector worksheet::cells(bool skip_null) const void worksheet::clear_cell(const cell_reference &ref) { - d_->cell_map_.at(ref.row()).erase(ref.column()); + d_->cell_map_.erase(ref); // TODO: garbage collect newly unreferenced resources such as styles? } void worksheet::clear_row(row_t row) { - d_->cell_map_.erase(row); + for (auto it = d_->cell_map_.begin(); it != d_->cell_map_.end();) + { + if (it->first.row() == row) + { + it = d_->cell_map_.erase(it); + } + else { + ++it; + } + } d_->row_properties_.erase(row); // TODO: garbage collect newly unreferenced resources such as styles? } @@ -769,22 +750,16 @@ bool worksheet::compare(const worksheet &other, bool reference) const if (d_->parent_ != other.d_->parent_) return false; - for (auto &row : d_->cell_map_) - { - if (other.d_->cell_map_.find(row.first) == other.d_->cell_map_.end()) - { - return false; - } - for (auto &cell : row.second) + for (auto &cell : d_->cell_map_) { - if (other.d_->cell_map_[row.first].find(cell.first) == other.d_->cell_map_[row.first].end()) + if (other.d_->cell_map_.find(cell.first) == other.d_->cell_map_.end()) { return false; } xlnt::cell this_cell(&cell.second); - xlnt::cell other_cell(&other.d_->cell_map_[row.first][cell.first]); + xlnt::cell other_cell(&other.d_->cell_map_[cell.first]); if (this_cell.data_type() != other_cell.data_type()) { @@ -797,7 +772,6 @@ bool worksheet::compare(const worksheet &other, bool reference) const return false; } } - } // todo: missing some comparisons