@shbernal/ts-xlsx 3.1.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 (214) hide show
  1. package/README.md +30 -23
  2. package/dist/bytes.d.ts +3 -2
  3. package/dist/core/address.d.ts +36 -0
  4. package/dist/core/address.js +47 -9
  5. package/dist/core/array-formula-ranges.d.ts +33 -0
  6. package/dist/core/array-formula-ranges.js +62 -0
  7. package/dist/core/color-resolution.js +2 -2
  8. package/dist/core/column.js +3 -2
  9. package/dist/core/comment-thread.d.ts +7 -3
  10. package/dist/core/conditional-formatting-overlay.d.ts +9 -0
  11. package/dist/core/conditional-formatting-overlay.js +28 -1
  12. package/dist/core/conditional-formatting.d.ts +98 -10
  13. package/dist/core/conditional-formatting.js +42 -1
  14. package/dist/core/data-validation-overlay.d.ts +5 -0
  15. package/dist/core/data-validation-overlay.js +15 -0
  16. package/dist/core/data-validation.d.ts +16 -0
  17. package/dist/core/data-validation.js +15 -0
  18. package/dist/core/date-format.js +14 -4
  19. package/dist/core/date.js +1 -1
  20. package/dist/core/formula-references.d.ts +26 -0
  21. package/dist/core/formula-references.js +173 -0
  22. package/dist/core/formula-scan.d.ts +2 -0
  23. package/dist/core/formula-scan.js +58 -0
  24. package/dist/core/formula.d.ts +51 -18
  25. package/dist/core/formula.js +77 -86
  26. package/dist/core/function-values.d.ts +4 -0
  27. package/dist/core/function-values.js +58 -0
  28. package/dist/core/future-functions.d.ts +3 -0
  29. package/dist/core/{modern-functions.js → future-functions.js} +119 -88
  30. package/dist/core/grid-edits.d.ts +28 -0
  31. package/dist/core/grid-edits.js +193 -25
  32. package/dist/core/hyperlink.d.ts +39 -0
  33. package/dist/core/hyperlink.js +88 -0
  34. package/dist/core/image.d.ts +36 -5
  35. package/dist/core/image.js +9 -0
  36. package/dist/core/internal.d.ts +3 -1
  37. package/dist/core/limits.d.ts +5 -3
  38. package/dist/core/merge.d.ts +13 -0
  39. package/dist/core/merge.js +15 -1
  40. package/dist/core/page-setup.d.ts +26 -11
  41. package/dist/core/pivot-table.d.ts +49 -10
  42. package/dist/core/pivot-table.js +55 -21
  43. package/dist/core/protection.d.ts +66 -4
  44. package/dist/core/protection.js +4 -0
  45. package/dist/core/row.js +3 -2
  46. package/dist/core/structured-reference.d.ts +7 -0
  47. package/dist/core/structured-reference.js +7 -0
  48. package/dist/core/table.d.ts +38 -8
  49. package/dist/core/table.js +37 -7
  50. package/dist/core/theme.d.ts +2 -0
  51. package/dist/core/theme.js +4 -1
  52. package/dist/core/value.d.ts +51 -28
  53. package/dist/core/value.js +15 -12
  54. package/dist/core/workbook-styles.d.ts +17 -1
  55. package/dist/core/workbook-styles.js +4 -0
  56. package/dist/core/workbook-theme.js +5 -3
  57. package/dist/core/workbook.d.ts +20 -3
  58. package/dist/core/workbook.js +26 -5
  59. package/dist/core/worksheet-merges.d.ts +9 -4
  60. package/dist/core/worksheet-merges.js +17 -11
  61. package/dist/core/worksheet-model.d.ts +1 -1
  62. package/dist/core/worksheet-model.js +4 -0
  63. package/dist/core/worksheet-pictures.d.ts +3 -3
  64. package/dist/core/worksheet-pictures.js +7 -6
  65. package/dist/core/worksheet.d.ts +110 -21
  66. package/dist/core/worksheet.js +174 -49
  67. package/dist/entries/core.d.ts +7 -5
  68. package/dist/entries/core.js +4 -3
  69. package/dist/entries/errors.d.ts +1 -0
  70. package/dist/entries/errors.js +1 -0
  71. package/dist/errors.js +1 -1
  72. package/dist/io/cell-metadata/metadata.d.ts +42 -0
  73. package/dist/io/cell-metadata/metadata.js +35 -0
  74. package/dist/io/cell-metadata/rich-values.d.ts +11 -0
  75. package/dist/io/cell-metadata/rich-values.js +80 -0
  76. package/dist/io/csv/delimiter.d.ts +14 -2
  77. package/dist/io/csv/delimiter.js +12 -0
  78. package/dist/io/csv/errors.d.ts +14 -0
  79. package/dist/io/csv/errors.js +5 -0
  80. package/dist/io/csv/read.d.ts +6 -1
  81. package/dist/io/csv/read.js +46 -24
  82. package/dist/io/csv/write.d.ts +10 -3
  83. package/dist/io/csv/write.js +16 -7
  84. package/dist/io/opc/part-paths.js +4 -4
  85. package/dist/io/opc/read-opc.d.ts +4 -0
  86. package/dist/io/opc/read-opc.js +10 -4
  87. package/dist/io/opc/strict-relationships.d.ts +2 -0
  88. package/dist/io/opc/strict-relationships.js +12 -0
  89. package/dist/io/read-policy/column-budget.d.ts +26 -0
  90. package/dist/io/{xlsx → read-policy}/column-budget.js +4 -8
  91. package/dist/io/read-policy/read-repair.d.ts +74 -0
  92. package/dist/io/read-policy/read-repair.js +119 -0
  93. package/dist/io/style/cell-style-resolution.d.ts +25 -0
  94. package/dist/io/style/cell-style-resolution.js +30 -0
  95. package/dist/io/xlsb/formula.js +4 -2
  96. package/dist/io/xlsb/ptg-functions.js +110 -0
  97. package/dist/io/xlsb/read-metadata.d.ts +7 -0
  98. package/dist/io/xlsb/read-metadata.js +69 -0
  99. package/dist/io/xlsb/read-worksheet.d.ts +6 -0
  100. package/dist/io/xlsb/read-worksheet.js +95 -37
  101. package/dist/io/xlsb/read.js +26 -8
  102. package/dist/io/xlsb/record-types.d.ts +13 -0
  103. package/dist/io/xlsb/record-types.js +13 -0
  104. package/dist/io/xlsb/sheet-protection.d.ts +20 -0
  105. package/dist/io/xlsb/sheet-protection.js +60 -0
  106. package/dist/io/xlsx/cell-accumulator.d.ts +20 -12
  107. package/dist/io/xlsx/cell-accumulator.js +49 -19
  108. package/dist/io/xlsx/cell-metadata.d.ts +39 -0
  109. package/dist/io/xlsx/cell-metadata.js +162 -0
  110. package/dist/io/xlsx/cell-value.d.ts +16 -4
  111. package/dist/io/xlsx/cell-value.js +38 -21
  112. package/dist/io/xlsx/color-xml.js +1 -1
  113. package/dist/io/xlsx/column-span.d.ts +11 -0
  114. package/dist/io/xlsx/column-span.js +9 -0
  115. package/dist/io/xlsx/comments.d.ts +3 -28
  116. package/dist/io/xlsx/comments.js +3 -65
  117. package/dist/io/xlsx/conditional-formatting.d.ts +42 -25
  118. package/dist/io/xlsx/conditional-formatting.js +424 -129
  119. package/dist/io/xlsx/data-validation.d.ts +12 -7
  120. package/dist/io/xlsx/data-validation.js +41 -29
  121. package/dist/io/xlsx/edit-vba.js +43 -36
  122. package/dist/io/xlsx/hyperlinks.d.ts +16 -50
  123. package/dist/io/xlsx/hyperlinks.js +8 -37
  124. package/dist/io/xlsx/images.d.ts +49 -16
  125. package/dist/io/xlsx/images.js +145 -44
  126. package/dist/io/xlsx/namespaces.d.ts +8 -0
  127. package/dist/io/xlsx/namespaces.js +4 -0
  128. package/dist/io/xlsx/package-plan.d.ts +48 -15
  129. package/dist/io/xlsx/package-plan.js +46 -9
  130. package/dist/io/xlsx/part-names.d.ts +6 -8
  131. package/dist/io/xlsx/part-names.js +3 -7
  132. package/dist/io/xlsx/pivot.d.ts +5 -5
  133. package/dist/io/xlsx/pivot.js +23 -22
  134. package/dist/io/xlsx/preserved-splices.d.ts +6 -0
  135. package/dist/io/xlsx/preserved-splices.js +74 -0
  136. package/dist/io/xlsx/read-comments.d.ts +29 -0
  137. package/dist/io/xlsx/read-comments.js +75 -0
  138. package/dist/io/xlsx/read-parts.d.ts +2 -2
  139. package/dist/io/xlsx/read-parts.js +44 -37
  140. package/dist/io/xlsx/read-pivot.js +9 -3
  141. package/dist/io/xlsx/read-rich-runs.d.ts +8 -5
  142. package/dist/io/xlsx/read-rich-runs.js +18 -1
  143. package/dist/io/xlsx/read-rows.d.ts +18 -7
  144. package/dist/io/xlsx/read-rows.js +47 -37
  145. package/dist/io/xlsx/read-styles.d.ts +0 -25
  146. package/dist/io/xlsx/read-styles.js +39 -34
  147. package/dist/io/xlsx/read-workbook-xml.d.ts +6 -0
  148. package/dist/io/xlsx/read-workbook-xml.js +9 -4
  149. package/dist/io/xlsx/read-worksheet.d.ts +8 -3
  150. package/dist/io/xlsx/read-worksheet.js +45 -23
  151. package/dist/io/xlsx/read.d.ts +0 -4
  152. package/dist/io/xlsx/read.js +17 -21
  153. package/dist/io/xlsx/relationships.d.ts +3 -0
  154. package/dist/io/xlsx/relationships.js +4 -0
  155. package/dist/io/xlsx/row-position.d.ts +3 -1
  156. package/dist/io/xlsx/row-xml.d.ts +18 -7
  157. package/dist/io/xlsx/row-xml.js +54 -35
  158. package/dist/io/xlsx/sheet-properties.js +68 -42
  159. package/dist/io/xlsx/strict-parts.d.ts +7 -0
  160. package/dist/io/xlsx/strict-parts.js +171 -0
  161. package/dist/io/xlsx/style-elements.d.ts +0 -2
  162. package/dist/io/xlsx/style-elements.js +9 -29
  163. package/dist/io/xlsx/styles.d.ts +5 -0
  164. package/dist/io/xlsx/styles.js +11 -5
  165. package/dist/io/xlsx/tables.d.ts +10 -2
  166. package/dist/io/xlsx/tables.js +49 -42
  167. package/dist/io/xlsx/theme-xml.d.ts +6 -0
  168. package/dist/io/xlsx/theme-xml.js +16 -19
  169. package/dist/io/xlsx/threaded-comments.d.ts +6 -1
  170. package/dist/io/xlsx/threaded-comments.js +14 -7
  171. package/dist/io/xlsx/workbook-xml.d.ts +13 -24
  172. package/dist/io/xlsx/workbook-xml.js +15 -33
  173. package/dist/io/xlsx/worksheet-xml.d.ts +5 -2
  174. package/dist/io/xlsx/worksheet-xml.js +37 -69
  175. package/dist/io/xlsx/write-stream.d.ts +28 -6
  176. package/dist/io/xlsx/write-stream.js +42 -17
  177. package/dist/io/xlsx/write.d.ts +11 -4
  178. package/dist/io/xlsx/write.js +153 -90
  179. package/dist/rel-type.d.ts +9 -0
  180. package/dist/rel-type.js +3 -0
  181. package/dist/vba/bytes.d.ts +2 -0
  182. package/dist/vba/bytes.js +11 -0
  183. package/dist/vba/cfb-format.d.ts +47 -0
  184. package/dist/vba/cfb-format.js +34 -0
  185. package/dist/vba/cfb-writer.d.ts +1 -2
  186. package/dist/vba/cfb-writer.js +33 -36
  187. package/dist/vba/cfb.d.ts +9 -4
  188. package/dist/vba/cfb.js +55 -43
  189. package/dist/vba/dir-records.d.ts +6 -0
  190. package/dist/vba/dir-records.js +10 -2
  191. package/dist/vba/errors.d.ts +4 -4
  192. package/dist/vba/index.d.ts +0 -2
  193. package/dist/vba/index.js +0 -2
  194. package/dist/vba/ms-ovba.d.ts +6 -2
  195. package/dist/vba/ms-ovba.js +15 -6
  196. package/dist/vba/project-editor.js +102 -87
  197. package/dist/vba/project.d.ts +4 -0
  198. package/dist/vba/project.js +12 -6
  199. package/dist/vba/vba-encoding.d.ts +4 -10
  200. package/dist/vba/vba-encoding.js +2 -15
  201. package/dist/xml/xml-read.d.ts +29 -1
  202. package/dist/xml/xml-read.js +56 -0
  203. package/dist/xml/xml-scan.d.ts +13 -0
  204. package/dist/xml/xml-scan.js +58 -7
  205. package/dist/xml/xml.d.ts +33 -1
  206. package/dist/xml/xml.js +22 -0
  207. package/package.json +5 -4
  208. package/skills/ts-xlsx-upstream/SKILL.md +21 -13
  209. package/dist/core/modern-functions.d.ts +0 -1
  210. package/dist/io/xlsx/cell-style-resolution.d.ts +0 -25
  211. package/dist/io/xlsx/cell-style-resolution.js +0 -29
  212. package/dist/io/xlsx/column-budget.d.ts +0 -26
  213. package/dist/io/xlsx/read-repair.d.ts +0 -26
  214. package/dist/io/xlsx/read-repair.js +0 -39
