@shbernal/ts-xlsx 3.0.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 (213) 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 +4 -3
  7. package/dist/core/autofilter.js +31 -30
  8. package/dist/core/axis-handle.d.ts +2 -0
  9. package/dist/core/axis-handle.js +5 -2
  10. package/dist/core/cell.d.ts +4 -3
  11. package/dist/core/cell.js +4 -6
  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 +1 -0
  16. package/dist/core/column.js +3 -0
  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 +9 -1
  21. package/dist/core/conditional-formatting.js +34 -18
  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 +6 -0
  27. package/dist/core/data-validation.js +15 -4
  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 +13 -9
  39. package/dist/core/image.js +7 -5
  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.d.ts +5 -4
  44. package/dist/core/merge.js +18 -19
  45. package/dist/core/pivot-table.js +63 -45
  46. package/dist/core/protection.js +2 -11
  47. package/dist/core/range.js +34 -30
  48. package/dist/core/row-input.js +2 -2
  49. package/dist/core/row.d.ts +1 -0
  50. package/dist/core/row.js +3 -0
  51. package/dist/core/style.d.ts +85 -70
  52. package/dist/core/style.js +92 -28
  53. package/dist/core/table-style.d.ts +1 -1
  54. package/dist/core/table-style.js +4 -4
  55. package/dist/core/table.d.ts +27 -9
  56. package/dist/core/table.js +51 -34
  57. package/dist/core/theme.js +6 -5
  58. package/dist/core/value.d.ts +9 -0
  59. package/dist/core/value.js +36 -34
  60. package/dist/core/workbook-media.d.ts +34 -0
  61. package/dist/core/workbook-media.js +44 -0
  62. package/dist/core/workbook-styles.d.ts +67 -1
  63. package/dist/core/workbook-vba.js +3 -2
  64. package/dist/core/workbook.d.ts +23 -2
  65. package/dist/core/workbook.js +24 -40
  66. package/dist/core/worksheet-comments.d.ts +2 -1
  67. package/dist/core/worksheet-comments.js +20 -13
  68. package/dist/core/worksheet-merges.d.ts +44 -0
  69. package/dist/core/worksheet-merges.js +59 -0
  70. package/dist/core/worksheet-model.d.ts +9 -1
  71. package/dist/core/worksheet-model.js +24 -9
  72. package/dist/core/worksheet.d.ts +55 -6
  73. package/dist/core/worksheet.js +36 -44
  74. package/dist/customui/ribbon.js +2 -1
  75. package/dist/entries/core.d.ts +18 -13
  76. package/dist/entries/core.js +12 -6
  77. package/dist/entries/xlsx.d.ts +1 -1
  78. package/dist/errors.d.ts +64 -0
  79. package/dist/errors.js +22 -0
  80. package/dist/hex.d.ts +8 -0
  81. package/dist/hex.js +3 -0
  82. package/dist/io/csv/delimiter.d.ts +9 -0
  83. package/dist/io/csv/delimiter.js +6 -0
  84. package/dist/io/csv/read.js +11 -4
  85. package/dist/io/csv/write.d.ts +9 -2
  86. package/dist/io/csv/write.js +9 -38
  87. package/dist/io/opc/inflate.js +14 -0
  88. package/dist/io/opc/namespaces.d.ts +16 -0
  89. package/dist/io/opc/namespaces.js +4 -0
  90. package/dist/io/opc/part-paths.d.ts +9 -0
  91. package/dist/io/opc/part-paths.js +1 -1
  92. package/dist/io/opc/read-opc.d.ts +54 -14
  93. package/dist/io/opc/read-opc.js +42 -20
  94. package/dist/io/opc/read-options.d.ts +5 -1
  95. package/dist/io/opc/sniff-format.d.ts +3 -3
  96. package/dist/io/opc/sniff-format.js +2 -2
  97. package/dist/io/style/xf-style.d.ts +23 -1
  98. package/dist/io/style/xf-style.js +8 -0
  99. package/dist/io/xlsb/formula.d.ts +2 -2
  100. package/dist/io/xlsb/formula.js +49 -34
  101. package/dist/io/xlsb/primitives.js +8 -14
  102. package/dist/io/xlsb/ptg-functions.d.ts +19 -10
  103. package/dist/io/xlsb/ptg-functions.js +385 -596
  104. package/dist/io/xlsb/read-styles.js +45 -81
  105. package/dist/io/xlsb/read-worksheet.d.ts +20 -1
  106. package/dist/io/xlsb/read-worksheet.js +73 -89
  107. package/dist/io/xlsb/read.d.ts +6 -3
  108. package/dist/io/xlsb/read.js +44 -23
  109. package/dist/io/xlsb/record-stream.d.ts +30 -0
  110. package/dist/io/xlsb/record-stream.js +20 -0
  111. package/dist/io/xlsb/record-types.d.ts +1 -5
  112. package/dist/io/xlsb/record-types.js +1 -5
  113. package/dist/io/xlsx/cell-accumulator.d.ts +8 -1
  114. package/dist/io/xlsx/cell-accumulator.js +32 -9
  115. package/dist/io/xlsx/cell-style-resolution.d.ts +25 -0
  116. package/dist/io/xlsx/cell-style-resolution.js +29 -0
  117. package/dist/io/xlsx/cell-value.d.ts +5 -3
  118. package/dist/io/xlsx/cell-value.js +13 -15
  119. package/dist/io/xlsx/color-xml.js +3 -2
  120. package/dist/io/xlsx/column-budget.d.ts +26 -0
  121. package/dist/io/xlsx/column-budget.js +22 -0
  122. package/dist/io/xlsx/comments.d.ts +6 -2
  123. package/dist/io/xlsx/comments.js +36 -32
  124. package/dist/io/xlsx/conditional-formatting.d.ts +6 -3
  125. package/dist/io/xlsx/conditional-formatting.js +30 -26
  126. package/dist/io/xlsx/data-validation.js +29 -27
  127. package/dist/io/xlsx/edit-vba.d.ts +7 -2
  128. package/dist/io/xlsx/edit-vba.js +14 -21
  129. package/dist/io/xlsx/font-xml.d.ts +7 -0
  130. package/dist/io/xlsx/font-xml.js +104 -0
  131. package/dist/io/xlsx/hyperlinks.d.ts +37 -5
  132. package/dist/io/xlsx/hyperlinks.js +22 -13
  133. package/dist/io/xlsx/images.js +11 -13
  134. package/dist/io/xlsx/namespaces.d.ts +16 -0
  135. package/dist/io/xlsx/namespaces.js +7 -0
  136. package/dist/io/xlsx/package-plan.d.ts +14 -2
  137. package/dist/io/xlsx/package-plan.js +39 -11
  138. package/dist/io/xlsx/pivot.js +8 -7
  139. package/dist/io/xlsx/read-parts.d.ts +59 -0
  140. package/dist/io/xlsx/read-parts.js +252 -0
  141. package/dist/io/xlsx/read-pivot.js +2 -1
  142. package/dist/io/xlsx/read-repair.d.ts +26 -0
  143. package/dist/io/xlsx/read-repair.js +39 -0
  144. package/dist/io/xlsx/{rich-runs.js → read-rich-runs.js} +6 -3
  145. package/dist/io/xlsx/read-rows.d.ts +18 -5
  146. package/dist/io/xlsx/read-rows.js +45 -31
  147. package/dist/io/xlsx/read-shared-strings.js +3 -2
  148. package/dist/io/xlsx/read-styles.d.ts +1 -6
  149. package/dist/io/xlsx/read-styles.js +6 -71
  150. package/dist/io/xlsx/read-workbook-xml.d.ts +36 -0
  151. package/dist/io/xlsx/read-workbook-xml.js +170 -0
  152. package/dist/io/xlsx/read-worksheet.d.ts +2 -1
  153. package/dist/io/xlsx/read-worksheet.js +35 -164
  154. package/dist/io/xlsx/read.d.ts +5 -12
  155. package/dist/io/xlsx/read.js +73 -406
  156. package/dist/io/xlsx/rich-text.js +1 -1
  157. package/dist/io/xlsx/row-position.d.ts +18 -0
  158. package/dist/io/xlsx/row-position.js +11 -0
  159. package/dist/io/xlsx/row-xml.d.ts +128 -0
  160. package/dist/io/xlsx/row-xml.js +203 -0
  161. package/dist/io/xlsx/shared-formulas.js +2 -2
  162. package/dist/io/xlsx/sheet-properties.d.ts +10 -1
  163. package/dist/io/xlsx/sheet-properties.js +145 -9
  164. package/dist/io/xlsx/style-elements.d.ts +43 -0
  165. package/dist/io/xlsx/style-elements.js +156 -0
  166. package/dist/io/xlsx/styles.d.ts +12 -3
  167. package/dist/io/xlsx/styles.js +5 -207
  168. package/dist/io/xlsx/tables.js +10 -14
  169. package/dist/io/xlsx/theme-xml.d.ts +26 -4
  170. package/dist/io/xlsx/theme-xml.js +107 -69
  171. package/dist/io/xlsx/threaded-comments.d.ts +8 -10
  172. package/dist/io/xlsx/threaded-comments.js +2 -1
  173. package/dist/io/xlsx/workbook-xml.d.ts +54 -4
  174. package/dist/io/xlsx/workbook-xml.js +60 -34
  175. package/dist/io/xlsx/worksheet-xml.d.ts +23 -70
  176. package/dist/io/xlsx/worksheet-xml.js +48 -229
  177. package/dist/io/xlsx/write-stream.d.ts +65 -16
  178. package/dist/io/xlsx/write-stream.js +50 -20
  179. package/dist/io/xlsx/write.d.ts +1 -2
  180. package/dist/io/xlsx/write.js +114 -74
  181. package/dist/rel-type.d.ts +16 -0
  182. package/dist/rel-type.js +6 -0
  183. package/dist/token-set.d.ts +13 -0
  184. package/dist/token-set.js +4 -1
  185. package/dist/vba/bytes.d.ts +14 -5
  186. package/dist/vba/bytes.js +16 -6
  187. package/dist/vba/cfb-format.d.ts +8 -0
  188. package/dist/vba/cfb-format.js +1 -0
  189. package/dist/vba/cfb-writer.d.ts +9 -0
  190. package/dist/vba/cfb-writer.js +12 -9
  191. package/dist/vba/cfb.d.ts +13 -1
  192. package/dist/vba/cfb.js +73 -51
  193. package/dist/vba/codepage.js +2 -1
  194. package/dist/vba/errors.d.ts +7 -0
  195. package/dist/vba/ms-ovba.js +51 -16
  196. package/dist/vba/project-editor.js +35 -40
  197. package/dist/vba/project.d.ts +11 -1
  198. package/dist/vba/project.js +27 -8
  199. package/dist/vba/vba-encoding.js +4 -5
  200. package/dist/xml/xml-attrs.d.ts +37 -0
  201. package/dist/xml/xml-attrs.js +30 -0
  202. package/dist/xml/xml-chars.d.ts +40 -0
  203. package/dist/xml/xml-chars.js +12 -0
  204. package/dist/xml/xml-namespaces.d.ts +31 -0
  205. package/dist/xml/xml-namespaces.js +50 -0
  206. package/dist/xml/xml-read.d.ts +72 -10
  207. package/dist/xml/xml-read.js +94 -18
  208. package/dist/xml/xml-scan.d.ts +15 -37
  209. package/dist/xml/xml-scan.js +12 -40
  210. package/dist/xml/xml.d.ts +28 -27
  211. package/dist/xml/xml.js +24 -24
  212. package/package.json +12 -7
  213. /package/dist/io/xlsx/{rich-runs.d.ts → read-rich-runs.d.ts} +0 -0
