QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
qgslayoutexporter.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgslayoutexporter.cpp
3  -------------------
4  begin : October 2017
5  copyright : (C) 2017 by Nyall Dawson
6  email : nyall dot dawson at gmail dot com
7  ***************************************************************************/
8 /***************************************************************************
9  * *
10  * This program is free software; you can redistribute it and/or modify *
11  * it under the terms of the GNU General Public License as published by *
12  * the Free Software Foundation; either version 2 of the License, or *
13  * (at your option) any later version. *
14  * *
15  ***************************************************************************/
16 
17 #include "qgslayoutexporter.h"
18 #ifndef QT_NO_PRINTER
19 
20 #include "qgslayout.h"
21 #include "qgslayoutitemmap.h"
23 #include "qgsogrutils.h"
24 #include "qgspaintenginehack.h"
27 #include "qgsfeedback.h"
29 #include "qgslinestring.h"
30 #include "qgsmessagelog.h"
31 #include "qgslabelingresults.h"
32 #include <QImageWriter>
33 #include <QSize>
34 #include <QSvgGenerator>
35 #include <QBuffer>
36 #include <QTimeZone>
37 #include <QTextStream>
38 
39 #include "gdal.h"
40 #include "cpl_conv.h"
41 
43 class LayoutContextPreviewSettingRestorer
44 {
45  public:
46 
47  LayoutContextPreviewSettingRestorer( QgsLayout *layout )
48  : mLayout( layout )
49  , mPreviousSetting( layout->renderContext().mIsPreviewRender )
50  {
51  mLayout->renderContext().mIsPreviewRender = false;
52  }
53 
54  ~LayoutContextPreviewSettingRestorer()
55  {
56  mLayout->renderContext().mIsPreviewRender = mPreviousSetting;
57  }
58 
59  LayoutContextPreviewSettingRestorer( const LayoutContextPreviewSettingRestorer &other ) = delete;
60  LayoutContextPreviewSettingRestorer &operator=( const LayoutContextPreviewSettingRestorer &other ) = delete;
61 
62  private:
63  QgsLayout *mLayout = nullptr;
64  bool mPreviousSetting = false;
65 };
66 
67 class LayoutGuideHider
68 {
69  public:
70 
71  LayoutGuideHider( QgsLayout *layout )
72  : mLayout( layout )
73  {
74  const QList< QgsLayoutGuide * > guides = mLayout->guides().guides();
75  for ( QgsLayoutGuide *guide : guides )
76  {
77  mPrevVisibility.insert( guide, guide->item()->isVisible() );
78  guide->item()->setVisible( false );
79  }
80  }
81 
82  ~LayoutGuideHider()
83  {
84  for ( auto it = mPrevVisibility.constBegin(); it != mPrevVisibility.constEnd(); ++it )
85  {
86  it.key()->item()->setVisible( it.value() );
87  }
88  }
89 
90  LayoutGuideHider( const LayoutGuideHider &other ) = delete;
91  LayoutGuideHider &operator=( const LayoutGuideHider &other ) = delete;
92 
93  private:
94  QgsLayout *mLayout = nullptr;
95  QHash< QgsLayoutGuide *, bool > mPrevVisibility;
96 };
97 
98 class LayoutItemHider
99 {
100  public:
101  explicit LayoutItemHider( const QList<QGraphicsItem *> &items )
102  {
103  mItemsToIterate.reserve( items.count() );
104  for ( QGraphicsItem *item : items )
105  {
106  const bool isVisible = item->isVisible();
107  mPrevVisibility[item] = isVisible;
108  if ( isVisible )
109  mItemsToIterate.append( item );
110  if ( QgsLayoutItem *layoutItem = dynamic_cast< QgsLayoutItem * >( item ) )
111  layoutItem->setProperty( "wasVisible", isVisible );
112 
113  item->hide();
114  }
115  }
116 
117  void hideAll()
118  {
119  for ( auto it = mPrevVisibility.constBegin(); it != mPrevVisibility.constEnd(); ++it )
120  {
121  it.key()->hide();
122  }
123  }
124 
125  ~LayoutItemHider()
126  {
127  for ( auto it = mPrevVisibility.constBegin(); it != mPrevVisibility.constEnd(); ++it )
128  {
129  it.key()->setVisible( it.value() );
130  if ( QgsLayoutItem *layoutItem = dynamic_cast< QgsLayoutItem * >( it.key() ) )
131  layoutItem->setProperty( "wasVisible", QVariant() );
132  }
133  }
134 
135  QList< QGraphicsItem * > itemsToIterate() const { return mItemsToIterate; }
136 
137  LayoutItemHider( const LayoutItemHider &other ) = delete;
138  LayoutItemHider &operator=( const LayoutItemHider &other ) = delete;
139 
140  private:
141 
142  QList<QGraphicsItem * > mItemsToIterate;
143  QHash<QGraphicsItem *, bool> mPrevVisibility;
144 };
145 
147 
149  : mLayout( layout )
150 {
151 
152 }
153 
155 {
156  qDeleteAll( mLabelingResults );
157 }
158 
160 {
161  return mLayout;
162 }
163 
164 void QgsLayoutExporter::renderPage( QPainter *painter, int page ) const
165 {
166  if ( !mLayout )
167  return;
168 
169  if ( mLayout->pageCollection()->pageCount() <= page || page < 0 )
170  {
171  return;
172  }
173 
174  QgsLayoutItemPage *pageItem = mLayout->pageCollection()->page( page );
175  if ( !pageItem )
176  {
177  return;
178  }
179 
180  LayoutContextPreviewSettingRestorer restorer( mLayout );
181  ( void )restorer;
182 
183  QRectF paperRect = QRectF( pageItem->pos().x(), pageItem->pos().y(), pageItem->rect().width(), pageItem->rect().height() );
184  renderRegion( painter, paperRect );
185 }
186 
187 QImage QgsLayoutExporter::renderPageToImage( int page, QSize imageSize, double dpi ) const
188 {
189  if ( !mLayout )
190  return QImage();
191 
192  if ( mLayout->pageCollection()->pageCount() <= page || page < 0 )
193  {
194  return QImage();
195  }
196 
197  QgsLayoutItemPage *pageItem = mLayout->pageCollection()->page( page );
198  if ( !pageItem )
199  {
200  return QImage();
201  }
202 
203  LayoutContextPreviewSettingRestorer restorer( mLayout );
204  ( void )restorer;
205 
206  QRectF paperRect = QRectF( pageItem->pos().x(), pageItem->pos().y(), pageItem->rect().width(), pageItem->rect().height() );
207 
208  const double imageAspectRatio = static_cast< double >( imageSize.width() ) / imageSize.height();
209  const double paperAspectRatio = paperRect.width() / paperRect.height();
210  if ( imageSize.isValid() && ( !qgsDoubleNear( imageAspectRatio, paperAspectRatio, 0.008 ) ) )
211  {
212  // specified image size is wrong aspect ratio for paper rect - so ignore it and just use dpi
213  // this can happen e.g. as a result of data defined page sizes
214  // see https://github.com/qgis/QGIS/issues/26422
215  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 );
216  imageSize = QSize();
217  }
218 
219  return renderRegionToImage( paperRect, imageSize, dpi );
220 }
221 
223 class LayoutItemCacheSettingRestorer
224 {
225  public:
226 
227  LayoutItemCacheSettingRestorer( QgsLayout *layout )
228  : mLayout( layout )
229  {
230  const QList< QGraphicsItem * > items = mLayout->items();
231  for ( QGraphicsItem *item : items )
232  {
233  mPrevCacheMode.insert( item, item->cacheMode() );
234  item->setCacheMode( QGraphicsItem::NoCache );
235  }
236  }
237 
238  ~LayoutItemCacheSettingRestorer()
239  {
240  for ( auto it = mPrevCacheMode.constBegin(); it != mPrevCacheMode.constEnd(); ++it )
241  {
242  it.key()->setCacheMode( it.value() );
243  }
244  }
245 
246  LayoutItemCacheSettingRestorer( const LayoutItemCacheSettingRestorer &other ) = delete;
247  LayoutItemCacheSettingRestorer &operator=( const LayoutItemCacheSettingRestorer &other ) = delete;
248 
249  private:
250  QgsLayout *mLayout = nullptr;
251  QHash< QGraphicsItem *, QGraphicsItem::CacheMode > mPrevCacheMode;
252 };
253 
255 
256 void QgsLayoutExporter::renderRegion( QPainter *painter, const QRectF &region ) const
257 {
258  QPaintDevice *paintDevice = painter->device();
259  if ( !paintDevice || !mLayout )
260  {
261  return;
262  }
263 
264  LayoutItemCacheSettingRestorer cacheRestorer( mLayout );
265  ( void )cacheRestorer;
266  LayoutContextPreviewSettingRestorer restorer( mLayout );
267  ( void )restorer;
268  LayoutGuideHider guideHider( mLayout );
269  ( void ) guideHider;
270 
271  painter->setRenderHint( QPainter::Antialiasing, mLayout->renderContext().flags() & QgsLayoutRenderContext::FlagAntialiasing );
272 
273  mLayout->render( painter, QRectF( 0, 0, paintDevice->width(), paintDevice->height() ), region );
274 }
275 
276 QImage QgsLayoutExporter::renderRegionToImage( const QRectF &region, QSize imageSize, double dpi ) const
277 {
278  if ( !mLayout )
279  return QImage();
280 
281  LayoutContextPreviewSettingRestorer restorer( mLayout );
282  ( void )restorer;
283 
284  double resolution = mLayout->renderContext().dpi();
285  double oneInchInLayoutUnits = mLayout->convertToLayoutUnits( QgsLayoutMeasurement( 1, QgsUnitTypes::LayoutInches ) );
286  if ( imageSize.isValid() )
287  {
288  //output size in pixels specified, calculate resolution using average of
289  //derived x/y dpi
290  resolution = ( imageSize.width() / region.width()
291  + imageSize.height() / region.height() ) / 2.0 * oneInchInLayoutUnits;
292  }
293  else if ( dpi > 0 )
294  {
295  //dpi overridden by function parameters
296  resolution = dpi;
297  }
298 
299  int width = imageSize.isValid() ? imageSize.width()
300  : static_cast< int >( resolution * region.width() / oneInchInLayoutUnits );
301  int height = imageSize.isValid() ? imageSize.height()
302  : static_cast< int >( resolution * region.height() / oneInchInLayoutUnits );
303 
304  QImage image( QSize( width, height ), QImage::Format_ARGB32 );
305  if ( !image.isNull() )
306  {
307  // see https://doc.qt.io/qt-5/qpainter.html#limitations
308  if ( width > 32768 || height > 32768 )
309  QgsMessageLog::logMessage( QObject::tr( "Error: output width or height is larger than 32768 pixel, result will be clipped" ) );
310  image.setDotsPerMeterX( static_cast< int >( std::round( resolution / 25.4 * 1000 ) ) );
311  image.setDotsPerMeterY( static_cast< int>( std::round( resolution / 25.4 * 1000 ) ) );
312  image.fill( Qt::transparent );
313  QPainter imagePainter( &image );
314  renderRegion( &imagePainter, region );
315  if ( !imagePainter.isActive() )
316  return QImage();
317  }
318 
319  return image;
320 }
321 
323 class LayoutContextSettingsRestorer
324 {
325  public:
326 
328  LayoutContextSettingsRestorer( QgsLayout *layout )
329  : mLayout( layout )
330  , mPreviousDpi( layout->renderContext().dpi() )
331  , mPreviousFlags( layout->renderContext().flags() )
332  , mPreviousTextFormat( layout->renderContext().textRenderFormat() )
333  , mPreviousExportLayer( layout->renderContext().currentExportLayer() )
334  , mPreviousSimplifyMethod( layout->renderContext().simplifyMethod() )
335  , mExportThemes( layout->renderContext().exportThemes() )
336  , mPredefinedScales( layout->renderContext().predefinedScales() )
337  {
338  }
340 
341  ~LayoutContextSettingsRestorer()
342  {
343  mLayout->renderContext().setDpi( mPreviousDpi );
344  mLayout->renderContext().setFlags( mPreviousFlags );
345  mLayout->renderContext().setTextRenderFormat( mPreviousTextFormat );
347  mLayout->renderContext().setCurrentExportLayer( mPreviousExportLayer );
349  mLayout->renderContext().setSimplifyMethod( mPreviousSimplifyMethod );
350  mLayout->renderContext().setExportThemes( mExportThemes );
351  mLayout->renderContext().setPredefinedScales( mPredefinedScales );
352  }
353 
354  LayoutContextSettingsRestorer( const LayoutContextSettingsRestorer &other ) = delete;
355  LayoutContextSettingsRestorer &operator=( const LayoutContextSettingsRestorer &other ) = delete;
356 
357  private:
358  QgsLayout *mLayout = nullptr;
359  double mPreviousDpi = 0;
360  QgsLayoutRenderContext::Flags mPreviousFlags = QgsLayoutRenderContext::Flags();
361  Qgis::TextRenderFormat mPreviousTextFormat = Qgis::TextRenderFormat::AlwaysOutlines;
362  int mPreviousExportLayer = 0;
363  QgsVectorSimplifyMethod mPreviousSimplifyMethod;
364  QStringList mExportThemes;
365  QVector< double > mPredefinedScales;
366 
367 };
369 
371 {
372  if ( !mLayout )
373  return PrintError;
374 
375  ImageExportSettings settings = s;
376  if ( settings.dpi <= 0 )
377  settings.dpi = mLayout->renderContext().dpi();
378 
379  mErrorFileName.clear();
380 
381  int worldFilePageNo = -1;
382  if ( QgsLayoutItemMap *referenceMap = mLayout->referenceMap() )
383  {
384  worldFilePageNo = referenceMap->page();
385  }
386 
387  QFileInfo fi( filePath );
388  QDir dir;
389  if ( !dir.exists( fi.absolutePath() ) )
390  {
391  dir.mkpath( fi.absolutePath() );
392  }
393 
394  PageExportDetails pageDetails;
395  pageDetails.directory = fi.path();
396  pageDetails.baseName = fi.completeBaseName();
397  pageDetails.extension = fi.suffix();
398 
399  LayoutContextPreviewSettingRestorer restorer( mLayout );
400  ( void )restorer;
401  LayoutContextSettingsRestorer dpiRestorer( mLayout );
402  ( void )dpiRestorer;
403  mLayout->renderContext().setDpi( settings.dpi );
404  mLayout->renderContext().setFlags( settings.flags );
405  mLayout->renderContext().setPredefinedScales( settings.predefinedMapScales );
406 
407  QList< int > pages;
408  if ( settings.pages.empty() )
409  {
410  for ( int page = 0; page < mLayout->pageCollection()->pageCount(); ++page )
411  pages << page;
412  }
413  else
414  {
415  for ( int page : std::as_const( settings.pages ) )
416  {
417  if ( page >= 0 && page < mLayout->pageCollection()->pageCount() )
418  pages << page;
419  }
420  }
421 
422  for ( int page : std::as_const( pages ) )
423  {
424  if ( !mLayout->pageCollection()->shouldExportPage( page ) )
425  {
426  continue;
427  }
428 
429  bool skip = false;
430  QRectF bounds;
431  QImage image = createImage( settings, page, bounds, skip );
432 
433  if ( skip )
434  continue; // should skip this page, e.g. null size
435 
436  pageDetails.page = page;
437  QString outputFilePath = generateFileName( pageDetails );
438 
439  if ( image.isNull() )
440  {
441  mErrorFileName = outputFilePath;
442  return MemoryError;
443  }
444 
445  if ( !saveImage( image, outputFilePath, pageDetails.extension, settings.exportMetadata ? mLayout->project() : nullptr ) )
446  {
447  mErrorFileName = outputFilePath;
448  return FileError;
449  }
450 
451  const bool shouldGeoreference = ( page == worldFilePageNo );
452  if ( shouldGeoreference )
453  {
454  georeferenceOutputPrivate( outputFilePath, nullptr, bounds, settings.dpi, shouldGeoreference );
455 
456  if ( settings.generateWorldFile )
457  {
458  // should generate world file for this page
459  double a, b, c, d, e, f;
460  if ( bounds.isValid() )
461  computeWorldFileParameters( bounds, a, b, c, d, e, f, settings.dpi );
462  else
463  computeWorldFileParameters( a, b, c, d, e, f, settings.dpi );
464 
465  QFileInfo fi( outputFilePath );
466  // build the world file name
467  QString outputSuffix = fi.suffix();
468  QString worldFileName = fi.absolutePath() + '/' + fi.completeBaseName() + '.'
469  + outputSuffix.at( 0 ) + outputSuffix.at( fi.suffix().size() - 1 ) + 'w';
470 
471  writeWorldFile( worldFileName, a, b, c, d, e, f );
472  }
473  }
474 
475  }
476  captureLabelingResults();
477  return Success;
478 }
479 
480 QgsLayoutExporter::ExportResult QgsLayoutExporter::exportToImage( QgsAbstractLayoutIterator *iterator, const QString &baseFilePath, const QString &extension, const QgsLayoutExporter::ImageExportSettings &settings, QString &error, QgsFeedback *feedback )
481 {
482  error.clear();
483 
484  if ( !iterator->beginRender() )
485  return IteratorError;
486 
487  int total = iterator->count();
488  double step = total > 0 ? 100.0 / total : 100.0;
489  int i = 0;
490  while ( iterator->next() )
491  {
492  if ( feedback )
493  {
494  if ( total > 0 )
495  feedback->setProperty( "progress", QObject::tr( "Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
496  else
497  feedback->setProperty( "progress", QObject::tr( "Exporting section %1" ).arg( i + 1 ).arg( total ) );
498  feedback->setProgress( step * i );
499  }
500  if ( feedback && feedback->isCanceled() )
501  {
502  iterator->endRender();
503  return Canceled;
504  }
505 
506  QgsLayoutExporter exporter( iterator->layout() );
507  QString filePath = iterator->filePath( baseFilePath, extension );
508  ExportResult result = exporter.exportToImage( filePath, settings );
509  if ( result != Success )
510  {
511  if ( result == FileError )
512  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 ) );
513  iterator->endRender();
514  return result;
515  }
516  i++;
517  }
518 
519  if ( feedback )
520  {
521  feedback->setProgress( 100 );
522  }
523 
524  iterator->endRender();
525  return Success;
526 }
527 
529 {
530  if ( !mLayout || mLayout->pageCollection()->pageCount() == 0 )
531  return PrintError;
532 
533  PdfExportSettings settings = s;
534  if ( settings.dpi <= 0 )
535  settings.dpi = mLayout->renderContext().dpi();
536 
537  mErrorFileName.clear();
538 
539  LayoutContextPreviewSettingRestorer restorer( mLayout );
540  ( void )restorer;
541  LayoutContextSettingsRestorer contextRestorer( mLayout );
542  ( void )contextRestorer;
543  mLayout->renderContext().setDpi( settings.dpi );
544  mLayout->renderContext().setPredefinedScales( settings.predefinedMapScales );
545 
546  if ( settings.simplifyGeometries )
547  {
548  mLayout->renderContext().setSimplifyMethod( createExportSimplifyMethod() );
549  }
550 
551  std::unique_ptr< QgsLayoutGeoPdfExporter > geoPdfExporter;
552  if ( settings.writeGeoPdf || settings.exportLayersAsSeperateFiles ) //#spellok
553  geoPdfExporter = std::make_unique< QgsLayoutGeoPdfExporter >( mLayout );
554 
555  mLayout->renderContext().setFlags( settings.flags );
556 
557  // If we are not printing as raster, temporarily disable advanced effects
558  // as QPrinter does not support composition modes and can result
559  // in items missing from the output
560  mLayout->renderContext().setFlag( QgsLayoutRenderContext::FlagUseAdvancedEffects, !settings.forceVectorOutput );
561  mLayout->renderContext().setFlag( QgsLayoutRenderContext::FlagForceVectorOutput, settings.forceVectorOutput );
562  mLayout->renderContext().setTextRenderFormat( settings.textRenderFormat );
563  mLayout->renderContext().setExportThemes( settings.exportThemes );
564 
565  ExportResult result = Success;
566  if ( settings.writeGeoPdf || settings.exportLayersAsSeperateFiles ) //#spellok
567  {
568  mLayout->renderContext().setFlag( QgsLayoutRenderContext::FlagRenderLabelsByMapLayer, true );
569 
570  // here we need to export layers to individual PDFs
571  PdfExportSettings subSettings = settings;
572  subSettings.writeGeoPdf = false;
573  subSettings.exportLayersAsSeperateFiles = false; //#spellok
574 
575  const QList<QGraphicsItem *> items = mLayout->items( Qt::AscendingOrder );
576 
577  QList< QgsLayoutGeoPdfExporter::ComponentLayerDetail > pdfComponents;
578 
579  const QDir baseDir = settings.exportLayersAsSeperateFiles ? QFileInfo( filePath ).dir() : QDir(); //#spellok
580  const QString baseFileName = settings.exportLayersAsSeperateFiles ? QFileInfo( filePath ).completeBaseName() : QString(); //#spellok
581 
582  auto exportFunc = [this, &subSettings, &pdfComponents, &geoPdfExporter, &settings, &baseDir, &baseFileName]( unsigned int layerId, const QgsLayoutItem::ExportLayerDetail & layerDetail )->QgsLayoutExporter::ExportResult
583  {
584  ExportResult layerExportResult = Success;
585  QPrinter printer;
587  component.name = layerDetail.name;
588  component.mapLayerId = layerDetail.mapLayerId;
589  component.opacity = layerDetail.opacity;
590  component.compositionMode = layerDetail.compositionMode;
591  component.group = layerDetail.mapTheme;
592  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' ) ) );
593  pdfComponents << component;
594  preparePrintAsPdf( mLayout, printer, component.sourcePdfPath );
595  preparePrint( mLayout, printer, false );
596  QPainter p;
597  if ( !p.begin( &printer ) )
598  {
599  //error beginning print
600  return FileError;
601  }
602 #if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)
603  p.setRenderHint( QPainter::LosslessImageRendering, mLayout->renderContext().flags() & QgsLayoutRenderContext::FlagLosslessImageRendering );
604 #endif
605  layerExportResult = printPrivate( printer, p, false, subSettings.dpi, subSettings.rasterizeWholeImage );
606  p.end();
607  return layerExportResult;
608  };
609  result = handleLayeredExport( items, exportFunc );
610  if ( result != Success )
611  return result;
612 
613  if ( settings.writeGeoPdf )
614  {
616  details.dpi = settings.dpi;
617  // TODO - multipages
618  QgsLayoutSize pageSize = mLayout->pageCollection()->page( 0 )->sizeWithUnits();
619  QgsLayoutSize pageSizeMM = mLayout->renderContext().measurementConverter().convert( pageSize, QgsUnitTypes::LayoutMillimeters );
620  details.pageSizeMm = pageSizeMM.toQSizeF();
621 
622  if ( settings.exportMetadata )
623  {
624  // copy layout metadata to GeoPDF export settings
625  details.author = mLayout->project()->metadata().author();
626  details.producer = QStringLiteral( "QGIS %1" ).arg( Qgis::version() );
627  details.creator = QStringLiteral( "QGIS %1" ).arg( Qgis::version() );
628  details.creationDateTime = mLayout->project()->metadata().creationDateTime();
629  details.subject = mLayout->project()->metadata().abstract();
630  details.title = mLayout->project()->metadata().title();
631  details.keywords = mLayout->project()->metadata().keywords();
632  }
633 
634  const QList< QgsMapLayer * > layers = mLayout->project()->mapLayers().values();
635  for ( const QgsMapLayer *layer : layers )
636  {
637  details.layerIdToPdfLayerTreeNameMap.insert( layer->id(), layer->name() );
638  }
639 
640  if ( settings.appendGeoreference )
641  {
642  // setup georeferencing
643  QList< QgsLayoutItemMap * > maps;
644  mLayout->layoutItems( maps );
645  for ( QgsLayoutItemMap *map : std::as_const( maps ) )
646  {
648  georef.crs = map->crs();
649 
650  const QPointF topLeft = map->mapToScene( QPointF( 0, 0 ) );
651  const QPointF topRight = map->mapToScene( QPointF( map->rect().width(), 0 ) );
652  const QPointF bottomLeft = map->mapToScene( QPointF( 0, map->rect().height() ) );
653  const QPointF bottomRight = map->mapToScene( QPointF( map->rect().width(), map->rect().height() ) );
654  const QgsLayoutPoint topLeftMm = mLayout->convertFromLayoutUnits( topLeft, QgsUnitTypes::LayoutMillimeters );
655  const QgsLayoutPoint topRightMm = mLayout->convertFromLayoutUnits( topRight, QgsUnitTypes::LayoutMillimeters );
656  const QgsLayoutPoint bottomLeftMm = mLayout->convertFromLayoutUnits( bottomLeft, QgsUnitTypes::LayoutMillimeters );
657  const QgsLayoutPoint bottomRightMm = mLayout->convertFromLayoutUnits( bottomRight, QgsUnitTypes::LayoutMillimeters );
658 
659  georef.pageBoundsPolygon.setExteriorRing( new QgsLineString( QVector< QgsPointXY >() << QgsPointXY( topLeftMm.x(), topLeftMm.y() )
660  << QgsPointXY( topRightMm.x(), topRightMm.y() )
661  << QgsPointXY( bottomRightMm.x(), bottomRightMm.y() )
662  << QgsPointXY( bottomLeftMm.x(), bottomLeftMm.y() )
663  << QgsPointXY( topLeftMm.x(), topLeftMm.y() ) ) );
664 
665  georef.controlPoints.reserve( 4 );
666  const QTransform t = map->layoutToMapCoordsTransform();
667  const QgsPointXY topLeftMap = t.map( topLeft );
668  const QgsPointXY topRightMap = t.map( topRight );
669  const QgsPointXY bottomLeftMap = t.map( bottomLeft );
670  const QgsPointXY bottomRightMap = t.map( bottomRight );
671 
672  georef.controlPoints << QgsAbstractGeoPdfExporter::ControlPoint( QgsPointXY( topLeftMm.x(), topLeftMm.y() ), topLeftMap );
673  georef.controlPoints << QgsAbstractGeoPdfExporter::ControlPoint( QgsPointXY( topRightMm.x(), topRightMm.y() ), topRightMap );
674  georef.controlPoints << QgsAbstractGeoPdfExporter::ControlPoint( QgsPointXY( bottomLeftMm.x(), bottomLeftMm.y() ), bottomLeftMap );
675  georef.controlPoints << QgsAbstractGeoPdfExporter::ControlPoint( QgsPointXY( bottomRightMm.x(), bottomRightMm.y() ), bottomRightMap );
676  details.georeferencedSections << georef;
677  }
678  }
679 
680  details.customLayerTreeGroups = geoPdfExporter->customLayerTreeGroups();
681  details.initialLayerVisibility = geoPdfExporter->initialLayerVisibility();
682  details.layerOrder = geoPdfExporter->layerOrder();
683  details.includeFeatures = settings.includeGeoPdfFeatures;
684  details.useOgcBestPracticeFormatGeoreferencing = settings.useOgcBestPracticeFormatGeoreferencing;
685  details.useIso32000ExtensionFormatGeoreferencing = settings.useIso32000ExtensionFormatGeoreferencing;
686 
687  if ( !geoPdfExporter->finalize( pdfComponents, filePath, details ) )
688  result = PrintError;
689  }
690  else
691  {
692  result = Success;
693  }
694  }
695  else
696  {
697  QPrinter printer;
698  preparePrintAsPdf( mLayout, printer, filePath );
699  preparePrint( mLayout, printer, false );
700  QPainter p;
701  if ( !p.begin( &printer ) )
702  {
703  //error beginning print
704  return FileError;
705  }
706 #if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)
707  p.setRenderHint( QPainter::LosslessImageRendering, mLayout->renderContext().flags() & QgsLayoutRenderContext::FlagLosslessImageRendering );
708 #endif
709  result = printPrivate( printer, p, false, settings.dpi, settings.rasterizeWholeImage );
710  p.end();
711 
712  bool shouldAppendGeoreference = settings.appendGeoreference && mLayout && mLayout->referenceMap() && mLayout->referenceMap()->page() == 0;
713  if ( settings.appendGeoreference || settings.exportMetadata )
714  {
715  georeferenceOutputPrivate( filePath, nullptr, QRectF(), settings.dpi, shouldAppendGeoreference, settings.exportMetadata );
716  }
717  }
718  captureLabelingResults();
719  return result;
720 }
721 
723 {
724  error.clear();
725 
726  if ( !iterator->beginRender() )
727  return IteratorError;
728 
729  PdfExportSettings settings = s;
730 
731  QPrinter printer;
732  QPainter p;
733 
734  int total = iterator->count();
735  double step = total > 0 ? 100.0 / total : 100.0;
736  int i = 0;
737  bool first = true;
738  while ( iterator->next() )
739  {
740  if ( feedback )
741  {
742  if ( total > 0 )
743  feedback->setProperty( "progress", QObject::tr( "Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
744  else
745  feedback->setProperty( "progress", QObject::tr( "Exporting section %1" ).arg( i + 1 ) );
746  feedback->setProgress( step * i );
747  }
748  if ( feedback && feedback->isCanceled() )
749  {
750  iterator->endRender();
751  return Canceled;
752  }
753 
754  if ( s.dpi <= 0 )
755  settings.dpi = iterator->layout()->renderContext().dpi();
756 
757  LayoutContextPreviewSettingRestorer restorer( iterator->layout() );
758  ( void )restorer;
759  LayoutContextSettingsRestorer contextRestorer( iterator->layout() );
760  ( void )contextRestorer;
761  iterator->layout()->renderContext().setDpi( settings.dpi );
762 
763  iterator->layout()->renderContext().setFlags( settings.flags );
764  iterator->layout()->renderContext().setPredefinedScales( settings.predefinedMapScales );
765 
766  if ( settings.simplifyGeometries )
767  {
768  iterator->layout()->renderContext().setSimplifyMethod( createExportSimplifyMethod() );
769  }
770 
771  // If we are not printing as raster, temporarily disable advanced effects
772  // as QPrinter does not support composition modes and can result
773  // in items missing from the output
775 
777 
778  iterator->layout()->renderContext().setTextRenderFormat( settings.textRenderFormat );
779 
780  if ( first )
781  {
782  preparePrintAsPdf( iterator->layout(), printer, fileName );
783  preparePrint( iterator->layout(), printer, false );
784 
785  if ( !p.begin( &printer ) )
786  {
787  //error beginning print
788  return PrintError;
789  }
790 #if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)
791  p.setRenderHint( QPainter::LosslessImageRendering, iterator->layout()->renderContext().flags() & QgsLayoutRenderContext::FlagLosslessImageRendering );
792 #endif
793  }
794 
795  QgsLayoutExporter exporter( iterator->layout() );
796 
797  ExportResult result = exporter.printPrivate( printer, p, !first, settings.dpi, settings.rasterizeWholeImage );
798  if ( result != Success )
799  {
800  if ( result == FileError )
801  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 ) );
802  iterator->endRender();
803  return result;
804  }
805  first = false;
806  i++;
807  }
808 
809  if ( feedback )
810  {
811  feedback->setProgress( 100 );
812  }
813 
814  iterator->endRender();
815  return Success;
816 }
817 
818 QgsLayoutExporter::ExportResult QgsLayoutExporter::exportToPdfs( QgsAbstractLayoutIterator *iterator, const QString &baseFilePath, const QgsLayoutExporter::PdfExportSettings &settings, QString &error, QgsFeedback *feedback )
819 {
820  error.clear();
821 
822  if ( !iterator->beginRender() )
823  return IteratorError;
824 
825  int total = iterator->count();
826  double step = total > 0 ? 100.0 / total : 100.0;
827  int i = 0;
828  while ( iterator->next() )
829  {
830  if ( feedback )
831  {
832  if ( total > 0 )
833  feedback->setProperty( "progress", QObject::tr( "Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
834  else
835  feedback->setProperty( "progress", QObject::tr( "Exporting section %1" ).arg( i + 1 ).arg( total ) );
836  feedback->setProgress( step * i );
837  }
838  if ( feedback && feedback->isCanceled() )
839  {
840  iterator->endRender();
841  return Canceled;
842  }
843 
844  QString filePath = iterator->filePath( baseFilePath, QStringLiteral( "pdf" ) );
845 
846  QgsLayoutExporter exporter( iterator->layout() );
847  ExportResult result = exporter.exportToPdf( filePath, settings );
848  if ( result != Success )
849  {
850  if ( result == FileError )
851  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 ) );
852  iterator->endRender();
853  return result;
854  }
855  i++;
856  }
857 
858  if ( feedback )
859  {
860  feedback->setProgress( 100 );
861  }
862 
863  iterator->endRender();
864  return Success;
865 }
866 
868 {
869  if ( !mLayout )
870  return PrintError;
871 
873  if ( settings.dpi <= 0 )
874  settings.dpi = mLayout->renderContext().dpi();
875 
876  mErrorFileName.clear();
877 
878  LayoutContextPreviewSettingRestorer restorer( mLayout );
879  ( void )restorer;
880  LayoutContextSettingsRestorer contextRestorer( mLayout );
881  ( void )contextRestorer;
882  mLayout->renderContext().setDpi( settings.dpi );
883 
884  mLayout->renderContext().setFlags( settings.flags );
885  mLayout->renderContext().setPredefinedScales( settings.predefinedMapScales );
886  // If we are not printing as raster, temporarily disable advanced effects
887  // as QPrinter does not support composition modes and can result
888  // in items missing from the output
889  mLayout->renderContext().setFlag( QgsLayoutRenderContext::FlagUseAdvancedEffects, !settings.rasterizeWholeImage );
890 
891  preparePrint( mLayout, printer, true );
892  QPainter p;
893  if ( !p.begin( &printer ) )
894  {
895  //error beginning print
896  return PrintError;
897  }
898 #if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)
899  p.setRenderHint( QPainter::LosslessImageRendering, mLayout->renderContext().flags() & QgsLayoutRenderContext::FlagLosslessImageRendering );
900 #endif
901  ExportResult result = printPrivate( printer, p, false, settings.dpi, settings.rasterizeWholeImage );
902  p.end();
903 
904  captureLabelingResults();
905  return result;
906 }
907 
909 {
910  error.clear();
911 
912  if ( !iterator->beginRender() )
913  return IteratorError;
914 
915  PrintExportSettings settings = s;
916 
917  QPainter p;
918 
919  int total = iterator->count();
920  double step = total > 0 ? 100.0 / total : 100.0;
921  int i = 0;
922  bool first = true;
923  while ( iterator->next() )
924  {
925  if ( feedback )
926  {
927  if ( total > 0 )
928  feedback->setProperty( "progress", QObject::tr( "Printing %1 of %2" ).arg( i + 1 ).arg( total ) );
929  else
930  feedback->setProperty( "progress", QObject::tr( "Printing section %1" ).arg( i + 1 ).arg( total ) );
931  feedback->setProgress( step * i );
932  }
933  if ( feedback && feedback->isCanceled() )
934  {
935  iterator->endRender();
936  return Canceled;
937  }
938 
939  if ( s.dpi <= 0 )
940  settings.dpi = iterator->layout()->renderContext().dpi();
941 
942  LayoutContextPreviewSettingRestorer restorer( iterator->layout() );
943  ( void )restorer;
944  LayoutContextSettingsRestorer contextRestorer( iterator->layout() );
945  ( void )contextRestorer;
946  iterator->layout()->renderContext().setDpi( settings.dpi );
947 
948  iterator->layout()->renderContext().setFlags( settings.flags );
949  iterator->layout()->renderContext().setPredefinedScales( settings.predefinedMapScales );
950 
951  // If we are not printing as raster, temporarily disable advanced effects
952  // as QPrinter does not support composition modes and can result
953  // in items missing from the output
955 
956  if ( first )
957  {
958  preparePrint( iterator->layout(), printer, true );
959 
960  if ( !p.begin( &printer ) )
961  {
962  //error beginning print
963  return PrintError;
964  }
965 #if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)
966  p.setRenderHint( QPainter::LosslessImageRendering, iterator->layout()->renderContext().flags() & QgsLayoutRenderContext::FlagLosslessImageRendering );
967 #endif
968  }
969 
970  QgsLayoutExporter exporter( iterator->layout() );
971 
972  ExportResult result = exporter.printPrivate( printer, p, !first, settings.dpi, settings.rasterizeWholeImage );
973  if ( result != Success )
974  {
975  iterator->endRender();
976  return result;
977  }
978  first = false;
979  i++;
980  }
981 
982  if ( feedback )
983  {
984  feedback->setProgress( 100 );
985  }
986 
987  iterator->endRender();
988  return Success;
989 }
990 
992 {
993  if ( !mLayout )
994  return PrintError;
995 
996  SvgExportSettings settings = s;
997  if ( settings.dpi <= 0 )
998  settings.dpi = mLayout->renderContext().dpi();
999 
1000  mErrorFileName.clear();
1001 
1002  LayoutContextPreviewSettingRestorer restorer( mLayout );
1003  ( void )restorer;
1004  LayoutContextSettingsRestorer contextRestorer( mLayout );
1005  ( void )contextRestorer;
1006  mLayout->renderContext().setDpi( settings.dpi );
1007 
1008  mLayout->renderContext().setFlags( settings.flags );
1009  mLayout->renderContext().setFlag( QgsLayoutRenderContext::FlagForceVectorOutput, settings.forceVectorOutput );
1010  mLayout->renderContext().setTextRenderFormat( s.textRenderFormat );
1011  mLayout->renderContext().setPredefinedScales( settings.predefinedMapScales );
1012 
1013  if ( settings.simplifyGeometries )
1014  {
1015  mLayout->renderContext().setSimplifyMethod( createExportSimplifyMethod() );
1016  }
1017 
1018  QFileInfo fi( filePath );
1019  PageExportDetails pageDetails;
1020  pageDetails.directory = fi.path();
1021  pageDetails.baseName = fi.baseName();
1022  pageDetails.extension = fi.completeSuffix();
1023 
1024  double inchesToLayoutUnits = mLayout->convertToLayoutUnits( QgsLayoutMeasurement( 1, QgsUnitTypes::LayoutInches ) );
1025 
1026  for ( int i = 0; i < mLayout->pageCollection()->pageCount(); ++i )
1027  {
1028  if ( !mLayout->pageCollection()->shouldExportPage( i ) )
1029  {
1030  continue;
1031  }
1032 
1033  pageDetails.page = i;
1034  QString fileName = generateFileName( pageDetails );
1035 
1036  QgsLayoutItemPage *pageItem = mLayout->pageCollection()->page( i );
1037  QRectF bounds;
1038  if ( settings.cropToContents )
1039  {
1040  if ( mLayout->pageCollection()->pageCount() == 1 )
1041  {
1042  // single page, so include everything
1043  bounds = mLayout->layoutBounds( true );
1044  }
1045  else
1046  {
1047  // multi page, so just clip to items on current page
1048  bounds = mLayout->pageItemBounds( i, true );
1049  }
1050  bounds = bounds.adjusted( -settings.cropMargins.left(),
1051  -settings.cropMargins.top(),
1052  settings.cropMargins.right(),
1053  settings.cropMargins.bottom() );
1054  }
1055  else
1056  {
1057  bounds = QRectF( pageItem->pos().x(), pageItem->pos().y(), pageItem->rect().width(), pageItem->rect().height() );
1058  }
1059 
1060  //width in pixel
1061  int width = static_cast< int >( bounds.width() * settings.dpi / inchesToLayoutUnits );
1062  //height in pixel
1063  int height = static_cast< int >( bounds.height() * settings.dpi / inchesToLayoutUnits );
1064  if ( width == 0 || height == 0 )
1065  {
1066  //invalid size, skip this page
1067  continue;
1068  }
1069 
1070  if ( settings.exportAsLayers )
1071  {
1072  mLayout->renderContext().setFlag( QgsLayoutRenderContext::FlagRenderLabelsByMapLayer, settings.exportLabelsToSeparateLayers );
1073  const QRectF paperRect = QRectF( pageItem->pos().x(),
1074  pageItem->pos().y(),
1075  pageItem->rect().width(),
1076  pageItem->rect().height() );
1077  QDomDocument svg;
1078  QDomNode svgDocRoot;
1079  const QList<QGraphicsItem *> items = mLayout->items( paperRect,
1080  Qt::IntersectsItemBoundingRect,
1081  Qt::AscendingOrder );
1082 
1083  auto exportFunc = [this, &settings, width, height, i, bounds, fileName, &svg, &svgDocRoot]( unsigned int layerId, const QgsLayoutItem::ExportLayerDetail & layerDetail )->QgsLayoutExporter::ExportResult
1084  {
1085  return renderToLayeredSvg( settings, width, height, i, bounds, fileName, layerId, layerDetail.name, svg, svgDocRoot, settings.exportMetadata );
1086  };
1087  ExportResult res = handleLayeredExport( items, exportFunc );
1088  if ( res != Success )
1089  return res;
1090 
1091  if ( settings.exportMetadata )
1092  appendMetadataToSvg( svg );
1093 
1094  QFile out( fileName );
1095  bool openOk = out.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate );
1096  if ( !openOk )
1097  {
1098  mErrorFileName = fileName;
1099  return FileError;
1100  }
1101 
1102  out.write( svg.toByteArray() );
1103  }
1104  else
1105  {
1106  QBuffer svgBuffer;
1107  {
1108  QSvgGenerator generator;
1109  if ( settings.exportMetadata )
1110  {
1111  generator.setTitle( mLayout->project()->metadata().title() );
1112  generator.setDescription( mLayout->project()->metadata().abstract() );
1113  }
1114  generator.setOutputDevice( &svgBuffer );
1115  generator.setSize( QSize( width, height ) );
1116  generator.setViewBox( QRect( 0, 0, width, height ) );
1117  generator.setResolution( static_cast< int >( std::round( settings.dpi ) ) );
1118 
1119  QPainter p;
1120  bool createOk = p.begin( &generator );
1121  if ( !createOk )
1122  {
1123  mErrorFileName = fileName;
1124  return FileError;
1125  }
1126 
1127  if ( settings.cropToContents )
1128  renderRegion( &p, bounds );
1129  else
1130  renderPage( &p, i );
1131 
1132  p.end();
1133  }
1134  {
1135  svgBuffer.close();
1136  svgBuffer.open( QIODevice::ReadOnly );
1137  QDomDocument svg;
1138  QString errorMsg;
1139  int errorLine;
1140  if ( ! svg.setContent( &svgBuffer, false, &errorMsg, &errorLine ) )
1141  {
1142  mErrorFileName = fileName;
1143  return SvgLayerError;
1144  }
1145 
1146  if ( settings.exportMetadata )
1147  appendMetadataToSvg( svg );
1148 
1149  QFile out( fileName );
1150  bool openOk = out.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate );
1151  if ( !openOk )
1152  {
1153  mErrorFileName = fileName;
1154  return FileError;
1155  }
1156 
1157  out.write( svg.toByteArray() );
1158  }
1159  }
1160  }
1161  captureLabelingResults();
1162  return Success;
1163 }
1164 
1165 QgsLayoutExporter::ExportResult QgsLayoutExporter::exportToSvg( QgsAbstractLayoutIterator *iterator, const QString &baseFilePath, const QgsLayoutExporter::SvgExportSettings &settings, QString &error, QgsFeedback *feedback )
1166 {
1167  error.clear();
1168 
1169  if ( !iterator->beginRender() )
1170  return IteratorError;
1171 
1172  int total = iterator->count();
1173  double step = total > 0 ? 100.0 / total : 100.0;
1174  int i = 0;
1175  while ( iterator->next() )
1176  {
1177  if ( feedback )
1178  {
1179  if ( total > 0 )
1180  feedback->setProperty( "progress", QObject::tr( "Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
1181  else
1182  feedback->setProperty( "progress", QObject::tr( "Exporting section %1" ).arg( i + 1 ).arg( total ) );
1183 
1184  feedback->setProgress( step * i );
1185  }
1186  if ( feedback && feedback->isCanceled() )
1187  {
1188  iterator->endRender();
1189  return Canceled;
1190  }
1191 
1192  QString filePath = iterator->filePath( baseFilePath, QStringLiteral( "svg" ) );
1193 
1194  QgsLayoutExporter exporter( iterator->layout() );
1195  ExportResult result = exporter.exportToSvg( filePath, settings );
1196  if ( result != Success )
1197  {
1198  if ( result == FileError )
1199  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 ) );
1200  iterator->endRender();
1201  return result;
1202  }
1203  i++;
1204  }
1205 
1206  if ( feedback )
1207  {
1208  feedback->setProgress( 100 );
1209  }
1210 
1211  iterator->endRender();
1212  return Success;
1213 
1214 }
1215 
1216 QMap<QString, QgsLabelingResults *> QgsLayoutExporter::labelingResults()
1217 {
1218  return mLabelingResults;
1219 }
1220 
1221 QMap<QString, QgsLabelingResults *> QgsLayoutExporter::takeLabelingResults()
1222 {
1223  QMap<QString, QgsLabelingResults *> res;
1224  std::swap( mLabelingResults, res );
1225  return res;
1226 }
1227 
1228 void QgsLayoutExporter::preparePrintAsPdf( QgsLayout *layout, QPrinter &printer, const QString &filePath )
1229 {
1230  QFileInfo fi( filePath );
1231  QDir dir;
1232  if ( !dir.exists( fi.absolutePath() ) )
1233  {
1234  dir.mkpath( fi.absolutePath() );
1235  }
1236 
1237  printer.setOutputFileName( filePath );
1238  printer.setOutputFormat( QPrinter::PdfFormat );
1239 
1240  updatePrinterPageSize( layout, printer, firstPageToBeExported( layout ) );
1241 
1242  // TODO: add option for this in layout
1243  // May not work on Windows or non-X11 Linux. Works fine on Mac using QPrinter::NativeFormat
1244  //printer.setFontEmbeddingEnabled( true );
1245 
1246 #if defined(HAS_KDE_QT5_PDF_TRANSFORM_FIX) || QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)
1247  // paint engine hack not required, fixed upstream
1248 #else
1249  QgsPaintEngineHack::fixEngineFlags( printer.paintEngine() );
1250 #endif
1251 }
1252 
1253 void QgsLayoutExporter::preparePrint( QgsLayout *layout, QPrinter &printer, bool setFirstPageSize )
1254 {
1255  printer.setFullPage( true );
1256  printer.setColorMode( QPrinter::Color );
1257 
1258  //set user-defined resolution
1259  printer.setResolution( static_cast< int>( std::round( layout->renderContext().dpi() ) ) );
1260 
1261  if ( setFirstPageSize )
1262  {
1263  updatePrinterPageSize( layout, printer, firstPageToBeExported( layout ) );
1264  }
1265 }
1266 
1268 {
1269  if ( mLayout->pageCollection()->pageCount() == 0 )
1270  return PrintError;
1271 
1272  preparePrint( mLayout, printer, true );
1273  QPainter p;
1274  if ( !p.begin( &printer ) )
1275  {
1276  //error beginning print
1277  return PrintError;
1278  }
1279 
1280  printPrivate( printer, p );
1281  p.end();
1282  return Success;
1283 }
1284 
1285 QgsLayoutExporter::ExportResult QgsLayoutExporter::printPrivate( QPrinter &printer, QPainter &painter, bool startNewPage, double dpi, bool rasterize )
1286 {
1287  //layout starts page numbering at 0
1288  int fromPage = ( printer.fromPage() < 1 ) ? 0 : printer.fromPage() - 1;
1289  int toPage = ( printer.toPage() < 1 ) ? mLayout->pageCollection()->pageCount() - 1 : printer.toPage() - 1;
1290 
1291  bool pageExported = false;
1292  if ( rasterize )
1293  {
1294  for ( int i = fromPage; i <= toPage; ++i )
1295  {
1296  if ( !mLayout->pageCollection()->shouldExportPage( i ) )
1297  {
1298  continue;
1299  }
1300 
1301  updatePrinterPageSize( mLayout, printer, i );
1302  if ( ( pageExported && i > fromPage ) || startNewPage )
1303  {
1304  printer.newPage();
1305  }
1306 
1307  QImage image = renderPageToImage( i, QSize(), dpi );
1308  if ( !image.isNull() )
1309  {
1310  QRectF targetArea( 0, 0, image.width(), image.height() );
1311  painter.drawImage( targetArea, image, targetArea );
1312  }
1313  else
1314  {
1315  return MemoryError;
1316  }
1317  pageExported = true;
1318  }
1319  }
1320  else
1321  {
1322  for ( int i = fromPage; i <= toPage; ++i )
1323  {
1324  if ( !mLayout->pageCollection()->shouldExportPage( i ) )
1325  {
1326  continue;
1327  }
1328 
1329  updatePrinterPageSize( mLayout, printer, i );
1330 
1331  if ( ( pageExported && i > fromPage ) || startNewPage )
1332  {
1333  printer.newPage();
1334  }
1335  renderPage( &painter, i );
1336  pageExported = true;
1337  }
1338  }
1339  return Success;
1340 }
1341 
1342 void QgsLayoutExporter::updatePrinterPageSize( QgsLayout *layout, QPrinter &printer, int page )
1343 {
1344  QgsLayoutSize pageSize = layout->pageCollection()->page( page )->sizeWithUnits();
1346 
1347  QPageLayout pageLayout( QPageSize( pageSizeMM.toQSizeF(), QPageSize::Millimeter ),
1348  QPageLayout::Portrait,
1349  QMarginsF( 0, 0, 0, 0 ) );
1350  pageLayout.setMode( QPageLayout::FullPageMode );
1351  printer.setPageLayout( pageLayout );
1352  printer.setFullPage( true );
1353  printer.setPageMargins( QMarginsF( 0, 0, 0, 0 ) );
1354 }
1355 
1356 QgsLayoutExporter::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
1357 {
1358  QBuffer svgBuffer;
1359  {
1360  QSvgGenerator generator;
1361  if ( includeMetadata )
1362  {
1363  if ( const QgsMasterLayoutInterface *l = dynamic_cast< const QgsMasterLayoutInterface * >( mLayout.data() ) )
1364  generator.setTitle( l->name() );
1365  else if ( mLayout->project() )
1366  generator.setTitle( mLayout->project()->title() );
1367  }
1368 
1369  generator.setOutputDevice( &svgBuffer );
1370  generator.setSize( QSize( static_cast< int >( std::round( width ) ),
1371  static_cast< int >( std::round( height ) ) ) );
1372  generator.setViewBox( QRect( 0, 0,
1373  static_cast< int >( std::round( width ) ),
1374  static_cast< int >( std::round( height ) ) ) );
1375  generator.setResolution( static_cast< int >( std::round( settings.dpi ) ) ); //because the rendering is done in mm, convert the dpi
1376 
1377  QPainter svgPainter( &generator );
1378  if ( settings.cropToContents )
1379  renderRegion( &svgPainter, bounds );
1380  else
1381  renderPage( &svgPainter, page );
1382  }
1383 
1384 // post-process svg output to create groups in a single svg file
1385 // we create inkscape layers since it's nice and clean and free
1386 // and fully svg compatible
1387  {
1388  svgBuffer.close();
1389  svgBuffer.open( QIODevice::ReadOnly );
1390  QDomDocument doc;
1391  QString errorMsg;
1392  int errorLine;
1393  if ( ! doc.setContent( &svgBuffer, false, &errorMsg, &errorLine ) )
1394  {
1395  mErrorFileName = filename;
1396  return SvgLayerError;
1397  }
1398  if ( 1 == svgLayerId )
1399  {
1400  svg = QDomDocument( doc.doctype() );
1401  svg.appendChild( svg.importNode( doc.firstChild(), false ) );
1402  svgDocRoot = svg.importNode( doc.elementsByTagName( QStringLiteral( "svg" ) ).at( 0 ), false );
1403  svgDocRoot.toElement().setAttribute( QStringLiteral( "xmlns:inkscape" ), QStringLiteral( "http://www.inkscape.org/namespaces/inkscape" ) );
1404  svg.appendChild( svgDocRoot );
1405  }
1406  QDomNode mainGroup = svg.importNode( doc.elementsByTagName( QStringLiteral( "g" ) ).at( 0 ), true );
1407  mainGroup.toElement().setAttribute( QStringLiteral( "id" ), layerName );
1408  mainGroup.toElement().setAttribute( QStringLiteral( "inkscape:label" ), layerName );
1409  mainGroup.toElement().setAttribute( QStringLiteral( "inkscape:groupmode" ), QStringLiteral( "layer" ) );
1410  QDomNode defs = svg.importNode( doc.elementsByTagName( QStringLiteral( "defs" ) ).at( 0 ), true );
1411  svgDocRoot.appendChild( defs );
1412  svgDocRoot.appendChild( mainGroup );
1413  }
1414  return Success;
1415 }
1416 
1417 void QgsLayoutExporter::appendMetadataToSvg( QDomDocument &svg ) const
1418 {
1419  const QgsProjectMetadata &metadata = mLayout->project()->metadata();
1420  QDomElement metadataElement = svg.createElement( QStringLiteral( "metadata" ) );
1421  QDomElement rdfElement = svg.createElement( QStringLiteral( "rdf:RDF" ) );
1422  rdfElement.setAttribute( QStringLiteral( "xmlns:rdf" ), QStringLiteral( "http://www.w3.org/1999/02/22-rdf-syntax-ns#" ) );
1423  rdfElement.setAttribute( QStringLiteral( "xmlns:rdfs" ), QStringLiteral( "http://www.w3.org/2000/01/rdf-schema#" ) );
1424  rdfElement.setAttribute( QStringLiteral( "xmlns:dc" ), QStringLiteral( "http://purl.org/dc/elements/1.1/" ) );
1425  QDomElement descriptionElement = svg.createElement( QStringLiteral( "rdf:Description" ) );
1426  QDomElement workElement = svg.createElement( QStringLiteral( "cc:Work" ) );
1427  workElement.setAttribute( QStringLiteral( "rdf:about" ), QString() );
1428 
1429  auto addTextNode = [&workElement, &descriptionElement, &svg]( const QString & tag, const QString & value )
1430  {
1431  // inkscape compatible
1432  QDomElement element = svg.createElement( tag );
1433  QDomText t = svg.createTextNode( value );
1434  element.appendChild( t );
1435  workElement.appendChild( element );
1436 
1437  // svg spec compatible
1438  descriptionElement.setAttribute( tag, value );
1439  };
1440 
1441  addTextNode( QStringLiteral( "dc:format" ), QStringLiteral( "image/svg+xml" ) );
1442  addTextNode( QStringLiteral( "dc:title" ), metadata.title() );
1443  addTextNode( QStringLiteral( "dc:date" ), metadata.creationDateTime().toString( Qt::ISODate ) );
1444  addTextNode( QStringLiteral( "dc:identifier" ), metadata.identifier() );
1445  addTextNode( QStringLiteral( "dc:description" ), metadata.abstract() );
1446 
1447  auto addAgentNode = [&workElement, &descriptionElement, &svg]( const QString & tag, const QString & value )
1448  {
1449  // inkscape compatible
1450  QDomElement inkscapeElement = svg.createElement( tag );
1451  QDomElement agentElement = svg.createElement( QStringLiteral( "cc:Agent" ) );
1452  QDomElement titleElement = svg.createElement( QStringLiteral( "dc:title" ) );
1453  QDomText t = svg.createTextNode( value );
1454  titleElement.appendChild( t );
1455  agentElement.appendChild( titleElement );
1456  inkscapeElement.appendChild( agentElement );
1457  workElement.appendChild( inkscapeElement );
1458 
1459  // svg spec compatible
1460  QDomElement bagElement = svg.createElement( QStringLiteral( "rdf:Bag" ) );
1461  QDomElement liElement = svg.createElement( QStringLiteral( "rdf:li" ) );
1462  t = svg.createTextNode( value );
1463  liElement.appendChild( t );
1464  bagElement.appendChild( liElement );
1465 
1466  QDomElement element = svg.createElement( tag );
1467  element.appendChild( bagElement );
1468  descriptionElement.appendChild( element );
1469  };
1470 
1471  addAgentNode( QStringLiteral( "dc:creator" ), metadata.author() );
1472  addAgentNode( QStringLiteral( "dc:publisher" ), QStringLiteral( "QGIS %1" ).arg( Qgis::version() ) );
1473 
1474  // keywords
1475  {
1476  QDomElement element = svg.createElement( QStringLiteral( "dc:subject" ) );
1477  QDomElement bagElement = svg.createElement( QStringLiteral( "rdf:Bag" ) );
1478  QgsAbstractMetadataBase::KeywordMap keywords = metadata.keywords();
1479  for ( auto it = keywords.constBegin(); it != keywords.constEnd(); ++it )
1480  {
1481  const QStringList words = it.value();
1482  for ( const QString &keyword : words )
1483  {
1484  QDomElement liElement = svg.createElement( QStringLiteral( "rdf:li" ) );
1485  QDomText t = svg.createTextNode( keyword );
1486  liElement.appendChild( t );
1487  bagElement.appendChild( liElement );
1488  }
1489  }
1490  element.appendChild( bagElement );
1491  workElement.appendChild( element );
1492  descriptionElement.appendChild( element );
1493  }
1494 
1495  rdfElement.appendChild( descriptionElement );
1496  rdfElement.appendChild( workElement );
1497  metadataElement.appendChild( rdfElement );
1498  svg.documentElement().appendChild( metadataElement );
1499  svg.documentElement().setAttribute( QStringLiteral( "xmlns:cc" ), QStringLiteral( "http://creativecommons.org/ns#" ) );
1500 }
1501 
1502 std::unique_ptr<double[]> QgsLayoutExporter::computeGeoTransform( const QgsLayoutItemMap *map, const QRectF &region, double dpi ) const
1503 {
1504  if ( !map )
1505  map = mLayout->referenceMap();
1506 
1507  if ( !map )
1508  return nullptr;
1509 
1510  if ( dpi < 0 )
1511  dpi = mLayout->renderContext().dpi();
1512 
1513  // calculate region of composition to export (in mm)
1514  QRectF exportRegion = region;
1515  if ( !exportRegion.isValid() )
1516  {
1517  int pageNumber = map->page();
1518 
1519  QgsLayoutItemPage *page = mLayout->pageCollection()->page( pageNumber );
1520  double pageY = page->pos().y();
1521  QSizeF pageSize = page->rect().size();
1522  exportRegion = QRectF( 0, pageY, pageSize.width(), pageSize.height() );
1523  }
1524 
1525  // map rectangle (in mm)
1526  QRectF mapItemSceneRect = map->mapRectToScene( map->rect() );
1527 
1528  // destination width/height in mm
1529  double outputHeightMM = exportRegion.height();
1530  double outputWidthMM = exportRegion.width();
1531 
1532  // map properties
1533  QgsRectangle mapExtent = map->extent();
1534  double mapXCenter = mapExtent.center().x();
1535  double mapYCenter = mapExtent.center().y();
1536  double alpha = - map->mapRotation() / 180 * M_PI;
1537  double sinAlpha = std::sin( alpha );
1538  double cosAlpha = std::cos( alpha );
1539 
1540  // get the extent (in map units) for the exported region
1541  QPointF mapItemPos = map->pos();
1542  //adjust item position so it is relative to export region
1543  mapItemPos.rx() -= exportRegion.left();
1544  mapItemPos.ry() -= exportRegion.top();
1545 
1546  // calculate extent of entire page in map units
1547  double xRatio = mapExtent.width() / mapItemSceneRect.width();
1548  double yRatio = mapExtent.height() / mapItemSceneRect.height();
1549  double xmin = mapExtent.xMinimum() - mapItemPos.x() * xRatio;
1550  double ymax = mapExtent.yMaximum() + mapItemPos.y() * yRatio;
1551  QgsRectangle paperExtent( xmin, ymax - outputHeightMM * yRatio, xmin + outputWidthMM * xRatio, ymax );
1552 
1553  // calculate origin of page
1554  double X0 = paperExtent.xMinimum();
1555  double Y0 = paperExtent.yMaximum();
1556 
1557  if ( !qgsDoubleNear( alpha, 0.0 ) )
1558  {
1559  // translate origin to account for map rotation
1560  double X1 = X0 - mapXCenter;
1561  double Y1 = Y0 - mapYCenter;
1562  double X2 = X1 * cosAlpha + Y1 * sinAlpha;
1563  double Y2 = -X1 * sinAlpha + Y1 * cosAlpha;
1564  X0 = X2 + mapXCenter;
1565  Y0 = Y2 + mapYCenter;
1566  }
1567 
1568  // calculate scaling of pixels
1569  int pageWidthPixels = static_cast< int >( dpi * outputWidthMM / 25.4 );
1570  int pageHeightPixels = static_cast< int >( dpi * outputHeightMM / 25.4 );
1571  double pixelWidthScale = paperExtent.width() / pageWidthPixels;
1572  double pixelHeightScale = paperExtent.height() / pageHeightPixels;
1573 
1574  // transform matrix
1575  std::unique_ptr<double[]> t( new double[6] );
1576  t[0] = X0;
1577  t[1] = cosAlpha * pixelWidthScale;
1578  t[2] = -sinAlpha * pixelWidthScale;
1579  t[3] = Y0;
1580  t[4] = -sinAlpha * pixelHeightScale;
1581  t[5] = -cosAlpha * pixelHeightScale;
1582 
1583  return t;
1584 }
1585 
1586 void QgsLayoutExporter::writeWorldFile( const QString &worldFileName, double a, double b, double c, double d, double e, double f ) const
1587 {
1588  QFile worldFile( worldFileName );
1589  if ( !worldFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
1590  {
1591  return;
1592  }
1593  QTextStream fout( &worldFile );
1594 
1595  // QString::number does not use locale settings (for the decimal point)
1596  // which is what we want here
1597  fout << QString::number( a, 'f', 12 ) << "\r\n";
1598  fout << QString::number( d, 'f', 12 ) << "\r\n";
1599  fout << QString::number( b, 'f', 12 ) << "\r\n";
1600  fout << QString::number( e, 'f', 12 ) << "\r\n";
1601  fout << QString::number( c, 'f', 12 ) << "\r\n";
1602  fout << QString::number( f, 'f', 12 ) << "\r\n";
1603 }
1604 
1605 bool QgsLayoutExporter::georeferenceOutput( const QString &file, QgsLayoutItemMap *map, const QRectF &exportRegion, double dpi ) const
1606 {
1607  return georeferenceOutputPrivate( file, map, exportRegion, dpi, false );
1608 }
1609 
1610 bool QgsLayoutExporter::georeferenceOutputPrivate( const QString &file, QgsLayoutItemMap *map, const QRectF &exportRegion, double dpi, bool includeGeoreference, bool includeMetadata ) const
1611 {
1612  if ( !mLayout )
1613  return false;
1614 
1615  if ( !map && includeGeoreference )
1616  map = mLayout->referenceMap();
1617 
1618  std::unique_ptr<double[]> t;
1619 
1620  if ( map && includeGeoreference )
1621  {
1622  if ( dpi < 0 )
1623  dpi = mLayout->renderContext().dpi();
1624 
1625  t = computeGeoTransform( map, exportRegion, dpi );
1626  }
1627 
1628  // important - we need to manually specify the DPI in advance, as GDAL will otherwise
1629  // assume a DPI of 150
1630  CPLSetConfigOption( "GDAL_PDF_DPI", QString::number( dpi ).toLocal8Bit().constData() );
1631  gdal::dataset_unique_ptr outputDS( GDALOpen( file.toLocal8Bit().constData(), GA_Update ) );
1632  if ( outputDS )
1633  {
1634  if ( t )
1635  GDALSetGeoTransform( outputDS.get(), t.get() );
1636 
1637  if ( includeMetadata )
1638  {
1639  QString creationDateString;
1640  const QDateTime creationDateTime = mLayout->project()->metadata().creationDateTime();
1641  if ( creationDateTime.isValid() )
1642  {
1643  creationDateString = QStringLiteral( "D:%1" ).arg( mLayout->project()->metadata().creationDateTime().toString( QStringLiteral( "yyyyMMddHHmmss" ) ) );
1644  if ( creationDateTime.timeZone().isValid() )
1645  {
1646  int offsetFromUtc = creationDateTime.timeZone().offsetFromUtc( creationDateTime );
1647  creationDateString += ( offsetFromUtc >= 0 ) ? '+' : '-';
1648  offsetFromUtc = std::abs( offsetFromUtc );
1649  int offsetHours = offsetFromUtc / 3600;
1650  int offsetMins = ( offsetFromUtc % 3600 ) / 60;
1651  creationDateString += QStringLiteral( "%1'%2'" ).arg( offsetHours ).arg( offsetMins );
1652  }
1653  }
1654  GDALSetMetadataItem( outputDS.get(), "CREATION_DATE", creationDateString.toUtf8().constData(), nullptr );
1655 
1656  GDALSetMetadataItem( outputDS.get(), "AUTHOR", mLayout->project()->metadata().author().toUtf8().constData(), nullptr );
1657  const QString creator = QStringLiteral( "QGIS %1" ).arg( Qgis::version() );
1658  GDALSetMetadataItem( outputDS.get(), "CREATOR", creator.toUtf8().constData(), nullptr );
1659  GDALSetMetadataItem( outputDS.get(), "PRODUCER", creator.toUtf8().constData(), nullptr );
1660  GDALSetMetadataItem( outputDS.get(), "SUBJECT", mLayout->project()->metadata().abstract().toUtf8().constData(), nullptr );
1661  GDALSetMetadataItem( outputDS.get(), "TITLE", mLayout->project()->metadata().title().toUtf8().constData(), nullptr );
1662 
1663  const QgsAbstractMetadataBase::KeywordMap keywords = mLayout->project()->metadata().keywords();
1664  QStringList allKeywords;
1665  for ( auto it = keywords.constBegin(); it != keywords.constEnd(); ++it )
1666  {
1667  allKeywords.append( QStringLiteral( "%1: %2" ).arg( it.key(), it.value().join( ',' ) ) );
1668  }
1669  const QString keywordString = allKeywords.join( ';' );
1670  GDALSetMetadataItem( outputDS.get(), "KEYWORDS", keywordString.toUtf8().constData(), nullptr );
1671  }
1672 
1673  if ( t )
1674  GDALSetProjection( outputDS.get(), map->crs().toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED_GDAL ).toLocal8Bit().constData() );
1675  }
1676  CPLSetConfigOption( "GDAL_PDF_DPI", nullptr );
1677 
1678  return true;
1679 }
1680 
1681 QString nameForLayerWithItems( const QList< QGraphicsItem * > &items, unsigned int layerId )
1682 {
1683  if ( items.count() == 1 )
1684  {
1685  if ( QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( items.at( 0 ) ) )
1686  {
1687  QString name = layoutItem->displayName();
1688  // cleanup default item ID format
1689  if ( name.startsWith( '<' ) && name.endsWith( '>' ) )
1690  name = name.mid( 1, name.length() - 2 );
1691  return name;
1692  }
1693  }
1694  else if ( items.count() > 1 )
1695  {
1696  QStringList currentLayerItemTypes;
1697  for ( QGraphicsItem *item : items )
1698  {
1699  if ( QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( item ) )
1700  {
1701  const QString itemType = QgsApplication::layoutItemRegistry()->itemMetadata( layoutItem->type() )->visibleName();
1702  const QString itemTypePlural = QgsApplication::layoutItemRegistry()->itemMetadata( layoutItem->type() )->visiblePluralName();
1703  if ( !currentLayerItemTypes.contains( itemType ) && !currentLayerItemTypes.contains( itemTypePlural ) )
1704  currentLayerItemTypes << itemType;
1705  else if ( currentLayerItemTypes.contains( itemType ) )
1706  {
1707  currentLayerItemTypes.replace( currentLayerItemTypes.indexOf( itemType ), itemTypePlural );
1708  }
1709  }
1710  else
1711  {
1712  if ( !currentLayerItemTypes.contains( QObject::tr( "Other" ) ) )
1713  currentLayerItemTypes.append( QObject::tr( "Other" ) );
1714  }
1715  }
1716  return currentLayerItemTypes.join( QLatin1String( ", " ) );
1717  }
1718  return QObject::tr( "Layer %1" ).arg( layerId );
1719 }
1720 
1721 QgsLayoutExporter::ExportResult QgsLayoutExporter::handleLayeredExport( const QList<QGraphicsItem *> &items,
1722  const std::function<QgsLayoutExporter::ExportResult( unsigned int, const QgsLayoutItem::ExportLayerDetail & )> &exportFunc )
1723 {
1724  LayoutItemHider itemHider( items );
1725  ( void )itemHider;
1726 
1727  int prevType = -1;
1729  unsigned int layerId = 1;
1730  QgsLayoutItem::ExportLayerDetail layerDetails;
1731  itemHider.hideAll();
1732  const QList< QGraphicsItem * > itemsToIterate = itemHider.itemsToIterate();
1733  QList< QGraphicsItem * > currentLayerItems;
1734  for ( QGraphicsItem *item : itemsToIterate )
1735  {
1736  QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( item );
1737 
1738  bool canPlaceInExistingLayer = false;
1739  if ( layoutItem )
1740  {
1741  switch ( layoutItem->exportLayerBehavior() )
1742  {
1744  {
1745  switch ( prevItemBehavior )
1746  {
1748  canPlaceInExistingLayer = true;
1749  break;
1750 
1752  canPlaceInExistingLayer = prevType == -1 || prevType == layoutItem->type();
1753  break;
1754 
1757  canPlaceInExistingLayer = false;
1758  break;
1759  }
1760  break;
1761  }
1762 
1764  {
1765  switch ( prevItemBehavior )
1766  {
1769  canPlaceInExistingLayer = prevType == -1 || prevType == layoutItem->type();
1770  break;
1771 
1774  canPlaceInExistingLayer = false;
1775  break;
1776  }
1777  break;
1778  }
1779 
1781  {
1782  canPlaceInExistingLayer = false;
1783  break;
1784  }
1785 
1787  canPlaceInExistingLayer = false;
1788  break;
1789  }
1790  prevItemBehavior = layoutItem->exportLayerBehavior();
1791  prevType = layoutItem->type();
1792  }
1793  else
1794  {
1795  prevItemBehavior = QgsLayoutItem::MustPlaceInOwnLayer;
1796  }
1797 
1798  if ( canPlaceInExistingLayer )
1799  {
1800  currentLayerItems << item;
1801  item->show();
1802  }
1803  else
1804  {
1805  if ( !currentLayerItems.isEmpty() )
1806  {
1807  layerDetails.name = nameForLayerWithItems( currentLayerItems, layerId );
1808 
1809  ExportResult result = exportFunc( layerId, layerDetails );
1810  if ( result != Success )
1811  return result;
1812  layerId++;
1813  currentLayerItems.clear();
1814  }
1815 
1816  itemHider.hideAll();
1817  item->show();
1818 
1819  if ( layoutItem && layoutItem->exportLayerBehavior() == QgsLayoutItem::ItemContainsSubLayers )
1820  {
1821  int layoutItemLayerIdx = 0;
1823  mLayout->renderContext().setCurrentExportLayer( layoutItemLayerIdx );
1825  layoutItem->startLayeredExport();
1826  while ( layoutItem->nextExportPart() )
1827  {
1829  mLayout->renderContext().setCurrentExportLayer( layoutItemLayerIdx );
1831 
1832  layerDetails = layoutItem->exportLayerDetails();
1833  ExportResult result = exportFunc( layerId, layerDetails );
1834  if ( result != Success )
1835  return result;
1836  layerId++;
1837 
1838  layoutItemLayerIdx++;
1839  }
1840  layerDetails.mapLayerId.clear();
1842  mLayout->renderContext().setCurrentExportLayer( -1 );
1844  layoutItem->stopLayeredExport();
1845  currentLayerItems.clear();
1846  }
1847  else
1848  {
1849  currentLayerItems << item;
1850  }
1851  }
1852  }
1853  if ( !currentLayerItems.isEmpty() )
1854  {
1855  layerDetails.name = nameForLayerWithItems( currentLayerItems, layerId );
1856  ExportResult result = exportFunc( layerId, layerDetails );
1857  if ( result != Success )
1858  return result;
1859  }
1860  return Success;
1861 }
1862 
1863 QgsVectorSimplifyMethod QgsLayoutExporter::createExportSimplifyMethod()
1864 {
1865  QgsVectorSimplifyMethod simplifyMethod;
1867  simplifyMethod.setForceLocalOptimization( true );
1868  // we use SnappedToGridGlobal, because it avoids gaps and slivers between previously adjacent polygons
1870  simplifyMethod.setThreshold( 0.1f ); // (pixels). We are quite conservative here. This could possibly be bumped all the way up to 1. But let's play it safe.
1871  return simplifyMethod;
1872 }
1873 
1874 void QgsLayoutExporter::computeWorldFileParameters( double &a, double &b, double &c, double &d, double &e, double &f, double dpi ) const
1875 {
1876  if ( !mLayout )
1877  return;
1878 
1879  QgsLayoutItemMap *map = mLayout->referenceMap();
1880  if ( !map )
1881  {
1882  return;
1883  }
1884 
1885  int pageNumber = map->page();
1886  QgsLayoutItemPage *page = mLayout->pageCollection()->page( pageNumber );
1887  double pageY = page->pos().y();
1888  QSizeF pageSize = page->rect().size();
1889  QRectF pageRect( 0, pageY, pageSize.width(), pageSize.height() );
1890  computeWorldFileParameters( pageRect, a, b, c, d, e, f, dpi );
1891 }
1892 
1893 void QgsLayoutExporter::computeWorldFileParameters( const QRectF &exportRegion, double &a, double &b, double &c, double &d, double &e, double &f, double dpi ) const
1894 {
1895  if ( !mLayout )
1896  return;
1897 
1898  // World file parameters : affine transformation parameters from pixel coordinates to map coordinates
1899  QgsLayoutItemMap *map = mLayout->referenceMap();
1900  if ( !map )
1901  {
1902  return;
1903  }
1904 
1905  double destinationHeight = exportRegion.height();
1906  double destinationWidth = exportRegion.width();
1907 
1908  QRectF mapItemSceneRect = map->mapRectToScene( map->rect() );
1909  QgsRectangle mapExtent = map->extent();
1910 
1911  double alpha = map->mapRotation() / 180 * M_PI;
1912 
1913  double xRatio = mapExtent.width() / mapItemSceneRect.width();
1914  double yRatio = mapExtent.height() / mapItemSceneRect.height();
1915 
1916  double xCenter = mapExtent.center().x();
1917  double yCenter = mapExtent.center().y();
1918 
1919  // get the extent (in map units) for the region
1920  QPointF mapItemPos = map->pos();
1921  //adjust item position so it is relative to export region
1922  mapItemPos.rx() -= exportRegion.left();
1923  mapItemPos.ry() -= exportRegion.top();
1924 
1925  double xmin = mapExtent.xMinimum() - mapItemPos.x() * xRatio;
1926  double ymax = mapExtent.yMaximum() + mapItemPos.y() * yRatio;
1927  QgsRectangle paperExtent( xmin, ymax - destinationHeight * yRatio, xmin + destinationWidth * xRatio, ymax );
1928 
1929  double X0 = paperExtent.xMinimum();
1930  double Y0 = paperExtent.yMinimum();
1931 
1932  if ( dpi < 0 )
1933  dpi = mLayout->renderContext().dpi();
1934 
1935  int widthPx = static_cast< int >( dpi * destinationWidth / 25.4 );
1936  int heightPx = static_cast< int >( dpi * destinationHeight / 25.4 );
1937 
1938  double Ww = paperExtent.width() / widthPx;
1939  double Hh = paperExtent.height() / heightPx;
1940 
1941  // scaling matrix
1942  double s[6];
1943  s[0] = Ww;
1944  s[1] = 0;
1945  s[2] = X0;
1946  s[3] = 0;
1947  s[4] = -Hh;
1948  s[5] = Y0 + paperExtent.height();
1949 
1950  // rotation matrix
1951  double r[6];
1952  r[0] = std::cos( alpha );
1953  r[1] = -std::sin( alpha );
1954  r[2] = xCenter * ( 1 - std::cos( alpha ) ) + yCenter * std::sin( alpha );
1955  r[3] = std::sin( alpha );
1956  r[4] = std::cos( alpha );
1957  r[5] = - xCenter * std::sin( alpha ) + yCenter * ( 1 - std::cos( alpha ) );
1958 
1959  // result = rotation x scaling = rotation(scaling(X))
1960  a = r[0] * s[0] + r[1] * s[3];
1961  b = r[0] * s[1] + r[1] * s[4];
1962  c = r[0] * s[2] + r[1] * s[5] + r[2];
1963  d = r[3] * s[0] + r[4] * s[3];
1964  e = r[3] * s[1] + r[4] * s[4];
1965  f = r[3] * s[2] + r[4] * s[5] + r[5];
1966 }
1967 
1969 {
1970  if ( !layout )
1971  return false;
1972 
1973  QList< QgsLayoutItem *> items;
1974  layout->layoutItems( items );
1975 
1976  for ( QgsLayoutItem *currentItem : std::as_const( items ) )
1977  {
1978  // ignore invisible items, they won't affect the output in any way...
1979  if ( currentItem->isVisible() && currentItem->requiresRasterization() )
1980  return true;
1981  }
1982  return false;
1983 }
1984 
1986 {
1987  if ( !layout )
1988  return false;
1989 
1990  QList< QgsLayoutItem *> items;
1991  layout->layoutItems( items );
1992 
1993  for ( QgsLayoutItem *currentItem : std::as_const( items ) )
1994  {
1995  // ignore invisible items, they won't affect the output in any way...
1996  if ( currentItem->isVisible() && currentItem->containsAdvancedEffects() )
1997  return true;
1998  }
1999  return false;
2000 }
2001 
2002 QImage QgsLayoutExporter::createImage( const QgsLayoutExporter::ImageExportSettings &settings, int page, QRectF &bounds, bool &skipPage ) const
2003 {
2004  bounds = QRectF();
2005  skipPage = false;
2006 
2007  if ( settings.cropToContents )
2008  {
2009  if ( mLayout->pageCollection()->pageCount() == 1 )
2010  {
2011  // single page, so include everything
2012  bounds = mLayout->layoutBounds( true );
2013  }
2014  else
2015  {
2016  // multi page, so just clip to items on current page
2017  bounds = mLayout->pageItemBounds( page, true );
2018  }
2019  if ( bounds.width() <= 0 || bounds.height() <= 0 )
2020  {
2021  //invalid size, skip page
2022  skipPage = true;
2023  return QImage();
2024  }
2025 
2026  double pixelToLayoutUnits = mLayout->convertToLayoutUnits( QgsLayoutMeasurement( 1, QgsUnitTypes::LayoutPixels ) );
2027  bounds = bounds.adjusted( -settings.cropMargins.left() * pixelToLayoutUnits,
2028  -settings.cropMargins.top() * pixelToLayoutUnits,
2029  settings.cropMargins.right() * pixelToLayoutUnits,
2030  settings.cropMargins.bottom() * pixelToLayoutUnits );
2031  return renderRegionToImage( bounds, QSize(), settings.dpi );
2032  }
2033  else
2034  {
2035  return renderPageToImage( page, settings.imageSize, settings.dpi );
2036  }
2037 }
2038 
2039 int QgsLayoutExporter::firstPageToBeExported( QgsLayout *layout )
2040 {
2041  const int pageCount = layout->pageCollection()->pageCount();
2042  for ( int i = 0; i < pageCount; ++i )
2043  {
2044  if ( !layout->pageCollection()->shouldExportPage( i ) )
2045  {
2046  continue;
2047  }
2048 
2049  return i;
2050  }
2051  return 0; // shouldn't really matter -- we aren't exporting ANY pages!
2052 }
2053 
2055 {
2056  if ( details.page == 0 )
2057  {
2058  return details.directory + '/' + details.baseName + '.' + details.extension;
2059  }
2060  else
2061  {
2062  return details.directory + '/' + details.baseName + '_' + QString::number( details.page + 1 ) + '.' + details.extension;
2063  }
2064 }
2065 
2066 void QgsLayoutExporter::captureLabelingResults()
2067 {
2068  qDeleteAll( mLabelingResults );
2069  mLabelingResults.clear();
2070 
2071  QList< QgsLayoutItemMap * > maps;
2072  mLayout->layoutItems( maps );
2073 
2074  for ( QgsLayoutItemMap *map : std::as_const( maps ) )
2075  {
2076  mLabelingResults[ map->uuid() ] = map->mExportLabelingResults.release();
2077  }
2078 }
2079 
2080 bool QgsLayoutExporter::saveImage( const QImage &image, const QString &imageFilename, const QString &imageFormat, QgsProject *projectForMetadata )
2081 {
2082  QImageWriter w( imageFilename, imageFormat.toLocal8Bit().constData() );
2083  if ( imageFormat.compare( QLatin1String( "tiff" ), Qt::CaseInsensitive ) == 0 || imageFormat.compare( QLatin1String( "tif" ), Qt::CaseInsensitive ) == 0 )
2084  {
2085  w.setCompression( 1 ); //use LZW compression
2086  }
2087  if ( projectForMetadata )
2088  {
2089  w.setText( QStringLiteral( "Author" ), projectForMetadata->metadata().author() );
2090  const QString creator = QStringLiteral( "QGIS %1" ).arg( Qgis::version() );
2091  w.setText( QStringLiteral( "Creator" ), creator );
2092  w.setText( QStringLiteral( "Producer" ), creator );
2093  w.setText( QStringLiteral( "Subject" ), projectForMetadata->metadata().abstract() );
2094  w.setText( QStringLiteral( "Created" ), projectForMetadata->metadata().creationDateTime().toString( Qt::ISODate ) );
2095  w.setText( QStringLiteral( "Title" ), projectForMetadata->metadata().title() );
2096 
2097  const QgsAbstractMetadataBase::KeywordMap keywords = projectForMetadata->metadata().keywords();
2098  QStringList allKeywords;
2099  for ( auto it = keywords.constBegin(); it != keywords.constEnd(); ++it )
2100  {
2101  allKeywords.append( QStringLiteral( "%1: %2" ).arg( it.key(), it.value().join( ',' ) ) );
2102  }
2103  const QString keywordString = allKeywords.join( ';' );
2104  w.setText( QStringLiteral( "Keywords" ), keywordString );
2105  }
2106  return w.write( image );
2107 }
2108 
2109 #endif // ! QT_NO_PRINTER
QgsMargins::bottom
double bottom() const
Returns the bottom margin.
Definition: qgsmargins.h:90
QgsLayoutRenderContext::FlagUseAdvancedEffects
@ FlagUseAdvancedEffects
Enable advanced effects such as blend modes.
Definition: qgslayoutrendercontext.h:49
QgsLayoutExporter::ImageExportSettings::exportMetadata
bool exportMetadata
Indicates whether image export should include metadata generated from the layout's project's metadata...
Definition: qgslayoutexporter.h:207
QgsAbstractLayoutIterator::count
virtual int count() const =0
Returns the number of features to iterate over.
QgsLayoutRenderContext::flags
QgsLayoutRenderContext::Flags flags() const
Returns the current combination of flags used for rendering the layout.
Definition: qgslayoutrendercontext.cpp:52
QgsVectorSimplifyMethod::setForceLocalOptimization
void setForceLocalOptimization(bool localOptimization)
Sets where the simplification executes, after fetch the geometries from provider, or when supported,...
Definition: qgsvectorsimplifymethod.h:92
QgsFeedback::setProgress
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition: qgsfeedback.h:76
QgsAbstractGeoPdfExporter::ExportDetails::layerOrder
QStringList layerOrder
Optional list of layer IDs, in the order desired to appear in the generated GeoPDF file.
Definition: qgsabstractgeopdfexporter.h:286
QgsVectorSimplifyMethod
This class contains information how to simplify geometries fetched from a vector layer.
Definition: qgsvectorsimplifymethod.h:29
QgsRectangle::height
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
Definition: qgsrectangle.h:230
QgsAbstractGeoPdfExporter::ControlPoint
Contains details of a control point used during georeferencing GeoPDF outputs.
Definition: qgsabstractgeopdfexporter.h:145
QgsLayoutExporter::PdfExportSettings::exportMetadata
bool exportMetadata
Indicates whether PDF export should include metadata generated from the layout's project's metadata.
Definition: qgslayoutexporter.h:292
QgsLayoutExporter::georeferenceOutput
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.
Definition: qgslayoutexporter.cpp:1605
QgsAbstractLayoutIterator::next
virtual bool next()=0
Iterates to next feature, returning false if no more features exist to iterate over.
QgsPointXY::y
double y
Definition: qgspointxy.h:63
QgsLayoutRenderContext::measurementConverter
const QgsLayoutMeasurementConverter & measurementConverter() const
Returns the layout measurement converter to be used in the layout.
Definition: qgslayoutrendercontext.h:133
QgsLayoutRenderContext::FlagRenderLabelsByMapLayer
@ FlagRenderLabelsByMapLayer
When rendering map items to multi-layered exports, render labels belonging to different layers into s...
Definition: qgslayoutrendercontext.h:54
QgsLayoutExporter::PdfExportSettings::rasterizeWholeImage
bool rasterizeWholeImage
Set to true to force whole layout to be rasterized while exporting.
Definition: qgslayoutexporter.h:268
Qgis::version
static QString version()
Version string.
Definition: qgis.cpp:277
QgsMargins::top
double top() const
Returns the top margin.
Definition: qgsmargins.h:78
QgsLayoutRenderContext::setTextRenderFormat
void setTextRenderFormat(Qgis::TextRenderFormat format)
Sets the text render format, which dictates how text is rendered (e.g.
Definition: qgslayoutrendercontext.h:229
Qgis::TextRenderFormat
TextRenderFormat
Options for rendering text.
Definition: qgis.h:1331
QgsProjectMetadata::author
QString author() const
Returns the project author string.
Definition: qgsprojectmetadata.cpp:84
QgsLayoutItemPage
Item representing the paper in a layout.
Definition: qgslayoutitempage.h:58
QgsLayoutExporter::SvgExportSettings::exportAsLayers
bool exportAsLayers
Set to true to export as a layered SVG file.
Definition: qgslayoutexporter.h:519
QgsLayoutExporter::QgsLayoutExporter
QgsLayoutExporter(QgsLayout *layout)
Constructor for QgsLayoutExporter, for the specified layout.
Definition: qgslayoutexporter.cpp:148
QgsLayoutExporter::PdfExportSettings::simplifyGeometries
bool simplifyGeometries
Indicates whether vector geometries should be simplified to avoid redundant extraneous detail,...
Definition: qgslayoutexporter.h:313
qgslinestring.h
QgsUnitTypes::LayoutPixels
@ LayoutPixels
Pixels.
Definition: qgsunittypes.h:190
QgsProjectMetadata
A structured metadata store for a map layer.
Definition: qgsprojectmetadata.h:53
QgsLayout::layoutItems
void layoutItems(QList< T * > &itemList) const
Returns a list of layout items of a specific type.
Definition: qgslayout.h:122
QgsCoordinateReferenceSystem::WKT_PREFERRED_GDAL
@ WKT_PREFERRED_GDAL
Preferred format for conversion of CRS to WKT for use with the GDAL library.
Definition: qgscoordinatereferencesystem.h:682
QgsLayoutExporter::print
ExportResult print(QPrinter &printer, const QgsLayoutExporter::PrintExportSettings &settings)
Prints the layout to a printer, using the specified export settings.
Definition: qgslayoutexporter.cpp:867
QgsLayoutExporter::PdfExportSettings::exportThemes
QStringList exportThemes
Optional list of map themes to export as GeoPDF layer groups.
Definition: qgslayoutexporter.h:379
QgsLayout::renderContext
QgsLayoutRenderContext & renderContext()
Returns a reference to the layout's render context, which stores information relating to the current ...
Definition: qgslayout.cpp:359
QgsLayoutItemAbstractMetadata::visiblePluralName
QString visiblePluralName() const
Returns a translated, user visible name for plurals of the layout item class (e.g.
Definition: qgslayoutitemregistry.h:78
QgsLayoutExporter::PrintError
@ PrintError
Could not start printing to destination device.
Definition: qgslayoutexporter.h:144
QgsLayoutExporter::renderPage
void renderPage(QPainter *painter, int page) const
Renders a full page to a destination painter.
Definition: qgslayoutexporter.cpp:164
QgsRectangle::center
QgsPointXY center() const SIP_HOLDGIL
Returns the center point of the rectangle.
Definition: qgsrectangle.h:251
QgsLayoutExporter::ExportResult
ExportResult
Result codes for exporting layouts.
Definition: qgslayoutexporter.h:138
QgsVectorSimplifyMethod::setThreshold
void setThreshold(float threshold)
Sets the simplification threshold of the vector layer managed.
Definition: qgsvectorsimplifymethod.h:87
QgsLayoutExporter::ImageExportSettings::flags
QgsLayoutRenderContext::Flags flags
Layout context flags, which control how the export will be created.
Definition: qgslayoutexporter.h:213
QgsLayoutExporter::PdfExportSettings::writeGeoPdf
bool writeGeoPdf
true if GeoPDF files should be created, instead of normal PDF files.
Definition: qgslayoutexporter.h:326
QgsLayoutItem::nextExportPart
virtual bool nextExportPart()
Moves to the next export part for a multi-layered export item, during a multi-layered export.
Definition: qgslayoutitem.cpp:267
QgsPolygon::setExteriorRing
void setExteriorRing(QgsCurve *ring) override
Sets the exterior ring of the polygon.
Definition: qgspolygon.cpp:219
QgsLayoutExporter::PrintExportSettings::rasterizeWholeImage
bool rasterizeWholeImage
Set to true to force whole layout to be rasterized while exporting.
Definition: qgslayoutexporter.h:446
QgsRectangle::yMinimum
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:198
QgsLayoutItem::CanGroupWithAnyOtherItem
@ CanGroupWithAnyOtherItem
Item can be placed on a layer with any other item (default behavior)
Definition: qgslayoutitem.h:431
QgsLayoutExporter::PdfExportSettings::exportLayersAsSeperateFiles
bool exportLayersAsSeperateFiles
true if individual layers from the layout should be rendered to separate PDF files.
Definition: qgslayoutexporter.h:341
QgsAbstractGeoPdfExporter::ComponentLayerDetail::mapLayerId
QString mapLayerId
Associated map layer ID, or an empty string if this component layer is not associated with a map laye...
Definition: qgsabstractgeopdfexporter.h:124
QgsLayoutPoint::y
double y() const
Returns y coordinate of point.
Definition: qgslayoutpoint.h:86
QgsFeedback::isCanceled
bool isCanceled() const SIP_HOLDGIL
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:67
QgsLayoutExporter::PageExportDetails
Contains details of a page being exported by the class.
Definition: qgslayoutexporter.h:52
QgsLayoutExporter::renderRegion
void renderRegion(QPainter *painter, const QRectF &region) const
Renders a region from the layout to a painter.
Definition: qgslayoutexporter.cpp:256
QgsLayoutExporter::renderRegionToImage
QImage renderRegionToImage(const QRectF &region, QSize imageSize=QSize(), double dpi=-1) const
Renders a region of the layout to an image.
Definition: qgslayoutexporter.cpp:276
QgsAbstractGeoPdfExporter::GeoReferencedSection::pageBoundsPolygon
QgsPolygon pageBoundsPolygon
Bounds of the georeferenced section on the page, in millimeters, as a free-form polygon.
Definition: qgsabstractgeopdfexporter.h:179
QgsLayoutExporter::Success
@ Success
Export was successful.
Definition: qgslayoutexporter.h:140
qgsogrutils.h
QgsLayoutExporter::PageExportDetails::page
int page
Page number, where 0 = first page.
Definition: qgslayoutexporter.h:64
QgsLayoutExporter::layout
QgsLayout * layout() const
Returns the layout linked to this exporter.
Definition: qgslayoutexporter.cpp:159
qgslayoutgeopdfexporter.h
QgsAbstractMetadataBase::title
QString title() const
Returns the human readable name of the resource, typically displayed in search results.
Definition: qgsabstractmetadatabase.cpp:51
QgsAbstractGeoPdfExporter::ExportDetails
Definition: qgsabstractgeopdfexporter.h:198
QgsLayoutExporter::ImageExportSettings::pages
QList< int > pages
List of specific pages to export, or an empty list to export all pages.
Definition: qgslayoutexporter.h:193
QgsVectorSimplifyMethod::setSimplifyHints
void setSimplifyHints(SimplifyHints simplifyHints)
Sets the simplification hints of the vector layer managed.
Definition: qgsvectorsimplifymethod.h:62
QgsLayoutRenderContext::setFlags
void setFlags(QgsLayoutRenderContext::Flags flags)
Sets the combination of flags that will be used for rendering the layout.
Definition: qgslayoutrendercontext.cpp:28
QgsLayoutItem::page
int page() const
Returns the page the item is currently on, with the first page returning 0.
Definition: qgslayoutitem.cpp:542
QgsLayoutExporter::PdfExportSettings::forceVectorOutput
bool forceVectorOutput
Set to true to force vector object exports, even when the resultant appearance will differ from the l...
Definition: qgslayoutexporter.h:277
QgsAbstractGeoPdfExporter::GeoReferencedSection
Definition: qgsabstractgeopdfexporter.h:164
QgsLayoutExporter::Canceled
@ Canceled
Export was canceled.
Definition: qgslayoutexporter.h:141
QgsLineString
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:44
QgsAbstractGeoPdfExporter::ComponentLayerDetail::opacity
double opacity
Component opacity.
Definition: qgsabstractgeopdfexporter.h:136
QgsAbstractGeoPdfExporter::ExportDetails::layerIdToPdfLayerTreeNameMap
QMap< QString, QString > layerIdToPdfLayerTreeNameMap
Optional map of map layer ID to custom layer tree name to show in the created PDF file.
Definition: qgsabstractgeopdfexporter.h:269
QgsAbstractGeoPdfExporter::ExportDetails::title
QString title
Metadata title tag.
Definition: qgsabstractgeopdfexporter.h:225
QgsLayoutExporter::SvgExportSettings::flags
QgsLayoutRenderContext::Flags flags
Layout context flags, which control how the export will be created.
Definition: qgslayoutexporter.h:542
QgsLayoutExporter::ImageExportSettings::cropMargins
QgsMargins cropMargins
Crop to content margins, in pixels.
Definition: qgslayoutexporter.h:184
QgsLayout::pageCollection
QgsLayoutPageCollection * pageCollection()
Returns a pointer to the layout's page collection, which stores and manages page items in the layout.
Definition: qgslayout.cpp:459
QgsLayoutMeasurementConverter::convert
QgsLayoutMeasurement convert(QgsLayoutMeasurement measurement, QgsUnitTypes::LayoutUnit targetUnits) const
Converts a measurement from one unit to another.
Definition: qgslayoutmeasurementconverter.cpp:21
QgsLayoutItem::sizeWithUnits
QgsLayoutSize sizeWithUnits() const
Returns the item's current size, including units.
Definition: qgslayoutitem.h:673
QgsPaintEngineHack::fixEngineFlags
static void fixEngineFlags(QPaintEngine *engine)
Definition: qgspaintenginehack.cpp:51
QgsRectangle
A rectangle specified with double values.
Definition: qgsrectangle.h:41
QgsAbstractGeoPdfExporter::ExportDetails::keywords
QgsAbstractMetadataBase::KeywordMap keywords
Metadata keyword map.
Definition: qgsabstractgeopdfexporter.h:228
QgsLayoutItemRegistry::itemMetadata
QgsLayoutItemAbstractMetadata * itemMetadata(int type) const
Returns the metadata for the specified item type.
Definition: qgslayoutitemregistry.cpp:93
QgsProject
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition: qgsproject.h:103
QgsAbstractGeoPdfExporter::ExportDetails::georeferencedSections
QList< QgsAbstractGeoPdfExporter::GeoReferencedSection > georeferencedSections
List of georeferenced sections.
Definition: qgsabstractgeopdfexporter.h:207
QgsLayoutRenderContext::setSimplifyMethod
void setSimplifyMethod(const QgsVectorSimplifyMethod &method)
Sets the simplification setting to use when rendering vector layers.
Definition: qgslayoutrendercontext.h:251
QgsLayoutExporter::ImageExportSettings::predefinedMapScales
QVector< qreal > predefinedMapScales
A list of predefined scales to use with the layout.
Definition: qgslayoutexporter.h:220
qgslabelingresults.h
QgsAbstractGeoPdfExporter::ComponentLayerDetail::compositionMode
QPainter::CompositionMode compositionMode
Component composition mode.
Definition: qgsabstractgeopdfexporter.h:133
QgsLayoutItemMap::mapRotation
double mapRotation(QgsLayoutObject::PropertyValueType valueType=QgsLayoutObject::EvaluatedValue) const
Returns the rotation used for drawing the map within the layout item, in degrees clockwise.
Definition: qgslayoutitemmap.cpp:521
QgsLayoutItem::type
int type() const override
Returns a unique graphics item type identifier.
Definition: qgslayoutitem.cpp:124
QgsLayoutExporter::MemoryError
@ MemoryError
Unable to allocate memory required to export.
Definition: qgslayoutexporter.h:142
QgsUnitTypes::LayoutInches
@ LayoutInches
Inches.
Definition: qgsunittypes.h:186
QgsLayoutExporter::SvgExportSettings::predefinedMapScales
QVector< qreal > predefinedMapScales
A list of predefined scales to use with the layout.
Definition: qgslayoutexporter.h:565
qgslayoutexporter.h
QgsAbstractLayoutIterator
An abstract base class for QgsLayout based classes which can be exported by QgsLayoutExporter.
Definition: qgsabstractlayoutiterator.h:30
QgsLayoutExporter::SvgLayerError
@ SvgLayerError
Could not create layered SVG file.
Definition: qgslayoutexporter.h:145
QgsLayoutExporter::SvgExportSettings::exportLabelsToSeparateLayers
bool exportLabelsToSeparateLayers
Set to true to export labels to separate layers (grouped by map layer) in layered SVG exports.
Definition: qgslayoutexporter.h:529
QgsAbstractGeoPdfExporter::ExportDetails::initialLayerVisibility
QMap< QString, bool > initialLayerVisibility
Optional map of map layer ID to initial visibility state.
Definition: qgsabstractgeopdfexporter.h:277
Q_NOWARN_DEPRECATED_POP
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:2820
QgsLayoutRenderContext::setDpi
void setDpi(double dpi)
Sets the dpi for outputting the layout.
Definition: qgslayoutrendercontext.cpp:80
QgsMargins::left
double left() const
Returns the left margin.
Definition: qgsmargins.h:72
QgsLayoutExporter::exportToImage
ExportResult exportToImage(const QString &filePath, const QgsLayoutExporter::ImageExportSettings &settings)
Exports the layout to the filePath, using the specified export settings.
Definition: qgslayoutexporter.cpp:370
QgsLayoutItem::stopLayeredExport
virtual void stopLayeredExport()
Stops a multi-layer export operation.
Definition: qgslayoutitem.cpp:262
QgsLayoutItem::ExportLayerDetail
Contains details of a particular export layer relating to a layout item.
Definition: qgslayoutitem.h:492
QgsLayoutExporter::SvgExportSettings::dpi
double dpi
Resolution to export layout at. If dpi <= 0 the default layout dpi will be used.
Definition: qgslayoutexporter.h:491
QgsLayoutExporter::PageExportDetails::baseName
QString baseName
Base part of filename (i.e. file name without extension or '.')
Definition: qgslayoutexporter.h:58
QgsLayoutExporter::SvgExportSettings::textRenderFormat
Qgis::TextRenderFormat textRenderFormat
Text rendering format, which controls how text should be rendered in the export (e....
Definition: qgslayoutexporter.h:550
QgsLayoutItem::exportLayerBehavior
virtual ExportLayerBehavior exportLayerBehavior() const
Returns the behavior of this item during exporting to layered exports (e.g.
Definition: qgslayoutitem.cpp:247
QgsLayoutExporter::PrintExportSettings::dpi
double dpi
Resolution to export layout at. If dpi <= 0 the default layout dpi will be used.
Definition: qgslayoutexporter.h:439
QgsLayoutExporter::SvgExportSettings::cropMargins
QgsMargins cropMargins
Crop to content margins, in layout units.
Definition: qgslayoutexporter.h:512
QgsAbstractGeoPdfExporter::ExportDetails::useIso32000ExtensionFormatGeoreferencing
bool useIso32000ExtensionFormatGeoreferencing
true if ISO3200 extension format georeferencing should be used.
Definition: qgsabstractgeopdfexporter.h:236
QgsLayoutItem::ExportLayerDetail::name
QString name
User-friendly name for the export layer.
Definition: qgslayoutitem.h:495
QgsLayoutSize::toQSizeF
QSizeF toQSizeF() const
Converts the layout size to a QSizeF.
Definition: qgslayoutsize.cpp:47
QgsLayoutItem::ExportLayerBehavior
ExportLayerBehavior
Behavior of item when exporting to layered outputs.
Definition: qgslayoutitem.h:429
QgsAbstractMetadataBase::abstract
QString abstract() const
Returns a free-form description of the resource.
Definition: qgsabstractmetadatabase.cpp:61
QgsAbstractLayoutIterator::layout
virtual QgsLayout * layout()=0
Returns the layout associated with the iterator.
gdal::dataset_unique_ptr
std::unique_ptr< std::remove_pointer< GDALDatasetH >::type, GDALDatasetCloser > dataset_unique_ptr
Scoped GDAL dataset.
Definition: qgsogrutils.h:139
QgsLayoutRenderContext::setPredefinedScales
void setPredefinedScales(const QVector< qreal > &scales)
Sets the list of predefined scales to use with the layout.
Definition: qgslayoutrendercontext.cpp:129
qgsabstractlayoutiterator.h
qgsDoubleNear
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:2265
QgsFeedback
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition: qgsfeedback.h:44
QgsLayoutExporter::PdfExportSettings::predefinedMapScales
QVector< qreal > predefinedMapScales
A list of predefined scales to use with the layout.
Definition: qgslayoutexporter.h:386
QgsAbstractLayoutIterator::endRender
virtual bool endRender()=0
Ends the render, performing any required cleanup tasks.
QgsLayoutExporter::PageExportDetails::extension
QString extension
File suffix/extension (without the leading '.')
Definition: qgslayoutexporter.h:61
QgsAbstractGeoPdfExporter::ComponentLayerDetail::sourcePdfPath
QString sourcePdfPath
File path to the (already created) PDF to use as the source for this component layer.
Definition: qgsabstractgeopdfexporter.h:130
QgsLayoutExporter::ImageExportSettings::imageSize
QSize imageSize
Manual size in pixels for output image.
Definition: qgslayoutexporter.h:172
qgslayoutguidecollection.h
QgsAbstractGeoPdfExporter::ExportDetails::producer
QString producer
Metadata producer tag.
Definition: qgsabstractgeopdfexporter.h:213
QgsCoordinateReferenceSystem::toWkt
QString toWkt(WktVariant variant=WKT1_GDAL, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
Definition: qgscoordinatereferencesystem.cpp:1810
QgsLayoutItemMap::extent
QgsRectangle extent() const
Returns the current map extent.
Definition: qgslayoutitemmap.cpp:281
QgsMessageLog::logMessage
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).
Definition: qgsmessagelog.cpp:27
QgsLayoutExporter::PrintExportSettings::flags
QgsLayoutRenderContext::Flags flags
Layout context flags, which control how the export will be created.
Definition: qgslayoutexporter.h:451
nameForLayerWithItems
QString nameForLayerWithItems(const QList< QGraphicsItem * > &items, unsigned int layerId)
Definition: qgslayoutexporter.cpp:1681
QgsMargins::right
double right() const
Returns the right margin.
Definition: qgsmargins.h:84
QgsLayoutPageCollection::page
QgsLayoutItemPage * page(int pageNumber)
Returns a specific page (by pageNumber) from the collection.
Definition: qgslayoutpagecollection.cpp:472
QgsLayoutExporter
Handles rendering and exports of layouts to various formats.
Definition: qgslayoutexporter.h:46
QgsLayoutItem::MustPlaceInOwnLayer
@ MustPlaceInOwnLayer
Item must be placed in its own individual layer.
Definition: qgslayoutitem.h:433
QgsLayoutItem::ItemContainsSubLayers
@ ItemContainsSubLayers
Item contains multiple sublayers which must be individually exported.
Definition: qgslayoutitem.h:434
QgsLayoutExporter::PageExportDetails::directory
QString directory
Target folder.
Definition: qgslayoutexporter.h:55
QgsLayoutExporter::PdfExportSettings::flags
QgsLayoutRenderContext::Flags flags
Layout context flags, which control how the export will be created.
Definition: qgslayoutexporter.h:297
QgsLayoutExporter::PrintExportSettings
Contains settings relating to printing layouts.
Definition: qgslayoutexporter.h:431
QgsLayoutItem
Base class for graphical items within a QgsLayout.
Definition: qgslayoutitem.h:112
QgsRectangle::xMinimum
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:188
QgsLayoutGuide
Contains the configuration for a single snap guide used by a layout.
Definition: qgslayoutguidecollection.h:42
QgsLayoutExporter::renderPageToImage
QImage renderPageToImage(int page, QSize imageSize=QSize(), double dpi=-1) const
Renders a full page to an image.
Definition: qgslayoutexporter.cpp:187
qgslayout.h
QgsLayoutItemMap::crs
QgsCoordinateReferenceSystem crs() const
Returns coordinate reference system used for rendering the map.
Definition: qgslayoutitemmap.cpp:308
QgsProject::metadata
QgsProjectMetadata metadata
Definition: qgsproject.h:117
QgsLayoutItem::startLayeredExport
virtual void startLayeredExport()
Starts a multi-layer export operation.
Definition: qgslayoutitem.cpp:257
QgsPointXY
A class to represent a 2D point.
Definition: qgspointxy.h:58
QgsLayoutItemMap
Layout graphical items for displaying a map.
Definition: qgslayoutitemmap.h:317
QgsLayoutExporter::PdfExportSettings::textRenderFormat
Qgis::TextRenderFormat textRenderFormat
Text rendering format, which controls how text should be rendered in the export (e....
Definition: qgslayoutexporter.h:305
QgsLayoutRenderContext::FlagLosslessImageRendering
@ FlagLosslessImageRendering
Render images losslessly whenever possible, instead of the default lossy jpeg rendering used for some...
Definition: qgslayoutrendercontext.h:55
QgsLayoutExporter::PdfExportSettings::dpi
double dpi
Resolution to export layout at. If dpi <= 0 the default layout dpi will be used.
Definition: qgslayoutexporter.h:261
QgsLayoutRenderContext::setFlag
void setFlag(QgsLayoutRenderContext::Flag flag, bool on=true)
Enables or disables a particular rendering flag for the layout.
Definition: qgslayoutrendercontext.cpp:37
QgsLayoutExporter::ImageExportSettings
Contains settings relating to exporting layouts to raster images.
Definition: qgslayoutexporter.h:150
QgsLayoutExporter::generateFileName
virtual QString generateFileName(const PageExportDetails &details) const
Generates the file name for a page during export.
Definition: qgslayoutexporter.cpp:2054
QgsLayoutItem::uuid
virtual QString uuid() const
Returns the item identification string.
Definition: qgslayoutitem.h:345
QgsApplication::layoutItemRegistry
static QgsLayoutItemRegistry * layoutItemRegistry()
Returns the application's layout item registry, used for layout item types.
Definition: qgsapplication.cpp:2395
QgsAbstractGeoPdfExporter::ExportDetails::useOgcBestPracticeFormatGeoreferencing
bool useOgcBestPracticeFormatGeoreferencing
true if OGC "best practice" format georeferencing should be used.
Definition: qgsabstractgeopdfexporter.h:245
QgsAbstractGeoPdfExporter::ExportDetails::creator
QString creator
Metadata creator tag.
Definition: qgsabstractgeopdfexporter.h:216
QgsVectorSimplifyMethod::setSimplifyAlgorithm
void setSimplifyAlgorithm(SimplifyAlgorithm simplifyAlgorithm)
Sets the local simplification algorithm of the vector layer managed.
Definition: qgsvectorsimplifymethod.h:77
QgsRectangle::yMaximum
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:193
QgsAbstractGeoPdfExporter::ExportDetails::includeFeatures
bool includeFeatures
true if feature vector information (such as attributes) should be exported.
Definition: qgsabstractgeopdfexporter.h:250
QgsLayoutExporter::computeWorldFileParameters
void computeWorldFileParameters(double &a, double &b, double &c, double &d, double &e, double &f, double dpi=-1) const
Compute world file parameters.
Definition: qgslayoutexporter.cpp:1874
QgsProjectMetadata::creationDateTime
QDateTime creationDateTime() const
Returns the project's creation date/timestamp.
Definition: qgsprojectmetadata.cpp:94
QgsAbstractGeoPdfExporter::ComponentLayerDetail
Contains details of a particular input component to be used during PDF composition.
Definition: qgsabstractgeopdfexporter.h:117
QgsLayoutExporter::requiresRasterization
static bool requiresRasterization(const QgsLayout *layout)
Returns true if the specified layout contains visible items which have settings that require rasteriz...
Definition: qgslayoutexporter.cpp:1968
c
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
Definition: porting_processing.dox:1
QgsLayout
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition: qgslayout.h:50
QgsRectangle::width
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
Definition: qgsrectangle.h:223
QgsLayoutExporter::IteratorError
@ IteratorError
Error iterating over layout.
Definition: qgslayoutexporter.h:146
QgsMapLayer
Base class for all map layer types. This is the base class for all map layer types (vector,...
Definition: qgsmaplayer.h:72
QgsAbstractGeoPdfExporter::GeoReferencedSection::controlPoints
QList< QgsAbstractGeoPdfExporter::ControlPoint > controlPoints
List of control points corresponding to this georeferenced section.
Definition: qgsabstractgeopdfexporter.h:185
QgsVectorSimplifyMethod::SnappedToGridGlobal
@ SnappedToGridGlobal
Snap to a global grid based on the tolerance. Good for consistent results for incoming vertices,...
Definition: qgsvectorsimplifymethod.h:72
QgsLayoutItem::exportLayerDetails
virtual QgsLayoutItem::ExportLayerDetail exportLayerDetails() const
Returns the details for the specified current export layer.
Definition: qgslayoutitem.cpp:280
QgsPointXY::x
double x
Definition: qgspointxy.h:62
QgsAbstractGeoPdfExporter::ExportDetails::author
QString author
Metadata author tag.
Definition: qgsabstractgeopdfexporter.h:210
QgsLayoutExporter::SvgExportSettings
Contains settings relating to exporting layouts to SVG.
Definition: qgslayoutexporter.h:483
QgsAbstractGeoPdfExporter::ComponentLayerDetail::group
QString group
Optional group name, for arranging layers in top-level groups.
Definition: qgsabstractgeopdfexporter.h:127
QgsAbstractGeoPdfExporter::GeoReferencedSection::crs
QgsCoordinateReferenceSystem crs
Coordinate reference system for georeferenced section.
Definition: qgsabstractgeopdfexporter.h:182
QgsLayoutExporter::exportToSvg
ExportResult exportToSvg(const QString &filePath, const QgsLayoutExporter::SvgExportSettings &settings)
Exports the layout as an SVG to the filePath, using the specified export settings.
Definition: qgslayoutexporter.cpp:991
QgsLayoutExporter::SvgExportSettings::simplifyGeometries
bool simplifyGeometries
Indicates whether vector geometries should be simplified to avoid redundant extraneous detail,...
Definition: qgslayoutexporter.h:558
QgsLayoutExporter::ImageExportSettings::cropToContents
bool cropToContents
Set to true if image should be cropped so only parts of the layout containing items are exported.
Definition: qgslayoutexporter.h:178
QgsLayoutExporter::SvgExportSettings::exportMetadata
bool exportMetadata
Indicates whether SVG export should include RDF metadata generated from the layout's project's metada...
Definition: qgslayoutexporter.h:537
qgslayoutpagecollection.h
QgsLayoutRenderContext::FlagForceVectorOutput
@ FlagForceVectorOutput
Force output in vector format where possible, even if items require rasterization to keep their corre...
Definition: qgslayoutrendercontext.h:50
QgsAbstractLayoutIterator::beginRender
virtual bool beginRender()=0
Called when rendering begins, before iteration commences.
QgsAbstractMetadataBase::KeywordMap
QMap< QString, QStringList > KeywordMap
Map of vocabulary string to keyword list.
Definition: qgsabstractmetadatabase.h:78
QgsLayoutSize
This class provides a method of storing sizes, consisting of a width and height, for use in QGIS layo...
Definition: qgslayoutsize.h:40
QgsAbstractGeoPdfExporter::ExportDetails::subject
QString subject
Metadata subject tag.
Definition: qgsabstractgeopdfexporter.h:222
QgsAbstractGeoPdfExporter::ExportDetails::creationDateTime
QDateTime creationDateTime
Metadata creation datetime.
Definition: qgsabstractgeopdfexporter.h:219
QgsLayoutRenderContext::dpi
double dpi() const
Returns the dpi for outputting the layout.
Definition: qgslayoutrendercontext.cpp:89
QgsLayoutExporter::ImageExportSettings::generateWorldFile
bool generateWorldFile
Set to true to generate an external world file alongside exported images.
Definition: qgslayoutexporter.h:199
QgsLayoutExporter::SvgExportSettings::cropToContents
bool cropToContents
Set to true if image should be cropped so only parts of the layout containing items are exported.
Definition: qgslayoutexporter.h:506
QgsLayoutExporter::PdfExportSettings
Contains settings relating to exporting layouts to PDF.
Definition: qgslayoutexporter.h:253
QgsLayoutExporter::containsAdvancedEffects
static bool containsAdvancedEffects(const QgsLayout *layout)
Returns true if the specified layout contains visible items which have settings such as opacity which...
Definition: qgslayoutexporter.cpp:1985
QgsLayoutExporter::labelingResults
QMap< QString, QgsLabelingResults * > labelingResults()
Returns the labeling results for all map items included in the export.
Definition: qgslayoutexporter.cpp:1216
QgsLayoutExporter::PrintExportSettings::predefinedMapScales
QVector< qreal > predefinedMapScales
A list of predefined scales to use with the layout.
Definition: qgslayoutexporter.h:458
QgsUnitTypes::LayoutMillimeters
@ LayoutMillimeters
Millimeters.
Definition: qgsunittypes.h:183
QgsLayoutExporter::exportToPdfs
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.
Definition: qgslayoutexporter.cpp:818
QgsMasterLayoutInterface
Interface for master layout type objects, such as print layouts and reports.
Definition: qgsmasterlayoutinterface.h:42
QgsLayoutPageCollection::shouldExportPage
bool shouldExportPage(int page) const
Returns whether the specified page number should be included in exports of the layouts.
Definition: qgslayoutpagecollection.cpp:548
QgsAbstractGeoPdfExporter::ComponentLayerDetail::name
QString name
User-friendly name for the generated PDF layer.
Definition: qgsabstractgeopdfexporter.h:121
QgsLayoutPageCollection::pageCount
int pageCount() const
Returns the number of pages in the collection.
Definition: qgslayoutpagecollection.cpp:467
QgsLayoutItemAbstractMetadata::visibleName
QString visibleName() const
Returns a translated, user visible name for the layout item class.
Definition: qgslayoutitemregistry.h:72
QgsLayoutRenderContext::FlagAntialiasing
@ FlagAntialiasing
Use antialiasing when drawing items.
Definition: qgslayoutrendercontext.h:48
QgsLayoutExporter::SvgExportSettings::forceVectorOutput
bool forceVectorOutput
Set to true to force vector object exports, even when the resultant appearance will differ from the l...
Definition: qgslayoutexporter.h:500
QgsLayoutPoint
This class provides a method of storing points, consisting of an x and y coordinate,...
Definition: qgslayoutpoint.h:39
QgsLayoutExporter::exportToPdf
ExportResult exportToPdf(const QString &filePath, const QgsLayoutExporter::PdfExportSettings &settings)
Exports the layout as a PDF to the filePath, using the specified export settings.
Definition: qgslayoutexporter.cpp:528
Q_NOWARN_DEPRECATED_PUSH
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:2819
QgsAbstractMetadataBase::identifier
QString identifier() const
A reference, URI, URL or some other mechanism to identify the resource.
Definition: qgsabstractmetadatabase.cpp:21
QgsAbstractLayoutIterator::filePath
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.
qgsfeedback.h
qgspaintenginehack.h
QgsAbstractMetadataBase::keywords
QgsAbstractMetadataBase::KeywordMap keywords() const
Returns the keywords map, which is a set of descriptive keywords associated with the resource.
Definition: qgsabstractmetadatabase.cpp:86
QgsLayoutItem::CanGroupWithItemsOfSameType
@ CanGroupWithItemsOfSameType
Item can only be placed on layers with other items of the same type, but multiple items of this type ...
Definition: qgslayoutitem.h:432
QgsLayoutPoint::x
double x() const
Returns x coordinate of point.
Definition: qgslayoutpoint.h:72
QgsAbstractGeoPdfExporter::ExportDetails::dpi
double dpi
Output DPI.
Definition: qgsabstractgeopdfexporter.h:204
QgsLayoutExporter::ImageExportSettings::dpi
double dpi
Resolution to export layout at. If dpi <= 0 the default layout dpi will be used.
Definition: qgslayoutexporter.h:158
QgsVectorSimplifyMethod::GeometrySimplification
@ GeometrySimplification
The geometries can be simplified using the current map2pixel context state.
Definition: qgsvectorsimplifymethod.h:53
QgsLayoutExporter::~QgsLayoutExporter
virtual ~QgsLayoutExporter()
Definition: qgslayoutexporter.cpp:154
QgsAbstractGeoPdfExporter::ExportDetails::pageSizeMm
QSizeF pageSizeMm
Page size, in millimeters.
Definition: qgsabstractgeopdfexporter.h:201
QgsLayoutMeasurement
This class provides a method of storing measurements for use in QGIS layouts using a variety of diffe...
Definition: qgslayoutmeasurement.h:33
QgsAbstractGeoPdfExporter::ExportDetails::customLayerTreeGroups
QMap< QString, QString > customLayerTreeGroups
Optional map of map layer ID to custom logical layer tree group in created PDF file.
Definition: qgsabstractgeopdfexporter.h:262
QgsLayoutExporter::takeLabelingResults
QMap< QString, QgsLabelingResults * > takeLabelingResults()
Takes the labeling results for all map items included in the export.
Definition: qgslayoutexporter.cpp:1221
QgsLayoutExporter::FileError
@ FileError
Could not write to destination file, likely due to a lock held by another application.
Definition: qgslayoutexporter.h:143
qgslayoutitemmap.h
qgsmessagelog.h
QgsLayoutExporter::PdfExportSettings::appendGeoreference
bool appendGeoreference
Indicates whether PDF export should append georeference data.
Definition: qgslayoutexporter.h:284