El problema
Convertir documentos de Word a PDF es una de las tareas más habituales del software de empresa. Facturas, contratos, informes, formularios de cumplimiento: nacen como archivos .docx y tienen que convertirse en PDF para compartirlos, archivarlos o imprimirlos.
Todas las soluciones existentes vienen con compromisos importantes:
- Microsoft Office / LibreOffice — obliga a instalar una suite ofimática completa en cada servidor. El modo headless de LibreOffice es lento, consume mucha memoria y produce resultados inconsistentes entre versiones. Escalar significa levantar varias instancias que se comen gigabytes de RAM.
- APIs en la nube (Google Docs, Adobe, CloudConvert) — añaden latencia, cuestan por conversión y envían documentos potencialmente sensibles a servidores de terceros. No es viable en sectores regulados ni en entornos aislados de la red.
- Herramientas de HTML a PDF (wkhtmltopdf, Puppeteer) — obligan a convertir primero el DOCX a HTML, perdiendo fidelidad de formato. Las tablas, los encabezados y pies de página y los saltos de página rara vez sobreviven al viaje de ida y vuelta.
Ninguna funciona bien cuando necesitas una conversión rápida, fiel y sin conexión a escala, especialmente en pipelines automatizados, sistemas de CI/CD o aplicaciones embebidas donde instalar LibreOffice no es una opción.
Cómo lo resuelve dxpdf
dxpdf es un conversor de DOCX a PDF independiente, escrito en Rust y basado en la librería gráfica Skia de Google. Lee los archivos .docx directamente, parsea la estructura OOXML y renderiza una salida PDF fiel al píxel, todo en un único binario sin más dependencias externas que Skia.
Un pipeline medir-maquetar-pintar inspirado en Flutter garantiza que el ajuste del texto, el dimensionado de las tablas y los saltos de página coincidan con lo que produce Microsoft Word:
DOCX (ZIP) → Parse → Document Model → Measure → Layout → Paint → PDF
El resultado es un conversor que tarda unos 115 ms en un documento de 3 páginas con tablas e imágenes, usando apenas 19 MB de memoria: lo bastante rápido como para ejecutarse dentro de un manejador de peticiones o procesar miles de documentos por lotes.
Qué soporta
dxpdf implementa 34 funcionalidades de OOXML con cobertura completa, entre ellas:
- Formato de texto — negrita, cursiva, subrayado, tamaño de fuente, familia tipográfica, color, espaciado entre caracteres, superíndice, subíndice y sombreado de fragmentos
- Párrafos — alineación (izquierda, centro, derecha), espaciado, sangría, tabulaciones, bordes y sombreado
- Tablas — anchos de columna, márgenes de celda con cascada de 3 niveles, celdas combinadas (horizontal y vertical), alturas de fila, bordes, sombreado de celdas y tablas anidadas
- Imágenes — en línea (PNG, JPEG, BMP, WebP) y flotantes/ancladas con alineación y posicionamiento porcentual
- Estilos — estilos de párrafo y de carácter con herencia
basedOn, valores por defecto del documento y fuentes del tema - Encabezados y pies de página — texto, imágenes y números de página (códigos de campo PAGE/NUMPAGES)
- Listas — viñetas, decimal, letra minúscula/mayúscula y romana minúscula/mayúscula con seguimiento de contadores
- Hipervínculos — renderizados como anotaciones de enlace clicables en el PDF
- Secciones — varios tamaños de página y márgenes, saltos de sección y orientaciones vertical y horizontal
- Maquetación — paginación automática, ajuste de línea, modos de interlineado y flujo del texto alrededor de imágenes flotantes
Tres formas de usarlo
Herramienta de línea de comandos
Instálalo y ejecútalo con un solo comando:
cargo install dxpdf
dxpdf input.docx -o output.pdf
Librería de Rust
Una llamada a función: bytes de entrada, bytes de salida.
let docx_bytes = std::fs::read("document.docx")?;
let pdf_bytes = dxpdf::convert(&docx_bytes)?;
std::fs::write("output.pdf", &pdf_bytes)?;
Para más control, inspecciona el modelo de documento parseado antes de renderizar:
use dxpdf::{parse, model};
let document = parse::parse(&std::fs::read("document.docx")?)?;
for block in &document.blocks {
match block {
model::Block::Paragraph(p) => { /* inspect paragraph */ }
model::Block::Table(t) => { /* inspect table */ }
}
}
let pdf_bytes = dxpdf::convert_document(&document)?;
Paquete de Python
Instálalo desde PyPI y úsalo en cualquier aplicación Python:
pip install dxpdf
import dxpdf
# Bytes in, bytes out
pdf_bytes = dxpdf.convert(open("input.docx", "rb").read())
# File to file
dxpdf.convert_file("input.docx", "output.pdf")
Rendimiento
Medido en un Apple M3 Max con hyperfine (20 ejecuciones, 3 de calentamiento), convirtiendo un documento de 3 páginas con 11 tablas, 2 imágenes y 2 secciones:
| Métrica | Valor |
|---|---|
| Tiempo medio de conversión | 113 ms |
| Pico de memoria (RSS) | 19 MB |
El núcleo en Rust tiene 104 tests unitarios y 9 tests de integración, incluidos tests de regresión visual que comparan los PDF renderizados con documentos de referencia generados por Word.
Casos de uso reales
Pipelines documentales automatizados
Sistemas de CI/CD o procesadores por lotes que generan contratos, facturas o informes a partir de plantillas .docx. dxpdf se ejecuta como un único binario: sin instalar LibreOffice, sin una imagen Docker con un entorno de escritorio completo y sin costes de API por documento.
Entornos regulados
Aplicaciones sanitarias, jurídicas y financieras donde los documentos no pueden salir de la red. dxpdf funciona completamente sin conexión y sin llamadas a servicios externos, lo que lo hace apto para despliegues aislados y on-premise.
Sistemas embebidos y edge computing
Dispositivos IoT, kioscos o contenedores ligeros donde instalar una suite ofimática de 500 MB no es práctico. Los 19 MB de memoria de dxpdf y sus tiempos de conversión por debajo del segundo lo hacen viable en entornos con recursos limitados.
Aplicaciones web en Python
Backends de Django, Flask o FastAPI que necesitan convertir archivos DOCX subidos al vuelo. Los bindings de Python envuelven el núcleo de Rust mediante PyO3, ofreciendo rendimiento nativo sin subprocesos ni servicios externos.