@cj-tech-master/excelts 4.2.3 → 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 (84) hide show
  1. package/dist/browser/modules/csv/csv-core.d.ts +0 -9
  2. package/dist/browser/modules/csv/csv.browser.js +3 -3
  3. package/dist/browser/modules/excel/column.d.ts +5 -0
  4. package/dist/browser/modules/excel/column.js +10 -2
  5. package/dist/browser/modules/excel/row.d.ts +2 -0
  6. package/dist/browser/modules/excel/row.js +3 -1
  7. package/dist/browser/modules/excel/utils/parse-sax.d.ts +0 -3
  8. package/dist/browser/modules/excel/utils/parse-sax.js +13 -32
  9. package/dist/browser/modules/excel/utils/passthrough-manager.d.ts +77 -0
  10. package/dist/browser/modules/excel/utils/passthrough-manager.js +129 -0
  11. package/dist/browser/modules/excel/workbook.d.ts +12 -0
  12. package/dist/browser/modules/excel/workbook.js +12 -1
  13. package/dist/browser/modules/excel/worksheet.d.ts +4 -0
  14. package/dist/browser/modules/excel/worksheet.js +4 -1
  15. package/dist/browser/modules/excel/xlsx/xform/base-xform.js +68 -1
  16. package/dist/browser/modules/excel/xlsx/xform/core/content-types-xform.js +16 -10
  17. package/dist/browser/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.d.ts +35 -11
  18. package/dist/browser/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.js +271 -94
  19. package/dist/browser/modules/excel/xlsx/xform/sheet/row-xform.d.ts +1 -0
  20. package/dist/browser/modules/excel/xlsx/xform/sheet/row-xform.js +7 -1
  21. package/dist/browser/modules/excel/xlsx/xform/sheet/sheet-format-properties-xform.d.ts +1 -0
  22. package/dist/browser/modules/excel/xlsx/xform/sheet/sheet-format-properties-xform.js +9 -4
  23. package/dist/browser/modules/excel/xlsx/xform/sheet/sheet-view-xform.js +4 -2
  24. package/dist/browser/modules/excel/xlsx/xform/sheet/worksheet-xform.js +40 -12
  25. package/dist/browser/modules/excel/xlsx/xform/style/style-xform.d.ts +7 -0
  26. package/dist/browser/modules/excel/xlsx/xform/style/style-xform.js +26 -6
  27. package/dist/browser/modules/excel/xlsx/xform/style/styles-xform.d.ts +6 -0
  28. package/dist/browser/modules/excel/xlsx/xform/style/styles-xform.js +52 -4
  29. package/dist/browser/modules/excel/xlsx/xlsx.browser.d.ts +36 -1
  30. package/dist/browser/modules/excel/xlsx/xlsx.browser.js +220 -131
  31. package/dist/browser/modules/stream/streams.browser.js +0 -3
  32. package/dist/cjs/modules/csv/csv.browser.js +3 -3
  33. package/dist/cjs/modules/excel/column.js +10 -2
  34. package/dist/cjs/modules/excel/row.js +3 -1
  35. package/dist/cjs/modules/excel/utils/parse-sax.js +13 -32
  36. package/dist/cjs/modules/excel/utils/passthrough-manager.js +133 -0
  37. package/dist/cjs/modules/excel/workbook.js +12 -1
  38. package/dist/cjs/modules/excel/worksheet.js +4 -1
  39. package/dist/cjs/modules/excel/xlsx/xform/base-xform.js +68 -1
  40. package/dist/cjs/modules/excel/xlsx/xform/core/content-types-xform.js +16 -10
  41. package/dist/cjs/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.js +271 -94
  42. package/dist/cjs/modules/excel/xlsx/xform/sheet/row-xform.js +7 -1
  43. package/dist/cjs/modules/excel/xlsx/xform/sheet/sheet-format-properties-xform.js +9 -4
  44. package/dist/cjs/modules/excel/xlsx/xform/sheet/sheet-view-xform.js +4 -2
  45. package/dist/cjs/modules/excel/xlsx/xform/sheet/worksheet-xform.js +40 -12
  46. package/dist/cjs/modules/excel/xlsx/xform/style/style-xform.js +26 -6
  47. package/dist/cjs/modules/excel/xlsx/xform/style/styles-xform.js +52 -4
  48. package/dist/cjs/modules/excel/xlsx/xlsx.browser.js +220 -131
  49. package/dist/cjs/modules/stream/streams.browser.js +0 -3
  50. package/dist/esm/modules/csv/csv.browser.js +3 -3
  51. package/dist/esm/modules/excel/column.js +10 -2
  52. package/dist/esm/modules/excel/row.js +3 -1
  53. package/dist/esm/modules/excel/utils/parse-sax.js +13 -32
  54. package/dist/esm/modules/excel/utils/passthrough-manager.js +129 -0
  55. package/dist/esm/modules/excel/workbook.js +12 -1
  56. package/dist/esm/modules/excel/worksheet.js +4 -1
  57. package/dist/esm/modules/excel/xlsx/xform/base-xform.js +68 -1
  58. package/dist/esm/modules/excel/xlsx/xform/core/content-types-xform.js +16 -10
  59. package/dist/esm/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.js +271 -94
  60. package/dist/esm/modules/excel/xlsx/xform/sheet/row-xform.js +7 -1
  61. package/dist/esm/modules/excel/xlsx/xform/sheet/sheet-format-properties-xform.js +9 -4
  62. package/dist/esm/modules/excel/xlsx/xform/sheet/sheet-view-xform.js +4 -2
  63. package/dist/esm/modules/excel/xlsx/xform/sheet/worksheet-xform.js +40 -12
  64. package/dist/esm/modules/excel/xlsx/xform/style/style-xform.js +26 -6
  65. package/dist/esm/modules/excel/xlsx/xform/style/styles-xform.js +52 -4
  66. package/dist/esm/modules/excel/xlsx/xlsx.browser.js +220 -131
  67. package/dist/esm/modules/stream/streams.browser.js +0 -3
  68. package/dist/iife/excelts.iife.js +1009 -650
  69. package/dist/iife/excelts.iife.js.map +1 -1
  70. package/dist/iife/excelts.iife.min.js +25 -52
  71. package/dist/types/modules/csv/csv-core.d.ts +0 -9
  72. package/dist/types/modules/excel/column.d.ts +5 -0
  73. package/dist/types/modules/excel/row.d.ts +2 -0
  74. package/dist/types/modules/excel/utils/parse-sax.d.ts +0 -3
  75. package/dist/types/modules/excel/utils/passthrough-manager.d.ts +77 -0
  76. package/dist/types/modules/excel/workbook.d.ts +12 -0
  77. package/dist/types/modules/excel/worksheet.d.ts +4 -0
  78. package/dist/types/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.d.ts +35 -11
  79. package/dist/types/modules/excel/xlsx/xform/sheet/row-xform.d.ts +1 -0
  80. package/dist/types/modules/excel/xlsx/xform/sheet/sheet-format-properties-xform.d.ts +1 -0
  81. package/dist/types/modules/excel/xlsx/xform/style/style-xform.d.ts +7 -0
  82. package/dist/types/modules/excel/xlsx/xform/style/styles-xform.d.ts +6 -0
  83. package/dist/types/modules/excel/xlsx/xlsx.browser.d.ts +36 -1
  84. package/package.json +15 -15
