PDF Oxide frente a lopdf
lopdf es un crate de Rust de bajo nivel para la manipulación directa de objetos PDF. PDF Oxide es una biblioteca de alto nivel con extracción de texto, creación y edición integradas. Ambas apuntan a casos de uso fundamentalmente distintos.
Diferencias clave
Nivel de abstracción. lopdf te entrega objetos PDF en bruto: diccionarios, streams y tablas de referencias cruzadas. No hay extracción de texto, ni decodificación de fuentes, ni exportación de imágenes. PDF Oxide ofrece métodos diseñados a tal efecto: extract_text(), extract_images(), to_markdown().
Fiabilidad. lopdf no logra analizar el 20 % del corpus de pruebas de 3830 PDF. De los PDF que sí analiza, el 57 % produce una salida vacía porque lopdf no tiene extracción de texto: obtienes los objetos, pero ningún texto. PDF Oxide supera el 100 %.
Velocidad en PDF analizables. lopdf es más rápido en el análisis de objetos en bruto: 0,3 ms de media frente a 0,8 ms de PDF Oxide. Pero lopdf no realiza ningún trabajo de extracción de texto: tendrías que construir tú mismo la decodificación de fuentes, la resolución de CMap, el análisis de espaciado y el orden de lectura.
Comparación rápida
| PDF Oxide | lopdf | |
|---|---|---|
| Nivel de la API | Alto nivel | Bajo nivel |
| Extracción de texto | Integrada (nivel de producción) | Ninguna |
| Tasa de éxito (3830 PDF) | 100 % | 80,2 % |
| Tiempo medio de análisis | 0,8 ms | 0,3 ms |
| Extracción de imágenes | Integrada | Manual (streams en bruto) |
| Campos de formulario | Lectura + escritura | Manual (diccionarios en bruto) |
| Creación de PDF | Sí (Markdown/HTML) | Sí (objetos en bruto) |
| Salida en Markdown/HTML | Sí | No |
| Cifrado | Lectura + escritura | No |
| Renderizado | Sí | No |
| Validación PDF/A | Sí | No |
| Licencia | MIT | MIT |
Lo que lopdf no puede hacer
lopdf da acceso a los objetos PDF, pero la extracción de texto exige interpretar esos objetos conforme a la especificación PDF. Esto es lo que tendrías que construir tú mismo:
- Análisis del stream de contenido: analizar operadores al estilo PostScript (Tj, TJ, Tm, Tf, etc.)
- Resolución de fuentes: buscar los recursos /Font y resolver referencias indirectas
- Decodificación de CMap/ToUnicode: convertir los ID de glifo en caracteres Unicode
- Espaciado por métricas de fuente: calcular el ancho de los caracteres a partir de los descriptores de fuente
- Transformaciones de la matriz de texto: aplicar los operadores Tm, Td, T* para posicionar el texto
- Orden de lectura: determinar el orden correcto en diseños de varias columnas
- Reconstrucción de ligaduras: tratar las ligaduras fi, fl, ffi
- Codificación CJK: decodificar codificaciones de texto en chino, japonés y coreano
Esto supone miles de líneas de código y un conocimiento profundo de ISO 32000. PDF Oxide se encarga de todo ello internamente.
Código en paralelo
Extracción de texto
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 does not provide text extraction.
// You get access to PDF objects only:
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()?)?;
// To get actual text, you must:
// 1. Parse content stream operators
// 2. Resolve font references from /Resources
// 3. Decode CMap/ToUnicode mappings
// 4. Apply text matrix transformations
// 5. Handle encoding differences
// ... (hundreds to thousands of lines of code)
Creación de 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");
// Create font dictionary
let font_id = doc.add_object(dictionary! {
"Type" => "Font",
"Subtype" => "Type1",
"BaseFont" => "Helvetica",
});
// Create resources
let resources_id = doc.add_object(dictionary! {
"Font" => dictionary! { "F1" => font_id },
});
// Create content stream (raw PostScript operators)
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);
// Create page
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,
});
// Wire up page tree
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 cifrados
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 does not support encrypted PDFs.
// Loading an encrypted PDF will fail or produce undecrypted streams.
Comparación de fiabilidad
| Métrica | PDF Oxide | lopdf |
|---|---|---|
| PDF analizados correctamente | 3823 / 3823 (100 %) | 3071 / 3823 (80,2 %) |
| PDF con salida de texto | 3823 / 3823 | ~1320 / 3823 (estimado) |
| Compatibilidad con PDF cifrados | Sí | No |
| Recuperación de PDF malformados | Sí | No |
La tasa de éxito del 80,2 % de lopdf significa que falla en aproximadamente 1 de cada 5 PDF. Los fallos se producen en documentos cifrados, PDF con tablas xref no estándar y documentos que usan streams de referencias cruzadas. PDF Oxide gestiona todos estos casos con un análisis tolerante y estrategias de respaldo.
Cuándo usar cada uno
Elige PDF Oxide si:
- Necesitas extracción de texto, extracción de imágenes o cualquier operación a nivel de contenido
- Quieres un único crate para lectura + escritura + creación
- Necesitas procesar todos los PDF de forma fiable (cifrados, malformados, complejos)
- Necesitas salida en Markdown/HTML, renderizado u OCR
- Necesitas validación de conformidad (PDF/A, PDF/X, PDF/UA)
Elige lopdf si:
- Necesitas acceso directo a los objetos PDF para un procesamiento personalizado
- Estás construyendo una herramienta PDF especializada que trabaja a nivel de objeto
- Necesitas fusionar documentos manipulando árboles de objetos directamente
- Tus PDF son sencillos y están bien formados (no cifrados, tablas xref estándar)
Combina ambos:
Usa PDF Oxide para las operaciones de alto nivel y lopdf para los casos límite que requieran acceso a objetos en bruto:
[dependencies]
pdf_oxide = "0.3"
lopdf = "0.32"
Páginas relacionadas
- Pruebas de rendimiento: resultados completos del corpus
- frente a bibliotecas PDF en Rust: comparación de todos los crates de Rust
- Primeros pasos con Rust: instalación y primera extracción