HTML to boxpdf renderer
- ✓Open-source license (MIT)
- ✓Actively maintained (<30d)
- ✓Documented (README)
claude mcp add boxpdf-html -- npx -y @boxpdf/html-reader{
"mcpServers": {
"boxpdf-html": {
"command": "npx",
"args": ["-y", "@boxpdf/html-reader"],
"env": {
"BOXPDF_PASSWORD": "<boxpdf_password>"
}
}
}
}BOXPDF_PASSWORDResumen de MCP Servers
# @boxpdf/html-reader
Readable HTML-to-PDF rendering built on [`@boxpdf/writer`](https://github.com/earonesty/boxpdf). It is for invoices, receipts, reports, emails, and other authored document HTML where a useful static PDF matters more than browser pixel emulation.
```sh
npm install @boxpdf/html-reader @boxpdf/writer pdf-lib
```
The original `boxpdf-html` package and `boxpdf-html` command remain supported. They are published
from the same build and at the same version as `@boxpdf/html-reader`. New projects should use the
scoped package; existing projects do not need to change.
## CLI
Render an HTML file directly:
```sh
npx @boxpdf/html-reader invoice.html invoice.pdf
```
The scoped package also provides `html-reader` as a shorter command name.
With generated Tailwind CSS:
```sh
npx tailwindcss -i ./tailwind.css -o ./dist/tailwind.css --minify
npx @boxpdf/html-reader invoice.html invoice.pdf --css ./dist/tailwind.css
```
For very large inputs, add `--stream`:
```sh
npx @boxpdf/html-reader archive.html archive.pdf --stream
type archive.html | npx @boxpdf/html-reader - archive.pdf --stream
```
Streaming makes two bounded passes over the HTML: one for CSS, fonts, and
images, then one for incremental layout and PDF output. Stdin is spooled to a
temporary file so it can be reopened. The output replaces its destination only
after a successful conversion. This path requires `boxpdf` 1.12.0 or newer.
With PDF 2.0 AES-256 password encryption:
```sh
BOXPDF_PASSWORD='open me' \
npx @boxpdf/html-reader invoice.html invoice.pdf --password-env BOXPDF_PASSWORD
```
`--password-env` accepts an environment-variable name. The variable must be
set to a non-empty password. The password value is never accepted as a command
argument, which keeps it out of the command line and process listing.
With custom fonts and local images:
```sh
npx @boxpdf/html-reader invoice.html invoice.pdf \
--font ./Inter-Regular.ttf \
--bold-font ./Inter-Bold.ttf \
--font-family 'Inter=normal:Inter-Regular.ttf,bold:Inter-Bold.ttf'
```
Useful flags:
```sh
html-reader <input.html> <output.pdf>
html-reader - <output.pdf> # read HTML from stdin
html-reader input.html output.pdf --css app.css
html-reader input.html output.pdf --base-url ./public
html-reader input.html output.pdf --password-env BOXPDF_PASSWORD
html-reader input.html output.pdf --debug
html-reader input.html output.pdf --unsupported-css
html-reader input.html output.pdf --profile
html-reader input.html output.pdf --stream
```
The CLI defaults to pdf-lib's built-in Helvetica family. Use real embedded fonts for production output when brand matching, unicode coverage, or exact metrics matter.
## MCP server
`boxpdf-html mcp` is a stdio [MCP](https://modelcontextprotocol.io) server for AI agents. It's batteries-included: an `html_to_pdf` tool plus the full boxpdf library docs, so an agent never has to add a second server.
```sh
claude mcp add boxpdf-html -- npx -y @boxpdf/html-reader mcp
```
**Tools**
- `html_to_pdf` — render an HTML string (and optional `css`) to a PDF. Writes to `outputPath`, or returns the PDF inline as a base64 resource. Always returns `warnings` and `unsupportedCss` diagnostics so the agent can fix its input.
- Args: `html` (required), `css`, `outputPath`, `size` (`Letter`/`A4`/`Legal`/`Tabloid`, default `Letter`), `margin` (default 40), `baseUrl`, `fonts: { regular, bold, italic, boldItalic }` (TTF/OTF paths), `allowRemote` (default `false` — http(s) image fetches are blocked unless enabled), `debug`.
- `boxpdf_docs` — focused guidance for building PDFs with the libraries directly. `topic`: `quickstart` (default), `fonts`, `themes`, `tables`, `pagination`, `streaming`, `html-api`, `cloudflare`.
**Resources**: `boxpdf-html://guide`, `boxpdf-html://readme`, `boxpdf://readme`, and the five `boxpdf://templates/<name>` sources (receipt, boarding-pass, resume, order-confirmation, certificate).
The server runs no JavaScript and (by default) makes no network requests. `outputPath` writes with the agent's filesystem permissions; without it, PDFs over 1 MB are summarized rather than inlined.
## API
### `htmlToPdf` — one call to bytes
`htmlToPdf(html, options?)` is the simplest path: it creates the document, embeds fonts, renders, and returns the PDF bytes. Fonts default to the built-in Helvetica family, so the minimal call needs no setup.
```ts
import { htmlToPdf } from "@boxpdf/html-reader";
const bytes = await htmlToPdf("<h1>Invoice</h1><p>Thanks for your order.</p>");
```
Pass embedded fonts (via `loadFont`) and a `resolveImage` callback for production output:
```ts
import { readFile } from "node:fs/promises";
import { PDFDocument } from "pdf-lib";
import { loadFont, loadImage } from "@boxpdf/writer";
import { htmlToPdf } from "@boxpdf/html-reader";
const pdf = await PDFDocument.create();
const inter = await loadFont(pdf, await readFile("Inter-Regular.ttf"));
const interBold = await loadFont(pdf, await readFile("Inter-Bold.ttf"));
const logo = await loadImage(pdf, await readFile("logo.png"));
const bytes = await htmlToPdf(await readFile("invoice.html", "utf8"), {
pdf, // reuse the document you embedded into
font: inter,
boldFont: interBold,
resolveImage: ({ url }) => (url === "logo.png" ? logo : undefined),
margin: 40
});
```
Options: `font` / `boldFont` / `italicFont` / `boldItalicFont` (default to Helvetica), `pdf` (render into an existing document), `margin` (default 40), `size` (default US Letter), `width` (CSS containing-block width; defaults to the page's content width), `debug`, plus everything `htmlToBoxpdf` accepts (`resolveFont`, `resolveImage`, `baseUrl`, `defaultFontSize`, `defaultColor`, `diagnostics`, `profile`).
### `htmlToBoxpdf` — the nodes, for full control
`htmlToBoxpdf` turns HTML into normal boxpdf nodes without rendering. Reach for it when you need the nodes themselves, the `warnings`/`diagnostics`, multiple render passes, or `renderFlow` headers/footers.
```ts
import { readFile } from "node:fs/promises";
import { PDFDocument } from "pdf-lib";
import { loadFont, loadImage, renderFlow } from "@boxpdf/writer";
import { fontFamily, htmlToBoxpdf } from "@boxpdf/html-reader";
const html = await readFile("invoice.html", "utf8");
const pdf = await PDFDocument.create();
const inter = await loadFont(pdf, await readFile("Inter-Regular.ttf"));
const interBold = await loadFont(pdf, await readFile("Inter-Bold.ttf"));
const logo = await loadImage(pdf, await readFile("logo.png"));
const result = htmlToBoxpdf(html, {
font: inter,
boldFont: interBold,
resolveFont: fontFamily({
Inter: { normal: inter, bold: interBold },
"sans-serif": { normal: inter, bold: interBold }
}),
resolveImage: ({ url }) => (url === "logo.png" ? logo : undefined),
baseUrl: process.cwd(),
width: 532
});
console.log(result.warnings);
await renderFlow(pdf, result.nodes, { margin: 40 });
const bytes = await pdf.save();
```
`width` is the CSS containing block width in PDF points. A US Letter page with 40pt margins has a 532pt content width, so `width: 532` is a good default.
### `streamHtmlToPdf` — bounded large-document conversion
`streamHtmlToPdf` accepts a function that reopens the HTML for each of its two
passes and writes PDF bytes incrementally. Embed fonts before calling it; use
`prepare` to embed images found by the resource preflight before output begins.
```ts
import { createReadStream, createWriteStream } from "node:fs";
import { PDFDocument, StandardFonts } from "pdf-lib";
import { nodeAdapter } from "@boxpdf/writer";
import { streamHtmlToPdf } from "@boxpdf/html-reader";
const pdf = await PDFDocument.create();
const font = await pdf.embedFont(StandardFonts.Helvetica);
const result = await streamHtmlToPdf(
() => createReadStream("archive.html"),
nodeAdapter(createWriteStream("archive.pdf")),
{ pdf, font, width: 532, margin: 40 }
);
console.log(result.pageCount, result.dom.maxBufferedNodes);
```
Ordinary block wrappers and tables are released in bounded continuation
fragments. Atomic layouts such as flex/grid, positioned or transformed
containers, and single uninterrupted text nodes have explicit safety caps and
fail with a useful error when they cannot be streamed safely. Selectors whose
meaning depends on sibling position conservatively disable wrapper
fragmentation.
## Fonts
Fonts are explicit. `boxpdf-html` does not discover system fonts and does not ship a browser font stack. This keeps rendering deterministic and works in serverless runtimes.
At minimum, pass `font`. Pass `boldFont` and `italicFont` if your HTML uses bold or italic text:
```ts
const result = htmlToBoxpdf(html, {
font,
boldFont,
italicFont,
width: 532
});
```
For CSS `font-family`, use `fontFamily()`:
```ts
const resolveFont = fontFamily({
Inter: {
normal: interRegular,
bold: interBold,
italic: interItalic,
boldItalic: interBoldItalic
},
Helvetica: {
normal: fallback,
bold: fallbackBold
},
"sans-serif": {
normal: fallback,
bold: fallbackBold
}
});
```
The resolver receives `{ families, weight, style }` and returns a pdf-lib `PDFFont`. You can provide your own resolver when you need looser mapping, font aliases, language-specific fallbacks, or weight synthesis.
Gotchas:
- `font-family: system-ui` only works if your resolver maps `system-ui`.
- Standard pdf-lib fonts are convenient but limited; use embedded TTF/OTF fonts for real documents.
- Complex shaping depends on pdf-lib/fontkit behavior. Western-language invoice/report text is the target.
- Font metrics affect layout. Use the same embedded fonts in tests and production when visual stability matters.
## Tailwind CSS
Tailwind works when you render its generated CSS, not raw class names alone. The usual flow is:
1. Write document HTML with Tailwind classes.
2. Run Tailwind against that HTML.
3. Inline or pass the generated CSS to `boxpdf-html`.
4. Render with a containing widLo que la gente pregunta sobre boxpdf-html
¿Qué es earonesty/boxpdf-html?
+
earonesty/boxpdf-html es mcp servers para el ecosistema de Claude AI. HTML to boxpdf renderer Tiene 2 estrellas en GitHub y su última actualización registrada es del 2026-10-08.
¿Cómo se instala boxpdf-html?
+
Puedes instalar boxpdf-html clonando el repositorio (https://github.com/earonesty/boxpdf-html) o siguiendo las instrucciones del README en GitHub. ClaudeWave también te ofrece bloques de instalación rápida en esta misma página.
¿Es seguro usar earonesty/boxpdf-html?
+
Nuestro agente de seguridad ha analizado earonesty/boxpdf-html y le ha asignado un Trust Score de 82/100 (tier: Trusted). Revisa el desglose completo de comprobaciones superadas y flags en esta página.
¿Quién mantiene earonesty/boxpdf-html?
+
earonesty/boxpdf-html es mantenido por earonesty. La última actividad registrada en GitHub es del 2026-10-08, con 0 issues abiertos.
¿Hay alternativas a boxpdf-html?
+
Sí. En ClaudeWave puedes explorar mcp servers similares en /categories/mcp, ordenados por popularidad o actividad reciente.
Despliega boxpdf-html en tu cloud
Lleva este repo a producción en minutos. Cada plataforma genera su propio entorno con variables de entorno editables.
¿Mantienes este repo? Añade un badge a tu README
Pega el badge en tu README de GitHub para mostrar que está auditado por ClaudeWave. Cada badge enlaza de vuelta a esta página y muestra el Trust Score actual.
[](https://claudewave.com/repo/earonesty-boxpdf-html)<a href="https://claudewave.com/repo/earonesty-boxpdf-html"><img src="https://claudewave.com/api/badge/earonesty-boxpdf-html" alt="Featured on ClaudeWave: earonesty/boxpdf-html" width="320" height="64" /></a>Más MCP Servers
Fair-code workflow automation platform with native AI capabilities. Combine visual building with custom code, self-host or cloud, 400+ integrations.
User-friendly AI Interface (Supports Ollama, OpenAI API, ...)
An open-source AI agent that brings the power of Gemini directly into your terminal.
Real-time global intelligence dashboard. AI-powered news aggregation, geopolitical monitoring, and infrastructure tracking in a unified situational awareness interface
🕷️ An adaptive Web Scraping framework that handles everything from a single request to a full-scale crawl! Don't be shy, join here: https://discord.gg/EMgGbDceNQ and follow here for daily tips and tricks: https://x.com/Scrapling_dev
The fastest path to AI-powered full stack observability, even for lean teams.