28#include "cpl_string.h"
31#include <QMutexLocker>
32#include <QDomDocument>
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 );
165bool 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;
207QString 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 );
581QString 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.
@ 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, Qgis::TransformDirection d=Qgis::TransformDirection::Forward, 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.