33#include <QImageWriter> 
   35#include <QSvgGenerator> 
   44class LayoutContextPreviewSettingRestorer
 
   48    LayoutContextPreviewSettingRestorer( 
QgsLayout *layout )
 
   50      , mPreviousSetting( layout->renderContext().mIsPreviewRender )
 
   52      mLayout->renderContext().mIsPreviewRender = 
false;
 
   55    ~LayoutContextPreviewSettingRestorer()
 
   57      mLayout->renderContext().mIsPreviewRender = mPreviousSetting;
 
   60    LayoutContextPreviewSettingRestorer( 
const LayoutContextPreviewSettingRestorer &other ) = 
delete;
 
   61    LayoutContextPreviewSettingRestorer &operator=( 
const LayoutContextPreviewSettingRestorer &other ) = 
delete;
 
   65    bool mPreviousSetting = 
false;
 
   75      const QList< QgsLayoutGuide * > guides = mLayout->guides().guides();
 
   78        mPrevVisibility.insert( guide, guide->item()->isVisible() );
 
   79        guide->item()->setVisible( 
false );
 
   85      for ( 
auto it = mPrevVisibility.constBegin(); it != mPrevVisibility.constEnd(); ++it )
 
   87        it.key()->item()->setVisible( it.value() );
 
   91    LayoutGuideHider( 
const LayoutGuideHider &other ) = 
delete;
 
   92    LayoutGuideHider &operator=( 
const LayoutGuideHider &other ) = 
delete;
 
   96    QHash< QgsLayoutGuide *, bool > mPrevVisibility;
 
  102    explicit LayoutItemHider( 
const QList<QGraphicsItem *> &items )
 
  104      mItemsToIterate.reserve( items.count() );
 
  105      for ( QGraphicsItem *item : items )
 
  107        const bool isVisible = item->isVisible();
 
  108        mPrevVisibility[item] = isVisible;
 
  110          mItemsToIterate.append( item );
 
  112          layoutItem->setProperty( 
"wasVisible", isVisible );
 
  120      for ( 
auto it = mPrevVisibility.constBegin(); it != mPrevVisibility.constEnd(); ++it )
 
  128      for ( 
auto it = mPrevVisibility.constBegin(); it != mPrevVisibility.constEnd(); ++it )
 
  130        it.key()->setVisible( it.value() );
 
  132          layoutItem->setProperty( 
"wasVisible", QVariant() );
 
  136    QList< QGraphicsItem * > itemsToIterate()
 const { 
return mItemsToIterate; }
 
  138    LayoutItemHider( 
const LayoutItemHider &other ) = 
delete;
 
  139    LayoutItemHider &operator=( 
const LayoutItemHider &other ) = 
delete;
 
  143    QList<QGraphicsItem * > mItemsToIterate;
 
  144    QHash<QGraphicsItem *, bool> mPrevVisibility;
 
  161  qDeleteAll( mLabelingResults );
 
 
  174  if ( mLayout->pageCollection()->pageCount() <= page || page < 0 )
 
  185  LayoutContextPreviewSettingRestorer restorer( mLayout );
 
  188  QRectF paperRect = QRectF( pageItem->pos().x(), pageItem->pos().y(), pageItem->rect().width(), pageItem->rect().height() );
 
 
  197  if ( mLayout->pageCollection()->pageCount() <= page || page < 0 )
 
  208  LayoutContextPreviewSettingRestorer restorer( mLayout );
 
  211  QRectF paperRect = QRectF( pageItem->pos().x(), pageItem->pos().y(), pageItem->rect().width(), pageItem->rect().height() );
 
  213  const double imageAspectRatio = 
static_cast< double >( imageSize.width() ) / imageSize.height();
 
  214  const double paperAspectRatio = paperRect.width() / paperRect.height();
 
  215  if ( imageSize.isValid() && ( !
qgsDoubleNear( imageAspectRatio, paperAspectRatio, 0.008 ) ) )
 
  220    QgsMessageLog::logMessage( QObject::tr( 
"Ignoring custom image size because aspect ratio %1 does not match paper ratio %2" ).arg( QString::number( imageAspectRatio, 
'g', 3 ), QString::number( paperAspectRatio, 
'g', 3 ) ), QStringLiteral( 
"Layout" ), 
Qgis::MessageLevel::Warning );
 
 
  228class LayoutItemCacheSettingRestorer
 
  232    LayoutItemCacheSettingRestorer( 
QgsLayout *layout )
 
  235      const QList< QGraphicsItem * > items = mLayout->items();
 
  236      for ( QGraphicsItem *item : items )
 
  238        mPrevCacheMode.insert( item, item->cacheMode() );
 
  239        item->setCacheMode( QGraphicsItem::NoCache );
 
  243    ~LayoutItemCacheSettingRestorer()
 
  245      for ( 
auto it = mPrevCacheMode.constBegin(); it != mPrevCacheMode.constEnd(); ++it )
 
  247        it.key()->setCacheMode( it.value() );
 
  251    LayoutItemCacheSettingRestorer( 
const LayoutItemCacheSettingRestorer &other ) = 
delete;
 
  252    LayoutItemCacheSettingRestorer &operator=( 
const LayoutItemCacheSettingRestorer &other ) = 
delete;
 
  256    QHash< QGraphicsItem *, QGraphicsItem::CacheMode > mPrevCacheMode;
 
  263  QPaintDevice *paintDevice = painter->device();
 
  264  if ( !paintDevice || !mLayout )
 
  269  LayoutItemCacheSettingRestorer cacheRestorer( mLayout );
 
  270  ( void )cacheRestorer;
 
  271  LayoutContextPreviewSettingRestorer restorer( mLayout );
 
  273  LayoutGuideHider guideHider( mLayout );
 
  278  mLayout->render( painter, QRectF( 0, 0, paintDevice->width(), paintDevice->height() ), region );
 
 
  286  LayoutContextPreviewSettingRestorer restorer( mLayout );
 
  289  double resolution = mLayout->renderContext().dpi();
 
  291  if ( imageSize.isValid() )
 
  295    resolution = ( imageSize.width() / region.width()
 
  296                   + imageSize.height() / region.height() ) / 2.0 * oneInchInLayoutUnits;
 
  304  int width = imageSize.isValid() ? imageSize.width()
 
  305              : 
static_cast< int >( resolution * region.width() / oneInchInLayoutUnits );
 
  306  int height = imageSize.isValid() ? imageSize.height()
 
  307               : 
static_cast< int >( resolution * region.height() / oneInchInLayoutUnits );
 
  309  QImage image( QSize( width, height ), QImage::Format_ARGB32 );
 
  310  if ( !image.isNull() )
 
  313    if ( width > 32768 || height > 32768 )
 
  314      QgsMessageLog::logMessage( QObject::tr( 
"Error: output width or height is larger than 32768 pixel, result will be clipped" ) );
 
  315    image.setDotsPerMeterX( 
static_cast< int >( std::round( resolution / 25.4 * 1000 ) ) );
 
  316    image.setDotsPerMeterY( 
static_cast< int>( std::round( resolution / 25.4 * 1000 ) ) );
 
  317    image.fill( Qt::transparent );
 
  318    QPainter imagePainter( &image );
 
  320    if ( !imagePainter.isActive() )
 
 
  328class LayoutContextSettingsRestorer
 
  333    LayoutContextSettingsRestorer( 
QgsLayout *layout )
 
  335      , mPreviousDpi( layout->renderContext().dpi() )
 
  336      , mPreviousFlags( layout->renderContext().flags() )
 
  337      , mPreviousTextFormat( layout->renderContext().textRenderFormat() )
 
  338      , mPreviousExportLayer( layout->renderContext().currentExportLayer() )
 
  339      , mPreviousSimplifyMethod( layout->renderContext().simplifyMethod() )
 
  340      , mPreviousMaskSettings( layout->renderContext().maskSettings() )
 
  341      , mExportThemes( layout->renderContext().exportThemes() )
 
  342      , mPredefinedScales( layout->renderContext().predefinedScales() )
 
  347    ~LayoutContextSettingsRestorer()
 
  349      mLayout->renderContext().setDpi( mPreviousDpi );
 
  350      mLayout->renderContext().setFlags( mPreviousFlags );
 
  351      mLayout->renderContext().setTextRenderFormat( mPreviousTextFormat );
 
  353      mLayout->renderContext().setCurrentExportLayer( mPreviousExportLayer );
 
  355      mLayout->renderContext().setSimplifyMethod( mPreviousSimplifyMethod );
 
  356      mLayout->renderContext().setMaskSettings( mPreviousMaskSettings );
 
  357      mLayout->renderContext().setExportThemes( mExportThemes );
 
  358      mLayout->renderContext().setPredefinedScales( mPredefinedScales );
 
  361    LayoutContextSettingsRestorer( 
const LayoutContextSettingsRestorer &other ) = 
delete;
 
  362    LayoutContextSettingsRestorer &operator=( 
const LayoutContextSettingsRestorer &other ) = 
delete;
 
  366    double mPreviousDpi = 0;
 
  369    int mPreviousExportLayer = 0;
 
  372    QStringList mExportThemes;
 
  373    QVector< double > mPredefinedScales;
 
  384  if ( settings.
dpi <= 0 )
 
  385    settings.
