19#include "moc_qgssvgcache.cpp" 
   28#include <QApplication> 
   29#include <QCoreApplication> 
   31#include <QDomDocument> 
   37#include <QRegularExpression> 
   38#include <QSvgRenderer> 
   40#include <QNetworkReply> 
   41#include <QNetworkRequest> 
   49QgsSvgCacheEntry::QgsSvgCacheEntry( 
const QString &path, 
double size, 
double strokeWidth, 
double widthScaleFactor, 
const QColor &fill, 
const QColor &stroke, 
double fixedAspectRatio, 
const QMap<QString, QString> ¶meters )
 
   52  , strokeWidth( strokeWidth )
 
   53  , widthScaleFactor( widthScaleFactor )
 
   54  , fixedAspectRatio( fixedAspectRatio )
 
   57  , parameters( parameters )
 
   63  const QgsSvgCacheEntry *otherSvg = 
dynamic_cast< const QgsSvgCacheEntry * 
>( other );
 
   66       || !
qgsDoubleNear( otherSvg->fixedAspectRatio, fixedAspectRatio )
 
   69       || !
qgsDoubleNear( otherSvg->widthScaleFactor, widthScaleFactor )
 
   70       || otherSvg->fill != fill
 
   71       || otherSvg->stroke != stroke
 
   72       || otherSvg->path != path
 
   73       || otherSvg->parameters != parameters )
 
   79int QgsSvgCacheEntry::dataSize()
 const 
   81  int size = svgContent.size();
 
   84    size += picture->size();
 
   88    size += ( image->width() * image->height() * 32 );
 
   93void QgsSvgCacheEntry::dump()
 const 
   95  QgsDebugMsgLevel( QStringLiteral( 
"path: %1, size %2, width scale factor %3" ).arg( path ).arg( size ).arg( widthScaleFactor ), 4 );
 
  107  mMissingSvg = QStringLiteral( 
"<svg width='10' height='10'><text x='5' y='10' font-size='10' text-anchor='middle'>?</text></svg>" ).toLatin1();
 
  110  if ( QFile::exists( downloadingSvgPath ) )
 
  112    QFile file( downloadingSvgPath );
 
  113    if ( file.open( QIODevice::ReadOnly ) )
 
  115      mFetchingSvg = file.readAll();
 
  119  if ( mFetchingSvg.isEmpty() )
 
  121    mFetchingSvg = QStringLiteral( 
"<svg width='10' height='10'><text x='5' y='10' font-size='10' text-anchor='middle'>?</text></svg>" ).toLatin1();
 
 
  127QImage 
QgsSvgCache::svgAsImage( 
const QString &file, 
double size, 
const QColor &fill, 
const QColor &stroke, 
double strokeWidth,
 
  128                                double widthScaleFactor, 
bool &fitsInCache, 
double fixedAspectRatio, 
bool blocking, 
const QMap<QString, QString> ¶meters )
 
  130  const QMutexLocker locker( &
mMutex );
 
  133  QgsSvgCacheEntry *currentEntry = cacheEntry( file, size, fill, stroke, strokeWidth, widthScaleFactor, fixedAspectRatio, parameters, blocking );
 
  140  if ( !currentEntry->image )
 
  142    const QSvgRenderer r( currentEntry->svgContent );
 
  143    double hwRatio = 1.0;
 
  144    if ( r.viewBoxF().width() > 0 )
 
  146      if ( currentEntry->fixedAspectRatio > 0 )
 
  148        hwRatio = currentEntry->fixedAspectRatio;
 
  152        hwRatio = r.viewBoxF().height() / r.viewBoxF().width();
 
  155    long cachedDataSize = 0;
 
  156    cachedDataSize += currentEntry->svgContent.size();
 
  157    cachedDataSize += 
static_cast< int >( currentEntry->size * currentEntry->size * hwRatio * 32 );
 
  161      currentEntry->image.reset();
 
  164      if ( !currentEntry->picture )
 
  166        cachePicture( currentEntry, 
false );
 
  170      result = imageFromCachedPicture( *currentEntry );
 
  174      cacheImage( currentEntry );
 
  175      result = *( currentEntry->image );
 
  181    result = *( currentEntry->image );
 
 
  188                                    double widthScaleFactor, 
