@openpresentation/opf-render 0.0.1 → 0.0.2

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # OPF Render
2
2
 
3
- Deterministic local renderer for Open Presentation Format documents. This repo is the Phase 1 toolkit lane for SVG, PNG, and PDF output.
3
+ Deterministic local renderer for Open Presentation Format documents. This repo is the Phase 1 toolkit lane for SVG, PNG, and PDF output. The current package implements the validation, catalog resolution, placeholder binding, deterministic text layout, and SVG core that PNG/PDF conversion will build on.
4
4
 
5
5
  ## Scope
6
6
 
@@ -8,9 +8,29 @@ Deterministic local renderer for Open Presentation Format documents. This repo i
8
8
  - Repository: `OpenPresentation/opf-render`
9
9
  - License: MIT
10
10
  - Compatibility target: `@openpresentation/opf`
11
- - Planned public API: `renderSvg(opf, opts)`, `svgToPng(svg, opts)`, and `svgToPdf(svgs, opts)`
11
+ - Public API: `renderSvg(opf, opts)`, `renderSvgDeck(opf, opts)`, `resolvePresentation(opf, opts)`, `svgToPng(svg, opts)`, and `svgToPdf(svgs, opts)`
12
12
 
13
- Feature implementation is intentionally not in this provisioning slice. The first implementation task owns validation, catalog resolution, placeholder binding, SVG emission, and deterministic golden tests.
13
+ `renderSvg` renders a single slide selected by `opts.slideIndex` (default `0`). `renderSvgDeck` returns one SVG string per slide. Both validate OPF at the boundary via `@openpresentation/opf`, resolve inline and bundled catalogs locally, and emit byte-stable SVG for the same input.
14
+
15
+ ```js
16
+ import { renderSvgDeck } from "@openpresentation/opf-render";
17
+
18
+ const svgs = renderSvgDeck(opf, { trace: true });
19
+ ```
20
+
21
+ Set `trace: true` to stamp rendered SVG elements with `data-opf-path` values such as `slides.0.title`; omit it for smaller production SVG.
22
+
23
+ PNG and PDF conversion APIs are async because they load the local raster/PDF engines on demand:
24
+
25
+ ```js
26
+ import { renderSvgDeck, svgToPdf, svgToPng } from "@openpresentation/opf-render";
27
+
28
+ const svgs = renderSvgDeck(opf);
29
+ const png = await svgToPng(svgs[0], { scale: 1 });
30
+ const pdf = await svgToPdf(svgs, { scale: 1 });
31
+ ```
32
+
33
+ `svgToPng` returns PNG bytes for one SVG. `svgToPdf` accepts one SVG or an array of SVGs and returns PDF bytes with one slide per page. The SVG page `width`/`height` or `viewBox` determines the PDF page size; `scale` controls raster density only.
14
34
 
15
35
  ## Runtime Policy
16
36
 
@@ -23,22 +43,40 @@ The package runtime must stay local and deterministic:
23
43
  - No clock, locale, random, or system-font fallback drift in render output
24
44
  - Host applications own auth, storage, queues, analytics, collaboration, and product workflow
25
45
 
46
+ Dependency policy:
47
+
48
+ - `@openpresentation/opf` is the compatibility source for schemas, validation, and bundled catalogs.
49
+ - `@resvg/resvg-js` is used only for local SVG rasterization in Node; it makes no network calls and does not require a browser.
50
+ - `pdf-lib` assembles PDF bytes locally. Metadata timestamps are disabled so repeated PDF output is byte-stable for the same SVG input and options.
51
+ - Bundled OFL Roboto and Roboto Mono TTF files provide the default deterministic font fallback.
52
+ - `svgToPng` and `svgToPdf` disable system-font loading by default. Hosts that require branded fonts should pass explicit `fontFiles` or `fontDirs`; `loadSystemFonts: true` is an opt-in escape hatch and can make output environment-dependent.
53
+
54
+ Browser support boundary: `renderSvg`, `renderSvgDeck`, and `resolvePresentation` are browser-importable pure JavaScript APIs. `svgToPng` and `svgToPdf` are Node APIs in this package version because they depend on the Node build of resvg.
55
+
26
56
  ## Development
27
57
 
28
58
  ```sh
29
59
  npm ci
30
60
  npm run build
61
+ npm run typecheck
31
62
  npm run validate
63
+ npm test
64
+ ```
65
+
66
+ When this repo is checked out beside `openpresentation/opf`, `npm test` also renders every `examples/**/*.opf.json` deck twice and asserts byte-identical SVG output. Golden PNG drift is checked against `test/golden/opf-examples-png.sha256.json` when the sibling corpus is at the approved OPF commit. To regenerate the approved PNG hash manifest after an intentional visual change:
67
+
68
+ ```sh
69
+ OPF_EXAMPLES_DIR=/path/to/opf/examples npm run golden:update
32
70
  ```
33
71
 
34
72
  ## Release Lane
35
73
 
