PDF Oxide vs lopdf
lopdf — низкоуровневый Rust-крейт для прямой работы с PDF-объектами, без встроенного извлечения текста и рендеринга. PDF Oxide — высокоуровневая библиотека, в которой извлечение, создание и редактирование уже готовы. Эти крейты закрывают принципиально разные задачи.
Ключевые отличия
Уровень абстракции. lopdf даёт доступ к сырым PDF-объектам — словарям, потокам и таблицам перекрёстных ссылок. Нет ни извлечения текста, ни декодирования шрифтов, ни экспорта изображений. PDF Oxide предоставляет готовые методы: extract_text(), extract_images(), to_markdown().
Надёжность. lopdf не может распарсить 20% тестового корпуса из 3 830 PDF. Из тех, что парсятся, 57% дают пустой результат, потому что lopdf не извлекает текст — вы получаете объекты, но не текст. PDF Oxide проходит 100%.
Скорость на парсируемых PDF. lopdf быстрее при сыром парсинге объектов: 0.3 мс против 0.8 мс у PDF Oxide. Но lopdf не выполняет работу по извлечению текста — декодирование шрифтов, разрешение CMap, анализ пробелов и определение порядка чтения вам придётся реализовать самостоятельно.
Краткое сравнение
| PDF Oxide | lopdf | |
|---|---|---|
| Уровень API | Высокоуровневый | Низкоуровневый |
| Извлечение текста | Встроенное (production-уровня) | Нет |
| Успешность (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 — преобразование идентификаторов глифов в символы 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% означает, что он не справляется примерно с каждым пятым 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"
Связанные страницы
- Benchmark производительности — полные результаты benchmark на корпусе
- Сравнение с Rust-библиотеками — все Rust-крейты в сравнении
- Начало работы с Rust — установка и первое извлечение