Продуктивність
PDF Oxide v0.3.11 забезпечує 0.8мс середнє вилучення тексту в Python на корпусі з 3 830 PDF — у 5× швидше за PyMuPDF та у 15× швидше за pypdf, зі 100% рівнем успіху на валідних PDF.
Результати бенчмарків
Корпус: 3 830 PDF
Три незалежні публічні тестові набори, об’єднані разом:
| Набір | Джерело | |
|---|---|---|
| veraPDF | 2 907 | Корпус тестування відповідності PDF/A |
| Mozilla pdf.js | 897 | Тестовий набір рендерингу PDF для браузера |
| SafeDocs | 26 | Корпус некоректних PDF від DARPA SafeDocs |
Чому цей корпус надійний
Це не вручну обрані PDF. Кожен набір — це усталений, рецензований тестовий корпус, що підтримується органами стандартизації або великими проєктами з відкритим кодом:
-
veraPDF — офіційний валідатор відповідності PDF/A, який використовується спільнотою стандартів PDF понад 10 років. Його 2 907 тестових файлів є атомарними — кожен тестує рівно одну характеристику специфікації PDF — охоплюючи всі версії PDF/A (1A/1B, 2A/2B/2U, 3A/3B/3U, 4/4E/4F), доступність PDF/UA (UA1, UA2) та обидва стандарти ISO 32000-1 (PDF 1.7) та ISO 32000-2 (PDF 2.0). Ліцензія CC BY 4.0.
-
Mozilla pdf.js забезпечує рендеринг PDF у Firefox, обробляючи мільярди PDF щорічно. Його 897 тестових файлів охоплюють реальні граничні випадки рендерингу: складні багатосторінкові документи, типи анотацій (стилі рамок, виділення, маркери, вкладені файли), віджети форм, незвичні кодування шрифтів, великі стрес-тестові документи та граничні випадки content stream. 7 файлів навмисно пошкоджені для тестування коректного відхилення помилок.
-
DARPA SafeDocs — програма дослідження безпеки, фінансована урядом США, зосереджена на стійкості парсерів. Її 26 файлів націлені на найскладніші граничні випадки: цикли content stream у шрифтах Type3, подвійні трейлери startxref, компактований синтаксис PDF, варіації діалектів, граничні випадки вбудованих зображень, рекурсивне вкладення шрифтів та зашифровані PDF з паролями Unicode (включаючи UTF-16 LE). Ці файли розроблені для того, щоб аварійно завершити, заблокувати або скомпрометувати вразливі парсери.
Що охоплює цей корпус
- Усі основні версії PDF: PDF 1.0 до PDF 2.0
- Шифрування та паролі: AES-256, RC4, паролі Unicode, кодування UTF-16 LE
- Граничні випадки безпеки: Рекурсивні структури, цикли content stream, некоректні трейлери, пошкодження, згенеровані фазингом
- Різноманітність шрифтів: TrueType, CIDFont, Type1, Type3, кодування CJK, вбудовані підмножини
- Складність документів: Від фікстур з однією сторінкою до документів з понад 10 000 сторінок, вбудовані зображення, вкладені Form XObjects
- Коректне відхилення: 7 навмисно зламаних файлів (відсутні заголовки PDF, невалідні потоки xref) — бібліотека, яка «парсить» їх, є менш безпечною, а не більш надійною
З 3 830 файлів 3 823 є валідними PDF. 7 невалідних файлів — це тестові фікстури для обробки помилок — PDF Oxide коректно відхиляє всі 7.
Порівняння бібліотек Python
Середній час вилучення тексту на PDF на повному корпусі з 3 830 PDF:
| Бібліотека | Середнє | p99 | Рівень успіху | Ліцензія |
|---|---|---|---|---|
| PDF Oxide | 0.8мс | 9мс | 100% | MIT |
| PyMuPDF | 4.6мс | 28мс | 99.3% | AGPL-3.0 |
| pypdfium2 | 4.1мс | 42мс | 99.2% | Apache-2.0 |
| pymupdf4llm | 55.5мс | 280мс | 99.1% | AGPL-3.0 |
| pdftext | 7.3мс | 82мс | 99.0% | GPL-3.0 |
| pdfminer | 16.8мс | 124мс | 98.8% | MIT |
| pdfplumber | 23.2мс | 189мс | 98.8% | MIT |
| markitdown | 108.8мс | 378мс | 98.6% | MIT |
| pypdf | 12.1мс | 97мс | 98.4% | BSD-3 |
PDF Oxide — найшвидша бібліотека PDF, доступна для Python. На відміну від PyMuPDF, використовує ліцензію MIT — жодних обмежень AGPL для комерційного використання.
Порівняння бібліотек Rust
| Бібліотека | Середнє | p99 | Рівень успіху | Вилучення тексту |
|---|---|---|---|---|
| PDF Oxide | 0.8мс | 9мс | 100% | Вбудоване, рівень продакшену |
| oxidize_pdf | 13.5мс | 11мс | 99.1% | Базове |
| unpdf | 2.8мс | 10мс | 95.1% | Базове |
| pdf_extract | 4.08мс | 37мс | 91.5% | Базове |
| lopdf | 0.3мс | 2мс | 80.2% | Без вбудованого вилучення |
lopdf швидший на PDF, які може розпарсити, але не справляється з 20% корпусу. pdf_oxide — єдиний Rust crate, що поєднує 100% надійність із вбудованим вилученням тексту. Зверніть увагу, що lopdf не надає вилучення тексту — вам потрібно самостійно будувати декодування шрифтів та аналіз інтервалів.
Якість тексту
PDF Oxide досягає 99.5% паритету тексту порівняно з PyMuPDF та pypdfium2 на повному корпусі. Якість вимірювалася посимвольним порівнянням вилученого тексту по всіх 3 823 валідних PDF.
Розбивка за корпусами
| Корпус | PDF Oxide середнє | pypdfium2 середнє | PyMuPDF середнє | |
|---|---|---|---|---|
| veraPDF | 2 907 | 0.7мс | 3.6мс | 4.1мс |
| Mozilla pdf.js | 897 | 1.1мс | 5.8мс | 6.2мс |
| SafeDocs | 26 | 0.9мс | 4.0мс | 4.3мс |
Історія оптимізацій: v0.3.5 → v0.3.8
v0.3.8 усунула два критичних вузьких місця O(n), що знизило середнє з 23.3мс до 0.8мс (Python) на тому ж корпусі.
1. Масове кешування дерева сторінок
До: get_page() обходив дерево сторінок від кореня для кожної некешованої сторінки. Для послідовного вилучення всіх сторінок це було O(n) на сторінку та O(n²) загалом.
Після: При першому зверненні до сторінки все дерево сторінок обходиться один раз, і всі сторінки кешуються в HashMap<usize, Object>. Кожне наступне звернення — O(1).
Це виправлення, яке зменшило час обробки PDF з 10 000 сторінок з 55 секунд до 332 мілісекунд.
2. Кеш зміщень scan-for-object
До: Коли об’єкти були відсутні в таблиці xref, scan_for_object() читав весь PDF-файл для кожного відсутнього об’єкта. Теговані PDF з сотнями елементів дерева структури, яких не було в xref, спричиняли сотні повних читань файлу.
Після: Файл сканується один раз, і всі зміщення об’єктів кешуються в HashMap. Подальші пошуки — O(1).
3. Однопрохідне вилучення тексту
До: extract_spans() виконував два проходи по вмісту сторінки — спочатку для класифікації типу документа (академічний, газета, форма тощо), потім для вилучення тексту.
Після: Прохід класифікації повністю усунено. Адаптивні пороги з урахуванням шрифту тепер дають рівні або кращі результати за один прохід.
4. Попереднє виділення пам’яті для content stream
До: parse_content_stream() будував Vec операторів, починаючи зі стандартної ємності, що спричиняло повторні перерозподіли при великих content streams.
Після: Vec попередньо виділяється на основі розміру потоку (data.len() / 20), що оцінює приблизно один оператор на 20 байтів.
Методологія
Всі бенчмарки використовують однакову методологію:
- Кожна бібліотека обробляє всі 3 830 PDF з використанням multiprocessing Python (один PDF на процес)
- Тайм-аут 60 секунд на PDF — будь-який PDF, що перевищує цей час, вважається невдачею
- Вилучений текст зберігається на диск для кожної бібліотеки для порівняння якості
- Час вимірюється від відкриття файлу до фінального вилучення тексту
- Без прогрівних запусків, без кешування між файлами
- Один потік на PDF
Тестовий стенд запускає всі 18 бібліотек (3 Rust, 15 Python) на одній машині, одному корпусі, в однакових умовах.
Відтворення бенчмарків
Публічні тестові корпуси доступні безкоштовно:
- veraPDF: github.com/veraPDF/veraPDF-corpus
- Mozilla pdf.js: github.com/mozilla/pdf.js/tree/master/test/pdfs
- SafeDocs: github.com/pdf-association/safedocs
Запустіть верифікацію:
cargo run --release --example verify_corpus -- \
/path/to/veraPDF-corpus \
/path/to/pdfjs-test \
/path/to/safedocs \
--csv results.csv
Характеристики продуктивності
Де PDF Oxide швидкий
- Вилучення тексту: Головна ціль оптимізації. Суб-мілісекунда для типових документів.
- Послідовне вилучення з багатьох сторінок: Кеш дерева сторінок робить вилучення всіх сторінок великого документа майже таким же швидким, як вилучення однієї.
- Теговані PDF: Обхід дерева структури та роздільна здатність об’єктів тепер кешуються.
- Некоректні PDF: Толерантний парсинг зі стратегіями fallback уникає дорогих повторних спроб.
Що масштабується лінійно
- Кількість сторінок: Кожна сторінка обробляється незалежно. 100 сторінок займає приблизно у 100× більше, ніж одна сторінка.
- Розмір content stream: Парсинг операторів є лінійним відносно довжини потоку.
- Вилучення зображень: Пропорційне кількості та розміру зображень.
Коли очікувати повільніших результатів
- Скановані PDF з OCR: Обробка OCR (якщо увімкнена) значно повільніша за вилучення тексту.
- Рендеринг: Рендеринг сторінок у зображення залежить від складності вмісту та цільового DPI.
- Сильно зашифровані PDF: Дешифрування AES-256 додає накладні витрати на кожен потік.
- PDF з тисячами шрифтів: Парсинг шрифтів кешується на рівні документа, але початковий парсинг масштабується з кількістю шрифтів.
Наступні кроки
- Журнал змін – повна історія версій
- Порівняння з бібліотеками Python – детальне порівняння з PyMuPDF, pypdf, pdfplumber, pdfminer
- Порівняння з бібліотеками Rust – детальне порівняння з lopdf, pdf_extract, pdf-rs
- Початок роботи з Rust – встановлення та перше вилучення
- Довідник API Rust – повна документація API