@shbernal/ts-xlsx 1.3.1 → 2.1.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 (186) hide show
  1. package/README.md +20 -10
  2. package/dist/bytes.d.ts +24 -0
  3. package/dist/bytes.js +34 -0
  4. package/dist/core/address.d.ts +72 -3
  5. package/dist/core/address.js +50 -10
  6. package/dist/core/autofilter.d.ts +13 -2
  7. package/dist/core/autofilter.js +35 -10
  8. package/dist/core/cell.d.ts +17 -16
  9. package/dist/core/cell.js +3 -7
  10. package/dist/core/color-resolution.d.ts +8 -8
  11. package/dist/core/color-resolution.js +7 -8
  12. package/dist/core/column.d.ts +8 -9
  13. package/dist/core/column.js +2 -4
  14. package/dist/core/comment-thread.d.ts +32 -23
  15. package/dist/core/comment-thread.js +4 -4
  16. package/dist/core/conditional-formatting-overlay.d.ts +6 -1
  17. package/dist/core/conditional-formatting-overlay.js +11 -0
  18. package/dist/core/conditional-formatting.d.ts +48 -8
  19. package/dist/core/conditional-formatting.js +88 -0
  20. package/dist/core/data-validation-overlay.d.ts +12 -1
  21. package/dist/core/data-validation-overlay.js +21 -2
  22. package/dist/core/data-validation.d.ts +12 -5
  23. package/dist/core/data-validation.js +34 -0
  24. package/dist/core/date.d.ts +3 -3
  25. package/dist/core/formula.d.ts +6 -6
  26. package/dist/core/grid-edits.d.ts +16 -0
  27. package/dist/core/grid-edits.js +29 -11
  28. package/dist/core/grid-shift.d.ts +19 -0
  29. package/dist/core/grid-shift.js +8 -0
  30. package/dist/core/image.d.ts +39 -2
  31. package/dist/core/image.js +10 -0
  32. package/dist/core/internal.d.ts +9 -1
  33. package/dist/core/limits.d.ts +4 -4
  34. package/dist/core/merge.d.ts +17 -12
  35. package/dist/core/merge.js +36 -5
  36. package/dist/core/page-setup.d.ts +20 -6
  37. package/dist/core/page-setup.js +8 -1
  38. package/dist/core/pivot-table.d.ts +4 -4
  39. package/dist/core/pivot-table.js +29 -20
  40. package/dist/core/preserved.d.ts +3 -3
  41. package/dist/core/protection.d.ts +7 -4
  42. package/dist/core/protection.js +20 -14
  43. package/dist/core/range.d.ts +16 -14
  44. package/dist/core/range.js +5 -5
  45. package/dist/core/row-input.d.ts +9 -8
  46. package/dist/core/row-input.js +12 -10
  47. package/dist/core/row.d.ts +7 -8
  48. package/dist/core/row.js +2 -4
  49. package/dist/core/style.d.ts +41 -14
  50. package/dist/core/style.js +82 -67
  51. package/dist/core/table-style.d.ts +9 -9
  52. package/dist/core/table-style.js +1 -1
  53. package/dist/core/table.d.ts +53 -51
  54. package/dist/core/table.js +83 -41
  55. package/dist/core/theme.d.ts +16 -44
  56. package/dist/core/theme.js +6 -148
  57. package/dist/core/value.d.ts +7 -7
  58. package/dist/core/workbook-protection.d.ts +2 -2
  59. package/dist/core/workbook-styles.d.ts +26 -0
  60. package/dist/core/workbook-styles.js +48 -0
  61. package/dist/core/workbook-theme.d.ts +25 -0
  62. package/dist/core/workbook-theme.js +49 -0
  63. package/dist/core/workbook-vba.d.ts +17 -0
  64. package/dist/core/workbook-vba.js +79 -0
  65. package/dist/core/workbook.d.ts +133 -87
  66. package/dist/core/workbook.js +74 -133
  67. package/dist/core/worksheet-comments.d.ts +17 -0
  68. package/dist/core/worksheet-comments.js +70 -0
  69. package/dist/core/worksheet-model.d.ts +4 -4
  70. package/dist/core/worksheet-model.js +1 -0
  71. package/dist/core/worksheet-pictures.d.ts +29 -0
  72. package/dist/core/worksheet-pictures.js +42 -0
  73. package/dist/core/worksheet.d.ts +100 -79
  74. package/dist/core/worksheet.js +63 -142
  75. package/dist/customui/errors.d.ts +1 -1
  76. package/dist/customui/ribbon.d.ts +3 -3
  77. package/dist/customui/ribbon.js +24 -23
  78. package/dist/entries/core.d.ts +7 -7
  79. package/dist/entries/core.js +1 -1
  80. package/dist/entries/csv.d.ts +1 -1
  81. package/dist/entries/node-unavailable.d.ts +15 -0
  82. package/dist/entries/node-unavailable.js +20 -0
  83. package/dist/entries/node.d.ts +1 -0
  84. package/dist/entries/node.js +1 -0
  85. package/dist/entries/vba.d.ts +1 -1
  86. package/dist/entries/vba.js +1 -1
  87. package/dist/entries/xlsx.d.ts +1 -1
  88. package/dist/entries/xlsx.js +1 -1
  89. package/dist/errors.d.ts +12 -12
  90. package/dist/io/csv/read.js +2 -3
  91. package/dist/io/csv/write.d.ts +19 -4
  92. package/dist/io/csv/write.js +34 -4
  93. package/dist/io/opc/errors.d.ts +6 -6
  94. package/dist/io/opc/inflate.d.ts +1 -1
  95. package/dist/io/opc/inflate.js +2 -12
  96. package/dist/io/opc/namespaces.d.ts +1 -1
  97. package/dist/io/opc/read-opc.d.ts +19 -3
  98. package/dist/io/opc/read-opc.js +14 -8
  99. package/dist/io/opc/read-options.d.ts +2 -2
  100. package/dist/io/opc/rels.d.ts +11 -0
  101. package/dist/io/opc/rels.js +3 -2
  102. package/dist/io/opc/sniff-format.d.ts +2 -2
  103. package/dist/io/style/xf-style.d.ts +44 -7
  104. package/dist/io/style/xf-style.js +27 -0
  105. package/dist/io/xlsb/errors.d.ts +1 -1
  106. package/dist/io/xlsb/formula.d.ts +4 -4
  107. package/dist/io/xlsb/primitives.d.ts +8 -8
  108. package/dist/io/xlsb/ptg-functions.d.ts +1 -1
  109. package/dist/io/xlsb/read-styles.js +2 -20
  110. package/dist/io/xlsb/read.d.ts +2 -2
  111. package/dist/io/xlsx/cell-accumulator.d.ts +26 -9
  112. package/dist/io/xlsx/cell-accumulator.js +89 -46
  113. package/dist/io/xlsx/cell-value.d.ts +3 -3
  114. package/dist/io/xlsx/cell-value.js +9 -6
  115. package/dist/io/xlsx/color-xml.d.ts +1 -1
  116. package/dist/io/xlsx/color-xml.js +18 -23
  117. package/dist/io/xlsx/comments.d.ts +5 -5
  118. package/dist/io/xlsx/comments.js +17 -8
  119. package/dist/io/xlsx/conditional-formatting.d.ts +4 -4
  120. package/dist/io/xlsx/conditional-formatting.js +64 -58
  121. package/dist/io/xlsx/data-validation.d.ts +3 -3
  122. package/dist/io/xlsx/data-validation.js +35 -33
  123. package/dist/io/xlsx/errors.d.ts +3 -3
  124. package/dist/io/xlsx/hyperlinks.d.ts +4 -2
  125. package/dist/io/xlsx/hyperlinks.js +10 -18
  126. package/dist/io/xlsx/images.d.ts +1 -1
  127. package/dist/io/xlsx/images.js +25 -28
  128. package/dist/io/xlsx/namespaces.d.ts +2 -2
  129. package/dist/io/xlsx/package-plan.js +5 -2
  130. package/dist/io/xlsx/read-pivot.js +4 -13
  131. package/dist/io/xlsx/read-rows.d.ts +13 -13
  132. package/dist/io/xlsx/read-rows.js +19 -55
  133. package/dist/io/xlsx/read-shared-strings.js +7 -44
  134. package/dist/io/xlsx/read-styles.d.ts +3 -3
  135. package/dist/io/xlsx/read-styles.js +42 -66
  136. package/dist/io/xlsx/read-worksheet.js +73 -129
  137. package/dist/io/xlsx/read.d.ts +4 -6
  138. package/dist/io/xlsx/read.js +104 -142
  139. package/dist/io/xlsx/rich-runs.d.ts +41 -7
  140. package/dist/io/xlsx/rich-runs.js +94 -30
  141. package/dist/io/xlsx/rich-text.d.ts +1 -1
  142. package/dist/io/xlsx/shared-strings.d.ts +2 -2
  143. package/dist/io/xlsx/sheet-properties.d.ts +1 -1
  144. package/dist/io/xlsx/sheet-properties.js +14 -10
  145. package/dist/io/xlsx/styles.d.ts +9 -22
  146. package/dist/io/xlsx/styles.js +65 -89
  147. package/dist/io/xlsx/tables.d.ts +2 -2
  148. package/dist/io/xlsx/tables.js +20 -25
  149. package/dist/io/xlsx/theme-xml.d.ts +37 -0
  150. package/dist/io/xlsx/theme-xml.js +145 -0
  151. package/dist/io/xlsx/threaded-comments.d.ts +18 -27
  152. package/dist/io/xlsx/threaded-comments.js +26 -38
  153. package/dist/io/xlsx/workbook-xml.js +14 -8
  154. package/dist/io/xlsx/worksheet-xml.d.ts +2 -2
  155. package/dist/io/xlsx/worksheet-xml.js +40 -48
  156. package/dist/io/xlsx/write-stream.d.ts +23 -16
  157. package/dist/io/xlsx/write-stream.js +13 -14
  158. package/dist/io/xlsx/write.d.ts +8 -7
  159. package/dist/io/xlsx/write.js +6 -3
  160. package/dist/io/xlsx/x14-ext.d.ts +1 -1
  161. package/dist/sha512.d.ts +2 -0
  162. package/dist/sha512.js +146 -0
  163. package/dist/vba/bytes.d.ts +11 -0
  164. package/dist/vba/bytes.js +29 -0
  165. package/dist/vba/cfb-writer.d.ts +1 -1
  166. package/dist/vba/cfb.d.ts +1 -1
  167. package/dist/vba/cfb.js +15 -51
  168. package/dist/vba/codepage.d.ts +2 -2
  169. package/dist/vba/dir-records.d.ts +53 -0
  170. package/dist/vba/dir-records.js +28 -0
  171. package/dist/vba/errors.d.ts +2 -2
  172. package/dist/vba/index.d.ts +2 -2
  173. package/dist/vba/index.js +2 -2
  174. package/dist/vba/ms-ovba.d.ts +1 -1
  175. package/dist/vba/ms-ovba.js +1 -3
  176. package/dist/vba/project-editor.d.ts +9 -9
  177. package/dist/vba/project-editor.js +6 -44
  178. package/dist/vba/project.d.ts +5 -5
  179. package/dist/vba/project.js +3 -28
  180. package/dist/vba/vba-encoding.d.ts +1 -1
  181. package/dist/xml/errors.d.ts +1 -1
  182. package/dist/xml/xml-read.d.ts +98 -9
  183. package/dist/xml/xml-read.js +70 -0
  184. package/dist/xml/xml.d.ts +74 -10
  185. package/dist/xml/xml.js +39 -7
  186. package/package.json +59 -36
