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
58 LayoutContextPreviewSettingRestorer( QgsLayout *layout )
60 , mPreviousSetting( layout->renderContext().mIsPreviewRender )
62 mLayout->renderContext().mIsPreviewRender =
false;
65 ~LayoutContextPreviewSettingRestorer()
67 mLayout->renderContext().mIsPreviewRender = mPreviousSetting;
70 LayoutContextPreviewSettingRestorer(
const LayoutContextPreviewSettingRestorer &other ) =
delete;
71 LayoutContextPreviewSettingRestorer &operator=(
const LayoutContextPreviewSettingRestorer &other ) =
delete;
74 QgsLayout *mLayout =
nullptr;
75 bool mPreviousSetting =
false;
82 LayoutGuideHider( QgsLayout *layout )
85 const QList< QgsLayoutGuide * > guides = mLayout->guides().guides();
86 for ( QgsLayoutGuide *guide : guides )
88 mPrevVisibility.insert( guide, guide->item()->isVisible() );
89 guide->item()->setVisible(
false );
95 for (
auto it = mPrevVisibility.constBegin(); it != mPrevVisibility.constEnd(); ++it )
97 it.key()->item()->setVisible( it.value() );
101 LayoutGuideHider(
const LayoutGuideHider &other ) =
delete;
102 LayoutGuideHider &operator=(
const LayoutGuideHider &other ) =
delete;
105 QgsLayout *mLayout =
nullptr;
106 QHash< QgsLayoutGuide *, bool > mPrevVisibility;
112 explicit LayoutItemHider(
const QList<QGraphicsItem *> &items )
114 mItemsToIterate.reserve( items.count() );
115 for ( QGraphicsItem *item : items )
117 const bool isVisible = item->isVisible();
118 mPrevVisibility[item] = isVisible;
120 mItemsToIterate.append( item );
121 if ( QgsLayoutItem *layoutItem =
dynamic_cast< QgsLayoutItem *
>( item ) )
122 layoutItem->setProperty(
"wasVisible", isVisible );
130 for (
auto it = mPrevVisibility.constBegin(); it != mPrevVisibility.constEnd(); ++it )
138 for (
auto it = mPrevVisibility.constBegin(); it != mPrevVisibility.constEnd(); ++it )
140 it.key()->setVisible( it.value() );
141 if ( QgsLayoutItem *layoutItem =
dynamic_cast< QgsLayoutItem *
>( it.key() ) )
142 layoutItem->setProperty(
"wasVisible", QVariant() );
146 QList< QGraphicsItem * > itemsToIterate()
const {
return mItemsToIterate; }
148 LayoutItemHider(
const LayoutItemHider &other ) =
delete;
149 LayoutItemHider &operator=(
const LayoutItemHider &other ) =
delete;
153 QList<QGraphicsItem * > mItemsToIterate;
154 QHash<QGraphicsItem *, bool> mPrevVisibility;
172 qDeleteAll( mLabelingResults );
185 if ( mLayout->pageCollection()->pageCount() <= page || page < 0 )
196 LayoutContextPreviewSettingRestorer restorer( mLayout );
199 QRectF paperRect = QRectF( pageItem->pos().x(), pageItem->pos().y(), pageItem->rect().width(), pageItem->rect().height() );
208 if ( mLayout->pageCollection()->pageCount() <= page || page < 0 )
219 LayoutContextPreviewSettingRestorer restorer( mLayout );
222 QRectF paperRect = QRectF( pageItem->pos().x(), pageItem->pos().y(), pageItem->rect().width(), pageItem->rect().height() );
224 const double imageAspectRatio =
static_cast< double >( imageSize.width() ) / imageSize.height();
225 const double paperAspectRatio = paperRect.width() / paperRect.height();
226 if ( imageSize.isValid() && ( !
qgsDoubleNear( imageAspectRatio, paperAspectRatio, 0.008 ) ) )
231 QgsMessageLog::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 );
239class LayoutItemCacheSettingRestorer
243 LayoutItemCacheSettingRestorer(
QgsLayout *layout )
246 const QList< QGraphicsItem * > items = mLayout->items();
247 for ( QGraphicsItem *item : items )
249 mPrevCacheMode.insert( item, item->cacheMode() );
250 item->setCacheMode( QGraphicsItem::NoCache );
254 ~LayoutItemCacheSettingRestorer()
256 for (
auto it = mPrevCacheMode.constBegin(); it != mPrevCacheMode.constEnd(); ++it )
258 it.key()->setCacheMode( it.value() );
262 LayoutItemCacheSettingRestorer(
const LayoutItemCacheSettingRestorer &other ) =
delete;
263 LayoutItemCacheSettingRestorer &operator=(
const LayoutItemCacheSettingRestorer &other ) =
delete;
266 QgsLayout *mLayout =
nullptr;
267 QHash< QGraphicsItem *, QGraphicsItem::CacheMode > mPrevCacheMode;
274 QPaintDevice *paintDevice = painter->device();
275 if ( !paintDevice || !mLayout )
280 LayoutItemCacheSettingRestorer cacheRestorer( mLayout );
281 ( void )cacheRestorer;
282 LayoutContextPreviewSettingRestorer restorer( mLayout );
284 LayoutGuideHider guideHider( mLayout );
289 mLayout->render( painter, QRectF( 0, 0, paintDevice->width(), paintDevice->height() ), region );
297 LayoutContextPreviewSettingRestorer restorer( mLayout );
300 double resolution = mLayout->renderContext().dpi();
302 if ( imageSize.isValid() )
306 resolution = ( imageSize.width() / region.width()
307 + imageSize.height() / region.height() ) / 2.0 * oneInchInLayoutUnits;
315 int width = imageSize.isValid() ? imageSize.width()
316 :
static_cast< int >( resolution * region.width() / oneInchInLayoutUnits );
317 int height = imageSize.isValid() ? imageSize.height()
318 :
static_cast< int >( resolution * region.height() / oneInchInLayoutUnits );
320 QImage image( QSize( width, height ), QImage::Format_ARGB32 );
321 if ( !image.isNull() )
324 if ( width > 32768 || height > 32768 )
325 QgsMessageLog::logMessage( QObject::tr(
"Error: output width or height is larger than 32768 pixel, result will be clipped" ) );
326 image.setDotsPerMeterX(
static_cast< int >( std::round( resolution / 25.4 * 1000 ) ) );
327 image.setDotsPerMeterY(
static_cast< int>( std::round( resolution / 25.4 * 1000 ) ) );
328 image.fill( Qt::transparent );
329 QPainter imagePainter( &image );
331 if ( !imagePainter.isActive() )
339class LayoutContextSettingsRestorer
344 LayoutContextSettingsRestorer(
QgsLayout *layout )
346 , mPreviousDpi( layout->renderContext().dpi() )
347 , mPreviousFlags( layout->renderContext().flags() )
348 , mPreviousRasterPolicy( layout->renderContext().rasterizedRenderingPolicy() )
349 , mPreviousTextFormat( layout->renderContext().textRenderFormat() )
350 , mPreviousExportLayer( layout->renderContext().currentExportLayer() )
351 , mPreviousSimplifyMethod( layout->renderContext().simplifyMethod() )
352 , mPreviousMaskSettings( layout->renderContext().maskSettings() )
353 , mExportThemes( layout->renderContext().exportThemes() )
354 , mPredefinedScales( layout->renderContext().predefinedScales() )
359 ~LayoutContextSettingsRestorer()
361 mLayout->renderContext().setDpi( mPreviousDpi );
362 mLayout->renderContext().setFlags( mPreviousFlags );
363 mLayout->renderContext().setRasterizedRenderingPolicy( mPreviousRasterPolicy );
364 mLayout->renderContext().setTextRenderFormat( mPreviousTextFormat );
366 mLayout->renderContext().setCurrentExportLayer( mPreviousExportLayer );
368 mLayout->renderContext().setSimplifyMethod( mPreviousSimplifyMethod );
369 mLayout->renderContext().setMaskSettings( mPreviousMaskSettings );
370 mLayout->renderContext().setExportThemes( mExportThemes );
371 mLayout->renderContext().setPredefinedScales( mPredefinedScales );
374 LayoutContextSettingsRestorer(
const LayoutContextSettingsRestorer &other ) =
delete;
375 LayoutContextSettingsRestorer &operator=(
const LayoutContextSettingsRestorer &other ) =
delete;
378 QgsLayout *mLayout =
nullptr;
379 double mPreviousDpi = 0;
383 int mPreviousExportLayer = 0;
384 QgsVectorSimplifyMethod mPreviousSimplifyMethod;
385 QgsMaskRenderSettings mPreviousMaskSettings;
386 QStringList mExportThemes;
387 QVector< double > mPredefinedScales;
398 if ( settings.
dpi <= 0 )
399 settings.
dpi = mLayout->renderContext().dpi();
401 mErrorFileName.clear();
403 int worldFilePageNo = -1;
406 worldFilePageNo = referenceMap->page();
409 QFileInfo fi( filePath );
411 if ( !dir.exists( fi.absolutePath() ) )
413 dir.mkpath( fi.absolutePath() );
418 pageDetails.
baseName = fi.completeBaseName();
421 LayoutContextPreviewSettingRestorer restorer( mLayout );
423 LayoutContextSettingsRestorer dpiRestorer( mLayout );
425 mLayout->renderContext().setDpi( settings.
dpi );
426 mLayout->renderContext().setFlags( settings.
flags );
431 if ( settings.
pages.empty() )
433 for (
int page = 0; page < mLayout->pageCollection()->pageCount(); ++page )
438 for (
int page : std::as_const( settings.
pages ) )
440 if ( page >= 0 && page < mLayout->pageCollection()->pageCount() )
445 for (
int page : std::as_const( pages ) )
447 if ( !mLayout->pageCollection()->shouldExportPage( page ) )
454 QImage image = createImage( settings, page, bounds, skip );
459 pageDetails.
page = page;
462 if ( image.isNull() )
464 mErrorFileName = outputFilePath;
470 mErrorFileName = outputFilePath;
474 const bool shouldGeoreference = ( page == worldFilePageNo );
475 if ( shouldGeoreference )
477 georeferenceOutputPrivate( outputFilePath,
nullptr, bounds, settings.
dpi, shouldGeoreference );
482 double a, b,
c, d, e, f;
483 if ( bounds.isValid() )
488 QFileInfo fi( outputFilePath );
490 QString outputSuffix = fi.suffix();
491 QString worldFileName = fi.absolutePath() +
'/' + fi.completeBaseName() +
'.'
492 + outputSuffix.at( 0 ) + outputSuffix.at( fi.suffix().size() - 1 ) +
'w';
494 writeWorldFile( worldFileName, a, b,
c, d, e, f );
499 captureLabelingResults();
510 int total = iterator->
count();
511 double step = total > 0 ? 100.0 / total : 100.0;
513 while ( iterator->
next() )
518 feedback->setProperty(
"progress", QObject::tr(
"Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
520 feedback->setProperty(
"progress", QObject::tr(
"Exporting section %1" ).arg( i + 1 ) );
530 QString filePath = iterator->
filePath( baseFilePath, extension );
535 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 ) );
555 if ( !mLayout || mLayout->pageCollection()->pageCount() == 0 )
559 if ( settings.
dpi <= 0 )
560 settings.
dpi = mLayout->renderContext().dpi();
562 mErrorFileName.clear();
564 LayoutContextPreviewSettingRestorer restorer( mLayout );
566 LayoutContextSettingsRestorer contextRestorer( mLayout );
567 ( void )contextRestorer;
568 mLayout->renderContext().setDpi( settings.
dpi );
570 mLayout->renderContext().setMaskSettings( createExportMaskSettings() );
574 mLayout->renderContext().setSimplifyMethod( createExportSimplifyMethod() );
577 std::unique_ptr< QgsLayoutGeospatialPdfExporter > geospatialPdfExporter;
579 geospatialPdfExporter = std::make_unique< QgsLayoutGeospatialPdfExporter >( mLayout );
581 mLayout->renderContext().setFlags( settings.
flags );
601 mLayout->renderContext().setExportThemes( settings.
exportThemes );
613 const QList<QGraphicsItem *> items = mLayout->items( Qt::AscendingOrder );
615 QList< QgsLayoutGeospatialPdfExporter::ComponentLayerDetail > pdfComponents;
620 QSet<QString> mutuallyExclusiveGroups;
626 component.
name = layerDetail.name;
627 component.
mapLayerId = layerDetail.mapLayerId;
628 component.
opacity = layerDetail.opacity;
630 component.
group = layerDetail.groupName;
631 if ( !layerDetail.mapTheme.isEmpty() )
633 component.
group = layerDetail.mapTheme;
634 mutuallyExclusiveGroups.insert( layerDetail.mapTheme );
637 component.
sourcePdfPath = settings.writeGeoPdf ? geospatialPdfExporter->generateTemporaryFilepath( u
"layer_%1.pdf"_s.arg( layerId ) ) : baseDir.filePath( u
"%1_%2.pdf"_s.arg( baseFileName ).arg( layerId, 4, 10, QChar(
'0' ) ) );
638 pdfComponents << component;
640 preparePrintAsPdf( mLayout, &printer, component.
sourcePdfPath );
641 preparePrint( mLayout, &printer,
false );
643 if ( !p.begin( &printer ) )
651 return layerExportResult;
653 auto getExportGroupNameFunc = [](
QgsLayoutItem * item )->QString
655 return item->customProperty( u
"pdfExportGroup"_s ).toString();
657 result = handleLayeredExport( items, exportFunc, getExportGroupNameFunc );
661 if ( settings.writeGeoPdf )
664 details.
dpi = settings.dpi;
666 QgsLayoutSize pageSize = mLayout->pageCollection()->page( 0 )->sizeWithUnits();
671 if ( settings.exportMetadata )
674 details.
author = mLayout->project()->metadata().author();
676 details.
creator = getCreator();
677 details.
creationDateTime = mLayout->project()->metadata().creationDateTime();
678 details.
subject = mLayout->project()->metadata().abstract();
679 details.
title = mLayout->project()->metadata().title();
680 details.
keywords = mLayout->project()->metadata().keywords();
683 const QList< QgsMapLayer * > layers = mLayout->project()->mapLayers().values();
689 if ( settings.appendGeoreference )
692 QList< QgsLayoutItemMap * > maps;
693 mLayout->layoutItems( maps );
697 georef.
crs = map->crs();
699 const QPointF topLeft = map->mapToScene( QPointF( 0, 0 ) );
700 const QPointF topRight = map->mapToScene( QPointF( map->rect().width(), 0 ) );
701 const QPointF bottomLeft = map->mapToScene( QPointF( 0, map->rect().height() ) );
702 const QPointF bottomRight = map->mapToScene( QPointF( map->rect().width(), map->rect().height() ) );
715 const QTransform t = map->layoutToMapCoordsTransform();
716 const QgsPointXY topLeftMap = t.map( topLeft );
717 const QgsPointXY topRightMap = t.map( topRight );
718 const QgsPointXY bottomLeftMap = t.map( bottomLeft );
719 const QgsPointXY bottomRightMap = t.map( bottomRight );
731 details.
layerOrder = geospatialPdfExporter->layerOrder();
736 if ( !geospatialPdfExporter->finalize( pdfComponents, filePath, details ) )
739 mErrorMessage = geospatialPdfExporter->errorMessage();
749 QPdfWriter printer = QPdfWriter( filePath );
750 preparePrintAsPdf( mLayout, &printer, filePath );
751 preparePrint( mLayout, &printer,
false );
753 if ( !p.begin( &printer ) )
762 bool shouldAppendGeoreference = settings.
appendGeoreference && mLayout && mLayout->referenceMap() && mLayout->referenceMap()->page() == 0;
765 georeferenceOutputPrivate( filePath,
nullptr, QRectF(), settings.
dpi, shouldAppendGeoreference, settings.
exportMetadata );
768 captureLabelingResults();
781 QPdfWriter printer = QPdfWriter( fileName );
784 int total = iterator->
count();
785 double step = total > 0 ? 100.0 / total : 100.0;
788 while ( iterator->
next() )
793 feedback->setProperty(
"progress", QObject::tr(
"Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
795 feedback->setProperty(
"progress", QObject::tr(
"Exporting section %1" ).arg( i + 1 ) );
807 LayoutContextPreviewSettingRestorer restorer( iterator->
layout() );
809 LayoutContextSettingsRestorer contextRestorer( iterator->
layout() );
810 ( void )contextRestorer;
837 preparePrintAsPdf( iterator->
layout(), &printer, fileName );
838 preparePrint( iterator->
layout(), &printer,
false );
840 if ( !p.begin( &printer ) )
854 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 ) );
881 int total = iterator->
count();
882 double step = total > 0 ? 100.0 / total : 100.0;
884 while ( iterator->
next() )
889 feedback->setProperty(
"progress", QObject::tr(
"Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
891 feedback->setProperty(
"progress", QObject::tr(
"Exporting section %1" ).arg( i + 1 ) );
900 QString filePath = iterator->
filePath( baseFilePath, u
"pdf"_s );
907 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 ) );
925#if defined( HAVE_QTPRINTER )
932 if ( settings.
dpi <= 0 )
933 settings.
dpi = mLayout->renderContext().dpi();
935 mErrorFileName.clear();
937 LayoutContextPreviewSettingRestorer restorer( mLayout );
939 LayoutContextSettingsRestorer contextRestorer( mLayout );
940 ( void )contextRestorer;
941 mLayout->renderContext().setDpi( settings.
dpi );
943 mLayout->renderContext().setFlags( settings.
flags );
950 preparePrint( mLayout, &printer,
true );
952 if ( !p.begin( &printer ) )
961 captureLabelingResults();
976 int total = iterator->
count();
977 double step = total > 0 ? 100.0 / total : 100.0;
980 while ( iterator->
next() )
985 feedback->setProperty(
"progress", QObject::tr(
"Printing %1 of %2" ).arg( i + 1 ).arg( total ) );
987 feedback->setProperty(
"progress", QObject::tr(
"Printing section %1" ).arg( i + 1 ) );
999 LayoutContextPreviewSettingRestorer restorer( iterator->
layout() );
1001 LayoutContextSettingsRestorer contextRestorer( iterator->
layout() );
1002 ( void )contextRestorer;
1011 if ( !settings.rasterizeWholeImage )
1016 preparePrint( iterator->
layout(), &printer,
true );
1018 if ( !p.begin( &printer ) )
1028 ExportResult result = exporter.printPrivate( &printer, p, !first, settings.dpi, settings.rasterizeWholeImage );
1032 error = exporter.errorMessage();
1055 if ( settings.
dpi <= 0 )
1056 settings.
dpi = mLayout->renderContext().dpi();
1058 mErrorFileName.clear();
1060 LayoutContextPreviewSettingRestorer restorer( mLayout );
1062 LayoutContextSettingsRestorer contextRestorer( mLayout );
1063 ( void )contextRestorer;
1064 mLayout->renderContext().setDpi( settings.
dpi );
1066 mLayout->renderContext().setFlags( settings.
flags );
1078 mLayout->renderContext().setMaskSettings( createExportMaskSettings() );
1082 mLayout->renderContext().setSimplifyMethod( createExportSimplifyMethod() );
1085 QFileInfo fi( filePath );
1088 pageDetails.
baseName = fi.baseName();
1089 pageDetails.
extension = fi.completeSuffix();
1093 for (
int i = 0; i < mLayout->pageCollection()->pageCount(); ++i )
1095 if ( !mLayout->pageCollection()->shouldExportPage( i ) )
1100 pageDetails.
page = i;
1107 if ( mLayout->pageCollection()->pageCount() == 1 )
1110 bounds = mLayout->layoutBounds(
true );
1115 bounds = mLayout->pageItemBounds( i,
true );
1124 bounds = QRectF( pageItem->pos().x(), pageItem->pos().y(), pageItem->rect().width(), pageItem->rect().height() );
1128 int width =
static_cast< int >( bounds.width() * settings.
dpi / inchesToLayoutUnits );
1130 int height =
static_cast< int >( bounds.height() * settings.
dpi / inchesToLayoutUnits );
1131 if ( width == 0 || height == 0 )
1140 const QRectF paperRect = QRectF( pageItem->pos().x(),
1141 pageItem->pos().y(),
1142 pageItem->rect().width(),
1143 pageItem->rect().height() );
1145 QDomNode svgDocRoot;
1146 const QList<QGraphicsItem *> items = mLayout->items( paperRect,
1147 Qt::IntersectsItemBoundingRect,
1148 Qt::AscendingOrder );
1152 return renderToLayeredSvg( settings, width, height, i, bounds, fileName, layerId, layerDetail.name, svg, svgDocRoot, settings.
exportMetadata );
1154 auto getExportGroupNameFunc = [](
QgsLayoutItem * )->QString
1158 ExportResult res = handleLayeredExport( items, exportFunc, getExportGroupNameFunc );
1163 appendMetadataToSvg( svg );
1165 QFile out( fileName );
1166 bool openOk = out.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate );
1169 mErrorFileName = fileName;
1173 out.write( svg.toByteArray() );
1179 QSvgGenerator generator;
1182 generator.setTitle( mLayout->project()->metadata().title() );
1183 generator.setDescription( mLayout->project()->metadata().abstract() );
1185 generator.setOutputDevice( &svgBuffer );
1186 generator.setSize( QSize( width, height ) );
1187 generator.setViewBox( QRect( 0, 0, width, height ) );
1188 generator.setResolution(
static_cast< int >( std::round( settings.
dpi ) ) );
1191 bool createOk = p.begin( &generator );
1194 mErrorFileName = fileName;
1207 svgBuffer.open( QIODevice::ReadOnly );
1211 if ( ! svg.setContent( &svgBuffer,
false, &errorMsg, &errorLine ) )
1213 mErrorFileName = fileName;
1218 appendMetadataToSvg( svg );
1220 QFile out( fileName );
1221 bool openOk = out.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate );
1224 mErrorFileName = fileName;
1228 out.write( svg.toByteArray() );
1232 captureLabelingResults();
1243 int total = iterator->
count();
1244 double step = total > 0 ? 100.0 / total : 100.0;
1246 while ( iterator->
next() )
1251 feedback->setProperty(
"progress", QObject::tr(
"Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
1253 feedback->setProperty(
"progress", QObject::tr(
"Exporting section %1" ).arg( i + 1 ) );
1263 QString filePath = iterator->
filePath( baseFilePath, u
"svg"_s );
1270 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 ) );
1291 return mLabelingResults;
1296 QMap<QString, QgsLabelingResults *> res;
1297 std::swap( mLabelingResults, res );
1301void QgsLayoutExporter::preparePrintAsPdf(
QgsLayout *layout, QPdfWriter *device,
const QString &filePath )
1303 QFileInfo fi( filePath );
1305 if ( !dir.exists( fi.absolutePath() ) )
1307 dir.mkpath( fi.absolutePath() );
1310 updatePrinterPageSize(
layout, device, firstPageToBeExported(
layout ) );
1313 const QString title = !
layout->project() ||
layout->project()->metadata().title().isEmpty() ?
1314 fi.baseName() :
layout->project()->metadata().title();
1316 device->setTitle( title );
1318 QPagedPaintDevice::PdfVersion pdfVersion = QPagedPaintDevice::PdfVersion_1_4;
1320#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
1322 if (
const QgsProjectStyleSettings *styleSettings = (
layout->project() ?
layout->project()->styleSettings() :
nullptr ) )
1326 switch ( styleSettings->colorModel() )
1329 device->setColorModel( QPdfWriter::ColorModel::CMYK );
1333 device->setColorModel( QPdfWriter::ColorModel::RGB );
1337 const QColorSpace colorSpace = styleSettings->colorSpace();
1338 if ( colorSpace.isValid() )
1340 QPdfOutputIntent outputIntent;
1341 outputIntent.setOutputProfile( colorSpace );
1342 outputIntent.setOutputCondition( colorSpace.description() );
1346 outputIntent.setOutputConditionIdentifier( u
"Unknown identifier"_s );
1347 outputIntent.setRegistryName( u
"Unknown registry"_s );
1348 device->setOutputIntent( outputIntent );
1351 pdfVersion = QPagedPaintDevice::PdfVersion_X4;
1357 device->setPdfVersion( pdfVersion );
1358 setXmpMetadata( device,
layout );
1365void QgsLayoutExporter::preparePrint(
QgsLayout *layout, QPagedPaintDevice *device,
bool setFirstPageSize )
1367 if ( QPdfWriter *pdf =
dynamic_cast<QPdfWriter *
>( device ) )
1369 pdf->setResolution(
static_cast< int>( std::round(
layout->renderContext().dpi() ) ) );
1371#if defined( HAVE_QTPRINTER )
1372 else if ( QPrinter *printer =
dynamic_cast<QPrinter *
>( device ) )
1374 printer->setFullPage(
true );
1375 printer->setColorMode( QPrinter::Color );
1377 printer->setResolution(
static_cast< int>( std::round(
layout->renderContext().dpi() ) ) );
1381 if ( setFirstPageSize )
1383 updatePrinterPageSize(
layout, device, firstPageToBeExported(
layout ) );
1389 if ( mLayout->pageCollection()->pageCount() == 0 )
1392 preparePrint( mLayout, device,
true );
1394 if ( !p.begin( device ) )
1400 printPrivate( device, p );
1405QgsLayoutExporter::ExportResult QgsLayoutExporter::printPrivate( QPagedPaintDevice *device, QPainter &painter,
bool startNewPage,
double dpi,
bool rasterize )
1409 int toPage = mLayout->pageCollection()->pageCount() - 1;
1411#if defined( HAVE_QTPRINTER )
1412 if ( QPrinter *printer =
dynamic_cast<QPrinter *
>( device ) )
1414 if ( printer->fromPage() >= 1 )
1415 fromPage = printer->fromPage() - 1;
1416 if ( printer->toPage() >= 1 )
1417 toPage = printer->toPage() - 1;
1421 bool pageExported =
false;
1424 for (
int i = fromPage; i <= toPage; ++i )
1426 if ( !mLayout->pageCollection()->shouldExportPage( i ) )
1431 updatePrinterPageSize( mLayout, device, i );
1432 if ( ( pageExported && i > fromPage ) || startNewPage )
1438 if ( !image.isNull() )
1440 QRectF targetArea( 0, 0, image.width(), image.height() );
1441 painter.drawImage( targetArea, image, targetArea );
1447 pageExported =
true;
1452 for (
int i = fromPage; i <= toPage; ++i )
1454 if ( !mLayout->pageCollection()->shouldExportPage( i ) )
1459 updatePrinterPageSize( mLayout, device, i );
1461 if ( ( pageExported && i > fromPage ) || startNewPage )
1466 pageExported =
true;
1472void QgsLayoutExporter::updatePrinterPageSize(
QgsLayout *layout, QPagedPaintDevice *device,
int page )
1474 QgsLayoutSize pageSize =
layout->pageCollection()->page( page )->sizeWithUnits();
1477 QPageLayout pageLayout( QPageSize( pageSizeMM.
toQSizeF(), QPageSize::Millimeter ),
1478 QPageLayout::Portrait,
1479 QMarginsF( 0, 0, 0, 0 ) );
1480 pageLayout.setMode( QPageLayout::FullPageMode );
1481 device->setPageLayout( pageLayout );
1482 device->setPageMargins( QMarginsF( 0, 0, 0, 0 ) );
1484#if defined( HAVE_QTPRINTER )
1485 if ( QPrinter *printer =
dynamic_cast<QPrinter *
>( device ) )
1487 printer->setFullPage(
true );
1492QgsLayoutExporter::ExportResult QgsLayoutExporter::renderToLayeredSvg(
const SvgExportSettings &settings,
double width,
double height,
int page,
const QRectF &bounds,
const QString &filename,
unsigned int svgLayerId,
const QString &layerName, QDomDocument &svg, QDomNode &svgDocRoot,
bool includeMetadata )
const
1496 QSvgGenerator generator;
1497 if ( includeMetadata )
1499 if (
const QgsMasterLayoutInterface *l =
dynamic_cast< const QgsMasterLayoutInterface *
>( mLayout.data() ) )
1500 generator.setTitle( l->name() );
1501 else if ( mLayout->project() )
1502 generator.setTitle( mLayout->project()->title() );
1505 generator.setOutputDevice( &svgBuffer );
1506 generator.setSize( QSize(
static_cast< int >( std::round( width ) ),
1507 static_cast< int >( std::round( height ) ) ) );
1508 generator.setViewBox( QRect( 0, 0,
1509 static_cast< int >( std::round( width ) ),
1510 static_cast< int >( std::round( height ) ) ) );
1511 generator.setResolution(
static_cast< int >( std::round( settings.dpi ) ) );
1513 QPainter svgPainter( &generator );
1514 if ( settings.cropToContents )
1525 svgBuffer.open( QIODevice::ReadOnly );
1529 if ( ! doc.setContent( &svgBuffer,
false, &errorMsg, &errorLine ) )
1531 mErrorFileName = filename;
1534 if ( 1 == svgLayerId )
1536 svg = QDomDocument( doc.doctype() );
1537 svg.appendChild( svg.importNode( doc.firstChild(),
false ) );
1538 svgDocRoot = svg.importNode( doc.elementsByTagName( u
"svg"_s ).at( 0 ),
false );
1539 svgDocRoot.toElement().setAttribute( u
"xmlns:inkscape"_s, u
"http://www.inkscape.org/namespaces/inkscape"_s );
1540 svg.appendChild( svgDocRoot );
1542 QDomNode mainGroup = svg.importNode( doc.elementsByTagName( u
"g"_s ).at( 0 ),
true );
1543 mainGroup.toElement().setAttribute( u
"id"_s, layerName );
1544 mainGroup.toElement().setAttribute( u
"inkscape:label"_s, layerName );
1545 mainGroup.toElement().setAttribute( u
"inkscape:groupmode"_s, u
"layer"_s );
1546 QDomNode defs = svg.importNode( doc.elementsByTagName( u
"defs"_s ).at( 0 ),
true );
1547 svgDocRoot.appendChild( defs );
1548 svgDocRoot.appendChild( mainGroup );
1553void QgsLayoutExporter::appendMetadataToSvg( QDomDocument &svg )
const
1555 const QgsProjectMetadata &metadata = mLayout->project()->metadata();
1556 QDomElement metadataElement = svg.createElement( u
"metadata"_s );
1557 QDomElement rdfElement = svg.createElement( u
"rdf:RDF"_s );
1558 rdfElement.setAttribute( u
"xmlns:rdf"_s, u
"http://www.w3.org/1999/02/22-rdf-syntax-ns#"_s );
1559 rdfElement.setAttribute( u
"xmlns:rdfs"_s, u
"http://www.w3.org/2000/01/rdf-schema#"_s );
1560 rdfElement.setAttribute( u
"xmlns:dc"_s, u
"http://purl.org/dc/elements/1.1/"_s );
1561 QDomElement descriptionElement = svg.createElement( u
"rdf:Description"_s );
1562 QDomElement workElement = svg.createElement( u
"cc:Work"_s );
1563 workElement.setAttribute( u
"rdf:about"_s, QString() );
1565 auto addTextNode = [&workElement, &descriptionElement, &svg](
const QString & tag,
const QString & value )
1568 QDomElement element = svg.createElement( tag );
1569 QDomText t = svg.createTextNode( value );
1570 element.appendChild( t );
1571 workElement.appendChild( element );
1574 descriptionElement.setAttribute( tag, value );
1577 addTextNode( u
"dc:format"_s, u
"image/svg+xml"_s );
1578 addTextNode( u
"dc:title"_s, metadata.
title() );
1579 addTextNode( u
"dc:date"_s, metadata.
creationDateTime().toString( Qt::ISODate ) );
1580 addTextNode( u
"dc:identifier"_s, metadata.
identifier() );
1581 addTextNode( u
"dc:description"_s, metadata.
abstract() );
1583 auto addAgentNode = [&workElement, &descriptionElement, &svg](
const QString & tag,
const QString & value )
1586 QDomElement inkscapeElement = svg.createElement( tag );
1587 QDomElement agentElement = svg.createElement( u
"cc:Agent"_s );
1588 QDomElement titleElement = svg.createElement( u
"dc:title"_s );
1589 QDomText t = svg.createTextNode( value );
1590 titleElement.appendChild( t );
1591 agentElement.appendChild( titleElement );
1592 inkscapeElement.appendChild( agentElement );
1593 workElement.appendChild( inkscapeElement );
1596 QDomElement bagElement = svg.createElement( u
"rdf:Bag"_s );
1597 QDomElement liElement = svg.createElement( u
"rdf:li"_s );
1598 t = svg.createTextNode( value );
1599 liElement.appendChild( t );
1600 bagElement.appendChild( liElement );
1602 QDomElement element = svg.createElement( tag );
1603 element.appendChild( bagElement );
1604 descriptionElement.appendChild( element );
1607 addAgentNode( u
"dc:creator"_s, metadata.
author() );
1608 addAgentNode( u
"dc:publisher"_s, getCreator() );
1612 QDomElement element = svg.createElement( u
"dc:subject"_s );
1613 QDomElement bagElement = svg.createElement( u
"rdf:Bag"_s );
1615 for (
auto it = keywords.constBegin(); it != keywords.constEnd(); ++it )
1617 const QStringList words = it.value();
1618 for (
const QString &keyword : words )
1620 QDomElement liElement = svg.createElement( u
"rdf:li"_s );
1621 QDomText t = svg.createTextNode( keyword );
1622 liElement.appendChild( t );
1623 bagElement.appendChild( liElement );
1626 element.appendChild( bagElement );
1627 workElement.appendChild( element );
1628 descriptionElement.appendChild( element );
1631 rdfElement.appendChild( descriptionElement );
1632 rdfElement.appendChild( workElement );
1633 metadataElement.appendChild( rdfElement );
1634 svg.documentElement().appendChild( metadataElement );
1635 svg.documentElement().setAttribute( u
"xmlns:cc"_s, u
"http://creativecommons.org/ns#"_s );
1638std::unique_ptr<double[]> QgsLayoutExporter::computeGeoTransform(
const QgsLayoutItemMap *map,
const QRectF ®ion,
double dpi )
const
1641 map = mLayout->referenceMap();
1647 dpi = mLayout->renderContext().dpi();
1650 QRectF exportRegion = region;
1651 if ( !exportRegion.isValid() )
1653 int pageNumber = map->
page();
1655 QgsLayoutItemPage *page = mLayout->pageCollection()->page( pageNumber );
1656 double pageY = page->pos().y();
1657 QSizeF pageSize = page->rect().size();
1658 exportRegion = QRectF( 0, pageY, pageSize.width(), pageSize.height() );
1662 QRectF mapItemSceneRect = map->mapRectToScene( map->rect() );
1665 double outputHeightMM = exportRegion.height();
1666 double outputWidthMM = exportRegion.width();
1669 QgsRectangle mapExtent = map->
extent();
1670 double mapXCenter = mapExtent.
center().
x();
1671 double mapYCenter = mapExtent.
center().
y();
1673 double sinAlpha = std::sin( alpha );
1674 double cosAlpha = std::cos( alpha );
1677 QPointF mapItemPos = map->pos();
1679 mapItemPos.rx() -= exportRegion.left();
1680 mapItemPos.ry() -= exportRegion.top();
1683 double xRatio = mapExtent.
width() / mapItemSceneRect.width();
1684 double yRatio = mapExtent.
height() / mapItemSceneRect.height();
1685 double xmin = mapExtent.
xMinimum() - mapItemPos.x() * xRatio;
1686 double ymax = mapExtent.
yMaximum() + mapItemPos.y() * yRatio;
1687 QgsRectangle paperExtent( xmin, ymax - outputHeightMM * yRatio, xmin + outputWidthMM * xRatio, ymax );
1690 double X0 = paperExtent.xMinimum();
1691 double Y0 = paperExtent.yMaximum();
1696 double X1 = X0 - mapXCenter;
1697 double Y1 = Y0 - mapYCenter;
1698 double X2 = X1 * cosAlpha + Y1 * sinAlpha;
1699 double Y2 = -X1 * sinAlpha + Y1 * cosAlpha;
1700 X0 = X2 + mapXCenter;
1701 Y0 = Y2 + mapYCenter;
1705 int pageWidthPixels =
static_cast< int >( dpi * outputWidthMM / 25.4 );
1706 int pageHeightPixels =
static_cast< int >( dpi * outputHeightMM / 25.4 );
1707 double pixelWidthScale = paperExtent.width() / pageWidthPixels;
1708 double pixelHeightScale = paperExtent.height() / pageHeightPixels;
1711 std::unique_ptr<double[]> t(
new double[6] );
1713 t[1] = cosAlpha * pixelWidthScale;
1714 t[2] = -sinAlpha * pixelWidthScale;
1716 t[4] = -sinAlpha * pixelHeightScale;
1717 t[5] = -cosAlpha * pixelHeightScale;
1722void QgsLayoutExporter::writeWorldFile(
const QString &worldFileName,
double a,
double b,
double c,
double d,
double e,
double f )
const
1724 QFile worldFile( worldFileName );
1725 if ( !worldFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
1729 QTextStream fout( &worldFile );
1733 fout << QString::number( a,
'f', 12 ) <<
"\r\n";
1734 fout << QString::number( d,
'f', 12 ) <<
"\r\n";
1735 fout << QString::number( b,
'f', 12 ) <<
"\r\n";
1736 fout << QString::number( e,
'f', 12 ) <<
"\r\n";
1737 fout << QString::number(
c,
'f', 12 ) <<
"\r\n";
1738 fout << QString::number( f,
'f', 12 ) <<
"\r\n";
1743 return georeferenceOutputPrivate( file, map, exportRegion, dpi,
false );
1746bool QgsLayoutExporter::georeferenceOutputPrivate(
const QString &file,
QgsLayoutItemMap *map,
const QRectF &exportRegion,
double dpi,
bool includeGeoreference,
bool includeMetadata )
const
1751 if ( !map && includeGeoreference )
1752 map = mLayout->referenceMap();
1754 std::unique_ptr<double[]> t;
1756 if ( map && includeGeoreference )
1759 dpi = mLayout->renderContext().dpi();
1761 t = computeGeoTransform( map, exportRegion, dpi );
1766 CPLSetConfigOption(
"GDAL_PDF_DPI", QString::number( dpi ).toUtf8().constData() );
1771 GDALSetGeoTransform( outputDS.get(), t.get() );
1773 if ( includeMetadata )
1775 QString creationDateString;
1776 const QDateTime creationDateTime = mLayout->project()->metadata().creationDateTime();
1777#if QT_FEATURE_timezone > 0
1778 if ( creationDateTime.isValid() )
1780 creationDateString = u
"D:%1"_s.arg( mLayout->project()->metadata().creationDateTime().toString( u
"yyyyMMddHHmmss"_s ) );
1781 if ( creationDateTime.timeZone().isValid() )
1783 int offsetFromUtc = creationDateTime.timeZone().offsetFromUtc( creationDateTime );
1784 creationDateString += ( offsetFromUtc >= 0 ) ?
'+' :
'-';
1785 offsetFromUtc = std::abs( offsetFromUtc );
1786 int offsetHours = offsetFromUtc / 3600;
1787 int offsetMins = ( offsetFromUtc % 3600 ) / 60;
1788 creationDateString += u
"%1'%2'"_s.arg( offsetHours ).arg( offsetMins );
1792 QgsDebugError( u
"Qt is built without timezone support, skipping timezone for pdf export"_s );
1794 GDALSetMetadataItem( outputDS.get(),
"CREATION_DATE", creationDateString.toUtf8().constData(),
nullptr );
1796 GDALSetMetadataItem( outputDS.get(),
"AUTHOR", mLayout->project()->metadata().author().toUtf8().constData(),
nullptr );
1797 const QString creator = getCreator();
1798 GDALSetMetadataItem( outputDS.get(),
"CREATOR", creator.toUtf8().constData(),
nullptr );
1799 GDALSetMetadataItem( outputDS.get(),
"PRODUCER", creator.toUtf8().constData(),
nullptr );
1800 GDALSetMetadataItem( outputDS.get(),
"SUBJECT", mLayout->project()->metadata().abstract().toUtf8().constData(),
nullptr );
1801 GDALSetMetadataItem( outputDS.get(),
"TITLE", mLayout->project()->metadata().title().toUtf8().constData(),
nullptr );
1804 QStringList allKeywords;
1805 for (
auto it = keywords.constBegin(); it != keywords.constEnd(); ++it )
1807 allKeywords.append( u
"%1: %2"_s.arg( it.key(), it.value().join(
',' ) ) );
1809 const QString keywordString = allKeywords.join(
';' );
1810 GDALSetMetadataItem( outputDS.get(),
"KEYWORDS", keywordString.toUtf8().constData(),
nullptr );
1816 CPLSetConfigOption(
"GDAL_PDF_DPI",
nullptr );
1823 if ( items.count() == 1 )
1827 QString name = layoutItem->displayName();
1829 if ( name.startsWith(
'<' ) && name.endsWith(
'>' ) )
1830 name = name.mid( 1, name.length() - 2 );
1834 else if ( items.count() > 1 )
1836 QStringList currentLayerItemTypes;
1837 for ( QGraphicsItem *item : items )
1843 if ( !currentLayerItemTypes.contains( itemType ) && !currentLayerItemTypes.contains( itemTypePlural ) )
1844 currentLayerItemTypes << itemType;
1845 else if ( currentLayerItemTypes.contains( itemType ) )
1847 currentLayerItemTypes.replace( currentLayerItemTypes.indexOf( itemType ), itemTypePlural );
1852 if ( !currentLayerItemTypes.contains( QObject::tr(
"Other" ) ) )
1853 currentLayerItemTypes.append( QObject::tr(
"Other" ) );
1856 return currentLayerItemTypes.join(
", "_L1 );
1858 return QObject::tr(
"Layer %1" ).arg( layerId );
1863 const std::function<QString(
QgsLayoutItem *item )> &getItemExportGroupFunc )
1865 LayoutItemHider itemHider( items );
1870 QString previousItemGroup;
1871 unsigned int layerId = 1;
1872 QgsLayoutItem::ExportLayerDetail layerDetails;
1873 itemHider.hideAll();
1874 const QList< QGraphicsItem * > itemsToIterate = itemHider.itemsToIterate();
1875 QList< QGraphicsItem * > currentLayerItems;
1876 for ( QGraphicsItem *item : itemsToIterate )
1878 QgsLayoutItem *layoutItem =
dynamic_cast<QgsLayoutItem *
>( item );
1880 bool canPlaceInExistingLayer =
false;
1881 QString thisItemExportGroupName;
1885 thisItemExportGroupName = getItemExportGroupFunc( layoutItem );
1886 if ( !thisItemExportGroupName.isEmpty() )
1888 if ( thisItemExportGroupName != previousItemGroup && !currentLayerItems.empty() )
1891 layerDetails.
groupName = thisItemExportGroupName;
1894 switch ( itemExportBehavior )
1898 switch ( prevItemBehavior )
1901 canPlaceInExistingLayer =
true;
1905 canPlaceInExistingLayer = prevType == -1 || prevType == layoutItem->
type();
1910 canPlaceInExistingLayer =
false;
1918 switch ( prevItemBehavior )
1922 canPlaceInExistingLayer = prevType == -1 || prevType == layoutItem->
type();
1927 canPlaceInExistingLayer =
false;
1935 canPlaceInExistingLayer =
false;
1940 canPlaceInExistingLayer =
false;
1943 prevItemBehavior = itemExportBehavior;
1944 prevType = layoutItem->
type();
1945 previousItemGroup = thisItemExportGroupName;
1950 previousItemGroup.clear();
1953 if ( canPlaceInExistingLayer )
1955 currentLayerItems << item;
1960 if ( !currentLayerItems.isEmpty() )
1964 ExportResult result = exportFunc( layerId, layerDetails );
1968 currentLayerItems.clear();
1971 itemHider.hideAll();
1976 int layoutItemLayerIdx = 0;
1978 mLayout->renderContext().setCurrentExportLayer( layoutItemLayerIdx );
1984 mLayout->renderContext().setCurrentExportLayer( layoutItemLayerIdx );
1988 ExportResult result = exportFunc( layerId, layerDetails );
1993 layoutItemLayerIdx++;
1995 layerDetails.mapLayerId.clear();
1996 layerDetails.mapTheme.clear();
1998 mLayout->renderContext().setCurrentExportLayer( -1 );
2001 currentLayerItems.clear();
2005 currentLayerItems << item;
2007 layerDetails.groupName = thisItemExportGroupName;
2010 if ( !currentLayerItems.isEmpty() )
2013 ExportResult result = exportFunc( layerId, layerDetails );
2022 QgsVectorSimplifyMethod simplifyMethod;
2028 return simplifyMethod;
2033 QgsMaskRenderSettings settings;
2051 int pageNumber = map->
page();
2053 double pageY = page->pos().y();
2054 QSizeF pageSize = page->rect().size();
2055 QRectF pageRect( 0, pageY, pageSize.width(), pageSize.height() );
2071 double destinationHeight = exportRegion.height();
2072 double destinationWidth = exportRegion.width();
2074 QRectF mapItemSceneRect = map->mapRectToScene( map->rect() );
2079 double xRatio = mapExtent.
width() / mapItemSceneRect.width();
2080 double yRatio = mapExtent.
height() / mapItemSceneRect.height();
2082 double xCenter = mapExtent.
center().
x();
2083 double yCenter = mapExtent.
center().
y();
2086 QPointF mapItemPos = map->pos();
2088 mapItemPos.rx() -= exportRegion.left();
2089 mapItemPos.ry() -= exportRegion.top();
2091 double xmin = mapExtent.
xMinimum() - mapItemPos.x() * xRatio;
2092 double ymax = mapExtent.
yMaximum() + mapItemPos.y() * yRatio;
2093 QgsRectangle paperExtent( xmin, ymax - destinationHeight * yRatio, xmin + destinationWidth * xRatio, ymax );
2095 double X0 = paperExtent.
xMinimum();
2096 double Y0 = paperExtent.
yMinimum();
2099 dpi = mLayout->renderContext().dpi();
2101 int widthPx =
static_cast< int >( dpi * destinationWidth / 25.4 );
2102 int heightPx =
static_cast< int >( dpi * destinationHeight / 25.4 );
2104 double Ww = paperExtent.
width() / widthPx;
2105 double Hh = paperExtent.
height() / heightPx;
2114 s[5] = Y0 + paperExtent.
height();
2118 r[0] = std::cos( alpha );
2119 r[1] = -std::sin( alpha );
2120 r[2] = xCenter * ( 1 - std::cos( alpha ) ) + yCenter * std::sin( alpha );
2121 r[3] = std::sin( alpha );
2122 r[4] = std::cos( alpha );
2123 r[5] = - xCenter * std::sin( alpha ) + yCenter * ( 1 - std::cos( alpha ) );
2126 a = r[0] * s[0] + r[1] * s[3];
2127 b = r[0] * s[1] + r[1] * s[4];
2128 c = r[0] * s[2] + r[1] * s[5] + r[2];
2129 d = r[3] * s[0] + r[4] * s[3];
2130 e = r[3] * s[1] + r[4] * s[4];
2131 f = r[3] * s[2] + r[4] * s[5] + r[5];
2139 QList< QgsLayoutItem *> items;
2140 layout->layoutItems( items );
2145 if ( currentItem->isVisible() && currentItem->requiresRasterization() )
2156 QList< QgsLayoutItem *> items;
2157 layout->layoutItems( items );
2162 if ( currentItem->isVisible() && currentItem->containsAdvancedEffects() )
2175 if ( mLayout->pageCollection()->pageCount() == 1 )
2178 bounds = mLayout->layoutBounds(
true );
2183 bounds = mLayout->pageItemBounds( page,
true );
2185 if ( bounds.width() <= 0 || bounds.height() <= 0 )
2193 bounds = bounds.adjusted( -settings.
cropMargins.
left() * pixelToLayoutUnits,
2205int QgsLayoutExporter::firstPageToBeExported(
QgsLayout *layout )
2207 const int pageCount =
layout->pageCollection()->pageCount();
2208 for (
int i = 0; i < pageCount; ++i )
2210 if ( !
layout->pageCollection()->shouldExportPage( i ) )
2222 if ( details.
page == 0 )
2232void QgsLayoutExporter::captureLabelingResults()
2234 qDeleteAll( mLabelingResults );
2235 mLabelingResults.clear();
2237 QList< QgsLayoutItemMap * > maps;
2238 mLayout->layoutItems( maps );
2242 mLabelingResults[ map->
uuid() ] = map->mExportLabelingResults.release();
2246bool QgsLayoutExporter::saveImage(
const QImage &image,
const QString &imageFilename,
const QString &imageFormat,
QgsProject *projectForMetadata,
int quality )
2248 QImageWriter w( imageFilename, imageFormat.toLocal8Bit().constData() );
2249 if ( imageFormat.compare(
"tiff"_L1, Qt::CaseInsensitive ) == 0 || imageFormat.compare(
"tif"_L1, Qt::CaseInsensitive ) == 0 )
2251 w.setCompression( 1 );
2255 w.setQuality( quality );
2257 if ( projectForMetadata )
2259 w.setText( u
"Author"_s, projectForMetadata->
metadata().
author() );
2260 const QString creator = getCreator();
2261 w.setText( u
"Creator"_s, creator );
2262 w.setText( u
"Producer"_s, creator );
2265 w.setText( u
"Title"_s, projectForMetadata->
metadata().
title() );
2268 QStringList allKeywords;
2269 for (
auto it = keywords.constBegin(); it != keywords.constEnd(); ++it )
2271 allKeywords.append( u
"%1: %2"_s.arg( it.key(), it.value().join(
',' ) ) );
2273 const QString keywordString = allKeywords.join(
';' );
2274 w.setText( u
"Keywords"_s, keywordString );
2276 return w.write( image );
2279QString QgsLayoutExporter::getCreator()
2284void QgsLayoutExporter::setXmpMetadata( QPdfWriter *pdfWriter,
QgsLayout *layout )
2286#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
2287 QUuid documentId = pdfWriter->documentId();
2289 QUuid documentId = QUuid::createUuid();
2293 const QDateTime creationDateTime =
layout->project() ?
layout->project()->metadata().creationDateTime() : QDateTime();
2294 const QString metaDataDate = creationDateTime.isValid() ? creationDateTime.toOffsetFromUtc( creationDateTime.offsetFromUtc() ).toString( Qt::ISODate ) : QString();
2295 const QString title = pdfWriter->title();
2296 const QString creator = getCreator();
2297 const QString producer = creator;
2298 const QString author =
layout->project() ?
layout->project()->metadata().author() : QString();
2302 const QLatin1String xmlNS(
"http://www.w3.org/XML/1998/namespace" );
2303 const QLatin1String adobeNS(
"adobe:ns:meta/" );
2304 const QLatin1String rdfNS(
"http://www.w3.org/1999/02/22-rdf-syntax-ns#" );
2305 const QLatin1String dcNS(
"http://purl.org/dc/elements/1.1/" );
2306 const QLatin1String xmpNS(
"http://ns.adobe.com/xap/1.0/" );
2307 const QLatin1String xmpMMNS(
"http://ns.adobe.com/xap/1.0/mm/" );
2308 const QLatin1String pdfNS(
"http://ns.adobe.com/pdf/1.3/" );
2309 const QLatin1String pdfaidNS(
"http://www.aiim.org/pdfa/ns/id/" );
2311 QByteArray xmpMetadata;
2312 QBuffer output( &xmpMetadata );
2313 output.open( QIODevice::WriteOnly );
2314 output.write(
"<?xpacket begin='' ?>" );
2316 QXmlStreamWriter w( &output );
2317 w.setAutoFormatting(
true );
2318 w.writeNamespace( adobeNS,
"x" );
2319 w.writeNamespace( rdfNS,
"rdf" );
2320 w.writeNamespace( dcNS,
"dc" );
2321 w.writeNamespace( xmpNS,
"xmp" );
2322 w.writeNamespace( xmpMMNS,
"xmpMM" );
2323 w.writeNamespace( pdfNS,
"pdf" );
2324 w.writeNamespace( pdfaidNS,
"pdfaid" );
2326 w.writeStartElement( adobeNS,
"xmpmeta" );
2327 w.writeStartElement( rdfNS,
"RDF" );
2330 w.writeStartElement( rdfNS,
"Description" );
2331 w.writeAttribute( rdfNS,
"about",
"" );
2332 w.writeStartElement( dcNS,
"title" );
2333 w.writeStartElement( rdfNS,
"Alt" );
2334 w.writeStartElement( rdfNS,
"li" );
2335 w.writeAttribute( xmlNS,
"lang",
"x-default" );
2336 w.writeCharacters( title );
2337 w.writeEndElement();
2338 w.writeEndElement();
2339 w.writeEndElement();
2341 w.writeStartElement( dcNS,
"creator" );
2342 w.writeStartElement( rdfNS,
"Seq" );
2343 w.writeStartElement( rdfNS,
"li" );
2344 w.writeCharacters( author );
2345 w.writeEndElement();
2346 w.writeEndElement();
2347 w.writeEndElement();
2349 w.writeEndElement();
2352 w.writeStartElement( rdfNS,
"Description" );
2353 w.writeAttribute( rdfNS,
"about",
"" );
2354 w.writeAttribute( pdfNS,
"Producer", producer );
2355 w.writeAttribute( pdfNS,
"Trapped",
"False" );
2356 w.writeEndElement();
2359 w.writeStartElement( rdfNS,
"Description" );
2360 w.writeAttribute( rdfNS,
"about",
"" );
2361 w.writeAttribute( xmpNS,
"CreatorTool", creator );
2362 w.writeAttribute( xmpNS,
"CreateDate", metaDataDate );
2363 w.writeAttribute( xmpNS,
"ModifyDate", metaDataDate );
2364 w.writeAttribute( xmpNS,
"MetadataDate", metaDataDate );
2365 w.writeEndElement();
2368 w.writeStartElement( rdfNS,
"Description" );
2369 w.writeAttribute( rdfNS,
"about",
"" );
2370 w.writeAttribute( xmpMMNS,
"DocumentID",
"uuid:" + documentId.toString( QUuid::WithoutBraces ) );
2371 w.writeAttribute( xmpMMNS,
"VersionID",
"1" );
2372 w.writeAttribute( xmpMMNS,
"RenditionClass",
"default" );
2373 w.writeEndElement();
2375#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
2378 switch ( pdfWriter->pdfVersion() )
2380 case QPagedPaintDevice::PdfVersion_1_4:
2381 case QPagedPaintDevice::PdfVersion_A1b:
2382 case QPagedPaintDevice::PdfVersion_1_6:
2384 case QPagedPaintDevice::PdfVersion_X4:
2385 const QLatin1String pdfxidNS(
"http://www.npes.org/pdfx/ns/id/" );
2386 w.writeNamespace( pdfxidNS,
"pdfxid" );
2387 w.writeStartElement( rdfNS,
"Description" );
2388 w.writeAttribute( rdfNS,
"about",
"" );
2389 w.writeAttribute( pdfxidNS,
"GTS_PDFXVersion",
"PDF/X-4" );
2390 w.writeEndElement();
2396 w.writeEndElement();
2397 w.writeEndElement();
2399 w.writeEndDocument();
2400 output.write(
"<?xpacket end='w'?>" );
2402 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.