reamkit 1.12.0 → 1.14.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 (35) hide show
  1. package/dist/esm/core/document-model/types.d.ts +3 -0
  2. package/dist/esm/core/ir/sheet.d.ts +49 -3
  3. package/dist/esm/core/spreadsheet-model/index.d.ts +1 -1
  4. package/dist/esm/core/spreadsheet-model/types.d.ts +80 -1
  5. package/dist/esm/excel/comments-parser.d.ts +4 -0
  6. package/dist/esm/excel/comments-parser.js +104 -0
  7. package/dist/esm/excel/conditional-format.d.ts +2 -2
  8. package/dist/esm/excel/conditional-format.js +113 -11
  9. package/dist/esm/excel/form-control-parser.d.ts +6 -0
  10. package/dist/esm/excel/form-control-parser.js +29 -0
  11. package/dist/esm/excel/header-footer.d.ts +2 -0
  12. package/dist/esm/excel/header-footer.js +127 -0
  13. package/dist/esm/excel/index.d.ts +1 -1
  14. package/dist/esm/excel/print-model.d.ts +7 -2
  15. package/dist/esm/excel/print-model.js +218 -19
  16. package/dist/esm/excel/shared-strings-parser.d.ts +6 -1
  17. package/dist/esm/excel/shared-strings-parser.js +91 -12
  18. package/dist/esm/excel/sheet-drawing.d.ts +13 -1
  19. package/dist/esm/excel/sheet-drawing.js +47 -14
  20. package/dist/esm/excel/sheet-shape-parser.d.ts +4 -0
  21. package/dist/esm/excel/sheet-shape-parser.js +96 -0
  22. package/dist/esm/excel/sheet-to-flow.js +130 -3
  23. package/dist/esm/excel/slicer-parser.d.ts +15 -0
  24. package/dist/esm/excel/slicer-parser.js +76 -0
  25. package/dist/esm/excel/styles-parser.js +37 -0
  26. package/dist/esm/excel/table-parser.d.ts +10 -0
  27. package/dist/esm/excel/table-parser.js +36 -4
  28. package/dist/esm/excel/worksheet-parser.js +181 -1
  29. package/dist/esm/excel/xlsx-reader.js +203 -17
  30. package/dist/esm/excel/xlsx-writer.js +19 -3
  31. package/dist/esm/html/html-writer.js +4 -0
  32. package/dist/esm/layout/styled-layout.js +72 -1
  33. package/dist/esm/pptx/slide-parser.d.ts +4 -2
  34. package/dist/esm/pptx/slide-parser.js +1 -1
  35. package/package.json +1 -1
@@ -1,5 +1,6 @@
1
- import { BodyElement, SectionProperties } from '../core/document-model/index.js';
2
- import { CellRange, DefinedName, ParsedWorksheet, XlsxStyles } from './index.js';
1
+ import { BodyElement, SectionProperties, Table } from '../core/document-model/index.js';
2
+ import { CellRange, DefinedName, ParsedWorksheet, SheetRichRun, WorksheetCell, XlsxStyles } from './index.js';
3
+ import { SheetHyperlink, SheetSlicer } from '../core/ir/sheet.js';
3
4
  export declare const TWIPS_PER_EXCEL_CHAR = 105;
4
5
  export declare const DEFAULT_COL_TWIPS = 960;
5
6
  export declare const DEFAULT_ROW_TWIPS = 300;
@@ -17,6 +18,10 @@ interface PrintModelOptions {
17
18
  readonly endRow: number;
18
19
  };
19
20
  readonly sheetGrids?: ReadonlyMap<string, ParsedWorksheet>;
21
+ readonly hyperlinks?: ReadonlyArray<SheetHyperlink>;
22
+ readonly sharedStringRuns?: ReadonlyArray<ReadonlyArray<SheetRichRun> | undefined>;
20
23
  }
21
24
  export declare function worksheetToBody(worksheet: ParsedWorksheet, sharedStrings: ReadonlyArray<string>, styles: XlsxStyles, date1904: boolean, print: PrintModelOptions): Array<BodyElement>;
25
+ export declare function resolveCellText(cell: WorksheetCell, sharedStrings: ReadonlyArray<string>, styles: XlsxStyles, date1904: boolean): string;
26
+ export declare function slicerTable(slicer: SheetSlicer): Table;
22
27
  export {};
@@ -1,4 +1,4 @@
1
- import { eighthPtToPt, halfPtToPt, twipsToPt } from "../core/ir/units.js";
1
+ import { eighthPtToPt, halfPtToPt, pt, twipsToPt } from "../core/ir/units.js";
2
2
  import { parseAreaRef, parseTitleRowRange } from "./defined-name-ref.js";
