@shbernal/ts-xlsx 3.0.0 → 3.2.0

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 (277) hide show
  1. package/README.md +34 -24
  2. package/dist/bytes.d.ts +10 -2
  3. package/dist/bytes.js +22 -0
  4. package/dist/core/address.d.ts +96 -2
  5. package/dist/core/address.js +87 -33
  6. package/dist/core/array-formula-ranges.d.ts +33 -0
  7. package/dist/core/array-formula-ranges.js +62 -0
  8. package/dist/core/autofilter.d.ts +4 -3
  9. package/dist/core/autofilter.js +31 -30
  10. package/dist/core/axis-handle.d.ts +2 -0
  11. package/dist/core/axis-handle.js +5 -2
  12. package/dist/core/cell.d.ts +4 -3
  13. package/dist/core/cell.js +4 -6
  14. package/dist/core/clone.d.ts +29 -0
  15. package/dist/core/clone.js +24 -0
  16. package/dist/core/color-resolution.js +4 -6
  17. package/dist/core/column.d.ts +1 -0
  18. package/dist/core/column.js +6 -2
  19. package/dist/core/comment-thread.d.ts +7 -3
  20. package/dist/core/comment-thread.js +2 -1
  21. package/dist/core/conditional-formatting-overlay.d.ts +11 -1
  22. package/dist/core/conditional-formatting-overlay.js +30 -3
  23. package/dist/core/conditional-formatting.d.ts +107 -11
  24. package/dist/core/conditional-formatting.js +76 -19
  25. package/dist/core/containers.d.ts +12 -0
  26. package/dist/core/containers.js +5 -0
  27. package/dist/core/data-validation-overlay.d.ts +7 -1
  28. package/dist/core/data-validation-overlay.js +19 -4
  29. package/dist/core/data-validation.d.ts +22 -0
  30. package/dist/core/data-validation.js +30 -4
  31. package/dist/core/date-format.d.ts +20 -0
  32. package/dist/core/date-format.js +170 -0
  33. package/dist/core/date.d.ts +43 -8
  34. package/dist/core/date.js +19 -3
  35. package/dist/core/formula-references.d.ts +26 -0
  36. package/dist/core/formula-references.js +173 -0
  37. package/dist/core/formula-scan.d.ts +2 -0
  38. package/dist/core/formula-scan.js +58 -0
  39. package/dist/core/formula.d.ts +76 -17
  40. package/dist/core/formula.js +87 -80
  41. package/dist/core/function-values.d.ts +4 -0
  42. package/dist/core/function-values.js +58 -0
  43. package/dist/core/future-functions.d.ts +3 -0
  44. package/dist/core/{modern-functions.js → future-functions.js} +119 -88
  45. package/dist/core/grid-edits.d.ts +30 -3
  46. package/dist/core/grid-edits.js +248 -81
  47. package/dist/core/grid-shift.d.ts +58 -9
  48. package/dist/core/grid-shift.js +26 -4
  49. package/dist/core/hyperlink.d.ts +39 -0
  50. package/dist/core/hyperlink.js +88 -0
  51. package/dist/core/image.d.ts +49 -14
  52. package/dist/core/image.js +16 -5
  53. package/dist/core/internal.d.ts +10 -2
  54. package/dist/core/limits.d.ts +25 -0
  55. package/dist/core/limits.js +4 -0
  56. package/dist/core/merge.d.ts +18 -4
  57. package/dist/core/merge.js +33 -20
  58. package/dist/core/page-setup.d.ts +26 -11
  59. package/dist/core/pivot-table.d.ts +49 -10
  60. package/dist/core/pivot-table.js +112 -60
  61. package/dist/core/protection.d.ts +66 -4
  62. package/dist/core/protection.js +6 -11
  63. package/dist/core/range.js +34 -30
  64. package/dist/core/row-input.js +2 -2
  65. package/dist/core/row.d.ts +1 -0
  66. package/dist/core/row.js +6 -2
  67. package/dist/core/structured-reference.d.ts +7 -0
  68. package/dist/core/structured-reference.js +7 -0
  69. package/dist/core/style.d.ts +85 -70
  70. package/dist/core/style.js +92 -28
  71. package/dist/core/table-style.d.ts +1 -1
  72. package/dist/core/table-style.js +4 -4
  73. package/dist/core/table.d.ts +64 -16
  74. package/dist/core/table.js +87 -40
  75. package/dist/core/theme.d.ts +2 -0
  76. package/dist/core/theme.js +9 -5
  77. package/dist/core/value.d.ts +60 -28
  78. package/dist/core/value.js +48 -43
  79. package/dist/core/workbook-media.d.ts +34 -0
  80. package/dist/core/workbook-media.js +44 -0
  81. package/dist/core/workbook-styles.d.ts +83 -1
  82. package/dist/core/workbook-styles.js +4 -0
  83. package/dist/core/workbook-theme.js +5 -3
  84. package/dist/core/workbook-vba.js +3 -2
  85. package/dist/core/workbook.d.ts +41 -3
  86. package/dist/core/workbook.js +46 -41
  87. package/dist/core/worksheet-comments.d.ts +2 -1
  88. package/dist/core/worksheet-comments.js +20 -13
  89. package/dist/core/worksheet-merges.d.ts +49 -0
  90. package/dist/core/worksheet-merges.js +65 -0
  91. package/dist/core/worksheet-model.d.ts +10 -2
  92. package/dist/core/worksheet-model.js +28 -9
  93. package/dist/core/worksheet-pictures.d.ts +3 -3
  94. package/dist/core/worksheet-pictures.js +7 -6
  95. package/dist/core/worksheet.d.ts +165 -27
  96. package/dist/core/worksheet.js +206 -89
  97. package/dist/customui/ribbon.js +2 -1
  98. package/dist/entries/core.d.ts +21 -14
  99. package/dist/entries/core.js +14 -7
  100. package/dist/entries/errors.d.ts +1 -0
  101. package/dist/entries/errors.js +1 -0
  102. package/dist/entries/xlsx.d.ts +1 -1
  103. package/dist/errors.d.ts +64 -0
  104. package/dist/errors.js +22 -0
  105. package/dist/hex.d.ts +8 -0
  106. package/dist/hex.js +3 -0
  107. package/dist/io/cell-metadata/metadata.d.ts +42 -0
  108. package/dist/io/cell-metadata/metadata.js +35 -0
  109. package/dist/io/cell-metadata/rich-values.d.ts +11 -0
  110. package/dist/io/cell-metadata/rich-values.js +80 -0
  111. package/dist/io/csv/delimiter.d.ts +21 -0
  112. package/dist/io/csv/delimiter.js +18 -0
  113. package/dist/io/csv/errors.d.ts +14 -0
  114. package/dist/io/csv/errors.js +5 -0
  115. package/dist/io/csv/read.d.ts +6 -1
  116. package/dist/io/csv/read.js +51 -22
  117. package/dist/io/csv/write.d.ts +19 -5
  118. package/dist/io/csv/write.js +24 -44
  119. package/dist/io/opc/inflate.js +14 -0
  120. package/dist/io/opc/namespaces.d.ts +16 -0
  121. package/dist/io/opc/namespaces.js +4 -0
  122. package/dist/io/opc/part-paths.d.ts +9 -0
  123. package/dist/io/opc/part-paths.js +5 -5
  124. package/dist/io/opc/read-opc.d.ts +58 -14
  125. package/dist/io/opc/read-opc.js +49 -21
  126. package/dist/io/opc/read-options.d.ts +5 -1
  127. package/dist/io/opc/sniff-format.d.ts +3 -3
  128. package/dist/io/opc/sniff-format.js +2 -2
  129. package/dist/io/opc/strict-relationships.d.ts +2 -0
  130. package/dist/io/opc/strict-relationships.js +12 -0
  131. package/dist/io/read-policy/column-budget.d.ts +26 -0
  132. package/dist/io/read-policy/column-budget.js +18 -0
  133. package/dist/io/read-policy/read-repair.d.ts +74 -0
  134. package/dist/io/read-policy/read-repair.js +119 -0
  135. package/dist/io/style/cell-style-resolution.d.ts +25 -0
  136. package/dist/io/style/cell-style-resolution.js +30 -0
  137. package/dist/io/style/xf-style.d.ts +23 -1
  138. package/dist/io/style/xf-style.js +8 -0
  139. package/dist/io/xlsb/formula.d.ts +2 -2
  140. package/dist/io/xlsb/formula.js +52 -35
  141. package/dist/io/xlsb/primitives.js +8 -14
  142. package/dist/io/xlsb/ptg-functions.d.ts +19 -10
  143. package/dist/io/xlsb/ptg-functions.js +495 -596
  144. package/dist/io/xlsb/read-metadata.d.ts +7 -0
  145. package/dist/io/xlsb/read-metadata.js +69 -0
  146. package/dist/io/xlsb/read-styles.js +45 -81
  147. package/dist/io/xlsb/read-worksheet.d.ts +26 -1
  148. package/dist/io/xlsb/read-worksheet.js +148 -106
  149. package/dist/io/xlsb/read.d.ts +6 -3
  150. package/dist/io/xlsb/read.js +70 -31
  151. package/dist/io/xlsb/record-stream.d.ts +30 -0
  152. package/dist/io/xlsb/record-stream.js +20 -0
  153. package/dist/io/xlsb/record-types.d.ts +14 -5
  154. package/dist/io/xlsb/record-types.js +14 -5
  155. package/dist/io/xlsb/sheet-protection.d.ts +20 -0
  156. package/dist/io/xlsb/sheet-protection.js +60 -0
  157. package/dist/io/xlsx/cell-accumulator.d.ts +24 -9
  158. package/dist/io/xlsx/cell-accumulator.js +74 -21
  159. package/dist/io/xlsx/cell-metadata.d.ts +39 -0
  160. package/dist/io/xlsx/cell-metadata.js +162 -0
  161. package/dist/io/xlsx/cell-value.d.ts +19 -5
  162. package/dist/io/xlsx/cell-value.js +45 -30
  163. package/dist/io/xlsx/color-xml.js +3 -2
  164. package/dist/io/xlsx/column-span.d.ts +11 -0
  165. package/dist/io/xlsx/column-span.js +9 -0
  166. package/dist/io/xlsx/comments.d.ts +9 -30
  167. package/dist/io/xlsx/comments.js +25 -83
  168. package/dist/io/xlsx/conditional-formatting.d.ts +43 -23
  169. package/dist/io/xlsx/conditional-formatting.js +437 -138
  170. package/dist/io/xlsx/data-validation.d.ts +12 -7
  171. package/dist/io/xlsx/data-validation.js +67 -53
  172. package/dist/io/xlsx/edit-vba.d.ts +7 -2
  173. package/dist/io/xlsx/edit-vba.js +52 -52
  174. package/dist/io/xlsx/font-xml.d.ts +7 -0
  175. package/dist/io/xlsx/font-xml.js +104 -0
  176. package/dist/io/xlsx/hyperlinks.d.ts +19 -21
  177. package/dist/io/xlsx/hyperlinks.js +12 -32
  178. package/dist/io/xlsx/images.d.ts +49 -16
  179. package/dist/io/xlsx/images.js +149 -50
  180. package/dist/io/xlsx/namespaces.d.ts +24 -0
  181. package/dist/io/xlsx/namespaces.js +11 -0
  182. package/dist/io/xlsx/package-plan.d.ts +60 -15
  183. package/dist/io/xlsx/package-plan.js +83 -18
  184. package/dist/io/xlsx/part-names.d.ts +6 -8
  185. package/dist/io/xlsx/part-names.js +3 -7
  186. package/dist/io/xlsx/pivot.d.ts +5 -5
  187. package/dist/io/xlsx/pivot.js +26 -24
  188. package/dist/io/xlsx/preserved-splices.d.ts +6 -0
  189. package/dist/io/xlsx/preserved-splices.js +74 -0
  190. package/dist/io/xlsx/read-comments.d.ts +29 -0
  191. package/dist/io/xlsx/read-comments.js +75 -0
  192. package/dist/io/xlsx/read-parts.d.ts +59 -0
  193. package/dist/io/xlsx/read-parts.js +259 -0
  194. package/dist/io/xlsx/read-pivot.js +11 -4
  195. package/dist/io/xlsx/{rich-runs.d.ts → read-rich-runs.d.ts} +8 -5
  196. package/dist/io/xlsx/{rich-runs.js → read-rich-runs.js} +24 -4
  197. package/dist/io/xlsx/read-rows.d.ts +35 -11
  198. package/dist/io/xlsx/read-rows.js +73 -49
  199. package/dist/io/xlsx/read-shared-strings.js +3 -2
  200. package/dist/io/xlsx/read-styles.d.ts +0 -30
  201. package/dist/io/xlsx/read-styles.js +45 -105
  202. package/dist/io/xlsx/read-workbook-xml.d.ts +42 -0
  203. package/dist/io/xlsx/read-workbook-xml.js +175 -0
  204. package/dist/io/xlsx/read-worksheet.d.ts +9 -3
  205. package/dist/io/xlsx/read-worksheet.js +70 -177
  206. package/dist/io/xlsx/read.d.ts +3 -14
  207. package/dist/io/xlsx/read.js +74 -411
  208. package/dist/io/xlsx/relationships.d.ts +3 -0
  209. package/dist/io/xlsx/relationships.js +4 -0
  210. package/dist/io/xlsx/rich-text.js +1 -1
  211. package/dist/io/xlsx/row-position.d.ts +20 -0
  212. package/dist/io/xlsx/row-position.js +11 -0
  213. package/dist/io/xlsx/row-xml.d.ts +139 -0
  214. package/dist/io/xlsx/row-xml.js +222 -0
  215. package/dist/io/xlsx/shared-formulas.js +2 -2
  216. package/dist/io/xlsx/sheet-properties.d.ts +10 -1
  217. package/dist/io/xlsx/sheet-properties.js +182 -20
  218. package/dist/io/xlsx/strict-parts.d.ts +7 -0
  219. package/dist/io/xlsx/strict-parts.js +171 -0
  220. package/dist/io/xlsx/style-elements.d.ts +41 -0
  221. package/dist/io/xlsx/style-elements.js +136 -0
  222. package/dist/io/xlsx/styles.d.ts +17 -3
  223. package/dist/io/xlsx/styles.js +13 -209
  224. package/dist/io/xlsx/tables.d.ts +10 -2
  225. package/dist/io/xlsx/tables.js +55 -52
  226. package/dist/io/xlsx/theme-xml.d.ts +32 -4
  227. package/dist/io/xlsx/theme-xml.js +106 -71
  228. package/dist/io/xlsx/threaded-comments.d.ts +14 -11
  229. package/dist/io/xlsx/threaded-comments.js +16 -8
  230. package/dist/io/xlsx/workbook-xml.d.ts +45 -6
  231. package/dist/io/xlsx/workbook-xml.js +62 -54
  232. package/dist/io/xlsx/worksheet-xml.d.ts +26 -70
  233. package/dist/io/xlsx/worksheet-xml.js +69 -282
  234. package/dist/io/xlsx/write-stream.d.ts +90 -19
  235. package/dist/io/xlsx/write-stream.js +80 -25
  236. package/dist/io/xlsx/write.d.ts +12 -6
  237. package/dist/io/xlsx/write.js +226 -123
  238. package/dist/rel-type.d.ts +25 -0
  239. package/dist/rel-type.js +9 -0
  240. package/dist/token-set.d.ts +13 -0
  241. package/dist/token-set.js +4 -1
  242. package/dist/vba/bytes.d.ts +16 -5
  243. package/dist/vba/bytes.js +27 -6
  244. package/dist/vba/cfb-format.d.ts +55 -0
  245. package/dist/vba/cfb-format.js +35 -0
  246. package/dist/vba/cfb-writer.d.ts +8 -0
  247. package/dist/vba/cfb-writer.js +40 -40
  248. package/dist/vba/cfb.d.ts +21 -4
  249. package/dist/vba/cfb.js +124 -90
  250. package/dist/vba/codepage.js +2 -1
  251. package/dist/vba/dir-records.d.ts +6 -0
  252. package/dist/vba/dir-records.js +10 -2
  253. package/dist/vba/errors.d.ts +11 -4
  254. package/dist/vba/index.d.ts +0 -2
  255. package/dist/vba/index.js +0 -2
  256. package/dist/vba/ms-ovba.d.ts +6 -2
  257. package/dist/vba/ms-ovba.js +66 -22
  258. package/dist/vba/project-editor.js +129 -119
  259. package/dist/vba/project.d.ts +15 -1
  260. package/dist/vba/project.js +38 -13
  261. package/dist/vba/vba-encoding.d.ts +4 -10
  262. package/dist/vba/vba-encoding.js +3 -17
  263. package/dist/xml/xml-attrs.d.ts +37 -0
  264. package/dist/xml/xml-attrs.js +30 -0
  265. package/dist/xml/xml-chars.d.ts +40 -0
  266. package/dist/xml/xml-chars.js +12 -0
  267. package/dist/xml/xml-namespaces.d.ts +31 -0
  268. package/dist/xml/xml-namespaces.js +50 -0
  269. package/dist/xml/xml-read.d.ts +101 -11
  270. package/dist/xml/xml-read.js +150 -18
  271. package/dist/xml/xml-scan.d.ts +25 -34
  272. package/dist/xml/xml-scan.js +69 -46
  273. package/dist/xml/xml.d.ts +59 -26
  274. package/dist/xml/xml.js +45 -23
  275. package/package.json +15 -9
  276. package/skills/ts-xlsx-upstream/SKILL.md +21 -13
  277. package/dist/core/modern-functions.d.ts +0 -1
