Skip to content

2026 最佳 Rust PDF crate 对比

PDF Oxide 与 Rust 生态中最常用的 PDF crate 正面对比:lopdf、printpdf、pdf-rs、pdf_extract。每个 crate 定位的抽象层级与使用场景不同——本页帮你为当前项目选出合适的 crate。

总结

PDF Oxide lopdf printpdf pdf-rs pdf_extract
API 层级 高级 底层 中级(创建) 底层(读取) 中级(读取)
读取 PDF 支持 支持 不支持 支持 支持
写入 PDF 支持 支持 支持 不支持 不支持
文本提取 支持(高级) 手动 不支持 手动 支持(基础)
图片提取 支持(高级) 手动 不支持 手动 不支持
表单字段 读写 手动 不支持 只读 不支持
PDF 创建 支持 支持 支持 不支持 不支持
Markdown/HTML 输入 支持 不支持 不支持 不支持 不支持
编辑现有 PDF 支持 支持(底层) 不支持 不支持 不支持
注释 读写 手动 不支持 只读 不支持
加密 读写 不支持 不支持 不支持 不支持
PDF/A 验证 支持 不支持 不支持 不支持 不支持
渲染 支持(tiny-skia) 不支持 不支持 部分 不支持
Python 绑定 支持 不支持 不支持 不支持 不支持
许可证 MIT MIT MIT MIT Apache-2.0

所有库都采用宽松许可证。差异在于功能范围和抽象层级。

性能对比

完整语料库基准测试(3,830 个 PDF)

在完整的 3,830 个 PDF 语料库上测试——包含三个独立的公开测试集,覆盖 PDF 规范合规性(veraPDF,2,907 个文件)、真实世界浏览器渲染边界情况(Mozilla pdf.js,897 个文件)和安全/健壮性压力测试,包括格式错误的结构和模糊测试生成的损坏(DARPA SafeDocs,26 个文件)。参见完整语料库详情

平均 p99 通过率 文本提取 备注
PDF Oxide 0.8ms 9ms 100% 内置,生产级 Unicode、CJK、阅读顺序
oxidize_pdf 13.5ms 11ms 99.1% 基础 48 秒最大异常值
unpdf 2.8ms 10ms 95.1% 基础 完整语料库 185 个失败
pdf_extract 4.08ms 37ms 91.5% 基础 缺少复杂布局支持
lopdf 0.3ms 2ms 80.2% 无内置提取 20% 的 PDF 解析失败

lopdf 在它能解析的 PDF 上更快——但它在 20% 的语料库上失败,且不提供文本提取。你需要自己构建字体解码、CMap 解析和间距分析。

pdf_extract 提供基础文本提取但通过率为 91.5%,在复杂布局、CJK 文本和带标签的 PDF 上表现不佳。oxidize_pdf 有不错的可靠性(99.1%)但平均提取时间比 pdf_oxide 慢 17 倍,且最坏情况达 48 秒。unpdf 能处理完整语料库但在 185 个 PDF 上失败。

PDF Oxide 是唯一将 100% 可靠性与生产级文本提取相结合的 Rust crate。

API 设计对比

PDF Oxide:高级、面向任务

PDF Oxide 提供面向常见任务的专用方法。你直接操作文本、图片和表单字段——而非 PDF 对象和字典。

use pdf_oxide::PdfDocument;

let mut doc = PdfDocument::open("report.pdf")?;

// 文本提取——一次调用
let text = doc.extract_text(0)?;
println!("{}", text);

// 带字体元数据的样式 span
let spans = doc.extract_spans(0)?;
for span in &spans {
    println!("'{}' font={} size={:.1}pt", span.text, span.font_name, span.font_size);
}

// 图片提取
let images = doc.extract_images(0)?;
for img in &images {
    println!("{}x{} {:?}", img.width, img.height, img.format);
}

// 表单字段
let fields = doc.extract_form_fields()?;
for field in &fields {
    println!("{}: {:?}", field.name, field.value);
}

PDF 创建同样简洁:

use pdf_oxide::api::Pdf;

// 从 Markdown
let pdf = Pdf::from_markdown("# Report\n\n| A | B |\n|---|---|\n| 1 | 2 |")?;
pdf.save("report.pdf")?;

// 从 HTML
let pdf = Pdf::from_html("<h1>Report</h1><p>Content here.</p>")?;
pdf.save("report.pdf")?;

lopdf:底层对象操作

lopdf 让你直接访问 PDF 对象、流和交叉引用表。你必须理解 PDF 规范才能有效使用它。没有内置文本提取——你需要自己遍历字典并解码流。

use lopdf::Document;

let doc = Document::load("report.pdf")?;

// 获取页面字典
let page_id = doc.page_iter().next().unwrap();
let page = doc.get_dictionary(page_id)?;

// 获取内容流——手动操作
let contents = page.get("Contents")?;
let stream = doc.get_object(contents.as_reference()?)?;

// 要提取文本你必须:
// 1. 解析内容流操作符
// 2. 从 /Resources 解析字体引用
// 3. 解码 CMap/ToUnicode 映射
// 4. 应用文本矩阵变换
// 5. 处理编码差异
//
// lopdf 不提供这些——它只是原始对象访问
println!("Page has {} objects", doc.objects.len());

lopdf 适合需要直接操作 PDF 结构的场景:合并文档、重写对象流或构建专用 PDF 处理器。

printpdf:仅限 PDF 创建

printpdf 是一个仅用于创建的库。它不能读取或解析现有 PDF。它提供类型化 API 用于从零构建 PDF 文档,支持文本、图片和矢量图形。

use printpdf::*;

