@shbernal/ts-xlsx 1.2.0 → 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 (161) hide show
  1. package/README.md +5 -0
  2. package/dist/core/address.d.ts +49 -2
  3. package/dist/core/address.js +31 -3
  4. package/dist/core/autofilter.d.ts +2 -2
  5. package/dist/core/cell.d.ts +17 -16
  6. package/dist/core/cell.js +3 -7
  7. package/dist/core/color-resolution.d.ts +7 -7
  8. package/dist/core/column.d.ts +8 -9
  9. package/dist/core/column.js +2 -4
  10. package/dist/core/comment-thread.d.ts +32 -23
  11. package/dist/core/comment-thread.js +4 -4
  12. package/dist/core/conditional-formatting-overlay.d.ts +1 -1
  13. package/dist/core/conditional-formatting.d.ts +3 -3
  14. package/dist/core/data-validation-overlay.d.ts +1 -1
  15. package/dist/core/data-validation.d.ts +3 -3
  16. package/dist/core/date.d.ts +3 -3
  17. package/dist/core/formula.d.ts +6 -6
  18. package/dist/core/grid-edits.js +3 -3
  19. package/dist/core/image.d.ts +37 -2
  20. package/dist/core/image.js +6 -0
  21. package/dist/core/internal.d.ts +9 -1
  22. package/dist/core/limits.d.ts +4 -4
  23. package/dist/core/merge.d.ts +6 -12
  24. package/dist/core/merge.js +0 -3
  25. package/dist/core/page-setup.d.ts +4 -4
  26. package/dist/core/pivot-table.d.ts +2 -4
  27. package/dist/core/pivot-table.js +5 -5
  28. package/dist/core/preserved.d.ts +3 -3
  29. package/dist/core/protection.d.ts +4 -4
  30. package/dist/core/range.d.ts +16 -14
  31. package/dist/core/range.js +5 -5
  32. package/dist/core/row-input.d.ts +2 -8
  33. package/dist/core/row-input.js +2 -2
  34. package/dist/core/row.d.ts +7 -8
  35. package/dist/core/row.js +2 -4
  36. package/dist/core/style.d.ts +14 -14
  37. package/dist/core/table-style.d.ts +9 -9
  38. package/dist/core/table-style.js +1 -1
  39. package/dist/core/table.d.ts +30 -42
  40. package/dist/core/table.js +16 -10
  41. package/dist/core/theme.d.ts +13 -43
  42. package/dist/core/theme.js +2 -148
  43. package/dist/core/value.d.ts +7 -7
  44. package/dist/core/workbook-protection.d.ts +2 -2
  45. package/dist/core/workbook-styles.d.ts +26 -0
  46. package/dist/core/workbook-styles.js +48 -0
  47. package/dist/core/workbook-theme.d.ts +25 -0
  48. package/dist/core/workbook-theme.js +49 -0
  49. package/dist/core/workbook-vba.d.ts +17 -0
  50. package/dist/core/workbook-vba.js +79 -0
  51. package/dist/core/workbook.d.ts +141 -83
  52. package/dist/core/workbook.js +74 -133
  53. package/dist/core/worksheet-comments.d.ts +9 -0
  54. package/dist/core/worksheet-comments.js +52 -0
  55. package/dist/core/worksheet-model.d.ts +4 -4
  56. package/dist/core/worksheet-model.js +1 -0
  57. package/dist/core/worksheet-pictures.d.ts +29 -0
  58. package/dist/core/worksheet-pictures.js +42 -0
  59. package/dist/core/worksheet.d.ts +94 -77
  60. package/dist/core/worksheet.js +36 -95
  61. package/dist/customui/errors.d.ts +1 -1
  62. package/dist/customui/ribbon.d.ts +3 -3
  63. package/dist/entries/core.d.ts +4 -4
  64. package/dist/entries/core.js +1 -1
  65. package/dist/entries/vba.d.ts +1 -1
  66. package/dist/entries/vba.js +1 -1
  67. package/dist/entries/xlsx.d.ts +1 -0
  68. package/dist/entries/xlsx.js +1 -0
  69. package/dist/errors.d.ts +12 -12
  70. package/dist/io/csv/read.js +1 -2
  71. package/dist/io/csv/write.d.ts +8 -3
  72. package/dist/io/csv/write.js +17 -2
  73. package/dist/io/opc/errors.d.ts +6 -6
  74. package/dist/io/opc/inflate.d.ts +1 -1
  75. package/dist/io/opc/namespaces.d.ts +1 -1
  76. package/dist/io/opc/read-opc.d.ts +19 -3
  77. package/dist/io/opc/read-opc.js +14 -8
  78. package/dist/io/opc/read-options.d.ts +2 -2
  79. package/dist/io/opc/sniff-format.d.ts +2 -2
  80. package/dist/io/opc/zip-mtime.d.ts +5 -0
  81. package/dist/io/opc/zip-mtime.js +1 -0
  82. package/dist/io/style/xf-style.d.ts +38 -6
  83. package/dist/io/style/xf-style.js +22 -0
  84. package/dist/io/xlsb/errors.d.ts +1 -1
  85. package/dist/io/xlsb/formula.d.ts +4 -4
  86. package/dist/io/xlsb/primitives.d.ts +8 -8
  87. package/dist/io/xlsb/ptg-functions.d.ts +1 -1
  88. package/dist/io/xlsb/read-styles.js +2 -20
  89. package/dist/io/xlsb/read.d.ts +2 -2
  90. package/dist/io/xlsx/cell-accumulator.d.ts +25 -6
  91. package/dist/io/xlsx/cell-accumulator.js +111 -30
  92. package/dist/io/xlsx/cell-value.d.ts +2 -2
  93. package/dist/io/xlsx/cell-value.js +3 -3
  94. package/dist/io/xlsx/color-xml.js +11 -17
  95. package/dist/io/xlsx/comments.d.ts +5 -5
  96. package/dist/io/xlsx/comments.js +13 -6
  97. package/dist/io/xlsx/conditional-formatting.d.ts +3 -3
  98. package/dist/io/xlsx/conditional-formatting.js +7 -16
  99. package/dist/io/xlsx/data-validation.d.ts +2 -2
  100. package/dist/io/xlsx/data-validation.js +1 -1
  101. package/dist/io/xlsx/edit-vba.js +2 -1
  102. package/dist/io/xlsx/errors.d.ts +3 -3
  103. package/dist/io/xlsx/hyperlinks.d.ts +4 -2
  104. package/dist/io/xlsx/hyperlinks.js +5 -5
  105. package/dist/io/xlsx/images.d.ts +1 -1
  106. package/dist/io/xlsx/images.js +8 -8
  107. package/dist/io/xlsx/namespaces.d.ts +2 -2
  108. package/dist/io/xlsx/package-plan.js +5 -2
  109. package/dist/io/xlsx/read-pivot.js +2 -5
  110. package/dist/io/xlsx/read-rows.d.ts +13 -13
  111. package/dist/io/xlsx/read-rows.js +19 -55
  112. package/dist/io/xlsx/read-shared-strings.js +7 -5
  113. package/dist/io/xlsx/read-styles.d.ts +3 -3
  114. package/dist/io/xlsx/read-styles.js +40 -65
  115. package/dist/io/xlsx/read-worksheet.js +67 -115
  116. package/dist/io/xlsx/read.d.ts +3 -5
  117. package/dist/io/xlsx/read.js +94 -71
  118. package/dist/io/xlsx/rich-runs.d.ts +1 -1
  119. package/dist/io/xlsx/rich-runs.js +1 -1
  120. package/dist/io/xlsx/rich-text.d.ts +1 -1
  121. package/dist/io/xlsx/shared-strings.d.ts +2 -2
  122. package/dist/io/xlsx/sheet-properties.js +5 -4
  123. package/dist/io/xlsx/styles.d.ts +8 -9
  124. package/dist/io/xlsx/styles.js +4 -3
  125. package/dist/io/xlsx/tables.d.ts +2 -2
  126. package/dist/io/xlsx/tables.js +8 -13
  127. package/dist/io/xlsx/theme-xml.d.ts +37 -0
  128. package/dist/io/xlsx/theme-xml.js +142 -0
  129. package/dist/io/xlsx/threaded-comments.d.ts +18 -27
  130. package/dist/io/xlsx/threaded-comments.js +9 -21
  131. package/dist/io/xlsx/workbook-xml.d.ts +1 -1
  132. package/dist/io/xlsx/workbook-xml.js +6 -2
  133. package/dist/io/xlsx/worksheet-xml.d.ts +2 -2
  134. package/dist/io/xlsx/worksheet-xml.js +7 -12
  135. package/dist/io/xlsx/write-stream.d.ts +15 -15
  136. package/dist/io/xlsx/write-stream.js +5 -3
  137. package/dist/io/xlsx/write.d.ts +12 -7
  138. package/dist/io/xlsx/write.js +10 -6
  139. package/dist/io/xlsx/x14-ext.d.ts +1 -1
  140. package/dist/vba/bytes.d.ts +12 -0
  141. package/dist/vba/bytes.js +40 -0
  142. package/dist/vba/cfb-writer.d.ts +1 -1
  143. package/dist/vba/cfb.d.ts +1 -1
  144. package/dist/vba/cfb.js +15 -51
  145. package/dist/vba/codepage.d.ts +2 -2
  146. package/dist/vba/errors.d.ts +2 -2
  147. package/dist/vba/index.d.ts +2 -2
  148. package/dist/vba/index.js +2 -2
  149. package/dist/vba/ms-ovba.d.ts +1 -1
  150. package/dist/vba/ms-ovba.js +1 -3
  151. package/dist/vba/project-editor.d.ts +9 -9
  152. package/dist/vba/project-editor.js +2 -11
  153. package/dist/vba/project.d.ts +5 -5
  154. package/dist/vba/project.js +1 -10
  155. package/dist/vba/vba-encoding.d.ts +1 -1
  156. package/dist/xml/errors.d.ts +1 -1
  157. package/dist/xml/xml-read.d.ts +38 -9
  158. package/dist/xml/xml-read.js +23 -0
  159. package/dist/xml/xml.d.ts +31 -9
  160. package/dist/xml/xml.js +23 -3
  161. 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';
