Skip to content

Commit

Permalink
core part to handle dynamic SVGs
Browse files Browse the repository at this point in the history
this allow to use a map of parameters to dynamically replace attributes or nodes values in the XML of the SVG
  • Loading branch information
3nids committed Jan 6, 2021
1 parent fd7406c commit 9c0ee35
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 42 deletions.
13 changes: 9 additions & 4 deletions python/core/auto_generated/symbology/qgssvgcache.sip.in
Expand Up @@ -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.

Expand All @@ -66,10 +67,12 @@ Returns an SVG drawing as a QImage.

The ``blocking`` parameter must NEVER be ``True`` from GUI based applications (like the main QGIS
application) or crashes will result. Only for use in external scripts or QGIS server.
:param parameters: is a map of parameters to dynamically replace content in SVG.
%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.

Expand All @@ -95,10 +98,11 @@ Returns an SVG drawing as a QPicture.

The ``blocking`` parameter must NEVER be ``True`` from GUI based applications (like the main QGIS
application) or crashes will result. Only for use in external scripts or QGIS server.
:param parameters: is a map of parameters to dynamically replace content in SVG.
%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.

Expand All @@ -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

Expand Down Expand Up @@ -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``.

Expand Down
4 changes: 3 additions & 1 deletion src/core/layout/qgslayoutitempicture.cpp
Expand Up @@ -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
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 )
{
Expand Down
54 changes: 40 additions & 14 deletions src/core/symbology/qgssvgcache.cpp
Expand Up @@ -43,14 +43,15 @@
// QgsSvgCacheEntry
//

QgsSvgCacheEntry::QgsSvgCacheEntry( const QString &path, double size, double strokeWidth, double widthScaleFactor, const QColor &fill, const QColor &stroke, double fixedAspectRatio )
QgsSvgCacheEntry::QgsSvgCacheEntry( const QString &path, double size, double strokeWidth, double widthScaleFactor, const QColor &fill, const QColor &stroke, double fixedAspectRatio, const QMap<QString, QString> &parameters )
: QgsAbstractContentCacheEntry( path )
, size( size )
, strokeWidth( strokeWidth )
, widthScaleFactor( widthScaleFactor )
, fixedAspectRatio( fixedAspectRatio )
, fill( fill )
, stroke( stroke )
, parameters( parameters )
{
}

Expand Down Expand Up @@ -120,12 +121,12 @@ QgsSvgCache::QgsSvgCache( QObject *parent )
}

QImage QgsSvgCache::svgAsImage( const QString &file, double size, const QColor &fill, const QColor &stroke, double strokeWidth,
double widthScaleFactor, bool &fitsInCache, double fixedAspectRatio, bool blocking )
double widthScaleFactor, bool &fitsInCache, double fixedAspectRatio, bool blocking, const QMap<QString, QString> &parameters )
{
QMutexLocker locker( &mMutex );

fitsInCache = true;
QgsSvgCacheEntry *currentEntry = cacheEntry( file, size, fill, stroke, strokeWidth, widthScaleFactor, fixedAspectRatio, blocking );
QgsSvgCacheEntry *currentEntry = cacheEntry( file, size, fill, stroke, strokeWidth, widthScaleFactor, fixedAspectRatio, parameters, blocking );

QImage result;

Expand Down Expand Up @@ -180,11 +181,11 @@ QImage QgsSvgCache::svgAsImage( const QString &file, double size, const QColor &
}

QPicture QgsSvgCache::svgAsPicture( const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth,
double widthScaleFactor, bool forceVectorOutput, double fixedAspectRatio, bool blocking )
double widthScaleFactor, bool forceVectorOutput, double fixedAspectRatio, bool blocking, const QMap<QString, QString> &parameters )
{
QMutexLocker locker( &mMutex );

QgsSvgCacheEntry *currentEntry = cacheEntry( path, size, fill, stroke, strokeWidth, widthScaleFactor, fixedAspectRatio, blocking );
QgsSvgCacheEntry *currentEntry = cacheEntry( path, size, fill, stroke, strokeWidth, widthScaleFactor, fixedAspectRatio, parameters, blocking );

//if current entry picture is 0: cache picture for entry
//update stats for memory usage
Expand All @@ -204,21 +205,21 @@ QPicture QgsSvgCache::svgAsPicture( const QString &path, double size, const QCol
}

QByteArray QgsSvgCache::svgContent( const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth,
double widthScaleFactor, double fixedAspectRatio, bool blocking, bool *isMissingImage )
double widthScaleFactor, double fixedAspectRatio, bool blocking, const QMap<QString, QString> &parameters, bool *isMissingImage )
{
QMutexLocker locker( &mMutex );

QgsSvgCacheEntry *currentEntry = cacheEntry( path, size, fill, stroke, strokeWidth, widthScaleFactor, fixedAspectRatio, blocking, isMissingImage );
QgsSvgCacheEntry *currentEntry = cacheEntry( path, size, fill, stroke, strokeWidth, widthScaleFactor, fixedAspectRatio, parameters, blocking, isMissingImage );

return currentEntry->svgContent;
}

