Don't expose an outline subsymbol for SVG fill symbol layers

This is inconsistent with all other fill types, which don't
have a special outline subsymbol. Users should be creating
outline layers when they want an outline instead.

When reading an SVG fill symbol layer from XML, automatically
upgrade any outline subsymbol which is used by the fill to
be separate symbol layers for the parent fill symbol so
that existing symbols will appear as designed in older QGIS
versions.

Fixes qgis#14103
This commit is contained in:
Nyall Dawson 2021-10-23 18:08:18 +10:00
parent 853f21842f
commit ec41cb59b4
4 changed files with 140 additions and 136 deletions

View File

@ -818,11 +818,6 @@ Base class for polygon renderers generating texture images
virtual void renderPolygon( const QPolygonF &points, const QVector<QPolygonF> *rings, QgsSymbolRenderContext &context );
virtual QgsSymbol *subSymbol();
virtual bool setSubSymbol( QgsSymbol *symbol /Transfer/ );
void setStrokeWidthUnit( QgsUnitTypes::RenderUnit unit );
%Docstring
Sets the ``units`` fo the symbol's stroke width.
@ -893,28 +888,19 @@ fill is positioned relative to the feature.
virtual QgsMapUnitScale mapUnitScale() const;
virtual double estimateMaxBleed( const QgsRenderContext &context ) const;
virtual double dxfWidth( const QgsDxfExport &e, QgsSymbolRenderContext &context ) const;
virtual QColor dxfColor( QgsSymbolRenderContext &context ) const;
virtual Qt::PenStyle dxfPenStyle() const;
virtual QSet<QString> usedAttributes( const QgsRenderContext &context ) const;
virtual QVariantMap properties() const;
virtual bool hasDataDefinedProperties() const;
protected:
virtual void applyDataDefinedSettings( QgsSymbolRenderContext &context );
%Docstring
Custom stroke
Applies data defined settings prior to generating the fill symbol brush.
%End
virtual bool applyBrushTransformFromContext( QgsSymbolRenderContext *context = 0 ) const;
@ -1256,6 +1242,8 @@ Used internally when reading/writing symbols.
virtual void stopRender( QgsSymbolRenderContext &context );
virtual void renderPolygon( const QPolygonF &points, const QVector<QPolygonF> *rings, QgsSymbolRenderContext &context );
virtual QVariantMap properties() const;
virtual QgsSVGFillSymbolLayer *clone() const /Factory/;
@ -1264,6 +1252,18 @@ Used internally when reading/writing symbols.
virtual bool usesMapUnits() const;
virtual QgsSymbol *subSymbol();
virtual bool setSubSymbol( QgsSymbol *symbol /Transfer/ );
virtual double estimateMaxBleed( const QgsRenderContext &context ) const;
virtual QColor dxfColor( QgsSymbolRenderContext &context ) const;
virtual QSet<QString> usedAttributes( const QgsRenderContext &context ) const;
virtual bool hasDataDefinedProperties() const;
void setSvgFilePath( const QString &svgPath );
%Docstring

View File

