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は依存関係が多めですが、これはフォントのパース、画像のデコード、コンテンツストリームの解釈、暗号化といった、他のライブラリがユーザーに委ねるか完全に省いている機能を含んでいるためです。すべてのオプション機能(rendering、barcodes、office)を有効にすると、その数は約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を選ぶべき場合:基本的なテキスト抽出が必要で、高い信頼性や複雑なレイアウトのサポートを必要としない場合。
関連ページ
- パフォーマンスベンチマーク – 全コーパスベンチマークの結果
- Rustで始める – インストールと最初の抽出
- Rust APIリファレンス – 完全なRust API
- Python PDFライブラリとの比較 – Pythonエコシステムの比較