@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,6 +1,7 @@
|
|
|
1
1
|
import { encodeAddress } from '../../core/address.js';
|
|
2
|
+
import { HEADER_FOOTER_ELEMENTS, MARGIN_SIDES, PAGE_SETUP_FACETS, PRINT_OPTION_FLAGS, } from '../../core/page-setup.js';
|
|
2
3
|
import { SHEET_PROTECTION_FLAGS } from '../../core/protection.js';
|
|
3
|
-
import {
|
|
4
|
+
import { boolAttr, checkedToken, escapeAttr, escapeSpreadsheetText, numAttr, numberText, } from '../../xml/xml.js';
|
|
4
5
|
import { colorAttrs } from './color-xml.js';
|
|
5
6
|
export function sheetViewsXml(view, active) {
|
|
6
7
|
const selected = active ? ' tabSelected="1"' : '';
|
|
@@ -79,16 +80,8 @@ function filterCriteriaXml(criteria) {
|
|
|
79
80
|
.join('');
|
|
80
81
|
return `<customFilters${andAttr}>${predicates}</customFilters>`;
|
|
81
82
|
}
|
|
82
|
-
const HF_CHILDREN = [
|
|
83
|
-
{ tag: 'oddHeader', key: 'oddHeader' },
|
|
84
|
-
{ tag: 'oddFooter', key: 'oddFooter' },
|
|
85
|
-
{ tag: 'evenHeader', key: 'evenHeader' },
|
|
86
|
-
{ tag: 'evenFooter', key: 'evenFooter' },
|
|
87
|
-
{ tag: 'firstHeader', key: 'firstHeader' },
|
|
88
|
-
{ tag: 'firstFooter', key: 'firstFooter' },
|
|
89
|
-
];
|
|
90
83
|
export function headerFooterXml(hf) {
|
|
91
|
-
const children =
|
|
84
|
+
const children = HEADER_FOOTER_ELEMENTS.filter((key) => hf[key] !== undefined);
|
|
92
85
|
if (children.length === 0)
|
|
93
86
|
return '';
|
|
94
87
|
const differentOddEven = hf.evenHeader !== undefined || hf.evenFooter !== undefined;
|
|
@@ -99,7 +92,7 @@ export function headerFooterXml(hf) {
|
|
|
99
92
|
if (differentFirst)
|
|
100
93
|
attrs += ' differentFirst="1"';
|
|
101
94
|
const body = children
|
|
102
|
-
.map((
|
|
95
|
+
.map((key) => `<${key}>${escapeSpreadsheetText(hf[key] ?? '')}</${key}>`)
|
|
103
96
|
.join('');
|
|
104
97
|
return `<headerFooter${attrs}>${body}</headerFooter>`;
|
|
105
98
|
}
|
|
@@ -111,13 +104,8 @@ const DEFAULT_MARGINS = {
|
|
|
111
104
|
header: 0.3,
|
|
112
105
|
footer: 0.3,
|
|
113
106
|
};
|
|
114
|
-
const MARGIN_SIDES = ['left', 'right', 'top', 'bottom', 'header', 'footer'];
|
|
115
107
|
export function printOptionsXml(printOptions) {
|
|
116
|
-
const attrs = boolAttr(
|
|
117
|
-
boolAttr('verticalCentered', printOptions.verticalCentered) +
|
|
118
|
-
boolAttr('headings', printOptions.headings) +
|
|
119
|
-
boolAttr('gridLines', printOptions.gridLines) +
|
|
120
|
-
boolAttr('gridLinesSet', printOptions.gridLinesSet);
|
|
108
|
+
const attrs = PRINT_OPTION_FLAGS.map((flag) => boolAttr(flag, printOptions[flag])).join('');
|
|
121
109
|
return attrs === '' ? '' : `<printOptions${attrs}/>`;
|
|
122
110
|
}
|
|
123
111
|
export function pageMarginsXml(margins) {
|
|
@@ -127,13 +115,23 @@ export function pageMarginsXml(margins) {
|
|
|
127
115
|
return `<pageMargins ${attrs}/>`;
|
|
128
116
|
}
|
|
129
117
|
export function pageSetupXml(pageSetup, printerSettingsRelId) {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
118
|
+
let attrs = '';
|
|
119
|
+
for (const facet of PAGE_SETUP_FACETS) {
|
|
120
|
+
switch (facet.kind) {
|
|
121
|
+
case 'count':
|
|
122
|
+
attrs += numAttr(facet.key, pageSetup[facet.key]);
|
|
123
|
+
break;
|
|
124
|
+
case 'token': {
|
|
125
|
+
const value = pageSetup[facet.key];
|
|
126
|
+
if (value !== undefined) {
|
|
127
|
+
attrs += ` ${facet.key}="${checkedToken(value, facet.isValid, facet.label)}"`;
|
|
128
|
+
}
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (printerSettingsRelId !== null)
|
|
134
|
+
attrs += ` r:id="${printerSettingsRelId}"`;
|
|
137
135
|
return attrs === '' ? '' : `<pageSetup${attrs}/>`;
|
|
138
136
|
}
|
|
139
137
|
export function pageBreaksXml(breaks, element) {
|
|
@@ -141,8 +139,7 @@ export function pageBreaksXml(breaks, element) {
|
|
|
141
139
|
return '';
|
|
142
140
|
const brks = breaks
|
|
143
141
|
.map((brk) => {
|
|
144
|
-
|
|
145
|
-
return `<brk id="${brk.id}"${maxAttr} man="1"/>`;
|
|
142
|
+
return `<brk${numAttr('id', brk.id)}${numAttr('max', brk.max)} man="1"/>`;
|
|
146
143
|
})
|
|
147
144
|
.join('');
|
|
148
145
|
return `<${element} count="${breaks.length}" manualBreakCount="${breaks.length}">${brks}</${element}>`;
|
package/dist/io/xlsx/styles.d.ts
CHANGED
|
@@ -1,18 +1,6 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type DifferentialStyle, type Font, type NamedCellStyle, type TableStyleTable } from '../../core/style.ts';
|
|
2
2
|
import { type TableStyle } from '../../core/table-style.ts';
|
|
3
|
-
|
|
4
|
-
export interface CellStyle {
|
|
5
|
-
readonly fill?: Fill | undefined;
|
|
6
|
-
readonly numFmt?: string | undefined;
|
|
7
|
-
readonly font?: Font | undefined;
|
|
8
|
-
readonly border?: Border | undefined;
|
|
9
|
-
readonly alignment?: Alignment | undefined;
|
|
10
|
-
readonly protection?: Protection | undefined;
|
|
11
|
-
/** The quote-prefix flag: an attribute on the xf, not a shared sub-table entry. */
|
|
12
|
-
readonly quotePrefix?: boolean | undefined;
|
|
13
|
-
/** The `xfId` link into `cellStyleXfs`: the named cell style this format inherits from (0 = Normal). */
|
|
14
|
-
readonly xfId?: number | undefined;
|
|
15
|
-
}
|
|
3
|
+
import type { XfStyle } from '../style/xf-style.ts';
|
|
16
4
|
/** What a {@link StyleRegistry} needs from its workbook before any style is interned. */
|
|
17
5
|
export interface StyleRegistryOptions {
|
|
18
6
|
/**
|
|
@@ -41,7 +29,7 @@ export declare class StyleRegistry {
|
|
|
41
29
|
* The `<cellXfs>` index for a composed cell/row/column style. A style with no facet needs
|
|
42
30
|
* no entry and resolves to the default xf 0, so its owner emits no `s` attribute at all.
|
|
43
31
|
*/
|
|
44
|
-
styleId(style:
|
|
32
|
+
styleId(style: XfStyle): number;
|
|
45
33
|
/**
|
|
46
34
|
* Seed the named cell styles (`<cellStyleXfs>`/`<cellStyles>`) read from a file, in place of the
|
|
47
35
|
* lone default, interning each style's facets into the shared sub-tables so its `fillId`/`fontId`/…
|
package/dist/io/xlsx/styles.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { isBorderStyle, isFillPatternType, isFontScheme, isFontVerticalAlignment,
|
|
1
|
+
import { ALIGNMENT_FACETS, isBorderStyle, isFillPatternType, isFontScheme, isFontVerticalAlignment, isNamedUnderlineStyle, } from '../../core/style.js';
|
|
2
2
|
import { TABLE_STYLE_ELEMENT_TYPES } from '../../core/table-style.js';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { assertRepresentable, escapeAttr, XML_DECLARATION } from '../../xml/xml.js';
|
|
3
|
+
import { decodeEntities } from '../../xml/xml-scan.js';
|
|
4
|
+
import { assertRepresentable, checkedToken, escapeAttr, numberText, XML_DECLARATION, } from '../../xml/xml.js';
|
|
6
5
|
import { colorAttrs } from './color-xml.js';
|
|
7
6
|
import { MARKUP_COMPATIBILITY_NS, SPREADSHEETML_NS } from './namespaces.js';
|
|
8
7
|
const RESERVED_FILL_COUNT = 2;
|
|
@@ -21,6 +20,36 @@ const DEFAULT_FORMAT = {
|
|
|
21
20
|
quotePrefix: false,
|
|
22
21
|
xfId: 0,
|
|
23
22
|
};
|
|
23
|
+
class InternTable {
|
|
24
|
+
#entries = [];
|
|
25
|
+
#idByKey = new Map();
|
|
26
|
+
#base;
|
|
27
|
+
constructor(base) {
|
|
28
|
+
this.#base = base;
|
|
29
|
+
}
|
|
30
|
+
get entries() {
|
|
31
|
+
return this.#entries;
|
|
32
|
+
}
|
|
33
|
+
get size() {
|
|
34
|
+
return this.#entries.length;
|
|
35
|
+
}
|
|
36
|
+
intern(key, entry) {
|
|
37
|
+
const existing = this.#idByKey.get(key);
|
|
38
|
+
if (existing !== undefined)
|
|
39
|
+
return existing;
|
|
40
|
+
const id = this.#base + this.#entries.length;
|
|
41
|
+
this.#entries.push(entry);
|
|
42
|
+
this.#idByKey.set(key, id);
|
|
43
|
+
return id;
|
|
44
|
+
}
|
|
45
|
+
seed(entry) {
|
|
46
|
+
const id = this.#base + this.#entries.length;
|
|
47
|
+
this.#entries.push(entry);
|
|
48
|
+
if (!this.#idByKey.has(entry))
|
|
49
|
+
this.#idByKey.set(entry, id);
|
|
50
|
+
return id;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
24
53
|
export class StyleRegistry {
|
|
25
54
|
#defaultFontBody;
|
|
26
55
|
#font0Bodies;
|
|
@@ -31,22 +60,17 @@ export class StyleRegistry {
|
|
|
31
60
|
? [this.#defaultFontBody]
|
|
32
61
|
: [this.#defaultFontBody, fontXml(options.declaredDefaultFont)]);
|
|
33
62
|
}
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
#fontXml = [];
|
|
39
|
-
#fontIdBySignature = new Map();
|
|
40
|
-
#borderXml = [];
|
|
41
|
-
#borderIdBySignature = new Map();
|
|
63
|
+
#fills = new InternTable(RESERVED_FILL_COUNT);
|
|
64
|
+
#numFmts = new InternTable(CUSTOM_NUMFMT_BASE);
|
|
65
|
+
#fonts = new InternTable(RESERVED_FONT_COUNT);
|
|
66
|
+
#borders = new InternTable(RESERVED_BORDER_COUNT);
|
|
42
67
|
#formats = [DEFAULT_FORMAT];
|
|
43
68
|
#xfIndexBySignature = new Map();
|
|
44
69
|
#cellStyleXfs = [DEFAULT_FORMAT];
|
|
45
70
|
#cellStyleNames = [
|
|
46
71
|
{ name: 'Normal', builtinId: 0, xfId: 0 },
|
|
47
72
|
];
|
|
48
|
-
#
|
|
49
|
-
#dxfIndexByFragment = new Map();
|
|
73
|
+
#dxfs = new InternTable(0);
|
|
50
74
|
#indexedColors = [];
|
|
51
75
|
#mruColors = [];
|
|
52
76
|
#tableStyles = { styles: [] };
|
|
@@ -94,12 +118,8 @@ export class StyleRegistry {
|
|
|
94
118
|
});
|
|
95
119
|
}
|
|
96
120
|
seedDifferentialStyles(fragments) {
|
|
97
|
-
for (const fragment of fragments)
|
|
98
|
-
|
|
99
|
-
this.#dxfXml.push(fragment);
|
|
100
|
-
if (!this.#dxfIndexByFragment.has(fragment))
|
|
101
|
-
this.#dxfIndexByFragment.set(fragment, index);
|
|
102
|
-
}
|
|
121
|
+
for (const fragment of fragments)
|
|
122
|
+
this.#dxfs.seed(fragment);
|
|
103
123
|
}
|
|
104
124
|
seedIndexedColors(fragments) {
|
|
105
125
|
this.#indexedColors.length = 0;
|
|
@@ -128,69 +148,36 @@ export class StyleRegistry {
|
|
|
128
148
|
}
|
|
129
149
|
differentialStyleId(style) {
|
|
130
150
|
const fragment = dxfXml(style);
|
|
131
|
-
|
|
132
|
-
if (index === undefined) {
|
|
133
|
-
index = this.#dxfXml.length;
|
|
134
|
-
this.#dxfXml.push(fragment);
|
|
135
|
-
this.#dxfIndexByFragment.set(fragment, index);
|
|
136
|
-
}
|
|
137
|
-
return index;
|
|
151
|
+
return this.#dxfs.intern(fragment, fragment);
|
|
138
152
|
}
|
|
139
153
|
#internFill(fill) {
|
|
140
|
-
|
|
141
|
-
let id = this.#fillIdBySignature.get(signature);
|
|
142
|
-
if (id === undefined) {
|
|
143
|
-
id = RESERVED_FILL_COUNT + this.#fillXml.length;
|
|
144
|
-
this.#fillXml.push(patternFillXml(fill, { solidBgFallback: true }));
|
|
145
|
-
this.#fillIdBySignature.set(signature, id);
|
|
146
|
-
}
|
|
147
|
-
return id;
|
|
154
|
+
return this.#fills.intern(fillSignature(fill), patternFillXml(fill, { solidBgFallback: true }));
|
|
148
155
|
}
|
|
149
156
|
#internNumFmt(code) {
|
|
150
|
-
|
|
151
|
-
if (id === undefined) {
|
|
152
|
-
id = CUSTOM_NUMFMT_BASE + this.#numFmtCodes.length;
|
|
153
|
-
this.#numFmtCodes.push(code);
|
|
154
|
-
this.#numFmtIdByCode.set(code, id);
|
|
155
|
-
}
|
|
156
|
-
return id;
|
|
157
|
+
return this.#numFmts.intern(code, code);
|
|
157
158
|
}
|
|
158
159
|
#internFont(font) {
|
|
159
160
|
const xml = fontXml(font);
|
|
160
161
|
if (xml === '' || this.#font0Bodies.has(xml))
|
|
161
162
|
return 0;
|
|
162
|
-
|
|
163
|
-
if (id === undefined) {
|
|
164
|
-
id = RESERVED_FONT_COUNT + this.#fontXml.length;
|
|
165
|
-
this.#fontXml.push(`<font>${xml}</font>`);
|
|
166
|
-
this.#fontIdBySignature.set(xml, id);
|
|
167
|
-
}
|
|
168
|
-
return id;
|
|
163
|
+
return this.#fonts.intern(xml, `<font>${xml}</font>`);
|
|
169
164
|
}
|
|
170
165
|
#internBorder(border) {
|
|
171
166
|
const xml = borderXml(border);
|
|
172
|
-
|
|
173
|
-
return 0;
|
|
174
|
-
let id = this.#borderIdBySignature.get(xml);
|
|
175
|
-
if (id === undefined) {
|
|
176
|
-
id = RESERVED_BORDER_COUNT + this.#borderXml.length;
|
|
177
|
-
this.#borderXml.push(xml);
|
|
178
|
-
this.#borderIdBySignature.set(xml, id);
|
|
179
|
-
}
|
|
180
|
-
return id;
|
|
167
|
+
return xml === DEFAULT_BORDER ? 0 : this.#borders.intern(xml, xml);
|
|
181
168
|
}
|
|
182
169
|
toXml() {
|
|
183
|
-
const fillCount = RESERVED_FILL_COUNT + this.#
|
|
170
|
+
const fillCount = RESERVED_FILL_COUNT + this.#fills.size;
|
|
184
171
|
const fills = '<fill><patternFill patternType="none"/></fill>' +
|
|
185
172
|
'<fill><patternFill patternType="gray125"/></fill>' +
|
|
186
|
-
this.#
|
|
173
|
+
this.#fills.entries.join('');
|
|
187
174
|
const cellXfs = this.#formats.map((format) => xfXml(format, format.xfId)).join('');
|
|
188
175
|
const cellStyleXfs = this.#cellStyleXfs.map((format) => xfXml(format, null)).join('');
|
|
189
176
|
const cellStyles = this.#cellStyleNames.map(cellStyleTag).join('');
|
|
190
|
-
const fontCount = RESERVED_FONT_COUNT + this.#
|
|
191
|
-
const fonts = `<font>${this.#defaultFontBody}</font>${this.#
|
|
192
|
-
const borderCount = RESERVED_BORDER_COUNT + this.#
|
|
193
|
-
const borders = DEFAULT_BORDER + this.#
|
|
177
|
+
const fontCount = RESERVED_FONT_COUNT + this.#fonts.size;
|
|
178
|
+
const fonts = `<font>${this.#defaultFontBody}</font>${this.#fonts.entries.join('')}`;
|
|
179
|
+
const borderCount = RESERVED_BORDER_COUNT + this.#borders.size;
|
|
180
|
+
const borders = DEFAULT_BORDER + this.#borders.entries.join('');
|
|
194
181
|
return (XML_DECLARATION +
|
|
195
182
|
`<styleSheet xmlns="${SPREADSHEETML_NS}"${this.#foreignNamespaceAttrs()}>` +
|
|
196
183
|
this.#numFmtsXml() +
|
|
@@ -245,17 +232,17 @@ export class StyleRegistry {
|
|
|
245
232
|
return `<colors>${indexed}${mru}</colors>`;
|
|
246
233
|
}
|
|
247
234
|
#dxfsXml() {
|
|
248
|
-
if (this.#
|
|
235
|
+
if (this.#dxfs.size === 0)
|
|
249
236
|
return '<dxfs count="0"/>';
|
|
250
|
-
return `<dxfs count="${this.#
|
|
237
|
+
return `<dxfs count="${this.#dxfs.size}">${this.#dxfs.entries.join('')}</dxfs>`;
|
|
251
238
|
}
|
|
252
239
|
#numFmtsXml() {
|
|
253
|
-
if (this.#
|
|
240
|
+
if (this.#numFmts.size === 0)
|
|
254
241
|
return '';
|
|
255
|
-
const entries = this.#
|
|
242
|
+
const entries = this.#numFmts.entries
|
|
256
243
|
.map((code, i) => `<numFmt numFmtId="${CUSTOM_NUMFMT_BASE + i}" formatCode="${escapeFormatCode(code)}"/>`)
|
|
257
244
|
.join('');
|
|
258
|
-
return `<numFmts count="${this.#
|
|
245
|
+
return `<numFmts count="${this.#numFmts.size}">${entries}</numFmts>`;
|
|
259
246
|
}
|
|
260
247
|
}
|
|
261
248
|
function isDefaultFormat(format) {
|
|
@@ -292,32 +279,29 @@ function cellStyleTag(entry) {
|
|
|
292
279
|
const builtin = entry.builtinId === undefined ? '' : ` builtinId="${entry.builtinId}"`;
|
|
293
280
|
return `<cellStyle name="${escapeAttr(entry.name)}" xfId="${entry.xfId}"${builtin}/>`;
|
|
294
281
|
}
|
|
295
|
-
function checkedToken(value, isValid, kind) {
|
|
296
|
-
if (!isValid(value)) {
|
|
297
|
-
throw new AuthoringError(`Invalid ${kind} ${JSON.stringify(value)}: not a value the OOXML enumeration allows`);
|
|
298
|
-
}
|
|
299
|
-
return value;
|
|
300
|
-
}
|
|
301
282
|
function alignmentAttrs(alignment) {
|
|
302
283
|
const parts = [];
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
284
|
+
for (const facet of ALIGNMENT_FACETS) {
|
|
285
|
+
switch (facet.kind) {
|
|
286
|
+
case 'token': {
|
|
287
|
+
const value = alignment[facet.key];
|
|
288
|
+
if (value === undefined || value === facet.omit)
|
|
289
|
+
break;
|
|
290
|
+
parts.push(`${facet.key}="${checkedToken(value, facet.isValid, facet.label)}"`);
|
|
291
|
+
break;
|
|
292
|
+
}
|
|
293
|
+
case 'number': {
|
|
294
|
+
const value = alignment[facet.key];
|
|
295
|
+
if (value === undefined || value === 0)
|
|
296
|
+
break;
|
|
297
|
+
parts.push(`${facet.key}="${numberText(value)}"`);
|
|
298
|
+
break;
|
|
299
|
+
}
|
|
300
|
+
case 'flag':
|
|
301
|
+
if (alignment[facet.key])
|
|
302
|
+
parts.push(`${facet.key}="1"`);
|
|
303
|
+
break;
|
|
304
|
+
}
|
|
321
305
|
}
|
|
322
306
|
return parts.join(' ');
|
|
323
307
|
}
|
|
@@ -349,15 +333,15 @@ export function fontXml(font, nameTag = 'name') {
|
|
|
349
333
|
parts.push(`<vertAlign val="${checkedToken(font.vertAlign, isFontVerticalAlignment, 'font vertical alignment')}"/>`);
|
|
350
334
|
}
|
|
351
335
|
if (font.size !== undefined)
|
|
352
|
-
parts.push(`<sz val="${
|
|
336
|
+
parts.push(`<sz val="${numberText(font.size)}"/>`);
|
|
353
337
|
if (font.color !== undefined)
|
|
354
338
|
parts.push(`<color ${colorAttrs(font.color)}/>`);
|
|
355
339
|
if (font.name !== undefined)
|
|
356
340
|
parts.push(`<${nameTag} val="${escapeAttr(font.name)}"/>`);
|
|
357
341
|
if (font.family !== undefined)
|
|
358
|
-
parts.push(`<family val="${
|
|
342
|
+
parts.push(`<family val="${numberText(font.family)}"/>`);
|
|
359
343
|
if (font.charset !== undefined)
|
|
360
|
-
parts.push(`<charset val="${
|
|
344
|
+
parts.push(`<charset val="${numberText(font.charset)}"/>`);
|
|
361
345
|
if (font.scheme !== undefined && font.scheme !== 'none')
|
|
362
346
|
parts.push(`<scheme val="${checkedToken(font.scheme, isFontScheme, 'font scheme')}"/>`);
|
|
363
347
|
return parts.join('');
|
|
@@ -380,18 +364,18 @@ function dxfXml(style) {
|
|
|
380
364
|
}
|
|
381
365
|
function gradientFillXml(fill) {
|
|
382
366
|
const attrs = (fill.gradient === 'path' ? ' type="path"' : '') +
|
|
383
|
-
(fill.degree ? ` degree="${
|
|
367
|
+
(fill.degree ? ` degree="${numberText(fill.degree)}"` : '') +
|
|
384
368
|
insetAttr('left', fill.left) +
|
|
385
369
|
insetAttr('right', fill.right) +
|
|
386
370
|
insetAttr('top', fill.top) +
|
|
387
371
|
insetAttr('bottom', fill.bottom);
|
|
388
372
|
const stops = fill.stops
|
|
389
|
-
.map((stop) => `<stop position="${
|
|
373
|
+
.map((stop) => `<stop position="${numberText(stop.position)}"><color ${colorAttrs(stop.color)}/></stop>`)
|
|
390
374
|
.join('');
|
|
391
375
|
return `<gradientFill${attrs}>${stops}</gradientFill>`;
|
|
392
376
|
}
|
|
393
377
|
function insetAttr(name, value) {
|
|
394
|
-
return value ? ` ${name}="${
|
|
378
|
+
return value ? ` ${name}="${numberText(value)}"` : '';
|
|
395
379
|
}
|
|
396
380
|
function underlineXml(underline) {
|
|
397
381
|
if (underline === undefined || underline === false || underline === 'none')
|
|
@@ -400,12 +384,6 @@ function underlineXml(underline) {
|
|
|
400
384
|
return '<u/>';
|
|
401
385
|
return `<u val="${checkedToken(underline, isNamedUnderlineStyle, 'underline style')}"/>`;
|
|
402
386
|
}
|
|
403
|
-
function numberAttr(value) {
|
|
404
|
-
if (!Number.isFinite(value)) {
|
|
405
|
-
throw new AuthoringError(`cannot serialise a non-finite font metric (${value})`);
|
|
406
|
-
}
|
|
407
|
-
return String(value);
|
|
408
|
-
}
|
|
409
387
|
function borderXml(border) {
|
|
410
388
|
const attrs = (border.diagonalUp ? ' diagonalUp="1"' : '') + (border.diagonalDown ? ' diagonalDown="1"' : '');
|
|
411
389
|
return (`<border${attrs}>` +
|
package/dist/io/xlsx/tables.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { encodeAddress, tryDecodeRange } from '../../core/address.js';
|
|
2
2
|
import { isTotalsRowFunction, } from '../../core/table.js';
|
|
3
|
-
import {
|
|
3
|
+
import { parseXml, TextCapture } from '../../xml/xml-read.js';
|
|
4
|
+
import { boolPresent, localName, numInteger } from '../../xml/xml-scan.js';
|
|
4
5
|
import { boolAttr, escapeAttr, escapeText, XML_DECLARATION } from '../../xml/xml.js';
|
|
5
6
|
import { NS } from './relationships.js';
|
|
6
7
|
export function tableXml(table, id) {
|
|
@@ -65,10 +66,9 @@ export function parseTable(xml) {
|
|
|
65
66
|
let style;
|
|
66
67
|
let hasAutoFilter = false;
|
|
67
68
|
const columns = [];
|
|
68
|
-
|
|
69
|
-
let totalsFormula = '';
|
|
69
|
+
const totalsFormula = new TextCapture('totalsRowFormula');
|
|
70
70
|
parseXml(xml, {
|
|
71
|
-
onOpen(elementName, attrs) {
|
|
71
|
+
onOpen(elementName, attrs, selfClosing) {
|
|
72
72
|
switch (localName(elementName)) {
|
|
73
73
|
case 'table':
|
|
74
74
|
name = attrs.name ?? attrs.displayName;
|
|
@@ -112,27 +112,28 @@ export function parseTable(xml) {
|
|
|
112
112
|
break;
|
|
113
113
|
}
|
|
114
114
|
case 'totalsRowFormula':
|
|
115
|
-
|
|
116
|
-
totalsFormula = '';
|
|
115
|
+
totalsFormula.open('totalsRowFormula', selfClosing);
|
|
117
116
|
break;
|
|
118
117
|
}
|
|
119
118
|
},
|
|
120
119
|
onText(text) {
|
|
121
|
-
|
|
122
|
-
totalsFormula += text;
|
|
120
|
+
totalsFormula.text(text);
|
|
123
121
|
},
|
|
124
122
|
onClose(elementName) {
|
|
125
|
-
|
|
123
|
+
const formula = totalsFormula.close(localName(elementName));
|
|
124
|
+
if (formula === undefined)
|
|
126
125
|
return;
|
|
127
|
-
inTotalsFormula = false;
|
|
128
126
|
const column = columns[columns.length - 1];
|
|
129
127
|
if (column !== undefined)
|
|
130
|
-
column.totalsRowFormula =
|
|
128
|
+
column.totalsRowFormula = formula;
|
|
131
129
|
},
|
|
132
130
|
});
|
|
133
131
|
if (name === undefined || ref === undefined || columns.length === 0)
|
|
134
132
|
return undefined;
|
|
135
|
-
const
|
|
133
|
+
const decoded = tryDecodeRange(ref);
|
|
134
|
+
if (decoded === undefined)
|
|
135
|
+
return undefined;
|
|
136
|
+
const { top, left, bottom } = decoded;
|
|
136
137
|
if (top === undefined || left === undefined || bottom === undefined)
|
|
137
138
|
return undefined;
|
|
138
139
|
const headerRow = headerRowCount !== 0;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { DEFAULT_THEME_COLOR_SCHEME, normalizeThemeColor, } from '../../core/theme.js';
|
|
2
|
-
import { decodeEntities } from '../../xml/xml-
|
|
1
|
+
import { DEFAULT_THEME_COLOR_SCHEME, isThemeColorSlot, normalizeThemeColor, } from '../../core/theme.js';
|
|
2
|
+
import { decodeEntities } from '../../xml/xml-scan.js';
|
|
3
3
|
import { escapeAttr } from '../../xml/xml.js';
|
|
4
4
|
const SCHEME_SLOT = new RegExp('<a:(dk1|lt1|dk2|lt2|accent[1-6]|hlink|folHlink)\\b[^>]*>' + '\\s*<a:(srgbClr|sysClr)\\b([^>]*)>', 'g');
|
|
5
5
|
export function parseThemeColorScheme(themeXml) {
|
|
@@ -9,6 +9,8 @@ export function parseThemeColorScheme(themeXml) {
|
|
|
9
9
|
const scheme = {};
|
|
10
10
|
for (const match of (block[1] ?? '').matchAll(SCHEME_SLOT)) {
|
|
11
11
|
const slot = match[1];
|
|
12
|
+
if (slot === undefined || !isThemeColorSlot(slot))
|
|
13
|
+
continue;
|
|
12
14
|
const attrs = match[3] ?? '';
|
|
13
15
|
const source = match[2] === 'sysClr' ? /\blastClr="([^"]*)"/ : /\bval="([^"]*)"/;
|
|
14
16
|
const value = source.exec(attrs)?.[1];
|
|
@@ -58,8 +60,7 @@ export function applyThemeOverrides(baseXml, overrides) {
|
|
|
58
60
|
const authored = colors[slot];
|
|
59
61
|
const inner = authored !== undefined
|
|
60
62
|
? `<a:srgbClr val="${normalizeThemeColor(authored)}"/>`
|
|
61
|
-
: (sourceElements[slot] ??
|
|
62
|
-
`<a:srgbClr val="${DEFAULT_THEME_COLOR_SCHEME[slot]}"/>`);
|
|
63
|
+
: (sourceElements[slot] ?? `<a:srgbClr val="${DEFAULT_THEME_COLOR_SCHEME[slot]}"/>`);
|
|
63
64
|
return `<a:${slot}>${inner}</a:${slot}>`;
|
|
64
65
|
}).join('');
|
|
65
66
|
xml = replaceBlockBody(xml, 'clrScheme', body);
|
|
@@ -78,7 +79,9 @@ function parseThemeColorElements(themeXml) {
|
|
|
78
79
|
const elements = {};
|
|
79
80
|
const pattern = /<a:(dk1|lt1|dk2|lt2|accent[1-6]|hlink|folHlink)>([\s\S]*?)<\/a:\1>|<a:(dk1|lt1|dk2|lt2|accent[1-6]|hlink|folHlink)\/>/g;
|
|
80
81
|
for (const match of (block[1] ?? '').matchAll(pattern)) {
|
|
81
|
-
const slot =
|
|
82
|
+
const slot = match[1] ?? match[3];
|
|
83
|
+
if (slot === undefined || !isThemeColorSlot(slot))
|
|
84
|
+
continue;
|
|
82
85
|
const inner = match[2];
|
|
83
86
|
if (inner !== undefined && inner !== '')
|
|
84
87
|
elements[slot] = inner;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { encodeAddress, tryDecodeCellRef } from '../../core/address.js';
|
|
2
2
|
import { MENTION_OFFSET_MAX, } from '../../core/comment-thread.js';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { parseXml, TextCapture } from '../../xml/xml-read.js';
|
|
4
|
+
import { boolStrict, decodeSpreadsheetText, localName, numInteger, } from '../../xml/xml-scan.js';
|
|
5
|
+
import { escapeAttr, escapeSpreadsheetText, textAttr, XML_DECLARATION } from '../../xml/xml.js';
|
|
5
6
|
import { THREADED_COMMENTS_NS } from './namespaces.js';
|
|
6
7
|
export function parsePersons(xml) {
|
|
7
8
|
const persons = [];
|
|
@@ -27,33 +28,33 @@ export function parseThreadedComments(xml) {
|
|
|
27
28
|
const messages = [];
|
|
28
29
|
let open;
|
|
29
30
|
let mentions = [];
|
|
30
|
-
let inText = false;
|
|
31
31
|
let text = '';
|
|
32
|
+
const capture = new TextCapture('text');
|
|
32
33
|
parseXml(xml, {
|
|
33
|
-
onOpen(name, attrs) {
|
|
34
|
+
onOpen(name, attrs, selfClosing) {
|
|
34
35
|
const local = localName(name);
|
|
35
36
|
if (local === 'threadedComment') {
|
|
36
37
|
open = attrs;
|
|
37
38
|
text = '';
|
|
38
39
|
mentions = [];
|
|
39
40
|
}
|
|
40
|
-
else if (
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
41
|
+
else if (open !== undefined) {
|
|
42
|
+
capture.open(local, selfClosing);
|
|
43
|
+
if (local === 'mention') {
|
|
44
|
+
const mention = mentionFrom(attrs);
|
|
45
|
+
if (mention !== undefined)
|
|
46
|
+
mentions.push(mention);
|
|
47
|
+
}
|
|
47
48
|
}
|
|
48
49
|
},
|
|
49
50
|
onText(chunk) {
|
|
50
|
-
|
|
51
|
-
text += chunk;
|
|
51
|
+
capture.text(chunk);
|
|
52
52
|
},
|
|
53
53
|
onClose(name) {
|
|
54
54
|
const local = localName(name);
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
const body = capture.close(local);
|
|
56
|
+
if (body !== undefined) {
|
|
57
|
+
text = body;
|
|
57
58
|
}
|
|
58
59
|
else if (local === 'threadedComment') {
|
|
59
60
|
if (open !== undefined) {
|
|
@@ -159,8 +160,8 @@ export function threadedCommentsXml(threads) {
|
|
|
159
160
|
`<ThreadedComments xmlns="${THREADED_COMMENTS_NS}">${messages.join('')}</ThreadedComments>`);
|
|
160
161
|
}
|
|
161
162
|
function threadedCommentXml(ref, comment, tail) {
|
|
162
|
-
const date =
|
|
163
|
-
const person =
|
|
163
|
+
const date = textAttr('dT', comment.date);
|
|
164
|
+
const person = textAttr('personId', comment.personId);
|
|
164
165
|
return (`<threadedComment ref="${escapeAttr(ref)}"${date}${person} id="${escapeAttr(comment.id)}"${tail}>` +
|
|
165
166
|
`<text>${escapeSpreadsheetText(comment.text)}</text>` +
|
|
166
167
|
mentionsXml(comment.mentions) +
|
|
@@ -184,8 +185,8 @@ function writableOffset(value) {
|
|
|
184
185
|
}
|
|
185
186
|
export function personsXml(persons) {
|
|
186
187
|
const entries = persons.map((person) => {
|
|
187
|
-
const userId =
|
|
188
|
-
const providerId =
|
|
188
|
+
const userId = textAttr('userId', person.userId);
|
|
189
|
+
const providerId = textAttr('providerId', person.providerId);
|
|
189
190
|
return (`<person displayName="${escapeAttr(person.displayName)}"` +
|
|
190
191
|
` id="${escapeAttr(person.id)}"${userId}${providerId}/>`);
|
|
191
192
|
});
|