26 #include <QApplication> 
   27 #include <QCoreApplication> 
   29 #include <QDomDocument> 
   30 #include <QDomElement> 
   35 #include <QRegularExpression> 
   36 #include <QSvgRenderer> 
   38 #include <QNetworkReply> 
   39 #include <QNetworkRequest> 
   47 QgsSvgCacheEntry::QgsSvgCacheEntry( 
const QString &path, 
double size, 
double strokeWidth, 
double widthScaleFactor, 
const QColor &fill, 
const QColor &stroke, 
double fixedAspectRatio, 
const QMap<QString, QString> ¶meters )
 
   50   , strokeWidth( strokeWidth )
 
   51   , widthScaleFactor( widthScaleFactor )
 
   52   , fixedAspectRatio( fixedAspectRatio )
 
   55   , parameters( parameters )
 
   61   const QgsSvgCacheEntry *otherSvg = 
dynamic_cast< const QgsSvgCacheEntry * 
>( other );
 
   64        || !
qgsDoubleNear( otherSvg->fixedAspectRatio, fixedAspectRatio )
 
   67        || !
qgsDoubleNear( otherSvg->widthScaleFactor, widthScaleFactor )
 
   68        || otherSvg->fill != fill
 
   69        || otherSvg->stroke != stroke
 
   70        || otherSvg->path != path
 
   71        || otherSvg->parameters != parameters )
 
   77 int QgsSvgCacheEntry::dataSize()
 const 
   79   int size = svgContent.size();
 
   82     size += picture->size();
 
   86     size += ( image->width() * image->height() * 32 );
 
   91 void QgsSvgCacheEntry::dump()
 const 
   93   QgsDebugMsgLevel( QStringLiteral( 
"path: %1, size %2, width scale factor %3" ).arg( path ).arg( size ).arg( widthScaleFactor ), 4 );
 
  105   mMissingSvg = QStringLiteral( 
"<svg width='10' height='10'><text x='5' y='10' font-size='10' text-anchor='middle'>?</text></svg>" ).toLatin1();
 
  108   if ( QFile::exists( downloadingSvgPath ) )
 
  110     QFile file( downloadingSvgPath );
 
  111     if ( file.open( QIODevice::ReadOnly ) )
 
  113       mFetchingSvg = file.readAll();
 
  117   if ( mFetchingSvg.isEmpty() )
 
  119     mFetchingSvg = QStringLiteral( 
"<svg width='10' height='10'><text x='5' y='10' font-size='10' text-anchor='middle'>?</text></svg>" ).toLatin1();
 
  125 QImage 
QgsSvgCache::svgAsImage( 
const QString &file, 
double size, 
const QColor &fill, 
const QColor &stroke, 
double strokeWidth,
 
  126                                 double widthScaleFactor, 
bool &fitsInCache, 
double fixedAspectRatio, 
bool blocking, 
const QMap<QString, QString> ¶meters )
 
  128   QMutexLocker locker( &
mMutex );
 
  131   QgsSvgCacheEntry *currentEntry = cacheEntry( file, size, fill, stroke, strokeWidth, widthScaleFactor, fixedAspectRatio, parameters, blocking );
 
  138   if ( !currentEntry->image )
 
  140     QSvgRenderer r( currentEntry->svgContent );
 
  141     double hwRatio = 1.0;
 
  142     if ( r.viewBoxF().width() > 0 )
 
  144       if ( currentEntry->fixedAspectRatio > 0 )
 
  146         hwRatio = currentEntry->fixedAspectRatio;
 
  150         hwRatio = r.viewBoxF().height() / r.viewBoxF().width();
 
  153     long cachedDataSize = 0;
 
  154     cachedDataSize += currentEntry->svgContent.size();
 
  155     cachedDataSize += 
