Private betav0.1.0
Docs
Format developer documentation

Images

Add images to a document, size them for sharp print and screen output, pick the right format, and keep vectors vector.

How an image renders in the final PDF comes down to three things: the format you ship (file size), the source resolution (sharpness), and whether a CSS effect forces it onto the raster path. The rest of this page works through each, alongside how to reference images, handle SVG, and deal with a source that won't load.

Adding an image

There are four ways to put an image on the page, and all of them work the way they do on the web.

<!-- An <img> element -->
<img src="./logo.png" alt="Acme" />

<!-- An inline SVG, written straight into your markup -->
<svg viewBox="0 0 24 24"><path d="..." /></svg>

<!-- A CSS background -->
<style>
  .hero {
		background-image: url("./banner.jpg");
	}
</style>
<div class="hero"></div>

<!-- A data URI: the image bytes inlined, no separate file -->
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA..." alt="Acme" />

The first three reference a file by relative path, resolved against your assets bundle: ./logo.png means the file sits next to the document that references it. See Assets for how paths resolve and what goes in the bundle.

A data URI is the exception. It carries the image bytes inline, so there's no separate file and no bundle lookup. It's handy for a small image generated at render time, though it inflates your HTML and isn't shared between references, so prefer a bundled file for anything reused.

Local assets only

Format reads images from the bundle you ship, never from a remote URL at render time. A path like https://cdn.acme.com/logo.png is rejected.

This keeps every render deterministic and offline-safe: a server that's down or a link that's changed can't break a document in production. The full reasoning is in Assets.

What happens when a reference can't be resolved depends on where you are:

  • In Studio, Format renders a loud animated placeholder in the image's place and a warning message. It's deliberately hard to miss in preview.
  • With the API, Format raises INVALID_ASSET_REFERENCE so a broken reference fails the render rather than shipping a PDF with a missing image.

Sizing and fitting

An image sizes with ordinary CSS inside the page's fixed box (see Page sizes). Set a width, cap it with max-width: 100%, or leave it to size intrinsically from the source:

img {
  width: 100%;
  max-width: 480px;
}

When you constrain an image to a box whose aspect ratio differs from the source, object-fit controls how the image resolves the mismatch. Use it for a photo dropped into a fixed flow, or a cover image that spans the page:

img {
  width: 100%;
  height: 200px;
  object-fit: cover;        /* fill the box, cropping the overflow */
  object-position: center;  /* choose which part survives the crop */
}

The cover value fills the box and crops the overflow, while contain fits the whole image inside and leaves letterboxing.

For a full-bleed page background, set the image on :host with background-size: cover and use padding rather than margin so it reaches the page edge. See Margins and Bleed and trim.

Sizing for sharpness

CSS measures in pixels at 96 DPI, but high quality print output needs 300 DPI. The gap between the two is often why an image that looks fine on screen can come out blurry in print, and it's primarily decided by your source's pixel dimensions.

How an image renders depends on whether it's on the plain path (drawn directly) or the raster path (passed through an effect that has no PDF equivalent).

Plain path

This is the default. Format embeds the source bitmap at its full resolution, independent of the CSS display size. A 6000 px wide source shown in a 500 px box stores all 6000 px in the PDF, so a viewer or printer can use the extra detail. A 200 px source in the same box stores 200 px and is upscaled at view time, and the quality depends on the viewer.

The takeaway: on the plain path, more source pixels never hurt sharpness. They only cost file size.

Raster path

This is the fallback for CSS effects that have no PDF equivalent (see What triggers the raster path). Format flattens the affected content to a bitmap at 300 DPI. That's destructive both ways: a source larger than 300 DPI is downsampled (wasted bytes and render time), and a source smaller than 300 DPI is upsampled and blurs.

How much source to ship

Match your source to the highest density it will be viewed at:

300 DPI (print resolution)96 DPI (CSS resolution)=3.125
  • Screen-only PDFs: ship at the CSS display size, the same principle as @2x retina images on the web. This stays sharp at 100% zoom on retina displays.
  • Print, or any image on the raster path: ship at 3.125× the CSS display size so it lands exactly at 300 DPI with no upscaling.
