Caso de estudio · pdfcn-rs
pdfcn-rs: PDFs como componentes, sin navegador
Plantillas tipo HAML, componentes al estilo shadcn y utilidades tipo Tailwind compiladas a PDF en memoria, en Rust puro y dentro de una función serverless.
El problema
Generar facturas, catálogos o informes en PDF suele acabar en una de dos cosas: dibujar coordenadas a mano con una librería de bajo nivel, o arrancar un Chromium sin cabeza para imprimir HTML. Lo primero es lento de mantener; lo segundo pesa cientos de megas, tarda en arrancar y encaja mal en una función serverless.
Quería escribir un PDF como se escribe una interfaz: con componentes y clases de utilidad, y generarlo dentro de una Vercel Function o una Lambda.
Decisiones
Sin Chromium. El HTML y el CSS se maquetan con un motor de layout escrito en Rust, sin proceso de navegador ni dependencias dinámicas del sistema.
Un pipeline de piezas pequeñas. Un parser de plantillas con indentación tipo HAML, un motor de expresiones para {{ }}, - for e - if, un registro de componentes (%InvoiceTable, %Card, %Badge…) que se expanden a marcado, y un escáner de utilidades tipo Tailwind sin Node. Cada crate tiene una responsabilidad.
Escapar una sola vez, en el borde. Las plantillas resuelven datos sin escapar, y el escapado ocurre al renderizar el HTML final. Así no hay dobles escapados ni huecos.
Pensado para imprimir. Se inyectan @page, color exacto y break-inside: avoid en filas y tarjetas por defecto.
Qué hay construido
- CLI con
new,add,buildydev(vista previa con recarga en vivo). - Gráficos, códigos de barras y SVG rasterizados a resolución de impresión.
- Facturas Factur-X: el mismo PDF lleva incrustado el XML EN 16931 que lee el sistema contable del cliente.
- Una función de Vercel en
/api/generate-pdf, bindings para Node con napi-rs y una demo.
Lo que no hace (todavía)
El repositorio documenta sus límites con detalle: gap en flex y grid no tiene efecto, box-shadow no se pinta y las cabeceras y pies de página de la librería de PDF aún no se dibujan. Prefiero dejarlos escritos que descubrirlos en producción.
Lo que aprendí
El modelo mental de componentes y utilidades funciona igual de bien en papel que en pantalla, y quitar el navegador del camino cambia lo que es posible en serverless. Documentar los límites con la misma precisión que las funciones ahorra más tiempo que cualquier feature.