QGIS API Documentation  2.8.6-Wien
qgssvgcache.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgssvgcache.h
3  ------------------------------
4  begin : 2011
5  copyright : (C) 2011 by Marco Hugentobler
6  email : marco dot hugentobler at sourcepole dot ch
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "qgssvgcache.h"
19 #include "qgis.h"
20 #include "qgslogger.h"
22 #include "qgsmessagelog.h"
23 #include "qgssymbollayerv2utils.h"
24 
25 #include <QApplication>
26 #include <QCoreApplication>
27 #include <QCursor>
28 #include <QDomDocument>
29 #include <QDomElement>
30 #include <QFile>
31 #include <QImage>
32 #include <QPainter>
33 #include <QPicture>
34 #include <QSvgRenderer>
35 #include <QFileInfo>
36 #include <QNetworkReply>
37 #include <QNetworkRequest>
38 
40  : file( QString() )
41  , size( 0.0 )
42  , outlineWidth( 0 )
43  , widthScaleFactor( 1.0 )
44  , rasterScaleFactor( 1.0 )
45  , fill( Qt::black )
46  , outline( Qt::black )
47  , image( 0 )
48  , picture( 0 )
49  , nextEntry( 0 )
50  , previousEntry( 0 )
51 {
52 }
53 
54 QgsSvgCacheEntry::QgsSvgCacheEntry( const QString& f, double s, double ow, double wsf, double rsf, const QColor& fi, const QColor& ou, const QString& lk )
55  : file( f )
56  , lookupKey( lk.isEmpty() ? f : lk )
57  , size( s )
58  , outlineWidth( ow )
59  , widthScaleFactor( wsf )
60  , rasterScaleFactor( rsf )
61  , fill( fi )
62  , outline( ou )
63  , image( 0 )
64  , picture( 0 )
65  , nextEntry( 0 )
66  , previousEntry( 0 )
67 {
68 }
69 
70 
72 {
73  delete image;
74  delete picture;
75 }
76 
78 {
79  return other.file == file && other.size == size && other.outlineWidth == outlineWidth && other.widthScaleFactor == widthScaleFactor
80  && other.rasterScaleFactor == rasterScaleFactor && other.fill == fill && other.outline == outline;
81 }
82 
84 {
85  int size = svgContent.size();
86  if ( picture )
87  {
88  size += picture->size();
89  }
90  if ( image )
91  {
92  size += ( image->width() * image->height() * 32 );
93  }
94  return size;
95 }
96 
98 {
99  static QgsSvgCache mInstance;
100  return &mInstance;
101 }
102 
103 QgsSvgCache::QgsSvgCache( QObject *parent )
104  : QObject( parent )
105  , mTotalSize( 0 )
106  , mLeastRecentEntry( 0 )
107  , mMostRecentEntry( 0 )
108 {
109  mMissingSvg = QString( "<svg width='10' height='10'><text x='5' y='10' font-size='10' text-anchor='middle'>?</text></svg>" ).toAscii();
110 }
111 
113 {
114  QMultiHash< QString, QgsSvgCacheEntry* >::iterator it = mEntryLookup.begin();
115  for ( ; it != mEntryLookup.end(); ++it )
116  {
117  delete it.value();
118  }
119 }
120 
121 
122 const QImage& QgsSvgCache::svgAsImage( const QString& file, double size, const QColor& fill, const QColor& outline, double outlineWidth,
123  double widthScaleFactor, double rasterScaleFactor, bool& fitsInCache )
124 {
125  QMutexLocker locker( &mMutex );
126 
127  fitsInCache = true;
128  QgsSvgCacheEntry* currentEntry = cacheEntry( file, size, fill, outline, outlineWidth, widthScaleFactor, rasterScaleFactor );
129 
130  //if current entry image is 0: cache image for entry
131  // checks to see if image will fit into cache
132  //update stats for memory usage
133  if ( !currentEntry->image )
134  {
135  QSvgRenderer r( currentEntry->svgContent );
136  double hwRatio = 1.0;
137  if ( r.viewBoxF().width() > 0 )
138  {
139  hwRatio = r.viewBoxF().height() / r.viewBoxF().width();
140  }
141  long cachedDataSize = 0;
142  cachedDataSize += currentEntry->svgContent.size();
143  cachedDataSize += ( int )( currentEntry->size * currentEntry->size * hwRatio * 32 );
144  if ( cachedDataSize > mMaximumSize / 2 )
145  {
146  fitsInCache = false;
147  delete currentEntry->image;
148  currentEntry->image = 0;
149  //currentEntry->image = new QImage( 0, 0 );
150 
151  // instead cache picture
152  if ( !currentEntry->picture )
153  {
154  cachePicture( currentEntry, false );
155  }
156  }
157  else
158  {
159  cacheImage( currentEntry );
160  }
162  }
163 
164  return *( currentEntry->image );
165 }
166 
167 const QPicture& QgsSvgCache::svgAsPicture( const QString& file, double size, const QColor& fill, const QColor& outline, double outlineWidth,
168  double widthScaleFactor, double rasterScaleFactor, bool forceVectorOutput )
169 {
170  QMutexLocker locker( &mMutex );
171 
172  QgsSvgCacheEntry* currentEntry = cacheEntry( file, size, fill, outline, outlineWidth, widthScaleFactor, rasterScaleFactor );
173 
174  //if current entry picture is 0: cache picture for entry
175  //update stats for memory usage
176  if ( !currentEntry->picture )
177  {
178  cachePicture( currentEntry, forceVectorOutput );
180  }
181 
182  return *( currentEntry->picture );
183 }
184 
185 const QByteArray& QgsSvgCache::svgContent( const QString& file, double size, const QColor& fill, const QColor& outline, double outlineWidth,
186  double widthScaleFactor, double rasterScaleFactor )
187 {
188  QMutexLocker locker( &mMutex );
189 
190  QgsSvgCacheEntry *currentEntry = cacheEntry( file, size, fill, outline, outlineWidth, widthScaleFactor, rasterScaleFactor );
191 
192  return currentEntry->svgContent;
193 }
194 
195 QgsSvgCacheEntry* QgsSvgCache::insertSVG( const QString& file, double size, const QColor& fill, const QColor& outline, double outlineWidth,
196  double widthScaleFactor, double rasterScaleFactor )
197 {
198  // The file may be relative path (e.g. if path is data defined)
199  QString path = QgsSymbolLayerV2Utils::symbolNameToPath( file );
200 
201  QgsSvgCacheEntry* entry = new QgsSvgCacheEntry( path, size, outlineWidth, widthScaleFactor, rasterScaleFactor, fill, outline, file );
202 
203  replaceParamsAndCacheSvg( entry );
204 
205  mEntryLookup.insert( file, entry );
206 
207  //insert to most recent place in entry list
208  if ( !mMostRecentEntry ) //inserting first entry
209  {
210  mLeastRecentEntry = entry;
211  mMostRecentEntry = entry;
212  entry->previousEntry = 0;
213  entry->nextEntry = 0;
214  }
215  else
216  {
217  entry->previousEntry = mMostRecentEntry;
218  entry->nextEntry = 0;
219  mMostRecentEntry->nextEntry = entry;
220  mMostRecentEntry = entry;
221  }
222 
224  return entry;
225 }
226 
227 void QgsSvgCache::containsParams( const QString& path, bool& hasFillParam, QColor& defaultFillColor, bool& hasOutlineParam, QColor& defaultOutlineColor,
228  bool& hasOutlineWidthParam, double& defaultOutlineWidth ) const
229 {
230  hasFillParam = false;
231  hasOutlineParam = false;
232  hasOutlineWidthParam = false;
233  defaultFillColor = QColor( Qt::black );
234  defaultOutlineColor = QColor( Qt::black );
235  defaultOutlineWidth = 1.0;
236 
237  QDomDocument svgDoc;
238  if ( !svgDoc.setContent( getImageData( path ) ) )
239  {
240  return;
241  }
242 
243  QDomElement docElem = svgDoc.documentElement();
244  containsElemParams( docElem, hasFillParam, defaultFillColor, hasOutlineParam, defaultOutlineColor, hasOutlineWidthParam, defaultOutlineWidth );
245 }
246 
248 {
249  if ( !entry )
250  {
251  return;
252  }
253 
254  QDomDocument svgDoc;
255  if ( !svgDoc.setContent( getImageData( entry->file ) ) )
256  {
257  return;
258  }
259 
260  //replace fill color, outline color, outline with in all nodes
261  QDomElement docElem = svgDoc.documentElement();
262  replaceElemParams( docElem, entry->fill, entry->outline, entry->outlineWidth );
263 
264  entry->svgContent = svgDoc.toByteArray();
265  mTotalSize += entry->svgContent.size();
266 }
267 
268 QByteArray QgsSvgCache::getImageData( const QString &path ) const
269 {
270  // is it a path to local file?
271  QFile svgFile( path );
272  if ( svgFile.exists() )
273  {
274  if ( svgFile.open( QIODevice::ReadOnly ) )
275  {
276  return svgFile.readAll();
277  }
278  else
279  {
280  return mMissingSvg;
281  }
282  }
283 
284  // maybe it's a url...
285  if ( !path.contains( "://" ) ) // otherwise short, relative SVG paths might be considered URLs
286  {
287  return mMissingSvg;
288  }
289 
290  QUrl svgUrl( path );
291  if ( !svgUrl.isValid() )
292  {
293  return mMissingSvg;
294  }
295 
296  // check whether it's a url pointing to a local file
297  if ( svgUrl.scheme().compare( "file", Qt::CaseInsensitive ) == 0 )
298  {
299  svgFile.setFileName( svgUrl.toLocalFile() );
300  if ( svgFile.exists() )
301  {
302  if ( svgFile.open( QIODevice::ReadOnly ) )
303  {
304  return svgFile.readAll();
305  }
306  }
307 
308  // not found...
309  return mMissingSvg;
310  }
311 
312  // the url points to a remote resource, download it!
313  QNetworkReply *reply = 0;
314 
315  // The following code blocks until the file is downloaded...
316  // TODO: use signals to get reply finished notification, in this moment
317  // it's executed while rendering.
318  while ( 1 )
319  {
320  QgsDebugMsg( QString( "get svg: %1" ).arg( svgUrl.toString() ) );
321  QNetworkRequest request( svgUrl );
322  request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache );
323  request.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true );
324 
325  reply = QgsNetworkAccessManager::instance()->get( request );
326  connect( reply, SIGNAL( downloadProgress( qint64, qint64 ) ), this, SLOT( downloadProgress( qint64, qint64 ) ) );
327 
328  //emit statusChanged( tr( "Downloading svg." ) );
329 
330  // wait until the image download finished
331  // TODO: connect to the reply->finished() signal
332  while ( !reply->isFinished() )
333  {
334  QCoreApplication::processEvents( QEventLoop::ExcludeUserInputEvents, 500 );
335  }
336 
337  if ( reply->error() != QNetworkReply::NoError )
338  {
339  QgsMessageLog::logMessage( tr( "SVG request failed [error: %1 - url: %2]" ).arg( reply->errorString() ).arg( reply->url().toString() ), tr( "SVG" ) );
340 
341  reply->deleteLater();
342  return QByteArray();
343  }
344 
345  QVariant redirect = reply->attribute( QNetworkRequest::RedirectionTargetAttribute );
346  if ( redirect.isNull() )
347  {
348  // neither network error nor redirection
349  // TODO: cache the image
350  break;
351  }
352 
353  // do a new request to the redirect url
354  svgUrl = redirect.toUrl();
355  reply->deleteLater();
356  }
357 
358  QVariant status = reply->attribute( QNetworkRequest::HttpStatusCodeAttribute );
359  if ( !status.isNull() && status.toInt() >= 400 )
360  {
361  QVariant phrase = reply->attribute( QNetworkRequest::HttpReasonPhraseAttribute );
362  QgsMessageLog::logMessage( tr( "SVG request error [status: %1 - reason phrase: %2]" ).arg( status.toInt() ).arg( phrase.toString() ), tr( "SVG" ) );
363 
364  reply->deleteLater();
365  return mMissingSvg;
366  }
367 
368  QString contentType = reply->header( QNetworkRequest::ContentTypeHeader ).toString();
369  QgsDebugMsg( "contentType: " + contentType );
370  if ( !contentType.startsWith( "image/svg+xml", Qt::CaseInsensitive ) )
371  {
372  reply->deleteLater();
373  return mMissingSvg;
374  }
375 
376  // read the image data
377  QByteArray ba = reply->readAll();
378  reply->deleteLater();
379 
380  return ba;
381 }
382 
384 {
385  if ( !entry )
386  {
387  return;
388  }
389 
390  delete entry->image;
391  entry->image = 0;
392 
393  QSvgRenderer r( entry->svgContent );
394  double hwRatio = 1.0;
395  if ( r.viewBoxF().width() > 0 )
396  {
397  hwRatio = r.viewBoxF().height() / r.viewBoxF().width();
398  }
399  double wSize = entry->size;
400  int wImgSize = ( int )wSize;
401  if ( wImgSize < 1 )
402  {
403  wImgSize = 1;
404  }
405  double hSize = wSize * hwRatio;
406  int hImgSize = ( int )hSize;
407  if ( hImgSize < 1 )
408  {
409  hImgSize = 1;
410  }
411  // cast double image sizes to int for QImage
412  QImage* image = new QImage( wImgSize, hImgSize, QImage::Format_ARGB32_Premultiplied );
413  image->fill( 0 ); // transparent background
414 
415  QPainter p( image );
416  if ( r.viewBoxF().width() == r.viewBoxF().height() )
417  {
418  r.render( &p );
419  }
420  else
421  {
422  QSizeF s( r.viewBoxF().size() );
423  s.scale( wSize, hSize, Qt::KeepAspectRatio );
424  QRectF rect(( wImgSize - s.width() ) / 2, ( hImgSize - s.height() ) / 2, s.width(), s.height() );
425  r.render( &p, rect );
426  }
427 
428  entry->image = image;
429  mTotalSize += ( image->width() * image->height() * 32 );
430 }
431 
432 void QgsSvgCache::cachePicture( QgsSvgCacheEntry *entry, bool forceVectorOutput )
433 {
434  Q_UNUSED( forceVectorOutput );
435  if ( !entry )
436  {
437  return;
438  }
439 
440  delete entry->picture;
441  entry->picture = 0;
442 
443  //correct QPictures dpi correction
444  QPicture* picture = new QPicture();
445  QRectF rect;
446  QSvgRenderer r( entry->svgContent );
447  double hwRatio = 1.0;
448  if ( r.viewBoxF().width() > 0 )
449  {
450  hwRatio = r.viewBoxF().height() / r.viewBoxF().width();
451  }
452 
453  double wSize = entry->size;
454  double hSize = wSize * hwRatio;
455  QSizeF s( r.viewBoxF().size() );
456  s.scale( wSize, hSize, Qt::KeepAspectRatio );
457  rect = QRectF( -s.width() / 2.0, -s.height() / 2.0, s.width(), s.height() );
458 
459  QPainter p( picture );
460  r.render( &p, rect );
461  entry->picture = picture;
462  mTotalSize += entry->picture->size();
463 }
464 
465 QgsSvgCacheEntry* QgsSvgCache::cacheEntry( const QString& file, double size, const QColor& fill, const QColor& outline, double outlineWidth,
466  double widthScaleFactor, double rasterScaleFactor )
467 {
468  //search entries in mEntryLookup
469  QgsSvgCacheEntry* currentEntry = 0;
470  QList<QgsSvgCacheEntry*> entries = mEntryLookup.values( file );
471 
472  QList<QgsSvgCacheEntry*>::iterator entryIt = entries.begin();
473  for ( ; entryIt != entries.end(); ++entryIt )
474  {
475  QgsSvgCacheEntry* cacheEntry = *entryIt;
476  if ( qgsDoubleNear( cacheEntry->size, size ) && cacheEntry->fill == fill && cacheEntry->outline == outline &&
477  cacheEntry->outlineWidth == outlineWidth && cacheEntry->widthScaleFactor == widthScaleFactor && cacheEntry->rasterScaleFactor == rasterScaleFactor )
478  {
479  currentEntry = cacheEntry;
480  break;
481  }
482  }
483 
484  //if not found: create new entry
485  //cache and replace params in svg content
486  if ( !currentEntry )
487  {
488  currentEntry = insertSVG( file, size, fill, outline, outlineWidth, widthScaleFactor, rasterScaleFactor );
489  }
490  else
491  {
492  takeEntryFromList( currentEntry );
493  if ( !mMostRecentEntry ) //list is empty
494  {
495  mMostRecentEntry = currentEntry;
496  mLeastRecentEntry = currentEntry;
497  }
498  else
499  {
500  mMostRecentEntry->nextEntry = currentEntry;
501  currentEntry->previousEntry = mMostRecentEntry;
502  currentEntry->nextEntry = 0;
503  mMostRecentEntry = currentEntry;
504  }
505  }
506 
507  //debugging
508  //printEntryList();
509 
510  return currentEntry;
511 }
512 
513 void QgsSvgCache::replaceElemParams( QDomElement& elem, const QColor& fill, const QColor& outline, double outlineWidth )
514 {
515  if ( elem.isNull() )
516  {
517  return;
518  }
519 
520  //go through attributes
521  QDomNamedNodeMap attributes = elem.attributes();
522  int nAttributes = attributes.count();
523  for ( int i = 0; i < nAttributes; ++i )
524  {
525  QDomAttr attribute = attributes.item( i ).toAttr();
526  //e.g. style="fill:param(fill);param(stroke)"
527  if ( attribute.name().compare( "style", Qt::CaseInsensitive ) == 0 )
528  {
529  //entries separated by ';'
530  QString newAttributeString;
531 
532  QStringList entryList = attribute.value().split( ';' );
533  QStringList::const_iterator entryIt = entryList.constBegin();
534  for ( ; entryIt != entryList.constEnd(); ++entryIt )
535  {
536  QStringList keyValueSplit = entryIt->split( ':' );
537  if ( keyValueSplit.size() < 2 )
538  {
539  continue;
540  }
541  QString key = keyValueSplit.at( 0 );
542  QString value = keyValueSplit.at( 1 );
543  if ( value.startsWith( "param(fill" ) )
544  {
545  value = fill.name();
546  }
547  else if ( value.startsWith( "param(outline)" ) )
548  {
549  value = outline.name();
550  }
551  else if ( value.startsWith( "param(outline-width)" ) )
552  {
553  value = QString::number( outlineWidth );
554  }
555 
556  if ( entryIt != entryList.constBegin() )
557  {
558  newAttributeString.append( ";" );
559  }
560  newAttributeString.append( key + ":" + value );
561  }
562  elem.setAttribute( attribute.name(), newAttributeString );
563  }
564  else
565  {
566  QString value = attribute.value();
567  if ( value.startsWith( "param(fill)" ) )
568  {
569  elem.setAttribute( attribute.name(), fill.name() );
570  }
571  else if ( value.startsWith( "param(outline)" ) )
572  {
573  elem.setAttribute( attribute.name(), outline.name() );
574  }
575  else if ( value.startsWith( "param(outline-width)" ) )
576  {
577  elem.setAttribute( attribute.name(), QString::number( outlineWidth ) );
578  }
579  }
580  }
581 
582  QDomNodeList childList = elem.childNodes();
583  int nChildren = childList.count();
584  for ( int i = 0; i < nChildren; ++i )
585  {
586  QDomElement childElem = childList.at( i ).toElement();
587  replaceElemParams( childElem, fill, outline, outlineWidth );
588  }
589 }
590 
591 void QgsSvgCache::containsElemParams( const QDomElement& elem, bool& hasFillParam, QColor& defaultFill, bool& hasOutlineParam, QColor& defaultOutline,
592  bool& hasOutlineWidthParam, double& defaultOutlineWidth ) const
593 {
594  if ( elem.isNull() )
595  {
596  return;
597  }
598 
599  //we already have all the information, no need to go deeper
600  if ( hasFillParam && hasOutlineParam && hasOutlineWidthParam )
601  {
602  return;
603  }
604 
605  //check this elements attribute
606  QDomNamedNodeMap attributes = elem.attributes();
607  int nAttributes = attributes.count();
608 
609  QStringList valueSplit;
610  for ( int i = 0; i < nAttributes; ++i )
611  {
612  QDomAttr attribute = attributes.item( i ).toAttr();
613  if ( attribute.name().compare( "style", Qt::CaseInsensitive ) == 0 )
614  {
615  //entries separated by ';'
616  QStringList entryList = attribute.value().split( ';' );
617  QStringList::const_iterator entryIt = entryList.constBegin();
618  for ( ; entryIt != entryList.constEnd(); ++entryIt )
619  {
620  QStringList keyValueSplit = entryIt->split( ':' );
621  if ( keyValueSplit.size() < 2 )
622  {
623  continue;
624  }
625  QString key = keyValueSplit.at( 0 );
626  QString value = keyValueSplit.at( 1 );
627  valueSplit = value.split( " " );
628  if ( !hasFillParam && value.startsWith( "param(fill)" ) )
629  {
630  hasFillParam = true;
631  if ( valueSplit.size() > 1 )
632  {
633  defaultFill = QColor( valueSplit.at( 1 ) );
634  }
635  }
636  else if ( !hasOutlineParam && value.startsWith( "param(outline)" ) )
637  {
638  hasOutlineParam = true;
639  if ( valueSplit.size() > 1 )
640  {
641  defaultOutline = QColor( valueSplit.at( 1 ) );
642  }
643  }
644  else if ( !hasOutlineWidthParam && value.startsWith( "param(outline-width)" ) )
645  {
646  hasOutlineWidthParam = true;
647  if ( valueSplit.size() > 1 )
648  {
649  defaultOutlineWidth = valueSplit.at( 1 ).toDouble();
650  }
651  }
652  }
653  }
654  else
655  {
656  QString value = attribute.value();
657  valueSplit = value.split( " " );
658  if ( !hasFillParam && value.startsWith( "param(fill)" ) )
659  {
660  hasFillParam = true;
661  if ( valueSplit.size() > 1 )
662  {
663  defaultFill = QColor( valueSplit.at( 1 ) );
664  }
665  }
666  else if ( !hasOutlineParam && value.startsWith( "param(outline)" ) )
667  {
668  hasOutlineParam = true;
669  if ( valueSplit.size() > 1 )
670  {
671  defaultOutline = QColor( valueSplit.at( 1 ) );
672  }
673  }
674  else if ( !hasOutlineWidthParam && value.startsWith( "param(outline-width)" ) )
675  {
676  hasOutlineWidthParam = true;
677  if ( valueSplit.size() > 1 )
678  {
679  defaultOutlineWidth = valueSplit.at( 1 ).toDouble();
680  }
681  }
682  }
683  }
684 
685  //pass it further to child items
686  QDomNodeList childList = elem.childNodes();
687  int nChildren = childList.count();
688  for ( int i = 0; i < nChildren; ++i )
689  {
690  QDomElement childElem = childList.at( i ).toElement();
691  containsElemParams( childElem, hasFillParam, defaultFill, hasOutlineParam, defaultOutline, hasOutlineWidthParam, defaultOutlineWidth );
692  }
693 }
694 
695 void QgsSvgCache::removeCacheEntry( QString s, QgsSvgCacheEntry* entry )
696 {
697  delete entry;
698  mEntryLookup.remove( s, entry );
699 }
700 
701 void QgsSvgCache::printEntryList()
702 {
703  QgsDebugMsg( "****************svg cache entry list*************************" );
704  QgsDebugMsg( "Cache size: " + QString::number( mTotalSize ) );
705  QgsSvgCacheEntry* entry = mLeastRecentEntry;
706  while ( entry )
707  {
708  QgsDebugMsg( "***Entry:" );
709  QgsDebugMsg( "File:" + entry->file );
710  QgsDebugMsg( "Size:" + QString::number( entry->size ) );
711  QgsDebugMsg( "Width scale factor" + QString::number( entry->widthScaleFactor ) );
712  QgsDebugMsg( "Raster scale factor" + QString::number( entry->rasterScaleFactor ) );
713  entry = entry->nextEntry;
714  }
715 }
716 
718 {
719  //only one entry in cache
720  if ( mLeastRecentEntry == mMostRecentEntry )
721  {
722  return;
723  }
724  QgsSvgCacheEntry* entry = mLeastRecentEntry;
725  while ( entry && ( mTotalSize > mMaximumSize ) )
726  {
727  QgsSvgCacheEntry* bkEntry = entry;
728  entry = entry->nextEntry;
729 
730  takeEntryFromList( bkEntry );
731  mEntryLookup.remove( bkEntry->lookupKey, bkEntry );
732  mTotalSize -= bkEntry->dataSize();
733  delete bkEntry;
734  }
735 }
736 
738 {
739  if ( !entry )
740  {
741  return;
742  }
743 
744  if ( entry->previousEntry )
745  {
746  entry->previousEntry->nextEntry = entry->nextEntry;
747  }
748  else
749  {
750  mLeastRecentEntry = entry->nextEntry;
751  }
752  if ( entry->nextEntry )
753  {
754  entry->nextEntry->previousEntry = entry->previousEntry;
755  }
756  else
757  {
758  mMostRecentEntry = entry->previousEntry;
759  }
760 }
761 
762 void QgsSvgCache::downloadProgress( qint64 bytesReceived, qint64 bytesTotal )
763 {
764  QString msg = tr( "%1 of %2 bytes of svg image downloaded." ).arg( bytesReceived ).arg( bytesTotal < 0 ? QString( "unknown number of" ) : QString::number( bytesTotal ) );
765  QgsDebugMsg( msg );
766  emit statusChanged( msg );
767 }
QgsSvgCacheEntry * previousEntry
Definition: qgssvgcache.h:66
int dataSize() const
Return memory usage in bytes.
Definition: qgssvgcache.cpp:83
QImage * image
Definition: qgssvgcache.h:59
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&.
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.
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
A cache for images / pictures derived from svg files.
Definition: qgssvgcache.h:77
static QgsNetworkAccessManager * instance()
Definition: qgssingleton.h:23
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Definition: qgis.h:330
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()
Definition: qgssvgcache.cpp:97
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.
QPicture * picture
Definition: qgssvgcache.h:60
QByteArray getImageData(const QString &path) const
Get image data.
static QString symbolNameToPath(QString name)
Get symbol&#39;s path from its name.
void statusChanged(const QString &theStatusQString)
Emit a signal to be caught by qgisapp and display a msg on status bar.
const QByteArray & svgContent(const QString &file, double size, const QColor &fill, const QColor &outline, double outlineWidth, double widthScaleFactor, double rasterScaleFactor)
Get SVG content.
void cachePicture(QgsSvgCacheEntry *entry, bool forceVectorOutput=false)
void trimToMaximumSize()
Removes the least used items until the maximum size is under the limit.
void takeEntryFromList(QgsSvgCacheEntry *entry)
QString lookupKey
Lookup key used by QgsSvgCache&#39;s hashtable (relative or absolute path). Needed for removal from the h...
Definition: qgssvgcache.h:52
void cacheImage(QgsSvgCacheEntry *entry)
void replaceParamsAndCacheSvg(QgsSvgCacheEntry *entry)
double outlineWidth
Definition: qgssvgcache.h:54
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.
double rasterScaleFactor
Definition: qgssvgcache.h:56
double widthScaleFactor
Definition: qgssvgcache.h:55
QString file
Absolute path to SVG file.
Definition: qgssvgcache.h:50
QByteArray svgContent
Definition: qgssvgcache.h:62
bool operator==(const QgsSvgCacheEntry &other) const
Don&#39;t consider image, picture, last used timestamp for comparison.
Definition: qgssvgcache.cpp:77
QgsSvgCacheEntry * nextEntry
Definition: qgssvgcache.h:65
#define tr(sourceText)