QSizeF QgsSvgCache::svgViewboxSize( const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth,
double widthScaleFactor, double fixedAspectRatio, bool blocking )
double widthScaleFactor, double fixedAspectRatio, bool blocking, const QMap<QString, QString> &parameters )
{
QMutexLocker locker( &mMutex );

QgsSvgCacheEntry *currentEntry = cacheEntry( path, size, fill, stroke, strokeWidth, widthScaleFactor, fixedAspectRatio, blocking );
QgsSvgCacheEntry *currentEntry = cacheEntry( path, size, fill, stroke, strokeWidth, widthScaleFactor, fixedAspectRatio, parameters, blocking );
return currentEntry->viewboxSize;
}

Expand Down Expand Up @@ -303,7 +304,7 @@ void QgsSvgCache::replaceParamsAndCacheSvg( QgsSvgCacheEntry *entry, bool blocki
QSizeF viewboxSize;
double sizeScaleFactor = calcSizeScaleFactor( entry, docElem, viewboxSize );
entry->viewboxSize = viewboxSize;
replaceElemParams( docElem, entry->fill, entry->stroke, entry->strokeWidth * sizeScaleFactor );
replaceElemParams( docElem, entry->fill, entry->stroke, entry->strokeWidth * sizeScaleFactor, entry->parameters );

entry->svgContent = svgDoc.toByteArray( 0 );

Expand Down Expand Up @@ -494,9 +495,9 @@ void QgsSvgCache::cachePicture( QgsSvgCacheEntry *entry, bool forceVectorOutput
}

QgsSvgCacheEntry *QgsSvgCache::cacheEntry( const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth,
double widthScaleFactor, double fixedAspectRatio, bool blocking, bool *isMissingImage )
double widthScaleFactor, double fixedAspectRatio, const QMap<QString, QString> &parameters, bool blocking, bool *isMissingImage )
{
QgsSvgCacheEntry *currentEntry = findExistingEntry( new QgsSvgCacheEntry( path, size, strokeWidth, widthScaleFactor, fill, stroke, fixedAspectRatio ) );
QgsSvgCacheEntry *currentEntry = findExistingEntry( new QgsSvgCacheEntry( path, size, strokeWidth, widthScaleFactor, fill, stroke, fixedAspectRatio, parameters ) );

if ( currentEntry->svgContent.isEmpty() )
{
Expand All @@ -510,7 +511,7 @@ QgsSvgCacheEntry *QgsSvgCache::cacheEntry( const QString &path, double size, con
}


void QgsSvgCache::replaceElemParams( QDomElement &elem, const QColor &fill, const QColor &stroke, double strokeWidth )
void QgsSvgCache::replaceElemParams( QDomElement &elem, const QColor &fill, const QColor &stroke, double strokeWidth, const QMap<QString, QString> &parameters )
{
if ( elem.isNull() )
{
Expand Down Expand Up @@ -595,6 +596,31 @@ void QgsSvgCache::replaceElemParams( QDomElement &elem, const QColor &fill, cons
{
elem.setAttribute( attribute.name(), QString::number( strokeWidth ) );
}
else
{
QMap<QString, QString>::const_iterator paramIt = parameters.constBegin();
for ( ; paramIt != parameters.constEnd(); ++paramIt )
{
if ( value.startsWith( QString( QLatin1String( "param(%1)" ) ).arg( paramIt.key() ) ) )
{
elem.setAttribute( attribute.name(), paramIt.value() );
break;
}
}
}
}
}

if ( elem.nodeValue().startsWith( "param(" ) )
{
QMap<QString, QString>::const_iterator paramIt = parameters.constBegin();
for ( ; paramIt != parameters.constEnd(); ++paramIt )
{
if ( elem.nodeValue().startsWith( QString( QLatin1String( "param(%1)" ) ).arg( paramIt.key() ) ) )
{
elem.setNodeValue( paramIt.value() );
break;
}
}
}

Expand All @@ -603,7 +629,7 @@ void QgsSvgCache::replaceElemParams( QDomElement &elem, const QColor &fill, cons
for ( int i = 0; i < nChildren; ++i )
{
QDomElement childElem = childList.at( i ).toElement();
replaceElemParams( childElem, fill, stroke, strokeWidth );
replaceElemParams( childElem, fill, stroke, strokeWidth, parameters );
}
}

Expand Down
24 changes: 16 additions & 8 deletions src/core/symbology/qgssvgcache.h
Expand Up @@ -46,9 +46,10 @@ class CORE_EXPORT QgsSvgCacheEntry : public QgsAbstractContentCacheEntry
* \param fill color of fill
* \param stroke color of stroke
* \param fixedAspectRatio fixed aspect ratio (optional)
* \param parameters an optional map of parameters to dynamically replace content in the SVG
*/
QgsSvgCacheEntry( const QString &path, double size, double strokeWidth, double widthScaleFactor, const QColor &fill, const QColor &stroke,
double fixedAspectRatio = 0 ) ;
double fixedAspectRatio = 0, const QMap<QString, QString> &parameters = QMap<QString, QString>() ) ;

//! QgsSvgCacheEntry cannot be copied.
QgsSvgCacheEntry( const QgsSvgCacheEntry &rh ) = delete;
Expand All @@ -70,6 +71,8 @@ class CORE_EXPORT QgsSvgCacheEntry : public QgsAbstractContentCacheEntry

QColor fill = Qt::black;
QColor stroke = Qt::black;
QMap<QString, QString> parameters;

std::unique_ptr< QImage > image;
std::unique_ptr< QPicture > picture;
//content (with params replaced)
Expand Down Expand Up @@ -143,9 +146,11 @@ class CORE_EXPORT QgsSvgCache : public QgsAbstractContentCache< QgsSvgCacheEntry
*
* \warning The \a blocking parameter must NEVER be TRUE from GUI based applications (like the main QGIS
* application) or crashes will result. Only for use in external scripts or QGIS server.
* \param parameters is a map of parameters to dynamically replace content in SVG.
*/
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>() );

/**
* Returns an SVG drawing as a QPicture.
Expand All @@ -167,9 +172,11 @@ class CORE_EXPORT QgsSvgCache : public QgsAbstractContentCache< QgsSvgCacheEntry
*
* \warning The \a blocking parameter must NEVER be TRUE from GUI based applications (like the main QGIS
* application) or crashes will result. Only for use in external scripts or QGIS server.
* \param parameters is a map of parameters to dynamically replace content in SVG.
*/
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>() );

