Branch data Line data Source code
1 : : /*************************************************************************** 2 : : qgsalgorithmlayouttoimage.cpp 3 : : --------------------- 4 : : begin : June 2020 5 : : copyright : (C) 2020 by Nyall Dawson 6 : : email : nyall dot dawson 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 "qgsalgorithmlayouttoimage.h" 19 : : #include "qgslayout.h" 20 : : #include "qgslayoutitemmap.h" 21 : : #include "qgsprintlayout.h" 22 : : #include "qgsprocessingoutputs.h" 23 : : #include "qgslayoutexporter.h" 24 : : #include <QImageWriter> 25 : : 26 : : ///@cond PRIVATE 27 : : 28 : 0 : QString QgsLayoutToImageAlgorithm::name() const 29 : : { 30 : 0 : return QStringLiteral( "printlayouttoimage" ); 31 : : } 32 : : 33 : 0 : QString QgsLayoutToImageAlgorithm::displayName() const 34 : : { 35 : 0 : return QObject::tr( "Export print layout as image" ); 36 : : } 37 : : 38 : 0 : QStringList QgsLayoutToImageAlgorithm::tags() const 39 : : { 40 : 0 : return QObject::tr( "layout,composer,composition,save,png,jpeg,jpg" ).split( ',' ); 41 : 0 : } 42 : : 43 : 0 : QString QgsLayoutToImageAlgorithm::group() const 44 : : { 45 : 0 : return QObject::tr( "Cartography" ); 46 : : } 47 : : 48 : 0 : QString QgsLayoutToImageAlgorithm::groupId() const 49 : : { 50 : 0 : return QStringLiteral( "cartography" ); 51 : : } 52 : : 53 : 0 : QString QgsLayoutToImageAlgorithm::shortDescription() const 54 : : { 55 : 0 : return QObject::tr( "Exports a print layout as an image." ); 56 : : } 57 : : 58 : 0 : QString QgsLayoutToImageAlgorithm::shortHelpString() const 59 : : { 60 : 0 : return QObject::tr( "This algorithm outputs a print layout as an image file (e.g. PNG or JPEG images)." ); 61 : : } 62 : : 63 : 0 : void QgsLayoutToImageAlgorithm::initAlgorithm( const QVariantMap & ) 64 : : { 65 : 0 : addParameter( new QgsProcessingParameterLayout( QStringLiteral( "LAYOUT" ), QObject::tr( "Print layout" ) ) ); 66 : : 67 : 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 ); 68 : 0 : layersParam->setFlags( layersParam->flags() | QgsProcessingParameterDefinition::FlagAdvanced ); 69 : 0 : addParameter( layersParam.release() ); 70 : : 71 : 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 ); 72 : 0 : dpiParam->setFlags( dpiParam->flags() | QgsProcessingParameterDefinition::FlagAdvanced ); 73 : 0 : addParameter( dpiParam.release() ); 74 : : 75 : 0 : std::unique_ptr< QgsProcessingParameterBoolean > appendGeorefParam = std::make_unique< QgsProcessingParameterBoolean >( QStringLiteral( "GEOREFERENCE" ), QObject::tr( "Generate world file" ), true ); 76 : 0 : appendGeorefParam->setFlags( appendGeorefParam->flags() | QgsProcessingParameterDefinition::FlagAdvanced ); 77 : 0 : addParameter( appendGeorefParam.release() ); 78 : : 79 : 0 : std::unique_ptr< QgsProcessingParameterBoolean > exportRDFParam = std::make_unique< QgsProcessingParameterBoolean >( QStringLiteral( "INCLUDE_METADATA" ), QObject::tr( "Export RDF metadata (title, author, etc.)" ), true ); 80 : 0 : exportRDFParam->setFlags( exportRDFParam->flags() | QgsProcessingParameterDefinition::FlagAdvanced ); 81 : 0 : addParameter( exportRDFParam.release() ); 82 : : 83 : 0 : std::unique_ptr< QgsProcessingParameterBoolean > antialias = std::make_unique< QgsProcessingParameterBoolean >( QStringLiteral( "ANTIALIAS" ), QObject::tr( "Enable antialiasing" ), true ); 84 : 0 : antialias->setFlags( antialias->flags() | QgsProcessingParameterDefinition::FlagAdvanced ); 85 : 0 : addParameter( antialias.release() ); 86 : : 87 : 0 : QStringList imageFilters; 88 : 0 : const auto supportedImageFormats { QImageWriter::supportedImageFormats() }; 89 : 0 : for ( const QByteArray &format : supportedImageFormats ) 90 : : { 91 : 0 : if ( format == "svg" ) 92 : 0 : continue; 93 : : 94 : 0 : QString longName = format.toUpper() + QObject::tr( " format" ); 95 : 0 : QString glob = QStringLiteral( "*." ) + format; 96 : : 97 : 0 : if ( format == "png" && !imageFilters.empty() ) 98 : 0 : imageFilters.insert( 0, QStringLiteral( "%1 (%2 %3)" ).arg( longName, glob.toLower(), glob.toUpper() ) ); 99 : : else 100 : 0 : imageFilters.append( QStringLiteral( "%1 (%2 %3)" ).arg( longName, glob.toLower(), glob.toUpper() ) ); 101 : 0 : } 102 : : 103 : 0 : addParameter( new QgsProcessingParameterFileDestination( QStringLiteral( "OUTPUT" ), QObject::tr( "Image file" ), imageFilters.join( QLatin1String( ";;" ) ) ) ); 104 : 0 : } 105 : : 106 : 0 : QgsProcessingAlgorithm::Flags QgsLayoutToImageAlgorithm::flags() const 107 : : { 108 : 0 : return QgsProcessingAlgorithm::flags() | FlagNoThreading; 109 : : } 110 : : 111 : 0 : QgsLayoutToImageAlgorithm *QgsLayoutToImageAlgorithm::createInstance() const 112 : : { 113 : 0 : return new QgsLayoutToImageAlgorithm(); 114 : : } 115 : : 116 : 0 : QVariantMap QgsLayoutToImageAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) 117 : : { 118 : : // this needs to be done in main thread, layouts are not thread safe 119 : 0 : QgsPrintLayout *l = parameterAsLayout( parameters, QStringLiteral( "LAYOUT" ), context ); 120 : 0 : if ( !l ) 121 : 0 : throw QgsProcessingException( QObject::tr( "Cannot find layout with name \"%1\"" ).arg( parameters.value( QStringLiteral( "LAYOUT" ) ).toString() ) ); 122 : 0 : std::unique_ptr< QgsPrintLayout > layout( l->clone() ); 123 : : 124 : 0 : const QList< QgsMapLayer * > layers = parameterAsLayerList( parameters, QStringLiteral( "LAYERS" ), context ); 125 : 0 : if ( layers.size() > 0 ) 126 : : { 127 : 0 : const QList<QGraphicsItem *> items = layout->items(); 128 : 0 : for ( QGraphicsItem *graphicsItem : items ) 129 : : { 130 : 0 : QgsLayoutItem *item = dynamic_cast<QgsLayoutItem *>( graphicsItem ); 131 : 0 : QgsLayoutItemMap *map = dynamic_cast<QgsLayoutItemMap *>( item ); 132 : 0 : if ( map && !map->followVisibilityPreset() && !map->keepLayerSet() ) 133 : : { 134 : 0 : map->setKeepLayerSet( true ); 135 : 0 : map->setLayers( layers ); 136 : 0 : } 137 : : } 138 : 0 : } 139 : : 140 : 0 : const QString dest = parameterAsFileOutput( parameters, QStringLiteral( "OUTPUT" ), context ); 141 : : 142 : 0 : QgsLayoutExporter exporter( layout.get() ); 143 : 0 : QgsLayoutExporter::ImageExportSettings settings; 144 : : 145 : 0 : if ( parameters.value( QStringLiteral( "DPI" ) ).isValid() ) 146 : : { 147 : 0 : settings.dpi = parameterAsDouble( parameters, QStringLiteral( "DPI" ), context ); 148 : 0 : } 149 : : 150 : 0 : settings.exportMetadata = parameterAsBool( parameters, QStringLiteral( "INCLUDE_METADATA" ), context ); 151 : 0 : settings.generateWorldFile = parameterAsBool( parameters, QStringLiteral( "GEOREFERENCE" ), context ); 152 : : 153 : 0 : if ( parameterAsBool( parameters, QStringLiteral( "ANTIALIAS" ), context ) ) 154 : 0 : settings.flags = settings.flags | QgsLayoutRenderContext::FlagAntialiasing; 155 : : else 156 : 0 : settings.flags = settings.flags & ~QgsLayoutRenderContext::FlagAntialiasing; 157 : : 158 : 0 : switch ( exporter.exportToImage( dest, settings ) ) 159 : : { 160 : : case QgsLayoutExporter::Success: 161 : : { 162 : 0 : feedback->pushInfo( QObject::tr( "Successfully exported layout to %1" ).arg( QDir::toNativeSeparators( dest ) ) ); 163 : 0 : break; 164 : : } 165 : : 166 : : case QgsLayoutExporter::FileError: 167 : 0 : throw QgsProcessingException( QObject::tr( "Cannot write to %1.\n\nThis file may be open in another application." ).arg( QDir::toNativeSeparators( dest ) ) ); 168 : : 169 : : case QgsLayoutExporter::MemoryError: 170 : 0 : throw QgsProcessingException( QObject::tr( "Trying to create the image " 171 : : "resulted in a memory overflow.\n\n" 172 : : "Please try a lower resolution or a smaller paper size." ) ); 173 : : 174 : : case QgsLayoutExporter::SvgLayerError: 175 : : case QgsLayoutExporter::IteratorError: 176 : : case QgsLayoutExporter::Canceled: 177 : : case QgsLayoutExporter::PrintError: 178 : : // no meaning for imageexports, will not be encountered 179 : 0 : break; 180 : : } 181 : : 182 : 0 : feedback->setProgress( 100 ); 183 : : 184 : 0 : QVariantMap outputs; 185 : 0 : outputs.insert( QStringLiteral( "OUTPUT" ), dest ); 186 : 0 : return outputs; 187 : 0 : } 188 : : 189 : : ///@endcond 190 : :