1# -*- coding: utf-8 -*- 2"""QGIS Unit tests for QgsLayoutExporter 3 4.. note:: This program is free software; you can redistribute it and/or modify 5it under the terms of the GNU General Public License as published by 6the Free Software Foundation; either version 2 of the License, or 7(at your option) any later version. 8""" 9__author__ = 'Nyall Dawson' 10__date__ = '11/12/2017' 11__copyright__ = 'Copyright 2017, The QGIS Project' 12 13import qgis # NOQA 14from qgis.PyQt import sip 15import tempfile 16import shutil 17import os 18import subprocess 19from xml.dom import minidom 20from osgeo import gdal 21 22from qgis.core import (QgsMultiRenderChecker, 23 QgsLayoutExporter, 24 QgsLayout, 25 QgsProject, 26 QgsMargins, 27 QgsLayoutItemShape, 28 QgsLayoutGuide, 29 QgsRectangle, 30 QgsLayoutItemPage, 31 QgsLayoutItemMap, 32 QgsLayoutItemScaleBar, 33 QgsLayoutPoint, 34 QgsLayoutMeasurement, 35 QgsUnitTypes, 36 QgsSimpleFillSymbolLayer, 37 QgsFillSymbol, 38 QgsVectorLayer, 39 QgsCoordinateReferenceSystem, 40 QgsPrintLayout, 41 QgsSingleSymbolRenderer, 42 QgsRenderContext, 43 QgsReport) 44from qgis.PyQt.QtCore import QSize, QSizeF, QDir, QRectF, Qt, QDateTime, QDate, QTime, QTimeZone 45from qgis.PyQt.QtGui import QImage, QPainter 46from qgis.PyQt.QtPrintSupport import QPrinter 47from qgis.PyQt.QtSvg import QSvgRenderer, QSvgGenerator 48 49from qgis.testing import start_app, unittest 50 51from utilities import getExecutablePath, unitTestDataPath 52 53TEST_DATA_DIR = unitTestDataPath() 54 55# PDF-to-image utility 56# look for Poppler w/ Cairo, then muPDF 57# * Poppler w/ Cairo renders correctly 58# * Poppler w/o Cairo does not always correctly render vectors in PDF to image 59# * muPDF renders correctly, but slightly shifts colors 60for util in [ 61 'pdftocairo', 62 # 'mudraw', 63]: 64 PDFUTIL = getExecutablePath(util) 65 if PDFUTIL: 66 break 67 68# noinspection PyUnboundLocalVariable 69if not PDFUTIL: 70 raise Exception('PDF-to-image utility not found on PATH: ' 71 'install Poppler (with Cairo)') 72 73 74def pdfToPng(pdf_file_path, rendered_file_path, page, dpi=96): 75 if PDFUTIL.strip().endswith('pdftocairo'): 76 filebase = os.path.join( 77 os.path.dirname(rendered_file_path), 78 os.path.splitext(os.path.basename(rendered_file_path))[0] 79 ) 80 call = [ 81 PDFUTIL, '-png', '-singlefile', '-r', str(dpi), 82 '-x', '0', '-y', '0', '-f', str(page), '-l', str(page), 83 pdf_file_path, filebase 84 ] 85 elif PDFUTIL.strip().endswith('mudraw'): 86 call = [ 87 PDFUTIL, '-c', 'rgba', 88 '-r', str(dpi), '-f', str(page), '-l', str(page), 89 # '-b', '8', 90 '-o', rendered_file_path, pdf_file_path 91 ] 92 else: 93 return False, '' 94 95 print("exportToPdf call: {0}".format(' '.join(call))) 96 try: 97 subprocess.check_call(call) 98 except subprocess.CalledProcessError as e: 99 assert False, ("exportToPdf failed!\n" 100 "cmd: {0}\n" 101 "returncode: {1}\n" 102 "message: {2}".format(e.cmd, e.returncode, e.message)) 103 104 105def svgToPng(svg_file_path, rendered_file_path, width): 106 svgr = QSvgRenderer(svg_file_path) 107 108 height = width / svgr.viewBoxF().width() * svgr.viewBoxF().height() 109 110 image = QImage(width, height, QImage.Format_ARGB32) 111 image.fill(Qt.transparent) 112 113 p = QPainter(image) 114 p.setRenderHint(QPainter.Antialiasing, False) 115 svgr.render(p) 116 p.end() 117 118 res = image.save(rendered_file_path, 'png') 119 if not res: 120 os.unlink(rendered_file_path) 121 122 123start_app() 124 125 126class TestQgsLayoutExporter(unittest.TestCase): 127 128 @classmethod 129 def setUpClass(cls): 130 """Run before all tests""" 131 cls.basetestpath = tempfile.mkdtemp() 132 cls.dots_per_meter = 96 / 25.4 * 1000 133 134 def setUp(self): 135 self.report = "<h1>Python QgsLayoutExporter Tests</h1>\n" 136 137 def tearDown(self): 138 report_file_path = "%s/qgistest.html" % QDir.tempPath() 139 with open(report_file_path, 'a') as report_file: 140 report_file.write(self.report) 141 142 def checkImage(self, name, reference_image, rendered_image, size_tolerance=0): 143 checker = QgsMultiRenderChecker() 144 checker.setControlPathPrefix("layout_exporter") 145 checker.setControlName("expected_layoutexporter_" + reference_image) 146 checker.setRenderedImage(rendered_image) 147 checker.setColorTolerance(2) 148 checker.setSizeTolerance(size_tolerance, size_tolerance) 149 result = checker.runTest(name, 20) 150 self.report += checker.report() 151 print((self.report)) 152 return result 153 154 def testRenderPage(self): 155 l = QgsLayout(QgsProject.instance()) 156 l.initializeDefaults() 157 158 # add some items 159 item1 = QgsLayoutItemShape(l) 160 item1.attemptSetSceneRect(QRectF(10, 20, 100, 150)) 161 fill = QgsSimpleFillSymbolLayer() 162 fill_symbol = QgsFillSymbol() 163 fill_symbol.changeSymbolLayer(0, fill) 164 fill.setColor(Qt.green) 165 fill.setStrokeStyle(Qt.NoPen) 166 item1.setSymbol(fill_symbol) 167 l.addItem(item1) 168 169 # get width/height, create image and render the composition to it 170 size = QSize(1122, 794) 171 output_image = QImage(size, QImage.Format_RGB32) 172 173 output_image.setDotsPerMeterX(self.dots_per_meter) 174 output_image.setDotsPerMeterY(self.dots_per_meter) 175 QgsMultiRenderChecker.drawBackground(output_image) 176 painter = QPainter(output_image) 177 exporter = QgsLayoutExporter(l) 178 179 # valid page 180 exporter.renderPage(painter, 0) 181 painter.end() 182 183 rendered_file_path = os.path.join(self.basetestpath, 'test_renderpage.png') 184 output_image.save(rendered_file_path, "PNG") 185 self.assertTrue(self.checkImage('renderpage', 'renderpage', rendered_file_path)) 186 187 def testRenderPageToImage(self): 188 l = QgsLayout(QgsProject.instance()) 189 l.initializeDefaults() 190 191 # add some items 192 item1 = QgsLayoutItemShape(l) 193 item1.attemptSetSceneRect(QRectF(10, 20, 100, 150)) 194 fill = QgsSimpleFillSymbolLayer() 195 fill_symbol = QgsFillSymbol() 196 fill_symbol.changeSymbolLayer(0, fill) 197 fill.setColor(Qt.green) 198 fill.setStrokeStyle(Qt.NoPen) 199 item1.setSymbol(fill_symbol) 200 l.addItem(item1) 201 202 exporter = QgsLayoutExporter(l) 203 size = QSize(1122, 794) 204 205 # bad page numbers 206 image = exporter.renderPageToImage(-1, size) 207 self.assertTrue(image.isNull()) 208 image = exporter.renderPageToImage(1, size) 209 self.assertTrue(image.isNull()) 210 211 # good page 212 image = exporter.renderPageToImage(0, size) 213 self.assertFalse(image.isNull()) 214 215 rendered_file_path = os.path.join(self.basetestpath, 'test_rendertoimagepage.png') 216 image.save(rendered_file_path, "PNG") 217 self.assertTrue(self.checkImage('rendertoimagepage', 'rendertoimagepage', rendered_file_path)) 218 219 def testRenderRegion(self): 220 l = QgsLayout(QgsProject.instance()) 221 l.initializeDefaults() 222 223 # add a guide, to ensure it is not included in export 224 g1 = QgsLayoutGuide(Qt.Horizontal, QgsLayoutMeasurement(15, QgsUnitTypes.LayoutMillimeters), l.pageCollection().page(0)) 225 l.guides().addGuide(g1) 226 227 # add some items 228 item1 = QgsLayoutItemShape(l) 229 item1.attemptSetSceneRect(QRectF(10, 20, 100, 150)) 230 fill = QgsSimpleFillSymbolLayer() 231 fill_symbol = QgsFillSymbol() 232 fill_symbol.changeSymbolLayer(0, fill) 233 fill.setColor(Qt.green) 234 fill.setStrokeStyle(Qt.NoPen) 235 item1.setSymbol(fill_symbol) 236 l.addItem(item1) 237 238 # get width/height, create image and render the composition to it 239 size = QSize(560, 509) 240 output_image = QImage(size, QImage.Format_RGB32) 241 242 output_image.setDotsPerMeterX(self.dots_per_meter) 243 output_image.setDotsPerMeterY(self.dots_per_meter) 244 QgsMultiRenderChecker.drawBackground(output_image) 245 painter = QPainter(output_image) 246 exporter = QgsLayoutExporter(l) 247 248 exporter.renderRegion(painter, QRectF(5, 10, 110, 100)) 249 painter.end() 250 251 rendered_file_path = os.path.join(self.basetestpath, 'test_renderregion.png') 252 output_image.save(rendered_file_path, "PNG") 253 self.assertTrue(self.checkImage('renderregion', 'renderregion', rendered_file_path)) 254 255 def testRenderRegionToImage(self): 256 l = QgsLayout(QgsProject.instance()) 257 l.initializeDefaults() 258 259 # add some items 260 item1 = QgsLayoutItemShape(l) 261 item1.attemptSetSceneRect(QRectF(10, 20, 100, 150)) 262 fill = QgsSimpleFillSymbolLayer() 263 fill_symbol = QgsFillSymbol() 264 fill_symbol.changeSymbolLayer(0, fill) 265 fill.setColor(Qt.green) 266 fill.setStrokeStyle(Qt.NoPen) 267 item1.setSymbol(fill_symbol) 268 l.addItem(item1) 269 270 exporter = QgsLayoutExporter(l) 271 size = QSize(560, 509) 272 273 image = exporter.renderRegionToImage(QRectF(5, 10, 110, 100), size) 274 self.assertFalse(image.isNull()) 275 276 rendered_file_path = os.path.join(self.basetestpath, 'test_rendertoimageregionsize.png') 277 image.save(rendered_file_path, "PNG") 278 self.assertTrue(self.checkImage('rendertoimageregionsize', 'rendertoimageregionsize', rendered_file_path)) 279 280 # using layout dpi 281 l.renderContext().setDpi(40) 282 image = exporter.renderRegionToImage(QRectF(5, 10, 110, 100)) 283 self.assertFalse(image.isNull()) 284 285 rendered_file_path = os.path.join(self.basetestpath, 'test_rendertoimageregiondpi.png') 286 image.save(rendered_file_path, "PNG") 287 self.assertTrue(self.checkImage('rendertoimageregiondpi', 'rendertoimageregiondpi', rendered_file_path)) 288 289 # overriding dpi 290 image = exporter.renderRegionToImage(QRectF(5, 10, 110, 100), QSize(), 80) 291 self.assertFalse(image.isNull()) 292 293 rendered_file_path = os.path.join(self.basetestpath, 'test_rendertoimageregionoverridedpi.png') 294 image.save(rendered_file_path, "PNG") 295 self.assertTrue(self.checkImage('rendertoimageregionoverridedpi', 'rendertoimageregionoverridedpi', rendered_file_path)) 296 297 def testExportToImage(self): 298 md = QgsProject.instance().metadata() 299 md.setTitle('proj title') 300 md.setAuthor('proj author') 301 md.setCreationDateTime(QDateTime(QDate(2011, 5, 3), QTime(9, 4, 5), QTimeZone(36000))) 302 md.setIdentifier('proj identifier') 303 md.setAbstract('proj abstract') 304 md.setKeywords({'kw': ['kw1', 'kw2'], 'KWx': ['kw3', 'kw4']}) 305 QgsProject.instance().setMetadata(md) 306 l = QgsLayout(QgsProject.instance()) 307 l.initializeDefaults() 308 309 # add a second page 310 page2 = QgsLayoutItemPage(l) 311 page2.setPageSize('A5') 312 l.pageCollection().addPage(page2) 313 314 # add some items 315 item1 = QgsLayoutItemShape(l) 316 item1.attemptSetSceneRect(QRectF(10, 20, 100, 150)) 317 fill = QgsSimpleFillSymbolLayer() 318 fill_symbol = QgsFillSymbol() 319 fill_symbol.changeSymbolLayer(0, fill) 320 fill.setColor(Qt.green) 321 fill.setStrokeStyle(Qt.NoPen) 322 item1.setSymbol(fill_symbol) 323 l.addItem(item1) 324 325 item2 = QgsLayoutItemShape(l) 326 item2.attemptSetSceneRect(QRectF(10, 20, 100, 150)) 327 item2.attemptMove(QgsLayoutPoint(10, 20), page=1) 328 fill = QgsSimpleFillSymbolLayer() 329 fill_symbol = QgsFillSymbol() 330 fill_symbol.changeSymbolLayer(0, fill) 331 fill.setColor(Qt.cyan) 332 fill.setStrokeStyle(Qt.NoPen) 333 item2.setSymbol(fill_symbol) 334 l.addItem(item2) 335 336 exporter = QgsLayoutExporter(l) 337 # setup settings 338 settings = QgsLayoutExporter.ImageExportSettings() 339 settings.dpi = 80 340 341 rendered_file_path = os.path.join(self.basetestpath, 'test_exporttoimagedpi.png') 342 self.assertEqual(exporter.exportToImage(rendered_file_path, settings), QgsLayoutExporter.Success) 343 344 self.assertTrue(self.checkImage('exporttoimagedpi_page1', 'exporttoimagedpi_page1', rendered_file_path)) 345 page2_path = os.path.join(self.basetestpath, 'test_exporttoimagedpi_2.png') 346 self.assertTrue(self.checkImage('exporttoimagedpi_page2', 'exporttoimagedpi_page2', page2_path)) 347 348 for f in (rendered_file_path, page2_path): 349 d = gdal.Open(f) 350 metadata = d.GetMetadata() 351 self.assertEqual(metadata['Author'], 'proj author') 352 self.assertEqual(metadata['Created'], '2011-05-03T09:04:05+10:00') 353 self.assertEqual(metadata['Keywords'], 'KWx: kw3,kw4;kw: kw1,kw2') 354 self.assertEqual(metadata['Subject'], 'proj abstract') 355 self.assertEqual(metadata['Title'], 'proj title') 356 357 # crop to contents 358 settings.cropToContents = True 359 settings.cropMargins = QgsMargins(10, 20, 30, 40) 360 361 rendered_file_path = os.path.join(self.basetestpath, 'test_exporttoimagecropped.png') 362 self.assertEqual(exporter.exportToImage(rendered_file_path, settings), QgsLayoutExporter.Success) 363 364 self.assertTrue(self.checkImage('exporttoimagecropped_page1', 'exporttoimagecropped_page1', rendered_file_path)) 365 page2_path = os.path.join(self.basetestpath, 'test_exporttoimagecropped_2.png') 366 self.assertTrue(self.checkImage('exporttoimagecropped_page2', 'exporttoimagecropped_page2', page2_path)) 367 368 # specific pages 369 settings.cropToContents = False 370 settings.pages = [1] 371 372 rendered_file_path = os.path.join(self.basetestpath, 'test_exporttoimagepages.png') 373 self.assertEqual(exporter.exportToImage(rendered_file_path, settings), QgsLayoutExporter.Success) 374 375 self.assertFalse(os.path.exists(rendered_file_path)) 376 page2_path = os.path.join(self.basetestpath, 'test_exporttoimagepages_2.png') 377 self.assertTrue(self.checkImage('exporttoimagedpi_page2', 'exporttoimagedpi_page2', page2_path)) 378 379 # image size 380 settings.imageSize = QSize(600, 851) 381 rendered_file_path = os.path.join(self.basetestpath, 'test_exporttoimagesize.png') 382 self.assertEqual(exporter.exportToImage(rendered_file_path, settings), QgsLayoutExporter.Success) 383 self.assertFalse(os.path.exists(rendered_file_path)) 384 page2_path = os.path.join(self.basetestpath, 'test_exporttoimagesize_2.png') 385 self.assertTrue(self.checkImage('exporttoimagesize_page2', 'exporttoimagesize_page2', page2_path)) 386 387 # image size with incorrect aspect ratio 388 # this can happen as a result of data defined page sizes 389 settings.imageSize = QSize(851, 600) 390 rendered_file_path = os.path.join(self.basetestpath, 'test_exporttoimagesizebadaspect.png') 391 self.assertEqual(exporter.exportToImage(rendered_file_path, settings), QgsLayoutExporter.Success) 392 393 page2_path = os.path.join(self.basetestpath, 'test_exporttoimagesizebadaspect_2.png') 394 im = QImage(page2_path) 395 self.assertTrue(self.checkImage('exporttoimagesize_badaspect', 'exporttoimagedpi_page2', page2_path), '{}x{}'.format(im.width(), im.height())) 396 397 def testExportToPdf(self): 398 md = QgsProject.instance().metadata() 399 md.setTitle('proj title') 400 md.setAuthor('proj author') 401 md.setCreationDateTime(QDateTime(QDate(2011, 5, 3), QTime(9, 4, 5), QTimeZone(36000))) 402 md.setIdentifier('proj identifier') 403 md.setAbstract('proj abstract') 404 md.setKeywords({'kw': ['kw1', 'kw2'], 'KWx': ['kw3', 'kw4']}) 405 QgsProject.instance().setMetadata(md) 406 407 l = QgsLayout(QgsProject.instance()) 408 l.initializeDefaults() 409 410 # add a second page 411 page2 = QgsLayoutItemPage(l) 412 page2.setPageSize('A5') 413 l.pageCollection().addPage(page2) 414 415 # add some items 416 item1 = QgsLayoutItemShape(l) 417 item1.attemptSetSceneRect(QRectF(10, 20, 100, 150)) 418 fill = QgsSimpleFillSymbolLayer() 419 fill_symbol = QgsFillSymbol() 420 fill_symbol.changeSymbolLayer(0, fill) 421 fill.setColor(Qt.green) 422 fill.setStrokeStyle(Qt.NoPen) 423 item1.setSymbol(fill_symbol) 424 l.addItem(item1) 425 426 item2 = QgsLayoutItemShape(l) 427 item2.attemptSetSceneRect(QRectF(10, 20, 100, 150)) 428 item2.attemptMove(QgsLayoutPoint(10, 20), page=1) 429 fill = QgsSimpleFillSymbolLayer() 430 fill_symbol = QgsFillSymbol() 431 fill_symbol.changeSymbolLayer(0, fill) 432 fill.setColor(Qt.cyan) 433 fill.setStrokeStyle(Qt.NoPen) 434 item2.setSymbol(fill_symbol) 435 l.addItem(item2) 436 437 exporter = QgsLayoutExporter(l) 438 # setup settings 439 settings = QgsLayoutExporter.PdfExportSettings() 440 settings.dpi = 80 441 settings.rasterizeWholeImage = False 442 settings.forceVectorOutput = False 443 settings.exportMetadata = True 444 445 pdf_file_path = os.path.join(self.basetestpath, 'test_exporttopdfdpi.pdf') 446 self.assertEqual(exporter.exportToPdf(pdf_file_path, settings), QgsLayoutExporter.Success) 447 self.assertTrue(os.path.exists(pdf_file_path)) 448 449 rendered_page_1 = os.path.join(self.basetestpath, 'test_exporttopdfdpi.png') 450 dpi = 80 451 pdfToPng(pdf_file_path, rendered_page_1, dpi=dpi, page=1) 452 rendered_page_2 = os.path.join(self.basetestpath, 'test_exporttopdfdpi2.png') 453 pdfToPng(pdf_file_path, rendered_page_2, dpi=dpi, page=2) 454 455 self.assertTrue(self.checkImage('exporttopdfdpi_page1', 'exporttopdfdpi_page1', rendered_page_1, size_tolerance=1)) 456 self.assertTrue(self.checkImage('exporttopdfdpi_page2', 'exporttopdfdpi_page2', rendered_page_2, size_tolerance=1)) 457 458 d = gdal.Open(pdf_file_path) 459 metadata = d.GetMetadata() 460 self.assertEqual(metadata['AUTHOR'], 'proj author') 461 self.assertEqual(metadata['CREATION_DATE'], "D:20110503090405+10'0'") 462 self.assertEqual(metadata['KEYWORDS'], 'KWx: kw3,kw4;kw: kw1,kw2') 463 self.assertEqual(metadata['SUBJECT'], 'proj abstract') 464 self.assertEqual(metadata['TITLE'], 'proj title') 465 466 def testExportToPdfGeoreference(self): 467 md = QgsProject.instance().metadata() 468 md.setTitle('proj title') 469 md.setAuthor('proj author') 470 md.setCreationDateTime(QDateTime(QDate(2011, 5, 3), QTime(9, 4, 5), QTimeZone(36000))) 471 md.setIdentifier('proj identifier') 472 md.setAbstract('proj abstract') 473 md.setKeywords({'kw': ['kw1', 'kw2'], 'KWx': ['kw3', 'kw4']}) 474 QgsProject.instance().setMetadata(md) 475 476 l = QgsLayout(QgsProject.instance()) 477 l.initializeDefaults() 478 479 # add some items 480 map = QgsLayoutItemMap(l) 481 map.attemptSetSceneRect(QRectF(30, 60, 200, 100)) 482 extent = QgsRectangle(333218, 1167809, 348781, 1180875) 483 map.setCrs(QgsCoordinateReferenceSystem('EPSG:3148')) 484 map.setExtent(extent) 485 l.addLayoutItem(map) 486 487 exporter = QgsLayoutExporter(l) 488 # setup settings 489 settings = QgsLayoutExporter.PdfExportSettings() 490 settings.dpi = 96 491 settings.rasterizeWholeImage = False 492 settings.forceVectorOutput = False 493 settings.appendGeoreference = True 494 settings.exportMetadata = False 495 496 pdf_file_path = os.path.join(self.basetestpath, 'test_exporttopdf_georeference.pdf') 497 self.assertEqual(exporter.exportToPdf(pdf_file_path, settings), QgsLayoutExporter.Success) 498 self.assertTrue(os.path.exists(pdf_file_path)) 499 500 d = gdal.Open(pdf_file_path) 501 502 # check if georeferencing was successful 503 geoTransform = d.GetGeoTransform() 504 self.assertAlmostEqual(geoTransform[0], 330883.5499999996, 4) 505 self.assertAlmostEqual(geoTransform[1], 13.184029109934016, 4) 506 self.assertAlmostEqual(geoTransform[2], 0.0, 4) 507 self.assertAlmostEqual(geoTransform[3], 1185550.768915511, 4) 508 self.assertAlmostEqual(geoTransform[4], 0.0, 4) 509 self.assertAlmostEqual(geoTransform[5], -13.183886222186642, 4) 510 511 # check that the metadata has _not_ been added to the exported PDF 512 metadata = d.GetMetadata() 513 self.assertFalse('AUTHOR' in metadata) 514 515 exporter = QgsLayoutExporter(l) 516 # setup settings 517 settings = QgsLayoutExporter.PdfExportSettings() 518 settings.dpi = 96 519 settings.rasterizeWholeImage = False 520 settings.forceVectorOutput = False 521 settings.appendGeoreference = False 522 settings.exportMetadata = False 523 524 pdf_file_path = os.path.join(self.basetestpath, 'test_exporttopdf_nogeoreference.pdf') 525 self.assertEqual(exporter.exportToPdf(pdf_file_path, settings), QgsLayoutExporter.Success) 526 self.assertTrue(os.path.exists(pdf_file_path)) 527 528 d = gdal.Open(pdf_file_path) 529 # check that georeference information has _not_ been added to the exported PDF 530 self.assertEqual(d.GetGeoTransform(), (0.0, 1.0, 0.0, 0.0, 0.0, 1.0)) 531 532 def testExportToPdfSkipFirstPage(self): 533 l = QgsLayout(QgsProject.instance()) 534 l.initializeDefaults() 535 536 # page 1 is excluded from export 537 page1 = l.pageCollection().page(0) 538 page1.setExcludeFromExports(True) 539 540 # add a second page 541 page2 = QgsLayoutItemPage(l) 542 page2.setPageSize('A5') 543 l.pageCollection().addPage(page2) 544 545 item2 = QgsLayoutItemShape(l) 546 item2.attemptSetSceneRect(QRectF(10, 20, 100, 150)) 547 item2.attemptMove(QgsLayoutPoint(10, 20), page=1) 548 fill = QgsSimpleFillSymbolLayer() 549 fill_symbol = QgsFillSymbol() 550 fill_symbol.changeSymbolLayer(0, fill) 551 fill.setColor(Qt.cyan) 552 fill.setStrokeStyle(Qt.NoPen) 553 item2.setSymbol(fill_symbol) 554 l.addItem(item2) 555 556 exporter = QgsLayoutExporter(l) 557 # setup settings 558 settings = QgsLayoutExporter.PdfExportSettings() 559 settings.dpi = 80 560 settings.rasterizeWholeImage = False 561 settings.forceVectorOutput = False 562 settings.exportMetadata = True 563 564 pdf_file_path = os.path.join(self.basetestpath, 'test_exporttopdfdpi_skip_first.pdf') 565 self.assertEqual(exporter.exportToPdf(pdf_file_path, settings), QgsLayoutExporter.Success) 566 self.assertTrue(os.path.exists(pdf_file_path)) 567 568 rendered_page_1 = os.path.join(self.basetestpath, 'test_exporttopdfdpi_skip_first.png') 569 dpi = 80 570 pdfToPng(pdf_file_path, rendered_page_1, dpi=dpi, page=1) 571 572 self.assertTrue(self.checkImage('test_exporttopdfdpi_skip_first', 'exporttopdfdpi_page2', rendered_page_1, size_tolerance=1)) 573 574 def testExportToSvg(self): 575 md = QgsProject.instance().metadata() 576 md.setTitle('proj title') 577 md.setAuthor('proj author') 578 md.setCreationDateTime(QDateTime(QDate(2011, 5, 3), QTime(9, 4, 5), QTimeZone(36000))) 579 md.setIdentifier('proj identifier') 580 md.setAbstract('proj abstract') 581 md.setKeywords({'kw': ['kw1', 'kw2']}) 582 QgsProject.instance().setMetadata(md) 583 l = QgsLayout(QgsProject.instance()) 584 l.initializeDefaults() 585 586 # add a second page 587 page2 = QgsLayoutItemPage(l) 588 page2.setPageSize('A5') 589 l.pageCollection().addPage(page2) 590 591 # add some items 592 item1 = QgsLayoutItemShape(l) 593 item1.attemptSetSceneRect(QRectF(10, 20, 100, 150)) 594 fill = QgsSimpleFillSymbolLayer() 595 fill_symbol = QgsFillSymbol() 596 fill_symbol.changeSymbolLayer(0, fill) 597 fill.setColor(Qt.green) 598 fill.setStrokeStyle(Qt.NoPen) 599 item1.setSymbol(fill_symbol) 600 l.addItem(item1) 601 602 item2 = QgsLayoutItemShape(l) 603 item2.attemptSetSceneRect(QRectF(10, 20, 100, 150)) 604 item2.attemptMove(QgsLayoutPoint(10, 20), page=1) 605 fill = QgsSimpleFillSymbolLayer() 606 fill_symbol = QgsFillSymbol() 607 fill_symbol.changeSymbolLayer(0, fill) 608 fill.setColor(Qt.cyan) 609 fill.setStrokeStyle(Qt.NoPen) 610 item2.setSymbol(fill_symbol) 611 l.addItem(item2) 612 613 exporter = QgsLayoutExporter(l) 614 # setup settings 615 settings = QgsLayoutExporter.SvgExportSettings() 616 settings.dpi = 80 617 settings.forceVectorOutput = False 618 settings.exportMetadata = True 619 620 svg_file_path = os.path.join(self.basetestpath, 'test_exporttosvgdpi.svg') 621 svg_file_path_2 = os.path.join(self.basetestpath, 'test_exporttosvgdpi_2.svg') 622 self.assertEqual(exporter.exportToSvg(svg_file_path, settings), QgsLayoutExporter.Success) 623 self.assertTrue(os.path.exists(svg_file_path)) 624 self.assertTrue(os.path.exists(svg_file_path_2)) 625 626 # metadata 627 def checkMetadata(f, expected): 628 # ideally we'd check the path too - but that's very complex given that 629 # the output from Qt svg generator isn't valid XML, and no Python standard library 630 # xml parser handles invalid xml... 631 self.assertEqual('proj title' in open(f).read(), expected) 632 self.assertEqual('proj author' in open(f).read(), expected) 633 self.assertEqual('proj identifier' in open(f).read(), expected) 634 self.assertEqual('2011-05-03' in open(f).read(), expected) 635 self.assertEqual('proj abstract' in open(f).read(), expected) 636 self.assertEqual('kw1' in open(f).read(), expected) 637 self.assertEqual('kw2' in open(f).read(), expected) 638 self.assertEqual('xmlns:cc="http://creativecommons.org/ns#"' in open(f).read(), expected) 639 640 for f in [svg_file_path, svg_file_path_2]: 641 checkMetadata(f, True) 642 643 rendered_page_1 = os.path.join(self.basetestpath, 'test_exporttosvgdpi.png') 644 svgToPng(svg_file_path, rendered_page_1, width=936) 645 rendered_page_2 = os.path.join(self.basetestpath, 'test_exporttosvgdpi2.png') 646 svgToPng(svg_file_path_2, rendered_page_2, width=467) 647 648 self.assertTrue(self.checkImage('exporttosvgdpi_page1', 'exporttopdfdpi_page1', rendered_page_1, size_tolerance=1)) 649 self.assertTrue(self.checkImage('exporttosvgdpi_page2', 'exporttopdfdpi_page2', rendered_page_2, size_tolerance=1)) 650 651 # no metadata 652 settings.exportMetadata = False 653 self.assertEqual(exporter.exportToSvg(svg_file_path, settings), QgsLayoutExporter.Success) 654 for f in [svg_file_path, svg_file_path_2]: 655 checkMetadata(f, False) 656 657 # layered 658 settings.exportAsLayers = True 659 settings.exportMetadata = True 660 661 svg_file_path = os.path.join(self.basetestpath, 'test_exporttosvglayered.svg') 662 svg_file_path_2 = os.path.join(self.basetestpath, 'test_exporttosvglayered_2.svg') 663 self.assertEqual(exporter.exportToSvg(svg_file_path, settings), QgsLayoutExporter.Success) 664 self.assertTrue(os.path.exists(svg_file_path)) 665 self.assertTrue(os.path.exists(svg_file_path_2)) 666 667 rendered_page_1 = os.path.join(self.basetestpath, 'test_exporttosvglayered.png') 668 svgToPng(svg_file_path, rendered_page_1, width=936) 669 rendered_page_2 = os.path.join(self.basetestpath, 'test_exporttosvglayered2.png') 670 svgToPng(svg_file_path_2, rendered_page_2, width=467) 671 672 self.assertTrue(self.checkImage('exporttosvglayered_page1', 'exporttopdfdpi_page1', rendered_page_1, size_tolerance=1)) 673 self.assertTrue(self.checkImage('exporttosvglayered_page2', 'exporttopdfdpi_page2', rendered_page_2, size_tolerance=1)) 674 675 for f in [svg_file_path, svg_file_path_2]: 676 checkMetadata(f, True) 677 678 # layered no metadata 679 settings.exportAsLayers = True 680 settings.exportMetadata = False 681 self.assertEqual(exporter.exportToSvg(svg_file_path, settings), QgsLayoutExporter.Success) 682 for f in [svg_file_path, svg_file_path_2]: 683 checkMetadata(f, False) 684 685 def testExportToSvgTextRenderFormat(self): 686 l = QgsLayout(QgsProject.instance()) 687 l.initializeDefaults() 688 689 # add a map and scalebar 690 mapitem = QgsLayoutItemMap(l) 691 mapitem.attemptSetSceneRect(QRectF(110, 120, 200, 250)) 692 mapitem.zoomToExtent(QgsRectangle(1, 1, 10, 10)) 693 mapitem.setScale(666) # unlikely to appear in the SVG by accident... unless... oh no! RUN! 694 l.addItem(mapitem) 695 696 item1 = QgsLayoutItemScaleBar(l) 697 item1.attemptSetSceneRect(QRectF(10, 20, 100, 150)) 698 item1.setLinkedMap(mapitem) 699 item1.setStyle('Numeric') 700 l.addItem(item1) 701 702 exporter = QgsLayoutExporter(l) 703 # setup settings 704 settings = QgsLayoutExporter.SvgExportSettings() 705 settings.dpi = 80 706 settings.forceVectorOutput = False 707 settings.exportMetadata = True 708 settings.textRenderFormat = QgsRenderContext.TextFormatAlwaysText 709 710 svg_file_path = os.path.join(self.basetestpath, 'test_exporttosvgtextformattext.svg') 711 self.assertEqual(exporter.exportToSvg(svg_file_path, settings), QgsLayoutExporter.Success) 712 self.assertTrue(os.path.exists(svg_file_path)) 713 714 # expect svg to contain a text object with the scale 715 with open(svg_file_path, 'r') as f: 716 lines = ''.join(f.readlines()) 717 self.assertIn('<text', lines) 718 self.assertIn('>1:666<', lines) 719 720 # force use of outlines 721 os.unlink(svg_file_path) 722 settings.textRenderFormat = QgsRenderContext.TextFormatAlwaysOutlines 723 self.assertEqual(exporter.exportToSvg(svg_file_path, settings), QgsLayoutExporter.Success) 724 self.assertTrue(os.path.exists(svg_file_path)) 725 726 # expect svg NOT to contain a text object with the scale 727 with open(svg_file_path, 'r') as f: 728 lines = ''.join(f.readlines()) 729 self.assertNotIn('<text', lines) 730 self.assertNotIn('>1:666<', lines) 731 732 def testPrint(self): 733 l = QgsLayout(QgsProject.instance()) 734 l.initializeDefaults() 735 736 # add a second page 737 page2 = QgsLayoutItemPage(l) 738 page2.setPageSize('A5') 739 l.pageCollection().addPage(page2) 740 741 # add some items 742 item1 = QgsLayoutItemShape(l) 743 item1.attemptSetSceneRect(QRectF(10, 20, 100, 150)) 744 fill = QgsSimpleFillSymbolLayer() 745 fill_symbol = QgsFillSymbol() 746 fill_symbol.changeSymbolLayer(0, fill) 747 fill.setColor(Qt.green) 748 fill.setStrokeStyle(Qt.NoPen) 749 item1.setSymbol(fill_symbol) 750 l.addItem(item1) 751 752 item2 = QgsLayoutItemShape(l) 753 item2.attemptSetSceneRect(QRectF(10, 20, 100, 150)) 754 item2.attemptMove(QgsLayoutPoint(10, 20), page=1) 755 fill = QgsSimpleFillSymbolLayer() 756 fill_symbol = QgsFillSymbol() 757 fill_symbol.changeSymbolLayer(0, fill) 758 fill.setColor(Qt.cyan) 759 fill.setStrokeStyle(Qt.NoPen) 760 item2.setSymbol(fill_symbol) 761 l.addItem(item2) 762 763 exporter = QgsLayoutExporter(l) 764 # setup settings 765 settings = QgsLayoutExporter.PrintExportSettings() 766 settings.dpi = 80 767 settings.rasterizeWholeImage = False 768 769 pdf_file_path = os.path.join(self.basetestpath, 'test_printdpi.pdf') 770 # make a qprinter directed to pdf 771 printer = QPrinter() 772 printer.setOutputFileName(pdf_file_path) 773 printer.setOutputFormat(QPrinter.PdfFormat) 774 775 self.assertEqual(exporter.print(printer, settings), QgsLayoutExporter.Success) 776 self.assertTrue(os.path.exists(pdf_file_path)) 777 778 rendered_page_1 = os.path.join(self.basetestpath, 'test_exporttopdfdpi.png') 779 dpi = 80 780 pdfToPng(pdf_file_path, rendered_page_1, dpi=dpi, page=1) 781 rendered_page_2 = os.path.join(self.basetestpath, 'test_exporttopdfdpi2.png') 782 pdfToPng(pdf_file_path, rendered_page_2, dpi=dpi, page=2) 783 784 self.assertTrue(self.checkImage('printdpi_page1', 'exporttopdfdpi_page1', rendered_page_1, size_tolerance=1)) 785 self.assertTrue(self.checkImage('printdpi_page2', 'exporttopdfdpi_page2', rendered_page_2, size_tolerance=1)) 786 787 def testExportWorldFile(self): 788 l = QgsLayout(QgsProject.instance()) 789 l.initializeDefaults() 790 791 # add some items 792 map = QgsLayoutItemMap(l) 793 map.attemptSetSceneRect(QRectF(30, 60, 200, 100)) 794 extent = QgsRectangle(2000, 2800, 2500, 2900) 795 map.setExtent(extent) 796 l.addLayoutItem(map) 797 798 exporter = QgsLayoutExporter(l) 799 # setup settings 800 settings = QgsLayoutExporter.ImageExportSettings() 801 settings.dpi = 80 802 settings.generateWorldFile = False 803 804 rendered_file_path = os.path.join(self.basetestpath, 'test_exportwithworldfile.png') 805 world_file_path = os.path.join(self.basetestpath, 'test_exportwithworldfile.pgw') 806 self.assertEqual(exporter.exportToImage(rendered_file_path, settings), QgsLayoutExporter.Success) 807 self.assertTrue(os.path.exists(rendered_file_path)) 808 self.assertFalse(os.path.exists(world_file_path)) 809 810 # with world file 811 settings.generateWorldFile = True 812 rendered_file_path = os.path.join(self.basetestpath, 'test_exportwithworldfile.png') 813 self.assertEqual(exporter.exportToImage(rendered_file_path, settings), QgsLayoutExporter.Success) 814 self.assertTrue(os.path.exists(rendered_file_path)) 815 self.assertTrue(os.path.exists(world_file_path)) 816 817 lines = tuple(open(world_file_path, 'r')) 818 values = [float(f) for f in lines] 819 self.assertAlmostEqual(values[0], 0.794117647059, 2) 820 self.assertAlmostEqual(values[1], 0.0, 2) 821 self.assertAlmostEqual(values[2], 0.0, 2) 822 self.assertAlmostEqual(values[3], -0.794251134644, 2) 823 self.assertAlmostEqual(values[4], 1925.000000000000, 2) 824 self.assertAlmostEqual(values[5], 3050.000000000000, 2) 825 826 def testExcludePagesImage(self): 827 l = QgsLayout(QgsProject.instance()) 828 l.initializeDefaults() 829 # add a second page 830 page2 = QgsLayoutItemPage(l) 831 page2.setPageSize('A5') 832 l.pageCollection().addPage(page2) 833 834 exporter = QgsLayoutExporter(l) 835 # setup settings 836 settings = QgsLayoutExporter.ImageExportSettings() 837 settings.dpi = 80 838 settings.generateWorldFile = False 839 840 rendered_file_path = os.path.join(self.basetestpath, 'test_exclude_export.png') 841 details = QgsLayoutExporter.PageExportDetails() 842 details.directory = self.basetestpath 843 details.baseName = 'test_exclude_export' 844 details.extension = 'png' 845 details.page = 0 846 847 self.assertEqual(exporter.exportToImage(rendered_file_path, settings), QgsLayoutExporter.Success) 848 self.assertTrue(os.path.exists(exporter.generateFileName(details))) 849 details.page = 1 850 self.assertTrue(os.path.exists(exporter.generateFileName(details))) 851 852 # exclude a page 853 l.pageCollection().page(0).setExcludeFromExports(True) 854 rendered_file_path = os.path.join(self.basetestpath, 'test_exclude_export_excluded.png') 855 details.baseName = 'test_exclude_export_excluded' 856 details.page = 0 857 self.assertEqual(exporter.exportToImage(rendered_file_path, settings), QgsLayoutExporter.Success) 858 self.assertFalse(os.path.exists(exporter.generateFileName(details))) 859 details.page = 1 860 self.assertTrue(os.path.exists(exporter.generateFileName(details))) 861 862 # exclude second page 863 l.pageCollection().page(1).setExcludeFromExports(True) 864 rendered_file_path = os.path.join(self.basetestpath, 'test_exclude_export_excluded_all.png') 865 details.baseName = 'test_exclude_export_excluded_all' 866 details.page = 0 867 self.assertEqual(exporter.exportToImage(rendered_file_path, settings), QgsLayoutExporter.Success) 868 self.assertFalse(os.path.exists(exporter.generateFileName(details))) 869 details.page = 1 870 self.assertFalse(os.path.exists(exporter.generateFileName(details))) 871 872 def testPageFileName(self): 873 l = QgsLayout(QgsProject.instance()) 874 exporter = QgsLayoutExporter(l) 875 details = QgsLayoutExporter.PageExportDetails() 876 details.directory = '/tmp/output' 877 details.baseName = 'my_maps' 878 details.extension = 'png' 879 details.page = 0 880 self.assertEqual(exporter.generateFileName(details), '/tmp/output/my_maps.png') 881 details.page = 1 882 self.assertEqual(exporter.generateFileName(details), '/tmp/output/my_maps_2.png') 883 details.page = 2 884 self.assertEqual(exporter.generateFileName(details), '/tmp/output/my_maps_3.png') 885 886 def prepareIteratorLayout(self): 887 layer_path = os.path.join(TEST_DATA_DIR, 'france_parts.shp') 888 layer = QgsVectorLayer(layer_path, 'test', "ogr") 889 890 project = QgsProject() 891 project.addMapLayers([layer]) 892 # select epsg:2154 893 crs = QgsCoordinateReferenceSystem('epsg:2154') 894 project.setCrs(crs) 895 896 layout = QgsPrintLayout(project) 897 layout.initializeDefaults() 898 899 # fix the renderer, fill with green 900 props = {"color": "0,127,0", "outline_width": "4", "outline_color": '255,255,255'} 901 fillSymbol = QgsFillSymbol.createSimple(props) 902 renderer = QgsSingleSymbolRenderer(fillSymbol) 903 layer.setRenderer(renderer) 904 905 # the atlas map 906 atlas_map = QgsLayoutItemMap(layout) 907 atlas_map.attemptSetSceneRect(QRectF(20, 20, 130, 130)) 908 atlas_map.setFrameEnabled(True) 909 atlas_map.setLayers([layer]) 910 layout.addLayoutItem(atlas_map) 911 912 # the atlas 913 atlas = layout.atlas() 914 atlas.setCoverageLayer(layer) 915 atlas.setEnabled(True) 916 917 atlas_map.setExtent( 918 QgsRectangle(332719.06221504929, 6765214.5887386119, 560957.85090677091, 6993453.3774303338)) 919 920 atlas_map.setAtlasDriven(True) 921 atlas_map.setAtlasScalingMode(QgsLayoutItemMap.Auto) 922 atlas_map.setAtlasMargin(0.10) 923 924 return project, layout 925 926 def testIteratorToImages(self): 927 project, layout = self.prepareIteratorLayout() 928 atlas = layout.atlas() 929 atlas.setFilenameExpression("'test_exportiteratortoimage_' || \"NAME_1\"") 930 931 # setup settings 932 settings = QgsLayoutExporter.ImageExportSettings() 933 settings.dpi = 80 934 935 result, error = QgsLayoutExporter.exportToImage(atlas, self.basetestpath + '/', 'png', settings) 936 self.assertEqual(result, QgsLayoutExporter.Success, error) 937 938 page1_path = os.path.join(self.basetestpath, 'test_exportiteratortoimage_Basse-Normandie.png') 939 self.assertTrue(self.checkImage('iteratortoimage1', 'iteratortoimage1', page1_path)) 940 page2_path = os.path.join(self.basetestpath, 'test_exportiteratortoimage_Bretagne.png') 941 self.assertTrue(self.checkImage('iteratortoimage2', 'iteratortoimage2', page2_path)) 942 page3_path = os.path.join(self.basetestpath, 'test_exportiteratortoimage_Centre.png') 943 self.assertTrue(os.path.exists(page3_path)) 944 page4_path = os.path.join(self.basetestpath, 'test_exportiteratortoimage_Pays de la Loire.png') 945 self.assertTrue(os.path.exists(page4_path)) 946 947 def testIteratorToSvgs(self): 948 project, layout = self.prepareIteratorLayout() 949 atlas = layout.atlas() 950 atlas.setFilenameExpression("'test_exportiteratortosvg_' || \"NAME_1\"") 951 952 # setup settings 953 settings = QgsLayoutExporter.SvgExportSettings() 954 settings.dpi = 80 955 settings.forceVectorOutput = False 956 957 result, error = QgsLayoutExporter.exportToSvg(atlas, self.basetestpath + '/', settings) 958 self.assertEqual(result, QgsLayoutExporter.Success, error) 959 960 page1_path = os.path.join(self.basetestpath, 'test_exportiteratortosvg_Basse-Normandie.svg') 961 rendered_page_1 = os.path.join(self.basetestpath, 'test_exportiteratortosvg_Basse-Normandie.png') 962 svgToPng(page1_path, rendered_page_1, width=935) 963 self.assertTrue(self.checkImage('iteratortosvg1', 'iteratortoimage1', rendered_page_1, size_tolerance=2)) 964 page2_path = os.path.join(self.basetestpath, 'test_exportiteratortosvg_Bretagne.svg') 965 rendered_page_2 = os.path.join(self.basetestpath, 'test_exportiteratortosvg_Bretagne.png') 966 svgToPng(page2_path, rendered_page_2, width=935) 967 self.assertTrue(self.checkImage('iteratortosvg2', 'iteratortoimage2', rendered_page_2, size_tolerance=2)) 968 page3_path = os.path.join(self.basetestpath, 'test_exportiteratortosvg_Centre.svg') 969 self.assertTrue(os.path.exists(page3_path)) 970 page4_path = os.path.join(self.basetestpath, 'test_exportiteratortosvg_Pays de la Loire.svg') 971 self.assertTrue(os.path.exists(page4_path)) 972 973 def testIteratorToPdfs(self): 974 project, layout = self.prepareIteratorLayout() 975 atlas = layout.atlas() 976 atlas.setFilenameExpression("'test_exportiteratortopdf_' || \"NAME_1\"") 977 978 # setup settings 979 settings = QgsLayoutExporter.PdfExportSettings() 980 settings.dpi = 80 981 settings.rasterizeWholeImage = False 982 settings.forceVectorOutput = False 983 984 result, error = QgsLayoutExporter.exportToPdfs(atlas, self.basetestpath + '/', settings) 985 self.assertEqual(result, QgsLayoutExporter.Success, error) 986 987 page1_path = os.path.join(self.basetestpath, 'test_exportiteratortopdf_Basse-Normandie.pdf') 988 rendered_page_1 = os.path.join(self.basetestpath, 'test_exportiteratortopdf_Basse-Normandie.png') 989 pdfToPng(page1_path, rendered_page_1, dpi=80, page=1) 990 self.assertTrue(self.checkImage('iteratortopdf1', 'iteratortoimage1', rendered_page_1, size_tolerance=2)) 991 page2_path = os.path.join(self.basetestpath, 'test_exportiteratortopdf_Bretagne.pdf') 992 rendered_page_2 = os.path.join(self.basetestpath, 'test_exportiteratortopdf_Bretagne.png') 993 pdfToPng(page2_path, rendered_page_2, dpi=80, page=1) 994 self.assertTrue(self.checkImage('iteratortopdf2', 'iteratortoimage2', rendered_page_2, size_tolerance=2)) 995 page3_path = os.path.join(self.basetestpath, 'test_exportiteratortopdf_Centre.pdf') 996 self.assertTrue(os.path.exists(page3_path)) 997 page4_path = os.path.join(self.basetestpath, 'test_exportiteratortopdf_Pays de la Loire.pdf') 998 self.assertTrue(os.path.exists(page4_path)) 999 1000 def testIteratorToPdf(self): 1001 project, layout = self.prepareIteratorLayout() 1002 atlas = layout.atlas() 1003 1004 # setup settings 1005 settings = QgsLayoutExporter.PdfExportSettings() 1006 settings.dpi = 80 1007 settings.rasterizeWholeImage = False 1008 settings.forceVectorOutput = False 1009 1010 pdf_path = os.path.join(self.basetestpath, 'test_exportiteratortopdf_single.pdf') 1011 result, error = QgsLayoutExporter.exportToPdf(atlas, pdf_path, settings) 1012 self.assertEqual(result, QgsLayoutExporter.Success, error) 1013 1014 rendered_page_1 = os.path.join(self.basetestpath, 'test_exportiteratortopdf_single1.png') 1015 pdfToPng(pdf_path, rendered_page_1, dpi=80, page=1) 1016 self.assertTrue(self.checkImage('iteratortopdfsingle1', 'iteratortoimage1', rendered_page_1, size_tolerance=2)) 1017 1018 rendered_page_2 = os.path.join(self.basetestpath, 'test_exportiteratortopdf_single2.png') 1019 pdfToPng(pdf_path, rendered_page_2, dpi=80, page=2) 1020 self.assertTrue(self.checkImage('iteratortopdfsingle2', 'iteratortoimage2', rendered_page_2, size_tolerance=2)) 1021 1022 rendered_page_3 = os.path.join(self.basetestpath, 'test_exportiteratortopdf_single3.png') 1023 pdfToPng(pdf_path, rendered_page_3, dpi=80, page=3) 1024 self.assertTrue(os.path.exists(rendered_page_3)) 1025 rendered_page_4 = os.path.join(self.basetestpath, 'test_exportiteratortopdf_single4.png') 1026 pdfToPng(pdf_path, rendered_page_4, dpi=80, page=4) 1027 self.assertTrue(os.path.exists(rendered_page_4)) 1028 1029 def testPrintIterator(self): 1030 project, layout = self.prepareIteratorLayout() 1031 atlas = layout.atlas() 1032 1033 # setup settings 1034 settings = QgsLayoutExporter.PrintExportSettings() 1035 settings.dpi = 80 1036 settings.rasterizeWholeImage = False 1037 1038 pdf_path = os.path.join(self.basetestpath, 'test_printiterator.pdf') 1039 # make a qprinter directed to pdf 1040 printer = QPrinter() 1041 printer.setOutputFileName(pdf_path) 1042 printer.setOutputFormat(QPrinter.PdfFormat) 1043 1044 result, error = QgsLayoutExporter.print(atlas, printer, settings) 1045 self.assertEqual(result, QgsLayoutExporter.Success, error) 1046 1047 rendered_page_1 = os.path.join(self.basetestpath, 'test_printiterator1.png') 1048 pdfToPng(pdf_path, rendered_page_1, dpi=80, page=1) 1049 self.assertTrue(self.checkImage('printeriterator1', 'iteratortoimage1', rendered_page_1, size_tolerance=2)) 1050 1051 rendered_page_2 = os.path.join(self.basetestpath, 'test_printiterator2.png') 1052 pdfToPng(pdf_path, rendered_page_2, dpi=80, page=2) 1053 self.assertTrue(self.checkImage('printiterator2', 'iteratortoimage2', rendered_page_2, size_tolerance=2)) 1054 1055 rendered_page_3 = os.path.join(self.basetestpath, 'test_printiterator3.png') 1056 pdfToPng(pdf_path, rendered_page_3, dpi=80, page=3) 1057 self.assertTrue(os.path.exists(rendered_page_3)) 1058 rendered_page_4 = os.path.join(self.basetestpath, 'test_printiterator4.png') 1059 pdfToPng(pdf_path, rendered_page_4, dpi=80, page=4) 1060 self.assertTrue(os.path.exists(rendered_page_4)) 1061 1062 def testExportReport(self): 1063 p = QgsProject() 1064 r = QgsReport(p) 1065 1066 # add a header 1067 r.setHeaderEnabled(True) 1068 report_header = QgsLayout(p) 1069 report_header.initializeDefaults() 1070 item1 = QgsLayoutItemShape(report_header) 1071 item1.attemptSetSceneRect(QRectF(10, 20, 100, 150)) 1072 fill = QgsSimpleFillSymbolLayer() 1073 fill_symbol = QgsFillSymbol() 1074 fill_symbol.changeSymbolLayer(0, fill) 1075 fill.setColor(Qt.green) 1076 fill.setStrokeStyle(Qt.NoPen) 1077 item1.setSymbol(fill_symbol) 1078 report_header.addItem(item1) 1079 1080 r.setHeader(report_header) 1081 1082 # add a footer 1083 r.setFooterEnabled(True) 1084 report_footer = QgsLayout(p) 1085 report_footer.initializeDefaults() 1086 item2 = QgsLayoutItemShape(report_footer) 1087 item2.attemptSetSceneRect(QRectF(10, 20, 100, 150)) 1088 item2.attemptMove(QgsLayoutPoint(10, 20)) 1089 fill = QgsSimpleFillSymbolLayer() 1090 fill_symbol = QgsFillSymbol() 1091 fill_symbol.changeSymbolLayer(0, fill) 1092 fill.setColor(Qt.cyan) 1093 fill.setStrokeStyle(Qt.NoPen) 1094 item2.setSymbol(fill_symbol) 1095 report_footer.addItem(item2) 1096 1097 r.setFooter(report_footer) 1098 1099 # setup settings 1100 settings = QgsLayoutExporter.ImageExportSettings() 1101 settings.dpi = 80 1102 1103 report_path = os.path.join(self.basetestpath, 'test_report') 1104 result, error = QgsLayoutExporter.exportToImage(r, report_path, 'png', settings) 1105 self.assertEqual(result, QgsLayoutExporter.Success, error) 1106 1107 page1_path = os.path.join(self.basetestpath, 'test_report_0001.png') 1108 self.assertTrue(self.checkImage('report_page1', 'report_page1', page1_path)) 1109 page2_path = os.path.join(self.basetestpath, 'test_report_0002.png') 1110 self.assertTrue(self.checkImage('report_page2', 'report_page2', page2_path)) 1111 1112 1113if __name__ == '__main__': 1114 unittest.main() 1115