/**
* Calculates the viewbox size of a (possibly cached) SVG file.
Expand All @@ -185,11 +192,12 @@ class CORE_EXPORT QgsSvgCache : public QgsAbstractContentCache< QgsSvgCacheEntry
*
* \warning The blocking parameter must NEVER be TRUE from GUI based applications (like the main QGIS
* application) or crashes will result. Only for use in external scripts or QGIS server.
* \param parameters is a map of parameters to dynamically replace content in SVG.
*
* \since QGIS 2.14
*/
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>() );

/**
* Tests if an SVG file contains parameters for fill, stroke color, stroke width. If yes, possible default values are returned. If there are several
Expand Down Expand Up @@ -268,10 +276,10 @@ class CORE_EXPORT QgsSvgCache : public QgsAbstractContentCache< QgsSvgCacheEntry
*/
#ifndef SIP_RUN
QByteArray svgContent( const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth,
double widthScaleFactor, double fixedAspectRatio = 0, bool blocking = false, bool *isMissingImage = nullptr );
double widthScaleFactor, double fixedAspectRatio = 0, bool blocking = false, const QMap<QString, QString> &parameters = QMap<QString, QString>(), bool *isMissingImage = nullptr );
#else
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>() );
#endif

signals:
Expand Down Expand Up @@ -299,10 +307,10 @@ class CORE_EXPORT QgsSvgCache : public QgsAbstractContentCache< QgsSvgCacheEntry
void cachePicture( QgsSvgCacheEntry *entry, bool forceVectorOutput = false );
//! Returns entry from cache or creates a new entry if it does not exist already
QgsSvgCacheEntry *cacheEntry( const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth,
double widthScaleFactor, double fixedAspectRatio = 0, bool blocking = false, bool *isMissingImage = nullptr );
double widthScaleFactor, double fixedAspectRatio = 0, const QMap<QString, QString> &parameters = QMap<QString, QString>(), bool blocking = false, bool *isMissingImage = nullptr );

//! Replaces parameters in elements of a dom node and calls method for all child nodes
void replaceElemParams( QDomElement &elem, const QColor &fill, const QColor &stroke, double strokeWidth );
void replaceElemParams( QDomElement &elem, const QColor &fill, const QColor &stroke, double strokeWidth, const QMap<QString, QString> &parameters );

void containsElemParams( const QDomElement &elem,
bool &hasFillParam, bool &hasDefaultFill, QColor &defaultFill,
Expand Down

0 comments on commit 9c0ee35

Please sign in to comment.