Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgsmaprenderertask.h
3 : : -------------------------
4 : : begin : Apr 2017
5 : : copyright : (C) 2017 by Mathieu Pellerin
6 : : email : nirvn dot asia at gmail dot com
7 : : ***************************************************************************/
8 : :
9 : : /***************************************************************************
10 : : * *
11 : : * This program is free software; you can redistribute it and/or modify *
12 : : * it under the terms of the GNU General Public License as published by *
13 : : * the Free Software Foundation; either version 2 of the License, or *
14 : : * (at your option) any later version. *
15 : : * *
16 : : ***************************************************************************/
17 : :
18 : : #include "qgsannotation.h"
19 : : #include "qgsannotationmanager.h"
20 : : #include "qgsmaprenderertask.h"
21 : : #include "qgsmapsettingsutils.h"
22 : : #include "qgsogrutils.h"
23 : : #include "qgslogger.h"
24 : : #include "qgsabstractgeopdfexporter.h"
25 : : #include "qgsmaprendererstagedrenderjob.h"
26 : : #include "qgsrenderedfeaturehandlerinterface.h"
27 : : #include "qgsfeaturerequest.h"
28 : : #include "qgsvectorlayer.h"
29 : :
30 : : #include <QFile>
31 : : #include <QTextStream>
32 : : #include <QTimeZone>
33 : : #ifndef QT_NO_PRINTER
34 : : #include <QPrinter>
35 : : #endif
36 : :
37 : : #include "gdal.h"
38 : : #include "cpl_conv.h"
39 : :
40 : : ///@cond PRIVATE
41 : :
42 : 0 : class QgsMapRendererTaskGeoPdfExporter : public QgsAbstractGeoPdfExporter
43 : : {
44 : :
45 : : public:
46 : :
47 : 0 : QgsMapRendererTaskGeoPdfExporter( const QgsMapSettings &ms )
48 : 0 : {
49 : : // collect details upfront, while we are still in the main thread
50 : 0 : const QList< QgsMapLayer * > layers = ms.layers();
51 : 0 : for ( const QgsMapLayer *layer : layers )
52 : : {
53 : 0 : VectorComponentDetail detail;
54 : 0 : detail.name = layer->name();
55 : 0 : detail.mapLayerId = layer->id();
56 : 0 : if ( const QgsVectorLayer *vl = qobject_cast< const QgsVectorLayer * >( layer ) )
57 : : {
58 : 0 : detail.displayAttribute = vl->displayField();
59 : 0 : }
60 : 0 : mLayerDetails[ layer->id() ] = detail;
61 : 0 : }
62 : 0 : }
63 : :
64 : : private:
65 : :
66 : 0 : QgsAbstractGeoPdfExporter::VectorComponentDetail componentDetailForLayerId( const QString &layerId ) override
67 : : {
68 : 0 : return mLayerDetails.value( layerId );
69 : 0 : }
70 : :
71 : : QMap< QString, VectorComponentDetail > mLayerDetails;
72 : : };
73 : :
74 : :
75 : 0 : class QgsMapRendererTaskRenderedFeatureHandler : public QgsRenderedFeatureHandlerInterface
76 : : {
77 : : public:
78 : :
79 : 0 : QgsMapRendererTaskRenderedFeatureHandler( QgsMapRendererTaskGeoPdfExporter *exporter, const QgsMapSettings &settings )
80 : 0 : : mExporter( exporter )
81 : 0 : , mMapSettings( settings )
82 : 0 : {
83 : : // PDF coordinate space uses a hardcoded DPI of 72, also vertical dimension is flipped from QGIS dimension
84 : 0 : const double pageHeightPdfUnits = settings.outputSize().height() * 72.0 / settings.outputDpi();
85 : 0 : mTransform = QTransform::fromTranslate( 0, pageHeightPdfUnits ).scale( 72.0 / mMapSettings.outputDpi(), -72.0 / mMapSettings.outputDpi() );
86 : 0 : }
87 : :
88 : 0 : void handleRenderedFeature( const QgsFeature &feature, const QgsGeometry &renderedBounds, const QgsRenderedFeatureHandlerInterface::RenderedFeatureContext &context ) override
89 : : {
90 : : // is it a hack retrieving the layer ID from an expression context like this? possibly... BUT
91 : : // the alternative is adding a layer ID member to QgsRenderContext, and that's just asking for people to abuse it
92 : : // and use it to retrieve QgsMapLayers mid-way through a render operation. Lesser of two evils it is!
93 : 0 : const QString layerId = context.renderContext.expressionContext().variable( QStringLiteral( "layer_id" ) ).toString();
94 : :
95 : 0 : QgsGeometry transformed = renderedBounds;
96 : 0 : transformed.transform( mTransform );
97 : :
98 : : // always convert to multitype, to make things consistent
99 : 0 : transformed.convertToMultiType();
100 : :
101 : 0 : mExporter->pushRenderedFeature( layerId, QgsAbstractGeoPdfExporter::RenderedFeature( feature, transformed ) );
102 : 0 : }
103 : :
104 : 0 : QSet<QString> usedAttributes( QgsVectorLayer *, const QgsRenderContext & ) const override
105 : : {
106 : 0 : return QSet< QString >() << QgsFeatureRequest::ALL_ATTRIBUTES;
107 : 0 : }
108 : :
109 : : private:
110 : :
111 : : QgsMapRendererTaskGeoPdfExporter *mExporter = nullptr;
112 : : QgsMapSettings mMapSettings;
113 : : //! Transform from output space (pixels) to PDF space (pixels at 72 dpi)
114 : : QTransform mTransform;
115 : :
116 : : };
117 : :
118 : : ///@endcond
119 : :
120 : 0 : QgsMapRendererTask::QgsMapRendererTask( const QgsMapSettings &ms, const QString &fileName, const QString &fileFormat, const bool forceRaster,
121 : : const bool geoPDF, const QgsAbstractGeoPdfExporter::ExportDetails &geoPdfExportDetails )
122 : 0 : : QgsTask( fileFormat == QLatin1String( "PDF" ) ? tr( "Saving as PDF" ) : tr( "Saving as image" ) )
123 : 0 : , mMapSettings( ms )
124 : 0 : , mFileName( fileName )
125 : 0 : , mFileFormat( fileFormat )
126 : 0 : , mForceRaster( forceRaster )
127 : 0 : , mGeoPDF( geoPDF && mFileFormat == QLatin1String( "PDF" ) && QgsAbstractGeoPdfExporter::geoPDFCreationAvailable() )
128 : 0 : , mGeoPdfExportDetails( geoPdfExportDetails )
129 : 0 : {
130 : 0 : prepare();
131 : 0 : }
132 : :
133 : 0 : QgsMapRendererTask::QgsMapRendererTask( const QgsMapSettings &ms, QPainter *p )
134 : 0 : : QgsTask( tr( "Rendering to painter" ) )
135 : 0 : , mMapSettings( ms )
136 : 0 : , mPainter( p )
137 : 0 : {
138 : 0 : prepare();
139 : 0 : }
140 : 0 :
141 : 0 : QgsMapRendererTask::~QgsMapRendererTask() = default;
142 : :
143 : 0 : void QgsMapRendererTask::addAnnotations( const QList< QgsAnnotation * > &annotations )
144 : : {
145 : 0 : qDeleteAll( mAnnotations );
146 : 0 : mAnnotations.clear();
147 : :
148 : 0 : const auto constAnnotations = annotations;
149 : 0 : for ( const QgsAnnotation *a : constAnnotations )
150 : : {
151 : 0 : mAnnotations << a->clone();
152 : 0 : }
153 : 0 : }
154 : 0 :
155 : 0 : void QgsMapRendererTask::addDecorations( const QList< QgsMapDecoration * > &decorations )
156 : : {
157 : 0 : mDecorations = decorations;
158 : 0 : }
159 : :
160 : :
161 : 0 : void QgsMapRendererTask::cancel()
162 : 0 : {
163 : 0 : mJobMutex.lock();
164 : 0 : if ( mJob )
165 : 0 : mJob->cancelWithoutBlocking();
166 : 0 : mJobMutex.unlock();
167 : :
168 : 0 : QgsTask::cancel();
169 : 0 : }
170 : :
171 : 0 : bool QgsMapRendererTask::run()
172 : : {
173 : 0 : if ( mErrored )
174 : 0 : return false;
175 : :
176 : 0 : if ( mGeoPDF )
177 : : {
178 : : #ifdef QT_NO_PRINTER
179 : : return false;
180 : : #else
181 : 0 : QList< QgsAbstractGeoPdfExporter::ComponentLayerDetail > pdfComponents;
182 : :
183 : 0 : QgsMapRendererStagedRenderJob *job = static_cast< QgsMapRendererStagedRenderJob * >( mJob.get() );
184 : 0 : int outputLayer = 1;
185 : 0 : while ( !job->isFinished() )
186 : : {
187 : 0 : QgsAbstractGeoPdfExporter::ComponentLayerDetail component;
188 : :
189 : 0 : component.name = QStringLiteral( "layer_%1" ).arg( outputLayer );
190 : 0 : component.mapLayerId = job->currentLayerId();
191 : 0 : component.opacity = job->currentLayerOpacity();
192 : 0 : component.compositionMode = job->currentLayerCompositionMode();
193 : 0 : component.sourcePdfPath = mGeoPdfExporter->generateTemporaryFilepath( QStringLiteral( "layer_%1.pdf" ).arg( outputLayer ) );
194 : 0 : pdfComponents << component;
195 : :
196 : 0 : QPrinter printer;
197 : 0 : printer.setOutputFileName( component.sourcePdfPath );
198 : 0 : printer.setOutputFormat( QPrinter::PdfFormat );
199 : : #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
200 : : printer.setOrientation( QPrinter::Portrait );
201 : : // paper size needs to be given in millimeters in order to be able to set a resolution to pass onto the map renderer
202 : : QSizeF outputSize = mMapSettings.outputSize();
203 : : printer.setPaperSize( outputSize * 25.4 / mMapSettings.outputDpi(), QPrinter::Millimeter );
204 : : printer.setPageMargins( 0, 0, 0, 0, QPrinter::Millimeter );
205 : : #else
206 : 0 : printer.setPageOrientation( QPageLayout::Orientation::Portrait );
207 : : // paper size needs to be given in millimeters in order to be able to set a resolution to pass onto the map renderer
208 : 0 : QSizeF outputSize = mMapSettings.outputSize();
209 : 0 : QPageSize pageSize( outputSize * 25.4 / mMapSettings.outputDpi(), QPageSize::Unit::Millimeter );
210 : 0 : printer.setPageSize( pageSize );
211 : 0 : printer.setPageMargins( QMarginsF( 0, 0, 0, 0 ) );
212 : : #endif
213 : 0 : printer.setResolution( mMapSettings.outputDpi() );
214 : :
215 : 0 : QPainter p( &printer );
216 : 0 : job->renderCurrentPart( &p );
217 : 0 : p.end();
218 : :
219 : 0 : outputLayer++;
220 : 0 : job->nextPart();
221 : 0 : }
222 : 0 : QgsAbstractGeoPdfExporter::ExportDetails exportDetails = mGeoPdfExportDetails;
223 : 0 : const double pageWidthMM = mMapSettings.outputSize().width() * 25.4 / mMapSettings.outputDpi();
224 : 0 : const double pageHeightMM = mMapSettings.outputSize().height() * 25.4 / mMapSettings.outputDpi();
225 : 0 : exportDetails.pageSizeMm = QSizeF( pageWidthMM, pageHeightMM );
226 : 0 : exportDetails.dpi = mMapSettings.outputDpi();
227 : :
228 : 0 : exportDetails.layerIdToPdfLayerTreeNameMap = mLayerIdToLayerNameMap;
229 : 0 : exportDetails.layerOrder = mMapLayerOrder;
230 : :
231 : 0 : if ( mSaveWorldFile )
232 : : {
233 : : // setup georeferencing
234 : 0 : QgsAbstractGeoPdfExporter::GeoReferencedSection georef;
235 : 0 : georef.crs = mMapSettings.destinationCrs();
236 : 0 : georef.pageBoundsMm = QgsRectangle( 0, 0, pageWidthMM, pageHeightMM );
237 : 0 : georef.controlPoints.reserve( 4 );
238 : 0 : georef.controlPoints << QgsAbstractGeoPdfExporter::ControlPoint( QgsPointXY( 0, 0 ), mMapSettings.mapToPixel().toMapCoordinates( 0, 0 ) );
239 : 0 : georef.controlPoints << QgsAbstractGeoPdfExporter::ControlPoint( QgsPointXY( pageWidthMM, 0 ), mMapSettings.mapToPixel().toMapCoordinates( mMapSettings.outputSize().width(), 0 ) );
240 : 0 : georef.controlPoints << QgsAbstractGeoPdfExporter::ControlPoint( QgsPointXY( pageWidthMM, pageHeightMM ), mMapSettings.mapToPixel().toMapCoordinates( mMapSettings.outputSize().width(), mMapSettings.outputSize().height() ) );
241 : 0 : georef.controlPoints << QgsAbstractGeoPdfExporter::ControlPoint( QgsPointXY( 0, pageHeightMM ), mMapSettings.mapToPixel().toMapCoordinates( 0, mMapSettings.outputSize().height() ) );
242 : 0 : exportDetails.georeferencedSections << georef;
243 : 0 : }
244 : :
245 : 0 : const bool res = mGeoPdfExporter->finalize( pdfComponents, mFileName, exportDetails );
246 : 0 : mGeoPdfExporter.reset();
247 : 0 : mTempPainter.reset();
248 : 0 : mPrinter.reset();
249 : 0 : return res;
250 : : #endif
251 : 0 : }
252 : : else
253 : 0 : static_cast< QgsMapRendererCustomPainterJob *>( mJob.get() )->renderPrepared();
254 : :
255 : 0 : mJobMutex.lock();
256 : 0 : mJob.reset( nullptr );
257 : 0 : mJobMutex.unlock();
258 : :
259 : 0 : if ( isCanceled() )
260 : 0 : return false;
261 : :
262 : 0 : QgsRenderContext context = QgsRenderContext::fromMapSettings( mMapSettings );
263 : 0 : context.setPainter( mDestPainter );
264 : :
265 : 0 : const auto constMDecorations = mDecorations;
266 : 0 : for ( QgsMapDecoration *decoration : constMDecorations )
267 : : {
268 : 0 : decoration->render( mMapSettings, context );
269 : : }
270 : :
271 : 0 : const auto constMAnnotations = mAnnotations;
272 : 0 : for ( QgsAnnotation *annotation : constMAnnotations )
273 : : {
274 : 0 : if ( isCanceled() )
275 : 0 : return false;
276 : :
277 : 0 : if ( !annotation || !annotation->isVisible() )
278 : : {
279 : 0 : continue;
280 : : }
281 : 0 : if ( annotation->mapLayer() && !mMapSettings.layers().contains( annotation->mapLayer() ) )
282 : : {
283 : 0 : continue;
284 : : }
285 : :
286 : 0 : QgsScopedQPainterState painterState( context.painter() );
287 : 0 : context.setPainterFlagsUsingContext();
288 : :
289 : : double itemX, itemY;
290 : 0 : if ( annotation->hasFixedMapPosition() )
291 : : {
292 : 0 : itemX = mMapSettings.outputSize().width() * ( annotation->mapPosition().x() - mMapSettings.extent().xMinimum() ) / mMapSettings.extent().width();
293 : 0 : itemY = mMapSettings.outputSize().height() * ( 1 - ( annotation->mapPosition().y() - mMapSettings.extent().yMinimum() ) / mMapSettings.extent().height() );
294 : 0 : }
295 : : else
296 : : {
297 : 0 : itemX = annotation->relativePosition().x() * mMapSettings.outputSize().width();
298 : 0 : itemY = annotation->relativePosition().y() * mMapSettings.outputSize().height();
299 : : }
300 : :
301 : 0 : context.painter()->translate( itemX, itemY );
302 : :
303 : 0 : annotation->render( context );
304 : 0 : }
305 : :
306 : 0 : if ( !mFileName.isEmpty() )
307 : : {
308 : 0 : mDestPainter->end();
309 : :
310 : 0 : if ( mFileFormat == QLatin1String( "PDF" ) )
311 : : {
312 : : #ifndef QT_NO_PRINTER
313 : 0 : if ( mForceRaster )
314 : : {
315 : 0 : QPainter pp;
316 : 0 : pp.begin( mPrinter.get() );
317 : 0 : QRectF rect( 0, 0, mImage.width(), mImage.height() );
318 : 0 : pp.drawImage( rect, mImage, rect );
319 : 0 : pp.end();
320 : 0 : }
321 : :
322 : 0 : if ( mSaveWorldFile || mExportMetadata )
323 : : {
324 : 0 : CPLSetThreadLocalConfigOption( "GDAL_PDF_DPI", QString::number( mMapSettings.outputDpi() ).toLocal8Bit().constData() );
325 : 0 : gdal::dataset_unique_ptr outputDS( GDALOpen( mFileName.toLocal8Bit().constData(), GA_Update ) );
326 : 0 : if ( outputDS )
327 : : {
328 : 0 : if ( mSaveWorldFile )
329 : : {
330 : : double a, b, c, d, e, f;
331 : 0 : QgsMapSettingsUtils::worldFileParameters( mMapSettings, a, b, c, d, e, f );
332 : 0 : c -= 0.5 * a;
333 : 0 : c -= 0.5 * b;
334 : 0 : f -= 0.5 * d;
335 : 0 : f -= 0.5 * e;
336 : 0 : double geoTransform[6] = { c, a, b, f, d, e };
337 : 0 : GDALSetGeoTransform( outputDS.get(), geoTransform );
338 : 0 : GDALSetProjection( outputDS.get(), mMapSettings.destinationCrs().toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED_GDAL ).toLocal8Bit().constData() );
339 : 0 : }
340 : :
341 : 0 : if ( mExportMetadata )
342 : : {
343 : 0 : QString creationDateString;
344 : 0 : const QDateTime creationDateTime = mGeoPdfExportDetails.creationDateTime;
345 : 0 : if ( creationDateTime.isValid() )
346 : : {
347 : 0 : creationDateString = QStringLiteral( "D:%1" ).arg( mGeoPdfExportDetails.creationDateTime.toString( QStringLiteral( "yyyyMMddHHmmss" ) ) );
348 : 0 : if ( creationDateTime.timeZone().isValid() )
349 : : {
350 : 0 : int offsetFromUtc = creationDateTime.timeZone().offsetFromUtc( creationDateTime );
351 : 0 : creationDateString += ( offsetFromUtc >= 0 ) ? '+' : '-';
352 : 0 : offsetFromUtc = std::abs( offsetFromUtc );
353 : 0 : int offsetHours = offsetFromUtc / 3600;
354 : 0 : int offsetMins = ( offsetFromUtc % 3600 ) / 60;
355 : 0 : creationDateString += QStringLiteral( "%1'%2'" ).arg( offsetHours ).arg( offsetMins );
356 : 0 : }
357 : 0 : }
358 : 0 : GDALSetMetadataItem( outputDS.get(), "CREATION_DATE", creationDateString.toUtf8().constData(), nullptr );
359 : :
360 : 0 : GDALSetMetadataItem( outputDS.get(), "AUTHOR", mGeoPdfExportDetails.author.toUtf8().constData(), nullptr );
361 : 0 : const QString creator = QStringLiteral( "QGIS %1" ).arg( Qgis::version() );
362 : 0 : GDALSetMetadataItem( outputDS.get(), "CREATOR", creator.toUtf8().constData(), nullptr );
363 : 0 : GDALSetMetadataItem( outputDS.get(), "PRODUCER", creator.toUtf8().constData(), nullptr );
364 : 0 : GDALSetMetadataItem( outputDS.get(), "SUBJECT", mGeoPdfExportDetails.subject.toUtf8().constData(), nullptr );
365 : 0 : GDALSetMetadataItem( outputDS.get(), "TITLE", mGeoPdfExportDetails.title.toUtf8().constData(), nullptr );
366 : :
367 : 0 : const QgsAbstractMetadataBase::KeywordMap keywords = mGeoPdfExportDetails.keywords;
368 : 0 : QStringList allKeywords;
369 : 0 : for ( auto it = keywords.constBegin(); it != keywords.constEnd(); ++it )
370 : : {
371 : 0 : allKeywords.append( QStringLiteral( "%1: %2" ).arg( it.key(), it.value().join( ',' ) ) );
372 : 0 : }
373 : 0 : const QString keywordString = allKeywords.join( ';' );
374 : 0 : GDALSetMetadataItem( outputDS.get(), "KEYWORDS", keywordString.toUtf8().constData(), nullptr );
375 : 0 : }
376 : 0 : }
377 : 0 : CPLSetThreadLocalConfigOption( "GDAL_PDF_DPI", nullptr );
378 : 0 : }
379 : : #else
380 : : mError = ImageUnsupportedFormat;
381 : : return false;
382 : : #endif // !QT_NO_PRINTER
383 : 0 : }
384 : 0 : else if ( mFileFormat != QLatin1String( "PDF" ) )
385 : : {
386 : 0 : bool success = mImage.save( mFileName, mFileFormat.toLocal8Bit().data() );
387 : 0 : if ( !success )
388 : : {
389 : 0 : mError = ImageSaveFail;
390 : 0 : return false;
391 : : }
392 : :
393 : 0 : if ( mSaveWorldFile )
394 : : {
395 : 0 : QFileInfo info = QFileInfo( mFileName );
396 : :
397 : : // build the world file name
398 : 0 : QString outputSuffix = info.suffix();
399 : 0 : bool skipWorldFile = false;
400 : 0 : if ( outputSuffix == QLatin1String( "tif" ) || outputSuffix == QLatin1String( "tiff" ) )
401 : : {
402 : 0 : gdal::dataset_unique_ptr outputDS( GDALOpen( mFileName.toLocal8Bit().constData(), GA_Update ) );
403 : 0 : if ( outputDS )
404 : : {
405 : 0 : skipWorldFile = true;
406 : : double a, b, c, d, e, f;
407 : 0 : QgsMapSettingsUtils::worldFileParameters( mMapSettings, a, b, c, d, e, f );
408 : 0 : c -= 0.5 * a;
409 : 0 : c -= 0.5 * b;
410 : 0 : f -= 0.5 * d;
411 : 0 : f -= 0.5 * e;
412 : 0 : double geoTransform[] = { c, a, b, f, d, e };
413 : 0 : GDALSetGeoTransform( outputDS.get(), geoTransform );
414 : 0 : GDALSetProjection( outputDS.get(), mMapSettings.destinationCrs().toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED_GDAL ).toLocal8Bit().constData() );
415 : 0 : }
416 : 0 : }
417 : :
418 : 0 : if ( !skipWorldFile )
419 : : {
420 : 0 : QString worldFileName = info.absolutePath() + '/' + info.completeBaseName() + '.'
421 : 0 : + outputSuffix.at( 0 ) + outputSuffix.at( info.suffix().size() - 1 ) + 'w';
422 : 0 : QFile worldFile( worldFileName );
423 : :
424 : 0 : if ( worldFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) ) //don't use QIODevice::Text
425 : : {
426 : 0 : QTextStream stream( &worldFile );
427 : 0 : stream << QgsMapSettingsUtils::worldFileContent( mMapSettings );
428 : 0 : }
429 : 0 : }
430 : 0 : }
431 : 0 : }
432 : 0 : }
433 : :
434 : 0 : mTempPainter.reset();
435 : : #ifndef QT_NO_PRINTER
436 : 0 : mPrinter.reset();
437 : : #endif
438 : :
439 : 0 : return true;
440 : 0 : }
441 : :
442 : 0 : void QgsMapRendererTask::finished( bool result )
443 : : {
444 : 0 : qDeleteAll( mAnnotations );
445 : 0 : mAnnotations.clear();
446 : :
447 : 0 : if ( result )
448 : 0 : emit renderingComplete();
449 : : else
450 : 0 : emit errorOccurred( mError );
451 : 0 : }
452 : :
453 : 0 : void QgsMapRendererTask::prepare()
454 : : {
455 : 0 : if ( mGeoPDF )
456 : : {
457 : 0 : mGeoPdfExporter = std::make_unique< QgsMapRendererTaskGeoPdfExporter >( mMapSettings );
458 : 0 : if ( mGeoPdfExportDetails.includeFeatures )
459 : : {
460 : 0 : mRenderedFeatureHandler = std::make_unique< QgsMapRendererTaskRenderedFeatureHandler >( static_cast< QgsMapRendererTaskGeoPdfExporter * >( mGeoPdfExporter.get() ), mMapSettings );
461 : 0 : mMapSettings.addRenderedFeatureHandler( mRenderedFeatureHandler.get() );
462 : 0 : }
463 : :
464 : 0 : const QList< QgsMapLayer * > layers = mMapSettings.layers();
465 : 0 : for ( const QgsMapLayer *layer : layers )
466 : : {
467 : 0 : mLayerIdToLayerNameMap.insert( layer->id(), layer->name() );
468 : 0 : mMapLayerOrder << layer->id();
469 : : }
470 : :
471 : 0 : mJob.reset( new QgsMapRendererStagedRenderJob( mMapSettings, QgsMapRendererStagedRenderJob::RenderLabelsByMapLayer ) );
472 : 0 : mJob->start();
473 : : return;
474 : 0 : }
475 : :
476 : 0 : mDestPainter = mPainter;
477 : :
478 : 0 : if ( mFileFormat == QLatin1String( "PDF" ) )
479 : : {
480 : : #ifndef QT_NO_PRINTER
481 : 0 : mPrinter.reset( new QPrinter() );
482 : 0 : mPrinter->setOutputFileName( mFileName );
483 : 0 : mPrinter->setOutputFormat( QPrinter::PdfFormat );
484 : : #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
485 : : mPrinter->setOrientation( QPrinter::Portrait );
486 : : // paper size needs to be given in millimeters in order to be able to set a resolution to pass onto the map renderer
487 : : QSizeF outputSize = mMapSettings.outputSize();
488 : : mPrinter->setPaperSize( outputSize * 25.4 / mMapSettings.outputDpi(), QPrinter::Millimeter );
489 : : mPrinter->setPageMargins( 0, 0, 0, 0, QPrinter::Millimeter );
490 : : #else
491 : 0 : mPrinter->setPageOrientation( QPageLayout::Orientation::Portrait );
492 : : // paper size needs to be given in millimeters in order to be able to set a resolution to pass onto the map renderer
493 : 0 : QSizeF outputSize = mMapSettings.outputSize();
494 : 0 : QPageSize pageSize( outputSize * 25.4 / mMapSettings.outputDpi(), QPageSize::Unit::Millimeter );
495 : 0 : mPrinter->setPageSize( pageSize );
496 : 0 : mPrinter->setPageMargins( QMarginsF( 0, 0, 0, 0 ) );
497 : : #endif
498 : 0 : mPrinter->setResolution( mMapSettings.outputDpi() );
499 : :
500 : 0 : if ( !mForceRaster )
501 : : {
502 : 0 : mTempPainter.reset( new QPainter( mPrinter.get() ) );
503 : 0 : mDestPainter = mTempPainter.get();
504 : 0 : }
505 : : #else
506 : : mError = ImageUnsupportedFormat;
507 : : #endif // ! QT_NO_PRINTER
508 : 0 : }
509 : :
510 : 0 : if ( !mDestPainter )
511 : : {
512 : : // save rendered map to an image file
513 : 0 : mImage = QImage( mMapSettings.outputSize(), QImage::Format_ARGB32 );
514 : 0 : if ( mImage.isNull() )
515 : : {
516 : 0 : mErrored = true;
517 : 0 : mError = ImageAllocationFail;
518 : 0 : return;
519 : : }
520 : :
521 : 0 : mImage.setDotsPerMeterX( 1000 * mMapSettings.outputDpi() / 25.4 );
522 : 0 : mImage.setDotsPerMeterY( 1000 * mMapSettings.outputDpi() / 25.4 );
523 : :
524 : 0 : mTempPainter.reset( new QPainter( &mImage ) );
525 : 0 : mDestPainter = mTempPainter.get();
526 : 0 : }
527 : :
528 : 0 : if ( !mDestPainter )
529 : : {
530 : 0 : mErrored = true;
531 : 0 : return;
532 : : }
533 : :
534 : 0 : mJob.reset( new QgsMapRendererCustomPainterJob( mMapSettings, mDestPainter ) );
535 : 0 : static_cast< QgsMapRendererCustomPainterJob *>( mJob.get() )->prepare();
536 : 0 : }
|