Skip to content
Permalink
Browse files

[FEATURE][processing] Add algorithms to export a print layout as PDF …

…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 3, 2020
1 parent e30c4c4 commit f75a9d21d5aa98221d2d32a7db1321000ba56c2c
@@ -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
@@ -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
@@ -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

@@ -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.
You can’t perform that action at this time.