
Recientemente, en una startup, resolví el problema de generar tickets en formato PDF. En ese momento, un sitio web con una pila establecida de tecnologías ya estaba listo, por lo que estaba buscando un enfoque que no requiriera el uso de herramientas adicionales. Al final, propuse crear tickets en formato HTML primero, y luego convertirlos a PDF usando el navegador Chrome. Al final resultó que, de esta manera, puede generar no solo tickets ricamente decorados con CSS, sino también una variedad de informes con gráficos en JavaScript. En este artículo, hablaré sobre cómo iniciar Chrome para estos fines, daré algunos consejos para personalizar CSS y también discutiré las desventajas de esta solución.
Las opciones alternativas no se discutirán aquí, porque ya se ha escrito lo suficiente sobre ellas, son fáciles de encontrar y son herramientas listas para usar, información sobre la cual es mejor buscar en las fuentes principales, en la documentación de los sitios web oficiales. El método propuesto no es una herramienta independiente y se parece más a un subproducto del desarrollo de varias tecnologías. En el segmento de Internet en ruso, hay poca información recopilada, así que decidí llenar el vacío.
¿Por qué se elige esta opción?
La mayor ventaja es que Chrome no necesita expandir la pila de tecnología para generar archivos PDF. Los desarrolladores frontend crean HTML con herramientas de desarrollo familiares e inmediatamente ven los resultados intermedios del trabajo en el navegador. Al mismo tiempo, Chrome probablemente esté girando en las pruebas y transferirlo al backend no es difícil. También debe tenerse en cuenta que el codificador puede acceder a todo el arsenal de propiedades css, incluidos Flexbox y Grid.
Hablaré sobre las deficiencias y las formas de sortearlas durante el transcurso del artículo.
Resolvemos el problema en una sola línea.
En la línea de comando, llamamos a Chrome en modo sin cabeza con guardar la página en pdf:
chrome --headless --disable-gpu --print-to-pdf https://google.com
Los usuarios de Linux pueden necesitar ejecutar chrome
chromium-browser
lugar de chrome
.
Los usuarios de MAC pueden encontrar útil crear previamente un alias:
alias chrome="/Applications/Google\\ \\Chrome.app/Contents/MacOS/Google\\ \\Chrome"
ACTUALIZACIÓN: Los comentarios aclararon que los usuarios de Windows deben establecer explícitamente el nombre del archivo PDF --print-to-pdf=output.pdf
Si ya tiene un generador de documentos HTML, en lugar de https://google.com
especifique la URL para recibir este documento.
Abra el archivo output.pdf
en el directorio local y mire el resultado.
Lo primero que llama la atención es la presencia de un encabezado con una fecha de impresión y un pie de página con una URL y paginación. Para eliminarlos, debe agregar algunas reglas CSS. Es poco probable que se agreguen estas reglas a google.com
, por lo que para un trabajo posterior es mejor crear su propio documento HTML.
Agregar CSS
CSS tiene una consulta especial de medios @page
, que se usa para imprimir; configuraremos una sangría para que el Encabezado y el Pie de página simplemente no encajen:
@page { size: A4; margin: 0mm; }
Este método solo funcionará para documentos de una sola página, al imprimir dos o más páginas, el pie de página con la URL y la numeración de la página permanecerán en la parte inferior inferior. Puede solicitar explícitamente a Chrome que desactive la visualización de Encabezado y Pie de página configurando el parámetro de impresión displayHeaderFooter = False
, pero en este momento no se mueve a la interfaz de línea de comandos. Para lograrlo, necesitará herramientas para automatizar el trabajo con el navegador: Selenium o titiritero. A continuación, consideraré la primera opción, porque mi proyecto usó Python.
Inicie Chrome a través de Selenium
Por lo tanto, instale Selenium con el comando pip install selenium
, descargue el controlador de Chrome que coincida con su versión de Chrome desde http://chromedriver.chromium.org/ y use la función get_pdf_from_html
del siguiente ejemplo:
import sys from selenium import webdriver from selenium.webdriver.chrome.options import Options import json, base64 def get_pdf_from_html(path, chromedriver='./chromedriver', print_options = {}):
Para obtener un archivo PDF, puede ejecutar este ejemplo desde la línea de comando que especifica la url y el nombre del archivo para guardar el PDF, o llamar a la función get_pdf_from_html
y pasarle tres argumentos:
- ruta - url del documento html;
- chromedriver: la ruta en la máquina local al controlador de Chrome (de manera predeterminada, debe estar en el directorio local);
- opciones_impresión: atributos de impresión adicionales.
Cabe señalar que Selenium no tiene una interfaz estándar para imprimir una página en PDF, y solo Chrome puede hacer esto, por lo que debe llamar directamente a driver.command_executor._request
.
Ahora veamos qué herramientas están disponibles para controlar la ubicación del contenido en documentos de varias páginas.
Tipografía CSS
Al imprimir a doble cara, puede establecer diferentes márgenes desde el borde para las páginas derecha e izquierda individualmente si planea unirlas en el futuro:
@page :left { margin-left: 4cm; margin-right: 2cm; } @page :right { margin-left: 4cm; margin-right: 2cm; }
Para la primera página, puede especificar su propio diseño, por ejemplo, una sangría aumentada desde el borde superior:
@page :first { margin-top: 10cm }
Es posible establecer el salto de página antes del encabezado de primer nivel para que comience en una página impar:
h1 { page-break-before : right }
Usando la propiedad de page-break-after
, puede evitar un salto de página inmediatamente después de algún elemento, por ejemplo, un encabezado de segundo nivel:
h2 { page-break-after : avoid }
La propiedad de page-break-inside
ayuda a evitar saltos de página donde no es deseable hacer esto, por ejemplo, en el medio de una tabla
table { page-break-inside : avoid }
Los orphans
y las orphans
ayudarán a evitar los saltos de página al principio y al final de un párrafo:
@page { orphans:4; widows:2; }
¿Qué pasa con el rendimiento?
En un Core i5-8600K 3600MHz en un flujo, una simple conversión de documentos lleva 0.6 segundos. En mi máquina de escribir portátil de finales de 2013, 2,4 GHz - 1,5 segundos.
Obviamente, los principales recursos se gastan en iniciar el navegador. Puede reducir el tiempo de conversión para una gran cantidad de archivos si ejecuta Chrome una vez como microservicio y le envía una URL para la conversión. La implementación de este método está más allá del alcance de este artículo.
¿Qué más está mal?
Veo dos problemas principales:
- La imposibilidad de simplemente determinar la posición de los elementos en un documento. Esto dificulta la creación de una tabla de contenido con indicación automática de los números de página, especialmente si el tamaño del contenido no se conoce de antemano.
- La conversión de Chrome es el producto de Google, que recopila una variedad de información sobre los usuarios. Si la fuga de datos del documento es inaceptable, debe tener cuidado con la solución propuesta: cierre el navegador con acceso a recursos externos o incluso busque otra solución. El uso de código abierto Chromium no resuelve el problema: ya se han encontrado errores de Google.
Conclusiones
Propongo sacar conclusiones sobre la admisibilidad de utilizar este enfoque por mi cuenta. Cada proyecto es único a su manera. Si este método es adecuado en su proyecto, depende de usted.