Images and custom fonts in API-rendered PDFs

pdfrender loads nothing from the network. Put images, fonts and CSS into the HTML as data: URIs, as shown here.

Last updated

pdfrender loads only data: URIs, so images, fonts and stylesheets go into the HTML itself. A URL with http(s), file or ftp, or an @import, fails the request with status 400 (external_resource_blocked). The error lists every URL.

<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==" alt="Logo">

<style>
  @font-face {
    font-family: "Inter";
    src: url("data:font/woff2;base64,d09GMgABAAA...") format("woff2"); /* shortened */
  }
  body { font-family: "Inter", sans-serif; }
</style>

Relative URLs such as logo.png are dropped without an error, so check the PDF for missing images.

Encode a file

This shell command works with the base64 of Linux and macOS:

printf 'data:image/png;base64,%s' "$(base64 < logo.png | tr -d '\n')" > logo.uri

The same in Python:

import base64

uri = "data:image/png;base64," + base64.b64encode(open("logo.png", "rb").read()).decode()

Use the MIME type of the file: image/png, image/jpeg, image/svg+xml, font/woff2 or font/ttf.

Size budget

The 2 MB limit counts the UTF-8 bytes of the html field, including every data: URI. Base64 makes a file about a third larger. Subset fonts to the characters you use and scale images to print size.

Next step

See which CSS works.

FAQ

Why does pdfrender reject external image and font URLs?
pdfrender loads nothing from the network, so a render cannot hang on a slow server or reach internal addresses. The error lists every URL to inline.
Do inlined assets count against the size limit?
Yes. The 2 MB limit counts the whole html field, and base64 makes each file about a third larger.