@shbernal/ts-xlsx 2.1.0 → 3.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 (225) hide show
  1. package/README.md +4 -1
  2. package/dist/bytes.d.ts +7 -0
  3. package/dist/bytes.js +22 -0
  4. package/dist/core/address.d.ts +60 -2
  5. package/dist/core/address.js +40 -24
  6. package/dist/core/autofilter.d.ts +5 -4
  7. package/dist/core/autofilter.js +34 -35
  8. package/dist/core/axis-handle.d.ts +10 -0
  9. package/dist/core/axis-handle.js +17 -0
  10. package/dist/core/cell.d.ts +25 -0
  11. package/dist/core/cell.js +9 -3
  12. package/dist/core/clone.d.ts +29 -0
  13. package/dist/core/clone.js +24 -0
  14. package/dist/core/color-resolution.js +2 -4
  15. package/dist/core/column.d.ts +5 -1
  16. package/dist/core/column.js +34 -35
  17. package/dist/core/comment-thread.js +2 -1
  18. package/dist/core/conditional-formatting-overlay.d.ts +2 -1
  19. package/dist/core/conditional-formatting-overlay.js +2 -2
  20. package/dist/core/conditional-formatting.d.ts +14 -6
  21. package/dist/core/conditional-formatting.js +45 -43
  22. package/dist/core/containers.d.ts +12 -0
  23. package/dist/core/containers.js +5 -0
  24. package/dist/core/data-validation-overlay.d.ts +2 -1
  25. package/dist/core/data-validation-overlay.js +4 -4
  26. package/dist/core/data-validation.d.ts +9 -3
  27. package/dist/core/data-validation.js +21 -18
  28. package/dist/core/date-format.d.ts +20 -0
  29. package/dist/core/date-format.js +160 -0
  30. package/dist/core/date.d.ts +43 -8
  31. package/dist/core/date.js +18 -2
  32. package/dist/core/formula.d.ts +26 -0
  33. package/dist/core/formula.js +20 -4
  34. package/dist/core/grid-edits.d.ts +2 -3
  35. package/dist/core/grid-edits.js +69 -70
  36. package/dist/core/grid-shift.d.ts +58 -9
  37. package/dist/core/grid-shift.js +26 -4
  38. package/dist/core/image.d.ts +14 -10
  39. package/dist/core/image.js +9 -9
  40. package/dist/core/internal.d.ts +8 -2
  41. package/dist/core/limits.d.ts +23 -0
  42. package/dist/core/limits.js +4 -0
  43. package/dist/core/merge-index.d.ts +31 -0
  44. package/dist/core/merge-index.js +60 -0
  45. package/dist/core/merge.d.ts +5 -14
  46. package/dist/core/merge.js +18 -27
  47. package/dist/core/page-setup.d.ts +72 -2
  48. package/dist/core/page-setup.js +34 -8
  49. package/dist/core/pivot-table.d.ts +1 -1
  50. package/dist/core/pivot-table.js +67 -53
  51. package/dist/core/protection.js +2 -11
  52. package/dist/core/range.js +34 -30
  53. package/dist/core/row-input.js +2 -2
  54. package/dist/core/row.d.ts +5 -1
  55. package/dist/core/row.js +22 -23
  56. package/dist/core/style.d.ts +134 -75
  57. package/dist/core/style.js +112 -53
  58. package/dist/core/table-style.d.ts +1 -1
  59. package/dist/core/table-style.js +4 -4
  60. package/dist/core/table.d.ts +28 -10
  61. package/dist/core/table.js +54 -39
  62. package/dist/core/theme.js +6 -5
  63. package/dist/core/used-extent.d.ts +44 -0
  64. package/dist/core/used-extent.js +102 -0
  65. package/dist/core/value.d.ts +9 -0
  66. package/dist/core/value.js +36 -34
  67. package/dist/core/workbook-media.d.ts +34 -0
  68. package/dist/core/workbook-media.js +44 -0
  69. package/dist/core/workbook-styles.d.ts +67 -1
  70. package/dist/core/workbook-vba.js +3 -2
  71. package/dist/core/workbook.d.ts +23 -2
  72. package/dist/core/workbook.js +24 -40
  73. package/dist/core/worksheet-comments.d.ts +2 -1
  74. package/dist/core/worksheet-comments.js +20 -13
  75. package/dist/core/worksheet-merges.d.ts +44 -0
  76. package/dist/core/worksheet-merges.js +59 -0
  77. package/dist/core/worksheet-model.d.ts +9 -1
  78. package/dist/core/worksheet-model.js +24 -9
  79. package/dist/core/worksheet.d.ts +72 -12
  80. package/dist/core/worksheet.js +68 -70
  81. package/dist/customui/ribbon.js +6 -7
  82. package/dist/entries/core.d.ts +18 -13
  83. package/dist/entries/core.js +12 -6
  84. package/dist/entries/xlsx.d.ts +1 -1
  85. package/dist/errors.d.ts +64 -0
  86. package/dist/errors.js +22 -0
  87. package/dist/hex.d.ts +8 -0
  88. package/dist/hex.js +3 -0
  89. package/dist/io/csv/delimiter.d.ts +9 -0
  90. package/dist/io/csv/delimiter.js +6 -0
  91. package/dist/io/csv/read.js +11 -4
  92. package/dist/io/csv/write.d.ts +9 -2
  93. package/dist/io/csv/write.js +9 -38
  94. package/dist/io/opc/inflate.js +15 -1
  95. package/dist/io/opc/namespaces.d.ts +16 -0
  96. package/dist/io/opc/namespaces.js +4 -0
  97. package/dist/io/opc/part-paths.d.ts +10 -0
  98. package/dist/io/opc/part-paths.js +16 -1
  99. package/dist/io/opc/read-opc.d.ts +69 -10
  100. package/dist/io/opc/read-opc.js +49 -55
  101. package/dist/io/opc/read-options.d.ts +5 -1
  102. package/dist/io/opc/rels.d.ts +1 -6
  103. package/dist/io/opc/rels.js +1 -4
  104. package/dist/io/opc/sniff-format.d.ts +3 -3
  105. package/dist/io/opc/sniff-format.js +2 -2
  106. package/dist/io/style/xf-style.d.ts +48 -2
  107. package/dist/io/style/xf-style.js +14 -0
  108. package/dist/io/xlsb/formula.d.ts +2 -2
  109. package/dist/io/xlsb/formula.js +49 -34
  110. package/dist/io/xlsb/primitives.js +8 -14
  111. package/dist/io/xlsb/ptg-functions.d.ts +19 -10
  112. package/dist/io/xlsb/ptg-functions.js +385 -596
  113. package/dist/io/xlsb/read-styles.js +50 -83
  114. package/dist/io/xlsb/read-worksheet.d.ts +20 -1
  115. package/dist/io/xlsb/read-worksheet.js +73 -89
  116. package/dist/io/xlsb/read.d.ts +6 -3
  117. package/dist/io/xlsb/read.js +47 -29
  118. package/dist/io/xlsb/record-stream.d.ts +30 -0
  119. package/dist/io/xlsb/record-stream.js +20 -0
  120. package/dist/io/xlsb/record-types.d.ts +1 -5
  121. package/dist/io/xlsb/record-types.js +1 -5
  122. package/dist/io/xlsx/cell-accumulator.d.ts +9 -2
  123. package/dist/io/xlsx/cell-accumulator.js +32 -9
  124. package/dist/io/xlsx/cell-style-resolution.d.ts +25 -0
  125. package/dist/io/xlsx/cell-style-resolution.js +29 -0
  126. package/dist/io/xlsx/cell-value.d.ts +5 -3
  127. package/dist/io/xlsx/cell-value.js +13 -15
  128. package/dist/io/xlsx/color-xml.js +3 -2
  129. package/dist/io/xlsx/column-budget.d.ts +26 -0
  130. package/dist/io/xlsx/column-budget.js +22 -0
  131. package/dist/io/xlsx/comments.d.ts +6 -2
  132. package/dist/io/xlsx/comments.js +36 -31
  133. package/dist/io/xlsx/conditional-formatting.d.ts +9 -5
  134. package/dist/io/xlsx/conditional-formatting.js +51 -46
  135. package/dist/io/xlsx/data-validation.d.ts +9 -7
  136. package/dist/io/xlsx/data-validation.js +38 -35
  137. package/dist/io/xlsx/edit-vba.d.ts +7 -2
  138. package/dist/io/xlsx/edit-vba.js +15 -29
  139. package/dist/io/xlsx/font-xml.d.ts +7 -0
  140. package/dist/io/xlsx/font-xml.js +104 -0
  141. package/dist/io/xlsx/hyperlinks.d.ts +41 -7
  142. package/dist/io/xlsx/hyperlinks.js +40 -28
  143. package/dist/io/xlsx/images.js +24 -25
  144. package/dist/io/xlsx/namespaces.d.ts +16 -0
  145. package/dist/io/xlsx/namespaces.js +7 -0
  146. package/dist/io/xlsx/package-plan.d.ts +23 -3
  147. package/dist/io/xlsx/package-plan.js +66 -25
  148. package/dist/io/xlsx/part-names.d.ts +41 -0
  149. package/dist/io/xlsx/part-names.js +46 -0
  150. package/dist/io/xlsx/pivot.js +8 -7
  151. package/dist/io/xlsx/read-parts.d.ts +59 -0
  152. package/dist/io/xlsx/read-parts.js +252 -0
  153. package/dist/io/xlsx/read-pivot.js +3 -1
  154. package/dist/io/xlsx/read-repair.d.ts +26 -0
  155. package/dist/io/xlsx/read-repair.js +39 -0
  156. package/dist/io/xlsx/{rich-runs.d.ts → read-rich-runs.d.ts} +1 -1
  157. package/dist/io/xlsx/{rich-runs.js → read-rich-runs.js} +7 -3
  158. package/dist/io/xlsx/read-rows.d.ts +18 -5
  159. package/dist/io/xlsx/read-rows.js +50 -37
  160. package/dist/io/xlsx/read-shared-strings.js +5 -3
  161. package/dist/io/xlsx/read-styles.d.ts +1 -6
  162. package/dist/io/xlsx/read-styles.js +65 -109
  163. package/dist/io/xlsx/read-workbook-xml.d.ts +36 -0
  164. package/dist/io/xlsx/read-workbook-xml.js +170 -0
  165. package/dist/io/xlsx/read-worksheet.d.ts +11 -1
  166. package/dist/io/xlsx/read-worksheet.js +51 -193
  167. package/dist/io/xlsx/read.d.ts +5 -13
  168. package/dist/io/xlsx/read.js +111 -436
  169. package/dist/io/xlsx/rich-text.js +1 -1
  170. package/dist/io/xlsx/row-position.d.ts +18 -0
  171. package/dist/io/xlsx/row-position.js +11 -0
  172. package/dist/io/xlsx/row-xml.d.ts +128 -0
  173. package/dist/io/xlsx/row-xml.js +203 -0
  174. package/dist/io/xlsx/shared-formulas.js +2 -2
  175. package/dist/io/xlsx/sheet-properties.d.ts +10 -1
  176. package/dist/io/xlsx/sheet-properties.js +166 -37
  177. package/dist/io/xlsx/style-elements.d.ts +43 -0
  178. package/dist/io/xlsx/style-elements.js +156 -0
  179. package/dist/io/xlsx/styles.d.ts +12 -3
  180. package/dist/io/xlsx/styles.js +5 -204
  181. package/dist/io/xlsx/tables.js +11 -14
  182. package/dist/io/xlsx/theme-xml.d.ts +26 -4
  183. package/dist/io/xlsx/theme-xml.js +107 -69
  184. package/dist/io/xlsx/threaded-comments.d.ts +8 -10
  185. package/dist/io/xlsx/threaded-comments.js +3 -1
  186. package/dist/io/xlsx/workbook-xml.d.ts +54 -4
  187. package/dist/io/xlsx/workbook-xml.js +84 -57
  188. package/dist/io/xlsx/worksheet-xml.d.ts +23 -70
  189. package/dist/io/xlsx/worksheet-xml.js +59 -242
  190. package/dist/io/xlsx/write-stream.d.ts +71 -16
  191. package/dist/io/xlsx/write-stream.js +79 -41
  192. package/dist/io/xlsx/write.d.ts +6 -2
  193. package/dist/io/xlsx/write.js +217 -153
  194. package/dist/rel-type.d.ts +16 -0
  195. package/dist/rel-type.js +6 -0
  196. package/dist/token-set.d.ts +28 -0
  197. package/dist/token-set.js +7 -0
  198. package/dist/vba/bytes.d.ts +14 -5
  199. package/dist/vba/bytes.js +16 -6
  200. package/dist/vba/cfb-format.d.ts +32 -0
  201. package/dist/vba/cfb-format.js +13 -0
  202. package/dist/vba/cfb-writer.d.ts +9 -0
  203. package/dist/vba/cfb-writer.js +28 -25
  204. package/dist/vba/cfb.d.ts +13 -1
  205. package/dist/vba/cfb.js +73 -57
  206. package/dist/vba/codepage.js +2 -1
  207. package/dist/vba/errors.d.ts +7 -0
  208. package/dist/vba/ms-ovba.js +79 -23
  209. package/dist/vba/project-editor.js +35 -40
  210. package/dist/vba/project.d.ts +11 -1
  211. package/dist/vba/project.js +40 -21
  212. package/dist/vba/vba-encoding.js +5 -6
  213. package/dist/xml/xml-attrs.d.ts +37 -0
  214. package/dist/xml/xml-attrs.js +30 -0
  215. package/dist/xml/xml-chars.d.ts +40 -0
  216. package/dist/xml/xml-chars.js +12 -0
  217. package/dist/xml/xml-namespaces.d.ts +31 -0
  218. package/dist/xml/xml-namespaces.js +50 -0
  219. package/dist/xml/xml-read.d.ts +128 -81
  220. package/dist/xml/xml-read.js +161 -178
  221. package/dist/xml/xml-scan.d.ts +78 -0
  222. package/dist/xml/xml-scan.js +180 -0
  223. package/dist/xml/xml.d.ts +29 -28
  224. package/dist/xml/xml.js +24 -24
  225. package/package.json +13 -9
