@cj-tech-master/excelts 5.0.0-canary.20260123012457.1fdf506 → 5.0.0

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