25 #include <QApplication> 26 #include <QCoreApplication> 28 #include <QDomDocument> 29 #include <QDomElement> 34 #include <QSvgRenderer> 36 #include <QNetworkReply> 37 #include <QNetworkRequest> 43 , widthScaleFactor( 1.0 )
44 , rasterScaleFactor( 1.0 )
46 , outline(
Qt::black )
49 , nextEntry( nullptr )
50 , previousEntry( nullptr )
106 , mLeastRecentEntry( nullptr )
107 , mMostRecentEntry( nullptr )
109 mMissingSvg =
QString(
"<svg width='10' height='10'><text x='5' y='10' font-size='10' text-anchor='middle'>?</text></svg>" ).
toAscii();
114 qDeleteAll( mEntryLookup );
119 double widthScaleFactor,
double rasterScaleFactor,
bool& fitsInCache )
129 if ( !currentEntry->
image )
132 double hwRatio = 1.0;
137 long cachedDataSize = 0;
139 cachedDataSize +=
static_cast< int >( currentEntry->
size * currentEntry->
size * hwRatio * 32 );
140 if ( cachedDataSize > mMaximumSize / 2 )
143 delete currentEntry->
image;
144 currentEntry->
image =
nullptr;
160 return *( currentEntry->
image );
164 double widthScaleFactor,
double rasterScaleFactor,
bool forceVectorOutput )
178 return *( currentEntry->
picture );
182 double widthScaleFactor,
double rasterScaleFactor )
201 double widthScaleFactor,
double rasterScaleFactor )
210 mEntryLookup.
insert( file, entry );
213 if ( !mMostRecentEntry )
215 mLeastRecentEntry = entry;
216 mMostRecentEntry = entry;
225 mMostRecentEntry = entry;
233 bool& hasOutlineWidthParam,
double& defaultOutlineWidth )
const 235 bool hasDefaultFillColor =
false;
236 bool hasFillOpacityParam =
false;
237 bool hasDefaultFillOpacity =
false;
238 double defaultFillOpacity = 1.0;
239 bool hasDefaultOutlineColor =
false;
240 bool hasDefaultOutlineWidth =
false;
241 bool hasOutlineOpacityParam =
false;
242 bool hasDefaultOutlineOpacity =
false;
243 double defaultOutlineOpacity = 1.0;
245 containsParams( path, hasFillParam, hasDefaultFillColor, defaultFillColor,
246 hasFillOpacityParam, hasDefaultFillOpacity, defaultFillOpacity,
247 hasOutlineParam, hasDefaultOutlineColor, defaultOutlineColor,
248 hasOutlineWidthParam, hasDefaultOutlineWidth, defaultOutlineWidth,
249 hasOutlineOpacityParam, hasDefaultOutlineOpacity, defaultOutlineOpacity );
254 bool& hasFillParam,
bool& hasDefaultFillParam,
QColor& defaultFillColor,
255 bool& hasOutlineParam,
bool& hasDefaultOutlineColor,
QColor& defaultOutlineColor,
256 bool& hasOutlineWidthParam,
bool& hasDefaultOutlineWidth,
double& defaultOutlineWidth )
const 258 bool hasFillOpacityParam =
false;
259 bool hasDefaultFillOpacity =
false;
260 double defaultFillOpacity = 1.0;
261 bool hasOutlineOpacityParam =
false;
262 bool hasDefaultOutlineOpacity =
false;
263 double defaultOutlineOpacity = 1.0;
265 containsParams( path, hasFillParam, hasDefaultFillParam, defaultFillColor,
266 hasFillOpacityParam, hasDefaultFillOpacity, defaultFillOpacity,
267 hasOutlineParam, hasDefaultOutlineColor, defaultOutlineColor,
268 hasOutlineWidthParam, hasDefaultOutlineWidth, defaultOutlineWidth,
269 hasOutlineOpacityParam, hasDefaultOutlineOpacity, defaultOutlineOpacity );
273 bool& hasFillParam,
bool& hasDefaultFillParam,
QColor& defaultFillColor,
274 bool& hasFillOpacityParam,
bool& hasDefaultFillOpacity,
double& defaultFillOpacity,
275 bool& hasOutlineParam,
bool& hasDefaultOutlineColor,
QColor& defaultOutlineColor,
276 bool& hasOutlineWidthParam,
bool& hasDefaultOutlineWidth,
double& defaultOutlineWidth,
277 bool& hasOutlineOpacityParam,
bool& hasDefaultOutlineOpacity,
double& defaultOutlineOpacity )
const 279 hasFillParam =
false;
280 hasFillOpacityParam =
false;
281 hasOutlineParam =
false;
282 hasOutlineWidthParam =
false;
283 hasOutlineOpacityParam =
false;
284 defaultFillColor =
QColor( Qt::white );
285 defaultFillOpacity = 1.0;
286 defaultOutlineColor =
QColor( Qt::black );
287 defaultOutlineWidth = 0.2;
288 defaultOutlineOpacity = 1.0;
290 hasDefaultFillParam =
false;
291 hasDefaultFillOpacity =
false;
292 hasDefaultOutlineColor =
false;
293 hasDefaultOutlineWidth =
false;
294 hasDefaultOutlineOpacity =
false;
303 containsElemParams( docElem, hasFillParam, hasDefaultFillParam, defaultFillColor,
304 hasFillOpacityParam, hasDefaultFillOpacity, defaultFillOpacity,
305 hasOutlineParam, hasDefaultOutlineColor, defaultOutlineColor,
306 hasOutlineWidthParam, hasDefaultOutlineWidth, defaultOutlineWidth,
307 hasOutlineOpacityParam, hasDefaultOutlineOpacity, defaultOutlineOpacity );
327 double sizeScaleFactor = calcSizeScaleFactor( entry, docElem, viewboxSize );
377 if ( parts.
count() != 4 )
380 bool heightOk =
false;
381 double height = parts.
at( 3 ).toDouble( &heightOk );
383 bool widthOk =
false;
384 double width = parts.
at( 2 ).toDouble( &widthOk );
388 viewboxSize =
QSizeF( width, height );
389 return width / entry->
size;
398 QFile svgFile( path );
401 if ( svgFile.
open( QIODevice::ReadOnly ) )
424 if ( svgUrl.
scheme().
compare(
"file", Qt::CaseInsensitive ) == 0 )
429 if ( svgFile.
open( QIODevice::ReadOnly ) )
449 request.
setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache );
450 request.
setAttribute( QNetworkRequest::CacheSaveControlAttribute,
true );
453 connect( reply, SIGNAL( downloadProgress( qint64, qint64 ) ),
this, SLOT( downloadProgress( qint64, qint64 ) ) );
464 if ( reply->
error() != QNetworkReply::NoError )
472 QVariant redirect = reply->
attribute( QNetworkRequest::RedirectionTargetAttribute );
481 svgUrl = redirect.
toUrl();
488 QVariant phrase = reply->
attribute( QNetworkRequest::HttpReasonPhraseAttribute );
497 if ( !contentType.
startsWith(
"image/svg+xml", Qt::CaseInsensitive ) )
518 entry->
image =
nullptr;
521 double hwRatio = 1.0;
526 double wSize = entry->
size;
527 int wImgSize =
static_cast< int >( wSize );
532 double hSize = wSize * hwRatio;
533 int hImgSize =
static_cast< int >( hSize );
539 QImage* image =
new QImage( wImgSize, hImgSize, QImage::Format_ARGB32_Premultiplied );
550 s.
scale( wSize, hSize, Qt::KeepAspectRatio );
551 QRectF rect(( wImgSize - s.width() ) / 2, ( hImgSize - s.height() ) / 2, s.width(), s.height() );
555 entry->
image = image;
556 mTotalSize += ( image->
width() * image->
height() * 32 );
561 Q_UNUSED( forceVectorOutput );
574 double hwRatio = 1.0;
580 double wSize = entry->
size;
581 double hSize = wSize * hwRatio;
583 s.
scale( wSize, hSize, Qt::KeepAspectRatio );
584 rect =
QRectF( -s.width() / 2.0, -s.height() / 2.0, s.width(), s.height() );
593 double widthScaleFactor,
double rasterScaleFactor )
600 for ( ; entryIt != entries.
end(); ++entryIt )
616 currentEntry =
insertSVG( file, size, fill, outline, outlineWidth, widthScaleFactor, rasterScaleFactor );
621 if ( !mMostRecentEntry )
623 mMostRecentEntry = currentEntry;
624 mLeastRecentEntry = currentEntry;
628 mMostRecentEntry->
nextEntry = currentEntry;
631 mMostRecentEntry = currentEntry;
641 void QgsSvgCache::replaceElemParams(
QDomElement& elem,
const QColor& fill,
const QColor& outline,
double outlineWidth )
650 int nAttributes = attributes.
count();
651 for (
int i = 0; i < nAttributes; ++i )
655 if ( attribute.
name().
compare(
"style", Qt::CaseInsensitive ) == 0 )
662 for ( ; entryIt != entryList.
constEnd(); ++entryIt )
665 if ( keyValueSplit.
size() < 2 )
675 else if ( value.
startsWith(
"param(fill-opacity)" ) )
679 else if ( value.
startsWith(
"param(outline)" ) )
681 value = outline.
name();
683 else if ( value.
startsWith(
"param(outline-opacity)" ) )
687 else if ( value.
startsWith(
"param(outline-width)" ) )
694 newAttributeString.
append(
';' );
696 newAttributeString.
append( key +
':' + value );
707 else if ( value.
startsWith(
"param(fill-opacity)" ) )
711 else if ( value.
startsWith(
"param(outline)" ) )
715 else if ( value.
startsWith(
"param(outline-opacity)" ) )
719 else if ( value.
startsWith(
"param(outline-width)" ) )
727 int nChildren = childList.
count();
728 for (
int i = 0; i < nChildren; ++i )
731 replaceElemParams( childElem, fill, outline, outlineWidth );
735 void QgsSvgCache::containsElemParams(
const QDomElement& elem,
bool& hasFillParam,
bool& hasDefaultFill,
QColor& defaultFill,
736 bool& hasFillOpacityParam,
bool& hasDefaultFillOpacity,
double& defaultFillOpacity,
737 bool& hasOutlineParam,
bool& hasDefaultOutline,
QColor& defaultOutline,
738 bool& hasOutlineWidthParam,
bool& hasDefaultOutlineWidth,
double& defaultOutlineWidth,
739 bool& hasOutlineOpacityParam,
bool& hasDefaultOutlineOpacity,
double& defaultOutlineOpacity )
const 747 if ( hasFillParam && hasOutlineParam && hasOutlineWidthParam && hasFillOpacityParam && hasOutlineOpacityParam )
754 int nAttributes = attributes.
count();
757 for (
int i = 0; i < nAttributes; ++i )
760 if ( attribute.
name().
compare(
"style", Qt::CaseInsensitive ) == 0 )
765 for ( ; entryIt != entryList.
constEnd(); ++entryIt )
768 if ( keyValueSplit.
size() < 2 )
773 valueSplit = value.
split(
' ' );
774 if ( !hasFillParam && value.
startsWith(
"param(fill)" ) )
777 if ( valueSplit.
size() > 1 )
779 defaultFill =
QColor( valueSplit.
at( 1 ) );
780 hasDefaultFill =
true;
783 else if ( !hasFillOpacityParam && value.
startsWith(
"param(fill-opacity)" ) )
785 hasFillOpacityParam =
true;
786 if ( valueSplit.
size() > 1 )
789 double opacity = valueSplit.
at( 1 ).toDouble( &ok );
792 defaultFillOpacity = opacity;
793 hasDefaultFillOpacity =
true;
797 else if ( !hasOutlineParam && value.
startsWith(
"param(outline)" ) )
799 hasOutlineParam =
true;
800 if ( valueSplit.
size() > 1 )
802 defaultOutline =
QColor( valueSplit.
at( 1 ) );
803 hasDefaultOutline =
true;
806 else if ( !hasOutlineWidthParam && value.
startsWith(
"param(outline-width)" ) )
808 hasOutlineWidthParam =
true;
809 if ( valueSplit.
size() > 1 )
811 defaultOutlineWidth = valueSplit.
at( 1 ).toDouble();
812 hasDefaultOutlineWidth =
true;
815 else if ( !hasOutlineOpacityParam && value.
startsWith(
"param(outline-opacity)" ) )
817 hasOutlineOpacityParam =
true;
818 if ( valueSplit.
size() > 1 )
821 double opacity = valueSplit.
at( 1 ).toDouble( &ok );
824 defaultOutlineOpacity = opacity;
825 hasDefaultOutlineOpacity =
true;
834 valueSplit = value.
split(
' ' );
835 if ( !hasFillParam && value.
startsWith(
"param(fill)" ) )
838 if ( valueSplit.
size() > 1 )
840 defaultFill =
QColor( valueSplit.
at( 1 ) );
841 hasDefaultFill =
true;
844 else if ( !hasFillOpacityParam && value.
startsWith(
"param(fill-opacity)" ) )
846 hasFillOpacityParam =
true;
847 if ( valueSplit.
size() > 1 )
850 double opacity = valueSplit.
at( 1 ).toDouble( &ok );
853 defaultFillOpacity = opacity;
854 hasDefaultFillOpacity =
true;
858 else if ( !hasOutlineParam && value.
startsWith(
"param(outline)" ) )
860 hasOutlineParam =
true;
861 if ( valueSplit.
size() > 1 )
863 defaultOutline =
QColor( valueSplit.
at( 1 ) );
864 hasDefaultOutline =
true;
867 else if ( !hasOutlineWidthParam && value.
startsWith(
"param(outline-width)" ) )
869 hasOutlineWidthParam =
true;
870 if ( valueSplit.
size() > 1 )
872 defaultOutlineWidth = valueSplit.
at( 1 ).toDouble();
873 hasDefaultOutlineWidth =
true;
876 else if ( !hasOutlineOpacityParam && value.
startsWith(
"param(outline-opacity)" ) )
878 hasOutlineOpacityParam =
true;
879 if ( valueSplit.
size() > 1 )
882 double opacity = valueSplit.
at( 1 ).toDouble( &ok );
885 defaultOutlineOpacity = opacity;
886 hasDefaultOutlineOpacity =
true;
895 int nChildren = childList.
count();
896 for (
int i = 0; i < nChildren; ++i )
899 containsElemParams( childElem, hasFillParam, hasDefaultFill, defaultFill,
900 hasFillOpacityParam, hasDefaultFillOpacity, defaultFillOpacity,
901 hasOutlineParam, hasDefaultOutline, defaultOutline,
902 hasOutlineWidthParam, hasDefaultOutlineWidth, defaultOutlineWidth,
903 hasOutlineOpacityParam, hasDefaultOutlineOpacity, defaultOutlineOpacity );
910 mEntryLookup.
remove( s, entry );
913 void QgsSvgCache::printEntryList()
915 QgsDebugMsg(
"****************svg cache entry list*************************" );
932 if ( mLeastRecentEntry == mMostRecentEntry )
937 while ( entry && ( mTotalSize > mMaximumSize ) )
974 void QgsSvgCache::downloadProgress( qint64 bytesReceived, qint64 bytesTotal )
QgsSvgCacheEntry * previousEntry
QString & append(QChar ch)
QSizeF viewboxSize
SVG viewbox size.
void scale(qreal width, qreal height, Qt::AspectRatioMode mode)
QByteArray getImageData(const QString &path) const
Get image data.
void render(QPainter *painter)
const QPicture & svgAsPicture(const QString &file, double size, const QColor &fill, const QColor &outline, double outlineWidth, double widthScaleFactor, double rasterScaleFactor, bool forceVectorOutput=false)
Get SVG as QPicture&.
QSizeF svgViewboxSize(const QString &file, double size, const QColor &fill, const QColor &outline, double outlineWidth, double widthScaleFactor, double rasterScaleFactor)
Calculates the viewbox size of a (possibly cached) SVG file.
QString attribute(const QString &name, const QString &defValue) const
QString errorString() const
QgsSvgCacheEntry * cacheEntry(const QString &file, double size, const QColor &fill, const QColor &outline, double outlineWidth, double widthScaleFactor, double rasterScaleFactor)
Returns entry from cache or creates a new entry if it does not exist already.
QStringList split(const QString &sep, SplitBehavior behavior, Qt::CaseSensitivity cs) const
const T & at(int i) const
A cache for images / pictures derived from svg files.
void setFileName(const QString &name)
void containsParams(const QString &path, bool &hasFillParam, QColor &defaultFillColor, bool &hasOutlineParam, QColor &defaultOutlineColor, bool &hasOutlineWidthParam, double &defaultOutlineWidth) const
Tests if an svg file contains parameters for fill, outline color, outline width.
QDomElement documentElement() const
QDomNodeList childNodes() const
QString toString(QFlags< QUrl::FormattingOption > options) const
QString tr(const char *sourceText, const char *disambiguation, int n)
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Compare two doubles (but allow some difference)
static QgsSvgCache * instance()
QDomElement toElement() const
const QImage & svgAsImage(const QString &file, double size, const QColor &fill, const QColor &outline, double outlineWidth, double widthScaleFactor, double rasterScaleFactor, bool &fitsInCache)
Get SVG as QImage.
int dataSize() const
Return memory usage in bytes.
QString number(int n, int base)
int count(const T &value) const
void processEvents(QFlags< QEventLoop::ProcessEventsFlag > flags)
int toInt(bool *ok) const
void fill(uint pixelValue)
bool hasAttribute(const QString &name) const
void setAttribute(const QString &name, const QString &value)
int remove(const Key &key)
static QString symbolNameToPath(QString name)
Get symbol's path from its name.
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const
QByteArray & replace(int pos, int len, const char *after)
static void logMessage(const QString &message, const QString &tag=QString::null, MessageLevel level=WARNING)
add a message to the instance (and create it if necessary)
void statusChanged(const QString &theStatusQString)
Emit a signal to be caught by qgisapp and display a msg on status bar.
QgsSvgCache(QObject *parent=nullptr)
protected constructor
const QByteArray & svgContent(const QString &file, double size, const QColor &fill, const QColor &outline, double outlineWidth, double widthScaleFactor, double rasterScaleFactor)
Get SVG content.
void cachePicture(QgsSvgCacheEntry *entry, bool forceVectorOutput=false)
QHash< Key, T >::iterator insert(const Key &key, const T &value)
virtual bool open(QFlags< QIODevice::OpenModeFlag > mode)
QString toLocalFile() const
bool contains(QChar ch, Qt::CaseSensitivity cs) const
void trimToMaximumSize()
Removes the least used items until the maximum size is under the limit.
void takeEntryFromList(QgsSvgCacheEntry *entry)
QString lookupKey
Lookup key used by QgsSvgCache's hashtable (relative or absolute path). Needed for removal from the h...
void cacheImage(QgsSvgCacheEntry *entry)
void replaceParamsAndCacheSvg(QgsSvgCacheEntry *entry)
static QgsNetworkAccessManager * instance()
returns a pointer to the single instance
QDomElement firstChildElement(const QString &tagName) const
QVariant attribute(QNetworkRequest::Attribute code) const
void setAttribute(Attribute code, const QVariant &value)
QgsSvgCacheEntry * insertSVG(const QString &file, double size, const QColor &fill, const QColor &outline, double outlineWidth, double widthScaleFactor, double rasterScaleFactor)
Creates new cache entry and returns pointer to it.
NetworkError error() const
QNetworkReply * get(const QNetworkRequest &request)
const_iterator constEnd() const
const_iterator constBegin() const
QDomNode item(int index) const
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
int compare(const QString &other) const
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
bool operator==(const QgsSvgCacheEntry &other) const
Don't consider image, picture, last used timestamp for comparison.
QString file
Absolute path to SVG file.
QByteArray toAscii() const
QByteArray toByteArray(int indent) const
QDomNode at(int index) const
bool setContent(const QByteArray &data, bool namespaceProcessing, QString *errorMsg, int *errorLine, int *errorColumn)
QgsSvgCacheEntry * nextEntry
QDomNamedNodeMap attributes() const