@@ -0,0 +1,25 @@
1
+ import type { XfStyle } from './xf-style.ts';
2
+ /**
3
+ * The cell → row → column → xf 0 style resolution for one worksheet, fed in the order every reader
4
+ * already visits a sheet: column formats before any cell refers to them, a row before its own cells.
5
+ * Columns are 1-based, and a negative style index means "declares no format".
6
+ */
7
+ export declare class CellStyleResolver {
8
+ #private;
9
+ /** Record the format a span of columns gives its cells. */
10
+ noteColumnSpan(first: number, last: number, styleIndex: number): void;
11
+ /**
12
+ * Open a row. Its format reaches its cells only when the row is marked `customFormat`; without that
13
+ * flag the format describes the row itself and is not inherited.
14
+ */
15
+ openRow(styleIndex: number, customFormat: boolean): void;
16
+ /** Close the row, so a cell outside one never inherits the last row's format. */
17
+ closeRow(): void;
18
+ /**
19
+ * The style a cell resolves to: its own, else its row's, else its column's, else xf 0. `undefined`
20
+ * only when the index it resolves to names no record in `xfStyles`, a damaged or truncated table.
21
+ */
22
+ styleFor(col: number, cellStyleIndex: number, xfStyles: ReadonlyArray<XfStyle>): XfStyle | undefined;
23
+ /** Whether the cell, its row or its column declares a format, rather than the cell falling to xf 0. */
24
+ declaresFormat(col: number, cellStyleIndex: number): boolean;
25
+ }
@@ -0,0 +1,30 @@
1
+ export class CellStyleResolver {
2
+ #columnStyle = new Map();
3
+ #rowStyle = -1;
4
+ noteColumnSpan(first, last, styleIndex) {
5
+ if (styleIndex < 0)
6
+ return;
7
+ for (let index = first; index <= last; index++)
8
+ this.#columnStyle.set(index, styleIndex);
9
+ }
10
+ openRow(styleIndex, customFormat) {
11
+ this.#rowStyle = customFormat ? styleIndex : -1;
12
+ }
13
+ closeRow() {
14
+ this.#rowStyle = -1;
15
+ }
16
+ styleFor(col, cellStyleIndex, xfStyles) {
17
+ const index = this.#indexFor(col, cellStyleIndex);
18
+ return xfStyles[index < 0 ? 0 : index];
19
+ }
20
+ declaresFormat(col, cellStyleIndex) {
21
+ return this.#indexFor(col, cellStyleIndex) >= 0;
22
+ }
23
+ #indexFor(col, cellStyleIndex) {
24
+ if (cellStyleIndex >= 0)
25
+ return cellStyleIndex;
26
+ if (this.#rowStyle >= 0)
27
+ return this.#rowStyle;
28
+ return this.#columnStyle.get(col) ?? -1;
29
+ }
30
+ }
@@ -1,5 +1,6 @@
1
1
  import { type Cell } from '../../core/cell.ts';
