Skip to content
Permalink
Browse files

Merge pull request #40596 from 3nids/dynamic_svgs

core part to handle dynamic SVGs
  • Loading branch information
3nids committed Jan 7, 2021
2 parents 0f68ad8 + 5e2cbab commit 17500a7ee774c10e2d4cd3387f0405deb610784d
@@ -656,6 +656,20 @@ if the value set is lower or equal to 0 the aspect ratio will be preserved in re
double strokeWidth() const;
void setStrokeWidth( double w );

QMap<QString, QgsProperty> parameters() const;
%Docstring
Returns the dynamic SVG parameters

.. versionadded:: 3.18
%End

void setParameters( const QMap<QString, QgsProperty> &parameters );
%Docstring
Sets the dynamic SVG parameters

.. versionadded:: 3.18
%End

void setStrokeWidthUnit( QgsUnitTypes::RenderUnit unit );
%Docstring
Sets the units for the stroke width.
@@ -691,6 +705,12 @@ Returns the units for the stroke width.
virtual QRectF bounds( QPointF point, QgsSymbolRenderContext &context );


virtual void prepareExpressions( const QgsSymbolRenderContext &context );


virtual QSet<QString> usedAttributes( const QgsRenderContext &context ) const;


protected:

double calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const;
@@ -48,7 +48,8 @@ Constructor for QgsSvgCache.
%End

QImage svgAsImage( const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth,
double widthScaleFactor, bool &fitsInCache, double fixedAspectRatio = 0, bool blocking = false );
double widthScaleFactor, bool &fitsInCache, double fixedAspectRatio = 0, bool blocking = false,
const QMap<QString, QString> &parameters = QMap<QString, QString>() );
%Docstring
Returns an SVG drawing as a QImage.

@@ -61,6 +62,7 @@ Returns an SVG drawing as a QImage.
:param fitsInCache:
:param fixedAspectRatio: fixed aspect ratio (optional)
:param blocking: forces to wait for loading before returning image (optional).
:param parameters: is a map of parameters to dynamically replace content in SVG.

.. warning::

@@ -69,7 +71,8 @@ Returns an SVG drawing as a QImage.
%End

QPicture svgAsPicture( const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth,
double widthScaleFactor, bool forceVectorOutput = false, double fixedAspectRatio = 0, bool blocking = false );
double widthScaleFactor, bool forceVectorOutput = false, double fixedAspectRatio = 0, bool blocking = false,
const QMap<QString, QString> &parameters = QMap<QString, QString>() );
%Docstring
Returns an SVG drawing as a QPicture.

@@ -82,6 +85,7 @@ Returns an SVG drawing as a QPicture.
:param forceVectorOutput:
:param fixedAspectRatio: fixed aspect ratio (optional)
:param blocking: forces to wait for loading before returning image (optional)
:param parameters: is a map of parameters to dynamically replace content in SVG.

.. note::

@@ -98,7 +102,7 @@ Returns an SVG drawing as a QPicture.
%End

QSizeF svgViewboxSize( const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth,
double widthScaleFactor, double fixedAspectRatio = 0, bool blocking = false );
double widthScaleFactor, double fixedAspectRatio = 0, bool blocking = false, const QMap<QString, QString> &parameters = QMap<QString, QString>() );
%Docstring
Calculates the viewbox size of a (possibly cached) SVG file.

@@ -110,6 +114,7 @@ Calculates the viewbox size of a (possibly cached) SVG file.
:param widthScaleFactor: width scale factor
:param fixedAspectRatio: fixed aspect ratio (optional)
:param blocking: forces to wait for loading before returning image (optional).
:param parameters: is a map of parameters to dynamically replace content in SVG.

:return: viewbox size set in SVG file

@@ -190,7 +195,7 @@ in the same thread to ensure provided the remote content.
%End

QByteArray svgContent( const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth,
double widthScaleFactor, double fixedAspectRatio = 0, bool blocking = false );
double widthScaleFactor, double fixedAspectRatio = 0, bool blocking = false, const QMap<QString, QString> &parameters = QMap<QString, QString>() );
%Docstring
Gets the SVG content corresponding to the given ``path``.

@@ -869,6 +869,13 @@ Creates a new symbol with size restricted to min/max size if original size is ou
:param height: expected height, can be changed by this function

:return: 0 if size is within minSize/maxSize range. New symbol if size was out of min/max range. Caller takes ownership
%End

static QgsStringMap evaluatePropertiesMap( const QMap<QString, QgsProperty> &propertiesMap, const QgsExpressionContext &context );
%Docstring
Evaluates a map of properties using the given ``context`` and returns a variant map with evaluated expressions from the properties.

.. versionadded:: 3.18
%End
};

