Images
PNG and JPEG img elements are embedded as reusable PDF image XObjects.
<img> elements are treated as image primitives. PNG and JPEG sources are embedded in the PDF instead of being captured as part of a larger screenshot.
Supported sources
| Source | Status | Notes |
|---|---|---|
data:image/png;base64,... | Supported | decoded without network |
data:image/jpeg;base64,... | Supported | decoded without network |
/logo.png | Supported | fetched from same origin |
https://.../photo.jpg | Supported | CORS must allow browser fetch |
blob: URL | Supported | fetched by the browser |
| GIF / WebP / AVIF | Not supported | dropped with a warning |
| SVG image file | Not supported as image | use raster fallback for SVG regions |
Format detection
The emitter sniffs magic bytes before embedding:
- PNG:
89 50 4E 47 - JPEG:
FF D8 FF
The URL extension is not trusted. If a CDN returns HTML, a 404 page, or WebP behind a .png path, the image is skipped with a warning.
Deduplication
The same image source is embedded once and reused.
<img src="/logo.png" />
<img src="/logo.png" />
<img src="/logo.png" />Those three DOM nodes produce one PDF XObject.
Sizing
The image is drawn into the DOM box reported by getBoundingClientRect().
<img src="/logo.png" className="h-12 w-12" />The PDF uses the CSS size, not the intrinsic image size.
CORS
Remote images must be readable by browser fetch().
If an image disappears in the PDF:
- Open DevTools network panel.
- Confirm the image request succeeds.
- Confirm the response has an
Access-Control-Allow-Originpolicy that allows your app origin. - If not, host the image on your app origin or convert it to a data URL before rendering.
Known gap
object-fit: fill | contain | cover is honored. fill (the CSS default) stretches the image to the box; contain letterboxes it inside the box, keeping aspect ratio; cover fills the box and clips the overflow via a PDF clip path. none and scale-down are not modeled and collapse to fill.