@shbernal/ts-xlsx 3.1.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 +30 -23
- package/dist/bytes.d.ts +3 -2
- package/dist/core/address.d.ts +36 -0
- package/dist/core/address.js +47 -9
- package/dist/core/array-formula-ranges.d.ts +33 -0
- package/dist/core/array-formula-ranges.js +62 -0
- package/dist/core/color-resolution.js +2 -2
- package/dist/core/column.js +3 -2
- package/dist/core/comment-thread.d.ts +7 -3
- package/dist/core/conditional-formatting-overlay.d.ts +9 -0
- package/dist/core/conditional-formatting-overlay.js +28 -1
- package/dist/core/conditional-formatting.d.ts +98 -10
- package/dist/core/conditional-formatting.js +42 -1
- package/dist/core/data-validation-overlay.d.ts +5 -0
- package/dist/core/data-validation-overlay.js +15 -0
- package/dist/core/data-validation.d.ts +16 -0
- package/dist/core/data-validation.js +15 -0
- package/dist/core/date-format.js +14 -4
- package/dist/core/date.js +1 -1
- 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 +51 -18
- package/dist/core/formula.js +77 -86
- 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 +28 -0
- package/dist/core/grid-edits.js +193 -25
- package/dist/core/hyperlink.d.ts +39 -0
- package/dist/core/hyperlink.js +88 -0
- package/dist/core/image.d.ts +36 -5
- package/dist/core/image.js +9 -0
- package/dist/core/internal.d.ts +3 -1
- package/dist/core/limits.d.ts +5 -3
- package/dist/core/merge.d.ts +13 -0
- package/dist/core/merge.js +15 -1
- package/dist/core/page-setup.d.ts +26 -11
- package/dist/core/pivot-table.d.ts +49 -10
- package/dist/core/pivot-table.js +55 -21
- package/dist/core/protection.d.ts +66 -4
- package/dist/core/protection.js +4 -0
- package/dist/core/row.js +3 -2
- package/dist/core/structured-reference.d.ts +7 -0
- package/dist/core/structured-reference.js +7 -0
- package/dist/core/table.d.ts +38 -8
- package/dist/core/table.js +37 -7
- package/dist/core/theme.d.ts +2 -0
- package/dist/core/theme.js +4 -1
- package/dist/core/value.d.ts +51 -28
- package/dist/core/value.js +15 -12
- package/dist/core/workbook-styles.d.ts +17 -1
- package/dist/core/workbook-styles.js +4 -0
- package/dist/core/workbook-theme.js +5 -3
- package/dist/core/workbook.d.ts +20 -3
- package/dist/core/workbook.js +26 -5
- package/dist/core/worksheet-merges.d.ts +9 -4
- package/dist/core/worksheet-merges.js +17 -11
- package/dist/core/worksheet-model.d.ts +1 -1
- package/dist/core/worksheet-model.js +4 -0
- package/dist/core/worksheet-pictures.d.ts +3 -3
- package/dist/core/worksheet-pictures.js +7 -6
- package/dist/core/worksheet.d.ts +110 -21
- package/dist/core/worksheet.js +174 -49
- package/dist/entries/core.d.ts +7 -5
- package/dist/entries/core.js +4 -3
- package/dist/entries/errors.d.ts +1 -0
- package/dist/entries/errors.js +1 -0
- package/dist/errors.js +1 -1
- 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 +14 -2
- package/dist/io/csv/delimiter.js +12 -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 +46 -24
- package/dist/io/csv/write.d.ts +10 -3
- package/dist/io/csv/write.js +16 -7
- package/dist/io/opc/part-paths.js +4 -4
- package/dist/io/opc/read-opc.d.ts +4 -0
- package/dist/io/opc/read-opc.js +10 -4
- 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/{xlsx → read-policy}/column-budget.js +4 -8
- 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/xlsb/formula.js +4 -2
- package/dist/io/xlsb/ptg-functions.js +110 -0
- package/dist/io/xlsb/read-metadata.d.ts +7 -0
- package/dist/io/xlsb/read-metadata.js +69 -0
- package/dist/io/xlsb/read-worksheet.d.ts +6 -0
- package/dist/io/xlsb/read-worksheet.js +95 -37
- package/dist/io/xlsb/read.js +26 -8
- package/dist/io/xlsb/record-types.d.ts +13 -0
- package/dist/io/xlsb/record-types.js +13 -0
- 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 +20 -12
- package/dist/io/xlsx/cell-accumulator.js +49 -19
- 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 +16 -4
- package/dist/io/xlsx/cell-value.js +38 -21
- package/dist/io/xlsx/color-xml.js +1 -1
- 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 +3 -28
- package/dist/io/xlsx/comments.js +3 -65
- package/dist/io/xlsx/conditional-formatting.d.ts +42 -25
- package/dist/io/xlsx/conditional-formatting.js +424 -129
- package/dist/io/xlsx/data-validation.d.ts +12 -7
- package/dist/io/xlsx/data-validation.js +41 -29
- package/dist/io/xlsx/edit-vba.js +43 -36
- package/dist/io/xlsx/hyperlinks.d.ts +16 -50
- package/dist/io/xlsx/hyperlinks.js +8 -37
- package/dist/io/xlsx/images.d.ts +49 -16
- package/dist/io/xlsx/images.js +145 -44
- package/dist/io/xlsx/namespaces.d.ts +8 -0
- package/dist/io/xlsx/namespaces.js +4 -0
- package/dist/io/xlsx/package-plan.d.ts +48 -15
- package/dist/io/xlsx/package-plan.js +46 -9
- 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 +23 -22
- 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 +2 -2
- package/dist/io/xlsx/read-parts.js +44 -37
- package/dist/io/xlsx/read-pivot.js +9 -3
- package/dist/io/xlsx/read-rich-runs.d.ts +8 -5
- package/dist/io/xlsx/read-rich-runs.js +18 -1
- package/dist/io/xlsx/read-rows.d.ts +18 -7
- package/dist/io/xlsx/read-rows.js +47 -37
- package/dist/io/xlsx/read-styles.d.ts +0 -25
- package/dist/io/xlsx/read-styles.js +39 -34
- package/dist/io/xlsx/read-workbook-xml.d.ts +6 -0
- package/dist/io/xlsx/read-workbook-xml.js +9 -4
- package/dist/io/xlsx/read-worksheet.d.ts +8 -3
- package/dist/io/xlsx/read-worksheet.js +45 -23
- package/dist/io/xlsx/read.d.ts +0 -4
- package/dist/io/xlsx/read.js +17 -21
- package/dist/io/xlsx/relationships.d.ts +3 -0
- package/dist/io/xlsx/relationships.js +4 -0
- package/dist/io/xlsx/row-position.d.ts +3 -1
- package/dist/io/xlsx/row-xml.d.ts +18 -7
- package/dist/io/xlsx/row-xml.js +54 -35
- package/dist/io/xlsx/sheet-properties.js +68 -42
- 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 +0 -2
- package/dist/io/xlsx/style-elements.js +9 -29
- package/dist/io/xlsx/styles.d.ts +5 -0
- package/dist/io/xlsx/styles.js +11 -5
- package/dist/io/xlsx/tables.d.ts +10 -2
- package/dist/io/xlsx/tables.js +49 -42
- package/dist/io/xlsx/theme-xml.d.ts +6 -0
- package/dist/io/xlsx/theme-xml.js +16 -19
- package/dist/io/xlsx/threaded-comments.d.ts +6 -1
- package/dist/io/xlsx/threaded-comments.js +14 -7
- package/dist/io/xlsx/workbook-xml.d.ts +13 -24
- package/dist/io/xlsx/workbook-xml.js +15 -33
- package/dist/io/xlsx/worksheet-xml.d.ts +5 -2
- package/dist/io/xlsx/worksheet-xml.js +37 -69
- package/dist/io/xlsx/write-stream.d.ts +28 -6
- package/dist/io/xlsx/write-stream.js +42 -17
- package/dist/io/xlsx/write.d.ts +11 -4
- package/dist/io/xlsx/write.js +153 -90
- package/dist/rel-type.d.ts +9 -0
- package/dist/rel-type.js +3 -0
- package/dist/vba/bytes.d.ts +2 -0
- package/dist/vba/bytes.js +11 -0
- package/dist/vba/cfb-format.d.ts +47 -0
- package/dist/vba/cfb-format.js +34 -0
- package/dist/vba/cfb-writer.d.ts +1 -2
- package/dist/vba/cfb-writer.js +33 -36
- package/dist/vba/cfb.d.ts +9 -4
- package/dist/vba/cfb.js +55 -43
- package/dist/vba/dir-records.d.ts +6 -0
- package/dist/vba/dir-records.js +10 -2
- package/dist/vba/errors.d.ts +4 -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 +15 -6
- package/dist/vba/project-editor.js +102 -87
- package/dist/vba/project.d.ts +4 -0
- package/dist/vba/project.js +12 -6
- package/dist/vba/vba-encoding.d.ts +4 -10
- package/dist/vba/vba-encoding.js +2 -15
- package/dist/xml/xml-read.d.ts +29 -1
- package/dist/xml/xml-read.js +56 -0
- package/dist/xml/xml-scan.d.ts +13 -0
- package/dist/xml/xml-scan.js +58 -7
- package/dist/xml/xml.d.ts +33 -1
- package/dist/xml/xml.js +22 -0
- package/package.json +5 -4
- package/skills/ts-xlsx-upstream/SKILL.md +21 -13
- package/dist/core/modern-functions.d.ts +0 -1
- package/dist/io/xlsx/cell-style-resolution.d.ts +0 -25
- package/dist/io/xlsx/cell-style-resolution.js +0 -29
- package/dist/io/xlsx/column-budget.d.ts +0 -26
- package/dist/io/xlsx/read-repair.d.ts +0 -26
- package/dist/io/xlsx/read-repair.js +0 -39
package/dist/io/xlsx/images.js
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { isImageEditAs, isOneCellAnchor, } from '../../core/image.js';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { assertWritableNumber, AuthoringError } from '../../errors.js';
|
|
3
|
+
import { enumToken, numFinite, numInteger } from '../../xml/xml-attrs.js';
|
|
4
|
+
import { elementRange, openElements, parseXml, TextCapture } from '../../xml/xml-read.js';
|
|
4
5
|
import { localName } from '../../xml/xml-scan.js';
|
|
5
|
-
import { checkedToken, numAttr, numberText, XML_DECLARATION } from '../../xml/xml.js';
|
|
6
|
+
import { checkedToken, escapeAttr, intAttr, numAttr, numberText, textAttr, XML_DECLARATION, } from '../../xml/xml.js';
|
|
6
7
|
import { relAttr, RELATIONSHIPS_NS } from '../opc/namespaces.js';
|
|
7
|
-
import { relationship, relationshipsPart } from '../opc/rels.js';
|
|
8
8
|
import { DRAWINGML_NS, XDR_NS } from './namespaces.js';
|
|
9
|
-
const IMAGE_REL_TYPE = `${RELATIONSHIPS_NS}/image`;
|
|
10
9
|
const IMAGE_CONTENT_TYPES = new Map([
|
|
11
10
|
['png', 'image/png'],
|
|
12
11
|
['jpg', 'image/jpeg'],
|
|
@@ -23,81 +22,185 @@ export function imageContentType(extension) {
|
|
|
23
22
|
const ext = extension.toLowerCase();
|
|
24
23
|
return IMAGE_CONTENT_TYPES.get(ext) ?? `image/${ext}`;
|
|
25
24
|
}
|
|
25
|
+
const ANCHOR_NAMESPACES = ` xmlns:xdr="${XDR_NS}" xmlns:a="${DRAWINGML_NS}" xmlns:r="${RELATIONSHIPS_NS}"`;
|
|
26
26
|
export function drawingXml(images) {
|
|
27
|
-
const anchors = images.map((image, i) => anchorXml(image, i + 1)).join('');
|
|
28
|
-
return
|
|
29
|
-
`<xdr:wsDr xmlns:xdr="${XDR_NS}" xmlns:a="${DRAWINGML_NS}" xmlns:r="${RELATIONSHIPS_NS}">` +
|
|
30
|
-
anchors +
|
|
31
|
-
'</xdr:wsDr>');
|
|
27
|
+
const anchors = images.map((image, i) => anchorXml(image, i + 1, '')).join('');
|
|
28
|
+
return `${XML_DECLARATION}<xdr:wsDr${ANCHOR_NAMESPACES}>${anchors}</xdr:wsDr>`;
|
|
32
29
|
}
|
|
33
|
-
function
|
|
30
|
+
export function mergePictures(drawing, images) {
|
|
31
|
+
const root = elementRange(drawing, ['wsDr']);
|
|
32
|
+
if (root === undefined) {
|
|
33
|
+
throw new AuthoringError('cannot add a picture to a kept drawing that has no <wsDr> root');
|
|
34
|
+
}
|
|
35
|
+
let lastId = 0;
|
|
36
|
+
for (const { attrs } of openElements(drawing, 'cNvPr')) {
|
|
37
|
+
lastId = Math.max(lastId, numInteger(attrs.id, 0) ?? 0);
|
|
38
|
+
}
|
|
39
|
+
const anchors = images
|
|
40
|
+
.map((image, i) => anchorXml(image, lastId + i + 1, ANCHOR_NAMESPACES))
|
|
41
|
+
.join('');
|
|
42
|
+
if (root.contentStart === root.end) {
|
|
43
|
+
const open = drawing.slice(root.start, root.end).replace(/\s*\/>$/, '>');
|
|
44
|
+
return `${drawing.slice(0, root.start)}${open}${anchors}</${root.name}>${drawing.slice(root.end)}`;
|
|
45
|
+
}
|
|
46
|
+
return drawing.slice(0, root.contentEnd) + anchors + drawing.slice(root.contentEnd);
|
|
47
|
+
}
|
|
48
|
+
function anchorXml(image, id, namespaces) {
|
|
34
49
|
const { anchor } = image;
|
|
50
|
+
const pic = picXml(image, id, anchor.rotation);
|
|
35
51
|
return isOneCellAnchor(anchor)
|
|
36
|
-
? oneCellAnchorXml(anchor.from, anchor.ext,
|
|
37
|
-
: twoCellAnchorXml(anchor.from, anchor.to, anchor.editAs ?? 'oneCell',
|
|
52
|
+
? oneCellAnchorXml(anchor.from, anchor.ext, pic, namespaces)
|
|
53
|
+
: twoCellAnchorXml(anchor.from, anchor.to, anchor.editAs ?? 'oneCell', pic, namespaces);
|
|
38
54
|
}
|
|
39
|
-
function twoCellAnchorXml(from, to, editAs,
|
|
40
|
-
return (`<xdr:twoCellAnchor editAs="${checkedToken(editAs, isImageEditAs, 'image anchor edit mode')}">` +
|
|
55
|
+
function twoCellAnchorXml(from, to, editAs, pic, namespaces) {
|
|
56
|
+
return (`<xdr:twoCellAnchor${namespaces} editAs="${checkedToken(editAs, isImageEditAs, 'image anchor edit mode')}">` +
|
|
41
57
|
`<xdr:from>${anchorPointXml(from)}</xdr:from>` +
|
|
42
58
|
`<xdr:to>${anchorPointXml(to)}</xdr:to>` +
|
|
43
|
-
|
|
59
|
+
pic +
|
|
44
60
|
'<xdr:clientData/>' +
|
|
45
61
|
'</xdr:twoCellAnchor>');
|
|
46
62
|
}
|
|
47
|
-
function oneCellAnchorXml(from, ext,
|
|
48
|
-
return (
|
|
63
|
+
function oneCellAnchorXml(from, ext, pic, namespaces) {
|
|
64
|
+
return (`<xdr:oneCellAnchor${namespaces}>` +
|
|
49
65
|
`<xdr:from>${anchorPointXml(from)}</xdr:from>` +
|
|
50
66
|
`<xdr:ext${numAttr('cx', ext.cx)}${numAttr('cy', ext.cy)}/>` +
|
|
51
|
-
|
|
67
|
+
pic +
|
|
52
68
|
'<xdr:clientData/>' +
|
|
53
69
|
'</xdr:oneCellAnchor>');
|
|
54
70
|
}
|
|
55
|
-
function picXml(
|
|
71
|
+
function picXml(image, id, rotation) {
|
|
72
|
+
const { embedId, properties, hyperlinkId } = image;
|
|
56
73
|
const xfrm = rotation !== undefined ? `<a:xfrm${numAttr('rot', rotation)}/>` : '';
|
|
74
|
+
const attributes = textAttr('descr', properties.description) + textAttr('title', properties.title);
|
|
75
|
+
const link = hyperlinkId === undefined
|
|
76
|
+
? ''
|
|
77
|
+
: `<a:hlinkClick r:id="${escapeAttr(hyperlinkId)}"` +
|
|
78
|
+
`${textAttr('tooltip', properties.hyperlink?.tooltip)}/>`;
|
|
79
|
+
const cNvPr = link === ''
|
|
80
|
+
? `<xdr:cNvPr id="${id}" name="Picture ${id}"${attributes}/>`
|
|
81
|
+
: `<xdr:cNvPr id="${id}" name="Picture ${id}"${attributes}>${link}</xdr:cNvPr>`;
|
|
57
82
|
return ('<xdr:pic>' +
|
|
58
|
-
`<xdr:nvPicPr
|
|
83
|
+
`<xdr:nvPicPr>${cNvPr}` +
|
|
59
84
|
'<xdr:cNvPicPr><a:picLocks noChangeAspect="1"/></xdr:cNvPicPr></xdr:nvPicPr>' +
|
|
60
85
|
`<xdr:blipFill><a:blip r:embed="${embedId}"/>` +
|
|
86
|
+
srcRectXml(properties.crop) +
|
|
61
87
|
'<a:stretch><a:fillRect/></a:stretch></xdr:blipFill>' +
|
|
62
88
|
`<xdr:spPr>${xfrm}<a:prstGeom prst="rect"><a:avLst/></a:prstGeom></xdr:spPr>` +
|
|
63
89
|
'</xdr:pic>');
|
|
64
90
|
}
|
|
91
|
+
const CROP_UNITS = 100_000;
|
|
92
|
+
const INT_MAX = 2_147_483_647;
|
|
93
|
+
function srcRectXml(crop) {
|
|
94
|
+
if (crop === undefined)
|
|
95
|
+
return '';
|
|
96
|
+
const edge = (name, fraction) => {
|
|
97
|
+
if (fraction === undefined)
|
|
98
|
+
return '';
|
|
99
|
+
assertWritableNumber(fraction);
|
|
100
|
+
const units = Math.round(fraction * CROP_UNITS);
|
|
101
|
+
if (units > INT_MAX) {
|
|
102
|
+
throw new AuthoringError(`cannot write the picture crop ${name}="${units}": past xsd:int`);
|
|
103
|
+
}
|
|
104
|
+
return units === 0 ? '' : intAttr(name, units, -INT_MAX - 1);
|
|
105
|
+
};
|
|
106
|
+
const attributes = edge('l', crop.left) + edge('t', crop.top) + edge('r', crop.right) + edge('b', crop.bottom);
|
|
107
|
+
return attributes === '' ? '' : `<a:srcRect${attributes}/>`;
|
|
108
|
+
}
|
|
109
|
+
function cropEdge(value) {
|
|
110
|
+
if (value === undefined)
|
|
111
|
+
return undefined;
|
|
112
|
+
const percent = value.endsWith('%') ? numFinite(value.slice(0, -1)) : undefined;
|
|
113
|
+
const units = percent === undefined ? numInteger(value) : Math.round(percent * 1000);
|
|
114
|
+
if (units === undefined || units === 0 || units > INT_MAX || units < -INT_MAX - 1) {
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
return units / CROP_UNITS;
|
|
118
|
+
}
|
|
65
119
|
function anchorPointXml(point) {
|
|
66
120
|
return (`<xdr:col>${numberText(point.col)}</xdr:col>` +
|
|
67
121
|
`<xdr:colOff>${numberText(point.colOff ?? 0)}</xdr:colOff>` +
|
|
68
122
|
`<xdr:row>${numberText(point.row)}</xdr:row>` +
|
|
69
123
|
`<xdr:rowOff>${numberText(point.rowOff ?? 0)}</xdr:rowOff>`);
|
|
70
124
|
}
|
|
71
|
-
export function drawingRelsXml(mediaTargets) {
|
|
72
|
-
return relationshipsPart(mediaTargets.map((target, i) => relationship(`rId${i + 1}`, IMAGE_REL_TYPE, target)));
|
|
73
|
-
}
|
|
74
125
|
function blankPoint() {
|
|
75
126
|
return { col: 0, row: 0, colOff: 0, rowOff: 0 };
|
|
76
127
|
}
|
|
128
|
+
const UNMODELED_CONTENT = new Set([
|
|
129
|
+
'graphicFrame',
|
|
130
|
+
'sp',
|
|
131
|
+
'cxnSp',
|
|
132
|
+
'grpSp',
|
|
133
|
+
'contentPart',
|
|
134
|
+
'absoluteAnchor',
|
|
135
|
+
'AlternateContent',
|
|
136
|
+
]);
|
|
77
137
|
export function parseDrawing(xml) {
|
|
78
138
|
const anchors = [];
|
|
139
|
+
let fullyModeled = true;
|
|
79
140
|
let from = null;
|
|
80
141
|
let to = null;
|
|
81
142
|
let ext;
|
|
82
143
|
let editAs;
|
|
83
144
|
let rotation;
|
|
84
145
|
let embed;
|
|
146
|
+
let pictures = 0;
|
|
147
|
+
let properties = {};
|
|
85
148
|
let target = null;
|
|
86
149
|
let picDepth = 0;
|
|
87
150
|
const coord = new TextCapture(COORDINATES);
|
|
88
151
|
parseXml(xml, {
|
|
89
152
|
onOpen(name, attrs, selfClosing, scope) {
|
|
90
153
|
const local = localName(name);
|
|
91
|
-
if (local
|
|
154
|
+
if (UNMODELED_CONTENT.has(local)) {
|
|
155
|
+
fullyModeled = false;
|
|
156
|
+
}
|
|
157
|
+
else if (local === 'twoCellAnchor' || local === 'oneCellAnchor') {
|
|
92
158
|
from = blankPoint();
|
|
93
159
|
to = local === 'twoCellAnchor' ? blankPoint() : null;
|
|
94
160
|
ext = undefined;
|
|
95
161
|
rotation = undefined;
|
|
96
162
|
embed = undefined;
|
|
97
|
-
|
|
163
|
+
pictures = 0;
|
|
164
|
+
properties = {};
|
|
165
|
+
editAs =
|
|
166
|
+
local === 'twoCellAnchor'
|
|
167
|
+
? (enumToken(attrs.editAs, isImageEditAs) ?? 'twoCell')
|
|
168
|
+
: undefined;
|
|
98
169
|
}
|
|
99
170
|
else if (local === 'pic') {
|
|
100
171
|
picDepth++;
|
|
172
|
+
pictures++;
|
|
173
|
+
}
|
|
174
|
+
else if (local === 'cNvPr' && picDepth > 0) {
|
|
175
|
+
if (attrs.descr !== undefined && attrs.descr !== '')
|
|
176
|
+
properties.description = attrs.descr;
|
|
177
|
+
if (attrs.title !== undefined && attrs.title !== '')
|
|
178
|
+
properties.title = attrs.title;
|
|
179
|
+
}
|
|
180
|
+
else if (local === 'hlinkClick' && picDepth > 0) {
|
|
181
|
+
const id = relAttr(scope, attrs, 'id');
|
|
182
|
+
if (id !== undefined && id !== '') {
|
|
183
|
+
properties.hyperlinkId = id;
|
|
184
|
+
if (attrs.tooltip !== undefined)
|
|
185
|
+
properties.tooltip = attrs.tooltip;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
else if (local === 'srcRect' && picDepth > 0) {
|
|
189
|
+
const crop = {};
|
|
190
|
+
const left = cropEdge(attrs.l);
|
|
191
|
+
const top = cropEdge(attrs.t);
|
|
192
|
+
const right = cropEdge(attrs.r);
|
|
193
|
+
const bottom = cropEdge(attrs.b);
|
|
194
|
+
if (left !== undefined)
|
|
195
|
+
crop.left = left;
|
|
196
|
+
if (top !== undefined)
|
|
197
|
+
crop.top = top;
|
|
198
|
+
if (right !== undefined)
|
|
199
|
+
crop.right = right;
|
|
200
|
+
if (bottom !== undefined)
|
|
201
|
+
crop.bottom = bottom;
|
|
202
|
+
if (Object.keys(crop).length > 0)
|
|
203
|
+
properties.crop = crop;
|
|
101
204
|
}
|
|
102
205
|
else if (local === 'xfrm' && picDepth > 0) {
|
|
103
206
|
const rot = numFinite(attrs.rot);
|
|
@@ -120,6 +223,8 @@ export function parseDrawing(xml) {
|
|
|
120
223
|
const value = relAttr(scope, attrs, 'embed');
|
|
121
224
|
if (value !== undefined)
|
|
122
225
|
embed = value;
|
|
226
|
+
if (relAttr(scope, attrs, 'link') !== undefined)
|
|
227
|
+
fullyModeled = false;
|
|
123
228
|
}
|
|
124
229
|
else if (target !== null) {
|
|
125
230
|
coord.open(local, selfClosing);
|
|
@@ -143,15 +248,19 @@ export function parseDrawing(xml) {
|
|
|
143
248
|
picDepth--;
|
|
144
249
|
}
|
|
145
250
|
else if (local === 'twoCellAnchor' || local === 'oneCellAnchor') {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
251
|
+
const rot = rotation !== undefined ? { rotation } : {};
|
|
252
|
+
if (from === null || embed === undefined || pictures !== 1) {
|
|
253
|
+
fullyModeled = false;
|
|
254
|
+
}
|
|
255
|
+
else if (to !== null) {
|
|
256
|
+
const mode = editAs !== undefined ? { editAs } : {};
|
|
257
|
+
anchors.push({ from: { ...from }, to: { ...to }, ...mode, ...rot, embed, ...properties });
|
|
258
|
+
}
|
|
259
|
+
else if (ext !== undefined) {
|
|
260
|
+
anchors.push({ from: { ...from }, ext, ...rot, embed, ...properties });
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
263
|
+
fullyModeled = false;
|
|
155
264
|
}
|
|
156
265
|
from = null;
|
|
157
266
|
to = null;
|
|
@@ -159,15 +268,7 @@ export function parseDrawing(xml) {
|
|
|
159
268
|
}
|
|
160
269
|
},
|
|
161
270
|
});
|
|
162
|
-
return anchors;
|
|
163
|
-
}
|
|
164
|
-
const UNMODELED_DRAWING_CONTENT = new Set(['graphicFrame', 'sp', 'cxnSp', 'grpSp']);
|
|
165
|
-
export function drawingHasUnmodeledContent(xml) {
|
|
166
|
-
for (const { local } of openElements(xml)) {
|
|
167
|
-
if (UNMODELED_DRAWING_CONTENT.has(local))
|
|
168
|
-
return true;
|
|
169
|
-
}
|
|
170
|
-
return false;
|
|
271
|
+
return { anchors, fullyModeled };
|
|
171
272
|
}
|
|
172
273
|
const COORDINATES = new Set(['col', 'colOff', 'row', 'rowOff']);
|
|
173
274
|
function setCoordinate(point, coord, value) {
|
|
@@ -54,6 +54,14 @@ export declare const DATABAR_LINK_EXT_URI = "{B025F937-C7B1-47D3-B67F-A62EFF666E
|
|
|
54
54
|
export declare const DATA_VALIDATION_EXT_URI = "{CCE6A557-97BC-4b89-ADB6-D9C93CAAB3DF}";
|
|
55
55
|
export declare const SLICER_LIST_EXT_URI = "{A8765BA9-456A-4dab-B4F3-ACF838C121DE}";
|
|
56
56
|
export declare const SLICER_CACHES_EXT_URI = "{BBE1A952-AA13-448e-AADC-164F8A28A991}";
|
|
57
|
+
/** The extension a dynamic-array formula's cell-metadata block carries its properties in. */
|
|
58
|
+
export declare const DYNAMIC_ARRAY_PROPERTIES_EXT_URI = "{bdbb8cdc-fa1e-496e-a857-3c3f30c029c3}";
|
|
59
|
+
/** The 2017 dynamic-array namespace (`xda:`), which scopes those properties in `xl/metadata.xml`. */
|
|
60
|
+
export declare const DYNAMIC_ARRAY_NS = "http://schemas.microsoft.com/office/spreadsheetml/2017/dynamicarray";
|
|
61
|
+
/** The extension a rich-value metadata block names its rich value in (`<xlrd:rvb i>`). */
|
|
62
|
+
export declare const RICH_VALUE_BLOCK_EXT_URI = "{3e2802c4-a4d2-4d8b-9148-e3be6c30e623}";
|
|
63
|
+
/** The 2017 rich-data namespace (`xlrd:`): the rich values, their structures, and the blocks naming them. */
|
|
64
|
+
export declare const RICH_DATA_NS = "http://schemas.microsoft.com/office/spreadsheetml/2017/richdata";
|
|
57
65
|
/**
|
|
58
66
|
* Whether an element belongs to the standard SpreadsheetML vocabulary rather than to an extension.
|
|
59
67
|
*
|
|
@@ -11,6 +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 const DYNAMIC_ARRAY_PROPERTIES_EXT_URI = '{bdbb8cdc-fa1e-496e-a857-3c3f30c029c3}';
|
|
15
|
+
export const DYNAMIC_ARRAY_NS = 'http://schemas.microsoft.com/office/spreadsheetml/2017/dynamicarray';
|
|
16
|
+
export const RICH_VALUE_BLOCK_EXT_URI = '{3e2802c4-a4d2-4d8b-9148-e3be6c30e623}';
|
|
17
|
+
export const RICH_DATA_NS = 'http://schemas.microsoft.com/office/spreadsheetml/2017/richdata';
|
|
14
18
|
export function isMainNamespaceElement(scope, name) {
|
|
15
19
|
const namespace = scope.elementNamespace(name);
|
|
16
20
|
return namespace === SPREADSHEETML_NS || (namespace === undefined && !name.includes(':'));
|
|
@@ -6,29 +6,59 @@ 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
8
|
import type { HyperlinkPlan } from './hyperlinks.ts';
|
|
9
|
-
import type
|
|
10
|
-
|
|
9
|
+
import { type DrawingImage } from './images.ts';
|
|
10
|
+
/** One relationship a generated `.rels` part declares, its target already relative to the owner. */
|
|
11
|
+
export interface PlannedRelationship {
|
|
12
|
+
readonly id: string;
|
|
13
|
+
readonly type: string;
|
|
14
|
+
readonly target: string;
|
|
15
|
+
readonly external?: boolean;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The relationships one part owns, recorded as their ids are handed out.
|
|
19
|
+
*
|
|
20
|
+
* An id and the relationship it names are one fact, so they are written down in one call. When the
|
|
21
|
+
* ids came from a counter and the `.rels` part was listed somewhere else, a sheet's set was spelled
|
|
22
|
+
* three times: once where ids were drawn, once in the rels renderer in another order, and once more in
|
|
23
|
+
* a nine-way condition deciding whether the rels part existed at all. A tenth kind of part needed all
|
|
24
|
+
* three edits, and missing the last dropped the `.rels` part while the sheet XML still cited the id.
|
|
25
|
+
* Now the ledger is the rels part, and a part with an empty ledger has none.
|
|
26
|
+
*
|
|
27
|
+
* Ids run `rId1`, `rId2`, … in the order parts are added, so no id is derived by summing the ones
|
|
28
|
+
* before it, and the `.rels` part lists them in that same order. One ledger per owning part; ids are
|
|
29
|
+
* scoped to it. A ledger adding to a part kept from a file skips the ids that part already holds.
|
|
30
|
+
*/
|
|
31
|
+
export declare class RelationshipLedger {
|
|
11
32
|
#private;
|
|
12
|
-
/**
|
|
13
|
-
|
|
33
|
+
/**
|
|
34
|
+
* @param owner the package path of the part whose `.rels` this is, `''` for the package root.
|
|
35
|
+
* @param taken the ids the part's `.rels` already holds, none of which a new relationship may take.
|
|
36
|
+
*/
|
|
37
|
+
constructor(owner: string, taken?: ReadonlySet<string>);
|
|
38
|
+
/** Record a relationship to a package part, named by its package path, and return its id. */
|
|
39
|
+
add(type: string, partPath: string): string;
|
|
40
|
+
/** Record a relationship to a target outside the package (a hyperlink's URL) and return its id. */
|
|
41
|
+
addExternal(type: string, url: string): string;
|
|
42
|
+
/** Record a relationship to a place inside this document (a picture link's `#Sheet1!C3`), kept as
|
|
43
|
+
* written rather than resolved as a part path, and return its id. */
|
|
44
|
+
addInDocument(type: string, location: string): string;
|
|
45
|
+
/** Every relationship recorded, in id order. */
|
|
46
|
+
get relationships(): readonly PlannedRelationship[];
|
|
14
47
|
}
|
|
15
48
|
export interface PivotPlan {
|
|
16
49
|
readonly number: number;
|
|
17
50
|
readonly cacheId: string;
|
|
18
51
|
readonly table: PivotTable;
|
|
19
|
-
readonly sheetRelId: string;
|
|
20
52
|
workbookRelId: string;
|
|
21
53
|
}
|
|
22
54
|
export interface CommentPlan {
|
|
23
55
|
readonly number: number;
|
|
24
56
|
readonly comments: readonly CommentCell[];
|
|
25
57
|
readonly vmlRelId: string;
|
|
26
|
-
readonly commentsRelId: string;
|
|
27
58
|
}
|
|
28
59
|
export interface ThreadedCommentPlan {
|
|
29
60
|
readonly number: number;
|
|
30
61
|
readonly threads: readonly CommentThread[];
|
|
31
|
-
readonly relId: string;
|
|
32
62
|
}
|
|
33
63
|
export interface PrinterSettingsPlan {
|
|
34
64
|
readonly number: number;
|
|
@@ -42,8 +72,6 @@ export interface TablePlan {
|
|
|
42
72
|
}
|
|
43
73
|
export interface BackgroundPlan {
|
|
44
74
|
readonly relId: string;
|
|
45
|
-
readonly mediaNumber: number;
|
|
46
|
-
readonly extension: string;
|
|
47
75
|
}
|
|
48
76
|
export interface PreservedPartPlan {
|
|
49
77
|
readonly path: string;
|
|
@@ -85,11 +113,8 @@ export interface PreservedPlan {
|
|
|
85
113
|
export interface DrawingPlan {
|
|
86
114
|
readonly number: number;
|
|
87
115
|
readonly relId: string;
|
|
88
|
-
readonly images: readonly
|
|
89
|
-
|
|
90
|
-
export interface ImagePlan extends DrawingImage {
|
|
91
|
-
readonly mediaNumber: number;
|
|
92
|
-
readonly extension: string;
|
|
116
|
+
readonly images: readonly DrawingImage[];
|
|
117
|
+
readonly relationships: readonly PlannedRelationship[];
|
|
93
118
|
}
|
|
94
119
|
export interface MediaPart {
|
|
95
120
|
readonly number: number;
|
|
@@ -109,8 +134,16 @@ export interface MediaPlan {
|
|
|
109
134
|
resolve(id: number): ResolvedMedia;
|
|
110
135
|
}
|
|
111
136
|
export declare function planMedia(workbook: Workbook, sheets: readonly Worksheet[]): MediaPlan;
|
|
112
|
-
|
|
137
|
+
/**
|
|
138
|
+
* The pictures a sheet adds to the drawing it kept from a file, laid out once that drawing's path in the
|
|
139
|
+
* package is known: each picture's media and link relationships go into `ledger`, the drawing's own,
|
|
140
|
+
* which already skips the ids the drawing holds.
|
|
141
|
+
*/
|
|
142
|
+
export type PicturesJoiningDrawing = (ledger: RelationshipLedger) => readonly DrawingImage[];
|
|
143
|
+
export declare function planPreservedParts(workbook: Workbook, generatedDrawingCount: number, generatedMediaCount: number, generatedPivotCount: number, pictures: ReadonlyMap<string, PicturesJoiningDrawing>): PreservedPlan;
|
|
113
144
|
export interface SheetPlan {
|
|
145
|
+
/** Every relationship the sheet's `.rels` part declares, in id order; empty when it needs none. */
|
|
146
|
+
readonly relationships: readonly PlannedRelationship[];
|
|
114
147
|
readonly tables: TablePlan[];
|
|
115
148
|
readonly drawing: DrawingPlan | null;
|
|
116
149
|
readonly comments: CommentPlan | null;
|
|
@@ -1,12 +1,39 @@
|
|
|
1
|
+
import { INTERNAL } from '../../core/internal.js';
|
|
1
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';
|
|
5
|
+
import { mergePictures } from './images.js';
|
|
4
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) {
|
|
@@ -51,7 +78,7 @@ export function planMedia(workbook, sheets) {
|
|
|
51
78
|
},
|
|
52
79
|
};
|
|
53
80
|
}
|
|
54
|
-
export function planPreservedParts(workbook, generatedDrawingCount, generatedMediaCount, generatedPivotCount) {
|
|
81
|
+
export function planPreservedParts(workbook, generatedDrawingCount, generatedMediaCount, generatedPivotCount, pictures) {
|
|
55
82
|
const sheets = workbook.worksheets;
|
|
56
83
|
const numbering = {
|
|
57
84
|
drawing: generatedDrawingCount,
|
|
@@ -78,6 +105,7 @@ export function planPreservedParts(workbook, generatedDrawingCount, generatedMed
|
|
|
78
105
|
}
|
|
79
106
|
}
|
|
80
107
|
const emitted = new Map();
|
|
108
|
+
const splices = workbook[INTERNAL].splices();
|
|
81
109
|
for (const reference of allReferences) {
|
|
82
110
|
for (const part of reference.parts) {
|
|
83
111
|
const newPath = resolveRemapped(remap, part.path);
|
|
@@ -93,14 +121,23 @@ export function planPreservedParts(workbook, generatedDrawingCount, generatedMed
|
|
|
93
121
|
: [{ id: rel.id, type: rel.type, target: relativePartPath(newPath, target) }];
|
|
94
122
|
});
|
|
95
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
|
+
}
|
|
96
135
|
emitted.set(newPath, {
|
|
97
136
|
path: newPath,
|
|
98
|
-
bytes
|
|
99
|
-
? part.bytes
|
|
100
|
-
: new TextEncoder().encode(applyThemeOverrides(new TextDecoder().decode(part.bytes), overrides)),
|
|
137
|
+
bytes,
|
|
101
138
|
contentType: part.contentType,
|
|
102
|
-
relsPath:
|
|
103
|
-
relsXml:
|
|
139
|
+
relsPath: relationships.length === 0 ? null : relsPathFor(newPath),
|
|
140
|
+
relsXml: relationships.length === 0 ? null : relsPartXml(relationships),
|
|
104
141
|
});
|
|
105
142
|
}
|
|
106
143
|
}
|
|
@@ -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 {
|
|
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,30 +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
|
-
boolAttr('containsInteger', numeric.allInteger) +
|
|
35
|
-
blank +
|
|
36
|
-
` minValue="${numberText(numeric.min)}" maxValue="${numberText(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>`;
|
|
37
26
|
return `<cacheField name="${escapeAttr(field.name)}" numFmtId="0">${descriptor}</cacheField>`;
|
|
38
27
|
})
|
|
39
28
|
.join('');
|
|
40
29
|
return (XML_DECLARATION +
|
|
41
|
-
`<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" ` +
|
|
42
31
|
`refreshedBy="ts-xlsx" createdVersion="8" refreshedVersion="8" minRefreshableVersion="3" ` +
|
|
43
32
|
`recordCount="${table.records.length}">` +
|
|
44
33
|
`<cacheSource type="worksheet">` +
|
|
@@ -64,15 +53,14 @@ export function pivotTableXml(table, name, cacheId) {
|
|
|
64
53
|
const location = `A1:${encodeAddress(2 + columnGroups, 3 + rowGroups)}`;
|
|
65
54
|
const pivotFields = table.cacheFields
|
|
66
55
|
.map((field, index) => {
|
|
56
|
+
const dataField = index === table.valueField ? ' dataField="1"' : '';
|
|
67
57
|
if (table.rowFields.includes(index) || table.columnFields.includes(index)) {
|
|
68
58
|
const axis = table.rowFields.includes(index) ? 'axisRow' : 'axisCol';
|
|
69
59
|
const items = field.sharedItems ?? [];
|
|
70
60
|
const entries = `${items.map((_item, i) => `<item x="${i}"/>`).join('')}<item t="default"/>`;
|
|
71
|
-
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>`;
|
|
72
62
|
}
|
|
73
|
-
|
|
74
|
-
return '<pivotField dataField="1" showAll="0"/>';
|
|
75
|
-
return '<pivotField showAll="0"/>';
|
|
63
|
+
return `<pivotField${dataField} showAll="0"/>`;
|
|
76
64
|
})
|
|
77
65
|
.join('');
|
|
78
66
|
const rowFields = table.rowFields.map((index) => `<field x="${index}"/>`).join('');
|
|
@@ -103,6 +91,19 @@ function dataFieldXml(table) {
|
|
|
103
91
|
return (`<dataField name="${escapeAttr(caption)}" fld="${table.valueField}"${subtotal} ` +
|
|
104
92
|
`baseField="0" baseItem="0"/>`);
|
|
105
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
|
+
}
|
|
106
107
|
function sharedItemXml(item) {
|
|
107
108
|
switch (item.kind) {
|
|
108
109
|
case 'string':
|