@@ -0,0 +1,252 @@
1
+ import { decodeRange } from '../../core/address.js';
2
+ import { INTERNAL } from '../../core/internal.js';
3
+ import { Workbook } from '../../core/workbook.js';
4
+ import { isAnyRelType } from '../../rel-type.js';
5
+ import { localName } from '../../xml/xml-scan.js';
6
+ import { relAttr } from '../opc/namespaces.js';
7
+ import { extensionOf, resolveRelativePart } from '../opc/part-paths.js';
8
+ import { capturePartClosure, parseRelationshipRecords, readPartRelationships, } from '../opc/read-opc.js';
9
+ import { parseComments } from './comments.js';
10
+ import { drawingHasUnmodeledContent, parseDrawing } from './images.js';
11
+ import { parsePivotTable } from './read-pivot.js';
12
+ import { admitting } from './read-repair.js';
13
+ import { parseTable } from './tables.js';
14
+ import { parseThemeColorScheme, parseThemeFontScheme } from './theme-xml.js';
15
+ import { buildCommentThreads, parsePersons, parseThreadedComments } from './threaded-comments.js';
16
+ export function readSheetComments(sheetRels) {
17
+ const commentsXml = sheetRels.relatedText('comments');
18
+ return commentsXml === undefined ? undefined : parseComments(commentsXml);
19
+ }
20
+ export function readWorkbookPersons({ workbook }, workbookRels) {
21
+ const xml = workbookRels.relatedText('person');
22
+ if (xml !== undefined)
23
+ workbook[INTERNAL].restorePersons(parsePersons(xml));
24
+ }
25
+ export function readWorkbookTheme(context, workbookRels) {
26
+ const { pkg, contentTypeOf, workbook } = context;
27
+ const entryPath = workbookRels.targetPath('theme');
28
+ if (entryPath === undefined)
29
+ return;
30
+ const parts = capturePartClosure(entryPath, pkg.partText, pkg.partBytes, contentTypeOf);
31
+ if (parts === undefined)
32
+ return;
33
+ const xml = pkg.partText(entryPath) ?? '';
34
+ workbook[INTERNAL].restoreThemePart({ entryPath, parts }, { colors: parseThemeColorScheme(xml), fonts: parseThemeFontScheme(xml) });
35
+ }
36
+ export function readSheetCommentThreads({ workbook }, sheetRels) {
37
+ const xml = sheetRels.relatedText('threadedComment');
38
+ if (xml === undefined)
39
+ return [];
40
+ return buildCommentThreads(parseThreadedComments(xml), (id) => workbook.getPerson(id));
41
+ }
42
+ export function readSheetPrinterSettings(sheetRels) {
43
+ return sheetRels.relatedBytes('printerSettings');
44
+ }
45
+ function internImage(context, mediaPath) {
46
+ const { workbook, pkg, imageIdByMediaPath } = context;
47
+ const known = imageIdByMediaPath.get(mediaPath);
48
+ if (known !== undefined)
49
+ return known;
50
+ const bytes = pkg.partBytes(mediaPath);
51
+ if (bytes === undefined)
52
+ return undefined;
53
+ const id = workbook.addImage({ buffer: bytes, extension: extensionOf(mediaPath) });
54
+ imageIdByMediaPath.set(mediaPath, id);
55
+ return id;
56
+ }
57
+ export function readSheetImages(context, sheetRels, sheet) {
58
+ const { partText } = context.pkg;
59
+ const drawingPath = sheetRels.targetPath('drawing');
60
+ if (drawingPath === undefined)
61
+ return;
62
+ const drawingXml = partText(drawingPath);
63
+ if (drawingXml === undefined)
64
+ return;
65
+ if (drawingHasUnmodeledContent(drawingXml))
66
+ return;
67
+ const drawingRels = readPartRelationships(drawingPath, partText);
68
+ for (const anchor of parseDrawing(drawingXml)) {
69
+ const embedded = drawingRels.byId(anchor.embed);
70
+ if (embedded === undefined)
71
+ continue;
72
+ const mediaPath = drawingRels.pathOf(embedded.target);
73
+ const id = internImage(context, mediaPath);
74
+ if (id === undefined)
75
+ continue;
76
+ const rot = anchor.rotation !== undefined ? { rotation: anchor.rotation } : {};
77
+ if (anchor.to !== undefined) {
78
+ const mode = anchor.editAs !== undefined ? { editAs: anchor.editAs } : {};
79
+ sheet.addImageAnchor(id, { from: anchor.from, to: anchor.to, ...mode, ...rot });
80
+ }
81
+ else if (anchor.ext !== undefined) {
82
+ sheet.addImageAnchor(id, { from: anchor.from, ext: anchor.ext, ...rot });
83
+ }
84
+ }
85
+ }
86
+ export function readSheetBackground(context, sheetRels, sheet) {
87
+ const mediaPath = sheetRels.targetPath('image');
88
+ if (mediaPath === undefined)
89
+ return;
90
+ const id = internImage(context, mediaPath);
91
+ if (id !== undefined)
92
+ sheet.addBackgroundImage(id);
93
+ }
94
+ export function readSheetPreservedReferences(context, sheetRels, referenceRelIds, sheet) {
95
+ const { contentTypeOf } = context;
96
+ const { partText, partBytes } = context.pkg;
97
+ const capture = (element, relType, target) => {
98
+ const entryPath = sheetRels.pathOf(target);
99
+ const parts = capturePartClosure(entryPath, partText, partBytes, contentTypeOf);
100
+ if (parts !== undefined)
101
+ sheet[INTERNAL].addPreservedReference({ element, relType, entryPath, parts });
102
+ };
103
+ const referenceElements = sheet.images.length === 0 ? ['drawing', 'legacyDrawingHF'] : ['legacyDrawingHF'];
104
+ for (const element of referenceElements) {
105
+ const relId = referenceRelIds[element];
106
+ const record = relId === undefined ? undefined : sheetRels.byId(relId);
107
+ if (record !== undefined && !record.external)
108
+ capture(element, record.type, record.target);
109
+ }
110
+ for (const record of sheetRels.records) {
111
+ if (record.external)
112
+ continue;
113
+ if (isPreservedSheetRelType(record.type))
114
+ capture(undefined, record.type, record.target);
115
+ }
116
+ }
117
+ function isPreservedSheetRelType(type) {
118
+ return isAnyRelType(type, 'pivotTable', 'slicer');
119
+ }
120
+ export function readWorkbookPreservedReferences(context, workbookRels, registrations) {
121
+ const { contentTypeOf, workbook } = context;
122
+ const { partText, partBytes } = context.pkg;
123
+ const { cacheIdByRelId, externalIndexByRelId } = registrations;
124
+ for (const record of workbookRels.records) {
125
+ if (record.external || !isPreservedWorkbookRelType(record.type))
126
+ continue;
127
+ const entryPath = workbookRels.pathOf(record.target);
128
+ const parts = capturePartClosure(entryPath, partText, partBytes, contentTypeOf);
129
+ if (parts === undefined)
130
+ continue;
131
+ const cacheId = cacheIdByRelId.get(record.id);
132
+ const externalReferenceIndex = externalIndexByRelId.get(record.id);
133
+ workbook[INTERNAL].addPreservedReference({
134
+ relType: record.type,
135
+ entryPath,
136
+ parts,
137
+ ...(cacheId !== undefined ? { pivotCacheId: cacheId } : {}),
138
+ ...(externalReferenceIndex !== undefined ? { externalReferenceIndex } : {}),
139
+ });
140
+ }
141
+ }
142
+ export function readRootPreservedReferences(context) {
143
+ const { contentTypeOf, workbook } = context;
144
+ const { partText, partBytes } = context.pkg;
145
+ const relsXml = partText('_rels/.rels');
146
+ if (relsXml === undefined)
147
+ return;
148
+ for (const record of parseRelationshipRecords(relsXml)) {
149
+ if (record.external || isRegeneratedRootRelType(record.type))
150
+ continue;
151
+ const entryPath = resolveRelativePart('', record.target);
152
+ const parts = capturePartClosure(entryPath, partText, partBytes, contentTypeOf);
153
+ if (parts === undefined)
154
+ continue;
155
+ workbook[INTERNAL].addPreservedRootReference({ relType: record.type, entryPath, parts });
156
+ }
157
+ }
158
+ function isRegeneratedRootRelType(type) {
159
+ return isAnyRelType(type, 'officeDocument', 'core-properties', 'extended-properties');
160
+ }
161
+ function isPreservedWorkbookRelType(type) {
162
+ return isAnyRelType(type, 'pivotCacheDefinition', 'slicerCache', 'vbaProject', 'externalLink');
163
+ }
164
+ export function pivotCacheRegistrationsPass() {
165
+ const byRelId = new Map();
166
+ return {
167
+ handlers: {
168
+ onOpen(name, attrs, _selfClosing, scope) {
169
+ if (localName(name) !== 'pivotCache')
170
+ return;
171
+ const relId = relAttr(scope, attrs, 'id');
172
+ if (relId !== undefined && attrs.cacheId !== undefined)
173
+ byRelId.set(relId, attrs.cacheId);
174
+ },
175
+ },
176
+ result: () => byRelId,
177
+ };
178
+ }
179
+ export function externalReferenceRegistrationsPass() {
180
+ const byRelId = new Map();
181
+ let index = 0;
182
+ return {
183
+ handlers: {
184
+ onOpen(name, attrs, _selfClosing, scope) {
185
+ if (localName(name) !== 'externalReference')
186
+ return;
187
+ const relId = relAttr(scope, attrs, 'id');
188
+ if (relId !== undefined)
189
+ byRelId.set(relId, index++);
190
+ },
191
+ },
192
+ result: () => byRelId,
193
+ };
194
+ }
195
+ export function worksheetReferencePass() {
196
+ const relIds = {};
197
+ return {
198
+ handlers: {
199
+ onOpen(name, attrs, _selfClosing, scope) {
200
+ const local = localName(name);
201
+ if (local !== 'drawing' && local !== 'legacyDrawingHF')
202
+ return;
203
+ if (relIds[local] !== undefined)
204
+ return;
205
+ const relId = relAttr(scope, attrs, 'id');
206
+ if (relId !== undefined)
207
+ relIds[local] = relId;
208
+ },
209
+ },
210
+ result: () => relIds,
211
+ };
212
+ }
213
+ export function readSheetTables({ pkg }, sheetRels, sheet) {
214
+ for (const tablePath of sheetRels.targetPaths('table')) {
215
+ const tableXml = pkg.partText(tablePath);
216
+ if (tableXml === undefined)
217
+ continue;
218
+ const options = parseTable(tableXml);
219
+ if (options !== undefined) {
220
+ admitting(() => {
221
+ sheet.addTable(options);
222
+ });
223
+ }
224
+ }
225
+ dropMergesInsideTables(sheet);
226
+ }
227
+ export function readSheetPivotTables({ pkg }, sheetRels, sheet) {
228
+ const { partText } = pkg;
229
+ for (const tablePath of sheetRels.targetPaths('pivotTable')) {
230
+ const tableXml = partText(tablePath);
231
+ if (tableXml === undefined)
232
+ continue;
233
+ const cacheXml = readPartRelationships(tablePath, partText).relatedText('pivotCacheDefinition') ?? '';
234
+ sheet[INTERNAL].addLoadedPivotTable(parsePivotTable(tableXml, cacheXml));
235
+ }
236
+ }
237
+ function dropMergesInsideTables(sheet) {
238
+ const regions = sheet.tables.map((table) => table.region);
239
+ if (regions.length === 0)
240
+ return;
241
+ for (const range of [...sheet.merges]) {
242
+ const { top, left, bottom, right } = decodeRange(range);
243
+ if (top === undefined || left === undefined || bottom === undefined || right === undefined)
244
+ continue;
245
+ const overlaps = regions.some((region) => left <= region.right &&
246
+ right >= region.left &&
247
+ top <= region.bottom &&
248
+ bottom >= region.top);
249
+ if (overlaps)
250
+ sheet.unmergeCells(range);
251
+ }
252
+ }
@@ -1,5 +1,7 @@
1
1
  import { isDeclarablePivotSourceKind, pivotMetricFromSubtotal, } from '../../core/pivot-table.js';
