@shbernal/ts-xlsx 2.0.0 → 3.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 (147) 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 +26 -4
  5. package/dist/core/address.js +21 -9
  6. package/dist/core/autofilter.d.ts +12 -1
  7. package/dist/core/autofilter.js +35 -12
  8. package/dist/core/axis-handle.d.ts +8 -0
  9. package/dist/core/axis-handle.js +14 -0
  10. package/dist/core/cell.d.ts +24 -0
  11. package/dist/core/cell.js +9 -1
  12. package/dist/core/color-resolution.d.ts +1 -1
  13. package/dist/core/color-resolution.js +7 -8
  14. package/dist/core/column.d.ts +4 -1
  15. package/dist/core/column.js +31 -35
  16. package/dist/core/conditional-formatting-overlay.d.ts +5 -0
  17. package/dist/core/conditional-formatting-overlay.js +11 -0
  18. package/dist/core/conditional-formatting.d.ts +45 -5
  19. package/dist/core/conditional-formatting.js +74 -0
  20. package/dist/core/data-validation-overlay.d.ts +11 -0
  21. package/dist/core/data-validation-overlay.js +21 -2
  22. package/dist/core/data-validation.d.ts +9 -2
  23. package/dist/core/data-validation.js +26 -0
  24. package/dist/core/grid-edits.d.ts +16 -0
  25. package/dist/core/grid-edits.js +26 -8
  26. package/dist/core/grid-shift.d.ts +19 -0
  27. package/dist/core/grid-shift.js +8 -0
  28. package/dist/core/image.d.ts +2 -0
  29. package/dist/core/image.js +2 -0
  30. package/dist/core/merge-index.d.ts +31 -0
  31. package/dist/core/merge-index.js +60 -0
  32. package/dist/core/merge.d.ts +12 -11
  33. package/dist/core/merge.js +36 -10
  34. package/dist/core/page-setup.d.ts +86 -2
  35. package/dist/core/page-setup.js +34 -1
  36. package/dist/core/pivot-table.d.ts +2 -0
  37. package/dist/core/pivot-table.js +23 -18
  38. package/dist/core/protection.d.ts +3 -0
  39. package/dist/core/protection.js +20 -14
  40. package/dist/core/row-input.d.ts +7 -0
  41. package/dist/core/row-input.js +10 -8
  42. package/dist/core/row.d.ts +4 -1
  43. package/dist/core/row.js +19 -23
  44. package/dist/core/style.d.ts +78 -7
  45. package/dist/core/style.js +90 -80
  46. package/dist/core/table.d.ts +26 -12
  47. package/dist/core/table.js +67 -33
  48. package/dist/core/theme.d.ts +3 -1
  49. package/dist/core/theme.js +4 -0
  50. package/dist/core/used-extent.d.ts +44 -0
  51. package/dist/core/used-extent.js +102 -0
  52. package/dist/core/workbook.d.ts +2 -2
  53. package/dist/core/worksheet-comments.d.ts +8 -0
  54. package/dist/core/worksheet-comments.js +18 -0
  55. package/dist/core/worksheet.d.ts +31 -7
  56. package/dist/core/worksheet.js +72 -86
  57. package/dist/customui/ribbon.js +23 -24
  58. package/dist/entries/core.d.ts +3 -3
  59. package/dist/entries/csv.d.ts +1 -1
  60. package/dist/entries/node-unavailable.d.ts +15 -0
  61. package/dist/entries/node-unavailable.js +20 -0
  62. package/dist/entries/node.d.ts +1 -0
  63. package/dist/entries/node.js +1 -0
  64. package/dist/entries/xlsx.d.ts +0 -1
  65. package/dist/entries/xlsx.js +0 -1
  66. package/dist/io/csv/read.js +1 -1
  67. package/dist/io/csv/write.d.ts +12 -2
  68. package/dist/io/csv/write.js +17 -2
  69. package/dist/io/opc/inflate.js +3 -13
  70. package/dist/io/opc/part-paths.d.ts +1 -0
  71. package/dist/io/opc/part-paths.js +15 -0
  72. package/dist/io/opc/read-opc.d.ts +22 -3
  73. package/dist/io/opc/read-opc.js +11 -39
  74. package/dist/io/opc/rels.d.ts +12 -6
  75. package/dist/io/opc/rels.js +3 -5
  76. package/dist/io/style/xf-style.d.ts +33 -4
  77. package/dist/io/style/xf-style.js +12 -1
  78. package/dist/io/xlsb/read-styles.js +6 -3
  79. package/dist/io/xlsb/read.d.ts +1 -1
  80. package/dist/io/xlsb/read.js +5 -8
  81. package/dist/io/xlsx/cell-accumulator.d.ts +2 -4
  82. package/dist/io/xlsx/cell-accumulator.js +20 -58
  83. package/dist/io/xlsx/cell-value.d.ts +1 -1
  84. package/dist/io/xlsx/cell-value.js +7 -4
  85. package/dist/io/xlsx/color-xml.d.ts +1 -1
  86. package/dist/io/xlsx/color-xml.js +8 -7
  87. package/dist/io/xlsx/comments.js +6 -3
  88. package/dist/io/xlsx/conditional-formatting.d.ts +4 -3
  89. package/dist/io/xlsx/conditional-formatting.js +81 -65
  90. package/dist/io/xlsx/data-validation.d.ts +10 -8
  91. package/dist/io/xlsx/data-validation.js +44 -41
  92. package/dist/io/xlsx/edit-vba.js +4 -11
  93. package/dist/io/xlsx/hyperlinks.d.ts +4 -2
  94. package/dist/io/xlsx/hyperlinks.js +26 -31
  95. package/dist/io/xlsx/images.js +33 -35
  96. package/dist/io/xlsx/package-plan.d.ts +9 -1
  97. package/dist/io/xlsx/package-plan.js +29 -16
  98. package/dist/io/xlsx/part-names.d.ts +41 -0
  99. package/dist/io/xlsx/part-names.js +46 -0
  100. package/dist/io/xlsx/read-pivot.js +4 -9
  101. package/dist/io/xlsx/read-rows.js +12 -13
  102. package/dist/io/xlsx/read-shared-strings.js +9 -47
  103. package/dist/io/xlsx/read-styles.d.ts +1 -1
  104. package/dist/io/xlsx/read-styles.js +65 -43
  105. package/dist/io/xlsx/read-worksheet.d.ts +10 -1
  106. package/dist/io/xlsx/read-worksheet.js +48 -63
  107. package/dist/io/xlsx/read.d.ts +3 -4
  108. package/dist/io/xlsx/read.js +124 -146
  109. package/dist/io/xlsx/rich-runs.d.ts +41 -7
  110. package/dist/io/xlsx/rich-runs.js +95 -30
  111. package/dist/io/xlsx/sheet-properties.d.ts +1 -1
  112. package/dist/io/xlsx/sheet-properties.js +23 -26
  113. package/dist/io/xlsx/styles.d.ts +3 -15
  114. package/dist/io/xlsx/styles.js +83 -105
  115. package/dist/io/xlsx/tables.js +14 -13
  116. package/dist/io/xlsx/theme-xml.js +8 -5
  117. package/dist/io/xlsx/threaded-comments.js +20 -19
  118. package/dist/io/xlsx/workbook-xml.js +43 -36
  119. package/dist/io/xlsx/worksheet-xml.d.ts +1 -1
  120. package/dist/io/xlsx/worksheet-xml.js +47 -52
  121. package/dist/io/xlsx/write-stream.d.ts +14 -1
  122. package/dist/io/xlsx/write-stream.js +40 -33
  123. package/dist/io/xlsx/write.d.ts +5 -0
  124. package/dist/io/xlsx/write.js +155 -131
  125. package/dist/sha512.d.ts +2 -0
  126. package/dist/sha512.js +146 -0
  127. package/dist/token-set.d.ts +15 -0
  128. package/dist/token-set.js +4 -0
  129. package/dist/vba/bytes.d.ts +1 -2
  130. package/dist/vba/bytes.js +1 -12
  131. package/dist/vba/cfb-format.d.ts +24 -0
  132. package/dist/vba/cfb-format.js +12 -0
  133. package/dist/vba/cfb-writer.js +17 -17
  134. package/dist/vba/cfb.js +1 -7
  135. package/dist/vba/dir-records.d.ts +53 -0
  136. package/dist/vba/dir-records.js +28 -0
  137. package/dist/vba/ms-ovba.js +32 -11
  138. package/dist/vba/project-editor.js +5 -34
  139. package/dist/vba/project.js +15 -31
  140. package/dist/vba/vba-encoding.js +1 -1
  141. package/dist/xml/xml-read.d.ts +117 -72
  142. package/dist/xml/xml-read.js +123 -169
  143. package/dist/xml/xml-scan.d.ts +100 -0
  144. package/dist/xml/xml-scan.js +208 -0
  145. package/dist/xml/xml.d.ts +44 -2
  146. package/dist/xml/xml.js +17 -5
  147. package/package.json +22 -6
