Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgsalgorithmlayoutatlastopdf.cpp
3 : : ---------------------
4 : : begin : August 2020
5 : : copyright : (C) 2020 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 "qgsalgorithmlayoutatlastopdf.h"
19 : : #include "qgslayout.h"
20 : : #include "qgslayoutatlas.h"
21 : : #include "qgslayoutitemmap.h"
22 : : #include "qgslayoututils.h"
23 : : #include "qgsprintlayout.h"
24 : : #include "qgsprocessingoutputs.h"
25 : : #include "qgslayoutexporter.h"
26 : :
27 : : ///@cond PRIVATE
28 : :
29 : 0 : QString QgsLayoutAtlasToPdfAlgorithm::name() const
30 : : {
31 : 0 : return QStringLiteral( "atlaslayouttopdf" );
32 : : }
33 : :
34 : 0 : QString QgsLayoutAtlasToPdfAlgorithm::displayName() const
35 : : {
36 : 0 : return QObject::tr( "Export atlas layout as PDF" );
37 : : }
38 : :
39 : 0 : QStringList QgsLayoutAtlasToPdfAlgorithm::tags() const
40 : : {
41 : 0 : return QObject::tr( "layout,atlas,composer,composition,save" ).split( ',' );
42 : 0 : }
43 : :
44 : 0 : QString QgsLayoutAtlasToPdfAlgorithm::group() const
45 : : {
46 : 0 : return QObject::tr( "Cartography" );
47 : : }
48 : :
49 : 0 : QString QgsLayoutAtlasToPdfAlgorithm::groupId() const
50 : : {
51 : 0 : return QStringLiteral( "cartography" );
52 : : }
53 : :
54 : 0 : QString QgsLayoutAtlasToPdfAlgorithm::shortDescription() const
55 : : {
56 : 0 : return QObject::tr( "Exports an atlas layout as a PDF." );
57 : : }
58 : :
59 : 0 : QString QgsLayoutAtlasToPdfAlgorithm::shortHelpString() const
60 : : {
61 : 0 : return QObject::tr( "This algorithm outputs an atlas layout as a PDF file.\n\n"
62 : : "If a coverage layer is set, the selected layout's atlas settings exposed in this algorithm "
63 : : "will be overwritten. In this case, an empty filter or sort by expression will turn those "
64 : : "settings off." );
65 : : }
66 : :
67 : 0 : void QgsLayoutAtlasToPdfAlgorithm::initAlgorithm( const QVariantMap & )
68 : : {
69 : 0 : addParameter( new QgsProcessingParameterLayout( QStringLiteral( "LAYOUT" ), QObject::tr( "Atlas layout" ) ) );
70 : :
71 : 0 : addParameter( new QgsProcessingParameterVectorLayer( QStringLiteral( "COVERAGE_LAYER" ), QObject::tr( "Coverage layer" ), QList< int >(), QVariant(), true ) );
72 : 0 : addParameter( new QgsProcessingParameterExpression( QStringLiteral( "FILTER_EXPRESSION" ), QObject::tr( "Filter expression" ), QString(), QStringLiteral( "COVERAGE_LAYER" ), true ) );
73 : 0 : addParameter( new QgsProcessingParameterExpression( QStringLiteral( "SORTBY_EXPRESSION" ), QObject::tr( "Sort expression" ), QString(), QStringLiteral( "COVERAGE_LAYER" ), true ) );
74 : 0 : addParameter( new QgsProcessingParameterBoolean( QStringLiteral( "SORTBY_REVERSE" ), QObject::tr( "Reverse sort order (used when a sort expression is provided)" ), false, true ) );
75 : :
76 : 0 : addParameter( new QgsProcessingParameterFileDestination( QStringLiteral( "OUTPUT" ), QObject::tr( "PDF file" ), QObject::tr( "PDF Format" ) + " (*.pdf *.PDF)" ) );
77 : :
78 : 0 : std::unique_ptr< QgsProcessingParameterMultipleLayers > layersParam = std::make_unique< QgsProcessingParameterMultipleLayers>( QStringLiteral( "LAYERS" ), QObject::tr( "Map layers to assign to unlocked map item(s)" ), QgsProcessing::TypeMapLayer, QVariant(), true );
79 : 0 : layersParam->setFlags( layersParam->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
80 : 0 : addParameter( layersParam.release() );
81 : :
82 : 0 : std::unique_ptr< QgsProcessingParameterNumber > dpiParam = std::make_unique< QgsProcessingParameterNumber >( QStringLiteral( "DPI" ), QObject::tr( "DPI (leave blank for default layout DPI)" ), QgsProcessingParameterNumber::Double, QVariant(), true, 0 );
83 : 0 : dpiParam->setFlags( dpiParam->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
84 : 0 : addParameter( dpiParam.release() );
85 : :
86 : 0 : std::unique_ptr< QgsProcessingParameterBoolean > forceVectorParam = std::make_unique< QgsProcessingParameterBoolean >( QStringLiteral( "FORCE_VECTOR" ), QObject::tr( "Always export as vectors" ), false );
87 : 0 : forceVectorParam->setFlags( forceVectorParam->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
88 : 0 : addParameter( forceVectorParam.release() );
89 : :
90 : 0 : std::unique_ptr< QgsProcessingParameterBoolean > appendGeorefParam = std::make_unique< QgsProcessingParameterBoolean >( QStringLiteral( "GEOREFERENCE" ), QObject::tr( "Append georeference information" ), true );
91 : 0 : appendGeorefParam->setFlags( appendGeorefParam->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
92 : 0 : addParameter( appendGeorefParam.release() );
93 : :
94 : 0 : std::unique_ptr< QgsProcessingParameterBoolean > exportRDFParam = std::make_unique< QgsProcessingParameterBoolean >( QStringLiteral( "INCLUDE_METADATA" ), QObject::tr( "Export RDF metadata (title, author, etc.)" ), true );
95 : 0 : exportRDFParam->setFlags( exportRDFParam->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
96 : 0 : addParameter( exportRDFParam.release() );
97 : :
98 : 0 : std::unique_ptr< QgsProcessingParameterBoolean > disableTiled = std::make_unique< QgsProcessingParameterBoolean >( QStringLiteral( "DISABLE_TILED" ), QObject::tr( "Disable tiled raster layer exports" ), false );
99 : 0 : disableTiled->setFlags( disableTiled->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
100 : 0 : addParameter( disableTiled.release() );
101 : :
102 : 0 : std::unique_ptr< QgsProcessingParameterBoolean > simplify = std::make_unique< QgsProcessingParameterBoolean >( QStringLiteral( "SIMPLIFY" ), QObject::tr( "Simplify geometries to reduce output file size" ), true );
103 : 0 : simplify->setFlags( simplify->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
104 : 0 : addParameter( simplify.release() );
105 : :
106 : 0 : QStringList textExportOptions
107 : 0 : {
108 : 0 : QObject::tr( "Always Export Text as Paths (Recommended)" ),
109 : 0 : QObject::tr( "Always Export Text as Text Objects" )
110 : : };
111 : :
112 : 0 : std::unique_ptr< QgsProcessingParameterEnum > textFormat = std::make_unique< QgsProcessingParameterEnum >( QStringLiteral( "TEXT_FORMAT" ), QObject::tr( "Text export" ), textExportOptions, false, 0 );
113 : 0 : textFormat->setFlags( textFormat->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
114 : 0 : addParameter( textFormat.release() );
115 : 0 : }
116 : :
117 : 0 : QgsProcessingAlgorithm::Flags QgsLayoutAtlasToPdfAlgorithm::flags() const
118 : : {
119 : 0 : return QgsProcessingAlgorithm::flags() | FlagNoThreading;
120 : : }
121 : :
122 : 0 : QgsLayoutAtlasToPdfAlgorithm *QgsLayoutAtlasToPdfAlgorithm::createInstance() const
123 : : {
124 : 0 : return new QgsLayoutAtlasToPdfAlgorithm();
125 : : }
126 : :
127 : 0 : QVariantMap QgsLayoutAtlasToPdfAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
128 : : {
129 : : // this needs to be done in main thread, layouts are not thread safe
130 : 0 : QgsPrintLayout *l = parameterAsLayout( parameters, QStringLiteral( "LAYOUT" ), context );
131 : 0 : if ( !l )
132 : 0 : throw QgsProcessingException( QObject::tr( "Cannot find layout with name \"%1\"" ).arg( parameters.value( QStringLiteral( "LAYOUT" ) ).toString() ) );
133 : :
134 : 0 : std::unique_ptr< QgsPrintLayout > layout( l->clone() );
135 : 0 : QgsLayoutAtlas *atlas = layout->atlas();
136 : :
137 : 0 : QString expression, error;
138 : 0 : QgsVectorLayer *layer = parameterAsVectorLayer( parameters, QStringLiteral( "COVERAGE_LAYER" ), context );
139 : 0 : if ( layer )
140 : : {
141 : 0 : atlas->setEnabled( true );
142 : 0 : atlas->setCoverageLayer( layer );
143 : :
144 : 0 : expression = parameterAsString( parameters, QStringLiteral( "FILTER_EXPRESSION" ), context );
145 : 0 : atlas->setFilterExpression( expression, error );
146 : 0 : if ( !expression.isEmpty() && !error.isEmpty() )
147 : : {
148 : 0 : throw QgsProcessingException( QObject::tr( "Error setting atlas filter expression" ) );
149 : : }
150 : 0 : error.clear();
151 : :
152 : 0 : expression = parameterAsString( parameters, QStringLiteral( "SORTBY_EXPRESSION" ), context );
153 : 0 : if ( !expression.isEmpty() )
154 : : {
155 : 0 : const bool sortByReverse = parameterAsBool( parameters, QStringLiteral( "SORTBY_REVERSE" ), context );
156 : 0 : atlas->setSortFeatures( true );
157 : 0 : atlas->setSortExpression( expression );
158 : 0 : atlas->setSortAscending( !sortByReverse );
159 : 0 : }
160 : : else
161 : : {
162 : 0 : atlas->setSortFeatures( false );
163 : : }
164 : 0 : }
165 : 0 : else if ( !atlas->enabled() )
166 : : {
167 : 0 : throw QgsProcessingException( QObject::tr( "Layout being export doesn't have an enabled atlas" ) );
168 : : }
169 : :
170 : 0 : QgsLayoutExporter exporter( layout.get() );
171 : 0 : QgsLayoutExporter::PdfExportSettings settings;
172 : :
173 : 0 : if ( parameters.value( QStringLiteral( "DPI" ) ).isValid() )
174 : : {
175 : 0 : settings.dpi = parameterAsDouble( parameters, QStringLiteral( "DPI" ), context );
176 : 0 : }
177 : 0 : settings.forceVectorOutput = parameterAsBool( parameters, QStringLiteral( "FORCE_VECTOR" ), context );
178 : 0 : settings.appendGeoreference = parameterAsBool( parameters, QStringLiteral( "GEOREFERENCE" ), context );
179 : 0 : settings.exportMetadata = parameterAsBool( parameters, QStringLiteral( "INCLUDE_METADATA" ), context );
180 : 0 : settings.simplifyGeometries = parameterAsBool( parameters, QStringLiteral( "SIMPLIFY" ), context );
181 : 0 : settings.textRenderFormat = parameterAsEnum( parameters, QStringLiteral( "TEXT_FORMAT" ), context ) == 0 ? QgsRenderContext::TextFormatAlwaysOutlines : QgsRenderContext::TextFormatAlwaysText;
182 : :
183 : 0 : if ( parameterAsBool( parameters, QStringLiteral( "DISABLE_TILED" ), context ) )
184 : 0 : settings.flags = settings.flags | QgsLayoutRenderContext::FlagDisableTiledRasterLayerRenders;
185 : : else
186 : 0 : settings.flags = settings.flags & ~QgsLayoutRenderContext::FlagDisableTiledRasterLayerRenders;
187 : :
188 : 0 : settings.predefinedMapScales = QgsLayoutUtils::predefinedScales( layout.get() );
189 : :
190 : 0 : const QList< QgsMapLayer * > layers = parameterAsLayerList( parameters, QStringLiteral( "LAYERS" ), context );
191 : 0 : if ( layers.size() > 0 )
192 : : {
193 : 0 : const QList<QGraphicsItem *> items = layout->items();
194 : 0 : for ( QGraphicsItem *graphicsItem : items )
195 : : {
196 : 0 : QgsLayoutItem *item = dynamic_cast<QgsLayoutItem *>( graphicsItem );
197 : 0 : QgsLayoutItemMap *map = dynamic_cast<QgsLayoutItemMap *>( item );
198 : 0 : if ( map && !map->followVisibilityPreset() && !map->keepLayerSet() )
199 : : {
200 : 0 : map->setKeepLayerSet( true );
201 : 0 : map->setLayers( layers );
202 : 0 : }
203 : : }
204 : 0 : }
205 : :
206 : 0 : const QString dest = parameterAsFileOutput( parameters, QStringLiteral( "OUTPUT" ), context );
207 : 0 : if ( atlas->updateFeatures() )
208 : : {
209 : 0 : feedback->pushInfo( QObject::tr( "Exporting %n atlas feature(s)", "", atlas->count() ) );
210 : 0 : switch ( exporter.exportToPdf( atlas, dest, settings, error, feedback ) )
211 : : {
212 : : case QgsLayoutExporter::Success:
213 : : {
214 : 0 : feedback->pushInfo( QObject::tr( "Successfully exported layout to %1" ).arg( QDir::toNativeSeparators( dest ) ) );
215 : 0 : break;
216 : : }
217 : :
218 : : case QgsLayoutExporter::FileError:
219 : 0 : throw QgsProcessingException( QObject::tr( "Cannot write to %1.\n\nThis file may be open in another application." ).arg( QDir::toNativeSeparators( dest ) ) );
220 : :
221 : : case QgsLayoutExporter::PrintError:
222 : 0 : throw QgsProcessingException( QObject::tr( "Could not create print device." ) );
223 : :
224 : : case QgsLayoutExporter::MemoryError:
225 : 0 : throw QgsProcessingException( QObject::tr( "Trying to create the image "
226 : : "resulted in a memory overflow.\n\n"
227 : : "Please try a lower resolution or a smaller paper size." ) );
228 : :
229 : : case QgsLayoutExporter::IteratorError:
230 : 0 : throw QgsProcessingException( QObject::tr( "Error encountered while exporting atlas." ) );
231 : :
232 : : case QgsLayoutExporter::SvgLayerError:
233 : : case QgsLayoutExporter::Canceled:
234 : : // no meaning for imageexports, will not be encountered
235 : 0 : break;
236 : : }
237 : 0 : }
238 : : else
239 : : {
240 : 0 : feedback->reportError( QObject::tr( "No atlas features found" ) );
241 : : }
242 : :
243 : 0 : feedback->setProgress( 100 );
244 : :
245 : 0 : QVariantMap outputs;
246 : 0 : outputs.insert( QStringLiteral( "OUTPUT" ), dest );
247 : 0 : return outputs;
248 : 0 : }
249 : :
250 : : ///@endcond
251 : :
|