@node-projects/excelforge 2.3.0 → 3.0.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.
Files changed (71) hide show
  1. package/.github/FUNDING.yml +4 -0
  2. package/MISSING.md +326 -0
  3. package/README.md +484 -12
  4. package/dist/core/SharedStrings.js +6 -2
  5. package/dist/core/SharedStrings.js.map +1 -1
  6. package/dist/core/Workbook.d.ts +41 -1
  7. package/dist/core/Workbook.js +773 -57
  8. package/dist/core/Workbook.js.map +1 -1
  9. package/dist/core/WorkbookReader.d.ts +18 -4
  10. package/dist/core/WorkbookReader.js +1386 -20
  11. package/dist/core/WorkbookReader.js.map +1 -1
  12. package/dist/core/Worksheet.d.ts +130 -2
  13. package/dist/core/Worksheet.js +792 -66
  14. package/dist/core/Worksheet.js.map +1 -1
  15. package/dist/core/types.d.ts +287 -5
  16. package/dist/core/types.js +12 -1
  17. package/dist/core/types.js.map +1 -1
  18. package/dist/features/ChartBuilder.d.ts +9 -1
  19. package/dist/features/ChartBuilder.js +140 -14
  20. package/dist/features/ChartBuilder.js.map +1 -1
  21. package/dist/features/CsvModule.d.ts +11 -0
  22. package/dist/features/CsvModule.js +137 -0
  23. package/dist/features/CsvModule.js.map +1 -0
  24. package/dist/features/Encryption.d.ts +6 -0
  25. package/dist/features/Encryption.js +806 -0
  26. package/dist/features/Encryption.js.map +1 -0
  27. package/dist/features/FormControlBuilder.d.ts +6 -0
  28. package/dist/features/FormControlBuilder.js +135 -0
  29. package/dist/features/FormControlBuilder.js.map +1 -0
  30. package/dist/features/FormulaEngine.d.ts +22 -0
  31. package/dist/features/FormulaEngine.js +498 -0
  32. package/dist/features/FormulaEngine.js.map +1 -0
  33. package/dist/features/HtmlModule.d.ts +21 -0
  34. package/dist/features/HtmlModule.js +1417 -0
  35. package/dist/features/HtmlModule.js.map +1 -0
  36. package/dist/features/JsonModule.d.ts +10 -0
  37. package/dist/features/JsonModule.js +76 -0
  38. package/dist/features/JsonModule.js.map +1 -0
  39. package/dist/features/PivotTableBuilder.d.ts +7 -0
  40. package/dist/features/PivotTableBuilder.js +170 -0
  41. package/dist/features/PivotTableBuilder.js.map +1 -0
  42. package/dist/features/Signing.d.ts +12 -0
  43. package/dist/features/Signing.js +318 -0
  44. package/dist/features/Signing.js.map +1 -0
  45. package/dist/features/TableBuilder.js +2 -2
  46. package/dist/features/TableBuilder.js.map +1 -1
  47. package/dist/index-min.js +579 -144
  48. package/dist/index.d.ts +17 -1
  49. package/dist/index.js +10 -0
  50. package/dist/index.js.map +1 -1
  51. package/dist/styles/StyleRegistry.d.ts +14 -0
  52. package/dist/styles/StyleRegistry.js +95 -30
  53. package/dist/styles/StyleRegistry.js.map +1 -1
  54. package/dist/utils/helpers.d.ts +4 -0
  55. package/dist/utils/helpers.js +64 -14
  56. package/dist/utils/helpers.js.map +1 -1
  57. package/dist/utils/zip.js +145 -73
  58. package/dist/utils/zip.js.map +1 -1
  59. package/dist/vba/VbaProject.d.ts +19 -0
  60. package/dist/vba/VbaProject.js +281 -0
  61. package/dist/vba/VbaProject.js.map +1 -0
  62. package/dist/vba/cfb.d.ts +7 -0
  63. package/dist/vba/cfb.js +352 -0
  64. package/dist/vba/cfb.js.map +1 -0
  65. package/dist/vba/ovba.d.ts +2 -0
  66. package/dist/vba/ovba.js +137 -0
  67. package/dist/vba/ovba.js.map +1 -0
  68. package/package.json +4 -3
  69. package/validator.cs +0 -155
  70. package/validatorEpplus.cs +0 -27
  71. package/validatorReadData.cs +0 -111
@@ -3,20 +3,30 @@ import { StyleRegistry } from '../styles/StyleRegistry.js';
3
3
  import { SharedStrings } from './SharedStrings.js';
4
4
  import { buildChartXml } from '../features/ChartBuilder.js';
5
5
  import { buildTableXml } from '../features/TableBuilder.js';
6
+ import { buildPivotTableFiles } from '../features/PivotTableBuilder.js';
7
+ import { buildCtrlPropXml, buildFormControlVmlShape, buildVmlWithControls } from '../features/FormControlBuilder.js';
8
+ import { VbaProject } from '../vba/VbaProject.js';
6
9
  import { buildZip } from '../utils/zip.js';
7
10
  import { strToBytes, base64ToBytes, escapeXml, colIndexToLetter } from '../utils/helpers.js';
8
- import { readWorkbook } from './WorkbookReader.js';
11
+ import { readWorkbook, connTypeToNum, cmdTypeToNum } from './WorkbookReader.js';
9
12
  import { buildCoreXml, buildAppXml, buildCustomXml, } from './properties.js';
