17
17
import tempfile
18
18
import shutil
19
19
import os
20
+ import subprocess
20
21
21
22
from qgis .core import (QgsMultiRenderChecker ,
22
23
QgsLayoutExporter ,
35
36
36
37
from qgis .testing import start_app , unittest
37
38
39
+ from utilities import getExecutablePath
40
+
41
+ # PDF-to-image utility
42
+ # look for Poppler w/ Cairo, then muPDF
43
+ # * Poppler w/ Cairo renders correctly
44
+ # * Poppler w/o Cairo does not always correctly render vectors in PDF to image
45
+ # * muPDF renders correctly, but sightly shifts colors
46
+ for util in [
47
+ 'pdftocairo' ,
48
+ # 'mudraw',
49
+ ]:
50
+ PDFUTIL = getExecutablePath (util )
51
+ if PDFUTIL :
52
+ break
53
+
54
+ # noinspection PyUnboundLocalVariable
55
+ if not PDFUTIL :
56
+ raise Exception ('PDF-to-image utility not found on PATH: '
57
+ 'install Poppler (with Cairo)' )
58
+
59
+
60
+ def pdfToPng (pdf_file_path , rendered_file_path , page , dpi = 96 ):
61
+ if PDFUTIL .strip ().endswith ('pdftocairo' ):
62
+ filebase = os .path .join (
63
+ os .path .dirname (rendered_file_path ),
64
+ os .path .splitext (os .path .basename (rendered_file_path ))[0 ]
65
+ )
66
+ call = [
67
+ PDFUTIL , '-png' , '-singlefile' , '-r' , str (dpi ),
68
+ '-x' , '0' , '-y' , '0' , '-f' , str (page ), '-l' , str (page ),
69
+ pdf_file_path , filebase
70
+ ]
71
+ elif PDFUTIL .strip ().endswith ('mudraw' ):
72
+ call = [
73
+ PDFUTIL , '-c' , 'rgba' ,
74
+ '-r' , str (dpi ), '-f' , str (page ), '-l' , str (page ),
75
+ # '-b', '8',
76
+ '-o' , rendered_file_path , pdf_file_path
77
+ ]
78
+ else :
79
+ return False , ''
80
+
81
+ print ("exportToPdf call: {0}" .format (' ' .join (call )))
82
+ try :
83
+ subprocess .check_call (call )
84
+ except subprocess .CalledProcessError as e :
85
+ assert False , ("exportToPdf failed!\n "
86
+ "cmd: {0}\n "
87
+ "returncode: {1}\n "
88
+ "message: {2}" .format (e .cmd , e .returncode , e .message ))
89
+
90
+
38
91
start_app ()
39
92
40
93
@@ -54,12 +107,13 @@ def tearDown(self):
54
107
with open (report_file_path , 'a' ) as report_file :
55
108
report_file .write (self .report )
56
109
57
- def checkImage (self , name , reference_image , rendered_image ):
110
+ def checkImage (self , name , reference_image , rendered_image , size_tolerance = 0 ):
58
111
checker = QgsMultiRenderChecker ()
59
112
checker .setControlPathPrefix ("layout_exporter" )
60
113
checker .setControlName ("expected_layoutexporter_" + reference_image )
61
114
checker .setRenderedImage (rendered_image )
62
115
checker .setColorTolerance (2 )
116
+ checker .setSizeTolerance (size_tolerance , size_tolerance )
63
117
result = checker .runTest (name , 20 )
64
118
self .report += checker .report ()
65
119
print ((self .report ))
@@ -278,6 +332,57 @@ def testExportToImage(self):
278
332
page2_path = os .path .join (self .basetestpath , 'test_exporttoimagesize_2.png' )
279
333
self .assertTrue (self .checkImage ('exporttoimagesize_page2' , 'exporttoimagesize_page2' , page2_path ))
280
334
335
+ def testExportToPdf (self ):
336
+ l = QgsLayout (QgsProject .instance ())
337
+ l .initializeDefaults ()
338
+
339
+ # add a second page
340
+ page2 = QgsLayoutItemPage (l )
341
+ page2 .setPageSize ('A5' )
342
+ l .pageCollection ().addPage (page2 )
343
+
344
+ # add some items
345
+ item1 = QgsLayoutItemShape (l )
346
+ item1 .attemptSetSceneRect (QRectF (10 , 20 , 100 , 150 ))
347
+ fill = QgsSimpleFillSymbolLayer ()
348
+ fill_symbol = QgsFillSymbol ()
349
+ fill_symbol .changeSymbolLayer (0 , fill )
350
+ fill .setColor (Qt .green )
351
+ fill .setStrokeStyle (Qt .NoPen )
352
+ item1 .setSymbol (fill_symbol )
353
+ l .addItem (item1 )
354
+
355
+ item2 = QgsLayoutItemShape (l )
356
+ item2 .attemptSetSceneRect (QRectF (10 , 20 , 100 , 150 ))
357
+ item2 .attemptMove (QgsLayoutPoint (10 , 20 ), page = 1 )
358
+ fill = QgsSimpleFillSymbolLayer ()
359
+ fill_symbol = QgsFillSymbol ()
360
+ fill_symbol .changeSymbolLayer (0 , fill )
361
+ fill .setColor (Qt .cyan )
362
+ fill .setStrokeStyle (Qt .NoPen )
363
+ item2 .setSymbol (fill_symbol )
364
+ l .addItem (item2 )
365
+
366
+ exporter = QgsLayoutExporter (l )
367
+ # setup settings
368
+ settings = QgsLayoutExporter .PdfExportSettings ()
369
+ settings .dpi = 80
370
+ settings .rasterizeWholeImage = False
371
+ settings .forceVectorOutput = False
372
+
373
+ pdf_file_path = os .path .join (self .basetestpath , 'test_exporttopdfdpi.pdf' )
374
+ self .assertEqual (exporter .exportToPdf (pdf_file_path , settings ), QgsLayoutExporter .Success )
375
+ self .assertTrue (os .path .exists (pdf_file_path ))
376
+
377
+ rendered_page_1 = os .path .join (self .basetestpath , 'test_exporttopdfdpi.png' )
378
+ dpi = 80
379
+ pdfToPng (pdf_file_path , rendered_page_1 , dpi = dpi , page = 1 )
380
+ rendered_page_2 = os .path .join (self .basetestpath , 'test_exporttopdfdpi2.png' )
381
+ pdfToPng (pdf_file_path , rendered_page_2 , dpi = dpi , page = 2 )
382
+
383
+ self .assertTrue (self .checkImage ('exporttopdfdpi_page1' , 'exporttopdfdpi_page1' , rendered_page_1 , size_tolerance = 1 ))
384
+ self .assertTrue (self .checkImage ('exporttopdfdpi_page2' , 'exporttopdfdpi_page2' , rendered_page_2 , size_tolerance = 1 ))
385
+
281
386
def testExportWorldFile (self ):
282
387
l = QgsLayout (QgsProject .instance ())
283
388
l .initializeDefaults ()
0 commit comments