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( p.startsWith( QLatin1String(
"base64:", Qt::CaseInsensitive ) ) ? QDateTime() : 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 if ( !entry->path.startsWith( QStringLiteral(
"base64:" ) ) )
223 entry->fileModified = QFileInfo( entry->path ).lastModified();
224 entry->fileModifiedLastCheckTimer.start();
227 mEntryLookup.insert( path, entry );
230 if ( !mMostRecentEntry )
232 mLeastRecentEntry = entry;
233 mMostRecentEntry = entry;
234 entry->previousEntry =
nullptr;
235 entry->nextEntry =
nullptr;
239 entry->previousEntry = mMostRecentEntry;
240 entry->nextEntry =
nullptr;
241 mMostRecentEntry->nextEntry = entry;
242 mMostRecentEntry = entry;
249 void QgsSvgCache::containsParams(
const QString &path,
bool &hasFillParam, QColor &defaultFillColor,
bool &hasStrokeParam, QColor &defaultStrokeColor,
250 bool &hasStrokeWidthParam,
double &defaultStrokeWidth )
const 252 bool hasDefaultFillColor =
false;
253 bool hasFillOpacityParam =
false;
254 bool hasDefaultFillOpacity =
false;
255 double defaultFillOpacity = 1.0;
256 bool hasDefaultStrokeColor =
false;
257 bool hasDefaultStrokeWidth =
false;
258 bool hasStrokeOpacityParam =
false;
259 bool hasDefaultStrokeOpacity =
false;
260 double defaultStrokeOpacity = 1.0;
262 containsParams( path, hasFillParam, hasDefaultFillColor, defaultFillColor,
263 hasFillOpacityParam, hasDefaultFillOpacity, defaultFillOpacity,
264 hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
265 hasStrokeWidthParam, hasDefaultStrokeWidth, defaultStrokeWidth,
266 hasStrokeOpacityParam, hasDefaultStrokeOpacity, defaultStrokeOpacity );
270 bool &hasFillParam,
bool &hasDefaultFillParam, QColor &defaultFillColor,
271 bool &hasFillOpacityParam,
bool &hasDefaultFillOpacity,
double &defaultFillOpacity,
272 bool &hasStrokeParam,
bool &hasDefaultStrokeColor, QColor &defaultStrokeColor,
273 bool &hasStrokeWidthParam,
bool &hasDefaultStrokeWidth,
double &defaultStrokeWidth,
274 bool &hasStrokeOpacityParam,
bool &hasDefaultStrokeOpacity,
double &defaultStrokeOpacity )
const 276 hasFillParam =
false;
277 hasFillOpacityParam =
false;
278 hasStrokeParam =
false;
279 hasStrokeWidthParam =
false;
280 hasStrokeOpacityParam =
false;
281 defaultFillColor = QColor( Qt::white );
282 defaultFillOpacity = 1.0;
283 defaultStrokeColor = QColor( Qt::black );
284 defaultStrokeWidth = 0.2;
285 defaultStrokeOpacity = 1.0;
287 hasDefaultFillParam =
false;
288 hasDefaultFillOpacity =
false;
289 hasDefaultStrokeColor =
false;
290 hasDefaultStrokeWidth =
false;
291 hasDefaultStrokeOpacity =
false;
299 QDomElement docElem = svgDoc.documentElement();
300 containsElemParams( docElem, hasFillParam, hasDefaultFillParam, defaultFillColor,
301 hasFillOpacityParam, hasDefaultFillOpacity, defaultFillOpacity,
302 hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
303 hasStrokeWidthParam, hasDefaultStrokeWidth, defaultStrokeWidth,
304 hasStrokeOpacityParam, hasDefaultStrokeOpacity, defaultStrokeOpacity );
307 void QgsSvgCache::replaceParamsAndCacheSvg( QgsSvgCacheEntry *entry )
315 if ( !svgDoc.setContent(
getImageData( entry->path ) ) )
321 QDomElement docElem = svgDoc.documentElement();
324 double sizeScaleFactor = calcSizeScaleFactor( entry, docElem, viewboxSize );
325 entry->viewboxSize = viewboxSize;
326 replaceElemParams( docElem, entry->fill, entry->stroke, entry->strokeWidth * sizeScaleFactor );
328 entry->svgContent = svgDoc.toByteArray( 0 );
332 entry->svgContent.replace(
"\n<tspan",
"<tspan" );
333 entry->svgContent.replace(
"</tspan>\n",
"</tspan>" );
335 mTotalSize += entry->svgContent.size();
338 double QgsSvgCache::calcSizeScaleFactor( QgsSvgCacheEntry *entry,
const QDomElement &docElem, QSizeF &viewboxSize )
const 348 if ( docElem.tagName() == QLatin1String(
"svg" ) && docElem.hasAttribute( QStringLiteral(
"viewBox" ) ) )
350 viewBox = docElem.attribute( QStringLiteral(
"viewBox" ), QString() );
352 else if ( docElem.tagName() == QLatin1String(
"svg" ) && docElem.hasAttribute( QStringLiteral(
"viewbox" ) ) )
354 viewBox = docElem.attribute( QStringLiteral(
"viewbox" ), QString() );
358 QDomElement svgElem = docElem.firstChildElement( QStringLiteral(
"svg" ) );
359 if ( !svgElem.isNull() )
361 if ( svgElem.hasAttribute( QStringLiteral(
"viewBox" ) ) )
362 viewBox = svgElem.attribute( QStringLiteral(
"viewBox" ), QString() );
363 else if ( svgElem.hasAttribute( QStringLiteral(
"viewbox" ) ) )
364 viewBox = svgElem.attribute( QStringLiteral(
"viewbox" ), QString() );
369 if ( viewBox.isEmpty() )
373 QStringList parts = viewBox.split(
' ' );
374 if ( parts.count() != 4 )
377 bool heightOk =
false;
378 double height = parts.at( 3 ).toDouble( &heightOk );
380 bool widthOk =
false;
381 double width = parts.at( 2 ).toDouble( &widthOk );
385 viewboxSize = QSizeF( width, height );
386 return width / entry->size;
395 if ( path.startsWith( QLatin1String(
"base64:" ), Qt::CaseInsensitive ) )
397 QByteArray base64 = path.mid( 7 ).toLocal8Bit();
398 return QByteArray::fromBase64( base64, QByteArray::OmitTrailingEquals );
402 QFile svgFile( path );
403 if ( svgFile.exists() )
405 if ( svgFile.open( QIODevice::ReadOnly ) )
407 return svgFile.readAll();
416 if ( !path.contains( QLatin1String(
"://" ) ) )
422 if ( !svgUrl.isValid() )
428 if ( svgUrl.scheme().compare( QLatin1String(
"file" ), Qt::CaseInsensitive ) == 0 )
430 svgFile.setFileName( svgUrl.toLocalFile() );
431 if ( svgFile.exists() )
433 if ( svgFile.open( QIODevice::ReadOnly ) )
435 return svgFile.readAll();
443 QMutexLocker locker( &mMutex );
446 if ( mPendingRemoteUrls.contains( path ) )
449 if ( mRemoteContentCache.contains( path ) )
452 return *mRemoteContentCache[ path ];
455 mPendingRemoteUrls.insert( path );
457 QNetworkRequest request( svgUrl );
458 request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache );
459 request.setAttribute( QNetworkRequest::CacheSaveControlAttribute,
true );
464 QMutexLocker locker( &mMutex );
466 QNetworkReply *reply = task->
reply();
470 QMetaObject::invokeMethod( const_cast< QgsSvgCache * >(
this ),
"onRemoteSvgFetched", Qt::QueuedConnection, Q_ARG( QString, path ), Q_ARG(
bool,
false ) );
474 if ( reply->error() != QNetworkReply::NoError )
476 QgsMessageLog::logMessage( tr(
"SVG request failed [error: %1 - url: %2]" ).arg( reply->errorString(), path ), tr(
"SVG" ) );
482 QVariant status = reply->attribute( QNetworkRequest::HttpStatusCodeAttribute );
483 if ( !status.isNull() && status.toInt() >= 400 )
485 QVariant phrase = reply->attribute( QNetworkRequest::HttpReasonPhraseAttribute );
486 QgsMessageLog::logMessage( tr(
"SVG request error [status: %1 - reason phrase: %2] for %3" ).arg( status.toInt() ).arg( phrase.toString(), path ), tr(
"SVG" ) );
487 mRemoteContentCache.insert( path,
new QByteArray( mMissingSvg ) );
493 QString contentType = reply->header( QNetworkRequest::ContentTypeHeader ).toString();
494 if ( !contentType.startsWith( QLatin1String(
"image/svg+xml" ), Qt::CaseInsensitive )
495 && !contentType.startsWith( QLatin1String(
"text/plain" ), Qt::CaseInsensitive ) )
498 mRemoteContentCache.insert( path,
new QByteArray( mMissingSvg ) );
505 mRemoteContentCache.insert( path,
new QByteArray( reply->readAll() ) );
507 QMetaObject::invokeMethod( const_cast< QgsSvgCache * >(
this ),
"onRemoteSvgFetched", Qt::QueuedConnection, Q_ARG( QString, path ), Q_ARG(
bool,
true ) );
514 void QgsSvgCache::cacheImage( QgsSvgCacheEntry *entry )
521 entry->image.reset();
525 QSize imageSize = sizeForImage( *entry, viewBoxSize, scaledSize );
528 std::unique_ptr< QImage > image = qgis::make_unique< QImage >( imageSize, QImage::Format_ARGB32_Premultiplied );
531 QPainter p( image.get() );
532 QSvgRenderer r( entry->svgContent );
533 if (
qgsDoubleNear( viewBoxSize.width(), viewBoxSize.height() ) )
539 QSizeF s( viewBoxSize );
540 s.scale( scaledSize.width(), scaledSize.height(), Qt::KeepAspectRatio );
541 QRectF rect( ( imageSize.width() - s.width() ) / 2, ( imageSize.height() - s.height() ) / 2, s.width(), s.height() );
542 r.render( &p, rect );
545 mTotalSize += ( image->width() * image->height() * 32 );
546 entry->image = std::move( image );
549 void QgsSvgCache::cachePicture( QgsSvgCacheEntry *entry,
bool forceVectorOutput )
551 Q_UNUSED( forceVectorOutput );
557 entry->picture.reset();
559 bool isFixedAR = entry->fixedAspectRatio > 0;
562 std::unique_ptr< QPicture > picture = qgis::make_unique< QPicture >();
564 QSvgRenderer r( entry->svgContent );
565 double hwRatio = 1.0;
566 if ( r.viewBoxF().width() > 0 )
570 hwRatio = entry->fixedAspectRatio;
574 hwRatio = r.viewBoxF().height() / r.viewBoxF().width();
578 double wSize = entry->size;
579 double hSize = wSize * hwRatio;
581 QSizeF s( r.viewBoxF().size() );
582 s.scale( wSize, hSize, isFixedAR ? Qt::IgnoreAspectRatio : Qt::KeepAspectRatio );
583 rect = QRectF( -s.width() / 2.0, -s.height() / 2.0, s.width(), s.height() );
585 QPainter p( picture.get() );
586 r.render( &p, rect );
587 entry->picture = std::move( picture );
588 mTotalSize += entry->picture->size();
591 QgsSvgCacheEntry *QgsSvgCache::cacheEntry(
const QString &path,
double size,
const QColor &fill,
const QColor &stroke,
double strokeWidth,
592 double widthScaleFactor,
double fixedAspectRatio )
595 QgsSvgCacheEntry *currentEntry =
nullptr;
596 QList<QgsSvgCacheEntry *> entries = mEntryLookup.values( path );
598 QList<QgsSvgCacheEntry *>::iterator entryIt = entries.begin();
599 for ( ; entryIt != entries.end(); ++entryIt )
601 QgsSvgCacheEntry *cacheEntry = *entryIt;
602 if (
qgsDoubleNear( cacheEntry->size, size ) && cacheEntry->fill == fill && cacheEntry->stroke == stroke &&
604 qgsDoubleNear( cacheEntry->fixedAspectRatio, fixedAspectRatio ) )
606 if ( mFileModifiedCheckTimeout <= 0 || cacheEntry->fileModifiedLastCheckTimer.hasExpired( mFileModifiedCheckTimeout ) )
608 if ( !modified.isValid() )
609 modified = QFileInfo( path ).lastModified();
611 if ( cacheEntry->fileModified != modified )
614 cacheEntry->fileModifiedLastCheckTimer.restart();
616 currentEntry = cacheEntry;
625 currentEntry = insertSvg( path, size, fill, stroke, strokeWidth, widthScaleFactor, fixedAspectRatio );
629 takeEntryFromList( currentEntry );
630 if ( !mMostRecentEntry )
632 mMostRecentEntry = currentEntry;
633 mLeastRecentEntry = currentEntry;
637 mMostRecentEntry->nextEntry = currentEntry;
638 currentEntry->previousEntry = mMostRecentEntry;
639 currentEntry->nextEntry =
nullptr;
640 mMostRecentEntry = currentEntry;
650 void QgsSvgCache::replaceElemParams( QDomElement &elem,
const QColor &fill,
const QColor &stroke,
double strokeWidth )
658 QDomNamedNodeMap attributes = elem.attributes();
659 int nAttributes = attributes.count();
660 for (
int i = 0; i < nAttributes; ++i )
662 QDomAttr attribute = attributes.item( i ).toAttr();
664 if ( attribute.name().compare( QLatin1String(
"style" ), Qt::CaseInsensitive ) == 0 )
667 QString newAttributeString;
669 QStringList entryList = attribute.value().split(
';' );
670 QStringList::const_iterator entryIt = entryList.constBegin();
671 for ( ; entryIt != entryList.constEnd(); ++entryIt )
673 QStringList keyValueSplit = entryIt->split(
':' );
674 if ( keyValueSplit.size() < 2 )
678 const QString key = keyValueSplit.at( 0 );
679 QString value = keyValueSplit.at( 1 );
680 QString newValue = value;
681 value = value.trimmed().toLower();
683 if ( value.startsWith( QLatin1String(
"param(fill)" ) ) )
685 newValue = fill.name();
687 else if ( value.startsWith( QLatin1String(
"param(fill-opacity)" ) ) )
689 newValue = QString::number( fill.alphaF() );
691 else if ( value.startsWith( QLatin1String(
"param(outline)" ) ) )
693 newValue = stroke.name();
695 else if ( value.startsWith( QLatin1String(
"param(outline-opacity)" ) ) )
697 newValue = QString::number( stroke.alphaF() );
699 else if ( value.startsWith( QLatin1String(
"param(outline-width)" ) ) )
701 newValue = QString::number( strokeWidth );
704 if ( entryIt != entryList.constBegin() )
706 newAttributeString.append(
';' );
708 newAttributeString.append( key +
':' + newValue );
710 elem.setAttribute( attribute.name(), newAttributeString );
714 const QString value = attribute.value().trimmed().toLower();
715 if ( value.startsWith( QLatin1String(
"param(fill)" ) ) )
717 elem.setAttribute( attribute.name(), fill.name() );
719 else if ( value.startsWith( QLatin1String(
"param(fill-opacity)" ) ) )
721 elem.setAttribute( attribute.name(), fill.alphaF() );
723 else if ( value.startsWith( QLatin1String(
"param(outline)" ) ) )
725 elem.setAttribute( attribute.name(), stroke.name() );
727 else if ( value.startsWith( QLatin1String(
"param(outline-opacity)" ) ) )
729 elem.setAttribute( attribute.name(), stroke.alphaF() );
731 else if ( value.startsWith( QLatin1String(
"param(outline-width)" ) ) )
733 elem.setAttribute( attribute.name(), QString::number( strokeWidth ) );
738 QDomNodeList childList = elem.childNodes();
739 int nChildren = childList.count();
740 for (
int i = 0; i < nChildren; ++i )
742 QDomElement childElem = childList.at( i ).toElement();
743 replaceElemParams( childElem, fill, stroke, strokeWidth );
747 void QgsSvgCache::containsElemParams(
const QDomElement &elem,
bool &hasFillParam,
bool &hasDefaultFill, QColor &defaultFill,
748 bool &hasFillOpacityParam,
bool &hasDefaultFillOpacity,
double &defaultFillOpacity,
749 bool &hasStrokeParam,
bool &hasDefaultStroke, QColor &defaultStroke,
750 bool &hasStrokeWidthParam,
bool &hasDefaultStrokeWidth,
double &defaultStrokeWidth,
751 bool &hasStrokeOpacityParam,
bool &hasDefaultStrokeOpacity,
double &defaultStrokeOpacity )
const 759 if ( hasFillParam && hasStrokeParam && hasStrokeWidthParam && hasFillOpacityParam && hasStrokeOpacityParam )
765 QDomNamedNodeMap attributes = elem.attributes();
766 int nAttributes = attributes.count();
768 QStringList valueSplit;
769 for (
int i = 0; i < nAttributes; ++i )
771 QDomAttr attribute = attributes.item( i ).toAttr();
772 if ( attribute.name().compare( QLatin1String(
"style" ), Qt::CaseInsensitive ) == 0 )
775 QStringList entryList = attribute.value().split(
';' );
776 QStringList::const_iterator entryIt = entryList.constBegin();
777 for ( ; entryIt != entryList.constEnd(); ++entryIt )
779 QStringList keyValueSplit = entryIt->split(
':' );
780 if ( keyValueSplit.size() < 2 )
784 QString value = keyValueSplit.at( 1 );
785 valueSplit = value.split(
' ' );
786 if ( !hasFillParam && value.startsWith( QLatin1String(
"param(fill)" ) ) )
789 if ( valueSplit.size() > 1 )
791 defaultFill = QColor( valueSplit.at( 1 ) );
792 hasDefaultFill =
true;
795 else if ( !hasFillOpacityParam && value.startsWith( QLatin1String(
"param(fill-opacity)" ) ) )
797 hasFillOpacityParam =
true;
798 if ( valueSplit.size() > 1 )
801 double opacity = valueSplit.at( 1 ).toDouble( &ok );
804 defaultFillOpacity = opacity;
805 hasDefaultFillOpacity =
true;
809 else if ( !hasStrokeParam && value.startsWith( QLatin1String(
"param(outline)" ) ) )
811 hasStrokeParam =
true;
812 if ( valueSplit.size() > 1 )
814 defaultStroke = QColor( valueSplit.at( 1 ) );
815 hasDefaultStroke =
true;
818 else if ( !hasStrokeWidthParam && value.startsWith( QLatin1String(
"param(outline-width)" ) ) )
820 hasStrokeWidthParam =
true;
821 if ( valueSplit.size() > 1 )
823 defaultStrokeWidth = valueSplit.at( 1 ).toDouble();
824 hasDefaultStrokeWidth =
true;
827 else if ( !hasStrokeOpacityParam && value.startsWith( QLatin1String(
"param(outline-opacity)" ) ) )
829 hasStrokeOpacityParam =
true;
830 if ( valueSplit.size() > 1 )
833 double opacity = valueSplit.at( 1 ).toDouble( &ok );
836 defaultStrokeOpacity = opacity;
837 hasDefaultStrokeOpacity =
true;
845 QString value = attribute.value();
846 valueSplit = value.split(
' ' );
847 if ( !hasFillParam && value.startsWith( QLatin1String(
"param(fill)" ) ) )
850 if ( valueSplit.size() > 1 )
852 defaultFill = QColor( valueSplit.at( 1 ) );
853 hasDefaultFill =
true;
856 else if ( !hasFillOpacityParam && value.startsWith( QLatin1String(
"param(fill-opacity)" ) ) )
858 hasFillOpacityParam =
true;
859 if ( valueSplit.size() > 1 )
862 double opacity = valueSplit.at( 1 ).toDouble( &ok );
865 defaultFillOpacity = opacity;
866 hasDefaultFillOpacity =
true;
870 else if ( !hasStrokeParam && value.startsWith( QLatin1String(
"param(outline)" ) ) )
872 hasStrokeParam =
true;
873 if ( valueSplit.size() > 1 )
875 defaultStroke = QColor( valueSplit.at( 1 ) );
876 hasDefaultStroke =
true;
879 else if ( !hasStrokeWidthParam && value.startsWith( QLatin1String(
"param(outline-width)" ) ) )
881 hasStrokeWidthParam =
true;
882 if ( valueSplit.size() > 1 )
884 defaultStrokeWidth = valueSplit.at( 1 ).toDouble();
885 hasDefaultStrokeWidth =
true;
888 else if ( !hasStrokeOpacityParam && value.startsWith( QLatin1String(
"param(outline-opacity)" ) ) )
890 hasStrokeOpacityParam =
true;
891 if ( valueSplit.size() > 1 )
894 double opacity = valueSplit.at( 1 ).toDouble( &ok );
897 defaultStrokeOpacity = opacity;
898 hasDefaultStrokeOpacity =
true;
906 QDomNodeList childList = elem.childNodes();
907 int nChildren = childList.count();
908 for (
int i = 0; i < nChildren; ++i )
910 QDomElement childElem = childList.at( i ).toElement();
911 containsElemParams( childElem, hasFillParam, hasDefaultFill, defaultFill,
912 hasFillOpacityParam, hasDefaultFillOpacity, defaultFillOpacity,
913 hasStrokeParam, hasDefaultStroke, defaultStroke,
914 hasStrokeWidthParam, hasDefaultStrokeWidth, defaultStrokeWidth,
915 hasStrokeOpacityParam, hasDefaultStrokeOpacity, defaultStrokeOpacity );
919 void QgsSvgCache::removeCacheEntry(
const QString &s, QgsSvgCacheEntry *entry )
922 mEntryLookup.remove( s, entry );
925 void QgsSvgCache::printEntryList()
927 QgsDebugMsg( QStringLiteral(
"****************svg cache entry list*************************" ) );
928 QgsDebugMsg(
"Cache size: " + QString::number( mTotalSize ) );
929 QgsSvgCacheEntry *entry = mLeastRecentEntry;
934 QgsDebugMsg(
"Size:" + QString::number( entry->size ) );
935 QgsDebugMsg(
"Width scale factor" + QString::number( entry->widthScaleFactor ) );
936 entry = entry->nextEntry;
940 QSize QgsSvgCache::sizeForImage(
const QgsSvgCacheEntry &entry, QSizeF &viewBoxSize, QSizeF &scaledSize )
const 942 bool isFixedAR = entry.fixedAspectRatio > 0;
944 QSvgRenderer r( entry.svgContent );
945 double hwRatio = 1.0;
946 viewBoxSize = r.viewBoxF().size();
947 if ( viewBoxSize.width() > 0 )
951 hwRatio = entry.fixedAspectRatio;
955 hwRatio = viewBoxSize.height() / viewBoxSize.width();
960 scaledSize.setWidth( entry.size );
961 int wImgSize =
static_cast< int >( scaledSize.width() );
966 scaledSize.setHeight( scaledSize.width() * hwRatio );
967 int hImgSize =
static_cast< int >( scaledSize.height() );
972 return QSize( wImgSize, hImgSize );
975 QImage QgsSvgCache::imageFromCachedPicture(
const QgsSvgCacheEntry &entry )
const 979 QImage image( sizeForImage( entry, viewBoxSize, scaledSize ), QImage::Format_ARGB32_Premultiplied );
982 QPainter p( &image );
983 p.drawPicture( QPoint( 0, 0 ), *entry.picture );
987 void QgsSvgCache::trimToMaximumSize()
990 if ( mLeastRecentEntry == mMostRecentEntry )
994 QgsSvgCacheEntry *entry = mLeastRecentEntry;
995 while ( entry && ( mTotalSize > MAXIMUM_SIZE ) )
997 QgsSvgCacheEntry *bkEntry = entry;
998 entry = entry->nextEntry;
1000 takeEntryFromList( bkEntry );
1001 mEntryLookup.remove( bkEntry->path, bkEntry );
1002 mTotalSize -= bkEntry->dataSize();
1007 void QgsSvgCache::takeEntryFromList( QgsSvgCacheEntry *entry )
1014 if ( entry->previousEntry )
1016 entry->previousEntry->nextEntry = entry->nextEntry;
1020 mLeastRecentEntry = entry->nextEntry;
1022 if ( entry->nextEntry )
1024 entry->nextEntry->previousEntry = entry->previousEntry;
1028 mMostRecentEntry = entry->previousEntry;
1032 void QgsSvgCache::downloadProgress( qint64 bytesReceived, qint64 bytesTotal )
1034 QString msg = tr(
"%1 of %2 bytes of svg image downloaded." ).arg( bytesReceived ).arg( bytesTotal < 0 ? QStringLiteral(
"unknown number of" ) : QString::number( bytesTotal ) );
1039 void QgsSvgCache::onRemoteSvgFetched(
const QString &url,
bool success )
1041 QMutexLocker locker( &mMutex );
1042 mPendingRemoteUrls.remove( url );
1044 QgsSvgCacheEntry *nextEntry = mLeastRecentEntry;
1045 while ( QgsSvgCacheEntry *entry = nextEntry )
1047 nextEntry = entry->nextEntry;
1048 if ( entry->path == url )
1050 takeEntryFromList( entry );
1051 mEntryLookup.remove( entry->path, entry );
1052 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&.
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...
QByteArray getImageData(const QString &path) const
Gets image data.
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.