@shbernal/ts-xlsx 3.0.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (213) hide show
  1. package/README.md +4 -1
  2. package/dist/bytes.d.ts +7 -0
  3. package/dist/bytes.js +22 -0
  4. package/dist/core/address.d.ts +60 -2
  5. package/dist/core/address.js +40 -24
  6. package/dist/core/autofilter.d.ts +4 -3
  7. package/dist/core/autofilter.js +31 -30
  8. package/dist/core/axis-handle.d.ts +2 -0
  9. package/dist/core/axis-handle.js +5 -2
  10. package/dist/core/cell.d.ts +4 -3
  11. package/dist/core/cell.js +4 -6
  12. package/dist/core/clone.d.ts +29 -0
  13. package/dist/core/clone.js +24 -0
  14. package/dist/core/color-resolution.js +2 -4
  15. package/dist/core/column.d.ts +1 -0
  16. package/dist/core/column.js +3 -0
  17. package/dist/core/comment-thread.js +2 -1
  18. package/dist/core/conditional-formatting-overlay.d.ts +2 -1
  19. package/dist/core/conditional-formatting-overlay.js +2 -2
  20. package/dist/core/conditional-formatting.d.ts +9 -1
  21. package/dist/core/conditional-formatting.js +34 -18
  22. package/dist/core/containers.d.ts +12 -0
  23. package/dist/core/containers.js +5 -0
  24. package/dist/core/data-validation-overlay.d.ts +2 -1
  25. package/dist/core/data-validation-overlay.js +4 -4
  26. package/dist/core/data-validation.d.ts +6 -0
  27. package/dist/core/data-validation.js +15 -4
  28. package/dist/core/date-format.d.ts +20 -0
  29. package/dist/core/date-format.js +160 -0
  30. package/dist/core/date.d.ts +43 -8
  31. package/dist/core/date.js +18 -2
  32. package/dist/core/formula.d.ts +26 -0
  33. package/dist/core/formula.js +20 -4
  34. package/dist/core/grid-edits.d.ts +2 -3
  35. package/dist/core/grid-edits.js +69 -70
  36. package/dist/core/grid-shift.d.ts +58 -9
  37. package/dist/core/grid-shift.js +26 -4
  38. package/dist/core/image.d.ts +13 -9
  39. package/dist/core/image.js +7 -5
  40. package/dist/core/internal.d.ts +8 -2
  41. package/dist/core/limits.d.ts +23 -0
  42. package/dist/core/limits.js +4 -0
  43. package/dist/core/merge.d.ts +5 -4
  44. package/dist/core/merge.js +18 -19
  45. package/dist/core/pivot-table.js +63 -45
  46. package/dist/core/protection.js +2 -11
  47. package/dist/core/range.js +34 -30
  48. package/dist/core/row-input.js +2 -2
  49. package/dist/core/row.d.ts +1 -0
  50. package/dist/core/row.js +3 -0
  51. package/dist/core/style.d.ts +85 -70
  52. package/dist/core/style.js +92 -28
  53. package/dist/core/table-style.d.ts +1 -1
  54. package/dist/core/table-style.js +4 -4
  55. package/dist/core/table.d.ts +27 -9
  56. package/dist/core/table.js +51 -34
  57. package/dist/core/theme.js +6 -5
  58. package/dist/core/value.d.ts +9 -0
  59. package/dist/core/value.js +36 -34
  60. package/dist/core/workbook-media.d.ts +34 -0
  61. package/dist/core/workbook-media.js +44 -0
  62. package/dist/core/workbook-styles.d.ts +67 -1
  63. package/dist/core/workbook-vba.js +3 -2
  64. package/dist/core/workbook.d.ts +23 -2
  65. package/dist/core/workbook.js +24 -40
  66. package/dist/core/worksheet-comments.d.ts +2 -1
  67. package/dist/core/worksheet-comments.js +20 -13
  68. package/dist/core/worksheet-merges.d.ts +44 -0
  69. package/dist/core/worksheet-merges.js +59 -0
  70. package/dist/core/worksheet-model.d.ts +9 -1
  71. package/dist/core/worksheet-model.js +24 -9
  72. package/dist/core/worksheet.d.ts +55 -6
  73. package/dist/core/worksheet.js +36 -44
  74. package/dist/customui/ribbon.js +2 -1
  75. package/dist/entries/core.d.ts +18 -13
  76. package/dist/entries/core.js +12 -6
  77. package/dist/entries/xlsx.d.ts +1 -1
  78. package/dist/errors.d.ts +64 -0
  79. package/dist/errors.js +22 -0
  80. package/dist/hex.d.ts +8 -0
  81. package/dist/hex.js +3 -0
  82. package/dist/io/csv/delimiter.d.ts +9 -0
  83. package/dist/io/csv/delimiter.js +6 -0
  84. package/dist/io/csv/read.js +11 -4
  85. package/dist/io/csv/write.d.ts +9 -2
  86. package/dist/io/csv/write.js +9 -38
  87. package/dist/io/opc/inflate.js +14 -0
  88. package/dist/io/opc/namespaces.d.ts +16 -0
  89. package/dist/io/opc/namespaces.js +4 -0
  90. package/dist/io/opc/part-paths.d.ts +9 -0
  91. package/dist/io/opc/part-paths.js +1 -1
  92. package/dist/io/opc/read-opc.d.ts +54 -14
  93. package/dist/io/opc/read-opc.js +42 -20
  94. package/dist/io/opc/read-options.d.ts +5 -1
  95. package/dist/io/opc/sniff-format.d.ts +3 -3
  96. package/dist/io/opc/sniff-format.js +2 -2
  97. package/dist/io/style/xf-style.d.ts +23 -1
  98. package/dist/io/style/xf-style.js +8 -0
  99. package/dist/io/xlsb/formula.d.ts +2 -2
  100. package/dist/io/xlsb/formula.js +49 -34
  101. package/dist/io/xlsb/primitives.js +8 -14
  102. package/dist/io/xlsb/ptg-functions.d.ts +19 -10
  103. package/dist/io/xlsb/ptg-functions.js +385 -596
  104. package/dist/io/xlsb/read-styles.js +45 -81
  105. package/dist/io/xlsb/read-worksheet.d.ts +20 -1
  106. package/dist/io/xlsb/read-worksheet.js +73 -89
  107. package/dist/io/xlsb/read.d.ts +6 -3
  108. package/dist/io/xlsb/read.js +44 -23
  109. package/dist/io/xlsb/record-stream.d.ts +30 -0
  110. package/dist/io/xlsb/record-stream.js +20 -0
  111. package/dist/io/xlsb/record-types.d.ts +1 -5
  112. package/dist/io/xlsb/record-types.js +1 -5
  113. package/dist/io/xlsx/cell-accumulator.d.ts +8 -1
  114. package/dist/io/xlsx/cell-accumulator.js +32 -9
  115. package/dist/io/xlsx/cell-style-resolution.d.ts +25 -0
  116. package/dist/io/xlsx/cell-style-resolution.js +29 -0
  117. package/dist/io/xlsx/cell-value.d.ts +5 -3
  118. package/dist/io/xlsx/cell-value.js +13 -15
  119. package/dist/io/xlsx/color-xml.js +3 -2
  120. package/dist/io/xlsx/column-budget.d.ts +26 -0
  121. package/dist/io/xlsx/column-budget.js +22 -0
  122. package/dist/io/xlsx/comments.d.ts +6 -2
  123. package/dist/io/xlsx/comments.js +36 -32
  124. package/dist/io/xlsx/conditional-formatting.d.ts +6 -3
  125. package/dist/io/xlsx/conditional-formatting.js +30 -26
  126. package/dist/io/xlsx/data-validation.js +29 -27
  127. package/dist/io/xlsx/edit-vba.d.ts +7 -2
  128. package/dist/io/xlsx/edit-vba.js +14 -21
  129. package/dist/io/xlsx/font-xml.d.ts +7 -0
  130. package/dist/io/xlsx/font-xml.js +104 -0
  131. package/dist/io/xlsx/hyperlinks.d.ts +37 -5
  132. package/dist/io/xlsx/hyperlinks.js +22 -13
  133. package/dist/io/xlsx/images.js +11 -13
  134. package/dist/io/xlsx/namespaces.d.ts +16 -0
  135. package/dist/io/xlsx/namespaces.js +7 -0
  136. package/dist/io/xlsx/package-plan.d.ts +14 -2
  137. package/dist/io/xlsx/package-plan.js +39 -11
  138. package/dist/io/xlsx/pivot.js +8 -7
  139. package/dist/io/xlsx/read-parts.d.ts +59 -0
  140. package/dist/io/xlsx/read-parts.js +252 -0
  141. package/dist/io/xlsx/read-pivot.js +2 -1
  142. package/dist/io/xlsx/read-repair.d.ts +26 -0
  143. package/dist/io/xlsx/read-repair.js +39 -0
  144. package/dist/io/xlsx/{rich-runs.js → read-rich-runs.js} +6 -3
  145. package/dist/io/xlsx/read-rows.d.ts +18 -5
  146. package/dist/io/xlsx/read-rows.js +45 -31
  147. package/dist/io/xlsx/read-shared-strings.js +3 -2
  148. package/dist/io/xlsx/read-styles.d.ts +1 -6
  149. package/dist/io/xlsx/read-styles.js +6 -71
  150. package/dist/io/xlsx/read-workbook-xml.d.ts +36 -0
  151. package/dist/io/xlsx/read-workbook-xml.js +170 -0
  152. package/dist/io/xlsx/read-worksheet.d.ts +2 -1
  153. package/dist/io/xlsx/read-worksheet.js +35 -164
  154. package/dist/io/xlsx/read.d.ts +5 -12
  155. package/dist/io/xlsx/read.js +73 -406
  156. package/dist/io/xlsx/rich-text.js +1 -1
  157. package/dist/io/xlsx/row-position.d.ts +18 -0
  158. package/dist/io/xlsx/row-position.js +11 -0
  159. package/dist/io/xlsx/row-xml.d.ts +128 -0
  160. package/dist/io/xlsx/row-xml.js +203 -0
  161. package/dist/io/xlsx/shared-formulas.js +2 -2
  162. package/dist/io/xlsx/sheet-properties.d.ts +10 -1
  163. package/dist/io/xlsx/sheet-properties.js +145 -9
  164. package/dist/io/xlsx/style-elements.d.ts +43 -0
  165. package/dist/io/xlsx/style-elements.js +156 -0
  166. package/dist/io/xlsx/styles.d.ts +12 -3
  167. package/dist/io/xlsx/styles.js +5 -207
  168. package/dist/io/xlsx/tables.js +10 -14
  169. package/dist/io/xlsx/theme-xml.d.ts +26 -4
  170. package/dist/io/xlsx/theme-xml.js +107 -69
  171. package/dist/io/xlsx/threaded-comments.d.ts +8 -10
  172. package/dist/io/xlsx/threaded-comments.js +2 -1
  173. package/dist/io/xlsx/workbook-xml.d.ts +54 -4
  174. package/dist/io/xlsx/workbook-xml.js +60 -34
  175. package/dist/io/xlsx/worksheet-xml.d.ts +23 -70
  176. package/dist/io/xlsx/worksheet-xml.js +48 -229
  177. package/dist/io/xlsx/write-stream.d.ts +65 -16
  178. package/dist/io/xlsx/write-stream.js +50 -20
  179. package/dist/io/xlsx/write.d.ts +1 -2
  180. package/dist/io/xlsx/write.js +114 -74
  181. package/dist/rel-type.d.ts +16 -0
  182. package/dist/rel-type.js +6 -0
  183. package/dist/token-set.d.ts +13 -0
  184. package/dist/token-set.js +4 -1
  185. package/dist/vba/bytes.d.ts +14 -5
  186. package/dist/vba/bytes.js +16 -6
  187. package/dist/vba/cfb-format.d.ts +8 -0
  188. package/dist/vba/cfb-format.js +1 -0
  189. package/dist/vba/cfb-writer.d.ts +9 -0
  190. package/dist/vba/cfb-writer.js +12 -9
  191. package/dist/vba/cfb.d.ts +13 -1
  192. package/dist/vba/cfb.js +73 -51
  193. package/dist/vba/codepage.js +2 -1
  194. package/dist/vba/errors.d.ts +7 -0
  195. package/dist/vba/ms-ovba.js +51 -16
  196. package/dist/vba/project-editor.js +35 -40
  197. package/dist/vba/project.d.ts +11 -1
  198. package/dist/vba/project.js +27 -8
  199. package/dist/vba/vba-encoding.js +4 -5
  200. package/dist/xml/xml-attrs.d.ts +37 -0
  201. package/dist/xml/xml-attrs.js +30 -0
  202. package/dist/xml/xml-chars.d.ts +40 -0
  203. package/dist/xml/xml-chars.js +12 -0
  204. package/dist/xml/xml-namespaces.d.ts +31 -0
  205. package/dist/xml/xml-namespaces.js +50 -0
  206. package/dist/xml/xml-read.d.ts +72 -10
  207. package/dist/xml/xml-read.js +94 -18
  208. package/dist/xml/xml-scan.d.ts +15 -37
  209. package/dist/xml/xml-scan.js +12 -40
  210. package/dist/xml/xml.d.ts +28 -27
  211. package/dist/xml/xml.js +24 -24
  212. package/package.json +12 -7
  213. /package/dist/io/xlsx/{rich-runs.d.ts → read-rich-runs.d.ts} +0 -0
