reamkit 1.22.0 → 1.24.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 (101) hide show
  1. package/dist/esm/core/bmp.d.ts +33 -0
  2. package/dist/esm/core/bmp.js +276 -0
  3. package/dist/esm/core/converter/ream.d.ts +16 -0
  4. package/dist/esm/core/converter/ream.js +59 -9
  5. package/dist/esm/core/document-model/types.d.ts +55 -0
  6. package/dist/esm/core/drawingml/chart-geometry.js +14 -7
  7. package/dist/esm/core/drawingml/colors.d.ts +4 -2
  8. package/dist/esm/core/drawingml/colors.js +15 -0
  9. package/dist/esm/core/drawingml/diagram/colors.d.ts +52 -0
  10. package/dist/esm/core/drawingml/diagram/colors.js +125 -0
  11. package/dist/esm/core/drawingml/diagram/data-model.d.ts +47 -0
  12. package/dist/esm/core/drawingml/diagram/data-model.js +119 -0
  13. package/dist/esm/core/drawingml/diagram/layout-engine.d.ts +46 -0
  14. package/dist/esm/core/drawingml/diagram/layout-engine.js +948 -0
  15. package/dist/esm/core/drawingml/diagram/run.d.ts +37 -0
  16. package/dist/esm/core/drawingml/diagram/run.js +50 -0
  17. package/dist/esm/core/drawingml/diagram/to-drawing.d.ts +16 -0
  18. package/dist/esm/core/drawingml/diagram/to-drawing.js +86 -0
  19. package/dist/esm/core/drawingml/preset-geometry.js +116 -6
  20. package/dist/esm/core/drawingml/text-warp.d.ts +58 -0
  21. package/dist/esm/core/drawingml/text-warp.js +355 -0
  22. package/dist/esm/core/drawingml/theme-parser.d.ts +38 -0
  23. package/dist/esm/core/drawingml/theme-parser.js +73 -4
  24. package/dist/esm/core/font/measure.d.ts +12 -0
  25. package/dist/esm/core/font/measure.js +36 -0
  26. package/dist/esm/core/fonts/families.d.ts +10 -0
  27. package/dist/esm/core/fonts/families.js +44 -0
  28. package/dist/esm/core/fonts/index.d.ts +3 -2
  29. package/dist/esm/core/fonts/remote-fonts.d.ts +50 -0
  30. package/dist/esm/core/fonts/remote-fonts.js +114 -8
  31. package/dist/esm/core/fonts/scripts.d.ts +26 -0
  32. package/dist/esm/core/fonts/scripts.js +177 -0
  33. package/dist/esm/core/images.d.ts +30 -2
  34. package/dist/esm/core/images.js +121 -5
  35. package/dist/esm/core/metafile/blit.d.ts +33 -0
  36. package/dist/esm/core/metafile/blit.js +67 -0
  37. package/dist/esm/core/metafile/dib.d.ts +61 -0
  38. package/dist/esm/core/metafile/dib.js +205 -0
  39. package/dist/esm/core/metafile/emf.js +280 -38
  40. package/dist/esm/core/metafile/picture.d.ts +61 -1
  41. package/dist/esm/core/metafile/picture.js +70 -1
  42. package/dist/esm/core/metafile/wmf.js +202 -13
  43. package/dist/esm/core/ole/escher-blip.d.ts +12 -0
  44. package/dist/esm/core/ole/escher-blip.js +98 -0
  45. package/dist/esm/{pdf-reader → core}/png-encode.js +1 -1
  46. package/dist/esm/excel/print-model.js +5 -3
  47. package/dist/esm/excel/sheet-drawing.js +1 -1
  48. package/dist/esm/excel/sheet-to-flow.js +16 -2
  49. package/dist/esm/excel/styles-parser.js +1 -1
  50. package/dist/esm/excel/xls/biff-reader.js +53 -18
  51. package/dist/esm/excel/xls/biff-styles.d.ts +5 -2
  52. package/dist/esm/excel/xls/biff-styles.js +9 -7
  53. package/dist/esm/excel/xls/escher.js +90 -24
  54. package/dist/esm/html/html-writer.js +2 -1
  55. package/dist/esm/layout/page-doc.d.ts +74 -0
  56. package/dist/esm/layout/page-doc.js +14 -1
  57. package/dist/esm/layout/styled-layout.d.ts +2 -2
  58. package/dist/esm/layout/styled-layout.js +475 -48
  59. package/dist/esm/pdf/shading.js +8 -6
  60. package/dist/esm/pdf/styled-page-emitter.js +77 -5
  61. package/dist/esm/pdf/vector-graphics.js +1 -1
  62. package/dist/esm/pdf-reader/image-decode.js +1 -1
  63. package/dist/esm/pptx/embedded-fonts.d.ts +31 -0
  64. package/dist/esm/pptx/embedded-fonts.js +104 -0
  65. package/dist/esm/pptx/placeholder-cascade.d.ts +73 -6
  66. package/dist/esm/pptx/placeholder-cascade.js +114 -18
  67. package/dist/esm/pptx/ppt/ppt-reader.js +191 -13
  68. package/dist/esm/pptx/ppt/ppt-text.d.ts +107 -1
  69. package/dist/esm/pptx/ppt/ppt-text.js +873 -144
  70. package/dist/esm/pptx/pptx-reader.d.ts +8 -0
  71. package/dist/esm/pptx/pptx-reader.js +117 -15
  72. package/dist/esm/pptx/preset-table-styles.d.ts +9 -0
  73. package/dist/esm/pptx/preset-table-styles.js +27 -0
  74. package/dist/esm/pptx/slide-parser.d.ts +11 -2
  75. package/dist/esm/pptx/slide-parser.js +203 -44
  76. package/dist/esm/pptx/sp-helpers.d.ts +9 -4
  77. package/dist/esm/pptx/sp-helpers.js +48 -9
  78. package/dist/esm/pptx/table-style.d.ts +16 -1
  79. package/dist/esm/pptx/table-style.js +104 -10
  80. package/dist/esm/svg/svg-writer.js +1 -0
  81. package/dist/esm/word/document-parser.d.ts +11 -2
  82. package/dist/esm/word/document-parser.js +10 -7
  83. package/dist/esm/word/docx-reader.js +32 -16
  84. package/dist/esm/word/docx-to-pdf.d.ts +8 -7
  85. package/dist/esm/word/docx-to-pdf.js +25 -7
  86. package/dist/esm/word/docx-writer.js +4 -0
  87. package/dist/esm/word/drawing-parser.d.ts +20 -0
  88. package/dist/esm/word/drawing-parser.js +67 -22
  89. package/dist/esm/word/numbering-parser.d.ts +2 -1
  90. package/dist/esm/word/numbering-parser.js +6 -6
  91. package/dist/esm/word/paragraph-properties.d.ts +2 -1
  92. package/dist/esm/word/paragraph-properties.js +2 -2
  93. package/dist/esm/word/run-properties.d.ts +5 -2
  94. package/dist/esm/word/run-properties.js +13 -8
  95. package/dist/esm/word/styles-parser.d.ts +2 -1
  96. package/dist/esm/word/styles-parser.js +12 -12
  97. package/dist/esm/word/table-parser.js +6 -3
  98. package/dist/esm/word/theme-fonts.d.ts +10 -0
  99. package/dist/esm/word/theme-fonts.js +28 -0
  100. package/package.json +1 -1
  101. /package/dist/esm/{pdf-reader → core}/png-encode.d.ts +0 -0
