Skip to content

Commit

Permalink
[FEATURE][processing] Add algorithms to export a print layout as PDF …
Browse files Browse the repository at this point in the history
…or image

This allows for models which export print layouts from the current project. One
use case for this is allowing users to create an in-project model which exports
a particular set of layouts from the project to certain folders, so that they
can easily re-export the current project in a single operation instead of
having to manually open multiple layouts and export one-by-one.

Additionally, with the new capabilities to have expression based output files
inside models, you can automatically export the layouts to a folder with
the current date tag and include this in the exported file names!
  • Loading branch information
nyalldawson committed Jun 29, 2020
1 parent e30c4c4 commit f75a9d2
Show file tree
Hide file tree
Showing 6 changed files with 470 additions and 0 deletions.
3 changes: 3 additions & 0 deletions src/analysis/CMakeLists.txt
Expand Up @@ -88,6 +88,8 @@ SET(QGIS_ANALYSIS_SRCS
processing/qgsalgorithmjoinbynearest.cpp
processing/qgsalgorithmjoinwithlines.cpp
processing/qgsalgorithmkmeansclustering.cpp
processing/qgsalgorithmlayouttoimage.cpp
processing/qgsalgorithmlayouttopdf.cpp
processing/qgsalgorithmlinedensity.cpp
processing/qgsalgorithmlineintersection.cpp
processing/qgsalgorithmlinesubstring.cpp
Expand Down Expand Up @@ -409,6 +411,7 @@ INCLUDE_DIRECTORIES(
${CMAKE_SOURCE_DIR}/src/core/labeling
${CMAKE_SOURCE_DIR}/src/core/processing
${CMAKE_SOURCE_DIR}/src/core/raster
${CMAKE_SOURCE_DIR}/src/core/layout
${CMAKE_SOURCE_DIR}/src/core/mesh
${CMAKE_SOURCE_DIR}/src/core/providers/meshmemory
${CMAKE_SOURCE_DIR}/src/core/symbology
Expand Down
168 changes: 168 additions & 0 deletions src/analysis/processing/qgsalgorithmlayouttoimage.cpp
@@ -0,0 +1,168 @@
/***************************************************************************
qgsalgorithmlayouttoimage.cpp
---------------------
begin : June 2020
copyright : (C) 2020 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************/

/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#include "qgsalgorithmlayouttoimage.h"
#include "qgslayout.h"
#include "qgsprintlayout.h"
#include "qgsprocessingoutputs.h"
#include "qgslayoutexporter.h"
#include <QImageWriter>

///@cond PRIVATE

QString QgsLayoutToImageAlgorithm::name() const
{
return QStringLiteral( "printlayouttoimage" );
}

QString QgsLayoutToImageAlgorithm::displayName() const
{
return QObject::tr( "Export print layout as image" );
}

QStringList QgsLayoutToImageAlgorithm::tags() const
{
return QObject::tr( "layout,composer,composition,save,png,jpeg,jpg" ).split( ',' );
}

QString QgsLayoutToImageAlgorithm::group() const
{
return QObject::tr( "Cartography" );
}

QString QgsLayoutToImageAlgorithm::groupId() const
{
return QStringLiteral( "cartography" );
}

QString QgsLayoutToImageAlgorithm::shortDescription() const
{
return QObject::tr( "Exports a print layout as an image." );
}

QString QgsLayoutToImageAlgorithm::shortHelpString() const
{
return QObject::tr( "This algorithm outputs a print layout as an image file (e.g. PNG or JPEG images)." );
}

void QgsLayoutToImageAlgorithm::initAlgorithm( const QVariantMap & )
{
addParameter( new QgsProcessingParameterLayout( QStringLiteral( "LAYOUT" ), QObject::tr( "Print layout" ) ) );

std::unique_ptr< QgsProcessingParameterNumber > dpiParam = qgis::make_unique< QgsProcessingParameterNumber >( QStringLiteral( "DPI" ), QObject::tr( "DPI (leave blank for default layout DPI)" ), QgsProcessingParameterNumber::Double, QVariant(), true, 0 );
dpiParam->setFlags( dpiParam->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
addParameter( dpiParam.release() );

std::unique_ptr< QgsProcessingParameterBoolean > appendGeorefParam = qgis::make_unique< QgsProcessingParameterBoolean >( QStringLiteral( "GEOREFERENCE" ), QObject::tr( "Generate world file" ), true );
appendGeorefParam->setFlags( appendGeorefParam->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
addParameter( appendGeorefParam.release() );

std::unique_ptr< QgsProcessingParameterBoolean > exportRDFParam = qgis::make_unique< QgsProcessingParameterBoolean >( QStringLiteral( "INCLUDE_METADATA" ), QObject::tr( "Export RDF metadata (title, author, etc.)" ), true );
exportRDFParam->setFlags( exportRDFParam->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
addParameter( exportRDFParam.release() );

std::unique_ptr< QgsProcessingParameterBoolean > antialias = qgis::make_unique< QgsProcessingParameterBoolean >( QStringLiteral( "ANTIALIAS" ), QObject::tr( "Enable antialiasing" ), true );
antialias->setFlags( antialias->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
addParameter( antialias.release() );

QStringList imageFilters;
const auto supportedImageFormats { QImageWriter::supportedImageFormats() };
for ( const QByteArray &format : supportedImageFormats )
{
if ( format == "svg" )
continue;

QString longName = format.toUpper() + QObject::tr( " format" );
QString glob = "*." + format;

if ( format == "png" && !imageFilters.empty() )
imageFilters.insert( 0, QStringLiteral( "%1 (%2 %3)" ).arg( longName, glob.toLower(), glob.toUpper() ) );
else
imageFilters.append( QStringLiteral( "%1 (%2 %3)" ).arg( longName, glob.toLower(), glob.toUpper() ) );
}

addParameter( new QgsProcessingParameterFileDestination( QStringLiteral( "OUTPUT" ), QObject::tr( "Image file" ), imageFilters.join( QStringLiteral( ";;" ) ) ) );
}

QgsProcessingAlgorithm::Flags QgsLayoutToImageAlgorithm::flags() const
{
return QgsProcessingAlgorithm::flags() | FlagNoThreading;
}

QgsLayoutToImageAlgorithm *QgsLayoutToImageAlgorithm::createInstance() const
{
return new QgsLayoutToImageAlgorithm();
}

QVariantMap QgsLayoutToImageAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
// this needs to be done in main thread, layouts are not thread safe
QgsPrintLayout *layout = parameterAsLayout( parameters, QStringLiteral( "LAYOUT" ), context );
if ( !layout )
throw QgsProcessingException( QObject::tr( "Cannot find layout with name \"%1\"" ).arg( parameters.value( QStringLiteral( "LAYOUT" ) ).toString() ) );

const QString dest = parameterAsFileOutput( parameters, QStringLiteral( "OUTPUT" ), context );

QgsLayoutExporter exporter( layout );
QgsLayoutExporter::ImageExportSettings settings;

if ( parameters.value( QStringLiteral( "DPI" ) ).isValid() )
{
settings.dpi = parameterAsDouble( parameters, QStringLiteral( "DPI" ), context );
}

settings.exportMetadata = parameterAsBool( parameters, QStringLiteral( "INCLUDE_METADATA" ), context );
settings.generateWorldFile = parameterAsBool( parameters, QStringLiteral( "GEOREFERENCE" ), context );

if ( parameterAsBool( parameters, QStringLiteral( "ANTIALIAS" ), context ) )
settings.flags = settings.flags | QgsLayoutRenderContext::FlagAntialiasing;
else
settings.flags = settings.flags & ~QgsLayoutRenderContext::FlagAntialiasing;

switch ( exporter.exportToImage( dest, settings ) )
{
case QgsLayoutExporter::Success:
{
feedback->pushInfo( QObject::tr( "Successfully exported layout to %1" ).arg( QDir::toNativeSeparators( dest ) ) );
break;
}

case QgsLayoutExporter::FileError:
throw QgsProcessingException( QObject::tr( "Cannot write to %1.\n\nThis file may be open in another application." ).arg( QDir::toNativeSeparators( dest ) ) );

case QgsLayoutExporter::MemoryError:
throw QgsProcessingException( QObject::tr( "Trying to create the image "
"resulted in a memory overflow.\n\n"
"Please try a lower resolution or a smaller paper size." ) );

case QgsLayoutExporter::SvgLayerError:
case QgsLayoutExporter::IteratorError:
case QgsLayoutExporter::Canceled:
case QgsLayoutExporter::PrintError:
// no meaning for imageexports, will not be encountered
break;
}

feedback->setProgress( 100 );

QVariantMap outputs;
outputs.insert( QStringLiteral( "OUTPUT" ), dest );
return outputs;
}

///@endcond

60 changes: 60 additions & 0 deletions src/analysis/processing/qgsalgorithmlayouttoimage.h
@@ -0,0 +1,60 @@
/***************************************************************************
qgsalgorithmlayouttoimage.h
---------------------
begin : June 2020
copyright : (C) 2020 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************/

/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#ifndef QGSALGORITHMLAYOUTTOIMAGE_H
#define QGSALGORITHMLAYOUTTOIMAGE_H

#define SIP_NO_FILE

#include "qgis_sip.h"
#include "qgsprocessingalgorithm.h"

///@cond PRIVATE

/**
* Native export layout to image algorithm.
*/
class QgsLayoutToImageAlgorithm : public QgsProcessingAlgorithm
{

public:

QgsLayoutToImageAlgorithm() = default;
void initAlgorithm( const QVariantMap &configuration = QVariantMap() ) override;
Flags flags() const override;
QString name() const override;
QString displayName() const override;
QStringList tags() const override;
QString group() const override;
QString groupId() const override;
QString shortDescription() const override;
QString shortHelpString() const override;
QgsLayoutToImageAlgorithm *createInstance() const override SIP_FACTORY;

protected:

QVariantMap processAlgorithm( const QVariantMap &parameters,
QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;


};

///@endcond PRIVATE

#endif // QGSALGORITHMLAYOUTTOIMAGE_H


0 comments on commit f75a9d2

Please sign in to comment.