Primeros pasos con PDF Oxide (Dart / Flutter)
PDF Oxide es la forma más rápida de leer PDF desde Dart y Flutter: 0,8 ms de media en extracción de texto y 100 % de aciertos en 3830 PDF. El paquete pdf_oxide es un wrapper idiomático sobre dart:ffi del núcleo escrito en Rust: los handles de PDF se liberan automáticamente mediante un NativeFinalizer (y con close() explícito), las cadenas y los búferes de C se copian a Dart por ti, y los códigos de error de la C-ABI se exponen como una excepción PdfOxideError.
Instalación
Añade pdf_oxide a tu pubspec.yaml:
dependencies:
pdf_oxide: ^0.3.69
Luego resuelve las dependencias:
dart pub get
El binding carga la librería nativa (libpdf_oxide.{so,dylib,dll}) en tiempo de ejecución. El orden de resolución es PDF_OXIDE_LIB_PATH (ruta completa) → PDF_OXIDE_LIB_DIR → ../target/release → target/release → el cargador del sistema. En Flutter, distribuye la librería de la plataforma junto con tu aplicación y apunta PDF_OXIDE_LIB_PATH a ella.
Inicio rápido
Abre un PDF y extrae el texto de la primera página. Llama siempre a close() cuando termines: un bloque try/finally lo mantiene ordenado.
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 un PDF que ya está en memoria (por ejemplo, descargado por HTTP en una aplicación Flutter), usa 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 y HTML
Cada página puede renderizarse como texto plano, Markdown o HTML. Los métodos por página reciben un índice de página basado en 0; las variantes …All() se ejecutan sobre todo el documento.
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();
}
}
También existe una vista ligera Page si prefieres trabajar página a página:
final doc = PdfDocument.open('report.pdf');
final page = doc.page(0);
print(page.text());
print(page.markdown());
doc.close();
Palabras y líneas con coordenadas
extractWords devuelve cada palabra con su bounding box, su fuente y su grosor; extractTextLines devuelve líneas completas. Las coordenadas se expresan en puntos del espacio de usuario del 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 detalle a nivel de glifo, extractChars devuelve cada Char con su punto de código Unicode, su bounding box, el nombre de la fuente y el tamaño:
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();
Búsqueda
search busca dentro de una sola página; searchAll recorre todo el documento. Ambos reciben un término de búsqueda y un flag caseSensitive, y devuelven registros SearchResult que llevan el texto coincidente, su índice de página y su 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();
}
}
Crear un PDF
El builder Pdf convierte Markdown, HTML o texto plano en un PDF. Llama a toBytes() para obtener los bytes o a save() para escribir un archivo, y a close() cuando termines.
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>') y Pdf.fromText('Plain text content.') funcionan de la misma manera. Como un Pdf construido no es más que bytes, puedes pasarlo directamente a PdfDocument.openFromBytes(pdf.toBytes()) para volver a extraerlo sin tocar el disco.
Manejo de errores
Toda llamada que pueda fallar lanza PdfOxideError (que implements Exception), que lleva el código de error subyacente de la 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 pasos
- Primeros pasos con Rust — el núcleo nativo que impulsa este binding
- Primeros pasos con Python — usar PDF Oxide desde Python
- Extracción de texto — opciones de extracción y recetas en detalle
- Creación de PDF — creación avanzada con metadatos y cifrado