dpi = mLayout->renderContext().dpi();
 
  387  mErrorFileName.clear();
 
  389  int worldFilePageNo = -1;
 
  392    worldFilePageNo = referenceMap->page();
 
  395  QFileInfo fi( filePath );
 
  397  if ( !dir.exists( fi.absolutePath() ) )
 
  399    dir.mkpath( fi.absolutePath() );
 
  404  pageDetails.
baseName = fi.completeBaseName();
 
  407  LayoutContextPreviewSettingRestorer restorer( mLayout );
 
  409  LayoutContextSettingsRestorer dpiRestorer( mLayout );
 
  411  mLayout->renderContext().setDpi( settings.
dpi );
 
  412  mLayout->renderContext().setFlags( settings.
flags );
 
  416  if ( settings.
pages.empty() )
 
  418    for ( 
int page = 0; page < mLayout->pageCollection()->pageCount(); ++page )
 
  423    for ( 
int page : std::as_const( settings.
pages ) )
 
  425      if ( page >= 0 && page < mLayout->pageCollection()->pageCount() )
 
  430  for ( 
int page : std::as_const( pages ) )
 
  432    if ( !mLayout->pageCollection()->shouldExportPage( page ) )
 
  439    QImage image = createImage( settings, page, bounds, skip );
 
  444    pageDetails.
page = page;
 
  447    if ( image.isNull() )
 
  449      mErrorFileName = outputFilePath;
 
  453    if ( !saveImage( image, outputFilePath, pageDetails.
extension, settings.
exportMetadata ? mLayout->project() : nullptr ) )
 
  455      mErrorFileName = outputFilePath;
 
  459    const bool shouldGeoreference = ( page == worldFilePageNo );
 
  460    if ( shouldGeoreference )
 
  462      georeferenceOutputPrivate( outputFilePath, 
nullptr, bounds, settings.
dpi, shouldGeoreference );
 
  467        double a, b, 
c, d, e, f;
 
  468        if ( bounds.isValid() )
 
  473        QFileInfo fi( outputFilePath );
 
  475        QString outputSuffix = fi.suffix();
 
  476        QString worldFileName = fi.absolutePath() + 
'/' + fi.completeBaseName() + 
'.' 
  477                                + outputSuffix.at( 0 ) + outputSuffix.at( fi.suffix().size() - 1 ) + 
'w';
 
  479        writeWorldFile( worldFileName, a, b, 
c, d, e, f );
 
  484  captureLabelingResults();
 
 
  495  int total = iterator->
count();
 
  496  double step = total > 0 ? 100.0 / total : 100.0;
 
  498  while ( iterator->
next() )
 
  503        feedback->setProperty( 
"progress", QObject::tr( 
"Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
 
  505        feedback->setProperty( 
"progress", QObject::tr( 
"Exporting section %1" ).arg( i + 1 ).arg( total ) );
 
  515    QString filePath = iterator->
filePath( baseFilePath, extension );
 
  520        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 ) );
 
 
  540  if ( !mLayout || mLayout->pageCollection()->pageCount() == 0 )
 
  544  if ( settings.
dpi <= 0 )
 
  545    settings.
dpi = mLayout->renderContext().dpi();
 
  547  mErrorFileName.clear();
 
  549  LayoutContextPreviewSettingRestorer restorer( mLayout );
 
  551  LayoutContextSettingsRestorer contextRestorer( mLayout );
 
  552  ( void )contextRestorer;
 
  553  mLayout->renderContext().setDpi( settings.
dpi );
 
  555  mLayout->renderContext().setMaskSettings( createExportMaskSettings() );
 
  559    mLayout->renderContext().setSimplifyMethod( createExportSimplifyMethod() );
 
  562  std::unique_ptr< QgsLayoutGeoPdfExporter > geoPdfExporter;
 
  564    geoPdfExporter = std::make_unique< QgsLayoutGeoPdfExporter >( mLayout );
 
  566  mLayout->renderContext().setFlags( settings.
flags );
 
  579  mLayout->renderContext().setExportThemes( settings.
exportThemes );
 
  591    const QList<QGraphicsItem *> items = mLayout->items( Qt::AscendingOrder );
 
  593    QList< QgsLayoutGeoPdfExporter::ComponentLayerDetail > pdfComponents;
 
  602      component.
name = layerDetail.name;
 
  603      component.
mapLayerId = layerDetail.mapLayerId;
 
  604      component.
opacity = layerDetail.opacity;
 
  606      component.
group = layerDetail.mapTheme;
 
  607      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' ) ) );
 
  608      pdfComponents << component;
 
  610      preparePrintAsPdf( mLayout, &printer, component.
sourcePdfPath );
 
  611      preparePrint( mLayout, &printer, 
false );
 
  613      if ( !p.begin( &printer ) )
 
  621      return layerExportResult;
 
  623    result = handleLayeredExport( items, exportFunc );
 
  627    if ( settings.writeGeoPdf )
 
  630      details.
dpi = settings.dpi;
 
  632      QgsLayoutSize pageSize = mLayout->pageCollection()->page( 0 )->sizeWithUnits();
 
  636      if ( settings.exportMetadata )
 
  639        details.
author = mLayout->project()->metadata().author();
 
  642        details.
creationDateTime = mLayout->project()->metadata().creationDateTime();
 
  643        details.
subject = mLayout->project()->metadata().abstract();
 
  644        details.
title = mLayout->project()->metadata().title();
 
  645        details.
keywords = mLayout->project()->metadata().keywords();
 
  648      const QList< QgsMapLayer * > layers = mLayout->project()->mapLayers().values();
 
  654      if ( settings.appendGeoreference )
 
  657        QList< QgsLayoutItemMap * > maps;
 
  658        mLayout->layoutItems( maps );
 
  662          georef.
crs = map->crs();
 
  664          const QPointF topLeft = map->mapToScene( QPointF( 0, 0 ) );
 
  665          const QPointF topRight = map->mapToScene( QPointF( map->rect().width(), 0 ) );
 
  666          const QPointF bottomLeft = map->mapToScene( QPointF( 0, map->rect().height() ) );
 
  667          const QPointF bottomRight = map->mapToScene( QPointF( map->rect().width(), map->rect().height() ) );
 
  680          const QTransform t = map->layoutToMapCoordsTransform();
 
  681          const QgsPointXY topLeftMap = t.map( topLeft );
 
  682          const QgsPointXY topRightMap = t.map( topRight );
 
  683          const QgsPointXY bottomLeftMap = t.map( bottomLeft );
 
  684          const QgsPointXY bottomRightMap = t.map( bottomRight );
 
  696      details.
layerOrder = geoPdfExporter->layerOrder();
 
  702      if ( !geoPdfExporter->finalize( pdfComponents, filePath, details ) )
 
  705        mErrorMessage = geoPdfExporter->errorMessage();
 
  715    QPdfWriter printer = QPdfWriter( filePath );
 
  716    preparePrintAsPdf( mLayout, &printer, filePath );
 
  717    preparePrint( mLayout, &printer, 
false );
 
  719    if ( !p.begin( &printer ) )
 
  728    bool shouldAppendGeoreference = settings.
appendGeoreference && mLayout && mLayout->referenceMap() && mLayout->referenceMap()->page() == 0;
 
  731      georeferenceOutputPrivate( filePath, 
nullptr, QRectF(), settings.
dpi, shouldAppendGeoreference, settings.
exportMetadata );
 
  734  captureLabelingResults();
 
 
  747  QPdfWriter printer = QPdfWriter( fileName );
 
  750  int total = iterator->
count();
 
  751  double step = total > 0 ? 100.0 / total : 100.0;
 
  754  while ( iterator->
next() )
 
  759        feedback->setProperty( 
"progress", QObject::tr( 
"Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
 
  761        feedback->setProperty( 
"progress", QObject::tr( 
"Exporting section %1" ).arg( i + 1 ) );
 
  773    LayoutContextPreviewSettingRestorer restorer( iterator->
layout() );
 
  775    LayoutContextSettingsRestorer contextRestorer( iterator->
layout() );
 
  776    ( void )contextRestorer;
 
  799      preparePrintAsPdf( iterator->
layout(), &printer, fileName );
 
  800      preparePrint( iterator->
layout(), &printer, 
false );
 
  802      if ( !p.begin( &printer ) )
 
  816        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 ) );
 
 
  843  int total = iterator->
