These docs describe changes that aren’t released yet. For the newest release, see the released docs.
Errors and warnings
Real PDFs are often damaged, so Lamina PDF separates a call that failed from one that worked around a problem. An error stops the call. A warning lets it finish and tells you what was repaired, dropped or approximated. Both carry a stable code your program can branch on.
Errors stop the call
Section titled “Errors stop the call”In TypeScript an error is a LaminaPdfError, and in Rust it’s a laminapdf::Error. Branch on its code, never on its message: isLaminaPdfError(e, "wrong-password") checks the code and narrows the type in one step.
Warnings say what was repaired
Section titled “Warnings say what was repaired”A warning means the call went ahead, but Lamina PDF had to repair, drop or approximate something in the file. Read them with doc.warnings in TypeScript or doc.warnings() in Rust. Show them to the user when the difference matters, such as a page that may not look the way its author intended.
Every surface has the same fields
Section titled “Every surface has the same fields”Errors and warnings have the same fields in TypeScript and Rust: code, kind, message, hint, location, data and docsUrl. toJSON() in TypeScript gives the same object as serde does in Rust, so a server and a browser can pass them to each other unchanged.
kindsorts errors by who can fix them, such asinput,limit,usage,stateorio. For example, a server returns a 4xx status forinputandlimitand a 5xx for the rest, as the Cloudflare Workers quick start does.- Text from the document is untrusted, so it never appears in
messageorhint. It goes only indata, where you can escape it before you show it.
This example opens someone else’s file and returns the error’s code and hint as a value, instead of letting the error escape:
// Open bytes from someone else: an optional password, tighter limits than the defaults, and the// outcome as a value rather than an exception. The example writes nothing: it returns what it found.import { Document, isLaminaPdfError } from "@laminapdf/sdk";
const MiB = 1024 * 1024;/** Limits for an upload endpoint: each decoded stream, and all of them together. */const LIMITS = { maxStreamDecoded: 16 * MiB, maxDecodedTotal: 64 * MiB } as const;
/** What opening a file gave: its page count and warning codes, or why it didn't open. */export type Opened = | { readonly ok: true; readonly pages: number; readonly warnings: readonly string[] } | { readonly ok: false; readonly code: string; readonly hint: string | undefined };
/** Opens `bytes`, with `password` if the file needs one, under {@link LIMITS}. Needs `init()`. */export function openUntrusted(bytes: Uint8Array, password?: string): Opened { try { using doc = Document.open(bytes, { ...(password === undefined ? {} : { password }), limits: LIMITS }); // Repairs and dropped values are warnings: the file opened, but not exactly as written. return { ok: true, pages: doc.pageCount, warnings: doc.warnings.map((w) => w.code) }; } catch (e) { // `password-required`, `wrong-password`, `not-pdf`, `limit-exceeded`, …: a coded error whose // message and hint never contain text from the file. Anything else is a bug: let it throw. if (isLaminaPdfError(e)) return { ok: false, code: e.code, hint: e.hint }; throw e; }}The error and warning codes list every code with its kind, a summary and a hint.
Next: make a PDF with text and fonts.