react-native-nitro-xlsx 0.1.4 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NitroXlsx.podspec +27 -27
- package/Package.swift +35 -10
- package/README.md +220 -58
- package/android/CMakeLists.txt +24 -27
- package/android/cpp-adapter.cpp +12 -0
- package/android/src/main/java/com/margelo/nitro/xlsx/NitroXlsxPackage.kt +9 -0
- package/cpp/HybridNitroXlsx.cpp +25 -179
- package/cpp/HybridNitroXlsx.hpp +2 -2
- package/cpp/HybridXlsxConditionalFormat.cpp +252 -0
- package/cpp/HybridXlsxConditionalFormat.hpp +62 -0
- package/cpp/HybridXlsxConditionalFormats.cpp +46 -0
- package/cpp/HybridXlsxConditionalFormats.hpp +28 -0
- package/cpp/HybridXlsxWorkbook.cpp +159 -196
- package/cpp/HybridXlsxWorkbook.hpp +7 -2
- package/cpp/HybridXlsxWorksheet.cpp +433 -62
- package/cpp/HybridXlsxWorksheet.hpp +73 -26
- package/cpp/XlsxCacheDir.cpp +76 -0
- package/cpp/XlsxCacheDir.hpp +27 -0
- package/cpp/XlsxError.hpp +68 -0
- package/cpp/XlsxJson.cpp +185 -0
- package/cpp/XlsxJson.hpp +36 -0
- package/ios/XlsxCacheDir.mm +60 -0
- package/nitrogen/generated/android/NitroXlsx+autolinking.cmake +2 -0
- package/nitrogen/generated/shared/c++/HybridNitroXlsxSpec.hpp +5 -3
- package/nitrogen/generated/shared/c++/HybridXlsxConditionalFormatSpec.cpp +54 -0
- package/nitrogen/generated/shared/c++/HybridXlsxConditionalFormatSpec.hpp +95 -0
- package/nitrogen/generated/shared/c++/HybridXlsxConditionalFormatsSpec.cpp +24 -0
- package/nitrogen/generated/shared/c++/HybridXlsxConditionalFormatsSpec.hpp +68 -0
- package/nitrogen/generated/shared/c++/HybridXlsxWorkbookSpec.cpp +3 -0
- package/nitrogen/generated/shared/c++/HybridXlsxWorkbookSpec.hpp +6 -1
- package/nitrogen/generated/shared/c++/HybridXlsxWorksheetSpec.cpp +47 -19
- package/nitrogen/generated/shared/c++/HybridXlsxWorksheetSpec.hpp +52 -20
- package/package.json +3 -3
- package/scripts/run-cpp-tests.sh +14 -0
- package/src/__mocks__/react-native-nitro-modules.ts +1 -0
- package/src/__tests__/index.test.ts +76 -7
- package/src/index.ts +107 -18
- package/src/specs/NitroXlsx.nitro.ts +141 -44
- package/thirdparty/OpenXLSX/.clang-format +118 -0
- package/thirdparty/OpenXLSX/.clang-tidy +28 -0
- package/thirdparty/OpenXLSX/.gitignore +130 -0
- package/thirdparty/OpenXLSX/Benchmarks/2024-07-29-benchmarks_before_XMLNode_wrappers +46 -0
- package/thirdparty/OpenXLSX/Benchmarks/2024-07-29-benchmarks_with_XMLNode_wrappers_and_namespace_and_stripping +49 -0
- package/thirdparty/OpenXLSX/Benchmarks/2024-07-29-benchmarks_with_XMLNode_wrappers_and_namespace_char_support +49 -0
- package/thirdparty/OpenXLSX/Benchmarks/2024-07-29-benchmarks_with_XMLNode_wrappers_and_namespace_support +49 -0
- package/thirdparty/OpenXLSX/Benchmarks/2024-07-29-benchmarks_with_XMLNode_wrappers_and_namespaced_name_disabled +49 -0
- package/thirdparty/OpenXLSX/Benchmarks/2024-08-11-benchmarks_post-XLCellIterator-Rework +46 -0
- package/thirdparty/OpenXLSX/Benchmarks/2024-08-14-benchmarks_post-default-row-column-style-support +46 -0
- package/thirdparty/OpenXLSX/Benchmarks/2024-09-02-benchmarks +46 -0
- package/thirdparty/OpenXLSX/Benchmarks/2024-12-19-benchmarks +46 -0
- package/thirdparty/OpenXLSX/Benchmarks/2024-12-30-benchmarks-post-XLSharedStrings-by-ref +46 -0
- package/thirdparty/OpenXLSX/Benchmarks/2025-01-09-benchmarks-post-conditionalFormatsAndSheetProtection +46 -0
- package/thirdparty/OpenXLSX/Benchmarks/2025-01-10-benchmarks-post-XLCellValue-bugfix +46 -0
- package/thirdparty/OpenXLSX/Benchmarks/2025-01-31-benchmarks-post-XLComments-implementation +46 -0
- package/thirdparty/OpenXLSX/Benchmarks/2025-02-02-benchmarks-post-XLRowIterator-Rework +46 -0
- package/thirdparty/OpenXLSX/Benchmarks/2025-07-20-benchmarks-after-hiding-pugixml-internals +46 -0
- package/thirdparty/OpenXLSX/Benchmarks/2025-08-29-benchmarks-after-code-review-with-libzip +46 -0
- package/thirdparty/OpenXLSX/Benchmarks/Benchmark.cpp +249 -0
- package/thirdparty/OpenXLSX/Benchmarks/CMakeLists.txt +19 -0
- package/thirdparty/OpenXLSX/CMakeLists.txt +466 -0
- package/thirdparty/OpenXLSX/Documentation/CMakeLists.txt +31 -0
- package/thirdparty/OpenXLSX/Documentation/Doxyfile.in +2471 -0
- package/thirdparty/OpenXLSX/Documentation/DoxygenLayout.xml +194 -0
- package/thirdparty/OpenXLSX/Documentation/customdoxygen.css +1355 -0
- package/thirdparty/OpenXLSX/Examples/CMakeLists.txt +103 -0
- package/thirdparty/OpenXLSX/Examples/Demo1.cpp +279 -0
- package/thirdparty/OpenXLSX/Examples/Demo10.cpp +545 -0
- package/thirdparty/OpenXLSX/Examples/Demo1A.cpp +137 -0
- package/thirdparty/OpenXLSX/Examples/Demo2.cpp +65 -0
- package/thirdparty/OpenXLSX/Examples/Demo3.cpp +89 -0
- package/thirdparty/OpenXLSX/Examples/Demo4.cpp +86 -0
- package/thirdparty/OpenXLSX/Examples/Demo5.cpp +77 -0
- package/thirdparty/OpenXLSX/Examples/Demo6.cpp +103 -0
- package/thirdparty/OpenXLSX/Examples/Demo7.cpp +79 -0
- package/thirdparty/OpenXLSX/Examples/Demo8.cpp +117 -0
- package/thirdparty/OpenXLSX/Examples/Demo9.cpp +363 -0
- package/thirdparty/OpenXLSX/Examples/cmake/FindLIBZIP.cmake +61 -0
- package/thirdparty/OpenXLSX/Examples/external/CustomZip/CustomZip.hpp +256 -0
- package/thirdparty/OpenXLSX/LICENSE.md +29 -0
- package/thirdparty/OpenXLSX/Makefile.GNU +530 -0
- package/thirdparty/OpenXLSX/Notes/TBDs.txt +16 -0
- package/thirdparty/OpenXLSX/Notes/confirmed-TBCs.txt +8 -0
- package/thirdparty/OpenXLSX/Notes/styles-missing-support.xml +12 -0
- package/thirdparty/OpenXLSX/Notes/todo-list.txt +108 -0
- package/thirdparty/OpenXLSX/OpenXLSX/CMakeLists.txt +631 -0
- package/thirdparty/OpenXLSX/OpenXLSX/OpenXLSX.hpp +63 -0
- package/thirdparty/OpenXLSX/OpenXLSX/OpenXLSXConfig.cmake +11 -0
- package/thirdparty/OpenXLSX/OpenXLSX/cmake/bundle_static_library.cmake +198 -0
- package/thirdparty/OpenXLSX/OpenXLSX/cmake/patch_pkg_config.cmake +69 -0
- package/thirdparty/OpenXLSX/OpenXLSX/headers/IZipArchive.hpp +361 -0
- package/thirdparty/OpenXLSX/OpenXLSX/headers/OpenXLSX-Exports.hpp +12 -0
- package/thirdparty/OpenXLSX/OpenXLSX/headers/XLCell.hpp +367 -0
- package/thirdparty/OpenXLSX/OpenXLSX/headers/XLCellIterator.hpp +252 -0
- package/thirdparty/OpenXLSX/OpenXLSX/headers/XLCellRange.hpp +223 -0
- package/thirdparty/OpenXLSX/OpenXLSX/headers/XLCellReference.hpp +317 -0
- package/thirdparty/OpenXLSX/OpenXLSX/headers/XLCellValue.hpp +793 -0
- package/thirdparty/OpenXLSX/OpenXLSX/headers/XLColor.hpp +232 -0
- package/thirdparty/OpenXLSX/OpenXLSX/headers/XLColumn.hpp +159 -0
- package/thirdparty/OpenXLSX/OpenXLSX/headers/XLCommandQuery.hpp +228 -0
- package/thirdparty/OpenXLSX/OpenXLSX/headers/XLComments.hpp +270 -0
- package/thirdparty/OpenXLSX/OpenXLSX/headers/XLConstants.hpp +18 -0
- package/thirdparty/OpenXLSX/OpenXLSX/headers/XLContentTypes.hpp +264 -0
- package/thirdparty/OpenXLSX/OpenXLSX/headers/XLDateTime.hpp +175 -0
- package/thirdparty/OpenXLSX/OpenXLSX/headers/XLDocument.hpp +505 -0
- package/thirdparty/OpenXLSX/OpenXLSX/headers/XLDrawing.hpp +452 -0
- package/thirdparty/OpenXLSX/OpenXLSX/headers/XLException.hpp +161 -0
- package/thirdparty/OpenXLSX/OpenXLSX/headers/XLFormula.hpp +375 -0
- package/thirdparty/OpenXLSX/OpenXLSX/headers/XLIterator.hpp +40 -0
- package/thirdparty/OpenXLSX/OpenXLSX/headers/XLMergeCells.hpp +216 -0
- package/thirdparty/OpenXLSX/OpenXLSX/headers/XLProperties.hpp +348 -0
- package/thirdparty/OpenXLSX/OpenXLSX/headers/XLRelationships.hpp +348 -0
- package/thirdparty/OpenXLSX/OpenXLSX/headers/XLRow.hpp +721 -0
- package/thirdparty/OpenXLSX/OpenXLSX/headers/XLRowData.hpp +488 -0
- package/thirdparty/OpenXLSX/OpenXLSX/headers/XLSharedStrings.hpp +204 -0
- package/thirdparty/OpenXLSX/OpenXLSX/headers/XLSheet.hpp +1640 -0
- package/thirdparty/OpenXLSX/OpenXLSX/headers/XLStyles.hpp +2390 -0
- package/thirdparty/OpenXLSX/OpenXLSX/headers/XLTables.hpp +132 -0
- package/thirdparty/OpenXLSX/OpenXLSX/headers/XLWorkbook.hpp +404 -0
- package/thirdparty/OpenXLSX/OpenXLSX/headers/XLXmlData.hpp +283 -0
- package/thirdparty/OpenXLSX/OpenXLSX/headers/XLXmlFile.hpp +194 -0
- package/thirdparty/OpenXLSX/OpenXLSX/headers/XLXmlParser.hpp +282 -0
- package/thirdparty/OpenXLSX/OpenXLSX/headers/XLXmlParserForwardDeclarations.hpp +85 -0
- package/thirdparty/OpenXLSX/OpenXLSX/headers/XLZipArchive.hpp +232 -0
- package/thirdparty/OpenXLSX/OpenXLSX/headers/detail/LibZip.hpp +447 -0
- package/thirdparty/OpenXLSX/OpenXLSX/headers/detail/OpenXLSXFileSystemTools.hpp +207 -0
- package/thirdparty/OpenXLSX/OpenXLSX/headers/detail/Zippy.hpp +1375 -0
- package/thirdparty/OpenXLSX/OpenXLSX/sources/XLCell.cpp +340 -0
- package/thirdparty/OpenXLSX/OpenXLSX/sources/XLCellIterator.cpp +427 -0
- package/thirdparty/OpenXLSX/OpenXLSX/sources/XLCellRange.cpp +221 -0
- package/thirdparty/OpenXLSX/OpenXLSX/sources/XLCellReference.cpp +408 -0
- package/thirdparty/OpenXLSX/OpenXLSX/sources/XLCellValue.cpp +519 -0
- package/thirdparty/OpenXLSX/OpenXLSX/sources/XLColor.cpp +202 -0
- package/thirdparty/OpenXLSX/OpenXLSX/sources/XLColumn.cpp +141 -0
- package/thirdparty/OpenXLSX/OpenXLSX/sources/XLComments.cpp +637 -0
- package/thirdparty/OpenXLSX/OpenXLSX/sources/XLContentTypes.cpp +329 -0
- package/thirdparty/OpenXLSX/OpenXLSX/sources/XLDateTime.cpp +269 -0
- package/thirdparty/OpenXLSX/OpenXLSX/sources/XLDocument.cpp +1678 -0
- package/thirdparty/OpenXLSX/OpenXLSX/sources/XLDrawing.cpp +784 -0
- package/thirdparty/OpenXLSX/OpenXLSX/sources/XLFormula.cpp +201 -0
- package/thirdparty/OpenXLSX/OpenXLSX/sources/XLMergeCells.cpp +357 -0
- package/thirdparty/OpenXLSX/OpenXLSX/sources/XLProperties.cpp +810 -0
- package/thirdparty/OpenXLSX/OpenXLSX/sources/XLRelationships.cpp +529 -0
- package/thirdparty/OpenXLSX/OpenXLSX/sources/XLRow.cpp +918 -0
- package/thirdparty/OpenXLSX/OpenXLSX/sources/XLRowData.cpp +508 -0
- package/thirdparty/OpenXLSX/OpenXLSX/sources/XLSharedStrings.cpp +220 -0
- package/thirdparty/OpenXLSX/OpenXLSX/sources/XLSheet.cpp +1970 -0
- package/thirdparty/OpenXLSX/OpenXLSX/sources/XLStyles.cpp +3014 -0
- package/thirdparty/OpenXLSX/OpenXLSX/sources/XLTables.cpp +125 -0
- package/thirdparty/OpenXLSX/OpenXLSX/sources/XLWorkbook.cpp +784 -0
- package/thirdparty/OpenXLSX/OpenXLSX/sources/XLXmlData.cpp +247 -0
- package/thirdparty/OpenXLSX/OpenXLSX/sources/XLXmlFile.cpp +160 -0
- package/thirdparty/OpenXLSX/OpenXLSX/sources/XLXmlParser.cpp +261 -0
- package/thirdparty/OpenXLSX/OpenXLSX/sources/XLZipArchive.cpp +266 -0
- package/thirdparty/OpenXLSX/OpenXLSX/sources/utilities/XLUtilities.hpp +478 -0
- package/thirdparty/OpenXLSX/README.md +1279 -0
- package/thirdparty/OpenXLSX/Scripts/cmake-cleanup.sh +102 -0
- package/thirdparty/OpenXLSX/Scripts/compile.sh +23 -0
- package/thirdparty/OpenXLSX/Scripts/demos-cleanup.sh +25 -0
- package/thirdparty/OpenXLSX/Scripts/demos-run-all.sh +60 -0
- package/thirdparty/OpenXLSX/Scripts/guarded-xml-format.sh +47 -0
- package/thirdparty/OpenXLSX/Scripts/make-gnu.sh +11 -0
- package/thirdparty/OpenXLSX/Scripts/uninstall-manifest.sh +48 -0
- package/thirdparty/OpenXLSX/Scripts/xml-format-all.sh +19 -0
- package/thirdparty/OpenXLSX/Scripts/xml-format.sh +38 -0
- package/thirdparty/OpenXLSX/Tests/CMakeLists.txt +37 -0
- package/thirdparty/OpenXLSX/Tests/catch/catch.hpp +17799 -0
- package/thirdparty/OpenXLSX/Tests/catch/catch_reporter_automake.hpp +68 -0
- package/thirdparty/OpenXLSX/Tests/catch/catch_reporter_sonarqube.hpp +182 -0
- package/thirdparty/OpenXLSX/Tests/catch/catch_reporter_tap.hpp +278 -0
- package/thirdparty/OpenXLSX/Tests/catch/catch_reporter_teamcity.hpp +212 -0
- package/thirdparty/OpenXLSX/Tests/main.cpp +55 -0
- package/thirdparty/OpenXLSX/Tests/testXLCell.cpp +176 -0
- package/thirdparty/OpenXLSX/Tests/testXLCellRange.cpp +180 -0
- package/thirdparty/OpenXLSX/Tests/testXLCellReference.cpp +160 -0
- package/thirdparty/OpenXLSX/Tests/testXLCellValue.cpp +366 -0
- package/thirdparty/OpenXLSX/Tests/testXLCellValueProxy.cpp +255 -0
- package/thirdparty/OpenXLSX/Tests/testXLColor.cpp +75 -0
- package/thirdparty/OpenXLSX/Tests/testXLDateTime.cpp +229 -0
- package/thirdparty/OpenXLSX/Tests/testXLDocument.cpp +197 -0
- package/thirdparty/OpenXLSX/Tests/testXLFormula.cpp +131 -0
- package/thirdparty/OpenXLSX/Tests/testXLRow.cpp +597 -0
- package/thirdparty/OpenXLSX/Tests/testXLSheet.cpp +77 -0
- package/thirdparty/OpenXLSX/Tests/ziptest-with-libs.cpp +88 -0
- package/thirdparty/OpenXLSX/Tests/ziptest.cpp +735 -0
- package/thirdparty/OpenXLSX/cmake/get-git-HEAD-ID.cmake +45 -0
- package/thirdparty/OpenXLSX/cmake/libopenxlsxCPackOptions.cmake.in +20 -0
- package/thirdparty/OpenXLSX/cmake/manage_dependency.cmake +813 -0
- package/thirdparty/OpenXLSX/cmake/recursive_library_type_test.cmake +52 -0
- package/thirdparty/OpenXLSX/gnu-make-crutch/OpenXLSX-Exports-shared.hpp +42 -0
- package/thirdparty/OpenXLSX/gnu-make-crutch/OpenXLSX-Exports-static.hpp +42 -0
- package/thirdparty/OpenXLSX/vcpkg.json +24 -0
- package/thirdparty/README.md +42 -0
- package/thirdparty/miniz/.clang-format +47 -0
- package/thirdparty/miniz/.travis.yml +2 -0
- package/thirdparty/miniz/CMakeLists.txt +333 -0
- package/thirdparty/miniz/CMakePresets.json +31 -0
- package/thirdparty/miniz/ChangeLog.md +272 -0
- package/thirdparty/miniz/Config.cmake.in +1 -0
- package/thirdparty/miniz/LICENSE +22 -0
- package/thirdparty/miniz/amalgamate.sh +57 -0
- package/thirdparty/miniz/examples/example1.c +105 -0
- package/thirdparty/miniz/examples/example2.c +164 -0
- package/thirdparty/miniz/examples/example3.c +269 -0
- package/thirdparty/miniz/examples/example4.c +102 -0
- package/thirdparty/miniz/examples/example5.c +327 -0
- package/thirdparty/miniz/examples/example6.c +189 -0
- package/thirdparty/miniz/meson.build +21 -0
- package/thirdparty/miniz/miniz.c +646 -0
- package/thirdparty/miniz/miniz.h +615 -0
- package/thirdparty/miniz/miniz.pc.in +13 -0
- package/thirdparty/miniz/miniz_common.h +89 -0
- package/thirdparty/miniz/miniz_export.h +20 -0
- package/thirdparty/miniz/miniz_tdef.c +1602 -0
- package/thirdparty/miniz/miniz_tdef.h +199 -0
- package/thirdparty/miniz/miniz_tinfl.c +778 -0
- package/thirdparty/miniz/miniz_tinfl.h +150 -0
- package/thirdparty/miniz/miniz_zip.c +4895 -0
- package/thirdparty/miniz/miniz_zip.h +454 -0
- package/thirdparty/miniz/readme.md +46 -0
- package/thirdparty/miniz/test.sh +36 -0
- package/thirdparty/miniz/tests/add_in_place_fuzzer.c +87 -0
- package/thirdparty/miniz/tests/catch_amalgamated.cpp +10795 -0
- package/thirdparty/miniz/tests/catch_amalgamated.hpp +13143 -0
- package/thirdparty/miniz/tests/checksum_fuzzer.c +26 -0
- package/thirdparty/miniz/tests/compress_fuzzer.c +91 -0
- package/thirdparty/miniz/tests/flush_fuzzer.c +88 -0
- package/thirdparty/miniz/tests/fuzz_main.c +55 -0
- package/thirdparty/miniz/tests/large_fuzzer.c +133 -0
- package/thirdparty/miniz/tests/main.cpp +185 -0
- package/thirdparty/miniz/tests/miniz_tdefl_fuzzer.cc +115 -0
- package/thirdparty/miniz/tests/miniz_tester.cpp +1881 -0
- package/thirdparty/miniz/tests/miniz_zip_validate_fuzzer.cc +71 -0
- package/thirdparty/miniz/tests/miniz_zip_writer_fuzzer.cc +96 -0
- package/thirdparty/miniz/tests/ossfuzz.sh +48 -0
- package/thirdparty/miniz/tests/small_fuzzer.c +126 -0
- package/thirdparty/miniz/tests/timer.cpp +152 -0
- package/thirdparty/miniz/tests/timer.h +40 -0
- package/thirdparty/miniz/tests/uncompress2_fuzzer.c +21 -0
- package/thirdparty/miniz/tests/uncompress_fuzzer.c +30 -0
- package/thirdparty/miniz/tests/zip.dict +9 -0
- package/thirdparty/miniz/tests/zip_fuzzer.c +63 -0
- package/thirdparty/pugixml/.codecov.yml +7 -0
- package/thirdparty/pugixml/.gitattributes +1 -0
- package/thirdparty/pugixml/CMakeLists.txt +330 -0
- package/thirdparty/pugixml/LICENSE.md +24 -0
- package/thirdparty/pugixml/Makefile +98 -0
- package/thirdparty/pugixml/README.md +81 -0
- package/thirdparty/pugixml/SECURITY.md +9 -0
- package/thirdparty/pugixml/appveyor.yml +50 -0
- package/thirdparty/pugixml/docs/config.adoc +7 -0
- package/thirdparty/pugixml/docs/images/dom_tree.png +0 -0
- package/thirdparty/pugixml/docs/images/vs2005_link1.png +0 -0
- package/thirdparty/pugixml/docs/images/vs2005_link2.png +0 -0
- package/thirdparty/pugixml/docs/images/vs2005_pch1.png +0 -0
- package/thirdparty/pugixml/docs/images/vs2005_pch2.png +0 -0
- package/thirdparty/pugixml/docs/images/vs2005_pch3.png +0 -0
- package/thirdparty/pugixml/docs/images/vs2005_pch4.png +0 -0
- package/thirdparty/pugixml/docs/images/vs2010_link1.png +0 -0
- package/thirdparty/pugixml/docs/images/vs2010_link2.png +0 -0
- package/thirdparty/pugixml/docs/manual.adoc +3326 -0
- package/thirdparty/pugixml/docs/manual.html +6395 -0
- package/thirdparty/pugixml/docs/quickstart.adoc +287 -0
- package/thirdparty/pugixml/docs/quickstart.html +1107 -0
- package/thirdparty/pugixml/docs/samples/character.xml +8 -0
- package/thirdparty/pugixml/docs/samples/custom_memory_management.cpp +27 -0
- package/thirdparty/pugixml/docs/samples/include.cpp +64 -0
- package/thirdparty/pugixml/docs/samples/load_error_handling.cpp +33 -0
- package/thirdparty/pugixml/docs/samples/load_file.cpp +16 -0
- package/thirdparty/pugixml/docs/samples/load_memory.cpp +66 -0
- package/thirdparty/pugixml/docs/samples/load_options.cpp +30 -0
- package/thirdparty/pugixml/docs/samples/load_stream.cpp +97 -0
- package/thirdparty/pugixml/docs/samples/modify_add.cpp +29 -0
- package/thirdparty/pugixml/docs/samples/modify_base.cpp +43 -0
- package/thirdparty/pugixml/docs/samples/modify_remove.cpp +27 -0
- package/thirdparty/pugixml/docs/samples/save_custom_writer.cpp +116 -0
- package/thirdparty/pugixml/docs/samples/save_declaration.cpp +27 -0
- package/thirdparty/pugixml/docs/samples/save_file.cpp +17 -0
- package/thirdparty/pugixml/docs/samples/save_options.cpp +48 -0
- package/thirdparty/pugixml/docs/samples/save_stream.cpp +18 -0
- package/thirdparty/pugixml/docs/samples/save_subtree.cpp +26 -0
- package/thirdparty/pugixml/docs/samples/text.cpp +35 -0
- package/thirdparty/pugixml/docs/samples/transitions.xml +7 -0
- package/thirdparty/pugixml/docs/samples/traverse_base.cpp +51 -0
- package/thirdparty/pugixml/docs/samples/traverse_iter.cpp +27 -0
- package/thirdparty/pugixml/docs/samples/traverse_predicate.cpp +48 -0
- package/thirdparty/pugixml/docs/samples/traverse_rangefor.cpp +32 -0
- package/thirdparty/pugixml/docs/samples/traverse_walker.cpp +35 -0
- package/thirdparty/pugixml/docs/samples/tree.xml +12 -0
- package/thirdparty/pugixml/docs/samples/weekly-shift_jis.xml +78 -0
- package/thirdparty/pugixml/docs/samples/weekly-utf-16.xml +0 -0
- package/thirdparty/pugixml/docs/samples/weekly-utf-8.xml +78 -0
- package/thirdparty/pugixml/docs/samples/xgconsole.xml +12 -0
- package/thirdparty/pugixml/docs/samples/xpath_error.cpp +43 -0
- package/thirdparty/pugixml/docs/samples/xpath_query.cpp +36 -0
- package/thirdparty/pugixml/docs/samples/xpath_select.cpp +28 -0
- package/thirdparty/pugixml/docs/samples/xpath_variables.cpp +38 -0
- package/thirdparty/pugixml/readme.txt +50 -0
- package/thirdparty/pugixml/scripts/archive.py +54 -0
- package/thirdparty/pugixml/scripts/cocoapods_push.sh +4 -0
- package/thirdparty/pugixml/scripts/natvis/pugixml.natvis +77 -0
- package/thirdparty/pugixml/scripts/natvis/pugixml_compact.natvis +506 -0
- package/thirdparty/pugixml/scripts/nuget/build/native/pugixml-propertiesui.xml +17 -0
- package/thirdparty/pugixml/scripts/nuget/build/native/pugixml.targets +28 -0
- package/thirdparty/pugixml/scripts/nuget/pugixml.nuspec +20 -0
- package/thirdparty/pugixml/scripts/nuget_build.ps1 +60 -0
- package/thirdparty/pugixml/scripts/premake4.lua +92 -0
- package/thirdparty/pugixml/scripts/pugixml-config.cmake.in +12 -0
- package/thirdparty/pugixml/scripts/pugixml.pc.in +11 -0
- package/thirdparty/pugixml/scripts/pugixml.podspec +14 -0
- package/thirdparty/pugixml/scripts/pugixml.xcodeproj/project.pbxproj +212 -0
- package/thirdparty/pugixml/scripts/pugixml_airplay.mkf +13 -0
- package/thirdparty/pugixml/scripts/pugixml_codeblocks.cbp +44 -0
- package/thirdparty/pugixml/scripts/pugixml_codelite.project +56 -0
- package/thirdparty/pugixml/scripts/pugixml_dll.rc +45 -0
- package/thirdparty/pugixml/scripts/pugixml_vs2005.vcproj +343 -0
- package/thirdparty/pugixml/scripts/pugixml_vs2005_static.vcproj +343 -0
- package/thirdparty/pugixml/scripts/pugixml_vs2008.vcproj +339 -0
- package/thirdparty/pugixml/scripts/pugixml_vs2008_static.vcproj +339 -0
- package/thirdparty/pugixml/scripts/pugixml_vs2010.vcxproj +191 -0
- package/thirdparty/pugixml/scripts/pugixml_vs2010_static.vcxproj +191 -0
- package/thirdparty/pugixml/scripts/pugixml_vs2013.vcxproj +199 -0
- package/thirdparty/pugixml/scripts/pugixml_vs2013_static.vcxproj +199 -0
- package/thirdparty/pugixml/scripts/pugixml_vs2015.vcxproj +172 -0
- package/thirdparty/pugixml/scripts/pugixml_vs2015_static.vcxproj +176 -0
- package/thirdparty/pugixml/scripts/pugixml_vs2017.vcxproj +172 -0
- package/thirdparty/pugixml/scripts/pugixml_vs2017_static.vcxproj +176 -0
- package/thirdparty/pugixml/scripts/pugixml_vs2019.vcxproj +176 -0
- package/thirdparty/pugixml/scripts/pugixml_vs2019_static.vcxproj +180 -0
- package/thirdparty/pugixml/scripts/pugixml_vs2022.vcxproj +176 -0
- package/thirdparty/pugixml/scripts/pugixml_vs2022_static.vcxproj +180 -0
- package/thirdparty/pugixml/scripts/pugixml_vs2026.vcxproj +176 -0
- package/thirdparty/pugixml/scripts/pugixml_vs2026_static.vcxproj +180 -0
- package/thirdparty/pugixml/scripts/sbom.cdx.json +47 -0
- package/thirdparty/pugixml/src/pugiconfig.hpp +81 -0
- package/thirdparty/pugixml/src/pugixml.cpp +13776 -0
- package/thirdparty/pugixml/src/pugixml.hpp +1604 -0
- package/thirdparty/pugixml/tests/allocator.cpp +175 -0
- package/thirdparty/pugixml/tests/allocator.hpp +10 -0
- package/thirdparty/pugixml/tests/autotest-appveyor.ps1 +93 -0
- package/thirdparty/pugixml/tests/cmake-appveyor.ps1 +57 -0
- package/thirdparty/pugixml/tests/data/empty.xml +0 -0
- package/thirdparty/pugixml/tests/data/large.xml +1 -0
- package/thirdparty/pugixml/tests/data/latintest_latin1.xml +1 -0
- package/thirdparty/pugixml/tests/data/latintest_utf8.xml +1 -0
- package/thirdparty/pugixml/tests/data/multiline.xml +3 -0
- package/thirdparty/pugixml/tests/data/small.xml +1 -0
- package/thirdparty/pugixml/tests/data/truncation.xml +19 -0
- package/thirdparty/pugixml/tests/data/utftest_utf16_be.xml +0 -0
- package/thirdparty/pugixml/tests/data/utftest_utf16_be_bom.xml +0 -0
- package/thirdparty/pugixml/tests/data/utftest_utf16_be_clean.xml +0 -0
- package/thirdparty/pugixml/tests/data/utftest_utf16_be_nodecl.xml +0 -0
- package/thirdparty/pugixml/tests/data/utftest_utf16_le.xml +0 -0
- package/thirdparty/pugixml/tests/data/utftest_utf16_le_bom.xml +0 -0
- package/thirdparty/pugixml/tests/data/utftest_utf16_le_clean.xml +0 -0
- package/thirdparty/pugixml/tests/data/utftest_utf16_le_nodecl.xml +0 -0
- package/thirdparty/pugixml/tests/data/utftest_utf32_be.xml +0 -0
- package/thirdparty/pugixml/tests/data/utftest_utf32_be_bom.xml +0 -0
- package/thirdparty/pugixml/tests/data/utftest_utf32_be_clean.xml +0 -0
- package/thirdparty/pugixml/tests/data/utftest_utf32_be_nodecl.xml +0 -0
- package/thirdparty/pugixml/tests/data/utftest_utf32_le.xml +0 -0
- package/thirdparty/pugixml/tests/data/utftest_utf32_le_bom.xml +0 -0
- package/thirdparty/pugixml/tests/data/utftest_utf32_le_clean.xml +0 -0
- package/thirdparty/pugixml/tests/data/utftest_utf32_le_nodecl.xml +0 -0
- package/thirdparty/pugixml/tests/data/utftest_utf8.xml +87 -0
- package/thirdparty/pugixml/tests/data/utftest_utf8_bom.xml +87 -0
- package/thirdparty/pugixml/tests/data/utftest_utf8_clean.xml +84 -0
- package/thirdparty/pugixml/tests/data/utftest_utf8_nodecl.xml +86 -0
- package/thirdparty/pugixml/tests/data//321/202/320/265/321/201/321/202.xml +1 -0
- package/thirdparty/pugixml/tests/data_fuzz_parse/basic.xml +1 -0
- package/thirdparty/pugixml/tests/data_fuzz_parse/doctype.xml +1 -0
- package/thirdparty/pugixml/tests/data_fuzz_parse/refs.xml +1 -0
- package/thirdparty/pugixml/tests/data_fuzz_parse/types.xml +1 -0
- package/thirdparty/pugixml/tests/data_fuzz_parse/utf16.xml +0 -0
- package/thirdparty/pugixml/tests/data_fuzz_parse/utf32.xml +0 -0
- package/thirdparty/pugixml/tests/data_fuzz_xpath/basic.xpath +1 -0
- package/thirdparty/pugixml/tests/data_fuzz_xpath/functions.xpath +1 -0
- package/thirdparty/pugixml/tests/data_fuzz_xpath/math.xpath +1 -0
- package/thirdparty/pugixml/tests/data_fuzz_xpath/path.xpath +1 -0
- package/thirdparty/pugixml/tests/data_fuzz_xpath/predicate.xpath +1 -0
- package/thirdparty/pugixml/tests/fuzz_parse.cpp +14 -0
- package/thirdparty/pugixml/tests/fuzz_parse.dict +72 -0
- package/thirdparty/pugixml/tests/fuzz_setup.sh +11 -0
- package/thirdparty/pugixml/tests/fuzz_xpath.cpp +46 -0
- package/thirdparty/pugixml/tests/fuzz_xpath.dict +72 -0
- package/thirdparty/pugixml/tests/helpers.hpp +114 -0
- package/thirdparty/pugixml/tests/main.cpp +218 -0
- package/thirdparty/pugixml/tests/test.cpp +220 -0
- package/thirdparty/pugixml/tests/test.hpp +175 -0
- package/thirdparty/pugixml/tests/test_compact.cpp +150 -0
- package/thirdparty/pugixml/tests/test_deprecated.cpp +25 -0
- package/thirdparty/pugixml/tests/test_document.cpp +1910 -0
- package/thirdparty/pugixml/tests/test_dom_modify.cpp +2122 -0
- package/thirdparty/pugixml/tests/test_dom_text.cpp +601 -0
- package/thirdparty/pugixml/tests/test_dom_traverse.cpp +1481 -0
- package/thirdparty/pugixml/tests/test_header_guard.cpp +3 -0
- package/thirdparty/pugixml/tests/test_header_iosfwd_1.cpp +3 -0
- package/thirdparty/pugixml/tests/test_header_iosfwd_2.cpp +3 -0
- package/thirdparty/pugixml/tests/test_header_iostream_1.cpp +3 -0
- package/thirdparty/pugixml/tests/test_header_iostream_2.cpp +3 -0
- package/thirdparty/pugixml/tests/test_header_only_1.cpp +21 -0
- package/thirdparty/pugixml/tests/test_header_only_2.cpp +21 -0
- package/thirdparty/pugixml/tests/test_header_string_1.cpp +3 -0
- package/thirdparty/pugixml/tests/test_header_string_2.cpp +3 -0
- package/thirdparty/pugixml/tests/test_header_string_iostream.cpp +5 -0
- package/thirdparty/pugixml/tests/test_memory.cpp +303 -0
- package/thirdparty/pugixml/tests/test_parse.cpp +1463 -0
- package/thirdparty/pugixml/tests/test_parse_doctype.cpp +366 -0
- package/thirdparty/pugixml/tests/test_unicode.cpp +152 -0
- package/thirdparty/pugixml/tests/test_version.cpp +5 -0
- package/thirdparty/pugixml/tests/test_write.cpp +757 -0
- package/thirdparty/pugixml/tests/test_xpath.cpp +833 -0
- package/thirdparty/pugixml/tests/test_xpath_api.cpp +646 -0
- package/thirdparty/pugixml/tests/test_xpath_functions.cpp +844 -0
- package/thirdparty/pugixml/tests/test_xpath_operators.cpp +566 -0
- package/thirdparty/pugixml/tests/test_xpath_parse.cpp +428 -0
- package/thirdparty/pugixml/tests/test_xpath_paths.cpp +787 -0
- package/thirdparty/pugixml/tests/test_xpath_paths_abbrev_w3c.cpp +219 -0
- package/thirdparty/pugixml/tests/test_xpath_paths_w3c.cpp +312 -0
- package/thirdparty/pugixml/tests/test_xpath_variables.cpp +683 -0
- package/thirdparty/pugixml/tests/test_xpath_xalan_1.cpp +411 -0
- package/thirdparty/pugixml/tests/test_xpath_xalan_2.cpp +417 -0
- package/thirdparty/pugixml/tests/test_xpath_xalan_3.cpp +321 -0
- package/thirdparty/pugixml/tests/test_xpath_xalan_4.cpp +300 -0
- package/thirdparty/pugixml/tests/test_xpath_xalan_5.cpp +295 -0
- package/thirdparty/pugixml/tests/writer_string.cpp +79 -0
- package/thirdparty/pugixml/tests/writer_string.hpp +27 -0
- package/ios/dummy.swift +0 -2
- package/scripts/prepare-openxlsx.js +0 -109
|
@@ -0,0 +1,1970 @@
|
|
|
1
|
+
/*
|
|
2
|
+
|
|
3
|
+
____ ____ ___ ____ ____ ____ ___
|
|
4
|
+
6MMMMb `MM( )M' `MM' 6MMMMb\`MM( )M'
|
|
5
|
+
8P Y8 `MM. d' MM 6M' ` `MM. d'
|
|
6
|
+
6M Mb __ ____ ____ ___ __ `MM. d' MM MM `MM. d'
|
|
7
|
+
MM MM `M6MMMMb 6MMMMb `MM 6MMb `MM. d' MM YM. `MM. d'
|
|
8
|
+
MM MM MM' `Mb 6M' `Mb MMM9 `Mb `MMd MM YMMMMb `MMd
|
|
9
|
+
MM MM MM MM MM MM MM' MM dMM. MM `Mb dMM.
|
|
10
|
+
MM MM MM MM MMMMMMMM MM MM d'`MM. MM MM d'`MM.
|
|
11
|
+
YM M9 MM MM MM MM MM d' `MM. MM MM d' `MM.
|
|
12
|
+
8b d8 MM. ,M9 YM d9 MM MM d' `MM. MM / L ,M9 d' `MM.
|
|
13
|
+
YMMMM9 MMYMMM9 YMMMM9 _MM_ _MM_M(_ _)MM_ _MMMMMMM MYMMMM9 _M(_ _)MM_
|
|
14
|
+
MM
|
|
15
|
+
MM
|
|
16
|
+
_MM_
|
|
17
|
+
|
|
18
|
+
Copyright (c) 2018, Kenneth Troldal Balslev
|
|
19
|
+
|
|
20
|
+
All rights reserved.
|
|
21
|
+
|
|
22
|
+
Redistribution and use in source and binary forms, with or without
|
|
23
|
+
modification, are permitted provided that the following conditions are met:
|
|
24
|
+
- Redistributions of source code must retain the above copyright
|
|
25
|
+
notice, this list of conditions and the following disclaimer.
|
|
26
|
+
- Redistributions in binary form must reproduce the above copyright
|
|
27
|
+
notice, this list of conditions and the following disclaimer in the
|
|
28
|
+
documentation and/or other materials provided with the distribution.
|
|
29
|
+
- Neither the name of the author nor the
|
|
30
|
+
names of any contributors may be used to endorse or promote products
|
|
31
|
+
derived from this software without specific prior written permission.
|
|
32
|
+
|
|
33
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
34
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
35
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
36
|
+
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
|
37
|
+
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
38
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
39
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
40
|
+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
41
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
42
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
43
|
+
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
// ===== External Includes ===== //
|
|
47
|
+
#include <algorithm> // std::max
|
|
48
|
+
#include <cctype> // std::isdigit (issue #330)
|
|
49
|
+
#include <cstring> // strlen
|
|
50
|
+
#include <limits> // std::numeric_limits
|
|
51
|
+
#include <map> // std::multimap
|
|
52
|
+
|
|
53
|
+
// ===== OpenXLSX Includes ===== //
|
|
54
|
+
#include "XLCellRange.hpp"
|
|
55
|
+
#include "XLDocument.hpp"
|
|
56
|
+
#include "XLMergeCells.hpp"
|
|
57
|
+
#include "XLSheet.hpp"
|
|
58
|
+
#include "XLXmlParser.hpp" // pugixml wrapper
|
|
59
|
+
#include "utilities/XLUtilities.hpp"
|
|
60
|
+
|
|
61
|
+
using namespace OpenXLSX;
|
|
62
|
+
|
|
63
|
+
namespace OpenXLSX
|
|
64
|
+
{
|
|
65
|
+
// // Forward declaration. Implementation is in the XLUtilities.hpp file
|
|
66
|
+
// XMLNode getRowNode(XMLNode sheetDataNode, uint32_t rowNumber);
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* @brief Function for setting tab color.
|
|
70
|
+
* @param xmlDocument XMLDocument object
|
|
71
|
+
* @param color Thr color to set
|
|
72
|
+
*/
|
|
73
|
+
void setTabColor(const XMLDocument& xmlDocument, const XLColor& color)
|
|
74
|
+
{
|
|
75
|
+
if (!xmlDocument.document_element().child("sheetPr")) xmlDocument.document_element().prepend_child("sheetPr");
|
|
76
|
+
|
|
77
|
+
if (!xmlDocument.document_element().child("sheetPr").child("tabColor"))
|
|
78
|
+
xmlDocument.document_element().child("sheetPr").prepend_child("tabColor");
|
|
79
|
+
|
|
80
|
+
auto colorNode = xmlDocument.document_element().child("sheetPr").child("tabColor");
|
|
81
|
+
for (auto attr : colorNode.attributes()) colorNode.remove_attribute(attr);
|
|
82
|
+
|
|
83
|
+
colorNode.prepend_attribute("rgb").set_value(color.hex().c_str());
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* @brief Set the tab selected property to desired value
|
|
88
|
+
* @param xmlDocument
|
|
89
|
+
* @param selected
|
|
90
|
+
*/
|
|
91
|
+
void setTabSelected(const XMLDocument& xmlDocument, bool selected)
|
|
92
|
+
{ // 2024-04-30: whitespace support
|
|
93
|
+
unsigned int value = (selected ? 1 : 0);
|
|
94
|
+
XMLNode sheetView = xmlDocument.document_element().child("sheetViews").first_child_of_type(pugi::node_element);
|
|
95
|
+
XMLAttribute tabSelected = sheetView.attribute("tabSelected");
|
|
96
|
+
if (tabSelected.empty())
|
|
97
|
+
tabSelected = sheetView.prepend_attribute("tabSelected"); // BUGFIX 2025-03-15 issue #337: assign tabSelected value with newly created attribute if it didn't exist
|
|
98
|
+
tabSelected.set_value(value);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* @brief Function for checking if the tab is selected.
|
|
103
|
+
* @param xmlDocument
|
|
104
|
+
* @return
|
|
105
|
+
* @note pugi::xml_attribute::as_bool defaults to false for a non-existing (= empty) attribute
|
|
106
|
+
*/
|
|
107
|
+
bool tabIsSelected(const XMLDocument& xmlDocument)
|
|
108
|
+
{ // 2024-04-30: whitespace support
|
|
109
|
+
return xmlDocument.document_element()
|
|
110
|
+
.child("sheetViews")
|
|
111
|
+
.first_child_of_type(pugi::node_element)
|
|
112
|
+
.attribute("tabSelected")
|
|
113
|
+
.as_bool(); // BUGFIX 2024-05-01: .value() "0" was evaluating to true
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* @brief get the correct XLCfType from the OOXML cfRule type attribute string
|
|
119
|
+
* @param typeString the string as used in the OOXML
|
|
120
|
+
* @return the corresponding XLCfType enum value
|
|
121
|
+
*/
|
|
122
|
+
XLCfType XLCfTypeFromString(std::string const& typeString)
|
|
123
|
+
{
|
|
124
|
+
if (typeString == "expression") return XLCfType::Expression;
|
|
125
|
+
if (typeString == "cellIs") return XLCfType::CellIs;
|
|
126
|
+
if (typeString == "colorScale") return XLCfType::ColorScale;
|
|
127
|
+
if (typeString == "dataBar") return XLCfType::DataBar;
|
|
128
|
+
if (typeString == "iconSet") return XLCfType::IconSet;
|
|
129
|
+
if (typeString == "top10") return XLCfType::Top10;
|
|
130
|
+
if (typeString == "uniqueValues") return XLCfType::UniqueValues;
|
|
131
|
+
if (typeString == "duplicateValues") return XLCfType::DuplicateValues;
|
|
132
|
+
if (typeString == "containsText") return XLCfType::ContainsText;
|
|
133
|
+
if (typeString == "notContainsText") return XLCfType::NotContainsText;
|
|
134
|
+
if (typeString == "beginsWith") return XLCfType::BeginsWith;
|
|
135
|
+
if (typeString == "endsWith") return XLCfType::EndsWith;
|
|
136
|
+
if (typeString == "containsBlanks") return XLCfType::ContainsBlanks;
|
|
137
|
+
if (typeString == "notContainsBlanks") return XLCfType::NotContainsBlanks;
|
|
138
|
+
if (typeString == "containsErrors") return XLCfType::ContainsErrors;
|
|
139
|
+
if (typeString == "notContainsErrors") return XLCfType::NotContainsErrors;
|
|
140
|
+
if (typeString == "timePeriod") return XLCfType::TimePeriod;
|
|
141
|
+
if (typeString == "aboveAverage") return XLCfType::AboveAverage;
|
|
142
|
+
return XLCfType::Invalid;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* @brief inverse of XLCfTypeFromString
|
|
147
|
+
* @param cfType the type for which to get the OOXML string
|
|
148
|
+
*/
|
|
149
|
+
std::string XLCfTypeToString(XLCfType cfType)
|
|
150
|
+
{
|
|
151
|
+
switch (cfType) {
|
|
152
|
+
case XLCfType::Expression: return "expression";
|
|
153
|
+
case XLCfType::CellIs: return "cellIs";
|
|
154
|
+
case XLCfType::ColorScale: return "colorScale";
|
|
155
|
+
case XLCfType::DataBar: return "dataBar";
|
|
156
|
+
case XLCfType::IconSet: return "iconSet";
|
|
157
|
+
case XLCfType::Top10: return "top10";
|
|
158
|
+
case XLCfType::UniqueValues: return "uniqueValues";
|
|
159
|
+
case XLCfType::DuplicateValues: return "duplicateValues";
|
|
160
|
+
case XLCfType::ContainsText: return "containsText";
|
|
161
|
+
case XLCfType::NotContainsText: return "notContainsText";
|
|
162
|
+
case XLCfType::BeginsWith: return "beginsWith";
|
|
163
|
+
case XLCfType::EndsWith: return "endsWith";
|
|
164
|
+
case XLCfType::ContainsBlanks: return "containsBlanks";
|
|
165
|
+
case XLCfType::NotContainsBlanks: return "notContainsBlanks";
|
|
166
|
+
case XLCfType::ContainsErrors: return "containsErrors";
|
|
167
|
+
case XLCfType::NotContainsErrors: return "notContainsErrors";
|
|
168
|
+
case XLCfType::TimePeriod: return "timePeriod";
|
|
169
|
+
case XLCfType::AboveAverage: return "aboveAverage";
|
|
170
|
+
case XLCfType::Invalid: [[fallthrough]];
|
|
171
|
+
default: return "(invalid)";
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* @brief get the correct XLCfOperator from the OOXML cfRule operator attribute string
|
|
177
|
+
* @param operatorString the string as used in the OOXML
|
|
178
|
+
* @return the corresponding XLCfOperator enum value
|
|
179
|
+
*/
|
|
180
|
+
XLCfOperator XLCfOperatorFromString(std::string const& operatorString)
|
|
181
|
+
{
|
|
182
|
+
if (operatorString == "lessThan") return XLCfOperator::LessThan;
|
|
183
|
+
if (operatorString == "lessThanOrEqual") return XLCfOperator::LessThanOrEqual;
|
|
184
|
+
if (operatorString == "equal") return XLCfOperator::Equal;
|
|
185
|
+
if (operatorString == "notEqual") return XLCfOperator::NotEqual;
|
|
186
|
+
if (operatorString == "greaterThanOrEqual") return XLCfOperator::GreaterThanOrEqual;
|
|
187
|
+
if (operatorString == "greaterThan") return XLCfOperator::GreaterThan;
|
|
188
|
+
if (operatorString == "between") return XLCfOperator::Between;
|
|
189
|
+
if (operatorString == "notBetween") return XLCfOperator::NotBetween;
|
|
190
|
+
if (operatorString == "containsText") return XLCfOperator::ContainsText;
|
|
191
|
+
if (operatorString == "notContains") return XLCfOperator::NotContains;
|
|
192
|
+
if (operatorString == "beginsWith") return XLCfOperator::BeginsWith;
|
|
193
|
+
if (operatorString == "endsWith") return XLCfOperator::EndsWith;
|
|
194
|
+
return XLCfOperator::Invalid;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* @brief inverse of XLCfOperatorFromString
|
|
199
|
+
* @param cfOperator the XLCfOperator for which to get the OOXML string
|
|
200
|
+
*/
|
|
201
|
+
std::string XLCfOperatorToString(XLCfOperator cfOperator)
|
|
202
|
+
{
|
|
203
|
+
switch (cfOperator) {
|
|
204
|
+
case XLCfOperator::LessThan: return "lessThan";
|
|
205
|
+
case XLCfOperator::LessThanOrEqual: return "lessThanOrEqual";
|
|
206
|
+
case XLCfOperator::Equal: return "equal";
|
|
207
|
+
case XLCfOperator::NotEqual: return "notEqual";
|
|
208
|
+
case XLCfOperator::GreaterThanOrEqual: return "greaterThanOrEqual";
|
|
209
|
+
case XLCfOperator::GreaterThan: return "greaterThan";
|
|
210
|
+
case XLCfOperator::Between: return "between";
|
|
211
|
+
case XLCfOperator::NotBetween: return "notBetween";
|
|
212
|
+
case XLCfOperator::ContainsText: return "containsText";
|
|
213
|
+
case XLCfOperator::NotContains: return "notContains";
|
|
214
|
+
case XLCfOperator::BeginsWith: return "beginsWith";
|
|
215
|
+
case XLCfOperator::EndsWith: return "endsWith";
|
|
216
|
+
case XLCfOperator::Invalid: [[fallthrough]];
|
|
217
|
+
default: return "(invalid)";
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* @brief get the correct XLCfTimePeriod from the OOXML cfRule timePeriod attribute string
|
|
223
|
+
* @param operatorString the string as used in the OOXML
|
|
224
|
+
* @return the corresponding XLCfTimePeriod enum value
|
|
225
|
+
*/
|
|
226
|
+
XLCfTimePeriod XLCfTimePeriodFromString(std::string const& timePeriodString)
|
|
227
|
+
{
|
|
228
|
+
if (timePeriodString == "today") return XLCfTimePeriod::Today;
|
|
229
|
+
if (timePeriodString == "yesterday") return XLCfTimePeriod::Yesterday;
|
|
230
|
+
if (timePeriodString == "tomorrow") return XLCfTimePeriod::Tomorrow;
|
|
231
|
+
if (timePeriodString == "last7Days") return XLCfTimePeriod::Last7Days;
|
|
232
|
+
if (timePeriodString == "thisMonth") return XLCfTimePeriod::ThisMonth;
|
|
233
|
+
if (timePeriodString == "lastMonth") return XLCfTimePeriod::LastMonth;
|
|
234
|
+
if (timePeriodString == "nextMonth") return XLCfTimePeriod::NextMonth;
|
|
235
|
+
if (timePeriodString == "thisWeek") return XLCfTimePeriod::ThisWeek;
|
|
236
|
+
if (timePeriodString == "lastWeek") return XLCfTimePeriod::LastWeek;
|
|
237
|
+
if (timePeriodString == "nextWeek") return XLCfTimePeriod::NextWeek;
|
|
238
|
+
return XLCfTimePeriod::Invalid;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* @brief inverse of XLCfOperatorFromString
|
|
243
|
+
* @param cfOperator the XLCfTimePeriod for which to get the OOXML string
|
|
244
|
+
*/
|
|
245
|
+
std::string XLCfTimePeriodToString(XLCfTimePeriod cfTimePeriod)
|
|
246
|
+
{
|
|
247
|
+
switch (cfTimePeriod) {
|
|
248
|
+
case XLCfTimePeriod::Today: return "today";
|
|
249
|
+
case XLCfTimePeriod::Yesterday: return "yesterday";
|
|
250
|
+
case XLCfTimePeriod::Tomorrow: return "tomorrow";
|
|
251
|
+
case XLCfTimePeriod::Last7Days: return "last7Days";
|
|
252
|
+
case XLCfTimePeriod::ThisMonth: return "thisMonth";
|
|
253
|
+
case XLCfTimePeriod::LastMonth: return "lastMonth";
|
|
254
|
+
case XLCfTimePeriod::NextMonth: return "nextMonth";
|
|
255
|
+
case XLCfTimePeriod::ThisWeek: return "thisWeek";
|
|
256
|
+
case XLCfTimePeriod::LastWeek: return "lastWeek";
|
|
257
|
+
case XLCfTimePeriod::NextWeek: return "nextWeek";
|
|
258
|
+
case XLCfTimePeriod::Invalid: [[fallthrough]];
|
|
259
|
+
default: return "(invalid)";
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
} // namespace OpenXLSX
|
|
263
|
+
|
|
264
|
+
// ========== XLSheet Member Functions
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* @details The constructor begins by constructing an instance of its superclass, XLAbstractXMLFile. The default
|
|
268
|
+
* sheet type is WorkSheet and the default sheet state is Visible.
|
|
269
|
+
*/
|
|
270
|
+
XLSheet::XLSheet(XLXmlData* xmlData) : XLXmlFile(xmlData)
|
|
271
|
+
{
|
|
272
|
+
if (xmlData->getXmlType() == XLContentType::Worksheet)
|
|
273
|
+
m_sheet = XLWorksheet(xmlData);
|
|
274
|
+
else if (xmlData->getXmlType() == XLContentType::Chartsheet)
|
|
275
|
+
m_sheet = XLChartsheet(xmlData);
|
|
276
|
+
else
|
|
277
|
+
throw XLInternalError("Invalid XML data.");
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* @details Copy constructor.
|
|
282
|
+
*/
|
|
283
|
+
XLSheet::XLSheet(const XLSheet& other) = default;
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* @details Move constructor
|
|
287
|
+
*/
|
|
288
|
+
XLSheet::XLSheet(XLSheet&& other) noexcept = default;
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* @details
|
|
292
|
+
*/
|
|
293
|
+
XLSheet::~XLSheet() = default;
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* @details Copy assignment operator
|
|
297
|
+
*/
|
|
298
|
+
XLSheet& XLSheet::operator=(const XLSheet& other) = default;
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* @details Move assignment operator
|
|
302
|
+
*/
|
|
303
|
+
XLSheet& XLSheet::operator=(XLSheet&& other) noexcept = default;
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* @details This method uses visitor pattern to return the name() member variable of the underlying
|
|
307
|
+
* sheet object (XLWorksheet or XLChartsheet).
|
|
308
|
+
*/
|
|
309
|
+
std::string XLSheet::name() const
|
|
310
|
+
{
|
|
311
|
+
return std::visit([](auto&& arg) { return arg.name(); }, m_sheet);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* @details This method sets the name of the sheet to a new name, by calling the setName()
|
|
316
|
+
* member function of the underlying sheet object (XLWorksheet or XLChartsheet).
|
|
317
|
+
*/
|
|
318
|
+
void XLSheet::setName(const std::string& name)
|
|
319
|
+
{
|
|
320
|
+
std::visit([&](auto&& arg) { return arg.setName(name); }, m_sheet);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* @details This method uses visitor pattern to return the visibility() member variable of the underlying
|
|
325
|
+
* sheet object (XLWorksheet or XLChartsheet).
|
|
326
|
+
*/
|
|
327
|
+
XLSheetState XLSheet::visibility() const
|
|
328
|
+
{
|
|
329
|
+
return std::visit([](auto&& arg) { return arg.visibility(); }, m_sheet);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* @details This method sets the visibility state of the sheet, by calling the setVisibility()
|
|
334
|
+
* member function of the underlying sheet object (XLWorksheet or XLChartsheet).
|
|
335
|
+
*/
|
|
336
|
+
void XLSheet::setVisibility(XLSheetState state)
|
|
337
|
+
{
|
|
338
|
+
std::visit([&](auto&& arg) { return arg.setVisibility(state); }, m_sheet);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* @details This method uses visitor pattern to return the color() member variable of the underlying
|
|
343
|
+
* sheet object (XLWorksheet or XLChartsheet).
|
|
344
|
+
*/
|
|
345
|
+
XLColor XLSheet::color() const
|
|
346
|
+
{
|
|
347
|
+
return std::visit([](auto&& arg) { return arg.color(); }, m_sheet);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* @details This method sets the color of the sheet, by calling the setColor()
|
|
352
|
+
* member function of the underlying sheet object (XLWorksheet or XLChartsheet).
|
|
353
|
+
*/
|
|
354
|
+
void XLSheet::setColor(const XLColor& color)
|
|
355
|
+
{
|
|
356
|
+
std::visit([&](auto&& arg) { return arg.setColor(color); }, m_sheet);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* @details This reports the selection state of the sheet, by calling the isSelected()
|
|
361
|
+
* member function of the underlying sheet object (XLWorksheet or XLChartsheet).
|
|
362
|
+
*/
|
|
363
|
+
bool XLSheet::isSelected() const
|
|
364
|
+
{
|
|
365
|
+
return std::visit([](auto&& arg) { return arg.isSelected(); }, m_sheet);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* @details This method sets the selection state of the sheet, by calling the setSelected()
|
|
370
|
+
* member function of the underlying sheet object (XLWorksheet or XLChartsheet).
|
|
371
|
+
*/
|
|
372
|
+
void XLSheet::setSelected(bool selected)
|
|
373
|
+
{
|
|
374
|
+
std::visit([&](auto&& arg) { return arg.setSelected(selected); }, m_sheet);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* @details This reports the active state of the sheet, by calling the isActive()
|
|
379
|
+
* member function of the underlying sheet object (XLWorksheet or XLChartsheet).
|
|
380
|
+
*/
|
|
381
|
+
bool XLSheet::isActive() const
|
|
382
|
+
{
|
|
383
|
+
return std::visit([](auto&& arg) { return arg.isActive(); }, m_sheet);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* @details This method sets the active state of the sheet, by calling the setActive()
|
|
388
|
+
* member function of the underlying sheet object (XLWorksheet or XLChartsheet).
|
|
389
|
+
*/
|
|
390
|
+
bool XLSheet::setActive()
|
|
391
|
+
{
|
|
392
|
+
return std::visit([&](auto&& arg) { return arg.setActive(); }, m_sheet);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* @details Clones the sheet by calling the clone() method in the underlying sheet object
|
|
397
|
+
* (XLWorksheet or XLChartsheet), using the visitor pattern.
|
|
398
|
+
*/
|
|
399
|
+
void XLSheet::clone(const std::string& newName)
|
|
400
|
+
{
|
|
401
|
+
std::visit([&](auto&& arg) { arg.clone(newName); }, m_sheet);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* @details Get the index of the sheet, by calling the index() method in the underlying
|
|
406
|
+
* sheet object (XLWorksheet or XLChartsheet), using the visitor pattern.
|
|
407
|
+
*/
|
|
408
|
+
uint16_t XLSheet::index() const
|
|
409
|
+
{
|
|
410
|
+
return std::visit([](auto&& arg) { return arg.index(); }, m_sheet);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* @details This method sets the index of the sheet (i.e. move the sheet), by calling the setIndex()
|
|
415
|
+
* member function of the underlying sheet object (XLWorksheet or XLChartsheet).
|
|
416
|
+
*/
|
|
417
|
+
void XLSheet::setIndex(uint16_t index)
|
|
418
|
+
{
|
|
419
|
+
std::visit([&](auto&& arg) { arg.setIndex(index); }, m_sheet);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* @details Implicit conversion operator to XLWorksheet. Calls the get<>() member function, with XLWorksheet as
|
|
424
|
+
* the template argument.
|
|
425
|
+
*/
|
|
426
|
+
XLSheet::operator XLWorksheet() const { return this->get<XLWorksheet>(); }
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* @details Implicit conversion operator to XLChartsheet. Calls the get<>() member function, with XLChartsheet as
|
|
430
|
+
* the template argument.
|
|
431
|
+
*/
|
|
432
|
+
XLSheet::operator XLChartsheet() const { return this->get<XLChartsheet>(); }
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* @details Print the underlying XML using pugixml::xml_node::print
|
|
436
|
+
*/
|
|
437
|
+
void XLSheet::print(std::basic_ostream<char>& ostr) const { xmlDocument().document_element().print(ostr); }
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
// ========== BEGIN <conditionalFormatting> related member function definitions
|
|
441
|
+
|
|
442
|
+
// ========== XLCfRule member functions
|
|
443
|
+
/**
|
|
444
|
+
* @details Constructor. Initializes an empty XLCfRule object
|
|
445
|
+
*/
|
|
446
|
+
XLCfRule::XLCfRule() : m_cfRuleNode(std::make_unique<XMLNode>()) {}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* @details Constructor. Initializes the member variables for the new XLCfRule object.
|
|
450
|
+
*/
|
|
451
|
+
XLCfRule::XLCfRule(const XMLNode& node) : m_cfRuleNode(std::make_unique<XMLNode>(node)) {}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* @details Copy constructor
|
|
455
|
+
*/
|
|
456
|
+
XLCfRule::XLCfRule(const XLCfRule& other)
|
|
457
|
+
: m_cfRuleNode(std::make_unique<XMLNode>(*other.m_cfRuleNode))
|
|
458
|
+
{}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* @details Move constructor
|
|
462
|
+
*/
|
|
463
|
+
XLCfRule::XLCfRule(XLCfRule&& other) noexcept = default;
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* @details
|
|
467
|
+
*/
|
|
468
|
+
XLCfRule::~XLCfRule() = default;
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* @details Copy assignment operator
|
|
472
|
+
*/
|
|
473
|
+
XLCfRule& XLCfRule::operator=(const XLCfRule& other)
|
|
474
|
+
{
|
|
475
|
+
if (&other != this) *m_cfRuleNode = *other.m_cfRuleNode;
|
|
476
|
+
return *this;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* @details Move assignment operator
|
|
481
|
+
*/
|
|
482
|
+
XLCfRule& XLCfRule::operator=(XLCfRule&& other) noexcept = default;
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* @details Returns the node empty status
|
|
486
|
+
*/
|
|
487
|
+
bool XLCfRule::empty() const { return m_cfRuleNode->empty(); }
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* @details Element getter functions
|
|
491
|
+
*/
|
|
492
|
+
std::string XLCfRule::formula() const
|
|
493
|
+
{
|
|
494
|
+
// XMLNode formulaTextNode =
|
|
495
|
+
return m_cfRuleNode->child("formula").first_child_of_type(pugi::node_pcdata).value();
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* @details Unsupported element getter functions
|
|
500
|
+
*/
|
|
501
|
+
XLUnsupportedElement XLCfRule::colorScale() const { return XLUnsupportedElement(); } // <cfRule><colorScale>...</colorScale></cfRule>
|
|
502
|
+
XLUnsupportedElement XLCfRule::dataBar() const { return XLUnsupportedElement(); } // <cfRule><dataBar>...</dataBar></cfRule>
|
|
503
|
+
XLUnsupportedElement XLCfRule::iconSet() const { return XLUnsupportedElement(); } // <cfRule><iconSet>...</iconSet></cfRule>
|
|
504
|
+
XLUnsupportedElement XLCfRule::extLst() const { return XLUnsupportedElement{}; } // <cfRule><extLst>...</extLst></cfRule>
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* @details Attribute getter functions
|
|
508
|
+
*/
|
|
509
|
+
XLCfType XLCfRule::type() const { return XLCfTypeFromString (m_cfRuleNode->attribute("type").value() ); }
|
|
510
|
+
XLStyleIndex XLCfRule::dxfId() const { return m_cfRuleNode->attribute("dxfId").as_uint(XLInvalidStyleIndex); }
|
|
511
|
+
uint16_t XLCfRule::priority() const { return m_cfRuleNode->attribute("priority").as_uint(XLPriorityNotSet); }
|
|
512
|
+
bool XLCfRule::stopIfTrue() const { return m_cfRuleNode->attribute("stopIfTrue").as_bool(false) ; }
|
|
513
|
+
// TODO TBD: how does true default value manifest itself for aboveAverage? Evaluate as true if aboveAverage=""?
|
|
514
|
+
bool XLCfRule::aboveAverage() const { return m_cfRuleNode->attribute("aboveAverage").as_bool(false) ; }
|
|
515
|
+
bool XLCfRule::percent() const { return m_cfRuleNode->attribute("percent").as_bool(false) ; }
|
|
516
|
+
bool XLCfRule::bottom() const { return m_cfRuleNode->attribute("bottom").as_bool(false) ; }
|
|
517
|
+
XLCfOperator XLCfRule::Operator() const { return XLCfOperatorFromString (m_cfRuleNode->attribute("operator").value() ); }
|
|
518
|
+
std::string XLCfRule::text() const { return m_cfRuleNode->attribute("text").value() ; }
|
|
519
|
+
XLCfTimePeriod XLCfRule::timePeriod() const { return XLCfTimePeriodFromString(m_cfRuleNode->attribute("timePeriod").value() ); }
|
|
520
|
+
uint16_t XLCfRule::rank() const { return m_cfRuleNode->attribute("rank").as_uint() ; }
|
|
521
|
+
int16_t XLCfRule::stdDev() const { return m_cfRuleNode->attribute("stdDev").as_int() ; }
|
|
522
|
+
bool XLCfRule::equalAverage() const { return m_cfRuleNode->attribute("equalAverage").as_bool(false) ; }
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* @brief Element setter functions
|
|
526
|
+
*/
|
|
527
|
+
bool XLCfRule::setFormula(std::string const& newFormula)
|
|
528
|
+
{
|
|
529
|
+
XMLNode formula = appendAndGetNode(*m_cfRuleNode, "formula", m_nodeOrder);
|
|
530
|
+
if (formula.empty()) return false;
|
|
531
|
+
formula.remove_children(); // no-op if no children
|
|
532
|
+
return formula.append_child(pugi::node_pcdata).set_value(newFormula.c_str());
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* @brief Unsupported element setter function
|
|
537
|
+
*/
|
|
538
|
+
bool XLCfRule::setColorScale(XLUnsupportedElement const& newColorScale) { OpenXLSX::ignore(newColorScale); return false; }
|
|
539
|
+
bool XLCfRule::setDataBar( XLUnsupportedElement const& newDataBar ) { OpenXLSX::ignore(newDataBar ); return false; }
|
|
540
|
+
bool XLCfRule::setIconSet( XLUnsupportedElement const& newIconSet ) { OpenXLSX::ignore(newIconSet ); return false; }
|
|
541
|
+
bool XLCfRule::setExtLst( XLUnsupportedElement const& newExtLst ) { OpenXLSX::ignore(newExtLst ); return false; }
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* @details Attribute setter functions
|
|
545
|
+
*/
|
|
546
|
+
bool XLCfRule::setType (XLCfType newType ) { return appendAndSetAttribute(*m_cfRuleNode, "type", XLCfTypeToString( newType )).empty() == false; }
|
|
547
|
+
bool XLCfRule::setDxfId (XLStyleIndex newDxfId) { return appendAndSetAttribute(*m_cfRuleNode, "dxfId", std::to_string( newDxfId )).empty() == false; }
|
|
548
|
+
bool XLCfRule::setPriority (uint16_t newPriority) { return appendAndSetAttribute(*m_cfRuleNode, "priority", std::to_string( newPriority )).empty() == false; }
|
|
549
|
+
// TODO TBD whether true / false work with MS Office booleans here, or if 1 and 0 are mandatory
|
|
550
|
+
bool XLCfRule::setStopIfTrue (bool set) { return appendAndSetAttribute(*m_cfRuleNode, "stopIfTrue", (set ? "true" : "false") ).empty() == false; }
|
|
551
|
+
bool XLCfRule::setAboveAverage(bool set) { return appendAndSetAttribute(*m_cfRuleNode, "aboveAverage", (set ? "true" : "false") ).empty() == false; }
|
|
552
|
+
bool XLCfRule::setPercent (bool set) { return appendAndSetAttribute(*m_cfRuleNode, "percent", (set ? "true" : "false") ).empty() == false; }
|
|
553
|
+
bool XLCfRule::setBottom (bool set) { return appendAndSetAttribute(*m_cfRuleNode, "bottom", (set ? "true" : "false") ).empty() == false; }
|
|
554
|
+
bool XLCfRule::setOperator (XLCfOperator newOperator) { return appendAndSetAttribute(*m_cfRuleNode, "operator", XLCfOperatorToString( newOperator )).empty() == false; }
|
|
555
|
+
bool XLCfRule::setText (std::string const& newText) { return appendAndSetAttribute(*m_cfRuleNode, "text", newText.c_str() ).empty() == false; }
|
|
556
|
+
bool XLCfRule::setTimePeriod (XLCfTimePeriod newTimePeriod) { return appendAndSetAttribute(*m_cfRuleNode, "timePeriod", XLCfTimePeriodToString(newTimePeriod) ).empty() == false; }
|
|
557
|
+
bool XLCfRule::setRank (uint16_t newRank) { return appendAndSetAttribute(*m_cfRuleNode, "rank", std::to_string( newRank )).empty() == false; }
|
|
558
|
+
bool XLCfRule::setStdDev (int16_t newStdDev) { return appendAndSetAttribute(*m_cfRuleNode, "stdDev", std::to_string( newStdDev )).empty() == false; }
|
|
559
|
+
bool XLCfRule::setEqualAverage(bool set) { return appendAndSetAttribute(*m_cfRuleNode, "equalAverage", (set ? "true" : "false") ).empty() == false; }
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* @details assemble a string summary about the conditional formatting
|
|
564
|
+
*/
|
|
565
|
+
std::string XLCfRule::summary() const
|
|
566
|
+
{
|
|
567
|
+
using namespace std::literals::string_literals;
|
|
568
|
+
XLStyleIndex dxfIndex = dxfId();
|
|
569
|
+
return "formula is "s + formula()
|
|
570
|
+
+ ", type is "s + XLCfTypeToString(type())
|
|
571
|
+
+ ", dxfId is "s + (dxfIndex == XLInvalidStyleIndex ? "(invalid)"s : std::to_string(dxfId()))
|
|
572
|
+
+ ", priority is "s + std::to_string(priority())
|
|
573
|
+
+ ", stopIfTrue: "s + (stopIfTrue() ? "true"s : "false"s)
|
|
574
|
+
+ ", aboveAverage: "s + (aboveAverage() ? "true"s : "false"s)
|
|
575
|
+
+ ", percent: "s + (percent() ? "true"s : "false"s)
|
|
576
|
+
+ ", bottom: "s + (bottom() ? "true"s : "false"s)
|
|
577
|
+
+ ", operator is "s + XLCfOperatorToString(Operator())
|
|
578
|
+
+ ", text is \""s + text() + "\""s
|
|
579
|
+
+ ", timePeriod is "s + XLCfTimePeriodToString(timePeriod())
|
|
580
|
+
+ ", rank is "s + std::to_string(rank())
|
|
581
|
+
+ ", stdDev is "s + std::to_string(stdDev())
|
|
582
|
+
+ ", equalAverage: "s + (equalAverage() ? "true"s : "false"s)
|
|
583
|
+
;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
|
|
587
|
+
// ========== XLCfRules member functions, parent of XLCfRule
|
|
588
|
+
/**
|
|
589
|
+
* @details Constructor. Initializes an empty XLCfRules object
|
|
590
|
+
*/
|
|
591
|
+
XLCfRules::XLCfRules() : m_conditionalFormattingNode(std::make_unique<XMLNode>()) {}
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* @details Constructor. Initializes the member variables for the new XLCellStyle object.
|
|
595
|
+
*/
|
|
596
|
+
XLCfRules::XLCfRules(const XMLNode& node) : m_conditionalFormattingNode(std::make_unique<XMLNode>(node)) {}
|
|
597
|
+
|
|
598
|
+
/**
|
|
599
|
+
* @details Copy constructor
|
|
600
|
+
*/
|
|
601
|
+
XLCfRules::XLCfRules(const XLCfRules& other)
|
|
602
|
+
: m_conditionalFormattingNode(std::make_unique<XMLNode>(*other.m_conditionalFormattingNode))
|
|
603
|
+
{}
|
|
604
|
+
|
|
605
|
+
/**
|
|
606
|
+
* @details Move constructor
|
|
607
|
+
*/
|
|
608
|
+
XLCfRules::XLCfRules(XLCfRules&& other) noexcept = default;
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
* @details
|
|
612
|
+
*/
|
|
613
|
+
XLCfRules::~XLCfRules() = default;
|
|
614
|
+
|
|
615
|
+
/**
|
|
616
|
+
* @details Copy assignment operator
|
|
617
|
+
*/
|
|
618
|
+
XLCfRules& XLCfRules::operator=(const XLCfRules& other)
|
|
619
|
+
{
|
|
620
|
+
if (&other != this) *m_conditionalFormattingNode = *other.m_conditionalFormattingNode;
|
|
621
|
+
return *this;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
/**
|
|
625
|
+
* @details Move assignment operator
|
|
626
|
+
*/
|
|
627
|
+
XLCfRules& XLCfRules::operator=(XLCfRules&& other) noexcept = default;
|
|
628
|
+
|
|
629
|
+
/**
|
|
630
|
+
* @details Returns the node empty status
|
|
631
|
+
*/
|
|
632
|
+
bool XLCfRules::empty() const { return m_conditionalFormattingNode->empty(); }
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* @details Returns the maximum numerical priority value that a cfRule is using (= lowest rule priority)
|
|
636
|
+
*/
|
|
637
|
+
uint16_t XLCfRules::maxPriorityValue() const
|
|
638
|
+
{
|
|
639
|
+
XMLNode node = m_conditionalFormattingNode->first_child_of_type(pugi::node_element);
|
|
640
|
+
while (not node.empty() && std::string(node.name()) != "cfRule")
|
|
641
|
+
node = node.next_sibling_of_type(pugi::node_element);
|
|
642
|
+
uint16_t maxPriority = XLPriorityNotSet;
|
|
643
|
+
while (not node.empty() && std::string(node.name()) == "cfRule") {
|
|
644
|
+
maxPriority = std::max(maxPriority, XLCfRule(node).priority());
|
|
645
|
+
node = node.next_sibling_of_type(pugi::node_element);
|
|
646
|
+
}
|
|
647
|
+
return maxPriority;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
* @details ensures that newPriority is unique (by incrementing all existing priorities >= newPriority),
|
|
652
|
+
* then assigns newPriority to the rule with cfRuleIndex
|
|
653
|
+
* @note has a check for no-op if desired priority is already set
|
|
654
|
+
*/
|
|
655
|
+
bool XLCfRules::setPriority(size_t cfRuleIndex, uint16_t newPriority)
|
|
656
|
+
{
|
|
657
|
+
XLCfRule affectedRule = cfRuleByIndex(cfRuleIndex); // throws for cfRuleIndex out of bounds
|
|
658
|
+
if (newPriority == affectedRule.priority()) return true; // no-op if priority is already set
|
|
659
|
+
|
|
660
|
+
XMLNode node = m_conditionalFormattingNode->first_child_of_type(pugi::node_element);
|
|
661
|
+
while (not node.empty() && std::string(node.name()) != "cfRule") // skip past non cfRule elements
|
|
662
|
+
node = node.next_sibling_of_type(pugi::node_element);
|
|
663
|
+
|
|
664
|
+
// iterate once over rules to check if newPriority is in use by another rule
|
|
665
|
+
XMLNode node2 = node;
|
|
666
|
+
bool newPriorityFree = true;
|
|
667
|
+
while (newPriorityFree && not node2.empty() && std::string(node2.name()) == "cfRule") {
|
|
668
|
+
if (XLCfRule(node2).priority() == newPriority) newPriorityFree = false; // check if newPriority is in use
|
|
669
|
+
node2 = node2.next_sibling_of_type(pugi::node_element);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
if (!newPriorityFree) { // need to free up newPriority
|
|
673
|
+
size_t index = 0;
|
|
674
|
+
while (not node.empty() && std::string(node.name()) == "cfRule") { // loop over cfRule elements
|
|
675
|
+
if (index != cfRuleIndex) { // for all rules that are not at cfRuleIndex: increase priority if >= newPriority
|
|
676
|
+
XLCfRule rule(node);
|
|
677
|
+
uint16_t prio = rule.priority();
|
|
678
|
+
if (prio >= newPriority) rule.setPriority(prio + 1);
|
|
679
|
+
}
|
|
680
|
+
node = node.next_sibling_of_type(pugi::node_element);
|
|
681
|
+
++index;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
return affectedRule.setPriority(newPriority);
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
/**
|
|
688
|
+
* @details sort cfRule entries by priority and assign a continuous sequence of values using a given increment
|
|
689
|
+
* @note this mimics the BASIC functionality of line renumbering
|
|
690
|
+
*/
|
|
691
|
+
void XLCfRules::renumberPriorities(uint16_t increment)
|
|
692
|
+
{
|
|
693
|
+
if (increment == 0) // not allowed
|
|
694
|
+
throw XLException("XLCfRules::renumberPriorities: increment must not be 0");
|
|
695
|
+
|
|
696
|
+
std::multimap< uint16_t, XLCfRule > rules;
|
|
697
|
+
|
|
698
|
+
XMLNode node = m_conditionalFormattingNode->first_child_of_type(pugi::node_element);
|
|
699
|
+
while (not node.empty() && std::string(node.name()) != "cfRule") // skip past non cfRule elements
|
|
700
|
+
node = node.next_sibling_of_type(pugi::node_element);
|
|
701
|
+
|
|
702
|
+
while (not node.empty() && std::string(node.name()) == "cfRule") { // loop over cfRule elements
|
|
703
|
+
XLCfRule rule(node);
|
|
704
|
+
rules.insert(std::pair(rule.priority(), std::move(rule)));
|
|
705
|
+
node = node.next_sibling_of_type(pugi::node_element);
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
if (rules.size() * increment > std::numeric_limits< uint16_t >::max()) { // first rule always gets assigned "1*increment"
|
|
709
|
+
using namespace std::literals::string_literals;
|
|
710
|
+
throw XLException("XLCfRules::renumberPriorities: amount of rules "s + std::to_string(rules.size())
|
|
711
|
+
/**/ + " with given increment "s + std::to_string(increment) + " exceeds max range of uint16_t"s);
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
uint16_t prio = 0;
|
|
715
|
+
for (auto& [key, rule]: rules) {
|
|
716
|
+
prio += increment;
|
|
717
|
+
rule.setPriority(prio);
|
|
718
|
+
}
|
|
719
|
+
rules.clear();
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
/**
|
|
723
|
+
* @details Returns the amount of cfRule entries held by the class
|
|
724
|
+
*/
|
|
725
|
+
size_t XLCfRules::count() const
|
|
726
|
+
{
|
|
727
|
+
XMLNode node = m_conditionalFormattingNode->first_child_of_type(pugi::node_element);
|
|
728
|
+
while (not node.empty() && std::string(node.name()) != "cfRule")
|
|
729
|
+
node = node.next_sibling_of_type(pugi::node_element);
|
|
730
|
+
size_t count = 0;
|
|
731
|
+
while (not node.empty() && std::string(node.name()) == "cfRule") {
|
|
732
|
+
++count;
|
|
733
|
+
node = node.next_sibling_of_type(pugi::node_element);
|
|
734
|
+
}
|
|
735
|
+
return count;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
/**
|
|
739
|
+
* @details fetch XLCfRule from m_conditionalFormattingNode by index
|
|
740
|
+
*/
|
|
741
|
+
XLCfRule XLCfRules::cfRuleByIndex(size_t index) const
|
|
742
|
+
{
|
|
743
|
+
XMLNode node = m_conditionalFormattingNode->first_child_of_type(pugi::node_element);
|
|
744
|
+
while (not node.empty() && std::string(node.name()) != "cfRule")
|
|
745
|
+
node = node.next_sibling_of_type(pugi::node_element);
|
|
746
|
+
|
|
747
|
+
if (not node.empty()) {
|
|
748
|
+
size_t count = 0;
|
|
749
|
+
while (not node.empty() && std::string(node.name()) == "cfRule" && count < index) {
|
|
750
|
+
++count;
|
|
751
|
+
node = node.next_sibling_of_type(pugi::node_element);
|
|
752
|
+
}
|
|
753
|
+
if (count == index && std::string(node.name()) == "cfRule")
|
|
754
|
+
return XLCfRule(node);
|
|
755
|
+
}
|
|
756
|
+
using namespace std::literals::string_literals;
|
|
757
|
+
throw XLException("XLCfRules::"s + __func__ + ": cfRule with index "s + std::to_string(index) + " does not exist");
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
/**
|
|
761
|
+
* @details append a new XLCfRule to m_conditionalFormattingNode, based on copyFrom
|
|
762
|
+
*/
|
|
763
|
+
size_t XLCfRules::create([[maybe_unused]] XLCfRule copyFrom, std::string cfRulePrefix)
|
|
764
|
+
{
|
|
765
|
+
uint16_t maxPrio = maxPriorityValue();
|
|
766
|
+
if (maxPrio == std::numeric_limits< uint16_t >::max()) {
|
|
767
|
+
using namespace std::literals::string_literals;
|
|
768
|
+
throw XLException("XLCfRules::"s + __func__
|
|
769
|
+
+ ": can not create a new cfRule entry: no available priority value - please renumberPriorities or otherwise free up the highest value"s);
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
size_t index = count(); // index for the cfRule to be created
|
|
773
|
+
XMLNode newNode{}; // scope declaration
|
|
774
|
+
|
|
775
|
+
// ===== Append new node prior to final whitespaces, if any
|
|
776
|
+
if (index == 0) newNode = appendAndGetNode(*m_conditionalFormattingNode, "cfRule", m_nodeOrder);
|
|
777
|
+
else {
|
|
778
|
+
XMLNode lastCfRule = *cfRuleByIndex(index - 1).m_cfRuleNode;
|
|
779
|
+
if (not lastCfRule.empty())
|
|
780
|
+
newNode = m_conditionalFormattingNode->insert_child_after("cfRule", lastCfRule);
|
|
781
|
+
}
|
|
782
|
+
if (newNode.empty()) {
|
|
783
|
+
using namespace std::literals::string_literals;
|
|
784
|
+
throw XLException("XLCfRules::"s + __func__ + ": failed to create a new cfRule entry");
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
// copyXMLNode(newNode, *copyFrom.m_cfRuleNode); // will use copyFrom as template, does nothing if copyFrom is empty
|
|
788
|
+
m_conditionalFormattingNode->insert_child_before(pugi::node_pcdata, newNode).set_value(cfRulePrefix.c_str()); // prefix the new node with
|
|
789
|
+
|
|
790
|
+
// regardless of whether a template was provided or not: set the newest rule to the lowest possible priority == highest value in use plus 1
|
|
791
|
+
cfRuleByIndex(index).setPriority(maxPrio + 1);
|
|
792
|
+
|
|
793
|
+
return index;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
/**
|
|
797
|
+
* @details Getter functions
|
|
798
|
+
*/
|
|
799
|
+
// TODO
|
|
800
|
+
|
|
801
|
+
/**
|
|
802
|
+
* @details Setter functions
|
|
803
|
+
*/
|
|
804
|
+
// TODO / N/A
|
|
805
|
+
|
|
806
|
+
|
|
807
|
+
/**
|
|
808
|
+
* @details assemble a string summary about the conditional formatting rules
|
|
809
|
+
*/
|
|
810
|
+
std::string XLCfRules::summary() const
|
|
811
|
+
{
|
|
812
|
+
using namespace std::literals::string_literals;
|
|
813
|
+
size_t rulesCount = count();
|
|
814
|
+
if (rulesCount == 0)
|
|
815
|
+
return "(no cfRule entries)";
|
|
816
|
+
std::string result = "";
|
|
817
|
+
for (size_t idx = 0; idx < rulesCount; ++idx) {
|
|
818
|
+
using namespace std::literals::string_literals;
|
|
819
|
+
result += "cfRule["s + std::to_string(idx) + "] "s + cfRuleByIndex(idx).summary();
|
|
820
|
+
if (idx + 1 < rulesCount) result += ", ";
|
|
821
|
+
}
|
|
822
|
+
return result;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
|
|
826
|
+
// ========== XLConditionalFormat Member Functions
|
|
827
|
+
/**
|
|
828
|
+
* @details Constructor. Initializes an empty XLConditionalFormat object
|
|
829
|
+
*/
|
|
830
|
+
XLConditionalFormat::XLConditionalFormat() : m_conditionalFormattingNode(std::make_unique<XMLNode>()) {}
|
|
831
|
+
|
|
832
|
+
/**
|
|
833
|
+
* @details Constructor. Initializes the member variables for the new XLCellStyle object.
|
|
834
|
+
*/
|
|
835
|
+
XLConditionalFormat::XLConditionalFormat(const XMLNode& node) : m_conditionalFormattingNode(std::make_unique<XMLNode>(node)) {}
|
|
836
|
+
|
|
837
|
+
/**
|
|
838
|
+
* @details Copy constructor
|
|
839
|
+
*/
|
|
840
|
+
XLConditionalFormat::XLConditionalFormat(const XLConditionalFormat& other)
|
|
841
|
+
: m_conditionalFormattingNode(std::make_unique<XMLNode>(*other.m_conditionalFormattingNode))
|
|
842
|
+
{}
|
|
843
|
+
|
|
844
|
+
/**
|
|
845
|
+
* @details Move constructor
|
|
846
|
+
*/
|
|
847
|
+
XLConditionalFormat::XLConditionalFormat(XLConditionalFormat&& other) noexcept = default;
|
|
848
|
+
|
|
849
|
+
/**
|
|
850
|
+
* @details
|
|
851
|
+
*/
|
|
852
|
+
XLConditionalFormat::~XLConditionalFormat() = default;
|
|
853
|
+
|
|
854
|
+
/**
|
|
855
|
+
* @details Copy assignment operator
|
|
856
|
+
*/
|
|
857
|
+
XLConditionalFormat& XLConditionalFormat::operator=(const XLConditionalFormat& other)
|
|
858
|
+
{
|
|
859
|
+
if (&other != this) *m_conditionalFormattingNode = *other.m_conditionalFormattingNode;
|
|
860
|
+
return *this;
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
/**
|
|
864
|
+
* @details Move assignment operator
|
|
865
|
+
*/
|
|
866
|
+
XLConditionalFormat& XLConditionalFormat::operator=(XLConditionalFormat&& other) noexcept = default;
|
|
867
|
+
|
|
868
|
+
/**
|
|
869
|
+
* @details Returns the node empty status
|
|
870
|
+
*/
|
|
871
|
+
bool XLConditionalFormat::empty() const { return m_conditionalFormattingNode->empty(); }
|
|
872
|
+
|
|
873
|
+
/**
|
|
874
|
+
* @details Getter functions
|
|
875
|
+
*/
|
|
876
|
+
std::string XLConditionalFormat::sqref () const { return m_conditionalFormattingNode->attribute("sqref").value(); }
|
|
877
|
+
XLCfRules XLConditionalFormat::cfRules() const { return XLCfRules(*m_conditionalFormattingNode); }
|
|
878
|
+
|
|
879
|
+
/**
|
|
880
|
+
* @details Setter functions
|
|
881
|
+
*/
|
|
882
|
+
bool XLConditionalFormat::setSqref(std::string newSqref) { return appendAndSetAttribute(*m_conditionalFormattingNode, "sqref", newSqref).empty() == false; }
|
|
883
|
+
|
|
884
|
+
/**
|
|
885
|
+
* @brief Unsupported setter function
|
|
886
|
+
*/
|
|
887
|
+
bool XLConditionalFormat::setExtLst(XLUnsupportedElement const& newExtLst) { OpenXLSX::ignore(newExtLst); return false; }
|
|
888
|
+
|
|
889
|
+
/**
|
|
890
|
+
* @details assemble a string summary about the conditional formatting
|
|
891
|
+
*/
|
|
892
|
+
std::string XLConditionalFormat::summary() const
|
|
893
|
+
{
|
|
894
|
+
using namespace std::literals::string_literals;
|
|
895
|
+
return "sqref is "s + sqref()
|
|
896
|
+
+ ", cfRules: "s + cfRules().summary();
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
// ========== XLConditionalFormats member functions, parent of XLConditionalFormat
|
|
900
|
+
/**
|
|
901
|
+
* @details Constructor. Initializes an empty XLConditionalFormats object
|
|
902
|
+
*/
|
|
903
|
+
XLConditionalFormats::XLConditionalFormats() : m_sheetNode(std::make_unique<XMLNode>()) {}
|
|
904
|
+
|
|
905
|
+
/**
|
|
906
|
+
* @details Constructor. Initializes the member variables for the new XLConditionalFormats object.
|
|
907
|
+
*/
|
|
908
|
+
XLConditionalFormats::XLConditionalFormats(const XMLNode& sheet)
|
|
909
|
+
: m_sheetNode(std::make_unique<XMLNode>(sheet))
|
|
910
|
+
{}
|
|
911
|
+
|
|
912
|
+
/**
|
|
913
|
+
* @details Copy constructor
|
|
914
|
+
*/
|
|
915
|
+
XLConditionalFormats::XLConditionalFormats(const XLConditionalFormats& other)
|
|
916
|
+
: m_sheetNode(std::make_unique<XMLNode>(*other.m_sheetNode))
|
|
917
|
+
{}
|
|
918
|
+
|
|
919
|
+
/**
|
|
920
|
+
* @details Move constructor
|
|
921
|
+
*/
|
|
922
|
+
XLConditionalFormats::XLConditionalFormats(XLConditionalFormats&& other)
|
|
923
|
+
: m_sheetNode(std::move(other.m_sheetNode))
|
|
924
|
+
{}
|
|
925
|
+
|
|
926
|
+
/**
|
|
927
|
+
* @details
|
|
928
|
+
*/
|
|
929
|
+
XLConditionalFormats::~XLConditionalFormats() {}
|
|
930
|
+
|
|
931
|
+
/**
|
|
932
|
+
* @details Copy assignment operator
|
|
933
|
+
*/
|
|
934
|
+
XLConditionalFormats& XLConditionalFormats::operator=(const XLConditionalFormats& other)
|
|
935
|
+
{
|
|
936
|
+
if (&other != this) {
|
|
937
|
+
*m_sheetNode = *other.m_sheetNode;
|
|
938
|
+
}
|
|
939
|
+
return *this;
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
/**
|
|
943
|
+
* @details Move assignment operator
|
|
944
|
+
*/
|
|
945
|
+
XLConditionalFormats& XLConditionalFormats::operator=(XLConditionalFormats&& other) noexcept
|
|
946
|
+
{
|
|
947
|
+
m_sheetNode = std::move(other.m_sheetNode);
|
|
948
|
+
return *this;
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
/**
|
|
952
|
+
* @details Returns the node empty status
|
|
953
|
+
*/
|
|
954
|
+
bool XLConditionalFormats::empty() const { return m_sheetNode->empty(); }
|
|
955
|
+
|
|
956
|
+
/**
|
|
957
|
+
* @details Returns the amount of conditionalFormatting entries held by the class
|
|
958
|
+
*/
|
|
959
|
+
size_t XLConditionalFormats::count() const
|
|
960
|
+
{
|
|
961
|
+
XMLNode node = m_sheetNode->first_child_of_type(pugi::node_element);
|
|
962
|
+
while (not node.empty() && std::string(node.name()) != "conditionalFormatting")
|
|
963
|
+
node = node.next_sibling_of_type(pugi::node_element);
|
|
964
|
+
size_t count = 0;
|
|
965
|
+
while (not node.empty() && std::string(node.name()) == "conditionalFormatting") {
|
|
966
|
+
++count;
|
|
967
|
+
node = node.next_sibling_of_type(pugi::node_element);
|
|
968
|
+
}
|
|
969
|
+
return count;
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
/**
|
|
973
|
+
* @details fetch XLConditionalFormat from m_sheetNode by index
|
|
974
|
+
*/
|
|
975
|
+
XLConditionalFormat XLConditionalFormats::conditionalFormatByIndex(size_t index) const
|
|
976
|
+
{
|
|
977
|
+
XMLNode node = m_sheetNode->first_child_of_type(pugi::node_element);
|
|
978
|
+
while (not node.empty() && std::string(node.name()) != "conditionalFormatting")
|
|
979
|
+
node = node.next_sibling_of_type(pugi::node_element);
|
|
980
|
+
|
|
981
|
+
if (not node.empty()) {
|
|
982
|
+
size_t count = 0;
|
|
983
|
+
while (not node.empty() && std::string(node.name()) == "conditionalFormatting" && count < index) {
|
|
984
|
+
++count;
|
|
985
|
+
node = node.next_sibling_of_type(pugi::node_element);
|
|
986
|
+
}
|
|
987
|
+
if (count == index && std::string(node.name()) == "conditionalFormatting")
|
|
988
|
+
return XLConditionalFormat(node);
|
|
989
|
+
}
|
|
990
|
+
using namespace std::literals::string_literals;
|
|
991
|
+
throw XLException("XLConditionalFormats::"s + __func__ + ": conditional format with index "s + std::to_string(index) + " does not exist");
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
/**
|
|
995
|
+
* @details append a new XLConditionalFormat to m_sheetNode, based on copyFrom
|
|
996
|
+
*/
|
|
997
|
+
size_t XLConditionalFormats::create([[maybe_unused]] XLConditionalFormat copyFrom, std::string conditionalFormattingPrefix)
|
|
998
|
+
{
|
|
999
|
+
size_t index = count(); // index for the conditional formatting to be created
|
|
1000
|
+
XMLNode newNode{}; // scope declaration
|
|
1001
|
+
|
|
1002
|
+
// ===== Append new node prior to final whitespaces, if any
|
|
1003
|
+
if (index == 0) newNode = appendAndGetNode(*m_sheetNode, "conditionalFormatting", m_nodeOrder);
|
|
1004
|
+
else {
|
|
1005
|
+
XMLNode lastConditionalFormat = *conditionalFormatByIndex(index - 1).m_conditionalFormattingNode;
|
|
1006
|
+
if (not lastConditionalFormat.empty())
|
|
1007
|
+
newNode = m_sheetNode->insert_child_after("conditionalFormatting", lastConditionalFormat);
|
|
1008
|
+
}
|
|
1009
|
+
if (newNode.empty()) {
|
|
1010
|
+
using namespace std::literals::string_literals;
|
|
1011
|
+
throw XLException("XLConditionalFormats::"s + __func__ + ": failed to create a new conditional formatting entry");
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
// copyXMLNode(newNode, *copyFrom.m_conditionalFormattingNode); // will use copyFrom as template, does nothing if copyFrom is empty
|
|
1015
|
+
m_sheetNode->insert_child_before(pugi::node_pcdata, newNode).set_value(conditionalFormattingPrefix.c_str()); // prefix the new node with conditionalFormattingPrefix
|
|
1016
|
+
|
|
1017
|
+
return index;
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
/**
|
|
1021
|
+
* @details assemble a string summary about the conditional formattings
|
|
1022
|
+
*/
|
|
1023
|
+
std::string XLConditionalFormats::summary() const
|
|
1024
|
+
{
|
|
1025
|
+
using namespace std::literals::string_literals;
|
|
1026
|
+
size_t conditionalFormatsCount = count();
|
|
1027
|
+
if (conditionalFormatsCount == 0)
|
|
1028
|
+
return "(no conditionalFormatting entries)";
|
|
1029
|
+
std::string result = "";
|
|
1030
|
+
for (size_t idx = 0; idx < conditionalFormatsCount; ++idx) {
|
|
1031
|
+
using namespace std::literals::string_literals;
|
|
1032
|
+
result += "conditionalFormatting["s + std::to_string(idx) + "] "s + conditionalFormatByIndex(idx).summary();
|
|
1033
|
+
if (idx + 1 < conditionalFormatsCount) result += ", ";
|
|
1034
|
+
}
|
|
1035
|
+
return result;
|
|
1036
|
+
}
|
|
1037
|
+
// ========== END <conditionalFormatting> related member function definitions
|
|
1038
|
+
|
|
1039
|
+
|
|
1040
|
+
// ========== XLWorksheet Member Functions
|
|
1041
|
+
|
|
1042
|
+
/**
|
|
1043
|
+
* @details The constructor does some slight reconfiguration of the XML file, in order to make parsing easier.
|
|
1044
|
+
* For example, columns with identical formatting are by default grouped under the same node. However, this makes it more difficult to
|
|
1045
|
+
* parse, so the constructor reconfigures it so each column has it's own formatting.
|
|
1046
|
+
*/
|
|
1047
|
+
XLWorksheet::XLWorksheet(XLXmlData* xmlData) : XLSheetBase(xmlData)
|
|
1048
|
+
{
|
|
1049
|
+
// ===== Read the dimensions of the Sheet and set data members accordingly.
|
|
1050
|
+
XMLNode sheetNode = xmlDocument().document_element();
|
|
1051
|
+
sheetNode.remove_child("dimension"); // 2026-06-13: prefer no dimension tag over a wrong one.
|
|
1052
|
+
// NOTE: use XLWorksheet::setDimension right before saving the document to re-establish the dimension XML tag
|
|
1053
|
+
|
|
1054
|
+
// If Column properties are grouped, divide them into properties for individual Columns.
|
|
1055
|
+
if (sheetNode.child("cols").type() != pugi::node_null) {
|
|
1056
|
+
auto currentNode = sheetNode.child("cols").first_child_of_type(pugi::node_element);
|
|
1057
|
+
while (not currentNode.empty()) {
|
|
1058
|
+
uint16_t min {};
|
|
1059
|
+
uint16_t max {};
|
|
1060
|
+
try {
|
|
1061
|
+
min = std::stoi(currentNode.attribute("min").value());
|
|
1062
|
+
max = std::stoi(currentNode.attribute("max").value());
|
|
1063
|
+
}
|
|
1064
|
+
catch (...) {
|
|
1065
|
+
throw XLInternalError("Worksheet column min and/or max attributes are invalid.");
|
|
1066
|
+
}
|
|
1067
|
+
if (min != max) {
|
|
1068
|
+
currentNode.attribute("min").set_value(max);
|
|
1069
|
+
for (uint16_t i = min; i < max; i++) { // NOLINT
|
|
1070
|
+
auto newnode = sheetNode.child("cols").insert_child_before("col", currentNode);
|
|
1071
|
+
auto attr = currentNode.first_attribute();
|
|
1072
|
+
while (not attr.empty()) { // NOLINT
|
|
1073
|
+
newnode.append_attribute(attr.name()) = attr.value();
|
|
1074
|
+
attr = attr.next_attribute();
|
|
1075
|
+
}
|
|
1076
|
+
newnode.attribute("min") = i;
|
|
1077
|
+
newnode.attribute("max") = i;
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
currentNode = currentNode.next_sibling_of_type(pugi::node_element);
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
/**
|
|
1086
|
+
* @details copy-construct an XLWorksheet object from other
|
|
1087
|
+
*/
|
|
1088
|
+
XLWorksheet::XLWorksheet(const XLWorksheet& other) : XLSheetBase<XLWorksheet>(other)
|
|
1089
|
+
{
|
|
1090
|
+
m_relationships = other.m_relationships; // invoke XLRelationships copy assignment operator
|
|
1091
|
+
m_merges = other.m_merges; // " XLMergeCells "
|
|
1092
|
+
m_vmlDrawing = other.m_vmlDrawing; // " XLVmlDrawing
|
|
1093
|
+
m_comments = other.m_comments; // " XLComments "
|
|
1094
|
+
m_tables = other.m_tables; // " XLTables "
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
/**
|
|
1098
|
+
* @details move-construct an XLWorksheet object from other
|
|
1099
|
+
*/
|
|
1100
|
+
XLWorksheet::XLWorksheet(XLWorksheet&& other) : XLSheetBase< XLWorksheet >(other)
|
|
1101
|
+
{
|
|
1102
|
+
m_relationships = std::move(other.m_relationships); // invoke XLRelationships move assignment operator
|
|
1103
|
+
m_merges = std::move(other.m_merges); // " XLMergeCells "
|
|
1104
|
+
m_vmlDrawing = std::move(other.m_vmlDrawing); // " XLVmlDrawing
|
|
1105
|
+
m_comments = std::move(other.m_comments); // " XLComments "
|
|
1106
|
+
m_tables = std::move(other.m_tables); // " XLTables "
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
/**
|
|
1110
|
+
* @details
|
|
1111
|
+
*/
|
|
1112
|
+
XLWorksheet::~XLWorksheet() = default;
|
|
1113
|
+
|
|
1114
|
+
/**
|
|
1115
|
+
* @details copy-assign an XLWorksheet from other
|
|
1116
|
+
*/
|
|
1117
|
+
XLWorksheet& XLWorksheet::operator=(const XLWorksheet& other)
|
|
1118
|
+
{
|
|
1119
|
+
XLSheetBase<XLWorksheet>::operator=(other); // invoke base class copy assignment operator
|
|
1120
|
+
m_relationships = other.m_relationships;
|
|
1121
|
+
m_merges = other.m_merges;
|
|
1122
|
+
m_vmlDrawing = other.m_vmlDrawing;
|
|
1123
|
+
m_comments = other.m_comments;
|
|
1124
|
+
m_tables = other.m_tables;
|
|
1125
|
+
return *this;
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
/**
|
|
1129
|
+
* @details move-assign an XLWorksheet from other
|
|
1130
|
+
*/
|
|
1131
|
+
XLWorksheet& XLWorksheet::operator=(XLWorksheet&& other)
|
|
1132
|
+
{
|
|
1133
|
+
XLSheetBase<XLWorksheet>::operator=(other); // invoke base class move assignment operator
|
|
1134
|
+
m_relationships = std::move(other.m_relationships);
|
|
1135
|
+
m_merges = std::move(other.m_merges);
|
|
1136
|
+
m_vmlDrawing = std::move(other.m_vmlDrawing);
|
|
1137
|
+
m_comments = std::move(other.m_comments);
|
|
1138
|
+
m_tables = std::move(other.m_tables);
|
|
1139
|
+
return *this;
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
/**
|
|
1143
|
+
* @details
|
|
1144
|
+
*/
|
|
1145
|
+
XLColor XLWorksheet::getColor_impl() const
|
|
1146
|
+
{
|
|
1147
|
+
return XLColor(xmlDocument().document_element().child("sheetPr").child("tabColor").attribute("rgb").value());
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
/**
|
|
1151
|
+
* @details
|
|
1152
|
+
*/
|
|
1153
|
+
void XLWorksheet::setColor_impl(const XLColor& color) { setTabColor(xmlDocument(), color); }
|
|
1154
|
+
|
|
1155
|
+
/**
|
|
1156
|
+
* @details
|
|
1157
|
+
*/
|
|
1158
|
+
bool XLWorksheet::isSelected_impl() const { return tabIsSelected(xmlDocument()); }
|
|
1159
|
+
|
|
1160
|
+
/**
|
|
1161
|
+
* @details
|
|
1162
|
+
*/
|
|
1163
|
+
void XLWorksheet::setSelected_impl(bool selected) { setTabSelected(xmlDocument(), selected); }
|
|
1164
|
+
|
|
1165
|
+
/**
|
|
1166
|
+
* @details
|
|
1167
|
+
*/
|
|
1168
|
+
bool XLWorksheet::isActive_impl() const
|
|
1169
|
+
{
|
|
1170
|
+
return parentDoc().execQuery(XLQuery(XLQueryType::QuerySheetIsActive).setParam("sheetID", relationshipID())).result<bool>();
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
/**
|
|
1174
|
+
* @details
|
|
1175
|
+
*/
|
|
1176
|
+
bool XLWorksheet::setActive_impl()
|
|
1177
|
+
{
|
|
1178
|
+
return parentDoc().execCommand(XLCommand(XLCommandType::SetSheetActive).setParam("sheetID", relationshipID()));
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
/**
|
|
1182
|
+
* @details
|
|
1183
|
+
*/
|
|
1184
|
+
XLCellAssignable XLWorksheet::cell(const std::string& ref) const { return cell(XLCellReference(ref)); }
|
|
1185
|
+
|
|
1186
|
+
/**
|
|
1187
|
+
* @details
|
|
1188
|
+
*/
|
|
1189
|
+
XLCellAssignable XLWorksheet::cell(const XLCellReference& ref) const { return cell(ref.row(), ref.column()); }
|
|
1190
|
+
|
|
1191
|
+
/**
|
|
1192
|
+
* @details This function returns a pointer to an XLCell object in the worksheet. This particular overload
|
|
1193
|
+
* also serves as the main function, called by the other overloads.
|
|
1194
|
+
*/
|
|
1195
|
+
XLCellAssignable XLWorksheet::cell(uint32_t rowNumber, uint16_t columnNumber) const
|
|
1196
|
+
{
|
|
1197
|
+
const XMLNode rowNode = getRowNode(xmlDocument().document_element().child("sheetData"), rowNumber);
|
|
1198
|
+
const XMLNode cellNode = getCellNode(rowNode, columnNumber, rowNumber);
|
|
1199
|
+
// ===== Move-construct XLCellAssignable from temporary XLCell
|
|
1200
|
+
return XLCellAssignable(XLCell(cellNode, parentDoc().sharedStrings()));
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
/**
|
|
1204
|
+
* @details
|
|
1205
|
+
*/
|
|
1206
|
+
XLCellAssignable XLWorksheet::findCell(const std::string& ref) const { return findCell(XLCellReference(ref)); }
|
|
1207
|
+
|
|
1208
|
+
/**
|
|
1209
|
+
* @details
|
|
1210
|
+
*/
|
|
1211
|
+
XLCellAssignable XLWorksheet::findCell(const XLCellReference& ref) const { return findCell(ref.row(), ref.column()); }
|
|
1212
|
+
|
|
1213
|
+
/**
|
|
1214
|
+
* @details This function attempts to find a cell, but creates neither the row nor the cell XML if missing - and returns an empty XLCellAssignable instead
|
|
1215
|
+
*/
|
|
1216
|
+
XLCellAssignable XLWorksheet::findCell(uint32_t rowNumber, uint16_t columnNumber) const
|
|
1217
|
+
{
|
|
1218
|
+
return XLCellAssignable(XLCell(findCellNode(findRowNode( xmlDocument().document_element().child("sheetData"), rowNumber ), columnNumber), parentDoc().sharedStrings()));
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
/**
|
|
1222
|
+
* @details
|
|
1223
|
+
*/
|
|
1224
|
+
void XLWorksheet::setDimension(XLCellReference topLeft, XLCellReference bottomRight, bool autoCalculateLastCell)
|
|
1225
|
+
{
|
|
1226
|
+
// ===== Establish & validate corners
|
|
1227
|
+
if (bottomRight.row() == 1 && bottomRight.column() == 1 && autoCalculateLastCell) // if bottomRight is A1 and last cell shall be auto-calculated
|
|
1228
|
+
bottomRight = lastCell(); // calculate the last cell in the sheet
|
|
1229
|
+
if (bottomRight.row() < topLeft.row() || bottomRight.column() < topLeft.column() )
|
|
1230
|
+
throw XLException("XLWorksheet::setDimension: bottomRight row and column must be greater or equal to topLeft row and column respectively");
|
|
1231
|
+
|
|
1232
|
+
// ===== Assemble dimension string
|
|
1233
|
+
std::string dimension = topLeft.address();
|
|
1234
|
+
if( bottomRight > topLeft ) {
|
|
1235
|
+
using namespace std::literals::string_literals;
|
|
1236
|
+
dimension += ":"s + bottomRight.address();
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
// ===== Set dimension of the worksheet
|
|
1240
|
+
XMLNode dimensionNode = xmlDocument().document_element().child("dimension");
|
|
1241
|
+
if (dimensionNode.empty()) // ensure dimension node exists
|
|
1242
|
+
dimensionNode = xmlDocument().document_element().prepend_child("dimension");
|
|
1243
|
+
appendAndSetAttribute(dimensionNode, "ref", dimension);
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
/**
|
|
1247
|
+
* @details overload with std::string cell references
|
|
1248
|
+
*/
|
|
1249
|
+
void XLWorksheet::setDimension(std::string topLeft, std::string bottomRight) { setDimension(XLCellReference(topLeft), XLCellReference(bottomRight)); }
|
|
1250
|
+
|
|
1251
|
+
/**
|
|
1252
|
+
* @details overload with std::string range reference
|
|
1253
|
+
*/
|
|
1254
|
+
void XLWorksheet::setDimension(std::string dimension)
|
|
1255
|
+
{
|
|
1256
|
+
size_t pos = dimension.find_first_of(':');
|
|
1257
|
+
if (pos != std::string::npos) // if a cell address separator is found
|
|
1258
|
+
setDimension(dimension.substr(0, pos), dimension.substr(pos + 1, std::string::npos)); // XLCellReference constructor will throw on invalid strings
|
|
1259
|
+
else { // else: if only a single cell address is given
|
|
1260
|
+
setDimension(dimension, dimension, XLDoNotCalculateLastCell); // do not auto-calculate lastCell
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
/**
|
|
1265
|
+
* @details
|
|
1266
|
+
*/
|
|
1267
|
+
XLCellRange XLWorksheet::range() const { return range(XLCellReference("A1"), lastCell()); }
|
|
1268
|
+
|
|
1269
|
+
/**
|
|
1270
|
+
* @details
|
|
1271
|
+
*/
|
|
1272
|
+
XLCellRange XLWorksheet::range(const XLCellReference& topLeft, const XLCellReference& bottomRight) const
|
|
1273
|
+
{
|
|
1274
|
+
return XLCellRange(xmlDocument().document_element().child("sheetData"),
|
|
1275
|
+
topLeft,
|
|
1276
|
+
bottomRight,
|
|
1277
|
+
parentDoc().sharedStrings());
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
/**
|
|
1281
|
+
* @details Get a range based on two cell reference strings
|
|
1282
|
+
*/
|
|
1283
|
+
XLCellRange XLWorksheet::range(std::string const& topLeft, std::string const& bottomRight) const
|
|
1284
|
+
{
|
|
1285
|
+
return range(XLCellReference(topLeft), XLCellReference(bottomRight));
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
/**
|
|
1289
|
+
* @details Get a range based on a reference string
|
|
1290
|
+
*/
|
|
1291
|
+
XLCellRange XLWorksheet::range(std::string const& rangeReference) const
|
|
1292
|
+
{
|
|
1293
|
+
size_t pos = rangeReference.find_first_of(':');
|
|
1294
|
+
return range(rangeReference.substr(0, pos), rangeReference.substr(pos + 1, std::string::npos));
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
/**
|
|
1298
|
+
* @details
|
|
1299
|
+
* @pre
|
|
1300
|
+
* @post
|
|
1301
|
+
*/
|
|
1302
|
+
XLRowRange XLWorksheet::rows() const // 2024-04-29: patched for whitespace
|
|
1303
|
+
{
|
|
1304
|
+
const auto sheetDataNode = xmlDocument().document_element().child("sheetData");
|
|
1305
|
+
return XLRowRange(sheetDataNode,
|
|
1306
|
+
1,
|
|
1307
|
+
(sheetDataNode.last_child_of_type(pugi::node_element).empty()
|
|
1308
|
+
? 1
|
|
1309
|
+
: static_cast<uint32_t>(sheetDataNode.last_child_of_type(pugi::node_element).attribute("r").as_ullong())),
|
|
1310
|
+
parentDoc().sharedStrings());
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
/**
|
|
1314
|
+
* @details
|
|
1315
|
+
* @pre
|
|
1316
|
+
* @post
|
|
1317
|
+
*/
|
|
1318
|
+
XLRowRange XLWorksheet::rows(uint32_t rowCount) const
|
|
1319
|
+
{
|
|
1320
|
+
return XLRowRange(xmlDocument().document_element().child("sheetData"),
|
|
1321
|
+
1,
|
|
1322
|
+
rowCount,
|
|
1323
|
+
parentDoc().sharedStrings());
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
/**
|
|
1327
|
+
* @details
|
|
1328
|
+
* @pre
|
|
1329
|
+
* @post
|
|
1330
|
+
*/
|
|
1331
|
+
XLRowRange XLWorksheet::rows(uint32_t firstRow, uint32_t lastRow) const
|
|
1332
|
+
{
|
|
1333
|
+
return XLRowRange(xmlDocument().document_element().child("sheetData"),
|
|
1334
|
+
firstRow,
|
|
1335
|
+
lastRow,
|
|
1336
|
+
parentDoc().sharedStrings());
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
/**
|
|
1340
|
+
* @details Get the XLRow object corresponding to the given row number. In the XML file, all cell data are stored under
|
|
1341
|
+
* the corresponding row, and all rows have to be ordered in ascending order. If a row have no data, there may not be a
|
|
1342
|
+
* node for that row.
|
|
1343
|
+
*/
|
|
1344
|
+
XLRow XLWorksheet::row(uint32_t rowNumber) const
|
|
1345
|
+
{
|
|
1346
|
+
return XLRow { getRowNode(xmlDocument().document_element().child("sheetData"), rowNumber),
|
|
1347
|
+
parentDoc().sharedStrings() };
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
/**
|
|
1351
|
+
* @details Get the XLColumn object corresponding to the given column number. In the underlying XML data structure,
|
|
1352
|
+
* column nodes do not hold any cell data. Columns are used solely to hold data regarding column formatting.
|
|
1353
|
+
* @todo Consider simplifying this function. Can any standard algorithms be used?
|
|
1354
|
+
*/
|
|
1355
|
+
XLColumn XLWorksheet::column(uint16_t columnNumber) const
|
|
1356
|
+
{
|
|
1357
|
+
using namespace std::literals::string_literals;
|
|
1358
|
+
|
|
1359
|
+
if (columnNumber < 1 || columnNumber > OpenXLSX::MAX_COLS) // 2024-08-05: added range check
|
|
1360
|
+
throw XLException("XLWorksheet::column: columnNumber "s + std::to_string(columnNumber) + " is outside allowed range [1;"s + std::to_string(MAX_COLS) + "]"s);
|
|
1361
|
+
|
|
1362
|
+
// If no columns exists, create the <cols> node in the XML document.
|
|
1363
|
+
if (xmlDocument().document_element().child("cols").empty())
|
|
1364
|
+
xmlDocument().document_element().insert_child_before("cols", xmlDocument().document_element().child("sheetData"));
|
|
1365
|
+
|
|
1366
|
+
// ===== Find the column node, if it exists
|
|
1367
|
+
auto columnNode = xmlDocument().document_element().child("cols").find_child([&](const XMLNode node) {
|
|
1368
|
+
return (columnNumber >= node.attribute("min").as_int() && columnNumber <= node.attribute("max").as_int()) ||
|
|
1369
|
+
node.attribute("min").as_int() > columnNumber;
|
|
1370
|
+
});
|
|
1371
|
+
|
|
1372
|
+
uint16_t minColumn {};
|
|
1373
|
+
uint16_t maxColumn {};
|
|
1374
|
+
if (not columnNode.empty()) {
|
|
1375
|
+
minColumn = columnNode.attribute("min").as_int(); // only look it up once for multiple access
|
|
1376
|
+
maxColumn = columnNode.attribute("max").as_int(); // "
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
// ===== If the node exists for the column, and only spans that column, then continue...
|
|
1380
|
+
if (not columnNode.empty() && (minColumn == columnNumber) && (maxColumn == columnNumber)) {
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
// ===== If the node exists for the column, but spans several columns, split it into individual nodes, and set columnNode to the right
|
|
1384
|
+
// one...
|
|
1385
|
+
// BUGFIX 2024-04-27 - old if condition would split a multi-column setting even if columnNumber is < minColumn (see lambda return value
|
|
1386
|
+
// above)
|
|
1387
|
+
// NOTE 2024-04-27: the column splitting for loaded files is already handled in the constructor, technically this code is not necessary
|
|
1388
|
+
// here
|
|
1389
|
+
else if (not columnNode.empty() && (columnNumber >= minColumn) && (minColumn != maxColumn)) {
|
|
1390
|
+
// ===== Split the node in individual columns...
|
|
1391
|
+
columnNode.attribute("min").set_value(maxColumn); // Limit the original node to a single column
|
|
1392
|
+
for (int i = minColumn; i < maxColumn; ++i) {
|
|
1393
|
+
auto node = xmlDocument().document_element().child("cols").insert_copy_before(columnNode, columnNode);
|
|
1394
|
+
node.attribute("min").set_value(i);
|
|
1395
|
+
node.attribute("max").set_value(i);
|
|
1396
|
+
}
|
|
1397
|
+
// BUGFIX 2024-04-27: the original node should not be deleted, but - in line with XLWorksheet constructor - is now limited above to
|
|
1398
|
+
// min = max attribute
|
|
1399
|
+
// // ===== Delete the original node
|
|
1400
|
+
// columnNode = columnNode.previous_sibling_of_type(pugi::node_element); // due to insert loop, previous node should be guaranteed
|
|
1401
|
+
// // to be an element node
|
|
1402
|
+
// xmlDocument().document_element().child("cols").remove_child(columnNode.next_sibling());
|
|
1403
|
+
|
|
1404
|
+
// ===== Find the node corresponding to the column number - BUGFIX 2024-04-27: loop should abort on empty node
|
|
1405
|
+
while (not columnNode.empty() && columnNode.attribute("min").as_int() != columnNumber)
|
|
1406
|
+
columnNode = columnNode.previous_sibling_of_type(pugi::node_element);
|
|
1407
|
+
if (columnNode.empty())
|
|
1408
|
+
throw XLInternalError("XLWorksheet::"s + __func__ + ": column node for index "s + std::to_string(columnNumber) +
|
|
1409
|
+
"not found after splitting column nodes"s);
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
// ===== If a node for the column does NOT exist, but a node for a higher column exists...
|
|
1413
|
+
else if (not columnNode.empty() && minColumn > columnNumber) {
|
|
1414
|
+
columnNode = xmlDocument().document_element().child("cols").insert_child_before("col", columnNode);
|
|
1415
|
+
columnNode.append_attribute("min") = columnNumber;
|
|
1416
|
+
columnNode.append_attribute("max") = columnNumber;
|
|
1417
|
+
columnNode.append_attribute("width") = 9.8; // NOLINT
|
|
1418
|
+
columnNode.append_attribute("customWidth") = 0;
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
// ===== Otherwise, the end of the list is reached, and a new node is appended
|
|
1422
|
+
else if (columnNode.empty()) {
|
|
1423
|
+
columnNode = xmlDocument().document_element().child("cols").append_child("col");
|
|
1424
|
+
columnNode.append_attribute("min") = columnNumber;
|
|
1425
|
+
columnNode.append_attribute("max") = columnNumber;
|
|
1426
|
+
columnNode.append_attribute("width") = 9.8; // NOLINT
|
|
1427
|
+
columnNode.append_attribute("customWidth") = 0;
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
if (columnNode.empty()) {
|
|
1431
|
+
using namespace std::literals::string_literals;
|
|
1432
|
+
throw XLInternalError("XLWorksheet::"s + __func__ + ": was unable to find or create node for column "s +
|
|
1433
|
+
std::to_string(columnNumber));
|
|
1434
|
+
}
|
|
1435
|
+
return XLColumn(columnNode);
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
/**
|
|
1439
|
+
* @details Get the column with the given column reference by converting the columnRef into a column number
|
|
1440
|
+
* and forwarding the implementation to XLColumn XLWorksheet::column(uint16_t columnNumber) const
|
|
1441
|
+
*/
|
|
1442
|
+
XLColumn XLWorksheet::column(std::string const& columnRef) const { return column(XLCellReference::columnAsNumber(columnRef)); }
|
|
1443
|
+
|
|
1444
|
+
/**
|
|
1445
|
+
* @details Returns an XLCellReference to the last cell using rowCount() and columnCount() methods.
|
|
1446
|
+
*/
|
|
1447
|
+
XLCellReference XLWorksheet::lastCell() const noexcept {
|
|
1448
|
+
uint32_t rowCnt = rowCount();
|
|
1449
|
+
uint16_t colCnt = columnCount();
|
|
1450
|
+
if( rowCnt > 0 && colCnt > 0 ) return XLCellReference( rowCnt, colCnt ); // Fix for issue #382: bounds check without exception
|
|
1451
|
+
return XLCellReference(); // default-construct reference to last cell (= "A1")
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
/**
|
|
1455
|
+
* @details Iterates through the rows and finds the maximum number of cells.
|
|
1456
|
+
*/
|
|
1457
|
+
uint16_t XLWorksheet::columnCount() const noexcept
|
|
1458
|
+
{
|
|
1459
|
+
uint16_t maxCount = 0; // Pull request: Update XLSheet.cpp with correct type #176, Explicitely cast to unsigned short int #163
|
|
1460
|
+
XLRowRange rowsRange = rows();
|
|
1461
|
+
for (XLRowIterator rowIt = rowsRange.begin(); rowIt != rowsRange.end(); ++rowIt ) {
|
|
1462
|
+
if (rowIt.rowExists()) {
|
|
1463
|
+
uint16_t cellCount = rowIt->cellCount();
|
|
1464
|
+
maxCount = std::max(cellCount, maxCount);
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
return maxCount;
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
/**
|
|
1471
|
+
* @details Finds the last row (node) and returns the row number.
|
|
1472
|
+
*/
|
|
1473
|
+
uint32_t XLWorksheet::rowCount() const noexcept
|
|
1474
|
+
{
|
|
1475
|
+
return static_cast<uint32_t>(
|
|
1476
|
+
xmlDocument().document_element().child("sheetData").last_child_of_type(pugi::node_element).attribute("r").as_ullong());
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
/**
|
|
1480
|
+
* @details finds a given row and deletes it
|
|
1481
|
+
*/
|
|
1482
|
+
bool XLWorksheet::deleteRow(uint32_t rowNumber)
|
|
1483
|
+
{
|
|
1484
|
+
XMLNode row = xmlDocument().document_element().child("sheetData").first_child_of_type(pugi::node_element);
|
|
1485
|
+
XMLNode lastRow = xmlDocument().document_element().child("sheetData").last_child_of_type(pugi::node_element);
|
|
1486
|
+
|
|
1487
|
+
// ===== Fail if rowNumber is not in XML
|
|
1488
|
+
if (rowNumber < row.attribute("r").as_ullong() || rowNumber > lastRow.attribute("r").as_ullong()) return false;
|
|
1489
|
+
|
|
1490
|
+
// ===== If rowNumber is closer to first (existing) row than to last row, search forwards
|
|
1491
|
+
if (rowNumber - row.attribute("r").as_ullong() < lastRow.attribute("r").as_ullong() - rowNumber)
|
|
1492
|
+
while (not row.empty() && (row.attribute("r").as_ullong() < rowNumber)) row = row.next_sibling_of_type(pugi::node_element);
|
|
1493
|
+
|
|
1494
|
+
// ===== Otherwise, search backwards
|
|
1495
|
+
else {
|
|
1496
|
+
row = lastRow;
|
|
1497
|
+
while (not row.empty() && (row.attribute("r").as_ullong() > rowNumber)) row = row.previous_sibling_of_type(pugi::node_element);
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
if (row.attribute("r").as_ullong() != rowNumber) return false; // row not found in XML
|
|
1501
|
+
|
|
1502
|
+
// ===== If row was located: remove it
|
|
1503
|
+
return xmlDocument().document_element().child("sheetData").remove_child(row);
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1506
|
+
/**
|
|
1507
|
+
* @details
|
|
1508
|
+
*/
|
|
1509
|
+
void XLWorksheet::updateSheetName(const std::string& oldName, const std::string& newName) // 2024-04-29: patched for whitespace
|
|
1510
|
+
{
|
|
1511
|
+
// ===== Set up temporary variables
|
|
1512
|
+
std::string oldNameTemp = oldName;
|
|
1513
|
+
std::string newNameTemp = newName;
|
|
1514
|
+
std::string formula;
|
|
1515
|
+
|
|
1516
|
+
// ===== If the sheet name contains spaces, it should be enclosed in single quotes (')
|
|
1517
|
+
if (oldName.find(' ') != std::string::npos) oldNameTemp = "\'" + oldName + "\'";
|
|
1518
|
+
if (newName.find(' ') != std::string::npos) newNameTemp = "\'" + newName + "\'";
|
|
1519
|
+
|
|
1520
|
+
// ===== Ensure only sheet names are replaced (references to sheets always ends with a '!')
|
|
1521
|
+
oldNameTemp += '!';
|
|
1522
|
+
newNameTemp += '!';
|
|
1523
|
+
|
|
1524
|
+
// ===== Iterate through all defined names
|
|
1525
|
+
XMLNode row = xmlDocument().document_element().child("sheetData").first_child_of_type(pugi::node_element);
|
|
1526
|
+
for (; not row.empty(); row = row.next_sibling_of_type(pugi::node_element)) {
|
|
1527
|
+
for (XMLNode cell = row.first_child_of_type(pugi::node_element);
|
|
1528
|
+
not cell.empty();
|
|
1529
|
+
cell = cell.next_sibling_of_type(pugi::node_element))
|
|
1530
|
+
{
|
|
1531
|
+
if (!XLCell(cell, parentDoc().sharedStrings()).hasFormula()) continue;
|
|
1532
|
+
|
|
1533
|
+
formula = XLCell(cell, parentDoc().sharedStrings()).formula().get();
|
|
1534
|
+
|
|
1535
|
+
// ===== Skip if formula contains a '[' and ']' (means that the defined refers to external workbook)
|
|
1536
|
+
if (formula.find('[') == std::string::npos && formula.find(']') == std::string::npos) {
|
|
1537
|
+
// ===== For all instances of the old sheet name in the formula, replace with the new name.
|
|
1538
|
+
while (formula.find(oldNameTemp) != std::string::npos) { // NOLINT
|
|
1539
|
+
formula.replace(formula.find(oldNameTemp), oldNameTemp.length(), newNameTemp);
|
|
1540
|
+
}
|
|
1541
|
+
XLCell(cell, parentDoc().sharedStrings()).formula() = formula;
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
|
|
1547
|
+
/**
|
|
1548
|
+
* @details upon first access, ensure that the worksheet's <mergeCells> tag exists, and create an XLMergeCells object
|
|
1549
|
+
*/
|
|
1550
|
+
XLMergeCells & XLWorksheet::merges()
|
|
1551
|
+
{
|
|
1552
|
+
if (!m_merges.valid())
|
|
1553
|
+
m_merges = XLMergeCells(xmlDocument().document_element(), m_nodeOrder);
|
|
1554
|
+
return m_merges;
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
/**
|
|
1558
|
+
* @details create a new mergeCell element in the worksheet mergeCells array, with the only
|
|
1559
|
+
* attribute being ref="A1:D6" (with the top left and bottom right cells of the range)
|
|
1560
|
+
* The mergeCell element otherwise remains empty (no value)
|
|
1561
|
+
* The mergeCells attribute count will be increased by one
|
|
1562
|
+
* If emptyHiddenCells is true (XLEmptyHiddenCells), the values of all but the top left cell of the range
|
|
1563
|
+
* will be deleted (not from shared strings)
|
|
1564
|
+
* If any cell within the range is the sheet's active cell, the active cell will be set to the
|
|
1565
|
+
* top left corner of rangeToMerge
|
|
1566
|
+
*/
|
|
1567
|
+
void XLWorksheet::mergeCells(XLCellRange const& rangeToMerge, bool emptyHiddenCells)
|
|
1568
|
+
{
|
|
1569
|
+
if (rangeToMerge.numRows() * rangeToMerge.numColumns() < 2) {
|
|
1570
|
+
using namespace std::literals::string_literals;
|
|
1571
|
+
throw XLInputError("XLWorksheet::"s + __func__ + ": rangeToMerge must comprise at least 2 cells"s);
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
merges().appendMerge(rangeToMerge.address());
|
|
1575
|
+
if (emptyHiddenCells) {
|
|
1576
|
+
// ===== Iterate over rangeToMerge, delete values & attributes (except r and s) for all but the first cell in the range
|
|
1577
|
+
XLCellIterator it = rangeToMerge.begin();
|
|
1578
|
+
++it; // leave first cell untouched
|
|
1579
|
+
while (it != rangeToMerge.end()) {
|
|
1580
|
+
// ===== When merging with emptyHiddenCells in LO, subsequent unmerging restores the cell styles -> so keep them here as well
|
|
1581
|
+
it->clear(XLKeepCellStyle); // clear cell contents except style
|
|
1582
|
+
++it;
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
/**
|
|
1588
|
+
* @details Convenience wrapper for the previous function, using a std::string range reference
|
|
1589
|
+
*/
|
|
1590
|
+
void XLWorksheet::mergeCells(const std::string& rangeReference, bool emptyHiddenCells) {
|
|
1591
|
+
mergeCells(range(rangeReference), emptyHiddenCells);
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
/**
|
|
1595
|
+
* @details check if rangeToUnmerge exists in mergeCells array & remove it
|
|
1596
|
+
*/
|
|
1597
|
+
void XLWorksheet::unmergeCells(XLCellRange const& rangeToUnmerge)
|
|
1598
|
+
{
|
|
1599
|
+
int32_t mergeIndex = merges().findMerge(rangeToUnmerge.address());
|
|
1600
|
+
if (mergeIndex != -1)
|
|
1601
|
+
merges().deleteMerge(mergeIndex);
|
|
1602
|
+
else {
|
|
1603
|
+
using namespace std::literals::string_literals;
|
|
1604
|
+
throw XLInputError("XLWorksheet::"s + __func__ + ": merged range "s + rangeToUnmerge.address() + " does not exist"s);
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
/**
|
|
1609
|
+
* @details Convenience wrapper for the previous function, using a std::string range reference
|
|
1610
|
+
*/
|
|
1611
|
+
void XLWorksheet::unmergeCells(const std::string& rangeReference)
|
|
1612
|
+
{
|
|
1613
|
+
unmergeCells(range(rangeReference));
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
/**
|
|
1617
|
+
* @details Retrieve the column's format
|
|
1618
|
+
*/
|
|
1619
|
+
XLStyleIndex XLWorksheet::getColumnFormat(uint16_t columnNumber) const { return column(columnNumber).format(); }
|
|
1620
|
+
XLStyleIndex XLWorksheet::getColumnFormat(const std::string& columnNumber) const { return getColumnFormat(XLCellReference::columnAsNumber(columnNumber)); }
|
|
1621
|
+
|
|
1622
|
+
/**
|
|
1623
|
+
* @details Set the style for the identified column,
|
|
1624
|
+
* then iterate over all rows to set the same style for existing cells in that column
|
|
1625
|
+
*/
|
|
1626
|
+
bool XLWorksheet::setColumnFormat(uint16_t columnNumber, XLStyleIndex cellFormatIndex)
|
|
1627
|
+
{
|
|
1628
|
+
if (!column(columnNumber).setFormat(cellFormatIndex)) // attempt to set column format
|
|
1629
|
+
return false; // early fail
|
|
1630
|
+
|
|
1631
|
+
XLRowRange allRows = rows();
|
|
1632
|
+
for (XLRowIterator rowIt = allRows.begin(); rowIt != allRows.end(); ++rowIt) {
|
|
1633
|
+
XLCell curCell = rowIt->findCell(columnNumber);
|
|
1634
|
+
if (curCell && !curCell.setCellFormat(cellFormatIndex)) // attempt to set cell format for a non empty cell
|
|
1635
|
+
return false; // failure to set cell format
|
|
1636
|
+
}
|
|
1637
|
+
return true; // if loop finished nominally: success
|
|
1638
|
+
}
|
|
1639
|
+
bool XLWorksheet::setColumnFormat(const std::string& columnNumber, XLStyleIndex cellFormatIndex) { return setColumnFormat(XLCellReference::columnAsNumber(columnNumber), cellFormatIndex); }
|
|
1640
|
+
|
|
1641
|
+
/**
|
|
1642
|
+
* @details Retrieve the row's format
|
|
1643
|
+
*/
|
|
1644
|
+
XLStyleIndex XLWorksheet::getRowFormat(uint16_t rowNumber) const { return row(rowNumber).format(); }
|
|
1645
|
+
|
|
1646
|
+
/**
|
|
1647
|
+
* @details Set the style for the identified row,
|
|
1648
|
+
* then iterate over all existing cells in the row to set the same style
|
|
1649
|
+
*/
|
|
1650
|
+
bool XLWorksheet::setRowFormat(uint32_t rowNumber, XLStyleIndex cellFormatIndex)
|
|
1651
|
+
{
|
|
1652
|
+
if (!row(rowNumber).setFormat(cellFormatIndex)) // attempt to set row format
|
|
1653
|
+
return false; // early fail
|
|
1654
|
+
|
|
1655
|
+
XLRowDataRange rowCells = row(rowNumber).cells();
|
|
1656
|
+
for (XLRowDataIterator cellIt = rowCells.begin(); cellIt != rowCells.end(); ++cellIt)
|
|
1657
|
+
if (!cellIt->setCellFormat(cellFormatIndex)) // attempt to set cell format
|
|
1658
|
+
return false;
|
|
1659
|
+
return true; // if loop finished nominally: success
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1662
|
+
/**
|
|
1663
|
+
* @details Provide access to worksheet conditional formats
|
|
1664
|
+
*/
|
|
1665
|
+
XLConditionalFormats XLWorksheet::conditionalFormats() const { return XLConditionalFormats(xmlDocument().document_element()); }
|
|
1666
|
+
|
|
1667
|
+
/**
|
|
1668
|
+
* @brief Set the <sheetProtection> attributes sheet, objects and scenarios respectively
|
|
1669
|
+
*/
|
|
1670
|
+
bool XLWorksheet::protectSheet(bool set) {
|
|
1671
|
+
XMLNode sheetNode = xmlDocument().document_element();
|
|
1672
|
+
return appendAndSetNodeAttribute(sheetNode, "sheetProtection", "sheet", (set ? "true" : "false"),
|
|
1673
|
+
/**/ XLKeepAttributes, m_nodeOrder).empty() == false;
|
|
1674
|
+
}
|
|
1675
|
+
bool XLWorksheet::protectObjects(bool set) {
|
|
1676
|
+
XMLNode sheetNode = xmlDocument().document_element();
|
|
1677
|
+
return appendAndSetNodeAttribute(sheetNode, "sheetProtection", "objects", (set ? "true" : "false"),
|
|
1678
|
+
/**/ XLKeepAttributes, m_nodeOrder).empty() == false;
|
|
1679
|
+
}
|
|
1680
|
+
bool XLWorksheet::protectScenarios(bool set) {
|
|
1681
|
+
XMLNode sheetNode = xmlDocument().document_element();
|
|
1682
|
+
return appendAndSetNodeAttribute(sheetNode, "sheetProtection", "scenarios", (set ? "true" : "false"),
|
|
1683
|
+
/**/ XLKeepAttributes, m_nodeOrder).empty() == false;
|
|
1684
|
+
}
|
|
1685
|
+
|
|
1686
|
+
|
|
1687
|
+
/**
|
|
1688
|
+
* @brief Set the <sheetProtection> attributes insertColumns, insertRows, deleteColumns, deleteRows, selectLockedCells, selectUnlockedCells
|
|
1689
|
+
*/
|
|
1690
|
+
bool XLWorksheet::allowInsertColumns(bool set) {
|
|
1691
|
+
XMLNode sheetNode = xmlDocument().document_element();
|
|
1692
|
+
return appendAndSetNodeAttribute(sheetNode, "sheetProtection", "insertColumns", (!set ? "true" : "false"), // invert allow set(ting) for the protect setting
|
|
1693
|
+
/**/ XLKeepAttributes, m_nodeOrder).empty() == false;
|
|
1694
|
+
}
|
|
1695
|
+
bool XLWorksheet::allowInsertRows(bool set) {
|
|
1696
|
+
XMLNode sheetNode = xmlDocument().document_element();
|
|
1697
|
+
return appendAndSetNodeAttribute(sheetNode, "sheetProtection", "insertRows", (!set ? "true" : "false"), // invert allow set(ting) for the protect setting
|
|
1698
|
+
/**/ XLKeepAttributes, m_nodeOrder).empty() == false;
|
|
1699
|
+
}
|
|
1700
|
+
bool XLWorksheet::allowDeleteColumns(bool set) {
|
|
1701
|
+
XMLNode sheetNode = xmlDocument().document_element();
|
|
1702
|
+
return appendAndSetNodeAttribute(sheetNode, "sheetProtection", "deleteColumns", (!set ? "true" : "false"), // invert allow set(ting) for the protect setting
|
|
1703
|
+
/**/ XLKeepAttributes, m_nodeOrder).empty() == false;
|
|
1704
|
+
}
|
|
1705
|
+
bool XLWorksheet::allowDeleteRows(bool set) {
|
|
1706
|
+
XMLNode sheetNode = xmlDocument().document_element();
|
|
1707
|
+
return appendAndSetNodeAttribute(sheetNode, "sheetProtection", "deleteRows", (!set ? "true" : "false"), // invert allow set(ting) for the protect setting
|
|
1708
|
+
/**/ XLKeepAttributes, m_nodeOrder).empty() == false;
|
|
1709
|
+
}
|
|
1710
|
+
bool XLWorksheet::allowSelectLockedCells(bool set) {
|
|
1711
|
+
XMLNode sheetNode = xmlDocument().document_element();
|
|
1712
|
+
return appendAndSetNodeAttribute(sheetNode, "sheetProtection", "selectLockedCells", (!set ? "true" : "false"), // invert allow set(ting) for the protect setting
|
|
1713
|
+
/**/ XLKeepAttributes, m_nodeOrder).empty() == false;
|
|
1714
|
+
}
|
|
1715
|
+
bool XLWorksheet::allowSelectUnlockedCells(bool set) {
|
|
1716
|
+
XMLNode sheetNode = xmlDocument().document_element();
|
|
1717
|
+
return appendAndSetNodeAttribute(sheetNode, "sheetProtection", "selectUnlockedCells", (!set ? "true" : "false"), // invert allow set(ting) for the protect setting
|
|
1718
|
+
/**/ XLKeepAttributes, m_nodeOrder).empty() == false;
|
|
1719
|
+
}
|
|
1720
|
+
|
|
1721
|
+
/**
|
|
1722
|
+
* @details store the password hash to the underlying XML node
|
|
1723
|
+
*/
|
|
1724
|
+
bool XLWorksheet::setPasswordHash(std::string hash)
|
|
1725
|
+
{
|
|
1726
|
+
XMLNode sheetNode = xmlDocument().document_element();
|
|
1727
|
+
return appendAndSetNodeAttribute(sheetNode, "sheetProtection", "password", hash,
|
|
1728
|
+
/**/ XLKeepAttributes, m_nodeOrder).empty() == false;
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1731
|
+
/**
|
|
1732
|
+
* @details calculate the password hash and store it to the underlying XML node
|
|
1733
|
+
*/
|
|
1734
|
+
bool XLWorksheet::setPassword(std::string password) {
|
|
1735
|
+
return setPasswordHash(password.length() ? ExcelPasswordHashAsString(password) : std::string(""));
|
|
1736
|
+
}
|
|
1737
|
+
/**
|
|
1738
|
+
* @details
|
|
1739
|
+
*/
|
|
1740
|
+
bool XLWorksheet::clearPassword() { return setPasswordHash(""); }
|
|
1741
|
+
|
|
1742
|
+
/**
|
|
1743
|
+
* @details
|
|
1744
|
+
*/
|
|
1745
|
+
bool XLWorksheet::clearSheetProtection() { return xmlDocument().document_element().remove_child("sheetProtection"); }
|
|
1746
|
+
|
|
1747
|
+
/**
|
|
1748
|
+
* @details
|
|
1749
|
+
*/
|
|
1750
|
+
bool XLWorksheet::sheetProtected() const { return xmlDocument().document_element().child("sheetProtection").attribute("sheet").as_bool(false); }
|
|
1751
|
+
bool XLWorksheet::objectsProtected() const { return xmlDocument().document_element().child("sheetProtection").attribute("objects").as_bool(false); }
|
|
1752
|
+
bool XLWorksheet::scenariosProtected() const { return xmlDocument().document_element().child("sheetProtection").attribute("scenarios").as_bool(false); }
|
|
1753
|
+
|
|
1754
|
+
/**
|
|
1755
|
+
* @details the following settings are inverted to get from "protected" meaning in OOXML to "allowed" meaning in the OpenXLSX API
|
|
1756
|
+
* @details insertColumns, insertRows, deleteColumns, deleteRows are protected by default
|
|
1757
|
+
* @details selectLockedCells, selectUnlockedCells are allowed by default
|
|
1758
|
+
*/
|
|
1759
|
+
bool XLWorksheet::insertColumnsAllowed() const { return not xmlDocument().document_element().child("sheetProtection").attribute("insertColumns").as_bool(true); }
|
|
1760
|
+
bool XLWorksheet::insertRowsAllowed() const { return not xmlDocument().document_element().child("sheetProtection").attribute("insertRows").as_bool(true); }
|
|
1761
|
+
bool XLWorksheet::deleteColumnsAllowed() const { return not xmlDocument().document_element().child("sheetProtection").attribute("deleteColumns").as_bool(true); }
|
|
1762
|
+
bool XLWorksheet::deleteRowsAllowed() const { return not xmlDocument().document_element().child("sheetProtection").attribute("deleteRows").as_bool(true); }
|
|
1763
|
+
bool XLWorksheet::selectLockedCellsAllowed() const { return not xmlDocument().document_element().child("sheetProtection").attribute("selectLockedCells").as_bool(false); }
|
|
1764
|
+
bool XLWorksheet::selectUnlockedCellsAllowed() const { return not xmlDocument().document_element().child("sheetProtection").attribute("selectUnlockedCells").as_bool(false); }
|
|
1765
|
+
/**
|
|
1766
|
+
* @details
|
|
1767
|
+
*/
|
|
1768
|
+
std::string XLWorksheet::passwordHash() const { return xmlDocument().document_element().child("sheetProtection").attribute("password").value(); }
|
|
1769
|
+
|
|
1770
|
+
/**
|
|
1771
|
+
* @details
|
|
1772
|
+
*/
|
|
1773
|
+
bool XLWorksheet::passwordIsSet() const { return passwordHash().length() > 0; }
|
|
1774
|
+
|
|
1775
|
+
/**
|
|
1776
|
+
* @details
|
|
1777
|
+
*/
|
|
1778
|
+
std::string XLWorksheet::sheetProtectionSummary() const
|
|
1779
|
+
{
|
|
1780
|
+
using namespace std::literals::string_literals;
|
|
1781
|
+
return "sheet is"s + ( sheetProtected() ? ""s : " not"s ) + " protected"s
|
|
1782
|
+
+ ", objects are"s + ( objectsProtected() ? ""s : " not"s ) + " protected"s
|
|
1783
|
+
+ ", scenarios are"s + ( scenariosProtected() ? ""s : " not"s ) + " protected"s
|
|
1784
|
+
+ ", insertColumns is"s + ( insertColumnsAllowed() ? ""s : " not"s ) + " allowed"s
|
|
1785
|
+
+ ", insertRows is"s + ( insertRowsAllowed() ? ""s : " not"s ) + " allowed"s
|
|
1786
|
+
+ ", deleteColumns is"s + ( deleteColumnsAllowed() ? ""s : " not"s ) + " allowed"s
|
|
1787
|
+
+ ", deleteRows is"s + ( deleteRowsAllowed() ? ""s : " not"s ) + " allowed"s
|
|
1788
|
+
+ ", selectLockedCells is"s + ( selectLockedCellsAllowed() ? ""s : " not"s ) + " allowed"s
|
|
1789
|
+
+ ", selectUnlockedCells is"s + ( selectUnlockedCellsAllowed() ? ""s : " not"s ) + " allowed"s
|
|
1790
|
+
+ ", password is"s + ( passwordIsSet() ? ""s : " not"s ) + " set"s
|
|
1791
|
+
+ ( passwordIsSet() ? ( ", passwordHash is "s + passwordHash() ) : ""s )
|
|
1792
|
+
;
|
|
1793
|
+
}
|
|
1794
|
+
|
|
1795
|
+
/**
|
|
1796
|
+
* @details functions to test whether a VMLDrawing / Comments / Tables entry exists for this worksheet (without creating those entries)
|
|
1797
|
+
*/
|
|
1798
|
+
bool XLWorksheet::hasRelationships() const { return parentDoc().hasSheetRelationships(sheetXmlNumber()); }
|
|
1799
|
+
bool XLWorksheet::hasVmlDrawing() const { return parentDoc().hasSheetVmlDrawing( sheetXmlNumber()); }
|
|
1800
|
+
bool XLWorksheet::hasComments() const { return parentDoc().hasSheetComments( sheetXmlNumber()); }
|
|
1801
|
+
bool XLWorksheet::hasTables() const { return parentDoc().hasSheetTables( sheetXmlNumber()); }
|
|
1802
|
+
|
|
1803
|
+
/**
|
|
1804
|
+
* @details fetches XLVmlDrawing for the sheet - creates & assigns the class if empty
|
|
1805
|
+
*/
|
|
1806
|
+
XLVmlDrawing& XLWorksheet::vmlDrawing()
|
|
1807
|
+
{
|
|
1808
|
+
if (!m_vmlDrawing.valid()) {
|
|
1809
|
+
// ===== Append xdr namespace attribute to worksheet if not present
|
|
1810
|
+
XMLNode docElement = xmlDocument().document_element();
|
|
1811
|
+
XMLAttribute xdrNamespace = appendAndGetAttribute(docElement, "xmlns:xdr", "");
|
|
1812
|
+
xdrNamespace = "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing";
|
|
1813
|
+
|
|
1814
|
+
std::ignore = relationships(); // create sheet relationships if not existing
|
|
1815
|
+
|
|
1816
|
+
// ===== Trigger parentDoc to create drawing XML file and return it
|
|
1817
|
+
uint16_t sheetXmlNo = sheetXmlNumber();
|
|
1818
|
+
m_vmlDrawing = parentDoc().sheetVmlDrawing(sheetXmlNo); // fetch drawing for this worksheet
|
|
1819
|
+
if (!m_vmlDrawing.valid())
|
|
1820
|
+
throw XLException("XLWorksheet::vmlDrawing(): could not create drawing XML");
|
|
1821
|
+
std::string drawingRelativePath = getPathARelativeToPathB( m_vmlDrawing.getXmlPath(), getXmlPath() );
|
|
1822
|
+
XLRelationshipItem vmlDrawingRelationship;
|
|
1823
|
+
if (!m_relationships.targetExists(drawingRelativePath))
|
|
1824
|
+
vmlDrawingRelationship = m_relationships.addRelationship(XLRelationshipType::VMLDrawing, drawingRelativePath);
|
|
1825
|
+
else
|
|
1826
|
+
vmlDrawingRelationship = m_relationships.relationshipByTarget(drawingRelativePath);
|
|
1827
|
+
if (vmlDrawingRelationship.empty())
|
|
1828
|
+
throw XLException("XLWorksheet::vmlDrawing(): could not add determine sheet relationship for VML Drawing");
|
|
1829
|
+
XMLNode legacyDrawing = appendAndGetNode(docElement, "legacyDrawing", m_nodeOrder);
|
|
1830
|
+
if (legacyDrawing.empty())
|
|
1831
|
+
throw XLException("XLWorksheet::vmlDrawing(): could not add <legacyDrawing> element to worksheet XML");
|
|
1832
|
+
appendAndSetAttribute(legacyDrawing, "r:id", vmlDrawingRelationship.id());
|
|
1833
|
+
}
|
|
1834
|
+
|
|
1835
|
+
return m_vmlDrawing;
|
|
1836
|
+
}
|
|
1837
|
+
|
|
1838
|
+
/**
|
|
1839
|
+
* @details fetches XLComments for the sheet - creates & assigns the class if empty
|
|
1840
|
+
*/
|
|
1841
|
+
XLComments& XLWorksheet::comments()
|
|
1842
|
+
{
|
|
1843
|
+
if (!m_comments.valid()) {
|
|
1844
|
+
std::ignore = relationships(); // create sheet relationships if not existing
|
|
1845
|
+
// ===== Unfortunately, xl/vmlDrawing#.vml is needed to support comments: append xdr namespace attribute to worksheet if not present
|
|
1846
|
+
std::ignore = vmlDrawing(); // create sheet VML drawing if not existing
|
|
1847
|
+
|
|
1848
|
+
|
|
1849
|
+
// ===== Trigger parentDoc to create comment XML file and return it
|
|
1850
|
+
uint16_t sheetXmlNo = sheetXmlNumber();
|
|
1851
|
+
// std::cout << "worksheet comments for sheetId " << sheetXmlNo << std::endl;
|
|
1852
|
+
m_comments = parentDoc().sheetComments(sheetXmlNo); // fetch comments for this worksheet
|
|
1853
|
+
if (!m_comments.valid())
|
|
1854
|
+
throw XLException("XLWorksheet::comments(): could not create comments XML");
|
|
1855
|
+
m_comments.setVmlDrawing(m_vmlDrawing); // link XLVmlDrawing object to the comments so it can be modified from there
|
|
1856
|
+
std::string commentsRelativePath = getPathARelativeToPathB( m_comments.getXmlPath(), getXmlPath() );
|
|
1857
|
+
if (!m_relationships.targetExists(commentsRelativePath))
|
|
1858
|
+
m_relationships.addRelationship(XLRelationshipType::Comments, commentsRelativePath);
|
|
1859
|
+
}
|
|
1860
|
+
|
|
1861
|
+
return m_comments;
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1864
|
+
/**
|
|
1865
|
+
* @details fetches XLTables for the sheet - creates & assigns the class if empty
|
|
1866
|
+
*/
|
|
1867
|
+
XLTables& XLWorksheet::tables()
|
|
1868
|
+
{
|
|
1869
|
+
if (!m_tables.valid()) {
|
|
1870
|
+
std::ignore = relationships(); // create sheet relationships if not existing
|
|
1871
|
+
|
|
1872
|
+
|
|
1873
|
+
// ===== Trigger parentDoc to create tables XML file and return it
|
|
1874
|
+
uint16_t sheetXmlNo = sheetXmlNumber();
|
|
1875
|
+
// std::cout << "worksheet tables for sheetId " << sheetXmlNo << std::endl;
|
|
1876
|
+
m_tables = parentDoc().sheetTables(sheetXmlNo); // fetch tables for this worksheet
|
|
1877
|
+
if (!m_tables.valid())
|
|
1878
|
+
throw XLException("XLWorksheet::tables(): could not create tables XML");
|
|
1879
|
+
std::string tablesRelativePath = getPathARelativeToPathB( m_tables.getXmlPath(), getXmlPath() );
|
|
1880
|
+
if (!m_relationships.targetExists(tablesRelativePath))
|
|
1881
|
+
m_relationships.addRelationship(XLRelationshipType::Table, tablesRelativePath);
|
|
1882
|
+
}
|
|
1883
|
+
|
|
1884
|
+
return m_tables;
|
|
1885
|
+
}
|
|
1886
|
+
|
|
1887
|
+
/**
|
|
1888
|
+
* @details perform a pattern matching on getXmlPath for (regex) .*xl/worksheets/sheet([0-9]*)\.xml$ and extract the numeric part \1
|
|
1889
|
+
*/
|
|
1890
|
+
uint16_t XLWorksheet::sheetXmlNumber() const
|
|
1891
|
+
{
|
|
1892
|
+
constexpr const char *searchPattern = "xl/worksheets/sheet";
|
|
1893
|
+
std::string xmlPath = getXmlPath();
|
|
1894
|
+
size_t pos = xmlPath.find(searchPattern); // ensure compatibility with expected path
|
|
1895
|
+
if (pos == std::string::npos) return 0;
|
|
1896
|
+
pos += strlen(searchPattern);
|
|
1897
|
+
size_t pos2 = pos;
|
|
1898
|
+
while (std::isdigit(xmlPath[pos2])) ++pos2; // find sheet number in xmlPath - aborts on end of string
|
|
1899
|
+
if (pos2 == pos || xmlPath.substr(pos2) != ".xml" ) return 0; // ensure compatibility with expected path
|
|
1900
|
+
|
|
1901
|
+
// success: convert the sheet number part of xmlPath to uint16_t
|
|
1902
|
+
return static_cast<uint16_t>(std::stoi(xmlPath.substr(pos, pos2 - pos)));
|
|
1903
|
+
}
|
|
1904
|
+
|
|
1905
|
+
/**
|
|
1906
|
+
* @details fetches XLRelationships for the sheet - creates & assigns the class if empty
|
|
1907
|
+
*/
|
|
1908
|
+
XLRelationships& XLWorksheet::relationships()
|
|
1909
|
+
{
|
|
1910
|
+
if (!m_relationships.valid()){
|
|
1911
|
+
// trigger parentDoc to create relationships XML file and relationship and return it
|
|
1912
|
+
m_relationships = parentDoc().sheetRelationships(sheetXmlNumber()); // fetch relationships for this worksheet
|
|
1913
|
+
}
|
|
1914
|
+
if (!m_relationships.valid())
|
|
1915
|
+
throw XLException("XLWorksheet::relationships(): could not create relationships XML");
|
|
1916
|
+
return m_relationships;
|
|
1917
|
+
}
|
|
1918
|
+
|
|
1919
|
+
/**
|
|
1920
|
+
* @details Constructor
|
|
1921
|
+
*/
|
|
1922
|
+
XLChartsheet::XLChartsheet(XLXmlData* xmlData) : XLSheetBase(xmlData) {}
|
|
1923
|
+
|
|
1924
|
+
/**
|
|
1925
|
+
* @details Copy constructor
|
|
1926
|
+
*/
|
|
1927
|
+
XLChartsheet::XLChartsheet(const XLChartsheet& other) = default;
|
|
1928
|
+
|
|
1929
|
+
/**
|
|
1930
|
+
* @details Move constructor
|
|
1931
|
+
*/
|
|
1932
|
+
XLChartsheet::XLChartsheet(XLChartsheet&& other) noexcept = default;
|
|
1933
|
+
|
|
1934
|
+
/**
|
|
1935
|
+
* @details Destructor. Default implementation used.
|
|
1936
|
+
*/
|
|
1937
|
+
XLChartsheet::~XLChartsheet() = default;
|
|
1938
|
+
|
|
1939
|
+
/**
|
|
1940
|
+
* @details Copy assignment operator
|
|
1941
|
+
*/
|
|
1942
|
+
XLChartsheet& XLChartsheet::operator=(const XLChartsheet& other) = default;
|
|
1943
|
+
|
|
1944
|
+
/**
|
|
1945
|
+
* @details Move assignment operator
|
|
1946
|
+
*/
|
|
1947
|
+
XLChartsheet& XLChartsheet::operator=(XLChartsheet&& other) noexcept = default;
|
|
1948
|
+
|
|
1949
|
+
/**
|
|
1950
|
+
* @details
|
|
1951
|
+
*/
|
|
1952
|
+
XLColor XLChartsheet::getColor_impl() const
|
|
1953
|
+
{
|
|
1954
|
+
return XLColor(xmlDocument().document_element().child("sheetPr").child("tabColor").attribute("rgb").value());
|
|
1955
|
+
}
|
|
1956
|
+
|
|
1957
|
+
/**
|
|
1958
|
+
* @details Calls the setTabColor() free function.
|
|
1959
|
+
*/
|
|
1960
|
+
void XLChartsheet::setColor_impl(const XLColor& color) { setTabColor(xmlDocument(), color); }
|
|
1961
|
+
|
|
1962
|
+
/**
|
|
1963
|
+
* @details Calls the tabIsSelected() free function.
|
|
1964
|
+
*/
|
|
1965
|
+
bool XLChartsheet::isSelected_impl() const { return tabIsSelected(xmlDocument()); }
|
|
1966
|
+
|
|
1967
|
+
/**
|
|
1968
|
+
* @details Calls the setTabSelected() free function.
|
|
1969
|
+
*/
|
|
1970
|
+
void XLChartsheet::setSelected_impl(bool selected) { setTabSelected(xmlDocument(), selected); }
|