static_cast< int >( currentEntry->size * currentEntry->size * hwRatio * 32 );
 
  159       currentEntry->image.reset();
 
  162       if ( !currentEntry->picture )
 
  164         cachePicture( currentEntry, 
false );
 
  168       result = imageFromCachedPicture( *currentEntry );
 
  172       cacheImage( currentEntry );
 
  173       result = *( currentEntry->image );
 
  179     result = *( currentEntry->image );
 
  186                                     double widthScaleFactor, 
bool forceVectorOutput, 
double fixedAspectRatio, 
bool blocking, 
const QMap<QString, QString> ¶meters )
 
  188   QMutexLocker locker( &
mMutex );
 
  190   QgsSvgCacheEntry *currentEntry = cacheEntry( path, size, fill, stroke, strokeWidth, widthScaleFactor, fixedAspectRatio, parameters, blocking );
 
  194   if ( !currentEntry->picture )
 
  196     cachePicture( currentEntry, forceVectorOutput );
 
  205   p.setData( currentEntry->picture->data(), currentEntry->picture->size() );
 
  209 QByteArray 
QgsSvgCache::svgContent( 
const QString &path, 
double size, 
const QColor &fill, 
const QColor &stroke, 
double strokeWidth,
 
  210                                     double widthScaleFactor, 
double fixedAspectRatio, 
bool blocking, 
const QMap<QString, QString> ¶meters, 
bool *isMissingImage )
 
  212   QMutexLocker locker( &
mMutex );
 
  214   QgsSvgCacheEntry *currentEntry = cacheEntry( path, size, fill, stroke, strokeWidth, widthScaleFactor, fixedAspectRatio, parameters, blocking, isMissingImage );
 
  216   return currentEntry->svgContent;
 
  220                                     double widthScaleFactor, 
double fixedAspectRatio, 
bool blocking, 
const QMap<QString, QString> ¶meters )
 
  222   QMutexLocker locker( &
mMutex );
 
  224   QgsSvgCacheEntry *currentEntry = cacheEntry( path, size, fill, stroke, strokeWidth, widthScaleFactor, fixedAspectRatio, parameters, blocking );
 
  225   return currentEntry->viewboxSize;
 
  228 void QgsSvgCache::containsParams( 
const QString &path, 
bool &hasFillParam, QColor &defaultFillColor, 
bool &hasStrokeParam, QColor &defaultStrokeColor,
 
  229                                   bool &hasStrokeWidthParam, 
double &defaultStrokeWidth, 
bool blocking )
 const 
  231   bool hasDefaultFillColor = 
false;
 
  232   bool hasFillOpacityParam = 
false;
 
  233   bool hasDefaultFillOpacity = 
false;
 
  234   double defaultFillOpacity = 1.0;
 
  235   bool hasDefaultStrokeColor = 
false;
 
  236   bool hasDefaultStrokeWidth = 
false;
 
  237   bool hasStrokeOpacityParam = 
false;
 
  238   bool hasDefaultStrokeOpacity = 
false;
 
  239   double defaultStrokeOpacity = 1.0;
 
  241   containsParams( path, hasFillParam, hasDefaultFillColor, defaultFillColor,
 
  242                   hasFillOpacityParam, hasDefaultFillOpacity, defaultFillOpacity,
 
  243                   hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
 
  244                   hasStrokeWidthParam, hasDefaultStrokeWidth, defaultStrokeWidth,
 
  245                   hasStrokeOpacityParam, hasDefaultStrokeOpacity, defaultStrokeOpacity,
 
  250                                   bool &hasFillParam, 
bool &hasDefaultFillParam, QColor &defaultFillColor,
 
  251                                   bool &hasFillOpacityParam, 
bool &hasDefaultFillOpacity, 
double &defaultFillOpacity,
 
  252                                   bool &hasStrokeParam, 
bool &hasDefaultStrokeColor, QColor &defaultStrokeColor,
 
  253                                   bool &hasStrokeWidthParam, 
bool &hasDefaultStrokeWidth, 
double &defaultStrokeWidth,
 
  254                                   bool &hasStrokeOpacityParam, 
bool &hasDefaultStrokeOpacity, 
double &defaultStrokeOpacity,
 
  255                                   bool blocking )
 const 
  257   hasFillParam = 
