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