bool forceVectorOutput, 
double fixedAspectRatio, 
bool blocking, 
const QMap<QString, QString> ¶meters )
 
  190  const QMutexLocker locker( &
mMutex );
 
  192  QgsSvgCacheEntry *currentEntry = cacheEntry( path, size, fill, stroke, strokeWidth, widthScaleFactor, fixedAspectRatio, parameters, blocking );
 
  196  if ( !currentEntry->picture )
 
  198    cachePicture( currentEntry, forceVectorOutput );
 
  207  p.setData( currentEntry->picture->data(), currentEntry->picture->size() );
 
 
  211QByteArray 
QgsSvgCache::svgContent( 
const QString &path, 
double size, 
const QColor &fill, 
const QColor &stroke, 
double strokeWidth,
 
  212                                    double widthScaleFactor, 
double fixedAspectRatio, 
bool blocking, 
const QMap<QString, QString> ¶meters, 
bool *isMissingImage )
 
  214  const QMutexLocker locker( &
mMutex );
 
  216  QgsSvgCacheEntry *currentEntry = cacheEntry( path, size, fill, stroke, strokeWidth, widthScaleFactor, fixedAspectRatio, parameters, blocking, isMissingImage );
 
  218  return currentEntry->svgContent;
 
 
  222                                    double widthScaleFactor, 
double fixedAspectRatio, 
bool blocking, 
const QMap<QString, QString> ¶meters )
 
  224  const QMutexLocker locker( &
mMutex );
 
  226  QgsSvgCacheEntry *currentEntry = cacheEntry( path, size, fill, stroke, strokeWidth, widthScaleFactor, fixedAspectRatio, parameters, blocking );
 
  227  return currentEntry->viewboxSize;
 
 
  230void QgsSvgCache::containsParams( 
const QString &path, 
bool &hasFillParam, QColor &defaultFillColor, 
bool &hasStrokeParam, QColor &defaultStrokeColor,
 
  231                                  bool &hasStrokeWidthParam, 
double &defaultStrokeWidth, 
bool blocking )
 const 
  233  bool hasDefaultFillColor = 
false;
 
  234  bool hasFillOpacityParam = 
false;
 
  235  bool hasDefaultFillOpacity = 
false;
 
  236  double defaultFillOpacity = 1.0;
 
  237  bool hasDefaultStrokeColor = 
false;
 
  238  bool hasDefaultStrokeWidth = 
false;
 
  239  bool hasStrokeOpacityParam = 
false;
 
  240  bool hasDefaultStrokeOpacity = 
false;
 
  241  double defaultStrokeOpacity = 1.0;
 
  243  containsParams( path, hasFillParam, hasDefaultFillColor, defaultFillColor,
 
  244                  hasFillOpacityParam, hasDefaultFillOpacity, defaultFillOpacity,
 
  245                  hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
 
  246                  hasStrokeWidthParam, hasDefaultStrokeWidth, defaultStrokeWidth,
 
  247                  hasStrokeOpacityParam, hasDefaultStrokeOpacity, defaultStrokeOpacity,
 
 
  252                                  bool &hasFillParam, 
bool &hasDefaultFillParam, QColor &defaultFillColor,
 
  253                                  bool &hasFillOpacityParam, 
bool &hasDefaultFillOpacity, 
double &defaultFillOpacity,
 
  254                                  bool &hasStrokeParam, 
bool &hasDefaultStrokeColor, QColor &defaultStrokeColor,
 
  255                                  bool &hasStrokeWidthParam, 
bool &hasDefaultStrokeWidth, 
double &defaultStrokeWidth,
 
  256                                  bool &hasStrokeOpacityParam, 
bool &hasDefaultStrokeOpacity, 
double &defaultStrokeOpacity,
 
  257                                  bool blocking )
 const 
  259  hasFillParam = 
false;
 
  260  hasFillOpacityParam = 
false;
 
  261  hasStrokeParam = 
false;
 
  262  hasStrokeWidthParam = 
false;
 
  263  hasStrokeOpacityParam = 
