← dompdf.js Studio

Background Images and Data URL Embedding in dompdf.js

Background images are everywhere in web design: card textures, report headers, watermarks, decorative layers above gradients. When a PDF is generated, whether those backgrounds render correctly decides the perceived quality of the document, and backgrounds are more failure-prone than plain img tags precisely because they are controlled by CSS — loading timing, tiling behavior, and print styles all influence the final result. dompdf.js fully parses and renders CSS background-image, so backgrounds in your template flow into the PDF together with their styles. Converting background images to data URLs, meanwhile, is the universal technique for making a template self-contained: no paths to break, no CORS to negotiate, no network dependency at generation time. This guide starts with how backgrounds behave inside the PDF render, explains the data URL mechanism and the conversion paths for local files, network images, and canvas content, quantifies the base64 size inflation and how to control it, covers pagination and print adaptation for large backgrounds, and closes with a solution-selection checklist plus the most common failure modes. The goal is to make the background layer of your exports fast, stable, and visually faithful, so the delivered document looks exactly like the design. The decision framework also covers the situations where data URLs are the wrong answer, because choosing deliberately is as important as knowing how to convert. By the end you will have both the conversion recipes and the judgment to apply them, so background layers stop being a source of surprise in delivered documents and start being a predictable, repeatable part of your export pipeline.

How Background Images Render in the PDF

dompdf.js renders the real DOM and real CSS, so background-image is supported as a standard style property: url() references, linear gradients, tiling, and positioning parameters are all carried into the PDF according to the CSS specification. A background that looks right in the template looks right in the export with no special adaptation, which keeps the integration cost close to zero for the common cases.

The constraints live mostly at the resource level. Background images are subject to the same loading-timing and CORS rules as regular images: a background that has not finished loading, or that is not authorized for read access, silently disappears from the PDF. There is also a subtle rendering difference: background sizing and positioning parameters can behave slightly differently across render pipelines, so designs that depend on exact dimensions should declare background-size explicitly instead of relying on defaults.

A useful debugging trick is to render the same template in the browser first and confirm the background is visible and correct before handing it to PDF generation. If the page shows the background but the PDF does not, the problem is almost always loading timing or resource reachability, and you can apply the same troubleshooting approach you would use for an img tag. Backgrounds and images share the same resource-loading machinery, so the diagnosis skills transfer directly.

It is also worth remembering that backgrounds participate in the same resource machinery as images, which cuts both ways: the preload and CORS techniques you already use for img tags apply verbatim, and the same debugging habits carry over. The symmetry means you rarely need background-specific tooling; you need the discipline to treat backgrounds as first-class resources in the export flow.

Data URLs: The Key to Self-Contained Templates

A data URL writes the image bytes directly into the resource address as base64 text, in the form data:image/png;base64 followed by the encoded content. Its value is self-containment: the template no longer depends on external files, paths, or servers, so it works offline, never triggers CORS, and reproduces identically in any environment. That makes data URLs the natural choice for contracts, receipts, and other documents where stable, reproducible delivery matters more than marginal size savings.

The conversion paths are mature and standard. Local files go through FileReader.readAsDataURL; network images are fetched, turned into a Blob, and then read; canvas content is exported with toDataURL. All three produce a standard data URL that plugs directly into an img src or a CSS background-image url(), exactly like a regular URL, so the integration cost is essentially zero and the syntax is identical everywhere.

The cost is size: base64 inflates the bytes by about a third, and templates with dozens of backgrounds grow noticeably. Data URLs also live in memory as strings, so large or numerous images consume real memory for the whole lifetime of the template. The practical guidance is to use data URLs for small-to-medium images in limited quantities, and to prefer same-origin paths or a proxy for large backgrounds, choosing deliberately rather than converting everything by default.

There is a corollary worth stating: a data URL is only as good as the bytes it contains. Converting an oversized or poorly compressed source embeds the waste permanently into the template, so the compression guidance in this guide applies before conversion, not after. Treat data URL generation as the last step of an optimization pipeline rather than the first step of template assembly, and the template stays lean without any extra effort at generation time.

Code Example: Converting an Image to a Data URL Background

Using a data URL background means doing the conversion first and then composing the CSS. The example below shows the complete flow: fetch the background image, convert the response to a Blob, read the base64 string with FileReader, splice it into background-image, and finally generate the PDF with addPage. Because the template becomes fully self-contained, the result is identical in every environment, which is precisely the reliability contract that data URLs deliver.

The async/await style and the Promise wrapper keep the conversion readable, and the toDataUrl helper can be extracted into a shared utility used by both the page and the export flow. The source address can be any reachable URL; after conversion the template no longer cares whether that address still exists, and cross-origin and offline problems disappear in one step. This is the most care-free property of the data URL approach and the reason it survives in production codebases.

Quote nesting deserves attention when composing the template: the template literal uses backticks, and the CSS url() should wrap the data URL in single quotes to avoid escaping chaos. Long data URLs reduce template readability, so extract the background declaration into a separate variable before interpolating it. The result stays clean, maintainable, and easy for teammates to extend without decoding the string by eye.

One more consideration: data URLs in CSS are not cached by the browser the way network resources are, so a template that embeds the same background in several places duplicates the string in memory and in the output. If a background appears repeatedly, reference it once through a shared class or a CSS variable rather than pasting the data URL into every rule. The template stays smaller, and the render cost stays flat.

import { DomPDF } from 'dompdf.js';

