def testMapSettingsDpiTarget(self): """ Test that the CRS changed signal is emitted in the right circumstances """ p = QgsProject() layout = QgsLayout(p) layout.renderContext().setDpi(111.1) map = QgsLayoutItemMap(layout) ms = map.mapSettings(QgsRectangle(0, 0, 1, 1), QSizeF(10, 10), 96, False) self.assertEqual(ms.dpiTarget(), 111.1)
def testRenderRegionToImage(self): l = QgsLayout(QgsProject.instance()) l.initializeDefaults() # add some items item1 = QgsLayoutItemShape(l) item1.attemptSetSceneRect(QRectF(10, 20, 100, 150)) fill = QgsSimpleFillSymbolLayer() fill_symbol = QgsFillSymbol() fill_symbol.changeSymbolLayer(0, fill) fill.setColor(Qt.green) fill.setStrokeStyle(Qt.NoPen) item1.setSymbol(fill_symbol) l.addItem(item1) exporter = QgsLayoutExporter(l) size = QSize(560, 509) image = exporter.renderRegionToImage(QRectF(5, 10, 110, 100), size) self.assertFalse(image.isNull()) rendered_file_path = os.path.join(self.basetestpath, 'test_rendertoimageregionsize.png') image.save(rendered_file_path, "PNG") self.assertTrue( self.checkImage('rendertoimageregionsize', 'rendertoimageregionsize', rendered_file_path)) # using layout dpi l.renderContext().setDpi(40) image = exporter.renderRegionToImage(QRectF(5, 10, 110, 100)) self.assertFalse(image.isNull()) rendered_file_path = os.path.join(self.basetestpath, 'test_rendertoimageregiondpi.png') image.save(rendered_file_path, "PNG") self.assertTrue( self.checkImage('rendertoimageregiondpi', 'rendertoimageregiondpi', rendered_file_path)) # overriding dpi image = exporter.renderRegionToImage(QRectF(5, 10, 110, 100), QSize(), 80) self.assertFalse(image.isNull()) rendered_file_path = os.path.join( self.basetestpath, 'test_rendertoimageregionoverridedpi.png') image.save(rendered_file_path, "PNG") self.assertTrue( self.checkImage('rendertoimageregionoverridedpi', 'rendertoimageregionoverridedpi', rendered_file_path))
def testRenderRegionToImage(self): l = QgsLayout(QgsProject.instance()) l.initializeDefaults() # add some items item1 = QgsLayoutItemShape(l) item1.attemptSetSceneRect(QRectF(10, 20, 100, 150)) fill = QgsSimpleFillSymbolLayer() fill_symbol = QgsFillSymbol() fill_symbol.changeSymbolLayer(0, fill) fill.setColor(Qt.green) fill.setStrokeStyle(Qt.NoPen) item1.setSymbol(fill_symbol) l.addItem(item1) exporter = QgsLayoutExporter(l) size = QSize(560, 509) image = exporter.renderRegionToImage(QRectF(5, 10, 110, 100), size) self.assertFalse(image.isNull()) rendered_file_path = os.path.join(self.basetestpath, 'test_rendertoimageregionsize.png') image.save(rendered_file_path, "PNG") self.assertTrue(self.checkImage('rendertoimageregionsize', 'rendertoimageregionsize', rendered_file_path)) # using layout dpi l.renderContext().setDpi(40) image = exporter.renderRegionToImage(QRectF(5, 10, 110, 100)) self.assertFalse(image.isNull()) rendered_file_path = os.path.join(self.basetestpath, 'test_rendertoimageregiondpi.png') image.save(rendered_file_path, "PNG") self.assertTrue(self.checkImage('rendertoimageregiondpi', 'rendertoimageregiondpi', rendered_file_path)) # overriding dpi image = exporter.renderRegionToImage(QRectF(5, 10, 110, 100), QSize(), 80) self.assertFalse(image.isNull()) rendered_file_path = os.path.join(self.basetestpath, 'test_rendertoimageregionoverridedpi.png') image.save(rendered_file_path, "PNG") self.assertTrue(self.checkImage('rendertoimageregionoverridedpi', 'rendertoimageregionoverridedpi', rendered_file_path))
class TestLayoutBase(TestQgsPalLabeling): layer = None """:type: QgsVectorLayer""" @classmethod def setUpClass(cls): if not cls._BaseSetup: TestQgsPalLabeling.setUpClass() # the blue background (set via layer style) to match renderchecker's TestQgsPalLabeling.loadFeatureLayer('background', True) cls._TestKind = 0 # OutputKind.(Img|Svg|Pdf) @classmethod def tearDownClass(cls): """Run after all tests""" TestQgsPalLabeling.tearDownClass() cls.removeMapLayer(cls.layer) cls.layer = None # avoid crash on finish, probably related to https://bugreports.qt.io/browse/QTBUG-35760 QThreadPool.globalInstance().waitForDone() def setUp(self): """Run before each test.""" super(TestLayoutBase, self).setUp() self._TestImage = '' # ensure per test map settings stay encapsulated self._TestMapSettings = self.cloneMapSettings(self._MapSettings) self._Mismatch = 0 self._ColorTol = 0 self._Mismatches.clear() self._ColorTols.clear() def _set_up_composition(self, width, height, dpi, engine_settings): # set up layout and add map self._c = QgsLayout(QgsProject.instance()) """:type: QgsLayout""" # self._c.setUseAdvancedEffects(False) self._c.renderContext().setDpi(dpi) # 600 x 400 px = 211.67 x 141.11 mm @ 72 dpi paperw = width * 25.4 / dpi paperh = height * 25.4 / dpi page = QgsLayoutItemPage(self._c) page.attemptResize(QgsLayoutSize(paperw, paperh)) self._c.pageCollection().addPage(page) # NOTE: do not use QgsLayoutItemMap(self._c, 0, 0, paperw, paperh) since # it only takes integers as parameters and the composition will grow # larger based upon union of item scene rectangles and a slight buffer # see end of QgsComposition::compositionBounds() # add map as small graphics item first, then set its scene QRectF later self._cmap = QgsLayoutItemMap(self._c) self._cmap.attemptSetSceneRect(QRectF(10, 10, 10, 10)) """:type: QgsLayoutItemMap""" self._cmap.setFrameEnabled(False) self._cmap.setLayers(self._TestMapSettings.layers()) if self._TestMapSettings.labelingEngineSettings().flags( ) & QgsLabelingEngineSettings.UsePartialCandidates: self._cmap.setMapFlags(QgsLayoutItemMap.ShowPartialLabels) self._c.addLayoutItem(self._cmap) # now expand map to fill page and set its extent self._cmap.attemptSetSceneRect(QRectF(0, 0, paperw, paperw)) self._cmap.setExtent(self.aoiExtent()) # self._cmap.updateCachedImage() # composition takes labeling engine settings from project QgsProject.instance().setLabelingEngineSettings(engine_settings) # noinspection PyUnusedLocal def _get_layout_image(self, width, height, dpi): image = QImage(QSize(width, height), self._TestMapSettings.outputImageFormat()) image.fill(QColor(152, 219, 249).rgb()) image.setDotsPerMeterX(dpi / 25.4 * 1000) image.setDotsPerMeterY(dpi / 25.4 * 1000) p = QPainter(image) p.setRenderHint( QPainter.Antialiasing, self._TestMapSettings.testFlag(QgsMapSettings.Antialiasing)) exporter = QgsLayoutExporter(self._c) exporter.renderPage(p, 0) p.end() # image = self._c.printPageAsRaster(0) # """:type: QImage""" if image.isNull(): return False, '' filepath = getTempfilePath('png') res = image.save(filepath, 'png') if not res: os.unlink(filepath) filepath = '' return res, filepath def _get_layout_svg_image(self, width, height, dpi): svgpath = getTempfilePath('svg') temp_size = os.path.getsize(svgpath) svg_g = QSvgGenerator() # noinspection PyArgumentList svg_g.setTitle(QgsProject.instance().title()) svg_g.setFileName(svgpath) svg_g.setSize(QSize(width, height)) svg_g.setViewBox(QRect(0, 0, width, height)) svg_g.setResolution(dpi) sp = QPainter(svg_g) exporter = QgsLayoutExporter(self._c) exporter.renderPage(sp, 0) sp.end() if temp_size == os.path.getsize(svgpath): return False, '' image = QImage(width, height, self._TestMapSettings.outputImageFormat()) image.fill(QColor(152, 219, 249).rgb()) image.setDotsPerMeterX(dpi / 25.4 * 1000) image.setDotsPerMeterY(dpi / 25.4 * 1000) svgr = QSvgRenderer(svgpath) p = QPainter(image) p.setRenderHint( QPainter.Antialiasing, self._TestMapSettings.testFlag(QgsMapSettings.Antialiasing)) p.setRenderHint(QPainter.TextAntialiasing) svgr.render(p) p.end() filepath = getTempfilePath('png') res = image.save(filepath, 'png') if not res: os.unlink(filepath) filepath = '' # TODO: remove .svg file as well? return res, filepath def _get_layout_pdf_image(self, width, height, dpi): pdfpath = getTempfilePath('pdf') temp_size = os.path.getsize(pdfpath) p = QPrinter() p.setOutputFormat(QPrinter.PdfFormat) p.setOutputFileName(pdfpath) p.setPaperSize( QSizeF(self._c.pageCollection().page(0).sizeWithUnits().width(), self._c.pageCollection().page(0).sizeWithUnits().height()), QPrinter.Millimeter) p.setFullPage(True) p.setColorMode(QPrinter.Color) p.setResolution(self._c.renderContext().dpi()) pdf_p = QPainter(p) # page_mm = p.pageRect(QPrinter.Millimeter) # page_px = p.pageRect(QPrinter.DevicePixel) # self._c.render(pdf_p, page_px, page_mm) exporter = QgsLayoutExporter(self._c) exporter.renderPage(pdf_p, 0) pdf_p.end() if temp_size == os.path.getsize(pdfpath): return False, '' filepath = getTempfilePath('png') # Poppler (pdftocairo or pdftoppm): # PDFUTIL -png -singlefile -r 72 -x 0 -y 0 -W 420 -H 280 in.pdf pngbase # muPDF (mudraw): # PDFUTIL -c rgb[a] -r 72 -w 420 -h 280 -o out.png in.pdf if PDFUTIL.strip().endswith('pdftocairo'): filebase = os.path.join( os.path.dirname(filepath), os.path.splitext(os.path.basename(filepath))[0]) call = [ PDFUTIL, '-png', '-singlefile', '-r', str(dpi), '-x', '0', '-y', '0', '-W', str(width), '-H', str(height), pdfpath, filebase ] elif PDFUTIL.strip().endswith('mudraw'): call = [ PDFUTIL, '-c', 'rgba', '-r', str(dpi), '-w', str(width), '-h', str(height), # '-b', '8', '-o', filepath, pdfpath ] else: return False, '' qDebug("_get_layout_pdf_image call: {0}".format(' '.join(call))) res = False try: subprocess.check_call(call) res = True except subprocess.CalledProcessError as e: qDebug("_get_layout_pdf_image failed!\n" "cmd: {0}\n" "returncode: {1}\n" "message: {2}".format(e.cmd, e.returncode, e.message)) if not res: os.unlink(filepath) filepath = '' return res, filepath def get_layout_output(self, kind): ms = self._TestMapSettings osize = ms.outputSize() width, height, dpi = osize.width(), osize.height(), ms.outputDpi() self._set_up_composition(width, height, dpi, ms.labelingEngineSettings()) if kind == OutputKind.Svg: return self._get_layout_svg_image(width, height, dpi) elif kind == OutputKind.Pdf: return self._get_layout_pdf_image(width, height, dpi) else: # OutputKind.Img return self._get_layout_image(width, height, dpi) # noinspection PyUnusedLocal def checkTest(self, **kwargs): self.layer.setLabeling(QgsVectorLayerSimpleLabeling(self.lyr)) ms = self._MapSettings # class settings settings_type = 'Class' if self._TestMapSettings is not None: ms = self._TestMapSettings # per test settings settings_type = 'Test' if 'PAL_VERBOSE' in os.environ: qDebug('MapSettings type: {0}'.format(settings_type)) qDebug(mapSettingsString(ms)) res_m, self._TestImage = self.get_layout_output(self._TestKind) self.assertTrue(res_m, 'Failed to retrieve/save output from layout') self.saveControlImage(self._TestImage) mismatch = 0 if 'PAL_NO_MISMATCH' not in os.environ: # some mismatch expected mismatch = self._Mismatch if self._Mismatch else 20 if self._TestGroup in self._Mismatches: mismatch = self._Mismatches[self._TestGroup] colortol = 0 if 'PAL_NO_COLORTOL' not in os.environ: colortol = self._ColorTol if self._ColorTol else 0 if self._TestGroup in self._ColorTols: colortol = self._ColorTols[self._TestGroup] self.assertTrue(*self.renderCheck( mismatch=mismatch, colortol=colortol, imgpath=self._TestImage))
class TestLayoutBase(TestQgsPalLabeling): layer = None """:type: QgsVectorLayer""" @classmethod def setUpClass(cls): if not cls._BaseSetup: TestQgsPalLabeling.setUpClass() # the blue background (set via layer style) to match renderchecker's TestQgsPalLabeling.loadFeatureLayer('background', True) cls._TestKind = 0 # OutputKind.(Img|Svg|Pdf) @classmethod def tearDownClass(cls): """Run after all tests""" TestQgsPalLabeling.tearDownClass() cls.removeMapLayer(cls.layer) cls.layer = None # avoid crash on finish, probably related to https://bugreports.qt.io/browse/QTBUG-35760 QThreadPool.globalInstance().waitForDone() def setUp(self): """Run before each test.""" super(TestLayoutBase, self).setUp() self._TestImage = '' # ensure per test map settings stay encapsulated self._TestMapSettings = self.cloneMapSettings(self._MapSettings) self._Mismatch = 0 self._ColorTol = 0 self._Mismatches.clear() self._ColorTols.clear() def _set_up_composition(self, width, height, dpi, engine_settings): # set up layout and add map self._c = QgsLayout(QgsProject.instance()) """:type: QgsLayout""" # self._c.setUseAdvancedEffects(False) self._c.renderContext().setDpi(dpi) # 600 x 400 px = 211.67 x 141.11 mm @ 72 dpi paperw = width * 25.4 / dpi paperh = height * 25.4 / dpi page = QgsLayoutItemPage(self._c) page.attemptResize(QgsLayoutSize(paperw, paperh)) self._c.pageCollection().addPage(page) # NOTE: do not use QgsLayoutItemMap(self._c, 0, 0, paperw, paperh) since # it only takes integers as parameters and the composition will grow # larger based upon union of item scene rectangles and a slight buffer # see end of QgsComposition::compositionBounds() # add map as small graphics item first, then set its scene QRectF later self._cmap = QgsLayoutItemMap(self._c) self._cmap.attemptSetSceneRect(QRectF(10, 10, 10, 10)) """:type: QgsLayoutItemMap""" self._cmap.setFrameEnabled(False) self._cmap.setLayers(self._TestMapSettings.layers()) if self._TestMapSettings.labelingEngineSettings().flags() & QgsLabelingEngineSettings.UsePartialCandidates: self._cmap.setMapFlags(QgsLayoutItemMap.ShowPartialLabels) self._c.addLayoutItem(self._cmap) # now expand map to fill page and set its extent self._cmap.attemptSetSceneRect(QRectF(0, 0, paperw, paperw)) self._cmap.setExtent(self.aoiExtent()) # self._cmap.updateCachedImage() # composition takes labeling engine settings from project QgsProject.instance().setLabelingEngineSettings(engine_settings) # noinspection PyUnusedLocal def _get_layout_image(self, width, height, dpi): image = QImage(QSize(width, height), self._TestMapSettings.outputImageFormat()) image.fill(QColor(152, 219, 249).rgb()) image.setDotsPerMeterX(dpi / 25.4 * 1000) image.setDotsPerMeterY(dpi / 25.4 * 1000) p = QPainter(image) p.setRenderHint( QPainter.Antialiasing, self._TestMapSettings.testFlag(QgsMapSettings.Antialiasing) ) exporter = QgsLayoutExporter(self._c) exporter.renderPage(p, 0) p.end() # image = self._c.printPageAsRaster(0) # """:type: QImage""" if image.isNull(): return False, '' filepath = getTempfilePath('png') res = image.save(filepath, 'png') if not res: os.unlink(filepath) filepath = '' return res, filepath def _get_layout_svg_image(self, width, height, dpi): svgpath = getTempfilePath('svg') temp_size = os.path.getsize(svgpath) svg_g = QSvgGenerator() # noinspection PyArgumentList svg_g.setTitle(QgsProject.instance().title()) svg_g.setFileName(svgpath) svg_g.setSize(QSize(width, height)) svg_g.setViewBox(QRect(0, 0, width, height)) svg_g.setResolution(dpi) sp = QPainter(svg_g) exporter = QgsLayoutExporter(self._c) exporter.renderPage(sp, 0) sp.end() if temp_size == os.path.getsize(svgpath): return False, '' image = QImage(width, height, self._TestMapSettings.outputImageFormat()) image.fill(QColor(152, 219, 249).rgb()) image.setDotsPerMeterX(dpi / 25.4 * 1000) image.setDotsPerMeterY(dpi / 25.4 * 1000) svgr = QSvgRenderer(svgpath) p = QPainter(image) p.setRenderHint( QPainter.Antialiasing, self._TestMapSettings.testFlag(QgsMapSettings.Antialiasing) ) p.setRenderHint(QPainter.TextAntialiasing) svgr.render(p) p.end() filepath = getTempfilePath('png') res = image.save(filepath, 'png') if not res: os.unlink(filepath) filepath = '' # TODO: remove .svg file as well? return res, filepath def _get_layout_pdf_image(self, width, height, dpi): pdfpath = getTempfilePath('pdf') temp_size = os.path.getsize(pdfpath) p = QPrinter() p.setOutputFormat(QPrinter.PdfFormat) p.setOutputFileName(pdfpath) p.setPaperSize(QSizeF(self._c.pageCollection().page(0).sizeWithUnits().width(), self._c.pageCollection().page(0).sizeWithUnits().height()), QPrinter.Millimeter) p.setFullPage(True) p.setColorMode(QPrinter.Color) p.setResolution(self._c.renderContext().dpi()) pdf_p = QPainter(p) # page_mm = p.pageRect(QPrinter.Millimeter) # page_px = p.pageRect(QPrinter.DevicePixel) # self._c.render(pdf_p, page_px, page_mm) exporter = QgsLayoutExporter(self._c) exporter.renderPage(pdf_p, 0) pdf_p.end() if temp_size == os.path.getsize(pdfpath): return False, '' filepath = getTempfilePath('png') # Poppler (pdftocairo or pdftoppm): # PDFUTIL -png -singlefile -r 72 -x 0 -y 0 -W 420 -H 280 in.pdf pngbase # muPDF (mudraw): # PDFUTIL -c rgb[a] -r 72 -w 420 -h 280 -o out.png in.pdf if PDFUTIL.strip().endswith('pdftocairo'): filebase = os.path.join( os.path.dirname(filepath), os.path.splitext(os.path.basename(filepath))[0] ) call = [ PDFUTIL, '-png', '-singlefile', '-r', str(dpi), '-x', '0', '-y', '0', '-W', str(width), '-H', str(height), pdfpath, filebase ] elif PDFUTIL.strip().endswith('mudraw'): call = [ PDFUTIL, '-c', 'rgba', '-r', str(dpi), '-w', str(width), '-h', str(height), # '-b', '8', '-o', filepath, pdfpath ] else: return False, '' qDebug("_get_layout_pdf_image call: {0}".format(' '.join(call))) res = False try: subprocess.check_call(call) res = True except subprocess.CalledProcessError as e: qDebug("_get_layout_pdf_image failed!\n" "cmd: {0}\n" "returncode: {1}\n" "message: {2}".format(e.cmd, e.returncode, e.message)) if not res: os.unlink(filepath) filepath = '' return res, filepath def get_layout_output(self, kind): ms = self._TestMapSettings osize = ms.outputSize() width, height, dpi = osize.width(), osize.height(), ms.outputDpi() self._set_up_composition(width, height, dpi, ms.labelingEngineSettings()) if kind == OutputKind.Svg: return self._get_layout_svg_image(width, height, dpi) elif kind == OutputKind.Pdf: return self._get_layout_pdf_image(width, height, dpi) else: # OutputKind.Img return self._get_layout_image(width, height, dpi) # noinspection PyUnusedLocal def checkTest(self, **kwargs): self.layer.setLabeling(QgsVectorLayerSimpleLabeling(self.lyr)) ms = self._MapSettings # class settings settings_type = 'Class' if self._TestMapSettings is not None: ms = self._TestMapSettings # per test settings settings_type = 'Test' if 'PAL_VERBOSE' in os.environ: qDebug('MapSettings type: {0}'.format(settings_type)) qDebug(mapSettingsString(ms)) res_m, self._TestImage = self.get_layout_output(self._TestKind) self.assertTrue(res_m, 'Failed to retrieve/save output from layout') self.saveControlImage(self._TestImage) mismatch = 0 if 'PAL_NO_MISMATCH' not in os.environ: # some mismatch expected mismatch = self._Mismatch if self._Mismatch else 20 if self._TestGroup in self._Mismatches: mismatch = self._Mismatches[self._TestGroup] colortol = 0 if 'PAL_NO_COLORTOL' not in os.environ: colortol = self._ColorTol if self._ColorTol else 0 if self._TestGroup in self._ColorTols: colortol = self._ColorTols[self._TestGroup] self.assertTrue(*self.renderCheck(mismatch=mismatch, colortol=colortol, imgpath=self._TestImage))