25#include "cpl_string.h" 
   28#include <QMutexLocker> 
   29#include <QDomDocument> 
   38  GDALDriverH hDriverMem = GDALGetDriverByName( 
"PDF" );
 
   44  const char *pHavePoppler = GDALGetMetadataItem( hDriverMem, 
"HAVE_POPPLER", 
nullptr );
 
   45  if ( pHavePoppler && strstr( pHavePoppler, 
"YES" ) )
 
   48  const char *pHavePdfium = GDALGetMetadataItem( hDriverMem, 
"HAVE_PDFIUM", 
nullptr );
 
   49  if ( pHavePdfium && strstr( pHavePdfium, 
"YES" ) )
 
 
   58  GDALDriverH hDriverMem = GDALGetDriverByName( 
"PDF" );
 
   61    return QObject::tr( 
"No GDAL PDF driver available." );
 
   64  const char *pHavePoppler = GDALGetMetadataItem( hDriverMem, 
"HAVE_POPPLER", 
nullptr );
 
   65  if ( pHavePoppler && strstr( pHavePoppler, 
"YES" ) )
 
   68  const char *pHavePdfium = GDALGetMetadataItem( hDriverMem, 
"HAVE_PDFIUM", 
nullptr );
 
   69  if ( pHavePdfium && strstr( pHavePdfium, 
"YES" ) )
 
   72  return QObject::tr( 
"GDAL PDF driver was not built with PDF read support. A build with PDF read support is required for GeoPDF creation." );
 
 
   80  const QString composition = createCompositionXml( components, details );
 
   82  if ( composition.isEmpty() )
 
   86  GDALDriverH driver = GDALGetDriverByName( 
"PDF" );
 
   89    mErrorMessage = QObject::tr( 
"Cannot load GDAL PDF driver" );
 
   94  QFile file( xmlFilePath );
 
   95  if ( file.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
 
   97    QTextStream out( &file );
 
   98#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 
   99    out.setCodec( 
"UTF-8" );
 
  105    mErrorMessage = QObject::tr( 
"Could not create GeoPDF composition file" );
 
  109  char **papszOptions = CSLSetNameValue( 
nullptr, 
"COMPOSITION_FILE", xmlFilePath.toUtf8().constData() );
 
  112  gdal::dataset_unique_ptr outputDataset( GDALCreate( driver, destinationFile.toUtf8().constData(), 0, 0, 0, GDT_Unknown, papszOptions ) );
 
  113  const bool res = outputDataset.get() != 
nullptr;
 
  114  outputDataset.reset();
 
  116  CSLDestroy( papszOptions );
 
 
  130    case QPainter::CompositionMode_SourceOver:
 
  131    case QPainter::CompositionMode_Multiply:
 
  132    case QPainter::CompositionMode_Screen:
 
  133    case QPainter::CompositionMode_Overlay:
 
  134    case QPainter::CompositionMode_Darken:
 
  135    case QPainter::CompositionMode_Lighten:
 
  136    case QPainter::CompositionMode_ColorDodge:
 
  137    case QPainter::CompositionMode_ColorBurn:
 
  138    case QPainter::CompositionMode_HardLight:
 
  139    case QPainter::CompositionMode_SoftLight:
 
  140    case QPainter::CompositionMode_Difference:
 
  141    case  QPainter::CompositionMode_Exclusion:
 
 
  154  QMutexLocker locker( &mMutex );
 
  159  mCollatedFeatures[ group ][ layerId ].append( f );
 
 
  162bool QgsAbstractGeoPdfExporter::saveTemporaryLayers()
 
  164  for ( 
auto groupIt = mCollatedFeatures.constBegin(); groupIt != mCollatedFeatures.constEnd(); ++groupIt )
 
  166    for ( 
auto it = groupIt->constBegin(); it != groupIt->constEnd(); ++it )
 
  170      VectorComponentDetail detail = componentDetailForLayerId( it.key() );
 
  171      detail.sourceVectorPath = filePath;
 
  172      detail.group = groupIt.key();
 
  178      saveOptions.
driverName = QStringLiteral( 
"GPKG" );
 
  181      if ( writer->hasError() )
 
  183        mErrorMessage = writer->errorMessage();
 
  192          mErrorMessage = writer->errorMessage();
 
  197      detail.sourceVectorLayer = layerName;
 
  198      mVectorComponents << detail;
 
  204QString QgsAbstractGeoPdfExporter::createCompositionXml( 
const QList<ComponentLayerDetail> &components, 
const ExportDetails &details )
 
  208  QDomElement compositionElem = doc.createElement( QStringLiteral( 
"PDFComposition" ) );
 
  211  QDomElement metadata = doc.createElement( QStringLiteral( 
"Metadata" ) );
 
  212  if ( !details.author.isEmpty() )
 
  214    QDomElement author = doc.createElement( QStringLiteral( 
"Author" ) );
 
  215    author.appendChild( doc.createTextNode( details.author ) );
 
  216    metadata.appendChild( author );
 
  218  if ( !details.producer.isEmpty() )
 
  220    QDomElement producer = doc.createElement( QStringLiteral( 
"Producer" ) );
 
  221    producer.appendChild( doc.createTextNode( details.producer ) );
 
  222    metadata.appendChild( producer );
 
  224  if ( !details.creator.isEmpty() )
 
  226    QDomElement creator = doc.createElement( QStringLiteral( 
"Creator" ) );
 
  227    creator.appendChild( doc.createTextNode( details.creator ) );
 
  228    metadata.appendChild( creator );
 
  230  if ( details.creationDateTime.isValid() )
 
  232    QDomElement creationDate = doc.createElement( QStringLiteral( 
"CreationDate" ) );
 
  233    QString creationDateString = QStringLiteral( 
"D:%1" ).arg( details.creationDateTime.toString( QStringLiteral( 
"yyyyMMddHHmmss" ) ) );
 
  234    if ( details.creationDateTime.timeZone().isValid() )
 
  236      int offsetFromUtc = details.creationDateTime.timeZone().offsetFromUtc( details.creationDateTime );
 
  237      creationDateString += ( offsetFromUtc >= 0 ) ? 
'+' : 
'-';
 
  238      offsetFromUtc = std::abs( offsetFromUtc );
 
  239      int offsetHours = offsetFromUtc / 3600;
 
  240      int offsetMins = ( offsetFromUtc % 3600 ) / 60;
 
  241      creationDateString += QStringLiteral( 
"%1'%2'" ).arg( offsetHours ).arg( offsetMins );
 
  243    creationDate.appendChild( doc.createTextNode( creationDateString ) );
 
  244    metadata.appendChild( creationDate );
 
  246  if ( !details.subject.isEmpty() )
 
  248    QDomElement subject = doc.createElement( QStringLiteral( 
"Subject" ) );
 
  249    subject.appendChild( doc.createTextNode( details.subject ) );
 
  250    metadata.appendChild( subject );
 
  252  if ( !details.title.isEmpty() )
 
  254    QDomElement title = doc.createElement( QStringLiteral( 
"Title" ) );
 
  255    title.appendChild( doc.createTextNode( details.title ) );
 
  256    metadata.appendChild( title );
 
  258  if ( !details.keywords.empty() )
 
  260    QStringList allKeywords;
 
  261    for ( 
auto it = details.keywords.constBegin(); it != details.keywords.constEnd(); ++it )
 
  263      allKeywords.append( QStringLiteral( 
"%1: %2" ).arg( it.key(), it.value().join( 
',' ) ) );
 
  265    QDomElement keywords = doc.createElement( QStringLiteral( 
"Keywords" ) );
 
  266    keywords.appendChild( doc.createTextNode( allKeywords.join( 
';' ) ) );
 
  267    metadata.appendChild( keywords );
 
  269  compositionElem.appendChild( metadata );
 
  271  QMap< QString, QSet< QString > > createdLayerIds;
 
  272  QMap< QString, QDomElement > groupLayerMap;
 
  273  QMap< QString, QString > customGroupNamesToIds;
 
  275  QMultiMap< QString, QDomElement > pendingLayerTreeElements;
 
  277  if ( details.includeFeatures )
 
  279    for ( 
const VectorComponentDetail &component : std::as_const( mVectorComponents ) )
 
  281      if ( details.customLayerTreeGroups.contains( component.mapLayerId ) )
 
  284      QDomElement layer = doc.createElement( QStringLiteral( 
"Layer" ) );
 
  285      layer.setAttribute( QStringLiteral( 
"id" ), component.group.isEmpty() ? component.mapLayerId : QStringLiteral( 
"%1_%2" ).arg( component.group, component.mapLayerId ) );
 
  286      layer.setAttribute( QStringLiteral( 
"name" ), details.layerIdToPdfLayerTreeNameMap.contains( component.mapLayerId ) ? details.layerIdToPdfLayerTreeNameMap.value( component.mapLayerId ) : component.name );
 
  287      layer.setAttribute( QStringLiteral( 
"initiallyVisible" ), details.initialLayerVisibility.value( component.mapLayerId, 
true ) ? QStringLiteral( 
"true" ) : QStringLiteral( 
"false" ) );
 
  289      if ( !component.group.isEmpty() )
 
  291        if ( groupLayerMap.contains( component.group ) )
 
  293          groupLayerMap[ component.group ].appendChild( layer );
 
  297          QDomElement group = doc.createElement( QStringLiteral( 
"Layer" ) );
 
  298          group.setAttribute( QStringLiteral( 
"id" ), QStringLiteral( 
"group_%1" ).arg( component.group ) );
 
  299          group.setAttribute( QStringLiteral( 
"name" ), component.group );
 
  300          group.setAttribute( QStringLiteral( 
"initiallyVisible" ), groupLayerMap.empty() ? QStringLiteral( 
"true" ) : QStringLiteral( 
"false" ) );
 
  301          group.setAttribute( QStringLiteral( 
"mutuallyExclusiveGroupId" ), QStringLiteral( 
"__mutually_exclusive_groups__" ) );
 
  302          pendingLayerTreeElements.insert( component.mapLayerId, group );
 
  303          group.appendChild( layer );
 
  304          groupLayerMap[ component.group ] = group;
 
  309        pendingLayerTreeElements.insert( component.mapLayerId, layer );
 
  312      createdLayerIds[ component.group ].insert( component.mapLayerId );
 
  316  for ( 
const ComponentLayerDetail &component : components )
 
  318    if ( component.mapLayerId.isEmpty() || createdLayerIds.value( component.group ).contains( component.mapLayerId ) )
 
  321    if ( details.customLayerTreeGroups.contains( component.mapLayerId ) )
 
  324    QDomElement layer = doc.createElement( QStringLiteral( 
"Layer" ) );
 
  325    layer.setAttribute( QStringLiteral( 
"id" ), component.group.isEmpty() ? component.mapLayerId : QStringLiteral( 
"%1_%2" ).arg( component.group, component.mapLayerId ) );
 
  326    layer.setAttribute( QStringLiteral( 
"name" ), details.layerIdToPdfLayerTreeNameMap.contains( component.mapLayerId ) ? details.layerIdToPdfLayerTreeNameMap.value( component.mapLayerId ) : component.name );
 
  327    layer.setAttribute( QStringLiteral( 
"initiallyVisible" ), details.initialLayerVisibility.value( component.mapLayerId, 
true ) ? QStringLiteral( 
"true" ) : QStringLiteral( 
"false" ) );
 
  329    if ( !component.group.isEmpty() )
 
  331      if ( groupLayerMap.contains( component.group ) )
 
  333        groupLayerMap[ component.group ].appendChild( layer );
 
  337        QDomElement group = doc.createElement( QStringLiteral( 
"Layer" ) );
 
  338        group.setAttribute( QStringLiteral( 
"id" ), QStringLiteral( 
"group_%1" ).arg( component.group ) );
 
  339        group.setAttribute( QStringLiteral( 
"name" ), component.group );
 
  340        group.setAttribute( QStringLiteral( 
"initiallyVisible" ), groupLayerMap.empty() ? QStringLiteral( 
"true" ) : QStringLiteral( 
"false" ) );
 
  341        group.setAttribute( QStringLiteral( 
"mutuallyExclusiveGroupId" ), QStringLiteral( 
"__mutually_exclusive_groups__" ) );
 
  342        pendingLayerTreeElements.insert( component.mapLayerId, group );
 
  343        group.appendChild( layer );
 
  344        groupLayerMap[ component.group ] = group;
 
  349      pendingLayerTreeElements.insert( component.mapLayerId, layer );
 
  352    createdLayerIds[ component.group ].insert( component.mapLayerId );
 
  356  QDomElement 
layerTree = doc.createElement( QStringLiteral( 
"LayerTree" ) );
 
  360  for ( 
auto it = details.customLayerTreeGroups.constBegin(); it != details.customLayerTreeGroups.constEnd(); ++it )
 
  362    if ( customGroupNamesToIds.contains( it.value() ) )
 
  365    QDomElement layer = doc.createElement( QStringLiteral( 
"Layer" ) );
 
  366    const QString 
id = QUuid::createUuid().toString();
 
  367    customGroupNamesToIds[ it.value() ] = id;
 
  368    layer.setAttribute( QStringLiteral( 
"id" ), 
id );
 
  369    layer.setAttribute( QStringLiteral( 
"name" ), it.value() );
 
  370    layer.setAttribute( QStringLiteral( 
"initiallyVisible" ), QStringLiteral( 
"true" ) );
 
  375  for ( 
const QString &layerId : details.layerOrder )
 
  377    const QList< QDomElement> elements = pendingLayerTreeElements.values( layerId );
 
  378    for ( 
const QDomElement &element : elements )
 
  382  for ( 
auto it = pendingLayerTreeElements.constBegin(); it != pendingLayerTreeElements.constEnd(); ++it )
 
  384    if ( details.layerOrder.contains( it.key() ) )
 
  393  compositionElem.appendChild( layerTree );
 
  396  QDomElement page = doc.createElement( QStringLiteral( 
"Page" ) );
 
  397  QDomElement dpi = doc.createElement( QStringLiteral( 
"DPI" ) );
 
  400  page.appendChild( dpi );
 
  402  QDomElement width = doc.createElement( QStringLiteral( 
"Width" ) );
 
  403  const double pageWidthPdfUnits = std::ceil( details.pageSizeMm.width() / 25.4 * 72 );
 
  404  width.appendChild( doc.createTextNode( 
qgsDoubleToString( pageWidthPdfUnits ) ) );
 
  405  page.appendChild( width );
 
  406  QDomElement height = doc.createElement( QStringLiteral( 
"Height" ) );
 
  407  const double pageHeightPdfUnits = std::ceil( details.pageSizeMm.height() / 25.4 * 72 );
 
  408  height.appendChild( doc.createTextNode( 
qgsDoubleToString( pageHeightPdfUnits ) ) );
 
  409  page.appendChild( height );
 
  416    QDomElement georeferencing = doc.createElement( QStringLiteral( 
"Georeferencing" ) );
 
  417    georeferencing.setAttribute( QStringLiteral( 
"id" ), QStringLiteral( 
"georeferenced_%1" ).arg( i++ ) );
 
  418    georeferencing.setAttribute( QStringLiteral( 
"OGCBestPracticeFormat" ), details.useOgcBestPracticeFormatGeoreferencing ? QStringLiteral( 
"true" ) : QStringLiteral( 
"false" ) );
 
  419    georeferencing.setAttribute( QStringLiteral( 
"ISO32000ExtensionFormat" ), details.useIso32000ExtensionFormatGeoreferencing ? QStringLiteral( 
"true" ) : QStringLiteral( 
"false" ) );
 
  421    if ( section.crs.isValid() )
 
  423      QDomElement srs = doc.createElement( QStringLiteral( 
"SRS" ) );
 
  426      if ( !section.crs.authid().startsWith( QStringLiteral( 
"user" ), Qt::CaseInsensitive ) )
 
  428        srs.appendChild( doc.createTextNode( section.crs.authid() ) );
 
  434      georeferencing.appendChild( srs );
 
  437    if ( !section.pageBoundsPolygon.isEmpty() )
 
  446      QDomElement boundingPolygon = doc.createElement( QStringLiteral( 
"BoundingPolygon" ) );
 
  449      QTransform t = QTransform::fromTranslate( 0, pageHeightPdfUnits ).scale( pageWidthPdfUnits / details.pageSizeMm.width(),
 
  450                     -pageHeightPdfUnits / details.pageSizeMm.height() );
 
  454      boundingPolygon.appendChild( doc.createTextNode( p.
asWkt() ) );
 
  456      georeferencing.appendChild( boundingPolygon );
 
  465      QDomElement boundingBox = doc.createElement( QStringLiteral( 
"BoundingBox" ) );
 
  466      boundingBox.setAttribute( QStringLiteral( 
"x1" ), 
qgsDoubleToString( section.pageBoundsMm.xMinimum() / 25.4 * 72 ) );
 
  467      boundingBox.setAttribute( QStringLiteral( 
"y1" ), 
qgsDoubleToString( section.pageBoundsMm.yMinimum() / 25.4 * 72 ) );
 
  468      boundingBox.setAttribute( QStringLiteral( 
"x2" ), 
qgsDoubleToString( section.pageBoundsMm.xMaximum() / 25.4 * 72 ) );
 
  469      boundingBox.setAttribute( QStringLiteral( 
"y2" ), 
qgsDoubleToString( section.pageBoundsMm.yMaximum() / 25.4 * 72 ) );
 
  470      georeferencing.appendChild( boundingBox );
 
  473    for ( 
const ControlPoint &point : section.controlPoints )
 
  475      QDomElement cp1 = doc.createElement( QStringLiteral( 
"ControlPoint" ) );
 
  476      cp1.setAttribute( QStringLiteral( 
"x" ), 
qgsDoubleToString( point.pagePoint.x() / 25.4 * 72 ) );
 
  477      cp1.setAttribute( QStringLiteral( 
"y" ), 
qgsDoubleToString( ( details.pageSizeMm.height() - point.pagePoint.y() ) / 25.4 * 72 ) );
 
  478      cp1.setAttribute( QStringLiteral( 
"GeoX" ), 
qgsDoubleToString( point.geoPoint.x() ) );
 
  479      cp1.setAttribute( QStringLiteral( 
"GeoY" ), 
qgsDoubleToString( point.geoPoint.y() ) );
 
  480      georeferencing.appendChild( cp1 );
 
  483    page.appendChild( georeferencing );
 
  486  auto createPdfDatasetElement = [&doc]( 
const ComponentLayerDetail & component ) -> QDomElement
 
  488    QDomElement pdfDataset = doc.createElement( QStringLiteral( 
"PDF" ) );
 
  489    pdfDataset.setAttribute( QStringLiteral( 
"dataset" ), component.sourcePdfPath );
 
  490    if ( component.opacity != 1.0 || component.compositionMode != QPainter::CompositionMode_SourceOver )
 
  492      QDomElement blendingElement = doc.createElement( QStringLiteral( 
"Blending" ) );
 
  493      blendingElement.setAttribute( QStringLiteral( 
"opacity" ), component.opacity );
 
  494      blendingElement.setAttribute( QStringLiteral( 
"function" ), compositionModeToString( component.compositionMode ) );
 
  496      pdfDataset.appendChild( blendingElement );
 
  502  QDomElement content = doc.createElement( QStringLiteral( 
"Content" ) );
 
  503  for ( 
const ComponentLayerDetail &component : components )
 
  505    if ( component.mapLayerId.isEmpty() )
 
  507      content.appendChild( createPdfDatasetElement( component ) );
 
  509    else if ( !component.group.isEmpty() )
 
  512      QDomElement ifGroupOn = doc.createElement( QStringLiteral( 
"IfLayerOn" ) );
 
  513      ifGroupOn.setAttribute( QStringLiteral( 
"layerId" ), QStringLiteral( 
"group_%1" ).arg( component.group ) );
 
  514      QDomElement ifLayerOn = doc.createElement( QStringLiteral( 
"IfLayerOn" ) );
 
  515      if ( details.customLayerTreeGroups.contains( component.mapLayerId ) )
 
  516        ifLayerOn.setAttribute( QStringLiteral( 
"layerId" ), customGroupNamesToIds.value( details.customLayerTreeGroups.value( component.mapLayerId ) ) );
 
  517      else if ( component.group.isEmpty() )
 
  518        ifLayerOn.setAttribute( QStringLiteral( 
"layerId" ), component.mapLayerId );
 
  520        ifLayerOn.setAttribute( QStringLiteral( 
"layerId" ), QStringLiteral( 
"%1_%2" ).arg( component.group, component.mapLayerId ) );
 
  522      ifLayerOn.appendChild( createPdfDatasetElement( component ) );
 
  523      ifGroupOn.appendChild( ifLayerOn );
 
  524      content.appendChild( ifGroupOn );
 
  528      QDomElement ifLayerOn = doc.createElement( QStringLiteral( 
"IfLayerOn" ) );
 
  529      if ( details.customLayerTreeGroups.contains( component.mapLayerId ) )
 
  530        ifLayerOn.setAttribute( QStringLiteral( 
"layerId" ), customGroupNamesToIds.value( details.customLayerTreeGroups.value( component.mapLayerId ) ) );
 
  531      else if ( component.group.isEmpty() )
 
  532        ifLayerOn.setAttribute( QStringLiteral( 
"layerId" ), component.mapLayerId );
 
  534        ifLayerOn.setAttribute( QStringLiteral( 
"layerId" ), QStringLiteral( 
"%1_%2" ).arg( component.group, component.mapLayerId ) );
 
  535      ifLayerOn.appendChild( createPdfDatasetElement( component ) );
 
  536      content.appendChild( ifLayerOn );
 
  541  if ( details.includeFeatures )
 
  543    for ( 
const VectorComponentDetail &component : std::as_const( mVectorComponents ) )
 
  545      QDomElement ifLayerOn = doc.createElement( QStringLiteral( 
"IfLayerOn" ) );
 
  546      if ( details.customLayerTreeGroups.contains( component.mapLayerId ) )
 
  547        ifLayerOn.setAttribute( QStringLiteral( 
"layerId" ), customGroupNamesToIds.value( details.customLayerTreeGroups.value( component.mapLayerId ) ) );
 
  548      else if ( component.group.isEmpty() )
 
  549        ifLayerOn.setAttribute( QStringLiteral( 
"layerId" ), component.mapLayerId );
 
  551        ifLayerOn.setAttribute( QStringLiteral( 
"layerId" ), QStringLiteral( 
"%1_%2" ).arg( component.group, component.mapLayerId ) );
 
  552      QDomElement vectorDataset = doc.createElement( QStringLiteral( 
"Vector" ) );
 
  553      vectorDataset.setAttribute( QStringLiteral( 
"dataset" ), component.sourceVectorPath );
 
  554      vectorDataset.setAttribute( QStringLiteral( 
"layer" ), component.sourceVectorLayer );
 
  555      vectorDataset.setAttribute( QStringLiteral( 
"visible" ), QStringLiteral( 
"false" ) );
 
  556      QDomElement logicalStructure = doc.createElement( QStringLiteral( 
"LogicalStructure" ) );
 
  557      logicalStructure.setAttribute( QStringLiteral( 
"displayLayerName" ), component.name );
 
  558      if ( !component.displayAttribute.isEmpty() )
 
  559        logicalStructure.setAttribute( QStringLiteral( 
"fieldToDisplay" ), component.displayAttribute );
 
  560      vectorDataset.appendChild( logicalStructure );
 
  561      ifLayerOn.appendChild( vectorDataset );
 
  562      content.appendChild( ifLayerOn );
 
  566  page.appendChild( content );
 
  567  compositionElem.appendChild( page );
 
  569  doc.appendChild( compositionElem );
 
  572  QTextStream stream( &composition );
 
  573  doc.save( stream, -1 );
 
  578QString QgsAbstractGeoPdfExporter::compositionModeToString( QPainter::CompositionMode mode )
 
  582    case QPainter::CompositionMode_SourceOver:
 
  583      return QStringLiteral( 
"Normal" );
 
  585    case QPainter::CompositionMode_Multiply:
 
  586      return QStringLiteral( 
"Multiply" );
 
  588    case QPainter::CompositionMode_Screen:
 
  589      return QStringLiteral( 
"Screen" );
 
  591    case QPainter::CompositionMode_Overlay:
 
  592      return QStringLiteral( 
"Overlay" );
 
  594    case QPainter::CompositionMode_Darken:
 
  595      return QStringLiteral( 
"Darken" );
 
  597    case QPainter::CompositionMode_Lighten:
 
  598      return QStringLiteral( 
"Lighten" );
 
  600    case QPainter::CompositionMode_ColorDodge:
 
  601      return QStringLiteral( 
"ColorDodge" );
 
  603    case QPainter::CompositionMode_ColorBurn:
 
  604      return QStringLiteral( 
"ColorBurn" );
 
  606    case QPainter::CompositionMode_HardLight:
 
  607      return QStringLiteral( 
"HardLight" );
 
  609    case QPainter::CompositionMode_SoftLight:
 
  610      return QStringLiteral( 
"SoftLight" );
 
  612    case QPainter::CompositionMode_Difference:
 
  613      return QStringLiteral( 
"Difference" );
 
  615    case  QPainter::CompositionMode_Exclusion:
 
  616      return QStringLiteral( 
"Exclusion" );
 
  622  QgsDebugError( QStringLiteral( 
"Unsupported PDF blend mode %1" ).arg( mode ) );
 
  623  return QStringLiteral( 
"Normal" );
 
@ PreferredGdal
Preferred format for conversion of CRS to WKT for use with the GDAL library.
@ NoSymbology
Export only data.
void pushRenderedFeature(const QString &layerId, const QgsAbstractGeoPdfExporter::RenderedFeature &feature, const QString &group=QString())
Called multiple times during the rendering operation, whenever a feature associated with the specifie...
QString generateTemporaryFilepath(const QString &filename) const
Returns a file path to use for temporary files required for GeoPDF creation.
static bool geoPDFCreationAvailable()
Returns true if the current QGIS build is capable of GeoPDF support.
static bool compositionModeSupported(QPainter::CompositionMode mode)
Returns true if the specified composition mode is supported for layers during GeoPDF exports.
static QString geoPDFAvailabilityExplanation()
Returns a user-friendly, translated string explaining why GeoPDF export support is not available on t...
bool finalize(const QList< QgsAbstractGeoPdfExporter::ComponentLayerDetail > &components, const QString &destinationFile, const ExportDetails &details)
To be called after the rendering operation is complete.
This class represents a coordinate reference system (CRS).
Contains information about the context in which a coordinate transform is executed.
void transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection d=Qgis::TransformDirection::Forward, bool transformZ=false) override
Transforms the geometry using a coordinate transform.
@ FastInsert
Use faster inserts, at the cost of updating the passed features to reflect changes made at the provid...
@ RegeneratePrimaryKey
This flag indicates, that a primary key field cannot be guaranteed to be unique and the sink should i...
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
static QString stringToSafeFilename(const QString &string)
Converts a string to a safe filename, replacing characters which are not safe for filenames with an '...
QString asWkt(int precision=17) const override
Returns a WKT representation of the geometry.
Options to pass to writeAsVectorFormat()
QString driverName
OGR driver to use.
Qgis::FeatureSymbologyExport symbologyExport
Symbology to export.
static QgsVectorFileWriter * create(const QString &fileName, const QgsFields &fields, Qgis::WkbType geometryType, const QgsCoordinateReferenceSystem &srs, const QgsCoordinateTransformContext &transformContext, const QgsVectorFileWriter::SaveVectorOptions &options, QgsFeatureSink::SinkFlags sinkFlags=QgsFeatureSink::SinkFlags(), QString *newFilename=nullptr, QString *newLayer=nullptr)
Create a new vector file writer.
QgsLayerTree * layerTree(const QgsWmsRenderContext &context)
std::unique_ptr< std::remove_pointer< GDALDatasetH >::type, GDALDatasetCloser > dataset_unique_ptr
Scoped GDAL dataset.
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
QList< QgsFeature > QgsFeatureList
#define QgsDebugMsgLevel(str, level)
#define QgsDebugError(str)
bool includeFeatures
true if feature vector information (such as attributes) should be exported.
Contains information about a feature rendered inside the PDF.
QgsFeature feature
Rendered feature.
QgsGeometry renderedBounds
Bounds, in PDF units, of rendered feature.