@shbernal/ts-xlsx 1.3.1 → 2.0.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 (155) hide show
  1. package/dist/core/address.d.ts +49 -2
  2. package/dist/core/address.js +31 -3
  3. package/dist/core/autofilter.d.ts +2 -2
  4. package/dist/core/cell.d.ts +17 -16
  5. package/dist/core/cell.js +3 -7
  6. package/dist/core/color-resolution.d.ts +7 -7
  7. package/dist/core/column.d.ts +8 -9
  8. package/dist/core/column.js +2 -4
  9. package/dist/core/comment-thread.d.ts +32 -23
  10. package/dist/core/comment-thread.js +4 -4
  11. package/dist/core/conditional-formatting-overlay.d.ts +1 -1
  12. package/dist/core/conditional-formatting.d.ts +3 -3
  13. package/dist/core/data-validation-overlay.d.ts +1 -1
  14. package/dist/core/data-validation.d.ts +3 -3
  15. package/dist/core/date.d.ts +3 -3
  16. package/dist/core/formula.d.ts +6 -6
  17. package/dist/core/grid-edits.js +3 -3
  18. package/dist/core/image.d.ts +37 -2
  19. package/dist/core/image.js +6 -0
  20. package/dist/core/internal.d.ts +9 -1
  21. package/dist/core/limits.d.ts +4 -4
  22. package/dist/core/merge.d.ts +6 -12
  23. package/dist/core/merge.js +0 -3
  24. package/dist/core/page-setup.d.ts +4 -4
  25. package/dist/core/pivot-table.d.ts +2 -4
  26. package/dist/core/pivot-table.js +5 -5
  27. package/dist/core/preserved.d.ts +3 -3
  28. package/dist/core/protection.d.ts +4 -4
  29. package/dist/core/range.d.ts +16 -14
  30. package/dist/core/range.js +5 -5
  31. package/dist/core/row-input.d.ts +2 -8
  32. package/dist/core/row-input.js +2 -2
  33. package/dist/core/row.d.ts +7 -8
  34. package/dist/core/row.js +2 -4
  35. package/dist/core/style.d.ts +14 -14
  36. package/dist/core/table-style.d.ts +9 -9
  37. package/dist/core/table-style.js +1 -1
  38. package/dist/core/table.d.ts +30 -42
  39. package/dist/core/table.js +16 -10
  40. package/dist/core/theme.d.ts +13 -43
  41. package/dist/core/theme.js +2 -148
  42. package/dist/core/value.d.ts +7 -7
  43. package/dist/core/workbook-protection.d.ts +2 -2
  44. package/dist/core/workbook-styles.d.ts +26 -0
  45. package/dist/core/workbook-styles.js +48 -0
  46. package/dist/core/workbook-theme.d.ts +25 -0
  47. package/dist/core/workbook-theme.js +49 -0
  48. package/dist/core/workbook-vba.d.ts +17 -0
  49. package/dist/core/workbook-vba.js +79 -0
  50. package/dist/core/workbook.d.ts +131 -85
  51. package/dist/core/workbook.js +74 -133
  52. package/dist/core/worksheet-comments.d.ts +9 -0
  53. package/dist/core/worksheet-comments.js +52 -0
  54. package/dist/core/worksheet-model.d.ts +4 -4
  55. package/dist/core/worksheet-model.js +1 -0
  56. package/dist/core/worksheet-pictures.d.ts +29 -0
  57. package/dist/core/worksheet-pictures.js +42 -0
  58. package/dist/core/worksheet.d.ts +85 -77
  59. package/dist/core/worksheet.js +36 -95
  60. package/dist/customui/errors.d.ts +1 -1
  61. package/dist/customui/ribbon.d.ts +3 -3
  62. package/dist/entries/core.d.ts +4 -4
  63. package/dist/entries/core.js +1 -1
  64. package/dist/entries/vba.d.ts +1 -1
  65. package/dist/entries/vba.js +1 -1
  66. package/dist/entries/xlsx.d.ts +1 -0
  67. package/dist/entries/xlsx.js +1 -0
  68. package/dist/errors.d.ts +12 -12
  69. package/dist/io/csv/read.js +1 -2
  70. package/dist/io/csv/write.d.ts +8 -3
  71. package/dist/io/csv/write.js +17 -2
  72. package/dist/io/opc/errors.d.ts +6 -6
  73. package/dist/io/opc/inflate.d.ts +1 -1
  74. package/dist/io/opc/namespaces.d.ts +1 -1
  75. package/dist/io/opc/read-opc.d.ts +19 -3
  76. package/dist/io/opc/read-opc.js +14 -8
  77. package/dist/io/opc/read-options.d.ts +2 -2
  78. package/dist/io/opc/sniff-format.d.ts +2 -2
  79. package/dist/io/style/xf-style.d.ts +38 -6
  80. package/dist/io/style/xf-style.js +22 -0
  81. package/dist/io/xlsb/errors.d.ts +1 -1
  82. package/dist/io/xlsb/formula.d.ts +4 -4
  83. package/dist/io/xlsb/primitives.d.ts +8 -8
  84. package/dist/io/xlsb/ptg-functions.d.ts +1 -1
  85. package/dist/io/xlsb/read-styles.js +2 -20
  86. package/dist/io/xlsb/read.d.ts +2 -2
  87. package/dist/io/xlsx/cell-accumulator.d.ts +25 -6
  88. package/dist/io/xlsx/cell-accumulator.js +111 -30
  89. package/dist/io/xlsx/cell-value.d.ts +2 -2
  90. package/dist/io/xlsx/cell-value.js +3 -3
  91. package/dist/io/xlsx/color-xml.js +11 -17
  92. package/dist/io/xlsx/comments.d.ts +5 -5
  93. package/dist/io/xlsx/comments.js +13 -6
  94. package/dist/io/xlsx/conditional-formatting.d.ts +3 -3
  95. package/dist/io/xlsx/conditional-formatting.js +7 -16
  96. package/dist/io/xlsx/data-validation.d.ts +2 -2
  97. package/dist/io/xlsx/data-validation.js +1 -1
  98. package/dist/io/xlsx/errors.d.ts +3 -3
  99. package/dist/io/xlsx/hyperlinks.d.ts +4 -2
  100. package/dist/io/xlsx/hyperlinks.js +5 -5
  101. package/dist/io/xlsx/images.d.ts +1 -1
  102. package/dist/io/xlsx/images.js +8 -8
  103. package/dist/io/xlsx/namespaces.d.ts +2 -2
  104. package/dist/io/xlsx/package-plan.js +5 -2
  105. package/dist/io/xlsx/read-pivot.js +2 -5
  106. package/dist/io/xlsx/read-rows.d.ts +13 -13
  107. package/dist/io/xlsx/read-rows.js +19 -55
  108. package/dist/io/xlsx/read-shared-strings.js +7 -5
  109. package/dist/io/xlsx/read-styles.d.ts +3 -3
  110. package/dist/io/xlsx/read-styles.js +40 -65
  111. package/dist/io/xlsx/read-worksheet.js +61 -115
  112. package/dist/io/xlsx/read.d.ts +3 -5
  113. package/dist/io/xlsx/read.js +61 -69
  114. package/dist/io/xlsx/rich-runs.d.ts +1 -1
  115. package/dist/io/xlsx/rich-runs.js +1 -1
  116. package/dist/io/xlsx/rich-text.d.ts +1 -1
  117. package/dist/io/xlsx/shared-strings.d.ts +2 -2
  118. package/dist/io/xlsx/sheet-properties.js +2 -2
  119. package/dist/io/xlsx/styles.d.ts +8 -9
  120. package/dist/io/xlsx/styles.js +4 -3
  121. package/dist/io/xlsx/tables.d.ts +2 -2
  122. package/dist/io/xlsx/tables.js +8 -13
  123. package/dist/io/xlsx/theme-xml.d.ts +37 -0
  124. package/dist/io/xlsx/theme-xml.js +142 -0
  125. package/dist/io/xlsx/threaded-comments.d.ts +18 -27
  126. package/dist/io/xlsx/threaded-comments.js +9 -21
  127. package/dist/io/xlsx/worksheet-xml.d.ts +2 -2
  128. package/dist/io/xlsx/worksheet-xml.js +7 -12
  129. package/dist/io/xlsx/write-stream.d.ts +15 -15
  130. package/dist/io/xlsx/write-stream.js +3 -3
  131. package/dist/io/xlsx/write.d.ts +8 -7
  132. package/dist/io/xlsx/write.js +6 -3
  133. package/dist/io/xlsx/x14-ext.d.ts +1 -1
  134. package/dist/vba/bytes.d.ts +12 -0
  135. package/dist/vba/bytes.js +40 -0
  136. package/dist/vba/cfb-writer.d.ts +1 -1
  137. package/dist/vba/cfb.d.ts +1 -1
  138. package/dist/vba/cfb.js +15 -51
  139. package/dist/vba/codepage.d.ts +2 -2
  140. package/dist/vba/errors.d.ts +2 -2
  141. package/dist/vba/index.d.ts +2 -2
  142. package/dist/vba/index.js +2 -2
  143. package/dist/vba/ms-ovba.d.ts +1 -1
  144. package/dist/vba/ms-ovba.js +1 -3
  145. package/dist/vba/project-editor.d.ts +9 -9
  146. package/dist/vba/project-editor.js +2 -11
  147. package/dist/vba/project.d.ts +5 -5
  148. package/dist/vba/project.js +1 -10
  149. package/dist/vba/vba-encoding.d.ts +1 -1
  150. package/dist/xml/errors.d.ts +1 -1
  151. package/dist/xml/xml-read.d.ts +38 -9
  152. package/dist/xml/xml-read.js +23 -0
  153. package/dist/xml/xml.d.ts +31 -9
  154. package/dist/xml/xml.js +23 -3
  155. package/package.json +41 -35