false;
 
  258   hasFillOpacityParam = 
false;
 
  259   hasStrokeParam = 
false;
 
  260   hasStrokeWidthParam = 
false;
 
  261   hasStrokeOpacityParam = 
false;
 
  262   defaultFillColor = QColor( Qt::white );
 
  263   defaultFillOpacity = 1.0;
 
  264   defaultStrokeColor = QColor( Qt::black );
 
  265   defaultStrokeWidth = 0.2;
 
  266   defaultStrokeOpacity = 1.0;
 
  268   hasDefaultFillParam = 
false;
 
  269   hasDefaultFillOpacity = 
false;
 
  270   hasDefaultStrokeColor = 
false;
 
  271   hasDefaultStrokeWidth = 
false;
 
  272   hasDefaultStrokeOpacity = 
false;
 
  275   if ( !svgDoc.setContent( 
getContent( path, mMissingSvg, mFetchingSvg, blocking ) ) )
 
  280   QDomElement docElem = svgDoc.documentElement();
 
  281   containsElemParams( docElem, hasFillParam, hasDefaultFillParam, defaultFillColor,
 
  282                       hasFillOpacityParam, hasDefaultFillOpacity, defaultFillOpacity,
 
  283                       hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
 
  284                       hasStrokeWidthParam, hasDefaultStrokeWidth, defaultStrokeWidth,
 
  285                       hasStrokeOpacityParam, hasDefaultStrokeOpacity, defaultStrokeOpacity );
 
  288 void QgsSvgCache::replaceParamsAndCacheSvg( QgsSvgCacheEntry *entry, 
bool blocking )
 
  295   const QByteArray content = 
getContent( entry->path, mMissingSvg, mFetchingSvg, blocking ) ;
 
  296   entry->isMissingImage = content == mMissingSvg;
 
  298   if ( !svgDoc.setContent( content ) )
 
  304   QDomElement docElem = svgDoc.documentElement();
 
  307   double sizeScaleFactor = calcSizeScaleFactor( entry, docElem, viewboxSize );
 
  308   entry->viewboxSize = viewboxSize;
 
  309   replaceElemParams( docElem, entry->fill, entry->stroke, entry->strokeWidth * sizeScaleFactor, entry->parameters );
 
  311   entry->svgContent = svgDoc.toByteArray( 0 );
 
  316   entry->svgContent.replace( 
"\n<tspan", 
"<tspan" );
 
  317   entry->svgContent.replace( 
"</tspan>\n", 
"</tspan>" );
 
  322 double QgsSvgCache::calcSizeScaleFactor( QgsSvgCacheEntry *entry, 
const QDomElement &docElem, QSizeF &viewboxSize )
 const 
  332   if ( docElem.tagName() == QLatin1String( 
"svg" ) && docElem.hasAttribute( QStringLiteral( 
"viewBox" ) ) )
 
  334     viewBox = docElem.attribute( QStringLiteral( 
"viewBox" ), QString() );
 
  336   else if ( docElem.tagName() == QLatin1String( 
"svg" ) && docElem.hasAttribute( QStringLiteral( 
"viewbox" ) ) )
 
  338     viewBox = docElem.attribute( QStringLiteral( 
"viewbox" ), QString() );
 
  342     QDomElement svgElem = docElem.firstChildElement( QStringLiteral( 
"svg" ) );
 
  343     if ( !svgElem.isNull() )
 
  345       if ( svgElem.hasAttribute( QStringLiteral( 
"viewBox" ) ) )
 
  346         viewBox = svgElem.attribute( QStringLiteral( 
"viewBox" ), QString() );
 
  347       else if ( svgElem.hasAttribute( QStringLiteral( 
"viewbox" ) ) )
 
  348         viewBox = svgElem.attribute( QStringLiteral( 
"viewbox" ), QString() );
 
  353   if ( viewBox.isEmpty() )
 
  356     if ( docElem.tagName() == QLatin1String( 
"svg" ) && docElem.hasAttribute( QStringLiteral( 
"width" ) ) )
 
  358       const QString widthString = docElem.attribute( QStringLiteral( 
"width" ) );
 
  359       const QRegularExpression measureRegEx( QStringLiteral( 
"([\\d\\.]+).*?$" ) );
 
  360       const QRegularExpressionMatch widthMatch = measureRegEx.match( widthString );
 
  361       if ( widthMatch.hasMatch() )
 
  363         double width = widthMatch.captured( 1 ).toDouble();
 
  364         const QString heightString = docElem.attribute( QStringLiteral( 
"height" ) );
 
  366         const QRegularExpressionMatch heightMatch = measureRegEx.match( heightString );
 
  367         if ( heightMatch.hasMatch() )
 
  369           double height = heightMatch.captured( 1 ).toDouble();
 
  370           viewboxSize = QSizeF( width, height );
 
  371           return width / entry->size;
 
  381   QStringList parts = viewBox.split( 
' ' );
 
  382   if ( parts.count() != 4 )
 
  385   bool heightOk = 
