Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Layout export quality jpg #59171

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion python/PyQt6/core/auto_additions/qgslayoutexporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
except NameError:
pass
try:
QgsLayoutExporter.ImageExportSettings.__attribute_docs__ = {'dpi': 'Resolution to export layout at. If dpi <= 0 the default layout dpi will be used.', 'imageSize': "Manual size in pixels for output image. If imageSize is not\nset then it will be automatically calculated based on the\noutput dpi and layout size.\n\nIf cropToContents is ``True`` then imageSize has no effect.\n\nBe careful when specifying manual sizes if pages in the layout\nhave differing sizes! It's likely not going to give a reasonable\noutput in this case, and the automatic dpi-based image size should be\nused instead.", 'cropToContents': 'Set to ``True`` if image should be cropped so only parts of the layout\ncontaining items are exported.', 'cropMargins': 'Crop to content margins, in pixels. These margins will be added\nto the bounds of the exported layout if cropToContents is ``True``.', 'pages': 'List of specific pages to export, or an empty list to\nexport all pages.\n\nPage numbers are 0 index based, so the first page in the\nlayout corresponds to page 0.', 'generateWorldFile': 'Set to ``True`` to generate an external world file alongside\nexported images.', 'exportMetadata': "Indicates whether image export should include metadata generated\nfrom the layout's project's metadata.\n\n.. versionadded:: 3.2", 'flags': 'Layout context flags, which control how the export will be created.', 'predefinedMapScales': 'A list of predefined scales to use with the layout. This is used\nfor maps which are set to the predefined atlas scaling mode.\n\n.. versionadded:: 3.10'}
QgsLayoutExporter.ImageExportSettings.__attribute_docs__ = {'dpi': 'Resolution to export layout at. If dpi <= 0 the default layout dpi will be used.', 'imageSize': "Manual size in pixels for output image. If imageSize is not\nset then it will be automatically calculated based on the\noutput dpi and layout size.\n\nIf cropToContents is ``True`` then imageSize has no effect.\n\nBe careful when specifying manual sizes if pages in the layout\nhave differing sizes! It's likely not going to give a reasonable\noutput in this case, and the automatic dpi-based image size should be\nused instead.", 'cropToContents': 'Set to ``True`` if image should be cropped so only parts of the layout\ncontaining items are exported.', 'cropMargins': 'Crop to content margins, in pixels. These margins will be added\nto the bounds of the exported layout if cropToContents is ``True``.', 'pages': 'List of specific pages to export, or an empty list to\nexport all pages.\n\nPage numbers are 0 index based, so the first page in the\nlayout corresponds to page 0.', 'generateWorldFile': 'Set to ``True`` to generate an external world file alongside\nexported images.', 'exportMetadata': "Indicates whether image export should include metadata generated\nfrom the layout's project's metadata.\n\n.. versionadded:: 3.2", 'flags': 'Layout context flags, which control how the export will be created.', 'predefinedMapScales': 'A list of predefined scales to use with the layout. This is used\nfor maps which are set to the predefined atlas scaling mode.\n\n.. versionadded:: 3.10', 'quality': 'Image quality, typically used for JPEG compression (whose quality ranges from 0 to 100)\nif quality is set to -1, the default quality will be used.\n\n.. versionadded:: 3.42'}
QgsLayoutExporter.ImageExportSettings.__doc__ = """Contains settings relating to exporting layouts to raster images"""
QgsLayoutExporter.ImageExportSettings.__group__ = ['layout']
except NameError:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Handles rendering and exports of layouts to various formats.




struct PageExportDetails
{
QString directory;
Expand Down Expand Up @@ -142,6 +143,9 @@ Returns the rendered image, or a null QImage if the image does not fit into avai

QVector<qreal> predefinedMapScales;


int quality;

};

