27#include <QApplication> 
   28#include <QCoreApplication> 
   30#include <QDomDocument> 
   36#include <QRegularExpression> 
   37#include <QSvgRenderer> 
   39#include <QNetworkReply> 
   40#include <QNetworkRequest> 
   48QgsSvgCacheEntry::QgsSvgCacheEntry( 
const QString &path, 
double size, 
double strokeWidth, 
double widthScaleFactor, 
const QColor &fill, 
const QColor &stroke, 
double fixedAspectRatio, 
const QMap<QString, QString> ¶meters )
 
   51  , strokeWidth( strokeWidth )
 
   52  , widthScaleFactor( widthScaleFactor )
 
   53  , fixedAspectRatio( fixedAspectRatio )
 
   56  , parameters( parameters )
 
   62  const QgsSvgCacheEntry *otherSvg = 
dynamic_cast< const QgsSvgCacheEntry * 
>( other );
 
   65       || !
qgsDoubleNear( otherSvg->fixedAspectRatio, fixedAspectRatio )
 
   68       || !
qgsDoubleNear( otherSvg->widthScaleFactor, widthScaleFactor )
 
   69       || otherSvg->fill != fill
 
   70       || otherSvg->stroke != stroke
 
   71       || otherSvg->path != path
 
   72       || otherSvg->parameters != parameters )
 
   78int QgsSvgCacheEntry::dataSize()
 const 
   80  int size = svgContent.size();
 
   83    size += picture->size();
 
   87    size += ( image->width() * image->height() * 32 );
 
   92void QgsSvgCacheEntry::dump()
 const 
   94  QgsDebugMsgLevel( QStringLiteral( 
"path: %1, size %2, width scale factor %3" ).arg( path ).arg( size ).arg( widthScaleFactor ), 4 );
 
  106  mMissingSvg = QStringLiteral( 
"<svg width='10' height='10'><text x='5' y='10' font-size='10' text-anchor='middle'>?</text></svg>" ).toLatin1();
 
  109  if ( QFile::exists( downloadingSvgPath ) )
 
  111    QFile file( downloadingSvgPath );
 
  112    if ( file.open( QIODevice::ReadOnly ) )
 
  114      mFetchingSvg = file.readAll();
 
  118  if ( mFetchingSvg.isEmpty() )
 
  120    mFetchingSvg = QStringLiteral( 
"<svg width='10' height='10'><text x='5' y='10' font-size='10' text-anchor='middle'>?</text></svg>" ).toLatin1();
 
 
  126QImage 
QgsSvgCache::svgAsImage( 
const QString &file, 
double size, 
const QColor &fill, 
const QColor &stroke, 
double strokeWidth,
 
  127                                double widthScaleFactor, 
bool &fitsInCache, 
double fixedAspectRatio, 
bool blocking, 
const QMap<QString, QString> ¶meters )
 
  129  const QMutexLocker locker( &
mMutex );
 
  132  QgsSvgCacheEntry *currentEntry = cacheEntry( file, size, fill, stroke, strokeWidth, widthScaleFactor, fixedAspectRatio, parameters, blocking );
 
  139  if ( !currentEntry->image )
 
  141    const QSvgRenderer r( currentEntry->svgContent );
 
  142    double hwRatio = 1.0;
 
  143    if ( r.viewBoxF().width() > 0 )
 
  145      if ( currentEntry->fixedAspectRatio > 0 )
 
  147        hwRatio = currentEntry->fixedAspectRatio;
 
  151        hwRatio = r.viewBoxF().height() / r.viewBoxF().width();
 
  154    long cachedDataSize = 0;
 
  155    cachedDataSize += currentEntry->svgContent.size();
 
  156    cachedDataSize += 
static_cast< int >( currentEntry->size * currentEntry->size * hwRatio * 32 );
 
  160      currentEntry->image.reset();
 
  163      if ( !currentEntry->picture )
 
  165        cachePicture( currentEntry, 
false );
 
  169      result = imageFromCachedPicture( *currentEntry );
 
  173      cacheImage( currentEntry );
 
  174      result = *( currentEntry->image );
 
  180    result = *( currentEntry->image );
 
 
  187                                    double widthScaleFactor, 
