reamkit 1.11.0 → 1.13.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 (42) hide show
  1. package/dist/esm/core/converter/facade.js +1 -1
  2. package/dist/esm/core/converter/project.js +1 -0
  3. package/dist/esm/core/document-model/index.d.ts +1 -1
  4. package/dist/esm/core/document-model/types.d.ts +24 -0
  5. package/dist/esm/core/ir/features.d.ts +1 -0
  6. package/dist/esm/core/ir/features.js +1 -0
  7. package/dist/esm/core/ir/flow.d.ts +3 -1
  8. package/dist/esm/core/ir/sheet.d.ts +14 -0
  9. package/dist/esm/core/po-helpers.d.ts +2 -0
  10. package/dist/esm/core/po-helpers.js +14 -1
  11. package/dist/esm/core/spreadsheet-model/index.d.ts +1 -1
  12. package/dist/esm/core/spreadsheet-model/types.d.ts +34 -0
  13. package/dist/esm/excel/index.d.ts +1 -1
  14. package/dist/esm/excel/pivot-table-parser.d.ts +2 -0
  15. package/dist/esm/excel/pivot-table-parser.js +68 -0
  16. package/dist/esm/excel/print-model.d.ts +5 -2
  17. package/dist/esm/excel/print-model.js +123 -9
  18. package/dist/esm/excel/sheet-to-flow.js +5 -1
  19. package/dist/esm/excel/slicer-parser.d.ts +15 -0
  20. package/dist/esm/excel/slicer-parser.js +76 -0
  21. package/dist/esm/excel/table-parser.d.ts +10 -0
  22. package/dist/esm/excel/table-parser.js +36 -4
  23. package/dist/esm/excel/worksheet-parser.js +56 -0
  24. package/dist/esm/excel/xlsx-reader.js +129 -5
  25. package/dist/esm/excel/xlsx-writer.js +9 -1
  26. package/dist/esm/html/html-writer.js +56 -2
  27. package/dist/esm/layout/page-doc.d.ts +1 -0
  28. package/dist/esm/layout/styled-layout.d.ts +8 -1
  29. package/dist/esm/layout/styled-layout.js +154 -6
  30. package/dist/esm/pdf/styled-page-emitter.js +38 -0
  31. package/dist/esm/pptx/pptx-reader.d.ts +2 -0
  32. package/dist/esm/pptx/pptx-reader.js +30 -3
  33. package/dist/esm/pptx/slide-parser.d.ts +8 -2
  34. package/dist/esm/pptx/slide-parser.js +80 -12
  35. package/dist/esm/word/document-parser.d.ts +17 -2
  36. package/dist/esm/word/document-parser.js +178 -18
  37. package/dist/esm/word/docx-reader.js +54 -4
  38. package/dist/esm/word/docx-writer.js +84 -1
  39. package/dist/esm/word/drawing-parser.d.ts +7 -0
  40. package/dist/esm/word/drawing-parser.js +13 -0
  41. package/dist/esm/word/index.d.ts +2 -1
  42. package/package.json +1 -1
@@ -44,9 +44,10 @@ function isOutOfFlowFloat(f) {
44
44
  return f !== void 0 && f.wrap !== "topAndBottom";
45
45
  }
46
46
  var FOOTNOTE_RULE_WIDTH = 144;
47
- function assignNoteNumbers(body) {
47
+ function assignNoteNumbers(body, commentIds) {
48
48
  const footnotes = /* @__PURE__ */ new Map();
49
49
  const endnotes = /* @__PURE__ */ new Map();
50
+ const comments = /* @__PURE__ */ new Map();
50
51
  const paragraphFootnotes = /* @__PURE__ */ new Set();
51
52
  const numberRun = (run, n) => ({
52
53
  ...run,
@@ -56,8 +57,17 @@ function assignNoteNumbers(body) {
56
57
  verticalAlign: "superscript"
57
58
  }
58
59
  });
60
+ const commentMarkerRun = (run, n) => ({
61
+ ...run,
62
+ text: `[${n}]`,
63
+ ...run.commentRef !== void 0 && commentIds?.has(run.commentRef) ? { anchor: `comment-${n}` } : {},
64
+ properties: {
65
+ ...run.properties,
66
+ verticalAlign: "superscript"
67
+ }
68
+ });
59
69
  const mapParagraph = (paragraph) => {
60
- if (!paragraph.runs.some((r) => r.footnoteRef !== void 0 || r.endnoteRef !== void 0)) return paragraph;
70
+ if (!paragraph.runs.some((r) => r.footnoteRef !== void 0 || r.endnoteRef !== void 0 || r.commentRef !== void 0)) return paragraph;
61
71
  return {
62
72
  ...paragraph,
63
73
  runs: paragraph.runs.map((run) => {
@@ -77,6 +87,14 @@ function assignNoteNumbers(body) {
77
87
  }
78
88
  return numberRun(run, n);
79
89
  }
90
+ if (run.commentRef !== void 0) {
91
+ let n = comments.get(run.commentRef);
92
+ if (n === void 0) {
93
+ n = comments.size + 1;
94
+ comments.set(run.commentRef, n);
95
+ }
96
+ return commentMarkerRun(run, n);
97
+ }
80
98
  return run;
81
99
  })
82
100
  };
@@ -117,7 +135,7 @@ function assignNoteNumbers(body) {
117
135
  return el;
118
136
  };
119
137
  const hasRefs = (els) => els.some((el) => {
120
- if (el.kind === "paragraph") return el.paragraph.runs.some((r) => r.footnoteRef !== void 0 || r.endnoteRef !== void 0);
138
+ if (el.kind === "paragraph") return el.paragraph.runs.some((r) => r.footnoteRef !== void 0 || r.endnoteRef !== void 0 || r.commentRef !== void 0);
121
139
  if (el.kind === "table") return el.table.rows.some((row) => row.cells.some((c) => hasRefs(c.content)));
122
140
  if (el.kind === "shape" && el.shape.text) return hasRefs(el.shape.text.content);
123
141
  return false;
@@ -126,12 +144,14 @@ function assignNoteNumbers(body) {
126
144
  body,
127
145
  footnotes,
128
146
  endnotes,
147
+ comments,
129
148
  deferredFootnotes: []
130
149
  };
131
150
  return {
132
151
  body: body.map((el) => mapElement(el, true)),
133
152
  footnotes,
134
153
  endnotes,
154
+ comments,
135
155
  deferredFootnotes: [...footnotes.keys()].filter((id) => !paragraphFootnotes.has(id))
136
156
  };
137
157
  }
@@ -169,9 +189,62 @@ function substituteNoteNumber(content, n) {
169
189
  }
170
190
  }, ...out.slice(1)];
171
191
  }
