Conversion HTML-CSS vers PDF en ligne de commande

Un des mes "Graals" est de trouver enfin un moyen simple rapide et fiable pour générer du PDF à partir d'un document HTML complet, avec prise en compte des feuilles de style. La raison du besoin est assez simple, j'archive tout ou presque en PDF, et particulièrement les pages WEB que j'ai peur de voir disparaître du jour au lendemain. Or avec un outil en ligne de commande, quel que soit le navigateur utilisé, j'ai toujours le moyen de créer un menu click-droit qui va bien pour convertir une page et me la ranger sagement dans le bon dossier.

État des lieux

Les outils libres en ligne de commande de type HTMT-to-PDF ne manque pas mais beaucoup ont tous en commun l'incapacité de prendre en charge proprement les feuilles de styles. C'est le cas par exemple d'htmtopdf et htmldoc. Ils se contentent de convertir le format HTML en son équivalent PDF et l'on y perd une grande partie de la présentation. Inacceptable.

J'en suis arrivé à la conclusion que les outils qui convertissent le mieux HTML/CSS en PDF restent définitivement les navigateurs eux-mêmes et leur système d'impression PDF comme konqueror avec le moteur de rendu KHtml/WebKit et la librairies d'impression Qt4/PDF, et FireFox avec le moteur Gecko et le système d'impression vers PDF de Gtk. Là le rendu final est propre, fidèle à la source avec quelques problèmes malgré tout : les liens ne sont pas clickables, ce qui est un peu dommage sans être rédhibitoire.

Utilisation de KHTML/WebKIT

Pour ne pas embarquer tout la quincaillerie KDE, le mieux est de pouvoir utiliser directement le moteur de rendu WebKIT et la librairie PDF de QT4. Et cela tombe bien, il existe un projet wkhtmltopdf spécialement conçu pour cet usage.

Pour compiler, il faut préalablement installer le paquet libQt4WebKit-devel et procéder comme suit :

# Récupération des sources
gaston$wget http://wkhtmltopdf.googlecode.com/files/wkhtmltopdf-0.8.0.tar.bz2
gaston$tar -jxvf wkhtmltopdf-0.8.0.tar.bz2
gaston$cd wkhtmltopdf
gaston$qmake wkhtmltopdf.pro
gaston$make
gaston$ 
Compilation du renderer

Ceci fait, le moteur est disponible et utilisable très simplement comme ceci :

gaston$./wkhtmltopdf http://artisan.karma-lab.net/print/1711 ~/Desktop/article.pdf
 
# et un petit test...
gaston$evince ~/Desktop/article.pdf
gaston$ 
Utilisation du renderer

La solution marche plutôt bien et la génération est très rapide. La seule chose qui me déplaît ici est l'utilisation de Qt.

Utilisation de FireFox/Gecko

Pour utiliser l'impression de FireFox en ligne de commande, c'est une extension qui nous vient en aide, appelée en toute logique Command Line Print. Une fois installée, cela nous donne ceci :

# génération du PDF
gaston$firefox -print http://artisan.karma-lab.net/print/1711 -printmode pdf -printfile ~/Desktop/article.pdf
 
# et un petit test...
$evince ~/Desktop/article.pdf
gaston$ 
Génération de PDF à partir de firefox

Au lancement de la commande, apparaît une pop-up contenant l'article. L'impression se fait traditionnellement mais sans intervention manuelle. Le rendu visuel est nickel avec un seul "hic" : pas de fond image ou colorés...

La différence avec Qt4 est que là nous pouvons y faire quelque chose. Pour cela, vous allez éditer le fichier mininav.js de l'extension. Pour le trouver, il faut déjà se placer dans votre dossier firefox, ~/.mozilla/firefox/XXXXXX.default/extensions. Une fois dedans, fait un find -name "mininav.js" qui devrait vous indiquer le chemin. Éditez ce fichier et ajoutez le bloc suivant dans les eaux de la ligne 269, juste après les lignes que j'ai mis en commentaire :

