PDF Oxide проти lopdf
lopdf — це низькорівневий Rust-крейт для прямої роботи з об’єктами PDF. PDF Oxide — високорівнева бібліотека з вбудованим вилученням тексту, створенням і редагуванням. Вони розраховані на принципово різні сценарії використання.
Ключові відмінності
Рівень абстракції. lopdf дає вам сирі об’єкти PDF — словники, потоки й таблиці перехресних посилань. Тут немає ні вилучення тексту, ні декодування шрифтів, ні експорту зображень. PDF Oxide надає спеціалізовані методи: extract_text(), extract_images(), to_markdown().
Надійність. lopdf не може розібрати 20% тестового корпусу з 3830 PDF. Із тих PDF, які йому вдається розібрати, 57% дають порожній результат, бо в lopdf немає вилучення тексту — ви отримуєте об’єкти, але не текст. PDF Oxide успішно обробляє 100%.
Швидкість на придатних до розбору PDF. На сирому розборі об’єктів lopdf швидший: у середньому 0,3 мс проти 0,8 мс у PDF Oxide. Але lopdf не виконує жодної роботи з вилучення тексту — вам довелося б самостійно реалізувати декодування шрифтів, розв’язання CMap, аналіз інтервалів і визначення порядку читання.
Стисле порівняння
| PDF Oxide | lopdf | |
|---|---|---|
| Рівень API | Високорівневий | Низькорівневий |
| Вилучення тексту | Вбудоване (промислового рівня) | Немає |
| Частка успіху (3830 PDF) | 100% | 80,2% |
| Середній час розбору | 0,8 мс | 0,3 мс |
| Вилучення зображень | Вбудоване | Вручну (сирі потоки) |
| Поля форм | Читання + запис | Вручну (сирі словники) |
| Створення PDF | Так (Markdown/HTML) | Так (сирі об’єкти) |
| Виведення у Markdown/HTML | Так | Ні |
| Шифрування | Читання + запис | Ні |
| Рендеринг | Так | Ні |
| Валідація PDF/A | Так | Ні |
| Ліцензія | MIT | MIT |
Чого lopdf не вміє
lopdf надає доступ до об’єктів PDF, але вилучення тексту вимагає інтерпретувати ці об’єкти відповідно до специфікації PDF. Ось що вам довелося б реалізувати самостійно:
- Розбір потоку вмісту — парсинг PostScript-подібних операторів (Tj, TJ, Tm, Tf тощо)
- Розв’язання шрифтів — пошук ресурсів /Font, розв’язання непрямих посилань
- Декодування CMap/ToUnicode — перетворення ідентифікаторів гліфів на символи Unicode
- Інтервали за метриками шрифту — обчислення ширини символів із дескрипторів шрифту
- Перетворення текстової матриці — застосування операторів Tm, Td, T* для позиціонування тексту
- Порядок читання — визначення правильного порядку для багатоколонкових макетів
- Відновлення лігатур — обробка лігатур fi, fl, ffi
- Кодування CJK — декодування китайських, японських і корейських текстових кодувань
Це тисячі рядків коду та глибоке знання ISO 32000. PDF Oxide робить усе це всередині себе.
Порівняння коду пліч-о-пліч
Вилучення тексту
PDF Oxide:
use pdf_oxide::PdfDocument;
let mut doc = PdfDocument::open("report.pdf")?;
let text = doc.extract_text(0)?;
println!("{}", text);
lopdf:
use lopdf::Document;
let doc = Document::load("report.pdf")?;
// lopdf does not provide text extraction.
// You get access to PDF objects only:
let page_id = doc.page_iter().next().unwrap();
let page = doc.get_dictionary(page_id)?;
let contents = page.get("Contents")?;
let stream = doc.get_object(contents.as_reference()?)?;
// To get actual text, you must:
// 1. Parse content stream operators
// 2. Resolve font references from /Resources
// 3. Decode CMap/ToUnicode mappings
// 4. Apply text matrix transformations
// 5. Handle encoding differences
// ... (hundreds to thousands of lines of code)
Створення PDF
PDF Oxide:
use pdf_oxide::api::Pdf;
let pdf = Pdf::from_markdown("# Report\n\n| Q1 | Q2 |\n|---|---|\n| $1M | $2M |")?;
pdf.save("report.pdf")?;
lopdf:
use lopdf::{Document, Object, Stream, dictionary};
let mut doc = Document::with_version("1.5");
// Create font dictionary
let font_id = doc.add_object(dictionary! {
"Type" => "Font",
"Subtype" => "Type1",
"BaseFont" => "Helvetica",
});
// Create resources
let resources_id = doc.add_object(dictionary! {
"Font" => dictionary! { "F1" => font_id },
});
// Create content stream (raw PostScript operators)
let content = Stream::new(
dictionary! {},
b"BT /F1 12 Tf 72 720 Td (Hello World) Tj ET".to_vec(),
);
let content_id = doc.add_object(content);
// Create page
let page_id = doc.add_object(dictionary! {
"Type" => "Page",
"MediaBox" => vec![0.into(), 0.into(), 612.into(), 792.into()],
"Contents" => content_id,
"Resources" => resources_id,
});
// Wire up page tree
let pages_id = doc.add_object(dictionary! {
"Type" => "Pages",
"Kids" => vec![page_id.into()],
"Count" => 1,
});
doc.add_object(dictionary! {
"Type" => "Catalog",
"Pages" => pages_id,
});
doc.save("report.pdf")?;
Зашифровані PDF
PDF Oxide:
use pdf_oxide::PdfDocument;
let doc = PdfDocument::open_with_password("encrypted.pdf", "secret")?;
let text = doc.extract_text(0)?;
println!("{}", text);
lopdf:
// lopdf does not support encrypted PDFs.
// Loading an encrypted PDF will fail or produce undecrypted streams.
Порівняння надійності
| Метрика | PDF Oxide | lopdf |
|---|---|---|
| Успішно розібрані PDF | 3823 / 3823 (100%) | 3071 / 3823 (80,2%) |
| PDF із текстовим виведенням | 3823 / 3823 | ~1320 / 3823 (оцінково) |
| Підтримка зашифрованих PDF | Так | Ні |
| Відновлення пошкоджених PDF | Так | Ні |
Частка успіху 80,2% у lopdf означає, що він не справляється приблизно з 1 PDF із 5. Збої трапляються на зашифрованих документах, PDF із нестандартними таблицями xref і документах, що використовують потоки перехресних посилань. PDF Oxide справляється з усім цим завдяки нестрогому розбору й резервним стратегіям.
Коли що обирати
Обирайте PDF Oxide, якщо:
- Вам потрібне вилучення тексту, вилучення зображень або будь-яка операція на рівні вмісту
- Вам потрібен один крейт для читання + запису + створення
- Вам потрібно надійно обробляти будь-які PDF (зашифровані, пошкоджені, складні)
- Вам потрібне виведення у Markdown/HTML, рендеринг або OCR
- Вам потрібна валідація на відповідність (PDF/A, PDF/X, PDF/UA)
Обирайте lopdf, якщо:
- Вам потрібен прямий доступ до об’єктів PDF для власної обробки
- Ви створюєте спеціалізований PDF-інструмент, що працює на рівні об’єктів
- Вам потрібно об’єднувати документи, безпосередньо маніпулюючи деревами об’єктів
- Ваші PDF прості й коректні (без шифрування, зі стандартними таблицями xref)
Поєднуйте обидва:
Використовуйте PDF Oxide для високорівневих операцій, а lopdf — для граничних випадків, що потребують доступу до сирих об’єктів:
[dependencies]
pdf_oxide = "0.3"
lopdf = "0.32"
Пов’язані сторінки
- Тести продуктивності — повні результати по корпусу
- Проти PDF-бібліотек на Rust — порівняння всіх Rust-крейтів
- Початок роботи з Rust — встановлення та перше вилучення