LCOV - code coverage report
Current view: top level - core/symbology - qgssymbollayerutils.cpp (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 198 2852 6.9 %
Date: 2021-03-26 12:19:53 Functions: 0 0 -
Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : /***************************************************************************
       2                 :            :  qgssymbollayerutils.cpp
       3                 :            :  ---------------------
       4                 :            :  begin                : November 2009
       5                 :            :  copyright            : (C) 2009 by Martin Dobias
       6                 :            :  email                : wonder dot sk at gmail dot com
       7                 :            :  ***************************************************************************
       8                 :            :  *                                                                         *
       9                 :            :  *   This program is free software; you can redistribute it and/or modify  *
      10                 :            :  *   it under the terms of the GNU General Public License as published by  *
      11                 :            :  *   the Free Software Foundation; either version 2 of the License, or     *
      12                 :            :  *   (at your option) any later version.                                   *
      13                 :            :  *                                                                         *
      14                 :            :  ***************************************************************************/
      15                 :            : 
      16                 :            : #include "qgssymbollayerutils.h"
      17                 :            : 
      18                 :            : #include "qgssymbollayer.h"
      19                 :            : #include "qgssymbollayerregistry.h"
      20                 :            : #include "qgssymbol.h"
      21                 :            : #include "qgscolorramp.h"
      22                 :            : #include "qgsexpression.h"
      23                 :            : #include "qgsexpressionnode.h"
      24                 :            : #include "qgspainteffect.h"
      25                 :            : #include "qgspainteffectregistry.h"
      26                 :            : #include "qgsapplication.h"
      27                 :            : #include "qgspathresolver.h"
      28                 :            : #include "qgsproject.h"
      29                 :            : #include "qgsogcutils.h"
      30                 :            : #include "qgslogger.h"
      31                 :            : #include "qgsreadwritecontext.h"
      32                 :            : #include "qgsrendercontext.h"
      33                 :            : #include "qgsunittypes.h"
      34                 :            : #include "qgsexpressioncontextutils.h"
      35                 :            : #include "qgseffectstack.h"
      36                 :            : #include "qgsstyleentityvisitor.h"
      37                 :            : #include "qgsrenderer.h"
      38                 :            : #include "qgsxmlutils.h"
      39                 :            : 
      40                 :            : #include <QColor>
      41                 :            : #include <QFont>
      42                 :            : #include <QDomDocument>
      43                 :            : #include <QDomNode>
      44                 :            : #include <QDomElement>
      45                 :            : #include <QIcon>
      46                 :            : #include <QPainter>
      47                 :            : #include <QSettings>
      48                 :            : #include <QRegExp>
      49                 :            : #include <QPicture>
      50                 :            : #include <QUrl>
      51                 :            : #include <QUrlQuery>
      52                 :            : #include <QMimeData>
      53                 :            : #include <QRegularExpression>
      54                 :            : 
      55                 :            : #define POINTS_TO_MM 2.83464567
      56                 :            : 
      57                 :          0 : QString QgsSymbolLayerUtils::encodeColor( const QColor &color )
      58                 :            : {
      59                 :          0 :   return QStringLiteral( "%1,%2,%3,%4" ).arg( color.red() ).arg( color.green() ).arg( color.blue() ).arg( color.alpha() );
      60                 :          0 : }
      61                 :            : 
      62                 :       3830 : QColor QgsSymbolLayerUtils::decodeColor( const QString &str )
      63                 :            : {
      64                 :       3830 :   const QStringList lst = str.split( ',' );
      65                 :       3830 :   if ( lst.count() < 3 )
      66                 :            :   {
      67                 :          0 :     return QColor( str );
      68                 :            :   }
      69                 :            :   int red, green, blue, alpha;
      70                 :       3830 :   red = lst[0].toInt();
      71                 :       3830 :   green = lst[1].toInt();
      72                 :       3830 :   blue = lst[2].toInt();
      73                 :       3830 :   alpha = 255;
      74                 :       3830 :   if ( lst.count() > 3 )
      75                 :            :   {
      76                 :       3830 :     alpha = lst[3].toInt();
      77                 :       3830 :   }
      78                 :       3830 :   return QColor( red, green, blue, alpha );
      79                 :       3830 : }
      80                 :            : 
      81                 :          0 : QString QgsSymbolLayerUtils::encodeSldAlpha( int alpha )
      82                 :            : {
      83                 :          0 :   return QString::number( alpha / 255.0, 'g', 2 );
      84                 :            : }
      85                 :            : 
      86                 :          0 : int QgsSymbolLayerUtils::decodeSldAlpha( const QString &str )
      87                 :            : {
      88                 :            :   bool ok;
      89                 :          0 :   double alpha = str.toDouble( &ok );
      90                 :          0 :   if ( !ok || alpha > 1 )
      91                 :          0 :     alpha = 255;
      92                 :          0 :   else if ( alpha < 0 )
      93                 :          0 :     alpha = 0;
      94                 :          0 :   return alpha * 255;
      95                 :            : }
      96                 :            : 
      97                 :          0 : QString QgsSymbolLayerUtils::encodeSldFontStyle( QFont::Style style )
      98                 :            : {
      99                 :          0 :   switch ( style )
     100                 :            :   {
     101                 :            :     case QFont::StyleNormal:
     102                 :          0 :       return QStringLiteral( "normal" );
     103                 :            :     case QFont::StyleItalic:
     104                 :          0 :       return QStringLiteral( "italic" );
     105                 :            :     case QFont::StyleOblique:
     106                 :          0 :       return QStringLiteral( "oblique" );
     107                 :            :     default:
     108                 :          0 :       return QString();
     109                 :            :   }
     110                 :          0 : }
     111                 :            : 
     112                 :          0 : QFont::Style QgsSymbolLayerUtils::decodeSldFontStyle( const QString &str )
     113                 :            : {
     114                 :          0 :   if ( str == QLatin1String( "normal" ) ) return QFont::StyleNormal;
     115                 :          0 :   if ( str == QLatin1String( "italic" ) ) return QFont::StyleItalic;
     116                 :          0 :   if ( str == QLatin1String( "oblique" ) ) return QFont::StyleOblique;
     117                 :          0 :   return QFont::StyleNormal;
     118                 :          0 : }
     119                 :            : 
     120                 :          0 : QString QgsSymbolLayerUtils::encodeSldFontWeight( int weight )
     121                 :            : {
     122                 :          0 :   if ( weight == 50 ) return QStringLiteral( "normal" );
     123                 :          0 :   if ( weight == 75 ) return QStringLiteral( "bold" );
     124                 :            : 
     125                 :            :   // QFont::Weight is between 0 and 99
     126                 :            :   // CSS font-weight is between 100 and 900
     127                 :          0 :   if ( weight < 0 ) return QStringLiteral( "100" );
     128                 :          0 :   if ( weight > 99 ) return QStringLiteral( "900" );
     129                 :          0 :   return QString::number( weight * 800 / 99 + 100 );
     130                 :          0 : }
     131                 :            : 
     132                 :          0 : int QgsSymbolLayerUtils::decodeSldFontWeight( const QString &str )
     133                 :            : {
     134                 :            :   bool ok;
     135                 :          0 :   int weight = str.toInt( &ok );
     136                 :          0 :   if ( !ok )
     137                 :          0 :     return static_cast< int >( QFont::Normal );
     138                 :            : 
     139                 :            :   // CSS font-weight is between 100 and 900
     140                 :            :   // QFont::Weight is between 0 and 99
     141                 :          0 :   if ( weight > 900 ) return 99;
     142                 :          0 :   if ( weight < 100 ) return 0;
     143                 :          0 :   return ( weight - 100 ) * 99 / 800;
     144                 :          0 : }
     145                 :            : 
     146                 :          0 : QString QgsSymbolLayerUtils::encodePenStyle( Qt::PenStyle style )
     147                 :            : {
     148                 :          0 :   switch ( style )
     149                 :            :   {
     150                 :            :     case Qt::NoPen:
     151                 :          0 :       return QStringLiteral( "no" );
     152                 :            :     case Qt::SolidLine:
     153                 :          0 :       return QStringLiteral( "solid" );
     154                 :            :     case Qt::DashLine:
     155                 :          0 :       return QStringLiteral( "dash" );
     156                 :            :     case Qt::DotLine:
     157                 :          0 :       return QStringLiteral( "dot" );
     158                 :            :     case Qt::DashDotLine:
     159                 :          0 :       return QStringLiteral( "dash dot" );
     160                 :            :     case Qt::DashDotDotLine:
     161                 :          0 :       return QStringLiteral( "dash dot dot" );
     162                 :            :     default:
     163                 :          0 :       return QStringLiteral( "???" );
     164                 :            :   }
     165                 :          0 : }
     166                 :            : 
     167                 :        750 : Qt::PenStyle QgsSymbolLayerUtils::decodePenStyle( const QString &str )
     168                 :            : {
     169                 :        750 :   if ( str == QLatin1String( "no" ) ) return Qt::NoPen;
     170                 :        720 :   if ( str == QLatin1String( "solid" ) ) return Qt::SolidLine;
     171                 :          0 :   if ( str == QLatin1String( "dash" ) ) return Qt::DashLine;
     172                 :          0 :   if ( str == QLatin1String( "dot" ) ) return Qt::DotLine;
     173                 :          0 :   if ( str == QLatin1String( "dash dot" ) ) return Qt::DashDotLine;
     174                 :          0 :   if ( str == QLatin1String( "dash dot dot" ) ) return Qt::DashDotDotLine;
     175                 :          0 :   return Qt::SolidLine;
     176                 :        750 : }
     177                 :            : 
     178                 :          0 : QString QgsSymbolLayerUtils::encodePenJoinStyle( Qt::PenJoinStyle style )
     179                 :            : {
     180                 :          0 :   switch ( style )
     181                 :            :   {
     182                 :            :     case Qt::BevelJoin:
     183                 :          0 :       return QStringLiteral( "bevel" );
     184                 :            :     case Qt::MiterJoin:
     185                 :          0 :       return QStringLiteral( "miter" );
     186                 :            :     case Qt::RoundJoin:
     187                 :          0 :       return QStringLiteral( "round" );
     188                 :            :     default:
     189                 :          0 :       return QStringLiteral( "???" );
     190                 :            :   }
     191                 :          0 : }
     192                 :            : 
     193                 :        750 : Qt::PenJoinStyle QgsSymbolLayerUtils::decodePenJoinStyle( const QString &str )
     194                 :            : {
     195                 :        750 :   const QString cleaned = str.toLower().trimmed();
     196                 :        750 :   if ( cleaned == QLatin1String( "bevel" ) )
     197                 :        645 :     return Qt::BevelJoin;
     198                 :        105 :   if ( cleaned == QLatin1String( "miter" ) )
     199                 :          5 :     return Qt::MiterJoin;
     200                 :        100 :   if ( cleaned == QLatin1String( "round" ) )
     201                 :        100 :     return Qt::RoundJoin;
     202                 :          0 :   return Qt::BevelJoin;
     203                 :        750 : }
     204                 :            : 
     205                 :          0 : QString QgsSymbolLayerUtils::encodeSldLineJoinStyle( Qt::PenJoinStyle style )
     206                 :            : {
     207                 :          0 :   switch ( style )
     208                 :            :   {
     209                 :            :     case Qt::BevelJoin:
     210                 :          0 :       return QStringLiteral( "bevel" );
     211                 :            :     case Qt::MiterJoin:
     212                 :          0 :       return QStringLiteral( "mitre" );  //#spellok
     213                 :            :     case Qt::RoundJoin:
     214                 :          0 :       return QStringLiteral( "round" );
     215                 :            :     default:
     216                 :          0 :       return QString();
     217                 :            :   }
     218                 :          0 : }
     219                 :            : 
     220                 :          0 : Qt::PenJoinStyle QgsSymbolLayerUtils::decodeSldLineJoinStyle( const QString &str )
     221                 :            : {
     222                 :          0 :   if ( str == QLatin1String( "bevel" ) ) return Qt::BevelJoin;
     223                 :          0 :   if ( str == QLatin1String( "mitre" ) ) return Qt::MiterJoin;  //#spellok
     224                 :          0 :   if ( str == QLatin1String( "round" ) ) return Qt::RoundJoin;
     225                 :          0 :   return Qt::BevelJoin;
     226                 :          0 : }
     227                 :            : 
     228                 :          0 : QString QgsSymbolLayerUtils::encodePenCapStyle( Qt::PenCapStyle style )
     229                 :            : {
     230                 :          0 :   switch ( style )
     231                 :            :   {
     232                 :            :     case Qt::SquareCap:
     233                 :          0 :       return QStringLiteral( "square" );
     234                 :            :     case Qt::FlatCap:
     235                 :          0 :       return QStringLiteral( "flat" );
     236                 :            :     case Qt::RoundCap:
     237                 :          0 :       return QStringLiteral( "round" );
     238                 :            :     default:
     239                 :          0 :       return QStringLiteral( "???" );
     240                 :            :   }
     241                 :          0 : }
     242                 :            : 
     243                 :        493 : Qt::PenCapStyle QgsSymbolLayerUtils::decodePenCapStyle( const QString &str )
     244                 :            : {
     245                 :        493 :   if ( str == QLatin1String( "square" ) ) return Qt::SquareCap;
     246                 :         85 :   if ( str == QLatin1String( "flat" ) ) return Qt::FlatCap;
     247                 :         85 :   if ( str == QLatin1String( "round" ) ) return Qt::RoundCap;
     248                 :          0 :   return Qt::SquareCap;
     249                 :        493 : }
     250                 :            : 
     251                 :          0 : QString QgsSymbolLayerUtils::encodeSldLineCapStyle( Qt::PenCapStyle style )
     252                 :            : {
     253                 :          0 :   switch ( style )
     254                 :            :   {
     255                 :            :     case Qt::SquareCap:
     256                 :          0 :       return QStringLiteral( "square" );
     257                 :            :     case Qt::FlatCap:
     258                 :          0 :       return QStringLiteral( "butt" );
     259                 :            :     case Qt::RoundCap:
     260                 :          0 :       return QStringLiteral( "round" );
     261                 :            :     default:
     262                 :          0 :       return QString();
     263                 :            :   }
     264                 :          0 : }
     265                 :            : 
     266                 :          0 : Qt::PenCapStyle QgsSymbolLayerUtils::decodeSldLineCapStyle( const QString &str )
     267                 :            : {
     268                 :          0 :   if ( str == QLatin1String( "square" ) ) return Qt::SquareCap;
     269                 :          0 :   if ( str == QLatin1String( "butt" ) ) return Qt::FlatCap;
     270                 :          0 :   if ( str == QLatin1String( "round" ) ) return Qt::RoundCap;
     271                 :          0 :   return Qt::SquareCap;
     272                 :          0 : }
     273                 :            : 
     274                 :          0 : QString QgsSymbolLayerUtils::encodeBrushStyle( Qt::BrushStyle style )
     275                 :            : {
     276                 :          0 :   switch ( style )
     277                 :            :   {
     278                 :            :     case Qt::SolidPattern :
     279                 :          0 :       return QStringLiteral( "solid" );
     280                 :            :     case Qt::HorPattern :
     281                 :          0 :       return QStringLiteral( "horizontal" );
     282                 :            :     case Qt::VerPattern :
     283                 :          0 :       return QStringLiteral( "vertical" );
     284                 :            :     case Qt::CrossPattern :
     285                 :          0 :       return QStringLiteral( "cross" );
     286                 :            :     case Qt::BDiagPattern :
     287                 :          0 :       return QStringLiteral( "b_diagonal" );
     288                 :            :     case Qt::FDiagPattern :
     289                 :          0 :       return  QStringLiteral( "f_diagonal" );
     290                 :            :     case Qt::DiagCrossPattern :
     291                 :          0 :       return QStringLiteral( "diagonal_x" );
     292                 :            :     case Qt::Dense1Pattern  :
     293                 :          0 :       return QStringLiteral( "dense1" );
     294                 :            :     case Qt::Dense2Pattern  :
     295                 :          0 :       return QStringLiteral( "dense2" );
     296                 :            :     case Qt::Dense3Pattern  :
     297                 :          0 :       return QStringLiteral( "dense3" );
     298                 :            :     case Qt::Dense4Pattern  :
     299                 :          0 :       return QStringLiteral( "dense4" );
     300                 :            :     case Qt::Dense5Pattern  :
     301                 :          0 :       return QStringLiteral( "dense5" );
     302                 :            :     case Qt::Dense6Pattern  :
     303                 :          0 :       return QStringLiteral( "dense6" );
     304                 :            :     case Qt::Dense7Pattern  :
     305                 :          0 :       return QStringLiteral( "dense7" );
     306                 :            :     case Qt::NoBrush :
     307                 :          0 :       return QStringLiteral( "no" );
     308                 :            :     default:
     309                 :          0 :       return QStringLiteral( "???" );
     310                 :            :   }
     311                 :          0 : }
     312                 :            : 
     313                 :        165 : Qt::BrushStyle QgsSymbolLayerUtils::decodeBrushStyle( const QString &str )
     314                 :            : {
     315                 :        165 :   if ( str == QLatin1String( "solid" ) ) return Qt::SolidPattern;
     316                 :         50 :   if ( str == QLatin1String( "horizontal" ) ) return Qt::HorPattern;
     317                 :         50 :   if ( str == QLatin1String( "vertical" ) ) return Qt::VerPattern;
     318                 :         50 :   if ( str == QLatin1String( "cross" ) ) return Qt::CrossPattern;
     319                 :         50 :   if ( str == QLatin1String( "b_diagonal" ) ) return Qt::BDiagPattern;
     320                 :         50 :   if ( str == QLatin1String( "f_diagonal" ) ) return Qt::FDiagPattern;
     321                 :         50 :   if ( str == QLatin1String( "diagonal_x" ) ) return Qt::DiagCrossPattern;
     322                 :         50 :   if ( str == QLatin1String( "dense1" ) ) return Qt::Dense1Pattern;
     323                 :         50 :   if ( str == QLatin1String( "dense2" ) ) return Qt::Dense2Pattern;
     324                 :         50 :   if ( str == QLatin1String( "dense3" ) ) return Qt::Dense3Pattern;
     325                 :         50 :   if ( str == QLatin1String( "dense4" ) ) return Qt::Dense4Pattern;
     326                 :         50 :   if ( str == QLatin1String( "dense5" ) ) return Qt::Dense5Pattern;
     327                 :         50 :   if ( str == QLatin1String( "dense6" ) ) return Qt::Dense6Pattern;
     328                 :         50 :   if ( str == QLatin1String( "dense7" ) ) return Qt::Dense7Pattern;
     329                 :         50 :   if ( str == QLatin1String( "no" ) ) return Qt::NoBrush;
     330                 :          0 :   return Qt::SolidPattern;
     331                 :        165 : }
     332                 :            : 
     333                 :          0 : QString QgsSymbolLayerUtils::encodeSldBrushStyle( Qt::BrushStyle style )
     334                 :            : {
     335                 :          0 :   switch ( style )
     336                 :            :   {
     337                 :            :     case Qt::CrossPattern:
     338                 :          0 :       return QStringLiteral( "cross" );
     339                 :            :     case Qt::DiagCrossPattern:
     340                 :          0 :       return QStringLiteral( "x" );
     341                 :            : 
     342                 :            :     /* The following names are taken from the presentation "GeoServer
     343                 :            :      * Cartographic Rendering" by Andrea Aime at the FOSS4G 2010.
     344                 :            :      * (see http://2010.foss4g.org/presentations/3588.pdf)
     345                 :            :      */
     346                 :            :     case Qt::HorPattern:
     347                 :          0 :       return QStringLiteral( "horline" );
     348                 :            :     case Qt::VerPattern:
     349                 :          0 :       return QStringLiteral( "line" );
     350                 :            :     case Qt::BDiagPattern:
     351                 :          0 :       return QStringLiteral( "slash" );
     352                 :            :     case Qt::FDiagPattern:
     353                 :          0 :       return QStringLiteral( "backslash" );
     354                 :            : 
     355                 :            :     /* define the other names following the same pattern used above */
     356                 :            :     case Qt::Dense1Pattern:
     357                 :            :     case Qt::Dense2Pattern:
     358                 :            :     case Qt::Dense3Pattern:
     359                 :            :     case Qt::Dense4Pattern:
     360                 :            :     case Qt::Dense5Pattern:
     361                 :            :     case Qt::Dense6Pattern:
     362                 :            :     case Qt::Dense7Pattern:
     363                 :          0 :       return QStringLiteral( "brush://%1" ).arg( encodeBrushStyle( style ) );
     364                 :            : 
     365                 :            :     default:
     366                 :          0 :       return QString();
     367                 :            :   }
     368                 :          0 : }
     369                 :            : 
     370                 :          0 : Qt::BrushStyle QgsSymbolLayerUtils::decodeSldBrushStyle( const QString &str )
     371                 :            : {
     372                 :          0 :   if ( str == QLatin1String( "horline" ) ) return Qt::HorPattern;
     373                 :          0 :   if ( str == QLatin1String( "line" ) ) return Qt::VerPattern;
     374                 :          0 :   if ( str == QLatin1String( "cross" ) ) return Qt::CrossPattern;
     375                 :          0 :   if ( str == QLatin1String( "slash" ) ) return Qt::BDiagPattern;
     376                 :          0 :   if ( str == QLatin1String( "backshash" ) ) return Qt::FDiagPattern;
     377                 :          0 :   if ( str == QLatin1String( "x" ) ) return Qt::DiagCrossPattern;
     378                 :            : 
     379                 :          0 :   if ( str.startsWith( QLatin1String( "brush://" ) ) )
     380                 :          0 :     return decodeBrushStyle( str.mid( 8 ) );
     381                 :            : 
     382                 :          0 :   return Qt::NoBrush;
     383                 :          0 : }
     384                 :            : 
     385                 :          0 : QgsArrowSymbolLayer::HeadType QgsSymbolLayerUtils::decodeArrowHeadType( const QVariant &value, bool *ok )
     386                 :            : {
     387                 :          0 :   if ( ok )
     388                 :          0 :     *ok = true;
     389                 :            : 
     390                 :          0 :   bool intOk = false;
     391                 :          0 :   QString s = value.toString().toLower().trimmed();
     392                 :          0 :   if ( s == QLatin1String( "single" ) )
     393                 :          0 :     return QgsArrowSymbolLayer::HeadSingle;
     394                 :          0 :   else if ( s == QLatin1String( "reversed" ) )
     395                 :          0 :     return QgsArrowSymbolLayer::HeadReversed;
     396                 :          0 :   else if ( s == QLatin1String( "double" ) )
     397                 :          0 :     return QgsArrowSymbolLayer::HeadDouble;
     398                 :          0 :   else if ( value.toInt() == 1 )
     399                 :          0 :     return QgsArrowSymbolLayer::HeadReversed;
     400                 :          0 :   else if ( value.toInt() == 2 )
     401                 :          0 :     return QgsArrowSymbolLayer::HeadDouble;
     402                 :          0 :   else if ( value.toInt( &intOk ) == 0 && intOk )
     403                 :          0 :     return QgsArrowSymbolLayer::HeadSingle;
     404                 :            : 
     405                 :          0 :   if ( ok )
     406                 :          0 :     *ok = false;
     407                 :          0 :   return QgsArrowSymbolLayer::HeadSingle;
     408                 :          0 : }
     409                 :            : 
     410                 :          0 : QgsArrowSymbolLayer::ArrowType QgsSymbolLayerUtils::decodeArrowType( const QVariant &value, bool *ok )
     411                 :            : {
     412                 :          0 :   if ( ok )
     413                 :          0 :     *ok = true;
     414                 :            : 
     415                 :          0 :   bool intOk = false;
     416                 :          0 :   QString s = value.toString().toLower().trimmed();
     417                 :          0 :   if ( s == QLatin1String( "plain" ) )
     418                 :          0 :     return QgsArrowSymbolLayer::ArrowPlain;
     419                 :          0 :   else if ( s == QLatin1String( "lefthalf" ) )
     420                 :          0 :     return QgsArrowSymbolLayer::ArrowLeftHalf;
     421                 :          0 :   else if ( s == QLatin1String( "righthalf" ) )
     422                 :          0 :     return QgsArrowSymbolLayer::ArrowRightHalf;
     423                 :          0 :   else if ( value.toInt() == 1 )
     424                 :          0 :     return QgsArrowSymbolLayer::ArrowLeftHalf;
     425                 :          0 :   else if ( value.toInt() == 2 )
     426                 :          0 :     return QgsArrowSymbolLayer::ArrowRightHalf;
     427                 :          0 :   else if ( value.toInt( &intOk ) == 0 && intOk )
     428                 :          0 :     return QgsArrowSymbolLayer::ArrowPlain;
     429                 :            : 
     430                 :          0 :   if ( ok )
     431                 :          0 :     *ok = false;
     432                 :          0 :   return QgsArrowSymbolLayer::ArrowPlain;
     433                 :          0 : }
     434                 :            : 
     435                 :          0 : QString QgsSymbolLayerUtils::encodePoint( QPointF point )
     436                 :            : {
     437                 :          0 :   return QStringLiteral( "%1,%2" ).arg( qgsDoubleToString( point.x() ), qgsDoubleToString( point.y() ) );
     438                 :          0 : }
     439                 :            : 
     440                 :        590 : QPointF QgsSymbolLayerUtils::decodePoint( const QString &str )
     441                 :            : {
     442                 :        590 :   QStringList lst = str.split( ',' );
     443                 :        590 :   if ( lst.count() != 2 )
     444                 :          5 :     return QPointF( 0, 0 );
     445                 :        585 :   return QPointF( lst[0].toDouble(), lst[1].toDouble() );
     446                 :        590 : }
     447                 :            : 
     448                 :          0 : QPointF QgsSymbolLayerUtils::toPoint( const QVariant &value, bool *ok )
     449                 :            : {
     450                 :          0 :   if ( ok )
     451                 :          0 :     *ok = false;
     452                 :            : 
     453                 :          0 :   if ( value.isNull() )
     454                 :          0 :     return QPoint();
     455                 :            : 
     456                 :          0 :   if ( value.type() == QVariant::List )
     457                 :            :   {
     458                 :          0 :     const QVariantList list = value.toList();
     459                 :          0 :     if ( list.size() != 2 )
     460                 :            :     {
     461                 :          0 :       return QPointF();
     462                 :            :     }
     463                 :          0 :     bool convertOk = false;
     464                 :          0 :     double x = list.at( 0 ).toDouble( &convertOk );
     465                 :          0 :     if ( convertOk )
     466                 :            :     {
     467                 :          0 :       double y = list.at( 1 ).toDouble( &convertOk );
     468                 :          0 :       if ( convertOk )
     469                 :            :       {
     470                 :          0 :         if ( ok )
     471                 :          0 :           *ok = true;
     472                 :          0 :         return QPointF( x, y );
     473                 :            :       }
     474                 :          0 :     }
     475                 :          0 :     return QPointF();
     476                 :          0 :   }
     477                 :            :   else
     478                 :            :   {
     479                 :            :     // can't use decodePoint here -- has no OK handling
     480                 :          0 :     const QStringList list = value.toString().trimmed().split( ',' );
     481                 :          0 :     if ( list.count() != 2 )
     482                 :          0 :       return QPointF();
     483                 :          0 :     bool convertOk = false;
     484                 :          0 :     double x = list.at( 0 ).toDouble( &convertOk );
     485                 :          0 :     if ( convertOk )
     486                 :            :     {
     487                 :          0 :       double y = list.at( 1 ).toDouble( &convertOk );
     488                 :          0 :       if ( convertOk )
     489                 :            :       {
     490                 :          0 :         if ( ok )
     491                 :          0 :           *ok = true;
     492                 :          0 :         return QPointF( x, y );
     493                 :            :       }
     494                 :          0 :     }
     495                 :          0 :     return QPointF();
     496                 :          0 :   }
     497                 :          0 : }
     498                 :            : 
     499                 :          0 : QString QgsSymbolLayerUtils::encodeSize( QSizeF size )
     500                 :            : {
     501                 :          0 :   return QStringLiteral( "%1,%2" ).arg( qgsDoubleToString( size.width() ), qgsDoubleToString( size.height() ) );
     502                 :          0 : }
     503                 :            : 
     504                 :          0 : QSizeF QgsSymbolLayerUtils::decodeSize( const QString &string )
     505                 :            : {
     506                 :          0 :   QStringList lst = string.split( ',' );
     507                 :          0 :   if ( lst.count() != 2 )
     508                 :          0 :     return QSizeF( 0, 0 );
     509                 :          0 :   return QSizeF( lst[0].toDouble(), lst[1].toDouble() );
     510                 :          0 : }
     511                 :            : 
     512                 :          0 : QSizeF QgsSymbolLayerUtils::toSize( const QVariant &value, bool *ok )
     513                 :            : {
     514                 :          0 :   if ( ok )
     515                 :          0 :     *ok = false;
     516                 :            : 
     517                 :          0 :   if ( value.isNull() )
     518                 :          0 :     return QSizeF();
     519                 :            : 
     520                 :          0 :   if ( value.type() == QVariant::List )
     521                 :            :   {
     522                 :          0 :     const QVariantList list = value.toList();
     523                 :          0 :     if ( list.size() != 2 )
     524                 :            :     {
     525                 :          0 :       return QSizeF();
     526                 :            :     }
     527                 :          0 :     bool convertOk = false;
     528                 :          0 :     double x = list.at( 0 ).toDouble( &convertOk );
     529                 :          0 :     if ( convertOk )
     530                 :            :     {
     531                 :          0 :       double y = list.at( 1 ).toDouble( &convertOk );
     532                 :          0 :       if ( convertOk )
     533                 :            :       {
     534                 :          0 :         if ( ok )
     535                 :          0 :           *ok = true;
     536                 :          0 :         return QSizeF( x, y );
     537                 :            :       }
     538                 :          0 :     }
     539                 :          0 :     return QSizeF();
     540                 :          0 :   }
     541                 :            :   else
     542                 :            :   {
     543                 :            :     // can't use decodePoint here -- has no OK handling
     544                 :          0 :     const QStringList list = value.toString().trimmed().split( ',' );
     545                 :          0 :     if ( list.count() != 2 )
     546                 :          0 :       return QSizeF();
     547                 :          0 :     bool convertOk = false;
     548                 :          0 :     double x = list.at( 0 ).toDouble( &convertOk );
     549                 :          0 :     if ( convertOk )
     550                 :            :     {
     551                 :          0 :       double y = list.at( 1 ).toDouble( &convertOk );
     552                 :          0 :       if ( convertOk )
     553                 :            :       {
     554                 :          0 :         if ( ok )
     555                 :          0 :           *ok = true;
     556                 :          0 :         return QSizeF( x, y );
     557                 :            :       }
     558                 :          0 :     }
     559                 :          0 :     return QSizeF();
     560                 :          0 :   }
     561                 :          0 : }
     562                 :            : 
     563                 :          0 : QString QgsSymbolLayerUtils::encodeMapUnitScale( const QgsMapUnitScale &mapUnitScale )
     564                 :            : {
     565                 :          0 :   return QStringLiteral( "3x:%1,%2,%3,%4,%5,%6" ).arg( qgsDoubleToString( mapUnitScale.minScale ),
     566                 :          0 :          qgsDoubleToString( mapUnitScale.maxScale ) )
     567                 :          0 :          .arg( mapUnitScale.minSizeMMEnabled ? 1 : 0 )
     568                 :          0 :          .arg( mapUnitScale.minSizeMM )
     569                 :          0 :          .arg( mapUnitScale.maxSizeMMEnabled ? 1 : 0 )
     570                 :          0 :          .arg( mapUnitScale.maxSizeMM );
     571                 :          0 : }
     572                 :            : 
     573                 :       4331 : QgsMapUnitScale QgsSymbolLayerUtils::decodeMapUnitScale( const QString &str )
     574                 :            : {
     575                 :       4331 :   QStringList lst;
     576                 :       4331 :   bool v3 = false;
     577                 :       4331 :   if ( str.startsWith( QLatin1String( "3x:" ) ) )
     578                 :            :   {
     579                 :       4031 :     v3 = true;
     580                 :       4031 :     QString chopped = str.mid( 3 );
     581                 :       4031 :     lst = chopped.split( ',' );
     582                 :       4031 :   }
     583                 :            :   else
     584                 :            :   {
     585                 :        300 :     lst = str.split( ',' );
     586                 :            :   }
     587                 :       4331 :   if ( lst.count() < 2 )
     588                 :        300 :     return QgsMapUnitScale();
     589                 :            : 
     590                 :       4031 :   double minScale = lst[0].toDouble();
     591                 :       4031 :   if ( !v3 )
     592                 :          0 :     minScale = minScale != 0 ? 1.0 / minScale : 0;
     593                 :       4031 :   double maxScale = lst[1].toDouble();
     594                 :       4031 :   if ( !v3 )
     595                 :          0 :     maxScale = maxScale != 0 ? 1.0 / maxScale : 0;
     596                 :            : 
     597                 :       4031 :   if ( lst.count() < 6 )
     598                 :            :   {
     599                 :            :     // old format
     600                 :          0 :     return QgsMapUnitScale( minScale, maxScale );
     601                 :            :   }
     602                 :            : 
     603                 :       4031 :   QgsMapUnitScale s( minScale, maxScale );
     604                 :       4031 :   s.minSizeMMEnabled = lst[2].toInt();
     605                 :       4031 :   s.minSizeMM = lst[3].toDouble();
     606                 :       4031 :   s.maxSizeMMEnabled = lst[4].toInt();
     607                 :       4031 :   s.maxSizeMM = lst[5].toDouble();
     608                 :       4031 :   return s;
     609                 :       4331 : }
     610                 :            : 
     611                 :          0 : QString QgsSymbolLayerUtils::encodeSldUom( QgsUnitTypes::RenderUnit unit, double *scaleFactor )
     612                 :            : {
     613                 :          0 :   switch ( unit )
     614                 :            :   {
     615                 :            :     case QgsUnitTypes::RenderMapUnits:
     616                 :          0 :       if ( scaleFactor )
     617                 :          0 :         *scaleFactor = 0.001; // from millimeters to meters
     618                 :          0 :       return QStringLiteral( "http://www.opengeospatial.org/se/units/metre" );
     619                 :            : 
     620                 :            :     case QgsUnitTypes::RenderMillimeters:
     621                 :            :     default:
     622                 :            :       // pixel is the SLD default uom. The "standardized rendering pixel
     623                 :            :       // size" is defined to be 0.28mm × 0.28mm (millimeters).
     624                 :          0 :       if ( scaleFactor )
     625                 :          0 :         *scaleFactor = 1 / 0.28;  // from millimeters to pixels
     626                 :            : 
     627                 :            :       // http://www.opengeospatial.org/sld/units/pixel
     628                 :          0 :       return QString();
     629                 :            :   }
     630                 :          0 : }
     631                 :            : 
     632                 :          0 : QgsUnitTypes::RenderUnit QgsSymbolLayerUtils::decodeSldUom( const QString &str, double *scaleFactor )
     633                 :            : {
     634                 :          0 :   if ( str == QLatin1String( "http://www.opengeospatial.org/se/units/metre" ) )
     635                 :            :   {
     636                 :          0 :     if ( scaleFactor )
     637                 :          0 :       *scaleFactor = 1000.0;  // from meters to millimeters
     638                 :          0 :     return QgsUnitTypes::RenderMapUnits;
     639                 :            :   }
     640                 :          0 :   else if ( str == QLatin1String( "http://www.opengeospatial.org/se/units/foot" ) )
     641                 :            :   {
     642                 :          0 :     if ( scaleFactor )
     643                 :          0 :       *scaleFactor = 304.8; // from feet to meters
     644                 :          0 :     return QgsUnitTypes::RenderMapUnits;
     645                 :            :   }
     646                 :          0 :   else if ( str == QLatin1String( "http://www.opengeospatial.org/se/units/pixel" ) )
     647                 :            :   {
     648                 :          0 :     if ( scaleFactor )
     649                 :          0 :       *scaleFactor = 1.0; // from pixels to pixels
     650                 :          0 :     return QgsUnitTypes::RenderPixels;
     651                 :            :   }
     652                 :            : 
     653                 :            :   // pixel is the SLD default uom. The "standardized rendering pixel
     654                 :            :   // size" is defined to be 0.28mm x 0.28mm (millimeters).
     655                 :          0 :   if ( scaleFactor )
     656                 :          0 :     *scaleFactor = 1 / 0.00028; // from pixels to millimeters
     657                 :          0 :   return QgsUnitTypes::RenderMillimeters;
     658                 :          0 : }
     659                 :            : 
     660                 :          0 : QString QgsSymbolLayerUtils::encodeRealVector( const QVector<qreal> &v )
     661                 :            : {
     662                 :          0 :   QString vectorString;
     663                 :          0 :   QVector<qreal>::const_iterator it = v.constBegin();
     664                 :          0 :   for ( ; it != v.constEnd(); ++it )
     665                 :            :   {
     666                 :          0 :     if ( it != v.constBegin() )
     667                 :            :     {
     668                 :          0 :       vectorString.append( ';' );
     669                 :          0 :     }
     670                 :          0 :     vectorString.append( QString::number( *it ) );
     671                 :          0 :   }
     672                 :          0 :   return vectorString;
     673                 :          0 : }
     674                 :            : 
     675                 :        355 : QVector<qreal> QgsSymbolLayerUtils::decodeRealVector( const QString &s )
     676                 :            : {
     677                 :        355 :   QVector<qreal> resultVector;
     678                 :            : 
     679                 :        355 :   QStringList realList = s.split( ';' );
     680                 :        355 :   QStringList::const_iterator it = realList.constBegin();
     681                 :       1065 :   for ( ; it != realList.constEnd(); ++it )
     682                 :            :   {
     683                 :        710 :     resultVector.append( it->toDouble() );
     684                 :        710 :   }
     685                 :            : 
     686                 :        355 :   return resultVector;
     687                 :        355 : }
     688                 :            : 
     689                 :          0 : QString QgsSymbolLayerUtils::encodeSldRealVector( const QVector<qreal> &v )
     690                 :            : {
     691                 :          0 :   QString vectorString;
     692                 :          0 :   QVector<qreal>::const_iterator it = v.constBegin();
     693                 :          0 :   for ( ; it != v.constEnd(); ++it )
     694                 :            :   {
     695                 :          0 :     if ( it != v.constBegin() )
     696                 :            :     {
     697                 :          0 :       vectorString.append( ' ' );
     698                 :          0 :     }
     699                 :          0 :     vectorString.append( QString::number( *it ) );
     700                 :          0 :   }
     701                 :          0 :   return vectorString;
     702                 :          0 : }
     703                 :            : 
     704                 :          0 : QVector<qreal> QgsSymbolLayerUtils::decodeSldRealVector( const QString &s )
     705                 :            : {
     706                 :          0 :   QVector<qreal> resultVector;
     707                 :            : 
     708                 :          0 :   QStringList realList = s.split( ' ' );
     709                 :          0 :   QStringList::const_iterator it = realList.constBegin();
     710                 :          0 :   for ( ; it != realList.constEnd(); ++it )
     711                 :            :   {
     712                 :          0 :     resultVector.append( it->toDouble() );
     713                 :          0 :   }
     714                 :            : 
     715                 :          0 :   return resultVector;
     716                 :          0 : }
     717                 :            : 
     718                 :          0 : QString QgsSymbolLayerUtils::encodeScaleMethod( QgsSymbol::ScaleMethod scaleMethod )
     719                 :            : {
     720                 :          0 :   QString encodedValue;
     721                 :            : 
     722                 :          0 :   switch ( scaleMethod )
     723                 :            :   {
     724                 :            :     case QgsSymbol::ScaleDiameter:
     725                 :          0 :       encodedValue = QStringLiteral( "diameter" );
     726                 :          0 :       break;
     727                 :            :     case QgsSymbol::ScaleArea:
     728                 :          0 :       encodedValue = QStringLiteral( "area" );
     729                 :          0 :       break;
     730                 :            :   }
     731                 :          0 :   return encodedValue;
     732                 :          0 : }
     733                 :            : 
     734                 :        270 : QgsSymbol::ScaleMethod QgsSymbolLayerUtils::decodeScaleMethod( const QString &str )
     735                 :            : {
     736                 :            :   QgsSymbol::ScaleMethod scaleMethod;
     737                 :            : 
     738                 :        270 :   if ( str == QLatin1String( "diameter" ) )
     739                 :            :   {
     740                 :        220 :     scaleMethod = QgsSymbol::ScaleDiameter;
     741                 :        220 :   }
     742                 :            :   else
     743                 :            :   {
     744                 :         50 :     scaleMethod = QgsSymbol::ScaleArea;
     745                 :            :   }
     746                 :            : 
     747                 :        270 :   return scaleMethod;
     748                 :            : }
     749                 :            : 
     750                 :          0 : QPainter::CompositionMode QgsSymbolLayerUtils::decodeBlendMode( const QString &s )
     751                 :            : {
     752                 :          0 :   if ( s.compare( QLatin1String( "Lighten" ), Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_Lighten;
     753                 :          0 :   if ( s.compare( QLatin1String( "Screen" ), Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_Screen;
     754                 :          0 :   if ( s.compare( QLatin1String( "Dodge" ), Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_ColorDodge;
     755                 :          0 :   if ( s.compare( QLatin1String( "Addition" ), Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_Plus;
     756                 :          0 :   if ( s.compare( QLatin1String( "Darken" ), Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_Darken;
     757                 :          0 :   if ( s.compare( QLatin1String( "Multiply" ), Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_Multiply;
     758                 :          0 :   if ( s.compare( QLatin1String( "Burn" ), Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_ColorBurn;
     759                 :          0 :   if ( s.compare( QLatin1String( "Overlay" ), Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_Overlay;
     760                 :          0 :   if ( s.compare( QLatin1String( "SoftLight" ), Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_SoftLight;
     761                 :          0 :   if ( s.compare( QLatin1String( "HardLight" ), Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_HardLight;
     762                 :          0 :   if ( s.compare( QLatin1String( "Difference" ), Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_Difference;
     763                 :          0 :   if ( s.compare( QLatin1String( "Subtract" ), Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_Exclusion;
     764                 :          0 :   return QPainter::CompositionMode_SourceOver; // "Normal"
     765                 :          0 : }
     766                 :            : 
     767                 :          0 : QIcon QgsSymbolLayerUtils::symbolPreviewIcon( const QgsSymbol *symbol, QSize size, int padding, QgsLegendPatchShape *shape )
     768                 :            : {
     769                 :          0 :   return QIcon( symbolPreviewPixmap( symbol, size, padding, nullptr, false, nullptr, shape ) );
     770                 :          0 : }
     771                 :            : 
     772                 :          0 : QPixmap QgsSymbolLayerUtils::symbolPreviewPixmap( const QgsSymbol *symbol, QSize size, int padding, QgsRenderContext *customContext, bool selected, const QgsExpressionContext *expressionContext, const QgsLegendPatchShape *shape )
     773                 :            : {
     774                 :            :   Q_ASSERT( symbol );
     775                 :          0 :   QPixmap pixmap( size );
     776                 :          0 :   pixmap.fill( Qt::transparent );
     777                 :          0 :   QPainter painter;
     778                 :          0 :   painter.begin( &pixmap );
     779                 :          0 :   if ( customContext )
     780                 :          0 :     customContext->setPainterFlagsUsingContext( &painter );
     781                 :            :   else
     782                 :          0 :     painter.setRenderHint( QPainter::Antialiasing );
     783                 :            : 
     784                 :          0 :   if ( customContext )
     785                 :            :   {
     786                 :          0 :     customContext->setPainter( &painter );
     787                 :          0 :   }
     788                 :            : 
     789                 :          0 :   if ( padding > 0 )
     790                 :            :   {
     791                 :          0 :     size.setWidth( size.rwidth() - ( padding * 2 ) );
     792                 :          0 :     size.setHeight( size.rheight() - ( padding * 2 ) );
     793                 :          0 :     painter.translate( padding, padding );
     794                 :          0 :   }
     795                 :            : 
     796                 :            :   // If the context has no feature and there are DD properties,
     797                 :            :   // use a clone and clear some DDs: see issue #19096
     798                 :            :   // Applying a data defined size to a categorized layer hides its category symbol in the layers panel and legend
     799                 :          0 :   if ( symbol->hasDataDefinedProperties() &&
     800                 :          0 :        !( customContext
     801                 :          0 :           && customContext->expressionContext().hasFeature( ) ) )
     802                 :            :   {
     803                 :          0 :     std::unique_ptr<QgsSymbol> symbol_noDD( symbol->clone( ) );
     804                 :          0 :     const QgsSymbolLayerList layers( symbol_noDD->symbolLayers() );
     805                 :          0 :     for ( const auto &layer : layers )
     806                 :            :     {
     807                 :          0 :       for ( int i = 0; i < layer->dataDefinedProperties().count(); ++i )
     808                 :            :       {
     809                 :          0 :         QgsProperty &prop = layer->dataDefinedProperties().property( i );
     810                 :            :         // don't clear project color properties -- we want to show them in symbol previews
     811                 :          0 :         if ( prop.isActive() && !prop.isProjectColor() )
     812                 :          0 :           prop.setActive( false );
     813                 :          0 :       }
     814                 :            :     }
     815                 :          0 :     symbol_noDD->drawPreviewIcon( &painter, size, customContext, selected, expressionContext, shape );
     816                 :          0 :   }
     817                 :            :   else
     818                 :            :   {
     819                 :          0 :     std::unique_ptr<QgsSymbol> symbolClone( symbol->clone( ) );
     820                 :          0 :     symbolClone->drawPreviewIcon( &painter, size, customContext, selected, expressionContext, shape );
     821                 :          0 :   }
     822                 :            : 
     823                 :          0 :   painter.end();
     824                 :          0 :   return pixmap;
     825                 :          0 : }
     826                 :            : 
     827                 :          0 : double QgsSymbolLayerUtils::estimateMaxSymbolBleed( QgsSymbol *symbol, const QgsRenderContext &context )
     828                 :            : {
     829                 :          0 :   double maxBleed = 0;
     830                 :          0 :   for ( int i = 0; i < symbol->symbolLayerCount(); i++ )
     831                 :            :   {
     832                 :          0 :     QgsSymbolLayer *layer = symbol->symbolLayer( i );
     833                 :          0 :     double layerMaxBleed = layer->estimateMaxBleed( context );
     834                 :          0 :     maxBleed = layerMaxBleed > maxBleed ? layerMaxBleed : maxBleed;
     835                 :          0 :   }
     836                 :            : 
     837                 :          0 :   return maxBleed;
     838                 :            : }
     839                 :            : 
     840                 :          0 : QPicture QgsSymbolLayerUtils::symbolLayerPreviewPicture( const QgsSymbolLayer *layer, QgsUnitTypes::RenderUnit units, QSize size, const QgsMapUnitScale & )
     841                 :            : {
     842                 :          0 :   QPicture picture;
     843                 :          0 :   QPainter painter;
     844                 :          0 :   painter.begin( &picture );
     845                 :          0 :   painter.setRenderHint( QPainter::Antialiasing );
     846                 :          0 :   QgsRenderContext renderContext = QgsRenderContext::fromQPainter( &painter );
     847                 :          0 :   renderContext.setForceVectorOutput( true );
     848                 :          0 :   renderContext.setFlag( QgsRenderContext::RenderSymbolPreview, true );
     849                 :          0 :   QgsSymbolRenderContext symbolContext( renderContext, units, 1.0, false, QgsSymbol::RenderHints(), nullptr );
     850                 :          0 :   std::unique_ptr< QgsSymbolLayer > layerClone( layer->clone() );
     851                 :          0 :   layerClone->drawPreviewIcon( symbolContext, size );
     852                 :          0 :   painter.end();
     853                 :          0 :   return picture;
     854                 :          0 : }
     855                 :            : 
     856                 :          0 : QIcon QgsSymbolLayerUtils::symbolLayerPreviewIcon( const QgsSymbolLayer *layer, QgsUnitTypes::RenderUnit u, QSize size, const QgsMapUnitScale & )
     857                 :            : {
     858                 :          0 :   QPixmap pixmap( size );
     859                 :          0 :   pixmap.fill( Qt::transparent );
     860                 :          0 :   QPainter painter;
     861                 :          0 :   painter.begin( &pixmap );
     862                 :          0 :   painter.setRenderHint( QPainter::Antialiasing );
     863                 :          0 :   QgsRenderContext renderContext = QgsRenderContext::fromQPainter( &painter );
     864                 :          0 :   renderContext.setFlag( QgsRenderContext::RenderSymbolPreview );
     865                 :            :   // build a minimal expression context
     866                 :          0 :   QgsExpressionContext expContext;
     867                 :          0 :   expContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( nullptr ) );
     868                 :          0 :   renderContext.setExpressionContext( expContext );
     869                 :            : 
     870                 :          0 :   QgsSymbolRenderContext symbolContext( renderContext, u, 1.0, false, QgsSymbol::RenderHints(), nullptr );
     871                 :          0 :   std::unique_ptr< QgsSymbolLayer > layerClone( layer->clone() );
     872                 :          0 :   layerClone->drawPreviewIcon( symbolContext, size );
     873                 :          0 :   painter.end();
     874                 :          0 :   return QIcon( pixmap );
     875                 :          0 : }
     876                 :            : 
     877                 :          0 : QIcon QgsSymbolLayerUtils::colorRampPreviewIcon( QgsColorRamp *ramp, QSize size, int padding )
     878                 :            : {
     879                 :          0 :   return QIcon( colorRampPreviewPixmap( ramp, size, padding ) );
     880                 :          0 : }
     881                 :            : 
     882                 :          0 : QPixmap QgsSymbolLayerUtils::colorRampPreviewPixmap( QgsColorRamp *ramp, QSize size, int padding, Qt::Orientation direction, bool flipDirection, bool drawTransparentBackground )
     883                 :            : {
     884                 :          0 :   QPixmap pixmap( size );
     885                 :          0 :   pixmap.fill( Qt::transparent );
     886                 :            :   // pixmap.fill( Qt::white ); // this makes the background white instead of transparent
     887                 :          0 :   QPainter painter;
     888                 :          0 :   painter.begin( &pixmap );
     889                 :            : 
     890                 :            :   //draw stippled background, for transparent images
     891                 :          0 :   if ( drawTransparentBackground )
     892                 :          0 :     drawStippledBackground( &painter, QRect( padding, padding, size.width() - padding * 2, size.height() - padding  * 2 ) );
     893                 :            : 
     894                 :            :   // antialiasing makes the colors duller, and no point in antialiasing a color ramp
     895                 :            :   // painter.setRenderHint( QPainter::Antialiasing );
     896                 :          0 :   switch ( direction )
     897                 :            :   {
     898                 :            :     case Qt::Horizontal:
     899                 :            :     {
     900                 :          0 :       for ( int i = 0; i < size.width(); i++ )
     901                 :            :       {
     902                 :          0 :         QPen pen( ramp->color( static_cast< double >( i ) / size.width() ) );
     903                 :          0 :         painter.setPen( pen );
     904                 :          0 :         const int x = flipDirection ? size.width() - i - 1 : i;
     905                 :          0 :         painter.drawLine( x, 0 + padding, x, size.height() - 1 - padding );
     906                 :          0 :       }
     907                 :          0 :       break;
     908                 :            :     }
     909                 :            : 
     910                 :            :     case Qt::Vertical:
     911                 :            :     {
     912                 :          0 :       for ( int i = 0; i < size.height(); i++ )
     913                 :            :       {
     914                 :          0 :         QPen pen( ramp->color( static_cast< double >( i ) / size.height() ) );
     915                 :          0 :         painter.setPen( pen );
     916                 :          0 :         const int y = flipDirection ? size.height() - i - 1 : i;
     917                 :          0 :         painter.drawLine( 0 + padding, y, size.width() - 1 - padding, y );
     918                 :          0 :       }
     919                 :          0 :       break;
     920                 :            :     }
     921                 :            :   }
     922                 :            : 
     923                 :          0 :   painter.end();
     924                 :          0 :   return pixmap;
     925                 :          0 : }
     926                 :            : 
     927                 :          0 : void QgsSymbolLayerUtils::drawStippledBackground( QPainter *painter, QRect rect )
     928                 :            : {
     929                 :            :   // create a 2x2 checker-board image
     930                 :          0 :   uchar pixDataRGB[] = { 255, 255, 255, 255,
     931                 :            :                          127, 127, 127, 255,
     932                 :            :                          127, 127, 127, 255,
     933                 :            :                          255, 255, 255, 255
     934                 :            :                        };
     935                 :          0 :   QImage img( pixDataRGB, 2, 2, 8, QImage::Format_ARGB32 );
     936                 :            :   // scale it to rect so at least 5 patterns are shown
     937                 :          0 :   int width = ( rect.width() < rect.height() ) ?
     938                 :          0 :               rect.width() / 2.5 : rect.height() / 2.5;
     939                 :          0 :   QPixmap pix = QPixmap::fromImage( img.scaled( width, width ) );
     940                 :            :   // fill rect with texture
     941                 :          0 :   QBrush brush;
     942                 :          0 :   brush.setTexture( pix );
     943                 :          0 :   painter->fillRect( rect, brush );
     944                 :          0 : }
     945                 :            : 
     946                 :          0 : void QgsSymbolLayerUtils::drawVertexMarker( double x, double y, QPainter &p, QgsSymbolLayerUtils::VertexMarkerType type, int markerSize )
     947                 :            : {
     948                 :          0 :   qreal s = ( markerSize - 1 ) / 2.0;
     949                 :            : 
     950                 :          0 :   switch ( type )
     951                 :            :   {
     952                 :            :     case QgsSymbolLayerUtils::SemiTransparentCircle:
     953                 :          0 :       p.setPen( QColor( 50, 100, 120, 200 ) );
     954                 :          0 :       p.setBrush( QColor( 200, 200, 210, 120 ) );
     955                 :          0 :       p.drawEllipse( x - s, y - s, s * 2, s * 2 );
     956                 :          0 :       break;
     957                 :            :     case QgsSymbolLayerUtils::Cross:
     958                 :          0 :       p.setPen( QColor( 255, 0, 0 ) );
     959                 :          0 :       p.drawLine( x - s, y + s, x + s, y - s );
     960                 :          0 :       p.drawLine( x - s, y - s, x + s, y + s );
     961                 :          0 :       break;
     962                 :            :     case QgsSymbolLayerUtils::NoMarker:
     963                 :          0 :       break;
     964                 :            :   }
     965                 :          0 : }
     966                 :            : 
     967                 :            : #include <QPolygonF>
     968                 :            : 
     969                 :            : #include <cmath>
     970                 :            : #include <cfloat>
     971                 :            : 
     972                 :          0 : static QPolygonF makeOffsetGeometry( const QgsPolylineXY &polyline )
     973                 :            : {
     974                 :          0 :   int i, pointCount = polyline.count();
     975                 :            : 
     976                 :          0 :   QPolygonF resultLine;
     977                 :          0 :   resultLine.resize( pointCount );
     978                 :            : 
     979                 :          0 :   const QgsPointXY *tempPtr = polyline.data();
     980                 :            : 
     981                 :          0 :   for ( i = 0; i < pointCount; ++i, tempPtr++ )
     982                 :          0 :     resultLine[i] = QPointF( tempPtr->x(), tempPtr->y() );
     983                 :            : 
     984                 :          0 :   return resultLine;
     985                 :          0 : }
     986                 :          0 : static QList<QPolygonF> makeOffsetGeometry( const QgsPolygonXY &polygon )
     987                 :            : {
     988                 :          0 :   QList<QPolygonF> resultGeom;
     989                 :          0 :   resultGeom.reserve( polygon.size() );
     990                 :          0 :   for ( int ring = 0; ring < polygon.size(); ++ring )
     991                 :          0 :     resultGeom.append( makeOffsetGeometry( polygon[ ring ] ) );
     992                 :          0 :   return resultGeom;
     993                 :          0 : }
     994                 :            : 
     995                 :          0 : QList<QPolygonF> offsetLine( QPolygonF polyline, double dist, QgsWkbTypes::GeometryType geometryType )
     996                 :            : {
     997                 :          0 :   QList<QPolygonF> resultLine;
     998                 :            : 
     999                 :          0 :   if ( polyline.count() < 2 )
    1000                 :            :   {
    1001                 :          0 :     resultLine.append( polyline );
    1002                 :          0 :     return resultLine;
    1003                 :            :   }
    1004                 :            : 
    1005                 :          0 :   unsigned int i, pointCount = polyline.count();
    1006                 :            : 
    1007                 :          0 :   QgsPolylineXY tempPolyline( pointCount );
    1008                 :          0 :   QPointF *tempPtr = polyline.data();
    1009                 :          0 :   for ( i = 0; i < pointCount; ++i, tempPtr++ )
    1010                 :          0 :     tempPolyline[i] = QgsPointXY( tempPtr->rx(), tempPtr->ry() );
    1011                 :            : 
    1012                 :          0 :   QgsGeometry tempGeometry = geometryType == QgsWkbTypes::PolygonGeometry ? QgsGeometry::fromPolygonXY( QgsPolygonXY() << tempPolyline ) : QgsGeometry::fromPolylineXY( tempPolyline );
    1013                 :          0 :   if ( !tempGeometry.isNull() )
    1014                 :            :   {
    1015                 :          0 :     int quadSegments = 0; // we want miter joins, not round joins
    1016                 :          0 :     double miterLimit = 2.0; // the default value in GEOS (5.0) allows for fairly sharp endings
    1017                 :          0 :     QgsGeometry offsetGeom;
    1018                 :          0 :     if ( geometryType == QgsWkbTypes::PolygonGeometry )
    1019                 :          0 :       offsetGeom = tempGeometry.buffer( -dist, quadSegments, QgsGeometry::CapFlat,
    1020                 :          0 :                                         QgsGeometry::JoinStyleMiter, miterLimit );
    1021                 :            :     else
    1022                 :          0 :       offsetGeom = tempGeometry.offsetCurve( dist, quadSegments, QgsGeometry::JoinStyleMiter, miterLimit );
    1023                 :            : 
    1024                 :          0 :     if ( !offsetGeom.isNull() )
    1025                 :            :     {
    1026                 :          0 :       tempGeometry = offsetGeom;
    1027                 :            : 
    1028                 :          0 :       if ( QgsWkbTypes::flatType( tempGeometry.wkbType() ) == QgsWkbTypes::LineString )
    1029                 :            :       {
    1030                 :          0 :         QgsPolylineXY line = tempGeometry.asPolyline();
    1031                 :          0 :         resultLine.append( makeOffsetGeometry( line ) );
    1032                 :          0 :         return resultLine;
    1033                 :          0 :       }
    1034                 :          0 :       else if ( QgsWkbTypes::flatType( tempGeometry.wkbType() ) == QgsWkbTypes::Polygon )
    1035                 :            :       {
    1036                 :          0 :         resultLine.append( makeOffsetGeometry( tempGeometry.asPolygon() ) );
    1037                 :          0 :         return resultLine;
    1038                 :            :       }
    1039                 :          0 :       else if ( QgsWkbTypes::flatType( tempGeometry.wkbType() ) == QgsWkbTypes::MultiLineString )
    1040                 :            :       {
    1041                 :          0 :         QgsMultiPolylineXY tempMPolyline = tempGeometry.asMultiPolyline();
    1042                 :          0 :         resultLine.reserve( tempMPolyline.count() );
    1043                 :          0 :         for ( int part = 0; part < tempMPolyline.count(); ++part )
    1044                 :            :         {
    1045                 :          0 :           resultLine.append( makeOffsetGeometry( tempMPolyline[ part ] ) );
    1046                 :          0 :         }
    1047                 :          0 :         return resultLine;
    1048                 :          0 :       }
    1049                 :          0 :       else if ( QgsWkbTypes::flatType( tempGeometry.wkbType() ) == QgsWkbTypes::MultiPolygon )
    1050                 :            :       {
    1051                 :          0 :         QgsMultiPolygonXY tempMPolygon = tempGeometry.asMultiPolygon();
    1052                 :          0 :         resultLine.reserve( tempMPolygon.count() );
    1053                 :          0 :         for ( int part = 0; part < tempMPolygon.count(); ++part )
    1054                 :            :         {
    1055                 :          0 :           resultLine.append( makeOffsetGeometry( tempMPolygon[ part ] ) );
    1056                 :          0 :         }
    1057                 :          0 :         return resultLine;
    1058                 :          0 :       }
    1059                 :          0 :     }
    1060                 :          0 :   }
    1061                 :            : 
    1062                 :            :   // returns original polyline when 'GEOSOffsetCurve' fails!
    1063                 :          0 :   resultLine.append( polyline );
    1064                 :          0 :   return resultLine;
    1065                 :          0 : }
    1066                 :            : 
    1067                 :            : /////
    1068                 :            : 
    1069                 :            : 
    1070                 :        725 : QgsSymbol *QgsSymbolLayerUtils::loadSymbol( const QDomElement &element, const QgsReadWriteContext &context )
    1071                 :            : {
    1072                 :        725 :   QgsSymbolLayerList layers;
    1073                 :        725 :   QDomNode layerNode = element.firstChild();
    1074                 :            : 
    1075                 :       2295 :   while ( !layerNode.isNull() )
    1076                 :            :   {
    1077                 :       1570 :     QDomElement e = layerNode.toElement();
    1078                 :       1570 :     if ( !e.isNull() && e.tagName() != QLatin1String( "data_defined_properties" ) )
    1079                 :            :     {
    1080                 :        990 :       if ( e.tagName() != QLatin1String( "layer" ) )
    1081                 :            :       {
    1082                 :          0 :         QgsDebugMsg( "unknown tag " + e.tagName() );
    1083                 :          0 :       }
    1084                 :            :       else
    1085                 :            :       {
    1086                 :        990 :         QgsSymbolLayer *layer = loadSymbolLayer( e, context );
    1087                 :            : 
    1088                 :        990 :         if ( layer )
    1089                 :            :         {
    1090                 :            :           // Dealing with sub-symbols nested into a layer
    1091                 :       1980 :           QDomElement s = e.firstChildElement( QStringLiteral( "symbol" ) );
    1092                 :        990 :           if ( !s.isNull() )
    1093                 :            :           {
    1094                 :        150 :             QgsSymbol *subSymbol = loadSymbol( s, context );
    1095                 :        150 :             bool res = layer->setSubSymbol( subSymbol );
    1096                 :        150 :             if ( !res )
    1097                 :            :             {
    1098                 :          0 :               QgsDebugMsg( "symbol layer refused subsymbol: " + s.attribute( "name" ) );
    1099                 :          0 :             }
    1100                 :        150 :           }
    1101                 :        990 :           layers.append( layer );
    1102                 :        990 :         }
    1103                 :            :       }
    1104                 :        990 :     }
    1105                 :       1570 :     layerNode = layerNode.nextSibling();
    1106                 :       1570 :   }
    1107                 :            : 
    1108                 :        725 :   if ( layers.isEmpty() )
    1109                 :            :   {
    1110                 :          0 :     QgsDebugMsg( QStringLiteral( "no layers for symbol" ) );
    1111                 :          0 :     return nullptr;
    1112                 :            :   }
    1113                 :            : 
    1114                 :       1450 :   QString symbolType = element.attribute( QStringLiteral( "type" ) );
    1115                 :            : 
    1116                 :        725 :   QgsSymbol *symbol = nullptr;
    1117                 :        725 :   if ( symbolType == QLatin1String( "line" ) )
    1118                 :        235 :     symbol = new QgsLineSymbol( layers );
    1119                 :        490 :   else if ( symbolType == QLatin1String( "fill" ) )
    1120                 :        265 :     symbol = new QgsFillSymbol( layers );
    1121                 :        225 :   else if ( symbolType == QLatin1String( "marker" ) )
    1122                 :        225 :     symbol = new QgsMarkerSymbol( layers );
    1123                 :            :   else
    1124                 :            :   {
    1125                 :          0 :     QgsDebugMsg( "unknown symbol type " + symbolType );
    1126                 :          0 :     return nullptr;
    1127                 :            :   }
    1128                 :            : 
    1129                 :       1450 :   if ( element.hasAttribute( QStringLiteral( "outputUnit" ) ) )
    1130                 :            :   {
    1131                 :          0 :     symbol->setOutputUnit( QgsUnitTypes::decodeRenderUnit( element.attribute( QStringLiteral( "outputUnit" ) ) ) );
    1132                 :          0 :   }
    1133                 :       1450 :   if ( element.hasAttribute( ( QStringLiteral( "mapUnitScale" ) ) ) )
    1134                 :            :   {
    1135                 :          0 :     QgsMapUnitScale mapUnitScale;
    1136                 :          0 :     double oldMin = element.attribute( QStringLiteral( "mapUnitMinScale" ), QStringLiteral( "0.0" ) ).toDouble();
    1137                 :          0 :     mapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
    1138                 :          0 :     double oldMax = element.attribute( QStringLiteral( "mapUnitMaxScale" ), QStringLiteral( "0.0" ) ).toDouble();
    1139                 :          0 :     mapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
    1140                 :          0 :     symbol->setMapUnitScale( mapUnitScale );
    1141                 :          0 :   }
    1142                 :       2175 :   symbol->setOpacity( element.attribute( QStringLiteral( "alpha" ), QStringLiteral( "1.0" ) ).toDouble() );
    1143                 :       2175 :   symbol->setClipFeaturesToExtent( element.attribute( QStringLiteral( "clip_to_extent" ), QStringLiteral( "1" ) ).toInt() );
    1144                 :       2175 :   symbol->setForceRHR( element.attribute( QStringLiteral( "force_rhr" ), QStringLiteral( "0" ) ).toInt() );
    1145                 :            : 
    1146                 :       1450 :   QDomElement ddProps = element.firstChildElement( QStringLiteral( "data_defined_properties" ) );
    1147                 :        725 :   if ( !ddProps.isNull() )
    1148                 :            :   {
    1149                 :        580 :     symbol->dataDefinedProperties().readXml( ddProps, QgsSymbol::propertyDefinitions() );
    1150                 :        580 :   }
    1151                 :            : 
    1152                 :        725 :   return symbol;
    1153                 :        725 : }
    1154                 :            : 
    1155                 :        990 : QgsSymbolLayer *QgsSymbolLayerUtils::loadSymbolLayer( QDomElement &element, const QgsReadWriteContext &context )
    1156                 :            : {
    1157                 :       1980 :   QString layerClass = element.attribute( QStringLiteral( "class" ) );
    1158                 :       1980 :   bool locked = element.attribute( QStringLiteral( "locked" ) ).toInt();
    1159                 :       2970 :   bool enabled = element.attribute( QStringLiteral( "enabled" ), QStringLiteral( "1" ) ).toInt();
    1160                 :       1980 :   int pass = element.attribute( QStringLiteral( "pass" ) ).toInt();
    1161                 :            : 
    1162                 :            :   // parse properties
    1163                 :        990 :   QVariantMap props = parseProperties( element );
    1164                 :            : 
    1165                 :            :   // if there are any paths stored in properties, convert them from relative to absolute
    1166                 :        990 :   QgsApplication::symbolLayerRegistry()->resolvePaths( layerClass, props, context.pathResolver(), false );
    1167                 :            : 
    1168                 :        990 :   QgsSymbolLayer *layer = nullptr;
    1169                 :        990 :   layer = QgsApplication::symbolLayerRegistry()->createSymbolLayer( layerClass, props );
    1170                 :        990 :   if ( layer )
    1171                 :            :   {
    1172                 :        990 :     layer->setLocked( locked );
    1173                 :        990 :     layer->setRenderingPass( pass );
    1174                 :        990 :     layer->setEnabled( enabled );
    1175                 :            : 
    1176                 :            :     //restore layer effect
    1177                 :       1980 :     QDomElement effectElem = element.firstChildElement( QStringLiteral( "effect" ) );
    1178                 :        990 :     if ( !effectElem.isNull() )
    1179                 :            :     {
    1180                 :         50 :       std::unique_ptr< QgsPaintEffect > effect( QgsApplication::paintEffectRegistry()->createEffect( effectElem ) );
    1181                 :         50 :       if ( effect && !QgsPaintEffectRegistry::isDefaultStack( effect.get() ) )
    1182                 :         50 :         layer->setPaintEffect( effect.release() );
    1183                 :         50 :     }
    1184                 :            : 
    1185                 :            :     // restore data defined properties
    1186                 :       1980 :     QDomElement ddProps = element.firstChildElement( QStringLiteral( "data_defined_properties" ) );
    1187                 :        990 :     if ( !ddProps.isNull() )
    1188                 :            :     {
    1189                 :        990 :       layer->dataDefinedProperties().readXml( ddProps, QgsSymbolLayer::propertyDefinitions() );
    1190                 :        990 :     }
    1191                 :            : 
    1192                 :        990 :     return layer;
    1193                 :        990 :   }
    1194                 :            :   else
    1195                 :            :   {
    1196                 :          0 :     QgsDebugMsg( "unknown class " + layerClass );
    1197                 :          0 :     return nullptr;
    1198                 :            :   }
    1199                 :        990 : }
    1200                 :            : 
    1201                 :          0 : static QString _nameForSymbolType( QgsSymbol::SymbolType type )
    1202                 :            : {
    1203                 :          0 :   switch ( type )
    1204                 :            :   {
    1205                 :            :     case QgsSymbol::Line:
    1206                 :          0 :       return QStringLiteral( "line" );
    1207                 :            :     case QgsSymbol::Marker:
    1208                 :          0 :       return QStringLiteral( "marker" );
    1209                 :            :     case QgsSymbol::Fill:
    1210                 :          0 :       return QStringLiteral( "fill" );
    1211                 :            :     default:
    1212                 :          0 :       return QString();
    1213                 :            :   }
    1214                 :          0 : }
    1215                 :            : 
    1216                 :          0 : QDomElement QgsSymbolLayerUtils::saveSymbol( const QString &name, const QgsSymbol *symbol, QDomDocument &doc, const QgsReadWriteContext &context )
    1217                 :            : {
    1218                 :            :   Q_ASSERT( symbol );
    1219                 :          0 :   QDomElement symEl = doc.createElement( QStringLiteral( "symbol" ) );
    1220                 :          0 :   symEl.setAttribute( QStringLiteral( "type" ), _nameForSymbolType( symbol->type() ) );
    1221                 :          0 :   symEl.setAttribute( QStringLiteral( "name" ), name );
    1222                 :          0 :   symEl.setAttribute( QStringLiteral( "alpha" ), QString::number( symbol->opacity() ) );
    1223                 :          0 :   symEl.setAttribute( QStringLiteral( "clip_to_extent" ), symbol->clipFeaturesToExtent() ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
    1224                 :          0 :   symEl.setAttribute( QStringLiteral( "force_rhr" ), symbol->forceRHR() ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
    1225                 :            :   //QgsDebugMsg( "num layers " + QString::number( symbol->symbolLayerCount() ) );
    1226                 :            : 
    1227                 :          0 :   QDomElement ddProps = doc.createElement( QStringLiteral( "data_defined_properties" ) );
    1228                 :          0 :   symbol->dataDefinedProperties().writeXml( ddProps, QgsSymbol::propertyDefinitions() );
    1229                 :          0 :   symEl.appendChild( ddProps );
    1230                 :            : 
    1231                 :          0 :   for ( int i = 0; i < symbol->symbolLayerCount(); i++ )
    1232                 :            :   {
    1233                 :          0 :     const QgsSymbolLayer *layer = symbol->symbolLayer( i );
    1234                 :            : 
    1235                 :          0 :     QDomElement layerEl = doc.createElement( QStringLiteral( "layer" ) );
    1236                 :          0 :     layerEl.setAttribute( QStringLiteral( "class" ), layer->layerType() );
    1237                 :          0 :     layerEl.setAttribute( QStringLiteral( "enabled" ), layer->enabled() );
    1238                 :          0 :     layerEl.setAttribute( QStringLiteral( "locked" ), layer->isLocked() );
    1239                 :          0 :     layerEl.setAttribute( QStringLiteral( "pass" ), layer->renderingPass() );
    1240                 :            : 
    1241                 :          0 :     QVariantMap props = layer->properties();
    1242                 :            : 
    1243                 :            :     // if there are any paths in properties, convert them from absolute to relative
    1244                 :          0 :     QgsApplication::symbolLayerRegistry()->resolvePaths( layer->layerType(), props, context.pathResolver(), true );
    1245                 :            : 
    1246                 :          0 :     saveProperties( props, doc, layerEl );
    1247                 :            : 
    1248                 :          0 :     if ( layer->paintEffect() && !QgsPaintEffectRegistry::isDefaultStack( layer->paintEffect() ) )
    1249                 :          0 :       layer->paintEffect()->saveProperties( doc, layerEl );
    1250                 :            : 
    1251                 :          0 :     QDomElement ddProps = doc.createElement( QStringLiteral( "data_defined_properties" ) );
    1252                 :          0 :     layer->dataDefinedProperties().writeXml( ddProps, QgsSymbolLayer::propertyDefinitions() );
    1253                 :          0 :     layerEl.appendChild( ddProps );
    1254                 :            : 
    1255                 :          0 :     if ( const QgsSymbol *subSymbol = const_cast< QgsSymbolLayer * >( layer )->subSymbol() )
    1256                 :            :     {
    1257                 :          0 :       QString subname = QStringLiteral( "@%1@%2" ).arg( name ).arg( i );
    1258                 :          0 :       QDomElement subEl = saveSymbol( subname, subSymbol, doc, context );
    1259                 :          0 :       layerEl.appendChild( subEl );
    1260                 :          0 :     }
    1261                 :          0 :     symEl.appendChild( layerEl );
    1262                 :          0 :   }
    1263                 :            : 
    1264                 :          0 :   return symEl;
    1265                 :          0 : }
    1266                 :            : 
    1267                 :          0 : QString QgsSymbolLayerUtils::symbolProperties( QgsSymbol *symbol )
    1268                 :            : {
    1269                 :          0 :   QDomDocument doc( QStringLiteral( "qgis-symbol-definition" ) );
    1270                 :          0 :   QDomElement symbolElem = saveSymbol( QStringLiteral( "symbol" ), symbol, doc, QgsReadWriteContext() );
    1271                 :          0 :   QString props;
    1272                 :          0 :   QTextStream stream( &props );
    1273                 :          0 :   symbolElem.save( stream, -1 );
    1274                 :          0 :   return props;
    1275                 :          0 : }
    1276                 :            : 
    1277                 :          0 : bool QgsSymbolLayerUtils::createSymbolLayerListFromSld( QDomElement &element,
    1278                 :            :     QgsWkbTypes::GeometryType geomType,
    1279                 :            :     QgsSymbolLayerList &layers )
    1280                 :            : {
    1281                 :          0 :   QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
    1282                 :            : 
    1283                 :          0 :   if ( element.isNull() )
    1284                 :          0 :     return false;
    1285                 :            : 
    1286                 :          0 :   QgsSymbolLayer *l = nullptr;
    1287                 :            : 
    1288                 :          0 :   QString symbolizerName = element.localName();
    1289                 :            : 
    1290                 :          0 :   if ( symbolizerName == QLatin1String( "PointSymbolizer" ) )
    1291                 :            :   {
    1292                 :            :     // first check for Graphic element, nothing will be rendered if not found
    1293                 :          0 :     QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
    1294                 :          0 :     if ( graphicElem.isNull() )
    1295                 :            :     {
    1296                 :          0 :       QgsDebugMsg( QStringLiteral( "Graphic element not found in PointSymbolizer" ) );
    1297                 :          0 :     }
    1298                 :            :     else
    1299                 :            :     {
    1300                 :          0 :       switch ( geomType )
    1301                 :            :       {
    1302                 :            :         case QgsWkbTypes::PolygonGeometry:
    1303                 :            :           // polygon layer and point symbolizer: draw polygon centroid
    1304                 :          0 :           l = QgsApplication::symbolLayerRegistry()->createSymbolLayerFromSld( QStringLiteral( "CentroidFill" ), element );
    1305                 :          0 :           if ( l )
    1306                 :          0 :             layers.append( l );
    1307                 :            : 
    1308                 :          0 :           break;
    1309                 :            : 
    1310                 :            :         case QgsWkbTypes::PointGeometry:
    1311                 :            :           // point layer and point symbolizer: use markers
    1312                 :          0 :           l = createMarkerLayerFromSld( element );
    1313                 :          0 :           if ( l )
    1314                 :          0 :             layers.append( l );
    1315                 :            : 
    1316                 :          0 :           break;
    1317                 :            : 
    1318                 :            :         case QgsWkbTypes::LineGeometry:
    1319                 :            :           // line layer and point symbolizer: draw central point
    1320                 :          0 :           l = QgsApplication::symbolLayerRegistry()->createSymbolLayerFromSld( QStringLiteral( "SimpleMarker" ), element );
    1321                 :          0 :           if ( l )
    1322                 :          0 :             layers.append( l );
    1323                 :            : 
    1324                 :          0 :           break;
    1325                 :            : 
    1326                 :            :         default:
    1327                 :          0 :           break;
    1328                 :            :       }
    1329                 :            :     }
    1330                 :          0 :   }
    1331                 :            : 
    1332                 :          0 :   if ( symbolizerName == QLatin1String( "LineSymbolizer" ) )
    1333                 :            :   {
    1334                 :            :     // check for Stroke element, nothing will be rendered if not found
    1335                 :          0 :     QDomElement strokeElem = element.firstChildElement( QStringLiteral( "Stroke" ) );
    1336                 :          0 :     if ( strokeElem.isNull() )
    1337                 :            :     {
    1338                 :          0 :       QgsDebugMsg( QStringLiteral( "Stroke element not found in LineSymbolizer" ) );
    1339                 :          0 :     }
    1340                 :            :     else
    1341                 :            :     {
    1342                 :          0 :       switch ( geomType )
    1343                 :            :       {
    1344                 :            :         case QgsWkbTypes::PolygonGeometry:
    1345                 :            :         case QgsWkbTypes::LineGeometry:
    1346                 :            :           // polygon layer and line symbolizer: draw polygon stroke
    1347                 :            :           // line layer and line symbolizer: draw line
    1348                 :          0 :           l = createLineLayerFromSld( element );
    1349                 :          0 :           if ( l )
    1350                 :          0 :             layers.append( l );
    1351                 :            : 
    1352                 :          0 :           break;
    1353                 :            : 
    1354                 :            :         case QgsWkbTypes::PointGeometry:
    1355                 :            :           // point layer and line symbolizer: draw a little line marker
    1356                 :          0 :           l = QgsApplication::symbolLayerRegistry()->createSymbolLayerFromSld( QStringLiteral( "MarkerLine" ), element );
    1357                 :          0 :           if ( l )
    1358                 :          0 :             layers.append( l );
    1359                 :            : 
    1360                 :          0 :           break;
    1361                 :            : 
    1362                 :            :         default:
    1363                 :          0 :           break;
    1364                 :            :       }
    1365                 :            :     }
    1366                 :          0 :   }
    1367                 :            : 
    1368                 :          0 :   if ( symbolizerName == QLatin1String( "PolygonSymbolizer" ) )
    1369                 :            :   {
    1370                 :            :     // get Fill and Stroke elements, nothing will be rendered if both are missing
    1371                 :          0 :     QDomElement fillElem = element.firstChildElement( QStringLiteral( "Fill" ) );
    1372                 :          0 :     QDomElement strokeElem = element.firstChildElement( QStringLiteral( "Stroke" ) );
    1373                 :          0 :     if ( fillElem.isNull() && strokeElem.isNull() )
    1374                 :            :     {
    1375                 :          0 :       QgsDebugMsg( QStringLiteral( "neither Fill nor Stroke element not found in PolygonSymbolizer" ) );
    1376                 :          0 :     }
    1377                 :            :     else
    1378                 :            :     {
    1379                 :          0 :       QgsSymbolLayer *l = nullptr;
    1380                 :            : 
    1381                 :          0 :       switch ( geomType )
    1382                 :            :       {
    1383                 :            :         case QgsWkbTypes::PolygonGeometry:
    1384                 :            :           // polygon layer and polygon symbolizer: draw fill
    1385                 :            : 
    1386                 :          0 :           l = createFillLayerFromSld( element );
    1387                 :          0 :           if ( l )
    1388                 :            :           {
    1389                 :          0 :             layers.append( l );
    1390                 :            : 
    1391                 :            :             // SVGFill and SimpleFill symbolLayerV2 supports stroke internally,
    1392                 :            :             // so don't go forward to create a different symbolLayerV2 for stroke
    1393                 :          0 :             if ( l->layerType() == QLatin1String( "SimpleFill" ) || l->layerType() == QLatin1String( "SVGFill" ) )
    1394                 :          0 :               break;
    1395                 :          0 :           }
    1396                 :            : 
    1397                 :            :           // now create polygon stroke
    1398                 :            :           // polygon layer and polygon symbolizer: draw polygon stroke
    1399                 :          0 :           l = createLineLayerFromSld( element );
    1400                 :          0 :           if ( l )
    1401                 :          0 :             layers.append( l );
    1402                 :            : 
    1403                 :          0 :           break;
    1404                 :            : 
    1405                 :            :         case QgsWkbTypes::LineGeometry:
    1406                 :            :           // line layer and polygon symbolizer: draw line
    1407                 :          0 :           l = createLineLayerFromSld( element );
    1408                 :          0 :           if ( l )
    1409                 :          0 :             layers.append( l );
    1410                 :            : 
    1411                 :          0 :           break;
    1412                 :            : 
    1413                 :            :         case QgsWkbTypes::PointGeometry:
    1414                 :            :           // point layer and polygon symbolizer: draw a square marker
    1415                 :          0 :           convertPolygonSymbolizerToPointMarker( element, layers );
    1416                 :          0 :           break;
    1417                 :            : 
    1418                 :            :         default:
    1419                 :          0 :           break;
    1420                 :            :       }
    1421                 :            :     }
    1422                 :          0 :   }
    1423                 :            : 
    1424                 :          0 :   return true;
    1425                 :          0 : }
    1426                 :            : 
    1427                 :          0 : QgsSymbolLayer *QgsSymbolLayerUtils::createFillLayerFromSld( QDomElement &element )
    1428                 :            : {
    1429                 :          0 :   QDomElement fillElem = element.firstChildElement( QStringLiteral( "Fill" ) );
    1430                 :          0 :   if ( fillElem.isNull() )
    1431                 :            :   {
    1432                 :          0 :     QgsDebugMsg( QStringLiteral( "Fill element not found" ) );
    1433                 :          0 :     return nullptr;
    1434                 :            :   }
    1435                 :            : 
    1436                 :          0 :   QgsSymbolLayer *l = nullptr;
    1437                 :            : 
    1438                 :          0 :   if ( needLinePatternFill( element ) )
    1439                 :          0 :     l = QgsApplication::symbolLayerRegistry()->createSymbolLayerFromSld( QStringLiteral( "LinePatternFill" ), element );
    1440                 :          0 :   else if ( needPointPatternFill( element ) )
    1441                 :          0 :     l = QgsApplication::symbolLayerRegistry()->createSymbolLayerFromSld( QStringLiteral( "PointPatternFill" ), element );
    1442                 :          0 :   else if ( needSvgFill( element ) )
    1443                 :          0 :     l = QgsApplication::symbolLayerRegistry()->createSymbolLayerFromSld( QStringLiteral( "SVGFill" ), element );
    1444                 :            :   else
    1445                 :          0 :     l = QgsApplication::symbolLayerRegistry()->createSymbolLayerFromSld( QStringLiteral( "SimpleFill" ), element );
    1446                 :            : 
    1447                 :          0 :   return l;
    1448                 :          0 : }
    1449                 :            : 
    1450                 :          0 : QgsSymbolLayer *QgsSymbolLayerUtils::createLineLayerFromSld( QDomElement &element )
    1451                 :            : {
    1452                 :          0 :   QDomElement strokeElem = element.firstChildElement( QStringLiteral( "Stroke" ) );
    1453                 :          0 :   if ( strokeElem.isNull() )
    1454                 :            :   {
    1455                 :          0 :     QgsDebugMsg( QStringLiteral( "Stroke element not found" ) );
    1456                 :          0 :     return nullptr;
    1457                 :            :   }
    1458                 :            : 
    1459                 :          0 :   QgsSymbolLayer *l = nullptr;
    1460                 :            : 
    1461                 :          0 :   if ( needMarkerLine( element ) )
    1462                 :          0 :     l = QgsApplication::symbolLayerRegistry()->createSymbolLayerFromSld( QStringLiteral( "MarkerLine" ), element );
    1463                 :            :   else
    1464                 :          0 :     l = QgsApplication::symbolLayerRegistry()->createSymbolLayerFromSld( QStringLiteral( "SimpleLine" ), element );
    1465                 :            : 
    1466                 :          0 :   return l;
    1467                 :          0 : }
    1468                 :            : 
    1469                 :          0 : QgsSymbolLayer *QgsSymbolLayerUtils::createMarkerLayerFromSld( QDomElement &element )
    1470                 :            : {
    1471                 :          0 :   QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
    1472                 :          0 :   if ( graphicElem.isNull() )
    1473                 :            :   {
    1474                 :          0 :     QgsDebugMsg( QStringLiteral( "Graphic element not found" ) );
    1475                 :          0 :     return nullptr;
    1476                 :            :   }
    1477                 :            : 
    1478                 :          0 :   QgsSymbolLayer *l = nullptr;
    1479                 :            : 
    1480                 :          0 :   if ( needFontMarker( element ) )
    1481                 :          0 :     l = QgsApplication::symbolLayerRegistry()->createSymbolLayerFromSld( QStringLiteral( "FontMarker" ), element );
    1482                 :          0 :   else if ( needSvgMarker( element ) )
    1483                 :          0 :     l = QgsApplication::symbolLayerRegistry()->createSymbolLayerFromSld( QStringLiteral( "SvgMarker" ), element );
    1484                 :          0 :   else if ( needEllipseMarker( element ) )
    1485                 :          0 :     l = QgsApplication::symbolLayerRegistry()->createSymbolLayerFromSld( QStringLiteral( "EllipseMarker" ), element );
    1486                 :            :   else
    1487                 :          0 :     l = QgsApplication::symbolLayerRegistry()->createSymbolLayerFromSld( QStringLiteral( "SimpleMarker" ), element );
    1488                 :            : 
    1489                 :          0 :   return l;
    1490                 :          0 : }
    1491                 :            : 
    1492                 :          0 : bool QgsSymbolLayerUtils::hasExternalGraphic( QDomElement &element )
    1493                 :            : {
    1494                 :          0 :   QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
    1495                 :          0 :   if ( graphicElem.isNull() )
    1496                 :          0 :     return false;
    1497                 :            : 
    1498                 :          0 :   QDomElement externalGraphicElem = graphicElem.firstChildElement( QStringLiteral( "ExternalGraphic" ) );
    1499                 :          0 :   if ( externalGraphicElem.isNull() )
    1500                 :          0 :     return false;
    1501                 :            : 
    1502                 :            :   // check for format
    1503                 :          0 :   QDomElement formatElem = externalGraphicElem.firstChildElement( QStringLiteral( "Format" ) );
    1504                 :          0 :   if ( formatElem.isNull() )
    1505                 :          0 :     return false;
    1506                 :            : 
    1507                 :          0 :   QString format = formatElem.firstChild().nodeValue();
    1508                 :          0 :   if ( format != QLatin1String( "image/svg+xml" ) )
    1509                 :            :   {
    1510                 :          0 :     QgsDebugMsg( "unsupported External Graphic format found: " + format );
    1511                 :          0 :     return false;
    1512                 :            :   }
    1513                 :            : 
    1514                 :            :   // check for a valid content
    1515                 :          0 :   QDomElement onlineResourceElem = externalGraphicElem.firstChildElement( QStringLiteral( "OnlineResource" ) );
    1516                 :          0 :   QDomElement inlineContentElem = externalGraphicElem.firstChildElement( QStringLiteral( "InlineContent" ) );
    1517                 :          0 :   if ( !onlineResourceElem.isNull() )
    1518                 :            :   {
    1519                 :          0 :     return true;
    1520                 :            :   }
    1521                 :            : #if 0
    1522                 :            :   else if ( !inlineContentElem.isNull() )
    1523                 :            :   {
    1524                 :            :     return false; // not implemented yet
    1525                 :            :   }
    1526                 :            : #endif
    1527                 :            :   else
    1528                 :            :   {
    1529                 :          0 :     return false;
    1530                 :            :   }
    1531                 :          0 : }
    1532                 :            : 
    1533                 :          0 : bool QgsSymbolLayerUtils::hasWellKnownMark( QDomElement &element )
    1534                 :            : {
    1535                 :          0 :   QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
    1536                 :          0 :   if ( graphicElem.isNull() )
    1537                 :          0 :     return false;
    1538                 :            : 
    1539                 :          0 :   QDomElement markElem = graphicElem.firstChildElement( QStringLiteral( "Mark" ) );
    1540                 :          0 :   if ( markElem.isNull() )
    1541                 :          0 :     return false;
    1542                 :            : 
    1543                 :          0 :   QDomElement wellKnownNameElem = markElem.firstChildElement( QStringLiteral( "WellKnownName" ) );
    1544                 :          0 :   return !wellKnownNameElem.isNull();
    1545                 :          0 : }
    1546                 :            : 
    1547                 :            : 
    1548                 :          0 : bool QgsSymbolLayerUtils::needFontMarker( QDomElement &element )
    1549                 :            : {
    1550                 :          0 :   QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
    1551                 :          0 :   if ( graphicElem.isNull() )
    1552                 :          0 :     return false;
    1553                 :            : 
    1554                 :          0 :   QDomElement markElem = graphicElem.firstChildElement( QStringLiteral( "Mark" ) );
    1555                 :          0 :   if ( markElem.isNull() )
    1556                 :          0 :     return false;
    1557                 :            : 
    1558                 :            :   // check for format
    1559                 :          0 :   QDomElement formatElem = markElem.firstChildElement( QStringLiteral( "Format" ) );
    1560                 :          0 :   if ( formatElem.isNull() )
    1561                 :          0 :     return false;
    1562                 :            : 
    1563                 :          0 :   QString format = formatElem.firstChild().nodeValue();
    1564                 :          0 :   if ( format != QLatin1String( "ttf" ) )
    1565                 :            :   {
    1566                 :          0 :     QgsDebugMsg( "unsupported Graphic Mark format found: " + format );
    1567                 :          0 :     return false;
    1568                 :            :   }
    1569                 :            : 
    1570                 :            :   // check for a valid content
    1571                 :          0 :   QDomElement onlineResourceElem = markElem.firstChildElement( QStringLiteral( "OnlineResource" ) );
    1572                 :          0 :   QDomElement inlineContentElem = markElem.firstChildElement( QStringLiteral( "InlineContent" ) );
    1573                 :          0 :   if ( !onlineResourceElem.isNull() )
    1574                 :            :   {
    1575                 :            :     // mark with ttf format has a markIndex element
    1576                 :          0 :     QDomElement markIndexElem = markElem.firstChildElement( QStringLiteral( "MarkIndex" ) );
    1577                 :          0 :     if ( !markIndexElem.isNull() )
    1578                 :          0 :       return true;
    1579                 :          0 :   }
    1580                 :          0 :   else if ( !inlineContentElem.isNull() )
    1581                 :            :   {
    1582                 :          0 :     return false; // not implemented yet
    1583                 :            :   }
    1584                 :            : 
    1585                 :          0 :   return false;
    1586                 :          0 : }
    1587                 :            : 
    1588                 :          0 : bool QgsSymbolLayerUtils::needSvgMarker( QDomElement &element )
    1589                 :            : {
    1590                 :          0 :   return hasExternalGraphic( element );
    1591                 :            : }
    1592                 :            : 
    1593                 :          0 : bool QgsSymbolLayerUtils::needEllipseMarker( QDomElement &element )
    1594                 :            : {
    1595                 :          0 :   QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
    1596                 :          0 :   if ( graphicElem.isNull() )
    1597                 :          0 :     return false;
    1598                 :            : 
    1599                 :          0 :   QgsStringMap vendorOptions = QgsSymbolLayerUtils::getVendorOptionList( graphicElem );
    1600                 :          0 :   for ( QgsStringMap::iterator it = vendorOptions.begin(); it != vendorOptions.end(); ++it )
    1601                 :            :   {
    1602                 :          0 :     if ( it.key() == QLatin1String( "widthHeightFactor" ) )
    1603                 :            :     {
    1604                 :          0 :       return true;
    1605                 :            :     }
    1606                 :          0 :   }
    1607                 :            : 
    1608                 :          0 :   return false;
    1609                 :          0 : }
    1610                 :            : 
    1611                 :          0 : bool QgsSymbolLayerUtils::needMarkerLine( QDomElement &element )
    1612                 :            : {
    1613                 :          0 :   QDomElement strokeElem = element.firstChildElement( QStringLiteral( "Stroke" ) );
    1614                 :          0 :   if ( strokeElem.isNull() )
    1615                 :          0 :     return false;
    1616                 :            : 
    1617                 :          0 :   QDomElement graphicStrokeElem = strokeElem.firstChildElement( QStringLiteral( "GraphicStroke" ) );
    1618                 :          0 :   if ( graphicStrokeElem.isNull() )
    1619                 :          0 :     return false;
    1620                 :            : 
    1621                 :          0 :   return hasWellKnownMark( graphicStrokeElem );
    1622                 :          0 : }
    1623                 :            : 
    1624                 :          0 : bool QgsSymbolLayerUtils::needLinePatternFill( QDomElement &element )
    1625                 :            : {
    1626                 :          0 :   QDomElement fillElem = element.firstChildElement( QStringLiteral( "Fill" ) );
    1627                 :          0 :   if ( fillElem.isNull() )
    1628                 :          0 :     return false;
    1629                 :            : 
    1630                 :          0 :   QDomElement graphicFillElem = fillElem.firstChildElement( QStringLiteral( "GraphicFill" ) );
    1631                 :          0 :   if ( graphicFillElem.isNull() )
    1632                 :          0 :     return false;
    1633                 :            : 
    1634                 :          0 :   QDomElement graphicElem = graphicFillElem.firstChildElement( QStringLiteral( "Graphic" ) );
    1635                 :          0 :   if ( graphicElem.isNull() )
    1636                 :          0 :     return false;
    1637                 :            : 
    1638                 :            :   // line pattern fill uses horline wellknown marker with an angle
    1639                 :            : 
    1640                 :          0 :   QString name;
    1641                 :          0 :   QColor fillColor, strokeColor;
    1642                 :            :   double size, strokeWidth;
    1643                 :            :   Qt::PenStyle strokeStyle;
    1644                 :          0 :   if ( !wellKnownMarkerFromSld( graphicElem, name, fillColor, strokeColor, strokeStyle, strokeWidth, size ) )
    1645                 :          0 :     return false;
    1646                 :            : 
    1647                 :          0 :   if ( name != QLatin1String( "horline" ) )
    1648                 :          0 :     return false;
    1649                 :            : 
    1650                 :          0 :   QString angleFunc;
    1651                 :          0 :   if ( !rotationFromSldElement( graphicElem, angleFunc ) )
    1652                 :          0 :     return false;
    1653                 :            : 
    1654                 :            :   bool ok;
    1655                 :          0 :   double angle = angleFunc.toDouble( &ok );
    1656                 :          0 :   return !( !ok || qgsDoubleNear( angle, 0.0 ) );
    1657                 :          0 : }
    1658                 :            : 
    1659                 :          0 : bool QgsSymbolLayerUtils::needPointPatternFill( QDomElement &element )
    1660                 :            : {
    1661                 :          0 :   Q_UNUSED( element )
    1662                 :          0 :   return false;
    1663                 :            : }
    1664                 :            : 
    1665                 :          0 : bool QgsSymbolLayerUtils::needSvgFill( QDomElement &element )
    1666                 :            : {
    1667                 :          0 :   QDomElement fillElem = element.firstChildElement( QStringLiteral( "Fill" ) );
    1668                 :          0 :   if ( fillElem.isNull() )
    1669                 :          0 :     return false;
    1670                 :            : 
    1671                 :          0 :   QDomElement graphicFillElem = fillElem.firstChildElement( QStringLiteral( "GraphicFill" ) );
    1672                 :          0 :   if ( graphicFillElem.isNull() )
    1673                 :          0 :     return false;
    1674                 :            : 
    1675                 :          0 :   return hasExternalGraphic( graphicFillElem );
    1676                 :          0 : }
    1677                 :            : 
    1678                 :            : 
    1679                 :          0 : bool QgsSymbolLayerUtils::convertPolygonSymbolizerToPointMarker( QDomElement &element, QgsSymbolLayerList &layerList )
    1680                 :            : {
    1681                 :          0 :   QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
    1682                 :            : 
    1683                 :            :   /* SE 1.1 says about PolygonSymbolizer:
    1684                 :            :   if a point geometry is referenced instead of a polygon,
    1685                 :            :   then a small, square, ortho-normal polygon should be
    1686                 :            :   constructed for rendering.
    1687                 :            :    */
    1688                 :            : 
    1689                 :          0 :   QgsSymbolLayerList layers;
    1690                 :            : 
    1691                 :            :   // retrieve both Fill and Stroke elements
    1692                 :          0 :   QDomElement fillElem = element.firstChildElement( QStringLiteral( "Fill" ) );
    1693                 :          0 :   QDomElement strokeElem = element.firstChildElement( QStringLiteral( "Stroke" ) );
    1694                 :            : 
    1695                 :            :   // first symbol layer
    1696                 :            :   {
    1697                 :          0 :     bool validFill = false, validStroke = false;
    1698                 :            : 
    1699                 :            :     // check for simple fill
    1700                 :            :     // Fill element can contain some SvgParameter elements
    1701                 :          0 :     QColor fillColor;
    1702                 :            :     Qt::BrushStyle fillStyle;
    1703                 :            : 
    1704                 :          0 :     if ( fillFromSld( fillElem, fillStyle, fillColor ) )
    1705                 :          0 :       validFill = true;
    1706                 :            : 
    1707                 :            :     // check for simple stroke
    1708                 :            :     // Stroke element can contain some SvgParameter elements
    1709                 :          0 :     QColor strokeColor;
    1710                 :            :     Qt::PenStyle strokeStyle;
    1711                 :          0 :     double strokeWidth = 1.0, dashOffset = 0.0;
    1712                 :          0 :     QVector<qreal> customDashPattern;
    1713                 :            : 
    1714                 :          0 :     if ( lineFromSld( strokeElem, strokeStyle, strokeColor, strokeWidth,
    1715                 :            :                       nullptr, nullptr, &customDashPattern, &dashOffset ) )
    1716                 :          0 :       validStroke = true;
    1717                 :            : 
    1718                 :          0 :     if ( validFill || validStroke )
    1719                 :            :     {
    1720                 :          0 :       QVariantMap map;
    1721                 :          0 :       map[QStringLiteral( "name" )] = QStringLiteral( "square" );
    1722                 :          0 :       map[QStringLiteral( "color" )] = encodeColor( validFill ? fillColor : Qt::transparent );
    1723                 :          0 :       map[QStringLiteral( "color_border" )] = encodeColor( validStroke ? strokeColor : Qt::transparent );
    1724                 :          0 :       map[QStringLiteral( "size" )] = QString::number( 6 );
    1725                 :          0 :       map[QStringLiteral( "angle" )] = QString::number( 0 );
    1726                 :          0 :       map[QStringLiteral( "offset" )] = encodePoint( QPointF( 0, 0 ) );
    1727                 :          0 :       layers.append( QgsApplication::symbolLayerRegistry()->createSymbolLayer( QStringLiteral( "SimpleMarker" ), map ) );
    1728                 :          0 :     }
    1729                 :          0 :   }
    1730                 :            : 
    1731                 :            :   // second symbol layer
    1732                 :            :   {
    1733                 :          0 :     bool validFill = false, validStroke = false;
    1734                 :            : 
    1735                 :            :     // check for graphic fill
    1736                 :          0 :     QString name, format;
    1737                 :          0 :     int markIndex = -1;
    1738                 :          0 :     QColor fillColor, strokeColor;
    1739                 :          0 :     double strokeWidth = 1.0, size = 0.0, angle = 0.0;
    1740                 :          0 :     QPointF offset;
    1741                 :            : 
    1742                 :            :     // Fill element can contain a GraphicFill element
    1743                 :          0 :     QDomElement graphicFillElem = fillElem.firstChildElement( QStringLiteral( "GraphicFill" ) );
    1744                 :          0 :     if ( !graphicFillElem.isNull() )
    1745                 :            :     {
    1746                 :            :       // GraphicFill element must contain a Graphic element
    1747                 :          0 :       QDomElement graphicElem = graphicFillElem.firstChildElement( QStringLiteral( "Graphic" ) );
    1748                 :          0 :       if ( !graphicElem.isNull() )
    1749                 :            :       {
    1750                 :            :         // Graphic element can contains some ExternalGraphic and Mark element
    1751                 :            :         // search for the first supported one and use it
    1752                 :          0 :         bool found = false;
    1753                 :            : 
    1754                 :          0 :         QDomElement graphicChildElem = graphicElem.firstChildElement();
    1755                 :          0 :         while ( !graphicChildElem.isNull() )
    1756                 :            :         {
    1757                 :          0 :           if ( graphicChildElem.localName() == QLatin1String( "Mark" ) )
    1758                 :            :           {
    1759                 :            :             // check for a well known name
    1760                 :          0 :             QDomElement wellKnownNameElem = graphicChildElem.firstChildElement( QStringLiteral( "WellKnownName" ) );
    1761                 :          0 :             if ( !wellKnownNameElem.isNull() )
    1762                 :            :             {
    1763                 :          0 :               name = wellKnownNameElem.firstChild().nodeValue();
    1764                 :          0 :               found = true;
    1765                 :          0 :               break;
    1766                 :            :             }
    1767                 :          0 :           }
    1768                 :            : 
    1769                 :          0 :           if ( graphicChildElem.localName() == QLatin1String( "ExternalGraphic" ) || graphicChildElem.localName() == QLatin1String( "Mark" ) )
    1770                 :            :           {
    1771                 :            :             // check for external graphic format
    1772                 :          0 :             QDomElement formatElem = graphicChildElem.firstChildElement( QStringLiteral( "Format" ) );
    1773                 :          0 :             if ( formatElem.isNull() )
    1774                 :          0 :               continue;
    1775                 :            : 
    1776                 :          0 :             format = formatElem.firstChild().nodeValue();
    1777                 :            : 
    1778                 :            :             // TODO: remove this check when more formats will be supported
    1779                 :            :             // only SVG external graphics are supported in this moment
    1780                 :          0 :             if ( graphicChildElem.localName() == QLatin1String( "ExternalGraphic" ) && format != QLatin1String( "image/svg+xml" ) )
    1781                 :          0 :               continue;
    1782                 :            : 
    1783                 :            :             // TODO: remove this check when more formats will be supported
    1784                 :            :             // only ttf marks are supported in this moment
    1785                 :          0 :             if ( graphicChildElem.localName() == QLatin1String( "Mark" ) && format != QLatin1String( "ttf" ) )
    1786                 :          0 :               continue;
    1787                 :            : 
    1788                 :            :             // check for a valid content
    1789                 :          0 :             QDomElement onlineResourceElem = graphicChildElem.firstChildElement( QStringLiteral( "OnlineResource" ) );
    1790                 :          0 :             QDomElement inlineContentElem = graphicChildElem.firstChildElement( QStringLiteral( "InlineContent" ) );
    1791                 :            : 
    1792                 :          0 :             if ( !onlineResourceElem.isNull() )
    1793                 :            :             {
    1794                 :          0 :               name = onlineResourceElem.attributeNS( QStringLiteral( "http://www.w3.org/1999/xlink" ), QStringLiteral( "href" ) );
    1795                 :            : 
    1796                 :          0 :               if ( graphicChildElem.localName() == QLatin1String( "Mark" ) && format == QLatin1String( "ttf" ) )
    1797                 :            :               {
    1798                 :            :                 // mark with ttf format may have a name like ttf://fontFamily
    1799                 :          0 :                 if ( name.startsWith( QLatin1String( "ttf://" ) ) )
    1800                 :          0 :                   name = name.mid( 6 );
    1801                 :            : 
    1802                 :            :                 // mark with ttf format has a markIndex element
    1803                 :          0 :                 QDomElement markIndexElem = graphicChildElem.firstChildElement( QStringLiteral( "MarkIndex" ) );
    1804                 :          0 :                 if ( markIndexElem.isNull() )
    1805                 :          0 :                   continue;
    1806                 :            : 
    1807                 :            :                 bool ok;
    1808                 :          0 :                 int v = markIndexElem.firstChild().nodeValue().toInt( &ok );
    1809                 :          0 :                 if ( !ok || v < 0 )
    1810                 :          0 :                   continue;
    1811                 :            : 
    1812                 :          0 :                 markIndex = v;
    1813                 :          0 :               }
    1814                 :            : 
    1815                 :          0 :               found = true;
    1816                 :          0 :               break;
    1817                 :            :             }
    1818                 :            : #if 0
    1819                 :            :             else if ( !inlineContentElem.isNull() )
    1820                 :            :               continue; // TODO: not implemented yet
    1821                 :            : #endif
    1822                 :            :             else
    1823                 :          0 :               continue;
    1824                 :          0 :           }
    1825                 :            : 
    1826                 :            :           // if Mark element is present but it doesn't contains neither
    1827                 :            :           // WellKnownName nor OnlineResource nor InlineContent,
    1828                 :            :           // use the default mark (square)
    1829                 :          0 :           if ( graphicChildElem.localName() == QLatin1String( "Mark" ) )
    1830                 :            :           {
    1831                 :          0 :             name = QStringLiteral( "square" );
    1832                 :          0 :             found = true;
    1833                 :          0 :             break;
    1834                 :            :           }
    1835                 :            :         }
    1836                 :            : 
    1837                 :            :         // if found a valid Mark, check for its Fill and Stroke element
    1838                 :          0 :         if ( found && graphicChildElem.localName() == QLatin1String( "Mark" ) )
    1839                 :            :         {
    1840                 :            :           // XXX: recursive definition!?! couldn't be dangerous???
    1841                 :            :           // to avoid recursion we handle only simple fill and simple stroke
    1842                 :            : 
    1843                 :            :           // check for simple fill
    1844                 :            :           // Fill element can contain some SvgParameter elements
    1845                 :            :           Qt::BrushStyle markFillStyle;
    1846                 :            : 
    1847                 :          0 :           QDomElement markFillElem = graphicChildElem.firstChildElement( QStringLiteral( "Fill" ) );
    1848                 :          0 :           if ( fillFromSld( markFillElem, markFillStyle, fillColor ) )
    1849                 :          0 :             validFill = true;
    1850                 :            : 
    1851                 :            :           // check for simple stroke
    1852                 :            :           // Stroke element can contain some SvgParameter elements
    1853                 :            :           Qt::PenStyle strokeStyle;
    1854                 :          0 :           double strokeWidth = 1.0, dashOffset = 0.0;
    1855                 :          0 :           QVector<qreal> customDashPattern;
    1856                 :            : 
    1857                 :          0 :           QDomElement markStrokeElem = graphicChildElem.firstChildElement( QStringLiteral( "Stroke" ) );
    1858                 :          0 :           if ( lineFromSld( markStrokeElem, strokeStyle, strokeColor, strokeWidth,
    1859                 :            :                             nullptr, nullptr, &customDashPattern, &dashOffset ) )
    1860                 :          0 :             validStroke = true;
    1861                 :          0 :         }
    1862                 :            : 
    1863                 :          0 :         if ( found )
    1864                 :            :         {
    1865                 :            :           // check for Opacity, Size, Rotation, AnchorPoint, Displacement
    1866                 :          0 :           QDomElement opacityElem = graphicElem.firstChildElement( QStringLiteral( "Opacity" ) );
    1867                 :          0 :           if ( !opacityElem.isNull() )
    1868                 :          0 :             fillColor.setAlpha( decodeSldAlpha( opacityElem.firstChild().nodeValue() ) );
    1869                 :            : 
    1870                 :          0 :           QDomElement sizeElem = graphicElem.firstChildElement( QStringLiteral( "Size" ) );
    1871                 :          0 :           if ( !sizeElem.isNull() )
    1872                 :            :           {
    1873                 :            :             bool ok;
    1874                 :          0 :             double v = sizeElem.firstChild().nodeValue().toDouble( &ok );
    1875                 :          0 :             if ( ok && v > 0 )
    1876                 :          0 :               size = v;
    1877                 :          0 :           }
    1878                 :            : 
    1879                 :          0 :           QString angleFunc;
    1880                 :          0 :           if ( rotationFromSldElement( graphicElem, angleFunc ) && !angleFunc.isEmpty() )
    1881                 :            :           {
    1882                 :            :             bool ok;
    1883                 :          0 :             double v = angleFunc.toDouble( &ok );
    1884                 :          0 :             if ( ok )
    1885                 :          0 :               angle = v;
    1886                 :          0 :           }
    1887                 :            : 
    1888                 :          0 :           displacementFromSldElement( graphicElem, offset );
    1889                 :          0 :         }
    1890                 :          0 :       }
    1891                 :          0 :     }
    1892                 :            : 
    1893                 :          0 :     if ( validFill || validStroke )
    1894                 :            :     {
    1895                 :          0 :       if ( format == QLatin1String( "image/svg+xml" ) )
    1896                 :            :       {
    1897                 :          0 :         QVariantMap map;
    1898                 :          0 :         map[QStringLiteral( "name" )] = name;
    1899                 :          0 :         map[QStringLiteral( "fill" )] = fillColor.name();
    1900                 :          0 :         map[QStringLiteral( "outline" )] = strokeColor.name();
    1901                 :          0 :         map[QStringLiteral( "outline-width" )] = QString::number( strokeWidth );
    1902                 :          0 :         if ( !qgsDoubleNear( size, 0.0 ) )
    1903                 :          0 :           map[QStringLiteral( "size" )] = QString::number( size );
    1904                 :          0 :         if ( !qgsDoubleNear( angle, 0.0 ) )
    1905                 :          0 :           map[QStringLiteral( "angle" )] = QString::number( angle );
    1906                 :          0 :         if ( !offset.isNull() )
    1907                 :          0 :           map[QStringLiteral( "offset" )] = encodePoint( offset );
    1908                 :          0 :         layers.append( QgsApplication::symbolLayerRegistry()->createSymbolLayer( QStringLiteral( "SvgMarker" ), map ) );
    1909                 :          0 :       }
    1910                 :          0 :       else if ( format == QLatin1String( "ttf" ) )
    1911                 :            :       {
    1912                 :          0 :         QVariantMap map;
    1913                 :          0 :         map[QStringLiteral( "font" )] = name;
    1914                 :          0 :         map[QStringLiteral( "chr" )] = markIndex;
    1915                 :          0 :         map[QStringLiteral( "color" )] = encodeColor( validFill ? fillColor : Qt::transparent );
    1916                 :          0 :         if ( size > 0 )
    1917                 :          0 :           map[QStringLiteral( "size" )] = QString::number( size );
    1918                 :          0 :         if ( !qgsDoubleNear( angle, 0.0 ) )
    1919                 :          0 :           map[QStringLiteral( "angle" )] = QString::number( angle );
    1920                 :          0 :         if ( !offset.isNull() )
    1921                 :          0 :           map[QStringLiteral( "offset" )] = encodePoint( offset );
    1922                 :          0 :         layers.append( QgsApplication::symbolLayerRegistry()->createSymbolLayer( QStringLiteral( "FontMarker" ), map ) );
    1923                 :          0 :       }
    1924                 :          0 :     }
    1925                 :          0 :   }
    1926                 :            : 
    1927                 :          0 :   if ( layers.isEmpty() )
    1928                 :          0 :     return false;
    1929                 :            : 
    1930                 :          0 :   layerList << layers;
    1931                 :          0 :   layers.clear();
    1932                 :          0 :   return true;
    1933                 :          0 : }
    1934                 :            : 
    1935                 :          0 : void QgsSymbolLayerUtils::fillToSld( QDomDocument &doc, QDomElement &element, Qt::BrushStyle brushStyle, const QColor &color )
    1936                 :            : {
    1937                 :          0 :   QString patternName;
    1938                 :          0 :   switch ( brushStyle )
    1939                 :            :   {
    1940                 :            :     case Qt::NoBrush:
    1941                 :          0 :       return;
    1942                 :            : 
    1943                 :            :     case Qt::SolidPattern:
    1944                 :          0 :       if ( color.isValid() )
    1945                 :            :       {
    1946                 :          0 :         element.appendChild( createSvgParameterElement( doc, QStringLiteral( "fill" ), color.name() ) );
    1947                 :          0 :         if ( color.alpha() < 255 )
    1948                 :          0 :           element.appendChild( createSvgParameterElement( doc, QStringLiteral( "fill-opacity" ), encodeSldAlpha( color.alpha() ) ) );
    1949                 :          0 :       }
    1950                 :          0 :       return;
    1951                 :            : 
    1952                 :            :     case Qt::CrossPattern:
    1953                 :            :     case Qt::DiagCrossPattern:
    1954                 :            :     case Qt::HorPattern:
    1955                 :            :     case Qt::VerPattern:
    1956                 :            :     case Qt::BDiagPattern:
    1957                 :            :     case Qt::FDiagPattern:
    1958                 :            :     case Qt::Dense1Pattern:
    1959                 :            :     case Qt::Dense2Pattern:
    1960                 :            :     case Qt::Dense3Pattern:
    1961                 :            :     case Qt::Dense4Pattern:
    1962                 :            :     case Qt::Dense5Pattern:
    1963                 :            :     case Qt::Dense6Pattern:
    1964                 :            :     case Qt::Dense7Pattern:
    1965                 :          0 :       patternName = encodeSldBrushStyle( brushStyle );
    1966                 :          0 :       break;
    1967                 :            : 
    1968                 :            :     default:
    1969                 :          0 :       element.appendChild( doc.createComment( QStringLiteral( "Qt::BrushStyle '%1'' not supported yet" ).arg( brushStyle ) ) );
    1970                 :          0 :       return;
    1971                 :            :   }
    1972                 :            : 
    1973                 :          0 :   QDomElement graphicFillElem = doc.createElement( QStringLiteral( "se:GraphicFill" ) );
    1974                 :          0 :   element.appendChild( graphicFillElem );
    1975                 :            : 
    1976                 :          0 :   QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
    1977                 :          0 :   graphicFillElem.appendChild( graphicElem );
    1978                 :            : 
    1979                 :          0 :   QColor fillColor = patternName.startsWith( QLatin1String( "brush://" ) ) ? color : QColor();
    1980                 :          0 :   QColor strokeColor = !patternName.startsWith( QLatin1String( "brush://" ) ) ? color : QColor();
    1981                 :            : 
    1982                 :            :   /* Use WellKnownName tag to handle QT brush styles. */
    1983                 :          0 :   wellKnownMarkerToSld( doc, graphicElem, patternName, fillColor, strokeColor, Qt::SolidLine, -1, -1 );
    1984                 :          0 : }
    1985                 :            : 
    1986                 :          0 : bool QgsSymbolLayerUtils::fillFromSld( QDomElement &element, Qt::BrushStyle &brushStyle, QColor &color )
    1987                 :            : {
    1988                 :          0 :   QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
    1989                 :            : 
    1990                 :          0 :   brushStyle = Qt::SolidPattern;
    1991                 :          0 :   color = QColor( 128, 128, 128 );
    1992                 :            : 
    1993                 :          0 :   if ( element.isNull() )
    1994                 :            :   {
    1995                 :          0 :     brushStyle = Qt::NoBrush;
    1996                 :          0 :     color = QColor();
    1997                 :          0 :     return true;
    1998                 :            :   }
    1999                 :            : 
    2000                 :          0 :   QDomElement graphicFillElem = element.firstChildElement( QStringLiteral( "GraphicFill" ) );
    2001                 :            :   // if no GraphicFill element is found, it's a solid fill
    2002                 :          0 :   if ( graphicFillElem.isNull() )
    2003                 :            :   {
    2004                 :          0 :     QgsStringMap svgParams = getSvgParameterList( element );
    2005                 :          0 :     for ( QgsStringMap::iterator it = svgParams.begin(); it != svgParams.end(); ++it )
    2006                 :            :     {
    2007                 :          0 :       QgsDebugMsg( QStringLiteral( "found SvgParameter %1: %2" ).arg( it.key(), it.value() ) );
    2008                 :            : 
    2009                 :          0 :       if ( it.key() == QLatin1String( "fill" ) )
    2010                 :          0 :         color = QColor( it.value() );
    2011                 :          0 :       else if ( it.key() == QLatin1String( "fill-opacity" ) )
    2012                 :          0 :         color.setAlpha( decodeSldAlpha( it.value() ) );
    2013                 :          0 :     }
    2014                 :          0 :   }
    2015                 :            :   else  // wellKnown marker
    2016                 :            :   {
    2017                 :          0 :     QDomElement graphicElem = graphicFillElem.firstChildElement( QStringLiteral( "Graphic" ) );
    2018                 :          0 :     if ( graphicElem.isNull() )
    2019                 :          0 :       return false; // Graphic is required within GraphicFill
    2020                 :            : 
    2021                 :          0 :     QString patternName = QStringLiteral( "square" );
    2022                 :          0 :     QColor fillColor, strokeColor;
    2023                 :            :     double strokeWidth, size;
    2024                 :            :     Qt::PenStyle strokeStyle;
    2025                 :          0 :     if ( !wellKnownMarkerFromSld( graphicElem, patternName, fillColor, strokeColor, strokeStyle, strokeWidth, size ) )
    2026                 :          0 :       return false;
    2027                 :            : 
    2028                 :          0 :     brushStyle = decodeSldBrushStyle( patternName );
    2029                 :          0 :     if ( brushStyle == Qt::NoBrush )
    2030                 :          0 :       return false; // unable to decode brush style
    2031                 :            : 
    2032                 :          0 :     QColor c = patternName.startsWith( QLatin1String( "brush://" ) ) ? fillColor : strokeColor;
    2033                 :          0 :     if ( c.isValid() )
    2034                 :          0 :       color = c;
    2035                 :          0 :   }
    2036                 :            : 
    2037                 :          0 :   return true;
    2038                 :          0 : }
    2039                 :            : 
    2040                 :          0 : void QgsSymbolLayerUtils::lineToSld( QDomDocument &doc, QDomElement &element,
    2041                 :            :                                      Qt::PenStyle penStyle, const QColor &color, double width,
    2042                 :            :                                      const Qt::PenJoinStyle *penJoinStyle, const Qt::PenCapStyle *penCapStyle,
    2043                 :            :                                      const QVector<qreal> *customDashPattern, double dashOffset )
    2044                 :            : {
    2045                 :          0 :   QVector<qreal> dashPattern;
    2046                 :          0 :   const QVector<qreal> *pattern = &dashPattern;
    2047                 :            : 
    2048                 :          0 :   if ( penStyle == Qt::CustomDashLine && !customDashPattern )
    2049                 :            :   {
    2050                 :          0 :     element.appendChild( doc.createComment( QStringLiteral( "WARNING: Custom dash pattern required but not provided. Using default dash pattern." ) ) );
    2051                 :          0 :     penStyle = Qt::DashLine;
    2052                 :          0 :   }
    2053                 :            : 
    2054                 :          0 :   switch ( penStyle )
    2055                 :            :   {
    2056                 :            :     case Qt::NoPen:
    2057                 :          0 :       return;
    2058                 :            : 
    2059                 :            :     case Qt::SolidLine:
    2060                 :          0 :       break;
    2061                 :            : 
    2062                 :            :     case Qt::DashLine:
    2063                 :          0 :       dashPattern.push_back( 4.0 );
    2064                 :          0 :       dashPattern.push_back( 2.0 );
    2065                 :          0 :       break;
    2066                 :            :     case Qt::DotLine:
    2067                 :          0 :       dashPattern.push_back( 1.0 );
    2068                 :          0 :       dashPattern.push_back( 2.0 );
    2069                 :          0 :       break;
    2070                 :            :     case Qt::DashDotLine:
    2071                 :          0 :       dashPattern.push_back( 4.0 );
    2072                 :          0 :       dashPattern.push_back( 2.0 );
    2073                 :          0 :       dashPattern.push_back( 1.0 );
    2074                 :          0 :       dashPattern.push_back( 2.0 );
    2075                 :          0 :       break;
    2076                 :            :     case Qt::DashDotDotLine:
    2077                 :          0 :       dashPattern.push_back( 4.0 );
    2078                 :          0 :       dashPattern.push_back( 2.0 );
    2079                 :          0 :       dashPattern.push_back( 1.0 );
    2080                 :          0 :       dashPattern.push_back( 2.0 );
    2081                 :          0 :       dashPattern.push_back( 1.0 );
    2082                 :          0 :       dashPattern.push_back( 2.0 );
    2083                 :          0 :       break;
    2084                 :            : 
    2085                 :            :     case Qt::CustomDashLine:
    2086                 :            :       Q_ASSERT( customDashPattern );
    2087                 :          0 :       pattern = customDashPattern;
    2088                 :          0 :       break;
    2089                 :            : 
    2090                 :            :     default:
    2091                 :          0 :       element.appendChild( doc.createComment( QStringLiteral( "Qt::BrushStyle '%1'' not supported yet" ).arg( penStyle ) ) );
    2092                 :          0 :       return;
    2093                 :            :   }
    2094                 :            : 
    2095                 :          0 :   if ( color.isValid() )
    2096                 :            :   {
    2097                 :          0 :     element.appendChild( createSvgParameterElement( doc, QStringLiteral( "stroke" ), color.name() ) );
    2098                 :          0 :     if ( color.alpha() < 255 )
    2099                 :          0 :       element.appendChild( createSvgParameterElement( doc, QStringLiteral( "stroke-opacity" ), encodeSldAlpha( color.alpha() ) ) );
    2100                 :          0 :   }
    2101                 :          0 :   if ( width > 0 )
    2102                 :            :   {
    2103                 :          0 :     element.appendChild( createSvgParameterElement( doc, QStringLiteral( "stroke-width" ), qgsDoubleToString( width ) ) );
    2104                 :          0 :   }
    2105                 :          0 :   else if ( width == 0 )
    2106                 :            :   {
    2107                 :            :     // hairline, yet not zero. it's actually painted in qgis
    2108                 :          0 :     element.appendChild( createSvgParameterElement( doc, QStringLiteral( "stroke-width" ), QStringLiteral( "0.5" ) ) );
    2109                 :          0 :   }
    2110                 :          0 :   if ( penJoinStyle )
    2111                 :          0 :     element.appendChild( createSvgParameterElement( doc, QStringLiteral( "stroke-linejoin" ), encodeSldLineJoinStyle( *penJoinStyle ) ) );
    2112                 :          0 :   if ( penCapStyle )
    2113                 :          0 :     element.appendChild( createSvgParameterElement( doc, QStringLiteral( "stroke-linecap" ), encodeSldLineCapStyle( *penCapStyle ) ) );
    2114                 :            : 
    2115                 :          0 :   if ( !pattern->isEmpty() )
    2116                 :            :   {
    2117                 :          0 :     element.appendChild( createSvgParameterElement( doc, QStringLiteral( "stroke-dasharray" ), encodeSldRealVector( *pattern ) ) );
    2118                 :          0 :     if ( !qgsDoubleNear( dashOffset, 0.0 ) )
    2119                 :          0 :       element.appendChild( createSvgParameterElement( doc, QStringLiteral( "stroke-dashoffset" ), qgsDoubleToString( dashOffset ) ) );
    2120                 :          0 :   }
    2121                 :          0 : }
    2122                 :            : 
    2123                 :            : 
    2124                 :          0 : bool QgsSymbolLayerUtils::lineFromSld( QDomElement &element,
    2125                 :            :                                        Qt::PenStyle &penStyle, QColor &color, double &width,
    2126                 :            :                                        Qt::PenJoinStyle *penJoinStyle, Qt::PenCapStyle *penCapStyle,
    2127                 :            :                                        QVector<qreal> *customDashPattern, double *dashOffset )
    2128                 :            : {
    2129                 :          0 :   QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
    2130                 :            : 
    2131                 :          0 :   penStyle = Qt::SolidLine;
    2132                 :          0 :   color = QColor( 0, 0, 0 );
    2133                 :          0 :   width = 1;
    2134                 :          0 :   if ( penJoinStyle )
    2135                 :          0 :     *penJoinStyle = Qt::BevelJoin;
    2136                 :          0 :   if ( penCapStyle )
    2137                 :          0 :     *penCapStyle = Qt::SquareCap;
    2138                 :          0 :   if ( customDashPattern )
    2139                 :          0 :     customDashPattern->clear();
    2140                 :          0 :   if ( dashOffset )
    2141                 :          0 :     *dashOffset = 0;
    2142                 :            : 
    2143                 :          0 :   if ( element.isNull() )
    2144                 :            :   {
    2145                 :          0 :     penStyle = Qt::NoPen;
    2146                 :          0 :     color = QColor();
    2147                 :          0 :     return true;
    2148                 :            :   }
    2149                 :            : 
    2150                 :          0 :   QgsStringMap svgParams = getSvgParameterList( element );
    2151                 :          0 :   for ( QgsStringMap::iterator it = svgParams.begin(); it != svgParams.end(); ++it )
    2152                 :            :   {
    2153                 :          0 :     QgsDebugMsg( QStringLiteral( "found SvgParameter %1: %2" ).arg( it.key(), it.value() ) );
    2154                 :            : 
    2155                 :          0 :     if ( it.key() == QLatin1String( "stroke" ) )
    2156                 :            :     {
    2157                 :          0 :       color = QColor( it.value() );
    2158                 :          0 :     }
    2159                 :          0 :     else if ( it.key() == QLatin1String( "stroke-opacity" ) )
    2160                 :            :     {
    2161                 :          0 :       color.setAlpha( decodeSldAlpha( it.value() ) );
    2162                 :          0 :     }
    2163                 :          0 :     else if ( it.key() == QLatin1String( "stroke-width" ) )
    2164                 :            :     {
    2165                 :            :       bool ok;
    2166                 :          0 :       double w = it.value().toDouble( &ok );
    2167                 :          0 :       if ( ok )
    2168                 :          0 :         width = w;
    2169                 :          0 :     }
    2170                 :          0 :     else if ( it.key() == QLatin1String( "stroke-linejoin" ) && penJoinStyle )
    2171                 :            :     {
    2172                 :          0 :       *penJoinStyle = decodeSldLineJoinStyle( it.value() );
    2173                 :          0 :     }
    2174                 :          0 :     else if ( it.key() == QLatin1String( "stroke-linecap" ) && penCapStyle )
    2175                 :            :     {
    2176                 :          0 :       *penCapStyle = decodeSldLineCapStyle( it.value() );
    2177                 :          0 :     }
    2178                 :          0 :     else if ( it.key() == QLatin1String( "stroke-dasharray" ) )
    2179                 :            :     {
    2180                 :          0 :       QVector<qreal> dashPattern = decodeSldRealVector( it.value() );
    2181                 :          0 :       if ( !dashPattern.isEmpty() )
    2182                 :            :       {
    2183                 :            :         // convert the dasharray to one of the QT pen style,
    2184                 :            :         // if no match is found then set pen style to CustomDashLine
    2185                 :          0 :         bool dashPatternFound = false;
    2186                 :            : 
    2187                 :          0 :         if ( dashPattern.count() == 2 )
    2188                 :            :         {
    2189                 :          0 :           if ( dashPattern.at( 0 ) == 4.0 &&
    2190                 :          0 :                dashPattern.at( 1 ) == 2.0 )
    2191                 :            :           {
    2192                 :          0 :             penStyle = Qt::DashLine;
    2193                 :          0 :             dashPatternFound = true;
    2194                 :          0 :           }
    2195                 :          0 :           else if ( dashPattern.at( 0 ) == 1.0 &&
    2196                 :          0 :                     dashPattern.at( 1 ) == 2.0 )
    2197                 :            :           {
    2198                 :          0 :             penStyle = Qt::DotLine;
    2199                 :          0 :             dashPatternFound = true;
    2200                 :          0 :           }
    2201                 :          0 :         }
    2202                 :          0 :         else if ( dashPattern.count() == 4 )
    2203                 :            :         {
    2204                 :          0 :           if ( dashPattern.at( 0 ) == 4.0 &&
    2205                 :          0 :                dashPattern.at( 1 ) == 2.0 &&
    2206                 :          0 :                dashPattern.at( 2 ) == 1.0 &&
    2207                 :          0 :                dashPattern.at( 3 ) == 2.0 )
    2208                 :            :           {
    2209                 :          0 :             penStyle = Qt::DashDotLine;
    2210                 :          0 :             dashPatternFound = true;
    2211                 :          0 :           }
    2212                 :          0 :         }
    2213                 :          0 :         else if ( dashPattern.count() == 6 )
    2214                 :            :         {
    2215                 :          0 :           if ( dashPattern.at( 0 ) == 4.0 &&
    2216                 :          0 :                dashPattern.at( 1 ) == 2.0 &&
    2217                 :          0 :                dashPattern.at( 2 ) == 1.0 &&
    2218                 :          0 :                dashPattern.at( 3 ) == 2.0 &&
    2219                 :          0 :                dashPattern.at( 4 ) == 1.0 &&
    2220                 :          0 :                dashPattern.at( 5 ) == 2.0 )
    2221                 :            :           {
    2222                 :          0 :             penStyle = Qt::DashDotDotLine;
    2223                 :          0 :             dashPatternFound = true;
    2224                 :          0 :           }
    2225                 :          0 :         }
    2226                 :            : 
    2227                 :            :         // default case: set pen style to CustomDashLine
    2228                 :          0 :         if ( !dashPatternFound )
    2229                 :            :         {
    2230                 :          0 :           if ( customDashPattern )
    2231                 :            :           {
    2232                 :          0 :             penStyle = Qt::CustomDashLine;
    2233                 :          0 :             *customDashPattern = dashPattern;
    2234                 :          0 :           }
    2235                 :            :           else
    2236                 :            :           {
    2237                 :          0 :             QgsDebugMsg( QStringLiteral( "custom dash pattern required but not provided. Using default dash pattern." ) );
    2238                 :          0 :             penStyle = Qt::DashLine;
    2239                 :            :           }
    2240                 :          0 :         }
    2241                 :          0 :       }
    2242                 :          0 :     }
    2243                 :          0 :     else if ( it.key() == QLatin1String( "stroke-dashoffset" ) && dashOffset )
    2244                 :            :     {
    2245                 :            :       bool ok;
    2246                 :          0 :       double d = it.value().toDouble( &ok );
    2247                 :          0 :       if ( ok )
    2248                 :          0 :         *dashOffset = d;
    2249                 :          0 :     }
    2250                 :          0 :   }
    2251                 :            : 
    2252                 :          0 :   return true;
    2253                 :          0 : }
    2254                 :            : 
    2255                 :          0 : void QgsSymbolLayerUtils::externalGraphicToSld( QDomDocument &doc, QDomElement &element,
    2256                 :            :     const QString &path, const QString &mime,
    2257                 :            :     const QColor &color, double size )
    2258                 :            : {
    2259                 :          0 :   QDomElement externalGraphicElem = doc.createElement( QStringLiteral( "se:ExternalGraphic" ) );
    2260                 :          0 :   element.appendChild( externalGraphicElem );
    2261                 :            : 
    2262                 :          0 :   createOnlineResourceElement( doc, externalGraphicElem, path, mime );
    2263                 :            : 
    2264                 :            :   //TODO: missing a way to handle svg color. Should use <se:ColorReplacement>
    2265                 :          0 :   Q_UNUSED( color )
    2266                 :            : 
    2267                 :          0 :   if ( size >= 0 )
    2268                 :            :   {
    2269                 :          0 :     QDomElement sizeElem = doc.createElement( QStringLiteral( "se:Size" ) );
    2270                 :          0 :     sizeElem.appendChild( doc.createTextNode( qgsDoubleToString( size ) ) );
    2271                 :          0 :     element.appendChild( sizeElem );
    2272                 :          0 :   }
    2273                 :          0 : }
    2274                 :            : 
    2275                 :          0 : void QgsSymbolLayerUtils::parametricSvgToSld( QDomDocument &doc, QDomElement &graphicElem,
    2276                 :            :     const QString &path, const QColor &fillColor, double size, const QColor &strokeColor, double strokeWidth )
    2277                 :            : {
    2278                 :            :   // Parametric SVG paths are an extension that few systems will understand, but se:Graphic allows for fallback
    2279                 :            :   // symbols, this encodes the full parametric path first, the pure shape second, and a mark with the right colors as
    2280                 :            :   // a last resort for systems that cannot do SVG at all
    2281                 :            : 
    2282                 :            :   // encode parametric version with all coloring details (size is going to be encoded by the last fallback)
    2283                 :          0 :   graphicElem.appendChild( doc.createComment( QStringLiteral( "Parametric SVG" ) ) );
    2284                 :          0 :   QString parametricPath = getSvgParametricPath( path, fillColor, strokeColor, strokeWidth );
    2285                 :          0 :   QgsSymbolLayerUtils::externalGraphicToSld( doc, graphicElem, parametricPath, QStringLiteral( "image/svg+xml" ), fillColor, -1 );
    2286                 :            :   // also encode a fallback version without parameters, in case a renderer gets confused by the parameters
    2287                 :          0 :   graphicElem.appendChild( doc.createComment( QStringLiteral( "Plain SVG fallback, no parameters" ) ) );
    2288                 :          0 :   QgsSymbolLayerUtils::externalGraphicToSld( doc, graphicElem, path, QStringLiteral( "image/svg+xml" ), fillColor, -1 );
    2289                 :            :   // finally encode a simple mark with the right colors/outlines for renderers that cannot do SVG at all
    2290                 :          0 :   graphicElem.appendChild( doc.createComment( QStringLiteral( "Well known marker fallback" ) ) );
    2291                 :          0 :   QgsSymbolLayerUtils::wellKnownMarkerToSld( doc, graphicElem, QStringLiteral( "square" ), fillColor, strokeColor, Qt::PenStyle::SolidLine, strokeWidth, -1 );
    2292                 :            : 
    2293                 :            :   // size is encoded here, it's part of se:Graphic, not attached to the single symbol
    2294                 :          0 :   if ( size >= 0 )
    2295                 :            :   {
    2296                 :          0 :     QDomElement sizeElem = doc.createElement( QStringLiteral( "se:Size" ) );
    2297                 :          0 :     sizeElem.appendChild( doc.createTextNode( qgsDoubleToString( size ) ) );
    2298                 :          0 :     graphicElem.appendChild( sizeElem );
    2299                 :          0 :   }
    2300                 :          0 : }
    2301                 :            : 
    2302                 :            : 
    2303                 :          0 : QString QgsSymbolLayerUtils::getSvgParametricPath( const QString &basePath, const QColor &fillColor, const QColor &strokeColor, double strokeWidth )
    2304                 :            : {
    2305                 :          0 :   QUrlQuery url;
    2306                 :          0 :   if ( fillColor.isValid() )
    2307                 :            :   {
    2308                 :          0 :     url.addQueryItem( QStringLiteral( "fill" ), fillColor.name() );
    2309                 :          0 :     url.addQueryItem( QStringLiteral( "fill-opacity" ), encodeSldAlpha( fillColor.alpha() ) );
    2310                 :          0 :   }
    2311                 :            :   else
    2312                 :            :   {
    2313                 :          0 :     url.addQueryItem( QStringLiteral( "fill" ), QStringLiteral( "#000000" ) );
    2314                 :          0 :     url.addQueryItem( QStringLiteral( "fill-opacity" ), QStringLiteral( "1" ) );
    2315                 :            :   }
    2316                 :          0 :   if ( strokeColor.isValid() )
    2317                 :            :   {
    2318                 :          0 :     url.addQueryItem( QStringLiteral( "outline" ), strokeColor.name() );
    2319                 :          0 :     url.addQueryItem( QStringLiteral( "outline-opacity" ), encodeSldAlpha( strokeColor.alpha() ) );
    2320                 :          0 :   }
    2321                 :            :   else
    2322                 :            :   {
    2323                 :          0 :     url.addQueryItem( QStringLiteral( "outline" ), QStringLiteral( "#000000" ) );
    2324                 :          0 :     url.addQueryItem( QStringLiteral( "outline-opacity" ), QStringLiteral( "1" ) );
    2325                 :            :   }
    2326                 :          0 :   url.addQueryItem( QStringLiteral( "outline-width" ), QString::number( strokeWidth ) );
    2327                 :          0 :   QString params = url.toString( QUrl::FullyEncoded );
    2328                 :          0 :   if ( params.isEmpty() )
    2329                 :            :   {
    2330                 :          0 :     return basePath;
    2331                 :            :   }
    2332                 :            :   else
    2333                 :            :   {
    2334                 :          0 :     return basePath + "?" + params;
    2335                 :            :   }
    2336                 :          0 : }
    2337                 :            : 
    2338                 :          0 : bool QgsSymbolLayerUtils::externalGraphicFromSld( QDomElement &element,
    2339                 :            :     QString &path, QString &mime,
    2340                 :            :     QColor &color, double &size )
    2341                 :            : {
    2342                 :          0 :   QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
    2343                 :          0 :   Q_UNUSED( color )
    2344                 :            : 
    2345                 :          0 :   QDomElement externalGraphicElem = element.firstChildElement( QStringLiteral( "ExternalGraphic" ) );
    2346                 :          0 :   if ( externalGraphicElem.isNull() )
    2347                 :          0 :     return false;
    2348                 :            : 
    2349                 :          0 :   onlineResourceFromSldElement( externalGraphicElem, path, mime );
    2350                 :            : 
    2351                 :          0 :   QDomElement sizeElem = element.firstChildElement( QStringLiteral( "Size" ) );
    2352                 :          0 :   if ( !sizeElem.isNull() )
    2353                 :            :   {
    2354                 :            :     bool ok;
    2355                 :          0 :     double s = sizeElem.firstChild().nodeValue().toDouble( &ok );
    2356                 :          0 :     if ( ok )
    2357                 :          0 :       size = s;
    2358                 :          0 :   }
    2359                 :            : 
    2360                 :          0 :   return true;
    2361                 :          0 : }
    2362                 :            : 
    2363                 :          0 : void QgsSymbolLayerUtils::externalMarkerToSld( QDomDocument &doc, QDomElement &element,
    2364                 :            :     const QString &path, const QString &format, int *markIndex,
    2365                 :            :     const QColor &color, double size )
    2366                 :            : {
    2367                 :          0 :   QDomElement markElem = doc.createElement( QStringLiteral( "se:Mark" ) );
    2368                 :          0 :   element.appendChild( markElem );
    2369                 :            : 
    2370                 :          0 :   createOnlineResourceElement( doc, markElem, path, format );
    2371                 :            : 
    2372                 :          0 :   if ( markIndex )
    2373                 :            :   {
    2374                 :          0 :     QDomElement markIndexElem = doc.createElement( QStringLiteral( "se:MarkIndex" ) );
    2375                 :          0 :     markIndexElem.appendChild( doc.createTextNode( QString::number( *markIndex ) ) );
    2376                 :          0 :     markElem.appendChild( markIndexElem );
    2377                 :          0 :   }
    2378                 :            : 
    2379                 :            :   // <Fill>
    2380                 :          0 :   QDomElement fillElem = doc.createElement( QStringLiteral( "se:Fill" ) );
    2381                 :          0 :   fillToSld( doc, fillElem, Qt::SolidPattern, color );
    2382                 :          0 :   markElem.appendChild( fillElem );
    2383                 :            : 
    2384                 :            :   // <Size>
    2385                 :          0 :   if ( !qgsDoubleNear( size, 0.0 ) && size > 0 )
    2386                 :            :   {
    2387                 :          0 :     QDomElement sizeElem = doc.createElement( QStringLiteral( "se:Size" ) );
    2388                 :          0 :     sizeElem.appendChild( doc.createTextNode( qgsDoubleToString( size ) ) );
    2389                 :          0 :     element.appendChild( sizeElem );
    2390                 :          0 :   }
    2391                 :          0 : }
    2392                 :            : 
    2393                 :          0 : bool QgsSymbolLayerUtils::externalMarkerFromSld( QDomElement &element,
    2394                 :            :     QString &path, QString &format, int &markIndex,
    2395                 :            :     QColor &color, double &size )
    2396                 :            : {
    2397                 :          0 :   QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
    2398                 :            : 
    2399                 :          0 :   color = QColor();
    2400                 :          0 :   markIndex = -1;
    2401                 :          0 :   size = -1;
    2402                 :            : 
    2403                 :          0 :   QDomElement markElem = element.firstChildElement( QStringLiteral( "Mark" ) );
    2404                 :          0 :   if ( markElem.isNull() )
    2405                 :          0 :     return false;
    2406                 :            : 
    2407                 :          0 :   onlineResourceFromSldElement( markElem, path, format );
    2408                 :            : 
    2409                 :          0 :   QDomElement markIndexElem = markElem.firstChildElement( QStringLiteral( "MarkIndex" ) );
    2410                 :          0 :   if ( !markIndexElem.isNull() )
    2411                 :            :   {
    2412                 :            :     bool ok;
    2413                 :          0 :     int i = markIndexElem.firstChild().nodeValue().toInt( &ok );
    2414                 :          0 :     if ( ok )
    2415                 :          0 :       markIndex = i;
    2416                 :          0 :   }
    2417                 :            : 
    2418                 :            :   // <Fill>
    2419                 :          0 :   QDomElement fillElem = markElem.firstChildElement( QStringLiteral( "Fill" ) );
    2420                 :          0 :   Qt::BrushStyle b = Qt::SolidPattern;
    2421                 :          0 :   fillFromSld( fillElem, b, color );
    2422                 :            :   // ignore brush style, solid expected
    2423                 :            : 
    2424                 :            :   // <Size>
    2425                 :          0 :   QDomElement sizeElem = element.firstChildElement( QStringLiteral( "Size" ) );
    2426                 :          0 :   if ( !sizeElem.isNull() )
    2427                 :            :   {
    2428                 :            :     bool ok;
    2429                 :          0 :     double s = sizeElem.firstChild().nodeValue().toDouble( &ok );
    2430                 :          0 :     if ( ok )
    2431                 :          0 :       size = s;
    2432                 :          0 :   }
    2433                 :            : 
    2434                 :          0 :   return true;
    2435                 :          0 : }
    2436                 :            : 
    2437                 :          0 : void QgsSymbolLayerUtils::wellKnownMarkerToSld( QDomDocument &doc, QDomElement &element,
    2438                 :            :     const QString &name, const QColor &color, const QColor &strokeColor, Qt::PenStyle strokeStyle,
    2439                 :            :     double strokeWidth, double size )
    2440                 :            : {
    2441                 :          0 :   QDomElement markElem = doc.createElement( QStringLiteral( "se:Mark" ) );
    2442                 :          0 :   element.appendChild( markElem );
    2443                 :            : 
    2444                 :          0 :   QDomElement wellKnownNameElem = doc.createElement( QStringLiteral( "se:WellKnownName" ) );
    2445                 :          0 :   wellKnownNameElem.appendChild( doc.createTextNode( name ) );
    2446                 :          0 :   markElem.appendChild( wellKnownNameElem );
    2447                 :            : 
    2448                 :            :   // <Fill>
    2449                 :          0 :   if ( color.isValid() )
    2450                 :            :   {
    2451                 :          0 :     QDomElement fillElem = doc.createElement( QStringLiteral( "se:Fill" ) );
    2452                 :          0 :     fillToSld( doc, fillElem, Qt::SolidPattern, color );
    2453                 :          0 :     markElem.appendChild( fillElem );
    2454                 :          0 :   }
    2455                 :            : 
    2456                 :            :   // <Stroke>
    2457                 :          0 :   if ( strokeColor.isValid() )
    2458                 :            :   {
    2459                 :          0 :     QDomElement strokeElem = doc.createElement( QStringLiteral( "se:Stroke" ) );
    2460                 :          0 :     lineToSld( doc, strokeElem, strokeStyle, strokeColor, strokeWidth );
    2461                 :          0 :     markElem.appendChild( strokeElem );
    2462                 :          0 :   }
    2463                 :            : 
    2464                 :            :   // <Size>
    2465                 :          0 :   if ( !qgsDoubleNear( size, 0.0 ) && size > 0 )
    2466                 :            :   {
    2467                 :          0 :     QDomElement sizeElem = doc.createElement( QStringLiteral( "se:Size" ) );
    2468                 :          0 :     sizeElem.appendChild( doc.createTextNode( qgsDoubleToString( size ) ) );
    2469                 :          0 :     element.appendChild( sizeElem );
    2470                 :          0 :   }
    2471                 :          0 : }
    2472                 :            : 
    2473                 :          0 : bool QgsSymbolLayerUtils::wellKnownMarkerFromSld( QDomElement &element,
    2474                 :            :     QString &name, QColor &color, QColor &strokeColor, Qt::PenStyle &strokeStyle,
    2475                 :            :     double &strokeWidth, double &size )
    2476                 :            : {
    2477                 :          0 :   QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
    2478                 :            : 
    2479                 :          0 :   name = QStringLiteral( "square" );
    2480                 :          0 :   color = QColor();
    2481                 :          0 :   strokeColor = QColor( 0, 0, 0 );
    2482                 :          0 :   strokeWidth = 1;
    2483                 :          0 :   size = 6;
    2484                 :            : 
    2485                 :          0 :   QDomElement markElem = element.firstChildElement( QStringLiteral( "Mark" ) );
    2486                 :          0 :   if ( markElem.isNull() )
    2487                 :          0 :     return false;
    2488                 :            : 
    2489                 :          0 :   QDomElement wellKnownNameElem = markElem.firstChildElement( QStringLiteral( "WellKnownName" ) );
    2490                 :          0 :   if ( !wellKnownNameElem.isNull() )
    2491                 :            :   {
    2492                 :          0 :     name = wellKnownNameElem.firstChild().nodeValue();
    2493                 :          0 :     QgsDebugMsg( "found Mark with well known name: " + name );
    2494                 :          0 :   }
    2495                 :            : 
    2496                 :            :   // <Fill>
    2497                 :          0 :   QDomElement fillElem = markElem.firstChildElement( QStringLiteral( "Fill" ) );
    2498                 :          0 :   Qt::BrushStyle b = Qt::SolidPattern;
    2499                 :          0 :   fillFromSld( fillElem, b, color );
    2500                 :            :   // ignore brush style, solid expected
    2501                 :            : 
    2502                 :            :   // <Stroke>
    2503                 :          0 :   QDomElement strokeElem = markElem.firstChildElement( QStringLiteral( "Stroke" ) );
    2504                 :          0 :   lineFromSld( strokeElem, strokeStyle, strokeColor, strokeWidth );
    2505                 :            :   // ignore stroke style, solid expected
    2506                 :            : 
    2507                 :            :   // <Size>
    2508                 :          0 :   QDomElement sizeElem = element.firstChildElement( QStringLiteral( "Size" ) );
    2509                 :          0 :   if ( !sizeElem.isNull() )
    2510                 :            :   {
    2511                 :            :     bool ok;
    2512                 :          0 :     double s = sizeElem.firstChild().nodeValue().toDouble( &ok );
    2513                 :          0 :     if ( ok )
    2514                 :          0 :       size = s;
    2515                 :          0 :   }
    2516                 :            : 
    2517                 :          0 :   return true;
    2518                 :          0 : }
    2519                 :            : 
    2520                 :          0 : void QgsSymbolLayerUtils::createRotationElement( QDomDocument &doc, QDomElement &element, const QString &rotationFunc )
    2521                 :            : {
    2522                 :          0 :   if ( !rotationFunc.isEmpty() )
    2523                 :            :   {
    2524                 :          0 :     QDomElement rotationElem = doc.createElement( QStringLiteral( "se:Rotation" ) );
    2525                 :          0 :     createExpressionElement( doc, rotationElem, rotationFunc );
    2526                 :          0 :     element.appendChild( rotationElem );
    2527                 :          0 :   }
    2528                 :          0 : }
    2529                 :            : 
    2530                 :          0 : bool QgsSymbolLayerUtils::rotationFromSldElement( QDomElement &element, QString &rotationFunc )
    2531                 :            : {
    2532                 :          0 :   QDomElement rotationElem = element.firstChildElement( QStringLiteral( "Rotation" ) );
    2533                 :          0 :   if ( !rotationElem.isNull() )
    2534                 :            :   {
    2535                 :          0 :     return functionFromSldElement( rotationElem, rotationFunc );
    2536                 :            :   }
    2537                 :          0 :   return true;
    2538                 :          0 : }
    2539                 :            : 
    2540                 :            : 
    2541                 :          0 : void QgsSymbolLayerUtils::createOpacityElement( QDomDocument &doc, QDomElement &element, const QString &alphaFunc )
    2542                 :            : {
    2543                 :          0 :   if ( !alphaFunc.isEmpty() )
    2544                 :            :   {
    2545                 :          0 :     QDomElement opacityElem = doc.createElement( QStringLiteral( "se:Opacity" ) );
    2546                 :          0 :     createExpressionElement( doc, opacityElem, alphaFunc );
    2547                 :          0 :     element.appendChild( opacityElem );
    2548                 :          0 :   }
    2549                 :          0 : }
    2550                 :            : 
    2551                 :          0 : bool QgsSymbolLayerUtils::opacityFromSldElement( QDomElement &element, QString &alphaFunc )
    2552                 :            : {
    2553                 :          0 :   QDomElement opacityElem = element.firstChildElement( QStringLiteral( "Opacity" ) );
    2554                 :          0 :   if ( !opacityElem.isNull() )
    2555                 :            :   {
    2556                 :          0 :     return functionFromSldElement( opacityElem, alphaFunc );
    2557                 :            :   }
    2558                 :          0 :   return true;
    2559                 :          0 : }
    2560                 :            : 
    2561                 :          0 : void QgsSymbolLayerUtils::createDisplacementElement( QDomDocument &doc, QDomElement &element, QPointF offset )
    2562                 :            : {
    2563                 :          0 :   if ( offset.isNull() )
    2564                 :          0 :     return;
    2565                 :            : 
    2566                 :          0 :   QDomElement displacementElem = doc.createElement( QStringLiteral( "se:Displacement" ) );
    2567                 :          0 :   element.appendChild( displacementElem );
    2568                 :            : 
    2569                 :          0 :   QDomElement dispXElem = doc.createElement( QStringLiteral( "se:DisplacementX" ) );
    2570                 :          0 :   dispXElem.appendChild( doc.createTextNode( qgsDoubleToString( offset.x(), 2 ) ) );
    2571                 :            : 
    2572                 :          0 :   QDomElement dispYElem = doc.createElement( QStringLiteral( "se:DisplacementY" ) );
    2573                 :          0 :   dispYElem.appendChild( doc.createTextNode( qgsDoubleToString( offset.y(), 2 ) ) );
    2574                 :            : 
    2575                 :          0 :   displacementElem.appendChild( dispXElem );
    2576                 :          0 :   displacementElem.appendChild( dispYElem );
    2577                 :          0 : }
    2578                 :            : 
    2579                 :          0 : void QgsSymbolLayerUtils::createAnchorPointElement( QDomDocument &doc, QDomElement &element, QPointF anchor )
    2580                 :            : {
    2581                 :            :   // anchor is not tested for null, (0,0) is _not_ the default value (0.5, 0) is.
    2582                 :            : 
    2583                 :          0 :   QDomElement anchorElem = doc.createElement( QStringLiteral( "se:AnchorPoint" ) );
    2584                 :          0 :   element.appendChild( anchorElem );
    2585                 :            : 
    2586                 :          0 :   QDomElement anchorXElem = doc.createElement( QStringLiteral( "se:AnchorPointX" ) );
    2587                 :          0 :   anchorXElem.appendChild( doc.createTextNode( qgsDoubleToString( anchor.x() ) ) );
    2588                 :            : 
    2589                 :          0 :   QDomElement anchorYElem = doc.createElement( QStringLiteral( "se:AnchorPointY" ) );
    2590                 :          0 :   anchorYElem.appendChild( doc.createTextNode( qgsDoubleToString( anchor.y() ) ) );
    2591                 :            : 
    2592                 :          0 :   anchorElem.appendChild( anchorXElem );
    2593                 :          0 :   anchorElem.appendChild( anchorYElem );
    2594                 :          0 : }
    2595                 :            : 
    2596                 :          0 : bool QgsSymbolLayerUtils::displacementFromSldElement( QDomElement &element, QPointF &offset )
    2597                 :            : {
    2598                 :          0 :   offset = QPointF( 0, 0 );
    2599                 :            : 
    2600                 :          0 :   QDomElement displacementElem = element.firstChildElement( QStringLiteral( "Displacement" ) );
    2601                 :          0 :   if ( displacementElem.isNull() )
    2602                 :          0 :     return true;
    2603                 :            : 
    2604                 :          0 :   QDomElement dispXElem = displacementElem.firstChildElement( QStringLiteral( "DisplacementX" ) );
    2605                 :          0 :   if ( !dispXElem.isNull() )
    2606                 :            :   {
    2607                 :            :     bool ok;
    2608                 :          0 :     double offsetX = dispXElem.firstChild().nodeValue().toDouble( &ok );
    2609                 :          0 :     if ( ok )
    2610                 :          0 :       offset.setX( offsetX );
    2611                 :          0 :   }
    2612                 :            : 
    2613                 :          0 :   QDomElement dispYElem = displacementElem.firstChildElement( QStringLiteral( "DisplacementY" ) );
    2614                 :          0 :   if ( !dispYElem.isNull() )
    2615                 :            :   {
    2616                 :            :     bool ok;
    2617                 :          0 :     double offsetY = dispYElem.firstChild().nodeValue().toDouble( &ok );
    2618                 :          0 :     if ( ok )
    2619                 :          0 :       offset.setY( offsetY );
    2620                 :          0 :   }
    2621                 :            : 
    2622                 :          0 :   return true;
    2623                 :          0 : }
    2624                 :            : 
    2625                 :          0 : void QgsSymbolLayerUtils::labelTextToSld( QDomDocument &doc, QDomElement &element,
    2626                 :            :     const QString &label, const QFont &font,
    2627                 :            :     const QColor &color, double size )
    2628                 :            : {
    2629                 :          0 :   QDomElement labelElem = doc.createElement( QStringLiteral( "se:Label" ) );
    2630                 :          0 :   labelElem.appendChild( doc.createTextNode( label ) );
    2631                 :          0 :   element.appendChild( labelElem );
    2632                 :            : 
    2633                 :          0 :   QDomElement fontElem = doc.createElement( QStringLiteral( "se:Font" ) );
    2634                 :          0 :   element.appendChild( fontElem );
    2635                 :            : 
    2636                 :          0 :   fontElem.appendChild( createSvgParameterElement( doc, QStringLiteral( "font-family" ), font.family() ) );
    2637                 :            : #if 0
    2638                 :            :   fontElem.appendChild( createSldParameterElement( doc, "font-style", encodeSldFontStyle( font.style() ) ) );
    2639                 :            :   fontElem.appendChild( createSldParameterElement( doc, "font-weight", encodeSldFontWeight( font.weight() ) ) );
    2640                 :            : #endif
    2641                 :          0 :   fontElem.appendChild( createSvgParameterElement( doc, QStringLiteral( "font-size" ), QString::number( size ) ) );
    2642                 :            : 
    2643                 :            :   // <Fill>
    2644                 :          0 :   if ( color.isValid() )
    2645                 :            :   {
    2646                 :          0 :     QDomElement fillElem = doc.createElement( QStringLiteral( "Fill" ) );
    2647                 :          0 :     fillToSld( doc, fillElem, Qt::SolidPattern, color );
    2648                 :          0 :     element.appendChild( fillElem );
    2649                 :          0 :   }
    2650                 :          0 : }
    2651                 :            : 
    2652                 :          0 : QString QgsSymbolLayerUtils::ogrFeatureStylePen( double width, double mmScaleFactor, double mapUnitScaleFactor, const QColor &c,
    2653                 :            :     Qt::PenJoinStyle joinStyle,
    2654                 :            :     Qt::PenCapStyle capStyle,
    2655                 :            :     double offset,
    2656                 :            :     const QVector<qreal> *dashPattern )
    2657                 :            : {
    2658                 :          0 :   QString penStyle;
    2659                 :          0 :   penStyle.append( "PEN(" );
    2660                 :          0 :   penStyle.append( "c:" );
    2661                 :          0 :   penStyle.append( c.name() );
    2662                 :          0 :   penStyle.append( ",w:" );
    2663                 :            :   //dxf driver writes ground units as mm? Should probably be changed in ogr
    2664                 :          0 :   penStyle.append( QString::number( width * mmScaleFactor ) );
    2665                 :          0 :   penStyle.append( "mm" );
    2666                 :            : 
    2667                 :            :   //dash dot vector
    2668                 :          0 :   if ( dashPattern && !dashPattern->isEmpty() )
    2669                 :            :   {
    2670                 :          0 :     penStyle.append( ",p:\"" );
    2671                 :          0 :     QVector<qreal>::const_iterator pIt = dashPattern->constBegin();
    2672                 :          0 :     for ( ; pIt != dashPattern->constEnd(); ++pIt )
    2673                 :            :     {
    2674                 :          0 :       if ( pIt != dashPattern->constBegin() )
    2675                 :            :       {
    2676                 :          0 :         penStyle.append( ' ' );
    2677                 :          0 :       }
    2678                 :          0 :       penStyle.append( QString::number( *pIt * mapUnitScaleFactor ) );
    2679                 :          0 :       penStyle.append( 'g' );
    2680                 :          0 :     }
    2681                 :          0 :     penStyle.append( '\"' );
    2682                 :          0 :   }
    2683                 :            : 
    2684                 :            :   //cap
    2685                 :          0 :   penStyle.append( ",cap:" );
    2686                 :          0 :   switch ( capStyle )
    2687                 :            :   {
    2688                 :            :     case Qt::SquareCap:
    2689                 :          0 :       penStyle.append( 'p' );
    2690                 :          0 :       break;
    2691                 :            :     case Qt::RoundCap:
    2692                 :          0 :       penStyle.append( 'r' );
    2693                 :          0 :       break;
    2694                 :            :     case Qt::FlatCap:
    2695                 :            :     default:
    2696                 :          0 :       penStyle.append( 'b' );
    2697                 :          0 :   }
    2698                 :            : 
    2699                 :            :   //join
    2700                 :          0 :   penStyle.append( ",j:" );
    2701                 :          0 :   switch ( joinStyle )
    2702                 :            :   {
    2703                 :            :     case Qt::BevelJoin:
    2704                 :          0 :       penStyle.append( 'b' );
    2705                 :          0 :       break;
    2706                 :            :     case Qt::RoundJoin:
    2707                 :          0 :       penStyle.append( 'r' );
    2708                 :          0 :       break;
    2709                 :            :     case Qt::MiterJoin:
    2710                 :            :     default:
    2711                 :          0 :       penStyle.append( 'm' );
    2712                 :          0 :   }
    2713                 :            : 
    2714                 :            :   //offset
    2715                 :          0 :   if ( !qgsDoubleNear( offset, 0.0 ) )
    2716                 :            :   {
    2717                 :          0 :     penStyle.append( ",dp:" );
    2718                 :          0 :     penStyle.append( QString::number( offset * mapUnitScaleFactor ) );
    2719                 :          0 :     penStyle.append( 'g' );
    2720                 :          0 :   }
    2721                 :            : 
    2722                 :          0 :   penStyle.append( ')' );
    2723                 :          0 :   return penStyle;
    2724                 :          0 : }
    2725                 :            : 
    2726                 :          0 : QString QgsSymbolLayerUtils::ogrFeatureStyleBrush( const QColor &fillColor )
    2727                 :            : {
    2728                 :          0 :   QString brushStyle;
    2729                 :          0 :   brushStyle.append( "BRUSH(" );
    2730                 :          0 :   brushStyle.append( "fc:" );
    2731                 :          0 :   brushStyle.append( fillColor.name() );
    2732                 :          0 :   brushStyle.append( ')' );
    2733                 :          0 :   return brushStyle;
    2734                 :          0 : }
    2735                 :            : 
    2736                 :          0 : void QgsSymbolLayerUtils::createGeometryElement( QDomDocument &doc, QDomElement &element, const QString &geomFunc )
    2737                 :            : {
    2738                 :          0 :   if ( geomFunc.isEmpty() )
    2739                 :          0 :     return;
    2740                 :            : 
    2741                 :          0 :   QDomElement geometryElem = doc.createElement( QStringLiteral( "Geometry" ) );
    2742                 :          0 :   element.appendChild( geometryElem );
    2743                 :            : 
    2744                 :            :   /* About using a function within the Geometry tag.
    2745                 :            :    *
    2746                 :            :    * The SLD specification <= 1.1 is vague:
    2747                 :            :    * "In principle, a fixed geometry could be defined using GML or
    2748                 :            :    * operators could be defined for computing the geometry from
    2749                 :            :    * references or literals. However, using a feature property directly
    2750                 :            :    * is by far the most commonly useful method."
    2751                 :            :    *
    2752                 :            :    * Even if it seems that specs should take care all the possible cases,
    2753                 :            :    * looking at the XML schema fragment that encodes the Geometry element,
    2754                 :            :    * it has to be a PropertyName element:
    2755                 :            :    *   <xsd:element name="Geometry">
    2756                 :            :    *       <xsd:complexType>
    2757                 :            :    *           <xsd:sequence>
    2758                 :            :    *               <xsd:element ref="ogc:PropertyName"/>
    2759                 :            :    *           </xsd:sequence>
    2760                 :            :    *       </xsd:complexType>
    2761                 :            :    *   </xsd:element>
    2762                 :            :    *
    2763                 :            :    * Anyway we will use a ogc:Function to handle geometry transformations
    2764                 :            :    * like offset, centroid, ...
    2765                 :            :    */
    2766                 :            : 
    2767                 :          0 :   createExpressionElement( doc, geometryElem, geomFunc );
    2768                 :          0 : }
    2769                 :            : 
    2770                 :          0 : bool QgsSymbolLayerUtils::geometryFromSldElement( QDomElement &element, QString &geomFunc )
    2771                 :            : {
    2772                 :          0 :   QDomElement geometryElem = element.firstChildElement( QStringLiteral( "Geometry" ) );
    2773                 :          0 :   if ( geometryElem.isNull() )
    2774                 :          0 :     return true;
    2775                 :            : 
    2776                 :          0 :   return functionFromSldElement( geometryElem, geomFunc );
    2777                 :          0 : }
    2778                 :            : 
    2779                 :          0 : bool QgsSymbolLayerUtils::createExpressionElement( QDomDocument &doc, QDomElement &element, const QString &function )
    2780                 :            : {
    2781                 :            :   // let's use QgsExpression to generate the SLD for the function
    2782                 :          0 :   QgsExpression expr( function );
    2783                 :          0 :   if ( expr.hasParserError() )
    2784                 :            :   {
    2785                 :          0 :     element.appendChild( doc.createComment( "Parser Error: " + expr.parserErrorString() + " - Expression was: " + function ) );
    2786                 :          0 :     return false;
    2787                 :            :   }
    2788                 :          0 :   QDomElement filterElem = QgsOgcUtils::expressionToOgcExpression( expr, doc );
    2789                 :          0 :   if ( !filterElem.isNull() )
    2790                 :          0 :     element.appendChild( filterElem );
    2791                 :          0 :   return true;
    2792                 :          0 : }
    2793                 :            : 
    2794                 :            : 
    2795                 :          0 : bool QgsSymbolLayerUtils::createFunctionElement( QDomDocument &doc, QDomElement &element, const QString &function )
    2796                 :            : {
    2797                 :            :   // let's use QgsExpression to generate the SLD for the function
    2798                 :          0 :   QgsExpression expr( function );
    2799                 :          0 :   if ( expr.hasParserError() )
    2800                 :            :   {
    2801                 :          0 :     element.appendChild( doc.createComment( "Parser Error: " + expr.parserErrorString() + " - Expression was: " + function ) );
    2802                 :          0 :     return false;
    2803                 :            :   }
    2804                 :          0 :   QDomElement filterElem = QgsOgcUtils::expressionToOgcFilter( expr, doc );
    2805                 :          0 :   if ( !filterElem.isNull() )
    2806                 :          0 :     element.appendChild( filterElem );
    2807                 :          0 :   return true;
    2808                 :          0 : }
    2809                 :            : 
    2810                 :          0 : bool QgsSymbolLayerUtils::functionFromSldElement( QDomElement &element, QString &function )
    2811                 :            : {
    2812                 :            :   // check if ogc:Filter or contains ogc:Filters
    2813                 :          0 :   QDomElement elem = element;
    2814                 :          0 :   if ( element.tagName() != QLatin1String( "Filter" ) )
    2815                 :            :   {
    2816                 :          0 :     QDomNodeList filterNodes = element.elementsByTagName( QStringLiteral( "Filter" ) );
    2817                 :          0 :     if ( !filterNodes.isEmpty() )
    2818                 :            :     {
    2819                 :          0 :       elem = filterNodes.at( 0 ).toElement();
    2820                 :          0 :     }
    2821                 :          0 :   }
    2822                 :            : 
    2823                 :          0 :   if ( elem.isNull() )
    2824                 :            :   {
    2825                 :          0 :     return false;
    2826                 :            :   }
    2827                 :            : 
    2828                 :            :   // parse ogc:Filter
    2829                 :          0 :   QgsExpression *expr = QgsOgcUtils::expressionFromOgcFilter( elem );
    2830                 :          0 :   if ( !expr )
    2831                 :          0 :     return false;
    2832                 :            : 
    2833                 :          0 :   bool valid = !expr->hasParserError();
    2834                 :          0 :   if ( !valid )
    2835                 :            :   {
    2836                 :          0 :     QgsDebugMsg( "parser error: " + expr->parserErrorString() );
    2837                 :          0 :   }
    2838                 :            :   else
    2839                 :            :   {
    2840                 :          0 :     function = expr->expression();
    2841                 :            :   }
    2842                 :            : 
    2843                 :          0 :   delete expr;
    2844                 :          0 :   return valid;
    2845                 :          0 : }
    2846                 :            : 
    2847                 :          0 : void QgsSymbolLayerUtils::createOnlineResourceElement( QDomDocument &doc, QDomElement &element,
    2848                 :            :     const QString &path, const QString &format )
    2849                 :            : {
    2850                 :            :   // get resource url or relative path
    2851                 :          0 :   QString url = svgSymbolPathToName( path, QgsPathResolver() );
    2852                 :          0 :   QDomElement onlineResourceElem = doc.createElement( QStringLiteral( "se:OnlineResource" ) );
    2853                 :          0 :   onlineResourceElem.setAttribute( QStringLiteral( "xlink:type" ), QStringLiteral( "simple" ) );
    2854                 :          0 :   onlineResourceElem.setAttribute( QStringLiteral( "xlink:href" ), url );
    2855                 :          0 :   element.appendChild( onlineResourceElem );
    2856                 :            : 
    2857                 :          0 :   QDomElement formatElem = doc.createElement( QStringLiteral( "se:Format" ) );
    2858                 :          0 :   formatElem.appendChild( doc.createTextNode( format ) );
    2859                 :          0 :   element.appendChild( formatElem );
    2860                 :          0 : }
    2861                 :            : 
    2862                 :          0 : bool QgsSymbolLayerUtils::onlineResourceFromSldElement( QDomElement &element, QString &path, QString &format )
    2863                 :            : {
    2864                 :          0 :   QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
    2865                 :            : 
    2866                 :          0 :   QDomElement onlineResourceElem = element.firstChildElement( QStringLiteral( "OnlineResource" ) );
    2867                 :          0 :   if ( onlineResourceElem.isNull() )
    2868                 :          0 :     return false;
    2869                 :            : 
    2870                 :          0 :   path = onlineResourceElem.attributeNS( QStringLiteral( "http://www.w3.org/1999/xlink" ), QStringLiteral( "href" ) );
    2871                 :            : 
    2872                 :          0 :   QDomElement formatElem = element.firstChildElement( QStringLiteral( "Format" ) );
    2873                 :          0 :   if ( formatElem.isNull() )
    2874                 :          0 :     return false; // OnlineResource requires a Format sibling element
    2875                 :            : 
    2876                 :          0 :   format = formatElem.firstChild().nodeValue();
    2877                 :          0 :   return true;
    2878                 :          0 : }
    2879                 :            : 
    2880                 :            : 
    2881                 :          0 : QDomElement QgsSymbolLayerUtils::createSvgParameterElement( QDomDocument &doc, const QString &name, const QString &value )
    2882                 :            : {
    2883                 :          0 :   QDomElement nodeElem = doc.createElement( QStringLiteral( "se:SvgParameter" ) );
    2884                 :          0 :   nodeElem.setAttribute( QStringLiteral( "name" ), name );
    2885                 :          0 :   nodeElem.appendChild( doc.createTextNode( value ) );
    2886                 :          0 :   return nodeElem;
    2887                 :          0 : }
    2888                 :            : 
    2889                 :          0 : QgsStringMap QgsSymbolLayerUtils::getSvgParameterList( QDomElement &element )
    2890                 :            : {
    2891                 :          0 :   QgsStringMap params;
    2892                 :          0 :   QString value;
    2893                 :            : 
    2894                 :          0 :   QDomElement paramElem = element.firstChildElement();
    2895                 :          0 :   while ( !paramElem.isNull() )
    2896                 :            :   {
    2897                 :          0 :     if ( paramElem.localName() == QLatin1String( "SvgParameter" ) || paramElem.localName() == QLatin1String( "CssParameter" ) )
    2898                 :            :     {
    2899                 :          0 :       QString name = paramElem.attribute( QStringLiteral( "name" ) );
    2900                 :          0 :       if ( paramElem.firstChild().nodeType() == QDomNode::TextNode )
    2901                 :            :       {
    2902                 :          0 :         value = paramElem.firstChild().nodeValue();
    2903                 :          0 :       }
    2904                 :            :       else
    2905                 :            :       {
    2906                 :          0 :         if ( paramElem.firstChild().nodeType() == QDomNode::ElementNode &&
    2907                 :          0 :              paramElem.firstChild().localName() == QLatin1String( "Literal" ) )
    2908                 :            :         {
    2909                 :          0 :           QgsDebugMsg( paramElem.firstChild().localName() );
    2910                 :          0 :           value = paramElem.firstChild().firstChild().nodeValue();
    2911                 :          0 :         }
    2912                 :            :         else
    2913                 :            :         {
    2914                 :          0 :           QgsDebugMsg( QStringLiteral( "unexpected child of %1" ).arg( paramElem.localName() ) );
    2915                 :            :         }
    2916                 :            :       }
    2917                 :            : 
    2918                 :          0 :       if ( !name.isEmpty() && !value.isEmpty() )
    2919                 :          0 :         params[ name ] = value;
    2920                 :          0 :     }
    2921                 :            : 
    2922                 :          0 :     paramElem = paramElem.nextSiblingElement();
    2923                 :            :   }
    2924                 :            : 
    2925                 :          0 :   return params;
    2926                 :          0 : }
    2927                 :            : 
    2928                 :          0 : QDomElement QgsSymbolLayerUtils::createVendorOptionElement( QDomDocument &doc, const QString &name, const QString &value )
    2929                 :            : {
    2930                 :          0 :   QDomElement nodeElem = doc.createElement( QStringLiteral( "se:VendorOption" ) );
    2931                 :          0 :   nodeElem.setAttribute( QStringLiteral( "name" ), name );
    2932                 :          0 :   nodeElem.appendChild( doc.createTextNode( value ) );
    2933                 :          0 :   return nodeElem;
    2934                 :          0 : }
    2935                 :            : 
    2936                 :          0 : QgsStringMap QgsSymbolLayerUtils::getVendorOptionList( QDomElement &element )
    2937                 :            : {
    2938                 :          0 :   QgsStringMap params;
    2939                 :            : 
    2940                 :          0 :   QDomElement paramElem = element.firstChildElement( QStringLiteral( "VendorOption" ) );
    2941                 :          0 :   while ( !paramElem.isNull() )
    2942                 :            :   {
    2943                 :          0 :     QString name = paramElem.attribute( QStringLiteral( "name" ) );
    2944                 :          0 :     QString value = paramElem.firstChild().nodeValue();
    2945                 :            : 
    2946                 :          0 :     if ( !name.isEmpty() && !value.isEmpty() )
    2947                 :          0 :       params[ name ] = value;
    2948                 :            : 
    2949                 :          0 :     paramElem = paramElem.nextSiblingElement( QStringLiteral( "VendorOption" ) );
    2950                 :          0 :   }
    2951                 :            : 
    2952                 :          0 :   return params;
    2953                 :          0 : }
    2954                 :            : 
    2955                 :            : 
    2956                 :       1340 : QVariantMap QgsSymbolLayerUtils::parseProperties( const QDomElement &element )
    2957                 :            : {
    2958                 :       2680 :   QVariant newSymbols = QgsXmlUtils::readVariant( element.firstChildElement( QStringLiteral( "Option" ) ) );
    2959                 :       1340 :   if ( newSymbols.type() == QVariant::Map )
    2960                 :            :   {
    2961                 :        804 :     return newSymbols.toMap();
    2962                 :            :   }
    2963                 :            :   else
    2964                 :            :   {
    2965                 :            :     // read old style of writing properties
    2966                 :            :     // backward compatibility with project saved in <= 3.16
    2967                 :        536 :     QVariantMap props;
    2968                 :        536 :     QDomElement e = element.firstChildElement();
    2969                 :       9136 :     while ( !e.isNull() )
    2970                 :            :     {
    2971                 :       8600 :       if ( e.tagName() == QLatin1String( "prop" ) )
    2972                 :            :       {
    2973                 :      16248 :         QString propKey = e.attribute( QStringLiteral( "k" ) );
    2974                 :      16248 :         QString propValue = e.attribute( QStringLiteral( "v" ) );
    2975                 :       8124 :         props[propKey] = propValue;
    2976                 :       8124 :       }
    2977                 :       8600 :       e = e.nextSiblingElement();
    2978                 :            :     }
    2979                 :        536 :     return props;
    2980                 :        536 :   }
    2981                 :       1340 : }
    2982                 :            : 
    2983                 :            : 
    2984                 :          0 : void QgsSymbolLayerUtils::saveProperties( QVariantMap props, QDomDocument &doc, QDomElement &element )
    2985                 :            : {
    2986                 :          0 :   element.appendChild( QgsXmlUtils::writeVariant( props, doc ) );
    2987                 :            : 
    2988                 :            :   // -----
    2989                 :            :   // let's do this to try to keep some backward compatibility
    2990                 :            :   // to open a project saved on 3.18+ in QGIS <= 3.16
    2991                 :            :   // TODO QGIS 4: remove
    2992                 :          0 :   for ( QVariantMap::iterator it = props.begin(); it != props.end(); ++it )
    2993                 :            :   {
    2994                 :          0 :     QDomElement propEl = doc.createElement( QStringLiteral( "prop" ) );
    2995                 :          0 :     propEl.setAttribute( QStringLiteral( "k" ), it.key() );
    2996                 :          0 :     propEl.setAttribute( QStringLiteral( "v" ), it.value().toString() );
    2997                 :          0 :     element.appendChild( propEl );
    2998                 :          0 :   }
    2999                 :            :   // -----
    3000                 :          0 : }
    3001                 :            : 
    3002                 :          0 : QgsSymbolMap QgsSymbolLayerUtils::loadSymbols( QDomElement &element, const QgsReadWriteContext &context )
    3003                 :            : {
    3004                 :            :   // go through symbols one-by-one and load them
    3005                 :            : 
    3006                 :          0 :   QgsSymbolMap symbols;
    3007                 :          0 :   QDomElement e = element.firstChildElement();
    3008                 :            : 
    3009                 :          0 :   while ( !e.isNull() )
    3010                 :            :   {
    3011                 :          0 :     if ( e.tagName() == QLatin1String( "symbol" ) )
    3012                 :            :     {
    3013                 :          0 :       QgsSymbol *symbol = QgsSymbolLayerUtils::loadSymbol( e, context );
    3014                 :          0 :       if ( symbol )
    3015                 :          0 :         symbols.insert( e.attribute( QStringLiteral( "name" ) ), symbol );
    3016                 :          0 :     }
    3017                 :            :     else
    3018                 :            :     {
    3019                 :          0 :       QgsDebugMsg( "unknown tag: " + e.tagName() );
    3020                 :            :     }
    3021                 :          0 :     e = e.nextSiblingElement();
    3022                 :            :   }
    3023                 :            : 
    3024                 :            : 
    3025                 :            :   // now walk through the list of symbols and find those prefixed with @
    3026                 :            :   // these symbols are sub-symbols of some other symbol layers
    3027                 :            :   // e.g. symbol named "@foo@1" is sub-symbol of layer 1 in symbol "foo"
    3028                 :          0 :   QStringList subsymbols;
    3029                 :            : 
    3030                 :          0 :   for ( QMap<QString, QgsSymbol *>::iterator it = symbols.begin(); it != symbols.end(); ++it )
    3031                 :            :   {
    3032                 :          0 :     if ( it.key()[0] != '@' )
    3033                 :          0 :       continue;
    3034                 :            : 
    3035                 :            :     // add to array (for deletion)
    3036                 :          0 :     subsymbols.append( it.key() );
    3037                 :            : 
    3038                 :          0 :     QStringList parts = it.key().split( '@' );
    3039                 :          0 :     if ( parts.count() < 3 )
    3040                 :            :     {
    3041                 :          0 :       QgsDebugMsg( "found subsymbol with invalid name: " + it.key() );
    3042                 :          0 :       delete it.value(); // we must delete it
    3043                 :          0 :       continue; // some invalid syntax
    3044                 :            :     }
    3045                 :          0 :     QString symname = parts[1];
    3046                 :          0 :     int symlayer = parts[2].toInt();
    3047                 :            : 
    3048                 :          0 :     if ( !symbols.contains( symname ) )
    3049                 :            :     {
    3050                 :          0 :       QgsDebugMsg( "subsymbol references invalid symbol: " + symname );
    3051                 :          0 :       delete it.value(); // we must delete it
    3052                 :          0 :       continue;
    3053                 :            :     }
    3054                 :            : 
    3055                 :          0 :     QgsSymbol *sym = symbols[symname];
    3056                 :          0 :     if ( symlayer < 0 || symlayer >= sym->symbolLayerCount() )
    3057                 :            :     {
    3058                 :          0 :       QgsDebugMsg( "subsymbol references invalid symbol layer: " + QString::number( symlayer ) );
    3059                 :          0 :       delete it.value(); // we must delete it
    3060                 :          0 :       continue;
    3061                 :            :     }
    3062                 :            : 
    3063                 :            :     // set subsymbol takes ownership
    3064                 :          0 :     bool res = sym->symbolLayer( symlayer )->setSubSymbol( it.value() );
    3065                 :          0 :     if ( !res )
    3066                 :            :     {
    3067                 :          0 :       QgsDebugMsg( "symbol layer refused subsymbol: " + it.key() );
    3068                 :          0 :     }
    3069                 :            : 
    3070                 :            : 
    3071                 :          0 :   }
    3072                 :            : 
    3073                 :            :   // now safely remove sub-symbol entries (they have been already deleted or the ownership was taken away)
    3074                 :          0 :   for ( int i = 0; i < subsymbols.count(); i++ )
    3075                 :          0 :     symbols.take( subsymbols[i] );
    3076                 :            : 
    3077                 :          0 :   return symbols;
    3078                 :          0 : }
    3079                 :            : 
    3080                 :          0 : QDomElement QgsSymbolLayerUtils::saveSymbols( QgsSymbolMap &symbols, const QString &tagName, QDomDocument &doc, const QgsReadWriteContext &context )
    3081                 :            : {
    3082                 :          0 :   QDomElement symbolsElem = doc.createElement( tagName );
    3083                 :            : 
    3084                 :            :   // save symbols
    3085                 :          0 :   for ( QMap<QString, QgsSymbol *>::iterator its = symbols.begin(); its != symbols.end(); ++its )
    3086                 :            :   {
    3087                 :          0 :     QDomElement symEl = saveSymbol( its.key(), its.value(), doc, context );
    3088                 :          0 :     symbolsElem.appendChild( symEl );
    3089                 :          0 :   }
    3090                 :            : 
    3091                 :          0 :   return symbolsElem;
    3092                 :          0 : }
    3093                 :            : 
    3094                 :          0 : void QgsSymbolLayerUtils::clearSymbolMap( QgsSymbolMap &symbols )
    3095                 :            : {
    3096                 :          0 :   qDeleteAll( symbols );
    3097                 :          0 :   symbols.clear();
    3098                 :          0 : }
    3099                 :            : 
    3100                 :          0 : QMimeData *QgsSymbolLayerUtils::symbolToMimeData( const QgsSymbol *symbol )
    3101                 :            : {
    3102                 :          0 :   if ( !symbol )
    3103                 :          0 :     return nullptr;
    3104                 :            : 
    3105                 :          0 :   std::unique_ptr< QMimeData >mimeData( new QMimeData );
    3106                 :            : 
    3107                 :          0 :   QDomDocument symbolDoc;
    3108                 :          0 :   QDomElement symbolElem = saveSymbol( QStringLiteral( "symbol" ), symbol, symbolDoc, QgsReadWriteContext() );
    3109                 :          0 :   symbolDoc.appendChild( symbolElem );
    3110                 :          0 :   mimeData->setText( symbolDoc.toString() );
    3111                 :            : 
    3112                 :          0 :   mimeData->setImageData( symbolPreviewPixmap( symbol, QSize( 100, 100 ), 18 ).toImage() );
    3113                 :          0 :   mimeData->setColorData( symbol->color() );
    3114                 :            : 
    3115                 :          0 :   return mimeData.release();
    3116                 :          0 : }
    3117                 :            : 
    3118                 :          0 : QgsSymbol *QgsSymbolLayerUtils::symbolFromMimeData( const QMimeData *data )
    3119                 :            : {
    3120                 :          0 :   if ( !data )
    3121                 :          0 :     return nullptr;
    3122                 :            : 
    3123                 :          0 :   QString text = data->text();
    3124                 :          0 :   if ( !text.isEmpty() )
    3125                 :            :   {
    3126                 :          0 :     QDomDocument doc;
    3127                 :          0 :     QDomElement elem;
    3128                 :            : 
    3129                 :          0 :     if ( doc.setContent( text ) )
    3130                 :            :     {
    3131                 :          0 :       elem = doc.documentElement();
    3132                 :            : 
    3133                 :          0 :       if ( elem.nodeName() != QLatin1String( "symbol" ) )
    3134                 :          0 :         elem = elem.firstChildElement( QStringLiteral( "symbol" ) );
    3135                 :            : 
    3136                 :          0 :       return loadSymbol( elem, QgsReadWriteContext() );
    3137                 :            :     }
    3138                 :          0 :   }
    3139                 :          0 :   return nullptr;
    3140                 :          0 : }
    3141                 :            : 
    3142                 :            : 
    3143                 :        155 : QgsColorRamp *QgsSymbolLayerUtils::loadColorRamp( QDomElement &element )
    3144                 :            : {
    3145                 :        310 :   QString rampType = element.attribute( QStringLiteral( "type" ) );
    3146                 :            : 
    3147                 :            :   // parse properties
    3148                 :        155 :   QVariantMap props = QgsSymbolLayerUtils::parseProperties( element );
    3149                 :            : 
    3150                 :        155 :   if ( rampType == QgsGradientColorRamp::typeString() )
    3151                 :        155 :     return QgsGradientColorRamp::create( props );
    3152                 :          0 :   else if ( rampType == QgsLimitedRandomColorRamp::typeString() )
    3153                 :          0 :     return QgsLimitedRandomColorRamp::create( props );
    3154                 :          0 :   else if ( rampType == QgsColorBrewerColorRamp::typeString() )
    3155                 :          0 :     return QgsColorBrewerColorRamp::create( props );
    3156                 :          0 :   else if ( rampType == QgsCptCityColorRamp::typeString() )
    3157                 :          0 :     return QgsCptCityColorRamp::create( props );
    3158                 :          0 :   else if ( rampType == QgsPresetSchemeColorRamp::typeString() )
    3159                 :          0 :     return QgsPresetSchemeColorRamp::create( props );
    3160                 :            :   else
    3161                 :            :   {
    3162                 :          0 :     QgsDebugMsg( "unknown colorramp type " + rampType );
    3163                 :          0 :     return nullptr;
    3164                 :            :   }
    3165                 :        155 : }
    3166                 :            : 
    3167                 :            : 
    3168                 :          0 : QDomElement QgsSymbolLayerUtils::saveColorRamp( const QString &name, QgsColorRamp *ramp, QDomDocument &doc )
    3169                 :            : {
    3170                 :          0 :   QDomElement rampEl = doc.createElement( QStringLiteral( "colorramp" ) );
    3171                 :          0 :   rampEl.setAttribute( QStringLiteral( "type" ), ramp->type() );
    3172                 :          0 :   rampEl.setAttribute( QStringLiteral( "name" ), name );
    3173                 :            : 
    3174                 :          0 :   QgsSymbolLayerUtils::saveProperties( ramp->properties(), doc, rampEl );
    3175                 :          0 :   return rampEl;
    3176                 :          0 : }
    3177                 :            : 
    3178                 :          0 : QVariant QgsSymbolLayerUtils::colorRampToVariant( const QString &name, QgsColorRamp *ramp )
    3179                 :            : {
    3180                 :          0 :   QVariantMap rampMap;
    3181                 :            : 
    3182                 :          0 :   rampMap.insert( QStringLiteral( "type" ), ramp->type() );
    3183                 :          0 :   rampMap.insert( QStringLiteral( "name" ), name );
    3184                 :            : 
    3185                 :          0 :   QVariantMap properties = ramp->properties();
    3186                 :            : 
    3187                 :          0 :   QVariantMap propertyMap;
    3188                 :          0 :   for ( auto property = properties.constBegin(); property != properties.constEnd(); ++property )
    3189                 :            :   {
    3190                 :          0 :     propertyMap.insert( property.key(), property.value() );
    3191                 :          0 :   }
    3192                 :            : 
    3193                 :          0 :   rampMap.insert( QStringLiteral( "properties" ), propertyMap );
    3194                 :          0 :   return rampMap;
    3195                 :          0 : }
    3196                 :            : 
    3197                 :          0 : QgsColorRamp *QgsSymbolLayerUtils::loadColorRamp( const QVariant &value )
    3198                 :            : {
    3199                 :          0 :   QVariantMap rampMap = value.toMap();
    3200                 :            : 
    3201                 :          0 :   QString rampType = rampMap.value( QStringLiteral( "type" ) ).toString();
    3202                 :            : 
    3203                 :            :   // parse properties
    3204                 :          0 :   QVariantMap propertyMap = rampMap.value( QStringLiteral( "properties" ) ).toMap();
    3205                 :          0 :   QVariantMap props;
    3206                 :            : 
    3207                 :          0 :   for ( auto property = propertyMap.constBegin(); property != propertyMap.constEnd(); ++property )
    3208                 :            :   {
    3209                 :          0 :     props.insert( property.key(), property.value().toString() );
    3210                 :          0 :   }
    3211                 :            : 
    3212                 :          0 :   if ( rampType == QgsGradientColorRamp::typeString() )
    3213                 :          0 :     return QgsGradientColorRamp::create( props );
    3214                 :          0 :   else if ( rampType == QgsLimitedRandomColorRamp::typeString() )
    3215                 :          0 :     return QgsLimitedRandomColorRamp::create( props );
    3216                 :          0 :   else if ( rampType == QgsColorBrewerColorRamp::typeString() )
    3217                 :          0 :     return QgsColorBrewerColorRamp::create( props );
    3218                 :          0 :   else if ( rampType == QgsCptCityColorRamp::typeString() )
    3219                 :          0 :     return QgsCptCityColorRamp::create( props );
    3220                 :          0 :   else if ( rampType == QgsPresetSchemeColorRamp::typeString() )
    3221                 :          0 :     return QgsPresetSchemeColorRamp::create( props );
    3222                 :            :   else
    3223                 :            :   {
    3224                 :          0 :     QgsDebugMsg( "unknown colorramp type " + rampType );
    3225                 :          0 :     return nullptr;
    3226                 :            :   }
    3227                 :          0 : }
    3228                 :            : 
    3229                 :          0 : QString QgsSymbolLayerUtils::colorToName( const QColor &color )
    3230                 :            : {
    3231                 :          0 :   if ( !color.isValid() )
    3232                 :            :   {
    3233                 :          0 :     return QString();
    3234                 :            :   }
    3235                 :            : 
    3236                 :            :   //TODO - utilize a color names database (such as X11) to return nicer names
    3237                 :            :   //for now, just return hex codes
    3238                 :          0 :   return color.name();
    3239                 :          0 : }
    3240                 :            : 
    3241                 :          0 : QList<QColor> QgsSymbolLayerUtils::parseColorList( const QString &colorStr )
    3242                 :            : {
    3243                 :          0 :   QList<QColor> colors;
    3244                 :            : 
    3245                 :            :   //try splitting string at commas, spaces or newlines
    3246                 :          0 :   QStringList components = colorStr.simplified().split( QRegExp( "(,|\\s)" ) );
    3247                 :          0 :   QStringList::iterator it = components.begin();
    3248                 :          0 :   for ( ; it != components.end(); ++it )
    3249                 :            :   {
    3250                 :          0 :     QColor result = parseColor( *it, true );
    3251                 :          0 :     if ( result.isValid() )
    3252                 :            :     {
    3253                 :          0 :       colors << result;
    3254                 :          0 :     }
    3255                 :          0 :   }
    3256                 :          0 :   if ( colors.length() > 0 )
    3257                 :            :   {
    3258                 :          0 :     return colors;
    3259                 :            :   }
    3260                 :            : 
    3261                 :            :   //try splitting string at commas or newlines
    3262                 :          0 :   components = colorStr.split( QRegExp( "(,|\n)" ) );
    3263                 :          0 :   it = components.begin();
    3264                 :          0 :   for ( ; it != components.end(); ++it )
    3265                 :            :   {
    3266                 :          0 :     QColor result = parseColor( *it, true );
    3267                 :          0 :     if ( result.isValid() )
    3268                 :            :     {
    3269                 :          0 :       colors << result;
    3270                 :          0 :     }
    3271                 :          0 :   }
    3272                 :          0 :   if ( colors.length() > 0 )
    3273                 :            :   {
    3274                 :          0 :     return colors;
    3275                 :            :   }
    3276                 :            : 
    3277                 :            :   //try splitting string at whitespace or newlines
    3278                 :          0 :   components = colorStr.simplified().split( QString( ' ' ) );
    3279                 :          0 :   it = components.begin();
    3280                 :          0 :   for ( ; it != components.end(); ++it )
    3281                 :            :   {
    3282                 :          0 :     QColor result = parseColor( *it, true );
    3283                 :          0 :     if ( result.isValid() )
    3284                 :            :     {
    3285                 :          0 :       colors << result;
    3286                 :          0 :     }
    3287                 :          0 :   }
    3288                 :          0 :   if ( colors.length() > 0 )
    3289                 :            :   {
    3290                 :          0 :     return colors;
    3291                 :            :   }
    3292                 :            : 
    3293                 :            :   //try splitting string just at newlines
    3294                 :          0 :   components = colorStr.split( '\n' );
    3295                 :          0 :   it = components.begin();
    3296                 :          0 :   for ( ; it != components.end(); ++it )
    3297                 :            :   {
    3298                 :          0 :     QColor result = parseColor( *it, true );
    3299                 :          0 :     if ( result.isValid() )
    3300                 :            :     {
    3301                 :          0 :       colors << result;
    3302                 :          0 :     }
    3303                 :          0 :   }
    3304                 :            : 
    3305                 :          0 :   return colors;
    3306                 :          0 : }
    3307                 :            : 
    3308                 :          0 : QMimeData *QgsSymbolLayerUtils::colorToMimeData( const QColor &color )
    3309                 :            : {
    3310                 :            :   //set both the mime color data (which includes alpha channel), and the text (which is the color's hex
    3311                 :            :   //value, and can be used when pasting colors outside of QGIS).
    3312                 :          0 :   QMimeData *mimeData = new QMimeData;
    3313                 :          0 :   mimeData->setColorData( QVariant( color ) );
    3314                 :          0 :   mimeData->setText( color.name() );
    3315                 :          0 :   return mimeData;
    3316                 :          0 : }
    3317                 :            : 
    3318                 :          0 : QColor QgsSymbolLayerUtils::colorFromMimeData( const QMimeData *mimeData, bool &hasAlpha )
    3319                 :            : {
    3320                 :            :   //attempt to read color data directly from mime
    3321                 :          0 :   if ( mimeData->hasColor() )
    3322                 :            :   {
    3323                 :          0 :     QColor mimeColor = mimeData->colorData().value<QColor>();
    3324                 :          0 :     if ( mimeColor.isValid() )
    3325                 :            :     {
    3326                 :          0 :       hasAlpha = true;
    3327                 :          0 :       return mimeColor;
    3328                 :            :     }
    3329                 :          0 :   }
    3330                 :            : 
    3331                 :            :   //attempt to intrepret a color from mime text data
    3332                 :          0 :   if ( mimeData->hasText() )
    3333                 :            :   {
    3334                 :          0 :     hasAlpha = false;
    3335                 :          0 :     QColor textColor = QgsSymbolLayerUtils::parseColorWithAlpha( mimeData->text(), hasAlpha );
    3336                 :          0 :     if ( textColor.isValid() )
    3337                 :            :     {
    3338                 :          0 :       return textColor;
    3339                 :            :     }
    3340                 :          0 :   }
    3341                 :            : 
    3342                 :            :   //could not get color from mime data
    3343                 :          0 :   return QColor();
    3344                 :          0 : }
    3345                 :            : 
    3346                 :          0 : QgsNamedColorList QgsSymbolLayerUtils::colorListFromMimeData( const QMimeData *data )
    3347                 :            : {
    3348                 :          0 :   QgsNamedColorList mimeColors;
    3349                 :            : 
    3350                 :            :   //prefer xml format
    3351                 :          0 :   if ( data->hasFormat( QStringLiteral( "text/xml" ) ) )
    3352                 :            :   {
    3353                 :            :     //get XML doc
    3354                 :          0 :     QByteArray encodedData = data->data( QStringLiteral( "text/xml" ) );
    3355                 :          0 :     QDomDocument xmlDoc;
    3356                 :          0 :     xmlDoc.setContent( encodedData );
    3357                 :            : 
    3358                 :          0 :     QDomElement dragDataElem = xmlDoc.documentElement();
    3359                 :          0 :     if ( dragDataElem.tagName() == QLatin1String( "ColorSchemeModelDragData" ) )
    3360                 :            :     {
    3361                 :          0 :       QDomNodeList nodeList = dragDataElem.childNodes();
    3362                 :          0 :       int nChildNodes = nodeList.size();
    3363                 :          0 :       QDomElement currentElem;
    3364                 :            : 
    3365                 :          0 :       for ( int i = 0; i < nChildNodes; ++i )
    3366                 :            :       {
    3367                 :          0 :         currentElem = nodeList.at( i ).toElement();
    3368                 :          0 :         if ( currentElem.isNull() )
    3369                 :            :         {
    3370                 :          0 :           continue;
    3371                 :            :         }
    3372                 :            : 
    3373                 :          0 :         QPair< QColor, QString> namedColor;
    3374                 :          0 :         namedColor.first = QgsSymbolLayerUtils::decodeColor( currentElem.attribute( QStringLiteral( "color" ), QStringLiteral( "255,255,255,255" ) ) );
    3375                 :          0 :         namedColor.second = currentElem.attribute( QStringLiteral( "label" ), QString() );
    3376                 :            : 
    3377                 :          0 :         mimeColors << namedColor;
    3378                 :          0 :       }
    3379                 :          0 :     }
    3380                 :          0 :   }
    3381                 :            : 
    3382                 :          0 :   if ( mimeColors.length() == 0 && data->hasFormat( QStringLiteral( "application/x-colorobject-list" ) ) )
    3383                 :            :   {
    3384                 :            :     //get XML doc
    3385                 :          0 :     QByteArray encodedData = data->data( QStringLiteral( "application/x-colorobject-list" ) );
    3386                 :          0 :     QDomDocument xmlDoc;
    3387                 :          0 :     xmlDoc.setContent( encodedData );
    3388                 :            : 
    3389                 :          0 :     QDomNodeList colorsNodes = xmlDoc.elementsByTagName( QStringLiteral( "colors" ) );
    3390                 :          0 :     if ( colorsNodes.length() > 0 )
    3391                 :            :     {
    3392                 :          0 :       QDomElement colorsElem = colorsNodes.at( 0 ).toElement();
    3393                 :          0 :       QDomNodeList colorNodeList = colorsElem.childNodes();
    3394                 :          0 :       int nChildNodes = colorNodeList.size();
    3395                 :          0 :       QDomElement currentElem;
    3396                 :            : 
    3397                 :          0 :       for ( int i = 0; i < nChildNodes; ++i )
    3398                 :            :       {
    3399                 :            :         //li element
    3400                 :          0 :         currentElem = colorNodeList.at( i ).toElement();
    3401                 :          0 :         if ( currentElem.isNull() )
    3402                 :            :         {
    3403                 :          0 :           continue;
    3404                 :            :         }
    3405                 :            : 
    3406                 :          0 :         QDomNodeList colorNodes = currentElem.elementsByTagName( QStringLiteral( "color" ) );
    3407                 :          0 :         QDomNodeList nameNodes = currentElem.elementsByTagName( QStringLiteral( "name" ) );
    3408                 :            : 
    3409                 :          0 :         if ( colorNodes.length() > 0 )
    3410                 :            :         {
    3411                 :          0 :           QDomElement colorElem = colorNodes.at( 0 ).toElement();
    3412                 :            : 
    3413                 :          0 :           QStringList colorParts = colorElem.text().simplified().split( ' ' );
    3414                 :          0 :           if ( colorParts.length() < 3 )
    3415                 :            :           {
    3416                 :          0 :             continue;
    3417                 :            :           }
    3418                 :            : 
    3419                 :          0 :           int red = colorParts.at( 0 ).toDouble() * 255;
    3420                 :          0 :           int green = colorParts.at( 1 ).toDouble() * 255;
    3421                 :          0 :           int blue = colorParts.at( 2 ).toDouble() * 255;
    3422                 :          0 :           QPair< QColor, QString> namedColor;
    3423                 :          0 :           namedColor.first = QColor( red, green, blue );
    3424                 :          0 :           if ( nameNodes.length() > 0 )
    3425                 :            :           {
    3426                 :          0 :             QDomElement nameElem = nameNodes.at( 0 ).toElement();
    3427                 :          0 :             namedColor.second = nameElem.text();
    3428                 :          0 :           }
    3429                 :          0 :           mimeColors << namedColor;
    3430                 :          0 :         }
    3431                 :          0 :       }
    3432                 :          0 :     }
    3433                 :          0 :   }
    3434                 :            : 
    3435                 :          0 :   if ( mimeColors.length() == 0 && data->hasText() )
    3436                 :            :   {
    3437                 :            :     //attempt to read color data from mime text
    3438                 :          0 :     QList< QColor > parsedColors = QgsSymbolLayerUtils::parseColorList( data->text() );
    3439                 :          0 :     QList< QColor >::iterator it = parsedColors.begin();
    3440                 :          0 :     for ( ; it != parsedColors.end(); ++it )
    3441                 :            :     {
    3442                 :          0 :       mimeColors << qMakePair( *it, QString() );
    3443                 :          0 :     }
    3444                 :          0 :   }
    3445                 :            : 
    3446                 :          0 :   if ( mimeColors.length() == 0 && data->hasColor() )
    3447                 :            :   {
    3448                 :            :     //attempt to read color data directly from mime
    3449                 :          0 :     QColor mimeColor = data->colorData().value<QColor>();
    3450                 :          0 :     if ( mimeColor.isValid() )
    3451                 :            :     {
    3452                 :          0 :       mimeColors << qMakePair( mimeColor, QString() );
    3453                 :          0 :     }
    3454                 :          0 :   }
    3455                 :            : 
    3456                 :          0 :   return mimeColors;
    3457                 :          0 : }
    3458                 :            : 
    3459                 :          0 : QMimeData *QgsSymbolLayerUtils::colorListToMimeData( const QgsNamedColorList &colorList, const bool allFormats )
    3460                 :            : {
    3461                 :            :   //native format
    3462                 :          0 :   QMimeData *mimeData = new QMimeData();
    3463                 :          0 :   QDomDocument xmlDoc;
    3464                 :          0 :   QDomElement xmlRootElement = xmlDoc.createElement( QStringLiteral( "ColorSchemeModelDragData" ) );
    3465                 :          0 :   xmlDoc.appendChild( xmlRootElement );
    3466                 :            : 
    3467                 :          0 :   QgsNamedColorList::const_iterator colorIt = colorList.constBegin();
    3468                 :          0 :   for ( ; colorIt != colorList.constEnd(); ++colorIt )
    3469                 :            :   {
    3470                 :          0 :     QDomElement namedColor = xmlDoc.createElement( QStringLiteral( "NamedColor" ) );
    3471                 :          0 :     namedColor.setAttribute( QStringLiteral( "color" ), QgsSymbolLayerUtils::encodeColor( ( *colorIt ).first ) );
    3472                 :          0 :     namedColor.setAttribute( QStringLiteral( "label" ), ( *colorIt ).second );
    3473                 :          0 :     xmlRootElement.appendChild( namedColor );
    3474                 :          0 :   }
    3475                 :          0 :   mimeData->setData( QStringLiteral( "text/xml" ), xmlDoc.toByteArray() );
    3476                 :            : 
    3477                 :          0 :   if ( !allFormats )
    3478                 :            :   {
    3479                 :          0 :     return mimeData;
    3480                 :            :   }
    3481                 :            : 
    3482                 :            :   //set mime text to list of hex values
    3483                 :          0 :   colorIt = colorList.constBegin();
    3484                 :          0 :   QStringList colorListString;
    3485                 :          0 :   for ( ; colorIt != colorList.constEnd(); ++colorIt )
    3486                 :            :   {
    3487                 :          0 :     colorListString << ( *colorIt ).first.name();
    3488                 :          0 :   }
    3489                 :          0 :   mimeData->setText( colorListString.join( QLatin1Char( '\n' ) ) );
    3490                 :            : 
    3491                 :            :   //set mime color data to first color
    3492                 :          0 :   if ( colorList.length() > 0 )
    3493                 :            :   {
    3494                 :          0 :     mimeData->setColorData( QVariant( colorList.at( 0 ).first ) );
    3495                 :          0 :   }
    3496                 :            : 
    3497                 :          0 :   return mimeData;
    3498                 :          0 : }
    3499                 :            : 
    3500                 :          0 : bool QgsSymbolLayerUtils::saveColorsToGpl( QFile &file, const QString &paletteName, const QgsNamedColorList &colors )
    3501                 :            : {
    3502                 :          0 :   if ( !file.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
    3503                 :            :   {
    3504                 :          0 :     return false;
    3505                 :            :   }
    3506                 :            : 
    3507                 :          0 :   QTextStream stream( &file );
    3508                 :            : #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
    3509                 :            :   stream << "GIMP Palette" << endl;
    3510                 :            : #else
    3511                 :          0 :   stream << "GIMP Palette" << Qt::endl;
    3512                 :            : #endif
    3513                 :          0 :   if ( paletteName.isEmpty() )
    3514                 :            :   {
    3515                 :            : #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
    3516                 :            :     stream << "Name: QGIS Palette" << endl;
    3517                 :            : #else
    3518                 :          0 :     stream << "Name: QGIS Palette" << Qt::endl;
    3519                 :            : #endif
    3520                 :          0 :   }
    3521                 :            :   else
    3522                 :            :   {
    3523                 :            : #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
    3524                 :            :     stream << "Name: " << paletteName << endl;
    3525                 :            : #else
    3526                 :          0 :     stream << "Name: " << paletteName << Qt::endl;
    3527                 :            : #endif
    3528                 :            :   }
    3529                 :            : #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
    3530                 :            :   stream << "Columns: 4" << endl;
    3531                 :            :   stream << '#' << endl;
    3532                 :            : #else
    3533                 :          0 :   stream << "Columns: 4" << Qt::endl;
    3534                 :          0 :   stream << '#' << Qt::endl;
    3535                 :            : #endif
    3536                 :            : 
    3537                 :          0 :   for ( QgsNamedColorList::ConstIterator colorIt = colors.constBegin(); colorIt != colors.constEnd(); ++colorIt )
    3538                 :            :   {
    3539                 :          0 :     QColor color = ( *colorIt ).first;
    3540                 :          0 :     if ( !color.isValid() )
    3541                 :            :     {
    3542                 :          0 :       continue;
    3543                 :            :     }
    3544                 :          0 :     stream << QStringLiteral( "%1 %2 %3" ).arg( color.red(), 3 ).arg( color.green(), 3 ).arg( color.blue(), 3 );
    3545                 :            : #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
    3546                 :            :     stream << "\t" << ( ( *colorIt ).second.isEmpty() ? color.name() : ( *colorIt ).second ) << endl;
    3547                 :            : #else
    3548                 :          0 :     stream << "\t" << ( ( *colorIt ).second.isEmpty() ? color.name() : ( *colorIt ).second ) << Qt::endl;
    3549                 :            : #endif
    3550                 :          0 :   }
    3551                 :          0 :   file.close();
    3552                 :            : 
    3553                 :          0 :   return true;
    3554                 :          0 : }
    3555                 :            : 
    3556                 :          0 : QgsNamedColorList QgsSymbolLayerUtils::importColorsFromGpl( QFile &file, bool &ok, QString &name )
    3557                 :            : {
    3558                 :          0 :   QgsNamedColorList importedColors;
    3559                 :            : 
    3560                 :          0 :   if ( !file.open( QIODevice::ReadOnly ) )
    3561                 :            :   {
    3562                 :          0 :     ok = false;
    3563                 :          0 :     return importedColors;
    3564                 :            :   }
    3565                 :            : 
    3566                 :          0 :   QTextStream in( &file );
    3567                 :            : 
    3568                 :          0 :   QString line = in.readLine();
    3569                 :          0 :   if ( !line.startsWith( QLatin1String( "GIMP Palette" ) ) )
    3570                 :            :   {
    3571                 :          0 :     ok = false;
    3572                 :          0 :     return importedColors;
    3573                 :            :   }
    3574                 :            : 
    3575                 :            :   //find name line
    3576                 :          0 :   while ( !in.atEnd() && !line.startsWith( QLatin1String( "Name:" ) ) && !line.startsWith( '#' ) )
    3577                 :            :   {
    3578                 :          0 :     line = in.readLine();
    3579                 :            :   }
    3580                 :          0 :   if ( line.startsWith( QLatin1String( "Name:" ) ) )
    3581                 :            :   {
    3582                 :          0 :     QRegExp nameRx( "Name:\\s*(\\S.*)$" );
    3583                 :          0 :     if ( nameRx.indexIn( line ) != -1 )
    3584                 :            :     {
    3585                 :          0 :       name = nameRx.cap( 1 );
    3586                 :          0 :     }
    3587                 :          0 :   }
    3588                 :            : 
    3589                 :            :   //ignore lines until after "#"
    3590                 :          0 :   while ( !in.atEnd() && !line.startsWith( '#' ) )
    3591                 :            :   {
    3592                 :          0 :     line = in.readLine();
    3593                 :            :   }
    3594                 :          0 :   if ( in.atEnd() )
    3595                 :            :   {
    3596                 :          0 :     ok = false;
    3597                 :          0 :     return importedColors;
    3598                 :            :   }
    3599                 :            : 
    3600                 :            :   //ready to start reading colors
    3601                 :          0 :   QRegExp rx( "^\\s*(\\d+)\\s+(\\d+)\\s+(\\d+)(\\s.*)?$" );
    3602                 :          0 :   while ( !in.atEnd() )
    3603                 :            :   {
    3604                 :          0 :     line = in.readLine();
    3605                 :          0 :     if ( rx.indexIn( line ) == -1 )
    3606                 :            :     {
    3607                 :          0 :       continue;
    3608                 :            :     }
    3609                 :          0 :     int red = rx.cap( 1 ).toInt();
    3610                 :          0 :     int green = rx.cap( 2 ).toInt();
    3611                 :          0 :     int blue = rx.cap( 3 ).toInt();
    3612                 :          0 :     QColor color = QColor( red, green, blue );
    3613                 :          0 :     if ( !color.isValid() )
    3614                 :            :     {
    3615                 :          0 :       continue;
    3616                 :            :     }
    3617                 :            : 
    3618                 :            :     //try to read color name
    3619                 :          0 :     QString label;
    3620                 :          0 :     if ( rx.captureCount() > 3 )
    3621                 :            :     {
    3622                 :          0 :       label = rx.cap( 4 ).simplified();
    3623                 :          0 :     }
    3624                 :            :     else
    3625                 :            :     {
    3626                 :          0 :       label = colorToName( color );
    3627                 :            :     }
    3628                 :            : 
    3629                 :          0 :     importedColors << qMakePair( color, label );
    3630                 :          0 :   }
    3631                 :            : 
    3632                 :          0 :   file.close();
    3633                 :          0 :   ok = true;
    3634                 :          0 :   return importedColors;
    3635                 :          0 : }
    3636                 :            : 
    3637                 :          0 : QColor QgsSymbolLayerUtils::parseColor( const QString &colorStr, bool strictEval )
    3638                 :            : {
    3639                 :            :   bool hasAlpha;
    3640                 :          0 :   return parseColorWithAlpha( colorStr, hasAlpha, strictEval );
    3641                 :            : }
    3642                 :            : 
    3643                 :          0 : QColor QgsSymbolLayerUtils::parseColorWithAlpha( const QString &colorStr, bool &containsAlpha, bool strictEval )
    3644                 :            : {
    3645                 :          0 :   QColor parsedColor;
    3646                 :            : 
    3647                 :          0 :   QRegExp hexColorAlphaRx( "^\\s*#?([0-9a-fA-F]{6})([0-9a-fA-F]{2})\\s*$" );
    3648                 :          0 :   int hexColorIndex = hexColorAlphaRx.indexIn( colorStr );
    3649                 :            : 
    3650                 :            :   //color in hex format "#aabbcc", but not #aabbccdd
    3651                 :          0 :   if ( hexColorIndex == -1 && QColor::isValidColor( colorStr ) )
    3652                 :            :   {
    3653                 :            :     //string is a valid hex color string
    3654                 :          0 :     parsedColor.setNamedColor( colorStr );
    3655                 :          0 :     if ( parsedColor.isValid() )
    3656                 :            :     {
    3657                 :          0 :       containsAlpha = false;
    3658                 :          0 :       return parsedColor;
    3659                 :            :     }
    3660                 :          0 :   }
    3661                 :            : 
    3662                 :            :   //color in hex format, with alpha
    3663                 :          0 :   if ( hexColorIndex > -1 )
    3664                 :            :   {
    3665                 :          0 :     QString hexColor = hexColorAlphaRx.cap( 1 );
    3666                 :          0 :     parsedColor.setNamedColor( QStringLiteral( "#" ) + hexColor );
    3667                 :            :     bool alphaOk;
    3668                 :          0 :     int alphaHex = hexColorAlphaRx.cap( 2 ).toInt( &alphaOk, 16 );
    3669                 :            : 
    3670                 :          0 :     if ( parsedColor.isValid() && alphaOk )
    3671                 :            :     {
    3672                 :          0 :       parsedColor.setAlpha( alphaHex );
    3673                 :          0 :       containsAlpha = true;
    3674                 :          0 :       return parsedColor;
    3675                 :            :     }
    3676                 :          0 :   }
    3677                 :            : 
    3678                 :          0 :   if ( !strictEval )
    3679                 :            :   {
    3680                 :            :     //color in hex format, without #
    3681                 :          0 :     QRegExp hexColorRx2( "^\\s*(?:[0-9a-fA-F]{3}){1,2}\\s*$" );
    3682                 :          0 :     if ( hexColorRx2.indexIn( colorStr ) != -1 )
    3683                 :            :     {
    3684                 :            :       //add "#" and parse
    3685                 :          0 :       parsedColor.setNamedColor( QStringLiteral( "#" ) + colorStr );
    3686                 :          0 :       if ( parsedColor.isValid() )
    3687                 :            :       {
    3688                 :          0 :         containsAlpha = false;
    3689                 :          0 :         return parsedColor;
    3690                 :            :       }
    3691                 :          0 :     }
    3692                 :          0 :   }
    3693                 :            : 
    3694                 :            :   //color in (rrr,ggg,bbb) format, brackets and rgb prefix optional
    3695                 :          0 :   QRegExp rgbFormatRx( "^\\s*(?:rgb)?\\(?\\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\s*,\\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\s*,\\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\s*\\)?\\s*;?\\s*$" );
    3696                 :          0 :   if ( rgbFormatRx.indexIn( colorStr ) != -1 )
    3697                 :            :   {
    3698                 :          0 :     int r = rgbFormatRx.cap( 1 ).toInt();
    3699                 :          0 :     int g = rgbFormatRx.cap( 2 ).toInt();
    3700                 :          0 :     int b = rgbFormatRx.cap( 3 ).toInt();
    3701                 :          0 :     parsedColor.setRgb( r, g, b );
    3702                 :          0 :     if ( parsedColor.isValid() )
    3703                 :            :     {
    3704                 :          0 :       containsAlpha = false;
    3705                 :          0 :       return parsedColor;
    3706                 :            :     }
    3707                 :          0 :   }
    3708                 :            : 
    3709                 :            :   //color in hsl(h,s,l) format, brackets optional
    3710                 :          0 :   const QRegularExpression hslFormatRx( "^\\s*hsl\\(?\\s*(\\d+)\\s*,\\s*(\\d+)\\s*%\\s*,\\s*(\\d+)\\s*%\\s*\\)?\\s*;?\\s*$" );
    3711                 :          0 :   QRegularExpressionMatch match = hslFormatRx.match( colorStr );
    3712                 :          0 :   if ( match.hasMatch() )
    3713                 :            :   {
    3714                 :          0 :     int h = match.captured( 1 ).toInt();
    3715                 :          0 :     int s = match.captured( 2 ).toInt();
    3716                 :          0 :     int l = match.captured( 3 ).toInt();
    3717                 :          0 :     parsedColor.setHsl( h, s / 100.0 * 255.0, l / 100.0 * 255.0 );
    3718                 :          0 :     if ( parsedColor.isValid() )
    3719                 :            :     {
    3720                 :          0 :       containsAlpha = false;
    3721                 :          0 :       return parsedColor;
    3722                 :            :     }
    3723                 :          0 :   }
    3724                 :            : 
    3725                 :            :   //color in (r%,g%,b%) format, brackets and rgb prefix optional
    3726                 :          0 :   QRegExp rgbPercentFormatRx( "^\\s*(?:rgb)?\\(?\\s*(100|0*\\d{1,2})\\s*%\\s*,\\s*(100|0*\\d{1,2})\\s*%\\s*,\\s*(100|0*\\d{1,2})\\s*%\\s*\\)?\\s*;?\\s*$" );
    3727                 :          0 :   if ( rgbPercentFormatRx.indexIn( colorStr ) != -1 )
    3728                 :            :   {
    3729                 :          0 :     int r = std::round( rgbPercentFormatRx.cap( 1 ).toDouble() * 2.55 );
    3730                 :          0 :     int g = std::round( rgbPercentFormatRx.cap( 2 ).toDouble() * 2.55 );
    3731                 :          0 :     int b = std::round( rgbPercentFormatRx.cap( 3 ).toDouble() * 2.55 );
    3732                 :          0 :     parsedColor.setRgb( r, g, b );
    3733                 :          0 :     if ( parsedColor.isValid() )
    3734                 :            :     {
    3735                 :          0 :       containsAlpha = false;
    3736                 :          0 :       return parsedColor;
    3737                 :            :     }
    3738                 :          0 :   }
    3739                 :            : 
    3740                 :            :   //color in (r,g,b,a) format, brackets and rgba prefix optional
    3741                 :          0 :   QRegExp rgbaFormatRx( "^\\s*(?:rgba)?\\(?\\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\s*,\\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\s*,\\s*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\s*,\\s*(0|0?\\.\\d*|1(?:\\.0*)?)\\s*\\)?\\s*;?\\s*$" );
    3742                 :          0 :   if ( rgbaFormatRx.indexIn( colorStr ) != -1 )
    3743                 :            :   {
    3744                 :          0 :     int r = rgbaFormatRx.cap( 1 ).toInt();
    3745                 :          0 :     int g = rgbaFormatRx.cap( 2 ).toInt();
    3746                 :          0 :     int b = rgbaFormatRx.cap( 3 ).toInt();
    3747                 :          0 :     int a = std::round( rgbaFormatRx.cap( 4 ).toDouble() * 255.0 );
    3748                 :          0 :     parsedColor.setRgb( r, g, b, a );
    3749                 :          0 :     if ( parsedColor.isValid() )
    3750                 :            :     {
    3751                 :          0 :       containsAlpha = true;
    3752                 :          0 :       return parsedColor;
    3753                 :            :     }
    3754                 :          0 :   }
    3755                 :            : 
    3756                 :            :   //color in (r%,g%,b%,a) format, brackets and rgba prefix optional
    3757                 :          0 :   QRegExp rgbaPercentFormatRx( "^\\s*(?:rgba)?\\(?\\s*(100|0*\\d{1,2})\\s*%\\s*,\\s*(100|0*\\d{1,2})\\s*%\\s*,\\s*(100|0*\\d{1,2})\\s*%\\s*,\\s*(0|0?\\.\\d*|1(?:\\.0*)?)\\s*\\)?\\s*;?\\s*$" );
    3758                 :          0 :   if ( rgbaPercentFormatRx.indexIn( colorStr ) != -1 )
    3759                 :            :   {
    3760                 :          0 :     int r = std::round( rgbaPercentFormatRx.cap( 1 ).toDouble() * 2.55 );
    3761                 :          0 :     int g = std::round( rgbaPercentFormatRx.cap( 2 ).toDouble() * 2.55 );
    3762                 :          0 :     int b = std::round( rgbaPercentFormatRx.cap( 3 ).toDouble() * 2.55 );
    3763                 :          0 :     int a = std::round( rgbaPercentFormatRx.cap( 4 ).toDouble() * 255.0 );
    3764                 :          0 :     parsedColor.setRgb( r, g, b, a );
    3765                 :          0 :     if ( parsedColor.isValid() )
    3766                 :            :     {
    3767                 :          0 :       containsAlpha = true;
    3768                 :          0 :       return parsedColor;
    3769                 :            :     }
    3770                 :          0 :   }
    3771                 :            : 
    3772                 :            :   //color in hsla(h,s%,l%,a) format, brackets optional
    3773                 :          0 :   const QRegularExpression hslaPercentFormatRx( "^\\s*hsla\\(?\\s*(\\d+)\\s*,\\s*(\\d+)\\s*%\\s*,\\s*(\\d+)\\s*%\\s*,\\s*([\\d\\.]+)\\s*\\)?\\s*;?\\s*$" );
    3774                 :          0 :   match = hslaPercentFormatRx.match( colorStr );
    3775                 :          0 :   if ( match.hasMatch() )
    3776                 :            :   {
    3777                 :          0 :     int h = match.captured( 1 ).toInt();
    3778                 :          0 :     int s = match.captured( 2 ).toInt();
    3779                 :          0 :     int l = match.captured( 3 ).toInt();
    3780                 :          0 :     int a = std::round( match.captured( 4 ).toDouble() * 255.0 );
    3781                 :          0 :     parsedColor.setHsl( h, s / 100.0 * 255.0, l / 100.0 * 255.0, a );
    3782                 :          0 :     if ( parsedColor.isValid() )
    3783                 :            :     {
    3784                 :          0 :       containsAlpha = true;
    3785                 :          0 :       return parsedColor;
    3786                 :            :     }
    3787                 :          0 :   }
    3788                 :            : 
    3789                 :            :   //couldn't parse string as color
    3790                 :          0 :   return QColor();
    3791                 :          0 : }
    3792                 :            : 
    3793                 :          0 : void QgsSymbolLayerUtils::multiplyImageOpacity( QImage *image, qreal opacity )
    3794                 :            : {
    3795                 :          0 :   if ( !image )
    3796                 :            :   {
    3797                 :          0 :     return;
    3798                 :            :   }
    3799                 :            : 
    3800                 :            :   QRgb myRgb;
    3801                 :          0 :   QImage::Format format = image->format();
    3802                 :          0 :   if ( format != QImage::Format_ARGB32_Premultiplied && format != QImage::Format_ARGB32 )
    3803                 :            :   {
    3804                 :          0 :     QgsDebugMsg( QStringLiteral( "no alpha channel." ) );
    3805                 :          0 :     return;
    3806                 :            :   }
    3807                 :            : 
    3808                 :            :   //change the alpha component of every pixel
    3809                 :          0 :   for ( int heightIndex = 0; heightIndex < image->height(); ++heightIndex )
    3810                 :            :   {
    3811                 :          0 :     QRgb *scanLine = reinterpret_cast< QRgb * >( image->scanLine( heightIndex ) );
    3812                 :          0 :     for ( int widthIndex = 0; widthIndex < image->width(); ++widthIndex )
    3813                 :            :     {
    3814                 :          0 :       myRgb = scanLine[widthIndex];
    3815                 :          0 :       if ( format == QImage::Format_ARGB32_Premultiplied )
    3816                 :          0 :         scanLine[widthIndex] = qRgba( opacity * qRed( myRgb ), opacity * qGreen( myRgb ), opacity * qBlue( myRgb ), opacity * qAlpha( myRgb ) );
    3817                 :            :       else
    3818                 :          0 :         scanLine[widthIndex] = qRgba( qRed( myRgb ), qGreen( myRgb ), qBlue( myRgb ), opacity * qAlpha( myRgb ) );
    3819                 :          0 :     }
    3820                 :          0 :   }
    3821                 :          0 : }
    3822                 :            : 
    3823                 :          0 : void QgsSymbolLayerUtils::blurImageInPlace( QImage &image, QRect rect, int radius, bool alphaOnly )
    3824                 :            : {
    3825                 :            :   // culled from Qt's qpixmapfilter.cpp, see: http://www.qtcentre.org/archive/index.php/t-26534.html
    3826                 :          0 :   int tab[] = { 14, 10, 8, 6, 5, 5, 4, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2 };
    3827                 :          0 :   int alpha = ( radius < 1 )  ? 16 : ( radius > 17 ) ? 1 : tab[radius - 1];
    3828                 :            : 
    3829                 :          0 :   if ( image.format() != QImage::Format_ARGB32_Premultiplied
    3830                 :          0 :        && image.format() != QImage::Format_RGB32 )
    3831                 :            :   {
    3832                 :          0 :     image = image.convertToFormat( QImage::Format_ARGB32_Premultiplied );
    3833                 :          0 :   }
    3834                 :            : 
    3835                 :          0 :   int r1 = rect.top();
    3836                 :          0 :   int r2 = rect.bottom();
    3837                 :          0 :   int c1 = rect.left();
    3838                 :          0 :   int c2 = rect.right();
    3839                 :            : 
    3840                 :          0 :   int bpl = image.bytesPerLine();
    3841                 :            :   int rgba[4];
    3842                 :            :   unsigned char *p;
    3843                 :            : 
    3844                 :          0 :   int i1 = 0;
    3845                 :          0 :   int i2 = 3;
    3846                 :            : 
    3847                 :          0 :   if ( alphaOnly ) // this seems to only work right for a black color
    3848                 :          0 :     i1 = i2 = ( QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0 : 3 );
    3849                 :            : 
    3850                 :          0 :   for ( int col = c1; col <= c2; col++ )
    3851                 :            :   {
    3852                 :          0 :     p = image.scanLine( r1 ) + col * 4;
    3853                 :          0 :     for ( int i = i1; i <= i2; i++ )
    3854                 :          0 :       rgba[i] = p[i] << 4;
    3855                 :            : 
    3856                 :          0 :     p += bpl;
    3857                 :          0 :     for ( int j = r1; j < r2; j++, p += bpl )
    3858                 :          0 :       for ( int i = i1; i <= i2; i++ )
    3859                 :          0 :         p[i] = ( rgba[i] += ( ( p[i] << 4 ) - rgba[i] ) * alpha / 16 ) >> 4;
    3860                 :          0 :   }
    3861                 :            : 
    3862                 :          0 :   for ( int row = r1; row <= r2; row++ )
    3863                 :            :   {
    3864                 :          0 :     p = image.scanLine( row ) + c1 * 4;
    3865                 :          0 :     for ( int i = i1; i <= i2; i++ )
    3866                 :          0 :       rgba[i] = p[i] << 4;
    3867                 :            : 
    3868                 :          0 :     p += 4;
    3869                 :          0 :     for ( int j = c1; j < c2; j++, p += 4 )
    3870                 :          0 :       for ( int i = i1; i <= i2; i++ )
    3871                 :          0 :         p[i] = ( rgba[i] += ( ( p[i] << 4 ) - rgba[i] ) * alpha / 16 ) >> 4;
    3872                 :          0 :   }
    3873                 :            : 
    3874                 :          0 :   for ( int col = c1; col <= c2; col++ )
    3875                 :            :   {
    3876                 :          0 :     p = image.scanLine( r2 ) + col * 4;
    3877                 :          0 :     for ( int i = i1; i <= i2; i++ )
    3878                 :          0 :       rgba[i] = p[i] << 4;
    3879                 :            : 
    3880                 :          0 :     p -= bpl;
    3881                 :          0 :     for ( int j = r1; j < r2; j++, p -= bpl )
    3882                 :          0 :       for ( int i = i1; i <= i2; i++ )
    3883                 :          0 :         p[i] = ( rgba[i] += ( ( p[i] << 4 ) - rgba[i] ) * alpha / 16 ) >> 4;
    3884                 :          0 :   }
    3885                 :            : 
    3886                 :          0 :   for ( int row = r1; row <= r2; row++ )
    3887                 :            :   {
    3888                 :          0 :     p = image.scanLine( row ) + c2 * 4;
    3889                 :          0 :     for ( int i = i1; i <= i2; i++ )
    3890                 :          0 :       rgba[i] = p[i] << 4;
    3891                 :            : 
    3892                 :          0 :     p -= 4;
    3893                 :          0 :     for ( int j = c1; j < c2; j++, p -= 4 )
    3894                 :          0 :       for ( int i = i1; i <= i2; i++ )
    3895                 :          0 :         p[i] = ( rgba[i] += ( ( p[i] << 4 ) - rgba[i] ) * alpha / 16 ) >> 4;
    3896                 :          0 :   }
    3897                 :          0 : }
    3898                 :            : 
    3899                 :          0 : void QgsSymbolLayerUtils::premultiplyColor( QColor &rgb, int alpha )
    3900                 :            : {
    3901                 :          0 :   if ( alpha != 255 && alpha > 0 )
    3902                 :            :   {
    3903                 :            :     // Semi-transparent pixel. We need to adjust the colors for ARGB32_Premultiplied images
    3904                 :            :     // where color values have to be premultiplied by alpha
    3905                 :          0 :     double alphaFactor = alpha / 255.;
    3906                 :          0 :     int r = 0, g = 0, b = 0;
    3907                 :          0 :     rgb.getRgb( &r, &g, &b );
    3908                 :            : 
    3909                 :          0 :     r *= alphaFactor;
    3910                 :          0 :     g *= alphaFactor;
    3911                 :          0 :     b *= alphaFactor;
    3912                 :          0 :     rgb.setRgb( r, g, b, alpha );
    3913                 :          0 :   }
    3914                 :          0 :   else if ( alpha == 0 )
    3915                 :            :   {
    3916                 :          0 :     rgb.setRgb( 0, 0, 0, 0 );
    3917                 :          0 :   }
    3918                 :          0 : }
    3919                 :            : 
    3920                 :          0 : void QgsSymbolLayerUtils::sortVariantList( QList<QVariant> &list, Qt::SortOrder order )
    3921                 :            : {
    3922                 :          0 :   if ( order == Qt::AscendingOrder )
    3923                 :            :   {
    3924                 :            :     //std::sort( list.begin(), list.end(), _QVariantLessThan );
    3925                 :          0 :     std::sort( list.begin(), list.end(), qgsVariantLessThan );
    3926                 :          0 :   }
    3927                 :            :   else // Qt::DescendingOrder
    3928                 :            :   {
    3929                 :            :     //std::sort( list.begin(), list.end(), _QVariantGreaterThan );
    3930                 :          0 :     std::sort( list.begin(), list.end(), qgsVariantGreaterThan );
    3931                 :            :   }
    3932                 :          0 : }
    3933                 :            : 
    3934                 :          0 : QPointF QgsSymbolLayerUtils::pointOnLineWithDistance( QPointF startPoint, QPointF directionPoint, double distance )
    3935                 :            : {
    3936                 :          0 :   double dx = directionPoint.x() - startPoint.x();
    3937                 :          0 :   double dy = directionPoint.y() - startPoint.y();
    3938                 :          0 :   double length = std::sqrt( dx * dx + dy * dy );
    3939                 :          0 :   double scaleFactor = distance / length;
    3940                 :          0 :   return QPointF( startPoint.x() + dx * scaleFactor, startPoint.y() + dy * scaleFactor );
    3941                 :            : }
    3942                 :            : 
    3943                 :            : 
    3944                 :          0 : QStringList QgsSymbolLayerUtils::listSvgFiles()
    3945                 :            : {
    3946                 :            :   // copied from QgsMarkerCatalogue - TODO: unify  //#spellok
    3947                 :          0 :   QStringList list;
    3948                 :          0 :   QStringList svgPaths = QgsApplication::svgPaths();
    3949                 :            : 
    3950                 :          0 :   for ( int i = 0; i < svgPaths.size(); i++ )
    3951                 :            :   {
    3952                 :          0 :     QDir dir( svgPaths[i] );
    3953                 :          0 :     const auto svgSubPaths = dir.entryList( QDir::Dirs | QDir::NoDotAndDotDot );
    3954                 :          0 :     for ( const QString &item : svgSubPaths )
    3955                 :            :     {
    3956                 :          0 :       svgPaths.insert( i + 1, dir.path() + '/' + item );
    3957                 :            :     }
    3958                 :            : 
    3959                 :          0 :     const auto svgFiles = dir.entryList( QStringList( "*.svg" ), QDir::Files );
    3960                 :          0 :     for ( const QString &item : svgFiles )
    3961                 :            :     {
    3962                 :            :       // TODO test if it is correct SVG
    3963                 :          0 :       list.append( dir.path() + '/' + item );
    3964                 :            :     }
    3965                 :          0 :   }
    3966                 :          0 :   return list;
    3967                 :          0 : }
    3968                 :            : 
    3969                 :            : // Stripped down version of listSvgFiles() for specified directory
    3970                 :          0 : QStringList QgsSymbolLayerUtils::listSvgFilesAt( const QString &directory )
    3971                 :            : {
    3972                 :            :   // TODO anything that applies for the listSvgFiles() applies this also
    3973                 :            : 
    3974                 :          0 :   QStringList list;
    3975                 :          0 :   QStringList svgPaths;
    3976                 :          0 :   svgPaths.append( directory );
    3977                 :            : 
    3978                 :          0 :   for ( int i = 0; i < svgPaths.size(); i++ )
    3979                 :            :   {
    3980                 :          0 :     QDir dir( svgPaths[i] );
    3981                 :          0 :     const auto svgSubPaths = dir.entryList( QDir::Dirs | QDir::NoDotAndDotDot );
    3982                 :          0 :     for ( const QString &item : svgSubPaths )
    3983                 :            :     {
    3984                 :          0 :       svgPaths.insert( i + 1, dir.path() + '/' + item );
    3985                 :            :     }
    3986                 :            : 
    3987                 :          0 :     const auto svgFiles = dir.entryList( QStringList( "*.svg" ), QDir::Files );
    3988                 :          0 :     for ( const QString &item : svgFiles )
    3989                 :            :     {
    3990                 :          0 :       list.append( dir.path() + '/' + item );
    3991                 :            :     }
    3992                 :          0 :   }
    3993                 :          0 :   return list;
    3994                 :            : 
    3995                 :          0 : }
    3996                 :            : 
    3997                 :         40 : QString QgsSymbolLayerUtils::svgSymbolNameToPath( const QString &n, const QgsPathResolver &pathResolver )
    3998                 :            : {
    3999                 :         40 :   if ( n.isEmpty() )
    4000                 :          0 :     return QString();
    4001                 :            : 
    4002                 :         40 :   if ( n.startsWith( QLatin1String( "base64:" ) ) )
    4003                 :          0 :     return n;
    4004                 :            : 
    4005                 :            :   // we might have a full path...
    4006                 :         40 :   if ( QFileInfo::exists( n ) )
    4007                 :          0 :     return QFileInfo( n ).canonicalFilePath();
    4008                 :            : 
    4009                 :         40 :   QString name = n;
    4010                 :            :   // or it might be an url...
    4011                 :         40 :   if ( name.contains( QLatin1String( "://" ) ) )
    4012                 :            :   {
    4013                 :          0 :     QUrl url( name );
    4014                 :          0 :     if ( url.isValid() && !url.scheme().isEmpty() )
    4015                 :            :     {
    4016                 :          0 :       if ( url.scheme().compare( QLatin1String( "file" ), Qt::CaseInsensitive ) == 0 )
    4017                 :            :       {
    4018                 :            :         // it's a url to a local file
    4019                 :          0 :         name = url.toLocalFile();
    4020                 :          0 :         if ( QFile( name ).exists() )
    4021                 :            :         {
    4022                 :          0 :           return QFileInfo( name ).canonicalFilePath();
    4023                 :            :         }
    4024                 :          0 :       }
    4025                 :            :       else
    4026                 :            :       {
    4027                 :            :         // it's a url pointing to a online resource
    4028                 :          0 :         return name;
    4029                 :            :       }
    4030                 :          0 :     }
    4031                 :          0 :   }
    4032                 :            : 
    4033                 :            :   // SVG symbol not found - probably a relative path was used
    4034                 :            : 
    4035                 :         40 :   QStringList svgPaths = QgsApplication::svgPaths();
    4036                 :        120 :   for ( int i = 0; i < svgPaths.size(); i++ )
    4037                 :            :   {
    4038                 :         80 :     QString svgPath = svgPaths[i];
    4039                 :         80 :     if ( svgPath.endsWith( QChar( '/' ) ) )
    4040                 :            :     {
    4041                 :         80 :       svgPath.chop( 1 );
    4042                 :         80 :     }
    4043                 :            : 
    4044                 :         80 :     QgsDebugMsgLevel( "SvgPath: " + svgPath, 3 );
    4045                 :            :     // Not sure why to lowest dir was used instead of full relative path, it was causing #8664
    4046                 :            :     //QFileInfo myInfo( name );
    4047                 :            :     //QString myFileName = myInfo.fileName(); // foo.svg
    4048                 :            :     //QString myLowestDir = myInfo.dir().dirName();
    4049                 :            :     //QString myLocalPath = svgPath + QString( myLowestDir.isEmpty() ? "" : '/' + myLowestDir ) + '/' + myFileName;
    4050                 :         80 :     QString myLocalPath = svgPath + QDir::separator() + name;
    4051                 :            : 
    4052                 :         80 :     QgsDebugMsgLevel( "Alternative svg path: " + myLocalPath, 3 );
    4053                 :         80 :     if ( QFile( myLocalPath ).exists() )
    4054                 :            :     {
    4055                 :          0 :       QgsDebugMsgLevel( QStringLiteral( "Svg found in alternative path" ), 3 );
    4056                 :          0 :       return QFileInfo( myLocalPath ).canonicalFilePath();
    4057                 :            :     }
    4058                 :         80 :   }
    4059                 :            : 
    4060                 :         40 :   return pathResolver.readPath( name );
    4061                 :         40 : }
    4062                 :            : 
    4063                 :          0 : QString QgsSymbolLayerUtils::svgSymbolPathToName( const QString &p, const QgsPathResolver &pathResolver )
    4064                 :            : {
    4065                 :          0 :   if ( p.isEmpty() )
    4066                 :          0 :     return QString();
    4067                 :            : 
    4068                 :          0 :   if ( p.startsWith( QLatin1String( "base64:" ) ) )
    4069                 :          0 :     return p;
    4070                 :            : 
    4071                 :          0 :   if ( !QFileInfo::exists( p ) )
    4072                 :          0 :     return p;
    4073                 :            : 
    4074                 :          0 :   QString path = QFileInfo( p ).canonicalFilePath();
    4075                 :            : 
    4076                 :          0 :   QStringList svgPaths = QgsApplication::svgPaths();
    4077                 :            : 
    4078                 :          0 :   bool isInSvgPaths = false;
    4079                 :          0 :   for ( int i = 0; i < svgPaths.size(); i++ )
    4080                 :            :   {
    4081                 :          0 :     QString dir = QFileInfo( svgPaths[i] ).canonicalFilePath();
    4082                 :            : 
    4083                 :          0 :     if ( !dir.isEmpty() && path.startsWith( dir ) )
    4084                 :            :     {
    4085                 :          0 :       path = path.mid( dir.size() + 1 );
    4086                 :          0 :       isInSvgPaths = true;
    4087                 :          0 :       break;
    4088                 :            :     }
    4089                 :          0 :   }
    4090                 :            : 
    4091                 :          0 :   if ( isInSvgPaths )
    4092                 :          0 :     return path;
    4093                 :            : 
    4094                 :          0 :   return pathResolver.writePath( path );
    4095                 :          0 : }
    4096                 :            : 
    4097                 :            : 
    4098                 :          0 : QPointF QgsSymbolLayerUtils::polygonCentroid( const QPolygonF &points )
    4099                 :            : {
    4100                 :            :   //Calculate the centroid of points
    4101                 :          0 :   double cx = 0, cy = 0;
    4102                 :          0 :   double area, sum = 0;
    4103                 :          0 :   for ( int i = points.count() - 1, j = 0; j < points.count(); i = j++ )
    4104                 :            :   {
    4105                 :          0 :     const QPointF &p1 = points[i];
    4106                 :          0 :     const QPointF &p2 = points[j];
    4107                 :          0 :     area = p1.x() * p2.y() - p1.y() * p2.x();
    4108                 :          0 :     sum += area;
    4109                 :          0 :     cx += ( p1.x() + p2.x() ) * area;
    4110                 :          0 :     cy += ( p1.y() + p2.y() ) * area;
    4111                 :          0 :   }
    4112                 :          0 :   sum *= 3.0;
    4113                 :          0 :   if ( qgsDoubleNear( sum, 0.0 ) )
    4114                 :            :   {
    4115                 :            :     // the linear ring is invalid -  let's fall back to a solution that will still
    4116                 :            :     // allow us render at least something (instead of just returning point nan,nan)
    4117                 :          0 :     if ( points.count() >= 2 )
    4118                 :          0 :       return QPointF( ( points[0].x() + points[1].x() ) / 2, ( points[0].y() + points[1].y() ) / 2 );
    4119                 :          0 :     else if ( points.count() == 1 )
    4120                 :          0 :       return points[0];
    4121                 :            :     else
    4122                 :          0 :       return QPointF(); // hopefully we shouldn't ever get here
    4123                 :            :   }
    4124                 :          0 :   cx /= sum;
    4125                 :          0 :   cy /= sum;
    4126                 :            : 
    4127                 :          0 :   return QPointF( cx, cy );
    4128                 :          0 : }
    4129                 :            : 
    4130                 :          0 : QPointF QgsSymbolLayerUtils::polygonPointOnSurface( const QPolygonF &points, const QVector<QPolygonF> *rings )
    4131                 :            : {
    4132                 :          0 :   QPointF centroid = QgsSymbolLayerUtils::polygonCentroid( points );
    4133                 :            : 
    4134                 :          0 :   if ( ( rings && rings->count() > 0 ) || !pointInPolygon( points, centroid ) )
    4135                 :            :   {
    4136                 :          0 :     unsigned int i, pointCount = points.count();
    4137                 :          0 :     QgsPolylineXY polyline( pointCount );
    4138                 :          0 :     for ( i = 0; i < pointCount; ++i ) polyline[i] = QgsPointXY( points[i].x(), points[i].y() );
    4139                 :          0 :     QgsGeometry geom = QgsGeometry::fromPolygonXY( QgsPolygonXY() << polyline );
    4140                 :          0 :     if ( !geom.isNull() )
    4141                 :            :     {
    4142                 :          0 :       if ( rings )
    4143                 :            :       {
    4144                 :          0 :         for ( auto ringIt = rings->constBegin(); ringIt != rings->constEnd(); ++ringIt )
    4145                 :            :         {
    4146                 :          0 :           pointCount = ( *ringIt ).count();
    4147                 :          0 :           QgsPolylineXY polyline( pointCount );
    4148                 :          0 :           for ( i = 0; i < pointCount; ++i ) polyline[i] = QgsPointXY( ( *ringIt )[i].x(), ( *ringIt )[i].y() );
    4149                 :          0 :           geom.addRing( polyline );
    4150                 :          0 :         }
    4151                 :          0 :       }
    4152                 :            : 
    4153                 :          0 :       QgsGeometry pointOnSurfaceGeom = geom.pointOnSurface();
    4154                 :          0 :       if ( !pointOnSurfaceGeom.isNull() )
    4155                 :            :       {
    4156                 :          0 :         QgsPointXY point = pointOnSurfaceGeom.asPoint();
    4157                 :          0 :         centroid.setX( point.x() );
    4158                 :          0 :         centroid.setY( point.y() );
    4159                 :          0 :       }
    4160                 :          0 :     }
    4161                 :          0 :   }
    4162                 :            : 
    4163                 :          0 :   return QPointF( centroid.x(), centroid.y() );
    4164                 :          0 : }
    4165                 :            : 
    4166                 :          0 : bool QgsSymbolLayerUtils::pointInPolygon( const QPolygonF &points, QPointF point )
    4167                 :            : {
    4168                 :          0 :   bool inside = false;
    4169                 :            : 
    4170                 :          0 :   double x = point.x();
    4171                 :          0 :   double y = point.y();
    4172                 :            : 
    4173                 :          0 :   for ( int i = 0, j = points.count() - 1; i < points.count(); i++ )
    4174                 :            :   {
    4175                 :          0 :     const QPointF &p1 = points[i];
    4176                 :          0 :     const QPointF &p2 = points[j];
    4177                 :            : 
    4178                 :          0 :     if ( qgsDoubleNear( p1.x(), x ) && qgsDoubleNear( p1.y(), y ) )
    4179                 :          0 :       return true;
    4180                 :            : 
    4181                 :          0 :     if ( ( p1.y() < y && p2.y() >= y ) || ( p2.y() < y && p1.y() >= y ) )
    4182                 :            :     {
    4183                 :          0 :       if ( p1.x() + ( y - p1.y() ) / ( p2.y() - p1.y() ) * ( p2.x() - p1.x() ) <= x )
    4184                 :          0 :         inside = !inside;
    4185                 :          0 :     }
    4186                 :            : 
    4187                 :          0 :     j = i;
    4188                 :          0 :   }
    4189                 :          0 :   return inside;
    4190                 :          0 : }
    4191                 :            : 
    4192                 :          0 : double QgsSymbolLayerUtils::polylineLength( const QPolygonF &polyline )
    4193                 :            : {
    4194                 :          0 :   if ( polyline.size() < 2 )
    4195                 :          0 :     return 0;
    4196                 :            : 
    4197                 :          0 :   double totalLength = 0;
    4198                 :          0 :   auto it = polyline.begin();
    4199                 :          0 :   QPointF p1 = *it++;
    4200                 :          0 :   for ( ; it != polyline.end(); ++it )
    4201                 :            :   {
    4202                 :          0 :     QPointF p2 = *it;
    4203                 :          0 :     const double segmentLength = std::sqrt( std::pow( p1.x() - p2.x(), 2.0 ) + std::pow( p1.y() - p2.y(), 2.0 ) );
    4204                 :          0 :     totalLength += segmentLength;
    4205                 :          0 :     p1 = p2;
    4206                 :          0 :   }
    4207                 :          0 :   return totalLength;
    4208                 :          0 : }
    4209                 :            : 
    4210                 :          0 : QPolygonF QgsSymbolLayerUtils::polylineSubstring( const QPolygonF &polyline, double startOffset, double endOffset )
    4211                 :            : {
    4212                 :          0 :   if ( polyline.size() < 2 )
    4213                 :          0 :     return QPolygonF();
    4214                 :            : 
    4215                 :          0 :   double totalLength = 0;
    4216                 :          0 :   auto it = polyline.begin();
    4217                 :          0 :   QPointF p1 = *it++;
    4218                 :          0 :   std::vector< double > segmentLengths( polyline.size() - 1 );
    4219                 :          0 :   auto segmentLengthIt = segmentLengths.begin();
    4220                 :          0 :   for ( ; it != polyline.end(); ++it )
    4221                 :            :   {
    4222                 :          0 :     QPointF p2 = *it;
    4223                 :          0 :     *segmentLengthIt = std::sqrt( std::pow( p1.x() - p2.x(), 2.0 ) + std::pow( p1.y() - p2.y(), 2.0 ) );
    4224                 :          0 :     totalLength += *segmentLengthIt;
    4225                 :            : 
    4226                 :          0 :     segmentLengthIt++;
    4227                 :          0 :     p1 = p2;
    4228                 :          0 :   }
    4229                 :            : 
    4230                 :          0 :   if ( startOffset >= 0 && totalLength <= startOffset )
    4231                 :          0 :     return QPolygonF();
    4232                 :          0 :   if ( endOffset < 0 && totalLength <= -endOffset )
    4233                 :          0 :     return QPolygonF();
    4234                 :            : 
    4235                 :          0 :   const double startDistance = startOffset < 0 ? totalLength + startOffset : startOffset;
    4236                 :          0 :   const double endDistance = endOffset <= 0 ? totalLength + endOffset : endOffset;
    4237                 :          0 :   QPolygonF substringPoints;
    4238                 :          0 :   substringPoints.reserve( polyline.size() );
    4239                 :            : 
    4240                 :          0 :   it = polyline.begin();
    4241                 :          0 :   segmentLengthIt = segmentLengths.begin();
    4242                 :            : 
    4243                 :          0 :   p1 = *it++;
    4244                 :          0 :   bool foundStart = false;
    4245                 :          0 :   if ( qgsDoubleNear( startDistance, 0.0 ) || startDistance < 0 )
    4246                 :            :   {
    4247                 :          0 :     substringPoints << p1;
    4248                 :          0 :     foundStart = true;
    4249                 :          0 :   }
    4250                 :            : 
    4251                 :          0 :   double distanceTraversed = 0;
    4252                 :          0 :   for ( ; it != polyline.end(); ++it )
    4253                 :            :   {
    4254                 :          0 :     QPointF p2 = *it;
    4255                 :          0 :     if ( distanceTraversed < startDistance && distanceTraversed + *segmentLengthIt > startDistance )
    4256                 :            :     {
    4257                 :            :       // start point falls on this segment
    4258                 :          0 :       const double distanceToStart = startDistance - distanceTraversed;
    4259                 :            :       double startX, startY;
    4260                 :          0 :       QgsGeometryUtils::pointOnLineWithDistance( p1.x(), p1.y(), p2.x(), p2.y(), distanceToStart, startX, startY );
    4261                 :          0 :       substringPoints << QPointF( startX, startY );
    4262                 :          0 :       foundStart = true;
    4263                 :          0 :     }
    4264                 :          0 :     if ( foundStart && ( distanceTraversed + *segmentLengthIt > endDistance ) )
    4265                 :            :     {
    4266                 :            :       // end point falls on this segment
    4267                 :          0 :       const double distanceToEnd = endDistance - distanceTraversed;
    4268                 :            :       double endX, endY;
    4269                 :          0 :       QgsGeometryUtils::pointOnLineWithDistance( p1.x(), p1.y(), p2.x(), p2.y(), distanceToEnd, endX, endY );
    4270                 :          0 :       if ( substringPoints.last() != QPointF( endX, endY ) )
    4271                 :          0 :         substringPoints << QPointF( endX, endY );
    4272                 :          0 :     }
    4273                 :          0 :     else if ( foundStart )
    4274                 :            :     {
    4275                 :          0 :       if ( substringPoints.last() != QPointF( p2.x(), p2.y() ) )
    4276                 :          0 :         substringPoints << QPointF( p2.x(), p2.y() );
    4277                 :          0 :     }
    4278                 :            : 
    4279                 :          0 :     distanceTraversed += *segmentLengthIt;
    4280                 :          0 :     if ( distanceTraversed > endDistance )
    4281                 :          0 :       break;
    4282                 :            : 
    4283                 :          0 :     p1 = p2;
    4284                 :          0 :     segmentLengthIt++;
    4285                 :          0 :   }
    4286                 :            : 
    4287                 :          0 :   if ( ( substringPoints.size() < 2 ) || ( substringPoints.size() == 2 && substringPoints.at( 0 ) == substringPoints.at( 1 ) ) )
    4288                 :          0 :     return QPolygonF();
    4289                 :            : 
    4290                 :          0 :   return substringPoints;
    4291                 :          0 : }
    4292                 :            : 
    4293                 :          0 : bool QgsSymbolLayerUtils::isSharpCorner( QPointF p1, QPointF p2, QPointF p3 )
    4294                 :            : {
    4295                 :          0 :   double vertexAngle = M_PI - ( std::atan2( p3.y() - p2.y(), p3.x() - p2.x() ) - std::atan2( p2.y() - p1.y(), p2.x() - p1.x() ) );
    4296                 :          0 :   vertexAngle = QgsGeometryUtils::normalizedAngle( vertexAngle );
    4297                 :            : 
    4298                 :            :   // extreme angles form more than 45 degree angle at a node
    4299                 :          0 :   return vertexAngle < M_PI * 135.0 / 180.0 || vertexAngle > M_PI * 225.0 / 180.0;
    4300                 :            : }
    4301                 :            : 
    4302                 :          0 : void QgsSymbolLayerUtils::appendPolyline( QPolygonF &target, const QPolygonF &line )
    4303                 :            : {
    4304                 :          0 :   target.reserve( target.size() + line.size() );
    4305                 :          0 :   for ( const QPointF &pt : line )
    4306                 :            :   {
    4307                 :          0 :     if ( !target.empty() && target.last() == pt )
    4308                 :          0 :       continue;
    4309                 :            : 
    4310                 :          0 :     target << pt;
    4311                 :            :   }
    4312                 :          0 : }
    4313                 :            : 
    4314                 :          0 : QgsExpression *QgsSymbolLayerUtils::fieldOrExpressionToExpression( const QString &fieldOrExpression )
    4315                 :            : {
    4316                 :          0 :   if ( fieldOrExpression.isEmpty() )
    4317                 :          0 :     return nullptr;
    4318                 :            : 
    4319                 :          0 :   QgsExpression *expr = new QgsExpression( fieldOrExpression );
    4320                 :          0 :   if ( !expr->hasParserError() )
    4321                 :          0 :     return expr;
    4322                 :            : 
    4323                 :            :   // now try with quoted field name
    4324                 :          0 :   delete expr;
    4325                 :          0 :   QgsExpression *expr2 = new QgsExpression( QgsExpression::quotedColumnRef( fieldOrExpression ) );
    4326                 :            :   Q_ASSERT( !expr2->hasParserError() );
    4327                 :          0 :   return expr2;
    4328                 :          0 : }
    4329                 :            : 
    4330                 :          0 : QString QgsSymbolLayerUtils::fieldOrExpressionFromExpression( QgsExpression *expression )
    4331                 :            : {
    4332                 :          0 :   const QgsExpressionNode *n = expression->rootNode();
    4333                 :            : 
    4334                 :          0 :   if ( n && n->nodeType() == QgsExpressionNode::ntColumnRef )
    4335                 :          0 :     return static_cast<const QgsExpressionNodeColumnRef *>( n )->name();
    4336                 :            : 
    4337                 :          0 :   return expression->expression();
    4338                 :          0 : }
    4339                 :            : 
    4340                 :          0 : QList<double> QgsSymbolLayerUtils::prettyBreaks( double minimum, double maximum, int classes )
    4341                 :            : {
    4342                 :            :   // C++ implementation of R's pretty algorithm
    4343                 :            :   // Based on code for determining optimal tick placement for statistical graphics
    4344                 :            :   // from the R statistical programming language.
    4345                 :            :   // Code ported from R implementation from 'labeling' R package
    4346                 :            :   //
    4347                 :            :   // Computes a sequence of about 'classes' equally spaced round values
    4348                 :            :   // which cover the range of values from 'minimum' to 'maximum'.
    4349                 :            :   // The values are chosen so that they are 1, 2 or 5 times a power of 10.
    4350                 :            : 
    4351                 :          0 :   QList<double> breaks;
    4352                 :          0 :   if ( classes < 1 )
    4353                 :            :   {
    4354                 :          0 :     breaks.append( maximum );
    4355                 :          0 :     return breaks;
    4356                 :            :   }
    4357                 :            : 
    4358                 :          0 :   int minimumCount = static_cast< int >( classes ) / 3;
    4359                 :          0 :   double shrink = 0.75;
    4360                 :          0 :   double highBias = 1.5;
    4361                 :          0 :   double adjustBias = 0.5 + 1.5 * highBias;
    4362                 :          0 :   int divisions = classes;
    4363                 :          0 :   double h = highBias;
    4364                 :            :   double cell;
    4365                 :          0 :   bool small = false;
    4366                 :          0 :   double dx = maximum - minimum;
    4367                 :            : 
    4368                 :          0 :   if ( qgsDoubleNear( dx, 0.0 ) && qgsDoubleNear( maximum, 0.0 ) )
    4369                 :            :   {
    4370                 :          0 :     cell = 1.0;
    4371                 :          0 :     small = true;
    4372                 :          0 :   }
    4373                 :            :   else
    4374                 :            :   {
    4375                 :          0 :     int U = 1;
    4376                 :          0 :     cell = std::max( std::fabs( minimum ), std::fabs( maximum ) );
    4377                 :          0 :     if ( adjustBias >= 1.5 * h + 0.5 )
    4378                 :            :     {
    4379                 :          0 :       U = 1 + ( 1.0 / ( 1 + h ) );
    4380                 :          0 :     }
    4381                 :            :     else
    4382                 :            :     {
    4383                 :          0 :       U = 1 + ( 1.5 / ( 1 + adjustBias ) );
    4384                 :            :     }
    4385                 :          0 :     small = dx < ( cell * U * std::max( 1, divisions ) * 1e-07 * 3.0 );
    4386                 :            :   }
    4387                 :            : 
    4388                 :          0 :   if ( small )
    4389                 :            :   {
    4390                 :          0 :     if ( cell > 10 )
    4391                 :            :     {
    4392                 :          0 :       cell = 9 + cell / 10;
    4393                 :          0 :       cell = cell * shrink;
    4394                 :          0 :     }
    4395                 :          0 :     if ( minimumCount > 1 )
    4396                 :            :     {
    4397                 :          0 :       cell = cell / minimumCount;
    4398                 :          0 :     }
    4399                 :          0 :   }
    4400                 :            :   else
    4401                 :            :   {
    4402                 :          0 :     cell = dx;
    4403                 :          0 :     if ( divisions > 1 )
    4404                 :            :     {
    4405                 :          0 :       cell = cell / divisions;
    4406                 :          0 :     }
    4407                 :            :   }
    4408                 :          0 :   if ( cell < 20 * 1e-07 )
    4409                 :            :   {
    4410                 :          0 :     cell = 20 * 1e-07;
    4411                 :          0 :   }
    4412                 :            : 
    4413                 :          0 :   double base = std::pow( 10.0, std::floor( std::log10( cell ) ) );
    4414                 :          0 :   double unit = base;
    4415                 :          0 :   if ( ( 2 * base ) - cell < h * ( cell - unit ) )
    4416                 :            :   {
    4417                 :          0 :     unit = 2.0 * base;
    4418                 :          0 :     if ( ( 5 * base ) - cell < adjustBias * ( cell - unit ) )
    4419                 :            :     {
    4420                 :          0 :       unit = 5.0 * base;
    4421                 :          0 :       if ( ( 10.0 * base ) - cell < h * ( cell - unit ) )
    4422                 :            :       {
    4423                 :          0 :         unit = 10.0 * base;
    4424                 :          0 :       }
    4425                 :          0 :     }
    4426                 :          0 :   }
    4427                 :            :   // Maybe used to correct for the epsilon here??
    4428                 :          0 :   int start = std::floor( minimum / unit + 1e-07 );
    4429                 :          0 :   int end = std::ceil( maximum / unit - 1e-07 );
    4430                 :            : 
    4431                 :            :   // Extend the range out beyond the data. Does this ever happen??
    4432                 :          0 :   while ( start * unit > minimum + ( 1e-07 * unit ) )
    4433                 :            :   {
    4434                 :          0 :     start = start - 1;
    4435                 :            :   }
    4436                 :          0 :   while ( end * unit < maximum - ( 1e-07 * unit ) )
    4437                 :            :   {
    4438                 :          0 :     end = end + 1;
    4439                 :            :   }
    4440                 :          0 :   QgsDebugMsg( QStringLiteral( "pretty classes: %1" ).arg( end ) );
    4441                 :            : 
    4442                 :            :   // If we don't have quite enough labels, extend the range out
    4443                 :            :   // to make more (these labels are beyond the data :()
    4444                 :          0 :   int k = std::floor( 0.5 + end - start );
    4445                 :          0 :   if ( k < minimumCount )
    4446                 :            :   {
    4447                 :          0 :     k = minimumCount - k;
    4448                 :          0 :     if ( start >= 0 )
    4449                 :            :     {
    4450                 :          0 :       end = end + k / 2;
    4451                 :          0 :       start = start - k / 2 + k % 2;
    4452                 :          0 :     }
    4453                 :            :     else
    4454                 :            :     {
    4455                 :          0 :       start = start - k / 2;
    4456                 :          0 :       end = end + k / 2 + k % 2;
    4457                 :            :     }
    4458                 :          0 :   }
    4459                 :          0 :   double minimumBreak = start * unit;
    4460                 :            :   //double maximumBreak = end * unit;
    4461                 :          0 :   int count = end - start;
    4462                 :            : 
    4463                 :          0 :   breaks.reserve( count );
    4464                 :          0 :   for ( int i = 1; i < count + 1; i++ )
    4465                 :            :   {
    4466                 :          0 :     breaks.append( minimumBreak + i * unit );
    4467                 :          0 :   }
    4468                 :            : 
    4469                 :          0 :   if ( breaks.isEmpty() )
    4470                 :          0 :     return breaks;
    4471                 :            : 
    4472                 :          0 :   if ( breaks.first() < minimum )
    4473                 :            :   {
    4474                 :          0 :     breaks[0] = minimum;
    4475                 :          0 :   }
    4476                 :          0 :   if ( breaks.last() > maximum )
    4477                 :            :   {
    4478                 :          0 :     breaks[breaks.count() - 1] = maximum;
    4479                 :          0 :   }
    4480                 :            : 
    4481                 :            :   // because sometimes when number of classes is big,
    4482                 :            :   // break supposed to be at zero is something like -2.22045e-16
    4483                 :          0 :   if ( minimum < 0.0 && maximum > 0.0 ) //then there should be a zero somewhere
    4484                 :            :   {
    4485                 :          0 :     QList<double> breaksMinusZero; // compute difference "each break - 0"
    4486                 :          0 :     for ( int i = 0; i < breaks.count(); i++ )
    4487                 :            :     {
    4488                 :          0 :       breaksMinusZero.append( breaks[i] - 0.0 );
    4489                 :          0 :     }
    4490                 :          0 :     int posOfMin = 0;
    4491                 :          0 :     for ( int i = 1; i < breaks.count(); i++ ) // find position of minimal difference
    4492                 :            :     {
    4493                 :          0 :       if ( std::abs( breaksMinusZero[i] ) < std::abs( breaksMinusZero[i - 1] ) )
    4494                 :          0 :         posOfMin = i;
    4495                 :          0 :     }
    4496                 :          0 :     breaks[posOfMin] = 0.0;
    4497                 :          0 :   }
    4498                 :            : 
    4499                 :          0 :   return breaks;
    4500                 :          0 : }
    4501                 :            : 
    4502                 :          0 : double QgsSymbolLayerUtils::rescaleUom( double size, QgsUnitTypes::RenderUnit unit, const QVariantMap &props )
    4503                 :            : {
    4504                 :          0 :   double scale = 1;
    4505                 :          0 :   bool roundToUnit = false;
    4506                 :          0 :   if ( unit == QgsUnitTypes::RenderUnknownUnit )
    4507                 :            :   {
    4508                 :          0 :     if ( props.contains( QStringLiteral( "uomScale" ) ) )
    4509                 :            :     {
    4510                 :            :       bool ok;
    4511                 :          0 :       scale = props.value( QStringLiteral( "uomScale" ) ).toDouble( &ok );
    4512                 :          0 :       if ( !ok )
    4513                 :            :       {
    4514                 :          0 :         return size;
    4515                 :            :       }
    4516                 :          0 :     }
    4517                 :          0 :   }
    4518                 :            :   else
    4519                 :            :   {
    4520                 :          0 :     if ( props.value( QStringLiteral( "uom" ) ) == QLatin1String( "http://www.opengeospatial.org/se/units/metre" ) )
    4521                 :            :     {
    4522                 :          0 :       switch ( unit )
    4523                 :            :       {
    4524                 :            :         case QgsUnitTypes::RenderMillimeters:
    4525                 :          0 :           scale = 0.001;
    4526                 :          0 :           break;
    4527                 :            :         case QgsUnitTypes::RenderPixels:
    4528                 :          0 :           scale = 0.00028;
    4529                 :          0 :           roundToUnit = true;
    4530                 :          0 :           break;
    4531                 :            :         default:
    4532                 :          0 :           scale = 1;
    4533                 :          0 :       }
    4534                 :          0 :     }
    4535                 :            :     else
    4536                 :            :     {
    4537                 :            :       // target is pixels
    4538                 :          0 :       switch ( unit )
    4539                 :            :       {
    4540                 :            :         case QgsUnitTypes::RenderMillimeters:
    4541                 :          0 :           scale = 1 / 0.28;
    4542                 :          0 :           roundToUnit = true;
    4543                 :          0 :           break;
    4544                 :            :         case QgsUnitTypes::RenderInches:
    4545                 :          0 :           scale = 1 / 0.28 * 25.4;
    4546                 :          0 :           roundToUnit = true;
    4547                 :          0 :           break;
    4548                 :            :         case QgsUnitTypes::RenderPoints:
    4549                 :          0 :           scale = 90. /* dots per inch according to OGC SLD */ / 72. /* points per inch */;
    4550                 :          0 :           roundToUnit = true;
    4551                 :          0 :           break;
    4552                 :            :         case QgsUnitTypes::RenderPixels:
    4553                 :            :           // pixel is pixel
    4554                 :          0 :           scale = 1;
    4555                 :          0 :           break;
    4556                 :            :         case QgsUnitTypes::RenderMapUnits:
    4557                 :            :         case QgsUnitTypes::RenderMetersInMapUnits:
    4558                 :            :           // already handed via uom
    4559                 :          0 :           scale = 1;
    4560                 :          0 :           break;
    4561                 :            :         case QgsUnitTypes::RenderPercentage:
    4562                 :            :         case QgsUnitTypes::RenderUnknownUnit:
    4563                 :            :           // these do not make sense and should not really reach here
    4564                 :          0 :           scale = 1;
    4565                 :          0 :       }
    4566                 :            :     }
    4567                 :            : 
    4568                 :            :   }
    4569                 :          0 :   double rescaled = size * scale;
    4570                 :            :   // round to unit if the result is pixels to avoid a weird looking SLD (people often think
    4571                 :            :   // of pixels as integers, even if SLD allows for float values in there
    4572                 :          0 :   if ( roundToUnit )
    4573                 :            :   {
    4574                 :          0 :     rescaled = std::round( rescaled );
    4575                 :          0 :   }
    4576                 :          0 :   return rescaled;
    4577                 :          0 : }
    4578                 :            : 
    4579                 :          0 : QPointF QgsSymbolLayerUtils::rescaleUom( QPointF point, QgsUnitTypes::RenderUnit unit, const QVariantMap &props )
    4580                 :            : {
    4581                 :          0 :   double x = rescaleUom( point.x(), unit, props );
    4582                 :          0 :   double y = rescaleUom( point.y(), unit, props );
    4583                 :          0 :   return QPointF( x, y );
    4584                 :            : }
    4585                 :            : 
    4586                 :          0 : QVector<qreal> QgsSymbolLayerUtils::rescaleUom( const QVector<qreal> &array, QgsUnitTypes::RenderUnit unit, const QVariantMap &props )
    4587                 :            : {
    4588                 :          0 :   QVector<qreal> result;
    4589                 :          0 :   QVector<qreal>::const_iterator it = array.constBegin();
    4590                 :          0 :   for ( ; it != array.constEnd(); ++it )
    4591                 :            :   {
    4592                 :          0 :     result.append( rescaleUom( *it, unit, props ) );
    4593                 :          0 :   }
    4594                 :          0 :   return result;
    4595                 :          0 : }
    4596                 :            : 
    4597                 :          0 : void QgsSymbolLayerUtils::applyScaleDependency( QDomDocument &doc, QDomElement &ruleElem, QVariantMap &props )
    4598                 :            : {
    4599                 :          0 :   if ( !props.value( QStringLiteral( "scaleMinDenom" ), QString() ).toString().isEmpty() )
    4600                 :            :   {
    4601                 :          0 :     QDomElement scaleMinDenomElem = doc.createElement( QStringLiteral( "se:MinScaleDenominator" ) );
    4602                 :          0 :     scaleMinDenomElem.appendChild( doc.createTextNode( qgsDoubleToString( props.value( QStringLiteral( "scaleMinDenom" ) ).toString().toDouble() ) ) );
    4603                 :          0 :     ruleElem.appendChild( scaleMinDenomElem );
    4604                 :          0 :   }
    4605                 :            : 
    4606                 :          0 :   if ( !props.value( QStringLiteral( "scaleMaxDenom" ), QString() ).toString().isEmpty() )
    4607                 :            :   {
    4608                 :          0 :     QDomElement scaleMaxDenomElem = doc.createElement( QStringLiteral( "se:MaxScaleDenominator" ) );
    4609                 :          0 :     scaleMaxDenomElem.appendChild( doc.createTextNode( qgsDoubleToString( props.value( QStringLiteral( "scaleMaxDenom" ) ).toString().toDouble() ) ) );
    4610                 :          0 :     ruleElem.appendChild( scaleMaxDenomElem );
    4611                 :          0 :   }
    4612                 :          0 : }
    4613                 :            : 
    4614                 :          0 : void QgsSymbolLayerUtils::mergeScaleDependencies( double mScaleMinDenom, double mScaleMaxDenom, QVariantMap &props )
    4615                 :            : {
    4616                 :          0 :   if ( !qgsDoubleNear( mScaleMinDenom, 0 ) )
    4617                 :            :   {
    4618                 :            :     bool ok;
    4619                 :          0 :     double parentScaleMinDenom = props.value( QStringLiteral( "scaleMinDenom" ), QStringLiteral( "0" ) ).toString().toDouble( &ok );
    4620                 :          0 :     if ( !ok || parentScaleMinDenom <= 0 )
    4621                 :          0 :       props[ QStringLiteral( "scaleMinDenom" )] = QString::number( mScaleMinDenom );
    4622                 :            :     else
    4623                 :          0 :       props[ QStringLiteral( "scaleMinDenom" )] = QString::number( std::max( parentScaleMinDenom, mScaleMinDenom ) );
    4624                 :          0 :   }
    4625                 :            : 
    4626                 :          0 :   if ( !qgsDoubleNear( mScaleMaxDenom, 0 ) )
    4627                 :            :   {
    4628                 :            :     bool ok;
    4629                 :          0 :     double parentScaleMaxDenom = props.value( QStringLiteral( "scaleMaxDenom" ), QStringLiteral( "0" ) ).toString().toDouble( &ok );
    4630                 :          0 :     if ( !ok || parentScaleMaxDenom <= 0 )
    4631                 :          0 :       props[ QStringLiteral( "scaleMaxDenom" )] = QString::number( mScaleMaxDenom );
    4632                 :            :     else
    4633                 :          0 :       props[ QStringLiteral( "scaleMaxDenom" )] = QString::number( std::min( parentScaleMaxDenom, mScaleMaxDenom ) );
    4634                 :          0 :   }
    4635                 :          0 : }
    4636                 :            : 
    4637                 :          0 : double QgsSymbolLayerUtils::sizeInPixelsFromSldUom( const QString &uom, double size )
    4638                 :            : {
    4639                 :          0 :   double scale = 1.0;
    4640                 :            : 
    4641                 :          0 :   if ( uom == QLatin1String( "http://www.opengeospatial.org/se/units/metre" ) )
    4642                 :            :   {
    4643                 :          0 :     scale = 1.0 / 0.00028; // from meters to pixels
    4644                 :          0 :   }
    4645                 :          0 :   else if ( uom == QLatin1String( "http://www.opengeospatial.org/se/units/foot" ) )
    4646                 :            :   {
    4647                 :          0 :     scale = 304.8 / 0.28; // from feet to pixels
    4648                 :          0 :   }
    4649                 :            :   else
    4650                 :            :   {
    4651                 :          0 :     scale = 1.0; // from pixels to pixels (default unit)
    4652                 :            :   }
    4653                 :            : 
    4654                 :          0 :   return size * scale;
    4655                 :            : }
    4656                 :            : 
    4657                 :          0 : QSet<const QgsSymbolLayer *> QgsSymbolLayerUtils::toSymbolLayerPointers( QgsFeatureRenderer *renderer, const QSet<QgsSymbolLayerId> &symbolLayerIds )
    4658                 :            : {
    4659                 :          0 :   class SymbolLayerVisitor : public QgsStyleEntityVisitorInterface
    4660                 :            :   {
    4661                 :            :     public:
    4662                 :          0 :       SymbolLayerVisitor( const QSet<QgsSymbolLayerId> &layerIds )
    4663                 :          0 :         : mSymbolLayerIds( layerIds )
    4664                 :          0 :       {}
    4665                 :            : 
    4666                 :          0 :       bool visitEnter( const QgsStyleEntityVisitorInterface::Node &node ) override
    4667                 :            :       {
    4668                 :          0 :         if ( node.type == QgsStyleEntityVisitorInterface::NodeType::SymbolRule )
    4669                 :            :         {
    4670                 :          0 :           mCurrentRuleKey = node.identifier;
    4671                 :          0 :           return true;
    4672                 :            :         }
    4673                 :          0 :         return false;
    4674                 :          0 :       }
    4675                 :            : 
    4676                 :          0 :       void visitSymbol( const QgsSymbol *symbol, const QString &identifier, QVector<int> rootPath )
    4677                 :            :       {
    4678                 :          0 :         for ( int idx = 0; idx < symbol->symbolLayerCount(); idx++ )
    4679                 :            :         {
    4680                 :          0 :           QVector<int> indexPath = rootPath;
    4681                 :          0 :           indexPath.append( idx );
    4682                 :          0 :           const QgsSymbolLayer *sl = symbol->symbolLayer( idx );
    4683                 :          0 :           if ( mSymbolLayerIds.contains( QgsSymbolLayerId( mCurrentRuleKey + identifier, indexPath ) ) )
    4684                 :            :           {
    4685                 :          0 :             mSymbolLayers.insert( sl );
    4686                 :          0 :           }
    4687                 :            : 
    4688                 :          0 :           const QgsSymbol *subSymbol = const_cast<QgsSymbolLayer *>( sl )->subSymbol();
    4689                 :          0 :           if ( subSymbol )
    4690                 :          0 :             visitSymbol( subSymbol, identifier, indexPath );
    4691                 :          0 :         }
    4692                 :          0 :       }
    4693                 :            : 
    4694                 :          0 :       bool visit( const QgsStyleEntityVisitorInterface::StyleLeaf &leaf ) override
    4695                 :            :       {
    4696                 :          0 :         if ( leaf.entity && leaf.entity->type() == QgsStyle::SymbolEntity )
    4697                 :            :         {
    4698                 :          0 :           auto symbolEntity = static_cast<const QgsStyleSymbolEntity *>( leaf.entity );
    4699                 :          0 :           if ( symbolEntity->symbol() )
    4700                 :            :           {
    4701                 :          0 :             visitSymbol( symbolEntity->symbol(), leaf.identifier, {} );
    4702                 :          0 :           }
    4703                 :          0 :         }
    4704                 :          0 :         return true;
    4705                 :          0 :       }
    4706                 :            : 
    4707                 :            :       QString mCurrentRuleKey;
    4708                 :            :       const QSet<QgsSymbolLayerId> &mSymbolLayerIds;
    4709                 :            :       QSet<const QgsSymbolLayer *> mSymbolLayers;
    4710                 :            :   };
    4711                 :            : 
    4712                 :          0 :   SymbolLayerVisitor visitor( symbolLayerIds );
    4713                 :          0 :   renderer->accept( &visitor );
    4714                 :          0 :   return visitor.mSymbolLayers;
    4715                 :          0 : }
    4716                 :            : 
    4717                 :          0 : QgsSymbol *QgsSymbolLayerUtils::restrictedSizeSymbol( const QgsSymbol *s, double minSize, double maxSize, QgsRenderContext *context, double &width, double &height )
    4718                 :            : {
    4719                 :          0 :   if ( !s || !context )
    4720                 :            :   {
    4721                 :          0 :     return 0;
    4722                 :            :   }
    4723                 :            : 
    4724                 :            :   double size;
    4725                 :          0 :   const QgsMarkerSymbol *markerSymbol = dynamic_cast<const QgsMarkerSymbol *>( s );
    4726                 :          0 :   const QgsLineSymbol *lineSymbol = dynamic_cast<const QgsLineSymbol *>( s );
    4727                 :          0 :   if ( markerSymbol )
    4728                 :            :   {
    4729                 :          0 :     size = markerSymbol->size( *context );
    4730                 :          0 :   }
    4731                 :          0 :   else if ( lineSymbol )
    4732                 :            :   {
    4733                 :          0 :     size = lineSymbol->width( *context );
    4734                 :          0 :   }
    4735                 :            :   else
    4736                 :            :   {
    4737                 :          0 :     return 0; //not size restriction implemented for other symbol types
    4738                 :            :   }
    4739                 :            : 
    4740                 :          0 :   size /= context->scaleFactor();
    4741                 :            : 
    4742                 :          0 :   if ( minSize > 0 && size < minSize )
    4743                 :            :   {
    4744                 :          0 :     size = minSize;
    4745                 :          0 :   }
    4746                 :          0 :   else if ( maxSize > 0 && size > maxSize )
    4747                 :            :   {
    4748                 :          0 :     size = maxSize;
    4749                 :          0 :   }
    4750                 :            :   else
    4751                 :            :   {
    4752                 :          0 :     return 0;
    4753                 :            :   }
    4754                 :            : 
    4755                 :          0 :   if ( markerSymbol )
    4756                 :            :   {
    4757                 :          0 :     QgsMarkerSymbol *ms = dynamic_cast<QgsMarkerSymbol *>( s->clone() );
    4758                 :          0 :     ms->setSize( size );
    4759                 :          0 :     ms->setSizeUnit( QgsUnitTypes::RenderMillimeters );
    4760                 :          0 :     width = size;
    4761                 :          0 :     height = size;
    4762                 :          0 :     return ms;
    4763                 :            :   }
    4764                 :          0 :   else if ( lineSymbol )
    4765                 :            :   {
    4766                 :          0 :     QgsLineSymbol *ls = dynamic_cast<QgsLineSymbol *>( s->clone() );
    4767                 :          0 :     ls->setWidth( size );
    4768                 :          0 :     ls->setWidthUnit( QgsUnitTypes::RenderMillimeters );
    4769                 :          0 :     height = size;
    4770                 :          0 :     return ls;
    4771                 :            :   }
    4772                 :          0 :   return 0;
    4773                 :          0 : }
    4774                 :            : 
    4775                 :          0 : QgsStringMap QgsSymbolLayerUtils::evaluatePropertiesMap( const QMap<QString, QgsProperty> &propertiesMap, const QgsExpressionContext &context )
    4776                 :            : {
    4777                 :          0 :   QgsStringMap properties;
    4778                 :          0 :   QMap<QString, QgsProperty>::const_iterator paramIt = propertiesMap.constBegin();
    4779                 :          0 :   for ( ; paramIt != propertiesMap.constEnd(); ++paramIt )
    4780                 :            :   {
    4781                 :          0 :     properties.insert( paramIt.key(), paramIt.value().valueAsString( context ) );
    4782                 :          0 :   }
    4783                 :          0 :   return properties;
    4784                 :          0 : }
    4785                 :            : 

Generated by: LCOV version 1.14