chromium engine
Node · Bun · Deno
- Launch or reuse a browser via puppeteer / playwright
page.pdf()— vector text, real CSS- Native CSS page breaks and
@page
htmlpdfx is intentionally small and layered. The core has zero runtime dependencies — both the PDF writer and the DOM rasterizer are written from scratch and live in the package.
Every render walks the same path. Only the middle step branches:
toPdf(input, options)
normalizeOptions
One canonical, unit-free config — everything resolved to mm internally.
resolveInput
Works out what you passed: an HTML string, a URL, a file, or a live DOM node.
selectEngine
auto resolves to chromium on the server and canvas in the browser.
chromium engine
Node · Bun · Deno
page.pdf() — vector text, real CSS@pagecanvas engine
Browser
renderDomToImage — SVG foreignObjectcomputePageSlices — break-aware slicingPdfWriter — our own PDF writerPdfResult
bytes · pageCount · engine · save() · download() · toBlob() · toDataUrl()
Those libraries rasterize HTML into a single image, which is the root cause of most of their bugs: non-selectable text, blank pages past the canvas size limit, ignored page breaks, dropped modern CSS. htmlpdfx instead:
| Module | Responsibility |
|---|---|
core/options.ts | Normalize options → canonical mm-based config |
core/pagesize.ts | Paper sizes + orientation |
core/paginate.ts | Page-break-aware slicing (pure, fully tested) |
core/css.ts | Injected @page + break-* + color-adjust CSS |
core/template.ts | Header/footer token expansion |
pdf/writer.ts | Dependency-free PDF writer (images, text, metadata) |
dom/rasterize.ts | DOM → image via SVG foreignObject (browser) |
engine/chromium.ts | Puppeteer/Playwright engine |
engine/canvas.ts | Built-in browser engine |
Advanced users can use the primitives directly:
import { PdfWriter, renderDomToImage, computePageSlices } from "htmlpdfx";