news 2026/9/19 22:12:59

jsPDF 快速上手与深入实践:在浏览器与 Node.js 中纯客户端生成 PDF

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
jsPDF 快速上手与深入实践:在浏览器与 Node.js 中纯客户端生成 PDF

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 也印证了这些入口(mainmodulebrowserexports字段均指向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 分别以escjs(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:既支持预设名称(a0a10b0b10c0c10dllettergovernment-letterlegaljunior-legalledgertabloidcredit-card),也支持以数值数组自定义尺寸,如[595.28, 841.89](即 A4 的 pt 尺寸)。自定义数组传入后由getPageFormat之外的路径直接使用(见 src/jspdf.js 的_addPage处理)。

除上述三个常用选项外,构造器还支持putOnlyUsedFonts(仅把用到的字体写入 PDF)、compress(压缩生成的 PDF)、precision(元素位置精度,默认 16)、userUnithotfixes以及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.jsbrowser环境解析到./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.jsontypes/typings字段均指向它):

import { jsPDF } from "jspdf";

Meteor 项目可以这样添加:

meteor add jspdf:core

Polyfills

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

也可以直接用fetchXMLHttpRequest把 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 模式前,beginFormObjectbeginTilingPattern必须由对应方法关闭。

八、演示、示例与测试

仓库提供了丰富的上手资源:

  • 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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 22:11:57

自动驾驶示范区建设核心:路侧感知与云控平台全链路解析

简介&#xff1a;北京市高级别自动驾驶示范区建设发展报告&#xff08;2022年度&#xff09;系统梳理了示范区从设立到2.0阶段的建设历程&#xff0c;围绕车路云一体化技术路线、智能网联汽车政策创新、路侧基础设施与云控平台部署等关键内容展开&#xff0c;帮助读者全面了解高…

作者头像 李华
网站建设 2026/9/19 22:11:27

AI模型优化:从参数竞赛到体验优先的技术转型

1. 行业变局&#xff1a;当AI竞赛不再只是数字游戏上周业内朋友聚会时&#xff0c;大家讨论最多的不是某家机构又发布了万亿参数模型&#xff0c;而是Meta突然推迟了新AI模型的发布计划。这让我想起三年前参加某技术峰会时&#xff0c;全场都在比较各家模型的参数量&#xff0c…

作者头像 李华
网站建设 2026/9/19 22:10:26

智慧农业物联网系统建设:从架构设计到落地验收完整指南

简介&#xff1a;这是一份围绕智慧农业物联网系统建设的完整方案文档&#xff0c;面向农业园区规划者、设施农业工程师、物联网项目设计与投标人员&#xff0c;重点解决温室、大田、水产养殖场景下环境精准监测、自动化控制与远程管理落地难题。内容以托普云农园区案例为蓝本&a…

作者头像 李华
网站建设 2026/9/19 22:07:52

AI如何提升学术论文投稿成功率?核心技术解析

1. 期刊投稿困境与破局之道"又被拒稿了"——这大概是科研工作者最不愿看到的邮件开头。据统计&#xff0c;全球SCI期刊平均录用率仅为15%-30%&#xff0c;而中文核心期刊的竞争更加激烈。许多研究者花费数月完成的论文&#xff0c;往往在编辑初审阶段就被直接拒稿&am…

作者头像 李华