@@ -3,51 +3,45 @@ import { unmangleFunctions } from '../../core/formula.js';
3
3
  import { INTERNAL } from '../../core/internal.js';
4
4
  import { WORKBOOK_PROTECTION_CREDENTIAL_ATTRS, } from '../../core/workbook-protection.js';
5
5
  import { Workbook } from '../../core/workbook.js';
6
- import { boolStrict, localName, numInteger, openElements, parseXml } from '../../xml/xml-read.js';
6
+ import { isVisibility } from '../../core/worksheet.js';
7
+ import { capturedText, openElements, parseXml, parseXmlPasses } from '../../xml/xml-read.js';
8
+ import { boolStrict, enumToken, localName, numInteger } from '../../xml/xml-scan.js';
7
9
  import { UnsupportedFormatError } from '../opc/errors.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
- import { DEFAULT_MAX_UNCOMPRESSED } from '../opc/read-options.js';
11
- import { inflateSpreadsheetPackage } from '../opc/sniff-format.js';
10
+ import { extensionOf, resolveRelativePart } from '../opc/part-paths.js';
11
+ import { capturePartClosure, contentTypeResolver, openSpreadsheetPackage, parseRelationshipRecords, readPartRelationships, } from '../opc/read-opc.js';
12
12
  import { readXlsbPackage, XLSB_WORKBOOK_PART } from '../xlsb/read.js';
