25 #include <QApplication>
26 #include <QCoreApplication>
28 #include <QDomDocument>
29 #include <QDomElement>
34 #include <QSvgRenderer>
36 #include <QNetworkReply>
37 #include <QNetworkRequest>
40 outline( Qt::black ), image( 0 ), picture( 0 )
71 size += (
image->width() *
image->height() * 32 );
93 , mLeastRecentEntry( 0 )
94 , mMostRecentEntry( 0 )
96 mMissingSvg = QString(
"<svg width='10' height='10'><text x='5' y='10' font-size='10' text-anchor='middle'>?</text></svg>" ).toAscii();
101 QMultiHash< QString, QgsSvgCacheEntry* >::iterator it =
mEntryLookup.begin();
112 QMutexLocker locker( &
mMutex );
120 if ( !currentEntry->
image )
123 double hwRatio = 1.0;
124 if ( r.viewBoxF().width() > 0 )
126 hwRatio = r.viewBoxF().height() / r.viewBoxF().width();
128 long cachedDataSize = 0;
129 cachedDataSize += currentEntry->
svgContent.size();
130 cachedDataSize += ( int )( currentEntry->
size * currentEntry->
size * hwRatio * 32 );
134 delete currentEntry->
image;
135 currentEntry->
image = 0;
151 return *( currentEntry->
image );
157 QMutexLocker locker( &
mMutex );
169 return *( currentEntry->
picture );
204 void QgsSvgCache::containsParams(
const QString& path,
bool& hasFillParam, QColor& defaultFillColor,
bool& hasOutlineParam, QColor& defaultOutlineColor,
205 bool& hasOutlineWidthParam,
double& defaultOutlineWidth )
const
207 defaultFillColor = QColor( Qt::black );
208 defaultOutlineColor = QColor( Qt::black );
209 defaultOutlineWidth = 1.0;
217 QDomElement docElem = svgDoc.documentElement();
218 containsElemParams( docElem, hasFillParam, defaultFillColor, hasOutlineParam, defaultOutlineColor, hasOutlineWidthParam, defaultOutlineWidth );
235 QDomElement docElem = svgDoc.documentElement();
245 QFile svgFile( path );
246 if ( svgFile.exists() )
248 if ( svgFile.open( QIODevice::ReadOnly ) )
250 return svgFile.readAll();
259 if ( !path.contains(
"://" ) )
265 if ( !svgUrl.isValid() )
271 if ( svgUrl.scheme().compare(
"file", Qt::CaseInsensitive ) == 0 )
273 svgFile.setFileName( svgUrl.toLocalFile() );
274 if ( svgFile.exists() )
276 if ( svgFile.open( QIODevice::ReadOnly ) )
278 return svgFile.readAll();
287 QNetworkReply *reply = 0;
294 QgsDebugMsg( QString(
"get svg: %1" ).arg( svgUrl.toString() ) );
295 QNetworkRequest request( svgUrl );
296 request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache );
297 request.setAttribute( QNetworkRequest::CacheSaveControlAttribute,
true );
306 while ( !reply->isFinished() )
308 QCoreApplication::processEvents( QEventLoop::ExcludeUserInputEvents, 500 );
311 if ( reply->error() != QNetworkReply::NoError )
313 QgsMessageLog::logMessage(
tr(
"SVG request failed [error: %1 - url: %2]" ).arg( reply->errorString() ).arg( reply->url().toString() ),
tr(
"SVG" ) );
315 reply->deleteLater();
319 QVariant redirect = reply->attribute( QNetworkRequest::RedirectionTargetAttribute );
320 if ( redirect.isNull() )
328 svgUrl = redirect.toUrl();
329 reply->deleteLater();
332 QVariant status = reply->attribute( QNetworkRequest::HttpStatusCodeAttribute );
333 if ( !status.isNull() && status.toInt() >= 400 )
335 QVariant phrase = reply->attribute( QNetworkRequest::HttpReasonPhraseAttribute );
336 QgsMessageLog::logMessage(
tr(
"SVG request error [status: %1 - reason phrase: %2]" ).arg( status.toInt() ).arg( phrase.toString() ),
tr(
"SVG" ) );
338 reply->deleteLater();
342 QString contentType = reply->header( QNetworkRequest::ContentTypeHeader ).toString();
344 if ( !contentType.startsWith(
"image/svg+xml", Qt::CaseInsensitive ) )
346 reply->deleteLater();
351 QByteArray ba = reply->readAll();
352 reply->deleteLater();
368 double hwRatio = 1.0;
369 if ( r.viewBoxF().width() > 0 )
371 hwRatio = r.viewBoxF().height() / r.viewBoxF().width();
373 double wSize = entry->
size;
374 int wImgSize = ( int )wSize;
379 double hSize = wSize * hwRatio;
380 int hImgSize = ( int )hSize;
386 QImage* image =
new QImage( wImgSize, hImgSize, QImage::Format_ARGB32_Premultiplied );
390 if ( r.viewBoxF().width() == r.viewBoxF().height() )
396 QSizeF s( r.viewBoxF().size() );
397 s.scale( wSize, hSize, Qt::KeepAspectRatio );
398 QRectF rect(( wImgSize - s.width() ) / 2, ( hImgSize - s.height() ) / 2, s.width(), s.height() );
399 r.render( &p, rect );
402 entry->
image = image;
403 mTotalSize += ( image->width() * image->height() * 32 );
408 Q_UNUSED( forceVectorOutput );
418 QPicture* picture =
new QPicture();
421 double hwRatio = 1.0;
422 if ( r.viewBoxF().width() > 0 )
424 hwRatio = r.viewBoxF().height() / r.viewBoxF().width();
427 double wSize = entry->
size;
428 double hSize = wSize * hwRatio;
429 QSizeF s( r.viewBoxF().size() );
430 s.scale( wSize, hSize, Qt::KeepAspectRatio );
431 rect = QRectF( -s.width() / 2.0, -s.height() / 2.0, s.width(), s.height() );
433 QPainter p( picture );
434 r.render( &p, rect );
444 QList<QgsSvgCacheEntry*> entries =
mEntryLookup.values( file );
446 QList<QgsSvgCacheEntry*>::iterator entryIt = entries.begin();
447 for ( ; entryIt != entries.end(); ++entryIt )
462 currentEntry =
insertSVG( file, size, fill, outline, outlineWidth, widthScaleFactor, rasterScaleFactor );
495 QDomNamedNodeMap attributes = elem.attributes();
496 int nAttributes = attributes.count();
497 for (
int i = 0; i < nAttributes; ++i )
499 QDomAttr attribute = attributes.item( i ).toAttr();
501 if ( attribute.name().compare(
"style", Qt::CaseInsensitive ) == 0 )
504 QString newAttributeString;
506 QStringList entryList = attribute.value().split(
';' );
507 QStringList::const_iterator entryIt = entryList.constBegin();
508 for ( ; entryIt != entryList.constEnd(); ++entryIt )
510 QStringList keyValueSplit = entryIt->split(
':' );
511 if ( keyValueSplit.size() < 2 )
515 QString key = keyValueSplit.at( 0 );
516 QString value = keyValueSplit.at( 1 );
517 if ( value.startsWith(
"param(fill" ) )
521 else if ( value.startsWith(
"param(outline)" ) )
523 value = outline.name();
525 else if ( value.startsWith(
"param(outline-width)" ) )
527 value = QString::number( outlineWidth );
530 if ( entryIt != entryList.constBegin() )
532 newAttributeString.append(
";" );
534 newAttributeString.append( key +
":" + value );
536 elem.setAttribute( attribute.name(), newAttributeString );
540 QString value = attribute.value();
541 if ( value.startsWith(
"param(fill)" ) )
543 elem.setAttribute( attribute.name(), fill.name() );
545 else if ( value.startsWith(
"param(outline)" ) )
547 elem.setAttribute( attribute.name(), outline.name() );
549 else if ( value.startsWith(
"param(outline-width)" ) )
551 elem.setAttribute( attribute.name(), QString::number( outlineWidth ) );
556 QDomNodeList childList = elem.childNodes();
557 int nChildren = childList.count();
558 for (
int i = 0; i < nChildren; ++i )
560 QDomElement childElem = childList.at( i ).toElement();
566 bool& hasOutlineWidthParam,
double& defaultOutlineWidth )
const
574 if ( hasFillParam && hasOutlineParam && hasOutlineWidthParam )
580 QDomNamedNodeMap attributes = elem.attributes();
581 int nAttributes = attributes.count();
583 QStringList valueSplit;
584 for (
int i = 0; i < nAttributes; ++i )
586 QDomAttr attribute = attributes.item( i ).toAttr();
587 if ( attribute.name().compare(
"style", Qt::CaseInsensitive ) == 0 )
590 QStringList entryList = attribute.value().split(
';' );
591 QStringList::const_iterator entryIt = entryList.constBegin();
592 for ( ; entryIt != entryList.constEnd(); ++entryIt )
594 QStringList keyValueSplit = entryIt->split(
':' );
595 if ( keyValueSplit.size() < 2 )
599 QString key = keyValueSplit.at( 0 );
600 QString value = keyValueSplit.at( 1 );
601 valueSplit = value.split(
" " );
602 if ( !hasFillParam && value.startsWith(
"param(fill)" ) )
605 if ( valueSplit.size() > 1 )
607 defaultFill = QColor( valueSplit.at( 1 ) );
610 else if ( !hasOutlineParam && value.startsWith(
"param(outline)" ) )
612 hasOutlineParam =
true;
613 if ( valueSplit.size() > 1 )
615 defaultOutline = QColor( valueSplit.at( 1 ) );
618 else if ( !hasOutlineWidthParam && value.startsWith(
"param(outline-width)" ) )
620 hasOutlineWidthParam =
true;
621 if ( valueSplit.size() > 1 )
623 defaultOutlineWidth = valueSplit.at( 1 ).toDouble();
630 QString value = attribute.value();
631 valueSplit = value.split(
" " );
632 if ( !hasFillParam && value.startsWith(
"param(fill)" ) )
635 if ( valueSplit.size() > 1 )
637 defaultFill = QColor( valueSplit.at( 1 ) );
640 else if ( !hasOutlineParam && value.startsWith(
"param(outline)" ) )
642 hasOutlineParam =
true;
643 if ( valueSplit.size() > 1 )
645 defaultOutline = QColor( valueSplit.at( 1 ) );
648 else if ( !hasOutlineWidthParam && value.startsWith(
"param(outline-width)" ) )
650 hasOutlineWidthParam =
true;
651 if ( valueSplit.size() > 1 )
653 defaultOutlineWidth = valueSplit.at( 1 ).toDouble();
660 QDomNodeList childList = elem.childNodes();
661 int nChildren = childList.count();
662 for (
int i = 0; i < nChildren; ++i )
664 QDomElement childElem = childList.at( i ).toElement();
665 containsElemParams( childElem, hasFillParam, defaultFill, hasOutlineParam, defaultOutline, hasOutlineWidthParam, defaultOutlineWidth );
677 QgsDebugMsg(
"****************svg cache entry list*************************" );
738 QString msg =
tr(
"%1 of %2 bytes of svg image downloaded." ).arg( bytesReceived ).arg( bytesTotal < 0 ? QString(
"unknown number of" ) : QString::number( bytesTotal ) );
QgsSvgCacheEntry * previousEntry
int dataSize() const
Return memory usage in bytes.
const QPicture & svgAsPicture(const QString &file, double size, const QColor &fill, const QColor &outline, double outlineWidth, double widthScaleFactor, double rasterScaleFactor, bool forceVectorOutput=false)
Get SVG as QPicture&.
void containsElemParams(const QDomElement &elem, bool &hasFillParam, QColor &defaultFill, bool &hasOutlineParam, QColor &defaultOutline, bool &hasOutlineWidthParam, double &defaultOutlineWidth) const
QgsSvgCacheEntry * cacheEntry(const QString &file, double size, const QColor &fill, const QColor &outline, double outlineWidth, double widthScaleFactor, double rasterScaleFactor)
Returns entry from cache or creates a new entry if it does not exist already.
A cache for images / pictures derived from svg files.
void replaceElemParams(QDomElement &elem, const QColor &fill, const QColor &outline, double outlineWidth)
Replaces parameters in elements of a dom node and calls method for all child nodes.
QgsSvgCacheEntry * mLeastRecentEntry
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
static void logMessage(QString message, QString tag=QString::null, MessageLevel level=WARNING)
add a message to the instance (and create it if necessary)
void containsParams(const QString &path, bool &hasFillParam, QColor &defaultFillColor, bool &hasOutlineParam, QColor &defaultOutlineColor, bool &hasOutlineWidthParam, double &defaultOutlineWidth) const
Tests if an svg file contains parameters for fill, outline color, outline width.
static QgsSvgCache * instance()
QByteArray mMissingSvg
SVG content to be rendered if SVG file was not found.
const QImage & svgAsImage(const QString &file, double size, const QColor &fill, const QColor &outline, double outlineWidth, double widthScaleFactor, double rasterScaleFactor, bool &fitsInCache)
Get SVG as QImage.
QByteArray getImageData(const QString &path) const
Get image data.
void printEntryList()
For debugging.
void removeCacheEntry(QString s, QgsSvgCacheEntry *entry)
Release memory and remove cache entry from mEntryLookup.
static QString symbolNameToPath(QString name)
Get symbol's path from its name.
void downloadProgress(qint64, qint64)
void statusChanged(const QString &theStatusQString)
Emit a signal to be caught by qgisapp and display a msg on status bar.
void cachePicture(QgsSvgCacheEntry *entry, bool forceVectorOutput=false)
QgsSvgCacheEntry * mMostRecentEntry
long mTotalSize
Estimated total size of all images, pictures and svgContent.
void trimToMaximumSize()
Removes the least used items until the maximum size is under the limit.
void takeEntryFromList(QgsSvgCacheEntry *entry)
static const long mMaximumSize
void cacheImage(QgsSvgCacheEntry *entry)
void replaceParamsAndCacheSvg(QgsSvgCacheEntry *entry)
static QgsNetworkAccessManager * instance()
returns a pointer to the single instance
QgsSvgCache(QObject *parent=0)
protected constructor
QgsSvgCacheEntry * insertSVG(const QString &file, double size, const QColor &fill, const QColor &outline, double outlineWidth, double widthScaleFactor, double rasterScaleFactor)
Creates new cache entry and returns pointer to it.
QMutex mMutex
Mutex to prevent concurrent access to the class from multiple threads at once (may corrupt the entrie...
QMultiHash< QString, QgsSvgCacheEntry * > mEntryLookup
Entry pointers accessible by file name.
bool operator==(const QgsSvgCacheEntry &other) const
Don't consider image, picture, last used timestamp for comparison.
QgsSvgCacheEntry * nextEntry