@@ -472,9 +472,11 @@ void QgsLayoutItemPicture::loadPictureUsingCache( const QString &path )
QColor fillColor = mDataDefinedProperties.valueAsColor( QgsLayoutObject::PictureSvgBackgroundColor, context, mSvgFillColor );
QColor strokeColor = mDataDefinedProperties.valueAsColor( QgsLayoutObject::PictureSvgStrokeColor, context, mSvgStrokeColor );
double strokeWidth = mDataDefinedProperties.valueAsDouble( QgsLayoutObject::PictureSvgStrokeWidth, context, mSvgStrokeWidth );
// TODO parameters (handle this in the gui part)
QMap<QString, QString> parameters;
bool isMissingImage = false;
const QByteArray &svgContent = QgsApplication::svgCache()->svgContent( path, rect().width(), fillColor, strokeColor, strokeWidth,
1.0, 0, false, &isMissingImage );
1.0, 0, false, parameters, &isMissingImage );
mSVG.load( svgContent );
if ( mSVG.isValid() && !isMissingImage )
{
@@ -26,6 +26,7 @@
#include "qgslogger.h"
#include "qgssvgcache.h"
#include "qgsunittypes.h"
#include "qgsxmlutils.h"

#include <QPainter>
#include <QSvgRenderer>
@@ -1881,6 +1882,21 @@ QgsSymbolLayer *QgsSvgMarkerSymbolLayer::create( const QVariantMap &props )

m->updateDefaultAspectRatio();

if ( props.contains( QStringLiteral( "parameters" ) ) )
{
const QVariantMap parameters = props[QStringLiteral( "parameters" )].toMap();
QMap<QString, QgsProperty> parametersProperties;
QVariantMap::const_iterator it = parameters.constBegin();
for ( ; it != parameters.constEnd(); ++it )
{
QgsProperty property;
if ( property.loadVariant( it.value() ) )
parametersProperties.insert( it.key(), property );
}

m->setParameters( parametersProperties );
}

return m;
}

@@ -1977,6 +1993,11 @@ bool QgsSvgMarkerSymbolLayer::setPreservedAspectRatio( bool par )
return preservedAspectRatio();
}

void QgsSvgMarkerSymbolLayer::setParameters( const QMap<QString, QgsProperty> &parameters )
{
mParameters = parameters;
}


QString QgsSvgMarkerSymbolLayer::layerType() const
{
@@ -2016,6 +2037,8 @@ void QgsSvgMarkerSymbolLayer::renderPoint( QPointF point, QgsSymbolRenderContext
double aspectRatio = calculateAspectRatio( context, scaledWidth, hasDataDefinedAspectRatio );
double scaledHeight = scaledWidth * ( !qgsDoubleNear( aspectRatio, 0.0 ) ? aspectRatio : mDefaultAspectRatio );

QgsStringMap evaluatedParameters = QgsSymbolLayerUtils::evaluatePropertiesMap( mParameters, context.renderContext().expressionContext() );

double strokeWidth = mStrokeWidth;
if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyStrokeWidth ) )
{
@@ -2053,7 +2076,7 @@ void QgsSvgMarkerSymbolLayer::renderPoint( QPointF point, QgsSymbolRenderContext
// adjust height of data defined path
QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( path, scaledWidth, fillColor, strokeColor, strokeWidth,
context.renderContext().scaleFactor(), aspectRatio,
( context.renderContext().flags() & QgsRenderContext::RenderBlocking ) );
( context.renderContext().flags() & QgsRenderContext::RenderBlocking ), evaluatedParameters );
scaledHeight = svgViewbox.isValid() ? scaledWidth * svgViewbox.height() / svgViewbox.width() : scaledWidth;
}
}
@@ -2075,7 +2098,7 @@ void QgsSvgMarkerSymbolLayer::renderPoint( QPointF point, QgsSymbolRenderContext
{
QImage img = QgsApplication::svgCache()->svgAsImage( path, width, fillColor, strokeColor, strokeWidth,
context.renderContext().scaleFactor(), fitsInCache, aspectRatio,
( context.renderContext().flags() & QgsRenderContext::RenderBlocking ) );
( context.renderContext().flags() & QgsRenderContext::RenderBlocking ), evaluatedParameters );
if ( fitsInCache && img.width() > 1 )
{
usePict = false;
@@ -2102,7 +2125,7 @@ void QgsSvgMarkerSymbolLayer::renderPoint( QPointF point, QgsSymbolRenderContext
p->setOpacity( context.opacity() );
QPicture pct = QgsApplication::svgCache()->svgAsPicture( path, width, fillColor, strokeColor, strokeWidth,
context.renderContext().scaleFactor(), context.renderContext().forceVectorOutput(), aspectRatio,
( context.renderContext().flags() & QgsRenderContext::RenderBlocking ) );
( context.renderContext().flags() & QgsRenderContext::RenderBlocking ), evaluatedParameters );
if ( pct.width() > 1 )
{
QgsScopedQPainterState painterPictureState( p );
@@ -2251,6 +2274,13 @@ QVariantMap QgsSvgMarkerSymbolLayer::properties() const
map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );

QVariantMap parameters;
QMap<QString, QgsProperty>::const_iterator it = mParameters.constBegin();
for ( ; it != mParameters.constEnd(); ++it )
parameters.insert( it.key(), it.value().toVariant() );
map[QStringLiteral( "parameters" )] = parameters;

return map;
}

@@ -2277,6 +2307,8 @@ QgsSvgMarkerSymbolLayer *QgsSvgMarkerSymbolLayer::clone() const
m->setSizeMapUnitScale( mSizeMapUnitScale );
m->setHorizontalAnchorPoint( mHorizontalAnchorPoint );
m->setVerticalAnchorPoint( mVerticalAnchorPoint );
m->setParameters( mParameters );

copyDataDefinedProperties( m );
copyPaintEffect( m );
return m;
@@ -2477,9 +2509,11 @@ bool QgsSvgMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFa
strokeColor = mDataDefinedProperties.valueAsColor( QgsSymbolLayer::PropertyStrokeColor, context.renderContext().expressionContext(), mStrokeColor );
}

