Skip to content

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. Ось що вам потрібно було б побудувати самостійно:

  1. Парсинг потоку вмісту — розібрати PostScript-подібні оператори (Tj, TJ, Tm, Tf тощо)
  2. Розв’язання шрифтів — знайти ресурси /Font, розв’язати непрямі посилання
  3. Декодування CMap/ToUnicode — конвертувати ID гліфів у символи 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 не надає вилучення тексту.
// Ви отримуєте лише доступ до об'єктів 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"

Пов’язані сторінки