mirror of
https://github.com/qgis/QGIS.git
synced 2025-10-17 00:09:36 -04:00
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:
parent
853f21842f
commit
ec41cb59b4
@ -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
|
||||
|
@ -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 );
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user