false;
 
  264  defaultFillColor = QColor( Qt::white );
 
  265  defaultFillOpacity = 1.0;
 
  266  defaultStrokeColor = QColor( Qt::black );
 
  267  defaultStrokeWidth = 0.2;
 
  268  defaultStrokeOpacity = 1.0;
 
  270  hasDefaultFillParam = 
false;
 
  271  hasDefaultFillOpacity = 
false;
 
  272  hasDefaultStrokeColor = 
false;
 
  273  hasDefaultStrokeWidth = 
false;
 
  274  hasDefaultStrokeOpacity = 
false;
 
  277  if ( !svgDoc.setContent( 
getContent( path, mMissingSvg, mFetchingSvg, blocking ) ) )
 
  282  const QDomElement docElem = svgDoc.documentElement();
 
  283  containsElemParams( docElem, hasFillParam, hasDefaultFillParam, defaultFillColor,
 
  284                      hasFillOpacityParam, hasDefaultFillOpacity, defaultFillOpacity,
 
  285                      hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
 
  286                      hasStrokeWidthParam, hasDefaultStrokeWidth, defaultStrokeWidth,
 
  287                      hasStrokeOpacityParam, hasDefaultStrokeOpacity, defaultStrokeOpacity );
 
 
  290void QgsSvgCache::replaceParamsAndCacheSvg( QgsSvgCacheEntry *entry, 
bool blocking )
 
  297  const QByteArray content = 
getContent( entry->path, mMissingSvg, mFetchingSvg, blocking ) ;
 
  298  entry->isMissingImage = content == mMissingSvg;
 
  300  if ( !svgDoc.setContent( content ) )
 
  306  QDomElement docElem = svgDoc.documentElement();
 
  309  const double sizeScaleFactor = calcSizeScaleFactor( entry, docElem, viewboxSize );
 
  310  entry->viewboxSize = viewboxSize;
 
  311  replaceElemParams( docElem, entry->fill, entry->stroke, entry->strokeWidth * sizeScaleFactor, entry->parameters );
 
  313  entry->svgContent = svgDoc.toByteArray( 0 );
 
  318  entry->svgContent.replace( 
"\n<tspan", 
"<tspan" );
 
  319  entry->svgContent.replace( 
"</tspan>\n", 
"</tspan>" );
 
  324double QgsSvgCache::calcSizeScaleFactor( QgsSvgCacheEntry *entry, 
const QDomElement &docElem, QSizeF &viewboxSize )
 const 
  334  if ( docElem.tagName() == QLatin1String( 
"svg" ) && docElem.hasAttribute( QStringLiteral( 
"viewBox" ) ) )
 
  336    viewBox = docElem.attribute( QStringLiteral( 
"viewBox" ), QString() );
 
  338  else if ( docElem.tagName() == QLatin1String( 
"svg" ) && docElem.hasAttribute( QStringLiteral( 
"viewbox" ) ) )
 
  340    viewBox = docElem.attribute( QStringLiteral( 
"viewbox" ), QString() );
 
  344    const QDomElement svgElem = docElem.firstChildElement( QStringLiteral( 
"svg" ) );
 
  345    if ( !svgElem.isNull() )
 
  347      if ( svgElem.hasAttribute( QStringLiteral( 
"viewBox" ) ) )
 
  348        viewBox = svgElem.attribute( QStringLiteral( 
"viewBox" ), QString() );
 
  349      else if ( svgElem.hasAttribute( QStringLiteral( 
"viewbox" ) ) )
 
  350        viewBox = svgElem.attribute( QStringLiteral( 
"viewbox" ), QString() );
 
  355  if ( viewBox.isEmpty() )
 
  358    if ( docElem.tagName() == QLatin1String( 
"svg" ) && docElem.hasAttribute( QStringLiteral( 
"width" ) ) )
 
  360      const QString widthString = docElem.attribute( QStringLiteral( 
"width" ) );
 
  361      const thread_local QRegularExpression measureRegEx( QStringLiteral( 
"([\\d\\.]+).*?$" ) );
 
  362      const QRegularExpressionMatch widthMatch = measureRegEx.match( widthString );
 
  363      if ( widthMatch.hasMatch() )
 
  365        const double width = widthMatch.captured( 1 ).toDouble();
 
  366        const QString heightString = docElem.attribute( QStringLiteral( 
"height" ) );
 
  368        const QRegularExpressionMatch heightMatch = measureRegEx.match( heightString );
 
  369        if ( heightMatch.hasMatch() )
 
  371          const double height = heightMatch.captured( 1 ).toDouble();
 
  372          viewboxSize = QSizeF( width, height );
 
  373          return width / entry->size;
 
  383  const QStringList parts = viewBox.split( 
' ' );
 
  384  if ( parts.count() != 4 )
 
  387  bool heightOk = 