false;
 
  386   double height = parts.at( 3 ).toDouble( &heightOk );
 
  388   bool widthOk = 
false;
 
  389   double width = parts.at( 2 ).toDouble( &widthOk );
 
  393       viewboxSize = QSizeF( width, height );
 
  394     return width / entry->size;
 
  403   return getContent( path, mMissingSvg, mFetchingSvg, blocking );
 
  410   QString contentType = reply->header( QNetworkRequest::ContentTypeHeader ).toString();
 
  411   if ( !contentType.startsWith( QLatin1String( 
"image/svg+xml" ), Qt::CaseInsensitive )
 
  412        && !contentType.startsWith( QLatin1String( 
"text/plain" ), Qt::CaseInsensitive ) )
 
  420 void QgsSvgCache::cacheImage( QgsSvgCacheEntry *entry )
 
  427   entry->image.reset();
 
  431   QSize imageSize = sizeForImage( *entry, viewBoxSize, scaledSize );
 
  434   std::unique_ptr< QImage > image = std::make_unique< QImage >( imageSize, QImage::Format_ARGB32_Premultiplied );
 
  437   const bool isFixedAR = entry->fixedAspectRatio > 0;
 
  439   QPainter p( image.get() );
 
  440   QSvgRenderer r( entry->svgContent );
 
  441   if ( 
qgsDoubleNear( viewBoxSize.width(), viewBoxSize.height() ) )
 
  447     QSizeF s( viewBoxSize );
 
  448     s.scale( scaledSize.width(), scaledSize.height(), isFixedAR ? Qt::IgnoreAspectRatio : Qt::KeepAspectRatio );
 
  449     QRectF rect( ( imageSize.width() - s.width() ) / 2, ( imageSize.height() - s.height() ) / 2, s.width(), s.height() );
 
  450     r.render( &p, rect );
 
  453   mTotalSize += ( image->width() * image->height() * 32 );
 
  454   entry->image = std::move( image );
 
  457 void QgsSvgCache::cachePicture( QgsSvgCacheEntry *entry, 
bool forceVectorOutput )
 
  459   Q_UNUSED( forceVectorOutput )
 
  465   entry->picture.reset();
 
  467   bool isFixedAR = entry->fixedAspectRatio > 0;
 
  470   std::unique_ptr< QPicture > picture = std::make_unique< QPicture >();
 
  472   QSvgRenderer r( entry->svgContent );
 
  473   double hwRatio = 1.0;
 
  474   if ( r.viewBoxF().width() > 0 )
 
  478       hwRatio = entry->fixedAspectRatio;
 
  482       hwRatio = r.viewBoxF().height() / r.viewBoxF().width();
 
  486   double wSize = entry->size;
 
  487   double hSize = wSize * hwRatio;
 
  489   QSizeF s( r.viewBoxF().size() );
 
  490   s.scale( wSize, hSize, isFixedAR ? Qt::IgnoreAspectRatio : Qt::KeepAspectRatio );
 
  491   rect = QRectF( -s.width() / 2.0, -s.height() / 2.0, s.width(), s.height() );
 
  493   QPainter p( picture.get() );
 
  494   r.render( &p, rect );
 
  495   entry->picture = std::move( picture );
 
  499 QgsSvgCacheEntry *QgsSvgCache::cacheEntry( 
const QString &path, 
double size, 
const QColor &fill, 
const QColor &stroke, 
double strokeWidth,
 
  500     double widthScaleFactor, 
double fixedAspectRatio, 
const QMap<QString, QString> ¶meters, 
bool blocking, 
bool *isMissingImage )
 
  502   QgsSvgCacheEntry *currentEntry = 
