def testIteratorToSvgs(self): project, layout = self.prepareIteratorLayout() atlas = layout.atlas() atlas.setFilenameExpression("'test_exportiteratortosvg_' || \"NAME_1\"") # setup settings settings = QgsLayoutExporter.SvgExportSettings() settings.dpi = 80 settings.forceVectorOutput = False result, error = QgsLayoutExporter.exportToSvg(atlas, self.basetestpath + '/', settings) self.assertEqual(result, QgsLayoutExporter.Success, error) page1_path = os.path.join(self.basetestpath, 'test_exportiteratortosvg_Basse-Normandie.svg') rendered_page_1 = os.path.join(self.basetestpath, 'test_exportiteratortosvg_Basse-Normandie.png') svgToPng(page1_path, rendered_page_1, width=935) self.assertTrue(self.checkImage('iteratortosvg1', 'iteratortoimage1', rendered_page_1, size_tolerance=2)) page2_path = os.path.join(self.basetestpath, 'test_exportiteratortosvg_Bretagne.svg') rendered_page_2 = os.path.join(self.basetestpath, 'test_exportiteratortosvg_Bretagne.png') svgToPng(page2_path, rendered_page_2, width=935) self.assertTrue(self.checkImage('iteratortosvg2', 'iteratortoimage2', rendered_page_2, size_tolerance=2)) page3_path = os.path.join(self.basetestpath, 'test_exportiteratortosvg_Centre.svg') self.assertTrue(os.path.exists(page3_path)) page4_path = os.path.join(self.basetestpath, 'test_exportiteratortosvg_Pays de la Loire.svg') self.assertTrue(os.path.exists(page4_path))
def testExportToSvgTextRenderFormat(self): l = QgsLayout(QgsProject.instance()) l.initializeDefaults() # add a map and scalebar mapitem = QgsLayoutItemMap(l) mapitem.attemptSetSceneRect(QRectF(110, 120, 200, 250)) mapitem.zoomToExtent(QgsRectangle(1, 1, 10, 10)) mapitem.setScale( 666 ) # unlikely to appear in the SVG by accident... unless... oh no! RUN! l.addItem(mapitem) item1 = QgsLayoutItemScaleBar(l) item1.attemptSetSceneRect(QRectF(10, 20, 100, 150)) item1.setLinkedMap(mapitem) item1.setStyle('Numeric') l.addItem(item1) exporter = QgsLayoutExporter(l) # setup settings settings = QgsLayoutExporter.SvgExportSettings() settings.dpi = 80 settings.forceVectorOutput = False settings.exportMetadata = True settings.textRenderFormat = QgsRenderContext.TextFormatAlwaysText svg_file_path = os.path.join(self.basetestpath, 'test_exporttosvgtextformattext.svg') self.assertEqual(exporter.exportToSvg(svg_file_path, settings), QgsLayoutExporter.Success) self.assertTrue(os.path.exists(svg_file_path)) # expect svg to contain a text object with the scale with open(svg_file_path, 'r') as f: lines = ''.join(f.readlines()) self.assertIn('<text', lines) self.assertIn('>1:666<', lines) # force use of outlines os.unlink(svg_file_path) settings.textRenderFormat = QgsRenderContext.TextFormatAlwaysOutlines self.assertEqual(exporter.exportToSvg(svg_file_path, settings), QgsLayoutExporter.Success) self.assertTrue(os.path.exists(svg_file_path)) # expect svg NOT to contain a text object with the scale with open(svg_file_path, 'r') as f: lines = ''.join(f.readlines()) self.assertNotIn('<text', lines) self.assertNotIn('>1:666<', lines)
def overrideExportSettings(self, layout, extension): """Because GUI settings are not exposed in Python, we need to find and catch user selection See discussion at http://osgeo-org.1560.x6.nabble.com/Programmatically-export-layout-with-georeferenced-file-td5365462.html""" if extension == '.pdf': exportSettings = QgsLayoutExporter.PdfExportSettings() if layout.customProperty( 'dpi') and layout.customProperty('dpi') != -1: exportSettings.dpi = layout.customProperty('dpi') if layout.customProperty('forceVector') == True: exportSettings.forceVectorOutput = True if layout.customProperty('rasterize') == True: exportSettings.rasterizeWholeImage = True elif extension == '.svg': exportSettings = QgsLayoutExporter.SvgExportSettings() if layout.customProperty( 'dpi') and layout.customProperty('dpi') != -1: exportSettings.dpi = layout.customProperty('dpi') if layout.customProperty('forceVector') == True: exportSettings.forceVectorOutput = True if layout.customProperty('svgIncludeMetadata') == True: exportSettings.exportMetadata = True if layout.customProperty('svgGroupLayers') == True: exportSettings.exportAsLayers = True else: exportSettings = QgsLayoutExporter.ImageExportSettings() if layout.customProperty('exportWorldFile') == True: exportSettings.generateWorldFile = True if layout.customProperty('') == True: exportSettings.exportMetadata = True if layout.customProperty( 'dpi') and layout.customProperty('dpi') != -1: exportSettings.dpi = layout.customProperty('dpi') # if layout.customProperty('atlasRasterFormat') == True : exportSettings.xxxx = True # if layout.customProperty('imageAntialias') == True : exportSettings.xxxx = True return exportSettings
def testExportToSvg(self): md = QgsProject.instance().metadata() md.setTitle('proj title') md.setAuthor('proj author') md.setCreationDateTime(QDateTime(QDate(2011, 5, 3), QTime(9, 4, 5), QTimeZone(36000))) md.setIdentifier('proj identifier') md.setAbstract('proj abstract') md.setKeywords({'kw': ['kw1', 'kw2']}) QgsProject.instance().setMetadata(md) l = QgsLayout(QgsProject.instance()) l.initializeDefaults() # add a second page page2 = QgsLayoutItemPage(l) page2.setPageSize('A5') l.pageCollection().addPage(page2) # 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) item2 = QgsLayoutItemShape(l) item2.attemptSetSceneRect(QRectF(10, 20, 100, 150)) item2.attemptMove(QgsLayoutPoint(10, 20), page=1) fill = QgsSimpleFillSymbolLayer() fill_symbol = QgsFillSymbol() fill_symbol.changeSymbolLayer(0, fill) fill.setColor(Qt.cyan) fill.setStrokeStyle(Qt.NoPen) item2.setSymbol(fill_symbol) l.addItem(item2) exporter = QgsLayoutExporter(l) # setup settings settings = QgsLayoutExporter.SvgExportSettings() settings.dpi = 80 settings.forceVectorOutput = False settings.exportMetadata = True svg_file_path = os.path.join(self.basetestpath, 'test_exporttosvgdpi.svg') svg_file_path_2 = os.path.join(self.basetestpath, 'test_exporttosvgdpi_2.svg') self.assertEqual(exporter.exportToSvg(svg_file_path, settings), QgsLayoutExporter.Success) self.assertTrue(os.path.exists(svg_file_path)) self.assertTrue(os.path.exists(svg_file_path_2)) # metadata def checkMetadata(f, expected): # ideally we'd check the path too - but that's very complex given that # the output from Qt svg generator isn't valid XML, and no Python standard library # xml parser handles invalid xml... self.assertEqual('proj title' in open(f).read(), expected) self.assertEqual('proj author' in open(f).read(), expected) self.assertEqual('proj identifier' in open(f).read(), expected) self.assertEqual('2011-05-03' in open(f).read(), expected) self.assertEqual('proj abstract' in open(f).read(), expected) self.assertEqual('kw1' in open(f).read(), expected) self.assertEqual('kw2' in open(f).read(), expected) for f in [svg_file_path, svg_file_path_2]: checkMetadata(f, True) rendered_page_1 = os.path.join(self.basetestpath, 'test_exporttosvgdpi.png') svgToPng(svg_file_path, rendered_page_1, width=936) rendered_page_2 = os.path.join(self.basetestpath, 'test_exporttosvgdpi2.png') svgToPng(svg_file_path_2, rendered_page_2, width=467) self.assertTrue(self.checkImage('exporttosvgdpi_page1', 'exporttopdfdpi_page1', rendered_page_1, size_tolerance=1)) self.assertTrue(self.checkImage('exporttosvgdpi_page2', 'exporttopdfdpi_page2', rendered_page_2, size_tolerance=1)) # no metadata settings.exportMetadata = False self.assertEqual(exporter.exportToSvg(svg_file_path, settings), QgsLayoutExporter.Success) for f in [svg_file_path, svg_file_path_2]: checkMetadata(f, False) # layered settings.exportAsLayers = True settings.exportMetadata = True svg_file_path = os.path.join(self.basetestpath, 'test_exporttosvglayered.svg') svg_file_path_2 = os.path.join(self.basetestpath, 'test_exporttosvglayered_2.svg') self.assertEqual(exporter.exportToSvg(svg_file_path, settings), QgsLayoutExporter.Success) self.assertTrue(os.path.exists(svg_file_path)) self.assertTrue(os.path.exists(svg_file_path_2)) rendered_page_1 = os.path.join(self.basetestpath, 'test_exporttosvglayered.png') svgToPng(svg_file_path, rendered_page_1, width=936) rendered_page_2 = os.path.join(self.basetestpath, 'test_exporttosvglayered2.png') svgToPng(svg_file_path_2, rendered_page_2, width=467) self.assertTrue(self.checkImage('exporttosvglayered_page1', 'exporttopdfdpi_page1', rendered_page_1, size_tolerance=1)) self.assertTrue(self.checkImage('exporttosvglayered_page2', 'exporttopdfdpi_page2', rendered_page_2, size_tolerance=1)) for f in [svg_file_path, svg_file_path_2]: checkMetadata(f, True) # layered no metadata settings.exportAsLayers = True settings.exportMetadata = False self.assertEqual(exporter.exportToSvg(svg_file_path, settings), QgsLayoutExporter.Success) for f in [svg_file_path, svg_file_path_2]: checkMetadata(f, False)
def testExportToSvg(self): l = QgsLayout(QgsProject.instance()) l.initializeDefaults() # add a second page page2 = QgsLayoutItemPage(l) page2.setPageSize('A5') l.pageCollection().addPage(page2) # 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) item2 = QgsLayoutItemShape(l) item2.attemptSetSceneRect(QRectF(10, 20, 100, 150)) item2.attemptMove(QgsLayoutPoint(10, 20), page=1) fill = QgsSimpleFillSymbolLayer() fill_symbol = QgsFillSymbol() fill_symbol.changeSymbolLayer(0, fill) fill.setColor(Qt.cyan) fill.setStrokeStyle(Qt.NoPen) item2.setSymbol(fill_symbol) l.addItem(item2) exporter = QgsLayoutExporter(l) # setup settings settings = QgsLayoutExporter.SvgExportSettings() settings.dpi = 80 settings.forceVectorOutput = False svg_file_path = os.path.join(self.basetestpath, 'test_exporttosvgdpi.svg') svg_file_path_2 = os.path.join(self.basetestpath, 'test_exporttosvgdpi_2.svg') self.assertEqual(exporter.exportToSvg(svg_file_path, settings), QgsLayoutExporter.Success) self.assertTrue(os.path.exists(svg_file_path)) self.assertTrue(os.path.exists(svg_file_path_2)) rendered_page_1 = os.path.join(self.basetestpath, 'test_exporttosvgdpi.png') svgToPng(svg_file_path, rendered_page_1, width=936) rendered_page_2 = os.path.join(self.basetestpath, 'test_exporttosvgdpi2.png') svgToPng(svg_file_path_2, rendered_page_2, width=467) self.assertTrue( self.checkImage('exporttosvgdpi_page1', 'exporttopdfdpi_page1', rendered_page_1, size_tolerance=1)) self.assertTrue( self.checkImage('exporttosvgdpi_page2', 'exporttopdfdpi_page2', rendered_page_2, size_tolerance=1)) # layered settings.exportAsLayers = True svg_file_path = os.path.join(self.basetestpath, 'test_exporttosvglayered.svg') svg_file_path_2 = os.path.join(self.basetestpath, 'test_exporttosvglayered_2.svg') self.assertEqual(exporter.exportToSvg(svg_file_path, settings), QgsLayoutExporter.Success) self.assertTrue(os.path.exists(svg_file_path)) self.assertTrue(os.path.exists(svg_file_path_2)) rendered_page_1 = os.path.join(self.basetestpath, 'test_exporttosvglayered.png') svgToPng(svg_file_path, rendered_page_1, width=936) rendered_page_2 = os.path.join(self.basetestpath, 'test_exporttosvglayered2.png') svgToPng(svg_file_path_2, rendered_page_2, width=467) self.assertTrue( self.checkImage('exporttosvglayered_page1', 'exporttopdfdpi_page1', rendered_page_1, size_tolerance=1)) self.assertTrue( self.checkImage('exporttosvglayered_page2', 'exporttopdfdpi_page2', rendered_page_2, size_tolerance=1))
def overrideExportSettings(self, layout, extension): """Because GUI settings are not exposed in Python, we need to find and catch user selection and override export settings values with what is actually active in the GUI. See discussion at http://osgeo-org.1560.x6.nabble.com/Programmatically-export-layout-with-georeferenced-file-td5365462.html """ if extension == '.pdf': # See QgsLayoutDesignerDialog::getPdfExportSettings # let's follow non-default values if set exportSettings = QgsLayoutExporter.PdfExportSettings() exportSettings.flags = layout.renderContext().flags() exportSettings.dpi = layoutDpi if layout.customProperty('rasterize') in ['true', True]: exportSettings.rasterizeWholeImage = True if layout.customProperty('forceVector') == 1: exportSettings.forceVectorOutput = True if layout.customProperty('pdfTextFormat') == 1: exportSettings.textRenderFormat = 1 if layout.customProperty('pdfOgcBestPracticeFormat') == 1: exportSettings.useIso32000ExtensionFormatGeoreferencing = False exportSettings.useOgcBestPracticeFormatGeoreferencing = True if layout.customProperty('pdfExportThemes'): exportSettings.exportThemes = layout.customProperty( 'pdfExportThemes') if layout.customProperty('pdfIncludeMetadata') == 0: exportSettings.exportMetadata = False if layout.customProperty('pdfSimplify') == 0: exportSettings.simplifyGeometries = False if layout.customProperty('pdfCreateGeoPdf') == 1: exportSettings.writeGeoPdf = True if layout.customProperty('pdfAppendGeoreference') == 0: exportSettings.appendGeoreference = False if layout.customProperty('pdfExportGeoPdfFeatures') == 0: exportSettings.includeGeoPdfFeatures = False elif extension == '.svg': # See QgsLayoutDesignerDialog::getSvgExportSettings exportSettings = QgsLayoutExporter.SvgExportSettings() exportSettings.flags = layout.renderContext().flags() exportSettings.dpi = layoutDpi if layout.customProperty('forceVector') == 1: exportSettings.forceVectorOutput = True if layout.customProperty('svgIncludeMetadata') == 0: exportSettings.exportMetadata = False if layout.customProperty('svgSimplify') == 0: exportSettings.simplifyGeometries = False if layout.customProperty('svgGroupLayers') in ['true', True]: exportSettings.exportAsLayers = True if layout.customProperty('svgTextFormat') == 1: exportSettings.textRenderFormat = 1 if layout.customProperty('svgCropToContents') in ['true', True]: exportSettings.cropToContents = True # Todo: add margin values when cropping to content #exportSettings.cropMargins = ???QgsMargins??? #if layout.customProperty('svgDisableRasterTiles') in ['true', True] : ??? # to fine tune with flags FlagDisableTiledRasterLayerRenders else: # see QgsLayoutDesignerDialog::getRasterExportSettings for settings exportSettings = QgsLayoutExporter.ImageExportSettings() exportSettings.flags = layout.renderContext().flags() exportSettings.dpi = layoutDpi if layout.customProperty('exportWorldFile') in ['true', True]: exportSettings.generateWorldFile = True if layout.customProperty('imageCropToContents') in ['true', True]: exportSettings.cropToContents = True # Todo: add margin values when cropping to content #exportSettings.cropMargins = ???QgsMargins??? # exportSettings.exportMetadata = False # what's the corresponding layout's property? # layout.customProperty('atlasRasterFormat') # overridden by extension # # if layout.customProperty('imageAntialias') in ['true', True] : ??? # to fine tune with flags FlagAntialiasing return exportSettings
def print_layout( project: QgsProject, layout_name: str, output_format: OutputFormat, feature_filter: str = None, scales: list = None, scale: int = None, **kwargs, ): """Generate a PDF for an atlas or a report. :param project: The QGIS project. :type project: QgsProject :param layout_name: Name of the layout of the atlas or report. :type layout_name: basestring :param feature_filter: QGIS Expression to use to select the feature. It can return many features, a multiple pages PDF will be returned. This is required to print atlas, not report :type feature_filter: basestring :param scale: A scale to force in the atlas context. Default to None. :type scale: int :param scales: A list of predefined list of scales to force in the atlas context. Default to None. :type scales: list :param output_format: The output format, default to PDF if not provided. :return: Path to the PDF. :rtype: basestring """ canvas = QgsMapCanvas() bridge = QgsLayerTreeMapCanvasBridge(project.layerTreeRoot(), canvas) bridge.setCanvasLayers() manager = project.layoutManager() master_layout = manager.layoutByName(layout_name) if output_format == OutputFormat.Svg: settings = QgsLayoutExporter.SvgExportSettings() elif output_format in (OutputFormat.Png, OutputFormat.Jpeg): settings = QgsLayoutExporter.ImageExportSettings() else: # PDF by default settings = QgsLayoutExporter.PdfExportSettings() atlas = None atlas_layout = None report_layout = None logger = Logger() if not master_layout: raise AtlasPrintException("Layout `{}` not found".format(layout_name)) if master_layout.layoutType() == QgsMasterLayoutInterface.PrintLayout: for _print_layout in manager.printLayouts(): if _print_layout.name() == layout_name: atlas_layout = _print_layout break atlas = atlas_layout.atlas() if not atlas.enabled(): raise AtlasPrintException("The layout is not enabled for an atlas") layer = atlas.coverageLayer() if feature_filter is None: raise AtlasPrintException( "EXP_FILTER is mandatory to print an atlas layout") feature_filter = optimize_expression(layer, feature_filter) expression = QgsExpression(feature_filter) if expression.hasParserError(): raise AtlasPrintException( "Expression is invalid, parser error: {}".format( expression.parserErrorString())) context = QgsExpressionContext() context.appendScope(QgsExpressionContextUtils.globalScope()) context.appendScope(QgsExpressionContextUtils.projectScope(project)) context.appendScope( QgsExpressionContextUtils.layoutScope(atlas_layout)) context.appendScope(QgsExpressionContextUtils.atlasScope(atlas)) context.appendScope(QgsExpressionContextUtils.layerScope(layer)) expression.prepare(context) if expression.hasEvalError(): raise AtlasPrintException( "Expression is invalid, eval error: {}".format( expression.evalErrorString())) atlas.setFilterFeatures(True) atlas.setFilterExpression(feature_filter) atlas.updateFeatures() if scale: atlas_layout.referenceMap().setAtlasScalingMode( QgsLayoutItemMap.Fixed) atlas_layout.referenceMap().setScale(scale) if scales: atlas_layout.referenceMap().setAtlasScalingMode( QgsLayoutItemMap.Predefined) settings.predefinedMapScales = scales if (not scales and atlas_layout.referenceMap().atlasScalingMode() == QgsLayoutItemMap.Predefined): use_project = project.useProjectScales() map_scales = project.mapScales() if not use_project or len(map_scales) == 0: logger.info( "Map scales not found in project, fetching predefined map scales in global config" ) map_scales = global_scales() settings.predefinedMapScales = map_scales elif master_layout.layoutType() == QgsMasterLayoutInterface.Report: report_layout = master_layout else: raise AtlasPrintException("The layout is not supported by the plugin") for key, value in kwargs.items(): found = False if atlas_layout: item = atlas_layout.itemById(key.lower()) if isinstance(item, QgsLayoutItemLabel): item.setText(value) found = True logger.info( 'Additional parameters "{key}" {found} in layout, value "{value}"'. format(key=key, found="found" if found else "not found", value=value)) file_name = "{}_{}.{}".format(clean_string(layout_name), uuid4(), output_format.name.lower()) export_path = Path(tempfile.gettempdir()).joinpath(file_name) Logger().info("Exporting the request in {} using {}".format( export_path, output_format.value)) if output_format in (OutputFormat.Png, OutputFormat.Jpeg): exporter = QgsLayoutExporter(atlas_layout or report_layout) result = exporter.exportToImage(str(export_path), settings) error = result_message(result) elif output_format in (OutputFormat.Svg, ): exporter = QgsLayoutExporter(atlas_layout or report_layout) result = exporter.exportToSvg(str(export_path), settings) error = result_message(result) else: # Default to PDF result, error = QgsLayoutExporter.exportToPdf(atlas or report_layout, str(export_path), settings) # Let's override error message _ = error error = result_message(result) if result != QgsLayoutExporter.Success: raise Exception("Export not generated in QGIS exporter {} : {}".format( export_path, error)) if not export_path.is_file(): logger.warning( "No error from QGIS Exporter, but the file does not exist.\n" "Message from QGIS exporter : {}\n" "File path : {}\n".format(error, export_path)) raise Exception( "Export OK from QGIS, but file not found on the file system : {}". format(export_path)) return export_path