@office-open/xlsx 0.6.10 → 0.7.0
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/index.d.mts +178 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +989 -25
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -87,6 +87,21 @@ var ContentTypes = class extends BaseXmlComponent {
|
|
|
87
87
|
key: `/xl/drawings/drawing${index}.xml`
|
|
88
88
|
});
|
|
89
89
|
}
|
|
90
|
+
addComments(index) {
|
|
91
|
+
this.dynamicEntries.push({
|
|
92
|
+
type: "Override",
|
|
93
|
+
contentType: "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml",
|
|
94
|
+
key: `/xl/comments${index}.xml`
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
addVmlDrawing() {
|
|
98
|
+
if (this.dynamicEntries.some((e) => e.type === "Default" && e.key === "vml")) return;
|
|
99
|
+
this.dynamicEntries.push({
|
|
100
|
+
type: "Default",
|
|
101
|
+
contentType: "application/vnd.openxmlformats-officedocument.vmlDrawing",
|
|
102
|
+
key: "vml"
|
|
103
|
+
});
|
|
104
|
+
}
|
|
90
105
|
addImageType(extension) {
|
|
91
106
|
const contentType = extension === "png" ? "image/png" : "image/jpeg";
|
|
92
107
|
if (this.dynamicEntries.some((e) => e.type === "Default" && e.key === extension)) return;
|
|
@@ -96,6 +111,27 @@ var ContentTypes = class extends BaseXmlComponent {
|
|
|
96
111
|
key: extension
|
|
97
112
|
});
|
|
98
113
|
}
|
|
114
|
+
addPivotTable(index) {
|
|
115
|
+
this.dynamicEntries.push({
|
|
116
|
+
type: "Override",
|
|
117
|
+
contentType: "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml",
|
|
118
|
+
key: `/xl/pivotTables/pivotTable${index}.xml`
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
addPivotCacheDefinition(index) {
|
|
122
|
+
this.dynamicEntries.push({
|
|
123
|
+
type: "Override",
|
|
124
|
+
contentType: "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml",
|
|
125
|
+
key: `/xl/pivotCache/pivotCacheDefinition${index}.xml`
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
addPivotCacheRecords(index) {
|
|
129
|
+
this.dynamicEntries.push({
|
|
130
|
+
type: "Override",
|
|
131
|
+
contentType: "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheRecords+xml",
|
|
132
|
+
key: `/xl/pivotCache/pivotCacheRecords${index}.xml`
|
|
133
|
+
});
|
|
134
|
+
}
|
|
99
135
|
toXml(_context) {
|
|
100
136
|
const p = ["<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\">", STATIC_XML];
|
|
101
137
|
for (const e of this.dynamicEntries) if (e.type === "Default") p.push(`<Default ContentType="${e.contentType}" Extension="${e.key}"/>`);
|
|
@@ -241,6 +277,7 @@ var Styles = class extends BaseXmlComponent {
|
|
|
241
277
|
numFmtId: 0
|
|
242
278
|
}];
|
|
243
279
|
cellXfKeys = /* @__PURE__ */ new Map();
|
|
280
|
+
dxfs = [];
|
|
244
281
|
constructor() {
|
|
245
282
|
super("styleSheet");
|
|
246
283
|
this.fontKeys.set(fontKey(this.fonts[0]), 0);
|
|
@@ -269,6 +306,15 @@ var Styles = class extends BaseXmlComponent {
|
|
|
269
306
|
this.cellXfKeys.set(key, idx);
|
|
270
307
|
return idx;
|
|
271
308
|
}
|
|
309
|
+
/**
|
|
310
|
+
* Register a differential format and return its index (dxfId).
|
|
311
|
+
* Used by conditional formatting rules.
|
|
312
|
+
*/
|
|
313
|
+
registerDxf(opts) {
|
|
314
|
+
const idx = this.dxfs.length;
|
|
315
|
+
this.dxfs.push(opts);
|
|
316
|
+
return idx;
|
|
317
|
+
}
|
|
272
318
|
registerFont(opts) {
|
|
273
319
|
if (!opts) return 0;
|
|
274
320
|
const key = fontKey(opts);
|
|
@@ -357,7 +403,22 @@ var Styles = class extends BaseXmlComponent {
|
|
|
357
403
|
}
|
|
358
404
|
p.push("</cellXfs>");
|
|
359
405
|
p.push("<cellStyles count=\"1\"><cellStyle name=\"Normal\" xfId=\"0\" builtinId=\"0\"/></cellStyles>");
|
|
360
|
-
|
|
406
|
+
if (this.dxfs.length > 0) {
|
|
407
|
+
p.push(`<dxfs count="${this.dxfs.length}">`);
|
|
408
|
+
for (const dxf of this.dxfs) {
|
|
409
|
+
const dParts = [];
|
|
410
|
+
if (dxf.font) dParts.push(`<font>${this.fontXmlStr(dxf.font)}</font>`);
|
|
411
|
+
if (dxf.fill) {
|
|
412
|
+
const bgColor = dxf.fill.color ? `<bgColor rgb="FF${dxf.fill.color}"/>` : "";
|
|
413
|
+
const patAttrs = attrs({ patternType: dxf.fill.patternType ?? "solid" });
|
|
414
|
+
dParts.push(`<fill><patternFill${patAttrs}>${bgColor}</patternFill></fill>`);
|
|
415
|
+
}
|
|
416
|
+
if (dxf.numFmt) dParts.push(`<numFmt formatCode="${escapeXml(dxf.numFmt)}"/>`);
|
|
417
|
+
if (dParts.length > 0) p.push(`<dxf>${dParts.join("")}</dxf>`);
|
|
418
|
+
else p.push("<dxf/>");
|
|
419
|
+
}
|
|
420
|
+
p.push("</dxfs>");
|
|
421
|
+
} else p.push("<dxfs count=\"0\"/>");
|
|
361
422
|
p.push("<tableStyles count=\"0\" defaultTableStyle=\"TableStyleMedium2\" defaultPivotStyle=\"PivotStyleLight16\"/>");
|
|
362
423
|
p.push("<extLst/>");
|
|
363
424
|
p.push("</styleSheet>");
|
|
@@ -432,13 +493,17 @@ var DefaultTheme = class extends BaseXmlComponent {
|
|
|
432
493
|
*/
|
|
433
494
|
var WorkbookXml = class extends BaseXmlComponent {
|
|
434
495
|
sheets;
|
|
435
|
-
|
|
496
|
+
pivotCaches;
|
|
497
|
+
constructor(sheets, pivotCaches) {
|
|
436
498
|
super("workbook");
|
|
437
499
|
this.sheets = sheets;
|
|
500
|
+
this.pivotCaches = pivotCaches ?? [];
|
|
438
501
|
}
|
|
439
502
|
toXml(_context) {
|
|
440
503
|
const p = [
|
|
441
|
-
"<workbook xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\">",
|
|
504
|
+
"<workbook xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" mc:Ignorable=\"x15 xr xr6 xr10 xr2\" xmlns:x15=\"http://schemas.microsoft.com/office/spreadsheetml/2010/11/main\" xmlns:xr=\"http://schemas.microsoft.com/office/spreadsheetml/2014/revision\" xmlns:xr6=\"http://schemas.microsoft.com/office/spreadsheetml/2016/revision6\" xmlns:xr10=\"http://schemas.microsoft.com/office/spreadsheetml/2016/revision10\" xmlns:xr2=\"http://schemas.microsoft.com/office/spreadsheetml/2015/revision2\">",
|
|
505
|
+
"<fileVersion appName=\"xl\" lastEdited=\"7\" lowestEdited=\"6\" rupBuild=\"29929\"/>",
|
|
506
|
+
"<workbookPr/>",
|
|
442
507
|
"<bookViews><workbookView xWindow=\"0\" yWindow=\"0\" windowWidth=\"28800\" windowHeight=\"12300\"/></bookViews>",
|
|
443
508
|
"<sheets>"
|
|
444
509
|
];
|
|
@@ -446,7 +511,14 @@ var WorkbookXml = class extends BaseXmlComponent {
|
|
|
446
511
|
const stateAttr = s.state && s.state !== "visible" ? ` state="${s.state}"` : "";
|
|
447
512
|
p.push(`<sheet name="${escapeXml(s.name)}" sheetId="${s.sheetId}" r:id="${s.rId}"${stateAttr}/>`);
|
|
448
513
|
}
|
|
449
|
-
p.push("</sheets
|
|
514
|
+
p.push("</sheets>");
|
|
515
|
+
p.push("<calcPr calcId=\"162913\"/>");
|
|
516
|
+
if (this.pivotCaches.length > 0) {
|
|
517
|
+
p.push("<pivotCaches>");
|
|
518
|
+
for (const pc of this.pivotCaches) p.push(`<pivotCache cacheId="${pc.cacheId}" r:id="${pc.rId}"/>`);
|
|
519
|
+
p.push("</pivotCaches>");
|
|
520
|
+
}
|
|
521
|
+
p.push("</workbook>");
|
|
450
522
|
return p.join("");
|
|
451
523
|
}
|
|
452
524
|
};
|
|
@@ -468,22 +540,38 @@ var Worksheet = class extends IgnoreIfEmptyXmlComponent {
|
|
|
468
540
|
columns;
|
|
469
541
|
mergeCells;
|
|
470
542
|
freezePanes;
|
|
543
|
+
protection;
|
|
471
544
|
autoFilter;
|
|
472
545
|
images;
|
|
473
546
|
chartOptions;
|
|
474
547
|
dataValidations;
|
|
475
548
|
conditionalFormats;
|
|
549
|
+
hyperlinks;
|
|
550
|
+
comments;
|
|
551
|
+
headerFooter;
|
|
552
|
+
pageSetup;
|
|
553
|
+
tabColor;
|
|
554
|
+
sheetView;
|
|
555
|
+
pivotTableOptions;
|
|
476
556
|
constructor(options) {
|
|
477
557
|
super("worksheet");
|
|
478
558
|
this.rows = options.rows ?? [];
|
|
479
559
|
this.columns = options.columns ?? [];
|
|
480
560
|
this.mergeCells = options.mergeCells ?? [];
|
|
481
561
|
this.freezePanes = options.freezePanes;
|
|
562
|
+
this.protection = options.protection;
|
|
482
563
|
this.autoFilter = options.autoFilter;
|
|
483
564
|
this.images = options.images ?? [];
|
|
484
565
|
this.chartOptions = options.charts ?? [];
|
|
485
566
|
this.dataValidations = options.dataValidations ?? [];
|
|
486
567
|
this.conditionalFormats = options.conditionalFormats ?? [];
|
|
568
|
+
this.hyperlinks = options.hyperlinks ?? [];
|
|
569
|
+
this.comments = options.comments ?? [];
|
|
570
|
+
this.headerFooter = options.headerFooter;
|
|
571
|
+
this.pageSetup = options.pageSetup;
|
|
572
|
+
this.tabColor = options.tabColor;
|
|
573
|
+
this.sheetView = options.sheetView;
|
|
574
|
+
this.pivotTableOptions = options.pivotTables ?? [];
|
|
487
575
|
}
|
|
488
576
|
get imageOptions() {
|
|
489
577
|
return this.images;
|
|
@@ -491,6 +579,18 @@ var Worksheet = class extends IgnoreIfEmptyXmlComponent {
|
|
|
491
579
|
get charts() {
|
|
492
580
|
return this.chartOptions;
|
|
493
581
|
}
|
|
582
|
+
get hyperlinkOptions() {
|
|
583
|
+
return this.hyperlinks;
|
|
584
|
+
}
|
|
585
|
+
get worksheetRows() {
|
|
586
|
+
return this.rows;
|
|
587
|
+
}
|
|
588
|
+
get commentOptions() {
|
|
589
|
+
return this.comments;
|
|
590
|
+
}
|
|
591
|
+
get pivotTables() {
|
|
592
|
+
return this.pivotTableOptions;
|
|
593
|
+
}
|
|
494
594
|
/**
|
|
495
595
|
* Zero-allocation fast path: directly concatenate XML string.
|
|
496
596
|
* Bypasses the IXmlableObject intermediate tree entirely.
|
|
@@ -499,7 +599,22 @@ var Worksheet = class extends IgnoreIfEmptyXmlComponent {
|
|
|
499
599
|
const fileData = context.fileData;
|
|
500
600
|
const sharedStrings = fileData?.sharedStrings;
|
|
501
601
|
const styles = fileData?.styles;
|
|
502
|
-
const p = ["<worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\">"];
|
|
602
|
+
const p = ["<worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" mc:Ignorable=\"x14ac xr xr2 xr3\" xmlns:x14ac=\"http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac\" xmlns:xr=\"http://schemas.microsoft.com/office/spreadsheetml/2014/revision\" xmlns:xr2=\"http://schemas.microsoft.com/office/spreadsheetml/2015/revision2\" xmlns:xr3=\"http://schemas.microsoft.com/office/spreadsheetml/2016/revision3\">"];
|
|
603
|
+
const hasTabColor = !!this.tabColor;
|
|
604
|
+
const hasOutline = this.columns.some((c) => c.outlineLevel !== void 0);
|
|
605
|
+
if (hasTabColor || hasOutline) {
|
|
606
|
+
const prParts = [];
|
|
607
|
+
if (this.tabColor) {
|
|
608
|
+
const tc = this.tabColor;
|
|
609
|
+
const tcAttrs = {};
|
|
610
|
+
if (tc.rgb) tcAttrs.rgb = tc.rgb;
|
|
611
|
+
if (tc.theme !== void 0) tcAttrs.theme = tc.theme;
|
|
612
|
+
if (tc.tint !== void 0) tcAttrs.tint = tc.tint;
|
|
613
|
+
prParts.push(`<tabColor${attrs(tcAttrs)}/>`);
|
|
614
|
+
}
|
|
615
|
+
if (hasOutline) prParts.push("<outlinePr summaryBelow=\"1\" summaryRight=\"1\"/>");
|
|
616
|
+
p.push(`<sheetPr>${prParts.join("")}</sheetPr>`);
|
|
617
|
+
}
|
|
503
618
|
const maxRow = this.rows.length;
|
|
504
619
|
let maxCol = 0;
|
|
505
620
|
for (const row of this.rows) if (row.cells && row.cells.length > maxCol) maxCol = row.cells.length;
|
|
@@ -515,8 +630,12 @@ var Worksheet = class extends IgnoreIfEmptyXmlComponent {
|
|
|
515
630
|
const leftCol = fp.col ? fp.col + 1 : 1;
|
|
516
631
|
const topLeftCell = this.defaultCellRef(topRow, leftCol);
|
|
517
632
|
const activePane = ySplit > 0 && xSplit > 0 ? "bottomRight" : ySplit > 0 ? "bottomLeft" : "topRight";
|
|
518
|
-
|
|
519
|
-
|
|
633
|
+
const svAttrs = this.buildSheetViewAttrs();
|
|
634
|
+
p.push(`<sheetViews><sheetView${svAttrs}>`, `<pane ySplit="${ySplit}" xSplit="${xSplit}" topLeftCell="${topLeftCell}" activePane="${activePane}" state="frozen"/>`, "</sheetView></sheetViews>");
|
|
635
|
+
} else {
|
|
636
|
+
const svAttrs = this.buildSheetViewAttrs();
|
|
637
|
+
p.push(`<sheetViews><sheetView${svAttrs}/></sheetViews>`);
|
|
638
|
+
}
|
|
520
639
|
p.push("<sheetFormatPr defaultRowHeight=\"15\"/>");
|
|
521
640
|
if (this.columns.length > 0) {
|
|
522
641
|
p.push("<cols>");
|
|
@@ -530,6 +649,8 @@ var Worksheet = class extends IgnoreIfEmptyXmlComponent {
|
|
|
530
649
|
colAttrs.customWidth = 1;
|
|
531
650
|
}
|
|
532
651
|
if (col.hidden) colAttrs.hidden = 1;
|
|
652
|
+
if (col.outlineLevel !== void 0) colAttrs.outlineLevel = col.outlineLevel;
|
|
653
|
+
if (col.collapsed) colAttrs.collapsed = 1;
|
|
533
654
|
p.push(selfCloseElement("col", attrs(colAttrs)));
|
|
534
655
|
}
|
|
535
656
|
p.push("</cols>");
|
|
@@ -556,7 +677,62 @@ var Worksheet = class extends IgnoreIfEmptyXmlComponent {
|
|
|
556
677
|
} else p.push(`<row${attrs(rowAttrs)}/>`);
|
|
557
678
|
}
|
|
558
679
|
p.push("</sheetData>");
|
|
559
|
-
if (this.
|
|
680
|
+
if (this.protection) {
|
|
681
|
+
const prot = this.protection;
|
|
682
|
+
const protAttrs = {};
|
|
683
|
+
if (prot.password) protAttrs.password = this.hashPassword(prot.password);
|
|
684
|
+
if (prot.sheet) protAttrs.sheet = 1;
|
|
685
|
+
if (prot.objects) protAttrs.objects = 1;
|
|
686
|
+
if (prot.scenarios) protAttrs.scenarios = 1;
|
|
687
|
+
if (prot.formatCells === false) protAttrs.formatCells = 0;
|
|
688
|
+
if (prot.formatColumns === false) protAttrs.formatColumns = 0;
|
|
689
|
+
if (prot.formatRows === false) protAttrs.formatRows = 0;
|
|
690
|
+
if (prot.insertColumns === false) protAttrs.insertColumns = 0;
|
|
691
|
+
if (prot.insertRows === false) protAttrs.insertRows = 0;
|
|
692
|
+
if (prot.insertHyperlinks === false) protAttrs.insertHyperlinks = 0;
|
|
693
|
+
if (prot.deleteColumns === false) protAttrs.deleteColumns = 0;
|
|
694
|
+
if (prot.deleteRows === false) protAttrs.deleteRows = 0;
|
|
695
|
+
if (prot.selectLockedCells) protAttrs.selectLockedCells = 1;
|
|
696
|
+
if (prot.sort === false) protAttrs.sort = 0;
|
|
697
|
+
if (prot.autoFilter === false) protAttrs.autoFilter = 0;
|
|
698
|
+
if (prot.pivotTables === false) protAttrs.pivotTables = 0;
|
|
699
|
+
if (prot.selectUnlockedCells) protAttrs.selectUnlockedCells = 1;
|
|
700
|
+
p.push(selfCloseElement("sheetProtection", attrs(protAttrs)));
|
|
701
|
+
}
|
|
702
|
+
if (this.autoFilter) if (typeof this.autoFilter === "string") p.push(selfCloseElement("autoFilter", attrs({ ref: this.autoFilter })));
|
|
703
|
+
else {
|
|
704
|
+
const af = this.autoFilter;
|
|
705
|
+
const inner = [];
|
|
706
|
+
for (const t10 of af.top10 ?? []) {
|
|
707
|
+
const t10Attrs = { val: t10.val };
|
|
708
|
+
if (t10.top === false) t10Attrs.top = 0;
|
|
709
|
+
if (t10.percent) t10Attrs.percent = 1;
|
|
710
|
+
inner.push(`<filterColumn colId="${t10.colId}"><top10${attrs(t10Attrs)}/></filterColumn>`);
|
|
711
|
+
}
|
|
712
|
+
for (const cf of af.customFilters ?? []) {
|
|
713
|
+
const cfAttrs = {};
|
|
714
|
+
if (cf.and) cfAttrs.and = 1;
|
|
715
|
+
const filters = [];
|
|
716
|
+
if (cf.val !== void 0) {
|
|
717
|
+
const fAttrs = { val: cf.val };
|
|
718
|
+
if (cf.operator) fAttrs.operator = cf.operator;
|
|
719
|
+
filters.push(selfCloseElement("customFilter", attrs(fAttrs)));
|
|
720
|
+
}
|
|
721
|
+
if (cf.val2 !== void 0) filters.push(selfCloseElement("customFilter", attrs({ val: cf.val2 })));
|
|
722
|
+
if (filters.length > 0) inner.push(`<filterColumn colId="${cf.colId}"><customFilters${attrs(cfAttrs)}>${filters.join("")}</customFilters></filterColumn>`);
|
|
723
|
+
}
|
|
724
|
+
if (af.sort && af.sort.length > 0) {
|
|
725
|
+
const sortParts = [];
|
|
726
|
+
for (const sc of af.sort) {
|
|
727
|
+
const scAttrs = { ref: sc.ref };
|
|
728
|
+
if (sc.descending) scAttrs.descending = 1;
|
|
729
|
+
sortParts.push(selfCloseElement("sortCondition", attrs(scAttrs)));
|
|
730
|
+
}
|
|
731
|
+
inner.push(`<sortState ref="${af.ref}">${sortParts.join("")}</sortState>`);
|
|
732
|
+
}
|
|
733
|
+
if (inner.length > 0) p.push(`<autoFilter ref="${af.ref}">`, ...inner, "</autoFilter>");
|
|
734
|
+
else p.push(selfCloseElement("autoFilter", attrs({ ref: af.ref })));
|
|
735
|
+
}
|
|
560
736
|
if (this.mergeCells.length > 0) {
|
|
561
737
|
p.push(`<mergeCells count="${this.mergeCells.length}">`);
|
|
562
738
|
for (const mc of this.mergeCells) {
|
|
@@ -604,10 +780,82 @@ var Worksheet = class extends IgnoreIfEmptyXmlComponent {
|
|
|
604
780
|
}
|
|
605
781
|
p.push("</dataValidations>");
|
|
606
782
|
}
|
|
783
|
+
if (this.hyperlinks.length > 0) {
|
|
784
|
+
p.push("<hyperlinks>");
|
|
785
|
+
let hlIdx = 0;
|
|
786
|
+
for (const hl of this.hyperlinks) {
|
|
787
|
+
const hlAttrs = { ref: hl.cell };
|
|
788
|
+
if (hl.target.type === "external") {
|
|
789
|
+
hlIdx++;
|
|
790
|
+
hlAttrs["r:id"] = `rId${hlIdx}`;
|
|
791
|
+
} else hlAttrs.location = hl.target.location;
|
|
792
|
+
if (hl.tooltip) hlAttrs.tooltip = hl.tooltip;
|
|
793
|
+
if (hl.display) hlAttrs.display = hl.display;
|
|
794
|
+
p.push(selfCloseElement("hyperlink", attrs(hlAttrs)));
|
|
795
|
+
}
|
|
796
|
+
p.push("</hyperlinks>");
|
|
797
|
+
}
|
|
607
798
|
p.push("<pageMargins left=\"0.75\" right=\"0.75\" top=\"1\" bottom=\"1\" header=\"0.5\" footer=\"0.5\"/>");
|
|
799
|
+
if (this.pageSetup) {
|
|
800
|
+
const ps = this.pageSetup;
|
|
801
|
+
const psAttrs = {};
|
|
802
|
+
if (ps.paperSize !== void 0) psAttrs.paperSize = ps.paperSize;
|
|
803
|
+
if (ps.orientation && ps.orientation !== "default") psAttrs.orientation = ps.orientation;
|
|
804
|
+
if (ps.scale !== void 0) psAttrs.scale = ps.scale;
|
|
805
|
+
if (ps.fitToWidth !== void 0) psAttrs.fitToWidth = ps.fitToWidth;
|
|
806
|
+
if (ps.fitToHeight !== void 0) psAttrs.fitToHeight = ps.fitToHeight;
|
|
807
|
+
if (ps.pageOrder && ps.pageOrder !== "downThenOver") psAttrs.pageOrder = ps.pageOrder;
|
|
808
|
+
if (ps.useFirstPageNumber) psAttrs.useFirstPageNumber = 1;
|
|
809
|
+
if (ps.firstPageNumber !== void 0) psAttrs.firstPageNumber = ps.firstPageNumber;
|
|
810
|
+
p.push(selfCloseElement("pageSetup", attrs(psAttrs)));
|
|
811
|
+
}
|
|
812
|
+
if (this.headerFooter) {
|
|
813
|
+
const hf = this.headerFooter;
|
|
814
|
+
const hfAttrs = {};
|
|
815
|
+
if (hf.differentOddEven) hfAttrs.differentOddEven = 1;
|
|
816
|
+
if (hf.differentFirst) hfAttrs.differentFirst = 1;
|
|
817
|
+
const inner = [];
|
|
818
|
+
if (hf.oddHeader) inner.push(`<oddHeader>${escapeXml(hf.oddHeader)}</oddHeader>`);
|
|
819
|
+
if (hf.oddFooter) inner.push(`<oddFooter>${escapeXml(hf.oddFooter)}</oddFooter>`);
|
|
820
|
+
if (hf.evenHeader) inner.push(`<evenHeader>${escapeXml(hf.evenHeader)}</evenHeader>`);
|
|
821
|
+
if (hf.evenFooter) inner.push(`<evenFooter>${escapeXml(hf.evenFooter)}</evenFooter>`);
|
|
822
|
+
if (hf.firstHeader) inner.push(`<firstHeader>${escapeXml(hf.firstHeader)}</firstHeader>`);
|
|
823
|
+
if (hf.firstFooter) inner.push(`<firstFooter>${escapeXml(hf.firstFooter)}</firstFooter>`);
|
|
824
|
+
if (inner.length > 0) p.push(`<headerFooter${attrs(hfAttrs)}>`, ...inner, "</headerFooter>");
|
|
825
|
+
else if (hfAttrs.differentOddEven || hfAttrs.differentFirst) p.push(selfCloseElement("headerFooter", attrs(hfAttrs)));
|
|
826
|
+
}
|
|
608
827
|
p.push("</worksheet>");
|
|
609
828
|
return p.join("");
|
|
610
829
|
}
|
|
830
|
+
buildSheetViewAttrs() {
|
|
831
|
+
const sv = this.sheetView;
|
|
832
|
+
const svMap = { workbookViewId: 0 };
|
|
833
|
+
if (sv?.tabSelected !== void 0) svMap.tabSelected = sv.tabSelected ? 1 : 0;
|
|
834
|
+
else svMap.tabSelected = 1;
|
|
835
|
+
if (sv?.showGridLines === false) svMap.showGridLines = 0;
|
|
836
|
+
if (sv?.showRowColHeaders === false) svMap.showRowColHeaders = 0;
|
|
837
|
+
if (sv?.showZeros === false) svMap.showZeros = 0;
|
|
838
|
+
if (sv?.zoomScale !== void 0) svMap.zoomScale = sv.zoomScale;
|
|
839
|
+
if (sv?.rightToLeft) svMap.rightToLeft = 1;
|
|
840
|
+
return attrs(svMap);
|
|
841
|
+
}
|
|
842
|
+
/**
|
|
843
|
+
* Excel legacy password hash (16-bit, little-endian hex).
|
|
844
|
+
* Matches the algorithm used by ECMA-376 Part 1, §18.2.27.
|
|
845
|
+
*/
|
|
846
|
+
hashPassword(password) {
|
|
847
|
+
let hash = 0;
|
|
848
|
+
for (let i = 0; i < password.length; i++) {
|
|
849
|
+
const c = password.charCodeAt(i);
|
|
850
|
+
hash = (hash >> 14 & 1) + (hash << 1 & 32767);
|
|
851
|
+
hash ^= c;
|
|
852
|
+
hash = hash & 16384 ? hash ^ 1 : hash;
|
|
853
|
+
}
|
|
854
|
+
hash = (hash >> 14 & 1) + (hash << 1 & 32767);
|
|
855
|
+
hash = (hash >> 14 & 1) + (hash << 1 & 32767);
|
|
856
|
+
hash ^= password.length;
|
|
857
|
+
return hash.toString(16).toUpperCase().padStart(4, "0");
|
|
858
|
+
}
|
|
611
859
|
/**
|
|
612
860
|
* Build the <f> element string for a cell formula.
|
|
613
861
|
*/
|
|
@@ -695,6 +943,7 @@ var Worksheet = class extends IgnoreIfEmptyXmlComponent {
|
|
|
695
943
|
var File = class {
|
|
696
944
|
worksheetOptions;
|
|
697
945
|
corePropsOptions;
|
|
946
|
+
dxfOptions;
|
|
698
947
|
_coreProperties;
|
|
699
948
|
_appProperties;
|
|
700
949
|
_contentTypes;
|
|
@@ -707,9 +956,11 @@ var File = class {
|
|
|
707
956
|
_fileRels;
|
|
708
957
|
_workbookRels;
|
|
709
958
|
_charts;
|
|
959
|
+
_pivotCacheRefs = [];
|
|
710
960
|
constructor(options) {
|
|
711
961
|
this.worksheetOptions = options.worksheets ?? [];
|
|
712
962
|
this.corePropsOptions = options;
|
|
963
|
+
this.dxfOptions = options.dxfs ?? [];
|
|
713
964
|
}
|
|
714
965
|
get coreProperties() {
|
|
715
966
|
return this._coreProperties ??= new CoreProperties(this.corePropsOptions);
|
|
@@ -730,6 +981,13 @@ var File = class {
|
|
|
730
981
|
get styles() {
|
|
731
982
|
return this._styles ??= new Styles();
|
|
732
983
|
}
|
|
984
|
+
/**
|
|
985
|
+
* Register a differential format and return its dxfId.
|
|
986
|
+
* Call this before generating XML to use the ID in conditional formatting rules.
|
|
987
|
+
*/
|
|
988
|
+
registerDxf(opts) {
|
|
989
|
+
return this.styles.registerDxf(opts);
|
|
990
|
+
}
|
|
733
991
|
get theme() {
|
|
734
992
|
return this._theme ??= new DefaultTheme();
|
|
735
993
|
}
|
|
@@ -740,7 +998,7 @@ var File = class {
|
|
|
740
998
|
sheetId: i + 1,
|
|
741
999
|
rId: `rId${i + 1}`
|
|
742
1000
|
}));
|
|
743
|
-
this._workbookXml = new WorkbookXml(sheets);
|
|
1001
|
+
this._workbookXml = new WorkbookXml(sheets, this._pivotCacheRefs);
|
|
744
1002
|
}
|
|
745
1003
|
return this._workbookXml;
|
|
746
1004
|
}
|
|
@@ -753,6 +1011,21 @@ var File = class {
|
|
|
753
1011
|
get charts() {
|
|
754
1012
|
return this._charts ??= new ChartCollection();
|
|
755
1013
|
}
|
|
1014
|
+
get dxfEntries() {
|
|
1015
|
+
return this.dxfOptions;
|
|
1016
|
+
}
|
|
1017
|
+
get pivotCacheRefs() {
|
|
1018
|
+
return this._pivotCacheRefs;
|
|
1019
|
+
}
|
|
1020
|
+
registerPivotCache(cacheId, rId) {
|
|
1021
|
+
this._pivotCacheRefs.push({
|
|
1022
|
+
cacheId,
|
|
1023
|
+
rId
|
|
1024
|
+
});
|
|
1025
|
+
}
|
|
1026
|
+
get worksheetConfigs() {
|
|
1027
|
+
return this.worksheetOptions;
|
|
1028
|
+
}
|
|
756
1029
|
get worksheets() {
|
|
757
1030
|
if (!this._worksheets) this._worksheets = this.worksheetOptions.map((ws) => new Worksheet(ws));
|
|
758
1031
|
return this._worksheets;
|
|
@@ -779,6 +1052,41 @@ var File = class {
|
|
|
779
1052
|
}
|
|
780
1053
|
};
|
|
781
1054
|
//#endregion
|
|
1055
|
+
//#region src/file/comments.ts
|
|
1056
|
+
/**
|
|
1057
|
+
* Generates xl/comments{n}.xml — cell comment data.
|
|
1058
|
+
*
|
|
1059
|
+
* @module
|
|
1060
|
+
*/
|
|
1061
|
+
var Comments = class extends BaseXmlComponent {
|
|
1062
|
+
entries;
|
|
1063
|
+
constructor(entries) {
|
|
1064
|
+
super("comments");
|
|
1065
|
+
this.entries = entries;
|
|
1066
|
+
}
|
|
1067
|
+
toXml(_context) {
|
|
1068
|
+
const authors = this.collectAuthors();
|
|
1069
|
+
const p = ["<comments xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\">", `<authors>`];
|
|
1070
|
+
for (const author of authors) p.push(`<author>${escapeXml(author)}</author>`);
|
|
1071
|
+
p.push("</authors><commentList>");
|
|
1072
|
+
for (const entry of this.entries) {
|
|
1073
|
+
const authorId = authors.indexOf(entry.author);
|
|
1074
|
+
p.push(`<comment ref="${entry.cell}" authorId="${authorId}"><text><t>${escapeXml(entry.text)}</t></text></comment>`);
|
|
1075
|
+
}
|
|
1076
|
+
p.push("</commentList></comments>");
|
|
1077
|
+
return p.join("");
|
|
1078
|
+
}
|
|
1079
|
+
collectAuthors() {
|
|
1080
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1081
|
+
const result = [];
|
|
1082
|
+
for (const entry of this.entries) if (!seen.has(entry.author)) {
|
|
1083
|
+
seen.add(entry.author);
|
|
1084
|
+
result.push(entry.author);
|
|
1085
|
+
}
|
|
1086
|
+
return result.length > 0 ? result : [""];
|
|
1087
|
+
}
|
|
1088
|
+
};
|
|
1089
|
+
//#endregion
|
|
782
1090
|
//#region src/file/drawing/drawing.ts
|
|
783
1091
|
/**
|
|
784
1092
|
* XLSX Drawing component — generates xl/drawings/drawing{n}.xml.
|
|
@@ -816,6 +1124,387 @@ var Drawing = class extends BaseXmlComponent {
|
|
|
816
1124
|
}
|
|
817
1125
|
};
|
|
818
1126
|
//#endregion
|
|
1127
|
+
//#region src/file/pivot/pivot-utils.ts
|
|
1128
|
+
/**
|
|
1129
|
+
* Extract unique values from source data for a given field index.
|
|
1130
|
+
*/
|
|
1131
|
+
function collectUniqueValues(records, fieldIdx) {
|
|
1132
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1133
|
+
const result = [];
|
|
1134
|
+
for (const row of records) {
|
|
1135
|
+
const val = row[fieldIdx];
|
|
1136
|
+
const key = String(val);
|
|
1137
|
+
if (!seen.has(key)) {
|
|
1138
|
+
seen.add(key);
|
|
1139
|
+
result.push(val);
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
return result;
|
|
1143
|
+
}
|
|
1144
|
+
/**
|
|
1145
|
+
* Check if a field is numeric (all non-empty values are numbers).
|
|
1146
|
+
*/
|
|
1147
|
+
function isNumericField(records, fieldIdx) {
|
|
1148
|
+
for (const row of records) {
|
|
1149
|
+
const val = row[fieldIdx];
|
|
1150
|
+
if (typeof val === "string" && val !== "") return false;
|
|
1151
|
+
}
|
|
1152
|
+
return true;
|
|
1153
|
+
}
|
|
1154
|
+
/**
|
|
1155
|
+
* Aggregate values using the specified function.
|
|
1156
|
+
*/
|
|
1157
|
+
function aggregate(values, func) {
|
|
1158
|
+
if (values.length === 0) return 0;
|
|
1159
|
+
switch (func) {
|
|
1160
|
+
case "sum": return values.reduce((a, b) => a + b, 0);
|
|
1161
|
+
case "count":
|
|
1162
|
+
case "countNums": return values.length;
|
|
1163
|
+
case "average": return values.reduce((a, b) => a + b, 0) / values.length;
|
|
1164
|
+
case "max": return Math.max(...values);
|
|
1165
|
+
case "min": return Math.min(...values);
|
|
1166
|
+
case "product": return values.reduce((a, b) => a * b, 1);
|
|
1167
|
+
case "var": return sampleVariance(values);
|
|
1168
|
+
case "varp": return populationVariance(values);
|
|
1169
|
+
case "stdDev": return Math.sqrt(sampleVariance(values));
|
|
1170
|
+
case "stdDevp": return Math.sqrt(populationVariance(values));
|
|
1171
|
+
default: return values.reduce((a, b) => a + b, 0);
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
function populationVariance(values) {
|
|
1175
|
+
const mean = values.reduce((a, b) => a + b, 0) / values.length;
|
|
1176
|
+
return values.reduce((sum, v) => sum + (v - mean) ** 2, 0) / values.length;
|
|
1177
|
+
}
|
|
1178
|
+
function sampleVariance(values) {
|
|
1179
|
+
if (values.length < 2) return 0;
|
|
1180
|
+
const mean = values.reduce((a, b) => a + b, 0) / values.length;
|
|
1181
|
+
return values.reduce((sum, v) => sum + (v - mean) ** 2, 0) / (values.length - 1);
|
|
1182
|
+
}
|
|
1183
|
+
//#endregion
|
|
1184
|
+
//#region src/file/pivot/pivot-cache-definition-xml.ts
|
|
1185
|
+
/**
|
|
1186
|
+
* PivotCacheDefinition XML generator.
|
|
1187
|
+
*
|
|
1188
|
+
* Generates xl/pivotCache/pivotCacheDefinition{N}.xml.
|
|
1189
|
+
* Follows CT_PivotCacheDefinition from sml.xsd.
|
|
1190
|
+
*
|
|
1191
|
+
* @module
|
|
1192
|
+
*/
|
|
1193
|
+
var PivotCacheDefinitionXml = class extends BaseXmlComponent {
|
|
1194
|
+
sourceRef;
|
|
1195
|
+
sourceSheet;
|
|
1196
|
+
sourceData;
|
|
1197
|
+
recordsRid;
|
|
1198
|
+
constructor(_cacheIdx, sourceRef, sourceSheet, sourceData, recordsRid) {
|
|
1199
|
+
super("pivotCacheDefinition");
|
|
1200
|
+
this.sourceRef = sourceRef;
|
|
1201
|
+
this.sourceSheet = sourceSheet;
|
|
1202
|
+
this.sourceData = sourceData;
|
|
1203
|
+
this.recordsRid = recordsRid;
|
|
1204
|
+
}
|
|
1205
|
+
toXml(_context) {
|
|
1206
|
+
const p = [];
|
|
1207
|
+
p.push(`<pivotCacheDefinition xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" r:id="${escapeXml(this.recordsRid)}" recordCount="${this.sourceData.records.length}" createdVersion="6" refreshedVersion="6" minRefreshableVersion="3">`);
|
|
1208
|
+
p.push(`<cacheSource type="worksheet"><worksheetSource ref="${escapeXml(this.sourceRef)}" sheet="${escapeXml(this.sourceSheet)}"/></cacheSource>`);
|
|
1209
|
+
const fields = this.sourceData.fieldNames;
|
|
1210
|
+
p.push(`<cacheFields count="${fields.length}">`);
|
|
1211
|
+
for (let i = 0; i < fields.length; i++) {
|
|
1212
|
+
const fieldName = fields[i];
|
|
1213
|
+
const numeric = isNumericField(this.sourceData.records, i);
|
|
1214
|
+
const uniqueVals = collectUniqueValues(this.sourceData.records, i);
|
|
1215
|
+
if (numeric) {
|
|
1216
|
+
let min = Infinity;
|
|
1217
|
+
let max = -Infinity;
|
|
1218
|
+
for (const row of this.sourceData.records) {
|
|
1219
|
+
const v = row[i];
|
|
1220
|
+
if (typeof v === "number") {
|
|
1221
|
+
if (v < min) min = v;
|
|
1222
|
+
if (v > max) max = v;
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
if (!isFinite(min)) {
|
|
1226
|
+
min = 0;
|
|
1227
|
+
max = 0;
|
|
1228
|
+
}
|
|
1229
|
+
const allInteger = this.sourceData.records.every((row) => typeof row[i] === "number" && Number.isInteger(row[i]));
|
|
1230
|
+
p.push(`<cacheField name="${escapeXml(fieldName)}" numFmtId="0"><sharedItems containsSemiMixedTypes="0" containsString="0" containsNumber="1" containsInteger="${allInteger ? "1" : "0"}" minValue="${min}" maxValue="${max}" count="${uniqueVals.length}"/></cacheField>`);
|
|
1231
|
+
} else {
|
|
1232
|
+
p.push(`<cacheField name="${escapeXml(fieldName)}" numFmtId="0"><sharedItems count="${uniqueVals.length}">`);
|
|
1233
|
+
for (const v of uniqueVals) p.push(`<s v="${escapeXml(String(v))}"/>`);
|
|
1234
|
+
p.push("</sharedItems></cacheField>");
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
p.push("</cacheFields>");
|
|
1238
|
+
p.push("</pivotCacheDefinition>");
|
|
1239
|
+
return p.join("");
|
|
1240
|
+
}
|
|
1241
|
+
};
|
|
1242
|
+
//#endregion
|
|
1243
|
+
//#region src/file/pivot/pivot-cache-records-xml.ts
|
|
1244
|
+
/**
|
|
1245
|
+
* PivotCacheRecords XML generator.
|
|
1246
|
+
*
|
|
1247
|
+
* Generates xl/pivotCache/pivotCacheRecords{N}.xml.
|
|
1248
|
+
* Follows CT_PivotCacheRecords from sml.xsd.
|
|
1249
|
+
*
|
|
1250
|
+
* @module
|
|
1251
|
+
*/
|
|
1252
|
+
var PivotCacheRecordsXml = class extends BaseXmlComponent {
|
|
1253
|
+
sourceData;
|
|
1254
|
+
numericFields;
|
|
1255
|
+
/** For string fields: value → sharedItems index */
|
|
1256
|
+
fieldIndexMaps;
|
|
1257
|
+
constructor(sourceData) {
|
|
1258
|
+
super("pivotCacheRecords");
|
|
1259
|
+
this.sourceData = sourceData;
|
|
1260
|
+
this.numericFields = sourceData.fieldNames.map((_, i) => isNumericField(sourceData.records, i));
|
|
1261
|
+
this.fieldIndexMaps = sourceData.fieldNames.map((_, i) => {
|
|
1262
|
+
if (this.numericFields[i]) return /* @__PURE__ */ new Map();
|
|
1263
|
+
const unique = collectUniqueValues(sourceData.records, i);
|
|
1264
|
+
const map = /* @__PURE__ */ new Map();
|
|
1265
|
+
for (let j = 0; j < unique.length; j++) map.set(String(unique[j]), j);
|
|
1266
|
+
return map;
|
|
1267
|
+
});
|
|
1268
|
+
}
|
|
1269
|
+
toXml(_context) {
|
|
1270
|
+
const p = [];
|
|
1271
|
+
p.push(`<pivotCacheRecords xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="${this.sourceData.records.length}">`);
|
|
1272
|
+
for (const row of this.sourceData.records) {
|
|
1273
|
+
p.push("<r>");
|
|
1274
|
+
for (let i = 0; i < row.length; i++) {
|
|
1275
|
+
const val = row[i];
|
|
1276
|
+
if (this.numericFields[i]) p.push(`<n v="${val}"/>`);
|
|
1277
|
+
else {
|
|
1278
|
+
const idx = this.fieldIndexMaps[i].get(String(val)) ?? 0;
|
|
1279
|
+
p.push(`<x v="${idx}"/>`);
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
p.push("</r>");
|
|
1283
|
+
}
|
|
1284
|
+
p.push("</pivotCacheRecords>");
|
|
1285
|
+
return p.join("");
|
|
1286
|
+
}
|
|
1287
|
+
};
|
|
1288
|
+
//#endregion
|
|
1289
|
+
//#region src/file/pivot/pivot-table-xml.ts
|
|
1290
|
+
/**
|
|
1291
|
+
* PivotTableDefinition XML generator.
|
|
1292
|
+
*
|
|
1293
|
+
* Generates xl/pivotTables/pivotTable{N}.xml.
|
|
1294
|
+
* Follows CT_pivotTableDefinition from sml.xsd.
|
|
1295
|
+
*
|
|
1296
|
+
* @module
|
|
1297
|
+
*/
|
|
1298
|
+
var PivotTableXml = class extends BaseXmlComponent {
|
|
1299
|
+
options;
|
|
1300
|
+
sourceData;
|
|
1301
|
+
cacheId;
|
|
1302
|
+
constructor(options, sourceData, cacheId) {
|
|
1303
|
+
super("pivotTableDefinition");
|
|
1304
|
+
this.options = options;
|
|
1305
|
+
this.sourceData = sourceData;
|
|
1306
|
+
this.cacheId = cacheId;
|
|
1307
|
+
}
|
|
1308
|
+
toXml(_context) {
|
|
1309
|
+
const o = this.options;
|
|
1310
|
+
const fields = this.sourceData.fieldNames;
|
|
1311
|
+
const rowFieldNames = o.rows;
|
|
1312
|
+
const colFieldNames = o.columns ?? [];
|
|
1313
|
+
const dataFields = o.data;
|
|
1314
|
+
const style = o.style ?? "PivotStyleLight16";
|
|
1315
|
+
const location = o.location ?? "A3";
|
|
1316
|
+
const name = o.name ?? "PivotTable1";
|
|
1317
|
+
const rowFieldIndices = rowFieldNames.map((n) => fields.indexOf(n));
|
|
1318
|
+
const colFieldIndices = colFieldNames.map((n) => fields.indexOf(n));
|
|
1319
|
+
const dataFieldIndices = dataFields.map((df) => fields.indexOf(df.field));
|
|
1320
|
+
const pivotFieldsXml = this.buildPivotFields(rowFieldIndices, colFieldIndices, dataFieldIndices);
|
|
1321
|
+
const rowFieldsXml = this.buildRowFields(rowFieldIndices);
|
|
1322
|
+
const rowItemsXml = this.buildRowItems(rowFieldIndices);
|
|
1323
|
+
const colFieldsXml = this.buildColFields(colFieldIndices);
|
|
1324
|
+
const colItemsXml = this.buildColItems(colFieldIndices, dataFields);
|
|
1325
|
+
const dataFieldsXml = this.buildDataFields(dataFields, dataFieldIndices);
|
|
1326
|
+
const locationRef = this.computeLocationRef(location, rowFieldIndices, colFieldIndices, dataFields);
|
|
1327
|
+
const p = [];
|
|
1328
|
+
p.push(`<pivotTableDefinition xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" name="${escapeXml(name)}" cacheId="${this.cacheId}" dataCaption="Values" updatedVersion="6" minRefreshableVersion="3" createdVersion="6" applyNumberFormats="0" applyBorderFormats="0" applyFontFormats="0" applyPatternFormats="0" applyAlignmentFormats="0" applyWidthHeightFormats="1" autoFormatId="0" useAutoFormatting="1" itemPrintTitles="1" indent="0" outline="1" outlineData="1" compact="1" compactData="1" rowGrandTotals="1" colGrandTotals="1">`);
|
|
1329
|
+
p.push(`<location ref="${escapeXml(locationRef)}" firstHeaderRow="1" firstDataRow="${colFieldIndices.length + 1}" firstDataCol="${rowFieldIndices.length}"/>`);
|
|
1330
|
+
p.push(pivotFieldsXml);
|
|
1331
|
+
p.push(rowFieldsXml);
|
|
1332
|
+
p.push(rowItemsXml);
|
|
1333
|
+
if (colFieldIndices.length > 0) p.push(colFieldsXml);
|
|
1334
|
+
p.push(colItemsXml);
|
|
1335
|
+
if (dataFields.length > 0) p.push(dataFieldsXml);
|
|
1336
|
+
p.push(`<pivotTableStyleInfo name="${escapeXml(style)}" showRowHeaders="1" showColHeaders="1" showRowStripes="0" showColStripes="0" showLastColumn="1"/>`);
|
|
1337
|
+
p.push("</pivotTableDefinition>");
|
|
1338
|
+
return p.join("");
|
|
1339
|
+
}
|
|
1340
|
+
buildPivotFields(rowIndices, colIndices, dataIndices) {
|
|
1341
|
+
const fields = this.sourceData.fieldNames;
|
|
1342
|
+
const parts = [`<pivotFields count="${fields.length}">`];
|
|
1343
|
+
for (let i = 0; i < fields.length; i++) {
|
|
1344
|
+
const isRow = rowIndices.includes(i);
|
|
1345
|
+
const isCol = colIndices.includes(i);
|
|
1346
|
+
if (dataIndices.includes(i)) parts.push(`<pivotField dataField="1" showAll="0"/>`);
|
|
1347
|
+
else if (isRow) {
|
|
1348
|
+
const uniqueVals = collectUniqueValues(this.sourceData.records, i);
|
|
1349
|
+
parts.push(`<pivotField axis="axisRow" showAll="0">`);
|
|
1350
|
+
parts.push(`<items count="${uniqueVals.length + 1}">`);
|
|
1351
|
+
for (let j = 0; j < uniqueVals.length; j++) parts.push(`<item x="${j}"/>`);
|
|
1352
|
+
parts.push(`<item t="default"/>`);
|
|
1353
|
+
parts.push("</items></pivotField>");
|
|
1354
|
+
} else if (isCol) {
|
|
1355
|
+
const uniqueVals = collectUniqueValues(this.sourceData.records, i);
|
|
1356
|
+
parts.push(`<pivotField axis="axisCol" showAll="0">`);
|
|
1357
|
+
parts.push(`<items count="${uniqueVals.length + 1}">`);
|
|
1358
|
+
for (let j = 0; j < uniqueVals.length; j++) parts.push(`<item x="${j}"/>`);
|
|
1359
|
+
parts.push(`<item t="default"/>`);
|
|
1360
|
+
parts.push("</items></pivotField>");
|
|
1361
|
+
} else parts.push(`<pivotField showAll="0"/>`);
|
|
1362
|
+
}
|
|
1363
|
+
parts.push("</pivotFields>");
|
|
1364
|
+
return parts.join("");
|
|
1365
|
+
}
|
|
1366
|
+
buildRowFields(rowIndices) {
|
|
1367
|
+
if (rowIndices.length === 0) return "<rowFields count=\"0\"/>";
|
|
1368
|
+
const parts = [`<rowFields count="${rowIndices.length}">`];
|
|
1369
|
+
for (const idx of rowIndices) parts.push(`<field x="${idx}"/>`);
|
|
1370
|
+
parts.push("</rowFields>");
|
|
1371
|
+
return parts.join("");
|
|
1372
|
+
}
|
|
1373
|
+
buildRowItems(rowIndices) {
|
|
1374
|
+
if (rowIndices.length === 0) return "<rowItems count=\"1\"><i/></rowItems>";
|
|
1375
|
+
const allUniqueCounts = [];
|
|
1376
|
+
for (const idx of rowIndices) {
|
|
1377
|
+
const vals = collectUniqueValues(this.sourceData.records, idx);
|
|
1378
|
+
allUniqueCounts.push(vals.length);
|
|
1379
|
+
}
|
|
1380
|
+
if (rowIndices.length === 1) {
|
|
1381
|
+
const count = allUniqueCounts[0];
|
|
1382
|
+
const parts = [`<rowItems count="${count + 1}">`];
|
|
1383
|
+
for (let i = 0; i < count; i++) parts.push(`<i><x v="${i}"/></i>`);
|
|
1384
|
+
parts.push(`<i t="grand"><x/></i>`);
|
|
1385
|
+
parts.push("</rowItems>");
|
|
1386
|
+
return parts.join("");
|
|
1387
|
+
}
|
|
1388
|
+
const combos = cartesianOfCounts(allUniqueCounts);
|
|
1389
|
+
const rowItems = [];
|
|
1390
|
+
for (const combo of combos) {
|
|
1391
|
+
const xParts = combo.map((v) => `<x v="${v}"/>`).join("");
|
|
1392
|
+
rowItems.push(`<i>${xParts}</i>`);
|
|
1393
|
+
}
|
|
1394
|
+
rowItems.push(`<i t="grand">${rowIndices.map(() => "<x/>").join("")}</i>`);
|
|
1395
|
+
return `<rowItems count="${rowItems.length}">${rowItems.join("")}</rowItems>`;
|
|
1396
|
+
}
|
|
1397
|
+
buildColFields(colIndices) {
|
|
1398
|
+
if (colIndices.length === 0) return "<colFields count=\"0\"/>";
|
|
1399
|
+
const parts = [`<colFields count="${colIndices.length}">`];
|
|
1400
|
+
for (const idx of colIndices) parts.push(`<field x="${idx}"/>`);
|
|
1401
|
+
parts.push("</colFields>");
|
|
1402
|
+
return parts.join("");
|
|
1403
|
+
}
|
|
1404
|
+
buildColItems(colIndices, dataFields) {
|
|
1405
|
+
if (colIndices.length > 0) {
|
|
1406
|
+
const allUniqueCounts = [];
|
|
1407
|
+
for (const idx of colIndices) {
|
|
1408
|
+
const vals = collectUniqueValues(this.sourceData.records, idx);
|
|
1409
|
+
allUniqueCounts.push(vals.length);
|
|
1410
|
+
}
|
|
1411
|
+
const combos = cartesianOfCounts(allUniqueCounts);
|
|
1412
|
+
const items = [];
|
|
1413
|
+
for (const combo of combos) {
|
|
1414
|
+
const xParts = combo.map((v) => `<x v="${v}"/>`).join("");
|
|
1415
|
+
items.push(`<i>${xParts}</i>`);
|
|
1416
|
+
}
|
|
1417
|
+
items.push(`<i t="grand">${colIndices.map(() => "<x/>").join("")}</i>`);
|
|
1418
|
+
return `<colItems count="${items.length}">${items.join("")}</colItems>`;
|
|
1419
|
+
}
|
|
1420
|
+
if (dataFields.length > 1) {
|
|
1421
|
+
const items = dataFields.map((_, i) => `<i><x v="${i}"/></i>`);
|
|
1422
|
+
return `<colItems count="${items.length}">${items.join("")}</colItems>`;
|
|
1423
|
+
}
|
|
1424
|
+
return "<colItems count=\"1\"><i/></colItems>";
|
|
1425
|
+
}
|
|
1426
|
+
buildDataFields(dataFields, dataFieldIndices) {
|
|
1427
|
+
if (dataFields.length === 0) return "<dataFields count=\"0\"/>";
|
|
1428
|
+
const parts = [`<dataFields count="${dataFields.length}">`];
|
|
1429
|
+
for (let i = 0; i < dataFields.length; i++) {
|
|
1430
|
+
const df = dataFields[i];
|
|
1431
|
+
const subtotal = df.summarize ?? "sum";
|
|
1432
|
+
const name = df.name ?? `${subtotal === "sum" ? "Sum" : subtotal} of ${df.field}`;
|
|
1433
|
+
parts.push(`<dataField name="${escapeXml(name)}" fld="${dataFieldIndices[i]}" subtotal="${subtotal}"/>`);
|
|
1434
|
+
}
|
|
1435
|
+
parts.push("</dataFields>");
|
|
1436
|
+
return parts.join("");
|
|
1437
|
+
}
|
|
1438
|
+
computeLocationRef(location, rowFieldIndices, colFieldIndices, dataFields) {
|
|
1439
|
+
const match = location.split(":")[0].match(/^([A-Z]+)(\d+)$/);
|
|
1440
|
+
if (!match) return location;
|
|
1441
|
+
const startCol = match[1];
|
|
1442
|
+
const startRow = parseInt(match[2], 10);
|
|
1443
|
+
let rowCount = 1;
|
|
1444
|
+
if (rowFieldIndices.length > 0) rowCount += collectUniqueValues(this.sourceData.records, rowFieldIndices[0]).length;
|
|
1445
|
+
rowCount += 1;
|
|
1446
|
+
let colCount = Math.max(rowFieldIndices.length, 1);
|
|
1447
|
+
if (colFieldIndices.length > 0) colCount += collectUniqueValues(this.sourceData.records, colFieldIndices[0]).length;
|
|
1448
|
+
else if (dataFields.length > 1) colCount += dataFields.length - 1;
|
|
1449
|
+
colCount += 1;
|
|
1450
|
+
return `${startCol}${startRow}:${colIndexToLetter(letterToColIndex(startCol) + colCount - 1)}${startRow + rowCount - 1}`;
|
|
1451
|
+
}
|
|
1452
|
+
};
|
|
1453
|
+
function letterToColIndex(letters) {
|
|
1454
|
+
let col = 0;
|
|
1455
|
+
for (let i = 0; i < letters.length; i++) col = col * 26 + (letters.charCodeAt(i) - 64);
|
|
1456
|
+
return col;
|
|
1457
|
+
}
|
|
1458
|
+
function colIndexToLetter(col) {
|
|
1459
|
+
let result = "";
|
|
1460
|
+
let n = col;
|
|
1461
|
+
while (n > 0) {
|
|
1462
|
+
n--;
|
|
1463
|
+
result = String.fromCharCode(65 + n % 26) + result;
|
|
1464
|
+
n = Math.floor(n / 26);
|
|
1465
|
+
}
|
|
1466
|
+
return result;
|
|
1467
|
+
}
|
|
1468
|
+
/** Cartesian product from counts: e.g. [2, 3] → [[0,0],[0,1],[0,2],[1,0],[1,1],[1,2]] */
|
|
1469
|
+
function cartesianOfCounts(counts) {
|
|
1470
|
+
if (counts.length === 0) return [[]];
|
|
1471
|
+
let result = [[]];
|
|
1472
|
+
for (const count of counts) {
|
|
1473
|
+
const next = [];
|
|
1474
|
+
for (const prefix of result) for (let i = 0; i < count; i++) next.push([...prefix, i]);
|
|
1475
|
+
result = next;
|
|
1476
|
+
}
|
|
1477
|
+
return result;
|
|
1478
|
+
}
|
|
1479
|
+
//#endregion
|
|
1480
|
+
//#region src/file/vml-notes.ts
|
|
1481
|
+
var VmlNotes = class {
|
|
1482
|
+
comments;
|
|
1483
|
+
constructor(comments) {
|
|
1484
|
+
this.comments = comments;
|
|
1485
|
+
}
|
|
1486
|
+
toXml() {
|
|
1487
|
+
const p = [
|
|
1488
|
+
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>",
|
|
1489
|
+
"<xml xmlns:v=\"urn:schemas-microsoft-com:vml\" xmlns:o=\"urn:schemas-microsoft-com:office:office\" xmlns:x=\"urn:schemas-microsoft-com:office:excel\">",
|
|
1490
|
+
"<o:shapelayout v:ext=\"edit\"><o:idmap v:ext=\"edit\" data=\"1\"/></o:shapelayout>",
|
|
1491
|
+
"<v:shapetype id=\"_x0000_t202\" coordsize=\"21600,21600\" o:spt=\"202\" path=\"m,l,21600r21600,l21600,xe\">",
|
|
1492
|
+
"<v:stroke joinstyle=\"miter\"/>",
|
|
1493
|
+
"<v:path gradientshapeok=\"t\" o:connecttype=\"rect\"/>",
|
|
1494
|
+
"</v:shapetype>"
|
|
1495
|
+
];
|
|
1496
|
+
for (let i = 0; i < this.comments.length; i++) {
|
|
1497
|
+
const c = this.comments[i];
|
|
1498
|
+
const col = c.cell.charCodeAt(0) - 65;
|
|
1499
|
+
const row = parseInt(c.cell.slice(1), 10) - 1;
|
|
1500
|
+
const anchor = `${col}, 0, ${row}, 0, ${col + 2}, 0, ${row + 2}, 0`;
|
|
1501
|
+
p.push(`<v:shape id="_x0000_s${1025 + i}" type="#_x0000_t202" style="position:absolute;margin-left:59.25pt;margin-top:1.5pt;width:108pt;height:59.25pt;z-index:1;visibility:hidden" fillcolor="infoBackground [80]" strokecolor="none [81]" o:insetmode="auto">`, `<v:fill color2="infoBackground [80]"/>`, `<v:shadow color="none [81]" obscured="t"/>`, `<v:path o:connecttype="none"/>`, `<v:textbox style="mso-direction-alt:auto"><div style="text-align:left"/></v:textbox>`, `<x:ClientData ObjectType="Note"><x:MoveWithCells/><x:SizeWithCells/>`, `<x:Anchor>${anchor}</x:Anchor>`, `<x:AutoFill>False</x:AutoFill>`, `<x:Row>${row}</x:Row>`, `<x:Column>${col}</x:Column>`, `</x:ClientData>`, `</v:shape>`);
|
|
1502
|
+
}
|
|
1503
|
+
p.push("</xml>");
|
|
1504
|
+
return p.join("");
|
|
1505
|
+
}
|
|
1506
|
+
};
|
|
1507
|
+
//#endregion
|
|
819
1508
|
//#region src/export/packer/next-compiler.ts
|
|
820
1509
|
/**
|
|
821
1510
|
* XLSX Compiler — compiles a File object into a Zippable structure.
|
|
@@ -844,23 +1533,34 @@ var Compiler = class {
|
|
|
844
1533
|
data: fmt(file.fileRelationships),
|
|
845
1534
|
path: "_rels/.rels"
|
|
846
1535
|
};
|
|
847
|
-
mapping["Workbook"] = {
|
|
848
|
-
data: fmt(file.workbookXml),
|
|
849
|
-
path: "xl/workbook.xml"
|
|
850
|
-
};
|
|
851
|
-
mapping["WorkbookRelationships"] = {
|
|
852
|
-
data: fmt(file.workbookRelationships),
|
|
853
|
-
path: "xl/_rels/workbook.xml.rels"
|
|
854
|
-
};
|
|
855
1536
|
const worksheets = file.worksheets;
|
|
856
1537
|
let globalMediaIdx = 0;
|
|
857
1538
|
let globalChartIdx = 0;
|
|
1539
|
+
let globalPivotIdx = 0;
|
|
1540
|
+
let globalPivotCacheIdx = 0;
|
|
1541
|
+
const pivotCacheDataMap = /* @__PURE__ */ new Map();
|
|
858
1542
|
for (let i = 0; i < worksheets.length; i++) {
|
|
859
1543
|
const ws = worksheets[i];
|
|
860
1544
|
const imgOpts = ws.imageOptions;
|
|
861
1545
|
const chartOpts = ws.charts;
|
|
1546
|
+
const hlOpts = ws.hyperlinkOptions;
|
|
1547
|
+
const sheetName = file.worksheetConfigs[i]?.name ?? `Sheet${i + 1}`;
|
|
862
1548
|
let sheetXml = fmt(ws);
|
|
863
|
-
|
|
1549
|
+
const hasMedia = imgOpts.length > 0 || chartOpts.length > 0;
|
|
1550
|
+
const hasExternalHyperlinks = hlOpts.some((h) => h.target.type === "external");
|
|
1551
|
+
const commentOpts = ws.commentOptions;
|
|
1552
|
+
const hasComments = commentOpts.length > 0;
|
|
1553
|
+
const pivotOpts = ws.pivotTables;
|
|
1554
|
+
const hasPivots = pivotOpts.length > 0;
|
|
1555
|
+
let wsRels;
|
|
1556
|
+
let nextRid = 0;
|
|
1557
|
+
if (hasMedia || hasExternalHyperlinks || hasComments || hasPivots) wsRels = new Relationships();
|
|
1558
|
+
if (hasExternalHyperlinks) for (const hl of hlOpts) {
|
|
1559
|
+
if (hl.target.type !== "external") continue;
|
|
1560
|
+
const rid = ++nextRid;
|
|
1561
|
+
wsRels.addRelationship(rid, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink", hl.target.url, "External");
|
|
1562
|
+
}
|
|
1563
|
+
if (hasMedia) {
|
|
864
1564
|
const drawingImages = [];
|
|
865
1565
|
const drawingCharts = [];
|
|
866
1566
|
const drawingRels = new Relationships();
|
|
@@ -909,25 +1609,121 @@ var Compiler = class {
|
|
|
909
1609
|
data: fmt(drawingRels),
|
|
910
1610
|
path: `xl/drawings/_rels/drawing${drawingIdx}.xml.rels`
|
|
911
1611
|
};
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
wsRels.addRelationship(
|
|
915
|
-
mapping[`WorksheetRels${i}`] = {
|
|
916
|
-
data: fmt(wsRels),
|
|
917
|
-
path: `xl/worksheets/_rels/sheet${i + 1}.xml.rels`
|
|
918
|
-
};
|
|
1612
|
+
const drawingRid = ++nextRid;
|
|
1613
|
+
sheetXml = sheetXml.slice(0, -12) + `<drawing r:id="rId${drawingRid}"/></worksheet>`;
|
|
1614
|
+
wsRels.addRelationship(drawingRid, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing", `../drawings/drawing${drawingIdx}.xml`);
|
|
919
1615
|
file.contentTypes.addDrawing(drawingIdx);
|
|
920
1616
|
}
|
|
1617
|
+
if (hasComments) {
|
|
1618
|
+
const commentsIdx = i + 1;
|
|
1619
|
+
const commentsXml = new Comments(commentOpts);
|
|
1620
|
+
mapping[`Comments${i}`] = {
|
|
1621
|
+
data: fmt(commentsXml),
|
|
1622
|
+
path: `xl/comments${commentsIdx}.xml`
|
|
1623
|
+
};
|
|
1624
|
+
const vmlNotes = new VmlNotes(commentOpts);
|
|
1625
|
+
mapping[`VmlDrawing${i}`] = {
|
|
1626
|
+
data: vmlNotes.toXml(),
|
|
1627
|
+
path: `xl/drawings/vmlDrawing${commentsIdx}.vml`
|
|
1628
|
+
};
|
|
1629
|
+
const commentsRid = ++nextRid;
|
|
1630
|
+
wsRels.addRelationship(commentsRid, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments", `../comments${commentsIdx}.xml`);
|
|
1631
|
+
const vmlRid = ++nextRid;
|
|
1632
|
+
wsRels.addRelationship(vmlRid, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing", `../drawings/vmlDrawing${commentsIdx}.vml`);
|
|
1633
|
+
sheetXml = sheetXml.slice(0, -12) + `<legacyDrawing r:id="rId${vmlRid}"/></worksheet>`;
|
|
1634
|
+
file.contentTypes.addComments(commentsIdx);
|
|
1635
|
+
file.contentTypes.addVmlDrawing();
|
|
1636
|
+
}
|
|
1637
|
+
if (hasPivots) for (const pt of pivotOpts) {
|
|
1638
|
+
globalPivotIdx++;
|
|
1639
|
+
const pivotIdx = globalPivotIdx;
|
|
1640
|
+
const sourceSheet = pt.sourceSheet ?? sheetName;
|
|
1641
|
+
const sourceWsIdx = file.worksheetConfigs.findIndex((ws) => (ws.name ?? `Sheet${file.worksheetConfigs.indexOf(ws) + 1}`) === sourceSheet);
|
|
1642
|
+
if (sourceWsIdx === -1) continue;
|
|
1643
|
+
const sourceWs = worksheets[sourceWsIdx];
|
|
1644
|
+
const sourceData = extractPivotSourceData(sourceWs.worksheetRows, pt.source);
|
|
1645
|
+
const cacheKey = `${sourceSheet}:${pt.source}`;
|
|
1646
|
+
let cacheId;
|
|
1647
|
+
let cacheIdx;
|
|
1648
|
+
const existing = pivotCacheDataMap.get(cacheKey);
|
|
1649
|
+
if (existing) {
|
|
1650
|
+
cacheId = existing.cacheId;
|
|
1651
|
+
cacheIdx = existing.cacheIdx;
|
|
1652
|
+
} else {
|
|
1653
|
+
globalPivotCacheIdx++;
|
|
1654
|
+
cacheIdx = globalPivotCacheIdx;
|
|
1655
|
+
cacheId = cacheIdx - 1;
|
|
1656
|
+
pivotCacheDataMap.set(cacheKey, {
|
|
1657
|
+
cacheId,
|
|
1658
|
+
cacheIdx
|
|
1659
|
+
});
|
|
1660
|
+
const cacheDefRels = new Relationships();
|
|
1661
|
+
cacheDefRels.addRelationship(1, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotCacheRecords", "pivotCacheRecords1.xml");
|
|
1662
|
+
const cacheDef = new PivotCacheDefinitionXml(cacheIdx, pt.source.split(":")[0] ? pt.source : "A1", sourceSheet, sourceData, "rId1");
|
|
1663
|
+
mapping[`PivotCacheDef${cacheIdx}`] = {
|
|
1664
|
+
data: fmt(cacheDef),
|
|
1665
|
+
path: `xl/pivotCache/pivotCacheDefinition${cacheIdx}.xml`
|
|
1666
|
+
};
|
|
1667
|
+
mapping[`PivotCacheDefRels${cacheIdx}`] = {
|
|
1668
|
+
data: fmt(cacheDefRels),
|
|
1669
|
+
path: `xl/pivotCache/_rels/pivotCacheDefinition${cacheIdx}.xml.rels`
|
|
1670
|
+
};
|
|
1671
|
+
const cacheRecords = new PivotCacheRecordsXml(sourceData);
|
|
1672
|
+
mapping[`PivotCacheRecords${cacheIdx}`] = {
|
|
1673
|
+
data: fmt(cacheRecords),
|
|
1674
|
+
path: `xl/pivotCache/pivotCacheRecords${cacheIdx}.xml`
|
|
1675
|
+
};
|
|
1676
|
+
file.contentTypes.addPivotCacheDefinition(cacheIdx);
|
|
1677
|
+
file.contentTypes.addPivotCacheRecords(cacheIdx);
|
|
1678
|
+
const wbPivotRid = file.workbookRelationships.relationshipCount + 1;
|
|
1679
|
+
file.workbookRelationships.addRelationship(wbPivotRid, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotCacheDefinition", `pivotCache/pivotCacheDefinition${cacheIdx}.xml`);
|
|
1680
|
+
file.registerPivotCache(cacheId, `rId${wbPivotRid}`);
|
|
1681
|
+
}
|
|
1682
|
+
const pivotTable = new PivotTableXml(pt, sourceData, cacheId);
|
|
1683
|
+
mapping[`PivotTable${pivotIdx}`] = {
|
|
1684
|
+
data: fmt(pivotTable),
|
|
1685
|
+
path: `xl/pivotTables/pivotTable${pivotIdx}.xml`
|
|
1686
|
+
};
|
|
1687
|
+
const ptRels = new Relationships();
|
|
1688
|
+
ptRels.addRelationship(1, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotCacheDefinition", `../pivotCache/pivotCacheDefinition${cacheIdx}.xml`);
|
|
1689
|
+
mapping[`PivotTableRels${pivotIdx}`] = {
|
|
1690
|
+
data: fmt(ptRels),
|
|
1691
|
+
path: `xl/pivotTables/_rels/pivotTable${pivotIdx}.xml.rels`
|
|
1692
|
+
};
|
|
1693
|
+
const ptRid = ++nextRid;
|
|
1694
|
+
wsRels.addRelationship(ptRid, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotTable", `../pivotTables/pivotTable${pivotIdx}.xml`);
|
|
1695
|
+
file.contentTypes.addPivotTable(pivotIdx);
|
|
1696
|
+
}
|
|
1697
|
+
if (hasPivots) {
|
|
1698
|
+
const rendered = renderPivotSheetData(pivotOpts, worksheets, file.sharedStrings, file.worksheetConfigs, sheetName);
|
|
1699
|
+
if (rendered.sheetData.length > 0) {
|
|
1700
|
+
sheetXml = sheetXml.replace(/<sheetData\/>|<sheetData><\/sheetData>/, rendered.sheetData);
|
|
1701
|
+
if (!sheetXml.includes("<dimension")) sheetXml = sheetXml.replace("<sheetViews", `<dimension ref="${rendered.dimensionRef}"/><sheetViews`);
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
if (wsRels) mapping[`WorksheetRels${i}`] = {
|
|
1705
|
+
data: fmt(wsRels),
|
|
1706
|
+
path: `xl/worksheets/_rels/sheet${i + 1}.xml.rels`
|
|
1707
|
+
};
|
|
921
1708
|
mapping[`Worksheet${i}`] = {
|
|
922
1709
|
data: sheetXml,
|
|
923
1710
|
path: `xl/worksheets/sheet${i + 1}.xml`
|
|
924
1711
|
};
|
|
925
1712
|
}
|
|
1713
|
+
mapping["Workbook"] = {
|
|
1714
|
+
data: fmt(file.workbookXml),
|
|
1715
|
+
path: "xl/workbook.xml"
|
|
1716
|
+
};
|
|
1717
|
+
mapping["WorkbookRelationships"] = {
|
|
1718
|
+
data: fmt(file.workbookRelationships),
|
|
1719
|
+
path: "xl/_rels/workbook.xml.rels"
|
|
1720
|
+
};
|
|
926
1721
|
const sharedStrings = file.sharedStrings;
|
|
927
1722
|
if (sharedStrings.count > 0) mapping["SharedStrings"] = {
|
|
928
1723
|
data: fmt(sharedStrings),
|
|
929
1724
|
path: "xl/sharedStrings.xml"
|
|
930
1725
|
};
|
|
1726
|
+
for (const dxf of file.dxfEntries) file.registerDxf(dxf);
|
|
931
1727
|
mapping["Styles"] = {
|
|
932
1728
|
data: fmt(file.styles),
|
|
933
1729
|
path: "xl/styles.xml"
|
|
@@ -964,6 +1760,174 @@ var Compiler = class {
|
|
|
964
1760
|
return compileMapping(mapping, overrides, mediaFiles, mediaLevel);
|
|
965
1761
|
}
|
|
966
1762
|
};
|
|
1763
|
+
/**
|
|
1764
|
+
* Extract source data from worksheet rows for pivot cache generation.
|
|
1765
|
+
*/
|
|
1766
|
+
function extractPivotSourceData(rows, sourceRef) {
|
|
1767
|
+
const parts = sourceRef.split(":");
|
|
1768
|
+
const startMatch = parts[0]?.match(/^([A-Z]+)(\d+)$/);
|
|
1769
|
+
const endMatch = parts[1]?.match(/^([A-Z]+)(\d+)$/);
|
|
1770
|
+
if (!startMatch) return {
|
|
1771
|
+
fieldNames: [],
|
|
1772
|
+
records: []
|
|
1773
|
+
};
|
|
1774
|
+
const startRow = parseInt(startMatch[2], 10) - 1;
|
|
1775
|
+
const endRow = endMatch ? parseInt(endMatch[2], 10) - 1 : startRow;
|
|
1776
|
+
const startCol = colLetterToIndex(startMatch[1]);
|
|
1777
|
+
const endCol = endMatch ? colLetterToIndex(endMatch[1]) : startCol;
|
|
1778
|
+
const colCount = endCol - startCol + 1;
|
|
1779
|
+
const headerRow = rows[startRow];
|
|
1780
|
+
const fieldNames = [];
|
|
1781
|
+
if (headerRow?.cells) for (let c = startCol; c <= endCol && c < headerRow.cells.length; c++) fieldNames.push(String(headerRow.cells[c]?.value ?? `Col${c}`));
|
|
1782
|
+
const records = [];
|
|
1783
|
+
for (let r = startRow + 1; r <= endRow; r++) {
|
|
1784
|
+
const row = rows[r];
|
|
1785
|
+
if (!row?.cells) continue;
|
|
1786
|
+
const record = [];
|
|
1787
|
+
for (let c = startCol; c <= endCol; c++) {
|
|
1788
|
+
const val = row.cells[c]?.value;
|
|
1789
|
+
if (typeof val === "number") record.push(val);
|
|
1790
|
+
else if (val instanceof Date) record.push(val.getTime());
|
|
1791
|
+
else record.push(String(val ?? ""));
|
|
1792
|
+
}
|
|
1793
|
+
if (record.length === colCount) records.push(record);
|
|
1794
|
+
}
|
|
1795
|
+
return {
|
|
1796
|
+
fieldNames,
|
|
1797
|
+
records
|
|
1798
|
+
};
|
|
1799
|
+
}
|
|
1800
|
+
function colLetterToIndex(letters) {
|
|
1801
|
+
let col = 0;
|
|
1802
|
+
for (let i = 0; i < letters.length; i++) col = col * 26 + (letters.charCodeAt(i) - 64);
|
|
1803
|
+
return col - 1;
|
|
1804
|
+
}
|
|
1805
|
+
/**
|
|
1806
|
+
* Pre-render pivot table output as sheetData cells.
|
|
1807
|
+
* Excel requires this so the pivot table is visible on open without manual refresh.
|
|
1808
|
+
*/
|
|
1809
|
+
function renderPivotSheetData(pivotOpts, worksheets, sharedStrings, worksheetConfigs, currentSheetName) {
|
|
1810
|
+
const rowCells = /* @__PURE__ */ new Map();
|
|
1811
|
+
let maxRow = 0;
|
|
1812
|
+
let maxCol = 0;
|
|
1813
|
+
let minRow = Infinity;
|
|
1814
|
+
let minCol = Infinity;
|
|
1815
|
+
for (const pt of pivotOpts) {
|
|
1816
|
+
const locMatch = (pt.location ?? "A3").match(/^([A-Z]+)(\d+)$/);
|
|
1817
|
+
if (!locMatch) continue;
|
|
1818
|
+
const startCol = colLetterToIndex(locMatch[1]);
|
|
1819
|
+
const startRow = parseInt(locMatch[2], 10);
|
|
1820
|
+
const rowFieldNames = pt.rows;
|
|
1821
|
+
const dataFields = pt.data;
|
|
1822
|
+
const sourceSheetName = pt.sourceSheet ?? currentSheetName;
|
|
1823
|
+
const sourceWsIdx = worksheetConfigs.findIndex((ws) => (ws.name ?? `Sheet${worksheetConfigs.indexOf(ws) + 1}`) === sourceSheetName);
|
|
1824
|
+
if (sourceWsIdx === -1) continue;
|
|
1825
|
+
const sourceData = extractPivotSourceData(worksheets[sourceWsIdx]?.worksheetRows ?? [], pt.source);
|
|
1826
|
+
if (sourceData.fieldNames.length === 0) continue;
|
|
1827
|
+
const fields = sourceData.fieldNames;
|
|
1828
|
+
const rowFieldIndices = rowFieldNames.map((n) => fields.indexOf(n));
|
|
1829
|
+
const dataFieldIndices = dataFields.map((df) => fields.indexOf(df.field));
|
|
1830
|
+
if (rowFieldIndices.some((idx) => idx === -1)) continue;
|
|
1831
|
+
if (dataFieldIndices.some((idx) => idx === -1)) continue;
|
|
1832
|
+
const groupMap = /* @__PURE__ */ new Map();
|
|
1833
|
+
for (const record of sourceData.records) {
|
|
1834
|
+
const groupKey = rowFieldIndices.map((fi) => String(record[fi])).join("|");
|
|
1835
|
+
let group = groupMap.get(groupKey);
|
|
1836
|
+
if (!group) {
|
|
1837
|
+
group = {
|
|
1838
|
+
keys: rowFieldIndices.map((fi) => record[fi]),
|
|
1839
|
+
values: dataFieldIndices.map(() => [])
|
|
1840
|
+
};
|
|
1841
|
+
groupMap.set(groupKey, group);
|
|
1842
|
+
}
|
|
1843
|
+
for (let di = 0; di < dataFieldIndices.length; di++) {
|
|
1844
|
+
const val = record[dataFieldIndices[di]];
|
|
1845
|
+
if (typeof val === "number") group.values[di].push(val);
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
const endCol = startCol + rowFieldNames.length + dataFields.length - 1;
|
|
1849
|
+
minCol = Math.min(minCol, startCol);
|
|
1850
|
+
maxCol = Math.max(maxCol, endCol);
|
|
1851
|
+
const addCells = (rowIdx, cells) => {
|
|
1852
|
+
let arr = rowCells.get(rowIdx);
|
|
1853
|
+
if (!arr) {
|
|
1854
|
+
arr = [];
|
|
1855
|
+
rowCells.set(rowIdx, arr);
|
|
1856
|
+
}
|
|
1857
|
+
arr.push(...cells);
|
|
1858
|
+
minRow = Math.min(minRow, rowIdx);
|
|
1859
|
+
maxRow = Math.max(maxRow, rowIdx);
|
|
1860
|
+
};
|
|
1861
|
+
const headerCells = [];
|
|
1862
|
+
for (const rfName of rowFieldNames) {
|
|
1863
|
+
const cellRef = colIndexToLetterCompiler(startCol + headerCells.length) + startRow;
|
|
1864
|
+
const strIdx = sharedStrings.register(rfName);
|
|
1865
|
+
headerCells.push(`<c r="${cellRef}" t="s"><v>${strIdx}</v></c>`);
|
|
1866
|
+
}
|
|
1867
|
+
for (const df of dataFields) {
|
|
1868
|
+
const cellRef = colIndexToLetterCompiler(startCol + headerCells.length) + startRow;
|
|
1869
|
+
const subtotal = df.summarize ?? "sum";
|
|
1870
|
+
const dfName = df.name ?? `${subtotal === "sum" ? "Sum" : subtotal} of ${df.field}`;
|
|
1871
|
+
const strIdx = sharedStrings.register(dfName);
|
|
1872
|
+
headerCells.push(`<c r="${cellRef}" t="s"><v>${strIdx}</v></c>`);
|
|
1873
|
+
}
|
|
1874
|
+
addCells(startRow, headerCells);
|
|
1875
|
+
let currentRow = startRow + 1;
|
|
1876
|
+
for (const [, group] of groupMap) {
|
|
1877
|
+
const cells = [];
|
|
1878
|
+
for (let ri = 0; ri < group.keys.length; ri++) {
|
|
1879
|
+
const cellRef = colIndexToLetterCompiler(startCol + ri) + currentRow;
|
|
1880
|
+
const strIdx = sharedStrings.register(String(group.keys[ri]));
|
|
1881
|
+
cells.push(`<c r="${cellRef}" t="s"><v>${strIdx}</v></c>`);
|
|
1882
|
+
}
|
|
1883
|
+
for (let di = 0; di < dataFields.length; di++) {
|
|
1884
|
+
const cellRef = colIndexToLetterCompiler(startCol + (rowFieldNames.length + di)) + currentRow;
|
|
1885
|
+
const subtotal = dataFields[di].summarize ?? "sum";
|
|
1886
|
+
const result = aggregate(group.values[di], subtotal);
|
|
1887
|
+
cells.push(`<c r="${cellRef}"><v>${result}</v></c>`);
|
|
1888
|
+
}
|
|
1889
|
+
addCells(currentRow, cells);
|
|
1890
|
+
currentRow++;
|
|
1891
|
+
}
|
|
1892
|
+
const gtCells = [];
|
|
1893
|
+
const gtStrIdx = sharedStrings.register("Grand Total");
|
|
1894
|
+
gtCells.push(`<c r="${colIndexToLetterCompiler(startCol)}${currentRow}" t="s"><v>${gtStrIdx}</v></c>`);
|
|
1895
|
+
for (let di = 0; di < dataFields.length; di++) {
|
|
1896
|
+
const cellRef = colIndexToLetterCompiler(startCol + (rowFieldNames.length + di)) + currentRow;
|
|
1897
|
+
const subtotal = dataFields[di].summarize ?? "sum";
|
|
1898
|
+
const result = aggregate(sourceData.records.map((r) => r[dataFieldIndices[di]]).filter((v) => typeof v === "number"), subtotal);
|
|
1899
|
+
gtCells.push(`<c r="${cellRef}"><v>${result}</v></c>`);
|
|
1900
|
+
}
|
|
1901
|
+
addCells(currentRow, gtCells);
|
|
1902
|
+
}
|
|
1903
|
+
if (rowCells.size === 0) return {
|
|
1904
|
+
sheetData: "",
|
|
1905
|
+
dimensionRef: ""
|
|
1906
|
+
};
|
|
1907
|
+
const parts = ["<sheetData>"];
|
|
1908
|
+
const sortedRows = [...rowCells.entries()].sort((a, b) => a[0] - b[0]);
|
|
1909
|
+
for (const [rowIdx, cells] of sortedRows) {
|
|
1910
|
+
parts.push(`<row r="${rowIdx}" x14ac:dyDescent="0.25">`);
|
|
1911
|
+
parts.push(...cells);
|
|
1912
|
+
parts.push("</row>");
|
|
1913
|
+
}
|
|
1914
|
+
parts.push("</sheetData>");
|
|
1915
|
+
const dimensionRef = `${colIndexToLetterCompiler(minCol === Infinity ? 0 : minCol)}${minRow === Infinity ? 1 : minRow}:${colIndexToLetterCompiler(maxCol)}${maxRow}`;
|
|
1916
|
+
return {
|
|
1917
|
+
sheetData: parts.join(""),
|
|
1918
|
+
dimensionRef
|
|
1919
|
+
};
|
|
1920
|
+
}
|
|
1921
|
+
function colIndexToLetterCompiler(col) {
|
|
1922
|
+
let result = "";
|
|
1923
|
+
let n = col + 1;
|
|
1924
|
+
while (n > 0) {
|
|
1925
|
+
n--;
|
|
1926
|
+
result = String.fromCharCode(65 + n % 26) + result;
|
|
1927
|
+
n = Math.floor(n / 26);
|
|
1928
|
+
}
|
|
1929
|
+
return result;
|
|
1930
|
+
}
|
|
967
1931
|
//#endregion
|
|
968
1932
|
//#region src/export/packer/packer.ts
|
|
969
1933
|
/**
|