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< QgsLayoutGeoPdfExporter > geoPdfExporter;
570 geoPdfExporter = std::make_unique< QgsLayoutGeoPdfExporter >( mLayout );
572 mLayout->renderContext().setFlags( settings.
flags );
585 mLayout->renderContext().setExportThemes( settings.
exportThemes );
597 const QList<QGraphicsItem *> items = mLayout->items( Qt::AscendingOrder );
599 QList< QgsLayoutGeoPdfExporter::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 ? geoPdfExporter->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 = geoPdfExporter->layerOrder();
721 if ( !geoPdfExporter->finalize( pdfComponents, filePath, details ) )
724 mErrorMessage = geoPdfExporter->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 device->setOutputIntent( outputIntent );
1317 pdfVersion = QPagedPaintDevice::PdfVersion_X4;
1323 device->setPdfVersion( pdfVersion );
1324 setXmpMetadata( device,
layout );
1330#if defined(HAS_KDE_QT5_PDF_TRANSFORM_FIX) || QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)
1337void QgsLayoutExporter::preparePrint(
QgsLayout *layout, QPagedPaintDevice *device,
bool setFirstPageSize )
1339 if ( QPdfWriter *pdf =
dynamic_cast<QPdfWriter *
>( device ) )
1343#if defined( HAVE_QTPRINTER )
1344 else if ( QPrinter *printer =
dynamic_cast<QPrinter *
>( device ) )
1346 printer->setFullPage(
true );
1347 printer->setColorMode( QPrinter::Color );
1353 if ( setFirstPageSize )
1355 updatePrinterPageSize(
layout, device, firstPageToBeExported(
layout ) );
1361 if ( mLayout->pageCollection()->pageCount() == 0 )
1364 preparePrint( mLayout, device,
true );
1366 if ( !p.begin( device ) )
1372 printPrivate( device, p );
1377QgsLayoutExporter::ExportResult QgsLayoutExporter::printPrivate( QPagedPaintDevice *device, QPainter &painter,
bool startNewPage,
double dpi,
bool rasterize )
1381 int toPage = mLayout->pageCollection()->pageCount() - 1;
1383#if defined( HAVE_QTPRINTER )
1384 if ( QPrinter *printer =
dynamic_cast<QPrinter *
>( device ) )
1386 if ( printer->fromPage() >= 1 )
1387 fromPage = printer->fromPage() - 1;
1388 if ( printer->toPage() >= 1 )
1389 toPage = printer->toPage() - 1;
1393 bool pageExported =
false;
1396 for (
int i = fromPage; i <= toPage; ++i )
1398 if ( !mLayout->pageCollection()->shouldExportPage( i ) )
1403 updatePrinterPageSize( mLayout, device, i );
1404 if ( ( pageExported && i > fromPage ) || startNewPage )
1410 if ( !image.isNull() )
1412 QRectF targetArea( 0, 0, image.width(), image.height() );
1413 painter.drawImage( targetArea, image, targetArea );
1419 pageExported =
true;
1424 for (
int i = fromPage; i <= toPage; ++i )
1426 if ( !mLayout->pageCollection()->shouldExportPage( i ) )
1431 updatePrinterPageSize( mLayout, device, i );
1433 if ( ( pageExported && i > fromPage ) || startNewPage )
1438 pageExported =
true;
1444void QgsLayoutExporter::updatePrinterPageSize(
QgsLayout *layout, QPagedPaintDevice *device,
int page )
1449 QPageLayout pageLayout( QPageSize( pageSizeMM.
toQSizeF(), QPageSize::Millimeter ),
1450 QPageLayout::Portrait,
1451 QMarginsF( 0, 0, 0, 0 ) );
1452 pageLayout.setMode( QPageLayout::FullPageMode );
1453 device->setPageLayout( pageLayout );
1454 device->setPageMargins( QMarginsF( 0, 0, 0, 0 ) );
1456#if defined( HAVE_QTPRINTER )
1457 if ( QPrinter *printer =
dynamic_cast<QPrinter *
>( device ) )
1459 printer->setFullPage(
true );
1464QgsLayoutExporter::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
1468 QSvgGenerator generator;
1469 if ( includeMetadata )
1472 generator.setTitle( l->name() );
1473 else if ( mLayout->project() )
1474 generator.setTitle( mLayout->project()->title() );
1477 generator.setOutputDevice( &svgBuffer );
1478 generator.setSize( QSize(
static_cast< int >( std::round( width ) ),
1479 static_cast< int >( std::round( height ) ) ) );
1480 generator.setViewBox( QRect( 0, 0,
1481 static_cast< int >( std::round( width ) ),
1482 static_cast< int >( std::round( height ) ) ) );
1483 generator.setResolution(
static_cast< int >( std::round( settings.dpi ) ) );
1485 QPainter svgPainter( &generator );
1486 if ( settings.cropToContents )
1497 svgBuffer.open( QIODevice::ReadOnly );
1501 if ( ! doc.setContent( &svgBuffer,
false, &errorMsg, &errorLine ) )
1503 mErrorFileName = filename;
1506 if ( 1 == svgLayerId )
1508 svg = QDomDocument( doc.doctype() );
1509 svg.appendChild( svg.importNode( doc.firstChild(),
false ) );
1510 svgDocRoot = svg.importNode( doc.elementsByTagName( QStringLiteral(
"svg" ) ).at( 0 ),
false );
1511 svgDocRoot.toElement().setAttribute( QStringLiteral(
"xmlns:inkscape" ), QStringLiteral(
"http://www.inkscape.org/namespaces/inkscape" ) );
1512 svg.appendChild( svgDocRoot );
1514 QDomNode mainGroup = svg.importNode( doc.elementsByTagName( QStringLiteral(
"g" ) ).at( 0 ),
true );
1515 mainGroup.toElement().setAttribute( QStringLiteral(
"id" ), layerName );
1516 mainGroup.toElement().setAttribute( QStringLiteral(
"inkscape:label" ), layerName );
1517 mainGroup.toElement().setAttribute( QStringLiteral(
"inkscape:groupmode" ), QStringLiteral(
"layer" ) );
1518 QDomNode defs = svg.importNode( doc.elementsByTagName( QStringLiteral(
"defs" ) ).at( 0 ),
true );
1519 svgDocRoot.appendChild( defs );
1520 svgDocRoot.appendChild( mainGroup );
1525void QgsLayoutExporter::appendMetadataToSvg( QDomDocument &svg )
const
1528 QDomElement metadataElement = svg.createElement( QStringLiteral(
"metadata" ) );
1529 QDomElement rdfElement = svg.createElement( QStringLiteral(
"rdf:RDF" ) );
1530 rdfElement.setAttribute( QStringLiteral(
"xmlns:rdf" ), QStringLiteral(
"http://www.w3.org/1999/02/22-rdf-syntax-ns#" ) );
1531 rdfElement.setAttribute( QStringLiteral(
"xmlns:rdfs" ), QStringLiteral(
"http://www.w3.org/2000/01/rdf-schema#" ) );
1532 rdfElement.setAttribute( QStringLiteral(
"xmlns:dc" ), QStringLiteral(
"http://purl.org/dc/elements/1.1/" ) );
1533 QDomElement descriptionElement = svg.createElement( QStringLiteral(
"rdf:Description" ) );
1534 QDomElement workElement = svg.createElement( QStringLiteral(
"cc:Work" ) );
1535 workElement.setAttribute( QStringLiteral(
"rdf:about" ), QString() );
1537 auto addTextNode = [&workElement, &descriptionElement, &svg](
const QString & tag,
const QString & value )
1540 QDomElement element = svg.createElement( tag );
1541 QDomText t = svg.createTextNode( value );
1542 element.appendChild( t );
1543 workElement.appendChild( element );
1546 descriptionElement.setAttribute( tag, value );
1549 addTextNode( QStringLiteral(
"dc:format" ), QStringLiteral(
"image/svg+xml" ) );
1550 addTextNode( QStringLiteral(
"dc:title" ), metadata.
title() );
1551 addTextNode( QStringLiteral(
"dc:date" ), metadata.
creationDateTime().toString( Qt::ISODate ) );
1552 addTextNode( QStringLiteral(
"dc:identifier" ), metadata.
identifier() );
1553 addTextNode( QStringLiteral(
"dc:description" ), metadata.
abstract() );
1555 auto addAgentNode = [&workElement, &descriptionElement, &svg](
const QString & tag,
const QString & value )
1558 QDomElement inkscapeElement = svg.createElement( tag );
1559 QDomElement agentElement = svg.createElement( QStringLiteral(
"cc:Agent" ) );
1560 QDomElement titleElement = svg.createElement( QStringLiteral(
"dc:title" ) );
1561 QDomText t = svg.createTextNode( value );
1562 titleElement.appendChild( t );
1563 agentElement.appendChild( titleElement );
1564 inkscapeElement.appendChild( agentElement );
1565 workElement.appendChild( inkscapeElement );
1568 QDomElement bagElement = svg.createElement( QStringLiteral(
"rdf:Bag" ) );
1569 QDomElement liElement = svg.createElement( QStringLiteral(
"rdf:li" ) );
1570 t = svg.createTextNode( value );
1571 liElement.appendChild( t );
1572 bagElement.appendChild( liElement );
1574 QDomElement element = svg.createElement( tag );
1575 element.appendChild( bagElement );
1576 descriptionElement.appendChild( element );
1579 addAgentNode( QStringLiteral(
"dc:creator" ), metadata.
author() );
1580 addAgentNode( QStringLiteral(
"dc:publisher" ), getCreator() );
1584 QDomElement element = svg.createElement( QStringLiteral(
"dc:subject" ) );
1585 QDomElement bagElement = svg.createElement( QStringLiteral(
"rdf:Bag" ) );
1587 for (
auto it = keywords.constBegin(); it != keywords.constEnd(); ++it )
1589 const QStringList words = it.value();
1590 for (
const QString &keyword : words )
1592 QDomElement liElement = svg.createElement( QStringLiteral(
"rdf:li" ) );
1593 QDomText t = svg.createTextNode( keyword );
1594 liElement.appendChild( t );
1595 bagElement.appendChild( liElement );
1598 element.appendChild( bagElement );
1599 workElement.appendChild( element );
1600 descriptionElement.appendChild( element );
1603 rdfElement.appendChild( descriptionElement );
1604 rdfElement.appendChild( workElement );
1605 metadataElement.appendChild( rdfElement );
1606 svg.documentElement().appendChild( metadataElement );
1607 svg.documentElement().setAttribute( QStringLiteral(
"xmlns:cc" ), QStringLiteral(
"http://creativecommons.org/ns#" ) );
1610std::unique_ptr<double[]> QgsLayoutExporter::computeGeoTransform(
const QgsLayoutItemMap *map,
const QRectF ®ion,
double dpi )
const
1613 map = mLayout->referenceMap();
1619 dpi = mLayout->renderContext().dpi();
1622 QRectF exportRegion = region;
1623 if ( !exportRegion.isValid() )
1625 int pageNumber = map->
page();
1628 double pageY = page->pos().y();
1629 QSizeF pageSize = page->rect().size();
1630 exportRegion = QRectF( 0, pageY, pageSize.width(), pageSize.height() );
1634 QRectF mapItemSceneRect = map->mapRectToScene( map->rect() );
1637 double outputHeightMM = exportRegion.height();
1638 double outputWidthMM = exportRegion.width();
1642 double mapXCenter = mapExtent.
center().
x();
1643 double mapYCenter = mapExtent.
center().
y();
1645 double sinAlpha = std::sin( alpha );
1646 double cosAlpha = std::cos( alpha );
1649 QPointF mapItemPos = map->pos();
1651 mapItemPos.rx() -= exportRegion.left();
1652 mapItemPos.ry() -= exportRegion.top();
1655 double xRatio = mapExtent.
width() / mapItemSceneRect.width();
1656 double yRatio = mapExtent.
height() / mapItemSceneRect.height();
1657 double xmin = mapExtent.
xMinimum() - mapItemPos.x() * xRatio;
1658 double ymax = mapExtent.
yMaximum() + mapItemPos.y() * yRatio;
1659 QgsRectangle paperExtent( xmin, ymax - outputHeightMM * yRatio, xmin + outputWidthMM * xRatio, ymax );
1662 double X0 = paperExtent.xMinimum();
1663 double Y0 = paperExtent.yMaximum();
1668 double X1 = X0 - mapXCenter;
1669 double Y1 = Y0 - mapYCenter;
1670 double X2 = X1 * cosAlpha + Y1 * sinAlpha;
1671 double Y2 = -X1 * sinAlpha + Y1 * cosAlpha;
1672 X0 = X2 + mapXCenter;
1673 Y0 = Y2 + mapYCenter;
1677 int pageWidthPixels =
static_cast< int >( dpi * outputWidthMM / 25.4 );
1678 int pageHeightPixels =
static_cast< int >( dpi * outputHeightMM / 25.4 );
1679 double pixelWidthScale = paperExtent.width() / pageWidthPixels;
1680 double pixelHeightScale = paperExtent.height() / pageHeightPixels;
1683 std::unique_ptr<double[]> t(
new double[6] );
1685 t[1] = cosAlpha * pixelWidthScale;
1686 t[2] = -sinAlpha * pixelWidthScale;
1688 t[4] = -sinAlpha * pixelHeightScale;
1689 t[5] = -cosAlpha * pixelHeightScale;
1694void QgsLayoutExporter::writeWorldFile(
const QString &worldFileName,
double a,
double b,
double c,
double d,
double e,
double f )
const
1696 QFile worldFile( worldFileName );
1697 if ( !worldFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
1701 QTextStream fout( &worldFile );
1705 fout << QString::number( a,
'f', 12 ) <<
"\r\n";
1706 fout << QString::number( d,
'f', 12 ) <<
"\r\n";
1707 fout << QString::number( b,
'f', 12 ) <<
"\r\n";
1708 fout << QString::number( e,
'f', 12 ) <<
"\r\n";
1709 fout << QString::number(
c,
'f', 12 ) <<
"\r\n";
1710 fout << QString::number( f,
'f', 12 ) <<
"\r\n";
1715 return georeferenceOutputPrivate( file, map, exportRegion, dpi,
false );
1718bool QgsLayoutExporter::georeferenceOutputPrivate(
const QString &file,
QgsLayoutItemMap *map,
const QRectF &exportRegion,
double dpi,
bool includeGeoreference,
bool includeMetadata )
const
1723 if ( !map && includeGeoreference )
1724 map = mLayout->referenceMap();
1726 std::unique_ptr<double[]> t;
1728 if ( map && includeGeoreference )
1731 dpi = mLayout->renderContext().dpi();
1733 t = computeGeoTransform( map, exportRegion, dpi );
1738 CPLSetConfigOption(
"GDAL_PDF_DPI", QString::number( dpi ).toUtf8().constData() );
1743 GDALSetGeoTransform( outputDS.get(), t.get() );
1745 if ( includeMetadata )
1747 QString creationDateString;
1748 const QDateTime creationDateTime = mLayout->project()->metadata().creationDateTime();
1749 if ( creationDateTime.isValid() )
1751 creationDateString = QStringLiteral(
"D:%1" ).arg( mLayout->project()->metadata().creationDateTime().toString( QStringLiteral(
"yyyyMMddHHmmss" ) ) );
1752 if ( creationDateTime.timeZone().isValid() )
1754 int offsetFromUtc = creationDateTime.timeZone().offsetFromUtc( creationDateTime );
1755 creationDateString += ( offsetFromUtc >= 0 ) ?
'+' :
'-';
1756 offsetFromUtc = std::abs( offsetFromUtc );
1757 int offsetHours = offsetFromUtc / 3600;
1758 int offsetMins = ( offsetFromUtc % 3600 ) / 60;
1759 creationDateString += QStringLiteral(
"%1'%2'" ).arg( offsetHours ).arg( offsetMins );
1762 GDALSetMetadataItem( outputDS.get(),
"CREATION_DATE", creationDateString.toUtf8().constData(),
nullptr );
1764 GDALSetMetadataItem( outputDS.get(),
"AUTHOR", mLayout->project()->metadata().author().toUtf8().constData(),
nullptr );
1765 const QString creator = getCreator();
1766 GDALSetMetadataItem( outputDS.get(),
"CREATOR", creator.toUtf8().constData(),
nullptr );
1767 GDALSetMetadataItem( outputDS.get(),
"PRODUCER", creator.toUtf8().constData(),
nullptr );
1768 GDALSetMetadataItem( outputDS.get(),
"SUBJECT", mLayout->project()->metadata().abstract().toUtf8().constData(),
nullptr );
1769 GDALSetMetadataItem( outputDS.get(),
"TITLE", mLayout->project()->metadata().title().toUtf8().constData(),
nullptr );
1772 QStringList allKeywords;
1773 for (
auto it = keywords.constBegin(); it != keywords.constEnd(); ++it )
1775 allKeywords.append( QStringLiteral(
"%1: %2" ).arg( it.key(), it.value().join(
',' ) ) );
1777 const QString keywordString = allKeywords.join(
';' );
1778 GDALSetMetadataItem( outputDS.get(),
"KEYWORDS", keywordString.toUtf8().constData(),
nullptr );
1784 CPLSetConfigOption(
"GDAL_PDF_DPI",
nullptr );
1791 if ( items.count() == 1 )
1795 QString name = layoutItem->displayName();
1797 if ( name.startsWith(
'<' ) && name.endsWith(
'>' ) )
1798 name = name.mid( 1, name.length() - 2 );
1802 else if ( items.count() > 1 )
1804 QStringList currentLayerItemTypes;
1805 for ( QGraphicsItem *item : items )
1811 if ( !currentLayerItemTypes.contains( itemType ) && !currentLayerItemTypes.contains( itemTypePlural ) )
1812 currentLayerItemTypes << itemType;
1813 else if ( currentLayerItemTypes.contains( itemType ) )
1815 currentLayerItemTypes.replace( currentLayerItemTypes.indexOf( itemType ), itemTypePlural );
1820 if ( !currentLayerItemTypes.contains( QObject::tr(
"Other" ) ) )
1821 currentLayerItemTypes.append( QObject::tr(
"Other" ) );
1824 return currentLayerItemTypes.join( QLatin1String(
", " ) );
1826 return QObject::tr(
"Layer %1" ).arg( layerId );
1831 const std::function<QString(
QgsLayoutItem *item )> &getItemExportGroupFunc )
1833 LayoutItemHider itemHider( items );
1838 QString previousItemGroup;
1839 unsigned int layerId = 1;
1841 itemHider.hideAll();
1842 const QList< QGraphicsItem * > itemsToIterate = itemHider.itemsToIterate();
1843 QList< QGraphicsItem * > currentLayerItems;
1844 for ( QGraphicsItem *item : itemsToIterate )
1848 bool canPlaceInExistingLayer =
false;
1849 QString thisItemExportGroupName;
1853 thisItemExportGroupName = getItemExportGroupFunc( layoutItem );
1854 if ( !thisItemExportGroupName.isEmpty() )
1856 if ( thisItemExportGroupName != previousItemGroup && !currentLayerItems.empty() )
1859 layerDetails.
groupName = thisItemExportGroupName;
1862 switch ( itemExportBehavior )
1866 switch ( prevItemBehavior )
1869 canPlaceInExistingLayer =
true;
1873 canPlaceInExistingLayer = prevType == -1 || prevType == layoutItem->
type();
1878 canPlaceInExistingLayer =
false;
1886 switch ( prevItemBehavior )
1890 canPlaceInExistingLayer = prevType == -1 || prevType == layoutItem->
type();
1895 canPlaceInExistingLayer =
false;
1903 canPlaceInExistingLayer =
false;
1908 canPlaceInExistingLayer =
false;
1911 prevItemBehavior = itemExportBehavior;
1912 prevType = layoutItem->
type();
1913 previousItemGroup = thisItemExportGroupName;
1918 previousItemGroup.clear();
1921 if ( canPlaceInExistingLayer )
1923 currentLayerItems << item;
1928 if ( !currentLayerItems.isEmpty() )
1932 ExportResult result = exportFunc( layerId, layerDetails );
1936 currentLayerItems.clear();
1939 itemHider.hideAll();
1944 int layoutItemLayerIdx = 0;
1946 mLayout->renderContext().setCurrentExportLayer( layoutItemLayerIdx );
1952 mLayout->renderContext().setCurrentExportLayer( layoutItemLayerIdx );
1956 ExportResult result = exportFunc( layerId, layerDetails );
1961 layoutItemLayerIdx++;
1963 layerDetails.mapLayerId.clear();
1965 mLayout->renderContext().setCurrentExportLayer( -1 );
1968 currentLayerItems.clear();
1972 currentLayerItems << item;
1974 layerDetails.groupName = thisItemExportGroupName;
1977 if ( !currentLayerItems.isEmpty() )
1980 ExportResult result = exportFunc( layerId, layerDetails );
1995 return simplifyMethod;
2018 int pageNumber = map->
page();
2020 double pageY = page->pos().y();
2021 QSizeF pageSize = page->rect().size();
2022 QRectF pageRect( 0, pageY, pageSize.width(), pageSize.height() );
2038 double destinationHeight = exportRegion.height();
2039 double destinationWidth = exportRegion.width();
2041 QRectF mapItemSceneRect = map->mapRectToScene( map->rect() );
2046 double xRatio = mapExtent.
width() / mapItemSceneRect.width();
2047 double yRatio = mapExtent.
height() / mapItemSceneRect.height();
2049 double xCenter = mapExtent.
center().
x();
2050 double yCenter = mapExtent.
center().
y();
2053 QPointF mapItemPos = map->pos();
2055 mapItemPos.rx() -= exportRegion.left();
2056 mapItemPos.ry() -= exportRegion.top();
2058 double xmin = mapExtent.
xMinimum() - mapItemPos.x() * xRatio;
2059 double ymax = mapExtent.
yMaximum() + mapItemPos.y() * yRatio;
2060 QgsRectangle paperExtent( xmin, ymax - destinationHeight * yRatio, xmin + destinationWidth * xRatio, ymax );
2062 double X0 = paperExtent.
xMinimum();
2063 double Y0 = paperExtent.
yMinimum();
2066 dpi = mLayout->renderContext().dpi();
2068 int widthPx =
static_cast< int >( dpi * destinationWidth / 25.4 );
2069 int heightPx =
static_cast< int >( dpi * destinationHeight / 25.4 );
2071 double Ww = paperExtent.
width() / widthPx;
2072 double Hh = paperExtent.
height() / heightPx;
2081 s[5] = Y0 + paperExtent.
height();
2085 r[0] = std::cos( alpha );
2086 r[1] = -std::sin( alpha );
2087 r[2] = xCenter * ( 1 - std::cos( alpha ) ) + yCenter * std::sin( alpha );
2088 r[3] = std::sin( alpha );
2089 r[4] = std::cos( alpha );
2090 r[5] = - xCenter * std::sin( alpha ) + yCenter * ( 1 - std::cos( alpha ) );
2093 a = r[0] * s[0] + r[1] * s[3];
2094 b = r[0] * s[1] + r[1] * s[4];
2095 c = r[0] * s[2] + r[1] * s[5] + r[2];
2096 d = r[3] * s[0] + r[4] * s[3];
2097 e = r[3] * s[1] + r[4] * s[4];
2098 f = r[3] * s[2] + r[4] * s[5] + r[5];
2106 QList< QgsLayoutItem *> items;
2112 if ( currentItem->isVisible() && currentItem->requiresRasterization() )
2123 QList< QgsLayoutItem *> items;
2129 if ( currentItem->isVisible() && currentItem->containsAdvancedEffects() )
2142 if ( mLayout->pageCollection()->pageCount() == 1 )
2145 bounds = mLayout->layoutBounds(
true );
2150 bounds = mLayout->pageItemBounds( page,
true );
2152 if ( bounds.width() <= 0 || bounds.height() <= 0 )
2160 bounds = bounds.adjusted( -settings.
cropMargins.
left() * pixelToLayoutUnits,
2172int QgsLayoutExporter::firstPageToBeExported(
QgsLayout *layout )
2175 for (
int i = 0; i < pageCount; ++i )
2189 if ( details.
page == 0 )
2199void QgsLayoutExporter::captureLabelingResults()
2201 qDeleteAll( mLabelingResults );
2202 mLabelingResults.clear();
2204 QList< QgsLayoutItemMap * > maps;
2205 mLayout->layoutItems( maps );
2209 mLabelingResults[ map->
uuid() ] = map->mExportLabelingResults.release();
2213bool QgsLayoutExporter::saveImage(
const QImage &image,
const QString &imageFilename,
const QString &imageFormat,
QgsProject *projectForMetadata )
2215 QImageWriter w( imageFilename, imageFormat.toLocal8Bit().constData() );
2216 if ( imageFormat.compare( QLatin1String(
"tiff" ), Qt::CaseInsensitive ) == 0 || imageFormat.compare( QLatin1String(
"tif" ), Qt::CaseInsensitive ) == 0 )
2218 w.setCompression( 1 );
2220 if ( projectForMetadata )
2222 w.setText( QStringLiteral(
"Author" ), projectForMetadata->
metadata().
author() );
2223 const QString creator = getCreator();
2224 w.setText( QStringLiteral(
"Creator" ), creator );
2225 w.setText( QStringLiteral(
"Producer" ), creator );
2226 w.setText( QStringLiteral(
"Subject" ), projectForMetadata->
metadata().
abstract() );
2227 w.setText( QStringLiteral(
"Created" ), projectForMetadata->
metadata().
creationDateTime().toString( Qt::ISODate ) );
2228 w.setText( QStringLiteral(
"Title" ), projectForMetadata->
metadata().
title() );
2231 QStringList allKeywords;
2232 for (
auto it = keywords.constBegin(); it != keywords.constEnd(); ++it )
2234 allKeywords.append( QStringLiteral(
"%1: %2" ).arg( it.key(), it.value().join(
',' ) ) );
2236 const QString keywordString = allKeywords.join(
';' );
2237 w.setText( QStringLiteral(
"Keywords" ), keywordString );
2239 return w.write( image );
2242QString QgsLayoutExporter::getCreator()
2247void QgsLayoutExporter::setXmpMetadata( QPdfWriter *pdfWriter,
QgsLayout *layout )
2249#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
2250 QUuid documentId = pdfWriter->documentId();
2252 QUuid documentId = QUuid::createUuid();
2257 const QString metaDataDate = creationDateTime.isValid() ? creationDateTime.toOffsetFromUtc( creationDateTime.offsetFromUtc() ).toString( Qt::ISODate ) : QString();
2258 const QString title = pdfWriter->title();
2259 const QString creator = getCreator();
2260 const QString producer = creator;
2265 const QLatin1String xmlNS(
"http://www.w3.org/XML/1998/namespace" );
2266 const QLatin1String adobeNS(
"adobe:ns:meta/" );
2267 const QLatin1String rdfNS(
"http://www.w3.org/1999/02/22-rdf-syntax-ns#" );
2268 const QLatin1String dcNS(
"http://purl.org/dc/elements/1.1/" );
2269 const QLatin1String xmpNS(
"http://ns.adobe.com/xap/1.0/" );
2270 const QLatin1String xmpMMNS(
"http://ns.adobe.com/xap/1.0/mm/" );
2271 const QLatin1String pdfNS(
"http://ns.adobe.com/pdf/1.3/" );
2272 const QLatin1String pdfaidNS(
"http://www.aiim.org/pdfa/ns/id/" );
2273 const QLatin1String pdfxidNS(
"http://www.npes.org/pdfx/ns/id/" );
2275 QByteArray xmpMetadata;
2276 QBuffer output( &xmpMetadata );
2277 output.open( QIODevice::WriteOnly );
2278 output.write(
"<?xpacket begin='' ?>" );
2280 QXmlStreamWriter w( &output );
2281 w.setAutoFormatting(
true );
2282 w.writeNamespace( adobeNS,
"x" );
2283 w.writeNamespace( rdfNS,
"rdf" );
2284 w.writeNamespace( dcNS,
"dc" );
2285 w.writeNamespace( xmpNS,
"xmp" );
2286 w.writeNamespace( xmpMMNS,
"xmpMM" );
2287 w.writeNamespace( pdfNS,
"pdf" );
2288 w.writeNamespace( pdfaidNS,
"pdfaid" );
2289 w.writeNamespace( pdfxidNS,
"pdfxid" );
2291 w.writeStartElement( adobeNS,
"xmpmeta" );
2292 w.writeStartElement( rdfNS,
"RDF" );
2295 w.writeStartElement( rdfNS,
"Description" );
2296 w.writeAttribute( rdfNS,
"about",
"" );
2297 w.writeStartElement( dcNS,
"title" );
2298 w.writeStartElement( rdfNS,
"Alt" );
2299 w.writeStartElement( rdfNS,
"li" );
2300 w.writeAttribute( xmlNS,
"lang",
"x-default" );
2301 w.writeCharacters( title );
2302 w.writeEndElement();
2303 w.writeEndElement();
2304 w.writeEndElement();
2306 w.writeStartElement( dcNS,
"creator" );
2307 w.writeStartElement( rdfNS,
"Seq" );
2308 w.writeStartElement( rdfNS,
"li" );
2309 w.writeCharacters( author );
2310 w.writeEndElement();
2311 w.writeEndElement();
2312 w.writeEndElement();
2314 w.writeEndElement();
2317 w.writeStartElement( rdfNS,
"Description" );
2318 w.writeAttribute( rdfNS,
"about",
"" );
2319 w.writeAttribute( pdfNS,
"Producer", producer );
2320 w.writeAttribute( pdfNS,
"Trapped",
"False" );
2321 w.writeEndElement();
2324 w.writeStartElement( rdfNS,
"Description" );
2325 w.writeAttribute( rdfNS,
"about",
"" );
2326 w.writeAttribute( xmpNS,
"CreatorTool", creator );
2327 w.writeAttribute( xmpNS,
"CreateDate", metaDataDate );
2328 w.writeAttribute( xmpNS,
"ModifyDate", metaDataDate );
2329 w.writeAttribute( xmpNS,
"MetadataDate", metaDataDate );
2330 w.writeEndElement();
2333 w.writeStartElement( rdfNS,
"Description" );
2334 w.writeAttribute( rdfNS,
"about",
"" );
2335 w.writeAttribute( xmpMMNS,
"DocumentID",
"uuid:" + documentId.toString( QUuid::WithoutBraces ) );
2336 w.writeAttribute( xmpMMNS,
"VersionID",
"1" );
2337 w.writeAttribute( xmpMMNS,
"RenditionClass",
"default" );
2338 w.writeEndElement();
2340#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
2343 switch ( pdfWriter->pdfVersion() )
2345 case QPagedPaintDevice::PdfVersion_1_4:
2346 case QPagedPaintDevice::PdfVersion_A1b:
2347 case QPagedPaintDevice::PdfVersion_1_6:
2349 case QPagedPaintDevice::PdfVersion_X4:
2350 w.writeStartElement( rdfNS,
"Description" );
2351 w.writeAttribute( rdfNS,
"about",
"" );
2352 w.writeAttribute( pdfxidNS,
"GTS_PDFXVersion",
"PDF/X-4" );
2353 w.writeEndElement();
2359 w.writeEndElement();
2360 w.writeEndElement();
2362 w.writeEndDocument();
2363 output.write(
"<?xpacket end='w'?>" );
2365 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 sourcePdfPath
File path to the (already created) PDF to use as the source for this component layer.
QString mapLayerId
Associated map layer ID, or an empty string if this component layer is not associated with a map laye...
QPainter::CompositionMode compositionMode
Component composition mode.
QString group
Optional group name, for arranging layers in top-level groups.
QString name
User-friendly name for the generated PDF layer.
double opacity
Component opacity.
Contains details of a control point used during georeferencing GeoPDF outputs.
QgsAbstractMetadataBase::KeywordMap keywords
Metadata keyword map.
bool useIso32000ExtensionFormatGeoreferencing
true if ISO32000 extension format georeferencing should be used.
QMap< QString, QString > layerIdToPdfLayerTreeNameMap
Optional map of map layer ID to custom layer tree name to show in the created PDF file.
bool useOgcBestPracticeFormatGeoreferencing
true if OGC "best practice" format georeferencing should be used.
QDateTime creationDateTime
Metadata creation datetime.
QSizeF pageSizeMm
Page size, in millimeters.
QList< QgsAbstractGeoPdfExporter::GeoReferencedSection > georeferencedSections
List of georeferenced sections.
QStringList layerTreeGroupOrder
Specifies the ordering of layer tree groups in the generated GeoPDF file.
QString author
Metadata author tag.
QMap< QString, bool > initialLayerVisibility
Optional map of map layer ID to initial visibility state.
QString producer
Metadata producer tag.
QString creator
Metadata creator tag.
QSet< QString > mutuallyExclusiveGroups
Contains a list of group names which should be considered as mutually exclusive.
QMap< QString, QString > customLayerTreeGroups
Optional map of map layer ID to custom logical layer tree group in created PDF file.
bool includeFeatures
true if feature vector information (such as attributes) should be exported.
QStringList layerOrder
Optional list of layer IDs, in the order desired to appear in the generated GeoPDF file.
QString subject
Metadata subject tag.
QString title
Metadata title tag.
QgsCoordinateReferenceSystem crs
Coordinate reference system for georeferenced section.
QgsPolygon pageBoundsPolygon
Bounds of the georeferenced section on the page, in millimeters, as a free-form polygon.
QList< QgsAbstractGeoPdfExporter::ControlPoint > controlPoints
List of control points corresponding to this georeferenced section.
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 GeoPDF 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 GeoPDF 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.