@node-projects/excelforge 2.4.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 -63
  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,18 +1,23 @@
1
1
  import { readZip, entryText } from '../utils/zipReader.js';
2
- import { parseXml, child, children, localName, nodeToXml } from '../utils/xmlParser.js';
2
+ import { parseXml, child, children, attr, localName, nodeToXml } from '../utils/xmlParser.js';
3
3
  import { parseCoreXml, parseAppXml, parseCustomXml, } from './properties.js';
4
+ import { CellError, } from './types.js';
4
5
  import { Worksheet } from './Worksheet.js';
5
6
  import { cellRefToIndices } from '../utils/helpers.js';
7
+ import { OBJ_TYPE_TO_CTRL, CHECKED_REV } from '../features/FormControlBuilder.js';
6
8
  function parseRels(xml) {
7
9
  const map = new Map();
8
10
  try {
9
11
  const root = parseXml(xml);
10
12
  for (const c of root.children) {
11
13
  if (localName(c.tag) === 'Relationship') {
12
- map.set(c.attrs['Id'] ?? '', {
14
+ const entry = {
13
15
  type: c.attrs['Type'] ?? '',
14
16
  target: c.attrs['Target'] ?? '',
15
- });
17
+ };
18
+ if (c.attrs['TargetMode'])
19
+ entry.targetMode = c.attrs['TargetMode'];
20
+ map.set(c.attrs['Id'] ?? '', entry);
16
21
  }
17
22
  }
18
23
  }
@@ -119,7 +124,33 @@ function parseStyles(xml) {
119
124
  xfs.push(style);
120
125
  }
121
126
  }
122
- return { xfs, numFmts };
127
+ const dxfs = [];
128
+ const dxfsNode = find(root, 'dxfs');
129
+ if (dxfsNode) {
130
+ for (const dxf of children(dxfsNode, 'dxf')) {
131
+ const style = {};
132
+ const fontNode = child(dxf, 'font');
133
+ if (fontNode)
134
+ style.font = parseFont(fontNode);
135
+ const fillNode = child(dxf, 'fill');
136
+ if (fillNode)
137
+ style.fill = parseFill(fillNode);
138
+ const borderNode = child(dxf, 'border');
139
+ if (borderNode)
140
+ style.border = parseBorder(borderNode);
141
+ const numFmtDxf = child(dxf, 'numFmt');
142
+ if (numFmtDxf) {
143
+ const code = numFmtDxf.attrs['formatCode'] ?? '';
144
+ if (code)
145
+ style.numberFormat = { formatCode: code };
146
+ }
147
+ const alignDxf = child(dxf, 'alignment');
148
+ if (alignDxf)
149
+ style.alignment = parseAlignment(alignDxf);
150
+ dxfs.push(style);
151
+ }
152
+ }
153
+ return { xfs, numFmts, dxfs };
123
154
  }
124
155
  function find(node, localTag) {
125
156
  if (localName(node.tag) === localTag)
@@ -166,9 +197,12 @@ function parseFont(node) {
166
197
  f.vertAlign = c.attrs['val'];
167
198
  break;
168
199
  case 'color': {
169
- const rgb = c.attrs['rgb'] ?? c.attrs['theme'];
200
+ const rgb = c.attrs['rgb'];
201
+ const theme = c.attrs['theme'];
170
202
  if (rgb)
171
203
  f.color = rgb;
204
+ else if (theme)
205
+ f.color = `theme:${theme}`;
172
206
  break;
173
207
  }
174
208
  }
@@ -183,8 +217,8 @@ function parseFill(node) {
183
217
  return {
184
218
  type: 'pattern',
185
219
  pattern: (pattern.attrs['patternType'] ?? 'none'),
186
- fgColor: fg?.attrs['rgb'] ?? fg?.attrs['theme'],
187
- bgColor: bg?.attrs['rgb'] ?? bg?.attrs['theme'],
220
+ fgColor: fg?.attrs['rgb'] ?? (fg?.attrs['theme'] ? `theme:${fg.attrs['theme']}` : undefined),
221
+ bgColor: bg?.attrs['rgb'] ?? (bg?.attrs['theme'] ? `theme:${bg.attrs['theme']}` : undefined),
188
222
  };
189
223
  }
190
224
  const gradient = child(node, 'gradientFill');
@@ -193,7 +227,7 @@ function parseFill(node) {
193
227
  const colorNode = child(s, 'color');
194
228
  return {
195
229
  position: parseFloat(s.attrs['position'] ?? '0'),
196
- color: colorNode?.attrs['rgb'] ?? colorNode?.attrs['theme'] ?? 'FF000000',
230
+ color: colorNode?.attrs['rgb'] ?? (colorNode?.attrs['theme'] ? `theme:${colorNode.attrs['theme']}` : 'FF000000'),
197
231
  };
198
232
  });
