Skip to content

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. Вот что вам пришлось бы реализовать самостоятельно:

  1. Разбор потока содержимого — парсинг PostScript-подобных операторов (Tj, TJ, Tm, Tf и т. д.)
  2. Разрешение шрифтов — поиск ресурсов /Font, разрешение косвенных ссылок
  3. Декодирование CMap/ToUnicode — преобразование идентификаторов глифов в символы Unicode
  4. Интервалы по метрикам шрифта — вычисление ширины символов из дескрипторов шрифта
  5. Преобразования текстовой матрицы — применение операторов Tm, Td, T* для позиционирования текста
  6. Порядок чтения — определение правильного порядка для многоколоночных макетов
  7. Восстановление лигатур — обработка лигатур fi, fl, ffi
  8. Кодировки 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"

Связанные страницы