192
+ function flattenCommentText(content) {
193
+ const lines = [];
194
+ const visit = (els) => {
195
+ for (const el of els) if (el.kind === "paragraph") lines.push(el.paragraph.runs.map((r) => r.text).join(""));
196
+ else if (el.kind === "table") for (const row of el.table.rows) for (const cell of row.cells) visit(cell.content);
197
+ };
198
+ visit(content);
199
+ return lines.join("\n").trim();
200
+ }
201
+ function buildCommentNotes(numbers, comments) {
202
+ const out = /* @__PURE__ */ new Map();
203
+ for (const [id, n] of numbers) {
204
+ const c = comments.get(id);
205
+ if (!c) continue;
206
+ out.set(`comment-${n}`, {
207
+ ...c.author !== void 0 ? { author: c.author } : {},
208
+ contents: flattenCommentText(c.content)
209
+ });
210
+ }
211
+ return out;
212
+ }
213
+ function commentTailBlocks(comment, n, opts) {
214
+ const who = [comment.author, comment.date].filter((s) => s !== void 0 && s.length > 0).join(", ");
215
+ const inReplyTo = opts?.parentN !== void 0 ? ` (in reply to [${opts.parentN}])` : "";
216
+ const resolved = opts?.done ? " (resolved)" : "";
217
+ const labelRun = {
218
+ text: who ? `[${n}] ${who}${inReplyTo}${resolved}: ` : `[${n}]${inReplyTo}${resolved} `,
219
+ properties: {}
220
+ };
221
+ const bm = `comment-${n}`;
222
+ const indent = opts?.depth && opts.depth > 0 ? { indentLeft: pt(18 * opts.depth) } : {};
223
+ const first = comment.content[0];
224
+ if (first && first.kind === "paragraph") return [{
225
+ kind: "paragraph",
226
+ paragraph: {
227
+ ...first.paragraph,
228
+ properties: {
229
+ ...first.paragraph.properties,
230
+ ...indent
231
+ },
232
+ runs: [labelRun, ...first.paragraph.runs],
233
+ bookmarks: [bm, ...first.paragraph.bookmarks ?? []]
234
+ }
235
+ }, ...comment.content.slice(1)];
236
+ return [{
237
+ kind: "paragraph",
238
+ paragraph: {
239
+ properties: { ...indent },
240
+ runs: [labelRun],
241
+ bookmarks: [bm]
242
+ }
243
+ }, ...comment.content];
244
+ }
172
245
  function layoutStyledDocument(body, options) {
173
246
  const sectionList = resolveSectionList(body, options);
174
- const noteAssigned = assignNoteNumbers(applyNumbering(body, options.numbering));
247
+ const noteAssigned = assignNoteNumbers(applyNumbering(body, options.numbering), options.comments ? new Set(options.comments.keys()) : void 0);
175
248
  const numberedBody = noteAssigned.body;
176
249
  const numberedHeadersFooters = applyNumberingToHeadersFooters(options.headersFooters, options.numbering);
177
250
  const pdfaProfile = options.pdfA ? parsePdfAProfile(options.pdfA) : void 0;
@@ -229,8 +302,35 @@ function layoutStyledDocument(body, options) {
229
302
  });
230
303
  }
231
304
  for (const note of tailNotes.sort((a, b) => a.n - b.n)) for (const el of substituteNoteNumber(note.content, note.n)) blocks.push(layoutBodyElement(el, options, fontResources, imageResources, lastCtx.contentWidth, lastCtx.pageContentHeight));