bool forceVectorOutput, 
double fixedAspectRatio, 
bool blocking, 
const QMap<QString, QString> ¶meters )
 
  189  const QMutexLocker locker( &
mMutex );
 
  191  QgsSvgCacheEntry *currentEntry = cacheEntry( path, size, fill, stroke, strokeWidth, widthScaleFactor, fixedAspectRatio, parameters, blocking );
 
  195  if ( !currentEntry->picture )
 
  197    cachePicture( currentEntry, forceVectorOutput );
 
  206  p.setData( currentEntry->picture->data(), currentEntry->picture->size() );
 
 
  210QByteArray 
QgsSvgCache::svgContent( 
const QString &path, 
double size, 
const QColor &fill, 
const QColor &stroke, 
double strokeWidth,
 
  211                                    double widthScaleFactor, 
double fixedAspectRatio, 
bool blocking, 
const QMap<QString, QString> ¶meters, 
bool *isMissingImage )
 
  213  const QMutexLocker locker( &
mMutex );
 
  215  QgsSvgCacheEntry *currentEntry = cacheEntry( path, size, fill, stroke, strokeWidth, widthScaleFactor, fixedAspectRatio, parameters, blocking, isMissingImage );
 
  217  return currentEntry->svgContent;
 
 
  221                                    double widthScaleFactor, 
double fixedAspectRatio, 
bool blocking, 
const QMap<QString, QString> ¶meters )
 
  223  const QMutexLocker locker( &
mMutex );
 
  225  QgsSvgCacheEntry *currentEntry = cacheEntry( path, size, fill, stroke, strokeWidth, widthScaleFactor, fixedAspectRatio, parameters, blocking );
 
  226  return currentEntry->viewboxSize;
 
 
  229void QgsSvgCache::containsParams( 
const QString &path, 
bool &hasFillParam, QColor &defaultFillColor, 
bool &hasStrokeParam, QColor &defaultStrokeColor,
 
  230                                  bool &hasStrokeWidthParam, 
double &defaultStrokeWidth, 
bool blocking )
 const 
  232  bool hasDefaultFillColor = 
false;
 
  233  bool hasFillOpacityParam = 
false;
 
  234  bool hasDefaultFillOpacity = 
false;
 
  235  double defaultFillOpacity = 1.0;
 
  236  bool hasDefaultStrokeColor = 
false;
 
  237  bool hasDefaultStrokeWidth = 
false;
 
  238  bool hasStrokeOpacityParam = 
false;
 
  239  bool hasDefaultStrokeOpacity = 
false;
 
  240  double defaultStrokeOpacity = 1.0;
 
  242  containsParams( path, hasFillParam, hasDefaultFillColor, defaultFillColor,
 
  243                  hasFillOpacityParam, hasDefaultFillOpacity, defaultFillOpacity,
 
  244                  hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
 
  245                  hasStrokeWidthParam, hasDefaultStrokeWidth, defaultStrokeWidth,
 
  246                  hasStrokeOpacityParam, hasDefaultStrokeOpacity, defaultStrokeOpacity,
 
 
  251                                  bool &hasFillParam, 
bool &hasDefaultFillParam, QColor &defaultFillColor,
 
  252                                  bool &hasFillOpacityParam, 
bool &hasDefaultFillOpacity, 
double &defaultFillOpacity,
 
  253                                  bool &hasStrokeParam, 
bool &hasDefaultStrokeColor, QColor &defaultStrokeColor,
 
  254                                  bool &hasStrokeWidthParam, 
bool &hasDefaultStrokeWidth, 
double &defaultStrokeWidth,
 
  255                                  bool &hasStrokeOpacityParam, 
bool &hasDefaultStrokeOpacity, 
double &defaultStrokeOpacity,
 
  256                                  bool blocking )
 const 
  258  hasFillParam = 
false;
 
  259  hasFillOpacityParam = 
false;
 
  260  hasStrokeParam = 
false;
 
  261  hasStrokeWidthParam = 
false;
 
  262  hasStrokeOpacityParam = 
false;
 
  263  defaultFillColor = QColor( Qt::white );
 
  264  defaultFillOpacity = 1.0;
 
  265  defaultStrokeColor = QColor( Qt::black );
 
  266  defaultStrokeWidth = 0.2;
 
  267  defaultStrokeOpacity = 1.0;
 
  269  hasDefaultFillParam = 