@@ -1,6 +1,6 @@
1
1
  import { pt } from "../core/ir/units.js";
2
2
  import { poAttr, poChildren, poIntAttr, poIs, poTag, poText } from "../core/po-helpers.js";
3
- import { readColorMods, resolveColorNode } from "../core/drawingml/colors.js";
3
+ import { placeholderColors, readColorMods, resolveColorNode } from "../core/drawingml/colors.js";
4
4
  //#region src/pptx/table-style.ts
5
5
  /** The `a:tblPr` flags, all off unless the table says otherwise. */
6
6
  function tableStyleFlags(tblPr) {
@@ -26,9 +26,10 @@ function tableStyleId(tblPr) {
26
26
  * @param flags Which conditional parts the table asks for.
27
27
  * @param at Where the cell sits.
28
28
  * @param colors The deck's colour resolver.
29
+ * @param theme The theme's style lists, for the parts that point at them.
29
30
  * @returns The composed part, empty when the style says nothing.
30
31
  */
31
- function cellStyle(style, flags, at, colors) {
32
+ function cellStyle(style, flags, at, colors, theme) {
32
33
  const names = ["a:wholeTbl"];
33
34
  if (flags.bandCol) names.push(bandName(at.col, flags.firstCol, "V"));
34
35
  if (flags.bandRow) names.push(bandName(at.row, flags.firstRow, "H"));
@@ -36,31 +37,108 @@ function cellStyle(style, flags, at, colors) {
36
37
  if (flags.lastCol && at.col === at.colCount - 1) names.push("a:lastCol");
37
38
  if (flags.firstRow && at.row === 0) names.push("a:firstRow");
38
39
  if (flags.lastRow && at.row === at.rowCount - 1) names.push("a:lastRow");
39
- let out = {};
40
+ let out = tableBackground(style, colors, theme);
40
41
  for (const name of names) {
41
42
  const part = poChildren(style).find((c) => poIs(c, name));
42
- if (part) out = {
43
+ if (!part) continue;
44
+ const next = partStyle(part, colors, theme);
45
+ out = {
43
46
  ...out,
44
- ...partStyle(part, colors)
47
+ ...next,
48
+ ...overFill(out.shadingHex, next)
45
49
  };
46
50
  }
47
- return out;
51
+ const { shadingAlpha: _drop, ...flat } = out;
52
+ return flat;
53
+ }
54
+ /**
55
+ * A part's fill laid OVER what is already under it.
56
+ *
57
+ * A colour resolves flattened against the page, because that is what a shape's
58
+ * fill sits on; a table cell's sits on the table's background. Undoing the one
59
+ * and redoing the other needs only the alpha: bnc480256's banding is `accent1`
60
+ * at 40% over a background the theme's gradient makes pale, and taken against
61
+ * white instead it came out as the flat accent with every second row white.
62
+ */
63
+ function overFill(under, part) {
64
+ const a = part.shadingAlpha;
65
+ if (a === void 0 || a >= 1 || part.shadingHex === void 0 || under === void 0) return {};
66
+ const flat = rgb(part.shadingHex);
67
+ const back = rgb(under);
68
+ if (!flat || !back) return {};
69
+ return { shadingHex: toHex(flat.map((v, i) => v - (1 - a) * (1 - (back[i] ?? 1)))) };
70
+ }
71
+ function alphaOf(fill) {
72
+ const walk = (node) => {
73
+ for (const child of poChildren(node)) {
74
+ if (poIs(child, "a:alpha")) {
75
+ const v = poIntAttr(child, "val");
76
+ if (v !== void 0) return Math.max(0, Math.min(1, v / 1e5));
77
+ }
78
+ const deeper = walk(child);
79
+ if (deeper !== void 0) return deeper;
80
+ }
81
+ };
82
+ return walk(fill) ?? 1;
83
+ }
84
+ function rgb(h) {
85
+ const m = /^([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})$/u.exec(h);
86
+ return m ? [
87
+ 1,
88
+ 2,
89
+ 3
90
+ ].map((i) => parseInt(m[i] ?? "0", 16) / 255) : void 0;
91
+ }
92
+ function toHex(v) {
93
+ return v.map((c) => Math.round(Math.max(0, Math.min(1, c)) * 255).toString(16).padStart(2, "0").toUpperCase()).join("");
94
+ }
95
+ function tableBackground(style, colors, theme) {
96
+ const bg = poChildren(style).find((c) => poIs(c, "a:tblBg"));
97
+ if (!bg) return {};
98
+ const solid = poChildren(bg).find((c) => poIs(c, "a:solidFill"));
99
+ const hex = (solid ? colorOf(solid, colors) : void 0) ?? refFillColor(poChildren(bg).find((c) => poIs(c, "a:fillRef")), colors, theme);
100
+ return hex ? { shadingHex: hex } : {};
101
+ }
102
+ /**
103
+ * §20.1.4.1.16 `a:fillRef` — a fill named by its place in the theme's list,
104
+ * with the colour to put wherever that fill says `phClr`.
105
+ *
106
+ * Only its COLOUR is taken: a slot may be a gradient or a picture, and a cell's
107
+ * shading is one colour. The first solid colour the slot names, or the
108
+ * reference's own, is the nearest true answer.
109
+ */
110
+ function refFillColor(ref, colors, theme) {
111
+ if (!ref) return void 0;
112
+ const own = colorOf(ref, colors);
113
+ const idx = poIntAttr(ref, "idx");
114
+ const slot = idx !== void 0 && idx > 0 ? theme?.fills?.[idx - 1] : void 0;
115
+ if (!slot) return own;
116
+ const named = own === void 0 ? colors : placeholderColors(colors, own);
117
+ const solid = poIs(slot, "a:solidFill") ? slot : void 0;
118
+ if (solid) return colorOf(solid, named) ?? own;
119
+ const grad = poIs(slot, "a:gradFill") ? slot : void 0;
120
+ const list = grad ? poChildren(grad).find((c) => poIs(c, "a:gsLst")) : void 0;
121
+ const stops = list ? poChildren(list).filter((c) => poIs(c, "a:gs")) : [];
122
+ const last = stops[stops.length - 1];
123
+ return (last ? colorOf(last, named) : void 0) ?? own;
48
124
  }
49
125
  function bandName(index, edge, axis) {
50
126
  return (edge ? index - 1 : index) % 2 === 0 ? `a:band1${axis}` : `a:band2${axis}`;
51
127
  }
52
- function partStyle(part, colors) {
128
+ function partStyle(part, colors, theme) {
53
129
  const tcStyle = poChildren(part).find((c) => poIs(c, "a:tcStyle"));
54
130
  const txStyle = poChildren(part).find((c) => poIs(c, "a:tcTxStyle"));
55
131
  const fill = tcStyle ? poChildren(tcStyle).find((c) => poIs(c, "a:fill")) : void 0;
56
132
  const solid = fill ? poChildren(fill).find((c) => poIs(c, "a:solidFill")) : void 0;
57
133
  const shadingHex = solid ? colorOf(solid, colors) : void 0;
134
+ const shadingAlpha = solid ? alphaOf(solid) : void 0;
58
135
  const bdr = tcStyle ? poChildren(tcStyle).find((c) => poIs(c, "a:tcBdr")) : void 0;
59
- const borders = bdr ? partBorders(bdr, colors) : void 0;
136
+ const borders = bdr ? partBorders(bdr, colors, theme) : void 0;
60
137
  const on = (name) => poAttr(txStyle, name) === "on";
61
138
  const colorHex = txStyle ? colorOf(txStyle, colors) : void 0;
62
139
  return {
63
140
  ...shadingHex ? { shadingHex } : {},
141
+ ...shadingAlpha !== void 0 && shadingAlpha < 1 ? { shadingAlpha } : {},
64
142
  ...borders ? { borders } : {},
65
143
  ...txStyle && on("b") ? { bold: true } : {},
66
144
  ...txStyle && on("i") ? { italic: true } : {},
@@ -75,15 +153,31 @@ var SIDES = [
75
153
  ["a:insideH", "insideH"],
76
154
  ["a:insideV", "insideV"]
77
155
  ];
78
- function partBorders(bdr, colors) {
156
+ function partBorders(bdr, colors, theme) {
79
157
  const out = {};
80
158
  for (const [tag, side] of SIDES) {
81
159
  const holder = poChildren(bdr).find((c) => poIs(c, tag));
82
160
  const ln = holder ? poChildren(holder).find((c) => poIs(c, "a:ln")) : void 0;
83
- if (ln) out[side] = lineBorder(ln, colors);
161
+ if (ln) {
162
+ out[side] = lineBorder(ln, colors);
163
+ continue;
164
+ }
165
+ const border = refBorder(holder ? poChildren(holder).find((c) => poIs(c, "a:lnRef")) : void 0, colors, theme);
166
+ if (border) out[side] = border;
84
167
  }
85
168
  return Object.keys(out).length > 0 ? out : void 0;
86
169
  }
170
+ function refBorder(ref, colors, theme) {
171
+ if (!ref || poAttr(ref, "idx") === "0") return void 0;
172
+ const colorHex = colorOf(ref, colors);
173
+ if (colorHex === void 0) return void 0;
174
+ const idx = poIntAttr(ref, "idx");
175
+ return {
176
+ style: "single",
177
+ colorHex,
178
+ width: pt((idx !== void 0 && idx > 0 ? theme?.lineWidths?.[idx - 1] : void 0) ?? .75)
179
+ };
180
+ }
87
181
  /**
88
182
  * §20.1.2.1 `a:ln` → the rule it draws on one side of a cell.
89
183
  *
@@ -88,6 +88,7 @@ function emitPage(out, page, laid, losses, idc) {
88
88
  }
89
89
  for (const sh of plan.shapes) emitShape(out, sh.shape, idc);
90
90
  for (const picture of plan.pictures) for (const item of picture) emitPageItem(out, item, laid, losses, idc);
91
+ for (const run of plan.ordered) for (const item of run) emitPageItem(out, item, laid, losses, idc);
91
92
  for (const t of plan.lines) emitTextLine(out, t, losses, idc);
92
93
  }
93
94
  /**
@@ -2,6 +2,7 @@ import { BodyElement, Comment, RunProperties, Section, SectionProperties, ShapeF
2
2
  import { ColorResolver } from '../core/drawingml/colors.js';
3
3
  import { Loss, ResourceId } from '../core/ir/index.js';
4
4
  import { PoNode } from '../core/po-helpers.js';
5
+ import { ThemeFonts } from '../core/drawingml/theme-parser.js';
5
6
  import { ThemeStyles } from './drawing-parser.js';
6
7
  /**
7
8
  * Resolves a drawing relationship id to a content-addressed `ResourceId` —
@@ -38,7 +39,10 @@ export interface ParseContext {
38
39
  * pre-rendered drawing override, or `undefined` when the file ships none
39
40
  * (E-SMARTART SA2).
40
41
  */
41
- readonly resolveDiagram?: (relId: string) => ResolvedDiagram | undefined;
42
+ readonly resolveDiagram?: (relId: string, frame: {
43
+ readonly cx: number;
44
+ readonly cy: number;
45
+ }) => ResolvedDiagram | undefined;
42
46
  /**
43
47
  * §21.2 a `c:chart` `@r:id` → the chart part's path, the key the reader files
44
48
  * parsed charts under. Relationship ids are scoped to their owning part, so a
@@ -57,6 +61,11 @@ export interface ParseContext {
57
61
  * the shadow a gallery-drawn shape wears.
58
62
  */
59
63
  readonly themeStyles?: ThemeStyles;
64
+ /**
65
+ * §20.1.4.1.16 — the theme's font scheme, for the typefaces a run names by
66
+ * slot (`w:asciiTheme="minorHAnsi"`) rather than by name.
67
+ */
68
+ readonly themeFonts?: ThemeFonts;
60
69
  /**
61
70
  * Sink for graceful-degradation notices (E-SMARTART SA3): a SmartArt with no
62
71
  * drawing override records a dropped-feature {@link Loss} rather than vanishing.
@@ -185,7 +194,7 @@ export declare function parseBodyElements(children: ReadonlyArray<PoNode>, ctx?:
185
194
  * @param sdt The `w:sdt` element.
186
195
  * @returns The properties, or `undefined` when the control states none.
187
196
  */
188
- export declare function sdtRunProperties(sdt: PoNode): RunProperties | undefined;
197
+ export declare function sdtRunProperties(sdt: PoNode, themeFonts?: ThemeFonts): RunProperties | undefined;
189
198
  /**
190
199
  * Parse `footnotes.xml` / `endnotes.xml` (§17.11) into note content by id. The
191
200
  * separator / continuationSeparator / continuationNotice stubs (negative ids or
@@ -425,7 +425,7 @@ function tryExtractDrawingFromParagraph(p, ctx) {
425
425
  if (!vml && drawings.every((d) => poChildren(d).some((c) => poIs(c, "wp:anchor")))) return null;
426
426
  const parseBody = (children) => parseBodyElements(children, ctx);
427
427
  const pPrNode = poChildren(p).find((c) => poIs(c, "w:pPr"));
428
- const paragraphProperties = pPrNode ? parseParagraphProperties(poElementToFlat(pPrNode), ctx.autoSpacingPt) : {};
428
+ const paragraphProperties = pPrNode ? parseParagraphProperties(poElementToFlat(pPrNode), ctx.autoSpacingPt, ctx.themeFonts) : {};
429
429
  if (drawings.length === 0) {
430
430
  const content = parseVmlPicture(vml, parseBody);
431
431
  if (!content) return null;
@@ -484,7 +484,10 @@ function blocksForDrawing(content, paragraphProperties, ctx) {
484
484
  }
485
485
  }];
486
486
  if (content.kind === "diagram") {
487
- const diagram = ctx.resolveDiagram?.(content.dmRelId);
487
+ const diagram = ctx.resolveDiagram?.(content.dmRelId, {
488
+ cx: content.widthEmu,
489
+ cy: content.heightEmu
490
+ });
488
491
  if (!diagram) {
489
492
  ctx.onLoss?.(noDiagramOverrideLoss());
490
493
  return null;
@@ -536,7 +539,7 @@ function parseParagraph(p, ctx, extraBookmarks, anchored, sdtRunProps) {
536
539
  if (bookmarkName !== void 0 && bookmarkName !== "" && bookmarkName !== "_GoBack") bookmarks.push(bookmarkName);
537
540
  }
538
541
  const pPr = poChildren(p).find((c) => poIs(c, "w:pPr"));
539
- let properties = parseParagraphProperties(pPr ? poElementToFlat(pPr) : void 0, ctx.autoSpacingPt);
542
+ let properties = parseParagraphProperties(pPr ? poElementToFlat(pPr) : void 0, ctx.autoSpacingPt, ctx.themeFonts);
540
543
  if (pPr && poChildren(pPr).some((c) => poIs(c, "w:sectPr"))) properties = {
541
544
  ...properties,
542
545
  sectionBreak: true
@@ -773,7 +776,7 @@ function collectRuns(container, out, ctx, href, anchor, anchored, sdtRunProps) {
773
776
  if (bookmark !== void 0 && bookmark !== "") childAnchor = bookmark;
774
777
  }
775
778
  }
776
- const childProps = tag === "w:sdt" ? sdtRunProperties(child) ?? sdtRunProps : sdtRunProps;
779
+ const childProps = tag === "w:sdt" ? sdtRunProperties(child, ctx.themeFonts) ?? sdtRunProps : sdtRunProps;
777
780
  collectRuns(child, out, ctx, childHref, childAnchor, anchored, childProps);
778
781
  }
779
782
  }
@@ -785,11 +788,11 @@ function collectRuns(container, out, ctx, href, anchor, anchored, sdtRunProps) {
785
788
  * @param sdt The `w:sdt` element.
786
789
  * @returns The properties, or `undefined` when the control states none.
787
790
  */
788
- function sdtRunProperties(sdt) {
791
+ function sdtRunProperties(sdt, themeFonts) {
789
792
  const sdtPr = poChildren(sdt).find((c) => poIs(c, "w:sdtPr"));
790
793
  const rPr = sdtPr ? poChildren(sdtPr).find((c) => poIs(c, "w:rPr")) : void 0;
791
794
  if (!rPr) return void 0;
792
- const props = parseRunProperties(poElementToFlat(rPr));
795
+ const props = parseRunProperties(poElementToFlat(rPr), themeFonts);
793
796
  return Object.keys(props).length > 0 ? props : void 0;
794
797
  }
795
798
  var WINGDINGS_TO_UNICODE = new Map([
@@ -870,7 +873,7 @@ function symbolChar(font, char) {
870
873
  }
871
874
  function parseRun(r, ctx, anchored) {
872
875
  const rPr = poChildren(r).find((c) => poIs(c, "w:rPr"));
873
- const properties = parseRunProperties(rPr ? poElementToFlat(rPr) : void 0);
876
+ const properties = parseRunProperties(rPr ? poElementToFlat(rPr) : void 0, ctx.themeFonts);
874
877
  let text = "";
875
878
  let pageBreak = false;
876
879
  let columnBreak = false;
@@ -5,11 +5,11 @@ import { resolveTableStyles } from "../core/style-cascade/table.js";
5
5
  import "../core/style-cascade/index.js";
6
6
  import { applyNumbering, applyNumberingToHeadersFooters } from "../core/numbering/apply.js";
7
7
  import "../core/numbering/index.js";
8
- import { poFindDescendant } from "../core/po-helpers.js";
8
+ import { cachedDiagramTree, laidOutDiagramTree } from "../core/drawingml/diagram/run.js";
9
9
  import { packageHasPart } from "../core/bytes.js";
10
10
  import { DEFAULT_THEME_PALETTE, makeColorResolver } from "../core/drawingml/colors.js";
11
11
  import { parseChart, withChartColorStyle } from "../core/drawingml/chart-parser.js";
12
- import { parseTheme, parseThemeBgFillStyles, parseThemeEffectStyles, parseThemeFillStyles, parseThemeLineWidths } from "../core/drawingml/theme-parser.js";
12
+ import { parseTheme, parseThemeBgFillStyles, parseThemeEffectStyles, parseThemeFillStyles, parseThemeFonts, parseThemeLineWidths } from "../core/drawingml/theme-parser.js";
13
13
  import { isOoxmlRel } from "../core/opc/relationship-types.js";
14
14
  import { OpcPackage } from "../core/opc/package.js";
15
15
  import { parseCoreProperties } from "../core/opc/core-properties.js";
@@ -52,6 +52,7 @@ function readDocx(docx) {
52
52
  const resolveColor = buildColorResolver(pkg, main.path);
53
53
  const themeData = loadTheme(pkg, main.path);
54
54
  const themeLineWidths = themeData ? parseThemeLineWidths(themeData) : void 0;
55
+ const themeFonts = themeData ? parseThemeFonts(themeData) : void 0;
55
56
  const themeStyles = themeData ? {
56
57
  fills: parseThemeFillStyles(themeData),
57
58
  bgFills: parseThemeBgFillStyles(themeData),
@@ -68,6 +69,7 @@ function readDocx(docx) {
68
69
  ...settings.compatibilityMode === void 0 ? { autoSpacingPt: 14 } : {},
69
70
  ...themeLineWidths && themeLineWidths.length > 0 ? { themeLineWidths } : {},
70
71
  ...themeStyles ? { themeStyles } : {},
72
+ ...themeFonts ? { themeFonts } : {},
71
73
  resolveImage,
72
74
  resolveHyperlink,
73
75
  resolveDiagram: makeDiagramResolver(pkg, main.path, resources),
@@ -84,11 +86,12 @@ function readDocx(docx) {
84
86
  endIndex: bodyIndexForBlock(blocks, s.endIndex)
85
87
  }));
86
88
  const stylesData = pkg.getPart(STYLES_PART);
87
- const styles = stylesData ? parseStyles(stylesData) : EMPTY_STYLE_SHEET;
89
+ const styles = stylesData ? parseStyles(stylesData, themeFonts) : EMPTY_STYLE_SHEET;
88
90
  const numberingData = pkg.getPart(NUMBERING_PART);
89
- const numbering = numberingData ? parseNumbering(numberingData, makeImageResolver(pkg, resources, NUMBERING_PART)) : EMPTY_NUMBERING;
91
+ const numbering = numberingData ? parseNumbering(numberingData, makeImageResolver(pkg, resources, NUMBERING_PART), themeFonts) : EMPTY_NUMBERING;
90
92
  const noteCtx = (part) => ({
91
93
  resolveColor,
94
+ ...themeFonts ? { themeFonts } : {},
92
95
  resolveImage: makeImageResolver(pkg, resources, part),
93
96
  resolveHyperlink: makeHyperlinkResolver(pkg, part)
94
97
  });
@@ -182,29 +185,42 @@ function infoFromCore(core) {
182
185
  }
183
186
  function makeDiagramResolver(pkg, partName, store) {
184
187
  const cache = /* @__PURE__ */ new Map();
185
- return (relId) => {
186
- if (cache.has(relId)) return cache.get(relId);
188
+ return (relId, frame) => {
189
+ const key = `${relId}|${frame.cx}x${frame.cy}`;
190
+ if (cache.has(key)) return cache.get(key);
187
191
  let resolved;
188
192
  const dataRel = pkg.getPartRelationships(partName).find((r) => r.id === relId);
189
193
  const data = dataRel ? pkg.resolveRelatedPart(partName, dataRel) : void 0;
190
194
  if (data) {
191
195
  const own = pkg.getPartRelationships(data.path).find((r) => r.type.endsWith("/diagramDrawing"));
192
196
  const draw = own ? pkg.resolveRelatedPart(data.path, own) : drawingFromOwner(pkg, partName, data.path);
193
- if (draw) for (const root of parseXml(draw.data)) {
194
- const spTree = poFindDescendant(root, "dsp:spTree");
195
- if (spTree) {
196
- resolved = {
197
- spTree,
198
- resolveImage: makeImageResolver(pkg, store, draw.path)
199
- };
200
- break;
201
- }
197
+ const cached = draw ? cachedDiagramTree(draw.data, parseXml) : void 0;
198
+ if (cached && draw) resolved = {
199
+ spTree: cached,
200
+ resolveImage: makeImageResolver(pkg, store, draw.path)
201
+ };
202
+ else {
203
+ const spTree = laidOutDiagramTree({
204
+ data: data.data,
205
+ layout: siblingPart(pkg, partName, data.path, "/diagramLayout"),
206
+ colors: siblingPart(pkg, partName, data.path, "/diagramColors")
207
+ }, frame, parseXml);
208
+ if (spTree) resolved = { spTree };
202
209
  }
203
210
  }
204
- cache.set(relId, resolved);
211
+ cache.set(key, resolved);
205
212
  return resolved;
206
213
  };
207
214
  }
215
+ function siblingPart(pkg, ownerPart, dataPart, type) {
216
+ const own = pkg.getPartRelationships(dataPart).find((r) => r.type.endsWith(type));
217
+ if (own) return pkg.resolveRelatedPart(dataPart, own)?.data;
218
+ const index = (path) => /(\d*)\.[^.]+$/u.exec(path)?.[1] ?? "";
219
+ const want = index(dataPart);
220
+ const rels = pkg.getPartRelationships(ownerPart).filter((r) => r.type.endsWith(type));
221
+ const rel = rels.find((r) => index(r.target) === want) ?? (rels.length === 1 ? rels[0] : void 0);
222
+ return rel ? pkg.resolveRelatedPart(ownerPart, rel)?.data : void 0;
223
+ }
208
224
  function drawingFromOwner(pkg, ownerPart, dataPart) {
209
225
  const drawings = pkg.getPartRelationships(ownerPart).filter((r) => r.type.endsWith("/diagramDrawing"));
210
226
  if (drawings.length === 0) return void 0;
@@ -38,10 +38,11 @@ export interface ConvertDocxOptions extends Omit<StyledRenderOptions, 'registry'
38
38
  */
39
39
  export declare function convertDocxToPdfSync(docx: Uint8Array, options: ConvertDocxOptions): Uint8Array;
40
40
  /**
41
- * The distinct curated font families the document references (scanning the
42
- * `w:ascii` of `styles.xml` + `document.xml`), always including the sans default
43
- * `'arimo'` as a fallback for unstyled runs / math / charts. The async path
44
- * fetches one substitute set per family so each run renders in the right one.
41
+ * The distinct curated font families the document references — the `w:ascii` of
42
+ * `styles.xml` + `document.xml`, and the theme slots they point at instead
43
+ * (§17.3.2.26) — always including the sans default `'arimo'` as a fallback for
44
+ * unstyled runs / math / charts. The async path fetches one substitute set per
45
+ * family so each run renders in the right one.
45
46
  *
46
47
  * @param docx The `.docx` bytes.
47
48
  * @returns The resolved {@link FamilyKey} set; just the default when the package
@@ -76,9 +77,9 @@ export declare function resolveDocxAutoFonts(docx: Uint8Array, options?: {
76
77
  }>;
77
78
  /**
78
79
  * Best-effort detection of the document's primary font family, used to choose a
79
- * substitute for auto-download. Prefers the document defaults' `w:ascii` font,
80
- * then falls back to the most frequent run font. A cheap regex over the XML — no
81
- * need to fully parse for this hint.
80
+ * substitute for auto-download. Prefers the document defaults' font — spelled
81
+ * out or pointed at in the theme — then falls back to the most frequent run
82
+ * font. A cheap regex over the XML: no need to fully parse for this hint.
82
83
  *
83
84
  * @param docx The `.docx` bytes.
84
85
  * @returns The detected family name, or `undefined` when none is found.
@@ -3,12 +3,15 @@ import { FontRegistry } from "../core/font/font-registry.js";
3
3
  import { signPdf } from "../pdf/signature.js";
4
4
  import { renderStyledPdf, renderStyledPdfEncrypted } from "../pdf/styled-page-renderer.js";
5
5
  import "../pdf/index.js";
6
+ import { parseThemeFonts } from "../core/drawingml/theme-parser.js";
6
7
  import { OpcPackage } from "../core/opc/package.js";
7
8
  import "../core/opc/index.js";
9
+ import { resolveWordThemeFont } from "./theme-fonts.js";
8
10
  import { readDocx } from "./docx-reader.js";
9
11
  import { flowRenderOptions } from "../core/converter/project.js";
10
12
  //#region src/word/docx-to-pdf.ts
11
13
  var STYLES_PART = "word/styles.xml";
14
+ var THEME_PART = "word/theme/theme1.xml";
12
15
  var MAIN_DOCUMENT_PART = "word/document.xml";
13
16
  /**
14
17
  * Synchronous one-shot conversion (requires `fonts`/`fontBytes`, no network).
@@ -49,10 +52,11 @@ function prepareDocxStyledRender(docx, options) {
49
52
  };
50
53
  }
51
54
  /**
52
- * The distinct curated font families the document references (scanning the
53
- * `w:ascii` of `styles.xml` + `document.xml`), always including the sans default
54
- * `'arimo'` as a fallback for unstyled runs / math / charts. The async path
55
- * fetches one substitute set per family so each run renders in the right one.
55
+ * The distinct curated font families the document references — the `w:ascii` of
56
+ * `styles.xml` + `document.xml`, and the theme slots they point at instead
57
+ * (§17.3.2.26) — always including the sans default `'arimo'` as a fallback for
58
+ * unstyled runs / math / charts. The async path fetches one substitute set per
59
+ * family so each run renders in the right one.
56
60
  *
57
61
  * @param docx The `.docx` bytes.
58
62
  * @returns The resolved {@link FamilyKey} set; just the default when the package
@@ -67,16 +71,30 @@ function detectDocxFamilyKeys(docx) {
67
71
  return keys;
68
72
  }
69
73
  const decoder = new TextDecoder("utf-8");
70
- const re = /<w:rFonts[^>]*\bw:ascii="([^"]+)"/g;
74
+ const themeFonts = docxThemeFonts(pkg);
75
+ const re = /<w:rFonts[^>]*?\bw:(ascii|asciiTheme)="([^"]+)"/g;
71
76
  for (const part of [STYLES_PART, MAIN_DOCUMENT_PART]) {
72
77
  const data = pkg.getPart(part);
73
78
  if (!data) continue;
74
79
  const xml = decoder.decode(data);
75
80
  let m;
76
- while ((m = re.exec(xml)) !== null) keys.add(resolveFamilyKey(m[1]));
81
+ while ((m = re.exec(xml)) !== null) {
82
+ const name = m[1] === "ascii" ? m[2] : resolveWordThemeFont(m[2], themeFonts);
83
+ if (name !== void 0) keys.add(resolveFamilyKey(name));
84
+ }
77
85
  }
78
86
  return keys;
79
87
  }
88
+ /** §20.1.4.1.16 — the theme's font scheme, for the slots `w:rFonts` points at. */
89
+ function docxThemeFonts(pkg) {
90
+ const data = pkg.getPart(THEME_PART);
91
+ if (!data) return void 0;
92
+ try {
93
+ return parseThemeFonts(data);
94
+ } catch {
95
+ return;
96
+ }
97
+ }
80
98
  /**
81
99
  * One-shot .docx → PDF (auto-downloads a substitute font set when the caller
82
100
  * supplies none; zero network with `fonts`/`fontBytes`).
@@ -140,4 +158,4 @@ async function resolveDocxAutoFonts(docx, options = {}) {
140
158
  };
141
159
  }
142
160
  //#endregion
143
- export { convertDocxToPdf, resolveDocxAutoFonts };
161
+ export { convertDocxToPdf };
@@ -50,6 +50,10 @@ var RASTER_MEDIA = {
50
50
  gif: {
51
51
  ext: "gif",
52
52
  contentType: "image/gif"
53
+ },
54
+ bmp: {
55
+ ext: "bmp",
56
+ contentType: "image/bmp"
53
57
  }
54
58
  };
55
59
  function mediaInfo(bytes) {
@@ -157,6 +157,26 @@ export declare function parseVmlPicture(node: PoNode, parseBody?: ParseBody): Dr
157
157
  * @returns The fill.
158
158
  */
159
159
  export declare function vmlFill(shape: PoNode, shapeType?: PoNode, resolveImage?: (id: string) => ResourceId | undefined): ShapeFill;
160
+ /**
161
+ * §20.1.8.55 `a:srcRect` — the picture's own edges, cut away before it is
162
+ * fitted to its frame. Each is a percentage in thousandths (ST_Percentage), so
163
+ * `l="14711"` drops the left 14.711%.
164
+ *
165
+ * @param node The `a:srcRect` element, or `undefined` when the fill declares none.
166
+ * @returns The crop, or `undefined` when nothing is cut away.
167
+ */
168
+ export declare function parseSrcRect(node: PoNode | undefined): ImageCrop | undefined;
169
+ /**
170
+ * The fill with the direction the SHAPE states, where it states one and no
171
+ * colours: §20.1.8.33's `a:gradFill` holding only an `a:lin` says "sweep the
172
+ * gallery style's run of colours this way". 63200.pptx's ellipse asks for the
173
+ * diagonal where the theme's slot sweeps straight down.
174
+ *
175
+ * @param fill The fill resolved from the style (or anywhere else).
176
+ * @param spPr The shape's own properties.
177
+ * @returns The fill, turned; unchanged when the shape states no bare direction.
178
+ */
179
+ export declare function withStatedDirection(fill: ShapeFill, spPr: PoNode | undefined): ShapeFill;
160
180
  /**
161
181
  * Whether an `spPr` states a fill AT ALL — including `a:noFill`, which is a
162
182
  * shape saying it has none rather than saying nothing. A placeholder that says