@@ -1,9 +1,9 @@
1
- import { AuthoringError } from '../../errors.js';
1
+ import { AuthoringError, InternalError, quoted } from '../../errors.js';
2
2
  import { extensionOf, relativePartPath, relsPathFor, THEME_PART_PATH } from '../opc/part-paths.js';
3
3
  import { relsPartXml } from '../opc/rels.js';
4
- import { drawingPart, mediaPart, vmlDrawingPart } from './part-names.js';
4
+ import { drawingPart, mediaPart, pivotCacheDefinitionPart, pivotCacheRecordsPart, pivotTablePart, vmlDrawingPart, } from './part-names.js';
5
5
  import { applyThemeOverrides } from './theme-xml.js';
6
- export class SheetRelIds {
6
+ export class RelIdAllocator {
7
7
  #next = 1;
8
8
  next() {
9
9
  return `rId${this.#next++}`;
@@ -32,7 +32,7 @@ export function planMedia(workbook, sheets) {
32
32
  const image = workbook.getImage(id);
33
33
  if (image === undefined) {
34
34
  const reference = role === 'anchor' ? `anchors image id ${id}` : `sets background image id ${id}`;
35
- throw new AuthoringError(`sheet "${sheetName}" ${reference}, which is not registered on the workbook`);
35
+ throw new AuthoringError(`sheet ${quoted(sheetName)} ${reference}, which is not registered on the workbook`);
36
36
  }
37
37
  const number = i + 1;
38
38
  parts.push({ number, extension: image.extension, data: image.data });
@@ -51,12 +51,15 @@ export function planMedia(workbook, sheets) {
51
51
  },
52
52
  };
53
53
  }
54
- export function planPreservedParts(workbook, generatedDrawingCount, generatedMediaCount) {
54
+ export function planPreservedParts(workbook, generatedDrawingCount, generatedMediaCount, generatedPivotCount) {
55
55
  const sheets = workbook.worksheets;
56
56
  const numbering = {
57
57
  drawing: generatedDrawingCount,
58
58
  vml: sheets.length,
59
59
  media: generatedMediaCount,
60
+ pivotTable: generatedPivotCount,
61
+ pivotCacheDefinition: generatedPivotCount,
62
+ pivotCacheRecords: generatedPivotCount,
60
63
  };
61
64
  const remap = new Map();
62
65
  const theme = workbook.themePart;
@@ -77,7 +80,7 @@ export function planPreservedParts(workbook, generatedDrawingCount, generatedMed
77
80
  const emitted = new Map();
78
81
  for (const reference of allReferences) {
79
82
  for (const part of reference.parts) {
80
- const newPath = remap.get(part.path);
83
+ const newPath = resolveRemapped(remap, part.path);
81
84
  if (emitted.has(newPath))
82
85
  continue;
83
86
  const rels = part.rels.flatMap((rel) => {
@@ -104,17 +107,17 @@ export function planPreservedParts(workbook, generatedDrawingCount, generatedMed
104
107
  const perSheet = sheets.map((sheet) => sheet.preservedReferences.map((reference) => ({
105
108
  element: reference.element,
106
109
  relType: reference.relType,
107
- entryPath: remap.get(reference.entryPath),
110
+ entryPath: resolveRemapped(remap, reference.entryPath),
108
111
  })));
109
112
  const workbookRefs = workbook.preservedReferences.map((reference) => ({
110
113
  relType: reference.relType,
111
- entryPath: remap.get(reference.entryPath),
114
+ entryPath: resolveRemapped(remap, reference.entryPath),
112
115
  pivotCacheId: reference.pivotCacheId,
113
116
  externalReferenceIndex: reference.externalReferenceIndex,
114
117
  }));
115
118
  const rootRefs = workbook.preservedRootReferences.map((reference) => ({
116
119
  relType: reference.relType,
117
- entryPath: remap.get(reference.entryPath),
120
+ entryPath: resolveRemapped(remap, reference.entryPath),
118
121
  }));
119
122
  return {
120
123
  perSheet,
@@ -124,14 +127,39 @@ export function planPreservedParts(workbook, generatedDrawingCount, generatedMed
124
127
  themeEmitted: theme !== undefined,
125
128
  };
126
129
  }
130
+ function resolveRemapped(remap, path) {
131
+ const resolved = remap.get(path);
132
+ if (resolved === undefined) {
133
+ throw new InternalError(`preserved part ${quoted(path)} was never assigned a new path`);
134
+ }
135
+ return resolved;
136
+ }
127
137
  function preservedPartPath(originalPath, numbering) {
128
138
  const ext = extensionOf(originalPath);
129
- if (ext.toLowerCase() === 'vml')
139
+ if (ext === 'vml')
130
140
  return vmlDrawingPart(++numbering.vml);
131
141
  if (originalPath.startsWith('xl/media/'))
132
142
  return mediaPart(++numbering.media, ext);
133
- if (originalPath.startsWith('xl/drawings/') && ext.toLowerCase() === 'xml') {
143
+ if (originalPath.startsWith('xl/drawings/') && ext === 'xml') {
134
144
  return drawingPart(++numbering.drawing);
135
145
  }
146
+ if (isNumberedPart(originalPath, PIVOT_TABLE_PREFIX)) {
147
+ return pivotTablePart(++numbering.pivotTable);
148
+ }
149
+ if (isNumberedPart(originalPath, PIVOT_CACHE_DEFINITION_PREFIX)) {
150
+ return pivotCacheDefinitionPart(++numbering.pivotCacheDefinition);
151
+ }
152
+ if (isNumberedPart(originalPath, PIVOT_CACHE_RECORDS_PREFIX)) {
153
+ return pivotCacheRecordsPart(++numbering.pivotCacheRecords);
154
+ }
136
155
  return originalPath;
137
156
  }
157
+ const PIVOT_TABLE_PREFIX = numberedPartPrefix(pivotTablePart);
158
+ const PIVOT_CACHE_DEFINITION_PREFIX = numberedPartPrefix(pivotCacheDefinitionPart);
159
+ const PIVOT_CACHE_RECORDS_PREFIX = numberedPartPrefix(pivotCacheRecordsPart);
160
+ function numberedPartPrefix(part) {
161
+ return part(1).slice(0, -'1.xml'.length);
162
+ }
163
+ function isNumberedPart(path, prefix) {
164
+ return path.startsWith(prefix) && /^\d+\.xml$/.test(path.slice(prefix.length));
165
+ }
@@ -1,5 +1,5 @@
1
1
  import { encodeAddress } from '../../core/address.js';
2
- import { escapeAttr, XML_DECLARATION } from '../../xml/xml.js';
2
+ import { boolAttr, escapeAttr, numberText, XML_DECLARATION } from '../../xml/xml.js';
3
3
  import { RELATIONSHIPS_NS } from '../opc/namespaces.js';
4
4
  import { SPREADSHEETML_NS } from './namespaces.js';
5
5
  const METRIC_CAPTIONS = {
@@ -30,9 +30,10 @@ export function pivotCacheDefinitionXml(table) {
30
30
  const numeric = field.numeric;
31
31
  const descriptor = numeric === null
32
32
  ? `<sharedItems${blank}/>`
33
- : `<sharedItems containsSemiMixedTypes="0" containsString="0" containsNumber="1" ` +
34
- `containsInteger="${numeric.allInteger ? 1 : 0}"${blank} ` +
35
- `minValue="${numeric.min}" maxValue="${numeric.max}"/>`;
33
+ : `<sharedItems containsSemiMixedTypes="0" containsString="0" containsNumber="1"` +
34
+ boolAttr('containsInteger', numeric.allInteger) +
35
+ blank +
36
+ ` minValue="${numberText(numeric.min)}" maxValue="${numberText(numeric.max)}"/>`;
36
37
  return `<cacheField name="${escapeAttr(field.name)}" numFmtId="0">${descriptor}</cacheField>`;
37
38
  })
38
39
  .join('');
@@ -56,8 +57,8 @@ export function pivotCacheRecordsXml(table) {
56
57
  `</pivotCacheRecords>`);
57
58
  }
58
59
  export function pivotTableXml(table, name, cacheId) {
59
- const rowField = table.rowFields[0];
60
- const columnField = table.columnFields[0];
60
+ const rowField = table.rowFields[0] ?? 0;
61
+ const columnField = table.columnFields[0] ?? 0;
61
62
  const rowGroups = table.cacheFields[rowField]?.sharedItems?.length ?? 1;
62
63
  const columnGroups = table.cacheFields[columnField]?.sharedItems?.length ?? 1;
63
64
  const location = `A1:${encodeAddress(2 + columnGroups, 3 + rowGroups)}`;
@@ -107,7 +108,7 @@ function sharedItemXml(item) {
107
108
  case 'string':
108
109
  return `<s v="${escapeAttr(item.value)}"/>`;
109
110
  case 'number':
110
- return `<n v="${item.value}"/>`;
111
+ return `<n v="${numberText(item.value)}"/>`;
111
112
  case 'blank':
112
113
  return '<m/>';
113
114
  }
@@ -0,0 +1,59 @@
1
+ import type { CommentThread } from '../../core/comment-thread.ts';
2
+ import { Workbook } from '../../core/workbook.ts';
3
+ import type { Worksheet } from '../../core/worksheet.ts';
4
+ import type { CollectingPass } from '../../xml/xml-read.ts';
5
+ import { type PackageAccessors, type PartRelationships } from '../opc/read-opc.ts';
6
+ import { type ParsedComment } from './comments.ts';
7
+ /**
8
+ * What every part reader in one package read shares: the inflated package, the model being built, the
9
+ * content-type resolver a preserved part's closure is captured through, and the interning map that
10
+ * makes one workbook image per media part however many places point at it.
11
+ *
12
+ * None of the four varies within a read, and threading them positionally is what let `internImage`
13
+ * end up taking the same three in a different order from both of its callers. What genuinely varies
14
+ * -- a part's relationships, the sheet being filled -- stays a positional argument, and the sheet's
15
+ * rels in particular are deliberately *not* in here: they are per sheet, and a stale copy of them
16
+ * would send every sheet's parts to the first sheet's.
17
+ */
18
+ export interface PackageReadContext {
19
+ readonly pkg: PackageAccessors;
20
+ readonly workbook: Workbook;
21
+ readonly contentTypeOf: (path: string) => string;
22
+ readonly imageIdByMediaPath: Map<string, number>;
23
+ }
24
+ export declare function readSheetComments(sheetRels: PartRelationships): Map<string, ParsedComment> | undefined;
25
+ export declare function readWorkbookPersons({ workbook }: PackageReadContext, workbookRels: PartRelationships): void;
26
+ export declare function readWorkbookTheme(context: PackageReadContext, workbookRels: PartRelationships): void;
27
+ export declare function readSheetCommentThreads({ workbook }: PackageReadContext, sheetRels: PartRelationships): CommentThread[];
28
+ export declare function readSheetPrinterSettings(sheetRels: PartRelationships): Uint8Array | undefined;
29
+ export declare function readSheetImages(context: PackageReadContext, sheetRels: PartRelationships, sheet: Worksheet): void;
30
+ export declare function readSheetBackground(context: PackageReadContext, sheetRels: PartRelationships, sheet: Worksheet): void;
31
+ export declare function readSheetPreservedReferences(context: PackageReadContext, sheetRels: PartRelationships, referenceRelIds: WorksheetReferenceRelIds, sheet: Worksheet): void;
32
+ export declare function readWorkbookPreservedReferences(context: PackageReadContext, workbookRels: PartRelationships, registrations: WorkbookRegistrations): void;
33
+ export declare function readRootPreservedReferences(context: PackageReadContext): void;
34
+ /** The two `<pivotCaches>`/`<externalReferences>` registries a preserved reference is wired by,
35
+ * gathered from the workbook part's own scan rather than from two more of it. */
36
+ export interface WorkbookRegistrations {
37
+ readonly cacheIdByRelId: ReadonlyMap<string, string>;
38
+ readonly externalIndexByRelId: ReadonlyMap<string, number>;
39
+ }
40
+ export declare function pivotCacheRegistrationsPass(): CollectingPass<ReadonlyMap<string, string>>;
41
+ export declare function externalReferenceRegistrationsPass(): CollectingPass<ReadonlyMap<string, number>>;
42
+ /** The `r:id` each of the two element-wired references carries, or `undefined` where absent. */
43
+ export type WorksheetReferenceRelIds = Readonly<Partial<Record<'drawing' | 'legacyDrawingHF', string>>>;
44
+ /**
45
+ * A pass gathering the `r:id` of the first `<drawing>` and `<legacyDrawingHF>` in a worksheet.
46
+ *
47
+ * These two references live in the worksheet body rather than being distinguishable by relationship
48
+ * Type, since a header/footer VML and a comment VML share the `vmlDrawing` type, so the specific
49
+ * relationship is found by reading the element's own `r:id`.
50
+ *
51
+ * A pass rather than its own scan, because the sheet part is the largest in a package by a wide
52
+ * margin and this ran a full extra scan of it, once or twice per sheet, for two attributes.
53
+ * `readSheet` already builds a multi-pass single parse of it whose own comment records that five
54
+ * separate scans "spent 45% of a large file's read on four scans that matched no element"; this was
55
+ * the sixth scan, added later, that the same argument covers.
56
+ */
57
+ export declare function worksheetReferencePass(): CollectingPass<WorksheetReferenceRelIds>;
58
+ export declare function readSheetTables({ pkg }: PackageReadContext, sheetRels: PartRelationships, sheet: Worksheet): void;
59
+ export declare function readSheetPivotTables({ pkg }: PackageReadContext, sheetRels: PartRelationships, sheet: Worksheet): void;
@@ -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,6 +1,7 @@
1
1
  import { isDeclarablePivotSourceKind, pivotMetricFromSubtotal, } from '../../core/pivot-table.js';
2
+ import { numInteger } from '../../xml/xml-attrs.js';
2
3
  import { parseXml } from '../../xml/xml-read.js';
3
- import { localName, numInteger } from '../../xml/xml-scan.js';
4
+ import { localName } from '../../xml/xml-scan.js';
4
5
  export function parsePivotTable(tableXml, cacheXml) {
5
6
  const { fields, source } = parsePivotCacheDefinition(cacheXml);
6
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,6 +1,7 @@
1
+ import { decodeSpreadsheetText } from '../../xml/xml-attrs.js';
1
2
  import { TextCapture } from '../../xml/xml-read.js';
2
- import { decodeSpreadsheetText } from '../../xml/xml-scan.js';
3
- import { applyFontChild } from './read-styles.js';
3
+ import {} from '../../xml/xml-scan.js';
4
+ import { applyFontChild } from './font-xml.js';
4
5
  export class RunAccumulator {
5
6
  #container;
6
7
  #readRuns;
@@ -34,12 +35,14 @@ export class RunAccumulator {
34
35
  this.#plain = '';
35
36
  this.#inRun = false;
36
37
  this.#isRich = false;
38
+ this.#inContainer = false;
39
+ this.#capture.reset();
37
40
  }
38
41
  open(local, attrs, selfClosing) {
39
42
  switch (local) {
40
43
  case this.#container:
41
- this.#inContainer = true;
42
44
  this.beginContainer();
45
+ this.#inContainer = !selfClosing;
43
46
  return true;
44
47
  case 'r':
45
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>;