@office-open/xlsx 0.6.5 → 0.6.7

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
@@ -1 +1,1725 @@
1
- export {};
1
+ import { AppProperties, BaseXmlComponent, ChartCollection, ChartSpace, Formatter, IgnoreIfEmptyXmlComponent, OoxmlMimeType, Relationships, buildCorePropertiesXml, buildCorePropertiesXmlString, compileMapping, createPacker, parseArchive, parseCorePropsElement, strFromU8, toJson, unzipSync, zipAndConvert } from "@office-open/core";
2
+ import { attr, attrNum, attrs, escapeXml, findChild, js2xml, selfCloseElement, textOf } from "@office-open/xml";
3
+ import { toUint8Array } from "undio";
4
+ //#region src/file/content-types.ts
5
+ /**
6
+ * Content Types module for XLSX packages.
7
+ *
8
+ * @module
9
+ */
10
+ const XLSX_MAIN = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml";
11
+ const XLSX_WORKSHEET = "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml";
12
+ const XLSX_STYLES = "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml";
13
+ const XLSX_SHARED_STRINGS = "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml";
14
+ const XLSX_THEME = "application/vnd.openxmlformats-officedocument.theme+xml";
15
+ const XLSX_CHART = "application/vnd.openxmlformats-officedocument.drawingml.chart+xml";
16
+ const STATIC_ENTRIES = [
17
+ {
18
+ type: "Default",
19
+ contentType: "application/vnd.openxmlformats-package.relationships+xml",
20
+ key: "rels"
21
+ },
22
+ {
23
+ type: "Default",
24
+ contentType: "application/xml",
25
+ key: "xml"
26
+ },
27
+ {
28
+ type: "Override",
29
+ contentType: XLSX_MAIN,
30
+ key: "/xl/workbook.xml"
31
+ },
32
+ {
33
+ type: "Override",
34
+ contentType: "application/vnd.openxmlformats-package.core-properties+xml",
35
+ key: "/docProps/core.xml"
36
+ },
37
+ {
38
+ type: "Override",
39
+ contentType: "application/vnd.openxmlformats-officedocument.extended-properties+xml",
40
+ key: "/docProps/app.xml"
41
+ }
42
+ ];
43
+ const STATIC_CHILDREN = [{ _attr: { xmlns: "http://schemas.openxmlformats.org/package/2006/content-types" } }, ...STATIC_ENTRIES.map((e) => {
44
+ if (e.type === "Default") return { Default: { _attr: {
45
+ ContentType: e.contentType,
46
+ Extension: e.key
47
+ } } };
48
+ return { Override: { _attr: {
49
+ ContentType: e.contentType,
50
+ PartName: e.key
51
+ } } };
52
+ })];
53
+ const STATIC_XML = STATIC_ENTRIES.map((e) => e.type === "Default" ? `<Default ContentType="${e.contentType}" Extension="${e.key}"/>` : `<Override ContentType="${e.contentType}" PartName="${e.key}"/>`).join("");
54
+ var ContentTypes = class extends BaseXmlComponent {
55
+ dynamicEntries = [];
56
+ constructor() {
57
+ super("Types");
58
+ }
59
+ addWorksheet(index) {
60
+ this.dynamicEntries.push({
61
+ type: "Override",
62
+ contentType: XLSX_WORKSHEET,
63
+ key: `/xl/worksheets/sheet${index}.xml`
64
+ });
65
+ }
66
+ addStyles() {
67
+ this.dynamicEntries.push({
68
+ type: "Override",
69
+ contentType: XLSX_STYLES,
70
+ key: "/xl/styles.xml"
71
+ });
72
+ }
73
+ addSharedStrings() {
74
+ this.dynamicEntries.push({
75
+ type: "Override",
76
+ contentType: XLSX_SHARED_STRINGS,
77
+ key: "/xl/sharedStrings.xml"
78
+ });
79
+ }
80
+ addTheme(index = 1) {
81
+ this.dynamicEntries.push({
82
+ type: "Override",
83
+ contentType: XLSX_THEME,
84
+ key: `/xl/theme/theme${index}.xml`
85
+ });
86
+ }
87
+ addChart(index) {
88
+ this.dynamicEntries.push({
89
+ type: "Override",
90
+ contentType: XLSX_CHART,
91
+ key: `/xl/charts/chart${index}.xml`
92
+ });
93
+ }
94
+ addDrawing(index) {
95
+ this.dynamicEntries.push({
96
+ type: "Override",
97
+ contentType: "application/vnd.openxmlformats-officedocument.drawing+xml",
98
+ key: `/xl/drawings/drawing${index}.xml`
99
+ });
100
+ }
101
+ addImageType(extension) {
102
+ const contentType = extension === "png" ? "image/png" : "image/jpeg";
103
+ if (this.dynamicEntries.some((e) => e.type === "Default" && e.key === extension)) return;
104
+ this.dynamicEntries.push({
105
+ type: "Default",
106
+ contentType,
107
+ key: extension
108
+ });
109
+ }
110
+ prepForXml(_context) {
111
+ const children = [...STATIC_CHILDREN];
112
+ for (const e of this.dynamicEntries) if (e.type === "Default") children.push({ Default: { _attr: {
113
+ ContentType: e.contentType,
114
+ Extension: e.key
115
+ } } });
116
+ else children.push({ Override: { _attr: {
117
+ ContentType: e.contentType,
118
+ PartName: e.key
119
+ } } });
120
+ return { Types: children };
121
+ }
122
+ toXml(_context) {
123
+ const p = ["<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\">", STATIC_XML];
124
+ for (const e of this.dynamicEntries) if (e.type === "Default") p.push(`<Default ContentType="${e.contentType}" Extension="${e.key}"/>`);
125
+ else p.push(`<Override ContentType="${e.contentType}" PartName="${e.key}"/>`);
126
+ p.push("</Types>");
127
+ return p.join("");
128
+ }
129
+ };
130
+ //#endregion
131
+ //#region src/file/core-properties.ts
132
+ /**
133
+ * Core Properties module for SpreadsheetML documents.
134
+ *
135
+ * @module
136
+ */
137
+ var CoreProperties = class extends BaseXmlComponent {
138
+ options;
139
+ constructor(options) {
140
+ super("cp:coreProperties");
141
+ this.options = options;
142
+ }
143
+ prepForXml(_context) {
144
+ return buildCorePropertiesXml(this.options);
145
+ }
146
+ toXml(_context) {
147
+ return buildCorePropertiesXmlString(this.options);
148
+ }
149
+ };
150
+ //#endregion
151
+ //#region src/file/media/media.ts
152
+ var Media = class {
153
+ map = /* @__PURE__ */ new Map();
154
+ addImage(key, data) {
155
+ this.map.set(key, data);
156
+ }
157
+ get array() {
158
+ return [...this.map.values()];
159
+ }
160
+ };
161
+ //#endregion
162
+ //#region src/file/shared-strings.ts
163
+ /**
164
+ * Shared Strings Table — generates xl/sharedStrings.xml.
165
+ *
166
+ * XLSX stores repeated string values in a central table to reduce file size.
167
+ * Cells reference strings by index into this table.
168
+ *
169
+ * @module
170
+ */
171
+ var SharedStrings = class extends BaseXmlComponent {
172
+ strings = [];
173
+ indexMap = /* @__PURE__ */ new Map();
174
+ constructor() {
175
+ super("sst");
176
+ }
177
+ /**
178
+ * Register a string and return its index.
179
+ * Returns existing index if the string is already registered.
180
+ */
181
+ register(s) {
182
+ const existing = this.indexMap.get(s);
183
+ if (existing !== void 0) return existing;
184
+ const idx = this.strings.length;
185
+ this.strings.push(s);
186
+ this.indexMap.set(s, idx);
187
+ return idx;
188
+ }
189
+ get count() {
190
+ return this.strings.length;
191
+ }
192
+ /**
193
+ * Zero-allocation fast path: directly concatenate XML string.
194
+ * Bypasses the IXmlableObject intermediate tree entirely.
195
+ */
196
+ toXml(_context) {
197
+ const p = ["<sst xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\"", ` count="${this.strings.length}" uniqueCount="${this.indexMap.size}">`];
198
+ for (const s of this.strings) p.push(`<si><t>${escapeXml(s)}</t></si>`);
199
+ p.push("</sst>");
200
+ return p.join("");
201
+ }
202
+ prepForXml(_context) {
203
+ const children = [{ _attr: {
204
+ xmlns: "http://schemas.openxmlformats.org/spreadsheetml/2006/main",
205
+ count: this.strings.length,
206
+ uniqueCount: this.indexMap.size
207
+ } }];
208
+ for (const s of this.strings) children.push({ si: [{ t: [s] }] });
209
+ return { sst: children };
210
+ }
211
+ };
212
+ //#endregion
213
+ //#region src/file/styles.ts
214
+ /**
215
+ * Styles component — generates xl/styles.xml.
216
+ *
217
+ * XLSX uses an index-based style system: cells reference style entries
218
+ * via the `s` attribute, which is an index into `cellXfs`.
219
+ *
220
+ * @module
221
+ */
222
+ function fontKey(f) {
223
+ return `b${f.bold ? 1 : 0}i${f.italic ? 1 : 0}u${f.underline ? 1 : 0}s${f.strike ? 1 : 0}z${f.size ?? 0}c${f.color ?? ""}n${f.fontName ?? ""}`;
224
+ }
225
+ function fillKey(f) {
226
+ return `t${f.type ?? ""}c${f.color ?? ""}p${f.patternType ?? ""}`;
227
+ }
228
+ function borderKey(b) {
229
+ const sk = (o) => `${o?.style ?? ""}_${o?.color ?? ""}`;
230
+ return `t${sk(b.top)}b${sk(b.bottom)}l${sk(b.left)}r${sk(b.right)}d${sk(b.diagonal)}`;
231
+ }
232
+ const BUILTIN_NUMFMTS = {
233
+ General: 0,
234
+ "0": 1,
235
+ "0.00": 2,
236
+ "#,##0": 3,
237
+ "#,##0.00": 4,
238
+ "0%": 9,
239
+ "0.00%": 10,
240
+ "0.00E+00": 11,
241
+ "mm-dd-yy": 14,
242
+ "d-mmm-yy": 15,
243
+ "d-mmm": 16,
244
+ "mmm-yy": 17,
245
+ "h:mm AM/PM": 18,
246
+ "h:mm:ss AM/PM": 19,
247
+ "h:mm": 20,
248
+ "h:mm:ss": 21,
249
+ "m/d/yy h:mm": 22,
250
+ "#,##0 ;(#,##0)": 37,
251
+ "#,##0 ;[Red](#,##0)": 38,
252
+ "#,##0.00;(#,##0.00)": 39,
253
+ "#,##0.00;[Red](#,##0.00)": 40,
254
+ "mm:ss": 45,
255
+ "[h]:mm:ss": 46,
256
+ "mmss.0": 47,
257
+ "##0.0E+0": 48,
258
+ "@": 49
259
+ };
260
+ var Styles = class extends BaseXmlComponent {
261
+ fonts = [{
262
+ size: 11,
263
+ fontName: "Calibri"
264
+ }];
265
+ fontKeys = /* @__PURE__ */ new Map();
266
+ fills = [{ patternType: "none" }, { patternType: "gray125" }];
267
+ fillKeys = /* @__PURE__ */ new Map();
268
+ borders = [{}];
269
+ borderKeys = /* @__PURE__ */ new Map();
270
+ customNumFmts = /* @__PURE__ */ new Map();
271
+ nextCustomNumFmtId = 164;
272
+ cellXfs = [{
273
+ fontId: 0,
274
+ fillId: 0,
275
+ borderId: 0,
276
+ numFmtId: 0
277
+ }];
278
+ cellXfKeys = /* @__PURE__ */ new Map();
279
+ constructor() {
280
+ super("styleSheet");
281
+ this.fontKeys.set(fontKey(this.fonts[0]), 0);
282
+ this.fillKeys.set(fillKey(this.fills[0]), 0);
283
+ this.fillKeys.set(fillKey(this.fills[1]), 1);
284
+ this.borderKeys.set(borderKey(this.borders[0]), 0);
285
+ this.cellXfKeys.set(this.cellXfKey(this.cellXfs[0]), 0);
286
+ }
287
+ /**
288
+ * Register a style and return its index (for the cell `s` attribute).
289
+ * Deduplicates across fonts, fills, borders, numFmts, and cellXfs.
290
+ */
291
+ register(opts) {
292
+ const xf = {
293
+ fontId: this.registerFont(opts.font),
294
+ fillId: this.registerFill(opts.fill),
295
+ borderId: this.registerBorder(opts.border),
296
+ numFmtId: this.registerNumFmt(opts.numFmt),
297
+ alignment: opts.alignment
298
+ };
299
+ const key = this.cellXfKey(xf);
300
+ const existing = this.cellXfKeys.get(key);
301
+ if (existing !== void 0) return existing;
302
+ const idx = this.cellXfs.length;
303
+ this.cellXfs.push(xf);
304
+ this.cellXfKeys.set(key, idx);
305
+ return idx;
306
+ }
307
+ registerFont(opts) {
308
+ if (!opts) return 0;
309
+ const key = fontKey(opts);
310
+ const existing = this.fontKeys.get(key);
311
+ if (existing !== void 0) return existing;
312
+ const idx = this.fonts.length;
313
+ this.fonts.push(opts);
314
+ this.fontKeys.set(key, idx);
315
+ return idx;
316
+ }
317
+ registerFill(opts) {
318
+ if (!opts) return 0;
319
+ const key = fillKey(opts);
320
+ const existing = this.fillKeys.get(key);
321
+ if (existing !== void 0) return existing;
322
+ const idx = this.fills.length;
323
+ this.fills.push(opts);
324
+ this.fillKeys.set(key, idx);
325
+ return idx;
326
+ }
327
+ registerBorder(opts) {
328
+ if (!opts) return 0;
329
+ const key = borderKey(opts);
330
+ const existing = this.borderKeys.get(key);
331
+ if (existing !== void 0) return existing;
332
+ const idx = this.borders.length;
333
+ this.borders.push(opts);
334
+ this.borderKeys.set(key, idx);
335
+ return idx;
336
+ }
337
+ registerNumFmt(fmt) {
338
+ if (!fmt) return 0;
339
+ const builtin = BUILTIN_NUMFMTS[fmt];
340
+ if (builtin !== void 0) return builtin;
341
+ const existing = this.customNumFmts.get(fmt);
342
+ if (existing !== void 0) return existing;
343
+ const id = this.nextCustomNumFmtId++;
344
+ this.customNumFmts.set(fmt, id);
345
+ return id;
346
+ }
347
+ cellXfKey(xf) {
348
+ const a = xf.alignment;
349
+ const ak = a ? `h${a.horizontal ?? ""}v${a.vertical ?? ""}w${a.wrapText ? 1 : 0}r${a.textRotation ?? ""}i${a.indent ?? ""}` : "";
350
+ return `${xf.fontId}|${xf.fillId}|${xf.borderId}|${xf.numFmtId}|${ak}`;
351
+ }
352
+ /**
353
+ * Zero-allocation fast path: directly concatenate XML string.
354
+ * Bypasses the IXmlableObject intermediate tree entirely.
355
+ */
356
+ toXml(_context) {
357
+ const p = ["<styleSheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\">"];
358
+ if (this.customNumFmts.size > 0) {
359
+ p.push(`<numFmts count="${this.customNumFmts.size}">`);
360
+ for (const [fmt, id] of this.customNumFmts) p.push(`<numFmt numFmtId="${id}" formatCode="${escapeXml(fmt)}"/>`);
361
+ p.push("</numFmts>");
362
+ }
363
+ p.push(`<fonts count="${this.fonts.length}">`);
364
+ for (const f of this.fonts) p.push(`<font>${this.fontXmlStr(f)}</font>`);
365
+ p.push("</fonts>");
366
+ p.push(`<fills count="${this.fills.length}">`);
367
+ for (const f of this.fills) {
368
+ const patternAttrs = attrs({ patternType: f.patternType ?? "solid" });
369
+ const fgColor = f.color ? `<fgColor rgb="FF${f.color}"/>` : "";
370
+ p.push(fgColor ? `<fill><patternFill${patternAttrs}>${fgColor}</patternFill></fill>` : `<fill><patternFill${patternAttrs}/></fill>`);
371
+ }
372
+ p.push("</fills>");
373
+ p.push(`<borders count="${this.borders.length}">`);
374
+ for (const b of this.borders) p.push(`<border>${this.borderXmlStr(b)}</border>`);
375
+ p.push("</borders>");
376
+ p.push("<cellStyleXfs count=\"1\"><xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\"/></cellStyleXfs>");
377
+ p.push(`<cellXfs count="${this.cellXfs.length}">`);
378
+ for (const xf of this.cellXfs) {
379
+ const xAttrs = {
380
+ numFmtId: xf.numFmtId,
381
+ fontId: xf.fontId,
382
+ fillId: xf.fillId,
383
+ borderId: xf.borderId,
384
+ xfId: 0
385
+ };
386
+ if (xf.alignment) xAttrs.applyAlignment = 1;
387
+ if (xf.fontId > 0) xAttrs.applyFont = 1;
388
+ if (xf.fillId > 0) xAttrs.applyFill = 1;
389
+ if (xf.borderId > 0) xAttrs.applyBorder = 1;
390
+ const alignStr = xf.alignment ? this.alignmentXmlStr(xf.alignment) : "";
391
+ p.push(alignStr ? `<xf${attrs(xAttrs)}>${alignStr}</xf>` : `<xf${attrs(xAttrs)}/>`);
392
+ }
393
+ p.push("</cellXfs>");
394
+ p.push("<cellStyles count=\"1\"><cellStyle name=\"Normal\" xfId=\"0\" builtinId=\"0\"/></cellStyles>");
395
+ p.push("<dxfs count=\"0\"/>");
396
+ p.push("<tableStyles count=\"0\" defaultTableStyle=\"TableStyleMedium2\" defaultPivotStyle=\"PivotStyleLight16\"/>");
397
+ p.push("<extLst/>");
398
+ p.push("</styleSheet>");
399
+ return p.join("");
400
+ }
401
+ fontXmlStr(f) {
402
+ const parts = [];
403
+ if (f.bold) parts.push("<b/>");
404
+ if (f.italic) parts.push("<i/>");
405
+ if (f.underline) parts.push("<u/>");
406
+ if (f.strike) parts.push("<strike/>");
407
+ if (f.size) parts.push(`<sz val="${f.size}"/>`);
408
+ if (f.color) parts.push(`<color rgb="FF${f.color}"/>`);
409
+ if (f.fontName) parts.push(`<name val="${escapeXml(f.fontName)}"/>`);
410
+ return parts.join("");
411
+ }
412
+ borderXmlStr(b) {
413
+ const parts = [];
414
+ for (const side of [
415
+ "left",
416
+ "right",
417
+ "top",
418
+ "bottom",
419
+ "diagonal"
420
+ ]) {
421
+ const opts = b[side];
422
+ if (opts && opts.style && opts.style !== "none") {
423
+ const colorStr = opts.color ? `<color rgb="FF${opts.color}"/>` : "";
424
+ parts.push(`<${side} style="${opts.style}">${colorStr}</${side}>`);
425
+ } else parts.push(`<${side}/>`);
426
+ }
427
+ return parts.join("");
428
+ }
429
+ alignmentXmlStr(a) {
430
+ const aAttrs = {};
431
+ if (a.horizontal) aAttrs.horizontal = a.horizontal;
432
+ if (a.vertical) aAttrs.vertical = a.vertical;
433
+ if (a.wrapText) aAttrs.wrapText = 1;
434
+ if (a.textRotation !== void 0) aAttrs.textRotation = a.textRotation;
435
+ if (a.indent !== void 0) aAttrs.indent = a.indent;
436
+ return `<alignment${attrs(aAttrs)}/>`;
437
+ }
438
+ prepForXml(_context) {
439
+ const children = [{ _attr: { xmlns: "http://schemas.openxmlformats.org/spreadsheetml/2006/main" } }];
440
+ if (this.customNumFmts.size > 0) {
441
+ const fmtElements = [{ _attr: { count: this.customNumFmts.size } }];
442
+ for (const [fmt, id] of this.customNumFmts) fmtElements.push({ numFmt: { _attr: {
443
+ numFmtId: id,
444
+ formatCode: fmt
445
+ } } });
446
+ children.push({ numFmts: fmtElements });
447
+ }
448
+ children.push(this.buildFonts());
449
+ children.push(this.buildFills());
450
+ children.push(this.buildBorders());
451
+ children.push({ cellStyleXfs: [{ _attr: { count: 1 } }, { xf: [{ _attr: {
452
+ numFmtId: 0,
453
+ fontId: 0,
454
+ fillId: 0,
455
+ borderId: 0
456
+ } }] }] });
457
+ children.push(this.buildCellXfs());
458
+ children.push({ cellStyles: [{ _attr: { count: 1 } }, { cellStyle: [{ _attr: {
459
+ name: "Normal",
460
+ xfId: 0,
461
+ builtinId: 0
462
+ } }] }] });
463
+ children.push({ dxfs: { _attr: { count: 0 } } });
464
+ children.push({ tableStyles: { _attr: {
465
+ count: 0,
466
+ defaultTableStyle: "TableStyleMedium2",
467
+ defaultPivotStyle: "PivotStyleLight16"
468
+ } } });
469
+ children.push({ extLst: [] });
470
+ return { styleSheet: children };
471
+ }
472
+ buildFonts() {
473
+ const elements = [{ _attr: { count: this.fonts.length } }];
474
+ for (const f of this.fonts) elements.push({ font: this.fontXml(f) });
475
+ return { fonts: elements };
476
+ }
477
+ fontXml(f) {
478
+ const parts = [];
479
+ if (f.bold) parts.push({ b: [] });
480
+ if (f.italic) parts.push({ i: [] });
481
+ if (f.underline) parts.push({ u: [] });
482
+ if (f.strike) parts.push({ strike: [] });
483
+ if (f.size) parts.push({ sz: [{ _attr: { val: f.size } }] });
484
+ if (f.color) parts.push({ color: [{ _attr: { rgb: `FF${f.color}` } }] });
485
+ if (f.fontName) parts.push({ name: [{ _attr: { val: f.fontName } }] });
486
+ return parts;
487
+ }
488
+ buildFills() {
489
+ const elements = [{ _attr: { count: this.fills.length } }];
490
+ for (const f of this.fills) elements.push({ fill: [{ patternFill: [{ _attr: { patternType: f.patternType ?? "solid" } }, ...f.color ? [{ fgColor: [{ _attr: { rgb: `FF${f.color}` } }] }] : []] }] });
491
+ return { fills: elements };
492
+ }
493
+ buildBorders() {
494
+ const elements = [{ _attr: { count: this.borders.length } }];
495
+ for (const b of this.borders) elements.push({ border: this.borderXml(b) });
496
+ return { borders: elements };
497
+ }
498
+ borderXml(b) {
499
+ const parts = [];
500
+ for (const side of [
501
+ "left",
502
+ "right",
503
+ "top",
504
+ "bottom",
505
+ "diagonal"
506
+ ]) {
507
+ const opts = b[side];
508
+ if (opts && opts.style && opts.style !== "none") {
509
+ const children = [{ _attr: { style: opts.style } }];
510
+ if (opts.color) children.push({ color: [{ _attr: { rgb: `FF${opts.color}` } }] });
511
+ parts.push({ [side]: children });
512
+ } else parts.push({ [side]: [] });
513
+ }
514
+ return parts;
515
+ }
516
+ buildCellXfs() {
517
+ const elements = [{ _attr: { count: this.cellXfs.length } }];
518
+ for (const xf of this.cellXfs) {
519
+ const attrs = {
520
+ numFmtId: xf.numFmtId,
521
+ fontId: xf.fontId,
522
+ fillId: xf.fillId,
523
+ borderId: xf.borderId
524
+ };
525
+ if (xf.alignment) attrs.applyAlignment = 1;
526
+ if (xf.fontId > 0) attrs.applyFont = 1;
527
+ if (xf.fillId > 0) attrs.applyFill = 1;
528
+ if (xf.borderId > 0) attrs.applyBorder = 1;
529
+ const children = [{ _attr: attrs }];
530
+ if (xf.alignment) children.push(this.alignmentXml(xf.alignment));
531
+ elements.push({ xf: children });
532
+ }
533
+ return { cellXfs: elements };
534
+ }
535
+ alignmentXml(a) {
536
+ const attrs = {};
537
+ if (a.horizontal) attrs.horizontal = a.horizontal;
538
+ if (a.vertical) attrs.vertical = a.vertical;
539
+ if (a.wrapText) attrs.wrapText = 1;
540
+ if (a.textRotation !== void 0) attrs.textRotation = a.textRotation;
541
+ if (a.indent !== void 0) attrs.indent = a.indent;
542
+ return { alignment: [{ _attr: attrs }] };
543
+ }
544
+ };
545
+ //#endregion
546
+ //#region src/file/theme.ts
547
+ /**
548
+ * Default theme for XLSX files — matches Microsoft Office's output structure.
549
+ * Produces xl/theme/theme1.xml that Excel accepts without repair warnings.
550
+ *
551
+ * The theme XML is completely static — identical for every XLSX file.
552
+ * Pre-serialized as a string constant to avoid building the IXmlableObject
553
+ * tree and re-serializing on every compile.
554
+ *
555
+ * @module
556
+ */
557
+ const THEME_XML = "<a:theme xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\" name=\"Office Theme\"><a:themeElements><a:clrScheme name=\"Office\"><a:dk1><a:sysClr val=\"windowText\" lastClr=\"000000\"/></a:dk1><a:lt1><a:sysClr val=\"window\" lastClr=\"FFFFFF\"/></a:lt1><a:dk2><a:srgbClr val=\"44546A\"/></a:dk2><a:lt2><a:srgbClr val=\"E7E6E6\"/></a:lt2><a:accent1><a:srgbClr val=\"5B9BD5\"/></a:accent1><a:accent2><a:srgbClr val=\"ED7D31\"/></a:accent2><a:accent3><a:srgbClr val=\"A5A5A5\"/></a:accent3><a:accent4><a:srgbClr val=\"FFC000\"/></a:accent4><a:accent5><a:srgbClr val=\"4472C4\"/></a:accent5><a:accent6><a:srgbClr val=\"70AD47\"/></a:accent6><a:hlink><a:srgbClr val=\"0563C1\"/></a:hlink><a:folHlink><a:srgbClr val=\"954F72\"/></a:folHlink></a:clrScheme><a:fontScheme name=\"Office\"><a:majorFont><a:latin typeface=\"Calibri Light\" panose=\"020F0302020204030204\"/><a:ea typeface=\"\"/><a:cs typeface=\"\"/></a:majorFont><a:minorFont><a:latin typeface=\"Calibri\" panose=\"020F0502020204030204\"/><a:ea typeface=\"\"/><a:cs typeface=\"\"/></a:minorFont></a:fontScheme><a:fmtScheme name=\"Office\"><a:fillStyleLst><a:solidFill><a:schemeClr val=\"phClr\"/></a:solidFill><a:gradFill rotWithShape=\"1\"><a:gsLst><a:gs pos=\"0\"><a:schemeClr val=\"phClr\"><a:lumMod val=\"110000\"/><a:satMod val=\"105000\"/><a:tint val=\"67000\"/></a:schemeClr></a:gs><a:gs pos=\"50000\"><a:schemeClr val=\"phClr\"><a:lumMod val=\"105000\"/><a:satMod val=\"103000\"/><a:tint val=\"73000\"/></a:schemeClr></a:gs><a:gs pos=\"100000\"><a:schemeClr val=\"phClr\"><a:lumMod val=\"105000\"/><a:satMod val=\"109000\"/><a:tint val=\"81000\"/></a:schemeClr></a:gs></a:gsLst><a:lin ang=\"5400000\" scaled=\"0\"/></a:gradFill><a:gradFill rotWithShape=\"1\"><a:gsLst><a:gs pos=\"0\"><a:schemeClr val=\"phClr\"><a:lumMod val=\"102000\"/><a:satMod val=\"103000\"/><a:tint val=\"94000\"/></a:schemeClr></a:gs><a:gs pos=\"50000\"><a:schemeClr val=\"phClr\"><a:lumMod val=\"100000\"/><a:satMod val=\"110000\"/><a:shade val=\"100000\"/></a:schemeClr></a:gs><a:gs pos=\"100000\"><a:schemeClr val=\"phClr\"><a:lumMod val=\"99000\"/><a:satMod val=\"120000\"/><a:shade val=\"78000\"/></a:schemeClr></a:gs></a:gsLst><a:lin ang=\"5400000\" scaled=\"0\"/></a:gradFill></a:fillStyleLst><a:lnStyleLst><a:ln w=\"6350\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\"><a:solidFill><a:schemeClr val=\"phClr\"/></a:solidFill><a:prstDash val=\"solid\"/><a:miter lim=\"800000\"/></a:ln><a:ln w=\"12700\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\"><a:solidFill><a:schemeClr val=\"phClr\"/></a:solidFill><a:prstDash val=\"solid\"/><a:miter lim=\"800000\"/></a:ln><a:ln w=\"19050\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\"><a:solidFill><a:schemeClr val=\"phClr\"/></a:solidFill><a:prstDash val=\"solid\"/><a:miter lim=\"800000\"/></a:ln></a:lnStyleLst><a:effectStyleLst><a:effectStyle><a:effectLst/></a:effectStyle><a:effectStyle><a:effectLst/></a:effectStyle><a:effectStyle><a:effectLst><a:outerShdw blurRad=\"57150\" dist=\"19050\" dir=\"5400000\" algn=\"ctr\" rotWithShape=\"0\"><a:srgbClr val=\"000000\"><a:alpha val=\"63000\"/></a:srgbClr></a:outerShdw></a:effectLst></a:effectStyle></a:effectStyleLst><a:bgFillStyleLst><a:solidFill><a:schemeClr val=\"phClr\"/></a:solidFill><a:solidFill><a:schemeClr val=\"phClr\"><a:tint val=\"95000\"/><a:satMod val=\"170000\"/></a:schemeClr></a:solidFill><a:gradFill rotWithShape=\"1\"><a:gsLst><a:gs pos=\"0\"><a:schemeClr val=\"phClr\"><a:lumMod val=\"102000\"/><a:satMod val=\"150000\"/><a:tint val=\"93000\"/><a:shade val=\"98000\"/></a:schemeClr></a:gs><a:gs pos=\"50000\"><a:schemeClr val=\"phClr\"><a:lumMod val=\"103000\"/><a:satMod val=\"130000\"/><a:tint val=\"98000\"/><a:shade val=\"90000\"/></a:schemeClr></a:gs><a:gs pos=\"100000\"><a:schemeClr val=\"phClr\"><a:satMod val=\"120000\"/><a:shade val=\"63000\"/></a:schemeClr></a:gs></a:gsLst><a:lin ang=\"5400000\" scaled=\"0\"/></a:gradFill></a:bgFillStyleLst></a:fmtScheme></a:themeElements><a:objectDefaults/><a:extraClrSchemeLst/></a:theme>";
558
+ var DefaultTheme = class extends BaseXmlComponent {
559
+ constructor() {
560
+ super("a:theme");
561
+ }
562
+ /** Return pre-cached static theme XML — zero allocation. */
563
+ toXml(_context) {
564
+ return THEME_XML;
565
+ }
566
+ prepForXml(_context) {
567
+ return { "a:theme": [{ _attr: {
568
+ "xmlns:a": "http://schemas.openxmlformats.org/drawingml/2006/main",
569
+ name: "Office Theme"
570
+ } }] };
571
+ }
572
+ };
573
+ //#endregion
574
+ //#region src/file/workbook.ts
575
+ /**
576
+ * Workbook component — generates xl/workbook.xml.
577
+ *
578
+ * @module
579
+ */
580
+ var WorkbookXml = class extends BaseXmlComponent {
581
+ sheets;
582
+ constructor(sheets) {
583
+ super("workbook");
584
+ this.sheets = sheets;
585
+ }
586
+ prepForXml(_context) {
587
+ const sheetElements = [];
588
+ for (const s of this.sheets) {
589
+ const attrs = {
590
+ name: s.name,
591
+ sheetId: String(s.sheetId),
592
+ "r:id": s.rId
593
+ };
594
+ if (s.state && s.state !== "visible") attrs.state = s.state;
595
+ sheetElements.push({ sheet: { _attr: attrs } });
596
+ }
597
+ return { workbook: [
598
+ { _attr: {
599
+ xmlns: "http://schemas.openxmlformats.org/spreadsheetml/2006/main",
600
+ "xmlns:r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships"
601
+ } },
602
+ { bookViews: [{ workbookView: { _attr: {
603
+ xWindow: 0,
604
+ yWindow: 0,
605
+ windowWidth: 28800,
606
+ windowHeight: 12300
607
+ } } }] },
608
+ { sheets: sheetElements },
609
+ { calcPr: { _attr: { calcId: 191029 } } }
610
+ ] };
611
+ }
612
+ toXml(_context) {
613
+ const p = [
614
+ "<workbook xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\">",
615
+ "<bookViews><workbookView xWindow=\"0\" yWindow=\"0\" windowWidth=\"28800\" windowHeight=\"12300\"/></bookViews>",
616
+ "<sheets>"
617
+ ];
618
+ for (const s of this.sheets) {
619
+ const stateAttr = s.state && s.state !== "visible" ? ` state="${s.state}"` : "";
620
+ p.push(`<sheet name="${escapeXml(s.name)}" sheetId="${s.sheetId}" r:id="${s.rId}"${stateAttr}/>`);
621
+ }
622
+ p.push("</sheets><calcPr calcId=\"191029\"/></workbook>");
623
+ return p.join("");
624
+ }
625
+ };
626
+ //#endregion
627
+ //#region src/file/worksheet.ts
628
+ /**
629
+ * Worksheet component — generates xl/worksheets/sheet{n}.xml.
630
+ *
631
+ * @module
632
+ */
633
+ var Worksheet = class extends IgnoreIfEmptyXmlComponent {
634
+ rows;
635
+ columns;
636
+ mergeCells;
637
+ freezePanes;
638
+ autoFilter;
639
+ images;
640
+ chartOptions;
641
+ dataValidations;
642
+ conditionalFormats;
643
+ constructor(options) {
644
+ super("worksheet");
645
+ this.rows = options.children ?? [];
646
+ this.columns = options.columns ?? [];
647
+ this.mergeCells = options.mergeCells ?? [];
648
+ this.freezePanes = options.freezePanes;
649
+ this.autoFilter = options.autoFilter;
650
+ this.images = options.images ?? [];
651
+ this.chartOptions = options.charts ?? [];
652
+ this.dataValidations = options.dataValidations ?? [];
653
+ this.conditionalFormats = options.conditionalFormats ?? [];
654
+ }
655
+ get imageOptions() {
656
+ return this.images;
657
+ }
658
+ get charts() {
659
+ return this.chartOptions;
660
+ }
661
+ prepForXml(context) {
662
+ const fileData = context.fileData;
663
+ const sharedStrings = fileData?.sharedStrings;
664
+ const styles = fileData?.styles;
665
+ const children = [{ _attr: {
666
+ xmlns: "http://schemas.openxmlformats.org/spreadsheetml/2006/main",
667
+ "xmlns:r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships"
668
+ } }];
669
+ const maxRow = this.rows.length;
670
+ let maxCol = 0;
671
+ for (const row of this.rows) if (row.cells && row.cells.length > maxCol) maxCol = row.cells.length;
672
+ if (maxRow > 0 && maxCol > 0) {
673
+ const dimRef = `A1:${this.defaultCellRef(maxRow, maxCol)}`;
674
+ children.push({ dimension: { _attr: { ref: dimRef } } });
675
+ }
676
+ if (this.freezePanes) {
677
+ const fp = this.freezePanes;
678
+ const ySplit = fp.row ? fp.row : 0;
679
+ const xSplit = fp.col ? fp.col : 0;
680
+ const topRow = fp.row ? fp.row + 1 : 1;
681
+ const leftCol = fp.col ? fp.col + 1 : 1;
682
+ const attr = {
683
+ ySplit,
684
+ xSplit,
685
+ topLeftCell: this.defaultCellRef(topRow, leftCol),
686
+ activePane: ySplit > 0 && xSplit > 0 ? "bottomRight" : ySplit > 0 ? "bottomLeft" : "topRight",
687
+ state: "frozen"
688
+ };
689
+ children.push({ sheetViews: [{ sheetView: [{ _attr: {
690
+ tabSelected: 1,
691
+ workbookViewId: 0
692
+ } }, { pane: { _attr: attr } }] }] });
693
+ } else children.push({ sheetViews: [{ sheetView: [{ _attr: {
694
+ tabSelected: 1,
695
+ workbookViewId: 0
696
+ } }] }] });
697
+ children.push({ sheetFormatPr: { _attr: { defaultRowHeight: 15 } } });
698
+ if (this.columns.length > 0) {
699
+ const colElements = [];
700
+ for (const col of this.columns) {
701
+ const colAttrs = {
702
+ min: col.min,
703
+ max: col.max
704
+ };
705
+ if (col.width !== void 0) {
706
+ colAttrs.width = col.width;
707
+ colAttrs.customWidth = 1;
708
+ }
709
+ if (col.hidden) colAttrs.hidden = 1;
710
+ colElements.push({ col: { _attr: colAttrs } });
711
+ }
712
+ children.push({ cols: colElements });
713
+ }
714
+ const sheetDataChildren = [];
715
+ for (let i = 0; i < this.rows.length; i++) {
716
+ const rowOpts = this.rows[i];
717
+ const rowNumber = rowOpts.rowNumber ?? i + 1;
718
+ const rowAttrs = { r: rowNumber };
719
+ if (rowOpts.height !== void 0) {
720
+ rowAttrs.ht = rowOpts.height;
721
+ rowAttrs.customHeight = 1;
722
+ }
723
+ if (rowOpts.hidden) rowAttrs.hidden = 1;
724
+ const cellElements = [];
725
+ if (rowOpts.cells) for (let j = 0; j < rowOpts.cells.length; j++) {
726
+ const cell = rowOpts.cells[j];
727
+ const ref = cell.reference ?? this.defaultCellRef(rowNumber, j + 1);
728
+ const cellObj = this.buildCell(ref, cell, sharedStrings, styles);
729
+ if (cellObj) cellElements.push(cellObj);
730
+ }
731
+ sheetDataChildren.push({ row: [{ _attr: rowAttrs }, ...cellElements] });
732
+ }
733
+ children.push({ sheetData: sheetDataChildren });
734
+ if (this.autoFilter) children.push({ autoFilter: { _attr: { ref: this.autoFilter } } });
735
+ if (this.mergeCells.length > 0) {
736
+ const mergeElements = [{ _attr: { count: this.mergeCells.length } }];
737
+ for (const mc of this.mergeCells) {
738
+ const fromRef = this.defaultCellRef(mc.from.row, mc.from.col);
739
+ const toRef = this.defaultCellRef(mc.to.row, mc.to.col);
740
+ mergeElements.push({ mergeCell: { _attr: { ref: `${fromRef}:${toRef}` } } });
741
+ }
742
+ children.push({ mergeCells: mergeElements });
743
+ }
744
+ if (this.conditionalFormats.length > 0) for (const cf of this.conditionalFormats) {
745
+ const rules = [];
746
+ for (let ri = 0; ri < cf.rules.length; ri++) {
747
+ const rule = cf.rules[ri];
748
+ const ruleAttrs = {
749
+ type: rule.type,
750
+ priority: rule.priority ?? ri + 1
751
+ };
752
+ if (rule.operator) ruleAttrs.operator = rule.operator;
753
+ if (rule.dxfId !== void 0) ruleAttrs.dxfId = rule.dxfId;
754
+ const ruleChildren = [{ _attr: ruleAttrs }];
755
+ if (rule.formulas) for (const f of rule.formulas) ruleChildren.push({ formula: [f] });
756
+ rules.push({ cfRule: ruleChildren });
757
+ }
758
+ children.push({ conditionalFormatting: [{ _attr: { sqref: cf.sqref } }, ...rules] });
759
+ }
760
+ if (this.dataValidations.length > 0) {
761
+ const dvElements = [{ _attr: { count: this.dataValidations.length } }];
762
+ for (const dv of this.dataValidations) {
763
+ const dvAttrs = { sqref: dv.sqref };
764
+ if (dv.type && dv.type !== "none") dvAttrs.type = dv.type;
765
+ if (dv.operator) dvAttrs.operator = dv.operator;
766
+ if (dv.allowBlank) dvAttrs.allowBlank = 1;
767
+ if (dv.showErrorMessage) dvAttrs.showErrorMessage = 1;
768
+ if (dv.showInputMessage) dvAttrs.showInputMessage = 1;
769
+ if (dv.errorTitle) dvAttrs.errorTitle = dv.errorTitle;
770
+ if (dv.error) dvAttrs.error = dv.error;
771
+ if (dv.promptTitle) dvAttrs.promptTitle = dv.promptTitle;
772
+ if (dv.prompt) dvAttrs.prompt = dv.prompt;
773
+ const dvChildren = [{ _attr: dvAttrs }];
774
+ if (dv.formula1 !== void 0) dvChildren.push({ formula1: [dv.formula1] });
775
+ if (dv.formula2 !== void 0) dvChildren.push({ formula2: [dv.formula2] });
776
+ dvElements.push({ dataValidation: dvChildren });
777
+ }
778
+ children.push({ dataValidations: dvElements });
779
+ }
780
+ children.push({ pageMargins: { _attr: {
781
+ left: .75,
782
+ right: .75,
783
+ top: 1,
784
+ bottom: 1,
785
+ header: .5,
786
+ footer: .5
787
+ } } });
788
+ return { worksheet: children };
789
+ }
790
+ /**
791
+ * Zero-allocation fast path: directly concatenate XML string.
792
+ * Bypasses the IXmlableObject intermediate tree entirely.
793
+ */
794
+ toXml(context) {
795
+ const fileData = context.fileData;
796
+ const sharedStrings = fileData?.sharedStrings;
797
+ const styles = fileData?.styles;
798
+ const p = ["<worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\">"];
799
+ const maxRow = this.rows.length;
800
+ let maxCol = 0;
801
+ for (const row of this.rows) if (row.cells && row.cells.length > maxCol) maxCol = row.cells.length;
802
+ if (maxRow > 0 && maxCol > 0) {
803
+ const dimRef = `A1:${this.defaultCellRef(maxRow, maxCol)}`;
804
+ p.push(`<dimension ref="${dimRef}"/>`);
805
+ }
806
+ if (this.freezePanes) {
807
+ const fp = this.freezePanes;
808
+ const ySplit = fp.row ? fp.row : 0;
809
+ const xSplit = fp.col ? fp.col : 0;
810
+ const topRow = fp.row ? fp.row + 1 : 1;
811
+ const leftCol = fp.col ? fp.col + 1 : 1;
812
+ const topLeftCell = this.defaultCellRef(topRow, leftCol);
813
+ const activePane = ySplit > 0 && xSplit > 0 ? "bottomRight" : ySplit > 0 ? "bottomLeft" : "topRight";
814
+ p.push("<sheetViews><sheetView tabSelected=\"1\" workbookViewId=\"0\">", `<pane ySplit="${ySplit}" xSplit="${xSplit}" topLeftCell="${topLeftCell}" activePane="${activePane}" state="frozen"/>`, "</sheetView></sheetViews>");
815
+ } else p.push("<sheetViews><sheetView tabSelected=\"1\" workbookViewId=\"0\"/></sheetViews>");
816
+ p.push("<sheetFormatPr defaultRowHeight=\"15\"/>");
817
+ if (this.columns.length > 0) {
818
+ p.push("<cols>");
819
+ for (const col of this.columns) {
820
+ const colAttrs = {
821
+ min: col.min,
822
+ max: col.max
823
+ };
824
+ if (col.width !== void 0) {
825
+ colAttrs.width = col.width;
826
+ colAttrs.customWidth = 1;
827
+ }
828
+ if (col.hidden) colAttrs.hidden = 1;
829
+ p.push(selfCloseElement("col", attrs(colAttrs)));
830
+ }
831
+ p.push("</cols>");
832
+ }
833
+ p.push("<sheetData>");
834
+ for (let i = 0; i < this.rows.length; i++) {
835
+ const rowOpts = this.rows[i];
836
+ const rowNumber = rowOpts.rowNumber ?? i + 1;
837
+ const rowAttrs = { r: rowNumber };
838
+ if (rowOpts.height !== void 0) {
839
+ rowAttrs.ht = rowOpts.height;
840
+ rowAttrs.customHeight = 1;
841
+ }
842
+ if (rowOpts.hidden) rowAttrs.hidden = 1;
843
+ if (rowOpts.cells) {
844
+ const rowParts = [];
845
+ for (let j = 0; j < rowOpts.cells.length; j++) {
846
+ const cell = rowOpts.cells[j];
847
+ const ref = cell.reference ?? this.defaultCellRef(rowNumber, j + 1);
848
+ const cellStr = this.buildCellString(ref, cell, sharedStrings, styles);
849
+ if (cellStr) rowParts.push(cellStr);
850
+ }
851
+ p.push(`<row${attrs(rowAttrs)}>`, ...rowParts, "</row>");
852
+ } else p.push(`<row${attrs(rowAttrs)}/>`);
853
+ }
854
+ p.push("</sheetData>");
855
+ if (this.autoFilter) p.push(selfCloseElement("autoFilter", attrs({ ref: this.autoFilter })));
856
+ if (this.mergeCells.length > 0) {
857
+ p.push(`<mergeCells count="${this.mergeCells.length}">`);
858
+ for (const mc of this.mergeCells) {
859
+ const fromRef = this.defaultCellRef(mc.from.row, mc.from.col);
860
+ const toRef = this.defaultCellRef(mc.to.row, mc.to.col);
861
+ p.push(selfCloseElement("mergeCell", attrs({ ref: `${fromRef}:${toRef}` })));
862
+ }
863
+ p.push("</mergeCells>");
864
+ }
865
+ if (this.conditionalFormats.length > 0) for (const cf of this.conditionalFormats) {
866
+ p.push(`<conditionalFormatting sqref="${cf.sqref}">`);
867
+ for (let ri = 0; ri < cf.rules.length; ri++) {
868
+ const rule = cf.rules[ri];
869
+ const ruleAttrs = {
870
+ type: rule.type,
871
+ priority: rule.priority ?? ri + 1
872
+ };
873
+ if (rule.operator) ruleAttrs.operator = rule.operator;
874
+ if (rule.dxfId !== void 0) ruleAttrs.dxfId = rule.dxfId;
875
+ if (rule.formulas && rule.formulas.length > 0) {
876
+ const formulaParts = rule.formulas.map((f) => `<formula>${escapeXml(f)}</formula>`);
877
+ p.push(`<cfRule${attrs(ruleAttrs)}>`, ...formulaParts, "</cfRule>");
878
+ } else p.push(selfCloseElement("cfRule", attrs(ruleAttrs)));
879
+ }
880
+ p.push("</conditionalFormatting>");
881
+ }
882
+ if (this.dataValidations.length > 0) {
883
+ p.push(`<dataValidations count="${this.dataValidations.length}">`);
884
+ for (const dv of this.dataValidations) {
885
+ const dvAttrs = { sqref: dv.sqref };
886
+ if (dv.type && dv.type !== "none") dvAttrs.type = dv.type;
887
+ if (dv.operator) dvAttrs.operator = dv.operator;
888
+ if (dv.allowBlank) dvAttrs.allowBlank = 1;
889
+ if (dv.showErrorMessage) dvAttrs.showErrorMessage = 1;
890
+ if (dv.showInputMessage) dvAttrs.showInputMessage = 1;
891
+ if (dv.errorTitle) dvAttrs.errorTitle = dv.errorTitle;
892
+ if (dv.error) dvAttrs.error = dv.error;
893
+ if (dv.promptTitle) dvAttrs.promptTitle = dv.promptTitle;
894
+ if (dv.prompt) dvAttrs.prompt = dv.prompt;
895
+ const inner = [];
896
+ if (dv.formula1 !== void 0) inner.push(`<formula1>${escapeXml(dv.formula1)}</formula1>`);
897
+ if (dv.formula2 !== void 0) inner.push(`<formula2>${escapeXml(dv.formula2)}</formula2>`);
898
+ if (inner.length > 0) p.push(`<dataValidation${attrs(dvAttrs)}>`, ...inner, "</dataValidation>");
899
+ else p.push(selfCloseElement("dataValidation", attrs(dvAttrs)));
900
+ }
901
+ p.push("</dataValidations>");
902
+ }
903
+ p.push("<pageMargins left=\"0.75\" right=\"0.75\" top=\"1\" bottom=\"1\" header=\"0.5\" footer=\"0.5\"/>");
904
+ p.push("</worksheet>");
905
+ return p.join("");
906
+ }
907
+ /**
908
+ * Direct string serialization of a single cell — zero intermediate objects.
909
+ */
910
+ buildCellString(ref, cell, sharedStrings, styles) {
911
+ const cellAttrs = { r: ref };
912
+ if (cell.style !== void 0 && styles) cellAttrs.s = styles.register(cell.style);
913
+ else if (cell.styleIndex !== void 0) cellAttrs.s = cell.styleIndex;
914
+ const value = cell.value;
915
+ if (value === null || value === void 0) {
916
+ if (cell.styleIndex !== void 0) return selfCloseElement("c", attrs(cellAttrs));
917
+ return "";
918
+ }
919
+ if (typeof value === "string") {
920
+ if (sharedStrings) {
921
+ cellAttrs.t = "s";
922
+ const idx = sharedStrings.register(value);
923
+ return `<c${attrs(cellAttrs)}><v>${idx}</v></c>`;
924
+ }
925
+ cellAttrs.t = "inlineStr";
926
+ return `<c${attrs(cellAttrs)}><is><t>${escapeXml(value)}</t></is></c>`;
927
+ }
928
+ if (typeof value === "number") return `<c${attrs(cellAttrs)}><v>${value}</v></c>`;
929
+ if (typeof value === "boolean") {
930
+ cellAttrs.t = "b";
931
+ return `<c${attrs(cellAttrs)}><v>${value ? 1 : 0}</v></c>`;
932
+ }
933
+ if (value instanceof Date) {
934
+ const serial = this.dateToSerialNumber(value);
935
+ return `<c${attrs(cellAttrs)}><v>${serial}</v></c>`;
936
+ }
937
+ return "";
938
+ }
939
+ defaultCellRef(row, col) {
940
+ return this.columnToLetter(col) + row;
941
+ }
942
+ columnToLetter(col) {
943
+ let result = "";
944
+ let n = col;
945
+ while (n > 0) {
946
+ const remainder = (n - 1) % 26;
947
+ result = String.fromCharCode(65 + remainder) + result;
948
+ n = Math.floor((n - 1) / 26);
949
+ }
950
+ return result;
951
+ }
952
+ buildCell(ref, cell, sharedStrings, styles) {
953
+ const attrs = { r: ref };
954
+ if (cell.style !== void 0 && styles) attrs.s = styles.register(cell.style);
955
+ else if (cell.styleIndex !== void 0) attrs.s = cell.styleIndex;
956
+ const value = cell.value;
957
+ if (value === null || value === void 0) {
958
+ if (cell.styleIndex !== void 0) return { c: [{ _attr: attrs }] };
959
+ return;
960
+ }
961
+ if (typeof value === "string") {
962
+ if (sharedStrings) {
963
+ attrs.t = "s";
964
+ const idx = sharedStrings.register(value);
965
+ return { c: [{ _attr: attrs }, { v: [idx] }] };
966
+ }
967
+ attrs.t = "inlineStr";
968
+ return { c: [{ _attr: attrs }, { is: [{ t: [value] }] }] };
969
+ }
970
+ if (typeof value === "number") return { c: [{ _attr: attrs }, { v: [value] }] };
971
+ if (typeof value === "boolean") {
972
+ attrs.t = "b";
973
+ return { c: [{ _attr: attrs }, { v: [value ? 1 : 0] }] };
974
+ }
975
+ if (value instanceof Date) {
976
+ const serial = this.dateToSerialNumber(value);
977
+ return { c: [{ _attr: attrs }, { v: [serial] }] };
978
+ }
979
+ }
980
+ dateToSerialNumber(date) {
981
+ const epoch = new Date(1899, 11, 30);
982
+ return (date.getTime() - epoch.getTime()) / 864e5;
983
+ }
984
+ };
985
+ //#endregion
986
+ //#region src/file/file.ts
987
+ /**
988
+ * File class (exported as Workbook) — the top-level container for XLSX documents.
989
+ *
990
+ * @module
991
+ */
992
+ var File = class {
993
+ worksheetOptions;
994
+ corePropsOptions;
995
+ _coreProperties;
996
+ _appProperties;
997
+ _contentTypes;
998
+ _styles;
999
+ _theme;
1000
+ _workbookXml;
1001
+ _worksheets;
1002
+ _sharedStrings;
1003
+ _media;
1004
+ _fileRels;
1005
+ _workbookRels;
1006
+ _charts;
1007
+ constructor(options) {
1008
+ this.worksheetOptions = options.worksheets ?? [];
1009
+ this.corePropsOptions = options;
1010
+ }
1011
+ get coreProperties() {
1012
+ return this._coreProperties ??= new CoreProperties(this.corePropsOptions);
1013
+ }
1014
+ get appProperties() {
1015
+ return this._appProperties ??= new AppProperties();
1016
+ }
1017
+ get contentTypes() {
1018
+ if (!this._contentTypes) {
1019
+ this._contentTypes = new ContentTypes();
1020
+ for (let i = 0; i < this.worksheetOptions.length; i++) this._contentTypes.addWorksheet(i + 1);
1021
+ this._contentTypes.addStyles();
1022
+ this._contentTypes.addSharedStrings();
1023
+ this._contentTypes.addTheme();
1024
+ }
1025
+ return this._contentTypes;
1026
+ }
1027
+ get styles() {
1028
+ return this._styles ??= new Styles();
1029
+ }
1030
+ get theme() {
1031
+ return this._theme ??= new DefaultTheme();
1032
+ }
1033
+ get workbookXml() {
1034
+ if (!this._workbookXml) {
1035
+ const sheets = this.worksheetOptions.map((ws, i) => ({
1036
+ name: ws.name ?? `Sheet${i + 1}`,
1037
+ sheetId: i + 1,
1038
+ rId: `rId${i + 1}`
1039
+ }));
1040
+ this._workbookXml = new WorkbookXml(sheets);
1041
+ }
1042
+ return this._workbookXml;
1043
+ }
1044
+ get sharedStrings() {
1045
+ return this._sharedStrings ??= new SharedStrings();
1046
+ }
1047
+ get media() {
1048
+ return this._media ??= new Media();
1049
+ }
1050
+ get charts() {
1051
+ return this._charts ??= new ChartCollection();
1052
+ }
1053
+ get worksheets() {
1054
+ if (!this._worksheets) this._worksheets = this.worksheetOptions.map((ws) => new Worksheet(ws));
1055
+ return this._worksheets;
1056
+ }
1057
+ get fileRelationships() {
1058
+ if (!this._fileRels) {
1059
+ this._fileRels = new Relationships();
1060
+ this._fileRels.addRelationship(1, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument", "xl/workbook.xml");
1061
+ this._fileRels.addRelationship(2, "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties", "docProps/core.xml");
1062
+ this._fileRels.addRelationship(3, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties", "docProps/app.xml");
1063
+ }
1064
+ return this._fileRels;
1065
+ }
1066
+ get workbookRelationships() {
1067
+ if (!this._workbookRels) {
1068
+ this._workbookRels = new Relationships();
1069
+ let rid = 1;
1070
+ for (let i = 0; i < this.worksheetOptions.length; i++) this._workbookRels.addRelationship(rid++, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet", `worksheets/sheet${i + 1}.xml`);
1071
+ this._workbookRels.addRelationship(rid++, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles", "styles.xml");
1072
+ this._workbookRels.addRelationship(rid++, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme", "theme/theme1.xml");
1073
+ this._workbookRels.addRelationship(rid++, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings", "sharedStrings.xml");
1074
+ }
1075
+ return this._workbookRels;
1076
+ }
1077
+ };
1078
+ //#endregion
1079
+ //#region src/file/drawing/drawing.ts
1080
+ /**
1081
+ * XLSX Drawing component — generates xl/drawings/drawing{n}.xml.
1082
+ *
1083
+ * Uses the spreadsheetDrawing namespace (default, no prefix) for anchoring
1084
+ * images and charts to worksheet cells.
1085
+ *
1086
+ * @module
1087
+ */
1088
+ const XDR_NS = "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing";
1089
+ const A_NS = "http://schemas.openxmlformats.org/drawingml/2006/main";
1090
+ const R_NS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
1091
+ const C_URI = "http://schemas.openxmlformats.org/drawingml/2006/chart";
1092
+ var Drawing = class extends BaseXmlComponent {
1093
+ images;
1094
+ charts;
1095
+ constructor(images, charts = []) {
1096
+ super("wsDr");
1097
+ this.images = images;
1098
+ this.charts = charts;
1099
+ }
1100
+ prepForXml(_context) {
1101
+ const children = [{ _attr: {
1102
+ xmlns: XDR_NS,
1103
+ "xmlns:a": A_NS,
1104
+ "xmlns:r": R_NS
1105
+ } }];
1106
+ let nextId = 1;
1107
+ for (const img of this.images) children.push(this.buildImageAnchor(img, nextId++));
1108
+ for (const chart of this.charts) children.push(this.buildChartAnchor(chart, nextId++));
1109
+ return { wsDr: children };
1110
+ }
1111
+ buildFromAnchor(col, row, colOffset, rowOffset) {
1112
+ return { from: [
1113
+ { col: [col - 1] },
1114
+ { colOff: [colOffset ?? 0] },
1115
+ { row: [row - 1] },
1116
+ { rowOff: [rowOffset ?? 0] }
1117
+ ] };
1118
+ }
1119
+ buildToAnchor(col, row) {
1120
+ return { to: [
1121
+ { col: [col] },
1122
+ { colOff: [0] },
1123
+ { row: [row] },
1124
+ { rowOff: [0] }
1125
+ ] };
1126
+ }
1127
+ toXml(_context) {
1128
+ const p = [`<wsDr xmlns="${XDR_NS}" xmlns:a="${A_NS}" xmlns:r="${R_NS}">`];
1129
+ let id = 1;
1130
+ for (const img of this.images) {
1131
+ p.push(`<twoCellAnchor editAs="oneCell"><from><col>${img.col - 1}</col><colOff>${img.colOffset ?? 0}</colOff><row>${img.row - 1}</row><rowOff>${img.rowOffset ?? 0}</rowOff></from>`, `<to><col>${img.col}</col><colOff>0</colOff><row>${img.row}</row><rowOff>0</rowOff></to>`, `<pic><nvPicPr><cNvPr id="${id}" name="Picture ${id}"/><cNvPicPr preferRelativeResize="1"/></nvPicPr>`, `<blipFill><a:blip r:embed="${img.rId}"/><a:stretch><a:fillRect/></a:stretch></blipFill>`, `<spPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="400000" cy="300000"/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom></spPr></pic>`, `<clientData/></twoCellAnchor>`);
1132
+ id++;
1133
+ }
1134
+ for (const chart of this.charts) {
1135
+ p.push(`<twoCellAnchor editAs="oneCell"><from><col>${chart.col - 1}</col><colOff>${chart.colOffset ?? 0}</colOff><row>${chart.row - 1}</row><rowOff>${chart.rowOffset ?? 0}</rowOff></from>`, `<to><col>${chart.col + 8}</col><colOff>0</colOff><row>${chart.row + 15}</row><rowOff>0</rowOff></to>`, `<graphicFrame><nvGraphicFramePr><cNvPr id="${id}" name="Chart ${id}"/><cNvGraphicFramePr><a:graphicFrameLocks noGrp="1"/></cNvGraphicFramePr></nvGraphicFramePr>`, `<xfrm><a:off x="0" y="0"/><a:ext cx="0" cy="0"/></xfrm>`, `<a:graphic><a:graphicData uri="${C_URI}"><c:chart xmlns:c="http://schemas.openxmlformats.org/drawingml/2006/chart" xmlns:r="${R_NS}" r:id="${chart.rId}"/></a:graphicData></a:graphic></graphicFrame>`, `<clientData/></twoCellAnchor>`);
1136
+ id++;
1137
+ }
1138
+ p.push("</wsDr>");
1139
+ return p.join("");
1140
+ }
1141
+ buildImageAnchor(img, id) {
1142
+ return { twoCellAnchor: [
1143
+ { _attr: { editAs: "oneCell" } },
1144
+ this.buildFromAnchor(img.col, img.row, img.colOffset, img.rowOffset),
1145
+ this.buildToAnchor(img.col, img.row),
1146
+ { pic: [
1147
+ { nvPicPr: [{ cNvPr: { _attr: {
1148
+ id,
1149
+ name: `Picture ${id}`
1150
+ } } }, { cNvPicPr: [{ _attr: { preferRelativeResize: 1 } }] }] },
1151
+ { blipFill: [{ "a:blip": { _attr: { "r:embed": img.rId } } }, { "a:stretch": [{ "a:fillRect": [] }] }] },
1152
+ { spPr: [{ "a:xfrm": [{ "a:off": { _attr: {
1153
+ x: 0,
1154
+ y: 0
1155
+ } } }, { "a:ext": { _attr: {
1156
+ cx: 4e5,
1157
+ cy: 3e5
1158
+ } } }] }, { "a:prstGeom": [{ _attr: { prst: "rect" } }, { "a:avLst": [] }] }] }
1159
+ ] },
1160
+ { clientData: [] }
1161
+ ] };
1162
+ }
1163
+ buildChartAnchor(chart, id) {
1164
+ return { twoCellAnchor: [
1165
+ { _attr: { editAs: "oneCell" } },
1166
+ this.buildFromAnchor(chart.col, chart.row, chart.colOffset, chart.rowOffset),
1167
+ this.buildToAnchor(chart.col + 8, chart.row + 15),
1168
+ { graphicFrame: [
1169
+ { nvGraphicFramePr: [{ cNvPr: { _attr: {
1170
+ id,
1171
+ name: `Chart ${id}`
1172
+ } } }, { cNvGraphicFramePr: [{ "a:graphicFrameLocks": { _attr: { noGrp: 1 } } }] }] },
1173
+ { xfrm: [{ "a:off": { _attr: {
1174
+ x: 0,
1175
+ y: 0
1176
+ } } }, { "a:ext": { _attr: {
1177
+ cx: 0,
1178
+ cy: 0
1179
+ } } }] },
1180
+ { "a:graphic": [{ "a:graphicData": [{ _attr: { uri: C_URI } }, { "c:chart": { _attr: {
1181
+ "xmlns:c": "http://schemas.openxmlformats.org/drawingml/2006/chart",
1182
+ "xmlns:r": R_NS,
1183
+ "r:id": chart.rId
1184
+ } } }] }] }
1185
+ ] },
1186
+ { clientData: [] }
1187
+ ] };
1188
+ }
1189
+ };
1190
+ //#endregion
1191
+ //#region src/export/packer/next-compiler.ts
1192
+ /**
1193
+ * XLSX Compiler — compiles a File object into a Zippable structure.
1194
+ *
1195
+ * @module
1196
+ */
1197
+ var Compiler = class {
1198
+ formatter = new Formatter();
1199
+ compile(file, overrides = []) {
1200
+ const context = {
1201
+ fileData: file,
1202
+ stack: []
1203
+ };
1204
+ const f = this.formatter;
1205
+ const mapping = {};
1206
+ const fmt = (component) => f.formatToXml(component, context);
1207
+ mapping["Properties"] = {
1208
+ data: fmt(file.coreProperties),
1209
+ path: "docProps/core.xml"
1210
+ };
1211
+ mapping["AppProperties"] = {
1212
+ data: fmt(file.appProperties),
1213
+ path: "docProps/app.xml"
1214
+ };
1215
+ mapping["FileRelationships"] = {
1216
+ data: fmt(file.fileRelationships),
1217
+ path: "_rels/.rels"
1218
+ };
1219
+ mapping["Workbook"] = {
1220
+ data: fmt(file.workbookXml),
1221
+ path: "xl/workbook.xml"
1222
+ };
1223
+ mapping["WorkbookRelationships"] = {
1224
+ data: fmt(file.workbookRelationships),
1225
+ path: "xl/_rels/workbook.xml.rels"
1226
+ };
1227
+ const worksheets = file.worksheets;
1228
+ let globalMediaIdx = 0;
1229
+ let globalChartIdx = 0;
1230
+ for (let i = 0; i < worksheets.length; i++) {
1231
+ const ws = worksheets[i];
1232
+ const imgOpts = ws.imageOptions;
1233
+ const chartOpts = ws.charts;
1234
+ let sheetXml = fmt(ws);
1235
+ if (imgOpts.length > 0 || chartOpts.length > 0) {
1236
+ const drawingImages = [];
1237
+ const drawingCharts = [];
1238
+ const drawingRels = new Relationships();
1239
+ let rid = 1;
1240
+ for (const img of imgOpts) {
1241
+ const mediaKey = `image_${globalMediaIdx}`;
1242
+ const ext = img.type === "jpeg" || img.type === "jpg" ? "jpeg" : "png";
1243
+ file.media.addImage(mediaKey, {
1244
+ fileName: `image${globalMediaIdx + 1}.${ext}`,
1245
+ type: ext,
1246
+ data: img.data,
1247
+ width: 0,
1248
+ height: 0
1249
+ });
1250
+ drawingRels.addRelationship(rid, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", `../media/image${globalMediaIdx + 1}.${ext}`);
1251
+ drawingImages.push({
1252
+ col: img.col,
1253
+ row: img.row,
1254
+ rId: `rId${rid}`
1255
+ });
1256
+ rid++;
1257
+ globalMediaIdx++;
1258
+ }
1259
+ for (const chart of chartOpts) {
1260
+ const chartKey = `chart_${globalChartIdx}`;
1261
+ file.charts.addChart(chartKey, {
1262
+ key: chartKey,
1263
+ chartSpace: new ChartSpace(chart)
1264
+ });
1265
+ drawingRels.addRelationship(rid, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart", `../charts/chart${globalChartIdx + 1}.xml`);
1266
+ drawingCharts.push({
1267
+ col: chart.col,
1268
+ row: chart.row,
1269
+ rId: `rId${rid}`
1270
+ });
1271
+ rid++;
1272
+ globalChartIdx++;
1273
+ }
1274
+ const drawing = new Drawing(drawingImages, drawingCharts);
1275
+ const drawingIdx = i + 1;
1276
+ mapping[`Drawing${i}`] = {
1277
+ data: fmt(drawing),
1278
+ path: `xl/drawings/drawing${drawingIdx}.xml`
1279
+ };
1280
+ mapping[`DrawingRels${i}`] = {
1281
+ data: fmt(drawingRels),
1282
+ path: `xl/drawings/_rels/drawing${drawingIdx}.xml.rels`
1283
+ };
1284
+ sheetXml = sheetXml.slice(0, -12) + `<drawing r:id="rId${rid}"/></worksheet>`;
1285
+ const wsRels = new Relationships();
1286
+ wsRels.addRelationship(rid, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing", `../drawings/drawing${drawingIdx}.xml`);
1287
+ mapping[`WorksheetRels${i}`] = {
1288
+ data: fmt(wsRels),
1289
+ path: `xl/worksheets/_rels/sheet${i + 1}.xml.rels`
1290
+ };
1291
+ file.contentTypes.addDrawing(drawingIdx);
1292
+ }
1293
+ mapping[`Worksheet${i}`] = {
1294
+ data: sheetXml,
1295
+ path: `xl/worksheets/sheet${i + 1}.xml`
1296
+ };
1297
+ }
1298
+ const sharedStrings = file.sharedStrings;
1299
+ if (sharedStrings.count > 0) mapping["SharedStrings"] = {
1300
+ data: fmt(sharedStrings),
1301
+ path: "xl/sharedStrings.xml"
1302
+ };
1303
+ mapping["Styles"] = {
1304
+ data: fmt(file.styles),
1305
+ path: "xl/styles.xml"
1306
+ };
1307
+ mapping["Theme"] = {
1308
+ data: fmt(file.theme),
1309
+ path: "xl/theme/theme1.xml"
1310
+ };
1311
+ for (let i = 0; i < file.charts.array.length; i++) {
1312
+ const chartData = file.charts.array[i];
1313
+ mapping[`Chart${i}`] = {
1314
+ data: fmt(chartData.chartSpace),
1315
+ path: `xl/charts/chart${i + 1}.xml`
1316
+ };
1317
+ file.contentTypes.addChart(i + 1);
1318
+ }
1319
+ const imageExts = /* @__PURE__ */ new Set();
1320
+ for (const img of file.media.array) {
1321
+ const ext = img.fileName.endsWith(".png") ? "png" : "jpeg";
1322
+ if (!imageExts.has(ext)) {
1323
+ imageExts.add(ext);
1324
+ file.contentTypes.addImageType(ext);
1325
+ }
1326
+ }
1327
+ mapping["ContentTypes"] = {
1328
+ data: fmt(file.contentTypes),
1329
+ path: "[Content_Types].xml"
1330
+ };
1331
+ const mediaFiles = [];
1332
+ for (const img of file.media.array) mediaFiles.push({
1333
+ data: img.data,
1334
+ path: `xl/media/${img.fileName}`
1335
+ });
1336
+ return compileMapping(mapping, overrides, mediaFiles);
1337
+ }
1338
+ };
1339
+ //#endregion
1340
+ //#region src/export/packer/packer.ts
1341
+ /**
1342
+ * Packer module — export API for XLSX files.
1343
+ *
1344
+ * @module
1345
+ */
1346
+ const compiler = new Compiler();
1347
+ const Packer = createPacker({
1348
+ compile: (file, overrides) => compiler.compile(file, overrides),
1349
+ mimeType: OoxmlMimeType.XLSX
1350
+ });
1351
+ //#endregion
1352
+ //#region src/util/index.ts
1353
+ /**
1354
+ * Convert a 1-based column number to Excel column letter(s).
1355
+ * 1 → "A", 26 → "Z", 27 → "AA", 28 → "AB"
1356
+ */
1357
+ function columnToLetter(col) {
1358
+ let result = "";
1359
+ let n = col;
1360
+ while (n > 0) {
1361
+ const remainder = (n - 1) % 26;
1362
+ result = String.fromCharCode(65 + remainder) + result;
1363
+ n = Math.floor((n - 1) / 26);
1364
+ }
1365
+ return result;
1366
+ }
1367
+ /**
1368
+ * Convert Excel column letter(s) to a 1-based column number.
1369
+ * "A" → 1, "Z" → 26, "AA" → 27
1370
+ */
1371
+ function letterToColumn(s) {
1372
+ let result = 0;
1373
+ for (let i = 0; i < s.length; i++) result = result * 26 + (s.charCodeAt(i) - 64);
1374
+ return result;
1375
+ }
1376
+ /**
1377
+ * Convert a JavaScript Date to an Excel serial number.
1378
+ * Excel epoch: January 1, 1900 = 1 (with the 1900 leap year bug).
1379
+ */
1380
+ function dateToSerialNumber(date) {
1381
+ const epoch = new Date(1899, 11, 30);
1382
+ return (date.getTime() - epoch.getTime()) / 864e5;
1383
+ }
1384
+ //#endregion
1385
+ //#region src/parse.ts
1386
+ /**
1387
+ * XLSX parsing — parse .xlsx files into structured data.
1388
+ *
1389
+ * @module
1390
+ */
1391
+ function sortByNumber(paths) {
1392
+ return paths.sort((a, b) => {
1393
+ return parseInt(a.match(/(\d+)/)?.[1] ?? "0", 10) - parseInt(b.match(/(\d+)/)?.[1] ?? "0", 10);
1394
+ });
1395
+ }
1396
+ /**
1397
+ * Parse raw .xlsx data into a low-level XlsxDocument.
1398
+ */
1399
+ function parseXlsx(data) {
1400
+ const doc = parseArchive(toUint8Array(data));
1401
+ const workbook = doc.get("xl/workbook.xml");
1402
+ const styles = doc.get("xl/styles.xml");
1403
+ const sharedStrings = doc.get("xl/sharedStrings.xml");
1404
+ const worksheets = [];
1405
+ const charts = [];
1406
+ const drawings = [];
1407
+ const media = [];
1408
+ const wbRels = doc.get("xl/_rels/workbook.xml.rels");
1409
+ if (wbRels) for (const child of wbRels.elements ?? []) {
1410
+ if (child.name !== "Relationship") continue;
1411
+ const type = attr(child, "Type") ?? "";
1412
+ const target = attr(child, "Target") ?? "";
1413
+ if (!target) continue;
1414
+ if (type.includes("/worksheet")) worksheets.push(target.startsWith("/") ? target.slice(1) : `xl/${target}`);
1415
+ }
1416
+ sortByNumber(worksheets);
1417
+ drawings.push(...doc.keys("xl/drawings/").filter((k) => k.endsWith(".xml")));
1418
+ charts.push(...doc.keys("xl/charts/").filter((k) => k.endsWith(".xml")));
1419
+ media.push(...doc.keys("xl/media/"));
1420
+ sortByNumber(drawings);
1421
+ sortByNumber(charts);
1422
+ let coreProps;
1423
+ let appProps;
1424
+ const rootRels = doc.get("_rels/.rels");
1425
+ if (rootRels) for (const child of rootRels.elements ?? []) {
1426
+ if (child.name !== "Relationship") continue;
1427
+ const type = attr(child, "Type") ?? "";
1428
+ const target = attr(child, "Target") ?? "";
1429
+ if (type.includes("/core-properties")) coreProps = target;
1430
+ else if (type.includes("/extended-properties")) appProps = target;
1431
+ }
1432
+ return {
1433
+ doc,
1434
+ workbook,
1435
+ worksheets,
1436
+ styles,
1437
+ sharedStrings,
1438
+ partRefs: {
1439
+ worksheets,
1440
+ charts,
1441
+ media,
1442
+ drawings
1443
+ },
1444
+ coreProps,
1445
+ appProps
1446
+ };
1447
+ }
1448
+ function parseSharedStrings(el) {
1449
+ if (!el) return [];
1450
+ const strings = [];
1451
+ for (const si of el.elements ?? []) {
1452
+ if (si.name !== "si") continue;
1453
+ const t = findChild(si, "t");
1454
+ if (t) strings.push(textOf(t) ?? "");
1455
+ else {
1456
+ const parts = [];
1457
+ for (const r of si.elements ?? []) {
1458
+ if (r.name !== "r") continue;
1459
+ const rt = findChild(r, "t");
1460
+ if (rt) parts.push(textOf(rt) ?? "");
1461
+ }
1462
+ strings.push(parts.join(""));
1463
+ }
1464
+ }
1465
+ return strings;
1466
+ }
1467
+ function colFromRef(ref) {
1468
+ const match = ref.match(/^([A-Z]+)/);
1469
+ return match ? letterToColumn(match[1]) : 1;
1470
+ }
1471
+ function rowFromRef(ref) {
1472
+ const match = ref.match(/(\d+)$/);
1473
+ return match ? parseInt(match[1], 10) : 1;
1474
+ }
1475
+ function parseWorksheetElement(wsEl, strings) {
1476
+ const opts = {};
1477
+ const colsEl = findChild(wsEl, "cols") ?? findChildByLocalName(wsEl, "cols");
1478
+ if (colsEl) {
1479
+ const columns = [];
1480
+ for (const col of colsEl.elements ?? []) {
1481
+ if (localName$1(col) !== "col") continue;
1482
+ const min = attrNum(col, "min");
1483
+ const max = attrNum(col, "max");
1484
+ if (min === void 0 || max === void 0) continue;
1485
+ const colOpts = {
1486
+ min,
1487
+ max
1488
+ };
1489
+ const width = attrNum(col, "width");
1490
+ if (width !== void 0) colOpts.width = width;
1491
+ if (attr(col, "hidden") === "1") colOpts.hidden = true;
1492
+ columns.push(colOpts);
1493
+ }
1494
+ if (columns.length > 0) opts.columns = columns;
1495
+ }
1496
+ const sheetViews = findChildByLocalName(wsEl, "sheetViews");
1497
+ if (sheetViews) {
1498
+ const sheetView = findChildByLocalName(sheetViews, "sheetView");
1499
+ if (sheetView) {
1500
+ const pane = findChildByLocalName(sheetView, "pane");
1501
+ if (pane) {
1502
+ if (attr(pane, "state") === "frozen") {
1503
+ const freezePanes = {};
1504
+ const ySplit = attrNum(pane, "ySplit");
1505
+ const xSplit = attrNum(pane, "xSplit");
1506
+ if (ySplit && ySplit > 0) freezePanes.row = ySplit;
1507
+ if (xSplit && xSplit > 0) freezePanes.col = xSplit;
1508
+ if (Object.keys(freezePanes).length > 0) opts.freezePanes = freezePanes;
1509
+ }
1510
+ }
1511
+ }
1512
+ }
1513
+ const sheetData = findChildByLocalName(wsEl, "sheetData");
1514
+ const rows = [];
1515
+ if (sheetData) for (const rowEl of sheetData.elements ?? []) {
1516
+ if (localName$1(rowEl) !== "row") continue;
1517
+ const rowNumber = attrNum(rowEl, "r");
1518
+ const rowOpts = {};
1519
+ if (rowNumber !== void 0) rowOpts.rowNumber = rowNumber;
1520
+ const ht = attrNum(rowEl, "ht");
1521
+ if (ht !== void 0) rowOpts.height = ht;
1522
+ if (attr(rowEl, "hidden") === "1") rowOpts.hidden = true;
1523
+ const cells = [];
1524
+ for (const cellEl of rowEl.elements ?? []) {
1525
+ if (localName$1(cellEl) !== "c") continue;
1526
+ const ref = attr(cellEl, "r");
1527
+ const type = attr(cellEl, "t");
1528
+ const cellOpts = {};
1529
+ if (ref) cellOpts.reference = ref;
1530
+ const styleIdx = attrNum(cellEl, "s");
1531
+ if (styleIdx !== void 0) cellOpts.styleIndex = styleIdx;
1532
+ const vEl = findChildByLocalName(cellEl, "v");
1533
+ const isEl = findChildByLocalName(cellEl, "is");
1534
+ if (type === "s" && vEl) cellOpts.value = strings[parseInt(textOf(vEl) ?? "", 10)] ?? "";
1535
+ else if (type === "b" && vEl) cellOpts.value = textOf(vEl) === "1";
1536
+ else if (type === "inlineStr" && isEl) cellOpts.value = textOf(findChildByLocalName(isEl, "t")) ?? "";
1537
+ else if (vEl) {
1538
+ const raw = textOf(vEl) ?? "";
1539
+ const num = Number(raw);
1540
+ cellOpts.value = isNaN(num) ? raw : num;
1541
+ }
1542
+ cells.push(cellOpts);
1543
+ }
1544
+ rowOpts.cells = cells;
1545
+ rows.push(rowOpts);
1546
+ }
1547
+ opts.children = rows;
1548
+ const mergeCellsEl = findChildByLocalName(wsEl, "mergeCells");
1549
+ if (mergeCellsEl) {
1550
+ const mergeCells = [];
1551
+ for (const mc of mergeCellsEl.elements ?? []) {
1552
+ if (localName$1(mc) !== "mergeCell") continue;
1553
+ const ref = attr(mc, "ref");
1554
+ if (!ref) continue;
1555
+ const parts = ref.split(":");
1556
+ if (parts.length === 2) mergeCells.push({
1557
+ from: {
1558
+ row: rowFromRef(parts[0]),
1559
+ col: colFromRef(parts[0])
1560
+ },
1561
+ to: {
1562
+ row: rowFromRef(parts[1]),
1563
+ col: colFromRef(parts[1])
1564
+ }
1565
+ });
1566
+ }
1567
+ if (mergeCells.length > 0) opts.mergeCells = mergeCells;
1568
+ }
1569
+ const autoFilterEl = findChildByLocalName(wsEl, "autoFilter");
1570
+ if (autoFilterEl) {
1571
+ const ref = attr(autoFilterEl, "ref");
1572
+ if (ref) opts.autoFilter = ref;
1573
+ }
1574
+ return opts;
1575
+ }
1576
+ function localName$1(el) {
1577
+ const name = el.name ?? "";
1578
+ const colonIdx = name.indexOf(":");
1579
+ return colonIdx >= 0 ? name.slice(colonIdx + 1) : name;
1580
+ }
1581
+ function findChildByLocalName(parent, name) {
1582
+ return (parent.elements ?? []).find((el) => localName$1(el) === name);
1583
+ }
1584
+ /**
1585
+ * Parse a .xlsx file and convert it into WorkbookOptions.
1586
+ *
1587
+ * The returned options can be passed to `new Workbook(parsed)`.
1588
+ */
1589
+ function parseWorkbook(data) {
1590
+ const xlsx = parseXlsx(data);
1591
+ const opts = {};
1592
+ if (xlsx.coreProps) {
1593
+ const corePropsEl = xlsx.doc.get(xlsx.coreProps);
1594
+ if (corePropsEl) {
1595
+ const cp = parseCorePropsElement(corePropsEl);
1596
+ if (cp.title) opts.title = cp.title;
1597
+ if (cp.subject) opts.subject = cp.subject;
1598
+ if (cp.creator) opts.creator = cp.creator;
1599
+ if (cp.keywords) opts.keywords = cp.keywords;
1600
+ if (cp.description) opts.description = cp.description;
1601
+ if (cp.lastModifiedBy) opts.lastModifiedBy = cp.lastModifiedBy;
1602
+ if (cp.revision) opts.revision = parseInt(cp.revision, 10);
1603
+ }
1604
+ }
1605
+ const strings = parseSharedStrings(xlsx.sharedStrings);
1606
+ const sheetNames = [];
1607
+ if (xlsx.workbook) {
1608
+ const sheetsEl = findChildByLocalName(xlsx.workbook, "sheets");
1609
+ if (sheetsEl) for (const s of sheetsEl.elements ?? []) {
1610
+ if (localName$1(s) !== "sheet") continue;
1611
+ sheetNames.push(attr(s, "name") ?? "");
1612
+ }
1613
+ }
1614
+ const worksheets = [];
1615
+ for (let i = 0; i < xlsx.worksheets.length; i++) {
1616
+ const wsPath = xlsx.worksheets[i];
1617
+ const wsEl = xlsx.doc.get(wsPath);
1618
+ if (!wsEl) continue;
1619
+ const wsOpts = parseWorksheetElement(wsEl, strings);
1620
+ if (sheetNames[i]) wsOpts.name = sheetNames[i];
1621
+ worksheets.push(wsOpts);
1622
+ }
1623
+ opts.worksheets = worksheets;
1624
+ return opts;
1625
+ }
1626
+ //#endregion
1627
+ //#region src/patcher.ts
1628
+ /**
1629
+ * XLSX patching — replace placeholders in existing .xlsx files.
1630
+ *
1631
+ * Unlike DOCX/PPTX (paragraph-based), XLSX patching targets cell values:
1632
+ * - Replaces placeholders in the shared strings table (most common)
1633
+ * - Replaces placeholders in inline strings within worksheet cells
1634
+ *
1635
+ * @module
1636
+ */
1637
+ /** Reusable TextEncoder (stateless, safe to share). */
1638
+ const encoder = new TextEncoder();
1639
+ const PatchType = { CELL: "cell" };
1640
+ /**
1641
+ * Patch an existing .xlsx workbook by replacing placeholder text in cells.
1642
+ *
1643
+ * Placeholders are matched in shared strings and inline strings.
1644
+ * For string replacements, the shared string value is updated in-place.
1645
+ * For non-string replacements, the cell type and value are updated.
1646
+ */
1647
+ const patchWorkbook = async ({ outputType, data, patches, placeholderDelimiters = {
1648
+ start: "{{",
1649
+ end: "}}"
1650
+ } }) => {
1651
+ const zipContent = unzipSync(toUint8Array(data));
1652
+ const xmlMap = /* @__PURE__ */ new Map();
1653
+ const binaryMap = /* @__PURE__ */ new Map();
1654
+ for (const [key, value] of Object.entries(zipContent)) if (key.endsWith(".xml") || key.endsWith(".rels")) xmlMap.set(key, toJson(strFromU8(value)));
1655
+ else binaryMap.set(key, value);
1656
+ const { start, end } = placeholderDelimiters;
1657
+ const patchMap = /* @__PURE__ */ new Map();
1658
+ for (const [key, patch] of Object.entries(patches)) patchMap.set(`${start}${key}${end}`, patch);
1659
+ const sst = xmlMap.get("xl/sharedStrings.xml");
1660
+ if (sst) patchSharedStrings(sst, patchMap);
1661
+ const worksheetKeys = Object.keys(zipContent).filter((k) => k.startsWith("xl/worksheets/sheet") && k.endsWith(".xml"));
1662
+ for (const wsKey of worksheetKeys) {
1663
+ const ws = xmlMap.get(wsKey);
1664
+ if (ws) patchWorksheetInlineStrings(ws, patchMap);
1665
+ }
1666
+ const files = {};
1667
+ for (const [key, value] of xmlMap) files[key] = encoder.encode(js2xml(value));
1668
+ for (const [key, value] of binaryMap) files[key] = value;
1669
+ return await zipAndConvert(files, outputType, OoxmlMimeType.XLSX);
1670
+ };
1671
+ function patchSharedStrings(sst, patchMap) {
1672
+ const root = sst.name ? sst : sst.elements?.[0];
1673
+ if (!root) return;
1674
+ for (const si of root.elements ?? []) {
1675
+ if (si.name !== "si") continue;
1676
+ const t = findLocalChild(si, "t");
1677
+ if (t) patchTextElement(t, patchMap);
1678
+ else for (const r of si.elements ?? []) {
1679
+ if (r.name !== "r") continue;
1680
+ const rt = findLocalChild(r, "t");
1681
+ if (rt) patchTextElement(rt, patchMap);
1682
+ }
1683
+ }
1684
+ }
1685
+ function patchWorksheetInlineStrings(ws, patchMap) {
1686
+ const root = ws.name ? ws : ws.elements?.[0];
1687
+ if (!root) return;
1688
+ const sheetData = findLocalChild(root, "sheetData");
1689
+ if (!sheetData) return;
1690
+ for (const row of sheetData.elements ?? []) {
1691
+ if (localName(row) !== "row") continue;
1692
+ for (const cell of row.elements ?? []) {
1693
+ if (localName(cell) !== "c") continue;
1694
+ const isEl = findLocalChild(cell, "is");
1695
+ if (isEl) {
1696
+ const t = findLocalChild(isEl, "t");
1697
+ if (t) patchTextElement(t, patchMap);
1698
+ }
1699
+ }
1700
+ }
1701
+ }
1702
+ function patchTextElement(tEl, patchMap) {
1703
+ const text = tEl.elements?.[0]?.text;
1704
+ if (typeof text !== "string") return;
1705
+ for (const [placeholder, patch] of patchMap) if (text.includes(placeholder)) {
1706
+ const newValue = String(patch.value);
1707
+ const replaced = text.replace(placeholder, newValue);
1708
+ if (tEl.elements) tEl.elements[0] = {
1709
+ ...tEl.elements[0],
1710
+ text: replaced
1711
+ };
1712
+ }
1713
+ }
1714
+ function localName(el) {
1715
+ const name = el.name ?? "";
1716
+ const colonIdx = name.indexOf(":");
1717
+ return colonIdx >= 0 ? name.slice(colonIdx + 1) : name;
1718
+ }
1719
+ function findLocalChild(parent, name) {
1720
+ return (parent.elements ?? []).find((el) => localName(el) === name);
1721
+ }
1722
+ //#endregion
1723
+ export { File, File as Workbook, Packer, PatchType, SharedStrings, Styles, columnToLetter, dateToSerialNumber, letterToColumn, parseWorkbook, parseXlsx, patchWorkbook };
1724
+
1725
+ //# sourceMappingURL=index.mjs.map