Пример #1
0
 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)
Пример #2
0
    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))
Пример #3
0
    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))
Пример #4
0
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))
Пример #5
0
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))