@office-open/xlsx 0.6.9 → 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.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
- p.push("<dxfs count=\"0\"/>");
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
- constructor(sheets) {
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><calcPr calcId=\"191029\"/></workbook>");
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
- this.rows = options.children ?? [];
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
- p.push("<sheetViews><sheetView tabSelected=\"1\" workbookViewId=\"0\">", `<pane ySplit="${ySplit}" xSplit="${xSplit}" topLeftCell="${topLeftCell}" activePane="${activePane}" state="frozen"/>`, "</sheetView></sheetViews>");
519
- } else p.push("<sheetViews><sheetView tabSelected=\"1\" workbookViewId=\"0\"/></sheetViews>");
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.autoFilter) p.push(selfCloseElement("autoFilter", attrs({ ref: this.autoFilter })));
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.
@@ -824,7 +1513,7 @@ var Drawing = class extends BaseXmlComponent {
824
1513
  */
825
1514
  var Compiler = class {
826
1515
  formatter = new Formatter();
827
- compile(file, overrides = []) {
1516
+ compile(file, overrides = [], mediaLevel = 0) {
828
1517
  const context = {
829
1518
  fileData: file,
830
1519
  stack: []
@@ -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
- if (imgOpts.length > 0 || chartOpts.length > 0) {
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
- sheetXml = sheetXml.slice(0, -12) + `<drawing r:id="rId${rid}"/></worksheet>`;
913
- const wsRels = new Relationships();
914
- wsRels.addRelationship(rid, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing", `../drawings/drawing${drawingIdx}.xml`);
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"
@@ -961,9 +1757,177 @@ var Compiler = class {
961
1757
  data: img.data,
962
1758
  path: `xl/media/${img.fileName}`
963
1759
  });
964
- return compileMapping(mapping, overrides, mediaFiles);
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
  /**
@@ -973,7 +1937,7 @@ var Compiler = class {
973
1937
  */
974
1938
  const compiler = new Compiler();
975
1939
  const Packer = createPacker({
976
- compile: (file, overrides) => compiler.compile(file, overrides),
1940
+ compile: (file, overrides, mediaLevel) => compiler.compile(file, overrides, mediaLevel),
977
1941
  mimeType: OoxmlMimeType.XLSX
978
1942
  });
979
1943
  //#endregion
@@ -1172,7 +2136,7 @@ function parseWorksheetElement(wsEl, strings) {
1172
2136
  rowOpts.cells = cells;
1173
2137
  rows.push(rowOpts);
1174
2138
  }
1175
- opts.children = rows;
2139
+ opts.rows = rows;
1176
2140
  const mergeCellsEl = findChildByLocalName(wsEl, "mergeCells");
1177
2141
  if (mergeCellsEl) {
1178
2142
  const mergeCells = [];