@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,1365 @@
1
+ import { formulaToRelativeReference } from "@rowsncolumns/calculator";
2
+ import { IndexedColors } from "@rowsncolumns/utils";
3
+ import { addressToCell, ssfFormat } from "@rowsncolumns/utils";
4
+ import { standardExcelFormatCodeById } from "./numfmt";
5
+ import { detectValueTypeAndPattern } from "./value-type-detection";
6
+ const getNode = (node, ...keys) => {
7
+ if (!node)
8
+ return undefined;
9
+ for (const key of keys) {
10
+ if (node[key] !== undefined) {
11
+ return node[key];
12
+ }
13
+ }
14
+ return undefined;
15
+ };
16
+ const getNodeText = (node) => {
17
+ if (node === undefined || node === null) {
18
+ return undefined;
19
+ }
20
+ if (typeof node === "object") {
21
+ if ("t$" in node && node.t$ !== undefined) {
22
+ return node.t$;
23
+ }
24
+ if ("_" in node && node._ !== undefined) {
25
+ return node._;
26
+ }
27
+ if ("#text" in node && node["#text"] !== undefined) {
28
+ return node["#text"];
29
+ }
30
+ return undefined;
31
+ }
32
+ return String(node);
33
+ };
34
+ const getInlineStringValue = (cell) => {
35
+ if (!cell.is) {
36
+ return undefined;
37
+ }
38
+ if (cell.is.t !== undefined) {
39
+ const inlineValue = cell.is.t;
40
+ return typeof inlineValue === "object"
41
+ ? inlineValue.t$ ?? inlineValue._ ?? ""
42
+ : inlineValue;
43
+ }
44
+ if (cell.is.r) {
45
+ const runs = Array.isArray(cell.is.r) ? cell.is.r : [cell.is.r];
46
+ return runs
47
+ .map((run) => {
48
+ if (run.t === undefined)
49
+ return "";
50
+ return typeof run.t === "object" ? run.t.t$ ?? "" : run.t;
51
+ })
52
+ .join("");
53
+ }
54
+ return undefined;
55
+ };
56
+ /**
57
+ * Extract cell value and format information from Excel cell data
58
+ * @param {Object} cell - The cell object from the sheet data
59
+ * @param {WorkBook} workbook - The workbook object containing shared strings
60
+ * @returns {Object} Object containing value and cellFormat properties
61
+ */
62
+ export function getCellValueAndFormat(cell, workbook, enableCellXfsRegistry = false, enableSharedStrings = false, activeSheetName, mergeContext) {
63
+ if (!cell) {
64
+ return null;
65
+ }
66
+ // Extract raw value
67
+ const { rawValue: extractedRawValue, formulaValue, sharedStringIndex, isError, arrayFormulaRef, sharedFormulaSpec, } = extractCellValue(cell, workbook, activeSheetName);
68
+ let rawValue = extractedRawValue;
69
+ // Extract cell format
70
+ const cellFormat = extractCellFormat(cell, workbook.workbookStyles, mergeContext);
71
+ let ef = cellFormat;
72
+ // Cell-level protection — lives top-level on CellData, not on
73
+ // CellFormat. Excel's xf carries it, but it's a per-cell concept
74
+ // (toggled per cell to opt in/out of sheet protection), not a
75
+ // share-by-style property in our model.
76
+ const protection = extractCellProtection(cell, workbook.workbookStyles);
77
+ const isSharedStringCell = enableSharedStrings &&
78
+ sharedStringIndex !== undefined &&
79
+ formulaValue === null;
80
+ if (isSharedStringCell) {
81
+ if (enableCellXfsRegistry) {
82
+ const styleId = workbook.registerFormat(cellFormat);
83
+ ef = { sid: styleId };
84
+ }
85
+ const hasFormat = enableCellXfsRegistry || Object.keys(cellFormat ?? {}).length > 0;
86
+ // Cells only carry `ss`; rich-text runs (if any) live on the
87
+ // SharedStrings entry the index points at. The parser populates
88
+ // those rich entries via `loadSharedStrings`.
89
+ return {
90
+ ...(hasFormat ? { uf: ef } : {}),
91
+ ss: String(sharedStringIndex),
92
+ ...(protection ? { protection } : {}),
93
+ };
94
+ }
95
+ // If there is no numberFormat, coerce typed display strings ("$1,200",
96
+ // "50%") into a number plus a detected pattern. Only strings need this:
97
+ // a numeric `<v>` is already typed, and its missing numberFormat means
98
+ // General — synthesizing a pattern (e.g. NUMBER "#") here changed how
99
+ // untouched imported cells render and export vs the source workbook.
100
+ if (!cellFormat.numberFormat &&
101
+ typeof rawValue === "string" &&
102
+ formulaValue === null) {
103
+ const [detectedValueType, detectedNumberFormatType, detectedNumberValue, detectedPattern,] = detectValueTypeAndPattern(rawValue, undefined);
104
+ if (detectedValueType === "numberValue" &&
105
+ detectedNumberFormatType &&
106
+ detectedNumberValue !== undefined) {
107
+ rawValue = detectedNumberValue;
108
+ cellFormat.numberFormat = {
109
+ type: detectedNumberFormatType,
110
+ ...(detectedPattern ? { pattern: detectedPattern } : {}),
111
+ };
112
+ }
113
+ }
114
+ if (enableCellXfsRegistry) {
115
+ const styleId = workbook.registerFormat(cellFormat);
116
+ ef = { sid: styleId };
117
+ }
118
+ // User entered value
119
+ let ue;
120
+ let ev;
121
+ const extendedValue = getExtendedValue(rawValue);
122
+ if (isError) {
123
+ // Error cell: preserve the Excel error code on `name` so the renderer
124
+ // shows the original code (#REF!, #DIV/0!, …) instead of a generic
125
+ // "#ERROR!" fallback.
126
+ const code = typeof rawValue === "string" ? rawValue : String(rawValue);
127
+ const errorValue = {
128
+ type: "Error",
129
+ name: code,
130
+ message: code,
131
+ };
132
+ if (formulaValue) {
133
+ ue = { fv: `=` + formulaValue };
134
+ }
135
+ else {
136
+ ue = { ev: errorValue };
137
+ }
138
+ ev = { ev: errorValue };
139
+ }
140
+ else if (formulaValue) {
141
+ ue = {
142
+ fv: `=` + formulaValue,
143
+ };
144
+ ev = extendedValue;
145
+ }
146
+ else {
147
+ ue = extendedValue;
148
+ ev = extendedValue;
149
+ }
150
+ const hasFormat = enableCellXfsRegistry || Object.keys(cellFormat ?? {}).length > 0;
151
+ const hasValue = formulaValue !== null && formulaValue !== undefined && formulaValue !== "";
152
+ const hasRawValue = rawValue !== null && rawValue !== undefined;
153
+ if (!hasRawValue && !hasValue) {
154
+ return hasFormat ? { uf: ef } : null;
155
+ }
156
+ const pattern = cellFormat?.numberFormat?.pattern ?? "GENERAL";
157
+ // Skip persisting `fv` for formula cells — the renderer derives the
158
+ // formatted value at paint time from the formula's effective value and
159
+ // the precedent-derived number format. Excel ships a cached `fv` in the
160
+ // xml (the result from the last save), but persisting it here makes
161
+ // `getFormattedValue` short-circuit on that stale string and skip the
162
+ // derivation pipeline (so e.g. a formula that depends on a currency
163
+ // source won't pick up the currency pattern at render).
164
+ const fv = formulaValue || rawValue === null || rawValue === undefined
165
+ ? undefined
166
+ : typeof rawValue === "string"
167
+ ? rawValue
168
+ : ssfFormat(pattern, rawValue);
169
+ return {
170
+ ...(hasFormat ? { uf: ef } : {}),
171
+ ...(fv !== undefined ? { fv } : {}),
172
+ ...(ue ? { ue } : {}),
173
+ ...(ev ? { ev } : {}),
174
+ ...(arrayFormulaRef ? { af: arrayFormulaRef } : {}),
175
+ ...(sharedFormulaSpec ? { sf: sharedFormulaSpec } : {}),
176
+ ...(protection ? { protection } : {}),
177
+ };
178
+ }
179
+ /**
180
+ * Build a format-only CellData for a style index that a cell inherits from
181
+ * its row (`<row customFormat="1" s="N">`) or column (`<col style="N">`)
182
+ * rather than carrying itself. Excel paints such styles across every cell
183
+ * of the row/column even where no `<c>` element exists, so the importer
184
+ * materializes them as phantom cells. Returns null when the style resolves
185
+ * to an empty format (nothing to paint or inherit).
186
+ */
187
+ export function getCellDataForStyleIndex(styleIndex, workbook, enableCellXfsRegistry = false) {
188
+ const syntheticCell = { a$: { s: String(styleIndex) } };
189
+ const cellFormat = extractCellFormat(syntheticCell, workbook.workbookStyles);
190
+ if (!cellFormat || Object.keys(cellFormat).length === 0) {
191
+ return null;
192
+ }
193
+ const protection = extractCellProtection(syntheticCell, workbook.workbookStyles);
194
+ const ef = enableCellXfsRegistry
195
+ ? { sid: workbook.registerFormat(cellFormat) }
196
+ : cellFormat;
197
+ return {
198
+ uf: ef,
199
+ ...(protection ? { protection } : {}),
200
+ };
201
+ }
202
+ export const getExtendedValue = (rawValue) => {
203
+ const rawValueType = typeof rawValue;
204
+ if (rawValueType === "boolean") {
205
+ return { bv: rawValue };
206
+ }
207
+ else if (rawValueType === "string") {
208
+ return { sv: rawValue };
209
+ }
210
+ else if (rawValueType === "number") {
211
+ return { nv: rawValue };
212
+ }
213
+ };
214
+ /**
215
+ * Extract the raw and formatted value from a cell
216
+ */
217
+ function extractCellValue(cell, workbook, activeSheetName) {
218
+ // Handle namespaced properties (x:v and x:f)
219
+ const cellValueNode = cell.v ?? cell["x:v"];
220
+ const cellValueText = getNodeText(cellValueNode);
221
+ const inlineStringValue = getInlineStringValue(cell);
222
+ const cellFormula = cell.f ?? cell["x:f"];
223
+ const cellType = cell.a$ && cell.a$.t ? cell.a$.t : "n"; // Default to number
224
+ const hasPrimitiveValue = cellValueText !== undefined && cellValueText !== "";
225
+ const hasInlineValue = inlineStringValue !== undefined && inlineStringValue !== "";
226
+ // Check if the cell is empty
227
+ if (!hasPrimitiveValue &&
228
+ !hasInlineValue &&
229
+ !cellFormula &&
230
+ !cell.t$ &&
231
+ cellType !== "s") {
232
+ return { rawValue: null, formulaValue: null, sharedStringIndex: undefined };
233
+ }
234
+ let rawValue = null;
235
+ let formulaValue = null;
236
+ let sharedStringIndex;
237
+ let isError = false;
238
+ let arrayFormulaRef;
239
+ let sharedFormulaSpec;
240
+ if (cellFormula) {
241
+ // Shared formula. Capture provenance so the exporter can re-emit
242
+ // `<f t="shared" si="N" ref="…">FORMULA</f>` on the leader and
243
+ // `<f t="shared" si="N"/>` on followers. The leader cell carries both
244
+ // `t="shared"` and `ref` (the range the formula applies to); follower
245
+ // cells carry `t="shared"` + `si` but no `ref`. Without this, the
246
+ // exporter expands every follower to a standalone `<f>` and the
247
+ // structural compression is lost on round-trip.
248
+ if (cellFormula.a$ && cellFormula.a$.t === "shared") {
249
+ const sharedIndex = cellFormula.a$.si;
250
+ const sharedFormula = cellFormula.t$;
251
+ const siNum = sharedIndex !== undefined && sharedIndex !== null
252
+ ? Number(sharedIndex)
253
+ : NaN;
254
+ if (Number.isFinite(siNum)) {
255
+ sharedFormulaSpec = {
256
+ si: siNum,
257
+ ...(cellFormula.a$.ref ? { ref: cellFormula.a$.ref } : {}),
258
+ };
259
+ }
260
+ // if (!sharedFormulaMap.has(sharedIndex)) {
261
+ if (sharedFormula && workbook.sharedFormulaMap) {
262
+ workbook.sharedFormulaMap.set(sharedIndex, [sharedFormula, cell.a$.r]);
263
+ }
264
+ }
265
+ // Array formula. The `ref` attribute names the spill range
266
+ // ("G7:G7" single-cell, "B5:B20" multi-cell). Only the anchor cell
267
+ // carries the `<f>` element; spilled cells in the range have no formula
268
+ // and therefore never reach this branch. Default to the cell's own
269
+ // coordinate when `ref` is absent (some writers omit it for single-cell).
270
+ if (cellFormula.a$ && cellFormula.a$.t === "array") {
271
+ const cellRef = cell.a$?.r;
272
+ arrayFormulaRef =
273
+ cellFormula.a$.ref ?? (cellRef ? `${cellRef}:${cellRef}` : undefined);
274
+ }
275
+ // Shared-formula followers parse as `{ a$: { t: "shared", si }, t$: "" }` —
276
+ // the self-closing `<f t="shared" si=".."/>` has no text content but the
277
+ // XML parser still creates an empty `t$`. The original `t$ !== undefined`
278
+ // check matched that empty string, returning `""` and skipping the
279
+ // shared-formula translation branch below. Require `t$` to be a non-empty
280
+ // string so followers fall through and pick up the leader's formula.
281
+ if (typeof cellFormula === "string") {
282
+ formulaValue = cellFormula;
283
+ }
284
+ else if (typeof cellFormula.t$ === "string" && cellFormula.t$ !== "") {
285
+ formulaValue = cellFormula.t$;
286
+ }
287
+ else if (cellFormula._ !== undefined && cellFormula._ !== "") {
288
+ formulaValue = cellFormula._;
289
+ }
290
+ else if (cellFormula.a$ && cellFormula.a$.t === "shared") {
291
+ // Shared-formula reference. Excel's shared-formula semantics preserve
292
+ // absolute references (`$A$1`) verbatim and only shift relative refs.
293
+ // Pass `preserveAbsolutes: true` explicitly — the default shifts
294
+ // `$A$1` along with relative refs, which corrupts followers.
295
+ const sharedIndex = cellFormula.a$.si;
296
+ const sFormula = workbook.sharedFormulaMap?.get(sharedIndex);
297
+ if (sFormula) {
298
+ const [sharedFormula, sourceCell] = sFormula;
299
+ const sourceCellInterface = addressToCell(sourceCell);
300
+ const destinationCell = addressToCell(cell.a$.r);
301
+ if (sourceCellInterface && destinationCell) {
302
+ try {
303
+ const finalFormula = formulaToRelativeReference(sharedFormula, sourceCellInterface, destinationCell, activeSheetName ?? "", { preserveAbsolutes: true });
304
+ formulaValue = finalFormula;
305
+ }
306
+ catch (err) { }
307
+ }
308
+ }
309
+ }
310
+ }
311
+ if (formulaValue) {
312
+ formulaValue = normalizeFormulaSheetNames(formulaValue);
313
+ }
314
+ switch (cellType) {
315
+ case "s": {
316
+ // Shared string
317
+ if (hasPrimitiveValue) {
318
+ const stringIndex = Number(cellValueText);
319
+ if (!Number.isNaN(stringIndex)) {
320
+ sharedStringIndex = stringIndex;
321
+ rawValue =
322
+ workbook.sharedStrings?.[stringIndex] ?? cellValueText ?? "";
323
+ }
324
+ else {
325
+ rawValue = cellValueText ?? "";
326
+ }
327
+ }
328
+ else if (inlineStringValue !== undefined) {
329
+ rawValue = inlineStringValue;
330
+ }
331
+ else {
332
+ rawValue = "";
333
+ }
334
+ break;
335
+ }
336
+ case "n": // Number
337
+ rawValue =
338
+ cellValueText !== undefined && cellValueText !== ""
339
+ ? parseFloat(cellValueText)
340
+ : undefined;
341
+ // For time
342
+ // Check if this number should be treated as a date/time based on its format
343
+ if (cell.a$ && cell.a$.s) {
344
+ const styleIndex = parseInt(cell.a$.s);
345
+ // You'll need a function to get the numFmtId from the style
346
+ const xf = workbook.workbookStyles.getCellXfs(styleIndex);
347
+ const numFmtId = xf?.a$?.numFmtId ? parseInt(xf.a$.numFmtId) : NaN;
348
+ // Time formats typically have numFmtId values: 18-21 and 45-47
349
+ // Date formats typically have numFmtId values: 14-17 and 22
350
+ const isTimeFormat = [18, 19, 20, 21, 45, 46, 47].includes(numFmtId);
351
+ const isDateFormat = [14, 15, 16, 17, 22].includes(numFmtId);
352
+ if (isTimeFormat || isDateFormat) {
353
+ // Convert Excel date/time to JS Date object
354
+ // rawValue = toDate(rawValue);
355
+ }
356
+ }
357
+ break;
358
+ case "b": // Boolean
359
+ rawValue =
360
+ cellValueText === "1" ||
361
+ cellValueText === "TRUE" ||
362
+ cellValueText === "true";
363
+ break;
364
+ case "d": // Date
365
+ rawValue =
366
+ cellValueText !== undefined && cellValueText !== ""
367
+ ? parseFloat(cellValueText)
368
+ : undefined;
369
+ // Convert Excel date to JS Date object
370
+ // rawValue = toDate(rawValue);
371
+ break;
372
+ case "e": // Error
373
+ rawValue = cellValueText;
374
+ isError = true;
375
+ break;
376
+ case "str": // Formula string result
377
+ rawValue = cellValueText ?? "";
378
+ break;
379
+ case "inlineStr":
380
+ rawValue = inlineStringValue ?? "";
381
+ break;
382
+ default:
383
+ rawValue = cellValueText;
384
+ break;
385
+ }
386
+ return {
387
+ rawValue,
388
+ formulaValue,
389
+ sharedStringIndex,
390
+ isError,
391
+ arrayFormulaRef,
392
+ sharedFormulaSpec,
393
+ };
394
+ }
395
+ function normalizeFormulaSheetNames(formula) {
396
+ return formula.replace(/('([^']|'')+'|[^'!,]+)!/g, (match, sheetName) => {
397
+ if (!sheetName.startsWith("'")) {
398
+ return match;
399
+ }
400
+ const unescaped = sheetName.slice(1, -1).replace(/''/g, "'");
401
+ if (canUnquoteSheetName(unescaped)) {
402
+ return `${unescaped}!`;
403
+ }
404
+ return `'${unescaped.replace(/'/g, "''")}'!`;
405
+ });
406
+ }
407
+ function canUnquoteSheetName(sheetName) {
408
+ if (!/^[A-Za-z0-9_]+$/.test(sheetName)) {
409
+ return false;
410
+ }
411
+ if (/^[A-Za-z]{1,3}\d+$/.test(sheetName)) {
412
+ return false;
413
+ }
414
+ if (/^R\d+C\d+$/i.test(sheetName)) {
415
+ return false;
416
+ }
417
+ return true;
418
+ }
419
+ /**
420
+ * Extract the cell formatting information
421
+ */
422
+ // Per OOXML §18.8.45 (CT_Xf), a cellXf with xfId="N" (N>0) inherits from
423
+ // cellStyleXfs[N] (the named-style xf table). For each property X
424
+ // (numFmt, font, fill, border, alignment), the cell-level value is used
425
+ // only when applyX="1"; otherwise the parent named style provides X.
426
+ // Build an effective xf that merges the cell-level overrides on top of the
427
+ // parent named style so downstream extractors see the resolved values.
428
+ function buildEffectiveXf(cellXf, parentXf) {
429
+ if (!parentXf)
430
+ return cellXf;
431
+ const cellAttrs = cellXf.a$ || {};
432
+ const parentAttrs = parentXf.a$ || {};
433
+ const resolvedAttrs = { ...cellAttrs };
434
+ // Only inherit when applyX="0" is explicitly set on the cell-level xf —
435
+ // that means "ignore my X, take it from the parent named style." When
436
+ // applyX is absent, Excel treats the cell-level X as the active value
437
+ // (this matches openpyxl, LibreOffice, and Excel itself). The earlier
438
+ // `applyX !== "1"` rule was too aggressive and silently replaced
439
+ // explicit cell-level fontIds with the parent's default.
440
+ const inheritIfApplyZero = (applyKey, attrKey) => {
441
+ if (cellAttrs[applyKey] === "0" && parentAttrs[attrKey] !== undefined) {
442
+ resolvedAttrs[attrKey] = parentAttrs[attrKey];
443
+ }
444
+ };
445
+ inheritIfApplyZero("applyNumberFormat", "numFmtId");
446
+ inheritIfApplyZero("applyFont", "fontId");
447
+ inheritIfApplyZero("applyFill", "fillId");
448
+ inheritIfApplyZero("applyBorder", "borderId");
449
+ const effectiveXf = { ...cellXf, a$: resolvedAttrs };
450
+ if (cellAttrs.applyAlignment === "0") {
451
+ const parentAlignment = getNode(parentXf, "alignment", "x:alignment");
452
+ if (parentAlignment) {
453
+ effectiveXf.alignment = parentAlignment;
454
+ delete effectiveXf["x:alignment"];
455
+ }
456
+ }
457
+ return effectiveXf;
458
+ }
459
+ /**
460
+ * Read OOXML `<protection>` flags off a resolved xf. Returns undefined
461
+ * when the cell uses Excel's defaults (locked=true, hidden=false) so the
462
+ * caller only writes a `protection` field on the cell when it deviates.
463
+ */
464
+ function readProtectionFromXf(xf) {
465
+ const node = getNode(xf, "protection", "x:protection");
466
+ if (!node)
467
+ return undefined;
468
+ const attrs = node.a$ || {};
469
+ const lockedRaw = attrs.locked;
470
+ const hiddenRaw = attrs.hidden;
471
+ const protection = {};
472
+ if (lockedRaw !== undefined && lockedRaw !== null) {
473
+ const locked = lockedRaw === "0" || lockedRaw === "false" || lockedRaw === false
474
+ ? false
475
+ : true;
476
+ if (locked === false)
477
+ protection.locked = false;
478
+ }
479
+ if (hiddenRaw !== undefined && hiddenRaw !== null) {
480
+ const hidden = hiddenRaw === "1" || hiddenRaw === "true" || hiddenRaw === true;
481
+ if (hidden === true)
482
+ protection.hidden = true;
483
+ }
484
+ return Object.keys(protection).length ? protection : undefined;
485
+ }
486
+ /**
487
+ * Resolve the xf for a cell and extract its protection flags. Returns
488
+ * undefined when the cell has no style or the style is at Excel defaults.
489
+ */
490
+ export function extractCellProtection(cell, workbookStyles) {
491
+ if (!cell?.a$ || !cell.a$.s)
492
+ return undefined;
493
+ const styleIndex = parseInt(cell.a$.s);
494
+ const cellXf = workbookStyles.getCellXfs(styleIndex);
495
+ if (!cellXf)
496
+ return undefined;
497
+ const xfIdStr = cellXf.a$ && cellXf.a$.xfId;
498
+ const xfId = xfIdStr !== undefined ? parseInt(xfIdStr) : 0;
499
+ const parentXf = xfId > 0 ? workbookStyles.getCellStyleXf(xfId) : null;
500
+ const xf = buildEffectiveXf(cellXf, parentXf);
501
+ return readProtectionFromXf(xf);
502
+ }
503
+ function extractCellFormat(cell, workbookStyles, mergeContext) {
504
+ // Default empty cell format
505
+ const cellFormat = {};
506
+ // If no style info, return default
507
+ if (!cell.a$ || !cell.a$.s) {
508
+ return cellFormat;
509
+ }
510
+ const styleIndex = parseInt(cell.a$.s);
511
+ const cellXf = workbookStyles.getCellXfs(styleIndex);
512
+ if (!cellXf)
513
+ return cellFormat;
514
+ // Resolve named-style inheritance. Cells using a built-in or custom named
515
+ // style ("Currency", "Comma", custom company styles) reference the parent
516
+ // via xfId; properties the cell doesn't explicitly override (applyX != "1")
517
+ // come from the parent. Skipping this resolution silently drops the named
518
+ // style's formatting (font, fill, border, numFmt).
519
+ const xfIdStr = cellXf.a$ && cellXf.a$.xfId;
520
+ const xfId = xfIdStr !== undefined ? parseInt(xfIdStr) : 0;
521
+ const parentXf = xfId > 0 ? workbookStyles.getCellStyleXf(xfId) : null;
522
+ const xf = buildEffectiveXf(cellXf, parentXf);
523
+ // Extract number format
524
+ const numberFormat = extractNumberFormat(xf, workbookStyles);
525
+ if (!(numberFormat?.type === "GENERAL" && !numberFormat.pattern)) {
526
+ cellFormat.numberFormat = numberFormat;
527
+ }
528
+ // `[Red]`/`[ColorN]` number-format colors are a render-time concern:
529
+ // the canvas grid resolves the pattern's active section color against
530
+ // the CURRENT value at paint time. Import must store the cell's real
531
+ // font color — baking the format-derived color into `textFormat.color`
532
+ // froze the import-time value's color onto the cell (a cell that later
533
+ // changed sign kept the stale color) and destroyed the cell's actual
534
+ // explicit font color.
535
+ cellFormat.textFormat = extractTextFormat(xf, workbookStyles);
536
+ // Only set horizontalAlignment when the source xf carried an explicit
537
+ // <alignment horizontal="…"/>. Excel's default behavior (right-align
538
+ // numbers, left-align text) is a render-time concern, not a model-level
539
+ // attribute — fabricating it on import produces an alignment xf on export
540
+ // that wasn't in the source.
541
+ const xfAlignment = getNode(xf, "alignment", "x:alignment");
542
+ if (xfAlignment) {
543
+ const alignment = xfAlignment.a$ || {};
544
+ if (alignment.horizontal) {
545
+ cellFormat.horizontalAlignment = alignment.horizontal;
546
+ }
547
+ const verticalAlignment = mapVerticalAlignment(alignment.vertical);
548
+ if (verticalAlignment && verticalAlignment !== "bottom") {
549
+ cellFormat.verticalAlignment = verticalAlignment;
550
+ }
551
+ const wrapText = alignment.wrapText === "1" || alignment.wrapText === "true"
552
+ ? "wrap"
553
+ : "overflow";
554
+ if (wrapText !== "overflow") {
555
+ cellFormat.wrapStrategy = wrapText;
556
+ }
557
+ const indent = alignment.indent ? parseInt(alignment.indent) : null;
558
+ if (indent)
559
+ cellFormat.indent = indent;
560
+ const relativeIndent = alignment.relativeIndent
561
+ ? parseInt(alignment.relativeIndent)
562
+ : null;
563
+ if (relativeIndent)
564
+ cellFormat.relativeIndent = relativeIndent;
565
+ if (alignment.textRotation) {
566
+ const rotation = parseInt(alignment.textRotation);
567
+ cellFormat.textRotation = rotation === 255 ? "vertical" : rotation;
568
+ }
569
+ if (alignment.shrinkToFit === "1" || alignment.shrinkToFit === "true") {
570
+ cellFormat.shrinkToFit = true;
571
+ }
572
+ }
573
+ // Extract background color + pattern (if any). For solid fills only
574
+ // `backgroundColor` is populated (existing behavior). For patterned fills
575
+ // all three of `fillPattern` / `foregroundColor` / `backgroundColor` are
576
+ // set so the renderer can tile the pattern over the under-layer.
577
+ const fillInfo = extractFillInfo(xf, workbookStyles);
578
+ if (fillInfo) {
579
+ if (fillInfo.backgroundColor !== undefined) {
580
+ cellFormat.backgroundColor = fillInfo.backgroundColor;
581
+ }
582
+ if (fillInfo.foregroundColor !== undefined) {
583
+ cellFormat.foregroundColor = fillInfo.foregroundColor;
584
+ }
585
+ if (fillInfo.fillPattern !== undefined) {
586
+ cellFormat.fillPattern = fillInfo.fillPattern;
587
+ }
588
+ if (fillInfo.gradient !== undefined) {
589
+ cellFormat.gradient = fillInfo.gradient;
590
+ }
591
+ }
592
+ // Extract borders
593
+ let borders = extractBorders(xf, workbookStyles);
594
+ borders = mergeBordersForAnchorCell(borders, cell, workbookStyles, mergeContext);
595
+ if (borders)
596
+ cellFormat.borders = borders;
597
+ return cellFormat;
598
+ }
599
+ function mergeBordersForAnchorCell(borders, cell, workbookStyles, mergeContext) {
600
+ if (!mergeContext)
601
+ return borders;
602
+ const cellRef = cell?.a$?.r;
603
+ if (!cellRef)
604
+ return borders;
605
+ const cellPosition = addressToCell(cellRef);
606
+ if (!cellPosition)
607
+ return borders;
608
+ const { range, rowCellsByColumnIndex } = mergeContext;
609
+ const isAnchorCell = cellPosition.rowIndex === range.startRowIndex &&
610
+ cellPosition.columnIndex === range.startColumnIndex;
611
+ if (!isAnchorCell)
612
+ return borders;
613
+ const mergedBorders = borders ? { ...borders } : {};
614
+ const leftCell = rowCellsByColumnIndex.get(range.startColumnIndex);
615
+ const rightCell = rowCellsByColumnIndex.get(range.endColumnIndex);
616
+ const leftBorders = leftCell
617
+ ? extractBordersFromCell(leftCell, workbookStyles)
618
+ : null;
619
+ const rightBorders = rightCell
620
+ ? extractBordersFromCell(rightCell, workbookStyles)
621
+ : null;
622
+ if (!mergedBorders.left && leftBorders?.left) {
623
+ mergedBorders.left = leftBorders.left;
624
+ }
625
+ if (!mergedBorders.right && rightBorders?.right) {
626
+ mergedBorders.right = rightBorders.right;
627
+ }
628
+ if (range.startRowIndex === range.endRowIndex) {
629
+ const rowEdgeCell = leftCell ?? rightCell;
630
+ const rowBorders = rowEdgeCell
631
+ ? extractBordersFromCell(rowEdgeCell, workbookStyles)
632
+ : null;
633
+ if (!mergedBorders.top && rowBorders?.top) {
634
+ mergedBorders.top = rowBorders.top;
635
+ }
636
+ if (!mergedBorders.bottom && rowBorders?.bottom) {
637
+ mergedBorders.bottom = rowBorders.bottom;
638
+ }
639
+ }
640
+ return Object.keys(mergedBorders).length > 0 ? mergedBorders : null;
641
+ }
642
+ function extractBordersFromCell(cell, workbookStyles) {
643
+ if (!cell?.a$?.s)
644
+ return null;
645
+ const styleIndex = parseInt(cell.a$.s);
646
+ const xf = workbookStyles.getCellXfs(styleIndex);
647
+ if (!xf)
648
+ return null;
649
+ return extractBorders(xf, workbookStyles);
650
+ }
651
+ /**
652
+ * Extract text formatting information from a style
653
+ */
654
+ function extractTextFormat(xf, workbookStyles) {
655
+ if (!xf.a$ || !xf.a$.fontId) {
656
+ return null;
657
+ }
658
+ const fontId = parseInt(xf.a$.fontId);
659
+ const font = workbookStyles.getFont(fontId);
660
+ return fontNodeToTextFormat(font, workbookStyles);
661
+ }
662
+ /**
663
+ * Parse a raw styles.xml `<font>` node into a TextFormat. Also used for the
664
+ * workbook default font (fonts[0]) so the exporter can round-trip it.
665
+ */
666
+ export function fontNodeToTextFormat(font, workbookStyles) {
667
+ if (!font) {
668
+ return null;
669
+ }
670
+ // Create text format object
671
+ const textFormat = {};
672
+ // Extract properties. An explicit `<name val>` always wins; a font that
673
+ // instead carries `<scheme val="major|minor"/>` follows the workbook
674
+ // theme's `<a:fontScheme>` typefaces (headings/body). Without this,
675
+ // scheme-only fonts (Excel writes them for theme-styled cells) had no
676
+ // fontFamily at all and silently rendered in the renderer default.
677
+ // Missing theme → leave unresolved (mirrors rnc-import's
678
+ // `resolve_scheme_fonts`).
679
+ const fontName = getNode(font, "name", "x:name");
680
+ if (fontName?.a$?.val) {
681
+ textFormat.fontFamily = fontName.a$.val;
682
+ }
683
+ else {
684
+ const schemeVal = getNode(font, "scheme", "x:scheme")?.a$?.val;
685
+ const themeFonts = workbookStyles.themeFonts;
686
+ const resolved = schemeVal === "major"
687
+ ? themeFonts?.major
688
+ : schemeVal === "minor"
689
+ ? themeFonts?.minor
690
+ : undefined;
691
+ if (resolved) {
692
+ textFormat.fontFamily = resolved;
693
+ }
694
+ }
695
+ const fontSize = getNode(font, "sz", "x:sz");
696
+ if (fontSize?.a$?.val) {
697
+ textFormat.fontSize = parseInt(fontSize.a$.val);
698
+ }
699
+ const fontColor = getNode(font, "color", "x:color");
700
+ if (fontColor) {
701
+ textFormat.color = extractColor(fontColor, workbookStyles.indexedColors);
702
+ }
703
+ // Boolean font properties. OOXML allows `<X/>` (truthy by default),
704
+ // `<X val="true"/>` or `<X val="1"/>` (truthy), and `<X val="false"/>`,
705
+ // `<X val="0"/>`, or `<u val="none"/>` (explicitly falsy). The original
706
+ // code only checked element presence and treated `<u val="none"/>` as
707
+ // underlined — which fabricated underline on every cell whose source
708
+ // font carried that explicit "no underline" marker.
709
+ const isTruthyFlagNode = (node) => {
710
+ if (node === undefined || node === null)
711
+ return false;
712
+ const val = typeof node === "object"
713
+ ? (node.a$?.val ?? node["#text"] ?? node)
714
+ : node;
715
+ if (val === undefined || val === null || val === true)
716
+ return true;
717
+ const str = String(val).toLowerCase();
718
+ if (str === "" || str === "true" || str === "1")
719
+ return true;
720
+ return false;
721
+ };
722
+ if (isTruthyFlagNode(getNode(font, "b", "x:b")))
723
+ textFormat.bold = true;
724
+ if (isTruthyFlagNode(getNode(font, "i", "x:i")))
725
+ textFormat.italic = true;
726
+ if (isTruthyFlagNode(getNode(font, "strike", "x:strike"))) {
727
+ textFormat.strikethrough = true;
728
+ }
729
+ // Underline is not a boolean — `<u val="none"/>` means no underline,
730
+ // anything else (including absent val attribute) is some form of
731
+ // underline. Treat "none" specifically as falsy.
732
+ const uNode = getNode(font, "u", "x:u");
733
+ if (uNode !== undefined) {
734
+ const uVal = typeof uNode === "object"
735
+ ? (uNode.a$?.val ?? uNode["#text"])
736
+ : uNode;
737
+ const uStr = uVal === undefined || uVal === null ? "" : String(uVal).toLowerCase();
738
+ if (uStr !== "none") {
739
+ textFormat.underline = true;
740
+ }
741
+ }
742
+ const vertAlignNode = getNode(font, "vertAlign", "x:vertAlign");
743
+ if (vertAlignNode?.a$?.val) {
744
+ const v = String(vertAlignNode.a$.val).toLowerCase();
745
+ if (v === "superscript")
746
+ textFormat.baselineOffset = "superscript";
747
+ else if (v === "subscript")
748
+ textFormat.baselineOffset = "subscript";
749
+ }
750
+ return textFormat;
751
+ }
752
+ /**
753
+ * Extract color information
754
+ */
755
+ export function extractColor(colorObj, indexedColors) {
756
+ if (!colorObj || !colorObj.a$) {
757
+ return undefined;
758
+ }
759
+ if (colorObj.a$.auto &&
760
+ (colorObj.a$.auto === "1" || colorObj.a$.auto === "true")) {
761
+ return {
762
+ theme: 1,
763
+ };
764
+ }
765
+ if (colorObj.a$.indexed) {
766
+ const index = parseInt(colorObj.a$.indexed);
767
+ // Last color
768
+ if (index === 64) {
769
+ return {
770
+ theme: 1,
771
+ };
772
+ }
773
+ if (indexedColors && indexedColors.length > 0) {
774
+ const paletteColor = indexedColors[index];
775
+ if (paletteColor) {
776
+ return paletteColor;
777
+ }
778
+ }
779
+ return IndexedColors[index];
780
+ }
781
+ else if (colorObj.a$.rgb) {
782
+ // OOXML §18.3.1.15 says <color rgb> is 8-char aRGB, but real-world
783
+ // files commonly emit the 6-char RGB form (alpha implied FF — this
784
+ // is what Office's default theme accent colors look like in many
785
+ // sheets). Only strip the alpha-prefix when the input is actually
786
+ // 8 chars; otherwise pass through verbatim so the exporter can
787
+ // canonicalize 6→8 by prepending FF. Blindly substring(2) turned
788
+ // `44546A` into `546A`, which downstream consumers (openpyxl) then
789
+ // refuse to load.
790
+ const raw = String(colorObj.a$.rgb).trim();
791
+ return "#" + (raw.length === 8 ? raw.substring(2) : raw);
792
+ }
793
+ else if (colorObj.a$.theme) {
794
+ return {
795
+ theme: parseInt(colorObj.a$.theme),
796
+ tint: colorObj.a$.tint ? parseFloat(colorObj.a$.tint) : null,
797
+ };
798
+ }
799
+ return null;
800
+ }
801
+ /**
802
+ * Extract number format information
803
+ */
804
+ function extractNumberFormat(xf, workbookStyles) {
805
+ if (!xf.a$ || !xf.a$.numFmtId)
806
+ return null;
807
+ const numFmtId = parseInt(xf.a$.numFmtId);
808
+ let formatCode = standardExcelFormatCodeById.get(numFmtId) ?? null;
809
+ // Check for custom number formats
810
+ if (workbookStyles.numFmts?.length) {
811
+ const numFmts = workbookStyles.numFmts;
812
+ const customFormat = numFmts.find((fmt) => fmt.a$ && parseInt(fmt.a$.numFmtId) === numFmtId);
813
+ if (customFormat && customFormat.a$ && customFormat.a$.formatCode) {
814
+ formatCode = customFormat.a$.formatCode;
815
+ }
816
+ }
817
+ // Map number format type based on id or format code
818
+ const type = mapNumberFormatType(numFmtId, formatCode) ?? "GENERAL";
819
+ return {
820
+ type,
821
+ pattern: formatCode,
822
+ };
823
+ }
824
+ /**
825
+ * Map Excel number format ID to our NumberFormatType
826
+ */
827
+ function mapNumberFormatType(numFmtId, formatCode) {
828
+ // Built-in format ids
829
+ switch (numFmtId) {
830
+ case 0:
831
+ return "GENERAL";
832
+ case 1:
833
+ case 2:
834
+ case 3:
835
+ case 4:
836
+ case 6:
837
+ case 7:
838
+ case 8:
839
+ case 11:
840
+ return "NUMBER";
841
+ case 14:
842
+ case 15:
843
+ case 16:
844
+ case 17:
845
+ return "DATE";
846
+ case 18:
847
+ case 19:
848
+ case 20:
849
+ case 21:
850
+ case 45:
851
+ case 46:
852
+ case 47:
853
+ return "TIME";
854
+ case 22:
855
+ return "DATE_TIME";
856
+ case 9:
857
+ case 10:
858
+ return "PERCENT";
859
+ case 12:
860
+ case 13:
861
+ return "FRACTION";
862
+ case 48:
863
+ case 49:
864
+ return "TEXT";
865
+ case 5:
866
+ case 37:
867
+ case 38:
868
+ return "CURRENCY";
869
+ case 39:
870
+ case 40:
871
+ case 41:
872
+ case 42:
873
+ case 43:
874
+ case 44:
875
+ return "ACCOUNTING";
876
+ }
877
+ // For custom formats, analyze the pattern
878
+ if (formatCode) {
879
+ // Get the first section (positive numbers format)
880
+ const mainSection = formatCode.split(";")[0];
881
+ const { cleanedSection, hasBracketedTimeToken } = stripQuotedAndBracketLiterals(mainSection);
882
+ // Check for date components - d, m, y that aren't escaped
883
+ let hasDateComponent = false;
884
+ let i = 0;
885
+ while (i < cleanedSection.length) {
886
+ if (cleanedSection[i] === "\\") {
887
+ // Skip escaped character
888
+ i += 2;
889
+ }
890
+ else {
891
+ const char = cleanedSection[i].toLowerCase();
892
+ if (char === "d" || char === "m" || char === "y") {
893
+ hasDateComponent = true;
894
+ break;
895
+ }
896
+ i++;
897
+ }
898
+ }
899
+ if (hasDateComponent) {
900
+ // Check if it also has time components - h, s, :
901
+ let hasTimeComponent = false;
902
+ i = 0;
903
+ while (i < cleanedSection.length) {
904
+ if (cleanedSection[i] === "\\") {
905
+ // Skip escaped character
906
+ i += 2;
907
+ }
908
+ else {
909
+ const char = cleanedSection[i].toLowerCase();
910
+ if (char === "h" || char === "s" || char === ":") {
911
+ hasTimeComponent = true;
912
+ break;
913
+ }
914
+ i++;
915
+ }
916
+ }
917
+ if (hasTimeComponent || hasBracketedTimeToken) {
918
+ return "DATE_TIME";
919
+ }
920
+ else {
921
+ return "DATE";
922
+ }
923
+ }
924
+ // Check for currency symbols. They appear in formats as either:
925
+ // - a quoted literal: "$"#,##0 (Excel writes it this way)
926
+ // - a bracketed currency: [$$-409]#,##0 (international forms)
927
+ // - a bare symbol: $#,##0 (older format strings)
928
+ // `cleanedSection` has stripped quotes and brackets, so we check the
929
+ // RAW main section, but first remove the locale specifier pattern
930
+ // `[$-XXXX]` (the `$` there is structural, not a currency symbol).
931
+ const localeSpecifierPattern = /\[\$-[0-9A-Fa-f]+\]/g;
932
+ const rawWithoutLocale = mainSection.replace(localeSpecifierPattern, "");
933
+ if (rawWithoutLocale.includes("$") ||
934
+ rawWithoutLocale.includes("€") ||
935
+ rawWithoutLocale.includes("£") ||
936
+ rawWithoutLocale.includes("¥")) {
937
+ return "CURRENCY";
938
+ }
939
+ // Other types
940
+ if (cleanedSection.includes("%")) {
941
+ return "PERCENT";
942
+ }
943
+ if (cleanedSection.includes("/")) {
944
+ return "FRACTION";
945
+ }
946
+ // Check for time patterns that aren't escaped
947
+ let hasTimePattern = false;
948
+ i = 0;
949
+ while (i < cleanedSection.length) {
950
+ if (cleanedSection[i] === "\\") {
951
+ // Skip escaped character
952
+ i += 2;
953
+ }
954
+ else {
955
+ const char = cleanedSection[i].toLowerCase();
956
+ if (char === "h" ||
957
+ char === "s" ||
958
+ char === ":" ||
959
+ cleanedSection.substring(i).startsWith("AM/PM") ||
960
+ cleanedSection.substring(i).startsWith("am/pm")) {
961
+ hasTimePattern = true;
962
+ break;
963
+ }
964
+ i++;
965
+ }
966
+ }
967
+ if (hasTimePattern || hasBracketedTimeToken) {
968
+ return "TIME";
969
+ }
970
+ if (cleanedSection.match(/[0#?]+(\.[0#?]+)?[eE][+-]?[0#?]+/)) {
971
+ return "SCIENTIFIC";
972
+ }
973
+ if (cleanedSection.includes("@")) {
974
+ return "TEXT";
975
+ }
976
+ const hasNumberPlaceholder = cleanedSection.match(/[0#?]/);
977
+ if (!hasNumberPlaceholder) {
978
+ const trimmed = cleanedSection.trim();
979
+ if (trimmed === "" || trimmed.toLowerCase() === "general") {
980
+ return "GENERAL";
981
+ }
982
+ return "TEXT";
983
+ }
984
+ }
985
+ // Default to number for any other custom format
986
+ return "NUMBER";
987
+ }
988
+ function stripQuotedAndBracketLiterals(section) {
989
+ let result = "";
990
+ let inQuotes = false;
991
+ let i = 0;
992
+ let hasBracketedTimeToken = false;
993
+ while (i < section.length) {
994
+ const char = section[i];
995
+ if (char === "\\") {
996
+ // Skip escaped character entirely.
997
+ i += 2;
998
+ continue;
999
+ }
1000
+ if (char === '"') {
1001
+ if (inQuotes && section[i + 1] === '"') {
1002
+ // Escaped quote inside a quoted literal.
1003
+ i += 2;
1004
+ continue;
1005
+ }
1006
+ inQuotes = !inQuotes;
1007
+ i += 1;
1008
+ continue;
1009
+ }
1010
+ if (inQuotes) {
1011
+ i += 1;
1012
+ continue;
1013
+ }
1014
+ if (char === "[") {
1015
+ const end = section.indexOf("]", i + 1);
1016
+ const content = end === -1 ? section.substring(i + 1) : section.substring(i + 1, end);
1017
+ if (content.match(/^[hms]+$/i)) {
1018
+ hasBracketedTimeToken = true;
1019
+ }
1020
+ i = end === -1 ? section.length : end + 1;
1021
+ continue;
1022
+ }
1023
+ result += char;
1024
+ i += 1;
1025
+ }
1026
+ return { cleanedSection: result, hasBracketedTimeToken };
1027
+ }
1028
+ // Patterned-fill type → engine enum. Excel writes ~18 distinct
1029
+ // patternType values; the engine carries the same names. `none` is treated
1030
+ // as "no fill" (returns null).
1031
+ const KNOWN_FILL_PATTERNS = new Set([
1032
+ "solid",
1033
+ "mediumGray",
1034
+ "darkGray",
1035
+ "lightGray",
1036
+ "darkHorizontal",
1037
+ "darkVertical",
1038
+ "darkDown",
1039
+ "darkUp",
1040
+ "darkGrid",
1041
+ "darkTrellis",
1042
+ "lightHorizontal",
1043
+ "lightVertical",
1044
+ "lightDown",
1045
+ "lightUp",
1046
+ "lightGrid",
1047
+ "lightTrellis",
1048
+ "gray125",
1049
+ "gray0625",
1050
+ ]);
1051
+ /**
1052
+ * Extract patterned-fill information (pattern type + fg/bg colors). For
1053
+ * `solid` patterns the foreground color is hoisted to `backgroundColor` so
1054
+ * the existing renderer / consumers keep working — that's also OOXML's
1055
+ * authoring convention (a "solid" fill puts the visible color in fgColor).
1056
+ *
1057
+ * Also handles `<gradientFill>` (Excel Phase 8) — both `linear` (with a
1058
+ * `degree`) and `path` (radial/rectangular, with `left`/`right`/`top`/
1059
+ * `bottom` insets). Gradient fills coexist with — but take precedence
1060
+ * over — pattern fills, matching Excel's UI (you pick ONE or the other).
1061
+ */
1062
+ function extractFillInfo(xf, workbookStyles) {
1063
+ if (!xf.a$ || !xf.a$.fillId)
1064
+ return null;
1065
+ const fillId = parseInt(xf.a$.fillId);
1066
+ if (fillId === 0)
1067
+ return null;
1068
+ const fill = workbookStyles.getFill(fillId);
1069
+ if (!fill)
1070
+ return null;
1071
+ // <gradientFill> branch — extract type + stops + axis attrs.
1072
+ const gradientFill = getNode(fill, "gradientFill", "x:gradientFill");
1073
+ if (gradientFill) {
1074
+ const gradient = extractGradientFill(gradientFill, workbookStyles);
1075
+ if (gradient)
1076
+ return { gradient };
1077
+ }
1078
+ const patternFill = getNode(fill, "patternFill", "x:patternFill");
1079
+ if (!patternFill)
1080
+ return null;
1081
+ const patternAttrs = patternFill.a$ ?? {};
1082
+ const patternType = patternAttrs.patternType;
1083
+ if (patternType === "none" || patternType === undefined)
1084
+ return null;
1085
+ const fgColorNode = getNode(patternFill, "fgColor", "x:fgColor");
1086
+ const bgColorNode = getNode(patternFill, "bgColor", "x:bgColor");
1087
+ const fgColor = fgColorNode
1088
+ ? extractColor(fgColorNode, workbookStyles.indexedColors)
1089
+ : undefined;
1090
+ const bgColor = bgColorNode
1091
+ ? extractColor(bgColorNode, workbookStyles.indexedColors)
1092
+ : undefined;
1093
+ // gray125 is Excel's default placeholder fill — historically dropped on
1094
+ // import. Keep that behavior unless the file explicitly carries colors,
1095
+ // in which case it's a real pattern the user chose.
1096
+ if (patternType === "gray125" && !fgColor && !bgColor)
1097
+ return null;
1098
+ // For "solid" pattern, hoist the foreground into the legacy
1099
+ // `backgroundColor` slot so the existing renderer path stays the
1100
+ // hot path. The `fillPattern: "solid"` field is still emitted for
1101
+ // round-trip identity on export.
1102
+ if (patternType === "solid") {
1103
+ return {
1104
+ fillPattern: "solid",
1105
+ ...(fgColor !== undefined ? { backgroundColor: fgColor } : {}),
1106
+ };
1107
+ }
1108
+ // True patterned fill — preserve all three fields.
1109
+ return {
1110
+ fillPattern: KNOWN_FILL_PATTERNS.has(patternType) ? patternType : undefined,
1111
+ ...(fgColor !== undefined ? { foregroundColor: fgColor } : {}),
1112
+ ...(bgColor !== undefined ? { backgroundColor: bgColor } : {}),
1113
+ };
1114
+ }
1115
+ /**
1116
+ * Parse a `<gradientFill>` node into our `GradientFill` shape. Returns
1117
+ * `null` if the gradient is unusable (no parseable stops). Stops are
1118
+ * sorted by `position` ascending to match the renderer's expectations.
1119
+ */
1120
+ function extractGradientFill(gradientFill, workbookStyles) {
1121
+ const attrs = gradientFill.a$ ?? {};
1122
+ const type = attrs.type === "path" ? "path" : "linear";
1123
+ // The fast-xml-parser surfaces repeated <stop> children as either an
1124
+ // array (multiple) or a single object (one). Normalize.
1125
+ let rawStops = getNode(gradientFill, "stop", "x:stop") ??
1126
+ getNode(gradientFill, "stops", "x:stops");
1127
+ if (rawStops == null)
1128
+ return null;
1129
+ if (!Array.isArray(rawStops))
1130
+ rawStops = [rawStops];
1131
+ const stops = [];
1132
+ for (const s of rawStops) {
1133
+ if (!s)
1134
+ continue;
1135
+ const sAttrs = s.a$ ?? {};
1136
+ const positionRaw = sAttrs.position;
1137
+ const position = typeof positionRaw === "number"
1138
+ ? positionRaw
1139
+ : positionRaw != null
1140
+ ? parseFloat(positionRaw)
1141
+ : NaN;
1142
+ if (!Number.isFinite(position))
1143
+ continue;
1144
+ const colorNode = getNode(s, "color", "x:color");
1145
+ if (!colorNode)
1146
+ continue;
1147
+ const color = extractColor(colorNode, workbookStyles.indexedColors);
1148
+ if (color == null)
1149
+ continue;
1150
+ stops.push({ position, color: String(color) });
1151
+ }
1152
+ if (stops.length === 0)
1153
+ return null;
1154
+ stops.sort((a, b) => a.position - b.position);
1155
+ const numAttr = (k) => {
1156
+ const v = attrs[k];
1157
+ if (v == null)
1158
+ return undefined;
1159
+ const n = typeof v === "number" ? v : parseFloat(v);
1160
+ return Number.isFinite(n) ? n : undefined;
1161
+ };
1162
+ return {
1163
+ type,
1164
+ ...(type === "linear"
1165
+ ? { degree: numAttr("degree") ?? 0 }
1166
+ : {
1167
+ left: numAttr("left") ?? 0,
1168
+ right: numAttr("right") ?? 0,
1169
+ top: numAttr("top") ?? 0,
1170
+ bottom: numAttr("bottom") ?? 0,
1171
+ }),
1172
+ stops,
1173
+ };
1174
+ }
1175
+ /**
1176
+ * Extract borders information
1177
+ */
1178
+ function extractBorders(xf, workbookStyles) {
1179
+ if (!xf.a$ || !xf.a$.borderId)
1180
+ return null;
1181
+ const borderId = parseInt(xf.a$.borderId);
1182
+ // Default no borders
1183
+ if (borderId === 0)
1184
+ return null;
1185
+ const borders = workbookStyles.borders;
1186
+ const border = borders[borderId];
1187
+ if (!border)
1188
+ return null;
1189
+ const result = {};
1190
+ // Process each side. Source XML often uses `<side style="none"/>` to
1191
+ // indicate "no border on this edge"; the original truthy check on `style`
1192
+ // was treating "none" as a real style and `mapBorderStyle`'s default
1193
+ // branch returns "solid", producing fabricated solid borders on every
1194
+ // edge that the source explicitly marked as none.
1195
+ ["top", "right", "bottom", "left"].forEach((side) => {
1196
+ const sideNode = getNode(border, side, `x:${side}`);
1197
+ const style = sideNode?.a$?.style;
1198
+ if (style && style !== "none") {
1199
+ result[side] = {
1200
+ style: mapBorderStyle(style),
1201
+ width: mapBorderWidth(style),
1202
+ color: getNode(sideNode, "color", "x:color")
1203
+ ? extractColor(getNode(sideNode, "color", "x:color"), workbookStyles.indexedColors)
1204
+ : null,
1205
+ };
1206
+ }
1207
+ });
1208
+ // Diagonals: OOXML uses a single <diagonal> child for the style/color,
1209
+ // and `diagonalUp="1"` / `diagonalDown="1"` attributes on the parent
1210
+ // <border> to pick which corner-to-corner line(s) get drawn.
1211
+ const diagonalNode = getNode(border, "diagonal", "x:diagonal");
1212
+ const diagonalStyle = diagonalNode?.a$?.style;
1213
+ if (diagonalStyle && diagonalStyle !== "none") {
1214
+ const diagonalBorder = {
1215
+ style: mapBorderStyle(diagonalStyle),
1216
+ width: mapBorderWidth(diagonalStyle),
1217
+ color: getNode(diagonalNode, "color", "x:color")
1218
+ ? extractColor(getNode(diagonalNode, "color", "x:color"), workbookStyles.indexedColors)
1219
+ : null,
1220
+ };
1221
+ const diagonalUpFlag = border?.a$?.diagonalUp;
1222
+ const diagonalDownFlag = border?.a$?.diagonalDown;
1223
+ const isTruthy = (v) => v !== undefined && v !== "0" && v !== "false" && v !== false;
1224
+ if (isTruthy(diagonalUpFlag))
1225
+ result.diagonalUp = diagonalBorder;
1226
+ if (isTruthy(diagonalDownFlag))
1227
+ result.diagonalDown = diagonalBorder;
1228
+ }
1229
+ return Object.keys(result).length > 0 ? result : null;
1230
+ }
1231
+ /**
1232
+ * Map Excel border style to our BorderStyle. Covers all 13 OOXML
1233
+ * styles; the previous reduced set silently collapsed `hair` →
1234
+ * `thin` and lost `mediumDashed` / `dashDot` / `dashDotDot` /
1235
+ * `slantDashDot` entirely.
1236
+ */
1237
+ function mapBorderStyle(excelStyle) {
1238
+ switch (excelStyle) {
1239
+ case "hair":
1240
+ return "hair";
1241
+ case "thin":
1242
+ return "solid";
1243
+ case "medium":
1244
+ return "solid_medium";
1245
+ case "thick":
1246
+ return "solid_thick";
1247
+ case "dashed":
1248
+ return "dashed";
1249
+ case "dotted":
1250
+ return "dotted";
1251
+ case "double":
1252
+ return "double";
1253
+ case "mediumDashed":
1254
+ return "mediumDashed";
1255
+ case "mediumDashDot":
1256
+ return "mediumDashDot";
1257
+ case "dashDot":
1258
+ return "dashDot";
1259
+ case "dashDotDot":
1260
+ return "dashDotDot";
1261
+ case "slantDashDot":
1262
+ return "slantDashDot";
1263
+ default:
1264
+ return "solid";
1265
+ }
1266
+ }
1267
+ /**
1268
+ * Map border width based on style
1269
+ */
1270
+ function mapBorderWidth(excelStyle) {
1271
+ switch (excelStyle) {
1272
+ case "hair":
1273
+ // Hair is the lightest border in OOXML — slightly thinner than
1274
+ // thin but the canvas can't paint sub-pixel lines, so it gets
1275
+ // the same 1px width but a different dash signature.
1276
+ return 1;
1277
+ case "thin":
1278
+ return 1;
1279
+ case "medium":
1280
+ case "mediumDashed":
1281
+ case "mediumDashDot":
1282
+ return 2;
1283
+ case "thick":
1284
+ return 3;
1285
+ case "double":
1286
+ return 2;
1287
+ case "dashDot":
1288
+ case "dashDotDot":
1289
+ case "slantDashDot":
1290
+ return 1;
1291
+ default:
1292
+ return 1;
1293
+ }
1294
+ }
1295
+ /**
1296
+ * Map horizontal alignment
1297
+ */
1298
+ function mapHorizontalAlignment(excelAlign) {
1299
+ switch (excelAlign) {
1300
+ case "left":
1301
+ return "left";
1302
+ case "center":
1303
+ return "center";
1304
+ case "right":
1305
+ return "right";
1306
+ default:
1307
+ return null;
1308
+ }
1309
+ }
1310
+ /**
1311
+ * Map vertical alignment
1312
+ */
1313
+ function mapVerticalAlignment(excelAlign) {
1314
+ switch (excelAlign) {
1315
+ case "top":
1316
+ return "top";
1317
+ case "center":
1318
+ return "middle";
1319
+ case "bottom":
1320
+ return "bottom";
1321
+ default:
1322
+ return null;
1323
+ }
1324
+ }
1325
+ function getDefaultHorizontalAlignment(cell, xf, numberFormat) {
1326
+ const cellType = cell.a$ && cell.a$.t ? cell.a$.t : undefined; // Default to number
1327
+ if (cellType === "s") {
1328
+ return undefined;
1329
+ }
1330
+ if (cellType === "b") {
1331
+ return "center";
1332
+ }
1333
+ // If there's no explicit alignment, check the number format
1334
+ if (xf.a$ && xf.a$.numFmtId) {
1335
+ const numFmtId = parseInt(xf.a$.numFmtId);
1336
+ // Currency formats (including custom ones) are typically right-aligned
1337
+ // Custom number formats (numFmtId >= 164)
1338
+ if (numFmtId >= 164) {
1339
+ // Get the format code if workbookStyles is available
1340
+ if (numberFormat?.type === "NUMBER") {
1341
+ return "right";
1342
+ }
1343
+ }
1344
+ if ((numFmtId >= 5 && numFmtId <= 8) ||
1345
+ (numFmtId >= 37 && numFmtId <= 44)) {
1346
+ return "right";
1347
+ }
1348
+ // Most number formats are right-aligned
1349
+ if ((numFmtId >= 1 && numFmtId <= 4) ||
1350
+ (numFmtId >= 9 && numFmtId <= 11) ||
1351
+ numFmtId === 37 ||
1352
+ numFmtId === 38 ||
1353
+ (numFmtId >= 39 && numFmtId <= 44)) {
1354
+ return "right";
1355
+ }
1356
+ }
1357
+ if (cellType === "n") {
1358
+ return "right";
1359
+ }
1360
+ if (cellType === "b") {
1361
+ return "center";
1362
+ }
1363
+ // Default for text
1364
+ return undefined;
1365
+ }