Export a Layout to Image and SVG in PyQGIS
PDF is the default output for print layouts, but many maps end up elsewhere: a PNG for a web page or a slide, a georeferenced TIFF to load back into GIS, an SVG for a designer to finish in Inkscape or Illustrator. QgsLayoutExporter produces all of these from the same layout, with settings that control resolution, cropping, georeferencing and how vector content is structured.
This recipe belongs to Automated Map Layout Generation. It exports to raster images with a chosen resolution, writes world files for georeferencing, crops to content with transparent backgrounds, exports SVG with separate layers for editing, handles multi-page layouts, and checks every export's result.
Prerequisites
- QGIS 3.34 LTR or newer, or the QGIS 4 series.
- A finished layout, either in the project or built in code. Exports in headless scripts need the QGIS application initialised, as in running Python scripts outside QGIS Desktop.
Export a PNG at a chosen resolution
Image exports render each page at a resolution in dots per inch; the pixel size follows from the page size and the dpi.
from qgis.core import QgsProject, QgsLayoutExporter
layout = QgsProject.instance().layoutManager().layoutByName("District report")
exporter = QgsLayoutExporter(layout)
settings = QgsLayoutExporter.ImageExportSettings()
settings.dpi = 150
settings.antialiasing = True
result = exporter.exportToImage("/data/exports/district_report.png", settings)
if result != QgsLayoutExporter.Success:
raise RuntimeError(f"export failed: {result} {exporter.errorMessage()}")
page = layout.pageCollection().page(0).pageSize()
print(f"{page.width():.0f} × {page.height():.0f} mm at {settings.dpi} dpi ≈ "
f"{page.width() / 25.4 * settings.dpi:.0f} × {page.height() / 25.4 * settings.dpi:.0f} px")
Breakdown: An A4 landscape page at 150 dpi is about 1,750 × 1,240 pixels — right for slides and web pages; 300 dpi doubles both dimensions for print-quality raster output. exportToImage returns a result code; Success is the only good one, and errorMessage() explains the rest, typically an unwritable path. The file extension chooses the format: .png for maps with sharp lines and text, .jpg only when the content is mostly imagery and file size matters.
Set an exact pixel size
When the target is a web page or a slide template with fixed dimensions, setting the pixel size directly is easier than calculating dpi.
from qgis.PyQt.QtCore import QSize
slide = QgsLayoutExporter.ImageExportSettings()
slide.imageSize = QSize(1920, 1080)
slide.cropToContents = False
exporter.exportToImage("/data/exports/district_report_slide.png", slide)
Breakdown: With imageSize set, the exporter scales the page into exactly that many pixels, so the effective dpi depends on the page size. Text and line widths, defined in millimetres and points in the layout, become larger or smaller in pixels accordingly; a layout designed for A4 exported to 1920 × 1080 is roughly 165 dpi. Match the page's aspect ratio to the target — 16:9 for slides — or the image will be letterboxed.
Georeference image exports
A map exported as an image can be loaded back into GIS in the right place if a world file is written alongside it. The exporter derives it from a map item.
geo = QgsLayoutExporter.ImageExportSettings()
geo.dpi = 300
geo.generateWorldFile = True
map_item = layout.itemById("main_map")
layout.setReferenceMap(map_item)
exporter.exportToImage("/data/exports/district_map.tif", geo)
# writes district_map.tif and district_map.tfw
Breakdown: The reference map tells the exporter which map item's extent and scale to encode; with several map items, setting it explicitly avoids georeferencing from an inset by mistake. The world file (.tfw for TIFF, .pgw for PNG) holds the pixel size and the coordinates of the top-left pixel, in the map item's CRS. It describes the whole page, so the page's non-map areas — title, legend — are georeferenced as if they were map, which is fine for overlays but means that for clean reuse a layout containing only the map item is best. QGIS writes a .aux.xml with the CRS, so the image loads directly with the right projection.
Crop to content with a transparent background
For maps embedded in documents or web pages, a tight crop and a transparent background avoid white borders and let the map sit on any colour.
inset = QgsLayoutExporter.ImageExportSettings()
inset.dpi = 200
inset.cropToContents = True
from qgis.core import QgsFillSymbol
from qgis.PyQt.QtGui import QColor
page_symbol = QgsFillSymbol.createSimple({"color": "0,0,0,0", "outline_style": "no"})
layout.pageCollection().setPageStyleSymbol(page_symbol)
exporter.exportToImage("/data/exports/locator_map.png", inset)
Breakdown: cropToContents trims the exported image to the bounding box of the items on the page, so empty paper around a small locator map disappears; cropMargins adds padding if needed. A transparent page symbol — fill colour with zero alpha and no outline — makes the background transparent in PNG output (JPEG cannot store transparency). Remember to restore the page symbol afterwards if the same layout is also exported to PDF. Map items with their own background colour stay opaque; set the map item's background to transparent too if the map itself should float.
Export SVG for further editing
SVG keeps vector content as vectors, which makes it the format of choice when a designer will refine the map. Exporting with separate layers gives them items and map layers as distinct groups.
from qgis.core import Qgis
svg = QgsLayoutExporter.SvgExportSettings()
svg.dpi = 96
svg.exportAsLayers = True
svg.exportMetadata = True
svg.forceVectorOutput = True
svg.textRenderFormat = Qgis.TextRenderFormat.AlwaysText
result = exporter.exportToSvg("/data/exports/district_report.svg", svg)
print("ok" if result == QgsLayoutExporter.Success else exporter.errorMessage())
Breakdown: exportAsLayers writes each layout item, and each map layer inside map items, as a separate SVG group named after it, which is what makes the file workable in an illustration program. forceVectorOutput avoids rasterising items that would otherwise be drawn as images, at the cost of larger files for complex symbology. Text as text keeps labels editable but depends on the designer having the same fonts; text as outlines (AlwaysOutlines) looks exactly right everywhere but cannot be edited. Effects such as blur and blending modes cannot be represented in SVG and are rasterised or lost — check the result.
Export thumbnails for catalogues
Map catalogues, web galleries and README files want small previews. Exporting a low-resolution PNG of each layout, then scaling it to a fixed width, produces consistent thumbnails in one pass.
from qgis.PyQt.QtGui import QImage
from qgis.PyQt.QtCore import Qt
def thumbnail(layout, out_png, width_px=480):
tmp = out_png.replace(".png", "_full.png")
s = QgsLayoutExporter.ImageExportSettings()
s.dpi = 72
s.pages = [0] # first page only
QgsLayoutExporter(layout).exportToImage(tmp, s)
img = QImage(tmp).scaledToWidth(width_px, Qt.SmoothTransformation)
img.save(out_png)
Path(tmp).unlink()
for lay in QgsProject.instance().layoutManager().printLayouts():
thumbnail(lay, f"/data/exports/thumbs/{lay.name().replace(' ', '_')}.png")
Breakdown: Rendering at a low dpi keeps exports fast, and restricting to the first page avoids writing every page of long layouts. Scaling with smooth transformation to a fixed width gives every thumbnail the same width for a tidy gallery, whatever the page size. File names are derived from layout names with spaces replaced; sanitise further if names contain slashes or other characters that are invalid in paths.
Handle multi-page layouts and check results
Image and SVG exports write one file per page. The exporter appends page numbers automatically; a script should know which files to expect and check them.
from pathlib import Path
pages = layout.pageCollection().pageCount()
result = exporter.exportToImage("/data/exports/atlas_report.png",
QgsLayoutExporter.ImageExportSettings())
expected = ["/data/exports/atlas_report.png"] + [
f"/data/exports/atlas_report_{i}.png" for i in range(2, pages + 1)]
missing = [p for p in expected if not Path(p).exists()]
print(pages, "pages;", "all written" if not missing else f"missing {missing}")
Breakdown: The first page keeps the given name and later pages get _2, _3 and so on before the extension. Checking that every expected file exists, and is not tiny, catches failures that a single result code can hide — such as a page whose map failed to render. For many layouts in one go, the same result checking applies to each, as in exporting multiple layouts to PDF; for atlases, QgsLayoutExporter.exportToImage(atlas, …) writes one image per feature with names from an expression.
QGIS version compatibility
QgsLayoutExporter image and SVG export settings shown work on QGIS 3.34 LTR, 3.40 LTR and QGIS 4. Qgis.TextRenderFormat replaced QgsRenderContext.TextRenderFormat in 3.30. On QGIS 4, QgsLayoutExporter.Success is QgsLayoutExporter.ExportResult.Success.
Troubleshooting
- Text looks tiny or huge in a fixed-size export. Effective dpi changed with
imageSize; design the page for the target size. - The world file places the image wrongly. The reference map was an inset, or the page includes non-map areas.
- The PNG background is white despite transparency. The page symbol or a map item background is still opaque.
- SVG lost effects. Blending and effects are not representable in SVG; avoid them for SVG exports.
Conclusion
Export images with a dpi for physical sizes or an exact pixel size for screens, write world files from an explicit reference map, crop to content with a transparent page for embedded maps, export layered SVG with vector output for designers, and check every page file and result code.
Frequently Asked Questions
Which is better for web maps, PNG or SVG? PNG for complex maps — SVG files with many features can be large and slow in browsers. SVG for simple diagrams and logos.
Can I export only the map item without the page?
Render the map with QgsMapRendererParallelJob instead, as in rendering a layer to an image without the GUI.
Why is the TIFF so large?
Uncompressed RGBA at 300 dpi; convert with gdal:translate and compression for archiving.
Do exports include hidden items? No — items hidden in the layout, or excluded from exports, are skipped.