28 #include "cpl_string.h" 
   31 #include <QMutexLocker> 
   32 #include <QDomDocument> 
   33 #include <QDomElement> 
   36 #include <QTextStream> 
   41   GDALDriverH hDriverMem = GDALGetDriverByName( 
"PDF" );
 
   47   const char *pHavePoppler = GDALGetMetadataItem( hDriverMem, 
"HAVE_POPPLER", 
nullptr );
 
   48   if ( pHavePoppler && strstr( pHavePoppler, 
"YES" ) )
 
   51   const char *pHavePdfium = GDALGetMetadataItem( hDriverMem, 
"HAVE_PDFIUM", 
nullptr );
 
   52   if ( pHavePdfium && strstr( pHavePdfium, 
"YES" ) )
 
   61   GDALDriverH hDriverMem = GDALGetDriverByName( 
"PDF" );
 
   64     return QObject::tr( 
"No GDAL PDF driver available." );
 
   67   const char *pHavePoppler = GDALGetMetadataItem( hDriverMem, 
"HAVE_POPPLER", 
nullptr );
 
   68   if ( pHavePoppler && strstr( pHavePoppler, 
"YES" ) )
 
   71   const char *pHavePdfium = GDALGetMetadataItem( hDriverMem, 
"HAVE_PDFIUM", 
nullptr );
 
   72   if ( pHavePdfium && strstr( pHavePdfium, 
"YES" ) )
 
   75   return QObject::tr( 
"GDAL PDF driver was not built with PDF read support. A build with PDF read support is required for GeoPDF creation." );
 
   83   const QString composition = createCompositionXml( components, details );
 
   85   if ( composition.isEmpty() )
 
   89   GDALDriverH driver = GDALGetDriverByName( 
"PDF" );
 
   92     mErrorMessage = QObject::tr( 
"Cannot load GDAL PDF driver" );
 
   97   QFile file( xmlFilePath );
 
   98   if ( file.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
 
  100     QTextStream out( &file );
 
  101 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 
  102     out.setCodec( 
"UTF-8" );
 
  108     mErrorMessage = QObject::tr( 
"Could not create GeoPDF composition file" );
 
  112   char **papszOptions = CSLSetNameValue( 
nullptr, 
"COMPOSITION_FILE", xmlFilePath.toUtf8().constData() );
 
  115   gdal::dataset_unique_ptr outputDataset( GDALCreate( driver, destinationFile.toUtf8().constData(), 0, 0, 0, GDT_Unknown, papszOptions ) );
 
  116   bool res = outputDataset.get();
 
  117   outputDataset.reset();
 
  119   CSLDestroy( papszOptions );
 
  126   return mTemporaryDir.filePath( filename );
 
  133     case QPainter::CompositionMode_SourceOver:
 
  134     case QPainter::CompositionMode_Multiply:
 
  135     case QPainter::CompositionMode_Screen:
 
  136     case QPainter::CompositionMode_Overlay:
 
  137     case QPainter::CompositionMode_Darken:
 
  138     case QPainter::CompositionMode_Lighten:
 
  139     case QPainter::CompositionMode_ColorDodge:
 
  140     case QPainter::CompositionMode_ColorBurn:
 
  141     case QPainter::CompositionMode_HardLight:
 
  142     case QPainter::CompositionMode_SoftLight:
 
  143     case QPainter::CompositionMode_Difference:
 
  144     case  QPainter::CompositionMode_Exclusion:
 
  157   QMutexLocker locker( &mMutex );
 
  162   mCollatedFeatures[ group ][ layerId ].append( f );
 
  165 bool QgsAbstractGeoPdfExporter::saveTemporaryLayers()
 
  167   for ( 
auto groupIt = mCollatedFeatures.constBegin(); groupIt != mCollatedFeatures.constEnd(); ++groupIt )
 
  169     for ( 
auto it = groupIt->constBegin(); it != groupIt->constEnd(); ++it )
 
  173       VectorComponentDetail detail = componentDetailForLayerId( it.key() );
 
  174       detail.sourceVectorPath = filePath;
 
  175       detail.group = groupIt.key();
 
  181       saveOptions.
driverName = QStringLiteral( 
"GPKG" );
 
  184       if ( writer->hasError() )
 
  186         mErrorMessage = writer->errorMessage();
 
  195           mErrorMessage = writer->errorMessage();
 
  200       detail.sourceVectorLayer = layerName;
 
  201       mVectorComponents << detail;
 
  207 QString QgsAbstractGeoPdfExporter::createCompositionXml( 
const QList<ComponentLayerDetail> &components, 
const ExportDetails &details )
 
  211   QDomElement compositionElem = doc.createElement( QStringLiteral( 
"PDFComposition" ) );
 
  214   QDomElement metadata = doc.createElement( QStringLiteral( 
"Metadata" ) );
 
  215   if ( !details.author.isEmpty() )
 
  217     QDomElement author = doc.createElement( QStringLiteral( 
"Author" ) );
 
  218     author.appendChild( doc.createTextNode( details.author ) );
 
  219     metadata.appendChild( author );
 
  221   if ( !details.producer.isEmpty() )
 
  223     QDomElement producer = doc.createElement( QStringLiteral( 
"Producer" ) );
 
  224     producer.appendChild( doc.createTextNode( details.producer ) );
 
  225     metadata.appendChild( producer );
 
  227   if ( !details.creator.isEmpty() )
 
  229     QDomElement creator = doc.createElement( QStringLiteral( 
"Creator" ) );
 
  230     creator.appendChild( doc.createTextNode( details.creator ) );
 
  231     metadata.appendChild( creator );
 
  233   if ( details.creationDateTime.isValid() )
 
  235     QDomElement creationDate = doc.createElement( QStringLiteral( 
"CreationDate" ) );
 
  236     QString creationDateString = QStringLiteral( 
"D:%1" ).arg( details.creationDateTime.toString( QStringLiteral( 
"yyyyMMddHHmmss" ) ) );
 
  237     if ( details.creationDateTime.timeZone().isValid() )
 
  239       int offsetFromUtc = details.creationDateTime.timeZone().offsetFromUtc( details.creationDateTime );
 
  240       creationDateString += ( offsetFromUtc >= 0 ) ? 
'+' : 
'-';
 
  241       offsetFromUtc = std::abs( offsetFromUtc );
 
  242       int offsetHours = offsetFromUtc / 3600;
 
  243       int offsetMins = ( offsetFromUtc % 3600 ) / 60;
 
  244       creationDateString += QStringLiteral( 
"%1'%2'" ).arg( offsetHours ).arg( offsetMins );
 
  246     creationDate.appendChild( doc.createTextNode( creationDateString ) );
 
  247     metadata.appendChild( creationDate );
 
  249   if ( !details.subject.isEmpty() )
 
  251     QDomElement subject = doc.createElement( QStringLiteral( 
"Subject" ) );
 
  252     subject.appendChild( doc.createTextNode( details.subject ) );
 
  253     metadata.appendChild( subject );
 
  255   if ( !details.title.isEmpty() )
 
  257     QDomElement title = doc.createElement( QStringLiteral( 
"Title" ) );
 
  258     title.appendChild( doc.createTextNode( details.title ) );
 
  259     metadata.appendChild( title );
 
  261   if ( !details.keywords.empty() )
 
  263     QStringList allKeywords;
 
  264     for ( 
auto it = details.keywords.constBegin(); it != details.keywords.constEnd(); ++it )
 
  266       allKeywords.append( QStringLiteral( 
"%1: %2" ).arg( it.key(), it.value().join( 
',' ) ) );
 
  268     QDomElement keywords = doc.createElement( QStringLiteral( 
"Keywords" ) );
 
  269     keywords.appendChild( doc.createTextNode( allKeywords.join( 
';' ) ) );
 
  270     metadata.appendChild( keywords );
 
  272   compositionElem.appendChild( metadata );
 
  274   QMap< QString, QSet< QString > > createdLayerIds;
 
  275   QMap< QString, QDomElement > groupLayerMap;
 
  276   QMap< QString, QString > customGroupNamesToIds;
 
  278   QMultiMap< QString, QDomElement > pendingLayerTreeElements;
 
  280   if ( details.includeFeatures )
 
  282     for ( 
const VectorComponentDetail &component : std::as_const( mVectorComponents ) )
 
  284       if ( details.customLayerTreeGroups.contains( component.mapLayerId ) )
 
  287       QDomElement layer = doc.createElement( QStringLiteral( 
"Layer" ) );
 
  288       layer.setAttribute( QStringLiteral( 
"id" ), component.group.isEmpty() ? component.mapLayerId : QStringLiteral( 
"%1_%2" ).arg( component.group, component.mapLayerId ) );
 
  289       layer.setAttribute( QStringLiteral( 
"name" ), details.layerIdToPdfLayerTreeNameMap.contains( component.mapLayerId ) ? details.layerIdToPdfLayerTreeNameMap.value( component.mapLayerId ) : component.name );
 
  290       layer.setAttribute( QStringLiteral( 
"initiallyVisible" ), details.initialLayerVisibility.value( component.mapLayerId, 
true ) ? QStringLiteral( 
"true" ) : QStringLiteral( 
"false" ) );
 
  292       if ( !component.group.isEmpty() )
 
  294         if ( groupLayerMap.contains( component.group ) )
 
  296           groupLayerMap[ component.group ].appendChild( layer );
 
  300           QDomElement group = doc.createElement( QStringLiteral( 
"Layer" ) );
 
  301           group.setAttribute( QStringLiteral( 
"id" ), QStringLiteral( 
"group_%1" ).arg( component.group ) );
 
  302           group.setAttribute( QStringLiteral( 
"name" ), component.group );
 
  303           group.setAttribute( QStringLiteral( 
"initiallyVisible" ), groupLayerMap.empty() ? QStringLiteral( 
"true" ) : QStringLiteral( 
"false" ) );
 
  304           group.setAttribute( QStringLiteral( 
"mutuallyExclusiveGroupId" ), QStringLiteral( 
"__mutually_exclusive_groups__" ) );
 
  305           pendingLayerTreeElements.insert( component.mapLayerId, group );
 
  306           group.appendChild( layer );
 
  307           groupLayerMap[ component.group ] = group;
 
  312         pendingLayerTreeElements.insert( component.mapLayerId, layer );
 
  315       createdLayerIds[ component.group ].insert( component.mapLayerId );
 
  319   for ( 
const ComponentLayerDetail &component : components )
 
  321     if ( component.mapLayerId.isEmpty() || createdLayerIds.value( component.group ).contains( component.mapLayerId ) )
 
  324     if ( details.customLayerTreeGroups.contains( component.mapLayerId ) )
 
  327     QDomElement layer = doc.createElement( QStringLiteral( 
"Layer" ) );
 
  328     layer.setAttribute( QStringLiteral( 
"id" ), component.group.isEmpty() ? component.mapLayerId : QStringLiteral( 
"%1_%2" ).arg( component.group, component.mapLayerId ) );
 
  329     layer.setAttribute( QStringLiteral( 
"name" ), details.layerIdToPdfLayerTreeNameMap.contains( component.mapLayerId ) ? details.layerIdToPdfLayerTreeNameMap.value( component.mapLayerId ) : component.name );
 
  330     layer.setAttribute( QStringLiteral( 
"initiallyVisible" ), details.initialLayerVisibility.value( component.mapLayerId, 
true ) ? QStringLiteral( 
"true" ) : QStringLiteral( 
"false" ) );
 
  332     if ( !component.group.isEmpty() )
 
  334       if ( groupLayerMap.contains( component.group ) )
 
  336         groupLayerMap[ component.group ].appendChild( layer );
 
  340         QDomElement group = doc.createElement( QStringLiteral( 
"Layer" ) );
 
  341         group.setAttribute( QStringLiteral( 
"id" ), QStringLiteral( 
"group_%1" ).arg( component.group ) );
 
  342         group.setAttribute( QStringLiteral( 
"name" ), component.group );
 
  343         group.setAttribute( QStringLiteral( 
"initiallyVisible" ), groupLayerMap.empty() ? QStringLiteral( 
"true" ) : QStringLiteral( 
"false" ) );
 
  344         group.setAttribute( QStringLiteral( 
"mutuallyExclusiveGroupId" ), QStringLiteral( 
"__mutually_exclusive_groups__" ) );
 
  345         pendingLayerTreeElements.insert( component.mapLayerId, group );
 
  346         group.appendChild( layer );
 
  347         groupLayerMap[ component.group ] = group;
 
  352       pendingLayerTreeElements.insert( component.mapLayerId, layer );
 
  355     createdLayerIds[ component.group ].insert( component.mapLayerId );
 
  359   QDomElement 
layerTree = doc.createElement( QStringLiteral( 
"LayerTree" ) );
 
  363   for ( 
auto it = details.customLayerTreeGroups.constBegin(); it != details.customLayerTreeGroups.constEnd(); ++it )
 
  365     if ( customGroupNamesToIds.contains( it.value() ) )
 
  368     QDomElement layer = doc.createElement( QStringLiteral( 
"Layer" ) );
 
  369     const QString 
id = QUuid::createUuid().toString();
 
  370     customGroupNamesToIds[ it.value() ] = id;
 
  371     layer.setAttribute( QStringLiteral( 
"id" ), 
id );
 
  372     layer.setAttribute( QStringLiteral( 
"name" ), it.value() );
 
  373     layer.setAttribute( QStringLiteral( 
"initiallyVisible" ), QStringLiteral( 
"true" ) );
 
  378   for ( 
const QString &layerId : details.layerOrder )
 
  380     const QList< QDomElement> elements = pendingLayerTreeElements.values( layerId );
 
  381     for ( 
const QDomElement &element : elements )
 
  385   for ( 
auto it = pendingLayerTreeElements.constBegin(); it != pendingLayerTreeElements.constEnd(); ++it )
 
  387     if ( details.layerOrder.contains( it.key() ) )
 
  396   compositionElem.appendChild( 
layerTree );
 
  399   QDomElement page = doc.createElement( QStringLiteral( 
"Page" ) );
 
  400   QDomElement dpi = doc.createElement( QStringLiteral( 
"DPI" ) );
 
  403   page.appendChild( dpi );
 
  405   QDomElement width = doc.createElement( QStringLiteral( 
"Width" ) );
 
  406   const double pageWidthPdfUnits = std::ceil( details.pageSizeMm.width() / 25.4 * 72 );
 
  407   width.appendChild( doc.createTextNode( 
qgsDoubleToString( pageWidthPdfUnits ) ) );
 
  408   page.appendChild( width );
 
  409   QDomElement height = doc.createElement( QStringLiteral( 
"Height" ) );
 
  410   const double pageHeightPdfUnits = std::ceil( details.pageSizeMm.height() / 25.4 * 72 );
 
  411   height.appendChild( doc.createTextNode( 
qgsDoubleToString( pageHeightPdfUnits ) ) );
 
  412   page.appendChild( height );
 
  419     QDomElement georeferencing = doc.createElement( QStringLiteral( 
"Georeferencing" ) );
 
  420     georeferencing.setAttribute( QStringLiteral( 
"id" ), QStringLiteral( 
"georeferenced_%1" ).arg( i++ ) );
 
  421     georeferencing.setAttribute( QStringLiteral( 
"OGCBestPracticeFormat" ), details.useOgcBestPracticeFormatGeoreferencing ? QStringLiteral( 
"true" ) : QStringLiteral( 
"false" ) );
 
  422     georeferencing.setAttribute( QStringLiteral( 
"ISO32000ExtensionFormat" ), details.useIso32000ExtensionFormatGeoreferencing ? QStringLiteral( 
"true" ) : QStringLiteral( 
"false" ) );
 
  426       QDomElement srs = doc.createElement( QStringLiteral( 
"SRS" ) );
 
  429       if ( !section.
crs.
authid().startsWith( QStringLiteral( 
"user" ), Qt::CaseInsensitive ) )
 
  431         srs.appendChild( doc.createTextNode( section.
crs.
authid() ) );
 
  437       georeferencing.appendChild( srs );
 
  449       QDomElement boundingPolygon = doc.createElement( QStringLiteral( 
"BoundingPolygon" ) );
 
  452       QTransform t = QTransform::fromTranslate( 0, pageHeightPdfUnits ).scale( pageWidthPdfUnits / details.pageSizeMm.width(),
 
  453                      -pageHeightPdfUnits / details.pageSizeMm.height() );
 
  457       boundingPolygon.appendChild( doc.createTextNode( p.
asWkt() ) );
 
  459       georeferencing.appendChild( boundingPolygon );
 
  468       QDomElement boundingBox = doc.createElement( QStringLiteral( 
"BoundingBox" ) );
 
  473       georeferencing.appendChild( boundingBox );
 
  478       QDomElement cp1 = doc.createElement( QStringLiteral( 
"ControlPoint" ) );
 
  479       cp1.setAttribute( QStringLiteral( 
"x" ), 
qgsDoubleToString( point.pagePoint.x() / 25.4 * 72 ) );
 
  480       cp1.setAttribute( QStringLiteral( 
"y" ), 
qgsDoubleToString( ( details.pageSizeMm.height() - point.pagePoint.y() ) / 25.4 * 72 ) );
 
  481       cp1.setAttribute( QStringLiteral( 
"GeoX" ), 
qgsDoubleToString( point.geoPoint.x() ) );
 
  482       cp1.setAttribute( QStringLiteral( 
"GeoY" ), 
qgsDoubleToString( point.geoPoint.y() ) );
 
  483       georeferencing.appendChild( cp1 );
 
  486     page.appendChild( georeferencing );
 
  489   auto createPdfDatasetElement = [&doc]( 
const ComponentLayerDetail & component ) -> QDomElement
 
  491     QDomElement pdfDataset = doc.createElement( QStringLiteral( 
"PDF" ) );
 
  492     pdfDataset.setAttribute( QStringLiteral( 
"dataset" ), component.sourcePdfPath );
 
  493     if ( component.opacity != 1.0 || component.compositionMode != QPainter::CompositionMode_SourceOver )
 
  495       QDomElement blendingElement = doc.createElement( QStringLiteral( 
"Blending" ) );
 
  496       blendingElement.setAttribute( QStringLiteral( 
"opacity" ), component.opacity );
 
  497       blendingElement.setAttribute( QStringLiteral( 
"function" ), compositionModeToString( component.compositionMode ) );
 
  499       pdfDataset.appendChild( blendingElement );
 
  505   QDomElement content = doc.createElement( QStringLiteral( 
"Content" ) );
 
  506   for ( 
const ComponentLayerDetail &component : components )
 
  508     if ( component.mapLayerId.isEmpty() )
 
  510       content.appendChild( createPdfDatasetElement( component ) );
 
  512     else if ( !component.group.isEmpty() )
 
  515       QDomElement ifGroupOn = doc.createElement( QStringLiteral( 
"IfLayerOn" ) );
 
  516       ifGroupOn.setAttribute( QStringLiteral( 
"layerId" ), QStringLiteral( 
"group_%1" ).arg( component.group ) );
 
  517       QDomElement ifLayerOn = doc.createElement( QStringLiteral( 
"IfLayerOn" ) );
 
  518       if ( details.customLayerTreeGroups.contains( component.mapLayerId ) )
 
  519         ifLayerOn.setAttribute( QStringLiteral( 
"layerId" ), customGroupNamesToIds.value( details.customLayerTreeGroups.value( component.mapLayerId ) ) );
 
  520       else if ( component.group.isEmpty() )
 
  521         ifLayerOn.setAttribute( QStringLiteral( 
"layerId" ), component.mapLayerId );
 
  523         ifLayerOn.setAttribute( QStringLiteral( 
"layerId" ), QStringLiteral( 
"%1_%2" ).arg( component.group, component.mapLayerId ) );
 
  525       ifLayerOn.appendChild( createPdfDatasetElement( component ) );
 
  526       ifGroupOn.appendChild( ifLayerOn );
 
  527       content.appendChild( ifGroupOn );
 
  531       QDomElement ifLayerOn = doc.createElement( QStringLiteral( 
"IfLayerOn" ) );
 
  532       if ( details.customLayerTreeGroups.contains( component.mapLayerId ) )
 
  533         ifLayerOn.setAttribute( QStringLiteral( 
"layerId" ), customGroupNamesToIds.value( details.customLayerTreeGroups.value( component.mapLayerId ) ) );
 
  534       else if ( component.group.isEmpty() )
 
  535         ifLayerOn.setAttribute( QStringLiteral( 
"layerId" ), component.mapLayerId );
 
  537         ifLayerOn.setAttribute( QStringLiteral( 
"layerId" ), QStringLiteral( 
"%1_%2" ).arg( component.group, component.mapLayerId ) );
 
  538       ifLayerOn.appendChild( createPdfDatasetElement( component ) );
 
  539       content.appendChild( ifLayerOn );
 
  544   if ( details.includeFeatures )
 
  546     for ( 
const VectorComponentDetail &component : std::as_const( mVectorComponents ) )
 
  548       QDomElement ifLayerOn = doc.createElement( QStringLiteral( 
"IfLayerOn" ) );
 
  549       if ( details.customLayerTreeGroups.contains( component.mapLayerId ) )
 
  550         ifLayerOn.setAttribute( QStringLiteral( 
"layerId" ), customGroupNamesToIds.value( details.customLayerTreeGroups.value( component.mapLayerId ) ) );
 
  551       else if ( component.group.isEmpty() )
 
  552         ifLayerOn.setAttribute( QStringLiteral( 
"layerId" ), component.mapLayerId );
 
  554         ifLayerOn.setAttribute( QStringLiteral( 
"layerId" ), QStringLiteral( 
"%1_%2" ).arg( component.group, component.mapLayerId ) );
 
  555       QDomElement vectorDataset = doc.createElement( QStringLiteral( 
"Vector" ) );
 
  556       vectorDataset.setAttribute( QStringLiteral( 
"dataset" ), component.sourceVectorPath );
 
  557       vectorDataset.setAttribute( QStringLiteral( 
"layer" ), component.sourceVectorLayer );
 
  558       vectorDataset.setAttribute( QStringLiteral( 
"visible" ), QStringLiteral( 
"false" ) );
 
  559       QDomElement logicalStructure = doc.createElement( QStringLiteral( 
"LogicalStructure" ) );
 
  560       logicalStructure.setAttribute( QStringLiteral( 
"displayLayerName" ), component.name );
 
  561       if ( !component.displayAttribute.isEmpty() )
 
  562         logicalStructure.setAttribute( QStringLiteral( 
"fieldToDisplay" ), component.displayAttribute );
 
  563       vectorDataset.appendChild( logicalStructure );
 
  564       ifLayerOn.appendChild( vectorDataset );
 
  565       content.appendChild( ifLayerOn );
 
  569   page.appendChild( content );
 
  570   compositionElem.appendChild( page );
 
  572   doc.appendChild( compositionElem );
 
  575   QTextStream stream( &composition );
 
  576   doc.save( stream, -1 );
 
  581 QString QgsAbstractGeoPdfExporter::compositionModeToString( QPainter::CompositionMode mode )
 
  585     case QPainter::CompositionMode_SourceOver:
 
  586       return QStringLiteral( 
"Normal" );
 
  588     case QPainter::CompositionMode_Multiply:
 
  589       return QStringLiteral( 
"Multiply" );
 
  591     case QPainter::CompositionMode_Screen:
 
  592       return QStringLiteral( 
"Screen" );
 
  594     case QPainter::CompositionMode_Overlay:
 
  595       return QStringLiteral( 
"Overlay" );
 
  597     case QPainter::CompositionMode_Darken:
 
  598       return QStringLiteral( 
"Darken" );
 
  600     case QPainter::CompositionMode_Lighten:
 
  601       return QStringLiteral( 
"Lighten" );
 
  603     case QPainter::CompositionMode_ColorDodge:
 
  604       return QStringLiteral( 
"ColorDodge" );
 
  606     case QPainter::CompositionMode_ColorBurn:
 
  607       return QStringLiteral( 
"ColorBurn" );
 
  609     case QPainter::CompositionMode_HardLight:
 
  610       return QStringLiteral( 
"HardLight" );
 
  612     case QPainter::CompositionMode_SoftLight:
 
  613       return QStringLiteral( 
"SoftLight" );
 
  615     case QPainter::CompositionMode_Difference:
 
  616       return QStringLiteral( 
"Difference" );
 
  618     case  QPainter::CompositionMode_Exclusion:
 
  619       return QStringLiteral( 
"Exclusion" );
 
  625   QgsDebugMsg( QStringLiteral( 
"Unsupported PDF blend mode %1" ).arg( mode ) );
 
  626   return QStringLiteral( 
"Normal" );
 
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).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
QString authid() const
Returns the authority identifier for the CRS.
@ WKT_PREFERRED_GDAL
Preferred format for conversion of CRS to WKT for use with the GDAL library.
QString toWkt(WktVariant variant=WKT1_GDAL, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
Contains information about the context in which a coordinate transform is executed.
bool isEmpty() const override SIP_HOLDGIL
Returns true if the geometry is empty.
QString asWkt(int precision=17) const override
Returns a WKT representation of the geometry.
void transform(const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection d=QgsCoordinateTransform::ForwardTransform, bool transformZ=false) override SIP_THROW(QgsCsException)
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.
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
Options to pass to writeAsVectorFormat()
QString driverName
OGR driver to use.
QgsVectorFileWriter::SymbologyExport symbologyExport
Symbology to export.
static QgsVectorFileWriter * create(const QString &fileName, const QgsFields &fields, QgsWkbTypes::Type 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
bool includeFeatures
true if feature vector information (such as attributes) should be exported.
QgsRectangle pageBoundsMm
Bounds of the georeferenced section on the page, in millimeters.
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 information about a feature rendered inside the PDF.
QgsFeature feature
Rendered feature.
QgsGeometry renderedBounds
Bounds, in PDF units, of rendered feature.