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