@@ -1,16 +1,22 @@
1
1
  import { Workbook } from '../../core/workbook.js';
2
+ import { WorksheetMerges } from '../../core/worksheet-merges.js';
2
3
  import { AuthoringError, quoted } from '../../errors.js';
4
+ import { numInteger } from '../../xml/xml-attrs.js';
3
5
  import { closeEmptyElements, parseXmlPasses } from '../../xml/xml-read.js';
4
6
  import { boolStrict, localName, xmlEvents } from '../../xml/xml-scan.js';
5
7
  import { openSpreadsheetPackage, readPartRelationships } from '../opc/read-opc.js';
6
8
  import { unsupportedWorkbookPart } from '../opc/sniff-format.js';
7
- import { CellAccumulator } from './cell-accumulator.js';
8
- import { CellStyleResolver } from './cell-style-resolution.js';
9
- import { ColumnRecordBudget, takeColumnSpan } from './column-budget.js';
9
+ import { ColumnRecordBudget } from '../read-policy/column-budget.js';
10
+ import { admitting, repairedSheetNames } from '../read-policy/read-repair.js';
11
+ import { CellStyleResolver } from '../style/cell-style-resolution.js';
12
+ import { CellAccumulator, WORKSHEET_BODY_EMPTY_CLOSES } from './cell-accumulator.js';
13
+ import { readCellMetadata } from './cell-metadata.js';
14
+ import { takeColumnSpan } from './column-span.js';
10
15
  import { XlsxParseError } from './errors.js';
