learn.chetana.fr

Générer un PDF de marque (HTML/CSS → WeasyPrint)

12 min de lectureAvancé

Produire des documents, pas seulement les lire

Le reste du module lit des documents (email, OCR). Ici, l'inverse : générer un PDF customer-facing (un devis, un accusé de commande) propre et à la marque. La tentation est une lib qui « dessine » le PDF primitive par primitive (lignes, rectangles) — pénible et illisible. La bonne approche : tu sais déjà faire une belle page en HTML/CSS, alors génère du HTML et convertis-le en PDF.

Avec un moteur comme WeasyPrint : un template Jinja produit le HTML, WeasyPrint applique le CSS (y compris @page, marges, en-têtes) et rend le PDF. Ton système de design devient ta charte de document, sans nouveau langage.

Quatre pièges de production (chacun avec sa cicatrice)

1. L'event loop est sacré

Le rendu WeasyPrint est synchrone et CPU-bound. Dans un serveur async (FastAPI), l'appeler directement bloque tout l'event loop pendant le rendu — toutes les autres requêtes gèlent. La parade : déporter dans un thread.

pdf_bytes = await asyncio.to_thread(render_pdf, html)   # hors de l'event loop

Principe général, pas spécifique au PDF : tout travail bloquant/CPU dans un contexte async doit être déporté (to_thread, un pool, une file). L'async ne rend pas magiquement le CPU non-bloquant.

2. Le PDF doit être auto-contenu

Un <img src="https://…/logo.png"> oblige le moteur à faire un appel réseau au rendu — lent, faillible, et parfois carrément coupé (pas de sortie réseau en runtime). On embarque les assets en data-URI base64 :

<img src="data:image/png;base64,iVBORw0KGgo…">

Le logo, les polices, tout vit dans le HTML. Zéro dépendance réseau à l'instant du rendu — reproductible, rapide, offline.

3. Les données client s'échappent (même dans un PDF)

Un devis contient des données non maîtrisées (nom de société, libellés produits). Injectées brutes dans le HTML, un < ou un <script> casse le rendu ou pire. L'autoescape de Jinja doit être activé : c'est de la prévention XSS, et elle vaut aussi pour un document généré côté serveur. Un PDF n'est pas « moins exposé » qu'une page web — mêmes règles.

4. Les libs système dans l'image runtime

WeasyPrint s'appuie sur des libs natives (rendu texte, images). Elles doivent être présentes dans l'étage runtime de ton image Docker, pas seulement à l'étage build. Le classique : ça marche en local, l'image de prod plante au 1er rendu (OSError: cannot load library …). Les dépendances natives se déclarent dans le runtime.

🧩 Quiz1/4

Pourquoi envelopper le rendu WeasyPrint dans asyncio.to_thread ?

🃏 Flashcards1/5