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 , mPreviousRasterPolicy( layout->renderContext().rasterizedRenderingPolicy() )
345 , mPreviousTextFormat( layout->renderContext().textRenderFormat() )
346 , mPreviousExportLayer( layout->renderContext().currentExportLayer() )
347 , mPreviousSimplifyMethod( layout->renderContext().simplifyMethod() )
348 , mPreviousMaskSettings( layout->renderContext().maskSettings() )
349 , mExportThemes( layout->renderContext().exportThemes() )
350 , mPredefinedScales( layout->renderContext().predefinedScales() )
355 ~LayoutContextSettingsRestorer()
357 mLayout->renderContext().setDpi( mPreviousDpi );
358 mLayout->renderContext().setFlags( mPreviousFlags );
359 mLayout->renderContext().setRasterizedRenderingPolicy( mPreviousRasterPolicy );
360 mLayout->renderContext().setTextRenderFormat( mPreviousTextFormat );
362 mLayout->renderContext().setCurrentExportLayer( mPreviousExportLayer );
364 mLayout->renderContext().setSimplifyMethod( mPreviousSimplifyMethod );
365 mLayout->renderContext().setMaskSettings( mPreviousMaskSettings );
366 mLayout->renderContext().setExportThemes( mExportThemes );
367 mLayout->renderContext().setPredefinedScales( mPredefinedScales );
370 LayoutContextSettingsRestorer(
const LayoutContextSettingsRestorer &other ) =
delete;
371 LayoutContextSettingsRestorer &operator=(
const LayoutContextSettingsRestorer &other ) =
delete;
375 double mPreviousDpi = 0;
379 int mPreviousExportLayer = 0;
382 QStringList mExportThemes;
383 QVector< double > mPredefinedScales;
394 if ( settings.
dpi <= 0 )
395 settings.
dpi = mLayout->renderContext().dpi();
397 mErrorFileName.clear();
399 int worldFilePageNo = -1;
402 worldFilePageNo = referenceMap->page();
405 QFileInfo fi( filePath );
407 if ( !dir.exists( fi.absolutePath() ) )
409 dir.mkpath( fi.absolutePath() );
414 pageDetails.
baseName = fi.completeBaseName();
417 LayoutContextPreviewSettingRestorer restorer( mLayout );
419 LayoutContextSettingsRestorer dpiRestorer( mLayout );
421 mLayout->renderContext().setDpi( settings.
dpi );
422 mLayout->renderContext().setFlags( settings.
flags );
427 if ( settings.
pages.empty() )
429 for (
int page = 0; page < mLayout->pageCollection()->pageCount(); ++page )
434 for (
int page : std::as_const( settings.
pages ) )
436 if ( page >= 0 && page < mLayout->pageCollection()->pageCount() )
441 for (
int page : std::as_const( pages ) )
443 if ( !mLayout->pageCollection()->shouldExportPage( page ) )
450 QImage image = createImage( settings, page, bounds, skip );
455 pageDetails.
page = page;
458 if ( image.isNull() )
460 mErrorFileName = outputFilePath;
466 mErrorFileName = outputFilePath;
470 const bool shouldGeoreference = ( page == worldFilePageNo );
471 if ( shouldGeoreference )
473 georeferenceOutputPrivate( outputFilePath,
nullptr, bounds, settings.
dpi, shouldGeoreference );
478 double a, b,
c, d, e, f;
479 if ( bounds.isValid() )
484 QFileInfo fi( outputFilePath );
486 QString outputSuffix = fi.suffix();
487 QString worldFileName = fi.absolutePath() +
'/' + fi.completeBaseName() +
'.'
488 + outputSuffix.at( 0 ) + outputSuffix.at( fi.suffix().size() - 1 ) +
'w';
490 writeWorldFile( worldFileName, a, b,
c, d, e, f );
495 captureLabelingResults();
506 int total = iterator->
count();
507 double step = total > 0 ? 100.0 / total : 100.0;
509 while ( iterator->
next() )
514 feedback->setProperty(
"progress", QObject::tr(
"Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
516 feedback->setProperty(
"progress", QObject::tr(
"Exporting section %1" ).arg( i + 1 ) );
526 QString filePath = iterator->
filePath( baseFilePath, extension );
531 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 ) );
551 if ( !mLayout || mLayout->pageCollection()->pageCount() == 0 )
555 if ( settings.
dpi <= 0 )
556 settings.
dpi = mLayout->renderContext().dpi();
558 mErrorFileName.clear();
560 LayoutContextPreviewSettingRestorer restorer( mLayout );
562 LayoutContextSettingsRestorer contextRestorer( mLayout );
563 ( void )contextRestorer;
564 mLayout->renderContext().setDpi( settings.
dpi );
566 mLayout->renderContext().setMaskSettings( createExportMaskSettings() );
570 mLayout->renderContext().setSimplifyMethod( createExportSimplifyMethod() );
573 std::unique_ptr< QgsLayoutGeospatialPdfExporter > geospatialPdfExporter;
575 geospatialPdfExporter = std::make_unique< QgsLayoutGeospatialPdfExporter >( mLayout );
577 mLayout->renderContext().setFlags( settings.
flags );
600 mLayout->renderContext().setExportThemes( settings.
exportThemes );
612 const QList<QGraphicsItem *> items = mLayout->items( Qt::AscendingOrder );
614 QList< QgsLayoutGeospatialPdfExporter::ComponentLayerDetail > pdfComponents;
619 QSet<QString> mutuallyExclusiveGroups;
625 component.
name = layerDetail.name;
626 component.
mapLayerId = layerDetail.mapLayerId;
627 component.
opacity = layerDetail.opacity;
629 component.
group = layerDetail.groupName;
630 if ( !layerDetail.mapTheme.isEmpty() )
632 component.
group = layerDetail.mapTheme;
633 mutuallyExclusiveGroups.insert( layerDetail.mapTheme );
636 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' ) ) );
637 pdfComponents << component;
639 preparePrintAsPdf( mLayout, &printer, component.
sourcePdfPath );
640 preparePrint( mLayout, &printer,
false );
642 if ( !p.begin( &printer ) )
650 return layerExportResult;
652 auto getExportGroupNameFunc = [](
QgsLayoutItem * item )->QString
654 return item->customProperty( QStringLiteral(
"pdfExportGroup" ) ).toString();
656 result = handleLayeredExport( items, exportFunc, getExportGroupNameFunc );
660 if ( settings.writeGeoPdf )
663 details.
dpi = settings.dpi;
665 QgsLayoutSize pageSize = mLayout->pageCollection()->page( 0 )->sizeWithUnits();
670 if ( settings.exportMetadata )
673 details.
author = mLayout->project()->metadata().author();
675 details.
creator = getCreator();
676 details.
creationDateTime = mLayout->project()->metadata().creationDateTime();
677 details.
subject = mLayout->project()->metadata().abstract();
678 details.
title = mLayout->project()->metadata().title();
679 details.
keywords = mLayout->project()->metadata().keywords();
682 const QList< QgsMapLayer * > layers = mLayout->project()->mapLayers().values();
688 if ( settings.appendGeoreference )
691 QList< QgsLayoutItemMap * > maps;
692 mLayout->layoutItems( maps );
696 georef.
crs = map->crs();
698 const QPointF topLeft = map->mapToScene( QPointF( 0, 0 ) );
699 const QPointF topRight = map->mapToScene( QPointF( map->rect().width(), 0 ) );
700 const QPointF bottomLeft = map->mapToScene( QPointF( 0, map->rect().height() ) );
701 const QPointF bottomRight = map->mapToScene( QPointF( map->rect().width(), map->rect().height() ) );
714 const QTransform t = map->layoutToMapCoordsTransform();
715 const QgsPointXY topLeftMap = t.map( topLeft );
716 const QgsPointXY topRightMap = t.map( topRight );
717 const QgsPointXY bottomLeftMap = t.map( bottomLeft );
718 const QgsPointXY bottomRightMap = t.map( bottomRight );
730 details.
layerOrder = geospatialPdfExporter->layerOrder();
735 if ( !geospatialPdfExporter->finalize( pdfComponents, filePath, details ) )
738 mErrorMessage = geospatialPdfExporter->errorMessage();
748 QPdfWriter printer = QPdfWriter( filePath );
749 preparePrintAsPdf( mLayout, &printer, filePath );
750 preparePrint( mLayout, &printer,
false );
752 if ( !p.begin( &printer ) )
761 bool shouldAppendGeoreference = settings.
appendGeoreference && mLayout && mLayout->referenceMap() && mLayout->referenceMap()->page() == 0;
764 georeferenceOutputPrivate( filePath,
nullptr, QRectF(), settings.
dpi, shouldAppendGeoreference, settings.
exportMetadata );
767 captureLabelingResults();
780 QPdfWriter printer = QPdfWriter( fileName );
783 int total = iterator->
count();
784 double step = total > 0 ? 100.0 / total : 100.0;
787 while ( iterator->
next() )
792 feedback->setProperty(
"progress", QObject::tr(
"Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
794 feedback->setProperty(
"progress", QObject::tr(
"Exporting section %1" ).arg( i + 1 ) );
806 LayoutContextPreviewSettingRestorer restorer( iterator->
layout() );
808 LayoutContextSettingsRestorer contextRestorer( iterator->
layout() );
809 ( void )contextRestorer;
836 preparePrintAsPdf( iterator->
layout(), &printer, fileName );
837 preparePrint( iterator->
layout(), &printer,
false );
839 if ( !p.begin( &printer ) )
853 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 ) );
880 int total = iterator->
count();
881 double step = total > 0 ? 100.0 / total : 100.0;
883 while ( iterator->
next() )
888 feedback->setProperty(
"progress", QObject::tr(
"Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
890 feedback->setProperty(
"progress", QObject::tr(
"Exporting section %1" ).arg( i + 1 ) );
899 QString filePath = iterator->
filePath( baseFilePath, QStringLiteral(
"pdf" ) );
906 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 ) );
924#if defined( HAVE_QTPRINTER )
931 if ( settings.
dpi <= 0 )
932 settings.
dpi = mLayout->renderContext().dpi();
934 mErrorFileName.clear();
936 LayoutContextPreviewSettingRestorer restorer( mLayout );
938 LayoutContextSettingsRestorer contextRestorer( mLayout );
939 ( void )contextRestorer;
940 mLayout->renderContext().setDpi( settings.
dpi );
942 mLayout->renderContext().setFlags( settings.
flags );
949 preparePrint( mLayout, &printer,
true );
951 if ( !p.begin( &printer ) )
960 captureLabelingResults();
971 PrintExportSettings settings = s;
975 int total = iterator->
count();
976 double step = total > 0 ? 100.0 / total : 100.0;
979 while ( iterator->
next() )
984 feedback->setProperty(
"progress", QObject::tr(
"Printing %1 of %2" ).arg( i + 1 ).arg( total ) );
986 feedback->setProperty(
"progress", QObject::tr(
"Printing section %1" ).arg( i + 1 ) );
998 LayoutContextPreviewSettingRestorer restorer( iterator->
layout() );
1000 LayoutContextSettingsRestorer contextRestorer( iterator->
layout() );
1001 ( void )contextRestorer;
1010 if ( !settings.rasterizeWholeImage )
1015 preparePrint( iterator->
layout(), &printer,
true );
1017 if ( !p.begin( &printer ) )
1027 ExportResult result = exporter.printPrivate( &printer, p, !first, settings.dpi, settings.rasterizeWholeImage );
1031 error = exporter.errorMessage();
1054 if ( settings.
dpi <= 0 )
1055 settings.
dpi = mLayout->renderContext().dpi();
1057 mErrorFileName.clear();
1059 LayoutContextPreviewSettingRestorer restorer( mLayout );
1061 LayoutContextSettingsRestorer contextRestorer( mLayout );
1062 ( void )contextRestorer;
1063 mLayout->renderContext().setDpi( settings.
dpi );
1065 mLayout->renderContext().setFlags( settings.
flags );
1080 mLayout->renderContext().setMaskSettings( createExportMaskSettings() );
1084 mLayout->renderContext().setSimplifyMethod( createExportSimplifyMethod() );
1087 QFileInfo fi( filePath );
1090 pageDetails.
baseName = fi.baseName();
1091 pageDetails.
extension = fi.completeSuffix();
1095 for (
int i = 0; i < mLayout->pageCollection()->pageCount(); ++i )
1097 if ( !mLayout->pageCollection()->shouldExportPage( i ) )
1102 pageDetails.
page = i;
1109 if ( mLayout->pageCollection()->pageCount() == 1 )
1112 bounds = mLayout->layoutBounds(
true );
1117 bounds = mLayout->pageItemBounds( i,
true );
1126 bounds = QRectF( pageItem->pos().x(), pageItem->pos().y(), pageItem->rect().width(), pageItem->rect().height() );
1130 int width =
static_cast< int >( bounds.width() * settings.
dpi / inchesToLayoutUnits );
1132 int height =
static_cast< int >( bounds.height() * settings.
dpi / inchesToLayoutUnits );
1133 if ( width == 0 || height == 0 )
1142 const QRectF paperRect = QRectF( pageItem->pos().x(),
1143 pageItem->pos().y(),
1144 pageItem->rect().width(),
1145 pageItem->rect().height() );
1147 QDomNode svgDocRoot;
1148 const QList<QGraphicsItem *> items = mLayout->items( paperRect,
1149 Qt::IntersectsItemBoundingRect,
1150 Qt::AscendingOrder );
1154 return renderToLayeredSvg( settings, width, height, i, bounds, fileName, layerId, layerDetail.name, svg, svgDocRoot, settings.
exportMetadata );
1156 auto getExportGroupNameFunc = [](
QgsLayoutItem * )->QString
1160 ExportResult res = handleLayeredExport( items, exportFunc, getExportGroupNameFunc );
1165 appendMetadataToSvg( svg );
1167 QFile out( fileName );
1168 bool openOk = out.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate );
1171 mErrorFileName = fileName;
1175 out.write( svg.toByteArray() );
1181 QSvgGenerator generator;
1184 generator.setTitle( mLayout->project()->metadata().title() );
1185 generator.setDescription( mLayout->project()->metadata().abstract() );
1187 generator.setOutputDevice( &svgBuffer );
1188 generator.setSize( QSize( width, height ) );
1189 generator.setViewBox( QRect( 0, 0, width, height ) );
1190 generator.setResolution(
static_cast< int >( std::round( settings.
dpi ) ) );
1193 bool createOk = p.begin( &generator );
1196 mErrorFileName = fileName;
1209 svgBuffer.open( QIODevice::ReadOnly );
1213 if ( ! svg.setContent( &svgBuffer,
false, &errorMsg, &errorLine ) )
1215 mErrorFileName = fileName;
1220 appendMetadataToSvg( svg );
1222 QFile out( fileName );
1223 bool openOk = out.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate );
1226 mErrorFileName = fileName;
1230 out.write( svg.toByteArray() );
1234 captureLabelingResults();
1245 int total = iterator->
count();
1246 double step = total > 0 ? 100.0 / total : 100.0;
1248 while ( iterator->
next() )
1253 feedback->setProperty(
"progress", QObject::tr(
"Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
1255 feedback->setProperty(
"progress", QObject::tr(
"Exporting section %1" ).arg( i + 1 ) );
1265 QString filePath = iterator->
filePath( baseFilePath, QStringLiteral(
"svg" ) );
1272 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 ) );
1293 return mLabelingResults;
1298 QMap<QString, QgsLabelingResults *> res;
1299 std::swap( mLabelingResults, res );
1303void QgsLayoutExporter::preparePrintAsPdf(
QgsLayout *layout, QPdfWriter *device,
const QString &filePath )
1305 QFileInfo fi( filePath );
1307 if ( !dir.exists( fi.absolutePath() ) )
1309 dir.mkpath( fi.absolutePath() );
1312 updatePrinterPageSize(
layout, device, firstPageToBeExported(
layout ) );
1318 device->setTitle( title );
1320 QPagedPaintDevice::PdfVersion pdfVersion = QPagedPaintDevice::PdfVersion_1_4;
1322#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
1328 switch ( styleSettings->colorModel() )
1331 device->setColorModel( QPdfWriter::ColorModel::CMYK );
1335 device->setColorModel( QPdfWriter::ColorModel::RGB );
1339 const QColorSpace colorSpace = styleSettings->colorSpace();
1340 if ( colorSpace.isValid() )
1342 QPdfOutputIntent outputIntent;
1343 outputIntent.setOutputProfile( colorSpace );
1344 outputIntent.setOutputCondition( colorSpace.description() );
1348 outputIntent.setOutputConditionIdentifier( QStringLiteral(
"Unknown identifier" ) );
1349 outputIntent.setRegistryName( QStringLiteral(
"Unknown registry" ) );
1350 device->setOutputIntent( outputIntent );
1353 pdfVersion = QPagedPaintDevice::PdfVersion_X4;
1359 device->setPdfVersion( pdfVersion );
1360 setXmpMetadata( device,
layout );
1366#if defined(HAS_KDE_QT5_PDF_TRANSFORM_FIX) || QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)
1373void QgsLayoutExporter::preparePrint(
QgsLayout *layout, QPagedPaintDevice *device,
bool setFirstPageSize )
1375 if ( QPdfWriter *pdf =
dynamic_cast<QPdfWriter *
>( device ) )
1379#if defined( HAVE_QTPRINTER )
1380 else if ( QPrinter *printer =
dynamic_cast<QPrinter *
>( device ) )
1382 printer->setFullPage(
true );
1383 printer->setColorMode( QPrinter::Color );
1389 if ( setFirstPageSize )
1391 updatePrinterPageSize(
layout, device, firstPageToBeExported(
layout ) );
1397 if ( mLayout->pageCollection()->pageCount() == 0 )
1400 preparePrint( mLayout, device,
true );
1402 if ( !p.begin( device ) )
1408 printPrivate( device, p );
1413QgsLayoutExporter::ExportResult QgsLayoutExporter::printPrivate( QPagedPaintDevice *device, QPainter &painter,
bool startNewPage,
double dpi,
bool rasterize )
1417 int toPage = mLayout->pageCollection()->pageCount() - 1;
1419#if defined( HAVE_QTPRINTER )
1420 if ( QPrinter *printer =
dynamic_cast<QPrinter *
>( device ) )
1422 if ( printer->fromPage() >= 1 )
1423 fromPage = printer->fromPage() - 1;
1424 if ( printer->toPage() >= 1 )
1425 toPage = printer->toPage() - 1;
1429 bool pageExported =
false;
1432 for (
int i = fromPage; i <= toPage; ++i )
1434 if ( !mLayout->pageCollection()->shouldExportPage( i ) )
1439 updatePrinterPageSize( mLayout, device, i );
1440 if ( ( pageExported && i > fromPage ) || startNewPage )
1446 if ( !image.isNull() )
1448 QRectF targetArea( 0, 0, image.width(), image.height() );
1449 painter.drawImage( targetArea, image, targetArea );
1455 pageExported =
true;
1460 for (
int i = fromPage; i <= toPage; ++i )
1462 if ( !mLayout->pageCollection()->shouldExportPage( i ) )
1467 updatePrinterPageSize( mLayout, device, i );
1469 if ( ( pageExported && i > fromPage ) || startNewPage )
1474 pageExported =
true;
1480void QgsLayoutExporter::updatePrinterPageSize(
QgsLayout *layout, QPagedPaintDevice *device,
int page )
1485 QPageLayout pageLayout( QPageSize( pageSizeMM.
toQSizeF(), QPageSize::Millimeter ),
1486 QPageLayout::Portrait,
1487 QMarginsF( 0, 0, 0, 0 ) );
1488 pageLayout.setMode( QPageLayout::FullPageMode );
1489 device->setPageLayout( pageLayout );
1490 device->setPageMargins( QMarginsF( 0, 0, 0, 0 ) );
1492#if defined( HAVE_QTPRINTER )
1493 if ( QPrinter *printer =
dynamic_cast<QPrinter *
>( device ) )
1495 printer->setFullPage(
true );
1500QgsLayoutExporter::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
1504 QSvgGenerator generator;
1505 if ( includeMetadata )
1508 generator.setTitle( l->name() );
1509 else if ( mLayout->project() )
1510 generator.setTitle( mLayout->project()->title() );
1513 generator.setOutputDevice( &svgBuffer );
1514 generator.setSize( QSize(
static_cast< int >( std::round( width ) ),
1515 static_cast< int >( std::round( height ) ) ) );
1516 generator.setViewBox( QRect( 0, 0,
1517 static_cast< int >( std::round( width ) ),
1518 static_cast< int >( std::round( height ) ) ) );
1519 generator.setResolution(
static_cast< int >( std::round( settings.dpi ) ) );
1521 QPainter svgPainter( &generator );
1522 if ( settings.cropToContents )
1533 svgBuffer.open( QIODevice::ReadOnly );
1537 if ( ! doc.setContent( &svgBuffer,
false, &errorMsg, &errorLine ) )
1539 mErrorFileName = filename;
1542 if ( 1 == svgLayerId )
1544 svg = QDomDocument( doc.doctype() );
1545 svg.appendChild( svg.importNode( doc.firstChild(),
false ) );
1546 svgDocRoot = svg.importNode( doc.elementsByTagName( QStringLiteral(
"svg" ) ).at( 0 ),
false );
1547 svgDocRoot.toElement().setAttribute( QStringLiteral(
"xmlns:inkscape" ), QStringLiteral(
"http://www.inkscape.org/namespaces/inkscape" ) );
1548 svg.appendChild( svgDocRoot );
1550 QDomNode mainGroup = svg.importNode( doc.elementsByTagName( QStringLiteral(
"g" ) ).at( 0 ),
true );
1551 mainGroup.toElement().setAttribute( QStringLiteral(
"id" ), layerName );
1552 mainGroup.toElement().setAttribute( QStringLiteral(
"inkscape:label" ), layerName );
1553 mainGroup.toElement().setAttribute( QStringLiteral(
"inkscape:groupmode" ), QStringLiteral(
"layer" ) );
1554 QDomNode defs = svg.importNode( doc.elementsByTagName( QStringLiteral(
"defs" ) ).at( 0 ),
true );
1555 svgDocRoot.appendChild( defs );
1556 svgDocRoot.appendChild( mainGroup );
1561void QgsLayoutExporter::appendMetadataToSvg( QDomDocument &svg )
const
1564 QDomElement metadataElement = svg.createElement( QStringLiteral(
"metadata" ) );
1565 QDomElement rdfElement = svg.createElement( QStringLiteral(
"rdf:RDF" ) );
1566 rdfElement.setAttribute( QStringLiteral(
"xmlns:rdf" ), QStringLiteral(
"http://www.w3.org/1999/02/22-rdf-syntax-ns#" ) );
1567 rdfElement.setAttribute( QStringLiteral(
"xmlns:rdfs" ), QStringLiteral(
"http://www.w3.org/2000/01/rdf-schema#" ) );
1568 rdfElement.setAttribute( QStringLiteral(
"xmlns:dc" ), QStringLiteral(
"http://purl.org/dc/elements/1.1/" ) );
1569 QDomElement descriptionElement = svg.createElement( QStringLiteral(
"rdf:Description" ) );
1570 QDomElement workElement = svg.createElement( QStringLiteral(
"cc:Work" ) );
1571 workElement.setAttribute( QStringLiteral(
"rdf:about" ), QString() );
1573 auto addTextNode = [&workElement, &descriptionElement, &svg](
const QString & tag,
const QString & value )
1576 QDomElement element = svg.createElement( tag );
1577 QDomText t = svg.createTextNode( value );
1578 element.appendChild( t );
1579 workElement.appendChild( element );
1582 descriptionElement.setAttribute( tag, value );
1585 addTextNode( QStringLiteral(
"dc:format" ), QStringLiteral(
"image/svg+xml" ) );
1586 addTextNode( QStringLiteral(
"dc:title" ), metadata.
title() );
1587 addTextNode( QStringLiteral(
"dc:date" ), metadata.
creationDateTime().toString( Qt::ISODate ) );
1588 addTextNode( QStringLiteral(
"dc:identifier" ), metadata.
identifier() );
1589 addTextNode( QStringLiteral(
"dc:description" ), metadata.
abstract() );
1591 auto addAgentNode = [&workElement, &descriptionElement, &svg](
const QString & tag,
const QString & value )
1594 QDomElement inkscapeElement = svg.createElement( tag );
1595 QDomElement agentElement = svg.createElement( QStringLiteral(
"cc:Agent" ) );
1596 QDomElement titleElement = svg.createElement( QStringLiteral(
"dc:title" ) );
1597 QDomText t = svg.createTextNode( value );
1598 titleElement.appendChild( t );
1599 agentElement.appendChild( titleElement );
1600 inkscapeElement.appendChild( agentElement );
1601 workElement.appendChild( inkscapeElement );
1604 QDomElement bagElement = svg.createElement( QStringLiteral(
"rdf:Bag" ) );
1605 QDomElement liElement = svg.createElement( QStringLiteral(
"rdf:li" ) );
1606 t = svg.createTextNode( value );
1607 liElement.appendChild( t );
1608 bagElement.appendChild( liElement );
1610 QDomElement element = svg.createElement( tag );
1611 element.appendChild( bagElement );
1612 descriptionElement.appendChild( element );
1615 addAgentNode( QStringLiteral(
"dc:creator" ), metadata.
author() );
1616 addAgentNode( QStringLiteral(
"dc:publisher" ), getCreator() );
1620 QDomElement element = svg.createElement( QStringLiteral(
"dc:subject" ) );
1621 QDomElement bagElement = svg.createElement( QStringLiteral(
"rdf:Bag" ) );
1623 for (
auto it = keywords.constBegin(); it != keywords.constEnd(); ++it )
1625 const QStringList words = it.value();
1626 for (
const QString &keyword : words )
1628 QDomElement liElement = svg.createElement( QStringLiteral(
"rdf:li" ) );
1629 QDomText t = svg.createTextNode( keyword );
1630 liElement.appendChild( t );
1631 bagElement.appendChild( liElement );
1634 element.appendChild( bagElement );
1635 workElement.appendChild( element );
1636 descriptionElement.appendChild( element );
1639 rdfElement.appendChild( descriptionElement );
1640 rdfElement.appendChild( workElement );
1641 metadataElement.appendChild( rdfElement );
1642 svg.documentElement().appendChild( metadataElement );
1643 svg.documentElement().setAttribute( QStringLiteral(
"xmlns:cc" ), QStringLiteral(
"http://creativecommons.org/ns#" ) );
1646std::unique_ptr<double[]> QgsLayoutExporter::computeGeoTransform(
const QgsLayoutItemMap *map,
const QRectF ®ion,
double dpi )
const
1649 map = mLayout->referenceMap();
1655 dpi = mLayout->renderContext().dpi();
1658 QRectF exportRegion = region;
1659 if ( !exportRegion.isValid() )
1661 int pageNumber = map->
page();
1664 double pageY = page->pos().y();
1665 QSizeF pageSize = page->rect().size();
1666 exportRegion = QRectF( 0, pageY, pageSize.width(), pageSize.height() );
1670 QRectF mapItemSceneRect = map->mapRectToScene( map->rect() );
1673 double outputHeightMM = exportRegion.height();
1674 double outputWidthMM = exportRegion.width();
1678 double mapXCenter = mapExtent.
center().
x();
1679 double mapYCenter = mapExtent.
center().
y();
1681 double sinAlpha = std::sin( alpha );
1682 double cosAlpha = std::cos( alpha );
1685 QPointF mapItemPos = map->pos();
1687 mapItemPos.rx() -= exportRegion.left();
1688 mapItemPos.ry() -= exportRegion.top();
1691 double xRatio = mapExtent.
width() / mapItemSceneRect.width();
1692 double yRatio = mapExtent.
height() / mapItemSceneRect.height();
1693 double xmin = mapExtent.
xMinimum() - mapItemPos.x() * xRatio;
1694 double ymax = mapExtent.
yMaximum() + mapItemPos.y() * yRatio;
1695 QgsRectangle paperExtent( xmin, ymax - outputHeightMM * yRatio, xmin + outputWidthMM * xRatio, ymax );
1698 double X0 = paperExtent.xMinimum();
1699 double Y0 = paperExtent.yMaximum();
1704 double X1 = X0 - mapXCenter;
1705 double Y1 = Y0 - mapYCenter;
1706 double X2 = X1 * cosAlpha + Y1 * sinAlpha;
1707 double Y2 = -X1 * sinAlpha + Y1 * cosAlpha;
1708 X0 = X2 + mapXCenter;
1709 Y0 = Y2 + mapYCenter;
1713 int pageWidthPixels =
static_cast< int >( dpi * outputWidthMM / 25.4 );
1714 int pageHeightPixels =
static_cast< int >( dpi * outputHeightMM / 25.4 );
1715 double pixelWidthScale = paperExtent.width() / pageWidthPixels;
1716 double pixelHeightScale = paperExtent.height() / pageHeightPixels;
1719 std::unique_ptr<double[]> t(
new double[6] );
1721 t[1] = cosAlpha * pixelWidthScale;
1722 t[2] = -sinAlpha * pixelWidthScale;
1724 t[4] = -sinAlpha * pixelHeightScale;
1725 t[5] = -cosAlpha * pixelHeightScale;
1730void QgsLayoutExporter::writeWorldFile(
const QString &worldFileName,
double a,
double b,
double c,
double d,
double e,
double f )
const
1732 QFile worldFile( worldFileName );
1733 if ( !worldFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
1737 QTextStream fout( &worldFile );
1741 fout << QString::number( a,
'f', 12 ) <<
"\r\n";
1742 fout << QString::number( d,
'f', 12 ) <<
"\r\n";
1743 fout << QString::number( b,
'f', 12 ) <<
"\r\n";
1744 fout << QString::number( e,
'f', 12 ) <<
"\r\n";
1745 fout << QString::number(
c,
'f', 12 ) <<
"\r\n";
1746 fout << QString::number( f,
'f', 12 ) <<
"\r\n";
1751 return georeferenceOutputPrivate( file, map, exportRegion, dpi,
false );
1754bool QgsLayoutExporter::georeferenceOutputPrivate(
const QString &file,
QgsLayoutItemMap *map,
const QRectF &exportRegion,
double dpi,
bool includeGeoreference,
bool includeMetadata )
const
1759 if ( !map && includeGeoreference )
1760 map = mLayout->referenceMap();
1762 std::unique_ptr<double[]> t;
1764 if ( map && includeGeoreference )
1767 dpi = mLayout->renderContext().dpi();
1769 t = computeGeoTransform( map, exportRegion, dpi );
1774 CPLSetConfigOption(
"GDAL_PDF_DPI", QString::number( dpi ).toUtf8().constData() );
1779 GDALSetGeoTransform( outputDS.get(), t.get() );
1781 if ( includeMetadata )
1783 QString creationDateString;
1784 const QDateTime creationDateTime = mLayout->project()->metadata().creationDateTime();
1785 if ( creationDateTime.isValid() )
1787 creationDateString = QStringLiteral(
"D:%1" ).arg( mLayout->project()->metadata().creationDateTime().toString( QStringLiteral(
"yyyyMMddHHmmss" ) ) );
1788 if ( creationDateTime.timeZone().isValid() )
1790 int offsetFromUtc = creationDateTime.timeZone().offsetFromUtc( creationDateTime );
1791 creationDateString += ( offsetFromUtc >= 0 ) ?
'+' :
'-';
1792 offsetFromUtc = std::abs( offsetFromUtc );
1793 int offsetHours = offsetFromUtc / 3600;
1794 int offsetMins = ( offsetFromUtc % 3600 ) / 60;
1795 creationDateString += QStringLiteral(
"%1'%2'" ).arg( offsetHours ).arg( offsetMins );
1798 GDALSetMetadataItem( outputDS.get(),
"CREATION_DATE", creationDateString.toUtf8().constData(),
nullptr );
1800 GDALSetMetadataItem( outputDS.get(),
"AUTHOR", mLayout->project()->metadata().author().toUtf8().constData(),
nullptr );
1801 const QString creator = getCreator();
1802 GDALSetMetadataItem( outputDS.get(),
"CREATOR", creator.toUtf8().constData(),
nullptr );
1803 GDALSetMetadataItem( outputDS.get(),
"PRODUCER", creator.toUtf8().constData(),
nullptr );
1804 GDALSetMetadataItem( outputDS.get(),
"SUBJECT", mLayout->project()->metadata().abstract().toUtf8().constData(),
nullptr );
1805 GDALSetMetadataItem( outputDS.get(),
"TITLE", mLayout->project()->metadata().title().toUtf8().constData(),
nullptr );
1808 QStringList allKeywords;
1809 for (
auto it = keywords.constBegin(); it != keywords.constEnd(); ++it )
1811 allKeywords.append( QStringLiteral(
"%1: %2" ).arg( it.key(), it.value().join(
',' ) ) );
1813 const QString keywordString = allKeywords.join(
';' );
1814 GDALSetMetadataItem( outputDS.get(),
"KEYWORDS", keywordString.toUtf8().constData(),
nullptr );
1820 CPLSetConfigOption(
"GDAL_PDF_DPI",
nullptr );
1827 if ( items.count() == 1 )
1831 QString name = layoutItem->displayName();
1833 if ( name.startsWith(
'<' ) && name.endsWith(
'>' ) )
1834 name = name.mid( 1, name.length() - 2 );
1838 else if ( items.count() > 1 )
1840 QStringList currentLayerItemTypes;
1841 for ( QGraphicsItem *item : items )
1847 if ( !currentLayerItemTypes.contains( itemType ) && !currentLayerItemTypes.contains( itemTypePlural ) )
1848 currentLayerItemTypes << itemType;
1849 else if ( currentLayerItemTypes.contains( itemType ) )
1851 currentLayerItemTypes.replace( currentLayerItemTypes.indexOf( itemType ), itemTypePlural );
1856 if ( !currentLayerItemTypes.contains( QObject::tr(
"Other" ) ) )
1857 currentLayerItemTypes.append( QObject::tr(
"Other" ) );
1860 return currentLayerItemTypes.join( QLatin1String(
", " ) );
1862 return QObject::tr(
"Layer %1" ).arg( layerId );
1867 const std::function<QString(
QgsLayoutItem *item )> &getItemExportGroupFunc )
1869 LayoutItemHider itemHider( items );
1874 QString previousItemGroup;
1875 unsigned int layerId = 1;
1877 itemHider.hideAll();
1878 const QList< QGraphicsItem * > itemsToIterate = itemHider.itemsToIterate();
1879 QList< QGraphicsItem * > currentLayerItems;
1880 for ( QGraphicsItem *item : itemsToIterate )
1884 bool canPlaceInExistingLayer =
false;
1885 QString thisItemExportGroupName;
1889 thisItemExportGroupName = getItemExportGroupFunc( layoutItem );
1890 if ( !thisItemExportGroupName.isEmpty() )
1892 if ( thisItemExportGroupName != previousItemGroup && !currentLayerItems.empty() )
1895 layerDetails.
groupName = thisItemExportGroupName;
1898 switch ( itemExportBehavior )
1902 switch ( prevItemBehavior )
1905 canPlaceInExistingLayer =
true;
1909 canPlaceInExistingLayer = prevType == -1 || prevType == layoutItem->
type();
1914 canPlaceInExistingLayer =
false;
1922 switch ( prevItemBehavior )
1926 canPlaceInExistingLayer = prevType == -1 || prevType == layoutItem->
type();
1931 canPlaceInExistingLayer =
false;
1939 canPlaceInExistingLayer =
false;
1944 canPlaceInExistingLayer =
false;
1947 prevItemBehavior = itemExportBehavior;
1948 prevType = layoutItem->
type();
1949 previousItemGroup = thisItemExportGroupName;
1954 previousItemGroup.clear();
1957 if ( canPlaceInExistingLayer )
1959 currentLayerItems << item;
1964 if ( !currentLayerItems.isEmpty() )
1968 ExportResult result = exportFunc( layerId, layerDetails );
1972 currentLayerItems.clear();
1975 itemHider.hideAll();
1980 int layoutItemLayerIdx = 0;
1982 mLayout->renderContext().setCurrentExportLayer( layoutItemLayerIdx );
1988 mLayout->renderContext().setCurrentExportLayer( layoutItemLayerIdx );
1992 ExportResult result = exportFunc( layerId, layerDetails );
1997 layoutItemLayerIdx++;
1999 layerDetails.mapLayerId.clear();
2001 mLayout->renderContext().setCurrentExportLayer( -1 );
2004 currentLayerItems.clear();
2008 currentLayerItems << item;
2010 layerDetails.groupName = thisItemExportGroupName;
2013 if ( !currentLayerItems.isEmpty() )
2016 ExportResult result = exportFunc( layerId, layerDetails );
2031 return simplifyMethod;
2054 int pageNumber = map->
page();
2056 double pageY = page->pos().y();
2057 QSizeF pageSize = page->rect().size();
2058 QRectF pageRect( 0, pageY, pageSize.width(), pageSize.height() );
2074 double destinationHeight = exportRegion.height();
2075 double destinationWidth = exportRegion.width();
2077 QRectF mapItemSceneRect = map->mapRectToScene( map->rect() );
2082 double xRatio = mapExtent.
width() / mapItemSceneRect.width();
2083 double yRatio = mapExtent.
height() / mapItemSceneRect.height();
2085 double xCenter = mapExtent.
center().
x();
2086 double yCenter = mapExtent.
center().
y();
2089 QPointF mapItemPos = map->pos();
2091 mapItemPos.rx() -= exportRegion.left();
2092 mapItemPos.ry() -= exportRegion.top();
2094 double xmin = mapExtent.
xMinimum() - mapItemPos.x() * xRatio;
2095 double ymax = mapExtent.
yMaximum() + mapItemPos.y() * yRatio;
2096 QgsRectangle paperExtent( xmin, ymax - destinationHeight * yRatio, xmin + destinationWidth * xRatio, ymax );
2098 double X0 = paperExtent.
xMinimum();
2099 double Y0 = paperExtent.
yMinimum();
2102 dpi = mLayout->renderContext().dpi();
2104 int widthPx =
static_cast< int >( dpi * destinationWidth / 25.4 );
2105 int heightPx =
static_cast< int >( dpi * destinationHeight / 25.4 );
2107 double Ww = paperExtent.
width() / widthPx;
2108 double Hh = paperExtent.
height() / heightPx;
2117 s[5] = Y0 + paperExtent.
height();
2121 r[0] = std::cos( alpha );
2122 r[1] = -std::sin( alpha );
2123 r[2] = xCenter * ( 1 - std::cos( alpha ) ) + yCenter * std::sin( alpha );
2124 r[3] = std::sin( alpha );
2125 r[4] = std::cos( alpha );
2126 r[5] = - xCenter * std::sin( alpha ) + yCenter * ( 1 - std::cos( alpha ) );
2129 a = r[0] * s[0] + r[1] * s[3];
2130 b = r[0] * s[1] + r[1] * s[4];
2131 c = r[0] * s[2] + r[1] * s[5] + r[2];
2132 d = r[3] * s[0] + r[4] * s[3];
2133 e = r[3] * s[1] + r[4] * s[4];
2134 f = r[3] * s[2] + r[4] * s[5] + r[5];
2142 QList< QgsLayoutItem *> items;
2148 if ( currentItem->isVisible() && currentItem->requiresRasterization() )
2159 QList< QgsLayoutItem *> items;
2165 if ( currentItem->isVisible() && currentItem->containsAdvancedEffects() )
2178 if ( mLayout->pageCollection()->pageCount() == 1 )
2181 bounds = mLayout->layoutBounds(
true );
2186 bounds = mLayout->pageItemBounds( page,
true );
2188 if ( bounds.width() <= 0 || bounds.height() <= 0 )
2196 bounds = bounds.adjusted( -settings.
cropMargins.
left() * pixelToLayoutUnits,
2208int QgsLayoutExporter::firstPageToBeExported(
QgsLayout *layout )
2211 for (
int i = 0; i < pageCount; ++i )
2225 if ( details.
page == 0 )
2235void QgsLayoutExporter::captureLabelingResults()
2237 qDeleteAll( mLabelingResults );
2238 mLabelingResults.clear();
2240 QList< QgsLayoutItemMap * > maps;
2241 mLayout->layoutItems( maps );
2245 mLabelingResults[ map->
uuid() ] = map->mExportLabelingResults.release();
2249bool QgsLayoutExporter::saveImage(
const QImage &image,
const QString &imageFilename,
const QString &imageFormat,
QgsProject *projectForMetadata,
int quality )
2251 QImageWriter w( imageFilename, imageFormat.toLocal8Bit().constData() );
2252 if ( imageFormat.compare( QLatin1String(
"tiff" ), Qt::CaseInsensitive ) == 0 || imageFormat.compare( QLatin1String(
"tif" ), Qt::CaseInsensitive ) == 0 )
2254 w.setCompression( 1 );
2258 w.setQuality( quality );
2260 if ( projectForMetadata )
2262 w.setText( QStringLiteral(
"Author" ), projectForMetadata->
metadata().
author() );
2263 const QString creator = getCreator();
2264 w.setText( QStringLiteral(
"Creator" ), creator );
2265 w.setText( QStringLiteral(
"Producer" ), creator );
2266 w.setText( QStringLiteral(
"Subject" ), projectForMetadata->
metadata().
abstract() );
2267 w.setText( QStringLiteral(
"Created" ), projectForMetadata->
metadata().
creationDateTime().toString( Qt::ISODate ) );
2268 w.setText( QStringLiteral(
"Title" ), projectForMetadata->
metadata().
title() );
2271 QStringList allKeywords;
2272 for (
auto it = keywords.constBegin(); it != keywords.constEnd(); ++it )
2274 allKeywords.append( QStringLiteral(
"%1: %2" ).arg( it.key(), it.value().join(
',' ) ) );
2276 const QString keywordString = allKeywords.join(
';' );
2277 w.setText( QStringLiteral(
"Keywords" ), keywordString );
2279 return w.write( image );
2282QString QgsLayoutExporter::getCreator()
2287void QgsLayoutExporter::setXmpMetadata( QPdfWriter *pdfWriter,
QgsLayout *layout )
2289#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
2290 QUuid documentId = pdfWriter->documentId();
2292 QUuid documentId = QUuid::createUuid();
2297 const QString metaDataDate = creationDateTime.isValid() ? creationDateTime.toOffsetFromUtc( creationDateTime.offsetFromUtc() ).toString( Qt::ISODate ) : QString();
2298 const QString title = pdfWriter->title();
2299 const QString creator = getCreator();
2300 const QString producer = creator;
2305 const QLatin1String xmlNS(
"http://www.w3.org/XML/1998/namespace" );
2306 const QLatin1String adobeNS(
"adobe:ns:meta/" );
2307 const QLatin1String rdfNS(
"http://www.w3.org/1999/02/22-rdf-syntax-ns#" );
2308 const QLatin1String dcNS(
"http://purl.org/dc/elements/1.1/" );
2309 const QLatin1String xmpNS(
"http://ns.adobe.com/xap/1.0/" );
2310 const QLatin1String xmpMMNS(
"http://ns.adobe.com/xap/1.0/mm/" );
2311 const QLatin1String pdfNS(
"http://ns.adobe.com/pdf/1.3/" );
2312 const QLatin1String pdfaidNS(
"http://www.aiim.org/pdfa/ns/id/" );
2314 QByteArray xmpMetadata;
2315 QBuffer output( &xmpMetadata );
2316 output.open( QIODevice::WriteOnly );
2317 output.write(
"<?xpacket begin='' ?>" );
2319 QXmlStreamWriter w( &output );
2320 w.setAutoFormatting(
true );
2321 w.writeNamespace( adobeNS,
"x" );
2322 w.writeNamespace( rdfNS,
"rdf" );
2323 w.writeNamespace( dcNS,
"dc" );
2324 w.writeNamespace( xmpNS,
"xmp" );
2325 w.writeNamespace( xmpMMNS,
"xmpMM" );
2326 w.writeNamespace( pdfNS,
"pdf" );
2327 w.writeNamespace( pdfaidNS,
"pdfaid" );
2329 w.writeStartElement( adobeNS,
"xmpmeta" );
2330 w.writeStartElement( rdfNS,
"RDF" );
2333 w.writeStartElement( rdfNS,
"Description" );
2334 w.writeAttribute( rdfNS,
"about",
"" );
2335 w.writeStartElement( dcNS,
"title" );
2336 w.writeStartElement( rdfNS,
"Alt" );
2337 w.writeStartElement( rdfNS,
"li" );
2338 w.writeAttribute( xmlNS,
"lang",
"x-default" );
2339 w.writeCharacters( title );
2340 w.writeEndElement();
2341 w.writeEndElement();
2342 w.writeEndElement();
2344 w.writeStartElement( dcNS,
"creator" );
2345 w.writeStartElement( rdfNS,
"Seq" );
2346 w.writeStartElement( rdfNS,
"li" );
2347 w.writeCharacters( author );
2348 w.writeEndElement();
2349 w.writeEndElement();
2350 w.writeEndElement();
2352 w.writeEndElement();
2355 w.writeStartElement( rdfNS,
"Description" );
2356 w.writeAttribute( rdfNS,
"about",
"" );
2357 w.writeAttribute( pdfNS,
"Producer", producer );
2358 w.writeAttribute( pdfNS,
"Trapped",
"False" );
2359 w.writeEndElement();
2362 w.writeStartElement( rdfNS,
"Description" );
2363 w.writeAttribute( rdfNS,
"about",
"" );
2364 w.writeAttribute( xmpNS,
"CreatorTool", creator );
2365 w.writeAttribute( xmpNS,
"CreateDate", metaDataDate );
2366 w.writeAttribute( xmpNS,
"ModifyDate", metaDataDate );
2367 w.writeAttribute( xmpNS,
"MetadataDate", metaDataDate );
2368 w.writeEndElement();
2371 w.writeStartElement( rdfNS,
"Description" );
2372 w.writeAttribute( rdfNS,
"about",
"" );
2373 w.writeAttribute( xmpMMNS,
"DocumentID",
"uuid:" + documentId.toString( QUuid::WithoutBraces ) );
2374 w.writeAttribute( xmpMMNS,
"VersionID",
"1" );
2375 w.writeAttribute( xmpMMNS,
"RenditionClass",
"default" );
2376 w.writeEndElement();
2378#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
2381 switch ( pdfWriter->pdfVersion() )
2383 case QPagedPaintDevice::PdfVersion_1_4:
2384 case QPagedPaintDevice::PdfVersion_A1b:
2385 case QPagedPaintDevice::PdfVersion_1_6:
2387 case QPagedPaintDevice::PdfVersion_X4:
2388 const QLatin1String pdfxidNS(
"http://www.npes.org/pdfx/ns/id/" );
2389 w.writeNamespace( pdfxidNS,
"pdfxid" );
2390 w.writeStartElement( rdfNS,
"Description" );
2391 w.writeAttribute( rdfNS,
"about",
"" );
2392 w.writeAttribute( pdfxidNS,
"GTS_PDFXVersion",
"PDF/X-4" );
2393 w.writeEndElement();
2399 w.writeEndElement();
2400 w.writeEndElement();
2402 w.writeEndDocument();
2403 output.write(
"<?xpacket end='w'?>" );
2405 pdfWriter->setDocumentXmpMetadata( xmpMetadata );
RasterizedRenderingPolicy
Policies controlling when rasterisation of content during renders is permitted.
@ Default
Allow raster-based rendering in situations where it is required for correct rendering or where it wil...
@ PreferVector
Prefer vector-based rendering, when the result will still be visually near-identical to a raster-base...
@ ForceVector
Always force vector-based rendering, even when the result will be visually different to a raster-base...
static QString version()
Version string.
@ Millimeters
Millimeters.
@ GeometrySimplification
The geometries can be simplified using the current map2pixel context state.
@ SnappedToGridGlobal
Snap to a global grid based on the tolerance. Good for consistent results for incoming vertices,...
@ Warning
Warning message.
QFlags< LayoutRenderFlag > LayoutRenderFlags
Flags for controlling how a layout is rendered.
TextRenderFormat
Options for rendering text.
@ AlwaysOutlines
Always render text using path objects (AKA outlines/curves). This setting guarantees the best quality...
@ PreferredGdal
Preferred format for conversion of CRS to WKT for use with the GDAL library.
@ SynchronousLegendGraphics
Query legend graphics synchronously.
@ LosslessImageRendering
Render images losslessly whenever possible, instead of the default lossy jpeg rendering used for some...
@ Antialiasing
Use antialiasing when drawing items.
@ RenderLabelsByMapLayer
When rendering map items to multi-layered exports, render labels belonging to different layers into s...
An abstract base class for QgsLayout based classes which can be exported by QgsLayoutExporter.
virtual bool endRender()=0
Ends the render, performing any required cleanup tasks.
virtual QgsLayout * layout()=0
Returns the layout associated with the iterator.
virtual bool next()=0
Iterates to next feature, returning false if no more features exist to iterate over.
virtual bool beginRender()=0
Called when rendering begins, before iteration commences.
virtual QString filePath(const QString &baseFilePath, const QString &extension)=0
Returns the file path for the current feature, based on a specified base file path and extension.
virtual int count() const =0
Returns the number of features to iterate over.
static QgsLayoutItemRegistry * layoutItemRegistry()
Returns the application's layout item registry, used for layout item types.
QString toWkt(Qgis::CrsWktVariant variant=Qgis::CrsWktVariant::Wkt1Gdal, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
Base class for feedback objects to be used for cancellation of something running in a worker thread.
bool isCanceled() const
Tells whether the operation has been canceled already.
void setProgress(double progress)
Sets the current progress for the feedback object.
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.
Provides a method of storing measurements for use in QGIS layouts using a variety of different measur...
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.
Provides a method of storing points, consisting of an x and y coordinate, for use in QGIS layouts.
double x() const
Returns x coordinate of point.
double y() const
Returns y coordinate of point.
void setDpi(double dpi)
Sets the dpi for outputting the layout.
void setSimplifyMethod(const QgsVectorSimplifyMethod &method)
Sets the simplification setting to use when rendering vector layers.
void setTextRenderFormat(Qgis::TextRenderFormat format)
Sets the text render format, which dictates how text is rendered (e.g.
Qgis::LayoutRenderFlags flags() const
Returns the current combination of flags used for rendering the layout.
void setRasterizedRenderingPolicy(Qgis::RasterizedRenderingPolicy policy)
Sets the policy controlling when rasterization of content during renders is permitted.
double dpi() const
Returns the dpi for outputting the layout.
void setPredefinedScales(const QVector< qreal > &scales)
Sets the list of predefined scales to use with the layout.
void setMaskSettings(const QgsMaskRenderSettings &settings)
Sets the mask render settings, which control how masks are drawn and behave during map renders.
void setFlags(Qgis::LayoutRenderFlags flags)
Sets the combination of flags that will be used for rendering the layout.
const QgsLayoutMeasurementConverter & measurementConverter() const
Returns the layout measurement converter to be used in the layout.
Provides a method of storing sizes, consisting of a width and height, for use in QGIS layouts.
QSizeF toQSizeF() const
Converts the layout size to a QSizeF.
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
QgsLayoutRenderContext & renderContext()
Returns a reference to the layout's render context, which stores information relating to the current ...
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, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE())
Adds a message to the log instance (and creates it if necessary).
static void fixEngineFlags(QPaintEngine *engine)
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
Contains settings for simplifying geometries fetched from a vector layer.
void setThreshold(float threshold)
Sets the simplification threshold of the vector layer managed.
void setForceLocalOptimization(bool localOptimization)
Sets where the simplification executes, after fetch the geometries from provider, or when supported,...
void setSimplifyHints(Qgis::VectorRenderingSimplificationFlags simplifyHints)
Sets the simplification hints of the vector layer managed.
void setSimplifyAlgorithm(Qgis::VectorSimplificationAlgorithm simplifyAlgorithm)
Sets the local simplification algorithm of the vector layer managed.
std::unique_ptr< std::remove_pointer< GDALDatasetH >::type, GDALDatasetCloser > dataset_unique_ptr
Scoped GDAL dataset.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
#define Q_NOWARN_DEPRECATED_POP
#define Q_NOWARN_DEPRECATED_PUSH
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
QString nameForLayerWithItems(const QList< QGraphicsItem * > &items, unsigned int layerId)
Contains details of a particular input component to be used during PDF composition.
QString mapLayerId
Associated map layer ID, or an empty string if this component layer is not associated with a map laye...
QString group
Optional group name, for arranging layers in top-level groups.
double opacity
Component opacity.
QString name
User-friendly name for the generated PDF layer.
QPainter::CompositionMode compositionMode
Component composition mode.
QString sourcePdfPath
File path to the (already created) PDF to use as the source for this component layer.
Contains details of a control point used during georeferencing Geospatial PDF outputs.
QMap< QString, QString > customLayerTreeGroups
Optional map of map layer ID to custom logical layer tree group in created PDF file.
QMap< QString, bool > initialLayerVisibility
Optional map of map layer ID to initial visibility state.
QList< QgsAbstractGeospatialPdfExporter::GeoReferencedSection > georeferencedSections
List of georeferenced sections.
QStringList layerOrder
Optional list of layer IDs, in the order desired to appear in the generated Geospatial PDF file.
QMap< QString, QString > layerIdToPdfLayerTreeNameMap
Optional map of map layer ID to custom layer tree name to show in the created PDF file.
QgsAbstractMetadataBase::KeywordMap keywords
Metadata keyword map.
QString creator
Metadata creator tag.
QSizeF pageSizeMm
Page size, in millimeters.
QString author
Metadata author tag.
QString subject
Metadata subject tag.
QString title
Metadata title tag.
QStringList layerTreeGroupOrder
Specifies the ordering of layer tree groups in the generated Geospatial PDF file.
QDateTime creationDateTime
Metadata creation datetime.
bool includeFeatures
true if feature vector information (such as attributes) should be exported.
QString producer
Metadata producer tag.
bool useIso32000ExtensionFormatGeoreferencing
true if ISO32000 extension format georeferencing should be used.
QSet< QString > mutuallyExclusiveGroups
Contains a list of group names which should be considered as mutually exclusive.
QgsCoordinateReferenceSystem crs
Coordinate reference system for georeferenced section.
QList< QgsAbstractGeospatialPdfExporter::ControlPoint > controlPoints
List of control points corresponding to this georeferenced section.
QgsPolygon pageBoundsPolygon
Bounds of the georeferenced section on the page, in millimeters, as a free-form polygon.
Contains settings relating to exporting layouts to raster images.
QgsMargins cropMargins
Crop to content margins, in pixels.
QList< int > pages
List of specific pages to export, or an empty list to export all pages.
bool generateWorldFile
Set to true to generate an external world file alongside exported images.
QSize imageSize
Manual size in pixels for output image.
bool exportMetadata
Indicates whether image export should include metadata generated from the layout's project's metadata...
int quality
Image quality, typically used for JPEG compression (whose quality ranges from 1 to 100) if quality is...
Qgis::LayoutRenderFlags flags
Layout context flags, which control how the export will be created.
bool cropToContents
Set to true if image should be cropped so only parts of the layout containing items are exported.
double dpi
Resolution to export layout at. If dpi <= 0 the default layout dpi will be used.
QVector< qreal > predefinedMapScales
A list of predefined scales to use with the layout.
Contains details of a page being exported by the class.
QString baseName
Base part of filename (i.e. file name without extension or '.')
QString extension
File suffix/extension (without the leading '.')
QString directory
Target folder.
int page
Page number, where 0 = first page.
Contains settings relating to exporting layouts to PDF.
bool forceVectorOutput
Set to true to force vector object exports, even when the resultant appearance will differ from the l...
bool rasterizeWholeImage
Set to true to force whole layout to be rasterized while exporting.
QStringList exportThemes
Optional list of map themes to export as Geospatial PDF layer groups.
bool exportMetadata
Indicates whether PDF export should include metadata generated from the layout's project's metadata.
bool appendGeoreference
Indicates whether PDF export should append georeference data.
Qgis::LayoutRenderFlags flags
Layout context flags, which control how the export will be created.
bool writeGeoPdf
true if geospatial PDF files should be created, instead of normal PDF files.
double dpi
Resolution to export layout at. If dpi <= 0 the default layout dpi will be used.
QVector< qreal > predefinedMapScales
A list of predefined scales to use with the layout.
bool exportLayersAsSeperateFiles
true if individual layers from the layout should be rendered to separate PDF files.
bool simplifyGeometries
Indicates whether vector geometries should be simplified to avoid redundant extraneous detail,...
Qgis::TextRenderFormat textRenderFormat
Text rendering format, which controls how text should be rendered in the export (e....
Contains settings relating to printing layouts.
QVector< qreal > predefinedMapScales
A list of predefined scales to use with the layout.
Qgis::LayoutRenderFlags flags
Layout context flags, which control how the export will be created.
double dpi
Resolution to export layout at. If dpi <= 0 the default layout dpi will be used.
bool rasterizeWholeImage
Set to true to force whole layout to be rasterized while exporting.
Contains settings relating to exporting layouts to SVG.
bool forceVectorOutput
Set to true to force vector object exports, even when the resultant appearance will differ from the l...
Qgis::LayoutRenderFlags flags
Layout context flags, which control how the export will be created.
Qgis::TextRenderFormat textRenderFormat
Text rendering format, which controls how text should be rendered in the export (e....
bool exportAsLayers
Set to true to export as a layered SVG file.
bool simplifyGeometries
Indicates whether vector geometries should be simplified to avoid redundant extraneous detail,...
bool exportMetadata
Indicates whether SVG export should include RDF metadata generated from the layout's project's metada...
double dpi
Resolution to export layout at. If dpi <= 0 the default layout dpi will be used.
QVector< qreal > predefinedMapScales
A list of predefined scales to use with the layout.
bool exportLabelsToSeparateLayers
Set to true to export labels to separate layers (grouped by map layer) in layered SVG exports.
bool cropToContents
Set to true if image should be cropped so only parts of the layout containing items are exported.
QgsMargins cropMargins
Crop to content margins, in layout units.
Contains details of a particular export layer relating to a layout item.
QString name
User-friendly name for the export layer.
QString groupName
Associated group name, if this layer is associated with an export group.