平均0.8ms、成功率100%。Rust製PDFライブラリ「pdf_oxide」

平均0.8ms、成功率100%。Rust製PDFライブラリ「pdf_oxide」 プログラミング

PythonでPDFを扱うとき、ライブラリ選びに悩んだ経験はないでしょうか。

速度を求めればPyMuPDFに行き着くものの、AGPLライセンスが商用利用の壁になる。
MITライセンスのpypdfを選ぶと、速度面で妥協が必要になってしまう。
このジレンマは長年、多くのPython開発者を悩ませてきました。

そんな中、Redditの r/Python で注目を集めているのが「pdf_oxide」です。
Rustで書かれたPDFエンジンにPythonバインディングを提供するこのライブラリは、速度とライセンスの両立という難題に正面から挑んでいます。

本記事では、Redditでの投稿やコミュニティの反応をもとに、pdf_oxideの特徴と可能性を探ります。

pdf_oxideとは何か

pdf_oxideは、テキスト抽出、Markdown変換、PDF生成、OCRなどの機能を備えたPDFライブラリです。

コアエンジンはRustで実装されており、PythonバインディングはPyO3を通じて提供されています。
ライセンスはMIT / Apache-2.0のデュアルライセンスで、商用プロジェクトでも自由に使えます。

インストールと基本的な使い方は極めてシンプルです。

pip install pdf_oxide
from pdf_oxide import PdfDocument

doc = PdfDocument("paper.pdf")
text = doc.extract_text(0)

たった数行でPDFからテキストを抽出できます。
この手軽さは、既存ライブラリと遜色ありません。

なぜ作られたのか

開発者がRedditに投稿した動機は明快でした。
高速なテキスト抽出をパーミッシブライセンスで実現したかったのです。

PyMuPDFは高速だけれど、AGPLライセンスのため多くの商用プロジェクトで採用が難しい。
pypdfはMITライセンスだが、速度が15倍ほど遅く、一部のファイルで処理に失敗してしまう。
pdfplumberはテーブル抽出に強いものの、バッチ処理のスピードには向かない。

こうした課題を解決するため、開発者はPDF仕様書を約1,000ページにわたって読み込み、ゼロからライブラリを構築したとのことです。

パフォーマンスの追求過程

開発の過程で語られたパフォーマンス改善のエピソードは、エンジニアとして非常に興味深い内容でした。

最初のバージョンでは、1ファイルあたり23msかかっていたそうです。
プロファイリングを行ったところ、ページツリーの走査にO(n²)のアルゴリズムが使われていることが判明しました。
10,000ページのPDFで55秒もかかっていた処理です。

この部分をHashMapによるキャッシュに置き換えたところ、処理時間は332msまで短縮されました。
その後もプロファイリングと修正を繰り返し、最終的に3,830のPDFファイルに対して平均0.8msという数値を達成しています。

勘ではなくプロファイリングに基づいてボトルネックを特定し、地道に改善を重ねる。
この姿勢は、パフォーマンスチューニングの教科書のようなアプローチと言えるでしょう。

ベンチマーク結果

Redditの投稿では、veraPDF、Mozilla pdf.js、DARPA SafeDocsという3つの公開テストスイートから構成される3,830のPDFを用いたベンチマーク結果が示されていました。

ライブラリ平均p99成功率ライセンス
pdf_oxide0.8ms9ms100%MIT
PyMuPDF4.6ms28ms99.3%AGPL-3.0
pypdfium24.1ms42ms99.2%Apache-2.0
pdftext7.3ms82ms99.0%GPL-3.0
pypdf12.1ms97ms98.4%BSD-3
pdfminer16.8ms124ms98.8%MIT
pdfplumber23.2ms189ms98.8%MIT
markitdown108.8ms378ms98.6%MIT

コミュニティからの反応

Redditのスレッドでは、様々な観点からフィードバックが寄せられていました。
いくつか興味深い反応を紹介します。

テーブル認識の課題

あるユーザーは、テーブルの認識精度が十分ではないと指摘しました。
複数のPDFで試したところ、テーブル解析においてはDoclingだけがまともな結果を返すものの、Doclingはメモリ消費とパフォーマンスに問題があるとのことでした。

開発者はこのフィードバックに対して、テストケースとなるPDFの共有を求め、テーブル認識の改善に取り組む意向を即座に示しています。
この対応の速さは好印象です。

Doclingバックエンドとしての可能性

pdf_oxideをDoclingのPDFバックエンドとして利用できないかという提案もありました。

開発者の回答によると、Doclingのレイアウトモデルが必要とする単語・行レベルのバウンディングボックスやベクターパスの抽出は既に対応済みとのこと。
DoclingのBaseBackendラッパーを実装すれば連携可能だと説明されていました。

既存のパイプラインにドロップイン置換できれば、RAGやLLMパイプラインの処理速度を大幅に改善できる可能性があります。

ページの画像変換

PDFのページを画像として保存できるかという質問に対して、render_page()メソッドの存在が紹介されていました。

image_bytes = doc.render_page(0, dpi=300)

with open("page0.png", "wb") as f:
    f.write(image_bytes)

PNGのバイトデータを直接取得できるため、PyMuPDFからの移行も比較的スムーズに進みそうです。

ヘッダー・フッター対応

ヘッダーやフッターの追加・編集機能を求めるフィードバックに対して、開発者はわずか数日後にヘッダー・フッター対応版をリリースしました。
ユーザーの声を取り込むスピード感は、このプロジェクトの大きな強みでしょう。

PyO3によるRust-Python連携

RustコアとPythonバインディングの連携に関する技術的な質問も上がっていました。
エラーの伝播処理などでPyO3にはある程度の手間がかかるものの、200〜300倍のパフォーマンス向上が得られるなら十分に見合うと開発者は答えています。

ライセンスに関する議論

MITライセンスの選択について、あるユーザーは「企業にコードを無料で使われるだけでは」と懸念を示していました。
AGPLを選ぶべきだったのではないかという意見です。

ライセンスの選択は開発者の哲学に関わる問題です。
pdf_oxideの開発者は、商用利用を制限しないことを意図的に選んでいます。

商用プロジェクトでの採用障壁を下げたいという当初の動機と一貫した判断と言えるでしょう。

PDFテキスト抽出の根本的な難しさ

コメント欄では、PDFからのテキスト抽出がそもそも困難であるという本質的な課題にも触れられていました。

PDFは本来、画面上の特定位置に文字を配置するフォーマットです。
単語や段落といったメタデータは持っていません。
あるユーザーが述べていたように、PDFから抽出したテキストの「正解」の表現は一つではないのです。

pypdfのドキュメントでも、テキストの最適な表現方法は存在しないと説明されているそうです。
この制約を理解した上で、用途に応じた前処理や後処理を組み合わせるのが現実的なアプローチになるでしょう。

まとめ

pdf_oxideは、PythonにおけるPDF処理の新たな選択肢として注目に値するライブラリです。

Rustエンジンによる高速処理、MIT/Apache-2.0のパーミッシブライセンス、100%の成功率というベンチマーク結果。
これらの特性は、特にバッチ処理や商用プロジェクトにおいて大きなアドバンテージとなり得ます。

一方で、テーブル認識の精度には改善の余地がありますし、比較的新しいプロジェクトであることも考慮すべき点です。
ただ、開発者のフィードバック対応の速さやコミュニティとの積極的なやり取りを見ると、今後の発展にも期待が持てます。

PDFライブラリの選定で「速度かライセンスか」という二者択一を迫られてきた方にとって、pdf_oxideは第三の選択肢になるかもしれません。

タイトルとURLをコピーしました