false;
 
  388  const double height = parts.at( 3 ).toDouble( &heightOk );
 
  390  bool widthOk = 
false;
 
  391  const double width = parts.at( 2 ).toDouble( &widthOk );
 
  395      viewboxSize = QSizeF( width, height );
 
  396    return width / entry->size;
 
  405  return getContent( path, mMissingSvg, mFetchingSvg, blocking );
 
 
  412  const QString contentType = reply->header( QNetworkRequest::ContentTypeHeader ).toString();
 
  413  if ( !contentType.startsWith( QLatin1String( 
"image/svg+xml" ), Qt::CaseInsensitive )
 
  414       && !contentType.startsWith( QLatin1String( 
"text/plain" ), Qt::CaseInsensitive ) )
 
 
  422void QgsSvgCache::cacheImage( QgsSvgCacheEntry *entry )
 
  429  entry->image.reset();
 
  433  const QSize imageSize = sizeForImage( *entry, viewBoxSize, scaledSize );
 
  436  auto image = std::make_unique< QImage >( imageSize, QImage::Format_ARGB32_Premultiplied );
 
  439  const bool isFixedAR = entry->fixedAspectRatio > 0;
 
  441  QPainter p( image.get() );
 
  442  QSvgRenderer r( entry->svgContent );
 
  443  if ( 
qgsDoubleNear( viewBoxSize.width(), viewBoxSize.height() ) )
 
  449    QSizeF s( viewBoxSize );
 
  450    s.scale( scaledSize.width(), scaledSize.height(), isFixedAR ? Qt::IgnoreAspectRatio : Qt::KeepAspectRatio );
 
  451    const QRectF rect( ( imageSize.width() - s.width() ) / 2, ( imageSize.height() - s.height() ) / 2, s.width(), s.height() );
 
  452    r.render( &p, rect );
 
  455  mTotalSize += ( image->width() * image->height() * 32 );
 
  456  entry->image = std::move( image );
 
  459void QgsSvgCache::cachePicture( QgsSvgCacheEntry *entry, 
bool forceVectorOutput )
 
  461  Q_UNUSED( forceVectorOutput )
 
  467  entry->picture.reset();
 
  469  const bool isFixedAR = entry->fixedAspectRatio > 0;
 
  472  auto picture = std::make_unique< QPicture >();
 
  474  QSvgRenderer r( entry->svgContent );
 
  475  double hwRatio = 1.0;
 
  476  if ( r.viewBoxF().width() > 0 )
 
  480      hwRatio = entry->fixedAspectRatio;
 
  484      hwRatio = r.viewBoxF().height() / r.viewBoxF().width();
 
  488  const double wSize = entry->size;
 
  489  const double hSize = wSize * hwRatio;
 
  491  QSizeF s( r.viewBoxF().size() );
 
  492  s.scale( wSize, hSize, isFixedAR ? Qt::IgnoreAspectRatio : Qt::KeepAspectRatio );
 
  493  rect = QRectF( -s.width() / 2.0, -s.height() / 2.0, s.width(), s.height() );
 
  495  QPainter p( picture.get() );
 
  496  r.render( &p, rect );
 
  497  entry->picture = std::move( picture );
 
  501QgsSvgCacheEntry *QgsSvgCache::cacheEntry( 
const QString &path, 
double size, 
const QColor &fill, 
const QColor &stroke, 
double strokeWidth,
 
  502    double widthScaleFactor, 
double fixedAspectRatio, 
const QMap<QString, QString> ¶meters, 
bool blocking, 
bool *isMissingImage )
 
  504  QgsSvgCacheEntry *currentEntry = 
