@openpresentation/opf-render 0.13.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/raster.js CHANGED
@@ -10,11 +10,24 @@ const DEFAULT_RASTER_BACKGROUND = "#FFFFFF";
10
10
  const DEFAULT_PDF_MODE = "vector";
11
11
  let bundledFontFilesCache=null;
12
12
 
13
+ // The faces a raster or PDF conversion draws with, from the fonts handle (`loadFonts()` from `/fonts-node`): its `fontFiles`, and
14
+ // whether the bundled base faces are added (`useBundledFonts`, default true; a Node handle already holds its own, so it says false) and
15
+ // whether system fonts load (`loadSystemFonts`, default false). Without a handle the bundled base faces draw.
16
+ function fontSettings(options) {
17
+ const fonts = options.fonts ?? {};
18
+ return { fontFiles: stringArray(fonts.fontFiles), useBundledFonts: fonts.useBundledFonts !== false, loadSystemFonts: fonts.loadSystemFonts === true };
19
+ }
20
+
21
+ /**
22
+ * Convert one SVG (the output of `renderSvg` or `renderSlideSvg`) to PNG bytes. `fonts` is the handle `loadFonts()` returns: the raster draws
23
+ * with its font files, so the preview and the PNG use the same faces. Without it the bundled base faces draw.
24
+ */
13
25
  export async function svgToPng(svg, options = {}) {
14
26
  const rendered = await rasterizeSvg(svg, options);
15
27
  return rendered.png;
16
28
  }
17
29
 