@@ -1,9 +1,12 @@
1
1
  import { isDataValidationErrorStyle, isDataValidationOperator, isDataValidationType, } from '../../core/data-validation.js';
2
+ import { stripFormulaEquals } from '../../core/formula.js';
2
3
  import { decodeSqrefRects } from '../../core/merge.js';
4
+ import { coerceNumericLiteral } from '../../xml/xml-attrs.js';
3
5
  import { TextCapture } from '../../xml/xml-read.js';
4
- import { boolStrict, coerceNumericLiteral, localName } from '../../xml/xml-scan.js';
5
- import { checkedToken, escapeAttr, escapeText, stripFormulaEquals, textAttr } from '../../xml/xml.js';
6
- import { DATA_VALIDATION_EXT_URI, XM_NS } from './namespaces.js';
6
+ import { boolStrict, localName } from '../../xml/xml-scan.js';
7
+ import { checkedToken, escapeAttr, escapeText, textAttr } from '../../xml/xml.js';
8
+ import { DATA_VALIDATION_EXT_URI, isExtensionElement, isMainNamespaceElement, XM_NS, } from './namespaces.js';
9
+ import { admitting } from './read-repair.js';
7
10
  import { x14Ext } from './x14-ext.js';
8
11
  const TYPED = new Set(['whole', 'decimal', 'date', 'time', 'textLength']);
