Puppeteer是目前最可靠的HTML转PDF方案,基于Chromium可真实还原CSS、字体、SVG及JS动态内容;需注意file://绝对路径、显式设置@font-face或启动参数、A4尺寸与页边距、printBackground:true等关键配置。
浏览器环境渲染的 HTML 转 PDF,Puppeteer 是目前最稳定的选择。它基于 Chromium,能真实还原 CSS 布局、字体、SVG、甚至 JS 动态内容,不像纯服务端工具(如 weasyprint 或 wkhtmltopdf)容易丢样式或报错。
常见错误现象:Puppeteer.launch() 报 Failed to launch chrome;生成的 PDF 字体缺失或乱码;页眉页脚位置偏移。
Puppeteer 自动下载:初始化时传 { headless: true, executablePath: null }(不指定路径即走自动下载)index.html 必须用 file:// 协议,且路径需绝对化:await page.goto('file://' + require('path').resolve('./index.html'))
@font-face 并引用本地 TTF 文件,或启动时加参数:{ args: ['--font-render-hinting=none'] }
page.pdf({ format: 'A4', margin: { top: '20px', right: '15px', bottom: '20px', left: '15px' } })
wkhtmltopdf 命令行简单,但底层依赖 QtWebkit,对现代 CSS(Flexbox/Grid)、ES6+ JS、@media print 支持差。很多用户卡在“PDF 空白”或“样式全崩”,其实不是配置问题,而是引擎不支持。
典型报错:QPainter::begin: Paint device returned engine == 0, type: 2;或 PDF 里只显示文字,无背景、无 border。
立即学习“前端免费学习笔记(深入)”;
apt install wkhtmltopdf),Ubuntu/Debian 默认版本太老,改用官网提供的静态二进制版--enable-local-file-access 才能读取本地 CSS/JS/图片,否则资源 404--no-stop-slow-scripts 和 --javascript-delay 2000 可缓解 JS 渲染不全,但治标不治本;复杂交互页面建议换 Puppeteer
以下是最小可运行脚本,保存为 html2pdf.js,直接 node html2pdf.js:
const puppeteer = require('puppeteer');const fs = require('fs').promises;(async () => { const browser = await puppeteer.launch({ headless: true }); const page = await browser.newPage(); // 注意:路径必须绝对,且带 file:// 前缀 await page.goto('file://' + (await fs.realpath('./index.html')), { waitUntil: 'networkidle0' // 等资源加载完再截图 }); await page.pdf({ path: 'output.pdf', format: 'A4', printBackground: true // 否则 background-color/background-image 不生效 }); await browser.close();})();
关键点:waitUntil: 'networkidle0' 比 'domcontentloaded' 更稳妥;printBackground: true 默认是 false,这点极易忽略。
PDF 里中文字体发虚、英文变宋体、图标变成方块——90% 是字体没嵌入或路径不对。不是 HTML 写得有问题,而是生成环节没告诉 Chromium “去哪找字”。
@font-face 引入绝对路径的 TTF 文件(如 url('/fonts/NotoSansCJK.ttc')),并确保该文件随 index.html 一起被 file:// 加载;② 启动 Puppeteer 时指定系统字体目录:{ args: ['--font-render-hinting=none', '--font-cache-dir=/usr/share/fonts/'] }(Linux 路径)file:// 下极易失效,所有 href / src / @font-face url() 都建议转成 file:///full/path/to/xxx 格式预处理真正卡住人的从来不是“怎么调 API”,而是路径协议、字体上下文、渲染时机这三个点交叉出问题。多打一行 console.log(page.url()) 看实际加载地址,比反复改 CSS 有用得多。