false;
 
  270  hasDefaultFillOpacity = 
false;
 
  271  hasDefaultStrokeColor = 
false;
 
  272  hasDefaultStrokeWidth = 
false;
 
  273  hasDefaultStrokeOpacity = 
false;
 
  276  if ( !svgDoc.setContent( 
getContent( path, mMissingSvg, mFetchingSvg, blocking ) ) )
 
  281  const QDomElement docElem = svgDoc.documentElement();
 
  282  containsElemParams( docElem, hasFillParam, hasDefaultFillParam, defaultFillColor,
 
  283                      hasFillOpacityParam, hasDefaultFillOpacity, defaultFillOpacity,
 
  284                      hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
 
  285                      hasStrokeWidthParam, hasDefaultStrokeWidth, defaultStrokeWidth,
 
  286                      hasStrokeOpacityParam, hasDefaultStrokeOpacity, defaultStrokeOpacity );
 
 
  289void QgsSvgCache::replaceParamsAndCacheSvg( QgsSvgCacheEntry *entry, 
bool blocking )
 
  296  const QByteArray content = 
getContent( entry->path, mMissingSvg, mFetchingSvg, blocking ) ;
 
  297  entry->isMissingImage = content == mMissingSvg;
 
  299  if ( !svgDoc.setContent( content ) )
 
  305  QDomElement docElem = svgDoc.documentElement();
 
  308  const double sizeScaleFactor = calcSizeScaleFactor( entry, docElem, viewboxSize );
 
  309  entry->viewboxSize = viewboxSize;
 
  310  replaceElemParams( docElem, entry->fill, entry->stroke, entry->strokeWidth * sizeScaleFactor, entry->parameters );
 
  312  entry->svgContent = svgDoc.toByteArray( 0 );
 
  317  entry->svgContent.replace( 
"\n<tspan", 
"<tspan" );
 
  318  entry->svgContent.replace( 
"</tspan>\n", 
"</tspan>" );
 
  323double QgsSvgCache::calcSizeScaleFactor( QgsSvgCacheEntry *entry, 
const QDomElement &docElem, QSizeF &viewboxSize )
 const 
  333  if ( docElem.tagName() == QLatin1String( 
"svg" ) && docElem.hasAttribute( QStringLiteral( 
"viewBox" ) ) )
 
  335    viewBox = docElem.attribute( QStringLiteral( 
"viewBox" ), QString() );
 
  337  else if ( docElem.tagName() == QLatin1String( 
"svg" ) && docElem.hasAttribute( QStringLiteral( 
"viewbox" ) ) )
 
  339    viewBox = docElem.attribute( QStringLiteral( 
"viewbox" ), QString() );
 
  343    const QDomElement svgElem = docElem.firstChildElement( QStringLiteral( 
"svg" ) );
 
  344    if ( !svgElem.isNull() )
 
  346      if ( svgElem.hasAttribute( QStringLiteral( 
"viewBox" ) ) )
 
  347        viewBox = svgElem.attribute( QStringLiteral( 
"viewBox" ), QString() );
 
  348      else if ( svgElem.hasAttribute( QStringLiteral( 
"viewbox" ) ) )
 
  349        viewBox = svgElem.attribute( QStringLiteral( 
"viewbox" ), QString() );
 
  354  if ( viewBox.isEmpty() )
 
  357    if ( docElem.tagName() == QLatin1String( 
"svg" ) && docElem.hasAttribute( QStringLiteral( 
"width" ) ) )
 
  359      const QString widthString = docElem.attribute( QStringLiteral( 
"width" ) );
 
  360      const thread_local QRegularExpression measureRegEx( QStringLiteral( 
"([\\d\\.]+).*?$" ) );
 
  361      const QRegularExpressionMatch widthMatch = measureRegEx.match( widthString );
 
  362      if ( widthMatch.hasMatch() )
 
  364        const double width = widthMatch.captured( 1 ).toDouble();
 
  365        const QString heightString = docElem.attribute( QStringLiteral( 
"height" ) );
 
  367        const QRegularExpressionMatch heightMatch = measureRegEx.match( heightString );
 
  368        if ( heightMatch.hasMatch() )
 
  370          const double height = heightMatch.captured( 1 ).toDouble();
 
  371          viewboxSize = QSizeF( width, height );
 
  372          return width / entry->size;
 
  382  const QStringList parts = viewBox.split( 
' ' );
 
  383  if ( parts.count() != 4 )
 
  386  bool heightOk = 