count();
 
  844  double step = total > 0 ? 100.0 / total : 100.0;
 
  846  while ( iterator->
next() )
 
  851        feedback->setProperty( 
"progress", QObject::tr( 
"Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
 
  853        feedback->setProperty( 
"progress", QObject::tr( 
"Exporting section %1" ).arg( i + 1 ).arg( total ) );
 
  862    QString filePath = iterator->
filePath( baseFilePath, QStringLiteral( 
"pdf" ) );
 
  869        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 ) );
 
 
  887#if defined( HAVE_QTPRINTER ) 
  894  if ( settings.
dpi <= 0 )
 
  895    settings.
dpi = mLayout->renderContext().dpi();
 
  897  mErrorFileName.clear();
 
  899  LayoutContextPreviewSettingRestorer restorer( mLayout );
 
  901  LayoutContextSettingsRestorer contextRestorer( mLayout );
 
  902  ( void )contextRestorer;
 
  903  mLayout->renderContext().setDpi( settings.
dpi );
 
  905  mLayout->renderContext().setFlags( settings.
flags );
 
  912  preparePrint( mLayout, &printer, 
true );
 
  914  if ( !p.begin( &printer ) )
 
  923  captureLabelingResults();
 
  934  PrintExportSettings settings = s;
 
  938  int total = iterator->
count();
 
  939  double step = total > 0 ? 100.0 / total : 100.0;
 
  942  while ( iterator->
next() )
 
  947        feedback->setProperty( 
"progress", QObject::tr( 
"Printing %1 of %2" ).arg( i + 1 ).arg( total ) );
 
  949        feedback->setProperty( 
"progress", QObject::tr( 
"Printing section %1" ).arg( i + 1 ).arg( total ) );
 
  961    LayoutContextPreviewSettingRestorer restorer( iterator->
layout() );
 
  963    LayoutContextSettingsRestorer contextRestorer( iterator->
layout() );
 
  964    ( void )contextRestorer;
 
  977      preparePrint( iterator->
layout(), &printer, 
true );
 
  979      if ( !p.begin( &printer ) )
 
  989    ExportResult result = exporter.printPrivate( &printer, p, !first, settings.dpi, settings.rasterizeWholeImage );
 
  993      error = exporter.errorMessage();
 
 1016  if ( settings.
dpi <= 0 )
 
 1017    settings.
dpi = mLayout->renderContext().dpi();
 
 1019  mErrorFileName.clear();
 
 1021  LayoutContextPreviewSettingRestorer restorer( mLayout );
 
 1023  LayoutContextSettingsRestorer contextRestorer( mLayout );
 
 1024  ( void )contextRestorer;
 
 1025  mLayout->renderContext().setDpi( settings.
dpi );
 
 1027  mLayout->renderContext().setFlags( settings.
flags );
 
 1031  mLayout->renderContext().setMaskSettings( createExportMaskSettings() );
 
 1035    mLayout->renderContext().setSimplifyMethod( createExportSimplifyMethod() );
 
 1038  QFileInfo fi( filePath );
 
 1041  pageDetails.
baseName = fi.baseName();
 
 1042  pageDetails.
extension = fi.completeSuffix();
 
 1046  for ( 
int i = 0; i < mLayout->pageCollection()->pageCount(); ++i )
 
 1048    if ( !mLayout->pageCollection()->shouldExportPage( i ) )
 
 1053    pageDetails.
page = i;
 
 1060      if ( mLayout->pageCollection()->pageCount() == 1 )
 
 1063        bounds = mLayout->layoutBounds( 
true );
 
 1068        bounds = mLayout->pageItemBounds( i, 
true );
 
 1077      bounds = QRectF( pageItem->pos().x(), pageItem->pos().y(), pageItem->rect().width(), pageItem->rect().height() );
 
 1081    int width = 
static_cast< int >( bounds.width() * settings.
dpi / inchesToLayoutUnits );
 
 1083    int height = 
static_cast< int >( bounds.height() * settings.
dpi / inchesToLayoutUnits );
 
 1084    if ( width == 0 || height == 0 )
 
 1093      const QRectF paperRect = QRectF( pageItem->pos().x(),
 
 1094                                       pageItem->pos().y(),
 
 1095                                       pageItem->rect().width(),
 
 1096                                       pageItem->rect().height() );
 
 1098      QDomNode svgDocRoot;
 
 1099      const QList<QGraphicsItem *> items = mLayout->items( paperRect,
 
 1100                                           Qt::IntersectsItemBoundingRect,
 
 1101                                           Qt::AscendingOrder );
 
 1105        return renderToLayeredSvg( settings, width, height, i, bounds, fileName, layerId, layerDetail.name, svg, svgDocRoot, settings.
exportMetadata );
 
 1107      ExportResult res = handleLayeredExport( items, exportFunc );
 
 1112        appendMetadataToSvg( svg );
 
 1114      QFile out( fileName );
 
 1115      bool openOk = out.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate );
 
 1118        mErrorFileName = fileName;
 
 1122      out.write( svg.toByteArray() );
 
 1128        QSvgGenerator generator;
 
 1131          generator.setTitle( mLayout->project()->metadata().title() );
 
 1132          generator.setDescription( mLayout->project()->metadata().abstract() );
 
 1134        generator.setOutputDevice( &svgBuffer );
 
 1135        generator.setSize( QSize( width, height ) );
 
 1136        generator.setViewBox( QRect( 0, 0, width, height ) );
 
 1137        generator.setResolution( 
static_cast< int >( std::round( settings.
dpi ) ) );
 
 1140        bool createOk = p.begin( &generator );
 
 1143          mErrorFileName = fileName;
 
 1156        svgBuffer.open( QIODevice::ReadOnly );
 
 1160        if ( ! svg.setContent( &svgBuffer, 
false, &errorMsg, &errorLine ) )
 
 1162          mErrorFileName = fileName;
 
 1167          appendMetadataToSvg( svg );
 
 1169        QFile out( fileName );
 
 1170        bool openOk = out.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate );
 
 1173          mErrorFileName = fileName;
 
 1177        out.write( svg.toByteArray() );
 
 1181  captureLabelingResults();
 
 
 1192  int total = iterator->
count();
 
 1193  double step = total > 0 ? 100.0 / total : 100.0;
 
 1195  while ( iterator->
next() )
 
 1200        feedback->setProperty( 
"progress", QObject::tr( 
"Exporting %1 of %2" ).arg( i + 1 ).arg( total ) );
 
 1202        feedback->setProperty( 
"progress", QObject::tr( 
"Exporting section %1" ).arg( i + 1 ).arg( total ) );
 
 1212    QString filePath = iterator->
filePath( baseFilePath, QStringLiteral( 
"svg" ) );
 
 1219        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 ) );
 
 
 1240  return mLabelingResults;
 
 
 1245  QMap<QString, QgsLabelingResults *> res;
 
 1246  std::swap( mLabelingResults, res );
 
 
 1250void QgsLayoutExporter::preparePrintAsPdf( 
QgsLayout *layout, QPagedPaintDevice *device, 
const QString &filePath )
 
 1252  QFileInfo fi( filePath );
 
 1254  if ( !dir.exists( fi.absolutePath() ) )
 
 1256    dir.mkpath( fi.absolutePath() );
 
 1259  updatePrinterPageSize( 
layout, device, firstPageToBeExported( 
layout ) );
 
 1265#if defined(HAS_KDE_QT5_PDF_TRANSFORM_FIX) || QT_VERSION >= QT_VERSION_CHECK(6, 3, 0) 
 1272void QgsLayoutExporter::preparePrint( 
QgsLayout *layout, QPagedPaintDevice *device, 
bool setFirstPageSize )
 
 1274  if ( QPdfWriter *pdf = 
dynamic_cast<QPdfWriter *
>( device ) )
 
 1278#if defined( HAVE_QTPRINTER ) 
 1279  else if ( QPrinter *printer = 
dynamic_cast<QPrinter *
>( device ) )
 
 1281    printer->setFullPage( 
true );
 
 1282    printer->setColorMode( QPrinter::Color );
 
 1288  if ( setFirstPageSize )
 
 1290    updatePrinterPageSize( 
layout, device, firstPageToBeExported( 
layout ) );
 
 1296  if ( mLayout->pageCollection()->pageCount() == 0 )
 
 1299  preparePrint( mLayout, device, 
true );
 
 1301  if ( !p.begin( device ) )
 
 1307  printPrivate( device, p );
 
 1312QgsLayoutExporter::ExportResult QgsLayoutExporter::printPrivate( QPagedPaintDevice *device, QPainter &painter, 
bool startNewPage, 
double dpi, 
bool rasterize )
 
 1316  int toPage = mLayout->pageCollection()->pageCount() - 1;
 
 1318#if defined( HAVE_QTPRINTER ) 
 1319  if ( QPrinter *printer = 
dynamic_cast<QPrinter *
>( device ) )
 
 1321    if ( printer->fromPage() >= 1 )
 
 1322      fromPage = printer->fromPage() - 1;
 
 1323    if ( printer->toPage() >= 1 )
 
 1324      toPage = printer->toPage() - 1;
 
 1328  bool pageExported = 
