34#include <QImageWriter>
36#include <QSvgGenerator>
40#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
42#include <QPdfOutputIntent>
44#include <QXmlStreamWriter>
50class LayoutContextPreviewSettingRestorer
54 LayoutContextPreviewSettingRestorer(
QgsLayout *layout )
56 , mPreviousSetting( layout->renderContext().mIsPreviewRender )
58 mLayout->renderContext().mIsPreviewRender =
false;
61 ~LayoutContextPreviewSettingRestorer()
63 mLayout->renderContext().mIsPreviewRender = mPreviousSetting;
66 LayoutContextPreviewSettingRestorer(
const LayoutContextPreviewSettingRestorer &other ) =
delete;
67 LayoutContextPreviewSettingRestorer &operator=(
const LayoutContextPreviewSettingRestorer &other ) =
delete;
71 bool mPreviousSetting =
false;
81 const QList< QgsLayoutGuide * > guides = mLayout->guides().guides();
84 mPrevVisibility.insert( guide, guide->item()->isVisible() );
85 guide->item()->setVisible(
false );
91 for (
auto it = mPrevVisibility.constBegin(); it != mPrevVisibility.constEnd(); ++it )
93 it.key()->item()->setVisible( it.value() );
97 LayoutGuideHider(
const LayoutGuideHider &other ) =
delete;
98 LayoutGuideHider &operator=(
const LayoutGuideHider &other ) =
delete;
102 QHash< QgsLayoutGuide *, bool > mPrevVisibility;
108 explicit LayoutItemHider(
const QList<QGraphicsItem *> &items )
110 mItemsToIterate.reserve( items.count() );
111 for ( QGraphicsItem *item : items )
113 const bool isVisible = item->isVisible();
114 mPrevVisibility[item] = isVisible;
116 mItemsToIterate.append( item );
118 layoutItem->setProperty(
"wasVisible", isVisible );
126 for (
auto it = mPrevVisibility.constBegin(); it != mPrevVisibility.constEnd(); ++it )
134 for (
auto it = mPrevVisibility.constBegin(); it != mPrevVisibility.constEnd(); ++it )
136 it.key()->setVisible( it.value() );
138 layoutItem->setProperty(
"wasVisible", QVariant() );
142 QList< QGraphicsItem * > itemsToIterate()
const {
return mItemsToIterate; }
144 LayoutItemHider(
const LayoutItemHider &other ) =
delete;
145 LayoutItemHider &operator=(
const LayoutItemHider &other ) =
delete;
149 QList<QGraphicsItem * > mItemsToIterate;
150 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 ) ) )
227 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 ) ), QStringLiteral(
"Layout" ),
Qgis::MessageLevel::Warning );
235class 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;
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()
303 + imageSize.height() / region.height() ) / 2.0 * oneInchInLayoutUnits;
311 int width = imageSize.isValid() ? imageSize.width()
312 :
static_cast< int >( resolution * region.width() / oneInchInLayoutUnits );
313 int height = imageSize.isValid() ? imageSize.height()
314 :
static_cast< int >( resolution * region.height() / oneInchInLayoutUnits );
316 QImage image( QSize( width, height ), QImage::Format_ARGB32 );
317 if ( !image.isNull() )
320 if ( width > 32768 || height > 32768 )
321 QgsMessageLog::logMessage( QObject::tr(
"Error: output width or height is larger than 32768 pixel, result will be clipped" ) );
322 image.setDotsPerMeterX(
static_cast< int >( std::round( resolution / 25.4 * 1000 ) ) );
323 image.setDotsPerMeterY(
static_cast< int>( std::round( resolution / 25.4 * 1000 ) ) );
324 image.fill( Qt::transparent );
325 QPainter imagePainter( &image );
327 if ( !imagePainter.isActive() )
335class LayoutContextSettingsRestorer
340 LayoutContextSettingsRestorer(
QgsLayout *layout )
342 , mPreviousDpi( layout->renderContext().dpi() )
343 , mPreviousFlags( layout->renderContext().flags() )
344 , mPreviousTextFormat( layout->renderContext().textRenderFormat() )
345 , mPreviousExportLayer( layout->renderContext().currentExportLayer() )
346 , mPreviousSimplifyMethod( layout->renderContext().simplifyMethod() )
347 , mPreviousMaskSettings( layout->renderContext().maskSettings() )
348 , mExportThemes( layout->renderContext().exportThemes() )
349 , mPredefinedScales( layout->renderContext().predefinedScales() )
354 ~LayoutContextSettingsRestorer()
356 mLayout->renderContext().setDpi( mPreviousDpi );
357 mLayout->renderContext().setFlags( mPreviousFlags );
358 mLayout->renderContext().setTextRenderFormat( mPreviousTextFormat );
360 mLayout->renderContext().setCurrentExportLayer( mPreviousExportLayer );
362 mLayout->renderContext().setSimplifyMethod( mPreviousSimplifyMethod );
363 mLayout->renderContext().setMaskSettings( mPreviousMaskSettings );
364 mLayout->renderContext().setExportThemes( mExportThemes );
365 mLayout->renderContext().setPredefinedScales( mPredefinedScales );
368 LayoutContextSettingsRestorer(
const LayoutContextSettingsRestorer &other ) =
delete;
369 LayoutContextSettingsRestorer &operator=(
const LayoutContextSettingsRestorer &other ) =
delete;
373 double mPreviousDpi = 0;
376 int mPreviousExportLayer = 0;
379 QStringList mExportThemes;
380 QVector< double > mPredefinedScales;
391 if ( settings.
dpi <= 0 )
392 settings.
dpi = mLayout->renderContext().dpi();
394 mErrorFileName.clear();
396 int worldFilePageNo = -1;
399 worldFilePageNo = referenceMap->page();
402 QFileInfo fi( filePath );
404 if ( !dir.exists( fi.absolutePath() ) )
406 dir.mkpath( fi.absolutePath() );
411 pageDetails.
baseName = fi.completeBaseName();
414 LayoutContextPreviewSettingRestorer restorer( mLayout );
416 LayoutContextSettingsRestorer dpiRestorer( mLayout );
418 mLayout->renderContext().setDpi( settings.
dpi );
419 mLayout->renderContext().setFlags( settings.
flags );
423 if ( settings.
pages.empty() )
425 for (
int page = 0; page < mLayout->pageCollection()->pageCount(); ++page )
430 for (
int page : std::as_const( settings.
pages ) )
432 if ( page >= 0 && page < mLayout->pageCollection()->pageCount() )
437 for (
int page : std::as_const( pages ) )
439 if ( !mLayout->pageCollection()->shouldExportPage( page ) )
446 QImage image = createImage( settings, page, bounds, skip );
451 pageDetails.
page = page;
454 if ( image.isNull() )
456 mErrorFileName = outputFilePath;
462 mErrorFileName = outputFilePath;
466 const bool shouldGeoreference = ( page == worldFilePageNo );
467 if ( shouldGeoreference )
469 georeferenceOutputPrivate( outputFilePath,
nullptr, bounds, settings.
dpi, shouldGeoreference );
474 double a, b,
c, d, e, f;
475 if ( bounds.isValid() )
480 QFileInfo fi( outputFilePath );
482 QString outputSuffix = fi.suffix();
483 QString worldFileName = fi.absolutePath() +
'/' + fi.completeBaseName() +
'.'
484 + outputSuffix.at( 0 ) + outputSuffix.at( fi.suffix().size() - 1 ) +
'w';
486 writeWorldFile( worldFileName, a, b,
c, d, e, f );
491 captureLabelingResults();
502 int total = iterator->
count();
503 double step = total > 0 ? 100.0 / total : 100.0;
505 while ( iterator->
next() )
510 feedback->setProperty(
"progress", QObject::tr(
"Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
512 feedback->setProperty(
"progress", QObject::tr(
"Exporting section %1" ).arg( i + 1 ) );
522 QString filePath = iterator->
filePath( baseFilePath, extension );
527 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 ) );
547 if ( !mLayout || mLayout->pageCollection()->pageCount() == 0 )
551 if ( settings.
dpi <= 0 )
552 settings.
dpi = mLayout->renderContext().dpi();
554 mErrorFileName.clear();
556 LayoutContextPreviewSettingRestorer restorer( mLayout );
558 LayoutContextSettingsRestorer contextRestorer( mLayout );
559 ( void )contextRestorer;
560 mLayout->renderContext().setDpi( settings.
dpi );
562 mLayout->renderContext().setMaskSettings( createExportMaskSettings() );
566 mLayout->renderContext().setSimplifyMethod( createExportSimplifyMethod() );
569 std::unique_ptr< QgsLayoutGeospatialPdfExporter > geospatialPdfExporter;
571 geospatialPdfExporter = std::make_unique< QgsLayoutGeospatialPdfExporter >( mLayout );
573 mLayout->renderContext().setFlags( settings.
flags );
586 mLayout->renderContext().setExportThemes( settings.
exportThemes );
598 const QList<QGraphicsItem *> items = mLayout->items( Qt::AscendingOrder );
600 QList< QgsLayoutGeospatialPdfExporter::ComponentLayerDetail > pdfComponents;
605 QSet<QString> mutuallyExclusiveGroups;
611 component.
name = layerDetail.name;
612 component.
mapLayerId = layerDetail.mapLayerId;
613 component.
opacity = layerDetail.opacity;
615 component.
group = layerDetail.groupName;
616 if ( !layerDetail.mapTheme.isEmpty() )
618 component.
group = layerDetail.mapTheme;
619 mutuallyExclusiveGroups.insert( layerDetail.mapTheme );
622 component.
sourcePdfPath = settings.writeGeoPdf ? geospatialPdfExporter->generateTemporaryFilepath( QStringLiteral(
"layer_%1.pdf" ).arg( layerId ) ) : baseDir.filePath( QStringLiteral(
"%1_%2.pdf" ).arg( baseFileName ).arg( layerId, 4, 10, QChar(
'0' ) ) );
623 pdfComponents << component;
625 preparePrintAsPdf( mLayout, &printer, component.
sourcePdfPath );
626 preparePrint( mLayout, &printer,
false );
628 if ( !p.begin( &printer ) )
636 return layerExportResult;
638 auto getExportGroupNameFunc = [](
QgsLayoutItem * item )->QString
640 return item->customProperty( QStringLiteral(
"pdfExportGroup" ) ).toString();
642 result = handleLayeredExport( items, exportFunc, getExportGroupNameFunc );
646 if ( settings.writeGeoPdf )
649 details.
dpi = settings.dpi;
651 QgsLayoutSize pageSize = mLayout->pageCollection()->page( 0 )->sizeWithUnits();
656 if ( settings.exportMetadata )
659 details.
author = mLayout->project()->metadata().author();
661 details.
creator = getCreator();
662 details.
creationDateTime = mLayout->project()->metadata().creationDateTime();
663 details.
subject = mLayout->project()->metadata().abstract();
664 details.
title = mLayout->project()->metadata().title();
665 details.
keywords = mLayout->project()->metadata().keywords();
668 const QList< QgsMapLayer * > layers = mLayout->project()->mapLayers().values();
674 if ( settings.appendGeoreference )
677 QList< QgsLayoutItemMap * > maps;
678 mLayout->layoutItems( maps );
682 georef.
crs = map->crs();
684 const QPointF topLeft = map->mapToScene( QPointF( 0, 0 ) );
685 const QPointF topRight = map->mapToScene( QPointF( map->rect().width(), 0 ) );
686 const QPointF bottomLeft = map->mapToScene( QPointF( 0, map->rect().height() ) );
687 const QPointF bottomRight = map->mapToScene( QPointF( map->rect().width(), map->rect().height() ) );
700 const QTransform t = map->layoutToMapCoordsTransform();
701 const QgsPointXY topLeftMap = t.map( topLeft );
702 const QgsPointXY topRightMap = t.map( topRight );
703 const QgsPointXY bottomLeftMap = t.map( bottomLeft );
704 const QgsPointXY bottomRightMap = t.map( bottomRight );
716 details.
layerOrder = geospatialPdfExporter->layerOrder();
722 if ( !geospatialPdfExporter->finalize( pdfComponents, filePath, details ) )
725 mErrorMessage = geospatialPdfExporter->errorMessage();
735 QPdfWriter printer = QPdfWriter( filePath );
736 preparePrintAsPdf( mLayout, &printer, filePath );
737 preparePrint( mLayout, &printer,
false );
739 if ( !p.begin( &printer ) )
748 bool shouldAppendGeoreference = settings.
appendGeoreference && mLayout && mLayout->referenceMap() && mLayout->referenceMap()->page() == 0;
751 georeferenceOutputPrivate( filePath,
nullptr, QRectF(), settings.
dpi, shouldAppendGeoreference, settings.
exportMetadata );
754 captureLabelingResults();
767 QPdfWriter printer = QPdfWriter( fileName );
770 int total = iterator->
count();
771 double step = total > 0 ? 100.0 / total : 100.0;
774 while ( iterator->
next() )
779 feedback->setProperty(
"progress", QObject::tr(
"Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
781 feedback->setProperty(
"progress", QObject::tr(
"Exporting section %1" ).arg( i + 1 ) );
793 LayoutContextPreviewSettingRestorer restorer( iterator->
layout() );
795 LayoutContextSettingsRestorer contextRestorer( iterator->
layout() );
796 ( void )contextRestorer;
819 preparePrintAsPdf( iterator->
layout(), &printer, fileName );
820 preparePrint( iterator->
layout(), &printer,
false );
822 if ( !p.begin( &printer ) )
836 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 ) );
863 int total = iterator->
count();
864 double step = total > 0 ? 100.0 / total : 100.0;
866 while ( iterator->
next() )
871 feedback->setProperty(
"progress", QObject::tr(
"Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
873 feedback->setProperty(
"progress", QObject::tr(
"Exporting section %1" ).arg( i + 1 ) );
882 QString filePath = iterator->
filePath( baseFilePath, QStringLiteral(
"pdf" ) );
889 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 ) );
907#if defined( HAVE_QTPRINTER )
914 if ( settings.
dpi <= 0 )
915 settings.
dpi = mLayout->renderContext().dpi();
917 mErrorFileName.clear();
919 LayoutContextPreviewSettingRestorer restorer( mLayout );
921 LayoutContextSettingsRestorer contextRestorer( mLayout );
922 ( void )contextRestorer;
923 mLayout->renderContext().setDpi( settings.
dpi );
925 mLayout->renderContext().setFlags( settings.
flags );
932 preparePrint( mLayout, &printer,
true );
934 if ( !p.begin( &printer ) )
943 captureLabelingResults();
954 PrintExportSettings settings = s;
958 int total = iterator->
count();
959 double step = total > 0 ? 100.0 / total : 100.0;
962 while ( iterator->
next() )
967 feedback->setProperty(
"progress", QObject::tr(
"Printing %1 of %2" ).arg( i + 1 ).arg( total ) );
969 feedback->setProperty(
"progress", QObject::tr(
"Printing section %1" ).arg( i + 1 ) );
981 LayoutContextPreviewSettingRestorer restorer( iterator->
layout() );
983 LayoutContextSettingsRestorer contextRestorer( iterator->
layout() );
984 ( void )contextRestorer;
997 preparePrint( iterator->
layout(), &printer,
true );
999 if ( !p.begin( &printer ) )
1009 ExportResult result = exporter.printPrivate( &printer, p, !first, settings.dpi, settings.rasterizeWholeImage );
1013 error = exporter.errorMessage();
1036 if ( settings.
dpi <= 0 )
1037 settings.
dpi = mLayout->renderContext().dpi();
1039 mErrorFileName.clear();
1041 LayoutContextPreviewSettingRestorer restorer( mLayout );
1043 LayoutContextSettingsRestorer contextRestorer( mLayout );
1044 ( void )contextRestorer;
1045 mLayout->renderContext().setDpi( settings.
dpi );
1047 mLayout->renderContext().setFlags( settings.
flags );
1051 mLayout->renderContext().setMaskSettings( createExportMaskSettings() );
1055 mLayout->renderContext().setSimplifyMethod( createExportSimplifyMethod() );
1058 QFileInfo fi( filePath );
1061 pageDetails.
baseName = fi.baseName();
1062 pageDetails.
extension = fi.completeSuffix();
1066 for (
int i = 0; i < mLayout->pageCollection()->pageCount(); ++i )
1068 if ( !mLayout->pageCollection()->shouldExportPage( i ) )
1073 pageDetails.
page = i;
1080 if ( mLayout->pageCollection()->pageCount() == 1 )
1083 bounds = mLayout->layoutBounds(
true );
1088 bounds = mLayout->pageItemBounds( i,
true );
1097 bounds = QRectF( pageItem->pos().x(), pageItem->pos().y(), pageItem->rect().width(), pageItem->rect().height() );
1101 int width =
static_cast< int >( bounds.width() * settings.
dpi / inchesToLayoutUnits );
1103 int height =
static_cast< int >( bounds.height() * settings.
dpi / inchesToLayoutUnits );
1104 if ( width == 0 || height == 0 )
1113 const QRectF paperRect = QRectF( pageItem->pos().x(),
1114 pageItem->pos().y(),
1115 pageItem->rect().width(),
1116 pageItem->rect().height() );
1118 QDomNode svgDocRoot;
1119 const QList<QGraphicsItem *> items = mLayout->items( paperRect,
1120 Qt::IntersectsItemBoundingRect,
1121 Qt::AscendingOrder );
1125 return renderToLayeredSvg( settings, width, height, i, bounds, fileName, layerId, layerDetail.name, svg, svgDocRoot, settings.
exportMetadata );
1127 auto getExportGroupNameFunc = [](
QgsLayoutItem * )->QString
1131 ExportResult res = handleLayeredExport( items, exportFunc, getExportGroupNameFunc );
1136 appendMetadataToSvg( svg );
1138 QFile out( fileName );
1139 bool openOk = out.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate );
1142 mErrorFileName = fileName;
1146 out.write( svg.toByteArray() );
1152 QSvgGenerator generator;
1155 generator.setTitle( mLayout->project()->metadata().title() );
1156 generator.setDescription( mLayout->project()->metadata().abstract() );
1158 generator.setOutputDevice( &svgBuffer );
1159 generator.setSize( QSize( width, height ) );
1160 generator.setViewBox( QRect( 0, 0, width, height ) );
1161 generator.setResolution(
static_cast< int >( std::round( settings.
dpi ) ) );
1164 bool createOk = p.begin( &generator );
1167 mErrorFileName = fileName;
1180 svgBuffer.open( QIODevice::ReadOnly );
1184 if ( ! svg.setContent( &svgBuffer,
false, &errorMsg, &errorLine ) )
1186 mErrorFileName = fileName;
1191 appendMetadataToSvg( svg );
1193 QFile out( fileName );
1194 bool openOk = out.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate );
1197 mErrorFileName = fileName;
1201 out.write( svg.toByteArray() );
1205 captureLabelingResults();
1216 int total = iterator->
count();
1217 double step = total > 0 ? 100.0 / total : 100.0;
1219 while ( iterator->
next() )
1224 feedback->setProperty(
"progress", QObject::tr(
"Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
1226 feedback->setProperty(
"progress", QObject::tr(
"Exporting section %1" ).arg( i + 1 ) );
1236 QString filePath = iterator->
filePath( baseFilePath, QStringLiteral(
"svg" ) );
1243 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 ) );
1264 return mLabelingResults;
1269 QMap<QString, QgsLabelingResults *> res;
1270 std::swap( mLabelingResults, res );
1274void QgsLayoutExporter::preparePrintAsPdf(
QgsLayout *layout, QPdfWriter *device,
const QString &filePath )
1276 QFileInfo fi( filePath );
1278 if ( !dir.exists( fi.absolutePath() ) )
1280 dir.mkpath( fi.absolutePath() );
1283 updatePrinterPageSize(
layout, device, firstPageToBeExported(
layout ) );
1289 device->setTitle( title );
1291 QPagedPaintDevice::PdfVersion pdfVersion = QPagedPaintDevice::PdfVersion_1_4;
1293#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
1299 switch ( styleSettings->colorModel() )
1302 device->setColorModel( QPdfWriter::ColorModel::CMYK );
1306 device->setColorModel( QPdfWriter::ColorModel::RGB );
1310 const QColorSpace colorSpace = styleSettings->colorSpace();
1311 if ( colorSpace.isValid() )
1313 QPdfOutputIntent outputIntent;
1314 outputIntent.setOutputProfile( colorSpace );
1315 outputIntent.setOutputCondition( colorSpace.description() );
1319 outputIntent.setOutputConditionIdentifier( QStringLiteral(
"Unknown identifier" ) );
1320 outputIntent.setRegistryName( QStringLiteral(
"Unknown registry" ) );
1321 device->setOutputIntent( outputIntent );
1324 pdfVersion = QPagedPaintDevice::PdfVersion_X4;
1330 device->setPdfVersion( pdfVersion );
1331 setXmpMetadata( device,
layout );
1337#if defined(HAS_KDE_QT5_PDF_TRANSFORM_FIX) || QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)
1344void QgsLayoutExporter::preparePrint(
QgsLayout *layout, QPagedPaintDevice *device,
bool setFirstPageSize )
1346 if ( QPdfWriter *pdf =
dynamic_cast<QPdfWriter *
>( device ) )
1350#if defined( HAVE_QTPRINTER )
1351 else if ( QPrinter *printer =
dynamic_cast<QPrinter *
>( device ) )
1353 printer->setFullPage(
true );
1354 printer->setColorMode( QPrinter::Color );
1360 if ( setFirstPageSize )
1362 updatePrinterPageSize(
layout, device, firstPageToBeExported(
layout ) );
1368 if ( mLayout->pageCollection()->pageCount() == 0 )
1371 preparePrint( mLayout, device,
true );
1373 if ( !p.begin( device ) )
1379 printPrivate( device, p );
1384QgsLayoutExporter::ExportResult QgsLayoutExporter::printPrivate( QPagedPaintDevice *device, QPainter &painter,
bool startNewPage,
double dpi,
bool rasterize )
1388 int toPage = mLayout->pageCollection()->pageCount() - 1;
1390#if defined( HAVE_QTPRINTER )
1391 if ( QPrinter *printer =
dynamic_cast<QPrinter *
>( device ) )
1393 if ( printer->fromPage() >= 1 )
1394 fromPage = printer->fromPage() - 1;
1395 if ( printer->toPage() >= 1 )
1396 toPage = printer->toPage() - 1;
1400 bool pageExported =
false;
1403 for (
int i = fromPage; i <= toPage; ++i )
1405 if ( !mLayout->pageCollection()->shouldExportPage( i ) )
1410 updatePrinterPageSize( mLayout, device, i );
1411 if ( ( pageExported && i > fromPage ) || startNewPage )
1417 if ( !image.isNull() )
1419 QRectF targetArea( 0, 0, image.width(), image.height() );
1420 painter.drawImage( targetArea, image, targetArea );
1426 pageExported =
true;
1431 for (
int i = fromPage; i <= toPage; ++i )
1433 if ( !mLayout->pageCollection()->shouldExportPage( i ) )
1438 updatePrinterPageSize( mLayout, device, i );
1440 if ( ( pageExported && i > fromPage ) || startNewPage )
1445 pageExported =
true;
1451void QgsLayoutExporter::updatePrinterPageSize(
QgsLayout *layout, QPagedPaintDevice *device,
int page )
1456 QPageLayout pageLayout( QPageSize( pageSizeMM.
toQSizeF(), QPageSize::Millimeter ),
1457 QPageLayout::Portrait,
1458 QMarginsF( 0, 0, 0, 0 ) );
1459 pageLayout.setMode( QPageLayout::FullPageMode );
1460 device->setPageLayout( pageLayout );
1461 device->setPageMargins( QMarginsF( 0, 0, 0, 0 ) );
1463#if defined( HAVE_QTPRINTER )
1464 if ( QPrinter *printer =
dynamic_cast<QPrinter *
>( device ) )
1466 printer->setFullPage(
true );
1471QgsLayoutExporter::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
1475 QSvgGenerator generator;
1476 if ( includeMetadata )
1479 generator.setTitle( l->name() );
1480 else if ( mLayout->project() )
1481 generator.setTitle( mLayout->project()->title() );
1484 generator.setOutputDevice( &svgBuffer );
1485 generator.setSize( QSize(
static_cast< int >( std::round( width ) ),
1486 static_cast< int >( std::round( height ) ) ) );
1487 generator.setViewBox( QRect( 0, 0,
1488 static_cast< int >( std::round( width ) ),
1489 static_cast< int >( std::round( height ) ) ) );
1490 generator.setResolution(
static_cast< int >( std::round( settings.dpi ) ) );
1492 QPainter svgPainter( &generator );
1493 if ( settings.cropToContents )
1504 svgBuffer.open( QIODevice::ReadOnly );
1508 if ( ! doc.setContent( &svgBuffer,
false, &errorMsg, &errorLine ) )
1510 mErrorFileName = filename;
1513 if ( 1 == svgLayerId )
1515 svg = QDomDocument( doc.doctype() );
1516 svg.appendChild( svg.importNode( doc.firstChild(),
false ) );
1517 svgDocRoot = svg.importNode( doc.elementsByTagName( QStringLiteral(
"svg" ) ).at( 0 ),
false );
1518 svgDocRoot.toElement().setAttribute( QStringLiteral(
"xmlns:inkscape" ), QStringLiteral(
"http://www.inkscape.org/namespaces/inkscape" ) );
1519 svg.appendChild( svgDocRoot );
1521 QDomNode mainGroup = svg.importNode( doc.elementsByTagName( QStringLiteral(
"g" ) ).at( 0 ),
true );
1522 mainGroup.toElement().setAttribute( QStringLiteral(
"id" ), layerName );
1523 mainGroup.toElement().setAttribute( QStringLiteral(
"inkscape:label" ), layerName );
1524 mainGroup.toElement().setAttribute( QStringLiteral(
"inkscape:groupmode" ), QStringLiteral(
"layer" ) );
1525 QDomNode defs = svg.importNode( doc.elementsByTagName( QStringLiteral(
"defs" ) ).at( 0 ),
true );
1526 svgDocRoot.appendChild( defs );
1527 svgDocRoot.appendChild( mainGroup );
1532void QgsLayoutExporter::appendMetadataToSvg( QDomDocument &svg )
const
1535 QDomElement metadataElement = svg.createElement( QStringLiteral(
"metadata" ) );
1536 QDomElement rdfElement = svg.createElement( QStringLiteral(
"rdf:RDF" ) );
1537 rdfElement.setAttribute( QStringLiteral(
"xmlns:rdf" ), QStringLiteral(
"http://www.w3.org/1999/02/22-rdf-syntax-ns#" ) );
1538 rdfElement.setAttribute( QStringLiteral(
"xmlns:rdfs" ), QStringLiteral(
"http://www.w3.org/2000/01/rdf-schema#" ) );
1539 rdfElement.setAttribute( QStringLiteral(
"xmlns:dc" ), QStringLiteral(
"http://purl.org/dc/elements/1.1/" ) );
1540 QDomElement descriptionElement = svg.createElement( QStringLiteral(
"rdf:Description" ) );
1541 QDomElement workElement = svg.createElement( QStringLiteral(
"cc:Work" ) );
1542 workElement.setAttribute( QStringLiteral(
"rdf:about" ), QString() );
1544 auto addTextNode = [&workElement, &descriptionElement, &svg](
const QString & tag,
const QString & value )
1547 QDomElement element = svg.createElement( tag );
1548 QDomText t = svg.createTextNode( value );
1549 element.appendChild( t );
1550 workElement.appendChild( element );
1553 descriptionElement.setAttribute( tag, value );
1556 addTextNode( QStringLiteral(
"dc:format" ), QStringLiteral(
"image/svg+xml" ) );
1557 addTextNode( QStringLiteral(
"dc:title" ), metadata.
title() );
1558 addTextNode( QStringLiteral(
"dc:date" ), metadata.
creationDateTime().toString( Qt::ISODate ) );
1559 addTextNode( QStringLiteral(
"dc:identifier" ), metadata.
identifier() );
1560 addTextNode( QStringLiteral(
"dc:description" ), metadata.
abstract() );
1562 auto addAgentNode = [&workElement, &descriptionElement, &svg](
const QString & tag,
const QString & value )
1565 QDomElement inkscapeElement = svg.createElement( tag );
1566 QDomElement agentElement = svg.createElement( QStringLiteral(
"cc:Agent" ) );
1567 QDomElement titleElement = svg.createElement( QStringLiteral(
"dc:title" ) );
1568 QDomText t = svg.createTextNode( value );
1569 titleElement.appendChild( t );
1570 agentElement.appendChild( titleElement );
1571 inkscapeElement.appendChild( agentElement );
1572 workElement.appendChild( inkscapeElement );
1575 QDomElement bagElement = svg.createElement( QStringLiteral(
"rdf:Bag" ) );
1576 QDomElement liElement = svg.createElement( QStringLiteral(
"rdf:li" ) );
1577 t = svg.createTextNode( value );
1578 liElement.appendChild( t );
1579 bagElement.appendChild( liElement );
1581 QDomElement element = svg.createElement( tag );
1582 element.appendChild( bagElement );
1583 descriptionElement.appendChild( element );
1586 addAgentNode( QStringLiteral(
"dc:creator" ), metadata.
author() );
1587 addAgentNode( QStringLiteral(
"dc:publisher" ), getCreator() );
1591 QDomElement element = svg.createElement( QStringLiteral(
"dc:subject" ) );
1592 QDomElement bagElement = svg.createElement( QStringLiteral(
"rdf:Bag" ) );
1594 for (
auto it = keywords.constBegin(); it != keywords.constEnd(); ++it )
1596 const QStringList words = it.value();
1597 for (
const QString &keyword : words )
1599 QDomElement liElement = svg.createElement( QStringLiteral(
"rdf:li" ) );
1600 QDomText t = svg.createTextNode( keyword );
1601 liElement.appendChild( t );
1602 bagElement.appendChild( liElement );
1605 element.appendChild( bagElement );
1606 workElement.appendChild( element );
1607 descriptionElement.appendChild( element );
1610 rdfElement.appendChild( descriptionElement );
1611 rdfElement.appendChild( workElement );
1612 metadataElement.appendChild( rdfElement );
1613 svg.documentElement().appendChild( metadataElement );
1614 svg.documentElement().setAttribute( QStringLiteral(
"xmlns:cc" ), QStringLiteral(
"http://creativecommons.org/ns#" ) );
1617std::unique_ptr<double[]> QgsLayoutExporter::computeGeoTransform(
const QgsLayoutItemMap *map,
const QRectF ®ion,
double dpi )
const
1620 map = mLayout->referenceMap();
1626 dpi = mLayout->renderContext().dpi();
1629 QRectF exportRegion = region;
1630 if ( !exportRegion.isValid() )
1632 int pageNumber = map->
page();
1635 double pageY = page->pos().y();
1636 QSizeF pageSize = page->rect().size();
1637 exportRegion = QRectF( 0, pageY, pageSize.width(), pageSize.height() );
1641 QRectF mapItemSceneRect = map->mapRectToScene( map->rect() );
1644 double outputHeightMM = exportRegion.height();
1645 double outputWidthMM = exportRegion.width();
1649 double mapXCenter = mapExtent.
center().
x();
1650 double mapYCenter = mapExtent.
center().
y();
1652 double sinAlpha = std::sin( alpha );
1653 double cosAlpha = std::cos( alpha );
1656 QPointF mapItemPos = map->pos();
1658 mapItemPos.rx() -= exportRegion.left();
1659 mapItemPos.ry() -= exportRegion.top();
1662 double xRatio = mapExtent.
width() / mapItemSceneRect.width();
1663 double yRatio = mapExtent.
height() / mapItemSceneRect.height();
1664 double xmin = mapExtent.
xMinimum() - mapItemPos.x() * xRatio;
1665 double ymax = mapExtent.
yMaximum() + mapItemPos.y() * yRatio;
1666 QgsRectangle paperExtent( xmin, ymax - outputHeightMM * yRatio, xmin + outputWidthMM * xRatio, ymax );
1669 double X0 = paperExtent.xMinimum();
1670 double Y0 = paperExtent.yMaximum();
1675 double X1 = X0 - mapXCenter;
1676 double Y1 = Y0 - mapYCenter;
1677 double X2 = X1 * cosAlpha + Y1 * sinAlpha;
1678 double Y2 = -X1 * sinAlpha + Y1 * cosAlpha;
1679 X0 = X2 + mapXCenter;
1680 Y0 = Y2 + mapYCenter;
1684 int pageWidthPixels =
static_cast< int >( dpi * outputWidthMM / 25.4 );
1685 int pageHeightPixels =
static_cast< int >( dpi * outputHeightMM / 25.4 );
1686 double pixelWidthScale = paperExtent.width() / pageWidthPixels;
1687 double pixelHeightScale = paperExtent.height() / pageHeightPixels;
1690 std::unique_ptr<double[]> t(
new double[6] );
1692 t[1] = cosAlpha * pixelWidthScale;
1693 t[2] = -sinAlpha * pixelWidthScale;
1695 t[4] = -sinAlpha * pixelHeightScale;
1696 t[5] = -cosAlpha * pixelHeightScale;
1701void QgsLayoutExporter::writeWorldFile(
const QString &worldFileName,
double a,
double b,
double c,
double d,
double e,
double f )
const
1703 QFile worldFile( worldFileName );
1704 if ( !worldFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
1708 QTextStream fout( &worldFile );
1712 fout << QString::number( a,
'f', 12 ) <<
"\r\n";
1713 fout << QString::number( d,
'f', 12 ) <<
"\r\n";
1714 fout << QString::number( b,
'f', 12 ) <<
"\r\n";
1715 fout << QString::number( e,
'f', 12 ) <<
"\r\n";
1716 fout << QString::number(
c,
'f', 12 ) <<
"\r\n";
1717 fout << QString::number( f,
'f', 12 ) <<
"\r\n";
1722 return georeferenceOutputPrivate( file, map, exportRegion, dpi,
false );
1725bool QgsLayoutExporter::georeferenceOutputPrivate(
const QString &file,
QgsLayoutItemMap *map,
const QRectF &exportRegion,
double dpi,
bool includeGeoreference,
bool includeMetadata )
const
1730 if ( !map && includeGeoreference )
1731 map = mLayout->referenceMap();
1733 std::unique_ptr<double[]> t;
1735 if ( map && includeGeoreference )
1738 dpi = mLayout->renderContext().dpi();
1740 t = computeGeoTransform( map, exportRegion, dpi );
1745 CPLSetConfigOption(
"GDAL_PDF_DPI", QString::number( dpi ).toUtf8().constData() );
1750 GDALSetGeoTransform( outputDS.get(), t.get() );
1752 if ( includeMetadata )
1754 QString creationDateString;
1755 const QDateTime creationDateTime = mLayout->project()->metadata().creationDateTime();
1756 if ( creationDateTime.isValid() )
1758 creationDateString = QStringLiteral(
"D:%1" ).arg( mLayout->project()->metadata().creationDateTime().toString( QStringLiteral(
"yyyyMMddHHmmss" ) ) );
1759 if ( creationDateTime.timeZone().isValid() )
1761 int offsetFromUtc = creationDateTime.timeZone().offsetFromUtc( creationDateTime );
1762 creationDateString += ( offsetFromUtc >= 0 ) ?
'+' :
'-';
1763 offsetFromUtc = std::abs( offsetFromUtc );
1764 int offsetHours = offsetFromUtc / 3600;
1765 int offsetMins = ( offsetFromUtc % 3600 ) / 60;
1766 creationDateString += QStringLiteral(
"%1'%2'" ).arg( offsetHours ).arg( offsetMins );
1769 GDALSetMetadataItem( outputDS.get(),
"CREATION_DATE", creationDateString.toUtf8().constData(),
nullptr );
1771 GDALSetMetadataItem( outputDS.get(),
"AUTHOR", mLayout->project()->metadata().author().toUtf8().constData(),
nullptr );
1772 const QString creator = getCreator();
1773 GDALSetMetadataItem( outputDS.get(),
"CREATOR", creator.toUtf8().constData(),
nullptr );
1774 GDALSetMetadataItem( outputDS.get(),
"PRODUCER", creator.toUtf8().constData(),
nullptr );
1775 GDALSetMetadataItem( outputDS.get(),
"SUBJECT", mLayout->project()->metadata().abstract().toUtf8().constData(),
nullptr );
1776 GDALSetMetadataItem( outputDS.get(),
"TITLE", mLayout->project()->metadata().title().toUtf8().constData(),
nullptr );
1779 QStringList allKeywords;
1780 for (
auto it = keywords.constBegin(); it != keywords.constEnd(); ++it )
1782 allKeywords.append( QStringLiteral(
"%1: %2" ).arg( it.key(), it.value().join(
',' ) ) );
1784 const QString keywordString = allKeywords.join(
';' );
1785 GDALSetMetadataItem( outputDS.get(),
"KEYWORDS", keywordString.toUtf8().constData(),
nullptr );
1791 CPLSetConfigOption(
"GDAL_PDF_DPI",
nullptr );
1798 if ( items.count() == 1 )
1802 QString name = layoutItem->displayName();
1804 if ( name.startsWith(
'<' ) && name.endsWith(
'>' ) )
1805 name = name.mid( 1, name.length() - 2 );
1809 else if ( items.count() > 1 )
1811 QStringList currentLayerItemTypes;
1812 for ( QGraphicsItem *item : items )
1818 if ( !currentLayerItemTypes.contains( itemType ) && !currentLayerItemTypes.contains( itemTypePlural ) )
1819 currentLayerItemTypes << itemType;
1820 else if ( currentLayerItemTypes.contains( itemType ) )
1822 currentLayerItemTypes.replace( currentLayerItemTypes.indexOf( itemType ), itemTypePlural );
1827 if ( !currentLayerItemTypes.contains( QObject::tr(
"Other" ) ) )
1828 currentLayerItemTypes.append( QObject::tr(
"Other" ) );
1831 return currentLayerItemTypes.join( QLatin1String(
", " ) );
1833 return QObject::tr(
"Layer %1" ).arg( layerId );
1838 const std::function<QString(
QgsLayoutItem *item )> &getItemExportGroupFunc )
1840 LayoutItemHider itemHider( items );
1845 QString previousItemGroup;
1846 unsigned int layerId = 1;
1848 itemHider.hideAll();
1849 const QList< QGraphicsItem * > itemsToIterate = itemHider.itemsToIterate();
1850 QList< QGraphicsItem * > currentLayerItems;
1851 for ( QGraphicsItem *item : itemsToIterate )
1855 bool canPlaceInExistingLayer =
false;
1856 QString thisItemExportGroupName;
1860 thisItemExportGroupName = getItemExportGroupFunc( layoutItem );
1861 if ( !thisItemExportGroupName.isEmpty() )
1863 if ( thisItemExportGroupName != previousItemGroup && !currentLayerItems.empty() )
1866 layerDetails.
groupName = thisItemExportGroupName;
1869 switch ( itemExportBehavior )
1873 switch ( prevItemBehavior )
1876 canPlaceInExistingLayer =
true;
1880 canPlaceInExistingLayer = prevType == -1 || prevType == layoutItem->
type();
1885 canPlaceInExistingLayer =
false;
1893 switch ( prevItemBehavior )
1897 canPlaceInExistingLayer = prevType == -1 || prevType == layoutItem->
type();
1902 canPlaceInExistingLayer =
false;
1910 canPlaceInExistingLayer =
false;
1915 canPlaceInExistingLayer =
false;
1918 prevItemBehavior = itemExportBehavior;
1919 prevType = layoutItem->
type();
1920 previousItemGroup = thisItemExportGroupName;
1925 previousItemGroup.clear();
1928 if ( canPlaceInExistingLayer )
1930 currentLayerItems << item;
1935 if ( !currentLayerItems.isEmpty() )
1939 ExportResult result = exportFunc( layerId, layerDetails );
1943 currentLayerItems.clear();
1946 itemHider.hideAll();
1951 int layoutItemLayerIdx = 0;
1953 mLayout->renderContext().setCurrentExportLayer( layoutItemLayerIdx );
1959 mLayout->renderContext().setCurrentExportLayer( layoutItemLayerIdx );
1963 ExportResult result = exportFunc( layerId, layerDetails );
1968 layoutItemLayerIdx++;
1970 layerDetails.mapLayerId.clear();
1972 mLayout->renderContext().setCurrentExportLayer( -1 );
1975 currentLayerItems.clear();
1979 currentLayerItems << item;
1981 layerDetails.groupName = thisItemExportGroupName;
1984 if ( !currentLayerItems.isEmpty() )
1987 ExportResult result = exportFunc( layerId, layerDetails );
2002 return simplifyMethod;
2025 int pageNumber = map->
page();
2027 double pageY = page->pos().y();
2028 QSizeF pageSize = page->rect().size();
2029 QRectF pageRect( 0, pageY, pageSize.width(), pageSize.height() );
2045 double destinationHeight = exportRegion.height();
2046 double destinationWidth = exportRegion.width();
2048 QRectF mapItemSceneRect = map->mapRectToScene( map->rect() );
2053 double xRatio = mapExtent.
width() / mapItemSceneRect.width();
2054 double yRatio = mapExtent.
height() / mapItemSceneRect.height();
2056 double xCenter = mapExtent.
center().
x();
2057 double yCenter = mapExtent.
center().
y();
2060 QPointF mapItemPos = map->pos();
2062 mapItemPos.rx() -= exportRegion.left();
2063 mapItemPos.ry() -= exportRegion.top();
2065 double xmin = mapExtent.
xMinimum() - mapItemPos.x() * xRatio;
2066 double ymax = mapExtent.
yMaximum() + mapItemPos.y() * yRatio;
2067 QgsRectangle paperExtent( xmin, ymax - destinationHeight * yRatio, xmin + destinationWidth * xRatio, ymax );
2069 double X0 = paperExtent.
xMinimum();
2070 double Y0 = paperExtent.
yMinimum();
2073 dpi = mLayout->renderContext().dpi();
2075 int widthPx =
static_cast< int >( dpi * destinationWidth / 25.4 );
2076 int heightPx =
static_cast< int >( dpi * destinationHeight / 25.4 );
2078 double Ww = paperExtent.
width() / widthPx;
2079 double Hh = paperExtent.
height() / heightPx;
2088 s[5] = Y0 + paperExtent.
height();
2092 r[0] = std::cos( alpha );
2093 r[1] = -std::sin( alpha );
2094 r[2] = xCenter * ( 1 - std::cos( alpha ) ) + yCenter * std::sin( alpha );
2095 r[3] = std::sin( alpha );
2096 r[4] = std::cos( alpha );
2097 r[5] = - xCenter * std::sin( alpha ) + yCenter * ( 1 - std::cos( alpha ) );
2100 a = r[0] * s[0] + r[1] * s[3];
2101 b = r[0] * s[1] + r[1] * s[4];
2102 c = r[0] * s[2] + r[1] * s[5] + r[2];
2103 d = r[3] * s[0] + r[4] * s[3];
2104 e = r[3] * s[1] + r[4] * s[4];
2105 f = r[3] * s[2] + r[4] * s[5] + r[5];
2113 QList< QgsLayoutItem *> items;
2119 if ( currentItem->isVisible() && currentItem->requiresRasterization() )
2130 QList< QgsLayoutItem *> items;
2136 if ( currentItem->isVisible() && currentItem->containsAdvancedEffects() )
2149 if ( mLayout->pageCollection()->pageCount() == 1 )
2152 bounds = mLayout->layoutBounds(
true );
2157 bounds = mLayout->pageItemBounds( page,
true );
2159 if ( bounds.width() <= 0 || bounds.height() <= 0 )
2167 bounds = bounds.adjusted( -settings.
cropMargins.
left() * pixelToLayoutUnits,
2179int QgsLayoutExporter::firstPageToBeExported(
QgsLayout *layout )
2182 for (
int i = 0; i < pageCount; ++i )
2196 if ( details.
page == 0 )
2206void QgsLayoutExporter::captureLabelingResults()
2208 qDeleteAll( mLabelingResults );
2209 mLabelingResults.clear();
2211 QList< QgsLayoutItemMap * > maps;
2212 mLayout->layoutItems( maps );
2216 mLabelingResults[ map->
uuid() ] = map->mExportLabelingResults.release();
2220bool QgsLayoutExporter::saveImage(
const QImage &image,
const QString &imageFilename,
const QString &imageFormat,
QgsProject *projectForMetadata,
int quality )
2222 QImageWriter w( imageFilename, imageFormat.toLocal8Bit().constData() );
2223 if ( imageFormat.compare( QLatin1String(
"tiff" ), Qt::CaseInsensitive ) == 0 || imageFormat.compare( QLatin1String(
"tif" ), Qt::CaseInsensitive ) == 0 )
2225 w.setCompression( 1 );
2229 w.setQuality( quality );
2231 if ( projectForMetadata )
2233 w.setText( QStringLiteral(
"Author" ), projectForMetadata->
metadata().
author() );
2234 const QString creator = getCreator();
2235 w.setText( QStringLiteral(
"Creator" ), creator );
2236 w.setText( QStringLiteral(
"Producer" ), creator );
2237 w.setText( QStringLiteral(
"Subject" ), projectForMetadata->
metadata().
abstract() );
2238 w.setText( QStringLiteral(
"Created" ), projectForMetadata->
metadata().
creationDateTime().toString( Qt::ISODate ) );
2239 w.setText( QStringLiteral(
"Title" ), projectForMetadata->
metadata().
title() );
2242 QStringList allKeywords;
2243 for (
auto it = keywords.constBegin(); it != keywords.constEnd(); ++it )
2245 allKeywords.append( QStringLiteral(
"%1: %2" ).arg( it.key(), it.value().join(
',' ) ) );
2247 const QString keywordString = allKeywords.join(
';' );
2248 w.setText( QStringLiteral(
"Keywords" ), keywordString );
2250 return w.write( image );
2253QString QgsLayoutExporter::getCreator()
2258void QgsLayoutExporter::setXmpMetadata( QPdfWriter *pdfWriter,
QgsLayout *layout )
2260#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
2261 QUuid documentId = pdfWriter->documentId();
2263 QUuid documentId = QUuid::createUuid();
2268 const QString metaDataDate = creationDateTime.isValid() ? creationDateTime.toOffsetFromUtc( creationDateTime.offsetFromUtc() ).toString( Qt::ISODate ) : QString();
2269 const QString title = pdfWriter->title();
2270 const QString creator = getCreator();
2271 const QString producer = creator;
2276 const QLatin1String xmlNS(
"http://www.w3.org/XML/1998/namespace" );
2277 const QLatin1String adobeNS(
"adobe:ns:meta/" );
2278 const QLatin1String rdfNS(
"http://www.w3.org/1999/02/22-rdf-syntax-ns#" );
2279 const QLatin1String dcNS(
"http://purl.org/dc/elements/1.1/" );
2280 const QLatin1String xmpNS(
"http://ns.adobe.com/xap/1.0/" );
2281 const QLatin1String xmpMMNS(
"http://ns.adobe.com/xap/1.0/mm/" );
2282 const QLatin1String pdfNS(
"http://ns.adobe.com/pdf/1.3/" );
2283 const QLatin1String pdfaidNS(
"http://www.aiim.org/pdfa/ns/id/" );
2284 const QLatin1String pdfxidNS(
"http://www.npes.org/pdfx/ns/id/" );
2286 QByteArray xmpMetadata;
2287 QBuffer output( &xmpMetadata );
2288 output.open( QIODevice::WriteOnly );
2289 output.write(
"<?xpacket begin='' ?>" );
2291 QXmlStreamWriter w( &output );
2292 w.setAutoFormatting(
true );
2293 w.writeNamespace( adobeNS,
"x" );
2294 w.writeNamespace( rdfNS,
"rdf" );
2295 w.writeNamespace( dcNS,
"dc" );
2296 w.writeNamespace( xmpNS,
"xmp" );
2297 w.writeNamespace( xmpMMNS,
"xmpMM" );
2298 w.writeNamespace( pdfNS,
"pdf" );
2299 w.writeNamespace( pdfaidNS,
"pdfaid" );
2300 w.writeNamespace( pdfxidNS,
"pdfxid" );
2302 w.writeStartElement( adobeNS,
"xmpmeta" );
2303 w.writeStartElement( rdfNS,
"RDF" );
2306 w.writeStartElement( rdfNS,
"Description" );
2307 w.writeAttribute( rdfNS,
"about",
"" );
2308 w.writeStartElement( dcNS,
"title" );
2309 w.writeStartElement( rdfNS,
"Alt" );
2310 w.writeStartElement( rdfNS,
"li" );
2311 w.writeAttribute( xmlNS,
"lang",
"x-default" );
2312 w.writeCharacters( title );
2313 w.writeEndElement();
2314 w.writeEndElement();
2315 w.writeEndElement();
2317 w.writeStartElement( dcNS,
"creator" );
2318 w.writeStartElement( rdfNS,
"Seq" );
2319 w.writeStartElement( rdfNS,
"li" );
2320 w.writeCharacters( author );
2321 w.writeEndElement();
2322 w.writeEndElement();
2323 w.writeEndElement();
2325 w.writeEndElement();
2328 w.writeStartElement( rdfNS,
"Description" );
2329 w.writeAttribute( rdfNS,
"about",
"" );
2330 w.writeAttribute( pdfNS,
"Producer", producer );
2331 w.writeAttribute( pdfNS,
"Trapped",
"False" );
2332 w.writeEndElement();
2335 w.writeStartElement( rdfNS,
"Description" );
2336 w.writeAttribute( rdfNS,
"about",
"" );
2337 w.writeAttribute( xmpNS,
"CreatorTool", creator );
2338 w.writeAttribute( xmpNS,
"CreateDate", metaDataDate );
2339 w.writeAttribute( xmpNS,
"ModifyDate", metaDataDate );
2340 w.writeAttribute( xmpNS,
"MetadataDate", metaDataDate );
2341 w.writeEndElement();
2344 w.writeStartElement( rdfNS,
"Description" );
2345 w.writeAttribute( rdfNS,
"about",
"" );
2346 w.writeAttribute( xmpMMNS,
"DocumentID",
"uuid:" + documentId.toString( QUuid::WithoutBraces ) );
2347 w.writeAttribute( xmpMMNS,
"VersionID",
"1" );
2348 w.writeAttribute( xmpMMNS,
"RenditionClass",
"default" );
2349 w.writeEndElement();
2351#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
2354 switch ( pdfWriter->pdfVersion() )
2356 case QPagedPaintDevice::PdfVersion_1_4:
2357 case QPagedPaintDevice::PdfVersion_A1b:
2358 case QPagedPaintDevice::PdfVersion_1_6:
2360 case QPagedPaintDevice::PdfVersion_X4:
2361 w.writeStartElement( rdfNS,
"Description" );
2362 w.writeAttribute( rdfNS,
"about",
"" );
2363 w.writeAttribute( pdfxidNS,
"GTS_PDFXVersion",
"PDF/X-4" );
2364 w.writeEndElement();
2370 w.writeEndElement();
2371 w.writeEndElement();
2373 w.writeEndDocument();
2374 output.write(
"<?xpacket end='w'?>" );
2376 pdfWriter->setDocumentXmpMetadata( xmpMetadata );
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.
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.
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.
Handles rendering and exports of layouts to various formats.
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.
Contains the configuration for a single snap guide used by a layout.
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.
QgsLayoutSize sizeWithUnits() const
Returns the item's current size, including units.
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.
QgsLayoutMeasurement convert(QgsLayoutMeasurement measurement, Qgis::LayoutUnit targetUnits) const
Converts a measurement from one unit to another.
This class provides a method of storing measurements for use in QGIS layouts using a variety of diffe...
int pageCount() const
Returns the number of pages in the collection.
bool shouldExportPage(int page) const
Returns whether the specified page number should be included in exports of the layouts.
QgsLayoutItemPage * page(int pageNumber)
Returns a specific page (by pageNumber) from the collection.
This class provides a method of storing points, consisting of an x and y coordinate,...
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.
QgsLayoutRenderContext::Flags flags() const
Returns the current combination of flags used for rendering the layout.
void setFlag(QgsLayoutRenderContext::Flag flag, bool on=true)
Enables or disables a particular rendering flag for the layout.
double dpi() const
Returns the dpi for outputting the layout.
@ FlagRenderLabelsByMapLayer
When rendering map items to multi-layered exports, render labels belonging to different layers into s...
@ FlagUseAdvancedEffects
Enable advanced effects such as blend modes.
@ FlagLosslessImageRendering
Render images losslessly whenever possible, instead of the default lossy jpeg rendering used for some...
@ FlagAntialiasing
Use antialiasing when drawing items.
@ FlagSynchronousLegendGraphics
Query legend graphics synchronously.
@ FlagForceVectorOutput
Force output in vector format where possible, even if items require rasterization to keep their corre...
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(QgsLayoutRenderContext::Flags flags)
Sets the combination of flags that will be used for rendering the layout.
const QgsLayoutMeasurementConverter & measurementConverter() const
Returns the layout measurement converter to be used in the layout.
This class provides a method of storing sizes, consisting of a width and height, for use in QGIS layo...
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 ...
QgsLayoutPageCollection * pageCollection()
Returns a pointer to the layout's page collection, which stores and manages page items in the layout.
void layoutItems(QList< T * > &itemList) const
Returns a list of layout items of a specific type.
QgsProject * project() const
The project associated with the layout.
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...
Interface for master layout type objects, such as print layouts and reports.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
static void fixEngineFlags(QPaintEngine *engine)
A class to represent a 2D point.
void setExteriorRing(QgsCurve *ring) override
Sets the exterior ring of the polygon.
Contains settings and properties relating to how a QgsProject should handle styling.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
const QgsProjectStyleSettings * styleSettings() const
Returns the project's style settings, which contains settings and properties relating to how a QgsPro...
QgsProjectMetadata metadata
A rectangle specified with double values.
A boolean settings entry.
An integer settings entry.
static QgsSettingsTreeNode * sTreeLayout
This class contains information how to simplify 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)
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.
bool useOgcBestPracticeFormatGeoreferencing
true if OGC "best practice" format georeferencing should be used.
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...
QgsLayoutRenderContext::Flags flags
Layout context flags, which control how the export will be created.
int quality
Image quality, typically used for JPEG compression (whose quality ranges from 1 to 100) if quality is...
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.
QgsLayoutRenderContext::Flags 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.
double dpi
Resolution to export layout at. If dpi <= 0 the default layout dpi will be used.
QgsLayoutRenderContext::Flags flags
Layout context flags, which control how the export will be created.
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::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.
QgsLayoutRenderContext::Flags flags
Layout context flags, which control how the export will be created.
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.