@shbernal/ts-xlsx 3.0.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 +34 -24
- package/dist/bytes.d.ts +10 -2
- package/dist/bytes.js +22 -0
- package/dist/core/address.d.ts +96 -2
- package/dist/core/address.js +87 -33
- package/dist/core/array-formula-ranges.d.ts +33 -0
- package/dist/core/array-formula-ranges.js +62 -0
- package/dist/core/autofilter.d.ts +4 -3
- package/dist/core/autofilter.js +31 -30
- package/dist/core/axis-handle.d.ts +2 -0
- package/dist/core/axis-handle.js +5 -2
- package/dist/core/cell.d.ts +4 -3
- package/dist/core/cell.js +4 -6
- package/dist/core/clone.d.ts +29 -0
- package/dist/core/clone.js +24 -0
- package/dist/core/color-resolution.js +4 -6
- package/dist/core/column.d.ts +1 -0
- package/dist/core/column.js +6 -2
- package/dist/core/comment-thread.d.ts +7 -3
- package/dist/core/comment-thread.js +2 -1
- package/dist/core/conditional-formatting-overlay.d.ts +11 -1
- package/dist/core/conditional-formatting-overlay.js +30 -3
- package/dist/core/conditional-formatting.d.ts +107 -11
- package/dist/core/conditional-formatting.js +76 -19
- package/dist/core/containers.d.ts +12 -0
- package/dist/core/containers.js +5 -0
- package/dist/core/data-validation-overlay.d.ts +7 -1
- package/dist/core/data-validation-overlay.js +19 -4
- package/dist/core/data-validation.d.ts +22 -0
- package/dist/core/data-validation.js +30 -4
- package/dist/core/date-format.d.ts +20 -0
- package/dist/core/date-format.js +170 -0
- package/dist/core/date.d.ts +43 -8
- package/dist/core/date.js +19 -3
- 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 +76 -17
- package/dist/core/formula.js +87 -80
- 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 +30 -3
- package/dist/core/grid-edits.js +248 -81
- package/dist/core/grid-shift.d.ts +58 -9
- package/dist/core/grid-shift.js +26 -4
- package/dist/core/hyperlink.d.ts +39 -0
- package/dist/core/hyperlink.js +88 -0
- package/dist/core/image.d.ts +49 -14
- package/dist/core/image.js +16 -5
- package/dist/core/internal.d.ts +10 -2
- package/dist/core/limits.d.ts +25 -0
- package/dist/core/limits.js +4 -0
- package/dist/core/merge.d.ts +18 -4
- package/dist/core/merge.js +33 -20
- package/dist/core/page-setup.d.ts +26 -11
- package/dist/core/pivot-table.d.ts +49 -10
- package/dist/core/pivot-table.js +112 -60
- package/dist/core/protection.d.ts +66 -4
- package/dist/core/protection.js +6 -11
- package/dist/core/range.js +34 -30
- package/dist/core/row-input.js +2 -2
- package/dist/core/row.d.ts +1 -0
- package/dist/core/row.js +6 -2
- package/dist/core/structured-reference.d.ts +7 -0
- package/dist/core/structured-reference.js +7 -0
- package/dist/core/style.d.ts +85 -70
- package/dist/core/style.js +92 -28
- package/dist/core/table-style.d.ts +1 -1
- package/dist/core/table-style.js +4 -4
- package/dist/core/table.d.ts +64 -16
- package/dist/core/table.js +87 -40
- package/dist/core/theme.d.ts +2 -0
- package/dist/core/theme.js +9 -5
- package/dist/core/value.d.ts +60 -28
- package/dist/core/value.js +48 -43
- package/dist/core/workbook-media.d.ts +34 -0
- package/dist/core/workbook-media.js +44 -0
- package/dist/core/workbook-styles.d.ts +83 -1
- package/dist/core/workbook-styles.js +4 -0
- package/dist/core/workbook-theme.js +5 -3
- package/dist/core/workbook-vba.js +3 -2
- package/dist/core/workbook.d.ts +41 -3
- package/dist/core/workbook.js +46 -41
- package/dist/core/worksheet-comments.d.ts +2 -1
- package/dist/core/worksheet-comments.js +20 -13
- package/dist/core/worksheet-merges.d.ts +49 -0
- package/dist/core/worksheet-merges.js +65 -0
- package/dist/core/worksheet-model.d.ts +10 -2
- package/dist/core/worksheet-model.js +28 -9
- package/dist/core/worksheet-pictures.d.ts +3 -3
- package/dist/core/worksheet-pictures.js +7 -6
- package/dist/core/worksheet.d.ts +165 -27
- package/dist/core/worksheet.js +206 -89
- package/dist/customui/ribbon.js +2 -1
- package/dist/entries/core.d.ts +21 -14
- package/dist/entries/core.js +14 -7
- package/dist/entries/errors.d.ts +1 -0
- package/dist/entries/errors.js +1 -0
- package/dist/entries/xlsx.d.ts +1 -1
- package/dist/errors.d.ts +64 -0
- package/dist/errors.js +22 -0
- package/dist/hex.d.ts +8 -0
- package/dist/hex.js +3 -0
- 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 +21 -0
- package/dist/io/csv/delimiter.js +18 -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 +51 -22
- package/dist/io/csv/write.d.ts +19 -5
- package/dist/io/csv/write.js +24 -44
- package/dist/io/opc/inflate.js +14 -0
- package/dist/io/opc/namespaces.d.ts +16 -0
- package/dist/io/opc/namespaces.js +4 -0
- package/dist/io/opc/part-paths.d.ts +9 -0
- package/dist/io/opc/part-paths.js +5 -5
- package/dist/io/opc/read-opc.d.ts +58 -14
- package/dist/io/opc/read-opc.js +49 -21
- package/dist/io/opc/read-options.d.ts +5 -1
- package/dist/io/opc/sniff-format.d.ts +3 -3
- package/dist/io/opc/sniff-format.js +2 -2
- 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/read-policy/column-budget.js +18 -0
- 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/style/xf-style.d.ts +23 -1
- package/dist/io/style/xf-style.js +8 -0
- package/dist/io/xlsb/formula.d.ts +2 -2
- package/dist/io/xlsb/formula.js +52 -35
- package/dist/io/xlsb/primitives.js +8 -14
- package/dist/io/xlsb/ptg-functions.d.ts +19 -10
- package/dist/io/xlsb/ptg-functions.js +495 -596
- package/dist/io/xlsb/read-metadata.d.ts +7 -0
- package/dist/io/xlsb/read-metadata.js +69 -0
- package/dist/io/xlsb/read-styles.js +45 -81
- package/dist/io/xlsb/read-worksheet.d.ts +26 -1
- package/dist/io/xlsb/read-worksheet.js +148 -106
- package/dist/io/xlsb/read.d.ts +6 -3
- package/dist/io/xlsb/read.js +70 -31
- package/dist/io/xlsb/record-stream.d.ts +30 -0
- package/dist/io/xlsb/record-stream.js +20 -0
- package/dist/io/xlsb/record-types.d.ts +14 -5
- package/dist/io/xlsb/record-types.js +14 -5
- 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 +24 -9
- package/dist/io/xlsx/cell-accumulator.js +74 -21
- 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 +19 -5
- package/dist/io/xlsx/cell-value.js +45 -30
- package/dist/io/xlsx/color-xml.js +3 -2
- 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 +9 -30
- package/dist/io/xlsx/comments.js +25 -83
- package/dist/io/xlsx/conditional-formatting.d.ts +43 -23
- package/dist/io/xlsx/conditional-formatting.js +437 -138
- package/dist/io/xlsx/data-validation.d.ts +12 -7
- package/dist/io/xlsx/data-validation.js +67 -53
- package/dist/io/xlsx/edit-vba.d.ts +7 -2
- package/dist/io/xlsx/edit-vba.js +52 -52
- package/dist/io/xlsx/font-xml.d.ts +7 -0
- package/dist/io/xlsx/font-xml.js +104 -0
- package/dist/io/xlsx/hyperlinks.d.ts +19 -21
- package/dist/io/xlsx/hyperlinks.js +12 -32
- package/dist/io/xlsx/images.d.ts +49 -16
- package/dist/io/xlsx/images.js +149 -50
- package/dist/io/xlsx/namespaces.d.ts +24 -0
- package/dist/io/xlsx/namespaces.js +11 -0
- package/dist/io/xlsx/package-plan.d.ts +60 -15
- package/dist/io/xlsx/package-plan.js +83 -18
- 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 +26 -24
- 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 +59 -0
- package/dist/io/xlsx/read-parts.js +259 -0
- package/dist/io/xlsx/read-pivot.js +11 -4
- package/dist/io/xlsx/{rich-runs.d.ts → read-rich-runs.d.ts} +8 -5
- package/dist/io/xlsx/{rich-runs.js → read-rich-runs.js} +24 -4
- package/dist/io/xlsx/read-rows.d.ts +35 -11
- package/dist/io/xlsx/read-rows.js +73 -49
- package/dist/io/xlsx/read-shared-strings.js +3 -2
- package/dist/io/xlsx/read-styles.d.ts +0 -30
- package/dist/io/xlsx/read-styles.js +45 -105
- package/dist/io/xlsx/read-workbook-xml.d.ts +42 -0
- package/dist/io/xlsx/read-workbook-xml.js +175 -0
- package/dist/io/xlsx/read-worksheet.d.ts +9 -3
- package/dist/io/xlsx/read-worksheet.js +70 -177
- package/dist/io/xlsx/read.d.ts +3 -14
- package/dist/io/xlsx/read.js +74 -411
- package/dist/io/xlsx/relationships.d.ts +3 -0
- package/dist/io/xlsx/relationships.js +4 -0
- package/dist/io/xlsx/rich-text.js +1 -1
- package/dist/io/xlsx/row-position.d.ts +20 -0
- package/dist/io/xlsx/row-position.js +11 -0
- package/dist/io/xlsx/row-xml.d.ts +139 -0
- package/dist/io/xlsx/row-xml.js +222 -0
- package/dist/io/xlsx/shared-formulas.js +2 -2
- package/dist/io/xlsx/sheet-properties.d.ts +10 -1
- package/dist/io/xlsx/sheet-properties.js +182 -20
- 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 +41 -0
- package/dist/io/xlsx/style-elements.js +136 -0
- package/dist/io/xlsx/styles.d.ts +17 -3
- package/dist/io/xlsx/styles.js +13 -209
- package/dist/io/xlsx/tables.d.ts +10 -2
- package/dist/io/xlsx/tables.js +55 -52
- package/dist/io/xlsx/theme-xml.d.ts +32 -4
- package/dist/io/xlsx/theme-xml.js +106 -71
- package/dist/io/xlsx/threaded-comments.d.ts +14 -11
- package/dist/io/xlsx/threaded-comments.js +16 -8
- package/dist/io/xlsx/workbook-xml.d.ts +45 -6
- package/dist/io/xlsx/workbook-xml.js +62 -54
- package/dist/io/xlsx/worksheet-xml.d.ts +26 -70
- package/dist/io/xlsx/worksheet-xml.js +69 -282
- package/dist/io/xlsx/write-stream.d.ts +90 -19
- package/dist/io/xlsx/write-stream.js +80 -25
- package/dist/io/xlsx/write.d.ts +12 -6
- package/dist/io/xlsx/write.js +226 -123
- package/dist/rel-type.d.ts +25 -0
- package/dist/rel-type.js +9 -0
- package/dist/token-set.d.ts +13 -0
- package/dist/token-set.js +4 -1
- package/dist/vba/bytes.d.ts +16 -5
- package/dist/vba/bytes.js +27 -6
- package/dist/vba/cfb-format.d.ts +55 -0
- package/dist/vba/cfb-format.js +35 -0
- package/dist/vba/cfb-writer.d.ts +8 -0
- package/dist/vba/cfb-writer.js +40 -40
- package/dist/vba/cfb.d.ts +21 -4
- package/dist/vba/cfb.js +124 -90
- package/dist/vba/codepage.js +2 -1
- package/dist/vba/dir-records.d.ts +6 -0
- package/dist/vba/dir-records.js +10 -2
- package/dist/vba/errors.d.ts +11 -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 +66 -22
- package/dist/vba/project-editor.js +129 -119
- package/dist/vba/project.d.ts +15 -1
- package/dist/vba/project.js +38 -13
- package/dist/vba/vba-encoding.d.ts +4 -10
- package/dist/vba/vba-encoding.js +3 -17
- package/dist/xml/xml-attrs.d.ts +37 -0
- package/dist/xml/xml-attrs.js +30 -0
- package/dist/xml/xml-chars.d.ts +40 -0
- package/dist/xml/xml-chars.js +12 -0
- package/dist/xml/xml-namespaces.d.ts +31 -0
- package/dist/xml/xml-namespaces.js +50 -0
- package/dist/xml/xml-read.d.ts +101 -11
- package/dist/xml/xml-read.js +150 -18
- package/dist/xml/xml-scan.d.ts +25 -34
- package/dist/xml/xml-scan.js +69 -46
- package/dist/xml/xml.d.ts +59 -26
- package/dist/xml/xml.js +45 -23
- package/package.json +15 -9
- package/skills/ts-xlsx-upstream/SKILL.md +21 -13
- package/dist/core/modern-functions.d.ts +0 -1
package/dist/io/xlsx/tables.js
CHANGED
|
@@ -1,27 +1,23 @@
|
|
|
1
1
|
import { encodeAddress, tryDecodeRange } from '../../core/address.js';
|
|
2
|
-
import {
|
|
2
|
+
import { mangleFormula, unmangleFunctions } from '../../core/formula.js';
|
|
3
|
+
import { isTotalsRowFunction, TABLE_STYLE_FLAGS, } from '../../core/table.js';
|
|
4
|
+
import { numInteger } from '../../xml/xml-attrs.js';
|
|
3
5
|
import { parseXml, TextCapture } from '../../xml/xml-read.js';
|
|
4
|
-
import {
|
|
5
|
-
import { boolAttr, escapeAttr, escapeText, XML_DECLARATION } from '../../xml/xml.js';
|
|
6
|
+
import { boolTristate, localName } from '../../xml/xml-scan.js';
|
|
7
|
+
import { boolAttr, checkedToken, escapeAttr, escapeText, XML_DECLARATION } from '../../xml/xml.js';
|
|
6
8
|
import { NS } from './relationships.js';
|
|
7
|
-
export function tableXml(table, id) {
|
|
9
|
+
export function tableXml(table, id, formulaNames) {
|
|
8
10
|
const name = escapeAttr(table.name);
|
|
9
|
-
const displayName = escapeAttr(table.displayName);
|
|
10
11
|
const headerRowCount = table.headerRow ? '' : ' headerRowCount="0"';
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
else if (table.totalsRowShown !== undefined) {
|
|
16
|
-
totals = ` totalsRowShown="${table.totalsRowShown ? '1' : '0'}"`;
|
|
17
|
-
}
|
|
18
|
-
else {
|
|
19
|
-
totals = '';
|
|
20
|
-
}
|
|
12
|
+
const totals = table.totalsRow
|
|
13
|
+
? ' totalsRowCount="1"'
|
|
14
|
+
: boolAttr('totalsRowShown', table.totalsRowShown);
|
|
21
15
|
const autoFilter = table.autoFilterRef !== undefined ? `<autoFilter ref="${table.autoFilterRef}"/>` : '';
|
|
22
|
-
const columns = table.columns
|
|
16
|
+
const columns = table.columns
|
|
17
|
+
.map((column, i) => tableColumnXml(column, i + 1, formulaNames))
|
|
18
|
+
.join('');
|
|
23
19
|
return (XML_DECLARATION +
|
|
24
|
-
`<table xmlns="${NS.main}" id="${id}" name="${name}" displayName="${
|
|
20
|
+
`<table xmlns="${NS.main}" id="${id}" name="${name}" displayName="${name}" ` +
|
|
25
21
|
`ref="${table.range}"${headerRowCount}${totals}>` +
|
|
26
22
|
autoFilter +
|
|
27
23
|
`<tableColumns count="${table.columns.length}">${columns}</tableColumns>` +
|
|
@@ -36,29 +32,32 @@ function tableStyleInfoXml(style) {
|
|
|
36
32
|
let attrs = '';
|
|
37
33
|
if (style.name !== undefined)
|
|
38
34
|
attrs += ` name="${escapeAttr(style.name)}"`;
|
|
39
|
-
attrs +=
|
|
40
|
-
boolAttr('showFirstColumn', style.showFirstColumn) +
|
|
41
|
-
boolAttr('showLastColumn', style.showLastColumn) +
|
|
42
|
-
boolAttr('showRowStripes', style.showRowStripes) +
|
|
43
|
-
boolAttr('showColumnStripes', style.showColumnStripes);
|
|
35
|
+
attrs += TABLE_STYLE_FLAGS.map((flag) => boolAttr(flag, style[flag])).join('');
|
|
44
36
|
return `<tableStyleInfo${attrs}/>`;
|
|
45
37
|
}
|
|
46
|
-
function tableColumnXml(column, id) {
|
|
38
|
+
function tableColumnXml(column, id, formulaNames) {
|
|
47
39
|
let attrs = `id="${id}" name="${escapeAttr(column.name)}"`;
|
|
48
40
|
if (column.totalsRowLabel !== undefined) {
|
|
49
41
|
attrs += ` totalsRowLabel="${escapeAttr(column.totalsRowLabel)}"`;
|
|
50
42
|
}
|
|
51
43
|
if (column.totalsRowFunction !== undefined) {
|
|
52
|
-
attrs += ` totalsRowFunction="${
|
|
44
|
+
attrs += ` totalsRowFunction="${checkedToken(column.totalsRowFunction, isTotalsRowFunction, 'totals row function')}"`;
|
|
45
|
+
}
|
|
46
|
+
let children = '';
|
|
47
|
+
if (column.calculatedColumnFormula !== undefined) {
|
|
48
|
+
const stored = mangleFormula(column.calculatedColumnFormula, formulaNames);
|
|
49
|
+
children += `<calculatedColumnFormula>${escapeText(stored)}</calculatedColumnFormula>`;
|
|
53
50
|
}
|
|
54
51
|
if (column.totalsRowFormula !== undefined) {
|
|
55
|
-
|
|
52
|
+
const stored = mangleFormula(column.totalsRowFormula, formulaNames);
|
|
53
|
+
children += `<totalsRowFormula>${escapeText(stored)}</totalsRowFormula>`;
|
|
56
54
|
}
|
|
57
|
-
return
|
|
55
|
+
return children === ''
|
|
56
|
+
? `<tableColumn ${attrs}/>`
|
|
57
|
+
: `<tableColumn ${attrs}>${children}</tableColumn>`;
|
|
58
58
|
}
|
|
59
|
-
export function parseTable(xml) {
|
|
59
|
+
export function parseTable(xml, definedNames) {
|
|
60
60
|
let name;
|
|
61
|
-
let displayName;
|
|
62
61
|
let ref;
|
|
63
62
|
let headerRowCount = 1;
|
|
64
63
|
let totalsRowCount = 0;
|
|
@@ -66,18 +65,17 @@ export function parseTable(xml) {
|
|
|
66
65
|
let style;
|
|
67
66
|
let hasAutoFilter = false;
|
|
68
67
|
const columns = [];
|
|
69
|
-
|
|
68
|
+
let currentColumn;
|
|
69
|
+
const formula = new TextCapture(['totalsRowFormula', 'calculatedColumnFormula']);
|
|
70
70
|
parseXml(xml, {
|
|
71
71
|
onOpen(elementName, attrs, selfClosing) {
|
|
72
72
|
switch (localName(elementName)) {
|
|
73
73
|
case 'table':
|
|
74
|
-
name = attrs.
|
|
75
|
-
displayName = attrs.displayName ?? attrs.name;
|
|
74
|
+
name = attrs.displayName ?? attrs.name;
|
|
76
75
|
ref = attrs.ref;
|
|
77
76
|
headerRowCount = numInteger(attrs.headerRowCount, 0) ?? headerRowCount;
|
|
78
77
|
totalsRowCount = numInteger(attrs.totalsRowCount, 0) ?? totalsRowCount;
|
|
79
|
-
|
|
80
|
-
totalsRowShown = boolPresent(attrs.totalsRowShown);
|
|
78
|
+
totalsRowShown = boolTristate(attrs.totalsRowShown);
|
|
81
79
|
break;
|
|
82
80
|
case 'autoFilter':
|
|
83
81
|
hasAutoFilter = true;
|
|
@@ -86,22 +84,21 @@ export function parseTable(xml) {
|
|
|
86
84
|
const captured = {};
|
|
87
85
|
if (attrs.name !== undefined)
|
|
88
86
|
captured.name = attrs.name;
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if (attrs.showRowStripes !== undefined)
|
|
94
|
-
captured.showRowStripes = boolPresent(attrs.showRowStripes);
|
|
95
|
-
if (attrs.showColumnStripes !== undefined) {
|
|
96
|
-
captured.showColumnStripes = boolPresent(attrs.showColumnStripes);
|
|
87
|
+
for (const flag of TABLE_STYLE_FLAGS) {
|
|
88
|
+
const value = boolTristate(attrs[flag]);
|
|
89
|
+
if (value !== undefined)
|
|
90
|
+
captured[flag] = value;
|
|
97
91
|
}
|
|
98
92
|
style = captured;
|
|
99
93
|
break;
|
|
100
94
|
}
|
|
101
95
|
case 'tableColumn': {
|
|
102
|
-
if (attrs.name === undefined)
|
|
96
|
+
if (attrs.name === undefined) {
|
|
97
|
+
currentColumn = undefined;
|
|
103
98
|
break;
|
|
99
|
+
}
|
|
104
100
|
const column = { name: attrs.name };
|
|
101
|
+
currentColumn = column;
|
|
105
102
|
if (attrs.totalsRowLabel !== undefined)
|
|
106
103
|
column.totalsRowLabel = attrs.totalsRowLabel;
|
|
107
104
|
if (attrs.totalsRowFunction !== undefined &&
|
|
@@ -112,20 +109,24 @@ export function parseTable(xml) {
|
|
|
112
109
|
break;
|
|
113
110
|
}
|
|
114
111
|
case 'totalsRowFormula':
|
|
115
|
-
|
|
112
|
+
case 'calculatedColumnFormula':
|
|
113
|
+
formula.open(localName(elementName), selfClosing);
|
|
116
114
|
break;
|
|
117
115
|
}
|
|
118
116
|
},
|
|
119
117
|
onText(text) {
|
|
120
|
-
|
|
118
|
+
formula.text(text);
|
|
121
119
|
},
|
|
122
120
|
onClose(elementName) {
|
|
123
|
-
const
|
|
124
|
-
|
|
121
|
+
const local = localName(elementName);
|
|
122
|
+
const text = formula.close(local);
|
|
123
|
+
if (text === undefined || currentColumn === undefined)
|
|
125
124
|
return;
|
|
126
|
-
const
|
|
127
|
-
if (
|
|
128
|
-
|
|
125
|
+
const formulaText = unmangleFunctions(text, definedNames);
|
|
126
|
+
if (local === 'totalsRowFormula')
|
|
127
|
+
currentColumn.totalsRowFormula = formulaText;
|
|
128
|
+
else
|
|
129
|
+
currentColumn.calculatedColumnFormula = formulaText;
|
|
129
130
|
},
|
|
130
131
|
});
|
|
131
132
|
if (name === undefined || ref === undefined || columns.length === 0)
|
|
@@ -133,15 +134,17 @@ export function parseTable(xml) {
|
|
|
133
134
|
const decoded = tryDecodeRange(ref);
|
|
134
135
|
if (decoded === undefined)
|
|
135
136
|
return undefined;
|
|
136
|
-
const { top, left, bottom } = decoded;
|
|
137
|
-
if (top === undefined || left === undefined || bottom === undefined)
|
|
137
|
+
const { top, left, bottom, right } = decoded;
|
|
138
|
+
if (top === undefined || left === undefined || bottom === undefined || right === undefined) {
|
|
139
|
+
return undefined;
|
|
140
|
+
}
|
|
141
|
+
if (columns.length !== right - left + 1)
|
|
138
142
|
return undefined;
|
|
139
143
|
const headerRow = headerRowCount !== 0;
|
|
140
144
|
const totalsRow = totalsRowCount > 0;
|
|
141
145
|
const dataRows = bottom - top + 1 - (headerRow ? 1 : 0) - (totalsRow ? 1 : 0);
|
|
142
146
|
const options = {
|
|
143
147
|
name,
|
|
144
|
-
displayName: displayName ?? name,
|
|
145
148
|
ref: encodeAddress(left, top),
|
|
146
149
|
columns,
|
|
147
150
|
rowCount: Math.max(0, dataRows),
|
|
@@ -1,16 +1,35 @@
|
|
|
1
|
-
import
|
|
1
|
+
import type { AssertNever } from '../../core/internal.ts';
|
|
2
|
+
import { type ThemeColorScheme, type ThemeColorSlot, type ThemeFontScheme, type ThemeOverrides } from '../../core/theme.ts';
|
|
2
3
|
/**
|
|
3
4
|
* Extract the colour scheme from a theme part. Returns only the slots the part actually declares in a
|
|
4
5
|
* colour model this reader understands; an unrecognised one is dropped rather than guessed at, so a
|
|
5
6
|
* caller can tell "the theme says nothing here" from "the theme says black".
|
|
6
7
|
*
|
|
7
|
-
* Reads the `<
|
|
8
|
-
* neither participates in resolving a colour, and scanning the whole part would let
|
|
8
|
+
* Reads the `<clrScheme>` block alone. A theme carries a font scheme and a format scheme too, but
|
|
9
|
+
* neither participates in resolving a colour, and scanning the whole part would let an `<srgbClr>`
|
|
9
10
|
* buried in a gradient stop masquerade as a scheme slot.
|
|
10
11
|
*/
|
|
11
12
|
export declare function parseThemeColorScheme(themeXml: string): ThemeColorScheme;
|
|
12
|
-
/** Extract the major/minor latin typefaces from a theme part's `<
|
|
13
|
+
/** Extract the major/minor latin typefaces from a theme part's `<fontScheme>`. */
|
|
13
14
|
export declare function parseThemeFontScheme(themeXml: string): ThemeFontScheme;
|
|
15
|
+
/** {@link parseThemeColorScheme} and {@link parseThemeFontScheme} from one scan of the part, for the
|
|
16
|
+
* reader, which wants both. */
|
|
17
|
+
export declare function parseThemeSchemes(themeXml: string): {
|
|
18
|
+
readonly colors: ThemeColorScheme;
|
|
19
|
+
readonly fonts: ThemeFontScheme;
|
|
20
|
+
};
|
|
21
|
+
declare const SCHEME_ELEMENT_ORDER: readonly ["dk1", "lt1", "dk2", "lt2", "accent1", "accent2", "accent3", "accent4", "accent5", "accent6", "hlink", "folHlink"];
|
|
22
|
+
/**
|
|
23
|
+
* The half of the proof the list above owes: `satisfies` covers "no invented slot", this covers "no
|
|
24
|
+
* omission". A slot missing here is not a slot written in the wrong place -- it is a slot dropped
|
|
25
|
+
* from every rewritten `<a:clrScheme>`, verbatim source element and all, so a caller who authored it
|
|
26
|
+
* gets back a theme that silently does not carry it.
|
|
27
|
+
*
|
|
28
|
+
* This is the one list in the library where the schema order and the index order differ, which makes
|
|
29
|
+
* it the one a new slot is least likely to be added to by hand. `DEFAULT_THEME_COLOR_SCHEME` proves
|
|
30
|
+
* the same completeness on the model side.
|
|
31
|
+
*/
|
|
32
|
+
export type EverySlotIsInSchemaOrder = AssertNever<Exclude<ThemeColorSlot, (typeof SCHEME_ELEMENT_ORDER)[number]>>;
|
|
14
33
|
/**
|
|
15
34
|
* Apply authored colour/font overrides to a theme part, returning the new part text.
|
|
16
35
|
*
|
|
@@ -24,6 +43,14 @@ export declare function parseThemeFontScheme(themeXml: string): ThemeFontScheme;
|
|
|
24
43
|
* its value. That matters for `dk1`/`lt1`, which Excel writes as `<a:sysClr val="windowText"
|
|
25
44
|
* lastClr="000000"/>`: rewriting those as `<a:srgbClr>` would pin them to one machine's resolved
|
|
26
45
|
* window colours and break dark-mode following.
|
|
46
|
+
*
|
|
47
|
+
* The edit is made by *offset*, not by pattern. What it replaces was three container-scanning regular
|
|
48
|
+
* expressions in the one file whose own header condemns that pattern, on a path a preserved source
|
|
49
|
+
* theme's bytes reach: `[\s\S]*?` terminated on a `</clrScheme>` inside a comment, `[^>]*` missed a
|
|
50
|
+
* `<latin typeface="X"></latin>` and dropped the override silently, and the DrawingML prefix -- read
|
|
51
|
+
* out of the part's own root element, where an NCName may legally contain `.` and `-` -- was
|
|
52
|
+
* interpolated into the pattern unescaped. {@link elementRange} answers the same question with the
|
|
53
|
+
* scanner, and splicing at its offsets is what makes an unoverridden part come back byte for byte.
|
|
27
54
|
*/
|
|
28
55
|
export declare function applyThemeOverrides(baseXml: string, overrides: ThemeOverrides): string;
|
|
29
56
|
/**
|
|
@@ -35,3 +62,4 @@ export declare function applyThemeOverrides(baseXml: string, overrides: ThemeOve
|
|
|
35
62
|
* built from scratch rather than read from a file.
|
|
36
63
|
*/
|
|
37
64
|
export declare const DEFAULT_THEME_XML: string;
|
|
65
|
+
export {};
|
|
@@ -1,41 +1,82 @@
|
|
|
1
|
-
import { DEFAULT_THEME_COLOR_SCHEME, isThemeColorSlot,
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import { DEFAULT_THEME_COLOR_SCHEME, isThemeColorSlot, isThemeRgb, } from '../../core/theme.js';
|
|
2
|
+
import { elementRange, parseXml } from '../../xml/xml-read.js';
|
|
3
|
+
import { localName } from '../../xml/xml-scan.js';
|
|
4
|
+
import { startTag } from '../../xml/xml.js';
|
|
5
|
+
const COLOR_ELEMENTS = new Set(['srgbClr', 'sysClr']);
|
|
6
|
+
function readThemeScheme(themeXml) {
|
|
7
|
+
const colors = {};
|
|
8
|
+
const elements = {};
|
|
9
|
+
const fonts = {};
|
|
10
|
+
let prefix = '';
|
|
11
|
+
let container;
|
|
12
|
+
let slot;
|
|
13
|
+
let fontSlot;
|
|
14
|
+
parseXml(themeXml, {
|
|
15
|
+
onOpen(name, attrs) {
|
|
16
|
+
const local = localName(name);
|
|
17
|
+
if (local === 'theme') {
|
|
18
|
+
const colon = name.indexOf(':');
|
|
19
|
+
prefix = colon === -1 ? '' : name.slice(0, colon + 1);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
if (local === 'clrScheme' || local === 'fontScheme') {
|
|
23
|
+
container = local;
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (container === 'clrScheme') {
|
|
27
|
+
if (isThemeColorSlot(local)) {
|
|
28
|
+
slot = local;
|
|
29
|
+
}
|
|
30
|
+
else if (slot !== undefined && COLOR_ELEMENTS.has(local)) {
|
|
31
|
+
const value = local === 'sysClr' ? attrs.lastClr : attrs.val;
|
|
32
|
+
if (value !== undefined && isThemeRgb(value))
|
|
33
|
+
colors[slot] = value.toUpperCase();
|
|
34
|
+
elements[slot] = startTag(name, attrs, true);
|
|
35
|
+
slot = undefined;
|
|
36
|
+
}
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (container === 'fontScheme') {
|
|
40
|
+
if (local === 'majorFont')
|
|
41
|
+
fontSlot = 'major';
|
|
42
|
+
else if (local === 'minorFont')
|
|
43
|
+
fontSlot = 'minor';
|
|
44
|
+
else if (local === 'latin' && fontSlot !== undefined && attrs.typeface !== undefined) {
|
|
45
|
+
fonts[fontSlot] ??= attrs.typeface;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
onClose(name) {
|
|
50
|
+
const local = localName(name);
|
|
51
|
+
if (local === 'clrScheme' || local === 'fontScheme') {
|
|
52
|
+
container = undefined;
|
|
53
|
+
slot = undefined;
|
|
54
|
+
fontSlot = undefined;
|
|
55
|
+
}
|
|
56
|
+
else if (local === 'majorFont' || local === 'minorFont') {
|
|
57
|
+
fontSlot = undefined;
|
|
58
|
+
}
|
|
59
|
+
else if (slot !== undefined && local === slot) {
|
|
60
|
+
slot = undefined;
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
});
|
|
9
64
|
const scheme = {};
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
scheme[slot] = value;
|
|
19
|
-
}
|
|
20
|
-
return scheme;
|
|
65
|
+
if (fonts.major !== undefined)
|
|
66
|
+
scheme.major = fonts.major;
|
|
67
|
+
if (fonts.minor !== undefined)
|
|
68
|
+
scheme.minor = fonts.minor;
|
|
69
|
+
return { colors, elements, fonts: scheme, prefix };
|
|
70
|
+
}
|
|
71
|
+
export function parseThemeColorScheme(themeXml) {
|
|
72
|
+
return readThemeScheme(themeXml).colors;
|
|
21
73
|
}
|
|
22
74
|
export function parseThemeFontScheme(themeXml) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
const typeface = /<a:latin\b[^>]*\btypeface="([^"]*)"/.exec(font?.[1] ?? '')?.[1];
|
|
29
|
-
return typeface === undefined ? undefined : decodeEntities(typeface);
|
|
30
|
-
};
|
|
31
|
-
const scheme = {};
|
|
32
|
-
const major = face('majorFont');
|
|
33
|
-
const minor = face('minorFont');
|
|
34
|
-
if (major !== undefined)
|
|
35
|
-
scheme.major = major;
|
|
36
|
-
if (minor !== undefined)
|
|
37
|
-
scheme.minor = minor;
|
|
38
|
-
return scheme;
|
|
75
|
+
return readThemeScheme(themeXml).fonts;
|
|
76
|
+
}
|
|
77
|
+
export function parseThemeSchemes(themeXml) {
|
|
78
|
+
const { colors, fonts } = readThemeScheme(themeXml);
|
|
79
|
+
return { colors, fonts };
|
|
39
80
|
}
|
|
40
81
|
const SCHEME_ELEMENT_ORDER = [
|
|
41
82
|
'dk1',
|
|
@@ -52,49 +93,43 @@ const SCHEME_ELEMENT_ORDER = [
|
|
|
52
93
|
'folHlink',
|
|
53
94
|
];
|
|
54
95
|
export function applyThemeOverrides(baseXml, overrides) {
|
|
55
|
-
|
|
96
|
+
const { elements: sourceElements, prefix } = readThemeScheme(baseXml);
|
|
97
|
+
const edits = [];
|
|
56
98
|
const colors = overrides.colors ?? {};
|
|
57
99
|
if (Object.keys(colors).length > 0) {
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
100
|
+
const scheme = elementRange(baseXml, ['clrScheme']);
|
|
101
|
+
if (scheme !== undefined) {
|
|
102
|
+
const body = SCHEME_ELEMENT_ORDER.map((slot) => {
|
|
103
|
+
const authored = colors[slot];
|
|
104
|
+
const inner = authored !== undefined
|
|
105
|
+
? `<${prefix}srgbClr val="${authored}"/>`
|
|
106
|
+
: (sourceElements[slot] ??
|
|
107
|
+
`<${prefix}srgbClr val="${DEFAULT_THEME_COLOR_SCHEME[slot]}"/>`);
|
|
108
|
+
return `<${prefix}${slot}>${inner}</${prefix}${slot}>`;
|
|
109
|
+
}).join('');
|
|
110
|
+
edits.push({ start: scheme.contentStart, end: scheme.contentEnd, text: body });
|
|
111
|
+
}
|
|
67
112
|
}
|
|
68
113
|
const { major, minor } = overrides.fonts ?? {};
|
|
114
|
+
const latin = (which, typeface) => {
|
|
115
|
+
const found = elementRange(baseXml, [which, 'latin']);
|
|
116
|
+
if (found === undefined)
|
|
117
|
+
return;
|
|
118
|
+
edits.push({
|
|
119
|
+
start: found.start,
|
|
120
|
+
end: found.end,
|
|
121
|
+
text: startTag(found.name, { ...found.attrs, typeface }, true),
|
|
122
|
+
});
|
|
123
|
+
};
|
|
69
124
|
if (major !== undefined)
|
|
70
|
-
|
|
125
|
+
latin('majorFont', major);
|
|
71
126
|
if (minor !== undefined)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const block = /<a:clrScheme\b[^>]*>([\s\S]*?)<\/a:clrScheme>/.exec(themeXml);
|
|
77
|
-
if (block === null)
|
|
78
|
-
return {};
|
|
79
|
-
const elements = {};
|
|
80
|
-
const pattern = /<a:(dk1|lt1|dk2|lt2|accent[1-6]|hlink|folHlink)>([\s\S]*?)<\/a:\1>|<a:(dk1|lt1|dk2|lt2|accent[1-6]|hlink|folHlink)\/>/g;
|
|
81
|
-
for (const match of (block[1] ?? '').matchAll(pattern)) {
|
|
82
|
-
const slot = match[1] ?? match[3];
|
|
83
|
-
if (slot === undefined || !isThemeColorSlot(slot))
|
|
84
|
-
continue;
|
|
85
|
-
const inner = match[2];
|
|
86
|
-
if (inner !== undefined && inner !== '')
|
|
87
|
-
elements[slot] = inner;
|
|
127
|
+
latin('minorFont', minor);
|
|
128
|
+
let xml = baseXml;
|
|
129
|
+
for (const edit of edits.sort((a, b) => b.start - a.start)) {
|
|
130
|
+
xml = xml.slice(0, edit.start) + edit.text + xml.slice(edit.end);
|
|
88
131
|
}
|
|
89
|
-
return
|
|
90
|
-
}
|
|
91
|
-
function replaceBlockBody(xml, name, body) {
|
|
92
|
-
const pattern = new RegExp(`(<a:${name}\\b[^>]*>)[\\s\\S]*?(</a:${name}>)`);
|
|
93
|
-
return xml.replace(pattern, (_all, open, close) => `${open}${body}${close}`);
|
|
94
|
-
}
|
|
95
|
-
function replaceLatinTypeface(xml, which, typeface) {
|
|
96
|
-
const pattern = new RegExp(`(<a:${which}\\b[^>]*>[\\s\\S]*?<a:latin\\b)[^>]*(/>)`);
|
|
97
|
-
return xml.replace(pattern, (_all, open, close) => `${open} typeface="${escapeAttr(typeface)}"${close}`);
|
|
132
|
+
return xml;
|
|
98
133
|
}
|
|
99
134
|
export const DEFAULT_THEME_XML = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n' +
|
|
100
135
|
'<a:theme xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" name="Office Theme">' +
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import { type CommentThread, type MentionRef, type Person } from '../../core/comment-thread.ts';
|
|
2
|
-
/**
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
readonly providerId?: string;
|
|
11
|
-
}
|
|
2
|
+
/**
|
|
3
|
+
* A registered author of threaded comments: one `<person>` of `xl/persons/person.xml`, which is the
|
|
4
|
+
* model's own {@link Person} with nothing added. Declared as an alias for the same reason
|
|
5
|
+
* {@link ParsedMention} is: this file's write half already emits `Person` directly, so re-declaring
|
|
6
|
+
* the four fields here made one feature module name one shape two ways, with nothing keeping the two
|
|
7
|
+
* in step.
|
|
8
|
+
*/
|
|
9
|
+
export type ParsedPerson = Person;
|
|
12
10
|
/**
|
|
13
11
|
* One `<mention>` of a message's `<mentions>` block: the model's own {@link MentionRef}, which is
|
|
14
12
|
* exactly the four wire attributes with nothing resolved yet. Declaring it a second time here would
|
|
@@ -80,8 +78,13 @@ export declare function buildCommentThreads(messages: readonly ParsedThreadedCom
|
|
|
80
78
|
*
|
|
81
79
|
* A thread with no messages writes nothing: it has neither text to say nor a head id for its replies and
|
|
82
80
|
* its legacy fallback to hang off.
|
|
81
|
+
*
|
|
82
|
+
* A message with no author is written under the null GUID, Excel's own spelling for one. `personById` is
|
|
83
|
+
* the workbook's registry, and a message naming an id it does not hold is refused.
|
|
84
|
+
*
|
|
85
|
+
* @throws {AuthoringError} if a message's author is not a registered person.
|
|
83
86
|
*/
|
|
84
|
-
export declare function threadedCommentsXml(threads: readonly CommentThread[]): string;
|
|
87
|
+
export declare function threadedCommentsXml(threads: readonly CommentThread[], personById: (id: string) => Person | undefined): string;
|
|
85
88
|
/**
|
|
86
89
|
* Serialise the workbook's identity registry into `xl/persons/person.xml`: singular and unnumbered,
|
|
87
90
|
* unlike the per-sheet thread parts.
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { encodeAddress, tryDecodeCellRef } from '../../core/address.js';
|
|
2
2
|
import { MENTION_OFFSET_MAX, } from '../../core/comment-thread.js';
|
|
3
|
+
import { AuthoringError, quoted } from '../../errors.js';
|
|
4
|
+
import { decodeSpreadsheetText, numInteger } from '../../xml/xml-attrs.js';
|
|
3
5
|
import { parseXml, TextCapture } from '../../xml/xml-read.js';
|
|
4
|
-
import { boolStrict,
|
|
6
|
+
import { boolStrict, localName } from '../../xml/xml-scan.js';
|
|
5
7
|
import { escapeAttr, escapeSpreadsheetText, textAttr, XML_DECLARATION } from '../../xml/xml.js';
|
|
6
8
|
import { THREADED_COMMENTS_NS } from './namespaces.js';
|
|
7
9
|
export function parsePersons(xml) {
|
|
@@ -131,8 +133,7 @@ function commentFrom(message, personById) {
|
|
|
131
133
|
id: message.id,
|
|
132
134
|
text: message.text,
|
|
133
135
|
mentions: message.mentions.map((mention) => mentionOf(mention, personById)),
|
|
134
|
-
...(author !== undefined ? { author } : {}),
|
|
135
|
-
...(message.personId !== undefined ? { personId: message.personId } : {}),
|
|
136
|
+
...(author !== undefined ? { author, personId: author.id } : {}),
|
|
136
137
|
...(message.date !== undefined ? { date: message.date } : {}),
|
|
137
138
|
};
|
|
138
139
|
}
|
|
@@ -146,27 +147,34 @@ function mentionOf(mention, personById) {
|
|
|
146
147
|
...(mention.mentionId !== undefined ? { mentionId: mention.mentionId } : {}),
|
|
147
148
|
};
|
|
148
149
|
}
|
|
149
|
-
export function threadedCommentsXml(threads) {
|
|
150
|
+
export function threadedCommentsXml(threads, personById) {
|
|
150
151
|
const messages = threads.flatMap((thread) => {
|
|
151
152
|
const [head, ...replies] = thread.comments;
|
|
152
153
|
if (head === undefined)
|
|
153
154
|
return [];
|
|
154
155
|
return [
|
|
155
|
-
threadedCommentXml(thread.ref, head, thread.resolved ? ' done="1"' : ''),
|
|
156
|
-
...replies.map((reply) => threadedCommentXml(thread.ref, reply, ` parentId="${escapeAttr(head.id)}"
|
|
156
|
+
threadedCommentXml(thread.ref, head, thread.resolved ? ' done="1"' : '', personById),
|
|
157
|
+
...replies.map((reply) => threadedCommentXml(thread.ref, reply, ` parentId="${escapeAttr(head.id)}"`, personById)),
|
|
157
158
|
];
|
|
158
159
|
});
|
|
159
160
|
return (XML_DECLARATION +
|
|
160
161
|
`<ThreadedComments xmlns="${THREADED_COMMENTS_NS}">${messages.join('')}</ThreadedComments>`);
|
|
161
162
|
}
|
|
162
|
-
function threadedCommentXml(ref, comment, tail) {
|
|
163
|
+
function threadedCommentXml(ref, comment, tail, personById) {
|
|
163
164
|
const date = textAttr('dT', comment.date);
|
|
164
|
-
const
|
|
165
|
+
const personId = comment.personId ?? UNKNOWN_AUTHOR;
|
|
166
|
+
if (personId !== UNKNOWN_AUTHOR && personById(personId) === undefined) {
|
|
167
|
+
throw new AuthoringError(`cannot write the threaded comment ${quoted(comment.id)}: its author ${quoted(personId)} is not ` +
|
|
168
|
+
'a registered person. Register it with Workbook.addPerson, or leave personId out for a ' +
|
|
169
|
+
'comment with no recorded author');
|
|
170
|
+
}
|
|
171
|
+
const person = textAttr('personId', personId);
|
|
165
172
|
return (`<threadedComment ref="${escapeAttr(ref)}"${date}${person} id="${escapeAttr(comment.id)}"${tail}>` +
|
|
166
173
|
`<text>${escapeSpreadsheetText(comment.text)}</text>` +
|
|
167
174
|
mentionsXml(comment.mentions) +
|
|
168
175
|
'</threadedComment>');
|
|
169
176
|
}
|
|
177
|
+
const UNKNOWN_AUTHOR = '{00000000-0000-0000-0000-000000000000}';
|
|
170
178
|
function mentionsXml(mentions) {
|
|
171
179
|
const entries = mentions.flatMap(({ personId, mentionId, startIndex, length }) => {
|
|
172
180
|
if (mentionId === undefined)
|
|
@@ -1,12 +1,51 @@
|
|
|
1
1
|
import type { Workbook, WorkbookProperties } from '../../core/workbook.ts';
|
|
2
|
-
import type { PivotPlan,
|
|
2
|
+
import type { PivotPlan, PlannedRelationship, PreservedPartPlan, PreservedWorkbookReferencePlan, TablePlan } from './package-plan.ts';
|
|
3
3
|
export type PreservedWorkbookRel = PreservedWorkbookReferencePlan & {
|
|
4
4
|
readonly relId: string;
|
|
5
5
|
};
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
/**
|
|
7
|
+
* The workbook part's relationships, recorded once by the writer's `planWorkbookRelationships`, with
|
|
8
|
+
* the ids the workbook body cites picked out of them.
|
|
9
|
+
*
|
|
10
|
+
* Declared here because the body is the consumer of the ids: it references them from `<sheet r:id>`.
|
|
11
|
+
* The body used to spell that reference `rId${i + 1}`, which agreed with the allocator only because
|
|
12
|
+
* sheets happen to be the first ids it hands out. Anything laid ahead of them would have re-pointed
|
|
13
|
+
* every sheet at the wrong part, and the package would have stayed schema-valid while doing it -- a
|
|
14
|
+
* class of corruption no validator catches and no round-trip through this library notices, because
|
|
15
|
+
* the reader resolves the same wrong ids consistently.
|
|
16
|
+
*/
|
|
17
|
+
export interface WorkbookRelPlan {
|
|
18
|
+
readonly sheetRelIds: readonly string[];
|
|
19
|
+
readonly preservedWorkbookRels: readonly PreservedWorkbookRel[];
|
|
20
|
+
/** Every relationship the workbook's `.rels` part declares, in id order. */
|
|
21
|
+
readonly relationships: readonly PlannedRelationship[];
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* What `[Content_Types].xml` needs to know about a package: every part family it declares.
|
|
25
|
+
*
|
|
26
|
+
* One object rather than twelve positional parameters, eight of them arrays and four of those bare
|
|
27
|
+
* `readonly number[]`, which two adjacent ones could be silently transposed between. That is the same
|
|
28
|
+
* hazard `SheetReferences`, `SheetPlan` and `WorksheetXmlInputs` remove elsewhere in this writer, and
|
|
29
|
+
* it travels the whole way down: the two functions that render from this take it as it stands rather
|
|
30
|
+
* than re-scattering its fields across their own parameter lists.
|
|
31
|
+
*/
|
|
32
|
+
export interface ContentTypeInputs {
|
|
33
|
+
readonly sheetCount: number;
|
|
34
|
+
readonly tables: readonly TablePlan[];
|
|
35
|
+
readonly commentNumbers: readonly number[];
|
|
36
|
+
readonly drawingNumbers: readonly number[];
|
|
37
|
+
readonly printerSettingsNumbers: readonly number[];
|
|
38
|
+
readonly mediaExtensions: readonly string[];
|
|
39
|
+
readonly hasSharedStrings: boolean;
|
|
40
|
+
readonly preservedParts: readonly PreservedPartPlan[];
|
|
41
|
+
readonly pivots: readonly PivotPlan[];
|
|
42
|
+
readonly preservedWorkbookRefs: readonly PreservedWorkbookReferencePlan[];
|
|
43
|
+
readonly threadedCommentNumbers: readonly number[];
|
|
44
|
+
readonly hasPersons: boolean;
|
|
45
|
+
readonly hasCellMetadata: boolean;
|
|
46
|
+
readonly hasRichValues: boolean;
|
|
47
|
+
}
|
|
48
|
+
export declare function contentTypesXml(inputs: ContentTypeInputs): string;
|
|
49
|
+
export declare function workbookXml(workbook: Workbook, plan: WorkbookRelPlan, pivots: readonly PivotPlan[]): string;
|
|
11
50
|
export declare function corePropsXml(properties: WorkbookProperties): string;
|
|
12
51
|
export declare function appPropsXml(properties: WorkbookProperties): string;
|