#      settings.printToFile = true
#      settings.toFileName = outputFilePath(mode)
      settings.printBGImages = true
      settings.printBGColors = true
      settings.marginLeft = 0.5
      settings.marginTop = 0.5
      settings.marginBottom = 0.5
      settings.marginRight = 0.5
      settings.unwriteableMarginLeft = 0
      settings.unwriteableMarginTop = 0
      settings.unwriteableMarginBottom = 0
      settings.unwriteableMarginRight = 0      
      settings.headerStrLeft="&T"
      settings.headerStrCenter=""
      settings.headerStrRight=""
      settings.footerStrLeft="&U"
      settings.footerStrCenter=""
      settings.footerStrRight=""
      settings.paperWidth=280

Tant qu'à modifier l'impression, nous en profitons pour l'améliorer en redéfinissant une marge plus fine, une largeur de page compatible avec la réalité WEB, des en-têtes et pieds de page plus explicites, et aussi l'impression des images et couleurs de fond. Ceci fait, il faut tuer toutes les instances de firefox et relancer la conversion. Là vous devriez avoir un PDF pleine largeur, correctement proportionné et en tout point identique à la source. L'autre avantage de cette approche est que le javascript de la page qui éventuellement apporte des détails d'affichage, est exécuté et donc pris en compte lors de l'impression.

Utilisation sans serveur X11

Quelle que soit l'outil utilisé, il aura besoin d'un serveur X11 pour fonctionner, et donc d'une variable DISPLAY correctement renseignée. Pour fonctionner "sans" serveur X11, nous allons devoir tricher et utilise le "faux" serveur Xvfb.

Xvfb est un outil fort intéressant qui offre une implémentation quasi complète du protocole X11 mais sans avoir besoin de sortie visuelle, car toutes les opérations se passent en mémoire. Utilisé avec firefox, cela donne donc ceci :

# lancement du faux serveur sur le port :10
gaston$Xvfb -kb -screen 0 1280x1024x24 -dpi 96 -terminate -auth /tmp/fake_authority -nolisten tcp :10 &;
 
# Lancement du convertisseur sur le nouveau "display"
gaston$DISPLAY=:10 wkhtmltopdf http://artisan.karma-lab.net/print/1711 /home/gaston/desktop/article.pdf
Xlib: extension "RANDR" missing on display ":10.0".
 
# et un petit test...
gaston$evince /home/gaston/desktop/article.pdf
 
# ménage
gaston$pkill Xvfb
gaston$rm -rf /tmp/fake_authority
gaston$ 

Nous voilà donc débarrassé du besoin d'un serveur X11 réel nous permettant d'utiliser nos deux outils sur un serveur sans écran.

firefox sans X11

Installer firefox sur un serveur ne pose en soit pas de problème. En revanche, c'est plus compliqué lorsqu'il s'agit d'installer une extension sans avoir la main sur la fenêtre. Heureusement les extensions s'installent très bien, et de manière globale (pour tous les utilisateurs, dont apache), en passant pas la ligne de commande :

root#Xvfb -kb -screen 0 1280x1024x24 -dpi 96 -terminate -auth /tmp/fake_authority -nolisten tcp :10 &
root#wget http://torisugari.googlepages.com/cmdlnprint_0_4_3.xpi
root#DISPLAY=:10 firefox -install-global-extension cmdlnprint_0_4_3.xpi
root# 
installation en ligne de commande de l'extension

Ceci fait, il est important de ne pas laisser firefox afficher sa boite de restauration de session sous peine de se retrouver bloqué sans possibilité de clicker sur un bouton qui se trouve en mémoire. Pour cela il faut tuer toutes les instances de firefox et éditer le fichier ~/.mozilla/firefox/XXXXXXX.default/prefs.js pour y ajouter :

user_pref("browser.sessionstore.resume_from_crash", false)
suppression du message de restauration de sessions

Conclusion

Voilà, nous avons maintenant tout l'outillage pour générer du PDF en ligne de commande, avec ou sans serveur X11. Après les usages de ces outils sont nombreux, cela peut aller de l'ajout d'un menu contextuel à votre navigateur préféré au script qui régulièrement détecte des raccourcis vers des URL et les converti en PDF. Ce n'est plus là qu'une question de besoin et d'imagination.

Vus : 335
Publié par Artisan Numérique : 100