19#include <cpl_string.h>
28#include <QDomDocument>
31#include <QMutexLocker>
39 GDALDriverH hDriverMem = GDALGetDriverByName(
"PDF" );
45 const char *pHavePoppler = GDALGetMetadataItem( hDriverMem,
"HAVE_POPPLER",
nullptr );
46 if ( pHavePoppler && strstr( pHavePoppler,
"YES" ) )
49 const char *pHavePdfium = GDALGetMetadataItem( hDriverMem,
"HAVE_PDFIUM",
nullptr );
50 if ( pHavePdfium && strstr( pHavePdfium,
"YES" ) )
59 GDALDriverH hDriverMem = GDALGetDriverByName(
"PDF" );
62 return QObject::tr(
"No GDAL PDF driver available." );
65 const char *pHavePoppler = GDALGetMetadataItem( hDriverMem,
"HAVE_POPPLER",
nullptr );
66 if ( pHavePoppler && strstr( pHavePoppler,
"YES" ) )
69 const char *pHavePdfium = GDALGetMetadataItem( hDriverMem,
"HAVE_PDFIUM",
nullptr );
70 if ( pHavePdfium && strstr( pHavePdfium,
"YES" ) )
73 return QObject::tr(
"GDAL PDF driver was not built with PDF read support. A build with PDF read support is required for geospatial PDF creation." );
78 QgsDebugError( u
"GDAL PDF creation error: %1 "_s.arg( msg ) );
79 if ( QStringList *errorList =
static_cast< QStringList *
>( CPLGetErrorHandlerUserData() ) )
81 errorList->append( QString( msg ) );
90 const QString composition = createCompositionXml( components, details );
92 if ( composition.isEmpty() )
96 GDALDriverH driver = GDALGetDriverByName(
"PDF" );
99 mErrorMessage = QObject::tr(
"Cannot load GDAL PDF driver" );
104 QFile file( xmlFilePath );
105 if ( file.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
107 QTextStream out( &file );
112 mErrorMessage = QObject::tr(
"Could not create geospatial PDF composition file" );
116 char **papszOptions = CSLSetNameValue(
nullptr,
"COMPOSITION_FILE", xmlFilePath.toUtf8().constData() );
118 QStringList creationErrors;
122 gdal::dataset_unique_ptr outputDataset( GDALCreate( driver, destinationFile.toUtf8().constData(), 0, 0, 0, GDT_Unknown, papszOptions ) );
124 CPLPopErrorHandler();
126 const bool res = outputDataset.get() !=
nullptr;
129 if ( creationErrors.size() == 1 )
131 mErrorMessage = QObject::tr(
"Could not create PDF file: %1" ).arg( creationErrors.at( 0 ) );
133 else if ( !creationErrors.empty() )
135 mErrorMessage = QObject::tr(
"Could not create PDF file. Received errors:\n" );
136 for (
const QString &error : std::as_const( creationErrors ) )
138 mErrorMessage += ( !mErrorMessage.isEmpty() ? u
"\n"_s : QString() ) + error;
144 mErrorMessage = QObject::tr(
"Could not create PDF file, but no error details are available" );
147 outputDataset.reset();
149 CSLDestroy( papszOptions );
163 case QPainter::CompositionMode_SourceOver:
164 case QPainter::CompositionMode_Multiply:
165 case QPainter::CompositionMode_Screen:
166 case QPainter::CompositionMode_Overlay:
167 case QPainter::CompositionMode_Darken:
168 case QPainter::CompositionMode_Lighten:
169 case QPainter::CompositionMode_ColorDodge:
170 case QPainter::CompositionMode_ColorBurn:
171 case QPainter::CompositionMode_HardLight:
172 case QPainter::CompositionMode_SoftLight:
173 case QPainter::CompositionMode_Difference:
174 case QPainter::CompositionMode_Exclusion:
187 QMutexLocker locker( &mMutex );
192 mCollatedFeatures[ group ][ layerId ].append( f );
195bool QgsAbstractGeospatialPdfExporter::saveTemporaryLayers()
197 for (
auto groupIt = mCollatedFeatures.constBegin(); groupIt != mCollatedFeatures.constEnd(); ++groupIt )
199 for (
auto it = groupIt->constBegin(); it != groupIt->constEnd(); ++it )
203 VectorComponentDetail detail = componentDetailForLayerId( it.key() );
204 detail.sourceVectorPath = filePath;
205 detail.group = groupIt.key();
214 if ( writer->hasError() )
216 mErrorMessage = writer->errorMessage();
220 for (
const QgsFeature &feature : features )
222 QgsFeature f = feature;
225 mErrorMessage = writer->errorMessage();
230 detail.sourceVectorLayer = layerName;
231 mVectorComponents << detail;
241 bool initiallyVisible =
false;
243 QString mutuallyExclusiveGroupId;
245 std::vector< std::unique_ptr< TreeNode > > children;
246 TreeNode *parent =
nullptr;
248 void addChild( std::unique_ptr< TreeNode > child )
250 child->parent =
this;
251 children.emplace_back( std::move( child ) );
254 QDomElement toElement( QDomDocument &doc )
const
256 QDomElement layerElement = doc.createElement( u
"Layer"_s );
257 layerElement.setAttribute( u
"id"_s,
id );
258 layerElement.setAttribute( u
"name"_s, name );
259 layerElement.setAttribute( u
"initiallyVisible"_s, initiallyVisible ? u
"true"_s : u
"false"_s );
260 if ( !mutuallyExclusiveGroupId.isEmpty() )
261 layerElement.setAttribute( u
"mutuallyExclusiveGroupId"_s, mutuallyExclusiveGroupId );
263 for (
const auto &child : children )
265 layerElement.appendChild( child->toElement( doc ) );
271 QDomElement createIfLayerOnElement( QDomDocument &doc, QDomElement &contentElement )
const
273 QDomElement element = doc.createElement( u
"IfLayerOn"_s );
274 element.setAttribute( u
"layerId"_s,
id );
275 contentElement.appendChild( element );
279 QDomElement createNestedIfLayerOnElements( QDomDocument &doc, QDomElement &contentElement )
const
281 TreeNode *currentParent = parent;
282 QDomElement finalElement = doc.createElement( u
"IfLayerOn"_s );
283 finalElement.setAttribute( u
"layerId"_s,
id );
285 QDomElement currentElement = finalElement;
286 while ( currentParent )
288 QDomElement ifGroupOn = doc.createElement( u
"IfLayerOn"_s );
289 ifGroupOn.setAttribute( u
"layerId"_s, currentParent->id );
290 ifGroupOn.appendChild( currentElement );
291 currentElement = ifGroupOn;
292 currentParent = currentParent->parent;
294 contentElement.appendChild( currentElement );
300QString QgsAbstractGeospatialPdfExporter::createCompositionXml(
const QList<ComponentLayerDetail> &components,
const ExportDetails &details )
304 QDomElement compositionElem = doc.createElement( u
"PDFComposition"_s );
307 QDomElement metadata = doc.createElement( u
"Metadata"_s );
308 if ( !details.author.isEmpty() )
310 QDomElement author = doc.createElement( u
"Author"_s );
311 author.appendChild( doc.createTextNode( details.author ) );
312 metadata.appendChild( author );
314 if ( !details.producer.isEmpty() )
316 QDomElement producer = doc.createElement( u
"Producer"_s );
317 producer.appendChild( doc.createTextNode( details.producer ) );
318 metadata.appendChild( producer );
320 if ( !details.creator.isEmpty() )
322 QDomElement creator = doc.createElement( u
"Creator"_s );
323 creator.appendChild( doc.createTextNode( details.creator ) );
324 metadata.appendChild( creator );
326 if ( details.creationDateTime.isValid() )
328 QDomElement creationDate = doc.createElement( u
"CreationDate"_s );
329 QString creationDateString = u
"D:%1"_s.arg( details.creationDateTime.toString( u
"yyyyMMddHHmmss"_s ) );
330#if QT_FEATURE_timezone > 0
331 if ( details.creationDateTime.timeZone().isValid() )
333 int offsetFromUtc = details.creationDateTime.timeZone().offsetFromUtc( details.creationDateTime );
334 creationDateString += ( offsetFromUtc >= 0 ) ?
'+' :
'-';
335 offsetFromUtc = std::abs( offsetFromUtc );
336 int offsetHours = offsetFromUtc / 3600;
337 int offsetMins = ( offsetFromUtc % 3600 ) / 60;
338 creationDateString += u
"%1'%2'"_s.arg( offsetHours ).arg( offsetMins );
341 QgsDebugError( u
"Qt is built without timezone support, skipping timezone for pdf export"_s );
343 creationDate.appendChild( doc.createTextNode( creationDateString ) );
344 metadata.appendChild( creationDate );
346 if ( !details.subject.isEmpty() )
348 QDomElement subject = doc.createElement( u
"Subject"_s );
349 subject.appendChild( doc.createTextNode( details.subject ) );
350 metadata.appendChild( subject );
352 if ( !details.title.isEmpty() )
354 QDomElement title = doc.createElement( u
"Title"_s );
355 title.appendChild( doc.createTextNode( details.title ) );
356 metadata.appendChild( title );
358 if ( !details.keywords.empty() )
360 QStringList allKeywords;
361 for (
auto it = details.keywords.constBegin(); it != details.keywords.constEnd(); ++it )
363 allKeywords.append( u
"%1: %2"_s.arg( it.key(), it.value().join(
',' ) ) );
365 QDomElement keywords = doc.createElement( u
"Keywords"_s );
366 keywords.appendChild( doc.createTextNode( allKeywords.join(
';' ) ) );
367 metadata.appendChild( keywords );
369 compositionElem.appendChild( metadata );
371 QSet< QString > createdLayerIds;
372 std::vector< std::unique_ptr< TreeNode > > rootGroups;
373 std::vector< std::unique_ptr< TreeNode > > rootLayers;
374 QMap< QString, TreeNode * > groupNameMap;
376 QStringList layerTreeGroupOrder = details.layerTreeGroupOrder;
380 for (
auto it = details.customLayerTreeGroups.constBegin(); it != details.customLayerTreeGroups.constEnd(); ++it )
382 if ( layerTreeGroupOrder.contains( it.value() ) )
384 layerTreeGroupOrder.append( it.value() );
388 if ( details.includeFeatures )
392 if ( !component.group.isEmpty() && !layerTreeGroupOrder.contains( component.group ) )
394 layerTreeGroupOrder.append( component.group );
402 if ( !component.group.isEmpty() && !layerTreeGroupOrder.contains( component.group ) )
404 layerTreeGroupOrder.append( component.group );
408 QMap< QString, TreeNode * > groupNameToTreeNode;
409 QMap< QString, TreeNode * > layerIdToTreeNode;
411 auto createGroup = [&details, &groupNameToTreeNode](
const QString & groupName ) -> std::unique_ptr< TreeNode >
413 auto group = std::make_unique< TreeNode >();
414 const QString
id = QUuid::createUuid().toString();
416 groupNameToTreeNode[ groupName ] = group.get();
418 group->name = groupName;
419 group->initiallyVisible =
true;
420 if ( details.mutuallyExclusiveGroups.contains( groupName ) )
421 group->mutuallyExclusiveGroupId = u
"__mutually_exclusive_groups__"_s;
425 if ( details.includeFeatures )
429 const QString destinationGroup = details.customLayerTreeGroups.value( component.mapLayerId, component.group );
431 auto layer = std::make_unique< TreeNode >();
432 layer->id = destinationGroup.isEmpty() ? component.mapLayerId : u
"%1_%2"_s.arg( destinationGroup, component.mapLayerId );
433 layer->name = details.layerIdToPdfLayerTreeNameMap.contains( component.mapLayerId ) ? details.layerIdToPdfLayerTreeNameMap.value( component.mapLayerId ) : component.name;
434 layer->initiallyVisible = details.initialLayerVisibility.value( component.mapLayerId,
true );
435 layer->mapLayerId = component.mapLayerId;
437 layerIdToTreeNode.insert( component.mapLayerId, layer.get() );
438 if ( !destinationGroup.isEmpty() )
440 if ( TreeNode *groupNode = groupNameMap.value( destinationGroup ) )
442 groupNode->addChild( std::move( layer ) );
446 std::unique_ptr< TreeNode > group = createGroup( destinationGroup );
447 group->addChild( std::move( layer ) );
448 groupNameMap.insert( destinationGroup, group.get() );
449 rootGroups.emplace_back( std::move( group ) );
454 rootLayers.emplace_back( std::move( layer ) );
457 createdLayerIds.insert( component.mapLayerId );
467 if ( !component.mapLayerId.isEmpty() && createdLayerIds.contains( component.mapLayerId ) )
470 const QString destinationGroup = details.customLayerTreeGroups.value( component.mapLayerId, component.group );
471 if ( destinationGroup.isEmpty() && component.mapLayerId.isEmpty() )
474 std::unique_ptr< TreeNode > mapLayerNode;
475 if ( !component.mapLayerId.isEmpty() )
477 mapLayerNode = std::make_unique< TreeNode >();
478 mapLayerNode->id = destinationGroup.isEmpty() ? component.mapLayerId : u
"%1_%2"_s.arg( destinationGroup, component.mapLayerId );
479 mapLayerNode->name = details.layerIdToPdfLayerTreeNameMap.value( component.mapLayerId, component.name );
480 mapLayerNode->initiallyVisible = details.initialLayerVisibility.value( component.mapLayerId,
true );
481 mapLayerNode->mapLayerId = component.mapLayerId;
483 layerIdToTreeNode.insert( component.mapLayerId, mapLayerNode.get() );
486 if ( !destinationGroup.isEmpty() )
488 if ( TreeNode *groupNode = groupNameMap.value( destinationGroup ) )
491 groupNode->addChild( std::move( mapLayerNode ) );
495 std::unique_ptr< TreeNode > group = createGroup( destinationGroup );
497 group->addChild( std::move( mapLayerNode ) );
498 groupNameMap.insert( destinationGroup, group.get() );
499 rootGroups.emplace_back( std::move( group ) );
505 rootLayers.emplace_back( std::move( mapLayerNode ) );
508 if ( !component.mapLayerId.isEmpty() )
510 createdLayerIds.insert( component.mapLayerId );
515 QDomElement page = doc.createElement( u
"Page"_s );
516 QDomElement dpi = doc.createElement( u
"DPI"_s );
519 page.appendChild( dpi );
521 QDomElement width = doc.createElement( u
"Width"_s );
522 const double pageWidthPdfUnits = std::ceil( details.pageSizeMm.width() / 25.4 * 72 );
523 width.appendChild( doc.createTextNode(
qgsDoubleToString( pageWidthPdfUnits ) ) );
524 page.appendChild( width );
525 QDomElement height = doc.createElement( u
"Height"_s );
526 const double pageHeightPdfUnits = std::ceil( details.pageSizeMm.height() / 25.4 * 72 );
527 height.appendChild( doc.createTextNode(
qgsDoubleToString( pageHeightPdfUnits ) ) );
528 page.appendChild( height );
532 for (
const QgsAbstractGeospatialPdfExporter::GeoReferencedSection §ion : details.georeferencedSections )
534 QDomElement georeferencing = doc.createElement( u
"Georeferencing"_s );
535 georeferencing.setAttribute( u
"id"_s, u
"georeferenced_%1"_s.arg( i++ ) );
536 georeferencing.setAttribute( u
"ISO32000ExtensionFormat"_s, details.useIso32000ExtensionFormatGeoreferencing ? u
"true"_s : u
"false"_s );
540 QDomElement srs = doc.createElement( u
"SRS"_s );
543 if ( !section.
crs.
authid().isEmpty() && !section.
crs.
authid().startsWith( u
"user"_s, Qt::CaseInsensitive ) )
545 srs.appendChild( doc.createTextNode( section.
crs.
authid() ) );
551 georeferencing.appendChild( srs );
563 QDomElement boundingPolygon = doc.createElement( u
"BoundingPolygon"_s );
566 QTransform t = QTransform::fromTranslate( 0, pageHeightPdfUnits ).scale( pageWidthPdfUnits / details.pageSizeMm.width(),
567 -pageHeightPdfUnits / details.pageSizeMm.height() );
571 boundingPolygon.appendChild( doc.createTextNode( p.
asWkt() ) );
573 georeferencing.appendChild( boundingPolygon );
582 QDomElement boundingBox = doc.createElement( u
"BoundingBox"_s );
587 georeferencing.appendChild( boundingBox );
592 QDomElement cp1 = doc.createElement( u
"ControlPoint"_s );
593 cp1.setAttribute( u
"x"_s,
qgsDoubleToString( point.pagePoint.x() / 25.4 * 72 ) );
594 cp1.setAttribute( u
"y"_s,
qgsDoubleToString( ( details.pageSizeMm.height() - point.pagePoint.y() ) / 25.4 * 72 ) );
597 georeferencing.appendChild( cp1 );
600 page.appendChild( georeferencing );
605 QDomElement pdfDataset = doc.createElement( u
"PDF"_s );
606 pdfDataset.setAttribute( u
"dataset"_s, component.sourcePdfPath );
607 if ( component.opacity != 1.0 || component.compositionMode != QPainter::CompositionMode_SourceOver )
609 QDomElement blendingElement = doc.createElement( u
"Blending"_s );
610 blendingElement.setAttribute( u
"opacity"_s, component.opacity );
611 blendingElement.setAttribute( u
"function"_s, compositionModeToString( component.compositionMode ) );
613 pdfDataset.appendChild( blendingElement );
619 QDomElement content = doc.createElement( u
"Content"_s );
622 if ( component.mapLayerId.isEmpty() && component.group.isEmpty() )
624 content.appendChild( createPdfDatasetElement( component ) );
626 else if ( !component.mapLayerId.isEmpty() )
628 if ( TreeNode *treeNode = layerIdToTreeNode.value( component.mapLayerId ) )
630 QDomElement ifLayerOnElement = treeNode->createNestedIfLayerOnElements( doc, content );
631 ifLayerOnElement.appendChild( createPdfDatasetElement( component ) );
634 else if ( TreeNode *groupNode = groupNameToTreeNode.value( component.group ) )
636 QDomElement ifGroupOn = groupNode->createIfLayerOnElement( doc, content );
637 ifGroupOn.appendChild( createPdfDatasetElement( component ) );
642 if ( details.includeFeatures )
646 if ( TreeNode *treeNode = layerIdToTreeNode.value( component.mapLayerId ) )
648 QDomElement ifLayerOnElement = treeNode->createNestedIfLayerOnElements( doc, content );
650 QDomElement vectorDataset = doc.createElement( u
"Vector"_s );
651 vectorDataset.setAttribute( u
"dataset"_s, component.sourceVectorPath );
652 vectorDataset.setAttribute( u
"layer"_s, component.sourceVectorLayer );
653 vectorDataset.setAttribute( u
"visible"_s, u
"false"_s );
654 QDomElement logicalStructure = doc.createElement( u
"LogicalStructure"_s );
655 logicalStructure.setAttribute( u
"displayLayerName"_s, component.name );
656 if ( !component.displayAttribute.isEmpty() )
657 logicalStructure.setAttribute( u
"fieldToDisplay"_s, component.displayAttribute );
658 vectorDataset.appendChild( logicalStructure );
659 ifLayerOnElement.appendChild( vectorDataset );
664 page.appendChild( content );
667 QDomElement
layerTree = doc.createElement( u
"LayerTree"_s );
673 std::sort( rootGroups.begin(), rootGroups.end(), [&layerTreeGroupOrder](
const std::unique_ptr< TreeNode > &a,
const std::unique_ptr< TreeNode > &b ) ->
bool
675 return layerTreeGroupOrder.indexOf( a->name ) < layerTreeGroupOrder.indexOf( b->name );
678 bool haveFoundMutuallyExclusiveGroup =
false;
679 for (
const auto &node : std::as_const( rootGroups ) )
681 if ( !node->mutuallyExclusiveGroupId.isEmpty() )
684 node->initiallyVisible = !haveFoundMutuallyExclusiveGroup;
685 haveFoundMutuallyExclusiveGroup =
true;
687 layerTree.appendChild( node->toElement( doc ) );
691 layerTreeGroupOrder.erase( std::remove_if( layerTreeGroupOrder.begin(), layerTreeGroupOrder.end(), [&details](
const QString & group )
693 return details.customLayerTreeGroups.key( group ).isEmpty();
694 } ), layerTreeGroupOrder.end() );
698 std::sort( rootLayers.begin(), rootLayers.end(), [&details](
const std::unique_ptr< TreeNode > &a,
const std::unique_ptr< TreeNode > &b ) ->
bool
700 const int indexA = details.layerOrder.indexOf( a->mapLayerId );
701 const int indexB = details.layerOrder.indexOf( b->mapLayerId );
703 if ( indexA >= 0 && indexB >= 0 )
704 return indexA < indexB;
705 else if ( indexA >= 0 )
707 else if ( indexB >= 0 )
710 return a->name.localeAwareCompare( b->name ) < 0;
713 for (
const auto &node : std::as_const( rootLayers ) )
715 layerTree.appendChild( node->toElement( doc ) );
718 compositionElem.appendChild( layerTree );
719 compositionElem.appendChild( page );
721 doc.appendChild( compositionElem );
724 QTextStream stream( &composition );
725 doc.save( stream, -1 );
730QString QgsAbstractGeospatialPdfExporter::compositionModeToString( QPainter::CompositionMode mode )
734 case QPainter::CompositionMode_SourceOver:
737 case QPainter::CompositionMode_Multiply:
738 return u
"Multiply"_s;
740 case QPainter::CompositionMode_Screen:
743 case QPainter::CompositionMode_Overlay:
746 case QPainter::CompositionMode_Darken:
749 case QPainter::CompositionMode_Lighten:
752 case QPainter::CompositionMode_ColorDodge:
753 return u
"ColorDodge"_s;
755 case QPainter::CompositionMode_ColorBurn:
756 return u
"ColorBurn"_s;
758 case QPainter::CompositionMode_HardLight:
759 return u
"HardLight"_s;
761 case QPainter::CompositionMode_SoftLight:
762 return u
"SoftLight"_s;
764 case QPainter::CompositionMode_Difference:
765 return u
"Difference"_s;
767 case QPainter::CompositionMode_Exclusion:
768 return u
"Exclusion"_s;
774 QgsDebugError( u
"Unsupported PDF blend mode %1"_s.arg( mode ) );
@ PreferredGdal
Preferred format for conversion of CRS to WKT for use with the GDAL library.
@ NoSymbology
Export only data.
static bool compositionModeSupported(QPainter::CompositionMode mode)
Returns true if the specified composition mode is supported for layers during Geospatial PDF exports.
static QString geospatialPDFAvailabilityExplanation()
Returns a user-friendly, translated string explaining why Geospatial PDF export support is not availa...
bool finalize(const QList< QgsAbstractGeospatialPdfExporter::ComponentLayerDetail > &components, const QString &destinationFile, const ExportDetails &details)
To be called after the rendering operation is complete.
void pushRenderedFeature(const QString &layerId, const QgsAbstractGeospatialPdfExporter::RenderedFeature &feature, const QString &group=QString())
Called multiple times during the rendering operation, whenever a feature associated with the specifie...
static bool geospatialPDFCreationAvailable()
Returns true if the current QGIS build is capable of Geospatial PDF support.
QString generateTemporaryFilepath(const QString &filename) const
Returns a file path to use for temporary files required for Geospatial PDF creation.
Represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
QString toWkt(Qgis::CrsWktVariant variant=Qgis::CrsWktVariant::Wkt1Gdal, 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
Returns true if the geometry is empty.
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 QgsVectorFileWriter::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.
void CPL_STDCALL collectErrors(CPLErr, int, const char *msg)
QList< QgsFeature > QgsFeatureList
#define QgsDebugMsgLevel(str, level)
#define QgsDebugError(str)
Contains details of a particular input component to be used during PDF composition.
Contains details of a control point used during georeferencing Geospatial PDF outputs.
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.
QList< QgsAbstractGeospatialPdfExporter::ControlPoint > controlPoints
List of control points corresponding to this georeferenced section.
QgsPolygon pageBoundsPolygon
Bounds of the georeferenced section on the page, in millimeters, as a free-form polygon.
Contains information about a feature rendered inside the PDF.
QgsGeometry renderedBounds
Bounds, in PDF units, of rendered feature.
QgsFeature feature
Rendered feature.
Contains information relating to a single PDF layer in the Geospatial PDF export.