@shbernal/ts-xlsx 2.0.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -10
- package/dist/bytes.d.ts +24 -0
- package/dist/bytes.js +34 -0
- package/dist/core/address.d.ts +26 -4
- package/dist/core/address.js +21 -9
- package/dist/core/autofilter.d.ts +12 -1
- package/dist/core/autofilter.js +35 -12
- package/dist/core/axis-handle.d.ts +8 -0
- package/dist/core/axis-handle.js +14 -0
- package/dist/core/cell.d.ts +24 -0
- package/dist/core/cell.js +9 -1
- package/dist/core/color-resolution.d.ts +1 -1
- package/dist/core/color-resolution.js +7 -8
- package/dist/core/column.d.ts +4 -1
- package/dist/core/column.js +31 -35
- package/dist/core/conditional-formatting-overlay.d.ts +5 -0
- package/dist/core/conditional-formatting-overlay.js +11 -0
- package/dist/core/conditional-formatting.d.ts +45 -5
- package/dist/core/conditional-formatting.js +74 -0
- package/dist/core/data-validation-overlay.d.ts +11 -0
- package/dist/core/data-validation-overlay.js +21 -2
- package/dist/core/data-validation.d.ts +9 -2
- package/dist/core/data-validation.js +26 -0
- package/dist/core/grid-edits.d.ts +16 -0
- package/dist/core/grid-edits.js +26 -8
- package/dist/core/grid-shift.d.ts +19 -0
- package/dist/core/grid-shift.js +8 -0
- package/dist/core/image.d.ts +2 -0
- package/dist/core/image.js +2 -0
- package/dist/core/merge-index.d.ts +31 -0
- package/dist/core/merge-index.js +60 -0
- package/dist/core/merge.d.ts +12 -11
- package/dist/core/merge.js +36 -10
- package/dist/core/page-setup.d.ts +86 -2
- package/dist/core/page-setup.js +34 -1
- package/dist/core/pivot-table.d.ts +2 -0
- package/dist/core/pivot-table.js +23 -18
- package/dist/core/protection.d.ts +3 -0
- package/dist/core/protection.js +20 -14
- package/dist/core/row-input.d.ts +7 -0
- package/dist/core/row-input.js +10 -8
- package/dist/core/row.d.ts +4 -1
- package/dist/core/row.js +19 -23
- package/dist/core/style.d.ts +78 -7
- package/dist/core/style.js +90 -80
- package/dist/core/table.d.ts +26 -12
- package/dist/core/table.js +67 -33
- package/dist/core/theme.d.ts +3 -1
- package/dist/core/theme.js +4 -0
- package/dist/core/used-extent.d.ts +44 -0
- package/dist/core/used-extent.js +102 -0
- package/dist/core/workbook.d.ts +2 -2
- package/dist/core/worksheet-comments.d.ts +8 -0
- package/dist/core/worksheet-comments.js +18 -0
- package/dist/core/worksheet.d.ts +31 -7
- package/dist/core/worksheet.js +72 -86
- package/dist/customui/ribbon.js +23 -24
- package/dist/entries/core.d.ts +3 -3
- package/dist/entries/csv.d.ts +1 -1
- package/dist/entries/node-unavailable.d.ts +15 -0
- package/dist/entries/node-unavailable.js +20 -0
- package/dist/entries/node.d.ts +1 -0
- package/dist/entries/node.js +1 -0
- package/dist/entries/xlsx.d.ts +0 -1
- package/dist/entries/xlsx.js +0 -1
- package/dist/io/csv/read.js +1 -1
- package/dist/io/csv/write.d.ts +12 -2
- package/dist/io/csv/write.js +17 -2
- package/dist/io/opc/inflate.js +3 -13
- package/dist/io/opc/part-paths.d.ts +1 -0
- package/dist/io/opc/part-paths.js +15 -0
- package/dist/io/opc/read-opc.d.ts +22 -3
- package/dist/io/opc/read-opc.js +11 -39
- package/dist/io/opc/rels.d.ts +12 -6
- package/dist/io/opc/rels.js +3 -5
- package/dist/io/style/xf-style.d.ts +33 -4
- package/dist/io/style/xf-style.js +12 -1
- package/dist/io/xlsb/read-styles.js +6 -3
- package/dist/io/xlsb/read.d.ts +1 -1
- package/dist/io/xlsb/read.js +5 -8
- package/dist/io/xlsx/cell-accumulator.d.ts +2 -4
- package/dist/io/xlsx/cell-accumulator.js +20 -58
- package/dist/io/xlsx/cell-value.d.ts +1 -1
- package/dist/io/xlsx/cell-value.js +7 -4
- package/dist/io/xlsx/color-xml.d.ts +1 -1
- package/dist/io/xlsx/color-xml.js +8 -7
- package/dist/io/xlsx/comments.js +6 -3
- package/dist/io/xlsx/conditional-formatting.d.ts +4 -3
- package/dist/io/xlsx/conditional-formatting.js +81 -65
- package/dist/io/xlsx/data-validation.d.ts +10 -8
- package/dist/io/xlsx/data-validation.js +44 -41
- package/dist/io/xlsx/edit-vba.js +4 -11
- package/dist/io/xlsx/hyperlinks.d.ts +4 -2
- package/dist/io/xlsx/hyperlinks.js +26 -31
- package/dist/io/xlsx/images.js +33 -35
- package/dist/io/xlsx/package-plan.d.ts +9 -1
- package/dist/io/xlsx/package-plan.js +29 -16
- package/dist/io/xlsx/part-names.d.ts +41 -0
- package/dist/io/xlsx/part-names.js +46 -0
- package/dist/io/xlsx/read-pivot.js +4 -9
- package/dist/io/xlsx/read-rows.js +12 -13
- package/dist/io/xlsx/read-shared-strings.js +9 -47
- package/dist/io/xlsx/read-styles.d.ts +1 -1
- package/dist/io/xlsx/read-styles.js +65 -43
- package/dist/io/xlsx/read-worksheet.d.ts +10 -1
- package/dist/io/xlsx/read-worksheet.js +48 -63
- package/dist/io/xlsx/read.d.ts +3 -4
- package/dist/io/xlsx/read.js +124 -146
- package/dist/io/xlsx/rich-runs.d.ts +41 -7
- package/dist/io/xlsx/rich-runs.js +95 -30
- package/dist/io/xlsx/sheet-properties.d.ts +1 -1
- package/dist/io/xlsx/sheet-properties.js +23 -26
- package/dist/io/xlsx/styles.d.ts +3 -15
- package/dist/io/xlsx/styles.js +83 -105
- package/dist/io/xlsx/tables.js +14 -13
- package/dist/io/xlsx/theme-xml.js +8 -5
- package/dist/io/xlsx/threaded-comments.js +20 -19
- package/dist/io/xlsx/workbook-xml.js +43 -36
- package/dist/io/xlsx/worksheet-xml.d.ts +1 -1
- package/dist/io/xlsx/worksheet-xml.js +47 -52
- package/dist/io/xlsx/write-stream.d.ts +14 -1
- package/dist/io/xlsx/write-stream.js +40 -33
- package/dist/io/xlsx/write.d.ts +5 -0
- package/dist/io/xlsx/write.js +155 -131
- package/dist/sha512.d.ts +2 -0
- package/dist/sha512.js +146 -0
- package/dist/token-set.d.ts +15 -0
- package/dist/token-set.js +4 -0
- package/dist/vba/bytes.d.ts +1 -2
- package/dist/vba/bytes.js +1 -12
- package/dist/vba/cfb-format.d.ts +24 -0
- package/dist/vba/cfb-format.js +12 -0
- package/dist/vba/cfb-writer.js +17 -17
- package/dist/vba/cfb.js +1 -7
- package/dist/vba/dir-records.d.ts +53 -0
- package/dist/vba/dir-records.js +28 -0
- package/dist/vba/ms-ovba.js +32 -11
- package/dist/vba/project-editor.js +5 -34
- package/dist/vba/project.js +15 -31
- package/dist/vba/vba-encoding.js +1 -1
- package/dist/xml/xml-read.d.ts +117 -72
- package/dist/xml/xml-read.js +123 -169
- package/dist/xml/xml-scan.d.ts +100 -0
- package/dist/xml/xml-scan.js +208 -0
- package/dist/xml/xml.d.ts +44 -2
- package/dist/xml/xml.js +17 -5
- package/package.json +22 -6
|
@@ -2,3 +2,4 @@ export declare const THEME_PART_PATH = "xl/theme/theme1.xml";
|
|
|
2
2
|
export declare function extensionOf(partPath: string): string;
|
|
3
3
|
export declare function relsPathFor(partPath: string): string;
|
|
4
4
|
export declare function relativePartPath(fromPath: string, toPath: string): string;
|
|
5
|
+
export declare function resolveRelativePart(basePart: string, target: string): string;
|
|
@@ -22,3 +22,18 @@ export function relativePartPath(fromPath, toPath) {
|
|
|
22
22
|
const up = fromDir.length - common;
|
|
23
23
|
return [...Array(up).fill('..'), ...toSegments.slice(common)].join('/');
|
|
24
24
|
}
|
|
25
|
+
export function resolveRelativePart(basePart, target) {
|
|
26
|
+
if (target.startsWith('/'))
|
|
27
|
+
return target.slice(1);
|
|
28
|
+
const baseDir = basePart.slice(0, basePart.lastIndexOf('/') + 1);
|
|
29
|
+
const out = [];
|
|
30
|
+
for (const segment of `${baseDir}${target}`.split('/')) {
|
|
31
|
+
if (segment === '' || segment === '.')
|
|
32
|
+
continue;
|
|
33
|
+
if (segment === '..')
|
|
34
|
+
out.pop();
|
|
35
|
+
else
|
|
36
|
+
out.push(segment);
|
|
37
|
+
}
|
|
38
|
+
return out.join('/');
|
|
39
|
+
}
|
|
@@ -6,12 +6,31 @@ export interface PackageAccessors {
|
|
|
6
6
|
/** A part's raw bytes, or undefined when the package holds no such part. */
|
|
7
7
|
partBytes: (path: string) => Uint8Array | undefined;
|
|
8
8
|
}
|
|
9
|
+
/** A spreadsheet package opened for reading: inflated under the read bound, its parts bound to
|
|
10
|
+
* accessors, and its XML office document read if it has one. */
|
|
11
|
+
export interface OpenedSpreadsheet {
|
|
12
|
+
/** The inflated parts, for a reader that hands the whole package on to another codec. */
|
|
13
|
+
readonly files: Record<string, Uint8Array>;
|
|
14
|
+
readonly pkg: PackageAccessors;
|
|
15
|
+
/** `xl/workbook.xml`, or undefined when the package carries no XML office document (a `.xlsb`
|
|
16
|
+
* carries `xl/workbook.bin` instead). Each entry point answers that case for itself: they
|
|
17
|
+
* genuinely want different answers, and the difference is documented where they diverge. */
|
|
18
|
+
readonly workbookXml: string | undefined;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Open a spreadsheet package: the six-step preamble every reader shares, and in particular the two
|
|
22
|
+
* decisions worth having exactly one of. The inflate bound is a security decision (an unbounded
|
|
23
|
+
* inflate is a zip bomb) and "does this package carry an XML office document" is the dispatch
|
|
24
|
+
* decision between the two codecs, so neither should be restated once per entry point.
|
|
25
|
+
*
|
|
26
|
+
* It deliberately stops short of the shared strings and the style table: those are codec-specific
|
|
27
|
+
* (`xl/sharedStrings.xml` against BIFF12's own record stream), and lifting them here would put
|
|
28
|
+
* SpreadsheetML knowledge into the container layer.
|
|
29
|
+
*/
|
|
30
|
+
export declare function openSpreadsheetPackage(data: Uint8Array, maxUncompressedBytes: number | undefined): OpenedSpreadsheet;
|
|
9
31
|
export declare function packageAccessors(files: Record<string, Uint8Array>): PackageAccessors;
|
|
10
32
|
export declare function relationshipTargetByType(xml: string, suffix: string): string | undefined;
|
|
11
33
|
export declare function relationshipTargetsByType(xml: string, suffix: string): string[];
|
|
12
|
-
export declare function resolveRelativePart(basePart: string, target: string): string;
|
|
13
|
-
export declare function resolveWorkbookPart(target: string): string;
|
|
14
|
-
export declare function parseRelationships(xml: string): Map<string, string>;
|
|
15
34
|
export interface RelationshipRecord {
|
|
16
35
|
readonly id: string;
|
|
17
36
|
readonly type: string;
|
package/dist/io/opc/read-opc.js
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { strFromU8 } from 'fflate';
|
|
2
2
|
import { openElements } from '../../xml/xml-read.js';
|
|
3
|
-
import { extensionOf, relsPathFor } from './part-paths.js';
|
|
3
|
+
import { extensionOf, relsPathFor, resolveRelativePart } from './part-paths.js';
|
|
4
|
+
import { DEFAULT_MAX_UNCOMPRESSED } from './read-options.js';
|
|
5
|
+
import { inflateSpreadsheetPackage } from './sniff-format.js';
|
|
6
|
+
export function openSpreadsheetPackage(data, maxUncompressedBytes) {
|
|
7
|
+
const files = inflateSpreadsheetPackage(data, maxUncompressedBytes ?? DEFAULT_MAX_UNCOMPRESSED);
|
|
8
|
+
const pkg = packageAccessors(files);
|
|
9
|
+
return { files, pkg, workbookXml: pkg.partText('xl/workbook.xml') };
|
|
10
|
+
}
|
|
4
11
|
export function packageAccessors(files) {
|
|
5
12
|
return {
|
|
6
13
|
partText: (path) => {
|
|
@@ -10,48 +17,13 @@ export function packageAccessors(files) {
|
|
|
10
17
|
partBytes: (path) => files[path],
|
|
11
18
|
};
|
|
12
19
|
}
|
|
13
|
-
function matchesType(attrs, suffix) {
|
|
14
|
-
return (attrs.Type !== undefined && attrs.Target !== undefined && attrs.Type.endsWith(`/${suffix}`));
|
|
15
|
-
}
|
|
16
20
|
export function relationshipTargetByType(xml, suffix) {
|
|
17
21
|
return relationshipTargetsByType(xml, suffix)[0];
|
|
18
22
|
}
|
|
19
23
|
export function relationshipTargetsByType(xml, suffix) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
targets.push(attrs.Target);
|
|
24
|
-
}
|
|
25
|
-
return targets;
|
|
26
|
-
}
|
|
27
|
-
export function resolveRelativePart(basePart, target) {
|
|
28
|
-
if (target.startsWith('/'))
|
|
29
|
-
return target.slice(1);
|
|
30
|
-
const baseDir = basePart.slice(0, basePart.lastIndexOf('/') + 1);
|
|
31
|
-
const out = [];
|
|
32
|
-
for (const segment of `${baseDir}${target}`.split('/')) {
|
|
33
|
-
if (segment === '' || segment === '.')
|
|
34
|
-
continue;
|
|
35
|
-
if (segment === '..')
|
|
36
|
-
out.pop();
|
|
37
|
-
else
|
|
38
|
-
out.push(segment);
|
|
39
|
-
}
|
|
40
|
-
return out.join('/');
|
|
41
|
-
}
|
|
42
|
-
export function resolveWorkbookPart(target) {
|
|
43
|
-
if (target.startsWith('/'))
|
|
44
|
-
return target.slice(1);
|
|
45
|
-
return `xl/${target.replace(/^\.\//, '')}`;
|
|
46
|
-
}
|
|
47
|
-
export function parseRelationships(xml) {
|
|
48
|
-
const rels = new Map();
|
|
49
|
-
for (const { attrs } of openElements(xml, 'Relationship')) {
|
|
50
|
-
if (attrs.Id !== undefined && attrs.Target !== undefined) {
|
|
51
|
-
rels.set(attrs.Id, attrs.Target);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
return rels;
|
|
24
|
+
return parseRelationshipRecords(xml)
|
|
25
|
+
.filter((record) => record.type.endsWith(`/${suffix}`))
|
|
26
|
+
.map((record) => record.target);
|
|
55
27
|
}
|
|
56
28
|
export function parseRelationshipRecords(xml) {
|
|
57
29
|
const records = [];
|
package/dist/io/opc/rels.d.ts
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A single `<Relationship>`. An `external` target lives outside the package (a hyperlink URL), so the
|
|
3
|
+
* element carries `TargetMode="External"`; a package-internal target (the default) omits it.
|
|
4
|
+
*
|
|
5
|
+
* All three values are escaped here, unconditionally. The obligation used to sit in a comment saying
|
|
6
|
+
* the caller escaped a target it did not control, which made the safety of this function a property
|
|
7
|
+
* of its call sites rather than of itself, on the one boundary in the tree where `xml.ts` says that is
|
|
8
|
+
* explicitly not how it is done. Escaping a writer-controlled package path is the identity, so the
|
|
9
|
+
* generated chains emit the same bytes and the guarantee holds by construction instead of by
|
|
10
|
+
* inspecting every caller. `TargetMode` stays a fixed token, not a value.
|
|
11
|
+
*/
|
|
1
12
|
export declare function relationship(id: string, type: string, target: string, options?: {
|
|
2
13
|
external?: boolean;
|
|
3
14
|
}): string;
|
|
4
15
|
export declare function relationshipsPart(relationships: readonly string[]): string;
|
|
5
|
-
export declare function preservedRelsXml(rels: readonly {
|
|
6
|
-
id: string;
|
|
7
|
-
type: string;
|
|
8
|
-
target: string;
|
|
9
|
-
external?: boolean;
|
|
10
|
-
}[]): string;
|
|
11
16
|
export declare function relsPartXml(rels: readonly {
|
|
12
17
|
id: string;
|
|
13
18
|
type: string;
|
|
14
19
|
target: string;
|
|
20
|
+
external?: boolean;
|
|
15
21
|
}[]): string;
|
package/dist/io/opc/rels.js
CHANGED
|
@@ -2,14 +2,12 @@ import { escapeAttr, XML_DECLARATION } from '../../xml/xml.js';
|
|
|
2
2
|
import { PKG_RELS_NS } from './namespaces.js';
|
|
3
3
|
export function relationship(id, type, target, options) {
|
|
4
4
|
const mode = options?.external ? ' TargetMode="External"' : '';
|
|
5
|
-
return `<Relationship Id="${id}" Type="${type}"
|
|
5
|
+
return (`<Relationship Id="${escapeAttr(id)}" Type="${escapeAttr(type)}" ` +
|
|
6
|
+
`Target="${escapeAttr(target)}"${mode}/>`);
|
|
6
7
|
}
|
|
7
8
|
export function relationshipsPart(relationships) {
|
|
8
9
|
return `${XML_DECLARATION}<Relationships xmlns="${PKG_RELS_NS}">${relationships.join('')}</Relationships>`;
|
|
9
10
|
}
|
|
10
|
-
export function preservedRelsXml(rels) {
|
|
11
|
-
return relationshipsPart(rels.map((rel) => relationship(rel.id, rel.type, escapeAttr(rel.target), rel.external ? { external: true } : {})));
|
|
12
|
-
}
|
|
13
11
|
export function relsPartXml(rels) {
|
|
14
|
-
return relationshipsPart(rels.map((rel) => relationship(rel.id, rel.type, rel.target)));
|
|
12
|
+
return relationshipsPart(rels.map((rel) => relationship(rel.id, rel.type, rel.target, rel.external ? { external: true } : {})));
|
|
15
13
|
}
|
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
import { type Cell } from '../../core/cell.ts';
|
|
2
|
-
import { type Border, type CellStyle, type Fill, type Font, type NamedCellStyle } from '../../core/style.ts';
|
|
2
|
+
import { type Border, type CellStyle, type Fill, type Font, type NamedCellStyle, type TableStyleTable } 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
|
-
*
|
|
9
|
+
* every codec 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
|
+
*
|
|
12
|
+
* Read-only throughout: an xf is a resolved snapshot, assembled once and then only consulted. Both
|
|
13
|
+
* readers already build theirs through an explicitly-mutable draft (`{-readonly [K in keyof
|
|
14
|
+
* XfStyle]?: …}`), so the constraint costs nothing and stops a consumer editing a table entry that
|
|
15
|
+
* other cells share.
|
|
11
16
|
*/
|
|
12
|
-
export interface XfStyle extends CellStyle {
|
|
17
|
+
export interface XfStyle extends Readonly<CellStyle> {
|
|
13
18
|
readonly quotePrefix?: boolean;
|
|
14
19
|
/** The `xfId` link into the named-style layer (`cellStyleXfs`); absent for the Normal default (0). */
|
|
15
20
|
readonly xfId?: number;
|
|
@@ -28,7 +33,31 @@ export interface StyleTable {
|
|
|
28
33
|
* file declares no font table.
|
|
29
34
|
*/
|
|
30
35
|
readonly defaultFont?: Font;
|
|
36
|
+
/**
|
|
37
|
+
* The stylesheet's preserved sub-tables, captured verbatim in the same scan that read the xfs: the
|
|
38
|
+
* differential styles a conditional format's `dxfId` indexes, a custom indexed palette, the
|
|
39
|
+
* author's recent-colour swatches, and the custom table-style definitions.
|
|
40
|
+
*
|
|
41
|
+
* They ride here rather than being extracted separately because they come out of the same part,
|
|
42
|
+
* and the reader that wanted them four more times over that part is what this replaced. Each is
|
|
43
|
+
* raw source text: re-parsing and re-serialising is exactly what would cost a foreign `<dxf>` its
|
|
44
|
+
* number format on a round trip.
|
|
45
|
+
*/
|
|
46
|
+
readonly preserved: PreservedStyleTables;
|
|
47
|
+
}
|
|
48
|
+
/** The stylesheet content the model does not interpret and re-emits byte for byte. */
|
|
49
|
+
export interface PreservedStyleTables {
|
|
50
|
+
readonly dxfs: readonly string[];
|
|
51
|
+
readonly indexedColors: readonly string[];
|
|
52
|
+
readonly mruColors: readonly string[];
|
|
53
|
+
readonly tableStyles: TableStyleTable;
|
|
31
54
|
}
|
|
55
|
+
/** The preserved sub-tables of a stylesheet that carries none: a package with no `styles.xml`, and
|
|
56
|
+
* the BIFF12 stylesheet, whose binary records hold no verbatim XML there would be anything to keep. */
|
|
57
|
+
export declare const NO_PRESERVED_STYLE_TABLES: PreservedStyleTables;
|
|
58
|
+
/** The xf layer of a stylesheet: what {@link resolveStyleTable} assembles out of the format tables,
|
|
59
|
+
* before the verbatim sub-tables that ride alongside it in a {@link StyleTable}. */
|
|
60
|
+
export type ResolvedXfTables = Omit<StyleTable, 'preserved'>;
|
|
32
61
|
/**
|
|
33
62
|
* The format code a number-format id denotes: the file's own `<numFmt>`/`BrtFmt` declaration if it
|
|
34
63
|
* has one, else the built-in Excel defines for that id. Id 0 is General, the absence of a format,
|
|
@@ -76,4 +105,4 @@ export declare function resolveStyleTable(tables: {
|
|
|
76
105
|
readonly namedXfs: ReadonlyArray<XfStyle>;
|
|
77
106
|
readonly labels: ReadonlyArray<StyleLabel>;
|
|
78
107
|
readonly fonts: ReadonlyArray<Font | undefined>;
|
|
79
|
-
}):
|
|
108
|
+
}): ResolvedXfTables;
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { applyCellStyle } from '../../core/cell.js';
|
|
2
2
|
import { NAMED_STYLE_ID } from '../../core/internal.js';
|
|
3
3
|
import { assignStyleFacets, } from '../../core/style.js';
|
|
4
|
+
export const NO_PRESERVED_STYLE_TABLES = {
|
|
5
|
+
dxfs: [],
|
|
6
|
+
indexedColors: [],
|
|
7
|
+
mruColors: [],
|
|
8
|
+
tableStyles: { styles: [] },
|
|
9
|
+
};
|
|
4
10
|
const BUILTIN_NUMFMTS = new Map([
|
|
5
11
|
[1, '0'],
|
|
6
12
|
[2, '0.00'],
|
|
@@ -71,8 +77,13 @@ export function resolveStyleTable(tables) {
|
|
|
71
77
|
const named = namedXfs[xf.xfId];
|
|
72
78
|
return named === undefined ? xf : { ...named, ...xf };
|
|
73
79
|
});
|
|
80
|
+
const labelsByXfId = new Map();
|
|
81
|
+
for (const label of labels) {
|
|
82
|
+
if (!labelsByXfId.has(label.xfId))
|
|
83
|
+
labelsByXfId.set(label.xfId, label);
|
|
84
|
+
}
|
|
74
85
|
const namedStyles = namedXfs.map((xf, index) => {
|
|
75
|
-
const label =
|
|
86
|
+
const label = labelsByXfId.get(index);
|
|
76
87
|
const style = {};
|
|
77
88
|
assignStyleFacets(style, xf);
|
|
78
89
|
if (label?.name !== undefined)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { numFmtCodeFor, resolveStyleTable, } from '../style/xf-style.js';
|
|
1
|
+
import { numFmtCodeFor, NO_PRESERVED_STYLE_TABLES, resolveStyleTable, } from '../style/xf-style.js';
|
|
2
2
|
import { RecordReader } from './primitives.js';
|
|
3
3
|
import { readRecords } from './record-stream.js';
|
|
4
4
|
import { BRT } from './record-types.js';
|
|
@@ -22,7 +22,7 @@ const COLLECTION_ENDS = new Set([
|
|
|
22
22
|
]);
|
|
23
23
|
export function parseStyleTable(part) {
|
|
24
24
|
if (part === undefined)
|
|
25
|
-
return { cellXfs: [], namedStyles: [] };
|
|
25
|
+
return { cellXfs: [], namedStyles: [], preserved: NO_PRESERVED_STYLE_TABLES };
|
|
26
26
|
const numFmtCodes = new Map();
|
|
27
27
|
const fonts = [];
|
|
28
28
|
const fills = [];
|
|
@@ -73,7 +73,10 @@ export function parseStyleTable(part) {
|
|
|
73
73
|
break;
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
|
-
return
|
|
76
|
+
return {
|
|
77
|
+
...resolveStyleTable({ directXfs, namedXfs, labels, fonts }),
|
|
78
|
+
preserved: NO_PRESERVED_STYLE_TABLES,
|
|
79
|
+
};
|
|
77
80
|
}
|
|
78
81
|
function readXf(reader, deps, isDirect) {
|
|
79
82
|
const parent = reader.u16();
|
package/dist/io/xlsb/read.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Workbook } from '../../core/workbook.ts';
|
|
2
|
-
import {
|
|
2
|
+
import type { ReadXlsxOptions } from '../opc/read-options.ts';
|
|
3
3
|
/** The office-document part every `.xlsb` package is entered through. */
|
|
4
4
|
export declare const XLSB_WORKBOOK_PART = "xl/workbook.bin";
|
|
5
5
|
/**
|
package/dist/io/xlsb/read.js
CHANGED
|
@@ -2,9 +2,7 @@ import { unmangleFunctions } from '../../core/formula.js';
|
|
|
2
2
|
import { INTERNAL } from '../../core/internal.js';
|
|
3
3
|
import { Workbook } from '../../core/workbook.js';
|
|
4
4
|
import { UnsupportedFormatError } from '../opc/errors.js';
|
|
5
|
-
import {
|
|
6
|
-
import { DEFAULT_MAX_UNCOMPRESSED } from '../opc/read-options.js';
|
|
7
|
-
import { inflateSpreadsheetPackage } from '../opc/sniff-format.js';
|
|
5
|
+
import { openSpreadsheetPackage, packageAccessors, readPartRelationships } from '../opc/read-opc.js';
|
|
8
6
|
import { decodeFormula } from './formula.js';
|
|
9
7
|
import { RecordReader } from './primitives.js';
|
|
10
8
|
import { parseSharedStrings } from './read-shared-strings.js';
|
|
@@ -14,8 +12,7 @@ import { readRecords } from './record-stream.js';
|
|
|
14
12
|
import { BRT } from './record-types.js';
|
|
15
13
|
export const XLSB_WORKBOOK_PART = 'xl/workbook.bin';
|
|
16
14
|
export function readXlsb(data, options = {}) {
|
|
17
|
-
|
|
18
|
-
return readXlsbPackage(inflateSpreadsheetPackage(data, cap));
|
|
15
|
+
return readXlsbPackage(openSpreadsheetPackage(data, options.maxUncompressedBytes).files);
|
|
19
16
|
}
|
|
20
17
|
export function readXlsbPackage(files) {
|
|
21
18
|
const { partText, partBytes } = packageAccessors(files);
|
|
@@ -23,7 +20,7 @@ export function readXlsbPackage(files) {
|
|
|
23
20
|
if (workbookPart === undefined) {
|
|
24
21
|
throw new UnsupportedFormatError('unknown', `not a valid .xlsb package: ${XLSB_WORKBOOK_PART} is missing`);
|
|
25
22
|
}
|
|
26
|
-
const rels =
|
|
23
|
+
const rels = readPartRelationships(XLSB_WORKBOOK_PART, partText);
|
|
27
24
|
const sharedStrings = parseSharedStrings(partBytes('xl/sharedStrings.bin'));
|
|
28
25
|
const { cellXfs, namedStyles, defaultFont } = parseStyleTable(partBytes('xl/styles.bin'));
|
|
29
26
|
const workbook = new Workbook();
|
|
@@ -39,8 +36,8 @@ export function readXlsbPackage(files) {
|
|
|
39
36
|
};
|
|
40
37
|
for (const declared of declaration.sheets) {
|
|
41
38
|
const sheet = workbook.addWorksheet(declared.name, { state: declared.state });
|
|
42
|
-
const target = declared.relId === undefined ? undefined : rels.
|
|
43
|
-
const part = target === undefined ? undefined : partBytes(
|
|
39
|
+
const target = declared.relId === undefined ? undefined : rels.byId(declared.relId)?.target;
|
|
40
|
+
const part = target === undefined ? undefined : partBytes(rels.pathOf(target));
|
|
44
41
|
if (part !== undefined)
|
|
45
42
|
parseWorksheet(part, sheet, sharedStrings, cellXfs, scope);
|
|
46
43
|
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import type { CellValue } from '../../core/value.ts';
|
|
2
2
|
import type { Worksheet } from '../../core/worksheet.ts';
|
|
3
|
-
import { type XmlAttributes } from '../../xml/xml-
|
|
3
|
+
import { type XmlAttributes } from '../../xml/xml-scan.ts';
|
|
4
4
|
import { type XfStyle } from '../style/xf-style.ts';
|
|
5
5
|
import { type SharedString } from './cell-value.ts';
|
|
6
|
-
import { RunAccumulator } from './rich-runs.ts';
|
|
7
6
|
export declare class CellAccumulator {
|
|
8
7
|
#private;
|
|
9
8
|
constructor(options: {
|
|
@@ -15,8 +14,6 @@ export declare class CellAccumulator {
|
|
|
15
14
|
get styleIndex(): number;
|
|
16
15
|
/** This cell's 1-based column, or -1 when its address was absent or unparseable. */
|
|
17
16
|
get col(): number;
|
|
18
|
-
/** The rich-text run accumulator, driven by the surrounding parser's `<r>`/`<rPr>` handling. */
|
|
19
|
-
get runs(): RunAccumulator;
|
|
20
17
|
/**
|
|
21
18
|
* The text gathered since the current element opened. A caller reads it for the elements it
|
|
22
19
|
* captures itself (see {@link capture}); the machine reads it for its own.
|
|
@@ -40,5 +37,6 @@ export declare class CellAccumulator {
|
|
|
40
37
|
*/
|
|
41
38
|
closeElement(local: string): 'cell' | 'claimed' | 'other';
|
|
42
39
|
finalize(sheet: Worksheet, sharedStrings: readonly SharedString[], style: XfStyle | undefined): void;
|
|
40
|
+
private cachedResult;
|
|
43
41
|
decode(sharedStrings: readonly SharedString[], style: XfStyle | undefined): CellValue;
|
|
44
42
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { encodeAddress, tryDecodeCellRef } from '../../core/address.js';
|
|
2
2
|
import { translateFormula, unmangleFunctions } from '../../core/formula.js';
|
|
3
|
-
import { boolStrict,
|
|
3
|
+
import { boolStrict, numInteger } from '../../xml/xml-scan.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';
|
|
@@ -16,17 +16,14 @@ export class CellAccumulator {
|
|
|
16
16
|
#sharedClone = false;
|
|
17
17
|
#dataTable = null;
|
|
18
18
|
#valueText = '';
|
|
19
|
-
#inlineText = '';
|
|
20
19
|
#hasFormula = false;
|
|
21
20
|
#hasValue = false;
|
|
22
|
-
#runs
|
|
21
|
+
#runs;
|
|
23
22
|
#masters = new Map();
|
|
24
|
-
#inInlineString = false;
|
|
25
23
|
#capture = false;
|
|
26
24
|
#text = '';
|
|
27
|
-
#richRuns;
|
|
28
25
|
constructor(options) {
|
|
29
|
-
this.#
|
|
26
|
+
this.#runs = new RunAccumulator({ container: 'is', readRuns: options.richRuns });
|
|
30
27
|
}
|
|
31
28
|
get ref() {
|
|
32
29
|
return this.#ref;
|
|
@@ -37,19 +34,15 @@ export class CellAccumulator {
|
|
|
37
34
|
get col() {
|
|
38
35
|
return this.#col;
|
|
39
36
|
}
|
|
40
|
-
get runs() {
|
|
41
|
-
return this.#runs;
|
|
42
|
-
}
|
|
43
37
|
#beginCell(attrs) {
|
|
44
|
-
this.#ref = attrs.r ?? '';
|
|
45
38
|
this.#type = attrs.t ?? '';
|
|
46
39
|
this.#style = numInteger(attrs.s, 0) ?? -1;
|
|
47
|
-
const decoded =
|
|
40
|
+
const decoded = tryDecodeCellRef(attrs.r ?? '');
|
|
41
|
+
this.#ref = decoded === undefined ? '' : (attrs.r ?? '');
|
|
48
42
|
this.#col = decoded?.col ?? -1;
|
|
49
43
|
this.#row = decoded?.row ?? -1;
|
|
50
44
|
this.#formula = '';
|
|
51
45
|
this.#valueText = '';
|
|
52
|
-
this.#inlineText = '';
|
|
53
46
|
this.#runs.beginContainer();
|
|
54
47
|
this.#hasFormula = false;
|
|
55
48
|
this.#hasValue = false;
|
|
@@ -81,15 +74,6 @@ export class CellAccumulator {
|
|
|
81
74
|
this.#valueText = text;
|
|
82
75
|
this.#hasValue = true;
|
|
83
76
|
}
|
|
84
|
-
#beginInlineString() {
|
|
85
|
-
this.#inlineText = '';
|
|
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
77
|
get capturedText() {
|
|
94
78
|
return this.#text;
|
|
95
79
|
}
|
|
@@ -99,14 +83,12 @@ export class CellAccumulator {
|
|
|
99
83
|
openElement(local, attrs, selfClosing) {
|
|
100
84
|
this.#text = '';
|
|
101
85
|
this.#capture = false;
|
|
86
|
+
if (this.#runs.open(local, attrs, selfClosing))
|
|
87
|
+
return true;
|
|
102
88
|
switch (local) {
|
|
103
89
|
case 'c':
|
|
104
90
|
this.#beginCell(attrs);
|
|
105
91
|
return true;
|
|
106
|
-
case 'is':
|
|
107
|
-
this.#inInlineString = true;
|
|
108
|
-
this.#beginInlineString();
|
|
109
|
-
return true;
|
|
110
92
|
case 'f':
|
|
111
93
|
this.#capture = !selfClosing;
|
|
112
94
|
this.#beginFormula(attrs, selfClosing);
|
|
@@ -114,25 +96,12 @@ export class CellAccumulator {
|
|
|
114
96
|
case 'v':
|
|
115
97
|
this.#capture = !selfClosing;
|
|
116
98
|
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
99
|
default:
|
|
132
100
|
return false;
|
|
133
101
|
}
|
|
134
102
|
}
|
|
135
103
|
appendChunk(chunk) {
|
|
104
|
+
this.#runs.text(chunk);
|
|
136
105
|
if (this.#capture)
|
|
137
106
|
this.#text += chunk;
|
|
138
107
|
}
|
|
@@ -142,6 +111,8 @@ export class CellAccumulator {
|
|
|
142
111
|
return verdict;
|
|
143
112
|
}
|
|
144
113
|
#closeElement(local) {
|
|
114
|
+
if (this.#runs.close(local) !== 'other')
|
|
115
|
+
return 'claimed';
|
|
145
116
|
switch (local) {
|
|
146
117
|
case 'f':
|
|
147
118
|
this.#setFormula(this.#text);
|
|
@@ -149,17 +120,6 @@ export class CellAccumulator {
|
|
|
149
120
|
case 'v':
|
|
150
121
|
this.#setValue(this.#text);
|
|
151
122
|
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
123
|
case 'c':
|
|
164
124
|
return 'cell';
|
|
165
125
|
default:
|
|
@@ -183,9 +143,7 @@ export class CellAccumulator {
|
|
|
183
143
|
...(boolStrict(this.#dataTable.dtr) ? { dataTableRow: true } : {}),
|
|
184
144
|
...(this.#dataTable.r1 !== undefined ? { r1: this.#dataTable.r1 } : {}),
|
|
185
145
|
...(this.#dataTable.r2 !== undefined ? { r2: this.#dataTable.r2 } : {}),
|
|
186
|
-
...(
|
|
187
|
-
? { result: decodeFormulaResult(this.#type, this.#valueText, style?.numFmt) }
|
|
188
|
-
: {}),
|
|
146
|
+
...this.cachedResult(style),
|
|
189
147
|
};
|
|
190
148
|
}
|
|
191
149
|
if (this.#hasFormula && this.#formulaShared && this.#formulaSi >= 0) {
|
|
@@ -198,14 +156,18 @@ export class CellAccumulator {
|
|
|
198
156
|
return {
|
|
199
157
|
sharedFormula: encodeAddress(master.col, master.row),
|
|
200
158
|
formula: unmangleFunctions(translated),
|
|
201
|
-
...(
|
|
202
|
-
? { result: decodeFormulaResult(this.#type, this.#valueText, style?.numFmt) }
|
|
203
|
-
: {}),
|
|
159
|
+
...this.cachedResult(style),
|
|
204
160
|
};
|
|
205
161
|
}
|
|
206
162
|
}
|
|
207
163
|
return this.decode(sharedStrings, style);
|
|
208
164
|
}
|
|
165
|
+
cachedResult(style) {
|
|
166
|
+
if (!this.#hasValue)
|
|
167
|
+
return {};
|
|
168
|
+
const result = decodeFormulaResult(this.#type, this.#valueText, style?.numFmt);
|
|
169
|
+
return result === undefined ? {} : { result };
|
|
170
|
+
}
|
|
209
171
|
decode(sharedStrings, style) {
|
|
210
172
|
const raw = {
|
|
211
173
|
type: this.#type,
|
|
@@ -213,7 +175,7 @@ export class CellAccumulator {
|
|
|
213
175
|
formula: this.#formula,
|
|
214
176
|
hasValue: this.#hasValue,
|
|
215
177
|
valueText: this.#valueText,
|
|
216
|
-
inlineText: this.#
|
|
178
|
+
inlineText: this.#runs.plainText,
|
|
217
179
|
richTextRuns: this.#runs.runs,
|
|
218
180
|
};
|
|
219
181
|
return decodeCellContent(raw, sharedStrings, style?.numFmt);
|
|
@@ -29,4 +29,4 @@ export declare function decodeCellContent(raw: RawCell, sharedStrings: readonly
|
|
|
29
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
|
-
export declare function decodeFormulaResult(type: string, valueText: string, numFmt?: string): FormulaResult;
|
|
32
|
+
export declare function decodeFormulaResult(type: string, valueText: string, numFmt?: string): FormulaResult | undefined;
|
|
@@ -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, decodeSpreadsheetText } from '../../xml/xml-
|
|
4
|
+
import { boolStrict, decodeSpreadsheetText, numFinite } from '../../xml/xml-scan.js';
|
|
5
5
|
export function decodeCellContent(raw, sharedStrings, numFmt) {
|
|
6
6
|
if (raw.hasFormula) {
|
|
7
7
|
const stored = unmangleFunctions(raw.formula);
|
|
@@ -32,8 +32,11 @@ function decodeValue(type, valueText, inlineText, hasValue, sharedStrings) {
|
|
|
32
32
|
return boolStrict(valueText);
|
|
33
33
|
case 'e':
|
|
34
34
|
return isErrorCode(valueText) ? { error: valueText } : valueText;
|
|
35
|
-
default:
|
|
36
|
-
|
|
35
|
+
default: {
|
|
36
|
+
if (!hasValue)
|
|
37
|
+
return null;
|
|
38
|
+
return numFinite(valueText) ?? null;
|
|
39
|
+
}
|
|
37
40
|
}
|
|
38
41
|
}
|
|
39
42
|
export function decodeFormulaResult(type, valueText, numFmt) {
|
|
@@ -51,6 +54,6 @@ function decodeResult(type, valueText) {
|
|
|
51
54
|
case 'e':
|
|
52
55
|
return isErrorCode(valueText) ? { error: valueText } : valueText;
|
|
53
56
|
default:
|
|
54
|
-
return
|
|
57
|
+
return numFinite(valueText);
|
|
55
58
|
}
|
|
56
59
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Color } from '../../core/style.ts';
|
|
2
2
|
/** Serialise a {@link Color} as the attribute list a `<color>`-shaped element carries. */
|
|
3
3
|
export declare function colorAttrs(color: Color): string;
|
|
4
4
|
export declare function parseColor(attrs: {
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { parseArgb } from '../../core/style.js';
|
|
2
|
+
import { numFinite, numInteger } from '../../xml/xml-scan.js';
|
|
3
|
+
import { numberText } from '../../xml/xml.js';
|
|
2
4
|
function normalizeArgb(argb) {
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
if (!/^[0-9a-fA-F]{8}$/.test(rgb)) {
|
|
5
|
+
const rgb = parseArgb(argb);
|
|
6
|
+
if (rgb === undefined) {
|
|
6
7
|
throw new SyntaxError(`Invalid ARGB colour ${JSON.stringify(argb)}: expected 6 or 8 hexadecimal digits`);
|
|
7
8
|
}
|
|
8
9
|
return rgb;
|
|
@@ -12,11 +13,11 @@ export function colorAttrs(color) {
|
|
|
12
13
|
if (color.argb !== undefined)
|
|
13
14
|
parts.push(`rgb="${normalizeArgb(color.argb)}"`);
|
|
14
15
|
if (color.theme !== undefined)
|
|
15
|
-
parts.push(`theme="${color.theme}"`);
|
|
16
|
+
parts.push(`theme="${numberText(color.theme)}"`);
|
|
16
17
|
if (color.tint !== undefined)
|
|
17
|
-
parts.push(`tint="${color.tint}"`);
|
|
18
|
+
parts.push(`tint="${numberText(color.tint)}"`);
|
|
18
19
|
if (color.indexed !== undefined)
|
|
19
|
-
parts.push(`indexed="${color.indexed}"`);
|
|
20
|
+
parts.push(`indexed="${numberText(color.indexed)}"`);
|
|
20
21
|
return parts.join(' ');
|
|
21
22
|
}
|
|
22
23
|
export function parseColor(attrs) {
|
package/dist/io/xlsx/comments.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { tryDecodeCellRef } from '../../core/address.js';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { parseXml } from '../../xml/xml-read.js';
|
|
3
|
+
import { decodeSpreadsheetText, localName, numInteger } from '../../xml/xml-scan.js';
|
|
4
|
+
import { escapeText, textAttr, textElement, XML_DECLARATION } from '../../xml/xml.js';
|
|
4
5
|
import { MARKUP_COMPATIBILITY_NS, REVISION_NS, SPREADSHEETML_NS } from './namespaces.js';
|
|
5
6
|
export function collectComments(sheet, threads) {
|
|
6
7
|
const fallbacks = threadFallbacks(threads);
|
|
@@ -56,7 +57,7 @@ export function commentsXml(comments) {
|
|
|
56
57
|
.map((comment) => {
|
|
57
58
|
const { threadId } = comment;
|
|
58
59
|
const authorId = threadId === undefined ? noteAuthorId : authorIdByThreadId.get(threadId);
|
|
59
|
-
const uid =
|
|
60
|
+
const uid = textAttr('xr:uid', threadId);
|
|
60
61
|
return (`<comment ref="${comment.ref}" authorId="${authorId}"${uid}>` +
|
|
61
62
|
`<text><r>${textElement(comment.text)}</r></text>` +
|
|
62
63
|
'</comment>');
|
|
@@ -161,6 +162,8 @@ export function applyNotes(sheet, comments) {
|
|
|
161
162
|
for (const [ref, comment] of comments) {
|
|
162
163
|
if (comment.threadId !== undefined && headIds.has(comment.threadId))
|
|
163
164
|
continue;
|
|
165
|
+
if (tryDecodeCellRef(ref) === undefined)
|
|
166
|
+
continue;
|
|
164
167
|
sheet.getCell(ref).note = comment.text;
|
|
165
168
|
}
|
|
166
169
|
}
|