Créez vos PDF en Python avec WeasyPrint

 06 janvier 2020   Arnaud

Catégorie / Mot clefs: Python / Python, Weasyprint, Pdf.


WeasyPrint est une librairie open-source sous license "BSD License 2.0" permettant d'exporter n'importe quel fichier HTML en PDF (libcairo sous le capot). Développée par Kozea, société lyonnaise experte du Web pour les acteurs santé. La librairie est aujourd'hui en version 51 et activement maintenue depuis 2011.

Cet article vise à vous faire découvrir ce logiciel que je trouve simple et efficace. Je vous présenterais donc les fonctionnalités de base et vous donnerais mes astuces afin que vous exportiez rapidement vos premiers PDF. Pour une exhaustivité complète voyez la documentation officielle.

Installation

Sous Debian, tout est dans les dépots

sudo apt-get install build-essential python3-dev python3-pip python3-setuptools python3-wheel python3-cffi libcairo2 libpango-1.0-0 libpangocairo-1.0-0 libgdk-pixbuf2.0-0 libffi-dev shared-mime-info

Et on enchaîne avec pip :

pip3 install WeasyPrint==51

Sous Windows, l'installation est possible mais complexe, Pango et Cairo n'étant pas disponible par défaut : voyez la doc…

Fonctionnement

Pour simplifier drastiquement, l'export se déroule comme suit :

  1. l'arborescence HTML est récupérée et analysée
  2. les feuilles de style sont récupérées (fournies par l'utilisateur dans la page)
  3. les feuilles de style sont appliquées
  4. Les éléments sont transformés en cases de dimensions fixes en respectant les règles d'empilement.
  5. Les cases sont dessinées sur le PDF
  6. Les métadonnées (bookmark et hyper-liens) sont intégrés

Usage

Basique :

weasyprint <fichier local ou URL> output.pdf

Un rapide tour des paramètres ?

--format [png ou pdf]
définit le format d'export.
--stylesheet <fichier local ou URL>
lieras le CSS passé en paramètre, vous pourrez en passer plusieurs qui seront appliqués dans l'ordre.
--base-url <URL>
si votre document contient des URLs relatives, entrez ici l'URL racine pour que Weasyprint exporte les urls complètes.
--attachment <file>
permet d'attacher n'importe quel document à votre PDF, le logiciel du lecteur autorisera l'ouverture de ce fichier par un programme externe en fonction de son type.
--debug
par défaut la commande est généreuse en logs et renverra toute erreurs rencontrées lors du processus de rendu, l'option debug ajoute toutes les étapes de rendu.

La ligne de commande est un bon moyen de créer rapidement un PDF mais tester des changements successifs peut être fastidieux en développement, voyez Recharger un PDF pour déveloper son CSS.

Apparence

Disposition

Par défaut les PDF sont exportés en format A4, pour changer le format de sortie :

@page {
size: Letter; // ie. portrait, landscape, Letter
margin: 2.5cm; }

Police

Par défaut, je fais souvent le choix d'intégrer la police dans le HTML, mais Weasyprint peut parfaitement récupérer une police externe depuis la feuille de style :

@import url(https://cdn.jsdelivr.net/npm/fork-awesome@1.1.7/fonts/forkawesome-webfont.woff2);
html {
font-family: ForkAwesome;
}

Recharger un PDF pour déveloper son CSS

L'affichage dans le navigateur de votre HTML risque de comporter des différences avec son export PDF, aussi je vous recommande de régulièrement visualiser la sortie de WeasyPrint après vos modifications CSS afin d'éviter les mauvaises surprises.

Afin de ne pas passer par la ligne de commande à chaque changement utilisez le serveur Web integré qui rechargera en direct vos modifications.

python -m weasyprint.tools.navigator

entrez l'url de votre page en développement sous la forme file://<chemin relatif ou absolu de votre page HTML> ex: file:///home/user/input.html

Il vous suffira de rafraîchir la page pour voir votre rendu dans votre navigateur.

API python

Les feuilles de style et la page HTML peuvent être passées sous forme de chemin, de liens ou de textes bruts à l'API de Weasyprint (les deux objets CSS et HTML) grâce à la puissance du "typage canard". Ici, nous préciserons les paramètres nommées pour l'exemple :

from Weasyprint import HTML, CSS

html = HTML(string='<h1>Le titre</h1>') # HTML(url='http://example.fr') ou encore HTML(filename='template/test.html')

css = CSS(string='''
    h1{
    color: red;}''')

html.write_pdf(
    '/tmp/exemple.pdf', stylesheets=[css])

html.write_pdf() # or write_png()

Servir des PDF une app Django

La doc mentionne django-weasyprint, qui fournit un mixin et une classe générique enrobant Weasyprint tout en respectant les conventions de Django. De qui créer des vues d'export au sein de votre projet avec puissance et élégance.

Ainsi pour créer une vue d'export de PDF:

import time

from django_weasyprint import WeasyTemplateView

class CustomPrintableView(WeasyTemplateView):
    template_name = 'exemple.html'

    def get_context_data(self, **kwargs):
        kwargs['time'] = str(time.time())
        return kwargs

    def get_pdf_filename(self):
        return 'exemple.pdf'

Conclusion

J'espère que cet article vous aura permis de cerner un peu de la puissance de cet outil qui permet non seulement d'exporter des PDF mais aussi de les gérer trés finement et facilement.

N'hésitez pas à piocher dans les exemples de Kozea qui feront une excellente base pour débuter vos templates: https://weasyprint.org/samples/