@rowsncolumns/toolkit 24.4.1

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 (456) hide show
  1. package/LICENSE.md +100 -0
  2. package/Readme.md +27 -0
  3. package/dist/cjs/autofilter-condition.d.ts +43 -0
  4. package/dist/cjs/autofilter-condition.d.ts.map +1 -0
  5. package/dist/cjs/autofilter-condition.js +155 -0
  6. package/dist/cjs/csv-export.d.ts +49 -0
  7. package/dist/cjs/csv-export.d.ts.map +1 -0
  8. package/dist/cjs/csv-export.js +142 -0
  9. package/dist/cjs/csv-import-worker.d.ts +16 -0
  10. package/dist/cjs/csv-import-worker.d.ts.map +1 -0
  11. package/dist/cjs/csv-import-worker.js +60 -0
  12. package/dist/cjs/csv-import.d.ts +38 -0
  13. package/dist/cjs/csv-import.d.ts.map +1 -0
  14. package/dist/cjs/csv-import.js +166 -0
  15. package/dist/cjs/csv-parser/helpers.d.ts +14 -0
  16. package/dist/cjs/csv-parser/helpers.d.ts.map +1 -0
  17. package/dist/cjs/csv-parser/helpers.js +145 -0
  18. package/dist/cjs/csv-parser/types.d.ts +66 -0
  19. package/dist/cjs/csv-parser/types.d.ts.map +1 -0
  20. package/dist/cjs/csv-parser/types.js +14 -0
  21. package/dist/cjs/csv-parser/worker.d.ts +2 -0
  22. package/dist/cjs/csv-parser/worker.d.ts.map +1 -0
  23. package/dist/cjs/csv-parser/worker.js +200 -0
  24. package/dist/cjs/excel-export.d.ts +11 -0
  25. package/dist/cjs/excel-export.d.ts.map +1 -0
  26. package/dist/cjs/excel-export.js +22 -0
  27. package/dist/cjs/excel-import-session.d.ts +51 -0
  28. package/dist/cjs/excel-import-session.d.ts.map +1 -0
  29. package/dist/cjs/excel-import-session.js +307 -0
  30. package/dist/cjs/excel-import-worker.d.ts +7 -0
  31. package/dist/cjs/excel-import-worker.d.ts.map +1 -0
  32. package/dist/cjs/excel-import-worker.js +119 -0
  33. package/dist/cjs/excel-parser/builtin-numfmts.d.ts +24 -0
  34. package/dist/cjs/excel-parser/builtin-numfmts.d.ts.map +1 -0
  35. package/dist/cjs/excel-parser/builtin-numfmts.js +63 -0
  36. package/dist/cjs/excel-parser/cfb-decryptor.d.ts +53 -0
  37. package/dist/cjs/excel-parser/cfb-decryptor.d.ts.map +1 -0
  38. package/dist/cjs/excel-parser/cfb-decryptor.js +553 -0
  39. package/dist/cjs/excel-parser/cfb-encryptor.d.ts +23 -0
  40. package/dist/cjs/excel-parser/cfb-encryptor.d.ts.map +1 -0
  41. package/dist/cjs/excel-parser/cfb-encryptor.js +200 -0
  42. package/dist/cjs/excel-parser/dv-cell-stamp.d.ts +29 -0
  43. package/dist/cjs/excel-parser/dv-cell-stamp.d.ts.map +1 -0
  44. package/dist/cjs/excel-parser/dv-cell-stamp.js +239 -0
  45. package/dist/cjs/excel-parser/helpers.d.ts +28 -0
  46. package/dist/cjs/excel-parser/helpers.d.ts.map +1 -0
  47. package/dist/cjs/excel-parser/helpers.js +188 -0
  48. package/dist/cjs/excel-parser/interface.d.ts +179 -0
  49. package/dist/cjs/excel-parser/interface.d.ts.map +1 -0
  50. package/dist/cjs/excel-parser/interface.js +1 -0
  51. package/dist/cjs/excel-parser/plugins/base/index.d.ts +31 -0
  52. package/dist/cjs/excel-parser/plugins/base/index.d.ts.map +1 -0
  53. package/dist/cjs/excel-parser/plugins/base/index.js +101 -0
  54. package/dist/cjs/excel-parser/plugins/ods/conditional-formatting.d.ts +16 -0
  55. package/dist/cjs/excel-parser/plugins/ods/conditional-formatting.d.ts.map +1 -0
  56. package/dist/cjs/excel-parser/plugins/ods/conditional-formatting.js +488 -0
  57. package/dist/cjs/excel-parser/plugins/ods/data-validations.d.ts +15 -0
  58. package/dist/cjs/excel-parser/plugins/ods/data-validations.d.ts.map +1 -0
  59. package/dist/cjs/excel-parser/plugins/ods/data-validations.js +353 -0
  60. package/dist/cjs/excel-parser/plugins/ods/formula.d.ts +8 -0
  61. package/dist/cjs/excel-parser/plugins/ods/formula.d.ts.map +1 -0
  62. package/dist/cjs/excel-parser/plugins/ods/formula.js +82 -0
  63. package/dist/cjs/excel-parser/plugins/ods/index.d.ts +122 -0
  64. package/dist/cjs/excel-parser/plugins/ods/index.d.ts.map +1 -0
  65. package/dist/cjs/excel-parser/plugins/ods/index.js +3642 -0
  66. package/dist/cjs/excel-parser/plugins/ods/sparklines.d.ts +25 -0
  67. package/dist/cjs/excel-parser/plugins/ods/sparklines.d.ts.map +1 -0
  68. package/dist/cjs/excel-parser/plugins/ods/sparklines.js +221 -0
  69. package/dist/cjs/excel-parser/plugins/ods/styles.d.ts +60 -0
  70. package/dist/cjs/excel-parser/plugins/ods/styles.d.ts.map +1 -0
  71. package/dist/cjs/excel-parser/plugins/ods/styles.js +808 -0
  72. package/dist/cjs/excel-parser/plugins/ods/types.d.ts +7 -0
  73. package/dist/cjs/excel-parser/plugins/ods/types.d.ts.map +1 -0
  74. package/dist/cjs/excel-parser/plugins/ods/types.js +1 -0
  75. package/dist/cjs/excel-parser/plugins/xls/index.d.ts +5 -0
  76. package/dist/cjs/excel-parser/plugins/xls/index.d.ts.map +1 -0
  77. package/dist/cjs/excel-parser/plugins/xls/index.js +19 -0
  78. package/dist/cjs/excel-parser/plugins/xlsb/biff-record-reader.d.ts +13 -0
  79. package/dist/cjs/excel-parser/plugins/xlsb/biff-record-reader.d.ts.map +1 -0
  80. package/dist/cjs/excel-parser/plugins/xlsb/biff-record-reader.js +49 -0
  81. package/dist/cjs/excel-parser/plugins/xlsb/index.d.ts +117 -0
  82. package/dist/cjs/excel-parser/plugins/xlsb/index.d.ts.map +1 -0
  83. package/dist/cjs/excel-parser/plugins/xlsb/index.js +2972 -0
  84. package/dist/cjs/excel-parser/plugins/xlsb/rgce-parser.d.ts +18 -0
  85. package/dist/cjs/excel-parser/plugins/xlsb/rgce-parser.d.ts.map +1 -0
  86. package/dist/cjs/excel-parser/plugins/xlsb/rgce-parser.js +527 -0
  87. package/dist/cjs/excel-parser/plugins/xlsm/index.d.ts +5 -0
  88. package/dist/cjs/excel-parser/plugins/xlsm/index.d.ts.map +1 -0
  89. package/dist/cjs/excel-parser/plugins/xlsm/index.js +10 -0
  90. package/dist/cjs/excel-parser/plugins/xlsx/charts.d.ts +60 -0
  91. package/dist/cjs/excel-parser/plugins/xlsx/charts.d.ts.map +1 -0
  92. package/dist/cjs/excel-parser/plugins/xlsx/charts.js +2178 -0
  93. package/dist/cjs/excel-parser/plugins/xlsx/conditional-formatting.d.ts +18 -0
  94. package/dist/cjs/excel-parser/plugins/xlsx/conditional-formatting.d.ts.map +1 -0
  95. package/dist/cjs/excel-parser/plugins/xlsx/conditional-formatting.js +620 -0
  96. package/dist/cjs/excel-parser/plugins/xlsx/data-validations.d.ts +4 -0
  97. package/dist/cjs/excel-parser/plugins/xlsx/data-validations.d.ts.map +1 -0
  98. package/dist/cjs/excel-parser/plugins/xlsx/data-validations.js +515 -0
  99. package/dist/cjs/excel-parser/plugins/xlsx/drawing.d.ts +39 -0
  100. package/dist/cjs/excel-parser/plugins/xlsx/drawing.d.ts.map +1 -0
  101. package/dist/cjs/excel-parser/plugins/xlsx/drawing.js +941 -0
  102. package/dist/cjs/excel-parser/plugins/xlsx/index.d.ts +320 -0
  103. package/dist/cjs/excel-parser/plugins/xlsx/index.d.ts.map +1 -0
  104. package/dist/cjs/excel-parser/plugins/xlsx/index.js +6056 -0
  105. package/dist/cjs/excel-parser/plugins/xlsx/numfmt.d.ts +2 -0
  106. package/dist/cjs/excel-parser/plugins/xlsx/numfmt.d.ts.map +1 -0
  107. package/dist/cjs/excel-parser/plugins/xlsx/numfmt.js +3 -0
  108. package/dist/cjs/excel-parser/plugins/xlsx/pivots.d.ts +40 -0
  109. package/dist/cjs/excel-parser/plugins/xlsx/pivots.d.ts.map +1 -0
  110. package/dist/cjs/excel-parser/plugins/xlsx/pivots.js +342 -0
  111. package/dist/cjs/excel-parser/plugins/xlsx/relationships.d.ts +8 -0
  112. package/dist/cjs/excel-parser/plugins/xlsx/relationships.d.ts.map +1 -0
  113. package/dist/cjs/excel-parser/plugins/xlsx/relationships.js +26 -0
  114. package/dist/cjs/excel-parser/plugins/xlsx/sheet-views.d.ts +26 -0
  115. package/dist/cjs/excel-parser/plugins/xlsx/sheet-views.d.ts.map +1 -0
  116. package/dist/cjs/excel-parser/plugins/xlsx/sheet-views.js +59 -0
  117. package/dist/cjs/excel-parser/plugins/xlsx/styles.d.ts +44 -0
  118. package/dist/cjs/excel-parser/plugins/xlsx/styles.d.ts.map +1 -0
  119. package/dist/cjs/excel-parser/plugins/xlsx/styles.js +1377 -0
  120. package/dist/cjs/excel-parser/plugins/xlsx/theme.d.ts +50 -0
  121. package/dist/cjs/excel-parser/plugins/xlsx/theme.d.ts.map +1 -0
  122. package/dist/cjs/excel-parser/plugins/xlsx/theme.js +150 -0
  123. package/dist/cjs/excel-parser/plugins/xlsx/threaded-comments.d.ts +47 -0
  124. package/dist/cjs/excel-parser/plugins/xlsx/threaded-comments.d.ts.map +1 -0
  125. package/dist/cjs/excel-parser/plugins/xlsx/threaded-comments.js +141 -0
  126. package/dist/cjs/excel-parser/plugins/xlsx/value-type-detection.d.ts +44 -0
  127. package/dist/cjs/excel-parser/plugins/xlsx/value-type-detection.d.ts.map +1 -0
  128. package/dist/cjs/excel-parser/plugins/xlsx/value-type-detection.js +407 -0
  129. package/dist/cjs/excel-parser/registry.d.ts +5 -0
  130. package/dist/cjs/excel-parser/registry.d.ts.map +1 -0
  131. package/dist/cjs/excel-parser/registry.js +21 -0
  132. package/dist/cjs/excel-parser/style-registry.d.ts +51 -0
  133. package/dist/cjs/excel-parser/style-registry.d.ts.map +1 -0
  134. package/dist/cjs/excel-parser/style-registry.js +175 -0
  135. package/dist/cjs/excel-parser/theme-default.d.ts +3 -0
  136. package/dist/cjs/excel-parser/theme-default.d.ts.map +1 -0
  137. package/dist/cjs/excel-parser/theme-default.js +19 -0
  138. package/dist/cjs/excel-parser/types.d.ts +365 -0
  139. package/dist/cjs/excel-parser/types.d.ts.map +1 -0
  140. package/dist/cjs/excel-parser/types.js +59 -0
  141. package/dist/cjs/excel-parser/utils.d.ts +6 -0
  142. package/dist/cjs/excel-parser/utils.d.ts.map +1 -0
  143. package/dist/cjs/excel-parser/utils.js +30 -0
  144. package/dist/cjs/excel-parser/workbook.d.ts +51 -0
  145. package/dist/cjs/excel-parser/workbook.d.ts.map +1 -0
  146. package/dist/cjs/excel-parser/workbook.js +317 -0
  147. package/dist/cjs/excel-parser/worker.d.ts +2 -0
  148. package/dist/cjs/excel-parser/worker.d.ts.map +1 -0
  149. package/dist/cjs/excel-parser/worker.js +1149 -0
  150. package/dist/cjs/excel-parser/zip-parser-streaming.d.ts +13 -0
  151. package/dist/cjs/excel-parser/zip-parser-streaming.d.ts.map +1 -0
  152. package/dist/cjs/excel-parser/zip-parser-streaming.js +200 -0
  153. package/dist/cjs/excel-parser/zip-parser.d.ts +11 -0
  154. package/dist/cjs/excel-parser/zip-parser.d.ts.map +1 -0
  155. package/dist/cjs/excel-parser/zip-parser.js +49 -0
  156. package/dist/cjs/exporter/invalid-data-validation.d.ts +19 -0
  157. package/dist/cjs/exporter/invalid-data-validation.d.ts.map +1 -0
  158. package/dist/cjs/exporter/invalid-data-validation.js +39 -0
  159. package/dist/cjs/exporter/ods/ods-export.d.ts +26 -0
  160. package/dist/cjs/exporter/ods/ods-export.d.ts.map +1 -0
  161. package/dist/cjs/exporter/ods/ods-export.js +3714 -0
  162. package/dist/cjs/exporter/ods/sparkline-builder.d.ts +33 -0
  163. package/dist/cjs/exporter/ods/sparkline-builder.d.ts.map +1 -0
  164. package/dist/cjs/exporter/ods/sparkline-builder.js +221 -0
  165. package/dist/cjs/exporter/xlsx/chart-builder.d.ts +3 -0
  166. package/dist/cjs/exporter/xlsx/chart-builder.d.ts.map +1 -0
  167. package/dist/cjs/exporter/xlsx/chart-builder.js +1901 -0
  168. package/dist/cjs/exporter/xlsx/chart-templates.d.ts +1781 -0
  169. package/dist/cjs/exporter/xlsx/chart-templates.d.ts.map +1 -0
  170. package/dist/cjs/exporter/xlsx/chart-templates.js +1798 -0
  171. package/dist/cjs/exporter/xlsx/future-functions.d.ts +10 -0
  172. package/dist/cjs/exporter/xlsx/future-functions.d.ts.map +1 -0
  173. package/dist/cjs/exporter/xlsx/future-functions.js +264 -0
  174. package/dist/cjs/exporter/xlsx/pivot-builder.d.ts +58 -0
  175. package/dist/cjs/exporter/xlsx/pivot-builder.d.ts.map +1 -0
  176. package/dist/cjs/exporter/xlsx/pivot-builder.js +277 -0
  177. package/dist/cjs/exporter/xlsx/power-query-builder.d.ts +26 -0
  178. package/dist/cjs/exporter/xlsx/power-query-builder.d.ts.map +1 -0
  179. package/dist/cjs/exporter/xlsx/power-query-builder.js +306 -0
  180. package/dist/cjs/exporter/xlsx/slicer-builder.d.ts +11 -0
  181. package/dist/cjs/exporter/xlsx/slicer-builder.d.ts.map +1 -0
  182. package/dist/cjs/exporter/xlsx/slicer-builder.js +87 -0
  183. package/dist/cjs/exporter/xlsx/sparkline-builder.d.ts +61 -0
  184. package/dist/cjs/exporter/xlsx/sparkline-builder.d.ts.map +1 -0
  185. package/dist/cjs/exporter/xlsx/sparkline-builder.js +489 -0
  186. package/dist/cjs/exporter/xlsx/style-builder.d.ts +93 -0
  187. package/dist/cjs/exporter/xlsx/style-builder.d.ts.map +1 -0
  188. package/dist/cjs/exporter/xlsx/style-builder.js +813 -0
  189. package/dist/cjs/exporter/xlsx/threaded-comments-builder.d.ts +58 -0
  190. package/dist/cjs/exporter/xlsx/threaded-comments-builder.d.ts.map +1 -0
  191. package/dist/cjs/exporter/xlsx/threaded-comments-builder.js +189 -0
  192. package/dist/cjs/exporter/xlsx/xlsx-export.d.ts +121 -0
  193. package/dist/cjs/exporter/xlsx/xlsx-export.d.ts.map +1 -0
  194. package/dist/cjs/exporter/xlsx/xlsx-export.js +4596 -0
  195. package/dist/cjs/helpers/theme-xml-generator.d.ts +8 -0
  196. package/dist/cjs/helpers/theme-xml-generator.d.ts.map +1 -0
  197. package/dist/cjs/helpers/theme-xml-generator.js +33 -0
  198. package/dist/cjs/helpers.d.ts +34 -0
  199. package/dist/cjs/helpers.d.ts.map +1 -0
  200. package/dist/cjs/helpers.js +189 -0
  201. package/dist/cjs/index.d.ts +16 -0
  202. package/dist/cjs/index.d.ts.map +1 -0
  203. package/dist/cjs/index.js +15 -0
  204. package/dist/cjs/indexed-colors.d.ts +2 -0
  205. package/dist/cjs/indexed-colors.d.ts.map +1 -0
  206. package/dist/cjs/indexed-colors.js +75 -0
  207. package/dist/cjs/ods-export.d.ts +24 -0
  208. package/dist/cjs/ods-export.d.ts.map +1 -0
  209. package/dist/cjs/ods-export.js +23 -0
  210. package/dist/cjs/parse.d.ts +24 -0
  211. package/dist/cjs/parse.d.ts.map +1 -0
  212. package/dist/cjs/parse.js +68 -0
  213. package/dist/cjs/power-query.d.ts +41 -0
  214. package/dist/cjs/power-query.d.ts.map +1 -0
  215. package/dist/cjs/power-query.js +22 -0
  216. package/dist/cjs/server.d.ts +9 -0
  217. package/dist/cjs/server.d.ts.map +1 -0
  218. package/dist/cjs/server.js +8 -0
  219. package/dist/cjs/theme1.d.ts +3 -0
  220. package/dist/cjs/theme1.d.ts.map +1 -0
  221. package/dist/cjs/theme1.js +2 -0
  222. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -0
  223. package/dist/cjs/types.d.ts +14 -0
  224. package/dist/cjs/types.d.ts.map +1 -0
  225. package/dist/cjs/types.js +14 -0
  226. package/dist/cjs/vitest.config.d.ts +3 -0
  227. package/dist/cjs/vitest.config.d.ts.map +1 -0
  228. package/dist/cjs/vitest.config.js +16 -0
  229. package/dist/esm/autofilter-condition.d.ts +43 -0
  230. package/dist/esm/autofilter-condition.d.ts.map +1 -0
  231. package/dist/esm/autofilter-condition.js +145 -0
  232. package/dist/esm/csv-export.d.ts +49 -0
  233. package/dist/esm/csv-export.d.ts.map +1 -0
  234. package/dist/esm/csv-export.js +126 -0
  235. package/dist/esm/csv-import-worker.d.ts +16 -0
  236. package/dist/esm/csv-import-worker.d.ts.map +1 -0
  237. package/dist/esm/csv-import-worker.js +60 -0
  238. package/dist/esm/csv-import.d.ts +38 -0
  239. package/dist/esm/csv-import.d.ts.map +1 -0
  240. package/dist/esm/csv-import.js +132 -0
  241. package/dist/esm/csv-parser/helpers.d.ts +14 -0
  242. package/dist/esm/csv-parser/helpers.d.ts.map +1 -0
  243. package/dist/esm/csv-parser/helpers.js +0 -0
  244. package/dist/esm/csv-parser/types.d.ts +66 -0
  245. package/dist/esm/csv-parser/types.d.ts.map +1 -0
  246. package/dist/esm/csv-parser/types.js +14 -0
  247. package/dist/esm/csv-parser/worker.d.ts +2 -0
  248. package/dist/esm/csv-parser/worker.d.ts.map +1 -0
  249. package/dist/esm/csv-parser/worker.js +154 -0
  250. package/dist/esm/excel-export.d.ts +11 -0
  251. package/dist/esm/excel-export.d.ts.map +1 -0
  252. package/dist/esm/excel-export.js +13 -0
  253. package/dist/esm/excel-import-session.d.ts +51 -0
  254. package/dist/esm/excel-import-session.d.ts.map +1 -0
  255. package/dist/esm/excel-import-session.js +255 -0
  256. package/dist/esm/excel-import-worker.d.ts +7 -0
  257. package/dist/esm/excel-import-worker.d.ts.map +1 -0
  258. package/dist/esm/excel-import-worker.js +116 -0
  259. package/dist/esm/excel-parser/builtin-numfmts.d.ts +24 -0
  260. package/dist/esm/excel-parser/builtin-numfmts.d.ts.map +1 -0
  261. package/dist/esm/excel-parser/builtin-numfmts.js +59 -0
  262. package/dist/esm/excel-parser/cfb-decryptor.d.ts +53 -0
  263. package/dist/esm/excel-parser/cfb-decryptor.d.ts.map +1 -0
  264. package/dist/esm/excel-parser/cfb-decryptor.js +493 -0
  265. package/dist/esm/excel-parser/cfb-encryptor.d.ts +23 -0
  266. package/dist/esm/excel-parser/cfb-encryptor.d.ts.map +1 -0
  267. package/dist/esm/excel-parser/cfb-encryptor.js +181 -0
  268. package/dist/esm/excel-parser/dv-cell-stamp.d.ts +29 -0
  269. package/dist/esm/excel-parser/dv-cell-stamp.d.ts.map +1 -0
  270. package/dist/esm/excel-parser/dv-cell-stamp.js +155 -0
  271. package/dist/esm/excel-parser/helpers.d.ts +28 -0
  272. package/dist/esm/excel-parser/helpers.d.ts.map +1 -0
  273. package/dist/esm/excel-parser/helpers.js +160 -0
  274. package/dist/esm/excel-parser/interface.d.ts +179 -0
  275. package/dist/esm/excel-parser/interface.d.ts.map +1 -0
  276. package/dist/esm/excel-parser/interface.js +1 -0
  277. package/dist/esm/excel-parser/plugins/base/index.d.ts +31 -0
  278. package/dist/esm/excel-parser/plugins/base/index.d.ts.map +1 -0
  279. package/dist/esm/excel-parser/plugins/base/index.js +64 -0
  280. package/dist/esm/excel-parser/plugins/ods/conditional-formatting.d.ts +16 -0
  281. package/dist/esm/excel-parser/plugins/ods/conditional-formatting.d.ts.map +1 -0
  282. package/dist/esm/excel-parser/plugins/ods/conditional-formatting.js +405 -0
  283. package/dist/esm/excel-parser/plugins/ods/data-validations.d.ts +15 -0
  284. package/dist/esm/excel-parser/plugins/ods/data-validations.d.ts.map +1 -0
  285. package/dist/esm/excel-parser/plugins/ods/data-validations.js +331 -0
  286. package/dist/esm/excel-parser/plugins/ods/formula.d.ts +8 -0
  287. package/dist/esm/excel-parser/plugins/ods/formula.d.ts.map +1 -0
  288. package/dist/esm/excel-parser/plugins/ods/formula.js +82 -0
  289. package/dist/esm/excel-parser/plugins/ods/index.d.ts +122 -0
  290. package/dist/esm/excel-parser/plugins/ods/index.d.ts.map +1 -0
  291. package/dist/esm/excel-parser/plugins/ods/index.js +2896 -0
  292. package/dist/esm/excel-parser/plugins/ods/sparklines.d.ts +25 -0
  293. package/dist/esm/excel-parser/plugins/ods/sparklines.d.ts.map +1 -0
  294. package/dist/esm/excel-parser/plugins/ods/sparklines.js +188 -0
  295. package/dist/esm/excel-parser/plugins/ods/styles.d.ts +60 -0
  296. package/dist/esm/excel-parser/plugins/ods/styles.d.ts.map +1 -0
  297. package/dist/esm/excel-parser/plugins/ods/styles.js +732 -0
  298. package/dist/esm/excel-parser/plugins/ods/types.d.ts +7 -0
  299. package/dist/esm/excel-parser/plugins/ods/types.d.ts.map +1 -0
  300. package/dist/esm/excel-parser/plugins/ods/types.js +1 -0
  301. package/dist/esm/excel-parser/plugins/xls/index.d.ts +5 -0
  302. package/dist/esm/excel-parser/plugins/xls/index.d.ts.map +1 -0
  303. package/dist/esm/excel-parser/plugins/xls/index.js +7 -0
  304. package/dist/esm/excel-parser/plugins/xlsb/biff-record-reader.d.ts +13 -0
  305. package/dist/esm/excel-parser/plugins/xlsb/biff-record-reader.d.ts.map +1 -0
  306. package/dist/esm/excel-parser/plugins/xlsb/biff-record-reader.js +48 -0
  307. package/dist/esm/excel-parser/plugins/xlsb/index.d.ts +117 -0
  308. package/dist/esm/excel-parser/plugins/xlsb/index.d.ts.map +1 -0
  309. package/dist/esm/excel-parser/plugins/xlsb/index.js +2339 -0
  310. package/dist/esm/excel-parser/plugins/xlsb/rgce-parser.d.ts +18 -0
  311. package/dist/esm/excel-parser/plugins/xlsb/rgce-parser.d.ts.map +1 -0
  312. package/dist/esm/excel-parser/plugins/xlsb/rgce-parser.js +522 -0
  313. package/dist/esm/excel-parser/plugins/xlsm/index.d.ts +5 -0
  314. package/dist/esm/excel-parser/plugins/xlsm/index.d.ts.map +1 -0
  315. package/dist/esm/excel-parser/plugins/xlsm/index.js +6 -0
  316. package/dist/esm/excel-parser/plugins/xlsx/charts.d.ts +60 -0
  317. package/dist/esm/excel-parser/plugins/xlsx/charts.d.ts.map +1 -0
  318. package/dist/esm/excel-parser/plugins/xlsx/charts.js +2036 -0
  319. package/dist/esm/excel-parser/plugins/xlsx/conditional-formatting.d.ts +18 -0
  320. package/dist/esm/excel-parser/plugins/xlsx/conditional-formatting.d.ts.map +1 -0
  321. package/dist/esm/excel-parser/plugins/xlsx/conditional-formatting.js +621 -0
  322. package/dist/esm/excel-parser/plugins/xlsx/data-validations.d.ts +4 -0
  323. package/dist/esm/excel-parser/plugins/xlsx/data-validations.d.ts.map +1 -0
  324. package/dist/esm/excel-parser/plugins/xlsx/data-validations.js +493 -0
  325. package/dist/esm/excel-parser/plugins/xlsx/drawing.d.ts +39 -0
  326. package/dist/esm/excel-parser/plugins/xlsx/drawing.d.ts.map +1 -0
  327. package/dist/esm/excel-parser/plugins/xlsx/drawing.js +905 -0
  328. package/dist/esm/excel-parser/plugins/xlsx/index.d.ts +320 -0
  329. package/dist/esm/excel-parser/plugins/xlsx/index.d.ts.map +1 -0
  330. package/dist/esm/excel-parser/plugins/xlsx/index.js +4654 -0
  331. package/dist/esm/excel-parser/plugins/xlsx/numfmt.d.ts +2 -0
  332. package/dist/esm/excel-parser/plugins/xlsx/numfmt.d.ts.map +1 -0
  333. package/dist/esm/excel-parser/plugins/xlsx/numfmt.js +3 -0
  334. package/dist/esm/excel-parser/plugins/xlsx/pivots.d.ts +40 -0
  335. package/dist/esm/excel-parser/plugins/xlsx/pivots.d.ts.map +1 -0
  336. package/dist/esm/excel-parser/plugins/xlsx/pivots.js +254 -0
  337. package/dist/esm/excel-parser/plugins/xlsx/relationships.d.ts +8 -0
  338. package/dist/esm/excel-parser/plugins/xlsx/relationships.d.ts.map +1 -0
  339. package/dist/esm/excel-parser/plugins/xlsx/relationships.js +13 -0
  340. package/dist/esm/excel-parser/plugins/xlsx/sheet-views.d.ts +26 -0
  341. package/dist/esm/excel-parser/plugins/xlsx/sheet-views.d.ts.map +1 -0
  342. package/dist/esm/excel-parser/plugins/xlsx/sheet-views.js +64 -0
  343. package/dist/esm/excel-parser/plugins/xlsx/styles.d.ts +44 -0
  344. package/dist/esm/excel-parser/plugins/xlsx/styles.d.ts.map +1 -0
  345. package/dist/esm/excel-parser/plugins/xlsx/styles.js +1365 -0
  346. package/dist/esm/excel-parser/plugins/xlsx/theme.d.ts +50 -0
  347. package/dist/esm/excel-parser/plugins/xlsx/theme.d.ts.map +1 -0
  348. package/dist/esm/excel-parser/plugins/xlsx/theme.js +127 -0
  349. package/dist/esm/excel-parser/plugins/xlsx/threaded-comments.d.ts +47 -0
  350. package/dist/esm/excel-parser/plugins/xlsx/threaded-comments.d.ts.map +1 -0
  351. package/dist/esm/excel-parser/plugins/xlsx/threaded-comments.js +103 -0
  352. package/dist/esm/excel-parser/plugins/xlsx/value-type-detection.d.ts +44 -0
  353. package/dist/esm/excel-parser/plugins/xlsx/value-type-detection.d.ts.map +1 -0
  354. package/dist/esm/excel-parser/plugins/xlsx/value-type-detection.js +399 -0
  355. package/dist/esm/excel-parser/registry.d.ts +5 -0
  356. package/dist/esm/excel-parser/registry.d.ts.map +1 -0
  357. package/dist/esm/excel-parser/registry.js +20 -0
  358. package/dist/esm/excel-parser/style-registry.d.ts +51 -0
  359. package/dist/esm/excel-parser/style-registry.d.ts.map +1 -0
  360. package/dist/esm/excel-parser/style-registry.js +163 -0
  361. package/dist/esm/excel-parser/theme-default.d.ts +3 -0
  362. package/dist/esm/excel-parser/theme-default.d.ts.map +1 -0
  363. package/dist/esm/excel-parser/theme-default.js +19 -0
  364. package/dist/esm/excel-parser/types.d.ts +365 -0
  365. package/dist/esm/excel-parser/types.d.ts.map +1 -0
  366. package/dist/esm/excel-parser/types.js +59 -0
  367. package/dist/esm/excel-parser/utils.d.ts +6 -0
  368. package/dist/esm/excel-parser/utils.d.ts.map +1 -0
  369. package/dist/esm/excel-parser/utils.js +25 -0
  370. package/dist/esm/excel-parser/workbook.d.ts +51 -0
  371. package/dist/esm/excel-parser/workbook.d.ts.map +1 -0
  372. package/dist/esm/excel-parser/workbook.js +192 -0
  373. package/dist/esm/excel-parser/worker.d.ts +2 -0
  374. package/dist/esm/excel-parser/worker.d.ts.map +1 -0
  375. package/dist/esm/excel-parser/worker.js +862 -0
  376. package/dist/esm/excel-parser/zip-parser-streaming.d.ts +13 -0
  377. package/dist/esm/excel-parser/zip-parser-streaming.d.ts.map +1 -0
  378. package/dist/esm/excel-parser/zip-parser-streaming.js +119 -0
  379. package/dist/esm/excel-parser/zip-parser.d.ts +11 -0
  380. package/dist/esm/excel-parser/zip-parser.d.ts.map +1 -0
  381. package/dist/esm/excel-parser/zip-parser.js +43 -0
  382. package/dist/esm/exporter/invalid-data-validation.d.ts +19 -0
  383. package/dist/esm/exporter/invalid-data-validation.d.ts.map +1 -0
  384. package/dist/esm/exporter/invalid-data-validation.js +37 -0
  385. package/dist/esm/exporter/ods/ods-export.d.ts +26 -0
  386. package/dist/esm/exporter/ods/ods-export.d.ts.map +1 -0
  387. package/dist/esm/exporter/ods/ods-export.js +3238 -0
  388. package/dist/esm/exporter/ods/sparkline-builder.d.ts +33 -0
  389. package/dist/esm/exporter/ods/sparkline-builder.d.ts.map +1 -0
  390. package/dist/esm/exporter/ods/sparkline-builder.js +165 -0
  391. package/dist/esm/exporter/xlsx/chart-builder.d.ts +3 -0
  392. package/dist/esm/exporter/xlsx/chart-builder.d.ts.map +1 -0
  393. package/dist/esm/exporter/xlsx/chart-builder.js +1805 -0
  394. package/dist/esm/exporter/xlsx/chart-templates.d.ts +1781 -0
  395. package/dist/esm/exporter/xlsx/chart-templates.d.ts.map +1 -0
  396. package/dist/esm/exporter/xlsx/chart-templates.js +1798 -0
  397. package/dist/esm/exporter/xlsx/future-functions.d.ts +10 -0
  398. package/dist/esm/exporter/xlsx/future-functions.d.ts.map +1 -0
  399. package/dist/esm/exporter/xlsx/future-functions.js +264 -0
  400. package/dist/esm/exporter/xlsx/pivot-builder.d.ts +58 -0
  401. package/dist/esm/exporter/xlsx/pivot-builder.d.ts.map +1 -0
  402. package/dist/esm/exporter/xlsx/pivot-builder.js +230 -0
  403. package/dist/esm/exporter/xlsx/power-query-builder.d.ts +26 -0
  404. package/dist/esm/exporter/xlsx/power-query-builder.d.ts.map +1 -0
  405. package/dist/esm/exporter/xlsx/power-query-builder.js +257 -0
  406. package/dist/esm/exporter/xlsx/slicer-builder.d.ts +11 -0
  407. package/dist/esm/exporter/xlsx/slicer-builder.d.ts.map +1 -0
  408. package/dist/esm/exporter/xlsx/slicer-builder.js +84 -0
  409. package/dist/esm/exporter/xlsx/sparkline-builder.d.ts +61 -0
  410. package/dist/esm/exporter/xlsx/sparkline-builder.d.ts.map +1 -0
  411. package/dist/esm/exporter/xlsx/sparkline-builder.js +437 -0
  412. package/dist/esm/exporter/xlsx/style-builder.d.ts +93 -0
  413. package/dist/esm/exporter/xlsx/style-builder.d.ts.map +1 -0
  414. package/dist/esm/exporter/xlsx/style-builder.js +795 -0
  415. package/dist/esm/exporter/xlsx/threaded-comments-builder.d.ts +58 -0
  416. package/dist/esm/exporter/xlsx/threaded-comments-builder.d.ts.map +1 -0
  417. package/dist/esm/exporter/xlsx/threaded-comments-builder.js +175 -0
  418. package/dist/esm/exporter/xlsx/xlsx-export.d.ts +121 -0
  419. package/dist/esm/exporter/xlsx/xlsx-export.d.ts.map +1 -0
  420. package/dist/esm/exporter/xlsx/xlsx-export.js +4062 -0
  421. package/dist/esm/helpers/theme-xml-generator.d.ts +8 -0
  422. package/dist/esm/helpers/theme-xml-generator.d.ts.map +1 -0
  423. package/dist/esm/helpers/theme-xml-generator.js +33 -0
  424. package/dist/esm/helpers.d.ts +34 -0
  425. package/dist/esm/helpers.d.ts.map +1 -0
  426. package/dist/esm/helpers.js +182 -0
  427. package/dist/esm/index.d.ts +16 -0
  428. package/dist/esm/index.d.ts.map +1 -0
  429. package/dist/esm/index.js +15 -0
  430. package/dist/esm/indexed-colors.d.ts +2 -0
  431. package/dist/esm/indexed-colors.d.ts.map +1 -0
  432. package/dist/esm/indexed-colors.js +75 -0
  433. package/dist/esm/ods-export.d.ts +24 -0
  434. package/dist/esm/ods-export.d.ts.map +1 -0
  435. package/dist/esm/ods-export.js +12 -0
  436. package/dist/esm/parse.d.ts +24 -0
  437. package/dist/esm/parse.d.ts.map +1 -0
  438. package/dist/esm/parse.js +55 -0
  439. package/dist/esm/power-query.d.ts +41 -0
  440. package/dist/esm/power-query.d.ts.map +1 -0
  441. package/dist/esm/power-query.js +19 -0
  442. package/dist/esm/server.d.ts +9 -0
  443. package/dist/esm/server.d.ts.map +1 -0
  444. package/dist/esm/server.js +8 -0
  445. package/dist/esm/theme1.d.ts +3 -0
  446. package/dist/esm/theme1.d.ts.map +1 -0
  447. package/dist/esm/theme1.js +2 -0
  448. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -0
  449. package/dist/esm/types.d.ts +14 -0
  450. package/dist/esm/types.d.ts.map +1 -0
  451. package/dist/esm/types.js +14 -0
  452. package/dist/esm/vitest.config.d.ts +3 -0
  453. package/dist/esm/vitest.config.d.ts.map +1 -0
  454. package/dist/esm/vitest.config.js +16 -0
  455. package/package.json +62 -0
  456. package/xlsx.d.ts +52 -0