@@ -10,7 +10,7 @@
10
10
  * container rather than the spreadsheet inside it, and live in `../opc/namespaces.ts`.
11
11
  */
12
12
  /**
13
- * SpreadsheetML main namespace the default `xmlns` of the workbook,
13
+ * SpreadsheetML main namespace: the default `xmlns` of the workbook,
14
14
  * worksheet, styles, sharedStrings, comments, table and pivot parts.
15
15
  */
16
16
  export declare const SPREADSHEETML_NS = "http://schemas.openxmlformats.org/spreadsheetml/2006/main";
@@ -25,7 +25,7 @@ export declare const MARKUP_COMPATIBILITY_NS = "http://schemas.openxmlformats.or
25
25
  */
26
26
  export declare const REVISION_NS = "http://schemas.microsoft.com/office/spreadsheetml/2014/revision";
27
27
  /**
28
- * The 2018 threaded-comments namespace, shared by both parts of the feature a sheet's
28
+ * The 2018 threaded-comments namespace, shared by both parts of the feature: a sheet's
29
29
  * `threadedComment{n}.xml` and the workbook's `person.xml`. Note the plural `threadedcomments`, all
30
30
  * lower-case: Excel matches the URI exactly and reads neither part under any other spelling.
31
31
  */
@@ -1,6 +1,7 @@
1
1
  import { AuthoringError } from '../../errors.js';
