Skip to content

Rust PDFライブラリとの比較

PDF Oxideを、最もよく使われるRust PDFクレートであるlopdf、printpdf、pdf-rs、pdf_extractと比較します。それぞれが異なる抽象度のレベルと異なるユースケースを対象としています。

まとめ

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からなる全コーパスでテストしました。これは3つの独立した公開テストスイートで構成され、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% 組み込み抽出なし PDFの20%で失敗

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クレートです。

API設計の比較

PDF Oxide:高レベルでタスク指向

PDF Oxideは一般的なタスク向けに専用に設計されたメソッドを提供します。PDFオブジェクトや辞書ではなく、テキスト、画像、フォームフィールドを扱います。

use pdf_oxide::PdfDocument;

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

// Text extraction -- one call
let text = doc.extract_text(0)?;
println!("{}", text);

// Styled spans with font metadata
let spans = doc.extract_spans(0)?;
for span in &spans {
    println!("'{}' font={} size={:.1}pt", span.text, span.font_name, span.font_size);
}

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

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

PDFの生成も同じくらい簡単です。

use pdf_oxide::api::Pdf;

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

// From 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")?;

// Get page dictionary
let page_id = doc.page_iter().next().unwrap();
let page = doc.get_dictionary(page_id)?;

// Get content stream -- manual work
let contents = page.get("Contents")?;
let stream = doc.get_object(contents.as_reference()?)?;

// To extract text you must:
// 1. Parse the content stream operators
// 2. Resolve font references from /Resources
// 3. Decode CMap/ToUnicode mappings
// 4. Apply text matrix transformations
// 5. Handle encoding differences
//
// lopdf does not provide any of this -- it is raw object access
println!("Page has {} objects", doc.objects.len());

lopdfは、ドキュメントのマージ、オブジェクトストリームの書き換え、専用のPDFプロセッサの構築など、PDF構造を直接操作する必要がある場合に適したツールです。

printpdf:PDF生成のみ

printpdfは生成専用のライブラリです。既存のPDFを読み込んだりパースしたりすることはできません。テキスト、画像、ベクターグラフィックスを使ってPDFドキュメントをゼロから構築するための型付きAPIを提供します。

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);

// Add text -- requires manual font loading
let font = doc.add_builtin_font(BuiltinFont::Helvetica)?;
current_layer.use_text("Hello World", 24.0, Mm(10.0), Mm(280.0), &font);

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

// Cannot read existing PDFs
// Cannot extract text, images, or form fields

printpdfは、新しいPDFを生成するだけで、クリーンで焦点を絞った生成APIが欲しい場合に適したツールです。

pdf-rs:低レベルのPDF読み込み

pdf-rsはPDF構造をRustの型にパースしますが、高レベルの機能は最小限です。PDFオブジェクトへの型付きアクセスは得られますが、テキストのデコード、フォントの解決、コンテンツストリームのパースは依然として自分で行う必要があります。

use pdf::file::FileOptions;

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

// Access page objects
let page = file.get_page(0)?;
let media_box = page.media_box()?;
println!("Page size: {:?}", media_box);

// Content stream access -- low-level
if let Some(ref contents) = page.contents {
    // Returns raw operations -- you must interpret them
    // No built-in text assembly, font decoding, or layout analysis
}

// Cannot write or modify PDFs

pdf-rsは、解析、検証、カスタムレンダラーの構築のために型安全なPDFパーサーが必要な場合に適したツールです。

タスク別の機能比較

テキスト抽出

ライブラリ 組み込み 品質 必要な労力
PDF Oxide はい 本番品質(Unicode、CJK、読み順) メソッド呼び出し1回
pdf_extract はい 基本的(複雑なレイアウトを取りこぼす) メソッド呼び出し1回
lopdf いいえ 該当なし 数百行のカスタムコード
printpdf いいえ 該当なし 不可能(書き込み専用)
pdf-rs いいえ 該当なし 多大なカスタムコードが必要