findExistingEntry( 
new QgsSvgCacheEntry( path, size, strokeWidth, widthScaleFactor, fill, stroke, fixedAspectRatio, parameters ) );
 
  506  if ( currentEntry->svgContent.isEmpty() )
 
  508    replaceParamsAndCacheSvg( currentEntry, blocking );
 
  511  if ( isMissingImage )
 
  512    *isMissingImage = currentEntry->isMissingImage;
 
  518void QgsSvgCache::replaceElemParams( QDomElement &elem, 
const QColor &fill, 
const QColor &stroke, 
double strokeWidth, 
const QMap<QString, QString> ¶meters )
 
  526  const QDomNamedNodeMap attributes = elem.attributes();
 
  527  const int nAttributes = attributes.count();
 
  528  for ( 
int i = 0; i < nAttributes; ++i )
 
  530    const QDomAttr attribute = attributes.item( i ).toAttr();
 
  532    if ( attribute.name().compare( QLatin1String( 
"style" ), Qt::CaseInsensitive ) == 0 )
 
  535      QString newAttributeString;
 
  537      const QStringList entryList = attribute.value().split( 
';' );
 
  538      QStringList::const_iterator entryIt = entryList.constBegin();
 
  539      for ( ; entryIt != entryList.constEnd(); ++entryIt )
 
  541        const QStringList keyValueSplit = entryIt->split( 
':' );
 
  542        if ( keyValueSplit.size() < 2 )
 
  546        const QString key = keyValueSplit.at( 0 );
 
  547        QString value = keyValueSplit.at( 1 );
 
  548        QString newValue = value;
 
  549        value = value.trimmed().toLower();
 
  551        if ( value.startsWith( QLatin1String( 
"param(fill)" ) ) )
 
  553          newValue = fill.name();
 
  555        else if ( value.startsWith( QLatin1String( 
"param(fill-opacity)" ) ) )
 
  557          newValue = QString::number( fill.alphaF() );
 
  559        else if ( value.startsWith( QLatin1String( 
"param(outline)" ) ) )
 
  561          newValue = stroke.name();
 
  563        else if ( value.startsWith( QLatin1String( 
"param(outline-opacity)" ) ) )
 
  565          newValue = QString::number( stroke.alphaF() );
 
  567        else if ( value.startsWith( QLatin1String( 
"param(outline-width)" ) ) )
 
  569          newValue = QString::number( strokeWidth );
 
  572        if ( entryIt != entryList.constBegin() )
 
  574          newAttributeString.append( 
';' );
 
  576        newAttributeString.append( key + 
':' + newValue );
 
  578      elem.setAttribute( attribute.name(), newAttributeString );
 
  582      const QString value = attribute.value().trimmed().toLower();
 
  583      if ( value.startsWith( QLatin1String( 
"param(fill)" ) ) )
 
  585        elem.setAttribute( attribute.name(), fill.name() );
 
  587      else if ( value.startsWith( QLatin1String( 
"param(fill-opacity)" ) ) )
 
  589        elem.setAttribute( attribute.name(), fill.alphaF() );
 
  591      else if ( value.startsWith( QLatin1String( 
"param(outline)" ) ) )
 
  593        elem.setAttribute( attribute.name(), stroke.name() );
 
  595      else if ( value.startsWith( QLatin1String( 
"param(outline-opacity)" ) ) )
 
  597        elem.setAttribute( attribute.name(), stroke.alphaF() );
 
  599      else if ( value.startsWith( QLatin1String( 
"param(outline-width)" ) ) )
 
  601        elem.setAttribute( attribute.name(), QString::number( strokeWidth ) );
 
  605        QMap<QString, QString>::const_iterator paramIt = parameters.constBegin();
 
  606        for ( ; paramIt != parameters.constEnd(); ++paramIt )
 
  608          if ( value.startsWith( QString( QLatin1String( 
"param(%1)" ) ).arg( paramIt.key() ) ) )
 
  610            elem.setAttribute( attribute.name(), paramIt.value() );
 
  618  QDomNode child = elem.firstChild();
 
  619  if ( child.isText() && child.nodeValue().startsWith( 
"param(" ) )
 
  621    QMap<QString, QString>::const_iterator paramIt = parameters.constBegin();
 
  622    for ( ; paramIt != parameters.constEnd(); ++paramIt )
 
  624      if ( child.toText().data().startsWith( QString( QLatin1String( 
"param(%1)" ) ).arg( paramIt.key() ) ) )
 
  626        child.setNodeValue( paramIt.value() );
 
  632  const QDomNodeList childList = elem.childNodes();
 
  633  const int nChildren = childList.count();
 
  634  for ( 
int i = 0; i < nChildren; ++i )
 
  636    QDomElement childElem = childList.at( i ).toElement();
 
  637    replaceElemParams( childElem, fill, stroke, strokeWidth, parameters );
 
  641void QgsSvgCache::containsElemParams( 
const QDomElement &elem, 
bool &hasFillParam, 
bool &hasDefaultFill, QColor &defaultFill,
 
  642                                      bool &hasFillOpacityParam, 
bool &hasDefaultFillOpacity, 
double &defaultFillOpacity,
 
  643                                      bool &hasStrokeParam, 
bool &hasDefaultStroke, QColor &defaultStroke,
 
  644                                      bool &hasStrokeWidthParam, 
bool &hasDefaultStrokeWidth, 
double &defaultStrokeWidth,
 
  645                                      bool &hasStrokeOpacityParam, 
bool &hasDefaultStrokeOpacity, 
double &defaultStrokeOpacity )
 const 
  653  if ( hasFillParam && hasStrokeParam && hasStrokeWidthParam && hasFillOpacityParam && hasStrokeOpacityParam )
 
  659  const QDomNamedNodeMap attributes = elem.attributes();
 
  660  const int nAttributes = attributes.count();
 
  662  QStringList valueSplit;
 
  663  for ( 
int i = 0; i < nAttributes; ++i )
 
  665    const QDomAttr attribute = attributes.item( i ).toAttr();
 
  666    if ( attribute.name().compare( QLatin1String( 
"style" ), Qt::CaseInsensitive ) == 0 )
 
  669      const QStringList entryList = attribute.value().split( 
';' );
 
  670      QStringList::const_iterator entryIt = entryList.constBegin();
 
  671      for ( ; entryIt != entryList.constEnd(); ++entryIt )
 
  673        const QStringList keyValueSplit = entryIt->split( 
':' );
 
  674        if ( keyValueSplit.size() < 2 )
 
  678        const QString value = keyValueSplit.at( 1 );
 
  679        valueSplit = value.split( 
' ' );
 
  680        if ( !hasFillParam && value.startsWith( QLatin1String( 
"param(fill)" ) ) )
 
  683          if ( valueSplit.size() > 1 )
 
  685            defaultFill = QColor( valueSplit.at( 1 ) );
 
  686            hasDefaultFill = 