36
- Public npm package publication is handled by `.github/workflows/release.yml` with npm provenance.
74
+ Public npm package publication is handled by `.github/workflows/npm-publish.yml` with npm provenance.
37
75
 
38
76
  Required first-publish setup:
39
77
 
40
78
  1. An npm owner for the `@openpresentation` scope must run the first publish or reserve/grant the `@openpresentation/opf-render` package.
41
- 2. Configure npm Trusted Publishing for GitHub repository `OpenPresentation/opf-render` and workflow `.github/workflows/release.yml`.
42
- 3. Publish by creating a GitHub Release or manually running the Release workflow after CI passes.
79
+ 2. Configure npm Trusted Publishing for GitHub repository `OpenPresentation/opf-render` and workflow `.github/workflows/npm-publish.yml`.
80
+ 3. Publish by pushing a git tag matching `opf-render-v<version>` or `@openpresentation/opf-render@v<version>` (the tag must match the `package.json` version), or by manually running the workflow after CI passes.
43
81
 
44
82
  This repo does not require an npm automation token when Trusted Publishing is configured.
package/dist/index.d.ts CHANGED
@@ -13,3 +13,94 @@ export declare const runtimePolicy: Readonly<{
13
13
  requiredNetworkCalls: false;
14
14
  deterministicLocalExecution: true;
15
15
  }>;
16
+
17
+ export declare const engineDefaults: Readonly<{
18
+ catalogs: Readonly<Record<string, Readonly<{ source: string }>>>;
19
+ theme: "minimal";
20
+ colorScheme: "cool-horizon";
21
+ language: "english";
22
+ narrative: "classic-story";
23
+ tone: "formal";
24
+ audience: "executives";
25
+ fontScheme: Readonly<{
26
+ pptx: Readonly<{ latin: "aptos"; ea: "microsoft-yahei"; cs: "nirmala-ui" }>;
27
+ google: Readonly<{ latin: "roboto"; ea: "noto-sans-sc"; cs: "noto-sans" }>;
28
+ }>;
29
+ chartTypes: readonly ["stacked-column-3x", "stacked-area-3x", "line-with-markers-3x"];
30
+ }>;
31
+
32
+ export interface RenderSvgOptions {
33
+ slideIndex?: number;
34
+ trace?: boolean;
35
+ catalogs?: Record<string, { records?: unknown[] } | unknown[]>;
36
+ catalogSources?: Record<string, { records?: unknown[] } | unknown[]>;
37
+ }
38
+
39
+ export interface SvgToPngOptions {
40
+ scale?: number;
41
+ background?: string;
42
+ dpi?: number;
43
+ useBundledFonts?: boolean;
44
+ loadSystemFonts?: boolean;
45
+ fontFiles?: string[];
46
+ fontDirs?: string[];
47
+ defaultFontFamily?: string;
48
+ sansSerifFamily?: string;
49
+ monospaceFamily?: string;
50
+ }
51
+
52
+ export interface SvgToPdfOptions extends SvgToPngOptions {}
53
+
54
+ export interface ResolvedPresentation {
55
+ presentation: unknown;
56
+ engineDefaults: typeof engineDefaults;
57
+ slides: ResolvedSlide[];
58
+ }
59
+
60
+ export interface ResolvedSlide {
61
+ index: number;
62
+ path: string;
63
+ slide: unknown;
64
+ layout: unknown;
65
+ design: {
66
+ theme: unknown;
67
+ colorScheme: unknown;
68
+ fontScheme: unknown;
69
+ dimensions: { width: number; height: number };
70
+ background: unknown;
71
+ backgroundColor: string | null;
72
+ colors: Record<string, string>;
73
+ fonts: Record<"heading" | "body" | "code", string>;
74
+ };
75
+ titleBindings: RenderBinding[];
76
+ contentItems: RenderBinding[];
77
+ blocks: RenderBinding[];
78
+ }
79
+
80
+ export interface RenderBinding {
81
+ type: string;
82
+ field?: string;
83
+ slot?: string;
84
+ value: unknown;
85
+ path: string;
86
+ placeholderIndex?: number;
87
+ regionKey?: string;
88
+ }
89
+
90
+ export declare class OPFRenderError extends Error {
91
+ readonly code: string;
92
+ readonly details: Record<string, unknown>;
93
+ readonly issues?: unknown[];
94
+ readonly path?: string;
95
+ constructor(code: string, message: string, details?: Record<string, unknown>);
96
+ }
97
+
98
+ export declare function resolvePresentation(input: unknown, options?: RenderSvgOptions): ResolvedPresentation;
99
+
100
+ export declare function renderSvg(input: unknown, options?: RenderSvgOptions): string;
101
+
102
+ export declare function renderSvgDeck(input: unknown, options?: RenderSvgOptions): string[];
103
+
104
+ export declare function svgToPng(svg: string | Uint8Array, options?: SvgToPngOptions): Promise<Uint8Array>;
105
+
106
+ export declare function svgToPdf(svgs: string | Uint8Array | Array<string | Uint8Array>, options?: SvgToPdfOptions): Promise<Uint8Array>;