@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
package/dist/core/theme.d.ts
CHANGED
|
@@ -3,18 +3,20 @@
|
|
|
3
3
|
*
|
|
4
4
|
* This order is not the order the slots appear in the theme part. ISO/IEC 29500 §20.1.6.2 documents
|
|
5
5
|
* the `<a:clrScheme>` child sequence as `dk1, lt1, dk2, lt2, accent1…6, hlink, folHlink`, and that is
|
|
6
|
-
* how the XML is written
|
|
6
|
+
* how the XML is written. But SpreadsheetML's `theme="n"` does **not** index that sequence: Excel
|
|
7
7
|
* swaps each dark/light pair: index 0 is `lt1`, 1 is `dk1`, 2 is `lt2`, 3 is `dk2`.
|
|
8
8
|
*
|
|
9
9
|
* Verified against Excel Desktop rather than inferred, because the two orders differ only in the
|
|
10
|
-
* first four entries and reading either one into the other silently inverts text against
|
|
11
|
-
*
|
|
10
|
+
* first four entries and reading either one into the other silently inverts text against
|
|
11
|
+
* background. See `docs/knowledge/specs/theme-color-index-order.md` and the recorded observation in
|
|
12
12
|
* `test/corpus/fixtures/excel-oracle/theme-color-index-order.json`. The stylesheet's own default font
|
|
13
13
|
* is the everyday witness: it carries `<color theme="1"/>` and renders black, which is `dk1`.
|
|
14
14
|
*/
|
|
15
15
|
export declare const THEME_COLOR_SLOTS: readonly ['lt1', 'dk1', 'lt2', 'dk2', 'accent1', 'accent2', 'accent3', 'accent4', 'accent5', 'accent6', 'hlink', 'folHlink'];
|
|
16
16
|
/** One slot of a theme's colour scheme. */
|
|
17
17
|
export type ThemeColorSlot = (typeof THEME_COLOR_SLOTS)[number];
|
|
18
|
+
/** Narrow a raw `<a:clrScheme>` child name to a known {@link ThemeColorSlot}. */
|
|
19
|
+
export declare function isThemeColorSlot(value: string): value is ThemeColorSlot;
|
|
18
20
|
/**
|
|
19
21
|
* A theme's colour scheme: each slot's colour as a 6-hex `RRGGBB` string. Partial because a foreign
|
|
20
22
|
* theme is free to omit a slot (or express one in a colour model this reader does not decode), and an
|
|
@@ -22,17 +24,7 @@ export type ThemeColorSlot = (typeof THEME_COLOR_SLOTS)[number];
|
|
|
22
24
|
*/
|
|
23
25
|
export type ThemeColorScheme = Readonly<Partial<Record<ThemeColorSlot, string>>>;
|
|
24
26
|
/** The Office default colour scheme, matching the theme part the writer emits for a workbook with none. */
|
|
25
|
-
export declare const DEFAULT_THEME_COLOR_SCHEME:
|
|
26
|
-
/**
|
|
27
|
-
* Extract the colour scheme from a theme part. Returns only the slots the part actually declares in a
|
|
28
|
-
* colour model this reader understands; an unrecognised one is dropped rather than guessed at, so a
|
|
29
|
-
* caller can tell "the theme says nothing here" from "the theme says black".
|
|
30
|
-
*
|
|
31
|
-
* Reads the `<a:clrScheme>` block alone. A theme carries a font scheme and a format scheme too, but
|
|
32
|
-
* neither participates in resolving a colour, and scanning the whole part would let a `<a:srgbClr>`
|
|
33
|
-
* buried in a gradient stop masquerade as a scheme slot.
|
|
34
|
-
*/
|
|
35
|
-
export declare function parseThemeColorScheme(themeXml: string): ThemeColorScheme;
|
|
27
|
+
export declare const DEFAULT_THEME_COLOR_SCHEME: Readonly<Record<ThemeColorSlot, string>>;
|
|
36
28
|
/**
|
|
37
29
|
* The two typefaces a theme nominates: the `major` face headings use and the `minor` face body text
|
|
38
30
|
* uses. A cell's font reaches them by `scheme="major"`/`scheme="minor"` instead of naming a typeface,
|
|
@@ -43,7 +35,7 @@ export interface ThemeFontScheme {
|
|
|
43
35
|
readonly minor?: string | undefined;
|
|
44
36
|
}
|
|
45
37
|
/**
|
|
46
|
-
* The body typeface a workbook falls back to when neither its theme nor its styles part names one
|
|
38
|
+
* The body typeface a workbook falls back to when neither its theme nor its styles part names one:
|
|
47
39
|
* the face the default theme nominates, and so the face every `scheme="minor"` font resolves to.
|
|
48
40
|
* Named rather than inlined because it is also the last resort of the default-font chain
|
|
49
41
|
* ({@link Workbook.defaultFont}), and the two must not drift.
|
|
@@ -51,40 +43,20 @@ export interface ThemeFontScheme {
|
|
|
51
43
|
export declare const OFFICE_BODY_FACE = "Calibri";
|
|
52
44
|
/** The Office default typefaces, matching the theme part the writer emits for a workbook with none. */
|
|
53
45
|
export declare const DEFAULT_THEME_FONTS: ThemeFontScheme;
|
|
54
|
-
/** Extract the major/minor latin typefaces from a theme part's `<a:fontScheme>`. */
|
|
55
|
-
export declare function parseThemeFontScheme(themeXml: string): ThemeFontScheme;
|
|
56
46
|
/** What a caller can author on a workbook's theme: any subset of the colour slots and typefaces. */
|
|
57
47
|
export interface ThemeOverrides {
|
|
58
48
|
readonly colors?: Readonly<Partial<Record<ThemeColorSlot, string>>> | undefined;
|
|
59
49
|
readonly fonts?: ThemeFontScheme | undefined;
|
|
60
50
|
}
|
|
61
|
-
/**
|
|
62
|
-
* Apply authored colour/font overrides to a theme part, returning the new part text.
|
|
63
|
-
*
|
|
64
|
-
* Surgical by design: the base part rides through untouched except for the `<a:clrScheme>` and
|
|
65
|
-
* `<a:fontScheme>` blocks, and within those, only what the caller actually named. The format scheme —
|
|
66
|
-
* the gradients, line styles and effect styles that make a theme look like a theme — is left exactly
|
|
67
|
-
* as it was, because nobody hand-authors `fillStyleLst` gradient stops from a spreadsheet API and
|
|
68
|
-
* regenerating it would replace a designer's work with the Office default.
|
|
51
|
+
/** Reduce an authored theme colour to the bare 6-hex RGB DrawingML wants.
|
|
69
52
|
*
|
|
70
|
-
* A
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*/
|
|
75
|
-
export declare function applyThemeOverrides(baseXml: string, overrides: ThemeOverrides): string;
|
|
76
|
-
/**
|
|
77
|
-
* Each colour slot's verbatim inner element from a theme part — `<a:srgbClr val="…"/>` or
|
|
78
|
-
* `<a:sysClr val="…" lastClr="…"/>`. The value-level counterpart is {@link parseThemeColorScheme};
|
|
79
|
-
* this keeps the *encoding* so an untouched slot can be re-emitted exactly as the source wrote it.
|
|
80
|
-
*/
|
|
81
|
-
export declare function parseThemeColorElements(themeXml: string): Readonly<Partial<Record<ThemeColorSlot, string>>>;
|
|
82
|
-
/**
|
|
83
|
-
* The theme part a workbook with no theme of its own ships — the standard Office theme.
|
|
53
|
+
* A theme colour is a bare 6-hex RGB: DrawingML has no alpha channel on `<a:srgbClr val>`. The two
|
|
54
|
+
* conveniences the rest of the library accepts (a leading '#', an 8-hex ARGB) are accepted and
|
|
55
|
+
* reduced here; anything else is a caller's bug and is refused rather than written as corrupt XML,
|
|
56
|
+
* which Excel does not report; it renders the slot as flat black.
|
|
84
57
|
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
* built from scratch rather than read from a file.
|
|
58
|
+
* @throws {SyntaxError} if the value is not a recognisable RGB or ARGB hex string. Native rather
|
|
59
|
+
* than the library's own `AuthoringError`: a string that does not parse is what `SyntaxError` is
|
|
60
|
+
* for, and it is the same kind of failure a malformed comment GUID raises.
|
|
89
61
|
*/
|
|
90
|
-
export declare
|
|
62
|
+
export declare function normalizeThemeColor(value: string): string;
|
package/dist/core/theme.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { AuthoringError } from '../errors.js';
|
|
2
1
|
export const THEME_COLOR_SLOTS = [
|
|
3
2
|
'lt1',
|
|
4
3
|
'dk1',
|
|
@@ -13,6 +12,10 @@ export const THEME_COLOR_SLOTS = [
|
|
|
13
12
|
'hlink',
|
|
14
13
|
'folHlink',
|
|
15
14
|
];
|
|
15
|
+
const THEME_COLOR_SLOT_NAMES = new Set(THEME_COLOR_SLOTS);
|
|
16
|
+
export function isThemeColorSlot(value) {
|
|
17
|
+
return THEME_COLOR_SLOT_NAMES.has(value);
|
|
18
|
+
}
|
|
16
19
|
export const DEFAULT_THEME_COLOR_SCHEME = {
|
|
17
20
|
lt1: 'FFFFFF',
|
|
18
21
|
dk1: '000000',
|
|
@@ -27,161 +30,16 @@ export const DEFAULT_THEME_COLOR_SCHEME = {
|
|
|
27
30
|
hlink: '0563C1',
|
|
28
31
|
folHlink: '954F72',
|
|
29
32
|
};
|
|
30
|
-
const SCHEME_SLOT = new RegExp('<a:(dk1|lt1|dk2|lt2|accent[1-6]|hlink|folHlink)\\b[^>]*>' + '\\s*<a:(srgbClr|sysClr)\\b([^>]*)>', 'g');
|
|
31
|
-
export function parseThemeColorScheme(themeXml) {
|
|
32
|
-
const block = /<a:clrScheme\b[^>]*>([\s\S]*?)<\/a:clrScheme>/.exec(themeXml);
|
|
33
|
-
if (block === null)
|
|
34
|
-
return {};
|
|
35
|
-
const scheme = {};
|
|
36
|
-
for (const match of (block[1] ?? '').matchAll(SCHEME_SLOT)) {
|
|
37
|
-
const slot = match[1];
|
|
38
|
-
const attrs = match[3] ?? '';
|
|
39
|
-
const source = match[2] === 'sysClr' ? /\blastClr="([^"]*)"/ : /\bval="([^"]*)"/;
|
|
40
|
-
const value = source.exec(attrs)?.[1];
|
|
41
|
-
if (value !== undefined && /^[0-9a-fA-F]{6}$/.test(value))
|
|
42
|
-
scheme[slot] = value;
|
|
43
|
-
}
|
|
44
|
-
return scheme;
|
|
45
|
-
}
|
|
46
33
|
export const OFFICE_BODY_FACE = 'Calibri';
|
|
47
34
|
export const DEFAULT_THEME_FONTS = {
|
|
48
35
|
major: 'Calibri Light',
|
|
49
36
|
minor: OFFICE_BODY_FACE,
|
|
50
37
|
};
|
|
51
|
-
export function
|
|
52
|
-
const block = /<a:fontScheme\b[^>]*>([\s\S]*?)<\/a:fontScheme>/.exec(themeXml);
|
|
53
|
-
if (block === null)
|
|
54
|
-
return {};
|
|
55
|
-
const face = (which) => {
|
|
56
|
-
const font = new RegExp(`<a:${which}\\b[^>]*>([\\s\\S]*?)</a:${which}>`).exec(block[1] ?? '');
|
|
57
|
-
return /<a:latin\b[^>]*\btypeface="([^"]*)"/.exec(font?.[1] ?? '')?.[1];
|
|
58
|
-
};
|
|
59
|
-
const scheme = {};
|
|
60
|
-
const major = face('majorFont');
|
|
61
|
-
const minor = face('minorFont');
|
|
62
|
-
if (major !== undefined)
|
|
63
|
-
scheme.major = major;
|
|
64
|
-
if (minor !== undefined)
|
|
65
|
-
scheme.minor = minor;
|
|
66
|
-
return scheme;
|
|
67
|
-
}
|
|
68
|
-
const SCHEME_ELEMENT_ORDER = [
|
|
69
|
-
'dk1',
|
|
70
|
-
'lt1',
|
|
71
|
-
'dk2',
|
|
72
|
-
'lt2',
|
|
73
|
-
'accent1',
|
|
74
|
-
'accent2',
|
|
75
|
-
'accent3',
|
|
76
|
-
'accent4',
|
|
77
|
-
'accent5',
|
|
78
|
-
'accent6',
|
|
79
|
-
'hlink',
|
|
80
|
-
'folHlink',
|
|
81
|
-
];
|
|
82
|
-
export function applyThemeOverrides(baseXml, overrides) {
|
|
83
|
-
let xml = baseXml;
|
|
84
|
-
const colors = overrides.colors ?? {};
|
|
85
|
-
if (Object.keys(colors).length > 0) {
|
|
86
|
-
const sourceElements = parseThemeColorElements(baseXml);
|
|
87
|
-
const body = SCHEME_ELEMENT_ORDER.map((slot) => {
|
|
88
|
-
const authored = colors[slot];
|
|
89
|
-
const inner = authored !== undefined
|
|
90
|
-
? `<a:srgbClr val="${normalizeSchemeValue(authored)}"/>`
|
|
91
|
-
: (sourceElements[slot] ??
|
|
92
|
-
`<a:srgbClr val="${DEFAULT_THEME_COLOR_SCHEME[slot]}"/>`);
|
|
93
|
-
return `<a:${slot}>${inner}</a:${slot}>`;
|
|
94
|
-
}).join('');
|
|
95
|
-
xml = replaceBlockBody(xml, 'clrScheme', body);
|
|
96
|
-
}
|
|
97
|
-
const { major, minor } = overrides.fonts ?? {};
|
|
98
|
-
if (major !== undefined)
|
|
99
|
-
xml = replaceLatinTypeface(xml, 'majorFont', major);
|
|
100
|
-
if (minor !== undefined)
|
|
101
|
-
xml = replaceLatinTypeface(xml, 'minorFont', minor);
|
|
102
|
-
return xml;
|
|
103
|
-
}
|
|
104
|
-
export function parseThemeColorElements(themeXml) {
|
|
105
|
-
const block = /<a:clrScheme\b[^>]*>([\s\S]*?)<\/a:clrScheme>/.exec(themeXml);
|
|
106
|
-
if (block === null)
|
|
107
|
-
return {};
|
|
108
|
-
const elements = {};
|
|
109
|
-
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;
|
|
110
|
-
for (const match of (block[1] ?? '').matchAll(pattern)) {
|
|
111
|
-
const slot = (match[1] ?? match[3]);
|
|
112
|
-
const inner = match[2];
|
|
113
|
-
if (inner !== undefined && inner !== '')
|
|
114
|
-
elements[slot] = inner;
|
|
115
|
-
}
|
|
116
|
-
return elements;
|
|
117
|
-
}
|
|
118
|
-
function normalizeSchemeValue(value) {
|
|
38
|
+
export function normalizeThemeColor(value) {
|
|
119
39
|
const hex = value.startsWith('#') ? value.slice(1) : value;
|
|
120
40
|
const rgb = hex.length === 8 ? hex.slice(2) : hex;
|
|
121
41
|
if (!/^[0-9a-fA-F]{6}$/.test(rgb)) {
|
|
122
|
-
throw new
|
|
42
|
+
throw new SyntaxError(`Invalid theme colour ${JSON.stringify(value)}: expected 6 hexadecimal digits (RRGGBB)`);
|
|
123
43
|
}
|
|
124
44
|
return rgb.toUpperCase();
|
|
125
45
|
}
|
|
126
|
-
function replaceBlockBody(xml, name, body) {
|
|
127
|
-
const pattern = new RegExp(`(<a:${name}\\b[^>]*>)[\\s\\S]*?(</a:${name}>)`);
|
|
128
|
-
return xml.replace(pattern, (_all, open, close) => `${open}${body}${close}`);
|
|
129
|
-
}
|
|
130
|
-
function replaceLatinTypeface(xml, which, typeface) {
|
|
131
|
-
const pattern = new RegExp(`(<a:${which}\\b[^>]*>[\\s\\S]*?<a:latin\\b)[^>]*(/>)`);
|
|
132
|
-
return xml.replace(pattern, (_all, open, close) => `${open} typeface="${escapeXmlAttr(typeface)}"${close}`);
|
|
133
|
-
}
|
|
134
|
-
function escapeXmlAttr(value) {
|
|
135
|
-
return value
|
|
136
|
-
.replace(/&/g, '&')
|
|
137
|
-
.replace(/</g, '<')
|
|
138
|
-
.replace(/>/g, '>')
|
|
139
|
-
.replace(/"/g, '"');
|
|
140
|
-
}
|
|
141
|
-
export const DEFAULT_THEME_XML = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n' +
|
|
142
|
-
'<a:theme xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" name="Office Theme">' +
|
|
143
|
-
'<a:themeElements>' +
|
|
144
|
-
'<a:clrScheme name="Office">' +
|
|
145
|
-
'<a:dk1><a:sysClr val="windowText" lastClr="000000"/></a:dk1>' +
|
|
146
|
-
'<a:lt1><a:sysClr val="window" lastClr="FFFFFF"/></a:lt1>' +
|
|
147
|
-
'<a:dk2><a:srgbClr val="44546A"/></a:dk2>' +
|
|
148
|
-
'<a:lt2><a:srgbClr val="E7E6E6"/></a:lt2>' +
|
|
149
|
-
'<a:accent1><a:srgbClr val="4472C4"/></a:accent1>' +
|
|
150
|
-
'<a:accent2><a:srgbClr val="ED7D31"/></a:accent2>' +
|
|
151
|
-
'<a:accent3><a:srgbClr val="A5A5A5"/></a:accent3>' +
|
|
152
|
-
'<a:accent4><a:srgbClr val="FFC000"/></a:accent4>' +
|
|
153
|
-
'<a:accent5><a:srgbClr val="5B9BD5"/></a:accent5>' +
|
|
154
|
-
'<a:accent6><a:srgbClr val="70AD47"/></a:accent6>' +
|
|
155
|
-
'<a:hlink><a:srgbClr val="0563C1"/></a:hlink>' +
|
|
156
|
-
'<a:folHlink><a:srgbClr val="954F72"/></a:folHlink>' +
|
|
157
|
-
'</a:clrScheme>' +
|
|
158
|
-
'<a:fontScheme name="Office">' +
|
|
159
|
-
'<a:majorFont><a:latin typeface="Calibri Light" panose="020F0302020204030204"/><a:ea typeface=""/><a:cs typeface=""/></a:majorFont>' +
|
|
160
|
-
'<a:minorFont><a:latin typeface="Calibri" panose="020F0502020204030204"/><a:ea typeface=""/><a:cs typeface=""/></a:minorFont>' +
|
|
161
|
-
'</a:fontScheme>' +
|
|
162
|
-
'<a:fmtScheme name="Office">' +
|
|
163
|
-
'<a:fillStyleLst>' +
|
|
164
|
-
'<a:solidFill><a:schemeClr val="phClr"/></a:solidFill>' +
|
|
165
|
-
'<a:gradFill rotWithShape="1"><a:gsLst><a:gs pos="0"><a:schemeClr val="phClr"><a:lumMod val="110000"/><a:satMod val="105000"/><a:tint val="67000"/></a:schemeClr></a:gs><a:gs pos="50000"><a:schemeClr val="phClr"><a:lumMod val="105000"/><a:satMod val="103000"/><a:tint val="73000"/></a:schemeClr></a:gs><a:gs pos="100000"><a:schemeClr val="phClr"><a:lumMod val="105000"/><a:satMod val="109000"/><a:tint val="81000"/></a:schemeClr></a:gs></a:gsLst><a:lin ang="5400000" scaled="0"/></a:gradFill>' +
|
|
166
|
-
'<a:gradFill rotWithShape="1"><a:gsLst><a:gs pos="0"><a:schemeClr val="phClr"><a:satMod val="103000"/><a:lumMod val="102000"/><a:tint val="94000"/></a:schemeClr></a:gs><a:gs pos="50000"><a:schemeClr val="phClr"><a:satMod val="110000"/><a:lumMod val="100000"/><a:shade val="100000"/></a:schemeClr></a:gs><a:gs pos="100000"><a:schemeClr val="phClr"><a:lumMod val="99000"/><a:satMod val="120000"/><a:shade val="78000"/></a:schemeClr></a:gs></a:gsLst><a:lin ang="5400000" scaled="0"/></a:gradFill>' +
|
|
167
|
-
'</a:fillStyleLst>' +
|
|
168
|
-
'<a:lnStyleLst>' +
|
|
169
|
-
'<a:ln w="6350" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:prstDash val="solid"/><a:miter lim="800000"/></a:ln>' +
|
|
170
|
-
'<a:ln w="12700" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:prstDash val="solid"/><a:miter lim="800000"/></a:ln>' +
|
|
171
|
-
'<a:ln w="19050" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:prstDash val="solid"/><a:miter lim="800000"/></a:ln>' +
|
|
172
|
-
'</a:lnStyleLst>' +
|
|
173
|
-
'<a:effectStyleLst>' +
|
|
174
|
-
'<a:effectStyle><a:effectLst/></a:effectStyle>' +
|
|
175
|
-
'<a:effectStyle><a:effectLst/></a:effectStyle>' +
|
|
176
|
-
'<a:effectStyle><a:effectLst><a:outerShdw blurRad="57150" dist="19050" dir="5400000" rotWithShape="0"><a:srgbClr val="000000"><a:alpha val="63000"/></a:srgbClr></a:outerShdw></a:effectLst></a:effectStyle>' +
|
|
177
|
-
'</a:effectStyleLst>' +
|
|
178
|
-
'<a:bgFillStyleLst>' +
|
|
179
|
-
'<a:solidFill><a:schemeClr val="phClr"/></a:solidFill>' +
|
|
180
|
-
'<a:solidFill><a:schemeClr val="phClr"><a:tint val="95000"/><a:satMod val="170000"/></a:schemeClr></a:solidFill>' +
|
|
181
|
-
'<a:gradFill rotWithShape="1"><a:gsLst><a:gs pos="0"><a:schemeClr val="phClr"><a:tint val="93000"/><a:satMod val="150000"/><a:shade val="98000"/><a:lumMod val="102000"/></a:schemeClr></a:gs><a:gs pos="50000"><a:schemeClr val="phClr"><a:tint val="98000"/><a:satMod val="130000"/><a:shade val="90000"/><a:lumMod val="103000"/></a:schemeClr></a:gs><a:gs pos="100000"><a:schemeClr val="phClr"><a:shade val="63000"/><a:satMod val="120000"/></a:schemeClr></a:gs></a:gsLst><a:lin ang="5400000" scaled="0"/></a:gradFill>' +
|
|
182
|
-
'</a:bgFillStyleLst>' +
|
|
183
|
-
'</a:fmtScheme>' +
|
|
184
|
-
'</a:themeElements>' +
|
|
185
|
-
'<a:objectDefaults/>' +
|
|
186
|
-
'<a:extraClrSchemeLst/>' +
|
|
187
|
-
'</a:theme>';
|
package/dist/core/value.d.ts
CHANGED
|
@@ -38,7 +38,7 @@ export interface HyperlinkValue {
|
|
|
38
38
|
* cell; `range` records how far Excel highlights the clickable area so it survives a round-trip. */
|
|
39
39
|
readonly range?: string;
|
|
40
40
|
}
|
|
41
|
-
/** The cached result a formula carries
|
|
41
|
+
/** The cached result a formula carries: any scalar, a date, or an error. */
|
|
42
42
|
export type FormulaResult = number | string | boolean | Date | ErrorValue;
|
|
43
43
|
/** A cell whose value is computed by its own formula. */
|
|
44
44
|
export interface FormulaValue {
|
|
@@ -46,7 +46,7 @@ export interface FormulaValue {
|
|
|
46
46
|
readonly result?: FormulaResult;
|
|
47
47
|
}
|
|
48
48
|
/**
|
|
49
|
-
* A cell that participates in a shared formula
|
|
49
|
+
* A cell that participates in a shared formula: a clone of a master formula cell filled across a
|
|
50
50
|
* range. `sharedFormula` is the master cell's address (e.g. `'B1'`); the master itself is a plain
|
|
51
51
|
* {@link FormulaValue}. On read, the clone's own formula is the master's translated to the clone's
|
|
52
52
|
* position and `result` is the clone's cached value; on write, the clones of a master collapse into
|
|
@@ -60,7 +60,7 @@ export interface SharedFormulaValue {
|
|
|
60
60
|
readonly result?: FormulaResult;
|
|
61
61
|
}
|
|
62
62
|
/**
|
|
63
|
-
* A cell computed by a What-If-Analysis data table (`<f t="dataTable">`)
|
|
63
|
+
* A cell computed by a What-If-Analysis data table (`<f t="dataTable">`), the OOXML formula kind that
|
|
64
64
|
* fills a range by re-evaluating a model against a grid of substituted input cells. The library does
|
|
65
65
|
* not evaluate it; it preserves the declaration so a read-modify-write cycle re-emits it verbatim
|
|
66
66
|
* rather than silently dropping the data-table kind.
|
|
@@ -88,7 +88,7 @@ export type CellValue = null | number | string | boolean | Date | ErrorValue | F
|
|
|
88
88
|
*/
|
|
89
89
|
export declare function isErrorValue(value: CellValue): value is ErrorValue;
|
|
90
90
|
/**
|
|
91
|
-
* Whether a value is a cell's own formula ({@link FormulaValue})
|
|
91
|
+
* Whether a value is a cell's own formula ({@link FormulaValue}): a master, or a formula
|
|
92
92
|
* belonging to no shared group. A shared-formula clone is **not** one of these; see
|
|
93
93
|
* {@link isSharedFormulaValue}. Both report as `ValueType.Formula`, so a caller that means "any
|
|
94
94
|
* formula-shaped cell" wants {@link detectValueType}, not this.
|
|
@@ -115,7 +115,7 @@ export declare function isHyperlinkValue(value: CellValue): value is HyperlinkVa
|
|
|
115
115
|
*/
|
|
116
116
|
export declare function richTextToPlain(value: RichTextValue): string;
|
|
117
117
|
/**
|
|
118
|
-
* The plain text of any cell value
|
|
118
|
+
* The plain text of any cell value, total over {@link CellValue}, so a caller reading a sheet
|
|
119
119
|
* whose cells it did not write never has to switch on the union itself.
|
|
120
120
|
*
|
|
121
121
|
* This is the *value's* text, not the cell's *display* text: a number renders as JavaScript
|
|
@@ -126,7 +126,7 @@ export declare function richTextToPlain(value: RichTextValue): string;
|
|
|
126
126
|
* - the empty cell (`null`) and an invalid `Date` → `""`, the two ways a cell has no text
|
|
127
127
|
* - a boolean → `"TRUE"` / `"FALSE"`, Excel's own literals rather than JavaScript's
|
|
128
128
|
* - a `Date` → a full ISO-8601 timestamp
|
|
129
|
-
* - an error → its literal, e.g. `"#REF!"
|
|
129
|
+
* - an error → its literal, e.g. `"#REF!"`, the same string the grid shows
|
|
130
130
|
* - rich text → every run concatenated ({@link richTextToPlain})
|
|
131
131
|
* - a hyperlink → its label, never its destination
|
|
132
132
|
* - any of the three formula kinds → the text of the *cached result*, and `""` when the cell
|
|
@@ -146,7 +146,7 @@ export declare function isErrorCode(text: string): text is ErrorCode;
|
|
|
146
146
|
* Normalise a raw assignment into a stored {@link CellValue}. `undefined` becomes the
|
|
147
147
|
* empty cell (`null`); every other kind is validated by {@link detectValueType}. The
|
|
148
148
|
* model never rewrites one value *kind* into another (a numeric-looking string stays a
|
|
149
|
-
* string)
|
|
149
|
+
* string). The single exception is formula text, which is canonicalised to the OOXML
|
|
150
150
|
* stored form (no leading `=`) so round-trips are idempotent regardless of how the
|
|
151
151
|
* caller supplied it.
|
|
152
152
|
*
|
|
@@ -12,11 +12,11 @@ export type WorkbookProtectionCredentialAttr = (typeof WORKBOOK_PROTECTION_CREDE
|
|
|
12
12
|
/**
|
|
13
13
|
* A workbook's structure/window protection. The three lock flags each default to `false` (absent),
|
|
14
14
|
* matching OOXML: an omitted attribute leaves that aspect unlocked. The optional {@link credentials}
|
|
15
|
-
* bag carries the opaque password attributes verbatim
|
|
15
|
+
* bag carries the opaque password attributes verbatim: the library never verifies a password, it
|
|
16
16
|
* only refuses to lose one.
|
|
17
17
|
*/
|
|
18
18
|
export interface WorkbookProtection {
|
|
19
|
-
/** Lock the workbook structure
|
|
19
|
+
/** Lock the workbook structure: no adding, deleting, reordering, or unhiding sheets. */
|
|
20
20
|
readonly lockStructure?: boolean;
|
|
21
21
|
/** Lock the workbook window geometry. */
|
|
22
22
|
readonly lockWindows?: boolean;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { NamedCellStyle, TableStyleTable } from './style.ts';
|
|
2
|
+
import type { TableStyle } from './table-style.ts';
|
|
3
|
+
/**
|
|
4
|
+
* The preserved style tables of a workbook: the `<dxfs>` fragments, the named cell styles, the two
|
|
5
|
+
* colour lists, and the table-style definitions a file declared plus the ones a caller authored.
|
|
6
|
+
*
|
|
7
|
+
* Every restore replaces what it restores rather than merging, because a reader states a table
|
|
8
|
+
* whole: a half-restored `<dxfs>` would leave existing `dxfId` references pointing into a mix of two
|
|
9
|
+
* files.
|
|
10
|
+
*/
|
|
11
|
+
export declare class WorkbookStyleTables {
|
|
12
|
+
#private;
|
|
13
|
+
get differentialStyles(): readonly string[];
|
|
14
|
+
get namedStyles(): readonly NamedCellStyle[];
|
|
15
|
+
get indexedColors(): readonly string[];
|
|
16
|
+
get mruColors(): readonly string[];
|
|
17
|
+
get tableStyles(): TableStyleTable;
|
|
18
|
+
get customTableStyles(): readonly TableStyle[];
|
|
19
|
+
addCustomTableStyle(style: TableStyle): void;
|
|
20
|
+
indexedPalette(): readonly string[];
|
|
21
|
+
restoreDifferentialStyles(fragments: readonly string[]): void;
|
|
22
|
+
restoreNamedStyles(styles: readonly NamedCellStyle[]): void;
|
|
23
|
+
restoreIndexedColors(fragments: readonly string[]): void;
|
|
24
|
+
restoreMruColors(fragments: readonly string[]): void;
|
|
25
|
+
restoreTableStyles(table: TableStyleTable): void;
|
|
26
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { replaceContents } from './containers.js';
|
|
2
|
+
export class WorkbookStyleTables {
|
|
3
|
+
#differentialStyles = [];
|
|
4
|
+
#namedStyles = [];
|
|
5
|
+
#indexedColors = [];
|
|
6
|
+
#mruColors = [];
|
|
7
|
+
#tableStyles = { styles: [] };
|
|
8
|
+
#customTableStyles = new Map();
|
|
9
|
+
get differentialStyles() {
|
|
10
|
+
return this.#differentialStyles;
|
|
11
|
+
}
|
|
12
|
+
get namedStyles() {
|
|
13
|
+
return this.#namedStyles;
|
|
14
|
+
}
|
|
15
|
+
get indexedColors() {
|
|
16
|
+
return this.#indexedColors;
|
|
17
|
+
}
|
|
18
|
+
get mruColors() {
|
|
19
|
+
return this.#mruColors;
|
|
20
|
+
}
|
|
21
|
+
get tableStyles() {
|
|
22
|
+
return this.#tableStyles;
|
|
23
|
+
}
|
|
24
|
+
get customTableStyles() {
|
|
25
|
+
return [...this.#customTableStyles.values()];
|
|
26
|
+
}
|
|
27
|
+
addCustomTableStyle(style) {
|
|
28
|
+
this.#customTableStyles.set(style.name, style);
|
|
29
|
+
}
|
|
30
|
+
indexedPalette() {
|
|
31
|
+
return this.#indexedColors.map((fragment) => /\brgb="([^"]*)"/.exec(fragment)?.[1] ?? '');
|
|
32
|
+
}
|
|
33
|
+
restoreDifferentialStyles(fragments) {
|
|
34
|
+
replaceContents(this.#differentialStyles, fragments);
|
|
35
|
+
}
|
|
36
|
+
restoreNamedStyles(styles) {
|
|
37
|
+
replaceContents(this.#namedStyles, styles);
|
|
38
|
+
}
|
|
39
|
+
restoreIndexedColors(fragments) {
|
|
40
|
+
replaceContents(this.#indexedColors, fragments);
|
|
41
|
+
}
|
|
42
|
+
restoreMruColors(fragments) {
|
|
43
|
+
replaceContents(this.#mruColors, fragments);
|
|
44
|
+
}
|
|
45
|
+
restoreTableStyles(table) {
|
|
46
|
+
this.#tableStyles = table;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Color } from './style.ts';
|
|
2
|
+
import { type ThemeColorScheme, type ThemeFontScheme, type ThemeOverrides } from './theme.ts';
|
|
3
|
+
import type { PreservedTheme } from './workbook.ts';
|
|
4
|
+
/** The schemes a theme part declares, as the codec that read it decoded them. */
|
|
5
|
+
export interface DeclaredThemeSchemes {
|
|
6
|
+
readonly colors: ThemeColorScheme;
|
|
7
|
+
readonly fonts: ThemeFontScheme;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* The theme slice of a workbook: the preserved part, the colour scheme and typefaces every
|
|
11
|
+
* `theme="n"` reference resolves against, and the overrides {@link Workbook.setTheme} authors over
|
|
12
|
+
* them.
|
|
13
|
+
*/
|
|
14
|
+
export declare class WorkbookTheme {
|
|
15
|
+
#private;
|
|
16
|
+
constructor(indexedPalette: () => readonly string[]);
|
|
17
|
+
get part(): PreservedTheme | undefined;
|
|
18
|
+
restorePart(theme: PreservedTheme | undefined, declared: DeclaredThemeSchemes): void;
|
|
19
|
+
author(overrides: ThemeOverrides): void;
|
|
20
|
+
get colors(): ThemeColorScheme;
|
|
21
|
+
get fonts(): ThemeFontScheme;
|
|
22
|
+
get authoredFonts(): ThemeFontScheme;
|
|
23
|
+
get overrides(): ThemeOverrides | undefined;
|
|
24
|
+
resolveColor(color: Color): string | undefined;
|
|
25
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { resolveColor } from './color-resolution.js';
|
|
2
|
+
import { DEFAULT_THEME_COLOR_SCHEME, DEFAULT_THEME_FONTS, normalizeThemeColor, } from './theme.js';
|
|
3
|
+
export class WorkbookTheme {
|
|
4
|
+
#indexedPalette;
|
|
5
|
+
#part;
|
|
6
|
+
#declared = { colors: {}, fonts: {} };
|
|
7
|
+
#authored = {
|
|
8
|
+
colors: {},
|
|
9
|
+
fonts: {},
|
|
10
|
+
};
|
|
11
|
+
constructor(indexedPalette) {
|
|
12
|
+
this.#indexedPalette = indexedPalette;
|
|
13
|
+
}
|
|
14
|
+
get part() {
|
|
15
|
+
return this.#part;
|
|
16
|
+
}
|
|
17
|
+
restorePart(theme, declared) {
|
|
18
|
+
this.#part = theme;
|
|
19
|
+
this.#declared = declared;
|
|
20
|
+
}
|
|
21
|
+
author(overrides) {
|
|
22
|
+
for (const value of Object.values(overrides.colors ?? {}))
|
|
23
|
+
normalizeThemeColor(value);
|
|
24
|
+
Object.assign(this.#authored.colors, overrides.colors ?? {});
|
|
25
|
+
this.#authored.fonts = { ...this.#authored.fonts, ...overrides.fonts };
|
|
26
|
+
}
|
|
27
|
+
get colors() {
|
|
28
|
+
const { colors } = this.#declared;
|
|
29
|
+
const base = Object.keys(colors).length === 0 ? DEFAULT_THEME_COLOR_SCHEME : colors;
|
|
30
|
+
return { ...base, ...this.#authored.colors };
|
|
31
|
+
}
|
|
32
|
+
get fonts() {
|
|
33
|
+
const { fonts } = this.#declared;
|
|
34
|
+
const base = Object.keys(fonts).length === 0 ? DEFAULT_THEME_FONTS : fonts;
|
|
35
|
+
return { ...base, ...this.#authored.fonts };
|
|
36
|
+
}
|
|
37
|
+
get authoredFonts() {
|
|
38
|
+
return this.#authored.fonts;
|
|
39
|
+
}
|
|
40
|
+
get overrides() {
|
|
41
|
+
const { colors, fonts } = this.#authored;
|
|
42
|
+
if (Object.keys(colors).length === 0 && Object.keys(fonts).length === 0)
|
|
43
|
+
return undefined;
|
|
44
|
+
return { colors, fonts };
|
|
45
|
+
}
|
|
46
|
+
resolveColor(color) {
|
|
47
|
+
return resolveColor(color, { theme: this.colors, indexed: this.#indexedPalette() });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type VbaLibraryReference, type VbaProject, type VbaProjectSignature } from '../vba/index.ts';
|
|
2
|
+
import type { PreservedWorkbookReference } from './workbook.ts';
|
|
3
|
+
/**
|
|
4
|
+
* The macro-project slice of a workbook: the lazily-decoded project, the raw blob the writer
|
|
5
|
+
* re-embeds, the signatures wired off it, and the two structural edits that can be made to a project
|
|
6
|
+
* without recompiling p-code.
|
|
7
|
+
*/
|
|
8
|
+
export declare class WorkbookVbaProject {
|
|
9
|
+
#private;
|
|
10
|
+
constructor(references: PreservedWorkbookReference[]);
|
|
11
|
+
get project(): VbaProject | undefined;
|
|
12
|
+
get bytes(): Uint8Array | undefined;
|
|
13
|
+
set bytes(bytes: Uint8Array | undefined);
|
|
14
|
+
get signatures(): readonly VbaProjectSignature[];
|
|
15
|
+
removeModule(name: string): void;
|
|
16
|
+
addReference(ref: VbaLibraryReference): void;
|
|
17
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { addVbaReference, parseVbaProject, removeVbaModule, VBA_PROJECT_CONTENT_TYPE, VBA_PROJECT_PART_PATH, VBA_PROJECT_REL_TYPE, VbaAuthorError, vbaProjectSignatureKind, } from '../vba/index.js';
|
|
2
|
+
import { replaceContents } from './containers.js';
|
|
3
|
+
export class WorkbookVbaProject {
|
|
4
|
+
#references;
|
|
5
|
+
#parsed = false;
|
|
6
|
+
#project = undefined;
|
|
7
|
+
constructor(references) {
|
|
8
|
+
this.#references = references;
|
|
9
|
+
}
|
|
10
|
+
get project() {
|
|
11
|
+
if (!this.#parsed) {
|
|
12
|
+
const bytes = this.#entry()?.bytes;
|
|
13
|
+
this.#project = bytes ? parseVbaProject(bytes) : undefined;
|
|
14
|
+
this.#parsed = true;
|
|
15
|
+
}
|
|
16
|
+
return this.#project;
|
|
17
|
+
}
|
|
18
|
+
get bytes() {
|
|
19
|
+
return this.#entry()?.bytes.slice();
|
|
20
|
+
}
|
|
21
|
+
set bytes(bytes) {
|
|
22
|
+
if (bytes !== undefined)
|
|
23
|
+
parseVbaProject(bytes);
|
|
24
|
+
replaceContents(this.#references, this.#references.filter((r) => !r.relType.endsWith('/vbaProject')));
|
|
25
|
+
if (bytes !== undefined) {
|
|
26
|
+
this.#references.push({
|
|
27
|
+
relType: VBA_PROJECT_REL_TYPE,
|
|
28
|
+
entryPath: VBA_PROJECT_PART_PATH,
|
|
29
|
+
parts: [
|
|
30
|
+
{
|
|
31
|
+
path: VBA_PROJECT_PART_PATH,
|
|
32
|
+
contentType: VBA_PROJECT_CONTENT_TYPE,
|
|
33
|
+
bytes: bytes.slice(),
|
|
34
|
+
rels: [],
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
this.#parsed = false;
|
|
40
|
+
this.#project = undefined;
|
|
41
|
+
}
|
|
42
|
+
get signatures() {
|
|
43
|
+
const ref = this.#ref();
|
|
44
|
+
const entry = ref?.parts.find((p) => p.path === ref.entryPath);
|
|
45
|
+
if (ref === undefined || entry === undefined)
|
|
46
|
+
return [];
|
|
47
|
+
const partByPath = new Map(ref.parts.map((p) => [p.path, p]));
|
|
48
|
+
const signatures = [];
|
|
49
|
+
for (const rel of entry.rels) {
|
|
50
|
+
const kind = vbaProjectSignatureKind(rel.type);
|
|
51
|
+
const part = kind === undefined ? undefined : partByPath.get(rel.targetPath);
|
|
52
|
+
if (kind !== undefined && part !== undefined) {
|
|
53
|
+
signatures.push({ kind, bytes: part.bytes.slice() });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return signatures;
|
|
57
|
+
}
|
|
58
|
+
removeModule(name) {
|
|
59
|
+
const bytes = this.bytes;
|
|
60
|
+
if (bytes === undefined) {
|
|
61
|
+
throw new VbaAuthorError('workbook has no VBA project to remove a module from');
|
|
62
|
+
}
|
|
63
|
+
this.bytes = removeVbaModule(bytes, name);
|
|
64
|
+
}
|
|
65
|
+
addReference(ref) {
|
|
66
|
+
const bytes = this.bytes;
|
|
67
|
+
if (bytes === undefined) {
|
|
68
|
+
throw new VbaAuthorError('workbook has no VBA project to add a reference to');
|
|
69
|
+
}
|
|
70
|
+
this.bytes = addVbaReference(bytes, ref);
|
|
71
|
+
}
|
|
72
|
+
#ref() {
|
|
73
|
+
return this.#references.find((r) => r.relType.endsWith('/vbaProject'));
|
|
74
|
+
}
|
|
75
|
+
#entry() {
|
|
76
|
+
const ref = this.#ref();
|
|
77
|
+
return ref?.parts.find((p) => p.path === ref.entryPath);
|
|
78
|
+
}
|
|
79
|
+
}
|