jsPDF 快速上手与深入实践:在浏览器与 Node.js 中纯客户端生成 PDF
【免费下载链接】jsPDFClient-side JavaScript PDF generation for everyone.项目地址: https://gitcode.com/gh_mirrors/js/jsPDF
jsPDF 是一个纯 JavaScript 的 PDF 生成库,目标是"面向所有人"的客户端 PDF 生成(Client-side JavaScript PDF generation for everyone)。本指南以仓库根目录 README.md 为主线,完整覆盖安装方式、浏览器/Node.js/AMD/全局变量等模块格式的使用方法、Unicode/自定义字体集成、Node 文件读取安全策略、可选依赖管理以及 compat/advanced 双 API 模式,并结合 src/jspdf.js、src/index.js、src/modules/ 等源码给出实现级佐证。读完本文,你将能够在浏览器端用几行代码生成并下载 PDF,在 Node 端安全地读写文件,并正确接入中文字体与构建工具(Webpack/Vue CLI/Angular/React)。
一、安装:npm、yarn 与 CDN 三种方式
README 推荐的安装方式是使用 npm(或 yarn),安装后即以依赖形式进入你的工程:
npm install jspdf --save # 或 yarn add jspdf如果你希望始终获取最新版本,也可以通过 unpkg CDN 以<script>标签方式直接引入 UMD 构建产物:
<script src="https://unpkg.com/jspdf@latest/dist/jspdf.umd.min.js"></script>dist 目录中的文件类型
README 明确指出,包的dist目录包含不同种类的文件,仓库中的 package.json 也印证了这些入口(main、module、browser、exports字段均指向dist下的不同构建):
- jspdf.es.*.js:现代 ES2015 模块格式,供打包工具进行 tree-shaking;
- jspdf.node.*.js:面向 Node 运行,使用文件操作而非浏览器 API 来加载/保存文件;
- jspdf.umd.*.js:UMD 模块格式,可用于 AMD 或
<script>标签直接加载; - polyfills*.js:面向 IE 等旧浏览器的必需 polyfill。ES 变体通过
core-js引入所有必需 polyfill,UMD 变体则是自包含的。
从 rollup.config.js 可以确认这些产物由同一套源码(src/index.js为输入)经 Rollup 分别以es、cjs(Node)与umd三种格式构建,其中es/cjs产物把 package.json 中的 dependencies 与 optionalDependencies 声明为 external(不打包进产物),而 UMD 产物只将可选依赖 external。
通常你不需要在 import 语句中指定具体文件:构建工具或 Node 会自动解析出正确文件,直接import "jspdf"即可。
二、最小用法:三行代码生成并下载 PDF
import { jsPDF } from "jspdf"; // 默认导出为 a4 纸张、纵向(portrait)、单位毫米(mm) const doc = new jsPDF(); doc.text("Hello world!", 10, 10); doc.save("a4.pdf");new jsPDF()的默认行为(a4/纵向/mm)在源码中有明确体现:src/jspdf.js 中format = format || "a4"、orientation缺省取"p"(portrait)、unit = unit || "mm"共同构成了这套默认值。
自定义纸张尺寸、方向与单位
README 给出的官方示例演示了"横版 2×4 英寸"的 PDF 生成:
// Landscape export, 2×4 inches const doc = new jsPDF({ orientation: "landscape", unit: "in", format: [4, 2] }); doc.text("Hello world!", 1, 1); doc.save("two-by-four.pdf");结合 src/jspdf.js 的构造器注释,可以梳理出完整的可用取值:
- orientation:
"portrait"或"landscape"(也支持简写"p"/"l"),默认 portrait; - unit:
"pt"(点)、"mm"、"cm"、"in"(英寸)、"px"、"pc"、"em"、"ex"。注意:若想获得"px"单位下的正确缩放,需要启用 hotfix,即传入hotfixes: ["px_scaling"]; - format:既支持预设名称(
a0–a10、b0–b10、c0–c10、dl、letter、government-letter、legal、junior-legal、ledger、tabloid、credit-card),也支持以数值数组自定义尺寸,如[595.28, 841.89](即 A4 的 pt 尺寸)。自定义数组传入后由getPageFormat之外的路径直接使用(见 src/jspdf.js 的_addPage处理)。
除上述三个常用选项外,构造器还支持putOnlyUsedFonts(仅把用到的字体写入 PDF)、compress(压缩生成的 PDF)、precision(元素位置精度,默认 16)、userUnit、hotfixes以及encryption(见 src/jspdf.js)。
三、在 Node.js 中运行
jsPDF 也可以在 Node 中直接生成并保存 PDF 文件:
const { jsPDF } = require("jspdf"); // 会自动加载 node 版本 const doc = new jsPDF(); doc.text("Hello world!", 10, 10); doc.save("a4.pdf"); // 将文件保存到当前工作目录之所以require("jspdf")能自动加载 Node 版本,是因为 package.json 的exports字段按条件导出:node环境解析到./dist/jspdf.node.min.js,browser环境解析到./dist/jspdf.es.min.js。Node 版本使用文件操作(fs)而非浏览器 API 来加载和保存文件,这一实现位于 src/modules/fileloading.js。
四、其他模块格式:AMD 与全局变量
除了 ES Module 与 CommonJS,jsPDF 还支持 AMD 和全局变量两种加载方式,README 给出了完整示例:
AMD:
require(["jspdf"], ({ jsPDF }) => { const doc = new jsPDF(); doc.text("Hello world!", 10, 10); doc.save("a4.pdf"); });Globals(<script>标签引入 UMD 构建后):
const { jsPDF } = window.jspdf; const doc = new jsPDF(); doc.text("Hello world!", 10, 10); doc.save("a4.pdf");仓库的 examples/basic.html 展示了 UMD 引入的实际用法:先<script src="../dist/jspdf.umd.js">加载,再通过window.jspdf.jsPDF取用构造函数。这一兼容矩阵(ESM/Node/AMD/Globals)也正是dist中多套构建产物存在的原因,并且仓库在test/deployment/下分别维护了 amd、esm、globals、typescript、webworker 等多套部署测试来保证各加载方式可用。
五、安全:输入清洗与 Node 文件读取权限
输入清洗
README 强烈建议:在把用户输入传给 jsPDF 之前,务必先做清洗(sanitize)。jsPDF 本身不承担 HTML 消毒职责,凡是来自用户的字符串内容都应先经过消毒处理再写入 PDF,避免注入风险。
Node 下读取本地文件系统的限制
当在 Node 中运行时,jsPDF默认禁止读取本地文件系统。README 给出了两种放行方式,安全性有强有弱:
方式一(强烈推荐):使用 Node 的权限标志,由运行时强制实施访问控制
node --permission --allow-fs-read=... ./scripts/generate.js注意:--allow-fs-read必须包含所有被 import 的 JavaScript 文件(包括全部依赖),否则运行时也会因权限不足而拒绝读取。
方式二(不推荐作为首选):在脚本中设置jsPDF.allowFsRead
import { jsPDF } from "jspdf"; const doc = new jsPDF(); doc.allowFsRead = ["./fonts/*", "./images/logo.png"]; // 允许 ./fonts 下所有文件以及单个文件README 明确警告:推荐使用 Node 标志而非allowFsRead,因为标志由运行时强制执行,安全性更强。
从实现上看(src/modules/fileloading.js 的nodeReadFile),jsPDF 的读取流程是:先检查是否存在process.permission(Node 权限 API)或this.allowFsRead,两者皆无则直接抛错;随后把路径realpathSync解析为真实路径,若process.permission拒绝则抛权限错误;最后按allowFsRead中的模式匹配——既支持精确路径,也支持以单个*结尾的前缀通配(例如"./assets/*"匹配该目录下所有路径以./assets/开头的文件)。若process.permission可用,它会先被检查,即使allowFsRead放行也无法绕过运行时的拒绝。
可选依赖与 Webpack externals
jsPDF 的部分功能依赖可选依赖(optionalDependencies,见 package.json):例如html方法依赖html2canvas,且当传入字符串形式的 HTML 文档时还依赖dompurify。jsPDF 会在需要时动态加载它们——src/modules/html.js 的loadHtml2Canvas/loadDomPurify实现说明,在 ES 构建下通过动态import()加载,在 CJS/AMD 下则用require/require([...])加载,并兼容全局变量(globalObject["html2canvas"]/globalObject["DOMPurify"])已存在的情况。构建工具(如 Webpack)会自动为每个可选依赖生成独立 chunk。
如果应用不使用这些可选依赖,可以通过 externals 阻止 Webpack 生成对应 chunk(README 给出的 webpack.config.js 示例):
// webpack.config.js module.exports = { // ... externals: { // 只把你【没有使用】的依赖声明为 externals! canvg: "canvg", html2canvas: "html2canvas", dompurify: "dompurify" } };对应不同框架的处理方式(README 原述):
- Vue CLI:通过
vue.config.js(新项目需先创建)中的 configureWebpack 或 chainWebpack 属性定义 externals; - Angular:使用 custom webpack builders 定义 externals;
- React(create-react-app):通过
react-app-rewired或 eject 方式定义 externals。
TypeScript 与其他框架
jsPDF 可以像任何第三方库一样被导入,兼容所有主流工具链与框架,并且提供 TypeScript 类型声明文件(仓库中的 types/index.d.ts,package.json的types/typings字段均指向它):
import { jsPDF } from "jspdf";Meteor 项目可以这样添加:
meteor add jspdf:corePolyfills
jsPDF 依赖现代浏览器 API。要在 IE 等旧浏览器中使用,需要加载 polyfill:
import "jspdf/dist/polyfills.es.js";或者加载预打包的 polyfill 文件(不推荐,可能重复加载 polyfill;但小型应用或快速 POC 仍可用)。polyfill 产物同样由 Rollup 从 src/polyfills.js 构建(见 rollup.config.js 的umdPolyfills/esPolyfills配置),UMD 变体自包含所有 polyfill,ES 变体则通过core-js引入。
六、Unicode / UTF-8 与自定义字体
PDF 的 14 种标准字体仅覆盖 ASCII 码页。要输出 UTF-8 文本(例如中文),必须集成包含所需字形(glyph)的自定义字体。jsPDF 支持.ttf字体文件——如果字体不含中文等所需字形,PDF 中会显示乱码,所以务必确认所选字体包含目标字符集。
方式一:使用 fontconverter 转换工具
仓库自带字体转换工具 fontconverter/fontconverter.html(源码位于 fontconverter/ 目录,含 FileSaver.js、filereader.js 等辅助脚本)。它会将提供的 ttf 文件内容转为 base64 字符串并生成一段 js 代码文件;把生成的 js 文件加入项目后,即可用setFont方法书写 UTF-8 文本。
方式二:运行时动态加载 ttf
也可以直接用fetch或XMLHttpRequest把 ttf 文件作为二进制字符串加载,再注册到 PDF:
const doc = new jsPDF(); const myFont = ... // 以二进制字符串形式加载 *.ttf 字体文件 // 把字体加入 jsPDF 的 vFS(虚拟文件系统) doc.addFileToVFS("MyFont.ttf", myFont); doc.addFont("MyFont.ttf", "MyFont", "normal"); doc.setFont("MyFont");这三个 API 的实现分别位于:
addFileToVFS:见 src/modules/vfs.js,将文件内容存入this.internal.vFS[filename];addFont:见 src/jspdf.js,注册字体的 PostScript 名、字体名、样式(如"normal")与字重;setFont:见 src/jspdf.js,切换当前活动字体(内部通过getFont(fontName, fontStyle, ...)解析)。
字体的实际解析发生在addFont事件处理中(src/modules/ttfsupport.js):如果是标准字体则直接从 vFS 取 base64 内容,否则要求字体已存在于 vFS,否则抛出"Font does not exist in vFS, import fonts or remove declaration doc.addFont('...')"这样的错误提示。TTF 文件内容会被转换为Uint8Array供后续字形度量使用。
七、高级功能:compat 与 advanced 双 API 模式
jsPDF 与 yWorks fork 合并后引入了大量新特性,但部分特性是破坏性 API 变更,因此提供了两种 API 模式:
- compat 模式:与 MrRio 原版 API 完全一致,兼容所有插件,但部分高级特性(如变换矩阵、图案 pattern)不可用。这是默认模式;
- advanced 模式:即 yWorks fork 的 API,支持 pattern、FormObject、变换矩阵等全部高级特性。
在两种模式间切换:
doc.advancedAPI(doc => { // 你的代码 }); // 或 doc.compatAPI(doc => { // 你的代码 });回调执行完毕后,jsPDF 会自动切回原来的 API 模式。
从 src/jspdf.js 的实现可以看到:
advancedAPI()内部会保存图形状态、压入一个坐标变换矩阵(把用户坐标系转换到 PDF 坐标系),并把默认路径操作改为"n"(不描边);compatAPI()则恢复图形状态并把默认路径操作改回"S"(描边);API.advancedAPI(body)/API.compatAPI(body)支持可选回调:传入回调时执行完自动切回原模式;不传回调时则只切换模式、需要手动调用对应方法切回;- 还有
API.isAdvancedAPI()用于查询当前是否处于 advanced 模式; - 某些方法(如需要变换矩阵的功能)会通过
advancedApiModeTrap检查,若不在 advanced 模式下调用会抛出"... is only available in 'advanced' API mode. You need to call advancedAPI() first."错误。
注意使用约束:回调内(或两次调用之间)的saveGraphicsState/restoreGraphicsState调用必须配对;在切回 compat 模式前,beginFormObject或beginTilingPattern必须由对应方法关闭。
八、演示、示例与测试
仓库提供了丰富的上手资源:
- examples/basic.html 及其同目录下的 examples/js/basic.js 等示例脚本,覆盖文本、图形、字体等基础元素,浏览器直接打开即可交互运行;
- examples/vite/ 是一个完整的 Vite 工程示例(含 examples/vite/package.json 与 examples/vite/main.js),展示了在现代打包工具下的接入方式;
- 文档站点源码位于 docs/(由 JSDoc 从源码生成,配置见 jsdoc.json),包含各模块(addImage、annotations、html、utf8、vfs 等)的 API 文档页面。
构建与测试命令(见 package.json 的scripts):npm run build使用 Rollup 构建 dist;npm test依次运行 Node 测试(Jasmine)与浏览器测试(Karma,ChromeHeadless);npm run test-local可依次运行 unit、node、amd、esm、globals、typescript、webworker 全套部署测试,仓库在 test/ 目录下维护了大量 specs 与参考 PDF(test/reference/*.pdf)用于回归对比。
九、贡献与许可
jsPDF 欢迎社区贡献:如果觉得缺少特性或发现 bug,可以查看 CONTRIBUTING.md 了解构建与测试指引,并参考 CODE_OF_CONDUCT.md。Bug 报告应遵循 README 的建议:提供最小可复现示例(mcve)、格式化良好的代码、可运行的示例,并尽量证明问题确实与 jsPDF 相关而非所用框架导致。安全问题请遵循 SECURITY.md 的披露流程。
项目采用 MIT 许可(LICENSE),版权归 James Hall 与 yWorks GmbH 所有(2010-2025 / 2015-2025),允许自由使用、修改与分发。
【免费下载链接】jsPDFClient-side JavaScript PDF generation for everyone.项目地址: https://gitcode.com/gh_mirrors/js/jsPDF
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考