CSS boxMinimum for screen (2×)Minimum for print (3.125×)
200 × 200 px400 × 400 px625 × 625 px
500 × 333 px1000 × 666 px1563 × 1041 px
1000 × 750 px2000 × 1500 px3125 × 2344 px

For an image on the raster path, supply exactly 3.125× and no more: anything larger is downsampled back to 300 DPI, so the extra pixels are pure waste.

Choosing a format

Format doesn't transcode images at render time, so the format you ship is the format that lands in the PDF. Convert before the file enters your assets bundle.

FormatUse it forNotes
JPEGPhotographsEmbedded byte-for-byte, no re-encoding.
PNGFlat color: logos, diagrams, UI, transparencyRe-encoded under FlateDecode, which compresses flat color extremely well.
SVGVectors: icons, charts, pattern backgroundsStays vector; <text> stays selectable and currentColor cascades. See SVGs.
WebPNot recommendedRe-encoded under FlateDecode, roughly 8 to 9× bloat for photos. Use JPEG.
AVIFNot recommendedRe-encoded under FlateDecode, roughly 8 to 9× bloat for photos. Use JPEG.
GIFNot recommendedRenders the first frame only. Convert to JPEG (photos) or PNG (flat color).
BMPNot recommendedConvert to JPEG (photos) or PNG (flat color).
TIFFNot supportedConvert to JPEG (photos) or PNG (flat color).

Why format changes file size

PDF stores images differently depending on the source format, and the differences can be huge.

JPEG passes through untouched because PDF supports the JPEG codec natively. A 2 MB photo.jpg stays embedded as a 2 MB stream, byte for byte. Everything else (PNG, WebP, AVIF, GIF, BMP) is decoded to raw pixels and re-encoded with FlateDecode (zlib).

For flat color images, FlateDecode is excellent; zlib compresses solid regions and sharp edges down to almost nothing. Therefore, you should use PNG for transparent logos, diagrams, UI mockups, anything with flat color and sharp edges.

Screenshots are content-dependent. Terminal output, code editors, and simple UI screens compress well as PNG. Screens containing photographs, video frames, or complex art typically compress better as JPEG.

For photographic content, FlateDecode fails. zlib has no model for the noisy variation in real photos, so the stream balloons: a 173 KB WebP photo becomes ~1.4 MB inside the PDF, typically 8 to 9× larger than the source. That's why WebP and AVIF are a poor choice for PDF documents in general. They render correctly, but every photo pays a significant bloat cost. Use JPEG for photographic content instead.

Useful

Why do modern image formats bloat PDFs?

The age of the PDF format and its focus on backwards-compatibility is the underlying reason. PDF was released in 1993 and the codecs it natively supports (JPEG among them) come from that era. WebP (2010) and AVIF (2019) didn't exist at the time, so they fall back to FlateDecode.

SVGs

An SVG is resolution-independent and stays crisp at any scale, so the 3.125× sizing rule doesn't apply. SVGs can be used like you would on the web: inline <svg>, an <img src="*.svg">, or background-image: url("*.svg").

What's preserved

Inside an SVG, Format keeps:

  • <text> elements, which stay selectable and searchable in the PDF rather than flattening to outlines.
  • currentColor, which cascades from the surrounding CSS so one icon recolors with its context.
  • <pattern> fills and repeating SVG backgrounds, which stay vector instead of rasterizing.
Useful

An SVG referenced as an image (<img> or background-image) runs in a restricted mode and can't pull in external resources, such as an <image href> pointing at another file or an external font. Inline the SVG, or embed what it needs directly inside the file.

What triggers the raster path

Most content stays vector. Format only flattens to a 300 DPI bitmap when a CSS effect has no PDF equivalent:

  • filter: blur, contrast, hue-rotate, and the rest.
  • Some mix-blend-mode combinations, particularly when stacked with gradients. See Mix blend modes.

You don't need to preemptively avoid common effects. Format keeps several things vector that a browser would otherwise rasterize on export, with no work from you. This includes: box-shadow, SVG <pattern> fills, and CSS background-repeat.

When you can't avoid the raster path, ship the affected image at 3.125× its display size so it lands at 300 DPI without upscaling.

Was this page helpful?