@@ -1,12 +1,12 @@
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 { boolStrict, localName, numInteger, openElements, parseXml } from '../../xml/xml-read.js';
7
7
  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';
8
+ import { extensionOf } from '../opc/part-paths.js';
9
+ import { capturePartClosure, contentTypeResolver, packageAccessors, parseRelationshipRecords, parseRelationships, readPartRelationships, relationshipTargetByType, resolveRelativePart, resolveWorkbookPart, } from '../opc/read-opc.js';
10
10
  import { DEFAULT_MAX_UNCOMPRESSED } from '../opc/read-options.js';
11
11
  import { inflateSpreadsheetPackage } from '../opc/sniff-format.js';
12
12
  import { readXlsbPackage, XLSB_WORKBOOK_PART } from '../xlsb/read.js';
@@ -20,6 +20,7 @@ import { parseSharedStrings } from './read-shared-strings.js';
20
20
  import { parseIndexedColors, parseMruColors, parseStyleTable, parseTableStyles, } from './read-styles.js';
21
21
  import { parseWorksheet } from './read-worksheet.js';
22
22
  import { parseTable } from './tables.js';
23
+ import { parseThemeColorScheme, parseThemeFontScheme } from './theme-xml.js';
23
24
  import { buildCommentThreads, parsePersons, parseThreadedComments } from './threaded-comments.js';
