@shbernal/ts-xlsx 1.2.0 → 2.0.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 (161) hide show
  1. package/README.md +5 -0
  2. package/dist/core/address.d.ts +49 -2
  3. package/dist/core/address.js +31 -3
  4. package/dist/core/autofilter.d.ts +2 -2
  5. package/dist/core/cell.d.ts +17 -16
  6. package/dist/core/cell.js +3 -7
  7. package/dist/core/color-resolution.d.ts +7 -7
  8. package/dist/core/column.d.ts +8 -9
  9. package/dist/core/column.js +2 -4
  10. package/dist/core/comment-thread.d.ts +32 -23
  11. package/dist/core/comment-thread.js +4 -4
  12. package/dist/core/conditional-formatting-overlay.d.ts +1 -1
  13. package/dist/core/conditional-formatting.d.ts +3 -3
  14. package/dist/core/data-validation-overlay.d.ts +1 -1
  15. package/dist/core/data-validation.d.ts +3 -3
  16. package/dist/core/date.d.ts +3 -3
  17. package/dist/core/formula.d.ts +6 -6
  18. package/dist/core/grid-edits.js +3 -3
  19. package/dist/core/image.d.ts +37 -2
  20. package/dist/core/image.js +6 -0
  21. package/dist/core/internal.d.ts +9 -1
  22. package/dist/core/limits.d.ts +4 -4
  23. package/dist/core/merge.d.ts +6 -12
  24. package/dist/core/merge.js +0 -3
  25. package/dist/core/page-setup.d.ts +4 -4
  26. package/dist/core/pivot-table.d.ts +2 -4
  27. package/dist/core/pivot-table.js +5 -5
  28. package/dist/core/preserved.d.ts +3 -3
  29. package/dist/core/protection.d.ts +4 -4
  30. package/dist/core/range.d.ts +16 -14
  31. package/dist/core/range.js +5 -5
  32. package/dist/core/row-input.d.ts +2 -8
  33. package/dist/core/row-input.js +2 -2
  34. package/dist/core/row.d.ts +7 -8
  35. package/dist/core/row.js +2 -4
  36. package/dist/core/style.d.ts +14 -14
  37. package/dist/core/table-style.d.ts +9 -9
  38. package/dist/core/table-style.js +1 -1
  39. package/dist/core/table.d.ts +30 -42
  40. package/dist/core/table.js +16 -10
  41. package/dist/core/theme.d.ts +13 -43
  42. package/dist/core/theme.js +2 -148
  43. package/dist/core/value.d.ts +7 -7
  44. package/dist/core/workbook-protection.d.ts +2 -2
  45. package/dist/core/workbook-styles.d.ts +26 -0
  46. package/dist/core/workbook-styles.js +48 -0
  47. package/dist/core/workbook-theme.d.ts +25 -0
  48. package/dist/core/workbook-theme.js +49 -0
  49. package/dist/core/workbook-vba.d.ts +17 -0
  50. package/dist/core/workbook-vba.js +79 -0
  51. package/dist/core/workbook.d.ts +141 -83
  52. package/dist/core/workbook.js +74 -133
  53. package/dist/core/worksheet-comments.d.ts +9 -0
  54. package/dist/core/worksheet-comments.js +52 -0
  55. package/dist/core/worksheet-model.d.ts +4 -4
  56. package/dist/core/worksheet-model.js +1 -0
  57. package/dist/core/worksheet-pictures.d.ts +29 -0
  58. package/dist/core/worksheet-pictures.js +42 -0
  59. package/dist/core/worksheet.d.ts +94 -77
  60. package/dist/core/worksheet.js +36 -95
  61. package/dist/customui/errors.d.ts +1 -1
  62. package/dist/customui/ribbon.d.ts +3 -3
  63. package/dist/entries/core.d.ts +4 -4
  64. package/dist/entries/core.js +1 -1
  65. package/dist/entries/vba.d.ts +1 -1
  66. package/dist/entries/vba.js +1 -1
  67. package/dist/entries/xlsx.d.ts +1 -0
  68. package/dist/entries/xlsx.js +1 -0
  69. package/dist/errors.d.ts +12 -12
  70. package/dist/io/csv/read.js +1 -2
  71. package/dist/io/csv/write.d.ts +8 -3
  72. package/dist/io/csv/write.js +17 -2
  73. package/dist/io/opc/errors.d.ts +6 -6
  74. package/dist/io/opc/inflate.d.ts +1 -1
  75. package/dist/io/opc/namespaces.d.ts +1 -1
  76. package/dist/io/opc/read-opc.d.ts +19 -3
  77. package/dist/io/opc/read-opc.js +14 -8
  78. package/dist/io/opc/read-options.d.ts +2 -2
  79. package/dist/io/opc/sniff-format.d.ts +2 -2
  80. package/dist/io/opc/zip-mtime.d.ts +5 -0
  81. package/dist/io/opc/zip-mtime.js +1 -0
  82. package/dist/io/style/xf-style.d.ts +38 -6
  83. package/dist/io/style/xf-style.js +22 -0
  84. package/dist/io/xlsb/errors.d.ts +1 -1
  85. package/dist/io/xlsb/formula.d.ts +4 -4
  86. package/dist/io/xlsb/primitives.d.ts +8 -8
  87. package/dist/io/xlsb/ptg-functions.d.ts +1 -1
  88. package/dist/io/xlsb/read-styles.js +2 -20
  89. package/dist/io/xlsb/read.d.ts +2 -2
  90. package/dist/io/xlsx/cell-accumulator.d.ts +25 -6
  91. package/dist/io/xlsx/cell-accumulator.js +111 -30
  92. package/dist/io/xlsx/cell-value.d.ts +2 -2
  93. package/dist/io/xlsx/cell-value.js +3 -3
  94. package/dist/io/xlsx/color-xml.js +11 -17
  95. package/dist/io/xlsx/comments.d.ts +5 -5
  96. package/dist/io/xlsx/comments.js +13 -6
  97. package/dist/io/xlsx/conditional-formatting.d.ts +3 -3
  98. package/dist/io/xlsx/conditional-formatting.js +7 -16
  99. package/dist/io/xlsx/data-validation.d.ts +2 -2
  100. package/dist/io/xlsx/data-validation.js +1 -1
  101. package/dist/io/xlsx/edit-vba.js +2 -1
  102. package/dist/io/xlsx/errors.d.ts +3 -3
  103. package/dist/io/xlsx/hyperlinks.d.ts +4 -2
  104. package/dist/io/xlsx/hyperlinks.js +5 -5
  105. package/dist/io/xlsx/images.d.ts +1 -1
  106. package/dist/io/xlsx/images.js +8 -8
  107. package/dist/io/xlsx/namespaces.d.ts +2 -2
  108. package/dist/io/xlsx/package-plan.js +5 -2
  109. package/dist/io/xlsx/read-pivot.js +2 -5
  110. package/dist/io/xlsx/read-rows.d.ts +13 -13
  111. package/dist/io/xlsx/read-rows.js +19 -55
  112. package/dist/io/xlsx/read-shared-strings.js +7 -5
  113. package/dist/io/xlsx/read-styles.d.ts +3 -3
  114. package/dist/io/xlsx/read-styles.js +40 -65
  115. package/dist/io/xlsx/read-worksheet.js +67 -115
  116. package/dist/io/xlsx/read.d.ts +3 -5
  117. package/dist/io/xlsx/read.js +94 -71
  118. package/dist/io/xlsx/rich-runs.d.ts +1 -1
  119. package/dist/io/xlsx/rich-runs.js +1 -1
  120. package/dist/io/xlsx/rich-text.d.ts +1 -1
  121. package/dist/io/xlsx/shared-strings.d.ts +2 -2
  122. package/dist/io/xlsx/sheet-properties.js +5 -4
  123. package/dist/io/xlsx/styles.d.ts +8 -9
  124. package/dist/io/xlsx/styles.js +4 -3
  125. package/dist/io/xlsx/tables.d.ts +2 -2
  126. package/dist/io/xlsx/tables.js +8 -13
  127. package/dist/io/xlsx/theme-xml.d.ts +37 -0
  128. package/dist/io/xlsx/theme-xml.js +142 -0
  129. package/dist/io/xlsx/threaded-comments.d.ts +18 -27
  130. package/dist/io/xlsx/threaded-comments.js +9 -21
  131. package/dist/io/xlsx/workbook-xml.d.ts +1 -1
  132. package/dist/io/xlsx/workbook-xml.js +6 -2
  133. package/dist/io/xlsx/worksheet-xml.d.ts +2 -2
  134. package/dist/io/xlsx/worksheet-xml.js +7 -12
  135. package/dist/io/xlsx/write-stream.d.ts +15 -15
  136. package/dist/io/xlsx/write-stream.js +5 -3
  137. package/dist/io/xlsx/write.d.ts +12 -7
  138. package/dist/io/xlsx/write.js +10 -6
  139. package/dist/io/xlsx/x14-ext.d.ts +1 -1
  140. package/dist/vba/bytes.d.ts +12 -0
  141. package/dist/vba/bytes.js +40 -0
  142. package/dist/vba/cfb-writer.d.ts +1 -1
  143. package/dist/vba/cfb.d.ts +1 -1
  144. package/dist/vba/cfb.js +15 -51
  145. package/dist/vba/codepage.d.ts +2 -2
  146. package/dist/vba/errors.d.ts +2 -2
  147. package/dist/vba/index.d.ts +2 -2
  148. package/dist/vba/index.js +2 -2
  149. package/dist/vba/ms-ovba.d.ts +1 -1
  150. package/dist/vba/ms-ovba.js +1 -3
  151. package/dist/vba/project-editor.d.ts +9 -9
  152. package/dist/vba/project-editor.js +2 -11
  153. package/dist/vba/project.d.ts +5 -5
  154. package/dist/vba/project.js +1 -10
  155. package/dist/vba/vba-encoding.d.ts +1 -1
  156. package/dist/xml/errors.d.ts +1 -1
  157. package/dist/xml/xml-read.d.ts +38 -9
  158. package/dist/xml/xml-read.js +23 -0
  159. package/dist/xml/xml.d.ts +31 -9
  160. package/dist/xml/xml.js +23 -3
  161. package/package.json +41 -35
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Modification time stamped on every zip entry, so package bytes depend on the workbook alone.
3
+ * Encodes as 2001-01-01 12:00:00 in the DOS date field on any machine, in any timezone.
4
+ */
5
+ export declare const FIXED_ENTRY_MTIME: number;
@@ -0,0 +1 @@
1
+ export const FIXED_ENTRY_MTIME = new Date(2001, 0, 1, 12, 0, 0, 0).getTime();
@@ -1,12 +1,12 @@
1
1
  import { type Cell } from '../../core/cell.ts';