305
+ const commentNums = noteAssigned.comments;
306
+ const threadDepth = (id) => {
307
+ let depth = 0;
308
+ const seen = new Set([id]);
309
+ let cur = options.comments?.get(id)?.parentId;
310
+ while (cur !== void 0 && commentNums.has(cur) && !seen.has(cur)) {
311
+ depth++;
312
+ seen.add(cur);
313
+ cur = options.comments?.get(cur)?.parentId;
314
+ }
315
+ return depth;
316
+ };
317
+ const commentTail = [...commentNums].map(([id, n]) => ({
318
+ id,
319
+ comment: options.comments?.get(id),
320
+ n
321
+ })).filter((e) => e.comment !== void 0).sort((a, b) => a.n - b.n);
322
+ for (const { id, comment, n } of commentTail) {
323
+ const parentId = comment.parentId;
324
+ const parentN = parentId !== void 0 && commentNums.has(parentId) ? commentNums.get(parentId) : void 0;
325
+ for (const el of commentTailBlocks(comment, n, {
326
+ ...parentN !== void 0 ? { parentN } : {},
327
+ depth: threadDepth(id),
328
+ ...comment.done ? { done: true } : {}
329
+ })) blocks.push(layoutBodyElement(el, options, fontResources, imageResources, lastCtx.contentWidth, lastCtx.pageContentHeight));
330
+ }
232
331
  }
233
332
  const bookmarks = /* @__PURE__ */ new Map();
333
+ const commentNotes = options.commentAnnotations && options.comments ? buildCommentNotes(noteAssigned.comments, options.comments) : void 0;
234
334
  const reflowParagraph = (paragraph, width, widths) => layoutParagraphBlock(paragraph, options, fontResources, imageResources, width, widths);
235
335
  return {
236
336
  pages: paginateSections(blocks, sectionCtxs, structBuilder, options.language ?? "en-US", notePlan, bookmarks, reflowParagraph),
@@ -242,7 +342,8 @@ function layoutStyledDocument(body, options) {
242
342
  sectionCtxs,
243
343
  pdfaProfile,
244
344
  tagged,
245
- bookmarks
345
+ bookmarks,
346
+ ...commentNotes ? { commentNotes } : {}
246
347
  }
247
348
  };
248
349
  }