true;
 
  689        else if ( !hasFillOpacityParam && value.startsWith( QLatin1String( 
"param(fill-opacity)" ) ) )
 
  691          hasFillOpacityParam = 
true;
 
  692          if ( valueSplit.size() > 1 )
 
  695            const double opacity = valueSplit.at( 1 ).toDouble( &ok );
 
  698              defaultFillOpacity = opacity;
 
  699              hasDefaultFillOpacity = 
true;
 
  703        else if ( !hasStrokeParam && value.startsWith( QLatin1String( 
"param(outline)" ) ) )
 
  705          hasStrokeParam = 
true;
 
  706          if ( valueSplit.size() > 1 )
 
  708            defaultStroke = QColor( valueSplit.at( 1 ) );
 
  709            hasDefaultStroke = 
true;
 
  712        else if ( !hasStrokeWidthParam && value.startsWith( QLatin1String( 
"param(outline-width)" ) ) )
 
  714          hasStrokeWidthParam = 
true;
 
  715          if ( valueSplit.size() > 1 )
 
  717            defaultStrokeWidth = valueSplit.at( 1 ).toDouble();
 
  718            hasDefaultStrokeWidth = 
true;
 
  721        else if ( !hasStrokeOpacityParam && value.startsWith( QLatin1String( 
"param(outline-opacity)" ) ) )
 
  723          hasStrokeOpacityParam = 
true;
 
  724          if ( valueSplit.size() > 1 )
 
  727            const double opacity = valueSplit.at( 1 ).toDouble( &ok );
 
  730              defaultStrokeOpacity = opacity;
 
  731              hasDefaultStrokeOpacity = 