let (doc, page1, layer1) = PdfDocument::new(
    "Report", Mm(210.0), Mm(297.0), "Layer 1"
);

let current_layer = doc.get_page(page1).get_layer(layer1);

// 添加文本——需要手动加载字体
let font = doc.add_builtin_font(BuiltinFont::Helvetica)?;
current_layer.use_text("Hello World", 24.0, Mm(10.0), Mm(280.0), &font);

// 保存
doc.save(&mut std::io::BufWriter::new(
    std::fs::File::create("output.pdf")?,
))?;

// 无法读取现有 PDF
// 无法提取文本、图片或表单字段

printpdf 适合只需要生成新 PDF 且需要干净的专用创建 API 的场景。

pdf-rs:底层 PDF 读取

pdf-rs 将 PDF 结构解析为 Rust 类型,但提供的高级功能极少。你可以获得对 PDF 对象的类型安全访问,但仍需自己处理文本解码、字体解析和内容流解析。

use pdf::file::FileOptions;

let file = FileOptions::cached().open("report.pdf")?;

// 访问页面对象
let page = file.get_page(0)?;
let media_box = page.media_box()?;
println!("Page size: {:?}", media_box);

// 内容流访问——底层
if let Some(ref contents) = page.contents {
    // 返回原始操作——你必须自己解释
    // 无内置文本组装、字体解码或布局分析
}

// 无法写入或修改 PDF

pdf-rs 适合需要类型安全的 PDF 解析器用于分析、验证或构建自定义渲染器的场景。

按任务的功能对比

文本提取

内置 质量 需要的工作量
PDF Oxide 支持 生产级(Unicode、CJK、阅读顺序) 一次方法调用
pdf_extract 支持 基础(缺少复杂布局) 一次方法调用
lopdf 不支持 数百行自定义代码
printpdf 不支持 不可能(只写)
pdf-rs 不支持 需要大量自定义代码

PDF Oxide 处理 CMap/ToUnicode 解码、基于字体度量的间距、结构树阅读顺序和连字重建。在 lopdf 或 pdf-rs 之上实现等效功能需要数千行代码和深入的 PDF 规范知识。

PDF 创建

方式 Markdown/HTML 输入 表格 条码
PDF Oxide 高级 + 底层 支持 支持 支持
lopdf 原始对象构建 不支持 不支持 不支持
printpdf 类型化图层 API 不支持 不支持 不支持
pdf-rs 无(只读)

加密

读取加密 写入加密 算法
PDF Oxide 支持 支持 RC4-40、RC4-128、AES-128、AES-256
lopdf 不支持 不支持
printpdf 不支持 不支持
pdf-rs 部分 不支持 仅 RC4

合规性

PDF/A PDF/X PDF/UA
PDF Oxide 验证 + 转换 验证 验证
lopdf 不支持 不支持 不支持
printpdf 部分(PDF/A-1b 输出) 不支持 不支持
pdf-rs 不支持 不支持 不支持

依赖足迹

依赖数 编译时间 二进制大小
PDF Oxide ~40(核心) ~30s ~4 MB
lopdf ~15 ~10s ~1 MB
printpdf ~20 ~15s ~2 MB
pdf-rs ~25 ~20s ~2 MB

PDF Oxide 的依赖更多是因为它包含字体解析、图片解码、内容流解释和加密——这些功能其他库要么留给用户要么完全省略。启用所有可选功能(renderingbarcodesoffice)后,依赖数会增加到 ~100。

组合使用库

由于所有库都采用宽松许可,你可以在同一个项目中组合使用:

[dependencies]
pdf_oxide = "0.3"
lopdf = "0.32"        # 可选:用于边界情况的原始对象访问

常见模式:

  • PDF Oxide + lopdf:使用 PDF Oxide 做提取和创建,对需要原始对象操作的边界情况回退到 lopdf。
  • PDF Oxide + printpdf:使用 PDF Oxide 做读取,printpdf 做专门的创建工作流。

用例矩阵

“我需要从 PDF 提取文本”

Crate 适合? 备注
PDF Oxide 最佳提取质量、100% 通过率、阅读顺序、字体元数据
pdf_extract 部分 基础提取,91.5% 通过率
lopdf 无文本提取
printpdf 不能读取 PDF
pdf-rs 部分 基础解析,无高级文本提取

“我需要创建 PDF”

Crate 适合? 备注
PDF Oxide 高级(Markdown/HTML)和底层 API
lopdf 部分 底层对象构建
printpdf 干净的创建 API,不能读取
pdf-rs 只读

“我需要编辑现有 PDF”

Crate 适合? 备注
PDF Oxide DOM 式编辑、注释、表单
lopdf 部分 底层对象操作
printpdf 不能读取 PDF
pdf-rs 只读

“我需要完整生命周期(提取 + 创建 + 编辑)”

Crate 适合? 备注
PDF Oxide 唯一覆盖所有三者的 crate
lopdf + printpdf 部分 两个 crate,无文本提取
pdf-rs + printpdf 部分 两个 crate,无编辑功能

何时使用各库

选择 PDF Oxide: 如果你需要多种 PDF 能力(提取 + 创建,或提取 + 编辑),并且需要一个经过充分测试、100% 可靠的单一依赖。

选择 lopdf: 如果你需要底层 PDF 结构操作,并且愿意直接使用 PDF 规范。适合合并、拆分和批量 PDF 处理。

选择 printpdf: 如果你只创建 PDF 而不需要读取。最干净的报告和文档生成 API。

选择 pdf-rs: 如果你需要一个符合规范的解析器用于 PDF 分析,或者正在构建自己的渲染流水线。

选择 pdf_extract: 如果你需要基础文本提取,且不要求高可靠性或复杂布局支持。

相关页面