@folsom/tspdf

中文文档
0.1.0发布于 2026/07/31MITTypeScriptES Module近 30 天下载 2206

一个纯TypeScript PDF核心:懒散的解析,仅附件写入,AcroForm读取/填充/平面/合成,并取出文本. 没有瓦斯姆.

中文使用文档
文档目录20 节
  1. 目录
  2. 安装
  3. 使用量
  4. 阅读文档
  5. 填写表格
  6. 检查字段
  7. 处理页面
  8. 文本和搜索
  9. 平滑
  10. 组合文档
  11. 元数据和导航
  12. 同步装入
  13. 权限
  14. 未信任输入
  15. 逃入对象模型
  16. 业绩
  17. 限制
  18. 如何测试
  19. 捐款
  20. 许可证

tspdf 缩写

TypeScript 的快捷,可依赖的光线PDF格式和文本. 没有瓦斯姆.

npm bundle size license

  • 读取并填充 AcroForm 字段,包括复选框、收音机和下拉
  • 生成字段外观( E)/DA,自动大小,对齐,梳理,多行)
  • 将窗体平整为页面内容
  • 提取和搜索页面文本
  • 合并、分拆、复制、重排和旋转页面
  • 读写 /Info 和 XMP 元数据、大纲、页面标签、附件
  • 打开加密文件(R2通过R6:RC4,AESV2,AESV3)
  • 递增保存该附件而不是重写
  • 在节点和浏览器中相同的构建

运行在 @noble/hashes, (中文(简体) ), @noble/ciphers 财务报告和审定财务报表 fflate没有别的.

目录

安装

bun add @folsom/tspdf     # or npm / pnpm / yarn

使用量

阅读文档

import { PdfDocument } from "@folsom/tspdf";

const doc = PdfDocument.open(bytes);

doc.pageCount;      // 2
doc.encrypted;      // false
doc.info.title;     // "2025 Form 1040"

填写表格

const form = doc.form();

form.field("f1_01")?.setValue("Ada Lovelace");
form.field("c1_checkbox")?.setValue(true);
form.field("dropdown")?.setValue("Option B");

const { bytes } = doc.save();

保存附件 。 原始字节是输出的字节前缀, 因此设置一个值 a 780 KB文档增加了约500字节,而不是重写文件. 以往的每一次修订都保留 完好无损,这对签署的文件很重要.

setValue 报告发生了什么,而不是扔:

const r = form.field("f1_01")?.setValue("a name longer than the field allows");

r?.ok;         // false if the field is read-only or the value is not a valid choice
r?.written;    // what was stored, truncated to /MaxLen if necessary
r?.reason;     // why, when ok is false

检查字段

for (const f of form.allFields()) {
  f.name;        // "topmostSubform[0].Page1[0].f1_01[0]"
  f.kind;        // "text" | "checkbox" | "radio" | "choice" | "signature" | "pushbutton"
  f.value;       // current /V, or null
  f.options;     // /Opt choices
  f.maxLen;      // 0 when unset
  f.readOnly;
}

form.fieldNames();

处理页面

const page = doc.page(0);

page.size;               // { width: 612, height: 792 }, after rotation
page.label;              // "i", "ii", "1" ... per /PageLabels
page.widgets();          // widget annotations on this page
page.box("CropBox");
page.setRotation(90);
page.text();                    // whole page as a string
page.search("gross income");    // [{ page, box, quads, text }, ...]
page.glyphs();                  // per-glyph boxes, for layout work

doc.search("income");           // every page

搜索默认会崩溃白空间, 因为 PDF 将文字显示操作符的字词断开 不断的,一个字形的匹配 发现远远低于你预期.

平滑

const { baked, dropped, skipped } = doc.flatten();
const { bytes } = doc.save();

外观被画入页面内容并去掉表单. 无法烘烤的部件 (隐藏,没有矩形,堕落的盒子) /Annots 报告于 skipped。 。 。 .

组合文档

import { createDocument, addPage, copyPages, mergeDocuments, splitDocument } from "@folsom/tspdf";

const merged = mergeDocuments([a, b]);
const [chapter1, chapter2] = splitDocument(doc, [[0, 4], [5, 9]]);   // inclusive ranges

const dst = createDocument();
addPage(dst);
copyPages(src, dst, [0, 2, 4]);   // omit indices to copy every page

