QByteArray QgsSvgCache::getImageData( const QString &path ) const { // is it a path to local file? QFile svgFile( path ); if ( svgFile.exists() ) { if ( svgFile.open( QIODevice::ReadOnly ) ) { return svgFile.readAll(); } else { return QByteArray(); } } // maybe it's a url... if ( !path.contains( "://" ) ) // otherwise short, relative SVG paths might be considered URLs { return QByteArray(); } QUrl svgUrl( path ); if ( !svgUrl.isValid() ) { return QByteArray(); } // check whether it's a url pointing to a local file if ( svgUrl.scheme().compare( "file", Qt::CaseInsensitive ) == 0 ) { svgFile.setFileName( svgUrl.toLocalFile() ); if ( svgFile.exists() ) { if ( svgFile.open( QIODevice::ReadOnly ) ) { return svgFile.readAll(); } } // not found... return QByteArray(); } // the url points to a remote resource, download it! QNetworkReply *reply = 0; // The following code blocks until the file is downloaded... // TODO: use signals to get reply finished notification, in this moment // it's executed while rendering. while ( 1 ) { QgsDebugMsg( QString( "get svg: %1" ).arg( svgUrl.toString() ) ); QNetworkRequest request( svgUrl ); request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache ); request.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true ); reply = QgsNetworkAccessManager::instance()->get( request ); connect( reply, SIGNAL( downloadProgress( qint64, qint64 ) ), this, SLOT( downloadProgress( qint64, qint64 ) ) ); //emit statusChanged( tr( "Downloading svg." ) ); // wait until the image download finished // TODO: connect to the reply->finished() signal while ( !reply->isFinished() ) { QCoreApplication::processEvents( QEventLoop::ExcludeUserInputEvents, 500 ); } if ( reply->error() != QNetworkReply::NoError ) { QgsMessageLog::logMessage( tr( "SVG request failed [error: %1 - url: %2]" ).arg( reply->errorString() ).arg( reply->url().toString() ), tr( "SVG" ) ); reply->deleteLater(); return QByteArray(); } QVariant redirect = reply->attribute( QNetworkRequest::RedirectionTargetAttribute ); if ( redirect.isNull() ) { // neither network error nor redirection // TODO: cache the image break; } // do a new request to the redirect url svgUrl = redirect.toUrl(); reply->deleteLater(); } QVariant status = reply->attribute( QNetworkRequest::HttpStatusCodeAttribute ); if ( !status.isNull() && status.toInt() >= 400 ) { QVariant phrase = reply->attribute( QNetworkRequest::HttpReasonPhraseAttribute ); QgsMessageLog::logMessage( tr( "SVG request error [status: %1 - reason phrase: %2]" ).arg( status.toInt() ).arg( phrase.toString() ), tr( "SVG" ) ); reply->deleteLater(); return QByteArray(); } QString contentType = reply->header( QNetworkRequest::ContentTypeHeader ).toString(); QgsDebugMsg( "contentType: " + contentType ); if ( !contentType.startsWith( "image/svg+xml", Qt::CaseInsensitive ) ) { reply->deleteLater(); return QByteArray(); } // read the image data QByteArray ba = reply->readAll(); reply->deleteLater(); return ba; }
QByteArray QgsSvgCache::getImageData( const QString &path ) const { // is it a path to local file? QFile svgFile( path ); if ( svgFile.exists() ) { if ( svgFile.open( QIODevice::ReadOnly ) ) { return svgFile.readAll(); } else { return mMissingSvg; } } // maybe it's an embedded base64 string if ( path.startsWith( QLatin1String( "base64:" ), Qt::CaseInsensitive ) ) { QByteArray base64 = path.mid( 7 ).toLocal8Bit(); // strip 'base64:' prefix return QByteArray::fromBase64( base64, QByteArray::OmitTrailingEquals ); } // maybe it's a url... if ( !path.contains( QLatin1String( "://" ) ) ) // otherwise short, relative SVG paths might be considered URLs { return mMissingSvg; } QUrl svgUrl( path ); if ( !svgUrl.isValid() ) { return mMissingSvg; } // check whether it's a url pointing to a local file if ( svgUrl.scheme().compare( QLatin1String( "file" ), Qt::CaseInsensitive ) == 0 ) { svgFile.setFileName( svgUrl.toLocalFile() ); if ( svgFile.exists() ) { if ( svgFile.open( QIODevice::ReadOnly ) ) { return svgFile.readAll(); } } // not found... return mMissingSvg; } QMutexLocker locker( &mMutex ); // already a request in progress for this url if ( mPendingRemoteUrls.contains( path ) ) return mFetchingSvg; if ( mRemoteContentCache.contains( path ) ) { // already fetched this content - phew. Just return what we already got. return *mRemoteContentCache[ path ]; } mPendingRemoteUrls.insert( path ); //fire up task to fetch image in background QNetworkRequest request( svgUrl ); request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache ); request.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true ); QgsNetworkContentFetcherTask *task = new QgsNetworkContentFetcherTask( request ); connect( task, &QgsNetworkContentFetcherTask::fetched, this, [this, task, path] { QMutexLocker locker( &mMutex ); QNetworkReply *reply = task->reply(); if ( !reply ) { // canceled QMetaObject::invokeMethod( const_cast< QgsSvgCache * >( this ), "onRemoteSvgFetched", Qt::QueuedConnection, Q_ARG( QString, path ), Q_ARG( bool, false ) ); return; } if ( reply->error() != QNetworkReply::NoError ) { QgsMessageLog::logMessage( tr( "SVG request failed [error: %1 - url: %2]" ).arg( reply->errorString(), path ), tr( "SVG" ) ); return; } bool ok = true; QVariant status = reply->attribute( QNetworkRequest::HttpStatusCodeAttribute ); if ( !status.isNull() && status.toInt() >= 400 ) { QVariant phrase = reply->attribute( QNetworkRequest::HttpReasonPhraseAttribute ); QgsMessageLog::logMessage( tr( "SVG request error [status: %1 - reason phrase: %2] for %3" ).arg( status.toInt() ).arg( phrase.toString(), path ), tr( "SVG" ) ); mRemoteContentCache.insert( path, new QByteArray( mMissingSvg ) ); ok = false; } // we accept both real SVG mime types AND plain text types - because some sites // (notably github) serve up svgs as raw text QString contentType = reply->header( QNetworkRequest::ContentTypeHeader ).toString(); if ( !contentType.startsWith( QLatin1String( "image/svg+xml" ), Qt::CaseInsensitive ) && !contentType.startsWith( QLatin1String( "text/plain" ), Qt::CaseInsensitive ) ) { QgsMessageLog::logMessage( tr( "Unexpected MIME type %1 received for %2" ).arg( contentType, path ), tr( "SVG" ) ); mRemoteContentCache.insert( path, new QByteArray( mMissingSvg ) ); ok = false; } if ( ok ) { // read the image data mRemoteContentCache.insert( path, new QByteArray( reply->readAll() ) ); } QMetaObject::invokeMethod( const_cast< QgsSvgCache * >( this ), "onRemoteSvgFetched", Qt::QueuedConnection, Q_ARG( QString, path ), Q_ARG( bool, true ) ); } );