11
16
  import { SHARED_STRINGS_PART, STYLES_PART } from './part-names.js';
12
17
  import { parseSharedStrings } from './read-shared-strings.js';
13
- import { workbookPropertiesPass, parseStyleTable, workbookSheetsPass, } from './read.js';
18
+ import { parseStyleTable } from './read-styles.js';
19
+ import { definedNamesPass, workbookPropertiesPass, workbookSheetsPass, } from './read-workbook-xml.js';
14
20
  import { RowPositionTracker } from './row-position.js';
15
21
  export function* readSheetRows(data, options = {}) {
16
22
  const pkg = openPackage(data, options.maxUncompressedBytes);
@@ -18,13 +24,13 @@ export function* readSheetRows(data, options = {}) {
18
24
  const sheetXml = pkg.sheetXml(chosen.relId);
19
25
  if (sheetXml === undefined)
20
26
  return;
21
- yield* scanSheet(sheetXml, pkg.sharedStrings, pkg.xfStyles, pkg.dateEpoch, new Set(), []);
27
+ yield* scanSheet(sheetXml, pkg, new Set(), new WorksheetMerges());
22
28
  }
23
29
  export function* readWorkbookStream(data, options = {}) {
24
30
  const pkg = openPackage(data, options.maxUncompressedBytes);
25
31
  for (const sheet of pkg.sheets) {
26
32
  const xml = pkg.sheetXml(sheet.relId) ?? '';
27
- yield new StreamedSheetReader(sheet.name, xml, pkg.sharedStrings, pkg.xfStyles, pkg.dateEpoch);
33
+ yield new StreamedSheetReader(sheet.name, xml, pkg);
28
34
  }
29
35
  }
30
36
  function openPackage(data, maxUncompressedBytes) {
@@ -35,8 +41,9 @@ function openPackage(data, maxUncompressedBytes) {
35
41
  }
36
42
  const properties = new Workbook();
37
43
  const sheetsPass = workbookSheetsPass();
38
- parseXmlPasses(workbookXml, [sheetsPass, workbookPropertiesPass(properties)]);
39
- const sheets = sheetsPass.result();
44
+ const namesPass = definedNamesPass();
45
+ parseXmlPasses(workbookXml, [sheetsPass, workbookPropertiesPass(properties), namesPass]);
46
+ const sheets = repairedSheetNames(sheetsPass.result());
40
47
  const rels = readPartRelationships(documentPath, text);
41
48
  const sharedStrings = parseSharedStrings(rels.relatedTextOrPath('sharedStrings', SHARED_STRINGS_PART));
42
49
  const { cellXfs: xfStyles } = parseStyleTable(rels.relatedTextOrPath('styles', STYLES_PART));
@@ -45,6 +52,8 @@ function openPackage(data, maxUncompressedBytes) {
45
52
  sharedStrings,
46
53
  xfStyles,
47
54
  dateEpoch: properties.dateEpoch,
55
+ definedNames: namesPass.spellings(),
56
+ cellMetadata: readCellMetadata(rels),
48
57
  sheetXml(relId) {
49
58
  const target = rels.byId(relId)?.target;
50
59
  return target === undefined ? undefined : text(rels.pathOf(target));
@@ -71,24 +80,20 @@ function pickSheet(sheets, selector) {
71
80
  class StreamedSheetReader {
72
81
  name;
73
82
  #xml;
74
- #sharedStrings;
75
- #xfStyles;
76
- #dateEpoch;
83
+ #tables;
77
84
  #hiddenColumns = new Set();
78
- #merges = [];
85
+ #merges = new WorksheetMerges();
79
86
  #scanned = false;
80
- constructor(name, xml, sharedStrings, xfStyles, dateEpoch) {
87
+ constructor(name, xml, tables) {
81
88
  this.name = name;
82
89
  this.#xml = xml;
83
- this.#sharedStrings = sharedStrings;
84
- this.#xfStyles = xfStyles;
85
- this.#dateEpoch = dateEpoch;
90
+ this.#tables = tables;
86
91
  }
87
92
  *rows() {
88
93
  this.#hiddenColumns = new Set();
89
- this.#merges = [];
94
+ this.#merges = new WorksheetMerges();
90
95
  this.#scanned = false;
91
- yield* scanSheet(this.#xml, this.#sharedStrings, this.#xfStyles, this.#dateEpoch, this.#hiddenColumns, this.#merges);
96
+ yield* scanSheet(this.#xml, this.#tables, this.#hiddenColumns, this.#merges);
92
97
  this.#scanned = true;
93
98
  }
94
99
  get hiddenColumns() {
@@ -97,7 +102,7 @@ class StreamedSheetReader {
97
102
  }
98
103
  get merges() {
99
104
  this.#ensureScanned();
100
- return [...this.#merges];
105
+ return [...this.#merges.ranges];
101
106
  }
102
107
  #ensureScanned() {
103
108
  if (this.#scanned)
@@ -106,28 +111,28 @@ class StreamedSheetReader {
106
111
  }
107
112
  }
108
113
  }
109
- const CELL_EMPTY_CLOSE = new Set(['c']);
110
- function* scanSheet(xml, sharedStrings, xfStyles, dateEpoch, hiddenColumns, merges) {
114
+ function* scanSheet(xml, tables, hiddenColumns, merges) {
115
+ const { sharedStrings, xfStyles, dateEpoch, definedNames, cellMetadata } = tables;
111
116
  let rowNumber = 0;
112
117
  let rowHidden = false;
113
- let rowInGrid = true;
114
118
  let cells = [];
115
119
  const columnBudget = new ColumnRecordBudget();
116
120
  const styleResolution = new CellStyleResolver();
117
121
  const rowPosition = new RowPositionTracker();
118
- const cell = new CellAccumulator({ richRuns: false, dateEpoch });
122
+ const cell = new CellAccumulator({ dateEpoch, definedNames, cellMetadata });
119
123
  const finalizeCell = () => {
120
- if (cell.ref === '' || cell.col < 0 || !rowInGrid)
124
+ if (cell.ref === '')
121
125
  return;
122
- const styleIndex = styleResolution.indexFor(cell.col, cell.styleIndex);
123
- const style = styleIndex >= 0 ? xfStyles[styleIndex] : undefined;
126
+ const style = styleResolution.styleFor(cell.col, cell.styleIndex, xfStyles);
124
127
  const value = cell.decode(sharedStrings, style);
125
128
  if (value !== null) {
126
129
  const { col, ref } = cell;
127
- cells.push(style ? { col, address: ref, value, style } : { col, address: ref, value });
130
+ cells.push(style !== undefined && styleResolution.declaresFormat(col, cell.styleIndex)
131
+ ? { col, address: ref, value, style }
132
+ : { col, address: ref, value });
128
133
  }
129
134
  };
130
- for (const event of closeEmptyElements(xmlEvents(xml), CELL_EMPTY_CLOSE)) {
135
+ for (const event of closeEmptyElements(xmlEvents(xml), WORKSHEET_BODY_EMPTY_CLOSES)) {
131
136
  if (event.kind === 'text') {
132
137
  cell.appendChunk(event.text);
133
138
  continue;
@@ -138,20 +143,22 @@ function* scanSheet(xml, sharedStrings, xfStyles, dateEpoch, hiddenColumns, merg
138
143
  continue;
139
144
  switch (local) {
140
145
  case 'row': {
141
- ({ number: rowNumber, inGrid: rowInGrid } = rowPosition.open(event.attrs));
146
+ const position = rowPosition.open(event.attrs);
147
+ rowNumber = position.number;
142
148
  rowHidden = boolStrict(event.attrs.hidden);
143
- cell.openRow(rowInGrid ? rowNumber : -1);
144
- styleResolution.openRow(event.attrs);
145
- cells = [];
149
+ cell.openRow(position.number, position.inGrid);
150
+ styleResolution.openRow(numInteger(event.attrs.s, 0) ?? -1, boolStrict(event.attrs.customFormat));
146
151
  break;
147
152
  }
148
153
  case 'col':
149
154
  collectColumn(event.attrs, hiddenColumns, styleResolution, columnBudget);
150
155
  break;
151
- case 'mergeCell':
152
- if (event.attrs.ref !== undefined)
153
- merges.push(event.attrs.ref);
156
+ case 'mergeCell': {
157
+ const ref = event.attrs.ref;
158
+ if (ref !== undefined)
159
+ admitting(() => merges.add(ref));
154
160
  break;
161
+ }
155
162
  default:
156
163
  break;
157
164
  }
@@ -163,8 +170,11 @@ function* scanSheet(xml, sharedStrings, xfStyles, dateEpoch, hiddenColumns, merg
163
170
  finalizeCell();
164
171
  else if (claimed === 'other' && local === 'row') {
165
172
  styleResolution.closeRow();
166
- if (rowInGrid)
173
+ const inGrid = cell.rowOpen;
174
+ cell.closeRow();
175
+ if (inGrid)
167
176
  yield { number: rowNumber, hidden: rowHidden, cells };
177
+ cells = [];
168
178
  }
169
179
  }
170
180
  }
@@ -173,7 +183,7 @@ function collectColumn(attrs, hiddenColumns, styleResolution, budget) {
173
183
  if (span === undefined)
174
184
  return;
175
185
  const { first: min, last } = span;
176
- styleResolution.noteColumnSpan(min, last, attrs);
186
+ styleResolution.noteColumnSpan(min, last, numInteger(attrs.style, 0) ?? -1);
177
187
  if (!boolStrict(attrs.hidden))
178
188
  return;
179
189
  for (let index = min; index <= last; index++)
@@ -1,27 +1,2 @@
1
- import type { TableStyleTable } from '../../core/workbook-styles.ts';
2
1
  import { type StyleTable } from '../style/xf-style.ts';
3
2
  export declare function parseStyleTable(xml: string): StyleTable;
4
- /**
5
- * Extract the custom indexed-color palette (`<colors><indexedColors>`) from styles.xml as verbatim
6
- * `<rgbColor rgb="…"/>` fragments, or an empty list when the file rides the default palette. Kept raw
7
- * rather than parsed into RGB and re-serialised, so the exact entries (count, order, casing) a
8
- * source file declared survive a round-trip and every `indexed="…"` reference keeps its RGB.
9
- */
10
- export declare function parseIndexedColors(stylesXml: string): string[];
11
- /**
12
- * Extract the most-recently-used colour swatches (`<colors><mruColors>`) from styles.xml as verbatim
13
- * `<color .../>` fragments, or an empty list when the file declares none. Kept raw for the same reason
14
- * the indexed palette is: the list is the author's own working set of colours and the model has no
15
- * use for its contents, only for not losing them.
16
- */
17
- export declare function parseMruColors(stylesXml: string): string[];
18
- /**
19
- * Extract the `<tableStyles>` block from styles.xml: each `<tableStyle>` definition verbatim, plus the
20
- * container's nominated `defaultTableStyle`/`defaultPivotStyle`. See {@link TableStyleTable} for why
21
- * the definitions stay raw while the two names are decoded.
22
- *
23
- * A file with no such block, or with the self-closing `count="0"` container Excel writes when it has
24
- * only defaults to state, yields an empty {@link TableStyleTable.styles} and whichever names it did
25
- * carry.
26
- */
27
- export declare function parseTableStyles(stylesXml: string): TableStyleTable;
@@ -11,6 +11,7 @@ const isBorderEdgeName = (name) => BORDER_EDGES.has(name);
11
11
  const STYLE_EMPTY_CLOSES = new Set([
12
12
  'font',
13
13
  'border',
14
+ 'fill',
14
15
  'patternFill',
15
16
  'gradientFill',
16
17
  'xf',
@@ -111,30 +112,37 @@ function parseFonts(events) {
111
112
  }
112
113
  function parseFills(events) {
113
114
  const fills = [];
114
- let pattern = '';
115
- let fgColor;
116
- let bgColor;
115
+ let fillDraft = null;
116
+ let patternDraft = null;
117
117
  let gradientDraft = null;
118
- let fillSlotAt = -1;
119
118
  for (const event of until(events, 'fills')) {
120
119
  if (event.kind === 'open') {
121
120
  const attrs = event.attrs;
121
+ const bodyExpected = fillDraft !== null && !fillDraft.bodyRead;
122
122
  switch (localName(event.name)) {
123
123
  case 'fill':
124
- fillSlotAt = fills.length;
124
+ fillDraft = { body: undefined, bodyRead: false };
125
125
  break;
126
126
  case 'patternFill':
127
- pattern = attrs.patternType ?? 'none';
128
- fgColor = undefined;
129
- bgColor = undefined;
127
+ if (!bodyExpected)
128
+ break;
129
+ patternDraft = {
130
+ pattern: attrs.patternType ?? 'none',
131
+ fgColor: undefined,
132
+ bgColor: undefined,
133
+ };
130
134
  break;
131
135
  case 'fgColor':
132
- fgColor = parseColor(attrs);
136
+ if (patternDraft !== null)
137
+ patternDraft.fgColor = parseColor(attrs);
133
138
  break;
134
139
  case 'bgColor':
135
- bgColor = parseColor(attrs);
140
+ if (patternDraft !== null)
141
+ patternDraft.bgColor = parseColor(attrs);
136
142
  break;
137
143
  case 'gradientFill':
144
+ if (!bodyExpected)
145
+ break;
138
146
  gradientDraft = {
139
147
  fill: {
140
148
  type: 'gradient',
@@ -161,7 +169,12 @@ function parseFills(events) {
161
169
  else if (event.kind === 'close') {
162
170
  switch (localName(event.name)) {
163
171
  case 'patternFill':
164
- fills.push(toFill(pattern, fgColor, bgColor));
172
+ if (fillDraft !== null && patternDraft !== null) {
173
+ const { pattern, fgColor, bgColor } = patternDraft;
174
+ fillDraft.body = toFill(pattern, fgColor, bgColor);
175
+ fillDraft.bodyRead = true;
176
+ patternDraft = null;
177
+ }
165
178
  break;
166
179
  case 'stop':
167
180
  if (gradientDraft !== null && gradientDraft.stopPosition !== null) {
@@ -175,14 +188,18 @@ function parseFills(events) {
175
188
  }
176
189
  break;
177
190
  case 'gradientFill':
178
- if (gradientDraft !== null) {
179
- fills.push(gradientDraft.fill);
191
+ if (fillDraft !== null && gradientDraft !== null) {
192
+ fillDraft.body = gradientDraft.fill;
193
+ fillDraft.bodyRead = true;
180
194
  gradientDraft = null;
181
195
  }
182
196
  break;
183
197
  case 'fill':
184
- if (fills.length === fillSlotAt)
185
- fills.push(undefined);
198
+ if (fillDraft !== null)
199
+ fills.push(fillDraft.body);
200
+ fillDraft = null;
201
+ patternDraft = null;
202
+ gradientDraft = null;
186
203
  break;
187
204
  }
188
205
  }
@@ -368,29 +385,24 @@ function assignAlignmentToken(out, key, raw) {
368
385
  function parseProtection(attrs) {
369
386
  return protectionFrom({ locked: boolTristate(attrs.locked), hidden: boolStrict(attrs.hidden) });
370
387
  }
371
- export function parseIndexedColors(stylesXml) {
372
- return capturedFragments(stylesXml, 'indexedColors');
373
- }
374
- export function parseMruColors(stylesXml) {
375
- return capturedFragments(stylesXml, 'mruColors');
376
- }
377
- export function parseTableStyles(stylesXml) {
378
- const { fragments, attributes } = elementSubtrees(stylesXml, new Map([['tableStyles', 'tableStyle']]));
379
- return buildTableStyleTable(stylesXml, fragments.get('tableStyles') ?? [], attributes.get('tableStyles'));
380
- }
381
388
  function buildTableStyleTable(stylesXml, styles, containerAttrs) {
382
- const table = { styles: [...styles] };
389
+ const table = { styles: styles.map((xml) => ({ name: tableStyleNameOf(xml), xml })) };
383
390
  if (containerAttrs?.defaultTableStyle !== undefined) {
384
391
  table.defaultTableStyle = containerAttrs.defaultTableStyle;
385
392
  }
386
393
  if (containerAttrs?.defaultPivotStyle !== undefined) {
387
394
  table.defaultPivotStyle = containerAttrs.defaultPivotStyle;
388
395
  }
389
- const namespaces = fragmentNamespaces(stylesXml, table.styles);
396
+ const namespaces = fragmentNamespaces(stylesXml, styles);
390
397
  if (namespaces.length > 0)
391
398
  table.namespaces = namespaces;
392
399
  return table;
393
400
  }
401
+ function tableStyleNameOf(fragment) {
402
+ for (const { attrs } of openElements(fragment, 'tableStyle'))
403
+ return attrs.name ?? '';
404
+ return '';
405
+ }
394
406
  function fragmentNamespaces(stylesXml, fragments) {
395
407
  if (fragments.length === 0)
396
408
  return [];
@@ -427,10 +439,3 @@ const PRESERVED_SUBTREES = new Map([
427
439
  ['mruColors', 'color'],
428
440
  ['tableStyles', 'tableStyle'],
429
441
  ]);
430
- function capturedFragments(xml, container) {
431
- const child = PRESERVED_SUBTREES.get(container);
432
- if (child === undefined)
433
- return [];
434
- const { fragments } = elementSubtrees(xml, new Map([[container, child]]));
435
- return [...(fragments.get(container) ?? [])];
436
- }
@@ -30,6 +30,12 @@ export interface DefinedNamesPass extends SaxPass {
30
30
  * and repaired the names it had to; resolving during the scan would have forced a second one.
31
31
  */
32
32
  result(sheetOrder: readonly string[]): DefinedName[];
33
+ /**
34
+ * Every name the part defines, whatever its scope, as `definedNameKeys` spells them. Known once the
35
+ * scan ends, before any sheet is read, and needed then: whether a function a formula passes as a value
36
+ * sheds its `_xleta.` depends on them.
37
+ */
38
+ spellings(): ReadonlySet<string>;
33
39
  }
34
40
  export declare function definedNamesPass(): DefinedNamesPass;
35
41
  export declare function applyCoreProperties(workbook: Workbook, xml: string): void;
@@ -1,5 +1,5 @@
1
1
  import { parseDateText } from '../../core/date.js';
2
- import { unmangleFunctions } from '../../core/formula.js';
2
+ import { definedNameKeys, unmangleFunctions } from '../../core/formula.js';
3
3
  import { WORKBOOK_PROTECTION_CREDENTIAL_ATTRS, } from '../../core/workbook-protection.js';
4
4
  import { isVisibility } from '../../core/worksheet.js';
5
5
  import { enumToken, numInteger } from '../../xml/xml-attrs.js';
@@ -125,17 +125,22 @@ export function definedNamesPass() {
125
125
  const text = refersTo.close(localName(name));
126
126
  if (text === undefined || pending === undefined)
127
127
  return;
128
- drafts.push({ ...pending, refersTo: unmangleFunctions(text) });
128
+ drafts.push({ ...pending, refersTo: text });
129
129
  pending = undefined;
130
130
  },
131
131
  },
132
132
  closeEmptyElements: new Set(['definedName']),
133
133
  result(sheetOrder) {
134
- return drafts.map(({ localSheetId, ...draft }) => {
134
+ const names = definedNameKeys(drafts);
135
+ return drafts.map(({ localSheetId, refersTo: stored, ...draft }) => {
136
+ const named = { ...draft, refersTo: unmangleFunctions(stored, names) };
135
137
  const scope = sheetOrder[localSheetId];
136
- return scope === undefined ? draft : { ...draft, scope };
138
+ return scope === undefined ? named : { ...named, scope };
137
139
  });
138
140
  },
141
+ spellings() {
142
+ return definedNameKeys(drafts);
143
+ },
139
144
  };
140
145
  }
141
146
  const CORE_PROPERTY_LOCAL_NAMES = new Set([
@@ -1,6 +1,7 @@
1
1
  import type { DateEpoch } from '../../core/date.ts';
2
2
  import type { Worksheet } from '../../core/worksheet.ts';
3
3
  import { type SaxPass } from '../../xml/xml-read.ts';
4
+ import type { CellMetadataIndex } from '../cell-metadata/metadata.ts';
4
5
  import type { XfStyle } from '../style/xf-style.ts';
5
6
  import type { SharedString } from './cell-value.ts';
6
7
  /**
@@ -8,7 +9,11 @@ import type { SharedString } from './cell-value.ts';
8
9
  * merges, the autofilter, page breaks, and the view and print layout. It commits as it goes rather
9
10
  * than gathering, so it has nothing to hand back once the parse ends.
10
11
  *
11
- * Offered as a pass because the worksheet is the largest part in a package and four other readers
12
- * want the same events, so the five share one parse of it rather than scanning it once each.
12
+ * Offered as a pass because the worksheet is the largest part in a package and other readers want
13
+ * the same events, so they all share one parse of it rather than scanning it once each.
14
+ *
15
+ * `definedNames` is every name the workbook defines, as `definedNameKeys` spells them: the workbook
16
+ * part is read before any sheet, so they are known before the first formula is. `cellMetadata` is
17
+ * what the workbook's `cm` and `vm` values resolve to, read from the parts they index.
13
18
  */
14
- export declare function worksheetPass(sheet: Worksheet, sharedStrings: readonly SharedString[], xfStyles: ReadonlyArray<XfStyle>, dateEpoch: DateEpoch): SaxPass;
19
+ export declare function worksheetPass(sheet: Worksheet, sharedStrings: readonly SharedString[], xfStyles: ReadonlyArray<XfStyle>, dateEpoch: DateEpoch, definedNames: ReadonlySet<string>, cellMetadata: CellMetadataIndex): SaxPass;
@@ -1,22 +1,26 @@
1
- import { tryDecodeRange } from '../../core/address.js';
1
+ import { boundedRect, tryDecodeRange } from '../../core/address.js';
2
2
  import { isCustomFilterOperator, } from '../../core/autofilter.js';
3
3
  import { INTERNAL } from '../../core/internal.js';
4
4
  import { HEADER_FOOTER_ELEMENTS } from '../../core/page-setup.js';
5
5
  import { assignStyleFacets } from '../../core/style.js';
6
6
  import { decodeSpreadsheetText, numFinite, numInteger } from '../../xml/xml-attrs.js';
7
- import {} from '../../xml/xml-read.js';
7
+ import { TextCapture } from '../../xml/xml-read.js';
8
8
  import { boolPresent, boolStrict, localName } from '../../xml/xml-scan.js';
9
- import { CellAccumulator } from './cell-accumulator.js';
10
- import { CellStyleResolver } from './cell-style-resolution.js';
11
- import { ColumnRecordBudget, takeColumnSpan } from './column-budget.js';
12
- import { admitting } from './read-repair.js';
9
+ import { ColumnRecordBudget } from '../read-policy/column-budget.js';
10
+ import { admitting } from '../read-policy/read-repair.js';
11
+ import { CellStyleResolver } from '../style/cell-style-resolution.js';
12
+ import { CellAccumulator, WORKSHEET_BODY_EMPTY_CLOSES } from './cell-accumulator.js';
13
+ import { takeColumnSpan } from './column-span.js';
13
14
  import { RowPositionTracker } from './row-position.js';
14
15
  import { applySheetProperties, PageBreakAccumulator, parseSheetProtection, } from './sheet-properties.js';
15
16
  const HEADER_FOOTER_CHILDREN = new Set(HEADER_FOOTER_ELEMENTS);
16
17
  function isHeaderFooterElement(local) {
17
18
  return HEADER_FOOTER_CHILDREN.has(local);
18
19
  }
19
- const WORKSHEET_EMPTY_CLOSES = new Set(['c', 'autoFilter']);
20
+ const WORKSHEET_EMPTY_CLOSES = new Set([
21
+ ...WORKSHEET_BODY_EMPTY_CLOSES,
22
+ 'autoFilter',
23
+ ]);
20
24
  function pendingFilterCriteria(values, blank, predicates, and) {
21
25
  if (values !== null && (values.length > 0 || blank)) {
22
26
  return { kind: 'values', values, blank };
@@ -75,35 +79,44 @@ class AutoFilterAccumulator {
75
79
  if (this.#ref === null)
76
80
  return;
77
81
  const decoded = tryDecodeRange(this.#ref);
78
- const left = decoded?.left;
79
- const right = decoded?.right;
80
- if (left !== undefined && right !== undefined) {
81
- const width = right - left + 1;
82
+ const rect = decoded === undefined ? undefined : boundedRect(decoded);
83
+ if (rect !== undefined) {
84
+ const width = rect.right - rect.left + 1;
82
85
  sheet.autoFilter = { ref: this.#ref, columns: this.#columns.filter((c) => c.colId < width) };
83
86
  }
84
87
  this.#ref = null;
85
88
  this.#columns = [];
86
89
  }
87
90
  }
88
- export function worksheetPass(sheet, sharedStrings, xfStyles, dateEpoch) {
89
- const cell = new CellAccumulator({ richRuns: true, dateEpoch });
91
+ export function worksheetPass(sheet, sharedStrings, xfStyles, dateEpoch, definedNames, cellMetadata) {
92
+ const cell = new CellAccumulator({ dateEpoch, definedNames, cellMetadata });
90
93
  const autoFilter = new AutoFilterAccumulator();
91
94
  const pageBreaks = new PageBreakAccumulator();
92
95
  const styleResolution = new CellStyleResolver();
93
96
  const columnBudget = new ColumnRecordBudget();
94
97
  const rowPosition = new RowPositionTracker();
98
+ const headerFooterText = new TextCapture(HEADER_FOOTER_ELEMENTS);
99
+ let customViewDepth = 0;
95
100
  const finalizeCellFromState = () => {
96
- const styleIndex = styleResolution.indexFor(cell.col, cell.styleIndex);
97
- const style = styleIndex >= 0 ? xfStyles[styleIndex] : xfStyles[0];
98
- cell.finalize(sheet, sharedStrings, style);
101
+ cell.finalize(sheet, sharedStrings, styleResolution.styleFor(cell.col, cell.styleIndex, xfStyles));
99
102
  };
100
103
  const handlers = {
101
104
  onOpen(name, attrs, selfClosing) {
102
105
  const local = localName(name);
106
+ if (customViewDepth > 0) {
107
+ if (!selfClosing)
108
+ customViewDepth++;
109
+ return;
110
+ }
111
+ if (local === 'customSheetViews') {
112
+ if (!selfClosing)
113
+ customViewDepth = 1;
114
+ return;
115
+ }
103
116
  if (cell.openElement(local, attrs, selfClosing))
104
117
  return;
105
- if (HEADER_FOOTER_CHILDREN.has(local)) {
106
- cell.capture();
118
+ if (isHeaderFooterElement(local)) {
119
+ headerFooterText.open(local, selfClosing);
107
120
  return;
108
121
  }
109
122
  switch (local) {
@@ -114,8 +127,8 @@ export function worksheetPass(sheet, sharedStrings, xfStyles, dateEpoch) {
114
127
  const { number, inGrid } = rowPosition.open(attrs);
115
128
  if (inGrid)
116
129
  applyRow(sheet, number, attrs);
117
- cell.openRow(inGrid ? number : -1);
118
- styleResolution.openRow(attrs);
130
+ cell.openRow(number, inGrid);
131
+ styleResolution.openRow(numInteger(attrs.s, 0) ?? -1, boolStrict(attrs.customFormat));
119
132
  break;
120
133
  }
121
134
  case 'mergeCell':
@@ -173,9 +186,16 @@ export function worksheetPass(sheet, sharedStrings, xfStyles, dateEpoch) {
173
186
  }
174
187
  },
175
188
  onText(chunk) {
189
+ if (customViewDepth > 0)
190
+ return;
176
191
  cell.appendChunk(chunk);
192
+ headerFooterText.text(chunk);
177
193
  },
178
194
  onClose(name) {
195
+ if (customViewDepth > 0) {
196
+ customViewDepth--;
197
+ return;
198
+ }
179
199
  const local = localName(name);
180
200
  const claimed = cell.closeElement(local);
181
201
  if (claimed === 'cell') {
@@ -184,13 +204,15 @@ export function worksheetPass(sheet, sharedStrings, xfStyles, dateEpoch) {
184
204
  }
185
205
  if (claimed === 'claimed')
186
206
  return;
187
- if (isHeaderFooterElement(local)) {
188
- sheet.headerFooter[local] = decodeSpreadsheetText(cell.capturedText);
207
+ const headerFooter = headerFooterText.close(local);
208
+ if (headerFooter !== undefined && isHeaderFooterElement(local)) {
209
+ sheet.headerFooter[local] = decodeSpreadsheetText(headerFooter);
189
210
  return;
190
211
  }
191
212
  switch (local) {
192
213
  case 'row':
193
214
  styleResolution.closeRow();
215
+ cell.closeRow();
194
216
  break;
195
217
  case 'filterColumn':
196
218
  autoFilter.endColumn();
@@ -232,7 +254,7 @@ function applyColumn(sheet, attrs, xfStyles, styleResolution, budget) {
232
254
  if (style !== undefined)
233
255
  assignStyleFacets(column, style);
234
256
  }
235
- styleResolution.noteColumnSpan(min, last, attrs);
257
+ styleResolution.noteColumnSpan(min, last, numInteger(attrs.style, 0) ?? -1);
236
258
  }
237
259
  function applyRow(sheet, number, attrs) {
238
260
  const row = sheet.getRow(number);
@@ -1,9 +1,5 @@
1
1
  import { Workbook } from '../../core/workbook.ts';
2
2
  import type { ReadPackageOptions } from '../opc/read-options.ts';
3
- export type { ReadPackageOptions } from '../opc/read-options.ts';
4
- export type { StyleTable, XfStyle } from '../style/xf-style.ts';
5
- export { parseStyleTable } from './read-styles.ts';
6
- export { type SheetEntry, workbookPropertiesPass, workbookSheetsPass, workbookViewPass, } from './read-workbook-xml.ts';
7
3
  /**
8
4
  * Read a spreadsheet package into a {@link Workbook}.
9
5
  *