复制页保留其格式字段:被移植的部件被重新植入目的地的下面 /AcroForm,因此合并或分拆的文档仍然可以填充.

元数据和导航

doc.info;            // { title, author, subject, keywords, creator, producer, ... }
doc.setInfo({ title: "Filed 2025" });

doc.outline;         // [{ title, page, children }, ...]
doc.pageLabels;      // ["i", "ii", "1", "2", ...]
doc.attachments;     // [{ name, description, data }, ...]

同步装入

open() 在每个平台上是同步的. load() 解码结构流与前端 平台的本土 DecompressionStream,这在浏览器中更快,之后的每个查询 保持同步 .

const doc = await PdfDocument.load(bytes, { pages: [0] });

权限

文件可通过下列途径限制填充、修改和复制: /Ptspdf执行这些旗帜,所以 限制操作会丢出, 除非您选择退出 :

doc.page(0).text();                            // PermissionDenied on a copy-restricted file
doc.page(0).text({ allowRestricted: true });   // your call

doc.permissions;   // { print, modify, copy, annotate, fillForms, ... }

这些旗帜不是安全边界,因为持有文件的人可以剥去. 他们是 因此,绕过一个是决定而不是意外.

未信任输入

打开 PDF 表示解析一个敌对的数据结构. 两个界限默认关闭 :

PdfDocument.open(bytes, {
  timeoutMs: 5_000,                    // bounds parsing, page walks and xref recovery
  decodeBudget: 64 * 1024 * 1024,      // total decompressed bytes for the document
});

超过预算的流解码为空并设置 doc.budgetExhausted 而不是抛出, 因此,一个炸弹会降解 而不是拆除过程.

逃入对象模型

外表覆盖了常见案件. 为了其他一切, @folsom/tspdf/lowlevel 暴露出生层 :

import { N, isDict, isStream } from "@folsom/tspdf/lowlevel";

const cat = doc.dict(doc.trailer.get(N.Root));
const custom = doc.dict(cat?.get(N.AcroForm));

数值类型(PdfDict, (中文(简体) ), PdfStream, (中文(简体) ), PdfValue)从包根再出口,所以 签名无需导入即可保留名称 lowlevel。 。 。 .

每一个外观方法代表到自由函数,也可以输出. doc.page(0).text() 财务报告和审定财务报表 getPageText(doc, 0) 是同一个代码.

业绩

重现为 bun run bench在f990.pdf上被测量,778 KB有1075个田地.

tspdf 缩写pdfium木兰花pdf-lib 系统
打开 + 读取所有字段7.3 分钟34.4 毫秒(秒)20.8毫秒(每秒)221.2毫秒(秒)
填入20个字段并保存7.0 ms( 分钟)无径43.8分549.7毫秒(分钟)
平整并保存11.7个毫秒无无525.4 ms (英语)
摘录文本, 一页2.6个毫秒4.8 毫秒( 秒)无无
输出大小790 KB (韩语)无1842 KB (英语)1492 KB (韩语)

两种情况不同: mupdf 和 pdf-lib 读取列只获取字段名称 pdfium 构建了一个比 tspdf 更丰富的文本页面,排序为读取顺序并导出来自 格莱弗轮廓.

持有文档即是不同之处。 木兹 fill() 费用是否相同 您设置一个值或200个值, 因为每次呼叫都会重开并重排文件 :

设定值木兰花tspdf 缩写附 录
1个6.50 毫秒(千分秒)0.060毫秒(分钟)0.5 KB (韩语)
10个6.20分钟0.116 毫秒(秒)4.3 KB (韩语)
200个6.57个毫秒0.848 分钟76.0 KB (韩语)

新鲜过程的冷起步是36.8 ms 与 pdfium 86.9 ms 和 mupdf 的 67.9 ms 相对应 用于处理一个文件和退出的 CLI 的编号.

限制

