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