网页上使用 Web Font 已经很普遍:Google Fonts、字体 CDN 提供一行 link 就能引入的字体服务,让页面摆脱系统字体限制。当页面需要导出 PDF 时,问题就来了——PDF 生成器在浏览器里运行,它能否用上页面已经加载的 Web Font,直接决定导出文档与网页观感是否一致。dompdf.js 渲染的是真实 DOM,页面与模板里通过 link 或 @font-face 引入的 Web Font 都能被识别和复用,配合 document.fonts.ready 等待字体就绪,就能把网页上精心挑选的字体原样带进 PDF。本文围绕 Web Font 的引入方式、Google Fonts 的具体集成步骤、加载时序控制、自托管与 CDN 的取舍以及中文场景的特殊策略展开,帮助开发者把 Web Font 集成做成一次到位、可维护的工程实践,而不是每次导出都靠运气,让网页与 PDF 的字体体验彻底统一。
系统字体依赖用户设备安装,同一份 HTML 在不同电脑上可能渲染出不同字体,PDF 导出结果也随之漂移;Web Font 把字体文件作为资源随页面分发,所有用户看到的字形一致,导出的 PDF 也天然统一。对品牌和文档规范化而言,这种确定性正是 Web Font 的核心价值,也是它成为现代前端标配的原因。
Web Font 按需加载:浏览器只在页面真正用到某个字体族时才下载对应文件,未用到的字重不会浪费流量。这个机制在 PDF 生成场景同样适用,模板里只引用需要的字重,就能控制加载体积;但同时也要注意,字体下载是异步的,生成 PDF 的时机必须在字体就绪之后,否则就会静默回退到系统字体,功亏一篑。
Web Font 文件格式经历了 EOT、WOFF、WOFF2 的演进,WOFF2 压缩率最高,已成为现代标准;dompdf.js 的字体加载基于 Web 平台能力,模板中同时声明多种格式可以让兼容性与体积兼得,浏览器会自动选择支持的最优格式,开发者按标准写法声明即可,不需要为 PDF 场景做额外适配。
Google Fonts 提供两种引入方式:link 标签引用 CSS,或 @import 在样式表中引入。link 方式有预连接优化、解析更快,是官方推荐做法;@import 写法更集中,适合所有样式都在 CSS 里维护的项目。两种方式本质都是加载一段 CSS,其中包含按需拆分的 @font-face 声明,浏览器按页面实际使用情况下载字体文件。
以 link 方式为例,Google Fonts 生成的 URL 形如 fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;700,浏览器请求后会按 UA 返回对应格式的字体声明。对 PDF 生成来说,只要这段 CSS 被页面加载,模板中同名 font-family 就能复用同一批字体文件,不需要二次声明,网页与 PDF 字形天然一致,集成成本非常低。
注意 Google Fonts 的 CSS 是按需拆分的:它会把字体拆成多个 unicode-range 子集,浏览器只下载用到的分片。模板如果只用到部分字符,实际下载量很小;但这也意味着首次生成 PDF 前字体可能尚未加载完,必须用 document.fonts.ready 等待,才能保证 PDF 里所有字符都有正确字形,这一步骤不能省略。
模板里同时放 link 标签与样式声明,dompdf.js 解析时会把页面已加载的字体资源一并用于渲染;如果模板是独立 HTML 字符串而非页面 DOM,link 指向的字体 CSS 同样会被请求和解析,@font-face 声明与页面级声明效果一致。关键是生成前等待 document.fonts.ready,确保字体文件真正下载完成,这一步是所有 Web Font 集成的共同前提。
字体族回退顺序要精心设计:拉丁字体在前、中文字体在后、通用字体族兜底。Inter 负责英文数字,Noto Sans SC 负责中文,sans-serif 兜底其他场景,三层的顺序让每个字符都能找到最合适的字形,混排文档里英文与中文的观感都经过设计,而不是各自为政,段落整体协调统一。
display=swap 参数让字体加载期间先用回退字体显示文本,避免文字不可见;对 PDF 生成而言,这个参数影响的是页面体验,生成结果仍以字体就绪后的字形为准。生产环境建议在真实网络条件下验证一次完整流程,确认字体 URL 可达、无跨域拦截,导出结果稳定可复现,上线后不用再回头排查。
import { DomPDF } from 'dompdf.js';
// 1. 模板中引入 Google Fonts(link 方式)与样式声明
const html = `
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;700&family=Inter:wght@400;600&display=swap" rel="stylesheet">
<style>
body { font-family: 'Inter', 'Noto Sans SC', sans-serif; font-size: 11pt; }
.title { font-weight: 700; font-size: 20pt; }
</style>
<h1 class="title">Web Font Integration Demo</h1>
<p>Inter 负责拉丁字符,Noto Sans SC 负责中文,sans-serif 兜底。</p>`;
// 2. 等待所有 Web Font 加载完成
await document.fonts.ready;
// 3. 字体就绪后再生成 PDF
const pdf = new DomPDF();
pdf.addPage(html, { format: 'A4' });
pdf.save('webfont-demo.pdf');
document.fonts.ready 返回的 Promise 在所有字体加载完成后 resolve,是最可靠的字体就绪信号。集成 Web Font 的页面导出 PDF 时,务必在 addPage 之前 await 它;如果模板还使用了页面尚未声明的字体,应先把字体加入 document.fonts(FontFace API)再等待,顺序不能颠倒,否则等待的字体集合与模板使用的字体集合不一致,门控就失效了。
更精细的控制可以用 FontFaceSet 的事件:load 事件在字体加载成功后触发,loadingdone 表示全部完成,loadingerror 表示部分失败。对导出这种一次性任务,await document.fonts.ready 已经足够;需要给用户进度反馈时,再监听 load 事件按加载数量更新进度条,体验更完整,代码也不复杂,工作量可控。
还有一个容易忽略的点:字体缓存。同一字体第二次生成 PDF 时,浏览器缓存命中,document.fonts.ready 会立即 resolve,生成速度显著提升。因此把字体资源放在可缓存的静态路径、URL 保持稳定,能让高频导出场景越用越快,是低成本高收益的优化手段,值得在部署阶段就规划好。
CDN 的优势是接入快、可能有跨域节点加速,缺点是可用性不受自己控制:服务被墙、限流或下线都会让字体加载失败,PDF 静默回退到系统字体。对面向中国用户的系统,Google Fonts 这类境外 CDN 尤其要评估可达性,建议要么选择国内可用的字体服务,要么干脆自托管,把风险彻底关在门外,交付稳定性优先。
自托管把字体文件放在自己的静态资源目录,配合 preload 或 CSS 内联声明,加载路径完全可控,离线环境也能生成 PDF,还便于统一走公司 CDN 和缓存策略。代价是要自己维护字体文件的更新,以及声明格式的编写,但这些是一次性成本,换来的稳定性和可预测性对导出类功能非常值得,长期收益明显。
自托管时建议同时提供 woff2 与 ttf/otf 两种格式:woff2 体积小、加载快,作为首选;ttf/otf 作为兼容回退。@font-face 里 src 可以写多个 url,浏览器按顺序尝试,直到找到可用格式,这种声明方式兼顾了现代浏览器与兼容场景,也是 PDF 生成环境最稳妥的写法,新旧环境都能正常工作。
中文 Web Font 与拉丁字体最大的不同是体积:完整中文字库十几兆起,直接整包加载会拖垮页面。Google Fonts 的 Noto Sans SC 按 unicode-range 分片,实际只下载用到的字符分片,是中文 Web Font 相对可行的方案;dompdf.js 内置思源黑体则提供了更省事的路径——中文正文直接用内置字体,零加载成本,效果同样规范。
推荐策略是分级使用:正文中文用内置思源黑体,标题和品牌文字用自定义 Web Font,英文数字用拉丁 Web Font。这样既保证中文不缺字、不等待,又让重点文字呈现品牌字形,加载体积与视觉效果取得平衡,是中文 PDF 项目里性价比最高的组合,也最容易被团队接受和长期执行。
常见问题:字体加载后 PDF 仍是默认字形,先检查 document.fonts.ready 是否真正执行、字体名是否一致;中文字体加载极慢,优先考虑子集化或内置字体;离线环境导出失败,改用自托管或 data URL 内嵌字体。把这三个问题提前想清楚,中文 Web Font 集成基本不会踩坑,交付质量稳定。
下面的按钮用 dompdf.js 在浏览器端实时生成 PDF,无需后端:
这是由 dompdf.js 渲染的示例 PDF 内容。