Primeiros Passos com o PDF Oxide (Zig)
O PDF Oxide é a biblioteca de PDF mais rápida com extração de texto embutida — 0,8ms de média e 100% de aprovação em 3.830 PDFs. O binding Zig é Zig idiomático sobre a C ABI do pdf_oxide via @cImport — sem camada intermediária, interoperabilidade com C de primeira classe. Os handles são structs com deinit, e as strings/buffers C retornados são copiados para um allocator fornecido pelo chamador.
Fixado no Zig 0.15.1 — a build pré-1.0 e a API de import de C mudam entre as versões, então o CI usa a mesma versão.
Instalação
O binding faz o link com a cdylib de features padrão (não com o wheel do Python). Compile a biblioteca nativa e então aponte o Zig para o header e para a cdylib:
# 1. compile a biblioteca nativa (conjunto de features do binding distribuído)
cargo build --release --lib \
--features ocr,rendering,signatures,barcodes,tsa-client,system-fonts
# 2. teste + rode o exemplo
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"
No seu próprio código, importe o módulo e está tudo pronto:
const pdf_oxide = @import("pdf_oxide");
Abrindo um PDF
Abra um arquivo com Document.open (ou Document.openFromBytes para dados em memória) e inspecione seus metadados. Cada handle é dono de recursos C, então combine-o com 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()});
}
Extração de Texto
extractText retorna o texto de uma única página (índice começando em 0). O resultado pertence ao allocator que você passa, então libere-o quando terminar.
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});
As variantes para o documento inteiro extraem todas as páginas de uma vez:
const all_text = try doc.toPlainTextAll(a);
defer a.free(all_text);
std.debug.print("{s}\n", .{all_text});
Conversão para Markdown e HTML
Converta uma única página ou o documento inteiro para Markdown ou HTML. Cada chamada retorna uma slice pertencente ao allocator.
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);
Extração no Nível de Palavras
extractWords retorna uma slice de structs Word com texto, bounding box, fonte e flag de negrito. Libere a slice inteira com o helper correspondente freeWords — ele libera as strings de cada palavra e a slice de apoio.
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,
});
}
Campos de Word:
| Campo | Tipo | Descrição |
|---|---|---|
text |
[]u8 |
Texto da palavra (pertence ao allocator) |
bbox |
Bbox |
{ x, y, width, height } em pontos |
fontName |
[]u8 |
Nome PostScript da fonte |
fontSize |
f32 |
Tamanho da fonte em pontos |
bold |
bool |
Se o trecho está em negrito |
O mesmo padrão entrega caracteres e linhas:
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);
Busca
search procura dentro de uma página; searchAll varre todas as páginas. Ambos recebem um termo terminado em NUL e uma flag case_sensitive, e retornam uma slice de 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,
});
}
Para restringir a busca a uma única página, use search com o índice da página:
const page_hits = try doc.search(a, 0, "Alpha", false);
defer pdf_oxide.Document.freeSearchResults(a, page_hits);
Criando um PDF
O tipo Pdf constrói documentos a partir de Markdown, HTML ou texto puro. toBytes serializa para a memória; save grava em disco.
const a = std.heap.page_allocator;
var pdf = try pdf_oxide.Pdf.fromMarkdown("# Hello\n\nThis is a **Zig** PDF.\n");
defer pdf.deinit();
// Serialize para a memória...
const bytes = try pdf.toBytes(a);
defer a.free(bytes);
// ...ou grave direto no disco.
try pdf.save("output.pdf");
Você pode fazer um round-trip de um PDF recém-construído direto de volta pelo extrator:
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});
Tratamento de Erros
Chamadas que podem falhar retornam Error!T, onde Error é error{ PdfOxide, OutOfMemory }. Como valores de erro em Zig não podem carregar um payload, o código da C ABI subjacente é exposto via lastErrorCode() — leia-o logo após capturar 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);
Próximos Passos
- Primeiros Passos com Rust — o núcleo nativo sobre o qual o PDF Oxide é construído
- Primeiros Passos com Python — usando o PDF Oxide a partir do Python
- Extração de Texto — opções e receitas detalhadas de extração
- Criação de PDF — criação avançada com metadados e criptografia