@node-projects/excelforge 2.4.0 → 3.1.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 (74) hide show
  1. package/.github/FUNDING.yml +4 -0
  2. package/FEATURES.md +294 -0
  3. package/README.md +628 -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 +43 -1
  7. package/dist/core/Workbook.js +881 -58
  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 +136 -2
  13. package/dist/core/Worksheet.js +828 -63
  14. package/dist/core/Worksheet.js.map +1 -1
  15. package/dist/core/types.d.ts +311 -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 +22 -0
  34. package/dist/features/HtmlModule.js +1441 -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/PdfModule.d.ts +30 -0
  40. package/dist/features/PdfModule.js +1567 -0
  41. package/dist/features/PdfModule.js.map +1 -0
  42. package/dist/features/PivotTableBuilder.d.ts +7 -0
  43. package/dist/features/PivotTableBuilder.js +170 -0
  44. package/dist/features/PivotTableBuilder.js.map +1 -0
  45. package/dist/features/Signing.d.ts +12 -0
  46. package/dist/features/Signing.js +326 -0
  47. package/dist/features/Signing.js.map +1 -0
  48. package/dist/features/TableBuilder.js +2 -2
  49. package/dist/features/TableBuilder.js.map +1 -1
  50. package/dist/index-min.js +609 -147
  51. package/dist/index.d.ts +19 -1
  52. package/dist/index.js +11 -0
  53. package/dist/index.js.map +1 -1
  54. package/dist/styles/StyleRegistry.d.ts +14 -0
  55. package/dist/styles/StyleRegistry.js +95 -30
  56. package/dist/styles/StyleRegistry.js.map +1 -1
  57. package/dist/utils/helpers.d.ts +4 -0
  58. package/dist/utils/helpers.js +64 -14
  59. package/dist/utils/helpers.js.map +1 -1
  60. package/dist/utils/zip.js +145 -73
  61. package/dist/utils/zip.js.map +1 -1
  62. package/dist/vba/VbaProject.d.ts +31 -0
  63. package/dist/vba/VbaProject.js +576 -0
  64. package/dist/vba/VbaProject.js.map +1 -0
  65. package/dist/vba/cfb.d.ts +7 -0
  66. package/dist/vba/cfb.js +352 -0
  67. package/dist/vba/cfb.js.map +1 -0
  68. package/dist/vba/ovba.d.ts +2 -0
  69. package/dist/vba/ovba.js +137 -0
  70. package/dist/vba/ovba.js.map +1 -0
  71. package/package.json +4 -3
  72. package/validator.cs +0 -155
  73. package/validatorEpplus.cs +0 -27
  74. package/validatorReadData.cs +0 -111
@@ -1,4 +1,16 @@
1
+ import { CellError, } from '../core/types.js';
1
2
  import { colIndexToLetter, indicesToCellRef, cellRefToIndices, parseRange, escapeXml, dateToSerial, pxToEmu, } from '../utils/helpers.js';