findExistingEntry( 
new QgsSvgCacheEntry( path, size, strokeWidth, widthScaleFactor, fill, stroke, fixedAspectRatio, parameters ) );
 
  504   if ( currentEntry->svgContent.isEmpty() )
 
  506     replaceParamsAndCacheSvg( currentEntry, blocking );
 
  509   if ( isMissingImage )
 
  510     *isMissingImage = currentEntry->isMissingImage;
 
  516 void QgsSvgCache::replaceElemParams( QDomElement &elem, 
const QColor &fill, 
const QColor &stroke, 
double strokeWidth, 
const QMap<QString, QString> ¶meters )
 
  524   QDomNamedNodeMap attributes = elem.attributes();
 
  525   int nAttributes = attributes.count();
 
  526   for ( 
int i = 0; i < nAttributes; ++i )
 
  528     QDomAttr attribute = attributes.item( i ).toAttr();
 
  530     if ( attribute.name().compare( QLatin1String( 
"style" ), Qt::CaseInsensitive ) == 0 )
 
  533       QString newAttributeString;
 
  535       QStringList entryList = attribute.value().split( 
';' );
 
  536       QStringList::const_iterator entryIt = entryList.constBegin();
 
  537       for ( ; entryIt != entryList.constEnd(); ++entryIt )
 
  539         QStringList keyValueSplit = entryIt->split( 
':' );
 
  540         if ( keyValueSplit.size() < 2 )
 
  544         const QString key = keyValueSplit.at( 0 );
 
  545         QString value = keyValueSplit.at( 1 );
 
  546         QString newValue = value;
 
  547         value = value.trimmed().toLower();
 
  549         if ( value.startsWith( QLatin1String( 
"param(fill)" ) ) )
 
  551           newValue = fill.name();
 
  553         else if ( value.startsWith( QLatin1String( 
"param(fill-opacity)" ) ) )
 
  555           newValue = QString::number( fill.alphaF() );
 
  557         else if ( value.startsWith( QLatin1String( 
"param(outline)" ) ) )
 
  559           newValue = stroke.name();
 
  561         else if ( value.startsWith( QLatin1String( 
"param(outline-opacity)" ) ) )
 
  563           newValue = QString::number( stroke.alphaF() );
 
  565         else if ( value.startsWith( QLatin1String( 
"param(outline-width)" ) ) )
 
  567           newValue = QString::number( strokeWidth );
 
  570         if ( entryIt != entryList.constBegin() )
 
  572           newAttributeString.append( 
';' );
 
  574         newAttributeString.append( key + 
':' + newValue );
 
  576       elem.setAttribute( attribute.name(), newAttributeString );
 
  580       const QString value = attribute.value().trimmed().toLower();
 
  581       if ( value.startsWith( QLatin1String( 
"param(fill)" ) ) )
 
  583         elem.setAttribute( attribute.name(), fill.name() );
 
  585       else if ( value.startsWith( QLatin1String( 
"param(fill-opacity)" ) ) )
 
  587         elem.setAttribute( attribute.name(), fill.alphaF() );
 
  589       else if ( value.startsWith( QLatin1String( 
"param(outline)" ) ) )
 
  591         elem.setAttribute( attribute.name(), stroke.name() );
 
  593       else if ( value.startsWith( QLatin1String( 
"param(outline-opacity)" ) ) )
 
  595         elem.setAttribute( attribute.name(), stroke.alphaF() );
 
  597       else if ( value.startsWith( QLatin1String( 
"param(outline-width)" ) ) )
 
  599         elem.setAttribute( attribute.name(), QString::number( strokeWidth ) );
 
  603         QMap<QString, QString>::const_iterator paramIt = parameters.constBegin();
 
  604         for ( ; paramIt != parameters.constEnd(); ++paramIt )
 
  606           if ( value.startsWith( QString( QLatin1String( 
"param(%1)" ) ).arg( paramIt.key() ) ) )
 
  608             elem.setAttribute( attribute.name(), paramIt.value() );
 
  616   QDomNode child = elem.firstChild();
 
  617   if ( child.isText() && child.nodeValue().startsWith( 
"param(" ) )
 
  619     QMap<QString, QString>::const_iterator paramIt = parameters.constBegin();
 
  620     for ( ; paramIt != parameters.constEnd(); ++paramIt )
 
  622       if ( child.toText().data().startsWith( QString( QLatin1String( 
"param(%1)" ) ).arg( paramIt.key() ) ) )
 
  624         child.setNodeValue( paramIt.value() );
 
  630   QDomNodeList childList = elem.childNodes();
 
  631   int nChildren = childList.count();
 
  632   for ( 
int i = 0; i < nChildren; ++i )
 
  634     QDomElement childElem = childList.at( i ).toElement();
 
  635     replaceElemParams( childElem, fill, stroke, strokeWidth, parameters );
 
  639 void QgsSvgCache::containsElemParams( 
const QDomElement &elem, 
bool &hasFillParam, 
bool &hasDefaultFill, QColor &defaultFill,
 
  640                                       bool &hasFillOpacityParam, 
bool &hasDefaultFillOpacity, 
double &defaultFillOpacity,
 
  641                                       bool &hasStrokeParam, 
bool &hasDefaultStroke, QColor &defaultStroke,
 
  642                                       bool &hasStrokeWidthParam, 
bool &hasDefaultStrokeWidth, 
double &defaultStrokeWidth,
 
  643                                       bool &hasStrokeOpacityParam, 
bool &hasDefaultStrokeOpacity, 
double &defaultStrokeOpacity )
 const 
  651   if ( hasFillParam && hasStrokeParam && hasStrokeWidthParam && hasFillOpacityParam && hasStrokeOpacityParam )
 
  657   QDomNamedNodeMap attributes = elem.attributes();
 
  658   int nAttributes = attributes.count();
 
  660   QStringList valueSplit;
 
  661   for ( 
int i = 0; i < nAttributes; ++i )
 
  663     QDomAttr attribute = attributes.item( i ).toAttr();
 
  664     if ( attribute.name().compare( QLatin1String( 
"style" ), Qt::CaseInsensitive ) == 0 )
 
  667       QStringList entryList = attribute.value().split( 
';' );
 
  668       QStringList::const_iterator entryIt = entryList.constBegin();
 
  669       for ( ; entryIt != entryList.constEnd(); ++entryIt )
 
  671         QStringList keyValueSplit = entryIt->split( 
':' );
 
  672         if ( keyValueSplit.size() < 2 )
 
  676         QString value = keyValueSplit.at( 1 );
 
  677         valueSplit = value.split( 
' ' );
 
  678         if ( !hasFillParam && value.startsWith( QLatin1String( 
"param(fill)" ) ) )
 
  681           if ( valueSplit.size() > 1 )
 
  683             defaultFill = QColor( valueSplit.at( 1 ) );
 
  684             hasDefaultFill = 
