Лучший Rust-крейт для PDF в 2026
PDF Oxide против самых популярных Rust-крейтов для PDF: lopdf, printpdf, pdf-rs и pdf_extract. Каждый крейт работает на своём уровне абстракции и закрывает разные задачи — эта страница поможет выбрать правильный для вашего проекта.
Обзор
| PDF Oxide | lopdf | printpdf | pdf-rs | pdf_extract | |
|---|---|---|---|---|---|
| Уровень API | Высокоуровневый | Низкоуровневый | Средний (создание) | Низкоуровневый (чтение) | Средний (чтение) |
| Чтение PDF | Да | Да | Нет | Да | Да |
| Запись PDF | Да | Да | Да | Нет | Нет |
| Извлечение текста | Да (высокоуровневое) | Вручную | Нет | Вручную | Да (базовое) |
| Извлечение изображений | Да (высокоуровневое) | Вручную | Нет | Вручную | Нет |
| Поля форм | Чтение + запись | Вручную | Нет | Только чтение | Нет |
| Создание PDF | Да | Да | Да | Нет | Нет |
| Ввод Markdown/HTML | Да | Нет | Нет | Нет | Нет |
| Редактирование существующих PDF | Да | Да (низкоуровневое) | Нет | Нет | Нет |
| Аннотации | Чтение + запись | Вручную | Нет | Только чтение | Нет |
| Шифрование | Чтение + запись | Нет | Нет | Нет | Нет |
| Валидация PDF/A | Да | Нет | Нет | Нет | Нет |
| Рендеринг | Да (tiny-skia) | Нет | Нет | Частично | Нет |
| Python-привязки | Да | Нет | Нет | Нет | Нет |
| Лицензия | MIT | MIT | MIT | MIT | Apache-2.0 |
Все библиотеки имеют разрешительные лицензии. Различия — в охвате функциональности и уровне абстракции.
Сравнение производительности
Полный бенчмарк на корпусе (3,830 PDF)
Тестирование проводилось на полном корпусе из 3 830 PDF — три независимых, публично доступных набора тестов, покрывающих соответствие спецификации PDF (veraPDF, 2 907 файлов), реальные граничные случаи рендеринга в браузерах (Mozilla pdf.js, 897 файлов) и стресс-тесты на безопасность/устойчивость, включая повреждённые структуры и сгенерированные фаззингом файлы (DARPA SafeDocs, 26 файлов). См. подробное описание корпуса.
| Библиотека | Среднее | p99 | Успешность | Извлечение текста | Примечания |
|---|---|---|---|---|---|
| PDF Oxide | 0.8ms | 9ms | 100% | Встроенное, production-уровня | Unicode, CJK, порядок чтения |
| oxidize_pdf | 13.5ms | 11ms | 99.1% | Базовое | Выброс 48 с максимум |
| unpdf | 2.8ms | 10ms | 95.1% | Базовое | 185 ошибок на полном корпусе |
| pdf_extract | 4.08ms | 37ms | 91.5% | Базовое | Пропускает сложные макеты |
| lopdf | 0.3ms | 2ms | 80.2% | Нет встроенного извлечения | Не справляется с 20% PDF |
lopdf быстрее на тех PDF, которые может распарсить, — но он не справляется с 20% корпуса и не предоставляет извлечения текста. Вам придётся самостоятельно реализовать декодирование шрифтов, разрешение CMap и анализ пробелов.
pdf_extract предоставляет базовое извлечение текста, но имеет успешность 91.5% и плохо справляется со сложными макетами, текстом CJK и тегированными PDF. oxidize_pdf показывает приемлемую надёжность (99.1%), но работает в 17 раз медленнее pdf_oxide по среднему времени извлечения и имеет выброс в 48 секунд в худшем случае. unpdf обрабатывает весь корпус, но не справляется со 185 PDF.
PDF Oxide — единственный Rust-крейт, сочетающий 100% надёжность с извлечением текста production-уровня.
Сравнение дизайна API
PDF Oxide: высокоуровневый, ориентированный на задачи
PDF Oxide предоставляет готовые методы для типичных задач. Вы работаете с текстом, изображениями и полями форм, а не с PDF-объектами и словарями.
use pdf_oxide::PdfDocument;
let mut doc = PdfDocument::open("report.pdf")?;
// Извлечение текста — один вызов
let text = doc.extract_text(0)?;
println!("{}", text);
// Стилизованные спаны с метаданными шрифтов
let spans = doc.extract_spans(0)?;
for span in &spans {
println!("'{}' font={} size={:.1}pt", span.text, span.font_name, span.font_size);
}
// Извлечение изображений
let images = doc.extract_images(0)?;
for img in &images {
println!("{}x{} {:?}", img.width, img.height, img.format);
}
// Поля форм
let fields = doc.extract_form_fields()?;
for field in &fields {
println!("{}: {:?}", field.name, field.value);
}
Создание PDF столь же просто:
use pdf_oxide::api::Pdf;
// Из Markdown
let pdf = Pdf::from_markdown("# Report\n\n| A | B |\n|---|---|\n| 1 | 2 |")?;
pdf.save("report.pdf")?;
// Из HTML
let pdf = Pdf::from_html("<h1>Report</h1><p>Content here.</p>")?;
pdf.save("report.pdf")?;
lopdf: низкоуровневые манипуляции с объектами
lopdf даёт прямой доступ к PDF-объектам, потокам и таблице перекрёстных ссылок. Для эффективного использования необходимо разбираться в спецификации PDF. Встроенного извлечения текста нет — вы самостоятельно навигируете по словарям и декодируете потоки.
use lopdf::Document;
let doc = Document::load("report.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. Обработать различия кодировок
//
// lopdf ничего из этого не предоставляет — это доступ к сырым объектам
println!("Page has {} objects", doc.objects.len());
lopdf подходит, когда нужно манипулировать структурой PDF напрямую: объединение документов, перезапись потоков объектов или создание специализированных PDF-процессоров.
printpdf: только создание PDF
printpdf — библиотека исключительно для создания PDF. Она не может читать или парсить существующие PDF. Предоставляет типизированный API для построения PDF-документов с нуля с текстом, изображениями и векторной графикой.
use printpdf::*;
let (doc, page1, layer1) = PdfDocument::new(
"Report", Mm(210.0), Mm(297.0), "Layer 1"
);
let current_layer = doc.get_page(page1).get_layer(layer1);
// Добавление текста — требуется ручная загрузка шрифта
let font = doc.add_builtin_font(BuiltinFont::Helvetica)?;
current_layer.use_text("Hello World", 24.0, Mm(10.0), Mm(280.0), &font);
// Сохранение
doc.save(&mut std::io::BufWriter::new(
std::fs::File::create("output.pdf")?,
))?;
// Не может читать существующие PDF
// Не может извлекать текст, изображения или поля форм
printpdf подходит, когда нужно только генерировать новые PDF и хочется чистый, специализированный API для создания.
pdf-rs: низкоуровневое чтение PDF
pdf-rs парсит структуру PDF в типы Rust, но предоставляет минимум высокоуровневой функциональности. Вы получаете типизированный доступ к PDF-объектам, но по-прежнему должны самостоятельно обрабатывать декодирование текста, разрешение шрифтов и парсинг потоков контента.
use pdf::file::FileOptions;
let file = FileOptions::cached().open("report.pdf")?;
// Доступ к объектам страниц
let page = file.get_page(0)?;
let media_box = page.media_box()?;
println!("Page size: {:?}", media_box);
// Доступ к потоку контента — низкоуровневый
if let Some(ref contents) = page.contents {
// Возвращает сырые операции — интерпретировать их нужно самостоятельно
// Нет встроенной сборки текста, декодирования шрифтов или анализа макета
}
// Не может записывать или модифицировать PDF
pdf-rs подходит, когда нужен типобезопасный парсер PDF для анализа, валидации или построения собственного рендерера.
Сравнение возможностей по задачам
Извлечение текста
| Библиотека | Встроенное | Качество | Необходимые усилия |
|---|---|---|---|
| PDF Oxide | Да | Production-уровня (Unicode, CJK, порядок чтения) | Один вызов метода |
| pdf_extract | Да | Базовое (пропускает сложные макеты) | Один вызов метода |
| lopdf | Нет | Н/Д | Сотни строк пользовательского кода |
| printpdf | Нет | Н/Д | Невозможно (только запись) |
| pdf-rs | Нет | Н/Д | Значительный объём пользовательского кода |
PDF Oxide обрабатывает декодирование CMap/ToUnicode, пробелы на основе метрик шрифтов, порядок чтения из дерева структуры и реконструкцию лигатур. Реализация аналогичной функциональности поверх lopdf или pdf-rs потребует тысяч строк кода и глубокого знания спецификации PDF.
Создание PDF
| Библиотека | Подход | Ввод Markdown/HTML | Таблицы | Штрихкоды |
|---|---|---|---|---|
| PDF Oxide | Высокоуровневый + низкоуровневый | Да | Да | Да |
| lopdf | Конструирование сырых объектов | Нет | Нет | Нет |
| printpdf | Типизированный API слоёв | Нет | Нет | Нет |
| pdf-rs | Н/Д (только чтение) | Н/Д | Н/Д | Н/Д |
Шифрование
| Библиотека | Чтение зашифрованных | Запись зашифрованных | Алгоритмы |
|---|---|---|---|
| PDF Oxide | Да | Да | RC4-40, RC4-128, AES-128, AES-256 |
| lopdf | Нет | Нет | – |
| printpdf | Нет | Нет | – |
| pdf-rs | Частично | Нет | Только RC4 |
Соответствие стандартам
| Библиотека | PDF/A | PDF/X | PDF/UA |
|---|---|---|---|
| PDF Oxide | Валидация + конвертация | Валидация | Валидация |
| lopdf | Нет | Нет | Нет |
| printpdf | Частично (вывод PDF/A-1b) | Нет | Нет |
| pdf-rs | Нет | Нет | Нет |
Размер зависимостей
| Библиотека | Зависимости | Время компиляции | Размер бинарника |
|---|---|---|---|
| PDF Oxide | ~40 (ядро) | ~30 с | ~4 МБ |
| lopdf | ~15 | ~10 с | ~1 МБ |
| printpdf | ~20 | ~15 с | ~2 МБ |
| pdf-rs | ~25 | ~20 с | ~2 МБ |
У PDF Oxide больше зависимостей, потому что он включает парсинг шрифтов, декодирование изображений, интерпретацию потоков контента и шифрование — функции, которые другие библиотеки оставляют пользователю или не реализуют. С включением всех опциональных возможностей (rendering, barcodes, office) количество вырастает до ~100.
Комбинирование библиотек
Поскольку все имеют разрешительные лицензии, их можно комбинировать в одном проекте:
[dependencies]
pdf_oxide = "0.3"
lopdf = "0.32" # Опционально: доступ к сырым объектам для граничных случаев
Типичные паттерны:
- PDF Oxide + lopdf: PDF Oxide для извлечения и создания, lopdf для граничных случаев, требующих прямой манипуляции объектами.
- PDF Oxide + printpdf: PDF Oxide для чтения, printpdf для специализированных сценариев создания.
Матрица сценариев использования
“Мне нужно извлекать текст из PDF”
| Крейт | Подходит? | Примечания |
|---|---|---|
| PDF Oxide | Да | Лучшее качество извлечения, 100% успешность, порядок чтения, метаданные шрифтов |
| pdf_extract | Частично | Базовое извлечение, 91.5% успешность |
| lopdf | Нет | Нет извлечения текста |
| printpdf | Нет | Не может читать PDF |
| pdf-rs | Частично | Базовый парсинг, нет высокоуровневого извлечения текста |
“Мне нужно создавать PDF”
| Крейт | Подходит? | Примечания |
|---|---|---|
| PDF Oxide | Да | Высокоуровневый (Markdown/HTML) и низкоуровневый API |
| lopdf | Частично | Конструирование низкоуровневых объектов |
| printpdf | Да | Чистый API для создания, без чтения |
| pdf-rs | Нет | Только чтение |
“Мне нужно редактировать существующие PDF”
| Крейт | Подходит? | Примечания |
|---|---|---|
| PDF Oxide | Да | DOM-подобное редактирование, аннотации, формы |
| lopdf | Частично | Низкоуровневая манипуляция объектами |
| printpdf | Нет | Не может читать PDF |
| pdf-rs | Нет | Только чтение |
“Мне нужен полный жизненный цикл (извлечение + создание + редактирование)”
| Крейт | Подходит? | Примечания |
|---|---|---|
| PDF Oxide | Да | Единственный крейт, покрывающий все три задачи |
| lopdf + printpdf | Частично | Два крейта, нет извлечения текста |
| pdf-rs + printpdf | Частично | Два крейта, нет редактирования |
Когда использовать какую библиотеку
Выбирайте PDF Oxide, если вам нужно больше одной PDF-возможности (извлечение + создание или извлечение + редактирование) и вы хотите единственную, хорошо протестированную зависимость с 100% надёжностью.
Выбирайте lopdf, если вам нужна низкоуровневая манипуляция структурой PDF и вы готовы работать со спецификацией напрямую. Хорош для объединения, разделения и пакетной обработки PDF.
Выбирайте printpdf, если вы только создаёте PDF и никогда не читаете их. Самый чистый API для генерации отчётов и документов.
Выбирайте pdf-rs, если вам нужен парсер, соответствующий спецификации, для анализа PDF или построения собственного рендеринг-пайплайна.
Выбирайте pdf_extract, если вам нужно базовое извлечение текста без требований к высокой надёжности или поддержке сложных макетов.
Связанные страницы
- Benchmark производительности — полные результаты benchmark на корпусе
- Начало работы с Rust — установка и первое извлечение
- Справочник Rust API — полный Rust API
- Сравнение с Python-библиотеками — сравнение с экосистемой Python