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." );
77 QgsDebugError( QStringLiteral(
"GDAL PDF creation error: %1 " ).arg( msg ) );
78 if ( QStringList *errorList =
static_cast< QStringList *
>( CPLGetErrorHandlerUserData() ) )
80 errorList->append( QString( msg ) );
89 const QString composition = createCompositionXml( components, details );
91 if ( composition.isEmpty() )
95 GDALDriverH driver = GDALGetDriverByName(
"PDF" );
98 mErrorMessage = QObject::tr(
"Cannot load GDAL PDF driver" );
103 QFile file( xmlFilePath );
104 if ( file.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
106 QTextStream out( &file );
107#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
108 out.setCodec(
"UTF-8" );
114 mErrorMessage = QObject::tr(
"Could not create GeoPDF composition file" );
118 char **papszOptions = CSLSetNameValue(
nullptr,
"COMPOSITION_FILE", xmlFilePath.toUtf8().constData() );
120 QStringList creationErrors;
124 gdal::dataset_unique_ptr outputDataset( GDALCreate( driver, destinationFile.toUtf8().constData(), 0, 0, 0, GDT_Unknown, papszOptions ) );
126 CPLPopErrorHandler();
127 const bool res = outputDataset.get() !=
nullptr;
130 if ( creationErrors.size() == 1 )
132 mErrorMessage = QObject::tr(
"Could not create PDF file: %1" ).arg( creationErrors.at( 0 ) );
134 else if ( !creationErrors.empty() )
136 mErrorMessage = QObject::tr(
"Could not create PDF file. Received errors:\n" );
137 for (
const QString &error : std::as_const( creationErrors ) )
139 mErrorMessage += ( !mErrorMessage.isEmpty() ? QStringLiteral(
"\n" ) : QString() ) + error;
145 mErrorMessage = QObject::tr(
"Could not create PDF file, but no error details are available" );
148 outputDataset.reset();
150 CSLDestroy( papszOptions );
164 case QPainter::CompositionMode_SourceOver:
165 case QPainter::CompositionMode_Multiply:
166 case QPainter::CompositionMode_Screen:
167 case QPainter::CompositionMode_Overlay:
168 case QPainter::CompositionMode_Darken:
169 case QPainter::CompositionMode_Lighten:
170 case QPainter::CompositionMode_ColorDodge:
171 case QPainter::CompositionMode_ColorBurn:
172 case QPainter::CompositionMode_HardLight:
173 case QPainter::CompositionMode_SoftLight:
174 case QPainter::CompositionMode_Difference:
175 case QPainter::CompositionMode_Exclusion:
188 QMutexLocker locker( &mMutex );
193 mCollatedFeatures[ group ][ layerId ].append( f );
196bool QgsAbstractGeoPdfExporter::saveTemporaryLayers()
198 for (
auto groupIt = mCollatedFeatures.constBegin(); groupIt != mCollatedFeatures.constEnd(); ++groupIt )
200 for (
auto it = groupIt->constBegin(); it != groupIt->constEnd(); ++it )
204 VectorComponentDetail detail = componentDetailForLayerId( it.key() );
205 detail.sourceVectorPath = filePath;
206 detail.group = groupIt.key();
212 saveOptions.
driverName = QStringLiteral(
"GPKG" );
215 if ( writer->hasError() )
217 mErrorMessage = writer->errorMessage();
226 mErrorMessage = writer->errorMessage();
231 detail.sourceVectorLayer = layerName;
232 mVectorComponents << detail;
238QString QgsAbstractGeoPdfExporter::createCompositionXml(
const QList<ComponentLayerDetail> &components,
const ExportDetails &details )
242 QDomElement compositionElem = doc.createElement( QStringLiteral(
"PDFComposition" ) );
245 QDomElement metadata = doc.createElement( QStringLiteral(
"Metadata" ) );
246 if ( !details.author.isEmpty() )
248 QDomElement author = doc.createElement( QStringLiteral(
"Author" ) );
249 author.appendChild( doc.createTextNode( details.author ) );
250 metadata.appendChild( author );
252 if ( !details.producer.isEmpty() )
254 QDomElement producer = doc.createElement( QStringLiteral(
"Producer" ) );
255 producer.appendChild( doc.createTextNode( details.producer ) );
256 metadata.appendChild( producer );
258 if ( !details.creator.isEmpty() )
260 QDomElement creator = doc.createElement( QStringLiteral(
"Creator" ) );
261 creator.appendChild( doc.createTextNode( details.creator ) );
262 metadata.appendChild( creator );
264 if ( details.creationDateTime.isValid() )
266 QDomElement creationDate = doc.createElement( QStringLiteral(
"CreationDate" ) );
267 QString creationDateString = QStringLiteral(
"D:%1" ).arg( details.creationDateTime.toString( QStringLiteral(
"yyyyMMddHHmmss" ) ) );
268 if ( details.creationDateTime.timeZone().isValid() )
270 int offsetFromUtc = details.creationDateTime.timeZone().offsetFromUtc( details.creationDateTime );
271 creationDateString += ( offsetFromUtc >= 0 ) ?
'+' :
'-';
272 offsetFromUtc = std::abs( offsetFromUtc );
273 int offsetHours = offsetFromUtc / 3600;
274 int offsetMins = ( offsetFromUtc % 3600 ) / 60;
275 creationDateString += QStringLiteral(
"%1'%2'" ).arg( offsetHours ).arg( offsetMins );
277 creationDate.appendChild( doc.createTextNode( creationDateString ) );
278 metadata.appendChild( creationDate );
280 if ( !details.subject.isEmpty() )
282 QDomElement subject = doc.createElement( QStringLiteral(
"Subject" ) );
283 subject.appendChild( doc.createTextNode( details.subject ) );
284 metadata.appendChild( subject );
286 if ( !details.title.isEmpty() )
288 QDomElement title = doc.createElement( QStringLiteral(
"Title" ) );
289 title.appendChild( doc.createTextNode( details.title ) );
290 metadata.appendChild( title );
292 if ( !details.keywords.empty() )
294 QStringList allKeywords;
295 for (
auto it = details.keywords.constBegin(); it != details.keywords.constEnd(); ++it )
297 allKeywords.append( QStringLiteral(
"%1: %2" ).arg( it.key(), it.value().join(
',' ) ) );
299 QDomElement keywords = doc.createElement( QStringLiteral(
"Keywords" ) );
300 keywords.appendChild( doc.createTextNode( allKeywords.join(
';' ) ) );
301 metadata.appendChild( keywords );
303 compositionElem.appendChild( metadata );
305 QMap< QString, QSet< QString > > createdLayerIds;
306 QMap< QString, QDomElement > groupLayerMap;
308 QMultiMap< QString, QDomElement > pendingLayerTreeElements;
310 if ( details.includeFeatures )
312 for (
const VectorComponentDetail &component : std::as_const( mVectorComponents ) )
314 if ( details.customLayerTreeGroups.contains( component.mapLayerId ) )
317 QDomElement layer = doc.createElement( QStringLiteral(
"Layer" ) );
318 layer.setAttribute( QStringLiteral(
"id" ), component.group.isEmpty() ? component.mapLayerId : QStringLiteral(
"%1_%2" ).arg( component.group, component.mapLayerId ) );
319 layer.setAttribute( QStringLiteral(
"name" ), details.layerIdToPdfLayerTreeNameMap.contains( component.mapLayerId ) ? details.layerIdToPdfLayerTreeNameMap.value( component.mapLayerId ) : component.name );
320 layer.setAttribute( QStringLiteral(
"initiallyVisible" ), details.initialLayerVisibility.value( component.mapLayerId,
true ) ? QStringLiteral(
"true" ) : QStringLiteral(
"false" ) );
322 if ( !component.group.isEmpty() )
324 if ( groupLayerMap.contains( component.group ) )
326 groupLayerMap[ component.group ].appendChild( layer );
330 QDomElement group = doc.createElement( QStringLiteral(
"Layer" ) );
331 group.setAttribute( QStringLiteral(
"id" ), QStringLiteral(
"group_%1" ).arg( component.group ) );
332 group.setAttribute( QStringLiteral(
"name" ), component.group );
333 group.setAttribute( QStringLiteral(
"initiallyVisible" ), groupLayerMap.empty() ? QStringLiteral(
"true" ) : QStringLiteral(
"false" ) );
334 group.setAttribute( QStringLiteral(
"mutuallyExclusiveGroupId" ), QStringLiteral(
"__mutually_exclusive_groups__" ) );
335 pendingLayerTreeElements.insert( component.mapLayerId, group );
336 group.appendChild( layer );
337 groupLayerMap[ component.group ] = group;
342 pendingLayerTreeElements.insert( component.mapLayerId, layer );
345 createdLayerIds[ component.group ].insert( component.mapLayerId );
349 for (
const ComponentLayerDetail &component : components )
351 if ( component.mapLayerId.isEmpty() || createdLayerIds.value( component.group ).contains( component.mapLayerId ) )
354 if ( details.customLayerTreeGroups.contains( component.mapLayerId ) )
357 QDomElement layer = doc.createElement( QStringLiteral(
"Layer" ) );
358 layer.setAttribute( QStringLiteral(
"id" ), component.group.isEmpty() ? component.mapLayerId : QStringLiteral(
"%1_%2" ).arg( component.group, component.mapLayerId ) );
359 layer.setAttribute( QStringLiteral(
"name" ), details.layerIdToPdfLayerTreeNameMap.contains( component.mapLayerId ) ? details.layerIdToPdfLayerTreeNameMap.value( component.mapLayerId ) : component.name );
360 layer.setAttribute( QStringLiteral(
"initiallyVisible" ), details.initialLayerVisibility.value( component.mapLayerId,
true ) ? QStringLiteral(
"true" ) : QStringLiteral(
"false" ) );
362 if ( !component.group.isEmpty() )
364 if ( groupLayerMap.contains( component.group ) )
366 groupLayerMap[ component.group ].appendChild( layer );
370 QDomElement group = doc.createElement( QStringLiteral(
"Layer" ) );
371 group.setAttribute( QStringLiteral(
"id" ), QStringLiteral(
"group_%1" ).arg( component.group ) );
372 group.setAttribute( QStringLiteral(
"name" ), component.group );
373 group.setAttribute( QStringLiteral(
"initiallyVisible" ), groupLayerMap.empty() ? QStringLiteral(
"true" ) : QStringLiteral(
"false" ) );
374 group.setAttribute( QStringLiteral(
"mutuallyExclusiveGroupId" ), QStringLiteral(
"__mutually_exclusive_groups__" ) );
375 pendingLayerTreeElements.insert( component.mapLayerId, group );
376 group.appendChild( layer );
377 groupLayerMap[ component.group ] = group;
382 pendingLayerTreeElements.insert( component.mapLayerId, layer );
385 createdLayerIds[ component.group ].insert( component.mapLayerId );
389 QDomElement
layerTree = doc.createElement( QStringLiteral(
"LayerTree" ) );
393 QStringList layerTreeGroupOrder = details.layerTreeGroupOrder;
396 for (
auto it = details.customLayerTreeGroups.constBegin(); it != details.customLayerTreeGroups.constEnd(); ++it )
398 if ( layerTreeGroupOrder.contains( it.value() ) )
400 layerTreeGroupOrder.append( it.value() );
403 layerTreeGroupOrder.erase( std::remove_if( layerTreeGroupOrder.begin(), layerTreeGroupOrder.end(), [&details](
const QString & group )
405 return details.customLayerTreeGroups.key( group ).isEmpty();
406 } ), layerTreeGroupOrder.end() );
408 QMap< QString, QString > customGroupNamesToIds;
409 for (
const QString &group : std::as_const( layerTreeGroupOrder ) )
411 if ( customGroupNamesToIds.contains( group ) )
414 QDomElement layer = doc.createElement( QStringLiteral(
"Layer" ) );
415 const QString
id = QUuid::createUuid().toString();
416 customGroupNamesToIds[ group ] = id;
417 layer.setAttribute( QStringLiteral(
"id" ),
id );
418 layer.setAttribute( QStringLiteral(
"name" ), group );
419 layer.setAttribute( QStringLiteral(
"initiallyVisible" ), QStringLiteral(
"true" ) );
424 for (
const QString &layerId : details.layerOrder )
426 const QList< QDomElement> elements = pendingLayerTreeElements.values( layerId );
427 for (
const QDomElement &element : elements )
431 for (
auto it = pendingLayerTreeElements.constBegin(); it != pendingLayerTreeElements.constEnd(); ++it )
433 if ( details.layerOrder.contains( it.key() ) )
442 compositionElem.appendChild( layerTree );
445 QDomElement page = doc.createElement( QStringLiteral(
"Page" ) );
446 QDomElement dpi = doc.createElement( QStringLiteral(
"DPI" ) );
449 page.appendChild( dpi );
451 QDomElement width = doc.createElement( QStringLiteral(
"Width" ) );
452 const double pageWidthPdfUnits = std::ceil( details.pageSizeMm.width() / 25.4 * 72 );
453 width.appendChild( doc.createTextNode(
qgsDoubleToString( pageWidthPdfUnits ) ) );
454 page.appendChild( width );
455 QDomElement height = doc.createElement( QStringLiteral(
"Height" ) );
456 const double pageHeightPdfUnits = std::ceil( details.pageSizeMm.height() / 25.4 * 72 );
457 height.appendChild( doc.createTextNode(
qgsDoubleToString( pageHeightPdfUnits ) ) );
458 page.appendChild( height );
465 QDomElement georeferencing = doc.createElement( QStringLiteral(
"Georeferencing" ) );
466 georeferencing.setAttribute( QStringLiteral(
"id" ), QStringLiteral(
"georeferenced_%1" ).arg( i++ ) );
467 georeferencing.setAttribute( QStringLiteral(
"OGCBestPracticeFormat" ), details.useOgcBestPracticeFormatGeoreferencing ? QStringLiteral(
"true" ) : QStringLiteral(
"false" ) );
468 georeferencing.setAttribute( QStringLiteral(
"ISO32000ExtensionFormat" ), details.useIso32000ExtensionFormatGeoreferencing ? QStringLiteral(
"true" ) : QStringLiteral(
"false" ) );
470 if ( section.crs.isValid() )
472 QDomElement srs = doc.createElement( QStringLiteral(
"SRS" ) );
475 if ( !section.crs.authid().isEmpty() && !section.crs.authid().startsWith( QStringLiteral(
"user" ), Qt::CaseInsensitive ) )
477 srs.appendChild( doc.createTextNode( section.crs.authid() ) );
483 georeferencing.appendChild( srs );
486 if ( !section.pageBoundsPolygon.isEmpty() )
495 QDomElement boundingPolygon = doc.createElement( QStringLiteral(
"BoundingPolygon" ) );
498 QTransform t = QTransform::fromTranslate( 0, pageHeightPdfUnits ).scale( pageWidthPdfUnits / details.pageSizeMm.width(),
499 -pageHeightPdfUnits / details.pageSizeMm.height() );
503 boundingPolygon.appendChild( doc.createTextNode( p.
asWkt() ) );
505 georeferencing.appendChild( boundingPolygon );
514 QDomElement boundingBox = doc.createElement( QStringLiteral(
"BoundingBox" ) );
515 boundingBox.setAttribute( QStringLiteral(
"x1" ),
qgsDoubleToString( section.pageBoundsMm.xMinimum() / 25.4 * 72 ) );
516 boundingBox.setAttribute( QStringLiteral(
"y1" ),
qgsDoubleToString( section.pageBoundsMm.yMinimum() / 25.4 * 72 ) );
517 boundingBox.setAttribute( QStringLiteral(
"x2" ),
qgsDoubleToString( section.pageBoundsMm.xMaximum() / 25.4 * 72 ) );
518 boundingBox.setAttribute( QStringLiteral(
"y2" ),
qgsDoubleToString( section.pageBoundsMm.yMaximum() / 25.4 * 72 ) );
519 georeferencing.appendChild( boundingBox );
522 for (
const ControlPoint &point : section.controlPoints )
524 QDomElement cp1 = doc.createElement( QStringLiteral(
"ControlPoint" ) );
525 cp1.setAttribute( QStringLiteral(
"x" ),
qgsDoubleToString( point.pagePoint.x() / 25.4 * 72 ) );
526 cp1.setAttribute( QStringLiteral(
"y" ),
qgsDoubleToString( ( details.pageSizeMm.height() - point.pagePoint.y() ) / 25.4 * 72 ) );
527 cp1.setAttribute( QStringLiteral(
"GeoX" ),
qgsDoubleToString( point.geoPoint.x() ) );
528 cp1.setAttribute( QStringLiteral(
"GeoY" ),
qgsDoubleToString( point.geoPoint.y() ) );
529 georeferencing.appendChild( cp1 );
532 page.appendChild( georeferencing );
535 auto createPdfDatasetElement = [&doc](
const ComponentLayerDetail & component ) -> QDomElement
537 QDomElement pdfDataset = doc.createElement( QStringLiteral(
"PDF" ) );
538 pdfDataset.setAttribute( QStringLiteral(
"dataset" ), component.sourcePdfPath );
539 if ( component.opacity != 1.0 || component.compositionMode != QPainter::CompositionMode_SourceOver )
541 QDomElement blendingElement = doc.createElement( QStringLiteral(
"Blending" ) );
542 blendingElement.setAttribute( QStringLiteral(
"opacity" ), component.opacity );
543 blendingElement.setAttribute( QStringLiteral(
"function" ), compositionModeToString( component.compositionMode ) );
545 pdfDataset.appendChild( blendingElement );
551 QDomElement content = doc.createElement( QStringLiteral(
"Content" ) );
552 for (
const ComponentLayerDetail &component : components )
554 if ( component.mapLayerId.isEmpty() )
556 content.appendChild( createPdfDatasetElement( component ) );
558 else if ( !component.group.isEmpty() )
561 QDomElement ifGroupOn = doc.createElement( QStringLiteral(
"IfLayerOn" ) );
562 ifGroupOn.setAttribute( QStringLiteral(
"layerId" ), QStringLiteral(
"group_%1" ).arg( component.group ) );
563 QDomElement ifLayerOn = doc.createElement( QStringLiteral(
"IfLayerOn" ) );
564 if ( details.customLayerTreeGroups.contains( component.mapLayerId ) )
565 ifLayerOn.setAttribute( QStringLiteral(
"layerId" ), customGroupNamesToIds.value( details.customLayerTreeGroups.value( component.mapLayerId ) ) );
566 else if ( component.group.isEmpty() )
567 ifLayerOn.setAttribute( QStringLiteral(
"layerId" ), component.mapLayerId );
569 ifLayerOn.setAttribute( QStringLiteral(
"layerId" ), QStringLiteral(
"%1_%2" ).arg( component.group, component.mapLayerId ) );
571 ifLayerOn.appendChild( createPdfDatasetElement( component ) );
572 ifGroupOn.appendChild( ifLayerOn );
573 content.appendChild( ifGroupOn );
577 QDomElement ifLayerOn = doc.createElement( QStringLiteral(
"IfLayerOn" ) );
578 if ( details.customLayerTreeGroups.contains( component.mapLayerId ) )
579 ifLayerOn.setAttribute( QStringLiteral(
"layerId" ), customGroupNamesToIds.value( details.customLayerTreeGroups.value( component.mapLayerId ) ) );
580 else if ( component.group.isEmpty() )
581 ifLayerOn.setAttribute( QStringLiteral(
"layerId" ), component.mapLayerId );
583 ifLayerOn.setAttribute( QStringLiteral(
"layerId" ), QStringLiteral(
"%1_%2" ).arg( component.group, component.mapLayerId ) );
584 ifLayerOn.appendChild( createPdfDatasetElement( component ) );
585 content.appendChild( ifLayerOn );
590 if ( details.includeFeatures )
592 for (
const VectorComponentDetail &component : std::as_const( mVectorComponents ) )
594 QDomElement ifLayerOn = doc.createElement( QStringLiteral(
"IfLayerOn" ) );
595 if ( details.customLayerTreeGroups.contains( component.mapLayerId ) )
596 ifLayerOn.setAttribute( QStringLiteral(
"layerId" ), customGroupNamesToIds.value( details.customLayerTreeGroups.value( component.mapLayerId ) ) );
597 else if ( component.group.isEmpty() )
598 ifLayerOn.setAttribute( QStringLiteral(
"layerId" ), component.mapLayerId );
600 ifLayerOn.setAttribute( QStringLiteral(
"layerId" ), QStringLiteral(
"%1_%2" ).arg( component.group, component.mapLayerId ) );
601 QDomElement vectorDataset = doc.createElement( QStringLiteral(
"Vector" ) );
602 vectorDataset.setAttribute( QStringLiteral(
"dataset" ), component.sourceVectorPath );
603 vectorDataset.setAttribute( QStringLiteral(
"layer" ), component.sourceVectorLayer );
604 vectorDataset.setAttribute( QStringLiteral(
"visible" ), QStringLiteral(
"false" ) );
605 QDomElement logicalStructure = doc.createElement( QStringLiteral(
"LogicalStructure" ) );
606 logicalStructure.setAttribute( QStringLiteral(
"displayLayerName" ), component.name );
607 if ( !component.displayAttribute.isEmpty() )
608 logicalStructure.setAttribute( QStringLiteral(
"fieldToDisplay" ), component.displayAttribute );
609 vectorDataset.appendChild( logicalStructure );
610 ifLayerOn.appendChild( vectorDataset );
611 content.appendChild( ifLayerOn );
615 page.appendChild( content );
616 compositionElem.appendChild( page );
618 doc.appendChild( compositionElem );
621 QTextStream stream( &composition );
622 doc.save( stream, -1 );
627QString QgsAbstractGeoPdfExporter::compositionModeToString( QPainter::CompositionMode mode )
631 case QPainter::CompositionMode_SourceOver:
632 return QStringLiteral(
"Normal" );
634 case QPainter::CompositionMode_Multiply:
635 return QStringLiteral(
"Multiply" );
637 case QPainter::CompositionMode_Screen:
638 return QStringLiteral(
"Screen" );
640 case QPainter::CompositionMode_Overlay:
641 return QStringLiteral(
"Overlay" );
643 case QPainter::CompositionMode_Darken:
644 return QStringLiteral(
"Darken" );
646 case QPainter::CompositionMode_Lighten:
647 return QStringLiteral(
"Lighten" );
649 case QPainter::CompositionMode_ColorDodge:
650 return QStringLiteral(
"ColorDodge" );
652 case QPainter::CompositionMode_ColorBurn:
653 return QStringLiteral(
"ColorBurn" );
655 case QPainter::CompositionMode_HardLight:
656 return QStringLiteral(
"HardLight" );
658 case QPainter::CompositionMode_SoftLight:
659 return QStringLiteral(
"SoftLight" );
661 case QPainter::CompositionMode_Difference:
662 return QStringLiteral(
"Difference" );
664 case QPainter::CompositionMode_Exclusion:
665 return QStringLiteral(
"Exclusion" );
671 QgsDebugError( QStringLiteral(
"Unsupported PDF blend mode %1" ).arg( mode ) );
672 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.
void CPL_STDCALL collectErrors(CPLErr, int, const char *msg)
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.