Puppeteer生成PDF时自定义字体不加载(截图正常)、div背景色异常求助
核心问题排查与解决步骤
一、自定义字体生成PDF时失效(回退Times New Roman)
你之前的等待逻辑存在两个核心问题,直接导致字体加载校验失效:
- 硬加15秒延时没有针对性,全新无缓存的Puppeteer上下文如果遇到字体路径404、CORS拦截,哪怕等1分钟也会回退默认字体;
- 你写的
page.evaluateHandle('document.fonts.ready')没有实际等待Promise完成,只是拿到了Promise对象的句柄就直接执行PDF生成,根本没等字体就绪,这也是为什么你在evaluate里取document.fonts经常返回undefined——旧版本Chromium在headless打印模式下不会完整暴露FontFace API接口。
对应修正操作:
- 替换原有等待逻辑,不依赖页面内的FontFace API,直接监听字体资源的网络请求完成,再加字体可用性校验:
// 替换原有的15秒硬延时、无效的fonts.ready调用 // 等待所有woff字体文件的网络请求返回完成 await page.waitForRequest(/\.woff2?$/, { timeout: 30000 }); // 主动校验自定义字体是否真正加载完成,避免路径错误导致的静默回退 await page.evaluate(() => { const testFontSize = '12px'; const customFont = `${testFontSize} ProximaNova-Regular, sans-serif`; const fallbackFont = `${testFontSize} sans-serif`; const canvas = document.createElement('canvas').getContext('2d'); // 测量自定义字体的文本渲染宽度 canvas.font = customFont; const customWidth = canvas.measureText('字体渲染宽度校验样本').width; // 测量回退字体的文本渲染宽度 canvas.font = fallbackFont; const fallbackWidth = canvas.measureText('字体渲染宽度校验样本').width; // 宽度差小于2px说明自定义字体未生效,强制重排触发加载 if (Math.abs(customWidth - fallbackWidth) < 2) { document.documentElement.style.fontFamily = '"ProximaNova-Regular", sans-serif'; } }); - 调整Puppeteer启动参数,避免字体加载被浏览器安全策略拦截:
const browser = await puppeteer.launch({ headless: true, args: [ '--disable-web-security', // 避免styled-components生成的@font-face被CORS规则拦截 '--font-render-hinting=none' // 关闭Chromium字体渲染提示,避免字重匹配错误导致的字体回退 ] }); - 额外校验:确认你写在
@font-face里的字体路径/fonts/ProximaNova-Regular.woff是真的部署在站点根路径下的,浏览器截图显示正常很大概率是本地缓存了旧的资源,Puppeteer是完全无缓存的全新上下文,一旦字体路径返回404,就会直接静默回退系统默认字体。
二、PDF中出现截图不存在的多余背景色
这个问题基本都是Chromium打印PDF时的渲染特性导致的,不是页面样式本身的问题:
- Puppeteer调用
page.pdf时,默认printBackground参数为false,打印时会自动忽略页面自定义的背景色,同时对带透明效果的元素(比如用了backdrop-filter、透明色的div)自动叠加不透明的白色背景,就会出现截图里完全不存在的色块。 - 对应修正操作:
调整page.pdf的调用参数,显式开启背景打印,关闭不必要的打印优化:await page.pdf({ path: 'hn.pdf', format: 'a4', printBackground: true, // 必须显式开启,否则Chromium会自动给透明元素叠加默认白背景 preferCSSPageSize: true, displayHeaderFooter: false, // 关闭默认的页眉页脚,避免额外的色块干扰 margin: { top: 0, right: 0, bottom: 0, left: 0 } }); - 如果调整参数后还是有多余背景,就检查页面内所有用了
backdrop-filter的元素——Chromium打印模式下完全不支持毛玻璃效果渲染,会强制给这类元素加不透明背景,可以在生成PDF前临时把这类样式清掉:await page.evaluate(() => { // 打印前临时移除毛玻璃样式,避免自动叠加背景 document.querySelectorAll('*').forEach(el => { el.style.backdropFilter = 'none'; el.style.webkitBackdropFilter = 'none'; }) })
内容的提问来源于stack exchange,提问作者Dharmi
相关产品推荐
相关产品推荐