PDF Oxideは、CMap/ToUnicodeのデコード、フォントメトリックに基づく間隔調整、構造ツリーによる読み順、合字の再構成を処理します。lopdfやpdf-rsの上に同等の機能を実装するには、数千行のコードと深いPDF仕様の知識が必要です。

PDF生成

ライブラリ アプローチ Markdown/HTML入力 テーブル バーコード
PDF Oxide 高レベル+低レベル はい はい はい
lopdf 生のオブジェクト構築 いいえ いいえ いいえ
printpdf 型付きレイヤーAPI いいえ いいえ いいえ
pdf-rs 該当なし(読み込み専用) 該当なし 該当なし 該当なし

暗号化

ライブラリ 暗号化PDFの読み込み 暗号化PDFの書き込み アルゴリズム
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(コア) 約30秒 約4 MB
lopdf 約15 約10秒 約1 MB
printpdf 約20 約15秒 約2 MB
pdf-rs 約25 約20秒 約2 MB

PDF Oxideは依存関係が多めですが、これはフォントのパース、画像のデコード、コンテンツストリームの解釈、暗号化といった、他のライブラリがユーザーに委ねるか完全に省いている機能を含んでいるためです。すべてのオプション機能(renderingbarcodesoffice)を有効にすると、その数は約100に増えます。

ライブラリの組み合わせ

すべて寛容なライセンスで提供されているため、1つのプロジェクトで組み合わせて使用できます。

[dependencies]
pdf_oxide = "0.3"
lopdf = "0.32"        # Optional: raw object access for edge cases

一般的なパターン:

  • PDF Oxide + lopdf:抽出と生成にPDF Oxideを使い、生のオブジェクト操作が必要なエッジケースではlopdfにフォールバックします。
  • PDF Oxide + printpdf:読み込みにPDF Oxideを使い、専用の生成ワークフローにprintpdfを使います。

ユースケースマトリクス

「PDFからテキストを抽出したい」

クレート 適している? 備考
PDF Oxide はい 最高の抽出品質、100%のパス率、読み順、フォントメタデータ
pdf_extract 部分的 基本的な抽出、91.5%のパス率
lopdf いいえ テキスト抽出なし
printpdf いいえ PDFを読み込めない
pdf-rs 部分的 基本的なパース、高レベルのテキスト抽出なし

「PDFを生成したい」

クレート 適している? 備考
PDF Oxide はい 高レベル(Markdown/HTML)と低レベルのAPI
lopdf 部分的 低レベルのオブジェクト構築
printpdf はい クリーンな生成API、読み込みなし
pdf-rs いいえ 読み込み専用

「既存のPDFを編集したい」

クレート 適している? 備考
PDF Oxide はい DOMライクな編集、注釈、フォーム
lopdf 部分的 低レベルのオブジェクト操作
printpdf いいえ PDFを読み込めない
pdf-rs いいえ 読み込み専用

「ライフサイクル全体が必要(抽出+生成+編集)」

クレート 適している? 備考
PDF Oxide はい この3つすべてをカバーする唯一のクレート
lopdf + printpdf 部分的 2つのクレート、テキスト抽出なし
pdf-rs + printpdf 部分的 2つのクレート、編集なし

それぞれの使いどころ

PDF Oxideを選ぶべき場合:複数のPDF機能(抽出+生成、または抽出+編集)が必要で、100%の信頼性を備えた、十分にテストされた単一の依存関係が欲しい場合。

lopdfを選ぶべき場合:低レベルのPDF構造操作が必要で、PDF仕様を直接扱うことに抵抗がない場合。マージ、分割、PDFのバッチ処理に適しています。

printpdfを選ぶべき場合:PDFの生成のみを行い、読み込む必要が一切ない場合。レポートやドキュメント生成のための最もクリーンなAPIです。

pdf-rsを選ぶべき場合:PDF解析のための仕様準拠のパーサーが必要な場合、または独自のレンダリングパイプラインを構築している場合。

pdf_extractを選ぶべき場合:基本的なテキスト抽出が必要で、高い信頼性や複雑なレイアウトのサポートを必要としない場合。

関連ページ