@shbernal/ts-xlsx 3.0.0 → 3.2.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.
- package/README.md +34 -24
- package/dist/bytes.d.ts +10 -2
- package/dist/bytes.js +22 -0
- package/dist/core/address.d.ts +96 -2
- package/dist/core/address.js +87 -33
- package/dist/core/array-formula-ranges.d.ts +33 -0
- package/dist/core/array-formula-ranges.js +62 -0
- package/dist/core/autofilter.d.ts +4 -3
- package/dist/core/autofilter.js +31 -30
- package/dist/core/axis-handle.d.ts +2 -0
- package/dist/core/axis-handle.js +5 -2
- package/dist/core/cell.d.ts +4 -3
- package/dist/core/cell.js +4 -6
- package/dist/core/clone.d.ts +29 -0
- package/dist/core/clone.js +24 -0
- package/dist/core/color-resolution.js +4 -6
- package/dist/core/column.d.ts +1 -0
- package/dist/core/column.js +6 -2
- package/dist/core/comment-thread.d.ts +7 -3
- package/dist/core/comment-thread.js +2 -1
- package/dist/core/conditional-formatting-overlay.d.ts +11 -1
- package/dist/core/conditional-formatting-overlay.js +30 -3
- package/dist/core/conditional-formatting.d.ts +107 -11
- package/dist/core/conditional-formatting.js +76 -19
- package/dist/core/containers.d.ts +12 -0
- package/dist/core/containers.js +5 -0
- package/dist/core/data-validation-overlay.d.ts +7 -1
- package/dist/core/data-validation-overlay.js +19 -4
- package/dist/core/data-validation.d.ts +22 -0
- package/dist/core/data-validation.js +30 -4
- package/dist/core/date-format.d.ts +20 -0
- package/dist/core/date-format.js +170 -0
- package/dist/core/date.d.ts +43 -8
- package/dist/core/date.js +19 -3
- package/dist/core/formula-references.d.ts +26 -0
- package/dist/core/formula-references.js +173 -0
- package/dist/core/formula-scan.d.ts +2 -0
- package/dist/core/formula-scan.js +58 -0
- package/dist/core/formula.d.ts +76 -17
- package/dist/core/formula.js +87 -80
- package/dist/core/function-values.d.ts +4 -0
- package/dist/core/function-values.js +58 -0
- package/dist/core/future-functions.d.ts +3 -0
- package/dist/core/{modern-functions.js → future-functions.js} +119 -88
- package/dist/core/grid-edits.d.ts +30 -3
- package/dist/core/grid-edits.js +248 -81
- package/dist/core/grid-shift.d.ts +58 -9
- package/dist/core/grid-shift.js +26 -4
- package/dist/core/hyperlink.d.ts +39 -0
- package/dist/core/hyperlink.js +88 -0
- package/dist/core/image.d.ts +49 -14
- package/dist/core/image.js +16 -5
- package/dist/core/internal.d.ts +10 -2
- package/dist/core/limits.d.ts +25 -0
- package/dist/core/limits.js +4 -0
- package/dist/core/merge.d.ts +18 -4
- package/dist/core/merge.js +33 -20
- package/dist/core/page-setup.d.ts +26 -11
- package/dist/core/pivot-table.d.ts +49 -10
- package/dist/core/pivot-table.js +112 -60
- package/dist/core/protection.d.ts +66 -4
- package/dist/core/protection.js +6 -11
- package/dist/core/range.js +34 -30
- package/dist/core/row-input.js +2 -2
- package/dist/core/row.d.ts +1 -0
- package/dist/core/row.js +6 -2
- package/dist/core/structured-reference.d.ts +7 -0
- package/dist/core/structured-reference.js +7 -0
- package/dist/core/style.d.ts +85 -70
- package/dist/core/style.js +92 -28
- package/dist/core/table-style.d.ts +1 -1
- package/dist/core/table-style.js +4 -4
- package/dist/core/table.d.ts +64 -16
- package/dist/core/table.js +87 -40
- package/dist/core/theme.d.ts +2 -0
- package/dist/core/theme.js +9 -5
- package/dist/core/value.d.ts +60 -28
- package/dist/core/value.js +48 -43
- package/dist/core/workbook-media.d.ts +34 -0
- package/dist/core/workbook-media.js +44 -0
- package/dist/core/workbook-styles.d.ts +83 -1
- package/dist/core/workbook-styles.js +4 -0
- package/dist/core/workbook-theme.js +5 -3
- package/dist/core/workbook-vba.js +3 -2
- package/dist/core/workbook.d.ts +41 -3
- package/dist/core/workbook.js +46 -41
- package/dist/core/worksheet-comments.d.ts +2 -1
- package/dist/core/worksheet-comments.js +20 -13
- package/dist/core/worksheet-merges.d.ts +49 -0
- package/dist/core/worksheet-merges.js +65 -0
- package/dist/core/worksheet-model.d.ts +10 -2
- package/dist/core/worksheet-model.js +28 -9
- package/dist/core/worksheet-pictures.d.ts +3 -3
- package/dist/core/worksheet-pictures.js +7 -6
- package/dist/core/worksheet.d.ts +165 -27
- package/dist/core/worksheet.js +206 -89
- package/dist/customui/ribbon.js +2 -1
- package/dist/entries/core.d.ts +21 -14
- package/dist/entries/core.js +14 -7
- package/dist/entries/errors.d.ts +1 -0
- package/dist/entries/errors.js +1 -0
- package/dist/entries/xlsx.d.ts +1 -1
- package/dist/errors.d.ts +64 -0
- package/dist/errors.js +22 -0
- package/dist/hex.d.ts +8 -0
- package/dist/hex.js +3 -0
- package/dist/io/cell-metadata/metadata.d.ts +42 -0
- package/dist/io/cell-metadata/metadata.js +35 -0
- package/dist/io/cell-metadata/rich-values.d.ts +11 -0
- package/dist/io/cell-metadata/rich-values.js +80 -0
- package/dist/io/csv/delimiter.d.ts +21 -0
- package/dist/io/csv/delimiter.js +18 -0
- package/dist/io/csv/errors.d.ts +14 -0
- package/dist/io/csv/errors.js +5 -0
- package/dist/io/csv/read.d.ts +6 -1
- package/dist/io/csv/read.js +51 -22
- package/dist/io/csv/write.d.ts +19 -5
- package/dist/io/csv/write.js +24 -44
- package/dist/io/opc/inflate.js +14 -0
- package/dist/io/opc/namespaces.d.ts +16 -0
- package/dist/io/opc/namespaces.js +4 -0
- package/dist/io/opc/part-paths.d.ts +9 -0
- package/dist/io/opc/part-paths.js +5 -5
- package/dist/io/opc/read-opc.d.ts +58 -14
- package/dist/io/opc/read-opc.js +49 -21
- package/dist/io/opc/read-options.d.ts +5 -1
- package/dist/io/opc/sniff-format.d.ts +3 -3
- package/dist/io/opc/sniff-format.js +2 -2
- package/dist/io/opc/strict-relationships.d.ts +2 -0
- package/dist/io/opc/strict-relationships.js +12 -0
- package/dist/io/read-policy/column-budget.d.ts +26 -0
- package/dist/io/read-policy/column-budget.js +18 -0
- package/dist/io/read-policy/read-repair.d.ts +74 -0
- package/dist/io/read-policy/read-repair.js +119 -0
- package/dist/io/style/cell-style-resolution.d.ts +25 -0
- package/dist/io/style/cell-style-resolution.js +30 -0
- package/dist/io/style/xf-style.d.ts +23 -1
- package/dist/io/style/xf-style.js +8 -0
- package/dist/io/xlsb/formula.d.ts +2 -2
- package/dist/io/xlsb/formula.js +52 -35
- package/dist/io/xlsb/primitives.js +8 -14
- package/dist/io/xlsb/ptg-functions.d.ts +19 -10
- package/dist/io/xlsb/ptg-functions.js +495 -596
- package/dist/io/xlsb/read-metadata.d.ts +7 -0
- package/dist/io/xlsb/read-metadata.js +69 -0
- package/dist/io/xlsb/read-styles.js +45 -81
- package/dist/io/xlsb/read-worksheet.d.ts +26 -1
- package/dist/io/xlsb/read-worksheet.js +148 -106
- package/dist/io/xlsb/read.d.ts +6 -3
- package/dist/io/xlsb/read.js +70 -31
- package/dist/io/xlsb/record-stream.d.ts +30 -0
- package/dist/io/xlsb/record-stream.js +20 -0
- package/dist/io/xlsb/record-types.d.ts +14 -5
- package/dist/io/xlsb/record-types.js +14 -5
- package/dist/io/xlsb/sheet-protection.d.ts +20 -0
- package/dist/io/xlsb/sheet-protection.js +60 -0
- package/dist/io/xlsx/cell-accumulator.d.ts +24 -9
- package/dist/io/xlsx/cell-accumulator.js +74 -21
- package/dist/io/xlsx/cell-metadata.d.ts +39 -0
- package/dist/io/xlsx/cell-metadata.js +162 -0
- package/dist/io/xlsx/cell-value.d.ts +19 -5
- package/dist/io/xlsx/cell-value.js +45 -30
- package/dist/io/xlsx/color-xml.js +3 -2
- package/dist/io/xlsx/column-span.d.ts +11 -0
- package/dist/io/xlsx/column-span.js +9 -0
- package/dist/io/xlsx/comments.d.ts +9 -30
- package/dist/io/xlsx/comments.js +25 -83
- package/dist/io/xlsx/conditional-formatting.d.ts +43 -23
- package/dist/io/xlsx/conditional-formatting.js +437 -138
- package/dist/io/xlsx/data-validation.d.ts +12 -7
- package/dist/io/xlsx/data-validation.js +67 -53
- package/dist/io/xlsx/edit-vba.d.ts +7 -2
- package/dist/io/xlsx/edit-vba.js +52 -52
- package/dist/io/xlsx/font-xml.d.ts +7 -0
- package/dist/io/xlsx/font-xml.js +104 -0
- package/dist/io/xlsx/hyperlinks.d.ts +19 -21
- package/dist/io/xlsx/hyperlinks.js +12 -32
- package/dist/io/xlsx/images.d.ts +49 -16
- package/dist/io/xlsx/images.js +149 -50
- package/dist/io/xlsx/namespaces.d.ts +24 -0
- package/dist/io/xlsx/namespaces.js +11 -0
- package/dist/io/xlsx/package-plan.d.ts +60 -15
- package/dist/io/xlsx/package-plan.js +83 -18
- package/dist/io/xlsx/part-names.d.ts +6 -8
- package/dist/io/xlsx/part-names.js +3 -7
- package/dist/io/xlsx/pivot.d.ts +5 -5
- package/dist/io/xlsx/pivot.js +26 -24
- package/dist/io/xlsx/preserved-splices.d.ts +6 -0
- package/dist/io/xlsx/preserved-splices.js +74 -0
- package/dist/io/xlsx/read-comments.d.ts +29 -0
- package/dist/io/xlsx/read-comments.js +75 -0
- package/dist/io/xlsx/read-parts.d.ts +59 -0
- package/dist/io/xlsx/read-parts.js +259 -0
- package/dist/io/xlsx/read-pivot.js +11 -4
- package/dist/io/xlsx/{rich-runs.d.ts → read-rich-runs.d.ts} +8 -5
- package/dist/io/xlsx/{rich-runs.js → read-rich-runs.js} +24 -4
- package/dist/io/xlsx/read-rows.d.ts +35 -11
- package/dist/io/xlsx/read-rows.js +73 -49
- package/dist/io/xlsx/read-shared-strings.js +3 -2
- package/dist/io/xlsx/read-styles.d.ts +0 -30
- package/dist/io/xlsx/read-styles.js +45 -105
- package/dist/io/xlsx/read-workbook-xml.d.ts +42 -0
- package/dist/io/xlsx/read-workbook-xml.js +175 -0
- package/dist/io/xlsx/read-worksheet.d.ts +9 -3
- package/dist/io/xlsx/read-worksheet.js +70 -177
- package/dist/io/xlsx/read.d.ts +3 -14
- package/dist/io/xlsx/read.js +74 -411
- package/dist/io/xlsx/relationships.d.ts +3 -0
- package/dist/io/xlsx/relationships.js +4 -0
- package/dist/io/xlsx/rich-text.js +1 -1
- package/dist/io/xlsx/row-position.d.ts +20 -0
- package/dist/io/xlsx/row-position.js +11 -0
- package/dist/io/xlsx/row-xml.d.ts +139 -0
- package/dist/io/xlsx/row-xml.js +222 -0
- package/dist/io/xlsx/shared-formulas.js +2 -2
- package/dist/io/xlsx/sheet-properties.d.ts +10 -1
- package/dist/io/xlsx/sheet-properties.js +182 -20
- package/dist/io/xlsx/strict-parts.d.ts +7 -0
- package/dist/io/xlsx/strict-parts.js +171 -0
- package/dist/io/xlsx/style-elements.d.ts +41 -0
- package/dist/io/xlsx/style-elements.js +136 -0
- package/dist/io/xlsx/styles.d.ts +17 -3
- package/dist/io/xlsx/styles.js +13 -209
- package/dist/io/xlsx/tables.d.ts +10 -2
- package/dist/io/xlsx/tables.js +55 -52
- package/dist/io/xlsx/theme-xml.d.ts +32 -4
- package/dist/io/xlsx/theme-xml.js +106 -71
- package/dist/io/xlsx/threaded-comments.d.ts +14 -11
- package/dist/io/xlsx/threaded-comments.js +16 -8
- package/dist/io/xlsx/workbook-xml.d.ts +45 -6
- package/dist/io/xlsx/workbook-xml.js +62 -54
- package/dist/io/xlsx/worksheet-xml.d.ts +26 -70
- package/dist/io/xlsx/worksheet-xml.js +69 -282
- package/dist/io/xlsx/write-stream.d.ts +90 -19
- package/dist/io/xlsx/write-stream.js +80 -25
- package/dist/io/xlsx/write.d.ts +12 -6
- package/dist/io/xlsx/write.js +226 -123
- package/dist/rel-type.d.ts +25 -0
- package/dist/rel-type.js +9 -0
- package/dist/token-set.d.ts +13 -0
- package/dist/token-set.js +4 -1
- package/dist/vba/bytes.d.ts +16 -5
- package/dist/vba/bytes.js +27 -6
- package/dist/vba/cfb-format.d.ts +55 -0
- package/dist/vba/cfb-format.js +35 -0
- package/dist/vba/cfb-writer.d.ts +8 -0
- package/dist/vba/cfb-writer.js +40 -40
- package/dist/vba/cfb.d.ts +21 -4
- package/dist/vba/cfb.js +124 -90
- package/dist/vba/codepage.js +2 -1
- package/dist/vba/dir-records.d.ts +6 -0
- package/dist/vba/dir-records.js +10 -2
- package/dist/vba/errors.d.ts +11 -4
- package/dist/vba/index.d.ts +0 -2
- package/dist/vba/index.js +0 -2
- package/dist/vba/ms-ovba.d.ts +6 -2
- package/dist/vba/ms-ovba.js +66 -22
- package/dist/vba/project-editor.js +129 -119
- package/dist/vba/project.d.ts +15 -1
- package/dist/vba/project.js +38 -13
- package/dist/vba/vba-encoding.d.ts +4 -10
- package/dist/vba/vba-encoding.js +3 -17
- package/dist/xml/xml-attrs.d.ts +37 -0
- package/dist/xml/xml-attrs.js +30 -0
- package/dist/xml/xml-chars.d.ts +40 -0
- package/dist/xml/xml-chars.js +12 -0
- package/dist/xml/xml-namespaces.d.ts +31 -0
- package/dist/xml/xml-namespaces.js +50 -0
- package/dist/xml/xml-read.d.ts +101 -11
- package/dist/xml/xml-read.js +150 -18
- package/dist/xml/xml-scan.d.ts +25 -34
- package/dist/xml/xml-scan.js +69 -46
- package/dist/xml/xml.d.ts +59 -26
- package/dist/xml/xml.js +45 -23
- package/package.json +15 -9
- package/skills/ts-xlsx-upstream/SKILL.md +21 -13
- package/dist/core/modern-functions.d.ts +0 -1
|
@@ -1,12 +1,39 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { INTERNAL } from '../../core/internal.js';
|
|
2
|
+
import { AuthoringError, InternalError, quoted } from '../../errors.js';
|
|
2
3
|
import { extensionOf, relativePartPath, relsPathFor, THEME_PART_PATH } from '../opc/part-paths.js';
|
|
3
4
|
import { relsPartXml } from '../opc/rels.js';
|
|
4
|
-
import {
|
|
5
|
+
import { mergePictures } from './images.js';
|
|
6
|
+
import { drawingPart, mediaPart, pivotCacheDefinitionPart, pivotCacheRecordsPart, pivotTablePart, vmlDrawingPart, } from './part-names.js';
|
|
7
|
+
import { splicePreservedPart } from './preserved-splices.js';
|
|
5
8
|
import { applyThemeOverrides } from './theme-xml.js';
|
|
6
|
-
export class
|
|
9
|
+
export class RelationshipLedger {
|
|
10
|
+
#owner;
|
|
11
|
+
#taken;
|
|
12
|
+
#relationships = [];
|
|
7
13
|
#next = 1;
|
|
8
|
-
|
|
9
|
-
|
|
14
|
+
constructor(owner, taken = new Set()) {
|
|
15
|
+
this.#owner = owner;
|
|
16
|
+
this.#taken = taken;
|
|
17
|
+
}
|
|
18
|
+
add(type, partPath) {
|
|
19
|
+
return this.#record({ type, target: relativePartPath(this.#owner, partPath) });
|
|
20
|
+
}
|
|
21
|
+
addExternal(type, url) {
|
|
22
|
+
return this.#record({ type, target: url, external: true });
|
|
23
|
+
}
|
|
24
|
+
addInDocument(type, location) {
|
|
25
|
+
return this.#record({ type, target: location });
|
|
26
|
+
}
|
|
27
|
+
get relationships() {
|
|
28
|
+
return this.#relationships;
|
|
29
|
+
}
|
|
30
|
+
#record(relationship) {
|
|
31
|
+
while (this.#taken.has(`rId${this.#next}`))
|
|
32
|
+
this.#next += 1;
|
|
33
|
+
const id = `rId${this.#next}`;
|
|
34
|
+
this.#next += 1;
|
|
35
|
+
this.#relationships.push({ id, ...relationship });
|
|
36
|
+
return id;
|
|
10
37
|
}
|
|
11
38
|
}
|
|
12
39
|
export function planMedia(workbook, sheets) {
|
|
@@ -32,7 +59,7 @@ export function planMedia(workbook, sheets) {
|
|
|
32
59
|
const image = workbook.getImage(id);
|
|
33
60
|
if (image === undefined) {
|
|
34
61
|
const reference = role === 'anchor' ? `anchors image id ${id}` : `sets background image id ${id}`;
|
|
35
|
-
throw new AuthoringError(`sheet
|
|
62
|
+
throw new AuthoringError(`sheet ${quoted(sheetName)} ${reference}, which is not registered on the workbook`);
|
|
36
63
|
}
|
|
37
64
|
const number = i + 1;
|
|
38
65
|
parts.push({ number, extension: image.extension, data: image.data });
|
|
@@ -51,12 +78,15 @@ export function planMedia(workbook, sheets) {
|
|
|
51
78
|
},
|
|
52
79
|
};
|
|
53
80
|
}
|
|
54
|
-
export function planPreservedParts(workbook, generatedDrawingCount, generatedMediaCount) {
|
|
81
|
+
export function planPreservedParts(workbook, generatedDrawingCount, generatedMediaCount, generatedPivotCount, pictures) {
|
|
55
82
|
const sheets = workbook.worksheets;
|
|
56
83
|
const numbering = {
|
|
57
84
|
drawing: generatedDrawingCount,
|
|
58
85
|
vml: sheets.length,
|
|
59
86
|
media: generatedMediaCount,
|
|
87
|
+
pivotTable: generatedPivotCount,
|
|
88
|
+
pivotCacheDefinition: generatedPivotCount,
|
|
89
|
+
pivotCacheRecords: generatedPivotCount,
|
|
60
90
|
};
|
|
61
91
|
const remap = new Map();
|
|
62
92
|
const theme = workbook.themePart;
|
|
@@ -75,9 +105,10 @@ export function planPreservedParts(workbook, generatedDrawingCount, generatedMed
|
|
|
75
105
|
}
|
|
76
106
|
}
|
|
77
107
|
const emitted = new Map();
|
|
108
|
+
const splices = workbook[INTERNAL].splices();
|
|
78
109
|
for (const reference of allReferences) {
|
|
79
110
|
for (const part of reference.parts) {
|
|
80
|
-
const newPath = remap
|
|
111
|
+
const newPath = resolveRemapped(remap, part.path);
|
|
81
112
|
if (emitted.has(newPath))
|
|
82
113
|
continue;
|
|
83
114
|
const rels = part.rels.flatMap((rel) => {
|
|
@@ -90,31 +121,40 @@ export function planPreservedParts(workbook, generatedDrawingCount, generatedMed
|
|
|
90
121
|
: [{ id: rel.id, type: rel.type, target: relativePartPath(newPath, target) }];
|
|
91
122
|
});
|
|
92
123
|
const overrides = newPath === THEME_PART_PATH ? workbook.themeOverrides : undefined;
|
|
124
|
+
let bytes = overrides === undefined
|
|
125
|
+
? splicePreservedPart(part.bytes, part.contentType, splices)
|
|
126
|
+
: new TextEncoder().encode(applyThemeOverrides(new TextDecoder().decode(part.bytes), overrides));
|
|
127
|
+
let relationships = rels;
|
|
128
|
+
const joining = pictures.get(part.path);
|
|
129
|
+
if (joining !== undefined) {
|
|
130
|
+
const ledger = new RelationshipLedger(newPath, new Set(rels.map((rel) => rel.id)));
|
|
131
|
+
const images = joining(ledger);
|
|
132
|
+
bytes = new TextEncoder().encode(mergePictures(new TextDecoder().decode(bytes), images));
|
|
133
|
+
relationships = [...rels, ...ledger.relationships];
|
|
134
|
+
}
|
|
93
135
|
emitted.set(newPath, {
|
|
94
136
|
path: newPath,
|
|
95
|
-
bytes
|
|
96
|
-
? part.bytes
|
|
97
|
-
: new TextEncoder().encode(applyThemeOverrides(new TextDecoder().decode(part.bytes), overrides)),
|
|
137
|
+
bytes,
|
|
98
138
|
contentType: part.contentType,
|
|
99
|
-
relsPath:
|
|
100
|
-
relsXml:
|
|
139
|
+
relsPath: relationships.length === 0 ? null : relsPathFor(newPath),
|
|
140
|
+
relsXml: relationships.length === 0 ? null : relsPartXml(relationships),
|
|
101
141
|
});
|
|
102
142
|
}
|
|
103
143
|
}
|
|
104
144
|
const perSheet = sheets.map((sheet) => sheet.preservedReferences.map((reference) => ({
|
|
105
145
|
element: reference.element,
|
|
106
146
|
relType: reference.relType,
|
|
107
|
-
entryPath: remap
|
|
147
|
+
entryPath: resolveRemapped(remap, reference.entryPath),
|
|
108
148
|
})));
|
|
109
149
|
const workbookRefs = workbook.preservedReferences.map((reference) => ({
|
|
110
150
|
relType: reference.relType,
|
|
111
|
-
entryPath: remap
|
|
151
|
+
entryPath: resolveRemapped(remap, reference.entryPath),
|
|
112
152
|
pivotCacheId: reference.pivotCacheId,
|
|
113
153
|
externalReferenceIndex: reference.externalReferenceIndex,
|
|
114
154
|
}));
|
|
115
155
|
const rootRefs = workbook.preservedRootReferences.map((reference) => ({
|
|
116
156
|
relType: reference.relType,
|
|
117
|
-
entryPath: remap
|
|
157
|
+
entryPath: resolveRemapped(remap, reference.entryPath),
|
|
118
158
|
}));
|
|
119
159
|
return {
|
|
120
160
|
perSheet,
|
|
@@ -124,14 +164,39 @@ export function planPreservedParts(workbook, generatedDrawingCount, generatedMed
|
|
|
124
164
|
themeEmitted: theme !== undefined,
|
|
125
165
|
};
|
|
126
166
|
}
|
|
167
|
+
function resolveRemapped(remap, path) {
|
|
168
|
+
const resolved = remap.get(path);
|
|
169
|
+
if (resolved === undefined) {
|
|
170
|
+
throw new InternalError(`preserved part ${quoted(path)} was never assigned a new path`);
|
|
171
|
+
}
|
|
172
|
+
return resolved;
|
|
173
|
+
}
|
|
127
174
|
function preservedPartPath(originalPath, numbering) {
|
|
128
175
|
const ext = extensionOf(originalPath);
|
|
129
|
-
if (ext
|
|
176
|
+
if (ext === 'vml')
|
|
130
177
|
return vmlDrawingPart(++numbering.vml);
|
|
131
178
|
if (originalPath.startsWith('xl/media/'))
|
|
132
179
|
return mediaPart(++numbering.media, ext);
|
|
133
|
-
if (originalPath.startsWith('xl/drawings/') && ext
|
|
180
|
+
if (originalPath.startsWith('xl/drawings/') && ext === 'xml') {
|
|
134
181
|
return drawingPart(++numbering.drawing);
|
|
135
182
|
}
|
|
183
|
+
if (isNumberedPart(originalPath, PIVOT_TABLE_PREFIX)) {
|
|
184
|
+
return pivotTablePart(++numbering.pivotTable);
|
|
185
|
+
}
|
|
186
|
+
if (isNumberedPart(originalPath, PIVOT_CACHE_DEFINITION_PREFIX)) {
|
|
187
|
+
return pivotCacheDefinitionPart(++numbering.pivotCacheDefinition);
|
|
188
|
+
}
|
|
189
|
+
if (isNumberedPart(originalPath, PIVOT_CACHE_RECORDS_PREFIX)) {
|
|
190
|
+
return pivotCacheRecordsPart(++numbering.pivotCacheRecords);
|
|
191
|
+
}
|
|
136
192
|
return originalPath;
|
|
137
193
|
}
|
|
194
|
+
const PIVOT_TABLE_PREFIX = numberedPartPrefix(pivotTablePart);
|
|
195
|
+
const PIVOT_CACHE_DEFINITION_PREFIX = numberedPartPrefix(pivotCacheDefinitionPart);
|
|
196
|
+
const PIVOT_CACHE_RECORDS_PREFIX = numberedPartPrefix(pivotCacheRecordsPart);
|
|
197
|
+
function numberedPartPrefix(part) {
|
|
198
|
+
return part(1).slice(0, -'1.xml'.length);
|
|
199
|
+
}
|
|
200
|
+
function isNumberedPart(path, prefix) {
|
|
201
|
+
return path.startsWith(prefix) && /^\d+\.xml$/.test(path.slice(prefix.length));
|
|
202
|
+
}
|
|
@@ -5,6 +5,12 @@ export declare const SHARED_STRINGS_PART = "xl/sharedStrings.xml";
|
|
|
5
5
|
/** Singular and unnumbered, unlike the per-sheet thread parts: one identity registry serves the
|
|
6
6
|
* whole workbook's threaded comments. */
|
|
7
7
|
export declare const PERSONS_PART = "xl/persons/person.xml";
|
|
8
|
+
/** The metadata a `<c cm>` and `<c vm>` index: what marks a formula a dynamic array, and what names
|
|
9
|
+
* the error behind a `#VALUE!`. */
|
|
10
|
+
export declare const METADATA_PART = "xl/metadata.xml";
|
|
11
|
+
/** The rich values a value-metadata block names, and the structures they are read against. */
|
|
12
|
+
export declare const RICH_VALUES_PART = "xl/richData/rdrichvalue.xml";
|
|
13
|
+
export declare const RICH_VALUE_STRUCTURES_PART = "xl/richData/rdrichvaluestructure.xml";
|
|
8
14
|
export declare const CORE_PROPS_PART = "docProps/core.xml";
|
|
9
15
|
export declare const APP_PROPS_PART = "docProps/app.xml";
|
|
10
16
|
/** `xl/worksheets/sheet{n}.xml`, numbered by the sheet's position in the workbook (1-based). */
|
|
@@ -31,11 +37,3 @@ export declare function pivotTablePart(number: number): string;
|
|
|
31
37
|
export declare function pivotCacheDefinitionPart(number: number): string;
|
|
32
38
|
/** `xl/pivotCache/pivotCacheRecords{n}.xml`: the cached rows the definition points at. */
|
|
33
39
|
export declare function pivotCacheRecordsPart(number: number): string;
|
|
34
|
-
/**
|
|
35
|
-
* A part named as a Target of a relationship carried by a *worksheet*. Every worksheet part lives in
|
|
36
|
-
* `xl/worksheets/`, so one sheet's view of a target is every sheet's, and the sheet number the
|
|
37
|
-
* arithmetic runs against does not matter.
|
|
38
|
-
*/
|
|
39
|
-
export declare function targetFromWorksheet(partPath: string): string;
|
|
40
|
-
/** A part named as a Target of a relationship carried by the workbook part. */
|
|
41
|
-
export declare function targetFromWorkbook(partPath: string): string;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { relativePartPath } from '../opc/part-paths.js';
|
|
2
1
|
export const WORKBOOK_PART = 'xl/workbook.xml';
|
|
3
2
|
export const STYLES_PART = 'xl/styles.xml';
|
|
4
3
|
export const SHARED_STRINGS_PART = 'xl/sharedStrings.xml';
|
|
5
4
|
export const PERSONS_PART = 'xl/persons/person.xml';
|
|
5
|
+
export const METADATA_PART = 'xl/metadata.xml';
|
|
6
|
+
export const RICH_VALUES_PART = 'xl/richData/rdrichvalue.xml';
|
|
7
|
+
export const RICH_VALUE_STRUCTURES_PART = 'xl/richData/rdrichvaluestructure.xml';
|
|
6
8
|
export const CORE_PROPS_PART = 'docProps/core.xml';
|
|
7
9
|
export const APP_PROPS_PART = 'docProps/app.xml';
|
|
8
10
|
export function worksheetPart(number) {
|
|
@@ -38,9 +40,3 @@ export function pivotCacheDefinitionPart(number) {
|
|
|
38
40
|
export function pivotCacheRecordsPart(number) {
|
|
39
41
|
return `xl/pivotCache/pivotCacheRecords${number}.xml`;
|
|
40
42
|
}
|
|
41
|
-
export function targetFromWorksheet(partPath) {
|
|
42
|
-
return relativePartPath(worksheetPart(1), partPath);
|
|
43
|
-
}
|
|
44
|
-
export function targetFromWorkbook(partPath) {
|
|
45
|
-
return relativePartPath(WORKBOOK_PART, partPath);
|
|
46
|
-
}
|
package/dist/io/xlsx/pivot.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { PivotTable } from '../../core/pivot-table.ts';
|
|
2
|
-
/** The `pivotCacheDefinition` part: the source reference and the field catalogue. `
|
|
3
|
-
*
|
|
4
|
-
* rebuild the pivot body from the cache on open, so the layout stays
|
|
5
|
-
* to the pixel. `recordCount` is the number of source data rows. */
|
|
6
|
-
export declare function pivotCacheDefinitionXml(table: PivotTable): string;
|
|
2
|
+
/** The `pivotCacheDefinition` part: the source reference and the field catalogue. `recordsRelId` is
|
|
3
|
+
* the id this cache's own rels part gives the cache-records part, which `r:id` cites.
|
|
4
|
+
* `refreshOnLoad="1"` tells Excel to rebuild the pivot body from the cache on open, so the layout stays
|
|
5
|
+
* correct without us computing it to the pixel. `recordCount` is the number of source data rows. */
|
|
6
|
+
export declare function pivotCacheDefinitionXml(table: PivotTable, recordsRelId: string): string;
|
|
7
7
|
/** The `pivotCacheRecords` part: one `<r>` per source data row, each cell either an index into an
|
|
8
8
|
* axis field's shared-items catalogue (`<x>`) or an inline value (`<n>`/`<s>`/`<m>`). */
|
|
9
9
|
export declare function pivotCacheRecordsXml(table: PivotTable): string;
|
package/dist/io/xlsx/pivot.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { encodeAddress } from '../../core/address.js';
|
|
2
|
-
import { escapeAttr, XML_DECLARATION } from '../../xml/xml.js';
|
|
2
|
+
import { escapeAttr, numberText, XML_DECLARATION } from '../../xml/xml.js';
|
|
3
3
|
import { RELATIONSHIPS_NS } from '../opc/namespaces.js';
|
|
4
4
|
import { SPREADSHEETML_NS } from './namespaces.js';
|
|
5
5
|
const METRIC_CAPTIONS = {
|
|
@@ -15,29 +15,19 @@ const METRIC_CAPTIONS = {
|
|
|
15
15
|
var: 'Var',
|
|
16
16
|
varp: 'Varp',
|
|
17
17
|
};
|
|
18
|
-
export function pivotCacheDefinitionXml(table) {
|
|
18
|
+
export function pivotCacheDefinitionXml(table, recordsRelId) {
|
|
19
19
|
const fields = table.cacheFields
|
|
20
20
|
.map((field) => {
|
|
21
21
|
const shared = field.sharedItems;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
`<sharedItems${blank} count="${shared.length}">${items}</sharedItems>` +
|
|
27
|
-
`</cacheField>`);
|
|
28
|
-
}
|
|
29
|
-
const blank = field.containsBlank ? ' containsBlank="1"' : '';
|
|
30
|
-
const numeric = field.numeric;
|
|
31
|
-
const descriptor = numeric === null
|
|
32
|
-
? `<sharedItems${blank}/>`
|
|
33
|
-
: `<sharedItems containsSemiMixedTypes="0" containsString="0" containsNumber="1" ` +
|
|
34
|
-
`containsInteger="${numeric.allInteger ? 1 : 0}"${blank} ` +
|
|
35
|
-
`minValue="${numeric.min}" maxValue="${numeric.max}"/>`;
|
|
22
|
+
const kinds = sharedItemsKinds(field);
|
|
23
|
+
const descriptor = shared === null
|
|
24
|
+
? `<sharedItems${kinds}/>`
|
|
25
|
+
: `<sharedItems${kinds} count="${shared.length}">${shared.map(sharedItemXml).join('')}</sharedItems>`;
|
|
36
26
|
return `<cacheField name="${escapeAttr(field.name)}" numFmtId="0">${descriptor}</cacheField>`;
|
|
37
27
|
})
|
|
38
28
|
.join('');
|
|
39
29
|
return (XML_DECLARATION +
|
|
40
|
-
`<pivotCacheDefinition xmlns="${SPREADSHEETML_NS}" xmlns:r="${RELATIONSHIPS_NS}" r:id="
|
|
30
|
+
`<pivotCacheDefinition xmlns="${SPREADSHEETML_NS}" xmlns:r="${RELATIONSHIPS_NS}" r:id="${escapeAttr(recordsRelId)}" refreshOnLoad="1" ` +
|
|
41
31
|
`refreshedBy="ts-xlsx" createdVersion="8" refreshedVersion="8" minRefreshableVersion="3" ` +
|
|
42
32
|
`recordCount="${table.records.length}">` +
|
|
43
33
|
`<cacheSource type="worksheet">` +
|
|
@@ -56,22 +46,21 @@ export function pivotCacheRecordsXml(table) {
|
|
|
56
46
|
`</pivotCacheRecords>`);
|
|
57
47
|
}
|
|
58
48
|
export function pivotTableXml(table, name, cacheId) {
|
|
59
|
-
const rowField = table.rowFields[0];
|
|
60
|
-
const columnField = table.columnFields[0];
|
|
49
|
+
const rowField = table.rowFields[0] ?? 0;
|
|
50
|
+
const columnField = table.columnFields[0] ?? 0;
|
|
61
51
|
const rowGroups = table.cacheFields[rowField]?.sharedItems?.length ?? 1;
|
|
62
52
|
const columnGroups = table.cacheFields[columnField]?.sharedItems?.length ?? 1;
|
|
63
53
|
const location = `A1:${encodeAddress(2 + columnGroups, 3 + rowGroups)}`;
|
|
64
54
|
const pivotFields = table.cacheFields
|
|
65
55
|
.map((field, index) => {
|
|
56
|
+
const dataField = index === table.valueField ? ' dataField="1"' : '';
|
|
66
57
|
if (table.rowFields.includes(index) || table.columnFields.includes(index)) {
|
|
67
58
|
const axis = table.rowFields.includes(index) ? 'axisRow' : 'axisCol';
|
|
68
59
|
const items = field.sharedItems ?? [];
|
|
69
60
|
const entries = `${items.map((_item, i) => `<item x="${i}"/>`).join('')}<item t="default"/>`;
|
|
70
|
-
return `<pivotField axis="${axis}" showAll="0"><items count="${items.length + 1}">${entries}</items></pivotField>`;
|
|
61
|
+
return `<pivotField axis="${axis}"${dataField} showAll="0"><items count="${items.length + 1}">${entries}</items></pivotField>`;
|
|
71
62
|
}
|
|
72
|
-
|
|
73
|
-
return '<pivotField dataField="1" showAll="0"/>';
|
|
74
|
-
return '<pivotField showAll="0"/>';
|
|
63
|
+
return `<pivotField${dataField} showAll="0"/>`;
|
|
75
64
|
})
|
|
76
65
|
.join('');
|
|
77
66
|
const rowFields = table.rowFields.map((index) => `<field x="${index}"/>`).join('');
|
|
@@ -102,12 +91,25 @@ function dataFieldXml(table) {
|
|
|
102
91
|
return (`<dataField name="${escapeAttr(caption)}" fld="${table.valueField}"${subtotal} ` +
|
|
103
92
|
`baseField="0" baseItem="0"/>`);
|
|
104
93
|
}
|
|
94
|
+
function sharedItemsKinds(field) {
|
|
95
|
+
const { containsString, containsBlank, numeric } = field;
|
|
96
|
+
const hasNumber = numeric !== null;
|
|
97
|
+
return ((!containsString && !containsBlank && hasNumber ? ' containsSemiMixedTypes="0"' : '') +
|
|
98
|
+
(containsString ? '' : ' containsString="0"') +
|
|
99
|
+
(containsBlank ? ' containsBlank="1"' : '') +
|
|
100
|
+
(containsString && hasNumber ? ' containsMixedTypes="1"' : '') +
|
|
101
|
+
(numeric === null
|
|
102
|
+
? ''
|
|
103
|
+
: ' containsNumber="1"' +
|
|
104
|
+
(numeric.allInteger ? ' containsInteger="1"' : '') +
|
|
105
|
+
` minValue="${numberText(numeric.min)}" maxValue="${numberText(numeric.max)}"`));
|
|
106
|
+
}
|
|
105
107
|
function sharedItemXml(item) {
|
|
106
108
|
switch (item.kind) {
|
|
107
109
|
case 'string':
|
|
108
110
|
return `<s v="${escapeAttr(item.value)}"/>`;
|
|
109
111
|
case 'number':
|
|
110
|
-
return `<n v="${item.value}"/>`;
|
|
112
|
+
return `<n v="${numberText(item.value)}"/>`;
|
|
111
113
|
case 'blank':
|
|
112
114
|
return '<m/>';
|
|
113
115
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type SheetSplice } from '../../core/formula-references.ts';
|
|
2
|
+
/**
|
|
3
|
+
* A preserved part's bytes with the references it spells moved through `splices`, in order, or the
|
|
4
|
+
* same bytes when the part is of a kind that spells none or nothing in it moved.
|
|
5
|
+
*/
|
|
6
|
+
export declare function splicePreservedPart(bytes: Uint8Array, contentType: string, splices: readonly SheetSplice[]): Uint8Array;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { spliceFormula } from '../../core/formula-references.js';
|
|
2
|
+
import { splicePivotSource } from '../../core/pivot-table.js';
|
|
3
|
+
import { XmlParseError } from '../../xml/errors.js';
|
|
4
|
+
import { elementRange, elementRanges, openElements } from '../../xml/xml-read.js';
|
|
5
|
+
import { decodeEntities } from '../../xml/xml-scan.js';
|
|
6
|
+
import { escapeText, startTag } from '../../xml/xml.js';
|
|
7
|
+
import { relAttr } from '../opc/namespaces.js';
|
|
8
|
+
const chartReferences = (xml, splices) => {
|
|
9
|
+
const edits = [];
|
|
10
|
+
for (const range of elementRanges(xml, 'f')) {
|
|
11
|
+
const content = xml.slice(range.contentStart, range.contentEnd);
|
|
12
|
+
if (content.includes('<'))
|
|
13
|
+
continue;
|
|
14
|
+
const formula = decodeEntities(content);
|
|
15
|
+
const moved = splices.reduce((text, edit) => spliceFormula(text, undefined, edit), formula);
|
|
16
|
+
if (moved !== formula) {
|
|
17
|
+
edits.push({ start: range.contentStart, end: range.contentEnd, text: escapeText(moved) });
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return edits;
|
|
21
|
+
};
|
|
22
|
+
const pivotCacheSource = (xml, splices) => {
|
|
23
|
+
for (const source of openElements(xml, 'worksheetSource')) {
|
|
24
|
+
if (relAttr(source.scope, source.attrs, 'id') !== undefined)
|
|
25
|
+
return [];
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
const range = elementRange(xml, ['cacheSource', 'worksheetSource']);
|
|
29
|
+
const { sheet, ref } = range?.attrs ?? {};
|
|
30
|
+
if (range === undefined || sheet === undefined || ref === undefined)
|
|
31
|
+
return [];
|
|
32
|
+
const moved = splices.reduce((text, edit) => splicePivotSource(sheet, text, edit), ref);
|
|
33
|
+
if (moved === ref)
|
|
34
|
+
return [];
|
|
35
|
+
return [
|
|
36
|
+
{
|
|
37
|
+
start: range.start,
|
|
38
|
+
end: range.end,
|
|
39
|
+
text: startTag(range.name, { ...range.attrs, ref: moved }, true),
|
|
40
|
+
},
|
|
41
|
+
];
|
|
42
|
+
};
|
|
43
|
+
const REWRITERS = new Map([
|
|
44
|
+
['application/vnd.openxmlformats-officedocument.drawingml.chart+xml', chartReferences],
|
|
45
|
+
['application/vnd.ms-office.chartex+xml', chartReferences],
|
|
46
|
+
[
|
|
47
|
+
'application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml',
|
|
48
|
+
pivotCacheSource,
|
|
49
|
+
],
|
|
50
|
+
].map(([type, rewrite]) => [type.toLowerCase(), rewrite]));
|
|
51
|
+
export function splicePreservedPart(bytes, contentType, splices) {
|
|
52
|
+
if (splices.length === 0)
|
|
53
|
+
return bytes;
|
|
54
|
+
const rewrite = REWRITERS.get(contentType.toLowerCase());
|
|
55
|
+
if (rewrite === undefined)
|
|
56
|
+
return bytes;
|
|
57
|
+
const source = new TextDecoder().decode(bytes);
|
|
58
|
+
let edits;
|
|
59
|
+
try {
|
|
60
|
+
edits = rewrite(source, splices);
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
if (error instanceof XmlParseError)
|
|
64
|
+
return bytes;
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
if (edits.length === 0)
|
|
68
|
+
return bytes;
|
|
69
|
+
let xml = source;
|
|
70
|
+
for (const edit of edits.toSorted((a, b) => b.start - a.start)) {
|
|
71
|
+
xml = xml.slice(0, edit.start) + edit.text + xml.slice(edit.end);
|
|
72
|
+
}
|
|
73
|
+
return new TextEncoder().encode(xml);
|
|
74
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Worksheet } from '../../core/worksheet.ts';
|
|
2
|
+
/** One `<comment>` read back from a comments part. */
|
|
3
|
+
export interface ParsedComment {
|
|
4
|
+
readonly text: string;
|
|
5
|
+
/**
|
|
6
|
+
* The thread head id this comment is the legacy fallback for, read off its synthetic `tc={headId}`
|
|
7
|
+
* author; absent for a user's own note.
|
|
8
|
+
*/
|
|
9
|
+
readonly threadId?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Parse a `comments{n}.xml` part into a map of A1 reference → comment. Text runs within one comment are
|
|
13
|
+
* concatenated; an author-name run is Excel's own convention and is not stripped, so a note reads back
|
|
14
|
+
* as exactly the text that was written.
|
|
15
|
+
*/
|
|
16
|
+
export declare function parseComments(xml: string): Map<string, ParsedComment>;
|
|
17
|
+
/**
|
|
18
|
+
* Apply a parsed comments part onto a sheet's cells as notes, addressing each by its A1 reference.
|
|
19
|
+
*
|
|
20
|
+
* A thread's legacy fallback is not a note and does not become one: its text is boilerplate wrapping a
|
|
21
|
+
* copy of the conversation, so surfacing it as `cell.note` hands the caller garbage, and on write it
|
|
22
|
+
* would be re-emitted as a plain note, destroying the `tc=`/`xr:uid` binding and leaving Excel unable to
|
|
23
|
+
* see the thread at all.
|
|
24
|
+
*
|
|
25
|
+
* Suppressed only for a conversation the reader actually holds: a file whose thread part is missing or
|
|
26
|
+
* damaged has nothing else left, so there the boilerplate is kept rather than the content lost. Call
|
|
27
|
+
* after the sheet's threads are restored, since that is what this reads to decide.
|
|
28
|
+
*/
|
|
29
|
+
export declare function applyNotes(sheet: Worksheet, comments: ReadonlyMap<string, ParsedComment>): void;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { tryDecodeCellRef } from '../../core/address.js';
|
|
2
|
+
import { numInteger } from '../../xml/xml-attrs.js';
|
|
3
|
+
import { parseXml, TextCapture } from '../../xml/xml-read.js';
|
|
4
|
+
import { localName } from '../../xml/xml-scan.js';
|
|
5
|
+
import { RunAccumulator } from './read-rich-runs.js';
|
|
6
|
+
const COMMENT_EMPTY_CLOSES = new Set(['author', 'text']);
|
|
7
|
+
const THREAD_AUTHOR_PREFIX = 'tc=';
|
|
8
|
+
export function parseComments(xml) {
|
|
9
|
+
const comments = new Map();
|
|
10
|
+
const authors = [];
|
|
11
|
+
let currentRef;
|
|
12
|
+
let currentAuthorId;
|
|
13
|
+
const author = new TextCapture('author');
|
|
14
|
+
const bodyRuns = new RunAccumulator({ container: 'text', readRuns: false });
|
|
15
|
+
let body = '';
|
|
16
|
+
parseXml(xml, {
|
|
17
|
+
onOpen(name, attrs, selfClosing) {
|
|
18
|
+
const local = localName(name);
|
|
19
|
+
if (local === 'comment') {
|
|
20
|
+
currentRef = attrs.ref;
|
|
21
|
+
currentAuthorId = attrs.authorId;
|
|
22
|
+
body = '';
|
|
23
|
+
}
|
|
24
|
+
if (!bodyRuns.open(local, attrs, selfClosing))
|
|
25
|
+
author.open(local, selfClosing);
|
|
26
|
+
},
|
|
27
|
+
onText(text) {
|
|
28
|
+
bodyRuns.text(text);
|
|
29
|
+
author.text(text);
|
|
30
|
+
},
|
|
31
|
+
onClose(name) {
|
|
32
|
+
const local = localName(name);
|
|
33
|
+
const bodyClose = bodyRuns.close(local);
|
|
34
|
+
if (bodyClose === 'container') {
|
|
35
|
+
body = bodyRuns.plainText;
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (bodyClose === 'claimed')
|
|
39
|
+
return;
|
|
40
|
+
const text = author.close(local);
|
|
41
|
+
if (local === 'author') {
|
|
42
|
+
authors.push(text ?? '');
|
|
43
|
+
}
|
|
44
|
+
else if (local === 'comment' && currentRef !== undefined) {
|
|
45
|
+
const threadId = threadIdOf(authors[numInteger(currentAuthorId, 0) ?? -1]);
|
|
46
|
+
comments.set(currentRef, {
|
|
47
|
+
text: body,
|
|
48
|
+
...(threadId !== undefined ? { threadId } : {}),
|
|
49
|
+
});
|
|
50
|
+
currentRef = undefined;
|
|
51
|
+
currentAuthorId = undefined;
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
}, { closeEmptyElements: COMMENT_EMPTY_CLOSES });
|
|
55
|
+
return comments;
|
|
56
|
+
}
|
|
57
|
+
function threadIdOf(author) {
|
|
58
|
+
if (author === undefined || !author.startsWith(THREAD_AUTHOR_PREFIX))
|
|
59
|
+
return undefined;
|
|
60
|
+
const id = author.slice(THREAD_AUTHOR_PREFIX.length);
|
|
61
|
+
return id === '' ? undefined : id;
|
|
62
|
+
}
|
|
63
|
+
export function applyNotes(sheet, comments) {
|
|
64
|
+
const headIds = new Set(sheet.commentThreads.flatMap((thread) => {
|
|
65
|
+
const head = thread.comments[0];
|
|
66
|
+
return head === undefined ? [] : [head.id];
|
|
67
|
+
}));
|
|
68
|
+
for (const [ref, comment] of comments) {
|
|
69
|
+
if (comment.threadId !== undefined && headIds.has(comment.threadId))
|
|
70
|
+
continue;
|
|
71
|
+
if (tryDecodeCellRef(ref) === undefined)
|
|
72
|
+
continue;
|
|
73
|
+
sheet.getCell(ref).note = comment.text;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { CommentThread } from '../../core/comment-thread.ts';
|
|
2
|
+
import { Workbook } from '../../core/workbook.ts';
|
|
3
|
+
import type { Worksheet } from '../../core/worksheet.ts';
|
|
4
|
+
import type { CollectingPass } from '../../xml/xml-read.ts';
|
|
5
|
+
import { type PackageAccessors, type PartRelationships } from '../opc/read-opc.ts';
|
|
6
|
+
import { type ParsedComment } from './read-comments.ts';
|
|
7
|
+
/**
|
|
8
|
+
* What every part reader in one package read shares: the inflated package, the model being built, the
|
|
9
|
+
* content-type resolver a preserved part's closure is captured through, and the interning map that
|
|
10
|
+
* makes one workbook image per media part however many places point at it.
|
|
11
|
+
*
|
|
12
|
+
* None of the four varies within a read, and threading them positionally is what let `internImage`
|
|
13
|
+
* end up taking the same three in a different order from both of its callers. What genuinely varies
|
|
14
|
+
* -- a part's relationships, the sheet being filled -- stays a positional argument, and the sheet's
|
|
15
|
+
* rels in particular are deliberately *not* in here: they are per sheet, and a stale copy of them
|
|
16
|
+
* would send every sheet's parts to the first sheet's.
|
|
17
|
+
*/
|
|
18
|
+
export interface PackageReadContext {
|
|
19
|
+
readonly pkg: PackageAccessors;
|
|
20
|
+
readonly workbook: Workbook;
|
|
21
|
+
readonly contentTypeOf: (path: string) => string;
|
|
22
|
+
readonly imageIdByMediaPath: Map<string, number>;
|
|
23
|
+
}
|
|
24
|
+
export declare function readSheetComments(sheetRels: PartRelationships): Map<string, ParsedComment> | undefined;
|
|
25
|
+
export declare function readWorkbookPersons({ workbook }: PackageReadContext, workbookRels: PartRelationships): void;
|
|
26
|
+
export declare function readWorkbookTheme(context: PackageReadContext, workbookRels: PartRelationships): void;
|
|
27
|
+
export declare function readSheetCommentThreads({ workbook }: PackageReadContext, sheetRels: PartRelationships): CommentThread[];
|
|
28
|
+
export declare function readSheetPrinterSettings(sheetRels: PartRelationships): Uint8Array | undefined;
|
|
29
|
+
export declare function readSheetImages(context: PackageReadContext, sheetRels: PartRelationships, sheet: Worksheet): void;
|
|
30
|
+
export declare function readSheetBackground(context: PackageReadContext, sheetRels: PartRelationships, sheet: Worksheet): void;
|
|
31
|
+
export declare function readSheetPreservedReferences(context: PackageReadContext, sheetRels: PartRelationships, referenceRelIds: WorksheetReferenceRelIds, sheet: Worksheet): void;
|
|
32
|
+
export declare function readWorkbookPreservedReferences(context: PackageReadContext, workbookRels: PartRelationships, registrations: WorkbookRegistrations): void;
|
|
33
|
+
export declare function readRootPreservedReferences(context: PackageReadContext): void;
|
|
34
|
+
/** The two `<pivotCaches>`/`<externalReferences>` registries a preserved reference is wired by,
|
|
35
|
+
* gathered from the workbook part's own scan rather than from two more of it. */
|
|
36
|
+
export interface WorkbookRegistrations {
|
|
37
|
+
readonly cacheIdByRelId: ReadonlyMap<string, string>;
|
|
38
|
+
readonly externalIndexByRelId: ReadonlyMap<string, number>;
|
|
39
|
+
}
|
|
40
|
+
export declare function pivotCacheRegistrationsPass(): CollectingPass<ReadonlyMap<string, string>>;
|
|
41
|
+
export declare function externalReferenceRegistrationsPass(): CollectingPass<ReadonlyMap<string, number>>;
|
|
42
|
+
/** The `r:id` each of the two element-wired references carries, or `undefined` where absent. */
|
|
43
|
+
export type WorksheetReferenceRelIds = Readonly<Partial<Record<'drawing' | 'legacyDrawingHF', string>>>;
|
|
44
|
+
/**
|
|
45
|
+
* A pass gathering the `r:id` of the first `<drawing>` and `<legacyDrawingHF>` in a worksheet.
|
|
46
|
+
*
|
|
47
|
+
* These two references live in the worksheet body rather than being distinguishable by relationship
|
|
48
|
+
* Type, since a header/footer VML and a comment VML share the `vmlDrawing` type, so the specific
|
|
49
|
+
* relationship is found by reading the element's own `r:id`.
|
|
50
|
+
*
|
|
51
|
+
* A pass rather than its own scan, because the sheet part is the largest in a package by a wide
|
|
52
|
+
* margin and this ran a full extra scan of it, once or twice per sheet, for two attributes.
|
|
53
|
+
* `readSheet` already builds a multi-pass single parse of it whose own comment records that five
|
|
54
|
+
* separate scans "spent 45% of a large file's read on four scans that matched no element"; this was
|
|
55
|
+
* the sixth scan, added later, that the same argument covers.
|
|
56
|
+
*/
|
|
57
|
+
export declare function worksheetReferencePass(): CollectingPass<WorksheetReferenceRelIds>;
|
|
58
|
+
export declare function readSheetTables({ pkg }: PackageReadContext, sheetRels: PartRelationships, sheet: Worksheet, definedNames: ReadonlySet<string>): void;
|
|
59
|
+
export declare function readSheetPivotTables({ pkg }: PackageReadContext, sheetRels: PartRelationships, sheet: Worksheet): void;
|