@terraza/pdf
v0.1.0
Published
Markdown-to-PDF rendering for Terraza apps. Headless Chromium via Playwright, PDF metadata injection via pdf-lib.
Readme
@terraza/pdf
Markdown-to-PDF rendering for Terraza apps. Uses headless Chromium via Playwright for layout and pdf-lib for metadata injection.
The package is pure rendering — no database, no storage, no app coupling. Consumers bring their own markdown and CSS strings and do whatever they like with the returned bytes.
Install
pnpm add @terraza/pdf
npx playwright install chromiumChromium is downloaded once by Playwright; subsequent builds reuse the cached binary.
Usage
import { renderMarkdownToPdf } from "@terraza/pdf";
import { writeFileSync } from "node:fs";
const css = `
body { font-family: "IBM Plex Sans", sans-serif; }
h1 { color: #E94B1C; }
`;
const bytes = await renderMarkdownToPdf("# Hello\n\nThis is **bold**.", {
css,
metadata: {
title: "Demo",
author: "Ken Grafals",
subject: "Example PDF",
keywords: ["demo", "terraza"],
},
format: "Letter",
margin: { top: "0.6in", bottom: "0.6in", left: "0.6in", right: "0.6in" },
});
writeFileSync("demo.pdf", bytes);API
renderMarkdownToPdf(markdown, options?) → Promise<Buffer>
Parses markdown (GFM enabled), wraps it in a minimal HTML document, renders via Chromium, and injects PDF metadata.
Options:
css(string) — inline CSS injected into a<style>tag in the HTML wrapper.metadata(PdfMetadata) — title, author, subject, keywords, creator, producer. All optional.keywordsmay be an array or a comma-separated string.format(PdfPageFormat) — page size. Default"Letter".margin(PdfMargin) — top/bottom/left/right. Default"0.6in"all around.wrapHtml(function) — override the HTML wrapper entirely. Receives(body, metadata, css)and returns the full HTML document string.
renderHtmlToPdf(html, options?) → Promise<Buffer>
Lower-level: takes a complete HTML document (no markdown parsing, no wrapper). Useful when the consumer already has rendered HTML or wants full control over the <head>.
Accepts the same options as renderMarkdownToPdf minus css and wrapHtml.
Combining with Shelf (example)
The package has no Shelf dependency, but here's the typical integration — a caller reads markdown + CSS from Shelf, renders, and stores the PDF back:
import { renderMarkdownToPdf } from "@terraza/pdf";
import { readDocument, storeDocument } from "@terraza/shelf-core";
const { document: md } = await readDocument(shelfDb, userId, "my-app/report.md");
const { document: css } = await readDocument(shelfDb, userId, "my-app/style.css").catch(() => ({ document: { body: "" } }));
const bytes = await renderMarkdownToPdf(md.body!, {
css: css.body ?? "",
metadata: { title: "Q1 Report", author: "Ken Grafals" },
});
await storeDocument(shelfDb, {
userId,
path: "my-app/report.pdf",
content: bytes,
editedBy: "my-app-mcp",
});Testing
pnpm --filter @terraza/pdf testTests require Chromium installed via Playwright (npx playwright install chromium). Each test launches a real browser, so the suite takes a few seconds.
