@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
|
@@ -3,20 +3,25 @@ import type { Worksheet } from '../../core/worksheet.ts';
|
|
|
3
3
|
import { type CollectingPass } from '../../xml/xml-read.ts';
|
|
4
4
|
/** The standard `<dataValidations>` element for the rules stored in the legacy form, or '' when the
|
|
5
5
|
* sheet has none of them, so a sheet with only extended (or no) validations stays byte-clean here.
|
|
6
|
-
* The extended rules are emitted separately by {@link dataValidationsExtXml}.
|
|
7
|
-
|
|
6
|
+
* The extended rules are emitted separately by {@link dataValidationsExtXml}.
|
|
7
|
+
*
|
|
8
|
+
* An operand is stored with the function prefixes a cell formula takes, as Excel stores it;
|
|
9
|
+
* `formulaNames` is `formulaNamesInScope` for the sheet. */
|
|
10
|
+
export declare function dataValidationsXml(entries: readonly DataValidationEntry[], formulaNames: ReadonlySet<string>): string;
|
|
8
11
|
/** The `<ext>` carrying the extended (`<x14:dataValidation>`) rules, or '' when the sheet declares
|
|
9
12
|
* none. Emitted bare (no `<extLst>` wrapper) so the worksheet serialiser can gather it into a single
|
|
10
13
|
* `<extLst>` beside the conditional-formatting extension: a worksheet may carry at most one. */
|
|
11
|
-
export declare function dataValidationsExtXml(entries: readonly DataValidationEntry[]): string;
|
|
14
|
+
export declare function dataValidationsExtXml(entries: readonly DataValidationEntry[], formulaNames: ReadonlySet<string>): string;
|
|
12
15
|
/** A pass gathering the standard `<dataValidation>` elements of a worksheet part, for a caller
|
|
13
|
-
* reading the part alongside its other readers in one parse.
|
|
14
|
-
|
|
16
|
+
* reading the part alongside its other readers in one parse. An operand sheds its function prefixes
|
|
17
|
+
* against `definedNames`, the workbook's names as `definedNameKeys` spells them. */
|
|
18
|
+
export declare function dataValidationPass(definedNames: ReadonlySet<string>): CollectingPass<DataValidationEntry[]>;
|
|
15
19
|
/** A pass gathering the extended `<x14:dataValidation>` elements of a worksheet's `<extLst>`, so a
|
|
16
20
|
* cross-sheet or whole-column list validation Excel stored only in the 2009 extension form is read
|
|
17
21
|
* back rather than dropped. The standard pass ignores these (they are prefixed); this one,
|
|
18
|
-
* symmetrically, handles only the prefixed elements.
|
|
19
|
-
|
|
22
|
+
* symmetrically, handles only the prefixed elements. An operand sheds its function prefixes against
|
|
23
|
+
* `definedNames`, as the standard pass's does. */
|
|
24
|
+
export declare function extendedDataValidationPass(definedNames: ReadonlySet<string>): CollectingPass<DataValidationEntry[]>;
|
|
20
25
|
/** Fold parsed validations onto a sheet, each bound to its original range and carrying its form: an
|
|
21
26
|
* `extended` entry is re-attached as extended so a round-trip writes it back to the x14 block. */
|
|
22
27
|
export declare function applyDataValidations(sheet: Worksheet, entries: readonly DataValidationEntry[]): void;
|
|
@@ -1,26 +1,30 @@
|
|
|
1
|
-
import { isDataValidationErrorStyle, isDataValidationOperator, isDataValidationType, } from '../../core/data-validation.js';
|
|
2
|
-
import { stripFormulaEquals } from '../../core/formula.js';
|
|
1
|
+
import { isDataValidationErrorStyle, isDataValidationImeMode, isDataValidationOperator, isDataValidationType, } from '../../core/data-validation.js';
|
|
2
|
+
import { mangleFormula, stripFormulaEquals, unmangleFunctions } from '../../core/formula.js';
|
|
3
3
|
import { decodeSqrefRects } from '../../core/merge.js';
|
|
4
|
-
import { coerceNumericLiteral } from '../../xml/xml-attrs.js';
|
|
4
|
+
import { coerceNumericLiteral, enumToken } from '../../xml/xml-attrs.js';
|
|
5
5
|
import { TextCapture } from '../../xml/xml-read.js';
|
|
6
6
|
import { boolStrict, localName } from '../../xml/xml-scan.js';
|
|
7
7
|
import { checkedToken, escapeAttr, escapeText, textAttr } from '../../xml/xml.js';
|
|
8
|
+
import { admitting } from '../read-policy/read-repair.js';
|
|
8
9
|
import { DATA_VALIDATION_EXT_URI, isExtensionElement, isMainNamespaceElement, XM_NS, } from './namespaces.js';
|
|
9
|
-
import { admitting } from './read-repair.js';
|
|
10
10
|
import { x14Ext } from './x14-ext.js';
|
|
11
11
|
const TYPED = new Set(['whole', 'decimal', 'date', 'time', 'textLength']);
|
|
12
|
-
export function dataValidationsXml(entries) {
|
|
12
|
+
export function dataValidationsXml(entries, formulaNames) {
|
|
13
13
|
const standard = entries.filter((entry) => !entry.extended);
|
|
14
14
|
if (standard.length === 0)
|
|
15
15
|
return '';
|
|
16
|
-
const items = standard
|
|
16
|
+
const items = standard
|
|
17
|
+
.map(({ sqref, rule }) => dataValidationXml(sqref, rule, formulaNames))
|
|
18
|
+
.join('');
|
|
17
19
|
return `<dataValidations count="${standard.length}">${items}</dataValidations>`;
|
|
18
20
|
}
|
|
19
|
-
export function dataValidationsExtXml(entries) {
|
|
21
|
+
export function dataValidationsExtXml(entries, formulaNames) {
|
|
20
22
|
const extended = entries.filter((entry) => entry.extended);
|
|
21
23
|
if (extended.length === 0)
|
|
22
24
|
return '';
|
|
23
|
-
const items = extended
|
|
25
|
+
const items = extended
|
|
26
|
+
.map(({ sqref, rule }) => extendedDataValidationXml(sqref, rule, formulaNames))
|
|
27
|
+
.join('');
|
|
24
28
|
return x14Ext(DATA_VALIDATION_EXT_URI, `<x14:dataValidations count="${extended.length}" xmlns:xm="${XM_NS}">${items}</x14:dataValidations>`);
|
|
25
29
|
}
|
|
26
30
|
function ruleAttrs(rule) {
|
|
@@ -28,10 +32,14 @@ function ruleAttrs(rule) {
|
|
|
28
32
|
(rule.errorStyle === undefined
|
|
29
33
|
? ''
|
|
30
34
|
: ` errorStyle="${checkedToken(rule.errorStyle, isDataValidationErrorStyle, 'data validation error style')}"`) +
|
|
35
|
+
(rule.imeMode === undefined
|
|
36
|
+
? ''
|
|
37
|
+
: ` imeMode="${checkedToken(rule.imeMode, isDataValidationImeMode, 'data validation IME mode')}"`) +
|
|
31
38
|
(rule.operator === undefined
|
|
32
39
|
? ''
|
|
33
40
|
: ` operator="${checkedToken(rule.operator, isDataValidationOperator, 'data validation operator')}"`) +
|
|
34
41
|
(rule.allowBlank ? ' allowBlank="1"' : '') +
|
|
42
|
+
(rule.suppressDropDown ? ' showDropDown="1"' : '') +
|
|
35
43
|
(rule.showInputMessage ? ' showInputMessage="1"' : '') +
|
|
36
44
|
(rule.showErrorMessage ? ' showErrorMessage="1"' : '') +
|
|
37
45
|
textAttr('errorTitle', rule.errorTitle) +
|
|
@@ -39,30 +47,28 @@ function ruleAttrs(rule) {
|
|
|
39
47
|
textAttr('promptTitle', rule.promptTitle) +
|
|
40
48
|
textAttr('prompt', rule.prompt));
|
|
41
49
|
}
|
|
42
|
-
function dataValidationXml(sqref, rule) {
|
|
43
|
-
const [f1, f2] =
|
|
44
|
-
const body = (f1 !== undefined ? `<formula1>${
|
|
45
|
-
(f2 !== undefined ? `<formula2>${
|
|
50
|
+
function dataValidationXml(sqref, rule, formulaNames) {
|
|
51
|
+
const [f1, f2] = rule.formulae ?? [];
|
|
52
|
+
const body = (f1 !== undefined ? `<formula1>${operandText(f1, formulaNames)}</formula1>` : '') +
|
|
53
|
+
(f2 !== undefined ? `<formula2>${operandText(f2, formulaNames)}</formula2>` : '');
|
|
46
54
|
return `<dataValidation${ruleAttrs(rule)} sqref="${escapeAttr(sqref)}">${body}</dataValidation>`;
|
|
47
55
|
}
|
|
48
|
-
function
|
|
49
|
-
const drop = (v) => typeof v === 'number' && !Number.isFinite(v) ? undefined : v;
|
|
56
|
+
function extendedDataValidationXml(sqref, rule, formulaNames) {
|
|
50
57
|
const [f1, f2] = rule.formulae ?? [];
|
|
51
|
-
return [drop(f1), drop(f2)];
|
|
52
|
-
}
|
|
53
|
-
function extendedDataValidationXml(sqref, rule) {
|
|
54
|
-
const [f1, f2] = operands(rule);
|
|
55
58
|
const body = (f1 !== undefined
|
|
56
|
-
? `<x14:formula1><xm:f>${
|
|
59
|
+
? `<x14:formula1><xm:f>${operandText(f1, formulaNames)}</xm:f></x14:formula1>`
|
|
57
60
|
: '') +
|
|
58
61
|
(f2 !== undefined
|
|
59
|
-
? `<x14:formula2><xm:f>${
|
|
62
|
+
? `<x14:formula2><xm:f>${operandText(f2, formulaNames)}</xm:f></x14:formula2>`
|
|
60
63
|
: '') +
|
|
61
64
|
`<xm:sqref>${escapeText(sqref)}</xm:sqref>`;
|
|
62
65
|
return `<x14:dataValidation${ruleAttrs(rule)}>${body}</x14:dataValidation>`;
|
|
63
66
|
}
|
|
67
|
+
function operandText(operand, formulaNames) {
|
|
68
|
+
return escapeText(mangleFormula(stripFormulaEquals(operand), formulaNames));
|
|
69
|
+
}
|
|
64
70
|
const FORMULA_ELEMENTS = new Set(['formula1', 'formula2']);
|
|
65
|
-
export function dataValidationPass() {
|
|
71
|
+
export function dataValidationPass(definedNames) {
|
|
66
72
|
const entries = [];
|
|
67
73
|
let current;
|
|
68
74
|
const formula = new TextCapture(FORMULA_ELEMENTS);
|
|
@@ -88,7 +94,7 @@ export function dataValidationPass() {
|
|
|
88
94
|
return;
|
|
89
95
|
}
|
|
90
96
|
if (ln === 'dataValidation' && current !== undefined) {
|
|
91
|
-
const built = buildEntry(current.attrs, current.formulae);
|
|
97
|
+
const built = buildEntry(current.attrs, current.formulae, definedNames);
|
|
92
98
|
if (built !== undefined)
|
|
93
99
|
entries.push(built);
|
|
94
100
|
current = undefined;
|
|
@@ -97,14 +103,14 @@ export function dataValidationPass() {
|
|
|
97
103
|
};
|
|
98
104
|
return { handlers, result: () => entries };
|
|
99
105
|
}
|
|
100
|
-
function buildEntry(attrs, formulae) {
|
|
106
|
+
function buildEntry(attrs, formulae, definedNames) {
|
|
101
107
|
const { sqref } = attrs;
|
|
102
108
|
if (sqref === undefined)
|
|
103
109
|
return undefined;
|
|
104
|
-
const rule = buildRule(attrs, formulae);
|
|
110
|
+
const rule = buildRule(attrs, formulae, definedNames);
|
|
105
111
|
return rule === undefined ? undefined : { sqref, rule };
|
|
106
112
|
}
|
|
107
|
-
function buildRule(attrs, formulae) {
|
|
113
|
+
function buildRule(attrs, formulae, definedNames) {
|
|
108
114
|
const { type } = attrs;
|
|
109
115
|
if (type === undefined || !isDataValidationType(type))
|
|
110
116
|
return undefined;
|
|
@@ -118,12 +124,17 @@ function buildRule(attrs, formulae) {
|
|
|
118
124
|
}
|
|
119
125
|
if (boolStrict(attrs.allowBlank))
|
|
120
126
|
rule.allowBlank = true;
|
|
127
|
+
if (boolStrict(attrs.showDropDown))
|
|
128
|
+
rule.suppressDropDown = true;
|
|
121
129
|
if (boolStrict(attrs.showInputMessage))
|
|
122
130
|
rule.showInputMessage = true;
|
|
123
131
|
if (boolStrict(attrs.showErrorMessage))
|
|
124
132
|
rule.showErrorMessage = true;
|
|
125
133
|
if (attrs.errorStyle !== undefined && isDataValidationErrorStyle(attrs.errorStyle))
|
|
126
134
|
rule.errorStyle = attrs.errorStyle;
|
|
135
|
+
const imeMode = enumToken(attrs.imeMode, isDataValidationImeMode);
|
|
136
|
+
if (imeMode !== undefined)
|
|
137
|
+
rule.imeMode = imeMode;
|
|
127
138
|
if (attrs.error !== undefined)
|
|
128
139
|
rule.error = attrs.error;
|
|
129
140
|
if (attrs.errorTitle !== undefined)
|
|
@@ -134,12 +145,13 @@ function buildRule(attrs, formulae) {
|
|
|
134
145
|
rule.promptTitle = attrs.promptTitle;
|
|
135
146
|
const parsed = formulae
|
|
136
147
|
.filter((f) => f !== undefined)
|
|
148
|
+
.map((f) => unmangleFunctions(f, definedNames))
|
|
137
149
|
.map((f) => (type === 'list' || type === 'custom' ? f : coerceNumericLiteral(f)));
|
|
138
150
|
if (parsed.length > 0)
|
|
139
151
|
rule.formulae = parsed;
|
|
140
152
|
return rule;
|
|
141
153
|
}
|
|
142
|
-
export function extendedDataValidationPass() {
|
|
154
|
+
export function extendedDataValidationPass(definedNames) {
|
|
143
155
|
const entries = [];
|
|
144
156
|
let current;
|
|
145
157
|
let slot;
|
|
@@ -181,7 +193,7 @@ export function extendedDataValidationPass() {
|
|
|
181
193
|
slot = undefined;
|
|
182
194
|
}
|
|
183
195
|
else if (ln === 'dataValidation' && extension && current !== undefined) {
|
|
184
|
-
const built = buildExtendedEntry(current.attrs, current.formulae, current.sqref);
|
|
196
|
+
const built = buildExtendedEntry(current.attrs, current.formulae, current.sqref, definedNames);
|
|
185
197
|
if (built !== undefined)
|
|
186
198
|
entries.push(built);
|
|
187
199
|
current = undefined;
|
|
@@ -190,10 +202,10 @@ export function extendedDataValidationPass() {
|
|
|
190
202
|
};
|
|
191
203
|
return { handlers, result: () => entries };
|
|
192
204
|
}
|
|
193
|
-
function buildExtendedEntry(attrs, formulae, sqref) {
|
|
205
|
+
function buildExtendedEntry(attrs, formulae, sqref, definedNames) {
|
|
194
206
|
if (sqref === '')
|
|
195
207
|
return undefined;
|
|
196
|
-
const rule = buildRule(attrs, formulae);
|
|
208
|
+
const rule = buildRule(attrs, formulae, definedNames);
|
|
197
209
|
return rule === undefined ? undefined : { sqref, rule, extended: true };
|
|
198
210
|
}
|
|
199
211
|
export function applyDataValidations(sheet, entries) {
|
package/dist/io/xlsx/edit-vba.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { strToU8, zipSync } from 'fflate';
|
|
2
2
|
import { VbaAuthorError } from '../../vba/errors.js';
|
|
3
3
|
import { addVbaReference, removeVbaModule, } from '../../vba/project-editor.js';
|
|
4
|
+
import { elementRange } from '../../xml/xml-read.js';
|
|
4
5
|
import { relsPathFor, resolveRelativePart } from '../opc/part-paths.js';
|
|
5
|
-
import { parseRelationshipRecords, readPartRelationships } from '../opc/read-opc.js';
|
|
6
|
+
import { packageAccessors, parseRelationshipRecords, readPartRelationships, } from '../opc/read-opc.js';
|
|
6
7
|
import { DEFAULT_MAX_UNCOMPRESSED } from '../opc/read-options.js';
|
|
7
8
|
import { inflateSpreadsheetPackage } from '../opc/sniff-format.js';
|
|
8
9
|
import { FIXED_ENTRY_MTIME } from '../opc/zip-mtime.js';
|
|
@@ -17,56 +18,62 @@ export function editXlsxVbaAddReference(xlsx, ref, options = {}) {
|
|
|
17
18
|
}
|
|
18
19
|
function applyToVbaProjectPart(xlsx, options, apply) {
|
|
19
20
|
const files = inflateSpreadsheetPackage(xlsx, options.maxUncompressedBytes ?? DEFAULT_MAX_UNCOMPRESSED);
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
|
|
21
|
+
const pkg = packageAccessors(files);
|
|
22
|
+
const binPath = locateVbaProjectPart(pkg);
|
|
23
|
+
const binKey = binPath === undefined ? undefined : pkg.partKey(binPath);
|
|
24
|
+
const bin = binKey === undefined ? undefined : files[binKey];
|
|
25
|
+
if (binPath === undefined || binKey === undefined || bin === undefined) {
|
|
23
26
|
throw new VbaAuthorError('package has no VBA project to edit');
|
|
24
27
|
}
|
|
25
|
-
files[
|
|
26
|
-
dropStaleSignature(files, binPath);
|
|
28
|
+
files[binKey] = apply(bin);
|
|
29
|
+
dropStaleSignature(files, pkg, binPath);
|
|
27
30
|
return zipSync(files, { mtime: FIXED_ENTRY_MTIME });
|
|
28
31
|
}
|
|
29
|
-
function locateVbaProjectPart(
|
|
30
|
-
const
|
|
31
|
-
const workbookPath = readPartRelationships('', partText).targetPath(OFFICE_DOCUMENT_REL);
|
|
32
|
+
function locateVbaProjectPart(pkg) {
|
|
33
|
+
const workbookPath = readPartRelationships('', pkg.partText).targetPath(OFFICE_DOCUMENT_REL);
|
|
32
34
|
if (workbookPath === undefined)
|
|
33
35
|
return undefined;
|
|
34
|
-
return readPartRelationships(workbookPath, partText).targetPath(VBA_PROJECT_REL);
|
|
36
|
+
return readPartRelationships(workbookPath, pkg.partText).targetPath(VBA_PROJECT_REL);
|
|
35
37
|
}
|
|
36
|
-
function dropStaleSignature(files, binPath) {
|
|
37
|
-
const
|
|
38
|
-
const binRels =
|
|
39
|
-
if (binRels === undefined)
|
|
38
|
+
function dropStaleSignature(files, pkg, binPath) {
|
|
39
|
+
const binRelsKey = pkg.partKey(relsPathFor(binPath));
|
|
40
|
+
const binRels = binRelsKey === undefined ? undefined : pkg.partText(binRelsKey);
|
|
41
|
+
if (binRelsKey === undefined || binRels === undefined)
|
|
40
42
|
return;
|
|
41
43
|
const signatureRels = parseRelationshipRecords(binRels).filter((rel) => !rel.external && rel.type.includes(VBA_SIGNATURE_REL_INFIX));
|
|
42
44
|
if (signatureRels.length === 0)
|
|
43
45
|
return;
|
|
44
|
-
|
|
46
|
+
const contentTypesKey = pkg.partKey(CONTENT_TYPES_PART);
|
|
47
|
+
let contentTypes = contentTypesKey === undefined ? undefined : pkg.partText(contentTypesKey);
|
|
45
48
|
let rels = binRels;
|
|
46
49
|
for (const rel of signatureRels) {
|
|
47
50
|
const partPath = resolveRelativePart(binPath, rel.target);
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
const partKey = pkg.partKey(partPath);
|
|
52
|
+
if (partKey !== undefined)
|
|
53
|
+
delete files[partKey];
|
|
54
|
+
const partName = `/${partPath}`.toLowerCase();
|
|
55
|
+
rels = removeElements(rels, 'Relationship', (attrs) => attrs.Id === rel.id);
|
|
56
|
+
if (contentTypes !== undefined) {
|
|
57
|
+
contentTypes = removeElements(contentTypes, 'Override', (attrs) => attrs.PartName?.toLowerCase() === partName);
|
|
58
|
+
}
|
|
52
59
|
}
|
|
53
60
|
if (parseRelationshipRecords(rels).length === 0)
|
|
54
|
-
delete files[
|
|
61
|
+
delete files[binRelsKey];
|
|
55
62
|
else
|
|
56
|
-
files[
|
|
57
|
-
if (contentTypes !== undefined)
|
|
58
|
-
files[
|
|
59
|
-
}
|
|
60
|
-
function textPart(files, path) {
|
|
61
|
-
const bytes = files[path];
|
|
62
|
-
return bytes === undefined ? undefined : strFromU8(bytes);
|
|
63
|
-
}
|
|
64
|
-
function removeRelationshipById(xml, id) {
|
|
65
|
-
return xml.replace(/<Relationship\b[^>]*?\/>|<Relationship\b[\s\S]*?<\/Relationship>/g, (element) => (new RegExp(`\\bId="${escapeRegExp(id)}"`).test(element) ? '' : element));
|
|
66
|
-
}
|
|
67
|
-
function removeContentTypeOverride(xml, partPath) {
|
|
68
|
-
return xml.replace(/<Override\b[^>]*?\/>/g, (element) => element.includes(`PartName="/${partPath}"`) ? '' : element);
|
|
63
|
+
files[binRelsKey] = strToU8(rels);
|
|
64
|
+
if (contentTypesKey !== undefined && contentTypes !== undefined) {
|
|
65
|
+
files[contentTypesKey] = strToU8(contentTypes);
|
|
66
|
+
}
|
|
69
67
|
}
|
|
70
|
-
|
|
71
|
-
|
|
68
|
+
const CONTENT_TYPES_PART = '[Content_Types].xml';
|
|
69
|
+
function removeElements(xml, local, drop) {
|
|
70
|
+
let kept = '';
|
|
71
|
+
let rest = xml;
|
|
72
|
+
let range = elementRange(rest, [local]);
|
|
73
|
+
while (range !== undefined) {
|
|
74
|
+
kept += rest.slice(0, drop(range.attrs) ? range.start : range.end);
|
|
75
|
+
rest = rest.slice(range.end);
|
|
76
|
+
range = elementRange(rest, [local]);
|
|
77
|
+
}
|
|
78
|
+
return kept + rest;
|
|
72
79
|
}
|
|
@@ -1,61 +1,26 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Hyperlink } from '../../core/hyperlink.ts';
|
|
2
2
|
import type { Worksheet } from '../../core/worksheet.ts';
|
|
3
3
|
import { type CollectingPass } from '../../xml/xml-read.ts';
|
|
4
|
-
import type {
|
|
5
|
-
/** A hyperlink gathered from a sheet for serialisation: the cell it sits on, its target, and an
|
|
6
|
-
* optional tooltip. The visible label is the cell's own value and is serialised as that value.
|
|
7
|
-
*
|
|
8
|
-
* `row`/`col` are the anchor's position, kept so links gathered from separate passes (the sheet's live
|
|
9
|
-
* rows, and the rows the streaming writer already flushed and evicted) can be merged back into the
|
|
10
|
-
* row-major order Excel writes them in. They are not serialised; `ref` is.
|
|
11
|
-
*/
|
|
12
|
-
export interface CollectedHyperlink {
|
|
13
|
-
readonly ref: string;
|
|
14
|
-
readonly row: number;
|
|
15
|
-
readonly col: number;
|
|
16
|
-
readonly target: string;
|
|
17
|
-
readonly tooltip?: string;
|
|
18
|
-
}
|
|
4
|
+
import type { RelationshipLedger } from './package-plan.ts';
|
|
19
5
|
/** A hyperlink resolved for serialisation. An external target carries a `relId` (the sheet
|
|
20
|
-
* relationship holding the URL)
|
|
21
|
-
*
|
|
6
|
+
* relationship holding the URL); an internal target carries a `location` (the in-workbook
|
|
7
|
+
* reference). Exactly one of `relId`/`location` is ever set. */
|
|
22
8
|
export interface HyperlinkPlan {
|
|
23
9
|
readonly ref: string;
|
|
24
10
|
readonly relId?: string;
|
|
25
|
-
readonly target?: string;
|
|
26
11
|
readonly location?: string;
|
|
27
12
|
readonly tooltip?: string;
|
|
28
13
|
}
|
|
29
14
|
/**
|
|
30
|
-
*
|
|
15
|
+
* Split a sheet's links into internal (location, no rel) and external (relationship) forms, recording
|
|
16
|
+
* each external link's URL in the sheet's ledger so its relationship follows every other sheet-local
|
|
17
|
+
* relationship in canonical order. An internal (`#`-prefixed) link records nothing.
|
|
31
18
|
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
* they still hold.
|
|
19
|
+
* The links keep the order the sheet holds them in. That order is what decides which of two links over
|
|
20
|
+
* one cell `Worksheet.hyperlinkAt` reports, and a re-read adds them in document order, so writing them
|
|
21
|
+
* any other way would change the answer across a save.
|
|
36
22
|
*/
|
|
37
|
-
export declare function
|
|
38
|
-
/** Every cell a sheet still holds, row-major: the live half of what a writer must gather. */
|
|
39
|
-
export declare function liveCells(sheet: Worksheet): Generator<Cell, void, undefined>;
|
|
40
|
-
/** An external hyperlink: the two fields a `TargetMode="External"` relationship needs, both present. */
|
|
41
|
-
export type ExternalHyperlinkPlan = HyperlinkPlan & {
|
|
42
|
-
readonly relId: string;
|
|
43
|
-
readonly target: string;
|
|
44
|
-
};
|
|
45
|
-
/**
|
|
46
|
-
* Whether a planned hyperlink is the external kind, narrowing it so the relationship writer reads
|
|
47
|
-
* `relId` and `target` as the strings they are.
|
|
48
|
-
*
|
|
49
|
-
* A predicate rather than a filter plus two casts: the filter already proved both fields present, and
|
|
50
|
-
* a cast repeating that proof one line later is a claim the compiler cannot check against the filter
|
|
51
|
-
* it is supposed to be echoing. `planHyperlinks` sets exactly one of `relId`/`location`, so testing
|
|
52
|
-
* either field is testing the kind.
|
|
53
|
-
*/
|
|
54
|
-
export declare function isExternalHyperlink(link: HyperlinkPlan): link is ExternalHyperlinkPlan;
|
|
55
|
-
/** Split collected links into internal (location, no rel) and external (relationship) forms, drawing
|
|
56
|
-
* each external link's relationship id from the sheet's allocator so external ids follow every other
|
|
57
|
-
* sheet-local relationship in canonical order. An internal ('#'-prefixed) link consumes no id. */
|
|
58
|
-
export declare function planHyperlinks(links: readonly CollectedHyperlink[], rels: RelIdAllocator): HyperlinkPlan[];
|
|
23
|
+
export declare function planHyperlinks(links: readonly Hyperlink[], rels: RelationshipLedger): HyperlinkPlan[];
|
|
59
24
|
/** The `<hyperlinks>` element, or '' when the sheet has none. Attribute order follows CT_Hyperlink:
|
|
60
25
|
* `ref`, `r:id`, `location`, `tooltip`. */
|
|
61
26
|
export declare function hyperlinksXml(links: readonly HyperlinkPlan[]): string;
|
|
@@ -70,9 +35,10 @@ interface ParsedHyperlink {
|
|
|
70
35
|
/** A pass gathering every `<hyperlink>` element of a worksheet part, for a caller reading the part
|
|
71
36
|
* alongside its other readers in one parse. */
|
|
72
37
|
export declare function sheetHyperlinkPass(): CollectingPass<ParsedHyperlink[]>;
|
|
73
|
-
/**
|
|
74
|
-
*
|
|
75
|
-
* URL for the external links hyperlinks almost always
|
|
76
|
-
* package rather than being handed over as a part path.
|
|
38
|
+
/**
|
|
39
|
+
* Put parsed hyperlinks on a sheet, in document order, whatever the cells they cover hold. `targetOf`
|
|
40
|
+
* resolves a relationship id to its raw Target: a URL for the external links hyperlinks almost always
|
|
41
|
+
* are, so it must stay unresolved against the package rather than being handed over as a part path.
|
|
42
|
+
*/
|
|
77
43
|
export declare function applyHyperlinks(sheet: Worksheet, links: readonly ParsedHyperlink[], targetOf: (relId: string) => string | undefined): void;
|
|
78
44
|
export {};
|
|
@@ -1,39 +1,16 @@
|
|
|
1
|
-
import { tryDecodeRange } from '../../core/address.js';
|
|
2
|
-
import { isHyperlinkValue, isRichTextValue } from '../../core/value.js';
|
|
1
|
+
import { boundedRect, tryDecodeRange } from '../../core/address.js';
|
|
3
2
|
import {} from '../../xml/xml-read.js';
|
|
4
3
|
import { localName } from '../../xml/xml-scan.js';
|
|
5
4
|
import { escapeAttr, textAttr } from '../../xml/xml.js';
|
|
6
5
|
import { relAttr } from '../opc/namespaces.js';
|
|
7
|
-
|
|
8
|
-
const links = [];
|
|
9
|
-
for (const cell of cells) {
|
|
10
|
-
const value = cell.value;
|
|
11
|
-
if (isHyperlinkValue(value)) {
|
|
12
|
-
links.push({
|
|
13
|
-
ref: value.range ?? cell.address,
|
|
14
|
-
row: cell.row,
|
|
15
|
-
col: cell.col,
|
|
16
|
-
target: value.hyperlink,
|
|
17
|
-
...(value.tooltip !== undefined ? { tooltip: value.tooltip } : {}),
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
return links;
|
|
22
|
-
}
|
|
23
|
-
export function* liveCells(sheet) {
|
|
24
|
-
for (const { cells } of sheet.rows())
|
|
25
|
-
yield* cells;
|
|
26
|
-
}
|
|
27
|
-
export function isExternalHyperlink(link) {
|
|
28
|
-
return link.relId !== undefined && link.target !== undefined;
|
|
29
|
-
}
|
|
6
|
+
import { REL } from './relationships.js';
|
|
30
7
|
export function planHyperlinks(links, rels) {
|
|
31
8
|
return links.map((link) => {
|
|
32
9
|
const tooltip = link.tooltip !== undefined ? { tooltip: link.tooltip } : {};
|
|
33
10
|
if (link.target.startsWith('#')) {
|
|
34
11
|
return { ref: link.ref, location: link.target.slice(1), ...tooltip };
|
|
35
12
|
}
|
|
36
|
-
return { ref: link.ref, relId: rels.
|
|
13
|
+
return { ref: link.ref, relId: rels.addExternal(REL.hyperlink, link.target), ...tooltip };
|
|
37
14
|
});
|
|
38
15
|
}
|
|
39
16
|
export function hyperlinksXml(links) {
|
|
@@ -77,19 +54,13 @@ export function applyHyperlinks(sheet, links, targetOf) {
|
|
|
77
54
|
if (target === undefined)
|
|
78
55
|
continue;
|
|
79
56
|
const decoded = tryDecodeRange(link.ref);
|
|
80
|
-
if (decoded === undefined || decoded
|
|
57
|
+
if (decoded === undefined || boundedRect(decoded) === undefined)
|
|
81
58
|
continue;
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const spansRange = decoded.tl.address !== decoded.br.address;
|
|
86
|
-
const value = {
|
|
87
|
-
hyperlink: target,
|
|
88
|
-
text,
|
|
59
|
+
sheet.addHyperlink({
|
|
60
|
+
ref: link.ref,
|
|
61
|
+
target,
|
|
89
62
|
...(link.tooltip !== undefined ? { tooltip: link.tooltip } : {}),
|
|
90
|
-
|
|
91
|
-
};
|
|
92
|
-
cell.value = value;
|
|
63
|
+
});
|
|
93
64
|
}
|
|
94
65
|
}
|
|
95
66
|
function resolveTarget(link, targetOf) {
|
package/dist/io/xlsx/images.d.ts
CHANGED
|
@@ -1,20 +1,32 @@
|
|
|
1
|
-
import { type AnchorPoint, type Extent, type ImageAnchor, type ImageEditAs } from '../../core/image.ts';
|
|
1
|
+
import { type AnchorPoint, type Extent, type ImageAnchor, type ImageCrop, type ImageEditAs, type PictureProperties } from '../../core/image.ts';
|
|
2
2
|
/** The content type for a media part's `<Default Extension>` entry in `[Content_Types].xml`. */
|
|
3
3
|
export declare function imageContentType(extension: string): string;
|
|
4
|
-
/** One image placed in a drawing: where it sits
|
|
5
|
-
*
|
|
4
|
+
/** One image placed in a drawing: where it sits, the drawing-local relationship id that ties it to its
|
|
5
|
+
* media bytes, and the picture's own properties. */
|
|
6
6
|
export interface DrawingImage {
|
|
7
7
|
readonly anchor: ImageAnchor;
|
|
8
8
|
/** The `r:embed` id referencing this image's entry in the drawing's own `.rels`. */
|
|
9
9
|
readonly embedId: string;
|
|
10
|
+
readonly properties: PictureProperties;
|
|
11
|
+
/** The `r:id` of the hyperlink relationship in the drawing's own `.rels`, when the picture is a link. */
|
|
12
|
+
readonly hyperlinkId?: string;
|
|
10
13
|
}
|
|
11
14
|
/** The `xl/drawings/drawing{n}.xml` part: one anchor per image, two-cell or one-cell by its shape. */
|
|
12
15
|
export declare function drawingXml(images: readonly DrawingImage[]): string;
|
|
13
|
-
/**
|
|
14
|
-
*
|
|
15
|
-
|
|
16
|
+
/**
|
|
17
|
+
* A drawing kept from a file with pictures added to it: each picture's anchor goes after the drawing's
|
|
18
|
+
* own content, so it draws on top, as a picture inserted last does in Excel.
|
|
19
|
+
*
|
|
20
|
+
* A drawing numbers its shapes by `cNvPr id`, unique within it, so the pictures take ids past the
|
|
21
|
+
* highest one there. Their `r:embed` and link ids are the caller's, allocated past the relationships
|
|
22
|
+
* the drawing already holds.
|
|
23
|
+
*
|
|
24
|
+
* @throws {AuthoringError} if the kept part has no `<wsDr>` root to add a picture to.
|
|
25
|
+
*/
|
|
26
|
+
export declare function mergePictures(drawing: string, images: readonly DrawingImage[]): string;
|
|
16
27
|
/** An image anchor parsed from a drawing part, with the `r:embed` id that names its media. A two-cell
|
|
17
|
-
* anchor carries `to`
|
|
28
|
+
* anchor carries `to` and its `editAs`, the schema default filled in; a one-cell anchor carries `ext`
|
|
29
|
+
* instead. */
|
|
18
30
|
export interface ParsedImageAnchor {
|
|
19
31
|
readonly from: AnchorPoint;
|
|
20
32
|
readonly to?: AnchorPoint;
|
|
@@ -22,13 +34,34 @@ export interface ParsedImageAnchor {
|
|
|
22
34
|
readonly editAs?: ImageEditAs;
|
|
23
35
|
readonly rotation?: number;
|
|
24
36
|
readonly embed: string;
|
|
37
|
+
readonly description?: string;
|
|
38
|
+
readonly title?: string;
|
|
39
|
+
readonly crop?: ImageCrop;
|
|
40
|
+
/** The `r:id` of the picture's `a:hlinkClick`, for the caller to resolve against the drawing's rels. */
|
|
41
|
+
readonly hyperlinkId?: string;
|
|
42
|
+
readonly tooltip?: string;
|
|
25
43
|
}
|
|
26
|
-
/**
|
|
27
|
-
*
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
44
|
+
/** A drawing part as the reader sees it: the picture anchors it could model, and whether those anchors
|
|
45
|
+
* are the whole of it. */
|
|
46
|
+
export interface ParsedDrawing {
|
|
47
|
+
readonly anchors: readonly ParsedImageAnchor[];
|
|
48
|
+
/**
|
|
49
|
+
* Whether every anchor is a two-cell or one-cell anchor holding exactly one embedded picture. When it
|
|
50
|
+
* is not, writing the drawing back from `anchors` would leave the rest out, so the reader keeps the
|
|
51
|
+
* whole part byte for byte instead.
|
|
52
|
+
*/
|
|
53
|
+
readonly fullyModeled: boolean;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Parse a drawing part into its picture anchors (`<xdr:twoCellAnchor>` and `<xdr:oneCellAnchor>`), and
|
|
57
|
+
* decide in the same scan whether they are the whole drawing. It is fully modeled only when every
|
|
58
|
+
* anchor holds exactly one picture whose bytes are embedded; anything else in it, including a picture
|
|
59
|
+
* that links its image rather than embedding it, has no place in the image model.
|
|
60
|
+
*
|
|
61
|
+
* This used to be two scans: one listing the content the model skips, and this one. The list missed
|
|
62
|
+
* an absolute anchor and a linked picture, so a drawing holding either counted as modeled and lost them
|
|
63
|
+
* on write. What an anchor holds is now judged by what the parse found in it, one embedded picture or
|
|
64
|
+
* not, so no kind of anchor content can slip past unnamed; the list left names only the anchor kinds
|
|
65
|
+
* and wrappers this parse does not otherwise open an anchor for.
|
|
66
|
+
*/
|
|
67
|
+
export declare function parseDrawing(xml: string): ParsedDrawing;
|