@ -1702,7 +1702,6 @@ QgsMapUnitScale QgsShapeburstFillSymbolLayer::mapUnitScale() const
QgsImageFillSymbolLayer::QgsImageFillSymbolLayer()
{
setSubSymbol( new QgsLineSymbol() );
}
QgsImageFillSymbolLayer::~QgsImageFillSymbolLayer() = default;
@ -1750,51 +1749,10 @@ void QgsImageFillSymbolLayer::renderPolygon( const QPolygonF &points, const QVec
}
p->setBrush( mBrush );
_renderPolygon( p, points, rings, context );
if ( mStroke )
{
mStroke->renderPolyline( points, context.feature(), context.renderContext(), -1, SELECT_FILL_BORDER && context.selected() );
if ( rings )
{
for ( auto ringIt = rings->constBegin(); ringIt != rings->constEnd(); ++ringIt )
{
mStroke->renderPolyline( *ringIt, context.feature(), context.renderContext(), -1, SELECT_FILL_BORDER && context.selected() );
}
}
}
mBrush.setTransform( bkTransform );
}
QgsSymbol *QgsImageFillSymbolLayer::subSymbol()
{
return mStroke.get();
}
bool QgsImageFillSymbolLayer::setSubSymbol( QgsSymbol *symbol )
{
if ( !symbol ) //unset current stroke
{
mStroke.reset( nullptr );
return true;
}
if ( symbol->type() != Qgis::SymbolType::Line )
{
delete symbol;
return false;
}
QgsLineSymbol *lineSymbol = dynamic_cast<QgsLineSymbol *>( symbol );
if ( lineSymbol )
{
mStroke.reset( lineSymbol );
return true;
}
delete symbol;
return false;
}
void QgsImageFillSymbolLayer::setOutputUnit( QgsUnitTypes::RenderUnit unit )
{
mStrokeWidthUnit = unit;
@ -1815,16 +1773,6 @@ QgsMapUnitScale QgsImageFillSymbolLayer::mapUnitScale() const
return mStrokeWidthMapUnitScale;
}
double QgsImageFillSymbolLayer::estimateMaxBleed( const QgsRenderContext &context ) const
{
if ( mStroke && mStroke->symbolLayer( 0 ) )
{
double subLayerBleed = mStroke->symbolLayer( 0 )->estimateMaxBleed( context );
return subLayerBleed;
}
return 0;
}
double QgsImageFillSymbolLayer::dxfWidth( const QgsDxfExport &e, QgsSymbolRenderContext &context ) const
{
double width = mStrokeWidth;
@ -1836,16 +1784,6 @@ double QgsImageFillSymbolLayer::dxfWidth( const QgsDxfExport &e, QgsSymbolRender
return width * e.mapUnitScaleFactor( e.symbologyScale(), mStrokeWidthUnit, e.mapUnits(), context.renderContext().mapToPixel().mapUnitsPerPixel() );
}
QColor QgsImageFillSymbolLayer::dxfColor( QgsSymbolRenderContext &context ) const
{
Q_UNUSED( context )
if ( !mStroke )
{
return QColor( Qt::black );
}
return mStroke->color();
}
Qt::PenStyle QgsImageFillSymbolLayer::dxfPenStyle() const
{
return Qt::SolidLine;
@ -1868,23 +1806,6 @@ QVariantMap QgsImageFillSymbolLayer::properties() const
return map;
}
QSet<QString> QgsImageFillSymbolLayer::usedAttributes( const QgsRenderContext &context ) const
{
QSet<QString> attr = QgsFillSymbolLayer::usedAttributes( context );
if ( mStroke )
attr.unite( mStroke->usedAttributes( context ) );
return attr;
}
bool QgsImageFillSymbolLayer::hasDataDefinedProperties() const
{
if ( QgsSymbolLayer::hasDataDefinedProperties() )
return true;
if ( mStroke && mStroke->hasDataDefinedProperties() )
return true;
return false;
}
bool QgsImageFillSymbolLayer::applyBrushTransformFromContext( QgsSymbolRenderContext *context ) const
{
//coordinate reference
@ -1926,7 +1847,6 @@ QgsSVGFillSymbolLayer::QgsSVGFillSymbolLayer( const QByteArray &svgData, double
mStrokeWidth = 0.3;
mAngle = angle;
mColor = QColor( 255, 255, 255 );
setSubSymbol( new QgsLineSymbol() );
setDefaultSvgParams();
}
@ -1938,7 +1858,8 @@ void QgsSVGFillSymbolLayer::setOutputUnit( QgsUnitTypes::RenderUnit unit )
mPatternWidthUnit = unit;
mSvgStrokeWidthUnit = unit;
mStrokeWidthUnit = unit;
mStroke->setOutputUnit( unit );
if ( mStroke )
mStroke->setOutputUnit( unit );
}
QgsUnitTypes::RenderUnit QgsSVGFillSymbolLayer::outputUnit() const
@ -2177,6 +2098,23 @@ void QgsSVGFillSymbolLayer::stopRender( QgsSymbolRenderContext &context )
}
}
void QgsSVGFillSymbolLayer::renderPolygon( const QPolygonF &points, const QVector<QPolygonF> *rings, QgsSymbolRenderContext &context )
{
QgsImageFillSymbolLayer::renderPolygon( points, rings, context );
if ( mStroke )
{
mStroke->renderPolyline( points, context.feature(), context.renderContext(), -1, SELECT_FILL_BORDER && context.selected() );
if ( rings )
{
for ( auto ringIt = rings->constBegin(); ringIt != rings->constEnd(); ++ringIt )
{
mStroke->renderPolyline( *ringIt, context.feature(), context.renderContext(), -1, SELECT_FILL_BORDER && context.selected() );
}
}
}
}
QVariantMap QgsSVGFillSymbolLayer::properties() const
{
QVariantMap map;
@ -2304,6 +2242,73 @@ bool QgsSVGFillSymbolLayer::usesMapUnits() const
|| mSvgStrokeWidthUnit == QgsUnitTypes::RenderMapUnits || mSvgStrokeWidthUnit == QgsUnitTypes::RenderMetersInMapUnits;
}
QgsSymbol *QgsSVGFillSymbolLayer::subSymbol()
{
return mStroke.get();
}
bool QgsSVGFillSymbolLayer::setSubSymbol( QgsSymbol *symbol )
{
if ( !symbol ) //unset current stroke
{
mStroke.reset( nullptr );
return true;
}
if ( symbol->type() != Qgis::SymbolType::Line )
{
delete symbol;
return false;
}
QgsLineSymbol *lineSymbol = dynamic_cast<QgsLineSymbol *>( symbol );
if ( lineSymbol )
{
mStroke.reset( lineSymbol );
return true;
}
delete symbol;
return false;
}
double QgsSVGFillSymbolLayer::estimateMaxBleed( const QgsRenderContext &context ) const
{
if ( mStroke && mStroke->symbolLayer( 0 ) )
{
double subLayerBleed = mStroke->symbolLayer( 0 )->estimateMaxBleed( context );
return subLayerBleed;
}
return 0;
}
QColor QgsSVGFillSymbolLayer::dxfColor( QgsSymbolRenderContext &context ) const
{
Q_UNUSED( context )
if ( !mStroke )
{
return QColor( Qt::black );
}
return mStroke->color();
}
QSet<QString> QgsSVGFillSymbolLayer::usedAttributes( const QgsRenderContext &context ) const
{
QSet<QString> attr = QgsImageFillSymbolLayer::usedAttributes( context );
if ( mStroke )
attr.unite( mStroke->usedAttributes( context ) );
return attr;
}
bool QgsSVGFillSymbolLayer::hasDataDefinedProperties() const
{
if ( QgsImageFillSymbolLayer::hasDataDefinedProperties() )
return true;
if ( mStroke && mStroke->hasDataDefinedProperties() )
return true;
return false;
}
QgsSymbolLayer *QgsSVGFillSymbolLayer::createFromSld( QDomElement &element )
{
QString path, mimeType;
@ -3561,11 +3566,6 @@ void QgsPointPatternFillSymbolLayer::startRender( QgsSymbolRenderContext &contex
// optimised render for screen only, use image based brush
applyPattern( context, mBrush, mDistanceX, mDistanceY, mDisplacementX, mDisplacementY, mOffsetX, mOffsetY );
}
if ( mStroke )
{
mStroke->startRender( context.renderContext(), context.fields() );
}
}
void QgsPointPatternFillSymbolLayer::stopRender( QgsSymbolRenderContext &context )
@ -3574,11 +3574,6 @@ void QgsPointPatternFillSymbolLayer::stopRender( QgsSymbolRenderContext &context
{
mMarkerSymbol->stopRender( context.renderContext() );
}
if ( mStroke )
{
mStroke->stopRender( context.renderContext() );
}
}
void QgsPointPatternFillSymbolLayer::renderPolygon( const QPolygonF &points, const QVector<QPolygonF> *rings, QgsSymbolRenderContext &context )
@ -3833,18 +3828,6 @@ void QgsPointPatternFillSymbolLayer::renderPolygon( const QPolygonF &points, con
p->restore();
if ( mStroke )
{
mStroke->renderPolyline( points, context.feature(), context.renderContext(), -1, SELECT_FILL_BORDER && context.selected() );
if ( rings )
{
for ( auto ringIt = rings->constBegin(); ringIt != rings->constEnd(); ++ringIt )
{
mStroke->renderPolyline( *ringIt, context.feature(), context.renderContext(), -1, SELECT_FILL_BORDER && context.selected() );
}
}
}
context.renderContext().setFlag( Qgis::RenderContextFlag::RenderingSubSymbol, prevIsSubsymbol );
}