true;
 
  739      const QString value = attribute.value();
 
  740      valueSplit = value.split( 
' ' );
 
  741      if ( !hasFillParam && value.startsWith( QLatin1String( 
"param(fill)" ) ) )
 
  744        if ( valueSplit.size() > 1 )
 
  746          defaultFill = QColor( valueSplit.at( 1 ) );
 
  747          hasDefaultFill = 
true;
 
  750      else if ( !hasFillOpacityParam && value.startsWith( QLatin1String( 
"param(fill-opacity)" ) ) )
 
  752        hasFillOpacityParam = 
true;
 
  753        if ( valueSplit.size() > 1 )
 
  756          const double opacity = valueSplit.at( 1 ).toDouble( &ok );
 
  759            defaultFillOpacity = opacity;
 
  760            hasDefaultFillOpacity = 
true;
 
  764      else if ( !hasStrokeParam && value.startsWith( QLatin1String( 
"param(outline)" ) ) )
 
  766        hasStrokeParam = 
true;
 
  767        if ( valueSplit.size() > 1 )
 
  769          defaultStroke = QColor( valueSplit.at( 1 ) );
 
  770          hasDefaultStroke = 
true;
 
  773      else if ( !hasStrokeWidthParam && value.startsWith( QLatin1String( 
"param(outline-width)" ) ) )
 
  775        hasStrokeWidthParam = 
true;
 
  776        if ( valueSplit.size() > 1 )
 
  778          defaultStrokeWidth = valueSplit.at( 1 ).toDouble();
 
  779          hasDefaultStrokeWidth = 
true;
 
  782      else if ( !hasStrokeOpacityParam && value.startsWith( QLatin1String( 
"param(outline-opacity)" ) ) )
 
  784        hasStrokeOpacityParam = 
true;
 
  785        if ( valueSplit.size() > 1 )
 
  788          const double opacity = valueSplit.at( 1 ).toDouble( &ok );
 
  791            defaultStrokeOpacity = opacity;
 
  792            hasDefaultStrokeOpacity = 
true;
 
  800  const QDomNodeList childList = elem.childNodes();
 
  801  const int nChildren = childList.count();
 
  802  for ( 
int i = 0; i < nChildren; ++i )
 
  804    const QDomElement childElem = childList.at( i ).toElement();
 
  805    containsElemParams( childElem, hasFillParam, hasDefaultFill, defaultFill,
 
  806                        hasFillOpacityParam, hasDefaultFillOpacity, defaultFillOpacity,
 
  807                        hasStrokeParam, hasDefaultStroke, defaultStroke,
 
  808                        hasStrokeWidthParam, hasDefaultStrokeWidth, defaultStrokeWidth,
 
  809                        hasStrokeOpacityParam, hasDefaultStrokeOpacity, defaultStrokeOpacity );
 
  813QSize QgsSvgCache::sizeForImage( 
const QgsSvgCacheEntry &entry, QSizeF &viewBoxSize, QSizeF &scaledSize )
 const 
  815  const bool isFixedAR = entry.fixedAspectRatio > 0;
 
  817  const QSvgRenderer r( entry.svgContent );
 
  818  double hwRatio = 1.0;
 
  819  viewBoxSize = r.viewBoxF().size();
 
  820  if ( viewBoxSize.width() > 0 )
 
  824      hwRatio = entry.fixedAspectRatio;
 
  828      hwRatio = viewBoxSize.height() / viewBoxSize.width();
 
  833  scaledSize.setWidth( entry.size );
 
  834  int wImgSize = 
static_cast< int >( scaledSize.width() );
 
  839  scaledSize.setHeight( scaledSize.width() * hwRatio );
 
  840  int hImgSize = 
static_cast< int >( scaledSize.height() );
 
  845  return QSize( wImgSize, hImgSize );
 
  848QImage QgsSvgCache::imageFromCachedPicture( 
const QgsSvgCacheEntry &entry )
 const 
  852  QImage image( sizeForImage( entry, viewBoxSize, scaledSize ), QImage::Format_ARGB32_Premultiplied );
 
  855  QPainter p( &image );
 
  856  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, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE())
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)