PDF Oxide vs lopdf
lopdf — низькорівневий Rust crate для прямої роботи з об’єктами PDF, без вбудованого видобування тексту чи рендерингу. PDF Oxide — високорівнева бібліотека, де видобування, створення й редагування вже готові з коробки. Ці crate-и закривають принципово різні задачі.
Ключові відмінності
Рівень абстракції. lopdf надає вам необроблені об’єкти PDF — словники, потоки та таблиці перехресних посилань. Немає вилучення тексту, немає декодування шрифтів, немає експорту зображень. PDF Oxide надає спеціалізовані методи: extract_text(), extract_images(), to_markdown().
Надійність. lopdf не може розпарсити 20% тестового корпусу з 3 830 PDF. З тих PDF, які він може розпарсити, 57% дають порожній вивід, оскільки lopdf не має вилучення тексту — ви отримуєте об’єкти, але не текст. PDF Oxide проходить 100%.
Швидкість на парсованих PDF. lopdf швидший при розборі необроблених об’єктів: 0.3мс середнє проти 0.8мс у PDF Oxide. Але lopdf не виконує жодної роботи з вилучення тексту — вам потрібно було б самостійно побудувати декодування шрифтів, розв’язання CMap, аналіз інтервалів та порядок читання.
Швидке порівняння
| PDF Oxide | lopdf | |
|---|---|---|
| Рівень API | Високий рівень | Низький рівень |
| Вилучення тексту | Вбудоване (виробничого рівня) | Немає |
| Рівень успіху (3 830 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 — конвертувати ID гліфів у символи 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 не надає вилучення тексту.
// Ви отримуєте лише доступ до об'єктів PDF:
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()?)?;
// Щоб отримати справжній текст, вам потрібно:
// 1. Розпарсити оператори потоку вмісту
// 2. Розв'язати посилання на шрифти з /Resources
// 3. Декодувати відображення CMap/ToUnicode
// 4. Застосувати перетворення текстової матриці
// 5. Обробити різниці кодувань
// ... (сотні чи тисячі рядків коду)
Створення 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");
// Створити словник шрифту
let font_id = doc.add_object(dictionary! {
"Type" => "Font",
"Subtype" => "Type1",
"BaseFont" => "Helvetica",
});
// Створити ресурси
let resources_id = doc.add_object(dictionary! {
"Font" => dictionary! { "F1" => font_id },
});
// Створити потік вмісту (необроблені оператори PostScript)
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);
// Створити сторінку
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,
});
// Зв'язати дерево сторінок
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 не підтримує зашифровані PDF.
// Завантаження зашифрованого PDF завершиться збоєм або видасть незашифровані потоки.
Порівняння надійності
| Метрика | PDF Oxide | lopdf |
|---|---|---|
| Успішно розпарсованих PDF | 3 823 / 3 823 (100%) | 3 071 / 3 823 (80.2%) |
| PDF з текстовим виводом | 3 823 / 3 823 | ~1 320 / 3 823 (оцінка) |
| Підтримка зашифрованих PDF | Так | Ні |
| Відновлення некоректних PDF | Так | Ні |
Рівень успіху lopdf 80.2% означає, що він збоїть приблизно на 1 з 5 PDF. Збої відбуваються на зашифрованих документах, 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 — встановлення та перше вилучення