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
@@ -284,14 +284,16 @@ function normalizeStops(stops) {
284
284
  colorHex: "000000",
285
285
  alpha: 1
286
286
  }];
287
- out[0] = {
288
- ...out[0],
287
+ const first = out[0];
288
+ if (first.offset > 0) out.unshift({
289
+ ...first,
289
290
  offset: 0
290
- };
291
- out[out.length - 1] = {
292
- ...out[out.length - 1],
291
+ });
292
+ const last = out[out.length - 1];
293
+ if (last.offset < 1) out.push({
294
+ ...last,
293
295
  offset: 1
294
- };
296
+ });
295
297
  return out;
296
298
  }
297
299
  function rgb01(hex) {
@@ -7,6 +7,7 @@ import { GLUE_SHRINK_RATIO } from "../layout/styled-layout.js";
7
7
  import { paintPlan } from "../layout/page-doc.js";
8
8
  import { emitClipPath, emitVectorShape, shadowBlurLayers } from "./vector-graphics.js";
9
9
  import { buildGradientAlphaMask, buildGradientPattern, shapeBbox } from "./shading.js";
10
+ import { warpGlyphMatrix } from "../core/drawingml/text-warp.js";
10
11
  import { sanitizeHref } from "../core/links.js";
11
12
  import { embedAssociatedFile } from "./embedded-file.js";
12
13
  import { buildSrgbIccProfile } from "./icc-profile.js";
@@ -111,7 +112,11 @@ function assembleStyledPdf(laid, options, doc) {
111
112
  if (!pdfaProfile) for (const page of renderedPages) {
112
113
  const ph = page.height;
113
114
  const plan = paintPlan(page.commands);
114
- const shapesNeedingPatterns = [...plan.shapes, ...[...plan.behind, ...plan.pictures.flat()].filter((i) => i.type === "shape")];
115
+ const shapesNeedingPatterns = [...plan.shapes, ...[
116
+ ...plan.behind,
117
+ ...plan.pictures.flat(),
118
+ ...plan.ordered.flat()
119
+ ].filter((i) => i.type === "shape")];
115
120
  for (const item of shapesNeedingPatterns) {
116
121
  const gradient = item.shape.fillGradient;
117
122
  if (!gradient) continue;
@@ -211,7 +216,11 @@ function assembleStyledPdf(laid, options, doc) {
211
216
  wantAlpha(img.alpha);
212
217
  wantDuotone(img, page.height);
213
218
  }
214
- for (const item of [...plan.behind, ...plan.pictures.flat()]) {
219
+ for (const item of [
220
+ ...plan.behind,
221
+ ...plan.pictures.flat(),
222
+ ...plan.ordered.flat()
223
+ ]) {
215
224
  if (item.type === "image") {
216
225
  wantAlpha(washVeil(item.wash)?.alpha);
217
226
  wantAlpha(item.alpha);
@@ -621,6 +630,7 @@ var BORDER_DASHES = new Map([
621
630
  2.5
622
631
  ]]
623
632
  ]);
633
+ var FAUX_ITALIC_SHEAR = .2126;
624
634
  /** The PDF dash array for a border style; an empty string means a solid rule. */
625
635
  function dashPatternFor(style) {
626
636
  const pattern = style !== void 0 ? BORDER_DASHES.get(style) : void 0;
@@ -642,6 +652,8 @@ function emitPageContent(page, tagging, gradientNames, alphaStateNames, duotoneS
642
652
  let lastTc = 0;
643
653
  let lastSize = -1;
644
654
  let lastColor = "";
655
+ let lastFauxWidth = 0;
656
+ let lastTz = 100;
645
657
  const switchFontIfNeeded = (tok) => {
646
658
  const fontKey = tok.font.resourceName;
647
659
  if (fontKey !== lastFont || tok.fontSizePt !== lastSize) {
@@ -649,6 +661,16 @@ function emitPageContent(page, tagging, gradientNames, alphaStateNames, duotoneS
649
661
  lastFont = fontKey;
650
662
  lastSize = tok.fontSizePt;
651
663
  }
664
+ const fauxWidth = tok.synthetic?.bold === true ? tok.fontSizePt * .03 : 0;
665
+ if (fauxWidth !== lastFauxWidth) {
666
+ out.push(fauxWidth > 0 ? `2 Tr ${formatNumber(fauxWidth)} w` : "0 Tr");
667
+ lastFauxWidth = fauxWidth;
668
+ }
669
+ const tz = Math.round((tok.synthetic?.widthScale ?? 1) * 100);
670
+ if (tz !== lastTz) {
671
+ out.push(`${formatNumber(tz)} Tz`);
672
+ lastTz = tz;
673
+ }
652
674
  const tc = tok.resolvedRun.letterSpacingPt ?? 0;
653
675
  if (tc !== lastTc) {
654
676
  out.push(`${formatNumber(tc)} Tc`);
@@ -670,6 +692,12 @@ function emitPageContent(page, tagging, gradientNames, alphaStateNames, duotoneS
670
692
  const b = tok.drawBox;
671
693
  const ox = x + (b?.dxPt ?? 0);
672
694
  const oy = baselineY + (b?.dyPt ?? 0);
695
+ for (const img of meta.images ?? []) {
696
+ if (img.resourceName === "") continue;
697
+ out.push("q");
698
+ out.push(...placeImage(ox + img.x, oy + img.y, img.width, img.height, void 0, img.rotationDeg));
699
+ out.push(`/${img.resourceName} Do`, "Q");
700
+ }
673
701
  for (const sh of meta.shapes) for (const op of emitVectorShape({
674
702
  ...sh,
675
703
  transform: [
@@ -693,6 +721,8 @@ function emitPageContent(page, tagging, gradientNames, alphaStateNames, duotoneS
693
721
  out.push("ET");
694
722
  }
695
723
  lastFont = "";
724
+ lastFauxWidth = 0;
725
+ lastTz = 100;
696
726
  lastSize = -1;
697
727
  lastColor = "";
698
728
  return;
@@ -739,6 +769,8 @@ function emitPageContent(page, tagging, gradientNames, alphaStateNames, duotoneS
739
769
  out.push("Q");
740
770
  }
741
771
  lastFont = "";
772
+ lastFauxWidth = 0;
773
+ lastTz = 100;
742
774
  lastSize = -1;
743
775
  lastColor = "";
744
776
  lastTc = 0;
@@ -771,6 +803,8 @@ function emitPageContent(page, tagging, gradientNames, alphaStateNames, duotoneS
771
803
  out.push("f");
772
804
  out.push("Q");
773
805
  lastFont = "";
806
+ lastFauxWidth = 0;
807
+ lastTz = 100;
774
808
  lastSize = -1;
775
809
  lastColor = "";
776
810
  } else {
@@ -796,6 +830,8 @@ function emitPageContent(page, tagging, gradientNames, alphaStateNames, duotoneS
796
830
  };
797
831
  for (const op of emitVectorShape(shape)) out.push(op);
798
832
  lastFont = "";
833
+ lastFauxWidth = 0;
834
+ lastTz = 100;
799
835
  lastSize = -1;
800
836
  lastColor = "";
801
837
  }
@@ -813,6 +849,8 @@ function emitPageContent(page, tagging, gradientNames, alphaStateNames, duotoneS
813
849
  out.push("q");
814
850
  out.push(`${formatNumber(clip.x)} ${formatNumber(H - clip.y - clip.height)} ${formatNumber(clip.width)} ${formatNumber(clip.height)} re W n`);
815
851
  lastFont = "";
852
+ lastFauxWidth = 0;
853
+ lastTz = 100;
816
854
  lastSize = -1;
817
855
  lastColor = "";
818
856
  lastTc = 0;
@@ -825,6 +863,8 @@ function emitPageContent(page, tagging, gradientNames, alphaStateNames, duotoneS
825
863
  }
826
864
  out.push("Q");
827
865
  lastFont = "";
866
+ lastFauxWidth = 0;
867
+ lastTz = 100;
828
868
  lastSize = -1;
829
869
  lastColor = "";
830
870
  lastTc = 0;
@@ -977,6 +1017,35 @@ function emitPageContent(page, tagging, gradientNames, alphaStateNames, duotoneS
977
1017
  out.push("Q");
978
1018
  }
979
1019
  const hasRise = line.tokens.some((t) => t.kind === "text" && t.risePt !== void 0);
1020
+ const hasFauxItalic = line.tokens.some((t) => t.kind === "text" && t.synthetic?.italic === true);
1021
+ const warp = cmd.warp;
1022
+ if (warp) {
1023
+ if (!inBT) {
1024
+ out.push("BT");
1025
+ inBT = true;
1026
+ }
1027
+ let cursor = originX;
1028
+ for (const tok of line.tokens) {
1029
+ if (tok.kind !== "text" || tok.isSpace) {
1030
+ cursor += tok.widthPt;
1031
+ continue;
1032
+ }
1033
+ switchFontIfNeeded(tok);
1034
+ const chars = [...tok.text];
1035
+ const raw = chars.map((c) => tok.font.measure.textWidthPt(c, tok.fontSizePt));
1036
+ const total = raw.reduce((a, b) => a + b, 0);
1037
+ const k = total > 0 ? tok.widthPt / total : 1;
1038
+ for (let i = 0; i < chars.length; i++) {
1039
+ const adv = raw[i] * k;
1040
+ const m = warpGlyphMatrix(warp, cursor, adv, cmd.baselineY);
1041
+ cursor += adv;
1042
+ if (!m) continue;
1043
+ out.push(`${formatNumber(m[0])} ${formatNumber(-m[1])} ${formatNumber(m[2])} ${formatNumber(-m[3])} ${formatNumber(m[4])} ${formatNumber(H - m[5])} Tm`);
1044
+ out.push(tok.font.measure.showText(chars[i]));
1045
+ }
1046
+ }
1047
+ return;
1048
+ }
980
1049
  if (rotationDeg !== 0 && !hasImageToken && !hasMathToken) {
981
1050
  if (!inBT) {
982
1051
  out.push("BT");
@@ -991,7 +1060,7 @@ function emitPageContent(page, tagging, gradientNames, alphaStateNames, duotoneS
991
1060
  switchFontIfNeeded(tok);
992
1061
  out.push(tok.font.measure.showText(tok.text));
993
1062
  }
994
- } else if (extraPerSpace !== 0 || hasImageToken || hasMathToken || hasRtl || hasRise || hasTab) {
1063
+ } else if (extraPerSpace !== 0 || hasImageToken || hasMathToken || hasRtl || hasRise || hasFauxItalic || hasTab) {
995
1064
  const order = hasRtl ? lineVisualOrder(line) : line.tokens.map((_, i) => i);
996
1065
  let x = originX;
997
1066
  for (const ti of order) {
@@ -1013,7 +1082,8 @@ function emitPageContent(page, tagging, gradientNames, alphaStateNames, duotoneS
1013
1082
  inBT = true;
1014
1083
  }
1015
1084
  switchFontIfNeeded(tok);
1016
- out.push(`1 0 0 1 ${formatNumber(x)} ${formatNumber(baselineY + (tok.risePt ?? 0))} Tm`);
1085
+ const slant = tok.synthetic?.italic === true ? FAUX_ITALIC_SHEAR : 0;
1086
+ out.push(`1 0 ${formatNumber(slant)} 1 ${formatNumber(x)} ${formatNumber(baselineY + (tok.risePt ?? 0))} Tm`);
1017
1087
  out.push(showToken(tok));
1018
1088
  const tokenX0 = x;
1019
1089
  x += tok.widthPt;
@@ -1071,6 +1141,7 @@ function emitPageContent(page, tagging, gradientNames, alphaStateNames, duotoneS
1071
1141
  if (item.type === "shape") {
1072
1142
  const t = item.shape.transform;
1073
1143
  const fillAlpha = item.shape.fillAlpha;
1144
+ const layer = item.shape.shadow ? shadowBlurLayers(item.shape.shadow) : void 0;
1074
1145
  for (const op of emitVectorShape({
1075
1146
  ...item.shape,
1076
1147
  transform: [
@@ -1081,7 +1152,7 @@ function emitPageContent(page, tagging, gradientNames, alphaStateNames, duotoneS
1081
1152
  t[4],
1082
1153
  H - t[5]
1083
1154
  ]
1084
- }, gradientNames?.get(item.shape), void 0, gradientMaskNames?.get(item.shape) ?? (fillAlpha !== void 0 && fillAlpha < 1 ? alphaStateNames?.get(Math.round(fillAlpha * 1e3) / 1e3) : void 0))) out.push(op);
1155
+ }, gradientNames?.get(item.shape), layer && layer.alpha < 1 ? alphaStateNames?.get(Math.round(layer.alpha * 1e3) / 1e3) : void 0, gradientMaskNames?.get(item.shape) ?? (fillAlpha !== void 0 && fillAlpha < 1 ? alphaStateNames?.get(Math.round(fillAlpha * 1e3) / 1e3) : void 0))) out.push(op);
1085
1156
  return;
1086
1157
  }
1087
1158
  if (item.type === "image") {
@@ -1254,6 +1325,7 @@ function emitPageContent(page, tagging, gradientNames, alphaStateNames, duotoneS
1254
1325
  for (const item of run) emitInOrder(item);
1255
1326
  if (tagging) out.push("EMC");
1256
1327
  }
1328
+ for (const run of plan.ordered) for (const item of run) emitInOrder(item);
1257
1329
  emitLinesPass();
1258
1330
  return {
1259
1331
  content: encoder.encode(out.join("\n")),
@@ -31,7 +31,7 @@ function shadowBlurLayers(shadow) {
31
31
  const count = Math.min(8, Math.max(2, Math.round(shadow.blurPt / 2)));
32
32
  return {
33
33
  count,
34
- alpha: shadow.alpha / count
34
+ alpha: 1 - Math.pow(1 - shadow.alpha, 1 / count)
35
35
  };
36
36
  }
37
37
  function shadowBbox(shape) {
@@ -1,8 +1,8 @@
1
1
  import { PDF_NULL, PdfHexString, PdfName, PdfStream } from "../pdf/objects.js";
2
+ import { encodePng } from "../core/png-encode.js";
2
3
  import { lzwDecodeMsb } from "../core/lzw.js";
3
4
  import { reversePredictor } from "./predictor.js";
4
5
  import { decodeCcitt } from "./ccitt.js";
5
- import { encodePng } from "./png-encode.js";
6
6
  import { unzlibSync } from "fflate";
7
7
  //#region src/pdf-reader/image-decode.ts
8
8
  var MAX_PIXELS = 4e7;
@@ -0,0 +1,31 @@
1
+ import { Loss } from '../core/ir/index.js';
2
+ import { OpcPackage } from '../core/opc/index.js';
3
+ import { PoNode } from '../core/po-helpers.js';
4
+ import { FontRegistry } from '../core/font/index.js';
5
+ /** What an `fntdata` part turned out to hold. */
6
+ type FaceBytes = {
7
+ readonly kind: 'font';
8
+ readonly bytes: Uint8Array;
9
+ } | {
10
+ readonly kind: 'compressed';
11
+ } | {
12
+ readonly kind: 'unreadable';
13
+ };
14
+ /**
15
+ * Read one `fntdata` part: the font itself when the container stores it
16
+ * plainly, and what stopped it otherwise.
17
+ *
18
+ * @param data The part's bytes.
19
+ */
20
+ export declare function readFntData(data: Uint8Array): FaceBytes;
21
+ /**
22
+ * §19.2.1.13 — the fonts a deck embeds, by normalized family name.
23
+ *
24
+ * @param pkg The opened `.pptx` package.
25
+ * @param presPath The presentation part's path (relationships resolve from it).
26
+ * @param pres Its parsed `p:presentation` element.
27
+ * @param onLoss Where a face that cannot be unpacked records itself.
28
+ * @returns The families whose faces were readable (empty when none were).
29
+ */
30
+ export declare function loadPptxEmbeddedFonts(pkg: OpcPackage, presPath: string, pres: PoNode | undefined, onLoss: (loss: Loss) => void): Map<string, FontRegistry>;
31
+ export {};
@@ -0,0 +1,104 @@
1
+ import { FontRegistry } from "../core/font/font-registry.js";
2
+ import { poAttr, poChildren, poIs } from "../core/po-helpers.js";
3
+ //#region src/pptx/embedded-fonts.ts
4
+ var REL_FONT = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/font";
5
+ /** §19.2.1.13 — the four faces a `p:embeddedFont` may name, by element. */
6
+ var VARIANTS = [
7
+ ["p:regular", "regular"],
8
+ ["p:bold", "bold"],
9
+ ["p:italic", "italic"],
10
+ ["p:boldItalic", "boldItalic"]
11
+ ];
12
+ var EOT_VERSIONS = new Set([
13
+ 65536,
14
+ 131073,
15
+ 131074
16
+ ]);
17
+ var TTEMBED_TTCOMPRESSED = 4;
18
+ var TTEMBED_XORENCRYPTDATA = 268435456;
19
+ /**
20
+ * Read one `fntdata` part: the font itself when the container stores it
21
+ * plainly, and what stopped it otherwise.
22
+ *
23
+ * @param data The part's bytes.
24
+ */
25
+ function readFntData(data) {
26
+ if (isSfnt(data)) return {
27
+ kind: "font",
28
+ bytes: data
29
+ };
30
+ if (data.length < 82) return { kind: "unreadable" };
31
+ const v = new DataView(data.buffer, data.byteOffset, data.byteLength);
32
+ const eotSize = v.getUint32(0, true);
33
+ const version = v.getUint32(8, true);
34
+ const flags = v.getUint32(12, true);
35
+ if (eotSize !== data.length || !EOT_VERSIONS.has(version)) return { kind: "unreadable" };
36
+ if ((flags & (TTEMBED_TTCOMPRESSED | TTEMBED_XORENCRYPTDATA)) !== 0) return { kind: "compressed" };
37
+ let at = 82;
38
+ for (let i = 0; i < 4; i++) {
39
+ if (at + 2 > data.length) return { kind: "unreadable" };
40
+ at += 2 + v.getUint16(at, true) + 2;
41
+ }
42
+ if (version !== 65536) {
43
+ if (at + 2 > data.length) return { kind: "unreadable" };
44
+ at += 2 + v.getUint16(at, true);
45
+ at = data.length - v.getUint32(4, true);
46
+ }
47
+ const bytes = data.subarray(at);
48
+ return isSfnt(bytes) ? {
49
+ kind: "font",
50
+ bytes
51
+ } : { kind: "unreadable" };
52
+ }
53
+ /**
54
+ * §19.2.1.13 — the fonts a deck embeds, by normalized family name.
55
+ *
56
+ * @param pkg The opened `.pptx` package.
57
+ * @param presPath The presentation part's path (relationships resolve from it).
58
+ * @param pres Its parsed `p:presentation` element.
59
+ * @param onLoss Where a face that cannot be unpacked records itself.
60
+ * @returns The families whose faces were readable (empty when none were).
61
+ */
62
+ function loadPptxEmbeddedFonts(pkg, presPath, pres, onLoss) {
63
+ const out = /* @__PURE__ */ new Map();
64
+ const list = pres ? poChildren(pres).find((c) => poIs(c, "p:embeddedFontLst")) : void 0;
65
+ if (!list) return out;
66
+ const relById = new Map(pkg.getPartRelationships(presPath).map((r) => [r.id, r]));
67
+ for (const font of poChildren(list).filter((c) => poIs(c, "p:embeddedFont"))) {
68
+ const bytes = {};
69
+ let compressed = false;
70
+ for (const [tag, variant] of VARIANTS) {
71
+ const node = poChildren(font).find((c) => poIs(c, tag));
72
+ const rId = node ? poAttr(node, "r:id") : void 0;
73
+ const rel = rId !== void 0 ? relById.get(rId) : void 0;
74
+ if (!rel || rel.type !== REL_FONT) continue;
75
+ const part = pkg.resolveRelatedPart(presPath, rel);
76
+ if (!part) continue;
77
+ const face = readFntData(part.data);
78
+ if (face.kind === "compressed") compressed = true;
79
+ if (face.kind === "font") bytes[variant] = face.bytes;
80
+ }
81
+ const typeface = poChildren(font).find((c) => poIs(c, "p:font"));
82
+ const name = (typeface ? poAttr(typeface, "typeface") : void 0)?.trim() ?? "";
83
+ if (bytes.regular) try {
84
+ out.set(name.toLowerCase(), FontRegistry.fromBytes({
85
+ ...bytes,
86
+ regular: bytes.regular
87
+ }));
88
+ continue;
89
+ } catch {}
90
+ if (compressed) onLoss({
91
+ feature: "font",
92
+ detail: `the deck embeds ${name || "a font"}, packed with MicroType Express (EOT TTEMBED_TTCOMPRESSED); it renders in a substitute`,
93
+ severity: "substituted"
94
+ });
95
+ }
96
+ return out;
97
+ }
98
+ function isSfnt(data) {
99
+ if (data.length < 4) return false;
100
+ const sig = (data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]) >>> 0;
101
+ return sig === 65536 || sig === 1330926671 || sig === 1953658213 || sig === 1953784678;
102
+ }
103
+ //#endregion
104
+ export { loadPptxEmbeddedFonts };
@@ -1,7 +1,9 @@
1
1
  import { ParagraphProperties, RunProperties } from '../core/document-model/index.js';
2
2
  import { ColorResolver } from '../core/drawingml/colors.js';
3
+ import { ThemeFonts } from '../core/drawingml/theme-parser.js';
3
4
  import { PoNode } from '../core/po-helpers.js';
4
5
  import { PlaceholderRef, ShapeBoxEmu } from './sp-helpers.js';
6
+ import { Pt } from '../core/ir/index.js';
5
7
  /**
6
8
  * The placeholder cascade (PX2): the resolver a slide parser consults whenever a
7
9
  * slide placeholder shape (`p:sp` with a `p:ph`) lacks its own geometry or run
@@ -27,17 +29,32 @@ export interface PlaceholderCascade {
27
29
  */
28
30
  readonly anchorFor: (ph: PlaceholderRef) => 't' | 'ctr' | 'b' | undefined;
29
31
  /**
30
- * The prototype's own `p:spPr`, for the properties a slide placeholder
31
- * inherits rather than states: its fill, its outline, its geometry. The
32
- * layout's before the master's.
32
+ * The prototypes' own `p:spPr`, nearest first — the layout's, then the
33
+ * master's — for the properties a slide placeholder inherits rather than
34
+ * states: its fill, its outline, its geometry.
35
+ *
36
+ * A CHAIN and not one node, because the inheritance is per property. The
37
+ * layout's prototype may state a box and nothing else, and the fill then
38
+ * comes from the master: tdf104015's title is red in every reader and was a
39
+ * bare outline here, because the layout's `p:spPr` existed and stopped the
40
+ * search.
33
41
  */
34
- readonly shapePropsFor: (ph: PlaceholderRef) => PoNode | undefined;
42
+ readonly shapePropsFor: (ph: PlaceholderRef) => ReadonlyArray<PoNode>;
35
43
  }
36
44
  /** One level of a text style: how its paragraphs sit and how their runs read. */
37
45
  interface LevelStyle {
38
46
  readonly run: RunProperties;
39
47
  readonly paragraph: ParagraphProperties;
40
48
  readonly bullet?: LevelBullet;
49
+ /**
50
+ * §21.1.2.2.9/.10 — the space around the paragraph stated as a FRACTION of a
51
+ * line rather than a distance. A fraction of WHAT is only known once the
52
+ * levels have merged and the size is settled, so it travels this far raw.
53
+ */
54
+ readonly spacingPct?: {
55
+ readonly before?: number;
56
+ readonly after?: number;
57
+ };
41
58
  }
42
59
  /**
43
60
  * §21.1.2.4.4/.5/.6 — the bullet a level declares: none at all, a literal
@@ -78,8 +95,24 @@ export declare function parseBullet(pPr: PoNode | undefined, colors?: ColorResol
78
95
  * @param layoutTree The parsed `p:sldLayout` part.
79
96
  * @param masterTree The parsed `p:sldMaster` part, when present.
80
97
  * @param colors The deck's colour resolver, for scheme colours in the text styles.
98
+ * @param deckDefaults The presentation's own default text style, as the floor.
99
+ * @param themeFonts The theme's font scheme, for the `+mn-lt` tokens.
100
+ */
101
+ export declare function buildPlaceholderCascade(layoutTree: ReadonlyArray<PoNode>, masterTree: ReadonlyArray<PoNode> | undefined, colors: ColorResolver, deckDefaults?: ReadonlyArray<LevelStyle>, themeFonts?: ThemeFonts): PlaceholderCascade;
102
+ /**
103
+ * §21.1.2.2.9/.10 — the space a paragraph states as a FRACTION of a line, in
104
+ * points.
105
+ *
106
+ * A "line" here is the paragraph's own text at its natural height, which is the
107
+ * size times the same 1.2 the layout gives an unstated line. A level that
108
+ * states the fraction and leaves the size to another level is resolved once
109
+ * they have merged, which is why this is not read where the fraction is.
110
+ *
111
+ * @param pct The fraction, `0..n` (20% arrives as 0.2).
112
+ * @param sizePt The paragraph's resolved font size.
113
+ * @returns The distance, or `undefined` when the size is not known.
81
114
  */
82
- export declare function buildPlaceholderCascade(layoutTree: ReadonlyArray<PoNode>, masterTree: ReadonlyArray<PoNode> | undefined, colors: ColorResolver, deckDefaults?: ReadonlyArray<LevelStyle>): PlaceholderCascade;
115
+ export declare function spacingFromLineFraction(pct: number | undefined, sizePt: number | undefined): Pt | undefined;
83
116
  /**
84
117
  * `a:lstStyle` (or a `p:txStyles` family) → its nine levels. Shared by the
85
118
  * deck's default text style, the master's families and a prototype's own list,
@@ -90,5 +123,39 @@ export declare function buildPlaceholderCascade(layoutTree: ReadonlyArray<PoNode
90
123
  * @param prefix The level element prefix: `a:lvl` here, always.
91
124
  * @returns The nine levels, or an empty list when there is no such node.
92
125
  */
93
- export declare function parseLevelStyles(list: PoNode | undefined, colors: ColorResolver): Array<LevelStyle>;
126
+ export declare function parseLevelStyles(list: PoNode | undefined, colors: ColorResolver, themeFonts?: ThemeFonts): Array<LevelStyle>;
127
+ /**
128
+ * A level's paragraph shape — §21.1.2.2.7 `@algn`, `@marL`/`@indent`, and the
129
+ * space around and between its lines.
130
+ *
131
+ * Both spacings come in two spellings: `a:spcPts` is a distance (hundredths of
132
+ * a point) and `a:spcPct` a FRACTION of a line (thousandths of a percent). Only
133
+ * the distance was read, so a deck that spaces its bullets the usual way — by
134
+ * fraction — set them solid.
135
+ */
136
+ export declare function pPrToParagraphProps(lvlPr: PoNode | undefined): ParagraphProperties;
137
+ /**
138
+ * §21.1.2.2.5 `a:lnSpc` — the height of each line: a fraction of the natural
139
+ * one (`a:spcPct`) or a distance (`a:spcPts`).
140
+ *
141
+ * The model states a multiple the way §17.3.1.33 does — 12pt under the `auto`
142
+ * rule IS single — so a fraction needs no font size to express, which is why
143
+ * this resolves here and the paragraph SPACING has to wait for the size.
144
+ *
145
+ * @param pPr The paragraph properties (a level's or a paragraph's own).
146
+ * @returns The line spacing, or nothing when the node states none.
147
+ */
148
+ export declare function lineSpacing(pPr: PoNode | undefined): ParagraphProperties;
149
+ /**
150
+ * §21.1.2.2.9/.10 — the fraction of a line a paragraph puts before and after
151
+ * itself, when it states one. The distance form is read beside it; this is what
152
+ * the caller must still resolve against the size.
153
+ *
154
+ * @param pPr The paragraph properties.
155
+ * @returns The two fractions (`0..n`), each present only when stated.
156
+ */
157
+ export declare function spacingFractions(pPr: PoNode | undefined): {
158
+ readonly before?: number;
159
+ readonly after?: number;
160
+ } | undefined;
94
161
  export {};