QgsStringMap evaluatedParameters = QgsSymbolLayerUtils::evaluatePropertiesMap( mParameters, context.renderContext().expressionContext() );

const QByteArray &svgContent = QgsApplication::svgCache()->svgContent( path, size, fillColor, strokeColor, strokeWidth,
context.renderContext().scaleFactor(), mFixedAspectRatio,
( context.renderContext().flags() & QgsRenderContext::RenderBlocking ) );
( context.renderContext().flags() & QgsRenderContext::RenderBlocking ), evaluatedParameters );

QSvgRenderer r( svgContent );
if ( !r.isValid() )
@@ -2560,10 +2594,12 @@ QRectF QgsSvgMarkerSymbolLayer::bounds( QPointF point, QgsSymbolRenderContext &c
fillColor = mDataDefinedProperties.valueAsColor( QgsSymbolLayer::PropertyStrokeColor, context.renderContext().expressionContext(), mStrokeColor );
}

QgsStringMap evaluatedParameters = QgsSymbolLayerUtils::evaluatePropertiesMap( mParameters, context.renderContext().expressionContext() );

// adjust height of data defined path
QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( path, scaledWidth, fillColor, strokeColor, strokeWidth,
context.renderContext().scaleFactor(), aspectRatio,
( context.renderContext().flags() & QgsRenderContext::RenderBlocking ) );
( context.renderContext().flags() & QgsRenderContext::RenderBlocking ), evaluatedParameters );
scaledHeight = svgViewbox.isValid() ? scaledWidth * svgViewbox.height() / svgViewbox.width() : scaledWidth;
}
}
@@ -3496,3 +3532,27 @@ QgsSymbolLayer *QgsFontMarkerSymbolLayer::createFromSld( QDomElement &element )
return m;
}


void QgsSvgMarkerSymbolLayer::prepareExpressions( const QgsSymbolRenderContext &context )
{
QMap<QString, QgsProperty>::iterator it = mParameters.begin();
for ( ; it != mParameters.end(); ++it )
it.value().prepare( context.renderContext().expressionContext() );

QgsMarkerSymbolLayer::prepareExpressions( context );
}


QSet<QString> QgsSvgMarkerSymbolLayer::usedAttributes( const QgsRenderContext &context ) const
{
QSet<QString> attrs = QgsMarkerSymbolLayer::usedAttributes( context );

QMap<QString, QgsProperty>::const_iterator it = mParameters.constBegin();
for ( ; it != mParameters.constEnd(); ++it )
{
it.value().prepare( context.expressionContext() );
attrs.unite( it.value().referencedFields( context.expressionContext() ) );
}

return attrs;
}
@@ -589,6 +589,18 @@ class CORE_EXPORT QgsSvgMarkerSymbolLayer : public QgsMarkerSymbolLayer
double strokeWidth() const { return mStrokeWidth; }
void setStrokeWidth( double w ) { mStrokeWidth = w; }

/**
* Returns the dynamic SVG parameters
* \since QGIS 3.18
*/
QMap<QString, QgsProperty> parameters() const { return mParameters; }

/**
* Sets the dynamic SVG parameters
* \since QGIS 3.18
*/
void setParameters( const QMap<QString, QgsProperty> &parameters );

/**
* Sets the units for the stroke width.
* \param unit width units
@@ -615,6 +627,10 @@ class CORE_EXPORT QgsSvgMarkerSymbolLayer : public QgsMarkerSymbolLayer

QRectF bounds( QPointF point, QgsSymbolRenderContext &context ) override;

void prepareExpressions( const QgsSymbolRenderContext &context ) override;

QSet<QString> usedAttributes( const QgsRenderContext &context ) const override;

protected:

/**
@@ -636,6 +652,7 @@ class CORE_EXPORT QgsSvgMarkerSymbolLayer : public QgsMarkerSymbolLayer
bool mHasFillParam = false;
QColor mStrokeColor;
double mStrokeWidth;
QMap<QString, QgsProperty> mParameters;

QgsUnitTypes::RenderUnit mStrokeWidthUnit;
QgsMapUnitScale mStrokeWidthMapUnitScale;

0 comments on commit 17500a7

Please sign in to comment.
You can’t perform that action at this time.