QGIS API Documentation  2.17.0-Master (0497e4a)
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::BevelJoin;
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::BevelJoin ) ).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::BevelJoin ) ).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  permissibleZone = *preparedZone;
2533  delete preparedZone;
2534  }
2535  }
2536 
2537  // if using perimeter based labeling for polygons, get the polygon's
2538  // linear boundary and use that for the label geometry
2539  if (( geom->type() == QGis::Polygon )
2540  && ( placement == Line || placement == PerimeterCurved ) )
2541  {
2542  QgsGeometry* boundaryGeom = new QgsGeometry( geom->geometry()->boundary() );
2543  geom = boundaryGeom;
2544  scopedClonedGeom.reset( boundaryGeom );
2545  }
2546 
2547  const GEOSGeometry* geos_geom = nullptr;
2548  const QgsGeometry* preparedGeom = geom;
2549  QScopedPointer<QgsGeometry> scopedPreparedGeom;
2550  if ( QgsPalLabeling::geometryRequiresPreparation( geom, context, ct, doClip ? extentGeom : nullptr ) )
2551  {
2552  scopedPreparedGeom.reset( QgsPalLabeling::prepareGeometry( geom, context, ct, doClip ? extentGeom : nullptr ) );
2553 
2554  if ( !scopedPreparedGeom.data() )
2555  return;
2556  preparedGeom = scopedPreparedGeom.data();
2557  geos_geom = scopedPreparedGeom.data()->asGeos();
2558  }
2559  else
2560  {
2561  geos_geom = geom->asGeos();
2562  }
2563  const GEOSGeometry* geosObstacleGeom = nullptr;
2564  QScopedPointer<QgsGeometry> scopedObstacleGeom;
2565  if ( isObstacle )
2566  {
2567  if ( obstacleGeometry && QgsPalLabeling::geometryRequiresPreparation( obstacleGeometry, context, ct, doClip ? extentGeom : nullptr ) )
2568  {
2569  scopedObstacleGeom.reset( QgsPalLabeling::prepareGeometry( obstacleGeometry, context, ct, doClip ? extentGeom : nullptr ) );
2570  obstacleGeometry = scopedObstacleGeom.data();
2571  }
2572  if ( obstacleGeometry )
2573  {
2574  geosObstacleGeom = obstacleGeometry->asGeos();
2575  }
2576  }
2577 
2578  if ( minFeatureSize > 0 && !checkMinimumSizeMM( context, preparedGeom, minFeatureSize ) )
2579  return;
2580 
2581  if ( !geos_geom )
2582  return; // invalid geometry
2583 
2584  // likelihood exists label will be registered with PAL and may be drawn
2585  // check if max number of features to label (already registered with PAL) has been reached
2586  // Debug output at end of QgsPalLabeling::drawLabeling(), when deleting temp geometries
2587  if ( limitNumLabels )
2588  {
2589  if ( !maxNumLabels )
2590  {
2591  return;
2592  }
2593  if ( mFeatsRegPal >= maxNumLabels )
2594  {
2595  return;
2596  }
2597 
2598  int divNum = static_cast< int >(( static_cast< double >( mFeaturesToLabel ) / maxNumLabels ) + 0.5 );
2599  if ( divNum && ( mFeatsRegPal == static_cast< int >( mFeatsSendingToPal / divNum ) ) )
2600  {
2601  mFeatsSendingToPal += 1;
2602  if ( divNum && mFeatsSendingToPal % divNum )
2603  {
2604  return;
2605  }
2606  }
2607  }
2608 
2609  GEOSGeometry* geos_geom_clone = GEOSGeom_clone_r( QgsGeometry::getGEOSHandler(), geos_geom );
2610  GEOSGeometry* geosObstacleGeomClone = nullptr;
2611  if ( geosObstacleGeom )
2612  {
2613  geosObstacleGeomClone = GEOSGeom_clone_r( QgsGeometry::getGEOSHandler(), geosObstacleGeom );
2614  }
2615 
2616 
2617  //data defined position / alignment / rotation?
2618  bool dataDefinedPosition = false;
2619  bool layerDefinedRotation = false;
2620  bool dataDefinedRotation = false;
2621  double xPos = 0.0, yPos = 0.0, angle = 0.0;
2622  bool ddXPos = false, ddYPos = false;
2623  double quadOffsetX = 0.0, quadOffsetY = 0.0;
2624  double offsetX = 0.0, offsetY = 0.0;
2625 
2626  //data defined quadrant offset?
2627  bool ddFixedQuad = false;
2628  QuadrantPosition quadOff = quadOffset;
2629  if ( dataDefinedEvaluate( QgsPalLayerSettings::OffsetQuad, exprVal, &context.expressionContext(), static_cast< int >( quadOff ) ) )
2630  {
2631  bool ok;
2632  int quadInt = exprVal.toInt( &ok );
2633  QgsDebugMsgLevel( QString( "exprVal OffsetQuad:%1" ).arg( quadInt ), 4 );
2634  if ( ok && 0 <= quadInt && quadInt <= 8 )
2635  {
2636  quadOff = static_cast< QuadrantPosition >( quadInt );
2637  ddFixedQuad = true;
2638  }
2639  }
2640 
2641  // adjust quadrant offset of labels
2642  switch ( quadOff )
2643  {
2644  case QuadrantAboveLeft:
2645  quadOffsetX = -1.0;
2646  quadOffsetY = 1.0;
2647  break;
2648  case QuadrantAbove:
2649  quadOffsetX = 0.0;
2650  quadOffsetY = 1.0;
2651  break;
2652  case QuadrantAboveRight:
2653  quadOffsetX = 1.0;
2654  quadOffsetY = 1.0;
2655  break;
2656  case QuadrantLeft:
2657  quadOffsetX = -1.0;
2658  quadOffsetY = 0.0;
2659  break;
2660  case QuadrantRight:
2661  quadOffsetX = 1.0;
2662  quadOffsetY = 0.0;
2663  break;
2664  case QuadrantBelowLeft:
2665  quadOffsetX = -1.0;
2666  quadOffsetY = -1.0;
2667  break;
2668  case QuadrantBelow:
2669  quadOffsetX = 0.0;
2670  quadOffsetY = -1.0;
2671  break;
2672  case QuadrantBelowRight:
2673  quadOffsetX = 1.0;
2674  quadOffsetY = -1.0;
2675  break;
2676  case QuadrantOver:
2677  default:
2678  break;
2679  }
2680 
2681  //data defined label offset?
2682  double xOff = xOffset;
2683  double yOff = yOffset;
2685  {
2686  QString ptstr = exprVal.toString().trimmed();
2687  QgsDebugMsgLevel( QString( "exprVal OffsetXY:%1" ).arg( ptstr ), 4 );
2688 
2689  if ( !ptstr.isEmpty() )
2690  {
2691  QPointF ddOffPt = QgsSymbolLayerV2Utils::decodePoint( ptstr );
2692  xOff = ddOffPt.x();
2693  yOff = ddOffPt.y();
2694  }
2695  }
2696 
2697  // data defined label offset units?
2698  bool offinmapunits = labelOffsetInMapUnits;
2700  {
2701  QString units = exprVal.toString().trimmed();
2702  QgsDebugMsgLevel( QString( "exprVal OffsetUnits:%1" ).arg( units ), 4 );
2703  if ( !units.isEmpty() )
2704  {
2705  offinmapunits = ( _decodeUnits( units ) == QgsPalLayerSettings::MapUnits );
2706  }
2707  }
2708 
2709  // adjust offset of labels to match chosen unit and map scale
2710  // offsets match those of symbology: -x = left, -y = up
2711  double mapUntsPerMM = labelOffsetMapUnitScale.computeMapUnitsPerPixel( context ) * context.scaleFactor();
2712  if ( !qgsDoubleNear( xOff, 0.0 ) )
2713  {
2714  offsetX = xOff; // must be positive to match symbology offset direction
2715  if ( !offinmapunits )
2716  {
2717  offsetX *= mapUntsPerMM; //convert offset from mm to map units
2718  }
2719  }
2720  if ( !qgsDoubleNear( yOff, 0.0 ) )
2721  {
2722  offsetY = -yOff; // must be negative to match symbology offset direction
2723  if ( !offinmapunits )
2724  {
2725  offsetY *= mapUntsPerMM; //convert offset from mm to map units
2726  }
2727  }
2728 
2729  // layer defined rotation?
2730  // only rotate non-pinned OverPoint placements until other placements are supported in pal::Feature
2732  {
2733  layerDefinedRotation = true;
2734  angle = angleOffset * M_PI / 180; // convert to radians
2735  }
2736 
2737  const QgsMapToPixel& m2p = context.mapToPixel();
2738  //data defined rotation?
2740  {
2741  bool ok;
2742  double rotD = exprVal.toDouble( &ok );
2743  QgsDebugMsgLevel( QString( "exprVal Rotation:%1" ).arg( rotD ), 4 );
2744  if ( ok )
2745  {
2746  dataDefinedRotation = true;
2747  // TODO: add setting to disable having data defined rotation follow
2748  // map rotation ?
2749  rotD -= m2p.mapRotation();
2750  angle = rotD * M_PI / 180.0;
2751  }
2752  }
2753 
2755  {
2756  if ( !exprVal.isNull() )
2757  xPos = exprVal.toDouble( &ddXPos );
2758  QgsDebugMsgLevel( QString( "exprVal PositionX:%1" ).arg( xPos ), 4 );
2759 
2761  {
2762  //data defined position. But field values could be NULL -> positions will be generated by PAL
2763  if ( !exprVal.isNull() )
2764  yPos = exprVal.toDouble( &ddYPos );
2765  QgsDebugMsgLevel( QString( "exprVal PositionY:%1" ).arg( yPos ), 4 );
2766 
2767  if ( ddXPos && ddYPos )
2768  {
2769  dataDefinedPosition = true;
2770  // layer rotation set, but don't rotate pinned labels unless data defined
2771  if ( layerDefinedRotation && !dataDefinedRotation )
2772  {
2773  angle = 0.0;
2774  }
2775 
2776  //x/y shift in case of alignment
2777  double xdiff = 0.0;
2778  double ydiff = 0.0;
2779 
2780  //horizontal alignment
2781  if ( dataDefinedEvaluate( QgsPalLayerSettings::Hali, exprVal, &context.expressionContext() ) )
2782  {
2783  QString haliString = exprVal.toString();
2784  QgsDebugMsgLevel( QString( "exprVal Hali:%1" ).arg( haliString ), 4 );
2785  if ( haliString.compare( "Center", Qt::CaseInsensitive ) == 0 )
2786  {
2787  xdiff -= labelX / 2.0;
2788  }
2789  else if ( haliString.compare( "Right", Qt::CaseInsensitive ) == 0 )
2790  {
2791  xdiff -= labelX;
2792  }
2793  }
2794 
2795  //vertical alignment
2796  if ( dataDefinedEvaluate( QgsPalLayerSettings::Vali, exprVal, &context.expressionContext() ) )
2797  {
2798  QString valiString = exprVal.toString();
2799  QgsDebugMsgLevel( QString( "exprVal Vali:%1" ).arg( valiString ), 4 );
2800 
2801  if ( valiString.compare( "Bottom", Qt::CaseInsensitive ) != 0 )
2802  {
2803  if ( valiString.compare( "Top", Qt::CaseInsensitive ) == 0 )
2804  {
2805  ydiff -= labelY;
2806  }
2807  else
2808  {
2809  double descentRatio = labelFontMetrics->descent() / labelFontMetrics->height();
2810  if ( valiString.compare( "Base", Qt::CaseInsensitive ) == 0 )
2811  {
2812  ydiff -= labelY * descentRatio;
2813  }
2814  else //'Cap' or 'Half'
2815  {
2816  double capHeightRatio = ( labelFontMetrics->boundingRect( 'H' ).height() + 1 + labelFontMetrics->descent() ) / labelFontMetrics->height();
2817  ydiff -= labelY * capHeightRatio;
2818  if ( valiString.compare( "Half", Qt::CaseInsensitive ) == 0 )
2819  {
2820  ydiff += labelY * ( capHeightRatio - descentRatio ) / 2.0;
2821  }
2822  }
2823  }
2824  }
2825  }
2826 
2827  if ( dataDefinedRotation )
2828  {
2829  //adjust xdiff and ydiff because the hali/vali point needs to be the rotation center
2830  double xd = xdiff * cos( angle ) - ydiff * sin( angle );
2831  double yd = xdiff * sin( angle ) + ydiff * cos( angle );
2832  xdiff = xd;
2833  ydiff = yd;
2834  }
2835 
2836  //project xPos and yPos from layer to map CRS
2837  double z = 0;
2838  if ( ct )
2839  {
2840  try
2841  {
2842  ct->transformInPlace( xPos, yPos, z );
2843  }
2844  catch ( QgsCsException &e )
2845  {
2846  Q_UNUSED( e );
2847  QgsDebugMsgLevel( QString( "Ignoring feature %1 due transformation exception on data-defined position" ).arg( f.id() ), 4 );
2848  return;
2849  }
2850  }
2851 
2852  //rotate position with map if data-defined
2853  if ( dataDefinedPosition && m2p.mapRotation() )
2854  {
2855  const QgsPoint& center = context.extent().center();
2856  QTransform t = QTransform::fromTranslate( center.x(), center.y() );
2857  t.rotate( -m2p.mapRotation() );
2858  t.translate( -center.x(), -center.y() );
2859  qreal xPosR, yPosR;
2860  qreal xPos_qreal = xPos, yPos_qreal = yPos;
2861  t.map( xPos_qreal, yPos_qreal, &xPosR, &yPosR );
2862  xPos = xPosR;
2863  yPos = yPosR;
2864 
2865  }
2866 
2867  xPos += xdiff;
2868  yPos += ydiff;
2869  }
2870  else
2871  {
2872  // only rotate non-pinned OverPoint placements until other placements are supported in pal::Feature
2873  if ( dataDefinedRotation && placement != QgsPalLayerSettings::OverPoint )
2874  {
2875  angle = 0.0;
2876  }
2877  }
2878  }
2879  }
2880 
2881  // data defined always show?
2882  bool alwaysShow = false;
2884  {
2885  alwaysShow = exprVal.toBool();
2886  }
2887 
2888  // set repeat distance
2889  // data defined repeat distance?
2890  double repeatDist = repeatDistance;
2892  {
2893  bool ok;
2894  double distD = exprVal.toDouble( &ok );
2895  if ( ok )
2896  {
2897  repeatDist = distD;
2898  }
2899  }
2900 
2901  // data defined label-repeat distance units?
2902  bool repeatdistinmapunit = repeatDistanceUnit == QgsPalLayerSettings::MapUnits;
2904  {
2905  QString units = exprVal.toString().trimmed();
2906  QgsDebugMsgLevel( QString( "exprVal RepeatDistanceUnits:%1" ).arg( units ), 4 );
2907  if ( !units.isEmpty() )
2908  {
2909  repeatdistinmapunit = ( _decodeUnits( units ) == QgsPalLayerSettings::MapUnits );
2910  }
2911  }
2912 
2913  if ( !qgsDoubleNear( repeatDist, 0.0 ) )
2914  {
2915  if ( !repeatdistinmapunit )
2916  {
2917  repeatDist *= mapUntsPerMM; //convert repeat distance from mm to map units
2918  }
2919  }
2920 
2921  // feature to the layer
2922  QgsTextLabelFeature* lf = new QgsTextLabelFeature( f.id(), geos_geom_clone, QSizeF( labelX, labelY ) );
2923  mFeatsRegPal++;
2924 
2925  *labelFeature = lf;
2926  ( *labelFeature )->setHasFixedPosition( dataDefinedPosition );
2927  ( *labelFeature )->setFixedPosition( QgsPoint( xPos, yPos ) );
2928  // use layer-level defined rotation, but not if position fixed
2929  ( *labelFeature )->setHasFixedAngle( dataDefinedRotation || ( !dataDefinedPosition && !qgsDoubleNear( angle, 0.0 ) ) );
2930  ( *labelFeature )->setFixedAngle( angle );
2931  ( *labelFeature )->setQuadOffset( QPointF( quadOffsetX, quadOffsetY ) );
2932  ( *labelFeature )->setPositionOffset( QgsPoint( offsetX, offsetY ) );
2933  ( *labelFeature )->setOffsetType( offsetType );
2934  ( *labelFeature )->setAlwaysShow( alwaysShow );
2935  ( *labelFeature )->setRepeatDistance( repeatDist );
2936  ( *labelFeature )->setLabelText( labelText );
2937  ( *labelFeature )->setPermissibleZone( permissibleZone );
2938  if ( geosObstacleGeomClone )
2939  {
2940  ( *labelFeature )->setObstacleGeometry( geosObstacleGeomClone );
2941 
2942  if ( geom->type() == QGis::Point )
2943  {
2944  //register symbol size
2945  ( *labelFeature )->setSymbolSize( QSizeF( obstacleGeometry->boundingBox().width(),
2946  obstacleGeometry->boundingBox().height() ) );
2947  }
2948  }
2949 
2950  //set label's visual margin so that top visual margin is the leading, and bottom margin is the font's descent
2951  //this makes labels align to the font's baseline or highest character
2952  double topMargin = qMax( 0.25 * labelFontMetrics->ascent(), 0.0 );
2953  double bottomMargin = 1.0 + labelFontMetrics->descent();
2954  QgsLabelFeature::VisualMargin vm( topMargin, 0.0, bottomMargin, 0.0 );
2956  ( *labelFeature )->setVisualMargin( vm );
2957 
2958  // store the label's calculated font for later use during painting
2959  QgsDebugMsgLevel( QString( "PAL font stored definedFont: %1, Style: %2" ).arg( labelFont.toString(), labelFont.styleName() ), 4 );
2960  lf->setDefinedFont( labelFont );
2961 
2962  // TODO: only for placement which needs character info
2963  // account for any data defined font metrics adjustments
2965  labelFontMetrics.data(), xform, rasterCompressFactor, maxcharanglein, maxcharangleout );
2966  // for labelFeature the LabelInfo is passed to feat when it is registered
2967 
2968  // TODO: allow layer-wide feature dist in PAL...?
2969 
2970  // data defined label-feature distance?
2971  double distance = dist;
2973  {
2974  bool ok;
2975  double distD = exprVal.toDouble( &ok );
2976  if ( ok )
2977  {
2978  distance = distD;
2979  }
2980  }
2981 
2982  // data defined label-feature distance units?
2983  bool distinmapunit = distInMapUnits;
2985  {
2986  QString units = exprVal.toString().trimmed();
2987  QgsDebugMsgLevel( QString( "exprVal DistanceUnits:%1" ).arg( units ), 4 );
2988  if ( !units.isEmpty() )
2989  {
2990  distinmapunit = ( _decodeUnits( units ) == QgsPalLayerSettings::MapUnits );
2991  }
2992  }
2993 
2994  if ( distinmapunit ) //convert distance from mm/map units to pixels
2995  {
2996  distance /= distMapUnitScale.computeMapUnitsPerPixel( context );
2997  }
2998  else //mm
2999  {
3000  distance *= vectorScaleFactor;
3001  }
3002 
3003  // when using certain placement modes, we force a tiny minimum distance. This ensures that
3004  // candidates are created just offset from a border and avoids candidates being incorrectly flagged as colliding with neighbours
3006  {
3007  distance = qMax( distance, 1.0 );
3008  }
3009 
3010  if ( !qgsDoubleNear( distance, 0.0 ) )
3011  {
3012  double d = ptOne.distance( ptZero ) * distance;
3013  ( *labelFeature )->setDistLabel( d );
3014  }
3015 
3016  if ( ddFixedQuad )
3017  {
3018  ( *labelFeature )->setHasFixedQuadrant( true );
3019  }
3020 
3021  // data defined z-index?
3022  double z = zIndex;
3024  {
3025  bool ok;
3026  double zIndexD = exprVal.toDouble( &ok );
3027  if ( ok )
3028  {
3029  z = zIndexD;
3030  }
3031  }
3032  ( *labelFeature )->setZIndex( z );
3033 
3034  // data defined priority?
3036  {
3037  bool ok;
3038  double priorityD = exprVal.toDouble( &ok );
3039  if ( ok )
3040  {
3041  priorityD = qBound( 0.0, priorityD, 10.0 );
3042  priorityD = 1 - priorityD / 10.0; // convert 0..10 --> 1..0
3043  ( *labelFeature )->setPriority( priorityD );
3044  }
3045  }
3046 
3047  ( *labelFeature )->setIsObstacle( isObstacle );
3048 
3049  double featObstacleFactor = obstacleFactor;
3051  {
3052  bool ok;
3053  double factorD = exprVal.toDouble( &ok );
3054  if ( ok )
3055  {
3056  factorD = qBound( 0.0, factorD, 10.0 );
3057  factorD = factorD / 5.0 + 0.0001; // convert 0 -> 10 to 0.0001 -> 2.0
3058  featObstacleFactor = factorD;
3059  }
3060  }
3061  ( *labelFeature )->setObstacleFactor( featObstacleFactor );
3062 
3064  if ( positionOrder.isEmpty() )
3065  positionOrder = QgsPalLayerSettings::DEFAULT_PLACEMENT_ORDER;
3066 
3068  {
3069  QString orderD = exprVal.toString();
3070  positionOrder = QgsLabelingUtils::decodePredefinedPositionOrder( orderD );
3071  }
3072  ( *labelFeature )->setPredefinedPositionOrder( positionOrder );
3073 
3074  // add parameters for data defined labeling to label feature
3075  lf->setDataDefinedValues( dataDefinedValues );
3076 }
3077 
3078 void QgsPalLayerSettings::registerObstacleFeature( QgsFeature& f, QgsRenderContext &context, QgsLabelFeature** obstacleFeature, QgsGeometry* obstacleGeometry )
3079 {
3080  mCurFeat = &f;
3081 
3082  const QgsGeometry* geom = nullptr;
3083  if ( obstacleGeometry )
3084  {
3085  geom = obstacleGeometry;
3086  }
3087  else
3088  {
3089  geom = f.constGeometry();
3090  }
3091 
3092  if ( !geom )
3093  {
3094  return;
3095  }
3096 
3097  // simplify?
3098  const QgsVectorSimplifyMethod &simplifyMethod = context.vectorSimplifyMethod();
3099  QScopedPointer<QgsGeometry> scopedClonedGeom;
3100  if ( simplifyMethod.simplifyHints() != QgsVectorSimplifyMethod::NoSimplification && simplifyMethod.forceLocalOptimization() )
3101  {
3102  int simplifyHints = simplifyMethod.simplifyHints() | QgsMapToPixelSimplifier::SimplifyEnvelope;
3104  QgsGeometry* g = new QgsGeometry( *geom );
3105 
3106  if ( QgsMapToPixelSimplifier::simplifyGeometry( g, simplifyHints, simplifyMethod.tolerance(), simplifyAlgorithm ) )
3107  {
3108  geom = g;
3109  scopedClonedGeom.reset( g );
3110  }
3111  else
3112  {
3113  delete g;
3114  }
3115  }
3116 
3117  const GEOSGeometry* geos_geom = nullptr;
3118  QScopedPointer<QgsGeometry> scopedPreparedGeom;
3119 
3120  if ( QgsPalLabeling::geometryRequiresPreparation( geom, context, ct, extentGeom ) )
3121  {
3122  scopedPreparedGeom.reset( QgsPalLabeling::prepareGeometry( geom, context, ct, extentGeom ) );
3123  if ( !scopedPreparedGeom.data() )
3124  return;
3125  geos_geom = scopedPreparedGeom.data()->asGeos();
3126  }
3127  else
3128  {
3129  geos_geom = geom->asGeos();
3130  }
3131 
3132  if ( !geos_geom )
3133  return; // invalid geometry
3134 
3135  GEOSGeometry* geos_geom_clone;
3136  geos_geom_clone = GEOSGeom_clone_r( QgsGeometry::getGEOSHandler(), geos_geom );
3137 
3138  // feature to the layer
3139  *obstacleFeature = new QgsLabelFeature( f.id(), geos_geom_clone, QSizeF( 0, 0 ) );
3140  ( *obstacleFeature )->setIsObstacle( true );
3141  mFeatsRegPal++;
3142 }
3143 
3144 bool QgsPalLayerSettings::dataDefinedValEval( DataDefinedValueType valType,
3146  QVariant& exprVal, QgsExpressionContext& context, const QVariant& originalValue )
3147 {
3148  if ( dataDefinedEvaluate( p, exprVal, &context, originalValue ) )
3149  {
3150 #ifdef QGISDEBUG
3151  QString dbgStr = QString( "exprVal %1:" ).arg( mDataDefinedNames.value( p ).first ) + "%1"; // clazy:exclude=unused-non-trivial-variable
3152 #endif
3153 
3154  switch ( valType )
3155  {
3156  case DDBool:
3157  {
3158  bool bol = exprVal.toBool();
3159  QgsDebugMsgLevel( dbgStr.arg( bol ? "true" : "false" ), 4 );
3160  dataDefinedValues.insert( p, QVariant( bol ) );
3161  return true;
3162  }
3163  case DDInt:
3164  {
3165  bool ok;
3166  int size = exprVal.toInt( &ok );
3167  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
3168 
3169  if ( ok )
3170  {
3171  dataDefinedValues.insert( p, QVariant( size ) );
3172  return true;
3173  }
3174  return false;
3175  }
3176  case DDIntPos:
3177  {
3178  bool ok;
3179  int size = exprVal.toInt( &ok );
3180  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
3181 
3182  if ( ok && size > 0 )
3183  {
3184  dataDefinedValues.insert( p, QVariant( size ) );
3185  return true;
3186  }
3187  return false;
3188  }
3189  case DDDouble:
3190  {
3191  bool ok;
3192  double size = exprVal.toDouble( &ok );
3193  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
3194 
3195  if ( ok )
3196  {
3197  dataDefinedValues.insert( p, QVariant( size ) );
3198  return true;
3199  }
3200  return false;
3201  }
3202  case DDDoublePos:
3203  {
3204  bool ok;
3205  double size = exprVal.toDouble( &ok );
3206  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
3207 
3208  if ( ok && size > 0.0 )
3209  {
3210  dataDefinedValues.insert( p, QVariant( size ) );
3211  return true;
3212  }
3213  return false;
3214  }
3215  case DDRotation180:
3216  {
3217  bool ok;
3218  double rot = exprVal.toDouble( &ok );
3219  QgsDebugMsgLevel( dbgStr.arg( rot ), 4 );
3220  if ( ok )
3221  {
3222  if ( rot < -180.0 && rot >= -360 )
3223  {
3224  rot += 360;
3225  }
3226  if ( rot > 180.0 && rot <= 360 )
3227  {
3228  rot -= 360;
3229  }
3230  if ( rot >= -180 && rot <= 180 )
3231  {
3232  dataDefinedValues.insert( p, QVariant( rot ) );
3233  return true;
3234  }
3235  }
3236  return false;
3237  }
3238  case DDTransparency:
3239  {
3240  bool ok;
3241  int size = exprVal.toInt( &ok );
3242  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
3243  if ( ok && size >= 0 && size <= 100 )
3244  {
3245  dataDefinedValues.insert( p, QVariant( size ) );
3246  return true;
3247  }
3248  return false;
3249  }
3250  case DDString:
3251  {
3252  QString str = exprVal.toString(); // don't trim whitespace
3253  QgsDebugMsgLevel( dbgStr.arg( str ), 4 );
3254 
3255  dataDefinedValues.insert( p, QVariant( str ) ); // let it stay empty if it is
3256  return true;
3257  }
3258  case DDUnits:
3259  {
3260  QString unitstr = exprVal.toString().trimmed();
3261  QgsDebugMsgLevel( dbgStr.arg( unitstr ), 4 );
3262 
3263  if ( !unitstr.isEmpty() )
3264  {
3265  dataDefinedValues.insert( p, QVariant( static_cast< int >( _decodeUnits( unitstr ) ) ) );
3266  return true;
3267  }
3268  return false;
3269  }
3270  case DDColor:
3271  {
3272  QString colorstr = exprVal.toString().trimmed();
3273  QgsDebugMsgLevel( dbgStr.arg( colorstr ), 4 );
3274  QColor color = QgsSymbolLayerV2Utils::decodeColor( colorstr );
3275 
3276  if ( color.isValid() )
3277  {
3278  dataDefinedValues.insert( p, QVariant( color ) );
3279  return true;
3280  }
3281  return false;
3282  }
3283  case DDJoinStyle:
3284  {
3285  QString joinstr = exprVal.toString().trimmed();
3286  QgsDebugMsgLevel( dbgStr.arg( joinstr ), 4 );
3287 
3288  if ( !joinstr.isEmpty() )
3289  {
3290  dataDefinedValues.insert( p, QVariant( static_cast< int >( _decodePenJoinStyle( joinstr ) ) ) );
3291  return true;
3292  }
3293  return false;
3294  }
3295  case DDBlendMode:
3296  {
3297  QString blendstr = exprVal.toString().trimmed();
3298  QgsDebugMsgLevel( dbgStr.arg( blendstr ), 4 );
3299 
3300  if ( !blendstr.isEmpty() )
3301  {
3302  dataDefinedValues.insert( p, QVariant( static_cast< int >( QgsSymbolLayerV2Utils::decodeBlendMode( blendstr ) ) ) );
3303  return true;
3304  }
3305  return false;
3306  }
3307  case DDPointF:
3308  {
3309  QString ptstr = exprVal.toString().trimmed();
3310  QgsDebugMsgLevel( dbgStr.arg( ptstr ), 4 );
3311 
3312  if ( !ptstr.isEmpty() )
3313  {
3314  dataDefinedValues.insert( p, QVariant( QgsSymbolLayerV2Utils::decodePoint( ptstr ) ) );
3315  return true;
3316  }
3317  return false;
3318  }
3319  }
3320  }
3321  return false;
3322 }
3323 
3324 void QgsPalLayerSettings::parseTextStyle( QFont& labelFont,
3326  QgsRenderContext &context )
3327 {
3328  // NOTE: labelFont already has pixelSize set, so pointSize or pointSizeF might return -1
3329 
3330  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
3331 
3332  // Two ways to generate new data defined font:
3333  // 1) Family + [bold] + [italic] (named style is ignored and font is built off of base family)
3334  // 2) Family + named style (bold or italic is ignored)
3335 
3336  // data defined font family?
3337  QString ddFontFamily( "" );
3338  if ( dataDefinedEvaluate( QgsPalLayerSettings::Family, exprVal, &context.expressionContext(), labelFont.family() ) )
3339  {
3340  QString family = exprVal.toString().trimmed();
3341  QgsDebugMsgLevel( QString( "exprVal Font family:%1" ).arg( family ), 4 );
3342 
3343  if ( labelFont.family() != family )
3344  {
3345  // testing for ddFontFamily in QFontDatabase.families() may be slow to do for every feature
3346  // (i.e. don't use QgsFontUtils::fontFamilyMatchOnSystem( family ) here)
3347  if ( QgsFontUtils::fontFamilyOnSystem( family ) )
3348  {
3349  ddFontFamily = family;
3350  }
3351  }
3352  }
3353 
3354  // data defined named font style?
3355  QString ddFontStyle( "" );
3357  {
3358  QString fontstyle = exprVal.toString().trimmed();
3359  QgsDebugMsgLevel( QString( "exprVal Font style:%1" ).arg( fontstyle ), 4 );
3360  ddFontStyle = fontstyle;
3361  }
3362 
3363  // data defined bold font style?
3364  bool ddBold = false;
3365  if ( dataDefinedEvaluate( QgsPalLayerSettings::Bold, exprVal, &context.expressionContext(), labelFont.bold() ) )
3366  {
3367  bool bold = exprVal.toBool();
3368  QgsDebugMsgLevel( QString( "exprVal Font bold:%1" ).arg( bold ? "true" : "false" ), 4 );
3369  ddBold = bold;
3370  }
3371 
3372  // data defined italic font style?
3373  bool ddItalic = false;
3374  if ( dataDefinedEvaluate( QgsPalLayerSettings::Italic, exprVal, &context.expressionContext(), labelFont.italic() ) )
3375  {
3376  bool italic = exprVal.toBool();
3377  QgsDebugMsgLevel( QString( "exprVal Font italic:%1" ).arg( italic ? "true" : "false" ), 4 );
3378  ddItalic = italic;
3379  }
3380 
3381  // TODO: update when pref for how to resolve missing family (use matching algorithm or just default font) is implemented
3382  // (currently defaults to what has been read in from layer settings)
3383  QFont newFont;
3384  QFont appFont = QApplication::font();
3385  bool newFontBuilt = false;
3386  if ( ddBold || ddItalic )
3387  {
3388  // new font needs built, since existing style needs removed
3389  newFont = QFont( !ddFontFamily.isEmpty() ? ddFontFamily : labelFont.family() );
3390  newFontBuilt = true;
3391  newFont.setBold( ddBold );
3392  newFont.setItalic( ddItalic );
3393  }
3394  else if ( !ddFontStyle.isEmpty()
3395  && ddFontStyle.compare( "Ignore", Qt::CaseInsensitive ) != 0 )
3396  {
3397  if ( !ddFontFamily.isEmpty() )
3398  {
3399  // both family and style are different, build font from database
3400  QFont styledfont = mFontDB.font( ddFontFamily, ddFontStyle, appFont.pointSize() );
3401  if ( appFont != styledfont )
3402  {
3403  newFont = styledfont;
3404  newFontBuilt = true;
3405  }
3406  }
3407 
3408  // update the font face style
3409  QgsFontUtils::updateFontViaStyle( newFontBuilt ? newFont : labelFont, ddFontStyle );
3410  }
3411  else if ( !ddFontFamily.isEmpty() )
3412  {
3413  if ( ddFontStyle.compare( "Ignore", Qt::CaseInsensitive ) != 0 )
3414  {
3415  // just family is different, build font from database
3416  QFont styledfont = mFontDB.font( ddFontFamily, textNamedStyle, appFont.pointSize() );
3417  if ( appFont != styledfont )
3418  {
3419  newFont = styledfont;
3420  newFontBuilt = true;
3421  }
3422  }
3423  else
3424  {
3425  newFont = QFont( ddFontFamily );
3426  newFontBuilt = true;
3427  }
3428  }
3429 
3430  if ( newFontBuilt )
3431  {
3432  // copy over existing font settings
3433  //newFont = newFont.resolve( labelFont ); // should work, but let's be sure what's being copied
3434  newFont.setPixelSize( labelFont.pixelSize() );
3435  newFont.setUnderline( labelFont.underline() );
3436  newFont.setStrikeOut( labelFont.strikeOut() );
3437  newFont.setWordSpacing( labelFont.wordSpacing() );
3438  newFont.setLetterSpacing( QFont::AbsoluteSpacing, labelFont.letterSpacing() );
3439 
3440  labelFont = newFont;
3441  }
3442 
3443  // data defined word spacing?
3444  double wordspace = labelFont.wordSpacing();
3445  if ( dataDefinedEvaluate( QgsPalLayerSettings::FontWordSpacing, exprVal, &context.expressionContext(), wordspace ) )
3446  {
3447  bool ok;
3448  double wspacing = exprVal.toDouble( &ok );
3449  QgsDebugMsgLevel( QString( "exprVal FontWordSpacing:%1" ).arg( wspacing ), 4 );
3450  if ( ok )
3451  {
3452  wordspace = wspacing;
3453  }
3454  }
3455  labelFont.setWordSpacing( scaleToPixelContext( wordspace, context, fontunits, false, fontSizeMapUnitScale ) );
3456 
3457  // data defined letter spacing?
3458  double letterspace = labelFont.letterSpacing();
3459  if ( dataDefinedEvaluate( QgsPalLayerSettings::FontLetterSpacing, exprVal, &context.expressionContext(), letterspace ) )
3460  {
3461  bool ok;
3462  double lspacing = exprVal.toDouble( &ok );
3463  QgsDebugMsgLevel( QString( "exprVal FontLetterSpacing:%1" ).arg( lspacing ), 4 );
3464  if ( ok )
3465  {
3466  letterspace = lspacing;
3467  }
3468  }
3469  labelFont.setLetterSpacing( QFont::AbsoluteSpacing, scaleToPixelContext( letterspace, context, fontunits, false, fontSizeMapUnitScale ) );
3470 
3471  // data defined strikeout font style?
3472  if ( dataDefinedEvaluate( QgsPalLayerSettings::Strikeout, exprVal, &context.expressionContext(), labelFont.strikeOut() ) )
3473  {
3474  bool strikeout = exprVal.toBool();
3475  QgsDebugMsgLevel( QString( "exprVal Font strikeout:%1" ).arg( strikeout ? "true" : "false" ), 4 );
3476  labelFont.setStrikeOut( strikeout );
3477  }
3478 
3479  // data defined underline font style?
3480  if ( dataDefinedEvaluate( QgsPalLayerSettings::Underline, exprVal, &context.expressionContext(), labelFont.underline() ) )
3481  {
3482  bool underline = exprVal.toBool();
3483  QgsDebugMsgLevel( QString( "exprVal Font underline:%1" ).arg( underline ? "true" : "false" ), 4 );
3484  labelFont.setUnderline( underline );
3485  }
3486 
3487  // pass the rest on to QgsPalLabeling::drawLabeling
3488 
3489  // data defined font color?
3490  dataDefinedValEval( DDColor, QgsPalLayerSettings::Color, exprVal, context.expressionContext(), QgsSymbolLayerV2Utils::encodeColor( textColor ) );
3491 
3492  // data defined font transparency?
3493  dataDefinedValEval( DDTransparency, QgsPalLayerSettings::FontTransp, exprVal, context.expressionContext(), textTransp );
3494 
3495  // data defined font blend mode?
3496  dataDefinedValEval( DDBlendMode, QgsPalLayerSettings::FontBlendMode, exprVal, context.expressionContext() );
3497 
3498 }
3499 
3500 void QgsPalLayerSettings::parseTextBuffer( QgsRenderContext &context )
3501 {
3502  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
3503 
3504  // data defined draw buffer?
3505  bool drawBuffer = bufferDraw;
3506  if ( dataDefinedValEval( DDBool, QgsPalLayerSettings::BufferDraw, exprVal, context.expressionContext(), bufferDraw ) )
3507  {
3508  drawBuffer = exprVal.toBool();
3509  }
3510 
3511  if ( !drawBuffer )
3512  {
3513  return;
3514  }
3515 
3516  // data defined buffer size?
3517  double bufrSize = bufferSize;
3518  if ( dataDefinedValEval( DDDoublePos, QgsPalLayerSettings::BufferSize, exprVal, context.expressionContext(), bufferSize ) )
3519  {
3520  bufrSize = exprVal.toDouble();
3521  }
3522 
3523  // data defined buffer transparency?
3524  int bufTransp = bufferTransp;
3525  if ( dataDefinedValEval( DDTransparency, QgsPalLayerSettings::BufferTransp, exprVal, context.expressionContext(), bufferTransp ) )
3526  {
3527  bufTransp = exprVal.toInt();
3528  }
3529 
3530  drawBuffer = ( drawBuffer && bufrSize > 0.0 && bufTransp < 100 );
3531 
3532  if ( !drawBuffer )
3533  {
3534  dataDefinedValues.insert( QgsPalLayerSettings::BufferDraw, QVariant( false ) ); // trigger value
3535  dataDefinedValues.remove( QgsPalLayerSettings::BufferSize );
3536  dataDefinedValues.remove( QgsPalLayerSettings::BufferTransp );
3537  return; // don't bother evaluating values that won't be used
3538  }
3539 
3540  // data defined buffer units?
3541  dataDefinedValEval( DDUnits, QgsPalLayerSettings::BufferUnit, exprVal, context.expressionContext() );
3542 
3543  // data defined buffer color?
3544  dataDefinedValEval( DDColor, QgsPalLayerSettings::BufferColor, exprVal, context.expressionContext(), QgsSymbolLayerV2Utils::encodeColor( bufferColor ) );
3545 
3546  // data defined buffer pen join style?
3548 
3549  // data defined buffer blend mode?
3550  dataDefinedValEval( DDBlendMode, QgsPalLayerSettings::BufferBlendMode, exprVal, context.expressionContext() );
3551 }
3552 
3553 void QgsPalLayerSettings::parseTextFormatting( QgsRenderContext &context )
3554 {
3555  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
3556 
3557  // data defined multiline wrap character?
3558  QString wrapchr = wrapChar;
3559  if ( dataDefinedValEval( DDString, QgsPalLayerSettings::MultiLineWrapChar, exprVal, context.expressionContext(), wrapChar ) )
3560  {
3561  wrapchr = exprVal.toString();
3562  }
3563 
3564  // data defined multiline height?
3565  dataDefinedValEval( DDDouble, QgsPalLayerSettings::MultiLineHeight, exprVal, context.expressionContext() );
3566 
3567  // data defined multiline text align?
3569  {
3570  QString str = exprVal.toString().trimmed();
3571  QgsDebugMsgLevel( QString( "exprVal MultiLineAlignment:%1" ).arg( str ), 4 );
3572 
3573  if ( !str.isEmpty() )
3574  {
3575  // "Left"
3577 
3578  if ( str.compare( "Center", Qt::CaseInsensitive ) == 0 )
3579  {
3581  }
3582  else if ( str.compare( "Right", Qt::CaseInsensitive ) == 0 )
3583  {
3584  aligntype = QgsPalLayerSettings::MultiRight;
3585  }
3586  else if ( str.compare( "Follow", Qt::CaseInsensitive ) == 0 )
3587  {
3589  }
3590  dataDefinedValues.insert( QgsPalLayerSettings::MultiLineAlignment, QVariant( static_cast< int >( aligntype ) ) );
3591  }
3592  }
3593 
3594  // data defined direction symbol?
3595  bool drawDirSymb = addDirectionSymbol;
3596  if ( dataDefinedValEval( DDBool, QgsPalLayerSettings::DirSymbDraw, exprVal, context.expressionContext(), addDirectionSymbol ) )
3597  {
3598  drawDirSymb = exprVal.toBool();
3599  }
3600 
3601  if ( drawDirSymb )
3602  {
3603  // data defined direction left symbol?
3604  dataDefinedValEval( DDString, QgsPalLayerSettings::DirSymbLeft, exprVal, context.expressionContext(), leftDirectionSymbol );
3605 
3606  // data defined direction right symbol?
3607  dataDefinedValEval( DDString, QgsPalLayerSettings::DirSymbRight, exprVal, context.expressionContext(), rightDirectionSymbol );
3608 
3609  // data defined direction symbol placement?
3611  {
3612  QString str = exprVal.toString().trimmed();
3613  QgsDebugMsgLevel( QString( "exprVal DirSymbPlacement:%1" ).arg( str ), 4 );
3614 
3615  if ( !str.isEmpty() )
3616  {
3617  // "LeftRight"
3619 
3620  if ( str.compare( "Above", Qt::CaseInsensitive ) == 0 )
3621  {
3623  }
3624  else if ( str.compare( "Below", Qt::CaseInsensitive ) == 0 )
3625  {
3627  }
3628  dataDefinedValues.insert( QgsPalLayerSettings::DirSymbPlacement, QVariant( static_cast< int >( placetype ) ) );
3629  }
3630  }
3631 
3632  // data defined direction symbol reversed?
3633  dataDefinedValEval( DDBool, QgsPalLayerSettings::DirSymbReverse, exprVal, context.expressionContext(), reverseDirectionSymbol );
3634  }
3635 
3636  // formatting for numbers is inline with generation of base label text and not passed to label painting
3637 }
3638 
3639 void QgsPalLayerSettings::parseShapeBackground( QgsRenderContext &context )
3640 {
3641  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
3642 
3643  // data defined draw shape?
3644  bool drawShape = shapeDraw;
3645  if ( dataDefinedValEval( DDBool, QgsPalLayerSettings::ShapeDraw, exprVal, context.expressionContext(), shapeDraw ) )
3646  {
3647  drawShape = exprVal.toBool();
3648  }
3649 
3650  if ( !drawShape )
3651  {
3652  return;
3653  }
3654 
3655  // data defined shape transparency?
3656  int shapeTransp = shapeTransparency;
3657  if ( dataDefinedValEval( DDTransparency, QgsPalLayerSettings::ShapeTransparency, exprVal, context.expressionContext(), shapeTransparency ) )
3658  {
3659  shapeTransp = exprVal.toInt();
3660  }
3661 
3662  drawShape = ( drawShape && shapeTransp < 100 ); // size is not taken into account (could be)
3663 
3664  if ( !drawShape )
3665  {
3666  dataDefinedValues.insert( QgsPalLayerSettings::ShapeDraw, QVariant( false ) ); // trigger value
3667  dataDefinedValues.remove( QgsPalLayerSettings::ShapeTransparency );
3668  return; // don't bother evaluating values that won't be used
3669  }
3670 
3671  // data defined shape kind?
3674  {
3675  QString skind = exprVal.toString().trimmed();
3676  QgsDebugMsgLevel( QString( "exprVal ShapeKind:%1" ).arg( skind ), 4 );
3677 
3678  if ( !skind.isEmpty() )
3679  {
3680  // "Rectangle"
3682 
3683  if ( skind.compare( "Square", Qt::CaseInsensitive ) == 0 )
3684  {
3686  }
3687  else if ( skind.compare( "Ellipse", Qt::CaseInsensitive ) == 0 )
3688  {
3690  }
3691  else if ( skind.compare( "Circle", Qt::CaseInsensitive ) == 0 )
3692  {
3694  }
3695  else if ( skind.compare( "SVG", Qt::CaseInsensitive ) == 0 )
3696  {
3698  }
3699  shapeKind = shpkind;
3700  dataDefinedValues.insert( QgsPalLayerSettings::ShapeKind, QVariant( static_cast< int >( shpkind ) ) );
3701  }
3702  }
3703 
3704  // data defined shape SVG path?
3705  QString svgPath = shapeSVGFile;
3707  {
3708  QString svgfile = exprVal.toString().trimmed();
3709  QgsDebugMsgLevel( QString( "exprVal ShapeSVGFile:%1" ).arg( svgfile ), 4 );
3710 
3711  // '' empty paths are allowed
3712  svgPath = svgfile;
3713  dataDefinedValues.insert( QgsPalLayerSettings::ShapeSVGFile, QVariant( svgfile ) );
3714  }
3715 
3716  // data defined shape size type?
3719  {
3720  QString stype = exprVal.toString().trimmed();
3721  QgsDebugMsgLevel( QString( "exprVal ShapeSizeType:%1" ).arg( stype ), 4 );
3722 
3723  if ( !stype.isEmpty() )
3724  {
3725  // "Buffer"
3727 
3728  if ( stype.compare( "Fixed", Qt::CaseInsensitive ) == 0 )
3729  {
3731  }
3732  shpSizeType = sizType;
3733  dataDefinedValues.insert( QgsPalLayerSettings::ShapeSizeType, QVariant( static_cast< int >( sizType ) ) );
3734  }
3735  }
3736 
3737  // data defined shape size X? (SVGs only use X for sizing)
3738  double ddShpSizeX = shapeSize.x();
3739  if ( dataDefinedValEval( DDDouble, QgsPalLayerSettings::ShapeSizeX, exprVal, context.expressionContext(), ddShpSizeX ) )
3740  {
3741  ddShpSizeX = exprVal.toDouble();
3742  }
3743 
3744  // data defined shape size Y?
3745  double ddShpSizeY = shapeSize.y();
3746  if ( dataDefinedValEval( DDDouble, QgsPalLayerSettings::ShapeSizeY, exprVal, context.expressionContext(), ddShpSizeY ) )
3747  {
3748  ddShpSizeY = exprVal.toDouble();
3749  }
3750 
3751  // don't continue under certain circumstances (e.g. size is fixed)
3752  bool skip = false;
3753  if ( shapeKind == QgsPalLayerSettings::ShapeSVG
3754  && ( svgPath.isEmpty()
3755  || ( !svgPath.isEmpty()
3756  && shpSizeType == QgsPalLayerSettings::SizeFixed
3757  && ddShpSizeX == 0.0 ) ) )
3758  {
3759  skip = true;
3760  }
3761  if ( shapeKind != QgsPalLayerSettings::ShapeSVG
3762  && shpSizeType == QgsPalLayerSettings::SizeFixed
3763  && ( ddShpSizeX == 0.0 || ddShpSizeY == 0.0 ) )
3764  {
3765  skip = true;
3766  }
3767 
3768  if ( skip )
3769  {
3770  dataDefinedValues.insert( QgsPalLayerSettings::ShapeDraw, QVariant( false ) ); // trigger value
3771  dataDefinedValues.remove( QgsPalLayerSettings::ShapeTransparency );
3772  dataDefinedValues.remove( QgsPalLayerSettings::ShapeKind );
3773  dataDefinedValues.remove( QgsPalLayerSettings::ShapeSVGFile );
3774  dataDefinedValues.remove( QgsPalLayerSettings::ShapeSizeX );
3775  dataDefinedValues.remove( QgsPalLayerSettings::ShapeSizeY );
3776  return; // don't bother evaluating values that won't be used
3777  }
3778 
3779  // data defined shape size units?
3780  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShapeSizeUnits, exprVal, context.expressionContext() );
3781 
3782  // data defined shape rotation type?
3784  {
3785  QString rotstr = exprVal.toString().trimmed();
3786  QgsDebugMsgLevel( QString( "exprVal ShapeRotationType:%1" ).arg( rotstr ), 4 );
3787 
3788  if ( !rotstr.isEmpty() )
3789  {
3790  // "Sync"
3792 
3793  if ( rotstr.compare( "Offset", Qt::CaseInsensitive ) == 0 )
3794  {
3796  }
3797  else if ( rotstr.compare( "Fixed", Qt::CaseInsensitive ) == 0 )
3798  {
3800  }
3801  dataDefinedValues.insert( QgsPalLayerSettings::ShapeRotationType, QVariant( static_cast< int >( rottype ) ) );
3802  }
3803  }
3804 
3805  // data defined shape rotation?
3806  dataDefinedValEval( DDRotation180, QgsPalLayerSettings::ShapeRotation, exprVal, context.expressionContext(), shapeRotation );
3807 
3808  // data defined shape offset?
3809  dataDefinedValEval( DDPointF, QgsPalLayerSettings::ShapeOffset, exprVal, context.expressionContext(), QgsSymbolLayerV2Utils::encodePoint( shapeOffset ) );
3810 
3811  // data defined shape offset units?
3812  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShapeOffsetUnits, exprVal, context.expressionContext() );
3813 
3814  // data defined shape radii?
3815  dataDefinedValEval( DDPointF, QgsPalLayerSettings::ShapeRadii, exprVal, context.expressionContext(), QgsSymbolLayerV2Utils::encodePoint( shapeRadii ) );
3816 
3817  // data defined shape radii units?
3818  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShapeRadiiUnits, exprVal, context.expressionContext() );
3819 
3820  // data defined shape blend mode?
3821  dataDefinedValEval( DDBlendMode, QgsPalLayerSettings::ShapeBlendMode, exprVal, context.expressionContext() );
3822 
3823  // data defined shape fill color?
3824  dataDefinedValEval( DDColor, QgsPalLayerSettings::ShapeFillColor, exprVal, context.expressionContext(), QgsSymbolLayerV2Utils::encodeColor( shapeFillColor ) );
3825 
3826  // data defined shape border color?
3828 
3829  // data defined shape border width?
3830  dataDefinedValEval( DDDoublePos, QgsPalLayerSettings::ShapeBorderWidth, exprVal, context.expressionContext(), shapeBorderWidth );
3831 
3832  // data defined shape border width units?
3833  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShapeBorderWidthUnits, exprVal, context.expressionContext() );
3834 
3835  // data defined shape join style?
3837 
3838 }
3839 
3840 void QgsPalLayerSettings::parseDropShadow( QgsRenderContext &context )
3841 {
3842  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
3843 
3844  // data defined draw shadow?
3845  bool drawShadow = shadowDraw;
3846  if ( dataDefinedValEval( DDBool, QgsPalLayerSettings::ShadowDraw, exprVal, context.expressionContext(), shadowDraw ) )
3847  {
3848  drawShadow = exprVal.toBool();
3849  }
3850 
3851  if ( !drawShadow )
3852  {
3853  return;
3854  }
3855 
3856  // data defined shadow transparency?
3857  int shadowTransp = shadowTransparency;
3858  if ( dataDefinedValEval( DDTransparency, QgsPalLayerSettings::ShadowTransparency, exprVal, context.expressionContext(), shadowTransparency ) )
3859  {
3860  shadowTransp = exprVal.toInt();
3861  }
3862 
3863  // data defined shadow offset distance?
3864  double shadowOffDist = shadowOffsetDist;
3865  if ( dataDefinedValEval( DDDoublePos, QgsPalLayerSettings::ShadowOffsetDist, exprVal, context.expressionContext(), shadowOffsetDist ) )
3866  {
3867  shadowOffDist = exprVal.toDouble();
3868  }
3869 
3870  // data defined shadow offset distance?
3871  double shadowRad = shadowRadius;
3872  if ( dataDefinedValEval( DDDoublePos, QgsPalLayerSettings::ShadowRadius, exprVal, context.expressionContext(), shadowRadius ) )
3873  {
3874  shadowRad = exprVal.toDouble();
3875  }
3876 
3877  drawShadow = ( drawShadow && shadowTransp < 100 && !( shadowOffDist == 0.0 && shadowRad == 0.0 ) );
3878 
3879  if ( !drawShadow )
3880  {
3881  dataDefinedValues.insert( QgsPalLayerSettings::ShadowDraw, QVariant( false ) ); // trigger value
3882  dataDefinedValues.remove( QgsPalLayerSettings::ShadowTransparency );
3883  dataDefinedValues.remove( QgsPalLayerSettings::ShadowOffsetDist );
3884  dataDefinedValues.remove( QgsPalLayerSettings::ShadowRadius );
3885  return; // don't bother evaluating values that won't be used
3886  }
3887 
3888  // data defined shadow under type?
3890  {
3891  QString str = exprVal.toString().trimmed();
3892  QgsDebugMsgLevel( QString( "exprVal ShadowUnder:%1" ).arg( str ), 4 );
3893 
3894  if ( !str.isEmpty() )
3895  {
3896  // "Lowest"
3898 
3899  if ( str.compare( "Text", Qt::CaseInsensitive ) == 0 )
3900  {
3902  }
3903  else if ( str.compare( "Buffer", Qt::CaseInsensitive ) == 0 )
3904  {
3906  }
3907  else if ( str.compare( "Background", Qt::CaseInsensitive ) == 0 )
3908  {
3910  }
3911  dataDefinedValues.insert( QgsPalLayerSettings::ShadowUnder, QVariant( static_cast< int >( shdwtype ) ) );
3912  }
3913  }
3914 
3915  // data defined shadow offset angle?
3916  dataDefinedValEval( DDRotation180, QgsPalLayerSettings::ShadowOffsetAngle, exprVal, context.expressionContext(), shadowOffsetAngle );
3917 
3918  // data defined shadow offset units?
3919  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShadowOffsetUnits, exprVal, context.expressionContext() );
3920 
3921  // data defined shadow radius?
3922  dataDefinedValEval( DDDouble, QgsPalLayerSettings::ShadowRadius, exprVal, context.expressionContext(), shadowRadius );
3923 
3924  // data defined shadow radius units?
3925  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShadowRadiusUnits, exprVal, context.expressionContext() );
3926 
3927  // data defined shadow scale? ( gui bounds to 0-2000, no upper bound here )
3928  dataDefinedValEval( DDIntPos, QgsPalLayerSettings::ShadowScale, exprVal, context.expressionContext(), shadowScale );
3929 
3930  // data defined shadow color?