@@ -567,6 +668,24 @@ function rectAtPath(x, y, w, h) {
567
668
  var CF_ICON_SIZE_PT = 9;
568
669
  var CF_ICON_GUTTER_PT = 12;
569
670
  var CF_ICON_EMPTY_HEX = "BFBFBF";
671
+ var DROPDOWN_BUTTON_PT = 11;
672
+ var DROPDOWN_GUTTER_PT = 13;
673
+ var DROPDOWN_FILL_HEX = "F0F0F0";
674
+ var DROPDOWN_BORDER_HEX = "B0B0B0";
675
+ var DROPDOWN_ARROW_HEX = "595959";
676
+ function buildDropdownPrims(s) {
677
+ return [{
678
+ paths: [rectAtPath(0, 0, s, s)],
679
+ fillColorHex: DROPDOWN_FILL_HEX,
680
+ stroke: {
681
+ colorHex: DROPDOWN_BORDER_HEX,
682
+ widthPt: .5
683
+ }
684
+ }, {
685
+ paths: [trianglePath([s / 2, s * .34], [s * .26, s * .64], [s * .74, s * .64])],
686
+ fillColorHex: DROPDOWN_ARROW_HEX
687
+ }];
688
+ }
570
689
  function buildCellIconShape(icon, s) {
571
690
  const color = icon.colorHex;
572
691
  switch (icon.shape) {
@@ -756,6 +875,7 @@ function collectImageResources(body, headersFooters, options) {
756
875
  for (const hf of headersFooters.values()) visit(hf);
757
876
  for (const note of options.footnotes?.values() ?? []) visit(note);
758
877
  for (const note of options.endnotes?.values() ?? []) visit(note);
878
+ for (const comment of options.comments?.values() ?? []) visit(comment.content);
759
879
  const flattenAlpha = options.pdfA ? parsePdfAProfile(options.pdfA).part === 1 : false;
760
880
  let counter = 0;
761
881
  for (const resourceId of seen) {
@@ -916,6 +1036,7 @@ function collectFontResources(body, headersFooters, options) {
916
1036
  for (const hf of headersFooters.values()) visit(hf);
917
1037
  for (const note of options.footnotes?.values() ?? []) visit(note);
918
1038
  for (const note of options.endnotes?.values() ?? []) visit(note);
1039
+ for (const comment of options.comments?.values() ?? []) visit(comment.content);
919
1040
  if (used.size === 0) {
920
1041
  const regular = options.registry.resolveByStyle(false, false);
921
1042
  used.set(regular.variant, {
@@ -1086,6 +1207,7 @@ function tokenizePlansLtr(plans) {
1086
1207
  });
1087
1208
  continue;
1088
1209
  }
1210
+ const highlight = (plan.run.commentRangeRefs?.length ?? 0) > 0;
1089
1211
  for (const t of tokenizeText(plan.run.text)) tokens.push({
1090
1212
  kind: "text",
1091
1213
  text: t.text,
@@ -1094,6 +1216,7 @@ function tokenizePlansLtr(plans) {
1094
1216
  ...plan.run.footnoteRef !== void 0 ? { footnoteRef: plan.run.footnoteRef } : {},
1095
1217
  ...plan.run.anchor !== void 0 ? { anchor: plan.run.anchor } : {},
1096
1218
  ...plan.run.listMarker ? { listMarker: true } : {},
1219
+ ...highlight ? { highlight: true } : {},
1097
1220
  resolvedRun: plan.resolvedRun,
1098
1221
  font: plan.font,
1099
1222
  fontSizePt: plan.fontSizePt,
@@ -1148,6 +1271,7 @@ function tokenizePlansBidi(plans, realLevels) {
1148
1271
  ...plan.run.footnoteRef !== void 0 ? { footnoteRef: plan.run.footnoteRef } : {},
1149
1272
  ...plan.run.anchor !== void 0 ? { anchor: plan.run.anchor } : {},
1150
1273
  ...plan.run.listMarker ? { listMarker: true } : {},
1274
+ ...(plan.run.commentRangeRefs?.length ?? 0) > 0 ? { highlight: true } : {},
1151
1275
  resolvedRun: plan.resolvedRun,
1152
1276
  font: plan.font,
1153
1277
  fontSizePt: plan.fontSizePt,
@@ -1565,8 +1689,9 @@ function layoutTableCell(cell, tableProps, widthPt, options, fontResources, imag
1565
1689
  const padTopPt = cellMar?.top ?? tableMar?.top ?? 0;
1566
1690
  const padBottomPt = cellMar?.bottom ?? tableMar?.bottom ?? 0;
1567
1691
  const padLeftBase = cellMar?.left ?? tableMar?.left ?? DEFAULT_CELL_PADDING_TWIPS * TWIP_TO_PT;
1568
- const padRightPt = cellMar?.right ?? tableMar?.right ?? DEFAULT_CELL_PADDING_TWIPS * TWIP_TO_PT;
1692
+ const padRightBase = cellMar?.right ?? tableMar?.right ?? DEFAULT_CELL_PADDING_TWIPS * TWIP_TO_PT;
1569
1693
  const padLeftPt = padLeftBase + (cell.properties.icon ? CF_ICON_GUTTER_PT : 0);
1694
+ const padRightPt = padRightBase + (cell.properties.dropdown ? DROPDOWN_GUTTER_PT : 0);
1570
1695
  const innerWidth = Math.max(1, widthPt - padLeftPt - padRightPt);
1571
1696
  const lines = [];
1572
1697
  const nestedTables = [];
@@ -1599,6 +1724,7 @@ function layoutTableCell(cell, tableProps, widthPt, options, fontResources, imag
1599
1724
  ...cell.properties.dataBar ? { dataBar: cell.properties.dataBar } : {},
1600
1725
  ...cell.properties.icon ? { icon: cell.properties.icon } : {},
1601
1726
  ...cell.properties.sparkline ? { sparkline: cell.properties.sparkline } : {},
1727
+ ...cell.properties.dropdown ? { dropdown: true } : {},
1602
1728
  lines,
1603
1729
  ...nestedTables.length > 0 ? { nestedTables } : {},
1604
1730
  contentHeightPt,
@@ -2256,6 +2382,28 @@ function emitRowChunk(out, row, marginLeft, cursorY, pageHeight, colCount, cellS
2256
2382
  });
2257
2383
  }
2258
2384
  }
2385
+ if (cell.dropdown && cell.mergeRole !== "middle" && cell.mergeRole !== "end") {
2386
+ const btn = Math.min(DROPDOWN_BUTTON_PT, Math.max(0, row.heightPt - 2));
2387
+ if (btn > 1) {
2388
+ const btnTransform = flipTransform([
2389
+ 1,
2390
+ 0,
2391
+ 0,
2392
+ 1,
2393
+ cellX + cell.widthPt - btn - 1.5,
2394
+ rowBottom + (row.heightPt - btn) / 2
2395
+ ], pageHeight);
2396
+ for (const prim of buildDropdownPrims(btn)) out.push({
2397
+ type: "shape",
2398
+ shape: {
2399
+ paths: prim.paths,
2400
+ ...prim.fillColorHex ? { fillColorHex: prim.fillColorHex } : {},
2401
+ ...prim.stroke ? { stroke: prim.stroke } : {},
2402
+ transform: btnTransform
2403
+ }
2404
+ });
2405
+ }
2406
+ }
2259
2407
  if (cell.sparkline && cell.sparkline.values.length > 0 && cell.mergeRole !== "middle" && cell.mergeRole !== "end") {
2260
2408
  const inset = 1.5;
2261
2409
  const sw = cell.widthPt - 2 * inset;
@@ -33,6 +33,7 @@ async function emitStyledPdfEncrypted(laid, options, doc, encrypt) {
33
33
  function assembleStyledPdf(laid, options, doc) {
34
34
  const { pages: renderedPages } = laid;
35
35
  const { structBuilder, sectionCtxs, pdfaProfile, tagged } = laid.pdf;
36
+ const commentNotes = !pdfaProfile && !structBuilder ? laid.pdf.commentNotes : void 0;
36
37
  const docInfo = options.pdfUA && !options.info?.title ? {
37
38
  ...options.info,
38
39
  title: "Untitled document"
@@ -158,6 +159,24 @@ function assembleStyledPdf(laid, options, doc) {
158
159
  annotParentCount++;
159
160
  }
160
161
  annots.push(ref(annotRef.id));
162
+ const note = l.anchor !== void 0 ? commentNotes?.get(l.anchor) : void 0;
163
+ if (note) {
164
+ const textAnnot = doc.add(dict({
165
+ Type: name("Annot"),
166
+ Subtype: name("Text"),
167
+ Name: name("Comment"),
168
+ Rect: [
169
+ l.rect[0],
170
+ l.rect[1],
171
+ l.rect[2],
172
+ l.rect[3]
173
+ ],
174
+ F: 28,
175
+ Contents: note.contents,
176
+ ...note.author !== void 0 ? { T: note.author } : {}
177
+ }));
178
+ annots.push(ref(textAnnot.id));
179
+ }
161
180
  }
162
181
  if (annots.length > 0) {
163
182
  pageEntries["Annots"] = annots;
@@ -624,6 +643,25 @@ function emitPageContent(page, tagging, gradientNames) {
624
643
  const text = tok.bidiLevel % 2 === 1 ? reverseByCodePoint(tok.text) : tok.text;
625
644
  return tok.font.measure.encodeTextAsCidHex(text);
626
645
  };
646
+ if (line.tokens.some((t) => t.kind === "text" && t.highlight === true)) {
647
+ if (inBT) {
648
+ out.push("ET");
649
+ inBT = false;
650
+ }
651
+ const hlAscent = Math.max(lineFs, line.mathAscentPt ?? 0);
652
+ const hlDescent = Math.max(lineFs * .2, line.mathDescentPt ?? 0);
653
+ const [hr, hg, hb] = hexToRgb01("fff3a3");
654
+ out.push("q");
655
+ out.push(`${formatNumber(hr)} ${formatNumber(hg)} ${formatNumber(hb)} rg`);
656
+ let hx = originX;
657
+ for (const tok of line.tokens) {
658
+ const w = tok.widthPt + (tok.kind === "text" && tok.isSpace ? extraPerSpace : 0);
659
+ if (tok.kind === "text" && tok.highlight === true) out.push(`${formatNumber(hx)} ${formatNumber(baselineY - hlDescent)} ${formatNumber(w)} ${formatNumber(hlAscent + hlDescent)} re`);
660
+ hx += w;
661
+ }
662
+ out.push("f");
663
+ out.push("Q");
664
+ }
627
665
  if (extraPerSpace > 0 || hasImageToken || hasMathToken || hasRtl) {
628
666
  const order = hasRtl ? lineVisualOrder(line) : line.tokens.map((_, i) => i);
629
667
  let x = originX;
@@ -1,4 +1,6 @@
1
1
  import { DocumentReader, ReadResult } from '../core/ir/adapters.js';
2
2
  import { FlowDoc } from '../core/ir/flow.js';
3
+ import { PoNode } from '../core/po-helpers.js';
3
4
  export declare function readPptx(bytes: Uint8Array): ReadResult<FlowDoc>;
5
+ export declare function parseXml(data: Uint8Array): Array<PoNode>;
4
6
  export declare const pptxReader: DocumentReader<FlowDoc>;
@@ -3,8 +3,8 @@ import { ResourceStore } from "../core/ir/resources.js";
3
3
  import { FEATURES } from "../core/ir/features.js";
4
4
  import { EMPTY_STYLE_SHEET, resolveBodyStyles } from "../core/style-cascade/resolver.js";
5
5
  import "../core/style-cascade/index.js";
6
+ import { poAttr, poChildren, poFindDescendant, poIntAttr, poIs } from "../core/po-helpers.js";
6
7
  import { bytesInclude } from "../core/bytes.js";
7
- import { poAttr, poChildren, poIntAttr, poIs } from "../core/po-helpers.js";
8
8
  import { DEFAULT_THEME_PALETTE, defaultColorResolver, makeColorResolver } from "../core/drawingml/colors.js";
9
9
  import { parseChart, withChartColorStyle } from "../core/drawingml/chart-parser.js";
10
10
  import { parseTheme } from "../core/drawingml/theme-parser.js";
@@ -81,7 +81,12 @@ function readPptx(bytes) {
81
81
  colors: styles.colors,
82
82
  resolveImage: makeSlideImageResolver(pkg, part.path, resources),
83
83
  resolveChart: makeSlideChartResolver(pkg, part.path, charts, styles.colors),
84
- resolveHyperlink: makeHyperlinkResolver(pkg, part.path)
84
+ resolveHyperlink: makeHyperlinkResolver(pkg, part.path),
85
+ resolveDiagram: makeSlideDiagramResolver(pkg, part.path),
86
+ onLoss: (loss) => losses.push(loss.where ? loss : {
87
+ ...loss,
88
+ where: `slide ${i + 1}`
89
+ })
85
90
  };
86
91
  body.push(...parseSlide(part.data, ctx, styles.background, pageW, pageH));
87
92
  }
@@ -167,6 +172,28 @@ function makeSlideChartResolver(pkg, slidePath, charts, colors) {
167
172
  return key;
168
173
  };
169
174
  }
175
+ function makeSlideDiagramResolver(pkg, slidePath) {
176
+ const cache = /* @__PURE__ */ new Map();
177
+ return (relId) => {
178
+ if (cache.has(relId)) return cache.get(relId);
179
+ let spTree;
180
+ const dataRel = pkg.getPartRelationships(slidePath).find((r) => r.id === relId);
181
+ const data = dataRel ? pkg.resolveRelatedPart(slidePath, dataRel) : void 0;
182
+ if (data) {
183
+ const drawRel = pkg.getPartRelationships(data.path).find((r) => r.type.endsWith("/diagramDrawing"));
184
+ const draw = drawRel ? pkg.resolveRelatedPart(data.path, drawRel) : void 0;
185
+ if (draw) for (const root of parseXml(draw.data)) {
186
+ const found = poFindDescendant(root, "dsp:spTree");
187
+ if (found) {
188
+ spTree = found;
189
+ break;
190
+ }
191
+ }
192
+ }
193
+ cache.set(relId, spTree);
194
+ return spTree;
195
+ };
196
+ }
170
197
  function slideStylesFor(pkg, slidePath, cache) {
171
198
  const layoutRel = pkg.getPartRelationships(slidePath).find((r) => r.type.endsWith("/slideLayout"));
172
199
  const layout = layoutRel ? pkg.resolveRelatedPart(slidePath, layoutRel) : void 0;
@@ -210,4 +237,4 @@ var pptxReader = {
210
237
  read: (bytes) => readPptx(bytes)
211
238
  };
212
239
  //#endregion
213
- export { pptxReader };
240
+ export { parseXml, pptxReader };
@@ -1,6 +1,6 @@
1
- import { BodyElement, ShapeFill } from '../core/document-model/index.js';
1
+ import { BodyElement, FloatAnchor, ShapeBlock, ShapeFill } from '../core/document-model/index.js';
2
2
  import { ColorResolver } from '../core/drawingml/colors.js';
3
- import { Pt, ResourceId } from '../core/ir/index.js';
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
6
  import { ShapeBoxEmu } from './sp-helpers.js';
@@ -10,9 +10,15 @@ export interface SlideContext {
10
10
  readonly resolveChart?: (relId: string) => string | undefined;
11
11
  readonly colors?: ColorResolver;
12
12
  readonly resolveHyperlink?: (relId: string) => string | undefined;
13
+ readonly resolveDiagram?: (relId: string) => PoNode | undefined;
14
+ readonly onLoss?: (loss: Loss) => void;
13
15
  }
16
+ type LinkResolver = ((relId: string) => string | undefined) | undefined;
14
17
  type GroupTransform = (box: ShapeBoxEmu) => ShapeBoxEmu;
15
18
  export declare function parseSlideShapes(container: PoNode, ctx?: SlideContext, transform?: GroupTransform): Array<BodyElement>;
19
+ export declare function diagramTransform(spTree: PoNode, frame: ShapeBoxEmu): GroupTransform;
20
+ export declare function noDiagramOverrideLoss(where?: string): Loss;
21
+ export declare function parseDiagramDrawing(spTree: PoNode, transform: GroupTransform, makeFloat: (box: ShapeBoxEmu) => FloatAnchor, colors: ColorResolver, resolveLink: LinkResolver): Array<ShapeBlock>;
16
22
  export declare function parseBackgroundFill(bg: PoNode, colors: ColorResolver): ShapeFill | undefined;
17
23
  export declare function backdropElement(fill: ShapeFill, widthPt: Pt, heightPt: Pt): BodyElement;
18
24
  export {};
@@ -1,11 +1,13 @@
1
1
  import { emuToPt } from "../core/ir/units.js";
2
+ import { FEATURES } from "../core/ir/features.js";
2
3
  import { poAttr, poChildren, poFindDescendant, poIntAttr, poIs, poText } from "../core/po-helpers.js";
3
4
  import { defaultColorResolver, resolveColorNode } from "../core/drawingml/colors.js";
4
- import { parseCustGeom, parseFill, parseLine, parsePrstGeom } from "../word/drawing-parser.js";
5
5
  import { boxFromXfrm, parsePh, parseXfrmBox, rPrToRunProps } from "./sp-helpers.js";
6
+ import { parseCustGeom, parseFill, parseLine, parsePrstGeom } from "../word/drawing-parser.js";
6
7
  //#region src/pptx/slide-parser.ts
7
8
  var CHART_URI = "http://schemas.openxmlformats.org/drawingml/2006/chart";
8
9
  var TABLE_URI = "http://schemas.openxmlformats.org/drawingml/2006/table";
10
+ var DIAGRAM_URI = "http://schemas.openxmlformats.org/drawingml/2006/diagram";
9
11
  var ALGN_TO_ALIGNMENT = {
10
12
  l: "left",
11
13
  ctr: "center",
@@ -33,10 +35,8 @@ function parseSlideShapes(container, ctx = {}, transform = IDENTITY_TRANSFORM) {
33
35
  kind: "image",
34
36
  image
35
37
  });
36
- } else if (poIs(child, "p:graphicFrame")) {
37
- const el = parseGraphicFrame(child, ctx, transform);
38
- if (el) out.push(el);
39
- } else if (poIs(child, "p:grpSp")) out.push(...parseSlideShapes(child, ctx, composeGroupTransform(child, transform)));
38
+ } else if (poIs(child, "p:graphicFrame")) out.push(...parseGraphicFrame(child, ctx, transform));
39
+ else if (poIs(child, "p:grpSp")) out.push(...parseSlideShapes(child, ctx, composeGroupTransform(child, transform)));
40
40
  return out;
41
41
  }
42
42
  function composeGroupTransform(grpSp, parent) {
@@ -106,7 +106,7 @@ function parseSp(sp, ctx, transform) {
106
106
  }
107
107
  function parseGraphicFrame(gf, ctx, transform) {
108
108
  const own = boxFromXfrm(poChildren(gf).find((c) => poIs(c, "p:xfrm")));
109
- if (!own) return void 0;
109
+ if (!own) return [];
110
110
  const box = transform(own);
111
111
  const graphicData = poFindDescendant(gf, "a:graphicData");
112
112
  const uri = graphicData ? poAttr(graphicData, "uri") : void 0;
@@ -114,8 +114,8 @@ function parseGraphicFrame(gf, ctx, transform) {
114
114
  const cChart = poFindDescendant(gf, "c:chart");
115
115
  const relId = cChart ? poAttr(cChart, "id") : void 0;
116
116
  const key = relId !== void 0 ? ctx.resolveChart?.(relId) : void 0;
117
- if (key === void 0) return void 0;
118
- return {
117
+ if (key === void 0) return [];
118
+ return [{
119
119
  kind: "chart",
120
120
  chart: {
121
121
  float: floatAt(box),
@@ -124,15 +124,83 @@ function parseGraphicFrame(gf, ctx, transform) {
124
124
  height: emuToPt(box.cy),
125
125
  paragraphProperties: {}
126
126
  }
127
- };
127
+ }];
128
128
  }
129
129
  if (uri === TABLE_URI) {
130
130
  const tbl = poFindDescendant(gf, "a:tbl");
131
- return tbl ? {
131
+ return tbl ? [{
132
132
  kind: "table",
133
133
  table: parseTable(tbl, ctx.colors ?? defaultColorResolver)
134
- } : void 0;
134
+ }] : [];
135
135
  }
136
+ if (uri === DIAGRAM_URI) {
137
+ const relIds = poFindDescendant(gf, "dgm:relIds");
138
+ const dmRelId = relIds ? poAttr(relIds, "dm") : void 0;
139
+ const spTree = dmRelId !== void 0 ? ctx.resolveDiagram?.(dmRelId) : void 0;
140
+ if (!spTree) {
141
+ if (dmRelId !== void 0) ctx.onLoss?.(noDiagramOverrideLoss());
142
+ return [];
143
+ }
144
+ return parseDiagramDrawing(spTree, diagramTransform(spTree, box), floatAt, ctx.colors ?? defaultColorResolver, ctx.resolveHyperlink).map((shape) => ({
145
+ kind: "shape",
146
+ shape
147
+ }));
148
+ }
149
+ return [];
150
+ }
151
+ function diagramTransform(spTree, frame) {
152
+ const grpSpPr = poChildren(spTree).find((c) => poIs(c, "dsp:grpSpPr"));
153
+ const xfrm = grpSpPr ? poChildren(grpSpPr).find((c) => poIs(c, "a:xfrm")) : void 0;
154
+ const chOff = xfrm ? poChildren(xfrm).find((c) => poIs(c, "a:chOff")) : void 0;
155
+ const chExt = xfrm ? poChildren(xfrm).find((c) => poIs(c, "a:chExt")) : void 0;
156
+ const chExtCx = chExt ? poIntAttr(chExt, "cx") : void 0;
157
+ const chExtCy = chExt ? poIntAttr(chExt, "cy") : void 0;
158
+ const chOffX = (chOff ? poIntAttr(chOff, "x") : void 0) ?? 0;
159
+ const chOffY = (chOff ? poIntAttr(chOff, "y") : void 0) ?? 0;
160
+ const sx = chExtCx && chExtCx > 0 ? frame.cx / chExtCx : 1;
161
+ const sy = chExtCy && chExtCy > 0 ? frame.cy / chExtCy : 1;
162
+ return (b) => ({
163
+ x: frame.x + (b.x - chOffX) * sx,
164
+ y: frame.y + (b.y - chOffY) * sy,
165
+ cx: b.cx * sx,
166
+ cy: b.cy * sy
167
+ });
168
+ }
169
+ function noDiagramOverrideLoss(where) {
170
+ return {
171
+ severity: "dropped",
172
+ feature: FEATURES.smartArt,
173
+ detail: "SmartArt diagram has no pre-rendered drawing override; its layout is not reconstructed",
174
+ ...where ? { where } : {}
175
+ };
176
+ }
177
+ function parseDiagramDrawing(spTree, transform, makeFloat, colors, resolveLink) {
178
+ const out = [];
179
+ for (const sp of poChildren(spTree)) {
180
+ if (!poIs(sp, "dsp:sp")) continue;
181
+ const spPr = poChildren(sp).find((c) => poIs(c, "dsp:spPr"));
182
+ const own = parseXfrmBox(spPr);
183
+ if (!own) continue;
184
+ const box = transform(own);
185
+ const txBody = poChildren(sp).find((c) => poIs(c, "dsp:txBody"));
186
+ const text = txBody ? parseTxBody(txBody, void 0, void 0, colors, resolveLink) : void 0;
187
+ const geometry = parseGeometry(spPr);
188
+ const fill = spPr ? parseFill(spPr, colors) : { kind: "none" };
189
+ const line = spPr ? parseLine(spPr, colors) : void 0;
190
+ const visibleLine = line !== void 0 && line.fill !== "none";
191
+ if (!text && fill.kind === "none" && !visibleLine) continue;
192
+ out.push({
193
+ float: makeFloat(box),
194
+ width: emuToPt(box.cx),
195
+ height: emuToPt(box.cy),
196
+ geometry,
197
+ fill,
198
+ ...line ? { line } : {},
199
+ ...text ? { text } : {},
200
+ paragraphProperties: {}
201
+ });
202
+ }
203
+ return out;
136
204
  }
137
205
  function parseBackgroundFill(bg, colors) {
138
206
  const bgPr = poChildren(bg).find((c) => poIs(c, "p:bgPr"));
@@ -356,4 +424,4 @@ function cellFillHex(tcPr, colors) {
356
424
  }
357
425
  }
358
426
  //#endregion
359
- export { backdropElement, parseBackgroundFill, parseSlideShapes };
427
+ export { backdropElement, diagramTransform, noDiagramOverrideLoss, parseBackgroundFill, parseDiagramDrawing, parseSlideShapes };
@@ -1,6 +1,6 @@
1
- import { BodyElement, Section, SectionProperties } from '../core/document-model/index.js';
1
+ import { BodyElement, Comment, Section, SectionProperties } from '../core/document-model/index.js';
2
2
  import { ColorResolver } from '../core/drawingml/colors.js';
3
- import { ResourceId } from '../core/ir/index.js';
3
+ import { Loss, ResourceId } from '../core/ir/index.js';
4
4
  import { PoNode } from '../core/po-helpers.js';
5
5
  export type ImageResolver = (relId: string) => ResourceId | undefined;
6
6
  export type HyperlinkResolver = (relId: string) => string | undefined;
@@ -8,6 +8,9 @@ export interface ParseContext {
8
8
  readonly resolveColor: ColorResolver;
9
9
  readonly resolveImage?: ImageResolver;
10
10
  readonly resolveHyperlink?: HyperlinkResolver;
11
+ readonly resolveDiagram?: (relId: string) => PoNode | undefined;
12
+ readonly onLoss?: (loss: Loss) => void;
13
+ readonly openCommentRanges?: Set<string>;
11
14
  }
12
15
  export declare const DEFAULT_PARSE_CONTEXT: ParseContext;
13
16
  export declare function parseDocument(documentXml: Uint8Array, ctx?: ParseContext): Array<BodyElement>;
@@ -17,3 +20,15 @@ export declare function parseSections(documentXml: Uint8Array): Array<Section>;
17
20
  export declare function parseHeaderFooter(xml: Uint8Array, ctx?: ParseContext): Array<BodyElement>;
18
21
  export declare function parseBodyElements(children: ReadonlyArray<PoNode>, ctx?: ParseContext): Array<BodyElement>;
19
22
  export declare function parseNotes(notesXml: Uint8Array, rootTag: 'w:footnotes' | 'w:endnotes', noteTag: 'w:footnote' | 'w:endnote', ctx?: ParseContext): Map<string, Array<BodyElement>>;
23
+ export declare function parseComments(commentsXml: Uint8Array, ctx?: ParseContext): Map<string, Comment>;
24
+ /** A commentsExtended entry, keyed by the comment's last-paragraph w14:paraId. */
25
+ export interface CommentExtension {
26
+ /** The parent comment's paraId — present when this comment is a reply. */
27
+ readonly paraIdParent?: string;
28
+ /** w15:done — the thread is resolved. */
29
+ readonly done: boolean;
30
+ }
31
+ export declare function parseCommentsExtended(xml: Uint8Array): Map<string, CommentExtension>;
32
+ export declare function parseCommentThreads(commentsXml: Uint8Array, commentsExtendedXml: Uint8Array | undefined, ctx?: ParseContext): Map<string, Comment>;
33
+ export declare function parsePeople(xml: Uint8Array): Map<string, string>;
34
+ export declare function applyAuthorIds(comments: Map<string, Comment>, people: Map<string, string>): Map<string, Comment>;