35#include <QImageWriter>
38#include <QSvgGenerator>
42using namespace Qt::StringLiterals;
44#if QT_VERSION >= QT_VERSION_CHECK( 6, 8, 0 )
46#include <QPdfOutputIntent>
48#include <QXmlStreamWriter>
54class LayoutContextPreviewSettingRestorer
57 LayoutContextPreviewSettingRestorer( QgsLayout *layout )
59 , mPreviousSetting( layout->renderContext().mIsPreviewRender )
61 mLayout->renderContext().mIsPreviewRender =
false;
64 ~LayoutContextPreviewSettingRestorer() { mLayout->renderContext().mIsPreviewRender = mPreviousSetting; }
66 LayoutContextPreviewSettingRestorer(
const LayoutContextPreviewSettingRestorer &other ) =
delete;
67 LayoutContextPreviewSettingRestorer &operator=(
const LayoutContextPreviewSettingRestorer &other ) =
delete;
70 QgsLayout *mLayout =
nullptr;
71 bool mPreviousSetting =
false;
77 LayoutGuideHider( QgsLayout *layout )
80 const QList< QgsLayoutGuide * > guides = mLayout->guides().guides();
81 for ( QgsLayoutGuide *guide : guides )
83 mPrevVisibility.insert( guide, guide->item()->isVisible() );
84 guide->item()->setVisible(
false );
90 for (
auto it = mPrevVisibility.constBegin(); it != mPrevVisibility.constEnd(); ++it )
92 it.key()->item()->setVisible( it.value() );
96 LayoutGuideHider(
const LayoutGuideHider &other ) =
delete;
97 LayoutGuideHider &operator=(
const LayoutGuideHider &other ) =
delete;
100 QgsLayout *mLayout =
nullptr;
101 QHash< QgsLayoutGuide *, bool > mPrevVisibility;
107 explicit LayoutItemHider(
const QList<QGraphicsItem *> &items )
109 mItemsToIterate.reserve( items.count() );
110 for ( QGraphicsItem *item : items )
112 const bool isVisible = item->isVisible();
113 mPrevVisibility[item] = isVisible;
115 mItemsToIterate.append( item );
116 if ( QgsLayoutItem *layoutItem =
dynamic_cast< QgsLayoutItem *
>( item ) )
117 layoutItem->setProperty(
"wasVisible", isVisible );
125 for (
auto it = mPrevVisibility.constBegin(); it != mPrevVisibility.constEnd(); ++it )
133 for (
auto it = mPrevVisibility.constBegin(); it != mPrevVisibility.constEnd(); ++it )
135 it.key()->setVisible( it.value() );
136 if ( QgsLayoutItem *layoutItem =
dynamic_cast< QgsLayoutItem *
>( it.key() ) )
137 layoutItem->setProperty(
"wasVisible", QVariant() );
141 QList< QGraphicsItem * > itemsToIterate()
const {
return mItemsToIterate; }
143 LayoutItemHider(
const LayoutItemHider &other ) =
delete;
144 LayoutItemHider &operator=(
const LayoutItemHider &other ) =
delete;
147 QList<QGraphicsItem * > mItemsToIterate;
148 QHash<QGraphicsItem *, bool> mPrevVisibility;
168 qDeleteAll( mLabelingResults );
181 if ( mLayout->pageCollection()->pageCount() <= page || page < 0 )
192 LayoutContextPreviewSettingRestorer restorer( mLayout );
195 QRectF paperRect = QRectF( pageItem->pos().x(), pageItem->pos().y(), pageItem->rect().width(), pageItem->rect().height() );
204 if ( mLayout->pageCollection()->pageCount() <= page || page < 0 )
215 LayoutContextPreviewSettingRestorer restorer( mLayout );
218 QRectF paperRect = QRectF( pageItem->pos().x(), pageItem->pos().y(), pageItem->rect().width(), pageItem->rect().height() );
220 const double imageAspectRatio =
static_cast< double >( imageSize.width() ) / imageSize.height();
221 const double paperAspectRatio = paperRect.width() / paperRect.height();
222 if ( imageSize.isValid() && ( !
qgsDoubleNear( imageAspectRatio, paperAspectRatio, 0.008 ) ) )
228 logMessage( QObject::tr(
"Ignoring custom image size because aspect ratio %1 does not match paper ratio %2" ).arg( QString::number( imageAspectRatio,
'g', 3 ), QString::number( paperAspectRatio,
'g', 3 ) ), u
"Layout"_s,
Qgis::MessageLevel::Warning );
236class LayoutItemCacheSettingRestorer
239 LayoutItemCacheSettingRestorer(
QgsLayout *layout )
242 const QList< QGraphicsItem * > items = mLayout->items();
243 for ( QGraphicsItem *item : items )
245 mPrevCacheMode.insert( item, item->cacheMode() );
246 item->setCacheMode( QGraphicsItem::NoCache );
250 ~LayoutItemCacheSettingRestorer()
252 for (
auto it = mPrevCacheMode.constBegin(); it != mPrevCacheMode.constEnd(); ++it )
254 it.key()->setCacheMode( it.value() );
258 LayoutItemCacheSettingRestorer(
const LayoutItemCacheSettingRestorer &other ) =
delete;
259 LayoutItemCacheSettingRestorer &operator=(
const LayoutItemCacheSettingRestorer &other ) =
delete;
262 QgsLayout *mLayout =
nullptr;
263 QHash< QGraphicsItem *, QGraphicsItem::CacheMode > mPrevCacheMode;
270 QPaintDevice *paintDevice = painter->device();
271 if ( !paintDevice || !mLayout )
276 LayoutItemCacheSettingRestorer cacheRestorer( mLayout );
277 ( void ) cacheRestorer;
278 LayoutContextPreviewSettingRestorer restorer( mLayout );
280 LayoutGuideHider guideHider( mLayout );
285 mLayout->render( painter, QRectF( 0, 0, paintDevice->width(), paintDevice->height() ), region );
293 LayoutContextPreviewSettingRestorer restorer( mLayout );
296 double resolution = mLayout->renderContext().dpi();
298 if ( imageSize.isValid() )
302 resolution = ( imageSize.width() / region.width() + imageSize.height() / region.height() ) / 2.0 * oneInchInLayoutUnits;
310 int width = imageSize.isValid() ? imageSize.width() :
static_cast< int >( resolution * region.width() / oneInchInLayoutUnits );
311 int height = imageSize.isValid() ? imageSize.height() :
static_cast< int >( resolution * region.height() / oneInchInLayoutUnits );
313 QImage image( QSize( width, height ), QImage::Format_ARGB32 );
314 if ( !image.isNull() )
317 if ( width > 32768 || height > 32768 )
318 QgsMessageLog::logMessage( QObject::tr(
"Error: output width or height is larger than 32768 pixel, result will be clipped" ) );
319 image.setDotsPerMeterX(
static_cast< int >( std::round( resolution / 25.4 * 1000 ) ) );
320 image.setDotsPerMeterY(
static_cast< int>( std::round( resolution / 25.4 * 1000 ) ) );
321 image.fill( Qt::transparent );
322 QPainter imagePainter( &image );
324 if ( !imagePainter.isActive() )
332class LayoutContextSettingsRestorer
336 LayoutContextSettingsRestorer(
QgsLayout *layout )
338 , mPreviousDpi( layout->renderContext().dpi() )
339 , mPreviousFlags( layout->renderContext().flags() )
340 , mPreviousRasterPolicy( layout->renderContext().rasterizedRenderingPolicy() )
341 , mPreviousTextFormat( layout->renderContext().textRenderFormat() )
342 , mPreviousExportLayer( layout->renderContext().currentExportLayer() )
343 , mPreviousSimplifyMethod( layout->renderContext().simplifyMethod() )
344 , mPreviousMaskSettings( layout->renderContext().maskSettings() )
345 , mExportThemes( layout->renderContext().exportThemes() )
346 , mPredefinedScales( layout->renderContext().predefinedScales() )
350 ~LayoutContextSettingsRestorer()
352 mLayout->renderContext().setDpi( mPreviousDpi );
353 mLayout->renderContext().setFlags( mPreviousFlags );
354 mLayout->renderContext().setRasterizedRenderingPolicy( mPreviousRasterPolicy );
355 mLayout->renderContext().setTextRenderFormat( mPreviousTextFormat );
357 mLayout->renderContext().setCurrentExportLayer( mPreviousExportLayer );
359 mLayout->renderContext().setSimplifyMethod( mPreviousSimplifyMethod );
360 mLayout->renderContext().setMaskSettings( mPreviousMaskSettings );
361 mLayout->renderContext().setExportThemes( mExportThemes );
362 mLayout->renderContext().setPredefinedScales( mPredefinedScales );
365 LayoutContextSettingsRestorer(
const LayoutContextSettingsRestorer &other ) =
delete;
366 LayoutContextSettingsRestorer &operator=(
const LayoutContextSettingsRestorer &other ) =
delete;
369 QgsLayout *mLayout =
nullptr;
370 double mPreviousDpi = 0;
374 int mPreviousExportLayer = 0;
375 QgsVectorSimplifyMethod mPreviousSimplifyMethod;
376 QgsMaskRenderSettings mPreviousMaskSettings;
377 QStringList mExportThemes;
378 QVector< double > mPredefinedScales;
388 if ( settings.
dpi <= 0 )
389 settings.
dpi = mLayout->renderContext().dpi();
391 mErrorFileName.clear();
393 int worldFilePageNo = -1;
396 worldFilePageNo = referenceMap->page();
399 QFileInfo fi( filePath );
401 if ( !dir.exists( fi.absolutePath() ) )
403 dir.mkpath( fi.absolutePath() );
408 pageDetails.
baseName = fi.completeBaseName();
411 LayoutContextPreviewSettingRestorer restorer( mLayout );
413 LayoutContextSettingsRestorer dpiRestorer( mLayout );
414 ( void ) dpiRestorer;
415 mLayout->renderContext().setDpi( settings.
dpi );
416 mLayout->renderContext().setFlags( settings.
flags );
421 if ( settings.
pages.empty() )
423 for (
int page = 0; page < mLayout->pageCollection()->pageCount(); ++page )
428 for (
int page : std::as_const( settings.
pages ) )
430 if ( page >= 0 && page < mLayout->pageCollection()->pageCount() )
435 for (
int page : std::as_const( pages ) )
437 if ( !mLayout->pageCollection()->shouldExportPage( page ) )
444 QImage image = createImage( settings, page, bounds, skip );
449 pageDetails.
page = page;
452 if ( image.isNull() )
454 mErrorFileName = outputFilePath;
460 mErrorFileName = outputFilePath;
464 const bool shouldGeoreference = ( page == worldFilePageNo );
465 if ( shouldGeoreference )
467 georeferenceOutputPrivate( outputFilePath,
nullptr, bounds, settings.
dpi, shouldGeoreference );
472 double a, b,
c, d, e, f;
473 if ( bounds.isValid() )
478 QFileInfo fi( outputFilePath );
480 QString outputSuffix = fi.suffix();
481 QString worldFileName = fi.absolutePath() +
'/' + fi.completeBaseName() +
'.' + outputSuffix.at( 0 ) + outputSuffix.at( fi.suffix().size() - 1 ) +
'w';
483 writeWorldFile( worldFileName, a, b,
c, d, e, f );
487 captureLabelingResults();
500 int total = iterator->
count();
501 double step = total > 0 ? 100.0 / total : 100.0;
503 while ( iterator->
next() )
508 feedback->setProperty(
"progress", QObject::tr(
"Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
510 feedback->setProperty(
"progress", QObject::tr(
"Exporting section %1" ).arg( i + 1 ) );
520 QString filePath = iterator->
filePath( baseFilePath, extension );
525 error = QObject::tr(
"Cannot write to %1. This file may be open in another application or may be an invalid path." ).arg( QDir::toNativeSeparators( filePath ) );
545 if ( !mLayout || mLayout->pageCollection()->pageCount() == 0 )
549 if ( settings.
dpi <= 0 )
550 settings.
dpi = mLayout->renderContext().dpi();
552 mErrorFileName.clear();
554 LayoutContextPreviewSettingRestorer restorer( mLayout );
556 LayoutContextSettingsRestorer contextRestorer( mLayout );
557 ( void ) contextRestorer;
558 mLayout->renderContext().setDpi( settings.
dpi );
560 mLayout->renderContext().setMaskSettings( createExportMaskSettings() );
564 mLayout->renderContext().setSimplifyMethod( createExportSimplifyMethod() );
567 std::unique_ptr< QgsLayoutGeospatialPdfExporter > geospatialPdfExporter;
569 geospatialPdfExporter = std::make_unique< QgsLayoutGeospatialPdfExporter >( mLayout );
571 mLayout->renderContext().setFlags( settings.
flags );
594 mLayout->renderContext().setExportThemes( settings.
exportThemes );
607 const QList<QGraphicsItem *> items = mLayout->items( Qt::AscendingOrder );
611 bool res = geospatialPdfExporter->setMapItemLayersBeforeRendering();
617 mErrorMessage = u
"The Geospatial PDF cannot be exported following QGIS project configuration: At least one map layout item must not follow map themes nor locked layers."_s;
622 QList< QgsLayoutGeospatialPdfExporter::ComponentLayerDetail > pdfComponents;
627 QSet<QString> mutuallyExclusiveGroups;
630 [
this, &subSettings, &pdfComponents, &geospatialPdfExporter, &settings, &baseDir, &baseFileName, &mutuallyExclusiveGroups](
unsigned int layerId,
const QgsLayoutItem::ExportLayerDetail &layerDetail )
634 component.
name = layerDetail.name;
635 component.
mapLayerId = layerDetail.mapLayerId;
636 component.
opacity = layerDetail.opacity;
638 component.
group = layerDetail.groupName;
639 if ( !layerDetail.mapTheme.isEmpty() )
641 component.
group = layerDetail.mapTheme;
642 mutuallyExclusiveGroups.insert( layerDetail.mapTheme );
645 component.
sourcePdfPath = settings.writeGeoPdf ? geospatialPdfExporter->generateTemporaryFilepath( u
"layer_%1.pdf"_s.arg( layerId ) )
646 : baseDir.filePath( u
"%1_%2.pdf"_s.arg( baseFileName ).arg( layerId, 4, 10, QChar(
'0' ) ) );
647 pdfComponents << component;
649 preparePrintAsPdf( mLayout, &printer, component.
sourcePdfPath );
650 preparePrint( mLayout, &printer,
false );
652 if ( !p.begin( &printer ) )
660 return layerExportResult;
662 auto getExportGroupNameFunc = [](
QgsLayoutItem *item ) -> QString {
return item->customProperty( u
"pdfExportGroup"_s ).toString(); };
663 result = handleLayeredExport( items, exportFunc, getExportGroupNameFunc );
665 if ( settings.writeGeoPdf && settings.useLayerTreeConfig )
668 geospatialPdfExporter->restoreMapItemLayersAfterRendering();
673 if ( settings.writeGeoPdf )
676 details.
dpi = settings.dpi;
678 QgsLayoutSize pageSize = mLayout->pageCollection()->page( 0 )->sizeWithUnits();
682 if ( settings.exportMetadata )
685 details.
author = mLayout->project()->metadata().author();
687 details.
creator = getCreator();
688 details.
creationDateTime = mLayout->project()->metadata().creationDateTime();
689 details.
subject = mLayout->project()->metadata().abstract();
690 details.
title = mLayout->project()->metadata().title();
691 details.
keywords = mLayout->project()->metadata().keywords();
694 if ( settings.appendGeoreference )
697 QList< QgsLayoutItemMap * > maps;
698 mLayout->layoutItems( maps );
702 georef.
crs = map->crs();
704 const QPointF topLeft = map->mapToScene( QPointF( 0, 0 ) );
705 const QPointF topRight = map->mapToScene( QPointF( map->rect().width(), 0 ) );
706 const QPointF bottomLeft = map->mapToScene( QPointF( 0, map->rect().height() ) );
707 const QPointF bottomRight = map->mapToScene( QPointF( map->rect().width(), map->rect().height() ) );
714 QVector< QgsPointXY >()
723 const QTransform t = map->layoutToMapCoordsTransform();
724 const QgsPointXY topLeftMap = t.map( topLeft );
725 const QgsPointXY topRightMap = t.map( topRight );
726 const QgsPointXY bottomLeftMap = t.map( bottomLeft );
727 const QgsPointXY bottomRightMap = t.map( bottomRight );
737 if ( !settings.useLayerTreeConfig )
741 details.
layerOrder = geospatialPdfExporter->layerOrder();
745 const QList< QgsMapLayer * > layers = mLayout->project()->mapLayers().values();
755 if ( !geospatialPdfExporter->finalize( pdfComponents, filePath, details ) )
758 mErrorMessage = geospatialPdfExporter->errorMessage();
768 QPdfWriter printer = QPdfWriter( filePath );
769 preparePrintAsPdf( mLayout, &printer, filePath );
770 preparePrint( mLayout, &printer,
false );
772 if ( !p.begin( &printer ) )
781 bool shouldAppendGeoreference = settings.
appendGeoreference && mLayout && mLayout->referenceMap() && mLayout->referenceMap()->page() == 0;
784 georeferenceOutputPrivate( filePath,
nullptr, QRectF(), settings.
dpi, shouldAppendGeoreference, settings.
exportMetadata );
787 captureLabelingResults();
802 QPdfWriter printer = QPdfWriter( fileName );
805 int total = iterator->
count();
806 double step = total > 0 ? 100.0 / total : 100.0;
809 while ( iterator->
next() )
814 feedback->setProperty(
"progress", QObject::tr(
"Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
816 feedback->setProperty(
"progress", QObject::tr(
"Exporting section %1" ).arg( i + 1 ) );
828 LayoutContextPreviewSettingRestorer restorer( iterator->
layout() );
830 LayoutContextSettingsRestorer contextRestorer( iterator->
layout() );
831 ( void ) contextRestorer;
858 preparePrintAsPdf( iterator->
layout(), &printer, fileName );
859 preparePrint( iterator->
layout(), &printer,
false );
861 if ( !p.begin( &printer ) )
875 error = QObject::tr(
"Cannot write to %1. This file may be open in another application or may be an invalid path." ).arg( QDir::toNativeSeparators( fileName ) );
904 int total = iterator->
count();
905 double step = total > 0 ? 100.0 / total : 100.0;
907 while ( iterator->
next() )
912 feedback->setProperty(
"progress", QObject::tr(
"Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
914 feedback->setProperty(
"progress", QObject::tr(
"Exporting section %1" ).arg( i + 1 ) );
923 QString filePath = iterator->
filePath( baseFilePath, u
"pdf"_s );
930 error = QObject::tr(
"Cannot write to %1. This file may be open in another application or may be an invalid path." ).arg( QDir::toNativeSeparators( filePath ) );
948#if defined( HAVE_QTPRINTER )
955 if ( settings.
dpi <= 0 )
956 settings.
dpi = mLayout->renderContext().dpi();
958 mErrorFileName.clear();
960 LayoutContextPreviewSettingRestorer restorer( mLayout );
962 LayoutContextSettingsRestorer contextRestorer( mLayout );
963 ( void ) contextRestorer;
964 mLayout->renderContext().setDpi( settings.
dpi );
966 mLayout->renderContext().setFlags( settings.
flags );
973 preparePrint( mLayout, &printer,
true );
975 if ( !p.begin( &printer ) )
984 captureLabelingResults();
999 int total = iterator->
count();
1000 double step = total > 0 ? 100.0 / total : 100.0;
1003 while ( iterator->
next() )
1008 feedback->setProperty(
"progress", QObject::tr(
"Printing %1 of %2" ).arg( i + 1 ).arg( total ) );
1010 feedback->setProperty(
"progress", QObject::tr(
"Printing section %1" ).arg( i + 1 ) );
1022 LayoutContextPreviewSettingRestorer restorer( iterator->
layout() );
1024 LayoutContextSettingsRestorer contextRestorer( iterator->
layout() );
1025 ( void ) contextRestorer;
1034 if ( !settings.rasterizeWholeImage )
1039 preparePrint( iterator->
layout(), &printer,
true );
1041 if ( !p.begin( &printer ) )
1051 ExportResult result = exporter.printPrivate( &printer, p, !first, settings.dpi, settings.rasterizeWholeImage );
1055 error = exporter.errorMessage();
1078 if ( settings.
dpi <= 0 )
1079 settings.
dpi = mLayout->renderContext().dpi();
1081 mErrorFileName.clear();
1083 LayoutContextPreviewSettingRestorer restorer( mLayout );
1085 LayoutContextSettingsRestorer contextRestorer( mLayout );
1086 ( void ) contextRestorer;
1087 mLayout->renderContext().setDpi( settings.
dpi );
1089 mLayout->renderContext().setFlags( settings.
flags );
1101 mLayout->renderContext().setMaskSettings( createExportMaskSettings() );
1105 mLayout->renderContext().setSimplifyMethod( createExportSimplifyMethod() );
1108 QFileInfo fi( filePath );
1111 pageDetails.
baseName = fi.baseName();
1112 pageDetails.
extension = fi.completeSuffix();
1116 for (
int i = 0; i < mLayout->pageCollection()->pageCount(); ++i )
1118 if ( !mLayout->pageCollection()->shouldExportPage( i ) )
1123 pageDetails.
page = i;
1130 if ( mLayout->pageCollection()->pageCount() == 1 )
1133 bounds = mLayout->layoutBounds(
true );
1138 bounds = mLayout->pageItemBounds( i,
true );
1144 bounds = QRectF( pageItem->pos().x(), pageItem->pos().y(), pageItem->rect().width(), pageItem->rect().height() );
1148 int width =
static_cast< int >( bounds.width() * settings.
dpi / inchesToLayoutUnits );
1150 int height =
static_cast< int >( bounds.height() * settings.
dpi / inchesToLayoutUnits );
1151 if ( width == 0 || height == 0 )
1160 const QRectF paperRect = QRectF( pageItem->pos().x(), pageItem->pos().y(), pageItem->rect().width(), pageItem->rect().height() );
1162 QDomNode svgDocRoot;
1163 const QList<QGraphicsItem *> items = mLayout->items( paperRect, Qt::IntersectsItemBoundingRect, Qt::AscendingOrder );
1167 return renderToLayeredSvg( settings, width, height, i, bounds, fileName, layerId, layerDetail.name, svg, svgDocRoot, settings.
exportMetadata );
1169 auto getExportGroupNameFunc = [](
QgsLayoutItem * ) -> QString {
return QString(); };
1170 ExportResult res = handleLayeredExport( items, exportFunc, getExportGroupNameFunc );
1175 appendMetadataToSvg( svg );
1177 QFile out( fileName );
1178 bool openOk = out.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate );
1181 mErrorFileName = fileName;
1185 out.write( svg.toByteArray() );
1191 QSvgGenerator generator;
1194 generator.setTitle( mLayout->project()->metadata().title() );
1195 generator.setDescription( mLayout->project()->metadata().abstract() );
1197 generator.setOutputDevice( &svgBuffer );
1198 generator.setSize( QSize( width, height ) );
1199 generator.setViewBox( QRect( 0, 0, width, height ) );
1200 generator.setResolution(
static_cast< int >( std::round( settings.
dpi ) ) );
1203 bool createOk = p.begin( &generator );
1206 mErrorFileName = fileName;
1219 svgBuffer.open( QIODevice::ReadOnly );
1223 if ( !svg.setContent( &svgBuffer,
false, &errorMsg, &errorLine ) )
1225 mErrorFileName = fileName;
1230 appendMetadataToSvg( svg );
1232 QFile out( fileName );
1233 bool openOk = out.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate );
1236 mErrorFileName = fileName;
1240 out.write( svg.toByteArray() );
1244 captureLabelingResults();
1257 int total = iterator->
count();
1258 double step = total > 0 ? 100.0 / total : 100.0;
1260 while ( iterator->
next() )
1265 feedback->setProperty(
"progress", QObject::tr(
"Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
1267 feedback->setProperty(
"progress", QObject::tr(
"Exporting section %1" ).arg( i + 1 ) );
1277 QString filePath = iterator->
filePath( baseFilePath, u
"svg"_s );
1284 error = QObject::tr(
"Cannot write to %1. This file may be open in another application or may be an invalid path." ).arg( QDir::toNativeSeparators( filePath ) );
1304 return mLabelingResults;
1309 QMap<QString, QgsLabelingResults *> res;
1310 std::swap( mLabelingResults, res );
1314void QgsLayoutExporter::preparePrintAsPdf(
QgsLayout *layout, QPdfWriter *device,
const QString &filePath )
1316 QFileInfo fi( filePath );
1318 if ( !dir.exists( fi.absolutePath() ) )
1320 dir.mkpath( fi.absolutePath() );
1323 updatePrinterPageSize(
layout, device, firstPageToBeExported(
layout ) );
1326 const QString title = !
layout->project() ||
layout->project()->metadata().title().isEmpty() ? fi.baseName() :
layout->project()->metadata().title();
1328 device->setTitle( title );
1330 QPagedPaintDevice::PdfVersion pdfVersion = QPagedPaintDevice::PdfVersion_1_4;
1332#if QT_VERSION >= QT_VERSION_CHECK( 6, 8, 0 )
1334 if (
const QgsProjectStyleSettings *styleSettings = (
layout->project() ?
layout->project()->styleSettings() :
nullptr ) )
1338 switch ( styleSettings->colorModel() )
1341 device->setColorModel( QPdfWriter::ColorModel::CMYK );
1345 device->setColorModel( QPdfWriter::ColorModel::RGB );
1349 const QColorSpace colorSpace = styleSettings->colorSpace();
1350 if ( colorSpace.isValid() )
1352 QPdfOutputIntent outputIntent;
1353 outputIntent.setOutputProfile( colorSpace );
1354 outputIntent.setOutputCondition( colorSpace.description() );
1358 outputIntent.setOutputConditionIdentifier( u
"Unknown identifier"_s );
1359 outputIntent.setRegistryName( u
"Unknown registry"_s );
1360 device->setOutputIntent( outputIntent );
1363 pdfVersion = QPagedPaintDevice::PdfVersion_X4;
1369 device->setPdfVersion( pdfVersion );
1370 setXmpMetadata( device,
layout );
1377void QgsLayoutExporter::preparePrint(
QgsLayout *layout, QPagedPaintDevice *device,
bool setFirstPageSize )
1379 if ( QPdfWriter *pdf =
dynamic_cast<QPdfWriter *
>( device ) )
1381 pdf->setResolution(
static_cast< int>( std::round(
layout->renderContext().dpi() ) ) );
1383#if defined( HAVE_QTPRINTER )
1384 else if ( QPrinter *printer =
dynamic_cast<QPrinter *
>( device ) )
1386 printer->setFullPage(
true );
1387 printer->setColorMode( QPrinter::Color );
1389 printer->setResolution(
static_cast< int>( std::round(
layout->renderContext().dpi() ) ) );
1393 if ( setFirstPageSize )
1395 updatePrinterPageSize(
layout, device, firstPageToBeExported(
layout ) );
1401 if ( mLayout->pageCollection()->pageCount() == 0 )
1404 preparePrint( mLayout, device,
true );
1406 if ( !p.begin( device ) )
1412 printPrivate( device, p );
1417QgsLayoutExporter::ExportResult QgsLayoutExporter::printPrivate( QPagedPaintDevice *device, QPainter &painter,
bool startNewPage,
double dpi,
bool rasterize )
1421 int toPage = mLayout->pageCollection()->pageCount() - 1;
1423#if defined( HAVE_QTPRINTER )
1424 if ( QPrinter *printer =
dynamic_cast<QPrinter *
>( device ) )
1426 if ( printer->fromPage() >= 1 )
1427 fromPage = printer->fromPage() - 1;
1428 if ( printer->toPage() >= 1 )
1429 toPage = printer->toPage() - 1;
1433 bool pageExported =
false;
1436 for (
int i = fromPage; i <= toPage; ++i )
1438 if ( !mLayout->pageCollection()->shouldExportPage( i ) )
1443 updatePrinterPageSize( mLayout, device, i );
1444 if ( ( pageExported && i > fromPage ) || startNewPage )
1450 if ( !image.isNull() )
1452 QRectF targetArea( 0, 0, image.width(), image.height() );
1453 painter.drawImage( targetArea, image, targetArea );
1459 pageExported =
true;
1464 for (
int i = fromPage; i <= toPage; ++i )
1466 if ( !mLayout->pageCollection()->shouldExportPage( i ) )
1471 updatePrinterPageSize( mLayout, device, i );
1473 if ( ( pageExported && i > fromPage ) || startNewPage )
1478 pageExported =
true;
1484void QgsLayoutExporter::updatePrinterPageSize(
QgsLayout *layout, QPagedPaintDevice *device,
int page )
1486 QgsLayoutSize pageSize =
layout->pageCollection()->page( page )->sizeWithUnits();
1489 QPageLayout pageLayout( QPageSize( pageSizeMM.
toQSizeF(), QPageSize::Millimeter ), QPageLayout::Portrait, QMarginsF( 0, 0, 0, 0 ) );
1490 pageLayout.setMode( QPageLayout::FullPageMode );
1491 device->setPageLayout( pageLayout );
1492 device->setPageMargins( QMarginsF( 0, 0, 0, 0 ) );
1494#if defined( HAVE_QTPRINTER )
1495 if ( QPrinter *printer =
dynamic_cast<QPrinter *
>( device ) )
1497 printer->setFullPage(
true );
1503 const SvgExportSettings &settings,
1507 const QRectF &bounds,
1508 const QString &filename,
1509 unsigned int svgLayerId,
1510 const QString &layerName,
1512 QDomNode &svgDocRoot,
1513 bool includeMetadata
1518 QSvgGenerator generator;
1519 if ( includeMetadata )
1521 if (
const QgsMasterLayoutInterface *l =
dynamic_cast< const QgsMasterLayoutInterface *
>( mLayout.data() ) )
1522 generator.setTitle( l->name() );
1523 else if ( mLayout->project() )
1524 generator.setTitle( mLayout->project()->title() );
1527 generator.setOutputDevice( &svgBuffer );
1528 generator.setSize( QSize(
static_cast< int >( std::round( width ) ),
static_cast< int >( std::round( height ) ) ) );
1529 generator.setViewBox( QRect( 0, 0,
static_cast< int >( std::round( width ) ),
static_cast< int >( std::round( height ) ) ) );
1530 generator.setResolution(
static_cast< int >( std::round( settings.dpi ) ) );
1532 QPainter svgPainter( &generator );
1533 if ( settings.cropToContents )
1544 svgBuffer.open( QIODevice::ReadOnly );
1548 if ( !doc.setContent( &svgBuffer,
false, &errorMsg, &errorLine ) )
1550 mErrorFileName = filename;
1553 if ( 1 == svgLayerId )
1555 svg = QDomDocument( doc.doctype() );
1556 svg.appendChild( svg.importNode( doc.firstChild(),
false ) );
1557 svgDocRoot = svg.importNode( doc.elementsByTagName( u
"svg"_s ).at( 0 ),
false );
1558 svgDocRoot.toElement().setAttribute( u
"xmlns:inkscape"_s, u
"http://www.inkscape.org/namespaces/inkscape"_s );
1559 svg.appendChild( svgDocRoot );
1561 QDomNode mainGroup = svg.importNode( doc.elementsByTagName( u
"g"_s ).at( 0 ),
true );
1562 mainGroup.toElement().setAttribute( u
"id"_s, layerName );
1563 mainGroup.toElement().setAttribute( u
"inkscape:label"_s, layerName );
1564 mainGroup.toElement().setAttribute( u
"inkscape:groupmode"_s, u
"layer"_s );
1565 QDomNode defs = svg.importNode( doc.elementsByTagName( u
"defs"_s ).at( 0 ),
true );
1566 svgDocRoot.appendChild( defs );
1567 svgDocRoot.appendChild( mainGroup );
1572void QgsLayoutExporter::appendMetadataToSvg( QDomDocument &svg )
const
1574 const QgsProjectMetadata &metadata = mLayout->project()->metadata();
1575 QDomElement metadataElement = svg.createElement( u
"metadata"_s );
1576 QDomElement rdfElement = svg.createElement( u
"rdf:RDF"_s );
1577 rdfElement.setAttribute( u
"xmlns:rdf"_s, u
"http://www.w3.org/1999/02/22-rdf-syntax-ns#"_s );
1578 rdfElement.setAttribute( u
"xmlns:rdfs"_s, u
"http://www.w3.org/2000/01/rdf-schema#"_s );
1579 rdfElement.setAttribute( u
"xmlns:dc"_s, u
"http://purl.org/dc/elements/1.1/"_s );
1580 QDomElement descriptionElement = svg.createElement( u
"rdf:Description"_s );
1581 QDomElement workElement = svg.createElement( u
"cc:Work"_s );
1582 workElement.setAttribute( u
"rdf:about"_s, QString() );
1584 auto addTextNode = [&workElement, &descriptionElement, &svg](
const QString &tag,
const QString &value ) {
1586 QDomElement element = svg.createElement( tag );
1587 QDomText t = svg.createTextNode( value );
1588 element.appendChild( t );
1589 workElement.appendChild( element );
1592 descriptionElement.setAttribute( tag, value );
1595 addTextNode( u
"dc:format"_s, u
"image/svg+xml"_s );
1596 addTextNode( u
"dc:title"_s, metadata.
title() );
1597 addTextNode( u
"dc:date"_s, metadata.
creationDateTime().toString( Qt::ISODate ) );
1598 addTextNode( u
"dc:identifier"_s, metadata.
identifier() );
1599 addTextNode( u
"dc:description"_s, metadata.
abstract() );
1601 auto addAgentNode = [&workElement, &descriptionElement, &svg](
const QString &tag,
const QString &value ) {
1603 QDomElement inkscapeElement = svg.createElement( tag );
1604 QDomElement agentElement = svg.createElement( u
"cc:Agent"_s );
1605 QDomElement titleElement = svg.createElement( u
"dc:title"_s );
1606 QDomText t = svg.createTextNode( value );
1607 titleElement.appendChild( t );
1608 agentElement.appendChild( titleElement );
1609 inkscapeElement.appendChild( agentElement );
1610 workElement.appendChild( inkscapeElement );
1613 QDomElement bagElement = svg.createElement( u
"rdf:Bag"_s );
1614 QDomElement liElement = svg.createElement( u
"rdf:li"_s );
1615 t = svg.createTextNode( value );
1616 liElement.appendChild( t );
1617 bagElement.appendChild( liElement );
1619 QDomElement element = svg.createElement( tag );
1620 element.appendChild( bagElement );
1621 descriptionElement.appendChild( element );
1624 addAgentNode( u
"dc:creator"_s, metadata.
author() );
1625 addAgentNode( u
"dc:publisher"_s, getCreator() );
1629 QDomElement element = svg.createElement( u
"dc:subject"_s );
1630 QDomElement bagElement = svg.createElement( u
"rdf:Bag"_s );
1632 for (
auto it = keywords.constBegin(); it != keywords.constEnd(); ++it )
1634 const QStringList words = it.value();
1635 for (
const QString &keyword : words )
1637 QDomElement liElement = svg.createElement( u
"rdf:li"_s );
1638 QDomText t = svg.createTextNode( keyword );
1639 liElement.appendChild( t );
1640 bagElement.appendChild( liElement );
1643 element.appendChild( bagElement );
1644 workElement.appendChild( element );
1645 descriptionElement.appendChild( element );
1648 rdfElement.appendChild( descriptionElement );
1649 rdfElement.appendChild( workElement );
1650 metadataElement.appendChild( rdfElement );
1651 svg.documentElement().appendChild( metadataElement );
1652 svg.documentElement().setAttribute( u
"xmlns:cc"_s, u
"http://creativecommons.org/ns#"_s );
1655std::unique_ptr<double[]> QgsLayoutExporter::computeGeoTransform(
const QgsLayoutItemMap *map,
const QRectF ®ion,
double dpi )
const
1658 map = mLayout->referenceMap();
1664 dpi = mLayout->renderContext().dpi();
1667 QRectF exportRegion = region;
1668 if ( !exportRegion.isValid() )
1670 int pageNumber = map->
page();
1672 QgsLayoutItemPage *page = mLayout->pageCollection()->page( pageNumber );
1673 double pageY = page->pos().y();
1674 QSizeF pageSize = page->rect().size();
1675 exportRegion = QRectF( 0, pageY, pageSize.width(), pageSize.height() );
1679 QRectF mapItemSceneRect = map->mapRectToScene( map->rect() );
1682 double outputHeightMM = exportRegion.height();
1683 double outputWidthMM = exportRegion.width();
1686 QgsRectangle mapExtent = map->
extent();
1687 double mapXCenter = mapExtent.
center().
x();
1688 double mapYCenter = mapExtent.
center().
y();
1690 double sinAlpha = std::sin( alpha );
1691 double cosAlpha = std::cos( alpha );
1694 QPointF mapItemPos = map->pos();
1696 mapItemPos.rx() -= exportRegion.left();
1697 mapItemPos.ry() -= exportRegion.top();
1700 double xRatio = mapExtent.
width() / mapItemSceneRect.width();
1701 double yRatio = mapExtent.
height() / mapItemSceneRect.height();
1702 double xmin = mapExtent.
xMinimum() - mapItemPos.x() * xRatio;
1703 double ymax = mapExtent.
yMaximum() + mapItemPos.y() * yRatio;
1704 QgsRectangle paperExtent( xmin, ymax - outputHeightMM * yRatio, xmin + outputWidthMM * xRatio, ymax );
1707 double X0 = paperExtent.xMinimum();
1708 double Y0 = paperExtent.yMaximum();
1713 double X1 = X0 - mapXCenter;
1714 double Y1 = Y0 - mapYCenter;
1715 double X2 = X1 * cosAlpha + Y1 * sinAlpha;
1716 double Y2 = -X1 * sinAlpha + Y1 * cosAlpha;
1717 X0 = X2 + mapXCenter;
1718 Y0 = Y2 + mapYCenter;
1722 int pageWidthPixels =
static_cast< int >( dpi * outputWidthMM / 25.4 );
1723 int pageHeightPixels =
static_cast< int >( dpi * outputHeightMM / 25.4 );
1724 double pixelWidthScale = paperExtent.width() / pageWidthPixels;
1725 double pixelHeightScale = paperExtent.height() / pageHeightPixels;
1728 std::unique_ptr<double[]> t(
new double[6] );
1730 t[1] = cosAlpha * pixelWidthScale;
1731 t[2] = -sinAlpha * pixelWidthScale;
1733 t[4] = -sinAlpha * pixelHeightScale;
1734 t[5] = -cosAlpha * pixelHeightScale;
1739void QgsLayoutExporter::writeWorldFile(
const QString &worldFileName,
double a,
double b,
double c,
double d,
double e,
double f )
const
1741 QFile worldFile( worldFileName );
1742 if ( !worldFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
1746 QTextStream fout( &worldFile );
1750 fout << QString::number( a,
'f', 12 ) <<
"\r\n";
1751 fout << QString::number( d,
'f', 12 ) <<
"\r\n";
1752 fout << QString::number( b,
'f', 12 ) <<
"\r\n";
1753 fout << QString::number( e,
'f', 12 ) <<
"\r\n";
1754 fout << QString::number(
c,
'f', 12 ) <<
"\r\n";
1755 fout << QString::number( f,
'f', 12 ) <<
"\r\n";
1760 return georeferenceOutputPrivate( file, map, exportRegion, dpi,
false );
1763bool QgsLayoutExporter::georeferenceOutputPrivate(
const QString &file,
QgsLayoutItemMap *map,
const QRectF &exportRegion,
double dpi,
bool includeGeoreference,
bool includeMetadata )
const
1768 if ( !map && includeGeoreference )
1769 map = mLayout->referenceMap();
1771 std::unique_ptr<double[]> t;
1773 if ( map && includeGeoreference )
1776 dpi = mLayout->renderContext().dpi();
1778 t = computeGeoTransform( map, exportRegion, dpi );
1783 CPLSetConfigOption(
"GDAL_PDF_DPI", QString::number( dpi ).toUtf8().constData() );
1788 GDALSetGeoTransform( outputDS.get(), t.get() );
1790 if ( includeMetadata )
1792 QString creationDateString;
1793 const QDateTime creationDateTime = mLayout->project()->metadata().creationDateTime();
1794#if QT_FEATURE_timezone > 0
1795 if ( creationDateTime.isValid() )
1797 creationDateString = u
"D:%1"_s.arg( mLayout->project()->metadata().creationDateTime().toString( u
"yyyyMMddHHmmss"_s ) );
1798 if ( creationDateTime.timeZone().isValid() )
1800 int offsetFromUtc = creationDateTime.timeZone().offsetFromUtc( creationDateTime );
1801 creationDateString += ( offsetFromUtc >= 0 ) ?
'+' :
'-';
1802 offsetFromUtc = std::abs( offsetFromUtc );
1803 int offsetHours = offsetFromUtc / 3600;
1804 int offsetMins = ( offsetFromUtc % 3600 ) / 60;
1805 creationDateString += u
"%1'%2'"_s.arg( offsetHours ).arg( offsetMins );
1809 QgsDebugError( u
"Qt is built without timezone support, skipping timezone for pdf export"_s );
1811 GDALSetMetadataItem( outputDS.get(),
"CREATION_DATE", creationDateString.toUtf8().constData(),
nullptr );
1813 GDALSetMetadataItem( outputDS.get(),
"AUTHOR", mLayout->project()->metadata().author().toUtf8().constData(),
nullptr );
1814 const QString creator = getCreator();
1815 GDALSetMetadataItem( outputDS.get(),
"CREATOR", creator.toUtf8().constData(),
nullptr );
1816 GDALSetMetadataItem( outputDS.get(),
"PRODUCER", creator.toUtf8().constData(),
nullptr );
1817 GDALSetMetadataItem( outputDS.get(),
"SUBJECT", mLayout->project()->metadata().abstract().toUtf8().constData(),
nullptr );
1818 GDALSetMetadataItem( outputDS.get(),
"TITLE", mLayout->project()->metadata().title().toUtf8().constData(),
nullptr );
1821 QStringList allKeywords;
1822 for (
auto it = keywords.constBegin(); it != keywords.constEnd(); ++it )
1824 allKeywords.append( u
"%1: %2"_s.arg( it.key(), it.value().join(
',' ) ) );
1826 const QString keywordString = allKeywords.join(
';' );
1827 GDALSetMetadataItem( outputDS.get(),
"KEYWORDS", keywordString.toUtf8().constData(),
nullptr );
1833 CPLSetConfigOption(
"GDAL_PDF_DPI",
nullptr );
1840 if ( items.count() == 1 )
1844 QString name = layoutItem->displayName();
1846 if ( name.startsWith(
'<' ) && name.endsWith(
'>' ) )
1847 name = name.mid( 1, name.length() - 2 );
1851 else if ( items.count() > 1 )
1853 QStringList currentLayerItemTypes;
1854 for ( QGraphicsItem *item : items )
1860 if ( !currentLayerItemTypes.contains( itemType ) && !currentLayerItemTypes.contains( itemTypePlural ) )
1861 currentLayerItemTypes << itemType;
1862 else if ( currentLayerItemTypes.contains( itemType ) )
1864 currentLayerItemTypes.replace( currentLayerItemTypes.indexOf( itemType ), itemTypePlural );
1869 if ( !currentLayerItemTypes.contains( QObject::tr(
"Other" ) ) )
1870 currentLayerItemTypes.append( QObject::tr(
"Other" ) );
1873 return currentLayerItemTypes.join(
", "_L1 );
1875 return QObject::tr(
"Layer %1" ).arg( layerId );
1879 const QList<QGraphicsItem *> &items,
1881 const std::function<QString(
QgsLayoutItem *item )> &getItemExportGroupFunc
1884 LayoutItemHider itemHider( items );
1889 QString previousItemGroup;
1890 unsigned int layerId = 1;
1891 QgsLayoutItem::ExportLayerDetail layerDetails;
1892 itemHider.hideAll();
1893 const QList< QGraphicsItem * > itemsToIterate = itemHider.itemsToIterate();
1894 QList< QGraphicsItem * > currentLayerItems;
1895 for ( QGraphicsItem *item : itemsToIterate )
1897 QgsLayoutItem *layoutItem =
dynamic_cast<QgsLayoutItem *
>( item );
1899 bool canPlaceInExistingLayer =
false;
1900 QString thisItemExportGroupName;
1904 thisItemExportGroupName = getItemExportGroupFunc( layoutItem );
1905 if ( !thisItemExportGroupName.isEmpty() )
1907 if ( thisItemExportGroupName != previousItemGroup && !currentLayerItems.empty() )
1910 layerDetails.
groupName = thisItemExportGroupName;
1913 switch ( itemExportBehavior )
1917 switch ( prevItemBehavior )
1920 canPlaceInExistingLayer =
true;
1924 canPlaceInExistingLayer = prevType == -1 || prevType == layoutItem->
type();
1929 canPlaceInExistingLayer =
false;
1937 switch ( prevItemBehavior )
1941 canPlaceInExistingLayer = prevType == -1 || prevType == layoutItem->
type();
1946 canPlaceInExistingLayer =
false;
1954 canPlaceInExistingLayer =
false;
1959 canPlaceInExistingLayer =
false;
1962 prevItemBehavior = itemExportBehavior;
1963 prevType = layoutItem->
type();
1964 previousItemGroup = thisItemExportGroupName;
1969 previousItemGroup.clear();
1972 if ( canPlaceInExistingLayer )
1974 currentLayerItems << item;
1979 if ( !currentLayerItems.isEmpty() )
1983 ExportResult result = exportFunc( layerId, layerDetails );
1987 currentLayerItems.clear();
1990 itemHider.hideAll();
1995 int layoutItemLayerIdx = 0;
1997 mLayout->renderContext().setCurrentExportLayer( layoutItemLayerIdx );
2003 mLayout->renderContext().setCurrentExportLayer( layoutItemLayerIdx );
2007 ExportResult result = exportFunc( layerId, layerDetails );
2012 layoutItemLayerIdx++;
2014 layerDetails.mapLayerId.clear();
2015 layerDetails.mapTheme.clear();
2017 mLayout->renderContext().setCurrentExportLayer( -1 );
2020 currentLayerItems.clear();
2024 currentLayerItems << item;
2026 layerDetails.groupName = thisItemExportGroupName;
2029 if ( !currentLayerItems.isEmpty() )
2032 ExportResult result = exportFunc( layerId, layerDetails );
2041 QgsVectorSimplifyMethod simplifyMethod;
2047 return simplifyMethod;
2052 QgsMaskRenderSettings settings;
2070 int pageNumber = map->
page();
2072 double pageY = page->pos().y();
2073 QSizeF pageSize = page->rect().size();
2074 QRectF pageRect( 0, pageY, pageSize.width(), pageSize.height() );
2090 double destinationHeight = exportRegion.height();
2091 double destinationWidth = exportRegion.width();
2093 QRectF mapItemSceneRect = map->mapRectToScene( map->rect() );
2098 double xRatio = mapExtent.
width() / mapItemSceneRect.width();
2099 double yRatio = mapExtent.
height() / mapItemSceneRect.height();
2101 double xCenter = mapExtent.
center().
x();
2102 double yCenter = mapExtent.
center().
y();
2105 QPointF mapItemPos = map->pos();
2107 mapItemPos.rx() -= exportRegion.left();
2108 mapItemPos.ry() -= exportRegion.top();
2110 double xmin = mapExtent.
xMinimum() - mapItemPos.x() * xRatio;
2111 double ymax = mapExtent.
yMaximum() + mapItemPos.y() * yRatio;
2112 QgsRectangle paperExtent( xmin, ymax - destinationHeight * yRatio, xmin + destinationWidth * xRatio, ymax );
2114 double X0 = paperExtent.
xMinimum();
2115 double Y0 = paperExtent.
yMinimum();
2118 dpi = mLayout->renderContext().dpi();
2120 int widthPx =
static_cast< int >( dpi * destinationWidth / 25.4 );
2121 int heightPx =
static_cast< int >( dpi * destinationHeight / 25.4 );
2123 double Ww = paperExtent.
width() / widthPx;
2124 double Hh = paperExtent.
height() / heightPx;
2133 s[5] = Y0 + paperExtent.
height();
2137 r[0] = std::cos( alpha );
2138 r[1] = -std::sin( alpha );
2139 r[2] = xCenter * ( 1 - std::cos( alpha ) ) + yCenter * std::sin( alpha );
2140 r[3] = std::sin( alpha );
2141 r[4] = std::cos( alpha );
2142 r[5] = -xCenter * std::sin( alpha ) + yCenter * ( 1 - std::cos( alpha ) );
2145 a = r[0] * s[0] + r[1] * s[3];
2146 b = r[0] * s[1] + r[1] * s[4];
2147 c = r[0] * s[2] + r[1] * s[5] + r[2];
2148 d = r[3] * s[0] + r[4] * s[3];
2149 e = r[3] * s[1] + r[4] * s[4];
2150 f = r[3] * s[2] + r[4] * s[5] + r[5];
2158 QList< QgsLayoutItem *> items;
2159 layout->layoutItems( items );
2164 if ( currentItem->isVisible() && currentItem->requiresRasterization() )
2175 QList< QgsLayoutItem *> items;
2176 layout->layoutItems( items );
2181 if ( currentItem->isVisible() && currentItem->containsAdvancedEffects() )
2194 if ( mLayout->pageCollection()->pageCount() == 1 )
2197 bounds = mLayout->layoutBounds(
true );
2202 bounds = mLayout->pageItemBounds( page,
true );
2204 if ( bounds.width() <= 0 || bounds.height() <= 0 )
2222int QgsLayoutExporter::firstPageToBeExported(
QgsLayout *layout )
2224 const int pageCount =
layout->pageCollection()->pageCount();
2225 for (
int i = 0; i < pageCount; ++i )
2227 if ( !
layout->pageCollection()->shouldExportPage( i ) )
2239 if ( details.
page == 0 )
2249void QgsLayoutExporter::captureLabelingResults()
2251 qDeleteAll( mLabelingResults );
2252 mLabelingResults.clear();
2254 QList< QgsLayoutItemMap * > maps;
2255 mLayout->layoutItems( maps );
2259 mLabelingResults[map->
uuid()] = map->mExportLabelingResults.release();
2263bool QgsLayoutExporter::saveImage(
const QImage &image,
const QString &imageFilename,
const QString &imageFormat,
QgsProject *projectForMetadata,
int quality )
2265 QImageWriter w( imageFilename, imageFormat.toLocal8Bit().constData() );
2266 if ( imageFormat.compare(
"tiff"_L1, Qt::CaseInsensitive ) == 0 || imageFormat.compare(
"tif"_L1, Qt::CaseInsensitive ) == 0 )
2268 w.setCompression( 1 );
2272 w.setQuality( quality );
2274 if ( projectForMetadata )
2276 w.setText( u
"Author"_s, projectForMetadata->
metadata().
author() );
2277 const QString creator = getCreator();
2278 w.setText( u
"Creator"_s, creator );
2279 w.setText( u
"Producer"_s, creator );
2282 w.setText( u
"Title"_s, projectForMetadata->
metadata().
title() );
2285 QStringList allKeywords;
2286 for (
auto it = keywords.constBegin(); it != keywords.constEnd(); ++it )
2288 allKeywords.append( u
"%1: %2"_s.arg( it.key(), it.value().join(
',' ) ) );
2290 const QString keywordString = allKeywords.join(
';' );
2291 w.setText( u
"Keywords"_s, keywordString );
2293 return w.write( image );
2296QString QgsLayoutExporter::getCreator()
2301void QgsLayoutExporter::setXmpMetadata( QPdfWriter *pdfWriter,
QgsLayout *layout )
2303#if QT_VERSION >= QT_VERSION_CHECK( 6, 8, 0 )
2304 QUuid documentId = pdfWriter->documentId();
2306 QUuid documentId = QUuid::createUuid();
2310 const QDateTime creationDateTime =
layout->project() ?
layout->project()->metadata().creationDateTime() : QDateTime();
2311 const QString metaDataDate = creationDateTime.isValid() ? creationDateTime.toOffsetFromUtc( creationDateTime.offsetFromUtc() ).toString( Qt::ISODate ) : QString();
2312 const QString title = pdfWriter->title();
2313 const QString creator = getCreator();
2314 const QString producer = creator;
2315 const QString author =
layout->project() ?
layout->project()->metadata().author() : QString();
2319 const QLatin1String xmlNS(
"http://www.w3.org/XML/1998/namespace" );
2320 const QLatin1String adobeNS(
"adobe:ns:meta/" );
2321 const QLatin1String rdfNS(
"http://www.w3.org/1999/02/22-rdf-syntax-ns#" );
2322 const QLatin1String dcNS(
"http://purl.org/dc/elements/1.1/" );
2323 const QLatin1String xmpNS(
"http://ns.adobe.com/xap/1.0/" );
2324 const QLatin1String xmpMMNS(
"http://ns.adobe.com/xap/1.0/mm/" );
2325 const QLatin1String pdfNS(
"http://ns.adobe.com/pdf/1.3/" );
2326 const QLatin1String pdfaidNS(
"http://www.aiim.org/pdfa/ns/id/" );
2328 QByteArray xmpMetadata;
2329 QBuffer output( &xmpMetadata );
2330 output.open( QIODevice::WriteOnly );
2331 output.write(
"<?xpacket begin='' ?>" );
2333 QXmlStreamWriter w( &output );
2334 w.setAutoFormatting(
true );
2335 w.writeNamespace( adobeNS,
"x" );
2336 w.writeNamespace( rdfNS,
"rdf" );
2337 w.writeNamespace( dcNS,
"dc" );
2338 w.writeNamespace( xmpNS,
"xmp" );
2339 w.writeNamespace( xmpMMNS,
"xmpMM" );
2340 w.writeNamespace( pdfNS,
"pdf" );
2341 w.writeNamespace( pdfaidNS,
"pdfaid" );
2343 w.writeStartElement( adobeNS,
"xmpmeta" );
2344 w.writeStartElement( rdfNS,
"RDF" );
2347 w.writeStartElement( rdfNS,
"Description" );
2348 w.writeAttribute( rdfNS,
"about",
"" );
2349 w.writeStartElement( dcNS,
"title" );
2350 w.writeStartElement( rdfNS,
"Alt" );
2351 w.writeStartElement( rdfNS,
"li" );
2352 w.writeAttribute( xmlNS,
"lang",
"x-default" );
2353 w.writeCharacters( title );
2354 w.writeEndElement();
2355 w.writeEndElement();
2356 w.writeEndElement();
2358 w.writeStartElement( dcNS,
"creator" );
2359 w.writeStartElement( rdfNS,
"Seq" );
2360 w.writeStartElement( rdfNS,
"li" );
2361 w.writeCharacters( author );
2362 w.writeEndElement();
2363 w.writeEndElement();
2364 w.writeEndElement();
2366 w.writeEndElement();
2369 w.writeStartElement( rdfNS,
"Description" );
2370 w.writeAttribute( rdfNS,
"about",
"" );
2371 w.writeAttribute( pdfNS,
"Producer", producer );
2372 w.writeAttribute( pdfNS,
"Trapped",
"False" );
2373 w.writeEndElement();
2376 w.writeStartElement( rdfNS,
"Description" );
2377 w.writeAttribute( rdfNS,
"about",
"" );
2378 w.writeAttribute( xmpNS,
"CreatorTool", creator );
2379 w.writeAttribute( xmpNS,
"CreateDate", metaDataDate );
2380 w.writeAttribute( xmpNS,
"ModifyDate", metaDataDate );
2381 w.writeAttribute( xmpNS,
"MetadataDate", metaDataDate );
2382 w.writeEndElement();
2385 w.writeStartElement( rdfNS,
"Description" );
2386 w.writeAttribute( rdfNS,
"about",
"" );
2387 w.writeAttribute( xmpMMNS,
"DocumentID",
"uuid:" + documentId.toString( QUuid::WithoutBraces ) );
2388 w.writeAttribute( xmpMMNS,
"VersionID",
"1" );
2389 w.writeAttribute( xmpMMNS,
"RenditionClass",
"default" );
2390 w.writeEndElement();
2392#if QT_VERSION >= QT_VERSION_CHECK( 6, 8, 0 )
2395 switch ( pdfWriter->pdfVersion() )
2397 case QPagedPaintDevice::PdfVersion_1_4:
2398 case QPagedPaintDevice::PdfVersion_A1b:
2399 case QPagedPaintDevice::PdfVersion_1_6:
2401 case QPagedPaintDevice::PdfVersion_X4:
2402 const QLatin1String pdfxidNS(
"http://www.npes.org/pdfx/ns/id/" );
2403 w.writeNamespace( pdfxidNS,
"pdfxid" );
2404 w.writeStartElement( rdfNS,
"Description" );
2405 w.writeAttribute( rdfNS,
"about",
"" );
2406 w.writeAttribute( pdfxidNS,
"GTS_PDFXVersion",
"PDF/X-4" );
2407 w.writeEndElement();
2413 w.writeEndElement();
2414 w.writeEndElement();
2416 w.writeEndDocument();
2417 output.write(
"<?xpacket end='w'?>" );
2419 pdfWriter->setDocumentXmpMetadata( xmpMetadata );
RasterizedRenderingPolicy
Policies controlling when rasterisation of content during renders is permitted.
@ Default
Allow raster-based rendering in situations where it is required for correct rendering or where it wil...
@ PreferVector
Prefer vector-based rendering, when the result will still be visually near-identical to a raster-base...
@ ForceVector
Always force vector-based rendering, even when the result will be visually different to a raster-base...
static QString version()
Version string.
@ Millimeters
Millimeters.
@ GeometrySimplification
The geometries can be simplified using the current map2pixel context state.
@ SnappedToGridGlobal
Snap to a global grid based on the tolerance. Good for consistent results for incoming vertices,...
@ Warning
Warning message.
QFlags< LayoutRenderFlag > LayoutRenderFlags
Flags for controlling how a layout is rendered.
TextRenderFormat
Options for rendering text.
@ AlwaysOutlines
Always render text using path objects (AKA outlines/curves). This setting guarantees the best quality...
@ PreferredGdal
Preferred format for conversion of CRS to WKT for use with the GDAL library.
@ SynchronousLegendGraphics
Query legend graphics synchronously.
@ LosslessImageRendering
Render images losslessly whenever possible, instead of the default lossy jpeg rendering used for some...
@ Antialiasing
Use antialiasing when drawing items.
@ RenderLabelsByMapLayer
When rendering map items to multi-layered exports, render labels belonging to different layers into s...
An abstract base class for QgsLayout based classes which can be exported by QgsLayoutExporter.
virtual bool endRender()=0
Ends the render, performing any required cleanup tasks.
virtual QgsLayout * layout()=0
Returns the layout associated with the iterator.
virtual bool next()=0
Iterates to next feature, returning false if no more features exist to iterate over.
virtual bool beginRender()=0
Called when rendering begins, before iteration commences.
virtual QString filePath(const QString &baseFilePath, const QString &extension)=0
Returns the file path for the current feature, based on a specified base file path and extension.
virtual int count() const =0
Returns the number of features to iterate over.
static QgsLayoutItemRegistry * layoutItemRegistry()
Returns the application's layout item registry, used for layout item types.
QString toWkt(Qgis::CrsWktVariant variant=Qgis::CrsWktVariant::Wkt1Gdal, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
Base class for feedback objects to be used for cancellation of something running in a worker thread.
bool isCanceled() const
Tells whether the operation has been canceled already.
void setProgress(double progress)
Sets the current progress for the feedback object.
ExportResult exportToSvg(const QString &filePath, const QgsLayoutExporter::SvgExportSettings &settings)
Exports the layout as an SVG to the filePath, using the specified export settings.
ExportResult exportToImage(const QString &filePath, const QgsLayoutExporter::ImageExportSettings &settings)
Exports the layout to the filePath, using the specified export settings.
QString errorMessage() const
Returns a string describing the last error encountered during an export.
ExportResult exportToPdf(const QString &filePath, const QgsLayoutExporter::PdfExportSettings &settings)
Exports the layout as a PDF to the filePath, using the specified export settings.
virtual ~QgsLayoutExporter()
QImage renderRegionToImage(const QRectF ®ion, QSize imageSize=QSize(), double dpi=-1) const
Renders a region of the layout to an image.
QMap< QString, QgsLabelingResults * > takeLabelingResults()
Takes the labeling results for all map items included in the export.
static bool requiresRasterization(const QgsLayout *layout)
Returns true if the specified layout contains visible items which have settings that require rasteriz...
QgsLayout * layout() const
Returns the layout linked to this exporter.
bool georeferenceOutput(const QString &file, QgsLayoutItemMap *referenceMap=nullptr, const QRectF &exportRegion=QRectF(), double dpi=-1) const
Georeferences a file (image of PDF) exported from the layout.
static const QgsSettingsEntryBool * settingOpenAfterExportingPdf
Settings entry - Whether to automatically open pdfs after exporting them.
virtual QString generateFileName(const PageExportDetails &details) const
Generates the file name for a page during export.
ExportResult
Result codes for exporting layouts.
@ Canceled
Export was canceled.
@ MemoryError
Unable to allocate memory required to export.
@ PrintError
Could not start printing to destination device.
@ IteratorError
Error iterating over layout.
@ FileError
Could not write to destination file, likely due to a lock held by another application.
@ Success
Export was successful.
@ SvgLayerError
Could not create layered SVG file.
static const QgsSettingsEntryInteger * settingImageQuality
Settings entry - Image quality for lossy formats.
QImage renderPageToImage(int page, QSize imageSize=QSize(), double dpi=-1) const
Renders a full page to an image.
QgsLayoutExporter(QgsLayout *layout)
Constructor for QgsLayoutExporter, for the specified layout.
static ExportResult exportToPdfs(QgsAbstractLayoutIterator *iterator, const QString &baseFilePath, const QgsLayoutExporter::PdfExportSettings &settings, QString &error, QgsFeedback *feedback=nullptr)
Exports a layout iterator to multiple PDF files, with the specified export settings.
void computeWorldFileParameters(double &a, double &b, double &c, double &d, double &e, double &f, double dpi=-1) const
Compute world file parameters.
void renderPage(QPainter *painter, int page) const
Renders a full page to a destination painter.
static const QgsSettingsEntryBool * settingOpenAfterExportingImage
Settings entry - Whether to automatically open images after exporting them.
static const QgsSettingsEntryBool * settingOpenAfterExportingSvg
Settings entry - Whether to automatically open svgs after exporting them.
QMap< QString, QgsLabelingResults * > labelingResults()
Returns the labeling results for all map items included in the export.
static bool containsAdvancedEffects(const QgsLayout *layout)
Returns true if the specified layout contains visible items which have settings such as opacity which...
void renderRegion(QPainter *painter, const QRectF ®ion) const
Renders a region from the layout to a painter.
Layout graphical items for displaying a map.
double mapRotation(QgsLayoutObject::PropertyValueType valueType=QgsLayoutObject::EvaluatedValue) const
Returns the rotation used for drawing the map within the layout item, in degrees clockwise.
QgsRectangle extent() const
Returns the current map extent.
QgsCoordinateReferenceSystem crs() const
Returns coordinate reference system used for rendering the map.
Item representing the paper in a layout.
QgsLayoutItemAbstractMetadata * itemMetadata(int type) const
Returns the metadata for the specified item type.
Base class for graphical items within a QgsLayout.
virtual QgsLayoutItem::ExportLayerDetail exportLayerDetails() const
Returns the details for the specified current export layer.
virtual bool nextExportPart()
Moves to the next export part for a multi-layered export item, during a multi-layered export.
virtual void startLayeredExport()
Starts a multi-layer export operation.
int page() const
Returns the page the item is currently on, with the first page returning 0.
int type() const override
Returns a unique graphics item type identifier.
virtual void stopLayeredExport()
Stops a multi-layer export operation.
virtual QString uuid() const
Returns the item identification string.
ExportLayerBehavior
Behavior of item when exporting to layered outputs.
@ ItemContainsSubLayers
Item contains multiple sublayers which must be individually exported.
@ MustPlaceInOwnLayer
Item must be placed in its own individual layer.
@ CanGroupWithItemsOfSameType
Item can only be placed on layers with other items of the same type, but multiple items of this type ...
@ CanGroupWithAnyOtherItem
Item can be placed on a layer with any other item (default behavior).
virtual ExportLayerBehavior exportLayerBehavior() const
Returns the behavior of this item during exporting to layered exports (e.g.
Provides a method of storing measurements for use in QGIS layouts using a variety of different measur...
Provides a method of storing points, consisting of an x and y coordinate, for use in QGIS layouts.
double x() const
Returns x coordinate of point.
double y() const
Returns y coordinate of point.
void setDpi(double dpi)
Sets the dpi for outputting the layout.
void setSimplifyMethod(const QgsVectorSimplifyMethod &method)
Sets the simplification setting to use when rendering vector layers.
void setTextRenderFormat(Qgis::TextRenderFormat format)
Sets the text render format, which dictates how text is rendered (e.g.
Qgis::LayoutRenderFlags flags() const
Returns the current combination of flags used for rendering the layout.
void setRasterizedRenderingPolicy(Qgis::RasterizedRenderingPolicy policy)
Sets the policy controlling when rasterization of content during renders is permitted.
double dpi() const
Returns the dpi for outputting the layout.
void setPredefinedScales(const QVector< qreal > &scales)
Sets the list of predefined scales to use with the layout.
void setMaskSettings(const QgsMaskRenderSettings &settings)
Sets the mask render settings, which control how masks are drawn and behave during map renders.
void setFlags(Qgis::LayoutRenderFlags flags)
Sets the combination of flags that will be used for rendering the layout.
Provides a method of storing sizes, consisting of a width and height, for use in QGIS layouts.
QSizeF toQSizeF() const
Converts the layout size to a QSizeF.
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
QgsLayoutRenderContext & renderContext()
Returns a reference to the layout's render context, which stores information relating to the current ...
Line string geometry type, with support for z-dimension and m-values.
Base class for all map layer types.
double top() const
Returns the top margin.
double right() const
Returns the right margin.
double bottom() const
Returns the bottom margin.
double left() const
Returns the left margin.
Contains settings regarding how masks are calculated and handled during a map render.
void setSimplificationTolerance(double tolerance)
Sets a simplification tolerance (in painter units) to use for on-the-fly simplification of mask paths...
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE(), Qgis::StringFormat format=Qgis::StringFormat::PlainText)
Adds a message to the log instance (and creates it if necessary).
void setExteriorRing(QgsCurve *ring) override
Sets the exterior ring of the polygon.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
QgsProjectMetadata metadata
A rectangle specified with double values.
A boolean settings entry.
An integer settings entry.
static QgsSettingsTreeNode * sTreeLayout
Contains settings for simplifying geometries fetched from a vector layer.
void setThreshold(float threshold)
Sets the simplification threshold of the vector layer managed.
void setForceLocalOptimization(bool localOptimization)
Sets where the simplification executes, after fetch the geometries from provider, or when supported,...
void setSimplifyHints(Qgis::VectorRenderingSimplificationFlags simplifyHints)
Sets the simplification hints of the vector layer managed.
void setSimplifyAlgorithm(Qgis::VectorSimplificationAlgorithm simplifyAlgorithm)
Sets the local simplification algorithm of the vector layer managed.
std::unique_ptr< std::remove_pointer< GDALDatasetH >::type, GDALDatasetCloser > dataset_unique_ptr
Scoped GDAL dataset.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
#define Q_NOWARN_DEPRECATED_POP
#define Q_NOWARN_DEPRECATED_PUSH
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
QString nameForLayerWithItems(const QList< QGraphicsItem * > &items, unsigned int layerId)
#define QgsDebugError(str)
Contains details of a particular input component to be used during PDF composition.
QString mapLayerId
Associated map layer ID, or an empty string if this component layer is not associated with a map laye...
QString group
Optional group name, for arranging layers in top-level groups.
double opacity
Component opacity.
QString name
User-friendly name for the generated PDF layer.
QPainter::CompositionMode compositionMode
Component composition mode.
QString sourcePdfPath
File path to the (already created) PDF to use as the source for this component layer.
Contains details of a control point used during georeferencing Geospatial PDF outputs.
QMap< QString, QString > customLayerTreeGroups
Optional map of map layer ID to custom logical layer tree group in created PDF file.
QMap< QString, bool > initialLayerVisibility
Optional map of map layer ID to initial visibility state.
QList< QgsAbstractGeospatialPdfExporter::GeoReferencedSection > georeferencedSections
List of georeferenced sections.
QStringList layerOrder
Optional list of layer IDs, in the order desired to appear in the generated Geospatial PDF file.
QMap< QString, QString > layerIdToPdfLayerTreeNameMap
Optional map of map layer ID to custom layer tree name to show in the created PDF file.
QgsAbstractMetadataBase::KeywordMap keywords
Metadata keyword map.
QString creator
Metadata creator tag.
QSizeF pageSizeMm
Page size, in millimeters.
QString author
Metadata author tag.
QString subject
Metadata subject tag.
QString title
Metadata title tag.
QStringList layerTreeGroupOrder
Specifies the ordering of layer tree groups in the generated Geospatial PDF file.
bool useLayerTreeConfig
If set to true, the layer tree from the QGIS project should be used when creating a Geospatial PDF.
QDateTime creationDateTime
Metadata creation datetime.
bool includeFeatures
true if feature vector information (such as attributes) should be exported.
QString producer
Metadata producer tag.
bool useIso32000ExtensionFormatGeoreferencing
true if ISO32000 extension format georeferencing should be used.
QSet< QString > mutuallyExclusiveGroups
Contains a list of group names which should be considered as mutually exclusive.
QgsCoordinateReferenceSystem crs
Coordinate reference system for georeferenced section.
QList< QgsAbstractGeospatialPdfExporter::ControlPoint > controlPoints
List of control points corresponding to this georeferenced section.
QgsPolygon pageBoundsPolygon
Bounds of the georeferenced section on the page, in millimeters, as a free-form polygon.
Contains settings relating to exporting layouts to raster images.
QgsMargins cropMargins
Crop to content margins, in pixels.
QList< int > pages
List of specific pages to export, or an empty list to export all pages.
bool generateWorldFile
Set to true to generate an external world file alongside exported images.
QSize imageSize
Manual size in pixels for output image.
bool exportMetadata
Indicates whether image export should include metadata generated from the layout's project's metadata...
int quality
Image quality, typically used for JPEG compression (whose quality ranges from 1 to 100) if quality is...
Qgis::LayoutRenderFlags flags
Layout context flags, which control how the export will be created.
bool cropToContents
Set to true if image should be cropped so only parts of the layout containing items are exported.
double dpi
Resolution to export layout at. If dpi <= 0 the default layout dpi will be used.
QVector< qreal > predefinedMapScales
A list of predefined scales to use with the layout.
Contains details of a page being exported by the class.
QString baseName
Base part of filename (i.e. file name without extension or '.').
QString extension
File suffix/extension (without the leading '.').
QString directory
Target folder.
int page
Page number, where 0 = first page.
Contains settings relating to exporting layouts to PDF.
bool useLayerTreeConfig
If set to true, the layer tree from the QGIS project should be used when creating a Geospatial PDF.
bool forceVectorOutput
Set to true to force vector object exports, even when the resultant appearance will differ from the l...
bool rasterizeWholeImage
Set to true to force whole layout to be rasterized while exporting.
QStringList exportThemes
Optional list of map themes to export as Geospatial PDF layer groups.
bool exportMetadata
Indicates whether PDF export should include metadata generated from the layout's project's metadata.
bool appendGeoreference
Indicates whether PDF export should append georeference data.
Qgis::LayoutRenderFlags flags
Layout context flags, which control how the export will be created.
bool writeGeoPdf
true if geospatial PDF files should be created, instead of normal PDF files.
double dpi
Resolution to export layout at. If dpi <= 0 the default layout dpi will be used.
QVector< qreal > predefinedMapScales
A list of predefined scales to use with the layout.
bool exportLayersAsSeperateFiles
true if individual layers from the layout should be rendered to separate PDF files.
bool simplifyGeometries
Indicates whether vector geometries should be simplified to avoid redundant extraneous detail,...
Qgis::TextRenderFormat textRenderFormat
Text rendering format, which controls how text should be rendered in the export (e....
Contains settings relating to printing layouts.
QVector< qreal > predefinedMapScales
A list of predefined scales to use with the layout.
Qgis::LayoutRenderFlags flags
Layout context flags, which control how the export will be created.
double dpi
Resolution to export layout at. If dpi <= 0 the default layout dpi will be used.
bool rasterizeWholeImage
Set to true to force whole layout to be rasterized while exporting.
Contains settings relating to exporting layouts to SVG.
bool forceVectorOutput
Set to true to force vector object exports, even when the resultant appearance will differ from the l...
Qgis::LayoutRenderFlags flags
Layout context flags, which control how the export will be created.
Qgis::TextRenderFormat textRenderFormat
Text rendering format, which controls how text should be rendered in the export (e....
bool exportAsLayers
Set to true to export as a layered SVG file.
bool simplifyGeometries
Indicates whether vector geometries should be simplified to avoid redundant extraneous detail,...
bool exportMetadata
Indicates whether SVG export should include RDF metadata generated from the layout's project's metada...
double dpi
Resolution to export layout at. If dpi <= 0 the default layout dpi will be used.
QVector< qreal > predefinedMapScales
A list of predefined scales to use with the layout.
bool exportLabelsToSeparateLayers
Set to true to export labels to separate layers (grouped by map layer) in layered SVG exports.
bool cropToContents
Set to true if image should be cropped so only parts of the layout containing items are exported.
QgsMargins cropMargins
Crop to content margins, in layout units.
Contains details of a particular export layer relating to a layout item.
QString name
User-friendly name for the export layer.
QString groupName
Associated group name, if this layer is associated with an export group.