ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

html-pdf-chrome 完整指南:3 步让 Node.js 输出与浏览器像素一致的 PDF

html-pdf-chrome 完整指南:3 步让 Node.js 输出与浏览器像素一致的 PDF html-pdf-chrome 完整指南3 步让 Node.js 输出与浏览器像素一致的 PDF【免费下载链接】html-pdf-chromeHTML to PDF or image (jpeg, png, webp) converter via Chrome/Chromium项目地址: https://gitcode.com/gh_mirrors/ht/html-pdf-chrome你在浏览器里打开账单页面样式、字体、背景色都挑不出毛病可一到服务器端导出 PDF排版就开始各显神通。问题出在渲染引擎很多老派转 PDF 的方案用的是过时的 WebKit 内核和你用户在 Chrome 里看到的根本不是同一个世界。html-pdf-chrome 的解法很直接——让真正的 Chrome/Chromium 来完成渲染再把结果导出成 PDF 或图片png、jpeg、webp 都支持。你在浏览器里看到什么导出的文件里就是什么。它基于 Chrome DevTools Protocol 实现Windows、macOS、Linux 通吃要求 Node.js 18 以上。 为什么浏览器看着对PDF 里不对生成 PDF 这件事本质上是找一个引擎把 HTML 重新渲染一遍。引擎越接近用户实际使用的浏览器输出越不会跑偏。传统方案如基于老 WebKit 的命令行工具对 CSS3、现代字体、复杂布局的支持有限复杂页面容易面目全非。html-pdf-chrome 直接复用 Chrome 内核JavaScript 照常执行、现代 CSS 照常生效所见即所得。它同时覆盖两类产物分页的 PDF适合报表、发票、合同和整页截图适合预览图、缩略图、营销配图。一个依赖两种产出。 原理一分钟速览借 Chrome 的打印管线干活理解它的工作方式只需要知道两点。第一连接方式有两种。如果你传入了host或port它就通过 DevTools 协议连上那个已经在运行的 Chrome如果两个都不传它会用chrome-launcher现场拉起一个 headless Chrome默认带--headless、--disable-gpu、--hide-scrollbars参数用完即关。第二产物是异步返回的一个结果对象。核心入口就一个函数import * as htmlPdf from html-pdf-chrome; // 第一个参数是 HTML 字符串也可以是 http(s)、file:、data: 开头的 URL const doc await htmlPdf.create(htmlString, options);拿到的doc是一个结果对象想怎么取就怎么取await doc.toFile(out.pdf); // 落盘成文件 const buf doc.toBuffer(); // Node Buffer const b64 doc.toBase64(); // Base64 字符串方便走接口返回 const stream doc.toStream();// 可读流适合直接吐给 HTTP 响应也就是说它既可以给你文件也可以给数据接入任何后端都顺手。 上手三步装好、常驻、出 PDF1. 安装依赖npm install --save html-pdf-chrome一行搞定没有额外的二进制下载环节Chrome 用你系统上现成的。2. 让 Chrome 常驻运行每次生成 PDF 都冷启动一次 Chrome开销不小。官方建议的做法是让 Chrome 与 Node 应用并行常驻推荐用 pm2 托管——万一进程挂了会自动拉起来npm install -g pm2 pm2 start google-chrome \ --interpreter none \ -- \ --headless \ --disable-gpu \ --remote-debugging-port9222这条命令做的事把 headless 的 Chrome 以 9222 端口对外暴露调试端口交给 pm2 监管。官方还提到一个参考数据headless Chrome 空闲时大约只占 65MB 内存常驻成本很低。3. 生成第一份 PDFimport * as htmlPdf from html-pdf-chrome; const invoiceHtml h1发票/h1p应付金额¥1,000.00/p; const options { port: 9222 }; // 常驻 Chrome 监听的端口 const doc await htmlPdf.create(invoiceHtml, options); await doc.toFile(invoice.pdf);上面这段代码把一段 HTML 交给 9222 端口上的 Chrome 渲染渲染完打印成 PDF最后写进invoice.pdf。如果你的场景是偶发调用比如本地脚本也可以什么都不配——不传host和port时它会自动拉起一个 Chrome任务完成后再关掉属于随用随起模式。️ 进阶玩法把每一页都攥在手里页眉页脚与边距Chrome 65给printOptions里塞两个 HTML 模板就能做自定义页眉页脚。模板里有五个魔法类名会自动注入真实值date打印日期、title文档标题、url文档地址、pageNumber当前页码、totalPages总页数const doc await htmlPdf.create(html, { port: 9222, printOptions: { displayHeaderFooter: true, headerTemplate: div classtext centerspan classpageNumber/span / span classtotalPages/span/div, footerTemplate: div classtext center打印日期 span classdate/span/div, marginTop: 0.5, // 边距单位是英寸四个方向都能调 marginBottom: 0.5, marginLeft: 0.5, marginRight: 0.5, }, });注意两个坑页眉页脚里如果想放图片必须用 base64 内联模板占用的空间要靠上面的 margin 给出来否则正文会被顶掉。输出高分辨率截图甚至模拟手机只要传了screenshotOptions产物就从 PDF 变成图片。格式支持 png、jpeg、webp可以裁剪区域再配合deviceMetrics模拟移动端const shot await htmlPdf.create(html, { port: 9222, screenshotOptions: { format: png, clip: { x: 0, y: 0, width: 800, height: 600 }, // 只截这块区域 }, deviceMetrics: { width: 375, // 手机视口宽度 height: 667, deviceScaleFactor: 2, // 2 倍图发朋友圈不糊 mobile: true, }, }); await shot.toFile(mobile-preview.png);这段代码模拟了一台 375x667 的移动端视口以 2 倍像素比截出指定区域的图——做响应式页面巡检或商品预览图很实用。加载完不等于渲染完选对完成信号页面load事件触发时AJAX 数据可能还在路上直接打印就会截到半成品。html-pdf-chrome 提供了一组completionTrigger让你自己定义什么时候算好了。常见选择const options { port: 9222, // 等网络空闲SPA、图表页首选 completionTrigger: new htmlPdf.CompletionTrigger.LifecycleEvent(networkIdle, 10000), };其余几种按需取用第二个参数都是超时毫秒数// 傻等固定时长适合没有明确信号的老页面 new htmlPdf.CompletionTrigger.Timer(3000) // 等某个 DOM 元素出现再打印最贴近内容就绪 new htmlPdf.CompletionTrigger.Element(#app-ready, 8000) // 等页面自定义 JS 把标志位置为 true new htmlPdf.CompletionTrigger.Variable(renderDone, 8000) // 等页面派发自定义事件也可以指定监听哪个元素默认 body new htmlPdf.CompletionTrigger.Event(chart-finished, #chart, 5000) // 让页面代码主动回调通知我画完了 new htmlPdf.CompletionTrigger.Callback(onPageDone, 5000)经验值静态页面用Element或Variable最稳数据驱动的 SPA 用LifecycleEvent(networkIdle)实在没有信号再退而求其次用Timer。带鉴权、带 Cookie 的受控环境如果你的页面需要登录态才能渲染可以在请求层做文章const options { port: 9222, extraHTTPHeaders: { Authorization: Bearer *** }, // 附加任意请求头 cookies: [{ name: sid, value: abc123, domain: .example.com, path: / }], clearCache: true, // 加载前先清掉浏览器缓存拿到的就是最新内容 timeout: 30000, // 整体超时 30 秒防止任务挂死 runtimeConsoleHandler: (e) console.log(页面 console:, e.type), runtimeExceptionHandler: (e) console.error(页面抛错:, e.exceptionDetails), };runtimeConsoleHandler和runtimeExceptionHandler这对组合排查问题特别好用页面里console.log了什么、抛了什么异常都会实时回传到你的 Node 进程不用再对着打印结果不对瞎猜。⚠️ 常见坑一次说清跨域资源加载失败。页面引用的第三方 CSS/图片被 CORS 拦住时可以加--disable-web-security这个 Chrome 参数外部启动时加或放进chromeFlags配置。但官方警告得很直白只有在你完全信任正在渲染的代码时才这么干。别喂不受信任的输入。官方明确说这个库不适合直接接收用户输入的内容——让无头浏览器去访问用户指定的任意地址就是 SSRF服务器端请求伪造风险。对外服务时URL 和 HTML 都要先过校验。任务卡住时看这三类报错。超时抛的是HtmlPdf.create() timed out.Chrome 中途挂掉抛HtmlPdf.create() connection lost.主文档导航失败抛HtmlPdf.create() page navigate failed.。看到前两种优先检查 Chrome 进程是否还活着、timeout和触发器的超时值是否给够了。超时值按页面复杂度分档。简单静态页 5–10 秒足够数据密集的 SPA 给 30–60 秒图片视频多的页面再往上加。下一步建议按这个组合起步pm2 常驻 Chrome port直连 LifecycleEvent(networkIdle)完成信号 显式timeout基本能覆盖 80% 的生产场景遇到截到半成品或CORS 报错再按上面的进阶章节逐项调整。所有可配置项的完整类型定义和注释都写在 src/CreateOptions.ts 里遇到拿不准的参数直接翻那个文件比自己猜要快得多。【免费下载链接】html-pdf-chromeHTML to PDF or image (jpeg, png, webp) converter via Chrome/Chromium项目地址: https://gitcode.com/gh_mirrors/ht/html-pdf-chrome创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表