ExportResult exportToImage( const QString &filePath, const QgsLayoutExporter::ImageExportSettings &settings );
Expand Down
2 changes: 1 addition & 1 deletion python/core/auto_additions/qgslayoutexporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
except NameError:
pass
try:
QgsLayoutExporter.ImageExportSettings.__attribute_docs__ = {'dpi': 'Resolution to export layout at. If dpi <= 0 the default layout dpi will be used.', 'imageSize': "Manual size in pixels for output image. If imageSize is not\nset then it will be automatically calculated based on the\noutput dpi and layout size.\n\nIf cropToContents is ``True`` then imageSize has no effect.\n\nBe careful when specifying manual sizes if pages in the layout\nhave differing sizes! It's likely not going to give a reasonable\noutput in this case, and the automatic dpi-based image size should be\nused instead.", 'cropToContents': 'Set to ``True`` if image should be cropped so only parts of the layout\ncontaining items are exported.', 'cropMargins': 'Crop to content margins, in pixels. These margins will be added\nto the bounds of the exported layout if cropToContents is ``True``.', 'pages': 'List of specific pages to export, or an empty list to\nexport all pages.\n\nPage numbers are 0 index based, so the first page in the\nlayout corresponds to page 0.', 'generateWorldFile': 'Set to ``True`` to generate an external world file alongside\nexported images.', 'exportMetadata': "Indicates whether image export should include metadata generated\nfrom the layout's project's metadata.\n\n.. versionadded:: 3.2", 'flags': 'Layout context flags, which control how the export will be created.', 'predefinedMapScales': 'A list of predefined scales to use with the layout. This is used\nfor maps which are set to the predefined atlas scaling mode.\n\n.. versionadded:: 3.10'}
QgsLayoutExporter.ImageExportSettings.__attribute_docs__ = {'dpi': 'Resolution to export layout at. If dpi <= 0 the default layout dpi will be used.', 'imageSize': "Manual size in pixels for output image. If imageSize is not\nset then it will be automatically calculated based on the\noutput dpi and layout size.\n\nIf cropToContents is ``True`` then imageSize has no effect.\n\nBe careful when specifying manual sizes if pages in the layout\nhave differing sizes! It's likely not going to give a reasonable\noutput in this case, and the automatic dpi-based image size should be\nused instead.", 'cropToContents': 'Set to ``True`` if image should be cropped so only parts of the layout\ncontaining items are exported.', 'cropMargins': 'Crop to content margins, in pixels. These margins will be added\nto the bounds of the exported layout if cropToContents is ``True``.', 'pages': 'List of specific pages to export, or an empty list to\nexport all pages.\n\nPage numbers are 0 index based, so the first page in the\nlayout corresponds to page 0.', 'generateWorldFile': 'Set to ``True`` to generate an external world file alongside\nexported images.', 'exportMetadata': "Indicates whether image export should include metadata generated\nfrom the layout's project's metadata.\n\n.. versionadded:: 3.2", 'flags': 'Layout context flags, which control how the export will be created.', 'predefinedMapScales': 'A list of predefined scales to use with the layout. This is used\nfor maps which are set to the predefined atlas scaling mode.\n\n.. versionadded:: 3.10', 'quality': 'Image quality, typically used for JPEG compression (whose quality ranges from 0 to 100)\nif quality is set to -1, the default quality will be used.\n\n.. versionadded:: 3.42'}
QgsLayoutExporter.ImageExportSettings.__doc__ = """Contains settings relating to exporting layouts to raster images"""
QgsLayoutExporter.ImageExportSettings.__group__ = ['layout']
except NameError:
Expand Down
4 changes: 4 additions & 0 deletions python/core/auto_generated/layout/qgslayoutexporter.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Handles rendering and exports of layouts to various formats.




struct PageExportDetails
{
QString directory;
Expand Down Expand Up @@ -142,6 +143,9 @@ Returns the rendered image, or a null QImage if the image does not fit into avai

QVector<qreal> predefinedMapScales;


int quality;

};

ExportResult exportToImage( const QString &filePath, const QgsLayoutExporter::ImageExportSettings &settings );
Expand Down
17 changes: 12 additions & 5 deletions src/app/layout/qgslayoutdesignerdialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2296,7 +2296,7 @@ void QgsLayoutDesignerDialog::exportToRaster()

QgsLayoutExporter::ImageExportSettings settings;
QSize imageSize;
if ( !getRasterExportSettings( settings, imageSize ) )
if ( !getRasterExportSettings( settings, imageSize, fileNExt.second ) )
return;

mView->setPaintingEnabled( false );
Expand Down Expand Up @@ -2999,7 +2999,7 @@ void QgsLayoutDesignerDialog::exportAtlasToRaster()

QgsLayoutExporter::ImageExportSettings settings;
QSize imageSize;
if ( !getRasterExportSettings( settings, imageSize ) )
if ( !getRasterExportSettings( settings, imageSize, format ) )
return;

