@shbernal/ts-xlsx 1.3.1 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. package/README.md +20 -10
  2. package/dist/bytes.d.ts +24 -0
  3. package/dist/bytes.js +34 -0
  4. package/dist/core/address.d.ts +72 -3
  5. package/dist/core/address.js +50 -10
  6. package/dist/core/autofilter.d.ts +13 -2
  7. package/dist/core/autofilter.js +35 -10
  8. package/dist/core/cell.d.ts +17 -16
  9. package/dist/core/cell.js +3 -7
  10. package/dist/core/color-resolution.d.ts +8 -8
  11. package/dist/core/color-resolution.js +7 -8
  12. package/dist/core/column.d.ts +8 -9
  13. package/dist/core/column.js +2 -4
  14. package/dist/core/comment-thread.d.ts +32 -23
  15. package/dist/core/comment-thread.js +4 -4
  16. package/dist/core/conditional-formatting-overlay.d.ts +6 -1
  17. package/dist/core/conditional-formatting-overlay.js +11 -0
  18. package/dist/core/conditional-formatting.d.ts +48 -8
  19. package/dist/core/conditional-formatting.js +88 -0
  20. package/dist/core/data-validation-overlay.d.ts +12 -1
  21. package/dist/core/data-validation-overlay.js +21 -2
  22. package/dist/core/data-validation.d.ts +12 -5
  23. package/dist/core/data-validation.js +34 -0
  24. package/dist/core/date.d.ts +3 -3
  25. package/dist/core/formula.d.ts +6 -6
  26. package/dist/core/grid-edits.d.ts +16 -0
  27. package/dist/core/grid-edits.js +29 -11
  28. package/dist/core/grid-shift.d.ts +19 -0
  29. package/dist/core/grid-shift.js +8 -0
  30. package/dist/core/image.d.ts +39 -2
  31. package/dist/core/image.js +10 -0
  32. package/dist/core/internal.d.ts +9 -1
  33. package/dist/core/limits.d.ts +4 -4
  34. package/dist/core/merge.d.ts +17 -12
  35. package/dist/core/merge.js +36 -5
  36. package/dist/core/page-setup.d.ts +20 -6
  37. package/dist/core/page-setup.js +8 -1
  38. package/dist/core/pivot-table.d.ts +4 -4
  39. package/dist/core/pivot-table.js +29 -20
  40. package/dist/core/preserved.d.ts +3 -3
  41. package/dist/core/protection.d.ts +7 -4
  42. package/dist/core/protection.js +20 -14
  43. package/dist/core/range.d.ts +16 -14
  44. package/dist/core/range.js +5 -5
  45. package/dist/core/row-input.d.ts +9 -8
  46. package/dist/core/row-input.js +12 -10
  47. package/dist/core/row.d.ts +7 -8
  48. package/dist/core/row.js +2 -4
  49. package/dist/core/style.d.ts +41 -14
  50. package/dist/core/style.js +82 -67
  51. package/dist/core/table-style.d.ts +9 -9
  52. package/dist/core/table-style.js +1 -1
  53. package/dist/core/table.d.ts +53 -51
  54. package/dist/core/table.js +83 -41
  55. package/dist/core/theme.d.ts +16 -44
  56. package/dist/core/theme.js +6 -148
  57. package/dist/core/value.d.ts +7 -7
  58. package/dist/core/workbook-protection.d.ts +2 -2
  59. package/dist/core/workbook-styles.d.ts +26 -0
  60. package/dist/core/workbook-styles.js +48 -0
  61. package/dist/core/workbook-theme.d.ts +25 -0
  62. package/dist/core/workbook-theme.js +49 -0
  63. package/dist/core/workbook-vba.d.ts +17 -0
  64. package/dist/core/workbook-vba.js +79 -0
  65. package/dist/core/workbook.d.ts +133 -87
  66. package/dist/core/workbook.js +74 -133
  67. package/dist/core/worksheet-comments.d.ts +17 -0
  68. package/dist/core/worksheet-comments.js +70 -0
  69. package/dist/core/worksheet-model.d.ts +4 -4
  70. package/dist/core/worksheet-model.js +1 -0
  71. package/dist/core/worksheet-pictures.d.ts +29 -0
  72. package/dist/core/worksheet-pictures.js +42 -0
  73. package/dist/core/worksheet.d.ts +100 -79
  74. package/dist/core/worksheet.js +63 -142
  75. package/dist/customui/errors.d.ts +1 -1
  76. package/dist/customui/ribbon.d.ts +3 -3
  77. package/dist/customui/ribbon.js +24 -23
  78. package/dist/entries/core.d.ts +7 -7
  79. package/dist/entries/core.js +1 -1
  80. package/dist/entries/csv.d.ts +1 -1
  81. package/dist/entries/node-unavailable.d.ts +15 -0
  82. package/dist/entries/node-unavailable.js +20 -0
  83. package/dist/entries/node.d.ts +1 -0
  84. package/dist/entries/node.js +1 -0
  85. package/dist/entries/vba.d.ts +1 -1
  86. package/dist/entries/vba.js +1 -1
  87. package/dist/entries/xlsx.d.ts +1 -1
  88. package/dist/entries/xlsx.js +1 -1
  89. package/dist/errors.d.ts +12 -12
  90. package/dist/io/csv/read.js +2 -3
  91. package/dist/io/csv/write.d.ts +19 -4
  92. package/dist/io/csv/write.js +34 -4
  93. package/dist/io/opc/errors.d.ts +6 -6
  94. package/dist/io/opc/inflate.d.ts +1 -1
  95. package/dist/io/opc/inflate.js +2 -12
  96. package/dist/io/opc/namespaces.d.ts +1 -1
  97. package/dist/io/opc/read-opc.d.ts +19 -3
  98. package/dist/io/opc/read-opc.js +14 -8
  99. package/dist/io/opc/read-options.d.ts +2 -2
  100. package/dist/io/opc/rels.d.ts +11 -0
  101. package/dist/io/opc/rels.js +3 -2
  102. package/dist/io/opc/sniff-format.d.ts +2 -2
  103. package/dist/io/style/xf-style.d.ts +44 -7
  104. package/dist/io/style/xf-style.js +27 -0
  105. package/dist/io/xlsb/errors.d.ts +1 -1
  106. package/dist/io/xlsb/formula.d.ts +4 -4
  107. package/dist/io/xlsb/primitives.d.ts +8 -8
  108. package/dist/io/xlsb/ptg-functions.d.ts +1 -1
  109. package/dist/io/xlsb/read-styles.js +2 -20
  110. package/dist/io/xlsb/read.d.ts +2 -2
  111. package/dist/io/xlsx/cell-accumulator.d.ts +26 -9
  112. package/dist/io/xlsx/cell-accumulator.js +89 -46
  113. package/dist/io/xlsx/cell-value.d.ts +3 -3
  114. package/dist/io/xlsx/cell-value.js +9 -6
  115. package/dist/io/xlsx/color-xml.d.ts +1 -1
  116. package/dist/io/xlsx/color-xml.js +18 -23
  117. package/dist/io/xlsx/comments.d.ts +5 -5
  118. package/dist/io/xlsx/comments.js +17 -8
  119. package/dist/io/xlsx/conditional-formatting.d.ts +4 -4
  120. package/dist/io/xlsx/conditional-formatting.js +64 -58
  121. package/dist/io/xlsx/data-validation.d.ts +3 -3
  122. package/dist/io/xlsx/data-validation.js +35 -33
  123. package/dist/io/xlsx/errors.d.ts +3 -3
  124. package/dist/io/xlsx/hyperlinks.d.ts +4 -2
  125. package/dist/io/xlsx/hyperlinks.js +10 -18
  126. package/dist/io/xlsx/images.d.ts +1 -1
  127. package/dist/io/xlsx/images.js +25 -28
  128. package/dist/io/xlsx/namespaces.d.ts +2 -2
  129. package/dist/io/xlsx/package-plan.js +5 -2
  130. package/dist/io/xlsx/read-pivot.js +4 -13
  131. package/dist/io/xlsx/read-rows.d.ts +13 -13
  132. package/dist/io/xlsx/read-rows.js +19 -55
  133. package/dist/io/xlsx/read-shared-strings.js +7 -44
  134. package/dist/io/xlsx/read-styles.d.ts +3 -3
  135. package/dist/io/xlsx/read-styles.js +42 -66
  136. package/dist/io/xlsx/read-worksheet.js +73 -129
  137. package/dist/io/xlsx/read.d.ts +4 -6
  138. package/dist/io/xlsx/read.js +104 -142
  139. package/dist/io/xlsx/rich-runs.d.ts +41 -7
  140. package/dist/io/xlsx/rich-runs.js +94 -30
  141. package/dist/io/xlsx/rich-text.d.ts +1 -1
  142. package/dist/io/xlsx/shared-strings.d.ts +2 -2
  143. package/dist/io/xlsx/sheet-properties.d.ts +1 -1
  144. package/dist/io/xlsx/sheet-properties.js +14 -10
  145. package/dist/io/xlsx/styles.d.ts +9 -22
  146. package/dist/io/xlsx/styles.js +65 -89
  147. package/dist/io/xlsx/tables.d.ts +2 -2
  148. package/dist/io/xlsx/tables.js +20 -25
  149. package/dist/io/xlsx/theme-xml.d.ts +37 -0
  150. package/dist/io/xlsx/theme-xml.js +145 -0
  151. package/dist/io/xlsx/threaded-comments.d.ts +18 -27
  152. package/dist/io/xlsx/threaded-comments.js +26 -38
  153. package/dist/io/xlsx/workbook-xml.js +14 -8
  154. package/dist/io/xlsx/worksheet-xml.d.ts +2 -2
  155. package/dist/io/xlsx/worksheet-xml.js +40 -48
  156. package/dist/io/xlsx/write-stream.d.ts +23 -16
  157. package/dist/io/xlsx/write-stream.js +13 -14
  158. package/dist/io/xlsx/write.d.ts +8 -7
  159. package/dist/io/xlsx/write.js +6 -3
  160. package/dist/io/xlsx/x14-ext.d.ts +1 -1
  161. package/dist/sha512.d.ts +2 -0
  162. package/dist/sha512.js +146 -0
  163. package/dist/vba/bytes.d.ts +11 -0
  164. package/dist/vba/bytes.js +29 -0
  165. package/dist/vba/cfb-writer.d.ts +1 -1
  166. package/dist/vba/cfb.d.ts +1 -1
  167. package/dist/vba/cfb.js +15 -51
  168. package/dist/vba/codepage.d.ts +2 -2
  169. package/dist/vba/dir-records.d.ts +53 -0
  170. package/dist/vba/dir-records.js +28 -0
  171. package/dist/vba/errors.d.ts +2 -2
  172. package/dist/vba/index.d.ts +2 -2
  173. package/dist/vba/index.js +2 -2
  174. package/dist/vba/ms-ovba.d.ts +1 -1
  175. package/dist/vba/ms-ovba.js +1 -3
  176. package/dist/vba/project-editor.d.ts +9 -9
  177. package/dist/vba/project-editor.js +6 -44
  178. package/dist/vba/project.d.ts +5 -5
  179. package/dist/vba/project.js +3 -28
  180. package/dist/vba/vba-encoding.d.ts +1 -1
  181. package/dist/xml/errors.d.ts +1 -1
  182. package/dist/xml/xml-read.d.ts +98 -9
  183. package/dist/xml/xml-read.js +70 -0
  184. package/dist/xml/xml.d.ts +74 -10
  185. package/dist/xml/xml.js +39 -7
  186. package/package.json +59 -36