@@ -53,6 +54,9 @@ export function readXlsx(data, options = {}) {
53
54
  const core = partText('docProps/core.xml');
54
55
  if (core !== undefined)
55
56
  applyCoreProperties(workbook, core);
57
+ const app = partText('docProps/app.xml');
58
+ if (app !== undefined)
59
+ applyAppProperties(workbook, app);
56
60
  workbook.protection = parseWorkbookProtection(workbookXml);
57
61
  applyWorkbookView(workbook.view, workbookXml);
58
62
  readWorkbookPersons(workbookRelsXml, pkg, workbook);
@@ -67,9 +71,9 @@ export function readXlsx(data, options = {}) {
67
71
  if (sheetXml !== undefined)
68
72
  parseWorksheet(sheetXml, sheet, sharedStrings, xfStyles);
69
73
  if (path !== undefined) {
74
+ const sheetRels = readPartRelationships(path, partText);
70
75
  if (sheetXml !== undefined) {
71
- const sheetRels = parseRelationships(partText(relsPathFor(path)) ?? '');
72
- applyHyperlinks(sheet, parseSheetHyperlinks(sheetXml), sheetRels);
76
+ applyHyperlinks(sheet, parseSheetHyperlinks(sheetXml), (id) => sheetRels.byId(id)?.target);
73
77
  applyDataValidations(sheet, [
74
78
  ...parseDataValidations(sheetXml),
75
79
  ...parseExtendedDataValidations(sheetXml),
@@ -77,20 +81,20 @@ export function readXlsx(data, options = {}) {
77
81
  for (const cf of parseConditionalFormattings(sheetXml))
78
82
  sheet.addConditionalFormatting(cf);
79
83
  }
80
- const threads = readSheetCommentThreads(path, pkg, workbook);
84
+ const threads = readSheetCommentThreads(sheetRels, pkg, workbook);
81
85
  if (threads.length > 0)
82
86
  sheet[INTERNAL].restoreCommentThreads(threads);
83
- const comments = readSheetComments(path, pkg);
87
+ const comments = readSheetComments(sheetRels, pkg);
84
88
  if (comments !== undefined)
85
89
  applyNotes(sheet, comments);
86
- readSheetImages(path, pkg, workbook, sheet, imageIdByMediaPath);
87
- readSheetBackground(path, pkg, workbook, sheet, imageIdByMediaPath);
90
+ readSheetImages(sheetRels, pkg, workbook, sheet, imageIdByMediaPath);
91
+ readSheetBackground(sheetRels, pkg, workbook, sheet, imageIdByMediaPath);
88
92
  if (sheetXml !== undefined) {
89
- readSheetPreservedReferences(path, sheetXml, pkg, contentTypeOf, sheet);
93
+ readSheetPreservedReferences(sheetRels, sheetXml, pkg, contentTypeOf, sheet);
90
94
  }
91
- readSheetTables(path, pkg, sheet);
92
- readSheetPivotTables(path, pkg, sheet);
93
- const printerSettings = readSheetPrinterSettings(path, pkg);
95
+ readSheetTables(sheetRels, pkg, sheet);
96
+ readSheetPivotTables(sheetRels, pkg, sheet);
97
+ const printerSettings = readSheetPrinterSettings(sheetRels, pkg);
94
98
  if (printerSettings !== undefined)
95
99
  sheet.pageSetup.printerSettings = printerSettings;
96
100
  }
@@ -102,8 +106,8 @@ export function readXlsx(data, options = {}) {
102
106
  }
103
107
  return workbook;
104
108
  }
105
- function readSheetComments(sheetPath, pkg) {
106
- const commentsPath = sheetRelTarget(sheetPath, pkg.partText, 'comments');
109
+ function readSheetComments(sheetRels, pkg) {
110
+ const commentsPath = sheetRels.targetPath('comments');
107
111
  if (commentsPath === undefined)
108
112
  return undefined;
109
113
  const commentsXml = pkg.partText(commentsPath);
@@ -123,23 +127,25 @@ function readWorkbookTheme(workbookRelsXml, pkg, contentTypeOf, workbook) {
123
127
  return;
124
128
  const entryPath = resolveWorkbookPart(target);
125
129
  const parts = capturePartClosure(entryPath, pkg.partText, pkg.partBytes, contentTypeOf);
126
- if (parts !== undefined)
127
- 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) });
128
134
  }
129
- function readSheetCommentThreads(sheetPath, pkg, workbook) {
130
- const path = sheetRelTarget(sheetPath, pkg.partText, 'threadedComment');
135
+ function readSheetCommentThreads(sheetRels, pkg, workbook) {
136
+ const path = sheetRels.targetPath('threadedComment');
131
137
  const xml = path === undefined ? undefined : pkg.partText(path);
132
138
  if (xml === undefined)
133
139
  return [];
134
140
  return buildCommentThreads(parseThreadedComments(xml), (id) => workbook.getPerson(id));
135
141
  }
136
- function readSheetPrinterSettings(sheetPath, pkg) {
137
- const path = sheetRelTarget(sheetPath, pkg.partText, 'printerSettings');
142
+ function readSheetPrinterSettings(sheetRels, pkg) {
143
+ const path = sheetRels.targetPath('printerSettings');
138
144
  return path === undefined ? undefined : pkg.partBytes(path);
139
145
  }
140
- function readSheetImages(sheetPath, pkg, workbook, sheet, imageIdByMediaPath) {
146
+ function readSheetImages(sheetRels, pkg, workbook, sheet, imageIdByMediaPath) {
141
147
  const { partText, partBytes } = pkg;
142
- const drawingPath = sheetRelTarget(sheetPath, partText, 'drawing');
148
+ const drawingPath = sheetRels.targetPath('drawing');
143
149
  if (drawingPath === undefined)
144
150
  return;
145
151
  const drawingXml = partText(drawingPath);
@@ -147,12 +153,12 @@ function readSheetImages(sheetPath, pkg, workbook, sheet, imageIdByMediaPath) {
147
153
  return;
148
154
  if (drawingHasUnmodeledContent(drawingXml))
149
155
  return;
150
- const drawingRels = parseRelationships(partText(relsPathFor(drawingPath)) ?? '');
156
+ const drawingRels = readPartRelationships(drawingPath, partText);
151
157
  for (const anchor of parseDrawing(drawingXml)) {
152
- const target = drawingRels.get(anchor.embed);
153
- if (target === undefined)
158
+ const embedded = drawingRels.byId(anchor.embed);
159
+ if (embedded === undefined)
154
160
  continue;
155
- const mediaPath = resolveRelativePart(drawingPath, target);
161
+ const mediaPath = drawingRels.pathOf(embedded.target);
156
162
  let id = imageIdByMediaPath.get(mediaPath);
157
163
  if (id === undefined) {
158
164
  const bytes = partBytes(mediaPath);
@@ -171,8 +177,8 @@ function readSheetImages(sheetPath, pkg, workbook, sheet, imageIdByMediaPath) {
171
177
  }
172
178
  }
173
179
  }
174
- function readSheetBackground(sheetPath, pkg, workbook, sheet, imageIdByMediaPath) {
175
- const mediaPath = sheetRelTarget(sheetPath, pkg.partText, 'image');
180
+ function readSheetBackground(sheetRels, pkg, workbook, sheet, imageIdByMediaPath) {
181
+ const mediaPath = sheetRels.targetPath('image');
176
182
  if (mediaPath === undefined)
177
183
  return;
178
184
  let id = imageIdByMediaPath.get(mediaPath);
@@ -185,15 +191,10 @@ function readSheetBackground(sheetPath, pkg, workbook, sheet, imageIdByMediaPath
185
191
  }
186
192
  sheet.addBackgroundImage(id);
187
193
  }
188
- function readSheetPreservedReferences(sheetPath, sheetXml, pkg, contentTypeOf, sheet) {
194
+ function readSheetPreservedReferences(sheetRels, sheetXml, pkg, contentTypeOf, sheet) {
189
195
  const { partText, partBytes } = pkg;
190
- const relsXml = partText(relsPathFor(sheetPath));
191
- if (relsXml === undefined)
192
- return;
193
- const records = parseRelationshipRecords(relsXml);
194
- const recordById = new Map(records.map((record) => [record.id, record]));
195
196
  const capture = (element, relType, target) => {
196
- const entryPath = resolveRelativePart(sheetPath, target);
197
+ const entryPath = sheetRels.pathOf(target);
197
198
  const parts = capturePartClosure(entryPath, partText, partBytes, contentTypeOf);
198
199
  if (parts !== undefined)
199
200
  sheet[INTERNAL].addPreservedReference({ element, relType, entryPath, parts });
@@ -201,11 +202,11 @@ function readSheetPreservedReferences(sheetPath, sheetXml, pkg, contentTypeOf, s
201
202
  const referenceElements = sheet.images.length === 0 ? ['drawing', 'legacyDrawingHF'] : ['legacyDrawingHF'];
202
203
  for (const element of referenceElements) {
203
204
  const relId = worksheetReferenceRelId(sheetXml, element);
204
- const record = relId === undefined ? undefined : recordById.get(relId);
205
+ const record = relId === undefined ? undefined : sheetRels.byId(relId);
205
206
  if (record !== undefined && !record.external)
206
207
  capture(element, record.type, record.target);
207
208
  }
208
- for (const record of records) {
209
+ for (const record of sheetRels.records) {
209
210
  if (record.external)
210
211
  continue;
211
212
  if (isPreservedSheetRelType(record.type))
@@ -291,12 +292,9 @@ function worksheetReferenceRelId(sheetXml, element) {
291
292
  }
292
293
  return undefined;
293
294
  }
294
- function readSheetTables(sheetPath, pkg, sheet) {
295
- const relsXml = pkg.partText(relsPathFor(sheetPath));
296
- if (relsXml === undefined)
297
- return;
298
- for (const target of relationshipTargetsByType(relsXml, 'table')) {
299
- 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);
300
298
  if (tableXml === undefined)
301
299
  continue;
302
300
  const options = parseTable(tableXml);
@@ -305,20 +303,14 @@ function readSheetTables(sheetPath, pkg, sheet) {
305
303
  }
306
304
  dropMergesInsideTables(sheet);
307
305
  }
308
- function readSheetPivotTables(sheetPath, pkg, sheet) {
306
+ function readSheetPivotTables(sheetRels, pkg, sheet) {
309
307
  const { partText } = pkg;
310
- const relsXml = partText(relsPathFor(sheetPath));
311
- if (relsXml === undefined)
312
- return;
313
- for (const target of relationshipTargetsByType(relsXml, 'pivotTable')) {
314
- const tablePath = resolveRelativePart(sheetPath, target);
308
+ for (const tablePath of sheetRels.targetPaths('pivotTable')) {
315
309
  const tableXml = partText(tablePath);
316
310
  if (tableXml === undefined)
317
311
  continue;
318
- const cacheTarget = relationshipTargetByType(partText(relsPathFor(tablePath)) ?? '', 'pivotCacheDefinition');
319
- const cacheXml = cacheTarget === undefined
320
- ? ''
321
- : (partText(resolveRelativePart(tablePath, cacheTarget)) ?? '');
312
+ const cachePath = readPartRelationships(tablePath, partText).targetPath('pivotCacheDefinition');
313
+ const cacheXml = cachePath === undefined ? '' : (partText(cachePath) ?? '');
322
314
  sheet[INTERNAL].addLoadedPivotTable(parsePivotTable(tableXml, cacheXml));
323
315
  }
324
316
  }
@@ -351,7 +343,7 @@ export function parseWorkbookSheets(xml) {
351
343
  }
352
344
  return sheets;
353
345
  }
354
- export function parseWorkbookProtection(xml) {
346
+ function parseWorkbookProtection(xml) {
355
347
  let result;
356
348
  parseXml(xml, {
357
349
  onOpen(name, attrs) {
@@ -379,11 +371,21 @@ export function parseWorkbookProtection(xml) {
379
371
  }
380
372
  export function applyWorkbookView(view, xml) {
381
373
  for (const { attrs } of openElements(xml, 'workbookView')) {
382
- applyViewNumber(attrs.xWindow, (value) => (view.x = value));
383
- applyViewNumber(attrs.yWindow, (value) => (view.y = value));
384
- applyViewNumber(attrs.windowWidth, (value) => (view.width = value));
385
- applyViewNumber(attrs.windowHeight, (value) => (view.height = value));
386
- 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;
387
389
  if (attrs.visibility === 'hidden' || attrs.visibility === 'veryHidden') {
388
390
  view.visibility = attrs.visibility;
389
391
  }
@@ -392,13 +394,6 @@ export function applyWorkbookView(view, xml) {
392
394
  return;
393
395
  }
394
396
  }
395
- function applyViewNumber(raw, assign) {
396
- if (raw === undefined)
397
- return;
398
- const value = Number(raw);
399
- if (Number.isFinite(value))
400
- assign(Math.trunc(value));
401
- }
402
397
  function parseWorkbookDefinedNames(xml, sheetOrder) {
403
398
  const names = [];
404
399
  let capture = false;
@@ -412,7 +407,7 @@ function parseWorkbookDefinedNames(xml, sheetOrder) {
412
407
  return;
413
408
  capture = true;
414
409
  refersTo = '';
415
- const scopeIndex = attrs.localSheetId === undefined ? -1 : Number(attrs.localSheetId);
410
+ const scopeIndex = numInteger(attrs.localSheetId, 0) ?? -1;
416
411
  const scope = sheetOrder[scopeIndex];
417
412
  pending = { name: attrs.name };
418
413
  if (scope !== undefined)
@@ -436,7 +431,13 @@ function parseWorkbookDefinedNames(xml, sheetOrder) {
436
431
  });
437
432
  return names;
438
433
  }
439
- const CORE_PROPERTY_LOCAL_NAMES = new Set(['creator', 'lastModifiedBy', 'created', 'modified']);
434
+ const CORE_PROPERTY_LOCAL_NAMES = new Set([
435
+ 'title',
436
+ 'creator',
437
+ 'lastModifiedBy',
438
+ 'created',
439
+ 'modified',
440
+ ]);
440
441
  function applyCoreProperties(workbook, xml) {
441
442
  let capture = '';
442
443
  let text = '';
@@ -453,7 +454,9 @@ function applyCoreProperties(workbook, xml) {
453
454
  onClose(name) {
454
455
  if (capture === '' || localName(name) !== capture)
455
456
  return;
456
- if (capture === 'creator')
457
+ if (capture === 'title')
458
+ workbook.properties.title = text;
459
+ else if (capture === 'creator')
457
460
  workbook.properties.creator = text;
458
461
  else if (capture === 'lastModifiedBy')
459
462
  workbook.properties.lastModifiedBy = text;
@@ -470,3 +473,23 @@ function applyCoreProperties(workbook, xml) {
470
473
  },
471
474
  });
472
475
  }
476
+ function applyAppProperties(workbook, xml) {
477
+ let capture = false;
478
+ let text = '';
479
+ parseXml(xml, {
480
+ onOpen(name) {
481
+ capture = localName(name) === 'Company';
482
+ text = '';
483
+ },
484
+ onText(chunk) {
485
+ if (capture)
486
+ text += chunk;
487
+ },
488
+ onClose(name) {
489
+ if (!capture || localName(name) !== 'Company')
490
+ return;
491
+ workbook.properties.company = text;
492
+ capture = false;
493
+ },
494
+ });
495
+ }
@@ -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,13 +1,14 @@
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"' : '';
7
+ const gridLines = view.showGridLines === false ? ' showGridLines="0"' : '';
7
8
  const xSplit = view.xSplit ?? 0;
8
9
  const ySplit = view.ySplit ?? 0;
9
10
  if (view.state !== 'frozen' || (xSplit === 0 && ySplit === 0)) {
10
- return `<sheetViews><sheetView${selected} workbookViewId="0"/></sheetViews>`;
11
+ return `<sheetViews><sheetView${gridLines}${selected} workbookViewId="0"/></sheetViews>`;
11
12
  }
12
13
  const topLeftCell = view.topLeftCell ?? encodeAddress(xSplit + 1, ySplit + 1);
13
14
  const activePane = xSplit > 0 && ySplit > 0 ? 'bottomRight' : xSplit > 0 ? 'topRight' : 'bottomLeft';
@@ -16,7 +17,7 @@ export function sheetViewsXml(view, active) {
16
17
  (ySplit > 0 ? ` ySplit="${ySplit}"` : '') +
17
18
  ` topLeftCell="${escapeAttr(topLeftCell)}" activePane="${activePane}" state="frozen"/>`;
18
19
  const selection = `<selection pane="${activePane}" activeCell="${escapeAttr(topLeftCell)}" sqref="${escapeAttr(topLeftCell)}"/>`;
19
- return `<sheetViews><sheetView${selected} workbookViewId="0">${pane}${selection}</sheetView></sheetViews>`;
20
+ return `<sheetViews><sheetView${gridLines}${selected} workbookViewId="0">${pane}${selection}</sheetView></sheetViews>`;
20
21
  }
21
22
  export function sheetPrXml(sheet) {
22
23
  const children = (sheet.tabColor !== undefined ? `<tabColor ${colorAttrs(sheet.tabColor)}/>` : '') +
@@ -98,7 +99,7 @@ export function headerFooterXml(hf) {
98
99
  if (differentFirst)
99
100
  attrs += ' differentFirst="1"';
100
101
  const body = children
101
- .map(({ tag, key }) => `<${tag}>${escapeText(hf[key])}</${tag}>`)
102
+ .map(({ tag, key }) => `<${tag}>${escapeSpreadsheetText(hf[key])}</${tag}>`)
102
103
  .join('');
103
104
  return `<headerFooter${attrs}>${body}</headerFooter>`;
104
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;