@@ -7,18 +7,30 @@ const base_xform_1 = require("../base-xform.js");
7
7
  class PivotTableXform extends base_xform_1.BaseXform {
8
8
  constructor() {
9
9
  super();
10
+ // Parser state consolidated into object for easier reset
11
+ this.state = {
12
+ inPivotFields: false,
13
+ inRowFields: false,
14
+ inColFields: false,
15
+ inDataFields: false,
16
+ inRowItems: false,
17
+ inColItems: false,
18
+ inLocation: false,
19
+ inItems: false,
20
+ inPivotTableStyleInfo: false,
21
+ inChartFormats: false,
22
+ inPivotArea: false
23
+ };
24
+ // Current parsing context
25
+ this.currentPivotField = null;
26
+ this.currentRowItem = null;
27
+ this.currentColItem = null;
28
+ this.currentChartFormat = null;
29
+ // Buffer for collecting pivotArea XML
30
+ this.pivotAreaXmlBuffer = [];
31
+ this.pivotAreaDepth = 0;
10
32
  this.map = {};
11
33
  this.model = null;
12
- this.inPivotFields = false;
13
- this.inRowFields = false;
14
- this.inColFields = false;
15
- this.inDataFields = false;
16
- this.inRowItems = false;
17
- this.inColItems = false;
18
- this.inLocation = false;
19
- this.currentPivotField = null;
20
- this.inItems = false;
21
- this.inPivotTableStyleInfo = false;
22
34
  }
23
35
  prepare(_model) {
24
36
  // No preparation needed
@@ -29,16 +41,17 @@ class PivotTableXform extends base_xform_1.BaseXform {
29
41
  }
30
42
  reset() {
31
43
  this.model = null;
32
- this.inPivotFields = false;
33
- this.inRowFields = false;
34
- this.inColFields = false;
35
- this.inDataFields = false;
36
- this.inRowItems = false;
37
- this.inColItems = false;
38
- this.inLocation = false;
44
+ // Reset all parser state flags using object
45
+ Object.keys(this.state).forEach(key => {
46
+ this.state[key] = false;
47
+ });
48
+ // Reset current context
39
49
  this.currentPivotField = null;
40
- this.inItems = false;
41
- this.inPivotTableStyleInfo = false;
50
+ this.currentRowItem = null;
51
+ this.currentColItem = null;
52
+ this.currentChartFormat = null;
53
+ this.pivotAreaXmlBuffer = [];
54
+ this.pivotAreaDepth = 0;
42
55
  }
43
56
  /**
44
57
  * Render pivot table XML.
@@ -159,8 +172,7 @@ class PivotTableXform extends base_xform_1.BaseXform {
159
172
  * Render loaded pivot table (preserving original structure)
160
173
  */
161
174
  renderLoaded(xmlStream, model) {
162
- xmlStream.openXml(xml_stream_1.XmlStream.StdDocAttributes);
163
- xmlStream.openNode(this.tag, {
175
+ const attrs = {
164
176
  ...PivotTableXform.PIVOT_TABLE_ATTRIBUTES,
165
177
  name: model.name || "PivotTable1",
166
178
  cacheId: model.cacheId,
@@ -169,7 +181,8 @@ class PivotTableXform extends base_xform_1.BaseXform {
169
181
  applyFontFormats: model.applyFontFormats || "0",
170
182
  applyPatternFormats: model.applyPatternFormats || "0",
171
183
  applyAlignmentFormats: model.applyAlignmentFormats || "0",
172
- applyWidthHeightFormats: model.applyWidthHeightFormats || "0",
184
+ // Preserve original value when present; default to Excel's typical "0".
185
+ applyWidthHeightFormats: model.applyWidthHeightFormats ?? "0",
173
186
  dataCaption: model.dataCaption || "Values",
174
187
  updatedVersion: model.updatedVersion || "8",
175
188
  minRefreshableVersion: model.minRefreshableVersion || "3",
@@ -177,10 +190,27 @@ class PivotTableXform extends base_xform_1.BaseXform {
177
190
  itemPrintTitles: model.itemPrintTitles ? "1" : "0",
178
191
  createdVersion: model.createdVersion || "8",
179
192
  indent: model.indent !== undefined ? String(model.indent) : "0",
180
- compact: model.compact ? "1" : "0",
181
- compactData: model.compactData ? "1" : "0",
182
193
  multipleFieldFilters: model.multipleFieldFilters ? "1" : "0"
183
- });
194
+ };
195
+ // Add outline attributes if present
196
+ if (model.outline) {
197
+ attrs.outline = "1";
198
+ }
199
+ if (model.outlineData) {
200
+ attrs.outlineData = "1";
201
+ }
202
+ if (model.chartFormat !== undefined) {
203
+ attrs.chartFormat = String(model.chartFormat);
204
+ }
205
+ // Only add compact/compactData if they are true (some files don't have them)
206
+ if (model.compact) {
207
+ attrs.compact = "1";
208
+ }
209
+ if (model.compactData) {
210
+ attrs.compactData = "1";
211
+ }
212
+ xmlStream.openXml(xml_stream_1.XmlStream.StdDocAttributes);
213
+ xmlStream.openNode(this.tag, attrs);
184
214
  // Location
185
215
  if (model.location) {
186
216
  xmlStream.leafNode("location", {
@@ -206,42 +236,79 @@ class PivotTableXform extends base_xform_1.BaseXform {
206
236
  }
207
237
  xmlStream.closeNode();
208
238
  }
209
- // Row items (simplified - just grand total)
210
- xmlStream.writeXml(`
211
- <rowItems count="1">
212
- <i t="grand"><x /></i>
213
- </rowItems>`);
214
- // Col fields
215
- const colFieldCount = model.colFields.length === 0 ? 1 : model.colFields.length;
216
- xmlStream.openNode("colFields", { count: colFieldCount });
217
- if (model.colFields.length === 0) {
218
- xmlStream.leafNode("field", { x: -2 });
239
+ // Row items - use parsed items if available; otherwise emit a minimal grand total
240
+ if (model.rowItems && model.rowItems.length > 0) {
241
+ xmlStream.openNode("rowItems", { count: model.rowItems.length });
242
+ for (const item of model.rowItems) {
243
+ this.renderRowColItem(xmlStream, item);
244
+ }
245
+ xmlStream.closeNode();
219
246
  }
220
247
  else {
221
- for (const fieldIndex of model.colFields) {
222
- xmlStream.leafNode("field", { x: fieldIndex });
248
+ xmlStream.writeXml('<rowItems count="1"><i t="grand"><x/></i></rowItems>');
249
+ }
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
+ }
223
263
  }
264
+ xmlStream.closeNode();
265
+ }
266
+ // Col items - use parsed items if available
267
+ if (model.colItems && model.colItems.length > 0) {
268
+ xmlStream.openNode("colItems", { count: model.colItems.length });
269
+ for (const item of model.colItems) {
270
+ this.renderRowColItem(xmlStream, item);
271
+ }
272
+ xmlStream.closeNode();
273
+ }
274
+ else {
275
+ xmlStream.writeXml('<colItems count="1"><i t="grand"><x/></i></colItems>');
224
276
  }
225
- xmlStream.closeNode();
226
- // Col items (simplified - just grand total)
227
- xmlStream.writeXml(`
228
- <colItems count="1">
229
- <i t="grand"><x /></i>
230
- </colItems>`);
231
277
  // Data fields
232
278
  if (model.dataFields.length > 0) {
233
279
  xmlStream.openNode("dataFields", { count: model.dataFields.length });
234
280
  for (const dataField of model.dataFields) {
235
- const attrs = {
281
+ const dfAttrs = {
236
282
  name: dataField.name,
237
283
  fld: dataField.fld,
238
284
  baseField: dataField.baseField ?? 0,
239
285
  baseItem: dataField.baseItem ?? 0
240
286
  };
241
287
  if (dataField.subtotal && dataField.subtotal !== "sum") {
242
- attrs.subtotal = dataField.subtotal;
288
+ dfAttrs.subtotal = dataField.subtotal;
243
289
  }
244
- xmlStream.leafNode("dataField", attrs);
290
+ xmlStream.leafNode("dataField", dfAttrs);
291
+ }
292
+ xmlStream.closeNode();
293
+ }
294
+ // Chart formats (for pivot charts) - preserve original pivotArea XML
295
+ if (model.chartFormats && model.chartFormats.length > 0) {
296
+ xmlStream.openNode("chartFormats", { count: model.chartFormats.length });
297
+ for (const cf of model.chartFormats) {
298
+ xmlStream.openNode("chartFormat", {
299
+ chart: cf.chart,
300
+ format: cf.format,
301
+ series: cf.series ? "1" : undefined
302
+ });
303
+ // Use preserved pivotArea XML or fallback to default
304
+ if (cf.pivotAreaXml) {
305
+ xmlStream.writeXml(cf.pivotAreaXml);
306
+ }
307
+ else {
308
+ // Fallback for newly created chart formats (shouldn't happen for loaded models)
309
+ xmlStream.writeXml(`<pivotArea type="data" outline="0" fieldPosition="0"><references count="1"><reference field="4294967294" count="1" selected="0"><x v="0"/></reference></references></pivotArea>`);
310
+ }
311
+ xmlStream.closeNode();
245
312
  }
246
313
  xmlStream.closeNode();
247
314
  }
@@ -255,46 +322,48 @@ class PivotTableXform extends base_xform_1.BaseXform {
255
322
  showLastColumn: "1"
256
323
  });
257
324
  // Extensions
258
- xmlStream.writeXml(`
259
- <extLst>
260
- <ext
261
- uri="{962EF5D1-5CA2-4c93-8EF4-DBF5C05439D2}"
262
- xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main"
263
- >
264
- <x14:pivotTableDefinition
265
- hideValuesRow="1"
266
- xmlns:xm="http://schemas.microsoft.com/office/excel/2006/main"
267
- />
268
- </ext>
269
- <ext
270
- uri="{747A6164-185A-40DC-8AA5-F01512510D54}"
271
- xmlns:xpdl="http://schemas.microsoft.com/office/spreadsheetml/2016/pivotdefaultlayout"
272
- >
273
- <xpdl:pivotTableDefinition16
274
- EnabledSubtotalsDefault="0"
275
- SubtotalsOnTopDefault="0"
276
- />
277
- </ext>
278
- </extLst>
279
- `);
325
+ xmlStream.writeXml(`<extLst><ext uri="{962EF5D1-5CA2-4c93-8EF4-DBF5C05439D2}" xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main"><x14:pivotTableDefinition hideValuesRow="1" xmlns:xm="http://schemas.microsoft.com/office/excel/2006/main"/></ext><ext uri="{747A6164-185A-40DC-8AA5-F01512510D54}" xmlns:xpdl="http://schemas.microsoft.com/office/spreadsheetml/2016/pivotdefaultlayout"><xpdl:pivotTableDefinition16/></ext></extLst>`);
280
326
  xmlStream.closeNode();
281
327
  }
328
+ /**
329
+ * Render a row or column item element
330
+ */
331
+ renderRowColItem(xmlStream, item) {
332
+ const attrs = {};
333
+ if (item.t) {
334
+ attrs.t = item.t;
335
+ }
336
+ if (item.x && item.x.length > 0) {
337
+ xmlStream.openNode("i", attrs);
338
+ for (const x of item.x) {
339
+ if (x.v && x.v !== 0) {
340
+ xmlStream.leafNode("x", { v: x.v });
341
+ }
342
+ else {
343
+ xmlStream.leafNode("x");
344
+ }
345
+ }
346
+ xmlStream.closeNode();
347
+ }
348
+ else {
349
+ // Empty item (like <i/> in colItems)
350
+ xmlStream.leafNode("i", attrs);
351
+ }
352
+ }
282
353
  /**
283
354
  * Render a loaded pivot field
284
355
  */
285
356
  renderPivotFieldLoaded(xmlStream, field) {
286
- const attrs = {
287
- compact: field.compact ? "1" : "0",
288
- outline: field.outline ? "1" : "0",
289
- showAll: field.showAll ? "1" : "0",
290
- defaultSubtotal: field.defaultSubtotal ? "1" : "0"
291
- };
357
+ const attrs = {};
358
+ // Only add attributes that were present in the original
292
359
  if (field.axis) {
293
360
  attrs.axis = field.axis;
294
361
  }
295
362
  if (field.dataField) {
296
363
  attrs.dataField = "1";
297
364
  }
365
+ // showAll is typically always present
366
+ attrs.showAll = field.showAll ? "1" : "0";
298
367
  if (field.items && field.items.length > 0) {
299
368
  xmlStream.openNode("pivotField", attrs);
300
369
  xmlStream.openNode("items", { count: field.items.length + 1 });
@@ -302,7 +371,7 @@ class PivotTableXform extends base_xform_1.BaseXform {
302
371
  xmlStream.leafNode("item", { x: itemIndex });
303
372
  }
304
373
  // Grand total item
305
- xmlStream.writeXml('<item t="default" />');
374
+ xmlStream.writeXml('<item t="default"/>');
306
375
  xmlStream.closeNode(); // items
307
376
  xmlStream.closeNode(); // pivotField
308
377
  }
@@ -340,6 +409,12 @@ class PivotTableXform extends base_xform_1.BaseXform {
340
409
  compact: attributes.compact === "1",
341
410
  compactData: attributes.compactData === "1",
342
411
  multipleFieldFilters: attributes.multipleFieldFilters === "1",
412
+ outline: attributes.outline === "1",
413
+ outlineData: attributes.outlineData === "1",
414
+ chartFormat: attributes.chartFormat ? parseInt(attributes.chartFormat, 10) : undefined,
415
+ rowItems: [],
416
+ colItems: [],
417
+ chartFormats: [],
343
418
  isLoaded: true
344
419
  };
345
420
  break;
@@ -360,10 +435,10 @@ class PivotTableXform extends base_xform_1.BaseXform {
360
435
  }
361
436
  break;
362
437
  case "pivotFields":
363
- this.inPivotFields = true;
438
+ this.state.inPivotFields = true;
364
439
  break;
365
440
  case "pivotField":
366
- if (this.inPivotFields) {
441
+ if (this.state.inPivotFields) {
367
442
  this.currentPivotField = {
368
443
  axis: attributes.axis,
369
444
  dataField: attributes.dataField === "1",
@@ -377,43 +452,105 @@ class PivotTableXform extends base_xform_1.BaseXform {
377
452
  break;
378
453
  case "items":
379
454
  if (this.currentPivotField) {
380
- this.inItems = true;
455
+ this.state.inItems = true;
381
456
  }
382
457
  break;
383
458
  case "item":
384
- if (this.inItems && this.currentPivotField && attributes.x !== undefined) {
459
+ if (this.state.inItems && this.currentPivotField && attributes.x !== undefined) {
385
460
  this.currentPivotField.items.push(parseInt(attributes.x, 10));
386
461
  }
387
462
  break;
388
463
  case "rowFields":
389
- this.inRowFields = true;
464
+ this.state.inRowFields = true;
390
465
  break;
391
466
  case "colFields":
392
- this.inColFields = true;
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
+ }
393
472
  break;
394
473
  case "dataFields":
395
- this.inDataFields = true;
474
+ this.state.inDataFields = true;
396
475
  break;
397
476
  case "rowItems":
398
- this.inRowItems = true;
477
+ this.state.inRowItems = true;
399
478
  break;
400
479
  case "colItems":
401
- this.inColItems = true;
480
+ this.state.inColItems = true;
481
+ break;
482
+ case "i":
483
+ // Handle row/col item element
484
+ if (this.state.inRowItems && this.model) {
485
+ this.currentRowItem = { t: attributes.t, x: [] };
486
+ }
487
+ else if (this.state.inColItems && this.model) {
488
+ this.currentColItem = { t: attributes.t, x: [] };
489
+ }
490
+ break;
491
+ case "x":
492
+ // Handle x element inside row/col items or pivotArea
493
+ if (this.state.inPivotArea) {
494
+ // Collect x element for pivotArea XML
495
+ const xAttrs = Object.entries(attributes)
496
+ .map(([k, v]) => `${k}="${v}"`)
497
+ .join(" ");
498
+ this.pivotAreaXmlBuffer.push(xAttrs ? `<x ${xAttrs}/>` : "<x/>");
499
+ }
500
+ else if (this.currentRowItem) {
501
+ this.currentRowItem.x.push({ v: attributes.v ? parseInt(attributes.v, 10) : 0 });
502
+ }
503
+ else if (this.currentColItem) {
504
+ this.currentColItem.x.push({ v: attributes.v ? parseInt(attributes.v, 10) : 0 });
505
+ }
506
+ break;
507
+ case "chartFormats":
508
+ this.state.inChartFormats = true;
509
+ break;
510
+ case "chartFormat":
511
+ if (this.state.inChartFormats && this.model) {
512
+ this.currentChartFormat = {
513
+ chart: attributes.chart ? parseInt(attributes.chart, 10) : 0,
514
+ format: attributes.format ? parseInt(attributes.format, 10) : 0,
515
+ series: attributes.series === "1"
516
+ };
517
+ }
518
+ break;
519
+ case "pivotArea":
520
+ // Start collecting pivotArea XML for chartFormat
521
+ if (this.currentChartFormat) {
522
+ this.state.inPivotArea = true;
523
+ const attrsStr = Object.entries(attributes)
524
+ .map(([k, v]) => `${k}="${v}"`)
525
+ .join(" ");
526
+ this.pivotAreaXmlBuffer = [attrsStr ? `<pivotArea ${attrsStr}>` : "<pivotArea>"];
527
+ }
528
+ break;
529
+ case "references":
530
+ case "reference":
531
+ // Collect nested elements in pivotArea
532
+ if (this.state.inPivotArea) {
533
+ this.pivotAreaDepth++;
534
+ const attrsStr = Object.entries(attributes)
535
+ .map(([k, v]) => `${k}="${v}"`)
536
+ .join(" ");
537
+ this.pivotAreaXmlBuffer.push(`<${name}${attrsStr ? " " + attrsStr : ""}>`);
538
+ }
402
539
  break;
403
540
  case "field":
404
541
  // Handle field element (used in rowFields, colFields)
405
542
  if (this.model) {
406
543
  const fieldIndex = parseInt(attributes.x || "0", 10);
407
- if (this.inRowFields) {
544
+ if (this.state.inRowFields) {
408
545
  this.model.rowFields.push(fieldIndex);
409
546
  }
410
- else if (this.inColFields) {
547
+ else if (this.state.inColFields) {
411
548
  this.model.colFields.push(fieldIndex);
412
549
  }
413
550
  }
414
551
  break;
415
552
  case "dataField":
416
- if (this.inDataFields && this.model) {
553
+ if (this.state.inDataFields && this.model) {
417
554
  this.model.dataFields.push({
418
555
  name: (0, utils_1.xmlDecode)(attributes.name || ""),
419
556
  fld: parseInt(attributes.fld || "0", 10),
@@ -435,12 +572,32 @@ class PivotTableXform extends base_xform_1.BaseXform {
435
572
  // No text content in pivot table elements
436
573
  }
437
574
  parseClose(name) {
575
+ // Handle pivotArea nested elements - close tags
576
+ if (this.state.inPivotArea) {
577
+ if (name === "pivotArea") {
578
+ this.pivotAreaXmlBuffer.push("</pivotArea>");
579
+ if (this.currentChartFormat) {
580
+ this.currentChartFormat.pivotAreaXml = this.pivotAreaXmlBuffer.join("");
581
+ }
582
+ this.state.inPivotArea = false;
583
+ this.pivotAreaXmlBuffer = [];
584
+ this.pivotAreaDepth = 0;
585
+ return true;
586
+ }
587
+ else if (name === "references" || name === "reference") {
588
+ this.pivotAreaXmlBuffer.push(`</${name}>`);
589
+ this.pivotAreaDepth--;
590
+ return true;
591
+ }
592
+ // x elements are self-closing, no need to handle close
593
+ return true;
594
+ }
438
595
  switch (name) {
439
596
  case this.tag:
440
597
  // End of pivotTableDefinition
441
598
  return false;
442
599
  case "pivotFields":
443
- this.inPivotFields = false;
600
+ this.state.inPivotFields = false;
444
601
  break;
445
602
  case "pivotField":
446
603
  if (this.currentPivotField && this.model) {
@@ -449,22 +606,42 @@ class PivotTableXform extends base_xform_1.BaseXform {
449
606
  }
450
607
  break;
451
608
  case "items":
452
- this.inItems = false;
609
+ this.state.inItems = false;
453
610
  break;
454
611
  case "rowFields":
455
- this.inRowFields = false;
612
+ this.state.inRowFields = false;
456
613
  break;
457
614
  case "colFields":
458
- this.inColFields = false;
615
+ this.state.inColFields = false;
459
616
  break;
460
617
  case "dataFields":
461
- this.inDataFields = false;
618
+ this.state.inDataFields = false;
462
619
  break;
463
620
  case "rowItems":
464
- this.inRowItems = false;
621
+ this.state.inRowItems = false;
465
622
  break;
466
623
  case "colItems":
467
- this.inColItems = false;
624
+ this.state.inColItems = false;
625
+ break;
626
+ case "i":
627
+ // Finish row/col item
628
+ if (this.currentRowItem && this.model) {
629
+ this.model.rowItems.push(this.currentRowItem);
630
+ this.currentRowItem = null;
631
+ }
632
+ else if (this.currentColItem && this.model) {
633
+ this.model.colItems.push(this.currentColItem);
634
+ this.currentColItem = null;
635
+ }
636
+ break;
637
+ case "chartFormats":
638
+ this.state.inChartFormats = false;
639
+ break;
640
+ case "chartFormat":
641
+ if (this.currentChartFormat && this.model) {
642
+ this.model.chartFormats.push(this.currentChartFormat);
643
+ this.currentChartFormat = null;
644
+ }
468
645
  break;
469
646
  }
470
647
  return 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
- // Note: dyDescent is MS extension, not output by default (Excel auto-calculates)
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 || undefined
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
- // default value for 'defaultRowHeight' is 15, this should not be 'custom'
24
- if (!model.defaultRowHeight || model.defaultRowHeight !== 15) {
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: parseFloat(node.attributes["x14ac:dyDescent"] || "0"),
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,