1 /*************************************************************************** 2 qgssvgcache.h 3 ------------------------------ 4 begin : 2011 5 copyright : (C) 2011 by Marco Hugentobler 6 email : marco dot hugentobler at sourcepole dot ch 7 ***************************************************************************/ 8 9 /*************************************************************************** 10 * * 11 * This program is free software; you can redistribute it and/or modify * 12 * it under the terms of the GNU General Public License as published by * 13 * the Free Software Foundation; either version 2 of the License, or * 14 * (at your option) any later version. * 15 * * 16 ***************************************************************************/ 17 18 #ifndef QGSSVGCACHE_H 19 #define QGSSVGCACHE_H 20 21 #include "qgsabstractcontentcache.h" 22 #include "qgis.h" 23 24 #include <QPicture> 25 26 class QDomElement; 27 28 #ifndef SIP_RUN 29 30 ///@cond PRIVATE 31 32 /** 33 * \ingroup core 34 * \class QgsSvgCacheEntry 35 */ 36 class CORE_EXPORT QgsSvgCacheEntry : public QgsAbstractContentCacheEntry 37 { 38 public: 39 40 /** 41 * Constructor. 42 * \param path Absolute path to SVG file (relative paths are not resolved). 43 * \param size 44 * \param strokeWidth width of stroke 45 * \param widthScaleFactor width scale factor 46 * \param fill color of fill 47 * \param stroke color of stroke 48 * \param fixedAspectRatio fixed aspect ratio (optional) 49 * \param parameters an optional map of parameters to dynamically replace content in the SVG 50 */ 51 QgsSvgCacheEntry( const QString &path, double size, double strokeWidth, double widthScaleFactor, const QColor &fill, const QColor &stroke, 52 double fixedAspectRatio = 0, const QMap<QString, QString> ¶meters = QMap<QString, QString>() ) ; 53 54 //! QgsSvgCacheEntry cannot be copied. 55 QgsSvgCacheEntry( const QgsSvgCacheEntry &rh ) = delete; 56 //! QgsSvgCacheEntry cannot be copied. 57 QgsSvgCacheEntry &operator=( const QgsSvgCacheEntry &rh ) = delete; 58 59 double size = 0.0; //size in pixels (cast to int for QImage) 60 double strokeWidth = 0; 61 double widthScaleFactor = 1.0; 62 63 //! Fixed aspect ratio 64 double fixedAspectRatio = 0; 65 66 /** 67 * SVG viewbox size. 68 * \since QGIS 2.14 69 */ 70 QSizeF viewboxSize; 71 72 QColor fill = Qt::black; 73 QColor stroke = Qt::black; 74 QMap<QString, QString> parameters; 75 76 std::unique_ptr< QImage > image; 77 std::unique_ptr< QPicture > picture; 78 //content (with params replaced) 79 QByteArray svgContent; 80 81 /** 82 * TRUE if the image represents a broken/missing path. 83 * 84 * \since QGIS 3.14 85 */ 86 bool isMissingImage = false; 87 88 bool isEqual( const QgsAbstractContentCacheEntry *other ) const override; 89 int dataSize() const override; 90 void dump() const override; 91 92 }; 93 94 ///@endcond 95 #endif 96 97 /** 98 * \ingroup core 99 * \brief A cache for images / pictures derived from SVG files 100 * 101 * This class supports parameter replacement in SVG files according to the SVG params specification 102 * (http://www.w3.org/TR/2009/WD-SVGParamPrimer-20090616/). 103 * 104 * Supported parameters are: 105 * 106 * - \a param(fill): fill color (with no opacity value) 107 * - \a param(fill-opacity): fill color opacity 108 * - \a param(outline): outline color (with no opacity value) 109 * - \a param(outline-opacity): outline color opacity 110 * - \a param(outline-width): width of outline strokes 111 * 112 * E.g: 113 * 114 * <circle fill="param(fill-color red)" stroke="param(pen-color black)" stroke-width="param(outline-width 1)" 115 * 116 * \note QgsSvgCache is not usually directly created, but rather accessed through QgsApplication::svgCache(). 117 */ 118 #ifdef SIP_RUN 119 class CORE_EXPORT QgsSvgCache : public QgsAbstractContentCacheBase // for sip we skip to the base class and avoid the template difficulty 120 { 121 #else 122 class CORE_EXPORT QgsSvgCache : public QgsAbstractContentCache< QgsSvgCacheEntry > 123 { 124 #endif 125 Q_OBJECT 126 127 public: 128 129 /** 130 * Constructor for QgsSvgCache. 131 */ 132 QgsSvgCache( QObject *parent SIP_TRANSFERTHIS = nullptr ); 133 134 /** 135 * Returns an SVG drawing as a QImage. 136 * 137 * \param path Absolute path to SVG file. 138 * \param size size of cached image 139 * \param fill color of fill 140 * \param stroke color of stroke 141 * \param strokeWidth width of stroke 142 * \param widthScaleFactor width scale factor 143 * \param fitsInCache 144 * \param fixedAspectRatio fixed aspect ratio (optional) 145 * \param blocking forces to wait for loading before returning image (optional). 146 * \param parameters is a map of parameters to dynamically replace content in SVG. 147 * 148 * \warning The \a blocking parameter must NEVER be TRUE from GUI based applications (like the main QGIS 149 * application) or crashes will result. Only for use in external scripts or QGIS server. 150 */ 151 QImage svgAsImage( const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, 152 double widthScaleFactor, bool &fitsInCache, double fixedAspectRatio = 0, bool blocking = false, 153 const QMap<QString, QString> ¶meters = QMap<QString, QString>() ); 154 155 /** 156 * Returns an SVG drawing as a QPicture. 157 * 158 * \param path Absolute path to SVG file. 159 * \param size size of cached image 160 * \param fill color of fill 161 * \param stroke color of stroke 162 * \param strokeWidth width of stroke 163 * \param widthScaleFactor width scale factor 164 * \param forceVectorOutput 165 * \param fixedAspectRatio fixed aspect ratio (optional) 166 * \param blocking forces to wait for loading before returning image (optional) 167 * \param parameters is a map of parameters to dynamically replace content in SVG. 168 * 169 * \note The returned QPicture contains the SVG file centered over the picture origin. I.e. if it is rendered 170 * using QPainter::drawPicture( QPointF( 5, 10 ), picture ) it will be drawn centered over the point (5, 10). 171 * Appropriate translation to the destination painter based on the picture's boundingRect may need to be applied 172 * if rendering the SVG using the top-left or other reference point is desired. 173 * 174 * \warning The \a blocking parameter must NEVER be TRUE from GUI based applications (like the main QGIS 175 * application) or crashes will result. Only for use in external scripts or QGIS server. 176 */ 177 QPicture svgAsPicture( const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, 178 double widthScaleFactor, bool forceVectorOutput = false, double fixedAspectRatio = 0, bool blocking = false, 179 const QMap<QString, QString> ¶meters = QMap<QString, QString>() ); 180 181 /** 182 * Calculates the viewbox size of a (possibly cached) SVG file. 183 * \param path Absolute path to SVG file. 184 * \param size size of cached image 185 * \param fill color of fill 186 * \param stroke color of stroke 187 * \param strokeWidth width of stroke 188 * \param widthScaleFactor width scale factor 189 * \param fixedAspectRatio fixed aspect ratio (optional) 190 * \param blocking forces to wait for loading before returning image (optional). 191 * \param parameters is a map of parameters to dynamically replace content in SVG. 192 * \returns viewbox size set in SVG file 193 * 194 * \warning The blocking parameter must NEVER be TRUE from GUI based applications (like the main QGIS 195 * application) or crashes will result. Only for use in external scripts or QGIS server. 196 * 197 * \since QGIS 2.14 198 */ 199 QSizeF svgViewboxSize( const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, 200 double widthScaleFactor, double fixedAspectRatio = 0, bool blocking = false, const QMap<QString, QString> ¶meters = QMap<QString, QString>() ); 201 202 /** 203 * Tests if an SVG file contains parameters for fill, stroke color, stroke width. If yes, possible default values are returned. If there are several 204 * default values in the SVG file, only the first one is considered. Blocking forces to wait for loading before returning image (optional). WARNING: the 205 * blocking parameter must NEVER be TRUE from GUI based applications (like the main QGIS application) or crashes will result. Only for use in external 206 * scripts or QGIS server. 207 */ 208 void containsParams( const QString &path, bool &hasFillParam, QColor &defaultFillColor, bool &hasStrokeParam, QColor &defaultStrokeColor, bool &hasStrokeWidthParam, 209 double &defaultStrokeWidth, bool blocking = false ) const; 210 211 /** 212 * Tests if an SVG file contains parameters for fill, stroke color, stroke width. If yes, possible default values are returned. If there are several 213 * default values in the SVG file, only the first one is considered. 214 * \param path path to SVG file 215 * \param hasFillParam will be TRUE if fill param present in SVG 216 * \param hasDefaultFillParam will be TRUE if fill param has a default value specified 217 * \param defaultFillColor will be set to default fill color specified in SVG, if present 218 * \param hasFillOpacityParam will be TRUE if fill opacity param present in SVG 219 * \param hasDefaultFillOpacity will be TRUE if fill opacity param has a default value specified 220 * \param defaultFillOpacity will be set to default fill opacity specified in SVG, if present 221 * \param hasStrokeParam will be TRUE if stroke param present in SVG 222 * \param hasDefaultStrokeColor will be TRUE if stroke param has a default value specified 223 * \param defaultStrokeColor will be set to default stroke color specified in SVG, if present 224 * \param hasStrokeWidthParam will be TRUE if stroke width param present in SVG 225 * \param hasDefaultStrokeWidth will be TRUE if stroke width param has a default value specified 226 * \param defaultStrokeWidth will be set to default stroke width specified in SVG, if present 227 * \param hasStrokeOpacityParam will be TRUE if stroke opacity param present in SVG 228 * \param hasDefaultStrokeOpacity will be TRUE if stroke opacity param has a default value specified 229 * \param defaultStrokeOpacity will be set to default stroke opacity specified in SVG, if present 230 * \param blocking forces to wait for loading before returning image (optional). 231 * 232 * \note Available in Python bindings as containsParamsV3 233 * 234 * \warning The \a blocking parameter must NEVER be TRUE from GUI based applications (like the main QGIS 235 * application) or crashes will result. Only for use in external scripts or QGIS server. 236 * 237 * \since QGIS 2.14 238 */ 239 void containsParams( const QString &path, bool &hasFillParam, bool &hasDefaultFillParam, QColor &defaultFillColor, 240 bool &hasFillOpacityParam, bool &hasDefaultFillOpacity, double &defaultFillOpacity, 241 bool &hasStrokeParam, bool &hasDefaultStrokeColor, QColor &defaultStrokeColor, 242 bool &hasStrokeWidthParam, bool &hasDefaultStrokeWidth, double &defaultStrokeWidth, 243 bool &hasStrokeOpacityParam, bool &hasDefaultStrokeOpacity, double &defaultStrokeOpacity, 244 bool blocking = false ) const SIP_PYNAME( containsParamsV3 ); 245 246 /** 247 * Gets the SVG content corresponding to the given \a path. 248 * 249 * \a path may be a local file, remote (HTTP) url, or a base 64 encoded string (with a "base64:" prefix). 250 * 251 * The class default missingContent byte array is returned if the \a path could not be resolved or is broken. If 252 * the \a path corresponds to a remote URL, then class default fetchingContent will be returned while the content 253 * is in the process of being fetched. 254 * The \a blocking boolean forces to wait for loading before returning result. The content is loaded 255 * in the same thread to ensure provided the remote content. 256 * 257 * \warning The \a blocking parameter must NEVER be TRUE from GUI based applications (like the main QGIS application) 258 * or crashes will result. Only for use in external scripts or QGIS server. 259 */ 260 QByteArray getImageData( const QString &path, bool blocking = false ) const; 261 262 /** 263 * Gets the SVG content corresponding to the given \a path. 264 * 265 * \a path may be a local file, remote (HTTP) url, or a base 64 encoded string (with a "base64:" prefix). 266 * 267 * The parameters \a size, \a strokeWidth for width of stroke, \a widthScaleFactor for width scale factor, 268 * \a fill for color of fill, \a stroke for color of stroke and \a fixedAspectRatio for fixed aspect ratio (optional) 269 * are needed to get the entry from cache or creates a new entry if it does not exist already. 270 * 271 * The \a blocking boolean forces to wait for loading before returning image. The content is loaded 272 * in the same thread to ensure provided the image. 273 * 274 * \warning The \a blocking parameter must NEVER be TRUE from GUI based applications (like the main QGIS application) 275 * or crashes will result. Only for use in external scripts or QGIS server. 276 */ 277 #ifndef SIP_RUN 278 QByteArray svgContent( const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, 279 double widthScaleFactor, double fixedAspectRatio = 0, bool blocking = false, const QMap<QString, QString> ¶meters = QMap<QString, QString>(), bool *isMissingImage = nullptr ); 280 #else 281 QByteArray svgContent( const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, 282 double widthScaleFactor, double fixedAspectRatio = 0, bool blocking = false, const QMap<QString, QString> ¶meters = QMap<QString, QString>() ); 283 #endif 284 285 signals: 286 287 /** 288 * Emit a signal to be caught by qgisapp and display a msg on status bar. 289 * \deprecated Deprecated since QGIS 3.6 -- no longer emitted. 290 */ 291 Q_DECL_DEPRECATED void statusChanged( const QString &statusQString ) SIP_DEPRECATED; 292 293 /** 294 * Emitted when the cache has finished retrieving an SVG file from a remote \a url. 295 * \since QGIS 3.2 296 */ 297 void remoteSvgFetched( const QString &url ); 298 299 protected: 300 301 bool checkReply( QNetworkReply *reply, const QString &path ) const override; 302 303 private: 304 305 void replaceParamsAndCacheSvg( QgsSvgCacheEntry *entry, bool blocking = false ); 306 void cacheImage( QgsSvgCacheEntry *entry ); 307 void cachePicture( QgsSvgCacheEntry *entry, bool forceVectorOutput = false ); 308 //! Returns entry from cache or creates a new entry if it does not exist already 309 QgsSvgCacheEntry *cacheEntry( const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, 310 double widthScaleFactor, double fixedAspectRatio = 0, const QMap<QString, QString> ¶meters = QMap<QString, QString>(), bool blocking = false, bool *isMissingImage = nullptr ); 311 312 //! Replaces parameters in elements of a dom node and calls method for all child nodes 313 void replaceElemParams( QDomElement &elem, const QColor &fill, const QColor &stroke, double strokeWidth, const QMap<QString, QString> ¶meters ); 314 315 void containsElemParams( const QDomElement &elem, 316 bool &hasFillParam, bool &hasDefaultFill, QColor &defaultFill, 317 bool &hasFillOpacityParam, bool &hasDefaultFillOpacity, double &defaultFillOpacity, 318 bool &hasStrokeParam, bool &hasDefaultStroke, QColor &defaultStroke, 319 bool &hasStrokeWidthParam, bool &hasDefaultStrokeWidth, double &defaultStrokeWidth, 320 bool &hasStrokeOpacityParam, bool &hasDefaultStrokeOpacity, double &defaultStrokeOpacity ) const SIP_PYNAME( containsParamsV3 ); 321 322 //! Calculates scaling for rendered image sizes to SVG logical sizes 323 double calcSizeScaleFactor( QgsSvgCacheEntry *entry, const QDomElement &docElem, QSizeF &viewboxSize ) const; 324 325 /** 326 * Returns the target size (in pixels) and calculates the \a viewBoxSize 327 * for a cache \a entry. 328 */ 329 QSize sizeForImage( const QgsSvgCacheEntry &entry, QSizeF &viewBoxSize, QSizeF &scaledSize ) const; 330 331 /** 332 * Returns a rendered image for a cached picture \a entry. 333 */ 334 QImage imageFromCachedPicture( const QgsSvgCacheEntry &entry ) const; 335 336 //! SVG content to be rendered if SVG file was not found. 337 QByteArray mMissingSvg; 338 339 QByteArray mFetchingSvg; 340 341 friend class TestQgsSvgCache; 342 }; 343 344 #endif // QGSSVGCACHE_H 345