mView->setPaintingEnabled( false );
Expand Down Expand Up @@ -3532,7 +3532,7 @@ void QgsLayoutDesignerDialog::exportReportToRaster()

QgsLayoutExporter::ImageExportSettings settings;
QSize imageSize;
if ( !getRasterExportSettings( settings, imageSize ) )
if ( !getRasterExportSettings( settings, imageSize, fileNExt.second ) )
return;

mView->setPaintingEnabled( false );
Expand Down Expand Up @@ -4336,7 +4336,7 @@ bool QgsLayoutDesignerDialog::showFileSizeWarning()
return true;
}

bool QgsLayoutDesignerDialog::getRasterExportSettings( QgsLayoutExporter::ImageExportSettings &settings, QSize &imageSize )
bool QgsLayoutDesignerDialog::getRasterExportSettings( QgsLayoutExporter::ImageExportSettings &settings, QSize &imageSize, const QString &fileExtension )
{
QSizeF maxPageSize;
bool hasUniformPageSizes = false;
Expand Down Expand Up @@ -4366,7 +4366,7 @@ bool QgsLayoutDesignerDialog::getRasterExportSettings( QgsLayoutExporter::ImageE
antialias = mLayout->customProperty( QStringLiteral( "imageAntialias" ), true ).toBool();
}

QgsLayoutImageExportOptionsDialog imageDlg( this );
QgsLayoutImageExportOptionsDialog imageDlg( this, fileExtension );
imageDlg.setImageSize( maxPageSize );
imageDlg.setResolution( dpi );
imageDlg.setCropToContents( cropToContents );
Expand All @@ -4375,6 +4375,7 @@ bool QgsLayoutDesignerDialog::getRasterExportSettings( QgsLayoutExporter::ImageE
imageDlg.setGenerateWorldFile( mLayout->customProperty( QStringLiteral( "exportWorldFile" ), false ).toBool() );
imageDlg.setAntialiasing( antialias );
imageDlg.setOpenAfterExporting( QgsLayoutExporter::settingOpenAfterExportingImage->value() );
imageDlg.setQuality( QgsLayoutExporter::settingImageQuality->value() );

if ( !imageDlg.exec() )
return false;
Expand Down Expand Up @@ -4409,6 +4410,12 @@ bool QgsLayoutDesignerDialog::getRasterExportSettings( QgsLayoutExporter::ImageE
else
settings.flags &= ~QgsLayoutRenderContext::FlagAntialiasing;

settings.quality = imageDlg.quality();
if ( settings.quality != -1 )
{
QgsLayoutExporter::settingImageQuality->setValue( imageDlg.quality() );
}

return true;
}

Expand Down
4 changes: 2 additions & 2 deletions src/app/layout/qgslayoutdesignerdialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -563,8 +563,8 @@ class QgsLayoutDesignerDialog: public QMainWindow, public Ui::QgsLayoutDesignerB
void showRasterizationWarning();
void showForceVectorWarning();

bool showFileSizeWarning();
bool getRasterExportSettings( QgsLayoutExporter::ImageExportSettings &settings, QSize &imageSize );
bool showFileSizeWarning() ;
bool getRasterExportSettings( QgsLayoutExporter::ImageExportSettings &settings, QSize &imageSize, const QString &fileExtension = QString() );
bool getSvgExportSettings( QgsLayoutExporter::SvgExportSettings &settings );
bool getPdfExportSettings( QgsLayoutExporter::PdfExportSettings &settings, bool allowGeospatialPdfExport = true, const QString &geospatialPdfReason = QString() );

Expand Down
9 changes: 7 additions & 2 deletions src/core/layout/qgslayoutexporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ class LayoutItemHider
const QgsSettingsEntryBool *QgsLayoutExporter::settingOpenAfterExportingImage = new QgsSettingsEntryBool( QStringLiteral( "open-after-exporting-image" ), QgsSettingsTree::sTreeLayout, false, QObject::tr( "Whether to open the exported image file with the default viewer after exporting a print layout" ) );
const QgsSettingsEntryBool *QgsLayoutExporter::settingOpenAfterExportingPdf = new QgsSettingsEntryBool( QStringLiteral( "open-after-exporting-pdf" ), QgsSettingsTree::sTreeLayout, false, QObject::tr( "Whether to open the exported PDF file with the default viewer after exporting a print layout" ) );
const QgsSettingsEntryBool *QgsLayoutExporter::settingOpenAfterExportingSvg = new QgsSettingsEntryBool( QStringLiteral( "open-after-exporting-svg" ), QgsSettingsTree::sTreeLayout, false, QObject::tr( "Whether to open the exported SVG file with the default viewer after exporting a print layout" ) );
const QgsSettingsEntryInteger *QgsLayoutExporter::settingImageQuality = new QgsSettingsEntryInteger( QStringLiteral( "image-quality" ), QgsSettingsTree::sTreeLayout, 90, QObject::tr( "Image quality for lossy formats (e.g. JPEG)" ) );

QgsLayoutExporter::QgsLayoutExporter( QgsLayout *layout )
: mLayout( layout )
Expand Down Expand Up @@ -456,7 +457,7 @@ QgsLayoutExporter::ExportResult QgsLayoutExporter::exportToImage( const QString
return MemoryError;
}

if ( !saveImage( image, outputFilePath, pageDetails.extension, settings.exportMetadata ? mLayout->project() : nullptr ) )
if ( !saveImage( image, outputFilePath, pageDetails.extension, settings.exportMetadata ? mLayout->project() : nullptr, settings.quality ) )
{
mErrorFileName = outputFilePath;
return FileError;
Expand Down Expand Up @@ -2216,13 +2217,17 @@ void QgsLayoutExporter::captureLabelingResults()
}
}

bool QgsLayoutExporter::saveImage( const QImage &image, const QString &imageFilename, const QString &imageFormat, QgsProject *projectForMetadata )
bool QgsLayoutExporter::saveImage( const QImage &image, const QString &imageFilename, const QString &imageFormat, QgsProject *projectForMetadata, int quality )
{
QImageWriter w( imageFilename, imageFormat.toLocal8Bit().constData() );
if ( imageFormat.compare( QLatin1String( "tiff" ), Qt::CaseInsensitive ) == 0 || imageFormat.compare( QLatin1String( "tif" ), Qt::CaseInsensitive ) == 0 )
{
w.setCompression( 1 ); //use LZW compression
}

// Set the quality for i.e. JPEG images. -1 means default quality.
w.setQuality( quality );

if ( projectForMetadata )
{
w.setText( QStringLiteral( "Author" ), projectForMetadata->metadata().author() );
Expand Down
14 changes: 13 additions & 1 deletion src/core/layout/qgslayoutexporter.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class QgsAbstractLayoutIterator;
class QgsFeedback;
class QgsLabelingResults;
class QgsSettingsEntryBool;
class QgsSettingsEntryInteger;

/**
* \ingroup core
Expand All @@ -61,6 +62,9 @@ class CORE_EXPORT QgsLayoutExporter
//! Settings entry - Whether to automatically open svgs after exporting them \since QGIS 3.34
static const QgsSettingsEntryBool *settingOpenAfterExportingSvg SIP_SKIP;

//! Settings entry - Image quality for lossy formats \since QGIS 3.42
static const QgsSettingsEntryInteger *settingImageQuality SIP_SKIP;

//! Contains details of a page being exported by the class
struct PageExportDetails
{
Expand Down Expand Up @@ -231,6 +235,14 @@ class CORE_EXPORT QgsLayoutExporter
*/
QVector<qreal> predefinedMapScales;


/**
* Image quality, typically used for JPEG compression (whose quality ranges from 0 to 100)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure which formats would accept 0. For JPEG the minimum is 1

* if quality is set to -1, the default quality will be used.
* \since QGIS 3.42
*/
int quality = -1;

};

/**
Expand Down Expand Up @@ -719,7 +731,7 @@ class CORE_EXPORT QgsLayoutExporter
/**
* Saves an image to a file, possibly using format specific options (e.g. LZW compression for tiff)
*/
static bool saveImage( const QImage &image, const QString &imageFilename, const QString &imageFormat, QgsProject *projectForMetadata );
static bool saveImage( const QImage &image, const QString &imageFilename, const QString &imageFormat, QgsProject *projectForMetadata, int quality = -1 );

/**
* Computes a GDAL style geotransform for georeferencing a layout.
Expand Down
42 changes: 40 additions & 2 deletions src/gui/layout/qgslayoutimageexportoptionsdialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@

#include <QCheckBox>
#include <QPushButton>
#include <QSpinBox>
#include <QSlider>

QgsLayoutImageExportOptionsDialog::QgsLayoutImageExportOptionsDialog( QWidget *parent, Qt::WindowFlags flags )
: QDialog( parent, flags )
QgsLayoutImageExportOptionsDialog::QgsLayoutImageExportOptionsDialog( QWidget *parent, const QString &fileExtension, Qt::WindowFlags flags )
: QDialog( parent, flags ), mFileExtension( fileExtension )
{
setupUi( this );
connect( mWidthSpinBox, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), this, &QgsLayoutImageExportOptionsDialog::mWidthSpinBox_valueChanged );
Expand All @@ -34,6 +36,15 @@ QgsLayoutImageExportOptionsDialog::QgsLayoutImageExportOptionsDialog( QWidget *p

connect( mClipToContentGroupBox, &QGroupBox::toggled, this, &QgsLayoutImageExportOptionsDialog::clipToContentsToggled );
connect( mHelpButtonBox, &QDialogButtonBox::helpRequested, this, &QgsLayoutImageExportOptionsDialog::showHelp );

const bool showQuality = shouldShowQuality();
mQualitySpinBox->setVisible( showQuality );
mQualitySlider->setVisible( showQuality );
mQualityLabel->setVisible( showQuality );

connect( mQualitySpinBox, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), mQualitySlider, &QSlider::setValue );
connect( mQualitySlider, &QSlider::valueChanged, mQualitySpinBox, &QSpinBox::setValue );

QgsGui::enableAutoGeometryRestore( this );
}

Expand Down Expand Up @@ -142,6 +153,20 @@ void QgsLayoutImageExportOptionsDialog::setOpenAfterExporting( bool enabled )
mOpenAfterExportingCheckBox->setChecked( enabled );
}

int QgsLayoutImageExportOptionsDialog::quality() const
{
if ( !shouldShowQuality() )
{
return -1;
}
return mQualitySpinBox->value();
}

void QgsLayoutImageExportOptionsDialog::setQuality( int quality )
{
mQualitySpinBox->setValue( quality );
}

void QgsLayoutImageExportOptionsDialog::mWidthSpinBox_valueChanged( int value )
{
mHeightSpinBox->blockSignals( true );
Expand Down Expand Up @@ -201,3 +226,16 @@ void QgsLayoutImageExportOptionsDialog::showHelp()
{
QgsHelp::openHelp( QStringLiteral( "print_composer/create_output.html" ) );
}

bool QgsLayoutImageExportOptionsDialog::shouldShowQuality() const
{
const QStringList validExtensions = { "jpeg", "jpg" };
for ( const QString &ext : validExtensions )
{
if ( mFileExtension.toLower() == ext )
{
return true;
}
}
return false;
}
11 changes: 10 additions & 1 deletion src/gui/layout/qgslayoutimageexportoptionsdialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,10 @@ class GUI_EXPORT QgsLayoutImageExportOptionsDialog: public QDialog, private Ui::
/**
* Constructor for QgsLayoutImageExportOptionsDialog
* \param parent parent widget
* \param fileExtension output image file extension
* \param flags window flags
*/
QgsLayoutImageExportOptionsDialog( QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags() );
QgsLayoutImageExportOptionsDialog( QWidget *parent = nullptr, const QString &fileExtension = QString(), Qt::WindowFlags flags = Qt::WindowFlags() );

/**
* Sets the initial resolution displayed in the dialog.
Expand Down Expand Up @@ -138,6 +139,12 @@ class GUI_EXPORT QgsLayoutImageExportOptionsDialog: public QDialog, private Ui::
//! Returns whether the pdf should be opened after exporting it
bool openAfterExporting() const;


//! Sets the image quality (for JPEG)
void setQuality( int quality );
//! Returns the image quality
int quality() const;

private slots:

void mWidthSpinBox_valueChanged( int value );
Expand All @@ -148,7 +155,9 @@ class GUI_EXPORT QgsLayoutImageExportOptionsDialog: public QDialog, private Ui::

private:

bool shouldShowQuality() const;
QSizeF mImageSize;
QString mFileExtension;


};
Expand Down
Loading
Loading