@shbernal/ts-xlsx 2.0.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -10
- package/dist/bytes.d.ts +24 -0
- package/dist/bytes.js +34 -0
- package/dist/core/address.d.ts +26 -4
- package/dist/core/address.js +21 -9
- package/dist/core/autofilter.d.ts +12 -1
- package/dist/core/autofilter.js +35 -12
- package/dist/core/axis-handle.d.ts +8 -0
- package/dist/core/axis-handle.js +14 -0
- package/dist/core/cell.d.ts +24 -0
- package/dist/core/cell.js +9 -1
- package/dist/core/color-resolution.d.ts +1 -1
- package/dist/core/color-resolution.js +7 -8
- package/dist/core/column.d.ts +4 -1
- package/dist/core/column.js +31 -35
- package/dist/core/conditional-formatting-overlay.d.ts +5 -0
- package/dist/core/conditional-formatting-overlay.js +11 -0
- package/dist/core/conditional-formatting.d.ts +45 -5
- package/dist/core/conditional-formatting.js +74 -0
- package/dist/core/data-validation-overlay.d.ts +11 -0
- package/dist/core/data-validation-overlay.js +21 -2
- package/dist/core/data-validation.d.ts +9 -2
- package/dist/core/data-validation.js +26 -0
- package/dist/core/grid-edits.d.ts +16 -0
- package/dist/core/grid-edits.js +26 -8
- package/dist/core/grid-shift.d.ts +19 -0
- package/dist/core/grid-shift.js +8 -0
- package/dist/core/image.d.ts +2 -0
- package/dist/core/image.js +2 -0
- package/dist/core/merge-index.d.ts +31 -0
- package/dist/core/merge-index.js +60 -0
- package/dist/core/merge.d.ts +12 -11
- package/dist/core/merge.js +36 -10
- package/dist/core/page-setup.d.ts +86 -2
- package/dist/core/page-setup.js +34 -1
- package/dist/core/pivot-table.d.ts +2 -0
- package/dist/core/pivot-table.js +23 -18
- package/dist/core/protection.d.ts +3 -0
- package/dist/core/protection.js +20 -14
- package/dist/core/row-input.d.ts +7 -0
- package/dist/core/row-input.js +10 -8
- package/dist/core/row.d.ts +4 -1
- package/dist/core/row.js +19 -23
- package/dist/core/style.d.ts +78 -7
- package/dist/core/style.js +90 -80
- package/dist/core/table.d.ts +26 -12
- package/dist/core/table.js +67 -33
- package/dist/core/theme.d.ts +3 -1
- package/dist/core/theme.js +4 -0
- package/dist/core/used-extent.d.ts +44 -0
- package/dist/core/used-extent.js +102 -0
- package/dist/core/workbook.d.ts +2 -2
- package/dist/core/worksheet-comments.d.ts +8 -0
- package/dist/core/worksheet-comments.js +18 -0
- package/dist/core/worksheet.d.ts +31 -7
- package/dist/core/worksheet.js +72 -86
- package/dist/customui/ribbon.js +23 -24
- package/dist/entries/core.d.ts +3 -3
- package/dist/entries/csv.d.ts +1 -1
- package/dist/entries/node-unavailable.d.ts +15 -0
- package/dist/entries/node-unavailable.js +20 -0
- package/dist/entries/node.d.ts +1 -0
- package/dist/entries/node.js +1 -0
- package/dist/entries/xlsx.d.ts +0 -1
- package/dist/entries/xlsx.js +0 -1
- package/dist/io/csv/read.js +1 -1
- package/dist/io/csv/write.d.ts +12 -2
- package/dist/io/csv/write.js +17 -2
- package/dist/io/opc/inflate.js +3 -13
- package/dist/io/opc/part-paths.d.ts +1 -0
- package/dist/io/opc/part-paths.js +15 -0
- package/dist/io/opc/read-opc.d.ts +22 -3
- package/dist/io/opc/read-opc.js +11 -39
- package/dist/io/opc/rels.d.ts +12 -6
- package/dist/io/opc/rels.js +3 -5
- package/dist/io/style/xf-style.d.ts +33 -4
- package/dist/io/style/xf-style.js +12 -1
- package/dist/io/xlsb/read-styles.js +6 -3
- package/dist/io/xlsb/read.d.ts +1 -1
- package/dist/io/xlsb/read.js +5 -8
- package/dist/io/xlsx/cell-accumulator.d.ts +2 -4
- package/dist/io/xlsx/cell-accumulator.js +20 -58
- package/dist/io/xlsx/cell-value.d.ts +1 -1
- package/dist/io/xlsx/cell-value.js +7 -4
- package/dist/io/xlsx/color-xml.d.ts +1 -1
- package/dist/io/xlsx/color-xml.js +8 -7
- package/dist/io/xlsx/comments.js +6 -3
- package/dist/io/xlsx/conditional-formatting.d.ts +4 -3
- package/dist/io/xlsx/conditional-formatting.js +81 -65
- package/dist/io/xlsx/data-validation.d.ts +10 -8
- package/dist/io/xlsx/data-validation.js +44 -41
- package/dist/io/xlsx/edit-vba.js +4 -11
- package/dist/io/xlsx/hyperlinks.d.ts +4 -2
- package/dist/io/xlsx/hyperlinks.js +26 -31
- package/dist/io/xlsx/images.js +33 -35
- package/dist/io/xlsx/package-plan.d.ts +9 -1
- package/dist/io/xlsx/package-plan.js +29 -16
- package/dist/io/xlsx/part-names.d.ts +41 -0
- package/dist/io/xlsx/part-names.js +46 -0
- package/dist/io/xlsx/read-pivot.js +4 -9
- package/dist/io/xlsx/read-rows.js +12 -13
- package/dist/io/xlsx/read-shared-strings.js +9 -47
- package/dist/io/xlsx/read-styles.d.ts +1 -1
- package/dist/io/xlsx/read-styles.js +65 -43
- package/dist/io/xlsx/read-worksheet.d.ts +10 -1
- package/dist/io/xlsx/read-worksheet.js +48 -63
- package/dist/io/xlsx/read.d.ts +3 -4
- package/dist/io/xlsx/read.js +124 -146
- package/dist/io/xlsx/rich-runs.d.ts +41 -7
- package/dist/io/xlsx/rich-runs.js +95 -30
- package/dist/io/xlsx/sheet-properties.d.ts +1 -1
- package/dist/io/xlsx/sheet-properties.js +23 -26
- package/dist/io/xlsx/styles.d.ts +3 -15
- package/dist/io/xlsx/styles.js +83 -105
- package/dist/io/xlsx/tables.js +14 -13
- package/dist/io/xlsx/theme-xml.js +8 -5
- package/dist/io/xlsx/threaded-comments.js +20 -19
- package/dist/io/xlsx/workbook-xml.js +43 -36
- package/dist/io/xlsx/worksheet-xml.d.ts +1 -1
- package/dist/io/xlsx/worksheet-xml.js +47 -52
- package/dist/io/xlsx/write-stream.d.ts +14 -1
- package/dist/io/xlsx/write-stream.js +40 -33
- package/dist/io/xlsx/write.d.ts +5 -0
- package/dist/io/xlsx/write.js +155 -131
- package/dist/sha512.d.ts +2 -0
- package/dist/sha512.js +146 -0
- package/dist/token-set.d.ts +15 -0
- package/dist/token-set.js +4 -0
- package/dist/vba/bytes.d.ts +1 -2
- package/dist/vba/bytes.js +1 -12
- package/dist/vba/cfb-format.d.ts +24 -0
- package/dist/vba/cfb-format.js +12 -0
- package/dist/vba/cfb-writer.js +17 -17
- package/dist/vba/cfb.js +1 -7
- package/dist/vba/dir-records.d.ts +53 -0
- package/dist/vba/dir-records.js +28 -0
- package/dist/vba/ms-ovba.js +32 -11
- package/dist/vba/project-editor.js +5 -34
- package/dist/vba/project.js +15 -31
- package/dist/vba/vba-encoding.js +1 -1
- package/dist/xml/xml-read.d.ts +117 -72
- package/dist/xml/xml-read.js +123 -169
- package/dist/xml/xml-scan.d.ts +100 -0
- package/dist/xml/xml-scan.js +208 -0
- package/dist/xml/xml.d.ts +44 -2
- package/dist/xml/xml.js +17 -5
- package/package.json +22 -6
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { tryDecodeRange } from '../../core/address.js';
|
|
2
2
|
import { isHyperlinkValue, isRichTextValue } from '../../core/value.js';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import {} from '../../xml/xml-read.js';
|
|
4
|
+
import { localName } from '../../xml/xml-scan.js';
|
|
5
|
+
import { escapeAttr, textAttr } from '../../xml/xml.js';
|
|
5
6
|
export function collectHyperlinks(sheet) {
|
|
6
7
|
const links = [];
|
|
7
8
|
for (const { cells } of sheet.rows()) {
|
|
@@ -33,39 +34,41 @@ export function hyperlinksXml(links) {
|
|
|
33
34
|
const items = links
|
|
34
35
|
.map((link) => {
|
|
35
36
|
const rid = link.relId !== undefined ? ` r:id="${link.relId}"` : '';
|
|
36
|
-
const location =
|
|
37
|
-
const tooltip =
|
|
37
|
+
const location = textAttr('location', link.location);
|
|
38
|
+
const tooltip = textAttr('tooltip', link.tooltip);
|
|
38
39
|
return `<hyperlink ref="${escapeAttr(link.ref)}"${rid}${location}${tooltip}/>`;
|
|
39
40
|
})
|
|
40
41
|
.join('');
|
|
41
42
|
return `<hyperlinks>${items}</hyperlinks>`;
|
|
42
43
|
}
|
|
43
|
-
export function
|
|
44
|
+
export function sheetHyperlinkPass() {
|
|
44
45
|
const links = [];
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
46
|
+
return {
|
|
47
|
+
handlers: {
|
|
48
|
+
onOpen(name, attrs) {
|
|
49
|
+
if (localName(name) !== 'hyperlink')
|
|
50
|
+
return;
|
|
51
|
+
const ref = attrs.ref;
|
|
52
|
+
if (ref === undefined)
|
|
53
|
+
return;
|
|
54
|
+
links.push({
|
|
55
|
+
ref,
|
|
56
|
+
...(attrs['r:id'] !== undefined ? { rid: attrs['r:id'] } : {}),
|
|
57
|
+
...(attrs.location !== undefined ? { location: attrs.location } : {}),
|
|
58
|
+
...(attrs.tooltip !== undefined ? { tooltip: attrs.tooltip } : {}),
|
|
59
|
+
});
|
|
60
|
+
},
|
|
58
61
|
},
|
|
59
|
-
|
|
60
|
-
|
|
62
|
+
result: () => links,
|
|
63
|
+
};
|
|
61
64
|
}
|
|
62
65
|
export function applyHyperlinks(sheet, links, targetOf) {
|
|
63
66
|
for (const link of links) {
|
|
64
67
|
const target = resolveTarget(link, targetOf);
|
|
65
68
|
if (target === undefined)
|
|
66
69
|
continue;
|
|
67
|
-
const decoded =
|
|
68
|
-
if (decoded === undefined)
|
|
70
|
+
const decoded = tryDecodeRange(link.ref);
|
|
71
|
+
if (decoded === undefined || decoded.tl.col === undefined || decoded.tl.row === undefined)
|
|
69
72
|
continue;
|
|
70
73
|
const cell = sheet.getCell(decoded.tl.address);
|
|
71
74
|
const cellValue = cell.value;
|
|
@@ -80,14 +83,6 @@ export function applyHyperlinks(sheet, links, targetOf) {
|
|
|
80
83
|
cell.value = value;
|
|
81
84
|
}
|
|
82
85
|
}
|
|
83
|
-
function decodeRefSafe(ref) {
|
|
84
|
-
try {
|
|
85
|
-
return decodeRange(ref);
|
|
86
|
-
}
|
|
87
|
-
catch {
|
|
88
|
-
return undefined;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
86
|
function resolveTarget(link, targetOf) {
|
|
92
87
|
if (link.rid !== undefined) {
|
|
93
88
|
const base = targetOf(link.rid);
|
package/dist/io/xlsx/images.js
CHANGED
|
@@ -1,25 +1,26 @@
|
|
|
1
|
-
import { isOneCellAnchor, } from '../../core/image.js';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
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';
|
|
4
|
+
import { checkedToken, numAttr, numberText, XML_DECLARATION } from '../../xml/xml.js';
|
|
4
5
|
import { RELATIONSHIPS_NS } from '../opc/namespaces.js';
|
|
5
6
|
import { relationship, relationshipsPart } from '../opc/rels.js';
|
|
6
7
|
import { DRAWINGML_NS, XDR_NS } from './namespaces.js';
|
|
7
8
|
const IMAGE_REL_TYPE = `${RELATIONSHIPS_NS}/image`;
|
|
8
|
-
const IMAGE_CONTENT_TYPES =
|
|
9
|
-
png
|
|
10
|
-
jpg
|
|
11
|
-
jpeg
|
|
12
|
-
gif
|
|
13
|
-
bmp
|
|
14
|
-
tif
|
|
15
|
-
tiff
|
|
16
|
-
emf
|
|
17
|
-
wmf
|
|
18
|
-
svg
|
|
19
|
-
|
|
9
|
+
const IMAGE_CONTENT_TYPES = new Map([
|
|
10
|
+
['png', 'image/png'],
|
|
11
|
+
['jpg', 'image/jpeg'],
|
|
12
|
+
['jpeg', 'image/jpeg'],
|
|
13
|
+
['gif', 'image/gif'],
|
|
14
|
+
['bmp', 'image/bmp'],
|
|
15
|
+
['tif', 'image/tiff'],
|
|
16
|
+
['tiff', 'image/tiff'],
|
|
17
|
+
['emf', 'image/x-emf'],
|
|
18
|
+
['wmf', 'image/x-wmf'],
|
|
19
|
+
['svg', 'image/svg+xml'],
|
|
20
|
+
]);
|
|
20
21
|
export function imageContentType(extension) {
|
|
21
22
|
const ext = extension.toLowerCase();
|
|
22
|
-
return IMAGE_CONTENT_TYPES
|
|
23
|
+
return IMAGE_CONTENT_TYPES.get(ext) ?? `image/${ext}`;
|
|
23
24
|
}
|
|
24
25
|
export function drawingXml(images) {
|
|
25
26
|
const anchors = images.map((image, i) => anchorXml(image, i + 1)).join('');
|
|
@@ -35,7 +36,7 @@ function anchorXml(image, id) {
|
|
|
35
36
|
: twoCellAnchorXml(anchor.from, anchor.to, anchor.editAs ?? 'oneCell', anchor.rotation, image.embedId, id);
|
|
36
37
|
}
|
|
37
38
|
function twoCellAnchorXml(from, to, editAs, rotation, embedId, id) {
|
|
38
|
-
return (`<xdr:twoCellAnchor editAs="${editAs}">` +
|
|
39
|
+
return (`<xdr:twoCellAnchor editAs="${checkedToken(editAs, isImageEditAs, 'image anchor edit mode')}">` +
|
|
39
40
|
`<xdr:from>${anchorPointXml(from)}</xdr:from>` +
|
|
40
41
|
`<xdr:to>${anchorPointXml(to)}</xdr:to>` +
|
|
41
42
|
picXml(embedId, id, rotation) +
|
|
@@ -45,13 +46,13 @@ function twoCellAnchorXml(from, to, editAs, rotation, embedId, id) {
|
|
|
45
46
|
function oneCellAnchorXml(from, ext, rotation, embedId, id) {
|
|
46
47
|
return ('<xdr:oneCellAnchor>' +
|
|
47
48
|
`<xdr:from>${anchorPointXml(from)}</xdr:from>` +
|
|
48
|
-
`<xdr:ext
|
|
49
|
+
`<xdr:ext${numAttr('cx', ext.cx)}${numAttr('cy', ext.cy)}/>` +
|
|
49
50
|
picXml(embedId, id, rotation) +
|
|
50
51
|
'<xdr:clientData/>' +
|
|
51
52
|
'</xdr:oneCellAnchor>');
|
|
52
53
|
}
|
|
53
54
|
function picXml(embedId, id, rotation) {
|
|
54
|
-
const xfrm = rotation !== undefined ? `<a:xfrm
|
|
55
|
+
const xfrm = rotation !== undefined ? `<a:xfrm${numAttr('rot', rotation)}/>` : '';
|
|
55
56
|
return ('<xdr:pic>' +
|
|
56
57
|
`<xdr:nvPicPr><xdr:cNvPr id="${id}" name="Picture ${id}"/>` +
|
|
57
58
|
'<xdr:cNvPicPr><a:picLocks noChangeAspect="1"/></xdr:cNvPicPr></xdr:nvPicPr>' +
|
|
@@ -61,8 +62,10 @@ function picXml(embedId, id, rotation) {
|
|
|
61
62
|
'</xdr:pic>');
|
|
62
63
|
}
|
|
63
64
|
function anchorPointXml(point) {
|
|
64
|
-
return (`<xdr:col>${point.col}</xdr:col
|
|
65
|
-
`<xdr:
|
|
65
|
+
return (`<xdr:col>${numberText(point.col)}</xdr:col>` +
|
|
66
|
+
`<xdr:colOff>${numberText(point.colOff ?? 0)}</xdr:colOff>` +
|
|
67
|
+
`<xdr:row>${numberText(point.row)}</xdr:row>` +
|
|
68
|
+
`<xdr:rowOff>${numberText(point.rowOff ?? 0)}</xdr:rowOff>`);
|
|
66
69
|
}
|
|
67
70
|
export function drawingRelsXml(mediaTargets) {
|
|
68
71
|
return relationshipsPart(mediaTargets.map((target, i) => relationship(`rId${i + 1}`, IMAGE_REL_TYPE, target)));
|
|
@@ -70,7 +73,6 @@ export function drawingRelsXml(mediaTargets) {
|
|
|
70
73
|
function blankPoint() {
|
|
71
74
|
return { col: 0, row: 0, colOff: 0, rowOff: 0 };
|
|
72
75
|
}
|
|
73
|
-
const EDIT_AS = new Set(['oneCell', 'twoCell', 'absolute']);
|
|
74
76
|
export function parseDrawing(xml) {
|
|
75
77
|
const anchors = [];
|
|
76
78
|
let from = null;
|
|
@@ -81,10 +83,9 @@ export function parseDrawing(xml) {
|
|
|
81
83
|
let embed;
|
|
82
84
|
let target = null;
|
|
83
85
|
let picDepth = 0;
|
|
84
|
-
|
|
85
|
-
let text = '';
|
|
86
|
+
const coord = new TextCapture(COORDINATES);
|
|
86
87
|
parseXml(xml, {
|
|
87
|
-
onOpen(name, attrs) {
|
|
88
|
+
onOpen(name, attrs, selfClosing) {
|
|
88
89
|
const local = localName(name);
|
|
89
90
|
if (local === 'twoCellAnchor' || local === 'oneCellAnchor') {
|
|
90
91
|
from = blankPoint();
|
|
@@ -92,8 +93,7 @@ export function parseDrawing(xml) {
|
|
|
92
93
|
ext = undefined;
|
|
93
94
|
rotation = undefined;
|
|
94
95
|
embed = undefined;
|
|
95
|
-
|
|
96
|
-
editAs = mode !== undefined && EDIT_AS.has(mode) ? mode : undefined;
|
|
96
|
+
editAs = enumToken(attrs.editAs, isImageEditAs);
|
|
97
97
|
}
|
|
98
98
|
else if (local === 'pic') {
|
|
99
99
|
picDepth++;
|
|
@@ -120,22 +120,20 @@ export function parseDrawing(xml) {
|
|
|
120
120
|
if (value !== undefined)
|
|
121
121
|
embed = value;
|
|
122
122
|
}
|
|
123
|
-
else if (target !== null
|
|
124
|
-
coord
|
|
125
|
-
text = '';
|
|
123
|
+
else if (target !== null) {
|
|
124
|
+
coord.open(local, selfClosing);
|
|
126
125
|
}
|
|
127
126
|
},
|
|
128
127
|
onText(chunk) {
|
|
129
|
-
|
|
130
|
-
text += chunk;
|
|
128
|
+
coord.text(chunk);
|
|
131
129
|
},
|
|
132
130
|
onClose(name) {
|
|
133
131
|
const local = localName(name);
|
|
134
|
-
|
|
132
|
+
const text = coord.close(local);
|
|
133
|
+
if (text !== undefined) {
|
|
135
134
|
const value = numFinite(text);
|
|
136
|
-
if (value !== undefined)
|
|
135
|
+
if (target !== null && value !== undefined)
|
|
137
136
|
setCoordinate(target, local, value);
|
|
138
|
-
coord = '';
|
|
139
137
|
}
|
|
140
138
|
else if (local === 'from' || local === 'to') {
|
|
141
139
|
target = null;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { CommentThread } from '../../core/comment-thread.ts';
|
|
2
|
+
import type { WorkbookImage } from '../../core/image.ts';
|
|
2
3
|
import type { PivotTable } from '../../core/pivot-table.ts';
|
|
3
4
|
import type { Table } from '../../core/table.ts';
|
|
4
5
|
import type { Workbook } from '../../core/workbook.ts';
|
|
@@ -94,10 +95,17 @@ export interface MediaPart {
|
|
|
94
95
|
readonly extension: string;
|
|
95
96
|
readonly data: Uint8Array;
|
|
96
97
|
}
|
|
98
|
+
export interface ResolvedMedia {
|
|
99
|
+
readonly number: number;
|
|
100
|
+
readonly image: WorkbookImage;
|
|
101
|
+
}
|
|
97
102
|
export interface MediaPlan {
|
|
98
103
|
readonly parts: readonly MediaPart[];
|
|
99
|
-
readonly numberById: ReadonlyMap<number, number>;
|
|
100
104
|
readonly extensions: readonly string[];
|
|
105
|
+
/** The media part number and registered image for a referenced id. Total over the ids
|
|
106
|
+
* {@link planMedia} saw on the sheets it planned, because planning already threw on any it could
|
|
107
|
+
* not resolve; an id from anywhere else is a caller bug and is reported as one. */
|
|
108
|
+
resolve(id: number): ResolvedMedia;
|
|
101
109
|
}
|
|
102
110
|
export declare function planMedia(workbook: Workbook, sheets: readonly Worksheet[]): MediaPlan;
|
|
103
111
|
export declare function planPreservedParts(workbook: Workbook, generatedDrawingCount: number, generatedMediaCount: number): PreservedPlan;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { AuthoringError } from '../../errors.js';
|
|
2
2
|
import { extensionOf, relativePartPath, relsPathFor, THEME_PART_PATH } from '../opc/part-paths.js';
|
|
3
|
-
import {
|
|
3
|
+
import { relsPartXml } from '../opc/rels.js';
|
|
4
|
+
import { drawingPart, mediaPart, vmlDrawingPart } from './part-names.js';
|
|
4
5
|
import { applyThemeOverrides } from './theme-xml.js';
|
|
5
6
|
export class SheetRelIds {
|
|
6
7
|
#next = 1;
|
|
@@ -9,34 +10,46 @@ export class SheetRelIds {
|
|
|
9
10
|
}
|
|
10
11
|
}
|
|
11
12
|
export function planMedia(workbook, sheets) {
|
|
12
|
-
const
|
|
13
|
+
const uses = [];
|
|
13
14
|
const seen = new Set();
|
|
14
|
-
const use = (id) => {
|
|
15
|
+
const use = (id, sheetName, role) => {
|
|
15
16
|
if (!seen.has(id)) {
|
|
16
17
|
seen.add(id);
|
|
17
|
-
|
|
18
|
+
uses.push({ id, sheetName, role });
|
|
18
19
|
}
|
|
19
20
|
};
|
|
20
21
|
for (const sheet of sheets) {
|
|
21
22
|
for (const image of sheet.images)
|
|
22
|
-
use(image.imageId);
|
|
23
|
-
if (sheet.backgroundImageId !== undefined)
|
|
24
|
-
use(sheet.backgroundImageId);
|
|
23
|
+
use(image.imageId, sheet.name, 'anchor');
|
|
24
|
+
if (sheet.backgroundImageId !== undefined) {
|
|
25
|
+
use(sheet.backgroundImageId, sheet.name, 'background');
|
|
26
|
+
}
|
|
25
27
|
}
|
|
26
28
|
const parts = [];
|
|
27
|
-
const
|
|
29
|
+
const byId = new Map();
|
|
28
30
|
const extensions = new Set();
|
|
29
|
-
|
|
31
|
+
uses.forEach(({ id, sheetName, role }, i) => {
|
|
30
32
|
const image = workbook.getImage(id);
|
|
31
33
|
if (image === undefined) {
|
|
32
|
-
|
|
34
|
+
const reference = role === 'anchor' ? `anchors image id ${id}` : `sets background image id ${id}`;
|
|
35
|
+
throw new AuthoringError(`sheet "${sheetName}" ${reference}, which is not registered on the workbook`);
|
|
33
36
|
}
|
|
34
37
|
const number = i + 1;
|
|
35
38
|
parts.push({ number, extension: image.extension, data: image.data });
|
|
36
|
-
|
|
39
|
+
byId.set(id, { number, image });
|
|
37
40
|
extensions.add(image.extension);
|
|
38
41
|
});
|
|
39
|
-
return {
|
|
42
|
+
return {
|
|
43
|
+
parts,
|
|
44
|
+
extensions: [...extensions],
|
|
45
|
+
resolve(id) {
|
|
46
|
+
const resolved = byId.get(id);
|
|
47
|
+
if (resolved === undefined) {
|
|
48
|
+
throw new AuthoringError(`image id ${id} was not planned into this workbook's media`);
|
|
49
|
+
}
|
|
50
|
+
return resolved;
|
|
51
|
+
},
|
|
52
|
+
};
|
|
40
53
|
}
|
|
41
54
|
export function planPreservedParts(workbook, generatedDrawingCount, generatedMediaCount) {
|
|
42
55
|
const sheets = workbook.worksheets;
|
|
@@ -84,7 +97,7 @@ export function planPreservedParts(workbook, generatedDrawingCount, generatedMed
|
|
|
84
97
|
: new TextEncoder().encode(applyThemeOverrides(new TextDecoder().decode(part.bytes), overrides)),
|
|
85
98
|
contentType: part.contentType,
|
|
86
99
|
relsPath: rels.length === 0 ? null : relsPathFor(newPath),
|
|
87
|
-
relsXml: rels.length === 0 ? null :
|
|
100
|
+
relsXml: rels.length === 0 ? null : relsPartXml(rels),
|
|
88
101
|
});
|
|
89
102
|
}
|
|
90
103
|
}
|
|
@@ -114,11 +127,11 @@ export function planPreservedParts(workbook, generatedDrawingCount, generatedMed
|
|
|
114
127
|
function preservedPartPath(originalPath, numbering) {
|
|
115
128
|
const ext = extensionOf(originalPath);
|
|
116
129
|
if (ext.toLowerCase() === 'vml')
|
|
117
|
-
return
|
|
130
|
+
return vmlDrawingPart(++numbering.vml);
|
|
118
131
|
if (originalPath.startsWith('xl/media/'))
|
|
119
|
-
return
|
|
132
|
+
return mediaPart(++numbering.media, ext);
|
|
120
133
|
if (originalPath.startsWith('xl/drawings/') && ext.toLowerCase() === 'xml') {
|
|
121
|
-
return
|
|
134
|
+
return drawingPart(++numbering.drawing);
|
|
122
135
|
}
|
|
123
136
|
return originalPath;
|
|
124
137
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/** The office document part: the workbook every other part hangs off. */
|
|
2
|
+
export declare const WORKBOOK_PART = "xl/workbook.xml";
|
|
3
|
+
export declare const STYLES_PART = "xl/styles.xml";
|
|
4
|
+
export declare const SHARED_STRINGS_PART = "xl/sharedStrings.xml";
|
|
5
|
+
/** Singular and unnumbered, unlike the per-sheet thread parts: one identity registry serves the
|
|
6
|
+
* whole workbook's threaded comments. */
|
|
7
|
+
export declare const PERSONS_PART = "xl/persons/person.xml";
|
|
8
|
+
export declare const CORE_PROPS_PART = "docProps/core.xml";
|
|
9
|
+
export declare const APP_PROPS_PART = "docProps/app.xml";
|
|
10
|
+
/** `xl/worksheets/sheet{n}.xml`, numbered by the sheet's position in the workbook (1-based). */
|
|
11
|
+
export declare function worksheetPart(number: number): string;
|
|
12
|
+
/** `xl/tables/table{n}.xml`, numbered across the whole workbook rather than per sheet. */
|
|
13
|
+
export declare function tablePart(number: number): string;
|
|
14
|
+
/** `xl/media/image{n}.{ext}`: the one part name that carries the source file's own extension, since
|
|
15
|
+
* the `<Default>` content type is registered per extension. */
|
|
16
|
+
export declare function mediaPart(number: number, extension: string): string;
|
|
17
|
+
/** `xl/drawings/drawing{n}.xml`: the DrawingML anchors for one sheet's images. */
|
|
18
|
+
export declare function drawingPart(number: number): string;
|
|
19
|
+
/** `xl/drawings/vmlDrawing{n}.vml`: the legacy shape a note's box is drawn as. Numbered with the
|
|
20
|
+
* comments part it accompanies, not with the DrawingML drawings it shares a directory with. */
|
|
21
|
+
export declare function vmlDrawingPart(number: number): string;
|
|
22
|
+
/** `xl/comments{n}.xml`: directly under `xl/`, unlike every other numbered part. */
|
|
23
|
+
export declare function commentsPart(number: number): string;
|
|
24
|
+
/** `xl/threadedComments/threadedComment{n}.xml`: one sheet's modern conversations. */
|
|
25
|
+
export declare function threadedCommentsPart(number: number): string;
|
|
26
|
+
/** `xl/printerSettings/printerSettings{n}.bin`: an opaque blob carried through verbatim. */
|
|
27
|
+
export declare function printerSettingsPart(number: number): string;
|
|
28
|
+
/** `xl/pivotTables/pivotTable{n}.xml`: the pivot as its host sheet reaches it. */
|
|
29
|
+
export declare function pivotTablePart(number: number): string;
|
|
30
|
+
/** `xl/pivotCache/pivotCacheDefinition{n}.xml`: the field catalogue and source reference. */
|
|
31
|
+
export declare function pivotCacheDefinitionPart(number: number): string;
|
|
32
|
+
/** `xl/pivotCache/pivotCacheRecords{n}.xml`: the cached rows the definition points at. */
|
|
33
|
+
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;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { relativePartPath } from '../opc/part-paths.js';
|
|
2
|
+
export const WORKBOOK_PART = 'xl/workbook.xml';
|
|
3
|
+
export const STYLES_PART = 'xl/styles.xml';
|
|
4
|
+
export const SHARED_STRINGS_PART = 'xl/sharedStrings.xml';
|
|
5
|
+
export const PERSONS_PART = 'xl/persons/person.xml';
|
|
6
|
+
export const CORE_PROPS_PART = 'docProps/core.xml';
|
|
7
|
+
export const APP_PROPS_PART = 'docProps/app.xml';
|
|
8
|
+
export function worksheetPart(number) {
|
|
9
|
+
return `xl/worksheets/sheet${number}.xml`;
|
|
10
|
+
}
|
|
11
|
+
export function tablePart(number) {
|
|
12
|
+
return `xl/tables/table${number}.xml`;
|
|
13
|
+
}
|
|
14
|
+
export function mediaPart(number, extension) {
|
|
15
|
+
return `xl/media/image${number}.${extension}`;
|
|
16
|
+
}
|
|
17
|
+
export function drawingPart(number) {
|
|
18
|
+
return `xl/drawings/drawing${number}.xml`;
|
|
19
|
+
}
|
|
20
|
+
export function vmlDrawingPart(number) {
|
|
21
|
+
return `xl/drawings/vmlDrawing${number}.vml`;
|
|
22
|
+
}
|
|
23
|
+
export function commentsPart(number) {
|
|
24
|
+
return `xl/comments${number}.xml`;
|
|
25
|
+
}
|
|
26
|
+
export function threadedCommentsPart(number) {
|
|
27
|
+
return `xl/threadedComments/threadedComment${number}.xml`;
|
|
28
|
+
}
|
|
29
|
+
export function printerSettingsPart(number) {
|
|
30
|
+
return `xl/printerSettings/printerSettings${number}.bin`;
|
|
31
|
+
}
|
|
32
|
+
export function pivotTablePart(number) {
|
|
33
|
+
return `xl/pivotTables/pivotTable${number}.xml`;
|
|
34
|
+
}
|
|
35
|
+
export function pivotCacheDefinitionPart(number) {
|
|
36
|
+
return `xl/pivotCache/pivotCacheDefinition${number}.xml`;
|
|
37
|
+
}
|
|
38
|
+
export function pivotCacheRecordsPart(number) {
|
|
39
|
+
return `xl/pivotCache/pivotCacheRecords${number}.xml`;
|
|
40
|
+
}
|
|
41
|
+
export function targetFromWorksheet(partPath) {
|
|
42
|
+
return relativePartPath(worksheetPart(1), partPath);
|
|
43
|
+
}
|
|
44
|
+
export function targetFromWorkbook(partPath) {
|
|
45
|
+
return relativePartPath(WORKBOOK_PART, partPath);
|
|
46
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { pivotMetricFromSubtotal, } from '../../core/pivot-table.js';
|
|
2
|
-
import {
|
|
1
|
+
import { isDeclarablePivotSourceKind, pivotMetricFromSubtotal, } from '../../core/pivot-table.js';
|
|
2
|
+
import { parseXml } from '../../xml/xml-read.js';
|
|
3
|
+
import { localName, numInteger } from '../../xml/xml-scan.js';
|
|
3
4
|
export function parsePivotTable(tableXml, cacheXml) {
|
|
4
5
|
const { fields, source } = parsePivotCacheDefinition(cacheXml);
|
|
5
6
|
const def = parsePivotTableDefinition(tableXml);
|
|
@@ -85,16 +86,10 @@ function parsePivotTableDefinition(tableXml) {
|
|
|
85
86
|
});
|
|
86
87
|
return { name, cacheId, rowFields, columnFields, valueField, valueCaption, metric };
|
|
87
88
|
}
|
|
88
|
-
const SOURCE_KINDS = new Set([
|
|
89
|
-
'worksheet',
|
|
90
|
-
'external',
|
|
91
|
-
'consolidation',
|
|
92
|
-
'scenario',
|
|
93
|
-
]);
|
|
94
89
|
function sourceKind(type) {
|
|
95
90
|
if (type === undefined)
|
|
96
91
|
return 'worksheet';
|
|
97
|
-
return
|
|
92
|
+
return isDeclarablePivotSourceKind(type) ? type : 'unknown';
|
|
98
93
|
}
|
|
99
94
|
function toIndex(value) {
|
|
100
95
|
return numInteger(value, 0) ?? -1;
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { MAX_COLUMN, MAX_ROW } from '../../core/address.js';
|
|
2
2
|
import { AuthoringError } from '../../errors.js';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { closeEmptyElements } from '../../xml/xml-read.js';
|
|
4
|
+
import { boolStrict, localName, numInteger, xmlEvents } from '../../xml/xml-scan.js';
|
|
5
|
+
import { openSpreadsheetPackage, readPartRelationships } from '../opc/read-opc.js';
|
|
6
|
+
import { unsupportedWorkbookPart } from '../opc/sniff-format.js';
|
|
6
7
|
import { CellAccumulator } from './cell-accumulator.js';
|
|
7
8
|
import { XlsxParseError } from './errors.js';
|
|
8
9
|
import { parseSharedStrings } from './read-shared-strings.js';
|
|
9
|
-
import {
|
|
10
|
+
import { parseStyleTable, parseWorkbookSheets, } from './read.js';
|
|
10
11
|
export function* readSheetRows(data, options = {}) {
|
|
11
12
|
const pkg = openPackage(data, options.maxUncompressedBytes);
|
|
12
13
|
const chosen = pickSheet(pkg.sheets, options.sheet);
|
|
@@ -23,14 +24,13 @@ export function* readWorkbookStream(data, options = {}) {
|
|
|
23
24
|
}
|
|
24
25
|
}
|
|
25
26
|
function openPackage(data, maxUncompressedBytes) {
|
|
26
|
-
const
|
|
27
|
-
const { partText: text } =
|
|
28
|
-
const workbookXml = text('xl/workbook.xml');
|
|
27
|
+
const { pkg, workbookXml } = openSpreadsheetPackage(data, maxUncompressedBytes);
|
|
28
|
+
const { partText: text } = pkg;
|
|
29
29
|
if (workbookXml === undefined) {
|
|
30
30
|
throw unsupportedWorkbookPart(text, 'the binary .xlsb format (BIFF12) cannot be row-streamed yet; read it with readXlsx or readXlsb');
|
|
31
31
|
}
|
|
32
32
|
const sheets = parseWorkbookSheets(workbookXml);
|
|
33
|
-
const rels =
|
|
33
|
+
const rels = readPartRelationships('xl/workbook.xml', text);
|
|
34
34
|
const sharedStrings = parseSharedStrings(text('xl/sharedStrings.xml') ?? '');
|
|
35
35
|
const { cellXfs: xfStyles } = parseStyleTable(text('xl/styles.xml') ?? '');
|
|
36
36
|
return {
|
|
@@ -38,9 +38,8 @@ function openPackage(data, maxUncompressedBytes) {
|
|
|
38
38
|
sharedStrings,
|
|
39
39
|
xfStyles,
|
|
40
40
|
sheetXml(relId) {
|
|
41
|
-
const target = rels.
|
|
42
|
-
|
|
43
|
-
return path === undefined ? undefined : text(path);
|
|
41
|
+
const target = rels.byId(relId)?.target;
|
|
42
|
+
return target === undefined ? undefined : text(rels.pathOf(target));
|
|
44
43
|
},
|
|
45
44
|
};
|
|
46
45
|
}
|
|
@@ -88,7 +87,7 @@ class StreamedSheetReader {
|
|
|
88
87
|
}
|
|
89
88
|
get merges() {
|
|
90
89
|
this.#ensureScanned();
|
|
91
|
-
return this.#merges;
|
|
90
|
+
return [...this.#merges];
|
|
92
91
|
}
|
|
93
92
|
#ensureScanned() {
|
|
94
93
|
if (this.#scanned)
|
|
@@ -156,7 +155,7 @@ function* scanSheet(xml, sharedStrings, xfStyles, hiddenColumns, merges) {
|
|
|
156
155
|
}
|
|
157
156
|
}
|
|
158
157
|
function collectHiddenColumn(attrs, hiddenColumns) {
|
|
159
|
-
if (attrs.hidden
|
|
158
|
+
if (!boolStrict(attrs.hidden))
|
|
160
159
|
return;
|
|
161
160
|
const min = numInteger(attrs.min, 1);
|
|
162
161
|
const max = numInteger(attrs.max, 1);
|
|
@@ -1,60 +1,22 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { parseXml } from '../../xml/xml-read.js';
|
|
2
|
+
import { localName } from '../../xml/xml-scan.js';
|
|
2
3
|
import { RunAccumulator } from './rich-runs.js';
|
|
3
4
|
export function parseSharedStrings(xml) {
|
|
4
5
|
if (xml === '')
|
|
5
6
|
return [];
|
|
6
7
|
const strings = [];
|
|
7
|
-
|
|
8
|
-
const runs = new RunAccumulator();
|
|
9
|
-
let isRich = false;
|
|
10
|
-
let capture = false;
|
|
11
|
-
let text = '';
|
|
8
|
+
const runs = new RunAccumulator({ container: 'si', readRuns: true });
|
|
12
9
|
parseXml(xml, {
|
|
13
|
-
onOpen(name, attrs) {
|
|
14
|
-
|
|
15
|
-
switch (local) {
|
|
16
|
-
case 'si':
|
|
17
|
-
plain = '';
|
|
18
|
-
runs.beginContainer();
|
|
19
|
-
isRich = false;
|
|
20
|
-
break;
|
|
21
|
-
case 'r':
|
|
22
|
-
isRich = true;
|
|
23
|
-
runs.beginRun();
|
|
24
|
-
break;
|
|
25
|
-
case 'rPr':
|
|
26
|
-
runs.beginProperties();
|
|
27
|
-
break;
|
|
28
|
-
case 't':
|
|
29
|
-
capture = true;
|
|
30
|
-
text = '';
|
|
31
|
-
break;
|
|
32
|
-
default:
|
|
33
|
-
runs.applyProperty(local, attrs);
|
|
34
|
-
break;
|
|
35
|
-
}
|
|
10
|
+
onOpen(name, attrs, selfClosing) {
|
|
11
|
+
runs.open(localName(name), attrs, selfClosing);
|
|
36
12
|
},
|
|
37
13
|
onText(chunk) {
|
|
38
|
-
|
|
39
|
-
text += chunk;
|
|
14
|
+
runs.text(chunk);
|
|
40
15
|
},
|
|
41
16
|
onClose(name) {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const decoded = decodeSpreadsheetText(text);
|
|
46
|
-
if (!runs.appendText(decoded))
|
|
47
|
-
plain += decoded;
|
|
48
|
-
capture = false;
|
|
49
|
-
break;
|
|
50
|
-
}
|
|
51
|
-
case 'r':
|
|
52
|
-
runs.endRun();
|
|
53
|
-
break;
|
|
54
|
-
case 'si':
|
|
55
|
-
strings.push(isRich ? { richText: runs.runs } : plain);
|
|
56
|
-
break;
|
|
57
|
-
}
|
|
17
|
+
if (runs.close(localName(name)) !== 'container')
|
|
18
|
+
return;
|
|
19
|
+
strings.push(runs.isRich ? { richText: runs.runs } : runs.plainText);
|
|
58
20
|
},
|
|
59
21
|
});
|
|
60
22
|
return strings;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Font, type TableStyleTable } from '../../core/style.ts';
|
|
2
|
-
import { type XmlAttributes } from '../../xml/xml-
|
|
2
|
+
import { type XmlAttributes } from '../../xml/xml-scan.ts';
|
|
3
3
|
import { type StyleTable } from '../style/xf-style.ts';
|
|
4
4
|
export type FontDraft = {
|
|
5
5
|
-readonly [K in keyof Font]?: Font[K];
|