Fonts and images in HTML to PDF: embed or link them correctly
How assets are loaded during conversion, and the reliable ways to include images and web fonts.
Your HTML looks perfect on your computer, but the PDF has broken images or falls back to a plain font. Almost always, the converter simply couldn’t load the files. Here’s how assets are loaded and the two reliable ways to include them.
Why relative paths don’t work
A path such as images/logo.png or ../fonts/brand.woff2 is relative to where the HTML file lives. When you upload one HTML file or paste code, only that HTML is sent; the folder around it isn’t. The converter has nothing to resolve the path against, so the asset can’t load.
Option 1: embed assets as data URLs
A data URL puts the file’s contents directly inside your HTML, so nothing needs to be fetched. It’s the most dependable choice for logos, icons and small images:
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA…" alt="Company logo">Most build tools and online encoders can produce these. Data URLs make the HTML bigger (base64 adds about a third), and pasted or uploaded HTML is limited to 2 MiB, so use them for small and medium assets.
Option 2: link to full public URLs
Absolute https:// URLs that anyone can open work too. The converter fetches them through a filtered network connection that only allows public addresses on the standard ports:
<img src="https://example.com/images/chart.png" alt="Sales chart">
<link rel="stylesheet" href="https://example.com/print.css">Assets on localhost, an intranet, a private IP address or behind a login can’t be fetched. Test each URL in a private browser window first.
What happens when an asset is missing
- The server answers with an error such as 404: the PDF is still made, without that asset. A missing image shows its alt text; a missing font falls back to the next font in your stack. Nothing warns you, so check the PDF preview.
- The host can’t be reached or is blocked (a domain that doesn’t exist, or a private address such as
localhost): the conversion fails with a message asking you to check asset URLs.
We measured both behaviours in our font case study.
Custom fonts with @font-face
Declare the font with an absolute URL or a data URL, then use it as normal:
<style>
@font-face {
font-family: "Brand Sans";
src: url("https://example.com/fonts/brand-sans.woff2") format("woff2");
font-weight: 400;
}
body { font-family: "Brand Sans", Arial, sans-serif; }
</style>- Include a fallback font in the stack in case a weight is missing.
- Declare each weight and style you use (400, 700, italic); otherwise the browser may synthesise bold or italic.
- Hosted font services that serve public CSS and font files generally work, provided their URLs are reachable.
- Check the font’s licence allows embedding in PDFs you distribute.
Accents, symbols and other scripts
Save your HTML as UTF-8 and include <meta charset="utf-8">. Non-UTF-8 files are rejected rather than converted with garbled text. For languages beyond Latin script, using a web font that covers those characters gives you consistent results.
Images that never appear
- Lazy loading: in our tests, images with
loading="lazy"near the top of the page were usually missing from the PDF. Remove it from images that must appear; see lazy-loaded images in PDF. - CSS background images: they follow the same rules as other images (public URL or data URL). They print with Print backgrounds on or off, except on
htmlorbody, which the setting removes. - Huge images: very large photos can push the PDF over the 20 MiB limit. Resize them for print first.
The live preview never fetches external files, for your privacy, so linked images show in the PDF but not in the preview. Embedded data images appear in both.