j'ai cette interface je veut gérer un fichier qui contient le contenu de cette QtableView
j'ai installer pyPdf
Pièce jointe 262343
est ce que c'est possible de l'afficher le dans un fichier pdf
Version imprimable
j'ai cette interface je veut gérer un fichier qui contient le contenu de cette QtableView
j'ai installer pyPdf
Pièce jointe 262343
est ce que c'est possible de l'afficher le dans un fichier pdf
je voix que Reportlab est plus pratique est facile non ?
Salut,
Regarde d'abord du côté de QPrinter, Qt peut imprimer directement dans un PDF.
merci pour votre réponse . mais je voix que je peut pas gérer le fichier pdf comme (ajouter un tableau ou faire un en tete pour page ..... )
Bonjour,
On peut faire à peu près n'importe quel pdf avec PyQt, y compris plusieurs pages avec entêtes et bas de pages.
Le principe est:
- d'utiliser un QPrinter en lui disant qu'on veut créer un pdf (il faut donner un nom de fichier avec une extension ".pdf").
- on crée un QTextDocument avec un contenu en html, ou plutôt en "Rich Text" qui est un sous-ensemble de html spécifique à Qt.
- on imprime le QTextDocument
Dans ce cas, il faut bien noter que le QTableView n'est pas imprimé directement, mais nécessite d'être recodé en "Rich Text".
A titre d'inspiration, voilà un petit code d'exemple (Python 3, PyQt5) qui affiche un QTableView contenant une extraction d'une base de données sqlite, et qui fabrique le pdf correspondant:
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330 #!/usr/bin/python # -*- coding: utf-8 -*- # Python 3 import sys, os import time import sqlite3 from PyQt5 import (QtWidgets, QtGui, QtPrintSupport, QtCore, QtSql) ############################################################################# def affiche_url(ch): """affiche ch (chaine unicode) avec le programme par défaut de l'OS ch peut être une adresse web ou un fichier sur disque """ ch = ch.strip() if ch != "": if os.path.exists(ch): # ajoute "file:///" si nécessaire url = QtCore.QUrl.fromLocalFile(os.path.abspath(ch)) else: # ajoute "http://" si nécessaire url = QtCore.QUrl.fromUserInput(ch) # appelle le programme par défaut pour affichage de l'url QtGui.QDesktopServices.openUrl(url) ############################################################################# def creatableview(basesql, tablebase): """création d'un QTableView pour afficher la table "tablebase" de la base de données sqlite "basesql" """ # ouverture d'une connexion avec la base de données driver = "QSQLITE" # =driver (accès à la base sqlite3) cnx = QtSql.QSqlDatabase.addDatabase(driver, "nomcnx") cnx.setDatabaseName(basesql) if not cnx.open(): QtWidgets.QMessageBox.critical(None, "Ouverture de la base de données", "Erreur d'ouverture: %s" % cnx.lastError().text()) return None, None # création du QTableView tableview = QtWidgets.QTableView() # création et connexion du modèle model = QtSql.QSqlRelationalTableModel(None, cnx) model.setTable(tablebase) model.setEditStrategy(QtSql.QSqlTableModel.OnManualSubmit) model.select() # peupler le modèle tableview.setModel(model) #print(tableview.model().rowCount()) # nombre de lignes du tableview return tableview, cnx ############################################################################# def creatextdocument(tableview): """création d'un QTextDocument contenant le richtext à afficher """ doc = QtGui.QTextDocument() nblignes = tableview.model().rowCount() # nombre de lignes du tableau nbchamps = tableview.model().columnCount() # nombre de colonnes du tableau # début du code de la page html html = """ <html> <head> <meta content="text/html; charset=ISO-8859-1" http-equiv="content-type"> <title>mondocument</title> </head> <body style="font-family: Helvetica,Arial,sans-serif;"> <h1>Titre</h1> <h2>Sous-titre</h2> <br> """ # début de la table html += """ <table border="1" cellpadding="50" cellspacing="0" width="100%"> <tbody> """ # affichage de l'en-tête du tableau html += """<tr>""" for j in range(0, nbchamps): html += "<th>" + tableview.model().headerData(j, QtCore.Qt.Horizontal, 0) + "</th>" html += "</tr>" # affichage des lignes du tableau for i in range(0, nblignes): html += "<tr>" for j in range(0, nbchamps): html += "<td valign=middle>" + tableview.model().index(i, j).data() + "</td>" html += "</tr>" # fin de la table html += """ </table> """ # </tbody> # fin de la page html html += """ </body> </html> """ # introduction du script html dans le doc doc.setHtml(html) return doc ############################################################################# class Tableview2pdf(QtCore.QObject): #======================================================================== def __init__(self, doc, parent=None): super(Tableview2pdf, self).__init__(parent) self.doc = doc # = le QTextDocument contenant le richtext à afficher self.fichierpdf = "fichierpdf.pdf" self.margeGT = 15 # marge totale gauche en mm self.margeHT = 10 # marge totale haute en mm self.margeDT = 10 # marge totale droite en mm self.margeBT = 10 # marge totale basse en mm self.enteteHT = 15 # hauteur totale de l'en-tête en mm self.basdepageHT = 15 # hauteur totale du bas de page en mm self.separationHT = 2 # hauteur de la séparation avec le corps du texte en mm self.formatpapier = QtPrintSupport.QPrinter.A4 self.orientation = QtPrintSupport.QPrinter.Portrait self.police = "DejaVu Sans" # police de caractères pour l'impression self.taille = 10 # taille de la police pour l'impression # date du jour (=de l'ordi) pour utilisation dans en-tête ou bas de page d = time.localtime() self.datejour = time.strftime('%d/%m/%Y', d) #======================================================================== def imprime_entete(self): """imprime l'en-tête de la page sont disponibles: - numéro de page courante: self.numpage - nombre total de pages: self.nbpages - date du jour (= date de l'ordi): self.datejour """ # pour convertir des mm en pixels en utilisant la résolution de l'imprimante mm2px = lambda mm: int(mm / 25.4 * self.printer.resolution()) # police de car. pour le texte de l'en-tête (ne change rien au doc) fonttitre = QtGui.QFont() fonttitre.setFamily("DejaVu Sans") fonttitre.setPointSize(18) # fonttitre.setBold(True) # fonttitre.setItalic(True) # fonttitre.setUnderline(True) #-------------------------------------------------------------------- # imprime le titre du document au centre de l'en-tête self.painter.setFont(fonttitre) # rappel: rect_e est le rectangle QRect du bas de page self.painter.drawText(self.rect_e, QtCore.Qt.AlignVCenter | QtCore.Qt.AlignHCenter, "En-tête") #======================================================================== def imprime_basdepage(self): """imprime le bas de la page sont disponibles: - numéro de page courante: self.numpage - nombre total de pages: self.nbpages - date du jour (= date de l'ordi): self.datejour """ # pour convertir des mm en pixels en utilisant la résolution de l'imprimante mm2px = lambda mm: int(mm / 25.4 * self.printer.resolution()) # police de car. pour le texte du bas de page (ne change rien au doc) fonttitre = QtGui.QFont() fonttitre.setFamily("DejaVu Sans") fonttitre.setPointSize(14) # fonttitre.setBold(True) # fonttitre.setItalic(True) # fonttitre.setUnderline(True) # imprime le titre du document au centre de l'en-tête self.painter.setFont(fonttitre) # rappel: rect_b est le rectangle QRect du bas de page self.painter.drawText(self.rect_b, QtCore.Qt.AlignVCenter | QtCore.Qt.AlignHCenter, "Bas de page") #======================================================================== def imprimepdf(self): #-------------------------------------------------------------------- # crée et configure l'imprimante printer self.printer = QtPrintSupport.QPrinter(QtPrintSupport.QPrinter.HighResolution) self.printer.setPaperSize(self.formatpapier) self.printer.setOrientation(self.orientation) self.printer.setOutputFormat(QtPrintSupport.QPrinter.PdfFormat) self.printer.setOutputFileName(self.fichierpdf) # ajuster la taille des car. du doc.drawContents à printer/highresolution font = QtGui.QFont() font.setFamily(self.police) font.setPointSize(self.taille) paintFont = QtGui.QFont(font, self.printer) self.doc.setDefaultFont(paintFont) # pour convertir des mm en pixels en utilisant la résolution de printer mm2px = lambda mm: int(mm / 25.4 * self.printer.resolution()) # hauteur à réserver pour l'en-tête: conversion en pixels enteteH = mm2px(self.enteteHT) # hauteur à réserver pour le bas de page: conversion en pixels basdepageH = mm2px(self.basdepageHT) # séparation entre entete/basdepage et corps du texte: conv. => pixels separationH = mm2px(self.separationHT) # marges pour définir le rectangle d'impression de chaque page margeG = mm2px(self.margeGT) margeH = mm2px(self.margeHT) + enteteH + separationH margeD = mm2px(self.margeDT) margeB = mm2px(self.margeBT) + basdepageH + separationH self.printer.setPageMargins(margeG, margeH, margeD, margeB, QtPrintSupport.QPrinter.DevicePixel) # rectangle d'impression du texte (coordonnées du papier) self.rect = self.printer.pageRect() # mettre pour le doc le même rectangle d'impression que printer self.doc.setPageSize(QtCore.QSizeF(QtCore.QSize(self.rect.size().width(), self.rect.size().height()))) # rectangle d'impression du texte ((0,0) => coord. pt haut et gauche) self.rect_t = QtCore.QRect(0, 0, self.rect.size().width(), self.rect.size().height()) # rectangle d'impression de l'en-tête s'il y en a une self.rect_e = QtCore.QRect(0, -enteteH - separationH, self.rect.size().width(), enteteH) # rectangle d'impression du bas de page s'il y en a un self.rect_b = QtCore.QRect(0, self.rect.size().height() + separationH, self.rect.size().width(), basdepageH) # grand rectangle d'impression de tout le doc self.contentRect = QtCore.QRectF(QtCore.QRect(QtCore.QPoint(0, 0), self.doc.size().toSize())) # rectangle d'impression courant de la page self.currentRect = QtCore.QRectF(QtCore.QRect(QtCore.QPoint(0, 0), self.rect.size())) #-------------------------------------------------------------------- # crée le painter lié au printer self.painter = QtGui.QPainter(self.printer) # sauvegarde le painter dans la pile self.painter.save() # nombre total de pages du doc à imprimer self.nbpages = self.doc.pageCount() # impression de toutes les pages for numpage in range(1, self.nbpages + 1): # pour récupérer le numéro de page dans en-tête et bas de page self.numpage = numpage #---------------------------------------------------------------- # imprime le corps du texte sur la page courante self.doc.drawContents(self.painter, QtCore.QRectF(self.currentRect)) self.currentRect.translate(0, self.currentRect.height()) self.painter.restore() #---------------------------------------------------------------- # impression de l'en-tête si c'est demandé if self.enteteHT > 0: self.imprime_entete() #---------------------------------------------------------------- # impression du bas de page si c'est demandé if self.basdepageHT > 0: self.imprime_basdepage() #---------------------------------------------------------------- # sauvegarde le painter dans la pile self.painter.save() # décale la partie à imprimer de la hauteur de la zone d'impression self.painter.translate(0, -self.currentRect.height() * numpage) # provoque un changement de page s'il reste quelque chose à imprimer if self.currentRect.intersects(self.contentRect): self.printer.newPage() # restaure le painter de la pile self.painter.restore() # fin d'impression self.painter.end() ############################################################################# if __name__ == "__main__": app = QtWidgets.QApplication(sys.argv) basesql = "concours.db3" tablebase = "nations" tableview, cnx = creatableview(basesql, tablebase) if tableview == None: sys.exit() # affichage pour mise au point du code tableview.setAttribute(QtCore.Qt.WA_DeleteOnClose) tableview.show() doc = creatextdocument(tableview) pdf = Tableview2pdf(doc) pdf.imprimepdf() affiche_url(pdf.fichierpdf) # fermer la connexion à la base de données if cnx != None: try: # détruire le model et libérer ses ressources tableview.model.clear() # couper la liaison entre le QTableView et le model tableview.setModel(None) # détruire la connection et libérer ses ressources cnx.close() cnx = None except Exception: pass # en cas d'erreur: ne rien faire! sys.exit(app.exec_())
Bonjour,
Je découvre ce poste et le code très intéressant pour générer un pdf en python.
Je cherche à l'adapter pour pouvoir enrichir l'en-tête et le pied de page, à savoir:
- pouvoir insérer un logo dans l'en-tête (je pense qu'il faut écrire en html, mais ça plante -> un exemple?)
- avoir dans le pied de page 3 zones (gauche, centre, droit) : là aussi je pense qu'il faut faire ça avec une table en html ?
Auriez-vous des exemples pour faire ça?
Merci,
Nico
Bonjour DiverSIG,
Je n'ai pas d'exemple tout fait, et ce que j'ai fait est très spécifique et très complexe. J'avais réussi:
- un entête avec logo à gauche, titre au milieu et date à droite
- un bas de page avec du texte à gauche, au milieu et à droite.
Je pense qu'il serait plus facile d'insérer carrément du html pour l'entête et le base de page, en se limitant, bien sûr, aux instructions permises par le richtext (sous-ensemble de html).
Pour faire ce genre de travail, j'écris ce que je veux sous "bluegriffon" (par exemple) qui est un éditeur html "wysiwyg" gratuit (http://www.bluegriffon.org/). Et je reprends le code html que je corrige si nécessaire.
Il faudra, bien sûr, affiner la mise en page, et en particulier définir en pixels la place que devront prendre les entêtes, bas de page, et la partie réservée au texte. En général, je définis en mm, et j'utilise une formule de conversion du genre:
Essayez comme ça et dites-moi si ça marche.Code:mm2px = lambda mm: int(mm / 25.4 * self.printer.resolution())
Bonjour DiverSIG,
Finalement, comme je suis curieux, j'ai cherché un peu, et j'ai trouvé une solution qui a l'air de marcher et qui n'est pas trop compliquée.
Je reviens tout de même sur ce que je suggérais dans ma réponse précédente: insérer des en-têtes et des bas-de-page en html ne semble pas marcher. En tout cas, je n'ai pas réussi, et mes recherches sur le web sont restées vaines.
Voilà les éléments de la solution proposée (code complet à la fin)
- Puisque je m'intéressais aux en-têtes et aux bas de pages, j'ai simplifié le contenu lui-même pour l'essai: j'ai pris un texte latin bidon (appelé "Lorem Ipsum") qu'on peut générer ici: https://fr.lipsum.com/. On le traduit en html (par copier-coller dans l'éditeur html "bluegriffon" par exemple), et ça donne le fichier "contenu.html" qu'on va afficher dans le pdf. Mais on peut, bien sûr, avoir un texte html aussi complexe qu'on veut (avec tableaux, images, graphiques, etc...), à condition de respecter les limitations du "richtext" (voir la doc de Qt5).
- pour afficher les éléments des en-têtes et bas-de-pages, j'utilise les méthodes du QPainter: .drawImage et drawText. Remarque: drawText ne supporte pas le html.
- Pour l'en-tête, j'ai choisi de mettre un logo à gauche (j'en ai pris un au hasard: celui de youtube), un titre au milieu et la date du jour à droite. Mais bien sûr, on met ce qu'on veut une fois qu'on a compris le code.
- Pour le bas-de-page, j'ai choisi de mettre l'émetteur du document à gauche, la référence du classement au milieu, et le nombre de page à droite. Mais bien sûr, on met ce qu'on veut une fois qu'on a compris le code.
- Pour l'en-tête, et pour le bas-de-page, il suffit d'indiquer une hauteur==0 pour que les éléments ne soient pas affichés.
- Le code complet est plus un "démonstrateur" qu'une application finie! En particulier, il faudrait paramétrer beaucoup plus de choses pour qu'on ait une vraie fonction de bibliothèque. On pourrait ajouter, par exemple, des en-têtes et bas-de-pages différents pour la 1ère page et pour toutes les autres. Il suffirait d'ajouter un test sur le numéro de page dans les méthodes qui impriment entêtes et bas-de-pages.
Voilà une image de ce que cela donne:
Pièce jointe 595818
Et voilà le code complet (il ne manque que le fichier "contenu.html" que vous ajoutez comme vous voulez). Le code est très commenté comme d'habitude (je le fais aussi pour moi!). Python v3.7, PyQt5 v5.15, Windows 10.
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272 #!/usr/bin/python3 # -*- coding: utf-8 -*- import sys, os import time from PyQt5 import (QtWidgets, QtGui, QtPrintSupport, QtCore) ############################################################################# def affiche_url(ch): """affiche ch (chaine unicode) avec le programme par défaut de l'OS ch peut être une adresse web ou un fichier sur disque """ ch = ch.strip() if ch != "": if os.path.exists(ch): # ajoute "file:///" si nécessaire url = QtCore.QUrl.fromLocalFile(os.path.abspath(ch)) else: # ajoute "http://" si nécessaire url = QtCore.QUrl.fromUserInput(ch) # appelle le programme par défaut pour affichage de l'url QtGui.QDesktopServices.openUrl(url) ############################################################################# class Doc2pdf(QtCore.QObject): #======================================================================== def __init__(self, doc, fichierpdf, entete=[], basdepage=[], parent=None): super().__init__(parent) self.doc = doc # le QTextDocument contenant le richtext à afficher self.fichierpdf = fichierpdf # le fichier pdf à créer self.entete = entete # = liste: [hauteur_mm, image, titre, date] self.basdepage = basdepage # liste: [hauteur_mm, gauche, milieu, page] self.margeGT = 15 # marge totale gauche en mm self.margeHT = 10 # marge totale haute en mm self.margeDT = 10 # marge totale droite en mm self.margeBT = 10 # marge totale basse en mm self.enteteHT = self.entete.pop(0) # hauteur totale de l'en-tête en mm self.basdepageHT = self.basdepage.pop(0) # hauteur totale du bas de page en mm self.separationHT = 2 # hauteur de la séparation avec le corps du texte en mm self.formatpapier = QtPrintSupport.QPrinter.A4 self.orientation = QtPrintSupport.QPrinter.Portrait self.police = "Arial" # police de caractères pour l'impression self.taille = 12 # taille de la police pour l'impression # date du jour (=de l'ordi) pour utilisation dans en-tête ou bas de page d = time.localtime() self.datejour = time.strftime('%d/%m/%Y', d) #======================================================================== def imprime_entete(self): """Imprime l'en-tête de la page Sont disponibles: - numéro de page courante: self.numpage - nombre total de pages: self.nbpages - date du jour (= date de l'ordi): self.datejour """ if self.enteteHT==0: return # pas d'en-tête #--------------------------------------------------------------------- # Impression du logo image = QtGui.QImage(self.entete[0]) v = self.rect_e.height()*0.80 h = v/image.height()*image.width() rect_logo = QtCore.QRect(self.rect_e.x(), self.rect_e.y(), h, v) image2 = image.scaled(h, v, QtCore.Qt.KeepAspectRatioByExpanding, QtCore.Qt.SmoothTransformation) self.painter.drawImage(rect_logo, image2) #--------------------------------------------------------------------- # Impression du titre au milieu fonttitre = QtGui.QFont() fonttitre.setFamily("Arial") fonttitre.setPointSize(18) fonttitre.setBold(True) # fonttitre.setItalic(True) # fonttitre.setUnderline(True) self.painter.setFont(fonttitre) self.painter.drawText(self.rect_e, QtCore.Qt.AlignVCenter | QtCore.Qt.AlignHCenter, self.entete[1]) #--------------------------------------------------------------------- # Impression à droite. si texte=="date" => date du jour fonttitre = QtGui.QFont() fonttitre.setFamily("Arial") fonttitre.setPointSize(12) fonttitre.setBold(True) # fonttitre.setItalic(True) # fonttitre.setUnderline(True) self.painter.setFont(fonttitre) texte = self.entete[2] if texte=="date": texte = self.datejour self.painter.drawText(self.rect_e, QtCore.Qt.AlignVCenter | QtCore.Qt.AlignRight, texte) #======================================================================== def imprime_basdepage(self): """Imprime le bas de la page Sont disponibles: - numéro de page courante: self.numpage - nombre total de pages: self.nbpages - date du jour (= date de l'ordi): self.datejour """ if self.basdepageHT==0: return # pas de bas de page #--------------------------------------------------------------------- # Impression du texte à gauche fonttitre = QtGui.QFont() fonttitre.setFamily("Arial") fonttitre.setPointSize(12) #fonttitre.setBold(True) fonttitre.setItalic(True) # fonttitre.setUnderline(True) self.painter.setFont(fonttitre) self.painter.drawText(self.rect_b, QtCore.Qt.AlignVCenter | QtCore.Qt.AlignLeft, self.basdepage[0]) #--------------------------------------------------------------------- # Impression du texte au milieu fonttitre = QtGui.QFont() fonttitre.setFamily("Arial") fonttitre.setPointSize(12) #fonttitre.setBold(True) fonttitre.setItalic(True) # fonttitre.setUnderline(True) self.painter.setFont(fonttitre) self.painter.drawText(self.rect_b, QtCore.Qt.AlignVCenter | QtCore.Qt.AlignHCenter, self.basdepage[1]) #--------------------------------------------------------------------- # Impression du texte à droite => si texte=="page" => le nombre de page fonttitre = QtGui.QFont() fonttitre.setFamily("Arial") fonttitre.setPointSize(12) #fonttitre.setBold(True) fonttitre.setItalic(True) # fonttitre.setUnderline(True) self.painter.setFont(fonttitre) if self.basdepage[2]=="page": texte = "page {}/{}".format(self.numpage, self.nbpages) else: texte = self.basdepage[2] self.painter.drawText(self.rect_b, QtCore.Qt.AlignVCenter | QtCore.Qt.AlignRight, texte) #======================================================================== def imprimepdf(self): #-------------------------------------------------------------------- # crée et configure l'imprimante printer self.printer = QtPrintSupport.QPrinter(QtPrintSupport.QPrinter.HighResolution) self.printer.setPaperSize(self.formatpapier) self.printer.setOrientation(self.orientation) self.printer.setOutputFormat(QtPrintSupport.QPrinter.PdfFormat) self.printer.setOutputFileName(self.fichierpdf) # ajuster la taille des car. du doc.drawContents à printer/highresolution font = QtGui.QFont() font.setFamily(self.police) font.setPointSize(self.taille) paintFont = QtGui.QFont(font, self.printer) self.doc.setDefaultFont(paintFont) # pour convertir des mm en pixels en utilisant la résolution de printer mm2px = lambda mm: int(mm / 25.4 * self.printer.resolution()) # hauteur à réserver pour l'en-tête: conversion en pixels enteteH = mm2px(self.enteteHT) # hauteur à réserver pour le bas de page: conversion en pixels basdepageH = mm2px(self.basdepageHT) # séparation entre entete/basdepage et corps du texte: conv. => pixels separationH = mm2px(self.separationHT) # marges (pixels) pour définir le rectangle d'impression de chaque page margeG = mm2px(self.margeGT) margeH = mm2px(self.margeHT) + enteteH + separationH margeD = mm2px(self.margeDT) margeB = mm2px(self.margeBT) + basdepageH + separationH # fournit les marges au printer self.printer.setPageMargins(margeG, margeH, margeD, margeB, QtPrintSupport.QPrinter.DevicePixel) # rectangle d'impression du texte (coordonnées du papier) self.rect = self.printer.pageRect() # mettre pour le doc le même rectangle d'impression que printer self.doc.setPageSize(QtCore.QSizeF(QtCore.QSize(self.rect.size().width(), self.rect.size().height()))) # rectangle d'impression du texte ((0,0) => coord. pt haut et gauche) self.rect_t = QtCore.QRect(0, 0, self.rect.size().width(), self.rect.size().height()) # rectangle d'impression de l'en-tête s'il y en a une self.rect_e = QtCore.QRect(0, -enteteH - separationH, self.rect.size().width(), enteteH) # rectangle d'impression du bas de page s'il y en a un self.rect_b = QtCore.QRect(0, self.rect.size().height() + separationH, self.rect.size().width(), basdepageH) # grand rectangle d'impression de tout le doc self.contentRect = QtCore.QRectF(QtCore.QRect(QtCore.QPoint(0, 0), self.doc.size().toSize())) # rectangle d'impression courant de la page self.currentRect = QtCore.QRectF(QtCore.QRect(QtCore.QPoint(0, 0), self.rect.size())) #-------------------------------------------------------------------- # crée le painter lié au printer self.painter = QtGui.QPainter(self.printer) # sauvegarde le painter dans la pile self.painter.save() # nombre total de pages du doc à imprimer self.nbpages = self.doc.pageCount() # impression de toutes les pages for numpage in range(1, self.nbpages + 1): # pour récupérer le numéro de page dans en-tête et bas de page self.numpage = numpage #---------------------------------------------------------------- # imprime le corps du texte sur la page courante self.doc.drawContents(self.painter, QtCore.QRectF(self.currentRect)) self.currentRect.translate(0, self.currentRect.height()) self.painter.restore() #---------------------------------------------------------------- # imprime l'en-tête si c'est demandé self.imprime_entete() #---------------------------------------------------------------- # impression du bas de page si c'est demandé self.imprime_basdepage() #---------------------------------------------------------------- # sauvegarde le painter dans la pile self.painter.save() # décale la partie à imprimer de la hauteur de la zone d'impression self.painter.translate(0, -self.currentRect.height() * numpage) # provoque un changement de page s'il reste quelque chose à imprimer if self.currentRect.intersects(self.contentRect): self.printer.newPage() # restaure le painter de la pile self.painter.restore() # fin d'impression self.painter.end() ############################################################################# if __name__ == "__main__": app = QtWidgets.QApplication(sys.argv) doc = QtGui.QTextDocument() with open("contenu.html", "r", encoding="utf-8") as fs: doc.setHtml(fs.read()) fichierpdf = "fichier.pdf" hauteurET = 15 # hauteur de l'en-tête en mm entete = [hauteurET, "logo.jpg", "TITRE", "date"] hauteurBP = 10 # hauteur du bas-de-page en mm basdepage = [hauteurBP, "Emetteur:xxx", "classement:yyy", "page"] pdf = Doc2pdf(doc, fichierpdf, entete, basdepage) pdf.imprimepdf() affiche_url(fichierpdf)
ça marche nikel, c'est super !
Merci,
Nico