Leistung
PDF Oxide v0.3.11 liefert 0,8ms durchschnittliche Textextraktion in Python über einen Korpus von 3.830 PDFs — 5× schneller als PyMuPDF und 15× schneller als pypdf, mit 100% Erfolgsrate bei gültigen PDFs.
Benchmark-Ergebnisse
Korpus: 3.830 PDFs
Drei unabhängige öffentliche Testsuiten kombiniert:
| Suite | PDFs | Quelle |
|---|---|---|
| veraPDF | 2.907 | PDF/A-Konformitätstest-Korpus |
| Mozilla pdf.js | 897 | Browser-PDF-Rendering-Testsuite |
| SafeDocs | 26 | DARPA SafeDocs Korpus fehlerhafter PDFs |
Warum dieser Korpus zuverlässig ist
Dies sind keine handverlesenen PDFs. Jede Suite ist ein etablierter, peer-reviewter Testkorpus, der von Standardisierungsgremien oder großen Open-Source-Projekten gepflegt wird:
-
veraPDF ist der offizielle PDF/A-Konformitätsvalidator, der seit über 10 Jahren von der PDF-Standardisierungsgemeinschaft verwendet wird. Seine 2.907 Testdateien sind atomar — jede testet genau ein PDF-Spezifikationsmerkmal — und decken alle PDF/A-Versionen (1A/1B, 2A/2B/2U, 3A/3B/3U, 4/4E/4F), PDF/UA-Barrierefreiheit (UA1, UA2) sowie ISO 32000-1 (PDF 1.7) und ISO 32000-2 (PDF 2.0) ab. Lizenziert unter CC BY 4.0.
-
Mozilla pdf.js betreibt das PDF-Rendering in Firefox und verarbeitet jährlich Milliarden von PDFs. Seine 897 Testdateien decken praxisnahe Rendering-Grenzfälle ab: komplexe mehrseitige Dokumente, Anmerkungentypen (Randstile, Hervorhebungen, Einfügemarken, Dateianhänge), Formular-Widgets, ungewöhnliche Schriftkodierungen, große Stresstest-Dokumente und Content-Stream-Grenzfälle. 7 Dateien sind absichtlich beschädigt, um die korrekte Fehlererkennung zu testen.
-
DARPA SafeDocs ist ein vom US-Staat finanziertes Sicherheitsforschungsprogramm, das sich auf Parser-Robustheit konzentriert. Seine 26 Dateien zielen auf die schwierigsten Grenzfälle ab: Content-Stream-Zyklen in Type3-Schriften, doppelte Startxref-Trailer, kompaktierte PDF-Syntax, Dialektvariationen, Inline-Bild-Grenzfälle, rekursive Schriftverschachtelung und verschlüsselte PDFs mit Unicode-Passwörtern (einschließlich UTF-16 LE). Diese Dateien sind darauf ausgelegt, anfällige Parser zum Absturz zu bringen, zu blockieren oder auszunutzen.
Was dieser Korpus abdeckt
- Alle wichtigen PDF-Versionen: PDF 1.0 bis PDF 2.0
- Verschlüsselung und Passwörter: AES-256, RC4, Unicode-Passwörter, UTF-16-LE-Kodierung
- Sicherheitsgrenzfälle: Rekursive Strukturen, Content-Stream-Zyklen, fehlerhafte Trailer, durch Fuzzing erzeugte Korruption
- Schriftvielfalt: TrueType, CIDFont, Type1, Type3, CJK-Kodierungen, eingebettete Untergruppen
- Dokumentkomplexität: Von einseitigen Fixtures bis zu Dokumenten mit über 10.000 Seiten, Inline-Bilder, verschachtelte Form XObjects
- Korrekte Ablehnung: 7 absichtlich fehlerhafte Dateien (fehlende PDF-Header, ungültige Xref-Streams) — eine Bibliothek, die diese „parst", ist weniger sicher, nicht zuverlässiger
Von den 3.830 Dateien sind 3.823 gültige PDFs. Die 7 ungültigen Dateien sind Test-Fixtures für die Fehlerbehandlung — PDF Oxide lehnt alle 7 korrekt ab.
Vergleich von Python-Bibliotheken
Durchschnittliche Textextraktionszeit pro PDF über den vollständigen Korpus von 3.830 PDFs:
| Bibliothek | Durchschnitt | p99 | Erfolgsrate | Lizenz |
|---|---|---|---|---|
| PDF Oxide | 0,8ms | 9ms | 100% | MIT |
| PyMuPDF | 4,6ms | 28ms | 99,3% | AGPL-3.0 |
| pypdfium2 | 4,1ms | 42ms | 99,2% | Apache-2.0 |
| pymupdf4llm | 55,5ms | 280ms | 99,1% | AGPL-3.0 |
| pdftext | 7,3ms | 82ms | 99,0% | GPL-3.0 |
| pdfminer | 16,8ms | 124ms | 98,8% | MIT |
| pdfplumber | 23,2ms | 189ms | 98,8% | MIT |
| markitdown | 108,8ms | 378ms | 98,6% | MIT |
| pypdf | 12,1ms | 97ms | 98,4% | BSD-3 |
PDF Oxide ist die schnellste verfügbare Python-PDF-Bibliothek. Im Gegensatz zu PyMuPDF verwendet sie die MIT-Lizenz — keine AGPL-Einschränkungen für kommerzielle Nutzung.
Vergleich von Rust-Bibliotheken
| Bibliothek | Durchschnitt | p99 | Erfolgsrate | Textextraktion |
|---|---|---|---|---|
| PDF Oxide | 0,8ms | 9ms | 100% | Integriert, produktionsreif |
| oxidize_pdf | 13,5ms | 11ms | 99,1% | Grundlegend |
| unpdf | 2,8ms | 10ms | 95,1% | Grundlegend |
| pdf_extract | 4,08ms | 37ms | 91,5% | Grundlegend |
| lopdf | 0,3ms | 2ms | 80,2% | Keine integrierte Extraktion |
lopdf ist bei den PDFs, die es parsen kann, schneller, scheitert aber bei 20% des Korpus. pdf_oxide ist das einzige Rust-Crate, das 100% Zuverlässigkeit mit integrierter Textextraktion vereint. Beachten Sie, dass lopdf keine Textextraktion bietet — Sie müssen Schriftdekodierung und Abstandsanalyse selbst implementieren.
Textqualität
PDF Oxide erreicht 99,5% Textparität im Vergleich zu PyMuPDF und pypdfium2 über den gesamten Korpus. Die Qualität wurde durch zeichenweisen Vergleich der extrahierten Textausgabe über alle 3.823 gültigen PDFs gemessen.
Aufschlüsselung nach Korpus
| Korpus | PDFs | PDF Oxide Durchschnitt | pypdfium2 Durchschnitt | PyMuPDF Durchschnitt |
|---|---|---|---|---|
| veraPDF | 2.907 | 0,7ms | 3,6ms | 4,1ms |
| Mozilla pdf.js | 897 | 1,1ms | 5,8ms | 6,2ms |
| SafeDocs | 26 | 0,9ms | 4,0ms | 4,3ms |
Optimierungshistorie: v0.3.5 → v0.3.8
v0.3.8 beseitigte zwei kritische O(n)-Engpässe, die den Durchschnitt von 23,3ms auf 0,8ms (Python) über denselben Korpus senkten.
1. Bulk-Seitenbaum-Cache
Vorher: get_page() durchlief den Seitenbaum von der Wurzel für jede nicht gecachte Seite. Bei sequentieller Extraktion aller Seiten war dies O(n) pro Seite und O(n²) insgesamt.
Nachher: Beim ersten Seitenzugriff wird der gesamte Seitenbaum einmal durchlaufen und alle Seiten werden in einer HashMap<usize, Object> gecacht. Jeder weitere Zugriff ist O(1).
Dies ist die Korrektur, die ein 10.000-seitiges PDF von 55 Sekunden auf 332 Millisekunden brachte.
2. Scan-for-Object Offset-Cache
Vorher: Wenn Objekte in der Xref-Tabelle fehlten, las scan_for_object() die gesamte PDF-Datei für jedes fehlende Objekt. Getaggte PDFs mit Hunderten von Strukturbaum-Elementen, die nicht im Xref waren, lösten Hunderte vollständiger Dateilesungen aus.
Nachher: Die Datei wird einmal gescannt und alle Objekt-Offsets werden in einer HashMap gecacht. Nachfolgende Abfragen sind O(1).
3. Einpass-Textextraktion
Vorher: extract_spans() führte zwei Durchgänge über den Seiteninhalt durch — zuerst zur Klassifizierung des Dokumenttyps (akademisch, Zeitung, Formular usw.), dann zur Textextraktion.
Nachher: Der Klassifizierungsdurchgang wurde vollständig eliminiert. Adaptive schriftbewusste Schwellenwerte liefern nun gleiche oder bessere Ergebnisse in einem einzigen Durchgang.
4. Content-Stream-Vorabzuweisung
Vorher: parse_content_stream() baute den Operator-Vec mit Standardkapazität auf, was bei großen Content Streams wiederholte Neuzuweisungen verursachte.
Nachher: Der Vec wird basierend auf der Stream-Größe (data.len() / 20) vorab zugewiesen, was etwa einen Operator pro 20 Bytes schätzt.
Methodik
Alle Benchmarks verwenden dieselbe Methodik:
- Jede Bibliothek verarbeitet alle 3.830 PDFs mit Python-Multiprocessing (ein PDF pro Prozess)
- 60-Sekunden-Timeout pro PDF — jedes PDF, das dies überschreitet, wird als Fehler gezählt
- Extrahierter Text wird pro Bibliothek auf der Festplatte für Qualitätsvergleiche gespeichert
- Die Wanduhrzeit wird vom Öffnen der Datei bis zur endgültigen Textextraktion gemessen
- Keine Aufwärmdurchläufe, kein Caching zwischen Dateien
- Ein Thread pro PDF
Der Benchmark-Harness führt alle 18 Bibliotheken (3 Rust, 15 Python) auf derselben Maschine, demselben Korpus und unter denselben Bedingungen aus.
Benchmarks reproduzieren
Die öffentlichen Testkorpora sind frei verfügbar:
- veraPDF: github.com/veraPDF/veraPDF-corpus
- Mozilla pdf.js: github.com/mozilla/pdf.js/tree/master/test/pdfs
- SafeDocs: github.com/pdf-association/safedocs
Führen Sie die Verifizierung aus:
cargo run --release --example verify_corpus -- \
/path/to/veraPDF-corpus \
/path/to/pdfjs-test \
/path/to/safedocs \
--csv results.csv
Leistungsmerkmale
Wobei PDF Oxide schnell ist
- Textextraktion: Das primäre Optimierungsziel. Sub-Millisekunde für typische Dokumente.
- Sequentielle Mehrseitenextraktion: Der Seitenbaum-Cache macht die Extraktion aller Seiten eines großen Dokuments nahezu so schnell wie die Extraktion einer einzigen.
- Getaggte PDFs: Strukturbaum-Traversierung und Objektauflösung sind jetzt gecacht.
- Fehlerhafte PDFs: Tolerantes Parsing mit Fallback-Strategien vermeidet teure Wiederholungsversuche.
Was linear skaliert
- Seitenzahl: Jede Seite wird unabhängig verarbeitet. 100 Seiten dauern ungefähr 100× so lang wie eine Seite.
- Content-Stream-Größe: Das Parsen von Operatoren ist linear zur Stream-Länge.
- Bildextraktion: Proportional zur Anzahl und Größe der Bilder.
Wann langsamere Ergebnisse zu erwarten sind
- Gescannte PDFs mit OCR: OCR-Verarbeitung (falls aktiviert) ist erheblich langsamer als Textextraktion.
- Rendering: Seitenrendering zu Bildern hängt von der Inhaltskomplexität und dem Ziel-DPI ab.
- Stark verschlüsselte PDFs: AES-256-Entschlüsselung verursacht Overhead pro Stream.
- PDFs mit Tausenden von Schriften: Schrift-Parsing wird pro Dokument gecacht, aber das initiale Parsing skaliert mit der Schriftanzahl.
Nächste Schritte
- Changelog – vollständige Versionshistorie
- Vergleich mit Python-Bibliotheken – detaillierter Vergleich mit PyMuPDF, pypdf, pdfplumber, pdfminer
- Vergleich mit Rust-Bibliotheken – detaillierter Vergleich mit lopdf, pdf_extract, pdf-rs
- Erste Schritte mit Rust – Installation und erste Extraktion
- Rust-API-Referenz – vollständige API-Dokumentation