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 );
185 QPicture p = *( currentEntry->picture );
190 QByteArray
QgsSvgCache::svgContent(
const QString &path,
double size,
const QColor &fill,
const QColor &stroke,
double strokeWidth,
191 double widthScaleFactor,
double fixedAspectRatio )
193 QMutexLocker locker( &mMutex );
195 QgsSvgCacheEntry *currentEntry = cacheEntry( path, size, fill, stroke, strokeWidth, widthScaleFactor, fixedAspectRatio );
197 return currentEntry->svgContent;
200 QSizeF
QgsSvgCache::svgViewboxSize(
const QString &path,
double size,
const QColor &fill,
const QColor &stroke,
double strokeWidth,
double widthScaleFactor,
double fixedAspectRatio )
202 QMutexLocker locker( &mMutex );
204 QgsSvgCacheEntry *currentEntry = cacheEntry( path, size, fill, stroke, strokeWidth, widthScaleFactor, fixedAspectRatio );
206 return currentEntry->viewboxSize;
209 QgsSvgCacheEntry *QgsSvgCache::insertSvg(
const QString &path,
double size,
const QColor &fill,
const QColor &stroke,
double strokeWidth,
210 double widthScaleFactor,
double fixedAspectRatio )
212 QgsSvgCacheEntry *entry =
new QgsSvgCacheEntry( path, size, strokeWidth, widthScaleFactor, fill, stroke, fixedAspectRatio );
213 entry->mFileModifiedCheckTimeout = mFileModifiedCheckTimeout;
215 replaceParamsAndCacheSvg( entry );
217 mEntryLookup.insert( path, entry );
220 if ( !mMostRecentEntry )
222 mLeastRecentEntry = entry;
223 mMostRecentEntry = entry;
224 entry->previousEntry =
nullptr;
225 entry->nextEntry =
nullptr;
229 entry->previousEntry = mMostRecentEntry;
230 entry->nextEntry =
nullptr;
231 mMostRecentEntry->nextEntry = entry;
232 mMostRecentEntry = entry;
239 void QgsSvgCache::containsParams(
const QString &path,
bool &hasFillParam, QColor &defaultFillColor,
bool &hasStrokeParam, QColor &defaultStrokeColor,
240 bool &hasStrokeWidthParam,
double &defaultStrokeWidth )
const 242 bool hasDefaultFillColor =
false;
243 bool hasFillOpacityParam =
false;
244 bool hasDefaultFillOpacity =
false;
245 double defaultFillOpacity = 1.0;
246 bool hasDefaultStrokeColor =
false;
247 bool hasDefaultStrokeWidth =
false;
248 bool hasStrokeOpacityParam =
false;
249 bool hasDefaultStrokeOpacity =
false;
250 double defaultStrokeOpacity = 1.0;
252 containsParams( path, hasFillParam, hasDefaultFillColor, defaultFillColor,
253 hasFillOpacityParam, hasDefaultFillOpacity, defaultFillOpacity,
254 hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
255 hasStrokeWidthParam, hasDefaultStrokeWidth, defaultStrokeWidth,
256 hasStrokeOpacityParam, hasDefaultStrokeOpacity, defaultStrokeOpacity );
260 bool &hasFillParam,
bool &hasDefaultFillParam, QColor &defaultFillColor,
261 bool &hasFillOpacityParam,
bool &hasDefaultFillOpacity,
double &defaultFillOpacity,
262 bool &hasStrokeParam,
bool &hasDefaultStrokeColor, QColor &defaultStrokeColor,
263 bool &hasStrokeWidthParam,
bool &hasDefaultStrokeWidth,
double &defaultStrokeWidth,
264 bool &hasStrokeOpacityParam,
bool &hasDefaultStrokeOpacity,
double &defaultStrokeOpacity )
const 266 hasFillParam =
false;
267 hasFillOpacityParam =
false;
268 hasStrokeParam =
false;
269 hasStrokeWidthParam =
false;
270 hasStrokeOpacityParam =
false;
271 defaultFillColor = QColor( Qt::white );
272 defaultFillOpacity = 1.0;
273 defaultStrokeColor = QColor( Qt::black );
274 defaultStrokeWidth = 0.2;
275 defaultStrokeOpacity = 1.0;
277 hasDefaultFillParam =
false;
278 hasDefaultFillOpacity =
false;
279 hasDefaultStrokeColor =
false;
280 hasDefaultStrokeWidth =
false;
281 hasDefaultStrokeOpacity =
false;
289 QDomElement docElem = svgDoc.documentElement();
290 containsElemParams( docElem, hasFillParam, hasDefaultFillParam, defaultFillColor,
291 hasFillOpacityParam, hasDefaultFillOpacity, defaultFillOpacity,
292 hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
293 hasStrokeWidthParam, hasDefaultStrokeWidth, defaultStrokeWidth,
294 hasStrokeOpacityParam, hasDefaultStrokeOpacity, defaultStrokeOpacity );
297 void QgsSvgCache::replaceParamsAndCacheSvg( QgsSvgCacheEntry *entry )
305 if ( !svgDoc.setContent(
getImageData( entry->path ) ) )
311 QDomElement docElem = svgDoc.documentElement();
314 double sizeScaleFactor = calcSizeScaleFactor( entry, docElem, viewboxSize );
315 entry->viewboxSize = viewboxSize;
316 replaceElemParams( docElem, entry->fill, entry->stroke, entry->strokeWidth * sizeScaleFactor );
318 entry->svgContent = svgDoc.toByteArray( 0 );
322 entry->svgContent.replace(
"\n<tspan",
"<tspan" );
323 entry->svgContent.replace(
"</tspan>\n",
"</tspan>" );
325 mTotalSize += entry->svgContent.size();
328 double QgsSvgCache::calcSizeScaleFactor( QgsSvgCacheEntry *entry,
const QDomElement &docElem, QSizeF &viewboxSize )
const 338 if ( docElem.tagName() == QLatin1String(
"svg" ) && docElem.hasAttribute( QStringLiteral(
"viewBox" ) ) )
340 viewBox = docElem.attribute( QStringLiteral(
"viewBox" ), QString() );
342 else if ( docElem.tagName() == QLatin1String(
"svg" ) && docElem.hasAttribute( QStringLiteral(
"viewbox" ) ) )
344 viewBox = docElem.attribute( QStringLiteral(
"viewbox" ), QString() );
348 QDomElement svgElem = docElem.firstChildElement( QStringLiteral(
"svg" ) );
349 if ( !svgElem.isNull() )
351 if ( svgElem.hasAttribute( QStringLiteral(
"viewBox" ) ) )
352 viewBox = svgElem.attribute( QStringLiteral(
"viewBox" ), QString() );
353 else if ( svgElem.hasAttribute( QStringLiteral(
"viewbox" ) ) )
354 viewBox = svgElem.attribute( QStringLiteral(
"viewbox" ), QString() );
359 if ( viewBox.isEmpty() )
363 QStringList parts = viewBox.split(
' ' );
364 if ( parts.count() != 4 )
367 bool heightOk =
false;
368 double height = parts.at( 3 ).toDouble( &heightOk );
370 bool widthOk =
false;
371 double width = parts.at( 2 ).toDouble( &widthOk );
375 viewboxSize = QSizeF( width, height );
376 return width / entry->size;
385 QFile svgFile( path );
386 if ( svgFile.exists() )
388 if ( svgFile.open( QIODevice::ReadOnly ) )
390 return svgFile.readAll();
399 if ( !path.contains( QLatin1String(
"://" ) ) )
405 if ( !svgUrl.isValid() )
411 if ( svgUrl.scheme().compare( QLatin1String(
"file" ), Qt::CaseInsensitive ) == 0 )
413 svgFile.setFileName( svgUrl.toLocalFile() );
414 if ( svgFile.exists() )
416 if ( svgFile.open( QIODevice::ReadOnly ) )
418 return svgFile.readAll();
426 QMutexLocker locker( &mMutex );
429 if ( mPendingRemoteUrls.contains( path ) )
432 if ( mRemoteContentCache.contains( path ) )
435 return *mRemoteContentCache[ path ];
438 mPendingRemoteUrls.insert( path );
440 QNetworkRequest request( svgUrl );
441 request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache );
442 request.setAttribute( QNetworkRequest::CacheSaveControlAttribute,
true );
447 QMutexLocker locker( &mMutex );
449 QNetworkReply *reply = task->
reply();
453 QMetaObject::invokeMethod( const_cast< QgsSvgCache * >(
this ),
"onRemoteSvgFetched", Qt::QueuedConnection, Q_ARG( QString, path ), Q_ARG(
bool,
false ) );
457 if ( reply->error() != QNetworkReply::NoError )
459 QgsMessageLog::logMessage( tr(
"SVG request failed [error: %1 - url: %2]" ).arg( reply->errorString(), path ), tr(
"SVG" ) );
465 QVariant status = reply->attribute( QNetworkRequest::HttpStatusCodeAttribute );
466 if ( !status.isNull() && status.toInt() >= 400 )
468 QVariant phrase = reply->attribute( QNetworkRequest::HttpReasonPhraseAttribute );
469 QgsMessageLog::logMessage( tr(
"SVG request error [status: %1 - reason phrase: %2] for %3" ).arg( status.toInt() ).arg( phrase.toString(), path ), tr(
"SVG" ) );
470 mRemoteContentCache.insert( path,
new QByteArray( mMissingSvg ) );
476 QString contentType = reply->header( QNetworkRequest::ContentTypeHeader ).toString();
477 if ( !contentType.startsWith( QLatin1String(
"image/svg+xml" ), Qt::CaseInsensitive )
478 && !contentType.startsWith( QLatin1String(
"text/plain" ), Qt::CaseInsensitive ) )
481 mRemoteContentCache.insert( path,
new QByteArray( mMissingSvg ) );
488 mRemoteContentCache.insert( path,
new QByteArray( reply->readAll() ) );
490 QMetaObject::invokeMethod( const_cast< QgsSvgCache * >(
this ),
"onRemoteSvgFetched", Qt::QueuedConnection, Q_ARG( QString, path ), Q_ARG(
bool,
true ) );
497 void QgsSvgCache::cacheImage( QgsSvgCacheEntry *entry )
504 entry->image.reset();
508 QSize imageSize = sizeForImage( *entry, viewBoxSize, scaledSize );
511 std::unique_ptr< QImage > image = qgis::make_unique< QImage >( imageSize, QImage::Format_ARGB32_Premultiplied );
514 QPainter p( image.get() );
515 QSvgRenderer r( entry->svgContent );
516 if (
qgsDoubleNear( viewBoxSize.width(), viewBoxSize.height() ) )
522 QSizeF s( viewBoxSize );
523 s.scale( scaledSize.width(), scaledSize.height(), Qt::KeepAspectRatio );
524 QRectF rect( ( imageSize.width() - s.width() ) / 2, ( imageSize.height() - s.height() ) / 2, s.width(), s.height() );
525 r.render( &p, rect );
528 mTotalSize += ( image->width() * image->height() * 32 );
529 entry->image = std::move( image );
532 void QgsSvgCache::cachePicture( QgsSvgCacheEntry *entry,
bool forceVectorOutput )
534 Q_UNUSED( forceVectorOutput );
540 entry->picture.reset();
542 bool isFixedAR = entry->fixedAspectRatio > 0;
545 std::unique_ptr< QPicture > picture = qgis::make_unique< QPicture >();
547 QSvgRenderer r( entry->svgContent );
548 double hwRatio = 1.0;
549 if ( r.viewBoxF().width() > 0 )
553 hwRatio = entry->fixedAspectRatio;
557 hwRatio = r.viewBoxF().height() / r.viewBoxF().width();
561 double wSize = entry->size;
562 double hSize = wSize * hwRatio;
564 QSizeF s( r.viewBoxF().size() );
565 s.scale( wSize, hSize, isFixedAR ? Qt::IgnoreAspectRatio : Qt::KeepAspectRatio );
566 rect = QRectF( -s.width() / 2.0, -s.height() / 2.0, s.width(), s.height() );
568 QPainter p( picture.get() );
569 r.render( &p, rect );
570 entry->picture = std::move( picture );
571 mTotalSize += entry->picture->size();
574 QgsSvgCacheEntry *QgsSvgCache::cacheEntry(
const QString &path,
double size,
const QColor &fill,
const QColor &stroke,
double strokeWidth,
575 double widthScaleFactor,
double fixedAspectRatio )
578 QgsSvgCacheEntry *currentEntry =
nullptr;
579 QList<QgsSvgCacheEntry *> entries = mEntryLookup.values( path );
581 QList<QgsSvgCacheEntry *>::iterator entryIt = entries.begin();
582 for ( ; entryIt != entries.end(); ++entryIt )
584 QgsSvgCacheEntry *cacheEntry = *entryIt;
585 if (
qgsDoubleNear( cacheEntry->size, size ) && cacheEntry->fill == fill && cacheEntry->stroke == stroke &&
587 qgsDoubleNear( cacheEntry->fixedAspectRatio, fixedAspectRatio ) )
589 if ( mFileModifiedCheckTimeout <= 0 || cacheEntry->fileModifiedLastCheckTimer.hasExpired( mFileModifiedCheckTimeout ) )
591 if ( !modified.isValid() )
592 modified = QFileInfo( path ).lastModified();
594 if ( cacheEntry->fileModified != modified )
597 currentEntry = cacheEntry;
606 currentEntry = insertSvg( path, size, fill, stroke, strokeWidth, widthScaleFactor, fixedAspectRatio );
610 takeEntryFromList( currentEntry );
611 if ( !mMostRecentEntry )
613 mMostRecentEntry = currentEntry;
614 mLeastRecentEntry = currentEntry;
618 mMostRecentEntry->nextEntry = currentEntry;
619 currentEntry->previousEntry = mMostRecentEntry;
620 currentEntry->nextEntry =
nullptr;
621 mMostRecentEntry = currentEntry;
631 void QgsSvgCache::replaceElemParams( QDomElement &elem,
const QColor &fill,
const QColor &stroke,
double strokeWidth )
639 QDomNamedNodeMap attributes = elem.attributes();
640 int nAttributes = attributes.count();
641 for (
int i = 0; i < nAttributes; ++i )
643 QDomAttr attribute = attributes.item( i ).toAttr();
645 if ( attribute.name().compare( QLatin1String(
"style" ), Qt::CaseInsensitive ) == 0 )
648 QString newAttributeString;
650 QStringList entryList = attribute.value().split(
';' );
651 QStringList::const_iterator entryIt = entryList.constBegin();
652 for ( ; entryIt != entryList.constEnd(); ++entryIt )
654 QStringList keyValueSplit = entryIt->split(
':' );
655 if ( keyValueSplit.size() < 2 )
659 QString key = keyValueSplit.at( 0 );
660 QString value = keyValueSplit.at( 1 );
661 if ( value.startsWith( QLatin1String(
"param(fill)" ) ) )
665 else if ( value.startsWith( QLatin1String(
"param(fill-opacity)" ) ) )
667 value = fill.alphaF();
669 else if ( value.startsWith( QLatin1String(
"param(outline)" ) ) )
671 value = stroke.name();
673 else if ( value.startsWith( QLatin1String(
"param(outline-opacity)" ) ) )
675 value = stroke.alphaF();
677 else if ( value.startsWith( QLatin1String(
"param(outline-width)" ) ) )
679 value = QString::number( strokeWidth );
682 if ( entryIt != entryList.constBegin() )
684 newAttributeString.append(
';' );
686 newAttributeString.append( key +
':' + value );
688 elem.setAttribute( attribute.name(), newAttributeString );
692 QString value = attribute.value();
693 if ( value.startsWith( QLatin1String(
"param(fill)" ) ) )
695 elem.setAttribute( attribute.name(), fill.name() );
697 else if ( value.startsWith( QLatin1String(
"param(fill-opacity)" ) ) )
699 elem.setAttribute( attribute.name(), fill.alphaF() );
701 else if ( value.startsWith( QLatin1String(
"param(outline)" ) ) )
703 elem.setAttribute( attribute.name(), stroke.name() );
705 else if ( value.startsWith( QLatin1String(
"param(outline-opacity)" ) ) )
707 elem.setAttribute( attribute.name(), stroke.alphaF() );
709 else if ( value.startsWith( QLatin1String(
"param(outline-width)" ) ) )
711 elem.setAttribute( attribute.name(), QString::number( strokeWidth ) );
716 QDomNodeList childList = elem.childNodes();
717 int nChildren = childList.count();
718 for (
int i = 0; i < nChildren; ++i )
720 QDomElement childElem = childList.at( i ).toElement();
721 replaceElemParams( childElem, fill, stroke, strokeWidth );
725 void QgsSvgCache::containsElemParams(
const QDomElement &elem,
bool &hasFillParam,
bool &hasDefaultFill, QColor &defaultFill,
726 bool &hasFillOpacityParam,
bool &hasDefaultFillOpacity,
double &defaultFillOpacity,
727 bool &hasStrokeParam,
bool &hasDefaultStroke, QColor &defaultStroke,
728 bool &hasStrokeWidthParam,
bool &hasDefaultStrokeWidth,
double &defaultStrokeWidth,
729 bool &hasStrokeOpacityParam,
bool &hasDefaultStrokeOpacity,
double &defaultStrokeOpacity )
const 737 if ( hasFillParam && hasStrokeParam && hasStrokeWidthParam && hasFillOpacityParam && hasStrokeOpacityParam )
743 QDomNamedNodeMap attributes = elem.attributes();
744 int nAttributes = attributes.count();
746 QStringList valueSplit;
747 for (
int i = 0; i < nAttributes; ++i )
749 QDomAttr attribute = attributes.item( i ).toAttr();
750 if ( attribute.name().compare( QLatin1String(
"style" ), Qt::CaseInsensitive ) == 0 )
753 QStringList entryList = attribute.value().split(
';' );
754 QStringList::const_iterator entryIt = entryList.constBegin();
755 for ( ; entryIt != entryList.constEnd(); ++entryIt )
757 QStringList keyValueSplit = entryIt->split(
':' );
758 if ( keyValueSplit.size() < 2 )
762 QString value = keyValueSplit.at( 1 );
763 valueSplit = value.split(
' ' );
764 if ( !hasFillParam && value.startsWith( QLatin1String(
"param(fill)" ) ) )
767 if ( valueSplit.size() > 1 )
769 defaultFill = QColor( valueSplit.at( 1 ) );
770 hasDefaultFill =
true;
773 else if ( !hasFillOpacityParam && value.startsWith( QLatin1String(
"param(fill-opacity)" ) ) )
775 hasFillOpacityParam =
true;
776 if ( valueSplit.size() > 1 )
779 double opacity = valueSplit.at( 1 ).toDouble( &ok );
782 defaultFillOpacity = opacity;
783 hasDefaultFillOpacity =
true;
787 else if ( !hasStrokeParam && value.startsWith( QLatin1String(
"param(outline)" ) ) )
789 hasStrokeParam =
true;
790 if ( valueSplit.size() > 1 )
792 defaultStroke = QColor( valueSplit.at( 1 ) );
793 hasDefaultStroke =
true;
796 else if ( !hasStrokeWidthParam && value.startsWith( QLatin1String(
"param(outline-width)" ) ) )
798 hasStrokeWidthParam =
true;
799 if ( valueSplit.size() > 1 )
801 defaultStrokeWidth = valueSplit.at( 1 ).toDouble();
802 hasDefaultStrokeWidth =
true;
805 else if ( !hasStrokeOpacityParam && value.startsWith( QLatin1String(
"param(outline-opacity)" ) ) )
807 hasStrokeOpacityParam =
true;
808 if ( valueSplit.size() > 1 )
811 double opacity = valueSplit.at( 1 ).toDouble( &ok );
814 defaultStrokeOpacity = opacity;
815 hasDefaultStrokeOpacity =
true;
823 QString value = attribute.value();
824 valueSplit = value.split(
' ' );
825 if ( !hasFillParam && value.startsWith( QLatin1String(
"param(fill)" ) ) )
828 if ( valueSplit.size() > 1 )
830 defaultFill = QColor( valueSplit.at( 1 ) );
831 hasDefaultFill =
true;
834 else if ( !hasFillOpacityParam && value.startsWith( QLatin1String(
"param(fill-opacity)" ) ) )
836 hasFillOpacityParam =
true;
837 if ( valueSplit.size() > 1 )
840 double opacity = valueSplit.at( 1 ).toDouble( &ok );
843 defaultFillOpacity = opacity;
844 hasDefaultFillOpacity =
true;
848 else if ( !hasStrokeParam && value.startsWith( QLatin1String(
"param(outline)" ) ) )
850 hasStrokeParam =
true;
851 if ( valueSplit.size() > 1 )
853 defaultStroke = QColor( valueSplit.at( 1 ) );
854 hasDefaultStroke =
true;
857 else if ( !hasStrokeWidthParam && value.startsWith( QLatin1String(
"param(outline-width)" ) ) )
859 hasStrokeWidthParam =
true;
860 if ( valueSplit.size() > 1 )
862 defaultStrokeWidth = valueSplit.at( 1 ).toDouble();
863 hasDefaultStrokeWidth =
true;
866 else if ( !hasStrokeOpacityParam && value.startsWith( QLatin1String(
"param(outline-opacity)" ) ) )
868 hasStrokeOpacityParam =
true;
869 if ( valueSplit.size() > 1 )
872 double opacity = valueSplit.at( 1 ).toDouble( &ok );
875 defaultStrokeOpacity = opacity;
876 hasDefaultStrokeOpacity =
true;
884 QDomNodeList childList = elem.childNodes();
885 int nChildren = childList.count();
886 for (
int i = 0; i < nChildren; ++i )
888 QDomElement childElem = childList.at( i ).toElement();
889 containsElemParams( childElem, hasFillParam, hasDefaultFill, defaultFill,
890 hasFillOpacityParam, hasDefaultFillOpacity, defaultFillOpacity,
891 hasStrokeParam, hasDefaultStroke, defaultStroke,
892 hasStrokeWidthParam, hasDefaultStrokeWidth, defaultStrokeWidth,
893 hasStrokeOpacityParam, hasDefaultStrokeOpacity, defaultStrokeOpacity );
897 void QgsSvgCache::removeCacheEntry(
const QString &s, QgsSvgCacheEntry *entry )
900 mEntryLookup.remove( s, entry );
903 void QgsSvgCache::printEntryList()
905 QgsDebugMsg(
"****************svg cache entry list*************************" );
906 QgsDebugMsg(
"Cache size: " + QString::number( mTotalSize ) );
907 QgsSvgCacheEntry *entry = mLeastRecentEntry;
912 QgsDebugMsg(
"Size:" + QString::number( entry->size ) );
913 QgsDebugMsg(
"Width scale factor" + QString::number( entry->widthScaleFactor ) );
914 entry = entry->nextEntry;
918 QSize QgsSvgCache::sizeForImage(
const QgsSvgCacheEntry &entry, QSizeF &viewBoxSize, QSizeF &scaledSize )
const 920 bool isFixedAR = entry.fixedAspectRatio > 0;
922 QSvgRenderer r( entry.svgContent );
923 double hwRatio = 1.0;
924 viewBoxSize = r.viewBoxF().size();
925 if ( viewBoxSize.width() > 0 )
929 hwRatio = entry.fixedAspectRatio;
933 hwRatio = viewBoxSize.height() / viewBoxSize.width();
938 scaledSize.setWidth( entry.size );
939 int wImgSize =
static_cast< int >( scaledSize.width() );
944 scaledSize.setHeight( scaledSize.width() * hwRatio );
945 int hImgSize =
static_cast< int >( scaledSize.height() );
950 return QSize( wImgSize, hImgSize );
953 QImage QgsSvgCache::imageFromCachedPicture(
const QgsSvgCacheEntry &entry )
const 957 QImage image( sizeForImage( entry, viewBoxSize, scaledSize ), QImage::Format_ARGB32_Premultiplied );
960 QPainter p( &image );
961 p.drawPicture( QPoint( 0, 0 ), *entry.picture );
965 void QgsSvgCache::trimToMaximumSize()
968 if ( mLeastRecentEntry == mMostRecentEntry )
972 QgsSvgCacheEntry *entry = mLeastRecentEntry;
973 while ( entry && ( mTotalSize > MAXIMUM_SIZE ) )
975 QgsSvgCacheEntry *bkEntry = entry;
976 entry = entry->nextEntry;
978 takeEntryFromList( bkEntry );
979 mEntryLookup.remove( bkEntry->path, bkEntry );
980 mTotalSize -= bkEntry->dataSize();
985 void QgsSvgCache::takeEntryFromList( QgsSvgCacheEntry *entry )
992 if ( entry->previousEntry )
994 entry->previousEntry->nextEntry = entry->nextEntry;
998 mLeastRecentEntry = entry->nextEntry;
1000 if ( entry->nextEntry )
1002 entry->nextEntry->previousEntry = entry->previousEntry;
1006 mMostRecentEntry = entry->previousEntry;
1010 void QgsSvgCache::downloadProgress( qint64 bytesReceived, qint64 bytesTotal )
1012 QString msg = tr(
"%1 of %2 bytes of svg image downloaded." ).arg( bytesReceived ).arg( bytesTotal < 0 ? QStringLiteral(
"unknown number of" ) : QString::number( bytesTotal ) );
1017 void QgsSvgCache::onRemoteSvgFetched(
const QString &url,
bool success )
1019 QMutexLocker locker( &mMutex );
1020 mPendingRemoteUrls.remove( url );
1022 QgsSvgCacheEntry *nextEntry = mLeastRecentEntry;
1023 while ( QgsSvgCacheEntry *entry = nextEntry )
1025 nextEntry = entry->nextEntry;
1026 if ( entry->path == url )
1028 takeEntryFromList( entry );
1029 mEntryLookup.remove( entry->path, entry );
1030 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)
Get SVG as QPicture&.
QByteArray getImageData(const QString &path) const
Get 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)
Get SVG as QImage.
bool operator==(const QgsFeatureIterator &fi1, const QgsFeatureIterator &fi2)
QNetworkReply * reply()
Returns the network reply.
Handles HTTP network content fetching in a background task.
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Compare two doubles (but allow some difference)
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::Warning)
add a message to the instance (and create it if necessary)
static QgsTaskManager * taskManager()
Returns the application's task manager, used for managing application wide background task handling...
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)
Get 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.