26 #include <QApplication> 27 #include <QCoreApplication> 29 #include <QDomDocument> 30 #include <QDomElement> 35 #include <QSvgRenderer> 37 #include <QNetworkReply> 38 #include <QNetworkRequest> 42 QgsSvgCacheEntry::QgsSvgCacheEntry(
const QString &p,
double s,
double ow,
double wsf,
const QColor &fi,
const QColor &ou,
double far )
44 , fileModified( QFileInfo( p ).lastModified() )
47 , widthScaleFactor( wsf )
48 , fixedAspectRatio( far )
52 fileModifiedLastCheckTimer.start();
58 && other.fixedAspectRatio == fixedAspectRatio && other.fill == fill && other.stroke == stroke;
60 if ( equal && ( mFileModifiedCheckTimeout <= 0 || fileModifiedLastCheckTimer.hasExpired( mFileModifiedCheckTimeout ) ) )
61 equal = other.fileModified == fileModified;
66 int QgsSvgCacheEntry::dataSize()
const 68 int size = svgContent.size();
71 size += picture->size();
75 size += ( image->width() * image->height() * 32 );
84 , mMutex( QMutex::Recursive )
86 mMissingSvg = QStringLiteral(
"<svg width='10' height='10'><text x='5' y='10' font-size='10' text-anchor='middle'>?</text></svg>" ).toLatin1();
89 if ( QFile::exists( downloadingSvgPath ) )
91 QFile file( downloadingSvgPath );
92 if ( file.open( QIODevice::ReadOnly ) )
94 mFetchingSvg = file.readAll();
98 if ( mFetchingSvg.isEmpty() )
100 mFetchingSvg = QStringLiteral(
"<svg width='10' height='10'><text x='5' y='10' font-size='10' text-anchor='middle'>?</text></svg>" ).toLatin1();
106 qDeleteAll( mEntryLookup );
110 QImage
QgsSvgCache::svgAsImage(
const QString &file,
double size,
const QColor &fill,
const QColor &stroke,
double strokeWidth,
111 double widthScaleFactor,
bool &fitsInCache,
double fixedAspectRatio )
113 QMutexLocker locker( &mMutex );
116 QgsSvgCacheEntry *currentEntry = cacheEntry( file, size, fill, stroke, strokeWidth, widthScaleFactor, fixedAspectRatio );
123 if ( !currentEntry->image )
125 QSvgRenderer r( currentEntry->svgContent );
126 double hwRatio = 1.0;
127 if ( r.viewBoxF().width() > 0 )
129 if ( currentEntry->fixedAspectRatio > 0 )
131 hwRatio = currentEntry->fixedAspectRatio;
135 hwRatio = r.viewBoxF().height() / r.viewBoxF().width();
138 long cachedDataSize = 0;
139 cachedDataSize += currentEntry->svgContent.size();
140 cachedDataSize +=
static_cast< int >( currentEntry->size * currentEntry->size * hwRatio * 32 );
141 if ( cachedDataSize > MAXIMUM_SIZE / 2 )
144 currentEntry->image.reset();
147 if ( !currentEntry->picture )
149 cachePicture( currentEntry,
false );
153 result = imageFromCachedPicture( *currentEntry );
157 cacheImage( currentEntry );
158 result = *( currentEntry->image );
164 result = *( currentEntry->image );
171 double widthScaleFactor,
bool forceVectorOutput,
double fixedAspectRatio )
173 QMutexLocker locker( &mMutex );
175 QgsSvgCacheEntry *currentEntry = cacheEntry( path, size, fill, stroke, strokeWidth, widthScaleFactor, fixedAspectRatio );
179 if ( !currentEntry->picture )
181 cachePicture( currentEntry, forceVectorOutput );
190 p.setData( currentEntry->picture->data(), currentEntry->picture->size() );
194 QByteArray
QgsSvgCache::svgContent(
const QString &path,
double size,
const QColor &fill,
const QColor &stroke,
double strokeWidth,
195 double widthScaleFactor,
double fixedAspectRatio )
197 QMutexLocker locker( &mMutex );
199 QgsSvgCacheEntry *currentEntry = cacheEntry( path, size, fill, stroke, strokeWidth, widthScaleFactor, fixedAspectRatio );
201 return currentEntry->svgContent;
204 QSizeF
QgsSvgCache::svgViewboxSize(
const QString &path,
double size,
const QColor &fill,
const QColor &stroke,
double strokeWidth,
double widthScaleFactor,
double fixedAspectRatio )
206 QMutexLocker locker( &mMutex );
208 QgsSvgCacheEntry *currentEntry = cacheEntry( path, size, fill, stroke, strokeWidth, widthScaleFactor, fixedAspectRatio );
210 return currentEntry->viewboxSize;
213 QgsSvgCacheEntry *QgsSvgCache::insertSvg(
const QString &path,
double size,
const QColor &fill,
const QColor &stroke,
double strokeWidth,
214 double widthScaleFactor,
double fixedAspectRatio )
216 QgsSvgCacheEntry *entry =
new QgsSvgCacheEntry( path, size, strokeWidth, widthScaleFactor, fill, stroke, fixedAspectRatio );
217 entry->mFileModifiedCheckTimeout = mFileModifiedCheckTimeout;
219 replaceParamsAndCacheSvg( entry );
221 mEntryLookup.insert( path, entry );
224 if ( !mMostRecentEntry )
226 mLeastRecentEntry = entry;
227 mMostRecentEntry = entry;
228 entry->previousEntry =
nullptr;
229 entry->nextEntry =
nullptr;
233 entry->previousEntry = mMostRecentEntry;
234 entry->nextEntry =
nullptr;
235 mMostRecentEntry->nextEntry = entry;
236 mMostRecentEntry = entry;
243 void QgsSvgCache::containsParams(
const QString &path,
bool &hasFillParam, QColor &defaultFillColor,
bool &hasStrokeParam, QColor &defaultStrokeColor,
244 bool &hasStrokeWidthParam,
double &defaultStrokeWidth )
const 246 bool hasDefaultFillColor =
false;
247 bool hasFillOpacityParam =
false;
248 bool hasDefaultFillOpacity =
false;
249 double defaultFillOpacity = 1.0;
250 bool hasDefaultStrokeColor =
false;
251 bool hasDefaultStrokeWidth =
false;
252 bool hasStrokeOpacityParam =
false;
253 bool hasDefaultStrokeOpacity =
false;
254 double defaultStrokeOpacity = 1.0;
256 containsParams( path, hasFillParam, hasDefaultFillColor, defaultFillColor,
257 hasFillOpacityParam, hasDefaultFillOpacity, defaultFillOpacity,
258 hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
259 hasStrokeWidthParam, hasDefaultStrokeWidth, defaultStrokeWidth,
260 hasStrokeOpacityParam, hasDefaultStrokeOpacity, defaultStrokeOpacity );
264 bool &hasFillParam,
bool &hasDefaultFillParam, QColor &defaultFillColor,
265 bool &hasFillOpacityParam,
bool &hasDefaultFillOpacity,
double &defaultFillOpacity,
266 bool &hasStrokeParam,
bool &hasDefaultStrokeColor, QColor &defaultStrokeColor,
267 bool &hasStrokeWidthParam,
bool &hasDefaultStrokeWidth,
double &defaultStrokeWidth,
268 bool &hasStrokeOpacityParam,
bool &hasDefaultStrokeOpacity,
double &defaultStrokeOpacity )
const 270 hasFillParam =
false;
271 hasFillOpacityParam =
false;
272 hasStrokeParam =
false;
273 hasStrokeWidthParam =
false;
274 hasStrokeOpacityParam =
false;
275 defaultFillColor = QColor( Qt::white );
276 defaultFillOpacity = 1.0;
277 defaultStrokeColor = QColor( Qt::black );
278 defaultStrokeWidth = 0.2;
279 defaultStrokeOpacity = 1.0;
281 hasDefaultFillParam =
false;
282 hasDefaultFillOpacity =
false;
283 hasDefaultStrokeColor =
false;
284 hasDefaultStrokeWidth =
false;
285 hasDefaultStrokeOpacity =
false;
293 QDomElement docElem = svgDoc.documentElement();
294 containsElemParams( docElem, hasFillParam, hasDefaultFillParam, defaultFillColor,
295 hasFillOpacityParam, hasDefaultFillOpacity, defaultFillOpacity,
296 hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
297 hasStrokeWidthParam, hasDefaultStrokeWidth, defaultStrokeWidth,
298 hasStrokeOpacityParam, hasDefaultStrokeOpacity, defaultStrokeOpacity );
301 void QgsSvgCache::replaceParamsAndCacheSvg( QgsSvgCacheEntry *entry )
309 if ( !svgDoc.setContent(
getImageData( entry->path ) ) )
315 QDomElement docElem = svgDoc.documentElement();
318 double sizeScaleFactor = calcSizeScaleFactor( entry, docElem, viewboxSize );
319 entry->viewboxSize = viewboxSize;
320 replaceElemParams( docElem, entry->fill, entry->stroke, entry->strokeWidth * sizeScaleFactor );
322 entry->svgContent = svgDoc.toByteArray( 0 );
326 entry->svgContent.replace(
"\n<tspan",
"<tspan" );
327 entry->svgContent.replace(
"</tspan>\n",
"</tspan>" );
329 mTotalSize += entry->svgContent.size();
332 double QgsSvgCache::calcSizeScaleFactor( QgsSvgCacheEntry *entry,
const QDomElement &docElem, QSizeF &viewboxSize )
const 342 if ( docElem.tagName() == QLatin1String(
"svg" ) && docElem.hasAttribute( QStringLiteral(
"viewBox" ) ) )
344 viewBox = docElem.attribute( QStringLiteral(
"viewBox" ), QString() );
346 else if ( docElem.tagName() == QLatin1String(
"svg" ) && docElem.hasAttribute( QStringLiteral(
"viewbox" ) ) )
348 viewBox = docElem.attribute( QStringLiteral(
"viewbox" ), QString() );
352 QDomElement svgElem = docElem.firstChildElement( QStringLiteral(
"svg" ) );
353 if ( !svgElem.isNull() )
355 if ( svgElem.hasAttribute( QStringLiteral(
"viewBox" ) ) )
356 viewBox = svgElem.attribute( QStringLiteral(
"viewBox" ), QString() );
357 else if ( svgElem.hasAttribute( QStringLiteral(
"viewbox" ) ) )
358 viewBox = svgElem.attribute( QStringLiteral(
"viewbox" ), QString() );
363 if ( viewBox.isEmpty() )
367 QStringList parts = viewBox.split(
' ' );
368 if ( parts.count() != 4 )
371 bool heightOk =
false;
372 double height = parts.at( 3 ).toDouble( &heightOk );
374 bool widthOk =
false;
375 double width = parts.at( 2 ).toDouble( &widthOk );
379 viewboxSize = QSizeF( width, height );
380 return width / entry->size;
389 QFile svgFile( path );
390 if ( svgFile.exists() )
392 if ( svgFile.open( QIODevice::ReadOnly ) )
394 return svgFile.readAll();
403 if ( !path.contains( QLatin1String(
"://" ) ) )
409 if ( !svgUrl.isValid() )
415 if ( svgUrl.scheme().compare( QLatin1String(
"file" ), Qt::CaseInsensitive ) == 0 )
417 svgFile.setFileName( svgUrl.toLocalFile() );
418 if ( svgFile.exists() )
420 if ( svgFile.open( QIODevice::ReadOnly ) )
422 return svgFile.readAll();
430 QMutexLocker locker( &mMutex );
433 if ( mPendingRemoteUrls.contains( path ) )
436 if ( mRemoteContentCache.contains( path ) )
439 return *mRemoteContentCache[ path ];
442 mPendingRemoteUrls.insert( path );
444 QNetworkRequest request( svgUrl );
445 request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache );
446 request.setAttribute( QNetworkRequest::CacheSaveControlAttribute,
true );
451 QMutexLocker locker( &mMutex );
453 QNetworkReply *reply = task->
reply();
457 QMetaObject::invokeMethod( const_cast< QgsSvgCache * >(
this ),
"onRemoteSvgFetched", Qt::QueuedConnection, Q_ARG( QString, path ), Q_ARG(
bool,
false ) );
461 if ( reply->error() != QNetworkReply::NoError )
463 QgsMessageLog::logMessage( tr(
"SVG request failed [error: %1 - url: %2]" ).arg( reply->errorString(), path ), tr(
"SVG" ) );
469 QVariant status = reply->attribute( QNetworkRequest::HttpStatusCodeAttribute );
470 if ( !status.isNull() && status.toInt() >= 400 )
472 QVariant phrase = reply->attribute( QNetworkRequest::HttpReasonPhraseAttribute );
473 QgsMessageLog::logMessage( tr(
"SVG request error [status: %1 - reason phrase: %2] for %3" ).arg( status.toInt() ).arg( phrase.toString(), path ), tr(
"SVG" ) );
474 mRemoteContentCache.insert( path,
new QByteArray( mMissingSvg ) );
480 QString contentType = reply->header( QNetworkRequest::ContentTypeHeader ).toString();
481 if ( !contentType.startsWith( QLatin1String(
"image/svg+xml" ), Qt::CaseInsensitive )
482 && !contentType.startsWith( QLatin1String(
"text/plain" ), Qt::CaseInsensitive ) )
485 mRemoteContentCache.insert( path,
new QByteArray( mMissingSvg ) );
492 mRemoteContentCache.insert( path,
new QByteArray( reply->readAll() ) );
494 QMetaObject::invokeMethod( const_cast< QgsSvgCache * >(
this ),
"onRemoteSvgFetched", Qt::QueuedConnection, Q_ARG( QString, path ), Q_ARG(
bool,
true ) );
501 void QgsSvgCache::cacheImage( QgsSvgCacheEntry *entry )
508 entry->image.reset();
512 QSize imageSize = sizeForImage( *entry, viewBoxSize, scaledSize );
515 std::unique_ptr< QImage > image = qgis::make_unique< QImage >( imageSize, QImage::Format_ARGB32_Premultiplied );
518 QPainter p( image.get() );
519 QSvgRenderer r( entry->svgContent );
520 if (
qgsDoubleNear( viewBoxSize.width(), viewBoxSize.height() ) )
526 QSizeF s( viewBoxSize );
527 s.scale( scaledSize.width(), scaledSize.height(), Qt::KeepAspectRatio );
528 QRectF rect( ( imageSize.width() - s.width() ) / 2, ( imageSize.height() - s.height() ) / 2, s.width(), s.height() );
529 r.render( &p, rect );
532 mTotalSize += ( image->width() * image->height() * 32 );
533 entry->image = std::move( image );
536 void QgsSvgCache::cachePicture( QgsSvgCacheEntry *entry,
bool forceVectorOutput )
538 Q_UNUSED( forceVectorOutput );
544 entry->picture.reset();
546 bool isFixedAR = entry->fixedAspectRatio > 0;
549 std::unique_ptr< QPicture > picture = qgis::make_unique< QPicture >();
551 QSvgRenderer r( entry->svgContent );
552 double hwRatio = 1.0;
553 if ( r.viewBoxF().width() > 0 )
557 hwRatio = entry->fixedAspectRatio;
561 hwRatio = r.viewBoxF().height() / r.viewBoxF().width();
565 double wSize = entry->size;
566 double hSize = wSize * hwRatio;
568 QSizeF s( r.viewBoxF().size() );
569 s.scale( wSize, hSize, isFixedAR ? Qt::IgnoreAspectRatio : Qt::KeepAspectRatio );
570 rect = QRectF( -s.width() / 2.0, -s.height() / 2.0, s.width(), s.height() );
572 QPainter p( picture.get() );
573 r.render( &p, rect );
574 entry->picture = std::move( picture );
575 mTotalSize += entry->picture->size();
578 QgsSvgCacheEntry *QgsSvgCache::cacheEntry(
const QString &path,
double size,
const QColor &fill,
const QColor &stroke,
double strokeWidth,
579 double widthScaleFactor,
double fixedAspectRatio )
582 QgsSvgCacheEntry *currentEntry =
nullptr;
583 QList<QgsSvgCacheEntry *> entries = mEntryLookup.values( path );
585 QList<QgsSvgCacheEntry *>::iterator entryIt = entries.begin();
586 for ( ; entryIt != entries.end(); ++entryIt )
588 QgsSvgCacheEntry *cacheEntry = *entryIt;
589 if (
qgsDoubleNear( cacheEntry->size, size ) && cacheEntry->fill == fill && cacheEntry->stroke == stroke &&
591 qgsDoubleNear( cacheEntry->fixedAspectRatio, fixedAspectRatio ) )
593 if ( mFileModifiedCheckTimeout <= 0 || cacheEntry->fileModifiedLastCheckTimer.hasExpired( mFileModifiedCheckTimeout ) )
595 if ( !modified.isValid() )
596 modified = QFileInfo( path ).lastModified();
598 if ( cacheEntry->fileModified != modified )
601 currentEntry = cacheEntry;
610 currentEntry = insertSvg( path, size, fill, stroke, strokeWidth, widthScaleFactor, fixedAspectRatio );
614 takeEntryFromList( currentEntry );
615 if ( !mMostRecentEntry )
617 mMostRecentEntry = currentEntry;
618 mLeastRecentEntry = currentEntry;
622 mMostRecentEntry->nextEntry = currentEntry;
623 currentEntry->previousEntry = mMostRecentEntry;
624 currentEntry->nextEntry =
nullptr;
625 mMostRecentEntry = currentEntry;
635 void QgsSvgCache::replaceElemParams( QDomElement &elem,
const QColor &fill,
const QColor &stroke,
double strokeWidth )
643 QDomNamedNodeMap attributes = elem.attributes();
644 int nAttributes = attributes.count();
645 for (
int i = 0; i < nAttributes; ++i )
647 QDomAttr attribute = attributes.item( i ).toAttr();
649 if ( attribute.name().compare( QLatin1String(
"style" ), Qt::CaseInsensitive ) == 0 )
652 QString newAttributeString;
654 QStringList entryList = attribute.value().split(
';' );
655 QStringList::const_iterator entryIt = entryList.constBegin();
656 for ( ; entryIt != entryList.constEnd(); ++entryIt )
658 QStringList keyValueSplit = entryIt->split(
':' );
659 if ( keyValueSplit.size() < 2 )
663 QString key = keyValueSplit.at( 0 );
664 QString value = keyValueSplit.at( 1 );
665 if ( value.startsWith( QLatin1String(
"param(fill)" ) ) )
669 else if ( value.startsWith( QLatin1String(
"param(fill-opacity)" ) ) )
671 value = fill.alphaF();
673 else if ( value.startsWith( QLatin1String(
"param(outline)" ) ) )
675 value = stroke.name();
677 else if ( value.startsWith( QLatin1String(
"param(outline-opacity)" ) ) )
679 value = stroke.alphaF();
681 else if ( value.startsWith( QLatin1String(
"param(outline-width)" ) ) )
683 value = QString::number( strokeWidth );
686 if ( entryIt != entryList.constBegin() )
688 newAttributeString.append(
';' );
690 newAttributeString.append( key +
':' + value );
692 elem.setAttribute( attribute.name(), newAttributeString );
696 QString value = attribute.value();
697 if ( value.startsWith( QLatin1String(
"param(fill)" ) ) )
699 elem.setAttribute( attribute.name(), fill.name() );
701 else if ( value.startsWith( QLatin1String(
"param(fill-opacity)" ) ) )
703 elem.setAttribute( attribute.name(), fill.alphaF() );
705 else if ( value.startsWith( QLatin1String(
"param(outline)" ) ) )
707 elem.setAttribute( attribute.name(), stroke.name() );
709 else if ( value.startsWith( QLatin1String(
"param(outline-opacity)" ) ) )
711 elem.setAttribute( attribute.name(), stroke.alphaF() );
713 else if ( value.startsWith( QLatin1String(
"param(outline-width)" ) ) )
715 elem.setAttribute( attribute.name(), QString::number( strokeWidth ) );
720 QDomNodeList childList = elem.childNodes();
721 int nChildren = childList.count();
722 for (
int i = 0; i < nChildren; ++i )
724 QDomElement childElem = childList.at( i ).toElement();
725 replaceElemParams( childElem, fill, stroke, strokeWidth );
729 void QgsSvgCache::containsElemParams(
const QDomElement &elem,
bool &hasFillParam,
bool &hasDefaultFill, QColor &defaultFill,
730 bool &hasFillOpacityParam,
bool &hasDefaultFillOpacity,
double &defaultFillOpacity,
731 bool &hasStrokeParam,
bool &hasDefaultStroke, QColor &defaultStroke,
732 bool &hasStrokeWidthParam,
bool &hasDefaultStrokeWidth,
double &defaultStrokeWidth,
733 bool &hasStrokeOpacityParam,
bool &hasDefaultStrokeOpacity,
double &defaultStrokeOpacity )
const 741 if ( hasFillParam && hasStrokeParam && hasStrokeWidthParam && hasFillOpacityParam && hasStrokeOpacityParam )
747 QDomNamedNodeMap attributes = elem.attributes();
748 int nAttributes = attributes.count();
750 QStringList valueSplit;
751 for (
int i = 0; i < nAttributes; ++i )
753 QDomAttr attribute = attributes.item( i ).toAttr();
754 if ( attribute.name().compare( QLatin1String(
"style" ), Qt::CaseInsensitive ) == 0 )
757 QStringList entryList = attribute.value().split(
';' );
758 QStringList::const_iterator entryIt = entryList.constBegin();
759 for ( ; entryIt != entryList.constEnd(); ++entryIt )
761 QStringList keyValueSplit = entryIt->split(
':' );
762 if ( keyValueSplit.size() < 2 )
766 QString value = keyValueSplit.at( 1 );
767 valueSplit = value.split(
' ' );
768 if ( !hasFillParam && value.startsWith( QLatin1String(
"param(fill)" ) ) )
771 if ( valueSplit.size() > 1 )
773 defaultFill = QColor( valueSplit.at( 1 ) );
774 hasDefaultFill =
true;
777 else if ( !hasFillOpacityParam && value.startsWith( QLatin1String(
"param(fill-opacity)" ) ) )
779 hasFillOpacityParam =
true;
780 if ( valueSplit.size() > 1 )
783 double opacity = valueSplit.at( 1 ).toDouble( &ok );
786 defaultFillOpacity = opacity;
787 hasDefaultFillOpacity =
true;
791 else if ( !hasStrokeParam && value.startsWith( QLatin1String(
"param(outline)" ) ) )
793 hasStrokeParam =
true;
794 if ( valueSplit.size() > 1 )
796 defaultStroke = QColor( valueSplit.at( 1 ) );
797 hasDefaultStroke =
true;
800 else if ( !hasStrokeWidthParam && value.startsWith( QLatin1String(
"param(outline-width)" ) ) )
802 hasStrokeWidthParam =
true;
803 if ( valueSplit.size() > 1 )
805 defaultStrokeWidth = valueSplit.at( 1 ).toDouble();
806 hasDefaultStrokeWidth =
true;
809 else if ( !hasStrokeOpacityParam && value.startsWith( QLatin1String(
"param(outline-opacity)" ) ) )
811 hasStrokeOpacityParam =
true;
812 if ( valueSplit.size() > 1 )
815 double opacity = valueSplit.at( 1 ).toDouble( &ok );
818 defaultStrokeOpacity = opacity;
819 hasDefaultStrokeOpacity =
true;
827 QString value = attribute.value();
828 valueSplit = value.split(
' ' );
829 if ( !hasFillParam && value.startsWith( QLatin1String(
"param(fill)" ) ) )
832 if ( valueSplit.size() > 1 )
834 defaultFill = QColor( valueSplit.at( 1 ) );
835 hasDefaultFill =
true;
838 else if ( !hasFillOpacityParam && value.startsWith( QLatin1String(
"param(fill-opacity)" ) ) )
840 hasFillOpacityParam =
true;
841 if ( valueSplit.size() > 1 )
844 double opacity = valueSplit.at( 1 ).toDouble( &ok );
847 defaultFillOpacity = opacity;
848 hasDefaultFillOpacity =
true;
852 else if ( !hasStrokeParam && value.startsWith( QLatin1String(
"param(outline)" ) ) )
854 hasStrokeParam =
true;
855 if ( valueSplit.size() > 1 )
857 defaultStroke = QColor( valueSplit.at( 1 ) );
858 hasDefaultStroke =
true;
861 else if ( !hasStrokeWidthParam && value.startsWith( QLatin1String(
"param(outline-width)" ) ) )
863 hasStrokeWidthParam =
true;
864 if ( valueSplit.size() > 1 )
866 defaultStrokeWidth = valueSplit.at( 1 ).toDouble();
867 hasDefaultStrokeWidth =
true;
870 else if ( !hasStrokeOpacityParam && value.startsWith( QLatin1String(
"param(outline-opacity)" ) ) )
872 hasStrokeOpacityParam =
true;
873 if ( valueSplit.size() > 1 )
876 double opacity = valueSplit.at( 1 ).toDouble( &ok );
879 defaultStrokeOpacity = opacity;
880 hasDefaultStrokeOpacity =
true;
888 QDomNodeList childList = elem.childNodes();
889 int nChildren = childList.count();
890 for (
int i = 0; i < nChildren; ++i )
892 QDomElement childElem = childList.at( i ).toElement();
893 containsElemParams( childElem, hasFillParam, hasDefaultFill, defaultFill,
894 hasFillOpacityParam, hasDefaultFillOpacity, defaultFillOpacity,
895 hasStrokeParam, hasDefaultStroke, defaultStroke,
896 hasStrokeWidthParam, hasDefaultStrokeWidth, defaultStrokeWidth,
897 hasStrokeOpacityParam, hasDefaultStrokeOpacity, defaultStrokeOpacity );
901 void QgsSvgCache::removeCacheEntry(
const QString &s, QgsSvgCacheEntry *entry )
904 mEntryLookup.remove( s, entry );
907 void QgsSvgCache::printEntryList()
909 QgsDebugMsg(
"****************svg cache entry list*************************" );
910 QgsDebugMsg(
"Cache size: " + QString::number( mTotalSize ) );
911 QgsSvgCacheEntry *entry = mLeastRecentEntry;
916 QgsDebugMsg(
"Size:" + QString::number( entry->size ) );
917 QgsDebugMsg(
"Width scale factor" + QString::number( entry->widthScaleFactor ) );
918 entry = entry->nextEntry;
922 QSize QgsSvgCache::sizeForImage(
const QgsSvgCacheEntry &entry, QSizeF &viewBoxSize, QSizeF &scaledSize )
const 924 bool isFixedAR = entry.fixedAspectRatio > 0;
926 QSvgRenderer r( entry.svgContent );
927 double hwRatio = 1.0;
928 viewBoxSize = r.viewBoxF().size();
929 if ( viewBoxSize.width() > 0 )
933 hwRatio = entry.fixedAspectRatio;
937 hwRatio = viewBoxSize.height() / viewBoxSize.width();
942 scaledSize.setWidth( entry.size );
943 int wImgSize =
static_cast< int >( scaledSize.width() );
948 scaledSize.setHeight( scaledSize.width() * hwRatio );
949 int hImgSize =
static_cast< int >( scaledSize.height() );
954 return QSize( wImgSize, hImgSize );
957 QImage QgsSvgCache::imageFromCachedPicture(
const QgsSvgCacheEntry &entry )
const 961 QImage image( sizeForImage( entry, viewBoxSize, scaledSize ), QImage::Format_ARGB32_Premultiplied );
964 QPainter p( &image );
965 p.drawPicture( QPoint( 0, 0 ), *entry.picture );
969 void QgsSvgCache::trimToMaximumSize()
972 if ( mLeastRecentEntry == mMostRecentEntry )
976 QgsSvgCacheEntry *entry = mLeastRecentEntry;
977 while ( entry && ( mTotalSize > MAXIMUM_SIZE ) )
979 QgsSvgCacheEntry *bkEntry = entry;
980 entry = entry->nextEntry;
982 takeEntryFromList( bkEntry );
983 mEntryLookup.remove( bkEntry->path, bkEntry );
984 mTotalSize -= bkEntry->dataSize();
989 void QgsSvgCache::takeEntryFromList( QgsSvgCacheEntry *entry )
996 if ( entry->previousEntry )
998 entry->previousEntry->nextEntry = entry->nextEntry;
1002 mLeastRecentEntry = entry->nextEntry;
1004 if ( entry->nextEntry )
1006 entry->nextEntry->previousEntry = entry->previousEntry;
1010 mMostRecentEntry = entry->previousEntry;
1014 void QgsSvgCache::downloadProgress( qint64 bytesReceived, qint64 bytesTotal )
1016 QString msg = tr(
"%1 of %2 bytes of svg image downloaded." ).arg( bytesReceived ).arg( bytesTotal < 0 ? QStringLiteral(
"unknown number of" ) : QString::number( bytesTotal ) );
1021 void QgsSvgCache::onRemoteSvgFetched(
const QString &url,
bool success )
1023 QMutexLocker locker( &mMutex );
1024 mPendingRemoteUrls.remove( url );
1026 QgsSvgCacheEntry *nextEntry = mLeastRecentEntry;
1027 while ( QgsSvgCacheEntry *entry = nextEntry )
1029 nextEntry = entry->nextEntry;
1030 if ( entry->path == url )
1032 takeEntryFromList( entry );
1033 mEntryLookup.remove( entry->path, entry );
1034 mTotalSize -= entry->dataSize();
QPicture svgAsPicture(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, bool forceVectorOutput=false, double fixedAspectRatio=0)
Gets SVG as QPicture&.
QByteArray getImageData(const QString &path) const
Gets image data.
static QString defaultThemePath()
Returns the path to the default theme directory.
void fetched()
Emitted when the network content has been fetched, regardless of whether the fetch was successful or ...
QImage svgAsImage(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, bool &fitsInCache, double fixedAspectRatio=0)
Gets SVG as QImage.
bool operator==(const QgsFeatureIterator &fi1, const QgsFeatureIterator &fi2)
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
QNetworkReply * reply()
Returns the network reply.
Handles HTTP network content fetching in a background task.
static QgsTaskManager * taskManager()
Returns the application's task manager, used for managing application wide background task handling...
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
long addTask(QgsTask *task, int priority=0)
Adds a task to the manager.
QgsSvgCache(QObject *parent=nullptr)
Constructor for QgsSvgCache.
void containsParams(const QString &path, bool &hasFillParam, QColor &defaultFillColor, bool &hasStrokeParam, QColor &defaultStrokeColor, bool &hasStrokeWidthParam, double &defaultStrokeWidth) const
Tests if an svg file contains parameters for fill, stroke color, stroke width.
QByteArray svgContent(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, double fixedAspectRatio=0)
Gets SVG content.
void remoteSvgFetched(const QString &url)
Emitted when the cache has finished retrieving an SVG file from a remote url.
void statusChanged(const QString &statusQString)
Emit a signal to be caught by qgisapp and display a msg on status bar.
QSizeF svgViewboxSize(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, double fixedAspectRatio=0)
Calculates the viewbox size of a (possibly cached) SVG file.