false;
 
  387  const double height = parts.at( 3 ).toDouble( &heightOk );
 
  389  bool widthOk = 
false;
 
  390  const double width = parts.at( 2 ).toDouble( &widthOk );
 
  394      viewboxSize = QSizeF( width, height );
 
  395    return width / entry->size;
 
  404  return getContent( path, mMissingSvg, mFetchingSvg, blocking );
 
 
  411  const QString contentType = reply->header( QNetworkRequest::ContentTypeHeader ).toString();
 
  412  if ( !contentType.startsWith( QLatin1String( 
"image/svg+xml" ), Qt::CaseInsensitive )
 
  413       && !contentType.startsWith( QLatin1String( 
"text/plain" ), Qt::CaseInsensitive ) )
 
 
  421void QgsSvgCache::cacheImage( QgsSvgCacheEntry *entry )
 
  428  entry->image.reset();
 
  432  const QSize imageSize = sizeForImage( *entry, viewBoxSize, scaledSize );
 
  435  std::unique_ptr< QImage > image = std::make_unique< QImage >( imageSize, QImage::Format_ARGB32_Premultiplied );
 
  438  const bool isFixedAR = entry->fixedAspectRatio > 0;
 
  440  QPainter p( image.get() );
 
  441  QSvgRenderer r( entry->svgContent );
 
  442  if ( 
qgsDoubleNear( viewBoxSize.width(), viewBoxSize.height() ) )
 
  448    QSizeF s( viewBoxSize );
 
  449    s.scale( scaledSize.width(), scaledSize.height(), isFixedAR ? Qt::IgnoreAspectRatio : Qt::KeepAspectRatio );
 
  450    const QRectF rect( ( imageSize.width() - s.width() ) / 2, ( imageSize.height() - s.height() ) / 2, s.width(), s.height() );
 
  451    r.render( &p, rect );
 
  454  mTotalSize += ( image->width() * image->height() * 32 );
 
  455  entry->image = std::move( image );
 
  458void QgsSvgCache::cachePicture( QgsSvgCacheEntry *entry, 
bool forceVectorOutput )
 
  460  Q_UNUSED( forceVectorOutput )
 
  466  entry->picture.reset();
 
  468  const bool isFixedAR = entry->fixedAspectRatio > 0;
 
  471  std::unique_ptr< QPicture > picture = std::make_unique< QPicture >();
 
  473  QSvgRenderer r( entry->svgContent );
 
  474  double hwRatio = 1.0;
 
  475  if ( r.viewBoxF().width() > 0 )
 
  479      hwRatio = entry->fixedAspectRatio;
 
  483      hwRatio = r.viewBoxF().height() / r.viewBoxF().width();
 
  487  const double wSize = entry->size;
 
  488  const double hSize = wSize * hwRatio;
 
  490  QSizeF s( r.viewBoxF().size() );
 
  491  s.scale( wSize, hSize, isFixedAR ? Qt::IgnoreAspectRatio : Qt::KeepAspectRatio );
 
  492  rect = QRectF( -s.width() / 2.0, -s.height() / 2.0, s.width(), s.height() );
 
  494  QPainter p( picture.get() );
 
  495  r.render( &p, rect );
 
  496  entry->picture = std::move( picture );
 
  500QgsSvgCacheEntry *QgsSvgCache::cacheEntry( 
const QString &path, 
double size, 
const QColor &fill, 
const QColor &stroke, 
double strokeWidth,
 
  501    double widthScaleFactor, 
double fixedAspectRatio, 
const QMap<QString, QString> ¶meters, 
bool blocking, 
bool *isMissingImage )
 
  503  QgsSvgCacheEntry *currentEntry = 
