From b992e871ee1d51e78f4fe337ed3ec2cc288cd0af Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 12 Dec 2017 17:08:34 +1000 Subject: [PATCH] [layouts][FEATURE] Don't force the whole layout to be rasterized when exporting to PDF If an individual layout item needs rasterisation in order to be exported correctly, it can now be individually rasterised without forcing every other item to also be rasterised. This allows exports to PDF keeping as much as possible as vectors, e.g. a map with layer opacity won't force labels, scalebars, etc to be rasterised too. To accompany this, a new "Always export as vectors" checkbox was added to layout properties. If checked, this will force the export to keep items as vectors, even when it causes the output to look different to layouts. Fixes #7885 --- python/core/layout/qgslayoutcontext.sip | 1 + python/core/layout/qgslayoutexporter.sip | 13 ++++- python/core/layout/qgslayoutitem.sip | 10 ++++ python/core/layout/qgslayoutitempicture.sip | 2 + src/app/layout/qgslayoutdesignerdialog.cpp | 53 ++++++++++++++++--- src/app/layout/qgslayoutdesignerdialog.h | 5 +- src/app/layout/qgslayoutpropertieswidget.cpp | 29 +++++++++++ src/app/layout/qgslayoutpropertieswidget.h | 1 + src/core/layout/qgslayoutcontext.h | 1 + src/core/layout/qgslayoutexporter.cpp | 6 ++- src/core/layout/qgslayoutexporter.h | 15 +++++- src/core/layout/qgslayoutitem.cpp | 55 ++++++++++++++------ src/core/layout/qgslayoutitem.h | 9 ++++ src/core/layout/qgslayoutitemmap.cpp | 45 ++++++++++++---- src/core/layout/qgslayoutitempicture.cpp | 8 +++ src/core/layout/qgslayoutitempicture.h | 1 + src/ui/layout/qgslayoutwidgetbase.ui | 22 ++++++-- 17 files changed, 236 insertions(+), 40 deletions(-) diff --git a/python/core/layout/qgslayoutcontext.sip b/python/core/layout/qgslayoutcontext.sip index f82fa6afd2b..83e71d5a13f 100644 --- a/python/core/layout/qgslayoutcontext.sip +++ b/python/core/layout/qgslayoutcontext.sip @@ -27,6 +27,7 @@ class QgsLayoutContext : QObject FlagOutlineOnly, FlagAntialiasing, FlagUseAdvancedEffects, + FlagForceVectorOutput, }; typedef QFlags Flags; diff --git a/python/core/layout/qgslayoutexporter.sip b/python/core/layout/qgslayoutexporter.sip index 376df0b6803..c4bad687691 100644 --- a/python/core/layout/qgslayoutexporter.sip +++ b/python/core/layout/qgslayoutexporter.sip @@ -212,7 +212,18 @@ Resolution to export layout at. If dpi <= 0 the default layout dpi will be used. bool rasterizeWholeImage; %Docstring -Set to true to force whole layout to be rasterized while exporting + Set to true to force whole layout to be rasterized while exporting. + + This option is mutually exclusive with forceVectorOutput. +%End + + bool forceVectorOutput; +%Docstring + Set to true to force vector object exports, even when the resultant appearance will differ + from the layout. If false, some items may be rasterized in order to maintain their + correct appearance in the output. + + This option is mutually exclusive with rasterizeWholeImage. %End QgsLayoutContext::Flags flags; diff --git a/python/core/layout/qgslayoutitem.sip b/python/core/layout/qgslayoutitem.sip index bd69360c1d4..41048d0595a 100644 --- a/python/core/layout/qgslayoutitem.sip +++ b/python/core/layout/qgslayoutitem.sip @@ -827,6 +827,16 @@ Sets whether the item should be excluded from composer exports and prints. Subclasses should ensure that implemented overrides of this method also check the base class result. + +.. seealso:: :py:func:`requiresRasterization()` + :rtype: bool +%End + + virtual bool requiresRasterization() const; +%Docstring + Returns true if the item is drawn in such a way that forces the whole layout + to be rasterised when exporting to vector formats. +.. seealso:: :py:func:`containsAdvancedEffects()` :rtype: bool %End diff --git a/python/core/layout/qgslayoutitempicture.sip b/python/core/layout/qgslayoutitempicture.sip index 6f3109c0088..93824c58fe1 100644 --- a/python/core/layout/qgslayoutitempicture.sip +++ b/python/core/layout/qgslayoutitempicture.sip @@ -296,6 +296,8 @@ Forces a recalculation of the picture's frame size virtual void refreshDataDefinedProperty( const QgsLayoutObject::DataDefinedProperty property = QgsLayoutObject::AllProperties ); + virtual bool containsAdvancedEffects() const; + signals: void pictureRotationChanged( double newRotation ); diff --git a/src/app/layout/qgslayoutdesignerdialog.cpp b/src/app/layout/qgslayoutdesignerdialog.cpp index 9a03b012629..c6171b7a1bc 100644 --- a/src/app/layout/qgslayoutdesignerdialog.cpp +++ b/src/app/layout/qgslayoutdesignerdialog.cpp @@ -1552,9 +1552,14 @@ void QgsLayoutDesignerDialog::exportToPdf() showWmsPrintingWarning(); } - if ( containsAdvancedEffects() ) + if ( requiresRasterization() ) { - showAdvancedEffectsWarning(); + showRasterizationWarning(); + } + + if ( containsAdvancedEffects() && ( mLayout->customProperty( QStringLiteral( "forceVector" ), false ).toBool() ) ) + { + showForceVectorWarning(); } QgsSettings settings; @@ -1602,7 +1607,8 @@ void QgsLayoutDesignerDialog::exportToPdf() QApplication::setOverrideCursor( Qt::BusyCursor ); QgsLayoutExporter::PdfExportSettings pdfSettings; - pdfSettings.rasteriseWholeImage = mLayout->customProperty( QStringLiteral( "rasterise" ), false ).toBool(); + pdfSettings.rasterizeWholeImage = mLayout->customProperty( QStringLiteral( "rasterise" ), false ).toBool(); + pdfSettings.forceVectorOutput = mLayout->customProperty( QStringLiteral( "forceVector" ), false ).toBool(); QgsLayoutExporter exporter( mLayout ); switch ( exporter.exportToPdf( outputFileName, pdfSettings ) ) @@ -1783,6 +1789,19 @@ void QgsLayoutDesignerDialog::showWmsPrintingWarning() } } +bool QgsLayoutDesignerDialog::requiresRasterization() const +{ + QList< QgsLayoutItem *> items; + mLayout->layoutItems( items ); + + for ( QgsLayoutItem *currentItem : qgis::as_const( items ) ) + { + if ( currentItem->requiresRasterization() ) + return true; + } + return false; +} + bool QgsLayoutDesignerDialog::containsAdvancedEffects() const { QList< QgsLayoutItem *> items; @@ -1796,10 +1815,11 @@ bool QgsLayoutDesignerDialog::containsAdvancedEffects() const return false; } -void QgsLayoutDesignerDialog::showAdvancedEffectsWarning() +void QgsLayoutDesignerDialog::showRasterizationWarning() { - bool rasterize = mLayout->customProperty( QStringLiteral( "rasterise" ), false ).toBool(); - if ( rasterise ) + + if ( mLayout->customProperty( QStringLiteral( "rasterise" ), false ).toBool() || + mLayout->customProperty( QStringLiteral( "forceVector" ), false ).toBool() ) return; QgsMessageViewer *m = new QgsMessageViewer( this, QgsGuiUtils::ModalDialogFlags, false ); @@ -1817,6 +1837,27 @@ void QgsLayoutDesignerDialog::showAdvancedEffectsWarning() delete m; } +void QgsLayoutDesignerDialog::showForceVectorWarning() +{ + QgsSettings settings; + if ( settings.value( QStringLiteral( "LayoutDesigner/hideForceVectorWarning" ), false, QgsSettings::App ).toBool() ) + return; + + QgsMessageViewer *m = new QgsMessageViewer( this, QgsGuiUtils::ModalDialogFlags, false ); + m->setWindowTitle( tr( "Force Vector" ) ); + m->setMessage( tr( "This layout has the \"Always export as vectors\" option enabled, but the layout contains effects such as blend modes or vector layer transparency, which cannot be printed as vectors. The generated file will differ from the layout contents." ), QgsMessageOutput::MessageText ); + m->setCheckBoxText( tr( "Never show this message again" ) ); + m->setCheckBoxState( Qt::Unchecked ); + m->setCheckBoxVisible( true ); + m->showMessage( true ); + + if ( m->checkBoxState() == Qt::Checked ) + { + settings.setValue( QStringLiteral( "LayoutDesigner/hideForceVectorWarning" ), true, QgsSettings::App ); + } + delete m; +} + void QgsLayoutDesignerDialog::selectItems( const QList items ) { for ( QGraphicsItem *item : items ) diff --git a/src/app/layout/qgslayoutdesignerdialog.h b/src/app/layout/qgslayoutdesignerdialog.h index 576df1202b1..639ba748a5e 100644 --- a/src/app/layout/qgslayoutdesignerdialog.h +++ b/src/app/layout/qgslayoutdesignerdialog.h @@ -371,10 +371,13 @@ class QgsLayoutDesignerDialog: public QMainWindow, private Ui::QgsLayoutDesigner void showWmsPrintingWarning(); //! True if the layout contains advanced effects, such as blend modes + bool requiresRasterization() const; + bool containsAdvancedEffects() const; //! Displays a warning because of incompatibility between blend modes and QPrinter - void showAdvancedEffectsWarning(); + void showRasterizationWarning(); + void showForceVectorWarning(); }; #endif // QGSLAYOUTDESIGNERDIALOG_H diff --git a/src/app/layout/qgslayoutpropertieswidget.cpp b/src/app/layout/qgslayoutpropertieswidget.cpp index 4b7ed81f7c2..54d14a9ca18 100644 --- a/src/app/layout/qgslayoutpropertieswidget.cpp +++ b/src/app/layout/qgslayoutpropertieswidget.cpp @@ -59,6 +59,7 @@ QgsLayoutPropertiesWidget::QgsLayoutPropertiesWidget( QWidget *parent, QgsLayout connect( mGenerateWorldFileCheckBox, &QCheckBox::toggled, this, &QgsLayoutPropertiesWidget::worldFileToggled ); connect( mRasterizeCheckBox, &QCheckBox::toggled, this, &QgsLayoutPropertiesWidget::rasteriseToggled ); + connect( mForceVectorCheckBox, &QCheckBox::toggled, this, &QgsLayoutPropertiesWidget::forceVectorToggled ); mTopMarginSpinBox->setValue( topMargin ); mMarginUnitsComboBox->linkToWidget( mTopMarginSpinBox ); @@ -93,6 +94,19 @@ void QgsLayoutPropertiesWidget::updateGui() bool rasterise = mLayout->customProperty( QStringLiteral( "rasterise" ), false ).toBool(); whileBlocking( mRasterizeCheckBox )->setChecked( rasterise ); + + bool forceVectors = mLayout->customProperty( QStringLiteral( "forceVector" ), false ).toBool(); + whileBlocking( mForceVectorCheckBox )->setChecked( forceVectors ); + + if ( rasterise ) + { + mForceVectorCheckBox->setChecked( false ); + mForceVectorCheckBox->setEnabled( false ); + } + else + { + mForceVectorCheckBox->setEnabled( true ); + } } void QgsLayoutPropertiesWidget::updateSnappingElements() @@ -197,6 +211,21 @@ void QgsLayoutPropertiesWidget::worldFileToggled() void QgsLayoutPropertiesWidget::rasteriseToggled() { mLayout->setCustomProperty( QStringLiteral( "rasterise" ), mRasterizeCheckBox->isChecked() ); + + if ( mRasterizeCheckBox->isChecked() ) + { + mForceVectorCheckBox->setChecked( false ); + mForceVectorCheckBox->setEnabled( false ); + } + else + { + mForceVectorCheckBox->setEnabled( true ); + } +} + +void QgsLayoutPropertiesWidget::forceVectorToggled() +{ + mLayout->setCustomProperty( QStringLiteral( "forceVector" ), mForceVectorCheckBox->isChecked() ); } void QgsLayoutPropertiesWidget::blockSignals( bool block ) diff --git a/src/app/layout/qgslayoutpropertieswidget.h b/src/app/layout/qgslayoutpropertieswidget.h index 426f947e964..e65bdabed0b 100644 --- a/src/app/layout/qgslayoutpropertieswidget.h +++ b/src/app/layout/qgslayoutpropertieswidget.h @@ -47,6 +47,7 @@ class QgsLayoutPropertiesWidget: public QgsPanelWidget, private Ui::QgsLayoutWid void dpiChanged( int value ); void worldFileToggled(); void rasteriseToggled(); + void forceVectorToggled(); private: diff --git a/src/core/layout/qgslayoutcontext.h b/src/core/layout/qgslayoutcontext.h index 3ba94884265..6b9028302d3 100644 --- a/src/core/layout/qgslayoutcontext.h +++ b/src/core/layout/qgslayoutcontext.h @@ -45,6 +45,7 @@ class CORE_EXPORT QgsLayoutContext : public QObject FlagOutlineOnly = 1 << 2, //!< Render items as outlines only. FlagAntialiasing = 1 << 3, //!< Use antialiasing when drawing items. FlagUseAdvancedEffects = 1 << 4, //!< Enable advanced effects such as blend modes. + FlagForceVectorOutput = 1 << 5, //!< Force output in vector format where possible, even if items require rasterization to keep their correct appearance. }; Q_DECLARE_FLAGS( Flags, Flag ) diff --git a/src/core/layout/qgslayoutexporter.cpp b/src/core/layout/qgslayoutexporter.cpp index 66c9894b46d..7188494e7c9 100644 --- a/src/core/layout/qgslayoutexporter.cpp +++ b/src/core/layout/qgslayoutexporter.cpp @@ -310,7 +310,9 @@ QgsLayoutExporter::ExportResult QgsLayoutExporter::exportToPdf( const QString &f // If we are not printing as raster, temporarily disable advanced effects // as QPrinter does not support composition modes and can result // in items missing from the output - mLayout->context().setFlag( QgsLayoutContext::FlagUseAdvancedEffects, settings.rasteriseWholeImage ); + mLayout->context().setFlag( QgsLayoutContext::FlagUseAdvancedEffects, !settings.forceVectorOutput ); + + mLayout->context().setFlag( QgsLayoutContext::FlagForceVectorOutput, settings.forceVectorOutput ); QPrinter printer; preparePrintAsPdf( printer, filePath ); @@ -322,7 +324,7 @@ QgsLayoutExporter::ExportResult QgsLayoutExporter::exportToPdf( const QString &f return PrintError; } - ExportResult result = printPrivate( printer, p, false, settings.dpi, settings.rasteriseWholeImage ); + ExportResult result = printPrivate( printer, p, false, settings.dpi, settings.rasterizeWholeImage ); p.end(); #if 0//TODO diff --git a/src/core/layout/qgslayoutexporter.h b/src/core/layout/qgslayoutexporter.h index 1fd67344322..0aae845a8da 100644 --- a/src/core/layout/qgslayoutexporter.h +++ b/src/core/layout/qgslayoutexporter.h @@ -216,9 +216,22 @@ class CORE_EXPORT QgsLayoutExporter //! Resolution to export layout at. If dpi <= 0 the default layout dpi will be used. double dpi = -1; - //! Set to true to force whole layout to be rasterized while exporting + /** + * Set to true to force whole layout to be rasterized while exporting. + * + * This option is mutually exclusive with forceVectorOutput. + */ bool rasterizeWholeImage = false; + /** + * Set to true to force vector object exports, even when the resultant appearance will differ + * from the layout. If false, some items may be rasterized in order to maintain their + * correct appearance in the output. + * + * This option is mutually exclusive with rasterizeWholeImage. + */ + bool forceVectorOutput = false; + /** * Layout context flags, which control how the export will be created. */ diff --git a/src/core/layout/qgslayoutitem.cpp b/src/core/layout/qgslayoutitem.cpp index f3ffd38d0fa..bb141e0d6cf 100644 --- a/src/core/layout/qgslayoutitem.cpp +++ b/src/core/layout/qgslayoutitem.cpp @@ -242,18 +242,32 @@ void QgsLayoutItem::paint( QPainter *painter, const QStyleOptionGraphicsItem *it return; } - double destinationDpi = itemStyle->matrix.m11() * 25.4; + bool previewRender = !mLayout || mLayout->context().isPreviewRender(); + double destinationDpi = previewRender ? itemStyle->matrix.m11() * 25.4 : mLayout->context().dpi(); bool useImageCache = false; + bool forceRasterOutput = containsAdvancedEffects() && ( !mLayout || !( mLayout->context().flags() & QgsLayoutContext::FlagForceVectorOutput ) ); - if ( useImageCache ) + if ( useImageCache || forceRasterOutput ) { - double widthInPixels = boundingRect().width() * itemStyle->matrix.m11(); - double heightInPixels = boundingRect().height() * itemStyle->matrix.m11(); + double widthInPixels = 0; + double heightInPixels = 0; + + if ( previewRender ) + { + widthInPixels = boundingRect().width() * itemStyle->matrix.m11(); + heightInPixels = boundingRect().height() * itemStyle->matrix.m11(); + } + else + { + double layoutUnitsToPixels = mLayout ? mLayout->convertFromLayoutUnits( 1, QgsUnitTypes::LayoutPixels ).length() : destinationDpi / 25.4; + widthInPixels = boundingRect().width() * layoutUnitsToPixels; + heightInPixels = boundingRect().height() * layoutUnitsToPixels; + } // limit size of image for better performance - double scale = 1.0; - if ( widthInPixels > CACHE_SIZE_LIMIT || heightInPixels > CACHE_SIZE_LIMIT ) + if ( previewRender && ( widthInPixels > CACHE_SIZE_LIMIT || heightInPixels > CACHE_SIZE_LIMIT ) ) { + double scale = 1.0; if ( widthInPixels > heightInPixels ) { scale = widthInPixels / CACHE_SIZE_LIMIT; @@ -269,7 +283,7 @@ void QgsLayoutItem::paint( QPainter *painter, const QStyleOptionGraphicsItem *it destinationDpi = destinationDpi / scale; } - if ( !mItemCachedImage.isNull() && qgsDoubleNear( mItemCacheDpi, destinationDpi ) ) + if ( previewRender && !mItemCachedImage.isNull() && qgsDoubleNear( mItemCacheDpi, destinationDpi ) ) { // can reuse last cached image QgsRenderContext context = QgsLayoutUtils::createRenderContextForMap( nullptr, painter, destinationDpi ); @@ -284,13 +298,11 @@ void QgsLayoutItem::paint( QPainter *painter, const QStyleOptionGraphicsItem *it } else { - mItemCacheDpi = destinationDpi; - - mItemCachedImage = QImage( widthInPixels, heightInPixels, QImage::Format_ARGB32 ); - mItemCachedImage.fill( Qt::transparent ); - mItemCachedImage.setDotsPerMeterX( 1000 * destinationDpi * 25.4 ); - mItemCachedImage.setDotsPerMeterY( 1000 * destinationDpi * 25.4 ); - QPainter p( &mItemCachedImage ); + QImage image = QImage( widthInPixels, heightInPixels, QImage::Format_ARGB32 ); + image.fill( Qt::transparent ); + image.setDotsPerMeterX( 1000 * destinationDpi * 25.4 ); + image.setDotsPerMeterY( 1000 * destinationDpi * 25.4 ); + QPainter p( &image ); preparePainter( &p ); QgsRenderContext context = QgsLayoutUtils::createRenderContextForLayout( nullptr, &p, destinationDpi ); @@ -306,8 +318,14 @@ void QgsLayoutItem::paint( QPainter *painter, const QStyleOptionGraphicsItem *it // scale painter from mm to dots painter->scale( 1.0 / context.scaleFactor(), 1.0 / context.scaleFactor() ); painter->drawImage( boundingRect().x() * context.scaleFactor(), - boundingRect().y() * context.scaleFactor(), mItemCachedImage ); + boundingRect().y() * context.scaleFactor(), image ); painter->restore(); + + if ( previewRender ) + { + mItemCacheDpi = destinationDpi; + mItemCachedImage = image; + } } } else @@ -842,7 +860,12 @@ void QgsLayoutItem::setExcludeFromExports( bool exclude ) bool QgsLayoutItem::containsAdvancedEffects() const { - return blendMode() != QPainter::CompositionMode_SourceOver; + return false; +} + +bool QgsLayoutItem::requiresRasterization() const +{ + return itemOpacity() < 1.0 || blendMode() != QPainter::CompositionMode_SourceOver; } double QgsLayoutItem::estimatedFrameBleed() const diff --git a/src/core/layout/qgslayoutitem.h b/src/core/layout/qgslayoutitem.h index 56167ec5805..8c75460d8ef 100644 --- a/src/core/layout/qgslayoutitem.h +++ b/src/core/layout/qgslayoutitem.h @@ -748,9 +748,18 @@ class CORE_EXPORT QgsLayoutItem : public QgsLayoutObject, public QGraphicsRectIt * * Subclasses should ensure that implemented overrides of this method * also check the base class result. + * + * \see requiresRasterization() */ virtual bool containsAdvancedEffects() const; + /** + * Returns true if the item is drawn in such a way that forces the whole layout + * to be rasterised when exporting to vector formats. + * \see containsAdvancedEffects() + */ + virtual bool requiresRasterization() const; + /** * Returns the estimated amount the item's frame bleeds outside the item's * actual rectangle. For instance, if the item has a 2mm frame stroke, then diff --git a/src/core/layout/qgslayoutitemmap.cpp b/src/core/layout/qgslayoutitemmap.cpp index 38cc7adc44c..1caeaf3e6e8 100644 --- a/src/core/layout/qgslayoutitemmap.cpp +++ b/src/core/layout/qgslayoutitemmap.cpp @@ -798,19 +798,46 @@ void QgsLayoutItemMap::paint( QPainter *painter, const QStyleOptionGraphicsItem } QgsRectangle cExtent = extent(); - QSizeF size( cExtent.width() * mapUnitsToLayoutUnits(), cExtent.height() * mapUnitsToLayoutUnits() ); - painter->save(); - painter->translate( mXOffset, mYOffset ); + if ( containsAdvancedEffects() && ( !mLayout || !( mLayout->context().flags() & QgsLayoutContext::FlagForceVectorOutput ) ) ) + { + // rasterise + double destinationDpi = mLayout ? mLayout->context().dpi() : style->matrix.m11() * 25.4; - double dotsPerMM = paintDevice->logicalDpiX() / 25.4; - size *= dotsPerMM; // output size will be in dots (pixels) - painter->scale( 1 / dotsPerMM, 1 / dotsPerMM ); // scale painter from mm to dots - drawMap( painter, cExtent, size, paintDevice->logicalDpiX() ); + double layoutUnitsToPixels = mLayout ? mLayout->convertFromLayoutUnits( 1, QgsUnitTypes::LayoutPixels ).length() : destinationDpi / 25.4; + double widthInPixels = boundingRect().width() * layoutUnitsToPixels; + double heightInPixels = boundingRect().height() * layoutUnitsToPixels; + QImage image = QImage( widthInPixels, heightInPixels, QImage::Format_ARGB32 ); + + image.fill( Qt::transparent ); + image.setDotsPerMeterX( 1000 * destinationDpi / 25.4 ); + image.setDotsPerMeterY( 1000 * destinationDpi / 25.4 ); + QPainter p( &image ); + double dotsPerMM = image.logicalDpiX() / 25.4; + drawMap( &p, cExtent, image.size(), destinationDpi ); + p.end(); + + dotsPerMM = paintDevice->logicalDpiX() / 25.4; + painter->save(); + painter->scale( 1 / dotsPerMM, 1 / dotsPerMM ); // scale painter from mm to dots + painter->drawImage( 0, 0, image ); + painter->restore(); + + } + else + { + painter->save(); + painter->translate( mXOffset, mYOffset ); + + double dotsPerMM = paintDevice->logicalDpiX() / 25.4; + size *= dotsPerMM; // output size will be in dots (pixels) + painter->scale( 1 / dotsPerMM, 1 / dotsPerMM ); // scale painter from mm to dots + drawMap( painter, cExtent, size, paintDevice->logicalDpiX() ); + + painter->restore(); + } - //restore rotation - painter->restore(); mDrawing = false; } diff --git a/src/core/layout/qgslayoutitempicture.cpp b/src/core/layout/qgslayoutitempicture.cpp index 6715cb64d1c..3ee42d684d5 100644 --- a/src/core/layout/qgslayoutitempicture.cpp +++ b/src/core/layout/qgslayoutitempicture.cpp @@ -666,6 +666,14 @@ void QgsLayoutItemPicture::refreshDataDefinedProperty( const QgsLayoutObject::Da QgsLayoutItem::refreshDataDefinedProperty( property ); } +bool QgsLayoutItemPicture::containsAdvancedEffects() const +{ + if ( QgsLayoutItem::containsAdvancedEffects() ) + return true; + + return mMode == FormatSVG && itemOpacity() < 1.0; +} + void QgsLayoutItemPicture::setPicturePath( const QString &path ) { mSourcePath = path; diff --git a/src/core/layout/qgslayoutitempicture.h b/src/core/layout/qgslayoutitempicture.h index 179044fbfbc..22053f905f7 100644 --- a/src/core/layout/qgslayoutitempicture.h +++ b/src/core/layout/qgslayoutitempicture.h @@ -265,6 +265,7 @@ class CORE_EXPORT QgsLayoutItemPicture: public QgsLayoutItem void recalculateSize(); void refreshDataDefinedProperty( const QgsLayoutObject::DataDefinedProperty property = QgsLayoutObject::AllProperties ) override; + bool containsAdvancedEffects() const override; signals: //! Is emitted on picture rotation change diff --git a/src/ui/layout/qgslayoutwidgetbase.ui b/src/ui/layout/qgslayoutwidgetbase.ui index 724b7803dac..0bb37df5afc 100644 --- a/src/ui/layout/qgslayoutwidgetbase.ui +++ b/src/ui/layout/qgslayoutwidgetbase.ui @@ -53,9 +53,9 @@ 0 - 0 + -316 297 - 778 + 810 @@ -210,7 +210,7 @@ Export settings - + @@ -251,8 +251,21 @@ + + If checked, exports from this layout will be rasterized. + - Export as raster + Print as raster + + + + + + + If checked, the layout will always be kept as vector objects when exported to a compatible format, even if the appearance of the resultant file does not match the layouts settings. If unchecked, some elements in the layout may be rasterised in order to keep their appearance intact. + + + Always export as vectors @@ -445,6 +458,7 @@ mSnapToleranceSpinBox mResolutionSpinBox mRasterizeCheckBox + mForceVectorCheckBox mGenerateWorldFileCheckBox mMarginUnitsComboBox mTopMarginSpinBox