← dompdf.js Studio

dompdf.js 背景图与 dataURL 嵌入完整指南

网页设计中背景图无处不在:卡片纹理、报表头图、水印、渐变之上的装饰层。生成 PDF 时,背景图能否正确呈现,直接决定文档观感,而背景图恰恰比普通图片更容易出问题——它由 CSS 控制,加载时机、重复平铺、打印样式都可能影响最终渲染。dompdf.js 完整支持 CSS background-image 的解析与渲染,模板中的背景图会随样式一起进入 PDF;而把背景图转成 data URL 嵌入,则是保证模板自包含、彻底摆脱路径与跨域依赖的通用手段。本文从背景图在 PDF 中的渲染规则讲起,介绍 data URL 的原理与转换方法、base64 的体积膨胀问题、大背景图的分页与打印适配,最后给出方案选型与常见问题清单,帮助开发者把背景图这一环做得又快又稳。

背景图在 PDF 渲染中的支持与限制

dompdf.js 渲染真实 DOM 与 CSS,background-image 作为标准样式属性被完整支持:url() 引用的图片、线性渐变、平铺与定位参数都会按 CSS 规范还原到 PDF 中,模板里写好的背景效果,导出后与网页基本一致,不需要为背景图做特殊适配,使用成本很低。

需要注意的限制主要在资源层面:背景图同样受加载时序与跨域规则约束,未加载完成或未授权的背景图会静默缺失;另外 background 的尺寸与定位参数在不同渲染管线中存在细微差异,依赖精确尺寸的场景建议显式声明 background-size,而不是依赖默认行为,输出才可预期。

调试背景图问题有个技巧:先在浏览器里用同样的模板渲染一遍,确认背景可见且样式正确,再交给 PDF 生成。若网页正常而 PDF 缺失,问题几乎都在加载时序或资源可达性上,按普通图片的排查思路处理即可。背景图与 img 共享同一套资源加载机制,排查方法完全通用。

data URL:自包含模板的核心手段

data URL 把图片字节以 base64 文本形式直接写进资源地址,格式为 data:image/png;base64 加编码内容。它的价值在于自包含:模板不再依赖外部文件、路径与服务器,离线可用、不跨域、可复现,任何环境下生成的 PDF 都一致,非常适合合同、票据这类需要稳定交付的场景,彻底消除环境差异。

转换方式很成熟:本地文件用 FileReader.readAsDataURL,网络图片用 fetch 拿 Blob 再转,canvas 内容用 toDataURL。三种方式得到的都是标准 data URL,可以直接放进 img 的 src 或 CSS 的 background-image 的 url() 中,与普通 URL 用法完全一致,接入成本几乎为零。

代价是体积:base64 编码让字节膨胀约三分之一,几十张背景图的模板会明显变大;同时 data URL 作为字符串常驻内存,大图多图场景内存占用可观。因此 data URL 适合中小体积、数量有限的图片,大图背景应优先走同源路径或代理,按需选用而不是一刀切,平衡好体积与便利。

代码示例:图片转 data URL 作为背景

把背景图转成 data URL 再写入模板,需要先完成转换,再拼接 CSS。下面示例演示完整流程:fetch 拉取背景图、转 Blob、FileReader 读出 base64、拼成 data URL 放进 background-image,最后用 addPage 生成 PDF。模板完全自包含,任何环境下结果一致,交付稳定。

示例中用到了 async/await 与 Promise 封装,转换函数 toDataUrl 可以独立成公共工具,供页面与导出流程共用。背景图地址可以是任意可访问的 URL,转换后模板不再关心原地址是否可达,跨域、离线问题一并消失,这是 data URL 方案最省心的地方,也是它被广泛使用的原因。

拼接模板时注意引号层级:模板字符串用反引号,CSS 的 url() 里用单引号包住 data URL,避免转义混乱。data URL 较长时模板字符串可读性下降,可以把背景声明拆成单独变量再插入,保持代码整洁,也方便后续维护与复用,团队协作体验更好。

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">季度经营分析报告</div>`;

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

