@shbernal/ts-xlsx 3.0.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (277) hide show
  1. package/README.md +34 -24
  2. package/dist/bytes.d.ts +10 -2
  3. package/dist/bytes.js +22 -0
  4. package/dist/core/address.d.ts +96 -2
  5. package/dist/core/address.js +87 -33
  6. package/dist/core/array-formula-ranges.d.ts +33 -0
  7. package/dist/core/array-formula-ranges.js +62 -0
  8. package/dist/core/autofilter.d.ts +4 -3
  9. package/dist/core/autofilter.js +31 -30
  10. package/dist/core/axis-handle.d.ts +2 -0
  11. package/dist/core/axis-handle.js +5 -2
  12. package/dist/core/cell.d.ts +4 -3
  13. package/dist/core/cell.js +4 -6
  14. package/dist/core/clone.d.ts +29 -0
  15. package/dist/core/clone.js +24 -0
  16. package/dist/core/color-resolution.js +4 -6
  17. package/dist/core/column.d.ts +1 -0
  18. package/dist/core/column.js +6 -2
  19. package/dist/core/comment-thread.d.ts +7 -3
  20. package/dist/core/comment-thread.js +2 -1
  21. package/dist/core/conditional-formatting-overlay.d.ts +11 -1
  22. package/dist/core/conditional-formatting-overlay.js +30 -3
  23. package/dist/core/conditional-formatting.d.ts +107 -11
  24. package/dist/core/conditional-formatting.js +76 -19
  25. package/dist/core/containers.d.ts +12 -0
  26. package/dist/core/containers.js +5 -0
  27. package/dist/core/data-validation-overlay.d.ts +7 -1
  28. package/dist/core/data-validation-overlay.js +19 -4
  29. package/dist/core/data-validation.d.ts +22 -0
  30. package/dist/core/data-validation.js +30 -4
  31. package/dist/core/date-format.d.ts +20 -0
  32. package/dist/core/date-format.js +170 -0
  33. package/dist/core/date.d.ts +43 -8
  34. package/dist/core/date.js +19 -3
  35. package/dist/core/formula-references.d.ts +26 -0
  36. package/dist/core/formula-references.js +173 -0
  37. package/dist/core/formula-scan.d.ts +2 -0
  38. package/dist/core/formula-scan.js +58 -0
  39. package/dist/core/formula.d.ts +76 -17
  40. package/dist/core/formula.js +87 -80
  41. package/dist/core/function-values.d.ts +4 -0
  42. package/dist/core/function-values.js +58 -0
  43. package/dist/core/future-functions.d.ts +3 -0
  44. package/dist/core/{modern-functions.js → future-functions.js} +119 -88
  45. package/dist/core/grid-edits.d.ts +30 -3
  46. package/dist/core/grid-edits.js +248 -81
  47. package/dist/core/grid-shift.d.ts +58 -9
  48. package/dist/core/grid-shift.js +26 -4
  49. package/dist/core/hyperlink.d.ts +39 -0
  50. package/dist/core/hyperlink.js +88 -0
  51. package/dist/core/image.d.ts +49 -14
  52. package/dist/core/image.js +16 -5
  53. package/dist/core/internal.d.ts +10 -2
  54. package/dist/core/limits.d.ts +25 -0
  55. package/dist/core/limits.js +4 -0
  56. package/dist/core/merge.d.ts +18 -4
  57. package/dist/core/merge.js +33 -20
  58. package/dist/core/page-setup.d.ts +26 -11
  59. package/dist/core/pivot-table.d.ts +49 -10
  60. package/dist/core/pivot-table.js +112 -60
  61. package/dist/core/protection.d.ts +66 -4
  62. package/dist/core/protection.js +6 -11
  63. package/dist/core/range.js +34 -30
  64. package/dist/core/row-input.js +2 -2
  65. package/dist/core/row.d.ts +1 -0
  66. package/dist/core/row.js +6 -2
  67. package/dist/core/structured-reference.d.ts +7 -0
  68. package/dist/core/structured-reference.js +7 -0
  69. package/dist/core/style.d.ts +85 -70
  70. package/dist/core/style.js +92 -28
  71. package/dist/core/table-style.d.ts +1 -1
  72. package/dist/core/table-style.js +4 -4
  73. package/dist/core/table.d.ts +64 -16
  74. package/dist/core/table.js +87 -40
  75. package/dist/core/theme.d.ts +2 -0
  76. package/dist/core/theme.js +9 -5
  77. package/dist/core/value.d.ts +60 -28
  78. package/dist/core/value.js +48 -43
  79. package/dist/core/workbook-media.d.ts +34 -0
  80. package/dist/core/workbook-media.js +44 -0
  81. package/dist/core/workbook-styles.d.ts +83 -1
  82. package/dist/core/workbook-styles.js +4 -0
  83. package/dist/core/workbook-theme.js +5 -3
  84. package/dist/core/workbook-vba.js +3 -2
  85. package/dist/core/workbook.d.ts +41 -3
  86. package/dist/core/workbook.js +46 -41
  87. package/dist/core/worksheet-comments.d.ts +2 -1
  88. package/dist/core/worksheet-comments.js +20 -13
  89. package/dist/core/worksheet-merges.d.ts +49 -0
  90. package/dist/core/worksheet-merges.js +65 -0
  91. package/dist/core/worksheet-model.d.ts +10 -2
  92. package/dist/core/worksheet-model.js +28 -9
  93. package/dist/core/worksheet-pictures.d.ts +3 -3
  94. package/dist/core/worksheet-pictures.js +7 -6
  95. package/dist/core/worksheet.d.ts +165 -27
  96. package/dist/core/worksheet.js +206 -89
  97. package/dist/customui/ribbon.js +2 -1
  98. package/dist/entries/core.d.ts +21 -14
  99. package/dist/entries/core.js +14 -7
  100. package/dist/entries/errors.d.ts +1 -0
  101. package/dist/entries/errors.js +1 -0
  102. package/dist/entries/xlsx.d.ts +1 -1
  103. package/dist/errors.d.ts +64 -0
  104. package/dist/errors.js +22 -0
  105. package/dist/hex.d.ts +8 -0
  106. package/dist/hex.js +3 -0
  107. package/dist/io/cell-metadata/metadata.d.ts +42 -0
  108. package/dist/io/cell-metadata/metadata.js +35 -0
  109. package/dist/io/cell-metadata/rich-values.d.ts +11 -0
  110. package/dist/io/cell-metadata/rich-values.js +80 -0
  111. package/dist/io/csv/delimiter.d.ts +21 -0
  112. package/dist/io/csv/delimiter.js +18 -0
  113. package/dist/io/csv/errors.d.ts +14 -0
  114. package/dist/io/csv/errors.js +5 -0
  115. package/dist/io/csv/read.d.ts +6 -1
  116. package/dist/io/csv/read.js +51 -22
  117. package/dist/io/csv/write.d.ts +19 -5
  118. package/dist/io/csv/write.js +24 -44
  119. package/dist/io/opc/inflate.js +14 -0
  120. package/dist/io/opc/namespaces.d.ts +16 -0
  121. package/dist/io/opc/namespaces.js +4 -0
  122. package/dist/io/opc/part-paths.d.ts +9 -0
  123. package/dist/io/opc/part-paths.js +5 -5
  124. package/dist/io/opc/read-opc.d.ts +58 -14
  125. package/dist/io/opc/read-opc.js +49 -21
  126. package/dist/io/opc/read-options.d.ts +5 -1
  127. package/dist/io/opc/sniff-format.d.ts +3 -3
  128. package/dist/io/opc/sniff-format.js +2 -2
  129. package/dist/io/opc/strict-relationships.d.ts +2 -0
  130. package/dist/io/opc/strict-relationships.js +12 -0
  131. package/dist/io/read-policy/column-budget.d.ts +26 -0
  132. package/dist/io/read-policy/column-budget.js +18 -0
  133. package/dist/io/read-policy/read-repair.d.ts +74 -0
  134. package/dist/io/read-policy/read-repair.js +119 -0
  135. package/dist/io/style/cell-style-resolution.d.ts +25 -0
  136. package/dist/io/style/cell-style-resolution.js +30 -0
  137. package/dist/io/style/xf-style.d.ts +23 -1
  138. package/dist/io/style/xf-style.js +8 -0
  139. package/dist/io/xlsb/formula.d.ts +2 -2
  140. package/dist/io/xlsb/formula.js +52 -35
  141. package/dist/io/xlsb/primitives.js +8 -14
  142. package/dist/io/xlsb/ptg-functions.d.ts +19 -10
  143. package/dist/io/xlsb/ptg-functions.js +495 -596
  144. package/dist/io/xlsb/read-metadata.d.ts +7 -0
  145. package/dist/io/xlsb/read-metadata.js +69 -0
  146. package/dist/io/xlsb/read-styles.js +45 -81
  147. package/dist/io/xlsb/read-worksheet.d.ts +26 -1
  148. package/dist/io/xlsb/read-worksheet.js +148 -106
  149. package/dist/io/xlsb/read.d.ts +6 -3
  150. package/dist/io/xlsb/read.js +70 -31
  151. package/dist/io/xlsb/record-stream.d.ts +30 -0
  152. package/dist/io/xlsb/record-stream.js +20 -0
  153. package/dist/io/xlsb/record-types.d.ts +14 -5
  154. package/dist/io/xlsb/record-types.js +14 -5
  155. package/dist/io/xlsb/sheet-protection.d.ts +20 -0
  156. package/dist/io/xlsb/sheet-protection.js +60 -0
  157. package/dist/io/xlsx/cell-accumulator.d.ts +24 -9
  158. package/dist/io/xlsx/cell-accumulator.js +74 -21
  159. package/dist/io/xlsx/cell-metadata.d.ts +39 -0
  160. package/dist/io/xlsx/cell-metadata.js +162 -0
  161. package/dist/io/xlsx/cell-value.d.ts +19 -5
  162. package/dist/io/xlsx/cell-value.js +45 -30
  163. package/dist/io/xlsx/color-xml.js +3 -2
  164. package/dist/io/xlsx/column-span.d.ts +11 -0
  165. package/dist/io/xlsx/column-span.js +9 -0
  166. package/dist/io/xlsx/comments.d.ts +9 -30
  167. package/dist/io/xlsx/comments.js +25 -83
  168. package/dist/io/xlsx/conditional-formatting.d.ts +43 -23
  169. package/dist/io/xlsx/conditional-formatting.js +437 -138
  170. package/dist/io/xlsx/data-validation.d.ts +12 -7
  171. package/dist/io/xlsx/data-validation.js +67 -53
  172. package/dist/io/xlsx/edit-vba.d.ts +7 -2
  173. package/dist/io/xlsx/edit-vba.js +52 -52
  174. package/dist/io/xlsx/font-xml.d.ts +7 -0
  175. package/dist/io/xlsx/font-xml.js +104 -0
  176. package/dist/io/xlsx/hyperlinks.d.ts +19 -21
  177. package/dist/io/xlsx/hyperlinks.js +12 -32
  178. package/dist/io/xlsx/images.d.ts +49 -16
  179. package/dist/io/xlsx/images.js +149 -50
  180. package/dist/io/xlsx/namespaces.d.ts +24 -0
  181. package/dist/io/xlsx/namespaces.js +11 -0
  182. package/dist/io/xlsx/package-plan.d.ts +60 -15
  183. package/dist/io/xlsx/package-plan.js +83 -18
  184. package/dist/io/xlsx/part-names.d.ts +6 -8
  185. package/dist/io/xlsx/part-names.js +3 -7
  186. package/dist/io/xlsx/pivot.d.ts +5 -5
  187. package/dist/io/xlsx/pivot.js +26 -24
  188. package/dist/io/xlsx/preserved-splices.d.ts +6 -0
  189. package/dist/io/xlsx/preserved-splices.js +74 -0
  190. package/dist/io/xlsx/read-comments.d.ts +29 -0
  191. package/dist/io/xlsx/read-comments.js +75 -0
  192. package/dist/io/xlsx/read-parts.d.ts +59 -0
  193. package/dist/io/xlsx/read-parts.js +259 -0
  194. package/dist/io/xlsx/read-pivot.js +11 -4
  195. package/dist/io/xlsx/{rich-runs.d.ts → read-rich-runs.d.ts} +8 -5
  196. package/dist/io/xlsx/{rich-runs.js → read-rich-runs.js} +24 -4
  197. package/dist/io/xlsx/read-rows.d.ts +35 -11
  198. package/dist/io/xlsx/read-rows.js +73 -49
  199. package/dist/io/xlsx/read-shared-strings.js +3 -2
  200. package/dist/io/xlsx/read-styles.d.ts +0 -30
  201. package/dist/io/xlsx/read-styles.js +45 -105
  202. package/dist/io/xlsx/read-workbook-xml.d.ts +42 -0
  203. package/dist/io/xlsx/read-workbook-xml.js +175 -0
  204. package/dist/io/xlsx/read-worksheet.d.ts +9 -3
  205. package/dist/io/xlsx/read-worksheet.js +70 -177
  206. package/dist/io/xlsx/read.d.ts +3 -14
  207. package/dist/io/xlsx/read.js +74 -411
  208. package/dist/io/xlsx/relationships.d.ts +3 -0
  209. package/dist/io/xlsx/relationships.js +4 -0
  210. package/dist/io/xlsx/rich-text.js +1 -1
  211. package/dist/io/xlsx/row-position.d.ts +20 -0
  212. package/dist/io/xlsx/row-position.js +11 -0
  213. package/dist/io/xlsx/row-xml.d.ts +139 -0
  214. package/dist/io/xlsx/row-xml.js +222 -0
  215. package/dist/io/xlsx/shared-formulas.js +2 -2
  216. package/dist/io/xlsx/sheet-properties.d.ts +10 -1
  217. package/dist/io/xlsx/sheet-properties.js +182 -20
  218. package/dist/io/xlsx/strict-parts.d.ts +7 -0
  219. package/dist/io/xlsx/strict-parts.js +171 -0
  220. package/dist/io/xlsx/style-elements.d.ts +41 -0
  221. package/dist/io/xlsx/style-elements.js +136 -0
  222. package/dist/io/xlsx/styles.d.ts +17 -3
  223. package/dist/io/xlsx/styles.js +13 -209
  224. package/dist/io/xlsx/tables.d.ts +10 -2
  225. package/dist/io/xlsx/tables.js +55 -52
  226. package/dist/io/xlsx/theme-xml.d.ts +32 -4
  227. package/dist/io/xlsx/theme-xml.js +106 -71
  228. package/dist/io/xlsx/threaded-comments.d.ts +14 -11
  229. package/dist/io/xlsx/threaded-comments.js +16 -8
  230. package/dist/io/xlsx/workbook-xml.d.ts +45 -6
  231. package/dist/io/xlsx/workbook-xml.js +62 -54
  232. package/dist/io/xlsx/worksheet-xml.d.ts +26 -70
  233. package/dist/io/xlsx/worksheet-xml.js +69 -282
  234. package/dist/io/xlsx/write-stream.d.ts +90 -19
  235. package/dist/io/xlsx/write-stream.js +80 -25
  236. package/dist/io/xlsx/write.d.ts +12 -6
  237. package/dist/io/xlsx/write.js +226 -123
  238. package/dist/rel-type.d.ts +25 -0
  239. package/dist/rel-type.js +9 -0
  240. package/dist/token-set.d.ts +13 -0
  241. package/dist/token-set.js +4 -1
  242. package/dist/vba/bytes.d.ts +16 -5
  243. package/dist/vba/bytes.js +27 -6
  244. package/dist/vba/cfb-format.d.ts +55 -0
  245. package/dist/vba/cfb-format.js +35 -0
  246. package/dist/vba/cfb-writer.d.ts +8 -0
  247. package/dist/vba/cfb-writer.js +40 -40
  248. package/dist/vba/cfb.d.ts +21 -4
  249. package/dist/vba/cfb.js +124 -90
  250. package/dist/vba/codepage.js +2 -1
  251. package/dist/vba/dir-records.d.ts +6 -0
  252. package/dist/vba/dir-records.js +10 -2
  253. package/dist/vba/errors.d.ts +11 -4
  254. package/dist/vba/index.d.ts +0 -2
  255. package/dist/vba/index.js +0 -2
  256. package/dist/vba/ms-ovba.d.ts +6 -2
  257. package/dist/vba/ms-ovba.js +66 -22
  258. package/dist/vba/project-editor.js +129 -119
  259. package/dist/vba/project.d.ts +15 -1
  260. package/dist/vba/project.js +38 -13
  261. package/dist/vba/vba-encoding.d.ts +4 -10
  262. package/dist/vba/vba-encoding.js +3 -17
  263. package/dist/xml/xml-attrs.d.ts +37 -0
  264. package/dist/xml/xml-attrs.js +30 -0
  265. package/dist/xml/xml-chars.d.ts +40 -0
  266. package/dist/xml/xml-chars.js +12 -0
  267. package/dist/xml/xml-namespaces.d.ts +31 -0
  268. package/dist/xml/xml-namespaces.js +50 -0
  269. package/dist/xml/xml-read.d.ts +101 -11
  270. package/dist/xml/xml-read.js +150 -18
  271. package/dist/xml/xml-scan.d.ts +25 -34
  272. package/dist/xml/xml-scan.js +69 -46
  273. package/dist/xml/xml.d.ts +59 -26
  274. package/dist/xml/xml.js +45 -23
  275. package/package.json +15 -9
  276. package/skills/ts-xlsx-upstream/SKILL.md +21 -13
  277. package/dist/core/modern-functions.d.ts +0 -1
