25 #include <QApplication>
26 #include <QCoreApplication>
28 #include <QDomDocument>
29 #include <QDomElement>
34 #include <QSvgRenderer>
36 #include <QNetworkReply>
37 #include <QNetworkRequest>
40 outline( Qt::black ), image( 0 ), picture( 0 ), nextEntry( 0 ), previousEntry( 0 )
71 size += (
image->width() *
image->height() * 32 );
93 , mLeastRecentEntry( 0 )
94 , mMostRecentEntry( 0 )
96 mMissingSvg = QString(
"<svg width='10' height='10'><text x='5' y='10' font-size='10' text-anchor='middle'>?</text></svg>" ).toAscii();
101 QMultiHash< QString, QgsSvgCacheEntry* >::iterator it = mEntryLookup.begin();
102 for ( ; it != mEntryLookup.end(); ++it )
112 QMutexLocker locker( &mMutex );
120 if ( !currentEntry->
image )
123 double hwRatio = 1.0;
124 if ( r.viewBoxF().width() > 0 )
126 hwRatio = r.viewBoxF().height() / r.viewBoxF().width();
128 long cachedDataSize = 0;
129 cachedDataSize += currentEntry->
svgContent.size();
130 cachedDataSize += ( int )( currentEntry->
size * currentEntry->
size * hwRatio * 32 );
131 if ( cachedDataSize > mMaximumSize / 2 )
134 delete currentEntry->
image;
135 currentEntry->
image = 0;
151 return *( currentEntry->
image );
157 QMutexLocker locker( &mMutex );
169 return *( currentEntry->
picture );
182 mEntryLookup.insert( file, entry );
185 if ( !mMostRecentEntry )
187 mLeastRecentEntry = entry;
188 mMostRecentEntry = entry;
197 mMostRecentEntry = entry;
204 void QgsSvgCache::containsParams(
const QString& path,
bool& hasFillParam, QColor& defaultFillColor,
bool& hasOutlineParam, QColor& defaultOutlineColor,
205 bool& hasOutlineWidthParam,
double& defaultOutlineWidth )
const
207 defaultFillColor = QColor( Qt::black );
208 defaultOutlineColor = QColor( Qt::black );
209 defaultOutlineWidth = 1.0;
217 QDomElement docElem = svgDoc.documentElement();
218 containsElemParams( docElem, hasFillParam, defaultFillColor, hasOutlineParam, defaultOutlineColor, hasOutlineWidthParam, defaultOutlineWidth );
235 QDomElement docElem = svgDoc.documentElement();
245 QFile svgFile( path );
246 if ( svgFile.exists() )
248 if ( svgFile.open( QIODevice::ReadOnly ) )
250 return svgFile.readAll();
259 if ( !path.contains(
"://" ) )
265 if ( !svgUrl.isValid() )
271 if ( svgUrl.scheme().compare(
"file", Qt::CaseInsensitive ) == 0 )
273 svgFile.setFileName( svgUrl.toLocalFile() );
274 if ( svgFile.exists() )
276 if ( svgFile.open( QIODevice::ReadOnly ) )
278 return svgFile.readAll();
287 QNetworkReply *reply = 0;
294 QgsDebugMsg( QString(
"get svg: %1" ).arg( svgUrl.toString() ) );
295 QNetworkRequest request( svgUrl );
296 request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache );
297 request.setAttribute( QNetworkRequest::CacheSaveControlAttribute,
true );
300 connect( reply, SIGNAL( downloadProgress( qint64, qint64 ) ),
this, SLOT( downloadProgress( qint64, qint64 ) ) );
306 while ( !reply->isFinished() )
308 QCoreApplication::processEvents( QEventLoop::ExcludeUserInputEvents, 500 );
311 if ( reply->error() != QNetworkReply::NoError )
313 QgsMessageLog::logMessage(
tr(
"SVG request failed [error: %1 - url: %2]" ).arg( reply->errorString() ).arg( reply->url().toString() ),
tr(
"SVG" ) );
315 reply->deleteLater();
319 QVariant redirect = reply->attribute( QNetworkRequest::RedirectionTargetAttribute );
320 if ( redirect.isNull() )
328 svgUrl = redirect.toUrl();
329 reply->deleteLater();
332 QVariant status = reply->attribute( QNetworkRequest::HttpStatusCodeAttribute );
333 if ( !status.isNull() && status.toInt() >= 400 )
335 QVariant phrase = reply->attribute( QNetworkRequest::HttpReasonPhraseAttribute );
336 QgsMessageLog::logMessage(
tr(
"SVG request error [status: %1 - reason phrase: %2]" ).arg( status.toInt() ).arg( phrase.toString() ),
tr(
"SVG" ) );
338 reply->deleteLater();
342 QString contentType = reply->header( QNetworkRequest::ContentTypeHeader ).toString();
344 if ( !contentType.startsWith(
"image/svg+xml", Qt::CaseInsensitive ) )
346 reply->deleteLater();
351 QByteArray ba = reply->readAll();
352 reply->deleteLater();
368 double hwRatio = 1.0;
369 if ( r.viewBoxF().width() > 0 )
371 hwRatio = r.viewBoxF().height() / r.viewBoxF().width();
373 double wSize = entry->
size;
374 int wImgSize = ( int )wSize;
379 double hSize = wSize * hwRatio;
380 int hImgSize = ( int )hSize;
386 QImage* image =
new QImage( wImgSize, hImgSize, QImage::Format_ARGB32_Premultiplied );
390 if ( r.viewBoxF().width() == r.viewBoxF().height() )
396 QSizeF s( r.viewBoxF().size() );
397 s.scale( wSize, hSize, Qt::KeepAspectRatio );
398 QRectF rect(( wImgSize - s.width() ) / 2, ( hImgSize - s.height() ) / 2, s.width(), s.height() );
399 r.render( &p, rect );
402 entry->
image = image;
403 mTotalSize += ( image->width() * image->height() * 32 );
408 Q_UNUSED( forceVectorOutput );
418 QPicture* picture =
new QPicture();
421 double hwRatio = 1.0;
422 if ( r.viewBoxF().width() > 0 )
424 hwRatio = r.viewBoxF().height() / r.viewBoxF().width();
427 double wSize = entry->
size;
428 double hSize = wSize * hwRatio;
429 QSizeF s( r.viewBoxF().size() );
430 s.scale( wSize, hSize, Qt::KeepAspectRatio );
431 rect = QRectF( -s.width() / 2.0, -s.height() / 2.0, s.width(), s.height() );
433 QPainter p( picture );
434 r.render( &p, rect );
436 mTotalSize += entry->
picture->size();
444 QList<QgsSvgCacheEntry*> entries = mEntryLookup.values( file );
446 QList<QgsSvgCacheEntry*>::iterator entryIt = entries.begin();
447 for ( ; entryIt != entries.end(); ++entryIt )
462 currentEntry =
insertSVG( file, size, fill, outline, outlineWidth, widthScaleFactor, rasterScaleFactor );
467 if ( !mMostRecentEntry )
469 mMostRecentEntry = currentEntry;
470 mLeastRecentEntry = currentEntry;
474 mMostRecentEntry->
nextEntry = currentEntry;
477 mMostRecentEntry = currentEntry;
487 void QgsSvgCache::replaceElemParams( QDomElement& elem,
const QColor&
fill,
const QColor&
outline,
double outlineWidth )
495 QDomNamedNodeMap attributes = elem.attributes();
496 int nAttributes = attributes.count();
497 for (
int i = 0; i < nAttributes; ++i )
499 QDomAttr attribute = attributes.item( i ).toAttr();
501 if ( attribute.name().compare(
"style", Qt::CaseInsensitive ) == 0 )
504 QString newAttributeString;
506 QStringList entryList = attribute.value().split(
';' );
507 QStringList::const_iterator entryIt = entryList.constBegin();
508 for ( ; entryIt != entryList.constEnd(); ++entryIt )
510 QStringList keyValueSplit = entryIt->split(
':' );
511 if ( keyValueSplit.size() < 2 )
515 QString key = keyValueSplit.at( 0 );
516 QString value = keyValueSplit.at( 1 );
517 if ( value.startsWith(
"param(fill" ) )
521 else if ( value.startsWith(
"param(outline)" ) )
523 value = outline.name();
525 else if ( value.startsWith(
"param(outline-width)" ) )
527 value = QString::number( outlineWidth );
530 if ( entryIt != entryList.constBegin() )
532 newAttributeString.append(
";" );
534 newAttributeString.append( key +
":" + value );
536 elem.setAttribute( attribute.name(), newAttributeString );
540 QString value = attribute.value();
541 if ( value.startsWith(
"param(fill)" ) )
543 elem.setAttribute( attribute.name(), fill.name() );
545 else if ( value.startsWith(
"param(outline)" ) )
547 elem.setAttribute( attribute.name(), outline.name() );
549 else if ( value.startsWith(
"param(outline-width)" ) )
551 elem.setAttribute( attribute.name(), QString::number( outlineWidth ) );
556 QDomNodeList childList = elem.childNodes();
557 int nChildren = childList.count();
558 for (
int i = 0; i < nChildren; ++i )
560 QDomElement childElem = childList.at( i ).toElement();
561 replaceElemParams( childElem, fill, outline, outlineWidth );
565 void QgsSvgCache::containsElemParams(
const QDomElement& elem,
bool& hasFillParam, QColor& defaultFill,
bool& hasOutlineParam, QColor& defaultOutline,
566 bool& hasOutlineWidthParam,
double& defaultOutlineWidth )
const
574 if ( hasFillParam && hasOutlineParam && hasOutlineWidthParam )
580 QDomNamedNodeMap attributes = elem.attributes();
581 int nAttributes = attributes.count();
583 QStringList valueSplit;
584 for (
int i = 0; i < nAttributes; ++i )
586 QDomAttr attribute = attributes.item( i ).toAttr();
587 if ( attribute.name().compare(
"style", Qt::CaseInsensitive ) == 0 )
590 QStringList entryList = attribute.value().split(
';' );
591 QStringList::const_iterator entryIt = entryList.constBegin();
592 for ( ; entryIt != entryList.constEnd(); ++entryIt )
594 QStringList keyValueSplit = entryIt->split(
':' );
595 if ( keyValueSplit.size() < 2 )
599 QString key = keyValueSplit.at( 0 );
600 QString value = keyValueSplit.at( 1 );
601 valueSplit = value.split(
" " );
602 if ( !hasFillParam && value.startsWith(
"param(fill)" ) )
605 if ( valueSplit.size() > 1 )
607 defaultFill = QColor( valueSplit.at( 1 ) );
610 else if ( !hasOutlineParam && value.startsWith(
"param(outline)" ) )
612 hasOutlineParam =
true;
613 if ( valueSplit.size() > 1 )
615 defaultOutline = QColor( valueSplit.at( 1 ) );
618 else if ( !hasOutlineWidthParam && value.startsWith(
"param(outline-width)" ) )
620 hasOutlineWidthParam =
true;
621 if ( valueSplit.size() > 1 )
623 defaultOutlineWidth = valueSplit.at( 1 ).toDouble();
630 QString value = attribute.value();
631 valueSplit = value.split(
" " );
632 if ( !hasFillParam && value.startsWith(
"param(fill)" ) )
635 if ( valueSplit.size() > 1 )
637 defaultFill = QColor( valueSplit.at( 1 ) );
640 else if ( !hasOutlineParam && value.startsWith(
"param(outline)" ) )
642 hasOutlineParam =
true;
643 if ( valueSplit.size() > 1 )
645 defaultOutline = QColor( valueSplit.at( 1 ) );
648 else if ( !hasOutlineWidthParam && value.startsWith(
"param(outline-width)" ) )
650 hasOutlineWidthParam =
true;
651 if ( valueSplit.size() > 1 )
653 defaultOutlineWidth = valueSplit.at( 1 ).toDouble();
660 QDomNodeList childList = elem.childNodes();
661 int nChildren = childList.count();
662 for (
int i = 0; i < nChildren; ++i )
664 QDomElement childElem = childList.at( i ).toElement();
665 containsElemParams( childElem, hasFillParam, defaultFill, hasOutlineParam, defaultOutline, hasOutlineWidthParam, defaultOutlineWidth );
672 mEntryLookup.remove( s, entry );
675 void QgsSvgCache::printEntryList()
677 QgsDebugMsg(
"****************svg cache entry list*************************" );
678 QgsDebugMsg(
"Cache size: " + QString::number( mTotalSize ) );
694 if ( mLeastRecentEntry == mMostRecentEntry )
699 while ( entry && ( mTotalSize > mMaximumSize ) )
705 mEntryLookup.remove( bkEntry->
file, bkEntry );
736 void QgsSvgCache::downloadProgress( qint64 bytesReceived, qint64 bytesTotal )
738 QString msg =
tr(
"%1 of %2 bytes of svg image downloaded." ).arg( bytesReceived ).arg( bytesTotal < 0 ? QString(
"unknown number of" ) : QString::number( bytesTotal ) );