tspdf 缩写
TypeScript 的快捷,可依赖的光线PDF格式和文本. 没有瓦斯姆.
- 读取并填充 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) (中文(简体) ).
许可证
麻省理工学院