Skip to content

Commit

Permalink
Add export quality slider for jpeg/jpg
Browse files Browse the repository at this point in the history
  • Loading branch information
YoannQDQ authored Oct 25, 2024
1 parent 258b688 commit e8e55cd
Show file tree
Hide file tree
Showing 11 changed files with 170 additions and 47 deletions.
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 1 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 1 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
2 changes: 1 addition & 1 deletion src/app/layout/qgslayoutdesignerdialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -564,7 +564,7 @@ class QgsLayoutDesignerDialog: public QMainWindow, public Ui::QgsLayoutDesignerB
void showForceVectorWarning();

bool showFileSizeWarning();
bool getRasterExportSettings( QgsLayoutExporter::ImageExportSettings &settings, QSize &imageSize );
bool getRasterExportSettings( QgsLayoutExporter::ImageExportSettings &settings, QSize &imageSize, const QString &fileExtension );
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 1 to 100)
* 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: 41 additions & 1 deletion src/gui/layout/qgslayoutimageexportoptionsdialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,12 @@

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

QgsLayoutImageExportOptionsDialog::QgsLayoutImageExportOptionsDialog( QWidget *parent, Qt::WindowFlags 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 +37,16 @@ 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 );
mQualityLabel->setText( tr( "%1 quality", "Image format" ).arg( mFileExtension.toUpper() ) );

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

QgsGui::enableAutoGeometryRestore( this );
}

Expand Down Expand Up @@ -142,6 +155,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 +228,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

0 comments on commit e8e55cd

Please sign in to comment.