2
- import { type Border, type CellStyle, type Fill, type Font, type NamedCellStyle, type TableStyleTable } from '../../core/style.ts';
2
+ import { type Border, type CellStyle, type Fill, type Font, type Protection } from '../../core/style.ts';
3
+ import type { NamedCellStyle, TableStyleTable } from '../../core/workbook-styles.ts';
3
4
  /**
4
5
  * What an xf resolves to: the {@link CellStyle} facet tuple, plus the two flags an xf carries that
5
6
  * are not facets. Absent facets stay undefined, matching the contract that an unset facet is simply
@@ -74,6 +75,27 @@ export declare function numFmtCodeFor(id: number, custom: ReadonlyMap<number, st
74
75
  * from, and the two cannot drift on what "applying a style" means.
75
76
  */
76
77
  export declare function applyXfToCell(cell: Cell, style: XfStyle | undefined): void;
78
+ /**
79
+ * A cell's protection facets from the two flags either serialisation states, keeping only what
80
+ * carries information.
81
+ *
82
+ * `locked` defaults to TRUE in OOXML, so an explicitly *unlocked* cell is the state worth recording
83
+ * and a locked one merely restates the default; `hidden` defaults to false, so only a set flag does.
84
+ * An xf that states neither yields no protection at all rather than an empty object, which is what
85
+ * keeps the two readings of one workbook identical.
86
+ *
87
+ * Here rather than once per codec because that is exactly what it was: `parseProtection` in the XML
88
+ * reader and `readProtection` in the binary one, two spellings of two default rules, agreeing by
89
+ * review. The inputs differ (an attribute is tri-state, a bit is not) and the rule does not, so the
90
+ * codecs keep the parsing and share the rule.
91
+ *
92
+ * @param locked the `locked` attribute's tri-state reading, or the bit's boolean; `undefined` where
93
+ * the file states nothing.
94
+ */
95
+ export declare function protectionFrom(flags: {
96
+ readonly locked?: boolean | undefined;
97
+ readonly hidden?: boolean | undefined;
98
+ }): Protection | undefined;
77
99
  /**
78
100
  * A `<cellStyle>` / `BrtStyle` label: the name and builtinId that title one `cellStyleXfs` entry,
79
101
  * keyed to that entry's index.
@@ -69,6 +69,14 @@ export function applyXfToCell(cell, style) {
69
69
  if (style.xfId !== undefined)
70
70
  cell[NAMED_STYLE_ID] = style.xfId;
71
71
  }
72
+ export function protectionFrom(flags) {
73
+ const out = {};
74
+ if (flags.locked === false)
75
+ out.locked = false;
76
+ if (flags.hidden === true)
77
+ out.hidden = true;
78
+ return Object.keys(out).length > 0 ? out : undefined;
79
+ }
72
80
  export function resolveStyleTable(tables) {
73
81
  const { directXfs, namedXfs, labels, fonts } = tables;
74
82
  const cellXfs = directXfs.map((xf) => {
@@ -34,8 +34,8 @@ export interface FormulaAnchor {
34
34
  * @param rgce the token stream.
35
35
  * @param rgcb the trailing extra-data block: the array constants, and the cell ranges a precomputed
36
36
  * range token refers to. Its entries are consumed in token order.
37
- * @returns the formula text, or `undefined` if the stream uses a token this reader does not decode.
38
- * @throws {XlsbParseError} if a token runs past the end of the stream (a malformed formula).
37
+ * @returns the formula text, or `undefined` if the stream uses a token this reader does not decode,
38
+ * or runs off the end of its own record.
39
39
  */
