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;
167 qDeleteAll( mLabelingResults );
180 if ( mLayout->pageCollection()->pageCount() <= page || page < 0 )
191 LayoutContextPreviewSettingRestorer restorer( mLayout );
194 QRectF paperRect = QRectF( pageItem->pos().x(), pageItem->pos().y(), pageItem->rect().width(), pageItem->rect().height() );
203 if ( mLayout->pageCollection()->pageCount() <= page || page < 0 )
214 LayoutContextPreviewSettingRestorer restorer( mLayout );
217 QRectF paperRect = QRectF( pageItem->pos().x(), pageItem->pos().y(), pageItem->rect().width(), pageItem->rect().height() );
219 const double imageAspectRatio =
static_cast< double >( imageSize.width() ) / imageSize.height();
220 const double paperAspectRatio = paperRect.width() / paperRect.height();
221 if ( imageSize.isValid() && ( !
qgsDoubleNear( imageAspectRatio, paperAspectRatio, 0.008 ) ) )
226 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 );
234class LayoutItemCacheSettingRestorer
238 LayoutItemCacheSettingRestorer(
QgsLayout *layout )
241 const QList< QGraphicsItem * > items = mLayout->items();
242 for ( QGraphicsItem *item : items )
244 mPrevCacheMode.insert( item, item->cacheMode() );
245 item->setCacheMode( QGraphicsItem::NoCache );
249 ~LayoutItemCacheSettingRestorer()
251 for (
auto it = mPrevCacheMode.constBegin(); it != mPrevCacheMode.constEnd(); ++it )
253 it.key()->setCacheMode( it.value() );
257 LayoutItemCacheSettingRestorer(
const LayoutItemCacheSettingRestorer &other ) =
delete;
258 LayoutItemCacheSettingRestorer &operator=(
const LayoutItemCacheSettingRestorer &other ) =
delete;
262 QHash< QGraphicsItem *, QGraphicsItem::CacheMode > mPrevCacheMode;
269 QPaintDevice *paintDevice = painter->device();
270 if ( !paintDevice || !mLayout )
275 LayoutItemCacheSettingRestorer cacheRestorer( mLayout );
276 ( void )cacheRestorer;
277 LayoutContextPreviewSettingRestorer restorer( mLayout );
279 LayoutGuideHider guideHider( mLayout );
284 mLayout->render( painter, QRectF( 0, 0, paintDevice->width(), paintDevice->height() ), region );
292 LayoutContextPreviewSettingRestorer restorer( mLayout );
295 double resolution = mLayout->renderContext().dpi();
297 if ( imageSize.isValid() )
301 resolution = ( imageSize.width() / region.width()
302 + imageSize.height() / region.height() ) / 2.0 * oneInchInLayoutUnits;
310 int width = imageSize.isValid() ? imageSize.width()
311 :
static_cast< int >( resolution * region.width() / oneInchInLayoutUnits );
312 int height = imageSize.isValid() ? imageSize.height()
313 :
static_cast< int >( resolution * region.height() / oneInchInLayoutUnits );
315 QImage image( QSize( width, height ), QImage::Format_ARGB32 );
316 if ( !image.isNull() )
319 if ( width > 32768 || height > 32768 )
320 QgsMessageLog::logMessage( QObject::tr(
"Error: output width or height is larger than 32768 pixel, result will be clipped" ) );
321 image.setDotsPerMeterX(
static_cast< int >( std::round( resolution / 25.4 * 1000 ) ) );
322 image.setDotsPerMeterY(
static_cast< int>( std::round( resolution / 25.4 * 1000 ) ) );
323 image.fill( Qt::transparent );
324 QPainter imagePainter( &image );
326 if ( !imagePainter.isActive() )
334class LayoutContextSettingsRestorer
339 LayoutContextSettingsRestorer(
QgsLayout *layout )
341 , mPreviousDpi( layout->renderContext().dpi() )
342 , mPreviousFlags( layout->renderContext().flags() )
343 , mPreviousTextFormat( layout->renderContext().textRenderFormat() )
344 , mPreviousExportLayer( layout->renderContext().currentExportLayer() )
345 , mPreviousSimplifyMethod( layout->renderContext().simplifyMethod() )
346 , mPreviousMaskSettings( layout->renderContext().maskSettings() )
347 , mExportThemes( layout->renderContext().exportThemes() )
348 , mPredefinedScales( layout->renderContext().predefinedScales() )
353 ~LayoutContextSettingsRestorer()
355 mLayout->renderContext().setDpi( mPreviousDpi );
356 mLayout->renderContext().setFlags( mPreviousFlags );
357 mLayout->renderContext().setTextRenderFormat( mPreviousTextFormat );
359 mLayout->renderContext().setCurrentExportLayer( mPreviousExportLayer );
361 mLayout->renderContext().setSimplifyMethod( mPreviousSimplifyMethod );
362 mLayout->renderContext().setMaskSettings( mPreviousMaskSettings );
363 mLayout->renderContext().setExportThemes( mExportThemes );
364 mLayout->renderContext().setPredefinedScales( mPredefinedScales );
367 LayoutContextSettingsRestorer(
const LayoutContextSettingsRestorer &other ) =
delete;
368 LayoutContextSettingsRestorer &operator=(
const LayoutContextSettingsRestorer &other ) =
delete;
372 double mPreviousDpi = 0;
375 int mPreviousExportLayer = 0;
378 QStringList mExportThemes;
379 QVector< double > mPredefinedScales;
390 if ( settings.
dpi <= 0 )
391 settings.
dpi = mLayout->renderContext().dpi();
393 mErrorFileName.clear();
395 int worldFilePageNo = -1;
398 worldFilePageNo = referenceMap->page();
401 QFileInfo fi( filePath );
403 if ( !dir.exists( fi.absolutePath() ) )
405 dir.mkpath( fi.absolutePath() );
410 pageDetails.
baseName = fi.completeBaseName();
413 LayoutContextPreviewSettingRestorer restorer( mLayout );
415 LayoutContextSettingsRestorer dpiRestorer( mLayout );
417 mLayout->renderContext().setDpi( settings.
dpi );
418 mLayout->renderContext().setFlags( settings.
flags );
422 if ( settings.
pages.empty() )
424 for (
int page = 0; page < mLayout->pageCollection()->pageCount(); ++page )
429 for (
int page : std::as_const( settings.
pages ) )
431 if ( page >= 0 && page < mLayout->pageCollection()->pageCount() )
436 for (
int page : std::as_const( pages ) )
438 if ( !mLayout->pageCollection()->shouldExportPage( page ) )
445 QImage image = createImage( settings, page, bounds, skip );
450 pageDetails.
page = page;
453 if ( image.isNull() )
455 mErrorFileName = outputFilePath;
459 if ( !saveImage( image, outputFilePath, pageDetails.
extension, settings.
exportMetadata ? mLayout->project() : nullptr ) )
461 mErrorFileName = outputFilePath;
465 const bool shouldGeoreference = ( page == worldFilePageNo );
466 if ( shouldGeoreference )
468 georeferenceOutputPrivate( outputFilePath,
nullptr, bounds, settings.
dpi, shouldGeoreference );
473 double a, b,
c, d, e, f;
474 if ( bounds.isValid() )
479 QFileInfo fi( outputFilePath );
481 QString outputSuffix = fi.suffix();
482 QString worldFileName = fi.absolutePath() +
'/' + fi.completeBaseName() +
'.'
483 + outputSuffix.at( 0 ) + outputSuffix.at( fi.suffix().size() - 1 ) +
'w';
485 writeWorldFile( worldFileName, a, b,
c, d, e, f );
490 captureLabelingResults();
501 int total = iterator->
count();
502 double step = total > 0 ? 100.0 / total : 100.0;
504 while ( iterator->
next() )
509 feedback->setProperty(
"progress", QObject::tr(
"Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
511 feedback->setProperty(
"progress", QObject::tr(
"Exporting section %1" ).arg( i + 1 ) );
521 QString filePath = iterator->
filePath( baseFilePath, extension );
526 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 ) );
546 if ( !mLayout || mLayout->pageCollection()->pageCount() == 0 )
550 if ( settings.
dpi <= 0 )
551 settings.
dpi = mLayout->renderContext().dpi();
553 mErrorFileName.clear();
555 LayoutContextPreviewSettingRestorer restorer( mLayout );
557 LayoutContextSettingsRestorer contextRestorer( mLayout );
558 ( void )contextRestorer;
559 mLayout->renderContext().setDpi( settings.
dpi );
561 mLayout->renderContext().setMaskSettings( createExportMaskSettings() );
565 mLayout->renderContext().setSimplifyMethod( createExportSimplifyMethod() );
568 std::unique_ptr< QgsLayoutGeospatialPdfExporter > geospatialPdfExporter;
570 geospatialPdfExporter = std::make_unique< QgsLayoutGeospatialPdfExporter >( mLayout );
572 mLayout->renderContext().setFlags( settings.
flags );
585 mLayout->renderContext().setExportThemes( settings.
exportThemes );
597 const QList<QGraphicsItem *> items = mLayout->items( Qt::AscendingOrder );
599 QList< QgsLayoutGeospatialPdfExporter::ComponentLayerDetail > pdfComponents;
604 QSet<QString> mutuallyExclusiveGroups;
610 component.
name = layerDetail.name;
611 component.
mapLayerId = layerDetail.mapLayerId;
612 component.
opacity = layerDetail.opacity;
614 component.
group = layerDetail.groupName;
615 if ( !layerDetail.mapTheme.isEmpty() )
617 component.
group = layerDetail.mapTheme;
618 mutuallyExclusiveGroups.insert( layerDetail.mapTheme );
621 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' ) ) );
622 pdfComponents << component;
624 preparePrintAsPdf( mLayout, &printer, component.
sourcePdfPath );
625 preparePrint( mLayout, &printer,
false );
627 if ( !p.begin( &printer ) )
635 return layerExportResult;
637 auto getExportGroupNameFunc = [](
QgsLayoutItem * item )->QString
639 return item->customProperty( QStringLiteral(
"pdfExportGroup" ) ).toString();
641 result = handleLayeredExport( items, exportFunc, getExportGroupNameFunc );
645 if ( settings.writeGeoPdf )
648 details.
dpi = settings.dpi;
650 QgsLayoutSize pageSize = mLayout->pageCollection()->page( 0 )->sizeWithUnits();
655 if ( settings.exportMetadata )
658 details.
author = mLayout->project()->metadata().author();
660 details.
creator = getCreator();
661 details.
creationDateTime = mLayout->project()->metadata().creationDateTime();
662 details.
subject = mLayout->project()->metadata().abstract();
663 details.
title = mLayout->project()->metadata().title();
664 details.
keywords = mLayout->project()->metadata().keywords();
667 const QList< QgsMapLayer * > layers = mLayout->project()->mapLayers().values();
673 if ( settings.appendGeoreference )
676 QList< QgsLayoutItemMap * > maps;
677 mLayout->layoutItems( maps );
681 georef.
crs = map->crs();
683 const QPointF topLeft = map->mapToScene( QPointF( 0, 0 ) );
684 const QPointF topRight = map->mapToScene( QPointF( map->rect().width(), 0 ) );
685 const QPointF bottomLeft = map->mapToScene( QPointF( 0, map->rect().height() ) );
686 const QPointF bottomRight = map->mapToScene( QPointF( map->rect().width(), map->rect().height() ) );
699 const QTransform t = map->layoutToMapCoordsTransform();
700 const QgsPointXY topLeftMap = t.map( topLeft );
701 const QgsPointXY topRightMap = t.map( topRight );
702 const QgsPointXY bottomLeftMap = t.map( bottomLeft );
703 const QgsPointXY bottomRightMap = t.map( bottomRight );
715 details.
layerOrder = geospatialPdfExporter->layerOrder();
721 if ( !geospatialPdfExporter->finalize( pdfComponents, filePath, details ) )
724 mErrorMessage = geospatialPdfExporter->errorMessage();
734 QPdfWriter printer = QPdfWriter( filePath );
735 preparePrintAsPdf( mLayout, &printer, filePath );
736 preparePrint( mLayout, &printer,
false );
738 if ( !p.begin( &printer ) )
747 bool shouldAppendGeoreference = settings.
appendGeoreference && mLayout && mLayout->referenceMap() && mLayout->referenceMap()->page() == 0;
750 georeferenceOutputPrivate( filePath,
nullptr, QRectF(), settings.
dpi, shouldAppendGeoreference, settings.
exportMetadata );
753 captureLabelingResults();
766 QPdfWriter printer = QPdfWriter( fileName );
769 int total = iterator->
count();
770 double step = total > 0 ? 100.0 / total : 100.0;
773 while ( iterator->
next() )
778 feedback->setProperty(
"progress", QObject::tr(
"Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
780 feedback->setProperty(
"progress", QObject::tr(
"Exporting section %1" ).arg( i + 1 ) );
792 LayoutContextPreviewSettingRestorer restorer( iterator->
layout() );
794 LayoutContextSettingsRestorer contextRestorer( iterator->
layout() );
795 ( void )contextRestorer;
818 preparePrintAsPdf( iterator->
layout(), &printer, fileName );
819 preparePrint( iterator->
layout(), &printer,
false );
821 if ( !p.begin( &printer ) )
835 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 ) );
862 int total = iterator->
count();
863 double step = total > 0 ? 100.0 / total : 100.0;
865 while ( iterator->
next() )
870 feedback->setProperty(
"progress", QObject::tr(
"Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
872 feedback->setProperty(
"progress", QObject::tr(
"Exporting section %1" ).arg( i + 1 ) );
881 QString filePath = iterator->
filePath( baseFilePath, QStringLiteral(
"pdf" ) );
888 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 ) );
906#if defined( HAVE_QTPRINTER )
913 if ( settings.
dpi <= 0 )
914 settings.
dpi = mLayout->renderContext().dpi();
916 mErrorFileName.clear();
918 LayoutContextPreviewSettingRestorer restorer( mLayout );
920 LayoutContextSettingsRestorer contextRestorer( mLayout );
921 ( void )contextRestorer;
922 mLayout->renderContext().setDpi( settings.
dpi );
924 mLayout->renderContext().setFlags( settings.
flags );
931 preparePrint( mLayout, &printer,
true );
933 if ( !p.begin( &printer ) )
942 captureLabelingResults();
953 PrintExportSettings settings = s;
957 int total = iterator->
count();
958 double step = total > 0 ? 100.0 / total : 100.0;
961 while ( iterator->
next() )
966 feedback->setProperty(
"progress", QObject::tr(
"Printing %1 of %2" ).arg( i + 1 ).arg( total ) );
968 feedback->setProperty(
"progress", QObject::tr(
"Printing section %1" ).arg( i + 1 ) );
980 LayoutContextPreviewSettingRestorer restorer( iterator->
layout() );
982 LayoutContextSettingsRestorer contextRestorer( iterator->
layout() );
983 ( void )contextRestorer;
996 preparePrint( iterator->
layout(), &printer,
true );
998 if ( !p.begin( &printer ) )
1008 ExportResult result = exporter.printPrivate( &printer, p, !first, settings.dpi, settings.rasterizeWholeImage );
1012 error = exporter.errorMessage();
1035 if ( settings.
dpi <= 0 )
1036 settings.
dpi = mLayout->renderContext().dpi();
1038 mErrorFileName.clear();
1040 LayoutContextPreviewSettingRestorer restorer( mLayout );
1042 LayoutContextSettingsRestorer contextRestorer( mLayout );
1043 ( void )contextRestorer;
1044 mLayout->renderContext().setDpi( settings.
dpi );
1046 mLayout->renderContext().setFlags( settings.
flags );
1050 mLayout->renderContext().setMaskSettings( createExportMaskSettings() );
1054 mLayout->renderContext().setSimplifyMethod( createExportSimplifyMethod() );
1057 QFileInfo fi( filePath );
1060 pageDetails.
baseName = fi.baseName();
1061 pageDetails.
extension = fi.completeSuffix();
1065 for (
int i = 0; i < mLayout->pageCollection()->pageCount(); ++i )
1067 if ( !mLayout->pageCollection()->shouldExportPage( i ) )
1072 pageDetails.
page = i;
1079 if ( mLayout->pageCollection()->pageCount() == 1 )
1082 bounds = mLayout->layoutBounds(
true );
1087 bounds = mLayout->pageItemBounds( i,
true );
1096 bounds = QRectF( pageItem->pos().x(), pageItem->pos().y(), pageItem->rect().width(), pageItem->rect().height() );
1100 int width =
static_cast< int >( bounds.width() * settings.
dpi / inchesToLayoutUnits );
1102 int height =
static_cast< int >( bounds.height() * settings.
dpi / inchesToLayoutUnits );
1103 if ( width == 0 || height == 0 )
1112 const QRectF paperRect = QRectF( pageItem->pos().x(),
1113 pageItem->pos().y(),
1114 pageItem->rect().width(),
1115 pageItem->rect().height() );
1117 QDomNode svgDocRoot;
1118 const QList<QGraphicsItem *> items = mLayout->items( paperRect,
1119 Qt::IntersectsItemBoundingRect,
1120 Qt::AscendingOrder );
1124 return renderToLayeredSvg( settings, width, height, i, bounds, fileName, layerId, layerDetail.name, svg, svgDocRoot, settings.
exportMetadata );
1126 auto getExportGroupNameFunc = [](
QgsLayoutItem * )->QString
1130 ExportResult res = handleLayeredExport( items, exportFunc, getExportGroupNameFunc );
1135 appendMetadataToSvg( svg );
1137 QFile out( fileName );
1138 bool openOk = out.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate );
1141 mErrorFileName = fileName;
1145 out.write( svg.toByteArray() );
1151 QSvgGenerator generator;
1154 generator.setTitle( mLayout->project()->metadata().title() );
1155 generator.setDescription( mLayout->project()->metadata().abstract() );
1157 generator.setOutputDevice( &svgBuffer );
1158 generator.setSize( QSize( width, height ) );
1159 generator.setViewBox( QRect( 0, 0, width, height ) );
1160 generator.setResolution(
static_cast< int >( std::round( settings.
dpi ) ) );
1163 bool createOk = p.begin( &generator );
1166 mErrorFileName = fileName;
1179 svgBuffer.open( QIODevice::ReadOnly );
1183 if ( ! svg.setContent( &svgBuffer,
false, &errorMsg, &errorLine ) )
1185 mErrorFileName = fileName;
1190 appendMetadataToSvg( svg );
1192 QFile out( fileName );
1193 bool openOk = out.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate );
1196 mErrorFileName = fileName;
1200 out.write( svg.toByteArray() );
1204 captureLabelingResults();
1215 int total = iterator->
count();
1216 double step = total > 0 ? 100.0 / total : 100.0;
1218 while ( iterator->
next() )
1223 feedback->setProperty(
"progress", QObject::tr(
"Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
1225 feedback->setProperty(
"progress", QObject::tr(
"Exporting section %1" ).arg( i + 1 ) );
1235 QString filePath = iterator->
filePath( baseFilePath, QStringLiteral(
"svg" ) );
1242 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 ) );
1263 return mLabelingResults;
1268 QMap<QString, QgsLabelingResults *> res;
1269 std::swap( mLabelingResults, res );
1273void QgsLayoutExporter::preparePrintAsPdf(
QgsLayout *layout, QPdfWriter *device,
const QString &filePath )
1275 QFileInfo fi( filePath );
1277 if ( !dir.exists( fi.absolutePath() ) )
1279 dir.mkpath( fi.absolutePath() );
1282 updatePrinterPageSize(
layout, device, firstPageToBeExported(
layout ) );
1288 device->setTitle( title );
1290 QPagedPaintDevice::PdfVersion pdfVersion = QPagedPaintDevice::PdfVersion_1_4;
1292#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
1298 switch ( styleSettings->colorModel() )
1301 device->setColorModel( QPdfWriter::ColorModel::CMYK );
1305 device->setColorModel( QPdfWriter::ColorModel::RGB );
1309 const QColorSpace colorSpace = styleSettings->colorSpace();
1310 if ( colorSpace.isValid() )
1312 QPdfOutputIntent outputIntent;
1313 outputIntent.setOutputProfile( colorSpace );
1314 outputIntent.setOutputCondition( colorSpace.description() );
1318 outputIntent.setOutputConditionIdentifier( QStringLiteral(
"Unknown identifier" ) );
1319 outputIntent.setRegistryName( QStringLiteral(
"Unknown registry" ) );
1320 device->setOutputIntent( outputIntent );
1323 pdfVersion = QPagedPaintDevice::PdfVersion_X4;
1329 device->setPdfVersion( pdfVersion );
1330 setXmpMetadata( device,
layout );
1336#if defined(HAS_KDE_QT5_PDF_TRANSFORM_FIX) || QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)
1343void QgsLayoutExporter::preparePrint(
QgsLayout *layout, QPagedPaintDevice *device,
bool setFirstPageSize )
1345 if ( QPdfWriter *pdf =
dynamic_cast<QPdfWriter *
>( device ) )
1349#if defined( HAVE_QTPRINTER )
1350 else if ( QPrinter *printer =
dynamic_cast<QPrinter *
>( device ) )
1352 printer->setFullPage(
true );
1353 printer->setColorMode( QPrinter::Color );
1359 if ( setFirstPageSize )
1361 updatePrinterPageSize(
layout, device, firstPageToBeExported(
layout ) );
1367 if ( mLayout->pageCollection()->pageCount() == 0 )
1370 preparePrint( mLayout, device,
true );
1372 if ( !p.begin( device ) )
1378 printPrivate( device, p );
1383QgsLayoutExporter::ExportResult QgsLayoutExporter::printPrivate( QPagedPaintDevice *device, QPainter &painter,
bool startNewPage,
double dpi,
bool rasterize )
1387 int toPage = mLayout->pageCollection()->pageCount() - 1;
1389#if defined( HAVE_QTPRINTER )
1390 if ( QPrinter *printer =
dynamic_cast<QPrinter *
>( device ) )
1392 if ( printer->fromPage() >= 1 )
1393 fromPage = printer->fromPage() - 1;
1394 if ( printer->toPage() >= 1 )
1395 toPage = printer->toPage() - 1;
1399 bool pageExported =
false;
1402 for (
int i = fromPage; i <= toPage; ++i )
1404 if ( !mLayout->pageCollection()->shouldExportPage( i ) )
1409 updatePrinterPageSize( mLayout, device, i );
1410 if ( ( pageExported && i > fromPage ) || startNewPage )
1416 if ( !image.isNull() )
1418 QRectF targetArea( 0, 0, image.width(), image.height() );
1419 painter.drawImage( targetArea, image, targetArea );
1425 pageExported =
true;
1430 for (
int i = fromPage; i <= toPage; ++i )
1432 if ( !mLayout->pageCollection()->shouldExportPage( i ) )
1437 updatePrinterPageSize( mLayout, device, i );
1439 if ( ( pageExported && i > fromPage ) || startNewPage )
1444 pageExported =
true;
1450void QgsLayoutExporter::updatePrinterPageSize(
QgsLayout *layout, QPagedPaintDevice *device,
int page )
1455 QPageLayout pageLayout( QPageSize( pageSizeMM.
toQSizeF(), QPageSize::Millimeter ),
1456 QPageLayout::Portrait,
1457 QMarginsF( 0, 0, 0, 0 ) );
1458 pageLayout.setMode( QPageLayout::FullPageMode );
1459 device->setPageLayout( pageLayout );
1460 device->setPageMargins( QMarginsF( 0, 0, 0, 0 ) );
1462#if defined( HAVE_QTPRINTER )
1463 if ( QPrinter *printer =
dynamic_cast<QPrinter *
>( device ) )
1465 printer->setFullPage(
true );
1470QgsLayoutExporter::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
1474 QSvgGenerator generator;
1475 if ( includeMetadata )
1478 generator.setTitle( l->name() );
1479 else if ( mLayout->project() )
1480 generator.setTitle( mLayout->project()->title() );
1483 generator.setOutputDevice( &svgBuffer );
1484 generator.setSize( QSize(
static_cast< int >( std::round( width ) ),
1485 static_cast< int >( std::round( height ) ) ) );
1486 generator.setViewBox( QRect( 0, 0,
1487 static_cast< int >( std::round( width ) ),
1488 static_cast< int >( std::round( height ) ) ) );
1489 generator.setResolution(
static_cast< int >( std::round( settings.dpi ) ) );
1491 QPainter svgPainter( &generator );
1492 if ( settings.cropToContents )
1503 svgBuffer.open( QIODevice::ReadOnly );
1507 if ( ! doc.setContent( &svgBuffer,
false, &errorMsg, &errorLine ) )
1509 mErrorFileName = filename;
1512 if ( 1 == svgLayerId )
1514 svg = QDomDocument( doc.doctype() );
1515 svg.appendChild( svg.importNode( doc.firstChild(),
false ) );
1516 svgDocRoot = svg.importNode( doc.elementsByTagName( QStringLiteral(
"svg" ) ).at( 0 ),
false );
1517 svgDocRoot.toElement().setAttribute( QStringLiteral(
"xmlns:inkscape" ), QStringLiteral(
"http://www.inkscape.org/namespaces/inkscape" ) );
1518 svg.appendChild( svgDocRoot );
1520 QDomNode mainGroup = svg.importNode( doc.elementsByTagName( QStringLiteral(
"g" ) ).at( 0 ),
true );
1521 mainGroup.toElement().setAttribute( QStringLiteral(
"id" ), layerName );
1522 mainGroup.toElement().setAttribute( QStringLiteral(
"inkscape:label" ), layerName );
1523 mainGroup.toElement().setAttribute( QStringLiteral(
"inkscape:groupmode" ), QStringLiteral(
"layer" ) );
1524 QDomNode defs = svg.importNode( doc.elementsByTagName( QStringLiteral(
"defs" ) ).at( 0 ),
true );
1525 svgDocRoot.appendChild( defs );
1526 svgDocRoot.appendChild( mainGroup );
1531void QgsLayoutExporter::appendMetadataToSvg( QDomDocument &svg )
const
1534 QDomElement metadataElement = svg.createElement( QStringLiteral(
"metadata" ) );
1535 QDomElement rdfElement = svg.createElement( QStringLiteral(
"rdf:RDF" ) );
1536 rdfElement.setAttribute( QStringLiteral(
"xmlns:rdf" ), QStringLiteral(
"http://www.w3.org/1999/02/22-rdf-syntax-ns#" ) );
1537 rdfElement.setAttribute( QStringLiteral(
"xmlns:rdfs" ), QStringLiteral(
"http://www.w3.org/2000/01/rdf-schema#" ) );
1538 rdfElement.setAttribute( QStringLiteral(
"xmlns:dc" ), QStringLiteral(
"http://purl.org/dc/elements/1.1/" ) );
1539 QDomElement descriptionElement = svg.createElement( QStringLiteral(
"rdf:Description" ) );
1540 QDomElement workElement = svg.createElement( QStringLiteral(
"cc:Work" ) );
1541 workElement.setAttribute( QStringLiteral(
"rdf:about" ), QString() );
1543 auto addTextNode = [&workElement, &descriptionElement, &svg](
const QString & tag,
const QString & value )
1546 QDomElement element = svg.createElement( tag );
1547 QDomText t = svg.createTextNode( value );
1548 element.appendChild( t );
1549 workElement.appendChild( element );
1552 descriptionElement.setAttribute( tag, value );
1555 addTextNode( QStringLiteral(
"dc:format" ), QStringLiteral(
"image/svg+xml" ) );
1556 addTextNode( QStringLiteral(
"dc:title" ), metadata.
title() );
1557 addTextNode( QStringLiteral(
"dc:date" ), metadata.
creationDateTime().toString( Qt::ISODate ) );
1558 addTextNode( QStringLiteral(
"dc:identifier" ), metadata.
identifier() );
1559 addTextNode( QStringLiteral(
"dc:description" ), metadata.
abstract() );
1561 auto addAgentNode = [&workElement, &descriptionElement, &svg](
const QString & tag,
const QString & value )
1564 QDomElement inkscapeElement = svg.createElement( tag );
1565 QDomElement agentElement = svg.createElement( QStringLiteral(
"cc:Agent" ) );
1566 QDomElement titleElement = svg.createElement( QStringLiteral(
"dc:title" ) );
1567 QDomText t = svg.createTextNode( value );
1568 titleElement.appendChild( t );
1569 agentElement.appendChild( titleElement );
1570 inkscapeElement.appendChild( agentElement );
1571 workElement.appendChild( inkscapeElement );
1574 QDomElement bagElement = svg.createElement( QStringLiteral(
"rdf:Bag" ) );
1575 QDomElement liElement = svg.createElement( QStringLiteral(
"rdf:li" ) );
1576 t = svg.createTextNode( value );
1577 liElement.appendChild( t );
1578 bagElement.appendChild( liElement );
1580 QDomElement element = svg.createElement( tag );
1581 element.appendChild( bagElement );
1582 descriptionElement.appendChild( element );
1585 addAgentNode( QStringLiteral(
"dc:creator" ), metadata.
author() );
1586 addAgentNode( QStringLiteral(
"dc:publisher" ), getCreator() );
1590 QDomElement element = svg.createElement( QStringLiteral(
"dc:subject" ) );
1591 QDomElement bagElement = svg.createElement( QStringLiteral(
"rdf:Bag" ) );
1593 for (
auto it = keywords.constBegin(); it != keywords.constEnd(); ++it )
1595 const QStringList words = it.value();
1596 for (
const QString &keyword : words )
1598 QDomElement liElement = svg.createElement( QStringLiteral(
"rdf:li" ) );
1599 QDomText t = svg.createTextNode( keyword );
1600 liElement.appendChild( t );
1601 bagElement.appendChild( liElement );
1604 element.appendChild( bagElement );
1605 workElement.appendChild( element );
1606 descriptionElement.appendChild( element );
1609 rdfElement.appendChild( descriptionElement );
1610 rdfElement.appendChild( workElement );
1611 metadataElement.appendChild( rdfElement );
1612 svg.documentElement().appendChild( metadataElement );
1613 svg.documentElement().setAttribute( QStringLiteral(
"xmlns:cc" ), QStringLiteral(
"http://creativecommons.org/ns#" ) );
1616std::unique_ptr<double[]> QgsLayoutExporter::computeGeoTransform(
const QgsLayoutItemMap *map,
const QRectF ®ion,
double dpi )
const
1619 map = mLayout->referenceMap();
1625 dpi = mLayout->renderContext().dpi();
1628 QRectF exportRegion = region;
1629 if ( !exportRegion.isValid() )
1631 int pageNumber = map->
page();
1634 double pageY = page->pos().y();
1635 QSizeF pageSize = page->rect().size();
1636 exportRegion = QRectF( 0, pageY, pageSize.width(), pageSize.height() );
1640 QRectF mapItemSceneRect = map->mapRectToScene( map->rect() );
1643 double outputHeightMM = exportRegion.height();
1644 double outputWidthMM = exportRegion.width();
1648 double mapXCenter = mapExtent.
center().
x();
1649 double mapYCenter = mapExtent.
center().
y();
1651 double sinAlpha = std::sin( alpha );
1652 double cosAlpha = std::cos( alpha );
1655 QPointF mapItemPos = map->pos();
1657 mapItemPos.rx() -= exportRegion.left();
1658 mapItemPos.ry() -= exportRegion.top();
1661 double xRatio = mapExtent.
width() / mapItemSceneRect.width();
1662 double yRatio = mapExtent.
height() / mapItemSceneRect.height();
1663 double xmin = mapExtent.
xMinimum() - mapItemPos.x() * xRatio;
1664 double ymax = mapExtent.
yMaximum() + mapItemPos.y() * yRatio;
1665 QgsRectangle paperExtent( xmin, ymax - outputHeightMM * yRatio, xmin + outputWidthMM * xRatio, ymax );
1668 double X0 = paperExtent.xMinimum();
1669 double Y0 = paperExtent.yMaximum();
1674 double X1 = X0 - mapXCenter;
1675 double Y1 = Y0 - mapYCenter;
1676 double X2 = X1 * cosAlpha + Y1 * sinAlpha;
1677 double Y2 = -X1 * sinAlpha + Y1 * cosAlpha;
1678 X0 = X2 + mapXCenter;
1679 Y0 = Y2 + mapYCenter;
1683 int pageWidthPixels =
static_cast< int >( dpi * outputWidthMM / 25.4 );
1684 int pageHeightPixels =
static_cast< int >( dpi * outputHeightMM / 25.4 );
1685 double pixelWidthScale = paperExtent.width() / pageWidthPixels;
1686 double pixelHeightScale = paperExtent.height() / pageHeightPixels;
1689 std::unique_ptr<double[]> t(
new double[6] );
1691 t[1] = cosAlpha * pixelWidthScale;
1692 t[2] = -sinAlpha * pixelWidthScale;
1694 t[4] = -sinAlpha * pixelHeightScale;
1695 t[5] = -cosAlpha * pixelHeightScale;
1700void QgsLayoutExporter::writeWorldFile(
const QString &worldFileName,
double a,
double b,
double c,
double d,
double e,
double f )
const
1702 QFile worldFile( worldFileName );
1703 if ( !worldFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
1707 QTextStream fout( &worldFile );
1711 fout << QString::number( a,
'f', 12 ) <<
"\r\n";
1712 fout << QString::number( d,
'f', 12 ) <<
"\r\n";
1713 fout << QString::number( b,
'f', 12 ) <<
"\r\n";
1714 fout << QString::number( e,
'f', 12 ) <<
"\r\n";
1715 fout << QString::number(
c,
'f', 12 ) <<
"\r\n";
1716 fout << QString::number( f,
'f', 12 ) <<
"\r\n";
1721 return georeferenceOutputPrivate( file, map, exportRegion, dpi,
false );
1724bool QgsLayoutExporter::georeferenceOutputPrivate(
const QString &file,
QgsLayoutItemMap *map,
const QRectF &exportRegion,
double dpi,
bool includeGeoreference,
bool includeMetadata )
const
1729 if ( !map && includeGeoreference )
1730 map = mLayout->referenceMap();
1732 std::unique_ptr<double[]> t;
1734 if ( map && includeGeoreference )
1737 dpi = mLayout->renderContext().dpi();
1739 t = computeGeoTransform( map, exportRegion, dpi );
1744 CPLSetConfigOption(
"GDAL_PDF_DPI", QString::number( dpi ).toUtf8().constData() );
1749 GDALSetGeoTransform( outputDS.get(), t.get() );
1751 if ( includeMetadata )
1753 QString creationDateString;
1754 const QDateTime creationDateTime = mLayout->project()->metadata().creationDateTime();
1755 if ( creationDateTime.isValid() )
1757 creationDateString = QStringLiteral(
"D:%1" ).arg( mLayout->project()->metadata().creationDateTime().toString( QStringLiteral(
"yyyyMMddHHmmss" ) ) );
1758 if ( creationDateTime.timeZone().isValid() )
1760 int offsetFromUtc = creationDateTime.timeZone().offsetFromUtc( creationDateTime );
1761 creationDateString += ( offsetFromUtc >= 0 ) ?
'+' :
'-';
1762 offsetFromUtc = std::abs( offsetFromUtc );
1763 int offsetHours = offsetFromUtc / 3600;
1764 int offsetMins = ( offsetFromUtc % 3600 ) / 60;
1765 creationDateString += QStringLiteral(
"%1'%2'" ).arg( offsetHours ).arg( offsetMins );
1768 GDALSetMetadataItem( outputDS.get(),
"CREATION_DATE", creationDateString.toUtf8().constData(),
nullptr );
1770 GDALSetMetadataItem( outputDS.get(),
"AUTHOR", mLayout->project()->metadata().author().toUtf8().constData(),
nullptr );
1771 const QString creator = getCreator();
1772 GDALSetMetadataItem( outputDS.get(),
"CREATOR", creator.toUtf8().constData(),
nullptr );
1773 GDALSetMetadataItem( outputDS.get(),
"PRODUCER", creator.toUtf8().constData(),
nullptr );
1774 GDALSetMetadataItem( outputDS.get(),
"SUBJECT", mLayout->project()->metadata().abstract().toUtf8().constData(),
nullptr );
1775 GDALSetMetadataItem( outputDS.get(),
"TITLE", mLayout->project()->metadata().title().toUtf8().constData(),
nullptr );
1778 QStringList allKeywords;
1779 for (
auto it = keywords.constBegin(); it != keywords.constEnd(); ++it )
1781 allKeywords.append( QStringLiteral(
"%1: %2" ).arg( it.key(), it.value().join(
',' ) ) );
1783 const QString keywordString = allKeywords.join(
';' );
1784 GDALSetMetadataItem( outputDS.get(),
"KEYWORDS", keywordString.toUtf8().constData(),
nullptr );
1790 CPLSetConfigOption(
"GDAL_PDF_DPI",
nullptr );
1797 if ( items.count() == 1 )
1801 QString name = layoutItem->displayName();
1803 if ( name.startsWith(
'<' ) && name.endsWith(
'>' ) )
1804 name = name.mid( 1, name.length() - 2 );
1808 else if ( items.count() > 1 )
1810 QStringList currentLayerItemTypes;
1811 for ( QGraphicsItem *item : items )
1817 if ( !currentLayerItemTypes.contains( itemType ) && !currentLayerItemTypes.contains( itemTypePlural ) )
1818 currentLayerItemTypes << itemType;
1819 else if ( currentLayerItemTypes.contains( itemType ) )
1821 currentLayerItemTypes.replace( currentLayerItemTypes.indexOf( itemType ), itemTypePlural );
1826 if ( !currentLayerItemTypes.contains( QObject::tr(
"Other" ) ) )
1827 currentLayerItemTypes.append( QObject::tr(
"Other" ) );
1830 return currentLayerItemTypes.join( QLatin1String(
", " ) );
1832 return QObject::tr(
"Layer %1" ).arg( layerId );
1837 const std::function<QString(
QgsLayoutItem *item )> &getItemExportGroupFunc )
1839 LayoutItemHider itemHider( items );
1844 QString previousItemGroup;
1845 unsigned int layerId = 1;
1847 itemHider.hideAll();
1848 const QList< QGraphicsItem * > itemsToIterate = itemHider.itemsToIterate();
1849 QList< QGraphicsItem * > currentLayerItems;
1850 for ( QGraphicsItem *item : itemsToIterate )
1854 bool canPlaceInExistingLayer =
false;
1855 QString thisItemExportGroupName;
1859 thisItemExportGroupName = getItemExportGroupFunc( layoutItem );
1860 if ( !thisItemExportGroupName.isEmpty() )
1862 if ( thisItemExportGroupName != previousItemGroup && !currentLayerItems.empty() )
1865 layerDetails.
groupName = thisItemExportGroupName;
1868 switch ( itemExportBehavior )
1872 switch ( prevItemBehavior )
1875 canPlaceInExistingLayer =
true;
1879 canPlaceInExistingLayer = prevType == -1 || prevType == layoutItem->
type();
1884 canPlaceInExistingLayer =
false;
1892 switch ( prevItemBehavior )
1896 canPlaceInExistingLayer = prevType == -1 || prevType == layoutItem->
type();
1901 canPlaceInExistingLayer =
false;
1909 canPlaceInExistingLayer =
false;
1914 canPlaceInExistingLayer =
false;
1917 prevItemBehavior = itemExportBehavior;
1918 prevType = layoutItem->
type();
1919 previousItemGroup = thisItemExportGroupName;
1924 previousItemGroup.clear();
1927 if ( canPlaceInExistingLayer )
1929 currentLayerItems << item;
1934 if ( !currentLayerItems.isEmpty() )
1938 ExportResult result = exportFunc( layerId, layerDetails );
1942 currentLayerItems.clear();
1945 itemHider.hideAll();
1950 int layoutItemLayerIdx = 0;
1952 mLayout->renderContext().setCurrentExportLayer( layoutItemLayerIdx );
1958 mLayout->renderContext().setCurrentExportLayer( layoutItemLayerIdx );
1962 ExportResult result = exportFunc( layerId, layerDetails );
1967 layoutItemLayerIdx++;
1969 layerDetails.mapLayerId.clear();
1971 mLayout->renderContext().setCurrentExportLayer( -1 );
1974 currentLayerItems.clear();
1978 currentLayerItems << item;
1980 layerDetails.groupName = thisItemExportGroupName;
1983 if ( !currentLayerItems.isEmpty() )
1986 ExportResult result = exportFunc( layerId, layerDetails );
2001 return simplifyMethod;
2024 int pageNumber = map->
page();
2026 double pageY = page->pos().y();
2027 QSizeF pageSize = page->rect().size();
2028 QRectF pageRect( 0, pageY, pageSize.width(), pageSize.height() );
2044 double destinationHeight = exportRegion.height();
2045 double destinationWidth = exportRegion.width();
2047 QRectF mapItemSceneRect = map->mapRectToScene( map->rect() );
2052 double xRatio = mapExtent.
width() / mapItemSceneRect.width();
2053 double yRatio = mapExtent.
height() / mapItemSceneRect.height();
2055 double xCenter = mapExtent.
center().
x();
2056 double yCenter = mapExtent.
center().
y();
2059 QPointF mapItemPos = map->pos();
2061 mapItemPos.rx() -= exportRegion.left();
2062 mapItemPos.ry() -= exportRegion.top();
2064 double xmin = mapExtent.
xMinimum() - mapItemPos.x() * xRatio;
2065 double ymax = mapExtent.
yMaximum() + mapItemPos.y() * yRatio;
2066 QgsRectangle paperExtent( xmin, ymax - destinationHeight * yRatio, xmin + destinationWidth * xRatio, ymax );
2068 double X0 = paperExtent.
xMinimum();
2069 double Y0 = paperExtent.
yMinimum();
2072 dpi = mLayout->renderContext().dpi();
2074 int widthPx =
static_cast< int >( dpi * destinationWidth / 25.4 );
2075 int heightPx =
static_cast< int >( dpi * destinationHeight / 25.4 );
2077 double Ww = paperExtent.
width() / widthPx;
2078 double Hh = paperExtent.
height() / heightPx;
2087 s[5] = Y0 + paperExtent.
height();
2091 r[0] = std::cos( alpha );
2092 r[1] = -std::sin( alpha );
2093 r[2] = xCenter * ( 1 - std::cos( alpha ) ) + yCenter * std::sin( alpha );
2094 r[3] = std::sin( alpha );
2095 r[4] = std::cos( alpha );
2096 r[5] = - xCenter * std::sin( alpha ) + yCenter * ( 1 - std::cos( alpha ) );
2099 a = r[0] * s[0] + r[1] * s[3];
2100 b = r[0] * s[1] + r[1] * s[4];
2101 c = r[0] * s[2] + r[1] * s[5] + r[2];
2102 d = r[3] * s[0] + r[4] * s[3];
2103 e = r[3] * s[1] + r[4] * s[4];
2104 f = r[3] * s[2] + r[4] * s[5] + r[5];
2112 QList< QgsLayoutItem *> items;
2118 if ( currentItem->isVisible() && currentItem->requiresRasterization() )
2129 QList< QgsLayoutItem *> items;
2135 if ( currentItem->isVisible() && currentItem->containsAdvancedEffects() )
2148 if ( mLayout->pageCollection()->pageCount() == 1 )
2151 bounds = mLayout->layoutBounds(
true );
2156 bounds = mLayout->pageItemBounds( page,
true );
2158 if ( bounds.width() <= 0 || bounds.height() <= 0 )
2166 bounds = bounds.adjusted( -settings.
cropMargins.
left() * pixelToLayoutUnits,
2178int QgsLayoutExporter::firstPageToBeExported(
QgsLayout *layout )
2181 for (
int i = 0; i < pageCount; ++i )
2195 if ( details.
page == 0 )
2205void QgsLayoutExporter::captureLabelingResults()
2207 qDeleteAll( mLabelingResults );
2208 mLabelingResults.clear();
2210 QList< QgsLayoutItemMap * > maps;
2211 mLayout->layoutItems( maps );
2215 mLabelingResults[ map->
uuid() ] = map->mExportLabelingResults.release();
2219bool QgsLayoutExporter::saveImage(
const QImage &image,
const QString &imageFilename,
const QString &imageFormat,
QgsProject *projectForMetadata )
2221 QImageWriter w( imageFilename, imageFormat.toLocal8Bit().constData() );
2222 if ( imageFormat.compare( QLatin1String(
"tiff" ), Qt::CaseInsensitive ) == 0 || imageFormat.compare( QLatin1String(
"tif" ), Qt::CaseInsensitive ) == 0 )
2224 w.setCompression( 1 );
2226 if ( projectForMetadata )
2228 w.setText( QStringLiteral(
"Author" ), projectForMetadata->
metadata().
author() );
2229 const QString creator = getCreator();
2230 w.setText( QStringLiteral(
"Creator" ), creator );
2231 w.setText( QStringLiteral(
"Producer" ), creator );
2232 w.setText( QStringLiteral(
"Subject" ), projectForMetadata->
metadata().
abstract() );
2233 w.setText( QStringLiteral(
"Created" ), projectForMetadata->
metadata().
creationDateTime().toString( Qt::ISODate ) );
2234 w.setText( QStringLiteral(
"Title" ), projectForMetadata->
metadata().
title() );
2237 QStringList allKeywords;
2238 for (
auto it = keywords.constBegin(); it != keywords.constEnd(); ++it )
2240 allKeywords.append( QStringLiteral(
"%1: %2" ).arg( it.key(), it.value().join(
',' ) ) );
2242 const QString keywordString = allKeywords.join(
';' );
2243 w.setText( QStringLiteral(
"Keywords" ), keywordString );
2245 return w.write( image );
2248QString QgsLayoutExporter::getCreator()
2253void QgsLayoutExporter::setXmpMetadata( QPdfWriter *pdfWriter,
QgsLayout *layout )
2255#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
2256 QUuid documentId = pdfWriter->documentId();
2258 QUuid documentId = QUuid::createUuid();
2263 const QString metaDataDate = creationDateTime.isValid() ? creationDateTime.toOffsetFromUtc( creationDateTime.offsetFromUtc() ).toString( Qt::ISODate ) : QString();
2264 const QString title = pdfWriter->title();
2265 const QString creator = getCreator();
2266 const QString producer = creator;
2271 const QLatin1String xmlNS(
"http://www.w3.org/XML/1998/namespace" );
2272 const QLatin1String adobeNS(
"adobe:ns:meta/" );
2273 const QLatin1String rdfNS(
"http://www.w3.org/1999/02/22-rdf-syntax-ns#" );
2274 const QLatin1String dcNS(
"http://purl.org/dc/elements/1.1/" );
2275 const QLatin1String xmpNS(
"http://ns.adobe.com/xap/1.0/" );
2276 const QLatin1String xmpMMNS(
"http://ns.adobe.com/xap/1.0/mm/" );
2277 const QLatin1String pdfNS(
"http://ns.adobe.com/pdf/1.3/" );
2278 const QLatin1String pdfaidNS(
"http://www.aiim.org/pdfa/ns/id/" );
2279 const QLatin1String pdfxidNS(
"http://www.npes.org/pdfx/ns/id/" );
2281 QByteArray xmpMetadata;
2282 QBuffer output( &xmpMetadata );
2283 output.open( QIODevice::WriteOnly );
2284 output.write(
"<?xpacket begin='' ?>" );
2286 QXmlStreamWriter w( &output );
2287 w.setAutoFormatting(
true );
2288 w.writeNamespace( adobeNS,
"x" );
2289 w.writeNamespace( rdfNS,
"rdf" );
2290 w.writeNamespace( dcNS,
"dc" );
2291 w.writeNamespace( xmpNS,
"xmp" );
2292 w.writeNamespace( xmpMMNS,
"xmpMM" );
2293 w.writeNamespace( pdfNS,
"pdf" );
2294 w.writeNamespace( pdfaidNS,
"pdfaid" );
2295 w.writeNamespace( pdfxidNS,
"pdfxid" );
2297 w.writeStartElement( adobeNS,
"xmpmeta" );
2298 w.writeStartElement( rdfNS,
"RDF" );
2301 w.writeStartElement( rdfNS,
"Description" );
2302 w.writeAttribute( rdfNS,
"about",
"" );
2303 w.writeStartElement( dcNS,
"title" );
2304 w.writeStartElement( rdfNS,
"Alt" );
2305 w.writeStartElement( rdfNS,
"li" );
2306 w.writeAttribute( xmlNS,
"lang",
"x-default" );
2307 w.writeCharacters( title );
2308 w.writeEndElement();
2309 w.writeEndElement();
2310 w.writeEndElement();
2312 w.writeStartElement( dcNS,
"creator" );
2313 w.writeStartElement( rdfNS,
"Seq" );
2314 w.writeStartElement( rdfNS,
"li" );
2315 w.writeCharacters( author );
2316 w.writeEndElement();
2317 w.writeEndElement();
2318 w.writeEndElement();
2320 w.writeEndElement();
2323 w.writeStartElement( rdfNS,
"Description" );
2324 w.writeAttribute( rdfNS,
"about",
"" );
2325 w.writeAttribute( pdfNS,
"Producer", producer );
2326 w.writeAttribute( pdfNS,
"Trapped",
"False" );
2327 w.writeEndElement();
2330 w.writeStartElement( rdfNS,
"Description" );
2331 w.writeAttribute( rdfNS,
"about",
"" );
2332 w.writeAttribute( xmpNS,
"CreatorTool", creator );
2333 w.writeAttribute( xmpNS,
"CreateDate", metaDataDate );
2334 w.writeAttribute( xmpNS,
"ModifyDate", metaDataDate );
2335 w.writeAttribute( xmpNS,
"MetadataDate", metaDataDate );
2336 w.writeEndElement();
2339 w.writeStartElement( rdfNS,
"Description" );
2340 w.writeAttribute( rdfNS,
"about",
"" );
2341 w.writeAttribute( xmpMMNS,
"DocumentID",
"uuid:" + documentId.toString( QUuid::WithoutBraces ) );
2342 w.writeAttribute( xmpMMNS,
"VersionID",
"1" );
2343 w.writeAttribute( xmpMMNS,
"RenditionClass",
"default" );
2344 w.writeEndElement();
2346#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
2349 switch ( pdfWriter->pdfVersion() )
2351 case QPagedPaintDevice::PdfVersion_1_4:
2352 case QPagedPaintDevice::PdfVersion_A1b:
2353 case QPagedPaintDevice::PdfVersion_1_6:
2355 case QPagedPaintDevice::PdfVersion_X4:
2356 w.writeStartElement( rdfNS,
"Description" );
2357 w.writeAttribute( rdfNS,
"about",
"" );
2358 w.writeAttribute( pdfxidNS,
"GTS_PDFXVersion",
"PDF/X-4" );
2359 w.writeEndElement();
2365 w.writeEndElement();
2366 w.writeEndElement();
2368 w.writeEndDocument();
2369 output.write(
"<?xpacket end='w'?>" );
2371 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.
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.
double xMinimum() const
Returns the x minimum value (left side of rectangle).
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
double width() const
Returns the width of the rectangle.
double yMaximum() const
Returns the y maximum value (top side of rectangle).
QgsPointXY center() const
Returns the center point of the rectangle.
double height() const
Returns the height of the rectangle.
A boolean 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.
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.