@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
|
@@ -34,6 +34,7 @@ import { StreamingZip, ZipDeflateFile } from "../../archive/zip/stream.js";
|
|
|
34
34
|
import { ZipParser } from "../../archive/unzip/zip-parser.js";
|
|
35
35
|
import { PassThrough, concatUint8Arrays } from "../../stream/index.browser.js";
|
|
36
36
|
import { commentsPath, commentsRelTargetFromWorksheetName, ctrlPropPath, drawingPath, drawingRelsPath, OOXML_REL_TARGETS, pivotTableRelTargetFromWorksheetName, pivotCacheDefinitionRelTargetFromWorkbook, getCommentsIndexFromPath, getDrawingNameFromPath, getDrawingNameFromRelsPath, getMediaFilenameFromPath, mediaPath, getPivotCacheDefinitionNameFromPath, getPivotCacheDefinitionNameFromRelsPath, getPivotCacheRecordsNameFromPath, getPivotTableNameFromPath, getPivotTableNameFromRelsPath, pivotCacheDefinitionPath, pivotCacheDefinitionRelsPath, pivotCacheDefinitionRelTargetFromPivotTable, pivotCacheRecordsPath, pivotCacheRecordsRelTarget, pivotTablePath, pivotTableRelsPath, getTableNameFromPath, tablePath, tableRelTargetFromWorksheetName, themePath, getThemeNameFromPath, getVmlDrawingNameFromPath, getWorksheetNoFromWorksheetPath, getWorksheetNoFromWorksheetRelsPath, isBinaryEntryPath, normalizeZipPath, OOXML_PATHS, vmlDrawingRelTargetFromWorksheetName, vmlDrawingPath, worksheetPath, worksheetRelsPath } from "../utils/ooxml-paths.js";
|
|
37
|
+
import { PassthroughManager } from "../utils/passthrough-manager.js";
|
|
37
38
|
class StreamingZipWriterAdapter {
|
|
38
39
|
constructor(options) {
|
|
39
40
|
this.events = new Map();
|
|
@@ -201,6 +202,7 @@ class XLSX {
|
|
|
201
202
|
this.addDrawings(zip, model);
|
|
202
203
|
this.addTables(zip, model);
|
|
203
204
|
this.addPivotTables(zip, model);
|
|
205
|
+
this.addPassthrough(zip, model);
|
|
204
206
|
await Promise.all([this.addThemes(zip, model), this.addStyles(zip, model)]);
|
|
205
207
|
await this.addFeaturePropertyBag(zip, model);
|
|
206
208
|
await this.addMedia(zip, model);
|
|
@@ -300,8 +302,12 @@ class XLSX {
|
|
|
300
302
|
* This is the foundation for TRUE streaming reads on platforms that have a
|
|
301
303
|
* streaming ZIP parser (e.g. Node.js `modules/archive` Parse).
|
|
302
304
|
*/
|
|
303
|
-
|
|
304
|
-
|
|
305
|
+
/**
|
|
306
|
+
* Create an empty model for parsing XLSX files.
|
|
307
|
+
* Shared by loadFromZipEntries and loadFromFiles.
|
|
308
|
+
*/
|
|
309
|
+
createEmptyModel() {
|
|
310
|
+
return {
|
|
305
311
|
worksheets: [],
|
|
306
312
|
worksheetHash: {},
|
|
307
313
|
worksheetRels: [],
|
|
@@ -310,6 +316,8 @@ class XLSX {
|
|
|
310
316
|
mediaIndex: {},
|
|
311
317
|
drawings: {},
|
|
312
318
|
drawingRels: {},
|
|
319
|
+
// Raw drawing XML data for passthrough (when drawing contains chart references)
|
|
320
|
+
rawDrawings: {},
|
|
313
321
|
comments: {},
|
|
314
322
|
tables: {},
|
|
315
323
|
vmlDrawings: {},
|
|
@@ -317,8 +325,104 @@ class XLSX {
|
|
|
317
325
|
pivotTableRels: {},
|
|
318
326
|
pivotCacheDefinitions: {},
|
|
319
327
|
pivotCacheDefinitionRels: {},
|
|
320
|
-
pivotCacheRecords: {}
|
|
328
|
+
pivotCacheRecords: {},
|
|
329
|
+
// Passthrough storage for unknown/unsupported files (charts, etc.)
|
|
330
|
+
passthrough: {}
|
|
321
331
|
};
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Collect all data from a stream into a single Uint8Array.
|
|
335
|
+
* Reusable helper for passthrough and drawing processing.
|
|
336
|
+
*/
|
|
337
|
+
async collectStreamData(stream) {
|
|
338
|
+
const chunks = [];
|
|
339
|
+
await new Promise((resolve, reject) => {
|
|
340
|
+
stream.on("data", (chunk) => {
|
|
341
|
+
if (typeof chunk === "string") {
|
|
342
|
+
chunks.push(new TextEncoder().encode(chunk));
|
|
343
|
+
}
|
|
344
|
+
else if (chunk instanceof Uint8Array) {
|
|
345
|
+
chunks.push(chunk);
|
|
346
|
+
}
|
|
347
|
+
else {
|
|
348
|
+
chunks.push(new Uint8Array(chunk));
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
stream.on("end", () => resolve());
|
|
352
|
+
stream.on("error", reject);
|
|
353
|
+
});
|
|
354
|
+
return concatUint8Arrays(chunks);
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Check if a drawing has chart references in its relationships
|
|
358
|
+
*/
|
|
359
|
+
drawingHasChartReference(drawing) {
|
|
360
|
+
return (drawing.rels && drawing.rels.some((rel) => rel.Target && rel.Target.includes("/charts/")));
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Check if a drawing rels list references charts.
|
|
364
|
+
* Used to decide whether we need to keep raw drawing XML for passthrough.
|
|
365
|
+
*/
|
|
366
|
+
drawingRelsHasChartReference(drawingRels) {
|
|
367
|
+
return (Array.isArray(drawingRels) &&
|
|
368
|
+
drawingRels.some(rel => typeof rel?.Target === "string" && rel.Target.includes("/charts/")));
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Process a known OOXML entry (workbook, styles, shared strings, etc.)
|
|
372
|
+
* Returns true if handled, false if should be passed to _processDefaultEntry
|
|
373
|
+
*/
|
|
374
|
+
async _processKnownEntry(stream, model, entryName, options) {
|
|
375
|
+
const sheetNo = getWorksheetNoFromWorksheetPath(entryName);
|
|
376
|
+
if (sheetNo !== undefined) {
|
|
377
|
+
await this._processWorksheetEntry(stream, model, sheetNo, options, entryName);
|
|
378
|
+
return true;
|
|
379
|
+
}
|
|
380
|
+
switch (entryName) {
|
|
381
|
+
case OOXML_PATHS.rootRels:
|
|
382
|
+
model.globalRels = await this.parseRels(stream);
|
|
383
|
+
return true;
|
|
384
|
+
case OOXML_PATHS.xlWorkbook: {
|
|
385
|
+
const workbook = await this.parseWorkbook(stream);
|
|
386
|
+
model.sheets = workbook.sheets;
|
|
387
|
+
model.definedNames = workbook.definedNames;
|
|
388
|
+
model.views = workbook.views;
|
|
389
|
+
model.properties = workbook.properties;
|
|
390
|
+
model.calcProperties = workbook.calcProperties;
|
|
391
|
+
model.pivotCaches = workbook.pivotCaches;
|
|
392
|
+
return true;
|
|
393
|
+
}
|
|
394
|
+
case OOXML_PATHS.xlSharedStrings:
|
|
395
|
+
model.sharedStrings = new SharedStringsXform();
|
|
396
|
+
await model.sharedStrings.parseStream(stream);
|
|
397
|
+
return true;
|
|
398
|
+
case OOXML_PATHS.xlWorkbookRels:
|
|
399
|
+
model.workbookRels = await this.parseRels(stream);
|
|
400
|
+
return true;
|
|
401
|
+
case OOXML_PATHS.docPropsApp: {
|
|
402
|
+
const appXform = new AppXform();
|
|
403
|
+
const appProperties = await appXform.parseStream(stream);
|
|
404
|
+
if (appProperties) {
|
|
405
|
+
model.company = appProperties.company;
|
|
406
|
+
model.manager = appProperties.manager;
|
|
407
|
+
}
|
|
408
|
+
return true;
|
|
409
|
+
}
|
|
410
|
+
case OOXML_PATHS.docPropsCore: {
|
|
411
|
+
const coreXform = new CoreXform();
|
|
412
|
+
const coreProperties = await coreXform.parseStream(stream);
|
|
413
|
+
Object.assign(model, coreProperties);
|
|
414
|
+
return true;
|
|
415
|
+
}
|
|
416
|
+
case OOXML_PATHS.xlStyles:
|
|
417
|
+
model.styles = new StylesXform();
|
|
418
|
+
await model.styles.parseStream(stream);
|
|
419
|
+
return true;
|
|
420
|
+
default:
|
|
421
|
+
return false;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
async loadFromZipEntries(entries, options) {
|
|
425
|
+
const model = this.createEmptyModel();
|
|
322
426
|
for await (const entry of entries) {
|
|
323
427
|
let drained = false;
|
|
324
428
|
const drainEntry = async () => {
|
|
@@ -335,58 +439,12 @@ class XLSX {
|
|
|
335
439
|
const entryName = normalizeZipPath(entry.name);
|
|
336
440
|
const stream = entry.stream;
|
|
337
441
|
try {
|
|
338
|
-
const
|
|
339
|
-
if (
|
|
340
|
-
await this.
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
case OOXML_PATHS.rootRels:
|
|
345
|
-
model.globalRels = await this.parseRels(stream);
|
|
346
|
-
break;
|
|
347
|
-
case OOXML_PATHS.xlWorkbook: {
|
|
348
|
-
const workbook = await this.parseWorkbook(stream);
|
|
349
|
-
model.sheets = workbook.sheets;
|
|
350
|
-
model.definedNames = workbook.definedNames;
|
|
351
|
-
model.views = workbook.views;
|
|
352
|
-
model.properties = workbook.properties;
|
|
353
|
-
model.calcProperties = workbook.calcProperties;
|
|
354
|
-
model.pivotCaches = workbook.pivotCaches;
|
|
355
|
-
break;
|
|
356
|
-
}
|
|
357
|
-
case OOXML_PATHS.xlSharedStrings:
|
|
358
|
-
model.sharedStrings = new SharedStringsXform();
|
|
359
|
-
await model.sharedStrings.parseStream(stream);
|
|
360
|
-
break;
|
|
361
|
-
case OOXML_PATHS.xlWorkbookRels:
|
|
362
|
-
model.workbookRels = await this.parseRels(stream);
|
|
363
|
-
break;
|
|
364
|
-
case OOXML_PATHS.docPropsApp: {
|
|
365
|
-
const appXform = new AppXform();
|
|
366
|
-
const appProperties = await appXform.parseStream(stream);
|
|
367
|
-
if (appProperties) {
|
|
368
|
-
model.company = appProperties.company;
|
|
369
|
-
model.manager = appProperties.manager;
|
|
370
|
-
}
|
|
371
|
-
break;
|
|
372
|
-
}
|
|
373
|
-
case OOXML_PATHS.docPropsCore: {
|
|
374
|
-
const coreXform = new CoreXform();
|
|
375
|
-
const coreProperties = await coreXform.parseStream(stream);
|
|
376
|
-
Object.assign(model, coreProperties);
|
|
377
|
-
break;
|
|
378
|
-
}
|
|
379
|
-
case OOXML_PATHS.xlStyles:
|
|
380
|
-
model.styles = new StylesXform();
|
|
381
|
-
await model.styles.parseStream(stream);
|
|
382
|
-
break;
|
|
383
|
-
default: {
|
|
384
|
-
const handled = await this._processDefaultEntry(stream, model, entryName);
|
|
385
|
-
if (!handled) {
|
|
386
|
-
// Important for true streaming parsers: always consume unknown entries
|
|
387
|
-
await drainEntry();
|
|
388
|
-
}
|
|
389
|
-
break;
|
|
442
|
+
const handled = await this._processKnownEntry(stream, model, entryName, options);
|
|
443
|
+
if (!handled) {
|
|
444
|
+
const defaultHandled = await this._processDefaultEntry(stream, model, entryName);
|
|
445
|
+
if (!defaultHandled) {
|
|
446
|
+
// Important for true streaming parsers: always consume unknown entries
|
|
447
|
+
await drainEntry();
|
|
390
448
|
}
|
|
391
449
|
}
|
|
392
450
|
}
|
|
@@ -500,6 +558,15 @@ class XLSX {
|
|
|
500
558
|
drawingXform.reconcile(drawing, drawingOptions);
|
|
501
559
|
}
|
|
502
560
|
});
|
|
561
|
+
// Trim raw drawings for non-chart drawings to avoid bloating the serialized workbook model.
|
|
562
|
+
if (model.rawDrawings && model.drawingRels) {
|
|
563
|
+
for (const name of Object.keys(model.rawDrawings)) {
|
|
564
|
+
const drawingRel = model.drawingRels[name];
|
|
565
|
+
if (drawingRel && !this.drawingRelsHasChartReference(drawingRel)) {
|
|
566
|
+
delete model.rawDrawings[name];
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
}
|
|
503
570
|
// reconcile tables with the default styles
|
|
504
571
|
const tableOptions = {
|
|
505
572
|
styles: model.styles
|
|
@@ -516,6 +583,7 @@ class XLSX {
|
|
|
516
583
|
mediaIndex: model.mediaIndex,
|
|
517
584
|
date1904: model.properties && model.properties.date1904,
|
|
518
585
|
drawings: model.drawings,
|
|
586
|
+
drawingRels: model.drawingRels,
|
|
519
587
|
comments: model.comments,
|
|
520
588
|
tables: model.tables,
|
|
521
589
|
vmlDrawings: model.vmlDrawings,
|
|
@@ -532,6 +600,8 @@ class XLSX {
|
|
|
532
600
|
delete model.sharedStrings;
|
|
533
601
|
delete model.workbookRels;
|
|
534
602
|
delete model.sheetDefs;
|
|
603
|
+
// Preserve default font before deleting styles
|
|
604
|
+
model.defaultFont = model.styles?.defaultFont;
|
|
535
605
|
delete model.styles;
|
|
536
606
|
delete model.mediaIndex;
|
|
537
607
|
delete model.drawings;
|
|
@@ -692,9 +762,15 @@ class XLSX {
|
|
|
692
762
|
}
|
|
693
763
|
}
|
|
694
764
|
async _processDrawingEntry(entry, model, name) {
|
|
765
|
+
// Collect raw data first so we can preserve drawings that reference charts.
|
|
766
|
+
const rawData = await this.collectStreamData(entry);
|
|
767
|
+
// Parse the drawing for normal processing (images, etc.)
|
|
695
768
|
const xform = new DrawingXform();
|
|
696
|
-
const
|
|
769
|
+
const xmlString = this.bufferToString(rawData);
|
|
770
|
+
const drawing = await xform.parseStream(this.createTextStream(xmlString));
|
|
697
771
|
model.drawings[name] = drawing;
|
|
772
|
+
// Store raw data; reconcile() may later drop it if charts are not referenced.
|
|
773
|
+
model.rawDrawings[name] = rawData;
|
|
698
774
|
}
|
|
699
775
|
async _processDrawingRelsEntry(entry, model, name) {
|
|
700
776
|
const xform = new RelationshipsXform();
|
|
@@ -769,24 +845,7 @@ class XLSX {
|
|
|
769
845
|
// loadFromFiles - shared logic for loading from pre-extracted ZIP data
|
|
770
846
|
// ===========================================================================
|
|
771
847
|
async loadFromFiles(zipData, options) {
|
|
772
|
-
const model =
|
|
773
|
-
worksheets: [],
|
|
774
|
-
worksheetHash: {},
|
|
775
|
-
worksheetRels: [],
|
|
776
|
-
themes: {},
|
|
777
|
-
media: [],
|
|
778
|
-
mediaIndex: {},
|
|
779
|
-
drawings: {},
|
|
780
|
-
drawingRels: {},
|
|
781
|
-
comments: {},
|
|
782
|
-
tables: {},
|
|
783
|
-
vmlDrawings: {},
|
|
784
|
-
pivotTables: {},
|
|
785
|
-
pivotTableRels: {},
|
|
786
|
-
pivotCacheDefinitions: {},
|
|
787
|
-
pivotCacheDefinitionRels: {},
|
|
788
|
-
pivotCacheRecords: {}
|
|
789
|
-
};
|
|
848
|
+
const model = this.createEmptyModel();
|
|
790
849
|
const entries = Object.keys(zipData).map(name => ({
|
|
791
850
|
name,
|
|
792
851
|
dir: name.endsWith("/"),
|
|
@@ -800,54 +859,10 @@ class XLSX {
|
|
|
800
859
|
const stream = isBinaryEntry
|
|
801
860
|
? this.createBinaryStream(entry.data)
|
|
802
861
|
: this.createTextStream(this.bufferToString(entry.data));
|
|
803
|
-
const
|
|
804
|
-
if (
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
else {
|
|
808
|
-
switch (entryName) {
|
|
809
|
-
case OOXML_PATHS.rootRels:
|
|
810
|
-
model.globalRels = await this.parseRels(stream);
|
|
811
|
-
break;
|
|
812
|
-
case OOXML_PATHS.xlWorkbook: {
|
|
813
|
-
const workbook = await this.parseWorkbook(stream);
|
|
814
|
-
model.sheets = workbook.sheets;
|
|
815
|
-
model.definedNames = workbook.definedNames;
|
|
816
|
-
model.views = workbook.views;
|
|
817
|
-
model.properties = workbook.properties;
|
|
818
|
-
model.calcProperties = workbook.calcProperties;
|
|
819
|
-
model.pivotCaches = workbook.pivotCaches;
|
|
820
|
-
break;
|
|
821
|
-
}
|
|
822
|
-
case OOXML_PATHS.xlSharedStrings:
|
|
823
|
-
model.sharedStrings = new SharedStringsXform();
|
|
824
|
-
await model.sharedStrings.parseStream(stream);
|
|
825
|
-
break;
|
|
826
|
-
case OOXML_PATHS.xlWorkbookRels:
|
|
827
|
-
model.workbookRels = await this.parseRels(stream);
|
|
828
|
-
break;
|
|
829
|
-
case OOXML_PATHS.docPropsApp: {
|
|
830
|
-
const appXform = new AppXform();
|
|
831
|
-
const appProperties = await appXform.parseStream(stream);
|
|
832
|
-
if (appProperties) {
|
|
833
|
-
model.company = appProperties.company;
|
|
834
|
-
model.manager = appProperties.manager;
|
|
835
|
-
}
|
|
836
|
-
break;
|
|
837
|
-
}
|
|
838
|
-
case OOXML_PATHS.docPropsCore: {
|
|
839
|
-
const coreXform = new CoreXform();
|
|
840
|
-
const coreProperties = await coreXform.parseStream(stream);
|
|
841
|
-
Object.assign(model, coreProperties);
|
|
842
|
-
break;
|
|
843
|
-
}
|
|
844
|
-
case OOXML_PATHS.xlStyles:
|
|
845
|
-
model.styles = new StylesXform();
|
|
846
|
-
await model.styles.parseStream(stream);
|
|
847
|
-
break;
|
|
848
|
-
default:
|
|
849
|
-
await this._processDefaultEntry(stream, model, entryName);
|
|
850
|
-
}
|
|
862
|
+
const handled = await this._processKnownEntry(stream, model, entryName, options);
|
|
863
|
+
if (!handled) {
|
|
864
|
+
// Pass raw entry data for drawings to enable passthrough
|
|
865
|
+
await this._processDefaultEntry(stream, model, entryName, entry.data);
|
|
851
866
|
}
|
|
852
867
|
}
|
|
853
868
|
}
|
|
@@ -857,11 +872,11 @@ class XLSX {
|
|
|
857
872
|
}
|
|
858
873
|
/**
|
|
859
874
|
* Process default entries (drawings, comments, tables, etc.)
|
|
875
|
+
* @param rawData Optional raw entry data for passthrough preservation (used by loadFromFiles)
|
|
860
876
|
*/
|
|
861
|
-
async _processDefaultEntry(stream, model, entryName) {
|
|
862
|
-
const
|
|
863
|
-
if (
|
|
864
|
-
const sheetNo = worksheetRelsSheetNo;
|
|
877
|
+
async _processDefaultEntry(stream, model, entryName, rawData) {
|
|
878
|
+
const sheetNo = getWorksheetNoFromWorksheetRelsPath(entryName);
|
|
879
|
+
if (sheetNo !== undefined) {
|
|
865
880
|
await this._processWorksheetRelsEntry(stream, model, sheetNo);
|
|
866
881
|
return true;
|
|
867
882
|
}
|
|
@@ -873,6 +888,10 @@ class XLSX {
|
|
|
873
888
|
const drawingName = getDrawingNameFromPath(entryName);
|
|
874
889
|
if (drawingName) {
|
|
875
890
|
await this._processDrawingEntry(stream, model, drawingName);
|
|
891
|
+
// For loadFromFiles path, store raw data for passthrough (drawings with charts)
|
|
892
|
+
if (rawData) {
|
|
893
|
+
model.rawDrawings[drawingName] = rawData;
|
|
894
|
+
}
|
|
876
895
|
return true;
|
|
877
896
|
}
|
|
878
897
|
const drawingRelsName = getDrawingNameFromRelsPath(entryName);
|
|
@@ -927,8 +946,27 @@ class XLSX {
|
|
|
927
946
|
await this._processPivotCacheRecordsEntry(stream, model, pivotCacheRecordsName);
|
|
928
947
|
return true;
|
|
929
948
|
}
|
|
949
|
+
// Store passthrough files (charts, etc.) for preservation
|
|
950
|
+
if (PassthroughManager.isPassthroughPath(entryName)) {
|
|
951
|
+
// If raw data is available (loadFromFiles path), use it directly
|
|
952
|
+
if (rawData) {
|
|
953
|
+
model.passthrough[entryName] = rawData;
|
|
954
|
+
}
|
|
955
|
+
else {
|
|
956
|
+
await this._processPassthroughEntry(stream, model, entryName);
|
|
957
|
+
}
|
|
958
|
+
return true;
|
|
959
|
+
}
|
|
930
960
|
return false;
|
|
931
961
|
}
|
|
962
|
+
/**
|
|
963
|
+
* Store a passthrough file for preservation during read/write cycles.
|
|
964
|
+
* These files are not parsed but stored as raw bytes to be written back unchanged.
|
|
965
|
+
*/
|
|
966
|
+
async _processPassthroughEntry(stream, model, entryName) {
|
|
967
|
+
const data = await this.collectStreamData(stream);
|
|
968
|
+
model.passthrough[entryName] = data;
|
|
969
|
+
}
|
|
932
970
|
// ===========================================================================
|
|
933
971
|
// Write methods - shared by all platforms
|
|
934
972
|
// ===========================================================================
|
|
@@ -1071,14 +1109,46 @@ class XLSX {
|
|
|
1071
1109
|
addDrawings(zip, model) {
|
|
1072
1110
|
const drawingXform = new DrawingXform();
|
|
1073
1111
|
const relsXform = new RelationshipsXform();
|
|
1112
|
+
const rawDrawings = model.rawDrawings || {};
|
|
1074
1113
|
model.worksheets.forEach((worksheet) => {
|
|
1075
1114
|
const { drawing } = worksheet;
|
|
1076
1115
|
if (drawing) {
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1116
|
+
// Check if drawing rels contain chart references using helper
|
|
1117
|
+
const hasChartReference = this.drawingHasChartReference(drawing);
|
|
1118
|
+
if (hasChartReference && rawDrawings[drawing.name]) {
|
|
1119
|
+
// Use raw data for drawings with chart references (passthrough)
|
|
1120
|
+
zip.append(rawDrawings[drawing.name], { name: drawingPath(drawing.name) });
|
|
1121
|
+
}
|
|
1122
|
+
else {
|
|
1123
|
+
// Use regenerated XML for normal drawings (images, shapes)
|
|
1124
|
+
// Filter out invalid anchors (null, undefined, or missing content)
|
|
1125
|
+
const filteredAnchors = (drawing.anchors || []).filter((a) => {
|
|
1126
|
+
if (a == null) {
|
|
1127
|
+
return false;
|
|
1128
|
+
}
|
|
1129
|
+
// Form controls have range.br and shape properties
|
|
1130
|
+
if (a.range?.br && a.shape) {
|
|
1131
|
+
return true;
|
|
1132
|
+
}
|
|
1133
|
+
// One-cell anchors need a valid picture
|
|
1134
|
+
if (!a.br && !a.picture) {
|
|
1135
|
+
return false;
|
|
1136
|
+
}
|
|
1137
|
+
// Two-cell anchors need either picture or shape
|
|
1138
|
+
if (a.br && !a.picture && !a.shape) {
|
|
1139
|
+
return false;
|
|
1140
|
+
}
|
|
1141
|
+
return true;
|
|
1142
|
+
});
|
|
1143
|
+
const drawingForWrite = drawing.anchors
|
|
1144
|
+
? { ...drawing, anchors: filteredAnchors }
|
|
1145
|
+
: drawing;
|
|
1146
|
+
drawingXform.prepare(drawingForWrite);
|
|
1147
|
+
const xml = drawingXform.toXml(drawingForWrite);
|
|
1148
|
+
zip.append(xml, { name: drawingPath(drawing.name) });
|
|
1149
|
+
}
|
|
1150
|
+
const relsXml = relsXform.toXml(drawing.rels);
|
|
1151
|
+
zip.append(relsXml, { name: drawingRelsPath(drawing.name) });
|
|
1082
1152
|
}
|
|
1083
1153
|
});
|
|
1084
1154
|
}
|
|
@@ -1093,6 +1163,15 @@ class XLSX {
|
|
|
1093
1163
|
});
|
|
1094
1164
|
});
|
|
1095
1165
|
}
|
|
1166
|
+
/**
|
|
1167
|
+
* Write passthrough files (charts, etc.) that were preserved during read.
|
|
1168
|
+
* These files are written back unchanged to preserve unsupported features.
|
|
1169
|
+
*/
|
|
1170
|
+
addPassthrough(zip, model) {
|
|
1171
|
+
const passthroughManager = new PassthroughManager();
|
|
1172
|
+
passthroughManager.fromRecord(model.passthrough || {});
|
|
1173
|
+
passthroughManager.writeToZip(zip);
|
|
1174
|
+
}
|
|
1096
1175
|
addPivotTables(zip, model) {
|
|
1097
1176
|
if (!model.pivotTables.length) {
|
|
1098
1177
|
return;
|
|
@@ -1158,7 +1237,12 @@ class XLSX {
|
|
|
1158
1237
|
options.useSharedStrings !== undefined ? options.useSharedStrings : true;
|
|
1159
1238
|
model.useStyles = options.useStyles !== undefined ? options.useStyles : true;
|
|
1160
1239
|
model.sharedStrings = new SharedStringsXform();
|
|
1240
|
+
// Preserve default font from parsed styles if available
|
|
1241
|
+
const oldDefaultFont = model.defaultFont;
|
|
1161
1242
|
model.styles = model.useStyles ? new StylesXform(true) : new StylesXform.Mock();
|
|
1243
|
+
if (oldDefaultFont && model.styles.setDefaultFont) {
|
|
1244
|
+
model.styles.setDefaultFont(oldDefaultFont);
|
|
1245
|
+
}
|
|
1162
1246
|
const workbookXform = new WorkbookXform();
|
|
1163
1247
|
const worksheetXform = new WorkSheetXform();
|
|
1164
1248
|
workbookXform.prepare(model);
|
|
@@ -1185,6 +1269,11 @@ class XLSX {
|
|
|
1185
1269
|
});
|
|
1186
1270
|
// ContentTypesXform expects this flag
|
|
1187
1271
|
model.hasCheckboxes = model.styles.hasCheckboxes;
|
|
1272
|
+
// Build passthroughContentTypes for ContentTypesXform using PassthroughManager
|
|
1273
|
+
const passthrough = model.passthrough || {};
|
|
1274
|
+
const passthroughManager = new PassthroughManager();
|
|
1275
|
+
passthroughManager.fromRecord(passthrough);
|
|
1276
|
+
model.passthroughContentTypes = passthroughManager.getContentTypes();
|
|
1188
1277
|
}
|
|
1189
1278
|
}
|
|
1190
1279
|
XLSX.RelType = RelType;
|
|
@@ -119,7 +119,7 @@ class CSV {
|
|
|
119
119
|
const dateFormats = options?.dateFormats ?? DEFAULT_DATE_FORMATS;
|
|
120
120
|
const map = options?.map ||
|
|
121
121
|
createDefaultValueMapper(dateFormats, {
|
|
122
|
-
decimalSeparator: options?.valueMapperOptions?.decimalSeparator
|
|
122
|
+
decimalSeparator: options?.valueMapperOptions?.decimalSeparator
|
|
123
123
|
});
|
|
124
124
|
const rows = (0, csv_core_1.parseCsv)(str, options?.parserOptions);
|
|
125
125
|
for (const row of rows) {
|
|
@@ -161,7 +161,7 @@ class CSV {
|
|
|
161
161
|
const dateFormats = options?.dateFormats ?? DEFAULT_DATE_FORMATS;
|
|
162
162
|
const map = options?.map ||
|
|
163
163
|
createDefaultValueMapper(dateFormats, {
|
|
164
|
-
decimalSeparator: options?.valueMapperOptions?.decimalSeparator
|
|
164
|
+
decimalSeparator: options?.valueMapperOptions?.decimalSeparator
|
|
165
165
|
});
|
|
166
166
|
const parser = new csv_stream_1.CsvParserStream(options?.parserOptions);
|
|
167
167
|
return new Promise((resolve, reject) => {
|
|
@@ -231,7 +231,7 @@ class CSV {
|
|
|
231
231
|
const dateFormats = options?.dateFormats ?? DEFAULT_DATE_FORMATS;
|
|
232
232
|
const map = options?.map ||
|
|
233
233
|
createDefaultValueMapper(dateFormats, {
|
|
234
|
-
decimalSeparator: options?.valueMapperOptions?.decimalSeparator
|
|
234
|
+
decimalSeparator: options?.valueMapperOptions?.decimalSeparator
|
|
235
235
|
});
|
|
236
236
|
const parser = new csv_stream_1.CsvParserStream(options?.parserOptions);
|
|
237
237
|
parser.on("data", (row) => worksheet.addRow(row.map(map)));
|
|
@@ -41,7 +41,8 @@ class Column {
|
|
|
41
41
|
width: this.width,
|
|
42
42
|
style: this.style,
|
|
43
43
|
hidden: this.hidden,
|
|
44
|
-
outlineLevel: this.outlineLevel
|
|
44
|
+
outlineLevel: this.outlineLevel,
|
|
45
|
+
bestFit: this.bestFit
|
|
45
46
|
};
|
|
46
47
|
}
|
|
47
48
|
set defn(value) {
|
|
@@ -58,6 +59,7 @@ class Column {
|
|
|
58
59
|
// headers must be set after style
|
|
59
60
|
this.header = value.header;
|
|
60
61
|
this._hidden = !!value.hidden;
|
|
62
|
+
this.bestFit = value.bestFit;
|
|
61
63
|
}
|
|
62
64
|
else {
|
|
63
65
|
delete this._header;
|
|
@@ -65,6 +67,7 @@ class Column {
|
|
|
65
67
|
delete this.width;
|
|
66
68
|
this.style = {};
|
|
67
69
|
this.outlineLevel = 0;
|
|
70
|
+
delete this.bestFit;
|
|
68
71
|
}
|
|
69
72
|
}
|
|
70
73
|
/**
|
|
@@ -154,6 +157,7 @@ class Column {
|
|
|
154
157
|
return (this.width === model.width &&
|
|
155
158
|
this.hidden === model.hidden &&
|
|
156
159
|
this.outlineLevel === model.outlineLevel &&
|
|
160
|
+
this.bestFit === model.bestFit &&
|
|
157
161
|
(0, under_dash_1.isEqual)(this.style, model.style));
|
|
158
162
|
}
|
|
159
163
|
get isDefault() {
|
|
@@ -166,6 +170,9 @@ class Column {
|
|
|
166
170
|
if (this.outlineLevel) {
|
|
167
171
|
return false;
|
|
168
172
|
}
|
|
173
|
+
if (this.bestFit) {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
169
176
|
const s = this.style;
|
|
170
177
|
if (s && (s.font || s.numFmt || s.alignment || s.border || s.fill || s.protection)) {
|
|
171
178
|
return false;
|
|
@@ -316,7 +323,8 @@ class Column {
|
|
|
316
323
|
isCustomWidth: column.isCustomWidth,
|
|
317
324
|
hidden: column.hidden,
|
|
318
325
|
outlineLevel: column.outlineLevel,
|
|
319
|
-
collapsed: column.collapsed
|
|
326
|
+
collapsed: column.collapsed,
|
|
327
|
+
bestFit: column.bestFit
|
|
320
328
|
};
|
|
321
329
|
cols.push(col);
|
|
322
330
|
}
|
|
@@ -391,7 +391,8 @@ class Row {
|
|
|
391
391
|
style: this.style,
|
|
392
392
|
hidden: this.hidden,
|
|
393
393
|
outlineLevel: this.outlineLevel,
|
|
394
|
-
collapsed: this.collapsed
|
|
394
|
+
collapsed: this.collapsed,
|
|
395
|
+
dyDescent: this.dyDescent
|
|
395
396
|
}
|
|
396
397
|
: null;
|
|
397
398
|
}
|
|
@@ -438,6 +439,7 @@ class Row {
|
|
|
438
439
|
}
|
|
439
440
|
this.hidden = value.hidden;
|
|
440
441
|
this.outlineLevel = value.outlineLevel || 0;
|
|
442
|
+
this.dyDescent = value.dyDescent;
|
|
441
443
|
this.style = (value.style && JSON.parse(JSON.stringify(value.style))) || {};
|
|
442
444
|
}
|
|
443
445
|
}
|