@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.
- package/dist/browser/modules/csv/csv-core.d.ts +0 -9
- package/dist/browser/modules/csv/csv.browser.js +3 -3
- package/dist/browser/modules/excel/column.d.ts +5 -0
- package/dist/browser/modules/excel/column.js +10 -2
- package/dist/browser/modules/excel/row.d.ts +2 -0
- package/dist/browser/modules/excel/row.js +3 -1
- package/dist/browser/modules/excel/utils/parse-sax.d.ts +0 -3
- package/dist/browser/modules/excel/utils/parse-sax.js +13 -32
- package/dist/browser/modules/excel/utils/passthrough-manager.d.ts +77 -0
- package/dist/browser/modules/excel/utils/passthrough-manager.js +129 -0
- package/dist/browser/modules/excel/workbook.d.ts +12 -0
- package/dist/browser/modules/excel/workbook.js +12 -1
- package/dist/browser/modules/excel/worksheet.d.ts +4 -0
- package/dist/browser/modules/excel/worksheet.js +4 -1
- package/dist/browser/modules/excel/xlsx/xform/base-xform.js +68 -1
- package/dist/browser/modules/excel/xlsx/xform/core/content-types-xform.js +16 -10
- package/dist/browser/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.d.ts +35 -11
- package/dist/browser/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.js +271 -94
- package/dist/browser/modules/excel/xlsx/xform/sheet/row-xform.d.ts +1 -0
- package/dist/browser/modules/excel/xlsx/xform/sheet/row-xform.js +7 -1
- package/dist/browser/modules/excel/xlsx/xform/sheet/sheet-format-properties-xform.d.ts +1 -0
- package/dist/browser/modules/excel/xlsx/xform/sheet/sheet-format-properties-xform.js +9 -4
- package/dist/browser/modules/excel/xlsx/xform/sheet/sheet-view-xform.js +4 -2
- package/dist/browser/modules/excel/xlsx/xform/sheet/worksheet-xform.js +40 -12
- package/dist/browser/modules/excel/xlsx/xform/style/style-xform.d.ts +7 -0
- package/dist/browser/modules/excel/xlsx/xform/style/style-xform.js +26 -6
- package/dist/browser/modules/excel/xlsx/xform/style/styles-xform.d.ts +6 -0
- package/dist/browser/modules/excel/xlsx/xform/style/styles-xform.js +52 -4
- package/dist/browser/modules/excel/xlsx/xlsx.browser.d.ts +36 -1
- package/dist/browser/modules/excel/xlsx/xlsx.browser.js +220 -131
- package/dist/browser/modules/stream/streams.browser.js +0 -3
- package/dist/cjs/modules/csv/csv.browser.js +3 -3
- package/dist/cjs/modules/excel/column.js +10 -2
- package/dist/cjs/modules/excel/row.js +3 -1
- package/dist/cjs/modules/excel/utils/parse-sax.js +13 -32
- package/dist/cjs/modules/excel/utils/passthrough-manager.js +133 -0
- package/dist/cjs/modules/excel/workbook.js +12 -1
- package/dist/cjs/modules/excel/worksheet.js +4 -1
- package/dist/cjs/modules/excel/xlsx/xform/base-xform.js +68 -1
- package/dist/cjs/modules/excel/xlsx/xform/core/content-types-xform.js +16 -10
- package/dist/cjs/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.js +271 -94
- package/dist/cjs/modules/excel/xlsx/xform/sheet/row-xform.js +7 -1
- package/dist/cjs/modules/excel/xlsx/xform/sheet/sheet-format-properties-xform.js +9 -4
- package/dist/cjs/modules/excel/xlsx/xform/sheet/sheet-view-xform.js +4 -2
- package/dist/cjs/modules/excel/xlsx/xform/sheet/worksheet-xform.js +40 -12
- package/dist/cjs/modules/excel/xlsx/xform/style/style-xform.js +26 -6
- package/dist/cjs/modules/excel/xlsx/xform/style/styles-xform.js +52 -4
- package/dist/cjs/modules/excel/xlsx/xlsx.browser.js +220 -131
- package/dist/cjs/modules/stream/streams.browser.js +0 -3
- package/dist/esm/modules/csv/csv.browser.js +3 -3
- package/dist/esm/modules/excel/column.js +10 -2
- package/dist/esm/modules/excel/row.js +3 -1
- package/dist/esm/modules/excel/utils/parse-sax.js +13 -32
- package/dist/esm/modules/excel/utils/passthrough-manager.js +129 -0
- package/dist/esm/modules/excel/workbook.js +12 -1
- package/dist/esm/modules/excel/worksheet.js +4 -1
- package/dist/esm/modules/excel/xlsx/xform/base-xform.js +68 -1
- package/dist/esm/modules/excel/xlsx/xform/core/content-types-xform.js +16 -10
- package/dist/esm/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.js +271 -94
- package/dist/esm/modules/excel/xlsx/xform/sheet/row-xform.js +7 -1
- package/dist/esm/modules/excel/xlsx/xform/sheet/sheet-format-properties-xform.js +9 -4
- package/dist/esm/modules/excel/xlsx/xform/sheet/sheet-view-xform.js +4 -2
- package/dist/esm/modules/excel/xlsx/xform/sheet/worksheet-xform.js +40 -12
- package/dist/esm/modules/excel/xlsx/xform/style/style-xform.js +26 -6
- package/dist/esm/modules/excel/xlsx/xform/style/styles-xform.js +52 -4
- package/dist/esm/modules/excel/xlsx/xlsx.browser.js +220 -131
- package/dist/esm/modules/stream/streams.browser.js +0 -3
- package/dist/iife/excelts.iife.js +1009 -650
- package/dist/iife/excelts.iife.js.map +1 -1
- package/dist/iife/excelts.iife.min.js +25 -52
- package/dist/types/modules/csv/csv-core.d.ts +0 -9
- package/dist/types/modules/excel/column.d.ts +5 -0
- package/dist/types/modules/excel/row.d.ts +2 -0
- package/dist/types/modules/excel/utils/parse-sax.d.ts +0 -3
- package/dist/types/modules/excel/utils/passthrough-manager.d.ts +77 -0
- package/dist/types/modules/excel/workbook.d.ts +12 -0
- package/dist/types/modules/excel/worksheet.d.ts +4 -0
- package/dist/types/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.d.ts +35 -11
- package/dist/types/modules/excel/xlsx/xform/sheet/row-xform.d.ts +1 -0
- package/dist/types/modules/excel/xlsx/xform/sheet/sheet-format-properties-xform.d.ts +1 -0
- package/dist/types/modules/excel/xlsx/xform/style/style-xform.d.ts +7 -0
- package/dist/types/modules/excel/xlsx/xform/style/styles-xform.d.ts +6 -0
- package/dist/types/modules/excel/xlsx/xlsx.browser.d.ts +36 -1
- 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
|
-
|
|
307
|
-
|
|
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
|
|
342
|
-
if (
|
|
343
|
-
await this.
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
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
|
|
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
|
|
807
|
-
if (
|
|
808
|
-
|
|
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
|
|
866
|
-
if (
|
|
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
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
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;
|
|
@@ -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
|
|
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
|
|
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
|
|
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
|
}
|