|
QGIS API Documentation
master-6227475
|
00001 /*************************************************************************** 00002 qgssvgcache.h 00003 ------------------------------ 00004 begin : 2011 00005 copyright : (C) 2011 by Marco Hugentobler 00006 email : marco dot hugentobler at sourcepole dot ch 00007 ***************************************************************************/ 00008 00009 /*************************************************************************** 00010 * * 00011 * This program is free software; you can redistribute it and/or modify * 00012 * it under the terms of the GNU General Public License as published by * 00013 * the Free Software Foundation; either version 2 of the License, or * 00014 * (at your option) any later version. * 00015 * * 00016 ***************************************************************************/ 00017 00018 #include "qgssvgcache.h" 00019 #include "qgis.h" 00020 #include "qgslogger.h" 00021 #include "qgsnetworkaccessmanager.h" 00022 #include "qgsmessagelog.h" 00023 #include <QApplication> 00024 #include <QCoreApplication> 00025 #include <QCursor> 00026 #include <QDomDocument> 00027 #include <QDomElement> 00028 #include <QFile> 00029 #include <QImage> 00030 #include <QPainter> 00031 #include <QPicture> 00032 #include <QSvgRenderer> 00033 #include <QFileInfo> 00034 #include <QNetworkReply> 00035 #include <QNetworkRequest> 00036 00037 QgsSvgCacheEntry::QgsSvgCacheEntry(): file( QString() ), size( 0.0 ), outlineWidth( 0 ), widthScaleFactor( 1.0 ), rasterScaleFactor( 1.0 ), fill( Qt::black ), 00038 outline( Qt::black ), image( 0 ), picture( 0 ) 00039 { 00040 } 00041 00042 QgsSvgCacheEntry::QgsSvgCacheEntry( const QString& f, double s, double ow, double wsf, double rsf, const QColor& fi, const QColor& ou ): file( f ), size( s ), outlineWidth( ow ), 00043 widthScaleFactor( wsf ), rasterScaleFactor( rsf ), fill( fi ), outline( ou ), image( 0 ), picture( 0 ) 00044 { 00045 } 00046 00047 00048 QgsSvgCacheEntry::~QgsSvgCacheEntry() 00049 { 00050 delete image; 00051 delete picture; 00052 } 00053 00054 bool QgsSvgCacheEntry::operator==( const QgsSvgCacheEntry& other ) const 00055 { 00056 return ( other.file == file && other.size == size && other.outlineWidth == outlineWidth && other.widthScaleFactor == widthScaleFactor 00057 && other.rasterScaleFactor == rasterScaleFactor && other.fill == fill && other.outline == outline ); 00058 } 00059 00060 int QgsSvgCacheEntry::dataSize() const 00061 { 00062 int size = svgContent.size(); 00063 if ( picture ) 00064 { 00065 size += picture->size(); 00066 } 00067 if ( image ) 00068 { 00069 size += ( image->width() * image->height() * 32 ); 00070 } 00071 return size; 00072 } 00073 00074 QString file; 00075 double size; 00076 double outlineWidth; 00077 double widthScaleFactor; 00078 double rasterScaleFactor; 00079 QColor fill; 00080 QColor outline; 00081 00082 QgsSvgCache* QgsSvgCache::mInstance = 0; 00083 00084 QgsSvgCache* QgsSvgCache::instance() 00085 { 00086 if ( !mInstance ) 00087 { 00088 mInstance = new QgsSvgCache(); 00089 } 00090 return mInstance; 00091 } 00092 00093 QgsSvgCache::QgsSvgCache( QObject *parent ) 00094 : QObject( parent ) 00095 , mTotalSize( 0 ) 00096 , mLeastRecentEntry( 0 ) 00097 , mMostRecentEntry( 0 ) 00098 { 00099 } 00100 00101 QgsSvgCache::~QgsSvgCache() 00102 { 00103 QMultiHash< QString, QgsSvgCacheEntry* >::iterator it = mEntryLookup.begin(); 00104 for ( ; it != mEntryLookup.end(); ++it ) 00105 { 00106 delete it.value(); 00107 } 00108 } 00109 00110 00111 const QImage& QgsSvgCache::svgAsImage( const QString& file, double size, const QColor& fill, const QColor& outline, double outlineWidth, 00112 double widthScaleFactor, double rasterScaleFactor, bool& fitsInCache ) 00113 { 00114 fitsInCache = true; 00115 QgsSvgCacheEntry* currentEntry = cacheEntry( file, size, fill, outline, outlineWidth, widthScaleFactor, rasterScaleFactor ); 00116 00117 //if current entry image is 0: cache image for entry 00118 // checks to see if image will fit into cache 00119 //update stats for memory usage 00120 if ( !currentEntry->image ) 00121 { 00122 QSvgRenderer r( currentEntry->svgContent ); 00123 double hwRatio = 1.0; 00124 if ( r.viewBoxF().width() > 0 ) 00125 { 00126 hwRatio = r.viewBoxF().height() / r.viewBoxF().width(); 00127 } 00128 long cachedDataSize = 0; 00129 cachedDataSize += currentEntry->svgContent.size(); 00130 cachedDataSize += ( int )( currentEntry->size * currentEntry->size * hwRatio * 32 ); 00131 if ( cachedDataSize > mMaximumSize / 2 ) 00132 { 00133 fitsInCache = false; 00134 delete currentEntry->image; 00135 currentEntry->image = 0; 00136 //currentEntry->image = new QImage( 0, 0 ); 00137 00138 // instead cache picture 00139 cachePicture( currentEntry ); 00140 } 00141 else 00142 { 00143 cacheImage( currentEntry ); 00144 } 00145 trimToMaximumSize(); 00146 } 00147 00148 return *( currentEntry->image ); 00149 } 00150 00151 const QPicture& QgsSvgCache::svgAsPicture( const QString& file, double size, const QColor& fill, const QColor& outline, double outlineWidth, 00152 double widthScaleFactor, double rasterScaleFactor ) 00153 { 00154 QgsSvgCacheEntry* currentEntry = cacheEntry( file, size, fill, outline, outlineWidth, widthScaleFactor, rasterScaleFactor ); 00155 00156 //if current entry picture is 0: cache picture for entry 00157 //update stats for memory usage 00158 if ( !currentEntry->picture ) 00159 { 00160 cachePicture( currentEntry ); 00161 trimToMaximumSize(); 00162 } 00163 00164 return *( currentEntry->picture ); 00165 } 00166 00167 QgsSvgCacheEntry* QgsSvgCache::insertSVG( const QString& file, double size, const QColor& fill, const QColor& outline, double outlineWidth, 00168 double widthScaleFactor, double rasterScaleFactor ) 00169 { 00170 QgsSvgCacheEntry* entry = new QgsSvgCacheEntry( file, size, outlineWidth, widthScaleFactor, rasterScaleFactor, fill, outline ); 00171 00172 replaceParamsAndCacheSvg( entry ); 00173 00174 mEntryLookup.insert( file, entry ); 00175 00176 //insert to most recent place in entry list 00177 if ( !mMostRecentEntry ) //inserting first entry 00178 { 00179 mLeastRecentEntry = entry; 00180 mMostRecentEntry = entry; 00181 entry->previousEntry = 0; 00182 entry->nextEntry = 0; 00183 } 00184 else 00185 { 00186 entry->previousEntry = mMostRecentEntry; 00187 entry->nextEntry = 0; 00188 mMostRecentEntry->nextEntry = entry; 00189 mMostRecentEntry = entry; 00190 } 00191 00192 trimToMaximumSize(); 00193 return entry; 00194 } 00195 00196 void QgsSvgCache::containsParams( const QString& path, bool& hasFillParam, QColor& defaultFillColor, bool& hasOutlineParam, QColor& defaultOutlineColor, 00197 bool& hasOutlineWidthParam, double& defaultOutlineWidth ) const 00198 { 00199 defaultFillColor = QColor( Qt::black ); 00200 defaultOutlineColor = QColor( Qt::black ); 00201 defaultOutlineWidth = 1.0; 00202 00203 QDomDocument svgDoc; 00204 if ( !svgDoc.setContent( getImageData( path ) ) ) 00205 { 00206 return; 00207 } 00208 00209 QDomElement docElem = svgDoc.documentElement(); 00210 containsElemParams( docElem, hasFillParam, defaultFillColor, hasOutlineParam, defaultOutlineColor, hasOutlineWidthParam, defaultOutlineWidth ); 00211 } 00212 00213 void QgsSvgCache::replaceParamsAndCacheSvg( QgsSvgCacheEntry* entry ) 00214 { 00215 if ( !entry ) 00216 { 00217 return; 00218 } 00219 00220 QDomDocument svgDoc; 00221 if ( !svgDoc.setContent( getImageData( entry->file ) ) ) 00222 { 00223 return; 00224 } 00225 00226 //replace fill color, outline color, outline with in all nodes 00227 QDomElement docElem = svgDoc.documentElement(); 00228 replaceElemParams( docElem, entry->fill, entry->outline, entry->outlineWidth ); 00229 00230 entry->svgContent = svgDoc.toByteArray(); 00231 mTotalSize += entry->svgContent.size(); 00232 } 00233 00234 QByteArray QgsSvgCache::getImageData( const QString &path ) const 00235 { 00236 // is it a path to local file? 00237 QFile svgFile( path ); 00238 if ( svgFile.exists() ) 00239 { 00240 if ( svgFile.open( QIODevice::ReadOnly ) ) 00241 { 00242 return svgFile.readAll(); 00243 } 00244 else 00245 { 00246 return QByteArray(); 00247 } 00248 } 00249 00250 // maybe it's a url... 00251 if ( !path.contains( "://" ) ) // otherwise short, relative SVG paths might be considered URLs 00252 { 00253 return QByteArray(); 00254 } 00255 00256 QUrl svgUrl( path ); 00257 if ( !svgUrl.isValid() ) 00258 { 00259 return QByteArray(); 00260 } 00261 00262 // check whether it's a url pointing to a local file 00263 if ( svgUrl.scheme().compare( "file", Qt::CaseInsensitive ) == 0 ) 00264 { 00265 svgFile.setFileName( svgUrl.toLocalFile() ); 00266 if ( svgFile.exists() ) 00267 { 00268 if ( svgFile.open( QIODevice::ReadOnly ) ) 00269 { 00270 return svgFile.readAll(); 00271 } 00272 } 00273 00274 // not found... 00275 return QByteArray(); 00276 } 00277 00278 // the url points to a remote resource, download it! 00279 QNetworkReply *reply = 0; 00280 00281 // The following code blocks until the file is downloaded... 00282 // TODO: use signals to get reply finished notification, in this moment 00283 // it's executed while rendering. 00284 while ( 1 ) 00285 { 00286 QgsDebugMsg( QString( "get svg: %1" ).arg( svgUrl.toString() ) ); 00287 QNetworkRequest request( svgUrl ); 00288 request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache ); 00289 request.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true ); 00290 00291 reply = QgsNetworkAccessManager::instance()->get( request ); 00292 connect( reply, SIGNAL( downloadProgress( qint64, qint64 ) ), this, SLOT( downloadProgress( qint64, qint64 ) ) ); 00293 00294 //emit statusChanged( tr( "Downloading svg." ) ); 00295 00296 // wait until the image download finished 00297 // TODO: connect to the reply->finished() signal 00298 while ( !reply->isFinished() ) 00299 { 00300 QCoreApplication::processEvents( QEventLoop::ExcludeUserInputEvents, 500 ); 00301 } 00302 00303 if ( reply->error() != QNetworkReply::NoError ) 00304 { 00305 QgsMessageLog::logMessage( tr( "SVG request failed [error: %1 - url: %2]" ).arg( reply->errorString() ).arg( reply->url().toString() ), tr( "SVG" ) ); 00306 00307 reply->deleteLater(); 00308 return QByteArray(); 00309 } 00310 00311 QVariant redirect = reply->attribute( QNetworkRequest::RedirectionTargetAttribute ); 00312 if ( redirect.isNull() ) 00313 { 00314 // neither network error nor redirection 00315 // TODO: cache the image 00316 break; 00317 } 00318 00319 // do a new request to the redirect url 00320 svgUrl = redirect.toUrl(); 00321 reply->deleteLater(); 00322 } 00323 00324 QVariant status = reply->attribute( QNetworkRequest::HttpStatusCodeAttribute ); 00325 if ( !status.isNull() && status.toInt() >= 400 ) 00326 { 00327 QVariant phrase = reply->attribute( QNetworkRequest::HttpReasonPhraseAttribute ); 00328 QgsMessageLog::logMessage( tr( "SVG request error [status: %1 - reason phrase: %2]" ).arg( status.toInt() ).arg( phrase.toString() ), tr( "SVG" ) ); 00329 00330 reply->deleteLater(); 00331 return QByteArray(); 00332 } 00333 00334 QString contentType = reply->header( QNetworkRequest::ContentTypeHeader ).toString(); 00335 QgsDebugMsg( "contentType: " + contentType ); 00336 if ( !contentType.startsWith( "image/svg+xml", Qt::CaseInsensitive ) ) 00337 { 00338 reply->deleteLater(); 00339 return QByteArray(); 00340 } 00341 00342 // read the image data 00343 QByteArray ba = reply->readAll(); 00344 reply->deleteLater(); 00345 00346 return ba; 00347 } 00348 00349 void QgsSvgCache::cacheImage( QgsSvgCacheEntry* entry ) 00350 { 00351 if ( !entry ) 00352 { 00353 return; 00354 } 00355 00356 delete entry->image; 00357 entry->image = 0; 00358 00359 QSvgRenderer r( entry->svgContent ); 00360 double hwRatio = 1.0; 00361 if ( r.viewBoxF().width() > 0 ) 00362 { 00363 hwRatio = r.viewBoxF().height() / r.viewBoxF().width(); 00364 } 00365 double wSize = entry->size; 00366 int wImgSize = ( int )wSize; 00367 if ( wImgSize < 1 ) 00368 { 00369 wImgSize = 1; 00370 } 00371 double hSize = wSize * hwRatio; 00372 int hImgSize = ( int )hSize; 00373 if ( hImgSize < 1 ) 00374 { 00375 hImgSize = 1; 00376 } 00377 // cast double image sizes to int for QImage 00378 QImage* image = new QImage( wImgSize, hImgSize, QImage::Format_ARGB32_Premultiplied ); 00379 image->fill( 0 ); // transparent background 00380 00381 QPainter p( image ); 00382 if ( r.viewBoxF().width() == r.viewBoxF().height() ) 00383 { 00384 r.render( &p ); 00385 } 00386 else 00387 { 00388 QSizeF s( r.viewBoxF().size() ); 00389 s.scale( wSize, hSize, Qt::KeepAspectRatio ); 00390 QRectF rect(( wImgSize - s.width() ) / 2, ( hImgSize - s.height() ) / 2, s.width(), s.height() ); 00391 r.render( &p, rect ); 00392 } 00393 00394 entry->image = image; 00395 mTotalSize += ( image->width() * image->height() * 32 ); 00396 } 00397 00398 void QgsSvgCache::cachePicture( QgsSvgCacheEntry *entry ) 00399 { 00400 if ( !entry ) 00401 { 00402 return; 00403 } 00404 00405 delete entry->picture; 00406 entry->picture = 0; 00407 00408 //correct QPictures dpi correction 00409 QPicture* picture = new QPicture(); 00410 QRectF rect; 00411 QSvgRenderer r( entry->svgContent ); 00412 double hwRatio = 1.0; 00413 if ( r.viewBoxF().width() > 0 ) 00414 { 00415 hwRatio = r.viewBoxF().height() / r.viewBoxF().width(); 00416 } 00417 bool drawOnScreen = qgsDoubleNear( entry->rasterScaleFactor, 1.0, 0.1 ); 00418 if ( drawOnScreen ) 00419 { 00420 // fix to ensure rotated symbols scale with composer page (i.e. not map item) zoom 00421 double wSize = entry->size; 00422 double hSize = wSize * hwRatio; 00423 QSizeF s( r.viewBoxF().size() ); 00424 s.scale( wSize, hSize, Qt::KeepAspectRatio ); 00425 rect = QRectF( -s.width() / 2.0, -s.height() / 2.0, s.width(), s.height() ); 00426 } 00427 else 00428 { 00429 // output for print or image saving @ specific dpi 00430 double scaledSize = entry->size / 25.4 / ( entry->rasterScaleFactor * entry->widthScaleFactor ); 00431 double wSize = scaledSize * picture->logicalDpiX(); 00432 double hSize = scaledSize * picture->logicalDpiY() * r.viewBoxF().height() / r.viewBoxF().width(); 00433 rect = QRectF( QPointF( -wSize / 2.0, -hSize / 2.0 ), QSizeF( wSize, hSize ) ); 00434 } 00435 00436 QPainter p( picture ); 00437 r.render( &p, rect ); 00438 entry->picture = picture; 00439 mTotalSize += entry->picture->size(); 00440 } 00441 00442 QgsSvgCacheEntry* QgsSvgCache::cacheEntry( const QString& file, double size, const QColor& fill, const QColor& outline, double outlineWidth, 00443 double widthScaleFactor, double rasterScaleFactor ) 00444 { 00445 //search entries in mEntryLookup 00446 QgsSvgCacheEntry* currentEntry = 0; 00447 QList<QgsSvgCacheEntry*> entries = mEntryLookup.values( file ); 00448 00449 QList<QgsSvgCacheEntry*>::iterator entryIt = entries.begin(); 00450 for ( ; entryIt != entries.end(); ++entryIt ) 00451 { 00452 QgsSvgCacheEntry* cacheEntry = *entryIt; 00453 if ( cacheEntry->file == file && qgsDoubleNear( cacheEntry->size, size ) && cacheEntry->fill == fill && cacheEntry->outline == outline && 00454 cacheEntry->outlineWidth == outlineWidth && cacheEntry->widthScaleFactor == widthScaleFactor && cacheEntry->rasterScaleFactor == rasterScaleFactor ) 00455 { 00456 currentEntry = cacheEntry; 00457 break; 00458 } 00459 } 00460 00461 //if not found: create new entry 00462 //cache and replace params in svg content 00463 if ( !currentEntry ) 00464 { 00465 currentEntry = insertSVG( file, size, fill, outline, outlineWidth, widthScaleFactor, rasterScaleFactor ); 00466 } 00467 else 00468 { 00469 takeEntryFromList( currentEntry ); 00470 if ( !mMostRecentEntry ) //list is empty 00471 { 00472 mMostRecentEntry = currentEntry; 00473 mLeastRecentEntry = currentEntry; 00474 } 00475 else 00476 { 00477 mMostRecentEntry->nextEntry = currentEntry; 00478 currentEntry->previousEntry = mMostRecentEntry; 00479 currentEntry->nextEntry = 0; 00480 mMostRecentEntry = currentEntry; 00481 } 00482 } 00483 00484 //debugging 00485 //printEntryList(); 00486 00487 return currentEntry; 00488 } 00489 00490 void QgsSvgCache::replaceElemParams( QDomElement& elem, const QColor& fill, const QColor& outline, double outlineWidth ) 00491 { 00492 if ( elem.isNull() ) 00493 { 00494 return; 00495 } 00496 00497 //go through attributes 00498 QDomNamedNodeMap attributes = elem.attributes(); 00499 int nAttributes = attributes.count(); 00500 for ( int i = 0; i < nAttributes; ++i ) 00501 { 00502 QDomAttr attribute = attributes.item( i ).toAttr(); 00503 //e.g. style="fill:param(fill);param(stroke)" 00504 if ( attribute.name().compare( "style", Qt::CaseInsensitive ) == 0 ) 00505 { 00506 //entries separated by ';' 00507 QString newAttributeString; 00508 00509 QStringList entryList = attribute.value().split( ';' ); 00510 QStringList::const_iterator entryIt = entryList.constBegin(); 00511 for ( ; entryIt != entryList.constEnd(); ++entryIt ) 00512 { 00513 QStringList keyValueSplit = entryIt->split( ':' ); 00514 if ( keyValueSplit.size() < 2 ) 00515 { 00516 continue; 00517 } 00518 QString key = keyValueSplit.at( 0 ); 00519 QString value = keyValueSplit.at( 1 ); 00520 if ( value.startsWith( "param(fill" ) ) 00521 { 00522 value = fill.name(); 00523 } 00524 else if ( value.startsWith( "param(outline)" ) ) 00525 { 00526 value = outline.name(); 00527 } 00528 else if ( value.startsWith( "param(outline-width)" ) ) 00529 { 00530 value = QString::number( outlineWidth ); 00531 } 00532 00533 if ( entryIt != entryList.constBegin() ) 00534 { 00535 newAttributeString.append( ";" ); 00536 } 00537 newAttributeString.append( key + ":" + value ); 00538 } 00539 elem.setAttribute( attribute.name(), newAttributeString ); 00540 } 00541 else 00542 { 00543 QString value = attribute.value(); 00544 if ( value.startsWith( "param(fill)" ) ) 00545 { 00546 elem.setAttribute( attribute.name(), fill.name() ); 00547 } 00548 else if ( value.startsWith( "param(outline)" ) ) 00549 { 00550 elem.setAttribute( attribute.name(), outline.name() ); 00551 } 00552 else if ( value.startsWith( "param(outline-width)" ) ) 00553 { 00554 elem.setAttribute( attribute.name(), QString::number( outlineWidth ) ); 00555 } 00556 } 00557 } 00558 00559 QDomNodeList childList = elem.childNodes(); 00560 int nChildren = childList.count(); 00561 for ( int i = 0; i < nChildren; ++i ) 00562 { 00563 QDomElement childElem = childList.at( i ).toElement(); 00564 replaceElemParams( childElem, fill, outline, outlineWidth ); 00565 } 00566 } 00567 00568 void QgsSvgCache::containsElemParams( const QDomElement& elem, bool& hasFillParam, QColor& defaultFill, bool& hasOutlineParam, QColor& defaultOutline, 00569 bool& hasOutlineWidthParam, double& defaultOutlineWidth ) const 00570 { 00571 if ( elem.isNull() ) 00572 { 00573 return; 00574 } 00575 00576 //we already have all the information, no need to go deeper 00577 if ( hasFillParam && hasOutlineParam && hasOutlineWidthParam ) 00578 { 00579 return; 00580 } 00581 00582 //check this elements attribute 00583 QDomNamedNodeMap attributes = elem.attributes(); 00584 int nAttributes = attributes.count(); 00585 00586 QStringList valueSplit; 00587 for ( int i = 0; i < nAttributes; ++i ) 00588 { 00589 QDomAttr attribute = attributes.item( i ).toAttr(); 00590 if ( attribute.name().compare( "style", Qt::CaseInsensitive ) == 0 ) 00591 { 00592 //entries separated by ';' 00593 QStringList entryList = attribute.value().split( ';' ); 00594 QStringList::const_iterator entryIt = entryList.constBegin(); 00595 for ( ; entryIt != entryList.constEnd(); ++entryIt ) 00596 { 00597 QStringList keyValueSplit = entryIt->split( ':' ); 00598 if ( keyValueSplit.size() < 2 ) 00599 { 00600 continue; 00601 } 00602 QString key = keyValueSplit.at( 0 ); 00603 QString value = keyValueSplit.at( 1 ); 00604 valueSplit = value.split( " " ); 00605 if ( !hasFillParam && value.startsWith( "param(fill)" ) ) 00606 { 00607 hasFillParam = true; 00608 if ( valueSplit.size() > 1 ) 00609 { 00610 defaultFill = QColor( valueSplit.at( 1 ) ); 00611 } 00612 } 00613 else if ( !hasOutlineParam && value.startsWith( "param(outline)" ) ) 00614 { 00615 hasOutlineParam = true; 00616 if ( valueSplit.size() > 1 ) 00617 { 00618 defaultOutline = QColor( valueSplit.at( 1 ) ); 00619 } 00620 } 00621 else if ( !hasOutlineWidthParam && value.startsWith( "param(outline-width)" ) ) 00622 { 00623 hasOutlineWidthParam = true; 00624 if ( valueSplit.size() > 1 ) 00625 { 00626 defaultOutlineWidth = valueSplit.at( 1 ).toDouble(); 00627 } 00628 } 00629 } 00630 } 00631 else 00632 { 00633 QString value = attribute.value(); 00634 valueSplit = value.split( " " ); 00635 if ( !hasFillParam && value.startsWith( "param(fill)" ) ) 00636 { 00637 hasFillParam = true; 00638 if ( valueSplit.size() > 1 ) 00639 { 00640 defaultFill = QColor( valueSplit.at( 1 ) ); 00641 } 00642 } 00643 else if ( !hasOutlineParam && value.startsWith( "param(outline)" ) ) 00644 { 00645 hasOutlineParam = true; 00646 if ( valueSplit.size() > 1 ) 00647 { 00648 defaultOutline = QColor( valueSplit.at( 1 ) ); 00649 } 00650 } 00651 else if ( !hasOutlineWidthParam && value.startsWith( "param(outline-width)" ) ) 00652 { 00653 hasOutlineWidthParam = true; 00654 if ( valueSplit.size() > 1 ) 00655 { 00656 defaultOutlineWidth = valueSplit.at( 1 ).toDouble(); 00657 } 00658 } 00659 } 00660 } 00661 00662 //pass it further to child items 00663 QDomNodeList childList = elem.childNodes(); 00664 int nChildren = childList.count(); 00665 for ( int i = 0; i < nChildren; ++i ) 00666 { 00667 QDomElement childElem = childList.at( i ).toElement(); 00668 containsElemParams( childElem, hasFillParam, defaultFill, hasOutlineParam, defaultOutline, hasOutlineWidthParam, defaultOutlineWidth ); 00669 } 00670 } 00671 00672 void QgsSvgCache::removeCacheEntry( QString s, QgsSvgCacheEntry* entry ) 00673 { 00674 delete entry; 00675 mEntryLookup.remove( s , entry ); 00676 } 00677 00678 void QgsSvgCache::printEntryList() 00679 { 00680 QgsDebugMsg( "****************svg cache entry list*************************" ); 00681 QgsDebugMsg( "Cache size: " + QString::number( mTotalSize ) ); 00682 QgsSvgCacheEntry* entry = mLeastRecentEntry; 00683 while ( entry ) 00684 { 00685 QgsDebugMsg( "***Entry:" ); 00686 QgsDebugMsg( "File:" + entry->file ); 00687 QgsDebugMsg( "Size:" + QString::number( entry->size ) ); 00688 QgsDebugMsg( "Width scale factor" + QString::number( entry->widthScaleFactor ) ); 00689 QgsDebugMsg( "Raster scale factor" + QString::number( entry->rasterScaleFactor ) ); 00690 entry = entry->nextEntry; 00691 } 00692 } 00693 00694 void QgsSvgCache::trimToMaximumSize() 00695 { 00696 QgsSvgCacheEntry* entry = mLeastRecentEntry; 00697 while ( entry && ( mTotalSize > mMaximumSize ) ) 00698 { 00699 QgsSvgCacheEntry* bkEntry = entry; 00700 entry = entry->nextEntry; 00701 00702 takeEntryFromList( bkEntry ); 00703 mEntryLookup.remove( bkEntry->file, bkEntry ); 00704 mTotalSize -= bkEntry->dataSize(); 00705 delete bkEntry; 00706 } 00707 } 00708 00709 void QgsSvgCache::takeEntryFromList( QgsSvgCacheEntry* entry ) 00710 { 00711 if ( !entry ) 00712 { 00713 return; 00714 } 00715 00716 if ( entry->previousEntry ) 00717 { 00718 entry->previousEntry->nextEntry = entry->nextEntry; 00719 } 00720 else 00721 { 00722 mLeastRecentEntry = entry->nextEntry; 00723 } 00724 if ( entry->nextEntry ) 00725 { 00726 entry->nextEntry->previousEntry = entry->previousEntry; 00727 } 00728 else 00729 { 00730 mMostRecentEntry = entry->previousEntry; 00731 } 00732 } 00733 00734 void QgsSvgCache::downloadProgress( qint64 bytesReceived, qint64 bytesTotal ) 00735 { 00736 QString msg = tr( "%1 of %2 bytes of svg image downloaded." ).arg( bytesReceived ).arg( bytesTotal < 0 ? QString( "unknown number of" ) : QString::number( bytesTotal ) ); 00737 QgsDebugMsg( msg ); 00738 emit statusChanged( msg ); 00739 }