reamkit 1.13.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 (30) hide show
  1. package/dist/esm/core/document-model/types.d.ts +2 -0
  2. package/dist/esm/core/ir/sheet.d.ts +35 -3
  3. package/dist/esm/core/spreadsheet-model/index.d.ts +1 -1
  4. package/dist/esm/core/spreadsheet-model/types.d.ts +62 -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 +4 -2
  15. package/dist/esm/excel/print-model.js +123 -16
  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 +125 -2
  23. package/dist/esm/excel/styles-parser.js +37 -0
  24. package/dist/esm/excel/worksheet-parser.js +125 -1
  25. package/dist/esm/excel/xlsx-reader.js +107 -14
  26. package/dist/esm/excel/xlsx-writer.js +10 -2
  27. package/dist/esm/layout/styled-layout.js +29 -0
  28. package/dist/esm/pptx/slide-parser.d.ts +4 -2
  29. package/dist/esm/pptx/slide-parser.js +1 -1
  30. package/package.json +1 -1
@@ -2,14 +2,18 @@ import { pt } from "../core/ir/units.js";
2
2
  import { EMPTY_STYLE_SHEET, resolveBodyStyles } from "../core/style-cascade/resolver.js";
3
3
  import "../core/style-cascade/index.js";
4
4
  import { resolvePrintArea, resolvePrintTitleRows, sectionFromWorksheet, slicerTable, worksheetToBody } from "./print-model.js";
5
+ import { buildHeaderFooterContent } from "./header-footer.js";
5
6
  //#region src/excel/sheet-to-flow.ts
