Beispiel #1
0
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;
}
Beispiel #2
0
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 ) );
  } );