尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

在浏览器中使用 Webpack 5 打包 PDFKit:官方示例的完整配置指南

发布时间:2026/9/24 16:23:09

资讯中心
01
ARTICLE

在浏览器中使用 Webpack 5 打包 PDFKit:官方示例的完整配置指南

在浏览器中使用 Webpack 5 打包 PDFKit:官方示例的完整配置指南
后端文档【免费下载链接】pdfkitA JavaScript PDF generation library for Node and the browser项目地址https://gitcode.com/gh_mirrors/pd/pdfkit点击查看免费下载本文基于 PDFKit 仓库中官方自带的examples/webpack示例系统讲解如何在浏览器端用 Webpack 5 将 PDFKit 及其字体、图片资源打包为一个可运行的 PDF 生成应用。读完本文你将掌握 PDFKit 浏览器打包的全部关键配置如何处理 Node 原生模块buffer、stream、zlib、util、assert、如何把二进制资源内联为 base64、如何按需懒加载大文件、如何注册标准字体与自定义 TTF 字体以及如何在浏览器中实时生成并预览 PDF。示例项目概览PDFKit 的仓库中提供了两个浏览器端打包示例examples/browserify使用 Browserify和examples/webpack使用 Webpack。本文聚焦后者其完整目录结构如下examples/webpack/ ├── package.json # 依赖与 dev/prod 构建脚本 ├── webpack.config.js # 核心打包配置 └── src/ ├── index.html # 演示页面代码编辑器 PDF 预览 iframe ├── index.js # 入口含示例 PDF 生成代码 ├── assets.js # 内联字体与图片的注册/导出 ├── pdfkitHelpers.js # 收集 PDF 输出流并转成 data URL ├── httpHelpers.js # 基于 XMLHttpRequest 的懒加载工具 ├── static-assets/ # 被打包为 base64 内联的资源fonts/、images/ └── lazy-assets/ # 以独立 URL 输出的懒加载资源test.jpeg该示例实现的是一个“浏览器在线 PDF 演示页”左侧是 Ace 代码编辑器右侧 iframe 实时渲染 PDF。示例程序覆盖了 PDFKit 的核心能力文本排版多栏、两端对齐、缩进、省略号、矢量图形三角形、圆形、SVG path、doc.addPage()多页文档、标准字体与自定义 Roboto 字体、图片插入以及懒加载图片的兜底处理。运行示例示例的依赖与脚本定义在 examples/webpack/package.json{ dependencies: { assert: ^2.1.0, brace: ^0.11.1, browserify-zlib: ^0.2.0, buffer: ^6.0.3, pdfkit: ^0.15.0, process: ^0.11.10, readable-stream: ^4.5.2, util: ^0.12.5 }, devDependencies: { html-webpack-plugin: ^5.6.0, transform-loader: ^0.2.4, webpack: ^5.91.0, webpack-cli: ^5.1.4 }, scripts: { dev: webpack --mode development, prod: webpack --mode production } }运行步骤# 在 examples/webpack 目录下安装依赖 npm install # 开发模式构建输出未压缩的 bundle便于调试 npm run dev # 生产模式构建压缩产物适合部署 npm run prod构建产物由HtmlWebpackPlugin注入到由 examples/webpack/src/index.html 生成的页面中直接用浏览器打开即可看到编辑器与 PDF 预览。注意该示例声明依赖pdfkit: ^0.15.0而仓库根目录的 package.json 当前版本为0.20.1两者 API 兼容本文描述的核心机制对两个版本均成立。Webpack 5 核心打包配置webpack.config.js 是整个示例的灵魂只有 47 行却解决了浏览器端运行 PDFKit 的三大难题。下面逐段拆解。1. 忽略 crypto 模块显著缩小体积PDFKit 的 Node 端实现依赖 Node 内置crypto用于 PDF 加密如 AES-128但在纯浏览器场景下通过PDFDocument构造时若不传入密码参数则完全不需要加密功能。示例通过resolve.fallback将crypto显式置为false让 Webpack 在遇到require(crypto)时直接提供一个空模块而不是报错或打包庞大的 polyfillresolve: { fallback: { // crypto module is not necessary at browser crypto: false } }依据PDFKit 仓库中的加密实现位于 lib/security.jsAES/RC4 算法位于 lib/crypto/。源码层面crypto仅在启用加密时才被使用因此浏览器端可以安全地忽略它。仓库的build-standalone脚本见 package.json 的browserify --standalone PDFDocument --ignore crypto也采用了完全相同的策略。2. 为 Node 原生模块提供浏览器 polyfillPDFKit 在浏览器环境通过lib/fs/browser.js、lib/stream/browser.js、lib/zlib/browser.js等替代 Node 实现映射关系见 package.json 的imports字段但部分依赖链仍会引用 Node 内置模块。示例为这些模块逐一提供了经过验证的浏览器替代品resolve: { symlinks: false, fallback: { crypto: false, buffer: require.resolve(buffer/), stream: require.resolve(readable-stream), zlib: require.resolve(browserify-zlib), util: require.resolve(util/), assert: require.resolve(assert/) } }各 polyfill 的作用与对应依赖原生模块polyfill 包作用bufferbuffer提供Buffer全局能力PDF 输出流与 base64 转换依赖它streamreadable-stream提供流式接口PDFDocument本身就是可读流doc.on(data)zlibbrowserify-zlib提供 FlateDecode 压缩PDF 内容流压缩的核心utilutil工具函数 polyfillassertassert断言模块 polyfill同时示例用webpack.ProvidePlugin自动注入Buffer与process避免在每个文件中手动importplugins: [ new HtmlWebpackPlugin({ template: path.resolve(__dirname, src/index.html) }), new webpack.ProvidePlugin({ Buffer: [buffer, Buffer], process: process/browser }) ]resolve.symlinks: false则确保 Webpack 在解析node_modules中的链接依赖时不会因符号链接导致重复打包或路径混乱尤其在 monorepo 或 yarn PnP 环境下。3. 静态资源内联为 base64、懒加载资源输出为 URL这是示例中最具实战价值的部分——用两条 Webpack 5 内置 asset 规则将不同目录的二进制资源区分处理module: { rules: [ // bundle and load binary files inside static-assets folder as base64 { test: /src[/\\]static-assets/, type: asset/inline, generator: { dataUrl: content { return content.toString(base64); } } }, // load binary files inside lazy-assets folder as an URL { test: /src[/\\]lazy-assets/, type: asset/resource } ] }type: asset/inlineWebpack 5 内置规则匹配src/static-assets下的所有文件TTF 字体、PNG 图片打包时直接以 base64 Data URL 形式内联进 JS bundle运行时无需任何网络请求type: asset/resource匹配src/lazy-assets下的文件构建时输出为独立文件import得到的是该文件的 URL只有在运行时请求该 URL 才会下载数据。知识链接type: asset/inline/asset/resource是 Webpack 5 取代旧版url-loader/file-loader的内置资源模块Asset Modules无需额外安装 loader。dataUrl生成器中的content为 Buffercontent.toString(base64)得到纯 base64 字符串而asset/inline默认的 Data URL 带data:...;base64,前缀因此 assets.js 中为图片手动拼接了前缀export const images { bee: data:image/png;base64,${bee} };4. linebreak 与 fontkit 的二进制数据README 中提到的“convert binary files used by linebreak and fontkit to base64”指的是 PDFKit 的两个关键依赖linebreak用于文本自动换行断行判定需要 Unicode 断行数据fontkit负责 TTF/OTF 字体解析、字形测量与子集化。这些依赖内部引用的数据文件同样会经过上述 asset 规则或源码内的 base64 处理确保在无 Nodefs的浏览器环境中也能读取。这一点在 package.json 的依赖列表fontkit: ^2.0.4、linebreak: ^1.1.0中可以印证。浏览器端字体与资源管理assets.jsassets.js 展示了浏览器端注册字体的标准姿势import { registerStdFonts } from pdfkit; import Courier from pdfkit/standard-fonts/Courier; import CourierBold from pdfkit/standard-fonts/CourierBold; import Helvetica from pdfkit/standard-fonts/Helvetica; // webpack is configured to load files in static-assets as base64 import robotoRegular from ./static-assets/fonts/Roboto-Regular.ttf; import bee from ./static-assets/images/bee.png; // is good practice to register only required fonts to avoid the bundle size increase too much registerStdFonts(Courier, CourierBold, Helvetica); const toBytes base64 Uint8Array.from(atob(base64), char char.charCodeAt(0)); export const fonts { Roboto: toBytes(robotoRegular) }; export const images { bee: data:image/png;base64,${bee} };要点解析registerStdFonts按需注册标准字体pdfkit/standard-fonts/*是 PDFKit 通过 package.json 的exports字段暴露的子路径如./standard-fonts/Courier每个字体都是可独立 import 的模块。示例只注册Courier、CourierBold、Helvetica三种源码注释明确指出这是“good practice”——只注册必要的字体避免 bundle 体积失控。注册后可像 Node 端一样直接使用doc.font(Courier)、doc.font(Courier-Bold)自定义 TTF 字体转字节数组Webpack 内联后的robotoRegular是 base64 字符串toBytes用atob解码为二进制字符串再转成Uint8Array。PDFDocument.registerFont(name, src)在浏览器端接受这种字节数组随后doc.font(Roboto)即可使用资源统一出口fonts与images作为单一对象导出后续代码可以整体注入到编辑器执行环境。浏览器端 PDF 流收集pdfkitHelpers.js在 Node 端doc.pipe(fs.createWriteStream(...))即可落盘在浏览器端没有fs示例通过 pdfkitHelpers.js 收集 PDFDocument 输出流并转换为可在 iframe 中预览的 Data URLexport const waitForData async doc { return new Promise((resolve, reject) { const buffers []; doc.on(data, buffers.push.bind(buffers)); doc.on(end, async () { const pdfBuffer Buffer.concat(buffers); const pdfBase64 pdfBuffer.toString(base64); resolve(data:application/pdf;base64,${pdfBase64}); }); doc.on(error, reject); }); };其原理是PDFDocument是一个可读流doc.on(data)会持续收到 PDF 字节块Bufferdoc.on(end)在doc.end()之后触发此时将所有块Buffer.concat合并为完整 PDF再转 base64 拼接成data:application/pdf;base64,...URL。doc.on(error)用于异常透传。这个工具函数的使用有个关键约束——必须在调用doc.end()之前调用源码注释明确提示 “waitForData must be called before call to doc.end()”否则会漏掉输出事件。这一点在 index.js 的示例代码中也有体现// waitForData must be called before call to doc.end() waitForData(doc) .then(dataUrl { iframe.src dataUrl; }) .catch(error { console.log(error); }); doc.end();懒加载资源httpHelpers.js 与运行时兜底对于不希望随首屏 bundle 一起加载的大文件如示例中的 test.jpeghttpHelpers.js 提供了一个基于XMLHttpRequest的异步获取工具export function fetchFile(fileURL, { type arraybuffer } {}) { return new Promise((resolve, reject) { const request new XMLHttpRequest(); request.open(GET, fileURL, true); request.responseType type; request.onload function(e) { if (request.status 200) { resolve(request.response); } else { reject(createFetchError(fileURL, request.statusText)); } }; request.onerror error reject(createFetchError(fileURL, error)); request.send(); }); }入口 index.js 中展示了完整的“懒加载 兜底”模式// testImage is an URL import testImageURL from ./lazy-assets/test.jpeg; import { fonts, images } from ./assets.js; fetchFile(testImageURL) .then(testImageData { images.test testImageData; }) .catch(error { console.error(error); });随后在生成 PDF 时尝试插入这张图若资源尚未加载完成则捕获异常并在 PDF 中输出提示文字try { doc.image(images.test); } catch (error) { doc.moveDown().text(${error}); doc.text(Image not loaded. Try again later.); }这是浏览器端 PDF 生成的典型异步时序问题处理范式资源加载是异步的而 PDF 绘制是同步的因此需要try/catch与后续重试机制来保证文档生成流程不被未就绪的资源中断。实时编辑演示页index.js 的完整 PDF 示例入口 index.js 本身就是一个可运行的 PDFKit 用法全集编辑器中的初始代码覆盖了以下 API可直接复制到 Node 端运行自定义字体注册与文本绘制doc.registerFont(Roboto, fonts.Roboto)后doc.font(Roboto).fontSize(25).text(...)矢量图形doc.moveTo(100, 150).lineTo(100, 250).lineTo(200, 250).fill(#FF3300)绘制填充三角形doc.circle(280, 200, 50).fill(#6600FF)绘制圆形doc.scale(0.6).translate(470, 130).path(M 250,75 L 323,301 ...).fill(red, even-odd)绘制 SVG path多栏排版doc.text(lorem, { width: 412, align: justify, indent: 30, columns: 2, height: 300, ellipsis: true })实现两栏、两端对齐、首行缩进与溢出省略号多页与图片doc.addPage()后doc.image(images.bee)doc.font(Courier-Bold)切换标准字体实时重执行Ace 编辑器内容变化时通过new Function(PDFDocument, lorem, waitForData, iframe, fonts, images, code)重新执行代码实现“改代码即出 PDF”的交互。注意事项bundle 体积权衡README 的 Caveats 部分对这套方案给出了重要的工程提醒The strategy to bundle binary files and standard fonts inlines them in source code, increasing the bundle size significantly.将二进制文件与标准字体内联进源码会显著增大 bundle 体积。因此示例给出了三条平衡策略只注册必要的标准字体registerStdFonts只传用到的字体避免 14 种内置字体全量打包大文件走lazy-assets按需加载通过asset/resource输出为独立文件运行时才请求忽略不必要的crypto依赖直接降低基础体积。在实际项目中你可以根据字体使用频率进一步细分高频小资源内联、低频大资源懒加载、标准字体按需注册从而在“开箱即用”与“体积可控”之间找到平衡。小结PDFKit 本身是双端Node Browser的 PDF 生成库其浏览器端打包的难点不在 PDFKit 自身 API而在于Node 原生模块的 polyfill、二进制字体/图片资源的处理策略、以及异步资源的时序管理。examples/webpack这个官方示例以极简的配置给出了完整的参考答案resolve.fallbackProvidePlugin解决 Node 模块依赖webpack.config.jsasset/inline与asset/resource两条规则区分“立即内联”与“懒加载”资源registerStdFonts按需注册标准字体assets.jswaitForData完成浏览器端 PDF 流收集与 iframe 预览pdfkitHelpers.jsfetchFiletry/catch处理懒加载图片的异步时序httpHelpers.js。这套配置可直接作为你在自己的 Webpack 5 项目中集成 PDFKit 的起点相关配置细节均可对照仓库源码进一步验证与调整。赞分享后端文档【免费下载链接】pdfkitA JavaScript PDF generation library for Node and the browser项目地址https://gitcode.com/gh_mirrors/pd/pdfkit点击查看免费下载相关推荐AriaNg GUI RPC配置终极教程如何连接多个Aria2服务器实现集群下载AriaNg GUI RPC配置终极教程如何连接多个Aria2服务器实现集群下载 想要充分利用AriaNg GUI的强大下载功能吗本文将为您详细介绍Aria桌面应用在 Vitest 中使用 WebdriverIO Provider 配置真实浏览器测试的完整指南在 Vitest 中使用 WebdriverIO Provider 配置真实浏览器测试的完整指南 WebdriverIO 是 Vitest 浏览器测试模式Br测试前端开发工具VSCode浏览器预览完整配置与使用指南VSCode浏览器预览完整配置与使用指南 项目价值与核心优势 VSCode浏览器预览是一个革命性的扩展工具它允许开发者在不离开编辑器环境的情况下直接预览和调上一篇GlosSI让Steam控制器在任何游戏和程序中都能使用的终极方案下一篇GlosSI为Windows游戏解锁系统级Steam控制器支持的终极方案创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。