@cj-tech-master/excelts 4.2.3-canary.20260122080306.cc11b20 → 5.0.0-canary.20260123012457.1fdf506
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/dist/browser/modules/excel/column.d.ts +5 -0
- package/dist/browser/modules/excel/column.js +10 -2
- package/dist/browser/modules/excel/row.d.ts +2 -0
- package/dist/browser/modules/excel/row.js +3 -1
- package/dist/browser/modules/excel/workbook.d.ts +4 -0
- package/dist/browser/modules/excel/workbook.js +4 -1
- package/dist/browser/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.d.ts +1 -0
- package/dist/browser/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.js +17 -10
- package/dist/browser/modules/excel/xlsx/xform/sheet/row-xform.d.ts +1 -0
- package/dist/browser/modules/excel/xlsx/xform/sheet/row-xform.js +7 -1
- package/dist/browser/modules/excel/xlsx/xform/sheet/sheet-format-properties-xform.d.ts +1 -0
- package/dist/browser/modules/excel/xlsx/xform/sheet/sheet-format-properties-xform.js +9 -4
- package/dist/browser/modules/excel/xlsx/xform/sheet/sheet-view-xform.js +4 -2
- package/dist/browser/modules/excel/xlsx/xform/sheet/worksheet-xform.js +2 -1
- package/dist/browser/modules/excel/xlsx/xform/style/style-xform.d.ts +7 -0
- package/dist/browser/modules/excel/xlsx/xform/style/style-xform.js +26 -6
- package/dist/browser/modules/excel/xlsx/xform/style/styles-xform.d.ts +6 -0
- package/dist/browser/modules/excel/xlsx/xform/style/styles-xform.js +52 -4
- package/dist/browser/modules/excel/xlsx/xlsx.browser.js +7 -0
- package/dist/cjs/modules/excel/column.js +10 -2
- package/dist/cjs/modules/excel/row.js +3 -1
- package/dist/cjs/modules/excel/workbook.js +4 -1
- package/dist/cjs/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.js +17 -10
- package/dist/cjs/modules/excel/xlsx/xform/sheet/row-xform.js +7 -1
- package/dist/cjs/modules/excel/xlsx/xform/sheet/sheet-format-properties-xform.js +9 -4
- package/dist/cjs/modules/excel/xlsx/xform/sheet/sheet-view-xform.js +4 -2
- package/dist/cjs/modules/excel/xlsx/xform/sheet/worksheet-xform.js +2 -1
- package/dist/cjs/modules/excel/xlsx/xform/style/style-xform.js +26 -6
- package/dist/cjs/modules/excel/xlsx/xform/style/styles-xform.js +52 -4
- package/dist/cjs/modules/excel/xlsx/xlsx.browser.js +7 -0
- package/dist/esm/modules/excel/column.js +10 -2
- package/dist/esm/modules/excel/row.js +3 -1
- package/dist/esm/modules/excel/workbook.js +4 -1
- package/dist/esm/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.js +17 -10
- package/dist/esm/modules/excel/xlsx/xform/sheet/row-xform.js +7 -1
- package/dist/esm/modules/excel/xlsx/xform/sheet/sheet-format-properties-xform.js +9 -4
- package/dist/esm/modules/excel/xlsx/xform/sheet/sheet-view-xform.js +4 -2
- package/dist/esm/modules/excel/xlsx/xform/sheet/worksheet-xform.js +2 -1
- package/dist/esm/modules/excel/xlsx/xform/style/style-xform.js +26 -6
- package/dist/esm/modules/excel/xlsx/xform/style/styles-xform.js +52 -4
- package/dist/esm/modules/excel/xlsx/xlsx.browser.js +7 -0
- package/dist/iife/excelts.iife.js +450 -394
- package/dist/iife/excelts.iife.js.map +1 -1
- package/dist/iife/excelts.iife.min.js +7 -7
- package/dist/types/modules/excel/column.d.ts +5 -0
- package/dist/types/modules/excel/row.d.ts +2 -0
- package/dist/types/modules/excel/workbook.d.ts +4 -0
- package/dist/types/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.d.ts +1 -0
- package/dist/types/modules/excel/xlsx/xform/sheet/row-xform.d.ts +1 -0
- package/dist/types/modules/excel/xlsx/xform/sheet/sheet-format-properties-xform.d.ts +1 -0
- package/dist/types/modules/excel/xlsx/xform/style/style-xform.d.ts +7 -0
- package/dist/types/modules/excel/xlsx/xform/style/styles-xform.d.ts +6 -0
- package/package.json +14 -14
|
@@ -248,18 +248,21 @@ class PivotTableXform extends base_xform_1.BaseXform {
|
|
|
248
248
|
xmlStream.writeXml('<rowItems count="1"><i t="grand"><x/></i></rowItems>');
|
|
249
249
|
}
|
|
250
250
|
// Col fields
|
|
251
|
-
//
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
xmlStream.
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
251
|
+
// Only render colFields if it was present in the original file or if there are actual column fields
|
|
252
|
+
// Some pivot tables don't have colFields element at all
|
|
253
|
+
if (model.hasColFields || model.colFields.length > 0) {
|
|
254
|
+
const colFieldCount = model.colFields.length === 0 ? 1 : model.colFields.length;
|
|
255
|
+
xmlStream.openNode("colFields", { count: colFieldCount });
|
|
256
|
+
if (model.colFields.length === 0) {
|
|
257
|
+
xmlStream.leafNode("field", { x: -2 });
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
for (const fieldIndex of model.colFields) {
|
|
261
|
+
xmlStream.leafNode("field", { x: fieldIndex });
|
|
262
|
+
}
|
|
260
263
|
}
|
|
264
|
+
xmlStream.closeNode();
|
|
261
265
|
}
|
|
262
|
-
xmlStream.closeNode();
|
|
263
266
|
// Col items - use parsed items if available
|
|
264
267
|
if (model.colItems && model.colItems.length > 0) {
|
|
265
268
|
xmlStream.openNode("colItems", { count: model.colItems.length });
|
|
@@ -462,6 +465,10 @@ class PivotTableXform extends base_xform_1.BaseXform {
|
|
|
462
465
|
break;
|
|
463
466
|
case "colFields":
|
|
464
467
|
this.state.inColFields = true;
|
|
468
|
+
// Track that colFields element was present in original file
|
|
469
|
+
if (this.model) {
|
|
470
|
+
this.model.hasColFields = true;
|
|
471
|
+
}
|
|
465
472
|
break;
|
|
466
473
|
case "dataFields":
|
|
467
474
|
this.state.inDataFields = true;
|
|
@@ -51,7 +51,10 @@ class RowXform extends base_xform_1.BaseXform {
|
|
|
51
51
|
xmlStream.addAttribute("s", model.styleId);
|
|
52
52
|
xmlStream.addAttribute("customFormat", "1");
|
|
53
53
|
}
|
|
54
|
-
//
|
|
54
|
+
// Output dyDescent if present (MS extension for font descent)
|
|
55
|
+
if (model.dyDescent !== undefined) {
|
|
56
|
+
xmlStream.addAttribute("x14ac:dyDescent", model.dyDescent);
|
|
57
|
+
}
|
|
55
58
|
if (model.outlineLevel) {
|
|
56
59
|
xmlStream.addAttribute("outlineLevel", model.outlineLevel);
|
|
57
60
|
}
|
|
@@ -102,6 +105,9 @@ class RowXform extends base_xform_1.BaseXform {
|
|
|
102
105
|
if ((0, utils_1.parseBoolean)(node.attributes.collapsed)) {
|
|
103
106
|
model.collapsed = true;
|
|
104
107
|
}
|
|
108
|
+
if (node.attributes["x14ac:dyDescent"] !== undefined) {
|
|
109
|
+
model.dyDescent = parseFloat(node.attributes["x14ac:dyDescent"]);
|
|
110
|
+
}
|
|
105
111
|
return true;
|
|
106
112
|
}
|
|
107
113
|
this.parser = this.map[node.name];
|
|
@@ -14,14 +14,14 @@ class SheetFormatPropertiesXform extends base_xform_1.BaseXform {
|
|
|
14
14
|
outlineLevelRow: model.outlineLevelRow || undefined,
|
|
15
15
|
outlineLevelCol: model.outlineLevelCol || undefined,
|
|
16
16
|
// Only output dyDescent if explicitly set (MS extension, not ECMA-376 standard)
|
|
17
|
-
"x14ac:dyDescent": model.dyDescent
|
|
17
|
+
"x14ac:dyDescent": model.dyDescent !== undefined && model.dyDescent !== 0 ? model.dyDescent : undefined
|
|
18
18
|
};
|
|
19
19
|
// Only output defaultColWidth if explicitly set
|
|
20
20
|
if (model.defaultColWidth) {
|
|
21
21
|
attributes.defaultColWidth = model.defaultColWidth;
|
|
22
22
|
}
|
|
23
|
-
//
|
|
24
|
-
if (
|
|
23
|
+
// Only output customHeight if it was present in the original file
|
|
24
|
+
if (model.customHeight) {
|
|
25
25
|
attributes.customHeight = "1";
|
|
26
26
|
}
|
|
27
27
|
if (Object.values(attributes).some((value) => value !== undefined)) {
|
|
@@ -33,13 +33,18 @@ class SheetFormatPropertiesXform extends base_xform_1.BaseXform {
|
|
|
33
33
|
if (node.name === "sheetFormatPr") {
|
|
34
34
|
this.model = {
|
|
35
35
|
defaultRowHeight: parseFloat(node.attributes.defaultRowHeight || "0"),
|
|
36
|
-
dyDescent:
|
|
36
|
+
dyDescent: node.attributes["x14ac:dyDescent"] !== undefined
|
|
37
|
+
? parseFloat(node.attributes["x14ac:dyDescent"])
|
|
38
|
+
: undefined,
|
|
37
39
|
outlineLevelRow: parseInt(node.attributes.outlineLevelRow || "0", 10),
|
|
38
40
|
outlineLevelCol: parseInt(node.attributes.outlineLevelCol || "0", 10)
|
|
39
41
|
};
|
|
40
42
|
if (node.attributes.defaultColWidth) {
|
|
41
43
|
this.model.defaultColWidth = parseFloat(node.attributes.defaultColWidth);
|
|
42
44
|
}
|
|
45
|
+
if (node.attributes.customHeight === "1") {
|
|
46
|
+
this.model.customHeight = true;
|
|
47
|
+
}
|
|
43
48
|
return true;
|
|
44
49
|
}
|
|
45
50
|
return false;
|
|
@@ -40,8 +40,8 @@ class SheetViewXform extends base_xform_1.BaseXform {
|
|
|
40
40
|
add("showRuler", "0", model.showRuler === false);
|
|
41
41
|
add("showRowColHeaders", "0", model.showRowColHeaders === false);
|
|
42
42
|
add("showGridLines", "0", model.showGridLines === false);
|
|
43
|
-
add("zoomScale", model.zoomScale, model.zoomScale);
|
|
44
|
-
add("zoomScaleNormal", model.zoomScaleNormal, model.zoomScaleNormal);
|
|
43
|
+
add("zoomScale", model.zoomScale, model.zoomScale !== undefined && model.zoomScale !== 100);
|
|
44
|
+
add("zoomScaleNormal", model.zoomScaleNormal, model.zoomScaleNormal !== undefined && model.zoomScaleNormal !== 100);
|
|
45
45
|
add("view", model.style, model.style);
|
|
46
46
|
let topLeftCell;
|
|
47
47
|
let xSplit;
|
|
@@ -146,6 +146,7 @@ class SheetViewXform extends base_xform_1.BaseXform {
|
|
|
146
146
|
model = this.model = {
|
|
147
147
|
workbookViewId: this.sheetView.workbookViewId,
|
|
148
148
|
rightToLeft: this.sheetView.rightToLeft,
|
|
149
|
+
tabSelected: this.sheetView.tabSelected,
|
|
149
150
|
state: VIEW_STATES[this.pane.state] || "split", // split is default
|
|
150
151
|
xSplit: this.pane.xSplit,
|
|
151
152
|
ySplit: this.pane.ySplit,
|
|
@@ -171,6 +172,7 @@ class SheetViewXform extends base_xform_1.BaseXform {
|
|
|
171
172
|
model = this.model = {
|
|
172
173
|
workbookViewId: this.sheetView.workbookViewId,
|
|
173
174
|
rightToLeft: this.sheetView.rightToLeft,
|
|
175
|
+
tabSelected: this.sheetView.tabSelected,
|
|
174
176
|
state: "normal",
|
|
175
177
|
showRuler: this.sheetView.showRuler,
|
|
176
178
|
showRowColHeaders: this.sheetView.showRowColHeaders,
|
|
@@ -387,7 +387,8 @@ class WorkSheetXform extends base_xform_1.BaseXform {
|
|
|
387
387
|
defaultRowHeight: model.properties.defaultRowHeight,
|
|
388
388
|
dyDescent: model.properties.dyDescent,
|
|
389
389
|
outlineLevelCol: model.properties.outlineLevelCol,
|
|
390
|
-
outlineLevelRow: model.properties.outlineLevelRow
|
|
390
|
+
outlineLevelRow: model.properties.outlineLevelRow,
|
|
391
|
+
customHeight: model.properties.customHeight
|
|
391
392
|
}
|
|
392
393
|
: undefined;
|
|
393
394
|
if (model.properties && model.properties.defaultColWidth) {
|
|
@@ -27,24 +27,27 @@ class StyleXform extends base_xform_1.BaseXform {
|
|
|
27
27
|
if (this.xfId) {
|
|
28
28
|
xmlStream.addAttribute("xfId", model.xfId || 0);
|
|
29
29
|
}
|
|
30
|
-
if (model.numFmtId) {
|
|
30
|
+
if (model.applyNumberFormat || model.numFmtId) {
|
|
31
31
|
xmlStream.addAttribute("applyNumberFormat", "1");
|
|
32
32
|
}
|
|
33
|
-
if (model.fontId) {
|
|
33
|
+
if (model.applyFont || model.fontId) {
|
|
34
34
|
xmlStream.addAttribute("applyFont", "1");
|
|
35
35
|
}
|
|
36
|
-
if (model.fillId) {
|
|
36
|
+
if (model.applyFill || model.fillId) {
|
|
37
37
|
xmlStream.addAttribute("applyFill", "1");
|
|
38
38
|
}
|
|
39
|
-
if (model.borderId) {
|
|
39
|
+
if (model.applyBorder || model.borderId) {
|
|
40
40
|
xmlStream.addAttribute("applyBorder", "1");
|
|
41
41
|
}
|
|
42
|
-
if (model.alignment) {
|
|
42
|
+
if (model.applyAlignment || model.alignment) {
|
|
43
43
|
xmlStream.addAttribute("applyAlignment", "1");
|
|
44
44
|
}
|
|
45
|
-
if (model.protection) {
|
|
45
|
+
if (model.applyProtection || model.protection) {
|
|
46
46
|
xmlStream.addAttribute("applyProtection", "1");
|
|
47
47
|
}
|
|
48
|
+
if (model.pivotButton) {
|
|
49
|
+
xmlStream.addAttribute("pivotButton", "1");
|
|
50
|
+
}
|
|
48
51
|
/**
|
|
49
52
|
* Rendering tags causes close of XML stream.
|
|
50
53
|
* Therefore adding attributes must be done before rendering tags.
|
|
@@ -85,6 +88,23 @@ class StyleXform extends base_xform_1.BaseXform {
|
|
|
85
88
|
if (this.xfId) {
|
|
86
89
|
this.model.xfId = parseInt(node.attributes.xfId, 10);
|
|
87
90
|
}
|
|
91
|
+
if (node.attributes.pivotButton === "1") {
|
|
92
|
+
this.model.pivotButton = true;
|
|
93
|
+
}
|
|
94
|
+
// Preserve apply* flags from original file
|
|
95
|
+
const applyFlags = [
|
|
96
|
+
"applyNumberFormat",
|
|
97
|
+
"applyFont",
|
|
98
|
+
"applyFill",
|
|
99
|
+
"applyBorder",
|
|
100
|
+
"applyAlignment",
|
|
101
|
+
"applyProtection"
|
|
102
|
+
];
|
|
103
|
+
for (const flag of applyFlags) {
|
|
104
|
+
if (node.attributes[flag] === "1") {
|
|
105
|
+
this.model[flag] = true;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
88
108
|
return true;
|
|
89
109
|
case "alignment":
|
|
90
110
|
this.parser = this.map.alignment;
|
|
@@ -87,6 +87,13 @@ class StylesXform extends base_xform_1.BaseXform {
|
|
|
87
87
|
this.weakMap = new WeakMap();
|
|
88
88
|
this._hasCheckboxes = false;
|
|
89
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* Set the default font to use when no font is explicitly specified.
|
|
92
|
+
* This preserves the original file's default font during round-trip.
|
|
93
|
+
*/
|
|
94
|
+
setDefaultFont(font) {
|
|
95
|
+
this.defaultFont = font;
|
|
96
|
+
}
|
|
90
97
|
render(xmlStream, model) {
|
|
91
98
|
const renderModel = model || this.model;
|
|
92
99
|
//
|
|
@@ -103,8 +110,8 @@ class StylesXform extends base_xform_1.BaseXform {
|
|
|
103
110
|
xmlStream.closeNode();
|
|
104
111
|
}
|
|
105
112
|
if (!renderModel.fonts.length) {
|
|
106
|
-
// default (zero) font
|
|
107
|
-
this._addFont({
|
|
113
|
+
// default (zero) font - use preserved font or fallback to Calibri
|
|
114
|
+
this._addFont(this.defaultFont || {
|
|
108
115
|
size: 11,
|
|
109
116
|
color: { theme: 1 },
|
|
110
117
|
name: "Calibri",
|
|
@@ -196,6 +203,10 @@ class StylesXform extends base_xform_1.BaseXform {
|
|
|
196
203
|
add("borders", this.map.borders);
|
|
197
204
|
add("styles", this.map.cellXfs);
|
|
198
205
|
add("dxfs", this.map.dxfs);
|
|
206
|
+
// preserve the default (first) font from the original file
|
|
207
|
+
if (this.map.fonts.model && this.map.fonts.model.length > 0) {
|
|
208
|
+
this.defaultFont = this.map.fonts.model[0];
|
|
209
|
+
}
|
|
199
210
|
// index numFmts
|
|
200
211
|
this.index = {
|
|
201
212
|
model: [],
|
|
@@ -223,8 +234,14 @@ class StylesXform extends base_xform_1.BaseXform {
|
|
|
223
234
|
}
|
|
224
235
|
// if we have no default font, add it here now
|
|
225
236
|
if (!this.model.fonts.length) {
|
|
226
|
-
// default (zero) font
|
|
227
|
-
this._addFont(
|
|
237
|
+
// default (zero) font - use preserved font or fallback to Calibri
|
|
238
|
+
this._addFont(this.defaultFont || {
|
|
239
|
+
size: 11,
|
|
240
|
+
color: { theme: 1 },
|
|
241
|
+
name: "Calibri",
|
|
242
|
+
family: 2,
|
|
243
|
+
scheme: "minor"
|
|
244
|
+
});
|
|
228
245
|
}
|
|
229
246
|
const type = cellType || enums_1.Enums.ValueType.Number;
|
|
230
247
|
// If we have seen this style object before, assume it has the same styleId.
|
|
@@ -265,6 +282,21 @@ class StylesXform extends base_xform_1.BaseXform {
|
|
|
265
282
|
if (model.protection) {
|
|
266
283
|
style.protection = model.protection;
|
|
267
284
|
}
|
|
285
|
+
// Preserve xf-level attributes (pivotButton, apply* flags)
|
|
286
|
+
const xfFlags = [
|
|
287
|
+
"pivotButton",
|
|
288
|
+
"applyNumberFormat",
|
|
289
|
+
"applyFont",
|
|
290
|
+
"applyFill",
|
|
291
|
+
"applyBorder",
|
|
292
|
+
"applyAlignment",
|
|
293
|
+
"applyProtection"
|
|
294
|
+
];
|
|
295
|
+
for (const flag of xfFlags) {
|
|
296
|
+
if (model[flag]) {
|
|
297
|
+
style[flag] = true;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
268
300
|
if (type === enums_1.Enums.ValueType.Checkbox) {
|
|
269
301
|
// Checkbox rendering relies on style extensions (extLst) and workbook-level parts.
|
|
270
302
|
// Force applyAlignment="1" (without emitting an <alignment/> node) by providing
|
|
@@ -325,6 +357,22 @@ class StylesXform extends base_xform_1.BaseXform {
|
|
|
325
357
|
if (style.protection) {
|
|
326
358
|
model.protection = style.protection;
|
|
327
359
|
}
|
|
360
|
+
// -------------------------------------------------------
|
|
361
|
+
// xf-level attributes (pivotButton, apply* flags)
|
|
362
|
+
const xfFlags = [
|
|
363
|
+
"pivotButton",
|
|
364
|
+
"applyNumberFormat",
|
|
365
|
+
"applyFont",
|
|
366
|
+
"applyFill",
|
|
367
|
+
"applyBorder",
|
|
368
|
+
"applyAlignment",
|
|
369
|
+
"applyProtection"
|
|
370
|
+
];
|
|
371
|
+
for (const flag of xfFlags) {
|
|
372
|
+
if (style[flag]) {
|
|
373
|
+
model[flag] = true;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
328
376
|
return model;
|
|
329
377
|
}
|
|
330
378
|
addDxfStyle(style) {
|
|
@@ -603,6 +603,8 @@ class XLSX {
|
|
|
603
603
|
delete model.sharedStrings;
|
|
604
604
|
delete model.workbookRels;
|
|
605
605
|
delete model.sheetDefs;
|
|
606
|
+
// Preserve default font before deleting styles
|
|
607
|
+
model.defaultFont = model.styles?.defaultFont;
|
|
606
608
|
delete model.styles;
|
|
607
609
|
delete model.mediaIndex;
|
|
608
610
|
delete model.drawings;
|
|
@@ -1238,7 +1240,12 @@ class XLSX {
|
|
|
1238
1240
|
options.useSharedStrings !== undefined ? options.useSharedStrings : true;
|
|
1239
1241
|
model.useStyles = options.useStyles !== undefined ? options.useStyles : true;
|
|
1240
1242
|
model.sharedStrings = new shared_strings_xform_1.SharedStringsXform();
|
|
1243
|
+
// Preserve default font from parsed styles if available
|
|
1244
|
+
const oldDefaultFont = model.defaultFont;
|
|
1241
1245
|
model.styles = model.useStyles ? new styles_xform_1.StylesXform(true) : new styles_xform_1.StylesXform.Mock();
|
|
1246
|
+
if (oldDefaultFont && model.styles.setDefaultFont) {
|
|
1247
|
+
model.styles.setDefaultFont(oldDefaultFont);
|
|
1248
|
+
}
|
|
1242
1249
|
const workbookXform = new workbook_xform_1.WorkbookXform();
|
|
1243
1250
|
const worksheetXform = new worksheet_xform_1.WorkSheetXform();
|
|
1244
1251
|
workbookXform.prepare(model);
|
|
@@ -38,7 +38,8 @@ class Column {
|
|
|
38
38
|
width: this.width,
|
|
39
39
|
style: this.style,
|
|
40
40
|
hidden: this.hidden,
|
|
41
|
-
outlineLevel: this.outlineLevel
|
|
41
|
+
outlineLevel: this.outlineLevel,
|
|
42
|
+
bestFit: this.bestFit
|
|
42
43
|
};
|
|
43
44
|
}
|
|
44
45
|
set defn(value) {
|
|
@@ -55,6 +56,7 @@ class Column {
|
|
|
55
56
|
// headers must be set after style
|
|
56
57
|
this.header = value.header;
|
|
57
58
|
this._hidden = !!value.hidden;
|
|
59
|
+
this.bestFit = value.bestFit;
|
|
58
60
|
}
|
|
59
61
|
else {
|
|
60
62
|
delete this._header;
|
|
@@ -62,6 +64,7 @@ class Column {
|
|
|
62
64
|
delete this.width;
|
|
63
65
|
this.style = {};
|
|
64
66
|
this.outlineLevel = 0;
|
|
67
|
+
delete this.bestFit;
|
|
65
68
|
}
|
|
66
69
|
}
|
|
67
70
|
/**
|
|
@@ -151,6 +154,7 @@ class Column {
|
|
|
151
154
|
return (this.width === model.width &&
|
|
152
155
|
this.hidden === model.hidden &&
|
|
153
156
|
this.outlineLevel === model.outlineLevel &&
|
|
157
|
+
this.bestFit === model.bestFit &&
|
|
154
158
|
isEqual(this.style, model.style));
|
|
155
159
|
}
|
|
156
160
|
get isDefault() {
|
|
@@ -163,6 +167,9 @@ class Column {
|
|
|
163
167
|
if (this.outlineLevel) {
|
|
164
168
|
return false;
|
|
165
169
|
}
|
|
170
|
+
if (this.bestFit) {
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
166
173
|
const s = this.style;
|
|
167
174
|
if (s && (s.font || s.numFmt || s.alignment || s.border || s.fill || s.protection)) {
|
|
168
175
|
return false;
|
|
@@ -313,7 +320,8 @@ class Column {
|
|
|
313
320
|
isCustomWidth: column.isCustomWidth,
|
|
314
321
|
hidden: column.hidden,
|
|
315
322
|
outlineLevel: column.outlineLevel,
|
|
316
|
-
collapsed: column.collapsed
|
|
323
|
+
collapsed: column.collapsed,
|
|
324
|
+
bestFit: column.bestFit
|
|
317
325
|
};
|
|
318
326
|
cols.push(col);
|
|
319
327
|
}
|
|
@@ -388,7 +388,8 @@ class Row {
|
|
|
388
388
|
style: this.style,
|
|
389
389
|
hidden: this.hidden,
|
|
390
390
|
outlineLevel: this.outlineLevel,
|
|
391
|
-
collapsed: this.collapsed
|
|
391
|
+
collapsed: this.collapsed,
|
|
392
|
+
dyDescent: this.dyDescent
|
|
392
393
|
}
|
|
393
394
|
: null;
|
|
394
395
|
}
|
|
@@ -435,6 +436,7 @@ class Row {
|
|
|
435
436
|
}
|
|
436
437
|
this.hidden = value.hidden;
|
|
437
438
|
this.outlineLevel = value.outlineLevel || 0;
|
|
439
|
+
this.dyDescent = value.dyDescent;
|
|
438
440
|
this.style = (value.style && JSON.parse(JSON.stringify(value.style))) || {};
|
|
439
441
|
}
|
|
440
442
|
}
|
|
@@ -255,7 +255,8 @@ class Workbook {
|
|
|
255
255
|
pivotTables: this.pivotTables,
|
|
256
256
|
calcProperties: this.calcProperties,
|
|
257
257
|
passthrough: this._passthrough,
|
|
258
|
-
rawDrawings: this._rawDrawings
|
|
258
|
+
rawDrawings: this._rawDrawings,
|
|
259
|
+
defaultFont: this._defaultFont
|
|
259
260
|
};
|
|
260
261
|
}
|
|
261
262
|
set model(value) {
|
|
@@ -300,6 +301,8 @@ class Workbook {
|
|
|
300
301
|
this._passthrough = value.passthrough || {};
|
|
301
302
|
// Preserve raw drawing data for drawings with chart references
|
|
302
303
|
this._rawDrawings = value.rawDrawings || {};
|
|
304
|
+
// Preserve default font for round-trip fidelity
|
|
305
|
+
this._defaultFont = value.defaultFont;
|
|
303
306
|
}
|
|
304
307
|
}
|
|
305
308
|
// ===========================================================================
|
|
@@ -245,18 +245,21 @@ class PivotTableXform extends BaseXform {
|
|
|
245
245
|
xmlStream.writeXml('<rowItems count="1"><i t="grand"><x/></i></rowItems>');
|
|
246
246
|
}
|
|
247
247
|
// Col fields
|
|
248
|
-
//
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
xmlStream.
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
248
|
+
// Only render colFields if it was present in the original file or if there are actual column fields
|
|
249
|
+
// Some pivot tables don't have colFields element at all
|
|
250
|
+
if (model.hasColFields || model.colFields.length > 0) {
|
|
251
|
+
const colFieldCount = model.colFields.length === 0 ? 1 : model.colFields.length;
|
|
252
|
+
xmlStream.openNode("colFields", { count: colFieldCount });
|
|
253
|
+
if (model.colFields.length === 0) {
|
|
254
|
+
xmlStream.leafNode("field", { x: -2 });
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
for (const fieldIndex of model.colFields) {
|
|
258
|
+
xmlStream.leafNode("field", { x: fieldIndex });
|
|
259
|
+
}
|
|
257
260
|
}
|
|
261
|
+
xmlStream.closeNode();
|
|
258
262
|
}
|
|
259
|
-
xmlStream.closeNode();
|
|
260
263
|
// Col items - use parsed items if available
|
|
261
264
|
if (model.colItems && model.colItems.length > 0) {
|
|
262
265
|
xmlStream.openNode("colItems", { count: model.colItems.length });
|
|
@@ -459,6 +462,10 @@ class PivotTableXform extends BaseXform {
|
|
|
459
462
|
break;
|
|
460
463
|
case "colFields":
|
|
461
464
|
this.state.inColFields = true;
|
|
465
|
+
// Track that colFields element was present in original file
|
|
466
|
+
if (this.model) {
|
|
467
|
+
this.model.hasColFields = true;
|
|
468
|
+
}
|
|
462
469
|
break;
|
|
463
470
|
case "dataFields":
|
|
464
471
|
this.state.inDataFields = true;
|
|
@@ -48,7 +48,10 @@ class RowXform extends BaseXform {
|
|
|
48
48
|
xmlStream.addAttribute("s", model.styleId);
|
|
49
49
|
xmlStream.addAttribute("customFormat", "1");
|
|
50
50
|
}
|
|
51
|
-
//
|
|
51
|
+
// Output dyDescent if present (MS extension for font descent)
|
|
52
|
+
if (model.dyDescent !== undefined) {
|
|
53
|
+
xmlStream.addAttribute("x14ac:dyDescent", model.dyDescent);
|
|
54
|
+
}
|
|
52
55
|
if (model.outlineLevel) {
|
|
53
56
|
xmlStream.addAttribute("outlineLevel", model.outlineLevel);
|
|
54
57
|
}
|
|
@@ -99,6 +102,9 @@ class RowXform extends BaseXform {
|
|
|
99
102
|
if (parseBoolean(node.attributes.collapsed)) {
|
|
100
103
|
model.collapsed = true;
|
|
101
104
|
}
|
|
105
|
+
if (node.attributes["x14ac:dyDescent"] !== undefined) {
|
|
106
|
+
model.dyDescent = parseFloat(node.attributes["x14ac:dyDescent"]);
|
|
107
|
+
}
|
|
102
108
|
return true;
|
|
103
109
|
}
|
|
104
110
|
this.parser = this.map[node.name];
|
|
@@ -11,14 +11,14 @@ class SheetFormatPropertiesXform extends BaseXform {
|
|
|
11
11
|
outlineLevelRow: model.outlineLevelRow || undefined,
|
|
12
12
|
outlineLevelCol: model.outlineLevelCol || undefined,
|
|
13
13
|
// Only output dyDescent if explicitly set (MS extension, not ECMA-376 standard)
|
|
14
|
-
"x14ac:dyDescent": model.dyDescent
|
|
14
|
+
"x14ac:dyDescent": model.dyDescent !== undefined && model.dyDescent !== 0 ? model.dyDescent : undefined
|
|
15
15
|
};
|
|
16
16
|
// Only output defaultColWidth if explicitly set
|
|
17
17
|
if (model.defaultColWidth) {
|
|
18
18
|
attributes.defaultColWidth = model.defaultColWidth;
|
|
19
19
|
}
|
|
20
|
-
//
|
|
21
|
-
if (
|
|
20
|
+
// Only output customHeight if it was present in the original file
|
|
21
|
+
if (model.customHeight) {
|
|
22
22
|
attributes.customHeight = "1";
|
|
23
23
|
}
|
|
24
24
|
if (Object.values(attributes).some((value) => value !== undefined)) {
|
|
@@ -30,13 +30,18 @@ class SheetFormatPropertiesXform extends BaseXform {
|
|
|
30
30
|
if (node.name === "sheetFormatPr") {
|
|
31
31
|
this.model = {
|
|
32
32
|
defaultRowHeight: parseFloat(node.attributes.defaultRowHeight || "0"),
|
|
33
|
-
dyDescent:
|
|
33
|
+
dyDescent: node.attributes["x14ac:dyDescent"] !== undefined
|
|
34
|
+
? parseFloat(node.attributes["x14ac:dyDescent"])
|
|
35
|
+
: undefined,
|
|
34
36
|
outlineLevelRow: parseInt(node.attributes.outlineLevelRow || "0", 10),
|
|
35
37
|
outlineLevelCol: parseInt(node.attributes.outlineLevelCol || "0", 10)
|
|
36
38
|
};
|
|
37
39
|
if (node.attributes.defaultColWidth) {
|
|
38
40
|
this.model.defaultColWidth = parseFloat(node.attributes.defaultColWidth);
|
|
39
41
|
}
|
|
42
|
+
if (node.attributes.customHeight === "1") {
|
|
43
|
+
this.model.customHeight = true;
|
|
44
|
+
}
|
|
40
45
|
return true;
|
|
41
46
|
}
|
|
42
47
|
return false;
|
|
@@ -37,8 +37,8 @@ class SheetViewXform extends BaseXform {
|
|
|
37
37
|
add("showRuler", "0", model.showRuler === false);
|
|
38
38
|
add("showRowColHeaders", "0", model.showRowColHeaders === false);
|
|
39
39
|
add("showGridLines", "0", model.showGridLines === false);
|
|
40
|
-
add("zoomScale", model.zoomScale, model.zoomScale);
|
|
41
|
-
add("zoomScaleNormal", model.zoomScaleNormal, model.zoomScaleNormal);
|
|
40
|
+
add("zoomScale", model.zoomScale, model.zoomScale !== undefined && model.zoomScale !== 100);
|
|
41
|
+
add("zoomScaleNormal", model.zoomScaleNormal, model.zoomScaleNormal !== undefined && model.zoomScaleNormal !== 100);
|
|
42
42
|
add("view", model.style, model.style);
|
|
43
43
|
let topLeftCell;
|
|
44
44
|
let xSplit;
|
|
@@ -143,6 +143,7 @@ class SheetViewXform extends BaseXform {
|
|
|
143
143
|
model = this.model = {
|
|
144
144
|
workbookViewId: this.sheetView.workbookViewId,
|
|
145
145
|
rightToLeft: this.sheetView.rightToLeft,
|
|
146
|
+
tabSelected: this.sheetView.tabSelected,
|
|
146
147
|
state: VIEW_STATES[this.pane.state] || "split", // split is default
|
|
147
148
|
xSplit: this.pane.xSplit,
|
|
148
149
|
ySplit: this.pane.ySplit,
|
|
@@ -168,6 +169,7 @@ class SheetViewXform extends BaseXform {
|
|
|
168
169
|
model = this.model = {
|
|
169
170
|
workbookViewId: this.sheetView.workbookViewId,
|
|
170
171
|
rightToLeft: this.sheetView.rightToLeft,
|
|
172
|
+
tabSelected: this.sheetView.tabSelected,
|
|
171
173
|
state: "normal",
|
|
172
174
|
showRuler: this.sheetView.showRuler,
|
|
173
175
|
showRowColHeaders: this.sheetView.showRowColHeaders,
|
|
@@ -384,7 +384,8 @@ class WorkSheetXform extends BaseXform {
|
|
|
384
384
|
defaultRowHeight: model.properties.defaultRowHeight,
|
|
385
385
|
dyDescent: model.properties.dyDescent,
|
|
386
386
|
outlineLevelCol: model.properties.outlineLevelCol,
|
|
387
|
-
outlineLevelRow: model.properties.outlineLevelRow
|
|
387
|
+
outlineLevelRow: model.properties.outlineLevelRow,
|
|
388
|
+
customHeight: model.properties.customHeight
|
|
388
389
|
}
|
|
389
390
|
: undefined;
|
|
390
391
|
if (model.properties && model.properties.defaultColWidth) {
|
|
@@ -24,24 +24,27 @@ class StyleXform extends BaseXform {
|
|
|
24
24
|
if (this.xfId) {
|
|
25
25
|
xmlStream.addAttribute("xfId", model.xfId || 0);
|
|
26
26
|
}
|
|
27
|
-
if (model.numFmtId) {
|
|
27
|
+
if (model.applyNumberFormat || model.numFmtId) {
|
|
28
28
|
xmlStream.addAttribute("applyNumberFormat", "1");
|
|
29
29
|
}
|
|
30
|
-
if (model.fontId) {
|
|
30
|
+
if (model.applyFont || model.fontId) {
|
|
31
31
|
xmlStream.addAttribute("applyFont", "1");
|
|
32
32
|
}
|
|
33
|
-
if (model.fillId) {
|
|
33
|
+
if (model.applyFill || model.fillId) {
|
|
34
34
|
xmlStream.addAttribute("applyFill", "1");
|
|
35
35
|
}
|
|
36
|
-
if (model.borderId) {
|
|
36
|
+
if (model.applyBorder || model.borderId) {
|
|
37
37
|
xmlStream.addAttribute("applyBorder", "1");
|
|
38
38
|
}
|
|
39
|
-
if (model.alignment) {
|
|
39
|
+
if (model.applyAlignment || model.alignment) {
|
|
40
40
|
xmlStream.addAttribute("applyAlignment", "1");
|
|
41
41
|
}
|
|
42
|
-
if (model.protection) {
|
|
42
|
+
if (model.applyProtection || model.protection) {
|
|
43
43
|
xmlStream.addAttribute("applyProtection", "1");
|
|
44
44
|
}
|
|
45
|
+
if (model.pivotButton) {
|
|
46
|
+
xmlStream.addAttribute("pivotButton", "1");
|
|
47
|
+
}
|
|
45
48
|
/**
|
|
46
49
|
* Rendering tags causes close of XML stream.
|
|
47
50
|
* Therefore adding attributes must be done before rendering tags.
|
|
@@ -82,6 +85,23 @@ class StyleXform extends BaseXform {
|
|
|
82
85
|
if (this.xfId) {
|
|
83
86
|
this.model.xfId = parseInt(node.attributes.xfId, 10);
|
|
84
87
|
}
|
|
88
|
+
if (node.attributes.pivotButton === "1") {
|
|
89
|
+
this.model.pivotButton = true;
|
|
90
|
+
}
|
|
91
|
+
// Preserve apply* flags from original file
|
|
92
|
+
const applyFlags = [
|
|
93
|
+
"applyNumberFormat",
|
|
94
|
+
"applyFont",
|
|
95
|
+
"applyFill",
|
|
96
|
+
"applyBorder",
|
|
97
|
+
"applyAlignment",
|
|
98
|
+
"applyProtection"
|
|
99
|
+
];
|
|
100
|
+
for (const flag of applyFlags) {
|
|
101
|
+
if (node.attributes[flag] === "1") {
|
|
102
|
+
this.model[flag] = true;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
85
105
|
return true;
|
|
86
106
|
case "alignment":
|
|
87
107
|
this.parser = this.map.alignment;
|