7
+ var HEADER_REL = "_xlsxHeaderDefault";
8
+ var FOOTER_REL = "_xlsxFooterDefault";
6
9
  function projectSheetDoc(sheet) {
7
10
  const body = [];
8
11
  let firstSheetSection;
12
+ const headersFooters = /* @__PURE__ */ new Map();
9
13
  const sheetGrids = new Map(sheet.sheets.map((s) => [s.name, s.grid]));
10
14
  for (let sheetIdx = 0; sheetIdx < sheet.sheets.length; sheetIdx++) {
11
15
  const ws = sheet.sheets[sheetIdx];
12
- if (sheetIdx === 0) firstSheetSection = sectionFromWorksheet(ws.grid);
16
+ if (sheetIdx === 0) firstSheetSection = withHeaderFooter(sectionFromWorksheet(ws.grid), ws, headersFooters);
13
17
  if (sheetIdx > 0) body.push({
14
18
  kind: "paragraph",
15
19
  paragraph: {
@@ -24,7 +28,9 @@ function projectSheetDoc(sheet) {
24
28
  ...printArea ? { printArea } : {},
25
29
  ...titleRows ? { titleRows } : {},
26
30
  gridLines,
27
- sheetGrids
31
+ sheetGrids,
32
+ ...ws.hyperlinks ? { hyperlinks: ws.hyperlinks } : {},
33
+ ...sheet.sharedStringRuns ? { sharedStringRuns: sheet.sharedStringRuns } : {}
28
34
  }));
29
35
  for (const ref of ws.charts ?? []) body.push({
30
36
  kind: "chart",
@@ -35,10 +41,25 @@ function projectSheetDoc(sheet) {
35
41
  paragraphProperties: {}
36
42
  }
37
43
  });
44
+ for (const img of ws.images ?? []) body.push({
45
+ kind: "image",
46
+ image: {
47
+ resource: img.resourceId,
48
+ width: pt(img.widthPt),
49
+ height: pt(img.heightPt),
50
+ paragraphProperties: {}
51
+ }
52
+ });
53
+ for (const shape of ws.shapes ?? []) body.push({
54
+ kind: "shape",
55
+ shape
56
+ });
38
57
  for (const slicer of ws.slicers ?? []) body.push({
39
58
  kind: "table",
40
59
  table: slicerTable(slicer)
41
60
  });
61
+ if (ws.comments && ws.comments.length > 0) body.push(...commentBlocks(ws.comments));
62
+ if (ws.formControls && ws.formControls.length > 0) body.push(...formControlBlocks(ws.formControls));
42
63
  }
43
64
  return {
44
65
  kind: "flow",
@@ -48,8 +69,110 @@ function projectSheetDoc(sheet) {
48
69
  styles: EMPTY_STYLE_SHEET,
49
70
  resources: sheet.resources,
50
71
  ...sheet.chartData && sheet.chartData.size > 0 ? { charts: sheet.chartData } : {},
72
+ ...headersFooters.size > 0 ? { headersFooters } : {},
51
73
  ...sheet.info ? { info: sheet.info } : {}
52
74
  };
53
75
  }
76
+ function commentBlocks(comments) {
77
+ const out = [{
78
+ kind: "paragraph",
79
+ paragraph: {
80
+ properties: {},
81
+ runs: [{
82
+ text: "Comments",
83
+ properties: { bold: true }
84
+ }]
85
+ }
86
+ }];
87
+ for (const c of comments) {
88
+ const body = c.text.replace(/\s+/g, " ").trim();
89
+ const label = c.author ? `${c.ref} — ${c.author}: ` : `${c.ref}: `;
90
+ out.push({
91
+ kind: "paragraph",
92
+ paragraph: {
93
+ properties: {},
94
+ runs: [{
95
+ text: label,
96
+ properties: { bold: true }
97
+ }, ...body.length > 0 ? [{
98
+ text: body,
99
+ properties: {}
100
+ }] : []]
101
+ }
102
+ });
103
+ }
104
+ return out;
105
+ }
106
+ function formControlBlocks(controls) {
107
+ const out = [{
108
+ kind: "paragraph",
109
+ paragraph: {
110
+ properties: {},
111
+ runs: [{
112
+ text: "Form controls",
113
+ properties: { bold: true }
114
+ }]
115
+ }
116
+ }];
117
+ for (const c of controls) out.push({
118
+ kind: "paragraph",
119
+ paragraph: {
120
+ properties: {},
121
+ runs: [{
122
+ text: formControlLabel(c),
123
+ properties: {}
124
+ }]
125
+ }
126
+ });
127
+ return out;
128
+ }
129
+ function formControlLabel(c) {
130
+ const name = c.name ?? c.objectType ?? "Control";
131
+ switch ((c.objectType ?? "").toLowerCase()) {
132
+ case "checkbox": return `${c.checked ? "[x]" : "[ ]"} ${name}`;
133
+ case "radio": return `${c.checked ? "(o)" : "( )"} ${name}`;
134
+ case "buttons": return `[ ${name} ]`;
135
+ case "spin":
136
+ case "scroll": return c.value !== void 0 ? `${name} (value ${c.value})` : name;
137
+ case "drop":
138
+ case "list": return `${name} (list)`;
139
+ default: return c.objectType ? `${name} (${c.objectType})` : name;
140
+ }
141
+ }
142
+ function withHeaderFooter(section, ws, headersFooters) {
143
+ const hf = ws.grid.headerFooter;
144
+ if (!hf || !hf.oddHeader && !hf.oddFooter) return section;
145
+ const headers = [];
146
+ const footers = [];
147
+ if (hf.oddHeader) {
148
+ const content = buildHeaderFooterContent(hf.oddHeader, ws.name);
149
+ if (content.length > 0) {
150
+ headersFooters.set(HEADER_REL, resolveBodyStyles(content, EMPTY_STYLE_SHEET));
151
+ headers.push({
152
+ type: "default",
153
+ relationshipId: HEADER_REL
154
+ });
155
+ }
156
+ }
157
+ if (hf.oddFooter) {
158
+ const content = buildHeaderFooterContent(hf.oddFooter, ws.name);
159
+ if (content.length > 0) {
160
+ headersFooters.set(FOOTER_REL, resolveBodyStyles(content, EMPTY_STYLE_SHEET));
161
+ footers.push({
162
+ type: "default",
163
+ relationshipId: FOOTER_REL
164
+ });
165
+ }
166
+ }
167
+ if (headers.length === 0 && footers.length === 0) return section;
168
+ return {
169
+ ...section ?? {
170
+ headers: [],
171
+ footers: []
172
+ },
173
+ headers,
174
+ footers
175
+ };
176
+ }
54
177
  //#endregion
55
178
  export { projectSheetDoc };
@@ -101,6 +101,12 @@ function parseBorders(root) {
101
101
  if (right) border.right = right;
102
102
  if (bottom) border.bottom = bottom;
103
103
  if (left) border.left = left;
104
+ const diagonal = parseBorderEdge(asObject(obj["diagonal"]));
105
+ if (diagonal) {
106
+ border.diagonal = diagonal;
107
+ if (boolAttr(obj, "diagonalUp")) border.diagonalUp = true;
108
+ if (boolAttr(obj, "diagonalDown")) border.diagonalDown = true;
109
+ }
104
110
  out.push(border);
105
111
  }
106
112
  return out;
@@ -169,6 +175,13 @@ function parseFills(root) {
169
175
  if (fg) fill.fgColorHex = fg;
170
176
  if (bg) fill.bgColorHex = bg;
171
177
  }
178
+ if (!fill.patternType) {
179
+ const avg = averageGradientColor(asObject(obj["gradientFill"]));
180
+ if (avg) {
181
+ fill.patternType = "solid";
182
+ fill.fgColorHex = avg;
183
+ }
184
+ }
172
185
  out.push(fill);
173
186
  }
174
187
  return out;
@@ -205,6 +218,11 @@ function parseCellXfs(root) {
205
218
  if (h) a.horizontal = h;
206
219
  if (v) a.vertical = v;
207
220
  if (wrap !== void 0) a.wrapText = wrap;
221
+ const indent = numAttr(align, "indent");
222
+ if (indent !== void 0 && indent > 0) a.indent = indent;
223
+ const rotation = numAttr(align, "textRotation");
224
+ if (rotation !== void 0 && rotation !== 0) a.textRotation = rotation;
225
+ if (boolAttr(align, "shrinkToFit")) a.shrinkToFit = true;
208
226
  if (Object.keys(a).length > 0) xf.alignment = a;
209
227
  }
210
228
  out.push(xf);
@@ -250,6 +268,25 @@ function childToggle(obj, childName) {
250
268
  if (val === void 0) return true;
251
269
  return !(val === "false" || val === "0");
252
270
  }
271
+ function averageGradientColor(gf) {
272
+ if (!gf) return void 0;
273
+ let r = 0;
274
+ let g = 0;
275
+ let b = 0;
276
+ let n = 0;
277
+ for (const s of asArray(gf["stop"])) {
278
+ const so = asObject(s);
279
+ const hex = so ? colorOf(asObject(so["color"])) : void 0;
280
+ if (!hex) continue;
281
+ r += parseInt(hex.slice(0, 2), 16);
282
+ g += parseInt(hex.slice(2, 4), 16);
283
+ b += parseInt(hex.slice(4, 6), 16);
284
+ n++;
285
+ }
286
+ if (n === 0) return void 0;
287
+ const h = (x) => Math.round(x / n).toString(16).padStart(2, "0").toUpperCase();
288
+ return `${h(r)}${h(g)}${h(b)}`;
289
+ }
253
290
  function colorOf(node) {
254
291
  if (!node) return void 0;
255
292
  const rgb = strAttr(node, "rgb");
@@ -38,6 +38,9 @@ function parseWorksheet(data) {
38
38
  const drawingRelId = drawingNode && typeof drawingNode === "object" ? strAttr(drawingNode, "id") : void 0;
39
39
  const conditionalFormats = parseConditionalFormatting(wsObj);
40
40
  const dataValidations = parseDataValidations(wsObj);
41
+ const hyperlinks = parseHyperlinks(wsObj);
42
+ const headerFooter = parseHeaderFooter(wsObj);
43
+ const formControls = parseFormControls(wsObj);
41
44
  const sparklines = parseSparklines(wsObj);
42
45
  const tablePartRelIds = parseTableParts(wsObj);
43
46
  const printModel = {
@@ -51,6 +54,9 @@ function parseWorksheet(data) {
51
54
  ...drawingRelId !== void 0 ? { drawingRelId } : {},
52
55
  ...conditionalFormats.length > 0 ? { conditionalFormats } : {},
53
56
  ...dataValidations.length > 0 ? { dataValidations } : {},
57
+ ...hyperlinks.length > 0 ? { hyperlinks } : {},
58
+ ...headerFooter ? { headerFooter } : {},
59
+ ...formControls.length > 0 ? { formControls } : {},
54
60
  ...sparklines.length > 0 ? { sparklines } : {},
55
61
  ...tablePartRelIds.length > 0 ? { tablePartRelIds } : {}
56
62
  };
@@ -354,6 +360,41 @@ function boolAttr(obj, key) {
354
360
  const raw = strAttr(obj, key);
355
361
  return raw === "1" || raw === "true";
356
362
  }
363
+ function parseHeaderFooter(ws) {
364
+ const node = asObjectNode(ws["headerFooter"]);
365
+ if (!node) return void 0;
366
+ const oddHeader = formulaText(node["oddHeader"]);
367
+ const oddFooter = formulaText(node["oddFooter"]);
368
+ if (!oddHeader && !oddFooter) return void 0;
369
+ return {
370
+ ...oddHeader ? { oddHeader } : {},
371
+ ...oddFooter ? { oddFooter } : {}
372
+ };
373
+ }
374
+ function parseHyperlinks(ws) {
375
+ const node = asObjectNode(ws["hyperlinks"]);
376
+ if (!node) return [];
377
+ const out = [];
378
+ for (const h of toArray(node["hyperlink"])) {
379
+ const obj = asObjectNode(h);
380
+ if (!obj) continue;
381
+ const ref = strAttr(obj, "ref");
382
+ if (!ref) continue;
383
+ const relId = strAttr(obj, "id");
384
+ const location = strAttr(obj, "location");
385
+ if (relId === void 0 && location === void 0) continue;
386
+ const display = strAttr(obj, "display");
387
+ const tooltip = strAttr(obj, "tooltip");
388
+ out.push({
389
+ ref,
390
+ ...relId !== void 0 ? { relId } : {},
391
+ ...location !== void 0 ? { location } : {},
392
+ ...display !== void 0 ? { display } : {},
393
+ ...tooltip !== void 0 ? { tooltip } : {}
394
+ });
395
+ }
396
+ return out;
397
+ }
357
398
  function parseSqref(sqref) {
358
399
  const out = [];
359
400
  for (const token of sqref.split(/\s+/)) {
@@ -380,14 +421,73 @@ var CF_OPERATORS = new Set([
380
421
  ]);
381
422
  function parseCfRule(obj) {
382
423
  const priority = parseNumericAttr(obj, "priority") ?? 0;
383
- switch (strAttr(obj, "type")) {
424
+ const type = strAttr(obj, "type");
425
+ switch (type) {
384
426
  case "cellIs": return parseCellIsRule(obj, priority);
385
427
  case "colorScale": return parseColorScaleRule(obj, priority);
386
428
  case "dataBar": return parseDataBarRule(obj, priority);
387
429
  case "iconSet": return parseIconSetRule(obj, priority);
430
+ case "top10": return parseTop10Rule(obj, priority);
431
+ case "aboveAverage": return parseAboveAverageRule(obj, priority);
432
+ case "duplicateValues":
433
+ case "uniqueValues": return parseDupUniqueRule(type, obj, priority);
434
+ case "containsText":
435
+ case "notContainsText":
436
+ case "beginsWith":
437
+ case "endsWith": return parseTextRule(type, obj, priority);
388
438
  default: return;
389
439
  }
390
440
  }
441
+ function parseTop10Rule(obj, priority) {
442
+ const dxfId = parseNumericAttr(obj, "dxfId");
443
+ if (dxfId === void 0) return void 0;
444
+ return {
445
+ type: "top10",
446
+ priority,
447
+ rank: parseNumericAttr(obj, "rank") ?? 10,
448
+ percent: boolAttr(obj, "percent"),
449
+ bottom: boolAttr(obj, "bottom"),
450
+ dxfId
451
+ };
452
+ }
453
+ function parseAboveAverageRule(obj, priority) {
454
+ const dxfId = parseNumericAttr(obj, "dxfId");
455
+ if (dxfId === void 0) return void 0;
456
+ const aboveRaw = strAttr(obj, "aboveAverage");
457
+ const aboveAverage = aboveRaw === void 0 ? true : aboveRaw === "1" || aboveRaw === "true";
458
+ const stdDev = parseNumericAttr(obj, "stdDev");
459
+ return {
460
+ type: "aboveAverage",
461
+ priority,
462
+ aboveAverage,
463
+ equalAverage: boolAttr(obj, "equalAverage"),
464
+ ...stdDev !== void 0 ? { stdDev } : {},
465
+ dxfId
466
+ };
467
+ }
468
+ function parseDupUniqueRule(type, obj, priority) {
469
+ const dxfId = parseNumericAttr(obj, "dxfId");
470
+ if (dxfId === void 0) return void 0;
471
+ return {
472
+ type,
473
+ priority,
474
+ dxfId
475
+ };
476
+ }
477
+ function parseTextRule(type, obj, priority) {
478
+ const dxfId = parseNumericAttr(obj, "dxfId");
479
+ if (dxfId === void 0) return void 0;
480
+ const text = strAttr(obj, "text");
481
+ if (text === void 0) return void 0;
482
+ const formula = formulaText(obj["formula"]);
483
+ return {
484
+ type,
485
+ priority,
486
+ text,
487
+ dxfId,
488
+ ...formula !== void 0 ? { formula } : {}
489
+ };
490
+ }
391
491
  function parseIconSetRule(obj, priority) {
392
492
  const is = obj["iconSet"];
393
493
  if (!is || typeof is !== "object") return void 0;
@@ -545,6 +645,30 @@ function parseSparklines(ws) {
545
645
  }
546
646
  return out;
547
647
  }
648
+ function parseFormControls(ws) {
649
+ const out = [];
650
+ const seen = /* @__PURE__ */ new Set();
651
+ collectControls(asObjectNode(ws["controls"]), out, seen);
652
+ for (const ext of toArray(asObjectNode(ws["extLst"])?.["ext"])) collectControls(asObjectNode(asObjectNode(ext)?.["controls"]), out, seen);
653
+ return out;
654
+ }
655
+ function collectControls(node, out, seen) {
656
+ if (!node) return;
657
+ const direct = toArray(node["control"]);
658
+ const fromChoice = toArray(asObjectNode(node["AlternateContent"])?.["Choice"]).flatMap((c) => toArray(asObjectNode(c)?.["control"]));
659
+ for (const c of [...direct, ...fromChoice]) {
660
+ const obj = asObjectNode(c);
661
+ if (!obj) continue;
662
+ const relId = strAttr(obj, "id");
663
+ if (!relId || seen.has(relId)) continue;
664
+ seen.add(relId);
665
+ const name = strAttr(obj, "name");
666
+ out.push({
667
+ relId,
668
+ ...name ? { name } : {}
669
+ });
670
+ }
671
+ }
548
672
  function asObjectNode(v) {
549
673
  return v && typeof v === "object" && !Array.isArray(v) ? v : void 0;
550
674
  }
@@ -8,6 +8,7 @@ import { isOoxmlRel } from "../core/opc/relationship-types.js";
8
8
  import { OpcPackage } from "../core/opc/package.js";
9
9
  import { parseCoreProperties } from "../core/opc/core-properties.js";
10
10
  import "../core/opc/index.js";
11
+ import { parseAreaRef } from "./defined-name-ref.js";
11
12
  import { parseSharedStrings } from "./shared-strings-parser.js";
12
13
  import { parseWorkbook } from "./workbook-parser.js";
13
14
  import { parseWorksheet } from "./worksheet-parser.js";
@@ -18,6 +19,9 @@ import { parseSheetDrawing } from "./sheet-drawing.js";
18
19
  import { parseTablePartFull } from "./table-parser.js";
19
20
  import { parsePivotTablePart } from "./pivot-table-parser.js";
20
21
  import { parseSlicerCachePart, parseSlicerPart } from "./slicer-parser.js";
22
+ import { parseLegacyComments, parsePersons, parseThreadedComments } from "./comments-parser.js";
23
+ import { parseFormControlProps } from "./form-control-parser.js";
24
+ import { parseSheetShapes } from "./sheet-shape-parser.js";
21
25
  import { projectSheetDoc } from "./sheet-to-flow.js";
22
26
  //#region src/excel/xlsx-reader.ts
23
27
  var WORKBOOK_PART = "xl/workbook.xml";
@@ -26,6 +30,8 @@ var STYLES_PART = "xl/styles.xml";
26
30
  var CORE_PROPS_PART = "docProps/core.xml";
27
31
  var SLICER_REL_TAIL = "/slicer";
28
32
  var SLICER_CACHE_REL_TAIL = "/slicerCache";
33
+ var THREADED_COMMENTS_REL_TAIL = "/threadedComment";
34
+ var PERSON_REL_TAIL = "/person";
29
35
  var MAX_SLICER_ITEMS = 256;
30
36
  function readXlsx(xlsx) {
31
37
  return {
@@ -40,11 +46,21 @@ function readXlsxToSheetDoc(xlsx) {
40
46
  const { sheets, date1904, definedNames } = parseWorkbook(workbookData);
41
47
  if (sheets.length === 0) throw new Error("xlsx has no sheets");
42
48
  const sharedStringsData = pkg.getPart(SHARED_STRINGS_PART);
43
- const sharedStrings = sharedStringsData ? parseSharedStrings(sharedStringsData) : [];
49
+ const { texts: sharedStrings, runs: sharedStringRuns } = sharedStringsData ? parseSharedStrings(sharedStringsData) : {
50
+ texts: [],
51
+ runs: []
52
+ };
44
53
  const stylesData = pkg.getPart(STYLES_PART);
45
54
  const styles = stylesData ? parseXlsxStyles(stylesData) : EMPTY_XLSX_STYLES;
46
55
  const workbookRels = pkg.getPartRelationships(WORKBOOK_PART);
56
+ const persons = /* @__PURE__ */ new Map();
57
+ for (const rel of workbookRels) {
58
+ if (!rel.type.endsWith(PERSON_REL_TAIL)) continue;
59
+ const part = pkg.resolveRelatedPart(WORKBOOK_PART, rel);
60
+ if (part) for (const [id, name] of parsePersons(part.data)) persons.set(id, name);
61
+ }
47
62
  const chartData = /* @__PURE__ */ new Map();
63
+ const resources = new ResourceStore();
48
64
  const palette = buildThemePalette(pkg, workbookRels);
49
65
  const resolveColor = makeColorResolver(palette);
50
66
  const sheetsOut = [];
@@ -58,21 +74,39 @@ function readXlsxToSheetDoc(xlsx) {
58
74
  if (!resolved) continue;
59
75
  const worksheet = parseWorksheet(resolved.data);
60
76
  const charts = [];
77
+ const images = [];
78
+ let shapes;
61
79
  if (worksheet.drawingRelId) {
62
80
  const drawingRel = pkg.getPartRelationships(resolved.path).find((r) => r.id === worksheet.drawingRelId);
63
81
  const drawing = drawingRel ? pkg.resolveRelatedPart(resolved.path, drawingRel) : void 0;
64
- if (drawing) for (const ref of parseSheetDrawing(drawing.data, drawing.path, pkg, worksheet)) {
65
- if (!chartData.has(ref.chartPartPath)) {
66
- const chartXml = pkg.getPart(ref.chartPartPath);
67
- const parsed = chartXml ? parseChart(chartXml, resolveColor) : null;
68
- if (!parsed) continue;
69
- chartData.set(ref.chartPartPath, withChartColorStyle(parsed, pkg, ref.chartPartPath, resolveColor));
82
+ if (drawing) {
83
+ const { charts: chartRefs, pictures } = parseSheetDrawing(drawing.data, drawing.path, pkg, worksheet);
84
+ for (const ref of chartRefs) {
85
+ if (!chartData.has(ref.chartPartPath)) {
86
+ const chartXml = pkg.getPart(ref.chartPartPath);
87
+ const parsed = chartXml ? parseChart(chartXml, resolveColor) : null;
88
+ if (!parsed) continue;
89
+ chartData.set(ref.chartPartPath, withChartColorStyle(parsed, pkg, ref.chartPartPath, resolveColor));
90
+ }
91
+ charts.push({
92
+ chartPartPath: ref.chartPartPath,
93
+ widthPt: ref.widthPt,
94
+ heightPt: ref.heightPt
95
+ });
96
+ }
97
+ for (const pic of pictures) {
98
+ const bytes = pkg.getPart(pic.imagePartPath);
99
+ if (!bytes) continue;
100
+ images.push({
101
+ resourceId: resources.put(bytes),
102
+ widthPt: pic.widthPt,
103
+ heightPt: pic.heightPt
104
+ });
105
+ }
106
+ if (bytesInclude(drawing.data, ":sp>") || bytesInclude(drawing.data, ":sp ")) {
107
+ const parsed = parseSheetShapes(drawing.data, worksheet, resolveColor);
108
+ if (parsed.length > 0) shapes = parsed;
70
109
  }
71
- charts.push({
72
- chartPartPath: ref.chartPartPath,
73
- widthPt: ref.widthPt,
74
- heightPt: ref.heightPt
75
- });
76
110
  }
77
111
  }
78
112
  let tables;
@@ -118,6 +152,59 @@ function readXlsxToSheetDoc(xlsx) {
118
152
  }
119
153
  if (resolvedPivots.length > 0) pivotTables = resolvedPivots;
120
154
  }
155
+ let hyperlinks;
156
+ if (worksheet.hyperlinks && worksheet.hyperlinks.length > 0) {
157
+ const wsRels = pkg.getPartRelationships(resolved.path);
158
+ const resolvedLinks = [];
159
+ for (const h of worksheet.hyperlinks) {
160
+ if (h.relId === void 0) continue;
161
+ const rel = wsRels.find((r) => r.id === h.relId);
162
+ if (!rel || rel.targetMode !== "External" || !rel.target) continue;
163
+ const area = parseAreaRef(h.ref);
164
+ if (!area) continue;
165
+ resolvedLinks.push({
166
+ ref: {
167
+ startColumn: area.startColumn,
168
+ startRow: area.startRow,
169
+ endColumn: area.endColumn,
170
+ endRow: area.endRow
171
+ },
172
+ url: rel.target
173
+ });
174
+ }
175
+ if (resolvedLinks.length > 0) hyperlinks = resolvedLinks;
176
+ }
177
+ let comments;
178
+ {
179
+ const wsRels = pkg.getPartRelationships(resolved.path);
180
+ const resolvedComments = [];
181
+ for (const rel of wsRels) {
182
+ if (!isOoxmlRel(rel.type, "comments")) continue;
183
+ const part = pkg.resolveRelatedPart(resolved.path, rel);
184
+ if (part) resolvedComments.push(...parseLegacyComments(part.data));
185
+ }
186
+ for (const rel of wsRels) {
187
+ if (!rel.type.endsWith(THREADED_COMMENTS_REL_TAIL)) continue;
188
+ const part = pkg.resolveRelatedPart(resolved.path, rel);
189
+ if (part) resolvedComments.push(...parseThreadedComments(part.data, persons));
190
+ }
191
+ if (resolvedComments.length > 0) comments = resolvedComments;
192
+ }
193
+ let formControls;
194
+ if (worksheet.formControls && worksheet.formControls.length > 0) {
195
+ const wsRels = pkg.getPartRelationships(resolved.path);
196
+ const resolvedControls = [];
197
+ for (const fc of worksheet.formControls) {
198
+ const rel = wsRels.find((r) => r.id === fc.relId);
199
+ const part = rel ? pkg.resolveRelatedPart(resolved.path, rel) : void 0;
200
+ const props = part ? parseFormControlProps(part.data) : {};
201
+ resolvedControls.push({
202
+ ...fc.name ? { name: fc.name } : {},
203
+ ...props
204
+ });
205
+ }
206
+ if (resolvedControls.length > 0) formControls = resolvedControls;
207
+ }
121
208
  const grid = tables || pivotTables ? {
122
209
  ...worksheet,
123
210
  ...tables ? { tables } : {},
@@ -126,7 +213,12 @@ function readXlsxToSheetDoc(xlsx) {
126
213
  sheetsOut.push({
127
214
  name: sheet.name,
128
215
  grid,
129
- ...charts.length > 0 ? { charts } : {}
216
+ ...charts.length > 0 ? { charts } : {},
217
+ ...images.length > 0 ? { images } : {},
218
+ ...shapes ? { shapes } : {},
219
+ ...hyperlinks ? { hyperlinks } : {},
220
+ ...comments ? { comments } : {},
221
+ ...formControls ? { formControls } : {}
130
222
  });
131
223
  }
132
224
  if (pendingSlicers.length > 0) {
@@ -154,10 +246,11 @@ function readXlsxToSheetDoc(xlsx) {
154
246
  sheets: sheetsOut,
155
247
  styles,
156
248
  sharedStrings,
249
+ ...sharedStringRuns.some((r) => r !== void 0) ? { sharedStringRuns } : {},
157
250
  definedNames,
158
251
  date1904,
159
252
  ...chartData.size > 0 ? { chartData } : {},
160
- resources: new ResourceStore(),
253
+ resources,
161
254
  ...info ? { info } : {}
162
255
  };
163
256
  }
@@ -230,6 +230,14 @@ function cfRuleXml(rule) {
230
230
  case "colorScale": return `<cfRule type="colorScale"${p}><colorScale>` + rule.cfvos.map(cfvoXml).join("") + rule.colorsHex.map((c) => `<color rgb="FF${c}"/>`).join("") + "</colorScale></cfRule>";
231
231
  case "dataBar": return `<cfRule type="dataBar"${p}><dataBar` + (rule.minLength !== void 0 ? ` minLength="${rule.minLength}"` : "") + (rule.maxLength !== void 0 ? ` maxLength="${rule.maxLength}"` : "") + ">" + rule.cfvos.map(cfvoXml).join("") + `<color rgb="FF${rule.colorHex}"/></dataBar></cfRule>`;
232
232
  case "iconSet": return `<cfRule type="iconSet"${p}><iconSet iconSet="${rule.iconSet}"` + (rule.reverse ? " reverse=\"1\"" : "") + ">" + rule.cfvos.map(cfvoXml).join("") + "</iconSet></cfRule>";
233
+ case "top10": return `<cfRule type="top10"${p} dxfId="${rule.dxfId}" rank="${rule.rank}"` + (rule.percent ? " percent=\"1\"" : "") + (rule.bottom ? " bottom=\"1\"" : "") + "/>";
234
+ case "aboveAverage": return `<cfRule type="aboveAverage"${p} dxfId="${rule.dxfId}"` + (rule.aboveAverage ? "" : " aboveAverage=\"0\"") + (rule.equalAverage ? " equalAverage=\"1\"" : "") + (rule.stdDev !== void 0 ? ` stdDev="${rule.stdDev}"` : "") + "/>";
235
+ case "duplicateValues":
236
+ case "uniqueValues": return `<cfRule type="${rule.type}"${p} dxfId="${rule.dxfId}"/>`;
237
+ case "containsText":
238
+ case "notContainsText":
239
+ case "beginsWith":
240
+ case "endsWith": return `<cfRule type="${rule.type}"${p} operator="${rule.type}" dxfId="${rule.dxfId}" text="${escapeAttr(rule.text)}">` + (rule.formula !== void 0 ? `<formula>${escapeText(rule.formula)}</formula>` : "") + "</cfRule>";
233
241
  }
234
242
  }
235
243
  function cfvoXml(cfvo) {
@@ -313,7 +321,7 @@ function patternFill(f) {
313
321
  return `<patternFill${f.patternType ? ` patternType="${f.patternType}"` : ""}>${inner}</patternFill>`;
314
322
  }
315
323
  function borderXml(b) {
316
- return `<border>${edgeXml("left", b.left)}${edgeXml("right", b.right)}${edgeXml("top", b.top)}${edgeXml("bottom", b.bottom)}</border>`;
324
+ return `<border${b.diagonal ? `${b.diagonalUp ? " diagonalUp=\"1\"" : ""}${b.diagonalDown ? " diagonalDown=\"1\"" : ""}` : ""}>${edgeXml("left", b.left)}${edgeXml("right", b.right)}${edgeXml("top", b.top)}${edgeXml("bottom", b.bottom)}${edgeXml("diagonal", b.diagonal)}</border>`;
317
325
  }
318
326
  function edgeXml(name, edge) {
319
327
  if (!edge || !edge.style) return `<${name}/>`;
@@ -323,7 +331,7 @@ function edgeXml(name, edge) {
323
331
  function cellXfXml(xf) {
324
332
  const apply = applyAttr("applyNumberFormat", xf.applyNumberFormat) + applyAttr("applyFont", xf.applyFont) + applyAttr("applyFill", xf.applyFill) + applyAttr("applyBorder", xf.applyBorder) + applyAttr("applyAlignment", xf.applyAlignment);
325
333
  const a = xf.alignment;
326
- const alignment = a ? `<alignment${a.horizontal ? ` horizontal="${a.horizontal}"` : ""}${a.vertical ? ` vertical="${a.vertical}"` : ""}${a.wrapText ? " wrapText=\"1\"" : ""}/>` : "";
334
+ const alignment = a ? `<alignment${a.horizontal ? ` horizontal="${a.horizontal}"` : ""}${a.vertical ? ` vertical="${a.vertical}"` : ""}${a.wrapText ? " wrapText=\"1\"" : ""}${a.textRotation !== void 0 ? ` textRotation="${a.textRotation}"` : ""}${a.indent !== void 0 ? ` indent="${a.indent}"` : ""}${a.shrinkToFit ? " shrinkToFit=\"1\"" : ""}/>` : "";
327
335
  return `<xf numFmtId="${xf.numFmtId}" fontId="${xf.fontId}" fillId="${xf.fillId}" borderId="${xf.borderId}"${apply}>` + alignment + "</xf>";
328
336
  }
329
337
  function applyAttr(name, value) {
@@ -2430,6 +2430,35 @@ function emitRowChunk(out, row, marginLeft, cursorY, pageHeight, colCount, cellS
2430
2430
  }
2431
2431
  }
2432
2432
  emitCellBorders(out, cell, cellX, rowBottom, row.heightPt, pageHeight, row.rowIdx, row.rowCount, colCount);
2433
+ const diagDown = cell.borders.diagonalDown;
2434
+ const diagUp = cell.borders.diagonalUp;
2435
+ if ((diagDown || diagUp) && cell.mergeRole !== "middle" && cell.mergeRole !== "end") {
2436
+ const transform = flipTransform([
2437
+ 1,
2438
+ 0,
2439
+ 0,
2440
+ 1,
2441
+ cellX,
2442
+ rowBottom
2443
+ ], pageHeight);
2444
+ const w = cell.widthPt;
2445
+ const h = row.heightPt;
2446
+ const pushDiagonal = (b, y0, y1) => {
2447
+ out.push({
2448
+ type: "shape",
2449
+ shape: {
2450
+ paths: [new PathBuilder().moveTo(0, y0).lineTo(w, y1).build()],
2451
+ stroke: {
2452
+ colorHex: b.colorHex ?? "000000",
2453
+ widthPt: b.width ?? .75
2454
+ },
2455
+ transform
2456
+ }
2457
+ });
2458
+ };
2459
+ if (diagDown) pushDiagonal(diagDown, h, 0);
2460
+ if (diagUp) pushDiagonal(diagUp, 0, h);
2461
+ }
2433
2462
  if (cell.mergeRole === "middle" || cell.mergeRole === "end") continue;
2434
2463
  let textY = rowTop - cell.padTopPt;
2435
2464
  for (const line of cell.lines) {
@@ -1,9 +1,9 @@
1
- import { BodyElement, FloatAnchor, ShapeBlock, ShapeFill } from '../core/document-model/index.js';
1
+ import { BodyElement, FloatAnchor, ShapeBlock, ShapeFill, ShapeGeometry, ShapeTextBody } from '../core/document-model/index.js';
2
2
  import { ColorResolver } from '../core/drawingml/colors.js';
3
3
  import { Loss, Pt, ResourceId } from '../core/ir/index.js';
4
4
  import { PoNode } from '../core/po-helpers.js';
5
5
  import { PlaceholderCascade } from './placeholder-cascade.js';
6
- import { ShapeBoxEmu } from './sp-helpers.js';
6
+ import { PlaceholderRef, ShapeBoxEmu } from './sp-helpers.js';
7
7
  export interface SlideContext {
8
8
  readonly cascade?: PlaceholderCascade;
9
9
  readonly resolveImage?: (relId: string) => ResourceId | undefined;
@@ -21,4 +21,6 @@ export declare function noDiagramOverrideLoss(where?: string): Loss;
21
21
  export declare function parseDiagramDrawing(spTree: PoNode, transform: GroupTransform, makeFloat: (box: ShapeBoxEmu) => FloatAnchor, colors: ColorResolver, resolveLink: LinkResolver): Array<ShapeBlock>;
22
22
  export declare function parseBackgroundFill(bg: PoNode, colors: ColorResolver): ShapeFill | undefined;
23
23
  export declare function backdropElement(fill: ShapeFill, widthPt: Pt, heightPt: Pt): BodyElement;
24
+ export declare function parseGeometry(spPr: PoNode | undefined): ShapeGeometry;
25
+ export declare function parseTxBody(txBody: PoNode, ph: PlaceholderRef | undefined, cascade: PlaceholderCascade | undefined, colors: ColorResolver, resolveLink: LinkResolver): ShapeTextBody | undefined;
24
26
  export {};
@@ -424,4 +424,4 @@ function cellFillHex(tcPr, colors) {
424
424
  }
425
425
  }
426
426
  //#endregion
427
- export { backdropElement, diagramTransform, noDiagramOverrideLoss, parseBackgroundFill, parseDiagramDrawing, parseSlideShapes };
427
+ export { backdropElement, diagramTransform, noDiagramOverrideLoss, parseBackgroundFill, parseDiagramDrawing, parseGeometry, parseSlideShapes, parseTxBody };