3
+ const _xmlContentRe = /[&<>]/g;
4
+ const _xmlContentEsc = { '&': '&amp;', '<': '&lt;', '>': '&gt;' };
5
+ function escapeXmlContent(s) {
6
+ return _xmlContentRe.test(s) ? (_xmlContentRe.lastIndex = 0, s.replace(_xmlContentRe, ch => _xmlContentEsc[ch])) : s;
7
+ }
8
+ function colorEl(c) {
9
+ if (c.startsWith('theme:'))
10
+ return `<color theme="${c.slice(6)}"/>`;
11
+ const rgb = c.startsWith('#') ? 'FF' + c.slice(1) : c;
12
+ return `<color rgb="${rgb}"/>`;
13
+ }
2
14
  const SUBTOTAL_FN = {
3
15
  average: 101, count: 102, countNums: 103, max: 104, min: 105,
4
16
  stdDev: 107, sum: 109, var: 110, vars: 111,
@@ -8,34 +20,67 @@ export class Worksheet {
8
20
  this.cells = new Map();
9
21
  this.merges = [];
10
22
  this.images = [];
23
+ this.cellImages = [];
11
24
  this.charts = [];
12
25
  this.conditionalFormats = [];
13
26
  this.tables = [];
27
+ this.pivotTables = [];
14
28
  this.sparklines = [];
29
+ this.formControls = [];
30
+ this.shapes = [];
31
+ this.wordArt = [];
32
+ this.queryTables = [];
33
+ this.tableSlicers = [];
34
+ this.mathEquations = [];
35
+ this.oleObjects = [];
15
36
  this.colDefs = new Map();
16
37
  this.rowDefs = new Map();
17
38
  this.dataValidations = new Map();
39
+ this.rowBreaks = [];
40
+ this.colBreaks = [];
41
+ this.preservedXml = [];
42
+ this._nextSharedIdx = 0;
43
+ this._isChartSheet = false;
44
+ this._isDialogSheet = false;
18
45
  this.sheetIndex = 0;
19
46
  this.rId = '';
20
47
  this.drawingRId = '';
21
48
  this.legacyDrawingRId = '';
22
49
  this.tableRIds = [];
50
+ this.ctrlPropRIds = [];
51
+ this.slicerRId = '';
52
+ this._slicerDrawingInfo = [];
53
+ this._cellImageVm = new Map();
54
+ this.ignoreErrors = [];
55
+ this.oleRIds = [];
56
+ this.oleIconRIds = [];
23
57
  this.name = name;
24
58
  this.options = { ...options, name };
25
59
  }
26
- key(row, col) { return `${row},${col}`; }
27
60
  getCell(row, col) {
28
- const k = this.key(row, col);
29
- if (!this.cells.has(k))
30
- this.cells.set(k, {});
31
- return this.cells.get(k);
61
+ let rowMap = this.cells.get(row);
62
+ if (!rowMap) {
63
+ rowMap = new Map();
64
+ this.cells.set(row, rowMap);
65
+ }
66
+ let cell = rowMap.get(col);
67
+ if (!cell) {
68
+ cell = {};
69
+ rowMap.set(col, cell);
70
+ }
71
+ return cell;
32
72
  }
33
73
  getCellByRef(ref) {
34
74
  const { row, col } = cellRefToIndices(ref);
35
75
  return this.getCell(row, col);
36
76
  }
37
77
  setCell(row, col, cell) {
38
- this.cells.set(this.key(row, col), cell);
78
+ let rowMap = this.cells.get(row);
79
+ if (!rowMap) {
80
+ rowMap = new Map();
81
+ this.cells.set(row, rowMap);
82
+ }
83
+ rowMap.set(col, cell);
39
84
  return this;
40
85
  }
41
86
  setValue(row, col, value) {
@@ -46,6 +91,29 @@ export class Worksheet {
46
91
  this.getCell(row, col).formula = formula;
47
92
  return this;
48
93
  }
94
+ setDynamicArrayFormula(row, col, formula) {
95
+ const cell = this.getCell(row, col);
96
+ cell.arrayFormula = formula;
97
+ cell._dynamic = true;
98
+ return this;
99
+ }
100
+ setSharedFormula(masterRow, masterCol, formula, rangeRef) {
101
+ const si = this._nextSharedIdx++;
102
+ const master = this.getCell(masterRow, masterCol);
103
+ master.formula = formula;
104
+ master._sharedRef = rangeRef;
105
+ master._sharedIdx = si;
106
+ const { startRow, startCol, endRow, endCol } = parseRange(rangeRef);
107
+ for (let r = startRow; r <= endRow; r++) {
108
+ for (let c = startCol; c <= endCol; c++) {
109
+ if (r === masterRow && c === masterCol)
110
+ continue;
111
+ const dep = this.getCell(r, c);
112
+ dep._sharedIdx = si;
113
+ }
114
+ }
115
+ return this;
116
+ }
49
117
  setStyle(row, col, style) {
50
118
  this.getCell(row, col).style = style;
51
119
  return this;
@@ -94,17 +162,24 @@ export class Worksheet {
94
162
  const ec = cellRefToIndices(e);
95
163
  return this.merge(sc.row, sc.col, ec.row, ec.col);
96
164
  }
165
+ getMerges() { return this.merges; }
97
166
  addImage(img) {
98
167
  this.images.push(img);
99
168
  return this;
100
169
  }
101
170
  getImages() { return this.images; }
171
+ addCellImage(img) {
172
+ this.cellImages.push(img);
173
+ return this;
174
+ }
175
+ getCellImages() { return this.cellImages; }
102
176
  getComments() {
103
177
  const out = [];
104
- for (const [key, cell] of this.cells) {
105
- if (cell.comment) {
106
- const [r, c] = key.split(',').map(Number);
107
- out.push({ row: r, col: c, comment: cell.comment });
178
+ for (const [r, rowMap] of this.cells) {
179
+ for (const [c, cell] of rowMap) {
180
+ if (cell.comment) {
181
+ out.push({ row: r, col: c, comment: cell.comment });
182
+ }
108
183
  }
109
184
  }
110
185
  return out;
@@ -118,6 +193,8 @@ export class Worksheet {
118
193
  this.conditionalFormats.push(cf);
119
194
  return this;
120
195
  }
196
+ getConditionalFormats() { return this.conditionalFormats; }
197
+ getDataValidations() { return this.dataValidations; }
121
198
  addTable(table) {
122
199
  this.tables.push(table);
123
200
  if (table.totalsRow && table.columns?.length) {
@@ -141,14 +218,350 @@ export class Worksheet {
141
218
  return this;
142
219
  }
143
220
  getTables() { return this.tables; }
221
+ addPivotTable(pt) {
222
+ this.pivotTables.push(pt);
223
+ return this;
224
+ }
225
+ getPivotTables() { return this.pivotTables; }
226
+ readRange(ref) {
227
+ const { startRow, startCol, endRow, endCol } = parseRange(ref);
228
+ const result = [];
229
+ for (let r = startRow; r <= endRow; r++) {
230
+ const row = [];
231
+ const rowMap = this.cells.get(r);
232
+ for (let c = startCol; c <= endCol; c++) {
233
+ const cell = rowMap?.get(c);
234
+ row.push(cell?.value ?? null);
235
+ }
236
+ result.push(row);
237
+ }
238
+ return result;
239
+ }
240
+ readAllCells() {
241
+ const out = [];
242
+ for (const [r, rowMap] of this.cells) {
243
+ for (const [c, cell] of rowMap) {
244
+ out.push({ row: r, col: c, cell });
245
+ }
246
+ }
247
+ return out;
248
+ }
249
+ getUsedRange() {
250
+ let minR = Infinity, maxR = 0, minC = Infinity, maxC = 0;
251
+ for (const [r, rowMap] of this.cells) {
252
+ for (const [c] of rowMap) {
253
+ if (r < minR)
254
+ minR = r;
255
+ if (r > maxR)
256
+ maxR = r;
257
+ if (c < minC)
258
+ minC = c;
259
+ if (c > maxC)
260
+ maxC = c;
261
+ }
262
+ }
263
+ return maxR === 0 ? null : { startRow: minR, startCol: minC, endRow: maxR, endCol: maxC };
264
+ }
265
+ getColumn(col) {
266
+ return this.colDefs.get(col);
267
+ }
268
+ getRow(row) {
269
+ return this.rowDefs.get(row);
270
+ }
271
+ insertRows(atRow, count) {
272
+ const rows = [...this.cells.keys()].filter(r => r >= atRow).sort((a, b) => b - a);
273
+ for (const r of rows) {
274
+ const rowMap = this.cells.get(r);
275
+ this.cells.delete(r);
276
+ this.cells.set(r + count, rowMap);
277
+ }
278
+ const rdKeys = [...this.rowDefs.keys()].filter(r => r >= atRow).sort((a, b) => b - a);
279
+ for (const r of rdKeys) {
280
+ const d = this.rowDefs.get(r);
281
+ this.rowDefs.delete(r);
282
+ this.rowDefs.set(r + count, d);
283
+ }
284
+ for (const m of this.merges) {
285
+ if (m.startRow >= atRow)
286
+ m.startRow += count;
287
+ if (m.endRow >= atRow)
288
+ m.endRow += count;
289
+ }
290
+ return this;
291
+ }
292
+ deleteRows(atRow, count) {
293
+ for (let r = atRow; r < atRow + count; r++) {
294
+ this.cells.delete(r);
295
+ this.rowDefs.delete(r);
296
+ }
297
+ const rows = [...this.cells.keys()].filter(r => r >= atRow + count).sort((a, b) => a - b);
298
+ for (const r of rows) {
299
+ const rowMap = this.cells.get(r);
300
+ this.cells.delete(r);
301
+ this.cells.set(r - count, rowMap);
302
+ }
303
+ const rdKeys = [...this.rowDefs.keys()].filter(r => r >= atRow + count).sort((a, b) => a - b);
304
+ for (const r of rdKeys) {
305
+ const d = this.rowDefs.get(r);
306
+ this.rowDefs.delete(r);
307
+ this.rowDefs.set(r - count, d);
308
+ }
309
+ this.merges = this.merges.filter(m => !(m.startRow >= atRow && m.endRow < atRow + count));
310
+ for (const m of this.merges) {
311
+ if (m.startRow >= atRow + count)
312
+ m.startRow -= count;
313
+ if (m.endRow >= atRow + count)
314
+ m.endRow -= count;
315
+ }
316
+ return this;
317
+ }
318
+ insertColumns(atCol, count) {
319
+ for (const [, rowMap] of this.cells) {
320
+ const cols = [...rowMap.keys()].filter(c => c >= atCol).sort((a, b) => b - a);
321
+ for (const c of cols) {
322
+ const cell = rowMap.get(c);
323
+ rowMap.delete(c);
324
+ rowMap.set(c + count, cell);
325
+ }
326
+ }
327
+ const cdKeys = [...this.colDefs.keys()].filter(c => c >= atCol).sort((a, b) => b - a);
328
+ for (const c of cdKeys) {
329
+ const d = this.colDefs.get(c);
330
+ this.colDefs.delete(c);
331
+ this.colDefs.set(c + count, d);
332
+ }
333
+ for (const m of this.merges) {
334
+ if (m.startCol >= atCol)
335
+ m.startCol += count;
336
+ if (m.endCol >= atCol)
337
+ m.endCol += count;
338
+ }
339
+ return this;
340
+ }
341
+ copyRange(srcRef, targetRow, targetCol) {
342
+ const { startRow, startCol, endRow, endCol } = parseRange(srcRef);
343
+ for (let r = startRow; r <= endRow; r++) {
344
+ for (let c = startCol; c <= endCol; c++) {
345
+ const src = this.getCell(r, c);
346
+ const dr = targetRow + (r - startRow);
347
+ const dc = targetCol + (c - startCol);
348
+ if (src.value != null)
349
+ this.setValue(dr, dc, src.value);
350
+ if (src.formula)
351
+ this.setFormula(dr, dc, src.formula);
352
+ if (src.style)
353
+ this.setStyle(dr, dc, { ...src.style });
354
+ }
355
+ }
356
+ return this;
357
+ }
358
+ moveRange(srcRef, targetRow, targetCol) {
359
+ const { startRow, startCol, endRow, endCol } = parseRange(srcRef);
360
+ this.copyRange(srcRef, targetRow, targetCol);
361
+ const tEndRow = targetRow + (endRow - startRow);
362
+ const tEndCol = targetCol + (endCol - startCol);
363
+ for (let r = startRow; r <= endRow; r++) {
364
+ for (let c = startCol; c <= endCol; c++) {
365
+ const dr = targetRow + (r - startRow);
366
+ const dc = targetCol + (c - startCol);
367
+ if (dr === r && dc === c)
368
+ continue;
369
+ const rowMap = this.cells.get(r);
370
+ if (rowMap)
371
+ rowMap.delete(c);
372
+ }
373
+ }
374
+ return this;
375
+ }
376
+ sortRange(ref, sortCol, order = 'asc') {
377
+ const { startRow, startCol, endRow, endCol } = parseRange(ref);
378
+ const rows = [];
379
+ for (let r = startRow; r <= endRow; r++) {
380
+ const rowMap = this.cells.get(r);
381
+ const subset = new Map();
382
+ for (let c = startCol; c <= endCol; c++) {
383
+ const cell = rowMap?.get(c);
384
+ if (cell)
385
+ subset.set(c, { ...cell });
386
+ }
387
+ rows.push({ rowIdx: r, cells: subset });
388
+ }
389
+ rows.sort((a, b) => {
390
+ const va = a.cells.get(sortCol)?.value;
391
+ const vb = b.cells.get(sortCol)?.value;
392
+ const na = typeof va === 'number' ? va : typeof va === 'string' ? va : '';
393
+ const nb = typeof vb === 'number' ? vb : typeof vb === 'string' ? vb : '';
394
+ let cmp = 0;
395
+ if (typeof na === 'number' && typeof nb === 'number')
396
+ cmp = na - nb;
397
+ else
398
+ cmp = String(na).localeCompare(String(nb));
399
+ return order === 'desc' ? -cmp : cmp;
400
+ });
401
+ for (let i = 0; i < rows.length; i++) {
402
+ const r = startRow + i;
403
+ for (let c = startCol; c <= endCol; c++) {
404
+ const cell = rows[i].cells.get(c);
405
+ const rowMap = this.cells.get(r) ?? new Map();
406
+ if (!this.cells.has(r))
407
+ this.cells.set(r, rowMap);
408
+ if (cell)
409
+ rowMap.set(c, cell);
410
+ else
411
+ rowMap.delete(c);
412
+ }
413
+ }
414
+ return this;
415
+ }
416
+ fillNumber(startRow, col, count, startValue = 0, step = 1) {
417
+ for (let i = 0; i < count; i++) {
418
+ this.setValue(startRow + i, col, startValue + i * step);
419
+ }
420
+ return this;
421
+ }
422
+ fillDate(startRow, col, count, startDate, unit = 'day', step = 1) {
423
+ for (let i = 0; i < count; i++) {
424
+ const d = new Date(startDate);
425
+ switch (unit) {
426
+ case 'day':
427
+ d.setDate(d.getDate() + i * step);
428
+ break;
429
+ case 'week':
430
+ d.setDate(d.getDate() + i * step * 7);
431
+ break;
432
+ case 'month':
433
+ d.setMonth(d.getMonth() + i * step);
434
+ break;
435
+ case 'year':
436
+ d.setFullYear(d.getFullYear() + i * step);
437
+ break;
438
+ }
439
+ this.setValue(startRow + i, col, d);
440
+ }
441
+ return this;
442
+ }
443
+ fillList(startRow, col, list, count) {
444
+ for (let i = 0; i < count; i++) {
445
+ this.setValue(startRow + i, col, list[i % list.length]);
446
+ }
447
+ return this;
448
+ }
449
+ autoFitColumns(minWidth = 8, maxWidth = 60) {
450
+ const range = this.getUsedRange();
451
+ if (!range)
452
+ return this;
453
+ for (let c = range.startCol; c <= range.endCol; c++) {
454
+ let maxLen = 0;
455
+ for (let r = range.startRow; r <= range.endRow; r++) {
456
+ const cell = this.cells.get(r)?.get(c);
457
+ if (cell?.value != null) {
458
+ const s = String(cell.value);
459
+ if (s.length > maxLen)
460
+ maxLen = s.length;
461
+ }
462
+ if (cell?.richText) {
463
+ const s = cell.richText.map(r => r.text).join('');
464
+ if (s.length > maxLen)
465
+ maxLen = s.length;
466
+ }
467
+ }
468
+ if (maxLen > 0) {
469
+ const w = Math.max(minWidth, Math.min(maxWidth, maxLen * 1.2 + 2));
470
+ this.setColumn(c, { ...(this.colDefs.get(c) ?? {}), width: w, customWidth: true });
471
+ }
472
+ }
473
+ return this;
474
+ }
475
+ duplicateRow(sourceRow, targetRow) {
476
+ this.insertRows(targetRow, 1);
477
+ const srcMap = this.cells.get(sourceRow >= targetRow ? sourceRow + 1 : sourceRow);
478
+ if (srcMap) {
479
+ const newMap = new Map();
480
+ for (const [c, cell] of srcMap) {
481
+ newMap.set(c, { ...cell, style: cell.style ? { ...cell.style } : undefined });
482
+ }
483
+ this.cells.set(targetRow, newMap);
484
+ }
485
+ const srcDef = this.rowDefs.get(sourceRow >= targetRow ? sourceRow + 1 : sourceRow);
486
+ if (srcDef)
487
+ this.rowDefs.set(targetRow, { ...srcDef });
488
+ return this;
489
+ }
490
+ spliceRows(startRow, deleteCount, newRows) {
491
+ if (deleteCount > 0)
492
+ this.deleteRows(startRow, deleteCount);
493
+ if (newRows && newRows.length > 0) {
494
+ this.insertRows(startRow, newRows.length);
495
+ for (let i = 0; i < newRows.length; i++) {
496
+ this.writeRow(startRow + i, 1, newRows[i]);
497
+ }
498
+ }
499
+ return this;
500
+ }
501
+ setAutoFilter(ref, opts) {
502
+ this.autoFilter = { ref };
503
+ if (opts?.columns) {
504
+ this._filterColumns = opts.columns;
505
+ }
506
+ return this;
507
+ }
144
508
  addSparkline(s) {
145
509
  this.sparklines.push(s);
146
510
  return this;
147
511
  }
512
+ getSparklines() { return this.sparklines; }
148
513
  addDataValidation(sqref, dv) {
149
514
  this.dataValidations.set(sqref, dv);
150
515
  return this;
151
516
  }
517
+ addRowBreak(row, manual = true) {
518
+ this.rowBreaks.push({ id: row, manual });
519
+ return this;
520
+ }
521
+ addColBreak(col, manual = true) {
522
+ this.colBreaks.push({ id: col, manual });
523
+ return this;
524
+ }
525
+ getRowBreaks() { return this.rowBreaks; }
526
+ getColBreaks() { return this.colBreaks; }
527
+ addFormControl(ctrl) {
528
+ if (!ctrl.to && (ctrl.width || ctrl.height)) {
529
+ const COL_PX = 64, ROW_PX = 20;
530
+ const w = ctrl.width ?? 100, h = ctrl.height ?? 30;
531
+ const endColFrac = ctrl.from.col + w / COL_PX;
532
+ const endRowFrac = ctrl.from.row + h / ROW_PX;
533
+ ctrl = { ...ctrl, to: {
534
+ col: Math.floor(endColFrac),
535
+ row: Math.floor(endRowFrac),
536
+ colOff: Math.round((endColFrac % 1) * COL_PX),
537
+ rowOff: Math.round((endRowFrac % 1) * ROW_PX),
538
+ } };
539
+ }
540
+ this.formControls.push(ctrl);
541
+ return this;
542
+ }
543
+ getFormControls() { return this.formControls; }
544
+ addShape(shape) { this.shapes.push(shape); return this; }
545
+ getShapes() { return this.shapes; }
546
+ addOleObject(obj) { this.oleObjects.push(obj); return this; }
547
+ getOleObjects() { return this.oleObjects; }
548
+ addWordArt(wa) { this.wordArt.push(wa); return this; }
549
+ getWordArt() { return this.wordArt; }
550
+ addQueryTable(qt) { this.queryTables.push(qt); return this; }
551
+ getQueryTables() { return this.queryTables; }
552
+ addTableSlicer(slicer) { this.tableSlicers.push(slicer); return this; }
553
+ getTableSlicers() { return this.tableSlicers; }
554
+ addMathEquation(eq) { this.mathEquations.push(eq); return this; }
555
+ getMathEquations() { return this.mathEquations; }
556
+ addIgnoredError(sqref, opts) {
557
+ this.ignoreErrors.push({ sqref, ...opts });
558
+ return this;
559
+ }
560
+ getIgnoredErrors() { return this.ignoreErrors; }
561
+ addPreservedXml(xml) {
562
+ this.preservedXml.push(xml);
563
+ return this;
564
+ }
152
565
  freeze(row, col) {
153
566
  this.freezePane = { row, col };
154
567
  return this;
@@ -165,27 +578,39 @@ export class Worksheet {
165
578
  const mergesXml = this._mergesXml();
166
579
  const cfXml = this._conditionalFormatXml(styles);
167
580
  const dvXml = this._dataValidationsXml();
168
- const autoFilterXml = this.autoFilter ? `<autoFilter ref="${this.autoFilter.ref}"/>` : '';
581
+ const autoFilterXml = this.autoFilter && !this.tables.some(t => t.ref === this.autoFilter.ref)
582
+ ? this._autoFilterXml() : '';
169
583
  const tablePartsXml = this.tables.length
170
584
  ? `<tableParts count="${this.tables.length}">${this.tableRIds.map(rId => `<tablePart r:id="${rId}"/>`).join('')}</tableParts>`
171
585
  : '';
172
- const drawingXml = (this.images.length || this.charts.length) && this.drawingRId
586
+ const drawingXml = this.drawingRId
173
587
  ? `<drawing r:id="${this.drawingRId}"/>`
174
588
  : '';
175
589
  const legacyDrawingXml = this.legacyDrawingRId
176
590
  ? `<legacyDrawing r:id="${this.legacyDrawingRId}"/>`
177
591
  : '';
592
+ const controlsXml = this._formControlsXml();
593
+ const oleObjectsXml = this._oleObjectsXml();
178
594
  const sparklineXml = this._sparklineXml();
595
+ const customIconExtXml = this._customIconExtXml();
596
+ const slicerExtXml = this.slicerRId
597
+ ? `<extLst><ext uri="{A8765BA9-456A-4dab-B4F3-ACF838C121DE}" xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main"><x14:slicerList><x14:slicer r:id="${this.slicerRId}"/></x14:slicerList></ext></extLst>`
598
+ : '';
599
+ const ignoredErrorsXml = this._ignoredErrorsXml();
179
600
  const protectionXml = this._protectionXml();
180
601
  const pageSetupXml = this._pageSetupXml();
181
602
  const pageMarginsXml = this._pageMarginsXml();
182
603
  const headerFooterXml = this._headerFooterXml();
183
604
  const printOptionsXml = this._printOptionsXml();
605
+ const rowBreaksXml = this._pageBreaksXml('rowBreaks', this.rowBreaks, 16383);
606
+ const colBreaksXml = this._pageBreaksXml('colBreaks', this.colBreaks, 1048575);
184
607
  return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
185
608
  <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"
186
609
  xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
187
610
  xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main"
188
- xmlns:xr="http://schemas.microsoft.com/office/spreadsheetml/2014/revision">
611
+ xmlns:xr="http://schemas.microsoft.com/office/spreadsheetml/2014/revision"
612
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
613
+ xmlns:xdr="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing">
189
614
  ${sheetPrXml}
190
615
  ${sheetViewXml}
191
616
  ${colsXml}
@@ -199,11 +624,46 @@ ${printOptionsXml}
199
624
  ${pageMarginsXml}
200
625
  ${pageSetupXml}
201
626
  ${headerFooterXml}
627
+ ${rowBreaksXml}
628
+ ${colBreaksXml}
629
+ ${ignoredErrorsXml}
202
630
  ${drawingXml}
203
631
  ${legacyDrawingXml}
632
+ ${oleObjectsXml}
633
+ ${controlsXml}
204
634
  ${sparklineXml}
635
+ ${customIconExtXml}
205
636
  ${tablePartsXml}
637
+ ${slicerExtXml}
638
+ ${this.preservedXml.join('\n')}
206
639
  </worksheet>`;
640
+ }
641
+ toChartSheetXml() {
642
+ const pageMarginsXml = this._pageMarginsXml();
643
+ const pageSetupXml = this._pageSetupXml();
644
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
645
+ <chartsheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"
646
+ xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
647
+ <sheetPr/>
648
+ <sheetViews><sheetView zoomScale="99" workbookViewId="0" zoomToFit="1"/></sheetViews>
649
+ ${pageMarginsXml || '<pageMargins left="0.7" right="0.7" top="0.78740157499999996" bottom="0.78740157499999996" header="0.3" footer="0.3"/>'}
650
+ ${pageSetupXml}
651
+ <drawing r:id="${this.drawingRId}"/>
652
+ </chartsheet>`;
653
+ }
654
+ toDialogSheetXml(_styles, _shared) {
655
+ const pageMarginsXml = this._pageMarginsXml();
656
+ const legacyDrawingXml = this.legacyDrawingRId
657
+ ? `<legacyDrawing r:id="${this.legacyDrawingRId}"/>` : '';
658
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
659
+ <dialogsheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"
660
+ xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
661
+ <sheetViews><sheetView showRowColHeaders="0" showZeros="0" showOutlineSymbols="0" workbookViewId="0"/></sheetViews>
662
+ <sheetFormatPr baseColWidth="10" defaultColWidth="1" defaultRowHeight="5.65" customHeight="1"/>
663
+ <sheetProtection sheet="1"/>
664
+ ${pageMarginsXml || '<pageMargins left="0.7" right="0.7" top="0.78740157499999996" bottom="0.78740157499999996" header="0.3" footer="0.3"/>'}
665
+ ${legacyDrawingXml}
666
+ </dialogsheet>`;
207
667
  }
208
668
  _sheetViewXml() {
209
669
  const v = this.view ?? {};
@@ -247,65 +707,85 @@ ${tablePartsXml}
247
707
  return `<cols>${items.join('')}</cols>`;
248
708
  }
249
709
  _sheetDataXml(styles, shared) {
250
- const rows = new Map();
251
- for (const [key, cell] of this.cells) {
252
- const [r, c] = key.split(',').map(Number);
253
- if (!rows.has(r))
254
- rows.set(r, []);
255
- rows.get(r).push([c, cell]);
256
- }
257
- const sortedRows = [...rows.entries()].sort((a, b) => a[0] - b[0]);
258
- const rowsXml = sortedRows.map(([rowIdx, cells]) => {
710
+ const sortedRows = [...this.cells.entries()].sort((a, b) => a[0] - b[0]);
711
+ const out = ['<sheetData>'];
712
+ for (let ri = 0; ri < sortedRows.length; ri++) {
713
+ const [rowIdx, colMap] = sortedRows[ri];
259
714
  const rowDef = this.rowDefs.get(rowIdx);
260
715
  const rowStyleIdx = rowDef?.style ? styles.register(rowDef.style) : 0;
261
- const rowAttrs = [
262
- `r="${rowIdx}"`,
263
- rowDef?.height ? `ht="${rowDef.height}" customHeight="1"` : '',
264
- rowDef?.hidden ? `hidden="1"` : '',
265
- rowDef?.outlineLevel ? `outlineLevel="${rowDef.outlineLevel}"` : '',
266
- rowDef?.collapsed ? `collapsed="1"` : '',
267
- rowStyleIdx ? `s="${rowStyleIdx}" customFormat="1"` : '',
268
- rowDef?.thickTop ? `thickTop="1"` : '',
269
- rowDef?.thickBot ? `thickBot="1"` : '',
270
- ].filter(Boolean).join(' ');
271
- const sortedCells = cells.sort((a, b) => a[0] - b[0]);
272
- const cellsXml = sortedCells.map(([colIdx, cell]) => this._cellXml(rowIdx, colIdx, cell, styles, shared)).join('');
273
- return `<row ${rowAttrs}>${cellsXml}</row>`;
274
- });
275
- return `<sheetData>${rowsXml.join('')}</sheetData>`;
716
+ let attrs = `r="${rowIdx}"`;
717
+ if (rowDef?.height)
718
+ attrs += ` ht="${rowDef.height}" customHeight="1"`;
719
+ if (rowDef?.hidden)
720
+ attrs += ' hidden="1"';
721
+ if (rowDef?.outlineLevel)
722
+ attrs += ` outlineLevel="${rowDef.outlineLevel}"`;
723
+ if (rowDef?.collapsed)
724
+ attrs += ' collapsed="1"';
725
+ if (rowStyleIdx)
726
+ attrs += ` s="${rowStyleIdx}" customFormat="1"`;
727
+ if (rowDef?.thickTop)
728
+ attrs += ' thickTop="1"';
729
+ if (rowDef?.thickBot)
730
+ attrs += ' thickBot="1"';
731
+ out.push(`<row ${attrs}>`);
732
+ const sortedCells = [...colMap.entries()].sort((a, b) => a[0] - b[0]);
733
+ for (let ci = 0; ci < sortedCells.length; ci++) {
734
+ out.push(this._cellXml(rowIdx, sortedCells[ci][0], sortedCells[ci][1], styles, shared));
735
+ }
736
+ out.push('</row>');
737
+ }
738
+ out.push('</sheetData>');
739
+ return out.join('');
276
740
  }
277
741
  _cellXml(row, col, cell, styles, shared) {
278
742
  const ref = `${colIndexToLetter(col)}${row}`;
279
743
  const styleIdx = cell.style ? styles.register(cell.style) : 0;
280
744
  const sAttr = styleIdx ? ` s="${styleIdx}"` : '';
745
+ const vmIdx = this._cellImageVm.get(ref);
746
+ const vmAttr = vmIdx !== undefined ? ` vm="${vmIdx}"` : '';
281
747
  if (cell.arrayFormula) {
282
- const fml = `<f t="array" ref="${ref}">${escapeXml(cell.arrayFormula)}</f>`;
283
- return `<c r="${ref}"${sAttr}>${fml}<v>0</v></c>`;
748
+ const fml = `<f t="array" ref="${ref}">${escapeXmlContent(cell.arrayFormula)}</f>`;
749
+ return `<c r="${ref}"${sAttr}${vmAttr}>${fml}<v>0</v></c>`;
750
+ }
751
+ if (cell._sharedIdx !== undefined) {
752
+ const si = cell._sharedIdx;
753
+ const sharedRef = cell._sharedRef;
754
+ if (cell.formula && sharedRef) {
755
+ const fml = `<f t="shared" ref="${sharedRef}" si="${si}">${escapeXmlContent(cell.formula)}</f>`;
756
+ return `<c r="${ref}"${sAttr}${vmAttr}>${fml}</c>`;
757
+ }
758
+ return `<c r="${ref}"${sAttr}${vmAttr}><f t="shared" si="${si}"/></c>`;
284
759
  }
285
760
  if (cell.formula) {
286
- const fml = `<f>${escapeXml(cell.formula)}</f>`;
287
- return `<c r="${ref}"${sAttr}>${fml}</c>`;
761
+ const fml = `<f>${escapeXmlContent(cell.formula)}</f>`;
762
+ return `<c r="${ref}"${sAttr}${vmAttr}>${fml}</c>`;
288
763
  }
289
764
  if (cell.richText) {
290
765
  const si = shared.internRichText(cell.richText);
291
- return `<c r="${ref}" t="s"${sAttr}><v>${si}</v></c>`;
766
+ return `<c r="${ref}" t="s"${sAttr}${vmAttr}><v>${si}</v></c>`;
292
767
  }
293
768
  const v = cell.value;
294
769
  if (v === null || v === undefined) {
770
+ if (vmAttr)
771
+ return `<c r="${ref}"${sAttr} t="e"${vmAttr}><v>#VALUE!</v></c>`;
295
772
  return styleIdx ? `<c r="${ref}"${sAttr}/>` : '';
296
773
  }
774
+ if (v instanceof CellError) {
775
+ return `<c r="${ref}" t="e"${sAttr}${vmAttr}><v>${escapeXml(v.error)}</v></c>`;
776
+ }
297
777
  if (typeof v === 'boolean') {
298
- return `<c r="${ref}" t="b"${sAttr}><v>${v ? 1 : 0}</v></c>`;
778
+ return `<c r="${ref}" t="b"${sAttr}${vmAttr}><v>${v ? 1 : 0}</v></c>`;
299
779
  }
300
780
  if (v instanceof Date) {
301
781
  const serial = dateToSerial(v);
302
- return `<c r="${ref}"${sAttr}><v>${serial}</v></c>`;
782
+ return `<c r="${ref}"${sAttr}${vmAttr}><v>${serial}</v></c>`;
303
783
  }
304
784
  if (typeof v === 'number') {
305
- return `<c r="${ref}"${sAttr}><v>${v}</v></c>`;
785
+ return `<c r="${ref}"${sAttr}${vmAttr}><v>${v}</v></c>`;
306
786
  }
307
787
  const si = shared.intern(v);
308
- return `<c r="${ref}" t="s"${sAttr}><v>${si}</v></c>`;
788
+ return `<c r="${ref}" t="s"${sAttr}${vmAttr}><v>${si}</v></c>`;
309
789
  }
310
790
  _mergesXml() {
311
791
  if (!this.merges.length)
@@ -324,7 +804,7 @@ ${tablePartsXml}
324
804
  if (cf.colorScale?.type === 'colorScale') {
325
805
  const cs = cf.colorScale;
326
806
  const cfvos = cs.cfvo.map(v => `<cfvo type="${v.type}"${v.val ? ` val="${v.val}"` : ''}/>`).join('');
327
- const colors = cs.color.map(c => `<color rgb="${c.startsWith('#') ? 'FF' + c.slice(1) : c}"/>`).join('');
807
+ const colors = cs.color.map(c => colorEl(c)).join('');
328
808
  inner = `<colorScale>${cfvos}${colors}</colorScale>`;
329
809
  }
330
810
  else if (cf.dataBar?.type === 'dataBar') {
@@ -332,8 +812,7 @@ ${tablePartsXml}
332
812
  const minCfvo = `<cfvo type="${db.minType ?? 'min'}"${db.minVal != null ? ` val="${db.minVal}"` : ''}/>`;
333
813
  const maxCfvo = `<cfvo type="${db.maxType ?? 'max'}"${db.maxVal != null ? ` val="${db.maxVal}"` : ''}/>`;
334
814
  const color = db.color ?? db.minColor ?? 'FF638EC6';
335
- const rgb = color.startsWith('#') ? 'FF' + color.slice(1) : color;
336
- inner = `<dataBar${db.showValue === false ? ' showValue="0"' : ''}>${minCfvo}${maxCfvo}<color rgb="${rgb}"/></dataBar>`;
815
+ inner = `<dataBar${db.showValue === false ? ' showValue="0"' : ''}>${minCfvo}${maxCfvo}${colorEl(color)}</dataBar>`;
337
816
  }
338
817
  else if (cf.iconSet?.type === 'iconSet') {
339
818
  const is = cf.iconSet;
@@ -455,6 +934,45 @@ ${tablePartsXml}
455
934
  ].filter(Boolean).join(' ');
456
935
  return attrs ? `<printOptions ${attrs}/>` : '';
457
936
  }
937
+ _pageBreaksXml(tag, breaks, maxVal) {
938
+ if (!breaks.length)
939
+ return '';
940
+ const manualCount = breaks.filter(b => b.manual !== false).length;
941
+ const brks = breaks.map(b => `<brk id="${b.id}" max="${maxVal}"${b.manual !== false ? ' man="1"' : ''}/>`).join('');
942
+ return `<${tag} count="${breaks.length}" manualBreakCount="${manualCount}">${brks}</${tag}>`;
943
+ }
944
+ _formControlsXml() {
945
+ if (!this.formControls.length || !this.ctrlPropRIds.length)
946
+ return '';
947
+ const baseShapeId = 1025 + this.sheetIndex * 1000;
948
+ let commentCount = 0;
949
+ for (const rowMap of this.cells.values())
950
+ for (const c of rowMap.values())
951
+ if (c.comment)
952
+ commentCount++;
953
+ const controls = this.formControls.map((ctrl, i) => {
954
+ const shapeId = ctrl._shapeId ?? (baseShapeId + commentCount + i);
955
+ const ctrlPropRId = this.ctrlPropRIds[i];
956
+ if (!ctrlPropRId)
957
+ return '';
958
+ const name = ctrl.text ?? `${ctrl.type} ${i + 1}`;
959
+ return `<mc:AlternateContent><mc:Choice Requires="x14"><control shapeId="${shapeId}" r:id="${ctrlPropRId}" name="${escapeXml(name)}"><controlPr defaultSize="0" print="0" autoFill="0" autoPict="0"${ctrl.macro ? ` macro="${escapeXml(ctrl.macro)}"` : ''}><anchor moveWithCells="1"><from><xdr:col>${ctrl.from.col}</xdr:col><xdr:colOff>${ctrl.from.colOff ?? 0}</xdr:colOff><xdr:row>${ctrl.from.row}</xdr:row><xdr:rowOff>${ctrl.from.rowOff ?? 0}</xdr:rowOff></from><to><xdr:col>${ctrl.to.col}</xdr:col><xdr:colOff>${ctrl.to.colOff ?? 0}</xdr:colOff><xdr:row>${ctrl.to.row}</xdr:row><xdr:rowOff>${ctrl.to.rowOff ?? 0}</xdr:rowOff></to></anchor></controlPr></control></mc:Choice></mc:AlternateContent>`;
960
+ }).join('');
961
+ return `<mc:AlternateContent><mc:Choice Requires="x14"><controls>${controls}</controls></mc:Choice></mc:AlternateContent>`;
962
+ }
963
+ _oleObjectsXml() {
964
+ if (!this.oleObjects.length || !this.oleRIds.length)
965
+ return '';
966
+ const baseShapeId = 2025 + this.sheetIndex * 1000;
967
+ const items = this.oleObjects.map((ole, i) => {
968
+ const shapeId = baseShapeId + i;
969
+ const rId = this.oleRIds[i];
970
+ const progId = ole.progId ?? 'Package';
971
+ const link = ole.linkToFile && ole.linkPath ? ` link="${escapeXml(ole.linkPath)}"` : '';
972
+ return `<oleObject progId="${escapeXml(progId)}" shapeId="${shapeId}" r:id="${rId}"${link}/>`;
973
+ }).join('');
974
+ return `<oleObjects>${items}</oleObjects>`;
975
+ }
458
976
  _sparklineXml() {
459
977
  if (!this.sparklines.length)
460
978
  return '';
@@ -494,23 +1012,117 @@ ${tablePartsXml}
494
1012
  const inner = `<x14:sparklineGroups xmlns:xm="http://schemas.microsoft.com/office/excel/2006/main">${groups.join('')}</x14:sparklineGroups>`;
495
1013
  return `<extLst><ext uri="{05C60535-1F16-4fd2-B633-F4F36F0B64E0}" xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main">${inner}</ext></extLst>`;
496
1014
  }
1015
+ _customIconExtXml() {
1016
+ const customCFs = this.conditionalFormats.filter(cf => cf.iconSet?.type === 'iconSet' && 'custom' in cf.iconSet && cf.iconSet.custom?.length);
1017
+ if (!customCFs.length)
1018
+ return '';
1019
+ const rules = customCFs.map((cf, i) => {
1020
+ const is = cf.iconSet;
1021
+ const cfvos = is.cfvo.map(v => `<x14:cfvo type="${v.type}"${v.val ? ` val="${v.val}"` : ''}/>`).join('');
1022
+ const icons = is.custom.map(ci => `<x14:cfIcon iconSet="${ci.iconSet}" iconId="${ci.iconId}"/>`).join('');
1023
+ return `<x14:conditionalFormatting xmlns:xm="http://schemas.microsoft.com/office/excel/2006/main"><x14:cfRule type="iconSet" id="{${this._uuid()}}"><x14:iconSet iconSet="${is.iconSet}" custom="1"${is.showValue === false ? ' showValue="0"' : ''}${is.reverse ? ' reverse="1"' : ''}>${cfvos}${icons}</x14:iconSet></x14:cfRule><xm:sqref>${cf.sqref}</xm:sqref></x14:conditionalFormatting>`;
1024
+ }).join('');
1025
+ return `<extLst><ext uri="{78C0D931-6437-407d-A8EE-F0AAD7539E65}" xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main">${rules}</ext></extLst>`;
1026
+ }
1027
+ _uuid() {
1028
+ const h = '0123456789ABCDEF';
1029
+ let u = '';
1030
+ for (let i = 0; i < 36; i++) {
1031
+ if (i === 8 || i === 13 || i === 18 || i === 23)
1032
+ u += '-';
1033
+ else if (i === 14)
1034
+ u += '4';
1035
+ else if (i === 19)
1036
+ u += h[(Math.random() * 4 | 0) + 8];
1037
+ else
1038
+ u += h[Math.random() * 16 | 0];
1039
+ }
1040
+ return u;
1041
+ }
1042
+ _autoFilterXml() {
1043
+ if (!this.autoFilter)
1044
+ return '';
1045
+ const cols = this._filterColumns;
1046
+ if (!cols || cols.length === 0)
1047
+ return `<autoFilter ref="${this.autoFilter.ref}"/>`;
1048
+ const colXml = cols.map(fc => {
1049
+ const colId = fc.col - 1;
1050
+ let inner = '';
1051
+ if (fc.type === 'custom') {
1052
+ const op = fc.operator ?? 'greaterThan';
1053
+ inner = `<customFilters><customFilter operator="${op}" val="${fc.val ?? ''}"/></customFilters>`;
1054
+ }
1055
+ else if (fc.type === 'top10') {
1056
+ const attrs = [
1057
+ fc.top === false ? 'top="0"' : '',
1058
+ fc.percent ? 'percent="1"' : '',
1059
+ `val="${fc.val ?? 10}"`,
1060
+ ].filter(Boolean).join(' ');
1061
+ inner = `<top10 ${attrs}/>`;
1062
+ }
1063
+ else if (fc.type === 'value' && fc.items) {
1064
+ inner = `<filters>${fc.items.map(v => `<filter val="${escapeXml(String(v))}"/>`).join('')}</filters>`;
1065
+ }
1066
+ else if (fc.type === 'dynamic') {
1067
+ inner = `<dynamicFilter type="${fc.dynamicType ?? 'aboveAverage'}"/>`;
1068
+ }
1069
+ return `<filterColumn colId="${colId}">${inner}</filterColumn>`;
1070
+ }).join('');
1071
+ return `<autoFilter ref="${this.autoFilter.ref}">${colXml}</autoFilter>`;
1072
+ }
1073
+ _ignoredErrorsXml() {
1074
+ if (!this.ignoreErrors.length)
1075
+ return '';
1076
+ const items = this.ignoreErrors.map(ie => {
1077
+ const attrs = [`sqref="${ie.sqref}"`];
1078
+ if (ie.numberStoredAsText)
1079
+ attrs.push('numberStoredAsText="1"');
1080
+ if (ie.formula)
1081
+ attrs.push('formula="1"');
1082
+ if (ie.formulaRange)
1083
+ attrs.push('formulaRange="1"');
1084
+ if (ie.unlockedFormula)
1085
+ attrs.push('unlockedFormula="1"');
1086
+ if (ie.evalError)
1087
+ attrs.push('evalError="1"');
1088
+ if (ie.twoDigitTextYear)
1089
+ attrs.push('twoDigitTextYear="1"');
1090
+ if (ie.emptyRef)
1091
+ attrs.push('emptyRef="1"');
1092
+ if (ie.listDataValidation)
1093
+ attrs.push('listDataValidation="1"');
1094
+ if (ie.calculatedColumn)
1095
+ attrs.push('calculatedColumn="1"');
1096
+ return `<ignoredError ${attrs.join(' ')}/>`;
1097
+ });
1098
+ return `<ignoredErrors>${items.join('')}</ignoredErrors>`;
1099
+ }
497
1100
  toDrawingXml(imageRIds, chartRIds) {
498
1101
  const parts = [];
499
1102
  const EMU = pxToEmu;
500
1103
  this.images.forEach((img, i) => {
501
1104
  const rId = imageRIds[i];
502
- const from = img.from;
503
- const to = img.to;
504
- const fromXml = `<xdr:from><xdr:col>${from.col}</xdr:col><xdr:colOff>${from.colOff ?? 0}</xdr:colOff><xdr:row>${from.row}</xdr:row><xdr:rowOff>${from.rowOff ?? 0}</xdr:rowOff></xdr:from>`;
1105
+ const w = EMU(img.width ?? 100);
1106
+ const h = EMU(img.height ?? 100);
505
1107
  let anchor;
506
- if (to) {
507
- const toXml = `<xdr:to><xdr:col>${to.col}</xdr:col><xdr:colOff>${to.colOff ?? 0}</xdr:colOff><xdr:row>${to.row}</xdr:row><xdr:rowOff>${to.rowOff ?? 0}</xdr:rowOff></xdr:to>`;
508
- anchor = `<xdr:twoCellAnchor editAs="oneCell">${fromXml}${toXml}`;
1108
+ let closeTag;
1109
+ if (img.position) {
1110
+ anchor = `<xdr:absoluteAnchor><xdr:pos x="${EMU(img.position.x)}" y="${EMU(img.position.y)}"/><xdr:ext cx="${w}" cy="${h}"/>`;
1111
+ closeTag = `</xdr:absoluteAnchor>`;
509
1112
  }
510
1113
  else {
511
- const w = EMU(img.width ?? 100);
512
- const h = EMU(img.height ?? 100);
513
- anchor = `<xdr:oneCellAnchor>${fromXml}<xdr:ext cx="${w}" cy="${h}"/>`;
1114
+ const from = img.from;
1115
+ const to = img.to;
1116
+ const fromXml = `<xdr:from><xdr:col>${from.col}</xdr:col><xdr:colOff>${from.colOff ?? 0}</xdr:colOff><xdr:row>${from.row}</xdr:row><xdr:rowOff>${from.rowOff ?? 0}</xdr:rowOff></xdr:from>`;
1117
+ if (to) {
1118
+ const toXml = `<xdr:to><xdr:col>${to.col}</xdr:col><xdr:colOff>${to.colOff ?? 0}</xdr:colOff><xdr:row>${to.row}</xdr:row><xdr:rowOff>${to.rowOff ?? 0}</xdr:rowOff></xdr:to>`;
1119
+ anchor = `<xdr:twoCellAnchor editAs="oneCell">${fromXml}${toXml}`;
1120
+ closeTag = `</xdr:twoCellAnchor>`;
1121
+ }
1122
+ else {
1123
+ anchor = `<xdr:oneCellAnchor>${fromXml}<xdr:ext cx="${w}" cy="${h}"/>`;
1124
+ closeTag = `</xdr:oneCellAnchor>`;
1125
+ }
514
1126
  }
515
1127
  const picXml = `<xdr:pic>
516
1128
  <xdr:nvPicPr>
@@ -523,29 +1135,182 @@ ${tablePartsXml}
523
1135
  </xdr:blipFill>
524
1136
  <xdr:spPr>
525
1137
  <a:xfrm xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">
526
- <a:off x="0" y="0"/><a:ext cx="${EMU(img.width ?? 100)}" cy="${EMU(img.height ?? 100)}"/>
1138
+ <a:off x="0" y="0"/><a:ext cx="${w}" cy="${h}"/>
527
1139
  </a:xfrm>
528
1140
  <a:prstGeom xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" prst="rect"><a:avLst/></a:prstGeom>
529
1141
  </xdr:spPr>
530
1142
  </xdr:pic>`;
531
- const closeTag = to ? `</xdr:twoCellAnchor>` : `</xdr:oneCellAnchor>`;
532
1143
  parts.push(`${anchor}${picXml}<xdr:clientData/>${closeTag}`);
533
1144
  });
534
1145
  this.charts.forEach((chart, i) => {
535
1146
  const rId = chartRIds[i];
536
1147
  const from = chart.from;
537
1148
  const to = chart.to;
538
- const fromXml = `<xdr:from><xdr:col>${from.col}</xdr:col><xdr:colOff>${from.colOff ?? 0}</xdr:colOff><xdr:row>${from.row}</xdr:row><xdr:rowOff>${from.rowOff ?? 0}</xdr:rowOff></xdr:from>`;
539
- const toXml = `<xdr:to><xdr:col>${to.col}</xdr:col><xdr:colOff>${to.colOff ?? 0}</xdr:colOff><xdr:row>${to.row}</xdr:row><xdr:rowOff>${to.rowOff ?? 0}</xdr:rowOff></xdr:to>`;
540
1149
  const graphicXml = `<a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">
541
1150
  <a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/chart">
542
1151
  <c:chart xmlns:c="http://schemas.openxmlformats.org/drawingml/2006/chart" r:id="${rId}"/>
543
1152
  </a:graphicData>
544
1153
  </a:graphic>`;
545
- parts.push(`<xdr:twoCellAnchor editAs="oneCell">${fromXml}${toXml}<xdr:graphicFrame macro=""><xdr:nvGraphicFramePr><xdr:cNvPr id="${this.images.length + i + 2}" name="Chart ${i + 1}"/><xdr:cNvGraphicFramePr/></xdr:nvGraphicFramePr><xdr:xfrm><a:off xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" x="0" y="0"/><a:ext xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" cx="0" cy="0"/></xdr:xfrm>${graphicXml}</xdr:graphicFrame><xdr:clientData/></xdr:twoCellAnchor>`);
1154
+ if (this._isChartSheet) {
1155
+ parts.push(`<xdr:absoluteAnchor><xdr:pos x="0" y="0"/><xdr:ext cx="9294091" cy="6003636"/><xdr:graphicFrame macro=""><xdr:nvGraphicFramePr><xdr:cNvPr id="${this.images.length + i + 2}" name="Chart ${i + 1}"/><xdr:cNvGraphicFramePr><a:graphicFrameLocks xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" noGrp="1"/></xdr:cNvGraphicFramePr></xdr:nvGraphicFramePr><xdr:xfrm><a:off xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" x="0" y="0"/><a:ext xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" cx="0" cy="0"/></xdr:xfrm>${graphicXml}</xdr:graphicFrame><xdr:clientData/></xdr:absoluteAnchor>`);
1156
+ }
1157
+ else {
1158
+ const fromXml = `<xdr:from><xdr:col>${from.col}</xdr:col><xdr:colOff>${from.colOff ?? 0}</xdr:colOff><xdr:row>${from.row}</xdr:row><xdr:rowOff>${from.rowOff ?? 0}</xdr:rowOff></xdr:from>`;
1159
+ const toXml = `<xdr:to><xdr:col>${to.col}</xdr:col><xdr:colOff>${to.colOff ?? 0}</xdr:colOff><xdr:row>${to.row}</xdr:row><xdr:rowOff>${to.rowOff ?? 0}</xdr:rowOff></xdr:to>`;
1160
+ parts.push(`<xdr:twoCellAnchor editAs="oneCell">${fromXml}${toXml}<xdr:graphicFrame macro=""><xdr:nvGraphicFramePr><xdr:cNvPr id="${this.images.length + i + 2}" name="Chart ${i + 1}"/><xdr:cNvGraphicFramePr/></xdr:nvGraphicFramePr><xdr:xfrm><a:off xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" x="0" y="0"/><a:ext xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" cx="0" cy="0"/></xdr:xfrm>${graphicXml}</xdr:graphicFrame><xdr:clientData/></xdr:twoCellAnchor>`);
1161
+ }
546
1162
  });
1163
+ let shapeCounter = this.images.length + this.charts.length + 2;
1164
+ const toHex6 = (c) => { let h = c.replace(/^#/, ''); if (h.length === 8)
1165
+ h = h.substring(2); return h; };
1166
+ this.shapes.forEach(shape => {
1167
+ const id = shapeCounter++;
1168
+ const from = shape.from;
1169
+ const to = shape.to;
1170
+ const fromXml = `<xdr:from><xdr:col>${from.col}</xdr:col><xdr:colOff>${from.colOff ?? 0}</xdr:colOff><xdr:row>${from.row}</xdr:row><xdr:rowOff>${from.rowOff ?? 0}</xdr:rowOff></xdr:from>`;
1171
+ const toXml = `<xdr:to><xdr:col>${to.col}</xdr:col><xdr:colOff>${to.colOff ?? 0}</xdr:colOff><xdr:row>${to.row}</xdr:row><xdr:rowOff>${to.rowOff ?? 0}</xdr:rowOff></xdr:to>`;
1172
+ const fillXml = shape.fillColor
1173
+ ? `<a:solidFill><a:srgbClr val="${toHex6(shape.fillColor)}"/></a:solidFill>`
1174
+ : '<a:noFill/>';
1175
+ const lineXml = shape.lineColor
1176
+ ? `<a:ln${shape.lineWidth ? ` w="${shape.lineWidth * 12700}"` : ''}><a:solidFill><a:srgbClr val="${toHex6(shape.lineColor)}"/></a:solidFill></a:ln>`
1177
+ : '';
1178
+ const rotAttr = shape.rotation ? ` rot="${shape.rotation * 60000}"` : '';
1179
+ const textXml = shape.text ? `<xdr:txBody><a:bodyPr vertOverflow="clip" wrap="square" rtlCol="0" anchor="ctr"/><a:lstStyle/><a:p><a:pPr algn="ctr"/><a:r><a:rPr lang="en-US"${shape.font?.bold ? ' b="1"' : ''}${shape.font?.size ? ` sz="${shape.font.size * 100}"` : ''}/><a:t>${escapeXml(shape.text)}</a:t></a:r></a:p></xdr:txBody>` : '';
1180
+ parts.push(`<xdr:twoCellAnchor editAs="oneCell">${fromXml}${toXml}<xdr:sp><xdr:nvSpPr><xdr:cNvPr id="${id}" name="Shape ${id}"/><xdr:cNvSpPr/></xdr:nvSpPr><xdr:spPr><a:xfrm${rotAttr}><a:off x="0" y="0"/><a:ext cx="0" cy="0"/></a:xfrm><a:prstGeom prst="${shape.type}"><a:avLst/></a:prstGeom>${fillXml}${lineXml}</xdr:spPr>${textXml}</xdr:sp><xdr:clientData/></xdr:twoCellAnchor>`);
1181
+ });
1182
+ this.wordArt.forEach(wa => {
1183
+ const id = shapeCounter++;
1184
+ const from = wa.from;
1185
+ const to = wa.to;
1186
+ const fromXml = `<xdr:from><xdr:col>${from.col}</xdr:col><xdr:colOff>${from.colOff ?? 0}</xdr:colOff><xdr:row>${from.row}</xdr:row><xdr:rowOff>${from.rowOff ?? 0}</xdr:rowOff></xdr:from>`;
1187
+ const toXml = `<xdr:to><xdr:col>${to.col}</xdr:col><xdr:colOff>${to.colOff ?? 0}</xdr:colOff><xdr:row>${to.row}</xdr:row><xdr:rowOff>${to.rowOff ?? 0}</xdr:rowOff></xdr:to>`;
1188
+ const preset = wa.preset ?? 'textPlain';
1189
+ const fontSize = wa.font?.size ? wa.font.size * 100 : 3600;
1190
+ const fillHex = wa.fillColor ? toHex6(wa.fillColor) : '';
1191
+ const outlineHex = wa.outlineColor ? toHex6(wa.outlineColor) : '';
1192
+ const textFillXml = fillHex
1193
+ ? `<a:solidFill><a:srgbClr val="${fillHex}"/></a:solidFill>`
1194
+ : '<a:solidFill><a:schemeClr val="tx1"/></a:solidFill>';
1195
+ const textOutlineXml = outlineHex
1196
+ ? `<a:ln w="12700"><a:solidFill><a:srgbClr val="${outlineHex}"/></a:solidFill><a:prstDash val="solid"/></a:ln>`
1197
+ : '';
1198
+ const effectXml = `<a:effectLst><a:outerShdw dist="38100" dir="2700000" algn="bl" rotWithShape="0"><a:srgbClr val="000000"><a:alpha val="40000"/></a:srgbClr></a:outerShdw></a:effectLst>`;
1199
+ const fontAttrs = [
1200
+ 'lang="en-US"',
1201
+ `sz="${fontSize}"`,
1202
+ wa.font?.bold ? 'b="1"' : 'b="1"',
1203
+ wa.font?.italic ? 'i="1"' : '',
1204
+ 'cap="none" spc="0"',
1205
+ ].filter(Boolean).join(' ');
1206
+ const fontFace = wa.font?.name ?? 'Calibri';
1207
+ const prstWarpXml = preset !== 'textPlain'
1208
+ ? `<a:prstTxWarp prst="${preset}"><a:avLst/></a:prstTxWarp>`
1209
+ : '';
1210
+ parts.push(`<xdr:twoCellAnchor editAs="oneCell">${fromXml}${toXml}<xdr:sp macro="" textlink=""><xdr:nvSpPr><xdr:cNvPr id="${id}" name="WordArt ${id}"/><xdr:cNvSpPr/></xdr:nvSpPr><xdr:spPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="0" cy="0"/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom><a:noFill/></xdr:spPr><xdr:txBody><a:bodyPr wrap="square" lIns="91440" tIns="45720" rIns="91440" bIns="45720">${prstWarpXml}<a:spAutoFit/></a:bodyPr><a:lstStyle/><a:p><a:pPr algn="ctr"/><a:r><a:rPr ${fontAttrs}>${textOutlineXml}${textFillXml}${effectXml}<a:latin typeface="${escapeXml(fontFace)}"/></a:rPr><a:t>${escapeXml(wa.text)}</a:t></a:r></a:p></xdr:txBody></xdr:sp><xdr:clientData/></xdr:twoCellAnchor>`);
1211
+ });
1212
+ this.mathEquations.forEach(eq => {
1213
+ const id = shapeCounter++;
1214
+ const from = eq.from;
1215
+ const fromXml = `<xdr:from><xdr:col>${from.col}</xdr:col><xdr:colOff>${from.colOff ?? 0}</xdr:colOff><xdr:row>${from.row}</xdr:row><xdr:rowOff>${from.rowOff ?? 0}</xdr:rowOff></xdr:from>`;
1216
+ const w = eq.width ?? 1800000;
1217
+ const h = eq.height ?? 500000;
1218
+ const extXml = `<xdr:ext cx="${w}" cy="${h}"/>`;
1219
+ const fontSize = eq.fontSize ? eq.fontSize * 100 : 1100;
1220
+ const fontName = eq.fontName ?? 'Cambria Math';
1221
+ const rPr = `<a:rPr lang="en-US" sz="${fontSize}" i="1"><a:latin typeface="${escapeXml(fontName)}" panose="02040503050406030204" pitchFamily="18" charset="0"/></a:rPr>`;
1222
+ const ctrlPr = `<m:ctrlPr>${rPr}</m:ctrlPr>`;
1223
+ const buildOmml = (el) => {
1224
+ switch (el.type) {
1225
+ case 'text':
1226
+ return `<m:r>${rPr}<m:t>${escapeXml(el.text ?? '')}</m:t></m:r>`;
1227
+ case 'frac':
1228
+ return `<m:f><m:fPr>${el.hideDegree ? '<m:type m:val="noBar"/>' : ''}${ctrlPr}</m:fPr><m:num>${(el.base ?? []).map(buildOmml).join('')}</m:num><m:den>${(el.argument ?? []).map(buildOmml).join('')}</m:den></m:f>`;
1229
+ case 'sup':
1230
+ return `<m:sSup><m:sSupPr>${ctrlPr}</m:sSupPr><m:e>${(el.base ?? []).map(buildOmml).join('')}</m:e><m:sup>${(el.argument ?? []).map(buildOmml).join('')}</m:sup></m:sSup>`;
1231
+ case 'sub':
1232
+ return `<m:sSub><m:sSubPr>${ctrlPr}</m:sSubPr><m:e>${(el.base ?? []).map(buildOmml).join('')}</m:e><m:sub>${(el.argument ?? []).map(buildOmml).join('')}</m:sub></m:sSub>`;
1233
+ case 'subSup':
1234
+ return `<m:sSubSup><m:sSubSupPr>${ctrlPr}</m:sSubSupPr><m:e>${(el.base ?? []).map(buildOmml).join('')}</m:e><m:sub>${(el.subscript ?? []).map(buildOmml).join('')}</m:sub><m:sup>${(el.superscript ?? []).map(buildOmml).join('')}</m:sup></m:sSubSup>`;
1235
+ case 'nary': {
1236
+ const chr = el.operator ?? '∑';
1237
+ return `<m:nary><m:naryPr><m:chr m:val="${escapeXml(chr)}"/>${ctrlPr}</m:naryPr><m:sub>${(el.lower ?? []).map(buildOmml).join('')}</m:sub><m:sup>${(el.upper ?? []).map(buildOmml).join('')}</m:sup><m:e>${(el.body ?? []).map(buildOmml).join('')}</m:e></m:nary>`;
1238
+ }
1239
+ case 'rad':
1240
+ return `<m:rad><m:radPr>${el.hideDegree ? '<m:degHide m:val="1"/>' : ''}${ctrlPr}</m:radPr><m:deg>${(el.degree ?? []).map(buildOmml).join('')}</m:deg><m:e>${(el.body ?? []).map(buildOmml).join('')}</m:e></m:rad>`;
1241
+ case 'delim': {
1242
+ const open = el.open ?? '(';
1243
+ const close = el.close ?? ')';
1244
+ return `<m:d><m:dPr>${open !== '(' ? `<m:begChr m:val="${escapeXml(open)}"/>` : ''}${close !== ')' ? `<m:endChr m:val="${escapeXml(close)}"/>` : ''}${ctrlPr}</m:dPr><m:e>${(el.body ?? []).map(buildOmml).join('')}</m:e></m:d>`;
1245
+ }
1246
+ case 'func':
1247
+ return `<m:func><m:funcPr>${ctrlPr}</m:funcPr><m:fName>${(el.base ?? []).map(buildOmml).join('')}</m:fName><m:e>${(el.argument ?? []).map(buildOmml).join('')}</m:e></m:func>`;
1248
+ case 'accent':
1249
+ return `<m:acc><m:accPr>${el.operator ? `<m:chr m:val="${escapeXml(el.operator)}"/>` : ''}${ctrlPr}</m:accPr><m:e>${(el.body ?? []).map(buildOmml).join('')}</m:e></m:acc>`;
1250
+ case 'bar':
1251
+ return `<m:bar><m:barPr>${ctrlPr}</m:barPr><m:e>${(el.body ?? []).map(buildOmml).join('')}</m:e></m:bar>`;
1252
+ case 'matrix':
1253
+ return `<m:m><m:mPr>${ctrlPr}</m:mPr>${(el.rows ?? []).map(row => `<m:mr>${row.map(c => `<m:e>${buildOmml(c)}</m:e>`).join('')}</m:mr>`).join('')}</m:m>`;
1254
+ case 'eqArr':
1255
+ return `<m:eqArr><m:eqArrPr>${ctrlPr}</m:eqArrPr>${(el.rows ?? []).map(row => `<m:e>${row.map(buildOmml).join('')}</m:e>`).join('')}</m:eqArr>`;
1256
+ case 'limLow':
1257
+ return `<m:limLow><m:limLowPr>${ctrlPr}</m:limLowPr><m:e>${(el.base ?? []).map(buildOmml).join('')}</m:e><m:lim>${(el.argument ?? []).map(buildOmml).join('')}</m:lim></m:limLow>`;
1258
+ case 'limUpp':
1259
+ return `<m:limUpp><m:limUppPr>${ctrlPr}</m:limUppPr><m:e>${(el.base ?? []).map(buildOmml).join('')}</m:e><m:lim>${(el.argument ?? []).map(buildOmml).join('')}</m:lim></m:limUpp>`;
1260
+ case 'groupChar':
1261
+ return `<m:groupChr><m:groupChrPr>${el.operator ? `<m:chr m:val="${escapeXml(el.operator)}"/>` : ''}${ctrlPr}</m:groupChrPr><m:e>${(el.body ?? []).map(buildOmml).join('')}</m:e></m:groupChr>`;
1262
+ default:
1263
+ return `<m:r>${rPr}<m:t>${escapeXml(el.text ?? '')}</m:t></m:r>`;
1264
+ }
1265
+ };
1266
+ const ommlBody = eq.elements.map(buildOmml).join('');
1267
+ const ommlXml = `<m:oMathPara xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math"><m:oMathParaPr><m:jc m:val="centerGroup"/></m:oMathParaPr><m:oMath xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math">${ommlBody}</m:oMath></m:oMathPara>`;
1268
+ const flattenText = (el) => {
1269
+ if (el.text)
1270
+ return el.text;
1271
+ const parts = [el.base, el.argument, el.body, el.lower, el.upper, el.superscript, el.subscript, el.degree]
1272
+ .filter(Boolean).map(arr => arr.map(flattenText).join('')).join('');
1273
+ if (el.type === 'frac')
1274
+ return `(${(el.base ?? []).map(flattenText).join('')})/(${(el.argument ?? []).map(flattenText).join('')})`;
1275
+ if (el.type === 'sup')
1276
+ return `${(el.base ?? []).map(flattenText).join('')}^${(el.argument ?? []).map(flattenText).join('')}`;
1277
+ if (el.type === 'nary')
1278
+ return `${el.operator ?? '∑'}(${(el.body ?? []).map(flattenText).join('')})`;
1279
+ if (el.type === 'delim')
1280
+ return `${el.open ?? '('}${(el.body ?? []).map(flattenText).join('')}${el.close ?? ')'}`;
1281
+ if (el.type === 'rad')
1282
+ return `√(${(el.body ?? []).map(flattenText).join('')})`;
1283
+ return parts;
1284
+ };
1285
+ const fallbackText = eq.elements.map(flattenText).join('');
1286
+ parts.push(`<xdr:oneCellAnchor>${fromXml}${extXml}<mc:AlternateContent xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"><mc:Choice xmlns:a14="http://schemas.microsoft.com/office/drawing/2010/main" Requires="a14"><xdr:sp macro="" textlink=""><xdr:nvSpPr><xdr:cNvPr id="${id}" name="MathEq ${id}"/><xdr:cNvSpPr txBox="1"/></xdr:nvSpPr><xdr:spPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="${w}" cy="${h}"/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom><a:noFill/></xdr:spPr><xdr:txBody><a:bodyPr vertOverflow="clip" horzOverflow="clip" wrap="none" lIns="0" tIns="0" rIns="0" bIns="0" rtlCol="0" anchor="t"><a:spAutoFit/></a:bodyPr><a:lstStyle/><a:p><a14:m>${ommlXml}</a14:m><a:endParaRPr lang="en-US" sz="${fontSize}"/></a:p></xdr:txBody></xdr:sp></mc:Choice><mc:Fallback><xdr:sp macro="" textlink=""><xdr:nvSpPr><xdr:cNvPr id="${id}" name="MathEq ${id}"/><xdr:cNvSpPr txBox="1"/></xdr:nvSpPr><xdr:spPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="${w}" cy="${h}"/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom><a:noFill/></xdr:spPr><xdr:txBody><a:bodyPr vertOverflow="clip" horzOverflow="clip" wrap="none" lIns="0" tIns="0" rIns="0" bIns="0" rtlCol="0" anchor="t"><a:spAutoFit/></a:bodyPr><a:lstStyle/><a:p><a:r><a:rPr lang="en-US" sz="${fontSize}" i="0"><a:latin typeface="${escapeXml(fontName)}" panose="02040503050406030204" pitchFamily="18" charset="0"/></a:rPr><a:t>${escapeXml(fallbackText)}</a:t></a:r><a:endParaRPr lang="en-US" sz="${fontSize}"/></a:p></xdr:txBody></xdr:sp></mc:Fallback></mc:AlternateContent><xdr:clientData/></xdr:oneCellAnchor>`);
1287
+ });
1288
+ this.oleObjects.forEach((ole, i) => {
1289
+ const id = shapeCounter++;
1290
+ const from = ole.from;
1291
+ const to = ole.to;
1292
+ const fromXml = `<xdr:from><xdr:col>${from.col}</xdr:col><xdr:colOff>${from.colOff ?? 0}</xdr:colOff><xdr:row>${from.row}</xdr:row><xdr:rowOff>${from.rowOff ?? 0}</xdr:rowOff></xdr:from>`;
1293
+ const toXml = `<xdr:to><xdr:col>${to.col}</xdr:col><xdr:colOff>${to.colOff ?? 0}</xdr:colOff><xdr:row>${to.row}</xdr:row><xdr:rowOff>${to.rowOff ?? 0}</xdr:rowOff></xdr:to>`;
1294
+ const oleRId = this.oleRIds[i] ?? '';
1295
+ const iconRId = this.oleIconRIds[i] ?? '';
1296
+ const progId = ole.progId ?? 'Package';
1297
+ const displayAsIcon = ole.displayAsIcon ? ' showAsIcon="1"' : '';
1298
+ const linkAttr = ole.linkToFile && ole.linkPath ? ` link="${escapeXml(ole.linkPath)}"` : '';
1299
+ const blipRef = iconRId
1300
+ ? `<a:blip r:embed="${iconRId}"/><a:stretch><a:fillRect/></a:stretch>`
1301
+ : `<a:blip/><a:stretch><a:fillRect/></a:stretch>`;
1302
+ parts.push(`<xdr:twoCellAnchor editAs="oneCell">${fromXml}${toXml}<xdr:sp macro=""><xdr:nvSpPr><xdr:cNvPr id="${id}" name="${escapeXml(ole.name)}" hidden="1"/><xdr:cNvSpPr/></xdr:nvSpPr><xdr:spPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="0" cy="0"/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom></xdr:spPr></xdr:sp><xdr:clientData/></xdr:twoCellAnchor>`);
1303
+ });
1304
+ for (const slicerInfo of this._slicerDrawingInfo) {
1305
+ const id = shapeCounter++;
1306
+ const { row: sRow, col: sCol } = slicerInfo.cell ? cellRefToIndices(slicerInfo.cell) : { row: 1, col: 6 };
1307
+ const fromCol = sCol - 1, fromRow = sRow - 1;
1308
+ const toCol = fromCol + 2, toRow = fromRow + 12;
1309
+ parts.push(`<xdr:twoCellAnchor editAs="oneCell"><xdr:from><xdr:col>${fromCol}</xdr:col><xdr:colOff>0</xdr:colOff><xdr:row>${fromRow}</xdr:row><xdr:rowOff>0</xdr:rowOff></xdr:from><xdr:to><xdr:col>${toCol}</xdr:col><xdr:colOff>0</xdr:colOff><xdr:row>${toRow}</xdr:row><xdr:rowOff>0</xdr:rowOff></xdr:to><mc:AlternateContent xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"><mc:Choice xmlns:sle15="http://schemas.microsoft.com/office/drawing/2012/slicer" Requires="sle15"><xdr:graphicFrame macro=""><xdr:nvGraphicFramePr><xdr:cNvPr id="${id}" name="${escapeXml(slicerInfo.name)}"/><xdr:cNvGraphicFramePr/></xdr:nvGraphicFramePr><xdr:xfrm><a:off x="0" y="0"/><a:ext cx="0" cy="0"/></xdr:xfrm><a:graphic><a:graphicData uri="http://schemas.microsoft.com/office/drawing/2010/slicer"><sle:slicer xmlns:sle="http://schemas.microsoft.com/office/drawing/2010/slicer" name="${escapeXml(slicerInfo.name)}"/></a:graphicData></a:graphic></xdr:graphicFrame></mc:Choice><mc:Fallback/></mc:AlternateContent><xdr:clientData/></xdr:twoCellAnchor>`);
1310
+ }
547
1311
  return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
548
1312
  <xdr:wsDr xmlns:xdr="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing"
1313
+ xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"
549
1314
  xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
550
1315
  ${parts.join('\n')}
551
1316
  </xdr:wsDr>`;