Takumi

From Puppeteer

Move a Puppeteer PDF script to takumi-pdf by mapping each page.pdf() option, fetching images yourself, and checking for browser-only features.

Start with the HTML you pass to page.setContent(). Pass the same string to render(), and translate the page.pdf() options with the option map. Headers and footers use the same pageNumber and totalPages class hooks.

Takumi does not load a page or run its scripts. Generate dynamic content and fetch document images before you render.

Before and after

Before: Puppeteer
// Puppeteer
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setContent(html, { waitUntil: "networkidle0" });

const pdf = await page.pdf({
  format: "A4",
  margin: { top: "48px", bottom: "64px", left: "48px", right: "48px" },
  displayHeaderFooter: true,
  footerTemplate: `<div style="font-size:10px;width:100%;text-align:center">
    Page <span class="pageNumber"></span> of <span class="totalPages"></span>
  </div>`,
  printBackground: true,
});

await browser.close();
After: takumi-pdf
// takumi-pdf
import {  } from "@takumi-rs/helpers";
import {  } from "takumi-pdf";
import { ,  } from "takumi-pdf/primitives";

const  = await (, {
  : "a4",
  : { : 48, : 64, : 48, : 48 },
  : (
    < ="flex w-full justify-center text-[10px]">
      Page < /> of < />
    </>
  ),
  : await (["Inter"]),
});

render() parses HTML strings, including <style> tags. It also takes JSX and node trees, and so do header and footer.

Option map

page.pdf()takumi-pdf
format: "A4"size: "a4"
width, heightsize: { width, height }
landscapelandscape
marginmargin, in CSS px
displayHeaderFooter + headerTemplateheader
footerTemplatefooter
pageNumber, totalPages classesthe same classes
date, title, url classesinterpolate the value yourself
tagged (default on)tagged (default on)
outlineoutline
printBackgroundalways on
pathwrite the returned Uint8Array
timeout, waitForFontsnot needed, since there is no page to load
scalenot supported, so scale the CSS instead
pageRangespageRanges: [1, { from: 4, to: 8 }]
preferCSSPageSize, @pagenot supported, so set size and margin
omitBackgroundnot supported

Margins take numbers in CSS px, not length strings. For Chromium's "0.5in", pass 48.

Fetching moves to your code

render() does not fetch images. Call prepare() first. It styles the document as render() would, fetches the images it draws, and loads the fonts its text uses:

Fetch images, then render
import { ,  } from "takumi-pdf";

const  = await (); 

const  = await ();

prepare() fetches every remote http or https URL in <img src>, background-image, mask-image, and list-style-image, including those that <style> rules set and those in the header and footer.

  • Its images option takes timeout, maxBytes, an allowUrl predicate, a fetchCache that reuses bytes across renders, and your own fetch. Your fetch also receives relative URLs such as /logo.png.
  • Pass page and layout options such as size, margin, header, and css to prepare(). Pass file options such as metadata and pdfa to render().

Fonts come from the fonts option, not from CSS @font-face rules. See Fonts & images. Font URL loaders and googleFonts() make their own requests, so give them their own fetch limits.

Known gaps

Takumi supports a subset of browser CSS. Check these differences before you replace Puppeteer.

  • Scripts do not run, so charts and diagrams have to arrive as markup, SVG, or images. A client-side chart library does not work.
  • PDF output rejects filter: blur() and drop-shadow(), and does not draw backdrop-filter. Blurred box-shadow and text-shadow are approximated with bands. Grid, flexbox, transforms, gradients, masks, and clip paths all work.
  • @page is not supported, so page geometry comes from the options.

Keep Puppeteer to reproduce a live web page as a browser shows it. Use takumi-pdf for documents written for print. See Comparison for measured numbers.

Last updated on