base64 膨胀与体积控制

base64 把每 3 个字节编码为 4 个字符,体积固定膨胀约 33%,这是 data URL 的固有成本。控制膨胀的方法只有从源头下手:先用 canvas 把图片压缩或降采样到实际显示尺寸,再转 data URL。一张 2MB 的背景图压到 200KB 后,膨胀成本也随之缩小十倍,收益非常直接。

压缩要按图片类型选择策略:照片背景转 JPEG 并设置 0.8 左右的压缩质量,体积大幅下降且观感无损;带透明通道的素材保持 PNG,但先检查是否有冗余尺寸与多余通道,用工具裁剪优化后再嵌入,避免把无用像素一起带进模板,浪费体积与内存。

体积控制还要看整体预算:模板总大小、内存占用与生成耗时都要评估。超过一两百 KB 的背景图优先考虑同源 URL 而非 data URL;多张背景图可以合并成雪碧图,或改用纯 CSS 渐变实现视觉效果,用更轻的方案替代图片,从根上消掉体积问题,页面与导出同时受益。

还有一个容易忽略的点:data URL 会显著拉长模板字符串,若模板还需要传给后端或写入数据库,体积与转义问题都要一并考虑。批量场景建议把转换结果缓存起来,同一图片多次导出时直接复用,避免重复转换浪费 CPU 与内存,细节优化积累起来效果可观。

大背景图的分页与打印适配

背景图与分页的配合需要专门处理:dompdf.js 自动分页时,内容跨页会按块拆分,固定在元素上的背景随元素移动,而想要整页平铺的背景(如信纸、报告封面)最好放在每页独立的结构中,避免跨页断裂的观感问题,让每页背景都完整呈现。

打印样式是另一个关键点:浏览器默认不打印背景图,dompdf.js 的渲染需要样式配合。模板中显式声明 print-color-adjust: exact(及带浏览器前缀的版本),确保背景色与背景图在导出时被保留,否则可能出现网页正常、PDF 背景全丢的情况,这个声明几乎每个带背景的模板都需要。

页眉页脚场景推荐用 CSS @page 的 margin 盒子实现:@top-center 这类区域内的背景与内容按页重复出现,配合 counter(page) 可以做出每页一致的信头与水印效果,比在每个内容块里手动重复背景更可靠,也符合打印文档的规范结构,多页文档的观感统一专业。

打印适配的验证同样要覆盖真实场景:在浏览器打印预览与 PDF 导出两种方式下分别检查背景的呈现效果,确认 print-color-adjust 声明生效、每页背景完整无断裂。把验证步骤写进发布清单,背景图相关的回归问题就能在测试阶段被发现,而不是等到客户反馈。

背景图方案选型与常见问题

方案选型可以按三条规则决策:背景图来自自己服务且图片较大,直接用同源 URL;图片较小或需要离线自包含,转 data URL;来源不可控或需要鉴权,走代理。规则之外还要考虑复用频率,高频背景图值得做成模板公共样式,低频内容按需内嵌,投入产出比最合理。

常见问题集中在三处:背景缺失多半是加载时序,先预加载再渲染;背景模糊多半是源图分辨率不足,先检查源图再考虑降采样;背景位置偏移多半是 background-size 与定位参数差异,显式声明尺寸与位置即可。三类问题都有明确解法,按序排查,大多数都能快速解决。

最后建议建立背景图规范:统一图片目录、命名与尺寸,模板样式集中管理,背景图资源随文档一起走预加载与缓存。规范一旦落地,背景图就从易错点变成稳定项,PDF 输出与设计稿的还原度持续保持在高位,团队协作更顺畅,后续迭代也不用反复踩同样的坑。

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

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

Hello from dompdf.js!

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