@laminapdf/render 0.0.0-stage → 0.2.0-alpha.3

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,3 +1,31 @@
1
- # Temporary Holding Version
1
+ # @laminapdf/render
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ The interface Lamina PDF's web renderers share, the TypeScript twin of the Rust `laminapdf-render` crate. Two packages implement it: [`@laminapdf/render-pdfium`](../render-pdfium/README.md), the default, and [`@laminapdf/render-hayro`](../render-hayro/README.md), a second engine to compare against and debug with. Both run inside the viewer's document Web Worker. It's on npm as an alpha release, `@laminapdf/render@alpha`; most apps reach it through `@laminapdf/viewer`.
4
+
5
+ ```ts
6
+ const backend = await initPdfium(wasmUrl); // or initHayro()
7
+ using doc = backend.open(bytesSource(bytes), { password });
8
+ const plan = doc.plan({ page: 0, scale: 2, clip: { x: 0, y: 0, width: 512, height: 512 } });
9
+ const { width, height, pixels } = doc.render({ page: 0, scale: 2, clip: plan.tile }, { signal });
10
+ self.postMessage(await createImageBitmap(new ImageData(pixels, width, height)));
11
+ ```
12
+
13
+ - **`RenderSource`:** `{ size, readSync(offset, length) }`, read synchronously, because PDFium's file access can't await. A read returns `undefined` when its bytes haven't arrived; the call then throws `data-pending` with `ranges` to fetch before retrying. `bytesSource(bytes)` wraps bytes already in memory.
14
+ - **`RenderRequest`:**
15
+ - `page` (0-based) and `scale` (pixels per point, device pixel ratio applied);
16
+ - `clip?`: a tile, in the whole page's pixels;
17
+ - `rotation?`: 0, 90, 180 or 270;
18
+ - `flags?`: `annotations`, `formWidgets` (drawn only with annotations), `print`, `grayscale`;
19
+ - `background?`: white by default, `null` for transparent.
20
+ - **Output:** straight-alpha RGBA8 rows of `width × 4` bytes.
21
+ - **`planTile`:** the port of Rust's `TilePlan::new`, so both adapters agree on sizes. The page is `max(1, floor(points × scale))` pixels per side after the view rotation, and `RenderLimits` (16.7 MP and 16,384 px per tile, 1 GiB of input) are checked before anything is allocated. `pageToTile` is Rust's `TilePlan::page_to_tile`.
22
+ - **Errors:** `RenderError` (`code`, `kind`, `ranges`), with Lamina PDF's error codes. `isRenderError(e, code)` checks structurally, so it works across copies of the package. `toDiagnostic()` returns the same error as the plain object every Lamina PDF surface uses. `@laminapdf/sdk`'s Web Worker posts that object, and the page receives a `LaminaPdfError` with the same code, message and ranges. An aborted `signal` throws a `DOMException` named `AbortError`, and a closed document throws `disposed`.
23
+
24
+ ## For contributors
25
+
26
+ The contract is [ADR 0004](../../docs/decisions/0004-render-backend.md); the renderers run in the document worker of [ADR 0009](../../docs/decisions/0009-web-client-architecture.md) §2, and errors follow [ADR 0026](../../docs/decisions/0026-error-and-diagnostic-contract.md).
27
+
28
+ ### Tests
29
+
30
+ - **Unit tests:** `pnpm --filter @laminapdf/render test`, for the planner and the error, in Node.
31
+ - **The contract suite** that both adapters run, in Node and in Chromium, Firefox and WebKit, lives in [`@laminapdf/test-utils`](../test-utils/README.md), with the browser harness and config it uses.
@@ -0,0 +1,14 @@
1
+ /** The codes a render backend throws (ADR 0026). */
2
+ export type RenderErrorCode = "render-too-large" | "render-failed" | "render-backend-unavailable" | "invalid-argument" | "page-index-out-of-range" | "password-required" | "wrong-password" | "data-pending" | "source" | "cancelled" | "disposed";
3
+ /** What kind of failure a render code is (the ADR 0026 kinds its codes use). */
4
+ export type RenderErrorKind = "limit" | "input" | "io" | "usage" | "security" | "state" | "cancelled";
5
+ /** The kind of each render code. */
6
+ export declare const KINDS: {
7
+ readonly [C in RenderErrorCode]: RenderErrorKind;
8
+ };
9
+ /** Where each code's page is (ADR 0026 §6): `${base}${code}${suffix}`. */
10
+ export declare const DOCS_URL: {
11
+ readonly base: "https://github.com/laudebugs/laminapdf/blob/main/docs/errors/";
12
+ readonly suffix: ".md";
13
+ };
14
+ //# sourceMappingURL=codes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codes.d.ts","sourceRoot":"","sources":["../../src/generated/codes.ts"],"names":[],"mappings":"AAGA,oDAAoD;AACpD,MAAM,MAAM,eAAe,GACvB,kBAAkB,GAClB,eAAe,GACf,4BAA4B,GAC5B,kBAAkB,GAClB,yBAAyB,GACzB,mBAAmB,GACnB,gBAAgB,GAChB,cAAc,GACd,QAAQ,GACR,WAAW,GACX,UAAU,CAAC;AAEf,gFAAgF;AAChF,MAAM,MAAM,eAAe,GACvB,OAAO,GACP,OAAO,GACP,IAAI,GACJ,OAAO,GACP,UAAU,GACV,OAAO,GACP,WAAW,CAAC;AAEhB,oCAAoC;AACpC,eAAO,MAAM,KAAK,EAAE;IAAE,QAAQ,EAAE,CAAC,IAAI,eAAe,GAAG,eAAe;CAYrE,CAAC;AAEF,0EAA0E;AAC1E,eAAO,MAAM,QAAQ;aAAK,IAAI,EAAE,+DAA+D;aAAE,MAAM,EAAE,KAAK;CAAW,CAAC"}
@@ -0,0 +1,19 @@
1
+ // @generated by cargo xtask gen codes. DO NOT EDIT.
2
+ // From fixtures/codes.json (renderErrors and the codes it reuses); `cargo xtask check docs` fails when it is stale.
3
+ /** The kind of each render code. */
4
+ export const KINDS = {
5
+ "render-too-large": "limit",
6
+ "render-failed": "input",
7
+ "render-backend-unavailable": "io",
8
+ "invalid-argument": "usage",
9
+ "page-index-out-of-range": "usage",
10
+ "password-required": "security",
11
+ "wrong-password": "security",
12
+ "data-pending": "state",
13
+ "source": "io",
14
+ "cancelled": "cancelled",
15
+ "disposed": "state",
16
+ };
17
+ /** Where each code's page is (ADR 0026 §6): `${base}${code}${suffix}`. */
18
+ export const DOCS_URL = { base: "https://github.com/laudebugs/laminapdf/blob/main/docs/errors/", suffix: ".md" };
19
+ //# sourceMappingURL=codes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codes.js","sourceRoot":"","sources":["../../src/generated/codes.ts"],"names":[],"mappings":"AAAA,oDAAoD;AACpD,oHAAoH;AA0BpH,oCAAoC;AACpC,MAAM,CAAC,MAAM,KAAK,GAAyD;IACzE,kBAAkB,EAAE,OAAO;IAC3B,eAAe,EAAE,OAAO;IACxB,4BAA4B,EAAE,IAAI;IAClC,kBAAkB,EAAE,OAAO;IAC3B,yBAAyB,EAAE,OAAO;IAClC,mBAAmB,EAAE,UAAU;IAC/B,gBAAgB,EAAE,UAAU;IAC5B,cAAc,EAAE,OAAO;IACvB,QAAQ,EAAE,IAAI;IACd,WAAW,EAAE,WAAW;IACxB,UAAU,EAAE,OAAO;CACpB,CAAC;AAEF,0EAA0E;AAC1E,MAAM,CAAC,MAAM,QAAQ,GAAG,EAAE,IAAI,EAAE,+DAA+D,EAAE,MAAM,EAAE,KAAK,EAAW,CAAC"}
@@ -0,0 +1,211 @@
1
+ import { type RenderErrorCode, type RenderErrorKind } from "./generated/codes.js";
2
+ /**
3
+ * The bytes of a document, read synchronously (PDFium's file access can't await). A read
4
+ * returns `undefined` when those bytes haven't arrived yet: the call then throws `data-pending`
5
+ * with the ranges to fetch (ADR 0004 §1, the web form of `Pending`).
6
+ */
7
+ export interface RenderSource {
8
+ readonly size: number;
9
+ /** `length` bytes at `offset` (fewer only at the end), or `undefined` while they haven't arrived. */
10
+ readSync(offset: number, length: number): Uint8Array | undefined;
11
+ }
12
+ /** A source over bytes already in memory. */
13
+ export declare function bytesSource(bytes: Uint8Array): RenderSource;
14
+ /** Bounds on one render, checked before anything is allocated (`render-too-large`). */
15
+ export interface RenderLimits {
16
+ /** Most pixels in one tile. Default 16,777,216 (4096 × 4096), iOS Safari's per-canvas cap. */
17
+ readonly maxRenderPixels: number;
18
+ /** Most pixels on either side of one tile. Default 16,384. */
19
+ readonly maxRenderSide: number;
20
+ /** Most input bytes a backend that reads the whole file (hayro) reads. Default 1 GiB. */
21
+ readonly maxInputBytes: number;
22
+ }
23
+ /** The render limits when none are given (ADR 0004): 16.7 MP, 16,384 px a side, 1 GiB of input. */
24
+ export declare const DEFAULT_LIMITS: RenderLimits;
25
+ /** A view rotation, clockwise, added to the page's own `/Rotate`. */
26
+ export type ViewRotation = 0 | 90 | 180 | 270;
27
+ /** Whether `rotation` turns the page a quarter (90° or 270°), which swaps its width and height. */
28
+ export declare function isQuarterTurn(rotation: ViewRotation): boolean;
29
+ /** What to draw besides the page content. */
30
+ export interface RenderFlags {
31
+ /** Annotation appearance streams. Default on. */
32
+ readonly annotations: boolean;
33
+ /** Form widgets, read-only; drawn only when `annotations` is on too. Default on. */
34
+ readonly formWidgets: boolean;
35
+ /** Print intent: honours annotation `Print`/`NoView` flags. Default off. */
36
+ readonly print: boolean;
37
+ /** Grayscale content (the background keeps its colour). Default off. */
38
+ readonly grayscale: boolean;
39
+ }
40
+ /** The render flags when none are given: annotations and form widgets drawn, screen colours. */
41
+ export declare const DEFAULT_FLAGS: RenderFlags;
42
+ /** A rectangle in the whole page's output pixels (after scale and view rotation), y down. */
43
+ export interface PixelRect {
44
+ readonly x: number;
45
+ readonly y: number;
46
+ readonly width: number;
47
+ readonly height: number;
48
+ }
49
+ /** A straight-alpha sRGB colour, each channel 0–255. */
50
+ export interface Rgba8 {
51
+ readonly r: number;
52
+ readonly g: number;
53
+ readonly b: number;
54
+ readonly a: number;
55
+ }
56
+ /** Opaque white, the default background. */
57
+ export declare const WHITE: Rgba8;
58
+ /** One render: a page, a scale, an optional tile, a view rotation, flags and a background. */
59
+ export interface RenderRequest {
60
+ /** 0-based (ADR 0027). */
61
+ readonly page: number;
62
+ /** Output pixels per point, device pixel ratio applied (1 is 72 DPI). */
63
+ readonly scale: number;
64
+ /** The tile; omitted is the whole page (still bounded by the limits). */
65
+ readonly clip?: PixelRect;
66
+ readonly rotation?: ViewRotation;
67
+ readonly flags?: Partial<RenderFlags>;
68
+ /** Omitted is white; `null` is transparent. */
69
+ readonly background?: Rgba8 | null;
70
+ }
71
+ /** A page as a backend reports it: displayed size in points (`/Rotate` applied) and `/Rotate`. */
72
+ export interface RenderPage {
73
+ readonly width: number;
74
+ readonly height: number;
75
+ readonly rotate: number;
76
+ }
77
+ /** The geometry of one validated request. */
78
+ export interface TilePlan {
79
+ /** The whole page's pixel size at the scale, after the view rotation. */
80
+ readonly pageWidth: number;
81
+ readonly pageHeight: number;
82
+ readonly tile: PixelRect;
83
+ /** `tile.width × tile.height × 4`. */
84
+ readonly byteLength: number;
85
+ }
86
+ /** A rendered tile: straight-alpha RGBA8, ready for `new ImageData(pixels, width, height)`. */
87
+ export interface RenderedTile {
88
+ readonly width: number;
89
+ readonly height: number;
90
+ readonly pixels: Uint8ClampedArray<ArrayBuffer>;
91
+ }
92
+ /** Options for opening a document in a backend. */
93
+ export interface RenderOpenOptions {
94
+ /** Used by `open` only; the backend keeps no copy (ADR 0004 §1). */
95
+ readonly password?: string;
96
+ readonly limits?: Partial<RenderLimits>;
97
+ }
98
+ /** One open document. One call at a time (renders are synchronous). */
99
+ export interface RenderDocument extends Disposable {
100
+ readonly pageCount: number;
101
+ /** @throws RenderError `page-index-out-of-range`, `data-pending`, `render-failed`. */
102
+ page(index: number): RenderPage;
103
+ /** Validates `request` for this document. */
104
+ plan(request: RenderRequest): TilePlan;
105
+ /**
106
+ * Renders `request`. `signal` is checked before and after the engine call: an aborted
107
+ * request throws a `DOMException` named `AbortError`, and nothing it allocated survives.
108
+ */
109
+ render(request: RenderRequest, options?: {
110
+ readonly signal?: AbortSignal;
111
+ }): RenderedTile;
112
+ /** Frees the engine's document. Idempotent. */
113
+ close(): void;
114
+ }
115
+ /** A rendering engine. */
116
+ export interface RenderBackend {
117
+ readonly name: "pdfium" | "hayro";
118
+ readonly version: string;
119
+ /** The size of the engine's WebAssembly memory, in bytes (it only grows). */
120
+ memory(): number;
121
+ /** @throws RenderError `password-required`, `wrong-password`, `data-pending`, `render-failed`. */
122
+ open(source: RenderSource, options?: RenderOpenOptions): RenderDocument;
123
+ }
124
+ export type { RenderErrorCode, RenderErrorKind } from "./generated/codes.js";
125
+ /** A byte range of the input: its first byte and its length. */
126
+ export type ByteRange = readonly [offset: number, length: number];
127
+ /** A render backend's error, in the ADR 0026 contract. Messages carry no document text. */
128
+ export declare class RenderError extends Error {
129
+ readonly name = "RenderError";
130
+ readonly code: RenderErrorCode;
131
+ readonly kind: RenderErrorKind;
132
+ /** For `data-pending`: the ranges to fetch before calling again. */
133
+ readonly ranges: readonly ByteRange[] | undefined;
134
+ constructor(code: RenderErrorCode, message: string, ranges?: readonly ByteRange[]);
135
+ /**
136
+ * The ADR 0026 §1 object: what crosses a worker boundary (structured clone keeps neither a
137
+ * custom error name nor own properties). `@laminapdf/sdk`'s worker posts it, and the page gets
138
+ * a `LaminaPdfError` with the same code, message and `ranges`. A `data-pending` lists its ranges
139
+ * as `[offset, length]` pairs in `data.ranges`, as the Rust core does. It carries no hint.
140
+ */
141
+ toDiagnostic(): RenderDiagnostic;
142
+ }
143
+ /** A RenderError as the ADR 0026 §1 object (see {@link RenderError.toDiagnostic}). */
144
+ export interface RenderDiagnostic {
145
+ readonly code: RenderErrorCode;
146
+ readonly kind: RenderErrorKind;
147
+ readonly message: string;
148
+ readonly data?: {
149
+ readonly ranges: readonly ByteRange[];
150
+ };
151
+ readonly docsUrl: string;
152
+ }
153
+ /** Whether `e` is a RenderError (with `code`, if given). */
154
+ export declare function isRenderError<C extends RenderErrorCode>(e: unknown, code?: C): e is RenderError & {
155
+ code: C;
156
+ };
157
+ /** Whether `code` is one of the codes a render backend throws. */
158
+ export declare function isRenderErrorCode(code: unknown): code is RenderErrorCode;
159
+ /**
160
+ * Merges limits over the defaults. Sides are clamped to 2^32 − 1 (a u32 in the Rust planner),
161
+ * so both planners see the same limit.
162
+ */
163
+ export declare function limitsOf(limits?: Partial<RenderLimits>): RenderLimits;
164
+ /** Merges flags over the defaults. */
165
+ export declare function flagsOf(flags?: Partial<RenderFlags>): RenderFlags;
166
+ /**
167
+ * Validates `request` against `page` and `limits` (the port of Rust's `TilePlan::new`): the
168
+ * whole page is `max(1, floor(points × scale))` pixels per side after the view rotation, and the
169
+ * tile must lie inside it and within the limits.
170
+ *
171
+ * @throws RenderError `invalid-argument` or `render-too-large`.
172
+ */
173
+ export declare function planTile(page: RenderPage, request: RenderRequest, limits: RenderLimits): TilePlan;
174
+ /**
175
+ * The affine matrix `[a, b, c, d, e, f]` from the displayed page space (points, top-left origin,
176
+ * y down, `/Rotate` applied) to the tile's pixels: Rust's `TilePlan::page_to_tile`.
177
+ */
178
+ export declare function pageToTile(plan: TilePlan, page: RenderPage, rotation?: ViewRotation): readonly number[];
179
+ /** Throws the `AbortError` a cancelled render throws, if `signal` is aborted. */
180
+ export declare function throwIfAborted(signal: AbortSignal | undefined): void;
181
+ /**
182
+ * Where an engine's `.wasm` comes from: a URL (or URL string) to fetch, a `Response` (or a
183
+ * promise of one), the bytes, or a compiled `WebAssembly.Module`. The app controls caching and
184
+ * lazy loading (ADR 0009 §2).
185
+ */
186
+ export type WasmSource = URL | string | Response | PromiseLike<Response> | ArrayBuffer | ArrayBufferView<ArrayBuffer> | WebAssembly.Module;
187
+ /** The {@link WasmSource} forms that `postMessage` can send to a worker. */
188
+ export type CloneableWasmSource = string | ArrayBuffer | ArrayBufferView<ArrayBuffer> | WebAssembly.Module;
189
+ /** The most input bytes a wasm32 engine can address: its file access and buffers take 32-bit sizes. */
190
+ export declare const MAX_WASM_INPUT = 4294967295;
191
+ /** Throws `render-too-large` when an input of `size` bytes is over `most`. */
192
+ export declare function checkInputSize(size: number, most: number): void;
193
+ /** Throws `page-index-out-of-range` unless `index` is a page of a `pageCount`-page document. */
194
+ export declare function checkPageIndex(index: number, pageCount: number): void;
195
+ /** Throws `disposed` once the document is closed. */
196
+ export declare function checkOpen(open: boolean): asserts open;
197
+ /** The error of a failed read: the source's own RenderError, else `source`. */
198
+ export declare function readFailure(offset: number, length: number, cause?: unknown): RenderError;
199
+ /**
200
+ * Reads `length` bytes at `offset` from `source`: the bytes, or `undefined` while they haven't
201
+ * arrived.
202
+ *
203
+ * @throws RenderError `source` when the source throws or returns fewer bytes, or the source's own
204
+ * RenderError.
205
+ */
206
+ export declare function readRange(source: RenderSource, offset: number, length: number): Uint8Array | undefined;
207
+ /** `ranges` sorted, with overlapping and touching ones merged (an engine may ask for one window many times). */
208
+ export declare function mergeRanges(ranges: readonly ByteRange[]): [offset: number, length: number][];
209
+ /** `data-pending` with the missing `ranges`, merged. */
210
+ export declare function pendingError(ranges: readonly ByteRange[]): RenderError;
211
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAmB,KAAK,eAAe,EAAE,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEnG;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,qGAAqG;IACrG,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAAC;CAClE;AAED,6CAA6C;AAC7C,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,YAAY,CAK3D;AAED,uFAAuF;AACvF,MAAM,WAAW,YAAY;IAC3B,8FAA8F;IAC9F,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,8DAA8D;IAC9D,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,yFAAyF;IACzF,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED,mGAAmG;AACnG,eAAO,MAAM,cAAc,EAAE,YAI3B,CAAC;AAEH,qEAAqE;AACrE,MAAM,MAAM,YAAY,GAAG,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,GAAG,CAAC;AAE9C,mGAAmG;AACnG,wBAAgB,aAAa,CAAC,QAAQ,EAAE,YAAY,GAAG,OAAO,CAE7D;AAED,6CAA6C;AAC7C,MAAM,WAAW,WAAW;IAC1B,iDAAiD;IACjD,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,oFAAoF;IACpF,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,4EAA4E;IAC5E,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,wEAAwE;IACxE,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B;AAED,gGAAgG;AAChG,eAAO,MAAM,aAAa,EAAE,WAK1B,CAAC;AAEH,6FAA6F;AAC7F,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,wDAAwD;AACxD,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,4CAA4C;AAC5C,eAAO,MAAM,KAAK,EAAE,KAAyD,CAAC;AAE9E,8FAA8F;AAC9F,MAAM,WAAW,aAAa;IAC5B,0BAA0B;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,yEAAyE;IACzE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,yEAAyE;IACzE,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC;IAC1B,QAAQ,CAAC,QAAQ,CAAC,EAAE,YAAY,CAAC;IACjC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;IACtC,+CAA+C;IAC/C,QAAQ,CAAC,UAAU,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC;CACpC;AAED,kGAAkG;AAClG,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,6CAA6C;AAC7C,MAAM,WAAW,QAAQ;IACvB,yEAAyE;IACzE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,sCAAsC;IACtC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,+FAA+F;AAC/F,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAC,WAAW,CAAC,CAAC;CACjD;AAED,mDAAmD;AACnD,MAAM,WAAW,iBAAiB;IAChC,oEAAoE;IACpE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;CACzC;AAED,uEAAuE;AACvE,MAAM,WAAW,cAAe,SAAQ,UAAU;IAChD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,sFAAsF;IACtF,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAAC;IAChC,6CAA6C;IAC7C,IAAI,CAAC,OAAO,EAAE,aAAa,GAAG,QAAQ,CAAC;IACvC;;;OAGG;IACH,MAAM,CAAC,OAAO,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GAAG,YAAY,CAAC;IAC1F,+CAA+C;IAC/C,KAAK,IAAI,IAAI,CAAC;CACf;AAED,0BAA0B;AAC1B,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC;IAClC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,6EAA6E;IAC7E,MAAM,IAAI,MAAM,CAAC;IACjB,kGAAkG;IAClG,IAAI,CAAC,MAAM,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,cAAc,CAAC;CACzE;AAED,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAE7E,gEAAgE;AAChE,MAAM,MAAM,SAAS,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AAElE,2FAA2F;AAC3F,qBAAa,WAAY,SAAQ,KAAK;IACpC,SAAkB,IAAI,iBAAiB;IACvC,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAC/B,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAC/B,oEAAoE;IACpE,QAAQ,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,GAAG,SAAS,CAAC;IAElD,YAAY,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,SAAS,EAAE,EAKhF;IAED;;;;;OAKG;IACH,YAAY,IAAI,gBAAgB,CAQ/B;CACF;AAED,sFAAsF;AACtF,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAC/B,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAC/B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,CAAA;KAAE,CAAC;IAC1D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,4DAA4D;AAC5D,wBAAgB,aAAa,CAAC,CAAC,SAAS,eAAe,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,WAAW,GAAG;IAAE,IAAI,EAAE,CAAC,CAAA;CAAE,CAK7G;AAED,kEAAkE;AAClE,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,eAAe,CAExE;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,YAAY,CAGrE;AAED,sCAAsC;AACtC,wBAAgB,OAAO,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,WAAW,CAEjE;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,YAAY,GAAG,QAAQ,CAWjG;AAqED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,GAAE,YAAgB,GAAG,SAAS,MAAM,EAAE,CAiB1G;AAED,iFAAiF;AACjF,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,GAAG,IAAI,CAEpE;AAED;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAClB,GAAG,GACH,MAAM,GACN,QAAQ,GACR,WAAW,CAAC,QAAQ,CAAC,GACrB,WAAW,GACX,eAAe,CAAC,WAAW,CAAC,GAC5B,WAAW,CAAC,MAAM,CAAC;AAEvB,4EAA4E;AAC5E,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,WAAW,GAAG,eAAe,CAAC,WAAW,CAAC,GAAG,WAAW,CAAC,MAAM,CAAC;AAK3G,uGAAuG;AACvG,eAAO,MAAM,cAAc,aAAc,CAAC;AAE1C,8EAA8E;AAC9E,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAI/D;AAED,gGAAgG;AAChG,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAOrE;AAED,qDAAqD;AACrD,wBAAgB,SAAS,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAErD;AAED,+EAA+E;AAC/E,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,WAAW,CAIxF;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAStG;AAED,gHAAgH;AAChH,wBAAgB,WAAW,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CAW5F;AAED,wDAAwD;AACxD,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,GAAG,WAAW,CAGtE"}
package/dist/index.js ADDED
@@ -0,0 +1,251 @@
1
+ // The web render contract of ADR 0004: the interface both web adapters implement
2
+ // (@laminapdf/render-pdfium, @laminapdf/render-hayro), the tile planner they share, and RenderError.
3
+ // It mirrors laminapdf-render's Rust trait: bounded tiles of a page at a scale, straight-alpha RGBA8
4
+ // rows of width × 4 bytes, validated once by planTile so both adapters agree on sizes.
5
+ import { DOCS_URL, KINDS } from "./generated/codes.js";
6
+ /** A source over bytes already in memory. */
7
+ export function bytesSource(bytes) {
8
+ return {
9
+ size: bytes.length,
10
+ readSync: (offset, length) => bytes.subarray(offset, Math.min(bytes.length, offset + length)),
11
+ };
12
+ }
13
+ /** The render limits when none are given (ADR 0004): 16.7 MP, 16,384 px a side, 1 GiB of input. */
14
+ export const DEFAULT_LIMITS = Object.freeze({
15
+ maxRenderPixels: 16_777_216,
16
+ maxRenderSide: 16_384,
17
+ maxInputBytes: 2 ** 30,
18
+ });
19
+ /** Whether `rotation` turns the page a quarter (90° or 270°), which swaps its width and height. */
20
+ export function isQuarterTurn(rotation) {
21
+ return rotation === 90 || rotation === 270;
22
+ }
23
+ /** The render flags when none are given: annotations and form widgets drawn, screen colours. */
24
+ export const DEFAULT_FLAGS = Object.freeze({
25
+ annotations: true,
26
+ formWidgets: true,
27
+ print: false,
28
+ grayscale: false,
29
+ });
30
+ /** Opaque white, the default background. */
31
+ export const WHITE = Object.freeze({ r: 255, g: 255, b: 255, a: 255 });
32
+ /** A render backend's error, in the ADR 0026 contract. Messages carry no document text. */
33
+ export class RenderError extends Error {
34
+ name = "RenderError";
35
+ code;
36
+ kind;
37
+ /** For `data-pending`: the ranges to fetch before calling again. */
38
+ ranges;
39
+ constructor(code, message, ranges) {
40
+ super(message);
41
+ this.code = code;
42
+ this.kind = KINDS[code];
43
+ this.ranges = ranges;
44
+ }
45
+ /**
46
+ * The ADR 0026 §1 object: what crosses a worker boundary (structured clone keeps neither a
47
+ * custom error name nor own properties). `@laminapdf/sdk`'s worker posts it, and the page gets
48
+ * a `LaminaPdfError` with the same code, message and `ranges`. A `data-pending` lists its ranges
49
+ * as `[offset, length]` pairs in `data.ranges`, as the Rust core does. It carries no hint.
50
+ */
51
+ toDiagnostic() {
52
+ return {
53
+ code: this.code,
54
+ kind: this.kind,
55
+ message: this.message,
56
+ ...(this.ranges === undefined ? {} : { data: { ranges: this.ranges } }),
57
+ docsUrl: `${DOCS_URL.base}${this.code}${DOCS_URL.suffix}`,
58
+ };
59
+ }
60
+ }
61
+ /** Whether `e` is a RenderError (with `code`, if given). */
62
+ export function isRenderError(e, code) {
63
+ // Structural, not instanceof: module copies give one class per copy (ADR 0009 §3).
64
+ if (typeof e !== "object" || e === null || e.name !== "RenderError")
65
+ return false;
66
+ const actual = e.code;
67
+ return typeof actual === "string" && (code === undefined || actual === code);
68
+ }
69
+ /** Whether `code` is one of the codes a render backend throws. */
70
+ export function isRenderErrorCode(code) {
71
+ return typeof code === "string" && Object.hasOwn(KINDS, code);
72
+ }
73
+ /**
74
+ * Merges limits over the defaults. Sides are clamped to 2^32 − 1 (a u32 in the Rust planner),
75
+ * so both planners see the same limit.
76
+ */
77
+ export function limitsOf(limits) {
78
+ const merged = { ...DEFAULT_LIMITS, ...limits };
79
+ return { ...merged, maxRenderSide: Math.min(Math.max(0, Math.floor(merged.maxRenderSide)), 0xffff_ffff) };
80
+ }
81
+ /** Merges flags over the defaults. */
82
+ export function flagsOf(flags) {
83
+ return { ...DEFAULT_FLAGS, ...flags };
84
+ }
85
+ /**
86
+ * Validates `request` against `page` and `limits` (the port of Rust's `TilePlan::new`): the
87
+ * whole page is `max(1, floor(points × scale))` pixels per side after the view rotation, and the
88
+ * tile must lie inside it and within the limits.
89
+ *
90
+ * @throws RenderError `invalid-argument` or `render-too-large`.
91
+ */
92
+ export function planTile(page, request, limits) {
93
+ checkScale(request.scale);
94
+ checkPage(page);
95
+ checkBackground(request.background);
96
+ const quarter = isQuarterTurn(checkRotation(request.rotation ?? 0));
97
+ const pageWidth = wholePixels(quarter ? page.height : page.width, request.scale);
98
+ const pageHeight = wholePixels(quarter ? page.width : page.height, request.scale);
99
+ const tile = request.clip ?? { x: 0, y: 0, width: pageWidth, height: pageHeight };
100
+ checkTile(tile, pageWidth, pageHeight);
101
+ checkTileLimits(tile, limits);
102
+ return { pageWidth, pageHeight, tile, byteLength: tile.width * tile.height * 4 };
103
+ }
104
+ function checkScale(scale) {
105
+ // The Rust planner sees the scale as an f32: check that value, so both agree.
106
+ if (!Number.isFinite(Math.fround(scale)) || Math.fround(scale) <= 0) {
107
+ throw new RenderError("invalid-argument", "invalid render request: scale must be finite and positive");
108
+ }
109
+ }
110
+ function checkRotation(rotation) {
111
+ if (![0, 90, 180, 270].includes(rotation)) {
112
+ throw new RenderError("invalid-argument", "invalid render request: rotation must be 0, 90, 180 or 270");
113
+ }
114
+ return rotation;
115
+ }
116
+ function checkPage(page) {
117
+ if (!(page.width > 0 && page.height > 0 && Number.isFinite(page.width) && Number.isFinite(page.height))) {
118
+ throw new RenderError("invalid-argument", "invalid render request: the page has no drawable area");
119
+ }
120
+ }
121
+ function checkBackground(background) {
122
+ if (background === undefined || background === null)
123
+ return;
124
+ const channels = [background.r, background.g, background.b, background.a];
125
+ if (!channels.every((c) => Number.isInteger(c) && c >= 0 && c <= 255)) {
126
+ throw new RenderError("invalid-argument", "invalid render request: colour channels must be integers 0–255");
127
+ }
128
+ }
129
+ /** One side of the whole page in pixels: `max(1, floor(points × scale))`. */
130
+ function wholePixels(points, scale) {
131
+ // f32 like the Rust planner, so both agree on the floor at every scale.
132
+ const v = Math.max(1, Math.floor(Math.fround(points) * Math.fround(scale)));
133
+ if (v > 2_147_483_647) {
134
+ throw new RenderError("invalid-argument", "invalid render request: the page is too large to address at this scale");
135
+ }
136
+ return v;
137
+ }
138
+ /** The tile is whole pixels, not empty, and inside the whole page. */
139
+ function checkTile(tile, pageWidth, pageHeight) {
140
+ const ints = [tile.x, tile.y, tile.width, tile.height];
141
+ if (!ints.every((v) => Number.isInteger(v) && v >= 0)) {
142
+ throw new RenderError("invalid-argument", "invalid render request: the tile must be whole pixels");
143
+ }
144
+ if (tile.width === 0 || tile.height === 0) {
145
+ throw new RenderError("invalid-argument", "invalid render request: the tile is empty");
146
+ }
147
+ if (tile.x + tile.width > pageWidth || tile.y + tile.height > pageHeight) {
148
+ throw new RenderError("invalid-argument", "invalid render request: the tile extends past the page");
149
+ }
150
+ }
151
+ function checkTileLimits(tile, limits) {
152
+ if (tile.width > limits.maxRenderSide || tile.height > limits.maxRenderSide) {
153
+ throw new RenderError("render-too-large", `a ${tile.width}×${tile.height} pixel tile exceeds the render limit of ${limits.maxRenderSide} pixels per side`);
154
+ }
155
+ if (tile.width * tile.height > limits.maxRenderPixels) {
156
+ throw new RenderError("render-too-large", `a ${tile.width}×${tile.height} pixel tile exceeds the render limit of ${limits.maxRenderPixels} pixels`);
157
+ }
158
+ }
159
+ /**
160
+ * The affine matrix `[a, b, c, d, e, f]` from the displayed page space (points, top-left origin,
161
+ * y down, `/Rotate` applied) to the tile's pixels: Rust's `TilePlan::page_to_tile`.
162
+ */
163
+ export function pageToTile(plan, page, rotation = 0) {
164
+ const quarter = isQuarterTurn(rotation);
165
+ const w0 = quarter ? plan.pageHeight : plan.pageWidth;
166
+ const h0 = quarter ? plan.pageWidth : plan.pageHeight;
167
+ const sx = w0 / page.width;
168
+ const sy = h0 / page.height;
169
+ const { x: tx, y: ty } = plan.tile;
170
+ switch (rotation) {
171
+ case 0:
172
+ return [sx, 0, 0, sy, -tx, -ty];
173
+ case 90:
174
+ return [0, sx, -sy, 0, h0 - tx, -ty];
175
+ case 180:
176
+ return [-sx, 0, 0, -sy, w0 - tx, h0 - ty];
177
+ case 270:
178
+ return [0, -sx, sy, 0, -tx, w0 - ty];
179
+ }
180
+ }
181
+ /** Throws the `AbortError` a cancelled render throws, if `signal` is aborted. */
182
+ export function throwIfAborted(signal) {
183
+ if (signal?.aborted)
184
+ throw new DOMException("the render was aborted", "AbortError");
185
+ }
186
+ // For the adapters (@laminapdf/render-hayro, @laminapdf/render-pdfium): what both share, so they
187
+ // check pages, reads and sizes the same way. Apps don't need these.
188
+ /** The most input bytes a wasm32 engine can address: its file access and buffers take 32-bit sizes. */
189
+ export const MAX_WASM_INPUT = 0xffff_ffff;
190
+ /** Throws `render-too-large` when an input of `size` bytes is over `most`. */
191
+ export function checkInputSize(size, most) {
192
+ if (size > most) {
193
+ throw new RenderError("render-too-large", `the input is ${size} bytes, over the render limit of ${most} bytes`);
194
+ }
195
+ }
196
+ /** Throws `page-index-out-of-range` unless `index` is a page of a `pageCount`-page document. */
197
+ export function checkPageIndex(index, pageCount) {
198
+ if (!Number.isInteger(index) || index < 0 || index >= pageCount) {
199
+ throw new RenderError("page-index-out-of-range", `page index ${index} is out of range for a document of ${pageCount} pages`);
200
+ }
201
+ }
202
+ /** Throws `disposed` once the document is closed. */
203
+ export function checkOpen(open) {
204
+ if (!open)
205
+ throw new RenderError("disposed", "the document was closed");
206
+ }
207
+ /** The error of a failed read: the source's own RenderError, else `source`. */
208
+ export function readFailure(offset, length, cause) {
209
+ return isRenderError(cause)
210
+ ? cause
211
+ : new RenderError("source", `bytes ${offset}..+${length} of the input could not be read`);
212
+ }
213
+ /**
214
+ * Reads `length` bytes at `offset` from `source`: the bytes, or `undefined` while they haven't
215
+ * arrived.
216
+ *
217
+ * @throws RenderError `source` when the source throws or returns fewer bytes, or the source's own
218
+ * RenderError.
219
+ */
220
+ export function readRange(source, offset, length) {
221
+ let bytes;
222
+ try {
223
+ bytes = source.readSync(offset, length);
224
+ }
225
+ catch (e) {
226
+ throw readFailure(offset, length, e);
227
+ }
228
+ if (bytes !== undefined && bytes.length !== length)
229
+ throw readFailure(offset, length);
230
+ return bytes;
231
+ }
232
+ /** `ranges` sorted, with overlapping and touching ones merged (an engine may ask for one window many times). */
233
+ export function mergeRanges(ranges) {
234
+ const merged = [];
235
+ for (const [offset, length] of [...ranges].sort((a, b) => a[0] - b[0] || a[1] - b[1])) {
236
+ const last = merged.at(-1);
237
+ if (last !== undefined && offset <= last[0] + last[1]) {
238
+ last[1] = Math.max(last[1], offset + length - last[0]);
239
+ }
240
+ else {
241
+ merged.push([offset, length]);
242
+ }
243
+ }
244
+ return merged;
245
+ }
246
+ /** `data-pending` with the missing `ranges`, merged. */
247
+ export function pendingError(ranges) {
248
+ const merged = mergeRanges(ranges);
249
+ return new RenderError("data-pending", `${merged.length} byte range(s) of the input are not available yet`, merged);
250
+ }
251
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,47 @@
1
1
  {
2
2
  "name": "@laminapdf/render",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.2.0-alpha.3",
4
+ "description": "The web render contract of ADR 0004: the RenderBackend interface @laminapdf/render-pdfium and @laminapdf/render-hayro implement, the tile planner they share, and RenderError.",
5
+ "license": "MIT OR Apache-2.0",
6
+ "homepage": "https://laminapdf.com",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/laudebugs/laminapdf.git",
10
+ "directory": "packages/render"
11
+ },
12
+ "keywords": [
13
+ "pdf",
14
+ "render",
15
+ "renderer",
16
+ "interface"
17
+ ],
18
+ "type": "module",
19
+ "sideEffects": false,
20
+ "engines": {
21
+ "node": ">=22"
22
+ },
23
+ "files": [
24
+ "dist"
25
+ ],
26
+ "exports": {
27
+ ".": {
28
+ "types": "./dist/index.d.ts",
29
+ "default": "./dist/index.js"
30
+ },
31
+ "./package.json": "./package.json"
32
+ },
33
+ "devDependencies": {
34
+ "@types/node": "26.6.5",
35
+ "@vitest/browser-playwright": "5.0.5",
36
+ "typescript": "7.0.2",
37
+ "vitest": "5.0.5"
38
+ },
39
+ "publishConfig": {
40
+ "access": "public"
41
+ },
42
+ "scripts": {
43
+ "build": "tsc -p tsconfig.build.json",
44
+ "typecheck": "tsc -p tsconfig.build.json && tsc --noEmit -p tsconfig.json",
45
+ "test": "tsc -p tsconfig.build.json && vitest run"
46
+ }
6
47
  }