@@ -1,42 +1,59 @@
1
- import { MAX_COLUMN, MAX_ROW } from '../../core/address.js';
2
- import { AuthoringError } from '../../errors.js';
3
- import { closeEmptyElements } from '../../xml/xml-read.js';
4
- import { boolStrict, localName, numInteger, xmlEvents } from '../../xml/xml-scan.js';
1
+ import { Workbook } from '../../core/workbook.js';
2
+ import { WorksheetMerges } from '../../core/worksheet-merges.js';
3
+ import { AuthoringError, quoted } from '../../errors.js';
4
+ import { numInteger } from '../../xml/xml-attrs.js';
5
+ import { closeEmptyElements, parseXmlPasses } from '../../xml/xml-read.js';
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';
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';
8
15
  import { XlsxParseError } from './errors.js';
16
+ import { SHARED_STRINGS_PART, STYLES_PART } from './part-names.js';
9
17
  import { parseSharedStrings } from './read-shared-strings.js';
10
- import { parseStyleTable, parseWorkbookSheets, } from './read.js';
18
+ import { parseStyleTable } from './read-styles.js';
19
+ import { definedNamesPass, workbookPropertiesPass, workbookSheetsPass, } from './read-workbook-xml.js';
20
+ import { RowPositionTracker } from './row-position.js';
11
21
  export function* readSheetRows(data, options = {}) {
12
22
  const pkg = openPackage(data, options.maxUncompressedBytes);
13
23
  const chosen = pickSheet(pkg.sheets, options.sheet);
14
24
  const sheetXml = pkg.sheetXml(chosen.relId);
15
25
  if (sheetXml === undefined)
16
26
  return;
17
- yield* scanSheet(sheetXml, pkg.sharedStrings, pkg.xfStyles, new Set(), []);
27
+ yield* scanSheet(sheetXml, pkg, new Set(), new WorksheetMerges());
18
28
  }