2
- import { localName, numInteger, parseXml } from '../../xml/xml-read.js';
2
+ import { numInteger } from '../../xml/xml-attrs.js';
3
+ import { parseXml } from '../../xml/xml-read.js';
4
+ import { localName } from '../../xml/xml-scan.js';
3
5
  export function parsePivotTable(tableXml, cacheXml) {
4
6
  const { fields, source } = parsePivotCacheDefinition(cacheXml);
5
7
  const def = parsePivotTableDefinition(tableXml);
@@ -0,0 +1,26 @@
1
+ /**
2
+ * A sheet name from a file, rewritten into one the model will accept.
3
+ *
4
+ * Excel's own repair, in the order the constraints interact: forbidden characters go first (they
5
+ * can be anywhere), then the length ceiling, then the apostrophe rule *again*, because truncating
6
+ * can expose an interior apostrophe at the new edge. An empty result becomes `Sheet{n}`, and a
7
+ * collision takes a ` (2)` suffix that is itself made to fit inside the 31-character limit.
8
+ *
9
+ * @param taken the lower-cased names already in the workbook; sheet names collide
10
+ * case-insensitively, which is the comparison `Workbook.getWorksheet` makes.
11
+ */
12
+ export declare function repairSheetName(name: string, taken: ReadonlySet<string>): string;
13
+ /**
14
+ * Run a model call that is being handed foreign input, and answer `undefined` where the model
15
+ * refuses it.
16
+ *
17
+ * The three caught types are the three a model method raises *about its argument*: an
18
+ * `AuthoringError` for a composite that cannot exist, and the native `RangeError` / `SyntaxError`
19
+ * the taxonomy assigns to a single scalar that is out of range or does not parse. Every one of them
20
+ * is a statement about the caller, and on this path the caller is a file.
21
+ *
22
+ * Nothing else is caught. An `XlsxError` raised by a layer below is a real failure of the read and
23
+ * keeps its identity; an `InternalError` is a bug of ours and must not be swallowed by a reader
24
+ * being tolerant about somebody else's file.
25
+ */
26
+ export declare function admitting<T>(build: () => T): T | undefined;
@@ -0,0 +1,39 @@
1
+ import { INVALID_SHEET_NAME_CHARS, MAX_SHEET_NAME_LENGTH } from '../../core/limits.js';
2
+ import { AuthoringError } from '../../errors.js';
3
+ const INVALID_SHEET_NAME_CHARS_GLOBAL = new RegExp(INVALID_SHEET_NAME_CHARS.source, 'g');
4
+ export function repairSheetName(name, taken) {
5
+ const stripped = trimApostrophes(name.replace(INVALID_SHEET_NAME_CHARS_GLOBAL, '')).slice(0, MAX_SHEET_NAME_LENGTH);
6
+ const base = trimApostrophes(stripped);
7
+ if (base.length === 0)
8
+ return firstFree((n) => `Sheet${n}`, taken);
9
+ if (!taken.has(base.toLowerCase()))
10
+ return base;
11
+ return firstFree((n) => withSuffix(base, ` (${n})`), taken, 2);
12
+ }
13
+ function trimApostrophes(name) {
14
+ return name.replace(/^'+/, '').replace(/'+$/, '');
15
+ }
16
+ function firstFree(candidate, taken, from = 1) {
17
+ for (let n = from;; n++) {
18
+ const name = candidate(n);
19
+ if (!taken.has(name.toLowerCase()))
20
+ return name;
21
+ }
22
+ }
23
+ function withSuffix(base, suffix) {
24
+ const room = MAX_SHEET_NAME_LENGTH - suffix.length;
25
+ return trimApostrophes(base.slice(0, room)) + suffix;
26
+ }
27
+ export function admitting(build) {
28
+ try {
29
+ return build();
30
+ }
31
+ catch (error) {
32
+ if (error instanceof AuthoringError ||
33
+ error instanceof RangeError ||
34
+ error instanceof SyntaxError) {
35
+ return undefined;
36
+ }
37
+ throw error;
38
+ }
39
+ }
@@ -1,5 +1,5 @@
1
1
  import type { RichTextRun } from '../../core/value.ts';
2
- import { type XmlAttributes } from '../../xml/xml-read.ts';
2
+ import { type XmlAttributes } from '../../xml/xml-scan.ts';
3
3
  /**
4
4
  * What {@link RunAccumulator.close} did with an element: `'container'` means the `<si>`/`<is>` itself
5
5
  * closed and the caller should commit whatever it makes of the gathered runs, the one step the two
@@ -1,5 +1,7 @@
1
- import { decodeSpreadsheetText, TextCapture } from '../../xml/xml-read.js';
2
- import { applyFontChild } from './read-styles.js';
1
+ import { decodeSpreadsheetText } from '../../xml/xml-attrs.js';
2
+ import { TextCapture } from '../../xml/xml-read.js';
3
+ import {} from '../../xml/xml-scan.js';
4
+ import { applyFontChild } from './font-xml.js';
3
5
  export class RunAccumulator {
4
6
  #container;
5
7
  #readRuns;
@@ -33,12 +35,14 @@ export class RunAccumulator {
33
35
  this.#plain = '';
34
36
  this.#inRun = false;
35
37
  this.#isRich = false;
38
+ this.#inContainer = false;
39
+ this.#capture.reset();
36
40
  }
37
41
  open(local, attrs, selfClosing) {
38
42
  switch (local) {
39
43
  case this.#container:
40
- this.#inContainer = true;
41
44
  this.beginContainer();
45
+ this.#inContainer = !selfClosing;
42
46
  return true;
43
47
  case 'r':
44
48
  if (!this.#readRuns || !this.#inContainer)
@@ -1,6 +1,6 @@
1
1
  import type { CellValue } from '../../core/value.ts';
2
- import { type ReadXlsxOptions, type XfStyle } from './read.ts';
3
- export interface ReadSheetRowsOptions extends ReadXlsxOptions {
2
+ import { type ReadPackageOptions, type XfStyle } from './read.ts';
3
+ export interface ReadSheetRowsOptions extends ReadPackageOptions {
4
4
  /**
5
5
  * Which worksheet to stream: its name, or its 1-based position in the workbook. Defaults to the
6
6
  * first sheet.
@@ -11,9 +11,18 @@ export interface ReadSheetRowsOptions extends ReadXlsxOptions {
11
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
+ *
15
+ * @unpublished A named commitment this surface is not ready to make. `entries/xlsx.ts` records the
16
+ * decision: the streaming reader's granular output shapes stay inferred structural types while the
17
+ * surface settles, so a consumer destructures them and never has to spell them.
14
18
  */
15
19
  export type StreamedCellStyle = XfStyle;
16
- /** One non-empty cell in a {@link StreamedRow}. */
20
+ /** One non-empty cell in a {@link StreamedRow}.
21
+ *
22
+ * @unpublished A named commitment this surface is not ready to make. `entries/xlsx.ts` records the
23
+ * decision: the streaming reader's granular output shapes stay inferred structural types while the
24
+ * surface settles, so a consumer destructures them and never has to spell them.
25
+ */
17
26
  export interface StreamedCell {
18
27
  /** 1-based column index. */
19
28
  readonly col: number;
@@ -41,6 +50,10 @@ export interface StreamedRow {
41
50
  * its rows have not already been consumed, so their order relative to `rows()` never matters. (When
42
51
  * rows *are* consumed first, the streaming idiom, the accessors reuse that pass and re-scan
43
52
  * nothing.)
53
+ *
54
+ * @unpublished A named commitment this surface is not ready to make. `entries/xlsx.ts` records the
55
+ * decision: the streaming reader's granular output shapes stay inferred structural types while the
56
+ * surface settles, so a consumer destructures them and never has to spell them.
44
57
  */
45
58
  export interface StreamedSheet {
46
59
  /** The worksheet's declared name, joined from the workbook part. Never a positional placeholder. */
@@ -76,10 +89,10 @@ export declare function readSheetRows(data: Uint8Array, options?: ReadSheetRowsO
76
89
  * of walking `readXlsx(data).worksheets`.
77
90
  *
78
91
  * @param data The raw `.xlsx` bytes.
79
- * @param options The inflate bound (see {@link ReadXlsxOptions}).
92
+ * @param options The inflate bound (see {@link ReadPackageOptions}).
80
93
  * @throws {UnsupportedFormatError} if the input is not a readable `.xlsx` package (a legacy `.xls`, a
81
94
  * binary `.xlsb`, or an unrecognised/non-ZIP blob; branch on `.format`).
82
95
  * @throws {PackageReadError} if the input is a ZIP that cannot be unpacked: a corrupt or
83
96
  * truncated archive, or one exceeding the inflate bound (a probable zip bomb).
84
97
  */
85
- export declare function readWorkbookStream(data: Uint8Array, options?: ReadXlsxOptions): Generator<StreamedSheet, void, undefined>;
98
+ export declare function readWorkbookStream(data: Uint8Array, options?: ReadPackageOptions): Generator<StreamedSheet, void, undefined>;
@@ -1,46 +1,53 @@
1
- import { MAX_COLUMN, MAX_ROW } from '../../core/address.js';
2
- import { AuthoringError } from '../../errors.js';
3
- import { boolStrict, closeEmptyElements, localName, numInteger, xmlEvents, } from '../../xml/xml-read.js';
4
- import { packageAccessors } from '../opc/read-opc.js';
5
- import { inflateSpreadsheetPackage, unsupportedWorkbookPart } from '../opc/sniff-format.js';
1
+ import { Workbook } from '../../core/workbook.js';
2
+ import { AuthoringError, quoted } from '../../errors.js';
3
+ import { closeEmptyElements, parseXmlPasses } from '../../xml/xml-read.js';
4
+ import { boolStrict, localName, xmlEvents } from '../../xml/xml-scan.js';
5
+ import { openSpreadsheetPackage, readPartRelationships } from '../opc/read-opc.js';
6
+ import { unsupportedWorkbookPart } from '../opc/sniff-format.js';
6
7
  import { CellAccumulator } from './cell-accumulator.js';
8
+ import { CellStyleResolver } from './cell-style-resolution.js';
9
+ import { ColumnRecordBudget, takeColumnSpan } from './column-budget.js';
7
10
  import { XlsxParseError } from './errors.js';
11
+ import { SHARED_STRINGS_PART, STYLES_PART } from './part-names.js';
8
12
  import { parseSharedStrings } from './read-shared-strings.js';
9
- import { DEFAULT_MAX_UNCOMPRESSED, parseRelationships, parseStyleTable, parseWorkbookSheets, resolveWorkbookPart, } from './read.js';
13
+ import { workbookPropertiesPass, parseStyleTable, workbookSheetsPass, } from './read.js';
14
+ import { RowPositionTracker } from './row-position.js';
10
15
  export function* readSheetRows(data, options = {}) {
11
16
  const pkg = openPackage(data, options.maxUncompressedBytes);
12
17
  const chosen = pickSheet(pkg.sheets, options.sheet);
13
18
  const sheetXml = pkg.sheetXml(chosen.relId);
14
19
  if (sheetXml === undefined)
15
20
  return;
16
- yield* scanSheet(sheetXml, pkg.sharedStrings, pkg.xfStyles, new Set(), []);
21
+ yield* scanSheet(sheetXml, pkg.sharedStrings, pkg.xfStyles, pkg.dateEpoch, new Set(), []);
17
22
  }
18
23
  export function* readWorkbookStream(data, options = {}) {
19
24
  const pkg = openPackage(data, options.maxUncompressedBytes);
20
25
  for (const sheet of pkg.sheets) {
21
26
  const xml = pkg.sheetXml(sheet.relId) ?? '';
22
- yield new StreamedSheetReader(sheet.name, xml, pkg.sharedStrings, pkg.xfStyles);
27
+ yield new StreamedSheetReader(sheet.name, xml, pkg.sharedStrings, pkg.xfStyles, pkg.dateEpoch);
23
28
  }
24
29
  }
25
30
  function openPackage(data, maxUncompressedBytes) {
26
- const cap = maxUncompressedBytes ?? DEFAULT_MAX_UNCOMPRESSED;
27
- const { partText: text } = packageAccessors(inflateSpreadsheetPackage(data, cap));
28
- const workbookXml = text('xl/workbook.xml');
31
+ const { pkg, documentPath, workbookXml } = openSpreadsheetPackage(data, maxUncompressedBytes);
32
+ const { partText: text, partBytes } = pkg;
29
33
  if (workbookXml === undefined) {
30
- throw unsupportedWorkbookPart(text, 'the binary .xlsb format (BIFF12) cannot be row-streamed yet; read it with readXlsx or readXlsb');
34
+ throw unsupportedWorkbookPart(partBytes, documentPath, 'the binary .xlsb format (BIFF12) cannot be row-streamed yet; read it with readXlsx or readXlsb');
31
35
  }
32
- const sheets = parseWorkbookSheets(workbookXml);
33
- const rels = parseRelationships(text('xl/_rels/workbook.xml.rels') ?? '');
34
- const sharedStrings = parseSharedStrings(text('xl/sharedStrings.xml') ?? '');
35
- const { cellXfs: xfStyles } = parseStyleTable(text('xl/styles.xml') ?? '');
36
+ const properties = new Workbook();
37
+ const sheetsPass = workbookSheetsPass();
38
+ parseXmlPasses(workbookXml, [sheetsPass, workbookPropertiesPass(properties)]);
39
+ const sheets = sheetsPass.result();
40
+ const rels = readPartRelationships(documentPath, text);
41
+ const sharedStrings = parseSharedStrings(rels.relatedTextOrPath('sharedStrings', SHARED_STRINGS_PART));
42
+ const { cellXfs: xfStyles } = parseStyleTable(rels.relatedTextOrPath('styles', STYLES_PART));
36
43
  return {
37
44
  sheets,
38
45
  sharedStrings,
39
46
  xfStyles,
47
+ dateEpoch: properties.dateEpoch,
40
48
  sheetXml(relId) {
41
- const target = rels.get(relId);
42
- const path = target === undefined ? undefined : resolveWorkbookPart(target);
43
- return path === undefined ? undefined : text(path);
49
+ const target = rels.byId(relId)?.target;
50
+ return target === undefined ? undefined : text(rels.pathOf(target));
44
51
  },
45
52
  };
46
53
  }
@@ -58,7 +65,7 @@ function pickSheet(sheets, selector) {
58
65
  }
59
66
  const sheet = sheets.find((candidate) => candidate.name === selector);
60
67
  if (sheet === undefined)
61
- throw new AuthoringError(`no worksheet named ${JSON.stringify(selector)}`);
68
+ throw new AuthoringError(`no worksheet named ${quoted(selector)}`);
62
69
  return sheet;
63
70
  }
64
71
  class StreamedSheetReader {
@@ -66,20 +73,22 @@ class StreamedSheetReader {
66
73
  #xml;
67
74
  #sharedStrings;
68
75
  #xfStyles;
76
+ #dateEpoch;
69
77
  #hiddenColumns = new Set();
70
78
  #merges = [];
71
79
  #scanned = false;
72
- constructor(name, xml, sharedStrings, xfStyles) {
80
+ constructor(name, xml, sharedStrings, xfStyles, dateEpoch) {
73
81
  this.name = name;
74
82
  this.#xml = xml;
75
83
  this.#sharedStrings = sharedStrings;
76
84
  this.#xfStyles = xfStyles;
85
+ this.#dateEpoch = dateEpoch;
77
86
  }
78
87
  *rows() {
79
88
  this.#hiddenColumns = new Set();
80
89
  this.#merges = [];
81
90
  this.#scanned = false;
82
- yield* scanSheet(this.#xml, this.#sharedStrings, this.#xfStyles, this.#hiddenColumns, this.#merges);
91
+ yield* scanSheet(this.#xml, this.#sharedStrings, this.#xfStyles, this.#dateEpoch, this.#hiddenColumns, this.#merges);
83
92
  this.#scanned = true;
84
93
  }
85
94
  get hiddenColumns() {
@@ -88,7 +97,7 @@ class StreamedSheetReader {
88
97
  }
89
98
  get merges() {
90
99
  this.#ensureScanned();
91
- return this.#merges;
100
+ return [...this.#merges];
92
101
  }
93
102
  #ensureScanned() {
94
103
  if (this.#scanned)
@@ -98,17 +107,20 @@ class StreamedSheetReader {
98
107
  }
99
108
  }
100
109
  const CELL_EMPTY_CLOSE = new Set(['c']);
101
- function* scanSheet(xml, sharedStrings, xfStyles, hiddenColumns, merges) {
110
+ function* scanSheet(xml, sharedStrings, xfStyles, dateEpoch, hiddenColumns, merges) {
102
111
  let rowNumber = 0;
103
- let lastRow = 0;
104
112
  let rowHidden = false;
105
113
  let rowInGrid = true;
106
114
  let cells = [];
107
- const cell = new CellAccumulator({ richRuns: false });
115
+ const columnBudget = new ColumnRecordBudget();
116
+ const styleResolution = new CellStyleResolver();
117
+ const rowPosition = new RowPositionTracker();
118
+ const cell = new CellAccumulator({ richRuns: false, dateEpoch });
108
119
  const finalizeCell = () => {
109
120
  if (cell.ref === '' || cell.col < 0 || !rowInGrid)
110
121
  return;
111
- const style = cell.styleIndex >= 0 ? xfStyles[cell.styleIndex] : undefined;
122
+ const styleIndex = styleResolution.indexFor(cell.col, cell.styleIndex);
123
+ const style = styleIndex >= 0 ? xfStyles[styleIndex] : undefined;
112
124
  const value = cell.decode(sharedStrings, style);
113
125
  if (value !== null) {
114
126
  const { col, ref } = cell;
@@ -126,15 +138,15 @@ function* scanSheet(xml, sharedStrings, xfStyles, hiddenColumns, merges) {
126
138
  continue;
127
139
  switch (local) {
128
140
  case 'row': {
129
- rowNumber = numInteger(event.attrs.r, 1) ?? lastRow + 1;
130
- lastRow = rowNumber;
131
- rowInGrid = rowNumber <= MAX_ROW;
141
+ ({ number: rowNumber, inGrid: rowInGrid } = rowPosition.open(event.attrs));
132
142
  rowHidden = boolStrict(event.attrs.hidden);
143
+ cell.openRow(rowInGrid ? rowNumber : -1);
144
+ styleResolution.openRow(event.attrs);
133
145
  cells = [];
134
146
  break;
135
147
  }
136
148
  case 'col':
137
- collectHiddenColumn(event.attrs, hiddenColumns);
149
+ collectColumn(event.attrs, hiddenColumns, styleResolution, columnBudget);
138
150
  break;
139
151
  case 'mergeCell':
140
152
  if (event.attrs.ref !== undefined)
@@ -150,19 +162,20 @@ function* scanSheet(xml, sharedStrings, xfStyles, hiddenColumns, merges) {
150
162
  if (claimed === 'cell')
151
163
  finalizeCell();
152
164
  else if (claimed === 'other' && local === 'row') {
165
+ styleResolution.closeRow();
153
166
  if (rowInGrid)
154
167
  yield { number: rowNumber, hidden: rowHidden, cells };
155
168
  }
156
169
  }
157
170
  }
158
- function collectHiddenColumn(attrs, hiddenColumns) {
159
- if (attrs.hidden !== '1' && attrs.hidden !== 'true')
171
+ function collectColumn(attrs, hiddenColumns, styleResolution, budget) {
172
+ const span = takeColumnSpan(attrs, budget);
173
+ if (span === undefined)
160
174
  return;
161
- const min = numInteger(attrs.min, 1);
162
- const max = numInteger(attrs.max, 1);
163
- if (min === undefined || max === undefined)
175
+ const { first: min, last } = span;
176
+ styleResolution.noteColumnSpan(min, last, attrs);
177
+ if (!boolStrict(attrs.hidden))
164
178
  return;
165
- const last = Math.min(max, MAX_COLUMN);
166
179
  for (let index = min; index <= last; index++)
167
180
  hiddenColumns.add(index);
168
181
  }
@@ -1,5 +1,7 @@
1
- import { localName, parseXml } from '../../xml/xml-read.js';
2
- import { RunAccumulator } from './rich-runs.js';
1
+ import { parseXml } from '../../xml/xml-read.js';
2
+ import { localName } from '../../xml/xml-scan.js';
3
+ import { RunAccumulator } from './read-rich-runs.js';
4
+ const SHARED_STRING_EMPTY_CLOSES = new Set(['si']);
3
5
  export function parseSharedStrings(xml) {
4
6
  if (xml === '')
5
7
  return [];
@@ -17,6 +19,6 @@ export function parseSharedStrings(xml) {
17
19
  return;
18
20
  strings.push(runs.isRich ? { richText: runs.runs } : runs.plainText);
19
21
  },
20
- });
22
+ }, { closeEmptyElements: SHARED_STRING_EMPTY_CLOSES });
21
23
  return strings;
22
24
  }
@@ -1,11 +1,6 @@
1
- import { type Font, type TableStyleTable } from '../../core/style.ts';
2
- import { type XmlAttributes } from '../../xml/xml-read.ts';
1
+ import type { TableStyleTable } from '../../core/workbook-styles.ts';
3
2
  import { type StyleTable } from '../style/xf-style.ts';
4
- export type FontDraft = {
5
- -readonly [K in keyof Font]?: Font[K];
6
- };
7
3
  export declare function parseStyleTable(xml: string): StyleTable;
8
- export declare function applyFontChild(draft: FontDraft, local: string, attrs: XmlAttributes): void;
9
4
  /**
10
5
  * Extract the custom indexed-color palette (`<colors><indexedColors>`) from styles.xml as verbatim
11
6
  * `<rgbColor rgb="…"/>` fragments, or an empty list when the file rides the default palette. Kept raw