3
3
  import { applyNumberFormat } from "./number-format.js";
4
4
  import "./index.js";
@@ -192,9 +192,10 @@ function worksheetToBody(worksheet, sharedStrings, styles, date1904, print) {
192
192
  }
193
193
  return props;
194
194
  };
195
- const cfFormatter = buildConditionalFormatter(worksheet.conditionalFormats, styles, worksheet.cells);
195
+ const cfFormatter = buildConditionalFormatter(worksheet.conditionalFormats, styles, worksheet.cells, (cell) => resolveCellText(cell, sharedStrings, styles, date1904));
196
196
  const sparklineByCell = buildSparklineLookup(worksheet, print);
197
197
  const tableFormatByCell = buildTableFormatLookup(worksheet);
198
+ const dropdownRanges = listDropdownRanges(worksheet.dataValidations);
198
199
  const rows = [];
199
200
  for (let r = 0; r < rowCount; r++) {
200
201
  const absR = r + rowStart;
@@ -209,6 +210,7 @@ function worksheetToBody(worksheet, sharedStrings, styles, date1904, print) {
209
210
  }
210
211
  const ws = cellMatrix[r]?.[c];
211
212
  let text = ws ? resolveCellText(ws, sharedStrings, styles, date1904) : "";
213
+ const cfText = text.length > 0 ? text : void 0;
212
214
  if (text.length > textBudget) text = text.slice(0, Math.max(0, textBudget));
213
215
  textBudget -= text.length;
214
216
  const xf = ws && ws.styleIndex !== void 0 ? styles.cellXfs[ws.styleIndex] : void 0;
@@ -226,7 +228,7 @@ function worksheetToBody(worksheet, sharedStrings, styles, date1904, print) {
226
228
  let icon;
227
229
  const sparkline = sparklineByCell.get(key(absR, absC));
228
230
  if (cfFormatter && ws) {
229
- const over = cfFormatter(absR, absC, ws.type === "n" && Number.isFinite(Number(ws.rawValue)) ? Number(ws.rawValue) : void 0);
231
+ const over = cfFormatter(absR, absC, ws.type === "n" && Number.isFinite(Number(ws.rawValue)) ? Number(ws.rawValue) : void 0, cfText);
230
232
  if (over) {
231
233
  if (over.fillHex) shading = { colorHex: over.fillHex };
232
234
  if (over.dataBar) dataBar = over.dataBar;
@@ -234,7 +236,11 @@ function worksheetToBody(worksheet, sharedStrings, styles, date1904, print) {
234
236
  runProps = applyCfOverride(runProps, over);
235
237
  }
236
238
  }
237
- if (text.length > 0 && !merge && ws && (ws.type === "s" || ws.type === "str" || ws.type === "inlineStr") && (alignment === void 0 || alignment === "left")) {
239
+ const wrapText = xf?.alignment?.wrapText === true;
240
+ const rotation = xf?.alignment?.textRotation;
241
+ const rotated = rotation !== void 0 && rotation !== 0 && text.length > 0;
242
+ const shrinkToFit = xf?.alignment?.shrinkToFit === true && !wrapText && !rotated;
243
+ if (text.length > 0 && !merge && !wrapText && !rotated && !shrinkToFit && ws && (ws.type === "s" || ws.type === "str" || ws.type === "inlineStr") && (alignment === void 0 || alignment === "left")) {
238
244
  let availTwips = columnWidths[c];
239
245
  let cc = c + 1;
240
246
  while (cc < colCount && !cellHasContent(cellMatrix[r]?.[cc])) {
@@ -246,6 +252,19 @@ function worksheetToBody(worksheet, sharedStrings, styles, date1904, print) {
246
252
  if (text.length > charsFit) text = text.slice(0, charsFit);
247
253
  }
248
254
  }
255
+ if (shrinkToFit && ws && text.length > 0) {
256
+ const charsFit = Math.max(1, columnWidths[c] / 105);
257
+ if (text.length > charsFit) {
258
+ const baseHalfPt = (styles.fonts[xf.fontId]?.sizePt ?? 11) * 2;
259
+ const scaledHalfPt = Math.max(2, Math.round(baseHalfPt * charsFit / text.length));
260
+ runProps = {
261
+ ...runProps,
262
+ fontSizePt: halfPtToPt(scaledHalfPt)
263
+ };
264
+ }
265
+ }
266
+ const dropdown = dropdownRanges.length > 0 && rangesCover(dropdownRanges, absR, absC);
267
+ const href = print.hyperlinks && print.hyperlinks.length > 0 ? hyperlinkUrlAt(print.hyperlinks, absR, absC) : void 0;
249
268
  const visibleEndCol = merge ? Math.min(merge.endColumn, colWindowEnd) : 0;
250
269
  const properties = {
251
270
  ...merge && visibleEndCol > merge.startColumn ? { colSpan: visibleEndCol - merge.startColumn + 1 } : {},
@@ -254,21 +273,35 @@ function worksheetToBody(worksheet, sharedStrings, styles, date1904, print) {
254
273
  ...dataBar ? { dataBar } : {},
255
274
  ...icon ? { icon } : {},
256
275
  ...sparkline ? { sparkline } : {},
257
- ...borders ? { borders } : {}
276
+ ...borders ? { borders } : {},
277
+ ...dropdown ? { dropdown: true } : {}
258
278
  };
259
- const paragraphProps = cellParaProps(alignment);
279
+ const indentLevels = xf?.alignment?.indent ?? 0;
280
+ const baseParaProps = cellParaProps(alignment);
281
+ const paragraphProps = indentLevels > 0 ? {
282
+ ...baseParaProps,
283
+ indentLeft: twipsToPt(indentLevels * 3 * 105)
284
+ } : baseParaProps;
285
+ const richRuns = ws && ws.type === "s" && print.sharedStringRuns ? print.sharedStringRuns[Number(ws.rawValue)] : void 0;
286
+ const cellRuns = richRuns && richRuns.length > 0 ? richRuns.map((rr) => ({
287
+ text: rr.text,
288
+ properties: richRunProps(runProps, rr),
289
+ ...href ? { href } : {}
290
+ })) : text.length > 0 ? [{
291
+ text,
292
+ properties: runProps,
293
+ ...href ? { href } : {}
294
+ }] : [];
295
+ const content = rotated ? stackedVerticalContent(text, runProps, href) : [{
296
+ kind: "paragraph",
297
+ paragraph: {
298
+ properties: paragraphProps,
299
+ runs: cellRuns
300
+ }
301
+ }];
260
302
  cells.push({
261
303
  properties,
262
- content: [{
263
- kind: "paragraph",
264
- paragraph: {
265
- properties: paragraphProps,
266
- runs: text.length > 0 ? [{
267
- text,
268
- properties: runProps
269
- }] : []
270
- }
271
- }]
304
+ content
272
305
  });
273
306
  }
274
307
  const baseRowProps = rowHeightMap.get(r);
@@ -374,6 +407,31 @@ function runPropsFromXf(xf, styles) {
374
407
  if (font.colorHex) props.colorHex = font.colorHex;
375
408
  return props;
376
409
  }
410
+ function stackedVerticalContent(text, runProps, href) {
411
+ const out = [];
412
+ for (const ch of text) out.push({
413
+ kind: "paragraph",
414
+ paragraph: {
415
+ properties: { alignment: "center" },
416
+ runs: [{
417
+ text: ch,
418
+ properties: runProps,
419
+ ...href ? { href } : {}
420
+ }]
421
+ }
422
+ });
423
+ return out;
424
+ }
425
+ function richRunProps(base, rr) {
426
+ const out = { ...base };
427
+ if (rr.bold) out.bold = true;
428
+ if (rr.italic) out.italic = true;
429
+ if (rr.underline) out.underline = "single";
430
+ if (rr.colorHex) out.colorHex = rr.colorHex;
431
+ if (rr.sizePt !== void 0) out.fontSizePt = halfPtToPt(Math.round(rr.sizePt * 2));
432
+ if (rr.vertAlign) out.verticalAlign = rr.vertAlign;
433
+ return out;
434
+ }
377
435
  function applyCfOverride(base, o) {
378
436
  return {
379
437
  ...base,
@@ -395,14 +453,55 @@ function mapAlignment(h) {
395
453
  if (h === "justify") return "both";
396
454
  if (h === "distributed") return "distribute";
397
455
  }
456
+ var PATTERN_DENSITY = {
457
+ darkGray: .75,
458
+ mediumGray: .5,
459
+ lightGray: .25,
460
+ gray125: .125,
461
+ gray0625: .0625,
462
+ darkHorizontal: .5,
463
+ darkVertical: .5,
464
+ darkDown: .5,
465
+ darkUp: .5,
466
+ darkGrid: .62,
467
+ darkTrellis: .75,
468
+ lightHorizontal: .25,
469
+ lightVertical: .25,
470
+ lightDown: .25,
471
+ lightUp: .25,
472
+ lightGrid: .37,
473
+ lightTrellis: .5
474
+ };
398
475
  function shadingFromXf(xf, styles) {
399
476
  if (xf.applyFill === false) return void 0;
400
477
  if (xf.fillId === 0 || xf.fillId === 1) {
401
478
  if (!xf.applyFill) return void 0;
402
479
  }
403
480
  const fill = styles.fills[xf.fillId];
404
- if (!fill || fill.patternType !== "solid" || !fill.fgColorHex) return void 0;
405
- return { colorHex: fill.fgColorHex };
481
+ if (!fill) return void 0;
482
+ const pattern = fill.patternType;
483
+ if (!pattern || pattern === "none") return void 0;
484
+ if (pattern === "solid") return fill.fgColorHex ? { colorHex: fill.fgColorHex } : void 0;
485
+ const density = PATTERN_DENSITY[pattern];
486
+ if (density === void 0 || !fill.fgColorHex) return void 0;
487
+ const blended = blendHex(fill.fgColorHex, fill.bgColorHex ?? "FFFFFF", density);
488
+ return blended ? { colorHex: blended } : void 0;
489
+ }
490
+ function blendHex(fg, bg, fraction) {
491
+ const a = hexToRgb(fg);
492
+ const b = hexToRgb(bg);
493
+ if (!a || !b) return void 0;
494
+ const mix = (x, y) => Math.max(0, Math.min(255, Math.round(x * fraction + y * (1 - fraction)))).toString(16).padStart(2, "0").toUpperCase();
495
+ return `${mix(a[0], b[0])}${mix(a[1], b[1])}${mix(a[2], b[2])}`;
496
+ }
497
+ function hexToRgb(hex) {
498
+ const h = hex.length === 8 ? hex.slice(2) : hex;
499
+ if (!/^[0-9A-Fa-f]{6}$/.test(h)) return void 0;
500
+ return [
501
+ parseInt(h.slice(0, 2), 16),
502
+ parseInt(h.slice(2, 4), 16),
503
+ parseInt(h.slice(4, 6), 16)
504
+ ];
406
505
  }
407
506
  function bordersFromXf(xf, styles) {
408
507
  if (xf.applyBorder === false) return void 0;
@@ -418,6 +517,11 @@ function bordersFromXf(xf, styles) {
418
517
  if (right) out.right = right;
419
518
  if (bottom) out.bottom = bottom;
420
519
  if (left) out.left = left;
520
+ const diagonal = border.diagonal ? mapBorderEdge(border.diagonal) : void 0;
521
+ if (diagonal) {
522
+ if (border.diagonalDown) out.diagonalDown = diagonal;
523
+ if (border.diagonalUp) out.diagonalUp = diagonal;
524
+ }
421
525
  return Object.keys(out).length > 0 ? out : void 0;
422
526
  }
423
527
  function mapBorderEdge(edge) {
@@ -474,6 +578,25 @@ function mapBorderStyle(style) {
474
578
  function key(row, col) {
475
579
  return `${row},${col}`;
476
580
  }
581
+ function listDropdownRanges(dvs) {
582
+ if (!dvs || dvs.length === 0) return [];
583
+ const out = [];
584
+ for (const dv of dvs) {
585
+ if (dv.type !== "list" || dv.showDropDown) continue;
586
+ out.push(...dv.ranges);
587
+ }
588
+ return out;
589
+ }
590
+ function rangesCover(ranges, row, col) {
591
+ for (const r of ranges) if (row >= r.startRow && row <= r.endRow && col >= r.startColumn && col <= r.endColumn) return true;
592
+ return false;
593
+ }
594
+ function hyperlinkUrlAt(links, row, col) {
595
+ for (const l of links) {
596
+ const r = l.ref;
597
+ if (row >= r.startRow && row <= r.endRow && col >= r.startColumn && col <= r.endColumn) return l.url;
598
+ }
599
+ }
477
600
  function buildSparklineLookup(worksheet, print) {
478
601
  const out = /* @__PURE__ */ new Map();
479
602
  for (const sp of worksheet.sparklines ?? []) {
@@ -572,5 +695,81 @@ function buildTableFormatLookup(worksheet) {
572
695
  function cellHasContent(cell) {
573
696
  return !!cell && (cell.rawValue !== "" || cell.inlineText !== void 0);
574
697
  }
698
+ var SLICER_WIDTH_PT = 108;
699
+ var SLICER_ROW_PT = 16;
700
+ var SLICER_UNSELECTED_HEX = "F2F2F2";
701
+ function slicerTable(slicer) {
702
+ const cols = Math.max(1, slicer.columnCount);
703
+ const colWidthPt = SLICER_WIDTH_PT / cols;
704
+ const thin = {
705
+ style: "single",
706
+ width: eighthPtToPt(4)
707
+ };
708
+ const rowProps = {
709
+ height: pt(SLICER_ROW_PT),
710
+ heightRule: "atLeast"
711
+ };
712
+ const rows = [];
713
+ rows.push({
714
+ properties: rowProps,
715
+ cells: [{
716
+ properties: {
717
+ ...cols > 1 ? { colSpan: cols } : {},
718
+ ...slicer.headerHex ? { shading: { colorHex: slicer.headerHex } } : {}
719
+ },
720
+ content: [slicerParagraph(slicer.caption, {
721
+ bold: true,
722
+ ...slicer.headerTextHex ? { colorHex: slicer.headerTextHex } : {}
723
+ })]
724
+ }]
725
+ });
726
+ for (let i = 0; i < slicer.items.length; i += cols) {
727
+ const cells = [];
728
+ for (let c = 0; c < cols; c++) {
729
+ const item = slicer.items[i + c];
730
+ if (!item) {
731
+ cells.push({
732
+ properties: {},
733
+ content: [slicerParagraph("", {})]
734
+ });
735
+ continue;
736
+ }
737
+ const fill = item.selected ? slicer.selectedHex : SLICER_UNSELECTED_HEX;
738
+ const textHex = item.selected ? slicer.selectedTextHex : void 0;
739
+ cells.push({
740
+ properties: fill ? { shading: { colorHex: fill } } : {},
741
+ content: [slicerParagraph(item.label, textHex ? { colorHex: textHex } : {})]
742
+ });
743
+ }
744
+ rows.push({
745
+ properties: rowProps,
746
+ cells
747
+ });
748
+ }
749
+ return {
750
+ properties: { borders: {
751
+ top: thin,
752
+ bottom: thin,
753
+ left: thin,
754
+ right: thin,
755
+ insideH: thin,
756
+ insideV: thin
757
+ } },
758
+ grid: Array.from({ length: cols }, () => pt(colWidthPt)),
759
+ rows
760
+ };
761
+ }
762
+ function slicerParagraph(text, runProps) {
763
+ return {
764
+ kind: "paragraph",
765
+ paragraph: {
766
+ properties: {},
767
+ runs: text.length > 0 ? [{
768
+ text,
769
+ properties: runProps
770
+ }] : []
771
+ }
772
+ };
773
+ }
575
774
  //#endregion
576
- export { resolvePrintArea, resolvePrintTitleRows, sectionFromWorksheet, worksheetToBody };
775
+ export { resolveCellText, resolvePrintArea, resolvePrintTitleRows, sectionFromWorksheet, slicerTable, worksheetToBody };
@@ -1 +1,6 @@
1
- export declare function parseSharedStrings(data: Uint8Array): Array<string>;
1
+ import { SheetRichRun } from '../core/spreadsheet-model/index.js';
2
+ export interface SharedStrings {
3
+ readonly texts: ReadonlyArray<string>;
4
+ readonly runs: ReadonlyArray<ReadonlyArray<SheetRichRun> | undefined>;
5
+ }
6
+ export declare function parseSharedStrings(data: Uint8Array): SharedStrings;
@@ -17,22 +17,101 @@ function capLen(s) {
17
17
  function parseSharedStrings(data) {
18
18
  const xml = decoder.decode(data);
19
19
  const sst = parser.parse(xml)["sst"];
20
- if (!sst || typeof sst !== "object") return [];
20
+ if (!sst || typeof sst !== "object") return {
21
+ texts: [],
22
+ runs: []
23
+ };
21
24
  const siRaw = sst["si"];
22
- return (Array.isArray(siRaw) ? siRaw : siRaw !== void 0 ? [siRaw] : []).map((item) => capLen(extractSiText(item)));
25
+ const items = Array.isArray(siRaw) ? siRaw : siRaw !== void 0 ? [siRaw] : [];
26
+ const texts = [];
27
+ const runs = [];
28
+ for (const item of items) {
29
+ const parsed = extractSi(item);
30
+ texts.push(capLen(parsed.text));
31
+ runs.push(parsed.runs);
32
+ }
33
+ return {
34
+ texts,
35
+ runs
36
+ };
23
37
  }
24
- function extractSiText(si) {
25
- if (typeof si === "string") return si;
26
- if (typeof si === "number") return String(si);
27
- if (!si || typeof si !== "object") return "";
38
+ function extractSi(si) {
39
+ if (typeof si === "string") return {
40
+ text: si,
41
+ runs: void 0
42
+ };
43
+ if (typeof si === "number") return {
44
+ text: String(si),
45
+ runs: void 0
46
+ };
47
+ if (!si || typeof si !== "object") return {
48
+ text: "",
49
+ runs: void 0
50
+ };
28
51
  const obj = si;
29
- const t = obj["t"];
30
- const direct = textOf(t);
31
- if (direct) return direct;
52
+ const direct = textOf(obj["t"]);
53
+ if (direct) return {
54
+ text: direct,
55
+ runs: void 0
56
+ };
32
57
  const r = obj["r"];
33
- if (Array.isArray(r)) return r.map((rr) => textOf(rr?.["t"])).join("");
34
- if (r && typeof r === "object") return textOf(r["t"]);
35
- return "";
58
+ const rs = Array.isArray(r) ? r : r && typeof r === "object" ? [r] : [];
59
+ if (rs.length === 0) return {
60
+ text: "",
61
+ runs: void 0
62
+ };
63
+ const runs = [];
64
+ let anyFormatting = false;
65
+ for (const rr of rs) {
66
+ const ro = rr;
67
+ const text = textOf(ro?.["t"]);
68
+ if (text.length === 0) continue;
69
+ const run = richRun(text, ro?.["rPr"]);
70
+ if (Object.keys(run).length > 1) anyFormatting = true;
71
+ runs.push(run);
72
+ }
73
+ return {
74
+ text: runs.map((x) => x.text).join(""),
75
+ runs: anyFormatting && runs.length > 0 ? runs : void 0
76
+ };
77
+ }
78
+ function richRun(text, rPr) {
79
+ const p = rPr && typeof rPr === "object" ? rPr : void 0;
80
+ if (!p) return { text };
81
+ const out = { text };
82
+ if (has(p, "b")) out.bold = true;
83
+ if (has(p, "i")) out.italic = true;
84
+ if (has(p, "u")) out.underline = true;
85
+ const color = colorRgb(p["color"]);
86
+ if (color) out.colorHex = color;
87
+ const sz = attrNum(p["sz"], "val");
88
+ if (sz !== void 0) out.sizePt = sz;
89
+ const vert = attrStr(p["vertAlign"], "val");
90
+ if (vert === "superscript" || vert === "subscript") out.vertAlign = vert;
91
+ return out;
92
+ }
93
+ function has(p, key) {
94
+ if (!(key in p)) return false;
95
+ const node = p[key];
96
+ const val = attrStr(node, "val");
97
+ return val !== "0" && val !== "false";
98
+ }
99
+ function attrStr(node, key) {
100
+ if (!node || typeof node !== "object") return void 0;
101
+ const v = node[`@_${key}`];
102
+ return typeof v === "string" ? v : void 0;
103
+ }
104
+ function attrNum(node, key) {
105
+ const s = attrStr(node, key);
106
+ if (s === void 0) return void 0;
107
+ const n = Number(s);
108
+ return Number.isFinite(n) ? n : void 0;
109
+ }
110
+ function colorRgb(node) {
111
+ const rgb = attrStr(node, "rgb");
112
+ if (!rgb) return void 0;
113
+ const hex = rgb.length === 8 ? rgb.slice(2) : rgb;
114
+ return /^[0-9A-Fa-f]{6}$/.test(hex) ? hex.toUpperCase() : void 0;
36
115
  }
37
116
  function textOf(node) {
38
117
  if (typeof node === "string") return node;
@@ -6,4 +6,16 @@ export interface SheetChartRef {
6
6
  readonly heightPt: number;
7
7
  readonly anchorRow: number;
8
8
  }
9
- export declare function parseSheetDrawing(drawingXml: Uint8Array, drawingPartPath: string, pkg: OpcPackage, worksheet: ParsedWorksheet): Array<SheetChartRef>;
9
+ export interface SheetPicture {
10
+ readonly imagePartPath: string;
11
+ readonly widthPt: number;
12
+ readonly heightPt: number;
13
+ readonly anchorRow: number;
14
+ }
15
+ export interface SheetDrawing {
16
+ readonly charts: Array<SheetChartRef>;
17
+ readonly pictures: Array<SheetPicture>;
18
+ }
19
+ export declare function parseSheetDrawing(drawingXml: Uint8Array, drawingPartPath: string, pkg: OpcPackage, worksheet: ParsedWorksheet): SheetDrawing;
20
+ export declare function makeColWidthPt(ws: ParsedWorksheet): (col: number) => number;
21
+ export declare function makeRowHeightPt(ws: ParsedWorksheet): (row: number) => number;
@@ -12,12 +12,20 @@ var parser = new XMLParser({
12
12
  var TWIPS_PER_PT = 20;
13
13
  function parseSheetDrawing(drawingXml, drawingPartPath, pkg, worksheet) {
14
14
  const root = parser.parse(new TextDecoder().decode(drawingXml))["wsDr"];
15
- if (!root || typeof root !== "object") return [];
15
+ if (!root || typeof root !== "object") return {
16
+ charts: [],
17
+ pictures: []
18
+ };
16
19
  const rootObj = root;
17
20
  const colWidthPt = makeColWidthPt(worksheet);
18
21
  const rowHeightPt = makeRowHeightPt(worksheet);
19
22
  const rels = pkg.getPartRelationships(drawingPartPath);
20
- const out = [];
23
+ const partPathOf = (relId) => {
24
+ const rel = rels.find((r) => r.id === relId);
25
+ return rel ? pkg.resolveRelatedPart(drawingPartPath, rel)?.path : void 0;
26
+ };
27
+ const charts = [];
28
+ const pictures = [];
21
29
  for (const kind of [
22
30
  "twoCellAnchor",
23
31
  "oneCellAnchor",
@@ -26,10 +34,8 @@ function parseSheetDrawing(drawingXml, drawingPartPath, pkg, worksheet) {
26
34
  if (!anchor || typeof anchor !== "object") continue;
27
35
  const a = anchor;
28
36
  const chartRelId = chartRelIdOf(a);
29
- if (!chartRelId) continue;
30
- const rel = rels.find((r) => r.id === chartRelId);
31
- const resolved = rel ? pkg.resolveRelatedPart(drawingPartPath, rel) : void 0;
32
- if (!resolved) continue;
37
+ const picRelId = chartRelId ? void 0 : picRelIdOf(a);
38
+ if (!chartRelId && !picRelId) continue;
33
39
  const from = cellMarker(a["from"]);
34
40
  let widthPt = 0;
35
41
  let heightPt = 0;
@@ -46,14 +52,41 @@ function parseSheetDrawing(drawingXml, drawingPartPath, pkg, worksheet) {
46
52
  heightPt = emuToPt(num(e["@_cy"]) ?? 0);
47
53
  }
48
54
  if (widthPt <= 0 || heightPt <= 0) continue;
49
- out.push({
50
- chartPartPath: resolved.path,
51
- widthPt,
52
- heightPt,
53
- anchorRow: from?.row ?? 0
54
- });
55
+ const anchorRow = from?.row ?? 0;
56
+ if (chartRelId) {
57
+ const path = partPathOf(chartRelId);
58
+ if (path) charts.push({
59
+ chartPartPath: path,
60
+ widthPt,
61
+ heightPt,
62
+ anchorRow
63
+ });
64
+ } else if (picRelId) {
65
+ const path = partPathOf(picRelId);
66
+ if (path) pictures.push({
67
+ imagePartPath: path,
68
+ widthPt,
69
+ heightPt,
70
+ anchorRow
71
+ });
72
+ }
55
73
  }
56
- return out.sort((x, y) => x.anchorRow - y.anchorRow);
74
+ charts.sort((x, y) => x.anchorRow - y.anchorRow);
75
+ pictures.sort((x, y) => x.anchorRow - y.anchorRow);
76
+ return {
77
+ charts,
78
+ pictures
79
+ };
80
+ }
81
+ function picRelIdOf(anchor) {
82
+ const pic = anchor["pic"];
83
+ if (!pic || typeof pic !== "object") return void 0;
84
+ const blipFill = pic["blipFill"];
85
+ if (!blipFill || typeof blipFill !== "object") return void 0;
86
+ const blip = blipFill["blip"];
87
+ if (!blip || typeof blip !== "object") return void 0;
88
+ const embed = blip["@_embed"];
89
+ return typeof embed === "string" && embed !== "" ? embed : void 0;
57
90
  }
58
91
  function chartRelIdOf(anchor) {
59
92
  const frame = anchor["graphicFrame"];
@@ -110,4 +143,4 @@ function asArray(v) {
110
143
  return Array.isArray(v) ? v : v !== void 0 ? [v] : [];
111
144
  }
112
145
  //#endregion
113
- export { parseSheetDrawing };
146
+ export { makeColWidthPt, makeRowHeightPt, parseSheetDrawing };
@@ -0,0 +1,4 @@
1
+ import { ShapeBlock } from '../core/document-model/index.js';
2
+ import { ColorResolver } from '../core/drawingml/colors.js';
3
+ import { ParsedWorksheet } from '../core/spreadsheet-model/index.js';
4
+ export declare function parseSheetShapes(drawingXml: Uint8Array, worksheet: ParsedWorksheet, colors: ColorResolver): Array<ShapeBlock>;
@@ -0,0 +1,96 @@
1
+ import { emuToPt, pt } from "../core/ir/units.js";
2
+ import { poChildren, poFirstChild, poIntAttr, poIs, poText } from "../core/po-helpers.js";
3
+ import { parseFill, parseLine } from "../word/drawing-parser.js";
4
+ import { parseGeometry, parseTxBody } from "../pptx/slide-parser.js";
5
+ import { parseXml } from "../pptx/pptx-reader.js";
6
+ import { makeColWidthPt, makeRowHeightPt } from "./sheet-drawing.js";
7
+ //#region src/excel/sheet-shape-parser.ts
8
+ var ANCHOR_KINDS = [
9
+ "xdr:twoCellAnchor",
10
+ "xdr:oneCellAnchor",
11
+ "xdr:absoluteAnchor"
12
+ ];
13
+ function parseSheetShapes(drawingXml, worksheet, colors) {
14
+ const wsDr = parseXml(drawingXml).find((n) => poIs(n, "xdr:wsDr"));
15
+ if (!wsDr) return [];
16
+ const colWidthPt = makeColWidthPt(worksheet);
17
+ const rowHeightPt = makeRowHeightPt(worksheet);
18
+ const shapes = [];
19
+ for (const anchor of poChildren(wsDr)) {
20
+ if (!ANCHOR_KINDS.some((k) => poIs(anchor, k))) continue;
21
+ const sp = poChildren(anchor).find((c) => poIs(c, "xdr:sp"));
22
+ if (!sp) continue;
23
+ const box = anchorBox(anchor, colWidthPt, rowHeightPt);
24
+ if (!box) continue;
25
+ const spPr = poChildren(sp).find((c) => poIs(c, "xdr:spPr"));
26
+ const geometry = parseGeometry(spPr);
27
+ const fill = spPr ? parseFill(spPr, colors) : { kind: "none" };
28
+ const line = spPr ? parseLine(spPr, colors) : void 0;
29
+ const txBody = poChildren(sp).find((c) => poIs(c, "xdr:txBody"));
30
+ const text = txBody ? parseTxBody(txBody, void 0, void 0, colors, void 0) : void 0;
31
+ const visibleLine = line !== void 0 && line.fill !== "none";
32
+ if (!text && fill.kind === "none" && !visibleLine) continue;
33
+ shapes.push({
34
+ shape: {
35
+ width: pt(box.widthPt),
36
+ height: pt(box.heightPt),
37
+ geometry,
38
+ fill,
39
+ ...line ? { line } : {},
40
+ ...text ? { text } : {},
41
+ paragraphProperties: {}
42
+ },
43
+ anchorRow: box.anchorRow
44
+ });
45
+ }
46
+ shapes.sort((a, b) => a.anchorRow - b.anchorRow);
47
+ return shapes.map((s) => s.shape);
48
+ }
49
+ function anchorBox(anchor, colWidthPt, rowHeightPt) {
50
+ const from = marker(poChildren(anchor).find((c) => poIs(c, "xdr:from")));
51
+ if (poIs(anchor, "xdr:twoCellAnchor")) {
52
+ const to = marker(poChildren(anchor).find((c) => poIs(c, "xdr:to")));
53
+ if (!from || !to) return void 0;
54
+ const widthPt = span(from.col, from.colOffPt, to.col, to.colOffPt, colWidthPt);
55
+ const heightPt = span(from.row, from.rowOffPt, to.row, to.rowOffPt, rowHeightPt);
56
+ return widthPt > 0 && heightPt > 0 ? {
57
+ widthPt,
58
+ heightPt,
59
+ anchorRow: from.row
60
+ } : void 0;
61
+ }
62
+ const ext = poChildren(anchor).find((c) => poIs(c, "xdr:ext"));
63
+ if (!ext) return void 0;
64
+ const widthPt = emuToPt(poIntAttr(ext, "cx") ?? 0);
65
+ const heightPt = emuToPt(poIntAttr(ext, "cy") ?? 0);
66
+ return widthPt > 0 && heightPt > 0 ? {
67
+ widthPt,
68
+ heightPt,
69
+ anchorRow: from?.row ?? 0
70
+ } : void 0;
71
+ }
72
+ function marker(node) {
73
+ if (!node) return void 0;
74
+ const col = childInt(node, "xdr:col");
75
+ const row = childInt(node, "xdr:row");
76
+ if (col === void 0 || row === void 0) return void 0;
77
+ return {
78
+ col,
79
+ row,
80
+ colOffPt: emuToPt(childInt(node, "xdr:colOff") ?? 0),
81
+ rowOffPt: emuToPt(childInt(node, "xdr:rowOff") ?? 0)
82
+ };
83
+ }
84
+ function childInt(parent, tag) {
85
+ const child = poFirstChild(parent, tag);
86
+ if (!child) return void 0;
87
+ const n = Number(poText(child));
88
+ return Number.isFinite(n) ? n : void 0;
89
+ }
90
+ function span(from, fromOffPt, to, toOffPt, trackPt) {
91
+ let total = 0;
92
+ for (let i = from; i < to; i++) total += trackPt(i);
93
+ return total - fromOffPt + toOffPt;
94
+ }
95
+ //#endregion
96
+ export { parseSheetShapes };