Outils pour utilisateurs

Outils du site


lettrehtml_pdf

Imprimer une lettre html en passant par un fichier pdf

Objectif

Prenons un exemple: dans le cadre d'un concours, il faut pouvoir informer plusieurs centaines de participants de leurs résultats, et ceci:

  • pour un mailing papier: 1 seul fichier à imprimer pour mise sous enveloppe à fenêtre
  • et pour un emailing: chaque participant reçoit son fichier de résultat .pdf par email en pièce jointe.

Les informations de résultats étant extrait d'une base de données.

La particularité du code proposé ici, c'est que le programme n'imprime pas lui-même: il crée des pages .pdf. Cela permet de vérifier visuellement ce qui va être imprimé, et on laissera le visualiseur pdf utilisé (acrobat reader ou autres) imprimer lui-même.

Ce code est multiplateforme (au moins Windows-Linux)

Code proposé

Afin de permettre l'impression de lettres complexes avec mise en page, tableau, images, (etc…), il a été choisi un format html (comme les pages web). On peut donc construire les courrier avec un éditeur html comme par exemple Kompozer, mais il faut procéder à quelques modifications du code html pour aboutir: voir la construction d'un exemple après le code.

L'impression et l'affichage des étiquettes est présenté sous forme de classe.

La seule donnée à passer à l'instanciation de la classe est:

  • lettrehtml: chaine de caractère codée en html et en unicode, représentant la ou les lettres à imprimer sur fichier .pdf.

D'autres données peuvent être modifiées (voir le code auto-documenté).

Voilà le code proposé avec son exemple d'application. Vous pouvez l'essayer avec un simple copier-coller dans idle, mais il vous faut bien sûr le module PyQt4 installé avant. Et c'est écrit en Python 2.7.

#!/usr/bin/python
# -*- coding: utf-8 -*-
from __future__ import division
#Python v2.7
 
import os, sys
 
from PyQt4 import QtCore, QtGui
 
Apppdf = QtGui.QApplication(sys.argv)
 
#############################################################################
class Lettrepdf(object):
    """Imprime dans un fichier .pdf et affiche la lettre sous forme html"""
 
    # =======================================================================
    def __init__(self, lettrehtml):
        """Initialise les variables nécessaires
        """
        # chaine multiligne à imprimer sur pdf (html présenté en unicode)
        self.lettrehtml = lettrehtml
 
        # Les var. suivantes (modifiables) ont une valeur par défaut
        # nom du fichier pdf à créer par impression
        self.nomfichierpdf = u"lettrehtml.pdf"
        # marges gauche, haute, droite et basse
        self.margegauche = 20 # mm
        self.margehaute = 20 # mm
        self.margedroite = 20 # mm
        self.margebasse = 20 # mm
        # nom de l'application qui crée le fichier pdf
        self.nomapplication = u""
        # nom du document pdf créé
        self.nomdocument = u""
        # orientation de la page à imprimer
        self.orientation = QtGui.QPrinter.Portrait
        # format de la page à imprimer
        self.formatpage = QtGui.QPrinter.A4
        # mettre le mode de couleur (Color ou GrayScale)
        self.modecouleur = QtGui.QPrinter.Color
 
    # =======================================================================
    def imprimepdf(self):
        """Lance l'impression de la lettre sur fichier pdf """
 
        # création de l'instance printer => fichier etiquettes.pdf
        printer = QtGui.QPrinter(QtGui.QPrinter.HighResolution)
        printer.setOutputFileName(self.nomfichierpdf)
        printer.setCreator(self.nomapplication)
        printer.setDocName(self.nomdocument)
        resolution = printer.resolution() # 1200dpi avec 'HighResolution'
        printer.setPageSize(self.formatpage)
        printer.setColorMode(self.modecouleur)
        printer.setOrientation(self.orientation)
        printer.setFontEmbeddingEnabled(True)
 
        printer.setFullPage(False) # nécessaire pour mettre des marges
        mm2px = lambda mm: int(mm/25.4*resolution)
        margegauche = mm2px(self.margegauche)
        margehaute = mm2px(self.margehaute)
        margedroite = mm2px(self.margedroite)
        margebasse = mm2px(self.margebasse)
        printer.setPageMargins(margegauche, margehaute, margedroite, margebasse, QtGui.QPrinter.DevicePixel)
 
        doc = QtGui.QTextDocument()
        doc.setHtml(self.lettrehtml)
        doc.print_(printer);
 
    # =======================================================================
    def affichepdf(self):
        """affiche le pdf créé, dans le visualiseur pdf par défaut de l'OS"""
        try:
            # solution pour Windows
            os.startfile(self.nomfichierpdf)
        except:
            try:
                # solution pour autre (Linux)
                os.system('xdg-open ' + self.nomfichierpdf) # solution pour autre (Linux)
            except:
                # pas de solution: faire à la main!
                QtGui.QMessageBox.warning(None,
                    u"Affichage du fichier pdf" + self.nomfichierpdf,
                    u"""Désolé, l'afficheur du fichier pdf n'est pas trouvé""")