19
29
  export function* readWorkbookStream(data, options = {}) {
20
30
  const pkg = openPackage(data, options.maxUncompressedBytes);
21
31
  for (const sheet of pkg.sheets) {
22
32
  const xml = pkg.sheetXml(sheet.relId) ?? '';
23
- yield new StreamedSheetReader(sheet.name, xml, pkg.sharedStrings, pkg.xfStyles);
33
+ yield new StreamedSheetReader(sheet.name, xml, pkg);
24
34
  }
25
35
  }
26
36
  function openPackage(data, maxUncompressedBytes) {
27
- const { pkg, workbookXml } = openSpreadsheetPackage(data, maxUncompressedBytes);
28
- const { partText: text } = pkg;
37
+ const { pkg, documentPath, workbookXml } = openSpreadsheetPackage(data, maxUncompressedBytes);
38
+ const { partText: text, partBytes } = pkg;
29
39
  if (workbookXml === undefined) {
30
- throw unsupportedWorkbookPart(text, 'the binary .xlsb format (BIFF12) cannot be row-streamed yet; read it with readXlsx or readXlsb');
40
+ throw unsupportedWorkbookPart(partBytes, documentPath, 'the binary .xlsb format (BIFF12) cannot be row-streamed yet; read it with readXlsx or readXlsb');
31
41
  }
32
- const sheets = parseWorkbookSheets(workbookXml);
33
- const rels = readPartRelationships('xl/workbook.xml', text);
34
- const sharedStrings = parseSharedStrings(text('xl/sharedStrings.xml') ?? '');
35
- const { cellXfs: xfStyles } = parseStyleTable(text('xl/styles.xml') ?? '');
42
+ const properties = new Workbook();
43
+ const sheetsPass = workbookSheetsPass();
44
+ const namesPass = definedNamesPass();
45
+ parseXmlPasses(workbookXml, [sheetsPass, workbookPropertiesPass(properties), namesPass]);
46
+ const sheets = repairedSheetNames(sheetsPass.result());
47
+ const rels = readPartRelationships(documentPath, text);
48
+ const sharedStrings = parseSharedStrings(rels.relatedTextOrPath('sharedStrings', SHARED_STRINGS_PART));
49
+ const { cellXfs: xfStyles } = parseStyleTable(rels.relatedTextOrPath('styles', STYLES_PART));
36
50
  return {
37
51
  sheets,
38
52
  sharedStrings,
39
53
  xfStyles,
54
+ dateEpoch: properties.dateEpoch,
55
+ definedNames: namesPass.spellings(),
56
+ cellMetadata: readCellMetadata(rels),
40
57
  sheetXml(relId) {
41
58
  const target = rels.byId(relId)?.target;
42
59
  return target === undefined ? undefined : text(rels.pathOf(target));
@@ -57,28 +74,26 @@ function pickSheet(sheets, selector) {
57
74
  }
58
75
  const sheet = sheets.find((candidate) => candidate.name === selector);
59
76
  if (sheet === undefined)
60
- throw new AuthoringError(`no worksheet named ${JSON.stringify(selector)}`);
77
+ throw new AuthoringError(`no worksheet named ${quoted(selector)}`);
61
78
  return sheet;
62
79
  }
63
80
  class StreamedSheetReader {
64
81
  name;
65
82
  #xml;
66
- #sharedStrings;
67
- #xfStyles;
83
+ #tables;
68
84
  #hiddenColumns = new Set();
69
- #merges = [];
85
+ #merges = new WorksheetMerges();
70
86
  #scanned = false;
71
- constructor(name, xml, sharedStrings, xfStyles) {
87
+ constructor(name, xml, tables) {
72
88
  this.name = name;
73
89
  this.#xml = xml;
74
- this.#sharedStrings = sharedStrings;
75
- this.#xfStyles = xfStyles;
90
+ this.#tables = tables;
76
91
  }
77
92
  *rows() {
78
93
  this.#hiddenColumns = new Set();
79
- this.#merges = [];
94
+ this.#merges = new WorksheetMerges();
80
95
  this.#scanned = false;
81
- yield* scanSheet(this.#xml, this.#sharedStrings, this.#xfStyles, this.#hiddenColumns, this.#merges);
96
+ yield* scanSheet(this.#xml, this.#tables, this.#hiddenColumns, this.#merges);
82
97
  this.#scanned = true;
83
98
  }
84
99
  get hiddenColumns() {
@@ -87,7 +102,7 @@ class StreamedSheetReader {
87
102
  }
88
103
  get merges() {
89
104
  this.#ensureScanned();
90
- return [...this.#merges];
105
+ return [...this.#merges.ranges];
91
106
  }
92
107
  #ensureScanned() {
93
108
  if (this.#scanned)
@@ -96,25 +111,28 @@ class StreamedSheetReader {
96
111
  }
97
112
  }
98
113
  }
99
- const CELL_EMPTY_CLOSE = new Set(['c']);
100
- function* scanSheet(xml, sharedStrings, xfStyles, hiddenColumns, merges) {
114
+ function* scanSheet(xml, tables, hiddenColumns, merges) {
115
+ const { sharedStrings, xfStyles, dateEpoch, definedNames, cellMetadata } = tables;
101
116
  let rowNumber = 0;
102
- let lastRow = 0;
103
117
  let rowHidden = false;
104
- let rowInGrid = true;
105
118
  let cells = [];
106
- const cell = new CellAccumulator({ richRuns: false });
119
+ const columnBudget = new ColumnRecordBudget();
120
+ const styleResolution = new CellStyleResolver();
121
+ const rowPosition = new RowPositionTracker();
122
+ const cell = new CellAccumulator({ dateEpoch, definedNames, cellMetadata });
107
123
  const finalizeCell = () => {
108
- if (cell.ref === '' || cell.col < 0 || !rowInGrid)
124
+ if (cell.ref === '')
109
125
  return;
110
- const style = cell.styleIndex >= 0 ? xfStyles[cell.styleIndex] : undefined;
126
+ const style = styleResolution.styleFor(cell.col, cell.styleIndex, xfStyles);
111
127
  const value = cell.decode(sharedStrings, style);
112
128
  if (value !== null) {
113
129
  const { col, ref } = cell;
114
- 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 });
115
133
  }
116
134
  };
117
- for (const event of closeEmptyElements(xmlEvents(xml), CELL_EMPTY_CLOSE)) {
135
+ for (const event of closeEmptyElements(xmlEvents(xml), WORKSHEET_BODY_EMPTY_CLOSES)) {
118
136
  if (event.kind === 'text') {
119
137
  cell.appendChunk(event.text);
120
138
  continue;
@@ -125,20 +143,22 @@ function* scanSheet(xml, sharedStrings, xfStyles, hiddenColumns, merges) {
125
143
  continue;
126
144
  switch (local) {
127
145
  case 'row': {
128
- rowNumber = numInteger(event.attrs.r, 1) ?? lastRow + 1;
129
- lastRow = rowNumber;
130
- rowInGrid = rowNumber <= MAX_ROW;
146
+ const position = rowPosition.open(event.attrs);
147
+ rowNumber = position.number;
131
148
  rowHidden = boolStrict(event.attrs.hidden);
132
- cells = [];
149
+ cell.openRow(position.number, position.inGrid);
150
+ styleResolution.openRow(numInteger(event.attrs.s, 0) ?? -1, boolStrict(event.attrs.customFormat));
133
151
  break;
134
152
  }
135
153
  case 'col':
136
- collectHiddenColumn(event.attrs, hiddenColumns);
154
+ collectColumn(event.attrs, hiddenColumns, styleResolution, columnBudget);
137
155
  break;
138
- case 'mergeCell':
139
- if (event.attrs.ref !== undefined)
140
- merges.push(event.attrs.ref);
156
+ case 'mergeCell': {
157
+ const ref = event.attrs.ref;
158
+ if (ref !== undefined)
159
+ admitting(() => merges.add(ref));
141
160
  break;
161
+ }
142
162
  default:
143
163
  break;
144
164
  }
@@ -149,19 +169,23 @@ function* scanSheet(xml, sharedStrings, xfStyles, hiddenColumns, merges) {
149
169
  if (claimed === 'cell')
150
170
  finalizeCell();
151
171
  else if (claimed === 'other' && local === 'row') {
152
- if (rowInGrid)
172
+ styleResolution.closeRow();
173
+ const inGrid = cell.rowOpen;
174
+ cell.closeRow();
175
+ if (inGrid)
153
176
  yield { number: rowNumber, hidden: rowHidden, cells };
177
+ cells = [];
154
178
  }
155
179
  }
156
180
  }
157
- function collectHiddenColumn(attrs, hiddenColumns) {
158
- if (!boolStrict(attrs.hidden))
181
+ function collectColumn(attrs, hiddenColumns, styleResolution, budget) {
182
+ const span = takeColumnSpan(attrs, budget);
183
+ if (span === undefined)
159
184
  return;
160
- const min = numInteger(attrs.min, 1);
161
- const max = numInteger(attrs.max, 1);
162
- if (min === undefined || max === undefined)
185
+ const { first: min, last } = span;
186
+ styleResolution.noteColumnSpan(min, last, numInteger(attrs.style, 0) ?? -1);
187
+ if (!boolStrict(attrs.hidden))
163
188
  return;
164
- const last = Math.min(max, MAX_COLUMN);
165
189
  for (let index = min; index <= last; index++)
166
190
  hiddenColumns.add(index);
167
191
  }
@@ -1,6 +1,7 @@
1
1
  import { parseXml } from '../../xml/xml-read.js';
2
2
  import { localName } from '../../xml/xml-scan.js';
3
- import { RunAccumulator } from './rich-runs.js';
3
+ import { RunAccumulator } from './read-rich-runs.js';
4
+ const SHARED_STRING_EMPTY_CLOSES = new Set(['si']);
4
5
  export function parseSharedStrings(xml) {
5
6
  if (xml === '')
6
7
  return [];
@@ -18,6 +19,6 @@ export function parseSharedStrings(xml) {
18
19
  return;
19
20
  strings.push(runs.isRich ? { richText: runs.runs } : runs.plainText);
20
21
  },
21
- });
22
+ }, { closeEmptyElements: SHARED_STRING_EMPTY_CLOSES });
22
23
  return strings;
23
24
  }
@@ -1,32 +1,2 @@
1
- import { type Font, type TableStyleTable } from '../../core/style.ts';
2
- import { type XmlAttributes } from '../../xml/xml-scan.ts';
3
1
  import { type StyleTable } from '../style/xf-style.ts';
4
- export type FontDraft = {
5
- -readonly [K in keyof Font]?: Font[K];
6
- };
7
2
  export declare function parseStyleTable(xml: string): StyleTable;
8
- export declare function applyFontChild(draft: FontDraft, local: string, attrs: XmlAttributes): void;
9
- /**
10
- * Extract the custom indexed-color palette (`<colors><indexedColors>`) from styles.xml as verbatim
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
13
- * source file declared survive a round-trip and every `indexed="…"` reference keeps its RGB.
14
- */
15
- export declare function parseIndexedColors(stylesXml: string): string[];
16
- /**
17
- * Extract the most-recently-used colour swatches (`<colors><mruColors>`) from styles.xml as verbatim
18
- * `<color .../>` fragments, or an empty list when the file declares none. Kept raw for the same reason
19
- * the indexed palette is: the list is the author's own working set of colours and the model has no
20
- * use for its contents, only for not losing them.
21
- */
22
- export declare function parseMruColors(stylesXml: string): string[];
23
- /**
24
- * Extract the `<tableStyles>` block from styles.xml: each `<tableStyle>` definition verbatim, plus the
25
- * container's nominated `defaultTableStyle`/`defaultPivotStyle`. See {@link TableStyleTable} for why
26
- * the definitions stay raw while the two names are decoded.
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
30
- * carry.
31
- */
32
- export declare function parseTableStyles(stylesXml: string): TableStyleTable;
@@ -1,14 +1,17 @@
1
- import { ALIGNMENT_FACETS, isBorderStyle, isFillPatternType, isFontScheme, isFontVerticalAlignment, isNamedUnderlineStyle, } from '../../core/style.js';
1
+ import { ALIGNMENT_FACETS, isBorderStyle, isFillPatternType, } from '../../core/style.js';
2
+ import { numFinite, numInteger } from '../../xml/xml-attrs.js';
2
3
  import { closeEmptyElements, elementSubtrees, openElements, } from '../../xml/xml-read.js';
3
- import { boolPresent, boolStrict, boolTristate, localName, numFinite, numInteger, xmlEvents, } from '../../xml/xml-scan.js';
4
- import { NO_PRESERVED_STYLE_TABLES, numFmtCodeFor, resolveStyleTable, } from '../style/xf-style.js';
4
+ import { boolStrict, boolTristate, localName, xmlEvents, } from '../../xml/xml-scan.js';
5
+ import { NO_PRESERVED_STYLE_TABLES, numFmtCodeFor, protectionFrom, resolveStyleTable, } from '../style/xf-style.js';
5
6
  import { parseColor } from './color-xml.js';
7
+ import { applyFontChild } from './font-xml.js';
6
8
  const BORDER_EDGE_NAMES = ['left', 'right', 'top', 'bottom', 'diagonal'];
7
9
  const BORDER_EDGES = new Set(BORDER_EDGE_NAMES);
8
10
  const isBorderEdgeName = (name) => BORDER_EDGES.has(name);
9
11
  const STYLE_EMPTY_CLOSES = new Set([
10
12
  'font',
11
13
  'border',
14
+ 'fill',
12
15
  'patternFill',
13
16
  'gradientFill',
14
17
  'xf',
@@ -109,30 +112,37 @@ function parseFonts(events) {
109
112
  }
110
113
  function parseFills(events) {
111
114
  const fills = [];
112
- let pattern = '';
113
- let fgColor;
114
- let bgColor;
115
+ let fillDraft = null;
116
+ let patternDraft = null;
115
117
  let gradientDraft = null;
116
- let fillSlotAt = -1;
117
118
  for (const event of until(events, 'fills')) {
118
119
  if (event.kind === 'open') {
119
120
  const attrs = event.attrs;
121
+ const bodyExpected = fillDraft !== null && !fillDraft.bodyRead;
120
122
  switch (localName(event.name)) {
121
123
  case 'fill':
122
- fillSlotAt = fills.length;
124
+ fillDraft = { body: undefined, bodyRead: false };
123
125
  break;
124
126
  case 'patternFill':
125
- pattern = attrs.patternType ?? 'none';
126
- fgColor = undefined;
127
- bgColor = undefined;
127
+ if (!bodyExpected)
128
+ break;
129
+ patternDraft = {
130
+ pattern: attrs.patternType ?? 'none',
131
+ fgColor: undefined,
132
+ bgColor: undefined,
133
+ };
128
134
  break;
129
135
  case 'fgColor':
130
- fgColor = parseColor(attrs);
136
+ if (patternDraft !== null)
137
+ patternDraft.fgColor = parseColor(attrs);
131
138
  break;
132
139
  case 'bgColor':
133
- bgColor = parseColor(attrs);
140
+ if (patternDraft !== null)
141
+ patternDraft.bgColor = parseColor(attrs);
134
142
  break;
135
143
  case 'gradientFill':
144
+ if (!bodyExpected)
145
+ break;
136
146
  gradientDraft = {
137
147
  fill: {
138
148
  type: 'gradient',
@@ -159,7 +169,12 @@ function parseFills(events) {
159
169
  else if (event.kind === 'close') {
160
170
  switch (localName(event.name)) {
161
171
  case 'patternFill':
162
- 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
+ }
163
178
  break;
164
179
  case 'stop':
165
180
  if (gradientDraft !== null && gradientDraft.stopPosition !== null) {
@@ -173,14 +188,18 @@ function parseFills(events) {
173
188
  }
174
189
  break;
175
190
  case 'gradientFill':
176
- if (gradientDraft !== null) {
177
- fills.push(gradientDraft.fill);
191
+ if (fillDraft !== null && gradientDraft !== null) {
192
+ fillDraft.body = gradientDraft.fill;
193
+ fillDraft.bodyRead = true;
178
194
  gradientDraft = null;
179
195
  }
180
196
  break;
181
197
  case 'fill':
182
- if (fills.length === fillSlotAt)
183
- fills.push(undefined);
198
+ if (fillDraft !== null)
199
+ fills.push(fillDraft.body);
200
+ fillDraft = null;
201
+ patternDraft = null;
202
+ gradientDraft = null;
184
203
  break;
185
204
  }
186
205
  }
@@ -309,68 +328,6 @@ function parseCellStyles(events) {
309
328
  }
310
329
  return names;
311
330
  }
312
- export function applyFontChild(draft, local, attrs) {
313
- switch (local) {
314
- case 'b':
315
- draft.bold = boolPresent(attrs.val);
316
- break;
317
- case 'i':
318
- draft.italic = boolPresent(attrs.val);
319
- break;
320
- case 'strike':
321
- draft.strike = boolPresent(attrs.val);
322
- break;
323
- case 'outline':
324
- draft.outline = boolPresent(attrs.val);
325
- break;
326
- case 'u':
327
- draft.underline =
328
- attrs.val === undefined
329
- ? true
330
- : attrs.val === 'none'
331
- ? false
332
- : isNamedUnderlineStyle(attrs.val)
333
- ? attrs.val
334
- : true;
335
- break;
336
- case 'vertAlign':
337
- if (attrs.val !== undefined && isFontVerticalAlignment(attrs.val))
338
- draft.vertAlign = attrs.val;
339
- break;
340
- case 'sz': {
341
- const size = numFinite(attrs.val);
342
- if (size !== undefined)
343
- draft.size = size;
344
- break;
345
- }
346
- case 'color':
347
- draft.color = parseColor(attrs);
348
- break;
349
- case 'name':
350
- case 'rFont':
351
- if (attrs.val !== undefined)
352
- draft.name = attrs.val;
353
- break;
354
- case 'family': {
355
- const family = numInteger(attrs.val);
356
- if (family !== undefined)
357
- draft.family = family;
358
- break;
359
- }
360
- case 'charset': {
361
- const charset = numInteger(attrs.val);
362
- if (charset !== undefined)
363
- draft.charset = charset;
364
- break;
365
- }
366
- case 'scheme':
367
- if (attrs.val !== undefined && isFontScheme(attrs.val))
368
- draft.scheme = attrs.val;
369
- break;
370
- default:
371
- break;
372
- }
373
- }
374
331
  function resolveNumFmt(raw, custom) {
375
332
  const id = numInteger(raw, 1);
376
333
  return id === undefined ? undefined : numFmtCodeFor(id, custom);
@@ -426,36 +383,26 @@ function assignAlignmentToken(out, key, raw) {
426
383
  out[key] = raw;
427
384
  }
428
385
  function parseProtection(attrs) {
429
- const out = {};
430
- if (boolTristate(attrs.locked) === false)
431
- out.locked = false;
432
- if (boolStrict(attrs.hidden))
433
- out.hidden = true;
434
- return Object.keys(out).length > 0 ? out : undefined;
435
- }
436
- export function parseIndexedColors(stylesXml) {
437
- return capturedFragments(stylesXml, 'indexedColors');
438
- }
439
- export function parseMruColors(stylesXml) {
440
- return capturedFragments(stylesXml, 'mruColors');
441
- }
442
- export function parseTableStyles(stylesXml) {
443
- const { fragments, attributes } = elementSubtrees(stylesXml, new Map([['tableStyles', 'tableStyle']]));
444
- return buildTableStyleTable(stylesXml, fragments.get('tableStyles') ?? [], attributes.get('tableStyles'));
386
+ return protectionFrom({ locked: boolTristate(attrs.locked), hidden: boolStrict(attrs.hidden) });
445
387
  }
446
388
  function buildTableStyleTable(stylesXml, styles, containerAttrs) {
447
- const table = { styles: [...styles] };
389
+ const table = { styles: styles.map((xml) => ({ name: tableStyleNameOf(xml), xml })) };
448
390
  if (containerAttrs?.defaultTableStyle !== undefined) {
449
391
  table.defaultTableStyle = containerAttrs.defaultTableStyle;
450
392
  }
451
393
  if (containerAttrs?.defaultPivotStyle !== undefined) {
452
394
  table.defaultPivotStyle = containerAttrs.defaultPivotStyle;
453
395
  }
454
- const namespaces = fragmentNamespaces(stylesXml, table.styles);
396
+ const namespaces = fragmentNamespaces(stylesXml, styles);
455
397
  if (namespaces.length > 0)
456
398
  table.namespaces = namespaces;
457
399
  return table;
458
400
  }
401
+ function tableStyleNameOf(fragment) {
402
+ for (const { attrs } of openElements(fragment, 'tableStyle'))
403
+ return attrs.name ?? '';
404
+ return '';
405
+ }
459
406
  function fragmentNamespaces(stylesXml, fragments) {
460
407
  if (fragments.length === 0)
461
408
  return [];
@@ -492,10 +439,3 @@ const PRESERVED_SUBTREES = new Map([
492
439
  ['mruColors', 'color'],
493
440
  ['tableStyles', 'tableStyle'],
494
441
  ]);
495
- function capturedFragments(xml, container) {
496
- const child = PRESERVED_SUBTREES.get(container);
497
- if (child === undefined)
498
- return [];
499
- const { fragments } = elementSubtrees(xml, new Map([[container, child]]));
500
- return [...(fragments.get(container) ?? [])];
501
- }
@@ -0,0 +1,42 @@
1
+ import { type WorkbookProtection } from '../../core/workbook-protection.ts';
2
+ import type { DefinedName, Workbook, WorkbookView } from '../../core/workbook.ts';
3
+ import { type WorksheetState } from '../../core/worksheet.ts';
4
+ import { type CollectingPass, type SaxPass } from '../../xml/xml-read.ts';
5
+ export interface SheetEntry {
6
+ readonly name: string;
7
+ readonly relId: string;
8
+ readonly state?: WorksheetState['state'];
9
+ }
10
+ export declare function workbookSheetsPass(): CollectingPass<SheetEntry[]>;
11
+ export declare function workbookProtectionPass(): CollectingPass<WorkbookProtection | undefined>;
12
+ /**
13
+ * Apply `<workbookPr>`: the workbook's date system and its VBA code name.
14
+ *
15
+ * Both are read for the same reason and it is not that the model computes anything from them. The
16
+ * date system is what every serial in every sheet counts from, so a workbook read without it is read
17
+ * with every date four years and a day out; the code name is what the VBA project means by
18
+ * `ThisWorkbook`, so a `.xlsm` written back without it has had its macros unbound from its document.
19
+ * Neither has any signal in the file other than this element.
20
+ */
21
+ export declare function workbookPropertiesPass(workbook: Workbook): SaxPass;
22
+ export declare function workbookViewPass(view: WorkbookView): SaxPass;
23
+ export interface DefinedNamesPass extends SaxPass {
24
+ /**
25
+ * The names, scoped against the sheet order.
26
+ *
27
+ * The order is supplied here rather than to the pass, and that is what lets the names ride the same
28
+ * scan as everything else this part carries. A scoped name's `localSheetId` indexes the sheets in
29
+ * declaration order, and the model's answer for index n is known only after the sheet loop has run
30
+ * and repaired the names it had to; resolving during the scan would have forced a second one.
31
+ */
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>;
39
+ }
40
+ export declare function definedNamesPass(): DefinedNamesPass;
41
+ export declare function applyCoreProperties(workbook: Workbook, xml: string): void;
42
+ export declare function applyAppProperties(workbook: Workbook, xml: string): void;