Початок роботи з PDF Oxide (Zig)
PDF Oxide — найшвидша PDF-бібліотека із вбудованим видобуванням тексту: 0,8 мс у середньому, 100 % успіху на 3 830 PDF. Прив’язка для Zig — це ідіоматичний Zig поверх pdf_oxide C ABI через @cImport, без прошарку-shim, повноцінна взаємодія з C. Хендли — це структури з методом deinit, а повернуті C-рядки та буфери копіюються в аллокатор, який передає викликач.
Зафіксовано на Zig 0.15.1 — у передрелізній (до 1.0) збірці API імпорту C змінюється від релізу до релізу, тож CI використовує ту саму версію.
Встановлення
Прив’язка лінкується з cdylib типового набору можливостей (а не з Python-колесом wheel). Зберіть нативну бібліотеку, після чого вкажіть Zig на заголовковий файл і cdylib:
# 1. збираємо нативну бібліотеку (набір можливостей прив'язки)
cargo build --release --lib \
--features ocr,rendering,signatures,barcodes,tsa-client,system-fonts
# 2. тестуємо й запускаємо приклад
cd zig
LD_LIBRARY_PATH="$PWD/../target/release" \
zig build test \
-DPDF_OXIDE_INCLUDE_DIR="$PWD/../include" \
-DPDF_OXIDE_LIB_DIR="$PWD/../target/release"
LD_LIBRARY_PATH="$PWD/../target/release" \
zig build example \
-DPDF_OXIDE_INCLUDE_DIR="$PWD/../include" \
-DPDF_OXIDE_LIB_DIR="$PWD/../target/release"
У власному коді достатньо імпортувати модуль — і можна працювати:
const pdf_oxide = @import("pdf_oxide");
Відкриття PDF
Відкрийте файл через Document.open (або Document.openFromBytes для даних у пам’яті) і перегляньте його метадані. Кожен хендл володіє C-ресурсами, тож одразу додавайте до нього defer doc.deinit().
const std = @import("std");
const pdf_oxide = @import("pdf_oxide");
pub fn main() !void {
const a = std.heap.page_allocator;
var doc = try pdf_oxide.Document.open("research-paper.pdf");
defer doc.deinit();
std.debug.print("pages: {d}\n", .{try doc.pageCount()});
const v = doc.version();
std.debug.print("version: {d}.{d}\n", .{ v.major, v.minor });
std.debug.print("encrypted: {}\n", .{doc.isEncrypted()});
}
Видобування тексту
extractText повертає текст однієї сторінки (нумерація з 0). Результатом володіє аллокатор, який ви передали, тож звільніть його, коли він більше не потрібен.
const a = std.heap.page_allocator;
var doc = try pdf_oxide.Document.open("report.pdf");
defer doc.deinit();
const text = try doc.extractText(a, 0);
defer a.free(text);
std.debug.print("{s}\n", .{text});
Варіанти для всього документа видобувають усі сторінки одразу:
const all_text = try doc.toPlainTextAll(a);
defer a.free(all_text);
std.debug.print("{s}\n", .{all_text});
Перетворення на Markdown і HTML
Перетворіть окрему сторінку або весь документ на Markdown чи HTML. Кожен виклик повертає зріз, яким володіє аллокатор.
const md = try doc.toMarkdown(a, 0);
defer a.free(md);
std.debug.print("{s}\n", .{md});
const md_all = try doc.toMarkdownAll(a);
defer a.free(md_all);
const html = try doc.toHtml(a, 0);
defer a.free(html);
Видобування на рівні слів
extractWords повертає зріз структур Word із текстом, обмежувальним прямокутником, шрифтом і прапорцем жирності. Звільняйте весь зріз відповідним помічником freeWords — він вивільняє рядки кожного слова та сам базовий зріз.
const words = try doc.extractWords(a, 0);
defer pdf_oxide.Document.freeWords(a, words);
for (words) |w| {
std.debug.print("'{s}' at ({d:.1}, {d:.1}) font={s} size={d:.1} bold={}\n", .{
w.text, w.bbox.x, w.bbox.y, w.fontName, w.fontSize, w.bold,
});
}
Поля Word:
| Поле | Тип | Опис |
|---|---|---|
text |
[]u8 |
Текст слова (належить аллокатору) |
bbox |
Bbox |
{ x, y, width, height } у пунктах |
fontName |
[]u8 |
Назва шрифту в PostScript |
fontSize |
f32 |
Розмір шрифту в пунктах |
bold |
bool |
Чи є фрагмент жирним |
Той самий підхід дає доступ до символів і рядків:
const chars = try doc.extractChars(a, 0);
defer pdf_oxide.Document.freeChars(a, chars);
const lines = try doc.extractTextLines(a, 0);
defer pdf_oxide.Document.freeTextLines(a, lines);
Пошук
search шукає в межах однієї сторінки; searchAll сканує всі сторінки. Обидва приймають NUL-термінований термін і прапорець case_sensitive, а повертають зріз SearchResult.
const hits = try doc.searchAll(a, "configuration", false);
defer pdf_oxide.Document.freeSearchResults(a, hits);
for (hits) |hit| {
std.debug.print("page {d}: '{s}' at ({d:.0}, {d:.0})\n", .{
hit.page, hit.text, hit.bbox.x, hit.bbox.y,
});
}
Щоб обмежити пошук однією сторінкою, скористайтеся search з індексом сторінки:
const page_hits = try doc.search(a, 0, "Alpha", false);
defer pdf_oxide.Document.freeSearchResults(a, page_hits);
Створення PDF
Тип Pdf будує документи з Markdown, HTML або звичайного тексту. toBytes серіалізує в пам’ять; save записує на диск.
const a = std.heap.page_allocator;
var pdf = try pdf_oxide.Pdf.fromMarkdown("# Hello\n\nThis is a **Zig** PDF.\n");
defer pdf.deinit();
// Серіалізуємо в пам'ять...
const bytes = try pdf.toBytes(a);
defer a.free(bytes);
// ...або записуємо одразу на диск.
try pdf.save("output.pdf");
Щойно створений PDF можна одразу пропустити назад крізь екстрактор:
var pdf = try pdf_oxide.Pdf.fromHtml("<h1>Invoice</h1><p>Amount: $42</p>");
defer pdf.deinit();
const bytes = try pdf.toBytes(a);
defer a.free(bytes);
var doc = try pdf_oxide.Document.openFromBytes(bytes);
defer doc.deinit();
const text = try doc.extractText(a, 0);
defer a.free(text);
std.debug.print("{s}\n", .{text});
Обробка помилок
Виклики, що можуть зазнати невдачі, повертають Error!T, де Error — це error{ PdfOxide, OutOfMemory }. Оскільки значення помилок Zig не можуть нести корисне навантаження, базовий код C-ABI доступний через lastErrorCode() — зчитуйте його одразу після перехоплення error.PdfOxide.
const text = doc.extractText(a, 99) catch |err| switch (err) {
error.PdfOxide => {
std.debug.print("pdf_oxide error code: {d}\n", .{pdf_oxide.lastErrorCode()});
return;
},
error.OutOfMemory => return err,
};
defer a.free(text);
Наступні кроки
- Початок роботи з Rust — нативне ядро, на якому побудовано PDF Oxide
- Початок роботи з Python — використання PDF Oxide із Python
- Видобування тексту — докладні параметри й рецепти видобування
- Створення PDF — розширене створення з метаданими та шифруванням