@openpresentation/opf-render 0.17.1 → 0.18.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/fonts.d.ts CHANGED
@@ -34,14 +34,14 @@ export interface FontRegistryOptions {
34
34
  export declare const FONT_COMPATIBILITY: readonly Readonly<{requestedFamily:string;substitutes:readonly string[];compatibility:"metric"|"visual";weights?:readonly number[];weight?:number;source?:string;measured?:FontReplacementMeasurement;decision?:string;metricModeFallback?:true;licenseClass?:FontPolicyEntry['licenseClass'];note:string}>[];
35
35
  export declare const EXPERIMENTAL_FONT_CANDIDATES: readonly Readonly<{requestedFamily:string;substitute:string;source:string;note:string}>[];
36
36
  /**
37
- * What the deck-level functions read from their `fonts` option: `renderSvg`, `renderSlideSvg` and `<opf-deck>` take the measurement and
38
- * the faces to embed; `svgToPng` and `svgToPdf` (Node) take the font files; core `paginate`, `validate` and `resolveSlideContext` take the
37
+ * What the deck-level functions read from their `fonts` option: `toSvg` and `<opf-deck>` take the measurement and
38
+ * the faces to embed; `toPng` and `toPdf` (Node) take the font files; core `paginate`, `validate` and `resolveSlideContext` take the
39
39
  * measurement. The object `loadFonts()` returns is a `FontsHandle`, which extends this; any object with these fields works.
40
40
  */
41
41
  export interface RenderFonts {
42
42
  /** Measures text with the real faces. Without it layout uses core's portable estimate. */
43
43
  textMeasurement?: TextMeasurement;
44
- /** Faces `renderSvg` may write into an SVG as @font-face data: each only into the SVGs whose text draws it (its family, weight and style), unless flagged `embed: "always"` (RR-61). */
44
+ /** Faces `toSvg` may write into an SVG as @font-face data: each only into the SVGs whose text draws it (its family, weight and style), unless flagged `embed: "always"` (RR-61). */
45
45
  embeddedFonts?: readonly EmbeddedFont[];
46
46
  /** Font files the Node raster and PDF conversions draw with. */
47
47
  fontFiles?: readonly string[];
@@ -49,14 +49,14 @@ export interface RenderFonts {
49
49
  useBundledFonts?: boolean;
50
50
  /** Whether the raster conversion loads system fonts. Default false (the output never depends on the machine). */
51
51
  loadSystemFonts?: boolean;
52
- /** The outline engine `textAsPaths: true` draws text with (RR-64); a `loadFonts` handle carries one over its registry's faces. */
52
+ /** The outline engine `text: "paths"` draws text with (RR-64); a `loadFonts` handle carries one over its registry's faces. */
53
53
  outlines?: TextOutlines;
54
54
  /** The subset engine (RR-65): each face an SVG embeds is cut to the characters the slide draws. The Node `loadFonts` handle carries one; a browser handle with `subsetWasm`. */
55
55
  subsets?: FontSubsets;
56
56
  }
57
57
  /** Cuts an embedded face to code points (RR-65), with hb-subset; a face whose license, Reserved Font Name or fsType does not allow it keeps its own data URL. */
58
58
  export interface FontSubsets { subsetDataUrl(font: EmbeddedFont, codePoints: Iterable<number>): string }
59
- /** Draws a slide's text as glyph outlines (RR-64). Read by `renderSvg`; hosts pass the handle and `textAsPaths: true`. */
59
+ /** Draws a slide's text as glyph outlines (RR-64). Read by `toSvg`; hosts pass the handle and `text: "paths"`. */
60
60
  export interface TextOutlines { outlineSlideText(content: string[], options: object): { content: string[]; defs: string } }
61
61
  /** What `fonts.ensure(presentation)` loaded: the script packages and vendored faces that were missing, and drawn CJK characters no face covers. */
62
62
  export interface EnsureResult { scripts: string[]; lazy: LazyFont[]; uncovered: string[] }
@@ -66,7 +66,7 @@ export interface FontsHandle extends RenderFonts {
66
66
  readonly embeddedFonts: EmbeddedFont[];
67
67
  /** The face registry behind the handle (shaping, resolution, lazy and script loading). */
68
68
  readonly registry: FontRegistry;
69
- /** The outline engine for `textAsPaths: true` (RR-64), over this registry's faces, including faces `ensure` loads later. */
69
+ /** The outline engine for `text: "paths"` (RR-64), over this registry's faces, including faces `ensure` loads later. */
70
70
  readonly outlines: TextOutlines;
71
71
  /** The pinned manifest of the bundled font packages. */
72
72
  readonly manifest: { readonly version: number; readonly packages: readonly object[] };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { RenderFonts, ScriptFonts } from "./fonts.js";
2
- import type { Catalog, Finding, UnresolvedReferenceDiagnostic } from "@openpresentation/opf";
2
+ import type { Catalog, Finding, SlideSelection, UnresolvedReferenceDiagnostic } from "@openpresentation/opf";
3
+ export type { SlideSelection } from "@openpresentation/opf";
3
4
  import type { SlideComposition, LayoutDiagnostic, TextMeasurement } from "@openpresentation/opf/composition";
4
5
  export declare const packageName = "@openpresentation/opf-render";
5
6
 
@@ -49,8 +50,8 @@ export type RenderDiagnostic = LayoutDiagnostic | {
49
50
  placeholder: "label" | "icon";
50
51
  };
51
52
 
52
- export interface RenderSvgOptions {
53
- /** 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. */
53
+ export interface ToSvgOptions {
54
+ /** Leave out slides marked `hidden: true` when drawing the whole deck, as the player does. Default false: one SVG per slide. A slide number or selection draws exactly the slides it names. */
54
55
  skipHidden?: boolean;
55
56
  /**
56
57
  * The fonts the deck is laid out and drawn with: the handle `loadFonts()` returns (from `@openpresentation/opf-render/fonts-node` or
@@ -58,10 +59,12 @@ export interface RenderSvgOptions {
58
59
  * core's portable width estimate and the SVG names the design fonts without embedding them.
59
60
  */
60
61
  fonts?: RenderFonts;
61
- /** `false` writes no `@font-face` data into the SVG (RR-61): the fonts handle still measures and the SVG still names the families, for a host whose page already has the faces (a browser `loadFonts` handle adds them to the document). Default true: each SVG embeds the faces of `fonts.embeddedFonts` its own text draws. */
62
- embedFonts?: boolean;
63
62
  /**
64
- * `true` draws every `<text>` as glyph outlines (RR-64): `<use>` of outlines kept once per slide in `<defs>`, so the SVG needs
63
+ * How the SVG draws text (RR-74). `"fonts"` (default): each SVG embeds, as `@font-face` data, the faces of `fonts.embeddedFonts` its
64
+ * own text draws (RR-61). `"system"`: no `@font-face` data; the fonts handle still measures and the SVG still names the families,
65
+ * for a host whose page already has the faces (a browser `loadFonts` handle adds them to the document).
66
+ *
67
+ * `"paths"`: every `<text>` drawn as glyph outlines (RR-64): `<use>` of outlines kept once per slide in `<defs>`, so the SVG needs
65
68
  * no font and looks the same in every viewer (about 40 KB a slide over the core example decks, against 0.5 to 4 MB of embedded
66
69
  * faces). Needs the fonts handle `loadFonts()` returns (`text-as-paths-needs-fonts` otherwise). The text is no longer text: it
67
70
  * cannot be edited in place. The glyphs are shaped by HarfBuzz (the browser's shaper; the Node handle always, a browser handle
@@ -70,9 +73,9 @@ export interface RenderSvgOptions {
70
73
  * A run stays text, reported as `text-as-paths-fallback` with a `reason`, when its font is a colour or bitmap font
71
74
  * (`"colour-font"`), its face's fsType restricts embedding (`"restricted"`), or it has no pinned width and HarfBuzz and the
72
75
  * layout's measurement disagree on it (`"shaping"`). Use it for thumbnails, previews and portable SVG files, not for an editing
73
- * surface or the vector PDF (which keeps real text from `<text>` SVG). Default false.
76
+ * surface or the vector PDF (which keeps real text from `<text>` SVG).
74
77
  */
75
- textAsPaths?: boolean;
78
+ text?: "fonts" | "system" | "paths";
76
79
  /**
77
80
  * `false` embeds whole faces (RR-65). By default, with a fonts handle that carries `subsets` (the Node `loadFonts` handle, a browser
78
81
  * handle given `subsetWasm`), each face an SVG embeds is cut to the characters the slide draws, keeping every layout feature, so the
@@ -118,18 +121,18 @@ export interface RenderSvgOptions {
118
121
  strictReferences?: boolean;
119
122
  }
120
123
 
121
- export interface SvgToPngOptions {
124
+ /** The options of `toPng`: the conversion's own, and the `toSvg` options that draw a deck. */
125
+ export interface ToPngOptions extends Omit<ToSvgOptions, "fonts"> {
122
126
  /**
123
- * The fonts the conversion draws with: the handle `loadFonts()` returns from `/fonts-node` (its `fontFiles`; `useBundledFonts` and
124
- * `loadSystemFonts` say whether the bundled base faces are added, default true, and whether system fonts load, default false). Without
125
- * it the bundled base faces draw. The same handle goes to `renderSvg`, so the preview and the PNG use the same faces.
127
+ * The fonts the deck is laid out and drawn with: the handle `loadFonts()` returns from `/fonts-node` (it measures the deck, and its
128
+ * `fontFiles` draw; `useBundledFonts` and `loadSystemFonts` say whether the bundled base faces are added, default true, and whether
129
+ * system fonts load, default false), so the preview and the PNG use the same faces. For SVG input it may instead be font folders
130
+ * (a path or a list of paths), whose faces are added to the bundled base faces. Without it the bundled base faces draw.
126
131
  */
127
- fonts?: RenderFonts;
132
+ fonts?: RenderFonts | string | readonly string[];
128
133
  scale?: number;
129
134
  background?: string;
130
135
  dpi?: number;
131
- /** Extra directories of font files to draw with. */
132
- fontDirs?: string[];
133
136
  defaultFontFamily?: string;
134
137
  sansSerifFamily?: string;
135
138
  monospaceFamily?: string;
@@ -198,14 +201,14 @@ export interface PdfMetadata {
198
201
  modificationDate?: Date | string;
199
202
  }
200
203
 
201
- export interface SvgToPdfOptions extends SvgToPngOptions {
204
+ export interface ToPdfOptions extends Omit<ToPngOptions, "onDiagnostic"> {
202
205
  /**
203
- * `"vector"` (default): shapes, gradients and patterns as PDF vector graphics, pictures as images, text as real text
204
- * with embedded font subsets (selectable, searchable, correct copy and paste); the fonts are the bundled files, the `fonts` handle's
205
- * `fontFiles` and the `fontDirs` you supply, never system fonts. `"raster"`: each slide an image, as before.
206
+ * `true` draws each slide as an image (`scale`). Default false, a vector PDF: shapes, gradients and patterns as PDF vector graphics,
207
+ * pictures as images, text as real text with embedded font subsets (selectable, searchable, correct copy and paste); the fonts are
208
+ * the bundled files, the `fonts` handle's `fontFiles` and the font folders you supply, never system fonts.
206
209
  */
207
- mode?: "vector" | "raster";
208
- /** Vector mode paints the page this colour first (default white, as raster mode composites on white); `"none"` leaves it unpainted. */
210
+ raster?: boolean;
211
+ /** A vector PDF paints the page this colour first (default white, as a raster PDF composites on white); `"none"` leaves it unpainted. */
209
212
  background?: string;
210
213
  /** Vector only. Family drawn for the generic `serif`; default `defaultFontFamily`. */
211
214
  serifFamily?: string;
@@ -219,8 +222,8 @@ export interface SvgToPdfOptions extends SvgToPngOptions {
219
222
  strict?: boolean;
220
223
  /** Vector only. Pixel density of the rare element rasterized because it has no vector form; default 2. */
221
224
  rasterFallbackScale?: number;
222
- /** Vector only. Receives font embedding reports, substitutions, fallbacks and unsupported features. */
223
- onDiagnostic?: (diagnostic: PdfDiagnostic) => void;
225
+ /** Vector only: font embedding reports, substitutions, fallbacks and unsupported features; given a deck, also the diagnostics of drawing it. */
226
+ onDiagnostic?: (diagnostic: PdfDiagnostic | RenderDiagnostic) => void;
224
227
  /** Vector only. Checked between pages; an abort rejects with the signal's reason. */
225
228
  signal?: AbortSignal;
226
229
  /** Vector only. Called after each page is written. */
@@ -278,14 +281,32 @@ export declare class OPFRenderError extends Error {
278
281
  constructor(code: string, message: string, details?: Record<string, unknown>);
279
282
  }
280
283
 
281
- export declare function resolvePresentation(input: unknown, options?: RenderSvgOptions): ResolvedPresentation;
284
+ export declare function resolvePresentation(input: unknown, options?: ToSvgOptions): ResolvedPresentation;
282
285
 
283
- /** The SVG of every slide of the deck, in order. */
284
- export declare function renderSvg(input: unknown, options?: RenderSvgOptions): string[];
286
+ /** An SVG slide the renderer drew: its text, or its bytes. */
287
+ export type SvgSlide = string | Uint8Array;
285
288
 
286
- /** The SVG of one slide of the deck (`index` is zero-based; out of range throws `slide-index-out-of-range`). */
287
- export declare function renderSlideSvg(input: unknown, index: number, options?: RenderSvgOptions): string;
289
+ /**
290
+ * The deck as SVG (RR-73). `toSvg(deck)`: every slide, in order (without the hidden ones under `skipHidden`). `toSvg(deck, 3)`: the
291
+ * third slide (slides count from 1; out of range throws `slide-out-of-range`). `toSvg(deck, "1-3")` or `toSvg(deck, [1, 3])`: the
292
+ * selected slides (a malformed selection throws `invalid-slide-selection`). The second argument may be the options instead.
293
+ */
294
+ export declare function toSvg(input: unknown, options?: ToSvgOptions): string[];
295
+ export declare function toSvg(input: unknown, slide: number, options?: ToSvgOptions): string;
296
+ export declare function toSvg(input: unknown, slides: Exclude<SlideSelection, number>, options?: ToSvgOptions): string[];
288
297
 
289
- export declare function svgToPng(svg: string | Uint8Array, options?: SvgToPngOptions): Promise<Uint8Array>;
298
+ /**
299
+ * PNG (RR-73) of a deck, drawn with `toSvg` (the slide arguments are `toSvg`'s: one PNG for a slide number, a list otherwise), or of
300
+ * SVG the renderer drew (one SVG gives one PNG, a list a list). A slide selection with SVG input throws `invalid-conversion-option`.
301
+ */
302
+ export declare function toPng(svg: Uint8Array | `<${string}`, options?: ToPngOptions): Promise<Uint8Array>;
303
+ export declare function toPng(svgs: readonly SvgSlide[], options?: ToPngOptions): Promise<Uint8Array[]>;
304
+ export declare function toPng(deck: unknown, slide: number, options?: ToPngOptions): Promise<Uint8Array>;
305
+ export declare function toPng(deck: unknown, slides: Exclude<SlideSelection, number>, options?: ToPngOptions): Promise<Uint8Array[]>;
306
+ export declare function toPng(deck: object, options?: ToPngOptions): Promise<Uint8Array[]>;
307
+ /** A string is SVG when it starts with `<`, and the deck's JSON text otherwise. */
308
+ export declare function toPng(source: string, options?: ToPngOptions): Promise<Uint8Array | Uint8Array[]>;
290
309
 
291
- export declare function svgToPdf(svgs: string | Uint8Array | Array<string | Uint8Array>, options?: SvgToPdfOptions): Promise<Uint8Array>;
310
+ /** One PDF (RR-73), a page per slide: of a deck drawn with `toSvg` (every slide, or a selection: `toPdf(deck, "2-4")`), or of SVG slides. */
311
+ export declare function toPdf(source: unknown, options?: ToPdfOptions): Promise<Uint8Array>;
312
+ export declare function toPdf(deck: unknown, slides: SlideSelection, options?: ToPdfOptions): Promise<Uint8Array>;
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
1
  export * from './svg.js';
2
- export {svgToPng,svgToPdf} from './raster.js';
2
+ export {toPng,toPdf} from './raster.js';
@@ -1,6 +1,6 @@
1
1
  import { fontPolicyFor } from "@openpresentation/opf/font-policy";
2
2
  import { pickFace } from "./font-registry.js";
3
- import { renderSvg, resolvePresentation } from "./svg.js";
3
+ import { resolvePresentation, toSvg } from "./svg.js";
4
4
  export { lazyFontEntries, lazyFontList } from "./lazy-font-list.js";
5
5
 
6
6
  /**
@@ -17,7 +17,7 @@ const lc = value => String(value).trim().toLowerCase();
17
17
 
18
18
  /**
19
19
  * Render options that decide how a document resolves (catalogs, validate, date, ...) are forwarded from the host's
20
- * `renderSvg` options. The analysis supplies its own measurement and reports nothing: callbacks, the host's fonts handle
20
+ * `toSvg` options. The analysis supplies its own measurement and reports nothing: callbacks, the host's fonts handle
21
21
  * (measurement and embedded fonts), asset resolution and tracing are dropped.
22
22
  */
23
23
  const NOT_RESOLVE_OPTIONS = new Set(["fonts", "onDiagnostic", "trace", "glyphFallback", "imageResolver", "strictAssets", "signal"]);
@@ -27,10 +27,10 @@ function resolveOptionsOf(options) {
27
27
 
28
28
  /**
29
29
  * The font families a presentation's slides resolve (heading, body and code roles of every slide, including per-slide
30
- * design overrides), as `renderSvg` resolves them with the same `options` (`catalogs`, ...). This is the role families,
30
+ * design overrides), as `toSvg` resolves them with the same `options` (`catalogs`, ...). This is the role families,
31
31
  * not what is drawn: use `presentationFaces` for that.
32
32
  *
33
- * A document that does not resolve throws the error `renderSvg` throws for it (an OPFRenderError such as
33
+ * A document that does not resolve throws the error `toSvg` throws for it (an OPFRenderError such as
34
34
  * `catalog-resolution-failed` for a layout id no catalog has, or `invalid-opf`), never an empty set: a document whose layout comes only from
35
35
  * `options.catalogs` needs those catalogs here too.
36
36
  */
@@ -57,7 +57,7 @@ function registryView({ faces, policy = "none", aliases = new Map(), fallbackFam
57
57
 
58
58
  /**
59
59
  * The faces a preview of the presentation draws, as `[{family, weight, italic}]`, sorted by family, style and weight. It is
60
- * the renderer's own layout and painting run with a recording measurement, so it follows exactly what `renderSvg` measures
60
+ * the renderer's own layout and painting run with a recording measurement, so it follows exactly what `toSvg` measures
61
61
  * and draws: the heading and body roles of every slide that draws them, bold and italic runs of rich text, table and cell
62
62
  * styles, header and footer furniture, chart labels, metrics, quotes and timelines, the code role only where code is
63
63
  * drawn, per-slide design overrides, font scheme and theme resolution, and script slots (a script run is requested in its
@@ -73,8 +73,8 @@ function registryView({ faces, policy = "none", aliases = new Map(), fallbackFam
73
73
  * family found. The result is the faces the registry paints, for example `Intos 700` for a 600-weight label of an Aptos
74
74
  * deck. A style the registry cannot resolve is left as named (drawing reports it).
75
75
  *
76
- * `options` are the `renderSvg` options the document resolves with (`catalogs`, `date`, `validate`, ...). A document that
77
- * does not resolve throws what `renderSvg` throws for it.
76
+ * `options` are the `toSvg` options the document resolves with (`catalogs`, `date`, `validate`, ...). A document that
77
+ * does not resolve throws what `toSvg` throws for it.
78
78
  * @param {unknown} presentation
79
79
  * @param {object} [options]
80
80
  * @param {{faces: Iterable<{family: string, weight: number, italic: boolean}>, policy?: "none"|"metric"|"visual", aliases?: ReadonlyMap<string, string>, fallbackFamily?: string}} [registry]
@@ -98,7 +98,7 @@ export function presentationFaces(presentation, options = {}, registry) {
98
98
  measure(text, size, style) { if (text !== "") record(settle(style)); return String(text).length * size * RECORDED_EM; },
99
99
  resolveStyle: settle,
100
100
  };
101
- renderSvg(presentation, { ...resolveOptionsOf(options), fonts: { textMeasurement } });
101
+ toSvg(presentation, { ...resolveOptionsOf(options), fonts: { textMeasurement } });
102
102
  return [...seen.values()].sort((a, b) => a.family < b.family ? -1 : a.family > b.family ? 1 : Number(a.italic) - Number(b.italic) || a.weight - b.weight);
103
103
  }
104
104
 
@@ -106,8 +106,8 @@ export function presentationFaces(presentation, options = {}, registry) {
106
106
  * The vendored faces (of `lazy`) a presentation draws that the registry does not hold: what a registry loads before it can
107
107
  * paint the document as Node paints it with every vendored face loaded. `held` lists the registry's faces
108
108
  * (`registry.describeFaces()`), `loaded` the lazy files already loaded; `policy`, `aliases` and `fallbackFamily` are the
109
- * registry's. `renderOptions` are the `renderSvg` options the document resolves with (`catalogs`, ...), and a document that
110
- * does not resolve throws what `renderSvg` throws for it. Face level: a bold or italic run added by an edit adds just that face.
109
+ * registry's. `renderOptions` are the `toSvg` options the document resolves with (`catalogs`, ...), and a document that
110
+ * does not resolve throws what `toSvg` throws for it. Face level: a bold or italic run added by an edit adds just that face.
111
111
  */
112
112
  export function lazyFacesNeeded(presentation, renderOptions, { lazy, held, loaded = new Set(), policy = "none", aliases = new Map(), fallbackFamily }) {
113
113
  const heldFaces = [...held], heldKeys = new Set(heldFaces.map(faceKeyOf));
package/dist/pdf-fonts.js CHANGED
@@ -4,7 +4,7 @@ import { isTrueTypeOutlines, subsetTrueType } from "./pdf-subset.js";
4
4
  import { hex, name, num, sha256, textString, utf16Hex } from "./pdf-writer.js";
5
5
 
6
6
  // Font selection, shaping and embedding for the vector PDF export (RR-12). Fonts come only from the faces the
7
- // caller supplies (the same bundled font files, `fontFiles` and `fontDirs` the PNG preview draws with, and the
7
+ // caller supplies (the same bundled font files, fonts handle or font folders the PNG preview draws with, and the
8
8
  // `@font-face` data of the SVG itself); nothing is read from the system and nothing is fetched. A face whose
9
9
  // OS/2 embedding bits forbid embedding is never embedded.
10
10
 
@@ -89,7 +89,7 @@ class Converter {
89
89
  this.reported.add(key);
90
90
  this.unsupported(d.code, d.message, { fontFamily: d.fontFamily, codePoint: d.codePoint });
91
91
  },
92
- unavailable: (families) => this.error("pdf-font-unavailable", `No embeddable font face is available for ${families.length ? `'${families.join("', '")}'` : "the requested text"}. Supply font files (fontFiles/fontDirs) or keep useBundledFonts enabled.`, {}),
92
+ unavailable: (families) => this.error("pdf-font-unavailable", `No embeddable font face is available for ${families.length ? `'${families.join("', '")}'` : "the requested text"}. Pass the fonts handle or font folders as fonts, or keep useBundledFonts enabled.`, {}),
93
93
  };
94
94
  }
95
95
 
@@ -1228,7 +1228,7 @@ async function listFonts(directory, { readdir, path }) {
1228
1228
  }
1229
1229
 
1230
1230
  /**
1231
- * Convert SVG slides to a vector PDF. `options` carries the resolved conversion settings (see svgToPdf in raster.js).
1231
+ * Convert SVG slides to a vector PDF. `options` carries the resolved conversion settings (see toPdf in raster.js).
1232
1232
  */
1233
1233
  export async function svgsToVectorPdf(svgs, options) {
1234
1234
  const converter = new Converter(options);
package/dist/pdf.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export { svgToPdf } from "./index.js";
2
- export type { SvgToPdfOptions, PdfDiagnostic, PdfMetadata } from "./index.js";
1
+ export { toPdf } from "./index.js";
2
+ export type { ToPdfOptions, PdfDiagnostic, PdfMetadata } from "./index.js";
package/dist/pdf.js CHANGED
@@ -1,4 +1,4 @@
1
- // RR-63: the PDF format entry (Node). The default vector `svgToPdf` needs no converter for text and shapes; it loads the optional peer
2
- // `sharp` for pictures and `@resvg/resvg-js` for the rare element it rasterizes, and `mode: "raster"` also loads `pdf-lib`. A converter
1
+ // RR-63: the PDF format entry (Node). The default vector `toPdf` needs no converter for text and shapes; it loads the optional peer
2
+ // `sharp` for pictures and `@resvg/resvg-js` for the rare element it rasterizes, and `raster: true` also loads `pdf-lib`. A converter
3
3
  // that is not installed rejects with `OPFRenderError` code `converter-missing`. For a page use `@openpresentation/opf-render/export-browser`.
4
- export { svgToPdf } from "./raster.js";
4
+ export { toPdf } from "./raster.js";
package/dist/player.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { BrowserFontsHandle } from "./fonts-browser.js";
2
- import type { RenderSvgOptions } from "./svg.js";
2
+ import type { ToSvgOptions } from "./svg.js";
3
3
 
4
4
  /**
5
5
  * Why a deck could not be loaded, drawn or presented. `code` is stable: `invalid-document`, `fetch-failed`, `fonts-unavailable`,
@@ -19,8 +19,8 @@ export type BlankMode = "none" | "black" | "white";
19
19
  export interface PresentOptions {
20
20
  /** The self-hosted font root URL (see `copyPreviewFonts`), or a browser fonts handle the page already has (`loadFonts` from `/fonts-browser`). Without it layout uses estimated widths and system fonts. */
21
21
  fonts?: string | BrowserFontsHandle;
22
- /** Extra `renderSlideSvg` options (catalogs, imageResolver, date, ...). */
23
- renderOptions?: Partial<RenderSvgOptions>;
22
+ /** Extra `toSvg` options (catalogs, imageResolver, date, ...). */
23
+ renderOptions?: Partial<ToSvgOptions>;
24
24
  /** 1-based slide to start on, in the sequence that plays. Default 1. */
25
25
  startSlide?: number;
26
26
  /** Play hidden slides too. Default false. */
package/dist/player.js CHANGED
@@ -632,7 +632,7 @@ async function readSource(source, signal) {
632
632
  * player covers the page at once (and asks for full screen), then resolves with the running session when the first slide is
633
633
  * drawn. Call it from a click or key handler: full screen and the speaker view both need a user gesture.
634
634
  *
635
- * Options: `fonts` (the self-hosted font root URL, or a browser fonts handle from `loadFonts`), `renderOptions` (extra `renderSlideSvg` options),
635
+ * Options: `fonts` (the self-hosted font root URL, or a browser fonts handle from `loadFonts`), `renderOptions` (extra `toSvg` options),
636
636
  * `startSlide` (1-based), `includeHidden`, `fullscreen` (default true), `presenterView` (open the speaker view too),
637
637
  * `role: "presenter"` (this window is the speaker view: for a second tab or window of your page), `channel` (name, or `false`
638
638
  * for no sync), `container`, `signal` and `now` (an injectable clock).
package/dist/png.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export { svgToPng } from "./index.js";
2
- export type { SvgToPngOptions } from "./index.js";
1
+ export { toPng } from "./index.js";
2
+ export type { ToPngOptions } from "./index.js";
package/dist/png.js CHANGED
@@ -1,4 +1,4 @@
1
- // RR-63: the PNG format entry (Node). `svgToPng` loads the optional peers `@resvg/resvg-js` (and `sharp` for WebP and rotated JPEG
1
+ // RR-63: the PNG format entry (Node). `toPng` loads the optional peers `@resvg/resvg-js` (and `sharp` for WebP and rotated JPEG
2
2
  // pictures) on first use and rejects with `OPFRenderError` code `converter-missing` when they are not installed. For a page use
3
3
  // `@openpresentation/opf-render/export-browser`.
4
- export { svgToPng } from "./raster.js";
4
+ export { toPng } from "./raster.js";
package/dist/raster.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import {prepareRasterImages} from './raster-images.js';
2
- import {OPFRenderError,packageName} from './svg.js';
2
+ import {OPFRenderError,packageName,toSvg} from './svg.js';
3
+ import {slideArguments,slideSources} from './slide-sources.js';
3
4
  import {separateLigatures} from './font-compatibility.js';
4
5
  import {monochromeColorFonts,rasterFontFiles} from './color-fonts.js';
5
6
  import {pinScriptClusters} from './raster-text.js';
@@ -7,38 +8,47 @@ import {loadConverter} from './converters.js';
7
8
  const DEFAULT_DIMENSIONS = { width: 1280, height: 720 };
8
9
  const DEFAULT_RASTER_SCALE = 1;
9
10
  const DEFAULT_RASTER_BACKGROUND = "#FFFFFF";
10
- // RR-12: PDF output is vector (selectable text, vector drawing) unless a caller asks for the raster-backed compatibility mode.
11
- const DEFAULT_PDF_MODE = "vector";
12
11
  let bundledFontFilesCache=null;
13
12
 
14
- // The faces a raster or PDF conversion draws with, from the fonts handle (`loadFonts()` from `/fonts-node`): its `fontFiles`, and
15
- // whether the bundled base faces are added (`useBundledFonts`, default true; a Node handle already holds its own, so it says false) and
16
- // whether system fonts load (`loadSystemFonts`, default false). Without a handle the bundled base faces draw.
13
+ // The faces a raster or PDF conversion draws with (RR-74: one `fonts`). The fonts handle (`loadFonts()` from `/fonts-node`) gives its
14
+ // `fontFiles`, whether the bundled base faces are added (`useBundledFonts`, default true; a Node handle already holds its own, so it
15
+ // says false) and whether system fonts load (`loadSystemFonts`, default false). Font folders (a path or a list of paths) add every
16
+ // face in them to the bundled base faces. Without either the bundled base faces draw.
17
17
  function fontSettings(options) {
18
18
  const fonts = options.fonts ?? {};
19
- return { fontFiles: stringArray(fonts.fontFiles), useBundledFonts: fonts.useBundledFonts !== false, loadSystemFonts: fonts.loadSystemFonts === true };
19
+ if (typeof fonts === "string" || Array.isArray(fonts)) return { fontFiles: [], fontDirs: stringArray([fonts].flat()), useBundledFonts: true, loadSystemFonts: false };
20
+ return { fontFiles: stringArray(fonts.fontFiles), fontDirs: [], useBundledFonts: fonts.useBundledFonts !== false, loadSystemFonts: fonts.loadSystemFonts === true };
20
21
  }
21
22
 
22
23
  /**
23
- * Convert one SVG (the output of `renderSvg` or `renderSlideSvg`) to PNG bytes. `fonts` is the handle `loadFonts()` returns: the raster draws
24
- * with its font files, so the preview and the PNG use the same faces. Without it the bundled base faces draw.
24
+ * PNG (RR-73): a deck drawn with `toSvg` (`toPng(deck)` gives one PNG per slide, `toPng(deck, 3)` the third slide's PNG,
25
+ * `toPng(deck, "1-3")` those slides'), or SVG the renderer drew (one SVG gives one PNG, a list a list). `fonts` is the handle
26
+ * `loadFonts()` returns, so the preview and the PNG draw with the same faces, or font folders for SVG input. Without either the
27
+ * bundled base faces draw.
25
28
  */
26
- export async function svgToPng(svg, options = {}) {
27
- const rendered = await rasterizeSvg(svg, options);
28
- return rendered.png;
29
+ export async function toPng(source, slides, options) {
30
+ ({ slides, options } = slideArguments(slides, options));
31
+ const { svgs, one } = slideSources(source, slides, options, toSvg);
32
+ const pngs = [];
33
+ for (const svg of svgs) pngs.push((await rasterizeSvg(svg, options)).png);
34
+ return one ? pngs[0] : pngs;
29
35
  }
30
36
 
31
- /** Convert SVG slides (the output of `renderSvg`) to a PDF, one page each. `fonts` is as for `svgToPng`; `mode` is `"vector"` (default) or `"raster"`. */
32
- export async function svgToPdf(svgs, options = {}) {
33
- const pdfInputs = normalizeSvgList(svgs);
37
+ /**
38
+ * One PDF (RR-73), a page per slide, of a deck drawn with `toSvg` (all slides, or a selection: `toPdf(deck, "2-4")`) or of SVG
39
+ * slides the renderer drew. Vector by default (selectable text in embedded subsets); `raster: true` draws each slide as an image.
40
+ * `fonts` is as for `toPng`.
41
+ */
42
+ export async function toPdf(source, slides, options) {
43
+ ({ slides, options } = slideArguments(slides, options));
44
+ const pdfInputs = slideSources(source, slides, options, toSvg).svgs;
34
45
  if (!pdfInputs.length) {
35
- throw new OPFRenderError("empty-pdf", "svgToPdf requires at least one SVG slide.");
46
+ throw new OPFRenderError("empty-pdf", "toPdf needs at least one slide.");
36
47
  }
37
- const mode = options.mode ?? DEFAULT_PDF_MODE;
38
- if (mode !== "vector" && mode !== "raster") {
39
- throw new OPFRenderError("invalid-conversion-option", 'mode must be "vector" or "raster".', { option: "mode", value: mode });
48
+ if (options.raster !== undefined && typeof options.raster !== "boolean") {
49
+ throw new OPFRenderError("invalid-conversion-option", "raster must be true or false.", { option: "raster", value: options.raster });
40
50
  }
41
- if (mode === "vector") return vectorPdf(pdfInputs, options);
51
+ if (options.raster !== true) return vectorPdf(pdfInputs, options);
42
52
 
43
53
  const { PDFDocument } = await loadPdfLib();
44
54
  const pdf = await PDFDocument.create({ updateMetadata: false });
@@ -65,7 +75,7 @@ export async function svgToPdf(svgs, options = {}) {
65
75
  async function vectorPdf(inputs, options) {
66
76
  const settings = fontSettings(options);
67
77
  if (settings.loadSystemFonts) {
68
- 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" });
78
+ throw new OPFRenderError("pdf-system-fonts-unsupported", 'Vector PDF output embeds only the font files you supply: so fonts.loadSystemFonts does not apply. Pass a fonts handle with fontFiles, or font folders, or use raster: true.', { option: "fonts.loadSystemFonts" });
69
79
  }
70
80
  const scale = positiveNumber(options.rasterFallbackScale, "rasterFallbackScale", 2);
71
81
  const fontFiles = [
@@ -77,7 +87,7 @@ async function vectorPdf(inputs, options) {
77
87
  const {svgsToVectorPdf} = await import("./pdf-vector.js");
78
88
  return svgsToVectorPdf(svgs, {
79
89
  fontFiles,
80
- fontDirs: stringArray(options.fontDirs),
90
+ fontDirs: settings.fontDirs,
81
91
  defaultFontFamily: options.defaultFontFamily ?? "Roboto",
82
92
  sansSerifFamily: options.sansSerifFamily ?? options.defaultFontFamily ?? "Roboto",
83
93
  monospaceFamily: options.monospaceFamily ?? "Roboto Mono",
@@ -123,7 +133,7 @@ async function rasterizeSvg(svgInput, options) {
123
133
  const font = {
124
134
  loadSystemFonts: settings.loadSystemFonts,
125
135
  fontFiles,
126
- fontDirs: stringArray(options.fontDirs),
136
+ fontDirs: settings.fontDirs,
127
137
  defaultFontFamily: options.defaultFontFamily ?? "Roboto",
128
138
  sansSerifFamily: options.sansSerifFamily ?? options.defaultFontFamily ?? "Roboto",
129
139
  monospaceFamily: options.monospaceFamily ?? "Roboto Mono"
@@ -181,11 +191,6 @@ async function resolveBundledFontFiles() {
181
191
  return (await loadFonts()).fontFiles;
182
192
  }
183
193
 
184
- function normalizeSvgList(value) {
185
- if (Array.isArray(value)) return value;
186
- return [value];
187
- }
188
-
189
194
  function normalizeSvgInput(value) {
190
195
  if (typeof value === "string") return value;
191
196
  if (value instanceof Uint8Array) return new TextDecoder().decode(value);
@@ -65,7 +65,7 @@ const UNDRAWN_VALUE = /^(?:https?|data|blob|pkg|file|mailto):\S*$/i;
65
65
 
66
66
  // The built-in variables that copy a document value into drawn text: the inline token and the whole-field reference
67
67
  // (a logo or photo reference draws a picture, not text).
68
- const ORGANIZATION_TOKEN = /\{\{\s*organization\.|var:organization\.(?!logo\b)/;
68
+ const ORGANIZATION_TOKEN = /\{\{\s*organization\.|var:organization\.(?!(?:[^.\s]+\.)?logo\b)/;
69
69
  const SPEAKER_TOKEN = /\{\{\s*speaker\.|var:speaker\.(?!photo\b|image\b|avatar\b)/;
70
70
  const SECTION_TOKEN = /\{\{\s*slide\.section\s*[|}]/;
71
71
 
@@ -130,7 +130,7 @@ function scriptOfFamily(family) {
130
130
  * scheme (`slide.design`), so each such slide is resolved too. `han` is the CJK script the scheme names, which tells
131
131
  * Han-only text apart when the language does not (a Yu Gothic deck whose language is English).
132
132
  *
133
- * `renderOptions` are the host's `renderSvg` options: their `catalogs` (core `Catalog[]`) go to core's `resolveScriptFonts`
133
+ * `renderOptions` are the host's `toSvg` options: their `catalogs` (core `Catalog[]`) go to core's `resolveScriptFonts`
134
134
  * unchanged, and the families the renderer resolves per slide count too, so a scheme that exists only in the host's catalogs
135
135
  * and names a script font still loads that font.
136
136
  */
@@ -176,8 +176,8 @@ function* namedFontFamilies(value, key) {
176
176
  * itemization, so language-dependent punctuation counts), the scripts its font schemes need (`design`, for example
177
177
  * Jpan for a Yu Gothic scheme) and the CJK characters that a glyph fallback might have to draw with another CJK face.
178
178
  * The text decides, so a document is analyzed without resolving its layouts and catalogs; `renderOptions` (the host's
179
- * `renderSvg` options) matter only for font schemes the host supplies in `renderOptions.catalogs`, which then resolve
180
- * like the renderer resolves them; a document that then does not resolve throws what `renderSvg` throws for it.
179
+ * `toSvg` options) matter only for font schemes the host supplies in `renderOptions.catalogs`, which then resolve
180
+ * like the renderer resolves them; a document that then does not resolve throws what `toSvg` throws for it.
181
181
  */
182
182
  export function analyzePresentationScripts(presentation, profile, renderOptions) {
183
183
  profile ??= presentationScriptProfile(presentation, renderOptions);
@@ -198,7 +198,7 @@ export function analyzePresentationScripts(presentation, profile, renderOptions)
198
198
  * (Japanese, Korean, Simplified or Traditional Chinese) and makes curly quotes, dashes and the ellipsis
199
199
  * East Asian. Text the preview never draws (ids, alt text, sources, assets, catalogs, notes) is ignored.
200
200
  * Greek and Cyrillic add nothing: the office registry always carries Noto Sans (the glyph-fallback face) and Roboto covers them.
201
- * Pass `profile` to reuse a resolved profile. Other options are the host's `renderSvg` options (`catalogs`, see `analyzePresentationScripts`).
201
+ * Pass `profile` to reuse a resolved profile. Other options are the host's `toSvg` options (`catalogs`, see `analyzePresentationScripts`).
202
202
  */
203
203
  export function detectPresentationScripts(presentation, { profile, ...renderOptions } = {}) {
204
204
  return analyzePresentationScripts(presentation, profile, renderOptions).detected;
@@ -0,0 +1,47 @@
1
+ // RR-73: what `toPng` and `toPdf` draw. They take a deck (an object, or JSON text) and draw it with `toSvg`, or SVG the renderer
2
+ // already drew (a string or bytes starting with `<`, or a list of them). The second argument is a slide selection for a deck (`3`
3
+ // or `"1-3"`, slides counting from 1) or the options, told apart by type, as for `toSvg`.
4
+ import { parseSlideSelection } from "@openpresentation/opf";
5
+ import { OPFRenderError } from "./svg.js";
6
+
7
+ /** The slide numbers (from 1) a selection names: a list of numbers or text such as "1,3-5"; core's parser, with the renderer's error. */
8
+ export function selectSlides(slides, count) {
9
+ try {
10
+ return parseSlideSelection(slides, count, "slides");
11
+ } catch (error) {
12
+ throw new OPFRenderError("invalid-slide-selection", error.message, { option: "slides", slideCount: count, cause: error });
13
+ }
14
+ }
15
+
16
+ // The 0.17 option names (RR-73, RR-74): pre-v1 there is no alias, but a call that still passes one fails with the new name.
17
+ const RENAMED = { embedFonts: "text: \"system\" (embedFonts: false)", textAsPaths: "text: \"paths\"", mode: "raster: true (vector is the default)", fontDirs: "fonts: a font folder or a list of them" };
18
+
19
+ /** Throws for an option of 0.17 that 0.18 renamed, naming its replacement. */
20
+ export function rejectRenamedOptions(options, code) {
21
+ for (const name of Object.keys(RENAMED)) {
22
+ if (options?.[name] !== undefined) throw new OPFRenderError(code, `The ${name} option was replaced by ${RENAMED[name]}.`, { option: name });
23
+ }
24
+ }
25
+
26
+ /** `(slides, options)` with the options moved into place when the second argument is the options object. */
27
+ export function slideArguments(slides, options) {
28
+ if (slides !== null && typeof slides === "object" && !Array.isArray(slides)) return { slides: undefined, options: slides };
29
+ return { slides, options: options ?? {} };
30
+ }
31
+
32
+ const isSvg = (value) => value instanceof Uint8Array || (typeof value === "string" && /^\s*</.test(value));
33
+
34
+ /**
35
+ * The SVG slides to convert, and whether the caller asked for one (a slide number, or one SVG): `draw` is `toSvg`, called with the
36
+ * conversion options for a deck (they carry the fonts handle and the drawing options).
37
+ */
38
+ export function slideSources(source, slides, options, draw) {
39
+ rejectRenamedOptions(options, "invalid-conversion-option");
40
+ if (Array.isArray(source) || isSvg(source)) {
41
+ if (slides !== undefined) throw new OPFRenderError("invalid-conversion-option", "A slide selection applies to a deck; SVG input is already drawn, so pass the slides you want.", { option: "slides" });
42
+ if (Array.isArray(source) && !source.every(isSvg)) throw new OPFRenderError("invalid-svg-input", "A list must hold SVG slides (strings or bytes starting with <).");
43
+ return { svgs: Array.isArray(source) ? source : [source], one: !Array.isArray(source) };
44
+ }
45
+ const drawn = draw(source, slides, options);
46
+ return typeof drawn === "string" ? { svgs: [drawn], one: true } : { svgs: drawn, one: false };
47
+ }