@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
@@ -37,6 +37,7 @@ const stream_1 = require("../../archive/zip/stream.js");
37
37
  const zip_parser_1 = require("../../archive/unzip/zip-parser.js");
38
38
  const _stream_1 = require("../../stream/index.js");
39
39
  const ooxml_paths_1 = require("../utils/ooxml-paths.js");
40
+ const passthrough_manager_1 = require("../utils/passthrough-manager.js");
40
41
  class StreamingZipWriterAdapter {
41
42
  constructor(options) {
42
43
  this.events = new Map();
@@ -204,6 +205,7 @@ class XLSX {
204
205
  this.addDrawings(zip, model);
205
206
  this.addTables(zip, model);
206
207
  this.addPivotTables(zip, model);
208
+ this.addPassthrough(zip, model);
207
209
  await Promise.all([this.addThemes(zip, model), this.addStyles(zip, model)]);
208
210
  await this.addFeaturePropertyBag(zip, model);
209
211
  await this.addMedia(zip, model);
@@ -303,8 +305,12 @@ class XLSX {
303
305
  * This is the foundation for TRUE streaming reads on platforms that have a
304
306
  * streaming ZIP parser (e.g. Node.js `modules/archive` Parse).
305
307
  */
306
- async loadFromZipEntries(entries, options) {
307
- const model = {
308
+ /**
309
+ * Create an empty model for parsing XLSX files.
310
+ * Shared by loadFromZipEntries and loadFromFiles.
311
+ */
312
+ createEmptyModel() {
313
+ return {
308
314
  worksheets: [],
309
315
  worksheetHash: {},
310
316
  worksheetRels: [],
@@ -313,6 +319,8 @@ class XLSX {
313
319
  mediaIndex: {},
314
320
  drawings: {},
315
321
  drawingRels: {},
322
+ // Raw drawing XML data for passthrough (when drawing contains chart references)
323
+ rawDrawings: {},
316
324
  comments: {},
317
325
  tables: {},
318
326
  vmlDrawings: {},
@@ -320,8 +328,104 @@ class XLSX {
320
328
  pivotTableRels: {},
321
329
  pivotCacheDefinitions: {},
322
330
  pivotCacheDefinitionRels: {},
323
- pivotCacheRecords: {}
331
+ pivotCacheRecords: {},
332
+ // Passthrough storage for unknown/unsupported files (charts, etc.)
333
+ passthrough: {}
324
334
  };
335
+ }
336
+ /**
337
+ * Collect all data from a stream into a single Uint8Array.
338
+ * Reusable helper for passthrough and drawing processing.
339
+ */
340
+ async collectStreamData(stream) {
341
+ const chunks = [];
342
+ await new Promise((resolve, reject) => {
343
+ stream.on("data", (chunk) => {
344
+ if (typeof chunk === "string") {
345
+ chunks.push(new TextEncoder().encode(chunk));
346
+ }
347
+ else if (chunk instanceof Uint8Array) {
348
+ chunks.push(chunk);
349
+ }
350
+ else {
351
+ chunks.push(new Uint8Array(chunk));
352
+ }
353
+ });
354
+ stream.on("end", () => resolve());
355
+ stream.on("error", reject);
356
+ });
357
+ return (0, _stream_1.concatUint8Arrays)(chunks);
358
+ }
359
+ /**
360
+ * Check if a drawing has chart references in its relationships
361
+ */
362
+ drawingHasChartReference(drawing) {
363
+ return (drawing.rels && drawing.rels.some((rel) => rel.Target && rel.Target.includes("/charts/")));
364
+ }
365
+ /**
366
+ * Check if a drawing rels list references charts.
367
+ * Used to decide whether we need to keep raw drawing XML for passthrough.
368
+ */
369
+ drawingRelsHasChartReference(drawingRels) {
370
+ return (Array.isArray(drawingRels) &&
371
+ drawingRels.some(rel => typeof rel?.Target === "string" && rel.Target.includes("/charts/")));
372
+ }
373
+ /**
374
+ * Process a known OOXML entry (workbook, styles, shared strings, etc.)
375
+ * Returns true if handled, false if should be passed to _processDefaultEntry
376
+ */
377
+ async _processKnownEntry(stream, model, entryName, options) {
378
+ const sheetNo = (0, ooxml_paths_1.getWorksheetNoFromWorksheetPath)(entryName);
379
+ if (sheetNo !== undefined) {
380
+ await this._processWorksheetEntry(stream, model, sheetNo, options, entryName);
381
+ return true;
382
+ }
383
+ switch (entryName) {
384
+ case ooxml_paths_1.OOXML_PATHS.rootRels:
385
+ model.globalRels = await this.parseRels(stream);
386
+ return true;
387
+ case ooxml_paths_1.OOXML_PATHS.xlWorkbook: {
388
+ const workbook = await this.parseWorkbook(stream);
389
+ model.sheets = workbook.sheets;
390
+ model.definedNames = workbook.definedNames;
391
+ model.views = workbook.views;
392
+ model.properties = workbook.properties;
393
+ model.calcProperties = workbook.calcProperties;
394
+ model.pivotCaches = workbook.pivotCaches;
395
+ return true;
396
+ }
397
+ case ooxml_paths_1.OOXML_PATHS.xlSharedStrings:
398
+ model.sharedStrings = new shared_strings_xform_1.SharedStringsXform();
399
+ await model.sharedStrings.parseStream(stream);
400
+ return true;
401
+ case ooxml_paths_1.OOXML_PATHS.xlWorkbookRels:
402
+ model.workbookRels = await this.parseRels(stream);
403
+ return true;
404
+ case ooxml_paths_1.OOXML_PATHS.docPropsApp: {
405
+ const appXform = new app_xform_1.AppXform();
406
+ const appProperties = await appXform.parseStream(stream);
407
+ if (appProperties) {
408
+ model.company = appProperties.company;
409
+ model.manager = appProperties.manager;
410
+ }
411
+ return true;
412
+ }
413
+ case ooxml_paths_1.OOXML_PATHS.docPropsCore: {
414
+ const coreXform = new core_xform_1.CoreXform();
415
+ const coreProperties = await coreXform.parseStream(stream);
416
+ Object.assign(model, coreProperties);
417
+ return true;
418
+ }
419
+ case ooxml_paths_1.OOXML_PATHS.xlStyles:
420
+ model.styles = new styles_xform_1.StylesXform();
421
+ await model.styles.parseStream(stream);
422
+ return true;
423
+ default:
424
+ return false;
425
+ }
426
+ }
427
+ async loadFromZipEntries(entries, options) {
428
+ const model = this.createEmptyModel();
325
429
  for await (const entry of entries) {
326
430
  let drained = false;
327
431
  const drainEntry = async () => {
@@ -338,58 +442,12 @@ class XLSX {
338
442
  const entryName = (0, ooxml_paths_1.normalizeZipPath)(entry.name);
339
443
  const stream = entry.stream;
340
444
  try {
341
- const sheetNo = (0, ooxml_paths_1.getWorksheetNoFromWorksheetPath)(entryName);
342
- if (sheetNo !== undefined) {
343
- await this._processWorksheetEntry(stream, model, sheetNo, options, entryName);
344
- continue;
345
- }
346
- switch (entryName) {
347
- case ooxml_paths_1.OOXML_PATHS.rootRels:
348
- model.globalRels = await this.parseRels(stream);
349
- break;
350
- case ooxml_paths_1.OOXML_PATHS.xlWorkbook: {
351
- const workbook = await this.parseWorkbook(stream);
352
- model.sheets = workbook.sheets;
353
- model.definedNames = workbook.definedNames;
354
- model.views = workbook.views;
355
- model.properties = workbook.properties;
356
- model.calcProperties = workbook.calcProperties;
357
- model.pivotCaches = workbook.pivotCaches;
358
- break;
359
- }
360
- case ooxml_paths_1.OOXML_PATHS.xlSharedStrings:
361
- model.sharedStrings = new shared_strings_xform_1.SharedStringsXform();
362
- await model.sharedStrings.parseStream(stream);
363
- break;
364
- case ooxml_paths_1.OOXML_PATHS.xlWorkbookRels:
365
- model.workbookRels = await this.parseRels(stream);
366
- break;
367
- case ooxml_paths_1.OOXML_PATHS.docPropsApp: {
368
- const appXform = new app_xform_1.AppXform();
369
- const appProperties = await appXform.parseStream(stream);
370
- if (appProperties) {
371
- model.company = appProperties.company;
372
- model.manager = appProperties.manager;
373
- }
374
- break;
375
- }
376
- case ooxml_paths_1.OOXML_PATHS.docPropsCore: {
377
- const coreXform = new core_xform_1.CoreXform();
378
- const coreProperties = await coreXform.parseStream(stream);
379
- Object.assign(model, coreProperties);
380
- break;
381
- }
382
- case ooxml_paths_1.OOXML_PATHS.xlStyles:
383
- model.styles = new styles_xform_1.StylesXform();
384
- await model.styles.parseStream(stream);
385
- break;
386
- default: {
387
- const handled = await this._processDefaultEntry(stream, model, entryName);
388
- if (!handled) {
389
- // Important for true streaming parsers: always consume unknown entries
390
- await drainEntry();
391
- }
392
- break;
445
+ const handled = await this._processKnownEntry(stream, model, entryName, options);
446
+ if (!handled) {
447
+ const defaultHandled = await this._processDefaultEntry(stream, model, entryName);
448
+ if (!defaultHandled) {
449
+ // Important for true streaming parsers: always consume unknown entries
450
+ await drainEntry();
393
451
  }
394
452
  }
395
453
  }
@@ -503,6 +561,15 @@ class XLSX {
503
561
  drawingXform.reconcile(drawing, drawingOptions);
504
562
  }
505
563
  });
564
+ // Trim raw drawings for non-chart drawings to avoid bloating the serialized workbook model.
565
+ if (model.rawDrawings && model.drawingRels) {
566
+ for (const name of Object.keys(model.rawDrawings)) {
567
+ const drawingRel = model.drawingRels[name];
568
+ if (drawingRel && !this.drawingRelsHasChartReference(drawingRel)) {
569
+ delete model.rawDrawings[name];
570
+ }
571
+ }
572
+ }
506
573
  // reconcile tables with the default styles
507
574
  const tableOptions = {
508
575
  styles: model.styles
@@ -519,6 +586,7 @@ class XLSX {
519
586
  mediaIndex: model.mediaIndex,
520
587
  date1904: model.properties && model.properties.date1904,
521
588
  drawings: model.drawings,
589
+ drawingRels: model.drawingRels,
522
590
  comments: model.comments,
523
591
  tables: model.tables,
524
592
  vmlDrawings: model.vmlDrawings,
@@ -535,6 +603,8 @@ class XLSX {
535
603
  delete model.sharedStrings;
536
604
  delete model.workbookRels;
537
605
  delete model.sheetDefs;
606
+ // Preserve default font before deleting styles
607
+ model.defaultFont = model.styles?.defaultFont;
538
608
  delete model.styles;
539
609
  delete model.mediaIndex;
540
610
  delete model.drawings;
@@ -695,9 +765,15 @@ class XLSX {
695
765
  }
696
766
  }
697
767
  async _processDrawingEntry(entry, model, name) {
768
+ // Collect raw data first so we can preserve drawings that reference charts.
769
+ const rawData = await this.collectStreamData(entry);
770
+ // Parse the drawing for normal processing (images, etc.)
698
771
  const xform = new drawing_xform_1.DrawingXform();
699
- const drawing = await xform.parseStream(entry);
772
+ const xmlString = this.bufferToString(rawData);
773
+ const drawing = await xform.parseStream(this.createTextStream(xmlString));
700
774
  model.drawings[name] = drawing;
775
+ // Store raw data; reconcile() may later drop it if charts are not referenced.
776
+ model.rawDrawings[name] = rawData;
701
777
  }
702
778
  async _processDrawingRelsEntry(entry, model, name) {
703
779
  const xform = new relationships_xform_1.RelationshipsXform();
@@ -772,24 +848,7 @@ class XLSX {
772
848
  // loadFromFiles - shared logic for loading from pre-extracted ZIP data
773
849
  // ===========================================================================
774
850
  async loadFromFiles(zipData, options) {
775
- const model = {
776
- worksheets: [],
777
- worksheetHash: {},
778
- worksheetRels: [],
779
- themes: {},
780
- media: [],
781
- mediaIndex: {},
782
- drawings: {},
783
- drawingRels: {},
784
- comments: {},
785
- tables: {},
786
- vmlDrawings: {},
787
- pivotTables: {},
788
- pivotTableRels: {},
789
- pivotCacheDefinitions: {},
790
- pivotCacheDefinitionRels: {},
791
- pivotCacheRecords: {}
792
- };
851
+ const model = this.createEmptyModel();
793
852
  const entries = Object.keys(zipData).map(name => ({
794
853
  name,
795
854
  dir: name.endsWith("/"),
@@ -803,54 +862,10 @@ class XLSX {
803
862
  const stream = isBinaryEntry
804
863
  ? this.createBinaryStream(entry.data)
805
864
  : this.createTextStream(this.bufferToString(entry.data));
806
- const sheetNo = (0, ooxml_paths_1.getWorksheetNoFromWorksheetPath)(entryName);
807
- if (sheetNo !== undefined) {
808
- await this._processWorksheetEntry(stream, model, sheetNo, options, entryName);
809
- }
810
- else {
811
- switch (entryName) {
812
- case ooxml_paths_1.OOXML_PATHS.rootRels:
813
- model.globalRels = await this.parseRels(stream);
814
- break;
815
- case ooxml_paths_1.OOXML_PATHS.xlWorkbook: {
816
- const workbook = await this.parseWorkbook(stream);
817
- model.sheets = workbook.sheets;
818
- model.definedNames = workbook.definedNames;
819
- model.views = workbook.views;
820
- model.properties = workbook.properties;
821
- model.calcProperties = workbook.calcProperties;
822
- model.pivotCaches = workbook.pivotCaches;
823
- break;
824
- }
825
- case ooxml_paths_1.OOXML_PATHS.xlSharedStrings:
826
- model.sharedStrings = new shared_strings_xform_1.SharedStringsXform();
827
- await model.sharedStrings.parseStream(stream);
828
- break;
829
- case ooxml_paths_1.OOXML_PATHS.xlWorkbookRels:
830
- model.workbookRels = await this.parseRels(stream);
831
- break;
832
- case ooxml_paths_1.OOXML_PATHS.docPropsApp: {
833
- const appXform = new app_xform_1.AppXform();
834
- const appProperties = await appXform.parseStream(stream);
835
- if (appProperties) {
836
- model.company = appProperties.company;
837
- model.manager = appProperties.manager;
838
- }
839
- break;
840
- }
841
- case ooxml_paths_1.OOXML_PATHS.docPropsCore: {
842
- const coreXform = new core_xform_1.CoreXform();
843
- const coreProperties = await coreXform.parseStream(stream);
844
- Object.assign(model, coreProperties);
845
- break;
846
- }
847
- case ooxml_paths_1.OOXML_PATHS.xlStyles:
848
- model.styles = new styles_xform_1.StylesXform();
849
- await model.styles.parseStream(stream);
850
- break;
851
- default:
852
- await this._processDefaultEntry(stream, model, entryName);
853
- }
865
+ const handled = await this._processKnownEntry(stream, model, entryName, options);
866
+ if (!handled) {
867
+ // Pass raw entry data for drawings to enable passthrough
868
+ await this._processDefaultEntry(stream, model, entryName, entry.data);
854
869
  }
855
870
  }
856
871
  }
@@ -860,11 +875,11 @@ class XLSX {
860
875
  }
861
876
  /**
862
877
  * Process default entries (drawings, comments, tables, etc.)
878
+ * @param rawData Optional raw entry data for passthrough preservation (used by loadFromFiles)
863
879
  */
864
- async _processDefaultEntry(stream, model, entryName) {
865
- const worksheetRelsSheetNo = (0, ooxml_paths_1.getWorksheetNoFromWorksheetRelsPath)(entryName);
866
- if (worksheetRelsSheetNo !== undefined) {
867
- const sheetNo = worksheetRelsSheetNo;
880
+ async _processDefaultEntry(stream, model, entryName, rawData) {
881
+ const sheetNo = (0, ooxml_paths_1.getWorksheetNoFromWorksheetRelsPath)(entryName);
882
+ if (sheetNo !== undefined) {
868
883
  await this._processWorksheetRelsEntry(stream, model, sheetNo);
869
884
  return true;
870
885
  }
@@ -876,6 +891,10 @@ class XLSX {
876
891
  const drawingName = (0, ooxml_paths_1.getDrawingNameFromPath)(entryName);
877
892
  if (drawingName) {
878
893
  await this._processDrawingEntry(stream, model, drawingName);
894
+ // For loadFromFiles path, store raw data for passthrough (drawings with charts)
895
+ if (rawData) {
896
+ model.rawDrawings[drawingName] = rawData;
897
+ }
879
898
  return true;
880
899
  }
881
900
  const drawingRelsName = (0, ooxml_paths_1.getDrawingNameFromRelsPath)(entryName);
@@ -930,8 +949,27 @@ class XLSX {
930
949
  await this._processPivotCacheRecordsEntry(stream, model, pivotCacheRecordsName);
931
950
  return true;
932
951
  }
952
+ // Store passthrough files (charts, etc.) for preservation
953
+ if (passthrough_manager_1.PassthroughManager.isPassthroughPath(entryName)) {
954
+ // If raw data is available (loadFromFiles path), use it directly
955
+ if (rawData) {
956
+ model.passthrough[entryName] = rawData;
957
+ }
958
+ else {
959
+ await this._processPassthroughEntry(stream, model, entryName);
960
+ }
961
+ return true;
962
+ }
933
963
  return false;
934
964
  }
965
+ /**
966
+ * Store a passthrough file for preservation during read/write cycles.
967
+ * These files are not parsed but stored as raw bytes to be written back unchanged.
968
+ */
969
+ async _processPassthroughEntry(stream, model, entryName) {
970
+ const data = await this.collectStreamData(stream);
971
+ model.passthrough[entryName] = data;
972
+ }
935
973
  // ===========================================================================
936
974
  // Write methods - shared by all platforms
937
975
  // ===========================================================================
@@ -1074,14 +1112,46 @@ class XLSX {
1074
1112
  addDrawings(zip, model) {
1075
1113
  const drawingXform = new drawing_xform_1.DrawingXform();
1076
1114
  const relsXform = new relationships_xform_1.RelationshipsXform();
1115
+ const rawDrawings = model.rawDrawings || {};
1077
1116
  model.worksheets.forEach((worksheet) => {
1078
1117
  const { drawing } = worksheet;
1079
1118
  if (drawing) {
1080
- drawingXform.prepare(drawing);
1081
- let xml = drawingXform.toXml(drawing);
1082
- zip.append(xml, { name: (0, ooxml_paths_1.drawingPath)(drawing.name) });
1083
- xml = relsXform.toXml(drawing.rels);
1084
- zip.append(xml, { name: (0, ooxml_paths_1.drawingRelsPath)(drawing.name) });
1119
+ // Check if drawing rels contain chart references using helper
1120
+ const hasChartReference = this.drawingHasChartReference(drawing);
1121
+ if (hasChartReference && rawDrawings[drawing.name]) {
1122
+ // Use raw data for drawings with chart references (passthrough)
1123
+ zip.append(rawDrawings[drawing.name], { name: (0, ooxml_paths_1.drawingPath)(drawing.name) });
1124
+ }
1125
+ else {
1126
+ // Use regenerated XML for normal drawings (images, shapes)
1127
+ // Filter out invalid anchors (null, undefined, or missing content)
1128
+ const filteredAnchors = (drawing.anchors || []).filter((a) => {
1129
+ if (a == null) {
1130
+ return false;
1131
+ }
1132
+ // Form controls have range.br and shape properties
1133
+ if (a.range?.br && a.shape) {
1134
+ return true;
1135
+ }
1136
+ // One-cell anchors need a valid picture
1137
+ if (!a.br && !a.picture) {
1138
+ return false;
1139
+ }
1140
+ // Two-cell anchors need either picture or shape
1141
+ if (a.br && !a.picture && !a.shape) {
1142
+ return false;
1143
+ }
1144
+ return true;
1145
+ });
1146
+ const drawingForWrite = drawing.anchors
1147
+ ? { ...drawing, anchors: filteredAnchors }
1148
+ : drawing;
1149
+ drawingXform.prepare(drawingForWrite);
1150
+ const xml = drawingXform.toXml(drawingForWrite);
1151
+ zip.append(xml, { name: (0, ooxml_paths_1.drawingPath)(drawing.name) });
1152
+ }
1153
+ const relsXml = relsXform.toXml(drawing.rels);
1154
+ zip.append(relsXml, { name: (0, ooxml_paths_1.drawingRelsPath)(drawing.name) });
1085
1155
  }
1086
1156
  });
1087
1157
  }
@@ -1096,6 +1166,15 @@ class XLSX {
1096
1166
  });
1097
1167
  });
1098
1168
  }
1169
+ /**
1170
+ * Write passthrough files (charts, etc.) that were preserved during read.
1171
+ * These files are written back unchanged to preserve unsupported features.
1172
+ */
1173
+ addPassthrough(zip, model) {
1174
+ const passthroughManager = new passthrough_manager_1.PassthroughManager();
1175
+ passthroughManager.fromRecord(model.passthrough || {});
1176
+ passthroughManager.writeToZip(zip);
1177
+ }
1099
1178
  addPivotTables(zip, model) {
1100
1179
  if (!model.pivotTables.length) {
1101
1180
  return;
@@ -1161,7 +1240,12 @@ class XLSX {
1161
1240
  options.useSharedStrings !== undefined ? options.useSharedStrings : true;
1162
1241
  model.useStyles = options.useStyles !== undefined ? options.useStyles : true;
1163
1242
  model.sharedStrings = new shared_strings_xform_1.SharedStringsXform();
1243
+ // Preserve default font from parsed styles if available
1244
+ const oldDefaultFont = model.defaultFont;
1164
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
+ }
1165
1249
  const workbookXform = new workbook_xform_1.WorkbookXform();
1166
1250
  const worksheetXform = new worksheet_xform_1.WorkSheetXform();
1167
1251
  workbookXform.prepare(model);
@@ -1188,6 +1272,11 @@ class XLSX {
1188
1272
  });
1189
1273
  // ContentTypesXform expects this flag
1190
1274
  model.hasCheckboxes = model.styles.hasCheckboxes;
1275
+ // Build passthroughContentTypes for ContentTypesXform using PassthroughManager
1276
+ const passthrough = model.passthrough || {};
1277
+ const passthroughManager = new passthrough_manager_1.PassthroughManager();
1278
+ passthroughManager.fromRecord(passthrough);
1279
+ model.passthroughContentTypes = passthroughManager.getContentTypes();
1191
1280
  }
1192
1281
  }
1193
1282
  exports.XLSX = XLSX;
@@ -890,9 +890,6 @@ class Readable extends event_emitter_1.EventEmitter {
890
890
  yield chunk;
891
891
  }
892
892
  }
893
- if (streamError) {
894
- throw streamError;
895
- }
896
893
  }
897
894
  finally {
898
895
  this.off("data", dataHandler);
@@ -112,7 +112,7 @@ class CSV {
112
112
  const dateFormats = options?.dateFormats ?? DEFAULT_DATE_FORMATS;
113
113
  const map = options?.map ||
114
114
  createDefaultValueMapper(dateFormats, {
115
- decimalSeparator: options?.valueMapperOptions?.decimalSeparator ?? options?.parserOptions?.decimalSeparator
115
+ decimalSeparator: options?.valueMapperOptions?.decimalSeparator
116
116
  });
117
117
  const rows = parseCsv(str, options?.parserOptions);
118
118
  for (const row of rows) {
@@ -154,7 +154,7 @@ class CSV {
154
154
  const dateFormats = options?.dateFormats ?? DEFAULT_DATE_FORMATS;
155
155
  const map = options?.map ||
156
156
  createDefaultValueMapper(dateFormats, {
157
- decimalSeparator: options?.valueMapperOptions?.decimalSeparator ?? options?.parserOptions?.decimalSeparator
157
+ decimalSeparator: options?.valueMapperOptions?.decimalSeparator
158
158
  });
159
159
  const parser = new CsvParserStream(options?.parserOptions);
160
160
  return new Promise((resolve, reject) => {
@@ -224,7 +224,7 @@ class CSV {
224
224
  const dateFormats = options?.dateFormats ?? DEFAULT_DATE_FORMATS;
225
225
  const map = options?.map ||
226
226
  createDefaultValueMapper(dateFormats, {
227
- decimalSeparator: options?.valueMapperOptions?.decimalSeparator ?? options?.parserOptions?.decimalSeparator
227
+ decimalSeparator: options?.valueMapperOptions?.decimalSeparator
228
228
  });
229
229
  const parser = new CsvParserStream(options?.parserOptions);
230
230
  parser.on("data", (row) => worksheet.addRow(row.map(map)));
@@ -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
  }