40
40
  export declare function decodeFormula(rgce: Uint8Array, rgcb: Uint8Array, scope: FormulaScope): string | undefined;
41
41
  /**
@@ -1,19 +1,35 @@
1
- import { MAX_COLUMN, numberToColumn } from '../../core/address.js';
2
- import { quoteSheetName } from '../../core/formula.js';
1
+ import { MAX_COLUMN, MAX_COLUMN_INDEX, MAX_ROW_INDEX, numberToColumn } from '../../core/address.js';
2
+ import { formulaNumberLiteral, quoteSheetName } from '../../core/formula.js';
3
+ import { REF_ERROR } from '../../core/value.js';
4
+ import { XlsbParseError } from './errors.js';
3
5
  import { errorCodeFor, RecordReader } from './primitives.js';
4
- import { FTAB_USER_DEFINED, fixedArityFor, functionNameFor } from './ptg-functions.js';
6
+ import { builtinFunctionAt, FTAB_USER_DEFINED, functionNameFor } from './ptg-functions.js';
5
7
  export function decodeFormula(rgce, rgcb, scope) {
6
- const tokens = new RecordReader(rgce);
7
- const extra = new RecordReader(rgcb);
8
+ try {
9
+ return decodeTokens(rgce, rgcb, scope);
10
+ }
11
+ catch (error) {
12
+ if (error instanceof XlsbParseError)
13
+ return undefined;
14
+ throw error;
15
+ }
16
+ }
17
+ function decodeTokens(rgce, rgcb, scope) {
8
18
  const stack = [];
9
- const push = (text) => {
10
- if (text === undefined)
11
- return false;
12
- stack.push(text);
13
- return true;
19
+ const decode = {
20
+ tokens: new RecordReader(rgce),
21
+ extra: new RecordReader(rgcb),
22
+ stack,
23
+ scope,
24
+ push: (text) => {
25
+ if (text === undefined || text.length > MAX_DECODED_FORMULA_LENGTH)
26
+ return false;
27
+ stack.push(text);
28
+ return true;
29
+ },
14
30
  };
15
- while (!tokens.done) {
16
- if (!step(tokens.u8(), tokens, extra, stack, scope, push))
31
+ while (!decode.tokens.done) {
32
+ if (!step(decode.tokens.u8(), decode))
17
33
  return undefined;
18
34
  }
19
35
  return stack.length === 1 ? stack[0] : undefined;
@@ -29,7 +45,8 @@ export function formulaAnchor(rgce, rgcb) {
29
45
  return undefined;
30
46
  return { row, column: extra.u32() };
31
47
  }
32
- function step(ptg, tokens, extra, stack, scope, push) {
48
+ function step(ptg, decode) {
49
+ const { tokens, stack, push } = decode;
33
50
  const binary = BINARY_OPERATORS.get(ptg);
34
51
  if (binary !== undefined) {
35
52
  const right = stack.pop();
@@ -54,33 +71,33 @@ function step(ptg, tokens, extra, stack, scope, push) {
54
71
  case PTG.Str:
55
72
  return push(quoteString(tokens.shortString()));
56
73
  case PTG.Attr:
57
- return attribute(tokens, stack, push);
74
+ return attribute(decode);
58
75
  case PTG.Err:
59
76
  return push(errorCodeFor(tokens.u8()));
60
77
  case PTG.Bool:
61
78
  return push(tokens.u8() !== 0 ? 'TRUE' : 'FALSE');
62
79
  case PTG.Int:
63
- return push(String(tokens.u16()));
80
+ return push(numberLiteral(tokens.u16()));
64
81
  case PTG.Num:
65
- return push(numberText(tokens.f64()));
82
+ return push(numberLiteral(tokens.f64()));
66
83
  default:
67
- return ptg >= CLASSED_TOKEN_FLOOR
68
- ? operand((ptg & CLASSED_TOKEN_MASK) | CLASSED_TOKEN_FLOOR, tokens, extra, stack, scope, push)
84
+ return ptg >= CLASSED_TOKEN_FLOOR && ptg <= CLASSED_TOKEN_CEILING
85
+ ? operand((ptg & CLASSED_TOKEN_MASK) | CLASSED_TOKEN_FLOOR, decode)
69
86
  : false;
70
87
  }
71
88
  }
72
- function operand(base, tokens, extra, stack, scope, push) {
89
+ function operand(base, decode) {
90
+ const { tokens, extra, stack, scope, push } = decode;
73
91
  switch (base) {
74
92
  case PTG.Array:
75
93
  tokens.skip(14);
76
94
  return push(arrayConstant(extra));
77
95
  case PTG.Func: {
78
- const name = functionNameFor(tokens.u16());
79
- const arity = name === undefined ? undefined : fixedArityFor(name);
80
- return name !== undefined && arity !== undefined && push(call(name, arity, stack));
96
+ const fn = builtinFunctionAt(tokens.u16());
97
+ return fn !== undefined && fn[1] !== 'variadic' && push(call(fn[0], fn[1], stack));
81
98
  }
82
99
  case PTG.FuncVar:
83
- return variadicCall(tokens, stack, push);
100
+ return variadicCall(decode);
84
101
  case PTG.Name: {
85
102
  return push(scope.names[tokens.u32() - 1]);
86
103
  }
@@ -100,16 +117,16 @@ function operand(base, tokens, extra, stack, scope, push) {
100
117
  }
101
118
  case PTG.RefErr:
102
119
  tokens.skip(6);
103
- return push(REFERENCE_ERROR);
120
+ return push(REF_ERROR);
104
121
  case PTG.AreaErr:
105
122
  tokens.skip(12);
106
- return push(REFERENCE_ERROR);
123
+ return push(REF_ERROR);
107
124
  case PTG.RefErr3d:
108
125
  tokens.skip(8);
109
- return push(REFERENCE_ERROR);
126
+ return push(REF_ERROR);
110
127
  case PTG.AreaErr3d:
111
128
  tokens.skip(14);
112
- return push(REFERENCE_ERROR);
129
+ return push(REF_ERROR);
113
130
  case PTG.MemArea:
114
131
  tokens.skip(6);
115
132
  return skipExtraRanges(extra);
@@ -117,7 +134,7 @@ function operand(base, tokens, extra, stack, scope, push) {
117
134
  return false;
118
135
  }
119
136
  }
120
- function attribute(tokens, stack, push) {
137
+ function attribute({ tokens, stack, push }) {
121
138
  const flags = tokens.u8();
122
139
  const data = tokens.u16();
123
140
  if ((flags & ATTR_CHOOSE) !== 0) {
@@ -128,7 +145,7 @@ function attribute(tokens, stack, push) {
128
145
  return push(call('SUM', 1, stack));
129
146
  return true;
130
147
  }
131
- function variadicCall(tokens, stack, push) {
148
+ function variadicCall({ tokens, stack, push }) {
132
149
  const count = tokens.u8() & FUNCVAR_PARAM_MASK;
133
150
  const index = tokens.u16() & FUNCVAR_INDEX_MASK;
134
151
  if (index !== FTAB_USER_DEFINED) {
@@ -167,7 +184,7 @@ function arrayConstant(extra) {
167
184
  function arrayElement(extra) {
168
185
  switch (extra.u8()) {
169
186
  case SER_NUM:
170
- return numberText(extra.f64());
187
+ return numberLiteral(extra.f64());
171
188
  case SER_STR:
172
189
  return quoteString(extra.shortString());
173
190
  case SER_BOOL:
@@ -212,7 +229,7 @@ function rangeText(rowFirst, rowLast, packedFirst, packedLast) {
212
229
  const last = columnOnly(packedLast);
213
230
  return first === undefined || last === undefined ? undefined : `${first}:${last}`;
214
231
  }
215
- if ((packedFirst & COLUMN_MASK) === 0 && (packedLast & COLUMN_MASK) === MAX_COLUMN - 1) {
232
+ if ((packedFirst & COLUMN_MASK) === 0 && (packedLast & COLUMN_MASK) === MAX_COLUMN_INDEX) {
216
233
  return rowFirst > MAX_ROW_INDEX || rowLast > MAX_ROW_INDEX
217
234
  ? undefined
218
235
  : `${rowOnly(rowFirst, packedFirst)}:${rowOnly(rowLast, packedLast)}`;
@@ -233,10 +250,9 @@ function rowOnly(row, packedColumn) {
233
250
  function quoteString(text) {
234
251
  return `"${text.replace(/"/g, '""')}"`;
235
252
  }
236
- function numberText(value) {
237
- return String(value).toUpperCase();
253
+ function numberLiteral(value) {
254
+ return Number.isFinite(value) ? formulaNumberLiteral(value) : undefined;
238
255
  }
239
- const REFERENCE_ERROR = '#REF!';
240
256
  const BINARY_OPERATORS = new Map([
241
257
  [0x03, '+'],
242
258
  [0x04, '-'],
@@ -283,7 +299,9 @@ const PTG = {
283
299
  AreaErr3d: 0x3d,
284
300
  };
285
301
  const CLASSED_TOKEN_FLOOR = 0x20;
302
+ const CLASSED_TOKEN_CEILING = 0x7f;
286
303
  const CLASSED_TOKEN_MASK = 0x1f;
304
+ const MAX_DECODED_FORMULA_LENGTH = 4 * 8_192;
287
305
  const ATTR_CHOOSE = 0x04;
288
306
  const ATTR_SUM = 0x10;
289
307
  const FUNCVAR_PARAM_MASK = 0x7f;
@@ -291,7 +309,6 @@ const FUNCVAR_INDEX_MASK = 0x7fff;
291
309
  const COLUMN_MASK = 0x3fff;
292
310
  const COLUMN_RELATIVE = 0x4000;
293
311
  const ROW_RELATIVE = 0x8000;
294
- const MAX_ROW_INDEX = 1048575;
295
312
  const SER_NUM = 0x00;
296
313
  const SER_STR = 0x01;
297
314
  const SER_BOOL = 0x02;
@@ -1,15 +1,18 @@
1
+ import { decodeUtf16le } from '../../bytes.js';
1
2
  import { isErrorCode } from '../../core/value.js';
3
+ import { hex } from '../../hex.js';
2
4
  import { XlsbParseError } from './errors.js';
3
- const CHARS_PER_BATCH = 4096;
4
5
  const NULL_STRING_LENGTH = 0xffffffff;
5
6
  const rkScratch = new DataView(new ArrayBuffer(8));
6
7
  export class RecordReader {
7
8
  #data;
8
- #view;
9
+ #cachedView;
9
10
  #offset = 0;
10
11
  constructor(data) {
11
12
  this.#data = data;
12
- this.#view = new DataView(data.buffer, data.byteOffset, data.byteLength);
13
+ }
14
+ get #view() {
15
+ return (this.#cachedView ??= new DataView(this.#data.buffer, this.#data.byteOffset, this.#data.byteLength));
13
16
  }
14
17
  get remaining() {
15
18
  return this.#data.length - this.#offset;
@@ -89,16 +92,7 @@ export class RecordReader {
89
92
  }
90
93
  #characters(count) {
91
94
  const start = this.#take(count * 2);
92
- let text = '';
93
- let batch = [];
94
- for (let index = 0; index < count; index++) {
95
- batch.push(this.#view.getUint16(start + index * 2, true));
96
- if (batch.length === CHARS_PER_BATCH) {
97
- text += String.fromCharCode(...batch);
98
- batch = [];
99
- }
100
- }
101
- return batch.length > 0 ? text + String.fromCharCode(...batch) : text;
95
+ return decodeUtf16le(this.#data.subarray(start, start + count * 2));
102
96
  }
103
97
  #take(count) {
104
98
  if (count > this.remaining) {
@@ -144,5 +138,5 @@ function colorByType(type, index, alpha, red, green, blue) {
144
138
  }
145
139
  }
146
140
  function hexByte(value) {
147
- return value.toString(16).toUpperCase().padStart(2, '0');
141
+ return hex(value, 2);
148
142
  }
@@ -1,16 +1,25 @@
1
1
  /** The index [MS-XLS] reserves for a call whose name is given by the token stream's first operand. */