false;
 
 1331    for ( 
int i = fromPage; i <= toPage; ++i )
 
 1333      if ( !mLayout->pageCollection()->shouldExportPage( i ) )
 
 1338      updatePrinterPageSize( mLayout, device, i );
 
 1339      if ( ( pageExported && i > fromPage ) || startNewPage )
 
 1345      if ( !image.isNull() )
 
 1347        QRectF targetArea( 0, 0, image.width(), image.height() );
 
 1348        painter.drawImage( targetArea, image, targetArea );
 
 1354      pageExported = 
true;
 
 1359    for ( 
int i = fromPage; i <= toPage; ++i )
 
 1361      if ( !mLayout->pageCollection()->shouldExportPage( i ) )
 
 1366      updatePrinterPageSize( mLayout, device, i );
 
 1368      if ( ( pageExported && i > fromPage ) || startNewPage )
 
 1373      pageExported = 
true;
 
 1379void QgsLayoutExporter::updatePrinterPageSize( 
QgsLayout *layout, QPagedPaintDevice *device, 
int page )
 
 1384  QPageLayout pageLayout( QPageSize( pageSizeMM.
toQSizeF(), QPageSize::Millimeter ),
 
 1385                          QPageLayout::Portrait,
 
 1386                          QMarginsF( 0, 0, 0, 0 ) );
 
 1387  pageLayout.setMode( QPageLayout::FullPageMode );
 
 1388  device->setPageLayout( pageLayout );
 
 1389  device->setPageMargins( QMarginsF( 0, 0, 0, 0 ) );
 
 1391#if defined( HAVE_QTPRINTER ) 
 1392  if ( QPrinter *printer = 
dynamic_cast<QPrinter *
>( device ) )
 
 1394    printer->setFullPage( 
true );
 
 1399QgsLayoutExporter::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 
 1403    QSvgGenerator generator;
 
 1404    if ( includeMetadata )
 
 1407        generator.setTitle( l->name() );
 
 1408      else if ( mLayout->project() )
 
 1409        generator.setTitle( mLayout->project()->title() );
 
 1412    generator.setOutputDevice( &svgBuffer );
 
 1413    generator.setSize( QSize( 
static_cast< int >( std::round( width ) ),
 
 1414                              static_cast< int >( std::round( height ) ) ) );
 
 1415    generator.setViewBox( QRect( 0, 0,
 
 1416                                 static_cast< int >( std::round( width ) ),
 
 1417                                 static_cast< int >( std::round( height ) ) ) );
 
 1418    generator.setResolution( 
static_cast< int >( std::round( settings.dpi ) ) ); 
 
 1420    QPainter svgPainter( &generator );
 
 1421    if ( settings.cropToContents )
 
 1432    svgBuffer.open( QIODevice::ReadOnly );
 
 1436    if ( ! doc.setContent( &svgBuffer, 
false, &errorMsg, &errorLine ) )
 
 1438      mErrorFileName = filename;
 
 1441    if ( 1 == svgLayerId )
 
 1443      svg = QDomDocument( doc.doctype() );
 
 1444      svg.appendChild( svg.importNode( doc.firstChild(), 
false ) );
 
 1445      svgDocRoot = svg.importNode( doc.elementsByTagName( QStringLiteral( 
"svg" ) ).at( 0 ), 
false );
 
 1446      svgDocRoot.toElement().setAttribute( QStringLiteral( 
"xmlns:inkscape" ), QStringLiteral( 
"http://www.inkscape.org/namespaces/inkscape" ) );
 
 1447      svg.appendChild( svgDocRoot );
 
 1449    QDomNode mainGroup = svg.importNode( doc.elementsByTagName( QStringLiteral( 
"g" ) ).at( 0 ), 
true );
 
 1450    mainGroup.toElement().setAttribute( QStringLiteral( 
"id" ), layerName );
 
 1451    mainGroup.toElement().setAttribute( QStringLiteral( 
"inkscape:label" ), layerName );
 
 1452    mainGroup.toElement().setAttribute( QStringLiteral( 
"inkscape:groupmode" ), QStringLiteral( 
"layer" ) );
 
 1453    QDomNode defs = svg.importNode( doc.elementsByTagName( QStringLiteral( 
"defs" ) ).at( 0 ), 
true );
 
 1454    svgDocRoot.appendChild( defs );
 
 1455    svgDocRoot.appendChild( mainGroup );
 
 1460void QgsLayoutExporter::appendMetadataToSvg( QDomDocument &svg )
 const 
 1463  QDomElement metadataElement = svg.createElement( QStringLiteral( 
"metadata" ) );
 
 1464  QDomElement rdfElement = svg.createElement( QStringLiteral( 
"rdf:RDF" ) );
 
 1465  rdfElement.setAttribute( QStringLiteral( 
"xmlns:rdf" ), QStringLiteral( 
"http://www.w3.org/1999/02/22-rdf-syntax-ns#" ) );
 
 1466  rdfElement.setAttribute( QStringLiteral( 
"xmlns:rdfs" ), QStringLiteral( 
"http://www.w3.org/2000/01/rdf-schema#" ) );
 
 1467  rdfElement.setAttribute( QStringLiteral( 
"xmlns:dc" ), QStringLiteral( 
"http://purl.org/dc/elements/1.1/" ) );
 
 1468  QDomElement descriptionElement = svg.createElement( QStringLiteral( 
"rdf:Description" ) );
 
 1469  QDomElement workElement = svg.createElement( QStringLiteral( 
"cc:Work" ) );
 
 1470  workElement.setAttribute( QStringLiteral( 
"rdf:about" ), QString() );
 
 1472  auto addTextNode = [&workElement, &descriptionElement, &svg]( 
const QString & tag, 
const QString & value )
 
 1475    QDomElement element = svg.createElement( tag );
 
 1476    QDomText t = svg.createTextNode( value );
 
 1477    element.appendChild( t );
 
 1478    workElement.appendChild( element );
 
 1481    descriptionElement.setAttribute( tag, value );
 
 1484  addTextNode( QStringLiteral( 
"dc:format" ), QStringLiteral( 
"image/svg+xml" ) );
 
 1485  addTextNode( QStringLiteral( 
"dc:title" ), metadata.
title() );
 
 1486  addTextNode( QStringLiteral( 
"dc:date" ), metadata.
creationDateTime().toString( Qt::ISODate ) );
 
 1487  addTextNode( QStringLiteral( 
"dc:identifier" ), metadata.
identifier() );
 
 1488  addTextNode( QStringLiteral( 
"dc:description" ), metadata.
abstract() );
 
 1490  auto addAgentNode = [&workElement, &descriptionElement, &svg]( 
const QString & tag, 
const QString & value )
 
 1493    QDomElement inkscapeElement = svg.createElement( tag );
 
 1494    QDomElement agentElement = svg.createElement( QStringLiteral( 
"cc:Agent" ) );
 
 1495    QDomElement titleElement = svg.createElement( QStringLiteral( 
"dc:title" ) );
 
 1496    QDomText t = svg.createTextNode( value );
 
 1497    titleElement.appendChild( t );
 
 1498    agentElement.appendChild( titleElement );
 
 1499    inkscapeElement.appendChild( agentElement );
 
 1500    workElement.appendChild( inkscapeElement );
 
 1503    QDomElement bagElement = svg.createElement( QStringLiteral( 
"rdf:Bag" ) );
 
 1504    QDomElement liElement = svg.createElement( QStringLiteral( 
"rdf:li" ) );
 
 1505    t = svg.createTextNode( value );
 
 1506    liElement.appendChild( t );
 
 1507    bagElement.appendChild( liElement );
 
 1509    QDomElement element = svg.createElement( tag );
 
 1510    element.appendChild( bagElement );
 
 1511    descriptionElement.appendChild( element );
 
 1514  addAgentNode( QStringLiteral( 
"dc:creator" ), metadata.
author() );
 
 1515  addAgentNode( QStringLiteral( 
"dc:publisher" ), QStringLiteral( 
"QGIS %1" ).arg( 
Qgis::version() ) );
 
 1519    QDomElement element = svg.createElement( QStringLiteral( 
"dc:subject" ) );
 
 1520    QDomElement bagElement = svg.createElement( QStringLiteral( 
"rdf:Bag" ) );
 
 1522    for ( 
auto it = keywords.constBegin(); it != keywords.constEnd(); ++it )
 
 1524      const QStringList words = it.value();
 
 1525      for ( 
const QString &keyword : words )
 
 1527        QDomElement liElement = svg.createElement( QStringLiteral( 
"rdf:li" ) );
 
 1528        QDomText t = svg.createTextNode( keyword );
 
 1529        liElement.appendChild( t );
 
 1530        bagElement.appendChild( liElement );
 
 1533    element.appendChild( bagElement );
 
 1534    workElement.appendChild( element );
 
 1535    descriptionElement.appendChild( element );
 
 1538  rdfElement.appendChild( descriptionElement );
 
 1539  rdfElement.appendChild( workElement );
 
 1540  metadataElement.appendChild( rdfElement );
 
 1541  svg.documentElement().appendChild( metadataElement );
 
 1542  svg.documentElement().setAttribute( QStringLiteral( 
"xmlns:cc" ), QStringLiteral( 
"http://creativecommons.org/ns#" ) );
 
 1545std::unique_ptr<double[]> QgsLayoutExporter::computeGeoTransform( 
const QgsLayoutItemMap *map, 
const QRectF ®ion, 
double dpi )
 const 
 1548    map = mLayout->referenceMap();
 
 1554    dpi = mLayout->renderContext().dpi();
 
 1557  QRectF exportRegion = region;
 
 1558  if ( !exportRegion.isValid() )
 
 1560    int pageNumber = map->
