mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-15 00:04:00 -04:00
[FEATURE] add support to SVG images from url.
Work done for Regione Toscana-SITA
This commit is contained in:
parent
bc75a39475
commit
454c732a3d
@ -34,7 +34,7 @@ class QgsSvgCacheEntry
|
||||
/**A cache for images / pictures derived from svg files. This class supports parameter replacement in svg files
|
||||
according to the svg params specification (http://www.w3.org/TR/2009/WD-SVGParamPrimer-20090616/). Supported are
|
||||
the parameters 'fill-color', 'pen-color', 'outline-width', 'stroke-width'. E.g. <circle fill="param(fill-color red)" stroke="param(pen-color black)" stroke-width="param(outline-width 1)"*/
|
||||
class QgsSvgCache
|
||||
class QgsSvgCache : QObject
|
||||
{
|
||||
%TypeHeaderCode
|
||||
#include <qgssvgcache.h>
|
||||
@ -56,7 +56,8 @@ class QgsSvgCache
|
||||
double& defaultOutlineWidth ) const;
|
||||
|
||||
protected:
|
||||
QgsSvgCache();
|
||||
//! protected constructor
|
||||
QgsSvgCache( QObject* parent = 0 );
|
||||
|
||||
/**Creates new cache entry and returns pointer to it*/
|
||||
QgsSvgCacheEntry* insertSVG( const QString& file, int size, const QColor& fill, const QColor& outline, double outlineWidth,
|
||||
|
@ -320,6 +320,7 @@ SET(QGIS_CORE_MOC_HDRS
|
||||
gps/qgsgpsdconnection.h
|
||||
|
||||
symbology-ng/qgscptcityarchive.h
|
||||
symbology-ng/qgssvgcache.h
|
||||
)
|
||||
|
||||
IF (WITH_INTERNAL_QEXTSERIALPORT)
|
||||
|
@ -309,13 +309,9 @@ QgsSVGFillSymbolLayer::~QgsSVGFillSymbolLayer()
|
||||
|
||||
void QgsSVGFillSymbolLayer::setSvgFilePath( const QString& svgPath )
|
||||
{
|
||||
QFile svgFile( svgPath );
|
||||
if ( svgFile.open( QFile::ReadOnly ) )
|
||||
{
|
||||
mSvgData = svgFile.readAll();
|
||||
mSvgData = QgsSvgCache::instance()->getImageData( svgPath );
|
||||
storeViewBox();
|
||||
|
||||
storeViewBox();
|
||||
}
|
||||
mSvgFilePath = svgPath;
|
||||
setDefaultSvgParams();
|
||||
}
|
||||
|
@ -674,7 +674,7 @@ void QgsSvgMarkerSymbolLayerV2::renderPoint( const QPointF& point, QgsSymbolV2Re
|
||||
}
|
||||
else
|
||||
{
|
||||
p->setOpacity( context.alpha( ) );
|
||||
p->setOpacity( context.alpha() );
|
||||
const QPicture& pct = QgsSvgCache::instance()->svgAsPicture( mPath, size, mFillColor, mOutlineColor, mOutlineWidth,
|
||||
context.renderContext().scaleFactor(), context.renderContext().rasterScaleFactor() );
|
||||
p->drawPicture( 0, 0, pct );
|
||||
@ -842,6 +842,26 @@ QString QgsSvgMarkerSymbolLayerV2::symbolNameToPath( QString name )
|
||||
if ( QFile( name ).exists() )
|
||||
return QFileInfo( name ).canonicalFilePath();
|
||||
|
||||
// or it might be an url...
|
||||
QUrl url( name );
|
||||
if ( url.isValid() )
|
||||
{
|
||||
if ( url.isLocalFile() )
|
||||
{
|
||||
// it's a url to a local file
|
||||
name = url.toLocalFile();
|
||||
if ( QFile( name ).exists() )
|
||||
{
|
||||
return QFileInfo( name ).canonicalFilePath();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// it's a url pointing to a online resource
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
// SVG symbol not found - probably a relative path was used
|
||||
|
||||
QStringList svgPaths = QgsApplication::svgPaths();
|
||||
|
@ -17,6 +17,11 @@
|
||||
|
||||
#include "qgssvgcache.h"
|
||||
#include "qgslogger.h"
|
||||
#include "qgsnetworkaccessmanager.h"
|
||||
#include "qgsmessagelog.h"
|
||||
#include <QApplication>
|
||||
#include <QCoreApplication>
|
||||
#include <QCursor>
|
||||
#include <QDomDocument>
|
||||
#include <QDomElement>
|
||||
#include <QFile>
|
||||
@ -24,6 +29,9 @@
|
||||
#include <QPainter>
|
||||
#include <QPicture>
|
||||
#include <QSvgRenderer>
|
||||
#include <QFileInfo>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkRequest>
|
||||
|
||||
QgsSvgCacheEntry::QgsSvgCacheEntry(): file( QString() ), size( 0 ), outlineWidth( 0 ), widthScaleFactor( 1.0 ), rasterScaleFactor( 1.0 ), fill( Qt::black ),
|
||||
outline( Qt::black ), image( 0 ), picture( 0 )
|
||||
@ -81,7 +89,11 @@ QgsSvgCache* QgsSvgCache::instance()
|
||||
return mInstance;
|
||||
}
|
||||
|
||||
QgsSvgCache::QgsSvgCache(): mTotalSize( 0 ), mLeastRecentEntry( 0 ), mMostRecentEntry( 0 )
|
||||
QgsSvgCache::QgsSvgCache( QObject *parent )
|
||||
: QObject( parent )
|
||||
, mTotalSize( 0 )
|
||||
, mLeastRecentEntry( 0 )
|
||||
, mMostRecentEntry( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
@ -163,14 +175,8 @@ void QgsSvgCache::containsParams( const QString& path, bool& hasFillParam, QColo
|
||||
defaultOutlineColor = QColor( Qt::black );
|
||||
defaultOutlineWidth = 1.0;
|
||||
|
||||
QFile svgFile( path );
|
||||
if ( !svgFile.open( QIODevice::ReadOnly ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QDomDocument svgDoc;
|
||||
if ( !svgDoc.setContent( &svgFile ) )
|
||||
if ( !svgDoc.setContent( getImageData( path ) ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
@ -186,14 +192,8 @@ void QgsSvgCache::replaceParamsAndCacheSvg( QgsSvgCacheEntry* entry )
|
||||
return;
|
||||
}
|
||||
|
||||
QFile svgFile( entry->file );
|
||||
if ( !svgFile.open( QIODevice::ReadOnly ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QDomDocument svgDoc;
|
||||
if ( !svgDoc.setContent( &svgFile ) )
|
||||
if ( !svgDoc.setContent( getImageData( entry->file ) ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
@ -206,6 +206,118 @@ void QgsSvgCache::replaceParamsAndCacheSvg( QgsSvgCacheEntry* entry )
|
||||
mTotalSize += entry->svgContent.size();
|
||||
}
|
||||
|
||||
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...
|
||||
QUrl svgUrl( path );
|
||||
if ( !svgUrl.isValid() )
|
||||
{
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
// check whether it's a url pointing to a local file
|
||||
if ( svgUrl.isLocalFile() )
|
||||
{
|
||||
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
|
||||
QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );
|
||||
while ( !reply->isFinished() )
|
||||
{
|
||||
QCoreApplication::processEvents( QEventLoop::ExcludeUserInputEvents, 500 );
|
||||
}
|
||||
QApplication::restoreOverrideCursor();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void QgsSvgCache::cacheImage( QgsSvgCacheEntry* entry )
|
||||
{
|
||||
if ( !entry )
|
||||
@ -553,3 +665,9 @@ void QgsSvgCache::takeEntryFromList( QgsSvgCacheEntry* entry )
|
||||
}
|
||||
}
|
||||
|
||||
void QgsSvgCache::downloadProgress( qint64 bytesReceived, qint64 bytesTotal )
|
||||
{
|
||||
QString msg = tr( "%1 of %2 bytes of svg image downloaded." ).arg( bytesReceived ).arg( bytesTotal < 0 ? QString( "unknown number of" ) : QString::number( bytesTotal ) );
|
||||
QgsDebugMsg( msg );
|
||||
emit statusChanged( msg );
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <QMap>
|
||||
#include <QMultiHash>
|
||||
#include <QString>
|
||||
#include <QUrl>
|
||||
|
||||
class QDomElement;
|
||||
class QImage;
|
||||
@ -59,8 +60,10 @@ class CORE_EXPORT QgsSvgCacheEntry
|
||||
/**A cache for images / pictures derived from svg files. This class supports parameter replacement in svg files
|
||||
according to the svg params specification (http://www.w3.org/TR/2009/WD-SVGParamPrimer-20090616/). Supported are
|
||||
the parameters 'fill-color', 'pen-color', 'outline-width', 'stroke-width'. E.g. <circle fill="param(fill-color red)" stroke="param(pen-color black)" stroke-width="param(outline-width 1)"*/
|
||||
class CORE_EXPORT QgsSvgCache
|
||||
class CORE_EXPORT QgsSvgCache : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
static QgsSvgCache* instance();
|
||||
@ -76,8 +79,16 @@ class CORE_EXPORT QgsSvgCache
|
||||
void containsParams( const QString& path, bool& hasFillParam, QColor& defaultFillColor, bool& hasOutlineParam, QColor& defaultOutlineColor, bool& hasOutlineWidthParam,
|
||||
double& defaultOutlineWidth ) const;
|
||||
|
||||
/**Get image data*/
|
||||
QByteArray getImageData( const QString &path ) const;
|
||||
|
||||
signals:
|
||||
/** Emit a signal to be caught by qgisapp and display a msg on status bar */
|
||||
void statusChanged( QString const & theStatusQString );
|
||||
|
||||
protected:
|
||||
QgsSvgCache();
|
||||
//! protected constructor
|
||||
QgsSvgCache( QObject * parent = 0 );
|
||||
|
||||
/**Creates new cache entry and returns pointer to it*/
|
||||
QgsSvgCacheEntry* insertSVG( const QString& file, int size, const QColor& fill, const QColor& outline, double outlineWidth,
|
||||
@ -96,6 +107,9 @@ class CORE_EXPORT QgsSvgCache
|
||||
//Removes entry from the ordered list (but does not delete the entry itself)
|
||||
void takeEntryFromList( QgsSvgCacheEntry* entry );
|
||||
|
||||
private slots:
|
||||
void downloadProgress( qint64, qint64 );
|
||||
|
||||
private:
|
||||
static QgsSvgCache* mInstance;
|
||||
|
||||
|
@ -683,12 +683,16 @@ void QgsSvgMarkerSymbolLayerV2Widget::setGuiForSvg( const QgsSvgMarkerSymbolLaye
|
||||
mBorderWidthSpinBox->blockSignals( true );
|
||||
mBorderWidthSpinBox->setValue( layer->outlineWidth() );
|
||||
mBorderWidthSpinBox->blockSignals( false );
|
||||
|
||||
}
|
||||
|
||||
|
||||
void QgsSvgMarkerSymbolLayerV2Widget::setSymbolLayer( QgsSymbolLayerV2* layer )
|
||||
{
|
||||
if ( !layer )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( layer->layerType() != "SvgMarker" )
|
||||
return;
|
||||
|
||||
@ -711,8 +715,6 @@ void QgsSvgMarkerSymbolLayerV2Widget::setSymbolLayer( QgsSymbolLayerV2* layer )
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
spinSize->setValue( mLayer->size() );
|
||||
spinAngle->setValue( mLayer->angle() );
|
||||
|
||||
@ -725,7 +727,6 @@ void QgsSvgMarkerSymbolLayerV2Widget::setSymbolLayer( QgsSymbolLayerV2* layer )
|
||||
spinOffsetY->blockSignals( false );
|
||||
|
||||
setGuiForSvg( mLayer );
|
||||
|
||||
}
|
||||
|
||||
QgsSymbolLayerV2* QgsSvgMarkerSymbolLayerV2Widget::symbolLayer()
|
||||
@ -790,6 +791,21 @@ void QgsSvgMarkerSymbolLayerV2Widget::on_mFileLineEdit_textEdited( const QString
|
||||
emit changed();
|
||||
}
|
||||
|
||||
void QgsSvgMarkerSymbolLayerV2Widget::on_mFileLineEdit_editingFinished()
|
||||
{
|
||||
if ( !QFileInfo( mFileLineEdit->text() ).exists() )
|
||||
{
|
||||
QUrl url( mFileLineEdit->text() );
|
||||
if ( !url.isValid() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
mLayer->setPath( mFileLineEdit->text() );
|
||||
setGuiForSvg( mLayer );
|
||||
emit changed();
|
||||
}
|
||||
|
||||
void QgsSvgMarkerSymbolLayerV2Widget::on_mChangeColorButton_clicked()
|
||||
{
|
||||
if ( !mLayer )
|
||||
@ -945,7 +961,7 @@ void QgsSVGFillSymbolLayerWidget::on_mTextureWidthSpinBox_valueChanged( double d
|
||||
}
|
||||
}
|
||||
|
||||
void QgsSVGFillSymbolLayerWidget::on_mSVGLineEdit_textChanged( const QString & text )
|
||||
void QgsSVGFillSymbolLayerWidget::on_mSVGLineEdit_textEdited( const QString & text )
|
||||
{
|
||||
if ( !mLayer )
|
||||
{
|
||||
@ -958,14 +974,39 @@ void QgsSVGFillSymbolLayerWidget::on_mSVGLineEdit_textChanged( const QString & t
|
||||
return;
|
||||
}
|
||||
mLayer->setSvgFilePath( text );
|
||||
emit changed();
|
||||
updateParamGui();
|
||||
emit changed();
|
||||
}
|
||||
|
||||
void QgsSVGFillSymbolLayerWidget::on_mSVGLineEdit_editingFinished()
|
||||
{
|
||||
if ( !mLayer )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QFileInfo fi( mSVGLineEdit->text() );
|
||||
if ( !fi.exists() )
|
||||
{
|
||||
QUrl url( mSVGLineEdit->text() );
|
||||
if ( !url.isValid() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
mLayer->setSvgFilePath( mSVGLineEdit->text() );
|
||||
updateParamGui();
|
||||
emit changed();
|
||||
}
|
||||
|
||||
void QgsSVGFillSymbolLayerWidget::setFile( const QModelIndex& item )
|
||||
{
|
||||
mSVGLineEdit->setText( item.data( Qt::UserRole ).toString() );
|
||||
QString file = item.data( Qt::UserRole ).toString();
|
||||
mLayer->setSvgFilePath( file );
|
||||
mSVGLineEdit->setText( file );
|
||||
|
||||
updateParamGui();
|
||||
emit changed();
|
||||
}
|
||||
|
||||
void QgsSVGFillSymbolLayerWidget::insertIcons()
|
||||
@ -1000,8 +1041,8 @@ void QgsSVGFillSymbolLayerWidget::on_mRotationSpinBox_valueChanged( double d )
|
||||
if ( mLayer )
|
||||
{
|
||||
mLayer->setAngle( d );
|
||||
emit changed();
|
||||
}
|
||||
emit changed();
|
||||
}
|
||||
|
||||
void QgsSVGFillSymbolLayerWidget::updateParamGui()
|
||||
|
@ -198,6 +198,7 @@ class GUI_EXPORT QgsSvgMarkerSymbolLayerV2Widget : public QgsSymbolLayerV2Widget
|
||||
void setOffset();
|
||||
void on_mFileToolButton_clicked();
|
||||
void on_mFileLineEdit_textEdited( const QString& text );
|
||||
void on_mFileLineEdit_editingFinished();
|
||||
void on_mChangeColorButton_clicked();
|
||||
void on_mChangeBorderColorButton_clicked();
|
||||
void on_mBorderWidthSpinBox_valueChanged( double d );
|
||||
@ -266,7 +267,8 @@ class GUI_EXPORT QgsSVGFillSymbolLayerWidget : public QgsSymbolLayerV2Widget, pr
|
||||
private slots:
|
||||
void on_mBrowseToolButton_clicked();
|
||||
void on_mTextureWidthSpinBox_valueChanged( double d );
|
||||
void on_mSVGLineEdit_textChanged( const QString & text );
|
||||
void on_mSVGLineEdit_textEdited( const QString & text );
|
||||
void on_mSVGLineEdit_editingFinished();
|
||||
void setFile( const QModelIndex& item );
|
||||
void populateIcons( const QModelIndex& item );
|
||||
void on_mRotationSpinBox_valueChanged( double d );
|
||||
|
Loading…
x
Reference in New Issue
Block a user