Rendimiento
PDF Oxide v0.3.11 ofrece 0.8ms de extracción de texto promedio en Python sobre un corpus de 3,830 PDFs — 5× más rápido que PyMuPDF y 15× más rápido que pypdf, con 100% de tasa de éxito en PDFs válidos.
Resultados del Benchmark
Corpus: 3,830 PDFs
Tres suites de prueba públicas independientes combinadas:
| Suite | PDFs | Fuente |
|---|---|---|
| veraPDF | 2,907 | Corpus de prueba de conformidad PDF/A |
| Mozilla pdf.js | 897 | Suite de prueba de renderizado PDF del navegador |
| SafeDocs | 26 | Corpus de PDFs malformados de DARPA SafeDocs |
Por qué este corpus es confiable
Estos no son PDFs seleccionados a mano. Cada suite es un corpus de prueba establecido y revisado por pares, mantenido por organismos de estandarización o grandes proyectos de código abierto:
-
veraPDF es el validador oficial de conformidad PDF/A utilizado por la comunidad de estándares PDF durante más de 10 años. Sus 2,907 archivos de prueba son atómicos — cada uno prueba exactamente una característica de la especificación PDF — abarcando todas las versiones PDF/A (1A/1B, 2A/2B/2U, 3A/3B/3U, 4/4E/4F), accesibilidad PDF/UA (UA1, UA2), y tanto ISO 32000-1 (PDF 1.7) como ISO 32000-2 (PDF 2.0). Licenciado bajo CC BY 4.0.
-
Mozilla pdf.js impulsa el renderizado de PDF en Firefox, procesando miles de millones de PDFs anualmente. Sus 897 archivos de prueba cubren casos extremos de renderizado del mundo real: documentos complejos de múltiples páginas, tipos de anotaciones (estilos de borde, resaltados, marcas, archivos adjuntos), widgets de formulario, codificaciones de fuentes inusuales, documentos grandes de prueba de estrés y casos extremos de content stream. 7 archivos están intencionalmente corrompidos para probar el rechazo correcto de errores.
-
DARPA SafeDocs es un programa de investigación de seguridad financiado por el gobierno de EE.UU. enfocado en la robustez de parsers. Sus 26 archivos apuntan a los casos extremos más difíciles: ciclos de content stream en fuentes Type3, trailers startxref dobles, sintaxis PDF compactada, variaciones de dialecto, casos extremos de imágenes inline, anidamiento recursivo de fuentes y PDFs cifrados con contraseñas Unicode (incluyendo UTF-16 LE). Estos archivos están diseñados para colgar, bloquear o explotar parsers vulnerables.
Lo que cubre este corpus
- Todas las versiones principales de PDF: PDF 1.0 hasta PDF 2.0
- Cifrado y contraseñas: AES-256, RC4, contraseñas Unicode, codificación UTF-16 LE
- Casos extremos de seguridad: Estructuras recursivas, ciclos de content stream, trailers malformados, corrupción generada por fuzzing
- Diversidad de fuentes: TrueType, CIDFont, Type1, Type3, codificaciones CJK, subconjuntos incrustados
- Complejidad de documentos: Desde fixtures de una página hasta documentos con más de 10,000 páginas, imágenes inline, Form XObjects anidados
- Rechazo correcto: 7 archivos intencionalmente rotos (cabeceras PDF faltantes, streams xref inválidos) — una biblioteca que hace “parsing” de estos es menos segura, no más confiable
De los 3,830 archivos, 3,823 son PDFs válidos. Los 7 archivos inválidos son fixtures de prueba para manejo de errores — PDF Oxide rechaza correctamente los 7.
Comparación de Bibliotecas Python
Tiempo promedio de extracción de texto por PDF en el corpus completo de 3,830 PDFs:
| Biblioteca | Promedio | p99 | Tasa de Éxito | Licencia |
|---|---|---|---|---|
| PDF Oxide | 0.8ms | 9ms | 100% | MIT |
| PyMuPDF | 4.6ms | 28ms | 99.3% | AGPL-3.0 |
| pypdfium2 | 4.1ms | 42ms | 99.2% | Apache-2.0 |
| pymupdf4llm | 55.5ms | 280ms | 99.1% | AGPL-3.0 |
| pdftext | 7.3ms | 82ms | 99.0% | GPL-3.0 |
| pdfminer | 16.8ms | 124ms | 98.8% | MIT |
| pdfplumber | 23.2ms | 189ms | 98.8% | MIT |
| markitdown | 108.8ms | 378ms | 98.6% | MIT |
| pypdf | 12.1ms | 97ms | 98.4% | BSD-3 |
PDF Oxide es la biblioteca PDF más rápida disponible para Python. A diferencia de PyMuPDF, usa la licencia MIT — sin restricciones AGPL para uso comercial.
Comparación de Bibliotecas Rust
| Biblioteca | Promedio | p99 | Tasa de Éxito | Extracción de Texto |
|---|---|---|---|---|
| PDF Oxide | 0.8ms | 9ms | 100% | Integrada, nivel de producción |
| oxidize_pdf | 13.5ms | 11ms | 99.1% | Básica |
| unpdf | 2.8ms | 10ms | 95.1% | Básica |
| pdf_extract | 4.08ms | 37ms | 91.5% | Básica |
| lopdf | 0.3ms | 2ms | 80.2% | Sin extracción integrada |
lopdf es más rápido en los PDFs que puede procesar, pero falla en el 20% del corpus. pdf_oxide es el único crate Rust que combina 100% de confiabilidad con extracción de texto integrada. Ten en cuenta que lopdf no proporciona extracción de texto — necesitas construir la decodificación de fuentes y el análisis de espaciado por tu cuenta.
Calidad del Texto
PDF Oxide alcanza 99.5% de paridad de texto comparado con PyMuPDF y pypdfium2 en el corpus completo. La calidad se midió comparando la salida de texto extraído carácter por carácter en los 3,823 PDFs válidos.
Desglose por Corpus
| Corpus | PDFs | PDF Oxide Promedio | pypdfium2 Promedio | PyMuPDF Promedio |
|---|---|---|---|---|
| veraPDF | 2,907 | 0.7ms | 3.6ms | 4.1ms |
| Mozilla pdf.js | 897 | 1.1ms | 5.8ms | 6.2ms |
| SafeDocs | 26 | 0.9ms | 4.0ms | 4.3ms |
Historial de Optimización: v0.3.5 → v0.3.8
v0.3.8 eliminó dos cuellos de botella críticos O(n) que hicieron que el promedio bajara de 23.3ms a 0.8ms (Python) en el mismo corpus.
1. Caché Masivo del Árbol de Páginas
Antes: get_page() recorría el árbol de páginas desde la raíz para cada página no cacheada. Para la extracción secuencial de todas las páginas, esto era O(n) por página y O(n²) en total.
Después: En el primer acceso a una página, todo el árbol de páginas se recorre una vez y todas las páginas se cachean en un HashMap<usize, Object>. Cada acceso posterior es O(1).
Esta es la corrección que llevó un PDF de 10,000 páginas de 55 segundos a 332 milisegundos.
2. Caché de Offset de Scan-for-Object
Antes: Cuando los objetos faltaban en la tabla xref, scan_for_object() leía el archivo PDF completo para cada objeto faltante. PDFs con etiquetas y cientos de elementos del árbol de estructura que no estaban en el xref desencadenaban cientos de lecturas completas del archivo.
Después: El archivo se escanea una vez y todos los offsets de objetos se cachean en un HashMap. Las búsquedas posteriores son O(1).
3. Extracción de Texto en Una Sola Pasada
Antes: extract_spans() ejecutaba dos pasadas sobre el contenido de la página — primero para clasificar el tipo de documento (académico, periódico, formulario, etc.), luego para extraer texto.
Después: La pasada de clasificación se eliminó completamente. Umbrales adaptativos con consciencia de fuente ahora producen resultados iguales o mejores en una sola pasada.
4. Pre-asignación de Content Stream
Antes: parse_content_stream() construía el Vec de operadores empezando desde la capacidad predeterminada, causando reasignaciones repetidas en content streams grandes.
Después: El Vec se pre-asigna basándose en el tamaño del stream (data.len() / 20), que estima aproximadamente un operador cada 20 bytes.
Metodología
Todos los benchmarks usan la misma metodología:
- Cada biblioteca procesa los 3,830 PDFs usando multiprocessing de Python (un PDF por proceso)
- Timeout de 60 segundos por PDF — cualquier PDF que exceda esto se cuenta como falla
- El texto extraído se guarda en disco por biblioteca para comparación de calidad
- Tiempo de reloj medido desde la apertura del archivo hasta la extracción final del texto
- Sin ejecuciones de calentamiento, sin caché entre archivos
- Un hilo por PDF
El harness de benchmark ejecuta las 18 bibliotecas (3 Rust, 15 Python) en la misma máquina, mismo corpus, mismas condiciones.
Reproducir los Benchmarks
Los corpus de prueba públicos están disponibles gratuitamente:
- veraPDF: github.com/veraPDF/veraPDF-corpus
- Mozilla pdf.js: github.com/mozilla/pdf.js/tree/master/test/pdfs
- SafeDocs: github.com/pdf-association/safedocs
Ejecuta la verificación:
cargo run --release --example verify_corpus -- \
/path/to/veraPDF-corpus \
/path/to/pdfjs-test \
/path/to/safedocs \
--csv results.csv
Características de Rendimiento
Donde PDF Oxide Es Rápido
- Extracción de texto: El objetivo principal de optimización. Sub-milisegundo para documentos típicos.
- Extracción secuencial de múltiples páginas: El caché del árbol de páginas hace que extraer todas las páginas de un documento grande sea casi tan rápido como extraer una.
- PDFs con etiquetas: El recorrido del árbol de estructura y la resolución de objetos ahora están cacheados.
- PDFs malformados: Parsing tolerante con estrategias de fallback evita reintentos costosos.
Lo Que Escala Linealmente
- Conteo de páginas: Cada página se procesa independientemente. 100 páginas toma aproximadamente 100× una página.
- Tamaño del content stream: El parsing de operadores es lineal en la longitud del stream.
- Extracción de imágenes: Proporcional al número y tamaño de las imágenes.
Cuándo Esperar Resultados Más Lentos
- PDFs escaneados con OCR: El procesamiento OCR (si está habilitado) es significativamente más lento que la extracción de texto.
- Renderizado: El renderizado de páginas a imágenes depende de la complejidad del contenido y el DPI objetivo.
- PDFs fuertemente cifrados: El descifrado AES-256 agrega overhead por stream.
- PDFs con miles de fuentes: El parsing de fuentes se cachea por documento, pero el parsing inicial escala con el conteo de fuentes.
Siguientes Pasos
- Changelog – historial completo de versiones
- Comparación con Bibliotecas Python – comparación detallada con PyMuPDF, pypdf, pdfplumber, pdfminer
- Comparación con Bibliotecas Rust – comparación detallada con lopdf, pdf_extract, pdf-rs
- Primeros Pasos con Rust – instalación y primera extracción
- Referencia de API Rust – documentación completa de la API