page();
 
 1563    double pageY = page->pos().y();
 
 1564    QSizeF pageSize = page->rect().size();
 
 1565    exportRegion = QRectF( 0, pageY, pageSize.width(), pageSize.height() );
 
 1569  QRectF mapItemSceneRect = map->mapRectToScene( map->rect() );
 
 1572  double outputHeightMM = exportRegion.height();
 
 1573  double outputWidthMM = exportRegion.width();
 
 1577  double mapXCenter = mapExtent.
center().
x();
 
 1578  double mapYCenter = mapExtent.
center().
y();
 
 1580  double sinAlpha = std::sin( alpha );
 
 1581  double cosAlpha = std::cos( alpha );
 
 1584  QPointF mapItemPos = map->pos();
 
 1586  mapItemPos.rx() -= exportRegion.left();
 
 1587  mapItemPos.ry() -= exportRegion.top();
 
 1590  double xRatio = mapExtent.
width() / mapItemSceneRect.width();
 
 1591  double yRatio = mapExtent.
height() / mapItemSceneRect.height();
 
 1592  double xmin = mapExtent.
xMinimum() - mapItemPos.x() * xRatio;
 
 1593  double ymax = mapExtent.
yMaximum() + mapItemPos.y() * yRatio;
 
 1594  QgsRectangle paperExtent( xmin, ymax - outputHeightMM * yRatio, xmin + outputWidthMM * xRatio, ymax );
 
 1597  double X0 = paperExtent.xMinimum();
 
 1598  double Y0 = paperExtent.yMaximum();
 
 1603    double X1 = X0 - mapXCenter;
 
 1604    double Y1 = Y0 - mapYCenter;
 
 1605    double X2 = X1 * cosAlpha + Y1 * sinAlpha;
 
 1606    double Y2 = -X1 * sinAlpha + Y1 * cosAlpha;
 
 1607    X0 = X2 + mapXCenter;
 
 1608    Y0 = Y2 + mapYCenter;
 
 1612  int pageWidthPixels = 
static_cast< int >( dpi * outputWidthMM / 25.4 );
 
 1613  int pageHeightPixels = 