2
2
  import { extensionOf, relativePartPath, relsPathFor, THEME_PART_PATH } from '../opc/part-paths.js';
3
3
  import { preservedRelsXml } from '../opc/rels.js';
4
+ import { applyThemeOverrides } from './theme-xml.js';
4
5
  export class SheetRelIds {
5
6
  #next = 1;
6
7
  next() {
@@ -75,10 +76,12 @@ export function planPreservedParts(workbook, generatedDrawingCount, generatedMed
75
76
  ? []
76
77
  : [{ id: rel.id, type: rel.type, target: relativePartPath(newPath, target) }];
77
78
  });
78
- const authoredTheme = newPath === THEME_PART_PATH ? workbook.authoredThemeXml() : undefined;
79
+ const overrides = newPath === THEME_PART_PATH ? workbook.themeOverrides : undefined;
79
80
  emitted.set(newPath, {
80
81
  path: newPath,
81
- bytes: authoredTheme === undefined ? part.bytes : new TextEncoder().encode(authoredTheme),
82
+ bytes: overrides === undefined
83
+ ? part.bytes
84
+ : new TextEncoder().encode(applyThemeOverrides(new TextDecoder().decode(part.bytes), overrides)),
82
85
  contentType: part.contentType,
83
86
  relsPath: rels.length === 0 ? null : relsPathFor(newPath),
84
87
  relsXml: rels.length === 0 ? null : preservedRelsXml(rels),
@@ -1,5 +1,5 @@
1
- import { pivotMetricFromSubtotal, } from '../../core/pivot-table.js';
2
- import { localName, parseXml } from '../../xml/xml-read.js';
1
+ import { isDeclarablePivotSourceKind, pivotMetricFromSubtotal, } from '../../core/pivot-table.js';
2
+ import { localName, numInteger, parseXml } from '../../xml/xml-read.js';
3
3
  export function parsePivotTable(tableXml, cacheXml) {
4
4
  const { fields, source } = parsePivotCacheDefinition(cacheXml);
5
5
  const def = parsePivotTableDefinition(tableXml);
@@ -85,20 +85,11 @@ function parsePivotTableDefinition(tableXml) {
85
85
  });
86
86
  return { name, cacheId, rowFields, columnFields, valueField, valueCaption, metric };
87
87
  }
88
- const SOURCE_KINDS = new Set([
89
- 'worksheet',
90
- 'external',
91
- 'consolidation',
92
- 'scenario',
93
- ]);
94
88
  function sourceKind(type) {
95
89
  if (type === undefined)
96
90
  return 'worksheet';
97
- return SOURCE_KINDS.has(type) ? type : 'unknown';
91
+ return isDeclarablePivotSourceKind(type) ? type : 'unknown';
98
92
  }
99
93
  function toIndex(value) {
100
- if (value === undefined)
101
- return -1;
102
- const index = Number(value);
103
- return Number.isInteger(index) && index >= 0 ? index : -1;
94
+ return numInteger(value, 0) ?? -1;
104
95
  }
@@ -8,7 +8,7 @@ export interface ReadSheetRowsOptions extends ReadXlsxOptions {
8
8
  readonly sheet?: string | number;
9
9
  }
10
10
  /**
11
- * The resolved style facets of a streamed cell its own `<c s>` cell format, flattened exactly as
11
+ * The resolved style facets of a streamed cell: its own `<c s>` cell format, flattened exactly as
12
12
  * the buffered reader resolves it. Present only when the cell carries a format; a consumer can copy
13
13
  * these straight onto a writer cell to preserve its look through a streaming read→write.
14
14
  */
@@ -19,7 +19,7 @@ export interface StreamedCell {
19
19
  readonly col: number;
20
20
  /** Canonical A1 address (`"B3"`). */
21
21
  readonly address: string;
22
- /** The decoded value identical to what `readXlsx` would produce for the same cell. */
22
+ /** The decoded value, identical to what `readXlsx` would produce for the same cell. */
23
23
  readonly value: CellValue;
24
24
  /** The cell's resolved style facets, or absent when the cell carries no format of its own. */
25
25
  readonly style?: StreamedCellStyle;
@@ -39,14 +39,14 @@ export interface StreamedRow {
39
39
  *
40
40
  * The two summaries are resolved lazily: reading either accessor drives a full scan of the sheet if
41
41
  * its rows have not already been consumed, so their order relative to `rows()` never matters. (When
42
- * rows *are* consumed first the streaming idiom the accessors reuse that pass and re-scan
42
+ * rows *are* consumed first, the streaming idiom, the accessors reuse that pass and re-scan
43
43
  * nothing.)
44
44
  */
45
45
  export interface StreamedSheet {
46
- /** The worksheet's declared name, joined from the workbook part never a positional placeholder. */
46
+ /** The worksheet's declared name, joined from the workbook part. Never a positional placeholder. */
47
47
  readonly name: string;
48
48
  /** Stream this sheet's rows, one at a time, in sheet order. */
49
- rows(): Generator<StreamedRow>;
49
+ rows(): Generator<StreamedRow, void, undefined>;
50
50
  /** 1-based indices of columns the sheet declares hidden, ascending. */
51
51
  readonly hiddenColumns: readonly number[];
52
52
  /** The sheet's merged ranges, as canonical A1 range strings, in declaration order. */
@@ -55,31 +55,31 @@ export interface StreamedSheet {
55
55
  /**
56
56
  * Stream a worksheet's rows from an `.xlsx` package, yielding each in sheet order without building
57
57
  * the workbook model. Only rows the sheet actually declares are yielded, and within a row only its
58
- * non-empty cells a blank or style-only cell contributes nothing, matching the intent of a data
58
+ * non-empty cells: a blank or style-only cell contributes nothing, matching the intent of a data
59
59
  * read.
60
60
  *
61
61
  * @param data The raw `.xlsx` bytes.
62
62
  * @param options Sheet selector and the inflate bound (see {@link ReadSheetRowsOptions}).
63
63
  * @throws {UnsupportedFormatError} if the input is not a readable `.xlsx` package (a legacy `.xls`, a
64
- * binary `.xlsb`, or an unrecognised/non-ZIP blob branch on `.format`).
65
- * @throws {PackageReadError} if the input is a ZIP that cannot be unpacked a corrupt or
64
+ * binary `.xlsb`, or an unrecognised/non-ZIP blob; branch on `.format`).
65
+ * @throws {PackageReadError} if the input is a ZIP that cannot be unpacked: a corrupt or
66
66
  * truncated archive, or one exceeding the inflate bound (a probable zip bomb).
67
67
  * @throws {XlsxParseError} if the package's workbook part declares no worksheets.
68
68
  * @throws {RangeError} / {@link AuthoringError} if `options.sheet` selects a position, or a name,
69
69
  * that no worksheet has.
70
70
  */
71
- export declare function readSheetRows(data: Uint8Array, options?: ReadSheetRowsOptions): Generator<StreamedRow>;
71
+ export declare function readSheetRows(data: Uint8Array, options?: ReadSheetRowsOptions): Generator<StreamedRow, void, undefined>;
72
72
  /**
73
73
  * Stream every worksheet of an `.xlsx` package in workbook order, without building the workbook
74
74
  * model. Each yielded {@link StreamedSheet} carries the declared sheet name and lets the caller
75
- * stream that sheet's rows and read its hidden-column and merge summaries the streaming analogue
75
+ * stream that sheet's rows and read its hidden-column and merge summaries: the streaming analogue
76
76
  * of walking `readXlsx(data).worksheets`.
77
77
  *
78
78
  * @param data The raw `.xlsx` bytes.
79
79
  * @param options The inflate bound (see {@link ReadXlsxOptions}).
80
80
  * @throws {UnsupportedFormatError} if the input is not a readable `.xlsx` package (a legacy `.xls`, a
81
- * binary `.xlsb`, or an unrecognised/non-ZIP blob branch on `.format`).
82
- * @throws {PackageReadError} if the input is a ZIP that cannot be unpacked a corrupt or
81
+ * binary `.xlsb`, or an unrecognised/non-ZIP blob; branch on `.format`).
82
+ * @throws {PackageReadError} if the input is a ZIP that cannot be unpacked: a corrupt or
83
83
  * truncated archive, or one exceeding the inflate bound (a probable zip bomb).
84
84
  */
85
- export declare function readWorkbookStream(data: Uint8Array, options?: ReadXlsxOptions): Generator<StreamedSheet>;
85
+ export declare function readWorkbookStream(data: Uint8Array, options?: ReadXlsxOptions): Generator<StreamedSheet, void, undefined>;
@@ -1,12 +1,12 @@
1
- import { MAX_COLUMN } from '../../core/address.js';
1
+ import { MAX_COLUMN, MAX_ROW } from '../../core/address.js';
2
2
  import { AuthoringError } from '../../errors.js';
3
- import { boolStrict, closeEmptyElements, localName, xmlEvents } from '../../xml/xml-read.js';
3
+ import { boolStrict, closeEmptyElements, localName, numInteger, xmlEvents, } from '../../xml/xml-read.js';
4
4
  import { packageAccessors } from '../opc/read-opc.js';
5
5
  import { inflateSpreadsheetPackage, unsupportedWorkbookPart } from '../opc/sniff-format.js';
6
6
  import { CellAccumulator } from './cell-accumulator.js';
7
7
  import { XlsxParseError } from './errors.js';
8
- import { DEFAULT_MAX_UNCOMPRESSED, parseRelationships, parseStyleTable, parseWorkbookSheets, resolveWorkbookPart, } from './read.js';
9
8
  import { parseSharedStrings } from './read-shared-strings.js';
9
+ import { DEFAULT_MAX_UNCOMPRESSED, parseRelationships, parseStyleTable, parseWorkbookSheets, resolveWorkbookPart, } from './read.js';
10
10
  export function* readSheetRows(data, options = {}) {
11
11
  const pkg = openPackage(data, options.maxUncompressedBytes);
12
12
  const chosen = pickSheet(pkg.sheets, options.sheet);
@@ -102,13 +102,11 @@ function* scanSheet(xml, sharedStrings, xfStyles, hiddenColumns, merges) {
102
102
  let rowNumber = 0;
103
103
  let lastRow = 0;
104
104
  let rowHidden = false;
105
+ let rowInGrid = true;
105
106
  let cells = [];
106
- const cell = new CellAccumulator();
107
- let inInlineString = false;
108
- let capture = false;
109
- let text = '';
107
+ const cell = new CellAccumulator({ richRuns: false });
110
108
  const finalizeCell = () => {
111
- if (cell.ref === '' || cell.col < 0)
109
+ if (cell.ref === '' || cell.col < 0 || !rowInGrid)
112
110
  return;
113
111
  const style = cell.styleIndex >= 0 ? xfStyles[cell.styleIndex] : undefined;
114
112
  const value = cell.decode(sharedStrings, style);
@@ -119,19 +117,18 @@ function* scanSheet(xml, sharedStrings, xfStyles, hiddenColumns, merges) {
119
117
  };
120
118
  for (const event of closeEmptyElements(xmlEvents(xml), CELL_EMPTY_CLOSE)) {
121
119
  if (event.kind === 'text') {
122
- if (capture)
123
- text += event.text;
120
+ cell.appendChunk(event.text);
124
121
  continue;
125
122
  }
126
123
  if (event.kind === 'open') {
127
124
  const local = localName(event.name);
128
- text = '';
129
- capture = false;
125
+ if (cell.openElement(local, event.attrs, event.selfClosing))
126
+ continue;
130
127
  switch (local) {
131
128
  case 'row': {
132
- const declared = Number(event.attrs.r);
133
- rowNumber = Number.isInteger(declared) && declared >= 1 ? declared : lastRow + 1;
129
+ rowNumber = numInteger(event.attrs.r, 1) ?? lastRow + 1;
134
130
  lastRow = rowNumber;
131
+ rowInGrid = rowNumber <= MAX_ROW;
135
132
  rowHidden = boolStrict(event.attrs.hidden);
136
133
  cells = [];
137
134
  break;
@@ -143,60 +140,27 @@ function* scanSheet(xml, sharedStrings, xfStyles, hiddenColumns, merges) {
143
140
  if (event.attrs.ref !== undefined)
144
141
  merges.push(event.attrs.ref);
145
142
  break;
146
- case 'c':
147
- cell.beginCell(event.attrs);
148
- break;
149
- case 'is':
150
- inInlineString = true;
151
- cell.beginInlineString();
152
- break;
153
- case 'f':
154
- capture = true;
155
- cell.beginFormula(event.attrs, event.selfClosing);
156
- break;
157
- case 'v':
158
- case 't':
159
- capture = true;
160
- break;
161
143
  default:
162
144
  break;
163
145
  }
164
- if (event.selfClosing && (local === 'f' || local === 'v'))
165
- capture = false;
166
146
  continue;
167
147
  }
168
148
  const local = localName(event.name);
169
- switch (local) {
170
- case 'f':
171
- cell.setFormula(text);
172
- break;
173
- case 'v':
174
- cell.setValue(text);
175
- break;
176
- case 't':
177
- cell.appendText(text, inInlineString);
178
- break;
179
- case 'is':
180
- inInlineString = false;
181
- break;
182
- case 'c':
183
- finalizeCell();
184
- break;
185
- case 'row':
149
+ const claimed = cell.closeElement(local);
150
+ if (claimed === 'cell')
151
+ finalizeCell();
152
+ else if (claimed === 'other' && local === 'row') {
153
+ if (rowInGrid)
186
154
  yield { number: rowNumber, hidden: rowHidden, cells };
187
- break;
188
- default:
189
- break;
190
155
  }
191
- capture = false;
192
156
  }
193
157
  }
194
158
  function collectHiddenColumn(attrs, hiddenColumns) {
195
159
  if (attrs.hidden !== '1' && attrs.hidden !== 'true')
196
160
  return;
197
- const min = Number(attrs.min);
198
- const max = Number(attrs.max);
199
- if (!Number.isInteger(min) || !Number.isInteger(max) || min < 1)
161
+ const min = numInteger(attrs.min, 1);
162
+ const max = numInteger(attrs.max, 1);
163
+ if (min === undefined || max === undefined)
200
164
  return;
201
165
  const last = Math.min(max, MAX_COLUMN);
202
166
  for (let index = min; index <= last; index++)
@@ -4,55 +4,18 @@ export function parseSharedStrings(xml) {
4
4
  if (xml === '')
5
5
  return [];
6
6
  const strings = [];
7
- let plain = '';
8
- const runs = new RunAccumulator();
9
- let isRich = false;
10
- let capture = false;
11
- let text = '';
7
+ const runs = new RunAccumulator({ container: 'si', readRuns: true });
12
8
  parseXml(xml, {
13
- onOpen(name, attrs) {
14
- const local = localName(name);
15
- switch (local) {
16
- case 'si':
17
- plain = '';
18
- runs.reset();
19
- isRich = false;
20
- break;
21
- case 'r':
22
- isRich = true;
23
- runs.beginRun();
24
- break;
25
- case 'rPr':
26
- runs.beginProperties();
27
- break;
28
- case 't':
29
- capture = true;
30
- text = '';
31
- break;
32
- default:
33
- runs.applyProperty(local, attrs);
34
- break;
35
- }
9
+ onOpen(name, attrs, selfClosing) {
10
+ runs.open(localName(name), attrs, selfClosing);
36
11
  },
37
12
  onText(chunk) {
38
- if (capture)
39
- text += chunk;
13
+ runs.text(chunk);
40
14
  },
41
15
  onClose(name) {
42
- const local = localName(name);
43
- switch (local) {
44
- case 't':
45
- if (!runs.appendText(text))
46
- plain += text;
47
- capture = false;
48
- break;
49
- case 'r':
50
- runs.endRun();
51
- break;
52
- case 'si':
53
- strings.push(isRich ? { richText: runs.runs } : plain);
54
- break;
55
- }
16
+ if (runs.close(localName(name)) !== 'container')
17
+ return;
18
+ strings.push(runs.isRich ? { richText: runs.runs } : runs.plainText);
56
19
  },
57
20
  });
58
21
  return strings;
@@ -9,7 +9,7 @@ export declare function applyFontChild(draft: FontDraft, local: string, attrs: X
9
9
  /**
10
10
  * Extract the custom indexed-color palette (`<colors><indexedColors>`) from styles.xml as verbatim
11
11
  * `<rgbColor rgb="…"/>` fragments, or an empty list when the file rides the default palette. Kept raw
12
- * rather than parsed into RGB and re-serialised so the exact entries (count, order, casing) a
12
+ * rather than parsed into RGB and re-serialised, so the exact entries (count, order, casing) a
13
13
  * source file declared survive a round-trip and every `indexed="…"` reference keeps its RGB.
14
14
  */
15
15
  export declare function parseIndexedColors(stylesXml: string): string[];
@@ -25,8 +25,8 @@ export declare function parseMruColors(stylesXml: string): string[];
25
25
  * container's nominated `defaultTableStyle`/`defaultPivotStyle`. See {@link TableStyleTable} for why
26
26
  * the definitions stay raw while the two names are decoded.
27
27
  *
28
- * A file with no such block or with the self-closing `count="0"` container Excel writes when it has
29
- * only defaults to state yields an empty {@link TableStyleTable.styles} and whichever names it did
28
+ * A file with no such block, or with the self-closing `count="0"` container Excel writes when it has
29
+ * only defaults to state, yields an empty {@link TableStyleTable.styles} and whichever names it did
30
30
  * carry.
31
31
  */
32
32
  export declare function parseTableStyles(stylesXml: string): TableStyleTable;
@@ -1,9 +1,10 @@
1
- import { assignStyleFacets, isBorderStyle, isFillPatternType, isFontScheme, isFontVerticalAlignment, isHorizontalAlignment, isNamedUnderlineStyle, isVerticalAlignment, } from '../../core/style.js';
2
- import { boolPresent, boolStrict, closeEmptyElements, localName, openElements, xmlEvents, } from '../../xml/xml-read.js';
3
- import { numFmtCodeFor } from '../style/xf-style.js';
1
+ import { isBorderStyle, isFillPatternType, isFontScheme, isFontVerticalAlignment, isHorizontalAlignment, isNamedUnderlineStyle, isVerticalAlignment, } from '../../core/style.js';
2
+ import { boolPresent, boolStrict, closeEmptyElements, localName, numFinite, numInteger, openElements, xmlEvents, } from '../../xml/xml-read.js';
3
+ import { numFmtCodeFor, resolveStyleTable, } from '../style/xf-style.js';
4
4
  import { parseColor } from './color-xml.js';
5
5
  const BORDER_EDGE_NAMES = ['left', 'right', 'top', 'bottom', 'diagonal'];
6
6
  const BORDER_EDGES = new Set(BORDER_EDGE_NAMES);
7
+ const isBorderEdgeName = (name) => BORDER_EDGES.has(name);
7
8
  const STYLE_EMPTY_CLOSES = new Set([
8
9
  'font',
9
10
  'border',
@@ -53,24 +54,7 @@ export function parseStyleTable(xml) {
53
54
  }
54
55
  next = events.next();
55
56
  }
56
- const cellXfs = xfStyles.map((xf) => {
57
- if (xf.xfId === undefined)
58
- return xf;
59
- const named = namedXfs[xf.xfId];
60
- return named === undefined ? xf : { ...named, ...xf };
61
- });
62
- const namedStyles = namedXfs.map((xf, index) => {
63
- const label = cellStyleNames.find((entry) => entry.xfId === index);
64
- const style = {};
65
- assignStyleFacets(style, xf);
66
- if (label?.name !== undefined)
67
- style.name = label.name;
68
- if (label?.builtinId !== undefined)
69
- style.builtinId = label.builtinId;
70
- return style;
71
- });
72
- const defaultFont = fonts[0];
73
- return defaultFont === undefined ? { cellXfs, namedStyles } : { cellXfs, namedStyles, defaultFont };
57
+ return resolveStyleTable({ directXfs: xfStyles, namedXfs, labels: cellStyleNames, fonts });
74
58
  }
75
59
  function* until(events, container) {
76
60
  let next = events.next();
@@ -86,8 +70,8 @@ function parseNumFmts(events) {
86
70
  const codes = new Map();
87
71
  for (const event of until(events, 'numFmts')) {
88
72
  if (event.kind === 'open' && localName(event.name) === 'numFmt') {
89
- const id = Number(event.attrs.numFmtId);
90
- if (Number.isInteger(id) && id > 0 && event.attrs.formatCode !== undefined) {
73
+ const id = numInteger(event.attrs.numFmtId, 1);
74
+ if (id !== undefined && event.attrs.formatCode !== undefined) {
91
75
  codes.set(id, event.attrs.formatCode);
92
76
  }
93
77
  }
@@ -151,8 +135,7 @@ function parseFills(events) {
151
135
  break;
152
136
  case 'stop':
153
137
  if (gradientDraft !== null) {
154
- const position = Number(attrs.position);
155
- gradientDraft.stopPosition = Number.isFinite(position) ? position : 0;
138
+ gradientDraft.stopPosition = numFinite(attrs.position) ?? 0;
156
139
  gradientDraft.stopColor = undefined;
157
140
  }
158
141
  break;
@@ -210,7 +193,7 @@ function parseBorders(events) {
210
193
  borderDraft.diagonalDown = true;
211
194
  }
212
195
  else if (borderDraft !== null) {
213
- if (BORDER_EDGES.has(local)) {
196
+ if (isBorderEdgeName(local)) {
214
197
  if (attrs.style !== undefined && isBorderStyle(attrs.style)) {
215
198
  currentEdge = local;
216
199
  borderDraft[currentEdge] = { style: attrs.style };
@@ -271,12 +254,12 @@ function parseXfTable(events, container, deps) {
271
254
  return xfs;
272
255
  }
273
256
  function resolveXf(attrs, deps, captureXfId) {
274
- const fillId = Number(attrs.fillId);
275
- const fill = Number.isInteger(fillId) ? deps.fills[fillId] : undefined;
276
- const fontId = Number(attrs.fontId);
277
- const font = Number.isInteger(fontId) ? deps.fonts[fontId] : undefined;
278
- const borderId = Number(attrs.borderId);
279
- const border = Number.isInteger(borderId) && borderId > 0 ? deps.borders[borderId] : undefined;
257
+ const fillId = numInteger(attrs.fillId, 0);
258
+ const fill = fillId !== undefined ? deps.fills[fillId] : undefined;
259
+ const fontId = numInteger(attrs.fontId, 0);
260
+ const font = fontId !== undefined ? deps.fonts[fontId] : undefined;
261
+ const borderId = numInteger(attrs.borderId, 1);
262
+ const border = borderId !== undefined ? deps.borders[borderId] : undefined;
280
263
  const numFmt = resolveNumFmt(attrs.numFmtId, deps.numFmtCodes);
281
264
  const draft = {};
282
265
  if (fill)
@@ -289,9 +272,9 @@ function resolveXf(attrs, deps, captureXfId) {
289
272
  draft.border = border;
290
273
  if (boolStrict(attrs.quotePrefix))
291
274
  draft.quotePrefix = true;
292
- if (captureXfId && attrs.xfId !== undefined) {
293
- const xfId = Number(attrs.xfId);
294
- if (Number.isInteger(xfId) && xfId > 0)
275
+ if (captureXfId) {
276
+ const xfId = numInteger(attrs.xfId, 1);
277
+ if (xfId !== undefined)
295
278
  draft.xfId = xfId;
296
279
  }
297
280
  return draft;
@@ -301,16 +284,14 @@ function parseCellStyles(events) {
301
284
  for (const event of until(events, 'cellStyles')) {
302
285
  if (event.kind === 'open' && localName(event.name) === 'cellStyle') {
303
286
  const attrs = event.attrs;
304
- const xfId = Number(attrs.xfId);
305
- if (Number.isInteger(xfId)) {
287
+ const xfId = numInteger(attrs.xfId);
288
+ if (xfId !== undefined) {
306
289
  const entry = { xfId };
307
290
  if (attrs.name !== undefined)
308
291
  entry.name = attrs.name;
309
- if (attrs.builtinId !== undefined) {
310
- const builtinId = Number(attrs.builtinId);
311
- if (Number.isInteger(builtinId))
312
- entry.builtinId = builtinId;
313
- }
292
+ const builtinId = numInteger(attrs.builtinId);
293
+ if (builtinId !== undefined)
294
+ entry.builtinId = builtinId;
314
295
  names.push(entry);
315
296
  }
316
297
  }
@@ -346,8 +327,8 @@ export function applyFontChild(draft, local, attrs) {
346
327
  draft.vertAlign = attrs.val;
347
328
  break;
348
329
  case 'sz': {
349
- const size = Number(attrs.val);
350
- if (Number.isFinite(size))
330
+ const size = numFinite(attrs.val);
331
+ if (size !== undefined)
351
332
  draft.size = size;
352
333
  break;
353
334
  }
@@ -360,14 +341,14 @@ export function applyFontChild(draft, local, attrs) {
360
341
  draft.name = attrs.val;
361
342
  break;
362
343
  case 'family': {
363
- const family = Number(attrs.val);
364
- if (Number.isInteger(family))
344
+ const family = numInteger(attrs.val);
345
+ if (family !== undefined)
365
346
  draft.family = family;
366
347
  break;
367
348
  }
368
349
  case 'charset': {
369
- const charset = Number(attrs.val);
370
- if (Number.isInteger(charset))
350
+ const charset = numInteger(attrs.val);
351
+ if (charset !== undefined)
371
352
  draft.charset = charset;
372
353
  break;
373
354
  }
@@ -380,7 +361,8 @@ export function applyFontChild(draft, local, attrs) {
380
361
  }
381
362
  }
382
363
  function resolveNumFmt(raw, custom) {
383
- return raw === undefined ? undefined : numFmtCodeFor(Number(raw), custom);
364
+ const id = numInteger(raw, 1);
365
+ return id === undefined ? undefined : numFmtCodeFor(id, custom);
384
366
  }
385
367
  function toFill(pattern, fgColor, bgColor) {
386
368
  if (!isFillPatternType(pattern) || pattern === 'none')
@@ -394,8 +376,8 @@ function toFill(pattern, fgColor, bgColor) {
394
376
  }
395
377
  function assignGradientNumbers(fill, attrs) {
396
378
  for (const key of ['degree', 'left', 'right', 'top', 'bottom']) {
397
- const value = Number(attrs[key]);
398
- if (attrs[key] !== undefined && Number.isFinite(value))
379
+ const value = numFinite(attrs[key]);
380
+ if (value !== undefined)
399
381
  fill[key] = value;
400
382
  }
401
383
  }
@@ -414,25 +396,19 @@ function parseAlignment(attrs) {
414
396
  }
415
397
  if (attrs.vertical !== undefined && isVerticalAlignment(attrs.vertical))
416
398
  out.vertical = attrs.vertical;
417
- if (attrs.textRotation !== undefined) {
418
- const rotation = Number(attrs.textRotation);
419
- if (Number.isFinite(rotation) && rotation !== 0)
420
- out.textRotation = rotation;
421
- }
399
+ const rotation = numFinite(attrs.textRotation);
400
+ if (rotation !== undefined && rotation !== 0)
401
+ out.textRotation = rotation;
422
402
  if (boolStrict(attrs.wrapText))
423
403
  out.wrapText = true;
424
- if (attrs.indent !== undefined) {
425
- const indent = Number(attrs.indent);
426
- if (Number.isInteger(indent) && indent !== 0)
427
- out.indent = indent;
428
- }
404
+ const indent = numInteger(attrs.indent);
405
+ if (indent !== undefined && indent !== 0)
406
+ out.indent = indent;
429
407
  if (boolStrict(attrs.shrinkToFit))
430
408
  out.shrinkToFit = true;
431
- if (attrs.readingOrder !== undefined) {
432
- const order = Number(attrs.readingOrder);
433
- if (Number.isInteger(order) && order !== 0)
434
- out.readingOrder = order;
435
- }
409
+ const order = numInteger(attrs.readingOrder);
410
+ if (order !== undefined && order !== 0)
411
+ out.readingOrder = order;
436
412
  return Object.keys(out).length > 0 ? out : undefined;
437
413
  }
438
414
  function parseProtection(attrs) {