2
2
  export declare const FTAB_USER_DEFINED = 255;
3
- /** Look up a built-in function name by its `iftab` index, or `undefined` if the index names none. */
4
- export declare function functionNameFor(index: number): string | undefined;
5
3
  /**
6
- * How many arguments a fixed-arity function takes, or `undefined` for one whose argument count varies.
4
+ * One built-in function: the name an `iftab` index means, and how many arguments it takes.
7
5
  *
8
- * A `PtgFunc` token states only *which* function is called, never with how many arguments: it is the
9
- * token Excel emits precisely because the count is implied by the function. So decoding `ROUND(A1,2)`
10
- * back from its two operands and one call token is impossible without this: the arity is the only
11
- * thing that says which operands on the stack belong to the call. (`PtgFuncVar` carries its own count
12
- * and needs none of this; the two token forms partition the table between them.)
6
+ * The arity is not decoration. A `PtgFunc` token states only *which* function is called, never with
7
+ * how many arguments: it is the token Excel emits precisely because the count is implied by the
8
+ * function. So decoding `ROUND(A1,2)` back from its two operands and one call token is impossible
9
+ * without it, and `'variadic'` says the call cannot be spelled with that token at all (`PtgFuncVar`
10
+ * carries its own count; the two token forms partition the table between them). Transcribed from the
11
+ * parameter grammar each function's [MS-XLS] 2.5.198.17 entry states.
13
12
  *
14
- * Transcribed from the parameter grammar each function's [MS-XLS] 2.5.198.17 entry states.
13
+ * It used to live in a second table keyed by name, which let an entry exist in one and not the other
14
+ * with nothing to say so. A function whose arity was never transcribed simply read as variadic, and a
15
+ * `PtgFunc` citing it then took the wrong run of operands off the stack and produced a different
16
+ * formula, silently. Recording it beside the name makes "no arity recorded" unrepresentable: the
17
+ * compiler asks for it once per entry, and `'variadic'` becomes a decision written down rather than
18
+ * an absence that looks like one.
15
19
  */
16
- export declare function fixedArityFor(name: string): number | undefined;
20
+ export type FunctionEntry = readonly [name: string, arity: number | 'variadic'];
21
+ /** The built-in function an `iftab` index names, or `undefined` if the index names none. */
22
+ export declare function builtinFunctionAt(index: number): FunctionEntry | undefined;
23
+ /** Look up a built-in function name by its `iftab` index, or `undefined` if the index names none. */
24
+ export declare function functionNameFor(index: number): string | undefined;
25
+ export declare const FTAB: readonly (FunctionEntry | undefined)[];