@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.
Files changed (53) hide show
  1. package/dist/browser/modules/excel/column.d.ts +5 -0
  2. package/dist/browser/modules/excel/column.js +10 -2
  3. package/dist/browser/modules/excel/row.d.ts +2 -0
  4. package/dist/browser/modules/excel/row.js +3 -1
  5. package/dist/browser/modules/excel/workbook.d.ts +4 -0
  6. package/dist/browser/modules/excel/workbook.js +4 -1
  7. package/dist/browser/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.d.ts +1 -0
  8. package/dist/browser/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.js +17 -10
  9. package/dist/browser/modules/excel/xlsx/xform/sheet/row-xform.d.ts +1 -0
  10. package/dist/browser/modules/excel/xlsx/xform/sheet/row-xform.js +7 -1
  11. package/dist/browser/modules/excel/xlsx/xform/sheet/sheet-format-properties-xform.d.ts +1 -0
  12. package/dist/browser/modules/excel/xlsx/xform/sheet/sheet-format-properties-xform.js +9 -4
  13. package/dist/browser/modules/excel/xlsx/xform/sheet/sheet-view-xform.js +4 -2
  14. package/dist/browser/modules/excel/xlsx/xform/sheet/worksheet-xform.js +2 -1
  15. package/dist/browser/modules/excel/xlsx/xform/style/style-xform.d.ts +7 -0
  16. package/dist/browser/modules/excel/xlsx/xform/style/style-xform.js +26 -6
  17. package/dist/browser/modules/excel/xlsx/xform/style/styles-xform.d.ts +6 -0
  18. package/dist/browser/modules/excel/xlsx/xform/style/styles-xform.js +52 -4
  19. package/dist/browser/modules/excel/xlsx/xlsx.browser.js +7 -0
  20. package/dist/cjs/modules/excel/column.js +10 -2
  21. package/dist/cjs/modules/excel/row.js +3 -1
  22. package/dist/cjs/modules/excel/workbook.js +4 -1
  23. package/dist/cjs/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.js +17 -10
  24. package/dist/cjs/modules/excel/xlsx/xform/sheet/row-xform.js +7 -1
  25. package/dist/cjs/modules/excel/xlsx/xform/sheet/sheet-format-properties-xform.js +9 -4
  26. package/dist/cjs/modules/excel/xlsx/xform/sheet/sheet-view-xform.js +4 -2
  27. package/dist/cjs/modules/excel/xlsx/xform/sheet/worksheet-xform.js +2 -1
  28. package/dist/cjs/modules/excel/xlsx/xform/style/style-xform.js +26 -6
  29. package/dist/cjs/modules/excel/xlsx/xform/style/styles-xform.js +52 -4
  30. package/dist/cjs/modules/excel/xlsx/xlsx.browser.js +7 -0
  31. package/dist/esm/modules/excel/column.js +10 -2
  32. package/dist/esm/modules/excel/row.js +3 -1
  33. package/dist/esm/modules/excel/workbook.js +4 -1
  34. package/dist/esm/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.js +17 -10
  35. package/dist/esm/modules/excel/xlsx/xform/sheet/row-xform.js +7 -1
  36. package/dist/esm/modules/excel/xlsx/xform/sheet/sheet-format-properties-xform.js +9 -4
  37. package/dist/esm/modules/excel/xlsx/xform/sheet/sheet-view-xform.js +4 -2
  38. package/dist/esm/modules/excel/xlsx/xform/sheet/worksheet-xform.js +2 -1
  39. package/dist/esm/modules/excel/xlsx/xform/style/style-xform.js +26 -6
  40. package/dist/esm/modules/excel/xlsx/xform/style/styles-xform.js +52 -4
  41. package/dist/esm/modules/excel/xlsx/xlsx.browser.js +7 -0
  42. package/dist/iife/excelts.iife.js +450 -394
  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 +5 -0
  46. package/dist/types/modules/excel/row.d.ts +2 -0
  47. package/dist/types/modules/excel/workbook.d.ts +4 -0
  48. package/dist/types/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.d.ts +1 -0
  49. package/dist/types/modules/excel/xlsx/xform/sheet/row-xform.d.ts +1 -0
  50. package/dist/types/modules/excel/xlsx/xform/sheet/sheet-format-properties-xform.d.ts +1 -0
  51. package/dist/types/modules/excel/xlsx/xform/style/style-xform.d.ts +7 -0
  52. package/dist/types/modules/excel/xlsx/xform/style/styles-xform.d.ts +6 -0
  53. package/package.json +14 -14
@@ -11,6 +11,8 @@ export interface ColumnDefn {
11
11
  outlineLevel?: number;
12
12
  hidden?: boolean;
13
13
  style?: Partial<Style>;
14
+ /** Whether the column width is auto-fitted to content */
15
+ bestFit?: boolean;
14
16
  }