@@ -0,0 +1,2896 @@
1
+ import { addressToCell, cellToAddress, getCellXfsKey, uuid, } from "@rowsncolumns/utils";
2
+ import { XMLParser } from "fast-xml-parser";
3
+ import { get } from "lodash-es";
4
+ import { Unescape } from "../../utils";
5
+ import { ZipParser } from "../../zip-parser";
6
+ import { SheetDataValidationStamper } from "../../dv-cell-stamp";
7
+ import { BaseWorkbookPlugin } from "../base/index";
8
+ import { detectValueTypeAndPattern } from "../xlsx/value-type-detection";
9
+ import { extractConditionalFormats } from "./conditional-formatting";
10
+ import { extractDataValidations } from "./data-validations";
11
+ import { convertOdsFormulaToExcel } from "./formula";
12
+ import { extractSparklines, extractSparklineData, } from "./sparklines";
13
+ import { OdsStyles } from "./styles";
14
+ const ODS_XML_OPTIONS = {
15
+ ignoreAttributes: false,
16
+ attributeNamePrefix: "@_",
17
+ ignoreDeclaration: true,
18
+ // We keep namespaces because ODS relies on them (table:, office:, text:)
19
+ removeNSPrefix: false,
20
+ // Disable entity processing to avoid expansion limits on large files,
21
+ // then decode the bounded set of entities ourselves (same approach as
22
+ // the XLSX plugin's XMLTagProcessor) so text and attribute values don't
23
+ // keep literal &/</
 sequences.
24
+ processEntities: false,
25
+ tagValueProcessor: (_name, value) => Unescape(value),
26
+ attributeValueProcessor: (_name, value) => Unescape(value),
27
+ };
28
+ const ODS_ORDERED_XML_OPTIONS = {
29
+ ...ODS_XML_OPTIONS,
30
+ preserveOrder: true,
31
+ };
32
+ const DISPLAY_CURRENCY_REGEX = /[$€¥£₹]/;
33
+ function findChild(children, tagName) {
34
+ return children?.find((child) => child[tagName]);
35
+ }
36
+ function getChildren(node, tagName) {
37
+ const found = findChild(node, tagName);
38
+ return found ? found[tagName] : undefined;
39
+ }
40
+ function getAttribute(node, attributeName) {
41
+ if (!node)
42
+ return undefined;
43
+ if (node[`@_${attributeName}`] !== undefined) {
44
+ return node[`@_${attributeName}`];
45
+ }
46
+ if (node[":@"] && node[":@"][`@_${attributeName}`] !== undefined) {
47
+ return node[":@"][`@_${attributeName}`];
48
+ }
49
+ return undefined;
50
+ }
51
+ function fastMergeCellFormats(...formats) {
52
+ let result;
53
+ for (const format of formats) {
54
+ if (!format)
55
+ continue;
56
+ if (!result) {
57
+ result = { ...format };
58
+ continue;
59
+ }
60
+ // Merge properties
61
+ if (format.backgroundColor !== undefined)
62
+ result.backgroundColor = format.backgroundColor;
63
+ if (format.horizontalAlignment !== undefined)
64
+ result.horizontalAlignment = format.horizontalAlignment;
65
+ if (format.verticalAlignment !== undefined)
66
+ result.verticalAlignment = format.verticalAlignment;
67
+ if (format.wrapStrategy !== undefined)
68
+ result.wrapStrategy = format.wrapStrategy;
69
+ if (format.textRotation !== undefined)
70
+ result.textRotation = format.textRotation;
71
+ if (format.indent !== undefined)
72
+ result.indent = format.indent;
73
+ // Objects
74
+ if (format.textFormat) {
75
+ result.textFormat = result.textFormat
76
+ ? { ...result.textFormat, ...format.textFormat }
77
+ : { ...format.textFormat };
78
+ }
79
+ if (format.numberFormat) {
80
+ result.numberFormat = { ...format.numberFormat };
81
+ }
82
+ if (format.borders) {
83
+ result.borders = result.borders
84
+ ? { ...result.borders }
85
+ : { ...format.borders };
86
+ if (format.borders.top)
87
+ result.borders.top = format.borders.top;
88
+ if (format.borders.bottom)
89
+ result.borders.bottom = format.borders.bottom;
90
+ if (format.borders.left)
91
+ result.borders.left = format.borders.left;
92
+ if (format.borders.right)
93
+ result.borders.right = format.borders.right;
94
+ }
95
+ }
96
+ return result;
97
+ }
98
+ // Inverse of the exporter's mapConditionToFilterCondition: ODS
99
+ // <table:filter-condition table:operator> -> in-app condition type. "=" is
100
+ // intentionally absent — it round-trips as a value-list (the common case),
101
+ // which yields the same visible rows as NUMBER_EQ.
102
+ const ODS_OPERATOR_TO_CONDITION = {
103
+ "!=": "NUMBER_NOT_EQ",
104
+ "<>": "NUMBER_NOT_EQ",
105
+ ">": "NUMBER_GREATER",
106
+ ">=": "NUMBER_GREATER_THAN_EQ",
107
+ "<": "NUMBER_LESS",
108
+ "<=": "NUMBER_LESS_THAN_EQ",
109
+ between: "NUMBER_BETWEEN",
110
+ "not-between": "NUMBER_NOT_BETWEEN",
111
+ contains: "TEXT_CONTAINS",
112
+ "does-not-contain": "TEXT_NOT_CONTAINS",
113
+ "begins-with": "TEXT_STARTS_WITH",
114
+ begins: "TEXT_STARTS_WITH",
115
+ "ends-with": "TEXT_ENDS_WITH",
116
+ endswith: "TEXT_ENDS_WITH",
117
+ empty: "BLANK",
118
+ "non-empty": "NOT_BLANK",
119
+ };
120
+ export class OdsPlugin extends BaseWorkbookPlugin {
121
+ zipParser;
122
+ xmlParser;
123
+ xmlOrderedParser;
124
+ contentXml;
125
+ contentXmlOrdered;
126
+ settingsXml;
127
+ odsStyles;
128
+ cellXfs = new Map();
129
+ sharedStrings = [];
130
+ richSharedStringRuns = new Map();
131
+ sharedStringIndexByValue = new Map();
132
+ sheetCache = null;
133
+ sparklinesBySheet = new Map();
134
+ constructor() {
135
+ super();
136
+ this.zipParser = new ZipParser();
137
+ this.xmlParser = new XMLParser(ODS_XML_OPTIONS);
138
+ this.xmlOrderedParser = new XMLParser(ODS_ORDERED_XML_OPTIONS);
139
+ this.odsStyles = new OdsStyles();
140
+ }
141
+ async load(buffer, password) {
142
+ const source = buffer instanceof File ? await buffer.arrayBuffer() : buffer;
143
+ await this.zipParser.load(source, password);
144
+ this.cellXfs.clear();
145
+ this.odsStyles.reset();
146
+ this.sharedStrings = [];
147
+ this.richSharedStringRuns = new Map();
148
+ this.sharedStringIndexByValue.clear();
149
+ this.sheetCache = null;
150
+ this.sparklinesBySheet = new Map();
151
+ this.settingsXml = null;
152
+ try {
153
+ const content = this.zipParser.Get("content.xml");
154
+ if (content) {
155
+ this.contentXml = this.xmlParser.parse(content);
156
+ this.contentXmlOrdered = this.xmlOrderedParser.parse(content);
157
+ this.odsStyles.parseContentXml(this.contentXml);
158
+ }
159
+ const styles = this.zipParser.Get("styles.xml");
160
+ if (styles) {
161
+ const parsedStyles = this.xmlParser.parse(styles);
162
+ this.odsStyles.parseStylesXml(parsedStyles);
163
+ }
164
+ const settings = this.zipParser.Get("settings.xml");
165
+ if (settings) {
166
+ this.settingsXml = this.xmlParser.parse(settings);
167
+ }
168
+ }
169
+ catch (e) {
170
+ console.error("Failed to parse ODS content.xml", e);
171
+ }
172
+ }
173
+ getCellXfs() {
174
+ return this.cellXfs;
175
+ }
176
+ getSharedStrings() {
177
+ return this.sharedStrings;
178
+ }
179
+ getOrCreateSharedStringIndex(value, runs) {
180
+ const key = runs?.length ? JSON.stringify({ value, runs }) : value;
181
+ const existing = this.sharedStringIndexByValue.get(key);
182
+ if (existing !== undefined) {
183
+ return existing;
184
+ }
185
+ const index = this.sharedStrings.length;
186
+ this.sharedStrings.push(value);
187
+ if (runs?.length) {
188
+ this.richSharedStringRuns.set(index, runs);
189
+ }
190
+ this.sharedStringIndexByValue.set(key, index);
191
+ return index;
192
+ }
193
+ registerFormat(format) {
194
+ const key = getCellXfsKey(format);
195
+ if (!this.cellXfs.has(key)) {
196
+ this.cellXfs.set(key, format);
197
+ }
198
+ return key;
199
+ }
200
+ dispose() {
201
+ this.zipParser.Dispose();
202
+ this.contentXml = null;
203
+ this.contentXmlOrdered = null;
204
+ this.settingsXml = null;
205
+ this.sheetCache = null;
206
+ }
207
+ getSpreadsheetNode() {
208
+ if (!this.contentXml)
209
+ return undefined;
210
+ return (get(this.contentXml, "office:document-content.office:body.office:spreadsheet") || get(this.contentXml, "document-content.body.spreadsheet"));
211
+ }
212
+ getTables() {
213
+ const body = this.getSpreadsheetNode();
214
+ if (!body)
215
+ return [];
216
+ const tables = get(body, "table:table") || get(body, "table");
217
+ return Array.isArray(tables) ? tables : tables ? [tables] : [];
218
+ }
219
+ getSheetCount() {
220
+ return this.getTables().length;
221
+ }
222
+ ensureSheetCache() {
223
+ if (this.sheetCache) {
224
+ return this.sheetCache;
225
+ }
226
+ const cache = this.buildSheetCache();
227
+ this.sheetCache = cache;
228
+ return cache;
229
+ }
230
+ buildSheetCache() {
231
+ const tables = this.getTables();
232
+ const sheets = [];
233
+ const sheetContexts = [];
234
+ tables.forEach((table, index) => {
235
+ const name = table["@_table:name"] ||
236
+ table["@_name"] ||
237
+ table["table:name"] ||
238
+ table["name"] ||
239
+ `Sheet${index + 1}`;
240
+ const sheet = {
241
+ sheetId: index + 1,
242
+ title: name,
243
+ hidden: table["@_table:style-name"]?.includes("hidden") ||
244
+ table["table:style-name"]?.includes("hidden") ||
245
+ false,
246
+ rowMetadata: [],
247
+ columnMetadata: [],
248
+ merges: [],
249
+ };
250
+ // Column metadata from table-column elements
251
+ const columnNodes = get(table, "table:table-column") || get(table, "table-column") || [];
252
+ const allColumns = Array.isArray(columnNodes)
253
+ ? columnNodes
254
+ : columnNodes
255
+ ? [columnNodes]
256
+ : [];
257
+ let currentColumnIndex = 1;
258
+ let detectedColumnCount = 0;
259
+ let defaultColumnWidth;
260
+ let defaultColumnStyleName;
261
+ for (const column of allColumns) {
262
+ if (!column)
263
+ continue;
264
+ const colsRepeated = parseInt(getAttribute(column, "table:number-columns-repeated") ||
265
+ getAttribute(column, "number-columns-repeated") ||
266
+ "1", 10);
267
+ const styleName = getAttribute(column, "table:style-name") ||
268
+ getAttribute(column, "style-name");
269
+ const visibility = getAttribute(column, "table:visibility") ||
270
+ getAttribute(column, "visibility");
271
+ const columnStyle = styleName
272
+ ? this.odsStyles.getColumnStyle(styleName)
273
+ : undefined;
274
+ const width = columnStyle?.width;
275
+ const isHidden = visibility === "collapse" ||
276
+ visibility === "hidden" ||
277
+ visibility === "none" ||
278
+ columnStyle?.hidden;
279
+ for (let c = 0; c < colsRepeated; c++) {
280
+ const props = {};
281
+ let shouldAssign = false;
282
+ if (typeof width === "number") {
283
+ props.size = width;
284
+ if (defaultColumnWidth === undefined) {
285
+ defaultColumnWidth = width;
286
+ defaultColumnStyleName = styleName;
287
+ }
288
+ const widthDiffers = defaultColumnWidth === undefined
289
+ ? true
290
+ : Math.abs(width - defaultColumnWidth) > 0.5;
291
+ const styleDiffers = styleName &&
292
+ defaultColumnStyleName &&
293
+ styleName !== defaultColumnStyleName;
294
+ if (widthDiffers || styleDiffers) {
295
+ props.resizedByUser = true;
296
+ }
297
+ shouldAssign = true;
298
+ }
299
+ if (isHidden) {
300
+ props.hiddenByUser = true;
301
+ shouldAssign = true;
302
+ }
303
+ if (shouldAssign) {
304
+ sheet.columnMetadata[currentColumnIndex] = props;
305
+ }
306
+ currentColumnIndex++;
307
+ }
308
+ detectedColumnCount = Math.max(detectedColumnCount, currentColumnIndex - 1);
309
+ }
310
+ // Row metadata from table-row elements
311
+ const rowNodes = get(table, "table:table-row") || get(table, "table-row") || [];
312
+ const allRows = Array.isArray(rowNodes) ? rowNodes : [rowNodes];
313
+ // DO NOT DELETE
314
+ // console.log("allRows", allRows);
315
+ let currentRowIndex = 1;
316
+ let defaultRowHeight;
317
+ let defaultRowStyleName;
318
+ let detectedRowCount = 0;
319
+ for (const row of allRows) {
320
+ if (!row)
321
+ continue;
322
+ const rowsRepeated = parseInt(getAttribute(row, "table:number-rows-repeated") ||
323
+ getAttribute(row, "number-rows-repeated") ||
324
+ "1", 10);
325
+ const styleName = getAttribute(row, "table:style-name") ||
326
+ getAttribute(row, "style-name");
327
+ const visibility = getAttribute(row, "table:visibility") ||
328
+ getAttribute(row, "visibility");
329
+ const rowStyle = styleName
330
+ ? this.odsStyles.getRowStyle(styleName)
331
+ : undefined;
332
+ const height = rowStyle?.height;
333
+ const isHidden = visibility === "collapse" ||
334
+ visibility === "hidden" ||
335
+ visibility === "none" ||
336
+ rowStyle?.hidden;
337
+ const effectiveRepeats = Math.min(rowsRepeated, 10000);
338
+ const shouldProcessRowMetadata = typeof height === "number" || isHidden;
339
+ if (!shouldProcessRowMetadata) {
340
+ currentRowIndex += effectiveRepeats;
341
+ detectedRowCount = Math.max(detectedRowCount, currentRowIndex - 1);
342
+ continue;
343
+ }
344
+ for (let r = 0; r < effectiveRepeats; r++) {
345
+ const props = {};
346
+ let shouldAssign = false;
347
+ if (typeof height === "number") {
348
+ props.size = height;
349
+ if (defaultRowHeight === undefined) {
350
+ defaultRowHeight = height;
351
+ defaultRowStyleName = styleName;
352
+ }
353
+ const heightDiffers = defaultRowHeight === undefined
354
+ ? true
355
+ : Math.abs(height - defaultRowHeight) > 0.5;
356
+ const styleDiffers = styleName &&
357
+ defaultRowStyleName &&
358
+ styleName !== defaultRowStyleName;
359
+ if (heightDiffers || styleDiffers) {
360
+ props.resizedByUser = true;
361
+ }
362
+ shouldAssign = true;
363
+ }
364
+ if (isHidden) {
365
+ props.hiddenByUser = true;
366
+ shouldAssign = true;
367
+ }
368
+ if (shouldAssign) {
369
+ sheet.rowMetadata[currentRowIndex] = props;
370
+ }
371
+ currentRowIndex++;
372
+ }
373
+ detectedRowCount = Math.max(detectedRowCount, currentRowIndex - 1);
374
+ }
375
+ if (detectedRowCount > 0) {
376
+ sheet.rowCount = Math.max(sheet.rowCount ?? 0, detectedRowCount);
377
+ }
378
+ if (detectedColumnCount > 0) {
379
+ sheet.columnCount = Math.max(sheet.columnCount ?? 0, detectedColumnCount);
380
+ }
381
+ sheets.push(sheet);
382
+ sheetContexts.push({ node: table, sheetId: sheet.sheetId, name });
383
+ });
384
+ const sheetNameMap = this.buildSheetNameMap(sheetContexts);
385
+ const spreadsheetNode = this.getSpreadsheetNode();
386
+ const tableViews = this.extractTableViews(sheetContexts);
387
+ const conditionalFormatting = extractConditionalFormats({
388
+ sheetContexts,
389
+ sheetNameMap,
390
+ odsStyles: this.odsStyles,
391
+ getAttributes: this.getAttributes.bind(this),
392
+ parseRangeList: this.parseRangeList.bind(this),
393
+ normalizeExpression: this.normalizeExpression.bind(this),
394
+ normalizeConditionExpression: this.normalizeConditionExpression.bind(this),
395
+ toArray: this.toArray.bind(this),
396
+ });
397
+ const dataValidations = extractDataValidations({
398
+ sheetContexts,
399
+ sheetNameMap,
400
+ spreadsheetNode,
401
+ getAttributes: this.getAttributes.bind(this),
402
+ parseRangeList: this.parseRangeList.bind(this),
403
+ normalizeConditionExpression: this.normalizeConditionExpression.bind(this),
404
+ parseBooleanAttribute: this.parseBooleanAttribute.bind(this),
405
+ mapAlertStyle: this.mapAlertStyle.bind(this),
406
+ toArray: this.toArray.bind(this),
407
+ });
408
+ const namedRanges = this.extractNamedRanges(sheetContexts);
409
+ const { drawings, charts } = this.extractEmbeddedObjects(sheetContexts, sheetNameMap);
410
+ // Extract LibreOffice sparklines (calcext:sparkline-groups)
411
+ this.sparklinesBySheet = extractSparklines({
412
+ sheetContexts,
413
+ sheetNameMap,
414
+ getAttributes: this.getAttributes.bind(this),
415
+ toArray: this.toArray.bind(this),
416
+ });
417
+ const mergesMap = this.extractMerges(sheetContexts);
418
+ this.extractOutlineGroups(sheets);
419
+ const sheetSettings = this.extractSheetSettings(sheetContexts);
420
+ const basicFilters = this.extractBasicFilters(sheetContexts, sheetNameMap, spreadsheetNode);
421
+ for (const sheet of sheets) {
422
+ sheet.merges = mergesMap.get(sheet.sheetId) ?? sheet.merges ?? [];
423
+ const settings = sheetSettings.get(sheet.sheetId);
424
+ if (settings) {
425
+ if (settings.frozenRowCount !== undefined) {
426
+ sheet.frozenRowCount = settings.frozenRowCount;
427
+ }
428
+ if (settings.frozenColumnCount !== undefined) {
429
+ sheet.frozenColumnCount = settings.frozenColumnCount;
430
+ }
431
+ if (settings.showGridLines !== undefined) {
432
+ sheet.showGridLines = settings.showGridLines;
433
+ }
434
+ if (settings.tabColor) {
435
+ sheet.tabColor = settings.tabColor;
436
+ }
437
+ }
438
+ const filter = basicFilters.get(sheet.sheetId);
439
+ if (filter) {
440
+ sheet.basicFilter = filter;
441
+ }
442
+ }
443
+ return {
444
+ sheets,
445
+ tables: tableViews,
446
+ conditionalFormatting,
447
+ dataValidations,
448
+ namedRanges,
449
+ drawings,
450
+ charts,
451
+ };
452
+ }
453
+ extractTableViews(sheetContexts) {
454
+ if (!sheetContexts.length)
455
+ return [];
456
+ const spreadsheet = this.getSpreadsheetNode();
457
+ if (!spreadsheet)
458
+ return [];
459
+ const sheetNameMap = this.buildSheetNameMap(sheetContexts);
460
+ const tableViews = [];
461
+ const globalRanges = this.toArray(get(spreadsheet, "table:database-ranges.table:database-range") ||
462
+ get(spreadsheet, "database-ranges.database-range"));
463
+ for (const node of globalRanges) {
464
+ const tableView = this.convertDatabaseRangeNode(node, sheetNameMap, undefined, sheetContexts);
465
+ if (tableView) {
466
+ tableViews.push(tableView);
467
+ }
468
+ }
469
+ for (const context of sheetContexts) {
470
+ const inlineRanges = this.toArray(get(context.node, "table:database-range") ||
471
+ get(context.node, "database-range"));
472
+ for (const node of inlineRanges) {
473
+ const tableView = this.convertDatabaseRangeNode(node, sheetNameMap, context, sheetContexts);
474
+ if (tableView) {
475
+ tableViews.push(tableView);
476
+ }
477
+ }
478
+ }
479
+ return tableViews;
480
+ }
481
+ convertDatabaseRangeNode(node, sheetNameMap, context, sheetContexts) {
482
+ const attrs = this.getAttributes(node);
483
+ if (!attrs)
484
+ return undefined;
485
+ const rangeAttr = attrs["@_table:target-range-address"] ||
486
+ attrs["@_target-range-address"] ||
487
+ attrs["@_table:cell-range-address"];
488
+ const ranges = this.parseRangeList(rangeAttr, sheetNameMap, context);
489
+ if (!ranges.length)
490
+ return undefined;
491
+ const [range] = ranges;
492
+ const title = attrs["@_table:display-label"] ||
493
+ attrs["@_table:name"] ||
494
+ attrs["@_name"] ||
495
+ `Table${range.sheetId}`;
496
+ const tableView = {
497
+ id: attrs["@_table:id"] || attrs["@_id"] || title,
498
+ sheetId: range.sheetId,
499
+ title,
500
+ range,
501
+ };
502
+ const headerRow = this.parseBooleanAttribute(attrs["@_table:contains-header"] || attrs["@_contains-header"]);
503
+ if (headerRow !== undefined) {
504
+ tableView.headerRow = headerRow;
505
+ }
506
+ const totalRow = this.parseBooleanAttribute(attrs["@_table:contains-total-row"] || attrs["@_contains-total-row"]);
507
+ if (totalRow !== undefined) {
508
+ tableView.totalRow = totalRow;
509
+ }
510
+ const filterButton = this.parseBooleanAttribute(attrs["@_table:display-filter-buttons"] ||
511
+ attrs["@_display-filter-buttons"]);
512
+ if (filterButton !== undefined) {
513
+ tableView.filterButton = filterButton;
514
+ }
515
+ const showRowStripes = this.parseBooleanAttribute(attrs["@_table:use-banding-rows"] || attrs["@_use-banding-rows"]);
516
+ if (showRowStripes !== undefined) {
517
+ tableView.showRowStripes = showRowStripes;
518
+ }
519
+ const showColumnStripes = this.parseBooleanAttribute(attrs["@_table:use-banding-columns"] || attrs["@_use-banding-columns"]);
520
+ if (showColumnStripes !== undefined) {
521
+ tableView.showColumnStripes = showColumnStripes;
522
+ }
523
+ const effectiveContext = context ??
524
+ sheetContexts?.find((entry) => entry.sheetId === range.sheetId);
525
+ const columns = this.extractTableColumnsFromHeader(range, effectiveContext, tableView.headerRow !== false);
526
+ if (columns?.length) {
527
+ tableView.columns = columns;
528
+ }
529
+ return tableView;
530
+ }
531
+ extractNamedRanges(sheetContexts) {
532
+ const spreadsheet = this.getSpreadsheetNode();
533
+ if (!spreadsheet)
534
+ return [];
535
+ const sheetNameMap = this.buildSheetNameMap(sheetContexts);
536
+ const namedRanges = [];
537
+ const processContainer = (container, context) => {
538
+ if (!container)
539
+ return;
540
+ const rangeNodes = this.toArray(get(container, "table:named-range") || get(container, "named-range"));
541
+ for (const node of rangeNodes) {
542
+ const attrs = this.getAttributes(node);
543
+ if (!attrs)
544
+ continue;
545
+ const name = attrs["@_table:name"] ||
546
+ attrs["@_name"] ||
547
+ attrs["@_table:display-name"];
548
+ if (!name)
549
+ continue;
550
+ const addressAttr = attrs["@_table:cell-range-address"] ||
551
+ attrs["@_cell-range-address"] ||
552
+ attrs["@_table:base-cell-address"] ||
553
+ attrs["@_base-cell-address"];
554
+ const ranges = this.parseRangeList(addressAttr, sheetNameMap, context);
555
+ for (const range of ranges) {
556
+ namedRanges.push({
557
+ namedRangeId: uuid(),
558
+ name,
559
+ range,
560
+ });
561
+ }
562
+ }
563
+ const expressionNodes = this.toArray(get(container, "table:named-expression") ||
564
+ get(container, "named-expression"));
565
+ for (const node of expressionNodes) {
566
+ const attrs = this.getAttributes(node);
567
+ if (!attrs)
568
+ continue;
569
+ const name = attrs["@_table:name"] ||
570
+ attrs["@_name"] ||
571
+ attrs["@_table:display-name"];
572
+ if (!name)
573
+ continue;
574
+ const expressionAttr = attrs["@_table:expression"] || attrs["@_expression"];
575
+ const excelFormula = this.normalizeExpression(expressionAttr);
576
+ if (excelFormula) {
577
+ namedRanges.push({
578
+ namedRangeId: uuid(),
579
+ name,
580
+ value: excelFormula,
581
+ });
582
+ }
583
+ }
584
+ };
585
+ const globalContainers = this.toArray(get(spreadsheet, "table:named-expressions") ||
586
+ get(spreadsheet, "named-expressions"));
587
+ for (const container of globalContainers) {
588
+ processContainer(container);
589
+ }
590
+ for (const context of sheetContexts) {
591
+ const containers = this.toArray(get(context.node, "table:named-expressions") ||
592
+ get(context.node, "named-expressions"));
593
+ for (const container of containers) {
594
+ processContainer(container, context);
595
+ }
596
+ }
597
+ return namedRanges;
598
+ }
599
+ extractBasicFilters(sheetContexts, sheetNameMap, spreadsheetNode) {
600
+ const filters = new Map();
601
+ if (!sheetContexts.length) {
602
+ return filters;
603
+ }
604
+ const processNode = (node, context) => {
605
+ if (!node)
606
+ return;
607
+ const filterNode = get(node, "table:filter") || get(node, "filter");
608
+ if (!filterNode)
609
+ return;
610
+ const attrs = this.getAttributes(node);
611
+ if (!attrs)
612
+ return;
613
+ const rangeAttr = attrs["@_table:target-range-address"] ||
614
+ attrs["@_target-range-address"] ||
615
+ attrs["@_table:cell-range-address"] ||
616
+ attrs["@_cell-range-address"];
617
+ const ranges = this.parseRangeList(rangeAttr, sheetNameMap, context);
618
+ if (!ranges.length)
619
+ return;
620
+ const filterSpecs = this.convertFilterNodeToSpecs(filterNode);
621
+ if (!filterSpecs.length)
622
+ return;
623
+ const [range] = ranges;
624
+ const filterView = {
625
+ id: attrs["@_table:name"] || attrs["@_name"] || uuid(),
626
+ title: attrs["@_table:display-label"] ||
627
+ attrs["@_table:name"] ||
628
+ attrs["@_name"],
629
+ range,
630
+ filterSpecs,
631
+ };
632
+ filters.set(range.sheetId, filterView);
633
+ };
634
+ if (spreadsheetNode) {
635
+ const globalRanges = this.toArray(get(spreadsheetNode, "table:database-ranges.table:database-range") ||
636
+ get(spreadsheetNode, "database-ranges.database-range"));
637
+ for (const node of globalRanges) {
638
+ processNode(node);
639
+ }
640
+ }
641
+ for (const context of sheetContexts) {
642
+ const inlineRanges = this.toArray(get(context.node, "table:database-range") ||
643
+ get(context.node, "database-range"));
644
+ for (const node of inlineRanges) {
645
+ processNode(node, context);
646
+ }
647
+ }
648
+ return filters;
649
+ }
650
+ convertFilterNodeToSpecs(filterNode) {
651
+ const specMap = new Map();
652
+ const addConditions = (container) => {
653
+ const conditions = this.toArray(get(container, "table:filter-condition") ||
654
+ get(container, "filter-condition"));
655
+ for (const condition of conditions) {
656
+ this.applyFilterCondition(condition, specMap);
657
+ }
658
+ };
659
+ addConditions(filterNode);
660
+ const andNodes = this.toArray(get(filterNode, "table:filter-and") || get(filterNode, "filter-and"));
661
+ for (const node of andNodes) {
662
+ addConditions(node);
663
+ }
664
+ const orNodes = this.toArray(get(filterNode, "table:filter-or") || get(filterNode, "filter-or"));
665
+ for (const node of orNodes) {
666
+ addConditions(node);
667
+ }
668
+ return Array.from(specMap.values()).sort((a, b) => a.columnIndex - b.columnIndex);
669
+ }
670
+ applyFilterCondition(node, specMap) {
671
+ const attrs = this.getAttributes(node);
672
+ if (!attrs)
673
+ return;
674
+ const field = attrs["@_table:field-number"] ||
675
+ attrs["@_field-number"] ||
676
+ attrs["@_table:column-number"] ||
677
+ attrs["@_column-number"];
678
+ if (field === undefined)
679
+ return;
680
+ const columnIndex = parseInt(field, 10);
681
+ if (isNaN(columnIndex))
682
+ return;
683
+ const operator = String(attrs["@_table:operator"] ?? attrs["@_operator"] ?? "=");
684
+ const value = attrs["@_table:value"] ?? attrs["@_value"];
685
+ const value2 = attrs["@_table:value2"] ?? attrs["@_value2"];
686
+ let spec = specMap.get(columnIndex);
687
+ if (!spec) {
688
+ spec = {
689
+ columnIndex,
690
+ filterCriteria: {},
691
+ };
692
+ specMap.set(columnIndex, spec);
693
+ }
694
+ // A non-"=" operator is a CONDITION filter (greater-than / contains /
695
+ // between / blank / …). Map it back to filterCriteria.condition so it
696
+ // survives the round-trip instead of being flattened into a value list.
697
+ const conditionType = ODS_OPERATOR_TO_CONDITION[operator];
698
+ if (conditionType) {
699
+ const conditionValues = [];
700
+ if (value !== undefined) {
701
+ conditionValues.push({ userEnteredValue: String(value) });
702
+ }
703
+ if (value2 !== undefined) {
704
+ conditionValues.push({ userEnteredValue: String(value2) });
705
+ }
706
+ spec.filterCriteria.condition = {
707
+ type: conditionType,
708
+ values: conditionValues,
709
+ };
710
+ return;
711
+ }
712
+ // operator "=" (or unknown) -> value-list entry.
713
+ if (value === undefined)
714
+ return;
715
+ if (!spec.filterCriteria.visibleValues) {
716
+ spec.filterCriteria.visibleValues = [];
717
+ }
718
+ spec.filterCriteria.visibleValues.push(String(value));
719
+ }
720
+ extractTextContent(node) {
721
+ if (node === null || node === undefined)
722
+ return "";
723
+ const primitiveTypes = ["string", "number", "boolean"];
724
+ if (primitiveTypes.includes(typeof node)) {
725
+ return String(node);
726
+ }
727
+ if (Array.isArray(node)) {
728
+ return node.map((entry) => this.extractTextContent(entry)).join("");
729
+ }
730
+ let result = "";
731
+ if (node["text:s"]) {
732
+ result += this.extractSpaceNodes(node["text:s"], node);
733
+ }
734
+ if (node["text:line-break"]) {
735
+ const breaks = this.toArray(node["text:line-break"]);
736
+ result += "\n".repeat(breaks.length || 1);
737
+ }
738
+ if (node["text:span"]) {
739
+ result += this.extractTextContent(node["text:span"]);
740
+ }
741
+ if (node["text:a"]) {
742
+ result += this.extractTextContent(node["text:a"]);
743
+ }
744
+ if (node["text:p"]) {
745
+ result += this.extractTextContent(node["text:p"]);
746
+ }
747
+ if (node["#text"] !== undefined && node["#text"] !== null) {
748
+ const textValue = node["#text"];
749
+ if (typeof textValue === "string" ||
750
+ typeof textValue === "number" ||
751
+ typeof textValue === "boolean") {
752
+ result += String(textValue);
753
+ }
754
+ }
755
+ return result;
756
+ }
757
+ extractTextContentWithRuns(node) {
758
+ let text = "";
759
+ const runs = [];
760
+ const hasFormat = (format) => !!format && Object.keys(format).length > 0;
761
+ const mergeFormat = (base, next) => (base || next ? { ...(base ?? {}), ...(next ?? {}) } : undefined);
762
+ const appendText = (value) => {
763
+ if (typeof value === "string" ||
764
+ typeof value === "number" ||
765
+ typeof value === "boolean") {
766
+ text += String(value);
767
+ }
768
+ };
769
+ const walk = (value, inherited) => {
770
+ if (value === null || value === undefined)
771
+ return;
772
+ if (Array.isArray(value)) {
773
+ value.forEach((entry) => walk(entry, inherited));
774
+ return;
775
+ }
776
+ if (typeof value === "string" ||
777
+ typeof value === "number" ||
778
+ typeof value === "boolean") {
779
+ appendText(value);
780
+ return;
781
+ }
782
+ if (value["text:s"]) {
783
+ text += this.extractSpaceNodes(value["text:s"], value);
784
+ }
785
+ if (value["text:line-break"]) {
786
+ const breaks = this.toArray(value["text:line-break"]);
787
+ text += "\n".repeat(breaks.length || 1);
788
+ }
789
+ if (value["#text"] !== undefined && value["#text"] !== null) {
790
+ appendText(value["#text"]);
791
+ }
792
+ const spanNodes = value["text:span"];
793
+ if (spanNodes) {
794
+ for (const span of this.toArray(spanNodes)) {
795
+ const attrs = this.getAttributes(span) ?? {};
796
+ const styleName = attrs["@_text:style-name"] || attrs["@_style-name"];
797
+ const spanFormat = mergeFormat(inherited, this.odsStyles.getTextFormat(styleName));
798
+ const startIndex = text.length;
799
+ walk(span, spanFormat);
800
+ if (hasFormat(spanFormat) && text.length > startIndex) {
801
+ const format = spanFormat;
802
+ runs.push({
803
+ startIndex,
804
+ endIndex: text.length,
805
+ format,
806
+ nodeType: "text",
807
+ });
808
+ }
809
+ }
810
+ }
811
+ if (value["text:a"]) {
812
+ walk(value["text:a"], inherited);
813
+ }
814
+ if (value["text:p"]) {
815
+ walk(value["text:p"], inherited);
816
+ }
817
+ };
818
+ walk(node);
819
+ return { text, runs };
820
+ }
821
+ normalizeInternalHyperlinkLocation(href) {
822
+ let location = href.trim();
823
+ if (location.startsWith("#")) {
824
+ location = location.slice(1);
825
+ }
826
+ else if (location.startsWith("sheet://")) {
827
+ location = location.slice("sheet://".length);
828
+ }
829
+ else if (location.startsWith("vnd.sun.star.sheet:")) {
830
+ location = location.slice("vnd.sun.star.sheet:".length);
831
+ }
832
+ if (location.includes("!")) {
833
+ return location;
834
+ }
835
+ const match = location.match(/^(.*)\.([A-Za-z]+[0-9].*)$/);
836
+ if (match) {
837
+ return `${match[1]}!${match[2]}`;
838
+ }
839
+ return location;
840
+ }
841
+ extractHyperlinkValue(node) {
842
+ if (node === null || node === undefined)
843
+ return undefined;
844
+ if (Array.isArray(node)) {
845
+ for (const entry of node) {
846
+ const result = this.extractHyperlinkValue(entry);
847
+ if (result)
848
+ return result;
849
+ }
850
+ return undefined;
851
+ }
852
+ if (typeof node !== "object") {
853
+ return undefined;
854
+ }
855
+ if (node["text:a"]) {
856
+ const anchors = Array.isArray(node["text:a"])
857
+ ? node["text:a"]
858
+ : [node["text:a"]];
859
+ for (const anchor of anchors) {
860
+ const attrs = anchor?.[":@"] || anchor;
861
+ const href = attrs?.["@_xlink:href"] || attrs?.["@_href"];
862
+ if (!href) {
863
+ continue;
864
+ }
865
+ const display = this.extractTextContent(anchor);
866
+ if (href.startsWith("#") ||
867
+ href.startsWith("sheet://") ||
868
+ href.startsWith("vnd.sun.star.sheet:")) {
869
+ return {
870
+ kind: "internal",
871
+ location: this.normalizeInternalHyperlinkLocation(href),
872
+ ...(display ? { display } : {}),
873
+ };
874
+ }
875
+ return {
876
+ kind: "external",
877
+ url: href,
878
+ ...(display ? { display } : {}),
879
+ };
880
+ }
881
+ }
882
+ for (const value of Object.values(node)) {
883
+ const result = this.extractHyperlinkValue(value);
884
+ if (result)
885
+ return result;
886
+ }
887
+ return undefined;
888
+ }
889
+ extractSpaceNodes(node, fallback) {
890
+ const nodes = this.toArray(node);
891
+ if (!nodes.length) {
892
+ const fallbackAttrs = this.getAttributes(fallback);
893
+ if (!fallbackAttrs)
894
+ return " ";
895
+ const repeat = this.getSpaceRepeatCount(fallbackAttrs);
896
+ return " ".repeat(repeat);
897
+ }
898
+ return nodes
899
+ .map((entry) => {
900
+ const attrs = this.getAttributes(entry);
901
+ const repeat = this.getSpaceRepeatCount(attrs);
902
+ return " ".repeat(repeat);
903
+ })
904
+ .join("");
905
+ }
906
+ getSpaceRepeatCount(attrs) {
907
+ if (!attrs)
908
+ return 1;
909
+ const rawCount = attrs["@_text:c"] || attrs["@_c"] || attrs["@_text:count"];
910
+ if (rawCount === undefined) {
911
+ return 1;
912
+ }
913
+ const count = parseInt(rawCount, 10);
914
+ if (isNaN(count) || count < 0) {
915
+ return 1;
916
+ }
917
+ return Math.max(1, count + 1);
918
+ }
919
+ extractEmbeddedObjects(sheetContexts, sheetNameMap) {
920
+ const tables = this.getOrderedTableWrappers();
921
+ if (!tables.length || !sheetContexts.length) {
922
+ return { drawings: [], charts: [] };
923
+ }
924
+ const drawings = [];
925
+ const charts = [];
926
+ const chartAnchors = new Set();
927
+ const imageCache = new Map();
928
+ const tableCount = Math.min(tables.length, sheetContexts.length);
929
+ for (let sheetIndex = 0; sheetIndex < tableCount; sheetIndex++) {
930
+ const tableWrapper = tables[sheetIndex];
931
+ const tableNameKey = tableWrapper["table:table"]
932
+ ? "table:table"
933
+ : "table";
934
+ const tableChildren = tableWrapper[tableNameKey];
935
+ if (!tableChildren)
936
+ continue;
937
+ const rows = tableChildren.filter((child) => child["table:table-row"] || child["table-row"]);
938
+ let currentRowIndex = 1;
939
+ const sheetContext = sheetContexts[sheetIndex];
940
+ const sheetId = sheetContext?.sheetId ?? sheetIndex + 1;
941
+ for (const rowWrapper of rows) {
942
+ const rowNameKey = rowWrapper["table:table-row"]
943
+ ? "table:table-row"
944
+ : "table-row";
945
+ const rowChildren = rowWrapper[rowNameKey];
946
+ const rowAttrs = rowWrapper[":@"] || {};
947
+ const rowsRepeated = parseInt(rowAttrs["@_table:number-rows-repeated"] ||
948
+ rowAttrs["@_number-rows-repeated"] ||
949
+ "1", 10);
950
+ const effectiveRepeats = Math.min(rowsRepeated, 10000);
951
+ for (let repeat = 0; repeat < effectiveRepeats; repeat++) {
952
+ let columnIndex = 1;
953
+ const normalizedRowChildren = rowChildren
954
+ ? this.expandRowCells(rowChildren)
955
+ : null;
956
+ if (normalizedRowChildren) {
957
+ for (const cellWrapper of normalizedRowChildren) {
958
+ const isCell = !!cellWrapper["table:table-cell"];
959
+ const isCovered = !!cellWrapper["table:covered-table-cell"];
960
+ if (!isCell && !isCovered)
961
+ continue;
962
+ const cellKey = isCell
963
+ ? "table:table-cell"
964
+ : "table:covered-table-cell";
965
+ const cellChildren = cellWrapper[cellKey];
966
+ const cellAttrs = cellWrapper[":@"] || {};
967
+ const colsRepeated = parseInt(cellAttrs["@_table:number-columns-repeated"] ||
968
+ cellAttrs["@_number-columns-repeated"] ||
969
+ "1", 10);
970
+ if (isCovered) {
971
+ columnIndex += colsRepeated;
972
+ continue;
973
+ }
974
+ const frameNodes = this.collectFrameNodes(cellChildren, cellAttrs);
975
+ if (frameNodes.length) {
976
+ for (let c = 0; c < colsRepeated; c++) {
977
+ const anchorColumnIndex = columnIndex + c;
978
+ for (const { frame, cellAttrs: frameCellAttrs, } of frameNodes) {
979
+ const chart = this.convertFrameToEmbeddedChart(frame, sheetContext, sheetId, currentRowIndex, anchorColumnIndex, sheetNameMap, frameCellAttrs);
980
+ if (chart) {
981
+ chartAnchors.add(this.buildAnchorKey(sheetId, currentRowIndex, anchorColumnIndex));
982
+ charts.push(chart);
983
+ continue;
984
+ }
985
+ const anchorKey = this.buildAnchorKey(sheetId, currentRowIndex, anchorColumnIndex);
986
+ if (chartAnchors.has(anchorKey)) {
987
+ continue;
988
+ }
989
+ const embedded = this.convertFrameToEmbeddedObject(frame, sheetId, currentRowIndex, anchorColumnIndex, imageCache, frameCellAttrs);
990
+ if (embedded) {
991
+ drawings.push(embedded);
992
+ }
993
+ }
994
+ }
995
+ }
996
+ columnIndex += colsRepeated;
997
+ }
998
+ }
999
+ currentRowIndex++;
1000
+ }
1001
+ }
1002
+ }
1003
+ return { drawings, charts };
1004
+ }
1005
+ buildAnchorKey(sheetId, rowIndex, columnIndex) {
1006
+ return `${sheetId}:${rowIndex}:${columnIndex}`;
1007
+ }
1008
+ /**
1009
+ * Walk the ordered content XML for each table and assign outline
1010
+ * level + collapsed/hiddenByGroup state on `rowMetadata` and
1011
+ * `columnMetadata`. ODS encodes outline as `<table:table-row-group>`
1012
+ * / `<table:table-column-group>` wrappers (nest for level > 1); a
1013
+ * group is collapsed when its `table:display="false"`. Cells inside
1014
+ * a collapsed wrapper get `hiddenByGroup`; the summary cell (per
1015
+ * `outlinePr.summaryBelow`/`summaryRight`, default true = below /
1016
+ * right of the group) gets `collapsed = true`.
1017
+ */
1018
+ extractOutlineGroups(sheets) {
1019
+ if (!this.contentXmlOrdered || !sheets.length)
1020
+ return;
1021
+ const docContent = this.contentXmlOrdered[0]?.["office:document-content"] ||
1022
+ this.contentXmlOrdered[0]?.["document-content"];
1023
+ if (!docContent)
1024
+ return;
1025
+ const body = getChildren(docContent, "office:body") || getChildren(docContent, "body");
1026
+ if (!body)
1027
+ return;
1028
+ const spreadsheet = getChildren(body, "office:spreadsheet") ||
1029
+ getChildren(body, "spreadsheet");
1030
+ if (!spreadsheet)
1031
+ return;
1032
+ const tables = spreadsheet.filter((child) => child["table:table"] || child["table"]);
1033
+ const tableCount = Math.min(tables.length, sheets.length);
1034
+ for (let sheetIndex = 0; sheetIndex < tableCount; sheetIndex++) {
1035
+ const tableWrapper = tables[sheetIndex];
1036
+ const tableNameKey = tableWrapper["table:table"]
1037
+ ? "table:table"
1038
+ : "table";
1039
+ const tableChildren = tableWrapper[tableNameKey];
1040
+ if (!Array.isArray(tableChildren))
1041
+ continue;
1042
+ const sheet = sheets[sheetIndex];
1043
+ if (!sheet)
1044
+ continue;
1045
+ const summaryBelow = sheet.outlinePr?.summaryBelow !== false;
1046
+ const summaryRight = sheet.outlinePr?.summaryRight !== false;
1047
+ const rowMetadata = (sheet.rowMetadata ?? []);
1048
+ const columnMetadata = (sheet.columnMetadata ?? []);
1049
+ const setMeta = (meta, index, patch) => {
1050
+ if (!meta[index])
1051
+ meta[index] = {};
1052
+ Object.assign(meta[index], patch);
1053
+ };
1054
+ const walk = (nodes, itemTag, groupTag, meta, repeatAttrs, summaryAfter) => {
1055
+ let cursor = 1;
1056
+ const openStack = [];
1057
+ const collapsedDepth = () => openStack.some((g) => g.collapsed);
1058
+ const currentLevel = () => openStack.length;
1059
+ const visit = (children) => {
1060
+ for (const child of children) {
1061
+ if (!child)
1062
+ continue;
1063
+ if (child[groupTag]) {
1064
+ const attrs = child[":@"] || {};
1065
+ const display = attrs["@_table:display"] ?? attrs["@_display"];
1066
+ const collapsed = display === "false" || display === false;
1067
+ const startIndex = cursor;
1068
+ openStack.push({
1069
+ level: openStack.length + 1,
1070
+ startIndex,
1071
+ collapsed,
1072
+ });
1073
+ visit(child[groupTag] || []);
1074
+ const closing = openStack.pop();
1075
+ if (closing.collapsed) {
1076
+ const summaryIndex = summaryAfter
1077
+ ? cursor
1078
+ : closing.startIndex - 1;
1079
+ if (summaryIndex >= 1) {
1080
+ setMeta(meta, summaryIndex, { collapsed: true });
1081
+ }
1082
+ }
1083
+ }
1084
+ else if (child[itemTag]) {
1085
+ const attrs = child[":@"] || {};
1086
+ let repeat = 1;
1087
+ for (const attrName of repeatAttrs) {
1088
+ const raw = attrs[`@_${attrName}`];
1089
+ if (raw !== undefined) {
1090
+ repeat = Math.max(1, parseInt(String(raw), 10) || 1);
1091
+ break;
1092
+ }
1093
+ }
1094
+ repeat = Math.min(repeat, 10000);
1095
+ const level = currentLevel();
1096
+ const hidden = collapsedDepth();
1097
+ if (level > 0 || hidden) {
1098
+ for (let r = 0; r < repeat; r++) {
1099
+ const patch = {};
1100
+ if (level > 0)
1101
+ patch.outlineLevel = level;
1102
+ if (hidden)
1103
+ patch.hiddenByGroup = true;
1104
+ setMeta(meta, cursor + r, patch);
1105
+ }
1106
+ }
1107
+ cursor += repeat;
1108
+ }
1109
+ }
1110
+ };
1111
+ visit(nodes);
1112
+ };
1113
+ walk(tableChildren, "table:table-column", "table:table-column-group", columnMetadata, ["table:number-columns-repeated", "number-columns-repeated"], summaryRight);
1114
+ walk(tableChildren, "table:table-row", "table:table-row-group", rowMetadata, ["table:number-rows-repeated", "number-rows-repeated"], summaryBelow);
1115
+ if (columnMetadata.length > 0)
1116
+ sheet.columnMetadata = columnMetadata;
1117
+ if (rowMetadata.length > 0)
1118
+ sheet.rowMetadata = rowMetadata;
1119
+ }
1120
+ }
1121
+ extractMerges(sheetContexts) {
1122
+ const merges = new Map();
1123
+ if (!this.contentXmlOrdered || !sheetContexts.length) {
1124
+ return merges;
1125
+ }
1126
+ const docContent = this.contentXmlOrdered[0]?.["office:document-content"] ||
1127
+ this.contentXmlOrdered[0]?.["document-content"];
1128
+ if (!docContent)
1129
+ return merges;
1130
+ const body = getChildren(docContent, "office:body") || getChildren(docContent, "body");
1131
+ if (!body)
1132
+ return merges;
1133
+ const spreadsheet = getChildren(body, "office:spreadsheet") ||
1134
+ getChildren(body, "spreadsheet");
1135
+ if (!spreadsheet)
1136
+ return merges;
1137
+ const tables = spreadsheet.filter((child) => child["table:table"] || child["table"]);
1138
+ const tableCount = Math.min(tables.length, sheetContexts.length);
1139
+ const seen = new Set();
1140
+ for (let sheetIndex = 0; sheetIndex < tableCount; sheetIndex++) {
1141
+ const tableWrapper = tables[sheetIndex];
1142
+ const tableNameKey = tableWrapper["table:table"]
1143
+ ? "table:table"
1144
+ : "table";
1145
+ const tableChildren = tableWrapper[tableNameKey];
1146
+ if (!tableChildren)
1147
+ continue;
1148
+ const rows = tableChildren.filter((child) => child["table:table-row"] || child["table-row"]);
1149
+ const sheetId = sheetContexts[sheetIndex]?.sheetId ?? sheetIndex + 1;
1150
+ let currentRowIndex = 1;
1151
+ for (const rowWrapper of rows) {
1152
+ const rowNameKey = rowWrapper["table:table-row"]
1153
+ ? "table:table-row"
1154
+ : "table-row";
1155
+ const rowChildren = rowWrapper[rowNameKey];
1156
+ const rowAttrs = rowWrapper[":@"] || {};
1157
+ const rowsRepeated = parseInt(rowAttrs["@_table:number-rows-repeated"] ||
1158
+ rowAttrs["@_number-rows-repeated"] ||
1159
+ "1", 10);
1160
+ const effectiveRepeats = Math.min(rowsRepeated, 10000);
1161
+ for (let repeat = 0; repeat < effectiveRepeats; repeat++) {
1162
+ let columnIndex = 1;
1163
+ if (rowChildren) {
1164
+ for (const cellWrapper of rowChildren) {
1165
+ const isCell = !!cellWrapper["table:table-cell"];
1166
+ const isCovered = !!cellWrapper["table:covered-table-cell"];
1167
+ if (!isCell && !isCovered)
1168
+ continue;
1169
+ const cellKey = isCell
1170
+ ? "table:table-cell"
1171
+ : "table:covered-table-cell";
1172
+ const cellAttrs = cellWrapper[":@"] || {};
1173
+ const colsRepeated = parseInt(cellAttrs["@_table:number-columns-repeated"] ||
1174
+ cellAttrs["@_number-columns-repeated"] ||
1175
+ "1", 10);
1176
+ if (isCovered) {
1177
+ columnIndex += colsRepeated;
1178
+ continue;
1179
+ }
1180
+ const rowsSpannedRaw = parseInt(cellAttrs["@_table:number-rows-spanned"] ||
1181
+ cellAttrs["@_number-rows-spanned"] ||
1182
+ "1", 10);
1183
+ const colsSpannedRaw = parseInt(cellAttrs["@_table:number-columns-spanned"] ||
1184
+ cellAttrs["@_number-columns-spanned"] ||
1185
+ "1", 10);
1186
+ const rowsSpanned = Math.max(1, rowsSpannedRaw || 1);
1187
+ const colsSpanned = Math.max(1, colsSpannedRaw || 1);
1188
+ if (rowsSpanned > 1 || colsSpanned > 1) {
1189
+ for (let colOffset = 0; colOffset < colsRepeated; colOffset++) {
1190
+ const startColumnIndex = columnIndex + colOffset;
1191
+ const range = {
1192
+ sheetId,
1193
+ startRowIndex: currentRowIndex,
1194
+ endRowIndex: currentRowIndex + rowsSpanned - 1,
1195
+ startColumnIndex,
1196
+ endColumnIndex: startColumnIndex + colsSpanned - 1,
1197
+ };
1198
+ const key = `${range.sheetId}:${range.startRowIndex}:${range.endRowIndex}:${range.startColumnIndex}:${range.endColumnIndex}`;
1199
+ if (!seen.has(key)) {
1200
+ seen.add(key);
1201
+ if (!merges.has(sheetId)) {
1202
+ merges.set(sheetId, []);
1203
+ }
1204
+ merges.get(sheetId).push(range);
1205
+ }
1206
+ }
1207
+ }
1208
+ columnIndex += colsRepeated;
1209
+ }
1210
+ }
1211
+ currentRowIndex++;
1212
+ }
1213
+ }
1214
+ }
1215
+ return merges;
1216
+ }
1217
+ getOrderedTableWrappers() {
1218
+ if (!this.contentXmlOrdered)
1219
+ return [];
1220
+ const docContent = this.contentXmlOrdered[0]?.["office:document-content"] ||
1221
+ this.contentXmlOrdered[0]?.["document-content"];
1222
+ if (!docContent)
1223
+ return [];
1224
+ const body = getChildren(docContent, "office:body") || getChildren(docContent, "body");
1225
+ if (!body)
1226
+ return [];
1227
+ const spreadsheet = getChildren(body, "office:spreadsheet") ||
1228
+ getChildren(body, "spreadsheet");
1229
+ if (!spreadsheet)
1230
+ return [];
1231
+ return spreadsheet.filter((child) => child["table:table"] || child["table"]);
1232
+ }
1233
+ findOrderedTableWrapperForContext(sheetContext, sheetId) {
1234
+ const tables = this.getOrderedTableWrappers();
1235
+ if (!tables.length)
1236
+ return undefined;
1237
+ if (sheetContext?.name) {
1238
+ const normalizedTarget = this.normalizeSheetName(sheetContext.name);
1239
+ for (const tableWrapper of tables) {
1240
+ const attrs = tableWrapper[":@"] || {};
1241
+ const rawName = attrs["@_table:name"] ||
1242
+ attrs["@_name"] ||
1243
+ attrs["@_table:display-name"];
1244
+ const normalizedCandidate = this.normalizeSheetName(rawName);
1245
+ if ((rawName && rawName === sheetContext.name) ||
1246
+ (normalizedCandidate &&
1247
+ normalizedTarget &&
1248
+ normalizedCandidate === normalizedTarget)) {
1249
+ return tableWrapper;
1250
+ }
1251
+ }
1252
+ }
1253
+ if (sheetId && tables[sheetId - 1]) {
1254
+ return tables[sheetId - 1];
1255
+ }
1256
+ return tables[0];
1257
+ }
1258
+ extractTableColumnsFromHeader(range, sheetContext, useHeaderRow = true) {
1259
+ const startColumn = Math.max(1, range.startColumnIndex ?? 1);
1260
+ const endColumn = Math.max(startColumn, range.endColumnIndex ?? startColumn);
1261
+ const columnCount = Math.max(1, endColumn - startColumn + 1);
1262
+ const buildDefaultColumns = () => Array.from({ length: columnCount }, (_, index) => ({
1263
+ name: `Column ${index + 1}`,
1264
+ }));
1265
+ if (!useHeaderRow) {
1266
+ return buildDefaultColumns();
1267
+ }
1268
+ const tableWrapper = this.findOrderedTableWrapperForContext(sheetContext, range.sheetId);
1269
+ if (!tableWrapper) {
1270
+ return buildDefaultColumns();
1271
+ }
1272
+ const tableNameKey = tableWrapper["table:table"] ? "table:table" : "table";
1273
+ const tableChildren = tableWrapper[tableNameKey];
1274
+ if (!tableChildren) {
1275
+ return buildDefaultColumns();
1276
+ }
1277
+ const rows = tableChildren.filter((child) => child["table:table-row"] || child["table-row"]);
1278
+ if (!rows.length) {
1279
+ return buildDefaultColumns();
1280
+ }
1281
+ const headerRowIndex = Math.max(1, range.startRowIndex ?? 1);
1282
+ const headerValues = [];
1283
+ let currentRowIndex = 1;
1284
+ outer: for (const rowWrapper of rows) {
1285
+ const rowNameKey = rowWrapper["table:table-row"]
1286
+ ? "table:table-row"
1287
+ : "table-row";
1288
+ const rowChildren = rowWrapper[rowNameKey];
1289
+ const rowAttrs = rowWrapper[":@"] || {};
1290
+ const rowsRepeated = parseInt(rowAttrs["@_table:number-rows-repeated"] ||
1291
+ rowAttrs["@_number-rows-repeated"] ||
1292
+ "1", 10);
1293
+ const effectiveRepeats = Math.min(rowsRepeated, 10000);
1294
+ const normalizedRowChildren = rowChildren
1295
+ ? this.expandRowCells(rowChildren)
1296
+ : null;
1297
+ for (let repeat = 0; repeat < effectiveRepeats; repeat++) {
1298
+ if (currentRowIndex === headerRowIndex) {
1299
+ let columnIndex = 1;
1300
+ if (normalizedRowChildren) {
1301
+ for (const cellWrapper of normalizedRowChildren) {
1302
+ const isCell = !!cellWrapper["table:table-cell"];
1303
+ const isCovered = !!cellWrapper["table:covered-table-cell"];
1304
+ if (!isCell && !isCovered) {
1305
+ continue;
1306
+ }
1307
+ const cellNameKey = isCell
1308
+ ? "table:table-cell"
1309
+ : "table:covered-table-cell";
1310
+ const cellChildren = cellWrapper[cellNameKey];
1311
+ const cellAttrs = cellWrapper[":@"] || {};
1312
+ const colsRepeated = parseInt(cellAttrs["@_table:number-columns-repeated"] ||
1313
+ cellAttrs["@_number-columns-repeated"] ||
1314
+ "1", 10);
1315
+ if (isCovered) {
1316
+ columnIndex += colsRepeated;
1317
+ continue;
1318
+ }
1319
+ let displayValue = "";
1320
+ if (cellChildren) {
1321
+ const textWrapper = findChild(cellChildren, "text:p") ||
1322
+ findChild(cellChildren, "p");
1323
+ if (textWrapper) {
1324
+ const key = textWrapper["text:p"] ? "text:p" : "p";
1325
+ displayValue = this.extractTextContent(textWrapper[key]);
1326
+ }
1327
+ else {
1328
+ displayValue = this.extractTextContent(cellChildren);
1329
+ }
1330
+ }
1331
+ for (let c = 0; c < colsRepeated; c++) {
1332
+ const targetColumnIndex = columnIndex;
1333
+ if (targetColumnIndex >= startColumn &&
1334
+ targetColumnIndex <= endColumn) {
1335
+ const relativeIndex = headerValues.length;
1336
+ const fallbackName = `Column ${relativeIndex + 1}`;
1337
+ const trimmed = displayValue?.trim();
1338
+ headerValues.push(trimmed && trimmed.length ? trimmed : fallbackName);
1339
+ if (headerValues.length >= columnCount) {
1340
+ break;
1341
+ }
1342
+ }
1343
+ columnIndex++;
1344
+ }
1345
+ if (headerValues.length >= columnCount) {
1346
+ break;
1347
+ }
1348
+ }
1349
+ }
1350
+ break outer;
1351
+ }
1352
+ currentRowIndex++;
1353
+ }
1354
+ if (currentRowIndex > headerRowIndex) {
1355
+ break;
1356
+ }
1357
+ }
1358
+ if (!headerValues.length) {
1359
+ return buildDefaultColumns();
1360
+ }
1361
+ const columns = [];
1362
+ for (let i = 0; i < columnCount; i++) {
1363
+ const label = headerValues[i];
1364
+ columns.push({
1365
+ name: label && label.trim().length ? label : `Column ${i + 1}`,
1366
+ });
1367
+ }
1368
+ return columns;
1369
+ }
1370
+ collectFrameNodes(cellChildren, cellAttrs) {
1371
+ if (!cellChildren)
1372
+ return [];
1373
+ const children = Array.isArray(cellChildren)
1374
+ ? cellChildren
1375
+ : [cellChildren];
1376
+ const frames = [];
1377
+ for (const child of children) {
1378
+ if (!child || typeof child !== "object")
1379
+ continue;
1380
+ const frameNode = child["draw:frame"] || child["frame"];
1381
+ if (!frameNode)
1382
+ continue;
1383
+ if (Array.isArray(frameNode)) {
1384
+ for (const node of frameNode) {
1385
+ frames.push({ frame: node, cellAttrs: child[":@"] || cellAttrs });
1386
+ }
1387
+ }
1388
+ else {
1389
+ frames.push({ frame: frameNode, cellAttrs: child[":@"] || cellAttrs });
1390
+ }
1391
+ }
1392
+ return frames;
1393
+ }
1394
+ extractSheetSettings(sheetContexts) {
1395
+ const map = new Map();
1396
+ if (!this.settingsXml || !sheetContexts.length) {
1397
+ return map;
1398
+ }
1399
+ const documentSettings = get(this.settingsXml, "office:document-settings") ||
1400
+ get(this.settingsXml, "document-settings");
1401
+ if (!documentSettings)
1402
+ return map;
1403
+ const settingsNode = get(documentSettings, "office:settings") ||
1404
+ get(documentSettings, "settings");
1405
+ if (!settingsNode)
1406
+ return map;
1407
+ const nameMap = new Map();
1408
+ const traverse = (node) => {
1409
+ if (!node || typeof node !== "object")
1410
+ return;
1411
+ const entries = this.toArray(node["config:config-item-map-entry"] || node["config-item-map-entry"]);
1412
+ for (const entry of entries) {
1413
+ const attrs = this.getAttributes(entry);
1414
+ const entryName = attrs?.["@_config:name"] ||
1415
+ attrs?.["@_name"] ||
1416
+ attrs?.["@_table:name"];
1417
+ if (entryName) {
1418
+ const parsed = this.parseConfigItemEntry(entry);
1419
+ if (parsed) {
1420
+ const existing = nameMap.get(entryName) || {};
1421
+ const combined = { ...existing, ...parsed };
1422
+ nameMap.set(entryName, combined);
1423
+ const normalized = this.normalizeSheetName(entryName);
1424
+ if (normalized) {
1425
+ nameMap.set(normalized, combined);
1426
+ nameMap.set(`'${normalized}'`, combined);
1427
+ }
1428
+ }
1429
+ }
1430
+ traverse(entry);
1431
+ }
1432
+ const namedMaps = this.toArray(node["config:config-item-map-named"] || node["config-item-map-named"]);
1433
+ for (const named of namedMaps) {
1434
+ traverse(named);
1435
+ }
1436
+ };
1437
+ const itemSets = this.toArray(get(settingsNode, "config:config-item-set") ||
1438
+ get(settingsNode, "config-item-set"));
1439
+ for (const set of itemSets) {
1440
+ traverse(set);
1441
+ }
1442
+ for (const context of sheetContexts) {
1443
+ const normalized = this.normalizeSheetName(context.name);
1444
+ const settings = nameMap.get(context.name) ||
1445
+ (normalized ? nameMap.get(normalized) : undefined);
1446
+ if (settings) {
1447
+ map.set(context.sheetId, settings);
1448
+ }
1449
+ }
1450
+ return map;
1451
+ }
1452
+ parseConfigItemEntry(entry) {
1453
+ const configItems = this.toArray(get(entry, "config:config-item") || get(entry, "config-item"));
1454
+ if (!configItems.length)
1455
+ return undefined;
1456
+ const settings = {};
1457
+ for (const item of configItems) {
1458
+ const attrs = this.getAttributes(item);
1459
+ if (!attrs)
1460
+ continue;
1461
+ const name = attrs["@_config:name"] || attrs["@_name"];
1462
+ if (!name)
1463
+ continue;
1464
+ const value = this.getNodeText(item);
1465
+ if (value === undefined)
1466
+ continue;
1467
+ const normalized = value.trim();
1468
+ if (!normalized.length)
1469
+ continue;
1470
+ if (name === "ShowGrid" || name === "ShowGridLines") {
1471
+ const parsed = this.parseBooleanAttribute(normalized);
1472
+ if (parsed !== undefined) {
1473
+ settings.showGridLines = parsed;
1474
+ }
1475
+ continue;
1476
+ }
1477
+ if (name === "FreezeCell") {
1478
+ const freeze = this.parseFreezeCoordinates(normalized);
1479
+ if (freeze) {
1480
+ Object.assign(settings, freeze);
1481
+ }
1482
+ continue;
1483
+ }
1484
+ if (name === "VerticalSplitPosition") {
1485
+ const parsed = this.parseIntegerValue(normalized);
1486
+ if (parsed !== undefined) {
1487
+ settings.frozenRowCount = parsed;
1488
+ }
1489
+ continue;
1490
+ }
1491
+ if (name === "HorizontalSplitPosition") {
1492
+ const parsed = this.parseIntegerValue(normalized);
1493
+ if (parsed !== undefined) {
1494
+ settings.frozenColumnCount = parsed;
1495
+ }
1496
+ continue;
1497
+ }
1498
+ if (name === "TabColor") {
1499
+ const color = this.normalizeOdsColor(normalized);
1500
+ if (color) {
1501
+ settings.tabColor = color;
1502
+ }
1503
+ }
1504
+ }
1505
+ return Object.keys(settings).length ? settings : undefined;
1506
+ }
1507
+ parseFreezeCoordinates(value) {
1508
+ const normalized = value.replace(/\$/g, "").trim();
1509
+ if (!normalized)
1510
+ return undefined;
1511
+ const cell = addressToCell(normalized.toUpperCase());
1512
+ if (!cell)
1513
+ return undefined;
1514
+ return {
1515
+ frozenRowCount: Math.max(0, cell.rowIndex - 1),
1516
+ frozenColumnCount: Math.max(0, cell.columnIndex - 1),
1517
+ };
1518
+ }
1519
+ convertFrameToEmbeddedObject(frameNode, sheetId, rowIndex, columnIndex, imageCache, cellAttrs) {
1520
+ const frameAttrs = this.getAttributes(frameNode);
1521
+ const imageNode = this.findFrameImageNode(frameNode);
1522
+ const imageAttrs = this.getAttributes(imageNode);
1523
+ const imagePath = this.normalizeImagePath(imageAttrs?.["@_xlink:href"] ||
1524
+ imageAttrs?.["@_href"] ||
1525
+ frameAttrs?.["@_xlink:href"] ||
1526
+ frameAttrs?.["@_href"]);
1527
+ if (!imagePath)
1528
+ return undefined;
1529
+ const imageUrl = this.getImageDataUrl(imagePath, imageCache);
1530
+ if (!imageUrl)
1531
+ return undefined;
1532
+ const { overlayPosition, isInsideCell } = this.buildOverlayPlacement(frameAttrs, cellAttrs, rowIndex, columnIndex);
1533
+ const description = frameAttrs?.["@_draw:name"] || cellAttrs?.["@_draw:name"] || undefined;
1534
+ return {
1535
+ embedId: uuid(),
1536
+ spec: {
1537
+ type: "image",
1538
+ imageUrl,
1539
+ description,
1540
+ },
1541
+ position: {
1542
+ sheetId,
1543
+ overlayPosition,
1544
+ },
1545
+ draggable: true,
1546
+ lockAspectRatio: true,
1547
+ isInsideCell,
1548
+ };
1549
+ }
1550
+ convertFrameToEmbeddedChart(frameNode, sheetContext, sheetId, rowIndex, columnIndex, sheetNameMap, cellAttrs) {
1551
+ const frameAttrs = this.getAttributes(frameNode);
1552
+ const inlineChart = this.findInlineChartNode(frameNode);
1553
+ let chartNode = inlineChart;
1554
+ if (!chartNode) {
1555
+ const objectNode = this.findFrameObjectNode(frameNode);
1556
+ const chartFromObject = this.loadChartNodeFromObject(objectNode, frameAttrs);
1557
+ chartNode = chartFromObject;
1558
+ }
1559
+ if (!chartNode)
1560
+ return undefined;
1561
+ const spec = this.convertChartNodeToSpec(chartNode, sheetNameMap, sheetContext);
1562
+ if (!spec)
1563
+ return undefined;
1564
+ const { overlayPosition } = this.buildOverlayPlacement(frameAttrs, cellAttrs, rowIndex, columnIndex);
1565
+ const chartId = uuid();
1566
+ return {
1567
+ chartId,
1568
+ spec,
1569
+ position: {
1570
+ sheetId,
1571
+ overlayPosition,
1572
+ },
1573
+ draggable: true,
1574
+ resizable: true,
1575
+ };
1576
+ }
1577
+ findInlineChartNode(frameNode) {
1578
+ if (!frameNode)
1579
+ return undefined;
1580
+ if (Array.isArray(frameNode)) {
1581
+ for (const entry of frameNode) {
1582
+ const found = this.findInlineChartNode(entry);
1583
+ if (found)
1584
+ return found;
1585
+ }
1586
+ return undefined;
1587
+ }
1588
+ const chartNode = frameNode["chart:chart"] || frameNode["chart"];
1589
+ if (chartNode) {
1590
+ return Array.isArray(chartNode) ? chartNode[0] : chartNode;
1591
+ }
1592
+ return undefined;
1593
+ }
1594
+ findFrameObjectNode(frameNode) {
1595
+ if (!frameNode)
1596
+ return undefined;
1597
+ if (Array.isArray(frameNode)) {
1598
+ for (const entry of frameNode) {
1599
+ const found = this.findFrameObjectNode(entry);
1600
+ if (found)
1601
+ return found;
1602
+ }
1603
+ return undefined;
1604
+ }
1605
+ const objectNode = frameNode["draw:object"] || frameNode["object"];
1606
+ if (objectNode) {
1607
+ return Array.isArray(objectNode) ? objectNode[0] : objectNode;
1608
+ }
1609
+ return undefined;
1610
+ }
1611
+ findFrameImageNode(frameNode) {
1612
+ if (!frameNode)
1613
+ return undefined;
1614
+ if (Array.isArray(frameNode)) {
1615
+ for (const entry of frameNode) {
1616
+ const found = this.findFrameImageNode(entry);
1617
+ if (found)
1618
+ return found;
1619
+ }
1620
+ return undefined;
1621
+ }
1622
+ const direct = frameNode["draw:image"] || frameNode["image"] || frameNode["draw:object"];
1623
+ if (direct) {
1624
+ return Array.isArray(direct) ? direct[0] : direct;
1625
+ }
1626
+ return undefined;
1627
+ }
1628
+ loadChartNodeFromObject(objectNode, frameAttrs) {
1629
+ if (!objectNode)
1630
+ return undefined;
1631
+ const objectAttrs = this.getAttributes(objectNode);
1632
+ const href = objectAttrs?.["@_xlink:href"] ||
1633
+ objectAttrs?.["@_href"] ||
1634
+ frameAttrs?.["@_xlink:href"] ||
1635
+ frameAttrs?.["@_href"];
1636
+ return this.loadChartNodeFromHref(href);
1637
+ }
1638
+ loadChartNodeFromHref(href) {
1639
+ const path = this.resolveObjectPath(href);
1640
+ if (!path)
1641
+ return undefined;
1642
+ try {
1643
+ const content = this.zipParser.Get(path);
1644
+ const chartDoc = this.xmlParser.parse(content);
1645
+ this.odsStyles.parseContentXml(chartDoc);
1646
+ return this.findChartNodeInDocument(chartDoc);
1647
+ }
1648
+ catch {
1649
+ return undefined;
1650
+ }
1651
+ }
1652
+ resolveObjectPath(href) {
1653
+ if (!href)
1654
+ return undefined;
1655
+ let normalized = href.trim();
1656
+ if (!normalized)
1657
+ return undefined;
1658
+ if (normalized.startsWith("#")) {
1659
+ normalized = normalized.slice(1);
1660
+ }
1661
+ normalized = normalized.replace(/^\.\/+/, "");
1662
+ while (normalized.startsWith("../")) {
1663
+ normalized = normalized.slice(3);
1664
+ }
1665
+ if (!normalized)
1666
+ return undefined;
1667
+ const candidates = new Set();
1668
+ if (normalized.endsWith(".xml")) {
1669
+ candidates.add(normalized);
1670
+ candidates.add(`ObjectReplacements/${normalized}`);
1671
+ }
1672
+ else {
1673
+ candidates.add(`${normalized}/content.xml`);
1674
+ candidates.add(`${normalized}/Content.xml`);
1675
+ candidates.add(`${normalized}.xml`);
1676
+ candidates.add(`ObjectReplacements/${normalized}/content.xml`);
1677
+ candidates.add(`ObjectReplacements/${normalized}/Content.xml`);
1678
+ }
1679
+ for (const candidate of candidates) {
1680
+ if (!candidate)
1681
+ continue;
1682
+ if (this.zipParser.Has(candidate)) {
1683
+ return candidate;
1684
+ }
1685
+ }
1686
+ return undefined;
1687
+ }
1688
+ findChartNodeInDocument(doc) {
1689
+ if (!doc)
1690
+ return undefined;
1691
+ const chartNode = get(doc, "office:document-content.office:body.office:chart.chart:chart") ||
1692
+ get(doc, "office:document-content.office:body.chart:chart") ||
1693
+ get(doc, "chart:chart");
1694
+ if (chartNode) {
1695
+ return Array.isArray(chartNode) ? chartNode[0] : chartNode;
1696
+ }
1697
+ return this.findNodeRecursive(doc, "chart:chart");
1698
+ }
1699
+ findNodeRecursive(node, key) {
1700
+ if (!node || typeof node !== "object")
1701
+ return undefined;
1702
+ if (node[key]) {
1703
+ return Array.isArray(node[key]) ? node[key][0] : node[key];
1704
+ }
1705
+ for (const value of Object.values(node)) {
1706
+ if (Array.isArray(value)) {
1707
+ for (const entry of value) {
1708
+ const found = this.findNodeRecursive(entry, key);
1709
+ if (found)
1710
+ return found;
1711
+ }
1712
+ }
1713
+ else if (typeof value === "object") {
1714
+ const found = this.findNodeRecursive(value, key);
1715
+ if (found)
1716
+ return found;
1717
+ }
1718
+ }
1719
+ return undefined;
1720
+ }
1721
+ convertChartNodeToSpec(chartNode, sheetNameMap, context) {
1722
+ if (!chartNode)
1723
+ return undefined;
1724
+ const chartAttrs = this.getAttributes(chartNode);
1725
+ const chartType = this.mapOdsChartClassToType(chartAttrs?.["@_chart:class"] || chartAttrs?.["@_class"]);
1726
+ const titleNode = get(chartNode, "chart:title.chart:title-text") ||
1727
+ get(chartNode, "chart:title") ||
1728
+ get(chartNode, "title");
1729
+ const rawTitle = titleNode ? this.extractTextContent(titleNode) : "";
1730
+ const title = rawTitle?.trim() ? rawTitle.trim() : undefined;
1731
+ const plotArea = get(chartNode, "chart:plot-area") || get(chartNode, "plot-area");
1732
+ if (!plotArea)
1733
+ return undefined;
1734
+ const seriesNodes = this.toArray(get(plotArea, "chart:series") ||
1735
+ get(plotArea, "chart:data-series") ||
1736
+ get(plotArea, "series"));
1737
+ if (!seriesNodes.length)
1738
+ return undefined;
1739
+ const domains = [];
1740
+ const series = [];
1741
+ let seriesIndex = 1;
1742
+ for (const seriesNode of seriesNodes) {
1743
+ const domainNode = get(seriesNode, "chart:domain") ||
1744
+ get(seriesNode, "domain") ||
1745
+ get(seriesNode, "chart:categories");
1746
+ if (domainNode) {
1747
+ const domainAddress = this.findRangeString(domainNode);
1748
+ if (domainAddress) {
1749
+ const chartData = this.createChartDataFromAddress(domainAddress, sheetNameMap, context);
1750
+ if (chartData) {
1751
+ domains.push(chartData);
1752
+ }
1753
+ }
1754
+ }
1755
+ const valueAddress = this.findRangeString(seriesNode, new Set(["chart:domain", "domain", "chart:categories"]));
1756
+ if (valueAddress) {
1757
+ const seriesData = this.createChartDataFromAddress(valueAddress, sheetNameMap, context);
1758
+ if (seriesData) {
1759
+ const label = this.extractSeriesLabel(seriesNode);
1760
+ seriesData.dataLabel = label || `Series ${seriesIndex}`;
1761
+ const color = this.extractChartSeriesColor(seriesNode);
1762
+ if (color) {
1763
+ seriesData.color = color;
1764
+ }
1765
+ series.push(seriesData);
1766
+ seriesIndex++;
1767
+ }
1768
+ }
1769
+ }
1770
+ if (!series.length) {
1771
+ return undefined;
1772
+ }
1773
+ const finalizedDomains = domains.length ? domains : [series[0]];
1774
+ return {
1775
+ chartType,
1776
+ title,
1777
+ domains: finalizedDomains,
1778
+ series,
1779
+ };
1780
+ }
1781
+ extractSeriesLabel(seriesNode) {
1782
+ if (!seriesNode)
1783
+ return undefined;
1784
+ const labelNode = get(seriesNode, "chart:label") ||
1785
+ get(seriesNode, "label") ||
1786
+ get(seriesNode, "chart:series-label");
1787
+ const label = labelNode ? this.extractTextContent(labelNode) : undefined;
1788
+ return label?.trim() || undefined;
1789
+ }
1790
+ extractChartSeriesColor(seriesNode) {
1791
+ if (!seriesNode)
1792
+ return undefined;
1793
+ const attrs = this.getAttributes(seriesNode);
1794
+ const styleCandidates = [
1795
+ attrs?.["@_chart:style-name"],
1796
+ attrs?.["@_chart:data-style-name"],
1797
+ attrs?.["@_style-name"],
1798
+ ];
1799
+ for (const candidate of styleCandidates) {
1800
+ const color = this.odsStyles.getChartStyleColor(candidate);
1801
+ if (color) {
1802
+ return typeof color === "string" ? color : undefined;
1803
+ }
1804
+ }
1805
+ const seriesProps = get(seriesNode, "chart:data-point-properties") ||
1806
+ get(seriesNode, "chart:series-properties");
1807
+ const inlineColor = this.extractColorFromChartProperties(seriesProps);
1808
+ if (inlineColor) {
1809
+ return inlineColor;
1810
+ }
1811
+ const dataPoints = this.toArray(get(seriesNode, "chart:data-point") ||
1812
+ get(seriesNode, "chart:data-points") ||
1813
+ get(seriesNode, "data-point"));
1814
+ for (const dataPoint of dataPoints) {
1815
+ const pointAttrs = this.getAttributes(dataPoint);
1816
+ const pointStyleCandidates = [
1817
+ pointAttrs?.["@_chart:style-name"],
1818
+ pointAttrs?.["@_chart:data-style-name"],
1819
+ pointAttrs?.["@_style-name"],
1820
+ ];
1821
+ for (const candidate of pointStyleCandidates) {
1822
+ const color = this.odsStyles.getChartStyleColor(candidate);
1823
+ if (color)
1824
+ return typeof color === "string" ? color : undefined;
1825
+ }
1826
+ const pointColor = this.extractColorFromChartProperties(get(dataPoint, "chart:data-point-properties") || dataPoint);
1827
+ if (pointColor)
1828
+ return pointColor;
1829
+ }
1830
+ return undefined;
1831
+ }
1832
+ extractColorFromChartProperties(node) {
1833
+ if (!node)
1834
+ return undefined;
1835
+ const attrs = this.getAttributes(node);
1836
+ if (!attrs)
1837
+ return undefined;
1838
+ const rawValue = attrs["@_draw:fill-color"] ||
1839
+ attrs["@_svg:fill-color"] ||
1840
+ attrs["@_chart:color"] ||
1841
+ attrs["@_fo:background-color"] ||
1842
+ attrs["@_style:background-color"];
1843
+ if (!rawValue)
1844
+ return undefined;
1845
+ const normalized = this.normalizeOdsColor(rawValue);
1846
+ if (normalized &&
1847
+ normalized.toLowerCase() !== "none" &&
1848
+ normalized.toLowerCase() !== "transparent") {
1849
+ return normalized;
1850
+ }
1851
+ return undefined;
1852
+ }
1853
+ mapOdsChartClassToType(value) {
1854
+ const normalized = value?.toLowerCase() || "";
1855
+ if (normalized.includes("line") || normalized.includes("scatter")) {
1856
+ return "line";
1857
+ }
1858
+ if (normalized.includes("pie")) {
1859
+ return "pie";
1860
+ }
1861
+ if (normalized.includes("area")) {
1862
+ return "area";
1863
+ }
1864
+ return "bar";
1865
+ }
1866
+ findRangeString(node, skipKeys) {
1867
+ if (!node)
1868
+ return undefined;
1869
+ const stack = [node];
1870
+ while (stack.length) {
1871
+ const current = stack.shift();
1872
+ if (current === undefined || current === null)
1873
+ continue;
1874
+ if (typeof current === "string") {
1875
+ if (this.looksLikeCellRangeAddress(current)) {
1876
+ return current;
1877
+ }
1878
+ continue;
1879
+ }
1880
+ if (Array.isArray(current)) {
1881
+ stack.unshift(...current);
1882
+ continue;
1883
+ }
1884
+ if (typeof current === "object") {
1885
+ const attrs = current[":@"] || current;
1886
+ for (const [key, attrValue] of Object.entries(attrs)) {
1887
+ if (key.startsWith("@_") &&
1888
+ typeof attrValue === "string" &&
1889
+ this.looksLikeCellRangeAddress(attrValue)) {
1890
+ return attrValue;
1891
+ }
1892
+ }
1893
+ for (const [childKey, childValue] of Object.entries(current)) {
1894
+ if (childKey === ":@")
1895
+ continue;
1896
+ if (skipKeys && skipKeys.has(childKey))
1897
+ continue;
1898
+ stack.push(childValue);
1899
+ }
1900
+ }
1901
+ }
1902
+ return undefined;
1903
+ }
1904
+ looksLikeCellRangeAddress(value) {
1905
+ if (!value)
1906
+ return false;
1907
+ const trimmed = value.trim();
1908
+ if (!trimmed)
1909
+ return false;
1910
+ if (trimmed.includes("#REF"))
1911
+ return false;
1912
+ if (!/[A-Za-z]/.test(trimmed) || !/\d/.test(trimmed))
1913
+ return false;
1914
+ return trimmed.includes(".") || trimmed.includes(":");
1915
+ }
1916
+ createChartDataFromAddress(address, sheetNameMap, context) {
1917
+ if (!address)
1918
+ return undefined;
1919
+ let ranges = this.parseRangeList(address, sheetNameMap, context);
1920
+ if (!ranges.length && context?.name) {
1921
+ const augmented = `'${context.name}'.${address}`;
1922
+ ranges = this.parseRangeList(augmented, sheetNameMap, context);
1923
+ }
1924
+ if (!ranges.length)
1925
+ return undefined;
1926
+ return {
1927
+ sources: ranges,
1928
+ };
1929
+ }
1930
+ buildOverlayPlacement(frameAttrs, cellAttrs, rowIndex, columnIndex) {
1931
+ const widthPixels = this.convertLengthToPixels(frameAttrs?.["@_svg:width"] ||
1932
+ frameAttrs?.["@_width"] ||
1933
+ cellAttrs?.["@_svg:width"] ||
1934
+ cellAttrs?.["@_width"]);
1935
+ const heightPixels = this.convertLengthToPixels(frameAttrs?.["@_svg:height"] ||
1936
+ frameAttrs?.["@_height"] ||
1937
+ cellAttrs?.["@_svg:height"] ||
1938
+ cellAttrs?.["@_height"]);
1939
+ const offsetXPixels = this.convertLengthToPixels(frameAttrs?.["@_svg:x"] ||
1940
+ frameAttrs?.["@_x"] ||
1941
+ cellAttrs?.["@_svg:x"] ||
1942
+ cellAttrs?.["@_x"]);
1943
+ const offsetYPixels = this.convertLengthToPixels(frameAttrs?.["@_svg:y"] ||
1944
+ frameAttrs?.["@_y"] ||
1945
+ cellAttrs?.["@_svg:y"] ||
1946
+ cellAttrs?.["@_y"]);
1947
+ const overlayPosition = {
1948
+ anchorCell: { rowIndex, columnIndex },
1949
+ };
1950
+ if (typeof widthPixels === "number") {
1951
+ overlayPosition.widthPixels = widthPixels;
1952
+ }
1953
+ if (typeof heightPixels === "number") {
1954
+ overlayPosition.heightPixels = heightPixels;
1955
+ }
1956
+ if (typeof offsetXPixels === "number") {
1957
+ overlayPosition.offsetXPixels = offsetXPixels;
1958
+ }
1959
+ if (typeof offsetYPixels === "number") {
1960
+ overlayPosition.offsetYPixels = offsetYPixels;
1961
+ }
1962
+ const DEFAULT_CELL_IMAGE_MAX_SIZE = 60;
1963
+ const exceedsCellSize = (typeof widthPixels === "number" &&
1964
+ widthPixels > DEFAULT_CELL_IMAGE_MAX_SIZE) ||
1965
+ (typeof heightPixels === "number" &&
1966
+ heightPixels > DEFAULT_CELL_IMAGE_MAX_SIZE);
1967
+ const isInsideCell = !exceedsCellSize &&
1968
+ (offsetXPixels === undefined || Math.abs(offsetXPixels) < 0.01) &&
1969
+ (offsetYPixels === undefined || Math.abs(offsetYPixels) < 0.01);
1970
+ return { overlayPosition, isInsideCell };
1971
+ }
1972
+ getImageDataUrl(imagePath, cache) {
1973
+ if (cache.has(imagePath)) {
1974
+ return cache.get(imagePath);
1975
+ }
1976
+ try {
1977
+ const binary = this.zipParser.GetBinary(imagePath);
1978
+ const dataUrl = this.createDataUrlFromBytes(binary, this.getImageMimeType(imagePath));
1979
+ cache.set(imagePath, dataUrl);
1980
+ return dataUrl;
1981
+ }
1982
+ catch {
1983
+ return undefined;
1984
+ }
1985
+ }
1986
+ normalizeImagePath(href) {
1987
+ if (!href)
1988
+ return undefined;
1989
+ let normalized = href.trim();
1990
+ if (!normalized || normalized.startsWith("#"))
1991
+ return undefined;
1992
+ normalized = normalized.replace(/^\.\/+/, "");
1993
+ while (normalized.startsWith("../")) {
1994
+ normalized = normalized.slice(3);
1995
+ }
1996
+ normalized = normalized.replace(/^\/+/, "");
1997
+ return normalized;
1998
+ }
1999
+ getImageMimeType(path) {
2000
+ const extension = path.split(".").pop()?.toLowerCase();
2001
+ switch (extension) {
2002
+ case "jpg":
2003
+ case "jpeg":
2004
+ return "image/jpeg";
2005
+ case "gif":
2006
+ return "image/gif";
2007
+ case "bmp":
2008
+ return "image/bmp";
2009
+ case "svg":
2010
+ return "image/svg+xml";
2011
+ case "webp":
2012
+ return "image/webp";
2013
+ default:
2014
+ return "image/png";
2015
+ }
2016
+ }
2017
+ createDataUrlFromBytes(data, mimeType) {
2018
+ const base64 = this.bytesToBase64(data);
2019
+ return `data:${mimeType};base64,${base64}`;
2020
+ }
2021
+ bytesToBase64(data) {
2022
+ const globalRef = typeof globalThis !== "undefined" ? globalThis : undefined;
2023
+ if (globalRef?.Buffer) {
2024
+ return globalRef.Buffer.from(data).toString("base64");
2025
+ }
2026
+ const chunkSize = 0x8000;
2027
+ let binary = "";
2028
+ for (let i = 0; i < data.length; i += chunkSize) {
2029
+ const chunk = data.subarray(i, i + chunkSize);
2030
+ binary += String.fromCharCode(...Array.from(chunk));
2031
+ }
2032
+ if (typeof btoa === "function") {
2033
+ return btoa(binary);
2034
+ }
2035
+ if (globalRef?.btoa) {
2036
+ return globalRef.btoa(binary);
2037
+ }
2038
+ throw new Error("Unable to encode image data");
2039
+ }
2040
+ convertLengthToPixels(value) {
2041
+ if (!value)
2042
+ return undefined;
2043
+ const match = value
2044
+ .toString()
2045
+ .trim()
2046
+ .match(/^(-?\d+(?:\.\d+)?)(cm|mm|in|pt|pc|px)?$/i);
2047
+ if (!match)
2048
+ return undefined;
2049
+ const amount = parseFloat(match[1]);
2050
+ const unit = (match[2] || "px").toLowerCase();
2051
+ const unitMap = {
2052
+ px: 1,
2053
+ cm: 37.7952755906,
2054
+ mm: 3.77952755906,
2055
+ in: 96,
2056
+ pt: 96 / 72,
2057
+ pc: 16,
2058
+ };
2059
+ const factor = unitMap[unit] ?? 1;
2060
+ return amount * factor;
2061
+ }
2062
+ normalizeOdsColor(value) {
2063
+ if (!value)
2064
+ return undefined;
2065
+ const trimmed = value.trim();
2066
+ if (!trimmed)
2067
+ return undefined;
2068
+ if (trimmed.startsWith("#")) {
2069
+ return trimmed.toUpperCase();
2070
+ }
2071
+ if (/^\d+$/.test(trimmed)) {
2072
+ const numeric = parseInt(trimmed, 10);
2073
+ if (!isNaN(numeric)) {
2074
+ return `#${(numeric & 0xffffff)
2075
+ .toString(16)
2076
+ .padStart(6, "0")
2077
+ .toUpperCase()}`;
2078
+ }
2079
+ }
2080
+ return trimmed;
2081
+ }
2082
+ parseIntegerValue(value) {
2083
+ if (value === undefined || value === null)
2084
+ return undefined;
2085
+ const parsed = parseInt(value, 10);
2086
+ return isNaN(parsed) ? undefined : parsed;
2087
+ }
2088
+ getNodeText(node) {
2089
+ if (node === undefined || node === null)
2090
+ return undefined;
2091
+ if (typeof node === "string")
2092
+ return node;
2093
+ if (typeof node["#text"] === "string")
2094
+ return node["#text"];
2095
+ if (typeof node["_text"] === "string")
2096
+ return node["_text"];
2097
+ return undefined;
2098
+ }
2099
+ buildSheetNameMap(sheetContexts) {
2100
+ const map = new Map();
2101
+ for (const context of sheetContexts) {
2102
+ this.registerSheetNameVariants(map, context.name, context.sheetId);
2103
+ }
2104
+ return map;
2105
+ }
2106
+ registerSheetNameVariants(map, name, sheetId) {
2107
+ if (!name)
2108
+ return;
2109
+ map.set(name, sheetId);
2110
+ const normalized = this.normalizeSheetName(name);
2111
+ if (normalized && normalized !== name) {
2112
+ map.set(normalized, sheetId);
2113
+ }
2114
+ if (normalized) {
2115
+ map.set(`'${normalized}'`, sheetId);
2116
+ }
2117
+ }
2118
+ normalizeSheetName(name) {
2119
+ if (!name)
2120
+ return undefined;
2121
+ let normalized = name.trim();
2122
+ normalized = normalized.replace(/^'+|'+$/g, "");
2123
+ normalized = normalized.replace(/^"+|"+$/g, "");
2124
+ normalized = normalized.replace(/^\$+/, "");
2125
+ return normalized;
2126
+ }
2127
+ parseRangeList(addresses, sheetNameMap, context) {
2128
+ if (!addresses)
2129
+ return [];
2130
+ const segments = this.splitAddressSegments(addresses);
2131
+ const ranges = [];
2132
+ for (const segment of segments) {
2133
+ const range = this.parseRange(segment, sheetNameMap, context);
2134
+ if (range) {
2135
+ ranges.push(range);
2136
+ }
2137
+ }
2138
+ return ranges;
2139
+ }
2140
+ splitAddressSegments(addresses) {
2141
+ const segments = [];
2142
+ let buffer = "";
2143
+ let inQuotes = false;
2144
+ for (let i = 0; i < addresses.length; i++) {
2145
+ const char = addresses[i];
2146
+ if (char === "'") {
2147
+ if (inQuotes && addresses[i + 1] === "'") {
2148
+ buffer += "''";
2149
+ i++;
2150
+ continue;
2151
+ }
2152
+ inQuotes = !inQuotes;
2153
+ buffer += char;
2154
+ continue;
2155
+ }
2156
+ if (!inQuotes && (char === "," || /\s/.test(char))) {
2157
+ if (buffer.trim()) {
2158
+ segments.push(buffer.trim());
2159
+ buffer = "";
2160
+ }
2161
+ continue;
2162
+ }
2163
+ buffer += char;
2164
+ }
2165
+ if (buffer.trim()) {
2166
+ segments.push(buffer.trim());
2167
+ }
2168
+ return segments;
2169
+ }
2170
+ parseRange(address, sheetNameMap, context) {
2171
+ if (!address)
2172
+ return undefined;
2173
+ const normalized = address.replace(/\$/g, "");
2174
+ const [startRef, endRef] = normalized.split(":");
2175
+ const start = this.parseCellReference(startRef, sheetNameMap, context);
2176
+ if (!start)
2177
+ return undefined;
2178
+ const end = this.parseCellReference(endRef ?? startRef, sheetNameMap, context) ||
2179
+ start;
2180
+ const sheetId = end.sheetId ?? start.sheetId ?? context?.sheetId;
2181
+ if (!sheetId)
2182
+ return undefined;
2183
+ return {
2184
+ sheetId,
2185
+ startRowIndex: Math.min(start.rowIndex, end.rowIndex),
2186
+ endRowIndex: Math.max(start.rowIndex, end.rowIndex),
2187
+ startColumnIndex: Math.min(start.columnIndex, end.columnIndex),
2188
+ endColumnIndex: Math.max(start.columnIndex, end.columnIndex),
2189
+ };
2190
+ }
2191
+ parseCellReference(reference, sheetNameMap, context) {
2192
+ if (!reference)
2193
+ return undefined;
2194
+ let ref = reference.trim();
2195
+ if (!ref)
2196
+ return undefined;
2197
+ ref = ref.replace(/\$/g, "");
2198
+ let sheetName;
2199
+ let cellPart = ref;
2200
+ const dotMatch = ref.match(/^(.*)[.!]([A-Za-z]+\d+)$/);
2201
+ if (dotMatch) {
2202
+ sheetName = dotMatch[1];
2203
+ cellPart = dotMatch[2];
2204
+ }
2205
+ const bangMatch = ref.match(/^(.*)!([A-Za-z]+\d+)$/);
2206
+ if (!sheetName && bangMatch) {
2207
+ sheetName = bangMatch[1];
2208
+ cellPart = bangMatch[2];
2209
+ }
2210
+ const quotedMatch = ref.match(/^'([^']+)'\.([A-Za-z]+\d+)$/);
2211
+ if (!sheetName && quotedMatch) {
2212
+ sheetName = quotedMatch[1];
2213
+ cellPart = quotedMatch[2];
2214
+ }
2215
+ const cell = addressToCell(cellPart.toUpperCase());
2216
+ if (!cell)
2217
+ return undefined;
2218
+ const normalizedSheet = sheetName
2219
+ ? this.normalizeSheetName(sheetName)
2220
+ : undefined;
2221
+ const sheetId = (normalizedSheet && sheetNameMap.get(normalizedSheet)) ??
2222
+ (sheetName && sheetNameMap.get(sheetName)) ??
2223
+ context?.sheetId;
2224
+ return {
2225
+ rowIndex: cell.rowIndex,
2226
+ columnIndex: cell.columnIndex,
2227
+ sheetId,
2228
+ };
2229
+ }
2230
+ parseBooleanAttribute(value) {
2231
+ if (value === undefined || value === null)
2232
+ return undefined;
2233
+ if (typeof value === "boolean")
2234
+ return value;
2235
+ const normalized = value.toString().toLowerCase();
2236
+ if (normalized === "true" || normalized === "1")
2237
+ return true;
2238
+ if (normalized === "false" || normalized === "0")
2239
+ return false;
2240
+ return undefined;
2241
+ }
2242
+ normalizeConditionExpression(expression) {
2243
+ if (!expression)
2244
+ return undefined;
2245
+ let normalized = expression.trim();
2246
+ if (!normalized)
2247
+ return undefined;
2248
+ if (normalized.startsWith("of:=")) {
2249
+ normalized = normalized.slice(4);
2250
+ }
2251
+ else if (normalized.startsWith("of:")) {
2252
+ normalized = normalized.slice(3);
2253
+ }
2254
+ if (!normalized.startsWith("=")) {
2255
+ normalized = `=${normalized}`;
2256
+ }
2257
+ return normalized;
2258
+ }
2259
+ normalizeExpression(expression) {
2260
+ if (!expression)
2261
+ return undefined;
2262
+ let normalized = expression.trim();
2263
+ if (!normalized)
2264
+ return undefined;
2265
+ normalized = convertOdsFormulaToExcel(normalized);
2266
+ normalized = normalized.trim();
2267
+ if (!normalized)
2268
+ return undefined;
2269
+ return normalized.startsWith("=") ? normalized : `=${normalized}`;
2270
+ }
2271
+ toArray(value) {
2272
+ if (!value)
2273
+ return [];
2274
+ return Array.isArray(value) ? value : [value];
2275
+ }
2276
+ expandRowCells(rowChildren) {
2277
+ if (!rowChildren) {
2278
+ return [];
2279
+ }
2280
+ const entries = Array.isArray(rowChildren) ? rowChildren : [rowChildren];
2281
+ const normalized = [];
2282
+ for (const entry of entries) {
2283
+ if (!entry)
2284
+ continue;
2285
+ if (entry["table:table-cell"] || entry["table:covered-table-cell"]) {
2286
+ normalized.push(entry);
2287
+ continue;
2288
+ }
2289
+ const normalizedEntry = this.wrapRawCellEntry(entry);
2290
+ if (normalizedEntry) {
2291
+ normalized.push(normalizedEntry);
2292
+ }
2293
+ }
2294
+ return normalized;
2295
+ }
2296
+ wrapRawCellEntry(entry) {
2297
+ if (entry === undefined || entry === null)
2298
+ return undefined;
2299
+ const { attributes, childNodes } = this.extractAttributesFromNode(entry);
2300
+ const wrapper = {
2301
+ "table:table-cell": childNodes.length ? childNodes : [],
2302
+ };
2303
+ if (Object.keys(attributes).length) {
2304
+ wrapper[":@"] = attributes;
2305
+ }
2306
+ return wrapper;
2307
+ }
2308
+ extractAttributesFromNode(node) {
2309
+ const attributes = {};
2310
+ const childNodes = [];
2311
+ if (node === undefined || node === null) {
2312
+ return { attributes, childNodes };
2313
+ }
2314
+ if (typeof node !== "object" || Array.isArray(node)) {
2315
+ childNodes.push(typeof node === "object" ? node : { "#text": node ?? "" });
2316
+ return { attributes, childNodes };
2317
+ }
2318
+ for (const [key, value] of Object.entries(node)) {
2319
+ if (key === ":@") {
2320
+ if (value && typeof value === "object") {
2321
+ Object.assign(attributes, value);
2322
+ }
2323
+ continue;
2324
+ }
2325
+ if (key.startsWith("@_")) {
2326
+ attributes[key] = value;
2327
+ continue;
2328
+ }
2329
+ childNodes.push({ [key]: value });
2330
+ }
2331
+ return { attributes, childNodes };
2332
+ }
2333
+ getAttributes(node) {
2334
+ if (!node)
2335
+ return undefined;
2336
+ if (Array.isArray(node)) {
2337
+ return node.length ? this.getAttributes(node[0]) : undefined;
2338
+ }
2339
+ return node[":@"] || node;
2340
+ }
2341
+ mapAlertStyle(value) {
2342
+ if (!value)
2343
+ return undefined;
2344
+ const normalized = value.toLowerCase();
2345
+ if (normalized.includes("warn"))
2346
+ return "warning";
2347
+ if (normalized.includes("info"))
2348
+ return "information";
2349
+ return "stop";
2350
+ }
2351
+ async getSheets(minRowCount = 0, minColumnCount = 0) {
2352
+ const cache = this.ensureSheetCache();
2353
+ if (!minRowCount && !minColumnCount) {
2354
+ return [cache.sheets, cache.tables];
2355
+ }
2356
+ const sheets = cache.sheets.map((sheet) => {
2357
+ const rowCount = Math.max(sheet.rowCount ?? 0, minRowCount);
2358
+ const columnCount = Math.max(sheet.columnCount ?? 0, minColumnCount);
2359
+ if (rowCount === sheet.rowCount && columnCount === sheet.columnCount) {
2360
+ return sheet;
2361
+ }
2362
+ return {
2363
+ ...sheet,
2364
+ rowCount,
2365
+ columnCount,
2366
+ };
2367
+ });
2368
+ return [sheets, cache.tables];
2369
+ }
2370
+ async getConditionalFormatting() {
2371
+ return this.ensureSheetCache().conditionalFormatting;
2372
+ }
2373
+ async getDrawings() {
2374
+ return this.ensureSheetCache().drawings;
2375
+ }
2376
+ async getCharts() {
2377
+ return this.ensureSheetCache().charts;
2378
+ }
2379
+ async getDataValidations() {
2380
+ return this.ensureSheetCache().dataValidations;
2381
+ }
2382
+ getNamedRanges() {
2383
+ return this.ensureSheetCache().namedRanges;
2384
+ }
2385
+ findCalculationSettings(node) {
2386
+ if (!node)
2387
+ return undefined;
2388
+ if (Array.isArray(node)) {
2389
+ for (const child of node) {
2390
+ const found = this.findCalculationSettings(child);
2391
+ if (found)
2392
+ return found;
2393
+ }
2394
+ return undefined;
2395
+ }
2396
+ if (node["table:calculation-settings"]) {
2397
+ const settingsNode = node["table:calculation-settings"];
2398
+ if (Array.isArray(settingsNode)) {
2399
+ return settingsNode[0];
2400
+ }
2401
+ return settingsNode;
2402
+ }
2403
+ return (this.findCalculationSettings(node["office:spreadsheet"]) ||
2404
+ this.findCalculationSettings(node["office:body"]) ||
2405
+ this.findCalculationSettings(node["office:document-content"]));
2406
+ }
2407
+ getIterativeCalculationOptions() {
2408
+ try {
2409
+ const calcSettings = this.findCalculationSettings(this.contentXml) ??
2410
+ this.findCalculationSettings(this.contentXmlOrdered);
2411
+ if (!calcSettings)
2412
+ return undefined;
2413
+ const enabledAttr = getAttribute(calcSettings, "table:use-iteration");
2414
+ const maxIterationsAttr = getAttribute(calcSettings, "table:iteration-count");
2415
+ const maxChangeAttr = getAttribute(calcSettings, "table:minimum-step");
2416
+ if (enabledAttr === undefined &&
2417
+ maxIterationsAttr === undefined &&
2418
+ maxChangeAttr === undefined) {
2419
+ return undefined;
2420
+ }
2421
+ const enabled = enabledAttr === "true" || enabledAttr === true || enabledAttr === "1";
2422
+ const options = { enabled };
2423
+ if (maxIterationsAttr !== undefined) {
2424
+ const parsed = Number(maxIterationsAttr);
2425
+ if (!Number.isNaN(parsed)) {
2426
+ options.maxIterations = parsed;
2427
+ }
2428
+ }
2429
+ if (maxChangeAttr !== undefined) {
2430
+ const parsed = Number(maxChangeAttr);
2431
+ if (!Number.isNaN(parsed)) {
2432
+ options.maxChange = parsed;
2433
+ }
2434
+ }
2435
+ return options;
2436
+ }
2437
+ catch {
2438
+ return undefined;
2439
+ }
2440
+ }
2441
+ async *processSheetData(chunkSize = 100, enableCellXfsRegistry = false, enableSharedStrings = true) {
2442
+ try {
2443
+ if (!this.contentXmlOrdered)
2444
+ return;
2445
+ const docContent = this.contentXmlOrdered[0]?.["office:document-content"] ||
2446
+ this.contentXmlOrdered[0]?.["document-content"];
2447
+ if (!docContent)
2448
+ return;
2449
+ const body = getChildren(docContent, "office:body") ||
2450
+ getChildren(docContent, "body");
2451
+ if (!body)
2452
+ return;
2453
+ const spreadsheet = getChildren(body, "office:spreadsheet") ||
2454
+ getChildren(body, "spreadsheet");
2455
+ if (!spreadsheet)
2456
+ return;
2457
+ const tables = spreadsheet.filter((child) => child["table:table"] || child["table"]);
2458
+ const totalSheets = tables.length;
2459
+ for (let currentSheetIndex = 0; currentSheetIndex < totalSheets; currentSheetIndex++) {
2460
+ const tableWrapper = tables[currentSheetIndex];
2461
+ const tableNameKey = tableWrapper["table:table"]
2462
+ ? "table:table"
2463
+ : "table";
2464
+ const tableChildren = tableWrapper[tableNameKey];
2465
+ const tableAttrs = tableWrapper[":@"] || {};
2466
+ const name = tableAttrs["@_table:name"] ||
2467
+ tableAttrs["@_name"] ||
2468
+ `Sheet${currentSheetIndex + 1}`;
2469
+ const sheetId = currentSheetIndex + 1; // 1-based ID
2470
+ const tableStyleName = tableAttrs["@_table:style-name"] || tableAttrs["@_style-name"];
2471
+ const tableCellFormat = tableStyleName
2472
+ ? this.odsStyles.getTableCellFormat(tableStyleName)
2473
+ : undefined;
2474
+ // Get rows
2475
+ const rows = tableChildren.filter((child) => child["table:table-row"] || child["table-row"]);
2476
+ const columnWrappers = tableChildren.filter((child) => child["table:table-column"] || child["table-column"]);
2477
+ const columnFormats = new Map();
2478
+ let formattedColumnIndex = 1;
2479
+ for (const columnWrapper of columnWrappers) {
2480
+ const columnAttrs = columnWrapper[":@"] || {};
2481
+ const colsRepeated = parseInt(columnAttrs["@_table:number-columns-repeated"] ||
2482
+ columnAttrs["@_number-columns-repeated"] ||
2483
+ "1", 10);
2484
+ const columnStyleName = columnAttrs["@_table:style-name"] || columnAttrs["@_style-name"];
2485
+ const columnStyle = columnStyleName
2486
+ ? this.odsStyles.getColumnStyle(columnStyleName)
2487
+ : undefined;
2488
+ const columnCellFormat = columnStyle?.format;
2489
+ for (let col = 0; col < colsRepeated; col++) {
2490
+ if (columnCellFormat) {
2491
+ columnFormats.set(formattedColumnIndex, columnCellFormat);
2492
+ }
2493
+ formattedColumnIndex++;
2494
+ }
2495
+ }
2496
+ let chunkIndex = 0;
2497
+ let currentChunk = [];
2498
+ let currentChunkRowIndices = [];
2499
+ let currentRowIndex = 1;
2500
+ // Stamp `cellData.dataValidation` refs while rows stream (see
2501
+ // dv-cell-stamp.ts) — hosts resolve a cell's rule only through the
2502
+ // per-cell reference. Same cached parse as `getDataValidations()`,
2503
+ // so the stamped ids match the rule list consumers persist.
2504
+ const dvStamper = new SheetDataValidationStamper(await this.getDataValidations(), sheetId);
2505
+ for (const rowWrapper of rows) {
2506
+ const rowNameKey = rowWrapper["table:table-row"]
2507
+ ? "table:table-row"
2508
+ : "table-row";
2509
+ const rowChildren = rowWrapper[rowNameKey];
2510
+ const rowAttrs = rowWrapper[":@"] || {};
2511
+ const rowsRepeated = parseInt(rowAttrs["@_table:number-rows-repeated"] ||
2512
+ rowAttrs["@_number-rows-repeated"] ||
2513
+ "1");
2514
+ const rowStyleName = rowAttrs["@_table:style-name"] || rowAttrs["@_style-name"];
2515
+ const rowStyle = rowStyleName
2516
+ ? this.odsStyles.getRowStyle(rowStyleName)
2517
+ : undefined;
2518
+ const rowCellFormat = rowStyle?.format;
2519
+ const rowBaseFormat = fastMergeCellFormats(tableCellFormat, rowCellFormat);
2520
+ const effectiveRepeats = Math.min(rowsRepeated, 10000); // Cap to avoid infinite loops on empty sheets
2521
+ for (let r = 0; r < effectiveRepeats; r++) {
2522
+ const rowData = {
2523
+ values: [],
2524
+ };
2525
+ let columnIndex = 1;
2526
+ const normalizedRowChildren = rowChildren
2527
+ ? this.expandRowCells(rowChildren)
2528
+ : null;
2529
+ if (normalizedRowChildren) {
2530
+ for (const cellWrapper of normalizedRowChildren) {
2531
+ const isCell = !!cellWrapper["table:table-cell"];
2532
+ const isCovered = !!cellWrapper["table:covered-table-cell"];
2533
+ if (!isCell && !isCovered)
2534
+ continue;
2535
+ const cellNameKey = isCell
2536
+ ? "table:table-cell"
2537
+ : "table:covered-table-cell";
2538
+ const cellChildren = cellWrapper[cellNameKey];
2539
+ const cellAttrs = cellWrapper[":@"] || {};
2540
+ const colsRepeated = parseInt(cellAttrs["@_table:number-columns-repeated"] ||
2541
+ cellAttrs["@_number-columns-repeated"] ||
2542
+ "1");
2543
+ if (isCovered) {
2544
+ columnIndex += colsRepeated;
2545
+ continue;
2546
+ }
2547
+ // Extract value
2548
+ const valueType = cellAttrs["@_office:value-type"] || cellAttrs["@_value-type"];
2549
+ let cellValue = null;
2550
+ let displayValue = "";
2551
+ let textFormatRuns;
2552
+ // Get text content
2553
+ const textPWrapper = findChild(cellChildren, "text:p") ||
2554
+ findChild(cellChildren, "p");
2555
+ if (textPWrapper) {
2556
+ const textPKey = textPWrapper["text:p"] ? "text:p" : "p";
2557
+ const richText = this.extractTextContentWithRuns(textPWrapper[textPKey]);
2558
+ displayValue = richText.text;
2559
+ if (richText.runs.length > 0) {
2560
+ textFormatRuns = richText.runs;
2561
+ }
2562
+ }
2563
+ const hyperlinkValue = this.extractHyperlinkValue(cellChildren);
2564
+ if (valueType === "float" ||
2565
+ valueType === "currency" ||
2566
+ valueType === "percentage") {
2567
+ cellValue = parseFloat(cellAttrs["@_office:value"] || cellAttrs["@_value"]);
2568
+ }
2569
+ else if (valueType === "date") {
2570
+ cellValue =
2571
+ cellAttrs["@_office:date-value"] ||
2572
+ cellAttrs["@_date-value"];
2573
+ }
2574
+ else if (valueType === "string") {
2575
+ cellValue =
2576
+ cellAttrs["@_office:string-value"] ||
2577
+ cellAttrs["@_string-value"] ||
2578
+ displayValue;
2579
+ }
2580
+ else {
2581
+ cellValue = displayValue;
2582
+ }
2583
+ const cellData = {};
2584
+ if (hyperlinkValue) {
2585
+ cellData.hyperlink = hyperlinkValue;
2586
+ }
2587
+ if (cellValue !== null && cellValue !== "") {
2588
+ const formula = cellAttrs["@_table:formula"] || cellAttrs["@_formula"];
2589
+ const isSharedString = enableSharedStrings &&
2590
+ !formula &&
2591
+ valueType === "string" &&
2592
+ typeof cellValue === "string";
2593
+ if (isSharedString) {
2594
+ cellData.ss = String(this.getOrCreateSharedStringIndex(String(cellValue), textFormatRuns));
2595
+ }
2596
+ else {
2597
+ if (valueType === "float" ||
2598
+ valueType === "currency" ||
2599
+ valueType === "percentage") {
2600
+ cellData.ev = { nv: cellValue };
2601
+ }
2602
+ else {
2603
+ cellData.ev = { sv: String(cellValue) };
2604
+ }
2605
+ if (formula) {
2606
+ const excelFormula = convertOdsFormulaToExcel(formula);
2607
+ cellData.ue = { fv: excelFormula };
2608
+ // Array (matrix) formula anchor: ODF marks the spill
2609
+ // shape with table:number-matrix-{columns,rows}-spanned.
2610
+ // Convert to A1 spill ref so xlsx export can emit
2611
+ // <f t="array" ref="..."/>. Spilled cells in the matrix
2612
+ // appear as ordinary table cells without `table:formula`
2613
+ // and so don't reach this branch.
2614
+ const matrixCols = parseInt(cellAttrs["@_table:number-matrix-columns-spanned"] ||
2615
+ cellAttrs["@_number-matrix-columns-spanned"] ||
2616
+ "0");
2617
+ const matrixRows = parseInt(cellAttrs["@_table:number-matrix-rows-spanned"] ||
2618
+ cellAttrs["@_number-matrix-rows-spanned"] ||
2619
+ "0");
2620
+ if (matrixCols > 0 || matrixRows > 0) {
2621
+ const cols = Math.max(matrixCols, 1);
2622
+ const rows = Math.max(matrixRows, 1);
2623
+ const start = cellToAddress({
2624
+ rowIndex: currentRowIndex,
2625
+ columnIndex,
2626
+ });
2627
+ const end = cellToAddress({
2628
+ rowIndex: currentRowIndex + rows - 1,
2629
+ columnIndex: columnIndex + cols - 1,
2630
+ });
2631
+ if (start && end) {
2632
+ cellData.af = `${start}:${end}`;
2633
+ }
2634
+ }
2635
+ }
2636
+ else {
2637
+ cellData.ue = cellData.ev;
2638
+ }
2639
+ // Skip persisted `fv` for formula cells — renderer
2640
+ // derives the formatted value at paint time.
2641
+ if (!formula) {
2642
+ cellData.fv = displayValue;
2643
+ }
2644
+ }
2645
+ }
2646
+ const cellStyleName = cellAttrs["@_table:style-name"] || cellAttrs["@_style-name"];
2647
+ const cellFormat = cellStyleName
2648
+ ? this.odsStyles.getStyle(cellStyleName)
2649
+ : undefined;
2650
+ const hasCellValue = Object.keys(cellData).length > 0;
2651
+ // Fill repeated columns
2652
+ for (let c = 0; c < colsRepeated; c++) {
2653
+ const targetColumnIndex = columnIndex;
2654
+ let mergedFormat = fastMergeCellFormats(rowBaseFormat, columnFormats.get(targetColumnIndex), cellFormat);
2655
+ if (mergedFormat) {
2656
+ if (mergedFormat.__alignValueType) {
2657
+ const alignment = valueType === "float" ||
2658
+ valueType === "currency" ||
2659
+ valueType === "percentage"
2660
+ ? "right"
2661
+ : "left";
2662
+ mergedFormat.horizontalAlignment = alignment;
2663
+ delete mergedFormat.__alignValueType;
2664
+ }
2665
+ }
2666
+ const needsDefaultRightAlign = (valueType === "float" ||
2667
+ valueType === "currency" ||
2668
+ valueType === "percentage" ||
2669
+ valueType === "date") &&
2670
+ (!mergedFormat || !mergedFormat.horizontalAlignment);
2671
+ if (needsDefaultRightAlign) {
2672
+ mergedFormat = mergedFormat
2673
+ ? { ...mergedFormat }
2674
+ : {};
2675
+ mergedFormat.horizontalAlignment = "right";
2676
+ }
2677
+ const displaySuggestsCurrency = typeof displayValue === "string" &&
2678
+ DISPLAY_CURRENCY_REGEX.test(displayValue);
2679
+ if (valueType === "currency") {
2680
+ mergedFormat = mergedFormat
2681
+ ? { ...mergedFormat }
2682
+ : {};
2683
+ const currencySymbol = cellAttrs["@_office:currency"] ||
2684
+ cellAttrs["@_currency"] ||
2685
+ "$";
2686
+ if (!mergedFormat.numberFormat ||
2687
+ mergedFormat.numberFormat.type === "NUMBER") {
2688
+ const pattern = mergedFormat.numberFormat?.pattern || "#,##0.00";
2689
+ mergedFormat.numberFormat = {
2690
+ type: "CURRENCY",
2691
+ pattern: pattern.includes(currencySymbol)
2692
+ ? pattern
2693
+ : `${currencySymbol}${pattern.startsWith("#") || pattern.startsWith("0")
2694
+ ? " "
2695
+ : ""}${pattern}`,
2696
+ };
2697
+ }
2698
+ }
2699
+ const shouldInferNumberFormat = valueType === "float" &&
2700
+ (!mergedFormat ||
2701
+ !mergedFormat.numberFormat ||
2702
+ displaySuggestsCurrency);
2703
+ if (shouldInferNumberFormat) {
2704
+ const detectionSource = typeof displayValue === "string" &&
2705
+ displayValue.trim() !== ""
2706
+ ? displayValue
2707
+ : cellValue;
2708
+ if (detectionSource !== undefined) {
2709
+ const [detectedValueType, detectedNumberFormatType, , detectedPattern,] = detectValueTypeAndPattern(detectionSource, undefined);
2710
+ if (detectedValueType === "numberValue" &&
2711
+ detectedNumberFormatType) {
2712
+ const shouldOverrideExistingFormat = !mergedFormat ||
2713
+ !mergedFormat.numberFormat ||
2714
+ mergedFormat.numberFormat.type === "NUMBER" ||
2715
+ (displaySuggestsCurrency &&
2716
+ detectedNumberFormatType === "CURRENCY");
2717
+ if (shouldOverrideExistingFormat) {
2718
+ mergedFormat = mergedFormat
2719
+ ? { ...mergedFormat }
2720
+ : {};
2721
+ mergedFormat.numberFormat = {
2722
+ type: detectedNumberFormatType,
2723
+ ...(detectedPattern
2724
+ ? { pattern: detectedPattern }
2725
+ : {}),
2726
+ };
2727
+ }
2728
+ }
2729
+ }
2730
+ }
2731
+ if (!hasCellValue && !mergedFormat) {
2732
+ columnIndex++;
2733
+ continue;
2734
+ }
2735
+ const finalCellData = hasCellValue
2736
+ ? { ...cellData }
2737
+ : {};
2738
+ if (mergedFormat) {
2739
+ if (enableCellXfsRegistry) {
2740
+ const styleId = this.registerFormat(mergedFormat);
2741
+ finalCellData.ef = { sid: styleId };
2742
+ finalCellData.uf = { sid: styleId };
2743
+ }
2744
+ else {
2745
+ finalCellData.userEnteredFormat = mergedFormat;
2746
+ }
2747
+ }
2748
+ rowData.values[targetColumnIndex] = finalCellData;
2749
+ columnIndex++;
2750
+ }
2751
+ }
2752
+ }
2753
+ // Inject sparkline formulas for this row
2754
+ const sheetSparklines = this.sparklinesBySheet.get(sheetId);
2755
+ if (sheetSparklines) {
2756
+ const rowSparklines = sheetSparklines.get(currentRowIndex);
2757
+ if (rowSparklines) {
2758
+ for (const [colIndex, sparkline] of rowSparklines.entries()) {
2759
+ const existingCell = rowData.values[colIndex];
2760
+ // Only set formula if cell doesn't already have one
2761
+ if (existingCell?.ue?.fv) {
2762
+ continue;
2763
+ }
2764
+ // Extract data values for effective value
2765
+ const sparklineData = extractSparklineData(sparkline, currentRowIndex, rowData.values);
2766
+ // Build options object for sparkline result
2767
+ const sparklineOptions = {};
2768
+ if (sparkline.chartType !== "line") {
2769
+ sparklineOptions.charttype = sparkline.chartType;
2770
+ }
2771
+ if (sparkline.color) {
2772
+ sparklineOptions.color = sparkline.color;
2773
+ }
2774
+ const cellData = {
2775
+ ...existingCell,
2776
+ ue: { fv: sparkline.formula },
2777
+ };
2778
+ // Only set effective value if we have enough data points
2779
+ if (sparklineData.length >= 2) {
2780
+ cellData.ev = {
2781
+ structuredValue: {
2782
+ kind: "sparkline",
2783
+ data: sparklineData,
2784
+ options: sparklineOptions,
2785
+ },
2786
+ };
2787
+ }
2788
+ rowData.values[colIndex] = cellData;
2789
+ }
2790
+ // Clean up processed sparklines
2791
+ sheetSparklines.delete(currentRowIndex);
2792
+ }
2793
+ }
2794
+ dvStamper.stampRow(currentRowIndex, rowData.values);
2795
+ const isRowEmpty = rowData.values.length === 0;
2796
+ if (isRowEmpty) {
2797
+ currentRowIndex++;
2798
+ if (r === 0 && effectiveRepeats > 1) {
2799
+ currentRowIndex += effectiveRepeats - 1;
2800
+ break;
2801
+ }
2802
+ continue;
2803
+ }
2804
+ currentChunk.push(rowData);
2805
+ currentChunkRowIndices.push(currentRowIndex);
2806
+ currentRowIndex++;
2807
+ if (currentChunk.length >= chunkSize) {
2808
+ yield {
2809
+ sheetId,
2810
+ title: name,
2811
+ currentSheetIndex,
2812
+ totalSheets,
2813
+ chunkIndex,
2814
+ rows: currentChunk,
2815
+ rowIndices: currentChunkRowIndices,
2816
+ startRowIndex: currentChunkRowIndices[0],
2817
+ endRowIndex: currentChunkRowIndices[currentChunk.length - 1],
2818
+ totalRows: -1,
2819
+ isLastChunk: false,
2820
+ cellXfs: enableCellXfsRegistry ? this.cellXfs : new Map(),
2821
+ };
2822
+ currentChunk = [];
2823
+ currentChunkRowIndices = [];
2824
+ chunkIndex++;
2825
+ }
2826
+ }
2827
+ }
2828
+ // Rows fully absent from the content (or skipped by the repeated-
2829
+ // empty-row fast path) but covered by a bounded DV range still need
2830
+ // phantom `{ dataValidation }` cells or blank template rows lose
2831
+ // their dropdowns.
2832
+ for (const trailing of dvStamper.trailingRows()) {
2833
+ currentChunk.push({ values: trailing.values });
2834
+ currentChunkRowIndices.push(trailing.rowIndex);
2835
+ if (currentChunk.length >= chunkSize) {
2836
+ yield {
2837
+ sheetId,
2838
+ title: name,
2839
+ currentSheetIndex,
2840
+ totalSheets,
2841
+ chunkIndex,
2842
+ rows: currentChunk,
2843
+ rowIndices: currentChunkRowIndices,
2844
+ startRowIndex: currentChunkRowIndices[0],
2845
+ endRowIndex: currentChunkRowIndices[currentChunk.length - 1],
2846
+ totalRows: -1,
2847
+ isLastChunk: false,
2848
+ cellXfs: enableCellXfsRegistry ? this.cellXfs : new Map(),
2849
+ };
2850
+ currentChunk = [];
2851
+ currentChunkRowIndices = [];
2852
+ chunkIndex++;
2853
+ }
2854
+ }
2855
+ // Yield remaining
2856
+ if (currentChunk.length > 0) {
2857
+ yield {
2858
+ sheetId,
2859
+ title: name,
2860
+ currentSheetIndex,
2861
+ totalSheets,
2862
+ chunkIndex,
2863
+ rows: currentChunk,
2864
+ rowIndices: currentChunkRowIndices,
2865
+ startRowIndex: currentChunkRowIndices[0],
2866
+ endRowIndex: currentChunkRowIndices[currentChunk.length - 1],
2867
+ totalRows: currentRowIndex - 1,
2868
+ isLastChunk: true,
2869
+ cellXfs: enableCellXfsRegistry ? this.cellXfs : new Map(),
2870
+ };
2871
+ }
2872
+ else {
2873
+ // Empty sheet
2874
+ yield {
2875
+ sheetId,
2876
+ title: name,
2877
+ currentSheetIndex,
2878
+ totalSheets,
2879
+ chunkIndex,
2880
+ rows: [],
2881
+ rowIndices: [],
2882
+ startRowIndex: 0,
2883
+ endRowIndex: 0,
2884
+ totalRows: 0,
2885
+ isLastChunk: true,
2886
+ cellXfs: enableCellXfsRegistry ? this.cellXfs : new Map(),
2887
+ };
2888
+ }
2889
+ }
2890
+ }
2891
+ catch (error) {
2892
+ console.error("Error processing ODS sheet data", error);
2893
+ throw error;
2894
+ }
2895
+ }
2896
+ }