9
12
  export function dataValidationsXml(entries) {
@@ -58,34 +61,30 @@ function extendedDataValidationXml(sqref, rule) {
58
61
  `<xm:sqref>${escapeText(sqref)}</xm:sqref>`;
59
62
  return `<x14:dataValidation${ruleAttrs(rule)}>${body}</x14:dataValidation>`;
60
63
  }
64
+ const FORMULA_ELEMENTS = new Set(['formula1', 'formula2']);
61
65
  export function dataValidationPass() {
62
66
  const entries = [];
63
67
  let current;
64
- let slot;
68
+ const formula = new TextCapture(FORMULA_ELEMENTS);
65
69
  const handlers = {
66
- onOpen(name, attrs) {
70
+ onOpen(name, attrs, selfClosing, scope) {
67
71
  const ln = localName(name);
68
- if (ln === 'dataValidation' && !name.includes(':')) {
72
+ if (ln === 'dataValidation' && isMainNamespaceElement(scope, name)) {
69
73
  current = { attrs, formulae: [] };
70
74
  }
71
- else if (current !== undefined && ln === 'formula1') {
72
- slot = 0;
73
- current.formulae[0] = '';
74
- }
75
- else if (current !== undefined && ln === 'formula2') {
76
- slot = 1;
77
- current.formulae[1] = '';
75
+ else if (current !== undefined) {
76
+ formula.open(ln, selfClosing);
78
77
  }
79
78
  },
80
79
  onText(text) {
81
- if (current !== undefined && slot !== undefined) {
82
- current.formulae[slot] = (current.formulae[slot] ?? '') + text;
83
- }
80
+ formula.text(text);
84
81
  },
85
82
  onClose(name) {
86
83
  const ln = localName(name);
87
- if (ln === 'formula1' || ln === 'formula2') {
88
- slot = undefined;
84
+ const text = formula.close(ln);
85
+ if (text !== undefined) {
86
+ if (current !== undefined)
87
+ current.formulae[ln === 'formula1' ? 0 : 1] = text;
89
88
  return;
90
89
  }
91
90
  if (ln === 'dataValidation' && current !== undefined) {
@@ -146,16 +145,16 @@ export function extendedDataValidationPass() {
146
145
  let slot;
147
146
  const capture = new TextCapture(['f', 'sqref']);
148
147
  const handlers = {
149
- onOpen(name, attrs, selfClosing) {
148
+ onOpen(name, attrs, selfClosing, scope) {
150
149
  const ln = localName(name);
151
- const prefixed = name.includes(':');
152
- if (ln === 'dataValidation' && prefixed) {
150
+ const extension = isExtensionElement(scope, name);
151
+ if (ln === 'dataValidation' && extension) {
153
152
  current = { attrs, formulae: [], sqref: '' };
154
153
  }
155
- else if (current !== undefined && prefixed && ln === 'formula1') {
154
+ else if (current !== undefined && extension && ln === 'formula1') {
156
155
  slot = 0;
157
156
  }
158
- else if (current !== undefined && prefixed && ln === 'formula2') {
157
+ else if (current !== undefined && extension && ln === 'formula2') {
159
158
  slot = 1;
160
159
  }
161
160
  else if (current !== undefined) {
@@ -165,8 +164,9 @@ export function extendedDataValidationPass() {
165
164
  onText(chunk) {
166
165
  capture.text(chunk);
167
166
  },
168
- onClose(name) {
167
+ onClose(name, scope) {
169
168
  const ln = localName(name);
169
+ const extension = isExtensionElement(scope, name);
170
170
  const text = capture.close(ln);
171
171
  if (text !== undefined) {
172
172
  if (ln === 'f') {
@@ -177,10 +177,10 @@ export function extendedDataValidationPass() {
177
177
  current.sqref = text;
178
178
  }
179
179
  }
180
- else if ((ln === 'formula1' || ln === 'formula2') && name.includes(':')) {
180
+ else if ((ln === 'formula1' || ln === 'formula2') && extension) {
181
181
  slot = undefined;
182
182
  }
183
- else if (ln === 'dataValidation' && name.includes(':') && current !== undefined) {
183
+ else if (ln === 'dataValidation' && extension && current !== undefined) {
184
184
  const built = buildExtendedEntry(current.attrs, current.formulae, current.sqref);
185
185
  if (built !== undefined)
186
186
  entries.push(built);
@@ -200,6 +200,8 @@ export function applyDataValidations(sheet, entries) {
200
200
  for (const { sqref, rule, extended } of entries) {
201
201
  if (decodeSqrefRects(sqref).length === 0)
202
202
  continue;
203
- sheet.addDataValidation(sqref, rule, extended ? { extended: true } : {});
203
+ admitting(() => {
204
+ sheet.addDataValidation(sqref, rule, extended ? { extended: true } : {});
205
+ });
204
206
  }
205
207
  }
@@ -1,4 +1,5 @@
1
1
  import { type VbaLibraryReference } from '../../vba/project-editor.ts';
2
+ import { type ReadPackageOptions } from '../opc/read-options.ts';
2
3
  /**
3
4
  * Remove a standard module from an existing macro-enabled package's VBA project, returning new package
4
5
  * bytes. Every part but `xl/vbaProject.bin` is preserved byte-for-byte (see {@link removeVbaModule} for
@@ -8,8 +9,10 @@ import { type VbaLibraryReference } from '../../vba/project-editor.ts';
8
9
  * @throws {VbaAuthorError} if the package carries no VBA project, `name` is not in the project, or names
9
10
  * a `document`/`designer` module.
10
11
  * @throws {VbaParseError} if the attached `vbaProject.bin` is malformed.
12
+ * @throws {PackageReadError} if the input is not a readable ZIP, or exceeds the inflate bound
13
+ * ({@link ReadPackageOptions.maxUncompressedBytes}, defaulting as `readXlsx` does).
11
14
  */
12
- export declare function editXlsxVbaRemoveModule(xlsx: Uint8Array, name: string): Uint8Array;
15
+ export declare function editXlsxVbaRemoveModule(xlsx: Uint8Array, name: string, options?: ReadPackageOptions): Uint8Array;
13
16
  /**
14
17
  * Add a registered (COM type-library) reference to an existing macro-enabled package's VBA project,
15
18
  * returning new package bytes. Every part but `xl/vbaProject.bin` is preserved byte-for-byte (see
@@ -19,5 +22,7 @@ export declare function editXlsxVbaRemoveModule(xlsx: Uint8Array, name: string):
19
22
  * @throws {VbaAuthorError} if the package carries no VBA project, or any field of `ref` is invalid (see
20
23
  * {@link VbaLibraryReference}).
21
24
  * @throws {VbaParseError} if the attached `vbaProject.bin` is malformed.
25
+ * @throws {PackageReadError} if the input is not a readable ZIP, or exceeds the inflate bound
26
+ * ({@link ReadPackageOptions.maxUncompressedBytes}, defaulting as `readXlsx` does).
22
27
  */
23
- export declare function editXlsxVbaAddReference(xlsx: Uint8Array, ref: VbaLibraryReference): Uint8Array;
28
+ export declare function editXlsxVbaAddReference(xlsx: Uint8Array, ref: VbaLibraryReference, options?: ReadPackageOptions): Uint8Array;
@@ -1,20 +1,22 @@
1
- import { strFromU8, strToU8, unzipSync, zipSync } from 'fflate';
1
+ import { strFromU8, strToU8, zipSync } from 'fflate';
2
2
  import { VbaAuthorError } from '../../vba/errors.js';
3
3
  import { addVbaReference, removeVbaModule, } from '../../vba/project-editor.js';
4
4
  import { relsPathFor, resolveRelativePart } from '../opc/part-paths.js';
5
- import { parseRelationshipRecords, relationshipTargetByType } from '../opc/read-opc.js';
5
+ import { parseRelationshipRecords, readPartRelationships } from '../opc/read-opc.js';
6
+ import { DEFAULT_MAX_UNCOMPRESSED } from '../opc/read-options.js';
7
+ import { inflateSpreadsheetPackage } from '../opc/sniff-format.js';
6
8
  import { FIXED_ENTRY_MTIME } from '../opc/zip-mtime.js';
7
9
  const OFFICE_DOCUMENT_REL = 'officeDocument';
8
10
  const VBA_PROJECT_REL = 'vbaProject';
9
11
  const VBA_SIGNATURE_REL_INFIX = 'vbaProjectSignature';
10
- export function editXlsxVbaRemoveModule(xlsx, name) {
11
- return applyToVbaProjectPart(xlsx, (bin) => removeVbaModule(bin, name));
12
+ export function editXlsxVbaRemoveModule(xlsx, name, options = {}) {
13
+ return applyToVbaProjectPart(xlsx, options, (bin) => removeVbaModule(bin, name));
12
14
  }
13
- export function editXlsxVbaAddReference(xlsx, ref) {
14
- return applyToVbaProjectPart(xlsx, (bin) => addVbaReference(bin, ref));
15
+ export function editXlsxVbaAddReference(xlsx, ref, options = {}) {
16
+ return applyToVbaProjectPart(xlsx, options, (bin) => addVbaReference(bin, ref));
15
17
  }
16
- function applyToVbaProjectPart(xlsx, apply) {
17
- const files = Object.assign(Object.create(null), unzipSync(xlsx));
18
+ function applyToVbaProjectPart(xlsx, options, apply) {
19
+ const files = inflateSpreadsheetPackage(xlsx, options.maxUncompressedBytes ?? DEFAULT_MAX_UNCOMPRESSED);
18
20
  const binPath = locateVbaProjectPart(files);
19
21
  const bin = binPath === undefined ? undefined : files[binPath];
20
22
  if (binPath === undefined || bin === undefined) {
@@ -25,20 +27,11 @@ function applyToVbaProjectPart(xlsx, apply) {
25
27
  return zipSync(files, { mtime: FIXED_ENTRY_MTIME });
26
28
  }
27
29
  function locateVbaProjectPart(files) {
28
- const rootRels = textPart(files, '_rels/.rels');
29
- if (rootRels === undefined)
30
+ const partText = (path) => textPart(files, path);
31
+ const workbookPath = readPartRelationships('', partText).targetPath(OFFICE_DOCUMENT_REL);
32
+ if (workbookPath === undefined)
30
33
  return undefined;
31
- const workbookTarget = relationshipTargetByType(rootRels, OFFICE_DOCUMENT_REL);
32
- if (workbookTarget === undefined)
33
- return undefined;
34
- const workbookPath = resolveRelativePart('', workbookTarget);
35
- const workbookRels = textPart(files, relsPathFor(workbookPath));
36
- if (workbookRels === undefined)
37
- return undefined;
38
- const vbaTarget = relationshipTargetByType(workbookRels, VBA_PROJECT_REL);
39
- if (vbaTarget === undefined)
40
- return undefined;
41
- return resolveRelativePart(workbookPath, vbaTarget);
34
+ return readPartRelationships(workbookPath, partText).targetPath(VBA_PROJECT_REL);
42
35
  }
43
36
  function dropStaleSignature(files, binPath) {
44
37
  const binRelsPath = relsPathFor(binPath);
@@ -0,0 +1,7 @@
1
+ import { type Font } from '../../core/style.ts';
2
+ import { type XmlAttributes } from '../../xml/xml-scan.ts';
3
+ export type FontDraft = {
4
+ -readonly [K in keyof Font]?: Font[K];
5
+ };
6
+ export declare function applyFontChild(draft: FontDraft, local: string, attrs: XmlAttributes): void;
7
+ export declare function fontXml(font: Font, nameTag?: 'name' | 'rFont'): string;
@@ -0,0 +1,104 @@
1
+ import { isFontScheme, isFontVerticalAlignment, isNamedUnderlineStyle, } from '../../core/style.js';
2
+ import { numFinite, numInteger } from '../../xml/xml-attrs.js';
3
+ import { boolPresent } from '../../xml/xml-scan.js';
4
+ import { checkedToken, escapeAttr, numberText } from '../../xml/xml.js';
5
+ import { colorAttrs, parseColor } from './color-xml.js';
6
+ export function applyFontChild(draft, local, attrs) {
7
+ switch (local) {
8
+ case 'b':
9
+ draft.bold = boolPresent(attrs.val);
10
+ break;
11
+ case 'i':
12
+ draft.italic = boolPresent(attrs.val);
13
+ break;
14
+ case 'strike':
15
+ draft.strike = boolPresent(attrs.val);
16
+ break;
17
+ case 'outline':
18
+ draft.outline = boolPresent(attrs.val);
19
+ break;
20
+ case 'u':
21
+ draft.underline =
22
+ attrs.val === undefined
23
+ ? true
24
+ : attrs.val === 'none'
25
+ ? false
26
+ : isNamedUnderlineStyle(attrs.val)
27
+ ? attrs.val
28
+ : true;
29
+ break;
30
+ case 'vertAlign':
31
+ if (attrs.val !== undefined && isFontVerticalAlignment(attrs.val))
32
+ draft.vertAlign = attrs.val;
33
+ break;
34
+ case 'sz': {
35
+ const size = numFinite(attrs.val);
36
+ if (size !== undefined)
37
+ draft.size = size;
38
+ break;
39
+ }
40
+ case 'color':
41
+ draft.color = parseColor(attrs);
42
+ break;
43
+ case 'name':
44
+ case 'rFont':
45
+ if (attrs.val !== undefined)
46
+ draft.name = attrs.val;
47
+ break;
48
+ case 'family': {
49
+ const family = numInteger(attrs.val);
50
+ if (family !== undefined)
51
+ draft.family = family;
52
+ break;
53
+ }
54
+ case 'charset': {
55
+ const charset = numInteger(attrs.val);
56
+ if (charset !== undefined)
57
+ draft.charset = charset;
58
+ break;
59
+ }
60
+ case 'scheme':
61
+ if (attrs.val !== undefined && isFontScheme(attrs.val))
62
+ draft.scheme = attrs.val;
63
+ break;
64
+ default:
65
+ break;
66
+ }
67
+ }
68
+ export function fontXml(font, nameTag = 'name') {
69
+ const parts = [];
70
+ if (font.bold)
71
+ parts.push('<b/>');
72
+ if (font.italic)
73
+ parts.push('<i/>');
74
+ if (font.strike)
75
+ parts.push('<strike/>');
76
+ if (font.outline)
77
+ parts.push('<outline/>');
78
+ const underline = underlineXml(font.underline);
79
+ if (underline !== '')
80
+ parts.push(underline);
81
+ if (font.vertAlign !== undefined) {
82
+ parts.push(`<vertAlign val="${checkedToken(font.vertAlign, isFontVerticalAlignment, 'font vertical alignment')}"/>`);
83
+ }
84
+ if (font.size !== undefined)
85
+ parts.push(`<sz val="${numberText(font.size)}"/>`);
86
+ if (font.color !== undefined)
87
+ parts.push(`<color ${colorAttrs(font.color)}/>`);
88
+ if (font.name !== undefined)
89
+ parts.push(`<${nameTag} val="${escapeAttr(font.name)}"/>`);
90
+ if (font.family !== undefined)
91
+ parts.push(`<family val="${numberText(font.family)}"/>`);
92
+ if (font.charset !== undefined)
93
+ parts.push(`<charset val="${numberText(font.charset)}"/>`);
94
+ if (font.scheme !== undefined && font.scheme !== 'none')
95
+ parts.push(`<scheme val="${checkedToken(font.scheme, isFontScheme, 'font scheme')}"/>`);
96
+ return parts.join('');
97
+ }
98
+ function underlineXml(underline) {
99
+ if (underline === undefined || underline === false || underline === 'none')
100
+ return '';
101
+ if (underline === true || underline === 'single')
102
+ return '<u/>';
103
+ return `<u val="${checkedToken(underline, isNamedUnderlineStyle, 'underline style')}"/>`;
104
+ }
@@ -1,10 +1,18 @@
1
+ import type { Cell } from '../../core/cell.ts';
1
2
  import type { Worksheet } from '../../core/worksheet.ts';
2
3
  import { type CollectingPass } from '../../xml/xml-read.ts';
3
- import type { SheetRelIds } from './package-plan.ts';
4
+ import type { RelIdAllocator } from './package-plan.ts';
4
5
  /** A hyperlink gathered from a sheet for serialisation: the cell it sits on, its target, and an
5
- * optional tooltip. The visible label is the cell's own value and is serialised as that value. */
6
+ * optional tooltip. The visible label is the cell's own value and is serialised as that value.
7
+ *
8
+ * `row`/`col` are the anchor's position, kept so links gathered from separate passes (the sheet's live
9
+ * rows, and the rows the streaming writer already flushed and evicted) can be merged back into the
10
+ * row-major order Excel writes them in. They are not serialised; `ref` is.
11
+ */
6
12
  export interface CollectedHyperlink {
7
13
  readonly ref: string;
14
+ readonly row: number;
15
+ readonly col: number;
8
16
  readonly target: string;
9
17
  readonly tooltip?: string;
10
18
  }
@@ -18,12 +26,36 @@ export interface HyperlinkPlan {
18
26
  readonly location?: string;
19
27
  readonly tooltip?: string;
20
28
  }
21
- /** Gather every hyperlink cell on a sheet, in row-major order. */
22
- export declare function collectHyperlinks(sheet: Worksheet): CollectedHyperlink[];
29
+ /**
30
+ * Gather every hyperlink among a run of cells.
31
+ *
32
+ * Takes the cells rather than the sheet, because the streaming writer has to ask this question of a
33
+ * row at the moment it commits: after that the row's cells are evicted, and a post-hoc walk of
34
+ * `sheet.rows()` finds nothing. Both writers therefore ask the same function, over whatever cells
35
+ * they still hold.
36
+ */
37
+ export declare function collectHyperlinks(cells: Iterable<Cell>): CollectedHyperlink[];
38
+ /** Every cell a sheet still holds, row-major: the live half of what a writer must gather. */
39
+ export declare function liveCells(sheet: Worksheet): Generator<Cell, void, undefined>;
40
+ /** An external hyperlink: the two fields a `TargetMode="External"` relationship needs, both present. */
41
+ export type ExternalHyperlinkPlan = HyperlinkPlan & {
42
+ readonly relId: string;
43
+ readonly target: string;
44
+ };
45
+ /**
46
+ * Whether a planned hyperlink is the external kind, narrowing it so the relationship writer reads
47
+ * `relId` and `target` as the strings they are.
48
+ *
49
+ * A predicate rather than a filter plus two casts: the filter already proved both fields present, and
50
+ * a cast repeating that proof one line later is a claim the compiler cannot check against the filter
51
+ * it is supposed to be echoing. `planHyperlinks` sets exactly one of `relId`/`location`, so testing
52
+ * either field is testing the kind.
53
+ */
54
+ export declare function isExternalHyperlink(link: HyperlinkPlan): link is ExternalHyperlinkPlan;
23
55
  /** Split collected links into internal (location, no rel) and external (relationship) forms, drawing
24
56
  * each external link's relationship id from the sheet's allocator so external ids follow every other
25
57
  * sheet-local relationship in canonical order. An internal ('#'-prefixed) link consumes no id. */
26
- export declare function planHyperlinks(links: readonly CollectedHyperlink[], rels: SheetRelIds): HyperlinkPlan[];
58
+ export declare function planHyperlinks(links: readonly CollectedHyperlink[], rels: RelIdAllocator): HyperlinkPlan[];
27
59
  /** The `<hyperlinks>` element, or '' when the sheet has none. Attribute order follows CT_Hyperlink:
28
60
  * `ref`, `r:id`, `location`, `tooltip`. */
29
61
  export declare function hyperlinksXml(links: readonly HyperlinkPlan[]): string;
@@ -3,22 +3,30 @@ import { isHyperlinkValue, isRichTextValue } from '../../core/value.js';
3
3
  import {} from '../../xml/xml-read.js';
4
4
  import { localName } from '../../xml/xml-scan.js';
5
5
  import { escapeAttr, textAttr } from '../../xml/xml.js';
6
- export function collectHyperlinks(sheet) {
6
+ import { relAttr } from '../opc/namespaces.js';
7
+ export function collectHyperlinks(cells) {
7
8
  const links = [];
8
- for (const { cells } of sheet.rows()) {
9
- for (const cell of cells) {
10
- const value = cell.value;
11
- if (isHyperlinkValue(value)) {
12
- links.push({
13
- ref: value.range ?? cell.address,
14
- target: value.hyperlink,
15
- ...(value.tooltip !== undefined ? { tooltip: value.tooltip } : {}),
16
- });
17
- }
9
+ for (const cell of cells) {
10
+ const value = cell.value;
11
+ if (isHyperlinkValue(value)) {
12
+ links.push({
13
+ ref: value.range ?? cell.address,
14
+ row: cell.row,
15
+ col: cell.col,
16
+ target: value.hyperlink,
17
+ ...(value.tooltip !== undefined ? { tooltip: value.tooltip } : {}),
18
+ });
18
19
  }
19
20
  }
20
21
  return links;
21
22
  }
23
+ export function* liveCells(sheet) {
24
+ for (const { cells } of sheet.rows())
25
+ yield* cells;
26
+ }
27
+ export function isExternalHyperlink(link) {
28
+ return link.relId !== undefined && link.target !== undefined;
29
+ }
22
30
  export function planHyperlinks(links, rels) {
23
31
  return links.map((link) => {
24
32
  const tooltip = link.tooltip !== undefined ? { tooltip: link.tooltip } : {};
@@ -45,15 +53,16 @@ export function sheetHyperlinkPass() {
45
53
  const links = [];
46
54
  return {
47
55
  handlers: {
48
- onOpen(name, attrs) {
56
+ onOpen(name, attrs, _selfClosing, scope) {
49
57
  if (localName(name) !== 'hyperlink')
50
58
  return;
51
59
  const ref = attrs.ref;
52
60
  if (ref === undefined)
53
61
  return;
62
+ const rid = relAttr(scope, attrs, 'id');
54
63
  links.push({
55
64
  ref,
56
- ...(attrs['r:id'] !== undefined ? { rid: attrs['r:id'] } : {}),
65
+ ...(rid !== undefined ? { rid } : {}),
57
66
  ...(attrs.location !== undefined ? { location: attrs.location } : {}),
58
67
  ...(attrs.tooltip !== undefined ? { tooltip: attrs.tooltip } : {}),
59
68
  });
@@ -1,8 +1,9 @@
1
1
  import { isImageEditAs, isOneCellAnchor, } from '../../core/image.js';
2
- import { parseXml, TextCapture } from '../../xml/xml-read.js';
3
- import { enumToken, localName, numFinite } from '../../xml/xml-scan.js';
2
+ import { enumToken, numFinite } from '../../xml/xml-attrs.js';
3
+ import { openElements, parseXml, TextCapture } from '../../xml/xml-read.js';
4
+ import { localName } from '../../xml/xml-scan.js';
4
5
  import { checkedToken, numAttr, numberText, XML_DECLARATION } from '../../xml/xml.js';
5
- import { RELATIONSHIPS_NS } from '../opc/namespaces.js';
6
+ import { relAttr, RELATIONSHIPS_NS } from '../opc/namespaces.js';
6
7
  import { relationship, relationshipsPart } from '../opc/rels.js';
7
8
  import { DRAWINGML_NS, XDR_NS } from './namespaces.js';
8
9
  const IMAGE_REL_TYPE = `${RELATIONSHIPS_NS}/image`;
@@ -85,7 +86,7 @@ export function parseDrawing(xml) {
85
86
  let picDepth = 0;
86
87
  const coord = new TextCapture(COORDINATES);
87
88
  parseXml(xml, {
88
- onOpen(name, attrs, selfClosing) {
89
+ onOpen(name, attrs, selfClosing, scope) {
89
90
  const local = localName(name);
90
91
  if (local === 'twoCellAnchor' || local === 'oneCellAnchor') {
91
92
  from = blankPoint();
@@ -116,7 +117,7 @@ export function parseDrawing(xml) {
116
117
  ext = { cx, cy };
117
118
  }
118
119
  else if (local === 'blip') {
119
- const value = attrs['r:embed'] ?? attrs.embed;
120
+ const value = relAttr(scope, attrs, 'embed');
120
121
  if (value !== undefined)
121
122
  embed = value;
122
123
  }
@@ -162,14 +163,11 @@ export function parseDrawing(xml) {
162
163
  }
163
164
  const UNMODELED_DRAWING_CONTENT = new Set(['graphicFrame', 'sp', 'cxnSp', 'grpSp']);
164
165
  export function drawingHasUnmodeledContent(xml) {
165
- let found = false;
166
- parseXml(xml, {
167
- onOpen(name) {
168
- if (!found && UNMODELED_DRAWING_CONTENT.has(localName(name)))
169
- found = true;
170
- },
171
- });
172
- return found;
166
+ for (const { local } of openElements(xml)) {
167
+ if (UNMODELED_DRAWING_CONTENT.has(local))
168
+ return true;
169
+ }
170
+ return false;
173
171
  }
174
172
  const COORDINATES = new Set(['col', 'colOff', 'row', 'rowOff']);
175
173
  function setCoordinate(point, coord, value) {
@@ -9,6 +9,7 @@
9
9
  * The package-level URIs (`.rels`, content types, the relationship vocabulary) describe the
10
10
  * container rather than the spreadsheet inside it, and live in `../opc/namespaces.ts`.
11
11
  */
12
+ import type { NamespaceScope } from '../../xml/xml-namespaces.ts';
12
13
  /**
13
14
  * SpreadsheetML main namespace: the default `xmlns` of the workbook,
14
15
  * worksheet, styles, sharedStrings, comments, table and pivot parts.
@@ -53,3 +54,18 @@ export declare const DATABAR_LINK_EXT_URI = "{B025F937-C7B1-47D3-B67F-A62EFF666E
53
54
  export declare const DATA_VALIDATION_EXT_URI = "{CCE6A557-97BC-4b89-ADB6-D9C93CAAB3DF}";
54
55
  export declare const SLICER_LIST_EXT_URI = "{A8765BA9-456A-4dab-B4F3-ACF838C121DE}";
55
56
  export declare const SLICER_CACHES_EXT_URI = "{BBE1A952-AA13-448e-AADC-164F8A28A991}";
57
+ /**
58
+ * Whether an element belongs to the standard SpreadsheetML vocabulary rather than to an extension.
59
+ *
60
+ * This used to be spelled `!name.includes(':')`, on the assumption that only extension elements carry
61
+ * a prefix. A worksheet may bind the *main* namespace to a prefix instead, which is legal and which
62
+ * real toolchains emit, and then every element has a colon in it: such a file had every one of its
63
+ * data validations and conditional formats read as an unknown extension and dropped, silently.
64
+ *
65
+ * An unprefixed element counts even when the part declares no default namespace. A part with no
66
+ * `xmlns` at all is not conforming OOXML, but this scanner does not reject it, such parts were read
67
+ * before, and a fix that gains files by resolving namespaces should not lose files that have none.
68
+ */
69
+ export declare function isMainNamespaceElement(scope: NamespaceScope, name: string): boolean;
70
+ /** The complement of {@link isMainNamespaceElement}: an element of one of the extension vocabularies. */
71
+ export declare function isExtensionElement(scope: NamespaceScope, name: string): boolean;
@@ -11,3 +11,10 @@ export const DATABAR_LINK_EXT_URI = '{B025F937-C7B1-47D3-B67F-A62EFF666E3E}';
11
11
  export const DATA_VALIDATION_EXT_URI = '{CCE6A557-97BC-4b89-ADB6-D9C93CAAB3DF}';
12
12
  export const SLICER_LIST_EXT_URI = '{A8765BA9-456A-4dab-B4F3-ACF838C121DE}';
13
13
  export const SLICER_CACHES_EXT_URI = '{BBE1A952-AA13-448e-AADC-164F8A28A991}';
14
+ export function isMainNamespaceElement(scope, name) {
15
+ const namespace = scope.elementNamespace(name);
16
+ return namespace === SPREADSHEETML_NS || (namespace === undefined && !name.includes(':'));
17
+ }
18
+ export function isExtensionElement(scope, name) {
19
+ return !isMainNamespaceElement(scope, name);
20
+ }
@@ -5,8 +5,9 @@ import type { Table } from '../../core/table.ts';
5
5
  import type { Workbook } from '../../core/workbook.ts';
6
6
  import type { Worksheet } from '../../core/worksheet.ts';
7
7
  import type { CommentCell } from './comments.ts';
8
+ import type { HyperlinkPlan } from './hyperlinks.ts';
8
9
  import type { DrawingImage } from './images.ts';
9
- export declare class SheetRelIds {
10
+ export declare class RelIdAllocator {
10
11
  #private;
11
12
  /** The next relationship id (`rId1`, `rId2`, …), advancing the counter. */
12
13
  next(): string;
@@ -108,4 +109,15 @@ export interface MediaPlan {
108
109
  resolve(id: number): ResolvedMedia;
109
110
  }
110
111
  export declare function planMedia(workbook: Workbook, sheets: readonly Worksheet[]): MediaPlan;
111
- export declare function planPreservedParts(workbook: Workbook, generatedDrawingCount: number, generatedMediaCount: number): PreservedPlan;
112
+ export declare function planPreservedParts(workbook: Workbook, generatedDrawingCount: number, generatedMediaCount: number, generatedPivotCount: number): PreservedPlan;
113
+ export interface SheetPlan {
114
+ readonly tables: TablePlan[];
115
+ readonly drawing: DrawingPlan | null;
116
+ readonly comments: CommentPlan | null;
117
+ readonly threadedComments: ThreadedCommentPlan | null;
118
+ readonly printerSettings: PrinterSettingsPlan | null;
119
+ readonly hyperlinks: HyperlinkPlan[];
120
+ readonly background: BackgroundPlan | null;
121
+ readonly preservedRefs: PreservedReferencePlan[];
122
+ readonly pivots: PivotPlan[];
123
+ }