10
13
  export class Workbook {
11
14
  constructor() {
12
15
  this.sheets = [];
13
16
  this.namedRanges = [];
17
+ this.connections = [];
18
+ this.powerQueries = [];
19
+ this.externalLinks = [];
20
+ this.customPivotStyles = [];
21
+ this.pivotSlicers = [];
14
22
  this.properties = {};
15
23
  this.compressionLevel = 6;
16
24
  this.coreProperties = {};
17
25
  this.extendedProperties = {};
18
26
  this.customProperties = [];
27
+ this.isTemplate = false;
19
28
  this._dirtySheets = new Set();
29
+ this._customTableStyles = new Map();
20
30
  }
21
31
  markDirty(sheetIndexOrName) {
22
32
  if (typeof sheetIndexOrName === 'string') {
@@ -49,6 +59,16 @@ export class Workbook {
49
59
  status: result.core.contentStatus,
50
60
  };
51
61
  wb.sheets = result.sheets.map(s => s.ws);
62
+ wb.namedRanges = result.namedRanges;
63
+ wb.connections = result.connections;
64
+ wb.powerQueries = result.powerQueries;
65
+ const vbaData = result.unknownParts.get('xl/vbaProject.bin');
66
+ if (vbaData) {
67
+ try {
68
+ wb.vbaProject = VbaProject.fromBytes(vbaData);
69
+ }
70
+ catch { }
71
+ }
52
72
  return wb;
53
73
  }
54
74
  static async fromBase64(b64) {
@@ -78,14 +98,102 @@ export class Workbook {
78
98
  getSheetNames() {
79
99
  return this.sheets.map(s => s.name);
80
100
  }
101
+ getSheets() {
102
+ return this.sheets;
103
+ }
81
104
  removeSheet(name) {
82
105
  this.sheets = this.sheets.filter(s => s.name !== name);
83
106
  return this;
84
107
  }
108
+ addChartSheet(name, chart) {
109
+ const ws = this.addSheet(name);
110
+ ws._isChartSheet = true;
111
+ ws.addChart(chart);
112
+ return ws;
113
+ }
114
+ addDialogSheet(name) {
115
+ const ws = this.addSheet(name);
116
+ ws._isDialogSheet = true;
117
+ return ws;
118
+ }
119
+ copySheet(sourceName, newName) {
120
+ const src = this.getSheet(sourceName);
121
+ if (!src)
122
+ throw new Error(`Sheet "${sourceName}" not found`);
123
+ const ws = this.addSheet(newName);
124
+ const cells = src.readAllCells();
125
+ for (const { row, col, cell } of cells) {
126
+ const target = ws.getCell(row, col);
127
+ if (cell.value != null)
128
+ target.value = cell.value;
129
+ if (cell.formula)
130
+ target.formula = cell.formula;
131
+ if (cell.arrayFormula)
132
+ target.arrayFormula = cell.arrayFormula;
133
+ if (cell.richText)
134
+ target.richText = cell.richText.map(r => ({ ...r, font: r.font ? { ...r.font } : undefined }));
135
+ if (cell.style)
136
+ target.style = { ...cell.style };
137
+ if (cell.comment)
138
+ target.comment = { ...cell.comment };
139
+ if (cell.hyperlink)
140
+ target.hyperlink = { ...cell.hyperlink };
141
+ }
142
+ for (const m of src.getMerges()) {
143
+ ws.merge(m.startRow, m.startCol, m.endRow, m.endCol);
144
+ }
145
+ const range = src.getUsedRange();
146
+ if (range) {
147
+ for (let c = range.startCol; c <= range.endCol; c++) {
148
+ const cd = src.getColumn(c);
149
+ if (cd)
150
+ ws.setColumn(c, { ...cd });
151
+ }
152
+ }
153
+ if (src.pageSetup)
154
+ ws.pageSetup = { ...src.pageSetup };
155
+ if (src.printArea)
156
+ ws.printArea = src.printArea;
157
+ return ws;
158
+ }
159
+ registerTableStyle(name, def) {
160
+ this._customTableStyles.set(name, def);
161
+ return this;
162
+ }
85
163
  addNamedRange(nr) {
86
164
  this.namedRanges.push(nr);
87
165
  return this;
88
166
  }
167
+ getNamedRanges() {
168
+ return this.namedRanges;
169
+ }
170
+ getNamedRange(name) {
171
+ return this.namedRanges.find(nr => nr.name === name);
172
+ }
173
+ removeNamedRange(name) {
174
+ this.namedRanges = this.namedRanges.filter(nr => nr.name !== name);
175
+ return this;
176
+ }
177
+ addConnection(conn) {
178
+ this.connections.push(conn);
179
+ return this;
180
+ }
181
+ getConnections() {
182
+ return this.connections;
183
+ }
184
+ getConnection(name) {
185
+ return this.connections.find(c => c.name === name);
186
+ }
187
+ removeConnection(name) {
188
+ this.connections = this.connections.filter(c => c.name !== name);
189
+ return this;
190
+ }
191
+ getPowerQueries() {
192
+ return this.powerQueries;
193
+ }
194
+ getPowerQuery(name) {
195
+ return this.powerQueries.find(q => q.name === name);
196
+ }
89
197
  getCustomProperty(name) {
90
198
  return this.customProperties.find(p => p.name === name);
91
199
  }
@@ -101,6 +209,24 @@ export class Workbook {
101
209
  this.customProperties = this.customProperties.filter(p => p.name !== name);
102
210
  return this;
103
211
  }
212
+ addExternalLink(link) {
213
+ this.externalLinks.push(link);
214
+ return this;
215
+ }
216
+ getExternalLinks() {
217
+ return this.externalLinks;
218
+ }
219
+ registerPivotStyle(style) {
220
+ this.customPivotStyles.push(style);
221
+ return this;
222
+ }
223
+ addPivotSlicer(slicer) {
224
+ this.pivotSlicers.push(slicer);
225
+ return this;
226
+ }
227
+ getPivotSlicers() {
228
+ return this.pivotSlicers;
229
+ }
104
230
  async build() {
105
231
  this._syncLegacyProperties();
106
232
  return this._readResult ? this._buildPatched() : this._buildFresh();
@@ -113,6 +239,13 @@ export class Workbook {
113
239
  const shared = new SharedStrings();
114
240
  const sheetXmls = new Map();
115
241
  if (hasDirty) {
242
+ const dxfRe = /<dxf>([\s\S]*?)<\/dxf>|<dxf\/>/g;
243
+ const rawDxfs = [];
244
+ let m;
245
+ while ((m = dxfRe.exec(rr.stylesXml)) !== null)
246
+ rawDxfs.push(m[1] ?? '');
247
+ if (rawDxfs.length)
248
+ styles.prependRawDxfs(rawDxfs);
116
249
  for (let i = 0; i < this.sheets.length; i++) {
117
250
  sheetXmls.set(i, this.sheets[i].toXml(styles, shared));
118
251
  }
@@ -127,7 +260,7 @@ export class Workbook {
127
260
  ...rr.extended,
128
261
  ...this.extendedProperties,
129
262
  titlesOfParts: this.sheets.map(s => s.name),
130
- headingPairs: [{ name: 'Worksheets', count: this.sheets.length }],
263
+ headingPairs: this._headingPairs(),
131
264
  }, rr.extendedUnknownRaw)),
132
265
  });
133
266
  const customProps = this.customProperties.length > 0
@@ -137,6 +270,10 @@ export class Workbook {
137
270
  entries.push({ name: 'docProps/custom.xml', data: strToBytes(buildCustomXml(customProps)) });
138
271
  }
139
272
  entries.push({ name: 'xl/workbook.xml', data: strToBytes(this._patchWorkbookXml(rr.workbookXml)) });
273
+ const connectionsXml = this._connectionsXml(rr.connectionsXml);
274
+ if (connectionsXml) {
275
+ entries.push({ name: 'xl/connections.xml', data: strToBytes(connectionsXml) });
276
+ }
140
277
  if (hasDirty) {
141
278
  entries.push({ name: 'xl/styles.xml', data: strToBytes(styles.toXml()) });
142
279
  entries.push({ name: 'xl/sharedStrings.xml', data: strToBytes(shared.toXml()) });
@@ -146,38 +283,43 @@ export class Workbook {
146
283
  entries.push({ name: 'xl/sharedStrings.xml', data: strToBytes(rr.sharedXml) });
147
284
  }
148
285
  for (let i = 0; i < this.sheets.length; i++) {
149
- const path = `xl/worksheets/sheet${i + 1}.xml`;
286
+ const ws = this.sheets[i];
287
+ const folder = ws._isChartSheet ? 'chartsheets' : ws._isDialogSheet ? 'dialogsheets' : 'worksheets';
288
+ const path = `xl/${folder}/sheet${i + 1}.xml`;
150
289
  if (hasDirty) {
151
- entries.push({ name: path, data: strToBytes(sheetXmls.get(i) ?? '') });
152
- }
153
- else {
154
- const orig = rr.sheets[i]?.originalXml ?? '';
155
- const unknown = rr.sheets[i]?.unknownParts.join('\n') ?? '';
156
- entries.push({ name: path, data: strToBytes(unknown ? orig.replace('</worksheet>', `${unknown}</worksheet>`) : orig) });
157
- }
158
- }
159
- const dirtyTablePaths = new Set();
160
- if (hasDirty) {
161
- for (let i = 0; i < this.sheets.length; i++) {
162
- if (rr.sheets[i]?.tablePaths) {
163
- for (const tp of rr.sheets[i].tablePaths)
164
- dirtyTablePaths.add(tp);
290
+ if (ws._isChartSheet) {
291
+ entries.push({ name: path, data: strToBytes(ws.toChartSheetXml()) });
292
+ }
293
+ else if (ws._isDialogSheet) {
294
+ entries.push({ name: path, data: strToBytes(ws.toDialogSheetXml(styles, shared)) });
295
+ }
296
+ else {
297
+ entries.push({ name: path, data: strToBytes(sheetXmls.get(i) ?? '') });
165
298
  }
166
299
  }
167
- }
168
- for (const [path, data] of rr.unknownParts) {
169
- if (!dirtyTablePaths.has(path)) {
170
- entries.push({ name: path, data });
300
+ else {
301
+ entries.push({ name: path, data: strToBytes(rr.sheets[i]?.originalXml ?? '') });
171
302
  }
172
303
  }
173
- if (hasDirty) {
174
- for (let i = 0; i < this.sheets.length; i++) {
175
- const ws = this.sheets[i];
176
- const tables = ws.getTables();
177
- const paths = rr.sheets[i]?.tablePaths ?? [];
178
- for (let j = 0; j < tables.length; j++) {
179
- const tblPath = paths[j];
180
- if (tblPath) {
304
+ const allTablePaths = new Set();
305
+ for (let i = 0; i < this.sheets.length; i++) {
306
+ const ws = this.sheets[i];
307
+ const tables = ws.getTables();
308
+ const paths = rr.sheets[i]?.tablePaths ?? [];
309
+ const xmls = rr.sheets[i]?.tableXmls ?? [];
310
+ for (let j = 0; j < tables.length; j++) {
311
+ const tblPath = paths[j];
312
+ if (tblPath) {
313
+ allTablePaths.add(tblPath);
314
+ if (j < xmls.length) {
315
+ let xml = xmls[j];
316
+ const origRefMatch = xml.match(/\bref="([^"]+)"/);
317
+ if (origRefMatch && origRefMatch[1] !== tables[j].ref) {
318
+ xml = xml.replace(`ref="${origRefMatch[1]}"`, `ref="${tables[j].ref}"`);
319
+ }
320
+ entries.push({ name: tblPath, data: strToBytes(xml) });
321
+ }
322
+ else {
181
323
  const idMatch = tblPath.match(/table(\d+)\.xml$/);
182
324
  const tableId = idMatch ? parseInt(idMatch[1], 10) : j + 1;
183
325
  entries.push({ name: tblPath, data: strToBytes(buildTableXml(tables[j], tableId)) });
@@ -185,8 +327,23 @@ export class Workbook {
185
327
  }
186
328
  }
187
329
  }
330
+ for (const [path, data] of rr.unknownParts) {
331
+ if (allTablePaths.has(path))
332
+ continue;
333
+ if (path === 'xl/vbaProject.bin' && this.vbaProject)
334
+ continue;
335
+ if (rr.allRels.has(path))
336
+ continue;
337
+ if (hasDirty && path === 'xl/calcChain.xml')
338
+ continue;
339
+ entries.push({ name: path, data });
340
+ }
341
+ if (this.vbaProject) {
342
+ this._ensureVbaSheetModules();
343
+ entries.push({ name: 'xl/vbaProject.bin', data: this.vbaProject.build() });
344
+ }
188
345
  entries.push({ name: '_rels/.rels', data: strToBytes(this._buildRootRels(customProps != null && customProps.length > 0)) });
189
- entries.push({ name: 'xl/_rels/workbook.xml.rels', data: strToBytes(this._buildWorkbookRels(rr)) });
346
+ entries.push({ name: 'xl/_rels/workbook.xml.rels', data: strToBytes(this._buildWorkbookRels(rr, hasDirty)) });
190
347
  for (const [relPath, relMap] of rr.allRels) {
191
348
  if (relPath === 'xl/_rels/workbook.xml.rels' || relPath === '_rels/.rels')
192
349
  continue;
@@ -194,7 +351,7 @@ export class Workbook {
194
351
  }
195
352
  entries.push({
196
353
  name: '[Content_Types].xml',
197
- data: strToBytes(this._patchContentTypes(rr.contentTypesXml, customProps != null && customProps.length > 0)),
354
+ data: strToBytes(this._patchContentTypes(rr.contentTypesXml, customProps != null && customProps.length > 0, hasDirty)),
198
355
  });
199
356
  return buildZip(entries, { level: this.compressionLevel });
200
357
  }
@@ -202,29 +359,35 @@ export class Workbook {
202
359
  const styles = new StyleRegistry();
203
360
  const shared = new SharedStrings();
204
361
  const entries = [];
362
+ for (const [name, def] of this._customTableStyles) {
363
+ styles.registerTableStyle(name, def);
364
+ }
205
365
  let globalRId = 1;
206
366
  for (const ws of this.sheets)
207
367
  ws.rId = `rId${globalRId++}`;
208
368
  const allImages = [];
209
369
  const allCharts = [];
210
370
  const allTables = [];
371
+ const allPivotTables = [];
211
372
  const sheetImageRIds = new Map();
212
373
  const sheetChartRIds = new Map();
213
374
  const sheetTableRIds = new Map();
214
- let imgCtr = 1, chartCtr = 1, tableCtr = 1, vmlCtr = 1;
375
+ const sheetPivotRIds = new Map();
376
+ let imgCtr = 1, chartCtr = 1, tableCtr = 1, vmlCtr = 1, pivotCtr = 1, pivotCacheIdCtr = 0, ctrlPropGlobal = 0;
215
377
  for (const ws of this.sheets) {
216
378
  const imgs = ws.getImages();
217
379
  const charts = ws.getCharts();
218
380
  const tables = ws.getTables();
219
381
  const imgRIds = [], chartRIds = [], tblRIds = [];
220
- if (imgs.length || charts.length)
382
+ if (imgs.length || charts.length || ws.getShapes().length || ws.getWordArt().length || ws.getMathEquations().length || ws.getTableSlicers().length)
221
383
  ws.drawingRId = `rId${globalRId++}`;
222
- if (ws.getComments().length)
384
+ const controls = ws.getFormControls();
385
+ if (ws.getComments().length || controls.length)
223
386
  ws.legacyDrawingRId = `rId${globalRId++}`;
224
387
  for (const img of imgs) {
225
388
  const r = `rId${globalRId++}`;
226
389
  imgRIds.push(r);
227
- allImages.push({ ws, img, ext: img.format === 'jpeg' ? 'jpg' : img.format, idx: imgCtr++ });
390
+ allImages.push({ ws, img, ext: imageExt(img.format), idx: imgCtr++ });
228
391
  }
229
392
  for (let i = 0; i < charts.length; i++) {
230
393
  const r = `rId${globalRId++}`;
@@ -236,49 +399,200 @@ export class Workbook {
236
399
  tblRIds.push(r);
237
400
  allTables.push({ ws, tableIdx: i, globalTableId: tableCtr++ });
238
401
  }
402
+ const ctrlPropRIds = [];
403
+ for (let ci = 0; ci < controls.length; ci++)
404
+ ctrlPropRIds.push(`rId${globalRId++}`);
405
+ ws.ctrlPropRIds = ctrlPropRIds;
239
406
  sheetImageRIds.set(ws, imgRIds);
240
407
  sheetChartRIds.set(ws, chartRIds);
241
408
  sheetTableRIds.set(ws, tblRIds);
242
409
  ws.tableRIds = tblRIds;
410
+ const ptRIds = [];
411
+ for (const pt of ws.getPivotTables()) {
412
+ const pivotRId = `rId${globalRId++}`;
413
+ const cacheRId = `rId${globalRId++}`;
414
+ ptRIds.push(pivotRId);
415
+ allPivotTables.push({ ws, pt, pivotIdx: pivotCtr++, cacheId: pivotCacheIdCtr++, pivotRId, cacheRId });
416
+ }
417
+ sheetPivotRIds.set(ws, ptRIds);
418
+ }
419
+ const allCellImages = [];
420
+ let cellImgCtr = imgCtr;
421
+ let vmCounter = 1;
422
+ for (const ws of this.sheets) {
423
+ ws._cellImageVm = new Map();
424
+ for (const ci of ws.getCellImages()) {
425
+ const ext = imageExt(ci.format);
426
+ allCellImages.push({ img: ci, ext, idx: cellImgCtr++ });
427
+ ws._cellImageVm.set(ci.cell, vmCounter++);
428
+ ws.getCellByRef(ci.cell);
429
+ }
430
+ }
431
+ const hasCellImages = allCellImages.length > 0;
432
+ const sheetSlicerMap = new Map();
433
+ const allSlicerCaches = [];
434
+ let slicerDefCtr = 0, slicerCacheCtr = 0;
435
+ for (const ws of this.sheets) {
436
+ const tSlicers = ws.getTableSlicers();
437
+ if (tSlicers.length) {
438
+ sheetSlicerMap.set(ws, { tableSlicers: [], pivotSlicers: [], slicerDefRId: '', slicerDefIdx: 0 });
439
+ }
440
+ }
441
+ for (const ps of this.pivotSlicers) {
442
+ for (const ws of this.sheets) {
443
+ if (ws.getPivotTables().some(p => p.name === ps.pivotTableName)) {
444
+ if (!sheetSlicerMap.has(ws)) {
445
+ sheetSlicerMap.set(ws, { tableSlicers: [], pivotSlicers: [], slicerDefRId: '', slicerDefIdx: 0 });
446
+ }
447
+ break;
448
+ }
449
+ }
450
+ }
451
+ for (const [ws, info] of sheetSlicerMap) {
452
+ if (!ws.drawingRId)
453
+ ws.drawingRId = `rId${globalRId++}`;
454
+ info.slicerDefRId = `rId${globalRId++}`;
455
+ info.slicerDefIdx = ++slicerDefCtr;
456
+ ws.slicerRId = info.slicerDefRId;
457
+ const drawingInfo = [];
458
+ for (const s of ws.getTableSlicers()) {
459
+ const table = ws.getTables().find(t => t.name === s.tableName);
460
+ const tableEntry = allTables.find(t => t.ws === ws && ws.getTables()[t.tableIdx] === table);
461
+ const tableId = tableEntry?.globalTableId ?? 1;
462
+ const columnIndex = table ? (table.columns?.findIndex(c => c.name === s.columnName) ?? 0) + 1 : 1;
463
+ info.tableSlicers.push({ slicer: s, tableId, columnIndex });
464
+ drawingInfo.push({ name: s.name, cell: s.cell });
465
+ allSlicerCaches.push({
466
+ name: s.name + '_cache', sourceName: s.columnName, type: 'table',
467
+ rId: `rId${globalRId++}`, idx: ++slicerCacheCtr,
468
+ tableId, columnIndex, sortOrder: s.sortOrder ?? 'ascending',
469
+ });
470
+ }
471
+ for (const ps of this.pivotSlicers) {
472
+ const pt = ws.getPivotTables().find(p => p.name === ps.pivotTableName);
473
+ if (!pt)
474
+ continue;
475
+ const ptEntry = allPivotTables.find(p => p.ws === ws && p.pt === pt);
476
+ const sheetIdx = this.sheets.indexOf(ws) + 1;
477
+ let items = [];
478
+ const sourceWs = this.sheets.find(s => s.name === pt.sourceSheet);
479
+ if (sourceWs) {
480
+ const sourceData = sourceWs.readRange(pt.sourceRef);
481
+ const headers = (sourceData[0] ?? []).map(v => String(v ?? ''));
482
+ const fieldIdx = headers.indexOf(ps.fieldName);
483
+ if (fieldIdx >= 0) {
484
+ const uniqueSet = new Set();
485
+ for (let r = 1; r < sourceData.length; r++)
486
+ uniqueSet.add(String(sourceData[r][fieldIdx] ?? ''));
487
+ items = [...uniqueSet];
488
+ }
489
+ }
490
+ info.pivotSlicers.push({ slicer: ps, pivotCacheId: ptEntry?.cacheId ?? 0 });
491
+ drawingInfo.push({ name: ps.name, cell: ps.cell });
492
+ allSlicerCaches.push({
493
+ name: ps.name + '_cache', sourceName: ps.fieldName, type: 'pivot',
494
+ rId: `rId${globalRId++}`, idx: ++slicerCacheCtr,
495
+ pivotTableName: ps.pivotTableName, pivotCacheId: ptEntry?.cacheId ?? 0,
496
+ tabId: sheetIdx, items,
497
+ });
498
+ }
499
+ ws._slicerDrawingInfo = drawingInfo;
243
500
  }
501
+ const hasSlicers = sheetSlicerMap.size > 0;
244
502
  const hasCustom = this.customProperties.length > 0;
503
+ const hasVba = !!this.vbaProject;
245
504
  const imgCTs = new Set();
246
- for (const { ext } of allImages) {
247
- const ct = ext === 'jpg' ? 'image/jpeg' : ext === 'png' ? 'image/png' : `image/${ext}`;
505
+ for (const { ext } of [...allImages, ...allCellImages]) {
506
+ const ct = imageContentType(ext);
248
507
  imgCTs.add(`<Default Extension="${ext}" ContentType="${ct}"/>`);
249
508
  }
250
509
  const sheetsWithComments = this.sheets.filter(ws => ws.getComments().length);
251
- const vmlCT = sheetsWithComments.length ? '<Default Extension="vml" ContentType="application/vnd.openxmlformats-officedocument.vmlDrawing"/>' : '';
510
+ const sheetsWithVml = this.sheets.filter(ws => ws.getComments().length || ws.getFormControls().length);
511
+ const vmlCT = sheetsWithVml.length ? '<Default Extension="vml" ContentType="application/vnd.openxmlformats-officedocument.vmlDrawing"/>' : '';
252
512
  let vmlIdx = 0;
253
513
  const commentsCTs = sheetsWithComments.map(() => `<Override PartName="/xl/comments${++vmlIdx}.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml"/>`).join('');
514
+ let ctrlPropCtr = 0;
515
+ const ctrlPropCTs = [];
516
+ for (const ws of this.sheets) {
517
+ if (ws._isDialogSheet)
518
+ continue;
519
+ for (let ci = 0; ci < ws.getFormControls().length; ci++) {
520
+ ctrlPropCTs.push(`<Override PartName="/xl/ctrlProps/ctrlProp${++ctrlPropCtr}.xml" ContentType="application/vnd.ms-excel.controlproperties+xml"/>`);
521
+ }
522
+ }
254
523
  entries.push({ name: '[Content_Types].xml', data: strToBytes(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
255
524
  <Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
256
525
  <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
257
526
  <Default Extension="xml" ContentType="application/xml"/>
258
527
  ${vmlCT}
259
528
  ${[...imgCTs].join('')}
260
- <Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>
529
+ <Override PartName="/xl/workbook.xml" ContentType="${hasVba ? 'application/vnd.ms-excel.sheet.macroEnabled.main+xml' : this.isTemplate ? 'application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml' : 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml'}"/>
530
+ ${hasVba ? '<Override PartName="/xl/vbaProject.bin" ContentType="application/vnd.ms-office.vbaProject"/>' : ''}
261
531
  <Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/>
262
532
  <Override PartName="/xl/sharedStrings.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"/>
263
- ${this.sheets.map((_, i) => `<Override PartName="/xl/worksheets/sheet${i + 1}.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>`).join('')}
533
+ <Override PartName="/xl/theme/theme1.xml" ContentType="application/vnd.openxmlformats-officedocument.theme+xml"/>
534
+ ${this.sheets.filter(ws => !ws._isChartSheet && !ws._isDialogSheet).map(ws => { const idx = this.sheets.indexOf(ws); return `<Override PartName="/xl/worksheets/sheet${idx + 1}.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>`; }).join('')}
535
+ ${this.sheets.filter(ws => ws._isChartSheet).map(ws => { const idx = this.sheets.indexOf(ws); return `<Override PartName="/xl/chartsheets/sheet${idx + 1}.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml"/>`; }).join('')}
536
+ ${this.sheets.filter(ws => ws._isDialogSheet).map(ws => { const idx = this.sheets.indexOf(ws); return `<Override PartName="/xl/dialogsheets/sheet${idx + 1}.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.dialogsheet+xml"/>`; }).join('')}
264
537
  ${this.sheets.filter(ws => ws.drawingRId).map((_, i) => `<Override PartName="/xl/drawings/drawing${i + 1}.xml" ContentType="application/vnd.openxmlformats-officedocument.drawing+xml"/>`).join('')}
265
538
  ${allCharts.map(({ globalIdx }) => `<Override PartName="/xl/charts/chart${globalIdx}.xml" ContentType="application/vnd.openxmlformats-officedocument.drawingml.chart+xml"/>`).join('')}
266
539
  ${allTables.map(({ globalTableId }) => `<Override PartName="/xl/tables/table${globalTableId}.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml"/>`).join('')}
540
+ ${allPivotTables.map(p => `<Override PartName="/xl/pivotTables/pivotTable${p.pivotIdx}.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml"/>`).join('\n')}
541
+ ${allPivotTables.map(p => `<Override PartName="/xl/pivotCache/pivotCacheDefinition${p.pivotIdx}.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml"/>`).join('\n')}
542
+ ${allPivotTables.map(p => `<Override PartName="/xl/pivotCache/pivotCacheRecords${p.pivotIdx}.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheRecords+xml"/>`).join('\n')}
267
543
  ${commentsCTs}
544
+ ${ctrlPropCTs.join('')}
268
545
  <Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>
269
546
  <Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/>
270
547
  ${hasCustom ? '<Override PartName="/docProps/custom.xml" ContentType="application/vnd.openxmlformats-officedocument.custom-properties+xml"/>' : ''}
548
+ ${this.connections.length ? '<Override PartName="/xl/connections.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.connections+xml"/>' : ''}
549
+ ${this.externalLinks.map((_, i) => `<Override PartName="/xl/externalLinks/externalLink${i + 1}.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.externalLink+xml"/>`).join('\n')}
550
+ ${[...sheetSlicerMap.values()].map(info => `<Override PartName="/xl/slicers/slicer${info.slicerDefIdx}.xml" ContentType="application/vnd.ms-excel.slicer+xml"/>`).join('\n')}
551
+ ${allSlicerCaches.map(sc => `<Override PartName="/xl/slicerCaches/slicerCache${sc.idx}.xml" ContentType="application/vnd.ms-excel.slicerCache+xml"/>`).join('\n')}
552
+ ${this.sheets.flatMap(ws => ws.getQueryTables()).map((_, i) => `<Override PartName="/xl/queryTables/queryTable${i + 1}.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.queryTable+xml"/>`).join('\n')}
553
+ ${hasCellImages ? `<Override PartName="/xl/metadata.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheetMetadata+xml"/>
554
+ <Override PartName="/xl/richData/rdrichvalue.xml" ContentType="application/vnd.ms-excel.rdrichvalue+xml"/>
555
+ <Override PartName="/xl/richData/rdRichValueStructure.xml" ContentType="application/vnd.ms-excel.rdrichvaluestructure+xml"/>
556
+ <Override PartName="/xl/richData/richValueRel.xml" ContentType="application/vnd.ms-excel.richvaluerel+xml"/>
557
+ <Override PartName="/xl/richData/rdRichValueTypes.xml" ContentType="application/vnd.ms-excel.rdrichvaluetypes+xml"/>
558
+ <Override PartName="/xl/richData/rdarray.xml" ContentType="application/vnd.ms-excel.rdarray+xml"/>` : ''}
271
559
  </Types>`) });
272
560
  entries.push({ name: '_rels/.rels', data: strToBytes(this._buildRootRels(hasCustom)) });
273
561
  entries.push({ name: 'xl/_rels/workbook.xml.rels', data: strToBytes(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
274
562
  <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
275
- ${this.sheets.map((ws, i) => `<Relationship Id="${ws.rId}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet${i + 1}.xml"/>`).join('')}
563
+ ${this.sheets.map((ws, i) => {
564
+ const type = ws._isChartSheet ? 'chartsheet' : ws._isDialogSheet ? 'dialogsheet' : 'worksheet';
565
+ const folder = ws._isChartSheet ? 'chartsheets' : ws._isDialogSheet ? 'dialogsheets' : 'worksheets';
566
+ return `<Relationship Id="${ws.rId}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/${type}" Target="${folder}/sheet${i + 1}.xml"/>`;
567
+ }).join('')}
276
568
  <Relationship Id="rIdStyles" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>
277
569
  <Relationship Id="rIdShared" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" Target="sharedStrings.xml"/>
570
+ <Relationship Id="rIdTheme" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme" Target="theme/theme1.xml"/>
571
+ ${allPivotTables.map(p => `<Relationship Id="${p.cacheRId}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotCacheDefinition" Target="pivotCache/pivotCacheDefinition${p.pivotIdx}.xml"/>`).join('\n')}
572
+ ${allSlicerCaches.map(sc => `<Relationship Id="${sc.rId}" Type="http://schemas.microsoft.com/office/2007/relationships/slicerCache" Target="slicerCaches/slicerCache${sc.idx}.xml"/>`).join('\n')}
573
+ ${hasVba ? '<Relationship Id="rIdVBA" Type="http://schemas.microsoft.com/office/2006/relationships/vbaProject" Target="vbaProject.bin"/>' : ''}
574
+ ${this.connections.length ? '<Relationship Id="rIdConns" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/connections" Target="connections.xml"/>' : ''}
575
+ ${this.externalLinks.map((_, i) => `<Relationship Id="rIdExtLink${i + 1}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/externalLink" Target="externalLinks/externalLink${i + 1}.xml"/>`).join('\n')}
576
+ ${hasCellImages ? '<Relationship Id="rIdMeta" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sheetMetadata" Target="metadata.xml"/>' : ''}
577
+ ${hasCellImages ? '<Relationship Id="rIdRichValueRel" Type="http://schemas.microsoft.com/office/2022/10/relationships/richValueRel" Target="richData/richValueRel.xml"/>' : ''}
578
+ ${hasCellImages ? '<Relationship Id="rIdRichValue" Type="http://schemas.microsoft.com/office/2017/06/relationships/rdRichValue" Target="richData/rdrichvalue.xml"/>' : ''}
579
+ ${hasCellImages ? '<Relationship Id="rIdRichValueStruct" Type="http://schemas.microsoft.com/office/2017/06/relationships/rdRichValueStructure" Target="richData/rdRichValueStructure.xml"/>' : ''}
580
+ ${hasCellImages ? '<Relationship Id="rIdRichValueTypes" Type="http://schemas.microsoft.com/office/2017/06/relationships/rdRichValueTypes" Target="richData/rdRichValueTypes.xml"/>' : ''}
581
+ ${hasCellImages ? '<Relationship Id="rIdRdArray" Type="http://schemas.microsoft.com/office/2017/06/relationships/rdArray" Target="richData/rdarray.xml"/>' : ''}
278
582
  </Relationships>`) });
279
- const date1904 = this.properties.date1904 ? `<workbookPr date1904="1"/>` : '<workbookPr/>';
280
- const namedRangesXml = this.namedRanges.length
281
- ? `<definedNames>${this.namedRanges.map(nr => `<definedName name="${escapeXml(nr.name)}"${nr.scope ? ` localSheetId="${this.sheets.findIndex(s => s.name === nr.scope)}"` : ''}>${escapeXml(nr.ref)}</definedName>`).join('')}</definedNames>` : '';
583
+ if (hasVba) {
584
+ this._ensureVbaSheetModules();
585
+ entries.push({ name: 'xl/vbaProject.bin', data: this.vbaProject.build() });
586
+ }
587
+ const wbPrAttrs = [
588
+ this.properties.date1904 ? 'date1904="1"' : '',
589
+ hasVba ? 'codeName="ThisWorkbook"' : '',
590
+ ].filter(Boolean).join(' ');
591
+ const date1904 = `<workbookPr${wbPrAttrs ? ' ' + wbPrAttrs : ''}/>`;
592
+ const namedRangesXml = this._definedNamesXml(allSlicerCaches);
593
+ const pivotCachesXml = allPivotTables.length
594
+ ? `<pivotCaches>${allPivotTables.map(p => `<pivotCache cacheId="${p.cacheId}" r:id="${p.cacheRId}"/>`).join('')}</pivotCaches>`
595
+ : '';
282
596
  entries.push({ name: 'xl/workbook.xml', data: strToBytes(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
283
597
  <workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
284
598
  ${date1904}
@@ -286,14 +600,39 @@ ${date1904}
286
600
  <sheets>${this.sheets.map((ws, i) => `<sheet name="${escapeXml(ws.name)}" sheetId="${i + 1}" r:id="${ws.rId}"${ws.options?.state && ws.options.state !== 'visible' ? ` state="${ws.options.state}"` : ''}/>`).join('')}</sheets>
287
601
  ${namedRangesXml}
288
602
  <calcPr calcId="191028"/>
603
+ ${pivotCachesXml}
604
+ ${hasSlicers ? (() => {
605
+ const tableSlicers = allSlicerCaches.filter(sc => sc.type === 'table');
606
+ const pivotSlicers = allSlicerCaches.filter(sc => sc.type === 'pivot');
607
+ let xml = '<extLst>';
608
+ if (pivotSlicers.length)
609
+ xml += `<ext uri="{BBE1A952-AA13-448e-AADC-164F8A28A991}" xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main"><x14:slicerCaches>${pivotSlicers.map(sc => `<x14:slicerCache r:id="${sc.rId}"/>`).join('')}</x14:slicerCaches></ext>`;
610
+ if (tableSlicers.length)
611
+ xml += `<ext uri="{46BE6895-7355-4a93-B00E-2C351335B9C9}" xmlns:x15="http://schemas.microsoft.com/office/spreadsheetml/2010/11/main" xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main"><x15:slicerCaches>${tableSlicers.map(sc => `<x14:slicerCache r:id="${sc.rId}"/>`).join('')}</x15:slicerCaches></ext>`;
612
+ xml += '</extLst>';
613
+ return xml;
614
+ })() : ''}
289
615
  </workbook>`) });
616
+ if (this.connections.length) {
617
+ entries.push({ name: 'xl/connections.xml', data: strToBytes(this._connectionsXml()) });
618
+ }
290
619
  for (let i = 0; i < this.sheets.length; i++) {
291
620
  const ws = this.sheets[i];
292
621
  const imgRIds = sheetImageRIds.get(ws) ?? [];
293
622
  const cRIds = sheetChartRIds.get(ws) ?? [];
294
623
  const tblEntries = allTables.filter(t => t.ws === ws);
295
624
  const tblRIds_ = sheetTableRIds.get(ws) ?? [];
296
- entries.push({ name: `xl/worksheets/sheet${i + 1}.xml`, data: strToBytes(ws.toXml(styles, shared)) });
625
+ const sheetFolder = ws._isChartSheet ? 'chartsheets' : ws._isDialogSheet ? 'dialogsheets' : 'worksheets';
626
+ const sheetPath = `xl/${sheetFolder}/sheet${i + 1}.xml`;
627
+ if (ws._isChartSheet) {
628
+ entries.push({ name: sheetPath, data: strToBytes(ws.toChartSheetXml()) });
629
+ }
630
+ else if (ws._isDialogSheet) {
631
+ entries.push({ name: sheetPath, data: strToBytes(ws.toDialogSheetXml(styles, shared)) });
632
+ }
633
+ else {
634
+ entries.push({ name: sheetPath, data: strToBytes(ws.toXml(styles, shared)) });
635
+ }
297
636
  const wsRels = [];
298
637
  if (ws.drawingRId) {
299
638
  const dIdx = this.sheets.filter((s, j) => j <= i && s.drawingRId).length;
@@ -307,17 +646,51 @@ ${namedRangesXml}
307
646
  for (let j = 0; j < tblEntries.length; j++) {
308
647
  wsRels.push(`<Relationship Id="${tblRIds_[j]}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/table" Target="../tables/table${tblEntries[j].globalTableId}.xml"/>`);
309
648
  }
649
+ const ptRIds_ = sheetPivotRIds.get(ws) ?? [];
650
+ const ptEntries = allPivotTables.filter(p => p.ws === ws);
651
+ for (let j = 0; j < ptEntries.length; j++) {
652
+ wsRels.push(`<Relationship Id="${ptRIds_[j]}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotTable" Target="../pivotTables/pivotTable${ptEntries[j].pivotIdx}.xml"/>`);
653
+ }
654
+ const slicerInfo = sheetSlicerMap.get(ws);
655
+ if (slicerInfo) {
656
+ wsRels.push(`<Relationship Id="${slicerInfo.slicerDefRId}" Type="http://schemas.microsoft.com/office/2007/relationships/slicer" Target="../slicers/slicer${slicerInfo.slicerDefIdx}.xml"/>`);
657
+ }
310
658
  const sheetComments = ws.getComments();
311
- if (sheetComments.length && ws.legacyDrawingRId) {
659
+ const sheetControls = ws.getFormControls();
660
+ if ((sheetComments.length || sheetControls.length) && ws.legacyDrawingRId) {
312
661
  const vIdx = vmlCtr++;
313
- const commRId = `rId${globalRId++}`;
314
662
  wsRels.push(`<Relationship Id="${ws.legacyDrawingRId}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing" Target="../drawings/vmlDrawing${vIdx}.vml"/>`);
315
- wsRels.push(`<Relationship Id="${commRId}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments" Target="../comments${vIdx}.xml"/>`);
316
- entries.push({ name: `xl/comments${vIdx}.xml`, data: strToBytes(this._buildCommentsXml(sheetComments)) });
317
- entries.push({ name: `xl/drawings/vmlDrawing${vIdx}.vml`, data: strToBytes(this._buildVmlXml(sheetComments, i)) });
663
+ if (sheetComments.length) {
664
+ const commRId = `rId${globalRId++}`;
665
+ wsRels.push(`<Relationship Id="${commRId}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments" Target="../comments${vIdx}.xml"/>`);
666
+ entries.push({ name: `xl/comments${vIdx}.xml`, data: strToBytes(this._buildCommentsXml(sheetComments)) });
667
+ }
668
+ const commentShapes = sheetComments.map(({ row, col }, ci) => {
669
+ const left = (col + 1) * 64;
670
+ const top = (row - 1) * 20;
671
+ const sid = 1025 + i * 1000 + ci;
672
+ return `<v:shape id="_x0000_s${sid}" type="#_x0000_t202" style="position:absolute;margin-left:${left}pt;margin-top:${top}pt;width:108pt;height:59.25pt;z-index:${ci + 1};visibility:hidden" fillcolor="#ffffe1" o:insetmode="auto">
673
+ <v:fill color2="#ffffe1"/>
674
+ <v:shadow color="black" obscured="t"/>
675
+ <v:path o:connecttype="none"/>
676
+ <v:textbox style="mso-direction-alt:auto"><div style="text-align:left"/></v:textbox>
677
+ <x:ClientData ObjectType="Note"><x:MoveWithCells/><x:SizeWithCells/><x:Anchor>${col + 1},15,${row - 1},10,${col + 3},15,${row + 4},4</x:Anchor><x:AutoFill>False</x:AutoFill><x:Row>${row - 1}</x:Row><x:Column>${col - 1}</x:Column></x:ClientData>
678
+ </v:shape>`;
679
+ });
680
+ const ctrlBaseId = 1025 + ws.sheetIndex * 1000 + sheetComments.length;
681
+ const controlShapes = sheetControls.map((ctrl, ci) => buildFormControlVmlShape(ctrl, ctrlBaseId + ci));
682
+ entries.push({ name: `xl/drawings/vmlDrawing${vIdx}.vml`, data: strToBytes(buildVmlWithControls(commentShapes, controlShapes)) });
683
+ if (!ws._isDialogSheet) {
684
+ const ctrlRIds = ws.ctrlPropRIds;
685
+ for (let ci = 0; ci < sheetControls.length; ci++) {
686
+ const ctrlPropIdx = ++ctrlPropGlobal;
687
+ wsRels.push(`<Relationship Id="${ctrlRIds[ci]}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/ctrlProp" Target="../ctrlProps/ctrlProp${ctrlPropIdx}.xml"/>`);
688
+ entries.push({ name: `xl/ctrlProps/ctrlProp${ctrlPropIdx}.xml`, data: strToBytes(buildCtrlPropXml(sheetControls[ci])) });
689
+ }
690
+ }
318
691
  }
319
692
  if (wsRels.length) {
320
- entries.push({ name: `xl/worksheets/_rels/sheet${i + 1}.xml.rels`, data: strToBytes(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
693
+ entries.push({ name: `xl/${sheetFolder}/_rels/sheet${i + 1}.xml.rels`, data: strToBytes(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
321
694
  <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
322
695
  ${wsRels.join('\n')}
323
696
  </Relationships>`) });
@@ -347,14 +720,105 @@ ${dRels.join('\n')}
347
720
  for (const { img, ext, idx } of allImages) {
348
721
  entries.push({ name: `xl/media/image${idx}.${ext}`, data: typeof img.data === 'string' ? base64ToBytes(img.data) : img.data });
349
722
  }
723
+ if (hasCellImages) {
724
+ const cellImgRIds = [];
725
+ const cellImgRels = [];
726
+ for (let ci = 0; ci < allCellImages.length; ci++) {
727
+ const { img, ext, idx } = allCellImages[ci];
728
+ const rId = `rId${ci + 1}`;
729
+ cellImgRIds.push(rId);
730
+ cellImgRels.push(`<Relationship Id="${rId}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="/xl/media/image${idx}.${ext}"/>`);
731
+ entries.push({ name: `xl/media/image${idx}.${ext}`, data: typeof img.data === 'string' ? base64ToBytes(img.data) : img.data });
732
+ }
733
+ entries.push({ name: 'xl/metadata.xml', data: strToBytes(buildMetadataXml(allCellImages.length)) });
734
+ entries.push({ name: 'xl/richData/rdrichvalue.xml', data: strToBytes(buildRichValueXml(allCellImages.length)) });
735
+ entries.push({ name: 'xl/richData/richValueRel.xml', data: strToBytes(buildRichValueRelXml(cellImgRIds)) });
736
+ entries.push({ name: 'xl/richData/rdRichValueStructure.xml', data: strToBytes(buildRichValueStructureXml()) });
737
+ entries.push({ name: 'xl/richData/rdRichValueTypes.xml', data: strToBytes(buildRichValueTypesXml()) });
738
+ entries.push({ name: 'xl/richData/rdarray.xml', data: strToBytes(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?><arrayData xmlns="http://schemas.microsoft.com/office/spreadsheetml/2017/richdata2" count="0"></arrayData>`) });
739
+ entries.push({ name: 'xl/richData/_rels/richValueRel.xml.rels', data: strToBytes(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
740
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
741
+ ${cellImgRels.join('\n')}
742
+ </Relationships>`) });
743
+ }
350
744
  for (const { ws, chartIdx, globalIdx } of allCharts) {
351
745
  entries.push({ name: `xl/charts/chart${globalIdx}.xml`, data: strToBytes(buildChartXml(ws.getCharts()[chartIdx])) });
352
746
  }
353
747
  for (const { ws, tableIdx, globalTableId } of allTables) {
354
748
  entries.push({ name: `xl/tables/table${globalTableId}.xml`, data: strToBytes(buildTableXml(ws.getTables()[tableIdx], globalTableId)) });
355
749
  }
750
+ for (const { ws, pt, pivotIdx, cacheId: cId } of allPivotTables) {
751
+ const sourceWs = this.sheets.find(s => s.name === pt.sourceSheet);
752
+ const sourceData = sourceWs ? sourceWs.readRange(pt.sourceRef) : [[]];
753
+ const { pivotTableXml, cacheDefXml, cacheRecordsXml } = buildPivotTableFiles(pt, sourceData, pivotIdx, cId);
754
+ const wbRel = (type, target) => `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">\n<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/${type}" Target="${target}"/>\n</Relationships>`;
755
+ entries.push({ name: `xl/pivotTables/pivotTable${pivotIdx}.xml`, data: strToBytes(pivotTableXml) });
756
+ entries.push({ name: `xl/pivotTables/_rels/pivotTable${pivotIdx}.xml.rels`, data: strToBytes(wbRel('pivotCacheDefinition', `../pivotCache/pivotCacheDefinition${pivotIdx}.xml`)) });
757
+ entries.push({ name: `xl/pivotCache/pivotCacheDefinition${pivotIdx}.xml`, data: strToBytes(cacheDefXml) });
758
+ entries.push({ name: `xl/pivotCache/_rels/pivotCacheDefinition${pivotIdx}.xml.rels`, data: strToBytes(wbRel('pivotCacheRecords', `pivotCacheRecords${pivotIdx}.xml`)) });
759
+ entries.push({ name: `xl/pivotCache/pivotCacheRecords${pivotIdx}.xml`, data: strToBytes(cacheRecordsXml) });
760
+ }
356
761
  entries.push({ name: 'xl/styles.xml', data: strToBytes(styles.toXml()) });
357
762
  entries.push({ name: 'xl/sharedStrings.xml', data: strToBytes(shared.toXml()) });
763
+ entries.push({ name: 'xl/theme/theme1.xml', data: strToBytes(this._buildThemeXml()) });
764
+ for (let i = 0; i < this.externalLinks.length; i++) {
765
+ const link = this.externalLinks[i];
766
+ const idx = i + 1;
767
+ const sheetsXml = link.sheets.map(s => {
768
+ const dNames = s.definedNames?.map(d => `<definedName name="${escapeXml(d.name)}" refersTo="${escapeXml(d.ref)}"/>`).join('') ?? '';
769
+ return `<sheetName val="${escapeXml(s.name)}"/>${dNames ? `<sheetDataSet>${dNames}</sheetDataSet>` : ''}`;
770
+ }).join('');
771
+ entries.push({ name: `xl/externalLinks/externalLink${idx}.xml`, data: strToBytes(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
772
+ <externalLink xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
773
+ <externalBook r:id="rId1"><sheetNames>${sheetsXml}</sheetNames></externalBook>
774
+ </externalLink>`) });
775
+ entries.push({ name: `xl/externalLinks/_rels/externalLink${idx}.xml.rels`, data: strToBytes(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
776
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
777
+ <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/externalLinkPath" Target="${escapeXml(link.target)}" TargetMode="External"/>
778
+ </Relationships>`) });
779
+ }
780
+ for (const [ws, info] of sheetSlicerMap) {
781
+ const allSheetSlicerItems = [];
782
+ for (const ts of info.tableSlicers) {
783
+ const s = ts.slicer;
784
+ allSheetSlicerItems.push(`<slicer name="${escapeXml(s.name)}" cache="${escapeXml(s.name + '_cache')}" caption="${escapeXml(s.caption ?? s.columnName)}" rowHeight="241300" columnCount="${s.columnCount ?? 1}" style="${s.style ?? 'SlicerStyleLight1'}"/>`);
785
+ }
786
+ for (const ps of info.pivotSlicers) {
787
+ const s = ps.slicer;
788
+ allSheetSlicerItems.push(`<slicer name="${escapeXml(s.name)}" cache="${escapeXml(s.name + '_cache')}" caption="${escapeXml(s.caption ?? s.fieldName)}" rowHeight="241300" columnCount="${s.columnCount ?? 1}" style="${s.style ?? 'SlicerStyleLight1'}"/>`);
789
+ }
790
+ entries.push({ name: `xl/slicers/slicer${info.slicerDefIdx}.xml`, data: strToBytes(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
791
+ <slicers xmlns="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:x="http://schemas.openxmlformats.org/spreadsheetml/2006/main" mc:Ignorable="x">
792
+ ${allSheetSlicerItems.join('\n')}
793
+ </slicers>`) });
794
+ }
795
+ for (const sc of allSlicerCaches) {
796
+ let cacheBody;
797
+ if (sc.type === 'table') {
798
+ cacheBody = `<extLst><x:ext uri="{2F2917AC-EB37-4324-AD4E-5DD8C200BD13}" xmlns:x15="http://schemas.microsoft.com/office/spreadsheetml/2010/11/main"><x15:tableSlicerCache tableId="${sc.tableId}" column="${sc.columnIndex}" sortOrder="${sc.sortOrder ?? 'ascending'}"/></x:ext></extLst>`;
799
+ }
800
+ else {
801
+ const itemsXml = (sc.items ?? []).map((_, xi) => `<i x="${xi}" s="1"/>`).join('');
802
+ cacheBody = `<pivotTables><pivotTable tabId="${sc.tabId}" name="${escapeXml(sc.pivotTableName ?? '')}"/></pivotTables>` +
803
+ (sc.items?.length ? `<data><tabular pivotCacheId="${sc.pivotCacheId}" showMissing="0"><items count="${sc.items.length}">${itemsXml}</items></tabular></data>` : '');
804
+ }
805
+ entries.push({ name: `xl/slicerCaches/slicerCache${sc.idx}.xml`, data: strToBytes(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
806
+ <slicerCacheDefinition xmlns="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main" xmlns:x="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="x" name="${escapeXml(sc.name)}" sourceName="${escapeXml(sc.sourceName)}">
807
+ ${cacheBody}
808
+ </slicerCacheDefinition>`) });
809
+ }
810
+ const allQueryTables = this.sheets.flatMap(ws => ws.getQueryTables());
811
+ for (let i = 0; i < allQueryTables.length; i++) {
812
+ const qt = allQueryTables[i];
813
+ entries.push({ name: `xl/queryTables/queryTable${i + 1}.xml`, data: strToBytes(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
814
+ <queryTable xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" name="${escapeXml(qt.name)}" connectionId="${qt.connectionId}" autoFormatId="16" applyNumberFormats="0" applyBorderFormats="0" applyFontFormats="0" applyPatternFormats="0" applyAlignmentFormats="0" applyWidthHeightFormats="0">
815
+ <queryTableRefresh nextId="${(qt.columns?.length ?? 0) + 1}">
816
+ <queryTableFields count="${qt.columns?.length ?? 0}">
817
+ ${(qt.columns ?? []).map((c, ci) => `<queryTableField id="${ci + 1}" name="${escapeXml(c)}" tableColumnId="${ci + 1}"/>`).join('\n')}
818
+ </queryTableFields>
819
+ </queryTableRefresh>
820
+ </queryTable>`) });
821
+ }
358
822
  const cp = { ...this.coreProperties, created: this.coreProperties.created ?? new Date(), modified: new Date() };
359
823
  if (!cp.creator && this.properties.author)
360
824
  cp.creator = this.properties.author;
@@ -364,12 +828,27 @@ ${dRels.join('\n')}
364
828
  application: this.extendedProperties.application ?? 'ExcelForge',
365
829
  company: this.extendedProperties.company ?? this.properties.company,
366
830
  titlesOfParts: this.sheets.map(s => s.name),
367
- headingPairs: [{ name: 'Worksheets', count: this.sheets.length }],
831
+ headingPairs: this._headingPairs(),
368
832
  })) });
369
833
  if (hasCustom)
370
834
  entries.push({ name: 'docProps/custom.xml', data: strToBytes(buildCustomXml(this.customProperties)) });
371
835
  return buildZip(entries, { level: this.compressionLevel });
372
836
  }
837
+ _headingPairs() {
838
+ const normalCount = this.sheets.filter(ws => !ws._isChartSheet && !ws._isDialogSheet).length;
839
+ const chartCount = this.sheets.filter(ws => ws._isChartSheet).length;
840
+ const dialogCount = this.sheets.filter(ws => ws._isDialogSheet).length;
841
+ const pairs = [];
842
+ if (normalCount)
843
+ pairs.push({ name: 'Worksheets', count: normalCount });
844
+ if (chartCount)
845
+ pairs.push({ name: 'Charts', count: chartCount });
846
+ if (dialogCount)
847
+ pairs.push({ name: 'Dialogs', count: dialogCount });
848
+ if (!pairs.length)
849
+ pairs.push({ name: 'Worksheets', count: 0 });
850
+ return pairs;
851
+ }
373
852
  _syncLegacyProperties() {
374
853
  const p = this.properties;
375
854
  if (p.title)
@@ -393,19 +872,101 @@ ${dRels.join('\n')}
393
872
  if (p.status)
394
873
  this.coreProperties.contentStatus ??= p.status;
395
874
  }
875
+ _ensureVbaSheetModules() {
876
+ if (!this.vbaProject)
877
+ return;
878
+ const existingDocModules = this.vbaProject.modules.filter(m => m.type === 'document' && m.name !== 'ThisWorkbook');
879
+ if (existingDocModules.length >= this.sheets.length)
880
+ return;
881
+ for (const ws of this.sheets) {
882
+ const sheetCodeName = ws.name.replace(/[^A-Za-z0-9_]/g, '_');
883
+ if (!this.vbaProject.getModule(sheetCodeName)) {
884
+ this.vbaProject.addModule({ name: sheetCodeName, type: 'document', code: '' });
885
+ }
886
+ }
887
+ }
396
888
  _patchWorkbookXml(originalXml) {
397
889
  let xml = originalXml;
398
890
  for (let i = 0; i < this.sheets.length; i++) {
399
891
  xml = xml.replace(new RegExp(`(<sheet[^>]+sheetId="${i + 1}"[^>]+)name="[^"]*"`), `$1name="${escapeXml(this.sheets[i].name)}"`);
400
892
  }
893
+ if (this.vbaProject && !xml.includes('codeName=')) {
894
+ xml = xml.replace('<workbookPr', '<workbookPr codeName="ThisWorkbook"');
895
+ if (!xml.includes('<workbookPr')) {
896
+ xml = xml.replace('<bookViews', '<workbookPr codeName="ThisWorkbook"/><bookViews');
897
+ }
898
+ }
899
+ const dnXml = this._definedNamesXml();
900
+ if (xml.includes('<definedNames')) {
901
+ xml = xml.replace(/<definedNames[\s\S]*?<\/definedNames>/, dnXml);
902
+ }
903
+ else if (dnXml) {
904
+ xml = xml.replace('</sheets>', `</sheets>${dnXml}`);
905
+ }
401
906
  return xml;
402
907
  }
403
- _buildWorkbookRels(rr) {
404
- const rels = [...rr.workbookRels.entries()].map(([id, rel]) => `<Relationship Id="${id}" Type="${rel.type}" Target="${rel.target}"/>`);
908
+ _definedNamesXml(slicerCaches) {
909
+ const printAreaNames = [];
910
+ for (const ws of this.sheets) {
911
+ if (ws.printArea) {
912
+ const ref = ws.printArea.includes('!') ? ws.printArea : `'${ws.name}'!${ws.printArea}`;
913
+ printAreaNames.push({ name: '_xlnm.Print_Area', ref, scope: ws.name });
914
+ }
915
+ }
916
+ const slicerNames = (slicerCaches ?? []).map(sc => ({ name: sc.name, ref: '#N/A' }));
917
+ const all = [...this.namedRanges, ...printAreaNames, ...slicerNames];
918
+ if (!all.length)
919
+ return '';
920
+ return `<definedNames>${all.map(nr => {
921
+ let attrs = `name="${escapeXml(nr.name)}"`;
922
+ if (nr.scope) {
923
+ const idx = this.sheets.findIndex(s => s.name === nr.scope);
924
+ if (idx >= 0)
925
+ attrs += ` localSheetId="${idx}"`;
926
+ }
927
+ if (nr.comment)
928
+ attrs += ` comment="${escapeXml(nr.comment)}"`;
929
+ return `<definedName ${attrs}>${escapeXml(nr.ref)}</definedName>`;
930
+ }).join('')}</definedNames>`;
931
+ }
932
+ _connectionsXml(originalXml) {
933
+ if (!this.connections.length)
934
+ return '';
935
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
936
+ <connections xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">${this.connections.map(c => {
937
+ if (c._rawXml)
938
+ return c._rawXml;
939
+ let attrs = ` id="${c.id}" name="${escapeXml(c.name)}" type="${connTypeToNum(c.type)}" refreshedVersion="6"`;
940
+ if (c.description)
941
+ attrs += ` description="${escapeXml(c.description)}"`;
942
+ if (c.refreshOnLoad)
943
+ attrs += ' refreshOnLoad="1"';
944
+ if (c.background)
945
+ attrs += ' background="1"';
946
+ if (c.saveData)
947
+ attrs += ' saveData="1"';
948
+ if (c.keepAlive)
949
+ attrs += ' keepAlive="1"';
950
+ if (c.interval)
951
+ attrs += ` interval="${c.interval}"`;
952
+ const dbPr = c.connectionString || c.command
953
+ ? `<dbPr${c.connectionString ? ` connection="${escapeXml(c.connectionString)}"` : ''}${c.command ? ` command="${escapeXml(c.command)}"` : ''}${c.commandType ? ` commandType="${cmdTypeToNum(c.commandType)}"` : ''}/>`
954
+ : '';
955
+ return `<connection${attrs}>${dbPr}</connection>`;
956
+ }).join('')}</connections>`;
957
+ }
958
+ _buildWorkbookRels(rr, dropCalcChain = false) {
959
+ const rels = [...rr.workbookRels.entries()]
960
+ .filter(([_, rel]) => !(dropCalcChain && rel.type.includes('/calcChain')))
961
+ .map(([id, rel]) => `<Relationship Id="${id}" Type="${rel.type}" Target="${rel.target}"/>`);
405
962
  if (![...rr.workbookRels.values()].some(r => r.type.includes('/styles')))
406
963
  rels.push(`<Relationship Id="rIdStyles" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>`);
407
964
  if (![...rr.workbookRels.values()].some(r => r.type.includes('/sharedStrings')))
408
965
  rels.push(`<Relationship Id="rIdShared" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" Target="sharedStrings.xml"/>`);
966
+ if (this.vbaProject && ![...rr.workbookRels.values()].some(r => r.type.includes('vbaProject')))
967
+ rels.push(`<Relationship Id="rIdVBA" Type="http://schemas.microsoft.com/office/2006/relationships/vbaProject" Target="vbaProject.bin"/>`);
968
+ if (this.connections.length && ![...rr.workbookRels.values()].some(r => r.type.includes('/connections')))
969
+ rels.push(`<Relationship Id="rIdConns" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/connections" Target="connections.xml"/>`);
409
970
  return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
410
971
  <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
411
972
  ${rels.join('\n')}
@@ -414,8 +975,51 @@ ${rels.join('\n')}
414
975
  _relsToXml(relMap) {
415
976
  return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
416
977
  <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
417
- ${[...relMap.entries()].map(([id, r]) => `<Relationship Id="${id}" Type="${r.type}" Target="${r.target}"/>`).join('\n')}
978
+ ${[...relMap.entries()].map(([id, r]) => `<Relationship Id="${escapeXml(id)}" Type="${escapeXml(r.type)}" Target="${escapeXml(r.target)}"${r.targetMode ? ` TargetMode="${escapeXml(r.targetMode)}"` : ''}/>`).join('\n')}
418
979
  </Relationships>`;
980
+ }
981
+ _buildThemeXml() {
982
+ const t = this.theme;
983
+ const majorFont = t?.majorFont ?? 'Calibri Light';
984
+ const minorFont = t?.minorFont ?? 'Calibri';
985
+ const defaultColors = [
986
+ { name: 'dk1', color: '000000' }, { name: 'lt1', color: 'FFFFFF' },
987
+ { name: 'dk2', color: '44546A' }, { name: 'lt2', color: 'E7E6E6' },
988
+ { name: 'accent1', color: '4472C4' }, { name: 'accent2', color: 'ED7D31' },
989
+ { name: 'accent3', color: 'A5A5A5' }, { name: 'accent4', color: 'FFC000' },
990
+ { name: 'accent5', color: '5B9BD5' }, { name: 'accent6', color: '70AD47' },
991
+ { name: 'hlink', color: '0563C1' }, { name: 'folHlink', color: '954F72' },
992
+ ];
993
+ const colors = t?.colors?.map(c => {
994
+ let hex = c.color.replace(/^#/, '');
995
+ if (hex.length === 8)
996
+ hex = hex.substring(2);
997
+ return { name: c.name, color: hex };
998
+ }) ?? defaultColors;
999
+ const colorElements = colors.map(c => {
1000
+ if (c.name === 'dk1' || c.name === 'lt1') {
1001
+ return `<a:${c.name}><a:sysClr val="${c.name === 'dk1' ? 'windowText' : 'window'}" lastClr="${c.color}"/></a:${c.name}>`;
1002
+ }
1003
+ return `<a:${c.name}><a:srgbClr val="${c.color}"/></a:${c.name}>`;
1004
+ }).join('');
1005
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
1006
+ <a:theme xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" name="${escapeXml(t?.name ?? 'Office Theme')}">
1007
+ <a:themeElements>
1008
+ <a:clrScheme name="Office">${colorElements}</a:clrScheme>
1009
+ <a:fontScheme name="Office">
1010
+ <a:majorFont><a:latin typeface="${escapeXml(majorFont)}"/><a:ea typeface=""/><a:cs typeface=""/></a:majorFont>
1011
+ <a:minorFont><a:latin typeface="${escapeXml(minorFont)}"/><a:ea typeface=""/><a:cs typeface=""/></a:minorFont>
1012
+ </a:fontScheme>
1013
+ <a:fmtScheme name="Office">
1014
+ <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:satMod val="103000"/><a:lumMod val="102000"/><a:tint val="94000"/></a:schemeClr></a:gs><a:gs pos="50000"><a:schemeClr val="phClr"><a:satMod val="110000"/><a:lumMod val="100000"/><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>
1015
+ <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>
1016
+ <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>
1017
+ <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:tint val="93000"/><a:satMod val="150000"/><a:shade val="98000"/><a:lumMod val="102000"/></a:schemeClr></a:gs><a:gs pos="50000"><a:schemeClr val="phClr"><a:tint val="98000"/><a:satMod val="130000"/><a:shade val="90000"/><a:lumMod val="103000"/></a:schemeClr></a:gs><a:gs pos="100000"><a:schemeClr val="phClr"><a:shade val="63000"/><a:satMod val="120000"/></a:schemeClr></a:gs></a:gsLst><a:lin ang="5400000" scaled="0"/></a:gradFill></a:bgFillStyleLst>
1018
+ </a:fmtScheme>
1019
+ </a:themeElements>
1020
+ <a:objectDefaults/>
1021
+ <a:extraClrSchemeLst/>
1022
+ </a:theme>`;
419
1023
  }
420
1024
  _buildRootRels(hasCustom) {
421
1025
  return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
@@ -426,12 +1030,24 @@ ${[...relMap.entries()].map(([id, r]) => `<Relationship Id="${id}" Type="${r.typ
426
1030
  ${hasCustom ? `<Relationship Id="rId4" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties" Target="docProps/custom.xml"/>` : ''}
427
1031
  </Relationships>`;
428
1032
  }
429
- _patchContentTypes(originalXml, addCustom) {
1033
+ _patchContentTypes(originalXml, addCustom, dropCalcChain = false) {
430
1034
  let xml = originalXml;
1035
+ if (dropCalcChain)
1036
+ xml = xml.replace(/<Override[^>]*calcChain[^>]*\/>/g, '');
431
1037
  if (!xml.includes('sharedStrings'))
432
1038
  xml = xml.replace('</Types>', `<Override PartName="/xl/sharedStrings.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"/>\n</Types>`);
433
1039
  if (addCustom && !xml.includes('custom-properties'))
434
1040
  xml = xml.replace('</Types>', `<Override PartName="/docProps/custom.xml" ContentType="application/vnd.openxmlformats-officedocument.custom-properties+xml"/>\n</Types>`);
1041
+ if (this.vbaProject) {
1042
+ if (!xml.includes('vbaProject.bin'))
1043
+ xml = xml.replace('</Types>', `<Override PartName="/xl/vbaProject.bin" ContentType="application/vnd.ms-office.vbaProject"/>\n</Types>`);
1044
+ xml = xml.replace('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml', 'application/vnd.ms-excel.sheet.macroEnabled.main+xml');
1045
+ }
1046
+ if (this.isTemplate && !this.vbaProject) {
1047
+ xml = xml.replace('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml', 'application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml');
1048
+ }
1049
+ if (this.connections.length && !xml.includes('connections.xml'))
1050
+ xml = xml.replace('</Types>', `<Override PartName="/xl/connections.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.connections+xml"/>\n</Types>`);
435
1051
  return xml;
436
1052
  }
437
1053
  async buildBase64() {
@@ -452,7 +1068,37 @@ ${hasCustom ? `<Relationship Id="rId4" Type="http://schemas.openxmlformats.org/o
452
1068
  const commentsXml = comments.map(({ row, col, comment }) => {
453
1069
  const ref = `${colIndexToLetter(col)}${row}`;
454
1070
  const authorIdx = authors.indexOf(comment.author ?? '');
455
- return `<comment ref="${ref}" authorId="${authorIdx}"><text><r><t>${escapeXml(comment.text)}</t></r></text></comment>`;
1071
+ let textXml;
1072
+ if (comment.richText && comment.richText.length > 0) {
1073
+ textXml = comment.richText.map(run => {
1074
+ let rPr = '';
1075
+ if (run.font) {
1076
+ const f = run.font;
1077
+ if (f.bold)
1078
+ rPr += '<b/>';
1079
+ if (f.italic)
1080
+ rPr += '<i/>';
1081
+ if (f.underline && f.underline !== 'none')
1082
+ rPr += `<u val="${f.underline === 'single' ? 'single' : f.underline}"/>`;
1083
+ if (f.strike)
1084
+ rPr += '<strike/>';
1085
+ if (f.size)
1086
+ rPr += `<sz val="${f.size}"/>`;
1087
+ if (f.color)
1088
+ rPr += `<color rgb="${f.color}"/>`;
1089
+ if (f.name)
1090
+ rPr += `<rFont val="${escapeXml(f.name)}"/>`;
1091
+ if (f.family != null)
1092
+ rPr += `<family val="${f.family}"/>`;
1093
+ }
1094
+ const rPrTag = rPr ? `<rPr>${rPr}</rPr>` : '';
1095
+ return `<r>${rPrTag}<t xml:space="preserve">${escapeXml(run.text)}</t></r>`;
1096
+ }).join('');
1097
+ }
1098
+ else {
1099
+ textXml = `<r><t>${escapeXml(comment.text)}</t></r>`;
1100
+ }
1101
+ return `<comment ref="${ref}" authorId="${authorIdx}"><text>${textXml}</text></comment>`;
456
1102
  }).join('');
457
1103
  return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
458
1104
  <comments xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
@@ -489,4 +1135,74 @@ ${shapes}
489
1135
  URL.revokeObjectURL(url);
490
1136
  }
491
1137
  }
1138
+ function imageContentType(ext) {
1139
+ switch (ext) {
1140
+ case 'jpg': return 'image/jpeg';
1141
+ case 'png': return 'image/png';
1142
+ case 'gif': return 'image/gif';
1143
+ case 'bmp': return 'image/bmp';
1144
+ case 'tiff': return 'image/tiff';
1145
+ case 'emf': return 'image/x-emf';
1146
+ case 'wmf': return 'image/x-wmf';
1147
+ case 'svg': return 'image/svg+xml';
1148
+ case 'ico': return 'image/x-icon';
1149
+ case 'webp': return 'image/webp';
1150
+ default: return `image/${ext}`;
1151
+ }
1152
+ }
1153
+ function imageExt(format) {
1154
+ return format === 'jpeg' ? 'jpg' : format;
1155
+ }
1156
+ function buildMetadataXml(count) {
1157
+ const bks = Array.from({ length: count }, (_, i) => `<bk><extLst><ext uri="{3e2802c4-a4d2-4d8b-9148-e3be6c30e623}"><xlrd:rvb i="${i}"/></ext></extLst></bk>`).join('');
1158
+ const cellBks = Array.from({ length: count }, (_, i) => `<bk><rc t="1" v="${i}"/></bk>`).join('');
1159
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>` +
1160
+ `<metadata xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"` +
1161
+ ` xmlns:xlrd="http://schemas.microsoft.com/office/spreadsheetml/2017/richdata">` +
1162
+ `<metadataTypes count="1">` +
1163
+ `<metadataType name="XLRICHVALUE" minSupportedVersion="120000" copy="1" pasteAll="1" pasteValues="1" merge="1" splitFirst="1" rowColShift="1" clearFormats="1" clearComments="1" assign="1" coerce="1"/>` +
1164
+ `</metadataTypes>` +
1165
+ `<futureMetadata name="XLRICHVALUE" count="${count}">${bks}</futureMetadata>` +
1166
+ `<valueMetadata count="${count}">${cellBks}</valueMetadata>` +
1167
+ `</metadata>`;
1168
+ }
1169
+ function buildRichValueXml(count) {
1170
+ const rvs = Array.from({ length: count }, (_, i) => `<rv s="0"><v>${i}</v><v>5</v><v></v></rv>`).join('');
1171
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>` +
1172
+ `<rvData xmlns="http://schemas.microsoft.com/office/spreadsheetml/2017/richdata" count="${count}">${rvs}</rvData>`;
1173
+ }
1174
+ function buildRichValueRelXml(rIds) {
1175
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>` +
1176
+ `<richValueRels xmlns="http://schemas.microsoft.com/office/spreadsheetml/2022/richvaluerel"` +
1177
+ ` xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">` +
1178
+ rIds.map(id => `<rel r:id="${id}"/>`).join('') +
1179
+ `</richValueRels>`;
1180
+ }
1181
+ function buildRichValueStructureXml() {
1182
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>` +
1183
+ `<rvStructures xmlns="http://schemas.microsoft.com/office/spreadsheetml/2017/richdata" count="1">` +
1184
+ `<s t="_localImage"><k n="_rvRel:LocalImageIdentifier" t="i"/><k n="CalcOrigin" t="i"/><k n="Text" t="s"/></s>` +
1185
+ `</rvStructures>`;
1186
+ }
1187
+ function buildRichValueTypesXml() {
1188
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>` +
1189
+ `<rvTypesInfo xmlns="http://schemas.microsoft.com/office/spreadsheetml/2017/richdata2"` +
1190
+ ` xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"` +
1191
+ ` mc:Ignorable="x"` +
1192
+ ` xmlns:x="http://schemas.openxmlformats.org/spreadsheetml/2006/main">` +
1193
+ `<global><keyFlags>` +
1194
+ `<key name="_Self"><flag name="ExcludeFromFile" value="1"/><flag name="ExcludeFromCalcComparison" value="1"/></key>` +
1195
+ `<key name="_DisplayString"><flag name="ExcludeFromCalcComparison" value="1"/></key>` +
1196
+ `<key name="_Flags"><flag name="ExcludeFromCalcComparison" value="1"/></key>` +
1197
+ `<key name="_Format"><flag name="ExcludeFromCalcComparison" value="1"/></key>` +
1198
+ `<key name="_SubLabel"><flag name="ExcludeFromCalcComparison" value="1"/></key>` +
1199
+ `<key name="_Attribution"><flag name="ExcludeFromCalcComparison" value="1"/></key>` +
1200
+ `<key name="_Icon"><flag name="ExcludeFromCalcComparison" value="1"/></key>` +
1201
+ `<key name="_Display"><flag name="ExcludeFromCalcComparison" value="1"/></key>` +
1202
+ `<key name="_CanonicalPropertyNames"><flag name="ExcludeFromCalcComparison" value="1"/></key>` +
1203
+ `<key name="_ClassificationId"><flag name="ExcludeFromCalcComparison" value="1"/></key>` +
1204
+ `</keyFlags></global>` +
1205
+ `<types></types>` +
1206
+ `</rvTypesInfo>`;
1207
+ }
492
1208
  //# sourceMappingURL=Workbook.js.map