30
+ /** Convert SVG slides (the output of `renderSvg`) to a PDF, one page each. `fonts` is as for `svgToPng`; `mode` is `"vector"` (default) or `"raster"`. */
18
31
  export async function svgToPdf(svgs, options = {}) {
19
32
  const pdfInputs = normalizeSvgList(svgs);
20
33
  if (!pdfInputs.length) {
@@ -49,13 +62,14 @@ export async function svgToPdf(svgs, options = {}) {
49
62
  }
50
63
 
51
64
  async function vectorPdf(inputs, options) {
52
- if (options.loadSystemFonts === true) {
53
- throw new OPFRenderError("pdf-system-fonts-unsupported", 'Vector PDF output embeds only the font files you supply: loadSystemFonts is not supported with mode "vector". Pass fontFiles/fontDirs, or use mode "raster".', { option: "loadSystemFonts" });
65
+ const settings = fontSettings(options);
66
+ if (settings.loadSystemFonts) {
67
+ throw new OPFRenderError("pdf-system-fonts-unsupported", 'Vector PDF output embeds only the font files you supply: fonts.loadSystemFonts is not supported with mode "vector". Pass a fonts handle with fontFiles (and fontDirs), or use mode "raster".', { option: "fonts.loadSystemFonts" });
54
68
  }
55
69
  const scale = positiveNumber(options.rasterFallbackScale, "rasterFallbackScale", 2);
56
70
  const fontFiles = [
57
- ...(options.useBundledFonts === false ? [] : await bundledFontFiles()),
58
- ...stringArray(options.fontFiles)
71
+ ...(settings.useBundledFonts ? await bundledFontFiles() : []),
72
+ ...settings.fontFiles
59
73
  ];
60
74
  const svgs = [];
61
75
  for (const input of inputs) svgs.push(await prepareRasterImages(normalizeSvgInput(input)));
@@ -100,12 +114,13 @@ async function rasterizeSvg(svgInput, options) {
100
114
  const { Resvg } = await loadResvg();
101
115
  const scale = positiveNumber(options.scale, "scale", DEFAULT_RASTER_SCALE);
102
116
  // FF-45: resvg draws no COLRv1 or OT-SVG colour glyphs; the colour faces stay out of its font list (see color-fonts.js).
117
+ const settings = fontSettings(options);
103
118
  const fontFiles = rasterFontFiles([
104
- ...(options.useBundledFonts === false ? [] : await bundledFontFiles()),
105
- ...stringArray(options.fontFiles)
119
+ ...(settings.useBundledFonts ? await bundledFontFiles() : []),
120
+ ...settings.fontFiles
106
121
  ]);
107
122
  const font = {
108
- loadSystemFonts: options.loadSystemFonts === true,
123
+ loadSystemFonts: settings.loadSystemFonts,
109
124
  fontFiles,
110
125
  fontDirs: stringArray(options.fontDirs),
111
126
  defaultFontFamily: options.defaultFontFamily ?? "Roboto",
@@ -178,8 +193,8 @@ async function bundledFontFiles() {
178
193
  }
179
194
 
180
195
  async function resolveBundledFontFiles() {
181
- const {loadBundledFontRegistry} = await import('./fonts-node.js');
182
- return (await loadBundledFontRegistry()).fontFiles;
196
+ const {loadFonts} = await import('./fonts-node.js');
197
+ return (await loadFonts()).fontFiles;
183
198
  }
184
199
 
185
200
  function normalizeSvgList(value) {
@@ -2,11 +2,9 @@
2
2
  // stand in for proprietary script fonts in previews. The packages are optional
3
3
  // peers, so the renderer install stays small; hosts install only what they use.
4
4
  import { BUNDLED_FONT_MANIFEST } from "./font-manifest.js";
5
- import { OPFFontError } from "./fonts.js";
6
- import * as scriptFontModule from "./script-fonts.js";
7
- import { scriptOfCharacter, scriptsOfText } from "./script-fonts.js";
8
- // Optional core exports are read from the namespace so an older published core still loads.
9
- import * as opfCore from "@openpresentation/opf";
5
+ import { OPFFontError } from "./font-registry.js";
6
+ import { SCRIPT_FONT_REPLACEMENTS, glyphFallbackFamilies, scriptOfCharacter, scriptsOfText } from "./script-fonts.js";
7
+ import { resolveScriptFonts } from "@openpresentation/opf/composition";
10
8
  import { presentationFamilies } from "./lazy-fonts.js";
11
9
  import { SYMBOL_SCRIPT, isSymbolEncodedFamily } from "./symbol-fonts.js";
12
10
 
@@ -33,7 +31,7 @@ export function scriptFontPackages(scripts) {
33
31
  * Browser entries for the script pack. `baseUrl` is where the host serves the
34
32
  * installed `@expo-google-fonts/*` packages (for example a copy of
35
33
  * `node_modules/@expo-google-fonts`). Each entry carries the reviewed SHA-256,
36
- * which `loadBrowserFontRegistry` verifies before use.
34
+ * which `loadFonts` (browser) verifies before use.
37
35
  */
38
36
  export function scriptFontEntries(scripts, {baseUrl}) {
39
37
  return scriptPackageEntries(scriptFontPackages(scripts), {baseUrl});
@@ -55,17 +53,17 @@ export function scriptPackageEntries(packages, {baseUrl}) {
55
53
  // Fields a preview never draws (checked against what the renderer and core composition draw).
56
54
  // Everywhere: the schema URL, embedded assets, catalog records, speaker notes, ids, image alt text and sources,
57
55
  // links, metadata and typed `extensions` passthrough. On the presentation: name (aria-label fallback), description,
58
- // filename, author, speaker, audience, purpose, tone, takeaway, duration, tags, narrative, language and version.
59
- // On a slide: beat. The organization (name) and a slide's section are drawn only as generated header/footer
60
- // furniture (`organization: true` / `section: true` in a design), so they count only when a design asks for
61
- // them. Everything else is drawn text: titles, text blocks, list items, runs, table cells, chart labels,
56
+ // filename, author, audience, purpose, tone, takeaway, duration, tags, narrative, language and version.
57
+ // On a slide: beat. The organization (name), the speaker (name and title) and a slide's section are drawn only as
58
+ // generated header/footer furniture (`organization: true` / `speaker: true` / `section: true` in a design), so they count
59
+ // only when a design asks for them; built-in variables copy their values into slide strings before this runs. Everything else is drawn text: titles, text blocks, list items, runs, table cells, chart labels,
62
60
  // categories and series names, code, quotes, metrics, timelines, furniture text.
63
61
  const UNDRAWN_KEYS = Object.freeze(["$schema", "assets", "catalogs", "notes", "id", "alt", "altText", "src", "url", "href", "link", "metadata", "extensions"]);
64
- const UNDRAWN_ROOT_KEYS = Object.freeze(["name", "description", "filename", "author", "authors", "creator", "speaker", "audience", "purpose", "tone", "takeaway", "duration", "tags", "narrative", "language", "license", "version", "keywords"]);
62
+ const UNDRAWN_ROOT_KEYS = Object.freeze(["name", "description", "filename", "author", "authors", "creator", "audience", "purpose", "tone", "takeaway", "duration", "tags", "narrative", "language", "license", "version", "keywords"]);
65
63
  const UNDRAWN_SLIDE_KEYS = Object.freeze(["beat"]);
66
64
  const UNDRAWN_VALUE = /^(?:https?|data|blob|pkg|file|mailto):\S*$/i;
67
65
 
68
- /** True when a design in the presentation turns on generated `field` furniture (`organization` or `section`). */
66
+ /** True when a design in the presentation turns on generated `field` furniture (`organization`, `speaker` or `section`). */
69
67
  function furnitureUses(presentation, field) {
70
68
  const designs = [presentation?.design, ...(Array.isArray(presentation?.slides) ? presentation.slides.map(slide => slide?.design) : [])];
71
69
  const pattern = new RegExp(`"${field}"\\s*:\\s*true`);
@@ -76,6 +74,7 @@ function furnitureUses(presentation, field) {
76
74
  export function* drawnStrings(presentation) {
77
75
  const undrawn = new Set(UNDRAWN_KEYS), undrawnRoot = new Set(UNDRAWN_ROOT_KEYS), undrawnSlide = new Set(UNDRAWN_SLIDE_KEYS);
78
76
  if (!furnitureUses(presentation, "organization")) undrawnRoot.add("organization");
77
+ if (!furnitureUses(presentation, "speaker")) undrawnRoot.add("speaker");
79
78
  if (!furnitureUses(presentation, "section")) undrawnSlide.add("section");
80
79
  function* visit(item, level) {
81
80
  if (typeof item === "string") { if (!UNDRAWN_VALUE.test(item)) yield item; }
@@ -92,21 +91,17 @@ export function* drawnStrings(presentation) {
92
91
 
93
92
  /**
94
93
  * The script profile of a presentation: core `resolveScriptFonts` output, exactly what the renderer plans
95
- * with. When the installed core has none (published core 0.11.0 and earlier) or it throws, the renderer
96
- * ignores the document language, so the profile is empty and Han text is Simplified Chinese, as drawn.
94
+ * with. When it throws, the renderer ignores the document language, so the profile is empty and Han text is
95
+ * Simplified Chinese, as drawn.
97
96
  */
98
97
  export function presentationScriptProfile(presentation) {
99
- if (typeof opfCore.resolveScriptFonts === "function") {
100
- try { return opfCore.resolveScriptFonts(presentation); } catch { /* the renderer falls back the same way */ }
101
- }
98
+ try { return resolveScriptFonts(presentation); } catch { /* the renderer falls back the same way */ }
102
99
  return {};
103
100
  }
104
101
 
105
102
  const servedScripts = () => new Set(BUNDLED_FONT_MANIFEST.packages.filter(item => item.pack === "scripts").flatMap(item => item.scripts));
106
103
  const cjkKeys = ["Jpan", "Hans", "Hant", "Kore"];
107
104
  const cjkCharacters = new Set(["Hani", "Hira", "Kana", "Hang", "Bopo"]);
108
- /** True when the renderer has per-character glyph fallback (FF-19 glyphFallbackFamilies). */
109
- const hasGlyphFallback = () => typeof scriptFontModule.glyphFallbackFamilies === "function";
110
105
 
111
106
  /**
112
107
  * ISO 15924 script whose pinned face a family resolves to: a proprietary script font (Yu Gothic: Jpan), a pinned face itself
@@ -116,7 +111,7 @@ function scriptOfFamily(family) {
116
111
  const name = String(family ?? "").toLowerCase();
117
112
  if (!name) return undefined;
118
113
  if (isSymbolEncodedFamily(name)) return SYMBOL_SCRIPT;
119
- const rule = scriptFontModule.SCRIPT_FONT_REPLACEMENTS.find(item => item.requestedFamily.toLowerCase() === name);
114
+ const rule = SCRIPT_FONT_REPLACEMENTS.find(item => item.requestedFamily.toLowerCase() === name);
120
115
  if (rule) return rule.script;
121
116
  return BUNDLED_FONT_MANIFEST.packages.find(item => item.pack === "scripts" && item.faces.some(face => face.family.toLowerCase() === name))?.scripts[0];
122
117
  }
@@ -144,10 +139,10 @@ function designScripts(presentation, profile, renderOptions) {
144
139
  }
145
140
  };
146
141
  note(profile);
147
- if (typeof opfCore.resolveScriptFonts === "function" && Array.isArray(presentation?.slides)) {
142
+ if (Array.isArray(presentation?.slides)) {
148
143
  presentation.slides.forEach((slide, slideIndex) => {
149
144
  if (!slide || typeof slide !== "object" || !slide.design || typeof slide.design !== "object") return;
150
- try { note(opfCore.resolveScriptFonts(presentation, { slideIndex })); } catch { /* the renderer reports it when it draws the slide */ }
145
+ try { note(resolveScriptFonts(presentation, { slideIndex })); } catch { /* the renderer reports it when it draws the slide */ }
151
146
  });
152
147
  }
153
148
  if (renderOptions?.catalogs) {
@@ -230,13 +225,12 @@ export function autoScriptSelection(presentation, options) {
230
225
  * renderer reports `missing-glyph` for them.
231
226
  */
232
227
  export function nextFallbackPackage(analysis, { covers, loaded }) {
233
- if (!hasGlyphFallback()) return undefined;
234
228
  const packages = BUNDLED_FONT_MANIFEST.packages.filter(item => item.pack === "scripts" && item.scripts.some(script => cjkKeys.includes(script)));
235
229
  const primary = new Set([...analysis.detected, ...(analysis.design ?? [])].filter(script => cjkKeys.includes(script))).size;
236
230
  if (packages.filter(item => loaded.has(item.name)).length >= primary + 1) return undefined;
237
231
  for (const character of analysis.cjk) {
238
232
  if (covers(character)) continue;
239
- for (const family of scriptFontModule.glyphFallbackFamilies(character, analysis.profile, analysis.profile?.serif === true)) {
233
+ for (const family of glyphFallbackFamilies(character, analysis.profile, analysis.profile?.serif === true)) {
240
234
  const pkg = packages.find(item => !loaded.has(item.name) && item.faces.some(face => face.family === family));
241
235
  if (pkg) return pkg;
242
236
  }
@@ -244,8 +238,7 @@ export function nextFallbackPackage(analysis, { covers, loaded }) {
244
238
  return undefined;
245
239
  }
246
240
 
247
- /** Drawn Han, kana or Hangul characters (at most `limit`) that no loaded script face covers. Empty without glyph fallback (nothing else would draw them). */
241
+ /** Drawn Han, kana or Hangul characters (at most `limit`) that no loaded script face covers. */
248
242
  export function uncoveredCjkCharacters(analysis, { covers, limit = 16 }) {
249
- if (!hasGlyphFallback()) return [];
250
243
  return [...analysis.cjk].filter(character => !covers(character)).slice(0, limit);
251
244
  }
@@ -16,8 +16,10 @@
16
16
  // is reported as a note. `glyphFallback: "none"` (strict mode) keeps the exact
17
17
  // faces and lets a missing glyph raise `missing-glyph`.
18
18
 
19
- import { fontPolicyFor } from "./font-policy.js";
20
- import { SYMBOL_PLACEHOLDER, SYMBOL_SCRIPT, mapSymbolText, symbolEncodingFor, symbolPreviewFaces } from "./symbol-fonts.js";
19
+ import { fontPolicyFor } from "@openpresentation/opf/font-policy";
20
+ import { scriptFontRole } from "@openpresentation/opf/composition";
21
+ import { SYMBOL_PLACEHOLDER, SYMBOL_SCRIPT, mapSymbolAdvances, symbolEncodingFamily, symbolPreviewFaces } from "./symbol-fonts.js";
22
+ export { scriptFontRole };
21
23
 
22
24
  const freeze = value => { if (value && typeof value === "object") { for (const child of Object.values(value)) freeze(child); Object.freeze(value); } return value; };
23
25
 
@@ -171,23 +173,10 @@ export function adjustedFontSize(size, adjust) {
171
173
  export const EMOJI_FONT_FAMILIES = SCRIPT_FONT_REPLACEMENTS.find(rule => rule.script === "Zsye").substitutes;
172
174
  const emojiFaces = new Set(EMOJI_FONT_FAMILIES);
173
175
 
174
- const eastAsianScripts = new Set(["Hani", "Hans", "Hant", "Hanb", "Jpan", "Kore", "Hang", "Jamo", "Hira", "Kana", "Hrkt", "Bopo", "Yiii"]);
175
- const complexScripts = new Set([
176
- "Arab", "Hebr", "Syrc", "Thaa", "Nkoo", "Adlm", "Rohg", "Mand", "Samr",
177
- "Deva", "Beng", "Guru", "Gujr", "Orya", "Taml", "Telu", "Knda", "Mlym", "Sinh",
178
- "Thai", "Laoo", "Tibt", "Mymr", "Khmr", "Mong", "Bali", "Java", "Lana", "Tale", "Talu", "Cakm", "Olck",
179
- ]);
180
176
  const rtlScripts = new Set(["Arab", "Hebr", "Syrc", "Thaa", "Nkoo", "Adlm", "Rohg", "Mand", "Samr"]);
181
177
  /** Scripts the latin slot covers directly; never split from the surrounding Latin run. */
182
178
  const latinGroup = new Set(["Latn", "Cyrl", "Grek", "Zyyy", "Zinh", "Zzzz"]);
183
179
 
184
- /** OOXML script slot for an ISO 15924 script code (the same table as core `scriptFontRole`). */
185
- export function scriptFontRole(script) {
186
- if (eastAsianScripts.has(script)) return "eastAsian";
187
- if (complexScripts.has(script)) return "complexScript";
188
- return "latin";
189
- }
190
-
191
180
  const detected = ["Latn", "Cyrl", "Grek", "Hani", "Hira", "Kana", "Hang", "Bopo", "Yiii", "Arab", "Hebr", "Syrc", "Thaa", "Nkoo", "Adlm", "Rohg", "Mand", "Samr",
192
181
  "Deva", "Beng", "Guru", "Gujr", "Orya", "Taml", "Telu", "Knda", "Mlym", "Sinh", "Thai", "Laoo", "Tibt", "Mymr", "Khmr", "Mong", "Bali", "Java", "Lana",
193
182
  "Tale", "Talu", "Cakm", "Olck", "Armn", "Geor", "Ethi", "Cher", "Tfng", "Vaii", "Zinh", "Zyyy"].map(code => [code, new RegExp(String.raw`^\p{Script=${code}}$`, "u")]);
@@ -564,7 +553,7 @@ export function createScriptFonts(profile = {}, measurement, options = {}) {
564
553
  const hex = item.code.toString(16).toUpperCase().padStart(2, "0");
565
554
  if (!entry.codes.includes(hex) && entry.codes.length < 32) { entry.codes.push(hex); if (entry.characters.length < 16) entry.characters.push(item.source); }
566
555
  };
567
- for (const item of mapSymbolText(encoding, text)) {
556
+ for (const item of mapSymbolAdvances(encoding, text)) {
568
557
  if (item.code === null) { foreign += item.source; continue; }
569
558
  flushForeign();
570
559
  let drawn = item.unicode, family = null, placeholder = false;
@@ -600,7 +589,7 @@ export function createScriptFonts(profile = {}, measurement, options = {}) {
600
589
  text = String(text ?? "");
601
590
  // A symbol-encoded family (resolved styles carry `symbolEncoding`; an unresolved style names the family itself) is mapped code by
602
591
  // code; `symbolEncoding: null` is the planner's own request for ordinary runs (characters that are not codes).
603
- const encoding = style.symbolEncoding === null ? undefined : style.symbolEncoding ?? symbolEncodingFor(style.fontFamily)?.family;
592
+ const encoding = style.symbolEncoding === null ? undefined : style.symbolEncoding ?? symbolEncodingFamily(style.fontFamily);
604
593
  if (encoding) return text ? planSymbols(text, style, encoding) : [{text, family: style.fontFamily, own: true}];
605
594
  // An emoji face (Noto Color Emoji for a Segoe UI Emoji run) draws emoji-presentation clusters only: its Latin text and digits
606
595
  // are planned per cluster below, where they take a text face (FF-45).
package/dist/svg.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import type { EmbeddedFont, ScriptFonts } from "./fonts.js";
1
+ import type { RenderFonts, ScriptFonts } from "./fonts.js";
2
+ import type { Finding } from "@openpresentation/opf";
2
3
  import type { SlideComposition, LayoutDiagnostic, TextMeasurement } from "@openpresentation/opf/composition";
3
4
  export declare const packageName = "@openpresentation/opf-render";
4
5
 
@@ -28,7 +29,7 @@ export declare const engineDefaults: Readonly<{
28
29
  pptx: Readonly<{ latin: "aptos"; ea: "microsoft-yahei"; cs: "nirmala-ui" }>;
29
30
  google: Readonly<{ latin: "roboto"; ea: "noto-sans-sc"; cs: "noto-sans" }>;
30
31
  }>;
31
- chartTypes: readonly ["stacked-column-3x", "stacked-area-3x", "line-with-markers-3x"];
32
+ chartTypes: readonly ["stacked-column", "stacked-area", "line-with-markers"];
32
33
  }>;
33
34
 
34
35
  export type RenderDiagnostic = LayoutDiagnostic | {
@@ -41,7 +42,7 @@ export type RenderDiagnostic = LayoutDiagnostic | {
41
42
  scripts: string[];
42
43
  characters: string[];
43
44
  } | {
44
- code: "unsupported-pattern" | "variable-example-used" | "date-needs-value" | "language-preview-unavailable" | "language-preview-unresolved" | "paragraph-direction-unavailable";
45
+ code: "unsupported-pattern" | "variable-example-used" | "variable-builtin-missing" | "date-needs-value" | "language-preview-unresolved";
45
46
  path: string;
46
47
  message: string;
47
48
  } | {
@@ -51,6 +52,12 @@ export type RenderDiagnostic = LayoutDiagnostic | {
51
52
  message: string;
52
53
  id: string;
53
54
  fallback: string;
55
+ } | {
56
+ /** A layout, theme or colour-scheme id matched no record: the slide composed with no layout record, the `minimal` theme or the `cool-horizon` colour scheme. Never an error. */
57
+ code: "unresolved-layout" | "unresolved-theme" | "unresolved-color-scheme";
58
+ path: string;
59
+ message: string;
60
+ id: string;
54
61
  } | {
55
62
  code: "unresolved-asset";
56
63
  path: string;
@@ -63,12 +70,18 @@ export type RenderDiagnostic = LayoutDiagnostic | {
63
70
  };
64
71
 
65
72
  export interface RenderSvgOptions {
66
- textMeasurement?: TextMeasurement;
73
+ /** Leave out slides marked `hidden: true`, as the player does. Default false: one SVG per slide. With true the result holds only the visible slides, in order, so an index no longer names the slide at that index. Read by the deck-level `renderSvg`; `renderSlideSvg` draws the slide it is given. */
74
+ skipHidden?: boolean;
75
+ /**
76
+ * The fonts the deck is laid out and drawn with: the handle `loadFonts()` returns (from `@openpresentation/opf-render/fonts-node` or
77
+ * `/fonts-browser`), or any object with a `textMeasurement` and, to embed faces in each SVG, an `embeddedFonts` list. Without it layout uses
78
+ * core's portable width estimate and the SVG names the design fonts without embedding them.
79
+ */
80
+ fonts?: RenderFonts;
67
81
  /** `"chain"` (default): a character the resolved face lacks is drawn with the first bundled face that has it, and reported as `font-glyph-fallback`. `"none"`: exact faces, a missing glyph raises `missing-glyph`. */
68
82
  glyphFallback?: "chain" | "none";
69
83
  /** Unscaled reference-pixel clearance around supplied vector text outlines; default 1. */
70
84
  textRasterPadding?: number;
71
- embeddedFonts?: EmbeddedFont[];
72
85
  strictAssets?: boolean;
73
86
  imageResolver?: (src: string | undefined, context: { asset: unknown; path: string }) => string | null | undefined;
74
87
  onDiagnostic?: (diagnostic: RenderDiagnostic) => void;
@@ -77,12 +90,11 @@ export interface RenderSvgOptions {
77
90
  /**
78
91
  * Values for the deck's template variables, keyed by variable id (core `resolveVariables`). A deck that uses
79
92
  * content variables, or is marked `template: true`, is resolved to a concrete deck first. A template previews with
80
- * each unfilled variable's example (reported as `variable-example-used`); a normal deck with an unfilled required
93
+ * each unfilled variable's example (reported as `variable-example-used`; a built-in such as `{{speaker.name}}` with no source value is reported as `variable-builtin-missing`); a normal deck with an unfilled required
81
94
  * variable throws `unfilled-variables`. `false` draws the document as authored, with `{{id}}` tokens and
82
95
  * `var:id` references visible and nothing resolved: the view an editor uses while the template itself is edited.
83
96
  */
84
97
  variables?: Record<string, unknown> | false;
85
- slideIndex?: number;
86
98
  /**
87
99
  * Today's calendar date (ISO YYYY-MM-DD) for `date: true` header/footer furniture. The renderer
88
100
  * never reads a clock; without it a current date is reported as unresolved content.
@@ -99,12 +111,12 @@ export interface RenderSvgOptions {
99
111
  }
100
112
 
101
113
  export interface SvgToPngOptions {
114
+ /** The fonts the conversion draws with (Node): see `SvgToPngOptions` in the main entry. */
115
+ fonts?: RenderFonts;
102
116
  scale?: number;
103
117
  background?: string;
104
118
  dpi?: number;
105
- useBundledFonts?: boolean;
106
- loadSystemFonts?: boolean;
107
- fontFiles?: string[];
119
+ /** Extra directories of font files to draw with. */
108
120
  fontDirs?: string[];
109
121
  defaultFontFamily?: string;
110
122
  sansSerifFamily?: string;
@@ -128,7 +140,8 @@ export interface ResolvedSlide {
128
140
  index: number;
129
141
  path: string;
130
142
  slide: unknown;
131
- layout: unknown;
143
+ /** The layout record the slide names; absent when it names none or an id no catalog has (the slide then composes with no layout). */
144
+ layout?: unknown;
132
145
  design: {
133
146
  theme: unknown;
134
147
  colorScheme: unknown;
@@ -157,15 +170,18 @@ export interface RenderBinding {
157
170
  export declare class OPFRenderError extends Error {
158
171
  readonly code: string;
159
172
  readonly details: Record<string, unknown>;
160
- readonly issues?: unknown[];
173
+ /** `invalid-opf`: the error findings of core `validate(presentation, { only: ["format"] })` (also `details.findings`; `details.report` is the whole report). */
174
+ readonly findings?: Finding[];
161
175
  readonly path?: string;
162
176
  constructor(code: string, message: string, details?: Record<string, unknown>);
163
177
  }
164
178
 
165
179
  export declare function resolvePresentation(input: unknown, options?: RenderSvgOptions): ResolvedPresentation;
166
180
 
167
- export declare function renderSvg(input: unknown, options?: RenderSvgOptions): string;
181
+ /** The SVG of every slide of the deck, in order. */
182
+ export declare function renderSvg(input: unknown, options?: RenderSvgOptions): string[];
168
183
 
169
- export declare function renderSvgDeck(input: unknown, options?: RenderSvgOptions): string[];
184
+ /** The SVG of one slide of the deck (`index` is zero-based; out of range throws `slide-index-out-of-range`). */
185
+ export declare function renderSlideSvg(input: unknown, index: number, options?: RenderSvgOptions): string;
170
186
 
171
187