@shbernal/ts-xlsx 1.2.0 → 2.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 +5 -0
- package/dist/core/address.d.ts +49 -2
- package/dist/core/address.js +31 -3
- package/dist/core/autofilter.d.ts +2 -2
- package/dist/core/cell.d.ts +17 -16
- package/dist/core/cell.js +3 -7
- package/dist/core/color-resolution.d.ts +7 -7
- 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 +1 -1
- package/dist/core/conditional-formatting.d.ts +3 -3
- package/dist/core/data-validation-overlay.d.ts +1 -1
- package/dist/core/data-validation.d.ts +3 -3
- package/dist/core/date.d.ts +3 -3
- package/dist/core/formula.d.ts +6 -6
- package/dist/core/grid-edits.js +3 -3
- package/dist/core/image.d.ts +37 -2
- package/dist/core/image.js +6 -0
- package/dist/core/internal.d.ts +9 -1
- package/dist/core/limits.d.ts +4 -4
- package/dist/core/merge.d.ts +6 -12
- package/dist/core/merge.js +0 -3
- package/dist/core/page-setup.d.ts +4 -4
- package/dist/core/pivot-table.d.ts +2 -4
- package/dist/core/pivot-table.js +5 -5
- package/dist/core/preserved.d.ts +3 -3
- package/dist/core/protection.d.ts +4 -4
- package/dist/core/range.d.ts +16 -14
- package/dist/core/range.js +5 -5
- package/dist/core/row-input.d.ts +2 -8
- package/dist/core/row-input.js +2 -2
- package/dist/core/row.d.ts +7 -8
- package/dist/core/row.js +2 -4
- package/dist/core/style.d.ts +14 -14
- package/dist/core/table-style.d.ts +9 -9
- package/dist/core/table-style.js +1 -1
- package/dist/core/table.d.ts +30 -42
- package/dist/core/table.js +16 -10
- package/dist/core/theme.d.ts +13 -43
- package/dist/core/theme.js +2 -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 +141 -83
- package/dist/core/workbook.js +74 -133
- package/dist/core/worksheet-comments.d.ts +9 -0
- package/dist/core/worksheet-comments.js +52 -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 +94 -77
- package/dist/core/worksheet.js +36 -95
- package/dist/customui/errors.d.ts +1 -1
- package/dist/customui/ribbon.d.ts +3 -3
- package/dist/entries/core.d.ts +4 -4
- package/dist/entries/core.js +1 -1
- package/dist/entries/vba.d.ts +1 -1
- package/dist/entries/vba.js +1 -1
- package/dist/entries/xlsx.d.ts +1 -0
- package/dist/entries/xlsx.js +1 -0
- package/dist/errors.d.ts +12 -12
- package/dist/io/csv/read.js +1 -2
- package/dist/io/csv/write.d.ts +8 -3
- package/dist/io/csv/write.js +17 -2
- package/dist/io/opc/errors.d.ts +6 -6
- package/dist/io/opc/inflate.d.ts +1 -1
- 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/sniff-format.d.ts +2 -2
- package/dist/io/opc/zip-mtime.d.ts +5 -0
- package/dist/io/opc/zip-mtime.js +1 -0
- package/dist/io/style/xf-style.d.ts +38 -6
- package/dist/io/style/xf-style.js +22 -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 +25 -6
- package/dist/io/xlsx/cell-accumulator.js +111 -30
- package/dist/io/xlsx/cell-value.d.ts +2 -2
- package/dist/io/xlsx/cell-value.js +3 -3
- package/dist/io/xlsx/color-xml.js +11 -17
- package/dist/io/xlsx/comments.d.ts +5 -5
- package/dist/io/xlsx/comments.js +13 -6
- package/dist/io/xlsx/conditional-formatting.d.ts +3 -3
- package/dist/io/xlsx/conditional-formatting.js +7 -16
- package/dist/io/xlsx/data-validation.d.ts +2 -2
- package/dist/io/xlsx/data-validation.js +1 -1
- package/dist/io/xlsx/edit-vba.js +2 -1
- package/dist/io/xlsx/errors.d.ts +3 -3
- package/dist/io/xlsx/hyperlinks.d.ts +4 -2
- package/dist/io/xlsx/hyperlinks.js +5 -5
- package/dist/io/xlsx/images.d.ts +1 -1
- package/dist/io/xlsx/images.js +8 -8
- 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 +2 -5
- 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 -5
- package/dist/io/xlsx/read-styles.d.ts +3 -3
- package/dist/io/xlsx/read-styles.js +40 -65
- package/dist/io/xlsx/read-worksheet.js +67 -115
- package/dist/io/xlsx/read.d.ts +3 -5
- package/dist/io/xlsx/read.js +94 -71
- package/dist/io/xlsx/rich-runs.d.ts +1 -1
- package/dist/io/xlsx/rich-runs.js +1 -1
- 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.js +5 -4
- package/dist/io/xlsx/styles.d.ts +8 -9
- package/dist/io/xlsx/styles.js +4 -3
- package/dist/io/xlsx/tables.d.ts +2 -2
- package/dist/io/xlsx/tables.js +8 -13
- package/dist/io/xlsx/theme-xml.d.ts +37 -0
- package/dist/io/xlsx/theme-xml.js +142 -0
- package/dist/io/xlsx/threaded-comments.d.ts +18 -27
- package/dist/io/xlsx/threaded-comments.js +9 -21
- package/dist/io/xlsx/workbook-xml.d.ts +1 -1
- package/dist/io/xlsx/workbook-xml.js +6 -2
- package/dist/io/xlsx/worksheet-xml.d.ts +2 -2
- package/dist/io/xlsx/worksheet-xml.js +7 -12
- package/dist/io/xlsx/write-stream.d.ts +15 -15
- package/dist/io/xlsx/write-stream.js +5 -3
- package/dist/io/xlsx/write.d.ts +12 -7
- package/dist/io/xlsx/write.js +10 -6
- package/dist/io/xlsx/x14-ext.d.ts +1 -1
- package/dist/vba/bytes.d.ts +12 -0
- package/dist/vba/bytes.js +40 -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/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 +2 -11
- package/dist/vba/project.d.ts +5 -5
- package/dist/vba/project.js +1 -10
- package/dist/vba/vba-encoding.d.ts +1 -1
- package/dist/xml/errors.d.ts +1 -1
- package/dist/xml/xml-read.d.ts +38 -9
- package/dist/xml/xml-read.js +23 -0
- package/dist/xml/xml.d.ts +31 -9
- package/dist/xml/xml.js +23 -3
- package/package.json +41 -35
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const FIXED_ENTRY_MTIME = new Date(2001, 0, 1, 12, 0, 0, 0).getTime();
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { type Cell } from '../../core/cell.ts';
|
|
2
|
-
import type
|
|
2
|
+
import { type Border, type CellStyle, type Fill, type Font, type NamedCellStyle } from '../../core/style.ts';
|
|
3
3
|
/**
|
|
4
4
|
* What an xf resolves to: the {@link CellStyle} facet tuple, plus the two flags an xf carries that
|
|
5
5
|
* are not facets. Absent facets stay undefined, matching the contract that an unset facet is simply
|
|
6
6
|
* not present on the reconstructed cell.
|
|
7
7
|
*
|
|
8
8
|
* It *derives* the facets rather than listing them, so a seventh facet added to `CellStyle` reaches
|
|
9
|
-
* both readers the moment it joins. Re-declaring them here
|
|
9
|
+
* both readers the moment it joins. Re-declaring them here, the shape this replaced, meant a new
|
|
10
10
|
* facet silently stopped at the model and never appeared in a file we read back.
|
|
11
11
|
*/
|
|
12
12
|
export interface XfStyle extends CellStyle {
|
|
@@ -21,7 +21,7 @@ export interface StyleTable {
|
|
|
21
21
|
readonly cellXfs: ReadonlyArray<XfStyle>;
|
|
22
22
|
readonly namedStyles: ReadonlyArray<NamedCellStyle>;
|
|
23
23
|
/**
|
|
24
|
-
* Font id 0
|
|
24
|
+
* Font id 0: the workbook's declared default font, the face every cell naming no font renders in.
|
|
25
25
|
* Surfaced separately from the fonts it was flattened onto because it is workbook-level state, not a
|
|
26
26
|
* cell format: a re-write must emit *this* face as font 0 rather than an assumed Calibri, or every
|
|
27
27
|
* empty cell changes face and every character-unit column width changes meaning. Absent when the
|
|
@@ -31,17 +31,49 @@ export interface StyleTable {
|
|
|
31
31
|
}
|
|
32
32
|
/**
|
|
33
33
|
* The format code a number-format id denotes: the file's own `<numFmt>`/`BrtFmt` declaration if it
|
|
34
|
-
* has one, else the built-in Excel defines for that id. Id 0 is General
|
|
34
|
+
* has one, else the built-in Excel defines for that id. Id 0 is General, the absence of a format,
|
|
35
35
|
* and resolves to nothing so an ordinary cell carries no `numFmt`.
|
|
36
36
|
*/
|
|
37
37
|
export declare function numFmtCodeFor(id: number, custom: ReadonlyMap<number, string>): string | undefined;
|
|
38
38
|
/**
|
|
39
|
-
* Apply a resolved xf's non-value facets to a cell
|
|
39
|
+
* Apply a resolved xf's non-value facets to a cell: the six {@link CellStyle} facets through the
|
|
40
40
|
* shared {@link applyCellStyle}, plus the two links that live on the xf itself rather than in the
|
|
41
41
|
* facet tuple (`quotePrefix`, and the `xfId` pointer into the named-style layer).
|
|
42
42
|
*
|
|
43
43
|
* Shared by every path that commits a cell: the XML reader's ordinary and shared-formula-clone paths,
|
|
44
|
-
* and the BIFF12 reader
|
|
44
|
+
* and the BIFF12 reader, so a styled cell keeps its look regardless of which serialisation it came
|
|
45
45
|
* from, and the two cannot drift on what "applying a style" means.
|
|
46
46
|
*/
|
|
47
47
|
export declare function applyXfToCell(cell: Cell, style: XfStyle | undefined): void;
|
|
48
|
+
/**
|
|
49
|
+
* A `<cellStyle>` / `BrtStyle` label: the name and builtinId that title one `cellStyleXfs` entry,
|
|
50
|
+
* keyed to that entry's index.
|
|
51
|
+
*/
|
|
52
|
+
export interface StyleLabel {
|
|
53
|
+
readonly xfId: number;
|
|
54
|
+
readonly name?: string;
|
|
55
|
+
readonly builtinId?: number;
|
|
56
|
+
}
|
|
57
|
+
/** The sub-tables an xf resolves its facet ids against, in whichever spelling the codec parsed them. */
|
|
58
|
+
export interface XfDeps {
|
|
59
|
+
readonly fonts: ReadonlyArray<Font | undefined>;
|
|
60
|
+
readonly fills: ReadonlyArray<Fill | undefined>;
|
|
61
|
+
readonly borders: ReadonlyArray<Border | undefined>;
|
|
62
|
+
readonly numFmtCodes: ReadonlyMap<number, string>;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Turn the four tables a style-sheet parse yields into the {@link StyleTable} both codecs hand back:
|
|
66
|
+
* layer each direct xf over the named style it links to, title the named layer with its labels, and
|
|
67
|
+
* carry font 0 out as the workbook default.
|
|
68
|
+
*
|
|
69
|
+
* A facet the direct xf sets wins; one it leaves unset falls through to the named base; and the
|
|
70
|
+
* `xfId` link is carried through so a re-write keeps it. None of that depends on whether the tables
|
|
71
|
+
* were parsed out of `xl/styles.xml` or `xl/styles.bin`, which is the point of stating it once: the
|
|
72
|
+
* two readers used to hold a copy each, cross-referenced by a comment saying they agreed.
|
|
73
|
+
*/
|
|
74
|
+
export declare function resolveStyleTable(tables: {
|
|
75
|
+
readonly directXfs: ReadonlyArray<XfStyle>;
|
|
76
|
+
readonly namedXfs: ReadonlyArray<XfStyle>;
|
|
77
|
+
readonly labels: ReadonlyArray<StyleLabel>;
|
|
78
|
+
readonly fonts: ReadonlyArray<Font | undefined>;
|
|
79
|
+
}): StyleTable;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { applyCellStyle } from '../../core/cell.js';
|
|
2
2
|
import { NAMED_STYLE_ID } from '../../core/internal.js';
|
|
3
|
+
import { assignStyleFacets, } from '../../core/style.js';
|
|
3
4
|
const BUILTIN_NUMFMTS = new Map([
|
|
4
5
|
[1, '0'],
|
|
5
6
|
[2, '0.00'],
|
|
@@ -62,3 +63,24 @@ export function applyXfToCell(cell, style) {
|
|
|
62
63
|
if (style.xfId !== undefined)
|
|
63
64
|
cell[NAMED_STYLE_ID] = style.xfId;
|
|
64
65
|
}
|
|
66
|
+
export function resolveStyleTable(tables) {
|
|
67
|
+
const { directXfs, namedXfs, labels, fonts } = tables;
|
|
68
|
+
const cellXfs = directXfs.map((xf) => {
|
|
69
|
+
if (xf.xfId === undefined)
|
|
70
|
+
return xf;
|
|
71
|
+
const named = namedXfs[xf.xfId];
|
|
72
|
+
return named === undefined ? xf : { ...named, ...xf };
|
|
73
|
+
});
|
|
74
|
+
const namedStyles = namedXfs.map((xf, index) => {
|
|
75
|
+
const label = labels.find((entry) => entry.xfId === index);
|
|
76
|
+
const style = {};
|
|
77
|
+
assignStyleFacets(style, xf);
|
|
78
|
+
if (label?.name !== undefined)
|
|
79
|
+
style.name = label.name;
|
|
80
|
+
if (label?.builtinId !== undefined)
|
|
81
|
+
style.builtinId = label.builtinId;
|
|
82
|
+
return style;
|
|
83
|
+
});
|
|
84
|
+
const defaultFont = fonts[0];
|
|
85
|
+
return defaultFont === undefined ? { cellXfs, namedStyles } : { cellXfs, namedStyles, defaultFont };
|
|
86
|
+
}
|
package/dist/io/xlsb/errors.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { XlsxError } from '../../errors.ts';
|
|
2
2
|
/**
|
|
3
|
-
* Thrown when an `.xlsb` package's binary BIFF12 content cannot be parsed
|
|
3
|
+
* Thrown when an `.xlsb` package's binary BIFF12 content cannot be parsed: a record whose declared
|
|
4
4
|
* size runs past the end of its part, a truncated record header, or a structure that does not conform
|
|
5
5
|
* to [MS-XLSB].
|
|
6
6
|
*
|
|
@@ -7,16 +7,16 @@ export interface ExternSheetRef {
|
|
|
7
7
|
}
|
|
8
8
|
/** The workbook-level tables a formula's references and names resolve through. */
|
|
9
9
|
export interface FormulaScope {
|
|
10
|
-
/** Sheet names in workbook (tab) order
|
|
10
|
+
/** Sheet names in workbook (tab) order: what an `Xti`'s sheet indices point into. */
|
|
11
11
|
readonly sheetNames: readonly string[];
|
|
12
12
|
/** The `BrtExternSheet` table, indexed by a 3-D token's `ixti`. */
|
|
13
13
|
readonly externSheets: readonly ExternSheetRef[];
|
|
14
14
|
/** The index of the supporting book that is this workbook, or `undefined` when the file declares a
|
|
15
15
|
* supporting book this reader does not recognise. A workbook with no external links declares
|
|
16
|
-
* exactly one
|
|
16
|
+
* exactly one, itself, so this is the ordinary case; anything else leaves the indices untrustworthy
|
|
17
17
|
* and no 3-D reference resolves, which drops those formulas rather than naming the wrong sheet. */
|
|
18
18
|
readonly selfSupBook: number | undefined;
|
|
19
|
-
/** Every `BrtName` in file order, function placeholders included
|
|
19
|
+
/** Every `BrtName` in file order, function placeholders included. A `PtgName` cites one by
|
|
20
20
|
* **1-based** index, so filtering this list would misaddress every name reference. */
|
|
21
21
|
readonly names: readonly string[];
|
|
22
22
|
}
|
|
@@ -28,7 +28,7 @@ export interface FormulaAnchor {
|
|
|
28
28
|
}
|
|
29
29
|
/**
|
|
30
30
|
* Decode a `CellParsedFormula`'s token stream into formula text, in the same on-disk spelling the XML
|
|
31
|
-
* form writes into `<f
|
|
31
|
+
* form writes into `<f>`, `_xlfn.`-prefixed function names included, so the caller applies the same
|
|
32
32
|
* `unmangleFunctions` normalisation to either serialisation.
|
|
33
33
|
*
|
|
34
34
|
* @param rgce the token stream.
|
|
@@ -7,7 +7,7 @@ export interface CellHeader {
|
|
|
7
7
|
/** Zero-based index into the style sheet's cell-XF collection. */
|
|
8
8
|
readonly styleIndex: number;
|
|
9
9
|
}
|
|
10
|
-
/** An `UncheckedRfX` ([MS-XLSB] 2.5.155) cell range
|
|
10
|
+
/** An `UncheckedRfX` ([MS-XLSB] 2.5.155) cell range, all four bounds zero-based and inclusive. */
|
|
11
11
|
export interface RangeBounds {
|
|
12
12
|
readonly rowFirst: number;
|
|
13
13
|
readonly rowLast: number;
|
|
@@ -18,7 +18,7 @@ export interface RangeBounds {
|
|
|
18
18
|
* A bounds-checked cursor over one record's payload.
|
|
19
19
|
*
|
|
20
20
|
* Each accessor advances the cursor by exactly the bytes it consumed, so a record is decoded by
|
|
21
|
-
* naming its fields in order. Reading past the payload throws {@link XlsbParseError}
|
|
21
|
+
* naming its fields in order. Reading past the payload throws {@link XlsbParseError}: a record that
|
|
22
22
|
* is shorter than its own definition is a malformed file, not a case to guess through.
|
|
23
23
|
*/
|
|
24
24
|
export declare class RecordReader {
|
|
@@ -28,10 +28,10 @@ export declare class RecordReader {
|
|
|
28
28
|
get remaining(): number;
|
|
29
29
|
/** Whether the whole payload has been consumed. */
|
|
30
30
|
get done(): boolean;
|
|
31
|
-
/** Advance past `count` bytes without decoding them
|
|
31
|
+
/** Advance past `count` bytes without decoding them: a reserved or unmodelled field. */
|
|
32
32
|
skip(count: number): void;
|
|
33
33
|
/**
|
|
34
|
-
* The next `count` bytes as a **view**, for a field whose own decoding happens elsewhere
|
|
34
|
+
* The next `count` bytes as a **view**, for a field whose own decoding happens elsewhere: a formula
|
|
35
35
|
* token stream, whose meaning depends on workbook tables this record knows nothing about. A view
|
|
36
36
|
* rather than a copy for the same reason a record's payload is one: the declared length comes from
|
|
37
37
|
* the file, so it must bound a read, never an allocation.
|
|
@@ -46,7 +46,7 @@ export declare class RecordReader {
|
|
|
46
46
|
f64(): number;
|
|
47
47
|
/**
|
|
48
48
|
* An `RkNumber` ([MS-XLSB] 2.5.122): a number packed into 32 bits. Two flag bits steal the low end
|
|
49
|
-
* of the word
|
|
49
|
+
* of the word: `fInt` says the remaining 30 bits are a signed integer rather than the *high* 30
|
|
50
50
|
* bits of a double whose low 34 bits are zero, and `fX100` says the result was scaled up by 100 to
|
|
51
51
|
* keep two decimal places in the integer form. It exists because most real spreadsheet numbers are
|
|
52
52
|
* small integers or two-decimal currency, and this stores them in half the bytes of a double.
|
|
@@ -55,7 +55,7 @@ export declare class RecordReader {
|
|
|
55
55
|
/** An `XLWideString` ([MS-XLSB] 2.5.169): a 4-byte character count then that many UTF-16LE units. */
|
|
56
56
|
wideString(): string;
|
|
57
57
|
/**
|
|
58
|
-
* A UTF-16 string whose character count is 16-bit rather than 32-bit
|
|
58
|
+
* A UTF-16 string whose character count is 16-bit rather than 32-bit: the form used *inside* a
|
|
59
59
|
* formula token stream (`PtgStr`, and the string elements of an array constant), where a 4-byte
|
|
60
60
|
* count on every literal would be pure overhead.
|
|
61
61
|
*/
|
|
@@ -64,7 +64,7 @@ export declare class RecordReader {
|
|
|
64
64
|
nullableWideString(): string | undefined;
|
|
65
65
|
/**
|
|
66
66
|
* A `RichStr` ([MS-XLSB] 2.5.124): a string that may carry per-run formatting and phonetic guides.
|
|
67
|
-
* Only the text is returned
|
|
67
|
+
* Only the text is returned; the run and phonetic tails are left unread, which is safe because the
|
|
68
68
|
* record's framing (not this cursor) bounds where the payload ends.
|
|
69
69
|
*/
|
|
70
70
|
richString(): string;
|
|
@@ -77,7 +77,7 @@ export declare class RecordReader {
|
|
|
77
77
|
*
|
|
78
78
|
* The four encodings are mutually exclusive and the type tag picks which of the payload's fields
|
|
79
79
|
* carry meaning; the rest are explicitly undefined. An *automatic* colour (type 0) names nothing at
|
|
80
|
-
* all, and reads back as no colour
|
|
80
|
+
* all, and reads back as no colour: the same absence the XML reader produces for `<color auto="1"/>`,
|
|
81
81
|
* so a cell whose font colour was never set does not gain one on read.
|
|
82
82
|
*/
|
|
83
83
|
color(): Color | undefined;
|
|
@@ -5,7 +5,7 @@ export declare function functionNameFor(index: number): string | undefined;
|
|
|
5
5
|
/**
|
|
6
6
|
* How many arguments a fixed-arity function takes, or `undefined` for one whose argument count varies.
|
|
7
7
|
*
|
|
8
|
-
* A `PtgFunc` token states only *which* function is called, never with how many arguments
|
|
8
|
+
* A `PtgFunc` token states only *which* function is called, never with how many arguments: it is the
|
|
9
9
|
* token Excel emits precisely because the count is implied by the function. So decoding `ROUND(A1,2)`
|
|
10
10
|
* back from its two operands and one call token is impossible without this: the arity is the only
|
|
11
11
|
* thing that says which operands on the stack belong to the call. (`PtgFuncVar` carries its own count
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { numFmtCodeFor } from '../style/xf-style.js';
|
|
1
|
+
import { numFmtCodeFor, resolveStyleTable, } from '../style/xf-style.js';
|
|
3
2
|
import { RecordReader } from './primitives.js';
|
|
4
3
|
import { readRecords } from './record-stream.js';
|
|
5
4
|
import { BRT } from './record-types.js';
|
|
@@ -74,24 +73,7 @@ export function parseStyleTable(part) {
|
|
|
74
73
|
break;
|
|
75
74
|
}
|
|
76
75
|
}
|
|
77
|
-
|
|
78
|
-
if (xf.xfId === undefined)
|
|
79
|
-
return xf;
|
|
80
|
-
const named = namedXfs[xf.xfId];
|
|
81
|
-
return named === undefined ? xf : { ...named, ...xf };
|
|
82
|
-
});
|
|
83
|
-
const namedStyles = namedXfs.map((xf, index) => {
|
|
84
|
-
const label = labels.find((entry) => entry.xfId === index);
|
|
85
|
-
const style = {};
|
|
86
|
-
assignStyleFacets(style, xf);
|
|
87
|
-
if (label?.name !== undefined)
|
|
88
|
-
style.name = label.name;
|
|
89
|
-
if (label?.builtinId !== undefined)
|
|
90
|
-
style.builtinId = label.builtinId;
|
|
91
|
-
return style;
|
|
92
|
-
});
|
|
93
|
-
const defaultFont = fonts[0];
|
|
94
|
-
return defaultFont === undefined ? { cellXfs, namedStyles } : { cellXfs, namedStyles, defaultFont };
|
|
76
|
+
return resolveStyleTable({ directXfs, namedXfs, labels, fonts });
|
|
95
77
|
}
|
|
96
78
|
function readXf(reader, deps, isDirect) {
|
|
97
79
|
const parent = reader.u16();
|
package/dist/io/xlsb/read.d.ts
CHANGED
|
@@ -5,10 +5,10 @@ export declare const XLSB_WORKBOOK_PART = "xl/workbook.bin";
|
|
|
5
5
|
/**
|
|
6
6
|
* Read an `.xlsb` (binary BIFF12) package into a {@link Workbook}.
|
|
7
7
|
*
|
|
8
|
-
* @throws {UnsupportedFormatError} if the input is not an `.xlsb` package
|
|
8
|
+
* @throws {UnsupportedFormatError} if the input is not an `.xlsb` package: a legacy `.xls`
|
|
9
9
|
* (`.format === 'xls'`), an XML `.xlsx` or unrecognised blob (`'unknown'`).
|
|
10
10
|
* @throws {XlsbParseError} if a binary part is malformed.
|
|
11
|
-
* @throws {PackageReadError} if the input is a ZIP that cannot be unpacked
|
|
11
|
+
* @throws {PackageReadError} if the input is a ZIP that cannot be unpacked: a corrupt or
|
|
12
12
|
* truncated archive, or one exceeding the inflate bound (a probable zip bomb).
|
|
13
13
|
*/
|
|
14
14
|
export declare function readXlsb(data: Uint8Array, options?: ReadXlsxOptions): Workbook;
|
|
@@ -6,6 +6,9 @@ import { type SharedString } from './cell-value.ts';
|
|
|
6
6
|
import { RunAccumulator } from './rich-runs.ts';
|
|
7
7
|
export declare class CellAccumulator {
|
|
8
8
|
#private;
|
|
9
|
+
constructor(options: {
|
|
10
|
+
readonly richRuns: boolean;
|
|
11
|
+
});
|
|
9
12
|
/** This cell's `<c r>` address (`"B3"`), or '' when it carried none. */
|
|
10
13
|
get ref(): string;
|
|
11
14
|
/** This cell's own `<c s>` style index, or -1 when it carries none. */
|
|
@@ -14,12 +17,28 @@ export declare class CellAccumulator {
|
|
|
14
17
|
get col(): number;
|
|
15
18
|
/** The rich-text run accumulator, driven by the surrounding parser's `<r>`/`<rPr>` handling. */
|
|
16
19
|
get runs(): RunAccumulator;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
/**
|
|
21
|
+
* The text gathered since the current element opened. A caller reads it for the elements it
|
|
22
|
+
* captures itself (see {@link capture}); the machine reads it for its own.
|
|
23
|
+
*/
|
|
24
|
+
get capturedText(): string;
|
|
25
|
+
/** Gather the current element's text for the caller's own use, the way `<v>` and `<t>` do. */
|
|
26
|
+
capture(): void;
|
|
27
|
+
/**
|
|
28
|
+
* Drive one element open, and return whether it was one of the cell machine's own. Every open
|
|
29
|
+
* resets the capture state first, which is true of both readers and of every element, not just
|
|
30
|
+
* these; a caller that captures its own text calls {@link capture} after this returns.
|
|
31
|
+
*/
|
|
32
|
+
openElement(local: string, attrs: XmlAttributes, selfClosing: boolean): boolean;
|
|
33
|
+
/** Feed one run of character data. Ignored unless something is capturing. */
|
|
34
|
+
appendChunk(chunk: string): void;
|
|
35
|
+
/**
|
|
36
|
+
* Drive one element close. `'cell'` means a `</c>` closed and the caller should commit the
|
|
37
|
+
* gathered cell, which is the one step the two readers do differently; `'claimed'` means the
|
|
38
|
+
* machine handled it; `'other'` leaves it to the caller. Capture always ends here, as it does on
|
|
39
|
+
* every close in both readers.
|
|
40
|
+
*/
|
|
41
|
+
closeElement(local: string): 'cell' | 'claimed' | 'other';
|
|
23
42
|
finalize(sheet: Worksheet, sharedStrings: readonly SharedString[], style: XfStyle | undefined): void;
|
|
24
43
|
decode(sharedStrings: readonly SharedString[], style: XfStyle | undefined): CellValue;
|
|
25
44
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { decodeAddress, encodeAddress } from '../../core/address.js';
|
|
2
2
|
import { translateFormula, unmangleFunctions } from '../../core/formula.js';
|
|
3
|
-
import {
|
|
3
|
+
import { boolStrict, decodeSpreadsheetText, numInteger, } from '../../xml/xml-read.js';
|
|
4
4
|
import { applyXfToCell } from '../style/xf-style.js';
|
|
5
5
|
import { decodeCellContent, decodeFormulaResult, } from './cell-value.js';
|
|
6
6
|
import { RunAccumulator } from './rich-runs.js';
|
|
@@ -21,6 +21,13 @@ export class CellAccumulator {
|
|
|
21
21
|
#hasValue = false;
|
|
22
22
|
#runs = new RunAccumulator();
|
|
23
23
|
#masters = new Map();
|
|
24
|
+
#inInlineString = false;
|
|
25
|
+
#capture = false;
|
|
26
|
+
#text = '';
|
|
27
|
+
#richRuns;
|
|
28
|
+
constructor(options) {
|
|
29
|
+
this.#richRuns = options.richRuns;
|
|
30
|
+
}
|
|
24
31
|
get ref() {
|
|
25
32
|
return this.#ref;
|
|
26
33
|
}
|
|
@@ -33,16 +40,17 @@ export class CellAccumulator {
|
|
|
33
40
|
get runs() {
|
|
34
41
|
return this.#runs;
|
|
35
42
|
}
|
|
36
|
-
beginCell(attrs) {
|
|
43
|
+
#beginCell(attrs) {
|
|
37
44
|
this.#ref = attrs.r ?? '';
|
|
38
45
|
this.#type = attrs.t ?? '';
|
|
39
|
-
this.#style = attrs.s
|
|
40
|
-
|
|
41
|
-
this.#
|
|
46
|
+
this.#style = numInteger(attrs.s, 0) ?? -1;
|
|
47
|
+
const decoded = this.#ref === '' ? undefined : decodeAddress(this.#ref);
|
|
48
|
+
this.#col = decoded?.col ?? -1;
|
|
49
|
+
this.#row = decoded?.row ?? -1;
|
|
42
50
|
this.#formula = '';
|
|
43
51
|
this.#valueText = '';
|
|
44
52
|
this.#inlineText = '';
|
|
45
|
-
this.#runs.
|
|
53
|
+
this.#runs.beginContainer();
|
|
46
54
|
this.#hasFormula = false;
|
|
47
55
|
this.#hasValue = false;
|
|
48
56
|
this.#formulaShared = false;
|
|
@@ -50,9 +58,9 @@ export class CellAccumulator {
|
|
|
50
58
|
this.#sharedClone = false;
|
|
51
59
|
this.#dataTable = null;
|
|
52
60
|
}
|
|
53
|
-
beginFormula(attrs, selfClosing) {
|
|
61
|
+
#beginFormula(attrs, selfClosing) {
|
|
54
62
|
this.#formulaShared = attrs.t === 'shared';
|
|
55
|
-
this.#formulaSi = attrs.si
|
|
63
|
+
this.#formulaSi = numInteger(attrs.si, 0) ?? -1;
|
|
56
64
|
if (selfClosing && this.#formulaShared)
|
|
57
65
|
this.#sharedClone = true;
|
|
58
66
|
if (attrs.t === 'dataTable' && attrs.ref !== undefined) {
|
|
@@ -65,41 +73,120 @@ export class CellAccumulator {
|
|
|
65
73
|
};
|
|
66
74
|
}
|
|
67
75
|
}
|
|
68
|
-
setFormula(text) {
|
|
76
|
+
#setFormula(text) {
|
|
69
77
|
this.#formula = text;
|
|
70
78
|
this.#hasFormula = true;
|
|
71
79
|
}
|
|
72
|
-
setValue(text) {
|
|
80
|
+
#setValue(text) {
|
|
73
81
|
this.#valueText = text;
|
|
74
82
|
this.#hasValue = true;
|
|
75
83
|
}
|
|
76
|
-
beginInlineString() {
|
|
84
|
+
#beginInlineString() {
|
|
77
85
|
this.#inlineText = '';
|
|
78
|
-
this.#runs.
|
|
86
|
+
this.#runs.beginContainer();
|
|
87
|
+
}
|
|
88
|
+
#appendText(text) {
|
|
89
|
+
const decoded = decodeSpreadsheetText(text);
|
|
90
|
+
if (!this.#runs.appendText(decoded) && this.#inInlineString)
|
|
91
|
+
this.#inlineText += decoded;
|
|
92
|
+
}
|
|
93
|
+
get capturedText() {
|
|
94
|
+
return this.#text;
|
|
95
|
+
}
|
|
96
|
+
capture() {
|
|
97
|
+
this.#capture = true;
|
|
79
98
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
99
|
+
openElement(local, attrs, selfClosing) {
|
|
100
|
+
this.#text = '';
|
|
101
|
+
this.#capture = false;
|
|
102
|
+
switch (local) {
|
|
103
|
+
case 'c':
|
|
104
|
+
this.#beginCell(attrs);
|
|
105
|
+
return true;
|
|
106
|
+
case 'is':
|
|
107
|
+
this.#inInlineString = true;
|
|
108
|
+
this.#beginInlineString();
|
|
109
|
+
return true;
|
|
110
|
+
case 'f':
|
|
111
|
+
this.#capture = !selfClosing;
|
|
112
|
+
this.#beginFormula(attrs, selfClosing);
|
|
113
|
+
return true;
|
|
114
|
+
case 'v':
|
|
115
|
+
this.#capture = !selfClosing;
|
|
116
|
+
return true;
|
|
117
|
+
case 't':
|
|
118
|
+
this.#capture = true;
|
|
119
|
+
return true;
|
|
120
|
+
case 'r':
|
|
121
|
+
if (!this.#richRuns)
|
|
122
|
+
return false;
|
|
123
|
+
if (this.#inInlineString)
|
|
124
|
+
this.#runs.beginRun();
|
|
125
|
+
return true;
|
|
126
|
+
case 'rPr':
|
|
127
|
+
if (!this.#richRuns)
|
|
128
|
+
return false;
|
|
129
|
+
this.#runs.beginProperties();
|
|
130
|
+
return true;
|
|
131
|
+
default:
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
appendChunk(chunk) {
|
|
136
|
+
if (this.#capture)
|
|
137
|
+
this.#text += chunk;
|
|
138
|
+
}
|
|
139
|
+
closeElement(local) {
|
|
140
|
+
const verdict = this.#closeElement(local);
|
|
141
|
+
this.#capture = false;
|
|
142
|
+
return verdict;
|
|
143
|
+
}
|
|
144
|
+
#closeElement(local) {
|
|
145
|
+
switch (local) {
|
|
146
|
+
case 'f':
|
|
147
|
+
this.#setFormula(this.#text);
|
|
148
|
+
return 'claimed';
|
|
149
|
+
case 'v':
|
|
150
|
+
this.#setValue(this.#text);
|
|
151
|
+
return 'claimed';
|
|
152
|
+
case 't':
|
|
153
|
+
this.#appendText(this.#text);
|
|
154
|
+
return 'claimed';
|
|
155
|
+
case 'r':
|
|
156
|
+
if (!this.#richRuns)
|
|
157
|
+
return 'other';
|
|
158
|
+
this.#runs.endRun();
|
|
159
|
+
return 'claimed';
|
|
160
|
+
case 'is':
|
|
161
|
+
this.#inInlineString = false;
|
|
162
|
+
return 'claimed';
|
|
163
|
+
case 'c':
|
|
164
|
+
return 'cell';
|
|
165
|
+
default:
|
|
166
|
+
return 'other';
|
|
167
|
+
}
|
|
83
168
|
}
|
|
84
169
|
finalize(sheet, sharedStrings, style) {
|
|
85
170
|
if (this.#ref === '')
|
|
86
171
|
return;
|
|
172
|
+
const value = this.#resolveValue(sharedStrings, style);
|
|
173
|
+
const cell = sheet.getCell(this.#ref);
|
|
174
|
+
applyXfToCell(cell, style);
|
|
175
|
+
cell.value = value;
|
|
176
|
+
}
|
|
177
|
+
#resolveValue(sharedStrings, style) {
|
|
87
178
|
if (this.#dataTable !== null) {
|
|
88
|
-
|
|
179
|
+
return {
|
|
89
180
|
shareType: 'dataTable',
|
|
90
181
|
ref: this.#dataTable.ref,
|
|
91
|
-
...(
|
|
92
|
-
...(
|
|
182
|
+
...(boolStrict(this.#dataTable.dt2D) ? { dataTable2D: true } : {}),
|
|
183
|
+
...(boolStrict(this.#dataTable.dtr) ? { dataTableRow: true } : {}),
|
|
93
184
|
...(this.#dataTable.r1 !== undefined ? { r1: this.#dataTable.r1 } : {}),
|
|
94
185
|
...(this.#dataTable.r2 !== undefined ? { r2: this.#dataTable.r2 } : {}),
|
|
95
186
|
...(this.#hasValue
|
|
96
187
|
? { result: decodeFormulaResult(this.#type, this.#valueText, style?.numFmt) }
|
|
97
188
|
: {}),
|
|
98
189
|
};
|
|
99
|
-
const cell = sheet.getCell(this.#ref);
|
|
100
|
-
applyXfToCell(cell, style);
|
|
101
|
-
cell.value = value;
|
|
102
|
-
return;
|
|
103
190
|
}
|
|
104
191
|
if (this.#hasFormula && this.#formulaShared && this.#formulaSi >= 0) {
|
|
105
192
|
this.#masters.set(this.#formulaSi, { formula: this.#formula, col: this.#col, row: this.#row });
|
|
@@ -108,22 +195,16 @@ export class CellAccumulator {
|
|
|
108
195
|
const master = this.#masters.get(this.#formulaSi);
|
|
109
196
|
if (master !== undefined) {
|
|
110
197
|
const translated = translateFormula(master.formula, this.#col - master.col, this.#row - master.row);
|
|
111
|
-
|
|
198
|
+
return {
|
|
112
199
|
sharedFormula: encodeAddress(master.col, master.row),
|
|
113
200
|
formula: unmangleFunctions(translated),
|
|
114
201
|
...(this.#hasValue
|
|
115
202
|
? { result: decodeFormulaResult(this.#type, this.#valueText, style?.numFmt) }
|
|
116
203
|
: {}),
|
|
117
204
|
};
|
|
118
|
-
const cell = sheet.getCell(this.#ref);
|
|
119
|
-
applyXfToCell(cell, style);
|
|
120
|
-
cell.value = value;
|
|
121
|
-
return;
|
|
122
205
|
}
|
|
123
206
|
}
|
|
124
|
-
|
|
125
|
-
applyXfToCell(cell, style);
|
|
126
|
-
cell.value = this.decode(sharedStrings, style);
|
|
207
|
+
return this.decode(sharedStrings, style);
|
|
127
208
|
}
|
|
128
209
|
decode(sharedStrings, style) {
|
|
129
210
|
const raw = {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type CellValue, type FormulaResult, type RichTextRun, type RichTextValue } from '../../core/value.ts';
|
|
2
2
|
/**
|
|
3
3
|
* One entry of the shared-strings pool. A `<si>` built from a bare `<t>` is a plain string; a `<si>`
|
|
4
|
-
* built from `<r>` runs is rich text
|
|
4
|
+
* built from `<r>` runs is rich text, so a `t="s"` cell can resolve to either kind, and rich text
|
|
5
5
|
* that Excel pooled reads back with its per-run formatting intact rather than flattened to text.
|
|
6
6
|
*/
|
|
7
7
|
export type SharedString = string | RichTextValue;
|
|
@@ -26,7 +26,7 @@ export interface RawCell {
|
|
|
26
26
|
*/
|
|
27
27
|
export declare function decodeCellContent(raw: RawCell, sharedStrings: readonly SharedString[], numFmt: string | undefined): CellValue;
|
|
28
28
|
/** Decode a formula's cached `<v>` result by its `t` type, coercing a numeric result under a date
|
|
29
|
-
* `numFmt` to a {@link Date} exactly as a bare numeric cell is
|
|
29
|
+
* `numFmt` to a {@link Date} exactly as a bare numeric cell is, so a date-valued formula result
|
|
30
30
|
* (e.g. `TODAY()`) reads back as a Date, not a serial. Shared by the buffered reader's shared-formula
|
|
31
31
|
* clone resolution, which caches a result the same way a plain formula cell does. */
|
|
32
32
|
export declare function decodeFormulaResult(type: string, valueText: string, numFmt?: string): FormulaResult;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { isDateFormat, serialToDate } from '../../core/date.js';
|
|
2
2
|
import { unmangleFunctions } from '../../core/formula.js';
|
|
3
3
|
import { isErrorCode, } from '../../core/value.js';
|
|
4
|
-
import { boolStrict } from '../../xml/xml-read.js';
|
|
4
|
+
import { boolStrict, decodeSpreadsheetText } from '../../xml/xml-read.js';
|
|
5
5
|
export function decodeCellContent(raw, sharedStrings, numFmt) {
|
|
6
6
|
if (raw.hasFormula) {
|
|
7
7
|
const stored = unmangleFunctions(raw.formula);
|
|
@@ -21,7 +21,7 @@ function decodeValue(type, valueText, inlineText, hasValue, sharedStrings) {
|
|
|
21
21
|
case 'inlineStr':
|
|
22
22
|
return inlineText;
|
|
23
23
|
case 'str':
|
|
24
|
-
return valueText;
|
|
24
|
+
return decodeSpreadsheetText(valueText);
|
|
25
25
|
case 'd':
|
|
26
26
|
return valueText === '' ? null : new Date(valueText);
|
|
27
27
|
case 's': {
|
|
@@ -45,7 +45,7 @@ export function decodeFormulaResult(type, valueText, numFmt) {
|
|
|
45
45
|
function decodeResult(type, valueText) {
|
|
46
46
|
switch (type) {
|
|
47
47
|
case 'str':
|
|
48
|
-
return valueText;
|
|
48
|
+
return decodeSpreadsheetText(valueText);
|
|
49
49
|
case 'b':
|
|
50
50
|
return boolStrict(valueText);
|
|
51
51
|
case 'e':
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { numFinite, numInteger } from '../../xml/xml-read.js';
|
|
2
2
|
function normalizeArgb(argb) {
|
|
3
3
|
const hex = argb.startsWith('#') ? argb.slice(1) : argb;
|
|
4
4
|
const rgb = hex.length === 6 ? `FF${hex}` : hex;
|
|
5
5
|
if (!/^[0-9a-fA-F]{8}$/.test(rgb)) {
|
|
6
|
-
throw new
|
|
6
|
+
throw new SyntaxError(`Invalid ARGB colour ${JSON.stringify(argb)}: expected 6 or 8 hexadecimal digits`);
|
|
7
7
|
}
|
|
8
8
|
return rgb;
|
|
9
9
|
}
|
|
@@ -23,20 +23,14 @@ export function parseColor(attrs) {
|
|
|
23
23
|
const color = {};
|
|
24
24
|
if (attrs.rgb !== undefined)
|
|
25
25
|
color.argb = attrs.rgb;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
if (attrs.indexed !== undefined) {
|
|
37
|
-
const indexed = Number(attrs.indexed);
|
|
38
|
-
if (Number.isInteger(indexed))
|
|
39
|
-
color.indexed = indexed;
|
|
40
|
-
}
|
|
26
|
+
const theme = numInteger(attrs.theme, 0);
|
|
27
|
+
if (theme !== undefined)
|
|
28
|
+
color.theme = theme;
|
|
29
|
+
const tint = numFinite(attrs.tint);
|
|
30
|
+
if (tint !== undefined)
|
|
31
|
+
color.tint = tint;
|
|
32
|
+
const indexed = numInteger(attrs.indexed, 0);
|
|
33
|
+
if (indexed !== undefined)
|
|
34
|
+
color.indexed = indexed;
|
|
41
35
|
return color;
|
|
42
36
|
}
|