15
17
  export interface ColumnModel {
16
18
  min: number;
@@ -21,6 +23,7 @@ export interface ColumnModel {
21
23
  hidden?: boolean;
22
24
  outlineLevel?: number;
23
25
  collapsed?: boolean;
26
+ bestFit?: boolean;
24
27
  }
25
28
  /**
26
29
  * Column defines the column properties for 1 column.
@@ -36,6 +39,8 @@ declare class Column {
36
39
  width?: number;
37
40
  private _hidden;
38
41
  private _outlineLevel;
42
+ /** Whether the column width is auto-fitted to content */
43
+ bestFit?: boolean;
39
44
  /** Styles applied to the column */
40
45
  style: Partial<Style>;
41
46
  constructor(worksheet: Worksheet, number: number, defn?: ColumnDefn | false);
@@ -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
  }
@@ -15,6 +15,7 @@ export interface RowModel {
15
15
  hidden: boolean;
16
16
  outlineLevel: number;
17
17
  collapsed: boolean;
18
+ dyDescent?: number;
18
19
  }
19
20
  declare class Row {
20
21
  private _worksheet;
@@ -24,6 +25,7 @@ declare class Row {
24
25
  private _hidden?;
25
26
  private _outlineLevel?;
26
27
  height?: number;
28
+ dyDescent?: number;
27
29
  constructor(worksheet: Worksheet, number: number);
28
30
  /**
29
31
  * The row number
@@ -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
  }
@@ -59,6 +59,8 @@ export interface WorkbookModel {
59
59
  passthrough?: Record<string, Uint8Array>;
60
60
  /** Raw drawing XML data for passthrough (when drawing contains chart references) */
61
61
  rawDrawings?: Record<string, Uint8Array>;
62
+ /** Default font preserved from the original file for round-trip fidelity */
63
+ defaultFont?: any;
62
64
  }
63
65
  declare class Workbook {
64
66
  /**
@@ -102,6 +104,8 @@ declare class Workbook {
102
104
  private _passthrough;
103
105
  /** Raw drawing XML data for passthrough (when drawing contains chart references) */
104
106
  private _rawDrawings;
107
+ /** Default font preserved from original file for round-trip fidelity */
108
+ private _defaultFont?;
105
109
  private _xlsx?;
106
110
  private _csv?;
107
111
  constructor();
@@ -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
  // ===========================================================================
@@ -72,6 +72,7 @@ interface ParsedPivotTableModel {
72
72
  chartFormat?: number;
73
73
  rowItems?: RowColItem[];
74
74
  colItems?: RowColItem[];
75
+ hasColFields?: boolean;
75
76
  chartFormats?: ChartFormatItem[];
76
77
  isLoaded?: boolean;
77
78
  extensions?: any[];
@@ -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
- // 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 });
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;
@@ -14,6 +14,7 @@ interface RowModel {
14
14
  outlineLevel?: number;
15
15
  collapsed?: boolean;
16
16
  style?: any;
17
+ dyDescent?: number;
17
18
  }
18
19
  declare class RowXform extends BaseXform {
19
20
  private maxItems?;
@@ -48,7 +48,10 @@ class RowXform extends BaseXform {
48
48
  xmlStream.addAttribute("s", model.styleId);
49
49
  xmlStream.addAttribute("customFormat", "1");
50
50
  }
51
- // Note: dyDescent is MS extension, not output by default (Excel auto-calculates)
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];
@@ -5,6 +5,7 @@ interface SheetFormatPropertiesModel {
5
5
  outlineLevelRow: number;
6
6
  outlineLevelCol: number;
7
7
  defaultColWidth?: number;
8
+ customHeight?: boolean;
8
9
  }
9
10
  declare class SheetFormatPropertiesXform extends BaseXform {
10
11
  get tag(): string;
@@ -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
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
- // default value for 'defaultRowHeight' is 15, this should not be 'custom'
21
- if (!model.defaultRowHeight || model.defaultRowHeight !== 15) {
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: parseFloat(node.attributes["x14ac:dyDescent"] || "0"),
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) {
@@ -11,6 +11,13 @@ interface StyleModel {
11
11
  protection?: any;
12
12
  checkbox?: boolean;
13
13
  xfComplementIndex?: number;
14
+ pivotButton?: boolean;
15
+ applyNumberFormat?: boolean;
16
+ applyFont?: boolean;
17
+ applyFill?: boolean;
18
+ applyBorder?: boolean;
19
+ applyAlignment?: boolean;
20
+ applyProtection?: boolean;
14
21
  }
15
22
  interface StyleOptions {
16
23
  xfId?: boolean;
@@ -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;
@@ -12,11 +12,17 @@ declare class StylesXform extends BaseXform {
12
12
  private index?;
13
13
  private weakMap?;
14
14
  private _hasCheckboxes?;
15
+ defaultFont?: any;
15
16
  parser: any;
16
17
  static Mock: typeof StylesXform;
17
18
  constructor(initialise?: boolean);
18
19
  initIndex(): void;
19
20
  init(): void;
21
+ /**
22
+ * Set the default font to use when no font is explicitly specified.
23
+ * This preserves the original file's default font during round-trip.
24
+ */
25
+ setDefaultFont(font: any): void;
20
26
  render(xmlStream: any, model?: StylesModel): void;
21
27
  parseOpen(node: any): boolean;
22
28
  parseText(text: string): void;
@@ -84,6 +84,13 @@ class StylesXform extends BaseXform {
84
84
  this.weakMap = new WeakMap();
85
85
  this._hasCheckboxes = false;
86
86
  }
87
+ /**
88
+ * Set the default font to use when no font is explicitly specified.
89
+ * This preserves the original file's default font during round-trip.
90
+ */
91
+ setDefaultFont(font) {
92
+ this.defaultFont = font;
93
+ }
87
94
  render(xmlStream, model) {
88
95
  const renderModel = model || this.model;
89
96
  //
@@ -100,8 +107,8 @@ class StylesXform extends BaseXform {
100
107
  xmlStream.closeNode();
101
108
  }
102
109
  if (!renderModel.fonts.length) {
103
- // default (zero) font
104
- this._addFont({
110
+ // default (zero) font - use preserved font or fallback to Calibri
111
+ this._addFont(this.defaultFont || {
105
112
  size: 11,
106
113
  color: { theme: 1 },
107
114
  name: "Calibri",
@@ -193,6 +200,10 @@ class StylesXform extends BaseXform {
193
200
  add("borders", this.map.borders);
194
201
  add("styles", this.map.cellXfs);
195
202
  add("dxfs", this.map.dxfs);
203
+ // preserve the default (first) font from the original file
204
+ if (this.map.fonts.model && this.map.fonts.model.length > 0) {
205
+ this.defaultFont = this.map.fonts.model[0];
206
+ }
196
207
  // index numFmts
197
208
  this.index = {
198
209
  model: [],
@@ -220,8 +231,14 @@ class StylesXform extends BaseXform {
220
231
  }
221
232
  // if we have no default font, add it here now
222
233
  if (!this.model.fonts.length) {
223
- // default (zero) font
224
- this._addFont({ size: 11, color: { theme: 1 }, name: "Calibri", family: 2, scheme: "minor" });
234
+ // default (zero) font - use preserved font or fallback to Calibri
235
+ this._addFont(this.defaultFont || {
236
+ size: 11,
237
+ color: { theme: 1 },
238
+ name: "Calibri",
239
+ family: 2,
240
+ scheme: "minor"
241
+ });
225
242
  }
226
243
  const type = cellType || Enums.ValueType.Number;
227
244
  // If we have seen this style object before, assume it has the same styleId.
@@ -262,6 +279,21 @@ class StylesXform extends BaseXform {
262
279
  if (model.protection) {
263
280
  style.protection = model.protection;
264
281
  }
282
+ // Preserve xf-level attributes (pivotButton, apply* flags)
283
+ const xfFlags = [
284
+ "pivotButton",
285
+ "applyNumberFormat",
286
+ "applyFont",
287
+ "applyFill",
288
+ "applyBorder",
289
+ "applyAlignment",
290
+ "applyProtection"
291
+ ];
292
+ for (const flag of xfFlags) {
293
+ if (model[flag]) {
294
+ style[flag] = true;
295
+ }
296
+ }
265
297
  if (type === Enums.ValueType.Checkbox) {
266
298
  // Checkbox rendering relies on style extensions (extLst) and workbook-level parts.
267
299
  // Force applyAlignment="1" (without emitting an <alignment/> node) by providing
@@ -322,6 +354,22 @@ class StylesXform extends BaseXform {
322
354
  if (style.protection) {
323
355
  model.protection = style.protection;
324
356
  }
357
+ // -------------------------------------------------------
358
+ // xf-level attributes (pivotButton, apply* flags)
359
+ const xfFlags = [
360
+ "pivotButton",
361
+ "applyNumberFormat",
362
+ "applyFont",
363
+ "applyFill",
364
+ "applyBorder",
365
+ "applyAlignment",
366
+ "applyProtection"
367
+ ];
368
+ for (const flag of xfFlags) {
369
+ if (style[flag]) {
370
+ model[flag] = true;
371
+ }
372
+ }
325
373
  return model;
326
374
  }
327
375
  addDxfStyle(style) {
@@ -600,6 +600,8 @@ class XLSX {
600
600
  delete model.sharedStrings;
601
601
  delete model.workbookRels;
602
602
  delete model.sheetDefs;
603
+ // Preserve default font before deleting styles
604
+ model.defaultFont = model.styles?.defaultFont;
603
605
  delete model.styles;
604
606
  delete model.mediaIndex;
605
607
  delete model.drawings;
@@ -1235,7 +1237,12 @@ class XLSX {
1235
1237
  options.useSharedStrings !== undefined ? options.useSharedStrings : true;
1236
1238
  model.useStyles = options.useStyles !== undefined ? options.useStyles : true;
1237
1239
  model.sharedStrings = new SharedStringsXform();
1240
+ // Preserve default font from parsed styles if available
1241
+ const oldDefaultFont = model.defaultFont;
1238
1242
  model.styles = model.useStyles ? new StylesXform(true) : new StylesXform.Mock();
1243
+ if (oldDefaultFont && model.styles.setDefaultFont) {
1244
+ model.styles.setDefaultFont(oldDefaultFont);
1245
+ }
1239
1246
  const workbookXform = new WorkbookXform();
1240
1247
  const worksheetXform = new WorkSheetXform();
1241
1248
  workbookXform.prepare(model);
@@ -41,7 +41,8 @@ class Column {
41
41
  width: this.width,
42
42
  style: this.style,
43
43
  hidden: this.hidden,
44
- outlineLevel: this.outlineLevel
44
+ outlineLevel: this.outlineLevel,
45
+ bestFit: this.bestFit
45
46
  };
46
47
  }
47
48
  set defn(value) {
@@ -58,6 +59,7 @@ class Column {
58
59
  // headers must be set after style
59
60
  this.header = value.header;
60
61
  this._hidden = !!value.hidden;
62
+ this.bestFit = value.bestFit;
61
63
  }
62
64
  else {
63
65
  delete this._header;
@@ -65,6 +67,7 @@ class Column {
65
67
  delete this.width;
66
68
  this.style = {};
67
69
  this.outlineLevel = 0;
70
+ delete this.bestFit;
68
71
  }
69
72
  }
70
73
  /**
@@ -154,6 +157,7 @@ class Column {
154
157
  return (this.width === model.width &&
155
158
  this.hidden === model.hidden &&
156
159
  this.outlineLevel === model.outlineLevel &&
160
+ this.bestFit === model.bestFit &&
157
161
  (0, under_dash_1.isEqual)(this.style, model.style));
158
162
  }
159
163
  get isDefault() {
@@ -166,6 +170,9 @@ class Column {
166
170
  if (this.outlineLevel) {
167
171
  return false;
168
172
  }
173
+ if (this.bestFit) {
174
+ return false;
175
+ }
169
176
  const s = this.style;
170
177
  if (s && (s.font || s.numFmt || s.alignment || s.border || s.fill || s.protection)) {
171
178
  return false;
@@ -316,7 +323,8 @@ class Column {
316
323
  isCustomWidth: column.isCustomWidth,
317
324
  hidden: column.hidden,
318
325
  outlineLevel: column.outlineLevel,
319
- collapsed: column.collapsed
326
+ collapsed: column.collapsed,
327
+ bestFit: column.bestFit
320
328
  };
321
329
  cols.push(col);
322
330
  }
@@ -391,7 +391,8 @@ class Row {
391
391
  style: this.style,
392
392
  hidden: this.hidden,
393
393
  outlineLevel: this.outlineLevel,
394
- collapsed: this.collapsed
394
+ collapsed: this.collapsed,
395
+ dyDescent: this.dyDescent
395
396
  }
396
397
  : null;
397
398
  }
@@ -438,6 +439,7 @@ class Row {
438
439
  }
439
440
  this.hidden = value.hidden;
440
441
  this.outlineLevel = value.outlineLevel || 0;
442
+ this.dyDescent = value.dyDescent;
441
443
  this.style = (value.style && JSON.parse(JSON.stringify(value.style))) || {};
442
444
  }
443
445
  }
@@ -258,7 +258,8 @@ class Workbook {
258
258
  pivotTables: this.pivotTables,
259
259
  calcProperties: this.calcProperties,
260
260
  passthrough: this._passthrough,
261
- rawDrawings: this._rawDrawings
261
+ rawDrawings: this._rawDrawings,
262
+ defaultFont: this._defaultFont
262
263
  };
263
264
  }
264
265
  set model(value) {
@@ -303,6 +304,8 @@ class Workbook {
303
304
  this._passthrough = value.passthrough || {};
304
305
  // Preserve raw drawing data for drawings with chart references
305
306
  this._rawDrawings = value.rawDrawings || {};
307
+ // Preserve default font for round-trip fidelity
308
+ this._defaultFont = value.defaultFont;
306
309
  }
307
310
  }
308
311
  exports.Workbook = Workbook;