[{"data":1,"prerenderedAt":553},["ShallowReactive",2],{"article__generer-un-pdf-avec-nodejs-et-puppeteer":3},{"id":4,"title":5,"author":6,"body":7,"date":540,"description":24,"extension":541,"img":542,"meta":543,"navigation":149,"path":544,"seo":545,"stem":546,"tags":547,"__hash__":552},"article/article/generer-un-pdf-avec-nodejs-et-puppeteer.md","Générer un PDF avec Node.js et Puppeteer à partir d'une page web",null,{"type":8,"value":9,"toc":533},"minimark",[10,15,19,25,30,42,46,58,88,95,99,104,438,442,449,455,497,501,516,529],[11,12,14],"h1",{"id":13},"générer-un-pdf-avec-nodejs-et-puppeteer","Générer un PDF avec Node.js et Puppeteer",[16,17,18],"p",{},"Vous en avez marre de maintenir votre script de génération de fiche produit PDF (ou votre CV), générez les directement à partir de votre site avec Node.js et Puppeteer.",[20,21,22],"blockquote",{},[16,23,24],{},"Pire que la mise en page d'un fichier Word, la mise en page d'un PDF généré par un script... Simplifiez-vous la vie en convertissant directement vos pages web en PDF.",[26,27,29],"h2",{"id":28},"introduction","Introduction",[16,31,32,33,37,38,41],{},"Nous allons voir ici comment créer un ",[34,35,36],"strong",{},"script Node.js"," qui vous permettra de ",[34,39,40],{},"générer un PDF à partir d'une page de votre site",".\nLe script tournera en local, mais vous pouvez l'intégrer de manière automatique dans votre CI/CD.",[26,43,45],{"id":44},"installation","Installation",[16,47,48,49,57],{},"La seule dépendance dont vous avez besoin, ",[50,51,56],"a",{"href":52,"target":53,"className":54},"https://pptr.dev/","_blank",[55],"ypk__external-link","Puppeteer"," qui est un navigateur headless (ou plutôt, il contrôle votre navigateur).",[59,60,65],"pre",{"className":61,"code":62,"language":63,"meta":64,"style":64},"language-bash shiki shiki-themes monokai","npm install -D puppeteer\n","bash","",[66,67,68],"code",{"__ignoreMap":64},[69,70,73,77,81,85],"span",{"class":71,"line":72},"line",1,[69,74,76],{"class":75},"sHkqI","npm",[69,78,80],{"class":79},"s_Ekj"," install",[69,82,84],{"class":83},"s7s5_"," -D",[69,86,87],{"class":79}," puppeteer\n",[16,89,90],{},[34,91,94],{"className":92},[93],"ypk--error--no-background","Attention si vous utilisez PhantomJS ! La librairie était couramment utilisée, mais elle est officiellement stoppée car elle génére de trop grosses failles de sécurité.",[26,96,98],{"id":97},"le-script-complet","Le script complet",[16,100,101],{},[66,102,103],{},"tools/pdf-generator.js",[59,105,109],{"className":106,"code":107,"language":108,"meta":64,"style":64},"language-js shiki shiki-themes monokai","// Chargement des dépendances\nconst puppeteer = require('puppeteer');\n\n(async () => {\n    // Lancement de Puppeteer\n    const browser = await puppeteer.launch({\n        headless: true\n    });\n    \n    // Ouverture et navigation vers la page à convertir\n    const page = await browser.newPage();\n    await page.goto('http://localhost/ma-super-page-a-convertir', {waitUntil: 'networkidle2'});\n    \n    // Génération du PDF\n    await page.pdf({\n        // Chemin vers le PDF relatif à la racine de votre projet\n        path: 'src/pdf/mon-super-pdf.pdf',\n        format: 'A4',\n        // N'oubliez pas de forcer le printBackground à true\n        // Car il est possible que dans votre style CSS cela soit l'inverse pour plus d'éfficacité\n        // Mais là on veut du beau\n        printBackground: true,\n        margin: {\n            top: '0.5cm',\n            right: '0.5cm',\n            bottom: '0.5cm',\n            left: '0.5cm'\n        }\n    });\n\n    // Fermeture du navigateur et fin du script\n    await browser.close();\n})();\n","js",[66,110,111,117,144,151,168,174,197,206,212,218,224,245,271,276,282,294,300,312,323,329,335,341,352,358,369,379,389,398,404,409,414,420,432],{"__ignoreMap":64},[69,112,113],{"class":71,"line":72},[69,114,116],{"class":115},"snpHw","// Chargement des dépendances\n",[69,118,120,124,128,132,135,138,141],{"class":71,"line":119},2,[69,121,123],{"class":122},"sOx1s","const",[69,125,127],{"class":126},"sCdxs"," puppeteer ",[69,129,131],{"class":130},"s8I7P","=",[69,133,134],{"class":75}," require",[69,136,137],{"class":126},"(",[69,139,140],{"class":79},"'puppeteer'",[69,142,143],{"class":126},");\n",[69,145,147],{"class":71,"line":146},3,[69,148,150],{"emptyLinePlaceholder":149},true,"\n",[69,152,154,156,159,162,165],{"class":71,"line":153},4,[69,155,137],{"class":126},[69,157,158],{"class":130},"async",[69,160,161],{"class":126}," () ",[69,163,164],{"class":122},"=>",[69,166,167],{"class":126}," {\n",[69,169,171],{"class":71,"line":170},5,[69,172,173],{"class":115},"    // Lancement de Puppeteer\n",[69,175,177,180,183,185,188,191,194],{"class":71,"line":176},6,[69,178,179],{"class":122},"    const",[69,181,182],{"class":126}," browser ",[69,184,131],{"class":130},[69,186,187],{"class":130}," await",[69,189,190],{"class":126}," puppeteer.",[69,192,193],{"class":75},"launch",[69,195,196],{"class":126},"({\n",[69,198,200,203],{"class":71,"line":199},7,[69,201,202],{"class":126},"        headless: ",[69,204,205],{"class":83},"true\n",[69,207,209],{"class":71,"line":208},8,[69,210,211],{"class":126},"    });\n",[69,213,215],{"class":71,"line":214},9,[69,216,217],{"class":126},"    \n",[69,219,221],{"class":71,"line":220},10,[69,222,223],{"class":115},"    // Ouverture et navigation vers la page à convertir\n",[69,225,227,229,232,234,236,239,242],{"class":71,"line":226},11,[69,228,179],{"class":122},[69,230,231],{"class":126}," page ",[69,233,131],{"class":130},[69,235,187],{"class":130},[69,237,238],{"class":126}," browser.",[69,240,241],{"class":75},"newPage",[69,243,244],{"class":126},"();\n",[69,246,248,251,254,257,259,262,265,268],{"class":71,"line":247},12,[69,249,250],{"class":130},"    await",[69,252,253],{"class":126}," page.",[69,255,256],{"class":75},"goto",[69,258,137],{"class":126},[69,260,261],{"class":79},"'http://localhost/ma-super-page-a-convertir'",[69,263,264],{"class":126},", {waitUntil: ",[69,266,267],{"class":79},"'networkidle2'",[69,269,270],{"class":126},"});\n",[69,272,274],{"class":71,"line":273},13,[69,275,217],{"class":126},[69,277,279],{"class":71,"line":278},14,[69,280,281],{"class":115},"    // Génération du PDF\n",[69,283,285,287,289,292],{"class":71,"line":284},15,[69,286,250],{"class":130},[69,288,253],{"class":126},[69,290,291],{"class":75},"pdf",[69,293,196],{"class":126},[69,295,297],{"class":71,"line":296},16,[69,298,299],{"class":115},"        // Chemin vers le PDF relatif à la racine de votre projet\n",[69,301,303,306,309],{"class":71,"line":302},17,[69,304,305],{"class":126},"        path: ",[69,307,308],{"class":79},"'src/pdf/mon-super-pdf.pdf'",[69,310,311],{"class":126},",\n",[69,313,315,318,321],{"class":71,"line":314},18,[69,316,317],{"class":126},"        format: ",[69,319,320],{"class":79},"'A4'",[69,322,311],{"class":126},[69,324,326],{"class":71,"line":325},19,[69,327,328],{"class":115},"        // N'oubliez pas de forcer le printBackground à true\n",[69,330,332],{"class":71,"line":331},20,[69,333,334],{"class":115},"        // Car il est possible que dans votre style CSS cela soit l'inverse pour plus d'éfficacité\n",[69,336,338],{"class":71,"line":337},21,[69,339,340],{"class":115},"        // Mais là on veut du beau\n",[69,342,344,347,350],{"class":71,"line":343},22,[69,345,346],{"class":126},"        printBackground: ",[69,348,349],{"class":83},"true",[69,351,311],{"class":126},[69,353,355],{"class":71,"line":354},23,[69,356,357],{"class":126},"        margin: {\n",[69,359,361,364,367],{"class":71,"line":360},24,[69,362,363],{"class":126},"            top: ",[69,365,366],{"class":79},"'0.5cm'",[69,368,311],{"class":126},[69,370,372,375,377],{"class":71,"line":371},25,[69,373,374],{"class":126},"            right: ",[69,376,366],{"class":79},[69,378,311],{"class":126},[69,380,382,385,387],{"class":71,"line":381},26,[69,383,384],{"class":126},"            bottom: ",[69,386,366],{"class":79},[69,388,311],{"class":126},[69,390,392,395],{"class":71,"line":391},27,[69,393,394],{"class":126},"            left: ",[69,396,397],{"class":79},"'0.5cm'\n",[69,399,401],{"class":71,"line":400},28,[69,402,403],{"class":126},"        }\n",[69,405,407],{"class":71,"line":406},29,[69,408,211],{"class":126},[69,410,412],{"class":71,"line":411},30,[69,413,150],{"emptyLinePlaceholder":149},[69,415,417],{"class":71,"line":416},31,[69,418,419],{"class":115},"    // Fermeture du navigateur et fin du script\n",[69,421,423,425,427,430],{"class":71,"line":422},32,[69,424,250],{"class":130},[69,426,238],{"class":126},[69,428,429],{"class":75},"close",[69,431,244],{"class":126},[69,433,435],{"class":71,"line":434},33,[69,436,437],{"class":126},"})();\n",[26,439,441],{"id":440},"lancement-du-script","Lancement du script",[16,443,444,445,448],{},"Pour que le script fonctionne, ",[34,446,447],{},"il faut que votre site soit lancé et l'URL en localhost accessible",".",[16,450,451,452,448],{},"Pour plus de praticité, pensez à ajouter le script dans votre ",[66,453,454],{},"package.json",[59,456,460],{"className":457,"code":458,"language":459,"meta":64,"style":64},"language-json shiki shiki-themes monokai","{\n    \"scripts\": {\n        \"pdf:build\": \"node tools/pdf-generator.js\"\n    }\n}\n","json",[66,461,462,467,475,487,492],{"__ignoreMap":64},[69,463,464],{"class":71,"line":72},[69,465,466],{"class":126},"{\n",[69,468,469,472],{"class":71,"line":119},[69,470,471],{"class":122},"    \"scripts\"",[69,473,474],{"class":126},": {\n",[69,476,477,480,483],{"class":71,"line":146},[69,478,479],{"class":122},"        \"pdf:build\"",[69,481,482],{"class":126},": ",[69,484,486],{"class":485},"susgL","\"node tools/pdf-generator.js\"\n",[69,488,489],{"class":71,"line":153},[69,490,491],{"class":126},"    }\n",[69,493,494],{"class":71,"line":170},[69,495,496],{"class":126},"}\n",[26,498,500],{"id":499},"conclusion","Conclusion",[16,502,503,504,507,508,511,512,515],{},"Avec ceci, vous avez la possibilité de faire ",[34,505,506],{},"de beaux PDFs"," rapidement, ",[34,509,510],{},"automatiquement mis à jour"," et ne nécessitant ",[34,513,514],{},"pas une énième source de données"," et d'erreur.",[16,517,518,519,448,525,528],{},"Le style de vos PDFs sera logiquement ",[34,520,521,522],{},"basé sur votre feuille de style avec la media query ",[66,523,524],{},"print",[526,527],"br",{},"\nC'est parfait, vous avez déjà cela d'implémenté sur votre site... N'est-ce pas ???",[530,531,532],"style",{},"html pre.shiki code .sHkqI, html code.shiki .sHkqI{--shiki-default:#A6E22E}html pre.shiki code .s_Ekj, html code.shiki .s_Ekj{--shiki-default:#E6DB74}html pre.shiki code .s7s5_, html code.shiki .s7s5_{--shiki-default:#AE81FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .snpHw, html code.shiki .snpHw{--shiki-default:#88846F}html pre.shiki code .sOx1s, html code.shiki .sOx1s{--shiki-default:#66D9EF;--shiki-default-font-style:italic}html pre.shiki code .sCdxs, html code.shiki .sCdxs{--shiki-default:#F8F8F2}html pre.shiki code .s8I7P, html code.shiki .s8I7P{--shiki-default:#F92672}html pre.shiki code .susgL, html code.shiki .susgL{--shiki-default:#CFCFC2}",{"title":64,"searchDepth":119,"depth":119,"links":534},[535,536,537,538,539],{"id":28,"depth":119,"text":29},{"id":44,"depth":119,"text":45},{"id":97,"depth":119,"text":98},{"id":440,"depth":119,"text":441},{"id":499,"depth":119,"text":500},"2024/10/21","md","/pictures/article/generer-un-pdf-avec-nodejs-et-puppeteer.jpg",{},"/article/generer-un-pdf-avec-nodejs-et-puppeteer",{"title":5,"description":24},"article/generer-un-pdf-avec-nodejs-et-puppeteer",[548,549,550,551],"article","technique","backend","nodejs","Y9V1HQZm7OIq5g99HtrhWycyAEaJ3YP2QKjEk1gz8pI",1787177915184]