@@ -1,12 +1,13 @@
1
1
  import { decodeRange } from '../../core/address.js';
2
2
  import { unmangleFunctions } from '../../core/formula.js';
3
3
  import { INTERNAL } from '../../core/internal.js';
4
- import { Workbook } from '../../core/workbook.js';
5
4
  import { WORKBOOK_PROTECTION_CREDENTIAL_ATTRS, } from '../../core/workbook-protection.js';
6
- import { boolStrict, localName, openElements, parseXml } from '../../xml/xml-read.js';
5
+ import { Workbook } from '../../core/workbook.js';
6
+ import { isVisibility } from '../../core/worksheet.js';
7
+ import { boolStrict, capturedText, enumToken, localName, numInteger, openElements, parseXml, } from '../../xml/xml-read.js';
7
8
  import { UnsupportedFormatError } from '../opc/errors.js';
8
- import { extensionOf, relsPathFor } from '../opc/part-paths.js';
9
- import { capturePartClosure, contentTypeResolver, packageAccessors, parseRelationshipRecords, parseRelationships, relationshipTargetByType, relationshipTargetsByType, resolveRelativePart, resolveWorkbookPart, sheetRelTarget, } from '../opc/read-opc.js';
9
+ import { extensionOf } from '../opc/part-paths.js';
10
+ import { capturePartClosure, contentTypeResolver, packageAccessors, parseRelationshipRecords, parseRelationships, readPartRelationships, relationshipTargetByType, resolveRelativePart, resolveWorkbookPart, } from '../opc/read-opc.js';
10
11
  import { DEFAULT_MAX_UNCOMPRESSED } from '../opc/read-options.js';
