Comprendre Google Chrome Convertir les fonctionnalités HTML en PDF


Récemment, dans une startup, j'ai résolu le problème de génération de tickets au format PDF. À cette époque, un site Web avec une pile de technologies établie était déjà prêt, donc je cherchais une approche qui ne nécessiterait pas l'utilisation d'outils supplémentaires. Au final, j'ai proposé de créer d'abord des tickets au format HTML, puis de les convertir au format PDF à l'aide du navigateur Chrome. Il s'est avéré que cette méthode peut générer non seulement des tickets richement décorés en CSS, mais également une variété de rapports avec des graphiques en JavaScript. Dans cet article, je vais vous expliquer comment lancer Chrome à ces fins, donner quelques conseils pour personnaliser le CSS et discuter également des inconvénients de cette solution.


Les options alternatives ne seront pas discutées ici, car suffisamment de choses ont déjà été écrites dessus, elles sont faciles à trouver et ce sont des outils prêts à l'emploi, dont il vaut mieux rechercher les informations dans les sources primaires - dans la documentation sur les sites officiels. La méthode proposée n'est pas un outil indépendant et ressemble davantage à un sous-produit du développement de plusieurs technologies. Dans le segment Internet de langue russe, il y a peu d'informations recueillies à ce sujet, j'ai donc décidé de combler le vide.


Pourquoi cette option est-elle choisie?


Le plus grand avantage est que Chrome n'a pas besoin d'étendre la pile technologique pour générer des PDF. Les développeurs frontaux créent du HTML avec des outils de développement familiers et voient immédiatement les résultats intermédiaires du travail dans le navigateur. Dans le même temps, Chrome tourne probablement dans les tests et le transférer vers le backend n'est pas difficile. Il convient également de noter que le codeur est en mesure d'accéder à l'intégralité de l'arsenal des propriétés CSS, notamment Flexbox et Grid.
Je parlerai des lacunes et des moyens de les contourner au cours de l'article.


Nous résolvons le problème en une seule ligne


Sur la ligne de commande, nous appelons Chrome en mode sans tête avec enregistrement de la page en pdf:


chrome --headless --disable-gpu --print-to-pdf https://google.com 

Les utilisateurs de Linux peuvent avoir besoin d'exécuter chromium-browser au lieu de chrome .
Les utilisateurs MAC peuvent trouver utile de pré-créer un alias:


 alias chrome="/Applications/Google\\ \\Chrome.app/Contents/MacOS/Google\\ \\Chrome" 

MISE À JOUR: Les commentaires ont précisé que les utilisateurs de Windows doivent définir explicitement le nom du fichier PDF --print-to-pdf=output.pdf


Si vous disposez déjà d'un générateur de document HTML, au lieu de https://google.com spécifiez l'URL pour recevoir ce document.


Ouvrez le fichier output.pdf dans le répertoire local et regardez le résultat.
La première chose qui attire votre attention est la présence d'un en-tête avec une date d'impression et d'un pied de page avec une URL et une pagination. Pour les supprimer, vous devez ajouter quelques règles CSS. Il est peu probable que ces règles soient ajoutées à google.com . Par conséquent, pour un travail ultérieur, il est préférable de créer votre propre document HTML.


Ajouter CSS


CSS a une requête média spéciale @page , qui est utilisée pour l'impression; nous allons définir une indentation afin que l'en-tête et le pied de page ne correspondent tout simplement pas:


 @page { size: A4; margin: 0mm; } 

Cette méthode ne fonctionnera que pour les documents d'une seule page, lors de l'impression de deux pages ou plus, le pied de page avec l'URL et la numérotation des pages restera en bas en bas. Vous pouvez explicitement demander à Chrome de désactiver l'affichage de l'en-tête et du pied de page en définissant le paramètre d'impression displayHeaderFooter = False , mais pour le moment, il n'est pas déplacé vers l'interface de ligne de commande. Pour y accéder, vous aurez besoin d'outils pour automatiser le travail avec le navigateur: Sélénium ou marionnettiste. Ensuite, je considérerai la première option, car mon projet utilisait Python.


Lancer Chrome via Selenium


