Primeiros passos com o PDF Oxide (Dart / Flutter)
O PDF Oxide é a forma mais rápida de ler PDFs em Dart e Flutter — média de 0,8ms na extração de texto e 100% de aprovação em 3.830 PDFs. O pacote pdf_oxide é um wrapper idiomático em dart:ffi sobre o núcleo em Rust: os handles de PDF são liberados automaticamente por um NativeFinalizer (além do close() explícito), strings e buffers em C são copiados para o Dart por você, e os códigos de erro do C-ABI surgem como uma exceção PdfOxideError.
Instalação
Adicione o pdf_oxide ao seu pubspec.yaml:
dependencies:
pdf_oxide: ^0.3.69
Em seguida, baixe as dependências:
dart pub get
O binding carrega a biblioteca nativa (libpdf_oxide.{so,dylib,dll}) em tempo de execução. A ordem de resolução é PDF_OXIDE_LIB_PATH (caminho completo) → PDF_OXIDE_LIB_DIR → ../target/release → target/release → o loader do sistema. No Flutter, distribua a biblioteca da plataforma junto com o seu app e aponte a PDF_OXIDE_LIB_PATH para ela.
Guia Rápido
Abra um PDF e extraia o texto da primeira página. Sempre chame close() no documento quando terminar — o try/finally mantém isso organizado.
import 'package:pdf_oxide/pdf_oxide.dart';
void main() {
final doc = PdfDocument.open('research-paper.pdf');
try {
print('Pages: ${doc.pageCount}');
print('PDF version: ${doc.version}'); // e.g. 1.7
print(doc.extractText(0));
} finally {
doc.close();
}
}
Para abrir um PDF que já está em memória (por exemplo, baixado via HTTP em um app Flutter), use openFromBytes:
import 'dart:typed_data';
import 'package:pdf_oxide/pdf_oxide.dart';
void render(Uint8List bytes) {
final doc = PdfDocument.openFromBytes(bytes);
try {
print(doc.extractText(0));
} finally {
doc.close();
}
}
Texto, Markdown e HTML
Cada página pode ser renderizada como texto puro, Markdown ou HTML. Os métodos por página recebem um índice de página baseado em 0; as variantes …All() rodam sobre o documento inteiro.
import 'package:pdf_oxide/pdf_oxide.dart';
void main() {
final doc = PdfDocument.open('report.pdf');
try {
// Single page (index 0)
print(doc.extractText(0)); // raw extracted text
print(doc.toPlainText(0)); // normalized plain text
print(doc.toMarkdown(0)); // Markdown with headings, lists, tables
print(doc.toHtml(0)); // HTML
// Whole document
print(doc.toMarkdownAll());
print(doc.toHtmlAll());
print(doc.toPlainTextAll());
} finally {
doc.close();
}
}
Há também uma visão Page leve, caso você prefira trabalhar uma página de cada vez:
final doc = PdfDocument.open('report.pdf');
final page = doc.page(0);
print(page.text());
print(page.markdown());
doc.close();
Palavras e Linhas com Coordenadas
extractWords retorna cada palavra com sua bounding box, fonte e peso; extractTextLines retorna linhas inteiras. As coordenadas estão em pontos do espaço de usuário do PDF.
import 'package:pdf_oxide/pdf_oxide.dart';
void main() {
final doc = PdfDocument.open('paper.pdf');
try {
for (final word in doc.extractWords(0)) {
final b = word.bbox; // Bbox(x, y, width, height)
print("'${word.text}' at (${b.x}, ${b.y}) "
'font=${word.fontName} size=${word.fontSize} bold=${word.bold}');
}
for (final line in doc.extractTextLines(0)) {
print('${line.wordCount} words: ${line.text}');
}
} finally {
doc.close();
}
}
Para detalhe a nível de glifo, extractChars retorna cada Char com seu codepoint Unicode, bounding box, nome da fonte e tamanho:
final doc = PdfDocument.open('paper.pdf');
for (final ch in doc.extractChars(0)) {
print('${String.fromCharCode(ch.character)} @ ${ch.bbox} ${ch.fontSize}pt');
}
doc.close();
Busca
search procura dentro de uma única página; searchAll varre o documento inteiro. Ambos recebem um termo de busca e uma flag caseSensitive, e retornam registros SearchResult contendo o texto correspondente, o índice da página e a bounding box.
import 'package:pdf_oxide/pdf_oxide.dart';
void main() {
final doc = PdfDocument.open('manual.pdf');
try {
// Single page (page 0), case-insensitive
for (final hit in doc.search(0, 'configuration', false)) {
print("page ${hit.page}: '${hit.text}' at ${hit.bbox}");
}
// Across the whole document
final hits = doc.searchAll('configuration', false);
print('${hits.length} matches');
} finally {
doc.close();
}
}
Criando um PDF
O builder Pdf transforma Markdown, HTML ou texto puro em um PDF. Chame toBytes() para obter os bytes ou save() para gravar um arquivo, e close() ao terminar.
import 'package:pdf_oxide/pdf_oxide.dart';
void main() {
final pdf = Pdf.fromMarkdown('# Hello World\n\nThis is a **PDF**.\n');
try {
pdf.save('output.pdf');
final bytes = pdf.toBytes();
print('Wrote ${bytes.length} bytes');
} finally {
pdf.close();
}
}
Pdf.fromHtml('<h1>Invoice</h1><p>Amount: \$42</p>') e Pdf.fromText('Plain text content.') funcionam da mesma forma. Como um Pdf construído é apenas bytes, você pode passá-lo diretamente para PdfDocument.openFromBytes(pdf.toBytes()) para extraí-lo de volta sem tocar no disco.
Tratamento de Erros
Toda chamada passível de falha lança PdfOxideError (que implements Exception) carregando o código de erro subjacente do C-ABI:
import 'package:pdf_oxide/pdf_oxide.dart';
void main() {
try {
final doc = PdfDocument.open('/nonexistent/nope.pdf');
doc.close();
} on PdfOxideError catch (e) {
print('Failed to open PDF: $e');
}
}
Próximos Passos
- Primeiros passos com Rust — o núcleo nativo que dá vida a este binding
- 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