J'essaie d'utiliser Google Chrome en remplacement de PhantomJS pour rendre HTML au format PDF. Jusqu'ici, ça a bien fonctionné pour moi. Le seul problème que je connaisse et pour lequel je n’ai trouvé aucun moyen de traduire le code PhantomJS suivant:
page.paperSize = {
footer: {
contents: phantom.callback(function(pageNum, numPages) {
return "Power by MyWebsite. Created on "+formatDate(new Date())+"<span style='float:right'>" + pageNum + " / " + numPages + "</span>";
})
}
}
La date de formatage a la même fonction que dans la question Comment formater une date JavaScript
Cependant, je n'ai pas trouvé de moyen de reproduire ce comportement dans Google Chrome sans tête. J'utilise l'interface à distance Chrome (CDP) de https://github.com/cyrus-and/chrome-remote-interface
Voici un aperçu de mon code d'interface distante chrome:
return new Promise(async function (resolve, reject) {
const url = "<MyURL here>";
const [tab] = await Cdp.List()
const client = await Cdp({ Host: '127.0.0.1', target: tab });
await Promise.all([
Network.enable(),
Page.enable()
]);
Page.loadEventFired(function () {
setTimeout(function () {
resolve(Page.printToPDF({displayHeaderFooter:true}))); //https://chromedevtools.github.io/devtools-protocol/tot/Page/#method-printToPDF
}, 3000);
});
await Page.navigate({ url });
};
Je reçois bien le PDF, mais je ne peux obtenir que les en-têtes et les pieds de page par défaut de Chrome. Un moyen de les modifier?
Note: je me rends compte que je peux utiliser JavaScript dans ma page pour ajouter des éléments au bas de chaque page, mais je préférerais modifier le pied de page ajouté par le navigateur lors de l'exportation/impression, comme je l'ai trouvé. Il est beaucoup plus fiable de se placer correctement et sans provoquer d'étranges rediffusions des autres divs dans la page.
Ceci est une mise à jour/réponse à la question. A partir du chrome 64, il est possible d'utiliser les paramètres headerTemplate
et footerTemplate
à printToPDF
Utilisation de interface distante chrome voici un exemple de code qui devrait fonctionner:
return new Promise(async function (resolve, reject) {
const url = "<MyURL here>";
const [tab] = await Cdp.List()
const client = await Cdp({ Host: '127.0.0.1', target: tab });
await Promise.all([
Network.enable(),
Page.enable()
]);
Page.loadEventFired(function () {
setTimeout(function () {
//https://chromedevtools.github.io/devtools-protocol/tot/Page/#method-printToPDF
resolve(Page.printToPDF({
displayHeaderFooter:true,
footerTemplate: "<span class='pageNumber'> of <span class='totalPages'>"
})));
}, 3000);
});
await Page.navigate({ url });
};
Il est possible de créer des en-têtes et des pieds de page personnalisés en utilisant les balises <header>
et <footer>
. J'utilise ceci pour générer des PDF à l'aide de Chrome Headless. Je ne l'ai pas testé dans Firefox, IE etc ...
<header>
Custom Header
<img src="http://imageurl.com/image.jpg"/>
</header>
<div class="content">Page Content - as long as you want</div>
<footer>
Footer Content
</footer>
le CSS
@page {
margin: 0;
}
@media print {
footer {
position: fixed;
bottom: 0;
}
header {
position: fixed;
top: 0;
}
}
Le @page { margin: 0 }
supprime l'en-tête et le pied de page par défaut.
J'espère que cela t'aides.
Il y a deux solutions à votre problème
A) Poussez l'en-tête chromé vers l'extérieur en ne laissant aucune marge:
@page {
margin: 0;
size: auto;
}
ou
@media print {
@page { margin: 0; }
body { margin: 1.6cm; }
}
B) À l'origine une solution Firefox qui devrait fonctionner pour Chrome
<html moznomarginboxes mozdisallowselectionprint>
un échantillon:
<!DOCTYPE html>
<html moznomarginboxes mozdisallowselectionprint>
<head>
<title>Print PDF without header</title>
<style>
@media print {
@page { margin: 0; }
body { margin: 1.6cm; }
}
</style>
</head>
<body>
<p>Some Text in Paragraph to print!</p>
<a href="javascript:print()">Print</a>
</body>
</html>
Selon ce forum , il n’existe actuellement aucun moyen de le faire dans Google Chrome. Tout ce que vous pouvez faire est d'activer ou de désactiver l'en-tête/le pied de page. Ceci est indiqué par le commentaire:
Il n’existe actuellement aucun moyen de modifier l’en-tête lors de l’impression d’un document. Vous ne pouvez actuellement activer ou désactiver que l'en-tête et le pied de page, qui incluent la date, le nom de la page Web, l'URL de la page et le nombre de pages du document que vous imprimez. Vous pouvez consulter le Chrome Web Store pour savoir s’il existe des extensions tierces utiles que vous pouvez installer sur Chrome et qui correspondent à vos besoins en termes d’impression - Source
Il peut y avoir des extensions tierces pour obtenir la fonctionnalité que vous recherchez, ou comme vous le suggérez, vous pouvez utiliser JavaScript pour ajouter les éléments à imprimer.
Si vous voulez vraiment faire cela d'une manière non-hacky, vous devez vous pencher sur le protocole chrome devtools car l'interface en ligne de commande ne prend pas en charge beaucoup (alias --print-to-pdf
est tout ce que vous obtenez, pas d'options d'impression).
Le protocole est documenté ici: https://chromedevtools.github.io/devtools-protocol/tot
Les bibliothèques client sont disponibles dans de nombreuses langues, via les gestionnaires de packages.
Voici quelques exemples que j'ai rassemblés pour illustrer l'utilisation:
La méthode de protocole Page.printToPDF
prend en charge les arguments permettant de transmettre un balisage personnalisé pour l'en-tête et le pied de page.
Fondamentalement, le protocole est défini dans un protocol.json
, que les bibliothèques clientes consomment pour générer les classes/méthodes à utiliser dans n'importe quelle application. Ces méthodes encapsulent les communications de niveau inférieur avec le protocole.
Tout ce que je devais faire était d'installer un paquet de bibliothèque client (via npm ou composer), de m'assurer que chrome était bien installé, d'écrire un peu de code et de générer des fichiers PDF sans en-tête/pied de page! Fantastique.
Pour ceux d'entre vous qui veulent juste quelque chose qui fonctionne, je voudrais partager mon scénario que j'ai écrit aujourd'hui, basé sur la réponse de apokryfos.
Au début, vous devez installer les dépendances
yarn global add chrome-remote-interface
Ensuite, vous devez démarrer un chrome sans tête avec un port de débogage activé
chromium-browser --headless --disable-gpu --run-all-compositor-stages-before-draw --remote-debugging-port=9222
Maintenant, vous devez sauvegarder mon script sous le nom print-via-chrome.js
:
#!/usr/bin/env node
const homedir = require('os').homedir();
const CDP = require(homedir+'/.config/yarn/global/node_modules/chrome-remote-interface/');
const fs = require('fs');
const port = process.argv[2];
const htmlFilePath = process.argv[3];
const pdfFilePath = process.argv[4];
(async function() {
const protocol = await CDP({port: port});
// Extract the DevTools protocol domains we need and enable them.
// See API docs: https://chromedevtools.github.io/devtools-protocol/
const {Page} = protocol;
await Page.enable();
Page.loadEventFired(function () {
console.log("Waiting 100ms just to be sure.")
setTimeout(function () {
//https://chromedevtools.github.io/devtools-protocol/tot/Page/#method-printToPDF
console.log("Printing...")
Page.printToPDF({
displayHeaderFooter: true,
headerTemplate: '<div></div>',
footerTemplate: '<div class="text center"><span class="pageNumber"></span></div>',
//footerTemplate: '<div class="text center"><span class="pageNumber"></span> of <span class="totalPages"></span></div>'
}).then((base64EncodedPdf) => {
fs.writeFileSync(pdfFilePath, Buffer.from(base64EncodedPdf.data, 'base64'), 'utf8');
console.log("Done")
protocol.close();
});
}, 100);
});
Page.navigate({url: 'file://'+htmlFilePath});
})();
Après l'avoir rendu exécutable avec chmod +x print-via-chrome.js
, vous devriez être capable de convertir des fichiers HTML en fichiers PDF comme ceci:
./print-via-chrome.js 9222 my.html my.pdf
N'oubliez pas de quitter le chrome une fois votre transformation terminée.
Je suis à peu près sûr que cette solution est loin d'être parfaite, mais au moins ça marche et comme j'ai vu beaucoup de questions sur cette fonctionnalité et que j'ai dû investir quelques heures de mon temps pour la faire fonctionner, je voulais partager ma solution. Certains de mes problèmes étaient liés aux modèles d'en-tête et de pied de page, car il semble que les modèles vides ne remplacent pas les modèles existants (vous avez besoin de <div></div>
) et, bien que documentés différemment, les nouveaux modèles n'apparaissent pas dans une région visible tant qu'ils ne sont pas entourés d'un <div class="text center">
. ou quelque chose de similaire.