findExistingEntry( 
new QgsSvgCacheEntry( path, size, strokeWidth, widthScaleFactor, fill, stroke, fixedAspectRatio, parameters ) );
 
  505  if ( currentEntry->svgContent.isEmpty() )
 
  507    replaceParamsAndCacheSvg( currentEntry, blocking );
 
  510  if ( isMissingImage )
 
  511    *isMissingImage = currentEntry->isMissingImage;
 
  517void QgsSvgCache::replaceElemParams( QDomElement &elem, 
const QColor &fill, 
const QColor &stroke, 
double strokeWidth, 
const QMap<QString, QString> ¶meters )
 
  525  const QDomNamedNodeMap attributes = elem.attributes();
 
  526  const int nAttributes = attributes.count();
 
  527  for ( 
int i = 0; i < nAttributes; ++i )
 
  529    const QDomAttr attribute = attributes.item( i ).toAttr();
 
  531    if ( attribute.name().compare( QLatin1String( 
"style" ), Qt::CaseInsensitive ) == 0 )
 
  534      QString newAttributeString;
 
  536      const QStringList entryList = attribute.value().split( 
';' );
 
  537      QStringList::const_iterator entryIt = entryList.constBegin();
 
  538      for ( ; entryIt != entryList.constEnd(); ++entryIt )
 
  540        const QStringList keyValueSplit = entryIt->split( 
':' );
 
  541        if ( keyValueSplit.size() < 2 )
 
  545        const QString key = keyValueSplit.at( 0 );
 
  546        QString value = keyValueSplit.at( 1 );
 
  547        QString newValue = value;
 
  548        value = value.trimmed().toLower();
 
  550        if ( value.startsWith( QLatin1String( 
"param(fill)" ) ) )
 
  552          newValue = fill.name();
 
  554        else if ( value.startsWith( QLatin1String( 
"param(fill-opacity)" ) ) )
 
  556          newValue = QString::number( fill.alphaF() );
 
  558        else if ( value.startsWith( QLatin1String( 
"param(outline)" ) ) )
 
  560          newValue = stroke.name();
 
  562        else if ( value.startsWith( QLatin1String( 
"param(outline-opacity)" ) ) )
 
  564          newValue = QString::number( stroke.alphaF() );
 
  566        else if ( value.startsWith( QLatin1String( 
"param(outline-width)" ) ) )
 
  568          newValue = QString::number( strokeWidth );
 
  571        if ( entryIt != entryList.constBegin() )
 
  573          newAttributeString.append( 
';' );
 
  575        newAttributeString.append( key + 
':' + newValue );
 
  577      elem.setAttribute( attribute.name(), newAttributeString );
 
  581      const QString value = attribute.value().trimmed().toLower();
 
  582      if ( value.startsWith( QLatin1String( 
"param(fill)" ) ) )
 
  584        elem.setAttribute( attribute.name(), fill.name() );
 
  586      else if ( value.startsWith( QLatin1String( 
"param(fill-opacity)" ) ) )
 
  588        elem.setAttribute( attribute.name(), fill.alphaF() );
 
  590      else if ( value.startsWith( QLatin1String( 
"param(outline)" ) ) )
 
  592        elem.setAttribute( attribute.name(), stroke.name() );
 
  594      else if ( value.startsWith( QLatin1String( 
"param(outline-opacity)" ) ) )
 
  596        elem.setAttribute( attribute.name(), stroke.alphaF() );
 
  598      else if ( value.startsWith( QLatin1String( 
"param(outline-width)" ) ) )
 
  600        elem.setAttribute( attribute.name(), QString::number( strokeWidth ) );
 
  604        QMap<QString, QString>::const_iterator paramIt = parameters.constBegin();
 
  605        for ( ; paramIt != parameters.constEnd(); ++paramIt )
 
  607          if ( value.startsWith( QString( QLatin1String( 
"param(%1)" ) ).arg( paramIt.key() ) ) )
 
  609            elem.setAttribute( attribute.name(), paramIt.value() );
 
  617  QDomNode child = elem.firstChild();
 
  618  if ( child.isText() && child.nodeValue().startsWith( 
"param(" ) )
 
  620    QMap<QString, QString>::const_iterator paramIt = parameters.constBegin();
 
  621    for ( ; paramIt != parameters.constEnd(); ++paramIt )
 
  623      if ( child.toText().data().startsWith( QString( QLatin1String( 
"param(%1)" ) ).arg( paramIt.key() ) ) )
 
  625        child.setNodeValue( paramIt.value() );
 
  631  const QDomNodeList childList = elem.childNodes();
 
  632  const int nChildren = childList.count();
 
  633  for ( 
int i = 0; i < nChildren; ++i )
 
  635    QDomElement childElem = childList.at( i ).toElement();
 
  636    replaceElemParams( childElem, fill, stroke, strokeWidth, parameters );
 
  640void QgsSvgCache::containsElemParams( 
const QDomElement &elem, 
bool &hasFillParam, 
bool &hasDefaultFill, QColor &defaultFill,
 
  641                                      bool &hasFillOpacityParam, 
bool &hasDefaultFillOpacity, 
double &defaultFillOpacity,
 
  642                                      bool &hasStrokeParam, 
bool &hasDefaultStroke, QColor &defaultStroke,
 
  643                                      bool &hasStrokeWidthParam, 
bool &hasDefaultStrokeWidth, 
double &defaultStrokeWidth,
 
  644                                      bool &hasStrokeOpacityParam, 
bool &hasDefaultStrokeOpacity, 
double &defaultStrokeOpacity )
 const 
  652  if ( hasFillParam && hasStrokeParam && hasStrokeWidthParam && hasFillOpacityParam && hasStrokeOpacityParam )
 
  658  const QDomNamedNodeMap attributes = elem.attributes();
 
  659  const int nAttributes = attributes.count();
 
  661  QStringList valueSplit;
 
  662  for ( 
int i = 0; i < nAttributes; ++i )
 
  664    const QDomAttr attribute = attributes.item( i ).toAttr();
 
  665    if ( attribute.name().compare( QLatin1String( 
"style" ), Qt::CaseInsensitive ) == 0 )
 
  668      const QStringList entryList = attribute.value().split( 
';' );
 
  669      QStringList::const_iterator entryIt = entryList.constBegin();
 
  670      for ( ; entryIt != entryList.constEnd(); ++entryIt )
 
  672        const QStringList keyValueSplit = entryIt->split( 
':' );
 
  673        if ( keyValueSplit.size() < 2 )
 
  677        const QString value = keyValueSplit.at( 1 );
 
  678        valueSplit = value.split( 
' ' );
 
  679        if ( !hasFillParam && value.startsWith( QLatin1String( 
"param(fill)" ) ) )
 
  682          if ( valueSplit.size() > 1 )
 
  684            defaultFill = QColor( valueSplit.at( 1 ) );
 
  685            hasDefaultFill = 