true;
 
  687         else if ( !hasFillOpacityParam && value.startsWith( QLatin1String( 
"param(fill-opacity)" ) ) )
 
  689           hasFillOpacityParam = 
true;
 
  690           if ( valueSplit.size() > 1 )
 
  693             double opacity = valueSplit.at( 1 ).toDouble( &ok );
 
  696               defaultFillOpacity = opacity;
 
  697               hasDefaultFillOpacity = 
true;
 
  701         else if ( !hasStrokeParam && value.startsWith( QLatin1String( 
"param(outline)" ) ) )
 
  703           hasStrokeParam = 
true;
 
  704           if ( valueSplit.size() > 1 )
 
  706             defaultStroke = QColor( valueSplit.at( 1 ) );
 
  707             hasDefaultStroke = 
true;
 
  710         else if ( !hasStrokeWidthParam && value.startsWith( QLatin1String( 
"param(outline-width)" ) ) )
 
  712           hasStrokeWidthParam = 
true;
 
  713           if ( valueSplit.size() > 1 )
 
  715             defaultStrokeWidth = valueSplit.at( 1 ).toDouble();
 
  716             hasDefaultStrokeWidth = 
true;
 
  719         else if ( !hasStrokeOpacityParam && value.startsWith( QLatin1String( 
"param(outline-opacity)" ) ) )
 
  721           hasStrokeOpacityParam = 
