@shbernal/ts-xlsx 3.1.0 → 3.2.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 +30 -23
- package/dist/bytes.d.ts +3 -2
- package/dist/core/address.d.ts +36 -0
- package/dist/core/address.js +47 -9
- package/dist/core/array-formula-ranges.d.ts +33 -0
- package/dist/core/array-formula-ranges.js +62 -0
- package/dist/core/color-resolution.js +2 -2
- package/dist/core/column.js +3 -2
- package/dist/core/comment-thread.d.ts +7 -3
- package/dist/core/conditional-formatting-overlay.d.ts +9 -0
- package/dist/core/conditional-formatting-overlay.js +28 -1
- package/dist/core/conditional-formatting.d.ts +98 -10
- package/dist/core/conditional-formatting.js +42 -1
- package/dist/core/data-validation-overlay.d.ts +5 -0
- package/dist/core/data-validation-overlay.js +15 -0
- package/dist/core/data-validation.d.ts +16 -0
- package/dist/core/data-validation.js +15 -0
- package/dist/core/date-format.js +14 -4
- package/dist/core/date.js +1 -1
- package/dist/core/formula-references.d.ts +26 -0
- package/dist/core/formula-references.js +173 -0
- package/dist/core/formula-scan.d.ts +2 -0
- package/dist/core/formula-scan.js +58 -0
- package/dist/core/formula.d.ts +51 -18
- package/dist/core/formula.js +77 -86
- package/dist/core/function-values.d.ts +4 -0
- package/dist/core/function-values.js +58 -0
- package/dist/core/future-functions.d.ts +3 -0
- package/dist/core/{modern-functions.js → future-functions.js} +119 -88
- package/dist/core/grid-edits.d.ts +28 -0
- package/dist/core/grid-edits.js +193 -25
- package/dist/core/hyperlink.d.ts +39 -0
- package/dist/core/hyperlink.js +88 -0
- package/dist/core/image.d.ts +36 -5
- package/dist/core/image.js +9 -0
- package/dist/core/internal.d.ts +3 -1
- package/dist/core/limits.d.ts +5 -3
- package/dist/core/merge.d.ts +13 -0
- package/dist/core/merge.js +15 -1
- package/dist/core/page-setup.d.ts +26 -11
- package/dist/core/pivot-table.d.ts +49 -10
- package/dist/core/pivot-table.js +55 -21
- package/dist/core/protection.d.ts +66 -4
- package/dist/core/protection.js +4 -0
- package/dist/core/row.js +3 -2
- package/dist/core/structured-reference.d.ts +7 -0
- package/dist/core/structured-reference.js +7 -0
- package/dist/core/table.d.ts +38 -8
- package/dist/core/table.js +37 -7
- package/dist/core/theme.d.ts +2 -0
- package/dist/core/theme.js +4 -1
- package/dist/core/value.d.ts +51 -28
- package/dist/core/value.js +15 -12
- package/dist/core/workbook-styles.d.ts +17 -1
- package/dist/core/workbook-styles.js +4 -0
- package/dist/core/workbook-theme.js +5 -3
- package/dist/core/workbook.d.ts +20 -3
- package/dist/core/workbook.js +26 -5
- package/dist/core/worksheet-merges.d.ts +9 -4
- package/dist/core/worksheet-merges.js +17 -11
- package/dist/core/worksheet-model.d.ts +1 -1
- package/dist/core/worksheet-model.js +4 -0
- package/dist/core/worksheet-pictures.d.ts +3 -3
- package/dist/core/worksheet-pictures.js +7 -6
- package/dist/core/worksheet.d.ts +110 -21
- package/dist/core/worksheet.js +174 -49
- package/dist/entries/core.d.ts +7 -5
- package/dist/entries/core.js +4 -3
- package/dist/entries/errors.d.ts +1 -0
- package/dist/entries/errors.js +1 -0
- package/dist/errors.js +1 -1
- package/dist/io/cell-metadata/metadata.d.ts +42 -0
- package/dist/io/cell-metadata/metadata.js +35 -0
- package/dist/io/cell-metadata/rich-values.d.ts +11 -0
- package/dist/io/cell-metadata/rich-values.js +80 -0
- package/dist/io/csv/delimiter.d.ts +14 -2
- package/dist/io/csv/delimiter.js +12 -0
- package/dist/io/csv/errors.d.ts +14 -0
- package/dist/io/csv/errors.js +5 -0
- package/dist/io/csv/read.d.ts +6 -1
- package/dist/io/csv/read.js +46 -24
- package/dist/io/csv/write.d.ts +10 -3
- package/dist/io/csv/write.js +16 -7
- package/dist/io/opc/part-paths.js +4 -4
- package/dist/io/opc/read-opc.d.ts +4 -0
- package/dist/io/opc/read-opc.js +10 -4
- package/dist/io/opc/strict-relationships.d.ts +2 -0
- package/dist/io/opc/strict-relationships.js +12 -0
- package/dist/io/read-policy/column-budget.d.ts +26 -0
- package/dist/io/{xlsx → read-policy}/column-budget.js +4 -8
- package/dist/io/read-policy/read-repair.d.ts +74 -0
- package/dist/io/read-policy/read-repair.js +119 -0
- package/dist/io/style/cell-style-resolution.d.ts +25 -0
- package/dist/io/style/cell-style-resolution.js +30 -0
- package/dist/io/xlsb/formula.js +4 -2
- package/dist/io/xlsb/ptg-functions.js +110 -0
- package/dist/io/xlsb/read-metadata.d.ts +7 -0
- package/dist/io/xlsb/read-metadata.js +69 -0
- package/dist/io/xlsb/read-worksheet.d.ts +6 -0
- package/dist/io/xlsb/read-worksheet.js +95 -37
- package/dist/io/xlsb/read.js +26 -8
- package/dist/io/xlsb/record-types.d.ts +13 -0
- package/dist/io/xlsb/record-types.js +13 -0
- package/dist/io/xlsb/sheet-protection.d.ts +20 -0
- package/dist/io/xlsb/sheet-protection.js +60 -0
- package/dist/io/xlsx/cell-accumulator.d.ts +20 -12
- package/dist/io/xlsx/cell-accumulator.js +49 -19
- package/dist/io/xlsx/cell-metadata.d.ts +39 -0
- package/dist/io/xlsx/cell-metadata.js +162 -0
- package/dist/io/xlsx/cell-value.d.ts +16 -4
- package/dist/io/xlsx/cell-value.js +38 -21
- package/dist/io/xlsx/color-xml.js +1 -1
- package/dist/io/xlsx/column-span.d.ts +11 -0
- package/dist/io/xlsx/column-span.js +9 -0
- package/dist/io/xlsx/comments.d.ts +3 -28
- package/dist/io/xlsx/comments.js +3 -65
- package/dist/io/xlsx/conditional-formatting.d.ts +42 -25
- package/dist/io/xlsx/conditional-formatting.js +424 -129
- package/dist/io/xlsx/data-validation.d.ts +12 -7
- package/dist/io/xlsx/data-validation.js +41 -29
- package/dist/io/xlsx/edit-vba.js +43 -36
- package/dist/io/xlsx/hyperlinks.d.ts +16 -50
- package/dist/io/xlsx/hyperlinks.js +8 -37
- package/dist/io/xlsx/images.d.ts +49 -16
- package/dist/io/xlsx/images.js +145 -44
- package/dist/io/xlsx/namespaces.d.ts +8 -0
- package/dist/io/xlsx/namespaces.js +4 -0
- package/dist/io/xlsx/package-plan.d.ts +48 -15
- package/dist/io/xlsx/package-plan.js +46 -9
- package/dist/io/xlsx/part-names.d.ts +6 -8
- package/dist/io/xlsx/part-names.js +3 -7
- package/dist/io/xlsx/pivot.d.ts +5 -5
- package/dist/io/xlsx/pivot.js +23 -22
- package/dist/io/xlsx/preserved-splices.d.ts +6 -0
- package/dist/io/xlsx/preserved-splices.js +74 -0
- package/dist/io/xlsx/read-comments.d.ts +29 -0
- package/dist/io/xlsx/read-comments.js +75 -0
- package/dist/io/xlsx/read-parts.d.ts +2 -2
- package/dist/io/xlsx/read-parts.js +44 -37
- package/dist/io/xlsx/read-pivot.js +9 -3
- package/dist/io/xlsx/read-rich-runs.d.ts +8 -5
- package/dist/io/xlsx/read-rich-runs.js +18 -1
- package/dist/io/xlsx/read-rows.d.ts +18 -7
- package/dist/io/xlsx/read-rows.js +47 -37
- package/dist/io/xlsx/read-styles.d.ts +0 -25
- package/dist/io/xlsx/read-styles.js +39 -34
- package/dist/io/xlsx/read-workbook-xml.d.ts +6 -0
- package/dist/io/xlsx/read-workbook-xml.js +9 -4
- package/dist/io/xlsx/read-worksheet.d.ts +8 -3
- package/dist/io/xlsx/read-worksheet.js +45 -23
- package/dist/io/xlsx/read.d.ts +0 -4
- package/dist/io/xlsx/read.js +17 -21
- package/dist/io/xlsx/relationships.d.ts +3 -0
- package/dist/io/xlsx/relationships.js +4 -0
- package/dist/io/xlsx/row-position.d.ts +3 -1
- package/dist/io/xlsx/row-xml.d.ts +18 -7
- package/dist/io/xlsx/row-xml.js +54 -35
- package/dist/io/xlsx/sheet-properties.js +68 -42
- package/dist/io/xlsx/strict-parts.d.ts +7 -0
- package/dist/io/xlsx/strict-parts.js +171 -0
- package/dist/io/xlsx/style-elements.d.ts +0 -2
- package/dist/io/xlsx/style-elements.js +9 -29
- package/dist/io/xlsx/styles.d.ts +5 -0
- package/dist/io/xlsx/styles.js +11 -5
- package/dist/io/xlsx/tables.d.ts +10 -2
- package/dist/io/xlsx/tables.js +49 -42
- package/dist/io/xlsx/theme-xml.d.ts +6 -0
- package/dist/io/xlsx/theme-xml.js +16 -19
- package/dist/io/xlsx/threaded-comments.d.ts +6 -1
- package/dist/io/xlsx/threaded-comments.js +14 -7
- package/dist/io/xlsx/workbook-xml.d.ts +13 -24
- package/dist/io/xlsx/workbook-xml.js +15 -33
- package/dist/io/xlsx/worksheet-xml.d.ts +5 -2
- package/dist/io/xlsx/worksheet-xml.js +37 -69
- package/dist/io/xlsx/write-stream.d.ts +28 -6
- package/dist/io/xlsx/write-stream.js +42 -17
- package/dist/io/xlsx/write.d.ts +11 -4
- package/dist/io/xlsx/write.js +153 -90
- package/dist/rel-type.d.ts +9 -0
- package/dist/rel-type.js +3 -0
- package/dist/vba/bytes.d.ts +2 -0
- package/dist/vba/bytes.js +11 -0
- package/dist/vba/cfb-format.d.ts +47 -0
- package/dist/vba/cfb-format.js +34 -0
- package/dist/vba/cfb-writer.d.ts +1 -2
- package/dist/vba/cfb-writer.js +33 -36
- package/dist/vba/cfb.d.ts +9 -4
- package/dist/vba/cfb.js +55 -43
- package/dist/vba/dir-records.d.ts +6 -0
- package/dist/vba/dir-records.js +10 -2
- package/dist/vba/errors.d.ts +4 -4
- package/dist/vba/index.d.ts +0 -2
- package/dist/vba/index.js +0 -2
- package/dist/vba/ms-ovba.d.ts +6 -2
- package/dist/vba/ms-ovba.js +15 -6
- package/dist/vba/project-editor.js +102 -87
- package/dist/vba/project.d.ts +4 -0
- package/dist/vba/project.js +12 -6
- package/dist/vba/vba-encoding.d.ts +4 -10
- package/dist/vba/vba-encoding.js +2 -15
- package/dist/xml/xml-read.d.ts +29 -1
- package/dist/xml/xml-read.js +56 -0
- package/dist/xml/xml-scan.d.ts +13 -0
- package/dist/xml/xml-scan.js +58 -7
- package/dist/xml/xml.d.ts +33 -1
- package/dist/xml/xml.js +22 -0
- package/package.json +5 -4
- package/skills/ts-xlsx-upstream/SKILL.md +21 -13
- package/dist/core/modern-functions.d.ts +0 -1
- package/dist/io/xlsx/cell-style-resolution.d.ts +0 -25
- package/dist/io/xlsx/cell-style-resolution.js +0 -29
- package/dist/io/xlsx/column-budget.d.ts +0 -26
- package/dist/io/xlsx/read-repair.d.ts +0 -26
- package/dist/io/xlsx/read-repair.js +0 -39
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { AssertNever } from '../../core/internal.ts';
|
|
2
|
+
import { type SheetProtection, type SheetProtectionFlags } from '../../core/protection.ts';
|
|
3
|
+
import type { RecordReader } from './primitives.ts';
|
|
4
|
+
declare const FLAGS_IN_RECORD_ORDER: readonly ["objects", "scenarios", "formatCells", "formatColumns", "formatRows", "insertColumns", "insertRows", "insertHyperlinks", "deleteColumns", "deleteRows", "selectLockedCells", "sort", "autoFilter", "pivotTables", "selectUnlockedCells"];
|
|
5
|
+
/**
|
|
6
|
+
* The half of the proof an ordered list owes: `satisfies` covers "no invented flag", this covers "no
|
|
7
|
+
* omission". A flag left out here would be read at the wrong offset along with every flag after it.
|
|
8
|
+
*/
|
|
9
|
+
export type EveryProtectionFlagHasARecordSlot = AssertNever<Exclude<keyof SheetProtectionFlags, (typeof FLAGS_IN_RECORD_ORDER)[number]>>;
|
|
10
|
+
/** What a worksheet's protection records say, gathered as they arrive. */
|
|
11
|
+
export declare class SheetProtectionRecords {
|
|
12
|
+
#private;
|
|
13
|
+
/** Read a `BrtSheetProtection`. */
|
|
14
|
+
legacy(reader: RecordReader): void;
|
|
15
|
+
/** Read a `BrtSheetProtectionIso`. */
|
|
16
|
+
iso(reader: RecordReader): void;
|
|
17
|
+
/** The protection the records described, or `undefined` for a sheet that is not protected. */
|
|
18
|
+
result(): SheetProtection | undefined;
|
|
19
|
+
}
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { toBase64 } from '../../bytes.js';
|
|
2
|
+
import { SHEET_PROTECTION_FLAGS, } from '../../core/protection.js';
|
|
3
|
+
import { hex } from '../../hex.js';
|
|
4
|
+
const FLAGS_IN_RECORD_ORDER = [
|
|
5
|
+
'objects',
|
|
6
|
+
'scenarios',
|
|
7
|
+
'formatCells',
|
|
8
|
+
'formatColumns',
|
|
9
|
+
'formatRows',
|
|
10
|
+
'insertColumns',
|
|
11
|
+
'insertRows',
|
|
12
|
+
'insertHyperlinks',
|
|
13
|
+
'deleteColumns',
|
|
14
|
+
'deleteRows',
|
|
15
|
+
'selectLockedCells',
|
|
16
|
+
'sort',
|
|
17
|
+
'autoFilter',
|
|
18
|
+
'pivotTables',
|
|
19
|
+
'selectUnlockedCells',
|
|
20
|
+
];
|
|
21
|
+
const ALLOWED_BY_DEFAULT = new Map(SHEET_PROTECTION_FLAGS.map(({ key, defaultForbidden }) => [key, !defaultForbidden]));
|
|
22
|
+
export class SheetProtectionRecords {
|
|
23
|
+
#locked = false;
|
|
24
|
+
#flags = {};
|
|
25
|
+
#legacyPasswordHash;
|
|
26
|
+
#credential;
|
|
27
|
+
legacy(reader) {
|
|
28
|
+
const protpwd = reader.u16();
|
|
29
|
+
this.#readFlags(reader);
|
|
30
|
+
this.#legacyPasswordHash = protpwd === 0 ? undefined : hex(protpwd, 4);
|
|
31
|
+
}
|
|
32
|
+
iso(reader) {
|
|
33
|
+
const spinCount = reader.u32();
|
|
34
|
+
this.#readFlags(reader);
|
|
35
|
+
const hashValue = toBase64(reader.bytes(reader.u32()));
|
|
36
|
+
const saltValue = toBase64(reader.bytes(reader.u32()));
|
|
37
|
+
this.#credential = { algorithmName: reader.wideString(), hashValue, saltValue, spinCount };
|
|
38
|
+
}
|
|
39
|
+
result() {
|
|
40
|
+
if (!this.#locked)
|
|
41
|
+
return undefined;
|
|
42
|
+
return {
|
|
43
|
+
flags: this.#flags,
|
|
44
|
+
...(this.#credential === undefined ? {} : { credential: this.#credential }),
|
|
45
|
+
...(this.#legacyPasswordHash === undefined
|
|
46
|
+
? {}
|
|
47
|
+
: { legacyPasswordHash: this.#legacyPasswordHash }),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
#readFlags(reader) {
|
|
51
|
+
this.#locked = reader.u32() !== 0;
|
|
52
|
+
const flags = {};
|
|
53
|
+
for (const key of FLAGS_IN_RECORD_ORDER) {
|
|
54
|
+
const allowed = reader.u32() !== 0;
|
|
55
|
+
if (allowed !== ALLOWED_BY_DEFAULT.get(key))
|
|
56
|
+
flags[key] = allowed;
|
|
57
|
+
}
|
|
58
|
+
this.#flags = flags;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -2,13 +2,23 @@ import type { DateEpoch } from '../../core/date.ts';
|
|
|
2
2
|
import type { CellValue } from '../../core/value.ts';
|
|
3
3
|
import type { Worksheet } from '../../core/worksheet.ts';
|
|
4
4
|
import { type XmlAttributes } from '../../xml/xml-scan.ts';
|
|
5
|
+
import type { CellMetadataIndex } from '../cell-metadata/metadata.ts';
|
|
5
6
|
import { type XfStyle } from '../style/xf-style.ts';
|
|
6
7
|
import { type SharedString } from './cell-value.ts';
|
|
8
|
+
/**
|
|
9
|
+
* The worksheet-body elements both readers expand from `<x/>` into an open and a close, so each
|
|
10
|
+
* commits from its close alone. A formatted-but-empty `<c/>` finalises once there, and a self-closing
|
|
11
|
+
* `<row/>` closes its row, where the streamer yields it and both readers end its style and its cells:
|
|
12
|
+
* a producer may write `<row r="2" hidden="1"/>`, and with no close the streamer lost the row and its
|
|
13
|
+
* `hidden` flag. The text-bearing `<f/>`/`<v/>`/`<t/>` stay out, so an empty one never commits text.
|
|
14
|
+
*/
|
|
15
|
+
export declare const WORKSHEET_BODY_EMPTY_CLOSES: ReadonlySet<string>;
|
|
7
16
|
export declare class CellAccumulator {
|
|
8
17
|
#private;
|
|
9
18
|
constructor(options: {
|
|
10
|
-
readonly richRuns: boolean;
|
|
11
19
|
readonly dateEpoch: DateEpoch;
|
|
20
|
+
readonly definedNames: ReadonlySet<string>;
|
|
21
|
+
readonly cellMetadata: CellMetadataIndex;
|
|
12
22
|
});
|
|
13
23
|
/** This cell's `<c r>` address (`"B3"`), or '' when it carried none. */
|
|
14
24
|
get ref(): string;
|
|
@@ -18,20 +28,19 @@ export declare class CellAccumulator {
|
|
|
18
28
|
get col(): number;
|
|
19
29
|
/**
|
|
20
30
|
* Open a `<row>`: the cells that follow belong to it, and the next one with no `r` of its own is
|
|
21
|
-
* its first column.
|
|
31
|
+
* its first column. A row past the grid opens no row, so none of its cells is placed, not even one
|
|
32
|
+
* whose own `r` names an in-grid cell. Both readers call this where they already tell the style
|
|
33
|
+
* resolver a row opened.
|
|
22
34
|
*/
|
|
23
|
-
openRow(number: number): void;
|
|
24
|
-
/**
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
get capturedText(): string;
|
|
29
|
-
/** Gather the current element's text for the caller's own use, the way `<v>` and `<t>` do. */
|
|
30
|
-
capture(): void;
|
|
35
|
+
openRow(number: number, inGrid: boolean): void;
|
|
36
|
+
/** Whether an in-grid `<row>` is open. */
|
|
37
|
+
get rowOpen(): boolean;
|
|
38
|
+
/** Close the `<row>`: a `<c>` before the next one belongs to no row and is not placed. */
|
|
39
|
+
closeRow(): void;
|
|
31
40
|
/**
|
|
32
41
|
* Drive one element open, and return whether it was one of the cell machine's own. Every open
|
|
33
42
|
* resets the capture state first, which is true of both readers and of every element, not just
|
|
34
|
-
* these
|
|
43
|
+
* these. A caller gathering text of its own uses a `TextCapture`, not this machine's buffer.
|
|
35
44
|
*/
|
|
36
45
|
openElement(local: string, attrs: XmlAttributes, selfClosing: boolean): boolean;
|
|
37
46
|
/** Feed one run of character data. Ignored unless something is capturing. */
|
|
@@ -44,6 +53,5 @@ export declare class CellAccumulator {
|
|
|
44
53
|
*/
|
|
45
54
|
closeElement(local: string): 'cell' | 'claimed' | 'other';
|
|
46
55
|
finalize(sheet: Worksheet, sharedStrings: readonly SharedString[], style: XfStyle | undefined): void;
|
|
47
|
-
private cachedResult;
|
|
48
56
|
decode(sharedStrings: readonly SharedString[], style: XfStyle | undefined): CellValue;
|
|
49
57
|
}
|
|
@@ -1,22 +1,28 @@
|
|
|
1
|
-
import { encodeAddress, MAX_COLUMN, tryDecodeCellRef } from '../../core/address.js';
|
|
2
|
-
import { translateFormula
|
|
1
|
+
import { encodeAddress, encodeRange, MAX_COLUMN, tryDecodeAnchoredRange, tryDecodeCellRef, } from '../../core/address.js';
|
|
2
|
+
import { translateFormula } from '../../core/formula-references.js';
|
|
3
|
+
import { unmangleFunctions } from '../../core/formula.js';
|
|
3
4
|
import { numInteger } from '../../xml/xml-attrs.js';
|
|
4
5
|
import { boolStrict } from '../../xml/xml-scan.js';
|
|
5
6
|
import { applyXfToCell } from '../style/xf-style.js';
|
|
6
7
|
import { decodeCellContent, decodeFormulaResult, } from './cell-value.js';
|
|
7
8
|
import { RunAccumulator } from './read-rich-runs.js';
|
|
9
|
+
export const WORKSHEET_BODY_EMPTY_CLOSES = new Set(['c', 'row']);
|
|
8
10
|
export class CellAccumulator {
|
|
9
11
|
#ref = '';
|
|
10
12
|
#type = '';
|
|
11
13
|
#style = -1;
|
|
12
14
|
#col = -1;
|
|
13
15
|
#row = -1;
|
|
16
|
+
#cellMetadataBlock = -1;
|
|
17
|
+
#valueMetadataBlock = -1;
|
|
18
|
+
#rowOpen = false;
|
|
14
19
|
#nextCol = 1;
|
|
15
20
|
#formula = '';
|
|
16
21
|
#formulaShared = false;
|
|
17
22
|
#formulaSi = -1;
|
|
18
23
|
#sharedClone = false;
|
|
19
24
|
#dataTable = null;
|
|
25
|
+
#arrayRef = undefined;
|
|
20
26
|
#valueText = '';
|
|
21
27
|
#hasFormula = false;
|
|
22
28
|
#hasValue = false;
|
|
@@ -25,9 +31,13 @@ export class CellAccumulator {
|
|
|
25
31
|
#capture = false;
|
|
26
32
|
#text = '';
|
|
27
33
|
#dateEpoch;
|
|
34
|
+
#definedNames;
|
|
35
|
+
#cellMetadata;
|
|
28
36
|
constructor(options) {
|
|
29
|
-
this.#runs = new RunAccumulator({ container: 'is', readRuns:
|
|
37
|
+
this.#runs = new RunAccumulator({ container: 'is', readRuns: true });
|
|
30
38
|
this.#dateEpoch = options.dateEpoch;
|
|
39
|
+
this.#definedNames = options.definedNames;
|
|
40
|
+
this.#cellMetadata = options.cellMetadata;
|
|
31
41
|
}
|
|
32
42
|
get ref() {
|
|
33
43
|
return this.#ref;
|
|
@@ -38,13 +48,23 @@ export class CellAccumulator {
|
|
|
38
48
|
get col() {
|
|
39
49
|
return this.#col;
|
|
40
50
|
}
|
|
41
|
-
openRow(number) {
|
|
42
|
-
this.#
|
|
51
|
+
openRow(number, inGrid) {
|
|
52
|
+
this.#rowOpen = inGrid;
|
|
53
|
+
this.#row = inGrid ? number : -1;
|
|
43
54
|
this.#nextCol = 1;
|
|
44
55
|
}
|
|
56
|
+
get rowOpen() {
|
|
57
|
+
return this.#rowOpen;
|
|
58
|
+
}
|
|
59
|
+
closeRow() {
|
|
60
|
+
this.#rowOpen = false;
|
|
61
|
+
this.#row = -1;
|
|
62
|
+
}
|
|
45
63
|
#beginCell(attrs) {
|
|
46
64
|
this.#type = attrs.t ?? '';
|
|
47
65
|
this.#style = numInteger(attrs.s, 0) ?? -1;
|
|
66
|
+
this.#cellMetadataBlock = numInteger(attrs.cm, 1) ?? -1;
|
|
67
|
+
this.#valueMetadataBlock = numInteger(attrs.vm, 1) ?? -1;
|
|
48
68
|
this.#placeCell(attrs.r);
|
|
49
69
|
this.#formula = '';
|
|
50
70
|
this.#valueText = '';
|
|
@@ -55,15 +75,16 @@ export class CellAccumulator {
|
|
|
55
75
|
this.#formulaSi = -1;
|
|
56
76
|
this.#sharedClone = false;
|
|
57
77
|
this.#dataTable = null;
|
|
78
|
+
this.#arrayRef = undefined;
|
|
58
79
|
}
|
|
59
80
|
#placeCell(ref) {
|
|
60
|
-
const decoded = ref
|
|
81
|
+
const decoded = this.#rowOpen && ref !== undefined ? tryDecodeCellRef(ref) : undefined;
|
|
61
82
|
if (decoded !== undefined) {
|
|
62
83
|
this.#ref = ref ?? '';
|
|
63
84
|
this.#col = decoded.col;
|
|
64
85
|
this.#row = decoded.row;
|
|
65
86
|
}
|
|
66
|
-
else if (ref === undefined && this.#
|
|
87
|
+
else if (ref === undefined && this.#rowOpen && this.#nextCol <= MAX_COLUMN) {
|
|
67
88
|
this.#col = this.#nextCol;
|
|
68
89
|
this.#ref = encodeAddress(this.#col, this.#row);
|
|
69
90
|
}
|
|
@@ -86,8 +107,15 @@ export class CellAccumulator {
|
|
|
86
107
|
dtr: attrs.dtr,
|
|
87
108
|
r1: attrs.r1,
|
|
88
109
|
r2: attrs.r2,
|
|
110
|
+
del1: attrs.del1,
|
|
111
|
+
del2: attrs.del2,
|
|
89
112
|
};
|
|
90
113
|
}
|
|
114
|
+
if (attrs.t === 'array' && attrs.ref !== undefined && this.#col > 0) {
|
|
115
|
+
const range = tryDecodeAnchoredRange(attrs.ref, this.#col, this.#row);
|
|
116
|
+
if (range !== undefined)
|
|
117
|
+
this.#arrayRef = encodeRange(range);
|
|
118
|
+
}
|
|
91
119
|
}
|
|
92
120
|
#setFormula(text) {
|
|
93
121
|
this.#formula = text;
|
|
@@ -97,12 +125,6 @@ export class CellAccumulator {
|
|
|
97
125
|
this.#valueText = text;
|
|
98
126
|
this.#hasValue = true;
|
|
99
127
|
}
|
|
100
|
-
get capturedText() {
|
|
101
|
-
return this.#text;
|
|
102
|
-
}
|
|
103
|
-
capture() {
|
|
104
|
-
this.#capture = true;
|
|
105
|
-
}
|
|
106
128
|
openElement(local, attrs, selfClosing) {
|
|
107
129
|
this.#text = '';
|
|
108
130
|
this.#capture = false;
|
|
@@ -118,6 +140,8 @@ export class CellAccumulator {
|
|
|
118
140
|
return true;
|
|
119
141
|
case 'v':
|
|
120
142
|
this.#capture = !selfClosing;
|
|
143
|
+
if (selfClosing)
|
|
144
|
+
this.#setValue('');
|
|
121
145
|
return true;
|
|
122
146
|
default:
|
|
123
147
|
return false;
|
|
@@ -166,7 +190,9 @@ export class CellAccumulator {
|
|
|
166
190
|
...(boolStrict(this.#dataTable.dtr) ? { dataTableRow: true } : {}),
|
|
167
191
|
...(this.#dataTable.r1 !== undefined ? { r1: this.#dataTable.r1 } : {}),
|
|
168
192
|
...(this.#dataTable.r2 !== undefined ? { r2: this.#dataTable.r2 } : {}),
|
|
169
|
-
...this.
|
|
193
|
+
...(boolStrict(this.#dataTable.del1) ? { r1Deleted: true } : {}),
|
|
194
|
+
...(boolStrict(this.#dataTable.del2) ? { r2Deleted: true } : {}),
|
|
195
|
+
...this.#cachedResult(style),
|
|
170
196
|
};
|
|
171
197
|
}
|
|
172
198
|
if (this.#hasFormula && this.#formulaShared && this.#formulaSi >= 0) {
|
|
@@ -178,17 +204,17 @@ export class CellAccumulator {
|
|
|
178
204
|
const translated = translateFormula(master.formula, this.#col - master.col, this.#row - master.row);
|
|
179
205
|
return {
|
|
180
206
|
sharedFormula: encodeAddress(master.col, master.row),
|
|
181
|
-
formula: unmangleFunctions(translated),
|
|
182
|
-
...this
|
|
207
|
+
formula: unmangleFunctions(translated, this.#definedNames),
|
|
208
|
+
...this.#cachedResult(style),
|
|
183
209
|
};
|
|
184
210
|
}
|
|
185
211
|
}
|
|
186
212
|
return this.decode(sharedStrings, style);
|
|
187
213
|
}
|
|
188
|
-
cachedResult(style) {
|
|
214
|
+
#cachedResult(style) {
|
|
189
215
|
if (!this.#hasValue)
|
|
190
216
|
return {};
|
|
191
|
-
const result = decodeFormulaResult(this.#type, this.#valueText, style?.numFmt, this.#dateEpoch);
|
|
217
|
+
const result = decodeFormulaResult(this.#type, this.#valueText, style?.numFmt, this.#dateEpoch, this.#cellMetadata.valueErrors.get(this.#valueMetadataBlock));
|
|
192
218
|
return result === undefined ? {} : { result };
|
|
193
219
|
}
|
|
194
220
|
decode(sharedStrings, style) {
|
|
@@ -196,11 +222,15 @@ export class CellAccumulator {
|
|
|
196
222
|
type: this.#type,
|
|
197
223
|
hasFormula: this.#hasFormula,
|
|
198
224
|
formula: this.#formula,
|
|
225
|
+
arrayRef: this.#arrayRef,
|
|
226
|
+
dynamicArray: this.#cellMetadata.dynamicArrayCells.has(this.#cellMetadataBlock),
|
|
227
|
+
valueError: this.#cellMetadata.valueErrors.get(this.#valueMetadataBlock),
|
|
199
228
|
hasValue: this.#hasValue,
|
|
200
229
|
valueText: this.#valueText,
|
|
230
|
+
hasInlineString: this.#runs.opened,
|
|
201
231
|
inlineText: this.#runs.plainText,
|
|
202
232
|
richTextRuns: this.#runs.runs,
|
|
203
233
|
};
|
|
204
|
-
return decodeCellContent(raw, sharedStrings, style?.numFmt, this.#dateEpoch);
|
|
234
|
+
return decodeCellContent(raw, sharedStrings, style?.numFmt, this.#dateEpoch, this.#definedNames);
|
|
205
235
|
}
|
|
206
236
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { ErrorCode } from '../../core/value.ts';
|
|
2
|
+
import { type CellMetadataIndex, type WorkbookMetadata } from '../cell-metadata/metadata.ts';
|
|
3
|
+
import type { PartRelationships } from '../opc/read-opc.ts';
|
|
4
|
+
/**
|
|
5
|
+
* What every `cm` and `vm` in the workbook resolves to: the metadata part and the two rich-value parts,
|
|
6
|
+
* each found through the workbook's relationships by type. Both worksheet readers resolve their cells
|
|
7
|
+
* through this one answer.
|
|
8
|
+
*/
|
|
9
|
+
export declare function readCellMetadata(workbookRels: PartRelationships): CellMetadataIndex;
|
|
10
|
+
/** Read `xl/metadata.xml` as far as a cell's `cm` and `vm` reach into it. */
|
|
11
|
+
export declare function parseMetadataPart(xml: string): WorkbookMetadata;
|
|
12
|
+
/**
|
|
13
|
+
* Records what the cells written so far point into, so the metadata part and the rich-value parts are
|
|
14
|
+
* emitted only for a workbook that needs them. Filled during the sheet pass, as the shared-strings table
|
|
15
|
+
* is, because the streaming writer renders a row long before the package around it is assembled.
|
|
16
|
+
*/
|
|
17
|
+
export declare class WorkbookMetadataTable {
|
|
18
|
+
#private;
|
|
19
|
+
/** The `cm` a dynamic-array formula's cell is written with. Every such cell shares the one block. */
|
|
20
|
+
markDynamicArray(): number;
|
|
21
|
+
/**
|
|
22
|
+
* The `vm` a cell holding `error` is written with, beside a `<v>` of `#VALUE!`, or `undefined` for
|
|
23
|
+
* an error Excel reads literally, which needs no metadata.
|
|
24
|
+
*/
|
|
25
|
+
markRichValueError(error: ErrorCode): number | undefined;
|
|
26
|
+
/** Whether no cell points into the metadata part; the writer omits it entirely when so. */
|
|
27
|
+
get isEmpty(): boolean;
|
|
28
|
+
/** Whether a cell points at a rich value, so the rich-value parts are emitted beside the metadata. */
|
|
29
|
+
get hasRichValues(): boolean;
|
|
30
|
+
/** Serialise the `xl/metadata.xml` part, in the form Excel saves it. */
|
|
31
|
+
toXml(): string;
|
|
32
|
+
/**
|
|
33
|
+
* Serialise `xl/richData/rdrichvalue.xml`: one rich value per error, in the order the `vm`s were handed
|
|
34
|
+
* out, each naming its `errorType` and the unknown sub-type, 0.
|
|
35
|
+
*/
|
|
36
|
+
richValuesXml(): string;
|
|
37
|
+
}
|
|
38
|
+
/** `xl/richData/rdrichvaluestructure.xml`: the one structure every rich value the writer emits has. */
|
|
39
|
+
export declare const RICH_VALUE_STRUCTURES_XML: string;
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { numInteger } from '../../xml/xml-attrs.js';
|
|
2
|
+
import { parseXml } from '../../xml/xml-read.js';
|
|
3
|
+
import { boolStrict, localName } from '../../xml/xml-scan.js';
|
|
4
|
+
import { XML_DECLARATION } from '../../xml/xml.js';
|
|
5
|
+
import { DYNAMIC_ARRAY_TYPE, indexCellMetadata, RICH_VALUE_TYPE, } from '../cell-metadata/metadata.js';
|
|
6
|
+
import { ERROR_STRUCTURE, parseRichValueErrors, RICH_VALUE_ERROR_TYPES, } from '../cell-metadata/rich-values.js';
|
|
7
|
+
import { DYNAMIC_ARRAY_NS, DYNAMIC_ARRAY_PROPERTIES_EXT_URI, RICH_DATA_NS, RICH_VALUE_BLOCK_EXT_URI, SPREADSHEETML_NS, } from './namespaces.js';
|
|
8
|
+
export function readCellMetadata(workbookRels) {
|
|
9
|
+
return indexCellMetadata(parseMetadataPart(workbookRels.relatedText('sheetMetadata') ?? ''), parseRichValueErrors(workbookRels.relatedText('rdRichValueStructure') ?? '', workbookRels.relatedText('rdRichValue') ?? ''));
|
|
10
|
+
}
|
|
11
|
+
export function parseMetadataPart(xml) {
|
|
12
|
+
const typeNames = [];
|
|
13
|
+
const dynamicArrayBlocks = [];
|
|
14
|
+
const richValueBlocks = [];
|
|
15
|
+
const cellBlocks = [];
|
|
16
|
+
const valueBlocks = [];
|
|
17
|
+
let container;
|
|
18
|
+
const records = () => container === 'cells'
|
|
19
|
+
? cellBlocks.at(-1)
|
|
20
|
+
: container === 'values'
|
|
21
|
+
? valueBlocks.at(-1)
|
|
22
|
+
: undefined;
|
|
23
|
+
if (xml === '')
|
|
24
|
+
return { typeNames, dynamicArrayBlocks, richValueBlocks, cellBlocks, valueBlocks };
|
|
25
|
+
parseXml(xml, {
|
|
26
|
+
onOpen(name, attrs, selfClosing) {
|
|
27
|
+
switch (localName(name)) {
|
|
28
|
+
case 'metadataType':
|
|
29
|
+
typeNames.push(attrs.name ?? '');
|
|
30
|
+
break;
|
|
31
|
+
case 'futureMetadata':
|
|
32
|
+
container = selfClosing
|
|
33
|
+
? undefined
|
|
34
|
+
: attrs.name === DYNAMIC_ARRAY_TYPE
|
|
35
|
+
? 'dynamicArrays'
|
|
36
|
+
: attrs.name === RICH_VALUE_TYPE
|
|
37
|
+
? 'richValues'
|
|
38
|
+
: undefined;
|
|
39
|
+
break;
|
|
40
|
+
case 'cellMetadata':
|
|
41
|
+
container = selfClosing ? undefined : 'cells';
|
|
42
|
+
break;
|
|
43
|
+
case 'valueMetadata':
|
|
44
|
+
container = selfClosing ? undefined : 'values';
|
|
45
|
+
break;
|
|
46
|
+
case 'bk':
|
|
47
|
+
if (container === 'dynamicArrays')
|
|
48
|
+
dynamicArrayBlocks.push(false);
|
|
49
|
+
else if (container === 'richValues')
|
|
50
|
+
richValueBlocks.push(-1);
|
|
51
|
+
else if (container === 'cells')
|
|
52
|
+
cellBlocks.push([]);
|
|
53
|
+
else if (container === 'values')
|
|
54
|
+
valueBlocks.push([]);
|
|
55
|
+
break;
|
|
56
|
+
case 'dynamicArrayProperties':
|
|
57
|
+
if (container === 'dynamicArrays' && dynamicArrayBlocks.length > 0) {
|
|
58
|
+
dynamicArrayBlocks[dynamicArrayBlocks.length - 1] = boolStrict(attrs.fDynamic);
|
|
59
|
+
}
|
|
60
|
+
break;
|
|
61
|
+
case 'rvb':
|
|
62
|
+
if (container === 'richValues' && richValueBlocks.length > 0) {
|
|
63
|
+
richValueBlocks[richValueBlocks.length - 1] = numInteger(attrs.i, 0) ?? -1;
|
|
64
|
+
}
|
|
65
|
+
break;
|
|
66
|
+
case 'rc': {
|
|
67
|
+
const block = records();
|
|
68
|
+
const type = numInteger(attrs.t, 1);
|
|
69
|
+
const value = numInteger(attrs.v, 0);
|
|
70
|
+
if (block !== undefined && type !== undefined && value !== undefined) {
|
|
71
|
+
block.push({ type, value });
|
|
72
|
+
}
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
default:
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
onClose(name) {
|
|
80
|
+
const local = localName(name);
|
|
81
|
+
if (local === 'futureMetadata' || local === 'cellMetadata' || local === 'valueMetadata') {
|
|
82
|
+
container = undefined;
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
return { typeNames, dynamicArrayBlocks, richValueBlocks, cellBlocks, valueBlocks };
|
|
87
|
+
}
|
|
88
|
+
const METADATA_TYPE_FLAGS = 'minSupportedVersion="120000" copy="1" pasteAll="1" pasteValues="1" merge="1" splitFirst="1" ' +
|
|
89
|
+
'rowColShift="1" clearFormats="1" clearComments="1" assign="1" coerce="1"';
|
|
90
|
+
export class WorkbookMetadataTable {
|
|
91
|
+
#dynamicArrays = false;
|
|
92
|
+
#errors = new Map();
|
|
93
|
+
markDynamicArray() {
|
|
94
|
+
this.#dynamicArrays = true;
|
|
95
|
+
return 1;
|
|
96
|
+
}
|
|
97
|
+
markRichValueError(error) {
|
|
98
|
+
if (!RICH_VALUE_ERROR_TYPES.has(error))
|
|
99
|
+
return undefined;
|
|
100
|
+
const known = this.#errors.get(error);
|
|
101
|
+
if (known !== undefined)
|
|
102
|
+
return known;
|
|
103
|
+
const vm = this.#errors.size + 1;
|
|
104
|
+
this.#errors.set(error, vm);
|
|
105
|
+
return vm;
|
|
106
|
+
}
|
|
107
|
+
get isEmpty() {
|
|
108
|
+
return !this.#dynamicArrays && this.#errors.size === 0;
|
|
109
|
+
}
|
|
110
|
+
get hasRichValues() {
|
|
111
|
+
return this.#errors.size > 0;
|
|
112
|
+
}
|
|
113
|
+
toXml() {
|
|
114
|
+
const dynamic = this.#dynamicArrays;
|
|
115
|
+
const errorCount = this.#errors.size;
|
|
116
|
+
const types = [
|
|
117
|
+
...(dynamic
|
|
118
|
+
? [`<metadataType name="${DYNAMIC_ARRAY_TYPE}" ${METADATA_TYPE_FLAGS} cellMeta="1"/>`]
|
|
119
|
+
: []),
|
|
120
|
+
...(errorCount > 0
|
|
121
|
+
? [`<metadataType name="${RICH_VALUE_TYPE}" ${METADATA_TYPE_FLAGS}/>`]
|
|
122
|
+
: []),
|
|
123
|
+
];
|
|
124
|
+
const richValueType = types.length;
|
|
125
|
+
const indices = Array.from({ length: errorCount }, (_, index) => index);
|
|
126
|
+
return (XML_DECLARATION +
|
|
127
|
+
`<metadata xmlns="${SPREADSHEETML_NS}"` +
|
|
128
|
+
(errorCount > 0 ? ` xmlns:xlrd="${RICH_DATA_NS}"` : '') +
|
|
129
|
+
(dynamic ? ` xmlns:xda="${DYNAMIC_ARRAY_NS}"` : '') +
|
|
130
|
+
'>' +
|
|
131
|
+
`<metadataTypes count="${types.length}">${types.join('')}</metadataTypes>` +
|
|
132
|
+
(dynamic
|
|
133
|
+
? `<futureMetadata name="${DYNAMIC_ARRAY_TYPE}" count="1"><bk><extLst>` +
|
|
134
|
+
`<ext uri="${DYNAMIC_ARRAY_PROPERTIES_EXT_URI}">` +
|
|
135
|
+
'<xda:dynamicArrayProperties fDynamic="1" fCollapsed="0"/>' +
|
|
136
|
+
'</ext></extLst></bk></futureMetadata>'
|
|
137
|
+
: '') +
|
|
138
|
+
(errorCount > 0
|
|
139
|
+
? `<futureMetadata name="${RICH_VALUE_TYPE}" count="${errorCount}">` +
|
|
140
|
+
indices
|
|
141
|
+
.map((index) => `<bk><extLst><ext uri="${RICH_VALUE_BLOCK_EXT_URI}"><xlrd:rvb i="${index}"/></ext></extLst></bk>`)
|
|
142
|
+
.join('') +
|
|
143
|
+
'</futureMetadata>'
|
|
144
|
+
: '') +
|
|
145
|
+
(dynamic ? '<cellMetadata count="1"><bk><rc t="1" v="0"/></bk></cellMetadata>' : '') +
|
|
146
|
+
(errorCount > 0
|
|
147
|
+
? `<valueMetadata count="${errorCount}">` +
|
|
148
|
+
indices.map((index) => `<bk><rc t="${richValueType}" v="${index}"/></bk>`).join('') +
|
|
149
|
+
'</valueMetadata>'
|
|
150
|
+
: '') +
|
|
151
|
+
'</metadata>');
|
|
152
|
+
}
|
|
153
|
+
richValuesXml() {
|
|
154
|
+
const values = [...this.#errors.keys()].map((error) => `<rv s="0"><v>${RICH_VALUE_ERROR_TYPES.get(error)}</v><v>0</v></rv>`);
|
|
155
|
+
return (XML_DECLARATION +
|
|
156
|
+
`<rvData xmlns="${RICH_DATA_NS}" count="${values.length}">${values.join('')}</rvData>`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
export const RICH_VALUE_STRUCTURES_XML = XML_DECLARATION +
|
|
160
|
+
`<rvStructures xmlns="${RICH_DATA_NS}" count="1">` +
|
|
161
|
+
`<s t="${ERROR_STRUCTURE}"><k n="errorType" t="i"/><k n="subType" t="i"/></s>` +
|
|
162
|
+
'</rvStructures>';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type DateEpoch } from '../../core/date.ts';
|
|
2
|
-
import { type CellValue, type FormulaResult, type RichTextRun, type RichTextValue } from '../../core/value.ts';
|
|
2
|
+
import { type CellValue, type ErrorCode, type FormulaResult, type RichTextRun, type RichTextValue } from '../../core/value.ts';
|
|
3
3
|
/**
|
|
4
4
|
* One entry of the shared-strings pool. A `<si>` built from a bare `<t>` is a plain string; a `<si>`
|
|
5
5
|
* built from `<r>` runs is rich text, so a `t="s"` cell can resolve to either kind, and rich text
|
|
@@ -12,8 +12,17 @@ export interface RawCell {
|
|
|
12
12
|
readonly type: string;
|
|
13
13
|
readonly hasFormula: boolean;
|
|
14
14
|
readonly formula: string;
|
|
15
|
+
/** The range an `<f t="array">` fills when it starts at this cell, as Excel spells a `ref`. */
|
|
16
|
+
readonly arrayRef?: string | undefined;
|
|
17
|
+
/** Whether the cell's `cm` points at cell metadata marking a dynamic array. */
|
|
18
|
+
readonly dynamicArray?: boolean;
|
|
19
|
+
/** The error the cell's `vm` names through a rich value, which is the error a `t="e"` cell holds. */
|
|
20
|
+
readonly valueError?: ErrorCode | undefined;
|
|
21
|
+
/** Whether a `<v>` was present, `<v/>` included. */
|
|
15
22
|
readonly hasValue: boolean;
|
|
16
23
|
readonly valueText: string;
|
|
24
|
+
/** Whether an `<is>` was present, `<is/>` included. */
|
|
25
|
+
readonly hasInlineString: boolean;
|
|
17
26
|
readonly inlineText: string;
|
|
18
27
|
/** The formatted runs of a rich inline string, when the `<is>` held `<r>` elements rather than a
|
|
19
28
|
* bare `<t>`. Absent (or empty) for a plain inline string, which decodes to `inlineText`. */
|
|
@@ -21,14 +30,17 @@ export interface RawCell {
|
|
|
21
30
|
}
|
|
22
31
|
/**
|
|
23
32
|
* Decode a gathered cell into its model value. A formula cell becomes a `{formula, result?}`
|
|
24
|
-
* object (the on-disk `_xlfn.`/`_xlpm.` mangling stripped back to the readable name)
|
|
33
|
+
* object (the on-disk `_xlfn.`/`_xlpm.` mangling stripped back to the readable name), or an array
|
|
34
|
+
* formula over its range, dynamic when the cell's metadata says so; a plain
|
|
25
35
|
* numeric cell under a date number format becomes a {@link Date}; everything else decodes by its
|
|
26
36
|
* `t` type. `numFmt` is the cell's resolved number-format code, used only for date detection, and
|
|
27
37
|
* `epoch` the workbook's date system, which is what a serial under such a format counts from.
|
|
38
|
+
* `definedNames` is the workbook's defined names as `definedNameKeys` spells them, which decide whether
|
|
39
|
+
* a function passed as a value sheds its `_xleta.`.
|
|
28
40
|
*/
|
|
29
|
-
export declare function decodeCellContent(raw: RawCell, sharedStrings: readonly SharedString[], numFmt: string | undefined, epoch: DateEpoch): CellValue;
|
|
41
|
+
export declare function decodeCellContent(raw: RawCell, sharedStrings: readonly SharedString[], numFmt: string | undefined, epoch: DateEpoch, definedNames: ReadonlySet<string>): CellValue;
|
|
30
42
|
/** Decode a formula's cached `<v>` result by its `t` type, coercing a numeric result under a date
|
|
31
43
|
* `numFmt` to a {@link Date} exactly as a bare numeric cell is, so a date-valued formula result
|
|
32
44
|
* (e.g. `TODAY()`) reads back as a Date, not a serial. Shared by the buffered reader's shared-formula
|
|
33
45
|
* clone resolution, which caches a result the same way a plain formula cell does. */
|
|
34
|
-
export declare function decodeFormulaResult(type: string, valueText: string, numFmt: string | undefined, epoch: DateEpoch): FormulaResult | undefined;
|
|
46
|
+
export declare function decodeFormulaResult(type: string, valueText: string, numFmt: string | undefined, epoch: DateEpoch, valueError?: ErrorCode): FormulaResult | undefined;
|
|
@@ -2,55 +2,72 @@ import { coerceDateSerial, parseDateText } from '../../core/date.js';
|
|
|
2
2
|
import { unmangleFunctions } from '../../core/formula.js';
|
|
3
3
|
import { isErrorCode, } from '../../core/value.js';
|
|
4
4
|
import { decodeSpreadsheetText, numFinite, numInteger } from '../../xml/xml-attrs.js';
|
|
5
|
-
import {
|
|
6
|
-
export function decodeCellContent(raw, sharedStrings, numFmt, epoch) {
|
|
5
|
+
import { boolTristate } from '../../xml/xml-scan.js';
|
|
6
|
+
export function decodeCellContent(raw, sharedStrings, numFmt, epoch, definedNames) {
|
|
7
7
|
if (raw.hasFormula) {
|
|
8
|
-
const stored = unmangleFunctions(raw.formula);
|
|
8
|
+
const stored = unmangleFunctions(raw.formula, definedNames);
|
|
9
9
|
const result = raw.hasValue
|
|
10
|
-
? decodeFormulaResult(raw.type, raw.valueText, numFmt, epoch)
|
|
10
|
+
? decodeFormulaResult(raw.type, raw.valueText, numFmt, epoch, raw.valueError)
|
|
11
11
|
: undefined;
|
|
12
|
-
|
|
12
|
+
const cached = result === undefined ? {} : { result };
|
|
13
|
+
if (raw.arrayRef !== undefined) {
|
|
14
|
+
return {
|
|
15
|
+
shareType: 'array',
|
|
16
|
+
formula: stored,
|
|
17
|
+
ref: raw.arrayRef,
|
|
18
|
+
...(raw.dynamicArray === true ? { dynamic: true } : {}),
|
|
19
|
+
...cached,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
return { formula: stored, ...cached };
|
|
13
23
|
}
|
|
14
24
|
if (raw.type === 'inlineStr' && raw.richTextRuns !== undefined && raw.richTextRuns.length > 0) {
|
|
15
25
|
return { richText: raw.richTextRuns };
|
|
16
26
|
}
|
|
17
|
-
const value = decodeValue(raw
|
|
27
|
+
const value = decodeValue(raw, sharedStrings);
|
|
18
28
|
return coerceDateSerial(value, numFmt, epoch);
|
|
19
29
|
}
|
|
20
|
-
function decodeValue(
|
|
21
|
-
|
|
30
|
+
function decodeValue(raw, sharedStrings) {
|
|
31
|
+
const { valueText } = raw;
|
|
32
|
+
switch (raw.type) {
|
|
22
33
|
case 'inlineStr':
|
|
23
|
-
return inlineText;
|
|
34
|
+
return raw.hasInlineString ? raw.inlineText : null;
|
|
24
35
|
case 'str':
|
|
25
|
-
return decodeSpreadsheetText(valueText);
|
|
36
|
+
return raw.hasValue ? decodeSpreadsheetText(valueText) : null;
|
|
26
37
|
case 'd':
|
|
27
38
|
return parseDateText(valueText);
|
|
28
39
|
case 's': {
|
|
29
40
|
const index = numInteger(valueText, 0);
|
|
30
|
-
return index === undefined ?
|
|
41
|
+
return index === undefined ? null : (sharedStrings[index] ?? null);
|
|
31
42
|
}
|
|
32
43
|
case 'b':
|
|
33
|
-
return
|
|
44
|
+
return boolTristate(valueText) ?? null;
|
|
34
45
|
case 'e':
|
|
35
|
-
|
|
36
|
-
default: {
|
|
37
|
-
if (!hasValue)
|
|
46
|
+
if (valueText === '')
|
|
38
47
|
return null;
|
|
48
|
+
return errorOf(valueText, raw.valueError);
|
|
49
|
+
default:
|
|
39
50
|
return numFinite(valueText) ?? null;
|
|
40
|
-
}
|
|
41
51
|
}
|
|
42
52
|
}
|
|
43
|
-
export function decodeFormulaResult(type, valueText, numFmt, epoch) {
|
|
44
|
-
return coerceDateSerial(decodeResult(type, valueText), numFmt, epoch);
|
|
53
|
+
export function decodeFormulaResult(type, valueText, numFmt, epoch, valueError) {
|
|
54
|
+
return coerceDateSerial(decodeResult(type, valueText, valueError), numFmt, epoch);
|
|
55
|
+
}
|
|
56
|
+
function errorOf(valueText, valueError) {
|
|
57
|
+
if (valueError !== undefined)
|
|
58
|
+
return { error: valueError };
|
|
59
|
+
return isErrorCode(valueText) ? { error: valueText } : valueText;
|
|
45
60
|
}
|
|
46
|
-
function decodeResult(type, valueText) {
|
|
61
|
+
function decodeResult(type, valueText, valueError) {
|
|
47
62
|
switch (type) {
|
|
48
63
|
case 'str':
|
|
49
64
|
return decodeSpreadsheetText(valueText);
|
|
50
65
|
case 'b':
|
|
51
|
-
return
|
|
66
|
+
return boolTristate(valueText);
|
|
52
67
|
case 'e':
|
|
53
|
-
|
|
68
|
+
if (valueText === '')
|
|
69
|
+
return undefined;
|
|
70
|
+
return errorOf(valueText, valueError);
|
|
54
71
|
default:
|
|
55
72
|
return numFinite(valueText);
|
|
56
73
|
}
|