true;
 
  688        else if ( !hasFillOpacityParam && value.startsWith( QLatin1String( 
"param(fill-opacity)" ) ) )
 
  690          hasFillOpacityParam = 
true;
 
  691          if ( valueSplit.size() > 1 )
 
  694            const double opacity = valueSplit.at( 1 ).toDouble( &ok );
 
  697              defaultFillOpacity = opacity;
 
  698              hasDefaultFillOpacity = 
true;
 
  702        else if ( !hasStrokeParam && value.startsWith( QLatin1String( 
"param(outline)" ) ) )
 
  704          hasStrokeParam = 
true;
 
  705          if ( valueSplit.size() > 1 )
 
  707            defaultStroke = QColor( valueSplit.at( 1 ) );
 
  708            hasDefaultStroke = 
true;
 
  711        else if ( !hasStrokeWidthParam && value.startsWith( QLatin1String( 
"param(outline-width)" ) ) )
 
  713          hasStrokeWidthParam = 
true;
 
  714          if ( valueSplit.size() > 1 )
 
  716            defaultStrokeWidth = valueSplit.at( 1 ).toDouble();
 
  717            hasDefaultStrokeWidth = 
true;
 
  720        else if ( !hasStrokeOpacityParam && value.startsWith( QLatin1String( 
"param(outline-opacity)" ) ) )
 
  722          hasStrokeOpacityParam = 
true;
 
  723          if ( valueSplit.size() > 1 )
 
  726            const double opacity = valueSplit.at( 1 ).toDouble( &ok );
 
  729              defaultStrokeOpacity = opacity;
 
  730              hasDefaultStrokeOpacity = 