199
233
  return {
@@ -214,7 +248,8 @@ function parseBorder(node) {
214
248
  const color = child(n, 'color');
215
249
  if (!style && !color)
216
250
  return undefined;
217
- return { style: style, color: color?.attrs['rgb'] };
251
+ const colorVal = color?.attrs['rgb'] ?? (color?.attrs['theme'] ? `theme:${color.attrs['theme']}` : undefined);
252
+ return { style: style, color: colorVal };
218
253
  };
219
254
  return {
220
255
  left: parseSide('left'),
@@ -248,9 +283,22 @@ function parseSharedStrings(xml) {
248
283
  const root = parseXml(xml);
249
284
  return children(root, 'si').map(si => {
250
285
  const t = child(si, 't');
251
- if (t && !child(si, 'r'))
252
- return t.text ?? '';
253
- return children(si, 'r').map(r => child(r, 't')?.text ?? '').join('');
286
+ const runs = children(si, 'r');
287
+ if (t && !runs.length)
288
+ return { text: t.text ?? '' };
289
+ const richRuns = runs.map(r => {
290
+ const runText = child(r, 't')?.text ?? '';
291
+ const rPr = child(r, 'rPr');
292
+ const run = { text: runText };
293
+ if (rPr)
294
+ run.font = parseFont(rPr);
295
+ return run;
296
+ });
297
+ const plainText = richRuns.map(r => r.text).join('');
298
+ const hasFormatting = richRuns.some(r => r.font && Object.keys(r.font).length > 0);
299
+ if (hasFormatting)
300
+ return { text: plainText, richText: richRuns };
301
+ return { text: plainText };
254
302
  });
255
303
  }
256
304
  function parseWorksheet(xml, name, styles, sharedStrings) {
@@ -258,12 +306,17 @@ function parseWorksheet(xml, name, styles, sharedStrings) {
258
306
  const root = parseXml(xml);
259
307
  const unknownParts = [];
260
308
  const tableRIds = [];
309
+ let legacyDrawingRId = '';
310
+ let drawingRId = '';
311
+ const ctrlPropRIds = [];
312
+ const vmCells = new Map();
261
313
  const knownTags = new Set([
262
314
  'sheetPr', 'dimension', 'sheetViews', 'sheetFormatPr', 'cols',
263
315
  'sheetData', 'mergeCells', 'conditionalFormatting', 'dataValidations',
264
316
  'sheetProtection', 'printOptions', 'pageMargins', 'pageSetup',
265
317
  'headerFooter', 'drawing', 'tableParts', 'autoFilter',
266
318
  'rowBreaks', 'colBreaks', 'picture', 'oleObjects', 'ctrlProps',
319
+ 'legacyDrawing', 'AlternateContent', 'extLst',
267
320
  ]);
268
321
  for (const node of root.children) {
269
322
  const tag = localName(node.tag);
@@ -275,7 +328,7 @@ function parseWorksheet(xml, name, styles, sharedStrings) {
275
328
  parseCols(node, ws, styles);
276
329
  break;
277
330
  case 'sheetData':
278
- parseSheetData(node, ws, styles, sharedStrings);
331
+ parseSheetData(node, ws, styles, sharedStrings, vmCells);
279
332
  break;
280
333
  case 'mergeCells':
281
334
  parseMerges(node, ws);
@@ -305,6 +358,60 @@ function parseWorksheet(xml, name, styles, sharedStrings) {
305
358
  case 'printOptions':
306
359
  parsePrintOptions(node, ws);
307
360
  break;
361
+ case 'conditionalFormatting':
362
+ parseConditionalFormatting(node, ws, styles);
363
+ break;
364
+ case 'dataValidations':
365
+ parseDataValidations(node, ws);
366
+ break;
367
+ case 'rowBreaks':
368
+ for (const brk of children(node, 'brk')) {
369
+ const id = parseInt(brk.attrs['id'] ?? '0', 10);
370
+ if (id > 0)
371
+ ws.addRowBreak(id, brk.attrs['man'] === '1');
372
+ }
373
+ break;
374
+ case 'colBreaks':
375
+ for (const brk of children(node, 'brk')) {
376
+ const id = parseInt(brk.attrs['id'] ?? '0', 10);
377
+ if (id > 0)
378
+ ws.addColBreak(id, brk.attrs['man'] === '1');
379
+ }
380
+ break;
381
+ case 'drawing':
382
+ drawingRId = node.attrs['r:id'] ?? '';
383
+ break;
384
+ case 'legacyDrawing':
385
+ legacyDrawingRId = node.attrs['r:id'] ?? '';
386
+ break;
387
+ case 'AlternateContent': {
388
+ const choiceNode = node.children.find((c) => typeof c !== 'string' && localName(c.tag) === 'Choice');
389
+ const controlsNode = choiceNode ? choiceNode.children.find((c) => typeof c !== 'string' && localName(c.tag) === 'controls') : undefined;
390
+ if (controlsNode) {
391
+ for (const acNode of controlsNode.children) {
392
+ if (typeof acNode === 'string')
393
+ continue;
394
+ let ctrlNode;
395
+ if (localName(acNode.tag) === 'control') {
396
+ ctrlNode = acNode;
397
+ }
398
+ else if (localName(acNode.tag) === 'AlternateContent') {
399
+ const innerChoice = acNode.children.find((c) => typeof c !== 'string' && localName(c.tag) === 'Choice');
400
+ ctrlNode = innerChoice?.children.find((c) => typeof c !== 'string' && localName(c.tag) === 'control');
401
+ }
402
+ if (!ctrlNode)
403
+ continue;
404
+ const controlPr = ctrlNode.children.find((c) => typeof c !== 'string' && localName(c.tag) === 'controlPr');
405
+ const ctrlRId = ctrlNode.attrs['r:id'] ?? controlPr?.attrs['r:id'] ?? '';
406
+ if (ctrlRId)
407
+ ctrlPropRIds.push(ctrlRId);
408
+ }
409
+ }
410
+ break;
411
+ }
412
+ case 'extLst':
413
+ parseSparklineExtensions(node, ws);
414
+ break;
308
415
  default:
309
416
  if (!knownTags.has(tag)) {
310
417
  unknownParts.push(nodeToXml(node));
@@ -312,7 +419,7 @@ function parseWorksheet(xml, name, styles, sharedStrings) {
312
419
  break;
313
420
  }
314
421
  }
315
- return { ws, originalXml: xml, unknownParts, tableRIds };
422
+ return { ws, originalXml: xml, unknownParts, tableRIds, legacyDrawingRId, ctrlPropRIds, drawingRId, vmCells };
316
423
  }
317
424
  function parseSheetViews(node, ws) {
318
425
  const sv = children(node, 'sheetView')[0];
@@ -349,7 +456,7 @@ function parseCols(node, ws, styles) {
349
456
  ws.setColumn(c, def);
350
457
  }
351
458
  }
352
- function parseSheetData(node, ws, styles, sharedStrings) {
459
+ function parseSheetData(node, ws, styles, sharedStrings, vmCells) {
353
460
  for (const rowNode of children(node, 'row')) {
354
461
  const rowIdx = parseInt(rowNode.attrs['r'] ?? '0', 10);
355
462
  if (!rowIdx)
@@ -380,6 +487,10 @@ function parseSheetData(node, ws, styles, sharedStrings) {
380
487
  const cell = {};
381
488
  if (cellStyle)
382
489
  cell.style = cellStyle;
490
+ const vmAttr = cNode.attrs['vm'];
491
+ if (vmAttr && vmCells) {
492
+ vmCells.set(ref, parseInt(vmAttr, 10));
493
+ }
383
494
  if (fNode) {
384
495
  if (fNode.attrs['t'] === 'array') {
385
496
  cell.arrayFormula = fNode.text ?? '';
@@ -393,7 +504,15 @@ function parseSheetData(node, ws, styles, sharedStrings) {
393
504
  switch (t) {
394
505
  case 's': {
395
506
  const idx = parseInt(raw, 10);
396
- cell.value = sharedStrings[idx] ?? '';
507
+ const ss = sharedStrings[idx];
508
+ if (ss) {
509
+ cell.value = ss.text;
510
+ if (ss.richText)
511
+ cell.richText = ss.richText;
512
+ }
513
+ else {
514
+ cell.value = '';
515
+ }
397
516
  break;
398
517
  }
399
518
  case 'b':
@@ -406,7 +525,7 @@ function parseSheetData(node, ws, styles, sharedStrings) {
406
525
  break;
407
526
  }
408
527
  case 'e':
409
- cell.value = raw;
528
+ cell.value = new CellError(raw);
410
529
  break;
411
530
  default: {
412
531
  const n = parseFloat(raw);
@@ -490,6 +609,115 @@ function parsePrintOptions(node, ws) {
490
609
  centerVertical: node.attrs['verticalCentered'] === '1',
491
610
  };
492
611
  }
612
+ function parseConditionalFormatting(node, ws, styles) {
613
+ const sqref = node.attrs['sqref'] ?? '';
614
+ for (const rule of children(node, 'cfRule')) {
615
+ const type = (rule.attrs['type'] ?? 'cellIs');
616
+ const cf = { sqref, type };
617
+ if (rule.attrs['operator'])
618
+ cf.operator = rule.attrs['operator'];
619
+ if (rule.attrs['priority'])
620
+ cf.priority = parseInt(rule.attrs['priority'], 10);
621
+ if (rule.attrs['text'])
622
+ cf.text = rule.attrs['text'];
623
+ if (rule.attrs['aboveAverage'] === '0')
624
+ cf.aboveAverage = false;
625
+ if (rule.attrs['percent'] === '1')
626
+ cf.percent = true;
627
+ if (rule.attrs['rank'])
628
+ cf.rank = parseInt(rule.attrs['rank'], 10);
629
+ if (rule.attrs['timePeriod'])
630
+ cf.timePeriod = rule.attrs['timePeriod'];
631
+ if (rule.attrs['dxfId'] !== undefined) {
632
+ const dxfId = parseInt(rule.attrs['dxfId'], 10);
633
+ if (styles.dxfs[dxfId])
634
+ cf.style = styles.dxfs[dxfId];
635
+ }
636
+ const formulas = children(rule, 'formula');
637
+ if (formulas[0]?.text)
638
+ cf.formula = formulas[0].text;
639
+ if (formulas[1]?.text)
640
+ cf.formula2 = formulas[1].text;
641
+ const csNode = child(rule, 'colorScale');
642
+ if (csNode) {
643
+ const cfvos = children(csNode, 'cfvo').map(c => ({
644
+ type: (c.attrs['type'] ?? 'min'),
645
+ val: c.attrs['val'],
646
+ }));
647
+ const colors = children(csNode, 'color').map(c => c.attrs['rgb'] ?? c.attrs['theme'] ?? '');
648
+ cf.colorScale = { type: 'colorScale', cfvo: cfvos, color: colors };
649
+ }
650
+ const dbNode = child(rule, 'dataBar');
651
+ if (dbNode) {
652
+ const cfvos = children(dbNode, 'cfvo');
653
+ const colorNode = child(dbNode, 'color');
654
+ cf.dataBar = {
655
+ type: 'dataBar',
656
+ showValue: dbNode.attrs['showValue'] !== '0' ? undefined : false,
657
+ minType: cfvos[0]?.attrs['type'],
658
+ minVal: cfvos[0]?.attrs['val'],
659
+ maxType: cfvos[1]?.attrs['type'],
660
+ maxVal: cfvos[1]?.attrs['val'],
661
+ color: colorNode?.attrs['rgb'],
662
+ };
663
+ }
664
+ const isNode = child(rule, 'iconSet');
665
+ if (isNode) {
666
+ const cfvos = children(isNode, 'cfvo').map(c => ({
667
+ type: c.attrs['type'] ?? 'percent',
668
+ val: c.attrs['val'],
669
+ }));
670
+ cf.iconSet = {
671
+ type: 'iconSet',
672
+ iconSet: (isNode.attrs['iconSet'] ?? '3TrafficLights1'),
673
+ cfvo: cfvos,
674
+ showValue: isNode.attrs['showValue'] === '0' ? false : undefined,
675
+ reverse: isNode.attrs['reverse'] === '1' ? true : undefined,
676
+ };
677
+ }
678
+ ws.addConditionalFormat(cf);
679
+ }
680
+ }
681
+ function parseDataValidations(node, ws) {
682
+ for (const dv of children(node, 'dataValidation')) {
683
+ const sqref = dv.attrs['sqref'] ?? '';
684
+ if (!sqref)
685
+ continue;
686
+ const type = (dv.attrs['type'] ?? 'whole');
687
+ const val = { type };
688
+ if (dv.attrs['operator'])
689
+ val.operator = dv.attrs['operator'];
690
+ if (dv.attrs['allowBlank'] === '1')
691
+ val.allowBlank = true;
692
+ if (dv.attrs['showErrorMessage'] === '1')
693
+ val.showErrorAlert = true;
694
+ if (dv.attrs['errorTitle'])
695
+ val.errorTitle = dv.attrs['errorTitle'];
696
+ if (dv.attrs['error'])
697
+ val.error = dv.attrs['error'];
698
+ if (dv.attrs['showInputMessage'] === '1')
699
+ val.showInputMessage = true;
700
+ if (dv.attrs['promptTitle'])
701
+ val.promptTitle = dv.attrs['promptTitle'];
702
+ if (dv.attrs['prompt'])
703
+ val.prompt = dv.attrs['prompt'];
704
+ if (dv.attrs['showDropDown'] === '1')
705
+ val.showDropDown = false;
706
+ const f1 = child(dv, 'formula1');
707
+ const f2 = child(dv, 'formula2');
708
+ if (f1?.text) {
709
+ if (type === 'list' && f1.text.startsWith('"') && f1.text.endsWith('"')) {
710
+ val.list = f1.text.slice(1, -1).split(',');
711
+ }
712
+ else {
713
+ val.formula1 = f1.text;
714
+ }
715
+ }
716
+ if (f2?.text)
717
+ val.formula2 = f2.text;
718
+ ws.addDataValidation(sqref, val);
719
+ }
720
+ }
493
721
  function parseTableXml(xml) {
494
722
  try {
495
723
  const root = parseXml(xml);
@@ -538,6 +766,873 @@ function parseTableXml(xml) {
538
766
  return null;
539
767
  }
540
768
  }
769
+ const EMU_PX = 9525;
770
+ function parseAnchorPos(node) {
771
+ return {
772
+ col: parseInt(child(node, 'col')?.text ?? '0', 10),
773
+ row: parseInt(child(node, 'row')?.text ?? '0', 10),
774
+ colOff: parseInt(child(node, 'colOff')?.text ?? '0', 10),
775
+ rowOff: parseInt(child(node, 'rowOff')?.text ?? '0', 10),
776
+ };
777
+ }
778
+ function extToFormat(ext) {
779
+ const map = {
780
+ png: 'png', jpg: 'jpeg', jpeg: 'jpeg', gif: 'gif',
781
+ emf: 'emf', wmf: 'wmf', tiff: 'tiff', tif: 'tiff',
782
+ svg: 'svg', ico: 'ico', webp: 'webp', bmp: 'bmp',
783
+ };
784
+ return map[ext.toLowerCase()] ?? 'png';
785
+ }
786
+ function parseDrawingObjects(drawXml, drawRels, drawDir, getEntry, ws) {
787
+ const root = parseXml(drawXml);
788
+ for (const anchor of root.children) {
789
+ if (typeof anchor === 'string')
790
+ continue;
791
+ const tag = localName(anchor.tag);
792
+ if (tag !== 'twoCellAnchor' && tag !== 'oneCellAnchor' && tag !== 'absoluteAnchor')
793
+ continue;
794
+ const anchorFrom = parseDrawingAnchorFrom(anchor, tag);
795
+ const pic = findDescendant(anchor, 'pic');
796
+ if (pic) {
797
+ parseDrawingImage(pic, anchor, tag, anchorFrom, drawRels, drawDir, getEntry, ws);
798
+ continue;
799
+ }
800
+ const graphicFrame = findDescendant(anchor, 'graphicFrame');
801
+ if (graphicFrame) {
802
+ parseDrawingChart(graphicFrame, anchor, tag, drawRels, drawDir, getEntry, ws);
803
+ continue;
804
+ }
805
+ const oMath = findDescendant(anchor, 'oMath');
806
+ if (oMath) {
807
+ parseDrawingMathEquation(oMath, anchor, tag, anchorFrom, ws);
808
+ continue;
809
+ }
810
+ const sp = findDescendant(anchor, 'sp');
811
+ if (sp) {
812
+ parseDrawingShape(sp, anchor, tag, ws);
813
+ continue;
814
+ }
815
+ }
816
+ }
817
+ function parseDrawingChart(graphicFrame, anchor, tag, drawRels, drawDir, getEntry, ws) {
818
+ const chartRef = findDescendant(graphicFrame, 'chart');
819
+ if (!chartRef || !drawRels)
820
+ return;
821
+ const rId = chartRef.attrs['r:id'] ?? attr(chartRef, 'r:id') ?? '';
822
+ if (!rId)
823
+ return;
824
+ const rel = drawRels.get(rId);
825
+ if (!rel)
826
+ return;
827
+ const chartPath = rel.target.startsWith('/')
828
+ ? rel.target.slice(1)
829
+ : resolvePath(drawDir, rel.target);
830
+ const chartEntry = getEntry(chartPath);
831
+ if (!chartEntry)
832
+ return;
833
+ const dec = new TextDecoder();
834
+ const chartXml = dec.decode(chartEntry.data);
835
+ const chartRoot = parseXml(chartXml);
836
+ let from = { col: 0, row: 0 };
837
+ let to = { col: 8, row: 15 };
838
+ if (tag === 'twoCellAnchor') {
839
+ const fromNode = child(anchor, 'from');
840
+ const toNode = child(anchor, 'to');
841
+ if (fromNode) {
842
+ from = {
843
+ col: parseInt(child(fromNode, 'col')?.text ?? '0', 10),
844
+ row: parseInt(child(fromNode, 'row')?.text ?? '0', 10),
845
+ };
846
+ }
847
+ if (toNode) {
848
+ to = {
849
+ col: parseInt(child(toNode, 'col')?.text ?? '0', 10),
850
+ row: parseInt(child(toNode, 'row')?.text ?? '0', 10),
851
+ };
852
+ }
853
+ }
854
+ const plotArea = findDescendant(chartRoot, 'plotArea');
855
+ if (!plotArea)
856
+ return;
857
+ const chartTypeMap = {
858
+ 'barChart': 'column', 'bar3DChart': 'column',
859
+ 'lineChart': 'line', 'line3DChart': 'line',
860
+ 'areaChart': 'area', 'area3DChart': 'area',
861
+ 'pieChart': 'pie', 'pie3DChart': 'pie',
862
+ 'doughnutChart': 'doughnut',
863
+ 'scatterChart': 'scatter',
864
+ 'bubbleChart': 'bubble',
865
+ 'radarChart': 'radar',
866
+ 'stockChart': 'stock',
867
+ };
868
+ let chartType = 'column';
869
+ let chartGroupNode;
870
+ for (const plotChild of plotArea.children) {
871
+ if (typeof plotChild === 'string')
872
+ continue;
873
+ const localTag = localName(plotChild.tag);
874
+ if (chartTypeMap[localTag]) {
875
+ chartType = chartTypeMap[localTag];
876
+ chartGroupNode = plotChild;
877
+ if (localTag === 'barChart' || localTag === 'bar3DChart') {
878
+ const barDir = child(plotChild, 'barDir');
879
+ const barDirVal = barDir?.attrs['val'] ?? '';
880
+ if (barDirVal === 'bar')
881
+ chartType = 'bar';
882
+ const grouping = child(plotChild, 'grouping');
883
+ const grpVal = grouping?.attrs['val'] ?? '';
884
+ if (grpVal === 'stacked')
885
+ chartType = (chartType === 'bar' ? 'barStacked' : 'columnStacked');
886
+ if (grpVal === 'percentStacked')
887
+ chartType = (chartType === 'bar' ? 'barStacked100' : 'columnStacked100');
888
+ }
889
+ if (localTag === 'lineChart') {
890
+ const marker = findDescendant(plotChild, 'marker');
891
+ if (marker) {
892
+ const markerVal = child(marker, 'symbol')?.attrs['val'] ?? '';
893
+ if (markerVal && markerVal !== 'none')
894
+ chartType = 'lineMarker';
895
+ }
896
+ }
897
+ if (localTag === 'scatterChart') {
898
+ const scatterStyle = child(plotChild, 'scatterStyle');
899
+ if (scatterStyle?.attrs['val']?.includes('Smooth'))
900
+ chartType = 'scatterSmooth';
901
+ }
902
+ if (localTag === 'radarChart') {
903
+ const radarStyle = child(plotChild, 'radarStyle');
904
+ if (radarStyle?.attrs['val'] === 'filled')
905
+ chartType = 'radarFilled';
906
+ }
907
+ break;
908
+ }
909
+ }
910
+ if (!chartGroupNode)
911
+ return;
912
+ const series = [];
913
+ for (const ser of children(chartGroupNode, 'ser')) {
914
+ const s = { values: '' };
915
+ const tx = child(ser, 'tx');
916
+ if (tx) {
917
+ const strRef = findDescendant(tx, 'strRef');
918
+ if (strRef) {
919
+ const f = child(strRef, 'f');
920
+ if (f?.text)
921
+ s.name = f.text;
922
+ const strCache = child(strRef, 'strCache');
923
+ if (strCache) {
924
+ const pt = findDescendant(strCache, 'pt');
925
+ const v = pt ? findDescendant(pt, 'v') : undefined;
926
+ if (v?.text)
927
+ s.name = v.text;
928
+ }
929
+ }
930
+ const v = findDescendant(tx, 'v');
931
+ if (v?.text && !s.name)
932
+ s.name = v.text;
933
+ }
934
+ const val = child(ser, 'val') || child(ser, 'yVal');
935
+ if (val) {
936
+ const numRef = findDescendant(val, 'numRef');
937
+ if (numRef) {
938
+ const f = child(numRef, 'f');
939
+ if (f?.text)
940
+ s.values = f.text;
941
+ }
942
+ }
943
+ const cat = child(ser, 'cat') || child(ser, 'xVal');
944
+ if (cat) {
945
+ const strRef = findDescendant(cat, 'strRef');
946
+ const numRef = findDescendant(cat, 'numRef');
947
+ const ref = strRef || numRef;
948
+ if (ref) {
949
+ const f = child(ref, 'f');
950
+ if (f?.text)
951
+ s.categories = f.text;
952
+ }
953
+ }
954
+ const spPr = child(ser, 'spPr');
955
+ if (spPr) {
956
+ const solidFill = child(spPr, 'solidFill');
957
+ if (solidFill) {
958
+ const srgb = child(solidFill, 'srgbClr');
959
+ if (srgb?.attrs['val'])
960
+ s.color = '#' + srgb.attrs['val'];
961
+ }
962
+ }
963
+ if (s.values)
964
+ series.push(s);
965
+ }
966
+ if (!series.length)
967
+ return;
968
+ let title;
969
+ const titleNode = child(chartRoot, 'title') || findDescendant(chartRoot, 'title');
970
+ if (titleNode) {
971
+ const aT = findDescendant(titleNode, 't');
972
+ if (aT?.text)
973
+ title = aT.text;
974
+ }
975
+ let xAxis;
976
+ let yAxis;
977
+ const catAx = findDescendant(plotArea, 'catAx') || findDescendant(plotArea, 'dateAx');
978
+ const valAx = findDescendant(plotArea, 'valAx');
979
+ if (catAx) {
980
+ xAxis = {};
981
+ const axTitle = findDescendant(catAx, 'title');
982
+ if (axTitle) {
983
+ const t = findDescendant(axTitle, 't');
984
+ if (t?.text)
985
+ xAxis.title = t.text;
986
+ }
987
+ }
988
+ if (valAx) {
989
+ yAxis = {};
990
+ const axTitle = findDescendant(valAx, 'title');
991
+ if (axTitle) {
992
+ const t = findDescendant(axTitle, 't');
993
+ if (t?.text)
994
+ yAxis.title = t.text;
995
+ }
996
+ const gridLines = child(valAx, 'majorGridlines');
997
+ if (gridLines)
998
+ yAxis.gridLines = true;
999
+ }
1000
+ let legend = false;
1001
+ const legendNode = findDescendant(chartRoot, 'legend');
1002
+ if (legendNode) {
1003
+ const legendPos = child(legendNode, 'legendPos');
1004
+ const posVal = legendPos?.attrs['val'];
1005
+ legend = posVal || true;
1006
+ }
1007
+ const varyColors = child(chartGroupNode, 'varyColors');
1008
+ const varyColorsVal = varyColors?.attrs['val'] !== '0';
1009
+ const chart = { type: chartType, series, from, to };
1010
+ if (title)
1011
+ chart.title = title;
1012
+ if (xAxis)
1013
+ chart.xAxis = xAxis;
1014
+ if (yAxis)
1015
+ chart.yAxis = yAxis;
1016
+ if (legend !== false)
1017
+ chart.legend = legend;
1018
+ if (varyColorsVal)
1019
+ chart.varyColors = true;
1020
+ ws.addChart(chart);
1021
+ }
1022
+ function parseDrawingShape(sp, anchor, tag, ws) {
1023
+ let from = { col: 0, row: 0 };
1024
+ let to = { col: 2, row: 2 };
1025
+ if (tag === 'twoCellAnchor') {
1026
+ const fromNode = child(anchor, 'from');
1027
+ const toNode = child(anchor, 'to');
1028
+ if (fromNode) {
1029
+ from = {
1030
+ col: parseInt(child(fromNode, 'col')?.text ?? '0', 10),
1031
+ row: parseInt(child(fromNode, 'row')?.text ?? '0', 10),
1032
+ };
1033
+ }
1034
+ if (toNode) {
1035
+ to = {
1036
+ col: parseInt(child(toNode, 'col')?.text ?? '0', 10),
1037
+ row: parseInt(child(toNode, 'row')?.text ?? '0', 10),
1038
+ };
1039
+ }
1040
+ }
1041
+ const spPr = findDescendant(sp, 'spPr');
1042
+ let shapeType = 'rect';
1043
+ let fillColor;
1044
+ let lineColor;
1045
+ let lineWidth;
1046
+ let rotation;
1047
+ if (spPr) {
1048
+ const prstGeom = findDescendant(spPr, 'prstGeom');
1049
+ if (prstGeom) {
1050
+ const prst = prstGeom.attrs['prst'] ?? '';
1051
+ const validTypes = new Set([
1052
+ 'rect', 'roundRect', 'ellipse', 'triangle', 'diamond',
1053
+ 'pentagon', 'hexagon', 'octagon', 'star5', 'star6',
1054
+ 'rightArrow', 'leftArrow', 'upArrow', 'downArrow',
1055
+ 'line', 'curvedConnector3', 'callout1', 'callout2',
1056
+ 'cloud', 'heart', 'lightningBolt', 'sun', 'moon',
1057
+ 'smileyFace', 'flowChartProcess', 'flowChartDecision',
1058
+ 'flowChartTerminator', 'flowChartDocument',
1059
+ ]);
1060
+ if (validTypes.has(prst))
1061
+ shapeType = prst;
1062
+ }
1063
+ const solidFill = child(spPr, 'solidFill');
1064
+ if (solidFill) {
1065
+ const srgb = child(solidFill, 'srgbClr');
1066
+ if (srgb?.attrs['val'])
1067
+ fillColor = '#' + srgb.attrs['val'];
1068
+ const schemeClr = child(solidFill, 'schemeClr');
1069
+ if (schemeClr?.attrs['val'])
1070
+ fillColor = 'theme:' + schemeClr.attrs['val'];
1071
+ }
1072
+ const ln = child(spPr, 'ln');
1073
+ if (ln) {
1074
+ const w = ln.attrs['w'];
1075
+ if (w)
1076
+ lineWidth = Math.round(parseInt(w, 10) / EMU_PX);
1077
+ const lineFill = child(ln, 'solidFill');
1078
+ if (lineFill) {
1079
+ const srgb = child(lineFill, 'srgbClr');
1080
+ if (srgb?.attrs['val'])
1081
+ lineColor = '#' + srgb.attrs['val'];
1082
+ }
1083
+ }
1084
+ const xfrm = findDescendant(spPr, 'xfrm');
1085
+ if (xfrm?.attrs['rot'])
1086
+ rotation = parseInt(xfrm.attrs['rot'], 10) / 60000;
1087
+ }
1088
+ let text;
1089
+ let font;
1090
+ const txBody = findDescendant(sp, 'txBody');
1091
+ if (txBody) {
1092
+ const bodyPr = child(txBody, 'bodyPr');
1093
+ const prstTxWarp = bodyPr ? child(bodyPr, 'prstTxWarp') : undefined;
1094
+ const wordArtPreset = prstTxWarp?.attrs['prst'] ?? bodyPr?.attrs['prstTxWarp'] ?? '';
1095
+ const textParts = [];
1096
+ for (const p of children(txBody, 'p')) {
1097
+ for (const r of children(p, 'r')) {
1098
+ const t = child(r, 't');
1099
+ if (t?.text)
1100
+ textParts.push(t.text);
1101
+ if (!font) {
1102
+ const rPr = child(r, 'rPr');
1103
+ if (rPr) {
1104
+ font = {};
1105
+ if (rPr.attrs['sz'])
1106
+ font.size = parseInt(rPr.attrs['sz'], 10) / 100;
1107
+ if (rPr.attrs['b'] === '1')
1108
+ font.bold = true;
1109
+ if (rPr.attrs['i'] === '1')
1110
+ font.italic = true;
1111
+ const latin = findDescendant(rPr, 'latin');
1112
+ if (latin?.attrs['typeface'])
1113
+ font.name = latin.attrs['typeface'];
1114
+ const solidFill = child(rPr, 'solidFill');
1115
+ if (solidFill) {
1116
+ const srgb = child(solidFill, 'srgbClr');
1117
+ if (srgb?.attrs['val'])
1118
+ font.color = '#' + srgb.attrs['val'];
1119
+ }
1120
+ }
1121
+ }
1122
+ }
1123
+ }
1124
+ text = textParts.join('');
1125
+ const validWordArtPresets = new Set([
1126
+ 'textPlain', 'textArchUp', 'textArchDown', 'textCircle',
1127
+ 'textWave1', 'textWave2', 'textInflate', 'textDeflate',
1128
+ 'textFadeUp', 'textFadeDown', 'textFadeLeft', 'textFadeRight',
1129
+ 'textSlantUp', 'textSlantDown', 'textCascadeUp', 'textCascadeDown',
1130
+ 'textChevron', 'textRingInside', 'textRingOutside', 'textStop',
1131
+ ]);
1132
+ if (wordArtPreset && validWordArtPresets.has(wordArtPreset) && text) {
1133
+ const wa = { text, from, to };
1134
+ if (wordArtPreset !== 'textPlain')
1135
+ wa.preset = wordArtPreset;
1136
+ if (font)
1137
+ wa.font = font;
1138
+ if (fillColor)
1139
+ wa.fillColor = fillColor;
1140
+ if (lineColor)
1141
+ wa.outlineColor = lineColor;
1142
+ ws.addWordArt(wa);
1143
+ return;
1144
+ }
1145
+ }
1146
+ const shape = { type: shapeType, from, to };
1147
+ if (text)
1148
+ shape.text = text;
1149
+ if (fillColor)
1150
+ shape.fillColor = fillColor;
1151
+ if (lineColor)
1152
+ shape.lineColor = lineColor;
1153
+ if (lineWidth)
1154
+ shape.lineWidth = lineWidth;
1155
+ if (font)
1156
+ shape.font = font;
1157
+ if (rotation)
1158
+ shape.rotation = rotation;
1159
+ ws.addShape(shape);
1160
+ }
1161
+ function parseSparklineExtensions(extLst, ws) {
1162
+ for (const ext of children(extLst, 'ext')) {
1163
+ const sparklineGroups = ext.children?.filter(c => typeof c !== 'string' && localName(c.tag) === 'sparklineGroups');
1164
+ if (!sparklineGroups?.length)
1165
+ continue;
1166
+ for (const groups of sparklineGroups) {
1167
+ for (const group of children(groups, 'sparklineGroup')) {
1168
+ const typeAttr = group.attrs['type'] ?? 'line';
1169
+ const spkType = typeAttr === 'column' ? 'bar' :
1170
+ typeAttr === 'stacked' ? 'stacked' : 'line';
1171
+ const colorSeries = group.children?.find(c => typeof c !== 'string' && localName(c.tag) === 'colorSeries');
1172
+ const colorHigh = group.children?.find(c => typeof c !== 'string' && localName(c.tag) === 'colorHigh');
1173
+ const colorLow = group.children?.find(c => typeof c !== 'string' && localName(c.tag) === 'colorLow');
1174
+ const colorFirst = group.children?.find(c => typeof c !== 'string' && localName(c.tag) === 'colorFirst');
1175
+ const colorLast = group.children?.find(c => typeof c !== 'string' && localName(c.tag) === 'colorLast');
1176
+ const colorNegative = group.children?.find(c => typeof c !== 'string' && localName(c.tag) === 'colorNegative');
1177
+ const colorMarkers = group.children?.find(c => typeof c !== 'string' && localName(c.tag) === 'colorMarkers');
1178
+ const showHigh = group.attrs['high'] === '1';
1179
+ const showLow = group.attrs['low'] === '1';
1180
+ const showFirst = group.attrs['first'] === '1';
1181
+ const showLast = group.attrs['last'] === '1';
1182
+ const showNegative = group.attrs['negative'] === '1';
1183
+ const showMarkers = group.attrs['markers'] === '1';
1184
+ const lwAttr = group.attrs['lineWeight'];
1185
+ const lineWidth = lwAttr ? parseFloat(lwAttr) : undefined;
1186
+ const minAxisType = group.attrs['minAxisType'] === 'individual' ? 'individual'
1187
+ : group.attrs['minAxisType'] === 'custom' ? 'custom' : undefined;
1188
+ const maxAxisType = group.attrs['maxAxisType'] === 'individual' ? 'individual'
1189
+ : group.attrs['maxAxisType'] === 'custom' ? 'custom' : undefined;
1190
+ const sparklinesNode = group.children?.find(c => typeof c !== 'string' && localName(c.tag) === 'sparklines');
1191
+ if (!sparklinesNode)
1192
+ continue;
1193
+ const sparklineNodes = sparklinesNode.children?.filter(c => typeof c !== 'string' && localName(c.tag) === 'sparkline');
1194
+ if (!sparklineNodes)
1195
+ continue;
1196
+ for (const spk of sparklineNodes) {
1197
+ const fNode = spk.children?.find(c => typeof c !== 'string' && localName(c.tag) === 'f');
1198
+ const sqrefNode = spk.children?.find(c => typeof c !== 'string' && localName(c.tag) === 'sqref');
1199
+ const dataRange = fNode?.text ?? '';
1200
+ const location = sqrefNode?.text ?? '';
1201
+ if (!dataRange || !location)
1202
+ continue;
1203
+ const sp = { type: spkType, dataRange, location };
1204
+ if (colorSeries) {
1205
+ const rgb = colorSeries.attrs['rgb'];
1206
+ if (rgb)
1207
+ sp.color = '#' + rgb.slice(2);
1208
+ const theme = colorSeries.attrs['theme'];
1209
+ if (theme)
1210
+ sp.color = 'theme:' + theme;
1211
+ }
1212
+ if (colorHigh) {
1213
+ const rgb = colorHigh.attrs['rgb'];
1214
+ if (rgb)
1215
+ sp.highColor = '#' + rgb.slice(2);
1216
+ const theme = colorHigh.attrs['theme'];
1217
+ if (theme)
1218
+ sp.highColor = 'theme:' + theme;
1219
+ }
1220
+ if (colorLow) {
1221
+ const rgb = colorLow.attrs['rgb'];
1222
+ if (rgb)
1223
+ sp.lowColor = '#' + rgb.slice(2);
1224
+ const theme = colorLow.attrs['theme'];
1225
+ if (theme)
1226
+ sp.lowColor = 'theme:' + theme;
1227
+ }
1228
+ if (colorFirst) {
1229
+ const rgb = colorFirst.attrs['rgb'];
1230
+ if (rgb)
1231
+ sp.firstColor = '#' + rgb.slice(2);
1232
+ const theme = colorFirst.attrs['theme'];
1233
+ if (theme)
1234
+ sp.firstColor = 'theme:' + theme;
1235
+ }
1236
+ if (colorLast) {
1237
+ const rgb = colorLast.attrs['rgb'];
1238
+ if (rgb)
1239
+ sp.lastColor = '#' + rgb.slice(2);
1240
+ const theme = colorLast.attrs['theme'];
1241
+ if (theme)
1242
+ sp.lastColor = 'theme:' + theme;
1243
+ }
1244
+ if (colorNegative) {
1245
+ const rgb = colorNegative.attrs['rgb'];
1246
+ if (rgb)
1247
+ sp.negativeColor = '#' + rgb.slice(2);
1248
+ const theme = colorNegative.attrs['theme'];
1249
+ if (theme)
1250
+ sp.negativeColor = 'theme:' + theme;
1251
+ }
1252
+ if (colorMarkers) {
1253
+ const rgb = colorMarkers.attrs['rgb'];
1254
+ if (rgb)
1255
+ sp.markersColor = '#' + rgb.slice(2);
1256
+ const theme = colorMarkers.attrs['theme'];
1257
+ if (theme)
1258
+ sp.markersColor = 'theme:' + theme;
1259
+ }
1260
+ if (showHigh)
1261
+ sp.showHigh = true;
1262
+ if (showLow)
1263
+ sp.showLow = true;
1264
+ if (showFirst)
1265
+ sp.showFirst = true;
1266
+ if (showLast)
1267
+ sp.showLast = true;
1268
+ if (showNegative)
1269
+ sp.showNegative = true;
1270
+ if (showMarkers)
1271
+ sp.showMarkers = true;
1272
+ if (lineWidth !== undefined)
1273
+ sp.lineWidth = lineWidth;
1274
+ if (minAxisType)
1275
+ sp.minAxisType = minAxisType;
1276
+ if (maxAxisType)
1277
+ sp.maxAxisType = maxAxisType;
1278
+ ws.addSparkline(sp);
1279
+ }
1280
+ }
1281
+ }
1282
+ }
1283
+ }
1284
+ function parseDrawingAnchorFrom(anchor, tag) {
1285
+ const result = {};
1286
+ if (tag === 'twoCellAnchor' || tag === 'oneCellAnchor') {
1287
+ const fromNode = child(anchor, 'from');
1288
+ if (fromNode) {
1289
+ result.from = {
1290
+ col: parseInt(child(fromNode, 'col')?.text ?? '0', 10),
1291
+ row: parseInt(child(fromNode, 'row')?.text ?? '0', 10),
1292
+ colOff: parseInt(child(fromNode, 'colOff')?.text ?? '0', 10),
1293
+ rowOff: parseInt(child(fromNode, 'rowOff')?.text ?? '0', 10),
1294
+ };
1295
+ }
1296
+ }
1297
+ if (tag === 'oneCellAnchor') {
1298
+ const ext2 = findDescendant(anchor, 'ext');
1299
+ if (ext2) {
1300
+ result.width = parseInt(ext2.attrs['cx'] ?? '0', 10);
1301
+ result.height = parseInt(ext2.attrs['cy'] ?? '0', 10);
1302
+ }
1303
+ }
1304
+ return result;
1305
+ }
1306
+ function parseDrawingImage(pic, anchor, tag, anchorInfo, drawRels, drawDir, getEntry, ws) {
1307
+ const blipFill = findDescendant(pic, 'blipFill');
1308
+ const blip = blipFill ? findDescendant(blipFill, 'blip') : undefined;
1309
+ const embedId = blip?.attrs['r:embed'] ?? blip?.attrs['embed'] ?? '';
1310
+ if (!embedId || !drawRels)
1311
+ return;
1312
+ const imgRel = drawRels.get(embedId);
1313
+ if (!imgRel)
1314
+ return;
1315
+ const imgPath = imgRel.target.startsWith('/')
1316
+ ? imgRel.target.slice(1)
1317
+ : resolvePath(drawDir, imgRel.target);
1318
+ const imgEntry = getEntry(imgPath);
1319
+ if (!imgEntry)
1320
+ return;
1321
+ const ext = imgPath.split('.').pop() ?? 'png';
1322
+ const format = extToFormat(ext);
1323
+ const cNvPr = findDescendant(pic, 'cNvPr');
1324
+ const altText = cNvPr?.attrs['descr'] || undefined;
1325
+ const img = { data: imgEntry.data, format };
1326
+ if (altText)
1327
+ img.altText = altText;
1328
+ if (tag === 'twoCellAnchor') {
1329
+ const fromNode = child(anchor, 'from');
1330
+ const toNode = child(anchor, 'to');
1331
+ if (fromNode)
1332
+ img.from = parseAnchorPos(fromNode);
1333
+ if (toNode)
1334
+ img.to = parseAnchorPos(toNode);
1335
+ }
1336
+ else if (tag === 'oneCellAnchor') {
1337
+ if (anchorInfo.from)
1338
+ img.from = anchorInfo.from;
1339
+ if (anchorInfo.width)
1340
+ img.width = Math.round(anchorInfo.width / EMU_PX);
1341
+ if (anchorInfo.height)
1342
+ img.height = Math.round(anchorInfo.height / EMU_PX);
1343
+ }
1344
+ else {
1345
+ const posNode = child(anchor, 'pos');
1346
+ const ext2 = child(anchor, 'ext');
1347
+ if (posNode) {
1348
+ img.position = {
1349
+ x: Math.round(parseInt(posNode.attrs['x'] ?? '0', 10) / EMU_PX),
1350
+ y: Math.round(parseInt(posNode.attrs['y'] ?? '0', 10) / EMU_PX),
1351
+ };
1352
+ }
1353
+ if (ext2) {
1354
+ img.width = Math.round(parseInt(ext2.attrs['cx'] ?? '0', 10) / EMU_PX);
1355
+ img.height = Math.round(parseInt(ext2.attrs['cy'] ?? '0', 10) / EMU_PX);
1356
+ }
1357
+ }
1358
+ ws.addImage(img);
1359
+ }
1360
+ function parseDrawingMathEquation(oMath, anchor, tag, anchorInfo, ws) {
1361
+ const elements = parseOmmlChildren(oMath);
1362
+ if (!elements.length)
1363
+ return;
1364
+ const from = anchorInfo.from ?? { col: 0, row: 0 };
1365
+ const eq = { elements, from };
1366
+ if (anchorInfo.width)
1367
+ eq.width = anchorInfo.width;
1368
+ if (anchorInfo.height)
1369
+ eq.height = anchorInfo.height;
1370
+ const firstRPr = findDescendant(oMath, 'rPr');
1371
+ if (firstRPr) {
1372
+ const szAttr = firstRPr.attrs['sz'];
1373
+ if (szAttr)
1374
+ eq.fontSize = parseInt(szAttr, 10) / 100;
1375
+ const latin = findDescendant(firstRPr, 'latin');
1376
+ if (latin?.attrs['typeface'])
1377
+ eq.fontName = latin.attrs['typeface'];
1378
+ }
1379
+ ws.addMathEquation(eq);
1380
+ }
1381
+ function parseOmmlChildren(node) {
1382
+ const elements = [];
1383
+ for (const c of node.children) {
1384
+ if (typeof c === 'string')
1385
+ continue;
1386
+ const el = parseOmmlElement(c);
1387
+ if (el)
1388
+ elements.push(el);
1389
+ }
1390
+ return elements;
1391
+ }
1392
+ function parseOmmlElement(node) {
1393
+ const tag = localName(node.tag);
1394
+ switch (tag) {
1395
+ case 'r': {
1396
+ const t = findDescendant(node, 't');
1397
+ return { type: 'text', text: t?.text ?? '' };
1398
+ }
1399
+ case 'f': {
1400
+ const num = child(node, 'num');
1401
+ const den = child(node, 'den');
1402
+ return {
1403
+ type: 'frac',
1404
+ base: num ? parseOmmlChildren(num) : [],
1405
+ argument: den ? parseOmmlChildren(den) : [],
1406
+ };
1407
+ }
1408
+ case 'sSup': {
1409
+ const e = child(node, 'e');
1410
+ const sup = child(node, 'sup');
1411
+ return {
1412
+ type: 'sup',
1413
+ base: e ? parseOmmlChildren(e) : [],
1414
+ argument: sup ? parseOmmlChildren(sup) : [],
1415
+ };
1416
+ }
1417
+ case 'sSub': {
1418
+ const e = child(node, 'e');
1419
+ const sub = child(node, 'sub');
1420
+ return {
1421
+ type: 'sub',
1422
+ base: e ? parseOmmlChildren(e) : [],
1423
+ argument: sub ? parseOmmlChildren(sub) : [],
1424
+ };
1425
+ }
1426
+ case 'sSubSup': {
1427
+ const e = child(node, 'e');
1428
+ const sub = child(node, 'sub');
1429
+ const sup = child(node, 'sup');
1430
+ return {
1431
+ type: 'subSup',
1432
+ base: e ? parseOmmlChildren(e) : [],
1433
+ subscript: sub ? parseOmmlChildren(sub) : [],
1434
+ superscript: sup ? parseOmmlChildren(sup) : [],
1435
+ };
1436
+ }
1437
+ case 'nary': {
1438
+ const naryPr = child(node, 'naryPr');
1439
+ const chrNode = naryPr ? child(naryPr, 'chr') : undefined;
1440
+ const operator = chrNode?.attrs['m:val'] ?? chrNode?.attrs['val'] ?? '∑';
1441
+ const sub = child(node, 'sub');
1442
+ const sup = child(node, 'sup');
1443
+ const e = child(node, 'e');
1444
+ return {
1445
+ type: 'nary',
1446
+ operator,
1447
+ lower: sub ? parseOmmlChildren(sub) : [],
1448
+ upper: sup ? parseOmmlChildren(sup) : [],
1449
+ body: e ? parseOmmlChildren(e) : [],
1450
+ };
1451
+ }
1452
+ case 'rad': {
1453
+ const radPr = child(node, 'radPr');
1454
+ const degHide = radPr ? child(radPr, 'degHide') : undefined;
1455
+ const hideDegree = degHide?.attrs['m:val'] === '1' || degHide?.attrs['val'] === '1';
1456
+ const deg = child(node, 'deg');
1457
+ const e = child(node, 'e');
1458
+ return {
1459
+ type: 'rad',
1460
+ hideDegree,
1461
+ degree: deg ? parseOmmlChildren(deg) : [],
1462
+ body: e ? parseOmmlChildren(e) : [],
1463
+ };
1464
+ }
1465
+ case 'd': {
1466
+ const dPr = child(node, 'dPr');
1467
+ const begChr = dPr ? child(dPr, 'begChr') : undefined;
1468
+ const endChr = dPr ? child(dPr, 'endChr') : undefined;
1469
+ const e = child(node, 'e');
1470
+ return {
1471
+ type: 'delim',
1472
+ open: begChr?.attrs['m:val'] ?? begChr?.attrs['val'] ?? '(',
1473
+ close: endChr?.attrs['m:val'] ?? endChr?.attrs['val'] ?? ')',
1474
+ body: e ? parseOmmlChildren(e) : [],
1475
+ };
1476
+ }
1477
+ case 'func': {
1478
+ const fName = child(node, 'fName');
1479
+ const e = child(node, 'e');
1480
+ return {
1481
+ type: 'func',
1482
+ base: fName ? parseOmmlChildren(fName) : [],
1483
+ argument: e ? parseOmmlChildren(e) : [],
1484
+ };
1485
+ }
1486
+ case 'groupChr': {
1487
+ const groupChrPr = child(node, 'groupChrPr');
1488
+ const chrNode = groupChrPr ? child(groupChrPr, 'chr') : undefined;
1489
+ const e = child(node, 'e');
1490
+ return {
1491
+ type: 'groupChar',
1492
+ operator: chrNode?.attrs['m:val'] ?? chrNode?.attrs['val'] ?? '⏟',
1493
+ base: e ? parseOmmlChildren(e) : [],
1494
+ };
1495
+ }
1496
+ case 'acc': {
1497
+ const accPr = child(node, 'accPr');
1498
+ const chrNode = accPr ? child(accPr, 'chr') : undefined;
1499
+ const e = child(node, 'e');
1500
+ return {
1501
+ type: 'accent',
1502
+ operator: chrNode?.attrs['m:val'] ?? chrNode?.attrs['val'] ?? '̂',
1503
+ base: e ? parseOmmlChildren(e) : [],
1504
+ };
1505
+ }
1506
+ case 'bar': {
1507
+ const e = child(node, 'e');
1508
+ return {
1509
+ type: 'bar',
1510
+ base: e ? parseOmmlChildren(e) : [],
1511
+ };
1512
+ }
1513
+ case 'limLow': {
1514
+ const e = child(node, 'e');
1515
+ const lim = child(node, 'lim');
1516
+ return {
1517
+ type: 'limLow',
1518
+ base: e ? parseOmmlChildren(e) : [],
1519
+ argument: lim ? parseOmmlChildren(lim) : [],
1520
+ };
1521
+ }
1522
+ case 'limUpp': {
1523
+ const e = child(node, 'e');
1524
+ const lim = child(node, 'lim');
1525
+ return {
1526
+ type: 'limUpp',
1527
+ base: e ? parseOmmlChildren(e) : [],
1528
+ argument: lim ? parseOmmlChildren(lim) : [],
1529
+ };
1530
+ }
1531
+ case 'm': {
1532
+ const rows = [];
1533
+ for (const mr of children(node, 'mr')) {
1534
+ const row = [];
1535
+ for (const e of children(mr, 'e')) {
1536
+ const cellElements = parseOmmlChildren(e);
1537
+ if (cellElements.length === 1)
1538
+ row.push(cellElements[0]);
1539
+ else
1540
+ row.push({ type: 'text', text: cellElements.map(el => el.text ?? '').join('') });
1541
+ }
1542
+ rows.push(row);
1543
+ }
1544
+ return { type: 'matrix', rows };
1545
+ }
1546
+ case 'eqArr': {
1547
+ const rows = [];
1548
+ for (const e of children(node, 'e')) {
1549
+ rows.push(parseOmmlChildren(e));
1550
+ }
1551
+ return { type: 'eqArr', rows };
1552
+ }
1553
+ default:
1554
+ return null;
1555
+ }
1556
+ }
1557
+ function parseCellImagesFromRichData(vmCells, getText, getEntry, ws) {
1558
+ const metaXml = getText('xl/metadata.xml');
1559
+ if (!metaXml)
1560
+ return;
1561
+ const metaRoot = parseXml(metaXml);
1562
+ const valueMeta = child(metaRoot, 'valueMetadata');
1563
+ if (!valueMeta)
1564
+ return;
1565
+ const vmBks = children(valueMeta, 'bk');
1566
+ const futureMeta = metaRoot.children.find((c) => typeof c !== 'string' && localName(c.tag) === 'futureMetadata');
1567
+ const fmBks = futureMeta ? children(futureMeta, 'bk') : [];
1568
+ const rvXml = getText('xl/richData/rdrichvalue.xml');
1569
+ if (!rvXml)
1570
+ return;
1571
+ const rvRoot = parseXml(rvXml);
1572
+ const rvEntries = children(rvRoot, 'rv');
1573
+ const rvRelRelsXml = getText('xl/richData/_rels/richValueRel.xml.rels');
1574
+ if (!rvRelRelsXml)
1575
+ return;
1576
+ const rvRelRels = parseRels(rvRelRelsXml);
1577
+ const rvRelXml = getText('xl/richData/richValueRel.xml');
1578
+ if (!rvRelXml)
1579
+ return;
1580
+ const rvRelRoot = parseXml(rvRelXml);
1581
+ const relEntries = children(rvRelRoot, 'rel');
1582
+ for (const [cellRef, vmIdx] of vmCells) {
1583
+ const vmBk = vmBks[vmIdx - 1];
1584
+ if (!vmBk)
1585
+ continue;
1586
+ const rc = vmBk.children.find((c) => typeof c !== 'string' && localName(c.tag) === 'rc');
1587
+ if (!rc)
1588
+ continue;
1589
+ const fmIdx = parseInt(rc.attrs['v'] ?? '-1', 10);
1590
+ if (fmIdx < 0)
1591
+ continue;
1592
+ const fmBk = fmBks[fmIdx];
1593
+ if (!fmBk)
1594
+ continue;
1595
+ const rvb = findDescendant(fmBk, 'rvb');
1596
+ const rvIdx = parseInt(rvb?.attrs['i'] ?? '-1', 10);
1597
+ if (rvIdx < 0)
1598
+ continue;
1599
+ const rv = rvEntries[rvIdx];
1600
+ if (!rv)
1601
+ continue;
1602
+ const vNodes = children(rv, 'v');
1603
+ const relIdx = parseInt(vNodes[0]?.text ?? '-1', 10);
1604
+ if (relIdx < 0)
1605
+ continue;
1606
+ const relEntry = relEntries[relIdx];
1607
+ if (!relEntry)
1608
+ continue;
1609
+ const rId = relEntry.attrs['r:id'] ?? '';
1610
+ const imgRel = rvRelRels.get(rId);
1611
+ if (!imgRel)
1612
+ continue;
1613
+ const imgPath = imgRel.target.startsWith('/')
1614
+ ? imgRel.target.slice(1)
1615
+ : `xl/richData/${imgRel.target}`;
1616
+ const imgEntry = getEntry(imgPath);
1617
+ if (!imgEntry)
1618
+ continue;
1619
+ const ext = imgPath.split('.').pop() ?? 'png';
1620
+ const format = extToFormat(ext);
1621
+ ws.addCellImage({ data: imgEntry.data, format, cell: cellRef });
1622
+ }
1623
+ }
1624
+ function findDescendant(node, tagName) {
1625
+ for (const c of node.children) {
1626
+ if (typeof c === 'string')
1627
+ continue;
1628
+ if (localName(c.tag) === tagName)
1629
+ return c;
1630
+ const found = findDescendant(c, tagName);
1631
+ if (found)
1632
+ return found;
1633
+ }
1634
+ return undefined;
1635
+ }
541
1636
  function resolvePath(base, relative) {
542
1637
  const parts = base.replace(/\/$/, '').split('/');
543
1638
  for (const seg of relative.split('/')) {
@@ -581,6 +1676,26 @@ export async function readWorkbook(data) {
581
1676
  const wbRoot = parseXml(wbXml);
582
1677
  const sheetsNode = find(wbRoot, 'sheets');
583
1678
  const sheetNodes = sheetsNode ? children(sheetsNode, 'sheet') : [];
1679
+ const namedRanges = [];
1680
+ const definedNamesNode = find(wbRoot, 'definedNames');
1681
+ if (definedNamesNode) {
1682
+ for (const dn of children(definedNamesNode, 'definedName')) {
1683
+ const name = dn.attrs['name'] ?? '';
1684
+ const ref = dn.text ?? '';
1685
+ if (!name || !ref)
1686
+ continue;
1687
+ const nr = { name, ref };
1688
+ if (dn.attrs['localSheetId'] !== undefined) {
1689
+ const idx = parseInt(dn.attrs['localSheetId'], 10);
1690
+ const scopeSheet = sheetNodes[idx];
1691
+ if (scopeSheet)
1692
+ nr.scope = scopeSheet.attrs['name'] ?? undefined;
1693
+ }
1694
+ if (dn.attrs['comment'])
1695
+ nr.comment = dn.attrs['comment'];
1696
+ namedRanges.push(nr);
1697
+ }
1698
+ }
584
1699
  const allRels = new Map();
585
1700
  for (const [path, entry] of zip) {
586
1701
  if (path.includes('_rels/')) {
@@ -599,10 +1714,11 @@ export async function readWorkbook(data) {
599
1714
  const sheetXml = getText(target) ?? '';
600
1715
  if (!sheetXml)
601
1716
  continue;
602
- const { ws, originalXml, unknownParts: sheetUnknown, tableRIds } = parseWorksheet(sheetXml, name, styles, sharedStrings);
1717
+ const { ws, originalXml, unknownParts: sheetUnknown, tableRIds, legacyDrawingRId, ctrlPropRIds, drawingRId, vmCells } = parseWorksheet(sheetXml, name, styles, sharedStrings);
603
1718
  ws.sheetIndex = sheets.length + 1;
604
1719
  ws.rId = rId;
605
1720
  const tablePaths = [];
1721
+ const tableXmls = [];
606
1722
  if (tableRIds.length) {
607
1723
  const sheetFileName = target.split('/').pop() ?? '';
608
1724
  const sheetDir = target.substring(0, target.lastIndexOf('/') + 1);
@@ -622,17 +1738,179 @@ export async function readWorkbook(data) {
622
1738
  if (table)
623
1739
  ws.addTable(table);
624
1740
  tablePaths.push(tblTarget);
1741
+ tableXmls.push(tblXml);
625
1742
  }
626
1743
  }
627
1744
  ws.tableRIds = tableRIds;
628
1745
  }
629
1746
  }
630
- sheets.push({ ws, sheetId, rId, originalXml, unknownParts: sheetUnknown, tablePaths });
1747
+ if (legacyDrawingRId && ctrlPropRIds.length) {
1748
+ const sheetFileName = target.split('/').pop() ?? '';
1749
+ const sheetDir = target.substring(0, target.lastIndexOf('/') + 1);
1750
+ const sheetRelsPath = `${sheetDir}_rels/${sheetFileName}.rels`;
1751
+ const sheetRels = allRels.get(sheetRelsPath);
1752
+ if (sheetRels) {
1753
+ const vmlRel = sheetRels.get(legacyDrawingRId);
1754
+ const vmlPath = vmlRel
1755
+ ? (vmlRel.target.startsWith('/') ? vmlRel.target.slice(1) : resolvePath(sheetDir, vmlRel.target))
1756
+ : '';
1757
+ const vmlXml = vmlPath ? getText(vmlPath) : '';
1758
+ const vmlControls = [];
1759
+ if (vmlXml) {
1760
+ const vmlRoot = parseXml(vmlXml);
1761
+ for (const shape of vmlRoot.children) {
1762
+ if (typeof shape === 'string')
1763
+ continue;
1764
+ if (localName(shape.tag) !== 'shape')
1765
+ continue;
1766
+ const cd = shape.children.find((c) => typeof c !== 'string' && localName(c.tag) === 'ClientData');
1767
+ if (!cd)
1768
+ continue;
1769
+ const objType = cd.attrs['ObjectType'] ?? '';
1770
+ if (objType === 'Note' || !objType)
1771
+ continue;
1772
+ const idStr = (shape.attrs['id'] ?? '').replace(/\D/g, '');
1773
+ const shapeId = parseInt(idStr, 10) || 0;
1774
+ vmlControls.push({ objectType: objType, shapeXml: nodeToXml(shape), clientData: cd, shapeId });
1775
+ }
1776
+ }
1777
+ for (let ci = 0; ci < ctrlPropRIds.length; ci++) {
1778
+ const cpRel = sheetRels.get(ctrlPropRIds[ci]);
1779
+ if (!cpRel)
1780
+ continue;
1781
+ const cpPath = cpRel.target.startsWith('/') ? cpRel.target.slice(1) : resolvePath(sheetDir, cpRel.target);
1782
+ const cpXml = getText(cpPath) ?? '';
1783
+ const cpRoot = cpXml ? parseXml(cpXml) : null;
1784
+ const objType = cpRoot?.attrs['objectType'] ?? '';
1785
+ const typeName = (OBJ_TYPE_TO_CTRL[objType] ?? 'button');
1786
+ const vml = vmlControls[ci];
1787
+ const anchor = parseVmlAnchor(vml?.clientData);
1788
+ const ctrl = {
1789
+ type: typeName,
1790
+ from: anchor.from,
1791
+ to: anchor.to,
1792
+ _ctrlPropXml: cpXml || undefined,
1793
+ _vmlShapeXml: vml?.shapeXml,
1794
+ _shapeId: vml?.shapeId,
1795
+ };
1796
+ if (cpRoot) {
1797
+ if (cpRoot.attrs['fmlaLink'])
1798
+ ctrl.linkedCell = cpRoot.attrs['fmlaLink'];
1799
+ if (cpRoot.attrs['fmlaRange'])
1800
+ ctrl.inputRange = cpRoot.attrs['fmlaRange'];
1801
+ if (cpRoot.attrs['checked'])
1802
+ ctrl.checked = (CHECKED_REV[cpRoot.attrs['checked']] ?? 'unchecked');
1803
+ if (cpRoot.attrs['dropLines'])
1804
+ ctrl.dropLines = parseInt(cpRoot.attrs['dropLines'], 10);
1805
+ if (cpRoot.attrs['min'])
1806
+ ctrl.min = parseInt(cpRoot.attrs['min'], 10);
1807
+ if (cpRoot.attrs['max'])
1808
+ ctrl.max = parseInt(cpRoot.attrs['max'], 10);
1809
+ if (cpRoot.attrs['inc'])
1810
+ ctrl.inc = parseInt(cpRoot.attrs['inc'], 10);
1811
+ if (cpRoot.attrs['page'])
1812
+ ctrl.page = parseInt(cpRoot.attrs['page'], 10);
1813
+ if (cpRoot.attrs['val'])
1814
+ ctrl.val = parseInt(cpRoot.attrs['val'], 10);
1815
+ if (cpRoot.attrs['selType']) {
1816
+ const selRev = { Single: 'single', Multi: 'multi', Extend: 'extend' };
1817
+ ctrl.selType = (selRev[cpRoot.attrs['selType']] ?? 'single');
1818
+ }
1819
+ if (cpRoot.attrs['noThreeD'] === '1')
1820
+ ctrl.noThreeD = true;
1821
+ }
1822
+ if (vml?.clientData) {
1823
+ const macroNode = vml.clientData.children.find((c) => typeof c !== 'string' && localName(c.tag) === 'FmlaMacro');
1824
+ if (macroNode)
1825
+ ctrl.macro = macroNode.text ?? '';
1826
+ }
1827
+ ws.addFormControl(ctrl);
1828
+ }
1829
+ ws.legacyDrawingRId = legacyDrawingRId;
1830
+ ws.ctrlPropRIds = ctrlPropRIds;
1831
+ }
1832
+ }
1833
+ if (drawingRId) {
1834
+ const sheetFileName = target.split('/').pop() ?? '';
1835
+ const sheetDir = target.substring(0, target.lastIndexOf('/') + 1);
1836
+ const sheetRelsPath = `${sheetDir}_rels/${sheetFileName}.rels`;
1837
+ const sheetRels = allRels.get(sheetRelsPath);
1838
+ if (sheetRels) {
1839
+ const drawRel = sheetRels.get(drawingRId);
1840
+ if (drawRel) {
1841
+ const drawPath = drawRel.target.startsWith('/')
1842
+ ? drawRel.target.slice(1)
1843
+ : resolvePath(sheetDir, drawRel.target);
1844
+ const drawXml = getText(drawPath);
1845
+ if (drawXml) {
1846
+ const drawDir = drawPath.substring(0, drawPath.lastIndexOf('/') + 1);
1847
+ const drawFileName = drawPath.split('/').pop() ?? '';
1848
+ const drawRelsPath = `${drawDir}_rels/${drawFileName}.rels`;
1849
+ const drawRels = allRels.get(drawRelsPath);
1850
+ parseDrawingObjects(drawXml, drawRels, drawDir, get, ws);
1851
+ }
1852
+ }
1853
+ }
1854
+ }
1855
+ if (vmCells.size > 0) {
1856
+ parseCellImagesFromRichData(vmCells, getText, get, ws);
1857
+ }
1858
+ sheets.push({ ws, sheetId, rId, originalXml, unknownParts: sheetUnknown, tablePaths, tableXmls });
1859
+ }
1860
+ const connectionsXml = getText('xl/connections.xml') ?? '';
1861
+ const connections = [];
1862
+ if (connectionsXml) {
1863
+ const connRoot = parseXml(connectionsXml);
1864
+ for (const cn of children(connRoot, 'connection')) {
1865
+ const id = parseInt(cn.attrs['id'] ?? '0', 10);
1866
+ const name = cn.attrs['name'] ?? '';
1867
+ const typeNum = parseInt(cn.attrs['type'] ?? '0', 10);
1868
+ const type = connTypeFromNum(typeNum);
1869
+ if (!name || !type)
1870
+ continue;
1871
+ const conn = { id, name, type };
1872
+ if (cn.attrs['description'])
1873
+ conn.description = cn.attrs['description'];
1874
+ if (cn.attrs['refreshOnLoad'] === '1')
1875
+ conn.refreshOnLoad = true;
1876
+ if (cn.attrs['background'] === '1')
1877
+ conn.background = true;
1878
+ if (cn.attrs['saveData'] === '1')
1879
+ conn.saveData = true;
1880
+ if (cn.attrs['keepAlive'] === '1')
1881
+ conn.keepAlive = true;
1882
+ if (cn.attrs['interval'])
1883
+ conn.interval = parseInt(cn.attrs['interval'], 10);
1884
+ const dbPr = child(cn, 'dbPr');
1885
+ if (dbPr) {
1886
+ if (dbPr.attrs['connection'])
1887
+ conn.connectionString = dbPr.attrs['connection'];
1888
+ if (dbPr.attrs['command'])
1889
+ conn.command = dbPr.attrs['command'];
1890
+ if (dbPr.attrs['commandType'])
1891
+ conn.commandType = cmdTypeFromNum(parseInt(dbPr.attrs['commandType'], 10));
1892
+ }
1893
+ conn._rawXml = nodeToXml(cn);
1894
+ connections.push(conn);
1895
+ }
1896
+ }
1897
+ const powerQueries = [];
1898
+ for (const [path, entry] of zip) {
1899
+ if (!path.startsWith('customXml/item') || path.includes('Props') || path.includes('_rels'))
1900
+ continue;
1901
+ try {
1902
+ const pqs = await parseDataMashup(entry.data);
1903
+ if (pqs.length) {
1904
+ powerQueries.push(...pqs);
1905
+ break;
1906
+ }
1907
+ }
1908
+ catch { }
631
1909
  }
632
1910
  const handledPrefixes = new Set([
633
1911
  'xl/workbook.xml', 'xl/styles.xml', 'xl/sharedStrings.xml',
634
1912
  'xl/worksheets/', 'docProps/', '[Content_Types].xml',
635
- '_rels/', 'xl/_rels/',
1913
+ '_rels/', 'xl/_rels/', 'xl/connections.xml',
636
1914
  ]);
637
1915
  const unknownParts = new Map();
638
1916
  for (const [path, entry] of zip) {
@@ -643,12 +1921,100 @@ export async function readWorkbook(data) {
643
1921
  unknownParts.set(path, entry.data);
644
1922
  }
645
1923
  }
1924
+ const nonPrintAreaRanges = [];
1925
+ for (const nr of namedRanges) {
1926
+ if (nr.name === '_xlnm.Print_Area' && nr.scope) {
1927
+ const ws = sheets.find(s => s.ws.name === nr.scope);
1928
+ if (ws)
1929
+ ws.ws.printArea = nr.ref;
1930
+ }
1931
+ else {
1932
+ nonPrintAreaRanges.push(nr);
1933
+ }
1934
+ }
646
1935
  return {
647
1936
  sheets, styles, stylesXml, sharedStrings, sharedXml: ssXml,
648
1937
  workbookXml: wbXml, workbookRels,
649
1938
  contentTypes, contentTypesXml: ctXml,
650
1939
  core, extended, extendedUnknownRaw, custom, hasCustomProps: custom.length > 0,
1940
+ namedRanges: nonPrintAreaRanges, connections, connectionsXml, powerQueries,
651
1941
  unknownParts, allRels,
652
1942
  };
653
1943
  }
1944
+ function parseVmlAnchor(clientData) {
1945
+ const defaultAnchor = { from: { col: 0, row: 0 }, to: { col: 2, row: 2 } };
1946
+ if (!clientData)
1947
+ return defaultAnchor;
1948
+ const anchorNode = clientData.children.find((c) => typeof c !== 'string' && localName(c.tag) === 'Anchor');
1949
+ if (!anchorNode)
1950
+ return defaultAnchor;
1951
+ const text = anchorNode.text ?? '';
1952
+ const parts = text.split(',').map(s => parseInt(s.trim(), 10));
1953
+ if (parts.length < 8 || parts.some(isNaN))
1954
+ return defaultAnchor;
1955
+ return {
1956
+ from: { col: parts[0], colOff: parts[1], row: parts[2], rowOff: parts[3] },
1957
+ to: { col: parts[4], colOff: parts[5], row: parts[6], rowOff: parts[7] },
1958
+ };
1959
+ }
1960
+ const CONN_TYPE_MAP = {
1961
+ 1: 'odbc', 2: 'dao', 3: 'file', 4: 'web', 5: 'oledb', 6: 'text', 7: 'dsp',
1962
+ };
1963
+ const CONN_TYPE_REV = Object.fromEntries(Object.entries(CONN_TYPE_MAP).map(([k, v]) => [v, Number(k)]));
1964
+ function connTypeFromNum(n) { return CONN_TYPE_MAP[n]; }
1965
+ export function connTypeToNum(t) { return CONN_TYPE_REV[t]; }
1966
+ const CMD_TYPE_MAP = {
1967
+ 1: 'sql', 2: 'table', 3: 'default', 4: 'web', 5: 'oledb',
1968
+ };
1969
+ const CMD_TYPE_REV = Object.fromEntries(Object.entries(CMD_TYPE_MAP).map(([k, v]) => [v, Number(k)]));
1970
+ function cmdTypeFromNum(n) { return CMD_TYPE_MAP[n]; }
1971
+ export function cmdTypeToNum(t) { return CMD_TYPE_REV[t]; }
1972
+ async function parseDataMashup(data) {
1973
+ if (data.length < 12)
1974
+ return [];
1975
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
1976
+ const version = view.getUint32(0, true);
1977
+ if (version !== 0)
1978
+ return [];
1979
+ const pkgLen = view.getUint32(4, true);
1980
+ if (pkgLen < 4 || 8 + pkgLen > data.length)
1981
+ return [];
1982
+ const pkgBytes = data.subarray(8, 8 + pkgLen);
1983
+ if (pkgBytes[0] !== 0x50 || pkgBytes[1] !== 0x4B)
1984
+ return [];
1985
+ const innerZip = await readZip(pkgBytes);
1986
+ const queries = [];
1987
+ for (const [path, entry] of innerZip) {
1988
+ if (!path.includes('/Formula/') || !path.endsWith('.m'))
1989
+ continue;
1990
+ const formula = entryText(entry);
1991
+ if (!formula)
1992
+ continue;
1993
+ const pathMatch = path.match(/Formulas\/([^/]+)\//);
1994
+ const nameFromPath = pathMatch ? pathMatch[1] : undefined;
1995
+ const sharedRe = /shared\s+(?:#"([^"]+)"|(\w+))\s*=/g;
1996
+ let m;
1997
+ const foundNames = new Set();
1998
+ while ((m = sharedRe.exec(formula)) !== null) {
1999
+ const qName = m[1] ?? m[2];
2000
+ foundNames.add(qName);
2001
+ }
2002
+ if (foundNames.size > 0) {
2003
+ const sectionRe = /shared\s+(?:#"([^"]+)"|(\w+))\s*=\s*([\s\S]*?)(?=,\s*shared\s|\]\s*$)/g;
2004
+ let sm;
2005
+ while ((sm = sectionRe.exec(formula)) !== null) {
2006
+ const qName = sm[1] ?? sm[2];
2007
+ const qFormula = sm[3].replace(/,\s*$/, '').trim();
2008
+ queries.push({ name: qName, formula: qFormula });
2009
+ }
2010
+ if (queries.length === 0) {
2011
+ queries.push({ name: nameFromPath ?? 'Section1', formula });
2012
+ }
2013
+ }
2014
+ else if (nameFromPath) {
2015
+ queries.push({ name: nameFromPath, formula });
2016
+ }
2017
+ }
2018
+ return queries;
2019
+ }
654
2020
  //# sourceMappingURL=WorkbookReader.js.map