mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-13 00:03:09 -04:00
Use QgsAbstractContentCache base class for QgsSvgCache
This commit is contained in:
parent
22496dc139
commit
cdba8f5621
@ -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;
|
||||
|
||||
|
||||
};
|
||||
|
||||
/************************************************************************
|
||||
|
@ -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 );
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user