true;
 
  738      const QString value = attribute.value();
 
  739      valueSplit = value.split( 
' ' );
 
  740      if ( !hasFillParam && value.startsWith( QLatin1String( 
"param(fill)" ) ) )
 
  743        if ( valueSplit.size() > 1 )
 
  745          defaultFill = QColor( valueSplit.at( 1 ) );
 
  746          hasDefaultFill = 
true;
 
  749      else if ( !hasFillOpacityParam && value.startsWith( QLatin1String( 
"param(fill-opacity)" ) ) )
 
  751        hasFillOpacityParam = 
true;
 
  752        if ( valueSplit.size() > 1 )
 
  755          const double opacity = valueSplit.at( 1 ).toDouble( &ok );
 
  758            defaultFillOpacity = opacity;
 
  759            hasDefaultFillOpacity = 
true;
 
  763      else if ( !hasStrokeParam && value.startsWith( QLatin1String( 
"param(outline)" ) ) )
 
  765        hasStrokeParam = 
true;
 
  766        if ( valueSplit.size() > 1 )
 
  768          defaultStroke = QColor( valueSplit.at( 1 ) );
 
  769          hasDefaultStroke = 
true;
 
  772      else if ( !hasStrokeWidthParam && value.startsWith( QLatin1String( 
"param(outline-width)" ) ) )
 
  774        hasStrokeWidthParam = 
true;
 
  775        if ( valueSplit.size() > 1 )
 
  777          defaultStrokeWidth = valueSplit.at( 1 ).toDouble();
 
  778          hasDefaultStrokeWidth = 
true;
 
  781      else if ( !hasStrokeOpacityParam && value.startsWith( QLatin1String( 
"param(outline-opacity)" ) ) )
 
  783        hasStrokeOpacityParam = 
true;
 
  784        if ( valueSplit.size() > 1 )
 
  787          const double opacity = valueSplit.at( 1 ).toDouble( &ok );
 
  790            defaultStrokeOpacity = opacity;
 
  791            hasDefaultStrokeOpacity = 
true;
 
  799  const QDomNodeList childList = elem.childNodes();
 
  800  const int nChildren = childList.count();
 
  801  for ( 
int i = 0; i < nChildren; ++i )
 
  803    const QDomElement childElem = childList.at( i ).toElement();
 
  804    containsElemParams( childElem, hasFillParam, hasDefaultFill, defaultFill,
 
  805                        hasFillOpacityParam, hasDefaultFillOpacity, defaultFillOpacity,
 
  806                        hasStrokeParam, hasDefaultStroke, defaultStroke,
 
  807                        hasStrokeWidthParam, hasDefaultStrokeWidth, defaultStrokeWidth,
 
  808                        hasStrokeOpacityParam, hasDefaultStrokeOpacity, defaultStrokeOpacity );
 
  812QSize QgsSvgCache::sizeForImage( 
const QgsSvgCacheEntry &entry, QSizeF &viewBoxSize, QSizeF &scaledSize )
 const 
  814  const bool isFixedAR = entry.fixedAspectRatio > 0;
 
  816  const QSvgRenderer r( entry.svgContent );
 
  817  double hwRatio = 1.0;
 
  818  viewBoxSize = r.viewBoxF().size();
 
  819  if ( viewBoxSize.width() > 0 )
 
  823      hwRatio = entry.fixedAspectRatio;
 
  827      hwRatio = viewBoxSize.height() / viewBoxSize.width();
 
  832  scaledSize.setWidth( entry.size );
 
  833  int wImgSize = 
static_cast< int >( scaledSize.width() );
 
  838  scaledSize.setHeight( scaledSize.width() * hwRatio );
 
  839  int hImgSize = 
static_cast< int >( scaledSize.height() );
 
  844  return QSize( wImgSize, hImgSize );
 
  847QImage QgsSvgCache::imageFromCachedPicture( 
const QgsSvgCacheEntry &entry )
 const 
  851  QImage image( sizeForImage( entry, viewBoxSize, scaledSize ), QImage::Format_ARGB32_Premultiplied );
 
  854  QPainter p( &image );
 
  855  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.
 
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.
 
QgsSvgCacheEntry * findExistingEntry(QgsSvgCacheEntry *entryTemplate)
Returns the existing entry from the cache which matches entryTemplate (deleting entryTemplate when do...
 
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)