@shbernal/ts-xlsx 1.3.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -10
- package/dist/bytes.d.ts +24 -0
- package/dist/bytes.js +34 -0
- package/dist/core/address.d.ts +72 -3
- package/dist/core/address.js +50 -10
- package/dist/core/autofilter.d.ts +13 -2
- package/dist/core/autofilter.js +35 -10
- package/dist/core/cell.d.ts +17 -16
- package/dist/core/cell.js +3 -7
- package/dist/core/color-resolution.d.ts +8 -8
- package/dist/core/color-resolution.js +7 -8
- package/dist/core/column.d.ts +8 -9
- package/dist/core/column.js +2 -4
- package/dist/core/comment-thread.d.ts +32 -23
- package/dist/core/comment-thread.js +4 -4
- package/dist/core/conditional-formatting-overlay.d.ts +6 -1
- package/dist/core/conditional-formatting-overlay.js +11 -0
- package/dist/core/conditional-formatting.d.ts +48 -8
- package/dist/core/conditional-formatting.js +88 -0
- package/dist/core/data-validation-overlay.d.ts +12 -1
- package/dist/core/data-validation-overlay.js +21 -2
- package/dist/core/data-validation.d.ts +12 -5
- package/dist/core/data-validation.js +34 -0
- package/dist/core/date.d.ts +3 -3
- package/dist/core/formula.d.ts +6 -6
- package/dist/core/grid-edits.d.ts +16 -0
- package/dist/core/grid-edits.js +29 -11
- package/dist/core/grid-shift.d.ts +19 -0
- package/dist/core/grid-shift.js +8 -0
- package/dist/core/image.d.ts +39 -2
- package/dist/core/image.js +10 -0
- package/dist/core/internal.d.ts +9 -1
- package/dist/core/limits.d.ts +4 -4
- package/dist/core/merge.d.ts +17 -12
- package/dist/core/merge.js +36 -5
- package/dist/core/page-setup.d.ts +20 -6
- package/dist/core/page-setup.js +8 -1
- package/dist/core/pivot-table.d.ts +4 -4
- package/dist/core/pivot-table.js +29 -20
- package/dist/core/preserved.d.ts +3 -3
- package/dist/core/protection.d.ts +7 -4
- package/dist/core/protection.js +20 -14
- package/dist/core/range.d.ts +16 -14
- package/dist/core/range.js +5 -5
- package/dist/core/row-input.d.ts +9 -8
- package/dist/core/row-input.js +12 -10
- package/dist/core/row.d.ts +7 -8
- package/dist/core/row.js +2 -4
- package/dist/core/style.d.ts +41 -14
- package/dist/core/style.js +82 -67
- package/dist/core/table-style.d.ts +9 -9
- package/dist/core/table-style.js +1 -1
- package/dist/core/table.d.ts +53 -51
- package/dist/core/table.js +83 -41
- package/dist/core/theme.d.ts +16 -44
- package/dist/core/theme.js +6 -148
- package/dist/core/value.d.ts +7 -7
- package/dist/core/workbook-protection.d.ts +2 -2
- package/dist/core/workbook-styles.d.ts +26 -0
- package/dist/core/workbook-styles.js +48 -0
- package/dist/core/workbook-theme.d.ts +25 -0
- package/dist/core/workbook-theme.js +49 -0
- package/dist/core/workbook-vba.d.ts +17 -0
- package/dist/core/workbook-vba.js +79 -0
- package/dist/core/workbook.d.ts +133 -87
- package/dist/core/workbook.js +74 -133
- package/dist/core/worksheet-comments.d.ts +17 -0
- package/dist/core/worksheet-comments.js +70 -0
- package/dist/core/worksheet-model.d.ts +4 -4
- package/dist/core/worksheet-model.js +1 -0
- package/dist/core/worksheet-pictures.d.ts +29 -0
- package/dist/core/worksheet-pictures.js +42 -0
- package/dist/core/worksheet.d.ts +100 -79
- package/dist/core/worksheet.js +63 -142
- package/dist/customui/errors.d.ts +1 -1
- package/dist/customui/ribbon.d.ts +3 -3
- package/dist/customui/ribbon.js +24 -23
- package/dist/entries/core.d.ts +7 -7
- package/dist/entries/core.js +1 -1
- 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/vba.d.ts +1 -1
- package/dist/entries/vba.js +1 -1
- package/dist/entries/xlsx.d.ts +1 -1
- package/dist/entries/xlsx.js +1 -1
- package/dist/errors.d.ts +12 -12
- package/dist/io/csv/read.js +2 -3
- package/dist/io/csv/write.d.ts +19 -4
- package/dist/io/csv/write.js +34 -4
- package/dist/io/opc/errors.d.ts +6 -6
- package/dist/io/opc/inflate.d.ts +1 -1
- package/dist/io/opc/inflate.js +2 -12
- package/dist/io/opc/namespaces.d.ts +1 -1
- package/dist/io/opc/read-opc.d.ts +19 -3
- package/dist/io/opc/read-opc.js +14 -8
- package/dist/io/opc/read-options.d.ts +2 -2
- package/dist/io/opc/rels.d.ts +11 -0
- package/dist/io/opc/rels.js +3 -2
- package/dist/io/opc/sniff-format.d.ts +2 -2
- package/dist/io/style/xf-style.d.ts +44 -7
- package/dist/io/style/xf-style.js +27 -0
- package/dist/io/xlsb/errors.d.ts +1 -1
- package/dist/io/xlsb/formula.d.ts +4 -4
- package/dist/io/xlsb/primitives.d.ts +8 -8
- package/dist/io/xlsb/ptg-functions.d.ts +1 -1
- package/dist/io/xlsb/read-styles.js +2 -20
- package/dist/io/xlsb/read.d.ts +2 -2
- package/dist/io/xlsx/cell-accumulator.d.ts +26 -9
- package/dist/io/xlsx/cell-accumulator.js +89 -46
- package/dist/io/xlsx/cell-value.d.ts +3 -3
- package/dist/io/xlsx/cell-value.js +9 -6
- package/dist/io/xlsx/color-xml.d.ts +1 -1
- package/dist/io/xlsx/color-xml.js +18 -23
- package/dist/io/xlsx/comments.d.ts +5 -5
- package/dist/io/xlsx/comments.js +17 -8
- package/dist/io/xlsx/conditional-formatting.d.ts +4 -4
- package/dist/io/xlsx/conditional-formatting.js +64 -58
- package/dist/io/xlsx/data-validation.d.ts +3 -3
- package/dist/io/xlsx/data-validation.js +35 -33
- package/dist/io/xlsx/errors.d.ts +3 -3
- package/dist/io/xlsx/hyperlinks.d.ts +4 -2
- package/dist/io/xlsx/hyperlinks.js +10 -18
- package/dist/io/xlsx/images.d.ts +1 -1
- package/dist/io/xlsx/images.js +25 -28
- package/dist/io/xlsx/namespaces.d.ts +2 -2
- package/dist/io/xlsx/package-plan.js +5 -2
- package/dist/io/xlsx/read-pivot.js +4 -13
- package/dist/io/xlsx/read-rows.d.ts +13 -13
- package/dist/io/xlsx/read-rows.js +19 -55
- package/dist/io/xlsx/read-shared-strings.js +7 -44
- package/dist/io/xlsx/read-styles.d.ts +3 -3
- package/dist/io/xlsx/read-styles.js +42 -66
- package/dist/io/xlsx/read-worksheet.js +73 -129
- package/dist/io/xlsx/read.d.ts +4 -6
- package/dist/io/xlsx/read.js +104 -142
- package/dist/io/xlsx/rich-runs.d.ts +41 -7
- package/dist/io/xlsx/rich-runs.js +94 -30
- package/dist/io/xlsx/rich-text.d.ts +1 -1
- package/dist/io/xlsx/shared-strings.d.ts +2 -2
- package/dist/io/xlsx/sheet-properties.d.ts +1 -1
- package/dist/io/xlsx/sheet-properties.js +14 -10
- package/dist/io/xlsx/styles.d.ts +9 -22
- package/dist/io/xlsx/styles.js +65 -89
- package/dist/io/xlsx/tables.d.ts +2 -2
- package/dist/io/xlsx/tables.js +20 -25
- package/dist/io/xlsx/theme-xml.d.ts +37 -0
- package/dist/io/xlsx/theme-xml.js +145 -0
- package/dist/io/xlsx/threaded-comments.d.ts +18 -27
- package/dist/io/xlsx/threaded-comments.js +26 -38
- package/dist/io/xlsx/workbook-xml.js +14 -8
- package/dist/io/xlsx/worksheet-xml.d.ts +2 -2
- package/dist/io/xlsx/worksheet-xml.js +40 -48
- package/dist/io/xlsx/write-stream.d.ts +23 -16
- package/dist/io/xlsx/write-stream.js +13 -14
- package/dist/io/xlsx/write.d.ts +8 -7
- package/dist/io/xlsx/write.js +6 -3
- package/dist/io/xlsx/x14-ext.d.ts +1 -1
- package/dist/sha512.d.ts +2 -0
- package/dist/sha512.js +146 -0
- package/dist/vba/bytes.d.ts +11 -0
- package/dist/vba/bytes.js +29 -0
- package/dist/vba/cfb-writer.d.ts +1 -1
- package/dist/vba/cfb.d.ts +1 -1
- package/dist/vba/cfb.js +15 -51
- package/dist/vba/codepage.d.ts +2 -2
- package/dist/vba/dir-records.d.ts +53 -0
- package/dist/vba/dir-records.js +28 -0
- package/dist/vba/errors.d.ts +2 -2
- package/dist/vba/index.d.ts +2 -2
- package/dist/vba/index.js +2 -2
- package/dist/vba/ms-ovba.d.ts +1 -1
- package/dist/vba/ms-ovba.js +1 -3
- package/dist/vba/project-editor.d.ts +9 -9
- package/dist/vba/project-editor.js +6 -44
- package/dist/vba/project.d.ts +5 -5
- package/dist/vba/project.js +3 -28
- package/dist/vba/vba-encoding.d.ts +1 -1
- package/dist/xml/errors.d.ts +1 -1
- package/dist/xml/xml-read.d.ts +98 -9
- package/dist/xml/xml-read.js +70 -0
- package/dist/xml/xml.d.ts +74 -10
- package/dist/xml/xml.js +39 -7
- package/package.json +59 -36
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { encodeAddress } from '../../core/address.js';
|
|
2
|
+
import { isPageOrder, isPageOrientation, } 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"' : '';
|
|
@@ -99,7 +100,7 @@ export function headerFooterXml(hf) {
|
|
|
99
100
|
if (differentFirst)
|
|
100
101
|
attrs += ' differentFirst="1"';
|
|
101
102
|
const body = children
|
|
102
|
-
.map(({ tag, key }) => `<${tag}>${
|
|
103
|
+
.map(({ tag, key }) => `<${tag}>${escapeSpreadsheetText(hf[key])}</${tag}>`)
|
|
103
104
|
.join('');
|
|
104
105
|
return `<headerFooter${attrs}>${body}</headerFooter>`;
|
|
105
106
|
}
|
|
@@ -127,12 +128,16 @@ export function pageMarginsXml(margins) {
|
|
|
127
128
|
return `<pageMargins ${attrs}/>`;
|
|
128
129
|
}
|
|
129
130
|
export function pageSetupXml(pageSetup, printerSettingsRelId) {
|
|
130
|
-
const attrs =
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
(pageSetup.pageOrder
|
|
135
|
-
|
|
131
|
+
const attrs = numAttr('paperSize', pageSetup.paperSize) +
|
|
132
|
+
numAttr('scale', pageSetup.scale) +
|
|
133
|
+
numAttr('fitToWidth', pageSetup.fitToWidth) +
|
|
134
|
+
numAttr('fitToHeight', pageSetup.fitToHeight) +
|
|
135
|
+
(pageSetup.pageOrder === undefined
|
|
136
|
+
? ''
|
|
137
|
+
: ` pageOrder="${checkedToken(pageSetup.pageOrder, isPageOrder, 'page order')}"`) +
|
|
138
|
+
(pageSetup.orientation === undefined
|
|
139
|
+
? ''
|
|
140
|
+
: ` orientation="${checkedToken(pageSetup.orientation, isPageOrientation, 'page orientation')}"`) +
|
|
136
141
|
(printerSettingsRelId !== null ? ` r:id="${printerSettingsRelId}"` : '');
|
|
137
142
|
return attrs === '' ? '' : `<pageSetup${attrs}/>`;
|
|
138
143
|
}
|
|
@@ -141,8 +146,7 @@ export function pageBreaksXml(breaks, element) {
|
|
|
141
146
|
return '';
|
|
142
147
|
const brks = breaks
|
|
143
148
|
.map((brk) => {
|
|
144
|
-
|
|
145
|
-
return `<brk id="${brk.id}"${maxAttr} man="1"/>`;
|
|
149
|
+
return `<brk${numAttr('id', brk.id)}${numAttr('max', brk.max)} man="1"/>`;
|
|
146
150
|
})
|
|
147
151
|
.join('');
|
|
148
152
|
return `<${element} count="${breaks.length}" manualBreakCount="${breaks.length}">${brks}</${element}>`;
|
package/dist/io/xlsx/styles.d.ts
CHANGED
|
@@ -1,22 +1,10 @@
|
|
|
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
|
/**
|
|
19
|
-
* The font every cell naming none of its own renders in
|
|
7
|
+
* The font every cell naming none of its own renders in, emitted as id 0. Take it from
|
|
20
8
|
* {@link Workbook.defaultFont}, which resolves the authored/declared/theme chain and guarantees a
|
|
21
9
|
* complete entry. Omitted, the registry falls back to the Office default.
|
|
22
10
|
*/
|
|
@@ -28,7 +16,7 @@ export interface StyleRegistryOptions {
|
|
|
28
16
|
*
|
|
29
17
|
* The reader flattens font 0 onto every xf that names it, so a cell that merely inherited the
|
|
30
18
|
* file's default arrives carrying it as a concrete face. That face is an artefact of reading, not
|
|
31
|
-
* an authored intent
|
|
19
|
+
* an authored intent (in the source file the cell said nothing about its font) so it must
|
|
32
20
|
* collapse back to id 0 and follow the new default rather than pin itself to the old one through a
|
|
33
21
|
* custom entry.
|
|
34
22
|
*/
|
|
@@ -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`/…
|
|
@@ -59,7 +47,7 @@ export declare class StyleRegistry {
|
|
|
59
47
|
* rule's `dxfId`, and every `<tableStyleElement dxfId="…">` inside a preserved `<tableStyle>` (see
|
|
60
48
|
* {@link seedTableStyles}). Those constructs are carried as opaque XML precisely *because* the
|
|
61
49
|
* indices they name do not move. Renumbering, reordering, or de-duplicating the seeded entries
|
|
62
|
-
* would silently re-point every one of them at a different format
|
|
50
|
+
* would silently re-point every one of them at a different format: a change no schema check and no
|
|
63
51
|
* round-trip of our own can catch, because the file stays perfectly valid and merely renders wrong.
|
|
64
52
|
*/
|
|
65
53
|
seedDifferentialStyles(fragments: readonly string[]): void;
|
|
@@ -88,13 +76,13 @@ export declare class StyleRegistry {
|
|
|
88
76
|
* {@link seedTableStyles}, whose preserved definitions these append after.
|
|
89
77
|
*
|
|
90
78
|
* A definition here **replaces** a preserved one of the same name. Two `<tableStyle>` elements
|
|
91
|
-
* sharing a name is ambiguous
|
|
92
|
-
* happened to index first
|
|
79
|
+
* sharing a name is ambiguous, since a table's `tableStyleInfo/@name` would reach whichever a
|
|
80
|
+
* consumer happened to index first, so authoring a name the source already used is read as overriding it,
|
|
93
81
|
* which is what asking for it means.
|
|
94
82
|
*/
|
|
95
83
|
addTableStyle(style: TableStyle): void;
|
|
96
84
|
/**
|
|
97
|
-
* Intern an authored differential style, returning the `<dxfs>` index that references it
|
|
85
|
+
* Intern an authored differential style, returning the `<dxfs>` index that references it: a
|
|
98
86
|
* conditional-formatting rule's `dxfId`, or a table style element's. Identical styles collapse to
|
|
99
87
|
* one entry, whichever feature asked for them, so a highlight rule and a table style's header row
|
|
100
88
|
* painted the same way share a single `<dxf>`.
|
|
@@ -107,4 +95,3 @@ export declare class StyleRegistry {
|
|
|
107
95
|
toXml(): string;
|
|
108
96
|
}
|
|
109
97
|
export declare function fontXml(font: Font, nameTag?: 'name' | 'rFont'): string;
|
|
110
|
-
export declare function dxfXml(style: DifferentialStyle): string;
|
package/dist/io/xlsx/styles.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { isBorderStyle, isFillPatternType, isFontScheme, isFontVerticalAlignment, isHorizontalAlignment, isNamedUnderlineStyle, isVerticalAlignment, } from '../../core/style.js';
|
|
2
2
|
import { TABLE_STYLE_ELEMENT_TYPES } from '../../core/table-style.js';
|
|
3
|
-
import { AuthoringError } from '../../errors.js';
|
|
4
|
-
import { escapeAttr, XML_DECLARATION } from '../../xml/xml.js';
|
|
5
3
|
import { decodeEntities } from '../../xml/xml-read.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
|
|
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,12 +279,6 @@ 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
284
|
if (alignment.horizontal !== undefined && alignment.horizontal !== 'general') {
|
|
@@ -307,17 +288,17 @@ function alignmentAttrs(alignment) {
|
|
|
307
288
|
parts.push(`vertical="${checkedToken(alignment.vertical, isVerticalAlignment, 'vertical alignment')}"`);
|
|
308
289
|
}
|
|
309
290
|
if (alignment.textRotation !== undefined && alignment.textRotation !== 0) {
|
|
310
|
-
parts.push(`textRotation="${
|
|
291
|
+
parts.push(`textRotation="${numberText(alignment.textRotation)}"`);
|
|
311
292
|
}
|
|
312
293
|
if (alignment.wrapText)
|
|
313
294
|
parts.push('wrapText="1"');
|
|
314
295
|
if (alignment.indent !== undefined && alignment.indent !== 0) {
|
|
315
|
-
parts.push(`indent="${
|
|
296
|
+
parts.push(`indent="${numberText(alignment.indent)}"`);
|
|
316
297
|
}
|
|
317
298
|
if (alignment.shrinkToFit)
|
|
318
299
|
parts.push('shrinkToFit="1"');
|
|
319
300
|
if (alignment.readingOrder !== undefined && alignment.readingOrder !== 0) {
|
|
320
|
-
parts.push(`readingOrder="${
|
|
301
|
+
parts.push(`readingOrder="${numberText(alignment.readingOrder)}"`);
|
|
321
302
|
}
|
|
322
303
|
return parts.join(' ');
|
|
323
304
|
}
|
|
@@ -349,20 +330,20 @@ export function fontXml(font, nameTag = 'name') {
|
|
|
349
330
|
parts.push(`<vertAlign val="${checkedToken(font.vertAlign, isFontVerticalAlignment, 'font vertical alignment')}"/>`);
|
|
350
331
|
}
|
|
351
332
|
if (font.size !== undefined)
|
|
352
|
-
parts.push(`<sz val="${
|
|
333
|
+
parts.push(`<sz val="${numberText(font.size)}"/>`);
|
|
353
334
|
if (font.color !== undefined)
|
|
354
335
|
parts.push(`<color ${colorAttrs(font.color)}/>`);
|
|
355
336
|
if (font.name !== undefined)
|
|
356
337
|
parts.push(`<${nameTag} val="${escapeAttr(font.name)}"/>`);
|
|
357
338
|
if (font.family !== undefined)
|
|
358
|
-
parts.push(`<family val="${
|
|
339
|
+
parts.push(`<family val="${numberText(font.family)}"/>`);
|
|
359
340
|
if (font.charset !== undefined)
|
|
360
|
-
parts.push(`<charset val="${
|
|
341
|
+
parts.push(`<charset val="${numberText(font.charset)}"/>`);
|
|
361
342
|
if (font.scheme !== undefined && font.scheme !== 'none')
|
|
362
343
|
parts.push(`<scheme val="${checkedToken(font.scheme, isFontScheme, 'font scheme')}"/>`);
|
|
363
344
|
return parts.join('');
|
|
364
345
|
}
|
|
365
|
-
|
|
346
|
+
function dxfXml(style) {
|
|
366
347
|
const parts = [];
|
|
367
348
|
if (style.font !== undefined) {
|
|
368
349
|
const font = fontXml(style.font);
|
|
@@ -380,18 +361,18 @@ export function dxfXml(style) {
|
|
|
380
361
|
}
|
|
381
362
|
function gradientFillXml(fill) {
|
|
382
363
|
const attrs = (fill.gradient === 'path' ? ' type="path"' : '') +
|
|
383
|
-
(fill.degree ? ` degree="${
|
|
364
|
+
(fill.degree ? ` degree="${numberText(fill.degree)}"` : '') +
|
|
384
365
|
insetAttr('left', fill.left) +
|
|
385
366
|
insetAttr('right', fill.right) +
|
|
386
367
|
insetAttr('top', fill.top) +
|
|
387
368
|
insetAttr('bottom', fill.bottom);
|
|
388
369
|
const stops = fill.stops
|
|
389
|
-
.map((stop) => `<stop position="${
|
|
370
|
+
.map((stop) => `<stop position="${numberText(stop.position)}"><color ${colorAttrs(stop.color)}/></stop>`)
|
|
390
371
|
.join('');
|
|
391
372
|
return `<gradientFill${attrs}>${stops}</gradientFill>`;
|
|
392
373
|
}
|
|
393
374
|
function insetAttr(name, value) {
|
|
394
|
-
return value ? ` ${name}="${
|
|
375
|
+
return value ? ` ${name}="${numberText(value)}"` : '';
|
|
395
376
|
}
|
|
396
377
|
function underlineXml(underline) {
|
|
397
378
|
if (underline === undefined || underline === false || underline === 'none')
|
|
@@ -400,12 +381,6 @@ function underlineXml(underline) {
|
|
|
400
381
|
return '<u/>';
|
|
401
382
|
return `<u val="${checkedToken(underline, isNamedUnderlineStyle, 'underline style')}"/>`;
|
|
402
383
|
}
|
|
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
384
|
function borderXml(border) {
|
|
410
385
|
const attrs = (border.diagonalUp ? ' diagonalUp="1"' : '') + (border.diagonalDown ? ' diagonalDown="1"' : '');
|
|
411
386
|
return (`<border${attrs}>` +
|
|
@@ -425,6 +400,7 @@ function edgeXml(tag, edge) {
|
|
|
425
400
|
return `<${tag} style="${style}"><color ${colorAttrs(edge.color)}/></${tag}>`;
|
|
426
401
|
}
|
|
427
402
|
function escapeFormatCode(code) {
|
|
403
|
+
assertRepresentable(code);
|
|
428
404
|
return code
|
|
429
405
|
.replace(/&/g, '&')
|
|
430
406
|
.replace(/</g, '<')
|
package/dist/io/xlsx/tables.d.ts
CHANGED
|
@@ -2,8 +2,8 @@ import { type Table, type TableOptions } from '../../core/table.ts';
|
|
|
2
2
|
export declare function tableXml(table: Table, id: number): string;
|
|
3
3
|
/**
|
|
4
4
|
* Parse a `<table>` part into the options that reconstruct it, or `undefined` when the XML is not a
|
|
5
|
-
* usable table (no name, no ref, or no columns
|
|
6
|
-
* rather than fabricate a degenerate table). Duplicate column names are not resolved here
|
|
5
|
+
* usable table (no name, no ref, or no columns: Excel treats such a part as corrupt, so we drop it
|
|
6
|
+
* rather than fabricate a degenerate table). Duplicate column names are not resolved here, since the
|
|
7
7
|
* {@link Table} constructor disambiguates them, so authoring and loading share one implementation.
|
|
8
8
|
*/
|
|
9
9
|
export declare function parseTable(xml: string): TableOptions | undefined;
|
package/dist/io/xlsx/tables.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { encodeAddress, tryDecodeRange } from '../../core/address.js';
|
|
2
2
|
import { isTotalsRowFunction, } from '../../core/table.js';
|
|
3
|
+
import { boolPresent, localName, numInteger, parseXml, TextCapture } from '../../xml/xml-read.js';
|
|
3
4
|
import { boolAttr, escapeAttr, escapeText, XML_DECLARATION } from '../../xml/xml.js';
|
|
4
|
-
import { localName, parseXml } from '../../xml/xml-read.js';
|
|
5
5
|
import { NS } from './relationships.js';
|
|
6
6
|
export function tableXml(table, id) {
|
|
7
7
|
const name = escapeAttr(table.name);
|
|
@@ -55,9 +55,6 @@ function tableColumnXml(column, id) {
|
|
|
55
55
|
}
|
|
56
56
|
return `<tableColumn ${attrs}/>`;
|
|
57
57
|
}
|
|
58
|
-
function parseOoxmlBool(value) {
|
|
59
|
-
return value !== '0' && value !== 'false';
|
|
60
|
-
}
|
|
61
58
|
export function parseTable(xml) {
|
|
62
59
|
let name;
|
|
63
60
|
let displayName;
|
|
@@ -68,21 +65,18 @@ export function parseTable(xml) {
|
|
|
68
65
|
let style;
|
|
69
66
|
let hasAutoFilter = false;
|
|
70
67
|
const columns = [];
|
|
71
|
-
|
|
72
|
-
let totalsFormula = '';
|
|
68
|
+
const totalsFormula = new TextCapture('totalsRowFormula');
|
|
73
69
|
parseXml(xml, {
|
|
74
|
-
onOpen(elementName, attrs) {
|
|
70
|
+
onOpen(elementName, attrs, selfClosing) {
|
|
75
71
|
switch (localName(elementName)) {
|
|
76
72
|
case 'table':
|
|
77
73
|
name = attrs.name ?? attrs.displayName;
|
|
78
74
|
displayName = attrs.displayName ?? attrs.name;
|
|
79
75
|
ref = attrs.ref;
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
if (attrs.totalsRowCount !== undefined)
|
|
83
|
-
totalsRowCount = Number(attrs.totalsRowCount);
|
|
76
|
+
headerRowCount = numInteger(attrs.headerRowCount, 0) ?? headerRowCount;
|
|
77
|
+
totalsRowCount = numInteger(attrs.totalsRowCount, 0) ?? totalsRowCount;
|
|
84
78
|
if (attrs.totalsRowShown !== undefined)
|
|
85
|
-
totalsRowShown =
|
|
79
|
+
totalsRowShown = boolPresent(attrs.totalsRowShown);
|
|
86
80
|
break;
|
|
87
81
|
case 'autoFilter':
|
|
88
82
|
hasAutoFilter = true;
|
|
@@ -92,13 +86,13 @@ export function parseTable(xml) {
|
|
|
92
86
|
if (attrs.name !== undefined)
|
|
93
87
|
captured.name = attrs.name;
|
|
94
88
|
if (attrs.showFirstColumn !== undefined)
|
|
95
|
-
captured.showFirstColumn =
|
|
89
|
+
captured.showFirstColumn = boolPresent(attrs.showFirstColumn);
|
|
96
90
|
if (attrs.showLastColumn !== undefined)
|
|
97
|
-
captured.showLastColumn =
|
|
91
|
+
captured.showLastColumn = boolPresent(attrs.showLastColumn);
|
|
98
92
|
if (attrs.showRowStripes !== undefined)
|
|
99
|
-
captured.showRowStripes =
|
|
93
|
+
captured.showRowStripes = boolPresent(attrs.showRowStripes);
|
|
100
94
|
if (attrs.showColumnStripes !== undefined) {
|
|
101
|
-
captured.showColumnStripes =
|
|
95
|
+
captured.showColumnStripes = boolPresent(attrs.showColumnStripes);
|
|
102
96
|
}
|
|
103
97
|
style = captured;
|
|
104
98
|
break;
|
|
@@ -117,27 +111,28 @@ export function parseTable(xml) {
|
|
|
117
111
|
break;
|
|
118
112
|
}
|
|
119
113
|
case 'totalsRowFormula':
|
|
120
|
-
|
|
121
|
-
totalsFormula = '';
|
|
114
|
+
totalsFormula.open('totalsRowFormula', selfClosing);
|
|
122
115
|
break;
|
|
123
116
|
}
|
|
124
117
|
},
|
|
125
118
|
onText(text) {
|
|
126
|
-
|
|
127
|
-
totalsFormula += text;
|
|
119
|
+
totalsFormula.text(text);
|
|
128
120
|
},
|
|
129
121
|
onClose(elementName) {
|
|
130
|
-
|
|
122
|
+
const formula = totalsFormula.close(localName(elementName));
|
|
123
|
+
if (formula === undefined)
|
|
131
124
|
return;
|
|
132
|
-
inTotalsFormula = false;
|
|
133
125
|
const column = columns[columns.length - 1];
|
|
134
126
|
if (column !== undefined)
|
|
135
|
-
column.totalsRowFormula =
|
|
127
|
+
column.totalsRowFormula = formula;
|
|
136
128
|
},
|
|
137
129
|
});
|
|
138
130
|
if (name === undefined || ref === undefined || columns.length === 0)
|
|
139
131
|
return undefined;
|
|
140
|
-
const
|
|
132
|
+
const decoded = tryDecodeRange(ref);
|
|
133
|
+
if (decoded === undefined)
|
|
134
|
+
return undefined;
|
|
135
|
+
const { top, left, bottom } = decoded;
|
|
141
136
|
if (top === undefined || left === undefined || bottom === undefined)
|
|
142
137
|
return undefined;
|
|
143
138
|
const headerRow = headerRowCount !== 0;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type ThemeColorScheme, type ThemeFontScheme, type ThemeOverrides } from '../../core/theme.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Extract the colour scheme from a theme part. Returns only the slots the part actually declares in a
|
|
4
|
+
* colour model this reader understands; an unrecognised one is dropped rather than guessed at, so a
|
|
5
|
+
* caller can tell "the theme says nothing here" from "the theme says black".
|
|
6
|
+
*
|
|
7
|
+
* Reads the `<a:clrScheme>` block alone. A theme carries a font scheme and a format scheme too, but
|
|
8
|
+
* neither participates in resolving a colour, and scanning the whole part would let a `<a:srgbClr>`
|
|
9
|
+
* buried in a gradient stop masquerade as a scheme slot.
|
|
10
|
+
*/
|
|
11
|
+
export declare function parseThemeColorScheme(themeXml: string): ThemeColorScheme;
|
|
12
|
+
/** Extract the major/minor latin typefaces from a theme part's `<a:fontScheme>`. */
|
|
13
|
+
export declare function parseThemeFontScheme(themeXml: string): ThemeFontScheme;
|
|
14
|
+
/**
|
|
15
|
+
* Apply authored colour/font overrides to a theme part, returning the new part text.
|
|
16
|
+
*
|
|
17
|
+
* Surgical by design: the base part rides through untouched except for the `<a:clrScheme>` and
|
|
18
|
+
* `<a:fontScheme>` blocks, and within those, only what the caller actually named. The format scheme,
|
|
19
|
+
* the gradients, line styles and effect styles that make a theme look like a theme, is left exactly
|
|
20
|
+
* as it was, because nobody hand-authors `fillStyleLst` gradient stops from a spreadsheet API and
|
|
21
|
+
* regenerating it would replace a designer's work with the Office default.
|
|
22
|
+
*
|
|
23
|
+
* A slot the caller did not override keeps its **verbatim source element**, not a re-serialisation of
|
|
24
|
+
* its value. That matters for `dk1`/`lt1`, which Excel writes as `<a:sysClr val="windowText"
|
|
25
|
+
* lastClr="000000"/>`: rewriting those as `<a:srgbClr>` would pin them to one machine's resolved
|
|
26
|
+
* window colours and break dark-mode following.
|
|
27
|
+
*/
|
|
28
|
+
export declare function applyThemeOverrides(baseXml: string, overrides: ThemeOverrides): string;
|
|
29
|
+
/**
|
|
30
|
+
* The theme part a workbook with no theme of its own ships: the standard Office theme.
|
|
31
|
+
*
|
|
32
|
+
* A spreadsheet must carry one even when nobody configured it: the stylesheet's own default font
|
|
33
|
+
* references `theme="1"`, which a consumer can only resolve against this part, so the two travel
|
|
34
|
+
* together. It is also the base {@link applyThemeOverrides} authors on top of when a workbook was
|
|
35
|
+
* built from scratch rather than read from a file.
|
|
36
|
+
*/
|
|
37
|
+
export declare const DEFAULT_THEME_XML: string;
|