async function toDataUrl(url) {
  const blob = await (await fetch(url)).blob();
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => resolve(reader.result);
    reader.onerror = reject;
    reader.readAsDataURL(blob);
  });
}

const bg = await toDataUrl('images/banner.png');

const html = `
  <style>
    body { font-family: 'Source Han Sans SC', sans-serif; }
    .banner {
      height: 200px;
      background-image: url('${bg}');
      background-size: cover;
      color: #fff;
      display: flex;
      align-items: center;
      justify-content: center;
    }
  </style>
  <div class="banner">Quarterly Business Analysis Report</div>`;

const pdf = new DomPDF({ format: 'A4', margin: '20mm' });
pdf.addPage(html, { format: 'A4' });
pdf.save('banner-demo.pdf');

Base64 Inflation and Size Control

Base64 encodes every three bytes as four characters, so the payload grows by a fixed 33 percent — an inherent cost of the data URL format. The only effective way to control the inflation is at the source: compress or downsample the image to its actual display size with a canvas before converting. A two-megabyte background compressed to 200 kilobytes shrinks the inflation cost tenfold, and the visible quality difference is usually zero for web-sized assets.

The compression strategy should follow the image type. Photographic backgrounds convert to JPEG at a quality around 0.8, which cuts size dramatically with no perceptible loss; assets that need transparency stay PNG, but check first for redundant dimensions and unused channels, and crop or optimize before embedding so useless pixels do not ride along into the template and its memory footprint.

Size control should also respect an overall budget: evaluate total template size, memory usage, and generation time together. Backgrounds beyond a couple of hundred kilobytes should generally use a same-origin URL rather than a data URL; multiple small backgrounds can be merged into a sprite, or replaced entirely by pure CSS gradients where the visual effect allows. Lighter alternatives remove the size problem at the root instead of fighting it after the fact.

For teams that generate many documents, the compression step belongs in a shared preprocessing utility so every template receives uniformly optimized assets. The utility can also record the original and final sizes, which gives you the data to decide when a background is small enough for a data URL and when it should stay a same-origin resource. Decisions backed by numbers replace rules of thumb, and the whole pipeline becomes measurably better with each asset it processes.

Pagination and Print Adaptation for Large Backgrounds

Large backgrounds and pagination need deliberate coordination. When dompdf.js paginates automatically, content crossing a page boundary is split by block, and a background attached to an element moves with that element. For full-page backgrounds such as letterhead or report covers, place the background in a per-page structure so every page carries a complete, unbroken background instead of a cut-off fragment.

Print styles are the second critical point: browsers do not print background images by default, and the render pipeline needs explicit help. Declare print-color-adjust: exact (with the vendor-prefixed -webkit-print-color-adjust: exact alongside it) in the template so backgrounds and background colors survive the export. Without this declaration, a template that looks perfect in the browser can export with all backgrounds missing — a failure mode that is silent and easy to ship to customers.

For headers and footers, the CSS @page margin boxes are the recommended mechanism: content placed in regions like @top-center repeats on every page, and combining it with counter(page) produces consistent letterhead and watermark effects across the document. This is more reliable than manually repeating background markup inside every content block, and it follows the structure printers and PDF viewers expect, keeping multi-page documents uniform and professional.

If a document mixes full-page backgrounds with flowing body content, test the boundary pages explicitly: generate a document whose content ends exactly at a page break and confirm the background on the last page renders complete rather than clipped. Boundary cases like this are where background-pagination bugs hide, and one targeted test catches them before they reach a delivered file.

A subtle point about print adaptation: the declaration affects backgrounds globally, so test it against the whole document rather than a single element. A template with many background layers can reveal interactions — a background that renders correctly on its own but disappears when combined with another rule — that only show up in a full-document check.

Selection Checklist and Common Problems

A three-rule decision checklist covers most background scenarios: if the background comes from your own service and is large, use a same-origin URL; if the image is small or must work offline, convert it to a data URL; if the source is uncontrolled or requires authentication, route it through a proxy. Frequency of reuse should also factor in — high-frequency backgrounds are worth promoting to shared template styles, while one-off content is best inlined on demand.

The common problems cluster into three groups. Missing backgrounds are almost always a loading-timing issue: preload the assets before rendering. Blurry backgrounds are usually a source-resolution issue: check the original before blaming the pipeline. Misplaced backgrounds are typically a background-size or positioning mismatch: declare the size and position explicitly. Each group has a direct fix, and working through them in order resolves the large majority of cases quickly.

Finally, adopt a background-image convention for the team: a unified image directory, consistent naming and dimensions, styles managed centrally, and background assets flowing through the same preload and caching path as regular images. Once the convention is in place, backgrounds move from the error-prone category to the stable one, the fidelity between PDF output and design stays consistently high, and future iterations stop re-litigating the same problems.

The convention also makes migrations easier: when an image host changes or a background is redesigned, the shared directory and style location mean one update fixes every document that uses it, rather than a hunt through individual templates. That maintainability is the quiet payoff of treating backgrounds as infrastructure rather than one-off decorations, and it compounds with every document the team produces. The rules may feel like overhead on day one; they feel like a gift on day two hundred.

⚡ 现场演示(点击生成 PDF)

下面的按钮用 dompdf.js 在浏览器端实时生成 PDF,无需后端:

Hello from dompdf.js!

这是由 dompdf.js 渲染的示例 PDF 内容。