13
13
  import { applyNotes, parseComments } from './comments.js';
14
- import { parseConditionalFormattings, parseDxfs } from './conditional-formatting.js';
15
- import { applyDataValidations, parseDataValidations, parseExtendedDataValidations, } from './data-validation.js';
16
- import { applyHyperlinks, parseSheetHyperlinks } from './hyperlinks.js';
14
+ import { conditionalFormattingPass } from './conditional-formatting.js';
15
+ import { applyDataValidations, dataValidationPass, extendedDataValidationPass, } from './data-validation.js';
16
+ import { applyHyperlinks, sheetHyperlinkPass } from './hyperlinks.js';
17
17
  import { drawingHasUnmodeledContent, parseDrawing } from './images.js';
18
18
  import { parsePivotTable } from './read-pivot.js';
19
19
  import { parseSharedStrings } from './read-shared-strings.js';
20
- import { parseIndexedColors, parseMruColors, parseStyleTable, parseTableStyles, } from './read-styles.js';
21
- import { parseWorksheet } from './read-worksheet.js';
20
+ import { parseStyleTable } from './read-styles.js';
21
+ import { worksheetPass } from './read-worksheet.js';
22
22
  import { parseTable } from './tables.js';
23
23
  import { parseThemeColorScheme, parseThemeFontScheme } from './theme-xml.js';
24
24
  import { buildCommentThreads, parsePersons, parseThreadedComments } from './threaded-comments.js';
25
- export { parseRelationships, resolveWorkbookPart } from '../opc/read-opc.js';
26
- export { DEFAULT_MAX_UNCOMPRESSED } from '../opc/read-options.js';
27
25
  export { parseStyleTable } from './read-styles.js';
