QGIS API Documentation  2.11.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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 
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)
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  defaultFillColor = QColor( Qt::black );
231  defaultOutlineColor = QColor( Qt::black );
232  defaultOutlineWidth = 1.0;
233 
234  QDomDocument svgDoc;
235  if ( !svgDoc.setContent( getImageData( path ) ) )
236  {
237  return;
238  }
239 
240  QDomElement docElem = svgDoc.documentElement();
241  containsElemParams( docElem, hasFillParam, defaultFillColor, hasOutlineParam, defaultOutlineColor, hasOutlineWidthParam, defaultOutlineWidth );
242 }
243 
245 {
246  if ( !entry )
247  {
248  return;
249  }
250 
251  QDomDocument svgDoc;
252  if ( !svgDoc.setContent( getImageData( entry->file ) ) )
253  {
254  return;
255  }
256 
257  //replace fill color, outline color, outline with in all nodes
258  QDomElement docElem = svgDoc.documentElement();
259  replaceElemParams( docElem, entry->fill, entry->outline, entry->outlineWidth );
260 
261  entry->svgContent = svgDoc.toByteArray();
262  mTotalSize += entry->svgContent.size();
263 }
264 
266 {
267  // is it a path to local file?
268  QFile svgFile( path );
269  if ( svgFile.exists() )
270  {
271  if ( svgFile.open( QIODevice::ReadOnly ) )
272  {
273  return svgFile.readAll();
274  }
275  else
276  {
277  return mMissingSvg;
278  }
279  }
280 
281  // maybe it's a url...
282  if ( !path.contains( "://" ) ) // otherwise short, relative SVG paths might be considered URLs
283  {
284  return mMissingSvg;
285  }
286 
287  QUrl svgUrl( path );
288  if ( !svgUrl.isValid() )
289  {
290  return mMissingSvg;
291  }
292 
293  // check whether it's a url pointing to a local file
294  if ( svgUrl.scheme().compare( "file", Qt::CaseInsensitive ) == 0 )
295  {
296  svgFile.setFileName( svgUrl.toLocalFile() );
297  if ( svgFile.exists() )
298  {
299  if ( svgFile.open( QIODevice::ReadOnly ) )
300  {
301  return svgFile.readAll();
302  }
303  }
304 
305  // not found...
306  return mMissingSvg;
307  }
308 
309  // the url points to a remote resource, download it!
310  QNetworkReply *reply = 0;
311 
312  // The following code blocks until the file is downloaded...
313  // TODO: use signals to get reply finished notification, in this moment
314  // it's executed while rendering.
315  while ( 1 )
316  {
317  QgsDebugMsg( QString( "get svg: %1" ).arg( svgUrl.toString() ) );
318  QNetworkRequest request( svgUrl );
319  request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache );
320  request.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true );
321 
322  reply = QgsNetworkAccessManager::instance()->get( request );
323  connect( reply, SIGNAL( downloadProgress( qint64, qint64 ) ), this, SLOT( downloadProgress( qint64, qint64 ) ) );
324 
325  //emit statusChanged( tr( "Downloading svg." ) );
326 
327  // wait until the image download finished
328  // TODO: connect to the reply->finished() signal
329  while ( !reply->isFinished() )
330  {
331  QCoreApplication::processEvents( QEventLoop::ExcludeUserInputEvents, 500 );
332  }
333 
334  if ( reply->error() != QNetworkReply::NoError )
335  {
336  QgsMessageLog::logMessage( tr( "SVG request failed [error: %1 - url: %2]" ).arg( reply->errorString() ).arg( reply->url().toString() ), tr( "SVG" ) );
337 
338  reply->deleteLater();
339  return QByteArray();
340  }
341 
342  QVariant redirect = reply->attribute( QNetworkRequest::RedirectionTargetAttribute );
343  if ( redirect.isNull() )
344  {
345  // neither network error nor redirection
346  // TODO: cache the image
347  break;
348  }
349 
350  // do a new request to the redirect url
351  svgUrl = redirect.toUrl();
352  reply->deleteLater();
353  }
354 
355  QVariant status = reply->attribute( QNetworkRequest::HttpStatusCodeAttribute );
356  if ( !status.isNull() && status.toInt() >= 400 )
357  {
358  QVariant phrase = reply->attribute( QNetworkRequest::HttpReasonPhraseAttribute );
359  QgsMessageLog::logMessage( tr( "SVG request error [status: %1 - reason phrase: %2]" ).arg( status.toInt() ).arg( phrase.toString() ), tr( "SVG" ) );
360 
361  reply->deleteLater();
362  return mMissingSvg;
363  }
364 
365  QString contentType = reply->header( QNetworkRequest::ContentTypeHeader ).toString();
366  QgsDebugMsg( "contentType: " + contentType );
367  if ( !contentType.startsWith( "image/svg+xml", Qt::CaseInsensitive ) )
368  {
369  reply->deleteLater();
370  return mMissingSvg;
371  }
372 
373  // read the image data
374  QByteArray ba = reply->readAll();
375  reply->deleteLater();
376 
377  return ba;
378 }
379 
381 {
382  if ( !entry )
383  {
384  return;
385  }
386 
387  delete entry->image;
388  entry->image = 0;
389 
390  QSvgRenderer r( entry->svgContent );
391  double hwRatio = 1.0;
392  if ( r.viewBoxF().width() > 0 )
393  {
394  hwRatio = r.viewBoxF().height() / r.viewBoxF().width();
395  }
396  double wSize = entry->size;
397  int wImgSize = ( int )wSize;
398  if ( wImgSize < 1 )
399  {
400  wImgSize = 1;
401  }
402  double hSize = wSize * hwRatio;
403  int hImgSize = ( int )hSize;
404  if ( hImgSize < 1 )
405  {
406  hImgSize = 1;
407  }
408  // cast double image sizes to int for QImage
409  QImage* image = new QImage( wImgSize, hImgSize, QImage::Format_ARGB32_Premultiplied );
410  image->fill( 0 ); // transparent background
411 
412  QPainter p( image );
413  if ( r.viewBoxF().width() == r.viewBoxF().height() )
414  {
415  r.render( &p );
416  }
417  else
418  {
419  QSizeF s( r.viewBoxF().size() );
420  s.scale( wSize, hSize, Qt::KeepAspectRatio );
421  QRectF rect(( wImgSize - s.width() ) / 2, ( hImgSize - s.height() ) / 2, s.width(), s.height() );
422  r.render( &p, rect );
423  }
424 
425  entry->image = image;
426  mTotalSize += ( image->width() * image->height() * 32 );
427 }
428 
429 void QgsSvgCache::cachePicture( QgsSvgCacheEntry *entry, bool forceVectorOutput )
430 {
431  Q_UNUSED( forceVectorOutput );
432  if ( !entry )
433  {
434  return;
435  }
436 
437  delete entry->picture;
438  entry->picture = 0;
439 
440  //correct QPictures dpi correction
441  QPicture* picture = new QPicture();
442  QRectF rect;
443  QSvgRenderer r( entry->svgContent );
444  double hwRatio = 1.0;
445  if ( r.viewBoxF().width() > 0 )
446  {
447  hwRatio = r.viewBoxF().height() / r.viewBoxF().width();
448  }
449 
450  double wSize = entry->size;
451  double hSize = wSize * hwRatio;
452  QSizeF s( r.viewBoxF().size() );
453  s.scale( wSize, hSize, Qt::KeepAspectRatio );
454  rect = QRectF( -s.width() / 2.0, -s.height() / 2.0, s.width(), s.height() );
455 
456  QPainter p( picture );
457  r.render( &p, rect );
458  entry->picture = picture;
459  mTotalSize += entry->picture->size();
460 }
461 
462 QgsSvgCacheEntry* QgsSvgCache::cacheEntry( const QString& file, double size, const QColor& fill, const QColor& outline, double outlineWidth,
463  double widthScaleFactor, double rasterScaleFactor )
464 {
465  //search entries in mEntryLookup
466  QgsSvgCacheEntry* currentEntry = 0;
467  QList<QgsSvgCacheEntry*> entries = mEntryLookup.values( file );
468 
469  QList<QgsSvgCacheEntry*>::iterator entryIt = entries.begin();
470  for ( ; entryIt != entries.end(); ++entryIt )
471  {
472  QgsSvgCacheEntry* cacheEntry = *entryIt;
473  if ( qgsDoubleNear( cacheEntry->size, size ) && cacheEntry->fill == fill && cacheEntry->outline == outline &&
474  cacheEntry->outlineWidth == outlineWidth && cacheEntry->widthScaleFactor == widthScaleFactor && cacheEntry->rasterScaleFactor == rasterScaleFactor )
475  {
476  currentEntry = cacheEntry;
477  break;
478  }
479  }
480 
481  //if not found: create new entry
482  //cache and replace params in svg content
483  if ( !currentEntry )
484  {
485  currentEntry = insertSVG( file, size, fill, outline, outlineWidth, widthScaleFactor, rasterScaleFactor );
486  }
487  else
488  {
489  takeEntryFromList( currentEntry );
490  if ( !mMostRecentEntry ) //list is empty
491  {
492  mMostRecentEntry = currentEntry;
493  mLeastRecentEntry = currentEntry;
494  }
495  else
496  {
497  mMostRecentEntry->nextEntry = currentEntry;
498  currentEntry->previousEntry = mMostRecentEntry;
499  currentEntry->nextEntry = 0;
500  mMostRecentEntry = currentEntry;
501  }
502  }
503 
504  //debugging
505  //printEntryList();
506 
507  return currentEntry;
508 }
509 
510 void QgsSvgCache::replaceElemParams( QDomElement& elem, const QColor& fill, const QColor& outline, double outlineWidth )
511 {
512  if ( elem.isNull() )
513  {
514  return;
515  }
516 
517  //go through attributes
518  QDomNamedNodeMap attributes = elem.attributes();
519  int nAttributes = attributes.count();
520  for ( int i = 0; i < nAttributes; ++i )
521  {
522  QDomAttr attribute = attributes.item( i ).toAttr();
523  //e.g. style="fill:param(fill);param(stroke)"
524  if ( attribute.name().compare( "style", Qt::CaseInsensitive ) == 0 )
525  {
526  //entries separated by ';'
527  QString newAttributeString;
528 
529  QStringList entryList = attribute.value().split( ';' );
530  QStringList::const_iterator entryIt = entryList.constBegin();
531  for ( ; entryIt != entryList.constEnd(); ++entryIt )
532  {
533  QStringList keyValueSplit = entryIt->split( ':' );
534  if ( keyValueSplit.size() < 2 )
535  {
536  continue;
537  }
538  QString key = keyValueSplit.at( 0 );
539  QString value = keyValueSplit.at( 1 );
540  if ( value.startsWith( "param(fill" ) )
541  {
542  value = fill.name();
543  }
544  else if ( value.startsWith( "param(outline)" ) )
545  {
546  value = outline.name();
547  }
548  else if ( value.startsWith( "param(outline-width)" ) )
549  {
550  value = QString::number( outlineWidth );
551  }
552 
553  if ( entryIt != entryList.constBegin() )
554  {
555  newAttributeString.append( ";" );
556  }
557  newAttributeString.append( key + ":" + value );
558  }
559  elem.setAttribute( attribute.name(), newAttributeString );
560  }
561  else
562  {
563  QString value = attribute.value();
564  if ( value.startsWith( "param(fill)" ) )
565  {
566  elem.setAttribute( attribute.name(), fill.name() );
567  }
568  else if ( value.startsWith( "param(outline)" ) )
569  {
570  elem.setAttribute( attribute.name(), outline.name() );
571  }
572  else if ( value.startsWith( "param(outline-width)" ) )
573  {
574  elem.setAttribute( attribute.name(), QString::number( outlineWidth ) );
575  }
576  }
577  }
578 
579  QDomNodeList childList = elem.childNodes();
580  int nChildren = childList.count();
581  for ( int i = 0; i < nChildren; ++i )
582  {
583  QDomElement childElem = childList.at( i ).toElement();
584  replaceElemParams( childElem, fill, outline, outlineWidth );
585  }
586 }
587 
588 void QgsSvgCache::containsElemParams( const QDomElement& elem, bool& hasFillParam, QColor& defaultFill, bool& hasOutlineParam, QColor& defaultOutline,
589  bool& hasOutlineWidthParam, double& defaultOutlineWidth ) const
590 {
591  if ( elem.isNull() )
592  {
593  return;
594  }
595 
596  //we already have all the information, no need to go deeper
597  if ( hasFillParam && hasOutlineParam && hasOutlineWidthParam )
598  {
599  return;
600  }
601 
602  //check this elements attribute
603  QDomNamedNodeMap attributes = elem.attributes();
604  int nAttributes = attributes.count();
605 
606  QStringList valueSplit;
607  for ( int i = 0; i < nAttributes; ++i )
608  {
609  QDomAttr attribute = attributes.item( i ).toAttr();
610  if ( attribute.name().compare( "style", Qt::CaseInsensitive ) == 0 )
611  {
612  //entries separated by ';'
613  QStringList entryList = attribute.value().split( ';' );
614  QStringList::const_iterator entryIt = entryList.constBegin();
615  for ( ; entryIt != entryList.constEnd(); ++entryIt )
616  {
617  QStringList keyValueSplit = entryIt->split( ':' );
618  if ( keyValueSplit.size() < 2 )
619  {
620  continue;
621  }
622  QString key = keyValueSplit.at( 0 );
623  QString value = keyValueSplit.at( 1 );
624  valueSplit = value.split( " " );
625  if ( !hasFillParam && value.startsWith( "param(fill)" ) )
626  {
627  hasFillParam = true;
628  if ( valueSplit.size() > 1 )
629  {
630  defaultFill = QColor( valueSplit.at( 1 ) );
631  }
632  }
633  else if ( !hasOutlineParam && value.startsWith( "param(outline)" ) )
634  {
635  hasOutlineParam = true;
636  if ( valueSplit.size() > 1 )
637  {
638  defaultOutline = QColor( valueSplit.at( 1 ) );
639  }
640  }
641  else if ( !hasOutlineWidthParam && value.startsWith( "param(outline-width)" ) )
642  {
643  hasOutlineWidthParam = true;
644  if ( valueSplit.size() > 1 )
645  {
646  defaultOutlineWidth = valueSplit.at( 1 ).toDouble();
647  }
648  }
649  }
650  }
651  else
652  {
653  QString value = attribute.value();
654  valueSplit = value.split( " " );
655  if ( !hasFillParam && value.startsWith( "param(fill)" ) )
656  {
657  hasFillParam = true;
658  if ( valueSplit.size() > 1 )
659  {
660  defaultFill = QColor( valueSplit.at( 1 ) );
661  }
662  }
663  else if ( !hasOutlineParam && value.startsWith( "param(outline)" ) )
664  {
665  hasOutlineParam = true;
666  if ( valueSplit.size() > 1 )
667  {
668  defaultOutline = QColor( valueSplit.at( 1 ) );
669  }
670  }
671  else if ( !hasOutlineWidthParam && value.startsWith( "param(outline-width)" ) )
672  {
673  hasOutlineWidthParam = true;
674  if ( valueSplit.size() > 1 )
675  {
676  defaultOutlineWidth = valueSplit.at( 1 ).toDouble();
677  }
678  }
679  }
680  }
681 
682  //pass it further to child items
683  QDomNodeList childList = elem.childNodes();
684  int nChildren = childList.count();
685  for ( int i = 0; i < nChildren; ++i )
686  {
687  QDomElement childElem = childList.at( i ).toElement();
688  containsElemParams( childElem, hasFillParam, defaultFill, hasOutlineParam, defaultOutline, hasOutlineWidthParam, defaultOutlineWidth );
689  }
690 }
691 
692 void QgsSvgCache::removeCacheEntry( QString s, QgsSvgCacheEntry* entry )
693 {
694  delete entry;
695  mEntryLookup.remove( s, entry );
696 }
697 
698 void QgsSvgCache::printEntryList()
699 {
700  QgsDebugMsg( "****************svg cache entry list*************************" );
701  QgsDebugMsg( "Cache size: " + QString::number( mTotalSize ) );
702  QgsSvgCacheEntry* entry = mLeastRecentEntry;
703  while ( entry )
704  {
705  QgsDebugMsg( "***Entry:" );
706  QgsDebugMsg( "File:" + entry->file );
707  QgsDebugMsg( "Size:" + QString::number( entry->size ) );
708  QgsDebugMsg( "Width scale factor" + QString::number( entry->widthScaleFactor ) );
709  QgsDebugMsg( "Raster scale factor" + QString::number( entry->rasterScaleFactor ) );
710  entry = entry->nextEntry;
711  }
712 }
713 
715 {
716  //only one entry in cache
717  if ( mLeastRecentEntry == mMostRecentEntry )
718  {
719  return;
720  }
721  QgsSvgCacheEntry* entry = mLeastRecentEntry;
722  while ( entry && ( mTotalSize > mMaximumSize ) )
723  {
724  QgsSvgCacheEntry* bkEntry = entry;
725  entry = entry->nextEntry;
726 
727  takeEntryFromList( bkEntry );
728  mEntryLookup.remove( bkEntry->lookupKey, bkEntry );
729  mTotalSize -= bkEntry->dataSize();
730  delete bkEntry;
731  }
732 }
733 
735 {
736  if ( !entry )
737  {
738  return;
739  }
740 
741  if ( entry->previousEntry )
742  {
743  entry->previousEntry->nextEntry = entry->nextEntry;
744  }
745  else
746  {
747  mLeastRecentEntry = entry->nextEntry;
748  }
749  if ( entry->nextEntry )
750  {
751  entry->nextEntry->previousEntry = entry->previousEntry;
752  }
753  else
754  {
755  mMostRecentEntry = entry->previousEntry;
756  }
757 }
758 
759 void QgsSvgCache::downloadProgress( qint64 bytesReceived, qint64 bytesTotal )
760 {
761  QString msg = tr( "%1 of %2 bytes of svg image downloaded." ).arg( bytesReceived ).arg( bytesTotal < 0 ? QString( "unknown number of" ) : QString::number( bytesTotal ) );
762  QgsDebugMsg( msg );
763  emit statusChanged( msg );
764 }
QgsSvgCacheEntry * previousEntry
Definition: qgssvgcache.h:66
QUrl toUrl() const
QString & append(QChar ch)
int dataSize() const
Return memory usage in bytes.
Definition: qgssvgcache.cpp:83
void scale(qreal width, qreal height, Qt::AspectRatioMode mode)
QImage * image
Definition: qgssvgcache.h:59
QString name() const
void render(QPainter *painter)
QString name() const
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&.
QString errorString() 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.
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
QStringList split(const QString &sep, SplitBehavior behavior, Qt::CaseSensitivity cs) const
QSizeF size() const
const T & at(int i) const
A cache for images / pictures derived from svg files.
Definition: qgssvgcache.h:77
void setFileName(const QString &name)
QDomElement documentElement() const
bool exists() const
QDomNodeList childNodes() const
QString toString(QFlags< QUrl::FormattingOption > options) const
QString tr(const char *sourceText, const char *disambiguation, int n)
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Definition: qgis.h:350
int size() const
static void logMessage(QString message, QString tag=QString::null, MessageLevel level=WARNING)
add a message to the instance (and create it if necessary)
uint size() const
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
QDomElement toElement() const
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
int count() const
QString number(int n, int base)
void processEvents(QFlags< QEventLoop::ProcessEventsFlag > flags)
int toInt(bool *ok) const
bool isNull() const
void fill(uint pixelValue)
QByteArray getImageData(const QString &path) const
Get image data.
int width() const
void setAttribute(const QString &name, const QString &value)
bool isFinished() const
int remove(const Key &key)
static QString symbolNameToPath(QString name)
Get symbol's path from its name.
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const
QByteArray readAll()
void statusChanged(const QString &theStatusQString)
Emit a signal to be caught by qgisapp and display a msg on status bar.
int count() const
const QByteArray & svgContent(const QString &file, double size, const QColor &fill, const QColor &outline, double outlineWidth, double widthScaleFactor, double rasterScaleFactor)
Get SVG content.
void deleteLater()
void cachePicture(QgsSvgCacheEntry *entry, bool forceVectorOutput=false)
QHash< Key, T >::iterator insert(const Key &key, const T &value)
virtual bool open(QFlags< QIODevice::OpenModeFlag > mode)
QString scheme() const
QString toLocalFile() const
iterator end()
const T value(const Key &key) const
bool contains(QChar ch, Qt::CaseSensitivity cs) const
QString value() const
bool isNull() const
void trimToMaximumSize()
Removes the least used items until the maximum size is under the limit.
QVariant header(QNetworkRequest::KnownHeaders header) const
void takeEntryFromList(QgsSvgCacheEntry *entry)
QString lookupKey
Lookup key used by QgsSvgCache's hashtable (relative or absolute path). Needed for removal from the h...
Definition: qgssvgcache.h:52
bool isValid() const
void cacheImage(QgsSvgCacheEntry *entry)
qreal width() const
void replaceParamsAndCacheSvg(QgsSvgCacheEntry *entry)
static QgsNetworkAccessManager * instance()
returns a pointer to the single instance
double outlineWidth
Definition: qgssvgcache.h:54
QDomAttr toAttr() const
QVariant attribute(QNetworkRequest::Attribute code) const
QUrl url() const
void setAttribute(Attribute code, const QVariant &value)
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.
NetworkError error() const
qreal height() const
int height() const
double rasterScaleFactor
Definition: qgssvgcache.h:56
QNetworkReply * get(const QNetworkRequest &request)
const_iterator constEnd() const
const_iterator constBegin() const
QDomNode item(int index) const
int size() const
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
int compare(const QString &other) const
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
QString toString() const
double widthScaleFactor
Definition: qgssvgcache.h:55
iterator begin()
QRectF viewBoxF() const
QString file
Absolute path to SVG file.
Definition: qgssvgcache.h:50
QByteArray toAscii() const
QByteArray svgContent
Definition: qgssvgcache.h:62
QByteArray toByteArray(int indent) const
QDomNode at(int index) const
bool setContent(const QByteArray &data, bool namespaceProcessing, QString *errorMsg, int *errorLine, int *errorColumn)
bool operator==(const QgsSvgCacheEntry &other) const
Don't consider image, picture, last used timestamp for comparison.
Definition: qgssvgcache.cpp:77
QgsSvgCacheEntry * nextEntry
Definition: qgssvgcache.h:65
QDomNamedNodeMap attributes() const