Donc, installez Selenium avec la commande pip install selenium , téléchargez le pilote chrome qui correspond à votre version de Chrome à partir de http://chromedriver.chromium.org/ et utilisez la fonction get_pdf_from_html de l'exemple ci-dessous:


 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 = {}): #  Chrome webdriver_options = Options() webdriver_options.add_argument('--headless') webdriver_options.add_argument('--disable-gpu') driver = webdriver.Chrome(chromedriver, options=webdriver_options) #   url driver.get(path) #    calculated_print_options = { 'landscape': False, 'displayHeaderFooter': False, 'printBackground': True, 'preferCSSPageSize': True, } calculated_print_options.update(print_options) #    pdf  result = send_devtools(driver, "Page.printToPDF", calculated_print_options) driver.quit() #    base64 -  return base64.b64decode(result['data']) def send_devtools(driver, cmd, params={}): resource = "/session/%s/chromium/send_command_and_get_result" % driver.session_id url = driver.command_executor._url + resource body = json.dumps({'cmd': cmd, 'params': params}) response = driver.command_executor._request('POST', url, body) if response['status']: raise Exception(response.get('value')) return response.get('value') if __name__ == "__main__": if len(sys.argv) != 3: print ("usage: converter.py <html_page_sourse> <filename_to_save>") exit() result = get_pdf_from_html(sys.argv[1]) with open(sys.argv[2], 'wb') as file: file.write(result) 

Pour obtenir un fichier PDF, vous pouvez exécuter cet exemple à partir de la ligne de commande en spécifiant l'URL et le nom de fichier pour enregistrer le PDF, ou appeler la fonction get_pdf_from_html et lui passer trois arguments:


  1. chemin - URL du document html;
  2. chromedriver - le chemin sur la machine locale vers le pilote chrome (par défaut, il doit être dans le répertoire local);
  3. print_options - attributs d'impression supplémentaires.

Il convient de noter que Selenium ne possède pas d'interface standard pour imprimer une page au format PDF, et seul Chrome peut le faire, vous devez donc appeler directement driver.command_executor._request .


Voyons maintenant quels outils sont disponibles pour contrôler le placement du contenu sur les documents de plusieurs pages.


Typographie CSS


Lors de l'impression recto verso, vous pouvez définir des marges différentes à partir du bord pour les pages droite et gauche individuellement si vous prévoyez de les assembler à l'avenir:


 @page :left { margin-left: 4cm; margin-right: 2cm; } @page :right { margin-left: 4cm; margin-right: 2cm; } 

Pour la première page, vous pouvez spécifier votre propre conception, par exemple, un retrait accru à partir du bord supérieur:


 @page :first { margin-top: 10cm /* Top margin on first page 10cm */ } 

Il est possible de définir le saut de page avant l'en-tête de premier niveau afin qu'il commence sur une page impaire:


 h1 { page-break-before : right } 

En utilisant la propriété page-break-after , vous pouvez empêcher un saut de page immédiatement après un élément, par exemple, un en-tête de deuxième niveau:


 h2 { page-break-after : avoid } 

La propriété page-break-inside permet d'éviter les sauts de page où il n'est pas souhaitable de le faire, par exemple au milieu d'un tableau


 table { page-break-inside : avoid } 

Les orphans orphans et orphans permettront d'éviter les sauts de page au début et à la fin d'un paragraphe:


 @page { orphans:4; widows:2; } 

Et la performance?


Sur un Core i5-8600K 3600 MHz en un seul flux, une simple conversion de document prend 0,6 seconde. Sur ma machine à écrire portable fin 2013, 2,4 GHz - 1,5 seconde.
De toute évidence, les principales ressources sont consacrées au lancement du navigateur. Vous pouvez réduire le temps de conversion d'un grand nombre de fichiers si vous exécutez Chrome une fois en tant que microservice et lui envoyez une URL pour la conversion. L'implémentation de cette méthode dépasse le cadre de cet article.


Qu'est-ce qui ne va pas?


Je vois deux problèmes principaux:


  1. L'impossibilité de déterminer simplement la position des éléments dans un document. Cela rend difficile la création d'une table des matières avec indication automatique des numéros de page, surtout si la taille du contenu n'est pas connue à l'avance.
  2. La conversion de Chrome est le produit de Google, qui recueille une variété d'informations sur les utilisateurs. Si la fuite de données du document est inacceptable, vous devez faire attention à la solution proposée - fermez le navigateur avec des ressources externes, ou même recherchez une autre solution. L'utilisation de Chromium open source ne résout pas le problème - des bogues de Google y ont déjà été trouvés.

Conclusions


Je propose de tirer des conclusions sur la recevabilité de l'utilisation de cette approche par moi-même. Chaque projet est unique à sa manière. C'est à vous de décider si cette méthode convient à votre projet.

Source: https://habr.com/ru/post/fr459112/


All Articles