View File

@ -783,9 +783,6 @@ class CORE_EXPORT QgsImageFillSymbolLayer: public QgsFillSymbolLayer
void renderPolygon( const QPolygonF &points, const QVector<QPolygonF> *rings, QgsSymbolRenderContext &context ) override;
QgsSymbol *subSymbol() override;
bool setSubSymbol( QgsSymbol *symbol SIP_TRANSFER ) override;
/**
* Sets the \a units fo the symbol's stroke width.
* \see strokeWidthUnit()
@ -840,13 +837,9 @@ class CORE_EXPORT QgsImageFillSymbolLayer: public QgsFillSymbolLayer
QgsUnitTypes::RenderUnit outputUnit() const override;
void setMapUnitScale( const QgsMapUnitScale &scale ) override;
QgsMapUnitScale mapUnitScale() const override;
double estimateMaxBleed( const QgsRenderContext &context ) const override;
double dxfWidth( const QgsDxfExport &e, QgsSymbolRenderContext &context ) const override;
QColor dxfColor( QgsSymbolRenderContext &context ) const override;
Qt::PenStyle dxfPenStyle() const override;
QSet<QString> usedAttributes( const QgsRenderContext &context ) const override;
QVariantMap properties() const override;
bool hasDataDefinedProperties() const override;
protected:
QBrush mBrush;
@ -858,9 +851,9 @@ class CORE_EXPORT QgsImageFillSymbolLayer: public QgsFillSymbolLayer
QgsUnitTypes::RenderUnit mStrokeWidthUnit = QgsUnitTypes::RenderMillimeters;
QgsMapUnitScale mStrokeWidthMapUnitScale;
//! Custom stroke
std::unique_ptr< QgsLineSymbol > mStroke;
/**
* Applies data defined settings prior to generating the fill symbol brush.
*/
virtual void applyDataDefinedSettings( QgsSymbolRenderContext &context ) { Q_UNUSED( context ) }
/**
@ -1143,10 +1136,17 @@ class CORE_EXPORT QgsSVGFillSymbolLayer: public QgsImageFillSymbolLayer
QString layerType() const override;
void startRender( QgsSymbolRenderContext &context ) override;
void stopRender( QgsSymbolRenderContext &context ) override;
void renderPolygon( const QPolygonF &points, const QVector<QPolygonF> *rings, QgsSymbolRenderContext &context ) override;
QVariantMap properties() const override;
QgsSVGFillSymbolLayer *clone() const override SIP_FACTORY;
void toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const override;
bool usesMapUnits() const override;
QgsSymbol *subSymbol() override;
bool setSubSymbol( QgsSymbol *symbol SIP_TRANSFER ) override;
double estimateMaxBleed( const QgsRenderContext &context ) const override;
QColor dxfColor( QgsSymbolRenderContext &context ) const override;
QSet<QString> usedAttributes( const QgsRenderContext &context ) const override;
bool hasDataDefinedProperties() const override;
/**
* Sets the path to the SVG file to render in the fill.
@ -1372,6 +1372,9 @@ class CORE_EXPORT QgsSVGFillSymbolLayer: public QgsImageFillSymbolLayer
QgsUnitTypes::RenderUnit mSvgStrokeWidthUnit = QgsUnitTypes::RenderMillimeters;
QgsMapUnitScale mSvgStrokeWidthMapUnitScale;
//! Custom stroke -- here for historic reasons only
std::unique_ptr< QgsLineSymbol > mStroke;
//! Helper function that gets the view box from the byte array
void storeViewBox();
void setDefaultSvgParams(); //fills mSvgFillColor, mSvgStrokeColor, mSvgStrokeWidth with default values for mSvgFilePath

View File

@ -1168,22 +1168,40 @@ QgsSymbol *QgsSymbolLayerUtils::loadSymbol( const QDomElement &element, const Qg
}
else
{
QgsSymbolLayer *layer = loadSymbolLayer( e, context );
if ( layer )
if ( QgsSymbolLayer *layer = loadSymbolLayer( e, context ) )
{
// Dealing with sub-symbols nested into a layer
const QDomElement s = e.firstChildElement( QStringLiteral( "symbol" ) );
if ( !s.isNull() )
{
QgsSymbol *subSymbol = loadSymbol( s, context );
const bool res = layer->setSubSymbol( subSymbol );
if ( !res )
std::unique_ptr< QgsSymbol > subSymbol( loadSymbol( s, context ) );
// special handling for SVG fill symbol layer -- upgrade the subsymbol which
// was historically used for the fill stroke to be dedicated symbol layer instead
// in order to match the behavior of all other fill symbol layer types
if ( dynamic_cast< QgsSVGFillSymbolLayer * >( layer ) )
{
QgsDebugMsg( "symbol layer refused subsymbol: " + s.attribute( "name" ) );
// add the SVG fill first
layers.append( layer );
// then add the layers from the subsymbol stroke outline on top
for ( int i = 0; i < subSymbol->symbolLayerCount(); ++i )
{
layers.append( subSymbol->symbolLayer( i )->clone() );
}
}
else
{
const bool res = layer->setSubSymbol( subSymbol.release() );
if ( !res )
{
QgsDebugMsg( QStringLiteral( "symbol layer refused subsymbol: " ) + s.attribute( "name" ) );
}
layers.append( layer );
}
}
layers.append( layer );
else
{
layers.append( layer );
}
}
}
}