true;
 
  722           if ( valueSplit.size() > 1 )
 
  725             double opacity = valueSplit.at( 1 ).toDouble( &ok );
 
  728               defaultStrokeOpacity = opacity;
 
  729               hasDefaultStrokeOpacity = 
true;
 
  737       QString value = attribute.value();
 
  738       valueSplit = value.split( 
' ' );
 
  739       if ( !hasFillParam && value.startsWith( QLatin1String( 
"param(fill)" ) ) )
 
  742         if ( valueSplit.size() > 1 )
 
  744           defaultFill = QColor( valueSplit.at( 1 ) );
 
  745           hasDefaultFill = 
true;
 
  748       else if ( !hasFillOpacityParam && value.startsWith( QLatin1String( 
"param(fill-opacity)" ) ) )
 
  750         hasFillOpacityParam = 
true;
 
  751         if ( valueSplit.size() > 1 )
 
  754           double opacity = valueSplit.at( 1 ).toDouble( &ok );
 
  757             defaultFillOpacity = opacity;
 
  758             hasDefaultFillOpacity = 
true;
 
  762       else if ( !hasStrokeParam && value.startsWith( QLatin1String( 
"param(outline)" ) ) )
 
  764         hasStrokeParam = 
true;
 
  765         if ( valueSplit.size() > 1 )
 
  767           defaultStroke = QColor( valueSplit.at( 1 ) );
 
  768           hasDefaultStroke = 
true;
 
  771       else if ( !hasStrokeWidthParam && value.startsWith( QLatin1String( 
"param(outline-width)" ) ) )
 
  773         hasStrokeWidthParam = 
true;
 
  774         if ( valueSplit.size() > 1 )
 
  776           defaultStrokeWidth = valueSplit.at( 1 ).toDouble();
 
  777           hasDefaultStrokeWidth = 
true;
 
  780       else if ( !hasStrokeOpacityParam && value.startsWith( QLatin1String( 
"param(outline-opacity)" ) ) )
 
  782         hasStrokeOpacityParam = 
true;
 
  783         if ( valueSplit.size() > 1 )
 
  786           double opacity = valueSplit.at( 1 ).toDouble( &ok );
 
  789             defaultStrokeOpacity = opacity;
 
  790             hasDefaultStrokeOpacity = 