28
26
  export function readXlsx(data, options = {}) {
29
- const cap = options.maxUncompressedBytes ?? DEFAULT_MAX_UNCOMPRESSED;
30
- const files = inflateSpreadsheetPackage(data, cap);
31
- const pkg = packageAccessors(files);
27
+ const { files, pkg, workbookXml } = openSpreadsheetPackage(data, options.maxUncompressedBytes);
32
28
  const { partText } = pkg;
33
- const workbookXml = partText('xl/workbook.xml');
34
29
  if (workbookXml === undefined) {
35
30
  if (files[XLSB_WORKBOOK_PART] !== undefined)
36
31
  return readXlsbPackage(files);
37
32
  throw new UnsupportedFormatError('unknown');
38
33
  }
39
34
  const contentTypeOf = contentTypeResolver(partText('[Content_Types].xml') ?? '');
40
- const workbookRelsXml = partText('xl/_rels/workbook.xml.rels') ?? '';
41
- const rels = parseRelationships(workbookRelsXml);
35
+ const workbookRels = readPartRelationships('xl/workbook.xml', partText);
42
36
  const sharedStrings = parseSharedStrings(partText('xl/sharedStrings.xml') ?? '');
43
37
  const stylesXml = partText('xl/styles.xml') ?? '';
44
- const { cellXfs: xfStyles, namedStyles, defaultFont } = parseStyleTable(stylesXml);
38
+ const { cellXfs: xfStyles, namedStyles, defaultFont, preserved } = parseStyleTable(stylesXml);
45
39
  const workbook = new Workbook();
46
- workbook[INTERNAL].restoreDifferentialStyles(parseDxfs(stylesXml));
47
- workbook[INTERNAL].restoreIndexedColors(parseIndexedColors(stylesXml));
48
- workbook[INTERNAL].restoreMruColors(parseMruColors(stylesXml));
49
- workbook[INTERNAL].restoreTableStyles(parseTableStyles(stylesXml));
50
- readWorkbookTheme(workbookRelsXml, pkg, contentTypeOf, workbook);
40
+ workbook[INTERNAL].restoreDifferentialStyles([...preserved.dxfs]);
41
+ workbook[INTERNAL].restoreIndexedColors([...preserved.indexedColors]);
42
+ workbook[INTERNAL].restoreMruColors([...preserved.mruColors]);
43
+ workbook[INTERNAL].restoreTableStyles(preserved.tableStyles);
44
+ readWorkbookTheme(workbookRels, pkg, contentTypeOf, workbook);
51
45
  if (namedStyles.length > 1)
52
46
  workbook[INTERNAL].restoreNamedStyles(namedStyles);
53
47
  workbook[INTERNAL].restoreDefaultFont(defaultFont);
@@ -59,53 +53,72 @@ export function readXlsx(data, options = {}) {
59
53
  applyAppProperties(workbook, app);
60
54
  workbook.protection = parseWorkbookProtection(workbookXml);
61
55
  applyWorkbookView(workbook.view, workbookXml);
62
- readWorkbookPersons(workbookRelsXml, pkg, workbook);
63
- const imageIdByMediaPath = new Map();
56
+ readWorkbookPersons(workbookRels, pkg, workbook);
57
+ const context = {
58
+ pkg,
59
+ workbook,
60
+ contentTypeOf,
61
+ sharedStrings,
62
+ xfStyles,
63
+ imageIdByMediaPath: new Map(),
64
+ };
64
65
  const sheetOrder = [];
65
66
  for (const { name, relId, state } of parseWorkbookSheets(workbookXml)) {
66
- const target = rels.get(relId);
67
+ const target = workbookRels.byId(relId)?.target;
67
68
  const sheet = workbook.addWorksheet(name, state === undefined ? undefined : { state });
68
69
  sheetOrder.push(name);
69
- const path = target === undefined ? undefined : resolveWorkbookPart(target);
70
- const sheetXml = path === undefined ? undefined : partText(path);
71
- if (sheetXml !== undefined)
72
- parseWorksheet(sheetXml, sheet, sharedStrings, xfStyles);
73
- if (path !== undefined) {
74
- const sheetRels = readPartRelationships(path, partText);
75
- if (sheetXml !== undefined) {
76
- applyHyperlinks(sheet, parseSheetHyperlinks(sheetXml), (id) => sheetRels.byId(id)?.target);
77
- applyDataValidations(sheet, [
78
- ...parseDataValidations(sheetXml),
79
- ...parseExtendedDataValidations(sheetXml),
80
- ]);
81
- for (const cf of parseConditionalFormattings(sheetXml))
82
- sheet.addConditionalFormatting(cf);
83
- }
84
- const threads = readSheetCommentThreads(sheetRels, pkg, workbook);
85
- if (threads.length > 0)
86
- sheet[INTERNAL].restoreCommentThreads(threads);
87
- const comments = readSheetComments(sheetRels, pkg);
88
- if (comments !== undefined)
89
- applyNotes(sheet, comments);
90
- readSheetImages(sheetRels, pkg, workbook, sheet, imageIdByMediaPath);
91
- readSheetBackground(sheetRels, pkg, workbook, sheet, imageIdByMediaPath);
92
- if (sheetXml !== undefined) {
93
- readSheetPreservedReferences(sheetRels, sheetXml, pkg, contentTypeOf, sheet);
94
- }
95
- readSheetTables(sheetRels, pkg, sheet);
96
- readSheetPivotTables(sheetRels, pkg, sheet);
97
- const printerSettings = readSheetPrinterSettings(sheetRels, pkg);
98
- if (printerSettings !== undefined)
99
- sheet.pageSetup.printerSettings = printerSettings;
100
- }
70
+ readSheet(sheet, target === undefined ? undefined : workbookRels.pathOf(target), context);
101
71
  }
102
- readWorkbookPreservedReferences(workbookXml, pkg, contentTypeOf, workbook);
72
+ readWorkbookPreservedReferences(workbookXml, workbookRels, pkg, contentTypeOf, workbook);
103
73
  readRootPreservedReferences(pkg, contentTypeOf, workbook);
104
74
  for (const name of parseWorkbookDefinedNames(workbookXml, sheetOrder)) {
105
75
  workbook.defineName(name);
106
76
  }
107
77
  return workbook;
108
78
  }
79
+ function readSheet(sheet, path, context) {
80
+ const { pkg, workbook, contentTypeOf, sharedStrings, xfStyles, imageIdByMediaPath } = context;
81
+ const { partText } = pkg;
82
+ const sheetXml = path === undefined ? undefined : partText(path);
83
+ const hyperlinks = sheetHyperlinkPass();
84
+ const validations = dataValidationPass();
85
+ const extendedValidations = extendedDataValidationPass();
86
+ const formattings = conditionalFormattingPass();
87
+ if (sheetXml !== undefined) {
88
+ parseXmlPasses(sheetXml, [
89
+ worksheetPass(sheet, sharedStrings, xfStyles),
90
+ hyperlinks,
91
+ validations,
92
+ extendedValidations,
93
+ formattings,
94
+ ]);
95
+ }
96
+ if (path === undefined)
97
+ return;
98
+ const sheetRels = readPartRelationships(path, partText);
99
+ if (sheetXml !== undefined) {
100
+ applyHyperlinks(sheet, hyperlinks.result(), (id) => sheetRels.byId(id)?.target);
101
+ applyDataValidations(sheet, [...validations.result(), ...extendedValidations.result()]);
102
+ for (const cf of formattings.result())
103
+ sheet.addConditionalFormatting(cf);
104
+ }
105
+ const threads = readSheetCommentThreads(sheetRels, pkg, workbook);
106
+ if (threads.length > 0)
107
+ sheet[INTERNAL].restoreCommentThreads(threads);
108
+ const comments = readSheetComments(sheetRels, pkg);
109
+ if (comments !== undefined)
110
+ applyNotes(sheet, comments);
111
+ readSheetImages(sheetRels, pkg, workbook, sheet, imageIdByMediaPath);
112
+ readSheetBackground(sheetRels, pkg, workbook, sheet, imageIdByMediaPath);
113
+ if (sheetXml !== undefined) {
114
+ readSheetPreservedReferences(sheetRels, sheetXml, pkg, contentTypeOf, sheet);
115
+ }
116
+ readSheetTables(sheetRels, pkg, sheet);
117
+ readSheetPivotTables(sheetRels, pkg, sheet);
118
+ const printerSettings = readSheetPrinterSettings(sheetRels, pkg);
119
+ if (printerSettings !== undefined)
120
+ sheet.pageSetup.printerSettings = printerSettings;
121
+ }
109
122
  function readSheetComments(sheetRels, pkg) {
110
123
  const commentsPath = sheetRels.targetPath('comments');
111
124
  if (commentsPath === undefined)
@@ -115,17 +128,16 @@ function readSheetComments(sheetRels, pkg) {
115
128
  return undefined;
116
129
  return parseComments(commentsXml);
117
130
  }
118
- function readWorkbookPersons(workbookRelsXml, pkg, workbook) {
119
- const target = relationshipTargetByType(workbookRelsXml, 'person');
120
- const xml = target === undefined ? undefined : pkg.partText(resolveWorkbookPart(target));
131
+ function readWorkbookPersons(workbookRels, pkg, workbook) {
132
+ const path = workbookRels.targetPath('person');
133
+ const xml = path === undefined ? undefined : pkg.partText(path);
121
134
  if (xml !== undefined)
122
135
  workbook[INTERNAL].restorePersons(parsePersons(xml));
123
136
  }
124
- function readWorkbookTheme(workbookRelsXml, pkg, contentTypeOf, workbook) {
125
- const target = relationshipTargetByType(workbookRelsXml, 'theme');
126
- if (target === undefined)
137
+ function readWorkbookTheme(workbookRels, pkg, contentTypeOf, workbook) {
138
+ const entryPath = workbookRels.targetPath('theme');
139
+ if (entryPath === undefined)
127
140
  return;
128
- const entryPath = resolveWorkbookPart(target);
129
141
  const parts = capturePartClosure(entryPath, pkg.partText, pkg.partBytes, contentTypeOf);
130
142
  if (parts === undefined)
131
143
  return;
@@ -143,8 +155,19 @@ function readSheetPrinterSettings(sheetRels, pkg) {
143
155
  const path = sheetRels.targetPath('printerSettings');
144
156
  return path === undefined ? undefined : pkg.partBytes(path);
145
157
  }
158
+ function internImage(workbook, pkg, imageIdByMediaPath, mediaPath) {
159
+ const known = imageIdByMediaPath.get(mediaPath);
160
+ if (known !== undefined)
161
+ return known;
162
+ const bytes = pkg.partBytes(mediaPath);
163
+ if (bytes === undefined)
164
+ return undefined;
165
+ const id = workbook.addImage({ buffer: bytes, extension: extensionOf(mediaPath) });
166
+ imageIdByMediaPath.set(mediaPath, id);
167
+ return id;
168
+ }
146
169
  function readSheetImages(sheetRels, pkg, workbook, sheet, imageIdByMediaPath) {
147
- const { partText, partBytes } = pkg;
170
+ const { partText } = pkg;
148
171
  const drawingPath = sheetRels.targetPath('drawing');
149
172
  if (drawingPath === undefined)
150
173
  return;
@@ -159,14 +182,9 @@ function readSheetImages(sheetRels, pkg, workbook, sheet, imageIdByMediaPath) {
159
182
  if (embedded === undefined)
160
183
  continue;
161
184
  const mediaPath = drawingRels.pathOf(embedded.target);
162
- let id = imageIdByMediaPath.get(mediaPath);
163
- if (id === undefined) {
164
- const bytes = partBytes(mediaPath);
165
- if (bytes === undefined)
166
- continue;
167
- id = workbook.addImage({ buffer: bytes, extension: extensionOf(mediaPath) });
168
- imageIdByMediaPath.set(mediaPath, id);
169
- }
185
+ const id = internImage(workbook, pkg, imageIdByMediaPath, mediaPath);
186
+ if (id === undefined)
187
+ continue;
170
188
  const rot = anchor.rotation !== undefined ? { rotation: anchor.rotation } : {};
171
189
  if (anchor.to !== undefined) {
172
190
  const mode = anchor.editAs !== undefined ? { editAs: anchor.editAs } : {};
@@ -181,15 +199,9 @@ function readSheetBackground(sheetRels, pkg, workbook, sheet, imageIdByMediaPath
181
199
  const mediaPath = sheetRels.targetPath('image');
182
200
  if (mediaPath === undefined)
183
201
  return;
184
- let id = imageIdByMediaPath.get(mediaPath);
185
- if (id === undefined) {
186
- const bytes = pkg.partBytes(mediaPath);
187
- if (bytes === undefined)
188
- return;
189
- id = workbook.addImage({ buffer: bytes, extension: extensionOf(mediaPath) });
190
- imageIdByMediaPath.set(mediaPath, id);
191
- }
192
- sheet.addBackgroundImage(id);
202
+ const id = internImage(workbook, pkg, imageIdByMediaPath, mediaPath);
203
+ if (id !== undefined)
204
+ sheet.addBackgroundImage(id);
193
205
  }
194
206
  function readSheetPreservedReferences(sheetRels, sheetXml, pkg, contentTypeOf, sheet) {
195
207
  const { partText, partBytes } = pkg;
@@ -216,17 +228,14 @@ function readSheetPreservedReferences(sheetRels, sheetXml, pkg, contentTypeOf, s
216
228
  function isPreservedSheetRelType(type) {
217
229
  return type.endsWith('/pivotTable') || type.endsWith('/slicer');
218
230
  }
219
- function readWorkbookPreservedReferences(workbookXml, pkg, contentTypeOf, workbook) {
231
+ function readWorkbookPreservedReferences(workbookXml, workbookRels, pkg, contentTypeOf, workbook) {
220
232
  const { partText, partBytes } = pkg;
221
- const relsXml = partText('xl/_rels/workbook.xml.rels');
222
- if (relsXml === undefined)
223
- return;
224
233
  const cacheIdByRelId = parsePivotCacheRegistrations(workbookXml);
225
234
  const externalIndexByRelId = parseExternalReferenceRegistrations(workbookXml);
226
- for (const record of parseRelationshipRecords(relsXml)) {
235
+ for (const record of workbookRels.records) {
227
236
  if (record.external || !isPreservedWorkbookRelType(record.type))
228
237
  continue;
229
- const entryPath = resolveWorkbookPart(record.target);
238
+ const entryPath = workbookRels.pathOf(record.target);
230
239
  const parts = capturePartClosure(entryPath, partText, partBytes, contentTypeOf);
231
240
  if (parts === undefined)
232
241
  continue;
@@ -337,8 +346,9 @@ export function parseWorkbookSheets(xml) {
337
346
  name: attrs.name ?? '',
338
347
  relId: attrs['r:id'] ?? '',
339
348
  };
340
- if (attrs.state === 'hidden' || attrs.state === 'veryHidden')
341
- entry.state = attrs.state;
349
+ const state = enumToken(attrs.state, isVisibility);
350
+ if (state !== undefined && state !== 'visible')
351
+ entry.state = state;
342
352
  sheets.push(entry);
343
353
  }
344
354
  return sheets;
@@ -386,9 +396,9 @@ export function applyWorkbookView(view, xml) {
386
396
  const activeTab = numInteger(attrs.activeTab, 0);
387
397
  if (activeTab !== undefined)
388
398
  view.activeTab = activeTab;
389
- if (attrs.visibility === 'hidden' || attrs.visibility === 'veryHidden') {
390
- view.visibility = attrs.visibility;
391
- }
399
+ const visibility = enumToken(attrs.visibility, isVisibility);
400
+ if (visibility !== undefined && visibility !== 'visible')
401
+ view.visibility = visibility;
392
402
  if (boolStrict(attrs.minimized))
393
403
  view.minimized = true;
394
404
  return;
@@ -439,57 +449,25 @@ const CORE_PROPERTY_LOCAL_NAMES = new Set([
439
449
  'modified',
440
450
  ]);
441
451
  function applyCoreProperties(workbook, xml) {
442
- let capture = '';
443
- let text = '';
444
- parseXml(xml, {
445
- onOpen(name) {
446
- const local = localName(name);
447
- capture = CORE_PROPERTY_LOCAL_NAMES.has(local) ? local : '';
448
- text = '';
449
- },
450
- onText(chunk) {
451
- if (capture !== '')
452
- text += chunk;
453
- },
454
- onClose(name) {
455
- if (capture === '' || localName(name) !== capture)
456
- return;
457
- if (capture === 'title')
458
- workbook.properties.title = text;
459
- else if (capture === 'creator')
460
- workbook.properties.creator = text;
461
- else if (capture === 'lastModifiedBy')
462
- workbook.properties.lastModifiedBy = text;
463
- else {
464
- const date = new Date(text);
465
- if (!Number.isNaN(date.getTime())) {
466
- if (capture === 'created')
467
- workbook.properties.created = date;
468
- else
469
- workbook.properties.modified = date;
470
- }
452
+ for (const { local, text } of capturedText(xml, CORE_PROPERTY_LOCAL_NAMES)) {
453
+ if (local === 'title')
454
+ workbook.properties.title = text;
455
+ else if (local === 'creator')
456
+ workbook.properties.creator = text;
457
+ else if (local === 'lastModifiedBy')
458
+ workbook.properties.lastModifiedBy = text;
459
+ else {
460
+ const date = new Date(text);
461
+ if (!Number.isNaN(date.getTime())) {
462
+ if (local === 'created')
463
+ workbook.properties.created = date;
464
+ else
465
+ workbook.properties.modified = date;
471
466
  }
472
- capture = '';
473
- },
474
- });
467
+ }
468
+ }
475
469
  }
476
470
  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
- });
471
+ for (const { text } of capturedText(xml, 'Company'))
472
+ workbook.properties.company = text;
495
473
  }
@@ -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-scan.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
- beginContainer(): 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,108 @@
1
+ import { TextCapture } from '../../xml/xml-read.js';
2
+ import { decodeSpreadsheetText } from '../../xml/xml-scan.js';
1
3
  import { applyFontChild } from './read-styles.js';
2
4
  export class RunAccumulator {
5
+ #container;
6
+ #readRuns;
7
+ #capture = new TextCapture('t');
3
8
  #runs = [];
4
9
  #font = null;
5
- #text = '';
10
+ #inProperties = false;
11
+ #runText = '';
12
+ #plain = '';
6
13
  #inRun = false;
7
- beginContainer() {
8
- this.#runs = [];
9
- this.#font = null;
10
- this.#inRun = false;
14
+ #isRich = false;
15
+ #inContainer = false;
16
+ constructor(options) {
17
+ this.#container = options.container;
18
+ this.#readRuns = options.readRuns;
11
19
  }
12
20
  get runs() {
13
21
  return this.#runs;
14
22
  }
15
- beginRun() {
16
- this.#inRun = true;
23
+ get isRich() {
24
+ return this.#isRich;
25
+ }
26
+ get plainText() {
27
+ return this.#plain;
28
+ }
29
+ beginContainer() {
30
+ this.#runs = [];
17
31
  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);
32
+ this.#inProperties = false;
33
+ this.#runText = '';
34
+ this.#plain = '';
41
35
  this.#inRun = false;
36
+ this.#isRich = false;
37
+ }
38
+ open(local, attrs, selfClosing) {
39
+ switch (local) {
40
+ case this.#container:
41
+ this.#inContainer = true;
42
+ this.beginContainer();
43
+ return true;
44
+ case 'r':
45
+ if (!this.#readRuns || !this.#inContainer)
46
+ return false;
47
+ this.#isRich = true;
48
+ this.#inRun = true;
49
+ this.#font = null;
50
+ this.#inProperties = false;
51
+ this.#runText = '';
52
+ return true;
53
+ case 'rPr':
54
+ if (!this.#readRuns || !this.#inRun)
55
+ return false;
56
+ this.#font = {};
57
+ this.#inProperties = true;
58
+ return true;
59
+ case 't':
60
+ this.#capture.open(local, selfClosing);
61
+ return true;
62
+ default:
63
+ if (!this.#inProperties || this.#font === null)
64
+ return false;
65
+ applyFontChild(this.#font, local, attrs);
66
+ return true;
67
+ }
68
+ }
69
+ text(chunk) {
70
+ this.#capture.text(chunk);
71
+ }
72
+ close(local) {
73
+ switch (local) {
74
+ case 't': {
75
+ const text = this.#capture.close(local);
76
+ if (text !== undefined) {
77
+ const decoded = decodeSpreadsheetText(text);
78
+ if (this.#inRun)
79
+ this.#runText += decoded;
80
+ else if (this.#inContainer)
81
+ this.#plain += decoded;
82
+ }
83
+ return 'claimed';
84
+ }
85
+ case 'rPr':
86
+ if (!this.#readRuns || !this.#inProperties)
87
+ return 'other';
88
+ this.#inProperties = false;
89
+ return 'claimed';
90
+ case 'r': {
91
+ if (!this.#readRuns || !this.#inRun)
92
+ return 'other';
93
+ this.#inProperties = false;
94
+ const run = { text: this.#runText };
95
+ if (this.#font !== null && Object.keys(this.#font).length > 0)
96
+ run.font = this.#font;
97
+ this.#runs.push(run);
98
+ this.#inRun = false;
99
+ return 'claimed';
100
+ }
101
+ case this.#container:
102
+ this.#inContainer = false;
103
+ return 'container';
104
+ default:
105
+ return 'other';
106
+ }
42
107
  }
43
108
  }
@@ -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;