@shbernal/ts-xlsx 2.0.0 → 2.1.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 +11 -0
- package/dist/core/autofilter.js +35 -10
- package/dist/core/color-resolution.d.ts +1 -1
- package/dist/core/color-resolution.js +7 -8
- 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 +88 -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 +34 -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 +4 -0
- package/dist/core/merge.d.ts +12 -1
- package/dist/core/merge.js +36 -2
- package/dist/core/page-setup.d.ts +16 -2
- package/dist/core/page-setup.js +8 -1
- package/dist/core/pivot-table.d.ts +2 -0
- package/dist/core/pivot-table.js +25 -16
- 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/style.d.ts +27 -0
- package/dist/core/style.js +82 -67
- package/dist/core/table.d.ts +25 -11
- package/dist/core/table.js +67 -31
- package/dist/core/theme.d.ts +3 -1
- package/dist/core/theme.js +4 -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 +15 -2
- package/dist/core/worksheet.js +28 -48
- package/dist/customui/ribbon.js +24 -23
- 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 +2 -12
- package/dist/io/opc/rels.d.ts +11 -0
- package/dist/io/opc/rels.js +3 -2
- package/dist/io/style/xf-style.d.ts +7 -2
- package/dist/io/style/xf-style.js +6 -1
- package/dist/io/xlsx/cell-accumulator.d.ts +1 -3
- 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 +7 -6
- package/dist/io/xlsx/comments.js +4 -2
- package/dist/io/xlsx/conditional-formatting.d.ts +1 -1
- package/dist/io/xlsx/conditional-formatting.js +60 -45
- package/dist/io/xlsx/data-validation.d.ts +1 -1
- package/dist/io/xlsx/data-validation.js +35 -33
- package/dist/io/xlsx/hyperlinks.js +6 -14
- package/dist/io/xlsx/images.js +19 -22
- package/dist/io/xlsx/read-pivot.js +2 -8
- package/dist/io/xlsx/read-shared-strings.js +8 -47
- package/dist/io/xlsx/read-styles.js +2 -1
- package/dist/io/xlsx/read-worksheet.js +14 -16
- package/dist/io/xlsx/read.d.ts +1 -1
- package/dist/io/xlsx/read.js +44 -74
- package/dist/io/xlsx/rich-runs.d.ts +41 -7
- package/dist/io/xlsx/rich-runs.js +94 -30
- package/dist/io/xlsx/sheet-properties.d.ts +1 -1
- package/dist/io/xlsx/sheet-properties.js +13 -9
- package/dist/io/xlsx/styles.d.ts +3 -15
- package/dist/io/xlsx/styles.js +63 -88
- package/dist/io/xlsx/tables.js +13 -13
- package/dist/io/xlsx/theme-xml.js +7 -4
- package/dist/io/xlsx/threaded-comments.js +19 -19
- package/dist/io/xlsx/workbook-xml.js +14 -8
- package/dist/io/xlsx/worksheet-xml.js +34 -37
- package/dist/io/xlsx/write-stream.d.ts +8 -1
- package/dist/io/xlsx/write-stream.js +10 -11
- package/dist/sha512.d.ts +2 -0
- package/dist/sha512.js +146 -0
- package/dist/vba/bytes.d.ts +1 -2
- package/dist/vba/bytes.js +1 -12
- package/dist/vba/dir-records.d.ts +53 -0
- package/dist/vba/dir-records.js +28 -0
- package/dist/vba/project-editor.js +5 -34
- package/dist/vba/project.js +2 -18
- package/dist/xml/xml-read.d.ts +60 -0
- package/dist/xml/xml-read.js +47 -0
- package/dist/xml/xml.d.ts +43 -1
- package/dist/xml/xml.js +17 -5
- package/package.json +21 -4
package/dist/io/xlsx/styles.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { isBorderStyle, isFillPatternType, isFontScheme, isFontVerticalAlignment, isHorizontalAlignment, isNamedUnderlineStyle, isVerticalAlignment, } from '../../core/style.js';
|
|
2
2
|
import { TABLE_STYLE_ELEMENT_TYPES } from '../../core/table-style.js';
|
|
3
|
-
import { AuthoringError } from '../../errors.js';
|
|
4
3
|
import { decodeEntities } from '../../xml/xml-read.js';
|
|
5
|
-
import { assertRepresentable, escapeAttr, XML_DECLARATION } from '../../xml/xml.js';
|
|
4
|
+
import { assertRepresentable, checkedToken, escapeAttr, numberText, XML_DECLARATION, } from '../../xml/xml.js';
|
|
6
5
|
import { colorAttrs } from './color-xml.js';
|
|
7
6
|
import { MARKUP_COMPATIBILITY_NS, SPREADSHEETML_NS } from './namespaces.js';
|
|
8
7
|
const RESERVED_FILL_COUNT = 2;
|
|
@@ -21,6 +20,36 @@ const DEFAULT_FORMAT = {
|
|
|
21
20
|
quotePrefix: false,
|
|
22
21
|
xfId: 0,
|
|
23
22
|
};
|
|
23
|
+
class InternTable {
|
|
24
|
+
#entries = [];
|
|
25
|
+
#idByKey = new Map();
|
|
26
|
+
#base;
|
|
27
|
+
constructor(base) {
|
|
28
|
+
this.#base = base;
|
|
29
|
+
}
|
|
30
|
+
get entries() {
|
|
31
|
+
return this.#entries;
|
|
32
|
+
}
|
|
33
|
+
get size() {
|
|
34
|
+
return this.#entries.length;
|
|
35
|
+
}
|
|
36
|
+
intern(key, entry) {
|
|
37
|
+
const existing = this.#idByKey.get(key);
|
|
38
|
+
if (existing !== undefined)
|
|
39
|
+
return existing;
|
|
40
|
+
const id = this.#base + this.#entries.length;
|
|
41
|
+
this.#entries.push(entry);
|
|
42
|
+
this.#idByKey.set(key, id);
|
|
43
|
+
return id;
|
|
44
|
+
}
|
|
45
|
+
seed(entry) {
|
|
46
|
+
const id = this.#base + this.#entries.length;
|
|
47
|
+
this.#entries.push(entry);
|
|
48
|
+
if (!this.#idByKey.has(entry))
|
|
49
|
+
this.#idByKey.set(entry, id);
|
|
50
|
+
return id;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
24
53
|
export class StyleRegistry {
|
|
25
54
|
#defaultFontBody;
|
|
26
55
|
#font0Bodies;
|
|
@@ -31,22 +60,17 @@ export class StyleRegistry {
|
|
|
31
60
|
? [this.#defaultFontBody]
|
|
32
61
|
: [this.#defaultFontBody, fontXml(options.declaredDefaultFont)]);
|
|
33
62
|
}
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
#fontXml = [];
|
|
39
|
-
#fontIdBySignature = new Map();
|
|
40
|
-
#borderXml = [];
|
|
41
|
-
#borderIdBySignature = new Map();
|
|
63
|
+
#fills = new InternTable(RESERVED_FILL_COUNT);
|
|
64
|
+
#numFmts = new InternTable(CUSTOM_NUMFMT_BASE);
|
|
65
|
+
#fonts = new InternTable(RESERVED_FONT_COUNT);
|
|
66
|
+
#borders = new InternTable(RESERVED_BORDER_COUNT);
|
|
42
67
|
#formats = [DEFAULT_FORMAT];
|
|
43
68
|
#xfIndexBySignature = new Map();
|
|
44
69
|
#cellStyleXfs = [DEFAULT_FORMAT];
|
|
45
70
|
#cellStyleNames = [
|
|
46
71
|
{ name: 'Normal', builtinId: 0, xfId: 0 },
|
|
47
72
|
];
|
|
48
|
-
#
|
|
49
|
-
#dxfIndexByFragment = new Map();
|
|
73
|
+
#dxfs = new InternTable(0);
|
|
50
74
|
#indexedColors = [];
|
|
51
75
|
#mruColors = [];
|
|
52
76
|
#tableStyles = { styles: [] };
|
|
@@ -94,12 +118,8 @@ export class StyleRegistry {
|
|
|
94
118
|
});
|
|
95
119
|
}
|
|
96
120
|
seedDifferentialStyles(fragments) {
|
|
97
|
-
for (const fragment of fragments)
|
|
98
|
-
|
|
99
|
-
this.#dxfXml.push(fragment);
|
|
100
|
-
if (!this.#dxfIndexByFragment.has(fragment))
|
|
101
|
-
this.#dxfIndexByFragment.set(fragment, index);
|
|
102
|
-
}
|
|
121
|
+
for (const fragment of fragments)
|
|
122
|
+
this.#dxfs.seed(fragment);
|
|
103
123
|
}
|
|
104
124
|
seedIndexedColors(fragments) {
|
|
105
125
|
this.#indexedColors.length = 0;
|
|
@@ -128,69 +148,36 @@ export class StyleRegistry {
|
|
|
128
148
|
}
|
|
129
149
|
differentialStyleId(style) {
|
|
130
150
|
const fragment = dxfXml(style);
|
|
131
|
-
|
|
132
|
-
if (index === undefined) {
|
|
133
|
-
index = this.#dxfXml.length;
|
|
134
|
-
this.#dxfXml.push(fragment);
|
|
135
|
-
this.#dxfIndexByFragment.set(fragment, index);
|
|
136
|
-
}
|
|
137
|
-
return index;
|
|
151
|
+
return this.#dxfs.intern(fragment, fragment);
|
|
138
152
|
}
|
|
139
153
|
#internFill(fill) {
|
|
140
|
-
|
|
141
|
-
let id = this.#fillIdBySignature.get(signature);
|
|
142
|
-
if (id === undefined) {
|
|
143
|
-
id = RESERVED_FILL_COUNT + this.#fillXml.length;
|
|
144
|
-
this.#fillXml.push(patternFillXml(fill, { solidBgFallback: true }));
|
|
145
|
-
this.#fillIdBySignature.set(signature, id);
|
|
146
|
-
}
|
|
147
|
-
return id;
|
|
154
|
+
return this.#fills.intern(fillSignature(fill), patternFillXml(fill, { solidBgFallback: true }));
|
|
148
155
|
}
|
|
149
156
|
#internNumFmt(code) {
|
|
150
|
-
|
|
151
|
-
if (id === undefined) {
|
|
152
|
-
id = CUSTOM_NUMFMT_BASE + this.#numFmtCodes.length;
|
|
153
|
-
this.#numFmtCodes.push(code);
|
|
154
|
-
this.#numFmtIdByCode.set(code, id);
|
|
155
|
-
}
|
|
156
|
-
return id;
|
|
157
|
+
return this.#numFmts.intern(code, code);
|
|
157
158
|
}
|
|
158
159
|
#internFont(font) {
|
|
159
160
|
const xml = fontXml(font);
|
|
160
161
|
if (xml === '' || this.#font0Bodies.has(xml))
|
|
161
162
|
return 0;
|
|
162
|
-
|
|
163
|
-
if (id === undefined) {
|
|
164
|
-
id = RESERVED_FONT_COUNT + this.#fontXml.length;
|
|
165
|
-
this.#fontXml.push(`<font>${xml}</font>`);
|
|
166
|
-
this.#fontIdBySignature.set(xml, id);
|
|
167
|
-
}
|
|
168
|
-
return id;
|
|
163
|
+
return this.#fonts.intern(xml, `<font>${xml}</font>`);
|
|
169
164
|
}
|
|
170
165
|
#internBorder(border) {
|
|
171
166
|
const xml = borderXml(border);
|
|
172
|
-
|
|
173
|
-
return 0;
|
|
174
|
-
let id = this.#borderIdBySignature.get(xml);
|
|
175
|
-
if (id === undefined) {
|
|
176
|
-
id = RESERVED_BORDER_COUNT + this.#borderXml.length;
|
|
177
|
-
this.#borderXml.push(xml);
|
|
178
|
-
this.#borderIdBySignature.set(xml, id);
|
|
179
|
-
}
|
|
180
|
-
return id;
|
|
167
|
+
return xml === DEFAULT_BORDER ? 0 : this.#borders.intern(xml, xml);
|
|
181
168
|
}
|
|
182
169
|
toXml() {
|
|
183
|
-
const fillCount = RESERVED_FILL_COUNT + this.#
|
|
170
|
+
const fillCount = RESERVED_FILL_COUNT + this.#fills.size;
|
|
184
171
|
const fills = '<fill><patternFill patternType="none"/></fill>' +
|
|
185
172
|
'<fill><patternFill patternType="gray125"/></fill>' +
|
|
186
|
-
this.#
|
|
173
|
+
this.#fills.entries.join('');
|
|
187
174
|
const cellXfs = this.#formats.map((format) => xfXml(format, format.xfId)).join('');
|
|
188
175
|
const cellStyleXfs = this.#cellStyleXfs.map((format) => xfXml(format, null)).join('');
|
|
189
176
|
const cellStyles = this.#cellStyleNames.map(cellStyleTag).join('');
|
|
190
|
-
const fontCount = RESERVED_FONT_COUNT + this.#
|
|
191
|
-
const fonts = `<font>${this.#defaultFontBody}</font>${this.#
|
|
192
|
-
const borderCount = RESERVED_BORDER_COUNT + this.#
|
|
193
|
-
const borders = DEFAULT_BORDER + this.#
|
|
177
|
+
const fontCount = RESERVED_FONT_COUNT + this.#fonts.size;
|
|
178
|
+
const fonts = `<font>${this.#defaultFontBody}</font>${this.#fonts.entries.join('')}`;
|
|
179
|
+
const borderCount = RESERVED_BORDER_COUNT + this.#borders.size;
|
|
180
|
+
const borders = DEFAULT_BORDER + this.#borders.entries.join('');
|
|
194
181
|
return (XML_DECLARATION +
|
|
195
182
|
`<styleSheet xmlns="${SPREADSHEETML_NS}"${this.#foreignNamespaceAttrs()}>` +
|
|
196
183
|
this.#numFmtsXml() +
|
|
@@ -245,17 +232,17 @@ export class StyleRegistry {
|
|
|
245
232
|
return `<colors>${indexed}${mru}</colors>`;
|
|
246
233
|
}
|
|
247
234
|
#dxfsXml() {
|
|
248
|
-
if (this.#
|
|
235
|
+
if (this.#dxfs.size === 0)
|
|
249
236
|
return '<dxfs count="0"/>';
|
|
250
|
-
return `<dxfs count="${this.#
|
|
237
|
+
return `<dxfs count="${this.#dxfs.size}">${this.#dxfs.entries.join('')}</dxfs>`;
|
|
251
238
|
}
|
|
252
239
|
#numFmtsXml() {
|
|
253
|
-
if (this.#
|
|
240
|
+
if (this.#numFmts.size === 0)
|
|
254
241
|
return '';
|
|
255
|
-
const entries = this.#
|
|
242
|
+
const entries = this.#numFmts.entries
|
|
256
243
|
.map((code, i) => `<numFmt numFmtId="${CUSTOM_NUMFMT_BASE + i}" formatCode="${escapeFormatCode(code)}"/>`)
|
|
257
244
|
.join('');
|
|
258
|
-
return `<numFmts count="${this.#
|
|
245
|
+
return `<numFmts count="${this.#numFmts.size}">${entries}</numFmts>`;
|
|
259
246
|
}
|
|
260
247
|
}
|
|
261
248
|
function isDefaultFormat(format) {
|
|
@@ -292,12 +279,6 @@ function cellStyleTag(entry) {
|
|
|
292
279
|
const builtin = entry.builtinId === undefined ? '' : ` builtinId="${entry.builtinId}"`;
|
|
293
280
|
return `<cellStyle name="${escapeAttr(entry.name)}" xfId="${entry.xfId}"${builtin}/>`;
|
|
294
281
|
}
|
|
295
|
-
function checkedToken(value, isValid, kind) {
|
|
296
|
-
if (!isValid(value)) {
|
|
297
|
-
throw new AuthoringError(`Invalid ${kind} ${JSON.stringify(value)}: not a value the OOXML enumeration allows`);
|
|
298
|
-
}
|
|
299
|
-
return value;
|
|
300
|
-
}
|
|
301
282
|
function alignmentAttrs(alignment) {
|
|
302
283
|
const parts = [];
|
|
303
284
|
if (alignment.horizontal !== undefined && alignment.horizontal !== 'general') {
|
|
@@ -307,17 +288,17 @@ function alignmentAttrs(alignment) {
|
|
|
307
288
|
parts.push(`vertical="${checkedToken(alignment.vertical, isVerticalAlignment, 'vertical alignment')}"`);
|
|
308
289
|
}
|
|
309
290
|
if (alignment.textRotation !== undefined && alignment.textRotation !== 0) {
|
|
310
|
-
parts.push(`textRotation="${
|
|
291
|
+
parts.push(`textRotation="${numberText(alignment.textRotation)}"`);
|
|
311
292
|
}
|
|
312
293
|
if (alignment.wrapText)
|
|
313
294
|
parts.push('wrapText="1"');
|
|
314
295
|
if (alignment.indent !== undefined && alignment.indent !== 0) {
|
|
315
|
-
parts.push(`indent="${
|
|
296
|
+
parts.push(`indent="${numberText(alignment.indent)}"`);
|
|
316
297
|
}
|
|
317
298
|
if (alignment.shrinkToFit)
|
|
318
299
|
parts.push('shrinkToFit="1"');
|
|
319
300
|
if (alignment.readingOrder !== undefined && alignment.readingOrder !== 0) {
|
|
320
|
-
parts.push(`readingOrder="${
|
|
301
|
+
parts.push(`readingOrder="${numberText(alignment.readingOrder)}"`);
|
|
321
302
|
}
|
|
322
303
|
return parts.join(' ');
|
|
323
304
|
}
|
|
@@ -349,15 +330,15 @@ export function fontXml(font, nameTag = 'name') {
|
|
|
349
330
|
parts.push(`<vertAlign val="${checkedToken(font.vertAlign, isFontVerticalAlignment, 'font vertical alignment')}"/>`);
|
|
350
331
|
}
|
|
351
332
|
if (font.size !== undefined)
|
|
352
|
-
parts.push(`<sz val="${
|
|
333
|
+
parts.push(`<sz val="${numberText(font.size)}"/>`);
|
|
353
334
|
if (font.color !== undefined)
|
|
354
335
|
parts.push(`<color ${colorAttrs(font.color)}/>`);
|
|
355
336
|
if (font.name !== undefined)
|
|
356
337
|
parts.push(`<${nameTag} val="${escapeAttr(font.name)}"/>`);
|
|
357
338
|
if (font.family !== undefined)
|
|
358
|
-
parts.push(`<family val="${
|
|
339
|
+
parts.push(`<family val="${numberText(font.family)}"/>`);
|
|
359
340
|
if (font.charset !== undefined)
|
|
360
|
-
parts.push(`<charset val="${
|
|
341
|
+
parts.push(`<charset val="${numberText(font.charset)}"/>`);
|
|
361
342
|
if (font.scheme !== undefined && font.scheme !== 'none')
|
|
362
343
|
parts.push(`<scheme val="${checkedToken(font.scheme, isFontScheme, 'font scheme')}"/>`);
|
|
363
344
|
return parts.join('');
|
|
@@ -380,18 +361,18 @@ function dxfXml(style) {
|
|
|
380
361
|
}
|
|
381
362
|
function gradientFillXml(fill) {
|
|
382
363
|
const attrs = (fill.gradient === 'path' ? ' type="path"' : '') +
|
|
383
|
-
(fill.degree ? ` degree="${
|
|
364
|
+
(fill.degree ? ` degree="${numberText(fill.degree)}"` : '') +
|
|
384
365
|
insetAttr('left', fill.left) +
|
|
385
366
|
insetAttr('right', fill.right) +
|
|
386
367
|
insetAttr('top', fill.top) +
|
|
387
368
|
insetAttr('bottom', fill.bottom);
|
|
388
369
|
const stops = fill.stops
|
|
389
|
-
.map((stop) => `<stop position="${
|
|
370
|
+
.map((stop) => `<stop position="${numberText(stop.position)}"><color ${colorAttrs(stop.color)}/></stop>`)
|
|
390
371
|
.join('');
|
|
391
372
|
return `<gradientFill${attrs}>${stops}</gradientFill>`;
|
|
392
373
|
}
|
|
393
374
|
function insetAttr(name, value) {
|
|
394
|
-
return value ? ` ${name}="${
|
|
375
|
+
return value ? ` ${name}="${numberText(value)}"` : '';
|
|
395
376
|
}
|
|
396
377
|
function underlineXml(underline) {
|
|
397
378
|
if (underline === undefined || underline === false || underline === 'none')
|
|
@@ -400,12 +381,6 @@ function underlineXml(underline) {
|
|
|
400
381
|
return '<u/>';
|
|
401
382
|
return `<u val="${checkedToken(underline, isNamedUnderlineStyle, 'underline style')}"/>`;
|
|
402
383
|
}
|
|
403
|
-
function numberAttr(value) {
|
|
404
|
-
if (!Number.isFinite(value)) {
|
|
405
|
-
throw new AuthoringError(`cannot serialise a non-finite font metric (${value})`);
|
|
406
|
-
}
|
|
407
|
-
return String(value);
|
|
408
|
-
}
|
|
409
384
|
function borderXml(border) {
|
|
410
385
|
const attrs = (border.diagonalUp ? ' diagonalUp="1"' : '') + (border.diagonalDown ? ' diagonalDown="1"' : '');
|
|
411
386
|
return (`<border${attrs}>` +
|
package/dist/io/xlsx/tables.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { encodeAddress, tryDecodeRange } from '../../core/address.js';
|
|
2
2
|
import { isTotalsRowFunction, } from '../../core/table.js';
|
|
3
|
-
import { boolPresent, localName, numInteger, parseXml } from '../../xml/xml-read.js';
|
|
3
|
+
import { boolPresent, localName, numInteger, parseXml, TextCapture } from '../../xml/xml-read.js';
|
|
4
4
|
import { boolAttr, escapeAttr, escapeText, XML_DECLARATION } from '../../xml/xml.js';
|
|
5
5
|
import { NS } from './relationships.js';
|
|
6
6
|
export function tableXml(table, id) {
|
|
@@ -65,10 +65,9 @@ export function parseTable(xml) {
|
|
|
65
65
|
let style;
|
|
66
66
|
let hasAutoFilter = false;
|
|
67
67
|
const columns = [];
|
|
68
|
-
|
|
69
|
-
let totalsFormula = '';
|
|
68
|
+
const totalsFormula = new TextCapture('totalsRowFormula');
|
|
70
69
|
parseXml(xml, {
|
|
71
|
-
onOpen(elementName, attrs) {
|
|
70
|
+
onOpen(elementName, attrs, selfClosing) {
|
|
72
71
|
switch (localName(elementName)) {
|
|
73
72
|
case 'table':
|
|
74
73
|
name = attrs.name ?? attrs.displayName;
|
|
@@ -112,27 +111,28 @@ export function parseTable(xml) {
|
|
|
112
111
|
break;
|
|
113
112
|
}
|
|
114
113
|
case 'totalsRowFormula':
|
|
115
|
-
|
|
116
|
-
totalsFormula = '';
|
|
114
|
+
totalsFormula.open('totalsRowFormula', selfClosing);
|
|
117
115
|
break;
|
|
118
116
|
}
|
|
119
117
|
},
|
|
120
118
|
onText(text) {
|
|
121
|
-
|
|
122
|
-
totalsFormula += text;
|
|
119
|
+
totalsFormula.text(text);
|
|
123
120
|
},
|
|
124
121
|
onClose(elementName) {
|
|
125
|
-
|
|
122
|
+
const formula = totalsFormula.close(localName(elementName));
|
|
123
|
+
if (formula === undefined)
|
|
126
124
|
return;
|
|
127
|
-
inTotalsFormula = false;
|
|
128
125
|
const column = columns[columns.length - 1];
|
|
129
126
|
if (column !== undefined)
|
|
130
|
-
column.totalsRowFormula =
|
|
127
|
+
column.totalsRowFormula = formula;
|
|
131
128
|
},
|
|
132
129
|
});
|
|
133
130
|
if (name === undefined || ref === undefined || columns.length === 0)
|
|
134
131
|
return undefined;
|
|
135
|
-
const
|
|
132
|
+
const decoded = tryDecodeRange(ref);
|
|
133
|
+
if (decoded === undefined)
|
|
134
|
+
return undefined;
|
|
135
|
+
const { top, left, bottom } = decoded;
|
|
136
136
|
if (top === undefined || left === undefined || bottom === undefined)
|
|
137
137
|
return undefined;
|
|
138
138
|
const headerRow = headerRowCount !== 0;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DEFAULT_THEME_COLOR_SCHEME, normalizeThemeColor, } from '../../core/theme.js';
|
|
1
|
+
import { DEFAULT_THEME_COLOR_SCHEME, isThemeColorSlot, normalizeThemeColor, } from '../../core/theme.js';
|
|
2
2
|
import { decodeEntities } from '../../xml/xml-read.js';
|
|
3
3
|
import { escapeAttr } from '../../xml/xml.js';
|
|
4
4
|
const SCHEME_SLOT = new RegExp('<a:(dk1|lt1|dk2|lt2|accent[1-6]|hlink|folHlink)\\b[^>]*>' + '\\s*<a:(srgbClr|sysClr)\\b([^>]*)>', 'g');
|
|
@@ -9,6 +9,8 @@ export function parseThemeColorScheme(themeXml) {
|
|
|
9
9
|
const scheme = {};
|
|
10
10
|
for (const match of (block[1] ?? '').matchAll(SCHEME_SLOT)) {
|
|
11
11
|
const slot = match[1];
|
|
12
|
+
if (slot === undefined || !isThemeColorSlot(slot))
|
|
13
|
+
continue;
|
|
12
14
|
const attrs = match[3] ?? '';
|
|
13
15
|
const source = match[2] === 'sysClr' ? /\blastClr="([^"]*)"/ : /\bval="([^"]*)"/;
|
|
14
16
|
const value = source.exec(attrs)?.[1];
|
|
@@ -58,8 +60,7 @@ export function applyThemeOverrides(baseXml, overrides) {
|
|
|
58
60
|
const authored = colors[slot];
|
|
59
61
|
const inner = authored !== undefined
|
|
60
62
|
? `<a:srgbClr val="${normalizeThemeColor(authored)}"/>`
|
|
61
|
-
: (sourceElements[slot] ??
|
|
62
|
-
`<a:srgbClr val="${DEFAULT_THEME_COLOR_SCHEME[slot]}"/>`);
|
|
63
|
+
: (sourceElements[slot] ?? `<a:srgbClr val="${DEFAULT_THEME_COLOR_SCHEME[slot]}"/>`);
|
|
63
64
|
return `<a:${slot}>${inner}</a:${slot}>`;
|
|
64
65
|
}).join('');
|
|
65
66
|
xml = replaceBlockBody(xml, 'clrScheme', body);
|
|
@@ -78,7 +79,9 @@ function parseThemeColorElements(themeXml) {
|
|
|
78
79
|
const elements = {};
|
|
79
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;
|
|
80
81
|
for (const match of (block[1] ?? '').matchAll(pattern)) {
|
|
81
|
-
const slot =
|
|
82
|
+
const slot = match[1] ?? match[3];
|
|
83
|
+
if (slot === undefined || !isThemeColorSlot(slot))
|
|
84
|
+
continue;
|
|
82
85
|
const inner = match[2];
|
|
83
86
|
if (inner !== undefined && inner !== '')
|
|
84
87
|
elements[slot] = inner;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { encodeAddress, tryDecodeCellRef } from '../../core/address.js';
|
|
2
2
|
import { MENTION_OFFSET_MAX, } from '../../core/comment-thread.js';
|
|
3
|
-
import { boolStrict, decodeSpreadsheetText, localName, numInteger, parseXml, } from '../../xml/xml-read.js';
|
|
4
|
-
import { escapeAttr, escapeSpreadsheetText, XML_DECLARATION } from '../../xml/xml.js';
|
|
3
|
+
import { boolStrict, decodeSpreadsheetText, localName, numInteger, parseXml, TextCapture, } from '../../xml/xml-read.js';
|
|
4
|
+
import { escapeAttr, escapeSpreadsheetText, textAttr, XML_DECLARATION } from '../../xml/xml.js';
|
|
5
5
|
import { THREADED_COMMENTS_NS } from './namespaces.js';
|
|
6
6
|
export function parsePersons(xml) {
|
|
7
7
|
const persons = [];
|
|
@@ -27,33 +27,33 @@ export function parseThreadedComments(xml) {
|
|
|
27
27
|
const messages = [];
|
|
28
28
|
let open;
|
|
29
29
|
let mentions = [];
|
|
30
|
-
let inText = false;
|
|
31
30
|
let text = '';
|
|
31
|
+
const capture = new TextCapture('text');
|
|
32
32
|
parseXml(xml, {
|
|
33
|
-
onOpen(name, attrs) {
|
|
33
|
+
onOpen(name, attrs, selfClosing) {
|
|
34
34
|
const local = localName(name);
|
|
35
35
|
if (local === 'threadedComment') {
|
|
36
36
|
open = attrs;
|
|
37
37
|
text = '';
|
|
38
38
|
mentions = [];
|
|
39
39
|
}
|
|
40
|
-
else if (
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
40
|
+
else if (open !== undefined) {
|
|
41
|
+
capture.open(local, selfClosing);
|
|
42
|
+
if (local === 'mention') {
|
|
43
|
+
const mention = mentionFrom(attrs);
|
|
44
|
+
if (mention !== undefined)
|
|
45
|
+
mentions.push(mention);
|
|
46
|
+
}
|
|
47
47
|
}
|
|
48
48
|
},
|
|
49
49
|
onText(chunk) {
|
|
50
|
-
|
|
51
|
-
text += chunk;
|
|
50
|
+
capture.text(chunk);
|
|
52
51
|
},
|
|
53
52
|
onClose(name) {
|
|
54
53
|
const local = localName(name);
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
const body = capture.close(local);
|
|
55
|
+
if (body !== undefined) {
|
|
56
|
+
text = body;
|
|
57
57
|
}
|
|
58
58
|
else if (local === 'threadedComment') {
|
|
59
59
|
if (open !== undefined) {
|
|
@@ -159,8 +159,8 @@ export function threadedCommentsXml(threads) {
|
|
|
159
159
|
`<ThreadedComments xmlns="${THREADED_COMMENTS_NS}">${messages.join('')}</ThreadedComments>`);
|
|
160
160
|
}
|
|
161
161
|
function threadedCommentXml(ref, comment, tail) {
|
|
162
|
-
const date =
|
|
163
|
-
const person =
|
|
162
|
+
const date = textAttr('dT', comment.date);
|
|
163
|
+
const person = textAttr('personId', comment.personId);
|
|
164
164
|
return (`<threadedComment ref="${escapeAttr(ref)}"${date}${person} id="${escapeAttr(comment.id)}"${tail}>` +
|
|
165
165
|
`<text>${escapeSpreadsheetText(comment.text)}</text>` +
|
|
166
166
|
mentionsXml(comment.mentions) +
|
|
@@ -184,8 +184,8 @@ function writableOffset(value) {
|
|
|
184
184
|
}
|
|
185
185
|
export function personsXml(persons) {
|
|
186
186
|
const entries = persons.map((person) => {
|
|
187
|
-
const userId =
|
|
188
|
-
const providerId =
|
|
187
|
+
const userId = textAttr('userId', person.userId);
|
|
188
|
+
const providerId = textAttr('providerId', person.providerId);
|
|
189
189
|
return (`<person displayName="${escapeAttr(person.displayName)}"` +
|
|
190
190
|
` id="${escapeAttr(person.id)}"${userId}${providerId}/>`);
|
|
191
191
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { mangleFormula, quoteSheetName } from '../../core/formula.js';
|
|
2
2
|
import { WORKBOOK_PROTECTION_CREDENTIAL_ATTRS } from '../../core/workbook-protection.js';
|
|
3
|
-
import {
|
|
3
|
+
import { isVisibility } from '../../core/worksheet.js';
|
|
4
|
+
import { checkedToken, escapeAttr, escapeText, numAttr, textAttr, XML_DECLARATION, } from '../../xml/xml.js';
|
|
4
5
|
import { extensionOf, relativePartPath, THEME_PART_PATH } from '../opc/part-paths.js';
|
|
5
6
|
import { relationship, relationshipsPart } from '../opc/rels.js';
|
|
6
7
|
import { imageContentType } from './images.js';
|
|
@@ -102,14 +103,16 @@ export function rootRelsXml(rootRefs) {
|
|
|
102
103
|
relationship('rId1', REL.officeDocument, 'xl/workbook.xml'),
|
|
103
104
|
relationship('rId2', REL.coreProps, 'docProps/core.xml'),
|
|
104
105
|
relationship('rId3', REL.extProps, 'docProps/app.xml'),
|
|
105
|
-
...rootRefs.map((ref, i) => relationship(`rId${4 + i}`, ref.relType,
|
|
106
|
+
...rootRefs.map((ref, i) => relationship(`rId${4 + i}`, ref.relType, ref.entryPath)),
|
|
106
107
|
]);
|
|
107
108
|
}
|
|
108
109
|
export function workbookXml(workbook, preservedRels, pivots) {
|
|
109
110
|
const sheets = workbook.worksheets;
|
|
110
111
|
const entries = sheets
|
|
111
112
|
.map((sheet, i) => {
|
|
112
|
-
const state = sheet.state === 'visible'
|
|
113
|
+
const state = sheet.state === 'visible'
|
|
114
|
+
? ''
|
|
115
|
+
: ` state="${checkedToken(sheet.state, isVisibility, 'sheet visibility')}"`;
|
|
113
116
|
return `<sheet name="${escapeAttr(sheet.name)}" sheetId="${sheet.id}"${state} r:id="rId${i + 1}"/>`;
|
|
114
117
|
})
|
|
115
118
|
.join('');
|
|
@@ -128,11 +131,14 @@ export function workbookXml(workbook, preservedRels, pivots) {
|
|
|
128
131
|
function bookViewsXml(workbook) {
|
|
129
132
|
const view = workbook.view;
|
|
130
133
|
const activeTab = workbook.activeTabIndex;
|
|
131
|
-
const attrs = (view.visibility
|
|
132
|
-
?
|
|
133
|
-
: '') +
|
|
134
|
+
const attrs = (view.visibility === undefined || view.visibility === 'visible'
|
|
135
|
+
? ''
|
|
136
|
+
: ` visibility="${checkedToken(view.visibility, isVisibility, 'window visibility')}"`) +
|
|
134
137
|
(view.minimized ? ' minimized="1"' : '') +
|
|
135
|
-
|
|
138
|
+
numAttr('xWindow', view.x) +
|
|
139
|
+
numAttr('yWindow', view.y) +
|
|
140
|
+
numAttr('windowWidth', view.width) +
|
|
141
|
+
numAttr('windowHeight', view.height) +
|
|
136
142
|
(activeTab === 0 ? '' : ` activeTab="${activeTab}"`);
|
|
137
143
|
return `<bookViews><workbookView${attrs}/></bookViews>`;
|
|
138
144
|
}
|
|
@@ -192,7 +198,7 @@ function definedNamesXml(workbook) {
|
|
|
192
198
|
const scopeAttr = name.scope === undefined
|
|
193
199
|
? ''
|
|
194
200
|
: ` localSheetId="${sheets.findIndex((sheet) => sheet.name === name.scope)}"`;
|
|
195
|
-
const commentAttr =
|
|
201
|
+
const commentAttr = textAttr('comment', name.comment);
|
|
196
202
|
const hiddenAttr = name.hidden ? ' hidden="1"' : '';
|
|
197
203
|
return (`<definedName name="${escapeAttr(name.name)}"${scopeAttr}${commentAttr}${hiddenAttr}>` +
|
|
198
204
|
`${escapeText(mangleFormula(name.refersTo))}</definedName>`);
|
|
@@ -2,9 +2,10 @@ import { decodeRange, encodeAddress } from '../../core/address.js';
|
|
|
2
2
|
import { DEFAULT_DATE_NUMFMT, dateToSerial } from '../../core/date.js';
|
|
3
3
|
import { mangleFormula } from '../../core/formula.js';
|
|
4
4
|
import { NAMED_STYLE_ID } from '../../core/internal.js';
|
|
5
|
+
import { CELL_STYLE_FACETS, pickStyleFacets } from '../../core/style.js';
|
|
5
6
|
import { detectValueType, isDataTableFormulaValue, isErrorValue, isFormulaValue, isHyperlinkValue, isRichTextValue, isSharedFormulaValue, } from '../../core/value.js';
|
|
6
7
|
import { AuthoringError, InternalError } from '../../errors.js';
|
|
7
|
-
import { escapeAttr, escapeSpreadsheetText, escapeText, numberText, textElement, XML_DECLARATION, } from '../../xml/xml.js';
|
|
8
|
+
import { assertWritableNumber, escapeAttr, escapeSpreadsheetText, escapeText, numberText, textAttr, textElement, XML_DECLARATION, } from '../../xml/xml.js';
|
|
8
9
|
import { relativePartPath } from '../opc/part-paths.js';
|
|
9
10
|
import { relationship, relationshipsPart } from '../opc/rels.js';
|
|
10
11
|
import { conditionalFormattingsExtXml, conditionalFormattingsXml } from './conditional-formatting.js';
|
|
@@ -136,16 +137,20 @@ export function renderRow(entry, ctx) {
|
|
|
136
137
|
return { xml: `<row r="${number}"${attrs}>${cellsXml}</row>`, minCol, maxCol };
|
|
137
138
|
}
|
|
138
139
|
function composeCellStyle(cell, rowFill, colDef) {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
140
|
+
const style = {};
|
|
141
|
+
for (const facet of CELL_STYLE_FACETS)
|
|
142
|
+
inheritFacet(style, facet, cell, colDef);
|
|
143
|
+
style.fill = cell.fill ?? rowFill ?? colDef?.fill;
|
|
144
|
+
style.numFmt = cell.numFmt ?? colDef?.numFmt ?? dateDefaultNumFmt(cell.value);
|
|
145
|
+
if (cell.quotePrefix !== undefined)
|
|
146
|
+
style.quotePrefix = cell.quotePrefix;
|
|
147
|
+
const xfId = cell[NAMED_STYLE_ID];
|
|
148
|
+
if (xfId !== undefined)
|
|
149
|
+
style.xfId = xfId;
|
|
150
|
+
return style;
|
|
151
|
+
}
|
|
152
|
+
function inheritFacet(target, key, cell, colDef) {
|
|
153
|
+
target[key] = cell[key] ?? colDef?.[key];
|
|
149
154
|
}
|
|
150
155
|
function worksheetExtLstXml(sheet, slicerRelIds) {
|
|
151
156
|
const exts = [
|
|
@@ -228,7 +233,7 @@ export function worksheetRelsXml(tables, drawing, comments, threadedComments, pr
|
|
|
228
233
|
...preservedReferences.map((reference) => relationship(reference.relId, reference.relType, escapeAttr(relativePartPath('xl/worksheets/sheet1.xml', reference.entryPath)))),
|
|
229
234
|
...hyperlinks
|
|
230
235
|
.filter((link) => link.relId !== undefined && link.target !== undefined)
|
|
231
|
-
.map((link) => relationship(link.relId, REL.hyperlink,
|
|
236
|
+
.map((link) => relationship(link.relId, REL.hyperlink, link.target, {
|
|
232
237
|
external: true,
|
|
233
238
|
})),
|
|
234
239
|
];
|
|
@@ -243,12 +248,16 @@ function sheetFormatPr(properties, outlineLevel) {
|
|
|
243
248
|
}
|
|
244
249
|
if (properties.defaultRowHeight !== undefined)
|
|
245
250
|
attrs += ' customHeight="1"';
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
if (outlineLevel.row > 0)
|
|
249
|
-
attrs += ` outlineLevelRow="${outlineLevel.row}"`;
|
|
251
|
+
attrs += outlineAttr('outlineLevelCol', outlineLevel.col);
|
|
252
|
+
attrs += outlineAttr('outlineLevelRow', outlineLevel.row);
|
|
250
253
|
return `<sheetFormatPr${attrs}/>`;
|
|
251
254
|
}
|
|
255
|
+
function outlineAttr(name, level) {
|
|
256
|
+
if (level === undefined)
|
|
257
|
+
return '';
|
|
258
|
+
const text = numberText(level);
|
|
259
|
+
return level > 0 ? ` ${name}="${text}"` : '';
|
|
260
|
+
}
|
|
252
261
|
function maxColumnOutlineLevel(sheet) {
|
|
253
262
|
let max = 0;
|
|
254
263
|
for (const column of sheet.columns())
|
|
@@ -285,22 +294,16 @@ function colBody(properties, styles) {
|
|
|
285
294
|
attrs += ' hidden="1"';
|
|
286
295
|
meaningful = true;
|
|
287
296
|
}
|
|
288
|
-
|
|
289
|
-
|
|
297
|
+
const outline = outlineAttr('outlineLevel', properties.outlineLevel);
|
|
298
|
+
if (outline !== '') {
|
|
299
|
+
attrs += outline;
|
|
290
300
|
meaningful = true;
|
|
291
301
|
}
|
|
292
302
|
if (properties.collapsed) {
|
|
293
303
|
attrs += ' collapsed="1"';
|
|
294
304
|
meaningful = true;
|
|
295
305
|
}
|
|
296
|
-
const style = styles.styleId(
|
|
297
|
-
fill: properties.fill,
|
|
298
|
-
numFmt: properties.numFmt,
|
|
299
|
-
font: properties.font,
|
|
300
|
-
border: properties.border,
|
|
301
|
-
alignment: properties.alignment,
|
|
302
|
-
protection: properties.protection,
|
|
303
|
-
});
|
|
306
|
+
const style = styles.styleId(pickStyleFacets(properties));
|
|
304
307
|
if (style !== 0) {
|
|
305
308
|
attrs += ` style="${style}"`;
|
|
306
309
|
meaningful = true;
|
|
@@ -315,9 +318,7 @@ function rowAttrs(properties, styles, collapsedSummary) {
|
|
|
315
318
|
attrs += ` ht="${numberText(properties.height)}" customHeight="1"`;
|
|
316
319
|
if (properties.hidden)
|
|
317
320
|
attrs += ' hidden="1"';
|
|
318
|
-
|
|
319
|
-
attrs += ` outlineLevel="${properties.outlineLevel}"`;
|
|
320
|
-
}
|
|
321
|
+
attrs += outlineAttr('outlineLevel', properties.outlineLevel);
|
|
321
322
|
if (properties.collapsed || collapsedSummary)
|
|
322
323
|
attrs += ' collapsed="1"';
|
|
323
324
|
const style = styles.styleId({ fill: properties.fill });
|
|
@@ -331,6 +332,7 @@ function scanRowOutline(sheet) {
|
|
|
331
332
|
let maxLevel = 0;
|
|
332
333
|
for (const { number, properties } of sheet.rows()) {
|
|
333
334
|
const rowLevel = properties?.outlineLevel ?? 0;
|
|
335
|
+
assertWritableNumber(rowLevel);
|
|
334
336
|
level.set(number, rowLevel);
|
|
335
337
|
hidden.set(number, properties?.hidden ?? false);
|
|
336
338
|
if (rowLevel > maxLevel)
|
|
@@ -408,12 +410,7 @@ function cellXml(cell, style, shared, sharedStrings) {
|
|
|
408
410
|
throw new InternalError(`writing a ${detectValueType(value)} cell value has no arm: every CellValue kind is handled above`);
|
|
409
411
|
}
|
|
410
412
|
function hasOwnStyle(cell) {
|
|
411
|
-
return (
|
|
412
|
-
cell.numFmt !== undefined ||
|
|
413
|
-
cell.font !== undefined ||
|
|
414
|
-
cell.border !== undefined ||
|
|
415
|
-
cell.alignment !== undefined ||
|
|
416
|
-
cell.protection !== undefined ||
|
|
413
|
+
return (CELL_STYLE_FACETS.some((facet) => cell[facet] !== undefined) ||
|
|
417
414
|
cell.quotePrefix === true ||
|
|
418
415
|
cell[NAMED_STYLE_ID] !== undefined);
|
|
419
416
|
}
|
|
@@ -430,8 +427,8 @@ function cellFormulaXml(ref, s, value, shared) {
|
|
|
430
427
|
const attrs = `ref="${escapeAttr(value.ref)}"` +
|
|
431
428
|
` dt2D="${value.dataTable2D ? 1 : 0}"` +
|
|
432
429
|
` dtr="${value.dataTableRow ? 1 : 0}"` +
|
|
433
|
-
(
|
|
434
|
-
(
|
|
430
|
+
textAttr('r1', value.r1) +
|
|
431
|
+
textAttr('r2', value.r2);
|
|
435
432
|
return formulaBodyXml(ref, s, `<f t="dataTable" ${attrs}/>`, value.result);
|
|
436
433
|
}
|
|
437
434
|
if (isFormulaValue(value)) {
|