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 );
591 mLayout->renderContext().setExportThemes( settings.
exportThemes );
603 const QList<QGraphicsItem *> items = mLayout->items( Qt::AscendingOrder );
605 QList< QgsLayoutGeospatialPdfExporter::ComponentLayerDetail > pdfComponents;
610 QSet<QString> mutuallyExclusiveGroups;
613 [
this, &subSettings, &pdfComponents, &geospatialPdfExporter, &settings, &baseDir, &baseFileName, &mutuallyExclusiveGroups](
unsigned int layerId,
const QgsLayoutItem::ExportLayerDetail &layerDetail )
617 component.
name = layerDetail.name;
618 component.
mapLayerId = layerDetail.mapLayerId;
619 component.
opacity = layerDetail.opacity;
621 component.
group = layerDetail.groupName;
622 if ( !layerDetail.mapTheme.isEmpty() )
624 component.
group = layerDetail.mapTheme;
625 mutuallyExclusiveGroups.insert( layerDetail.mapTheme );
628 component.
sourcePdfPath = settings.writeGeoPdf ? geospatialPdfExporter->generateTemporaryFilepath( u
"layer_%1.pdf"_s.arg( layerId ) )
629 : baseDir.filePath( u
"%1_%2.pdf"_s.arg( baseFileName ).arg( layerId, 4, 10, QChar(
'0' ) ) );
630 pdfComponents << component;
632 preparePrintAsPdf( mLayout, &printer, component.
sourcePdfPath );
633 preparePrint( mLayout, &printer,
false );
635 if ( !p.begin( &printer ) )
643 return layerExportResult;
645 auto getExportGroupNameFunc = [](
QgsLayoutItem *item ) -> QString {
return item->customProperty( u
"pdfExportGroup"_s ).toString(); };
646 result = handleLayeredExport( items, exportFunc, getExportGroupNameFunc );
650 if ( settings.writeGeoPdf )
653 details.
dpi = settings.dpi;
655 QgsLayoutSize pageSize = mLayout->pageCollection()->page( 0 )->sizeWithUnits();
660 if ( settings.exportMetadata )
663 details.
author = mLayout->project()->metadata().author();
665 details.
creator = getCreator();
666 details.
creationDateTime = mLayout->project()->metadata().creationDateTime();
667 details.
subject = mLayout->project()->metadata().abstract();
668 details.
title = mLayout->project()->metadata().title();
669 details.
keywords = mLayout->project()->metadata().keywords();
672 const QList< QgsMapLayer * > layers = mLayout->project()->mapLayers().values();
678 if ( settings.appendGeoreference )
681 QList< QgsLayoutItemMap * > maps;
682 mLayout->layoutItems( maps );
686 georef.
crs = map->crs();
688 const QPointF topLeft = map->mapToScene( QPointF( 0, 0 ) );
689 const QPointF topRight = map->mapToScene( QPointF( map->rect().width(), 0 ) );
690 const QPointF bottomLeft = map->mapToScene( QPointF( 0, map->rect().height() ) );
691 const QPointF bottomRight = map->mapToScene( QPointF( map->rect().width(), map->rect().height() ) );
698 QVector< QgsPointXY >()
707 const QTransform t = map->layoutToMapCoordsTransform();
708 const QgsPointXY topLeftMap = t.map( topLeft );
709 const QgsPointXY topRightMap = t.map( topRight );
710 const QgsPointXY bottomLeftMap = t.map( bottomLeft );
711 const QgsPointXY bottomRightMap = t.map( bottomRight );
723 details.
layerOrder = geospatialPdfExporter->layerOrder();
728 if ( !geospatialPdfExporter->finalize( pdfComponents, filePath, details ) )
731 mErrorMessage = geospatialPdfExporter->errorMessage();
741 QPdfWriter printer = QPdfWriter( filePath );
742 preparePrintAsPdf( mLayout, &printer, filePath );
743 preparePrint( mLayout, &printer,
false );
745 if ( !p.begin( &printer ) )
754 bool shouldAppendGeoreference = settings.
appendGeoreference && mLayout && mLayout->referenceMap() && mLayout->referenceMap()->page() == 0;
757 georeferenceOutputPrivate( filePath,
nullptr, QRectF(), settings.
dpi, shouldAppendGeoreference, settings.
exportMetadata );
760 captureLabelingResults();
775 QPdfWriter printer = QPdfWriter( fileName );
778 int total = iterator->
count();
779 double step = total > 0 ? 100.0 / total : 100.0;
782 while ( iterator->
next() )
787 feedback->setProperty(
"progress", QObject::tr(
"Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
789 feedback->setProperty(
"progress", QObject::tr(
"Exporting section %1" ).arg( i + 1 ) );
801 LayoutContextPreviewSettingRestorer restorer( iterator->
layout() );
803 LayoutContextSettingsRestorer contextRestorer( iterator->
layout() );
804 ( void ) contextRestorer;
831 preparePrintAsPdf( iterator->
layout(), &printer, fileName );
832 preparePrint( iterator->
layout(), &printer,
false );
834 if ( !p.begin( &printer ) )
848 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 ) );
877 int total = iterator->
count();
878 double step = total > 0 ? 100.0 / total : 100.0;
880 while ( iterator->
next() )
885 feedback->setProperty(
"progress", QObject::tr(
"Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
887 feedback->setProperty(
"progress", QObject::tr(
"Exporting section %1" ).arg( i + 1 ) );
896 QString filePath = iterator->
filePath( baseFilePath, u
"pdf"_s );
903 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 ) );
921#if defined( HAVE_QTPRINTER )
928 if ( settings.
dpi <= 0 )
929 settings.
dpi = mLayout->renderContext().dpi();
931 mErrorFileName.clear();
933 LayoutContextPreviewSettingRestorer restorer( mLayout );
935 LayoutContextSettingsRestorer contextRestorer( mLayout );
936 ( void ) contextRestorer;
937 mLayout->renderContext().setDpi( settings.
dpi );
939 mLayout->renderContext().setFlags( settings.
flags );
946 preparePrint( mLayout, &printer,
true );
948 if ( !p.begin( &printer ) )
957 captureLabelingResults();
972 int total = iterator->
count();
973 double step = total > 0 ? 100.0 / total : 100.0;
976 while ( iterator->
next() )
981 feedback->setProperty(
"progress", QObject::tr(
"Printing %1 of %2" ).arg( i + 1 ).arg( total ) );
983 feedback->setProperty(
"progress", QObject::tr(
"Printing section %1" ).arg( i + 1 ) );
995 LayoutContextPreviewSettingRestorer restorer( iterator->
layout() );
997 LayoutContextSettingsRestorer contextRestorer( iterator->
layout() );
998 ( void ) contextRestorer;
1007 if ( !settings.rasterizeWholeImage )
1012 preparePrint( iterator->
layout(), &printer,
true );
1014 if ( !p.begin( &printer ) )
1024 ExportResult result = exporter.printPrivate( &printer, p, !first, settings.dpi, settings.rasterizeWholeImage );
1028 error = exporter.errorMessage();
1051 if ( settings.
dpi <= 0 )
1052 settings.
dpi = mLayout->renderContext().dpi();
1054 mErrorFileName.clear();
1056 LayoutContextPreviewSettingRestorer restorer( mLayout );
1058 LayoutContextSettingsRestorer contextRestorer( mLayout );
1059 ( void ) contextRestorer;
1060 mLayout->renderContext().setDpi( settings.
dpi );
1062 mLayout->renderContext().setFlags( settings.
flags );
1074 mLayout->renderContext().setMaskSettings( createExportMaskSettings() );
1078 mLayout->renderContext().setSimplifyMethod( createExportSimplifyMethod() );
1081 QFileInfo fi( filePath );
1084 pageDetails.
baseName = fi.baseName();
1085 pageDetails.
extension = fi.completeSuffix();
1089 for (
int i = 0; i < mLayout->pageCollection()->pageCount(); ++i )
1091 if ( !mLayout->pageCollection()->shouldExportPage( i ) )
1096 pageDetails.
page = i;
1103 if ( mLayout->pageCollection()->pageCount() == 1 )
1106 bounds = mLayout->layoutBounds(
true );
1111 bounds = mLayout->pageItemBounds( i,
true );
1117 bounds = QRectF( pageItem->pos().x(), pageItem->pos().y(), pageItem->rect().width(), pageItem->rect().height() );
1121 int width =
static_cast< int >( bounds.width() * settings.
dpi / inchesToLayoutUnits );
1123 int height =
static_cast< int >( bounds.height() * settings.
dpi / inchesToLayoutUnits );
1124 if ( width == 0 || height == 0 )
1133 const QRectF paperRect = QRectF( pageItem->pos().x(), pageItem->pos().y(), pageItem->rect().width(), pageItem->rect().height() );
1135 QDomNode svgDocRoot;
1136 const QList<QGraphicsItem *> items = mLayout->items( paperRect, Qt::IntersectsItemBoundingRect, Qt::AscendingOrder );
1140 return renderToLayeredSvg( settings, width, height, i, bounds, fileName, layerId, layerDetail.name, svg, svgDocRoot, settings.
exportMetadata );
1142 auto getExportGroupNameFunc = [](
QgsLayoutItem * ) -> QString {
return QString(); };
1143 ExportResult res = handleLayeredExport( items, exportFunc, getExportGroupNameFunc );
1148 appendMetadataToSvg( svg );
1150 QFile out( fileName );
1151 bool openOk = out.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate );
1154 mErrorFileName = fileName;
1158 out.write( svg.toByteArray() );
1164 QSvgGenerator generator;
1167 generator.setTitle( mLayout->project()->metadata().title() );
1168 generator.setDescription( mLayout->project()->metadata().abstract() );
1170 generator.setOutputDevice( &svgBuffer );
1171 generator.setSize( QSize( width, height ) );
1172 generator.setViewBox( QRect( 0, 0, width, height ) );
1173 generator.setResolution(
static_cast< int >( std::round( settings.
dpi ) ) );
1176 bool createOk = p.begin( &generator );
1179 mErrorFileName = fileName;
1192 svgBuffer.open( QIODevice::ReadOnly );
1196 if ( !svg.setContent( &svgBuffer,
false, &errorMsg, &errorLine ) )
1198 mErrorFileName = fileName;
1203 appendMetadataToSvg( svg );
1205 QFile out( fileName );
1206 bool openOk = out.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate );
1209 mErrorFileName = fileName;
1213 out.write( svg.toByteArray() );
1217 captureLabelingResults();
1230 int total = iterator->
count();
1231 double step = total > 0 ? 100.0 / total : 100.0;
1233 while ( iterator->
next() )
1238 feedback->setProperty(
"progress", QObject::tr(
"Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
1240 feedback->setProperty(
"progress", QObject::tr(
"Exporting section %1" ).arg( i + 1 ) );
1250 QString filePath = iterator->
filePath( baseFilePath, u
"svg"_s );
1257 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 ) );
1277 return mLabelingResults;
1282 QMap<QString, QgsLabelingResults *> res;
1283 std::swap( mLabelingResults, res );
1287void QgsLayoutExporter::preparePrintAsPdf(
QgsLayout *layout, QPdfWriter *device,
const QString &filePath )
1289 QFileInfo fi( filePath );
1291 if ( !dir.exists( fi.absolutePath() ) )
1293 dir.mkpath( fi.absolutePath() );
1296 updatePrinterPageSize(
layout, device, firstPageToBeExported(
layout ) );
1299 const QString title = !
layout->project() ||
layout->project()->metadata().title().isEmpty() ? fi.baseName() :
layout->project()->metadata().title();
1301 device->setTitle( title );
1303 QPagedPaintDevice::PdfVersion pdfVersion = QPagedPaintDevice::PdfVersion_1_4;
1305#if QT_VERSION >= QT_VERSION_CHECK( 6, 8, 0 )
1307 if (
const QgsProjectStyleSettings *styleSettings = (
layout->project() ?
layout->project()->styleSettings() :
nullptr ) )
1311 switch ( styleSettings->colorModel() )
1314 device->setColorModel( QPdfWriter::ColorModel::CMYK );
1318 device->setColorModel( QPdfWriter::ColorModel::RGB );
1322 const QColorSpace colorSpace = styleSettings->colorSpace();
1323 if ( colorSpace.isValid() )
1325 QPdfOutputIntent outputIntent;
1326 outputIntent.setOutputProfile( colorSpace );
1327 outputIntent.setOutputCondition( colorSpace.description() );
1331 outputIntent.setOutputConditionIdentifier( u
"Unknown identifier"_s );
1332 outputIntent.setRegistryName( u
"Unknown registry"_s );
1333 device->setOutputIntent( outputIntent );
1336 pdfVersion = QPagedPaintDevice::PdfVersion_X4;
1342 device->setPdfVersion( pdfVersion );
1343 setXmpMetadata( device,
layout );
1350void QgsLayoutExporter::preparePrint(
QgsLayout *layout, QPagedPaintDevice *device,
bool setFirstPageSize )
1352 if ( QPdfWriter *pdf =
dynamic_cast<QPdfWriter *
>( device ) )
1354 pdf->setResolution(
static_cast< int>( std::round(
layout->renderContext().dpi() ) ) );
1356#if defined( HAVE_QTPRINTER )
1357 else if ( QPrinter *printer =
dynamic_cast<QPrinter *
>( device ) )
1359 printer->setFullPage(
true );
1360 printer->setColorMode( QPrinter::Color );
1362 printer->setResolution(
static_cast< int>( std::round(
layout->renderContext().dpi() ) ) );
1366 if ( setFirstPageSize )
1368 updatePrinterPageSize(
layout, device, firstPageToBeExported(
layout ) );
1374 if ( mLayout->pageCollection()->pageCount() == 0 )
1377 preparePrint( mLayout, device,
true );
1379 if ( !p.begin( device ) )
1385 printPrivate( device, p );
1390QgsLayoutExporter::ExportResult QgsLayoutExporter::printPrivate( QPagedPaintDevice *device, QPainter &painter,
bool startNewPage,
double dpi,
bool rasterize )
1394 int toPage = mLayout->pageCollection()->pageCount() - 1;
1396#if defined( HAVE_QTPRINTER )
1397 if ( QPrinter *printer =
dynamic_cast<QPrinter *
>( device ) )
1399 if ( printer->fromPage() >= 1 )
1400 fromPage = printer->fromPage() - 1;
1401 if ( printer->toPage() >= 1 )
1402 toPage = printer->toPage() - 1;
1406 bool pageExported =
false;
1409 for (
int i = fromPage; i <= toPage; ++i )
1411 if ( !mLayout->pageCollection()->shouldExportPage( i ) )
1416 updatePrinterPageSize( mLayout, device, i );
1417 if ( ( pageExported && i > fromPage ) || startNewPage )
1423 if ( !image.isNull() )
1425 QRectF targetArea( 0, 0, image.width(), image.height() );
1426 painter.drawImage( targetArea, image, targetArea );
1432 pageExported =
true;
1437 for (
int i = fromPage; i <= toPage; ++i )
1439 if ( !mLayout->pageCollection()->shouldExportPage( i ) )
1444 updatePrinterPageSize( mLayout, device, i );
1446 if ( ( pageExported && i > fromPage ) || startNewPage )
1451 pageExported =
true;
1457void QgsLayoutExporter::updatePrinterPageSize(
QgsLayout *layout, QPagedPaintDevice *device,
int page )
1459 QgsLayoutSize pageSize =
layout->pageCollection()->page( page )->sizeWithUnits();
1462 QPageLayout pageLayout( QPageSize( pageSizeMM.
toQSizeF(), QPageSize::Millimeter ), QPageLayout::Portrait, QMarginsF( 0, 0, 0, 0 ) );
1463 pageLayout.setMode( QPageLayout::FullPageMode );
1464 device->setPageLayout( pageLayout );
1465 device->setPageMargins( QMarginsF( 0, 0, 0, 0 ) );
1467#if defined( HAVE_QTPRINTER )
1468 if ( QPrinter *printer =
dynamic_cast<QPrinter *
>( device ) )
1470 printer->setFullPage(
true );
1476 const SvgExportSettings &settings,
1480 const QRectF &bounds,
1481 const QString &filename,
1482 unsigned int svgLayerId,
1483 const QString &layerName,
1485 QDomNode &svgDocRoot,
1486 bool includeMetadata
1491 QSvgGenerator generator;
1492 if ( includeMetadata )
1494 if (
const QgsMasterLayoutInterface *l =
dynamic_cast< const QgsMasterLayoutInterface *
>( mLayout.data() ) )
1495 generator.setTitle( l->name() );
1496 else if ( mLayout->project() )
1497 generator.setTitle( mLayout->project()->title() );
1500 generator.setOutputDevice( &svgBuffer );
1501 generator.setSize( QSize(
static_cast< int >( std::round( width ) ),
static_cast< int >( std::round( height ) ) ) );
1502 generator.setViewBox( QRect( 0, 0,
static_cast< int >( std::round( width ) ),
static_cast< int >( std::round( height ) ) ) );
1503 generator.setResolution(
static_cast< int >( std::round( settings.dpi ) ) );
1505 QPainter svgPainter( &generator );
1506 if ( settings.cropToContents )
1517 svgBuffer.open( QIODevice::ReadOnly );
1521 if ( !doc.setContent( &svgBuffer,
false, &errorMsg, &errorLine ) )
1523 mErrorFileName = filename;
1526 if ( 1 == svgLayerId )
1528 svg = QDomDocument( doc.doctype() );
1529 svg.appendChild( svg.importNode( doc.firstChild(),
false ) );
1530 svgDocRoot = svg.importNode( doc.elementsByTagName( u
"svg"_s ).at( 0 ),
false );
1531 svgDocRoot.toElement().setAttribute( u
"xmlns:inkscape"_s, u
"http://www.inkscape.org/namespaces/inkscape"_s );
1532 svg.appendChild( svgDocRoot );
1534 QDomNode mainGroup = svg.importNode( doc.elementsByTagName( u
"g"_s ).at( 0 ),
true );
1535 mainGroup.toElement().setAttribute( u
"id"_s, layerName );
1536 mainGroup.toElement().setAttribute( u
"inkscape:label"_s, layerName );
1537 mainGroup.toElement().setAttribute( u
"inkscape:groupmode"_s, u
"layer"_s );
1538 QDomNode defs = svg.importNode( doc.elementsByTagName( u
"defs"_s ).at( 0 ),
true );
1539 svgDocRoot.appendChild( defs );
1540 svgDocRoot.appendChild( mainGroup );
1545void QgsLayoutExporter::appendMetadataToSvg( QDomDocument &svg )
const
1547 const QgsProjectMetadata &metadata = mLayout->project()->metadata();
1548 QDomElement metadataElement = svg.createElement( u
"metadata"_s );
1549 QDomElement rdfElement = svg.createElement( u
"rdf:RDF"_s );
1550 rdfElement.setAttribute( u
"xmlns:rdf"_s, u
"http://www.w3.org/1999/02/22-rdf-syntax-ns#"_s );
1551 rdfElement.setAttribute( u
"xmlns:rdfs"_s, u
"http://www.w3.org/2000/01/rdf-schema#"_s );
1552 rdfElement.setAttribute( u
"xmlns:dc"_s, u
"http://purl.org/dc/elements/1.1/"_s );
1553 QDomElement descriptionElement = svg.createElement( u
"rdf:Description"_s );
1554 QDomElement workElement = svg.createElement( u
"cc:Work"_s );
1555 workElement.setAttribute( u
"rdf:about"_s, QString() );
1557 auto addTextNode = [&workElement, &descriptionElement, &svg](
const QString &tag,
const QString &value ) {
1559 QDomElement element = svg.createElement( tag );
1560 QDomText t = svg.createTextNode( value );
1561 element.appendChild( t );
1562 workElement.appendChild( element );
1565 descriptionElement.setAttribute( tag, value );
1568 addTextNode( u
"dc:format"_s, u
"image/svg+xml"_s );
1569 addTextNode( u
"dc:title"_s, metadata.
title() );
1570 addTextNode( u
"dc:date"_s, metadata.
creationDateTime().toString( Qt::ISODate ) );
1571 addTextNode( u
"dc:identifier"_s, metadata.
identifier() );
1572 addTextNode( u
"dc:description"_s, metadata.
abstract() );
1574 auto addAgentNode = [&workElement, &descriptionElement, &svg](
const QString &tag,
const QString &value ) {
1576 QDomElement inkscapeElement = svg.createElement( tag );
1577 QDomElement agentElement = svg.createElement( u
"cc:Agent"_s );
1578 QDomElement titleElement = svg.createElement( u
"dc:title"_s );
1579 QDomText t = svg.createTextNode( value );
1580 titleElement.appendChild( t );
1581 agentElement.appendChild( titleElement );
1582 inkscapeElement.appendChild( agentElement );
1583 workElement.appendChild( inkscapeElement );
1586 QDomElement bagElement = svg.createElement( u
"rdf:Bag"_s );
1587 QDomElement liElement = svg.createElement( u
"rdf:li"_s );
1588 t = svg.createTextNode( value );
1589 liElement.appendChild( t );
1590 bagElement.appendChild( liElement );
1592 QDomElement element = svg.createElement( tag );
1593 element.appendChild( bagElement );
1594 descriptionElement.appendChild( element );
1597 addAgentNode( u
"dc:creator"_s, metadata.
author() );
1598 addAgentNode( u
"dc:publisher"_s, getCreator() );
1602 QDomElement element = svg.createElement( u
"dc:subject"_s );
1603 QDomElement bagElement = svg.createElement( u
"rdf:Bag"_s );
1605 for (
auto it = keywords.constBegin(); it != keywords.constEnd(); ++it )
1607 const QStringList words = it.value();
1608 for (
const QString &keyword : words )
1610 QDomElement liElement = svg.createElement( u
"rdf:li"_s );
1611 QDomText t = svg.createTextNode( keyword );
1612 liElement.appendChild( t );
1613 bagElement.appendChild( liElement );
1616 element.appendChild( bagElement );
1617 workElement.appendChild( element );
1618 descriptionElement.appendChild( element );
1621 rdfElement.appendChild( descriptionElement );
1622 rdfElement.appendChild( workElement );
1623 metadataElement.appendChild( rdfElement );
1624 svg.documentElement().appendChild( metadataElement );
1625 svg.documentElement().setAttribute( u
"xmlns:cc"_s, u
"http://creativecommons.org/ns#"_s );
1628std::unique_ptr<double[]> QgsLayoutExporter::computeGeoTransform(
const QgsLayoutItemMap *map,
const QRectF ®ion,
double dpi )
const
1631 map = mLayout->referenceMap();
1637 dpi = mLayout->renderContext().dpi();
1640 QRectF exportRegion = region;
1641 if ( !exportRegion.isValid() )
1643 int pageNumber = map->
page();
1645 QgsLayoutItemPage *page = mLayout->pageCollection()->page( pageNumber );
1646 double pageY = page->pos().y();
1647 QSizeF pageSize = page->rect().size();
1648 exportRegion = QRectF( 0, pageY, pageSize.width(), pageSize.height() );
1652 QRectF mapItemSceneRect = map->mapRectToScene( map->rect() );
1655 double outputHeightMM = exportRegion.height();
1656 double outputWidthMM = exportRegion.width();
1659 QgsRectangle mapExtent = map->
extent();
1660 double mapXCenter = mapExtent.
center().
x();
1661 double mapYCenter = mapExtent.
center().
y();
1663 double sinAlpha = std::sin( alpha );
1664 double cosAlpha = std::cos( alpha );
1667 QPointF mapItemPos = map->pos();
1669 mapItemPos.rx() -= exportRegion.left();
1670 mapItemPos.ry() -= exportRegion.top();
1673 double xRatio = mapExtent.
width() / mapItemSceneRect.width();
1674 double yRatio = mapExtent.
height() / mapItemSceneRect.height();
1675 double xmin = mapExtent.
xMinimum() - mapItemPos.x() * xRatio;
1676 double ymax = mapExtent.
yMaximum() + mapItemPos.y() * yRatio;
1677 QgsRectangle paperExtent( xmin, ymax - outputHeightMM * yRatio, xmin + outputWidthMM * xRatio, ymax );
1680 double X0 = paperExtent.xMinimum();
1681 double Y0 = paperExtent.yMaximum();
1686 double X1 = X0 - mapXCenter;
1687 double Y1 = Y0 - mapYCenter;
1688 double X2 = X1 * cosAlpha + Y1 * sinAlpha;
1689 double Y2 = -X1 * sinAlpha + Y1 * cosAlpha;
1690 X0 = X2 + mapXCenter;
1691 Y0 = Y2 + mapYCenter;
1695 int pageWidthPixels =
static_cast< int >( dpi * outputWidthMM / 25.4 );
1696 int pageHeightPixels =
static_cast< int >( dpi * outputHeightMM / 25.4 );
1697 double pixelWidthScale = paperExtent.width() / pageWidthPixels;
1698 double pixelHeightScale = paperExtent.height() / pageHeightPixels;
1701 std::unique_ptr<double[]> t(
new double[6] );
1703 t[1] = cosAlpha * pixelWidthScale;
1704 t[2] = -sinAlpha * pixelWidthScale;
1706 t[4] = -sinAlpha * pixelHeightScale;
1707 t[5] = -cosAlpha * pixelHeightScale;
1712void QgsLayoutExporter::writeWorldFile(
const QString &worldFileName,
double a,
double b,
double c,
double d,
double e,
double f )
const
1714 QFile worldFile( worldFileName );
1715 if ( !worldFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
1719 QTextStream fout( &worldFile );
1723 fout << QString::number( a,
'f', 12 ) <<
"\r\n";
1724 fout << QString::number( d,
'f', 12 ) <<
"\r\n";
1725 fout << QString::number( b,
'f', 12 ) <<
"\r\n";
1726 fout << QString::number( e,
'f', 12 ) <<
"\r\n";
1727 fout << QString::number(
c,
'f', 12 ) <<
"\r\n";
1728 fout << QString::number( f,
'f', 12 ) <<
"\r\n";
1733 return georeferenceOutputPrivate( file, map, exportRegion, dpi,
false );
1736bool QgsLayoutExporter::georeferenceOutputPrivate(
const QString &file,
QgsLayoutItemMap *map,
const QRectF &exportRegion,
double dpi,
bool includeGeoreference,
bool includeMetadata )
const
1741 if ( !map && includeGeoreference )
1742 map = mLayout->referenceMap();
1744 std::unique_ptr<double[]> t;
1746 if ( map && includeGeoreference )
1749 dpi = mLayout->renderContext().dpi();
1751 t = computeGeoTransform( map, exportRegion, dpi );
1756 CPLSetConfigOption(
"GDAL_PDF_DPI", QString::number( dpi ).toUtf8().constData() );
1761 GDALSetGeoTransform( outputDS.get(), t.get() );
1763 if ( includeMetadata )
1765 QString creationDateString;
1766 const QDateTime creationDateTime = mLayout->project()->metadata().creationDateTime();
1767#if QT_FEATURE_timezone > 0
1768 if ( creationDateTime.isValid() )
1770 creationDateString = u
"D:%1"_s.arg( mLayout->project()->metadata().creationDateTime().toString( u
"yyyyMMddHHmmss"_s ) );
1771 if ( creationDateTime.timeZone().isValid() )
1773 int offsetFromUtc = creationDateTime.timeZone().offsetFromUtc( creationDateTime );
1774 creationDateString += ( offsetFromUtc >= 0 ) ?
'+' :
'-';
1775 offsetFromUtc = std::abs( offsetFromUtc );
1776 int offsetHours = offsetFromUtc / 3600;
1777 int offsetMins = ( offsetFromUtc % 3600 ) / 60;
1778 creationDateString += u
"%1'%2'"_s.arg( offsetHours ).arg( offsetMins );
1782 QgsDebugError( u
"Qt is built without timezone support, skipping timezone for pdf export"_s );
1784 GDALSetMetadataItem( outputDS.get(),
"CREATION_DATE", creationDateString.toUtf8().constData(),
nullptr );
1786 GDALSetMetadataItem( outputDS.get(),
"AUTHOR", mLayout->project()->metadata().author().toUtf8().constData(),
nullptr );
1787 const QString creator = getCreator();
1788 GDALSetMetadataItem( outputDS.get(),
"CREATOR", creator.toUtf8().constData(),
nullptr );
1789 GDALSetMetadataItem( outputDS.get(),
"PRODUCER", creator.toUtf8().constData(),
nullptr );
1790 GDALSetMetadataItem( outputDS.get(),
"SUBJECT", mLayout->project()->metadata().abstract().toUtf8().constData(),
nullptr );
1791 GDALSetMetadataItem( outputDS.get(),
"TITLE", mLayout->project()->metadata().title().toUtf8().constData(),
nullptr );
1794 QStringList allKeywords;
1795 for (
auto it = keywords.constBegin(); it != keywords.constEnd(); ++it )
1797 allKeywords.append( u
"%1: %2"_s.arg( it.key(), it.value().join(
',' ) ) );
1799 const QString keywordString = allKeywords.join(
';' );
1800 GDALSetMetadataItem( outputDS.get(),
"KEYWORDS", keywordString.toUtf8().constData(),
nullptr );
1806 CPLSetConfigOption(
"GDAL_PDF_DPI",
nullptr );
1813 if ( items.count() == 1 )
1817 QString name = layoutItem->displayName();
1819 if ( name.startsWith(
'<' ) && name.endsWith(
'>' ) )
1820 name = name.mid( 1, name.length() - 2 );
1824 else if ( items.count() > 1 )
1826 QStringList currentLayerItemTypes;
1827 for ( QGraphicsItem *item : items )
1833 if ( !currentLayerItemTypes.contains( itemType ) && !currentLayerItemTypes.contains( itemTypePlural ) )
1834 currentLayerItemTypes << itemType;
1835 else if ( currentLayerItemTypes.contains( itemType ) )
1837 currentLayerItemTypes.replace( currentLayerItemTypes.indexOf( itemType ), itemTypePlural );
1842 if ( !currentLayerItemTypes.contains( QObject::tr(
"Other" ) ) )
1843 currentLayerItemTypes.append( QObject::tr(
"Other" ) );
1846 return currentLayerItemTypes.join(
", "_L1 );
1848 return QObject::tr(
"Layer %1" ).arg( layerId );
1852 const QList<QGraphicsItem *> &items,
1854 const std::function<QString(
QgsLayoutItem *item )> &getItemExportGroupFunc
1857 LayoutItemHider itemHider( items );
1862 QString previousItemGroup;
1863 unsigned int layerId = 1;
1864 QgsLayoutItem::ExportLayerDetail layerDetails;
1865 itemHider.hideAll();
1866 const QList< QGraphicsItem * > itemsToIterate = itemHider.itemsToIterate();
1867 QList< QGraphicsItem * > currentLayerItems;
1868 for ( QGraphicsItem *item : itemsToIterate )
1870 QgsLayoutItem *layoutItem =
dynamic_cast<QgsLayoutItem *
>( item );
1872 bool canPlaceInExistingLayer =
false;
1873 QString thisItemExportGroupName;
1877 thisItemExportGroupName = getItemExportGroupFunc( layoutItem );
1878 if ( !thisItemExportGroupName.isEmpty() )
1880 if ( thisItemExportGroupName != previousItemGroup && !currentLayerItems.empty() )
1883 layerDetails.
groupName = thisItemExportGroupName;
1886 switch ( itemExportBehavior )
1890 switch ( prevItemBehavior )
1893 canPlaceInExistingLayer =
true;
1897 canPlaceInExistingLayer = prevType == -1 || prevType == layoutItem->
type();
1902 canPlaceInExistingLayer =
false;
1910 switch ( prevItemBehavior )
1914 canPlaceInExistingLayer = prevType == -1 || prevType == layoutItem->
type();
1919 canPlaceInExistingLayer =
false;
1927 canPlaceInExistingLayer =
false;
1932 canPlaceInExistingLayer =
false;
1935 prevItemBehavior = itemExportBehavior;
1936 prevType = layoutItem->
type();
1937 previousItemGroup = thisItemExportGroupName;
1942 previousItemGroup.clear();
1945 if ( canPlaceInExistingLayer )
1947 currentLayerItems << item;
1952 if ( !currentLayerItems.isEmpty() )
1956 ExportResult result = exportFunc( layerId, layerDetails );
1960 currentLayerItems.clear();
1963 itemHider.hideAll();
1968 int layoutItemLayerIdx = 0;
1970 mLayout->renderContext().setCurrentExportLayer( layoutItemLayerIdx );
1976 mLayout->renderContext().setCurrentExportLayer( layoutItemLayerIdx );
1980 ExportResult result = exportFunc( layerId, layerDetails );
1985 layoutItemLayerIdx++;
1987 layerDetails.mapLayerId.clear();
1988 layerDetails.mapTheme.clear();
1990 mLayout->renderContext().setCurrentExportLayer( -1 );
1993 currentLayerItems.clear();
1997 currentLayerItems << item;
1999 layerDetails.groupName = thisItemExportGroupName;
2002 if ( !currentLayerItems.isEmpty() )
2005 ExportResult result = exportFunc( layerId, layerDetails );
2014 QgsVectorSimplifyMethod simplifyMethod;
2020 return simplifyMethod;
2025 QgsMaskRenderSettings settings;
2043 int pageNumber = map->
page();
2045 double pageY = page->pos().y();
2046 QSizeF pageSize = page->rect().size();
2047 QRectF pageRect( 0, pageY, pageSize.width(), pageSize.height() );
2063 double destinationHeight = exportRegion.height();
2064 double destinationWidth = exportRegion.width();
2066 QRectF mapItemSceneRect = map->mapRectToScene( map->rect() );
2071 double xRatio = mapExtent.
width() / mapItemSceneRect.width();
2072 double yRatio = mapExtent.
height() / mapItemSceneRect.height();
2074 double xCenter = mapExtent.
center().
x();
2075 double yCenter = mapExtent.
center().
y();
2078 QPointF mapItemPos = map->pos();
2080 mapItemPos.rx() -= exportRegion.left();
2081 mapItemPos.ry() -= exportRegion.top();
2083 double xmin = mapExtent.
xMinimum() - mapItemPos.x() * xRatio;
2084 double ymax = mapExtent.
yMaximum() + mapItemPos.y() * yRatio;
2085 QgsRectangle paperExtent( xmin, ymax - destinationHeight * yRatio, xmin + destinationWidth * xRatio, ymax );
2087 double X0 = paperExtent.
xMinimum();
2088 double Y0 = paperExtent.
yMinimum();
2091 dpi = mLayout->renderContext().dpi();
2093 int widthPx =
static_cast< int >( dpi * destinationWidth / 25.4 );
2094 int heightPx =
static_cast< int >( dpi * destinationHeight / 25.4 );
2096 double Ww = paperExtent.
width() / widthPx;
2097 double Hh = paperExtent.
height() / heightPx;
2106 s[5] = Y0 + paperExtent.
height();
2110 r[0] = std::cos( alpha );
2111 r[1] = -std::sin( alpha );
2112 r[2] = xCenter * ( 1 - std::cos( alpha ) ) + yCenter * std::sin( alpha );
2113 r[3] = std::sin( alpha );
2114 r[4] = std::cos( alpha );
2115 r[5] = -xCenter * std::sin( alpha ) + yCenter * ( 1 - std::cos( alpha ) );
2118 a = r[0] * s[0] + r[1] * s[3];
2119 b = r[0] * s[1] + r[1] * s[4];
2120 c = r[0] * s[2] + r[1] * s[5] + r[2];
2121 d = r[3] * s[0] + r[4] * s[3];
2122 e = r[3] * s[1] + r[4] * s[4];
2123 f = r[3] * s[2] + r[4] * s[5] + r[5];
2131 QList< QgsLayoutItem *> items;
2132 layout->layoutItems( items );
2137 if ( currentItem->isVisible() && currentItem->requiresRasterization() )
2148 QList< QgsLayoutItem *> items;
2149 layout->layoutItems( items );
2154 if ( currentItem->isVisible() && currentItem->containsAdvancedEffects() )
2167 if ( mLayout->pageCollection()->pageCount() == 1 )
2170 bounds = mLayout->layoutBounds(
true );
2175 bounds = mLayout->pageItemBounds( page,
true );
2177 if ( bounds.width() <= 0 || bounds.height() <= 0 )
2195int QgsLayoutExporter::firstPageToBeExported(
QgsLayout *layout )
2197 const int pageCount =
layout->pageCollection()->pageCount();
2198 for (
int i = 0; i < pageCount; ++i )
2200 if ( !
layout->pageCollection()->shouldExportPage( i ) )
2212 if ( details.
page == 0 )
2222void QgsLayoutExporter::captureLabelingResults()
2224 qDeleteAll( mLabelingResults );
2225 mLabelingResults.clear();
2227 QList< QgsLayoutItemMap * > maps;
2228 mLayout->layoutItems( maps );
2232 mLabelingResults[map->
uuid()] = map->mExportLabelingResults.release();
2236bool QgsLayoutExporter::saveImage(
const QImage &image,
const QString &imageFilename,
const QString &imageFormat,
QgsProject *projectForMetadata,
int quality )
2238 QImageWriter w( imageFilename, imageFormat.toLocal8Bit().constData() );
2239 if ( imageFormat.compare(
"tiff"_L1, Qt::CaseInsensitive ) == 0 || imageFormat.compare(
"tif"_L1, Qt::CaseInsensitive ) == 0 )
2241 w.setCompression( 1 );
2245 w.setQuality( quality );
2247 if ( projectForMetadata )
2249 w.setText( u
"Author"_s, projectForMetadata->
metadata().
author() );
2250 const QString creator = getCreator();
2251 w.setText( u
"Creator"_s, creator );
2252 w.setText( u
"Producer"_s, creator );
2255 w.setText( u
"Title"_s, projectForMetadata->
metadata().
title() );
2258 QStringList allKeywords;
2259 for (
auto it = keywords.constBegin(); it != keywords.constEnd(); ++it )
2261 allKeywords.append( u
"%1: %2"_s.arg( it.key(), it.value().join(
',' ) ) );
2263 const QString keywordString = allKeywords.join(
';' );
2264 w.setText( u
"Keywords"_s, keywordString );
2266 return w.write( image );
2269QString QgsLayoutExporter::getCreator()
2274void QgsLayoutExporter::setXmpMetadata( QPdfWriter *pdfWriter,
QgsLayout *layout )
2276#if QT_VERSION >= QT_VERSION_CHECK( 6, 8, 0 )
2277 QUuid documentId = pdfWriter->documentId();
2279 QUuid documentId = QUuid::createUuid();
2283 const QDateTime creationDateTime =
layout->project() ?
layout->project()->metadata().creationDateTime() : QDateTime();
2284 const QString metaDataDate = creationDateTime.isValid() ? creationDateTime.toOffsetFromUtc( creationDateTime.offsetFromUtc() ).toString( Qt::ISODate ) : QString();
2285 const QString title = pdfWriter->title();
2286 const QString creator = getCreator();
2287 const QString producer = creator;
2288 const QString author =
layout->project() ?
layout->project()->metadata().author() : QString();
2292 const QLatin1String xmlNS(
"http://www.w3.org/XML/1998/namespace" );
2293 const QLatin1String adobeNS(
"adobe:ns:meta/" );
2294 const QLatin1String rdfNS(
"http://www.w3.org/1999/02/22-rdf-syntax-ns#" );
2295 const QLatin1String dcNS(
"http://purl.org/dc/elements/1.1/" );
2296 const QLatin1String xmpNS(
"http://ns.adobe.com/xap/1.0/" );
2297 const QLatin1String xmpMMNS(
"http://ns.adobe.com/xap/1.0/mm/" );
2298 const QLatin1String pdfNS(
"http://ns.adobe.com/pdf/1.3/" );
2299 const QLatin1String pdfaidNS(
"http://www.aiim.org/pdfa/ns/id/" );
2301 QByteArray xmpMetadata;
2302 QBuffer output( &xmpMetadata );
2303 output.open( QIODevice::WriteOnly );
2304 output.write(
"<?xpacket begin='' ?>" );
2306 QXmlStreamWriter w( &output );
2307 w.setAutoFormatting(
true );
2308 w.writeNamespace( adobeNS,
"x" );
2309 w.writeNamespace( rdfNS,
"rdf" );
2310 w.writeNamespace( dcNS,
"dc" );
2311 w.writeNamespace( xmpNS,
"xmp" );
2312 w.writeNamespace( xmpMMNS,
"xmpMM" );
2313 w.writeNamespace( pdfNS,
"pdf" );
2314 w.writeNamespace( pdfaidNS,
"pdfaid" );
2316 w.writeStartElement( adobeNS,
"xmpmeta" );
2317 w.writeStartElement( rdfNS,
"RDF" );
2320 w.writeStartElement( rdfNS,
"Description" );
2321 w.writeAttribute( rdfNS,
"about",
"" );
2322 w.writeStartElement( dcNS,
"title" );
2323 w.writeStartElement( rdfNS,
"Alt" );
2324 w.writeStartElement( rdfNS,
"li" );
2325 w.writeAttribute( xmlNS,
"lang",
"x-default" );
2326 w.writeCharacters( title );
2327 w.writeEndElement();
2328 w.writeEndElement();
2329 w.writeEndElement();
2331 w.writeStartElement( dcNS,
"creator" );
2332 w.writeStartElement( rdfNS,
"Seq" );
2333 w.writeStartElement( rdfNS,
"li" );
2334 w.writeCharacters( author );
2335 w.writeEndElement();
2336 w.writeEndElement();
2337 w.writeEndElement();
2339 w.writeEndElement();
2342 w.writeStartElement( rdfNS,
"Description" );
2343 w.writeAttribute( rdfNS,
"about",
"" );
2344 w.writeAttribute( pdfNS,
"Producer", producer );
2345 w.writeAttribute( pdfNS,
"Trapped",
"False" );
2346 w.writeEndElement();
2349 w.writeStartElement( rdfNS,
"Description" );
2350 w.writeAttribute( rdfNS,
"about",
"" );
2351 w.writeAttribute( xmpNS,
"CreatorTool", creator );
2352 w.writeAttribute( xmpNS,
"CreateDate", metaDataDate );
2353 w.writeAttribute( xmpNS,
"ModifyDate", metaDataDate );
2354 w.writeAttribute( xmpNS,
"MetadataDate", metaDataDate );
2355 w.writeEndElement();
2358 w.writeStartElement( rdfNS,
"Description" );
2359 w.writeAttribute( rdfNS,
"about",
"" );
2360 w.writeAttribute( xmpMMNS,
"DocumentID",
"uuid:" + documentId.toString( QUuid::WithoutBraces ) );
2361 w.writeAttribute( xmpMMNS,
"VersionID",
"1" );
2362 w.writeAttribute( xmpMMNS,
"RenditionClass",
"default" );
2363 w.writeEndElement();
2365#if QT_VERSION >= QT_VERSION_CHECK( 6, 8, 0 )
2368 switch ( pdfWriter->pdfVersion() )
2370 case QPagedPaintDevice::PdfVersion_1_4:
2371 case QPagedPaintDevice::PdfVersion_A1b:
2372 case QPagedPaintDevice::PdfVersion_1_6:
2374 case QPagedPaintDevice::PdfVersion_X4:
2375 const QLatin1String pdfxidNS(
"http://www.npes.org/pdfx/ns/id/" );
2376 w.writeNamespace( pdfxidNS,
"pdfxid" );
2377 w.writeStartElement( rdfNS,
"Description" );
2378 w.writeAttribute( rdfNS,
"about",
"" );
2379 w.writeAttribute( pdfxidNS,
"GTS_PDFXVersion",
"PDF/X-4" );
2380 w.writeEndElement();
2386 w.writeEndElement();
2387 w.writeEndElement();
2389 w.writeEndDocument();
2390 output.write(
"<?xpacket end='w'?>" );
2392 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.
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 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.