24
25
  export { parseRelationships, resolveWorkbookPart } from '../opc/read-opc.js';
25
26
  export { DEFAULT_MAX_UNCOMPRESSED } from '../opc/read-options.js';
@@ -70,9 +71,9 @@ export function readXlsx(data, options = {}) {
70
71
  if (sheetXml !== undefined)
71
72
  parseWorksheet(sheetXml, sheet, sharedStrings, xfStyles);
72
73
  if (path !== undefined) {
74
+ const sheetRels = readPartRelationships(path, partText);
73
75
  if (sheetXml !== undefined) {
74
- const sheetRels = parseRelationships(partText(relsPathFor(path)) ?? '');
75
- applyHyperlinks(sheet, parseSheetHyperlinks(sheetXml), sheetRels);
76
+ applyHyperlinks(sheet, parseSheetHyperlinks(sheetXml), (id) => sheetRels.byId(id)?.target);
76
77
  applyDataValidations(sheet, [
77
78
  ...parseDataValidations(sheetXml),
78
79
  ...parseExtendedDataValidations(sheetXml),
@@ -80,20 +81,20 @@ export function readXlsx(data, options = {}) {
80
81
  for (const cf of parseConditionalFormattings(sheetXml))
81
82
  sheet.addConditionalFormatting(cf);
82
83
  }
83
- const threads = readSheetCommentThreads(path, pkg, workbook);
84
+ const threads = readSheetCommentThreads(sheetRels, pkg, workbook);
84
85
  if (threads.length > 0)
85
86
  sheet[INTERNAL].restoreCommentThreads(threads);
86
- const comments = readSheetComments(path, pkg);
87
+ const comments = readSheetComments(sheetRels, pkg);
87
88
  if (comments !== undefined)
88
89
  applyNotes(sheet, comments);
89
- readSheetImages(path, pkg, workbook, sheet, imageIdByMediaPath);
90
- readSheetBackground(path, pkg, workbook, sheet, imageIdByMediaPath);
90
+ readSheetImages(sheetRels, pkg, workbook, sheet, imageIdByMediaPath);
91
+ readSheetBackground(sheetRels, pkg, workbook, sheet, imageIdByMediaPath);
91
92
  if (sheetXml !== undefined) {
92
- readSheetPreservedReferences(path, sheetXml, pkg, contentTypeOf, sheet);
93
+ readSheetPreservedReferences(sheetRels, sheetXml, pkg, contentTypeOf, sheet);
93
94
  }
94
- readSheetTables(path, pkg, sheet);
95
- readSheetPivotTables(path, pkg, sheet);
96
- const printerSettings = readSheetPrinterSettings(path, pkg);
95
+ readSheetTables(sheetRels, pkg, sheet);
96
+ readSheetPivotTables(sheetRels, pkg, sheet);
97
+ const printerSettings = readSheetPrinterSettings(sheetRels, pkg);
97
98
  if (printerSettings !== undefined)
98
99
  sheet.pageSetup.printerSettings = printerSettings;
99
100
  }
@@ -105,8 +106,8 @@ export function readXlsx(data, options = {}) {
105
106
  }
106
107
  return workbook;
107
108
  }
108
- function readSheetComments(sheetPath, pkg) {
109
- const commentsPath = sheetRelTarget(sheetPath, pkg.partText, 'comments');
109
+ function readSheetComments(sheetRels, pkg) {
110
+ const commentsPath = sheetRels.targetPath('comments');
110
111
  if (commentsPath === undefined)
111
112
  return undefined;
112
113
  const commentsXml = pkg.partText(commentsPath);
@@ -126,23 +127,25 @@ function readWorkbookTheme(workbookRelsXml, pkg, contentTypeOf, workbook) {
126
127
  return;
127
128
  const entryPath = resolveWorkbookPart(target);
128
129
  const parts = capturePartClosure(entryPath, pkg.partText, pkg.partBytes, contentTypeOf);
129
- if (parts !== undefined)
130
- workbook[INTERNAL].restoreThemePart({ entryPath, parts });
130
+ if (parts === undefined)
131
+ return;
132
+ const xml = pkg.partText(entryPath) ?? '';
133
+ workbook[INTERNAL].restoreThemePart({ entryPath, parts }, { colors: parseThemeColorScheme(xml), fonts: parseThemeFontScheme(xml) });
131
134
  }
132
- function readSheetCommentThreads(sheetPath, pkg, workbook) {
133
- const path = sheetRelTarget(sheetPath, pkg.partText, 'threadedComment');
135
+ function readSheetCommentThreads(sheetRels, pkg, workbook) {
136
+ const path = sheetRels.targetPath('threadedComment');
134
137
  const xml = path === undefined ? undefined : pkg.partText(path);
135
138
  if (xml === undefined)
136
139
  return [];
137
140
  return buildCommentThreads(parseThreadedComments(xml), (id) => workbook.getPerson(id));
138
141
  }
139
- function readSheetPrinterSettings(sheetPath, pkg) {
140
- const path = sheetRelTarget(sheetPath, pkg.partText, 'printerSettings');
142
+ function readSheetPrinterSettings(sheetRels, pkg) {
143
+ const path = sheetRels.targetPath('printerSettings');
141
144
  return path === undefined ? undefined : pkg.partBytes(path);
142
145
  }
143
- function readSheetImages(sheetPath, pkg, workbook, sheet, imageIdByMediaPath) {
146
+ function readSheetImages(sheetRels, pkg, workbook, sheet, imageIdByMediaPath) {
144
147
  const { partText, partBytes } = pkg;
145
- const drawingPath = sheetRelTarget(sheetPath, partText, 'drawing');
148
+ const drawingPath = sheetRels.targetPath('drawing');
146
149
  if (drawingPath === undefined)
147
150
  return;
148
151
  const drawingXml = partText(drawingPath);
@@ -150,12 +153,12 @@ function readSheetImages(sheetPath, pkg, workbook, sheet, imageIdByMediaPath) {
150
153
  return;
151
154
  if (drawingHasUnmodeledContent(drawingXml))
152
155
  return;
153
- const drawingRels = parseRelationships(partText(relsPathFor(drawingPath)) ?? '');
156
+ const drawingRels = readPartRelationships(drawingPath, partText);
154
157
  for (const anchor of parseDrawing(drawingXml)) {
155
- const target = drawingRels.get(anchor.embed);
156
- if (target === undefined)
158
+ const embedded = drawingRels.byId(anchor.embed);
159
+ if (embedded === undefined)
157
160
  continue;
158
- const mediaPath = resolveRelativePart(drawingPath, target);
161
+ const mediaPath = drawingRels.pathOf(embedded.target);
159
162
  let id = imageIdByMediaPath.get(mediaPath);
160
163
  if (id === undefined) {
161
164
  const bytes = partBytes(mediaPath);
@@ -174,8 +177,8 @@ function readSheetImages(sheetPath, pkg, workbook, sheet, imageIdByMediaPath) {
174
177
  }
175
178
  }
176
179
  }
177
- function readSheetBackground(sheetPath, pkg, workbook, sheet, imageIdByMediaPath) {
178
- const mediaPath = sheetRelTarget(sheetPath, pkg.partText, 'image');
180
+ function readSheetBackground(sheetRels, pkg, workbook, sheet, imageIdByMediaPath) {
181
+ const mediaPath = sheetRels.targetPath('image');
179
182
  if (mediaPath === undefined)
180
183
  return;
181
184
  let id = imageIdByMediaPath.get(mediaPath);
@@ -188,15 +191,10 @@ function readSheetBackground(sheetPath, pkg, workbook, sheet, imageIdByMediaPath
188
191
  }
189
192
  sheet.addBackgroundImage(id);
190
193
  }
191
- function readSheetPreservedReferences(sheetPath, sheetXml, pkg, contentTypeOf, sheet) {
194
+ function readSheetPreservedReferences(sheetRels, sheetXml, pkg, contentTypeOf, sheet) {
192
195
  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
196
  const capture = (element, relType, target) => {
199
- const entryPath = resolveRelativePart(sheetPath, target);
197
+ const entryPath = sheetRels.pathOf(target);
200
198
  const parts = capturePartClosure(entryPath, partText, partBytes, contentTypeOf);
201
199
  if (parts !== undefined)
202
200
  sheet[INTERNAL].addPreservedReference({ element, relType, entryPath, parts });
@@ -204,11 +202,11 @@ function readSheetPreservedReferences(sheetPath, sheetXml, pkg, contentTypeOf, s
204
202
  const referenceElements = sheet.images.length === 0 ? ['drawing', 'legacyDrawingHF'] : ['legacyDrawingHF'];
205
203
  for (const element of referenceElements) {
206
204
  const relId = worksheetReferenceRelId(sheetXml, element);
207
- const record = relId === undefined ? undefined : recordById.get(relId);
205
+ const record = relId === undefined ? undefined : sheetRels.byId(relId);
208
206
  if (record !== undefined && !record.external)
209
207
  capture(element, record.type, record.target);
210
208
  }
211
- for (const record of records) {
209
+ for (const record of sheetRels.records) {
212
210
  if (record.external)
213
211
  continue;
214
212
  if (isPreservedSheetRelType(record.type))
@@ -294,12 +292,9 @@ function worksheetReferenceRelId(sheetXml, element) {
294
292
  }
295
293
  return undefined;
296
294
  }
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));
295
+ function readSheetTables(sheetRels, pkg, sheet) {
296
+ for (const tablePath of sheetRels.targetPaths('table')) {
297
+ const tableXml = pkg.partText(tablePath);
303
298
  if (tableXml === undefined)
304
299
  continue;
305
300
  const options = parseTable(tableXml);
@@ -308,20 +303,14 @@ function readSheetTables(sheetPath, pkg, sheet) {
308
303
  }
309
304
  dropMergesInsideTables(sheet);
310
305
  }
311
- function readSheetPivotTables(sheetPath, pkg, sheet) {
306
+ function readSheetPivotTables(sheetRels, pkg, sheet) {
312
307
  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);
308
+ for (const tablePath of sheetRels.targetPaths('pivotTable')) {
318
309
  const tableXml = partText(tablePath);
319
310
  if (tableXml === undefined)
320
311
  continue;
321
- const cacheTarget = relationshipTargetByType(partText(relsPathFor(tablePath)) ?? '', 'pivotCacheDefinition');
322
- const cacheXml = cacheTarget === undefined
323
- ? ''
324
- : (partText(resolveRelativePart(tablePath, cacheTarget)) ?? '');
312
+ const cachePath = readPartRelationships(tablePath, partText).targetPath('pivotCacheDefinition');
313
+ const cacheXml = cachePath === undefined ? '' : (partText(cachePath) ?? '');
325
314
  sheet[INTERNAL].addLoadedPivotTable(parsePivotTable(tableXml, cacheXml));
326
315
  }
327
316
  }
@@ -354,7 +343,7 @@ export function parseWorkbookSheets(xml) {
354
343
  }
355
344
  return sheets;
356
345
  }
357
- export function parseWorkbookProtection(xml) {
346
+ function parseWorkbookProtection(xml) {
358
347
  let result;
359
348
  parseXml(xml, {
360
349
  onOpen(name, attrs) {
@@ -382,11 +371,21 @@ export function parseWorkbookProtection(xml) {
382
371
  }
383
372
  export function applyWorkbookView(view, xml) {
384
373
  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));
374
+ const x = numInteger(attrs.xWindow);
375
+ if (x !== undefined)
376
+ view.x = x;
377
+ const y = numInteger(attrs.yWindow);
378
+ if (y !== undefined)
379
+ view.y = y;
380
+ const width = numInteger(attrs.windowWidth, 0);
381
+ if (width !== undefined)
382
+ view.width = width;
383
+ const height = numInteger(attrs.windowHeight, 0);
384
+ if (height !== undefined)
385
+ view.height = height;
386
+ const activeTab = numInteger(attrs.activeTab, 0);
387
+ if (activeTab !== undefined)
388
+ view.activeTab = activeTab;
390
389
  if (attrs.visibility === 'hidden' || attrs.visibility === 'veryHidden') {
391
390
  view.visibility = attrs.visibility;
392
391
  }
@@ -395,13 +394,6 @@ export function applyWorkbookView(view, xml) {
395
394
  return;
396
395
  }
397
396
  }
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
397
  function parseWorkbookDefinedNames(xml, sheetOrder) {
406
398
  const names = [];
407
399
  let capture = false;
@@ -415,7 +407,7 @@ function parseWorkbookDefinedNames(xml, sheetOrder) {
415
407
  return;
416
408
  capture = true;
417
409
  refersTo = '';
418
- const scopeIndex = attrs.localSheetId === undefined ? -1 : Number(attrs.localSheetId);
410
+ const scopeIndex = numInteger(attrs.localSheetId, 0) ?? -1;
419
411
  const scope = sheetOrder[scopeIndex];
420
412
  pending = { name: attrs.name };
421
413
  if (scope !== undefined)
@@ -2,7 +2,7 @@ import type { RichTextRun } from '../../core/value.ts';
2
2
  import type { XmlAttributes } from '../../xml/xml-read.ts';
3
3
  export declare class RunAccumulator {
4
4
  #private;
5
- reset(): void;
5
+ beginContainer(): void;
6
6
  /** The runs gathered so far. */
7
7
  get runs(): RichTextRun[];
8
8
  beginRun(): void;
@@ -4,7 +4,7 @@ export class RunAccumulator {
4
4
  #font = null;
5
5
  #text = '';
6
6
  #inRun = false;
7
- reset() {
7
+ beginContainer() {
8
8
  this.#runs = [];
9
9
  this.#font = null;
10
10
  this.#inRun = false;
@@ -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,6 +1,6 @@
1
1
  import { encodeAddress } from '../../core/address.js';
2
2
  import { SHEET_PROTECTION_FLAGS } from '../../core/protection.js';
3
- import { attr, boolAttr, escapeAttr, escapeText, numberText } from '../../xml/xml.js';
3
+ import { attr, boolAttr, escapeAttr, escapeSpreadsheetText, numberText } from '../../xml/xml.js';
4
4
  import { colorAttrs } from './color-xml.js';
5
5
  export function sheetViewsXml(view, active) {
6
6
  const selected = active ? ' tabSelected="1"' : '';
@@ -99,7 +99,7 @@ export function headerFooterXml(hf) {
99
99
  if (differentFirst)
100
100
  attrs += ' differentFirst="1"';
101
101
  const body = children
102
- .map(({ tag, key }) => `<${tag}>${escapeText(hf[key])}</${tag}>`)
102
+ .map(({ tag, key }) => `<${tag}>${escapeSpreadsheetText(hf[key])}</${tag}>`)
103
103
  .join('');
104
104
  return `<headerFooter${attrs}>${body}</headerFooter>`;
105
105
  }
@@ -8,15 +8,15 @@ export interface CellStyle {
8
8
  readonly border?: Border | undefined;
9
9
  readonly alignment?: Alignment | undefined;
10
10
  readonly protection?: Protection | undefined;
11
- /** The quote-prefix flag an attribute on the xf, not a shared sub-table entry. */
11
+ /** The quote-prefix flag: an attribute on the xf, not a shared sub-table entry. */
12
12
  readonly quotePrefix?: boolean | undefined;
13
- /** The `xfId` link into `cellStyleXfs` the named cell style this format inherits from (0 = Normal). */
13
+ /** The `xfId` link into `cellStyleXfs`: the named cell style this format inherits from (0 = Normal). */
14
14
  readonly xfId?: number | undefined;
15
15
  }
16
16
  /** What a {@link StyleRegistry} needs from its workbook before any style is interned. */
17
17
  export interface StyleRegistryOptions {
18
18
  /**
19
- * The font every cell naming none of its own renders in emitted as id 0. Take it from
19
+ * The font every cell naming none of its own renders in, emitted as id 0. Take it from
20
20
  * {@link Workbook.defaultFont}, which resolves the authored/declared/theme chain and guarantees a
21
21
  * complete entry. Omitted, the registry falls back to the Office default.
22
22
  */
@@ -28,7 +28,7 @@ export interface StyleRegistryOptions {
28
28
  *
29
29
  * The reader flattens font 0 onto every xf that names it, so a cell that merely inherited the
30
30
  * file's default arrives carrying it as a concrete face. That face is an artefact of reading, not
31
- * an authored intent in the source file the cell said nothing about its font so it must
31
+ * an authored intent (in the source file the cell said nothing about its font) so it must
32
32
  * collapse back to id 0 and follow the new default rather than pin itself to the old one through a
33
33
  * custom entry.
34
34
  */
@@ -59,7 +59,7 @@ export declare class StyleRegistry {
59
59
  * rule's `dxfId`, and every `<tableStyleElement dxfId="…">` inside a preserved `<tableStyle>` (see
60
60
  * {@link seedTableStyles}). Those constructs are carried as opaque XML precisely *because* the
61
61
  * indices they name do not move. Renumbering, reordering, or de-duplicating the seeded entries
62
- * would silently re-point every one of them at a different format a change no schema check and no
62
+ * would silently re-point every one of them at a different format: a change no schema check and no
63
63
  * round-trip of our own can catch, because the file stays perfectly valid and merely renders wrong.
64
64
  */
65
65
  seedDifferentialStyles(fragments: readonly string[]): void;
@@ -88,13 +88,13 @@ export declare class StyleRegistry {
88
88
  * {@link seedTableStyles}, whose preserved definitions these append after.
89
89
  *
90
90
  * A definition here **replaces** a preserved one of the same name. Two `<tableStyle>` elements
91
- * sharing a name is ambiguous a table's `tableStyleInfo/@name` would reach whichever a consumer
92
- * happened to index first so authoring a name the source already used is read as overriding it,
91
+ * sharing a name is ambiguous, since a table's `tableStyleInfo/@name` would reach whichever a
92
+ * consumer happened to index first, so authoring a name the source already used is read as overriding it,
93
93
  * which is what asking for it means.
94
94
  */
95
95
  addTableStyle(style: TableStyle): void;
96
96
  /**
97
- * Intern an authored differential style, returning the `<dxfs>` index that references it a
97
+ * Intern an authored differential style, returning the `<dxfs>` index that references it: a
98
98
  * conditional-formatting rule's `dxfId`, or a table style element's. Identical styles collapse to
99
99
  * one entry, whichever feature asked for them, so a highlight rule and a table style's header row
100
100
  * painted the same way share a single `<dxf>`.
@@ -107,4 +107,3 @@ export declare class StyleRegistry {
107
107
  toXml(): string;
108
108
  }
109
109
  export declare function fontXml(font: Font, nameTag?: 'name' | 'rFont'): string;
110
- export declare function dxfXml(style: DifferentialStyle): string;
@@ -1,8 +1,8 @@
1
1
  import { isBorderStyle, isFillPatternType, isFontScheme, isFontVerticalAlignment, isHorizontalAlignment, isNamedUnderlineStyle, isVerticalAlignment, } from '../../core/style.js';
2
2
  import { TABLE_STYLE_ELEMENT_TYPES } from '../../core/table-style.js';
3
3
  import { AuthoringError } from '../../errors.js';
4
- import { escapeAttr, XML_DECLARATION } from '../../xml/xml.js';
5
4
  import { decodeEntities } from '../../xml/xml-read.js';
5
+ import { assertRepresentable, escapeAttr, XML_DECLARATION } from '../../xml/xml.js';
6
6
  import { colorAttrs } from './color-xml.js';
7
7
  import { MARKUP_COMPATIBILITY_NS, SPREADSHEETML_NS } from './namespaces.js';
8
8
  const RESERVED_FILL_COUNT = 2;
@@ -188,7 +188,7 @@ export class StyleRegistry {
188
188
  const cellStyleXfs = this.#cellStyleXfs.map((format) => xfXml(format, null)).join('');
189
189
  const cellStyles = this.#cellStyleNames.map(cellStyleTag).join('');
190
190
  const fontCount = RESERVED_FONT_COUNT + this.#fontXml.length;
191
- const fonts = `<font>${this.#defaultFontBody}</font>` + this.#fontXml.join('');
191
+ const fonts = `<font>${this.#defaultFontBody}</font>${this.#fontXml.join('')}`;
192
192
  const borderCount = RESERVED_BORDER_COUNT + this.#borderXml.length;
193
193
  const borders = DEFAULT_BORDER + this.#borderXml.join('');
194
194
  return (XML_DECLARATION +
@@ -362,7 +362,7 @@ export function fontXml(font, nameTag = 'name') {
362
362
  parts.push(`<scheme val="${checkedToken(font.scheme, isFontScheme, 'font scheme')}"/>`);
363
363
  return parts.join('');
364
364
  }
365
- export function dxfXml(style) {
365
+ function dxfXml(style) {
366
366
  const parts = [];
367
367
  if (style.font !== undefined) {
368
368
  const font = fontXml(style.font);
@@ -425,6 +425,7 @@ function edgeXml(tag, edge) {
425
425
  return `<${tag} style="${style}"><color ${colorAttrs(edge.color)}/></${tag}>`;
426
426
  }
427
427
  function escapeFormatCode(code) {
428
+ assertRepresentable(code);
428
429
  return code
429
430
  .replace(/&/g, '&amp;')
430
431
  .replace(/</g, '&lt;')
@@ -2,8 +2,8 @@ import { type Table, type TableOptions } from '../../core/table.ts';
2
2
  export declare function tableXml(table: Table, id: number): string;
3
3
  /**
4
4
  * Parse a `<table>` part into the options that reconstruct it, or `undefined` when the XML is not a
5
- * usable table (no name, no ref, or no columns Excel treats such a part as corrupt, so we drop it
6
- * rather than fabricate a degenerate table). Duplicate column names are not resolved here the
5
+ * usable table (no name, no ref, or no columns: Excel treats such a part as corrupt, so we drop it
6
+ * rather than fabricate a degenerate table). Duplicate column names are not resolved here, since the
7
7
  * {@link Table} constructor disambiguates them, so authoring and loading share one implementation.
8
8
  */
9
9
  export declare function parseTable(xml: string): TableOptions | undefined;
@@ -1,7 +1,7 @@
1
1
  import { decodeRange, encodeAddress } from '../../core/address.js';
2
2
  import { isTotalsRowFunction, } from '../../core/table.js';
3
+ import { boolPresent, localName, numInteger, parseXml } from '../../xml/xml-read.js';
3
4
  import { boolAttr, escapeAttr, escapeText, XML_DECLARATION } from '../../xml/xml.js';
4
- import { localName, parseXml } from '../../xml/xml-read.js';
5
5
  import { NS } from './relationships.js';
6
6
  export function tableXml(table, id) {
7
7
  const name = escapeAttr(table.name);
@@ -55,9 +55,6 @@ function tableColumnXml(column, id) {
55
55
  }
56
56
  return `<tableColumn ${attrs}/>`;
57
57
  }
58
- function parseOoxmlBool(value) {
59
- return value !== '0' && value !== 'false';
60
- }
61
58
  export function parseTable(xml) {
62
59
  let name;
63
60
  let displayName;
@@ -77,12 +74,10 @@ export function parseTable(xml) {
77
74
  name = attrs.name ?? attrs.displayName;
78
75
  displayName = attrs.displayName ?? attrs.name;
79
76
  ref = attrs.ref;
80
- if (attrs.headerRowCount !== undefined)
81
- headerRowCount = Number(attrs.headerRowCount);
82
- if (attrs.totalsRowCount !== undefined)
83
- totalsRowCount = Number(attrs.totalsRowCount);
77
+ headerRowCount = numInteger(attrs.headerRowCount, 0) ?? headerRowCount;
78
+ totalsRowCount = numInteger(attrs.totalsRowCount, 0) ?? totalsRowCount;
84
79
  if (attrs.totalsRowShown !== undefined)
85
- totalsRowShown = parseOoxmlBool(attrs.totalsRowShown);
80
+ totalsRowShown = boolPresent(attrs.totalsRowShown);
86
81
  break;
87
82
  case 'autoFilter':
88
83
  hasAutoFilter = true;
@@ -92,13 +87,13 @@ export function parseTable(xml) {
92
87
  if (attrs.name !== undefined)
93
88
  captured.name = attrs.name;
94
89
  if (attrs.showFirstColumn !== undefined)
95
- captured.showFirstColumn = parseOoxmlBool(attrs.showFirstColumn);
90
+ captured.showFirstColumn = boolPresent(attrs.showFirstColumn);
96
91
  if (attrs.showLastColumn !== undefined)
97
- captured.showLastColumn = parseOoxmlBool(attrs.showLastColumn);
92
+ captured.showLastColumn = boolPresent(attrs.showLastColumn);
98
93
  if (attrs.showRowStripes !== undefined)
99
- captured.showRowStripes = parseOoxmlBool(attrs.showRowStripes);
94
+ captured.showRowStripes = boolPresent(attrs.showRowStripes);
100
95
  if (attrs.showColumnStripes !== undefined) {
101
- captured.showColumnStripes = parseOoxmlBool(attrs.showColumnStripes);
96
+ captured.showColumnStripes = boolPresent(attrs.showColumnStripes);
102
97
  }
103
98
  style = captured;
104
99
  break;
@@ -0,0 +1,37 @@
1
+ import { type ThemeColorScheme, type ThemeFontScheme, type ThemeOverrides } from '../../core/theme.ts';
2
+ /**
3
+ * Extract the colour scheme from a theme part. Returns only the slots the part actually declares in a
4
+ * colour model this reader understands; an unrecognised one is dropped rather than guessed at, so a
5
+ * caller can tell "the theme says nothing here" from "the theme says black".
6
+ *
7
+ * Reads the `<a:clrScheme>` block alone. A theme carries a font scheme and a format scheme too, but
8
+ * neither participates in resolving a colour, and scanning the whole part would let a `<a:srgbClr>`
9
+ * buried in a gradient stop masquerade as a scheme slot.
10
+ */
11
+ export declare function parseThemeColorScheme(themeXml: string): ThemeColorScheme;
12
+ /** Extract the major/minor latin typefaces from a theme part's `<a:fontScheme>`. */
13
+ export declare function parseThemeFontScheme(themeXml: string): ThemeFontScheme;
14
+ /**
15
+ * Apply authored colour/font overrides to a theme part, returning the new part text.
16
+ *
17
+ * Surgical by design: the base part rides through untouched except for the `<a:clrScheme>` and
18
+ * `<a:fontScheme>` blocks, and within those, only what the caller actually named. The format scheme,
19
+ * the gradients, line styles and effect styles that make a theme look like a theme, is left exactly
20
+ * as it was, because nobody hand-authors `fillStyleLst` gradient stops from a spreadsheet API and
21
+ * regenerating it would replace a designer's work with the Office default.
22
+ *
23
+ * A slot the caller did not override keeps its **verbatim source element**, not a re-serialisation of
24
+ * its value. That matters for `dk1`/`lt1`, which Excel writes as `<a:sysClr val="windowText"
25
+ * lastClr="000000"/>`: rewriting those as `<a:srgbClr>` would pin them to one machine's resolved
26
+ * window colours and break dark-mode following.
27
+ */
28
+ export declare function applyThemeOverrides(baseXml: string, overrides: ThemeOverrides): string;
29
+ /**
30
+ * The theme part a workbook with no theme of its own ships: the standard Office theme.
31
+ *
32
+ * A spreadsheet must carry one even when nobody configured it: the stylesheet's own default font
33
+ * references `theme="1"`, which a consumer can only resolve against this part, so the two travel
34
+ * together. It is also the base {@link applyThemeOverrides} authors on top of when a workbook was
35
+ * built from scratch rather than read from a file.
36
+ */
37
+ export declare const DEFAULT_THEME_XML: string;