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.
Files changed (428) hide show
  1. package/NitroXlsx.podspec +27 -27
  2. package/Package.swift +35 -10
  3. package/README.md +220 -58
  4. package/android/CMakeLists.txt +24 -27
  5. package/android/cpp-adapter.cpp +12 -0
  6. package/android/src/main/java/com/margelo/nitro/xlsx/NitroXlsxPackage.kt +9 -0
  7. package/cpp/HybridNitroXlsx.cpp +25 -179
  8. package/cpp/HybridNitroXlsx.hpp +2 -2
  9. package/cpp/HybridXlsxConditionalFormat.cpp +252 -0
  10. package/cpp/HybridXlsxConditionalFormat.hpp +62 -0
  11. package/cpp/HybridXlsxConditionalFormats.cpp +46 -0
  12. package/cpp/HybridXlsxConditionalFormats.hpp +28 -0
  13. package/cpp/HybridXlsxWorkbook.cpp +159 -196
  14. package/cpp/HybridXlsxWorkbook.hpp +7 -2
  15. package/cpp/HybridXlsxWorksheet.cpp +433 -62
  16. package/cpp/HybridXlsxWorksheet.hpp +73 -26
  17. package/cpp/XlsxCacheDir.cpp +76 -0
  18. package/cpp/XlsxCacheDir.hpp +27 -0
  19. package/cpp/XlsxError.hpp +68 -0
  20. package/cpp/XlsxJson.cpp +185 -0
  21. package/cpp/XlsxJson.hpp +36 -0
  22. package/ios/XlsxCacheDir.mm +60 -0
  23. package/nitrogen/generated/android/NitroXlsx+autolinking.cmake +2 -0
  24. package/nitrogen/generated/shared/c++/HybridNitroXlsxSpec.hpp +5 -3
  25. package/nitrogen/generated/shared/c++/HybridXlsxConditionalFormatSpec.cpp +54 -0
  26. package/nitrogen/generated/shared/c++/HybridXlsxConditionalFormatSpec.hpp +95 -0
  27. package/nitrogen/generated/shared/c++/HybridXlsxConditionalFormatsSpec.cpp +24 -0
  28. package/nitrogen/generated/shared/c++/HybridXlsxConditionalFormatsSpec.hpp +68 -0
  29. package/nitrogen/generated/shared/c++/HybridXlsxWorkbookSpec.cpp +3 -0
  30. package/nitrogen/generated/shared/c++/HybridXlsxWorkbookSpec.hpp +6 -1
  31. package/nitrogen/generated/shared/c++/HybridXlsxWorksheetSpec.cpp +47 -19
  32. package/nitrogen/generated/shared/c++/HybridXlsxWorksheetSpec.hpp +52 -20
  33. package/package.json +3 -3
  34. package/scripts/run-cpp-tests.sh +14 -0
  35. package/src/__mocks__/react-native-nitro-modules.ts +1 -0
  36. package/src/__tests__/index.test.ts +76 -7
  37. package/src/index.ts +107 -18
  38. package/src/specs/NitroXlsx.nitro.ts +141 -44
  39. package/thirdparty/OpenXLSX/.clang-format +118 -0
  40. package/thirdparty/OpenXLSX/.clang-tidy +28 -0
  41. package/thirdparty/OpenXLSX/.gitignore +130 -0
  42. package/thirdparty/OpenXLSX/Benchmarks/2024-07-29-benchmarks_before_XMLNode_wrappers +46 -0
  43. package/thirdparty/OpenXLSX/Benchmarks/2024-07-29-benchmarks_with_XMLNode_wrappers_and_namespace_and_stripping +49 -0
  44. package/thirdparty/OpenXLSX/Benchmarks/2024-07-29-benchmarks_with_XMLNode_wrappers_and_namespace_char_support +49 -0
  45. package/thirdparty/OpenXLSX/Benchmarks/2024-07-29-benchmarks_with_XMLNode_wrappers_and_namespace_support +49 -0
  46. package/thirdparty/OpenXLSX/Benchmarks/2024-07-29-benchmarks_with_XMLNode_wrappers_and_namespaced_name_disabled +49 -0
  47. package/thirdparty/OpenXLSX/Benchmarks/2024-08-11-benchmarks_post-XLCellIterator-Rework +46 -0
  48. package/thirdparty/OpenXLSX/Benchmarks/2024-08-14-benchmarks_post-default-row-column-style-support +46 -0
  49. package/thirdparty/OpenXLSX/Benchmarks/2024-09-02-benchmarks +46 -0
  50. package/thirdparty/OpenXLSX/Benchmarks/2024-12-19-benchmarks +46 -0
  51. package/thirdparty/OpenXLSX/Benchmarks/2024-12-30-benchmarks-post-XLSharedStrings-by-ref +46 -0
  52. package/thirdparty/OpenXLSX/Benchmarks/2025-01-09-benchmarks-post-conditionalFormatsAndSheetProtection +46 -0
  53. package/thirdparty/OpenXLSX/Benchmarks/2025-01-10-benchmarks-post-XLCellValue-bugfix +46 -0
  54. package/thirdparty/OpenXLSX/Benchmarks/2025-01-31-benchmarks-post-XLComments-implementation +46 -0
  55. package/thirdparty/OpenXLSX/Benchmarks/2025-02-02-benchmarks-post-XLRowIterator-Rework +46 -0
  56. package/thirdparty/OpenXLSX/Benchmarks/2025-07-20-benchmarks-after-hiding-pugixml-internals +46 -0
  57. package/thirdparty/OpenXLSX/Benchmarks/2025-08-29-benchmarks-after-code-review-with-libzip +46 -0
  58. package/thirdparty/OpenXLSX/Benchmarks/Benchmark.cpp +249 -0
  59. package/thirdparty/OpenXLSX/Benchmarks/CMakeLists.txt +19 -0
  60. package/thirdparty/OpenXLSX/CMakeLists.txt +466 -0
  61. package/thirdparty/OpenXLSX/Documentation/CMakeLists.txt +31 -0
  62. package/thirdparty/OpenXLSX/Documentation/Doxyfile.in +2471 -0
  63. package/thirdparty/OpenXLSX/Documentation/DoxygenLayout.xml +194 -0
  64. package/thirdparty/OpenXLSX/Documentation/customdoxygen.css +1355 -0
  65. package/thirdparty/OpenXLSX/Examples/CMakeLists.txt +103 -0
  66. package/thirdparty/OpenXLSX/Examples/Demo1.cpp +279 -0
  67. package/thirdparty/OpenXLSX/Examples/Demo10.cpp +545 -0
  68. package/thirdparty/OpenXLSX/Examples/Demo1A.cpp +137 -0
  69. package/thirdparty/OpenXLSX/Examples/Demo2.cpp +65 -0
  70. package/thirdparty/OpenXLSX/Examples/Demo3.cpp +89 -0
  71. package/thirdparty/OpenXLSX/Examples/Demo4.cpp +86 -0
  72. package/thirdparty/OpenXLSX/Examples/Demo5.cpp +77 -0
  73. package/thirdparty/OpenXLSX/Examples/Demo6.cpp +103 -0
  74. package/thirdparty/OpenXLSX/Examples/Demo7.cpp +79 -0
  75. package/thirdparty/OpenXLSX/Examples/Demo8.cpp +117 -0
  76. package/thirdparty/OpenXLSX/Examples/Demo9.cpp +363 -0
  77. package/thirdparty/OpenXLSX/Examples/cmake/FindLIBZIP.cmake +61 -0
  78. package/thirdparty/OpenXLSX/Examples/external/CustomZip/CustomZip.hpp +256 -0
  79. package/thirdparty/OpenXLSX/LICENSE.md +29 -0
  80. package/thirdparty/OpenXLSX/Makefile.GNU +530 -0
  81. package/thirdparty/OpenXLSX/Notes/TBDs.txt +16 -0
  82. package/thirdparty/OpenXLSX/Notes/confirmed-TBCs.txt +8 -0
  83. package/thirdparty/OpenXLSX/Notes/styles-missing-support.xml +12 -0
  84. package/thirdparty/OpenXLSX/Notes/todo-list.txt +108 -0
  85. package/thirdparty/OpenXLSX/OpenXLSX/CMakeLists.txt +631 -0
  86. package/thirdparty/OpenXLSX/OpenXLSX/OpenXLSX.hpp +63 -0
  87. package/thirdparty/OpenXLSX/OpenXLSX/OpenXLSXConfig.cmake +11 -0
  88. package/thirdparty/OpenXLSX/OpenXLSX/cmake/bundle_static_library.cmake +198 -0
  89. package/thirdparty/OpenXLSX/OpenXLSX/cmake/patch_pkg_config.cmake +69 -0
  90. package/thirdparty/OpenXLSX/OpenXLSX/headers/IZipArchive.hpp +361 -0
  91. package/thirdparty/OpenXLSX/OpenXLSX/headers/OpenXLSX-Exports.hpp +12 -0
  92. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLCell.hpp +367 -0
  93. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLCellIterator.hpp +252 -0
  94. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLCellRange.hpp +223 -0
  95. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLCellReference.hpp +317 -0
  96. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLCellValue.hpp +793 -0
  97. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLColor.hpp +232 -0
  98. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLColumn.hpp +159 -0
  99. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLCommandQuery.hpp +228 -0
  100. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLComments.hpp +270 -0
  101. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLConstants.hpp +18 -0
  102. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLContentTypes.hpp +264 -0
  103. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLDateTime.hpp +175 -0
  104. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLDocument.hpp +505 -0
  105. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLDrawing.hpp +452 -0
  106. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLException.hpp +161 -0
  107. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLFormula.hpp +375 -0
  108. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLIterator.hpp +40 -0
  109. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLMergeCells.hpp +216 -0
  110. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLProperties.hpp +348 -0
  111. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLRelationships.hpp +348 -0
  112. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLRow.hpp +721 -0
  113. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLRowData.hpp +488 -0
  114. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLSharedStrings.hpp +204 -0
  115. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLSheet.hpp +1640 -0
  116. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLStyles.hpp +2390 -0
  117. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLTables.hpp +132 -0
  118. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLWorkbook.hpp +404 -0
  119. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLXmlData.hpp +283 -0
  120. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLXmlFile.hpp +194 -0
  121. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLXmlParser.hpp +282 -0
  122. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLXmlParserForwardDeclarations.hpp +85 -0
  123. package/thirdparty/OpenXLSX/OpenXLSX/headers/XLZipArchive.hpp +232 -0
  124. package/thirdparty/OpenXLSX/OpenXLSX/headers/detail/LibZip.hpp +447 -0
  125. package/thirdparty/OpenXLSX/OpenXLSX/headers/detail/OpenXLSXFileSystemTools.hpp +207 -0
  126. package/thirdparty/OpenXLSX/OpenXLSX/headers/detail/Zippy.hpp +1375 -0
  127. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLCell.cpp +340 -0
  128. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLCellIterator.cpp +427 -0
  129. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLCellRange.cpp +221 -0
  130. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLCellReference.cpp +408 -0
  131. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLCellValue.cpp +519 -0
  132. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLColor.cpp +202 -0
  133. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLColumn.cpp +141 -0
  134. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLComments.cpp +637 -0
  135. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLContentTypes.cpp +329 -0
  136. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLDateTime.cpp +269 -0
  137. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLDocument.cpp +1678 -0
  138. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLDrawing.cpp +784 -0
  139. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLFormula.cpp +201 -0
  140. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLMergeCells.cpp +357 -0
  141. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLProperties.cpp +810 -0
  142. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLRelationships.cpp +529 -0
  143. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLRow.cpp +918 -0
  144. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLRowData.cpp +508 -0
  145. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLSharedStrings.cpp +220 -0
  146. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLSheet.cpp +1970 -0
  147. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLStyles.cpp +3014 -0
  148. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLTables.cpp +125 -0
  149. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLWorkbook.cpp +784 -0
  150. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLXmlData.cpp +247 -0
  151. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLXmlFile.cpp +160 -0
  152. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLXmlParser.cpp +261 -0
  153. package/thirdparty/OpenXLSX/OpenXLSX/sources/XLZipArchive.cpp +266 -0
  154. package/thirdparty/OpenXLSX/OpenXLSX/sources/utilities/XLUtilities.hpp +478 -0
  155. package/thirdparty/OpenXLSX/README.md +1279 -0
  156. package/thirdparty/OpenXLSX/Scripts/cmake-cleanup.sh +102 -0
  157. package/thirdparty/OpenXLSX/Scripts/compile.sh +23 -0
  158. package/thirdparty/OpenXLSX/Scripts/demos-cleanup.sh +25 -0
  159. package/thirdparty/OpenXLSX/Scripts/demos-run-all.sh +60 -0
  160. package/thirdparty/OpenXLSX/Scripts/guarded-xml-format.sh +47 -0
  161. package/thirdparty/OpenXLSX/Scripts/make-gnu.sh +11 -0
  162. package/thirdparty/OpenXLSX/Scripts/uninstall-manifest.sh +48 -0
  163. package/thirdparty/OpenXLSX/Scripts/xml-format-all.sh +19 -0
  164. package/thirdparty/OpenXLSX/Scripts/xml-format.sh +38 -0
  165. package/thirdparty/OpenXLSX/Tests/CMakeLists.txt +37 -0
  166. package/thirdparty/OpenXLSX/Tests/catch/catch.hpp +17799 -0
  167. package/thirdparty/OpenXLSX/Tests/catch/catch_reporter_automake.hpp +68 -0
  168. package/thirdparty/OpenXLSX/Tests/catch/catch_reporter_sonarqube.hpp +182 -0
  169. package/thirdparty/OpenXLSX/Tests/catch/catch_reporter_tap.hpp +278 -0
  170. package/thirdparty/OpenXLSX/Tests/catch/catch_reporter_teamcity.hpp +212 -0
  171. package/thirdparty/OpenXLSX/Tests/main.cpp +55 -0
  172. package/thirdparty/OpenXLSX/Tests/testXLCell.cpp +176 -0
  173. package/thirdparty/OpenXLSX/Tests/testXLCellRange.cpp +180 -0
  174. package/thirdparty/OpenXLSX/Tests/testXLCellReference.cpp +160 -0
  175. package/thirdparty/OpenXLSX/Tests/testXLCellValue.cpp +366 -0
  176. package/thirdparty/OpenXLSX/Tests/testXLCellValueProxy.cpp +255 -0
  177. package/thirdparty/OpenXLSX/Tests/testXLColor.cpp +75 -0
  178. package/thirdparty/OpenXLSX/Tests/testXLDateTime.cpp +229 -0
  179. package/thirdparty/OpenXLSX/Tests/testXLDocument.cpp +197 -0
  180. package/thirdparty/OpenXLSX/Tests/testXLFormula.cpp +131 -0
  181. package/thirdparty/OpenXLSX/Tests/testXLRow.cpp +597 -0
  182. package/thirdparty/OpenXLSX/Tests/testXLSheet.cpp +77 -0
  183. package/thirdparty/OpenXLSX/Tests/ziptest-with-libs.cpp +88 -0
  184. package/thirdparty/OpenXLSX/Tests/ziptest.cpp +735 -0
  185. package/thirdparty/OpenXLSX/cmake/get-git-HEAD-ID.cmake +45 -0
  186. package/thirdparty/OpenXLSX/cmake/libopenxlsxCPackOptions.cmake.in +20 -0
  187. package/thirdparty/OpenXLSX/cmake/manage_dependency.cmake +813 -0
  188. package/thirdparty/OpenXLSX/cmake/recursive_library_type_test.cmake +52 -0
  189. package/thirdparty/OpenXLSX/gnu-make-crutch/OpenXLSX-Exports-shared.hpp +42 -0
  190. package/thirdparty/OpenXLSX/gnu-make-crutch/OpenXLSX-Exports-static.hpp +42 -0
  191. package/thirdparty/OpenXLSX/vcpkg.json +24 -0
  192. package/thirdparty/README.md +42 -0
  193. package/thirdparty/miniz/.clang-format +47 -0
  194. package/thirdparty/miniz/.travis.yml +2 -0
  195. package/thirdparty/miniz/CMakeLists.txt +333 -0
  196. package/thirdparty/miniz/CMakePresets.json +31 -0
  197. package/thirdparty/miniz/ChangeLog.md +272 -0
  198. package/thirdparty/miniz/Config.cmake.in +1 -0
  199. package/thirdparty/miniz/LICENSE +22 -0
  200. package/thirdparty/miniz/amalgamate.sh +57 -0
  201. package/thirdparty/miniz/examples/example1.c +105 -0
  202. package/thirdparty/miniz/examples/example2.c +164 -0
  203. package/thirdparty/miniz/examples/example3.c +269 -0
  204. package/thirdparty/miniz/examples/example4.c +102 -0
  205. package/thirdparty/miniz/examples/example5.c +327 -0
  206. package/thirdparty/miniz/examples/example6.c +189 -0
  207. package/thirdparty/miniz/meson.build +21 -0
  208. package/thirdparty/miniz/miniz.c +646 -0
  209. package/thirdparty/miniz/miniz.h +615 -0
  210. package/thirdparty/miniz/miniz.pc.in +13 -0
  211. package/thirdparty/miniz/miniz_common.h +89 -0
  212. package/thirdparty/miniz/miniz_export.h +20 -0
  213. package/thirdparty/miniz/miniz_tdef.c +1602 -0
  214. package/thirdparty/miniz/miniz_tdef.h +199 -0
  215. package/thirdparty/miniz/miniz_tinfl.c +778 -0
  216. package/thirdparty/miniz/miniz_tinfl.h +150 -0
  217. package/thirdparty/miniz/miniz_zip.c +4895 -0
  218. package/thirdparty/miniz/miniz_zip.h +454 -0
  219. package/thirdparty/miniz/readme.md +46 -0
  220. package/thirdparty/miniz/test.sh +36 -0
  221. package/thirdparty/miniz/tests/add_in_place_fuzzer.c +87 -0
  222. package/thirdparty/miniz/tests/catch_amalgamated.cpp +10795 -0
  223. package/thirdparty/miniz/tests/catch_amalgamated.hpp +13143 -0
  224. package/thirdparty/miniz/tests/checksum_fuzzer.c +26 -0
  225. package/thirdparty/miniz/tests/compress_fuzzer.c +91 -0
  226. package/thirdparty/miniz/tests/flush_fuzzer.c +88 -0
  227. package/thirdparty/miniz/tests/fuzz_main.c +55 -0
  228. package/thirdparty/miniz/tests/large_fuzzer.c +133 -0
  229. package/thirdparty/miniz/tests/main.cpp +185 -0
  230. package/thirdparty/miniz/tests/miniz_tdefl_fuzzer.cc +115 -0
  231. package/thirdparty/miniz/tests/miniz_tester.cpp +1881 -0
  232. package/thirdparty/miniz/tests/miniz_zip_validate_fuzzer.cc +71 -0
  233. package/thirdparty/miniz/tests/miniz_zip_writer_fuzzer.cc +96 -0
  234. package/thirdparty/miniz/tests/ossfuzz.sh +48 -0
  235. package/thirdparty/miniz/tests/small_fuzzer.c +126 -0
  236. package/thirdparty/miniz/tests/timer.cpp +152 -0
  237. package/thirdparty/miniz/tests/timer.h +40 -0
  238. package/thirdparty/miniz/tests/uncompress2_fuzzer.c +21 -0
  239. package/thirdparty/miniz/tests/uncompress_fuzzer.c +30 -0
  240. package/thirdparty/miniz/tests/zip.dict +9 -0
  241. package/thirdparty/miniz/tests/zip_fuzzer.c +63 -0
  242. package/thirdparty/pugixml/.codecov.yml +7 -0
  243. package/thirdparty/pugixml/.gitattributes +1 -0
  244. package/thirdparty/pugixml/CMakeLists.txt +330 -0
  245. package/thirdparty/pugixml/LICENSE.md +24 -0
  246. package/thirdparty/pugixml/Makefile +98 -0
  247. package/thirdparty/pugixml/README.md +81 -0
  248. package/thirdparty/pugixml/SECURITY.md +9 -0
  249. package/thirdparty/pugixml/appveyor.yml +50 -0
  250. package/thirdparty/pugixml/docs/config.adoc +7 -0
  251. package/thirdparty/pugixml/docs/images/dom_tree.png +0 -0
  252. package/thirdparty/pugixml/docs/images/vs2005_link1.png +0 -0
  253. package/thirdparty/pugixml/docs/images/vs2005_link2.png +0 -0
  254. package/thirdparty/pugixml/docs/images/vs2005_pch1.png +0 -0
  255. package/thirdparty/pugixml/docs/images/vs2005_pch2.png +0 -0
  256. package/thirdparty/pugixml/docs/images/vs2005_pch3.png +0 -0
  257. package/thirdparty/pugixml/docs/images/vs2005_pch4.png +0 -0
  258. package/thirdparty/pugixml/docs/images/vs2010_link1.png +0 -0
  259. package/thirdparty/pugixml/docs/images/vs2010_link2.png +0 -0
  260. package/thirdparty/pugixml/docs/manual.adoc +3326 -0
  261. package/thirdparty/pugixml/docs/manual.html +6395 -0
  262. package/thirdparty/pugixml/docs/quickstart.adoc +287 -0
  263. package/thirdparty/pugixml/docs/quickstart.html +1107 -0
  264. package/thirdparty/pugixml/docs/samples/character.xml +8 -0
  265. package/thirdparty/pugixml/docs/samples/custom_memory_management.cpp +27 -0
  266. package/thirdparty/pugixml/docs/samples/include.cpp +64 -0
  267. package/thirdparty/pugixml/docs/samples/load_error_handling.cpp +33 -0
  268. package/thirdparty/pugixml/docs/samples/load_file.cpp +16 -0
  269. package/thirdparty/pugixml/docs/samples/load_memory.cpp +66 -0
  270. package/thirdparty/pugixml/docs/samples/load_options.cpp +30 -0
  271. package/thirdparty/pugixml/docs/samples/load_stream.cpp +97 -0
  272. package/thirdparty/pugixml/docs/samples/modify_add.cpp +29 -0
  273. package/thirdparty/pugixml/docs/samples/modify_base.cpp +43 -0
  274. package/thirdparty/pugixml/docs/samples/modify_remove.cpp +27 -0
  275. package/thirdparty/pugixml/docs/samples/save_custom_writer.cpp +116 -0
  276. package/thirdparty/pugixml/docs/samples/save_declaration.cpp +27 -0
  277. package/thirdparty/pugixml/docs/samples/save_file.cpp +17 -0
  278. package/thirdparty/pugixml/docs/samples/save_options.cpp +48 -0
  279. package/thirdparty/pugixml/docs/samples/save_stream.cpp +18 -0
  280. package/thirdparty/pugixml/docs/samples/save_subtree.cpp +26 -0
  281. package/thirdparty/pugixml/docs/samples/text.cpp +35 -0
  282. package/thirdparty/pugixml/docs/samples/transitions.xml +7 -0
  283. package/thirdparty/pugixml/docs/samples/traverse_base.cpp +51 -0
  284. package/thirdparty/pugixml/docs/samples/traverse_iter.cpp +27 -0
  285. package/thirdparty/pugixml/docs/samples/traverse_predicate.cpp +48 -0
  286. package/thirdparty/pugixml/docs/samples/traverse_rangefor.cpp +32 -0
  287. package/thirdparty/pugixml/docs/samples/traverse_walker.cpp +35 -0
  288. package/thirdparty/pugixml/docs/samples/tree.xml +12 -0
  289. package/thirdparty/pugixml/docs/samples/weekly-shift_jis.xml +78 -0
  290. package/thirdparty/pugixml/docs/samples/weekly-utf-16.xml +0 -0
  291. package/thirdparty/pugixml/docs/samples/weekly-utf-8.xml +78 -0
  292. package/thirdparty/pugixml/docs/samples/xgconsole.xml +12 -0
  293. package/thirdparty/pugixml/docs/samples/xpath_error.cpp +43 -0
  294. package/thirdparty/pugixml/docs/samples/xpath_query.cpp +36 -0
  295. package/thirdparty/pugixml/docs/samples/xpath_select.cpp +28 -0
  296. package/thirdparty/pugixml/docs/samples/xpath_variables.cpp +38 -0
  297. package/thirdparty/pugixml/readme.txt +50 -0
  298. package/thirdparty/pugixml/scripts/archive.py +54 -0
  299. package/thirdparty/pugixml/scripts/cocoapods_push.sh +4 -0
  300. package/thirdparty/pugixml/scripts/natvis/pugixml.natvis +77 -0
  301. package/thirdparty/pugixml/scripts/natvis/pugixml_compact.natvis +506 -0
  302. package/thirdparty/pugixml/scripts/nuget/build/native/pugixml-propertiesui.xml +17 -0
  303. package/thirdparty/pugixml/scripts/nuget/build/native/pugixml.targets +28 -0
  304. package/thirdparty/pugixml/scripts/nuget/pugixml.nuspec +20 -0
  305. package/thirdparty/pugixml/scripts/nuget_build.ps1 +60 -0
  306. package/thirdparty/pugixml/scripts/premake4.lua +92 -0
  307. package/thirdparty/pugixml/scripts/pugixml-config.cmake.in +12 -0
  308. package/thirdparty/pugixml/scripts/pugixml.pc.in +11 -0
  309. package/thirdparty/pugixml/scripts/pugixml.podspec +14 -0
  310. package/thirdparty/pugixml/scripts/pugixml.xcodeproj/project.pbxproj +212 -0
  311. package/thirdparty/pugixml/scripts/pugixml_airplay.mkf +13 -0
  312. package/thirdparty/pugixml/scripts/pugixml_codeblocks.cbp +44 -0
  313. package/thirdparty/pugixml/scripts/pugixml_codelite.project +56 -0
  314. package/thirdparty/pugixml/scripts/pugixml_dll.rc +45 -0
  315. package/thirdparty/pugixml/scripts/pugixml_vs2005.vcproj +343 -0
  316. package/thirdparty/pugixml/scripts/pugixml_vs2005_static.vcproj +343 -0
  317. package/thirdparty/pugixml/scripts/pugixml_vs2008.vcproj +339 -0
  318. package/thirdparty/pugixml/scripts/pugixml_vs2008_static.vcproj +339 -0
  319. package/thirdparty/pugixml/scripts/pugixml_vs2010.vcxproj +191 -0
  320. package/thirdparty/pugixml/scripts/pugixml_vs2010_static.vcxproj +191 -0
  321. package/thirdparty/pugixml/scripts/pugixml_vs2013.vcxproj +199 -0
  322. package/thirdparty/pugixml/scripts/pugixml_vs2013_static.vcxproj +199 -0
  323. package/thirdparty/pugixml/scripts/pugixml_vs2015.vcxproj +172 -0
  324. package/thirdparty/pugixml/scripts/pugixml_vs2015_static.vcxproj +176 -0
  325. package/thirdparty/pugixml/scripts/pugixml_vs2017.vcxproj +172 -0
  326. package/thirdparty/pugixml/scripts/pugixml_vs2017_static.vcxproj +176 -0
  327. package/thirdparty/pugixml/scripts/pugixml_vs2019.vcxproj +176 -0
  328. package/thirdparty/pugixml/scripts/pugixml_vs2019_static.vcxproj +180 -0
  329. package/thirdparty/pugixml/scripts/pugixml_vs2022.vcxproj +176 -0
  330. package/thirdparty/pugixml/scripts/pugixml_vs2022_static.vcxproj +180 -0
  331. package/thirdparty/pugixml/scripts/pugixml_vs2026.vcxproj +176 -0
  332. package/thirdparty/pugixml/scripts/pugixml_vs2026_static.vcxproj +180 -0
  333. package/thirdparty/pugixml/scripts/sbom.cdx.json +47 -0
  334. package/thirdparty/pugixml/src/pugiconfig.hpp +81 -0
  335. package/thirdparty/pugixml/src/pugixml.cpp +13776 -0
  336. package/thirdparty/pugixml/src/pugixml.hpp +1604 -0
  337. package/thirdparty/pugixml/tests/allocator.cpp +175 -0
  338. package/thirdparty/pugixml/tests/allocator.hpp +10 -0
  339. package/thirdparty/pugixml/tests/autotest-appveyor.ps1 +93 -0
  340. package/thirdparty/pugixml/tests/cmake-appveyor.ps1 +57 -0
  341. package/thirdparty/pugixml/tests/data/empty.xml +0 -0
  342. package/thirdparty/pugixml/tests/data/large.xml +1 -0
  343. package/thirdparty/pugixml/tests/data/latintest_latin1.xml +1 -0
  344. package/thirdparty/pugixml/tests/data/latintest_utf8.xml +1 -0
  345. package/thirdparty/pugixml/tests/data/multiline.xml +3 -0
  346. package/thirdparty/pugixml/tests/data/small.xml +1 -0
  347. package/thirdparty/pugixml/tests/data/truncation.xml +19 -0
  348. package/thirdparty/pugixml/tests/data/utftest_utf16_be.xml +0 -0
  349. package/thirdparty/pugixml/tests/data/utftest_utf16_be_bom.xml +0 -0
  350. package/thirdparty/pugixml/tests/data/utftest_utf16_be_clean.xml +0 -0
  351. package/thirdparty/pugixml/tests/data/utftest_utf16_be_nodecl.xml +0 -0
  352. package/thirdparty/pugixml/tests/data/utftest_utf16_le.xml +0 -0
  353. package/thirdparty/pugixml/tests/data/utftest_utf16_le_bom.xml +0 -0
  354. package/thirdparty/pugixml/tests/data/utftest_utf16_le_clean.xml +0 -0
  355. package/thirdparty/pugixml/tests/data/utftest_utf16_le_nodecl.xml +0 -0
  356. package/thirdparty/pugixml/tests/data/utftest_utf32_be.xml +0 -0
  357. package/thirdparty/pugixml/tests/data/utftest_utf32_be_bom.xml +0 -0
  358. package/thirdparty/pugixml/tests/data/utftest_utf32_be_clean.xml +0 -0
  359. package/thirdparty/pugixml/tests/data/utftest_utf32_be_nodecl.xml +0 -0
  360. package/thirdparty/pugixml/tests/data/utftest_utf32_le.xml +0 -0
  361. package/thirdparty/pugixml/tests/data/utftest_utf32_le_bom.xml +0 -0
  362. package/thirdparty/pugixml/tests/data/utftest_utf32_le_clean.xml +0 -0
  363. package/thirdparty/pugixml/tests/data/utftest_utf32_le_nodecl.xml +0 -0
  364. package/thirdparty/pugixml/tests/data/utftest_utf8.xml +87 -0
  365. package/thirdparty/pugixml/tests/data/utftest_utf8_bom.xml +87 -0
  366. package/thirdparty/pugixml/tests/data/utftest_utf8_clean.xml +84 -0
  367. package/thirdparty/pugixml/tests/data/utftest_utf8_nodecl.xml +86 -0
  368. package/thirdparty/pugixml/tests/data//321/202/320/265/321/201/321/202.xml +1 -0
  369. package/thirdparty/pugixml/tests/data_fuzz_parse/basic.xml +1 -0
  370. package/thirdparty/pugixml/tests/data_fuzz_parse/doctype.xml +1 -0
  371. package/thirdparty/pugixml/tests/data_fuzz_parse/refs.xml +1 -0
  372. package/thirdparty/pugixml/tests/data_fuzz_parse/types.xml +1 -0
  373. package/thirdparty/pugixml/tests/data_fuzz_parse/utf16.xml +0 -0
  374. package/thirdparty/pugixml/tests/data_fuzz_parse/utf32.xml +0 -0
  375. package/thirdparty/pugixml/tests/data_fuzz_xpath/basic.xpath +1 -0
  376. package/thirdparty/pugixml/tests/data_fuzz_xpath/functions.xpath +1 -0
  377. package/thirdparty/pugixml/tests/data_fuzz_xpath/math.xpath +1 -0
  378. package/thirdparty/pugixml/tests/data_fuzz_xpath/path.xpath +1 -0
  379. package/thirdparty/pugixml/tests/data_fuzz_xpath/predicate.xpath +1 -0
  380. package/thirdparty/pugixml/tests/fuzz_parse.cpp +14 -0
  381. package/thirdparty/pugixml/tests/fuzz_parse.dict +72 -0
  382. package/thirdparty/pugixml/tests/fuzz_setup.sh +11 -0
  383. package/thirdparty/pugixml/tests/fuzz_xpath.cpp +46 -0
  384. package/thirdparty/pugixml/tests/fuzz_xpath.dict +72 -0
  385. package/thirdparty/pugixml/tests/helpers.hpp +114 -0
  386. package/thirdparty/pugixml/tests/main.cpp +218 -0
  387. package/thirdparty/pugixml/tests/test.cpp +220 -0
  388. package/thirdparty/pugixml/tests/test.hpp +175 -0
  389. package/thirdparty/pugixml/tests/test_compact.cpp +150 -0
  390. package/thirdparty/pugixml/tests/test_deprecated.cpp +25 -0
  391. package/thirdparty/pugixml/tests/test_document.cpp +1910 -0
  392. package/thirdparty/pugixml/tests/test_dom_modify.cpp +2122 -0
  393. package/thirdparty/pugixml/tests/test_dom_text.cpp +601 -0
  394. package/thirdparty/pugixml/tests/test_dom_traverse.cpp +1481 -0
  395. package/thirdparty/pugixml/tests/test_header_guard.cpp +3 -0
  396. package/thirdparty/pugixml/tests/test_header_iosfwd_1.cpp +3 -0
  397. package/thirdparty/pugixml/tests/test_header_iosfwd_2.cpp +3 -0
  398. package/thirdparty/pugixml/tests/test_header_iostream_1.cpp +3 -0
  399. package/thirdparty/pugixml/tests/test_header_iostream_2.cpp +3 -0
  400. package/thirdparty/pugixml/tests/test_header_only_1.cpp +21 -0
  401. package/thirdparty/pugixml/tests/test_header_only_2.cpp +21 -0
  402. package/thirdparty/pugixml/tests/test_header_string_1.cpp +3 -0
  403. package/thirdparty/pugixml/tests/test_header_string_2.cpp +3 -0
  404. package/thirdparty/pugixml/tests/test_header_string_iostream.cpp +5 -0
  405. package/thirdparty/pugixml/tests/test_memory.cpp +303 -0
  406. package/thirdparty/pugixml/tests/test_parse.cpp +1463 -0
  407. package/thirdparty/pugixml/tests/test_parse_doctype.cpp +366 -0
  408. package/thirdparty/pugixml/tests/test_unicode.cpp +152 -0
  409. package/thirdparty/pugixml/tests/test_version.cpp +5 -0
  410. package/thirdparty/pugixml/tests/test_write.cpp +757 -0
  411. package/thirdparty/pugixml/tests/test_xpath.cpp +833 -0
  412. package/thirdparty/pugixml/tests/test_xpath_api.cpp +646 -0
  413. package/thirdparty/pugixml/tests/test_xpath_functions.cpp +844 -0
  414. package/thirdparty/pugixml/tests/test_xpath_operators.cpp +566 -0
  415. package/thirdparty/pugixml/tests/test_xpath_parse.cpp +428 -0
  416. package/thirdparty/pugixml/tests/test_xpath_paths.cpp +787 -0
  417. package/thirdparty/pugixml/tests/test_xpath_paths_abbrev_w3c.cpp +219 -0
  418. package/thirdparty/pugixml/tests/test_xpath_paths_w3c.cpp +312 -0
  419. package/thirdparty/pugixml/tests/test_xpath_variables.cpp +683 -0
  420. package/thirdparty/pugixml/tests/test_xpath_xalan_1.cpp +411 -0
  421. package/thirdparty/pugixml/tests/test_xpath_xalan_2.cpp +417 -0
  422. package/thirdparty/pugixml/tests/test_xpath_xalan_3.cpp +321 -0
  423. package/thirdparty/pugixml/tests/test_xpath_xalan_4.cpp +300 -0
  424. package/thirdparty/pugixml/tests/test_xpath_xalan_5.cpp +295 -0
  425. package/thirdparty/pugixml/tests/writer_string.cpp +79 -0
  426. package/thirdparty/pugixml/tests/writer_string.hpp +27 -0
  427. package/ios/dummy.swift +0 -2
  428. 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); }