tspdf 无法渲染。 没有光栅,也没有计划。 如果您需要像素, 请使用 pdfium 或 mu. 它还没有:

  • 解码图像解码器. DCT, JPX, JBIG2, CCITTFax 和 Brotli 流作为原始字节返回, 带有一个 undecoded 旗帜而不是扔出,所以结构和文本仍然用于使用它们的文件.
  • 嵌入或子集字体,或嵌入图像. 标准14度标准.
  • 画. 没有 drawText 编号 : drawRectangle 编号 : drawImage。 。 。 .
  • 读取嵌入式字体程序. 宽和 Unicode 来源于字体词典,而非 TrueType/CFF (英语) cmap,它是剩余文本提取出错的主要来源.
  • 处理带有打出 API 的部件以外的注释, 虽然它们在回转中幸存下来 .
  • 接受用户密码。 处理者假设一个空的,这就是权限锁定的大小写: 大多数公布的表格使用。 它报告这种假设失败时.
  • 支持预定义的 CJK CMaps 或垂直写入. 命名的非身份编码作为 2字节身份和身份-V横向排列.
  • 创建数字签名,尽管现有签名在增量节约后仍然有效.

如何测试

bun run test 进行全程检查:122次行为检查,检查过程中产生的PDF,没有 固定文件,没有甲骨文. test/fixtures.ts 建造它们;增加一个案例,而不是实施 二进制.

更深的门将 tspdf 与 pdf 和 mupdf 作为 oracles 而不是 自己的 期望,需要根据 ../data实地 按记录比较记录(46个政府和公司表格的9418个部件,无 匹配; 将书面文件交回两个引擎读取; 生成的外观是 由mupdf进行光化,并比较了墨水覆盖. 文本提取与 pdfium 关于99.80% 字符键.

更宽的覆盖范围来自pdf.js和pdfium regress corpora,932个文件,每个文件都存在 因为他们打破了一个解析器。 tspdf打开了其中908个;pdfium打开了896. 在他们之间 从 R2 到 R6 、 经典和流 xrefs 、 对象流和 恢复路径.

捐款

bun install
bun run build     # dist/ with .js and .d.ts
bun run check     # typecheck, lint, format
bun run test
bun run bench

差分的门需要 pdf 和 mupdf 的基线 外加一个体 ../data数字 :

bun run gate:diff        # field-for-field against pdfium
bun run gate:roundtrip   # fill, save, re-read with pdfium and mupdf
bun run gate:text        # extraction and window agreement
bun run gate:appearance  # generated /AP against mupdf's rendering
bun run gate:wild <dir>  # obscure and hostile corpora

bun run gate:spec 不需要这些,而是工作时要跑的.

test/fetch-corpus.sh 取出野兽尸体 mupdf、pdfium和pdf-lib是使用的依赖关系 仅作为测量基线.

test/pdfium.ts 是 pdfium 甲骨文: 生于 FPDF_* 对 WASM 模块绑定 @hyzyla/pdfium 已经是船了 因为它出版的包装只覆盖了渲染 它故意不进口任何东西 从 src/ 一个与它判断的东西共享代码的神谕 证明什么也没有.

src/ 按子系统分类: core (字节、对象、词典、解析器), codec (过滤器,密码), doc, (中文(简体) ), text, (中文(简体) ), form, (中文(简体) ), write复合模块名称为kebab-case(page-labels.ts) (中文(简体) ).

许可证

麻省理工学院

版本与发布记录

@folsom/tspdf 共发布 1 个版本,首次发布于 2026/07/31;过去 12 个月发布了 1 个版本。

版本发布日期距今
0.1.0latest2026/07/313 个月前

关于 @folsom/tspdf 的常见问题

@folsom/tspdf 是什么?

一个纯TypeScript PDF核心:懒散的解析,仅附件写入,AcroForm读取/填充/平面/合成,并取出文本. 没有瓦斯姆.

如何安装 @folsom/tspdf?

在项目目录运行 npm install @folsom/tspdf,或使用 pnpm add / yarn add / bun add。

@folsom/tspdf 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@folsom/tspdf 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@folsom/tspdf 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=18。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@folsom/tspdf 可以商用吗?

@folsom/tspdf 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@folsom/tspdf 的下载量有多少?

根据 npm 官方下载统计,@folsom/tspdf 在 2026/10/15 – 2026/11/13 这 30 天内被下载 2,206 次,日均约 74 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@folsom/tspdf 还在维护吗?

最新版本 0.1.0 发布于 2026/07/31(3 个月前);至今共发布 1 个版本,其中过去 12 个月发布了 1 个;首次发布于 2026/07/31。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。