2
- import type { CellStyle, Font, NamedCellStyle } from '../../core/style.ts';
2
+ import { type Border, type CellStyle, type Fill, type Font, type NamedCellStyle } from '../../core/style.ts';
3
3
  /**
4
4
  * What an xf resolves to: the {@link CellStyle} facet tuple, plus the two flags an xf carries that
5
5
  * are not facets. Absent facets stay undefined, matching the contract that an unset facet is simply
6
6
  * not present on the reconstructed cell.
7
7
  *
8
8
  * It *derives* the facets rather than listing them, so a seventh facet added to `CellStyle` reaches
9
- * both readers the moment it joins. Re-declaring them here the shape this replaced meant a new
9
+ * both readers the moment it joins. Re-declaring them here, the shape this replaced, meant a new
10
10
  * facet silently stopped at the model and never appeared in a file we read back.
11
11
  */
12
12
  export interface XfStyle extends CellStyle {
@@ -21,7 +21,7 @@ export interface StyleTable {
21
21
  readonly cellXfs: ReadonlyArray<XfStyle>;
22
22
  readonly namedStyles: ReadonlyArray<NamedCellStyle>;
23
23
  /**
24
- * Font id 0 the workbook's declared default font, the face every cell naming no font renders in.
24
+ * Font id 0: the workbook's declared default font, the face every cell naming no font renders in.
25
25
  * Surfaced separately from the fonts it was flattened onto because it is workbook-level state, not a
26
26
  * cell format: a re-write must emit *this* face as font 0 rather than an assumed Calibri, or every
27
27
  * empty cell changes face and every character-unit column width changes meaning. Absent when the
@@ -31,17 +31,49 @@ export interface StyleTable {
31
31
  }
32
32
  /**
33
33
  * The format code a number-format id denotes: the file's own `<numFmt>`/`BrtFmt` declaration if it
34
- * has one, else the built-in Excel defines for that id. Id 0 is General the absence of a format
34
+ * has one, else the built-in Excel defines for that id. Id 0 is General, the absence of a format,
35
35
  * and resolves to nothing so an ordinary cell carries no `numFmt`.
36
36
  */
37
37
  export declare function numFmtCodeFor(id: number, custom: ReadonlyMap<number, string>): string | undefined;
38
38
  /**
39
- * Apply a resolved xf's non-value facets to a cell the six {@link CellStyle} facets through the
39
+ * Apply a resolved xf's non-value facets to a cell: the six {@link CellStyle} facets through the
40
40
  * shared {@link applyCellStyle}, plus the two links that live on the xf itself rather than in the
41
41
  * facet tuple (`quotePrefix`, and the `xfId` pointer into the named-style layer).
42
42
  *
43
43
  * Shared by every path that commits a cell: the XML reader's ordinary and shared-formula-clone paths,
44
- * and the BIFF12 reader so a styled cell keeps its look regardless of which serialisation it came
44
+ * and the BIFF12 reader, so a styled cell keeps its look regardless of which serialisation it came
45
45
  * from, and the two cannot drift on what "applying a style" means.
46
46
  */
47
47
  export declare function applyXfToCell(cell: Cell, style: XfStyle | undefined): void;
48
+ /**
49
+ * A `<cellStyle>` / `BrtStyle` label: the name and builtinId that title one `cellStyleXfs` entry,
50
+ * keyed to that entry's index.
51
+ */
52
+ export interface StyleLabel {
53
+ readonly xfId: number;
54
+ readonly name?: string;
55
+ readonly builtinId?: number;
56
+ }
57
+ /** The sub-tables an xf resolves its facet ids against, in whichever spelling the codec parsed them. */
58
+ export interface XfDeps {
59
+ readonly fonts: ReadonlyArray<Font | undefined>;
60
+ readonly fills: ReadonlyArray<Fill | undefined>;
61
+ readonly borders: ReadonlyArray<Border | undefined>;
62
+ readonly numFmtCodes: ReadonlyMap<number, string>;
63
+ }
64
+ /**
65
+ * Turn the four tables a style-sheet parse yields into the {@link StyleTable} both codecs hand back:
66
+ * layer each direct xf over the named style it links to, title the named layer with its labels, and
67
+ * carry font 0 out as the workbook default.
68
+ *
69
+ * A facet the direct xf sets wins; one it leaves unset falls through to the named base; and the
70
+ * `xfId` link is carried through so a re-write keeps it. None of that depends on whether the tables
71
+ * were parsed out of `xl/styles.xml` or `xl/styles.bin`, which is the point of stating it once: the
72
+ * two readers used to hold a copy each, cross-referenced by a comment saying they agreed.
73
+ */
74
+ export declare function resolveStyleTable(tables: {
75
+ readonly directXfs: ReadonlyArray<XfStyle>;
76
+ readonly namedXfs: ReadonlyArray<XfStyle>;
77
+ readonly labels: ReadonlyArray<StyleLabel>;
78
+ readonly fonts: ReadonlyArray<Font | undefined>;
79
+ }): StyleTable;
@@ -1,5 +1,6 @@
1
1
  import { applyCellStyle } from '../../core/cell.js';
2
2
  import { NAMED_STYLE_ID } from '../../core/internal.js';
3
+ import { assignStyleFacets, } from '../../core/style.js';
3
4
  const BUILTIN_NUMFMTS = new Map([
4
5
  [1, '0'],
5
6
  [2, '0.00'],
@@ -62,3 +63,24 @@ export function applyXfToCell(cell, style) {
62
63
  if (style.xfId !== undefined)
63
64
  cell[NAMED_STYLE_ID] = style.xfId;
64
65
  }
66
+ export function resolveStyleTable(tables) {
67
+ const { directXfs, namedXfs, labels, fonts } = tables;
68
+ const cellXfs = directXfs.map((xf) => {
69
+ if (xf.xfId === undefined)
70
+ return xf;
71
+ const named = namedXfs[xf.xfId];
72
+ return named === undefined ? xf : { ...named, ...xf };
73
+ });
74
+ const namedStyles = namedXfs.map((xf, index) => {
75
+ const label = labels.find((entry) => entry.xfId === index);
76
+ const style = {};
77
+ assignStyleFacets(style, xf);
78
+ if (label?.name !== undefined)
79
+ style.name = label.name;
80
+ if (label?.builtinId !== undefined)
81
+ style.builtinId = label.builtinId;
82
+ return style;
83
+ });
84
+ const defaultFont = fonts[0];
85
+ return defaultFont === undefined ? { cellXfs, namedStyles } : { cellXfs, namedStyles, defaultFont };
86
+ }
@@ -1,6 +1,6 @@
1
1
  import { XlsxError } from '../../errors.ts';
2
2
  /**
3
- * Thrown when an `.xlsb` package's binary BIFF12 content cannot be parsed a record whose declared
3
+ * Thrown when an `.xlsb` package's binary BIFF12 content cannot be parsed: a record whose declared
4
4
  * size runs past the end of its part, a truncated record header, or a structure that does not conform
5
5
  * to [MS-XLSB].
6
6
  *
@@ -7,16 +7,16 @@ export interface ExternSheetRef {
7
7
  }
8
8
  /** The workbook-level tables a formula's references and names resolve through. */
9
9
  export interface FormulaScope {
10
- /** Sheet names in workbook (tab) order what an `Xti`'s sheet indices point into. */
10
+ /** Sheet names in workbook (tab) order: what an `Xti`'s sheet indices point into. */
11
11
  readonly sheetNames: readonly string[];
12
12
  /** The `BrtExternSheet` table, indexed by a 3-D token's `ixti`. */
13
13
  readonly externSheets: readonly ExternSheetRef[];
14
14
  /** The index of the supporting book that is this workbook, or `undefined` when the file declares a
15
15
  * supporting book this reader does not recognise. A workbook with no external links declares
16
- * exactly one itself so this is the ordinary case; anything else leaves the indices untrustworthy
16
+ * exactly one, itself, so this is the ordinary case; anything else leaves the indices untrustworthy
17
17
  * and no 3-D reference resolves, which drops those formulas rather than naming the wrong sheet. */
18
18
  readonly selfSupBook: number | undefined;
19
- /** Every `BrtName` in file order, function placeholders included a `PtgName` cites one by
19
+ /** Every `BrtName` in file order, function placeholders included. A `PtgName` cites one by
20
20
  * **1-based** index, so filtering this list would misaddress every name reference. */
21
21
  readonly names: readonly string[];
22
22
  }
@@ -28,7 +28,7 @@ export interface FormulaAnchor {
28
28
  }
29
29
  /**
30
30
  * Decode a `CellParsedFormula`'s token stream into formula text, in the same on-disk spelling the XML
31
- * form writes into `<f>` `_xlfn.`-prefixed function names included, so the caller applies the same
31
+ * form writes into `<f>`, `_xlfn.`-prefixed function names included, so the caller applies the same
32
32
  * `unmangleFunctions` normalisation to either serialisation.
33
33
  *
34
34
  * @param rgce the token stream.
@@ -7,7 +7,7 @@ export interface CellHeader {
7
7
  /** Zero-based index into the style sheet's cell-XF collection. */
8
8
  readonly styleIndex: number;
9
9
  }
10
- /** An `UncheckedRfX` ([MS-XLSB] 2.5.155) cell range all four bounds zero-based and inclusive. */
10
+ /** An `UncheckedRfX` ([MS-XLSB] 2.5.155) cell range, all four bounds zero-based and inclusive. */
11
11
  export interface RangeBounds {
12
12
  readonly rowFirst: number;
13
13
  readonly rowLast: number;
@@ -18,7 +18,7 @@ export interface RangeBounds {
18
18
  * A bounds-checked cursor over one record's payload.
19
19
  *
20
20
  * Each accessor advances the cursor by exactly the bytes it consumed, so a record is decoded by
21
- * naming its fields in order. Reading past the payload throws {@link XlsbParseError} a record that
21
+ * naming its fields in order. Reading past the payload throws {@link XlsbParseError}: a record that
22
22
  * is shorter than its own definition is a malformed file, not a case to guess through.
23
23
  */
24
24
  export declare class RecordReader {
@@ -28,10 +28,10 @@ export declare class RecordReader {
28
28
  get remaining(): number;
29
29
  /** Whether the whole payload has been consumed. */
30
30
  get done(): boolean;
31
- /** Advance past `count` bytes without decoding them a reserved or unmodelled field. */
31
+ /** Advance past `count` bytes without decoding them: a reserved or unmodelled field. */
32
32
  skip(count: number): void;
33
33
  /**
34
- * The next `count` bytes as a **view**, for a field whose own decoding happens elsewhere a formula
34
+ * The next `count` bytes as a **view**, for a field whose own decoding happens elsewhere: a formula
35
35
  * token stream, whose meaning depends on workbook tables this record knows nothing about. A view
36
36
  * rather than a copy for the same reason a record's payload is one: the declared length comes from
37
37
  * the file, so it must bound a read, never an allocation.
@@ -46,7 +46,7 @@ export declare class RecordReader {
46
46
  f64(): number;
47
47
  /**
48
48
  * An `RkNumber` ([MS-XLSB] 2.5.122): a number packed into 32 bits. Two flag bits steal the low end
49
- * of the word `fInt` says the remaining 30 bits are a signed integer rather than the *high* 30
49
+ * of the word: `fInt` says the remaining 30 bits are a signed integer rather than the *high* 30
50
50
  * bits of a double whose low 34 bits are zero, and `fX100` says the result was scaled up by 100 to
51
51
  * keep two decimal places in the integer form. It exists because most real spreadsheet numbers are
52
52
  * small integers or two-decimal currency, and this stores them in half the bytes of a double.
@@ -55,7 +55,7 @@ export declare class RecordReader {
55
55
  /** An `XLWideString` ([MS-XLSB] 2.5.169): a 4-byte character count then that many UTF-16LE units. */
56
56
  wideString(): string;
57
57
  /**
58
- * A UTF-16 string whose character count is 16-bit rather than 32-bit the form used *inside* a
58
+ * A UTF-16 string whose character count is 16-bit rather than 32-bit: the form used *inside* a
59
59
  * formula token stream (`PtgStr`, and the string elements of an array constant), where a 4-byte
60
60
  * count on every literal would be pure overhead.
61
61
  */
@@ -64,7 +64,7 @@ export declare class RecordReader {
64
64
  nullableWideString(): string | undefined;
65
65
  /**
66
66
  * A `RichStr` ([MS-XLSB] 2.5.124): a string that may carry per-run formatting and phonetic guides.
67
- * Only the text is returned the run and phonetic tails are left unread, which is safe because the
67
+ * Only the text is returned; the run and phonetic tails are left unread, which is safe because the
68
68
  * record's framing (not this cursor) bounds where the payload ends.
69
69
  */
70
70
  richString(): string;
@@ -77,7 +77,7 @@ export declare class RecordReader {
77
77
  *
78
78
  * The four encodings are mutually exclusive and the type tag picks which of the payload's fields
79
79
  * carry meaning; the rest are explicitly undefined. An *automatic* colour (type 0) names nothing at
80
- * all, and reads back as no colour the same absence the XML reader produces for `<color auto="1"/>`,
80
+ * all, and reads back as no colour: the same absence the XML reader produces for `<color auto="1"/>`,
81
81
  * so a cell whose font colour was never set does not gain one on read.
82
82
  */
83
83
  color(): Color | undefined;
@@ -5,7 +5,7 @@ export declare function functionNameFor(index: number): string | undefined;
5
5
  /**
6
6
  * How many arguments a fixed-arity function takes, or `undefined` for one whose argument count varies.
7
7
  *
8
- * A `PtgFunc` token states only *which* function is called, never with how many arguments it is the
8
+ * A `PtgFunc` token states only *which* function is called, never with how many arguments: it is the
9
9
  * token Excel emits precisely because the count is implied by the function. So decoding `ROUND(A1,2)`
10
10
  * back from its two operands and one call token is impossible without this: the arity is the only
11
11
  * thing that says which operands on the stack belong to the call. (`PtgFuncVar` carries its own count
@@ -1,5 +1,4 @@
1
- import { assignStyleFacets } from '../../core/style.js';
2
- import { numFmtCodeFor } from '../style/xf-style.js';
1
+ import { numFmtCodeFor, resolveStyleTable, } from '../style/xf-style.js';
3
2
  import { RecordReader } from './primitives.js';
4
3
  import { readRecords } from './record-stream.js';
5
4
  import { BRT } from './record-types.js';
@@ -74,24 +73,7 @@ export function parseStyleTable(part) {
74
73
  break;
75
74
  }
76
75
  }
77
- const cellXfs = directXfs.map((xf) => {
78
- if (xf.xfId === undefined)
79
- return xf;
80
- const named = namedXfs[xf.xfId];
81
- return named === undefined ? xf : { ...named, ...xf };
82
- });
83
- const namedStyles = namedXfs.map((xf, index) => {
84
- const label = labels.find((entry) => entry.xfId === index);
85
- const style = {};
86
- assignStyleFacets(style, xf);
87
- if (label?.name !== undefined)
88
- style.name = label.name;
89
- if (label?.builtinId !== undefined)
90
- style.builtinId = label.builtinId;
91
- return style;
92
- });
93
- const defaultFont = fonts[0];
94
- return defaultFont === undefined ? { cellXfs, namedStyles } : { cellXfs, namedStyles, defaultFont };
76
+ return resolveStyleTable({ directXfs, namedXfs, labels, fonts });
95
77
  }
96
78
  function readXf(reader, deps, isDirect) {
97
79
  const parent = reader.u16();
@@ -5,10 +5,10 @@ export declare const XLSB_WORKBOOK_PART = "xl/workbook.bin";
5
5
  /**
6
6
  * Read an `.xlsb` (binary BIFF12) package into a {@link Workbook}.
7
7
  *
8
- * @throws {UnsupportedFormatError} if the input is not an `.xlsb` package a legacy `.xls`
8
+ * @throws {UnsupportedFormatError} if the input is not an `.xlsb` package: a legacy `.xls`
9
9
  * (`.format === 'xls'`), an XML `.xlsx` or unrecognised blob (`'unknown'`).
10
10
  * @throws {XlsbParseError} if a binary part is malformed.
11
- * @throws {PackageReadError} if the input is a ZIP that cannot be unpacked a corrupt or
11
+ * @throws {PackageReadError} if the input is a ZIP that cannot be unpacked: a corrupt or
12
12
  * truncated archive, or one exceeding the inflate bound (a probable zip bomb).
13
13
  */
14
14
  export declare function readXlsb(data: Uint8Array, options?: ReadXlsxOptions): Workbook;
@@ -6,6 +6,9 @@ import { type SharedString } from './cell-value.ts';
6
6
  import { RunAccumulator } from './rich-runs.ts';
7
7
  export declare class CellAccumulator {
8
8
  #private;
9
+ constructor(options: {
10
+ readonly richRuns: boolean;
11
+ });
9
12
  /** This cell's `<c r>` address (`"B3"`), or '' when it carried none. */
10
13
  get ref(): string;
11
14
  /** This cell's own `<c s>` style index, or -1 when it carries none. */
@@ -14,12 +17,28 @@ export declare class CellAccumulator {
14
17
  get col(): number;
15
18
  /** The rich-text run accumulator, driven by the surrounding parser's `<r>`/`<rPr>` handling. */
16
19
  get runs(): RunAccumulator;
17
- beginCell(attrs: XmlAttributes): void;
18
- beginFormula(attrs: XmlAttributes, selfClosing: boolean): void;
19
- setFormula(text: string): void;
20
- setValue(text: string): void;
21
- beginInlineString(): void;
22
- appendText(text: string, inInlineString: boolean): void;
20
+ /**
21
+ * The text gathered since the current element opened. A caller reads it for the elements it
22
+ * captures itself (see {@link capture}); the machine reads it for its own.
23
+ */
24
+ get capturedText(): string;
25
+ /** Gather the current element's text for the caller's own use, the way `<v>` and `<t>` do. */
26
+ capture(): void;
27
+ /**
28
+ * Drive one element open, and return whether it was one of the cell machine's own. Every open
29
+ * resets the capture state first, which is true of both readers and of every element, not just
30
+ * these; a caller that captures its own text calls {@link capture} after this returns.
31
+ */
32
+ openElement(local: string, attrs: XmlAttributes, selfClosing: boolean): boolean;
33
+ /** Feed one run of character data. Ignored unless something is capturing. */
34
+ appendChunk(chunk: string): void;
35
+ /**
36
+ * Drive one element close. `'cell'` means a `</c>` closed and the caller should commit the
37
+ * gathered cell, which is the one step the two readers do differently; `'claimed'` means the
38
+ * machine handled it; `'other'` leaves it to the caller. Capture always ends here, as it does on
39
+ * every close in both readers.
40
+ */
41
+ closeElement(local: string): 'cell' | 'claimed' | 'other';
23
42
  finalize(sheet: Worksheet, sharedStrings: readonly SharedString[], style: XfStyle | undefined): void;
24
43
  decode(sharedStrings: readonly SharedString[], style: XfStyle | undefined): CellValue;
25
44
  }
@@ -1,6 +1,6 @@
1
1
  import { decodeAddress, encodeAddress } from '../../core/address.js';
2
2
  import { translateFormula, unmangleFunctions } from '../../core/formula.js';
3
- import { boolPresent } from '../../xml/xml-read.js';
3
+ import { boolStrict, decodeSpreadsheetText, numInteger, } from '../../xml/xml-read.js';
4
4
  import { applyXfToCell } from '../style/xf-style.js';
5
5
  import { decodeCellContent, decodeFormulaResult, } from './cell-value.js';
6
6
  import { RunAccumulator } from './rich-runs.js';
@@ -21,6 +21,13 @@ export class CellAccumulator {
21
21
  #hasValue = false;
22
22
  #runs = new RunAccumulator();
23
23
  #masters = new Map();
24
+ #inInlineString = false;
25
+ #capture = false;
26
+ #text = '';
27
+ #richRuns;
28
+ constructor(options) {
29
+ this.#richRuns = options.richRuns;
30
+ }
24
31
  get ref() {
25
32
  return this.#ref;
26
33
  }
@@ -33,16 +40,17 @@ export class CellAccumulator {
33
40
  get runs() {
34
41
  return this.#runs;
35
42
  }
36
- beginCell(attrs) {
43
+ #beginCell(attrs) {
37
44
  this.#ref = attrs.r ?? '';
38
45
  this.#type = attrs.t ?? '';
39
- this.#style = attrs.s !== undefined ? Number(attrs.s) : -1;
40
- this.#col = this.#ref === '' ? -1 : (decodeAddress(this.#ref).col ?? -1);
41
- this.#row = this.#ref === '' ? -1 : (decodeAddress(this.#ref).row ?? -1);
46
+ this.#style = numInteger(attrs.s, 0) ?? -1;
47
+ const decoded = this.#ref === '' ? undefined : decodeAddress(this.#ref);
48
+ this.#col = decoded?.col ?? -1;
49
+ this.#row = decoded?.row ?? -1;
42
50
  this.#formula = '';
43
51
  this.#valueText = '';
44
52
  this.#inlineText = '';
45
- this.#runs.reset();
53
+ this.#runs.beginContainer();
46
54
  this.#hasFormula = false;
47
55
  this.#hasValue = false;
48
56
  this.#formulaShared = false;
@@ -50,9 +58,9 @@ export class CellAccumulator {
50
58
  this.#sharedClone = false;
51
59
  this.#dataTable = null;
52
60
  }
53
- beginFormula(attrs, selfClosing) {
61
+ #beginFormula(attrs, selfClosing) {
54
62
  this.#formulaShared = attrs.t === 'shared';
55
- this.#formulaSi = attrs.si !== undefined ? Number(attrs.si) : -1;
63
+ this.#formulaSi = numInteger(attrs.si, 0) ?? -1;
56
64
  if (selfClosing && this.#formulaShared)
57
65
  this.#sharedClone = true;
58
66
  if (attrs.t === 'dataTable' && attrs.ref !== undefined) {
@@ -65,41 +73,120 @@ export class CellAccumulator {
65
73
  };
66
74
  }
67
75
  }
68
- setFormula(text) {
76
+ #setFormula(text) {
69
77
  this.#formula = text;
70
78
  this.#hasFormula = true;
71
79
  }
72
- setValue(text) {
80
+ #setValue(text) {
73
81
  this.#valueText = text;
74
82
  this.#hasValue = true;
75
83
  }
76
- beginInlineString() {
84
+ #beginInlineString() {
77
85
  this.#inlineText = '';
78
- this.#runs.reset();
86
+ this.#runs.beginContainer();
87
+ }
88
+ #appendText(text) {
89
+ const decoded = decodeSpreadsheetText(text);
90
+ if (!this.#runs.appendText(decoded) && this.#inInlineString)
91
+ this.#inlineText += decoded;
92
+ }
93
+ get capturedText() {
94
+ return this.#text;
95
+ }
96
+ capture() {
97
+ this.#capture = true;
79
98
  }
80
- appendText(text, inInlineString) {
81
- if (!this.#runs.appendText(text) && inInlineString)
82
- this.#inlineText += text;
99
+ openElement(local, attrs, selfClosing) {
100
+ this.#text = '';
101
+ this.#capture = false;
102
+ switch (local) {
103
+ case 'c':
104
+ this.#beginCell(attrs);
105
+ return true;
106
+ case 'is':
107
+ this.#inInlineString = true;
108
+ this.#beginInlineString();
109
+ return true;
110
+ case 'f':
111
+ this.#capture = !selfClosing;
112
+ this.#beginFormula(attrs, selfClosing);
113
+ return true;
114
+ case 'v':
115
+ this.#capture = !selfClosing;
116
+ return true;
117
+ case 't':
118
+ this.#capture = true;
119
+ return true;
120
+ case 'r':
121
+ if (!this.#richRuns)
122
+ return false;
123
+ if (this.#inInlineString)
124
+ this.#runs.beginRun();
125
+ return true;
126
+ case 'rPr':
127
+ if (!this.#richRuns)
128
+ return false;
129
+ this.#runs.beginProperties();
130
+ return true;
131
+ default:
132
+ return false;
133
+ }
134
+ }
135
+ appendChunk(chunk) {
136
+ if (this.#capture)
137
+ this.#text += chunk;
138
+ }
139
+ closeElement(local) {
140
+ const verdict = this.#closeElement(local);
141
+ this.#capture = false;
142
+ return verdict;
143
+ }
144
+ #closeElement(local) {
145
+ switch (local) {
146
+ case 'f':
147
+ this.#setFormula(this.#text);
148
+ return 'claimed';
149
+ case 'v':
150
+ this.#setValue(this.#text);
151
+ return 'claimed';
152
+ case 't':
153
+ this.#appendText(this.#text);
154
+ return 'claimed';
155
+ case 'r':
156
+ if (!this.#richRuns)
157
+ return 'other';
158
+ this.#runs.endRun();
159
+ return 'claimed';
160
+ case 'is':
161
+ this.#inInlineString = false;
162
+ return 'claimed';
163
+ case 'c':
164
+ return 'cell';
165
+ default:
166
+ return 'other';
167
+ }
83
168
  }
84
169
  finalize(sheet, sharedStrings, style) {
85
170
  if (this.#ref === '')
86
171
  return;
172
+ const value = this.#resolveValue(sharedStrings, style);
173
+ const cell = sheet.getCell(this.#ref);
174
+ applyXfToCell(cell, style);
175
+ cell.value = value;
176
+ }
177
+ #resolveValue(sharedStrings, style) {
87
178
  if (this.#dataTable !== null) {
88
- const value = {
179
+ return {
89
180
  shareType: 'dataTable',
90
181
  ref: this.#dataTable.ref,
91
- ...(boolPresent(this.#dataTable.dt2D ?? '0') ? { dataTable2D: true } : {}),
92
- ...(boolPresent(this.#dataTable.dtr ?? '0') ? { dataTableRow: true } : {}),
182
+ ...(boolStrict(this.#dataTable.dt2D) ? { dataTable2D: true } : {}),
183
+ ...(boolStrict(this.#dataTable.dtr) ? { dataTableRow: true } : {}),
93
184
  ...(this.#dataTable.r1 !== undefined ? { r1: this.#dataTable.r1 } : {}),
94
185
  ...(this.#dataTable.r2 !== undefined ? { r2: this.#dataTable.r2 } : {}),
95
186
  ...(this.#hasValue
96
187
  ? { result: decodeFormulaResult(this.#type, this.#valueText, style?.numFmt) }
97
188
  : {}),
98
189
  };
99
- const cell = sheet.getCell(this.#ref);
100
- applyXfToCell(cell, style);
101
- cell.value = value;
102
- return;
103
190
  }
104
191
  if (this.#hasFormula && this.#formulaShared && this.#formulaSi >= 0) {
105
192
  this.#masters.set(this.#formulaSi, { formula: this.#formula, col: this.#col, row: this.#row });
@@ -108,22 +195,16 @@ export class CellAccumulator {
108
195
  const master = this.#masters.get(this.#formulaSi);
109
196
  if (master !== undefined) {
110
197
  const translated = translateFormula(master.formula, this.#col - master.col, this.#row - master.row);
111
- const value = {
198
+ return {
112
199
  sharedFormula: encodeAddress(master.col, master.row),
113
200
  formula: unmangleFunctions(translated),
114
201
  ...(this.#hasValue
115
202
  ? { result: decodeFormulaResult(this.#type, this.#valueText, style?.numFmt) }
116
203
  : {}),
117
204
  };
118
- const cell = sheet.getCell(this.#ref);
119
- applyXfToCell(cell, style);
120
- cell.value = value;
121
- return;
122
205
  }
123
206
  }
124
- const cell = sheet.getCell(this.#ref);
125
- applyXfToCell(cell, style);
126
- cell.value = this.decode(sharedStrings, style);
207
+ return this.decode(sharedStrings, style);
127
208
  }
128
209
  decode(sharedStrings, style) {
129
210
  const raw = {
@@ -1,7 +1,7 @@
1
1
  import { type CellValue, type FormulaResult, type RichTextRun, type RichTextValue } from '../../core/value.ts';
2
2
  /**
3
3
  * One entry of the shared-strings pool. A `<si>` built from a bare `<t>` is a plain string; a `<si>`
4
- * built from `<r>` runs is rich text so a `t="s"` cell can resolve to either kind, and rich text
4
+ * built from `<r>` runs is rich text, so a `t="s"` cell can resolve to either kind, and rich text
5
5
  * that Excel pooled reads back with its per-run formatting intact rather than flattened to text.
6
6
  */
7
7
  export type SharedString = string | RichTextValue;
@@ -26,7 +26,7 @@ export interface RawCell {
26
26
  */
27
27
  export declare function decodeCellContent(raw: RawCell, sharedStrings: readonly SharedString[], numFmt: string | undefined): CellValue;
28
28
  /** Decode a formula's cached `<v>` result by its `t` type, coercing a numeric result under a date
29
- * `numFmt` to a {@link Date} exactly as a bare numeric cell is so a date-valued formula result
29
+ * `numFmt` to a {@link Date} exactly as a bare numeric cell is, so a date-valued formula result
30
30
  * (e.g. `TODAY()`) reads back as a Date, not a serial. Shared by the buffered reader's shared-formula
31
31
  * clone resolution, which caches a result the same way a plain formula cell does. */
32
32
  export declare function decodeFormulaResult(type: string, valueText: string, numFmt?: string): FormulaResult;
@@ -1,7 +1,7 @@
1
1
  import { isDateFormat, serialToDate } from '../../core/date.js';
2
2
  import { unmangleFunctions } from '../../core/formula.js';
3
3
  import { isErrorCode, } from '../../core/value.js';
4
- import { boolStrict } from '../../xml/xml-read.js';
4
+ import { boolStrict, decodeSpreadsheetText } from '../../xml/xml-read.js';
5
5
  export function decodeCellContent(raw, sharedStrings, numFmt) {
6
6
  if (raw.hasFormula) {
7
7
  const stored = unmangleFunctions(raw.formula);
@@ -21,7 +21,7 @@ function decodeValue(type, valueText, inlineText, hasValue, sharedStrings) {
21
21
  case 'inlineStr':
22
22
  return inlineText;
23
23
  case 'str':
24
- return valueText;
24
+ return decodeSpreadsheetText(valueText);
25
25
  case 'd':
26
26
  return valueText === '' ? null : new Date(valueText);
27
27
  case 's': {
@@ -45,7 +45,7 @@ export function decodeFormulaResult(type, valueText, numFmt) {
45
45
  function decodeResult(type, valueText) {
46
46
  switch (type) {
47
47
  case 'str':
48
- return valueText;
48
+ return decodeSpreadsheetText(valueText);
49
49
  case 'b':
50
50
  return boolStrict(valueText);
51
51
  case 'e':
@@ -1,9 +1,9 @@
1
- import { AuthoringError } from '../../errors.js';
1
+ import { numFinite, numInteger } from '../../xml/xml-read.js';
2
2
  function normalizeArgb(argb) {
3
3
  const hex = argb.startsWith('#') ? argb.slice(1) : argb;
4
4
  const rgb = hex.length === 6 ? `FF${hex}` : hex;
5
5
  if (!/^[0-9a-fA-F]{8}$/.test(rgb)) {
6
- throw new AuthoringError(`Invalid ARGB colour ${JSON.stringify(argb)}: expected 6 or 8 hexadecimal digits`);
6
+ throw new SyntaxError(`Invalid ARGB colour ${JSON.stringify(argb)}: expected 6 or 8 hexadecimal digits`);
7
7
  }
8
8
  return rgb;
9
9
  }
@@ -23,20 +23,14 @@ export function parseColor(attrs) {
23
23
  const color = {};
24
24
  if (attrs.rgb !== undefined)
25
25
  color.argb = attrs.rgb;
26
- if (attrs.theme !== undefined) {
27
- const theme = Number(attrs.theme);
28
- if (Number.isInteger(theme))
29
- color.theme = theme;
30
- }
31
- if (attrs.tint !== undefined) {
32
- const tint = Number(attrs.tint);
33
- if (Number.isFinite(tint))
34
- color.tint = tint;
35
- }
36
- if (attrs.indexed !== undefined) {
37
- const indexed = Number(attrs.indexed);
38
- if (Number.isInteger(indexed))
39
- color.indexed = indexed;
40
- }
26
+ const theme = numInteger(attrs.theme, 0);
27
+ if (theme !== undefined)
28
+ color.theme = theme;
29
+ const tint = numFinite(attrs.tint);
30
+ if (tint !== undefined)
31
+ color.tint = tint;
32
+ const indexed = numInteger(attrs.indexed, 0);
33
+ if (indexed !== undefined)
34
+ color.indexed = indexed;
41
35
  return color;
42
36
  }