Convertir une page html en pdf avec Puppeteer et Node
Quand vient le temps de générer un PDF dans une application, c’est toujours compliqué. Il existe des solutions complètes comme Adobe LiveCyle ou Jasper Report, comme il existe d’autres solutions plus compliquées permettant de construire un PDF en écrivant du code, comme PDFKit par exemple. J’aimerais par contre m’attarder aujourd’hui sur une librairie qu’un collègue m’a présentée en solution à problème que nous avions dans une de nos applications. Nous allons donc voir ensemble Puppeteer pour générer un PDF à partir d’un code en html.
Qu’est-ce que Puppeteer
Puppeteer est une librairie pour node qui permet de contrôler Chrome ou Chromium via le protocol DevTools en mode headless. Elle permet, par exemple, d’accéder à une page web via son url, puis de rendre (render) la page en mémoire pour faire une capture d’écran. Dans cet article, je vais utiliser Puppeteer pour créer une page web vide, lui injecter du code html et générer un PDF à partir de la page web en mémoire.
Création du projet node
Quelque part sur votre machine, créez le projet node :
1 2 3 4 |
mkdir puppeteer-demo cd puppeteer-demo npm init -y npm i puppeteer |
Créez ensuite un fichier index.html qui contiendra le code du PDF à générer. Je vous laisse un lorem ipsum à copier coller si vous manquez d’inspriration :
1 2 3 4 5 6 7 8 9 10 11 |
<!DOCTYPE html> <head> <title>Example</title> </head> <body> <h1>Titre du document</h1> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse et lectus commodo, imperdiet arcu ut, pellentesque lectus. Quisque venenatis, enim non pellentesque consequat, lacus sem posuere neque, in laoreet nunc neque ut enim. Maecenas nibh justo, accumsan sed vulputate eget, placerat vel ipsum. Curabitur vitae dui eu dolor molestie lobortis eu a felis. Curabitur ac odio pellentesque, tincidunt nibh sed, aliquam augue. Ut aliquam metus in augue mattis, vitae viverra nunc efficitur. Donec scelerisque ultricies felis eu porta. Sed aliquam mauris eu nibh eleifend imperdiet. Sed ac justo ac lorem rutrum interdum a a mauris. Nulla ut sapien ullamcorper sem sodales placerat id vel risus.</p> <p>Pellentesque gravida sagittis nulla sit amet vehicula. Vivamus ultrices sem odio, ac vulputate mauris sodales at. Suspendisse blandit tortor ac augue hendrerit, sit amet finibus odio maximus. Curabitur egestas purus vitae nunc semper, vitae accumsan est scelerisque. Aenean ut luctus ex, commodo bibendum est. Fusce commodo lobortis felis, quis imperdiet elit aliquam vulputate. Vivamus lobortis, eros a imperdiet lobortis, sem orci porta nisl, sit amet lacinia dolor nibh id velit. Nunc accumsan placerat eros a laoreet. Fusce volutpat volutpat odio eget sodales. Mauris lacus purus, aliquam ac efficitur hendrerit, finibus ut est. Donec tempus ipsum venenatis, fringilla enim vel, porta nunc. Interdum et malesuada fames ac ante ipsum primis in faucibus. Fusce quis tellus a turpis feugiat tempor ac nec nisl.</p> <p>Nunc sed libero pharetra, ornare nibh eu, pulvinar leo. Donec egestas at dui non hendrerit. Interdum et malesuada fames ac ante ipsum primis in faucibus. Cras dapibus enim in aliquam interdum. Fusce pretium urna nec elementum vulputate. Vivamus malesuada fringilla magna sed ultrices. Suspendisse malesuada nisi sit amet est dignissim pharetra. Vivamus fermentum diam vitae congue porttitor. Donec ullamcorper ornare convallis. Integer non facilisis felis. Nulla ligula orci, vulputate at fermentum eu, dignissim nec diam. Aliquam accumsan ipsum in arcu tempus, vitae blandit velit hendrerit. Vestibulum pulvinar ac ligula nec rhoncus. Sed venenatis ipsum sed mauris auctor malesuada. Vivamus at ligula ut elit dictum tincidunt.</p> </body> </html> |
Puis, créer un nouveau fichier index.js qu’on ajoutera aussi au script de démarrage npm dans le fichier package.json :
1 |
"start": "node index.js" |
Pour le plaisir :
1 |
console.log('hello world!'); |
Résultat :
1 2 |
npm start hello world! |
Yes! Je sais encore coder 😅
Le service de génération de PDF
Pour les besoins de la cause, je vais créer un module de génération de PDF. Ceci nous pemettra de respecter un minimum le principe de responsabilité unique dans notre exemple. Créez un nouveau fichier et ajoutez :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
const puppeteer = require('puppeteer'); const path = require('path'); const fs = require('fs'); module.exports = { convertHtmlFileToPdf: async (htmlFilePath, pdfFileDestination) => { const sourcePath = path.join(__dirname, htmlFilePath); const destinationPath = path.join(__dirname, pdfFileDestination); const htmlContent = fs.readFileSync(sourcePath).toString(); const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.setContent(htmlContent); await page.pdf({ path: destinationPath, format: 'a4' }); await browser.close(); } } |
Simple, non? On lance Puppeteer, on créer une nouvelle page, on lui injecte notre code HTML et on sauvegarde le fichier PDF. Pour utiliser ce module, créons un fichier index.js avec le contenu suivant :
1 |
(async () => {})(); |
Cette bout de code peut paraitre étrange pour les gens qui ne connaissent pas trop JS, mais ça s’appelle « une fonction asynchrone auto-exécutée ». On pourrait voir ça comme un genre de main() dans d’autres langages.
Reste plus qu’à charger notre module et appeler la fonction de conversion pour tester :
1 2 3 4 5 6 7 |
const pdfGenerationService = require('./pdf-generation.service'); (async () => { const source = './index.html'; const destination = './index.pdf'; pdfGenerationService.convertHtmlFileToPdf(source, destination); })(); |
Conclusion
Aujourd’hui, vous avez vu comment on peut utiliser un navigateur sans tête pour générer un PDF assez facilement à partir d’un code html. Dans notre cas, nous avons utilisé cette fonctionnalité couplée avec un éditeur WYSIWYG (Quill) pour permettre à nos utilisateur de créer un formulaire de consentement en PDF à partir d’une application web.
Merci de nous lire chaque semaine 🙂 <3
Commentaires
Laisser un commentaire