QGIS API Documentation  2.18.21-Las Palmas (9fba24a)
qgspallabeling.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgspallabeling.cpp
3  Smart labeling for vector layers
4  -------------------
5  begin : June 2009
6  copyright : (C) Martin Dobias
7  email : wonder dot sk at gmail dot com
8 
9  ***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "qgspallabeling.h"
19 #include "qgstextlabelfeature.h"
20 #include "qgsunittypes.h"
21 
22 #include <list>
23 
24 #include <pal/pal.h>
25 #include <pal/feature.h>
26 #include <pal/layer.h>
27 #include <pal/palexception.h>
28 #include <pal/problem.h>
29 #include <pal/labelposition.h>
30 
31 #include <cmath>
32 
33 #include <QApplication>
34 #include <QByteArray>
35 #include <QString>
36 #include <QFontMetrics>
37 #include <QTime>
38 #include <QPainter>
39 
40 #include "diagram/qgsdiagram.h"
41 #include "qgsdiagramrendererv2.h"
42 #include "qgsfontutils.h"
43 #include "qgslabelsearchtree.h"
44 #include "qgsexpression.h"
45 #include "qgsdatadefined.h"
46 #include "qgslabelingenginev2.h"
47 #include "qgsvectorlayerlabeling.h"
48 
49 #include <qgslogger.h>
50 #include <qgsvectorlayer.h>
51 #include <qgsmaplayerregistry.h>
52 #include <qgsvectordataprovider.h>
55 #include <qgsgeometry.h>
56 #include <qgsmaprenderer.h>
57 #include <qgsmarkersymbollayerv2.h>
58 #include <qgsproject.h>
59 #include "qgssymbolv2.h"
60 #include "qgssymbollayerv2utils.h"
62 #include <QMessageBox>
63 
64 
65 Q_GUI_EXPORT extern int qt_defaultDpiX();
66 Q_GUI_EXPORT extern int qt_defaultDpiY();
67 
68 static void _fixQPictureDPI( QPainter* p )
69 {
70  // QPicture makes an assumption that we drawing to it with system DPI.
71  // Then when being drawn, it scales the painter. The following call
72  // negates the effect. There is no way of setting QPicture's DPI.
73  // See QTBUG-20361
74  p->scale( static_cast< double >( qt_defaultDpiX() ) / p->device()->logicalDpiX(),
75  static_cast< double >( qt_defaultDpiY() ) / p->device()->logicalDpiY() );
76 }
77 
78 
79 using namespace pal;
80 
81 // -------------
82 
83 /* ND: Default point label position priority. These are set to match variants of the ideal placement priority described
84  in "Making Maps", Krygier & Wood (2011) (p216),
85  "Elements of Cartography", Robinson et al (1995)
86  and "Designing Better Maps", Brewer (2005) (p76)
87  Note that while they agree on positions 1-4, 5-8 are more contentious so I've selected these placements
88  based on my preferences, and to follow Krygier and Wood's placements more closer. (I'm not going to disagree
89  with Denis Wood on anything cartography related...!)
90 */
100 //debugging only - don't use these placements by default
101 /* << QgsPalLayerSettings::TopSlightlyLeft
102 << QgsPalLayerSettings::BottomSlightlyLeft;
103 << QgsPalLayerSettings::TopMiddle
104 << QgsPalLayerSettings::BottomMiddle;*/
105 
107  : upsidedownLabels( Upright )
108  , mCurFeat( nullptr )
109  , xform( nullptr )
110  , ct( nullptr )
111  , extentGeom( nullptr )
112  , mFeaturesToLabel( 0 )
113  , mFeatsSendingToPal( 0 )
114  , mFeatsRegPal( 0 )
115  , expression( nullptr )
116 {
117  enabled = false;
118  drawLabels = true;
119  isExpression = false;
120  fieldIndex = 0;
121 
122  // text style
124  fontSizeInMapUnits = false;
125  textColor = Qt::black;
126  textTransp = 0;
127  blendMode = QPainter::CompositionMode_SourceOver;
128  previewBkgrdColor = Qt::white;
129  // font processing info
130  mTextFontFound = true;
132  useSubstitutions = false;
133 
134  // text formatting
135  wrapChar = "";
136  multilineHeight = 1.0;
138  addDirectionSymbol = false;
139  leftDirectionSymbol = QString( "<" );
140  rightDirectionSymbol = QString( ">" );
141  reverseDirectionSymbol = false;
143  formatNumbers = false;
144  decimals = 3;
145  plusSign = false;
146 
147  // text buffer
148  bufferDraw = false;
149  bufferSize = 1.0;
150  bufferSizeInMapUnits = false;
151  bufferColor = Qt::white;
152  bufferTransp = 0;
153  bufferNoFill = false;
154  bufferJoinStyle = Qt::RoundJoin;
155  bufferBlendMode = QPainter::CompositionMode_SourceOver;
156 
157  // shape background
158  shapeDraw = false;
160  shapeSVGFile = QString();
162  shapeSize = QPointF( 0.0, 0.0 );
163  shapeSizeUnits = MM;
165  shapeRotation = 0.0;
166  shapeOffset = QPointF( 0.0, 0.0 );
168  shapeRadii = QPointF( 0.0, 0.0 );
170  shapeFillColor = Qt::white;
171  shapeBorderColor = Qt::darkGray;
172  shapeBorderWidth = 0.0;
174  shapeJoinStyle = Qt::BevelJoin;
175  shapeTransparency = 0;
176  shapeBlendMode = QPainter::CompositionMode_SourceOver;
177 
178  // drop shadow
179  shadowDraw = false;
181  shadowOffsetAngle = 135;
182  shadowOffsetDist = 1.0;
184  shadowOffsetGlobal = true;
185  shadowRadius = 1.5;
187  shadowRadiusAlphaOnly = false;
188  shadowTransparency = 30;
189  shadowScale = 100;
190  shadowColor = Qt::black;
191  shadowBlendMode = QPainter::CompositionMode_Multiply;
192 
193  // placement
196  centroidWhole = false;
197  centroidInside = false;
198  predefinedPositionOrder = DEFAULT_PLACEMENT_ORDER;
199  fitInPolygonOnly = false;
201  xOffset = 0;
202  yOffset = 0;
203  labelOffsetInMapUnits = true;
204  dist = 0;
205  distInMapUnits = false;
207  angleOffset = 0;
208  preserveRotation = true;
209  maxCurvedCharAngleIn = 25.0;
210  maxCurvedCharAngleOut = -25.0;
211  priority = 5;
212  repeatDistance = 0;
214 
215  // rendering
216  scaleVisibility = false;
217  scaleMin = 1;
218  scaleMax = 10000000;
219  fontLimitPixelSize = false;
220  fontMinPixelSize = 0; //trigger to turn it on by default for map unit labels
221  fontMaxPixelSize = 10000;
222  displayAll = false;
224 
225  labelPerPart = false;
226  mergeLines = false;
227  minFeatureSize = 0.0;
228  limitNumLabels = false;
229  maxNumLabels = 2000;
230  obstacle = true;
231  obstacleFactor = 1.0;
233  zIndex = 0.0;
234 
235  // scale factors
236  vectorScaleFactor = 1.0;
237  rasterCompressFactor = 1.0;
238 
239  // data defined string and old-style index values
240  // NOTE: in QPair use -1 for second value (other values are for old-style layer properties migration)
241 
242  // text style
243  mDataDefinedNames.insert( Size, QPair<QString, int>( "Size", 0 ) );
244  mDataDefinedNames.insert( Bold, QPair<QString, int>( "Bold", 1 ) );
245  mDataDefinedNames.insert( Italic, QPair<QString, int>( "Italic", 2 ) );
246  mDataDefinedNames.insert( Underline, QPair<QString, int>( "Underline", 3 ) );
247  mDataDefinedNames.insert( Color, QPair<QString, int>( "Color", 4 ) );
248  mDataDefinedNames.insert( Strikeout, QPair<QString, int>( "Strikeout", 5 ) );
249  mDataDefinedNames.insert( Family, QPair<QString, int>( "Family", 6 ) );
250  mDataDefinedNames.insert( FontStyle, QPair<QString, int>( "FontStyle", -1 ) );
251  mDataDefinedNames.insert( FontSizeUnit, QPair<QString, int>( "FontSizeUnit", -1 ) );
252  mDataDefinedNames.insert( FontTransp, QPair<QString, int>( "FontTransp", 18 ) );
253  mDataDefinedNames.insert( FontCase, QPair<QString, int>( "FontCase", -1 ) );
254  mDataDefinedNames.insert( FontLetterSpacing, QPair<QString, int>( "FontLetterSpacing", -1 ) );
255  mDataDefinedNames.insert( FontWordSpacing, QPair<QString, int>( "FontWordSpacing", -1 ) );
256  mDataDefinedNames.insert( FontBlendMode, QPair<QString, int>( "FontBlendMode", -1 ) );
257 
258  // text formatting
259  mDataDefinedNames.insert( MultiLineWrapChar, QPair<QString, int>( "MultiLineWrapChar", -1 ) );
260  mDataDefinedNames.insert( MultiLineHeight, QPair<QString, int>( "MultiLineHeight", -1 ) );
261  mDataDefinedNames.insert( MultiLineAlignment, QPair<QString, int>( "MultiLineAlignment", -1 ) );
262  mDataDefinedNames.insert( DirSymbDraw, QPair<QString, int>( "DirSymbDraw", -1 ) );
263  mDataDefinedNames.insert( DirSymbLeft, QPair<QString, int>( "DirSymbLeft", -1 ) );
264  mDataDefinedNames.insert( DirSymbRight, QPair<QString, int>( "DirSymbRight", -1 ) );
265  mDataDefinedNames.insert( DirSymbPlacement, QPair<QString, int>( "DirSymbPlacement", -1 ) );
266  mDataDefinedNames.insert( DirSymbReverse, QPair<QString, int>( "DirSymbReverse", -1 ) );
267  mDataDefinedNames.insert( NumFormat, QPair<QString, int>( "NumFormat", -1 ) );
268  mDataDefinedNames.insert( NumDecimals, QPair<QString, int>( "NumDecimals", -1 ) );
269  mDataDefinedNames.insert( NumPlusSign, QPair<QString, int>( "NumPlusSign", -1 ) );
270 
271  // text buffer
272  mDataDefinedNames.insert( BufferDraw, QPair<QString, int>( "BufferDraw", -1 ) );
273  mDataDefinedNames.insert( BufferSize, QPair<QString, int>( "BufferSize", 7 ) );
274  mDataDefinedNames.insert( BufferUnit, QPair<QString, int>( "BufferUnit", -1 ) );
275  mDataDefinedNames.insert( BufferColor, QPair<QString, int>( "BufferColor", 8 ) );
276  mDataDefinedNames.insert( BufferTransp, QPair<QString, int>( "BufferTransp", 19 ) );
277  mDataDefinedNames.insert( BufferJoinStyle, QPair<QString, int>( "BufferJoinStyle", -1 ) );
278  mDataDefinedNames.insert( BufferBlendMode, QPair<QString, int>( "BufferBlendMode", -1 ) );
279 
280  // background
281  mDataDefinedNames.insert( ShapeDraw, QPair<QString, int>( "ShapeDraw", -1 ) );
282  mDataDefinedNames.insert( ShapeKind, QPair<QString, int>( "ShapeKind", -1 ) );
283  mDataDefinedNames.insert( ShapeSVGFile, QPair<QString, int>( "ShapeSVGFile", -1 ) );
284  mDataDefinedNames.insert( ShapeSizeType, QPair<QString, int>( "ShapeSizeType", -1 ) );
285  mDataDefinedNames.insert( ShapeSizeX, QPair<QString, int>( "ShapeSizeX", -1 ) );
286  mDataDefinedNames.insert( ShapeSizeY, QPair<QString, int>( "ShapeSizeY", -1 ) );
287  mDataDefinedNames.insert( ShapeSizeUnits, QPair<QString, int>( "ShapeSizeUnits", -1 ) );
288  mDataDefinedNames.insert( ShapeRotationType, QPair<QString, int>( "ShapeRotationType", -1 ) );
289  mDataDefinedNames.insert( ShapeRotation, QPair<QString, int>( "ShapeRotation", -1 ) );
290  mDataDefinedNames.insert( ShapeOffset, QPair<QString, int>( "ShapeOffset", -1 ) );
291  mDataDefinedNames.insert( ShapeOffsetUnits, QPair<QString, int>( "ShapeOffsetUnits", -1 ) );
292  mDataDefinedNames.insert( ShapeRadii, QPair<QString, int>( "ShapeRadii", -1 ) );
293  mDataDefinedNames.insert( ShapeRadiiUnits, QPair<QString, int>( "ShapeRadiiUnits", -1 ) );
294  mDataDefinedNames.insert( ShapeTransparency, QPair<QString, int>( "ShapeTransparency", -1 ) );
295  mDataDefinedNames.insert( ShapeBlendMode, QPair<QString, int>( "ShapeBlendMode", -1 ) );
296  mDataDefinedNames.insert( ShapeFillColor, QPair<QString, int>( "ShapeFillColor", -1 ) );
297  mDataDefinedNames.insert( ShapeBorderColor, QPair<QString, int>( "ShapeBorderColor", -1 ) );
298  mDataDefinedNames.insert( ShapeBorderWidth, QPair<QString, int>( "ShapeBorderWidth", -1 ) );
299  mDataDefinedNames.insert( ShapeBorderWidthUnits, QPair<QString, int>( "ShapeBorderWidthUnits", -1 ) );
300  mDataDefinedNames.insert( ShapeJoinStyle, QPair<QString, int>( "ShapeJoinStyle", -1 ) );
301 
302  // drop shadow
303  mDataDefinedNames.insert( ShadowDraw, QPair<QString, int>( "ShadowDraw", -1 ) );
304  mDataDefinedNames.insert( ShadowUnder, QPair<QString, int>( "ShadowUnder", -1 ) );
305  mDataDefinedNames.insert( ShadowOffsetAngle, QPair<QString, int>( "ShadowOffsetAngle", -1 ) );
306  mDataDefinedNames.insert( ShadowOffsetDist, QPair<QString, int>( "ShadowOffsetDist", -1 ) );
307  mDataDefinedNames.insert( ShadowOffsetUnits, QPair<QString, int>( "ShadowOffsetUnits", -1 ) );
308  mDataDefinedNames.insert( ShadowRadius, QPair<QString, int>( "ShadowRadius", -1 ) );
309  mDataDefinedNames.insert( ShadowRadiusUnits, QPair<QString, int>( "ShadowRadiusUnits", -1 ) );
310  mDataDefinedNames.insert( ShadowTransparency, QPair<QString, int>( "ShadowTransparency", -1 ) );
311  mDataDefinedNames.insert( ShadowScale, QPair<QString, int>( "ShadowScale", -1 ) );
312  mDataDefinedNames.insert( ShadowColor, QPair<QString, int>( "ShadowColor", -1 ) );
313  mDataDefinedNames.insert( ShadowBlendMode, QPair<QString, int>( "ShadowBlendMode", -1 ) );
314 
315  // placement
316  mDataDefinedNames.insert( CentroidWhole, QPair<QString, int>( "CentroidWhole", -1 ) );
317  mDataDefinedNames.insert( OffsetQuad, QPair<QString, int>( "OffsetQuad", -1 ) );
318  mDataDefinedNames.insert( OffsetXY, QPair<QString, int>( "OffsetXY", -1 ) );
319  mDataDefinedNames.insert( OffsetUnits, QPair<QString, int>( "OffsetUnits", -1 ) );
320  mDataDefinedNames.insert( LabelDistance, QPair<QString, int>( "LabelDistance", 13 ) );
321  mDataDefinedNames.insert( DistanceUnits, QPair<QString, int>( "DistanceUnits", -1 ) );
322  mDataDefinedNames.insert( OffsetRotation, QPair<QString, int>( "OffsetRotation", -1 ) );
323  mDataDefinedNames.insert( CurvedCharAngleInOut, QPair<QString, int>( "CurvedCharAngleInOut", -1 ) );
324  mDataDefinedNames.insert( RepeatDistance, QPair<QString, int>( "RepeatDistance", -1 ) );
325  mDataDefinedNames.insert( RepeatDistanceUnit, QPair<QString, int>( "RepeatDistanceUnit", -1 ) );
326  mDataDefinedNames.insert( Priority, QPair<QString, int>( "Priority", -1 ) );
327  mDataDefinedNames.insert( IsObstacle, QPair<QString, int>( "IsObstacle", -1 ) );
328  mDataDefinedNames.insert( ObstacleFactor, QPair<QString, int>( "ObstacleFactor", -1 ) );
329  mDataDefinedNames.insert( PredefinedPositionOrder, QPair<QString, int>( "PredefinedPositionOrder", -1 ) );
330 
331  // (data defined only)
332  mDataDefinedNames.insert( PositionX, QPair<QString, int>( "PositionX", 9 ) );
333  mDataDefinedNames.insert( PositionY, QPair<QString, int>( "PositionY", 10 ) );
334  mDataDefinedNames.insert( Hali, QPair<QString, int>( "Hali", 11 ) );
335  mDataDefinedNames.insert( Vali, QPair<QString, int>( "Vali", 12 ) );
336  mDataDefinedNames.insert( Rotation, QPair<QString, int>( "Rotation", 14 ) );
337 
338  //rendering
339  mDataDefinedNames.insert( ScaleVisibility, QPair<QString, int>( "ScaleVisibility", -1 ) );
340  mDataDefinedNames.insert( MinScale, QPair<QString, int>( "MinScale", 16 ) );
341  mDataDefinedNames.insert( MaxScale, QPair<QString, int>( "MaxScale", 17 ) );
342  mDataDefinedNames.insert( FontLimitPixel, QPair<QString, int>( "FontLimitPixel", -1 ) );
343  mDataDefinedNames.insert( FontMinPixel, QPair<QString, int>( "FontMinPixel", -1 ) );
344  mDataDefinedNames.insert( FontMaxPixel, QPair<QString, int>( "FontMaxPixel", -1 ) );
345  mDataDefinedNames.insert( ZIndex, QPair<QString, int>( "ZIndex", -1 ) );
346  // (data defined only)
347  mDataDefinedNames.insert( Show, QPair<QString, int>( "Show", 15 ) );
348  mDataDefinedNames.insert( AlwaysShow, QPair<QString, int>( "AlwaysShow", 20 ) );
349 
350  // temp stuff for when drawing label components (don't copy)
351  showingShadowRects = false;
352 }
353 
355  : mCurFeat( nullptr )
356  , fieldIndex( 0 )
357  , xform( nullptr )
358  , ct( nullptr )
359  , extentGeom( nullptr )
360  , mFeaturesToLabel( 0 )
361  , mFeatsSendingToPal( 0 )
362  , mFeatsRegPal( 0 )
363  , showingShadowRects( false )
364  , expression( nullptr )
365 {
366  *this = s;
367 }
368 
370 {
371  if ( this == &s )
372  return *this;
373 
374  // copy only permanent stuff
375 
376  enabled = s.enabled;
378 
379  // text style
380  fieldName = s.fieldName;
382  textFont = s.textFont;
386  textColor = s.textColor;
388  blendMode = s.blendMode;
390  // font processing info
395 
396  // text formatting
397  wrapChar = s.wrapChar;
406  decimals = s.decimals;
407  plusSign = s.plusSign;
408 
409  // text buffer
419 
420  // placement
421  placement = s.placement;
428  xOffset = s.xOffset;
429  yOffset = s.yOffset;
432  dist = s.dist;
440  priority = s.priority;
444 
445  // rendering
447  scaleMin = s.scaleMin;
448  scaleMax = s.scaleMax;
454 
460  obstacle = s.obstacle;
463  zIndex = s.zIndex;
464 
465  // shape background
466  shapeDraw = s.shapeDraw;
467  shapeType = s.shapeType;
470  shapeSize = s.shapeSize;
489 
490  // drop shadow
506 
507  // data defined
510  for ( ; it != s.dataDefinedProperties.constEnd(); ++it )
511  {
512  dataDefinedProperties.insert( it.key(), it.value() ? new QgsDataDefined( *it.value() ) : nullptr );
513  }
514  mDataDefinedNames = s.mDataDefinedNames;
515 
516  // scale factors
519  return *this;
520 }
521 
522 
524 {
525  // pal layer is deleted internally in PAL
526 
527  delete ct;
528  delete expression;
529  delete extentGeom;
530 
531  // delete all QgsDataDefined objects (which also deletes their QgsExpression object)
533 }
534 
535 
537 {
538  QgsPalLayerSettings settings;
539  settings.readFromLayer( layer );
540  return settings;
541 }
542 
543 
545 {
546  if ( !expression )
547  {
548  expression = new QgsExpression( fieldName );
549  }
550  return expression;
551 }
552 
553 static QColor _readColor( QgsVectorLayer* layer, const QString& property, const QColor& defaultColor = Qt::black, bool withAlpha = true )
554 {
555  int r = layer->customProperty( property + 'R', QVariant( defaultColor.red() ) ).toInt();
556  int g = layer->customProperty( property + 'G', QVariant( defaultColor.green() ) ).toInt();
557  int b = layer->customProperty( property + 'B', QVariant( defaultColor.blue() ) ).toInt();
558  int a = withAlpha ? layer->customProperty( property + 'A', QVariant( defaultColor.alpha() ) ).toInt() : 255;
559  return QColor( r, g, b, a );
560 }
561 
562 static void _writeColor( QgsVectorLayer* layer, const QString& property, const QColor& color, bool withAlpha = true )
563 {
564  layer->setCustomProperty( property + 'R', color.red() );
565  layer->setCustomProperty( property + 'G', color.green() );
566  layer->setCustomProperty( property + 'B', color.blue() );
567  if ( withAlpha )
568  layer->setCustomProperty( property + 'A', color.alpha() );
569 }
570 
572 {
573  if ( str.compare( "Point", Qt::CaseInsensitive ) == 0
574  || str.compare( "Points", Qt::CaseInsensitive ) == 0 ) return QgsPalLayerSettings::Points;
575  if ( str.compare( "MapUnit", Qt::CaseInsensitive ) == 0
576  || str.compare( "MapUnits", Qt::CaseInsensitive ) == 0 ) return QgsPalLayerSettings::MapUnits;
577  if ( str.compare( "Percent", Qt::CaseInsensitive ) == 0 ) return QgsPalLayerSettings::Percent;
578  return QgsPalLayerSettings::MM; // "MM"
579 }
580 
581 static Qt::PenJoinStyle _decodePenJoinStyle( const QString& str )
582 {
583  if ( str.compare( "Miter", Qt::CaseInsensitive ) == 0 ) return Qt::MiterJoin;
584  if ( str.compare( "Round", Qt::CaseInsensitive ) == 0 ) return Qt::RoundJoin;
585  return Qt::BevelJoin; // "Bevel"
586 }
587 
588 void QgsPalLayerSettings::readDataDefinedPropertyMap( QgsVectorLayer* layer, QDomElement* parentElem,
590 {
591  if ( !layer && !parentElem )
592  {
593  return;
594  }
595 
597  while ( i.hasNext() )
598  {
599  i.next();
600  if ( layer )
601  {
602  // reading from layer's custom properties (old way)
603  readDataDefinedProperty( layer, i.key(), propertyMap );
604  }
605  else if ( parentElem )
606  {
607  // reading from XML (new way)
608  QDomElement e = parentElem->firstChildElement( i.value().first );
609  if ( !e.isNull() )
610  {
611  QgsDataDefined* dd = new QgsDataDefined();
612  if ( dd->setFromXmlElement( e ) )
613  propertyMap.insert( i.key(), dd );
614  else
615  delete dd;
616  }
617  }
618  }
619 }
620 
621 void QgsPalLayerSettings::writeDataDefinedPropertyMap( QgsVectorLayer* layer, QDomElement* parentElem,
623 {
624  if ( !layer && !parentElem )
625  {
626  return;
627  }
628 
630  while ( i.hasNext() )
631  {
632  i.next();
633  QString newPropertyName = "labeling/dataDefined/" + i.value().first;
634  QVariant propertyValue = QVariant();
635 
637  if ( it != propertyMap.constEnd() )
638  {
639  QgsDataDefined* dd = it.value();
640  if ( dd )
641  {
642  bool active = dd->isActive();
643  bool useExpr = dd->useExpression();
644  QString expr = dd->expressionString();
645  QString field = dd->field();
646 
647  bool defaultVals = ( !active && !useExpr && expr.isEmpty() && field.isEmpty() );
648 
649  if ( !defaultVals )
650  {
651  // TODO: update this when project settings for labeling are migrated to better XML layout
652  QStringList values;
653  values << ( active ? "1" : "0" );
654  values << ( useExpr ? "1" : "0" );
655  values << expr;
656  values << field;
657  if ( !values.isEmpty() )
658  {
659  propertyValue = QVariant( values.join( "~~" ) );
660  }
661  }
662 
663  if ( parentElem )
664  {
665  // writing to XML document (instead of writing to layer)
666  QDomDocument doc = parentElem->ownerDocument();
667  QDomElement e = dd->toXmlElement( doc, i.value().first );
668  parentElem->appendChild( e );
669  }
670  }
671  }
672 
673  if ( layer )
674  {
675  // writing to layer's custom properties (old method)
676 
677  if ( propertyValue.isValid() )
678  {
679  layer->setCustomProperty( newPropertyName, propertyValue );
680  }
681  else
682  {
683  // remove unused properties
684  layer->removeCustomProperty( newPropertyName );
685  }
686 
687  if ( layer->customProperty( newPropertyName, QVariant() ).isValid() && i.value().second > -1 )
688  {
689  // remove old-style field index-based property, if still present
690  layer->removeCustomProperty( QString( "labeling/dataDefinedProperty" ) + QString::number( i.value().second ) );
691  }
692  }
693  }
694 }
695 
696 void QgsPalLayerSettings::readDataDefinedProperty( QgsVectorLayer* layer,
699 {
700  QString newPropertyName = "labeling/dataDefined/" + mDataDefinedNames.value( p ).first;
701  QVariant newPropertyField = layer->customProperty( newPropertyName, QVariant() );
702 
703  QString ddString = QString();
704  if ( newPropertyField.isValid() )
705  {
706  ddString = newPropertyField.toString();
707  }
708  else // maybe working with old-style field index-based property (< QGIS 2.0)
709  {
710  int oldIndx = mDataDefinedNames.value( p ).second;
711 
712  if ( oldIndx < 0 ) // something went wrong and we are working with new-style
713  {
714  return;
715  }
716 
717  QString oldPropertyName = "labeling/dataDefinedProperty" + QString::number( oldIndx );
718  QVariant oldPropertyField = layer->customProperty( oldPropertyName, QVariant() );
719 
720  if ( !oldPropertyField.isValid() )
721  {
722  return;
723  }
724 
725  // switch from old-style field index- to name-based properties
726  bool conversionOk;
727  int indx = oldPropertyField.toInt( &conversionOk );
728 
729  if ( conversionOk )
730  {
731  // Fix to migrate from old-style vector api, where returned QMap keys possibly
732  // had 'holes' in sequence of field indices, e.g. 0,2,3
733  // QgsAttrPalIndexNameHash provides a means of access field name in sequences from
734  // providers that procuded holes (e.g. PostGIS skipped geom column), otherwise it is empty
735  QgsAttrPalIndexNameHash oldIndicesToNames = layer->dataProvider()->palAttributeIndexNames();
736 
737  if ( !oldIndicesToNames.isEmpty() )
738  {
739  ddString = oldIndicesToNames.value( indx );
740  }
741  else
742  {
743  QgsFields fields = layer->dataProvider()->fields();
744  if ( indx < fields.size() ) // in case field count has changed
745  {
746  ddString = fields.at( indx ).name();
747  }
748  }
749  }
750 
751  if ( !ddString.isEmpty() )
752  {
753  //upgrade any existing property to field name-based
754  layer->setCustomProperty( newPropertyName, QVariant( updateDataDefinedString( ddString ) ) );
755 
756  // fix for buffer drawing triggered off of just its data defined size in the past (<2.0)
757  if ( oldIndx == 7 ) // old bufferSize enum
758  {
759  bufferDraw = true;
760  layer->setCustomProperty( "labeling/bufferDraw", true );
761  }
762 
763  // fix for scale visibility limits triggered off of just its data defined values in the past (<2.0)
764  if ( oldIndx == 16 || oldIndx == 17 ) // old minScale and maxScale enums
765  {
766  scaleVisibility = true;
767  layer->setCustomProperty( "labeling/scaleVisibility", true );
768  }
769  }
770 
771  // remove old-style field index-based property
772  layer->removeCustomProperty( oldPropertyName );
773  }
774 
775  if ( !ddString.isEmpty() && ddString != QLatin1String( "0~~0~~~~" ) )
776  {
777  // TODO: update this when project settings for labeling are migrated to better XML layout
778  QString newStyleString = updateDataDefinedString( ddString );
779  QStringList ddv = newStyleString.split( "~~" );
780 
781  QgsDataDefined* dd = new QgsDataDefined( ddv.at( 0 ).toInt(), ddv.at( 1 ).toInt(), ddv.at( 2 ), ddv.at( 3 ) );
782  propertyMap.insert( p, dd );
783  }
784  else
785  {
786  // remove unused properties
787  layer->removeCustomProperty( newPropertyName );
788  }
789 }
790 
792 {
793  if ( layer->customProperty( "labeling" ).toString() != QLatin1String( "pal" ) )
794  {
795  if ( layer->geometryType() == QGis::Point )
797 
798  // for polygons the "over point" (over centroid) placement is better than the default
799  // "around point" (around centroid) which is more suitable for points
800  if ( layer->geometryType() == QGis::Polygon )
802 
803  return; // there's no information available
804  }
805 
806  // NOTE: set defaults for newly added properties, for backwards compatibility
807 
808  enabled = layer->labelsEnabled();
809  drawLabels = layer->customProperty( "labeling/drawLabels", true ).toBool();
810 
811  // text style
812  fieldName = layer->customProperty( "labeling/fieldName" ).toString();
813  isExpression = layer->customProperty( "labeling/isExpression" ).toBool();
814  QFont appFont = QApplication::font();
815  mTextFontFamily = layer->customProperty( "labeling/fontFamily", QVariant( appFont.family() ) ).toString();
816  QString fontFamily = mTextFontFamily;
818  {
819  // trigger to notify user about font family substitution (signal emitted in QgsVectorLayer::prepareLabelingAndDiagrams)
820  mTextFontFound = false;
821 
822  // TODO: update when pref for how to resolve missing family (use matching algorithm or just default font) is implemented
823  // currently only defaults to matching algorithm for resolving [foundry], if a font of similar family is found (default for QFont)
824 
825  // for now, do not use matching algorithm for substitution if family not found, substitute default instead
826  fontFamily = appFont.family();
827  }
828 
829  double fontSize = layer->customProperty( "labeling/fontSize" ).toDouble();
830  fontSizeInMapUnits = layer->customProperty( "labeling/fontSizeInMapUnits" ).toBool();
831  if ( layer->customProperty( "labeling/fontSizeMapUnitScale" ).toString().isEmpty() )
832  {
833  //fallback to older property
834  fontSizeMapUnitScale.minScale = layer->customProperty( "labeling/fontSizeMapUnitMinScale", 0.0 ).toDouble();
835  fontSizeMapUnitScale.maxScale = layer->customProperty( "labeling/fontSizeMapUnitMaxScale", 0.0 ).toDouble();
836  }
837  else
838  {
839  fontSizeMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( layer->customProperty( "labeling/fontSizeMapUnitScale" ).toString() );
840  }
841  int fontWeight = layer->customProperty( "labeling/fontWeight" ).toInt();
842  bool fontItalic = layer->customProperty( "labeling/fontItalic" ).toBool();
843  textFont = QFont( fontFamily, fontSize, fontWeight, fontItalic );
844  textFont.setPointSizeF( fontSize ); //double precision needed because of map units
845  textNamedStyle = QgsFontUtils::translateNamedStyle( layer->customProperty( "labeling/namedStyle", QVariant( "" ) ).toString() );
846  QgsFontUtils::updateFontViaStyle( textFont, textNamedStyle ); // must come after textFont.setPointSizeF()
847  textFont.setCapitalization( static_cast< QFont::Capitalization >( layer->customProperty( "labeling/fontCapitals", QVariant( 0 ) ).toUInt() ) );
848  textFont.setUnderline( layer->customProperty( "labeling/fontUnderline" ).toBool() );
849  textFont.setStrikeOut( layer->customProperty( "labeling/fontStrikeout" ).toBool() );
850  textFont.setLetterSpacing( QFont::AbsoluteSpacing, layer->customProperty( "labeling/fontLetterSpacing", QVariant( 0.0 ) ).toDouble() );
851  textFont.setWordSpacing( layer->customProperty( "labeling/fontWordSpacing", QVariant( 0.0 ) ).toDouble() );
852  textColor = _readColor( layer, "labeling/textColor", Qt::black, false );
853  textTransp = layer->customProperty( "labeling/textTransp" ).toInt();
855  static_cast< QgsMapRenderer::BlendMode >( layer->customProperty( "labeling/blendMode", QVariant( QgsMapRenderer::BlendNormal ) ).toUInt() ) );
856  previewBkgrdColor = QColor( layer->customProperty( "labeling/previewBkgrdColor", QVariant( "#ffffff" ) ).toString() );
857  QDomDocument doc( "substitutions" );
858  doc.setContent( layer->customProperty( "labeling/substitutions" ).toString() );
859  QDomElement replacementElem = doc.firstChildElement( "substitutions" );
860  substitutions.readXml( replacementElem );
861  useSubstitutions = layer->customProperty( "labeling/useSubstitutions" ).toBool();
862 
863  // text formatting
864  wrapChar = layer->customProperty( "labeling/wrapChar" ).toString();
865  multilineHeight = layer->customProperty( "labeling/multilineHeight", QVariant( 1.0 ) ).toDouble();
866  multilineAlign = static_cast< MultiLineAlign >( layer->customProperty( "labeling/multilineAlign", QVariant( MultiFollowPlacement ) ).toUInt() );
867  addDirectionSymbol = layer->customProperty( "labeling/addDirectionSymbol" ).toBool();
868  leftDirectionSymbol = layer->customProperty( "labeling/leftDirectionSymbol", QVariant( "<" ) ).toString();
869  rightDirectionSymbol = layer->customProperty( "labeling/rightDirectionSymbol", QVariant( ">" ) ).toString();
870  reverseDirectionSymbol = layer->customProperty( "labeling/reverseDirectionSymbol" ).toBool();
871  placeDirectionSymbol = static_cast< DirectionSymbols >( layer->customProperty( "labeling/placeDirectionSymbol", QVariant( SymbolLeftRight ) ).toUInt() );
872  formatNumbers = layer->customProperty( "labeling/formatNumbers" ).toBool();
873  decimals = layer->customProperty( "labeling/decimals" ).toInt();
874  plusSign = layer->customProperty( "labeling/plussign" ).toBool();
875 
876  // text buffer
877  double bufSize = layer->customProperty( "labeling/bufferSize", QVariant( 0.0 ) ).toDouble();
878 
879  // fix for buffer being keyed off of just its size in the past (<2.0)
880  QVariant drawBuffer = layer->customProperty( "labeling/bufferDraw", QVariant() );
881  if ( drawBuffer.isValid() )
882  {
883  bufferDraw = drawBuffer.toBool();
884  bufferSize = bufSize;
885  }
886  else if ( bufSize != 0.0 )
887  {
888  bufferDraw = true;
889  bufferSize = bufSize;
890  }
891  else
892  {
893  // keep bufferSize at new 1.0 default
894  bufferDraw = false;
895  }
896 
897  bufferSizeInMapUnits = layer->customProperty( "labeling/bufferSizeInMapUnits" ).toBool();
898  if ( layer->customProperty( "labeling/bufferSizeMapUnitScale" ).toString().isEmpty() )
899  {
900  //fallback to older property
901  bufferSizeMapUnitScale.minScale = layer->customProperty( "labeling/bufferSizeMapUnitMinScale", 0.0 ).toDouble();
902  bufferSizeMapUnitScale.maxScale = layer->customProperty( "labeling/bufferSizeMapUnitMaxScale", 0.0 ).toDouble();
903  }
904  else
905  {
906  bufferSizeMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( layer->customProperty( "labeling/bufferSizeMapUnitScale" ).toString() );
907  }
908  bufferColor = _readColor( layer, "labeling/bufferColor", Qt::white, false );
909  bufferTransp = layer->customProperty( "labeling/bufferTransp" ).toInt();
911  static_cast< QgsMapRenderer::BlendMode >( layer->customProperty( "labeling/bufferBlendMode", QVariant( QgsMapRenderer::BlendNormal ) ).toUInt() ) );
912  bufferJoinStyle = static_cast< Qt::PenJoinStyle >( layer->customProperty( "labeling/bufferJoinStyle", QVariant( Qt::RoundJoin ) ).toUInt() );
913  bufferNoFill = layer->customProperty( "labeling/bufferNoFill", QVariant( false ) ).toBool();
914 
915  // background
916  shapeDraw = layer->customProperty( "labeling/shapeDraw", QVariant( false ) ).toBool();
917  shapeType = static_cast< ShapeType >( layer->customProperty( "labeling/shapeType", QVariant( ShapeRectangle ) ).toUInt() );
918  shapeSVGFile = layer->customProperty( "labeling/shapeSVGFile", QVariant( "" ) ).toString();
919  shapeSizeType = static_cast< SizeType >( layer->customProperty( "labeling/shapeSizeType", QVariant( SizeBuffer ) ).toUInt() );
920  shapeSize = QPointF( layer->customProperty( "labeling/shapeSizeX", QVariant( 0.0 ) ).toDouble(),
921  layer->customProperty( "labeling/shapeSizeY", QVariant( 0.0 ) ).toDouble() );
922  shapeSizeUnits = static_cast< SizeUnit >( layer->customProperty( "labeling/shapeSizeUnits", QVariant( MM ) ).toUInt() );
923  if ( layer->customProperty( "labeling/shapeSizeMapUnitScale" ).toString().isEmpty() )
924  {
925  //fallback to older property
926  shapeSizeMapUnitScale.minScale = layer->customProperty( "labeling/shapeSizeMapUnitMinScale", 0.0 ).toDouble();
927  shapeSizeMapUnitScale.maxScale = layer->customProperty( "labeling/shapeSizeMapUnitMaxScale", 0.0 ).toDouble();
928  }
929  else
930  {
931  shapeSizeMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( layer->customProperty( "labeling/shapeSizeMapUnitScale" ).toString() );
932  }
933  shapeRotationType = static_cast< RotationType >( layer->customProperty( "labeling/shapeRotationType", QVariant( RotationSync ) ).toUInt() );
934  shapeRotation = layer->customProperty( "labeling/shapeRotation", QVariant( 0.0 ) ).toDouble();
935  shapeOffset = QPointF( layer->customProperty( "labeling/shapeOffsetX", QVariant( 0.0 ) ).toDouble(),
936  layer->customProperty( "labeling/shapeOffsetY", QVariant( 0.0 ) ).toDouble() );
937  shapeOffsetUnits = static_cast< SizeUnit >( layer->customProperty( "labeling/shapeOffsetUnits", QVariant( MM ) ).toUInt() );
938  if ( layer->customProperty( "labeling/shapeOffsetMapUnitScale" ).toString().isEmpty() )
939  {
940  //fallback to older property
941  shapeOffsetMapUnitScale.minScale = layer->customProperty( "labeling/shapeOffsetMapUnitMinScale", 0.0 ).toDouble();
942  shapeOffsetMapUnitScale.maxScale = layer->customProperty( "labeling/shapeOffsetMapUnitMaxScale", 0.0 ).toDouble();
943  }
944  else
945  {
946  shapeOffsetMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( layer->customProperty( "labeling/shapeOffsetMapUnitScale" ).toString() );
947  }
948  shapeRadii = QPointF( layer->customProperty( "labeling/shapeRadiiX", QVariant( 0.0 ) ).toDouble(),
949  layer->customProperty( "labeling/shapeRadiiY", QVariant( 0.0 ) ).toDouble() );
950  shapeRadiiUnits = static_cast< SizeUnit >( layer->customProperty( "labeling/shapeRadiiUnits", QVariant( MM ) ).toUInt() );
951  if ( layer->customProperty( "labeling/shapeRadiiMapUnitScale" ).toString().isEmpty() )
952  {
953  //fallback to older property
954  shapeRadiiMapUnitScale.minScale = layer->customProperty( "labeling/shapeRadiiMapUnitMinScale", 0.0 ).toDouble();
955  shapeRadiiMapUnitScale.maxScale = layer->customProperty( "labeling/shapeRadiiMapUnitMaxScale", 0.0 ).toDouble();
956  }
957  else
958  {
959  shapeRadiiMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( layer->customProperty( "labeling/shapeRadiiMapUnitScale" ).toString() );
960  }
961  shapeFillColor = _readColor( layer, "labeling/shapeFillColor", Qt::white, true );
962  shapeBorderColor = _readColor( layer, "labeling/shapeBorderColor", Qt::darkGray, true );
963  shapeBorderWidth = layer->customProperty( "labeling/shapeBorderWidth", QVariant( .0 ) ).toDouble();
964  shapeBorderWidthUnits = static_cast< SizeUnit >( layer->customProperty( "labeling/shapeBorderWidthUnits", QVariant( MM ) ).toUInt() );
965  if ( layer->customProperty( "labeling/shapeBorderWidthMapUnitScale" ).toString().isEmpty() )
966  {
967  //fallback to older property
968  shapeBorderWidthMapUnitScale.minScale = layer->customProperty( "labeling/shapeBorderWidthMapUnitMinScale", 0.0 ).toDouble();
969  shapeBorderWidthMapUnitScale.maxScale = layer->customProperty( "labeling/shapeBorderWidthMapUnitMaxScale", 0.0 ).toDouble();
970  }
971  else
972  {
973  shapeBorderWidthMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( layer->customProperty( "labeling/shapeBorderWidthMapUnitScale" ).toString() );
974  }
975  shapeJoinStyle = static_cast< Qt::PenJoinStyle >( layer->customProperty( "labeling/shapeJoinStyle", QVariant( Qt::BevelJoin ) ).toUInt() );
976  shapeTransparency = layer->customProperty( "labeling/shapeTransparency", QVariant( 0 ) ).toInt();
978  static_cast< QgsMapRenderer::BlendMode >( layer->customProperty( "labeling/shapeBlendMode", QVariant( QgsMapRenderer::BlendNormal ) ).toUInt() ) );
979 
980  // drop shadow
981  shadowDraw = layer->customProperty( "labeling/shadowDraw", QVariant( false ) ).toBool();
982  shadowUnder = static_cast< ShadowType >( layer->customProperty( "labeling/shadowUnder", QVariant( ShadowLowest ) ).toUInt() );//ShadowLowest;
983  shadowOffsetAngle = layer->customProperty( "labeling/shadowOffsetAngle", QVariant( 135 ) ).toInt();
984  shadowOffsetDist = layer->customProperty( "labeling/shadowOffsetDist", QVariant( 1.0 ) ).toDouble();
985  shadowOffsetUnits = static_cast< SizeUnit >( layer->customProperty( "labeling/shadowOffsetUnits", QVariant( MM ) ).toUInt() );
986  if ( layer->customProperty( "labeling/shadowOffsetMapUnitScale" ).toString().isEmpty() )
987  {
988  //fallback to older property
989  shadowOffsetMapUnitScale.minScale = layer->customProperty( "labeling/shadowOffsetMapUnitMinScale", 0.0 ).toDouble();
990  shadowOffsetMapUnitScale.maxScale = layer->customProperty( "labeling/shadowOffsetMapUnitMaxScale", 0.0 ).toDouble();
991  }
992  else
993  {
994  shadowOffsetMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( layer->customProperty( "labeling/shadowOffsetMapUnitScale" ).toString() );
995  }
996  shadowOffsetGlobal = layer->customProperty( "labeling/shadowOffsetGlobal", QVariant( true ) ).toBool();
997  shadowRadius = layer->customProperty( "labeling/shadowRadius", QVariant( 1.5 ) ).toDouble();
998  shadowRadiusUnits = static_cast< SizeUnit >( layer->customProperty( "labeling/shadowRadiusUnits", QVariant( MM ) ).toUInt() );
999  if ( layer->customProperty( "labeling/shadowRadiusMapUnitScale" ).toString().isEmpty() )
1000  {
1001  //fallback to older property
1002  shadowRadiusMapUnitScale.minScale = layer->customProperty( "labeling/shadowRadiusMapUnitMinScale", 0.0 ).toDouble();
1003  shadowRadiusMapUnitScale.maxScale = layer->customProperty( "labeling/shadowRadiusMapUnitMaxScale", 0.0 ).toDouble();
1004  }
1005  else
1006  {
1007  shadowRadiusMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( layer->customProperty( "labeling/shadowRadiusMapUnitScale" ).toString() );
1008  }
1009  shadowRadiusAlphaOnly = layer->customProperty( "labeling/shadowRadiusAlphaOnly", QVariant( false ) ).toBool();
1010  shadowTransparency = layer->customProperty( "labeling/shadowTransparency", QVariant( 30 ) ).toInt();
1011  shadowScale = layer->customProperty( "labeling/shadowScale", QVariant( 100 ) ).toInt();
1012  shadowColor = _readColor( layer, "labeling/shadowColor", Qt::black, false );
1014  static_cast< QgsMapRenderer::BlendMode >( layer->customProperty( "labeling/shadowBlendMode", QVariant( QgsMapRenderer::BlendMultiply ) ).toUInt() ) );
1015 
1016  // placement
1017  placement = static_cast< Placement >( layer->customProperty( "labeling/placement" ).toInt() );
1018  placementFlags = layer->customProperty( "labeling/placementFlags" ).toUInt();
1019  centroidWhole = layer->customProperty( "labeling/centroidWhole", QVariant( false ) ).toBool();
1020  centroidInside = layer->customProperty( "labeling/centroidInside", QVariant( false ) ).toBool();
1021  predefinedPositionOrder = QgsLabelingUtils::decodePredefinedPositionOrder( layer->customProperty( "labeling/predefinedPositionOrder" ).toString() );
1023  predefinedPositionOrder = DEFAULT_PLACEMENT_ORDER;
1024  fitInPolygonOnly = layer->customProperty( "labeling/fitInPolygonOnly", QVariant( false ) ).toBool();
1025  dist = layer->customProperty( "labeling/dist" ).toDouble();
1026  distInMapUnits = layer->customProperty( "labeling/distInMapUnits" ).toBool();
1027  if ( layer->customProperty( "labeling/distMapUnitScale" ).toString().isEmpty() )
1028  {
1029  //fallback to older property
1030  distMapUnitScale.minScale = layer->customProperty( "labeling/distMapUnitMinScale", 0.0 ).toDouble();
1031  distMapUnitScale.maxScale = layer->customProperty( "labeling/distMapUnitMaxScale", 0.0 ).toDouble();
1032  }
1033  else
1034  {
1035  distMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( layer->customProperty( "labeling/distMapUnitScale" ).toString() );
1036  }
1037  offsetType = static_cast< OffsetType >( layer->customProperty( "labeling/offsetType", QVariant( FromPoint ) ).toUInt() );
1038  quadOffset = static_cast< QuadrantPosition >( layer->customProperty( "labeling/quadOffset", QVariant( QuadrantOver ) ).toUInt() );
1039  xOffset = layer->customProperty( "labeling/xOffset", QVariant( 0.0 ) ).toDouble();
1040  yOffset = layer->customProperty( "labeling/yOffset", QVariant( 0.0 ) ).toDouble();
1041  labelOffsetInMapUnits = layer->customProperty( "labeling/labelOffsetInMapUnits", QVariant( true ) ).toBool();
1042  if ( layer->customProperty( "labeling/labelOffsetMapUnitScale" ).toString().isEmpty() )
1043  {
1044  //fallback to older property
1045  labelOffsetMapUnitScale.minScale = layer->customProperty( "labeling/labelOffsetMapUnitMinScale", 0.0 ).toDouble();
1046  labelOffsetMapUnitScale.maxScale = layer->customProperty( "labeling/labelOffsetMapUnitMaxScale", 0.0 ).toDouble();
1047  }
1048  else
1049  {
1050  labelOffsetMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( layer->customProperty( "labeling/labelOffsetMapUnitScale" ).toString() );
1051  }
1052  angleOffset = layer->customProperty( "labeling/angleOffset", QVariant( 0.0 ) ).toDouble();
1053  preserveRotation = layer->customProperty( "labeling/preserveRotation", QVariant( true ) ).toBool();
1054  maxCurvedCharAngleIn = layer->customProperty( "labeling/maxCurvedCharAngleIn", QVariant( 25.0 ) ).toDouble();
1055  maxCurvedCharAngleOut = layer->customProperty( "labeling/maxCurvedCharAngleOut", QVariant( -25.0 ) ).toDouble();
1056  priority = layer->customProperty( "labeling/priority" ).toInt();
1057  repeatDistance = layer->customProperty( "labeling/repeatDistance", 0.0 ).toDouble();
1058  repeatDistanceUnit = static_cast< SizeUnit >( layer->customProperty( "labeling/repeatDistanceUnit", QVariant( MM ) ).toUInt() );
1059  if ( layer->customProperty( "labeling/repeatDistanceMapUnitScale" ).toString().isEmpty() )
1060  {
1061  //fallback to older property
1062  repeatDistanceMapUnitScale.minScale = layer->customProperty( "labeling/repeatDistanceMapUnitMinScale", 0.0 ).toDouble();
1063  repeatDistanceMapUnitScale.maxScale = layer->customProperty( "labeling/repeatDistanceMapUnitMaxScale", 0.0 ).toDouble();
1064  }
1065  else
1066  {
1067  repeatDistanceMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( layer->customProperty( "labeling/repeatDistanceMapUnitScale" ).toString() );
1068  }
1069 
1070  // rendering
1071  int scalemn = layer->customProperty( "labeling/scaleMin", QVariant( 0 ) ).toInt();
1072  int scalemx = layer->customProperty( "labeling/scaleMax", QVariant( 0 ) ).toInt();
1073 
1074  // fix for scale visibility limits being keyed off of just its values in the past (<2.0)
1075  QVariant scalevis = layer->customProperty( "labeling/scaleVisibility", QVariant() );
1076  if ( scalevis.isValid() )
1077  {
1078  scaleVisibility = scalevis.toBool();
1079  scaleMin = scalemn;
1080  scaleMax = scalemx;
1081  }
1082  else if ( scalemn > 0 || scalemx > 0 )
1083  {
1084  scaleVisibility = true;
1085  scaleMin = scalemn;
1086  scaleMax = scalemx;
1087  }
1088  else
1089  {
1090  // keep scaleMin and scaleMax at new 1.0 defaults (1 and 10000000, were 0 and 0)
1091  scaleVisibility = false;
1092  }
1093 
1094 
1095  fontLimitPixelSize = layer->customProperty( "labeling/fontLimitPixelSize", QVariant( false ) ).toBool();
1096  fontMinPixelSize = layer->customProperty( "labeling/fontMinPixelSize", QVariant( 0 ) ).toInt();
1097  fontMaxPixelSize = layer->customProperty( "labeling/fontMaxPixelSize", QVariant( 10000 ) ).toInt();
1098  displayAll = layer->customProperty( "labeling/displayAll", QVariant( false ) ).toBool();
1099  upsidedownLabels = static_cast< UpsideDownLabels >( layer->customProperty( "labeling/upsidedownLabels", QVariant( Upright ) ).toUInt() );
1100 
1101  labelPerPart = layer->customProperty( "labeling/labelPerPart" ).toBool();
1102  mergeLines = layer->customProperty( "labeling/mergeLines" ).toBool();
1103  minFeatureSize = layer->customProperty( "labeling/minFeatureSize" ).toDouble();
1104  limitNumLabels = layer->customProperty( "labeling/limitNumLabels", QVariant( false ) ).toBool();
1105  maxNumLabels = layer->customProperty( "labeling/maxNumLabels", QVariant( 2000 ) ).toInt();
1106  obstacle = layer->customProperty( "labeling/obstacle", QVariant( true ) ).toBool();
1107  obstacleFactor = layer->customProperty( "labeling/obstacleFactor", QVariant( 1.0 ) ).toDouble();
1108  obstacleType = static_cast< ObstacleType >( layer->customProperty( "labeling/obstacleType", QVariant( PolygonInterior ) ).toUInt() );
1109  zIndex = layer->customProperty( "labeling/zIndex", QVariant( 0.0 ) ).toDouble();
1110 
1111  readDataDefinedPropertyMap( layer, nullptr, dataDefinedProperties );
1112 }
1113 
1115 {
1116  // this is a mark that labeling information is present
1117  layer->setCustomProperty( "labeling", "pal" );
1118 
1119  layer->setCustomProperty( "labeling/enabled", enabled );
1120  layer->setCustomProperty( "labeling/drawLabels", drawLabels );
1121 
1122  // text style
1123  layer->setCustomProperty( "labeling/fieldName", fieldName );
1124  layer->setCustomProperty( "labeling/isExpression", isExpression );
1125  layer->setCustomProperty( "labeling/fontFamily", textFont.family() );
1126  layer->setCustomProperty( "labeling/namedStyle", QgsFontUtils::untranslateNamedStyle( textNamedStyle ) );
1127  layer->setCustomProperty( "labeling/fontSize", textFont.pointSizeF() );
1128  layer->setCustomProperty( "labeling/fontSizeInMapUnits", fontSizeInMapUnits );
1129  layer->setCustomProperty( "labeling/fontSizeMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( fontSizeMapUnitScale ) );
1130  layer->setCustomProperty( "labeling/fontWeight", textFont.weight() );
1131  layer->setCustomProperty( "labeling/fontItalic", textFont.italic() );
1132  layer->setCustomProperty( "labeling/fontStrikeout", textFont.strikeOut() );
1133  layer->setCustomProperty( "labeling/fontUnderline", textFont.underline() );
1134  _writeColor( layer, "labeling/textColor", textColor );
1135  layer->setCustomProperty( "labeling/fontCapitals", static_cast< unsigned int >( textFont.capitalization() ) );
1136  layer->setCustomProperty( "labeling/fontLetterSpacing", textFont.letterSpacing() );
1137  layer->setCustomProperty( "labeling/fontWordSpacing", textFont.wordSpacing() );
1138  layer->setCustomProperty( "labeling/textTransp", textTransp );
1139  layer->setCustomProperty( "labeling/blendMode", QgsMapRenderer::getBlendModeEnum( blendMode ) );
1140  layer->setCustomProperty( "labeling/previewBkgrdColor", previewBkgrdColor.name() );
1141  QDomDocument doc( "substitutions" );
1142  QDomElement replacementElem = doc.createElement( "substitutions" );
1143  substitutions.writeXml( replacementElem, doc );
1144  QString replacementProps;
1145  QTextStream stream( &replacementProps );
1146  replacementElem.save( stream, -1 );
1147  layer->setCustomProperty( "labeling/substitutions", replacementProps );
1148  layer->setCustomProperty( "labeling/useSubstitutions", useSubstitutions );
1149 
1150  // text formatting
1151  layer->setCustomProperty( "labeling/wrapChar", wrapChar );
1152  layer->setCustomProperty( "labeling/multilineHeight", multilineHeight );
1153  layer->setCustomProperty( "labeling/multilineAlign", static_cast< unsigned int >( multilineAlign ) );
1154  layer->setCustomProperty( "labeling/addDirectionSymbol", addDirectionSymbol );
1155  layer->setCustomProperty( "labeling/leftDirectionSymbol", leftDirectionSymbol );
1156  layer->setCustomProperty( "labeling/rightDirectionSymbol", rightDirectionSymbol );
1157  layer->setCustomProperty( "labeling/reverseDirectionSymbol", reverseDirectionSymbol );
1158  layer->setCustomProperty( "labeling/placeDirectionSymbol", static_cast< unsigned int >( placeDirectionSymbol ) );
1159  layer->setCustomProperty( "labeling/formatNumbers", formatNumbers );
1160  layer->setCustomProperty( "labeling/decimals", decimals );
1161  layer->setCustomProperty( "labeling/plussign", plusSign );
1162 
1163  // text buffer
1164  layer->setCustomProperty( "labeling/bufferDraw", bufferDraw );
1165  layer->setCustomProperty( "labeling/bufferSize", bufferSize );
1166  layer->setCustomProperty( "labeling/bufferSizeInMapUnits", bufferSizeInMapUnits );
1167  layer->setCustomProperty( "labeling/bufferSizeMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( bufferSizeMapUnitScale ) );
1168  _writeColor( layer, "labeling/bufferColor", bufferColor );
1169  layer->setCustomProperty( "labeling/bufferNoFill", bufferNoFill );
1170  layer->setCustomProperty( "labeling/bufferTransp", bufferTransp );
1171  layer->setCustomProperty( "labeling/bufferJoinStyle", static_cast< unsigned int >( bufferJoinStyle ) );
1172  layer->setCustomProperty( "labeling/bufferBlendMode", QgsMapRenderer::getBlendModeEnum( bufferBlendMode ) );
1173 
1174  // background
1175  layer->setCustomProperty( "labeling/shapeDraw", shapeDraw );
1176  layer->setCustomProperty( "labeling/shapeType", static_cast< unsigned int >( shapeType ) );
1177  layer->setCustomProperty( "labeling/shapeSVGFile", shapeSVGFile );
1178  layer->setCustomProperty( "labeling/shapeSizeType", static_cast< unsigned int >( shapeSizeType ) );
1179  layer->setCustomProperty( "labeling/shapeSizeX", shapeSize.x() );
1180  layer->setCustomProperty( "labeling/shapeSizeY", shapeSize.y() );
1181  layer->setCustomProperty( "labeling/shapeSizeUnits", static_cast< unsigned int >( shapeSizeUnits ) );
1182  layer->setCustomProperty( "labeling/shapeSizeMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shapeSizeMapUnitScale ) );
1183  layer->setCustomProperty( "labeling/shapeRotationType", static_cast< unsigned int >( shapeRotationType ) );
1184  layer->setCustomProperty( "labeling/shapeRotation", shapeRotation );
1185  layer->setCustomProperty( "labeling/shapeOffsetX", shapeOffset.x() );
1186  layer->setCustomProperty( "labeling/shapeOffsetY", shapeOffset.y() );
1187  layer->setCustomProperty( "labeling/shapeOffsetUnits", static_cast< unsigned int >( shapeOffsetUnits ) );
1188  layer->setCustomProperty( "labeling/shapeOffsetMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shapeOffsetMapUnitScale ) );
1189  layer->setCustomProperty( "labeling/shapeRadiiX", shapeRadii.x() );
1190  layer->setCustomProperty( "labeling/shapeRadiiY", shapeRadii.y() );
1191  layer->setCustomProperty( "labeling/shapeRadiiUnits", static_cast< unsigned int >( shapeRadiiUnits ) );
1192  layer->setCustomProperty( "labeling/shapeRadiiMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shapeRadiiMapUnitScale ) );
1193  _writeColor( layer, "labeling/shapeFillColor", shapeFillColor, true );
1194  _writeColor( layer, "labeling/shapeBorderColor", shapeBorderColor, true );
1195  layer->setCustomProperty( "labeling/shapeBorderWidth", shapeBorderWidth );
1196  layer->setCustomProperty( "labeling/shapeBorderWidthUnits", static_cast< unsigned int >( shapeBorderWidthUnits ) );
1197  layer->setCustomProperty( "labeling/shapeBorderWidthMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shapeBorderWidthMapUnitScale ) );
1198  layer->setCustomProperty( "labeling/shapeJoinStyle", static_cast< unsigned int >( shapeJoinStyle ) );
1199  layer->setCustomProperty( "labeling/shapeTransparency", shapeTransparency );
1200  layer->setCustomProperty( "labeling/shapeBlendMode", QgsMapRenderer::getBlendModeEnum( shapeBlendMode ) );
1201 
1202  // drop shadow
1203  layer->setCustomProperty( "labeling/shadowDraw", shadowDraw );
1204  layer->setCustomProperty( "labeling/shadowUnder", static_cast< unsigned int >( shadowUnder ) );
1205  layer->setCustomProperty( "labeling/shadowOffsetAngle", shadowOffsetAngle );
1206  layer->setCustomProperty( "labeling/shadowOffsetDist", shadowOffsetDist );
1207  layer->setCustomProperty( "labeling/shadowOffsetUnits", static_cast< unsigned int >( shadowOffsetUnits ) );
1208  layer->setCustomProperty( "labeling/shadowOffsetMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shadowOffsetMapUnitScale ) );
1209  layer->setCustomProperty( "labeling/shadowOffsetGlobal", shadowOffsetGlobal );
1210  layer->setCustomProperty( "labeling/shadowRadius", shadowRadius );
1211  layer->setCustomProperty( "labeling/shadowRadiusUnits", static_cast< unsigned int >( shadowRadiusUnits ) );
1212  layer->setCustomProperty( "labeling/shadowRadiusMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shadowRadiusMapUnitScale ) );
1213  layer->setCustomProperty( "labeling/shadowRadiusAlphaOnly", shadowRadiusAlphaOnly );
1214  layer->setCustomProperty( "labeling/shadowTransparency", shadowTransparency );
1215  layer->setCustomProperty( "labeling/shadowScale", shadowScale );
1216  _writeColor( layer, "labeling/shadowColor", shadowColor, false );
1217  layer->setCustomProperty( "labeling/shadowBlendMode", QgsMapRenderer::getBlendModeEnum( shadowBlendMode ) );
1218 
1219  // placement
1220  layer->setCustomProperty( "labeling/placement", placement );
1221  layer->setCustomProperty( "labeling/placementFlags", static_cast< unsigned int >( placementFlags ) );
1222  layer->setCustomProperty( "labeling/centroidWhole", centroidWhole );
1223  layer->setCustomProperty( "labeling/centroidInside", centroidInside );
1224  layer->setCustomProperty( "labeling/predefinedPositionOrder", QgsLabelingUtils::encodePredefinedPositionOrder( predefinedPositionOrder ) );
1225  layer->setCustomProperty( "labeling/fitInPolygonOnly", fitInPolygonOnly );
1226  layer->setCustomProperty( "labeling/dist", dist );
1227  layer->setCustomProperty( "labeling/distInMapUnits", distInMapUnits );
1228  layer->setCustomProperty( "labeling/distMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( distMapUnitScale ) );
1229  layer->setCustomProperty( "labeling/offsetType", static_cast< unsigned int >( offsetType ) );
1230  layer->setCustomProperty( "labeling/quadOffset", static_cast< unsigned int >( quadOffset ) );
1231  layer->setCustomProperty( "labeling/xOffset", xOffset );
1232  layer->setCustomProperty( "labeling/yOffset", yOffset );
1233  layer->setCustomProperty( "labeling/labelOffsetInMapUnits", labelOffsetInMapUnits );
1234  layer->setCustomProperty( "labeling/labelOffsetMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( labelOffsetMapUnitScale ) );
1235  layer->setCustomProperty( "labeling/angleOffset", angleOffset );
1236  layer->setCustomProperty( "labeling/preserveRotation", preserveRotation );
1237  layer->setCustomProperty( "labeling/maxCurvedCharAngleIn", maxCurvedCharAngleIn );
1238  layer->setCustomProperty( "labeling/maxCurvedCharAngleOut", maxCurvedCharAngleOut );
1239  layer->setCustomProperty( "labeling/priority", priority );
1240  layer->setCustomProperty( "labeling/repeatDistance", repeatDistance );
1241  layer->setCustomProperty( "labeling/repeatDistanceUnit", repeatDistanceUnit );
1242  layer->setCustomProperty( "labeling/repeatDistanceMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( repeatDistanceMapUnitScale ) );
1243 
1244  // rendering
1245  layer->setCustomProperty( "labeling/scaleVisibility", scaleVisibility );
1246  layer->setCustomProperty( "labeling/scaleMin", scaleMin );
1247  layer->setCustomProperty( "labeling/scaleMax", scaleMax );
1248  layer->setCustomProperty( "labeling/fontLimitPixelSize", fontLimitPixelSize );
1249  layer->setCustomProperty( "labeling/fontMinPixelSize", fontMinPixelSize );
1250  layer->setCustomProperty( "labeling/fontMaxPixelSize", fontMaxPixelSize );
1251  layer->setCustomProperty( "labeling/displayAll", displayAll );
1252  layer->setCustomProperty( "labeling/upsidedownLabels", static_cast< unsigned int >( upsidedownLabels ) );
1253 
1254  layer->setCustomProperty( "labeling/labelPerPart", labelPerPart );
1255  layer->setCustomProperty( "labeling/mergeLines", mergeLines );
1256  layer->setCustomProperty( "labeling/minFeatureSize", minFeatureSize );
1257  layer->setCustomProperty( "labeling/limitNumLabels", limitNumLabels );
1258  layer->setCustomProperty( "labeling/maxNumLabels", maxNumLabels );
1259  layer->setCustomProperty( "labeling/obstacle", obstacle );
1260  layer->setCustomProperty( "labeling/obstacleFactor", obstacleFactor );
1261  layer->setCustomProperty( "labeling/obstacleType", static_cast< unsigned int >( obstacleType ) );
1262  layer->setCustomProperty( "labeling/zIndex", zIndex );
1263 
1264  writeDataDefinedPropertyMap( layer, nullptr, dataDefinedProperties );
1265  layer->emitStyleChanged();
1266 }
1267 
1269 {
1270  enabled = true;
1271  drawLabels = true;
1272 
1273  // text style
1274  QDomElement textStyleElem = elem.firstChildElement( "text-style" );
1275  fieldName = textStyleElem.attribute( "fieldName" );
1276  isExpression = textStyleElem.attribute( "isExpression" ).toInt();
1277  QFont appFont = QApplication::font();
1278  mTextFontFamily = textStyleElem.attribute( "fontFamily", appFont.family() );
1279  QString fontFamily = mTextFontFamily;
1281  {
1282  // trigger to notify user about font family substitution (signal emitted in QgsVectorLayer::prepareLabelingAndDiagrams)
1283  mTextFontFound = false;
1284 
1285  // TODO: update when pref for how to resolve missing family (use matching algorithm or just default font) is implemented
1286  // currently only defaults to matching algorithm for resolving [foundry], if a font of similar family is found (default for QFont)
1287 
1288  // for now, do not use matching algorithm for substitution if family not found, substitute default instead
1289  fontFamily = appFont.family();
1290  }
1291 
1292  double fontSize = textStyleElem.attribute( "fontSize" ).toDouble();
1293  fontSizeInMapUnits = textStyleElem.attribute( "fontSizeInMapUnits" ).toInt();
1294  if ( !textStyleElem.hasAttribute( "fontSizeMapUnitScale" ) )
1295  {
1296  //fallback to older property
1297  fontSizeMapUnitScale.minScale = textStyleElem.attribute( "fontSizeMapUnitMinScale", "0" ).toDouble();
1298  fontSizeMapUnitScale.maxScale = textStyleElem.attribute( "fontSizeMapUnitMaxScale", "0" ).toDouble();
1299  }
1300  else
1301  {
1302  fontSizeMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( textStyleElem.attribute( "fontSizeMapUnitScale" ) );
1303  }
1304  int fontWeight = textStyleElem.attribute( "fontWeight" ).toInt();
1305  bool fontItalic = textStyleElem.attribute( "fontItalic" ).toInt();
1306  textFont = QFont( fontFamily, fontSize, fontWeight, fontItalic );
1307  textFont.setPointSizeF( fontSize ); //double precision needed because of map units
1308  textNamedStyle = QgsFontUtils::translateNamedStyle( textStyleElem.attribute( "namedStyle" ) );
1309  QgsFontUtils::updateFontViaStyle( textFont, textNamedStyle ); // must come after textFont.setPointSizeF()
1310  textFont.setCapitalization( static_cast< QFont::Capitalization >( textStyleElem.attribute( "fontCapitals", "0" ).toUInt() ) );
1311  textFont.setUnderline( textStyleElem.attribute( "fontUnderline" ).toInt() );
1312  textFont.setStrikeOut( textStyleElem.attribute( "fontStrikeout" ).toInt() );
1313  textFont.setLetterSpacing( QFont::AbsoluteSpacing, textStyleElem.attribute( "fontLetterSpacing", "0" ).toDouble() );
1314  textFont.setWordSpacing( textStyleElem.attribute( "fontWordSpacing", "0" ).toDouble() );
1315  textColor = QgsSymbolLayerV2Utils::decodeColor( textStyleElem.attribute( "textColor", QgsSymbolLayerV2Utils::encodeColor( Qt::black ) ) );
1316  textTransp = textStyleElem.attribute( "textTransp" ).toInt();
1318  static_cast< QgsMapRenderer::BlendMode >( textStyleElem.attribute( "blendMode", QString::number( QgsMapRenderer::BlendNormal ) ).toUInt() ) );
1319  previewBkgrdColor = QColor( textStyleElem.attribute( "previewBkgrdColor", "#ffffff" ) );
1320  substitutions.readXml( textStyleElem.firstChildElement( "substitutions" ) );
1321  useSubstitutions = textStyleElem.attribute( "useSubstitutions" ).toInt();
1322 
1323  // text formatting
1324  QDomElement textFormatElem = elem.firstChildElement( "text-format" );
1325  wrapChar = textFormatElem.attribute( "wrapChar" );
1326  multilineHeight = textFormatElem.attribute( "multilineHeight", "1" ).toDouble();
1327  multilineAlign = static_cast< MultiLineAlign >( textFormatElem.attribute( "multilineAlign", QString::number( MultiFollowPlacement ) ).toUInt() );
1328  addDirectionSymbol = textFormatElem.attribute( "addDirectionSymbol" ).toInt();
1329  leftDirectionSymbol = textFormatElem.attribute( "leftDirectionSymbol", "<" );
1330  rightDirectionSymbol = textFormatElem.attribute( "rightDirectionSymbol", ">" );
1331  reverseDirectionSymbol = textFormatElem.attribute( "reverseDirectionSymbol" ).toInt();
1332  placeDirectionSymbol = static_cast< DirectionSymbols >( textFormatElem.attribute( "placeDirectionSymbol", QString::number( SymbolLeftRight ) ).toUInt() );
1333  formatNumbers = textFormatElem.attribute( "formatNumbers" ).toInt();
1334  decimals = textFormatElem.attribute( "decimals" ).toInt();
1335  plusSign = textFormatElem.attribute( "plussign" ).toInt();
1336 
1337  // text buffer
1338  QDomElement textBufferElem = elem.firstChildElement( "text-buffer" );
1339  double bufSize = textBufferElem.attribute( "bufferSize", "0" ).toDouble();
1340 
1341  // fix for buffer being keyed off of just its size in the past (<2.0)
1342  QVariant drawBuffer = textBufferElem.attribute( "bufferDraw" );
1343  if ( drawBuffer.isValid() )
1344  {
1345  bufferDraw = drawBuffer.toBool();
1346  bufferSize = bufSize;
1347  }
1348  else if ( bufSize != 0.0 )
1349  {
1350  bufferDraw = true;
1351  bufferSize = bufSize;
1352  }
1353  else
1354  {
1355  // keep bufferSize at new 1.0 default
1356  bufferDraw = false;
1357  }
1358 
1359  bufferSizeInMapUnits = textBufferElem.attribute( "bufferSizeInMapUnits" ).toInt();
1360  if ( !textBufferElem.hasAttribute( "bufferSizeMapUnitScale" ) )
1361  {
1362  //fallback to older property
1363  bufferSizeMapUnitScale.minScale = textBufferElem.attribute( "bufferSizeMapUnitMinScale", "0" ).toDouble();
1364  bufferSizeMapUnitScale.maxScale = textBufferElem.attribute( "bufferSizeMapUnitMaxScale", "0" ).toDouble();
1365  }
1366  else
1367  {
1368  bufferSizeMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( textBufferElem.attribute( "bufferSizeMapUnitScale" ) );
1369  }
1370  bufferColor = QgsSymbolLayerV2Utils::decodeColor( textBufferElem.attribute( "bufferColor", QgsSymbolLayerV2Utils::encodeColor( Qt::white ) ) );
1371  bufferTransp = textBufferElem.attribute( "bufferTransp" ).toInt();
1373  static_cast< QgsMapRenderer::BlendMode >( textBufferElem.attribute( "bufferBlendMode", QString::number( QgsMapRenderer::BlendNormal ) ).toUInt() ) );
1374  bufferJoinStyle = static_cast< Qt::PenJoinStyle >( textBufferElem.attribute( "bufferJoinStyle", QString::number( Qt::RoundJoin ) ).toUInt() );
1375  bufferNoFill = textBufferElem.attribute( "bufferNoFill", "0" ).toInt();
1376 
1377  // background
1378  QDomElement backgroundElem = elem.firstChildElement( "background" );
1379  shapeDraw = backgroundElem.attribute( "shapeDraw", "0" ).toInt();
1380  shapeType = static_cast< ShapeType >( backgroundElem.attribute( "shapeType", QString::number( ShapeRectangle ) ).toUInt() );
1381  shapeSVGFile = backgroundElem.attribute( "shapeSVGFile" );
1382  shapeSizeType = static_cast< SizeType >( backgroundElem.attribute( "shapeSizeType", QString::number( SizeBuffer ) ).toUInt() );
1383  shapeSize = QPointF( backgroundElem.attribute( "shapeSizeX", "0" ).toDouble(),
1384  backgroundElem.attribute( "shapeSizeY", "0" ).toDouble() );
1385  shapeSizeUnits = static_cast< SizeUnit >( backgroundElem.attribute( "shapeSizeUnits", QString::number( MM ) ).toUInt() );
1386  if ( !backgroundElem.hasAttribute( "shapeSizeMapUnitScale" ) )
1387  {
1388  //fallback to older property
1389  shapeSizeMapUnitScale.minScale = backgroundElem.attribute( "shapeSizeMapUnitMinScale", "0" ).toDouble();
1390  shapeSizeMapUnitScale.maxScale = backgroundElem.attribute( "shapeSizeMapUnitMaxScale", "0" ).toDouble();
1391  }
1392  else
1393  {
1394  shapeSizeMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( backgroundElem.attribute( "shapeSizeMapUnitScale" ) );
1395  }
1396  shapeRotationType = static_cast< RotationType >( backgroundElem.attribute( "shapeRotationType", QString::number( RotationSync ) ).toUInt() );
1397  shapeRotation = backgroundElem.attribute( "shapeRotation", "0" ).toDouble();
1398  shapeOffset = QPointF( backgroundElem.attribute( "shapeOffsetX", "0" ).toDouble(),
1399  backgroundElem.attribute( "shapeOffsetY", "0" ).toDouble() );
1400  shapeOffsetUnits = static_cast< SizeUnit >( backgroundElem.attribute( "shapeOffsetUnits", QString::number( MM ) ).toUInt() );
1401  if ( !backgroundElem.hasAttribute( "shapeOffsetMapUnitScale" ) )
1402  {
1403  //fallback to older property
1404  shapeOffsetMapUnitScale.minScale = backgroundElem.attribute( "shapeOffsetMapUnitMinScale", "0" ).toDouble();
1405  shapeOffsetMapUnitScale.maxScale = backgroundElem.attribute( "shapeOffsetMapUnitMaxScale", "0" ).toDouble();
1406  }
1407  else
1408  {
1409  shapeOffsetMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( backgroundElem.attribute( "shapeOffsetMapUnitScale" ) );
1410  }
1411  shapeRadii = QPointF( backgroundElem.attribute( "shapeRadiiX", "0" ).toDouble(),
1412  backgroundElem.attribute( "shapeRadiiY", "0" ).toDouble() );
1413  shapeRadiiUnits = static_cast< SizeUnit >( backgroundElem.attribute( "shapeRadiiUnits", QString::number( MM ) ).toUInt() );
1414  if ( !backgroundElem.hasAttribute( "shapeRadiiMapUnitScale" ) )
1415  {
1416  //fallback to older property
1417  shapeRadiiMapUnitScale.minScale = backgroundElem.attribute( "shapeRadiiMapUnitMinScale", "0" ).toDouble();
1418  shapeRadiiMapUnitScale.maxScale = backgroundElem.attribute( "shapeRadiiMapUnitMaxScale", "0" ).toDouble();
1419  }
1420  else
1421  {
1422  shapeRadiiMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( backgroundElem.attribute( "shapeRadiiMapUnitScale" ) );
1423  }
1424  shapeFillColor = QgsSymbolLayerV2Utils::decodeColor( backgroundElem.attribute( "shapeFillColor", QgsSymbolLayerV2Utils::encodeColor( Qt::white ) ) );
1425  shapeBorderColor = QgsSymbolLayerV2Utils::decodeColor( backgroundElem.attribute( "shapeBorderColor", QgsSymbolLayerV2Utils::encodeColor( Qt::darkGray ) ) );
1426  shapeBorderWidth = backgroundElem.attribute( "shapeBorderWidth", "0" ).toDouble();
1427  shapeBorderWidthUnits = static_cast< SizeUnit >( backgroundElem.attribute( "shapeBorderWidthUnits", QString::number( MM ) ).toUInt() );
1428  if ( !backgroundElem.hasAttribute( "shapeBorderWidthMapUnitScale" ) )
1429  {
1430  //fallback to older property
1431  shapeBorderWidthMapUnitScale.minScale = backgroundElem.attribute( "shapeBorderWidthMapUnitMinScale", "0" ).toDouble();
1432  shapeBorderWidthMapUnitScale.maxScale = backgroundElem.attribute( "shapeBorderWidthMapUnitMaxScale", "0" ).toDouble();
1433  }
1434  else
1435  {
1436  shapeBorderWidthMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( backgroundElem.attribute( "shapeBorderWidthMapUnitScale" ) );
1437  }
1438  shapeJoinStyle = static_cast< Qt::PenJoinStyle >( backgroundElem.attribute( "shapeJoinStyle", QString::number( Qt::BevelJoin ) ).toUInt() );
1439  shapeTransparency = backgroundElem.attribute( "shapeTransparency", "0" ).toInt();
1441  static_cast< QgsMapRenderer::BlendMode >( backgroundElem.attribute( "shapeBlendMode", QString::number( QgsMapRenderer::BlendNormal ) ).toUInt() ) );
1442 
1443  // drop shadow
1444  QDomElement shadowElem = elem.firstChildElement( "shadow" );
1445  shadowDraw = shadowElem.attribute( "shadowDraw", "0" ).toInt();
1446  shadowUnder = static_cast< ShadowType >( shadowElem.attribute( "shadowUnder", QString::number( ShadowLowest ) ).toUInt() );//ShadowLowest ;
1447  shadowOffsetAngle = shadowElem.attribute( "shadowOffsetAngle", "135" ).toInt();
1448  shadowOffsetDist = shadowElem.attribute( "shadowOffsetDist", "1" ).toDouble();
1449  shadowOffsetUnits = static_cast< SizeUnit >( shadowElem.attribute( "shadowOffsetUnits", QString::number( MM ) ).toUInt() );
1450  if ( !shadowElem.hasAttribute( "shadowOffsetMapUnitScale" ) )
1451  {
1452  //fallback to older property
1453  shadowOffsetMapUnitScale.minScale = shadowElem.attribute( "shadowOffsetMapUnitMinScale", "0" ).toDouble();
1454  shadowOffsetMapUnitScale.maxScale = shadowElem.attribute( "shadowOffsetMapUnitMaxScale", "0" ).toDouble();
1455  }
1456  else
1457  {
1458  shadowOffsetMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( shadowElem.attribute( "shadowOffsetMapUnitScale" ) );
1459  }
1460  shadowOffsetGlobal = shadowElem.attribute( "shadowOffsetGlobal", "1" ).toInt();
1461  shadowRadius = shadowElem.attribute( "shadowRadius", "1.5" ).toDouble();
1462  shadowRadiusUnits = static_cast< SizeUnit >( shadowElem.attribute( "shadowRadiusUnits", QString::number( MM ) ).toUInt() );
1463  if ( !shadowElem.hasAttribute( "shadowRadiusMapUnitScale" ) )
1464  {
1465  //fallback to older property
1466  shadowRadiusMapUnitScale.minScale = shadowElem.attribute( "shadowRadiusMapUnitMinScale", "0" ).toDouble();
1467  shadowRadiusMapUnitScale.maxScale = shadowElem.attribute( "shadowRadiusMapUnitMaxScale", "0" ).toDouble();
1468  }
1469  else
1470  {
1471  shadowRadiusMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( shadowElem.attribute( "shadowRadiusMapUnitScale" ) );
1472  }
1473  shadowRadiusAlphaOnly = shadowElem.attribute( "shadowRadiusAlphaOnly", "0" ).toInt();
1474  shadowTransparency = shadowElem.attribute( "shadowTransparency", "30" ).toInt();
1475  shadowScale = shadowElem.attribute( "shadowScale", "100" ).toInt();
1476  shadowColor = QgsSymbolLayerV2Utils::decodeColor( shadowElem.attribute( "shadowColor", QgsSymbolLayerV2Utils::encodeColor( Qt::black ) ) );
1478  static_cast< QgsMapRenderer::BlendMode >( shadowElem.attribute( "shadowBlendMode", QString::number( QgsMapRenderer::BlendMultiply ) ).toUInt() ) );
1479 
1480  // placement
1481  QDomElement placementElem = elem.firstChildElement( "placement" );
1482  placement = static_cast< Placement >( placementElem.attribute( "placement" ).toInt() );
1483  placementFlags = placementElem.attribute( "placementFlags" ).toUInt();
1484  centroidWhole = placementElem.attribute( "centroidWhole", "0" ).toInt();
1485  centroidInside = placementElem.attribute( "centroidInside", "0" ).toInt();
1486  predefinedPositionOrder = QgsLabelingUtils::decodePredefinedPositionOrder( placementElem.attribute( "predefinedPositionOrder" ) );
1488  predefinedPositionOrder = DEFAULT_PLACEMENT_ORDER;
1489  fitInPolygonOnly = placementElem.attribute( "fitInPolygonOnly", "0" ).toInt();
1490  dist = placementElem.attribute( "dist" ).toDouble();
1491  distInMapUnits = placementElem.attribute( "distInMapUnits" ).toInt();
1492  if ( !placementElem.hasAttribute( "distMapUnitScale" ) )
1493  {
1494  //fallback to older property
1495  distMapUnitScale.minScale = placementElem.attribute( "distMapUnitMinScale", "0" ).toDouble();
1496  distMapUnitScale.maxScale = placementElem.attribute( "distMapUnitMaxScale", "0" ).toDouble();
1497  }
1498  else
1499  {
1500  distMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( placementElem.attribute( "distMapUnitScale" ) );
1501  }
1502  offsetType = static_cast< OffsetType >( placementElem.attribute( "offsetType", QString::number( FromPoint ) ).toUInt() );
1503  quadOffset = static_cast< QuadrantPosition >( placementElem.attribute( "quadOffset", QString::number( QuadrantOver ) ).toUInt() );
1504  xOffset = placementElem.attribute( "xOffset", "0" ).toDouble();
1505  yOffset = placementElem.attribute( "yOffset", "0" ).toDouble();
1506  labelOffsetInMapUnits = placementElem.attribute( "labelOffsetInMapUnits", "1" ).toInt();
1507  if ( !placementElem.hasAttribute( "labelOffsetMapUnitScale" ) )
1508  {
1509  //fallback to older property
1510  labelOffsetMapUnitScale.minScale = placementElem.attribute( "labelOffsetMapUnitMinScale", "0" ).toDouble();
1511  labelOffsetMapUnitScale.maxScale = placementElem.attribute( "labelOffsetMapUnitMaxScale", "0" ).toDouble();
1512  }
1513  else
1514  {
1515  labelOffsetMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( placementElem.attribute( "labelOffsetMapUnitScale" ) );
1516  }
1517  angleOffset = placementElem.attribute( "angleOffset", "0" ).toDouble();
1518  preserveRotation = placementElem.attribute( "preserveRotation", "1" ).toInt();
1519  maxCurvedCharAngleIn = placementElem.attribute( "maxCurvedCharAngleIn", "25" ).toDouble();
1520  maxCurvedCharAngleOut = placementElem.attribute( "maxCurvedCharAngleOut", "-25" ).toDouble();
1521  priority = placementElem.attribute( "priority" ).toInt();
1522  repeatDistance = placementElem.attribute( "repeatDistance", "0" ).toDouble();
1523  repeatDistanceUnit = static_cast< SizeUnit >( placementElem.attribute( "repeatDistanceUnit", QString::number( MM ) ).toUInt() );
1524  if ( !placementElem.hasAttribute( "repeatDistanceMapUnitScale" ) )
1525  {
1526  //fallback to older property
1527  repeatDistanceMapUnitScale.minScale = placementElem.attribute( "repeatDistanceMapUnitMinScale", "0" ).toDouble();
1528  repeatDistanceMapUnitScale.maxScale = placementElem.attribute( "repeatDistanceMapUnitMaxScale", "0" ).toDouble();
1529  }
1530  else
1531  {
1532  repeatDistanceMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( placementElem.attribute( "repeatDistanceMapUnitScale" ) );
1533  }
1534 
1535  // rendering
1536  QDomElement renderingElem = elem.firstChildElement( "rendering" );
1537  scaleMin = renderingElem.attribute( "scaleMin", "0" ).toInt();
1538  scaleMax = renderingElem.attribute( "scaleMax", "0" ).toInt();
1539  scaleVisibility = renderingElem.attribute( "scaleVisibility" ).toInt();
1540 
1541  fontLimitPixelSize = renderingElem.attribute( "fontLimitPixelSize", "0" ).toInt();
1542  fontMinPixelSize = renderingElem.attribute( "fontMinPixelSize", "0" ).toInt();
1543  fontMaxPixelSize = renderingElem.attribute( "fontMaxPixelSize", "10000" ).toInt();
1544  displayAll = renderingElem.attribute( "displayAll", "0" ).toInt();
1545  upsidedownLabels = static_cast< UpsideDownLabels >( renderingElem.attribute( "upsidedownLabels", QString::number( Upright ) ).toUInt() );
1546 
1547  labelPerPart = renderingElem.attribute( "labelPerPart" ).toInt();
1548  mergeLines = renderingElem.attribute( "mergeLines" ).toInt();
1549  minFeatureSize = renderingElem.attribute( "minFeatureSize" ).toDouble();
1550  limitNumLabels = renderingElem.attribute( "limitNumLabels", "0" ).toInt();
1551  maxNumLabels = renderingElem.attribute( "maxNumLabels", "2000" ).toInt();
1552  obstacle = renderingElem.attribute( "obstacle", "1" ).toInt();
1553  obstacleFactor = renderingElem.attribute( "obstacleFactor", "1" ).toDouble();
1554  obstacleType = static_cast< ObstacleType >( renderingElem.attribute( "obstacleType", QString::number( PolygonInterior ) ).toUInt() );
1555  zIndex = renderingElem.attribute( "zIndex", "0.0" ).toDouble();
1556 
1557  QDomElement ddElem = elem.firstChildElement( "data-defined" );
1558  readDataDefinedPropertyMap( nullptr, &ddElem, dataDefinedProperties );
1559 }
1560 
1561 
1562 
1564 {
1565  // we assume (enabled == true && drawLabels == true) so those are not saved
1566 
1567  // text style
1568  QDomElement textStyleElem = doc.createElement( "text-style" );
1569  textStyleElem.setAttribute( "fieldName", fieldName );
1570  textStyleElem.setAttribute( "isExpression", isExpression );
1571  textStyleElem.setAttribute( "fontFamily", textFont.family() );
1572  textStyleElem.setAttribute( "namedStyle", QgsFontUtils::untranslateNamedStyle( textNamedStyle ) );
1573  textStyleElem.setAttribute( "fontSize", textFont.pointSizeF() );
1574  textStyleElem.setAttribute( "fontSizeInMapUnits", fontSizeInMapUnits );
1575  textStyleElem.setAttribute( "fontSizeMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( fontSizeMapUnitScale ) );
1576  textStyleElem.setAttribute( "fontWeight", textFont.weight() );
1577  textStyleElem.setAttribute( "fontItalic", textFont.italic() );
1578  textStyleElem.setAttribute( "fontStrikeout", textFont.strikeOut() );
1579  textStyleElem.setAttribute( "fontUnderline", textFont.underline() );
1580  textStyleElem.setAttribute( "textColor", QgsSymbolLayerV2Utils::encodeColor( textColor ) );
1581  textStyleElem.setAttribute( "fontCapitals", static_cast< unsigned int >( textFont.capitalization() ) );
1582  textStyleElem.setAttribute( "fontLetterSpacing", textFont.letterSpacing() );
1583  textStyleElem.setAttribute( "fontWordSpacing", textFont.wordSpacing() );
1584  textStyleElem.setAttribute( "textTransp", textTransp );
1585  textStyleElem.setAttribute( "blendMode", QgsMapRenderer::getBlendModeEnum( blendMode ) );
1586  textStyleElem.setAttribute( "previewBkgrdColor", previewBkgrdColor.name() );
1587  QDomElement replacementElem = doc.createElement( "substitutions" );
1588  substitutions.writeXml( replacementElem, doc );
1589  textStyleElem.appendChild( replacementElem );
1590  textStyleElem.setAttribute( "useSubstitutions", useSubstitutions );
1591 
1592  // text formatting
1593  QDomElement textFormatElem = doc.createElement( "text-format" );
1594  textFormatElem.setAttribute( "wrapChar", wrapChar );
1595  textFormatElem.setAttribute( "multilineHeight", multilineHeight );
1596  textFormatElem.setAttribute( "multilineAlign", static_cast< unsigned int >( multilineAlign ) );
1597  textFormatElem.setAttribute( "addDirectionSymbol", addDirectionSymbol );
1598  textFormatElem.setAttribute( "leftDirectionSymbol", leftDirectionSymbol );
1599  textFormatElem.setAttribute( "rightDirectionSymbol", rightDirectionSymbol );
1600  textFormatElem.setAttribute( "reverseDirectionSymbol", reverseDirectionSymbol );
1601  textFormatElem.setAttribute( "placeDirectionSymbol", static_cast< unsigned int >( placeDirectionSymbol ) );
1602  textFormatElem.setAttribute( "formatNumbers", formatNumbers );
1603  textFormatElem.setAttribute( "decimals", decimals );
1604  textFormatElem.setAttribute( "plussign", plusSign );
1605 
1606  // text buffer
1607  QDomElement textBufferElem = doc.createElement( "text-buffer" );
1608  textBufferElem.setAttribute( "bufferDraw", bufferDraw );
1609  textBufferElem.setAttribute( "bufferSize", bufferSize );
1610  textBufferElem.setAttribute( "bufferSizeInMapUnits", bufferSizeInMapUnits );
1611  textBufferElem.setAttribute( "bufferSizeMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( bufferSizeMapUnitScale ) );
1612  textBufferElem.setAttribute( "bufferColor", QgsSymbolLayerV2Utils::encodeColor( bufferColor ) );
1613  textBufferElem.setAttribute( "bufferNoFill", bufferNoFill );
1614  textBufferElem.setAttribute( "bufferTransp", bufferTransp );
1615  textBufferElem.setAttribute( "bufferJoinStyle", static_cast< unsigned int >( bufferJoinStyle ) );
1616  textBufferElem.setAttribute( "bufferBlendMode", QgsMapRenderer::getBlendModeEnum( bufferBlendMode ) );
1617 
1618  // background
1619  QDomElement backgroundElem = doc.createElement( "background" );
1620  backgroundElem.setAttribute( "shapeDraw", shapeDraw );
1621  backgroundElem.setAttribute( "shapeType", static_cast< unsigned int >( shapeType ) );
1622  backgroundElem.setAttribute( "shapeSVGFile", shapeSVGFile );
1623  backgroundElem.setAttribute( "shapeSizeType", static_cast< unsigned int >( shapeSizeType ) );
1624  backgroundElem.setAttribute( "shapeSizeX", shapeSize.x() );
1625  backgroundElem.setAttribute( "shapeSizeY", shapeSize.y() );
1626  backgroundElem.setAttribute( "shapeSizeUnits", static_cast< unsigned int >( shapeSizeUnits ) );
1627  backgroundElem.setAttribute( "shapeSizeMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shapeSizeMapUnitScale ) );
1628  backgroundElem.setAttribute( "shapeRotationType", static_cast< unsigned int >( shapeRotationType ) );
1629  backgroundElem.setAttribute( "shapeRotation", shapeRotation );
1630  backgroundElem.setAttribute( "shapeOffsetX", shapeOffset.x() );
1631  backgroundElem.setAttribute( "shapeOffsetY", shapeOffset.y() );
1632  backgroundElem.setAttribute( "shapeOffsetUnits", static_cast< unsigned int >( shapeOffsetUnits ) );
1633  backgroundElem.setAttribute( "shapeOffsetMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shapeOffsetMapUnitScale ) );
1634  backgroundElem.setAttribute( "shapeRadiiX", shapeRadii.x() );
1635  backgroundElem.setAttribute( "shapeRadiiY", shapeRadii.y() );
1636  backgroundElem.setAttribute( "shapeRadiiUnits", static_cast< unsigned int >( shapeRadiiUnits ) );
1637  backgroundElem.setAttribute( "shapeRadiiMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shapeRadiiMapUnitScale ) );
1638  backgroundElem.setAttribute( "shapeFillColor", QgsSymbolLayerV2Utils::encodeColor( shapeFillColor ) );
1639  backgroundElem.setAttribute( "shapeBorderColor", QgsSymbolLayerV2Utils::encodeColor( shapeBorderColor ) );
1640  backgroundElem.setAttribute( "shapeBorderWidth", shapeBorderWidth );
1641  backgroundElem.setAttribute( "shapeBorderWidthUnits", static_cast< unsigned int >( shapeBorderWidthUnits ) );
1642  backgroundElem.setAttribute( "shapeBorderWidthMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shapeBorderWidthMapUnitScale ) );
1643  backgroundElem.setAttribute( "shapeJoinStyle", static_cast< unsigned int >( shapeJoinStyle ) );
1644  backgroundElem.setAttribute( "shapeTransparency", shapeTransparency );
1645  backgroundElem.setAttribute( "shapeBlendMode", QgsMapRenderer::getBlendModeEnum( shapeBlendMode ) );
1646 
1647  // drop shadow
1648  QDomElement shadowElem = doc.createElement( "shadow" );
1649  shadowElem.setAttribute( "shadowDraw", shadowDraw );
1650  shadowElem.setAttribute( "shadowUnder", static_cast< unsigned int >( shadowUnder ) );
1651  shadowElem.setAttribute( "shadowOffsetAngle", shadowOffsetAngle );
1652  shadowElem.setAttribute( "shadowOffsetDist", shadowOffsetDist );
1653  shadowElem.setAttribute( "shadowOffsetUnits", static_cast< unsigned int >( shadowOffsetUnits ) );
1654  shadowElem.setAttribute( "shadowOffsetMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shadowOffsetMapUnitScale ) );
1655  shadowElem.setAttribute( "shadowOffsetGlobal", shadowOffsetGlobal );
1656  shadowElem.setAttribute( "shadowRadius", shadowRadius );
1657  shadowElem.setAttribute( "shadowRadiusUnits", static_cast< unsigned int >( shadowRadiusUnits ) );
1658  shadowElem.setAttribute( "shadowRadiusMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shadowRadiusMapUnitScale ) );
1659  shadowElem.setAttribute( "shadowRadiusAlphaOnly", shadowRadiusAlphaOnly );
1660  shadowElem.setAttribute( "shadowTransparency", shadowTransparency );
1661  shadowElem.setAttribute( "shadowScale", shadowScale );
1662  shadowElem.setAttribute( "shadowColor", QgsSymbolLayerV2Utils::encodeColor( shadowColor ) );
1663  shadowElem.setAttribute( "shadowBlendMode", QgsMapRenderer::getBlendModeEnum( shadowBlendMode ) );
1664 
1665  // placement
1666  QDomElement placementElem = doc.createElement( "placement" );
1667  placementElem.setAttribute( "placement", placement );
1668  placementElem.setAttribute( "placementFlags", static_cast< unsigned int >( placementFlags ) );
1669  placementElem.setAttribute( "centroidWhole", centroidWhole );
1670  placementElem.setAttribute( "centroidInside", centroidInside );
1671  placementElem.setAttribute( "predefinedPositionOrder", QgsLabelingUtils::encodePredefinedPositionOrder( predefinedPositionOrder ) );
1672  placementElem.setAttribute( "fitInPolygonOnly", fitInPolygonOnly );
1673  placementElem.setAttribute( "dist", dist );
1674  placementElem.setAttribute( "distInMapUnits", distInMapUnits );
1675  placementElem.setAttribute( "distMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( distMapUnitScale ) );
1676  placementElem.setAttribute( "offsetType", static_cast< unsigned int >( offsetType ) );
1677  placementElem.setAttribute( "quadOffset", static_cast< unsigned int >( quadOffset ) );
1678  placementElem.setAttribute( "xOffset", xOffset );
1679  placementElem.setAttribute( "yOffset", yOffset );
1680  placementElem.setAttribute( "labelOffsetInMapUnits", labelOffsetInMapUnits );
1681  placementElem.setAttribute( "labelOffsetMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( labelOffsetMapUnitScale ) );
1682  placementElem.setAttribute( "angleOffset", angleOffset );
1683  placementElem.setAttribute( "preserveRotation", preserveRotation );
1684  placementElem.setAttribute( "maxCurvedCharAngleIn", maxCurvedCharAngleIn );
1685  placementElem.setAttribute( "maxCurvedCharAngleOut", maxCurvedCharAngleOut );
1686  placementElem.setAttribute( "priority", priority );
1687  placementElem.setAttribute( "repeatDistance", repeatDistance );
1688  placementElem.setAttribute( "repeatDistanceUnit", repeatDistanceUnit );
1689  placementElem.setAttribute( "repeatDistanceMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( repeatDistanceMapUnitScale ) );
1690 
1691  // rendering
1692  QDomElement renderingElem = doc.createElement( "rendering" );
1693  renderingElem.setAttribute( "scaleVisibility", scaleVisibility );
1694  renderingElem.setAttribute( "scaleMin", scaleMin );
1695  renderingElem.setAttribute( "scaleMax", scaleMax );
1696  renderingElem.setAttribute( "fontLimitPixelSize", fontLimitPixelSize );
1697  renderingElem.setAttribute( "fontMinPixelSize", fontMinPixelSize );
1698  renderingElem.setAttribute( "fontMaxPixelSize", fontMaxPixelSize );
1699  renderingElem.setAttribute( "displayAll", displayAll );
1700  renderingElem.setAttribute( "upsidedownLabels", static_cast< unsigned int >( upsidedownLabels ) );
1701 
1702  renderingElem.setAttribute( "labelPerPart", labelPerPart );
1703  renderingElem.setAttribute( "mergeLines", mergeLines );
1704  renderingElem.setAttribute( "minFeatureSize", minFeatureSize );
1705  renderingElem.setAttribute( "limitNumLabels", limitNumLabels );
1706  renderingElem.setAttribute( "maxNumLabels", maxNumLabels );
1707  renderingElem.setAttribute( "obstacle", obstacle );
1708  renderingElem.setAttribute( "obstacleFactor", obstacleFactor );
1709  renderingElem.setAttribute( "obstacleType", static_cast< unsigned int >( obstacleType ) );
1710  renderingElem.setAttribute( "zIndex", zIndex );
1711 
1712  QDomElement ddElem = doc.createElement( "data-defined" );
1713  writeDataDefinedPropertyMap( nullptr, &ddElem, dataDefinedProperties );
1714 
1715  QDomElement elem = doc.createElement( "settings" );
1716  elem.appendChild( textStyleElem );
1717  elem.appendChild( textFormatElem );
1718  elem.appendChild( textBufferElem );
1719  elem.appendChild( backgroundElem );
1720  elem.appendChild( shadowElem );
1721  elem.appendChild( placementElem );
1722  elem.appendChild( renderingElem );
1723  elem.appendChild( ddElem );
1724  return elem;
1725 }
1726 
1728  bool active, bool useExpr, const QString& expr, const QString& field )
1729 {
1730  bool defaultVals = ( !active && !useExpr && expr.isEmpty() && field.isEmpty() );
1731 
1732  if ( dataDefinedProperties.contains( p ) )
1733  {
1735  if ( it != dataDefinedProperties.constEnd() )
1736  {
1737  QgsDataDefined* dd = it.value();
1738  dd->setActive( active );
1739  dd->setExpressionString( expr );
1740  dd->setField( field );
1741  dd->setUseExpression( useExpr );
1742  }
1743  }
1744  else if ( !defaultVals )
1745  {
1746  QgsDataDefined* dd = new QgsDataDefined( active, useExpr, expr, field );
1747  dataDefinedProperties.insert( p, dd );
1748  }
1749 }
1750 
1752 {
1754  if ( it != dataDefinedProperties.end() )
1755  {
1756  delete( it.value() );
1758  }
1759 }
1760 
1762 {
1763  qDeleteAll( dataDefinedProperties );
1765 }
1766 
1768 {
1769  // TODO: update or remove this when project settings for labeling are migrated to better XML layout
1770  QString newValue = value;
1771  if ( !value.isEmpty() && !value.contains( "~~" ) )
1772  {
1773  QStringList values;
1774  values << "1"; // all old-style values are active if not empty
1775  values << "0";
1776  values << "";
1777  values << value; // all old-style values are only field names
1778  newValue = values.join( "~~" );
1779  }
1780 
1781  return newValue;
1782 }
1783 
1785 {
1787  return nullptr;
1788 
1790  if ( it != dataDefinedProperties.constEnd() )
1791  {
1792  return it.value();
1793  }
1794  return nullptr;
1795 }
1796 
1798 {
1801  if ( it != dataDefinedProperties.constEnd() )
1802  {
1803  return it.value()->toMap();
1804  }
1805  return map;
1806 }
1807 
1809 {
1811  {
1812  return QVariant();
1813  }
1814 
1815  //try to keep < 2.12 API - handle no passed expression context
1817  if ( !context )
1818  {
1819  scopedEc.reset( new QgsExpressionContext() );
1820  scopedEc->setFeature( f );
1821  scopedEc->setFields( fields );
1822  }
1823  const QgsExpressionContext* ec = context ? context : scopedEc.data();
1824 
1825  QgsDataDefined* dd = nullptr;
1827  if ( it != dataDefinedProperties.constEnd() )
1828  {
1829  dd = it.value();
1830  }
1831 
1832  if ( !dd )
1833  {
1834  return QVariant();
1835  }
1836 
1837  if ( !dd->isActive() )
1838  {
1839  return QVariant();
1840  }
1841 
1842  QVariant result = QVariant();
1843  bool useExpression = dd->useExpression();
1844  QString field = dd->field();
1845 
1846  //QgsDebugMsgLevel( QString( "isActive:" ) + isActive ? "1" : "0", 4 );
1847  //QgsDebugMsgLevel( QString( "useExpression:" ) + useExpression ? "1" : "0", 4 );
1848  //QgsDebugMsgLevel( QString( "expression:" ) + dd->expressionString(), 4 );
1849  //QgsDebugMsgLevel( QString( "field:" ) + field, 4 );
1850 
1851  if ( useExpression && dd->expressionIsPrepared() )
1852  {
1853  QgsExpression* expr = dd->expression();
1854  //QgsDebugMsgLevel( QString( "expr columns:" ) + expr->referencedColumns().join( "," ), 4 );
1855 
1856  result = expr->evaluate( ec );
1857  if ( expr->hasEvalError() )
1858  {
1859  QgsDebugMsgLevel( QString( "Evaluate error:" ) + expr->evalErrorString(), 4 );
1860  return QVariant();
1861  }
1862  }
1863  else if ( !useExpression && !field.isEmpty() )
1864  {
1865  // use direct attribute access instead of evaluating "field" expression (much faster)
1866  int indx = fields.indexFromName( field );
1867  if ( indx != -1 )
1868  {
1869  result = f.attribute( indx );
1870  }
1871  }
1872  return result;
1873 }
1874 
1876 {
1877  // null passed-around QVariant
1878  exprVal.clear();
1880  return false;
1881 
1882  //try to keep < 2.12 API - handle no passed expression context
1884  if ( !context )
1885  {
1886  scopedEc.reset( new QgsExpressionContext() );
1887  scopedEc->setFeature( *mCurFeat );
1888  scopedEc->setFields( mCurFields );
1889  }
1890  QgsExpressionContext* ec = context ? context : scopedEc.data();
1891 
1892  ec->setOriginalValueVariable( originalValue );
1893  QVariant result = dataDefinedValue( p, *mCurFeat, mCurFields, ec );
1894 
1895  if ( result.isValid() && !result.isNull() )
1896  {
1897  //QgsDebugMsgLevel( QString( "result type:" ) + QString( result.typeName() ), 4 );
1898  //QgsDebugMsgLevel( QString( "result string:" ) + result.toString(), 4 );
1899  exprVal = result;
1900  return true;
1901  }
1902 
1903  return false;
1904 }
1905 
1907 {
1909  return false;
1910 
1911  bool isActive = false;
1912 
1914  if ( it != dataDefinedProperties.constEnd() )
1915  {
1916  isActive = it.value()->isActive();
1917  }
1918 
1919  return isActive;
1920 }
1921 
1923 {
1925  return false;
1926 
1927  bool useExpression = false;
1929  if ( it != dataDefinedProperties.constEnd() )
1930  {
1931  useExpression = it.value()->useExpression();
1932  }
1933 
1934  return useExpression;
1935 }
1936 
1937 bool QgsPalLayerSettings::checkMinimumSizeMM( const QgsRenderContext& ct, const QgsGeometry* geom, double minSize ) const
1938 {
1939  return QgsPalLabeling::checkMinimumSizeMM( ct, geom, minSize );
1940 }
1941 
1942 void QgsPalLayerSettings::calculateLabelSize( const QFontMetricsF* fm, QString text, double& labelX, double& labelY, QgsFeature* f, QgsRenderContext *context )
1943 {
1944  if ( !fm || !f )
1945  {
1946  return;
1947  }
1948 
1949  //try to keep < 2.12 API - handle no passed render context
1951  if ( !context )
1952  {
1953  scopedRc.reset( new QgsRenderContext() );
1954  if ( f )
1955  scopedRc->expressionContext().setFeature( *f );
1956  }
1957  QgsRenderContext* rc = context ? context : scopedRc.data();
1958 
1959  QString wrapchr = wrapChar;
1960  double multilineH = multilineHeight;
1961 
1962  bool addDirSymb = addDirectionSymbol;
1963  QString leftDirSymb = leftDirectionSymbol;
1964  QString rightDirSymb = rightDirectionSymbol;
1966 
1967  if ( f == mCurFeat ) // called internally, use any stored data defined values
1968  {
1969  if ( dataDefinedValues.contains( QgsPalLayerSettings::MultiLineWrapChar ) )
1970  {
1971  wrapchr = dataDefinedValues.value( QgsPalLayerSettings::MultiLineWrapChar ).toString();
1972  }
1973 
1974  if ( dataDefinedValues.contains( QgsPalLayerSettings::MultiLineHeight ) )
1975  {
1976  multilineH = dataDefinedValues.value( QgsPalLayerSettings::MultiLineHeight ).toDouble();
1977  }
1978 
1979  if ( dataDefinedValues.contains( QgsPalLayerSettings::DirSymbDraw ) )
1980  {
1981  addDirSymb = dataDefinedValues.value( QgsPalLayerSettings::DirSymbDraw ).toBool();
1982  }
1983 
1984  if ( addDirSymb )
1985  {
1986 
1987  if ( dataDefinedValues.contains( QgsPalLayerSettings::DirSymbLeft ) )
1988  {
1989  leftDirSymb = dataDefinedValues.value( QgsPalLayerSettings::DirSymbLeft ).toString();
1990  }
1991  if ( dataDefinedValues.contains( QgsPalLayerSettings::DirSymbRight ) )
1992  {
1993  rightDirSymb = dataDefinedValues.value( QgsPalLayerSettings::DirSymbRight ).toString();
1994  }
1995 
1996  if ( dataDefinedValues.contains( QgsPalLayerSettings::DirSymbPlacement ) )
1997  {
1998  placeDirSymb = static_cast< QgsPalLayerSettings::DirectionSymbols >( dataDefinedValues.value( QgsPalLayerSettings::DirSymbPlacement ).toInt() );
1999  }
2000 
2001  }
2002 
2003  }
2004  else // called externally with passed-in feature, evaluate data defined
2005  {
2008  if ( exprVal.isValid() )
2009  {
2010  wrapchr = exprVal.toString();
2011  }
2012  exprVal.clear();
2013  rc->expressionContext().setOriginalValueVariable( multilineH );
2015  if ( exprVal.isValid() )
2016  {
2017  bool ok;
2018  double size = exprVal.toDouble( &ok );
2019  if ( ok )
2020  {
2021  multilineH = size;
2022  }
2023  }
2024 
2025  exprVal.clear();
2026  rc->expressionContext().setOriginalValueVariable( addDirSymb );
2028  if ( exprVal.isValid() )
2029  {
2030  addDirSymb = exprVal.toBool();
2031  }
2032 
2033  if ( addDirSymb ) // don't do extra evaluations if not adding a direction symbol
2034  {
2035  exprVal.clear();
2036  rc->expressionContext().setOriginalValueVariable( leftDirSymb );
2038  if ( exprVal.isValid() )
2039  {
2040  leftDirSymb = exprVal.toString();
2041  }
2042  exprVal.clear();
2043  rc->expressionContext().setOriginalValueVariable( rightDirSymb );
2045  if ( exprVal.isValid() )
2046  {
2047  rightDirSymb = exprVal.toString();
2048  }
2049  exprVal.clear();
2050  rc->expressionContext().setOriginalValueVariable( static_cast< int >( placeDirSymb ) );
2052  if ( exprVal.isValid() )
2053  {
2054  bool ok;
2055  int enmint = exprVal.toInt( &ok );
2056  if ( ok )
2057  {
2058  placeDirSymb = static_cast< QgsPalLayerSettings::DirectionSymbols >( enmint );
2059  }
2060  }
2061  }
2062 
2063  }
2064 
2065  if ( wrapchr.isEmpty() )
2066  {
2067  wrapchr = QLatin1String( "\n" ); // default to new line delimiter
2068  }
2069 
2070  //consider the space needed for the direction symbol
2071  if ( addDirSymb && placement == QgsPalLayerSettings::Line
2072  && ( !leftDirSymb.isEmpty() || !rightDirSymb.isEmpty() ) )
2073  {
2074  QString dirSym = leftDirSymb;
2075 
2076  if ( fm->width( rightDirSymb ) > fm->width( dirSym ) )
2077  dirSym = rightDirSymb;
2078 
2079  if ( placeDirSymb == QgsPalLayerSettings::SymbolLeftRight )
2080  {
2081  text.append( dirSym );
2082  }
2083  else
2084  {
2085  text.prepend( dirSym + QLatin1String( "\n" ) ); // SymbolAbove or SymbolBelow
2086  }
2087  }
2088 
2089  double w = 0.0, h = 0.0;
2090  QStringList multiLineSplit = QgsPalLabeling::splitToLines( text, wrapchr );
2091  int lines = multiLineSplit.size();
2092 
2093  double labelHeight = fm->ascent() + fm->descent(); // ignore +1 for baseline
2094 
2095  h += fm->height() + static_cast< double >(( lines - 1 ) * labelHeight * multilineH );
2096  h /= rasterCompressFactor;
2097 
2098  for ( int i = 0; i < lines; ++i )
2099  {
2100  double width = fm->width( multiLineSplit.at( i ) );
2101  if ( width > w )
2102  {
2103  w = width;
2104  }
2105  }
2106  w /= rasterCompressFactor;
2107 
2108 #if 0 // XXX strk
2109  QgsPoint ptSize = xform->toMapCoordinatesF( w, h );
2110  labelX = qAbs( ptSize.x() - ptZero.x() );
2111  labelY = qAbs( ptSize.y() - ptZero.y() );
2112 #else
2113  double uPP = xform->mapUnitsPerPixel();
2114  labelX = w * uPP;
2115  labelY = h * uPP;
2116 #endif
2117 }
2118 
2119 void QgsPalLayerSettings::registerFeature( QgsFeature& f, QgsRenderContext &context, QgsLabelFeature** labelFeature , QgsGeometry* obstacleGeometry )
2120 {
2121  // either used in QgsPalLabeling (palLayer is set) or in QgsLabelingEngineV2 (labelFeature is set)
2122  Q_ASSERT( labelFeature );
2123 
2124  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
2125  mCurFeat = &f;
2126 
2127  // data defined is obstacle? calculate this first, to avoid wasting time working with obstacles we don't require
2128  bool isObstacle = obstacle; // start with layer default
2130  {
2131  isObstacle = exprVal.toBool();
2132  }
2133 
2134  if ( !drawLabels )
2135  {
2136  if ( isObstacle )
2137  {
2138  registerObstacleFeature( f, context, labelFeature, obstacleGeometry );
2139  }
2140  return;
2141  }
2142 
2143 // mCurFields = &layer->pendingFields();
2144 
2145  // store data defined-derived values for later adding to label feature for use during rendering
2146  dataDefinedValues.clear();
2147 
2148  // data defined show label? defaults to show label if not 0
2150  {
2151  bool showLabel = dataDefinedEvaluate( QgsPalLayerSettings::Show, exprVal, &context.expressionContext(), true );
2152  showLabel &= exprVal.toBool();
2153  QgsDebugMsgLevel( QString( "exprVal Show:%1" ).arg( showLabel ? "true" : "false" ), 4 );
2154  if ( !showLabel )
2155  {
2156  return;
2157  }
2158  }
2159 
2160  // data defined scale visibility?
2161  bool useScaleVisibility = scaleVisibility;
2163  {
2164  QgsDebugMsgLevel( QString( "exprVal ScaleVisibility:%1" ).arg( exprVal.toBool() ? "true" : "false" ), 4 );
2165  useScaleVisibility = exprVal.toBool();
2166  }
2167 
2168  if ( useScaleVisibility )
2169  {
2170  // data defined min scale?
2171  double minScale = scaleMin;
2173  {
2174  QgsDebugMsgLevel( QString( "exprVal MinScale:%1" ).arg( exprVal.toDouble() ), 4 );
2175  bool conversionOk;
2176  double mins = exprVal.toDouble( &conversionOk );
2177  if ( conversionOk )
2178  {
2179  minScale = mins;
2180  }
2181  }
2182 
2183  // scales closer than 1:1
2184  if ( minScale < 0 )
2185  {
2186  minScale = 1 / qAbs( minScale );
2187  }
2188 
2189  if ( !qgsDoubleNear( minScale, 0.0 ) && context.rendererScale() < minScale )
2190  {
2191  return;
2192  }
2193 
2194  // data defined max scale?
2195  double maxScale = scaleMax;
2197  {
2198  QgsDebugMsgLevel( QString( "exprVal MaxScale:%1" ).arg( exprVal.toDouble() ), 4 );
2199  bool conversionOk;
2200  double maxs = exprVal.toDouble( &conversionOk );
2201  if ( conversionOk )
2202  {
2203  maxScale = maxs;
2204  }
2205  }
2206 
2207  // scales closer than 1:1
2208  if ( maxScale < 0 )
2209  {
2210  maxScale = 1 / qAbs( maxScale );
2211  }
2212 
2213  if ( !qgsDoubleNear( maxScale, 0.0 ) && context.rendererScale() > maxScale )
2214  {
2215  return;
2216  }
2217  }
2218 
2219  QFont labelFont = textFont;
2220  // labelFont will be added to label feature for use during label painting
2221 
2222  // data defined font units?
2225  {
2226  QString units = exprVal.toString().trimmed();
2227  QgsDebugMsgLevel( QString( "exprVal Font units:%1" ).arg( units ), 4 );
2228  if ( !units.isEmpty() )
2229  {
2230  fontunits = _decodeUnits( units );
2231  }
2232  }
2233 
2234  //data defined label size?
2235  double fontSize = labelFont.pointSizeF(); // font size doesn't have its own class data member
2236  if ( dataDefinedEvaluate( QgsPalLayerSettings::Size, exprVal, &context.expressionContext(), fontSize ) )
2237  {
2238  QgsDebugMsgLevel( QString( "exprVal Size:%1" ).arg( exprVal.toDouble() ), 4 );
2239  bool ok;
2240  double size = exprVal.toDouble( &ok );
2241  if ( ok )
2242  {
2243  fontSize = size;
2244  }
2245  }
2246  if ( fontSize <= 0.0 )
2247  {
2248  return;
2249  }
2250 
2251  int fontPixelSize = sizeToPixel( fontSize, context, fontunits, true, fontSizeMapUnitScale );
2252  // don't try to show font sizes less than 1 pixel (Qt complains)
2253  if ( fontPixelSize < 1 )
2254  {
2255  return;
2256  }
2257  labelFont.setPixelSize( fontPixelSize );
2258 
2259  // NOTE: labelFont now always has pixelSize set, so pointSize or pointSizeF might return -1
2260 
2261  // defined 'minimum/maximum pixel font size'?
2262  if ( fontunits == QgsPalLayerSettings::MapUnits )
2263  {
2264  bool useFontLimitPixelSize = fontLimitPixelSize;
2266  {
2267  QgsDebugMsgLevel( QString( "exprVal FontLimitPixel:%1" ).arg( exprVal.toBool() ? "true" : "false" ), 4 );
2268  useFontLimitPixelSize = exprVal.toBool();
2269  }
2270 
2271  if ( useFontLimitPixelSize )
2272  {
2273  int fontMinPixel = fontMinPixelSize;
2275  {
2276  bool ok;
2277  int sizeInt = exprVal.toInt( &ok );
2278  QgsDebugMsgLevel( QString( "exprVal FontMinPixel:%1" ).arg( sizeInt ), 4 );
2279  if ( ok )
2280  {
2281  fontMinPixel = sizeInt;
2282  }
2283  }
2284 
2285  int fontMaxPixel = fontMaxPixelSize;
2287  {
2288  bool ok;
2289  int sizeInt = exprVal.toInt( &ok );
2290  QgsDebugMsgLevel( QString( "exprVal FontMaxPixel:%1" ).arg( sizeInt ), 4 );
2291  if ( ok )
2292  {
2293  fontMaxPixel = sizeInt;
2294  }
2295  }
2296 
2297  if ( fontMinPixel > labelFont.pixelSize() || labelFont.pixelSize() > fontMaxPixel )
2298  {
2299  return;
2300  }
2301  }
2302  }
2303 
2304  // NOTE: the following parsing functions calculate and store any data defined values for later use in QgsPalLabeling::drawLabeling
2305  // this is done to provide clarity, and because such parsing is not directly related to PAL feature registration calculations
2306 
2307  // calculate rest of font attributes and store any data defined values
2308  // this is done here for later use in making label backgrounds part of collision management (when implemented)
2309  labelFont.setCapitalization( QFont::MixedCase ); // reset this - we don't use QFont's handling as it breaks with curved labels
2310  parseTextStyle( labelFont, fontunits, context );
2311  parseTextFormatting( context );
2312  parseTextBuffer( context );
2313  parseShapeBackground( context );
2314  parseDropShadow( context );
2315 
2316  QString labelText;
2317 
2318  // Check to see if we are a expression string.
2319  if ( isExpression )
2320  {
2322  if ( exp->hasParserError() )
2323  {
2324  QgsDebugMsgLevel( QString( "Expression parser error:%1" ).arg( exp->parserErrorString() ), 4 );
2325  return;
2326  }
2327  exp->setScale( context.rendererScale() );
2328 // QVariant result = exp->evaluate( &f, layer->pendingFields() );
2329  QVariant result = exp->evaluate( &context.expressionContext() ); // expression prepared in QgsPalLabeling::prepareLayer()
2330  if ( exp->hasEvalError() )
2331  {
2332  QgsDebugMsgLevel( QString( "Expression parser eval error:%1" ).arg( exp->evalErrorString() ), 4 );
2333  return;
2334  }
2335  labelText = result.isNull() ? "" : result.toString();
2336  }
2337  else
2338  {
2339  const QVariant &v = f.attribute( fieldIndex );
2340  labelText = v.isNull() ? "" : v.toString();
2341  }
2342 
2343  // apply text replacements
2344  if ( useSubstitutions )
2345  {
2346  labelText = substitutions.process( labelText );
2347  }
2348 
2349  // apply capitalization
2351  // maintain API - capitalization may have been set in textFont
2352  if ( textFont.capitalization() != QFont::MixedCase )
2353  {
2354  capitalization = static_cast< QgsStringUtils::Capitalization >( textFont.capitalization() );
2355  }
2356  // data defined font capitalization?
2358  {
2359  QString fcase = exprVal.toString().trimmed();
2360  QgsDebugMsgLevel( QString( "exprVal FontCase:%1" ).arg( fcase ), 4 );
2361 
2362  if ( !fcase.isEmpty() )
2363  {
2364  if ( fcase.compare( "NoChange", Qt::CaseInsensitive ) == 0 )
2365  {
2366  capitalization = QgsStringUtils::MixedCase;
2367  }
2368  else if ( fcase.compare( "Upper", Qt::CaseInsensitive ) == 0 )
2369  {
2370  capitalization = QgsStringUtils::AllUppercase;
2371  }
2372  else if ( fcase.compare( "Lower", Qt::CaseInsensitive ) == 0 )
2373  {
2374  capitalization = QgsStringUtils::AllLowercase;
2375  }
2376  else if ( fcase.compare( "Capitalize", Qt::CaseInsensitive ) == 0 )
2377  {
2379  }
2380  }
2381  }
2382  labelText = QgsStringUtils::capitalize( labelText, capitalization );
2383 
2384  // data defined format numbers?
2385  bool formatnum = formatNumbers;
2387  {
2388  formatnum = exprVal.toBool();
2389  QgsDebugMsgLevel( QString( "exprVal NumFormat:%1" ).arg( formatnum ? "true" : "false" ), 4 );
2390  }
2391 
2392  // format number if label text is coercible to a number
2393  if ( formatnum )
2394  {
2395  // data defined decimal places?
2396  int decimalPlaces = decimals;
2398  {
2399  bool ok;
2400  int dInt = exprVal.toInt( &ok );
2401  QgsDebugMsgLevel( QString( "exprVal NumDecimals:%1" ).arg( dInt ), 4 );
2402  if ( ok && dInt > 0 ) // needs to be positive
2403  {
2404  decimalPlaces = dInt;
2405  }
2406  }
2407 
2408  // data defined plus sign?
2409  bool signPlus = plusSign;
2411  {
2412  signPlus = exprVal.toBool();
2413  QgsDebugMsgLevel( QString( "exprVal NumPlusSign:%1" ).arg( signPlus ? "true" : "false" ), 4 );
2414  }
2415 
2416  QVariant textV( labelText );
2417  bool ok;
2418  double d = textV.toDouble( &ok );
2419  if ( ok )
2420  {
2421  QString numberFormat;
2422  if ( d > 0 && signPlus )
2423  {
2424  numberFormat.append( '+' );
2425  }
2426  numberFormat.append( "%1" );
2427  labelText = numberFormat.arg( d, 0, 'f', decimalPlaces );
2428  }
2429  }
2430 
2431  // NOTE: this should come AFTER any option that affects font metrics
2432  QScopedPointer<QFontMetricsF> labelFontMetrics( new QFontMetricsF( labelFont ) );
2433  double labelX, labelY; // will receive label size
2434  calculateLabelSize( labelFontMetrics.data(), labelText, labelX, labelY, mCurFeat, &context );
2435 
2436 
2437  // maximum angle between curved label characters (hardcoded defaults used in QGIS <2.0)
2438  //
2439  double maxcharanglein = 20.0; // range 20.0-60.0
2440  double maxcharangleout = -20.0; // range 20.0-95.0
2441 
2443  {
2444  maxcharanglein = maxCurvedCharAngleIn;
2445  maxcharangleout = maxCurvedCharAngleOut;
2446 
2447  //data defined maximum angle between curved label characters?
2449  {
2450  QString ptstr = exprVal.toString().trimmed();
2451  QgsDebugMsgLevel( QString( "exprVal CurvedCharAngleInOut:%1" ).arg( ptstr ), 4 );
2452 
2453  if ( !ptstr.isEmpty() )
2454  {
2455  QPointF maxcharanglePt = QgsSymbolLayerV2Utils::decodePoint( ptstr );
2456  maxcharanglein = qBound( 20.0, static_cast< double >( maxcharanglePt.x() ), 60.0 );
2457  maxcharangleout = qBound( 20.0, static_cast< double >( maxcharanglePt.y() ), 95.0 );
2458  }
2459  }
2460  // make sure maxcharangleout is always negative
2461  maxcharangleout = -( qAbs( maxcharangleout ) );
2462  }
2463 
2464  // data defined centroid whole or clipped?
2465  bool wholeCentroid = centroidWhole;
2467  {
2468  QString str = exprVal.toString().trimmed();
2469  QgsDebugMsgLevel( QString( "exprVal CentroidWhole:%1" ).arg( str ), 4 );
2470 
2471  if ( !str.isEmpty() )
2472  {
2473  if ( str.compare( "Visible", Qt::CaseInsensitive ) == 0 )
2474  {
2475  wholeCentroid = false;
2476  }
2477  else if ( str.compare( "Whole", Qt::CaseInsensitive ) == 0 )
2478  {
2479  wholeCentroid = true;
2480  }
2481  }
2482  }
2483 
2484  const QgsGeometry* geom = f.constGeometry();
2485  if ( !geom )
2486  {
2487  return;
2488  }
2489 
2490  // simplify?
2491  const QgsVectorSimplifyMethod &simplifyMethod = context.vectorSimplifyMethod();
2492  QScopedPointer<QgsGeometry> scopedClonedGeom;
2493  if ( simplifyMethod.simplifyHints() != QgsVectorSimplifyMethod::NoSimplification && simplifyMethod.forceLocalOptimization() )
2494  {
2495  int simplifyHints = simplifyMethod.simplifyHints() | QgsMapToPixelSimplifier::SimplifyEnvelope;
2497  QgsGeometry* g = new QgsGeometry( *geom );
2498 
2499  if ( QgsMapToPixelSimplifier::simplifyGeometry( g, simplifyHints, simplifyMethod.tolerance(), simplifyAlgorithm ) )
2500  {
2501  geom = g;
2502  scopedClonedGeom.reset( g );
2503  }
2504  else
2505  {
2506  delete g;
2507  }
2508  }
2509 
2510  // whether we're going to create a centroid for polygon
2511  bool centroidPoly = (( placement == QgsPalLayerSettings::AroundPoint
2513  && geom->type() == QGis::Polygon );
2514 
2515  // CLIP the geometry if it is bigger than the extent
2516  // don't clip if centroid is requested for whole feature
2517  bool doClip = false;
2518  if ( !centroidPoly || !wholeCentroid )
2519  {
2520  doClip = true;
2521  }
2522 
2523  // if using fitInPolygonOnly option, generate the permissible zone (must happen before geometry is modified - eg
2524  // as a result of using perimeter based labeling and the geometry is converted to a boundary)
2525  QgsGeometry permissibleZone;
2526  if ( geom->type() == QGis::Polygon && fitInPolygonOnly )
2527  {
2528  permissibleZone = *geom;
2529  if ( QgsPalLabeling::geometryRequiresPreparation( &permissibleZone, context, ct, doClip ? extentGeom : nullptr ) )
2530  {
2531  QgsGeometry* preparedZone = QgsPalLabeling::prepareGeometry( &permissibleZone, context, ct, doClip ? extentGeom : nullptr );
2532  if ( preparedZone )
2533  {
2534  // QgsPalLabeling::prepareGeometry may return NULL on
2535  // QgsCsException exception
2536  permissibleZone = *preparedZone;
2537  delete preparedZone;
2538  }
2539  }
2540  }
2541 
2542  // if using perimeter based labeling for polygons, get the polygon's
2543  // linear boundary and use that for the label geometry
2544  if (( geom->type() == QGis::Polygon )
2545  && ( placement == Line || placement == PerimeterCurved ) )
2546  {
2547  QgsGeometry* boundaryGeom = new QgsGeometry( geom->geometry()->boundary() );
2548  geom = boundaryGeom;
2549  scopedClonedGeom.reset( boundaryGeom );
2550  }
2551 
2552  const GEOSGeometry* geos_geom = nullptr;
2553  const QgsGeometry* preparedGeom = geom;
2554  QScopedPointer<QgsGeometry> scopedPreparedGeom;
2555  if ( QgsPalLabeling::geometryRequiresPreparation( geom, context, ct, doClip ? extentGeom : nullptr ) )
2556  {
2557  scopedPreparedGeom.reset( QgsPalLabeling::prepareGeometry( geom, context, ct, doClip ? extentGeom : nullptr ) );
2558 
2559  if ( !scopedPreparedGeom.data() )
2560  return;
2561  preparedGeom = scopedPreparedGeom.data();
2562  geos_geom = scopedPreparedGeom.data()->asGeos();
2563  }
2564  else
2565  {
2566  geos_geom = geom->asGeos();
2567  }
2568  const GEOSGeometry* geosObstacleGeom = nullptr;
2569  QScopedPointer<QgsGeometry> scopedObstacleGeom;
2570  if ( isObstacle )
2571  {
2572  if ( obstacleGeometry && QgsPalLabeling::geometryRequiresPreparation( obstacleGeometry, context, ct, doClip ? extentGeom : nullptr ) )
2573  {
2574  scopedObstacleGeom.reset( QgsPalLabeling::prepareGeometry( obstacleGeometry, context, ct, doClip ? extentGeom : nullptr ) );
2575  obstacleGeometry = scopedObstacleGeom.data();
2576  }
2577  if ( obstacleGeometry )
2578  {
2579  geosObstacleGeom = obstacleGeometry->asGeos();
2580  }
2581  }
2582 
2583  if ( minFeatureSize > 0 && !checkMinimumSizeMM( context, preparedGeom, minFeatureSize ) )
2584  return;
2585 
2586  if ( !geos_geom )
2587  return; // invalid geometry
2588 
2589  // likelihood exists label will be registered with PAL and may be drawn
2590  // check if max number of features to label (already registered with PAL) has been reached
2591  // Debug output at end of QgsPalLabeling::drawLabeling(), when deleting temp geometries
2592  if ( limitNumLabels )
2593  {
2594  if ( !maxNumLabels )
2595  {
2596  return;
2597  }
2598  if ( mFeatsRegPal >= maxNumLabels )
2599  {
2600  return;
2601  }
2602 
2603  int divNum = static_cast< int >(( static_cast< double >( mFeaturesToLabel ) / maxNumLabels ) + 0.5 );
2604  if ( divNum && ( mFeatsRegPal == static_cast< int >( mFeatsSendingToPal / divNum ) ) )
2605  {
2606  mFeatsSendingToPal += 1;
2607  if ( divNum && mFeatsSendingToPal % divNum )
2608  {
2609  return;
2610  }
2611  }
2612  }
2613 
2614  GEOSGeometry* geos_geom_clone = GEOSGeom_clone_r( QgsGeometry::getGEOSHandler(), geos_geom );
2615  GEOSGeometry* geosObstacleGeomClone = nullptr;
2616  if ( geosObstacleGeom )
2617  {
2618  geosObstacleGeomClone = GEOSGeom_clone_r( QgsGeometry::getGEOSHandler(), geosObstacleGeom );
2619  }
2620 
2621 
2622  //data defined position / alignment / rotation?
2623  bool dataDefinedPosition = false;
2624  bool layerDefinedRotation = false;
2625  bool dataDefinedRotation = false;
2626  double xPos = 0.0, yPos = 0.0, angle = 0.0;
2627  bool ddXPos = false, ddYPos = false;
2628  double quadOffsetX = 0.0, quadOffsetY = 0.0;
2629  double offsetX = 0.0, offsetY = 0.0;
2630 
2631  //data defined quadrant offset?
2632  bool ddFixedQuad = false;
2633  QuadrantPosition quadOff = quadOffset;
2634  if ( dataDefinedEvaluate( QgsPalLayerSettings::OffsetQuad, exprVal, &context.expressionContext(), static_cast< int >( quadOff ) ) )
2635  {
2636  bool ok;
2637  int quadInt = exprVal.toInt( &ok );
2638  QgsDebugMsgLevel( QString( "exprVal OffsetQuad:%1" ).arg( quadInt ), 4 );
2639  if ( ok && 0 <= quadInt && quadInt <= 8 )
2640  {
2641  quadOff = static_cast< QuadrantPosition >( quadInt );
2642  ddFixedQuad = true;
2643  }
2644  }
2645 
2646  // adjust quadrant offset of labels
2647  switch ( quadOff )
2648  {
2649  case QuadrantAboveLeft:
2650  quadOffsetX = -1.0;
2651  quadOffsetY = 1.0;
2652  break;
2653  case QuadrantAbove:
2654  quadOffsetX = 0.0;
2655  quadOffsetY = 1.0;
2656  break;
2657  case QuadrantAboveRight:
2658  quadOffsetX = 1.0;
2659  quadOffsetY = 1.0;
2660  break;
2661  case QuadrantLeft:
2662  quadOffsetX = -1.0;
2663  quadOffsetY = 0.0;
2664  break;
2665  case QuadrantRight:
2666  quadOffsetX = 1.0;
2667  quadOffsetY = 0.0;
2668  break;
2669  case QuadrantBelowLeft:
2670  quadOffsetX = -1.0;
2671  quadOffsetY = -1.0;
2672  break;
2673  case QuadrantBelow:
2674  quadOffsetX = 0.0;
2675  quadOffsetY = -1.0;
2676  break;
2677  case QuadrantBelowRight:
2678  quadOffsetX = 1.0;
2679  quadOffsetY = -1.0;
2680  break;
2681  case QuadrantOver:
2682  default:
2683  break;
2684  }
2685 
2686  //data defined label offset?
2687  double xOff = xOffset;
2688  double yOff = yOffset;
2690  {
2691  QString ptstr = exprVal.toString().trimmed();
2692  QgsDebugMsgLevel( QString( "exprVal OffsetXY:%1" ).arg( ptstr ), 4 );
2693 
2694  if ( !ptstr.isEmpty() )
2695  {
2696  QPointF ddOffPt = QgsSymbolLayerV2Utils::decodePoint( ptstr );
2697  xOff = ddOffPt.x();
2698  yOff = ddOffPt.y();
2699  }
2700  }
2701 
2702  // data defined label offset units?
2703  bool offinmapunits = labelOffsetInMapUnits;
2705  {
2706  QString units = exprVal.toString().trimmed();
2707  QgsDebugMsgLevel( QString( "exprVal OffsetUnits:%1" ).arg( units ), 4 );
2708  if ( !units.isEmpty() )
2709  {
2710  offinmapunits = ( _decodeUnits( units ) == QgsPalLayerSettings::MapUnits );
2711  }
2712  }
2713 
2714  // adjust offset of labels to match chosen unit and map scale
2715  // offsets match those of symbology: -x = left, -y = up
2716  double mapUntsPerMM = labelOffsetMapUnitScale.computeMapUnitsPerPixel( context ) * context.scaleFactor();
2717  if ( !qgsDoubleNear( xOff, 0.0 ) )
2718  {
2719  offsetX = xOff; // must be positive to match symbology offset direction
2720  if ( !offinmapunits )
2721  {
2722  offsetX *= mapUntsPerMM; //convert offset from mm to map units
2723  }
2724  }
2725  if ( !qgsDoubleNear( yOff, 0.0 ) )
2726  {
2727  offsetY = -yOff; // must be negative to match symbology offset direction
2728  if ( !offinmapunits )
2729  {
2730  offsetY *= mapUntsPerMM; //convert offset from mm to map units
2731  }
2732  }
2733 
2734  // layer defined rotation?
2735  // only rotate non-pinned OverPoint placements until other placements are supported in pal::Feature
2737  {
2738  layerDefinedRotation = true;
2739  angle = angleOffset * M_PI / 180; // convert to radians
2740  }
2741 
2742  const QgsMapToPixel& m2p = context.mapToPixel();
2743  //data defined rotation?
2745  {
2746  bool ok;
2747  double rotD = exprVal.toDouble( &ok );
2748  QgsDebugMsgLevel( QString( "exprVal Rotation:%1" ).arg( rotD ), 4 );
2749  if ( ok )
2750  {
2751  dataDefinedRotation = true;
2752  // TODO: add setting to disable having data defined rotation follow
2753  // map rotation ?
2754  rotD -= m2p.mapRotation();
2755  angle = rotD * M_PI / 180.0;
2756  }
2757  }
2758 
2760  {
2761  if ( !exprVal.isNull() )
2762  xPos = exprVal.toDouble( &ddXPos );
2763  QgsDebugMsgLevel( QString( "exprVal PositionX:%1" ).arg( xPos ), 4 );
2764 
2766  {
2767  //data defined position. But field values could be NULL -> positions will be generated by PAL
2768  if ( !exprVal.isNull() )
2769  yPos = exprVal.toDouble( &ddYPos );
2770  QgsDebugMsgLevel( QString( "exprVal PositionY:%1" ).arg( yPos ), 4 );
2771 
2772  if ( ddXPos && ddYPos )
2773  {
2774  dataDefinedPosition = true;
2775  // layer rotation set, but don't rotate pinned labels unless data defined
2776  if ( layerDefinedRotation && !dataDefinedRotation )
2777  {
2778  angle = 0.0;
2779  }
2780 
2781  //x/y shift in case of alignment
2782  double xdiff = 0.0;
2783  double ydiff = 0.0;
2784 
2785  //horizontal alignment
2786  if ( dataDefinedEvaluate( QgsPalLayerSettings::Hali, exprVal, &context.expressionContext() ) )
2787  {
2788  QString haliString = exprVal.toString();
2789  QgsDebugMsgLevel( QString( "exprVal Hali:%1" ).arg( haliString ), 4 );
2790  if ( haliString.compare( "Center", Qt::CaseInsensitive ) == 0 )
2791  {
2792  xdiff -= labelX / 2.0;
2793  }
2794  else if ( haliString.compare( "Right", Qt::CaseInsensitive ) == 0 )
2795  {
2796  xdiff -= labelX;
2797  }
2798  }
2799 
2800  //vertical alignment
2801  if ( dataDefinedEvaluate( QgsPalLayerSettings::Vali, exprVal, &context.expressionContext() ) )
2802  {
2803  QString valiString = exprVal.toString();
2804  QgsDebugMsgLevel( QString( "exprVal Vali:%1" ).arg( valiString ), 4 );
2805 
2806  if ( valiString.compare( "Bottom", Qt::CaseInsensitive ) != 0 )
2807  {
2808  if ( valiString.compare( "Top", Qt::CaseInsensitive ) == 0 )
2809  {
2810  ydiff -= labelY;
2811  }
2812  else
2813  {
2814  double descentRatio = labelFontMetrics->descent() / labelFontMetrics->height();
2815  if ( valiString.compare( "Base", Qt::CaseInsensitive ) == 0 )
2816  {
2817  ydiff -= labelY * descentRatio;
2818  }
2819  else //'Cap' or 'Half'
2820  {
2821  double capHeightRatio = ( labelFontMetrics->boundingRect( 'H' ).height() + 1 + labelFontMetrics->descent() ) / labelFontMetrics->height();
2822  ydiff -= labelY * capHeightRatio;
2823  if ( valiString.compare( "Half", Qt::CaseInsensitive ) == 0 )
2824  {
2825  ydiff += labelY * ( capHeightRatio - descentRatio ) / 2.0;
2826  }
2827  }
2828  }
2829  }
2830  }
2831 
2832  if ( dataDefinedRotation )
2833  {
2834  //adjust xdiff and ydiff because the hali/vali point needs to be the rotation center
2835  double xd = xdiff * cos( angle ) - ydiff * sin( angle );
2836  double yd = xdiff * sin( angle ) + ydiff * cos( angle );
2837  xdiff = xd;
2838  ydiff = yd;
2839  }
2840 
2841  //project xPos and yPos from layer to map CRS, handle rotation
2842  QgsGeometry ddPoint( new QgsPointV2( xPos, yPos ) );
2843  if ( QgsPalLabeling::geometryRequiresPreparation( &ddPoint, context, ct ) )
2844  {
2845  QgsGeometry* newPoint = QgsPalLabeling::prepareGeometry( &ddPoint, context, ct );
2846  xPos = static_cast< QgsPointV2* >( newPoint->geometry() )->x();
2847  yPos = static_cast< QgsPointV2* >( newPoint->geometry() )->y();
2848  delete newPoint;
2849  }
2850 
2851  xPos += xdiff;
2852  yPos += ydiff;
2853  }
2854  else
2855  {
2856  // only rotate non-pinned OverPoint placements until other placements are supported in pal::Feature
2857  if ( dataDefinedRotation && placement != QgsPalLayerSettings::OverPoint )
2858  {
2859  angle = 0.0;
2860  }
2861  }
2862  }
2863  }
2864 
2865  // data defined always show?
2866  bool alwaysShow = false;
2868  {
2869  alwaysShow = exprVal.toBool();
2870  }
2871 
2872  // set repeat distance
2873  // data defined repeat distance?
2874  double repeatDist = repeatDistance;
2876  {
2877  bool ok;
2878  double distD = exprVal.toDouble( &ok );
2879  if ( ok )
2880  {
2881  repeatDist = distD;
2882  }
2883  }
2884 
2885  // data defined label-repeat distance units?
2886  bool repeatdistinmapunit = repeatDistanceUnit == QgsPalLayerSettings::MapUnits;
2888  {
2889  QString units = exprVal.toString().trimmed();
2890  QgsDebugMsgLevel( QString( "exprVal RepeatDistanceUnits:%1" ).arg( units ), 4 );
2891  if ( !units.isEmpty() )
2892  {
2893  repeatdistinmapunit = ( _decodeUnits( units ) == QgsPalLayerSettings::MapUnits );
2894  }
2895  }
2896 
2897  if ( !qgsDoubleNear( repeatDist, 0.0 ) )
2898  {
2899  if ( !repeatdistinmapunit )
2900  {
2901  repeatDist *= mapUntsPerMM; //convert repeat distance from mm to map units
2902  }
2903  }
2904 
2905  // feature to the layer
2906  QgsTextLabelFeature* lf = new QgsTextLabelFeature( f.id(), geos_geom_clone, QSizeF( labelX, labelY ) );
2907  mFeatsRegPal++;
2908 
2909  *labelFeature = lf;
2910  ( *labelFeature )->setHasFixedPosition( dataDefinedPosition );
2911  ( *labelFeature )->setFixedPosition( QgsPoint( xPos, yPos ) );
2912  // use layer-level defined rotation, but not if position fixed
2913  ( *labelFeature )->setHasFixedAngle( dataDefinedRotation || ( !dataDefinedPosition && !qgsDoubleNear( angle, 0.0 ) ) );
2914  ( *labelFeature )->setFixedAngle( angle );
2915  ( *labelFeature )->setQuadOffset( QPointF( quadOffsetX, quadOffsetY ) );
2916  ( *labelFeature )->setPositionOffset( QgsPoint( offsetX, offsetY ) );
2917  ( *labelFeature )->setOffsetType( offsetType );
2918  ( *labelFeature )->setAlwaysShow( alwaysShow );
2919  ( *labelFeature )->setRepeatDistance( repeatDist );
2920  ( *labelFeature )->setLabelText( labelText );
2921  ( *labelFeature )->setPermissibleZone( permissibleZone );
2922  if ( geosObstacleGeomClone )
2923  {
2924  ( *labelFeature )->setObstacleGeometry( geosObstacleGeomClone );
2925 
2926  if ( geom->type() == QGis::Point )
2927  {
2928  //register symbol size
2929  ( *labelFeature )->setSymbolSize( QSizeF( obstacleGeometry->boundingBox().width(),
2930  obstacleGeometry->boundingBox().height() ) );
2931  }
2932  }
2933 
2934  //set label's visual margin so that top visual margin is the leading, and bottom margin is the font's descent
2935  //this makes labels align to the font's baseline or highest character
2936  double topMargin = qMax( 0.25 * labelFontMetrics->ascent(), 0.0 );
2937  double bottomMargin = 1.0 + labelFontMetrics->descent();
2938  QgsLabelFeature::VisualMargin vm( topMargin, 0.0, bottomMargin, 0.0 );
2940  ( *labelFeature )->setVisualMargin( vm );
2941 
2942  // store the label's calculated font for later use during painting
2943  QgsDebugMsgLevel( QString( "PAL font stored definedFont: %1, Style: %2" ).arg( labelFont.toString(), labelFont.styleName() ), 4 );
2944  lf->setDefinedFont( labelFont );
2945 
2946  // TODO: only for placement which needs character info
2947  // account for any data defined font metrics adjustments
2949  labelFontMetrics.data(), xform, rasterCompressFactor, maxcharanglein, maxcharangleout );
2950  // for labelFeature the LabelInfo is passed to feat when it is registered
2951 
2952  // TODO: allow layer-wide feature dist in PAL...?
2953 
2954  // data defined label-feature distance?
2955  double distance = dist;
2957  {
2958  bool ok;
2959  double distD = exprVal.toDouble( &ok );
2960  if ( ok )
2961  {
2962  distance = distD;
2963  }
2964  }
2965 
2966  // data defined label-feature distance units?
2967  bool distinmapunit = distInMapUnits;
2969  {
2970  QString units = exprVal.toString().trimmed();
2971  QgsDebugMsgLevel( QString( "exprVal DistanceUnits:%1" ).arg( units ), 4 );
2972  if ( !units.isEmpty() )
2973  {
2974  distinmapunit = ( _decodeUnits( units ) == QgsPalLayerSettings::MapUnits );
2975  }
2976  }
2977 
2978  if ( distinmapunit ) //convert distance from mm/map units to pixels
2979  {
2980  distance /= distMapUnitScale.computeMapUnitsPerPixel( context );
2981  }
2982  else //mm
2983  {
2984  distance *= vectorScaleFactor;
2985  }
2986 
2987  // when using certain placement modes, we force a tiny minimum distance. This ensures that
2988  // candidates are created just offset from a border and avoids candidates being incorrectly flagged as colliding with neighbours
2990  {
2991  distance = qMax( distance, 1.0 );
2992  }
2993 
2994  if ( !qgsDoubleNear( distance, 0.0 ) )
2995  {
2996  double d = ptOne.distance( ptZero ) * distance;
2997  ( *labelFeature )->setDistLabel( d );
2998  }
2999 
3000  if ( ddFixedQuad )
3001  {
3002  ( *labelFeature )->setHasFixedQuadrant( true );
3003  }
3004 
3005  // data defined z-index?
3006  double z = zIndex;
3008  {
3009  bool ok;
3010  double zIndexD = exprVal.toDouble( &ok );
3011  if ( ok )
3012  {
3013  z = zIndexD;
3014  }
3015  }
3016  ( *labelFeature )->setZIndex( z );
3017 
3018  // data defined priority?
3020  {
3021  bool ok;
3022  double priorityD = exprVal.toDouble( &ok );
3023  if ( ok )
3024  {
3025  priorityD = qBound( 0.0, priorityD, 10.0 );
3026  priorityD = 1 - priorityD / 10.0; // convert 0..10 --> 1..0
3027  ( *labelFeature )->setPriority( priorityD );
3028  }
3029  }
3030 
3031  ( *labelFeature )->setIsObstacle( isObstacle );
3032 
3033  double featObstacleFactor = obstacleFactor;
3035  {
3036  bool ok;
3037  double factorD = exprVal.toDouble( &ok );
3038  if ( ok )
3039  {
3040  factorD = qBound( 0.0, factorD, 10.0 );
3041  factorD = factorD / 5.0 + 0.0001; // convert 0 -> 10 to 0.0001 -> 2.0
3042  featObstacleFactor = factorD;
3043  }
3044  }
3045  ( *labelFeature )->setObstacleFactor( featObstacleFactor );
3046 
3048  if ( positionOrder.isEmpty() )
3049  positionOrder = QgsPalLayerSettings::DEFAULT_PLACEMENT_ORDER;
3050 
3052  {
3053  QString orderD = exprVal.toString();
3054  positionOrder = QgsLabelingUtils::decodePredefinedPositionOrder( orderD );
3055  }
3056  ( *labelFeature )->setPredefinedPositionOrder( positionOrder );
3057 
3058  // add parameters for data defined labeling to label feature
3059  lf->setDataDefinedValues( dataDefinedValues );
3060 }
3061 
3062 void QgsPalLayerSettings::registerObstacleFeature( QgsFeature& f, QgsRenderContext &context, QgsLabelFeature** obstacleFeature, QgsGeometry* obstacleGeometry )
3063 {
3064  mCurFeat = &f;
3065 
3066  const QgsGeometry* geom = nullptr;
3067  if ( obstacleGeometry )
3068  {
3069  geom = obstacleGeometry;
3070  }
3071  else
3072  {
3073  geom = f.constGeometry();
3074  }
3075 
3076  if ( !geom )
3077  {
3078  return;
3079  }
3080 
3081  // simplify?
3082  const QgsVectorSimplifyMethod &simplifyMethod = context.vectorSimplifyMethod();
3083  QScopedPointer<QgsGeometry> scopedClonedGeom;
3084  if ( simplifyMethod.simplifyHints() != QgsVectorSimplifyMethod::NoSimplification && simplifyMethod.forceLocalOptimization() )
3085  {
3086  int simplifyHints = simplifyMethod.simplifyHints() | QgsMapToPixelSimplifier::SimplifyEnvelope;
3088  QgsGeometry* g = new QgsGeometry( *geom );
3089 
3090  if ( QgsMapToPixelSimplifier::simplifyGeometry( g, simplifyHints, simplifyMethod.tolerance(), simplifyAlgorithm ) )
3091  {
3092  geom = g;
3093  scopedClonedGeom.reset( g );
3094  }
3095  else
3096  {
3097  delete g;
3098  }
3099  }
3100 
3101  const GEOSGeometry* geos_geom = nullptr;
3102  QScopedPointer<QgsGeometry> scopedPreparedGeom;
3103 
3104  if ( QgsPalLabeling::geometryRequiresPreparation( geom, context, ct, extentGeom ) )
3105  {
3106  scopedPreparedGeom.reset( QgsPalLabeling::prepareGeometry( geom, context, ct, extentGeom ) );
3107  if ( !scopedPreparedGeom.data() )
3108  return;
3109  geos_geom = scopedPreparedGeom.data()->asGeos();
3110  }
3111  else
3112  {
3113  geos_geom = geom->asGeos();
3114  }
3115 
3116  if ( !geos_geom )
3117  return; // invalid geometry
3118 
3119  GEOSGeometry* geos_geom_clone;
3120  geos_geom_clone = GEOSGeom_clone_r( QgsGeometry::getGEOSHandler(), geos_geom );
3121 
3122  // feature to the layer
3123  *obstacleFeature = new QgsLabelFeature( f.id(), geos_geom_clone, QSizeF( 0, 0 ) );
3124  ( *obstacleFeature )->setIsObstacle( true );
3125  mFeatsRegPal++;
3126 }
3127 
3128 bool QgsPalLayerSettings::dataDefinedValEval( DataDefinedValueType valType,
3130  QVariant& exprVal, QgsExpressionContext& context, const QVariant& originalValue )
3131 {
3132  if ( dataDefinedEvaluate( p, exprVal, &context, originalValue ) )
3133  {
3134 #ifdef QGISDEBUG
3135  QString dbgStr = QString( "exprVal %1:" ).arg( mDataDefinedNames.value( p ).first ) + "%1"; // clazy:exclude=unused-non-trivial-variable
3136 #endif
3137 
3138  switch ( valType )
3139  {
3140  case DDBool:
3141  {
3142  bool bol = exprVal.toBool();
3143  QgsDebugMsgLevel( dbgStr.arg( bol ? "true" : "false" ), 4 );
3144  dataDefinedValues.insert( p, QVariant( bol ) );
3145  return true;
3146  }
3147  case DDInt:
3148  {
3149  bool ok;
3150  int size = exprVal.toInt( &ok );
3151  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
3152 
3153  if ( ok )
3154  {
3155  dataDefinedValues.insert( p, QVariant( size ) );
3156  return true;
3157  }
3158  return false;
3159  }
3160  case DDIntPos:
3161  {
3162  bool ok;
3163  int size = exprVal.toInt( &ok );
3164  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
3165 
3166  if ( ok && size > 0 )
3167  {
3168  dataDefinedValues.insert( p, QVariant( size ) );
3169  return true;
3170  }
3171  return false;
3172  }
3173  case DDDouble:
3174  {
3175  bool ok;
3176  double size = exprVal.toDouble( &ok );
3177  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
3178 
3179  if ( ok )
3180  {
3181  dataDefinedValues.insert( p, QVariant( size ) );
3182  return true;
3183  }
3184  return false;
3185  }
3186  case DDDoublePos:
3187  {
3188  bool ok;
3189  double size = exprVal.toDouble( &ok );
3190  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
3191 
3192  if ( ok && size > 0.0 )
3193  {
3194  dataDefinedValues.insert( p, QVariant( size ) );
3195  return true;
3196  }
3197  return false;
3198  }
3199  case DDRotation180:
3200  {
3201  bool ok;
3202  double rot = exprVal.toDouble( &ok );
3203  QgsDebugMsgLevel( dbgStr.arg( rot ), 4 );
3204  if ( ok )
3205  {
3206  if ( rot < -180.0 && rot >= -360 )
3207  {
3208  rot += 360;
3209  }
3210  if ( rot > 180.0 && rot <= 360 )
3211  {
3212  rot -= 360;
3213  }
3214  if ( rot >= -180 && rot <= 180 )
3215  {
3216  dataDefinedValues.insert( p, QVariant( rot ) );
3217  return true;
3218  }
3219  }
3220  return false;
3221  }
3222  case DDTransparency:
3223  {
3224  bool ok;
3225  int size = exprVal.toInt( &ok );
3226  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
3227  if ( ok && size >= 0 && size <= 100 )
3228  {
3229  dataDefinedValues.insert( p, QVariant( size ) );
3230  return true;
3231  }
3232  return false;
3233  }
3234  case DDString:
3235  {
3236  QString str = exprVal.toString(); // don't trim whitespace
3237  QgsDebugMsgLevel( dbgStr.arg( str ), 4 );
3238 
3239  dataDefinedValues.insert( p, QVariant( str ) ); // let it stay empty if it is
3240  return true;
3241  }
3242  case DDUnits:
3243  {
3244  QString unitstr = exprVal.toString().trimmed();
3245  QgsDebugMsgLevel( dbgStr.arg( unitstr ), 4 );
3246 
3247  if ( !unitstr.isEmpty() )
3248  {
3249  dataDefinedValues.insert( p, QVariant( static_cast< int >( _decodeUnits( unitstr ) ) ) );
3250  return true;
3251  }
3252  return false;
3253  }
3254  case DDColor:
3255  {
3256  QString colorstr = exprVal.toString().trimmed();
3257  QgsDebugMsgLevel( dbgStr.arg( colorstr ), 4 );
3258  QColor color = QgsSymbolLayerV2Utils::decodeColor( colorstr );
3259 
3260  if ( color.isValid() )
3261  {
3262  dataDefinedValues.insert( p, QVariant( color ) );
3263  return true;
3264  }
3265  return false;
3266  }
3267  case DDJoinStyle:
3268  {
3269  QString joinstr = exprVal.toString().trimmed();
3270  QgsDebugMsgLevel( dbgStr.arg( joinstr ), 4 );
3271 
3272  if ( !joinstr.isEmpty() )
3273  {
3274  dataDefinedValues.insert( p, QVariant( static_cast< int >( _decodePenJoinStyle( joinstr ) ) ) );
3275  return true;
3276  }
3277  return false;
3278  }
3279  case DDBlendMode:
3280  {
3281  QString blendstr = exprVal.toString().trimmed();
3282  QgsDebugMsgLevel( dbgStr.arg( blendstr ), 4 );
3283 
3284  if ( !blendstr.isEmpty() )
3285  {
3286  dataDefinedValues.insert( p, QVariant( static_cast< int >( QgsSymbolLayerV2Utils::decodeBlendMode( blendstr ) ) ) );
3287  return true;
3288  }
3289  return false;
3290  }
3291  case DDPointF:
3292  {
3293  QString ptstr = exprVal.toString().trimmed();
3294  QgsDebugMsgLevel( dbgStr.arg( ptstr ), 4 );
3295 
3296  if ( !ptstr.isEmpty() )
3297  {
3298  dataDefinedValues.insert( p, QVariant( QgsSymbolLayerV2Utils::decodePoint( ptstr ) ) );
3299  return true;
3300  }
3301  return false;
3302  }
3303  }
3304  }
3305  return false;
3306 }
3307 
3308 void QgsPalLayerSettings::parseTextStyle( QFont& labelFont,
3310  QgsRenderContext &context )
3311 {
3312  // NOTE: labelFont already has pixelSize set, so pointSize or pointSizeF might return -1
3313 
3314  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
3315 
3316  // Two ways to generate new data defined font:
3317  // 1) Family + [bold] + [italic] (named style is ignored and font is built off of base family)
3318  // 2) Family + named style (bold or italic is ignored)
3319 
3320  // data defined font family?
3321  QString ddFontFamily( "" );
3322  if ( dataDefinedEvaluate( QgsPalLayerSettings::Family, exprVal, &context.expressionContext(), labelFont.family() ) )
3323  {
3324  QString family = exprVal.toString().trimmed();
3325  QgsDebugMsgLevel( QString( "exprVal Font family:%1" ).arg( family ), 4 );
3326 
3327  if ( labelFont.family() != family )
3328  {
3329  // testing for ddFontFamily in QFontDatabase.families() may be slow to do for every feature
3330  // (i.e. don't use QgsFontUtils::fontFamilyMatchOnSystem( family ) here)
3331  if ( QgsFontUtils::fontFamilyOnSystem( family ) )
3332  {
3333  ddFontFamily = family;
3334  }
3335  }
3336  }
3337 
3338  // data defined named font style?
3339  QString ddFontStyle( "" );
3341  {
3342  QString fontstyle = exprVal.toString().trimmed();
3343  QgsDebugMsgLevel( QString( "exprVal Font style:%1" ).arg( fontstyle ), 4 );
3344  ddFontStyle = fontstyle;
3345  }
3346 
3347  // data defined bold font style?
3348  bool ddBold = false;
3349  if ( dataDefinedEvaluate( QgsPalLayerSettings::Bold, exprVal, &context.expressionContext(), labelFont.bold() ) )
3350  {
3351  bool bold = exprVal.toBool();
3352  QgsDebugMsgLevel( QString( "exprVal Font bold:%1" ).arg( bold ? "true" : "false" ), 4 );
3353  ddBold = bold;
3354  }
3355 
3356  // data defined italic font style?
3357  bool ddItalic = false;
3358  if ( dataDefinedEvaluate( QgsPalLayerSettings::Italic, exprVal, &context.expressionContext(), labelFont.italic() ) )
3359  {
3360  bool italic = exprVal.toBool();
3361  QgsDebugMsgLevel( QString( "exprVal Font italic:%1" ).arg( italic ? "true" : "false" ), 4 );
3362  ddItalic = italic;
3363  }
3364 
3365  // TODO: update when pref for how to resolve missing family (use matching algorithm or just default font) is implemented
3366  // (currently defaults to what has been read in from layer settings)
3367  QFont newFont;
3368  QFont appFont = QApplication::font();
3369  bool newFontBuilt = false;
3370  if ( ddBold || ddItalic )
3371  {
3372  // new font needs built, since existing style needs removed
3373  newFont = QFont( !ddFontFamily.isEmpty() ? ddFontFamily : labelFont.family() );
3374  newFontBuilt = true;
3375  newFont.setBold( ddBold );
3376  newFont.setItalic( ddItalic );
3377  }
3378  else if ( !ddFontStyle.isEmpty()
3379  && ddFontStyle.compare( "Ignore", Qt::CaseInsensitive ) != 0 )
3380  {
3381  if ( !ddFontFamily.isEmpty() )
3382  {
3383  // both family and style are different, build font from database
3384  QFont styledfont = mFontDB.font( ddFontFamily, ddFontStyle, appFont.pointSize() );
3385  if ( appFont != styledfont )
3386  {
3387  newFont = styledfont;
3388  newFontBuilt = true;
3389  }
3390  }
3391 
3392  // update the font face style
3393  QgsFontUtils::updateFontViaStyle( newFontBuilt ? newFont : labelFont, ddFontStyle );
3394  }
3395  else if ( !ddFontFamily.isEmpty() )
3396  {
3397  if ( ddFontStyle.compare( "Ignore", Qt::CaseInsensitive ) != 0 )
3398  {
3399  // just family is different, build font from database
3400  QFont styledfont = mFontDB.font( ddFontFamily, textNamedStyle, appFont.pointSize() );
3401  if ( appFont != styledfont )
3402  {
3403  newFont = styledfont;
3404  newFontBuilt = true;
3405  }
3406  }
3407  else
3408  {
3409  newFont = QFont( ddFontFamily );
3410  newFontBuilt = true;
3411  }
3412  }
3413 
3414  if ( newFontBuilt )
3415  {
3416  // copy over existing font settings
3417  //newFont = newFont.resolve( labelFont ); // should work, but let's be sure what's being copied
3418  newFont.setPixelSize( labelFont.pixelSize() );
3419  newFont.setUnderline( labelFont.underline() );
3420  newFont.setStrikeOut( labelFont.strikeOut() );
3421  newFont.setWordSpacing( labelFont.wordSpacing() );
3422  newFont.setLetterSpacing( QFont::AbsoluteSpacing, labelFont.letterSpacing() );
3423 
3424  labelFont = newFont;
3425  }
3426 
3427  // data defined word spacing?
3428  double wordspace = labelFont.wordSpacing();
3429  if ( dataDefinedEvaluate( QgsPalLayerSettings::FontWordSpacing, exprVal, &context.expressionContext(), wordspace ) )
3430  {
3431  bool ok;
3432  double wspacing = exprVal.toDouble( &ok );
3433  QgsDebugMsgLevel( QString( "exprVal FontWordSpacing:%1" ).arg( wspacing ), 4 );
3434  if ( ok )
3435  {
3436  wordspace = wspacing;
3437  }
3438  }
3439  labelFont.setWordSpacing( scaleToPixelContext( wordspace, context, fontunits, false, fontSizeMapUnitScale ) );
3440 
3441  // data defined letter spacing?
3442  double letterspace = labelFont.letterSpacing();
3443  if ( dataDefinedEvaluate( QgsPalLayerSettings::FontLetterSpacing, exprVal, &context.expressionContext(), letterspace ) )
3444  {
3445  bool ok;
3446  double lspacing = exprVal.toDouble( &ok );
3447  QgsDebugMsgLevel( QString( "exprVal FontLetterSpacing:%1" ).arg( lspacing ), 4 );
3448  if ( ok )
3449  {
3450  letterspace = lspacing;
3451  }
3452  }
3453  labelFont.setLetterSpacing( QFont::AbsoluteSpacing, scaleToPixelContext( letterspace, context, fontunits, false, fontSizeMapUnitScale ) );
3454 
3455  // data defined strikeout font style?
3456  if ( dataDefinedEvaluate( QgsPalLayerSettings::Strikeout, exprVal, &context.expressionContext(), labelFont.strikeOut() ) )
3457  {
3458  bool strikeout = exprVal.toBool();
3459  QgsDebugMsgLevel( QString( "exprVal Font strikeout:%1" ).arg( strikeout ? "true" : "false" ), 4 );
3460  labelFont.setStrikeOut( strikeout );
3461  }
3462 
3463  // data defined underline font style?
3464  if ( dataDefinedEvaluate( QgsPalLayerSettings::Underline, exprVal, &context.expressionContext(), labelFont.underline() ) )
3465  {
3466  bool underline = exprVal.toBool();
3467  QgsDebugMsgLevel( QString( "exprVal Font underline:%1" ).arg( underline ? "true" : "false" ), 4 );
3468  labelFont.setUnderline( underline );
3469  }
3470 
3471  // pass the rest on to QgsPalLabeling::drawLabeling
3472 
3473  // data defined font color?
3474  dataDefinedValEval( DDColor, QgsPalLayerSettings::Color, exprVal, context.expressionContext(), QgsSymbolLayerV2Utils::encodeColor( textColor ) );
3475 
3476  // data defined font transparency?
3477  dataDefinedValEval( DDTransparency, QgsPalLayerSettings::FontTransp, exprVal, context.expressionContext(), textTransp );
3478 
3479  // data defined font blend mode?
3480  dataDefinedValEval( DDBlendMode, QgsPalLayerSettings::FontBlendMode, exprVal, context.expressionContext() );
3481 
3482 }
3483 
3484 void QgsPalLayerSettings::parseTextBuffer( QgsRenderContext &context )
3485 {
3486  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
3487 
3488  // data defined draw buffer?
3489  bool drawBuffer = bufferDraw;
3490  if ( dataDefinedValEval( DDBool, QgsPalLayerSettings::BufferDraw, exprVal, context.expressionContext(), bufferDraw ) )
3491  {
3492  drawBuffer = exprVal.toBool();
3493  }
3494 
3495  if ( !drawBuffer )
3496  {
3497  return;
3498  }
3499 
3500  // data defined buffer size?
3501  double bufrSize = bufferSize;
3502  if ( dataDefinedValEval( DDDoublePos, QgsPalLayerSettings::BufferSize, exprVal, context.expressionContext(), bufferSize ) )
3503  {
3504  bufrSize = exprVal.toDouble();
3505  }
3506 
3507  // data defined buffer transparency?
3508  int bufTransp = bufferTransp;
3509  if ( dataDefinedValEval( DDTransparency, QgsPalLayerSettings::BufferTransp, exprVal, context.expressionContext(), bufferTransp ) )
3510  {
3511  bufTransp = exprVal.toInt();
3512  }
3513 
3514  drawBuffer = ( drawBuffer && bufrSize > 0.0 && bufTransp < 100 );
3515 
3516  if ( !drawBuffer )
3517  {
3518  dataDefinedValues.insert( QgsPalLayerSettings::BufferDraw, QVariant( false ) ); // trigger value
3519  dataDefinedValues.remove( QgsPalLayerSettings::BufferSize );
3520  dataDefinedValues.remove( QgsPalLayerSettings::BufferTransp );
3521  return; // don't bother evaluating values that won't be used
3522  }
3523 
3524  // data defined buffer units?
3525  dataDefinedValEval( DDUnits, QgsPalLayerSettings::BufferUnit, exprVal, context.expressionContext() );
3526 
3527  // data defined buffer color?
3528  dataDefinedValEval( DDColor, QgsPalLayerSettings::BufferColor, exprVal, context.expressionContext(), QgsSymbolLayerV2Utils::encodeColor( bufferColor ) );
3529 
3530  // data defined buffer pen join style?
3532 
3533  // data defined buffer blend mode?
3534  dataDefinedValEval( DDBlendMode, QgsPalLayerSettings::BufferBlendMode, exprVal, context.expressionContext() );
3535 }
3536 
3537 void QgsPalLayerSettings::parseTextFormatting( QgsRenderContext &context )
3538 {
3539  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
3540 
3541  // data defined multiline wrap character?
3542  QString wrapchr = wrapChar;
3543  if ( dataDefinedValEval( DDString, QgsPalLayerSettings::MultiLineWrapChar, exprVal, context.expressionContext(), wrapChar ) )
3544  {
3545  wrapchr = exprVal.toString();
3546  }
3547 
3548  // data defined multiline height?
3549  dataDefinedValEval( DDDouble, QgsPalLayerSettings::MultiLineHeight, exprVal, context.expressionContext() );
3550 
3551  // data defined multiline text align?
3553  {
3554  QString str = exprVal.toString().trimmed();
3555  QgsDebugMsgLevel( QString( "exprVal MultiLineAlignment:%1" ).arg( str ), 4 );
3556 
3557  if ( !str.isEmpty() )
3558  {
3559  // "Left"
3561 
3562  if ( str.compare( "Center", Qt::CaseInsensitive ) == 0 )
3563  {
3565  }
3566  else if ( str.compare( "Right", Qt::CaseInsensitive ) == 0 )
3567  {
3568  aligntype = QgsPalLayerSettings::MultiRight;
3569  }
3570  else if ( str.compare( "Follow", Qt::CaseInsensitive ) == 0 )
3571  {
3573  }
3574  dataDefinedValues.insert( QgsPalLayerSettings::MultiLineAlignment, QVariant( static_cast< int >( aligntype ) ) );
3575  }
3576  }
3577 
3578  // data defined direction symbol?
3579  bool drawDirSymb = addDirectionSymbol;
3580  if ( dataDefinedValEval( DDBool, QgsPalLayerSettings::DirSymbDraw, exprVal, context.expressionContext(), addDirectionSymbol ) )
3581  {
3582  drawDirSymb = exprVal.toBool();
3583  }
3584 
3585  if ( drawDirSymb )
3586  {
3587  // data defined direction left symbol?
3588  dataDefinedValEval( DDString, QgsPalLayerSettings::DirSymbLeft, exprVal, context.expressionContext(), leftDirectionSymbol );
3589 
3590  // data defined direction right symbol?
3591  dataDefinedValEval( DDString, QgsPalLayerSettings::DirSymbRight, exprVal, context.expressionContext(), rightDirectionSymbol );
3592 
3593  // data defined direction symbol placement?
3595  {
3596  QString str = exprVal.toString().trimmed();
3597  QgsDebugMsgLevel( QString( "exprVal DirSymbPlacement:%1" ).arg( str ), 4 );
3598 
3599  if ( !str.isEmpty() )
3600  {
3601  // "LeftRight"
3603 
3604  if ( str.compare( "Above", Qt::CaseInsensitive ) == 0 )
3605  {
3607  }
3608  else if ( str.compare( "Below", Qt::CaseInsensitive ) == 0 )
3609  {
3611  }
3612  dataDefinedValues.insert( QgsPalLayerSettings::DirSymbPlacement, QVariant( static_cast< int >( placetype ) ) );
3613  }
3614  }
3615 
3616  // data defined direction symbol reversed?
3617  dataDefinedValEval( DDBool, QgsPalLayerSettings::DirSymbReverse, exprVal, context.expressionContext(), reverseDirectionSymbol );
3618  }
3619 
3620  // formatting for numbers is inline with generation of base label text and not passed to label painting
3621 }
3622 
3623 void QgsPalLayerSettings::parseShapeBackground( QgsRenderContext &context )
3624 {
3625  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
3626 
3627  // data defined draw shape?
3628  bool drawShape = shapeDraw;
3629  if ( dataDefinedValEval( DDBool, QgsPalLayerSettings::ShapeDraw, exprVal, context.expressionContext(), shapeDraw ) )
3630  {
3631  drawShape = exprVal.toBool();
3632  }
3633 
3634  if ( !drawShape )
3635  {
3636  return;
3637  }
3638 
3639  // data defined shape transparency?
3640  int shapeTransp = shapeTransparency;
3641  if ( dataDefinedValEval( DDTransparency, QgsPalLayerSettings::ShapeTransparency, exprVal, context.expressionContext(), shapeTransparency ) )
3642  {
3643  shapeTransp = exprVal.toInt();
3644  }
3645 
3646  drawShape = ( drawShape && shapeTransp < 100 ); // size is not taken into account (could be)
3647 
3648  if ( !drawShape )
3649  {
3650  dataDefinedValues.insert( QgsPalLayerSettings::ShapeDraw, QVariant( false ) ); // trigger value
3651  dataDefinedValues.remove( QgsPalLayerSettings::ShapeTransparency );
3652  return; // don't bother evaluating values that won't be used
3653  }
3654 
3655  // data defined shape kind?
3658  {
3659  QString skind = exprVal.toString().trimmed();
3660  QgsDebugMsgLevel( QString( "exprVal ShapeKind:%1" ).arg( skind ), 4 );
3661 
3662  if ( !skind.isEmpty() )
3663  {
3664  // "Rectangle"
3666 
3667  if ( skind.compare( "Square", Qt::CaseInsensitive ) == 0 )
3668  {
3670  }
3671  else if ( skind.compare( "Ellipse", Qt::CaseInsensitive ) == 0 )
3672  {
3674  }
3675  else if ( skind.compare( "Circle", Qt::CaseInsensitive ) == 0 )
3676  {
3678  }
3679  else if ( skind.compare( "SVG", Qt::CaseInsensitive ) == 0 )
3680  {
3682  }
3683  shapeKind = shpkind;
3684  dataDefinedValues.insert( QgsPalLayerSettings::ShapeKind, QVariant( static_cast< int >( shpkind ) ) );
3685  }
3686  }
3687 
3688  // data defined shape SVG path?
3689  QString svgPath = shapeSVGFile;
3691  {
3692  QString svgfile = exprVal.toString().trimmed();
3693  QgsDebugMsgLevel( QString( "exprVal ShapeSVGFile:%1" ).arg( svgfile ), 4 );
3694 
3695  // '' empty paths are allowed
3696  svgPath = svgfile;
3697  dataDefinedValues.insert( QgsPalLayerSettings::ShapeSVGFile, QVariant( svgfile ) );
3698  }
3699 
3700  // data defined shape size type?
3703  {
3704  QString stype = exprVal.toString().trimmed();
3705  QgsDebugMsgLevel( QString( "exprVal ShapeSizeType:%1" ).arg( stype ), 4 );
3706 
3707  if ( !stype.isEmpty() )
3708  {
3709  // "Buffer"
3711 
3712  if ( stype.compare( "Fixed", Qt::CaseInsensitive ) == 0 )
3713  {
3715  }
3716  shpSizeType = sizType;
3717  dataDefinedValues.insert( QgsPalLayerSettings::ShapeSizeType, QVariant( static_cast< int >( sizType ) ) );
3718  }
3719  }
3720 
3721  // data defined shape size X? (SVGs only use X for sizing)
3722  double ddShpSizeX = shapeSize.x();
3723  if ( dataDefinedValEval( DDDouble, QgsPalLayerSettings::ShapeSizeX, exprVal, context.expressionContext(), ddShpSizeX ) )
3724  {
3725  ddShpSizeX = exprVal.toDouble();
3726  }
3727 
3728  // data defined shape size Y?
3729  double ddShpSizeY = shapeSize.y();
3730  if ( dataDefinedValEval( DDDouble, QgsPalLayerSettings::ShapeSizeY, exprVal, context.expressionContext(), ddShpSizeY ) )
3731  {
3732  ddShpSizeY = exprVal.toDouble();
3733  }
3734 
3735  // don't continue under certain circumstances (e.g. size is fixed)
3736  bool skip = false;
3737  if ( shapeKind == QgsPalLayerSettings::ShapeSVG
3738  && ( svgPath.isEmpty()
3739  || ( !svgPath.isEmpty()
3740  && shpSizeType == QgsPalLayerSettings::SizeFixed
3741  && ddShpSizeX == 0.0 ) ) )
3742  {
3743  skip = true;
3744  }
3745  if ( shapeKind != QgsPalLayerSettings::ShapeSVG
3746  && shpSizeType == QgsPalLayerSettings::SizeFixed
3747  && ( ddShpSizeX == 0.0 || ddShpSizeY == 0.0 ) )
3748  {
3749  skip = true;
3750  }
3751 
3752  if ( skip )
3753  {
3754  dataDefinedValues.insert( QgsPalLayerSettings::ShapeDraw, QVariant( false ) ); // trigger value
3755  dataDefinedValues.remove( QgsPalLayerSettings::ShapeTransparency );
3756  dataDefinedValues.remove( QgsPalLayerSettings::ShapeKind );
3757  dataDefinedValues.remove( QgsPalLayerSettings::ShapeSVGFile );
3758  dataDefinedValues.remove( QgsPalLayerSettings::ShapeSizeX );
3759  dataDefinedValues.remove( QgsPalLayerSettings::ShapeSizeY );
3760  return; // don't bother evaluating values that won't be used
3761  }
3762 
3763  // data defined shape size units?
3764  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShapeSizeUnits, exprVal, context.expressionContext() );
3765 
3766  // data defined shape rotation type?
3768  {
3769  QString rotstr = exprVal.toString().trimmed();
3770  QgsDebugMsgLevel( QString( "exprVal ShapeRotationType:%1" ).arg( rotstr ), 4 );
3771 
3772  if ( !rotstr.isEmpty() )
3773  {
3774  // "Sync"
3776 
3777  if ( rotstr.compare( "Offset", Qt::CaseInsensitive ) == 0 )
3778  {
3780  }
3781  else if ( rotstr.compare( "Fixed", Qt::CaseInsensitive ) == 0 )
3782  {
3784  }
3785  dataDefinedValues.insert( QgsPalLayerSettings::ShapeRotationType, QVariant( static_cast< int >( rottype ) ) );
3786  }
3787  }
3788 
3789  // data defined shape rotation?
3790  dataDefinedValEval( DDRotation180, QgsPalLayerSettings::ShapeRotation, exprVal, context.expressionContext(), shapeRotation );
3791 
3792  // data defined shape offset?
3793  dataDefinedValEval( DDPointF, QgsPalLayerSettings::ShapeOffset, exprVal, context.expressionContext(), QgsSymbolLayerV2Utils::encodePoint( shapeOffset ) );
3794 
3795  // data defined shape offset units?
3796  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShapeOffsetUnits, exprVal, context.expressionContext() );
3797 
3798  // data defined shape radii?
3799  dataDefinedValEval( DDPointF, QgsPalLayerSettings::ShapeRadii, exprVal, context.expressionContext(), QgsSymbolLayerV2Utils::encodePoint( shapeRadii ) );
3800 
3801  // data defined shape radii units?
3802  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShapeRadiiUnits, exprVal, context.expressionContext() );
3803 
3804  // data defined shape blend mode?
3805  dataDefinedValEval( DDBlendMode, QgsPalLayerSettings::ShapeBlendMode, exprVal, context.expressionContext() );
3806 
3807  // data defined shape fill color?
3808  dataDefinedValEval( DDColor, QgsPalLayerSettings::ShapeFillColor, exprVal, context.expressionContext(), QgsSymbolLayerV2Utils::encodeColor( shapeFillColor ) );
3809 
3810  // data defined shape border color?
3812 
3813  // data defined shape border width?
3814  dataDefinedValEval( DDDoublePos, QgsPalLayerSettings::ShapeBorderWidth, exprVal, context.expressionContext(), shapeBorderWidth );
3815 
3816  // data defined shape border width units?
3817  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShapeBorderWidthUnits, exprVal, context.expressionContext() );
3818 
3819  // data defined shape join style?
3821 
3822 }
3823 
3824 void QgsPalLayerSettings::parseDropShadow( QgsRenderContext &context )
3825 {
3826  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
3827 
3828  // data defined draw shadow?
3829  bool drawShadow = shadowDraw;
3830  if ( dataDefinedValEval( DDBool, QgsPalLayerSettings::ShadowDraw, exprVal, context.expressionContext(), shadowDraw ) )
3831  {
3832  drawShadow = exprVal.toBool();
3833  }
3834 
3835  if ( !drawShadow )
3836  {
3837  return;
3838  }
3839 
3840  // data defined shadow transparency?
3841  int shadowTransp = shadowTransparency;
3842  if ( dataDefinedValEval( DDTransparency, QgsPalLayerSettings::ShadowTransparency, exprVal, context.expressionContext(), shadowTransparency ) )
3843  {
3844  shadowTransp = exprVal.toInt();
3845  }
3846 
3847  // data defined shadow offset distance?
3848  double shadowOffDist = shadowOffsetDist;
3849  if ( dataDefinedValEval( DDDoublePos, QgsPalLayerSettings::ShadowOffsetDist, exprVal, context.expressionContext(), shadowOffsetDist ) )
3850  {
3851  shadowOffDist = exprVal.toDouble();
3852  }
3853 
3854  // data defined shadow offset distance?
3855  double shadowRad = shadowRadius;
3856  if ( dataDefinedValEval( DDDoublePos, QgsPalLayerSettings::ShadowRadius, exprVal, context.expressionContext(), shadowRadius ) )
3857  {
3858  shadowRad = exprVal.toDouble();
3859  }
3860 
3861  drawShadow = ( drawShadow && shadowTransp < 100 && !( shadowOffDist == 0.0 && shadowRad == 0.0 ) );
3862 
3863  if ( !drawShadow )
3864  {
3865  dataDefinedValues.insert( QgsPalLayerSettings::ShadowDraw, QVariant( false ) ); // trigger value
3866  dataDefinedValues.remove( QgsPalLayerSettings::ShadowTransparency );
3867  dataDefinedValues.remove( QgsPalLayerSettings::ShadowOffsetDist );
3868  dataDefinedValues.remove( QgsPalLayerSettings::ShadowRadius );
3869  return; // don't bother evaluating values that won't be used
3870  }
3871 
3872  // data defined shadow under type?
3874  {
3875  QString str = exprVal.toString().trimmed();
3876  QgsDebugMsgLevel( QString( "exprVal ShadowUnder:%1" ).arg( str ), 4 );
3877 
3878  if ( !str.isEmpty() )
3879  {
3880  // "Lowest"
3882 
3883  if ( str.compare( "Text", Qt::CaseInsensitive ) == 0 )
3884  {
3886  }
3887  else if ( str.compare( "Buffer", Qt::CaseInsensitive ) == 0 )
3888  {
3890  }
3891  else if ( str.compare( "Background", Qt::CaseInsensitive ) == 0 )
3892  {
3894  }
3895  dataDefinedValues.insert( QgsPalLayerSettings::ShadowUnder, QVariant( static_cast< int >( shdwtype ) ) );
3896  }
3897  }
3898 
3899  // data defined shadow offset angle?
3900  dataDefinedValEval( DDRotation180, QgsPalLayerSettings::ShadowOffsetAngle, exprVal, context.expressionContext(), shadowOffsetAngle );
3901 
3902  // data defined shadow offset units?
3903  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShadowOffsetUnits, exprVal, context.expressionContext() );
3904 
3905  // data defined shadow radius?
3906  dataDefinedValEval( DDDouble, QgsPalLayerSettings::ShadowRadius, exprVal, context.expressionContext(), shadowRadius );
3907 
3908  // data defined shadow radius units?
3909  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShadowRadiusUnits, exprVal, context.expressionContext() );
3910 
3911  // data defined shadow scale? ( gui bounds to 0-2000, no upper bound here )
3912  dataDefinedValEval( DDIntPos, QgsPalLayerSettings::ShadowScale, exprVal, context.expressionContext(), shadowScale );
3913 
3914  // data defined shadow color?
3915  dataDefinedValEval( DDColor, QgsPalLayerSettings::ShadowColor, exprVal, context.expressionContext(), QgsSymbolLayerV2Utils::encodeColor( shadowColor ) );
3916 
3917  // data defined shadow blend mode?
3918  dataDefinedValEval( DDBlendMode, QgsPalLayerSettings::ShadowBlendMode, exprVal, context.expressionContext() );
3919</