static_cast< int >( dpi * outputHeightMM / 25.4 );
 
 1614  double pixelWidthScale = paperExtent.width() / pageWidthPixels;
 
 1615  double pixelHeightScale = paperExtent.height() / pageHeightPixels;
 
 1618  std::unique_ptr<double[]> t( 
new double[6] );
 
 1620  t[1] = cosAlpha * pixelWidthScale;
 
 1621  t[2] = -sinAlpha * pixelWidthScale;
 
 1623  t[4] = -sinAlpha * pixelHeightScale;
 
 1624  t[5] = -cosAlpha * pixelHeightScale;
 
 1629void QgsLayoutExporter::writeWorldFile( 
const QString &worldFileName, 
double a, 
double b, 
double c, 
double d, 
double e, 
double f )
 const 
 1631  QFile worldFile( worldFileName );
 
 1632  if ( !worldFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
 
 1636  QTextStream fout( &worldFile );
 
 1640  fout << QString::number( a, 
'f', 12 ) << 
"\r\n";
 
 1641  fout << QString::number( d, 
'f', 12 ) << 
"\r\n";
 
 1642  fout << QString::number( b, 
'f', 12 ) << 
"\r\n";
 
 1643  fout << QString::number( e, 
'f', 12 ) << 
"\r\n";
 
 1644  fout << QString::number( 
c, 
'f', 12 ) << 
"\r\n";
 
 1645  fout << QString::number( f, 
'f', 12 ) << 
"\r\n";
 
 1650  return georeferenceOutputPrivate( file, map, exportRegion, dpi, 
false );
 
 
 1653bool QgsLayoutExporter::georeferenceOutputPrivate( 
const QString &file, 
QgsLayoutItemMap *map, 
const QRectF &exportRegion, 
double dpi, 
bool includeGeoreference, 
bool includeMetadata )
 const 
 1658  if ( !map && includeGeoreference )
 
 1659    map = mLayout->referenceMap();
 
 1661  std::unique_ptr<double[]> t;
 
 1663  if ( map && includeGeoreference )
 
 1666      dpi = mLayout->renderContext().dpi();
 
 1668    t = computeGeoTransform( map, exportRegion, dpi );
 
 1673  CPLSetConfigOption( 
"GDAL_PDF_DPI", QString::number( dpi ).toUtf8().constData() );
 
 1678      GDALSetGeoTransform( outputDS.get(), t.get() );
 
 1680    if ( includeMetadata )
 
 1682      QString creationDateString;
 
 1683      const QDateTime creationDateTime = mLayout->project()->metadata().creationDateTime();
 
 1684      if ( creationDateTime.isValid() )
 
 1686        creationDateString = QStringLiteral( 
"D:%1" ).arg( mLayout->project()->metadata().creationDateTime().toString( QStringLiteral( 
"yyyyMMddHHmmss" ) ) );
 
 1687        if ( creationDateTime.timeZone().isValid() )
 
 1689          int offsetFromUtc = creationDateTime.timeZone().offsetFromUtc( creationDateTime );
 
 1690          creationDateString += ( offsetFromUtc >= 0 ) ? 
'+' : 
'-';
 
 1691          offsetFromUtc = std::abs( offsetFromUtc );
 
 1692          int offsetHours = offsetFromUtc / 3600;
 
 1693          int offsetMins = ( offsetFromUtc % 3600 ) / 60;
 
 1694          creationDateString += QStringLiteral( 
"%1'%2'" ).arg( offsetHours ).arg( offsetMins );
 
 1697      GDALSetMetadataItem( outputDS.get(), 
"CREATION_DATE", creationDateString.toUtf8().constData(), 
nullptr );
 
 1699      GDALSetMetadataItem( outputDS.get(), 
"AUTHOR", mLayout->project()->metadata().author().toUtf8().constData(), 
nullptr );
 
 1700      const QString creator = QStringLiteral( 
"QGIS %1" ).arg( 
Qgis::version() );
 
 1701      GDALSetMetadataItem( outputDS.get(), 
"CREATOR", creator.toUtf8().constData(), 
nullptr );
 
 1702      GDALSetMetadataItem( outputDS.get(), 
"PRODUCER", creator.toUtf8().constData(), 
nullptr );
 
 1703      GDALSetMetadataItem( outputDS.get(), 
"SUBJECT", mLayout->project()->metadata().abstract().toUtf8().constData(), 
nullptr );
 
 1704      GDALSetMetadataItem( outputDS.get(), 
"TITLE", mLayout->project()->metadata().title().toUtf8().constData(), 
nullptr );
 
 1707      QStringList allKeywords;
 
 1708      for ( 
auto it = keywords.constBegin(); it != keywords.constEnd(); ++it )
 
 1710        allKeywords.append( QStringLiteral( 
"%1: %2" ).arg( it.key(), it.value().join( 
',' ) ) );
 
 1712      const QString keywordString = allKeywords.join( 
';' );
 
 1713      GDALSetMetadataItem( outputDS.get(), 
"KEYWORDS", keywordString.toUtf8().constData(), 
nullptr );
 
 1719  CPLSetConfigOption( 
"GDAL_PDF_DPI", 
nullptr );
 
 1726  if ( items.count() == 1 )
 
 1730      QString name = layoutItem->displayName();
 
 1732      if ( name.startsWith( 
'<' ) && name.endsWith( 
'>' ) )
 
 1733        name = name.mid( 1, name.length() - 2 );
 
 1737  else if ( items.count() > 1 )
 
 1739    QStringList currentLayerItemTypes;
 
 1740    for ( QGraphicsItem *item : items )
 
 1746        if ( !currentLayerItemTypes.contains( itemType ) && !currentLayerItemTypes.contains( itemTypePlural ) )
 
 1747          currentLayerItemTypes << itemType;
 
 1748        else if ( currentLayerItemTypes.contains( itemType ) )
 
 1750          currentLayerItemTypes.replace( currentLayerItemTypes.indexOf( itemType ), itemTypePlural );
 
 1755        if ( !currentLayerItemTypes.contains( QObject::tr( 
"Other" ) ) )
 
 1756          currentLayerItemTypes.append( QObject::tr( 
"Other" ) );
 
 1759    return currentLayerItemTypes.join( QLatin1String( 
", " ) );
 
 1761  return QObject::tr( 
"Layer %1" ).arg( layerId );
 
 
 1767  LayoutItemHider itemHider( items );
 
 1772  unsigned int layerId = 1;
 
 1774  itemHider.hideAll();
 
 1775  const QList< QGraphicsItem * > itemsToIterate = itemHider.itemsToIterate();
 
 1776  QList< QGraphicsItem * > currentLayerItems;
 
 1777  for ( QGraphicsItem *item : itemsToIterate )
 
 1781    bool canPlaceInExistingLayer = 
false;
 
 1788          switch ( prevItemBehavior )
 
 1791              canPlaceInExistingLayer = 
true;
 
 1795              canPlaceInExistingLayer = prevType == -1 || prevType == layoutItem->
type();
 
 1800              canPlaceInExistingLayer = 
false;
 
 1808          switch ( prevItemBehavior )
 
 1812              canPlaceInExistingLayer = prevType == -1 || prevType == layoutItem->
type();
 
 1817              canPlaceInExistingLayer = 
false;
 
 1825          canPlaceInExistingLayer = 
false;
 
 1830          canPlaceInExistingLayer = 
false;
 
 1834      prevType = layoutItem->
type();
 
 1841    if ( canPlaceInExistingLayer )
 
 1843      currentLayerItems << item;
 
 1848      if ( !currentLayerItems.isEmpty() )
 
 1852        ExportResult result = exportFunc( layerId, layerDetails );
 
 1856        currentLayerItems.clear();
 
 1859      itemHider.hideAll();
 
 1864        int layoutItemLayerIdx = 0;
 
 1866        mLayout->renderContext().setCurrentExportLayer( layoutItemLayerIdx );
 
 1872          mLayout->renderContext().setCurrentExportLayer( layoutItemLayerIdx );
 
 1876          ExportResult result = exportFunc( layerId, layerDetails );
 
 1881          layoutItemLayerIdx++;
 
 1883        layerDetails.mapLayerId.clear();
 
 1885        mLayout->renderContext().setCurrentExportLayer( -1 );
 
 1888        currentLayerItems.clear();
 
 1892        currentLayerItems << item;
 
 1896  if ( !currentLayerItems.isEmpty() )
 
 1899    ExportResult result = exportFunc( layerId, layerDetails );
 
 1914  return simplifyMethod;
 
 1937  int pageNumber = map->
page();
 
 1939  double pageY = page->pos().y();
 
 1940  QSizeF pageSize = page->rect().size();
 
 1941  QRectF pageRect( 0, pageY, pageSize.width(), pageSize.height() );
 
 
 1957  double destinationHeight = exportRegion.height();
 
 1958  double destinationWidth = exportRegion.width();
 
 1960  QRectF mapItemSceneRect = map->mapRectToScene( map->rect() );
 
 1965  double xRatio = mapExtent.
width() / mapItemSceneRect.width();
 
 1966  double yRatio = mapExtent.
height() / mapItemSceneRect.height();
 
 1968  double xCenter = mapExtent.
center().
x();
 
 1969  double yCenter = mapExtent.
center().
y();
 
 1972  QPointF mapItemPos = map->pos();
 
 1974  mapItemPos.rx() -= exportRegion.left();
 
 1975  mapItemPos.ry() -= exportRegion.top();
 
 1977  double xmin = mapExtent.
xMinimum() - mapItemPos.x() * xRatio;
 
 1978  double ymax = mapExtent.
yMaximum() + mapItemPos.y() * yRatio;
 
 1979  QgsRectangle paperExtent( xmin, ymax - destinationHeight * yRatio, xmin + destinationWidth * xRatio, ymax );
 
 1981  double X0 = paperExtent.
xMinimum();
 
 1982  double Y0 = paperExtent.
yMinimum();
 
 1985    dpi = mLayout->renderContext().dpi();
 
 1987  int widthPx = 
static_cast< int >( dpi * destinationWidth / 25.4 );
 
 1988  int heightPx = 
static_cast< int >( dpi * destinationHeight / 25.4 );
 
 1990  double Ww = paperExtent.
width() / widthPx;
 
 1991  double Hh = paperExtent.
height() / heightPx;
 
 2000  s[5] = Y0 + paperExtent.
height();
 
 2004  r[0] = std::cos( alpha );
 
 2005  r[1] = -std::sin( alpha );
 
 2006  r[2] = xCenter * ( 1 - std::cos( alpha ) ) + yCenter * std::sin( alpha );
 
 2007  r[3] = std::sin( alpha );
 
 2008  r[4] = std::cos( alpha );
 
 2009  r[5] = - xCenter * std::sin( alpha ) + yCenter * ( 1 - std::cos( alpha ) );
 
 2012  a = r[0] * s[0] + r[1] * s[3];
 
 2013  b = r[0] * s[1] + r[1] * s[4];
 
 2014  c = r[0] * s[2] + r[1] * s[5] + r[2];
 
 2015  d = r[3] * s[0] + r[4] * s[3];
 
 2016  e = r[3] * s[1] + r[4] * s[4];
 
 2017  f = r[3] * s[2] + r[4] * s[5] + r[5];
 
 
 2025  QList< QgsLayoutItem *> items;
 
 2031    if ( currentItem->isVisible() && currentItem->requiresRasterization() )
 
 
 2042  QList< QgsLayoutItem *> items;
 
 2048    if ( currentItem->isVisible() && currentItem->containsAdvancedEffects() )
 
 
 2061    if ( mLayout->pageCollection()->pageCount() == 1 )
 
 2064      bounds = mLayout->layoutBounds( 
true );
 
 2069      bounds = mLayout->pageItemBounds( page, 
true );
 
 2071    if ( bounds.width() <= 0 || bounds.height() <= 0 )
 
 2079    bounds = bounds.adjusted( -settings.
cropMargins.
left() * pixelToLayoutUnits,
 
 2091int QgsLayoutExporter::firstPageToBeExported( 
QgsLayout *layout )
 
 2094  for ( 
int i = 0; i < pageCount; ++i )
 
 2108  if ( details.
page == 0 )
 
 
 2118void QgsLayoutExporter::captureLabelingResults()
 
 2120  qDeleteAll( mLabelingResults );
 
 2121  mLabelingResults.clear();
 
 2123  QList< QgsLayoutItemMap * > maps;
 
 2124  mLayout->layoutItems( maps );
 
 2128    mLabelingResults[ map->
uuid() ] = map->mExportLabelingResults.release();
 
 2132bool QgsLayoutExporter::saveImage( 
const QImage &image, 
const QString &imageFilename, 
const QString &imageFormat, 
QgsProject *projectForMetadata )
 
 2134  QImageWriter w( imageFilename, imageFormat.toLocal8Bit().constData() );
 
 2135  if ( imageFormat.compare( QLatin1String( 
"tiff" ), Qt::CaseInsensitive ) == 0 || imageFormat.compare( QLatin1String( 
"tif" ), Qt::CaseInsensitive ) == 0 )
 
 2137    w.setCompression( 1 ); 
 
 2139  if ( projectForMetadata )
 
 2141    w.setText( QStringLiteral( 
"Author" ), projectForMetadata->
metadata().
author() );
 
 2142    const QString creator = QStringLiteral( 
"QGIS %1" ).arg( 
Qgis::version() );
 
 2143    w.setText( QStringLiteral( 
"Creator" ), creator );
 
 2144    w.setText( QStringLiteral( 
"Producer" ), creator );
 
 2145    w.setText( QStringLiteral( 
"Subject" ), projectForMetadata->
metadata().
abstract() );
 
 2146    w.setText( QStringLiteral( 
"Created" ), projectForMetadata->
metadata().
creationDateTime().toString( Qt::ISODate ) );
 
 2147    w.setText( QStringLiteral( 
"Title" ), projectForMetadata->
metadata().
title() );
 
 2150    QStringList allKeywords;
 
 2151    for ( 
auto it = keywords.constBegin(); it != keywords.constEnd(); ++it )
 
 2153      allKeywords.append( QStringLiteral( 
"%1: %2" ).arg( it.key(), it.value().join( 
',' ) ) );
 
 2155    const QString keywordString = allKeywords.join( 
';' );
 
 2156    w.setText( QStringLiteral( 
"Keywords" ), keywordString );
 
 2158  return w.write( image );
 
static QString version()
Version string.
 
@ Millimeters
Millimeters.
 
@ GeometrySimplification
The geometries can be simplified using the current map2pixel context state.
 
@ SnappedToGridGlobal
Snap to a global grid based on the tolerance. Good for consistent results for incoming vertices,...
 
@ Warning
Warning message.
 
TextRenderFormat
Options for rendering text.
 
@ AlwaysOutlines
Always render text using path objects (AKA outlines/curves). This setting guarantees the best quality...
 
@ PreferredGdal
Preferred format for conversion of CRS to WKT for use with the GDAL library.
 
An abstract base class for QgsLayout based classes which can be exported by QgsLayoutExporter.
 
virtual bool endRender()=0
Ends the render, performing any required cleanup tasks.
 
virtual QgsLayout * layout()=0
Returns the layout associated with the iterator.
 
virtual bool next()=0
Iterates to next feature, returning false if no more features exist to iterate over.
 
virtual bool beginRender()=0
Called when rendering begins, before iteration commences.
 
virtual QString filePath(const QString &baseFilePath, const QString &extension)=0
Returns the file path for the current feature, based on a specified base file path and extension.
 
virtual int count() const =0
Returns the number of features to iterate over.
 
static QgsLayoutItemRegistry * layoutItemRegistry()
Returns the application's layout item registry, used for layout item types.
 
QString toWkt(Qgis::CrsWktVariant variant=Qgis::CrsWktVariant::Wkt1Gdal, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
 
Base class for feedback objects to be used for cancellation of something running in a worker thread.
 
bool isCanceled() const
Tells whether the operation has been canceled already.
 
void setProgress(double progress)
Sets the current progress for the feedback object.
 
Handles rendering and exports of layouts to various formats.
 
ExportResult exportToSvg(const QString &filePath, const QgsLayoutExporter::SvgExportSettings &settings)
Exports the layout as an SVG to the filePath, using the specified export settings.
 
ExportResult exportToImage(const QString &filePath, const QgsLayoutExporter::ImageExportSettings &settings)
Exports the layout to the filePath, using the specified export settings.
 
QString errorMessage() const
Returns a string describing the last error encountered during an export.
 
ExportResult exportToPdf(const QString &filePath, const QgsLayoutExporter::PdfExportSettings &settings)
Exports the layout as a PDF to the filePath, using the specified export settings.
 
virtual ~QgsLayoutExporter()
 
QImage renderRegionToImage(const QRectF ®ion, QSize imageSize=QSize(), double dpi=-1) const
Renders a region of the layout to an image.
 
QMap< QString, QgsLabelingResults * > takeLabelingResults()
Takes the labeling results for all map items included in the export.
 
static bool requiresRasterization(const QgsLayout *layout)
Returns true if the specified layout contains visible items which have settings that require rasteriz...
 
QgsLayout * layout() const
Returns the layout linked to this exporter.
 
bool georeferenceOutput(const QString &file, QgsLayoutItemMap *referenceMap=nullptr, const QRectF &exportRegion=QRectF(), double dpi=-1) const
Georeferences a file (image of PDF) exported from the layout.
 
static const QgsSettingsEntryBool * settingOpenAfterExportingPdf
Settings entry - Whether to automatically open pdfs after exporting them.
 
virtual QString generateFileName(const PageExportDetails &details) const
Generates the file name for a page during export.
 
ExportResult
Result codes for exporting layouts.
 
@ Canceled
Export was canceled.
 
@ MemoryError
Unable to allocate memory required to export.
 
@ PrintError
Could not start printing to destination device.
 
@ IteratorError
Error iterating over layout.
 
@ FileError
Could not write to destination file, likely due to a lock held by another application.
 
@ Success
Export was successful.
 
@ SvgLayerError
Could not create layered SVG file.
 
QImage renderPageToImage(int page, QSize imageSize=QSize(), double dpi=-1) const
Renders a full page to an image.
 
QgsLayoutExporter(QgsLayout *layout)
Constructor for QgsLayoutExporter, for the specified layout.
 
static ExportResult exportToPdfs(QgsAbstractLayoutIterator *iterator, const QString &baseFilePath, const QgsLayoutExporter::PdfExportSettings &settings, QString &error, QgsFeedback *feedback=nullptr)
Exports a layout iterator to multiple PDF files, with the specified export settings.
 
void computeWorldFileParameters(double &a, double &b, double &c, double &d, double &e, double &f, double dpi=-1) const
Compute world file parameters.
 
void renderPage(QPainter *painter, int page) const
Renders a full page to a destination painter.
 
static const QgsSettingsEntryBool * settingOpenAfterExportingImage
Settings entry - Whether to automatically open images after exporting them.
 
static const QgsSettingsEntryBool * settingOpenAfterExportingSvg
Settings entry - Whether to automatically open svgs after exporting them.
 
QMap< QString, QgsLabelingResults * > labelingResults()
Returns the labeling results for all map items included in the export.
 
static bool containsAdvancedEffects(const QgsLayout *layout)
Returns true if the specified layout contains visible items which have settings such as opacity which...
 
void renderRegion(QPainter *painter, const QRectF ®ion) const
Renders a region from the layout to a painter.
 
Contains the configuration for a single snap guide used by a layout.
 
Layout graphical items for displaying a map.
 
double mapRotation(QgsLayoutObject::PropertyValueType valueType=QgsLayoutObject::EvaluatedValue) const
Returns the rotation used for drawing the map within the layout item, in degrees clockwise.
 
QgsRectangle extent() const
Returns the current map extent.
 
QgsCoordinateReferenceSystem crs() const
Returns coordinate reference system used for rendering the map.
 
Item representing the paper in a layout.
 
QgsLayoutItemAbstractMetadata * itemMetadata(int type) const
Returns the metadata for the specified item type.
 
Base class for graphical items within a QgsLayout.
 
QgsLayoutSize sizeWithUnits() const
Returns the item's current size, including units.
 
virtual QgsLayoutItem::ExportLayerDetail exportLayerDetails() const
Returns the details for the specified current export layer.
 
virtual bool nextExportPart()
Moves to the next export part for a multi-layered export item, during a multi-layered export.
 
virtual void startLayeredExport()
Starts a multi-layer export operation.
 
int page() const
Returns the page the item is currently on, with the first page returning 0.
 
int type() const override
Returns a unique graphics item type identifier.
 
virtual void stopLayeredExport()
Stops a multi-layer export operation.
 
virtual QString uuid() const
Returns the item identification string.
 
ExportLayerBehavior
Behavior of item when exporting to layered outputs.
 
@ ItemContainsSubLayers
Item contains multiple sublayers which must be individually exported.
 
@ MustPlaceInOwnLayer
Item must be placed in its own individual layer.
 
@ CanGroupWithItemsOfSameType
Item can only be placed on layers with other items of the same type, but multiple items of this type ...
 
@ CanGroupWithAnyOtherItem
Item can be placed on a layer with any other item (default behavior)
 
virtual ExportLayerBehavior exportLayerBehavior() const
Returns the behavior of this item during exporting to layered exports (e.g.
 
QgsLayoutMeasurement convert(QgsLayoutMeasurement measurement, Qgis::LayoutUnit targetUnits) const
Converts a measurement from one unit to another.
 
This class provides a method of storing measurements for use in QGIS layouts using a variety of diffe...
 
int pageCount() const
Returns the number of pages in the collection.
 
bool shouldExportPage(int page) const
Returns whether the specified page number should be included in exports of the layouts.
 
QgsLayoutItemPage * page(int pageNumber)
Returns a specific page (by pageNumber) from the collection.
 
This class provides a method of storing points, consisting of an x and y coordinate,...
 
double x() const
Returns x coordinate of point.
 
double y() const
Returns y coordinate of point.
 
void setDpi(double dpi)
Sets the dpi for outputting the layout.
 
void setSimplifyMethod(const QgsVectorSimplifyMethod &method)
Sets the simplification setting to use when rendering vector layers.
 
void setTextRenderFormat(Qgis::TextRenderFormat format)
Sets the text render format, which dictates how text is rendered (e.g.
 
QgsLayoutRenderContext::Flags flags() const
Returns the current combination of flags used for rendering the layout.
 
void setFlag(QgsLayoutRenderContext::Flag flag, bool on=true)
Enables or disables a particular rendering flag for the layout.
 
double dpi() const
Returns the dpi for outputting the layout.
 
@ FlagRenderLabelsByMapLayer
When rendering map items to multi-layered exports, render labels belonging to different layers into s...
 
@ FlagUseAdvancedEffects
Enable advanced effects such as blend modes.
 
@ FlagLosslessImageRendering
Render images losslessly whenever possible, instead of the default lossy jpeg rendering used for some...
 
@ FlagAntialiasing
Use antialiasing when drawing items.
 
@ FlagSynchronousLegendGraphics
Query legend graphics synchronously.
 
@ FlagForceVectorOutput
Force output in vector format where possible, even if items require rasterization to keep their corre...
 
void setPredefinedScales(const QVector< qreal > &scales)
Sets the list of predefined scales to use with the layout.
 
void setMaskSettings(const QgsMaskRenderSettings &settings)
Sets the mask render settings, which control how masks are drawn and behave during map renders.
 
void setFlags(QgsLayoutRenderContext::Flags flags)
Sets the combination of flags that will be used for rendering the layout.
 
const QgsLayoutMeasurementConverter & measurementConverter() const
Returns the layout measurement converter to be used in the layout.
 
This class provides a method of storing sizes, consisting of a width and height, for use in QGIS layo...
 
QSizeF toQSizeF() const
Converts the layout size to a QSizeF.
 
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
 
QgsLayoutRenderContext & renderContext()
Returns a reference to the layout's render context, which stores information relating to the current ...
 
QgsLayoutPageCollection * pageCollection()
Returns a pointer to the layout's page collection, which stores and manages page items in the layout.
 
void layoutItems(QList< T * > &itemList) const
Returns a list of layout items of a specific type.
 
Line string geometry type, with support for z-dimension and m-values.
 
Base class for all map layer types.
 
double top() const
Returns the top margin.
 
double right() const
Returns the right margin.
 
double bottom() const
Returns the bottom margin.
 
double left() const
Returns the left margin.
 
Contains settings regarding how masks are calculated and handled during a map render.
 
void setSimplificationTolerance(double tolerance)
Sets a simplification tolerance (in painter units) to use for on-the-fly simplification of mask paths...
 
Interface for master layout type objects, such as print layouts and reports.
 
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
 
static void fixEngineFlags(QPaintEngine *engine)
 
A class to represent a 2D point.
 
void setExteriorRing(QgsCurve *ring) override
Sets the exterior ring of the polygon.
 
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
 
QgsProjectMetadata metadata
 
A rectangle specified with double values.
 
double xMinimum() const
Returns the x minimum value (left side of rectangle).
 
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
 
double width() const
Returns the width of the rectangle.
 
double yMaximum() const
Returns the y maximum value (top side of rectangle).
 
QgsPointXY center() const
Returns the center point of the rectangle.
 
double height() const
Returns the height of the rectangle.
 
A boolean settings entry.
 
static QgsSettingsTreeNode * sTreeLayout
 
This class contains information how to simplify geometries fetched from a vector layer.
 
void setThreshold(float threshold)
Sets the simplification threshold of the vector layer managed.
 
void setForceLocalOptimization(bool localOptimization)
Sets where the simplification executes, after fetch the geometries from provider, or when supported,...
 
void setSimplifyHints(Qgis::VectorRenderingSimplificationFlags simplifyHints)
Sets the simplification hints of the vector layer managed.
 
void setSimplifyAlgorithm(Qgis::VectorSimplificationAlgorithm simplifyAlgorithm)
Sets the local simplification algorithm of the vector layer managed.
 
std::unique_ptr< std::remove_pointer< GDALDatasetH >::type, GDALDatasetCloser > dataset_unique_ptr
Scoped GDAL dataset.
 
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
 
#define Q_NOWARN_DEPRECATED_POP
 
#define Q_NOWARN_DEPRECATED_PUSH
 
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
 
QString nameForLayerWithItems(const QList< QGraphicsItem * > &items, unsigned int layerId)
 
Contains details of a particular input component to be used during PDF composition.
 
QString sourcePdfPath
File path to the (already created) PDF to use as the source for this component layer.
 
QString mapLayerId
Associated map layer ID, or an empty string if this component layer is not associated with a map laye...
 
QPainter::CompositionMode compositionMode
Component composition mode.
 
QString group
Optional group name, for arranging layers in top-level groups.
 
QString name
User-friendly name for the generated PDF layer.
 
double opacity
Component opacity.
 
Contains details of a control point used during georeferencing GeoPDF outputs.
 
QgsAbstractMetadataBase::KeywordMap keywords
Metadata keyword map.
 
bool useIso32000ExtensionFormatGeoreferencing
true if ISO32000 extension format georeferencing should be used.
 
QMap< QString, QString > layerIdToPdfLayerTreeNameMap
Optional map of map layer ID to custom layer tree name to show in the created PDF file.
 
bool useOgcBestPracticeFormatGeoreferencing
true if OGC "best practice" format georeferencing should be used.
 
QDateTime creationDateTime
Metadata creation datetime.
 
QSizeF pageSizeMm
Page size, in millimeters.
 
QList< QgsAbstractGeoPdfExporter::GeoReferencedSection > georeferencedSections
List of georeferenced sections.
 
QStringList layerTreeGroupOrder
Specifies the ordering of layer tree groups in the generated GeoPDF file.
 
QString author
Metadata author tag.
 
QMap< QString, bool > initialLayerVisibility
Optional map of map layer ID to initial visibility state.
 
QString producer
Metadata producer tag.
 
QString creator
Metadata creator tag.
 
QMap< QString, QString > customLayerTreeGroups
Optional map of map layer ID to custom logical layer tree group in created PDF file.
 
bool includeFeatures
true if feature vector information (such as attributes) should be exported.
 
QStringList layerOrder
Optional list of layer IDs, in the order desired to appear in the generated GeoPDF file.
 
QString subject
Metadata subject tag.
 
QString title
Metadata title tag.
 
QgsCoordinateReferenceSystem crs
Coordinate reference system for georeferenced section.
 
QgsPolygon pageBoundsPolygon
Bounds of the georeferenced section on the page, in millimeters, as a free-form polygon.
 
QList< QgsAbstractGeoPdfExporter::ControlPoint > controlPoints
List of control points corresponding to this georeferenced section.
 
Contains settings relating to exporting layouts to raster images.
 
QgsMargins cropMargins
Crop to content margins, in pixels.
 
QList< int > pages
List of specific pages to export, or an empty list to export all pages.
 
bool generateWorldFile
Set to true to generate an external world file alongside exported images.
 
QSize imageSize
Manual size in pixels for output image.
 
bool exportMetadata
Indicates whether image export should include metadata generated from the layout's project's metadata...
 
QgsLayoutRenderContext::Flags flags
Layout context flags, which control how the export will be created.
 
bool cropToContents
Set to true if image should be cropped so only parts of the layout containing items are exported.
 
double dpi
Resolution to export layout at. If dpi <= 0 the default layout dpi will be used.
 
QVector< qreal > predefinedMapScales
A list of predefined scales to use with the layout.
 
Contains details of a page being exported by the class.
 
QString baseName
Base part of filename (i.e. file name without extension or '.')
 
QString extension
File suffix/extension (without the leading '.')
 
QString directory
Target folder.
 
int page
Page number, where 0 = first page.
 
Contains settings relating to exporting layouts to PDF.
 
bool forceVectorOutput
Set to true to force vector object exports, even when the resultant appearance will differ from the l...
 
bool rasterizeWholeImage
Set to true to force whole layout to be rasterized while exporting.
 
QStringList exportThemes
Optional list of map themes to export as GeoPDF layer groups.
 
bool exportMetadata
Indicates whether PDF export should include metadata generated from the layout's project's metadata.
 
bool appendGeoreference
Indicates whether PDF export should append georeference data.
 
QgsLayoutRenderContext::Flags flags
Layout context flags, which control how the export will be created.
 
bool writeGeoPdf
true if GeoPDF files should be created, instead of normal PDF files.
 
double dpi
Resolution to export layout at. If dpi <= 0 the default layout dpi will be used.
 
QVector< qreal > predefinedMapScales
A list of predefined scales to use with the layout.
 
bool exportLayersAsSeperateFiles
true if individual layers from the layout should be rendered to separate PDF files.
 
bool simplifyGeometries
Indicates whether vector geometries should be simplified to avoid redundant extraneous detail,...
 
Qgis::TextRenderFormat textRenderFormat
Text rendering format, which controls how text should be rendered in the export (e....
 
Contains settings relating to printing layouts.
 
QVector< qreal > predefinedMapScales
A list of predefined scales to use with the layout.
 
double dpi
Resolution to export layout at. If dpi <= 0 the default layout dpi will be used.
 
QgsLayoutRenderContext::Flags flags
Layout context flags, which control how the export will be created.
 
bool rasterizeWholeImage
Set to true to force whole layout to be rasterized while exporting.
 
Contains settings relating to exporting layouts to SVG.
 
bool forceVectorOutput
Set to true to force vector object exports, even when the resultant appearance will differ from the l...
 
Qgis::TextRenderFormat textRenderFormat
Text rendering format, which controls how text should be rendered in the export (e....
 
bool exportAsLayers
Set to true to export as a layered SVG file.
 
bool simplifyGeometries
Indicates whether vector geometries should be simplified to avoid redundant extraneous detail,...
 
bool exportMetadata
Indicates whether SVG export should include RDF metadata generated from the layout's project's metada...
 
double dpi
Resolution to export layout at. If dpi <= 0 the default layout dpi will be used.
 
QgsLayoutRenderContext::Flags flags
Layout context flags, which control how the export will be created.
 
QVector< qreal > predefinedMapScales
A list of predefined scales to use with the layout.
 
bool exportLabelsToSeparateLayers
Set to true to export labels to separate layers (grouped by map layer) in layered SVG exports.
 
bool cropToContents
Set to true if image should be cropped so only parts of the layout containing items are exported.
 
QgsMargins cropMargins
Crop to content margins, in layout units.
 
Contains details of a particular export layer relating to a layout item.
 
QString name
User-friendly name for the export layer.