true;
 
  798   QDomNodeList childList = elem.childNodes();
 
  799   int nChildren = childList.count();
 
  800   for ( 
int i = 0; i < nChildren; ++i )
 
  802     QDomElement childElem = childList.at( i ).toElement();
 
  803     containsElemParams( childElem, hasFillParam, hasDefaultFill, defaultFill,
 
  804                         hasFillOpacityParam, hasDefaultFillOpacity, defaultFillOpacity,
 
  805                         hasStrokeParam, hasDefaultStroke, defaultStroke,
 
  806                         hasStrokeWidthParam, hasDefaultStrokeWidth, defaultStrokeWidth,
 
  807                         hasStrokeOpacityParam, hasDefaultStrokeOpacity, defaultStrokeOpacity );
 
  811 QSize QgsSvgCache::sizeForImage( 
const QgsSvgCacheEntry &entry, QSizeF &viewBoxSize, QSizeF &scaledSize )
 const 
  813   bool isFixedAR = entry.fixedAspectRatio > 0;
 
  815   QSvgRenderer r( entry.svgContent );
 
  816   double hwRatio = 1.0;
 
  817   viewBoxSize = r.viewBoxF().size();
 
  818   if ( viewBoxSize.width() > 0 )
 
  822       hwRatio = entry.fixedAspectRatio;
 
  826       hwRatio = viewBoxSize.height() / viewBoxSize.width();
 
  831   scaledSize.setWidth( entry.size );
 
  832   int wImgSize = 
static_cast< int >( scaledSize.width() );
 
  837   scaledSize.setHeight( scaledSize.width() * hwRatio );
 
  838   int hImgSize = 
static_cast< int >( scaledSize.height() );
 
  843   return QSize( wImgSize, hImgSize );
 
  846 QImage QgsSvgCache::imageFromCachedPicture( 
const QgsSvgCacheEntry &entry )
 const 
  850   QImage image( sizeForImage( entry, viewBoxSize, scaledSize ), QImage::Format_ARGB32_Premultiplied );
 
  853   QPainter p( &image );
 
  854   p.drawPicture( QPoint( 0, 0 ), *entry.picture );
 
void remoteContentFetched(const QString &url)
Emitted when the cache has finished retrieving content from a remote url.
Base class for entries in a QgsAbstractContentCache.
Abstract base class for file content caches, such as SVG or raster image caches.
QgsSvgCacheEntry * findExistingEntry(QgsSvgCacheEntry *entryTemplate)
Returns the existing entry from the cache which matches entryTemplate (deleting entryTemplate when do...
long mMaxCacheSize
Maximum cache size.
QByteArray getContent(const QString &path, const QByteArray &missingContent, const QByteArray &fetchingContent, bool blocking=false) const
Gets the file content corresponding to the given path.
long mTotalSize
Estimated total size of all cached content.
void trimToMaximumSize()
Removes the least used cache entries until the maximum cache size is under the predefined size limit.
static QString defaultThemePath()
Returns the path to the default theme directory.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
QSizeF svgViewboxSize(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, double fixedAspectRatio=0, bool blocking=false, const QMap< QString, QString > ¶meters=QMap< QString, QString >())
Calculates the viewbox size of a (possibly cached) SVG file.
QByteArray getImageData(const QString &path, bool blocking=false) const
Gets the SVG content corresponding to the given path.
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, const QMap< QString, QString > ¶meters=QMap< QString, QString >())
Returns an SVG drawing as a QPicture.
QgsSvgCache(QObject *parent=nullptr)
Constructor for QgsSvgCache.
bool checkReply(QNetworkReply *reply, const QString &path) const override
Runs additional checks on a network reply to ensure that the reply content is consistent with that re...
void containsParams(const QString &path, bool &hasFillParam, QColor &defaultFillColor, bool &hasStrokeParam, QColor &defaultStrokeColor, bool &hasStrokeWidthParam, double &defaultStrokeWidth, bool blocking=false) const
Tests if an SVG file contains parameters for fill, stroke color, stroke width.
void remoteSvgFetched(const QString &url)
Emitted when the cache has finished retrieving an SVG file from a remote url.
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, const QMap< QString, QString > ¶meters=QMap< QString, QString >())
Returns an SVG drawing as a QImage.
QByteArray svgContent(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, double fixedAspectRatio=0, bool blocking=false, const QMap< QString, QString > ¶meters=QMap< QString, QString >(), bool *isMissingImage=nullptr)
Gets the SVG content corresponding to the given path.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
#define QgsDebugMsgLevel(str, level)