De HTML a PDF
Tags: javascript, html, webdev, php
Published: 2022-11-07 14:13:48

Para todo aquel que esté necesitando crear archivos PDF de forma programática en base a contenido dinámico en un navegador web o con una API, comparto mi experiencia y las conclusiones que fui sacando en base a la rápida investigación que realicé y en las prueba que hice.

Podemos decir que este problema se puede resolver de varias formas:

1. "Imprimir" la página entera que queremos transformar en PDF (del lado del cliente).
2. Utilizar una librería llamada html2pdf (del lado del cliente).
3. Utilizar una librería llamada jsPDF (del lado del cliente y del servidor)
4. Utilizar un paquete de PHP llamado DOMPDF (del lado del servidor)

Analicemos estas posibles soluciones y sus pros y contras:


## 1.- Imprimir
![Ejemplo de Mercado Libre ](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1fn9kpu995q7h694j9r0.png)

El primer enfoque quizás pueda ser el más fácil. Podemos maquetar la página con la tecnología que se nos ocurra para que luego el navegador y su opción de "Guardar como PDF" haga el resto.

Pros:

- No necesitamos instalar ninguna dependencia
- El navegador intentará "renderizar" aquello que es texto como tal (etiquetas a, p, h1, h2, etc)

Contras:

- Los sistemas operativos brindarán distintas opciones a la hora de elegir esta opción, esto significa que incluso puede no aparecer la opción "Guardar como PDF"
- Muchas veces el menú de "Imprimir" ofrecerá distintos tamaños de hoja, lo que hará que nuestra maqueta tenga que adaptarse a muchos tipos de tamaño si queremos evitar que alguien cree un archivo PDF que se rompa. Los tamaños pueden ser muy variados
- Si bien mencionamos como un punto a favor la capacidad del navegador de entender qué es un texto y renderizarlo como tal, esta funcionalidad no es perfecta. Se verá afectada por las reglas CSS que estemos usando
- El menú de imprimir no es un mini navegador, es decir que interpretará las instrucciones CSS de forma distinta que nuestra navegador, y esto incluso variará de navegador a navegador, así que hacerlo compatible requerirá de que trabajemos con muchos navegadores relevantes para nosotros
- Esta funcionalidad es del lado del cliente y requiere que estemos en la página en sí, no podemos automatizarlo de forma sencilla, quizás habría que utilizar alguna herramienta del tipo Puppeteer o Playwright o levantar un popup de html
- Requiere dos o tres clicks hasta obtener el resultado deseado lo cual puede ser molesto e incluso imposible de entender para algunos usuarios

Por último, se debe mencionar que ya que el menú de imprimir no interpretará de forma perfecta nuestro CSS, lo más posible es que tengamos que crear otro documento de CSS especialmente diseñado para imprimir. Esto no es necesariamente algo negativo, por eso no está dentro del listado de contras, pero sí que generará la necesidad de hacer algo de trabajo extra.


## 2.- html2pdf
![html2pdf](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sf1z2dyfmern2rkjc8qs.png)

Esta librería escrita en Javascript está disponible en *npmjs* (https://www.npmjs.com/package/html2pdf.js/v/0.9.0). Es en una herramienta para tomar lo que aparece en la página y exportarla a PDF. La técnica que usa es, dicho de forma coloquial, sacarle una "foto" a la página utilizando canvas, crear una imagen e incrustarla en el archivo PDF resultante.

Pros:

- Podemos usar la maqueta y tecnología que más nos guste ya que esta librería se encargará de que llegue al PDF de forma idéntica. (Hay excepciones)

Contras:

- Al ser una suerte de imagen incrustada en el archivo PDF, las piezas de nuestra composición que sean TEXTO no se renderizará como tal, con todo lo que eso implica, por ejemplo que se pixelará al hacer Zoom. (Los links sí funcionan)
- Si bien el concepto es bastante simple, se requiere entender y ajustar las opciones de configuración de esta librería para obtener el resultado deseado.
- Las imágenes deben contemplar el problema de CORS, para lo cual muchos sencillamente optan por utilizar imágenes vía base64


## 3.- jsPDF
![jsPDF](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4w2jiz1fx8ev0qnnj993.png)

Otra librería para la creación de documentos PDF, pero en este caso no solo depende de una página sino que también se puede crear un documento de forma programática utilizando la API provista. (https://parall.ax/products/jspdf).

Pros:

- Permite dos enfoques para resolver el mismo problema, lo cuál puede ser conveniente en algún caso
- Podemos usar la maqueta y tecnología que más nos guste ya que esta librería se encargará de que llegue al PDF de forma idéntica, ya que tiene la capacidad de utilizar la misma técnica de convertir la página a CANVAS para luego crear el documento deseado
- Es una librería muy utilizada y tiene buen soporte, está vigente
- Tiene una opción que intenta resolver el problema de textos cortados a la mitad cuando pasamos de una página a otra
- Se puede utilizar en Node.js

Contras:

- Si se decide utilizar la funcionalidad de crear el documento en base a la API provista, no será tan fácil crear composiciones complejas que quizás con tecnologías web sí es más sencillo de resolver


## 4.- DOMPDF

Ya que hablamos de tecnologías web, hablemos de PHP. Si necesitamos una solución similar a la de jsPDF pero del lado del servidor y con PHP, este es el paquete a elegir. Incluso tiene una adaptación para que se instale sin problemas en Laravel. Este es el sitio oficial: https://dompdf.github.io/
Este paquete puede ser utilizando con dos enfoques: Crear el documento con la API que nos provee (similar a jsPDF) ó crearlo en base a un HTML que le proveamos, no convertirá el HTML en canvas pero intentará resolver las piezas visuales  que aparezcan.

Pros:

- Permite dos enfoques para resolver el mismo problema, lo cuál puede ser conveniente en algún caso
- Es una librería muy utilizada y tiene buen soporte, está vigente
- La transformación de HTML a PDF contempla el correcto renderizado de los textos excepto para elementos de formularios (inputs, textareas, etc)
- Puede resolver el problema de textos cortados a la mitad cuando pasamos de una página a otra

Contras:

- La compatibilidad con CSS es oficialmente la de CSS 2.1 y no de CSS3, aunque es cierto que sí interpreta bien algunas cosas de CSS3.
- Si se decide utilizar la funcionalidad de crear el documento en base a la API provista, no será tan fácil crear composiciones complejas que quizás con tecnologías web sí es más sencillo de resolver

## Otras alternativas

- Browsershot https://github.com/spatie/browsershot

## Conclusión

Quizás la conclusión más sencilla sería la de utilizar jsPDF, pero eso dependerá más factores relacionados con el scope y características del proyecto en el que estamos trabajando.
Espero que este breve artículo pueda ser útil para aquel que esté necesitando una guía rápida y sencilla de esta funcionalidad.
Otro buen recurso es una pregunta que hicieron en stackoverflow (en inglés): https://stackoverflow.com/questions/18191893/generate-pdf-from-html-in-div-using-javascript
Este recurso tiene incluso algunos ejemplos de implementación y técnicas para evitar algunos problema de las soluciones antes mencionadas.