Exemple

Et voilà un exemple de lettre html qui utilise le code ci-dessus:

if __name__ == "__main__":
 
    # test
    html = r"""
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="fr">
<head>
  <meta content="text/html; charset=UTF-8"
 http-equiv="content-type">
  <title></title>
</head>
<body style="direction: ltr;">
<br>
<br>
Manifestation<br>
<br>
<br>
<br>
<div style="position: absolute; margin-left: 5669px;">
{}<br>
{}<br>
{}<br>
{}<br>
</div>
<br>
<br>
<br>
<br>
<br>
<div style="position: absolute; margin-left: 1417px;">
<img width="300" height="300" src="xxx.jpg" alt="xxx.jpg" >
</div>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<table
 border="1" cellpadding="2" cellspacing="2">
  <tbody>
    <tr>
      <td >machin</td>
      <td>toto</td>
      <td>truc</td>
      <td>bidule</td>
    </tr>
    <tr>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
    </tr>
  </tbody>
</table>
<br>
<br>
<br>
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, <br>
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, <br>
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, <br>
<br>
fin de la lettre<br>
<br>
</body>
</html>
    """.decode('utf-8').format(u"Nom prénom", u"rue xxx", u"cp ville", u"pays")
 
    lettre = Lettrepdf(html)
    lettre.imprimepdf()
    lettre.affichepdf()

Plusieurs remarques:

  • On a utilisé ici la méthode format pour intégrer dans le code html les données spécifiques à chaque lettre (ici l'adresse). Cela suppose d'une part que le code html n'a pas de '{}' au départ, et que l'on utilise Python v2.7.
  • Vous voyez qu'on peut ajouter d'autres lettres dans la chaine, à condition, bien sûr de générer un saut de page avec le code suivant:
<div style="page-break-before:always;"></div>

Dans la construction de la chaine complète comportant toutes les lettres, il faudra donc:

  • une entête, par exemple:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="fr">
<head>
  <meta content="text/html; charset=UTF-8"
 http-equiv="content-type">
  <title></title>
</head>
<body style="direction: ltr;">
<br>
  • un bas de page, par exemple:
<br>
</body>
</html>
  • et autant de lettres qu'on veut. Chaque lettre se continue sur la page suivante si le contenu dépasse la page. A la fin de chaque lettre, on place (à la main) un saut de page:
<div style="page-break-before:always;"></div>
  • Pour positionner avec précision l'adresse derrière la fenêtre transparent de l'enveloppe, on utilise le code suivant:
<div style="position: absolute; margin-left: 5669px;">
{}<br>
{}<br>
{}<br>
{}<br>
</div>

Pour trouver le décalage de 5669 pixel, on a procédé comme suit:

  • on mesure sur la lettre que l'adresse doit être, par exemple, à 14cm du bord de la page. Comme on a mis une marge de 2cm, il reste un décalage à faire de 12cm. Or, avec la valeur 'QtGui.QPrinter.HighResolution', la résolution de la page pdf est de 1200 dpi. On a donc: 120/25.4*1200=5669 pixels.

Méthode pour une seule lettre

Méthode pour plusieurs lettres

lettrehtml_pdf.txt · Dernière modification: 2010/10/14 19:14 par tyrtamos

Outils de la page