Use QgsAbstractContentCache base class for QgsSvgCache

This commit is contained in:
Nyall Dawson 2018-12-03 11:15:13 +10:00 committed by Mathieu Pellerin
parent 22496dc139
commit cdba8f5621
3 changed files with 89 additions and 417 deletions

View File

@ -12,7 +12,7 @@
class QgsSvgCache : QObject
class QgsSvgCache : QgsAbstractContentCacheBase
{
%Docstring
A cache for images / pictures derived from svg files. This class supports parameter replacement in svg files
@ -33,8 +33,6 @@ QgsSvgCache is not usually directly created, but rather accessed through
Constructor for QgsSvgCache.
%End
~QgsSvgCache();
QImage svgAsImage( const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth,
double widthScaleFactor, bool &fitsInCache, double fixedAspectRatio = 0 );
%Docstring
@ -135,9 +133,12 @@ Gets SVG content
%End
signals:
void statusChanged( const QString &statusQString );
void statusChanged( const QString &statusQString ) /Deprecated/;
%Docstring
Emit a signal to be caught by qgisapp and display a msg on status bar
Emit a signal to be caught by qgisapp and display a msg on status bar.
.. deprecated:: Deprecated since QGIS 3.6 -- no longer emitted.
%End
void remoteSvgFetched( const QString &url );
@ -147,6 +148,11 @@ Emitted when the cache has finished retrieving an SVG file from a remote ``url``
.. versionadded:: 3.2
%End
protected:
virtual bool checkReply( QNetworkReply *reply, const QString &path ) const;
};
/************************************************************************

View File

@ -39,28 +39,36 @@
///@cond PRIVATE
QgsSvgCacheEntry::QgsSvgCacheEntry( const QString &p, double s, double ow, double wsf, const QColor &fi, const QColor &ou, double far )
: path( p )
, fileModified( QFileInfo( p ).lastModified() )
, size( s )
, strokeWidth( ow )
, widthScaleFactor( wsf )
, fixedAspectRatio( far )
, fill( fi )
, stroke( ou )
//
// QgsSvgCacheEntry
//
QgsSvgCacheEntry::QgsSvgCacheEntry( const QString &path, double size, double strokeWidth, double widthScaleFactor, const QColor &fill, const QColor &stroke, double fixedAspectRatio )
: QgsAbstractContentCacheEntry( path )
, size( size )
, strokeWidth( strokeWidth )
, widthScaleFactor( widthScaleFactor )
, fixedAspectRatio( fixedAspectRatio )
, fill( fill )
, stroke( stroke )
{
fileModifiedLastCheckTimer.start();
}
bool QgsSvgCacheEntry::operator==( const QgsSvgCacheEntry &other ) const
bool QgsSvgCacheEntry::isEqual( const QgsAbstractContentCacheEntry *other ) const
{
bool equal = other.path == path && qgsDoubleNear( other.size, size ) && qgsDoubleNear( other.strokeWidth, strokeWidth ) && qgsDoubleNear( other.widthScaleFactor, widthScaleFactor )
&& other.fixedAspectRatio == fixedAspectRatio && other.fill == fill && other.stroke == stroke;
const QgsSvgCacheEntry *otherSvg = dynamic_cast< const QgsSvgCacheEntry * >( other );
// cheapest checks first!
if ( !otherSvg
|| !qgsDoubleNear( otherSvg->fixedAspectRatio, fixedAspectRatio )
|| !qgsDoubleNear( otherSvg->size, size )
|| !qgsDoubleNear( otherSvg->strokeWidth, strokeWidth )
|| !qgsDoubleNear( otherSvg->widthScaleFactor, widthScaleFactor )
|| otherSvg->fill != fill
|| otherSvg->stroke != stroke
|| otherSvg->path != path )
return false;
if ( equal && ( mFileModifiedCheckTimeout <= 0 || fileModifiedLastCheckTimer.hasExpired( mFileModifiedCheckTimeout ) ) )
equal = other.fileModified == fileModified;
return equal;
return true;
}
int QgsSvgCacheEntry::dataSize() const
@ -76,12 +84,20 @@ int QgsSvgCacheEntry::dataSize() const
}
return size;
}
void QgsSvgCacheEntry::dump() const
{
QgsDebugMsg( QStringLiteral( "path: %1, size %2, width scale factor %3" ).arg( path ).arg( size ).arg( widthScaleFactor ) );
}
///@endcond
//
// QgsSvgCache
//
QgsSvgCache::QgsSvgCache( QObject *parent )
: QObject( parent )
, mMutex( QMutex::Recursive )
: QgsAbstractContentCache< QgsSvgCacheEntry >( parent, QObject::tr( "SVG" ) )
{
mMissingSvg = QStringLiteral( "<svg width='10' height='10'><text x='5' y='10' font-size='10' text-anchor='middle'>?</text></svg>" ).toLatin1();
@ -99,14 +115,10 @@ QgsSvgCache::QgsSvgCache( QObject *parent )
{
mFetchingSvg = QStringLiteral( "<svg width='10' height='10'><text x='5' y='10' font-size='10' text-anchor='middle'>?</text></svg>" ).toLatin1();
}
}
QgsSvgCache::~QgsSvgCache()
{
qDeleteAll( mEntryLookup );
connect( this, &QgsAbstractContentCacheBase::remoteContentFetched, this, &QgsSvgCache::remoteSvgFetched );
}
QImage QgsSvgCache::svgAsImage( const QString &file, double size, const QColor &fill, const QColor &stroke, double strokeWidth,
double widthScaleFactor, bool &fitsInCache, double fixedAspectRatio )
{
@ -138,7 +150,7 @@ QImage QgsSvgCache::svgAsImage( const QString &file, double size, const QColor &
long cachedDataSize = 0;
cachedDataSize += currentEntry->svgContent.size();
cachedDataSize += static_cast< int >( currentEntry->size * currentEntry->size * hwRatio * 32 );
if ( cachedDataSize > MAXIMUM_SIZE / 2 )
if ( cachedDataSize > mMaxCacheSize / 2 )
{
fitsInCache = false;
currentEntry->image.reset();
@ -206,40 +218,9 @@ QSizeF QgsSvgCache::svgViewboxSize( const QString &path, double size, const QCol
QMutexLocker locker( &mMutex );
QgsSvgCacheEntry *currentEntry = cacheEntry( path, size, fill, stroke, strokeWidth, widthScaleFactor, fixedAspectRatio );
return currentEntry->viewboxSize;
}
QgsSvgCacheEntry *QgsSvgCache::insertSvg( const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth,
double widthScaleFactor, double fixedAspectRatio )
{
QgsSvgCacheEntry *entry = new QgsSvgCacheEntry( path, size, strokeWidth, widthScaleFactor, fill, stroke, fixedAspectRatio );
entry->mFileModifiedCheckTimeout = mFileModifiedCheckTimeout;
replaceParamsAndCacheSvg( entry );
mEntryLookup.insert( path, entry );
//insert to most recent place in entry list
if ( !mMostRecentEntry ) //inserting first entry
{
mLeastRecentEntry = entry;
mMostRecentEntry = entry;
entry->previousEntry = nullptr;
entry->nextEntry = nullptr;
}
else
{
entry->previousEntry = mMostRecentEntry;
entry->nextEntry = nullptr;
mMostRecentEntry->nextEntry = entry;
mMostRecentEntry = entry;
}
trimToMaximumSize();
return entry;
}
void QgsSvgCache::containsParams( const QString &path, bool &hasFillParam, QColor &defaultFillColor, bool &hasStrokeParam, QColor &defaultStrokeColor,
bool &hasStrokeWidthParam, double &defaultStrokeWidth ) const
{
@ -285,7 +266,7 @@ void QgsSvgCache::containsParams( const QString &path,
hasDefaultStrokeOpacity = false;
QDomDocument svgDoc;
if ( !svgDoc.setContent( getImageData( path ) ) )
if ( !svgDoc.setContent( getContent( path, mMissingSvg, mFetchingSvg ) ) )
{
return;
}
@ -306,7 +287,7 @@ void QgsSvgCache::replaceParamsAndCacheSvg( QgsSvgCacheEntry *entry )
}
QDomDocument svgDoc;
if ( !svgDoc.setContent( getImageData( entry->path ) ) )
if ( !svgDoc.setContent( getContent( entry->path, mMissingSvg, mFetchingSvg ) ) )
{
return;
}
@ -383,126 +364,24 @@ double QgsSvgCache::calcSizeScaleFactor( QgsSvgCacheEntry *entry, const QDomElem
return 1.0;
}
QByteArray QgsSvgCache::getImageData( const QString &path ) const
{
// is it a path to local file?
QFile svgFile( path );
if ( svgFile.exists() )
return getContent( path, mMissingSvg, mFetchingSvg );
};
bool QgsSvgCache::checkReply( QNetworkReply *reply, const QString &path ) const
{
// we accept both real SVG mime types AND plain text types - because some sites
// (notably github) serve up svgs as raw text
QString contentType = reply->header( QNetworkRequest::ContentTypeHeader ).toString();
if ( !contentType.startsWith( QLatin1String( "image/svg+xml" ), Qt::CaseInsensitive )
&& !contentType.startsWith( QLatin1String( "text/plain" ), Qt::CaseInsensitive ) )
{
if ( svgFile.open( QIODevice::ReadOnly ) )
{
return svgFile.readAll();
}
else
{
return mMissingSvg;
}
QgsMessageLog::logMessage( tr( "Unexpected MIME type %1 received for %2" ).arg( contentType, path ), tr( "SVG" ) );
return false;
}
// maybe it's an embedded base64 string
if ( path.startsWith( QLatin1String( "base64:" ), Qt::CaseInsensitive ) )
{
QByteArray base64 = path.mid( 7 ).toLocal8Bit(); // strip 'base64:' prefix
return QByteArray::fromBase64( base64, QByteArray::OmitTrailingEquals );
}
// maybe it's a url...
if ( !path.contains( QLatin1String( "://" ) ) ) // otherwise short, relative SVG paths might be considered URLs
{
return mMissingSvg;
}
QUrl svgUrl( path );
if ( !svgUrl.isValid() )
{
return mMissingSvg;
}
// check whether it's a url pointing to a local file
if ( svgUrl.scheme().compare( QLatin1String( "file" ), Qt::CaseInsensitive ) == 0 )
{
svgFile.setFileName( svgUrl.toLocalFile() );
if ( svgFile.exists() )
{
if ( svgFile.open( QIODevice::ReadOnly ) )
{
return svgFile.readAll();
}
}
// not found...
return mMissingSvg;
}
QMutexLocker locker( &mMutex );
// already a request in progress for this url
if ( mPendingRemoteUrls.contains( path ) )
return mFetchingSvg;
if ( mRemoteContentCache.contains( path ) )
{
// already fetched this content - phew. Just return what we already got.
return *mRemoteContentCache[ path ];
}
mPendingRemoteUrls.insert( path );
//fire up task to fetch image in background
QNetworkRequest request( svgUrl );
request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache );
request.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true );
QgsNetworkContentFetcherTask *task = new QgsNetworkContentFetcherTask( request );
connect( task, &QgsNetworkContentFetcherTask::fetched, this, [this, task, path]
{
QMutexLocker locker( &mMutex );
QNetworkReply *reply = task->reply();
if ( !reply )
{
// canceled
QMetaObject::invokeMethod( const_cast< QgsSvgCache * >( this ), "onRemoteSvgFetched", Qt::QueuedConnection, Q_ARG( QString, path ), Q_ARG( bool, false ) );
return;
}
if ( reply->error() != QNetworkReply::NoError )
{
QgsMessageLog::logMessage( tr( "SVG request failed [error: %1 - url: %2]" ).arg( reply->errorString(), path ), tr( "SVG" ) );
return;
}
bool ok = true;
QVariant status = reply->attribute( QNetworkRequest::HttpStatusCodeAttribute );
if ( !status.isNull() && status.toInt() >= 400 )
{
QVariant phrase = reply->attribute( QNetworkRequest::HttpReasonPhraseAttribute );
QgsMessageLog::logMessage( tr( "SVG request error [status: %1 - reason phrase: %2] for %3" ).arg( status.toInt() ).arg( phrase.toString(), path ), tr( "SVG" ) );
mRemoteContentCache.insert( path, new QByteArray( mMissingSvg ) );
ok = false;
}
// we accept both real SVG mime types AND plain text types - because some sites
// (notably github) serve up svgs as raw text
QString contentType = reply->header( QNetworkRequest::ContentTypeHeader ).toString();
if ( !contentType.startsWith( QLatin1String( "image/svg+xml" ), Qt::CaseInsensitive )
&& !contentType.startsWith( QLatin1String( "text/plain" ), Qt::CaseInsensitive ) )
{
QgsMessageLog::logMessage( tr( "Unexpected MIME type %1 received for %2" ).arg( contentType, path ), tr( "SVG" ) );
mRemoteContentCache.insert( path, new QByteArray( mMissingSvg ) );
ok = false;
}
if ( ok )
{
// read the image data
mRemoteContentCache.insert( path, new QByteArray( reply->readAll() ) );
}
QMetaObject::invokeMethod( const_cast< QgsSvgCache * >( this ), "onRemoteSvgFetched", Qt::QueuedConnection, Q_ARG( QString, path ), Q_ARG( bool, true ) );
} );
QgsApplication::taskManager()->addTask( task );
return mFetchingSvg;
return true;
}
void QgsSvgCache::cacheImage( QgsSvgCacheEntry *entry )
@ -585,60 +464,17 @@ void QgsSvgCache::cachePicture( QgsSvgCacheEntry *entry, bool forceVectorOutput
QgsSvgCacheEntry *QgsSvgCache::cacheEntry( const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth,
double widthScaleFactor, double fixedAspectRatio )
{
//search entries in mEntryLookup
QgsSvgCacheEntry *currentEntry = nullptr;
QList<QgsSvgCacheEntry *> entries = mEntryLookup.values( path );
QDateTime modified;
QList<QgsSvgCacheEntry *>::iterator entryIt = entries.begin();
for ( ; entryIt != entries.end(); ++entryIt )
{
QgsSvgCacheEntry *cacheEntry = *entryIt;
if ( qgsDoubleNear( cacheEntry->size, size ) && cacheEntry->fill == fill && cacheEntry->stroke == stroke &&
qgsDoubleNear( cacheEntry->strokeWidth, strokeWidth ) && qgsDoubleNear( cacheEntry->widthScaleFactor, widthScaleFactor ) &&
qgsDoubleNear( cacheEntry->fixedAspectRatio, fixedAspectRatio ) )
{
if ( mFileModifiedCheckTimeout <= 0 || cacheEntry->fileModifiedLastCheckTimer.hasExpired( mFileModifiedCheckTimeout ) )
{
if ( !modified.isValid() )
modified = QFileInfo( path ).lastModified();
QgsSvgCacheEntry *currentEntry = findExistingEntry( new QgsSvgCacheEntry( path, size, strokeWidth, widthScaleFactor, fill, stroke, fixedAspectRatio ) );
if ( cacheEntry->fileModified != modified )
continue;
}
currentEntry = cacheEntry;
break;
}
}
//if not found: create new entry
//cache and replace params in svg content
if ( !currentEntry )
if ( currentEntry->svgContent.isEmpty() )
{
currentEntry = insertSvg( path, size, fill, stroke, strokeWidth, widthScaleFactor, fixedAspectRatio );
replaceParamsAndCacheSvg( currentEntry );
}
else
{
takeEntryFromList( currentEntry );
if ( !mMostRecentEntry ) //list is empty
{
mMostRecentEntry = currentEntry;
mLeastRecentEntry = currentEntry;
}
else
{
mMostRecentEntry->nextEntry = currentEntry;
currentEntry->previousEntry = mMostRecentEntry;
currentEntry->nextEntry = nullptr;
mMostRecentEntry = currentEntry;
}
}
//debugging
//printEntryList();
return currentEntry;
}
void QgsSvgCache::replaceElemParams( QDomElement &elem, const QColor &fill, const QColor &stroke, double strokeWidth )
{
if ( elem.isNull() )
@ -905,27 +741,6 @@ void QgsSvgCache::containsElemParams( const QDomElement &elem, bool &hasFillPara
}
}
void QgsSvgCache::removeCacheEntry( const QString &s, QgsSvgCacheEntry *entry )
{
delete entry;
mEntryLookup.remove( s, entry );
}
void QgsSvgCache::printEntryList()
{
QgsDebugMsg( QStringLiteral( "****************svg cache entry list*************************" ) );
QgsDebugMsg( "Cache size: " + QString::number( mTotalSize ) );
QgsSvgCacheEntry *entry = mLeastRecentEntry;
while ( entry )
{
QgsDebugMsg( QStringLiteral( "***Entry:" ) );
QgsDebugMsg( "File:" + entry->path );
QgsDebugMsg( "Size:" + QString::number( entry->size ) );
QgsDebugMsg( "Width scale factor" + QString::number( entry->widthScaleFactor ) );
entry = entry->nextEntry;
}
}
QSize QgsSvgCache::sizeForImage( const QgsSvgCacheEntry &entry, QSizeF &viewBoxSize, QSizeF &scaledSize ) const
{
bool isFixedAR = entry.fixedAspectRatio > 0;
@ -973,76 +788,3 @@ QImage QgsSvgCache::imageFromCachedPicture( const QgsSvgCacheEntry &entry ) cons
return image;
}
void QgsSvgCache::trimToMaximumSize()
{
//only one entry in cache
if ( mLeastRecentEntry == mMostRecentEntry )
{
return;
}
QgsSvgCacheEntry *entry = mLeastRecentEntry;
while ( entry && ( mTotalSize > MAXIMUM_SIZE ) )
{
QgsSvgCacheEntry *bkEntry = entry;
entry = entry->nextEntry;
takeEntryFromList( bkEntry );
mEntryLookup.remove( bkEntry->path, bkEntry );
mTotalSize -= bkEntry->dataSize();
delete bkEntry;
}
}
void QgsSvgCache::takeEntryFromList( QgsSvgCacheEntry *entry )
{
if ( !entry )
{
return;
}
if ( entry->previousEntry )
{
entry->previousEntry->nextEntry = entry->nextEntry;
}
else
{
mLeastRecentEntry = entry->nextEntry;
}
if ( entry->nextEntry )
{
entry->nextEntry->previousEntry = entry->previousEntry;
}
else
{
mMostRecentEntry = entry->previousEntry;
}
}
void QgsSvgCache::downloadProgress( qint64 bytesReceived, qint64 bytesTotal )
{
QString msg = tr( "%1 of %2 bytes of svg image downloaded." ).arg( bytesReceived ).arg( bytesTotal < 0 ? QStringLiteral( "unknown number of" ) : QString::number( bytesTotal ) );
QgsDebugMsg( msg );
emit statusChanged( msg );
}
void QgsSvgCache::onRemoteSvgFetched( const QString &url, bool success )
{
QMutexLocker locker( &mMutex );
mPendingRemoteUrls.remove( url );
QgsSvgCacheEntry *nextEntry = mLeastRecentEntry;
while ( QgsSvgCacheEntry *entry = nextEntry )
{
nextEntry = entry->nextEntry;
if ( entry->path == url )
{
takeEntryFromList( entry );
mEntryLookup.remove( entry->path, entry );
mTotalSize -= entry->dataSize();
delete entry;
}
}
if ( success )
emit remoteSvgFetched( url );
}

View File

@ -18,24 +18,12 @@
#ifndef QGSSVGCACHE_H
#define QGSSVGCACHE_H
#include <QColor>
#include "qgsabstractcontentcache.h"
#include "qgis.h"
#include <QMap>
#include <QMultiHash>
#include <QMutex>
#include <QString>
#include <QUrl>
#include <QObject>
#include <QSizeF>
#include <QDateTime>
#include <QElapsedTimer>
#include <QPicture>
#include <QImage>
#include <QCache>
#include <QSet>
#include "qgis_core.h"
#include <QPicture>
class QDomElement;
#ifndef SIP_RUN
@ -46,12 +34,10 @@ class QDomElement;
* \ingroup core
* \class QgsSvgCacheEntry
*/
class CORE_EXPORT QgsSvgCacheEntry
class CORE_EXPORT QgsSvgCacheEntry : public QgsAbstractContentCacheEntry
{
public:
QgsSvgCacheEntry() = delete;
/**
* Constructor.
* \param path Absolute path to SVG file (relative paths are not resolved).
@ -70,15 +56,6 @@ class CORE_EXPORT QgsSvgCacheEntry
//! QgsSvgCacheEntry cannot be copied.
QgsSvgCacheEntry &operator=( const QgsSvgCacheEntry &rh ) = delete;
//! Absolute path to SVG file
QString path;
//! Timestamp when file was last modified
QDateTime fileModified;
//! Time since last check of file modified date
QElapsedTimer fileModifiedLastCheckTimer;
int mFileModifiedCheckTimeout = 30000;
double size = 0.0; //size in pixels (cast to int for QImage)
double strokeWidth = 0;
double widthScaleFactor = 1.0;
@ -99,19 +76,9 @@ class CORE_EXPORT QgsSvgCacheEntry
//content (with params replaced)
QByteArray svgContent;
//keep entries on a least, sorted by last access
QgsSvgCacheEntry *nextEntry = nullptr;
QgsSvgCacheEntry *previousEntry = nullptr;
//! Don't consider image, picture, last used timestamp for comparison
bool operator==( const QgsSvgCacheEntry &other ) const;
//! Returns memory usage in bytes
int dataSize() const;
private:
#ifdef SIP_RUN
QgsSvgCacheEntry( const QgsSvgCacheEntry &rh );
#endif
bool isEqual( const QgsAbstractContentCacheEntry *other ) const override;
int dataSize() const override;
void dump() const override;
};
@ -127,8 +94,13 @@ the parameters 'fill-color', 'pen-color', 'outline-width', 'stroke-width'. E.g.
* QgsSvgCache is not usually directly created, but rather accessed through
* QgsApplication::svgCache().
*/
class CORE_EXPORT QgsSvgCache : public QObject
#ifdef SIP_RUN
class CORE_EXPORT QgsSvgCache : public QgsAbstractContentCacheBase // for sip we skip to the base class and avoid the template difficulty
{
#else
class CORE_EXPORT QgsSvgCache : public QgsAbstractContentCache< QgsSvgCacheEntry >
{
#endif
Q_OBJECT
public:
@ -138,8 +110,6 @@ class CORE_EXPORT QgsSvgCache : public QObject
*/
QgsSvgCache( QObject *parent SIP_TRANSFERTHIS = nullptr );
~QgsSvgCache() override;
/**
* Gets SVG as QImage.
* \param path Absolute path to SVG file.
@ -225,8 +195,12 @@ class CORE_EXPORT QgsSvgCache : public QObject
double widthScaleFactor, double fixedAspectRatio = 0 );
signals:
//! Emit a signal to be caught by qgisapp and display a msg on status bar
void statusChanged( const QString &statusQString );
/**
* Emit a signal to be caught by qgisapp and display a msg on status bar.
* \deprecated Deprecated since QGIS 3.6 -- no longer emitted.
*/
Q_DECL_DEPRECATED void statusChanged( const QString &statusQString ) SIP_DEPRECATED;
/**
* Emitted when the cache has finished retrieving an SVG file from a remote \a url.
@ -234,26 +208,12 @@ class CORE_EXPORT QgsSvgCache : public QObject
*/
void remoteSvgFetched( const QString &url );
private slots:
void downloadProgress( qint64, qint64 );
protected:
void onRemoteSvgFetched( const QString &url, bool success );
bool checkReply( QNetworkReply *reply, const QString &path ) const override;
private:
/**
* Creates new cache entry and returns pointer to it
* \param path Absolute path to SVG file
* \param size size of cached image
* \param fill color of fill
* \param stroke color of stroke
* \param strokeWidth width of stroke
* \param widthScaleFactor width scale factor
* \param fixedAspectRatio fixed aspect ratio (optional)
*/
QgsSvgCacheEntry *insertSvg( const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth,
double widthScaleFactor, double fixedAspectRatio = 0 );
void replaceParamsAndCacheSvg( QgsSvgCacheEntry *entry );
void cacheImage( QgsSvgCacheEntry *entry );
void cachePicture( QgsSvgCacheEntry *entry, bool forceVectorOutput = false );
@ -261,28 +221,6 @@ class CORE_EXPORT QgsSvgCache : public QObject
QgsSvgCacheEntry *cacheEntry( const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth,
double widthScaleFactor, double fixedAspectRatio = 0 );
//! Removes the least used items until the maximum size is under the limit
void trimToMaximumSize();
//Removes entry from the ordered list (but does not delete the entry itself)
void takeEntryFromList( QgsSvgCacheEntry *entry );
//! Minimum time (in ms) between consecutive svg file modified time checks
int mFileModifiedCheckTimeout = 30000;
//! Entry pointers accessible by file name
QMultiHash< QString, QgsSvgCacheEntry * > mEntryLookup;
//! Estimated total size of all images, pictures and svgContent
long mTotalSize = 0;
//The svg cache keeps the entries on a double connected list, moving the current entry to the front.
//That way, removing entries for more space can start with the least used objects.
QgsSvgCacheEntry *mLeastRecentEntry = nullptr;
QgsSvgCacheEntry *mMostRecentEntry = nullptr;
//! Maximum cache size
static const long MAXIMUM_SIZE = 20000000;
//! Replaces parameters in elements of a dom node and calls method for all child nodes
void replaceElemParams( QDomElement &elem, const QColor &fill, const QColor &stroke, double strokeWidth );
@ -296,12 +234,6 @@ class CORE_EXPORT QgsSvgCache : public QObject
//! Calculates scaling for rendered image sizes to SVG logical sizes
double calcSizeScaleFactor( QgsSvgCacheEntry *entry, const QDomElement &docElem, QSizeF &viewboxSize ) const;
//! Release memory and remove cache entry from mEntryLookup
void removeCacheEntry( const QString &s, QgsSvgCacheEntry *entry );
//! For debugging
void printEntryList();
/**
* Returns the target size (in pixels) and calculates the \a viewBoxSize
* for a cache \a entry.
@ -313,19 +245,11 @@ class CORE_EXPORT QgsSvgCache : public QObject
*/
QImage imageFromCachedPicture( const QgsSvgCacheEntry &entry ) const;
QByteArray fetchImageData( const QString &path, bool &ok ) const;
//! SVG content to be rendered if SVG file was not found.
QByteArray mMissingSvg;
QByteArray mFetchingSvg;
//! Mutex to prevent concurrent access to the class from multiple threads at once (may corrupt the entries otherwise).
mutable QMutex mMutex;
mutable QCache< QString, QByteArray > mRemoteContentCache;
mutable QSet< QString > mPendingRemoteUrls;
friend class TestQgsSvgCache;
};