11
12
  import { inflateSpreadsheetPackage } from '../opc/sniff-format.js';
12
13
  import { readXlsbPackage, XLSB_WORKBOOK_PART } from '../xlsb/read.js';
@@ -20,6 +21,7 @@ import { parseSharedStrings } from './read-shared-strings.js';
20
21
  import { parseIndexedColors, parseMruColors, parseStyleTable, parseTableStyles, } from './read-styles.js';
21
22
  import { parseWorksheet } from './read-worksheet.js';
22
23
  import { parseTable } from './tables.js';
24
+ import { parseThemeColorScheme, parseThemeFontScheme } from './theme-xml.js';
23
25
  import { buildCommentThreads, parsePersons, parseThreadedComments } from './threaded-comments.js';
24
26
  export { parseRelationships, resolveWorkbookPart } from '../opc/read-opc.js';
25
27
  export { DEFAULT_MAX_UNCOMPRESSED } from '../opc/read-options.js';
@@ -70,9 +72,9 @@ export function readXlsx(data, options = {}) {
70
72
  if (sheetXml !== undefined)
71
73
  parseWorksheet(sheetXml, sheet, sharedStrings, xfStyles);
72
74
  if (path !== undefined) {
75
+ const sheetRels = readPartRelationships(path, partText);
73
76
  if (sheetXml !== undefined) {
74
- const sheetRels = parseRelationships(partText(relsPathFor(path)) ?? '');
75
- applyHyperlinks(sheet, parseSheetHyperlinks(sheetXml), sheetRels);
77
+ applyHyperlinks(sheet, parseSheetHyperlinks(sheetXml), (id) => sheetRels.byId(id)?.target);
76
78
  applyDataValidations(sheet, [
77
79
  ...parseDataValidations(sheetXml),
78
80
  ...parseExtendedDataValidations(sheetXml),
@@ -80,20 +82,20 @@ export function readXlsx(data, options = {}) {
80
82
  for (const cf of parseConditionalFormattings(sheetXml))
81
83
  sheet.addConditionalFormatting(cf);
82
84
  }
83
- const threads = readSheetCommentThreads(path, pkg, workbook);
85
+ const threads = readSheetCommentThreads(sheetRels, pkg, workbook);
84
86
  if (threads.length > 0)
85
87
  sheet[INTERNAL].restoreCommentThreads(threads);
86
- const comments = readSheetComments(path, pkg);
88
+ const comments = readSheetComments(sheetRels, pkg);
87
89
  if (comments !== undefined)
88
90
  applyNotes(sheet, comments);
89
- readSheetImages(path, pkg, workbook, sheet, imageIdByMediaPath);
90
- readSheetBackground(path, pkg, workbook, sheet, imageIdByMediaPath);
91
+ readSheetImages(sheetRels, pkg, workbook, sheet, imageIdByMediaPath);
92
+ readSheetBackground(sheetRels, pkg, workbook, sheet, imageIdByMediaPath);
91
93
  if (sheetXml !== undefined) {
92
- readSheetPreservedReferences(path, sheetXml, pkg, contentTypeOf, sheet);
94
+ readSheetPreservedReferences(sheetRels, sheetXml, pkg, contentTypeOf, sheet);
93
95
  }
94
- readSheetTables(path, pkg, sheet);
95
- readSheetPivotTables(path, pkg, sheet);
96
- const printerSettings = readSheetPrinterSettings(path, pkg);
96
+ readSheetTables(sheetRels, pkg, sheet);
97
+ readSheetPivotTables(sheetRels, pkg, sheet);
98
+ const printerSettings = readSheetPrinterSettings(sheetRels, pkg);
97
99
  if (printerSettings !== undefined)
98
100
  sheet.pageSetup.printerSettings = printerSettings;
99
101
  }
@@ -105,8 +107,8 @@ export function readXlsx(data, options = {}) {
105
107
  }
106
108
  return workbook;
107
109
  }
108
- function readSheetComments(sheetPath, pkg) {
109
- const commentsPath = sheetRelTarget(sheetPath, pkg.partText, 'comments');
110
+ function readSheetComments(sheetRels, pkg) {
111
+ const commentsPath = sheetRels.targetPath('comments');
110
112
  if (commentsPath === undefined)
111
113
  return undefined;
112
114
  const commentsXml = pkg.partText(commentsPath);
@@ -126,23 +128,36 @@ function readWorkbookTheme(workbookRelsXml, pkg, contentTypeOf, workbook) {
126
128
  return;
127
129
  const entryPath = resolveWorkbookPart(target);
128
130
  const parts = capturePartClosure(entryPath, pkg.partText, pkg.partBytes, contentTypeOf);
129
- if (parts !== undefined)
130
- workbook[INTERNAL].restoreThemePart({ entryPath, parts });
131
+ if (parts === undefined)
132
+ return;
133
+ const xml = pkg.partText(entryPath) ?? '';
134
+ workbook[INTERNAL].restoreThemePart({ entryPath, parts }, { colors: parseThemeColorScheme(xml), fonts: parseThemeFontScheme(xml) });
131
135
  }
132
- function readSheetCommentThreads(sheetPath, pkg, workbook) {
133
- const path = sheetRelTarget(sheetPath, pkg.partText, 'threadedComment');
136
+ function readSheetCommentThreads(sheetRels, pkg, workbook) {
137
+ const path = sheetRels.targetPath('threadedComment');
134
138
  const xml = path === undefined ? undefined : pkg.partText(path);
135
139
  if (xml === undefined)
136
140
  return [];
137
141
  return buildCommentThreads(parseThreadedComments(xml), (id) => workbook.getPerson(id));
138
142
  }
139
- function readSheetPrinterSettings(sheetPath, pkg) {
140
- const path = sheetRelTarget(sheetPath, pkg.partText, 'printerSettings');
143
+ function readSheetPrinterSettings(sheetRels, pkg) {
144
+ const path = sheetRels.targetPath('printerSettings');
141
145
  return path === undefined ? undefined : pkg.partBytes(path);
142
146
  }
143
- function readSheetImages(sheetPath, pkg, workbook, sheet, imageIdByMediaPath) {
144
- const { partText, partBytes } = pkg;
145
- const drawingPath = sheetRelTarget(sheetPath, partText, 'drawing');
147
+ function internImage(workbook, pkg, imageIdByMediaPath, mediaPath) {
148
+ const known = imageIdByMediaPath.get(mediaPath);
149
+ if (known !== undefined)
150
+ return known;
151
+ const bytes = pkg.partBytes(mediaPath);
152
+ if (bytes === undefined)
153
+ return undefined;
154
+ const id = workbook.addImage({ buffer: bytes, extension: extensionOf(mediaPath) });
155
+ imageIdByMediaPath.set(mediaPath, id);
156
+ return id;
157
+ }
158
+ function readSheetImages(sheetRels, pkg, workbook, sheet, imageIdByMediaPath) {
159
+ const { partText } = pkg;
160
+ const drawingPath = sheetRels.targetPath('drawing');
146
161
  if (drawingPath === undefined)
147
162
  return;
148
163
  const drawingXml = partText(drawingPath);
@@ -150,20 +165,15 @@ function readSheetImages(sheetPath, pkg, workbook, sheet, imageIdByMediaPath) {
150
165
  return;
151
166
  if (drawingHasUnmodeledContent(drawingXml))
152
167
  return;
153
- const drawingRels = parseRelationships(partText(relsPathFor(drawingPath)) ?? '');
168
+ const drawingRels = readPartRelationships(drawingPath, partText);
154
169
  for (const anchor of parseDrawing(drawingXml)) {
155
- const target = drawingRels.get(anchor.embed);
156
- if (target === undefined)
170
+ const embedded = drawingRels.byId(anchor.embed);
171
+ if (embedded === undefined)
172
+ continue;
173
+ const mediaPath = drawingRels.pathOf(embedded.target);
174
+ const id = internImage(workbook, pkg, imageIdByMediaPath, mediaPath);
175
+ if (id === undefined)
157
176
  continue;
158
- const mediaPath = resolveRelativePart(drawingPath, target);
159
- let id = imageIdByMediaPath.get(mediaPath);
160
- if (id === undefined) {
161
- const bytes = partBytes(mediaPath);
162
- if (bytes === undefined)
163
- continue;
164
- id = workbook.addImage({ buffer: bytes, extension: extensionOf(mediaPath) });
165
- imageIdByMediaPath.set(mediaPath, id);
166
- }
167
177
  const rot = anchor.rotation !== undefined ? { rotation: anchor.rotation } : {};
168
178
  if (anchor.to !== undefined) {
169
179
  const mode = anchor.editAs !== undefined ? { editAs: anchor.editAs } : {};
@@ -174,29 +184,18 @@ function readSheetImages(sheetPath, pkg, workbook, sheet, imageIdByMediaPath) {
174
184
  }
175
185
  }
176
186
  }
177
- function readSheetBackground(sheetPath, pkg, workbook, sheet, imageIdByMediaPath) {
178
- const mediaPath = sheetRelTarget(sheetPath, pkg.partText, 'image');
187
+ function readSheetBackground(sheetRels, pkg, workbook, sheet, imageIdByMediaPath) {
188
+ const mediaPath = sheetRels.targetPath('image');
179
189
  if (mediaPath === undefined)
180
190
  return;
181
- let id = imageIdByMediaPath.get(mediaPath);
182
- if (id === undefined) {
183
- const bytes = pkg.partBytes(mediaPath);
184
- if (bytes === undefined)
185
- return;
186
- id = workbook.addImage({ buffer: bytes, extension: extensionOf(mediaPath) });
187
- imageIdByMediaPath.set(mediaPath, id);
188
- }
189
- sheet.addBackgroundImage(id);
191
+ const id = internImage(workbook, pkg, imageIdByMediaPath, mediaPath);
192
+ if (id !== undefined)
193
+ sheet.addBackgroundImage(id);
190
194
  }
191
- function readSheetPreservedReferences(sheetPath, sheetXml, pkg, contentTypeOf, sheet) {
195
+ function readSheetPreservedReferences(sheetRels, sheetXml, pkg, contentTypeOf, sheet) {
192
196
  const { partText, partBytes } = pkg;
193
- const relsXml = partText(relsPathFor(sheetPath));
194
- if (relsXml === undefined)
195
- return;
196
- const records = parseRelationshipRecords(relsXml);
197
- const recordById = new Map(records.map((record) => [record.id, record]));
198
197
  const capture = (element, relType, target) => {
199
- const entryPath = resolveRelativePart(sheetPath, target);
198
+ const entryPath = sheetRels.pathOf(target);
200
199
  const parts = capturePartClosure(entryPath, partText, partBytes, contentTypeOf);
201
200
  if (parts !== undefined)
202
201
  sheet[INTERNAL].addPreservedReference({ element, relType, entryPath, parts });
@@ -204,11 +203,11 @@ function readSheetPreservedReferences(sheetPath, sheetXml, pkg, contentTypeOf, s
204
203
  const referenceElements = sheet.images.length === 0 ? ['drawing', 'legacyDrawingHF'] : ['legacyDrawingHF'];
205
204
  for (const element of referenceElements) {
206
205
  const relId = worksheetReferenceRelId(sheetXml, element);
207
- const record = relId === undefined ? undefined : recordById.get(relId);
206
+ const record = relId === undefined ? undefined : sheetRels.byId(relId);
208
207
  if (record !== undefined && !record.external)
209
208
  capture(element, record.type, record.target);
210
209
  }
211
- for (const record of records) {
210
+ for (const record of sheetRels.records) {
212
211
  if (record.external)
213
212
  continue;
214
213
  if (isPreservedSheetRelType(record.type))
@@ -294,12 +293,9 @@ function worksheetReferenceRelId(sheetXml, element) {
294
293
  }
295
294
  return undefined;
296
295
  }
297
- function readSheetTables(sheetPath, pkg, sheet) {
298
- const relsXml = pkg.partText(relsPathFor(sheetPath));
299
- if (relsXml === undefined)
300
- return;
301
- for (const target of relationshipTargetsByType(relsXml, 'table')) {
302
- const tableXml = pkg.partText(resolveRelativePart(sheetPath, target));
296
+ function readSheetTables(sheetRels, pkg, sheet) {
297
+ for (const tablePath of sheetRels.targetPaths('table')) {
298
+ const tableXml = pkg.partText(tablePath);
303
299
  if (tableXml === undefined)
304
300
  continue;
305
301
  const options = parseTable(tableXml);
@@ -308,20 +304,14 @@ function readSheetTables(sheetPath, pkg, sheet) {
308
304
  }
309
305
  dropMergesInsideTables(sheet);
310
306
  }
311
- function readSheetPivotTables(sheetPath, pkg, sheet) {
307
+ function readSheetPivotTables(sheetRels, pkg, sheet) {
312
308
  const { partText } = pkg;
313
- const relsXml = partText(relsPathFor(sheetPath));
314
- if (relsXml === undefined)
315
- return;
316
- for (const target of relationshipTargetsByType(relsXml, 'pivotTable')) {
317
- const tablePath = resolveRelativePart(sheetPath, target);
309
+ for (const tablePath of sheetRels.targetPaths('pivotTable')) {
318
310
  const tableXml = partText(tablePath);
319
311
  if (tableXml === undefined)
320
312
  continue;
321
- const cacheTarget = relationshipTargetByType(partText(relsPathFor(tablePath)) ?? '', 'pivotCacheDefinition');
322
- const cacheXml = cacheTarget === undefined
323
- ? ''
324
- : (partText(resolveRelativePart(tablePath, cacheTarget)) ?? '');
313
+ const cachePath = readPartRelationships(tablePath, partText).targetPath('pivotCacheDefinition');
314
+ const cacheXml = cachePath === undefined ? '' : (partText(cachePath) ?? '');
325
315
  sheet[INTERNAL].addLoadedPivotTable(parsePivotTable(tableXml, cacheXml));
326
316
  }
327
317
  }
@@ -348,13 +338,14 @@ export function parseWorkbookSheets(xml) {
348
338
  name: attrs.name ?? '',
349
339
  relId: attrs['r:id'] ?? '',
350
340
  };
351
- if (attrs.state === 'hidden' || attrs.state === 'veryHidden')
352
- entry.state = attrs.state;
341
+ const state = enumToken(attrs.state, isVisibility);
342
+ if (state !== undefined && state !== 'visible')
343
+ entry.state = state;
353
344
  sheets.push(entry);
354
345
  }
355
346
  return sheets;
356
347
  }
357
- export function parseWorkbookProtection(xml) {
348
+ function parseWorkbookProtection(xml) {
358
349
  let result;
359
350
  parseXml(xml, {
360
351
  onOpen(name, attrs) {
@@ -382,26 +373,29 @@ export function parseWorkbookProtection(xml) {
382
373
  }
383
374
  export function applyWorkbookView(view, xml) {
384
375
  for (const { attrs } of openElements(xml, 'workbookView')) {
385
- applyViewNumber(attrs.xWindow, (value) => (view.x = value));
386
- applyViewNumber(attrs.yWindow, (value) => (view.y = value));
387
- applyViewNumber(attrs.windowWidth, (value) => (view.width = value));
388
- applyViewNumber(attrs.windowHeight, (value) => (view.height = value));
389
- applyViewNumber(attrs.activeTab, (value) => (view.activeTab = value));
390
- if (attrs.visibility === 'hidden' || attrs.visibility === 'veryHidden') {
391
- view.visibility = attrs.visibility;
392
- }
376
+ const x = numInteger(attrs.xWindow);
377
+ if (x !== undefined)
378
+ view.x = x;
379
+ const y = numInteger(attrs.yWindow);
380
+ if (y !== undefined)
381
+ view.y = y;
382
+ const width = numInteger(attrs.windowWidth, 0);
383
+ if (width !== undefined)
384
+ view.width = width;
385
+ const height = numInteger(attrs.windowHeight, 0);
386
+ if (height !== undefined)
387
+ view.height = height;
388
+ const activeTab = numInteger(attrs.activeTab, 0);
389
+ if (activeTab !== undefined)
390
+ view.activeTab = activeTab;
391
+ const visibility = enumToken(attrs.visibility, isVisibility);
392
+ if (visibility !== undefined && visibility !== 'visible')
393
+ view.visibility = visibility;
393
394
  if (boolStrict(attrs.minimized))
394
395
  view.minimized = true;
395
396
  return;
396
397
  }
397
398
  }
398
- function applyViewNumber(raw, assign) {
399
- if (raw === undefined)
400
- return;
401
- const value = Number(raw);
402
- if (Number.isFinite(value))
403
- assign(Math.trunc(value));
404
- }
405
399
  function parseWorkbookDefinedNames(xml, sheetOrder) {
406
400
  const names = [];
407
401
  let capture = false;
@@ -415,7 +409,7 @@ function parseWorkbookDefinedNames(xml, sheetOrder) {
415
409
  return;
416
410
  capture = true;
417
411
  refersTo = '';
418
- const scopeIndex = attrs.localSheetId === undefined ? -1 : Number(attrs.localSheetId);
412
+ const scopeIndex = numInteger(attrs.localSheetId, 0) ?? -1;
419
413
  const scope = sheetOrder[scopeIndex];
420
414
  pending = { name: attrs.name };
421
415
  if (scope !== undefined)
@@ -447,57 +441,25 @@ const CORE_PROPERTY_LOCAL_NAMES = new Set([
447
441
  'modified',
448
442
  ]);
449
443
  function applyCoreProperties(workbook, xml) {
450
- let capture = '';
451
- let text = '';
452
- parseXml(xml, {
453
- onOpen(name) {
454
- const local = localName(name);
455
- capture = CORE_PROPERTY_LOCAL_NAMES.has(local) ? local : '';
456
- text = '';
457
- },
458
- onText(chunk) {
459
- if (capture !== '')
460
- text += chunk;
461
- },
462
- onClose(name) {
463
- if (capture === '' || localName(name) !== capture)
464
- return;
465
- if (capture === 'title')
466
- workbook.properties.title = text;
467
- else if (capture === 'creator')
468
- workbook.properties.creator = text;
469
- else if (capture === 'lastModifiedBy')
470
- workbook.properties.lastModifiedBy = text;
471
- else {
472
- const date = new Date(text);
473
- if (!Number.isNaN(date.getTime())) {
474
- if (capture === 'created')
475
- workbook.properties.created = date;
476
- else
477
- workbook.properties.modified = date;
478
- }
444
+ for (const { local, text } of capturedText(xml, CORE_PROPERTY_LOCAL_NAMES)) {
445
+ if (local === 'title')
446
+ workbook.properties.title = text;
447
+ else if (local === 'creator')
448
+ workbook.properties.creator = text;
449
+ else if (local === 'lastModifiedBy')
450
+ workbook.properties.lastModifiedBy = text;
451
+ else {
452
+ const date = new Date(text);
453
+ if (!Number.isNaN(date.getTime())) {
454
+ if (local === 'created')
455
+ workbook.properties.created = date;
456
+ else
457
+ workbook.properties.modified = date;
479
458
  }
480
- capture = '';
481
- },
482
- });
459
+ }
460
+ }
483
461
  }
484
462
  function applyAppProperties(workbook, xml) {
485
- let capture = false;
486
- let text = '';
487
- parseXml(xml, {
488
- onOpen(name) {
489
- capture = localName(name) === 'Company';
490
- text = '';
491
- },
492
- onText(chunk) {
493
- if (capture)
494
- text += chunk;
495
- },
496
- onClose(name) {
497
- if (!capture || localName(name) !== 'Company')
498
- return;
499
- workbook.properties.company = text;
500
- capture = false;
501
- },
502
- });
463
+ for (const { text } of capturedText(xml, 'Company'))
464
+ workbook.properties.company = text;
503
465
  }
@@ -1,13 +1,47 @@
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-read.ts';
3
+ /**
4
+ * What {@link RunAccumulator.close} did with an element: `'container'` means the `<si>`/`<is>` itself
5
+ * closed and the caller should commit whatever it makes of the gathered runs, the one step the two
6
+ * readers do differently; `'claimed'` means the machine handled it; `'other'` leaves it to the caller.
7
+ *
8
+ * Three values rather than a boolean because both callers need to tell "the machine dealt with it"
9
+ * from "your string is complete", and a boolean would mean something different to each of them.
10
+ */
11
+ export type RunClose = 'claimed' | 'container' | 'other';
3
12
  export declare class RunAccumulator {
4
13
  #private;
5
- reset(): void;
14
+ /**
15
+ * @param options.container the element that opens one string: `'si'` for the shared-string pool,
16
+ * `'is'` for an inline string in a worksheet body.
17
+ * @param options.readRuns whether `<r>`/`<rPr>` open a rich-text run. The buffered readers read
18
+ * them; the row stream deliberately does not, and with runs off every `<t>` falls through to
19
+ * {@link plainText}, which is exactly the documented flattening.
20
+ */
21
+ constructor(options: {
22
+ readonly container: 'si' | 'is';
23
+ readonly readRuns: boolean;
24
+ });
6
25
  /** The runs gathered so far. */
7
26
  get runs(): RichTextRun[];
8
- beginRun(): void;
9
- beginProperties(): void;
10
- applyProperty(local: string, attrs: XmlAttributes): void;
11
- appendText(text: string): boolean;
12
- endRun(): void;
27
+ /** Whether the container held at least one `<r>`: what decides rich text from a plain string. */
28
+ get isRich(): boolean;
29
+ /** The container's own text: every `<t>` no run claimed, concatenated and already unescaped. */
30
+ get plainText(): string;
31
+ /**
32
+ * Open a string container by discarding whatever the last one gathered. Called for the machine's
33
+ * own container automatically; a caller whose own element may *hold* one (a `<c>` around an
34
+ * `<is>`) calls it too, or that container inherits the previous one's runs, which is the one way
35
+ * this accumulator can be misused.
36
+ *
37
+ * A new array is installed rather than the existing one emptied, so a value already built from the
38
+ * last container's runs keeps its own array.
39
+ */
40
+ beginContainer(): void;
41
+ /** Drive one element open, and return whether it was one of this machine's own. */
42
+ open(local: string, attrs: XmlAttributes, selfClosing: boolean): boolean;
43
+ /** Feed one run of character data. Ignored unless a `<t>` is open. */
44
+ text(chunk: string): void;
45
+ /** Drive one element close. See {@link RunClose}. */
46
+ close(local: string): RunClose;
13
47
  }
@@ -1,43 +1,107 @@
1
+ import { decodeSpreadsheetText, TextCapture } from '../../xml/xml-read.js';
1
2
  import { applyFontChild } from './read-styles.js';
2
3
  export class RunAccumulator {
4
+ #container;
5
+ #readRuns;
6
+ #capture = new TextCapture('t');
3
7
  #runs = [];
4
8
  #font = null;
5
- #text = '';
9
+ #inProperties = false;
10
+ #runText = '';
11
+ #plain = '';
6
12
  #inRun = false;
7
- reset() {
8
- this.#runs = [];
9
- this.#font = null;
10
- this.#inRun = false;
13
+ #isRich = false;
14
+ #inContainer = false;
15
+ constructor(options) {
16
+ this.#container = options.container;
17
+ this.#readRuns = options.readRuns;
11
18
  }
12
19
  get runs() {
13
20
  return this.#runs;
14
21
  }
15
- beginRun() {
16
- this.#inRun = true;
22
+ get isRich() {
23
+ return this.#isRich;
24
+ }
25
+ get plainText() {
26
+ return this.#plain;
27
+ }
28
+ beginContainer() {
29
+ this.#runs = [];
17
30
  this.#font = null;
18
- this.#text = '';
19
- }
20
- beginProperties() {
21
- if (this.#inRun)
22
- this.#font = {};
23
- }
24
- applyProperty(local, attrs) {
25
- if (this.#font !== null)
26
- applyFontChild(this.#font, local, attrs);
27
- }
28
- appendText(text) {
29
- if (!this.#inRun)
30
- return false;
31
- this.#text += text;
32
- return true;
33
- }
34
- endRun() {
35
- if (!this.#inRun)
36
- return;
37
- const run = { text: this.#text };
38
- if (this.#font !== null && Object.keys(this.#font).length > 0)
39
- run.font = this.#font;
40
- this.#runs.push(run);
31
+ this.#inProperties = false;
32
+ this.#runText = '';
33
+ this.#plain = '';
41
34
  this.#inRun = false;
35
+ this.#isRich = false;
36
+ }
37
+ open(local, attrs, selfClosing) {
38
+ switch (local) {
39
+ case this.#container:
40
+ this.#inContainer = true;
41
+ this.beginContainer();
42
+ return true;
43
+ case 'r':
44
+ if (!this.#readRuns || !this.#inContainer)
45
+ return false;
46
+ this.#isRich = true;
47
+ this.#inRun = true;
48
+ this.#font = null;
49
+ this.#inProperties = false;
50
+ this.#runText = '';
51
+ return true;
52
+ case 'rPr':
53
+ if (!this.#readRuns || !this.#inRun)
54
+ return false;
55
+ this.#font = {};
56
+ this.#inProperties = true;
57
+ return true;
58
+ case 't':
59
+ this.#capture.open(local, selfClosing);
60
+ return true;
61
+ default:
62
+ if (!this.#inProperties || this.#font === null)
63
+ return false;
64
+ applyFontChild(this.#font, local, attrs);
65
+ return true;
66
+ }
67
+ }
68
+ text(chunk) {
69
+ this.#capture.text(chunk);
70
+ }
71
+ close(local) {
72
+ switch (local) {
73
+ case 't': {
74
+ const text = this.#capture.close(local);
75
+ if (text !== undefined) {
76
+ const decoded = decodeSpreadsheetText(text);
77
+ if (this.#inRun)
78
+ this.#runText += decoded;
79
+ else if (this.#inContainer)
80
+ this.#plain += decoded;
81
+ }
82
+ return 'claimed';
83
+ }
84
+ case 'rPr':
85
+ if (!this.#readRuns || !this.#inProperties)
86
+ return 'other';
87
+ this.#inProperties = false;
88
+ return 'claimed';
89
+ case 'r': {
90
+ if (!this.#readRuns || !this.#inRun)
91
+ return 'other';
92
+ this.#inProperties = false;
93
+ const run = { text: this.#runText };
94
+ if (this.#font !== null && Object.keys(this.#font).length > 0)
95
+ run.font = this.#font;
96
+ this.#runs.push(run);
97
+ this.#inRun = false;
98
+ return 'claimed';
99
+ }
100
+ case this.#container:
101
+ this.#inContainer = false;
102
+ return 'container';
103
+ default:
104
+ return 'other';
105
+ }
42
106
  }
43
107
  }
@@ -1,7 +1,7 @@
1
1
  import type { RichTextRun } from '../../core/value.ts';
2
2
  /**
3
3
  * Serialise a rich-text value's runs as the inner content of an `<is>` element. A zero-length run
4
- * is dropped: an empty `<t/>` is schema-invalid Excel flags the file as corrupt and an empty
4
+ * is dropped: an empty `<t/>` is schema-invalid (Excel flags the file as corrupt) and an empty
5
5
  * run contributes nothing to the rendered text, so omitting it is loss-free.
6
6
  */
7
7
  export declare function richTextRunsXml(runs: readonly RichTextRun[]): string;
@@ -3,14 +3,14 @@ import type { RichTextValue } from '../../core/value.ts';
3
3
  * Interns cell string values into the shared-strings pool. {@link intern} returns the index a
4
4
  * `t="s"` cell writes as its `<v>`; identical entries collapse to one, so `count` (total references)
5
5
  * and `uniqueCount` (distinct entries) diverge exactly as Excel records them. Each entry is stored as
6
- * its rendered `<si>` inner XML, which is also its dedup key a plain string (`<t>…`) and rich runs
6
+ * its rendered `<si>` inner XML, which is also its dedup key: a plain string (`<t>…`) and rich runs
7
7
  * (`<r>…`) render to distinct markup, so the two kinds never collide in the pool.
8
8
  */
9
9
  export declare class SharedStringTable {
10
10
  #private;
11
11
  /** Intern a plain or rich string and return its `<si>` index, reusing the entry when it repeats. */
12
12
  intern(value: string | RichTextValue): number;
13
- /** Whether no string has been interned the writer omits the part entirely when so. */
13
+ /** Whether no string has been interned; the writer omits the part entirely when so. */
14
14
  get isEmpty(): boolean;
15
15
  /** Serialise the pool as the `xl/sharedStrings.xml` part. */
16
16
  toXml(): string;
@@ -1,5 +1,5 @@
1
1
  import type { AutoFilter } from '../../core/autofilter.ts';
2
- import type { HeaderFooter, PageBreak, PageMargins, PageSetup, PrintOptions } from '../../core/page-setup.ts';
2
+ import { type HeaderFooter, type PageBreak, type PageMargins, type PageSetup, type PrintOptions } from '../../core/page-setup.ts';
3
3
  import { type SheetProtection } from '../../core/protection.ts';
4
4
  import type { SheetView, Worksheet } from '../../core/worksheet.ts';
5
5
  export declare function sheetViewsXml(view: SheetView, active: boolean): string;