QGIS API Documentation  3.0.2-Girona (307d082)
qgstextrenderer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgstextrenderer.cpp
3  -------------------s
4  begin : September 2015
5  copyright : (C) Nyall Dawson
6  email : nyall dot dawson at gmail dot com
7 
8  ***************************************************************************
9  * *
10  * This program is free software; you can redistribute it and/or modify *
11  * it under the terms of the GNU General Public License as published by *
12  * the Free Software Foundation; either version 2 of the License, or *
13  * (at your option) any later version. *
14  * *
15  ***************************************************************************/
16 
17 #include "qgstextrenderer.h"
18 #include "qgis.h"
19 #include "qgstextrenderer_p.h"
20 #include "qgsfontutils.h"
21 #include "qgspathresolver.h"
22 #include "qgsreadwritecontext.h"
23 #include "qgsvectorlayer.h"
24 #include "qgssymbollayerutils.h"
25 #include "qgspainting.h"
26 #include "qgsmarkersymbollayer.h"
27 #include "qgspainteffectregistry.h"
28 #include <QFontDatabase>
29 
30 Q_GUI_EXPORT extern int qt_defaultDpiX();
31 Q_GUI_EXPORT extern int qt_defaultDpiY();
32 
34 {
35  if ( val == 0 )
37  else if ( val == 1 )
39  else if ( val == 2 )
41  else if ( val == 3 )
43  else
45 }
46 
47 static void _fixQPictureDPI( QPainter *p )
48 {
49  // QPicture makes an assumption that we drawing to it with system DPI.
50  // Then when being drawn, it scales the painter. The following call
51  // negates the effect. There is no way of setting QPicture's DPI.
52  // See QTBUG-20361
53  p->scale( static_cast< double >( qt_defaultDpiX() ) / p->device()->logicalDpiX(),
54  static_cast< double >( qt_defaultDpiY() ) / p->device()->logicalDpiY() );
55 }
56 
57 static QColor _readColor( QgsVectorLayer *layer, const QString &property, const QColor &defaultColor = Qt::black, bool withAlpha = true )
58 {
59  int r = layer->customProperty( property + 'R', QVariant( defaultColor.red() ) ).toInt();
60  int g = layer->customProperty( property + 'G', QVariant( defaultColor.green() ) ).toInt();
61  int b = layer->customProperty( property + 'B', QVariant( defaultColor.blue() ) ).toInt();
62  int a = withAlpha ? layer->customProperty( property + 'A', QVariant( defaultColor.alpha() ) ).toInt() : 255;
63  return QColor( r, g, b, a );
64 }
65 
67 {
68  d = new QgsTextBufferSettingsPrivate();
69 }
70 
72  : d( other.d )
73 {
74 }
75 
77 {
78  d = other.d;
79  return *this;
80 }
81 
83 {
84 
85 }
86 
88 {
89  return d->enabled;
90 }
91 
93 {
94  d->enabled = enabled;
95 }
96 
98 {
99  return d->size;
100 }
101 
103 {
104  d->size = size;
105 }
106 
108 {
109  return d->sizeUnit;
110 }
111 
113 {
114  d->sizeUnit = unit;
115 }
116 
118 {
119  return d->sizeMapUnitScale;
120 }
121 
123 {
124  d->sizeMapUnitScale = scale;
125 }
126 
128 {
129  return d->color;
130 }
131 
133 {
134  d->color = color;
135 }
136 
138 {
139  return d->fillBufferInterior;
140 }
141 
143 {
144  d->fillBufferInterior = fill;
145 }
146 
148 {
149  return d->opacity;
150 }
151 
153 {
154  d->opacity = opacity;
155 }
156 
157 Qt::PenJoinStyle QgsTextBufferSettings::joinStyle() const
158 {
159  return d->joinStyle;
160 }
161 
162 void QgsTextBufferSettings::setJoinStyle( Qt::PenJoinStyle style )
163 {
164  d->joinStyle = style;
165 }
166 
167 QPainter::CompositionMode QgsTextBufferSettings::blendMode() const
168 {
169  return d->blendMode;
170 }
171 
172 void QgsTextBufferSettings::setBlendMode( QPainter::CompositionMode mode )
173 {
174  d->blendMode = mode;
175 }
176 
178 {
179  return d->paintEffect;
180 }
181 
183 {
184  delete d->paintEffect;
185  d->paintEffect = effect;
186 }
187 
189 {
190  // text buffer
191  double bufSize = layer->customProperty( QStringLiteral( "labeling/bufferSize" ), QVariant( 0.0 ) ).toDouble();
192 
193  // fix for buffer being keyed off of just its size in the past (<2.0)
194  QVariant drawBuffer = layer->customProperty( QStringLiteral( "labeling/bufferDraw" ), QVariant() );
195  if ( drawBuffer.isValid() )
196  {
197  d->enabled = drawBuffer.toBool();
198  d->size = bufSize;
199  }
200  else if ( bufSize != 0.0 )
201  {
202  d->enabled = true;
203  d->size = bufSize;
204  }
205  else
206  {
207  // keep bufferSize at new 1.0 default
208  d->enabled = false;
209  }
210 
211  if ( layer->customProperty( QStringLiteral( "labeling/bufferSizeUnits" ) ).toString().isEmpty() )
212  {
213  bool bufferSizeInMapUnits = layer->customProperty( QStringLiteral( "labeling/bufferSizeInMapUnits" ) ).toBool();
214  d->sizeUnit = bufferSizeInMapUnits ? QgsUnitTypes::RenderMapUnits : QgsUnitTypes::RenderMillimeters;
215  }
216  else
217  {
218  d->sizeUnit = QgsUnitTypes::decodeRenderUnit( layer->customProperty( QStringLiteral( "labeling/bufferSizeUnits" ) ).toString() );
219  }
220 
221  if ( layer->customProperty( QStringLiteral( "labeling/bufferSizeMapUnitScale" ) ).toString().isEmpty() )
222  {
223  //fallback to older property
224  double oldMin = layer->customProperty( QStringLiteral( "labeling/bufferSizeMapUnitMinScale" ), 0.0 ).toDouble();
225  d->sizeMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
226  double oldMax = layer->customProperty( QStringLiteral( "labeling/bufferSizeMapUnitMaxScale" ), 0.0 ).toDouble();
227  d->sizeMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
228  }
229  else
230  {
231  d->sizeMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( layer->customProperty( QStringLiteral( "labeling/bufferSizeMapUnitScale" ) ).toString() );
232  }
233  d->color = _readColor( layer, QStringLiteral( "labeling/bufferColor" ), Qt::white, false );
234  if ( layer->customProperty( QStringLiteral( "labeling/bufferOpacity" ) ).toString().isEmpty() )
235  {
236  d->opacity = ( 1 - layer->customProperty( QStringLiteral( "labeling/bufferTransp" ) ).toInt() / 100.0 ); //0 -100
237  }
238  else
239  {
240  d->opacity = ( layer->customProperty( QStringLiteral( "labeling/bufferOpacity" ) ).toDouble() );
241  }
242  d->blendMode = QgsPainting::getCompositionMode(
243  static_cast< QgsPainting::BlendMode >( layer->customProperty( QStringLiteral( "labeling/bufferBlendMode" ), QVariant( QgsPainting::BlendNormal ) ).toUInt() ) );
244  d->joinStyle = static_cast< Qt::PenJoinStyle >( layer->customProperty( QStringLiteral( "labeling/bufferJoinStyle" ), QVariant( Qt::RoundJoin ) ).toUInt() );
245 
246  d->fillBufferInterior = !layer->customProperty( QStringLiteral( "labeling/bufferNoFill" ), QVariant( false ) ).toBool();
247 
248  if ( layer->customProperty( QStringLiteral( "labeling/bufferEffect" ) ).isValid() )
249  {
250  QDomDocument doc( QStringLiteral( "effect" ) );
251  doc.setContent( layer->customProperty( QStringLiteral( "labeling/bufferEffect" ) ).toString() );
252  QDomElement effectElem = doc.firstChildElement( QStringLiteral( "effect" ) ).firstChildElement( QStringLiteral( "effect" ) );
253  setPaintEffect( QgsApplication::paintEffectRegistry()->createEffect( effectElem ) );
254  }
255  else
256  setPaintEffect( nullptr );
257 }
258 
259 void QgsTextBufferSettings::readXml( const QDomElement &elem )
260 {
261  QDomElement textBufferElem = elem.firstChildElement( QStringLiteral( "text-buffer" ) );
262  double bufSize = textBufferElem.attribute( QStringLiteral( "bufferSize" ), QStringLiteral( "0" ) ).toDouble();
263 
264  // fix for buffer being keyed off of just its size in the past (<2.0)
265  QVariant drawBuffer = textBufferElem.attribute( QStringLiteral( "bufferDraw" ) );
266  if ( drawBuffer.isValid() )
267  {
268  d->enabled = drawBuffer.toBool();
269  d->size = bufSize;
270  }
271  else if ( bufSize != 0.0 )
272  {
273  d->enabled = true;
274  d->size = bufSize;
275  }
276  else
277  {
278  // keep bufferSize at new 1.0 default
279  d->enabled = false;
280  }
281 
282  if ( !textBufferElem.hasAttribute( QStringLiteral( "bufferSizeUnits" ) ) )
283  {
284  bool bufferSizeInMapUnits = textBufferElem.attribute( QStringLiteral( "bufferSizeInMapUnits" ) ).toInt();
285  d->sizeUnit = bufferSizeInMapUnits ? QgsUnitTypes::RenderMapUnits : QgsUnitTypes::RenderMillimeters;
286  }
287  else
288  {
289  d->sizeUnit = QgsUnitTypes::decodeRenderUnit( textBufferElem.attribute( QStringLiteral( "bufferSizeUnits" ) ) );
290  }
291 
292  if ( !textBufferElem.hasAttribute( QStringLiteral( "bufferSizeMapUnitScale" ) ) )
293  {
294  //fallback to older property
295  double oldMin = textBufferElem.attribute( QStringLiteral( "bufferSizeMapUnitMinScale" ), QStringLiteral( "0" ) ).toDouble();
296  d->sizeMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
297  double oldMax = textBufferElem.attribute( QStringLiteral( "bufferSizeMapUnitMaxScale" ), QStringLiteral( "0" ) ).toDouble();
298  d->sizeMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
299  }
300  else
301  {
302  d->sizeMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( textBufferElem.attribute( QStringLiteral( "bufferSizeMapUnitScale" ) ) );
303  }
304  d->color = QgsSymbolLayerUtils::decodeColor( textBufferElem.attribute( QStringLiteral( "bufferColor" ), QgsSymbolLayerUtils::encodeColor( Qt::white ) ) );
305 
306  if ( !textBufferElem.hasAttribute( QStringLiteral( "bufferOpacity" ) ) )
307  {
308  d->opacity = ( 1 - textBufferElem.attribute( QStringLiteral( "bufferTransp" ) ).toInt() / 100.0 ); //0 -100
309  }
310  else
311  {
312  d->opacity = ( textBufferElem.attribute( QStringLiteral( "bufferOpacity" ) ).toDouble() );
313  }
314 
315  d->blendMode = QgsPainting::getCompositionMode(
316  static_cast< QgsPainting::BlendMode >( textBufferElem.attribute( QStringLiteral( "bufferBlendMode" ), QString::number( QgsPainting::BlendNormal ) ).toUInt() ) );
317  d->joinStyle = static_cast< Qt::PenJoinStyle >( textBufferElem.attribute( QStringLiteral( "bufferJoinStyle" ), QString::number( Qt::RoundJoin ) ).toUInt() );
318  d->fillBufferInterior = !textBufferElem.attribute( QStringLiteral( "bufferNoFill" ), QStringLiteral( "0" ) ).toInt();
319  QDomElement effectElem = textBufferElem.firstChildElement( QStringLiteral( "effect" ) );
320  if ( !effectElem.isNull() )
322  else
323  setPaintEffect( nullptr );
324 }
325 
326 QDomElement QgsTextBufferSettings::writeXml( QDomDocument &doc ) const
327 {
328  // text buffer
329  QDomElement textBufferElem = doc.createElement( QStringLiteral( "text-buffer" ) );
330  textBufferElem.setAttribute( QStringLiteral( "bufferDraw" ), d->enabled );
331  textBufferElem.setAttribute( QStringLiteral( "bufferSize" ), d->size );
332  textBufferElem.setAttribute( QStringLiteral( "bufferSizeUnits" ), QgsUnitTypes::encodeUnit( d->sizeUnit ) );
333  textBufferElem.setAttribute( QStringLiteral( "bufferSizeMapUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( d->sizeMapUnitScale ) );
334  textBufferElem.setAttribute( QStringLiteral( "bufferColor" ), QgsSymbolLayerUtils::encodeColor( d->color ) );
335  textBufferElem.setAttribute( QStringLiteral( "bufferNoFill" ), !d->fillBufferInterior );
336  textBufferElem.setAttribute( QStringLiteral( "bufferOpacity" ), d->opacity );
337  textBufferElem.setAttribute( QStringLiteral( "bufferJoinStyle" ), static_cast< unsigned int >( d->joinStyle ) );
338  textBufferElem.setAttribute( QStringLiteral( "bufferBlendMode" ), QgsPainting::getBlendModeEnum( d->blendMode ) );
339  if ( d->paintEffect && !QgsPaintEffectRegistry::isDefaultStack( d->paintEffect ) )
340  d->paintEffect->saveProperties( doc, textBufferElem );
341  return textBufferElem;
342 }
343 
344 
345 //
346 // QgsTextBackgroundSettings
347 //
348 
350 {
351  d = new QgsTextBackgroundSettingsPrivate();
352 }
353 
355  : d( other.d )
356 {
357 
358 }
359 
361 {
362  d = other.d;
363  return *this;
364 }
365 
367 {
368 
369 }
370 
372 {
373  return d->enabled;
374 }
375 
377 {
378  d->enabled = enabled;
379 }
380 
382 {
383  return d->type;
384 }
385 
387 {
388  d->type = type;
389 }
390 
392 {
393  return d->svgFile;
394 }
395 
396 void QgsTextBackgroundSettings::setSvgFile( const QString &file )
397 {
398  d->svgFile = file;
399 }
400 
402 {
403  return d->sizeType;
404 }
405 
407 {
408  d->sizeType = type;
409 }
410 
412 {
413  return d->size;
414 }
415 
417 {
418  d->size = size;
419 }
420 
422 {
423  return d->sizeUnits;
424 }
425 
427 {
428  d->sizeUnits = unit;
429 }
430 
432 {
433  return d->sizeMapUnitScale;
434 }
435 
437 {
438  d->sizeMapUnitScale = scale;
439 }
440 
442 {
443  return d->rotationType;
444 }
445 
447 {
448  d->rotationType = type;
449 }
450 
452 {
453  return d->rotation;
454 }
455 
457 {
458  d->rotation = rotation;
459 }
460 
462 {
463  return d->offset;
464 }
465 
467 {
468  d->offset = offset;
469 }
470 
472 {
473  return d->offsetUnits;
474 }
475 
477 {
478  d->offsetUnits = units;
479 }
480 
482 {
483  return d->offsetMapUnitScale;
484 }
485 
487 {
488  d->offsetMapUnitScale = scale;
489 }
490 
492 {
493  return d->radii;
494 }
495 
497 {
498  d->radii = radii;
499 }
500 
502 {
503  return d->radiiUnits;
504 }
505 
507 {
508  d->radiiUnits = units;
509 }
510 
512 {
513  return d->radiiMapUnitScale;
514 }
515 
517 {
518  d->radiiMapUnitScale = scale;
519 }
520 
522 {
523  return d->opacity;
524 }
525 
527 {
528  d->opacity = opacity;
529 }
530 
531 QPainter::CompositionMode QgsTextBackgroundSettings::blendMode() const
532 {
533  return d->blendMode;
534 }
535 
536 void QgsTextBackgroundSettings::setBlendMode( QPainter::CompositionMode mode )
537 {
538  d->blendMode = mode;
539 }
540 
542 {
543  return d->fillColor;
544 }
545 
546 void QgsTextBackgroundSettings::setFillColor( const QColor &color )
547 {
548  d->fillColor = color;
549 }
550 
552 {
553  return d->strokeColor;
554 }
555 
556 void QgsTextBackgroundSettings::setStrokeColor( const QColor &color )
557 {
558  d->strokeColor = color;
559 }
560 
562 {
563  return d->strokeWidth;
564 }
565 
567 {
568  d->strokeWidth = width;
569 }
570 
572 {
573  return d->strokeWidthUnits;
574 }
575 
577 {
578  d->strokeWidthUnits = units;
579 }
580 
582 {
583  return d->strokeWidthMapUnitScale;
584 }
585 
587 {
588  d->strokeWidthMapUnitScale = scale;
589 }
590 
591 Qt::PenJoinStyle QgsTextBackgroundSettings::joinStyle() const
592 {
593  return d->joinStyle;
594 }
595 
596 void QgsTextBackgroundSettings::setJoinStyle( Qt::PenJoinStyle style )
597 {
598  d->joinStyle = style;
599 }
600 
602 {
603  return d->paintEffect;
604 }
605 
607 {
608  delete d->paintEffect;
609  d->paintEffect = effect;
610 }
611 
613 {
614  d->enabled = layer->customProperty( QStringLiteral( "labeling/shapeDraw" ), QVariant( false ) ).toBool();
615  d->type = static_cast< ShapeType >( layer->customProperty( QStringLiteral( "labeling/shapeType" ), QVariant( ShapeRectangle ) ).toUInt() );
616  d->svgFile = layer->customProperty( QStringLiteral( "labeling/shapeSVGFile" ), QVariant( "" ) ).toString();
617  d->sizeType = static_cast< SizeType >( layer->customProperty( QStringLiteral( "labeling/shapeSizeType" ), QVariant( SizeBuffer ) ).toUInt() );
618  d->size = QSizeF( layer->customProperty( QStringLiteral( "labeling/shapeSizeX" ), QVariant( 0.0 ) ).toDouble(),
619  layer->customProperty( QStringLiteral( "labeling/shapeSizeY" ), QVariant( 0.0 ) ).toDouble() );
620 
621  if ( layer->customProperty( QStringLiteral( "labeling/shapeSizeUnit" ) ).toString().isEmpty() )
622  {
623  d->sizeUnits = convertFromOldLabelUnit( layer->customProperty( QStringLiteral( "labeling/shapeSizeUnits" ), 0 ).toUInt() );
624  }
625  else
626  {
627  d->sizeUnits = QgsUnitTypes::decodeRenderUnit( layer->customProperty( QStringLiteral( "labeling/shapeSizeUnit" ) ).toString() );
628  }
629 
630  if ( layer->customProperty( QStringLiteral( "labeling/shapeSizeMapUnitScale" ) ).toString().isEmpty() )
631  {
632  //fallback to older property
633  double oldMin = layer->customProperty( QStringLiteral( "labeling/shapeSizeMapUnitMinScale" ), 0.0 ).toDouble();
634  d->sizeMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
635  double oldMax = layer->customProperty( QStringLiteral( "labeling/shapeSizeMapUnitMaxScale" ), 0.0 ).toDouble();
636  d->sizeMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
637  }
638  else
639  {
640  d->sizeMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( layer->customProperty( QStringLiteral( "labeling/shapeSizeMapUnitScale" ) ).toString() );
641  }
642  d->rotationType = static_cast< RotationType >( layer->customProperty( QStringLiteral( "labeling/shapeRotationType" ), QVariant( RotationSync ) ).toUInt() );
643  d->rotation = layer->customProperty( QStringLiteral( "labeling/shapeRotation" ), QVariant( 0.0 ) ).toDouble();
644  d->offset = QPointF( layer->customProperty( QStringLiteral( "labeling/shapeOffsetX" ), QVariant( 0.0 ) ).toDouble(),
645  layer->customProperty( QStringLiteral( "labeling/shapeOffsetY" ), QVariant( 0.0 ) ).toDouble() );
646 
647  if ( layer->customProperty( QStringLiteral( "labeling/shapeOffsetUnit" ) ).toString().isEmpty() )
648  {
649  d->offsetUnits = convertFromOldLabelUnit( layer->customProperty( QStringLiteral( "labeling/shapeOffsetUnits" ), 0 ).toUInt() );
650  }
651  else
652  {
653  d->offsetUnits = QgsUnitTypes::decodeRenderUnit( layer->customProperty( QStringLiteral( "labeling/shapeOffsetUnit" ) ).toString() );
654  }
655 
656  if ( layer->customProperty( QStringLiteral( "labeling/shapeOffsetMapUnitScale" ) ).toString().isEmpty() )
657  {
658  //fallback to older property
659  double oldMin = layer->customProperty( QStringLiteral( "labeling/shapeOffsetMapUnitMinScale" ), 0.0 ).toDouble();
660  d->offsetMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
661  double oldMax = layer->customProperty( QStringLiteral( "labeling/shapeOffsetMapUnitMaxScale" ), 0.0 ).toDouble();
662  d->offsetMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
663  }
664  else
665  {
666  d->offsetMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( layer->customProperty( QStringLiteral( "labeling/shapeOffsetMapUnitScale" ) ).toString() );
667  }
668  d->radii = QSizeF( layer->customProperty( QStringLiteral( "labeling/shapeRadiiX" ), QVariant( 0.0 ) ).toDouble(),
669  layer->customProperty( QStringLiteral( "labeling/shapeRadiiY" ), QVariant( 0.0 ) ).toDouble() );
670 
671 
672  if ( layer->customProperty( QStringLiteral( "labeling/shapeRadiiUnit" ) ).toString().isEmpty() )
673  {
674  d->radiiUnits = convertFromOldLabelUnit( layer->customProperty( QStringLiteral( "labeling/shapeRadiiUnits" ), 0 ).toUInt() );
675  }
676  else
677  {
678  d->radiiUnits = QgsUnitTypes::decodeRenderUnit( layer->customProperty( QStringLiteral( "labeling/shapeRadiiUnit" ) ).toString() );
679  }
680 
681  if ( layer->customProperty( QStringLiteral( "labeling/shapeRadiiMapUnitScale" ) ).toString().isEmpty() )
682  {
683  //fallback to older property
684  double oldMin = layer->customProperty( QStringLiteral( "labeling/shapeRadiiMapUnitMinScale" ), 0.0 ).toDouble();
685  d->radiiMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
686  double oldMax = layer->customProperty( QStringLiteral( "labeling/shapeRadiiMapUnitMaxScale" ), 0.0 ).toDouble();
687  d->radiiMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
688  }
689  else
690  {
691  d->radiiMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( layer->customProperty( QStringLiteral( "labeling/shapeRadiiMapUnitScale" ) ).toString() );
692  }
693  d->fillColor = _readColor( layer, QStringLiteral( "labeling/shapeFillColor" ), Qt::white, true );
694  d->strokeColor = _readColor( layer, QStringLiteral( "labeling/shapeBorderColor" ), Qt::darkGray, true );
695  d->strokeWidth = layer->customProperty( QStringLiteral( "labeling/shapeBorderWidth" ), QVariant( .0 ) ).toDouble();
696  if ( layer->customProperty( QStringLiteral( "labeling/shapeBorderWidthUnit" ) ).toString().isEmpty() )
697  {
698  d->strokeWidthUnits = convertFromOldLabelUnit( layer->customProperty( QStringLiteral( "labeling/shapeBorderWidthUnits" ), 0 ).toUInt() );
699  }
700  else
701  {
702  d->strokeWidthUnits = QgsUnitTypes::decodeRenderUnit( layer->customProperty( QStringLiteral( "labeling/shapeBorderWidthUnit" ) ).toString() );
703  }
704  if ( layer->customProperty( QStringLiteral( "labeling/shapeBorderWidthMapUnitScale" ) ).toString().isEmpty() )
705  {
706  //fallback to older property
707  double oldMin = layer->customProperty( QStringLiteral( "labeling/shapeBorderWidthMapUnitMinScale" ), 0.0 ).toDouble();
708  d->strokeWidthMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
709  double oldMax = layer->customProperty( QStringLiteral( "labeling/shapeBorderWidthMapUnitMaxScale" ), 0.0 ).toDouble();
710  d->strokeWidthMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
711  }
712  else
713  {
714  d->strokeWidthMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( layer->customProperty( QStringLiteral( "labeling/shapeBorderWidthMapUnitScale" ) ).toString() );
715  }
716  d->joinStyle = static_cast< Qt::PenJoinStyle >( layer->customProperty( QStringLiteral( "labeling/shapeJoinStyle" ), QVariant( Qt::BevelJoin ) ).toUInt() );
717 
718  if ( layer->customProperty( QStringLiteral( "labeling/shapeOpacity" ) ).toString().isEmpty() )
719  {
720  d->opacity = ( 1 - layer->customProperty( QStringLiteral( "labeling/shapeTransparency" ) ).toInt() / 100.0 ); //0 -100
721  }
722  else
723  {
724  d->opacity = ( layer->customProperty( QStringLiteral( "labeling/shapeOpacity" ) ).toDouble() );
725  }
726  d->blendMode = QgsPainting::getCompositionMode(
727  static_cast< QgsPainting::BlendMode >( layer->customProperty( QStringLiteral( "labeling/shapeBlendMode" ), QVariant( QgsPainting::BlendNormal ) ).toUInt() ) );
728 
729  if ( layer->customProperty( QStringLiteral( "labeling/shapeEffect" ) ).isValid() )
730  {
731  QDomDocument doc( QStringLiteral( "effect" ) );
732  doc.setContent( layer->customProperty( QStringLiteral( "labeling/shapeEffect" ) ).toString() );
733  QDomElement effectElem = doc.firstChildElement( QStringLiteral( "effect" ) ).firstChildElement( QStringLiteral( "effect" ) );
734  setPaintEffect( QgsApplication::paintEffectRegistry()->createEffect( effectElem ) );
735  }
736  else
737  setPaintEffect( nullptr );
738 }
739 
740 void QgsTextBackgroundSettings::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
741 {
742  QDomElement backgroundElem = elem.firstChildElement( QStringLiteral( "background" ) );
743  d->enabled = backgroundElem.attribute( QStringLiteral( "shapeDraw" ), QStringLiteral( "0" ) ).toInt();
744  d->type = static_cast< ShapeType >( backgroundElem.attribute( QStringLiteral( "shapeType" ), QString::number( ShapeRectangle ) ).toUInt() );
745  d->svgFile = QgsSymbolLayerUtils::svgSymbolNameToPath( backgroundElem.attribute( QStringLiteral( "shapeSVGFile" ) ), context.pathResolver() );
746  d->sizeType = static_cast< SizeType >( backgroundElem.attribute( QStringLiteral( "shapeSizeType" ), QString::number( SizeBuffer ) ).toUInt() );
747  d->size = QSizeF( backgroundElem.attribute( QStringLiteral( "shapeSizeX" ), QStringLiteral( "0" ) ).toDouble(),
748  backgroundElem.attribute( QStringLiteral( "shapeSizeY" ), QStringLiteral( "0" ) ).toDouble() );
749 
750  if ( !backgroundElem.hasAttribute( QStringLiteral( "shapeSizeUnit" ) ) )
751  {
752  d->sizeUnits = convertFromOldLabelUnit( backgroundElem.attribute( QStringLiteral( "shapeSizeUnits" ) ).toUInt() );
753  }
754  else
755  {
756  d->sizeUnits = QgsUnitTypes::decodeRenderUnit( backgroundElem.attribute( QStringLiteral( "shapeSizeUnit" ) ) );
757  }
758 
759  if ( !backgroundElem.hasAttribute( QStringLiteral( "shapeSizeMapUnitScale" ) ) )
760  {
761  //fallback to older property
762  double oldMin = backgroundElem.attribute( QStringLiteral( "shapeSizeMapUnitMinScale" ), QStringLiteral( "0" ) ).toDouble();
763  d->sizeMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
764  double oldMax = backgroundElem.attribute( QStringLiteral( "shapeSizeMapUnitMaxScale" ), QStringLiteral( "0" ) ).toDouble();
765  d->sizeMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
766  }
767  else
768  {
769  d->sizeMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( backgroundElem.attribute( QStringLiteral( "shapeSizeMapUnitScale" ) ) );
770  }
771  d->rotationType = static_cast< RotationType >( backgroundElem.attribute( QStringLiteral( "shapeRotationType" ), QString::number( RotationSync ) ).toUInt() );
772  d->rotation = backgroundElem.attribute( QStringLiteral( "shapeRotation" ), QStringLiteral( "0" ) ).toDouble();
773  d->offset = QPointF( backgroundElem.attribute( QStringLiteral( "shapeOffsetX" ), QStringLiteral( "0" ) ).toDouble(),
774  backgroundElem.attribute( QStringLiteral( "shapeOffsetY" ), QStringLiteral( "0" ) ).toDouble() );
775 
776  if ( !backgroundElem.hasAttribute( QStringLiteral( "shapeOffsetUnit" ) ) )
777  {
778  d->offsetUnits = convertFromOldLabelUnit( backgroundElem.attribute( QStringLiteral( "shapeOffsetUnits" ) ).toUInt() );
779  }
780  else
781  {
782  d->offsetUnits = QgsUnitTypes::decodeRenderUnit( backgroundElem.attribute( QStringLiteral( "shapeOffsetUnit" ) ) );
783  }
784 
785  if ( !backgroundElem.hasAttribute( QStringLiteral( "shapeOffsetMapUnitScale" ) ) )
786  {
787  //fallback to older property
788  double oldMin = backgroundElem.attribute( QStringLiteral( "shapeOffsetMapUnitMinScale" ), QStringLiteral( "0" ) ).toDouble();
789  d->offsetMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
790  double oldMax = backgroundElem.attribute( QStringLiteral( "shapeOffsetMapUnitMaxScale" ), QStringLiteral( "0" ) ).toDouble();
791  d->offsetMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
792  }
793  else
794  {
795  d->offsetMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( backgroundElem.attribute( QStringLiteral( "shapeOffsetMapUnitScale" ) ) );
796  }
797  d->radii = QSizeF( backgroundElem.attribute( QStringLiteral( "shapeRadiiX" ), QStringLiteral( "0" ) ).toDouble(),
798  backgroundElem.attribute( QStringLiteral( "shapeRadiiY" ), QStringLiteral( "0" ) ).toDouble() );
799 
800  if ( !backgroundElem.hasAttribute( QStringLiteral( "shapeRadiiUnit" ) ) )
801  {
802  d->radiiUnits = convertFromOldLabelUnit( backgroundElem.attribute( QStringLiteral( "shapeRadiiUnits" ) ).toUInt() );
803  }
804  else
805  {
806  d->radiiUnits = QgsUnitTypes::decodeRenderUnit( backgroundElem.attribute( QStringLiteral( "shapeRadiiUnit" ) ) );
807  }
808  if ( !backgroundElem.hasAttribute( QStringLiteral( "shapeRadiiMapUnitScale" ) ) )
809  {
810  //fallback to older property
811  double oldMin = backgroundElem.attribute( QStringLiteral( "shapeRadiiMapUnitMinScale" ), QStringLiteral( "0" ) ).toDouble();
812  d->radiiMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
813  double oldMax = backgroundElem.attribute( QStringLiteral( "shapeRadiiMapUnitMaxScale" ), QStringLiteral( "0" ) ).toDouble();
814  d->radiiMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
815  }
816  else
817  {
818  d->radiiMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( backgroundElem.attribute( QStringLiteral( "shapeRadiiMapUnitScale" ) ) );
819  }
820  d->fillColor = QgsSymbolLayerUtils::decodeColor( backgroundElem.attribute( QStringLiteral( "shapeFillColor" ), QgsSymbolLayerUtils::encodeColor( Qt::white ) ) );
821  d->strokeColor = QgsSymbolLayerUtils::decodeColor( backgroundElem.attribute( QStringLiteral( "shapeBorderColor" ), QgsSymbolLayerUtils::encodeColor( Qt::darkGray ) ) );
822  d->strokeWidth = backgroundElem.attribute( QStringLiteral( "shapeBorderWidth" ), QStringLiteral( "0" ) ).toDouble();
823 
824  if ( !backgroundElem.hasAttribute( QStringLiteral( "shapeBorderWidthUnit" ) ) )
825  {
826  d->strokeWidthUnits = convertFromOldLabelUnit( backgroundElem.attribute( QStringLiteral( "shapeBorderWidthUnits" ) ).toUInt() );
827  }
828  else
829  {
830  d->strokeWidthUnits = QgsUnitTypes::decodeRenderUnit( backgroundElem.attribute( QStringLiteral( "shapeBorderWidthUnit" ) ) );
831  }
832  if ( !backgroundElem.hasAttribute( QStringLiteral( "shapeBorderWidthMapUnitScale" ) ) )
833  {
834  //fallback to older property
835  double oldMin = backgroundElem.attribute( QStringLiteral( "shapeBorderWidthMapUnitMinScale" ), QStringLiteral( "0" ) ).toDouble();
836  d->strokeWidthMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
837  double oldMax = backgroundElem.attribute( QStringLiteral( "shapeBorderWidthMapUnitMaxScale" ), QStringLiteral( "0" ) ).toDouble();
838  d->strokeWidthMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
839  }
840  else
841  {
842  d->strokeWidthMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( backgroundElem.attribute( QStringLiteral( "shapeBorderWidthMapUnitScale" ) ) );
843  }
844  d->joinStyle = static_cast< Qt::PenJoinStyle >( backgroundElem.attribute( QStringLiteral( "shapeJoinStyle" ), QString::number( Qt::BevelJoin ) ).toUInt() );
845 
846  if ( !backgroundElem.hasAttribute( QStringLiteral( "shapeOpacity" ) ) )
847  {
848  d->opacity = ( 1 - backgroundElem.attribute( QStringLiteral( "shapeTransparency" ) ).toInt() / 100.0 ); //0 -100
849  }
850  else
851  {
852  d->opacity = ( backgroundElem.attribute( QStringLiteral( "shapeOpacity" ) ).toDouble() );
853  }
854 
855  d->blendMode = QgsPainting::getCompositionMode(
856  static_cast< QgsPainting::BlendMode >( backgroundElem.attribute( QStringLiteral( "shapeBlendMode" ), QString::number( QgsPainting::BlendNormal ) ).toUInt() ) );
857 
858  QDomElement effectElem = backgroundElem.firstChildElement( QStringLiteral( "effect" ) );
859  if ( !effectElem.isNull() )
861  else
862  setPaintEffect( nullptr );
863 }
864 
865 QDomElement QgsTextBackgroundSettings::writeXml( QDomDocument &doc, const QgsReadWriteContext &context ) const
866 {
867  QDomElement backgroundElem = doc.createElement( QStringLiteral( "background" ) );
868  backgroundElem.setAttribute( QStringLiteral( "shapeDraw" ), d->enabled );
869  backgroundElem.setAttribute( QStringLiteral( "shapeType" ), static_cast< unsigned int >( d->type ) );
870  backgroundElem.setAttribute( QStringLiteral( "shapeSVGFile" ), QgsSymbolLayerUtils::svgSymbolPathToName( d->svgFile, context.pathResolver() ) );
871  backgroundElem.setAttribute( QStringLiteral( "shapeSizeType" ), static_cast< unsigned int >( d->sizeType ) );
872  backgroundElem.setAttribute( QStringLiteral( "shapeSizeX" ), d->size.width() );
873  backgroundElem.setAttribute( QStringLiteral( "shapeSizeY" ), d->size.height() );
874  backgroundElem.setAttribute( QStringLiteral( "shapeSizeUnit" ), QgsUnitTypes::encodeUnit( d->sizeUnits ) );
875  backgroundElem.setAttribute( QStringLiteral( "shapeSizeMapUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( d->sizeMapUnitScale ) );
876  backgroundElem.setAttribute( QStringLiteral( "shapeRotationType" ), static_cast< unsigned int >( d->rotationType ) );
877  backgroundElem.setAttribute( QStringLiteral( "shapeRotation" ), d->rotation );
878  backgroundElem.setAttribute( QStringLiteral( "shapeOffsetX" ), d->offset.x() );
879  backgroundElem.setAttribute( QStringLiteral( "shapeOffsetY" ), d->offset.y() );
880  backgroundElem.setAttribute( QStringLiteral( "shapeOffsetUnit" ), QgsUnitTypes::encodeUnit( d->offsetUnits ) );
881  backgroundElem.setAttribute( QStringLiteral( "shapeOffsetMapUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( d->offsetMapUnitScale ) );
882  backgroundElem.setAttribute( QStringLiteral( "shapeRadiiX" ), d->radii.width() );
883  backgroundElem.setAttribute( QStringLiteral( "shapeRadiiY" ), d->radii.height() );
884  backgroundElem.setAttribute( QStringLiteral( "shapeRadiiUnit" ), QgsUnitTypes::encodeUnit( d->radiiUnits ) );
885  backgroundElem.setAttribute( QStringLiteral( "shapeRadiiMapUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( d->radiiMapUnitScale ) );
886  backgroundElem.setAttribute( QStringLiteral( "shapeFillColor" ), QgsSymbolLayerUtils::encodeColor( d->fillColor ) );
887  backgroundElem.setAttribute( QStringLiteral( "shapeBorderColor" ), QgsSymbolLayerUtils::encodeColor( d->strokeColor ) );
888  backgroundElem.setAttribute( QStringLiteral( "shapeBorderWidth" ), d->strokeWidth );
889  backgroundElem.setAttribute( QStringLiteral( "shapeBorderWidthUnit" ), QgsUnitTypes::encodeUnit( d->strokeWidthUnits ) );
890  backgroundElem.setAttribute( QStringLiteral( "shapeBorderWidthMapUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( d->strokeWidthMapUnitScale ) );
891  backgroundElem.setAttribute( QStringLiteral( "shapeJoinStyle" ), static_cast< unsigned int >( d->joinStyle ) );
892  backgroundElem.setAttribute( QStringLiteral( "shapeOpacity" ), d->opacity );
893  backgroundElem.setAttribute( QStringLiteral( "shapeBlendMode" ), QgsPainting::getBlendModeEnum( d->blendMode ) );
894  if ( d->paintEffect && !QgsPaintEffectRegistry::isDefaultStack( d->paintEffect ) )
895  d->paintEffect->saveProperties( doc, backgroundElem );
896  return backgroundElem;
897 }
898 
899 
900 //
901 // QgsTextShadowSettings
902 //
903 
905 {
906  d = new QgsTextShadowSettingsPrivate();
907 }
908 
910  : d( other.d )
911 {
912 
913 }
914 
916 {
917  d = other.d;
918  return *this;
919 }
920 
922 {
923 
924 }
925 
927 {
928  return d->enabled;
929 }
930 
932 {
933  d->enabled = enabled;
934 }
935 
937 {
938  return d->shadowUnder;
939 }
940 
942 {
943  d->shadowUnder = placement;
944 }
945 
947 {
948  return d->offsetAngle;
949 }
950 
952 {
953  d->offsetAngle = angle;
954 }
955 
957 {
958  return d->offsetDist;
959 }
960 
962 {
963  d->offsetDist = distance;
964 }
965 
967 {
968  return d->offsetUnits;
969 }
970 
972 {
973  d->offsetUnits = units;
974 }
975 
977 {
978  return d->offsetMapUnitScale;
979 }
980 
982 {
983  d->offsetMapUnitScale = scale;
984 }
985 
987 {
988  return d->offsetGlobal;
989 }
990 
992 {
993  d->offsetGlobal = global;
994 }
995 
997 {
998  return d->radius;
999 }
1000 
1002 {
1003  d->radius = radius;
1004 }
1005 
1007 {
1008  return d->radiusUnits;
1009 }
1010 
1012 {
1013  d->radiusUnits = units;
1014 }
1015 
1017 {
1018  return d->radiusMapUnitScale;
1019 }
1020 
1022 {
1023  d->radiusMapUnitScale = scale;
1024 }
1025 
1027 {
1028  return d->radiusAlphaOnly;
1029 }
1030 
1032 {
1033  d->radiusAlphaOnly = alphaOnly;
1034 }
1035 
1037 {
1038  return d->opacity;
1039 }
1040 
1042 {
1043  d->opacity = opacity;
1044 }
1045 
1047 {
1048  return d->scale;
1049 }
1050 
1052 {
1053  d->scale = scale;
1054 }
1055 
1057 {
1058  return d->color;
1059 }
1060 
1062 {
1063  d->color = color;
1064 }
1065 
1066 QPainter::CompositionMode QgsTextShadowSettings::blendMode() const
1067 {
1068  return d->blendMode;
1069 }
1070 
1071 void QgsTextShadowSettings::setBlendMode( QPainter::CompositionMode mode )
1072 {
1073  d->blendMode = mode;
1074 }
1075 
1077 {
1078  d->enabled = layer->customProperty( QStringLiteral( "labeling/shadowDraw" ), QVariant( false ) ).toBool();
1079  d->shadowUnder = static_cast< ShadowPlacement >( layer->customProperty( QStringLiteral( "labeling/shadowUnder" ), QVariant( ShadowLowest ) ).toUInt() );//ShadowLowest;
1080  d->offsetAngle = layer->customProperty( QStringLiteral( "labeling/shadowOffsetAngle" ), QVariant( 135 ) ).toInt();
1081  d->offsetDist = layer->customProperty( QStringLiteral( "labeling/shadowOffsetDist" ), QVariant( 1.0 ) ).toDouble();
1082 
1083  if ( layer->customProperty( QStringLiteral( "labeling/shadowOffsetUnit" ) ).toString().isEmpty() )
1084  {
1085  d->offsetUnits = convertFromOldLabelUnit( layer->customProperty( QStringLiteral( "labeling/shadowOffsetUnits" ), 0 ).toUInt() );
1086  }
1087  else
1088  {
1089  d->offsetUnits = QgsUnitTypes::decodeRenderUnit( layer->customProperty( QStringLiteral( "labeling/shadowOffsetUnit" ) ).toString() );
1090  }
1091  if ( layer->customProperty( QStringLiteral( "labeling/shadowOffsetMapUnitScale" ) ).toString().isEmpty() )
1092  {
1093  //fallback to older property
1094  double oldMin = layer->customProperty( QStringLiteral( "labeling/shadowOffsetMapUnitMinScale" ), 0.0 ).toDouble();
1095  d->offsetMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
1096  double oldMax = layer->customProperty( QStringLiteral( "labeling/shadowOffsetMapUnitMaxScale" ), 0.0 ).toDouble();
1097  d->offsetMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
1098  }
1099  else
1100  {
1101  d->offsetMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( layer->customProperty( QStringLiteral( "labeling/shadowOffsetMapUnitScale" ) ).toString() );
1102  }
1103  d->offsetGlobal = layer->customProperty( QStringLiteral( "labeling/shadowOffsetGlobal" ), QVariant( true ) ).toBool();
1104  d->radius = layer->customProperty( QStringLiteral( "labeling/shadowRadius" ), QVariant( 1.5 ) ).toDouble();
1105 
1106  if ( layer->customProperty( QStringLiteral( "labeling/shadowRadiusUnit" ) ).toString().isEmpty() )
1107  {
1108  d->radiusUnits = convertFromOldLabelUnit( layer->customProperty( QStringLiteral( "labeling/shadowRadiusUnits" ), 0 ).toUInt() );
1109  }
1110  else
1111  {
1112  d->radiusUnits = QgsUnitTypes::decodeRenderUnit( layer->customProperty( QStringLiteral( "labeling/shadowRadiusUnit" ) ).toString() );
1113  }
1114  if ( layer->customProperty( QStringLiteral( "labeling/shadowRadiusMapUnitScale" ) ).toString().isEmpty() )
1115  {
1116  //fallback to older property
1117  double oldMin = layer->customProperty( QStringLiteral( "labeling/shadowRadiusMapUnitMinScale" ), 0.0 ).toDouble();
1118  d->radiusMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
1119  double oldMax = layer->customProperty( QStringLiteral( "labeling/shadowRadiusMapUnitMaxScale" ), 0.0 ).toDouble();
1120  d->radiusMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
1121  }
1122  else
1123  {
1124  d->radiusMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( layer->customProperty( QStringLiteral( "labeling/shadowRadiusMapUnitScale" ) ).toString() );
1125  }
1126  d->radiusAlphaOnly = layer->customProperty( QStringLiteral( "labeling/shadowRadiusAlphaOnly" ), QVariant( false ) ).toBool();
1127 
1128  if ( layer->customProperty( QStringLiteral( "labeling/shadowOpacity" ) ).toString().isEmpty() )
1129  {
1130  d->opacity = ( 1 - layer->customProperty( QStringLiteral( "labeling/shadowTransparency" ) ).toInt() / 100.0 ); //0 -100
1131  }
1132  else
1133  {
1134  d->opacity = ( layer->customProperty( QStringLiteral( "labeling/shadowOpacity" ) ).toDouble() );
1135  }
1136  d->scale = layer->customProperty( QStringLiteral( "labeling/shadowScale" ), QVariant( 100 ) ).toInt();
1137  d->color = _readColor( layer, QStringLiteral( "labeling/shadowColor" ), Qt::black, false );
1138  d->blendMode = QgsPainting::getCompositionMode(
1139  static_cast< QgsPainting::BlendMode >( layer->customProperty( QStringLiteral( "labeling/shadowBlendMode" ), QVariant( QgsPainting::BlendMultiply ) ).toUInt() ) );
1140 }
1141 
1142 void QgsTextShadowSettings::readXml( const QDomElement &elem )
1143 {
1144  QDomElement shadowElem = elem.firstChildElement( QStringLiteral( "shadow" ) );
1145  d->enabled = shadowElem.attribute( QStringLiteral( "shadowDraw" ), QStringLiteral( "0" ) ).toInt();
1146  d->shadowUnder = static_cast< ShadowPlacement >( shadowElem.attribute( QStringLiteral( "shadowUnder" ), QString::number( ShadowLowest ) ).toUInt() );//ShadowLowest;
1147  d->offsetAngle = shadowElem.attribute( QStringLiteral( "shadowOffsetAngle" ), QStringLiteral( "135" ) ).toInt();
1148  d->offsetDist = shadowElem.attribute( QStringLiteral( "shadowOffsetDist" ), QStringLiteral( "1" ) ).toDouble();
1149 
1150  if ( !shadowElem.hasAttribute( QStringLiteral( "shadowOffsetUnit" ) ) )
1151  {
1152  d->offsetUnits = convertFromOldLabelUnit( shadowElem.attribute( QStringLiteral( "shadowOffsetUnits" ) ).toUInt() );
1153  }
1154  else
1155  {
1156  d->offsetUnits = QgsUnitTypes::decodeRenderUnit( shadowElem.attribute( QStringLiteral( "shadowOffsetUnit" ) ) );
1157  }
1158 
1159  if ( !shadowElem.hasAttribute( QStringLiteral( "shadowOffsetMapUnitScale" ) ) )
1160  {
1161  //fallback to older property
1162  double oldMin = shadowElem.attribute( QStringLiteral( "shadowOffsetMapUnitMinScale" ), QStringLiteral( "0" ) ).toDouble();
1163  d->offsetMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
1164  double oldMax = shadowElem.attribute( QStringLiteral( "shadowOffsetMapUnitMaxScale" ), QStringLiteral( "0" ) ).toDouble();
1165  d->offsetMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
1166  }
1167  else
1168  {
1169  d->offsetMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( shadowElem.attribute( QStringLiteral( "shadowOffsetMapUnitScale" ) ) );
1170  }
1171  d->offsetGlobal = shadowElem.attribute( QStringLiteral( "shadowOffsetGlobal" ), QStringLiteral( "1" ) ).toInt();
1172  d->radius = shadowElem.attribute( QStringLiteral( "shadowRadius" ), QStringLiteral( "1.5" ) ).toDouble();
1173 
1174  if ( !shadowElem.hasAttribute( QStringLiteral( "shadowRadiusUnit" ) ) )
1175  {
1176  d->radiusUnits = convertFromOldLabelUnit( shadowElem.attribute( QStringLiteral( "shadowRadiusUnits" ) ).toUInt() );
1177  }
1178  else
1179  {
1180  d->radiusUnits = QgsUnitTypes::decodeRenderUnit( shadowElem.attribute( QStringLiteral( "shadowRadiusUnit" ) ) );
1181  }
1182  if ( !shadowElem.hasAttribute( QStringLiteral( "shadowRadiusMapUnitScale" ) ) )
1183  {
1184  //fallback to older property
1185  double oldMin = shadowElem.attribute( QStringLiteral( "shadowRadiusMapUnitMinScale" ), QStringLiteral( "0" ) ).toDouble();
1186  d->radiusMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
1187  double oldMax = shadowElem.attribute( QStringLiteral( "shadowRadiusMapUnitMaxScale" ), QStringLiteral( "0" ) ).toDouble();
1188  d->radiusMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
1189  }
1190  else
1191  {
1192  d->radiusMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( shadowElem.attribute( QStringLiteral( "shadowRadiusMapUnitScale" ) ) );
1193  }
1194  d->radiusAlphaOnly = shadowElem.attribute( QStringLiteral( "shadowRadiusAlphaOnly" ), QStringLiteral( "0" ) ).toInt();
1195 
1196  if ( !shadowElem.hasAttribute( QStringLiteral( "shadowOpacity" ) ) )
1197  {
1198  d->opacity = ( 1 - shadowElem.attribute( QStringLiteral( "shadowTransparency" ) ).toInt() / 100.0 ); //0 -100
1199  }
1200  else
1201  {
1202  d->opacity = ( shadowElem.attribute( QStringLiteral( "shadowOpacity" ) ).toDouble() );
1203  }
1204  d->scale = shadowElem.attribute( QStringLiteral( "shadowScale" ), QStringLiteral( "100" ) ).toInt();
1205  d->color = QgsSymbolLayerUtils::decodeColor( shadowElem.attribute( QStringLiteral( "shadowColor" ), QgsSymbolLayerUtils::encodeColor( Qt::black ) ) );
1206  d->blendMode = QgsPainting::getCompositionMode(
1207  static_cast< QgsPainting::BlendMode >( shadowElem.attribute( QStringLiteral( "shadowBlendMode" ), QString::number( QgsPainting::BlendMultiply ) ).toUInt() ) );
1208 }
1209 
1210 QDomElement QgsTextShadowSettings::writeXml( QDomDocument &doc ) const
1211 {
1212  QDomElement shadowElem = doc.createElement( QStringLiteral( "shadow" ) );
1213  shadowElem.setAttribute( QStringLiteral( "shadowDraw" ), d->enabled );
1214  shadowElem.setAttribute( QStringLiteral( "shadowUnder" ), static_cast< unsigned int >( d->shadowUnder ) );
1215  shadowElem.setAttribute( QStringLiteral( "shadowOffsetAngle" ), d->offsetAngle );
1216  shadowElem.setAttribute( QStringLiteral( "shadowOffsetDist" ), d->offsetDist );
1217  shadowElem.setAttribute( QStringLiteral( "shadowOffsetUnit" ), QgsUnitTypes::encodeUnit( d->offsetUnits ) );
1218  shadowElem.setAttribute( QStringLiteral( "shadowOffsetMapUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( d->offsetMapUnitScale ) );
1219  shadowElem.setAttribute( QStringLiteral( "shadowOffsetGlobal" ), d->offsetGlobal );
1220  shadowElem.setAttribute( QStringLiteral( "shadowRadius" ), d->radius );
1221  shadowElem.setAttribute( QStringLiteral( "shadowRadiusUnit" ), QgsUnitTypes::encodeUnit( d->radiusUnits ) );
1222  shadowElem.setAttribute( QStringLiteral( "shadowRadiusMapUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( d->radiusMapUnitScale ) );
1223  shadowElem.setAttribute( QStringLiteral( "shadowRadiusAlphaOnly" ), d->radiusAlphaOnly );
1224  shadowElem.setAttribute( QStringLiteral( "shadowOpacity" ), d->opacity );
1225  shadowElem.setAttribute( QStringLiteral( "shadowScale" ), d->scale );
1226  shadowElem.setAttribute( QStringLiteral( "shadowColor" ), QgsSymbolLayerUtils::encodeColor( d->color ) );
1227  shadowElem.setAttribute( QStringLiteral( "shadowBlendMode" ), QgsPainting::getBlendModeEnum( d->blendMode ) );
1228  return shadowElem;
1229 }
1230 
1231 //
1232 // QgsTextFormat
1233 //
1234 
1236 {
1237  d = new QgsTextSettingsPrivate();
1238 }
1239 
1241  : mBufferSettings( other.mBufferSettings )
1242  , mBackgroundSettings( other.mBackgroundSettings )
1243  , mShadowSettings( other.mShadowSettings )
1244  , mTextFontFamily( other.mTextFontFamily )
1245  , mTextFontFound( other.mTextFontFound )
1246  , d( other.d )
1247 {
1248 
1249 }
1250 
1252 {
1253  d = other.d;
1254  mBufferSettings = other.mBufferSettings;
1255  mBackgroundSettings = other.mBackgroundSettings;
1256  mShadowSettings = other.mShadowSettings;
1257  mTextFontFamily = other.mTextFontFamily;
1258  mTextFontFound = other.mTextFontFound;
1259  return *this;
1260 }
1261 
1263 {
1264 
1265 }
1266 
1267 QFont QgsTextFormat::font() const
1268 {
1269  return d->textFont;
1270 }
1271 
1272 QFont QgsTextFormat::scaledFont( const QgsRenderContext &context ) const
1273 {
1274  QFont font = d->textFont;
1275  int fontPixelSize = QgsTextRenderer::sizeToPixel( d->fontSize, context, d->fontSizeUnits,
1276  d->fontSizeMapUnitScale );
1277  font.setPixelSize( fontPixelSize );
1278  return font;
1279 }
1280 
1281 void QgsTextFormat::setFont( const QFont &font )
1282 {
1283  d->textFont = font;
1284 }
1285 
1287 {
1288  if ( !d->textNamedStyle.isEmpty() )
1289  return d->textNamedStyle;
1290 
1291  QFontDatabase db;
1292  return db.styleString( d->textFont );
1293 }
1294 
1295 void QgsTextFormat::setNamedStyle( const QString &style )
1296 {
1297  QgsFontUtils::updateFontViaStyle( d->textFont, style );
1298  d->textNamedStyle = style;
1299 }
1300 
1302 {
1303  return d->fontSizeUnits;
1304 }
1305 
1307 {
1308  d->fontSizeUnits = unit;
1309 }
1310 
1312 {
1313  return d->fontSizeMapUnitScale;
1314 }
1315 
1317 {
1318  d->fontSizeMapUnitScale = scale;
1319 }
1320 
1321 double QgsTextFormat::size() const
1322 {
1323  return d->fontSize;
1324 }
1325 
1327 {
1328  d->fontSize = size;
1329 }
1330 
1331 QColor QgsTextFormat::color() const
1332 {
1333  return d->textColor;
1334 }
1335 
1336 void QgsTextFormat::setColor( const QColor &color )
1337 {
1338  d->textColor = color;
1339 }
1340 
1342 {
1343  return d->opacity;
1344 }
1345 
1347 {
1348  d->opacity = opacity;
1349 }
1350 
1351 QPainter::CompositionMode QgsTextFormat::blendMode() const
1352 {
1353  return d->blendMode;
1354 }
1355 
1356 void QgsTextFormat::setBlendMode( QPainter::CompositionMode mode )
1357 {
1358  d->blendMode = mode;
1359 }
1360 
1362 {
1363  return d->multilineHeight;
1364 }
1365 
1366 void QgsTextFormat::setLineHeight( double height )
1367 {
1368  d->multilineHeight = height;
1369 }
1370 
1372 {
1373  QFont appFont = QApplication::font();
1374  mTextFontFamily = layer->customProperty( QStringLiteral( "labeling/fontFamily" ), QVariant( appFont.family() ) ).toString();
1375  QString fontFamily = mTextFontFamily;
1376  if ( mTextFontFamily != appFont.family() && !QgsFontUtils::fontFamilyMatchOnSystem( mTextFontFamily ) )
1377  {
1378  // trigger to notify about font family substitution
1379  mTextFontFound = false;
1380 
1381  // TODO: update when pref for how to resolve missing family (use matching algorithm or just default font) is implemented
1382  // currently only defaults to matching algorithm for resolving [foundry], if a font of similar family is found (default for QFont)
1383 
1384  // for now, do not use matching algorithm for substitution if family not found, substitute default instead
1385  fontFamily = appFont.family();
1386  }
1387  else
1388  {
1389  mTextFontFound = true;
1390  }
1391 
1392  if ( !layer->customProperty( QStringLiteral( "labeling/fontSize" ) ).isValid() )
1393  {
1394  d->fontSize = appFont.pointSizeF();
1395  }
1396  else
1397  {
1398  d->fontSize = layer->customProperty( QStringLiteral( "labeling/fontSize" ) ).toDouble();
1399  }
1400 
1401  if ( layer->customProperty( QStringLiteral( "labeling/fontSizeUnit" ) ).toString().isEmpty() )
1402  {
1403  d->fontSizeUnits = layer->customProperty( QStringLiteral( "labeling/fontSizeInMapUnits" ), QVariant( false ) ).toBool() ?
1405  }
1406  else
1407  {
1408  bool ok = false;
1409  d->fontSizeUnits = QgsUnitTypes::decodeRenderUnit( layer->customProperty( QStringLiteral( "labeling/fontSizeUnit" ) ).toString(), &ok );
1410  if ( !ok )
1411  d->fontSizeUnits = QgsUnitTypes::RenderPoints;
1412  }
1413  if ( layer->customProperty( QStringLiteral( "labeling/fontSizeMapUnitScale" ) ).toString().isEmpty() )
1414  {
1415  //fallback to older property
1416  double oldMin = layer->customProperty( QStringLiteral( "labeling/fontSizeMapUnitMinScale" ), 0.0 ).toDouble();
1417  d->fontSizeMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
1418  double oldMax = layer->customProperty( QStringLiteral( "labeling/fontSizeMapUnitMaxScale" ), 0.0 ).toDouble();
1419  d->fontSizeMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
1420  }
1421  else
1422  {
1423  d->fontSizeMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( layer->customProperty( QStringLiteral( "labeling/fontSizeMapUnitScale" ) ).toString() );
1424  }
1425  int fontWeight = layer->customProperty( QStringLiteral( "labeling/fontWeight" ) ).toInt();
1426  bool fontItalic = layer->customProperty( QStringLiteral( "labeling/fontItalic" ) ).toBool();
1427  d->textFont = QFont( fontFamily, d->fontSize, fontWeight, fontItalic );
1428  d->textNamedStyle = QgsFontUtils::translateNamedStyle( layer->customProperty( QStringLiteral( "labeling/namedStyle" ), QVariant( "" ) ).toString() );
1429  QgsFontUtils::updateFontViaStyle( d->textFont, d->textNamedStyle ); // must come after textFont.setPointSizeF()
1430  d->textFont.setCapitalization( static_cast< QFont::Capitalization >( layer->customProperty( QStringLiteral( "labeling/fontCapitals" ), QVariant( 0 ) ).toUInt() ) );
1431  d->textFont.setUnderline( layer->customProperty( QStringLiteral( "labeling/fontUnderline" ) ).toBool() );
1432  d->textFont.setStrikeOut( layer->customProperty( QStringLiteral( "labeling/fontStrikeout" ) ).toBool() );
1433  d->textFont.setLetterSpacing( QFont::AbsoluteSpacing, layer->customProperty( QStringLiteral( "labeling/fontLetterSpacing" ), QVariant( 0.0 ) ).toDouble() );
1434  d->textFont.setWordSpacing( layer->customProperty( QStringLiteral( "labeling/fontWordSpacing" ), QVariant( 0.0 ) ).toDouble() );
1435  d->textColor = _readColor( layer, QStringLiteral( "labeling/textColor" ), Qt::black, false );
1436  if ( layer->customProperty( QStringLiteral( "labeling/textOpacity" ) ).toString().isEmpty() )
1437  {
1438  d->opacity = ( 1 - layer->customProperty( QStringLiteral( "labeling/textTransp" ) ).toInt() / 100.0 ); //0 -100
1439  }
1440  else
1441  {
1442  d->opacity = ( layer->customProperty( QStringLiteral( "labeling/textOpacity" ) ).toDouble() );
1443  }
1444  d->blendMode = QgsPainting::getCompositionMode(
1445  static_cast< QgsPainting::BlendMode >( layer->customProperty( QStringLiteral( "labeling/blendMode" ), QVariant( QgsPainting::BlendNormal ) ).toUInt() ) );
1446  d->multilineHeight = layer->customProperty( QStringLiteral( "labeling/multilineHeight" ), QVariant( 1.0 ) ).toDouble();
1447 
1448  mBufferSettings.readFromLayer( layer );
1449  mShadowSettings.readFromLayer( layer );
1450  mBackgroundSettings.readFromLayer( layer );
1451 }
1452 
1453 void QgsTextFormat::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
1454 {
1455  QDomElement textStyleElem;
1456  if ( elem.nodeName() == QStringLiteral( "text-style" ) )
1457  textStyleElem = elem;
1458  else
1459  textStyleElem = elem.firstChildElement( QStringLiteral( "text-style" ) );
1460  QFont appFont = QApplication::font();
1461  mTextFontFamily = textStyleElem.attribute( QStringLiteral( "fontFamily" ), appFont.family() );
1462  QString fontFamily = mTextFontFamily;
1463  if ( mTextFontFamily != appFont.family() && !QgsFontUtils::fontFamilyMatchOnSystem( mTextFontFamily ) )
1464  {
1465  // trigger to notify user about font family substitution (signal emitted in QgsVectorLayer::prepareLabelingAndDiagrams)
1466  mTextFontFound = false;
1467 
1468  // TODO: update when pref for how to resolve missing family (use matching algorithm or just default font) is implemented
1469  // currently only defaults to matching algorithm for resolving [foundry], if a font of similar family is found (default for QFont)
1470 
1471  // for now, do not use matching algorithm for substitution if family not found, substitute default instead
1472  fontFamily = appFont.family();
1473  }
1474  else
1475  {
1476  mTextFontFound = true;
1477  }
1478 
1479  if ( textStyleElem.hasAttribute( QStringLiteral( "fontSize" ) ) )
1480  {
1481  d->fontSize = textStyleElem.attribute( QStringLiteral( "fontSize" ) ).toDouble();
1482  }
1483  else
1484  {
1485  d->fontSize = appFont.pointSizeF();
1486  }
1487 
1488  if ( !textStyleElem.hasAttribute( QStringLiteral( "fontSizeUnit" ) ) )
1489  {
1490  d->fontSizeUnits = textStyleElem.attribute( QStringLiteral( "fontSizeInMapUnits" ) ).toUInt() == 0 ? QgsUnitTypes::RenderPoints
1492  }
1493  else
1494  {
1495  d->fontSizeUnits = QgsUnitTypes::decodeRenderUnit( textStyleElem.attribute( QStringLiteral( "fontSizeUnit" ) ) );
1496  }
1497 
1498  if ( !textStyleElem.hasAttribute( QStringLiteral( "fontSizeMapUnitScale" ) ) )
1499  {
1500  //fallback to older property
1501  double oldMin = textStyleElem.attribute( QStringLiteral( "fontSizeMapUnitMinScale" ), QStringLiteral( "0" ) ).toDouble();
1502  d->fontSizeMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
1503  double oldMax = textStyleElem.attribute( QStringLiteral( "fontSizeMapUnitMaxScale" ), QStringLiteral( "0" ) ).toDouble();
1504  d->fontSizeMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
1505  }
1506  else
1507  {
1508  d->fontSizeMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( textStyleElem.attribute( QStringLiteral( "fontSizeMapUnitScale" ) ) );
1509  }
1510  int fontWeight = textStyleElem.attribute( QStringLiteral( "fontWeight" ) ).toInt();
1511  bool fontItalic = textStyleElem.attribute( QStringLiteral( "fontItalic" ) ).toInt();
1512  d->textFont = QFont( fontFamily, d->fontSize, fontWeight, fontItalic );
1513  d->textFont.setPointSizeF( d->fontSize ); //double precision needed because of map units
1514  d->textNamedStyle = QgsFontUtils::translateNamedStyle( textStyleElem.attribute( QStringLiteral( "namedStyle" ) ) );
1515  QgsFontUtils::updateFontViaStyle( d->textFont, d->textNamedStyle ); // must come after textFont.setPointSizeF()
1516  d->textFont.setCapitalization( static_cast< QFont::Capitalization >( textStyleElem.attribute( QStringLiteral( "fontCapitals" ), QStringLiteral( "0" ) ).toUInt() ) );
1517  d->textFont.setUnderline( textStyleElem.attribute( QStringLiteral( "fontUnderline" ) ).toInt() );
1518  d->textFont.setStrikeOut( textStyleElem.attribute( QStringLiteral( "fontStrikeout" ) ).toInt() );
1519  d->textFont.setLetterSpacing( QFont::AbsoluteSpacing, textStyleElem.attribute( QStringLiteral( "fontLetterSpacing" ), QStringLiteral( "0" ) ).toDouble() );
1520  d->textFont.setWordSpacing( textStyleElem.attribute( QStringLiteral( "fontWordSpacing" ), QStringLiteral( "0" ) ).toDouble() );
1521  d->textColor = QgsSymbolLayerUtils::decodeColor( textStyleElem.attribute( QStringLiteral( "textColor" ), QgsSymbolLayerUtils::encodeColor( Qt::black ) ) );
1522  if ( !textStyleElem.hasAttribute( QStringLiteral( "textOpacity" ) ) )
1523  {
1524  d->opacity = ( 1 - textStyleElem.attribute( QStringLiteral( "textTransp" ) ).toInt() / 100.0 ); //0 -100
1525  }
1526  else
1527  {
1528  d->opacity = ( textStyleElem.attribute( QStringLiteral( "textOpacity" ) ).toDouble() );
1529  }
1530  d->blendMode = QgsPainting::getCompositionMode(
1531  static_cast< QgsPainting::BlendMode >( textStyleElem.attribute( QStringLiteral( "blendMode" ), QString::number( QgsPainting::BlendNormal ) ).toUInt() ) );
1532 
1533  if ( !textStyleElem.hasAttribute( QStringLiteral( "multilineHeight" ) ) )
1534  {
1535  QDomElement textFormatElem = elem.firstChildElement( QStringLiteral( "text-format" ) );
1536  d->multilineHeight = textFormatElem.attribute( QStringLiteral( "multilineHeight" ), QStringLiteral( "1" ) ).toDouble();
1537  }
1538  else
1539  {
1540  d->multilineHeight = textStyleElem.attribute( QStringLiteral( "multilineHeight" ), QStringLiteral( "1" ) ).toDouble();
1541  }
1542 
1543  if ( textStyleElem.firstChildElement( QStringLiteral( "text-buffer" ) ).isNull() )
1544  {
1545  mBufferSettings.readXml( elem );
1546  }
1547  else
1548  {
1549  mBufferSettings.readXml( textStyleElem );
1550  }
1551  if ( textStyleElem.firstChildElement( QStringLiteral( "shadow" ) ).isNull() )
1552  {
1553  mShadowSettings.readXml( elem );
1554  }
1555  else
1556  {
1557  mShadowSettings.readXml( textStyleElem );
1558  }
1559  if ( textStyleElem.firstChildElement( QStringLiteral( "background" ) ).isNull() )
1560  {
1561  mBackgroundSettings.readXml( elem, context );
1562  }
1563  else
1564  {
1565  mBackgroundSettings.readXml( textStyleElem, context );
1566  }
1567 }
1568 
1569 QDomElement QgsTextFormat::writeXml( QDomDocument &doc, const QgsReadWriteContext &context ) const
1570 {
1571  // text style
1572  QDomElement textStyleElem = doc.createElement( QStringLiteral( "text-style" ) );
1573  textStyleElem.setAttribute( QStringLiteral( "fontFamily" ), d->textFont.family() );
1574  textStyleElem.setAttribute( QStringLiteral( "namedStyle" ), QgsFontUtils::untranslateNamedStyle( d->textNamedStyle ) );
1575  textStyleElem.setAttribute( QStringLiteral( "fontSize" ), d->fontSize );
1576  textStyleElem.setAttribute( QStringLiteral( "fontSizeUnit" ), QgsUnitTypes::encodeUnit( d->fontSizeUnits ) );
1577  textStyleElem.setAttribute( QStringLiteral( "fontSizeMapUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( d->fontSizeMapUnitScale ) );
1578  textStyleElem.setAttribute( QStringLiteral( "fontWeight" ), d->textFont.weight() );
1579  textStyleElem.setAttribute( QStringLiteral( "fontItalic" ), d->textFont.italic() );
1580  textStyleElem.setAttribute( QStringLiteral( "fontStrikeout" ), d->textFont.strikeOut() );
1581  textStyleElem.setAttribute( QStringLiteral( "fontUnderline" ), d->textFont.underline() );
1582  textStyleElem.setAttribute( QStringLiteral( "textColor" ), QgsSymbolLayerUtils::encodeColor( d->textColor ) );
1583  textStyleElem.setAttribute( QStringLiteral( "fontCapitals" ), static_cast< unsigned int >( d->textFont.capitalization() ) );
1584  textStyleElem.setAttribute( QStringLiteral( "fontLetterSpacing" ), d->textFont.letterSpacing() );
1585  textStyleElem.setAttribute( QStringLiteral( "fontWordSpacing" ), d->textFont.wordSpacing() );
1586  textStyleElem.setAttribute( QStringLiteral( "textOpacity" ), d->opacity );
1587  textStyleElem.setAttribute( QStringLiteral( "blendMode" ), QgsPainting::getBlendModeEnum( d->blendMode ) );
1588  textStyleElem.setAttribute( QStringLiteral( "multilineHeight" ), d->multilineHeight );
1589 
1590  textStyleElem.appendChild( mBufferSettings.writeXml( doc ) );
1591  textStyleElem.appendChild( mBackgroundSettings.writeXml( doc, context ) );
1592  textStyleElem.appendChild( mShadowSettings.writeXml( doc ) );
1593  return textStyleElem;
1594 }
1595 
1596 QMimeData *QgsTextFormat::toMimeData() const
1597 {
1598  //set both the mime color data, and the text (format settings).
1599  QMimeData *mimeData = new QMimeData;
1600  mimeData->setColorData( QVariant( color() ) );
1601 
1602  QgsReadWriteContext rwContext;
1603  QDomDocument textDoc;
1604  QDomElement textElem = writeXml( textDoc, rwContext );
1605  textDoc.appendChild( textElem );
1606  mimeData->setText( textDoc.toString() );
1607 
1608  return mimeData;
1609 }
1610 
1611 QgsTextFormat QgsTextFormat::fromMimeData( const QMimeData *data, bool *ok )
1612 {
1613  if ( ok )
1614  *ok = false;
1615  QgsTextFormat format;
1616  if ( !data )
1617  return format;
1618 
1619  QString text = data->text();
1620  if ( !text.isEmpty() )
1621  {
1622  QDomDocument doc;
1623  QDomElement elem;
1624  QgsReadWriteContext rwContext;
1625 
1626  if ( doc.setContent( text ) )
1627  {
1628  elem = doc.documentElement();
1629 
1630  format.readXml( elem, rwContext );
1631  if ( ok )
1632  *ok = true;
1633  return format;
1634  }
1635  }
1636  return format;
1637 }
1638 
1640 {
1641  if ( d->blendMode != QPainter::CompositionMode_SourceOver )
1642  return true;
1643 
1644  if ( mBufferSettings.enabled() && mBufferSettings.blendMode() != QPainter::CompositionMode_SourceOver )
1645  return true;
1646 
1647  if ( mBackgroundSettings.enabled() && mBackgroundSettings.blendMode() != QPainter::CompositionMode_SourceOver )
1648  return true;
1649 
1650  if ( mShadowSettings.enabled() && mShadowSettings.blendMode() != QPainter::CompositionMode_SourceOver )
1651  return true;
1652 
1653  return false;
1654 }
1655 
1656 
1658 {
1659  return static_cast< int >( c.convertToPainterUnits( size, unit, mapUnitScale ) + 0.5 ); //NOLINT
1660 }
1661 
1662 void QgsTextRenderer::drawText( const QRectF &rect, double rotation, QgsTextRenderer::HAlignment alignment, const QStringList &textLines, QgsRenderContext &context, const QgsTextFormat &format, bool drawAsOutlines )
1663 {
1664  QgsTextFormat tmpFormat = updateShadowPosition( format );
1665 
1666  if ( tmpFormat.background().enabled() )
1667  {
1668  drawPart( rect, rotation, alignment, textLines, context, tmpFormat, Background, drawAsOutlines );
1669  }
1670 
1671  if ( tmpFormat.buffer().enabled() )
1672  {
1673  drawPart( rect, rotation, alignment, textLines, context, tmpFormat, Buffer, drawAsOutlines );
1674  }
1675 
1676  drawPart( rect, rotation, alignment, textLines, context, tmpFormat, Text, drawAsOutlines );
1677 }
1678 
1679 void QgsTextRenderer::drawText( QPointF point, double rotation, QgsTextRenderer::HAlignment alignment, const QStringList &textLines, QgsRenderContext &context, const QgsTextFormat &format, bool drawAsOutlines )
1680 {
1681  QgsTextFormat tmpFormat = updateShadowPosition( format );
1682 
1683  if ( tmpFormat.background().enabled() )
1684  {
1685  drawPart( point, rotation, alignment, textLines, context, tmpFormat, Background, drawAsOutlines );
1686  }
1687 
1688  if ( tmpFormat.buffer().enabled() )
1689  {
1690  drawPart( point, rotation, alignment, textLines, context, tmpFormat, Buffer, drawAsOutlines );
1691  }
1692 
1693  drawPart( point, rotation, alignment, textLines, context, tmpFormat, Text, drawAsOutlines );
1694 }
1695 
1696 QgsTextFormat QgsTextRenderer::updateShadowPosition( const QgsTextFormat &format )
1697 {
1698  if ( !format.shadow().enabled() || format.shadow().shadowPlacement() != QgsTextShadowSettings::ShadowLowest )
1699  return format;
1700 
1701  QgsTextFormat tmpFormat = format;
1702  if ( tmpFormat.background().enabled() )
1703  {
1705  }
1706  else if ( tmpFormat.buffer().enabled() )
1707  {
1709  }
1710  else
1711  {
1713  }
1714  return tmpFormat;
1715 }
1716 
1717 void QgsTextRenderer::drawPart( const QRectF &rect, double rotation, HAlignment alignment,
1718  const QStringList &textLines, QgsRenderContext &context, const QgsTextFormat &format, QgsTextRenderer::TextPart part, bool drawAsOutlines )
1719 {
1720  if ( !context.painter() )
1721  {
1722  return;
1723  }
1724 
1725  Component component;
1726  component.dpiRatio = 1.0;
1727  component.origin = rect.topLeft();
1728  component.rotation = rotation;
1729  component.size = rect.size();
1730  component.hAlign = alignment;
1731 
1732  switch ( part )
1733  {
1734  case Background:
1735  {
1736  if ( !format.background().enabled() )
1737  return;
1738 
1739  if ( !qgsDoubleNear( rotation, 0.0 ) )
1740  {
1741  // get rotated label's center point
1742 
1743  double xc = rect.width() / 2.0;
1744  double yc = rect.height() / 2.0;
1745 
1746  double angle = -rotation;
1747  double xd = xc * std::cos( angle ) - yc * std::sin( angle );
1748  double yd = xc * std::sin( angle ) + yc * std::cos( angle );
1749 
1750  component.center = QPointF( component.origin.x() + xd, component.origin.y() + yd );
1751  }
1752  else
1753  {
1754  component.center = rect.center();
1755  }
1756 
1757  QgsTextRenderer::drawBackground( context, component, format, textLines, Rect );
1758 
1759  break;
1760  }
1761 
1762  case Buffer:
1763  {
1764  if ( !format.buffer().enabled() )
1765  break;
1766  }
1767  FALLTHROUGH;
1768  case Text:
1769  case Shadow:
1770  {
1771  QFontMetricsF fm( format.scaledFont( context ) );
1772  drawTextInternal( part, context, format, component,
1773  textLines,
1774  &fm,
1775  alignment,
1776  drawAsOutlines );
1777  break;
1778  }
1779  }
1780 }
1781 
1782 void QgsTextRenderer::drawPart( QPointF origin, double rotation, QgsTextRenderer::HAlignment alignment, const QStringList &textLines, QgsRenderContext &context, const QgsTextFormat &format, QgsTextRenderer::TextPart part, bool drawAsOutlines )
1783 {
1784  if ( !context.painter() )
1785  {
1786  return;
1787  }
1788 
1789  Component component;
1790  component.dpiRatio = 1.0;
1791  component.origin = origin;
1792  component.rotation = rotation;
1793  component.hAlign = alignment;
1794 
1795  switch ( part )
1796  {
1797  case Background:
1798  {
1799  if ( !format.background().enabled() )
1800  return;
1801 
1802  QgsTextRenderer::drawBackground( context, component, format, textLines, Point );
1803  break;
1804  }
1805 
1806  case Buffer:
1807  {
1808  if ( !format.buffer().enabled() )
1809  break;
1810  }
1811  FALLTHROUGH;
1812  case Text:
1813  case Shadow:
1814  {
1815  QFontMetricsF fm( format.scaledFont( context ) );
1816  drawTextInternal( part, context, format, component,
1817  textLines,
1818  &fm,
1819  alignment,
1820  drawAsOutlines,
1821  Point );
1822  break;
1823  }
1824  }
1825 }
1826 
1827 void QgsTextRenderer::drawBuffer( QgsRenderContext &context, const QgsTextRenderer::Component &component, const QgsTextFormat &format )
1828 {
1829  QPainter *p = context.painter();
1830 
1831  QgsTextBufferSettings buffer = format.buffer();
1832 
1833  double penSize = context.convertToPainterUnits( buffer.size(), buffer.sizeUnit(), buffer.sizeMapUnitScale() );
1834 
1835  QPainterPath path;
1836  path.setFillRule( Qt::WindingFill );
1837  path.addText( 0, 0, format.scaledFont( context ), component.text );
1838  QColor bufferColor = buffer.color();
1839  bufferColor.setAlphaF( buffer.opacity() );
1840  QPen pen( bufferColor );
1841  pen.setWidthF( penSize );
1842  pen.setJoinStyle( buffer.joinStyle() );
1843  QColor tmpColor( bufferColor );
1844  // honor pref for whether to fill buffer interior
1845  if ( !buffer.fillBufferInterior() )
1846  {
1847  tmpColor.setAlpha( 0 );
1848  }
1849 
1850  // store buffer's drawing in QPicture for drop shadow call
1851  QPicture buffPict;
1852  QPainter buffp;
1853  buffp.begin( &buffPict );
1854 
1855  if ( buffer.paintEffect() && buffer.paintEffect()->enabled() )
1856  {
1857  context.setPainter( &buffp );
1858 
1859  buffer.paintEffect()->begin( context );
1860  context.painter()->setPen( pen );
1861  context.painter()->setBrush( tmpColor );
1862  context.painter()->drawPath( path );
1863  buffer.paintEffect()->end( context );
1864 
1865  context.setPainter( p );
1866  }
1867  else
1868  {
1869  buffp.setPen( pen );
1870  buffp.setBrush( tmpColor );
1871  buffp.drawPath( path );
1872  }
1873  buffp.end();
1874 
1875  if ( format.shadow().enabled() && format.shadow().shadowPlacement() == QgsTextShadowSettings::ShadowBuffer )
1876  {
1877  QgsTextRenderer::Component bufferComponent = component;
1878  bufferComponent.origin = QPointF( 0.0, 0.0 );
1879  bufferComponent.picture = buffPict;
1880  bufferComponent.pictureBuffer = penSize / 2.0;
1881  drawShadow( context, bufferComponent, format );
1882  }
1883  p->save();
1884  if ( context.useAdvancedEffects() )
1885  {
1886  p->setCompositionMode( buffer.blendMode() );
1887  }
1888  if ( context.flags() & QgsRenderContext::Antialiasing )
1889  {
1890  p->setRenderHint( QPainter::Antialiasing );
1891  }
1892 
1893  // scale for any print output or image saving @ specific dpi
1894  p->scale( component.dpiRatio, component.dpiRatio );
1895  _fixQPictureDPI( p );
1896  p->drawPicture( 0, 0, buffPict );
1897  p->restore();
1898 }
1899 
1900 double QgsTextRenderer::textWidth( const QgsRenderContext &context, const QgsTextFormat &format, const QStringList &textLines, QFontMetricsF *fm )
1901 {
1902  //calculate max width of text lines
1903  std::unique_ptr< QFontMetricsF > newFm;
1904  if ( !fm )
1905  {
1906  newFm.reset( new QFontMetricsF( format.scaledFont( context ) ) );
1907  fm = newFm.get();
1908  }
1909 
1910  double maxWidth = 0;
1911  Q_FOREACH ( const QString &line, textLines )
1912  {
1913  maxWidth = std::max( maxWidth, fm->width( line ) );
1914  }
1915  return maxWidth;
1916 }
1917 
1918 double QgsTextRenderer::textHeight( const QgsRenderContext &context, const QgsTextFormat &format, const QStringList &textLines, DrawMode mode, QFontMetricsF *fm )
1919 {
1920  //calculate max width of text lines
1921  std::unique_ptr< QFontMetricsF > newFm;
1922  if ( !fm )
1923  {
1924  newFm.reset( new QFontMetricsF( format.scaledFont( context ) ) );
1925  fm = newFm.get();
1926  }
1927 
1928  double labelHeight = fm->ascent() + fm->descent(); // ignore +1 for baseline
1929 
1930  switch ( mode )
1931  {
1932  case Label:
1933  // rendering labels needs special handling - in this case text should be
1934  // drawn with the bottom left corner coinciding with origin, vs top left
1935  // for standard text rendering. Line height is also slightly different.
1936  return labelHeight + ( textLines.size() - 1 ) * labelHeight * format.lineHeight();
1937 
1938  case Rect:
1939  case Point:
1940  // standard rendering - designed to exactly replicate QPainter's drawText method
1941  return labelHeight + ( textLines.size() - 1 ) * fm->lineSpacing() * format.lineHeight();
1942  }
1943 
1944  return 0;
1945 }
1946 
1947 void QgsTextRenderer::drawBackground( QgsRenderContext &context, QgsTextRenderer::Component component, const QgsTextFormat &format,
1948  const QStringList &textLines, DrawMode mode )
1949 {
1951 
1952  QPainter *prevP = context.painter();
1953  QPainter *p = context.painter();
1954  if ( background.paintEffect() && background.paintEffect()->enabled() )
1955  {
1956  background.paintEffect()->begin( context );
1957  p = context.painter();
1958  }
1959 
1960  //QgsDebugMsgLevel( QString( "Background label rotation: %1" ).arg( component.rotation() ), 4 );
1961 
1962  // shared calculations between shapes and SVG
1963 
1964  // configure angles, set component rotation and rotationOffset
1966  {
1967  component.rotation = -( component.rotation * 180 / M_PI ); // RotationSync
1968  component.rotationOffset =
1969  background.rotationType() == QgsTextBackgroundSettings::RotationOffset ? background.rotation() : 0.0;
1970  }
1971  else // RotationFixed
1972  {
1973  component.rotation = 0.0; // don't use label's rotation
1974  component.rotationOffset = background.rotation();
1975  }
1976 
1977  if ( mode != Label )
1978  {
1979  // need to calculate size of text
1980  QFontMetricsF fm( format.scaledFont( context ) );
1981  double width = textWidth( context, format, textLines, &fm );
1982  double height = textHeight( context, format, textLines, mode, &fm );
1983 
1984  switch ( mode )
1985  {
1986  case Rect:
1987  switch ( component.hAlign )
1988  {
1989  case AlignLeft:
1990  component.center = QPointF( component.origin.x() + width / 2.0,
1991  component.origin.y() + height / 2.0 );
1992  break;
1993 
1994  case AlignCenter:
1995  component.center = QPointF( component.origin.x() + component.size.width() / 2.0,
1996  component.origin.y() + height / 2.0 );
1997  break;
1998 
1999  case AlignRight:
2000  component.center = QPointF( component.origin.x() + component.size.width() - width / 2.0,
2001  component.origin.y() + height / 2.0 );
2002  break;
2003  }
2004  break;
2005 
2006  case Point:
2007  {
2008  double originAdjust = fm.ascent() / 2.0 - fm.leading() / 2.0;
2009  switch ( component.hAlign )
2010  {
2011  case AlignLeft:
2012  component.center = QPointF( component.origin.x() + width / 2.0,
2013  component.origin.y() - height / 2.0 + originAdjust );
2014  break;
2015 
2016  case AlignCenter:
2017  component.center = QPointF( component.origin.x(),
2018  component.origin.y() - height / 2.0 + originAdjust );
2019  break;
2020 
2021  case AlignRight:
2022  component.center = QPointF( component.origin.x() - width / 2.0,
2023  component.origin.y() - height / 2.0 + originAdjust );
2024  break;
2025  }
2026  }
2027 
2028  case Label:
2029  break;
2030  }
2031 
2033  component.size = QSizeF( width, height );
2034  }
2035 
2036  // TODO: the following label-buffered generated shapes and SVG symbols should be moved into marker symbology classes
2037 
2038  if ( background.type() == QgsTextBackgroundSettings::ShapeSVG )
2039  {
2040  // all calculations done in shapeSizeUnits, which are then passed to symbology class for painting
2041 
2042  if ( background.svgFile().isEmpty() )
2043  return;
2044 
2045  double sizeOut = 0.0;
2046  // only one size used for SVG sizing/scaling (no use of shapeSize.y() or Y field in gui)
2047  if ( background.sizeType() == QgsTextBackgroundSettings::SizeFixed )
2048  {
2049  sizeOut = context.convertToPainterUnits( background.size().width(), background.sizeUnit(), background.sizeMapUnitScale() );
2050  }
2051  else if ( background.sizeType() == QgsTextBackgroundSettings::SizeBuffer )
2052  {
2053  sizeOut = std::max( component.size.width(), component.size.height() );
2054  double bufferSize = context.convertToPainterUnits( background.size().width(), background.sizeUnit(), background.sizeMapUnitScale() );
2055 
2056  // add buffer
2057  sizeOut += bufferSize * 2;
2058  }
2059 
2060  // don't bother rendering symbols smaller than 1x1 pixels in size
2061  // TODO: add option to not show any svgs under/over a certain size
2062  if ( sizeOut < 1.0 )
2063  return;
2064 
2065  QgsStringMap map; // for SVG symbology marker
2066  map[QStringLiteral( "name" )] = background.svgFile().trimmed();
2067  map[QStringLiteral( "size" )] = QString::number( sizeOut );
2068  map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( QgsUnitTypes::RenderPixels );
2069  map[QStringLiteral( "angle" )] = QString::number( 0.0 ); // angle is handled by this local painter
2070 
2071  // offset is handled by this local painter
2072  // TODO: see why the marker renderer doesn't seem to translate offset *after* applying rotation
2073  //map["offset"] = QgsSymbolLayerUtils::encodePoint( tmpLyr.shapeOffset );
2074  //map["offset_unit"] = QgsUnitTypes::encodeUnit(
2075  // tmpLyr.shapeOffsetUnits == QgsPalLayerSettings::MapUnits ? QgsUnitTypes::MapUnit : QgsUnitTypes::MM );
2076 
2077  map[QStringLiteral( "fill" )] = background.fillColor().name();
2078  map[QStringLiteral( "outline" )] = background.strokeColor().name();
2079  map[QStringLiteral( "outline-width" )] = QString::number( background.strokeWidth() );
2080  map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( background.strokeWidthUnit() );
2081 
2082  if ( format.shadow().enabled() && format.shadow().shadowPlacement() == QgsTextShadowSettings::ShadowShape )
2083  {
2084  QgsTextShadowSettings shadow = format.shadow();
2085  // configure SVG shadow specs
2086  QgsStringMap shdwmap( map );
2087  shdwmap[QStringLiteral( "fill" )] = shadow.color().name();
2088  shdwmap[QStringLiteral( "outline" )] = shadow.color().name();
2089  shdwmap[QStringLiteral( "size" )] = QString::number( sizeOut );
2090 
2091  // store SVG's drawing in QPicture for drop shadow call
2092  QPicture svgPict;
2093  QPainter svgp;
2094  svgp.begin( &svgPict );
2095 
2096  // draw shadow symbol
2097 
2098  // clone current render context map unit/mm conversion factors, but not
2099  // other map canvas parameters, then substitute this painter for use in symbology painting
2100  // NOTE: this is because the shadow needs to be scaled correctly for output to map canvas,
2101  // but will be created relative to the SVG's computed size, not the current map canvas
2102  QgsRenderContext shdwContext;
2103  shdwContext.setMapToPixel( context.mapToPixel() );
2104  shdwContext.setScaleFactor( context.scaleFactor() );
2105  shdwContext.setPainter( &svgp );
2106 
2107  QgsSymbolLayer *symShdwL = QgsSvgMarkerSymbolLayer::create( shdwmap );
2108  QgsSvgMarkerSymbolLayer *svgShdwM = static_cast<QgsSvgMarkerSymbolLayer *>( symShdwL );
2109  QgsSymbolRenderContext svgShdwContext( shdwContext, QgsUnitTypes::RenderUnknownUnit, background.opacity() );
2110 
2111  svgShdwM->renderPoint( QPointF( sizeOut / 2, -sizeOut / 2 ), svgShdwContext );
2112  svgp.end();
2113 
2114  component.picture = svgPict;
2115  // TODO: when SVG symbol's stroke width/units is fixed in QgsSvgCache, adjust for it here
2116  component.pictureBuffer = 0.0;
2117 
2118  component.size = QSizeF( sizeOut, sizeOut );
2119  component.offset = QPointF( 0.0, 0.0 );
2120 
2121  // rotate about origin center of SVG
2122  p->save();
2123  p->translate( component.center.x(), component.center.y() );
2124  p->rotate( component.rotation );
2125  double xoff = context.convertToPainterUnits( background.offset().x(), background.offsetUnit(), background.offsetMapUnitScale() );
2126  double yoff = context.convertToPainterUnits( background.offset().y(), background.offsetUnit(), background.offsetMapUnitScale() );
2127  p->translate( QPointF( xoff, yoff ) );
2128  p->rotate( component.rotationOffset );
2129  p->translate( -sizeOut / 2, sizeOut / 2 );
2130  if ( context.flags() & QgsRenderContext::Antialiasing )
2131  {
2132  p->setRenderHint( QPainter::Antialiasing );
2133  }
2134 
2135  drawShadow( context, component, format );
2136  p->restore();
2137 
2138  delete svgShdwM;
2139  svgShdwM = nullptr;
2140  }
2141 
2142  // draw the actual symbol
2144  QgsSvgMarkerSymbolLayer *svgM = static_cast<QgsSvgMarkerSymbolLayer *>( symL );
2145  QgsSymbolRenderContext svgContext( context, QgsUnitTypes::RenderUnknownUnit, background.opacity() );
2146 
2147  p->save();
2148  if ( context.useAdvancedEffects() )
2149  {
2150  p->setCompositionMode( background.blendMode() );
2151  }
2152  if ( context.flags() & QgsRenderContext::Antialiasing )
2153  {
2154  p->setRenderHint( QPainter::Antialiasing );
2155  }
2156  p->translate( component.center.x(), component.center.y() );
2157  p->rotate( component.rotation );
2158  double xoff = context.convertToPainterUnits( background.offset().x(), background.offsetUnit(), background.offsetMapUnitScale() );
2159  double yoff = context.convertToPainterUnits( background.offset().y(), background.offsetUnit(), background.offsetMapUnitScale() );
2160  p->translate( QPointF( xoff, yoff ) );
2161  p->rotate( component.rotationOffset );
2162  svgM->renderPoint( QPointF( 0, 0 ), svgContext );
2163  p->setCompositionMode( QPainter::CompositionMode_SourceOver ); // just to be sure
2164  p->restore();
2165 
2166  delete svgM;
2167  svgM = nullptr;
2168 
2169  }
2170  else // Generated Shapes
2171  {
2172  double w = component.size.width();
2173  double h = component.size.height();
2174 
2175  if ( background.sizeType() == QgsTextBackgroundSettings::SizeFixed )
2176  {
2177  w = context.convertToPainterUnits( background.size().width(), background.sizeUnit(),
2178  background.sizeMapUnitScale() );
2179  h = context.convertToPainterUnits( background.size().height(), background.sizeUnit(),
2180  background.sizeMapUnitScale() );
2181  }
2182  else if ( background.sizeType() == QgsTextBackgroundSettings::SizeBuffer )
2183  {
2184  if ( background.type() == QgsTextBackgroundSettings::ShapeSquare )
2185  {
2186  if ( w > h )
2187  h = w;
2188  else if ( h > w )
2189  w = h;
2190  }
2191  else if ( background.type() == QgsTextBackgroundSettings::ShapeCircle )
2192  {
2193  // start with label bound by circle
2194  h = std::sqrt( std::pow( w, 2 ) + std::pow( h, 2 ) );
2195  w = h;
2196  }
2197  else if ( background.type() == QgsTextBackgroundSettings::ShapeEllipse )
2198  {
2199  // start with label bound by ellipse
2200  h = h * M_SQRT1_2 * 2;
2201  w = w * M_SQRT1_2 * 2;
2202  }
2203 
2204  double bufferWidth = context.convertToPainterUnits( background.size().width(), background.sizeUnit(),
2205  background.sizeMapUnitScale() );
2206  double bufferHeight = context.convertToPainterUnits( background.size().height(), background.sizeUnit(),
2207  background.sizeMapUnitScale() );
2208 
2209  w += bufferWidth * 2;
2210  h += bufferHeight * 2;
2211  }
2212 
2213  // offsets match those of symbology: -x = left, -y = up
2214  QRectF rect( -w / 2.0, - h / 2.0, w, h );
2215 
2216  if ( rect.isNull() )
2217  return;
2218 
2219  p->save();
2220  if ( context.flags() & QgsRenderContext::Antialiasing )
2221  {
2222  p->setRenderHint( QPainter::Antialiasing );
2223  }
2224  p->translate( QPointF( component.center.x(), component.center.y() ) );
2225  p->rotate( component.rotation );
2226  double xoff = context.convertToPainterUnits( background.offset().x(), background.offsetUnit(), background.offsetMapUnitScale() );
2227  double yoff = context.convertToPainterUnits( background.offset().y(), background.offsetUnit(), background.offsetMapUnitScale() );
2228  p->translate( QPointF( xoff, yoff ) );
2229  p->rotate( component.rotationOffset );
2230 
2231  double penSize = context.convertToPainterUnits( background.strokeWidth(), background.strokeWidthUnit(), background.strokeWidthMapUnitScale() );
2232 
2233  QPen pen;
2234  if ( background.strokeWidth() > 0 )
2235  {
2236  pen.setColor( background.strokeColor() );
2237  pen.setWidthF( penSize );
2238  if ( background.type() == QgsTextBackgroundSettings::ShapeRectangle )
2239  pen.setJoinStyle( background.joinStyle() );
2240  }
2241  else
2242  {
2243  pen = Qt::NoPen;
2244  }
2245 
2246  // store painting in QPicture for shadow drawing
2247  QPicture shapePict;
2248  QPainter shapep;
2249  shapep.begin( &shapePict );
2250  shapep.setPen( pen );
2251  shapep.setBrush( background.fillColor() );
2252 
2253  if ( background.type() == QgsTextBackgroundSettings::ShapeRectangle
2254  || background.type() == QgsTextBackgroundSettings::ShapeSquare )
2255  {
2256  if ( background.radiiUnit() == QgsUnitTypes::RenderPercentage )
2257  {
2258  shapep.drawRoundedRect( rect, background.radii().width(), background.radii().height(), Qt::RelativeSize );
2259  }
2260  else
2261  {
2262  double xRadius = context.convertToPainterUnits( background.radii().width(), background.radiiUnit(), background.radiiMapUnitScale() );
2263  double yRadius = context.convertToPainterUnits( background.radii().height(), background.radiiUnit(), background.radiiMapUnitScale() );
2264  shapep.drawRoundedRect( rect, xRadius, yRadius );
2265  }
2266  }
2267  else if ( background.type() == QgsTextBackgroundSettings::ShapeEllipse
2268  || background.type() == QgsTextBackgroundSettings::ShapeCircle )
2269  {
2270  shapep.drawEllipse( rect );
2271  }
2272  shapep.end();
2273 
2274  if ( format.shadow().enabled() && format.shadow().shadowPlacement() == QgsTextShadowSettings::ShadowShape )
2275  {
2276  component.picture = shapePict;
2277  component.pictureBuffer = penSize / 2.0;
2278 
2279  component.size = rect.size();
2280  component.offset = QPointF( rect.width() / 2, -rect.height() / 2 );
2281  drawShadow( context, component, format );
2282  }
2283 
2284  p->setOpacity( background.opacity() );
2285  if ( context.useAdvancedEffects() )
2286  {
2287  p->setCompositionMode( background.blendMode() );
2288  }
2289 
2290  // scale for any print output or image saving @ specific dpi
2291  p->scale( component.dpiRatio, component.dpiRatio );
2292  _fixQPictureDPI( p );
2293  p->drawPicture( 0, 0, shapePict );
2294  p->restore();
2295  }
2296  if ( background.paintEffect() && background.paintEffect()->enabled() )
2297  {
2298  background.paintEffect()->end( context );
2299  context.setPainter( prevP );
2300  }
2301 }
2302 
2303 void QgsTextRenderer::drawShadow( QgsRenderContext &context, const QgsTextRenderer::Component &component, const QgsTextFormat &format )
2304 {
2305  QgsTextShadowSettings shadow = format.shadow();
2306 
2307  // incoming component sizes should be multiplied by rasterCompressFactor, as
2308  // this allows shadows to be created at paint device dpi (e.g. high resolution),
2309  // then scale device painter by 1.0 / rasterCompressFactor for output
2310 
2311  QPainter *p = context.painter();
2312  double componentWidth = component.size.width(), componentHeight = component.size.height();
2313  double xOffset = component.offset.x(), yOffset = component.offset.y();
2314  double pictbuffer = component.pictureBuffer;
2315 
2316  // generate pixmap representation of label component drawing
2317  bool mapUnits = shadow.blurRadiusUnit() == QgsUnitTypes::RenderMapUnits;
2318  double radius = context.convertToPainterUnits( shadow.blurRadius(), shadow.blurRadiusUnit(), shadow.blurRadiusMapUnitScale() );
2319  radius /= ( mapUnits ? context.scaleFactor() / component.dpiRatio : 1 );
2320  radius = static_cast< int >( radius + 0.5 ); //NOLINT
2321 
2322  // TODO: add labeling gui option to adjust blurBufferClippingScale to minimize pixels, or
2323  // to ensure shadow isn't clipped too tight. (Or, find a better method of buffering)
2324  double blurBufferClippingScale = 3.75;
2325  int blurbuffer = ( radius > 17 ? 16 : radius ) * blurBufferClippingScale;
2326 
2327  QImage blurImg( componentWidth + ( pictbuffer * 2.0 ) + ( blurbuffer * 2.0 ),
2328  componentHeight + ( pictbuffer * 2.0 ) + ( blurbuffer * 2.0 ),
2329  QImage::Format_ARGB32_Premultiplied );
2330 
2331  // TODO: add labeling gui option to not show any shadows under/over a certain size
2332  // keep very small QImages from causing paint device issues, i.e. must be at least > 1
2333  int minBlurImgSize = 1;
2334  // max limitation on QgsSvgCache is 10,000 for screen, which will probably be reasonable for future caching here, too
2335  // 4 x QgsSvgCache limit for output to print/image at higher dpi
2336  // TODO: should it be higher, scale with dpi, or have no limit? Needs testing with very large labels rendered at high dpi output
2337  int maxBlurImgSize = 40000;
2338  if ( blurImg.isNull()
2339  || ( blurImg.width() < minBlurImgSize || blurImg.height() < minBlurImgSize )
2340  || ( blurImg.width() > maxBlurImgSize || blurImg.height() > maxBlurImgSize ) )
2341  return;
2342 
2343  blurImg.fill( QColor( Qt::transparent ).rgba() );
2344  QPainter pictp;
2345  if ( !pictp.begin( &blurImg ) )
2346  return;
2347  pictp.setRenderHints( QPainter::Antialiasing | QPainter::SmoothPixmapTransform );
2348  QPointF imgOffset( blurbuffer + pictbuffer + xOffset,
2349  blurbuffer + pictbuffer + componentHeight + yOffset );
2350 
2351  pictp.drawPicture( imgOffset,
2352  component.picture );
2353 
2354  // overlay shadow color
2355  pictp.setCompositionMode( QPainter::CompositionMode_SourceIn );
2356  pictp.fillRect( blurImg.rect(), shadow.color() );
2357  pictp.end();
2358 
2359  // blur the QImage in-place
2360  if ( shadow.blurRadius() > 0.0 && radius > 0 )
2361  {
2362  QgsSymbolLayerUtils::blurImageInPlace( blurImg, blurImg.rect(), radius, shadow.blurAlphaOnly() );
2363  }
2364 
2365 #if 0
2366  // debug rect for QImage shadow registration and clipping visualization
2367  QPainter picti;
2368  picti.begin( &blurImg );
2369  picti.setBrush( Qt::Dense7Pattern );
2370  QPen imgPen( QColor( 0, 0, 255, 255 ) );
2371  imgPen.setWidth( 1 );
2372  picti.setPen( imgPen );
2373  picti.setOpacity( 0.1 );
2374  picti.drawRect( 0, 0, blurImg.width(), blurImg.height() );
2375  picti.end();
2376 #endif
2377 
2378  double offsetDist = context.convertToPainterUnits( shadow.offsetDistance(), shadow.offsetUnit(), shadow.offsetMapUnitScale() );
2379  double angleRad = shadow.offsetAngle() * M_PI / 180; // to radians
2380  if ( shadow.offsetGlobal() )
2381  {
2382  // TODO: check for differences in rotation origin and cw/ccw direction,
2383  // when this shadow function is used for something other than labels
2384 
2385  // it's 0-->cw-->360 for labels
2386  //QgsDebugMsgLevel( QString( "Shadow aggregated label rotation (degrees): %1" ).arg( component.rotation() + component.rotationOffset() ), 4 );
2387  angleRad -= ( component.rotation * M_PI / 180 + component.rotationOffset * M_PI / 180 );
2388  }
2389 
2390  QPointF transPt( -offsetDist * std::cos( angleRad + M_PI_2 ),
2391  -offsetDist * std::sin( angleRad + M_PI_2 ) );
2392 
2393  p->save();
2394  p->setRenderHint( QPainter::SmoothPixmapTransform );
2395  if ( context.flags() & QgsRenderContext::Antialiasing )
2396  {
2397  p->setRenderHint( QPainter::Antialiasing );
2398  }
2399  if ( context.useAdvancedEffects() )
2400  {
2401  p->setCompositionMode( shadow.blendMode() );
2402  }
2403  p->setOpacity( shadow.opacity() );
2404 
2405  double scale = shadow.scale() / 100.0;
2406  // TODO: scale from center/center, left/center or left/top, instead of default left/bottom?
2407  p->scale( scale, scale );
2408  if ( component.useOrigin )
2409  {
2410  p->translate( component.origin.x(), component.origin.y() );
2411  }
2412  p->translate( transPt );
2413  p->translate( -imgOffset.x(),
2414  -imgOffset.y() );
2415  p->drawImage( 0, 0, blurImg );
2416  p->restore();
2417 
2418  // debug rects
2419 #if 0
2420  // draw debug rect for QImage painting registration
2421  p->save();
2422  p->setBrush( Qt::NoBrush );
2423  QPen imgPen( QColor( 255, 0, 0, 10 ) );
2424  imgPen.setWidth( 2 );
2425  imgPen.setStyle( Qt::DashLine );
2426  p->setPen( imgPen );
2427  p->scale( scale, scale );
2428  if ( component.useOrigin() )
2429  {
2430  p->translate( component.origin().x(), component.origin().y() );
2431  }
2432  p->translate( transPt );
2433  p->translate( -imgOffset.x(),
2434  -imgOffset.y() );
2435  p->drawRect( 0, 0, blurImg.width(), blurImg.height() );
2436  p->restore();
2437 
2438  // draw debug rect for passed in component dimensions
2439  p->save();
2440  p->setBrush( Qt::NoBrush );
2441  QPen componentRectPen( QColor( 0, 255, 0, 70 ) );
2442  componentRectPen.setWidth( 1 );
2443  if ( component.useOrigin() )
2444  {
2445  p->translate( component.origin().x(), component.origin().y() );
2446  }
2447  p->setPen( componentRectPen );
2448  p->drawRect( QRect( -xOffset, -componentHeight - yOffset, componentWidth, componentHeight ) );
2449  p->restore();
2450 #endif
2451 }
2452 
2453 void QgsTextRenderer::drawTextInternal( TextPart drawType,
2454  QgsRenderContext &context,
2455  const QgsTextFormat &format,
2456  const Component &component,
2457  const QStringList &textLines,
2458  const QFontMetricsF *fontMetrics,
2459  HAlignment alignment,
2460  bool drawAsOutlines
2461  , DrawMode mode )
2462 {
2463  if ( !context.painter() )
2464  {
2465  return;
2466  }
2467 
2468  double labelWidest = 0.0;
2469  switch ( mode )
2470  {
2471  case Label:
2472  case Point:
2473  Q_FOREACH ( const QString &line, textLines )
2474  {
2475  double labelWidth = fontMetrics->width( line );
2476  if ( labelWidth > labelWidest )
2477  {
2478  labelWidest = labelWidth;
2479  }
2480  }
2481  break;
2482 
2483  case Rect:
2484  labelWidest = component.size.width();
2485  break;
2486  }
2487 
2488  double labelHeight = fontMetrics->ascent() + fontMetrics->descent(); // ignore +1 for baseline
2489  // double labelHighest = labelfm->height() + ( double )(( lines - 1 ) * labelHeight * tmpLyr.multilineHeight );
2490 
2491  // needed to move bottom of text's descender to within bottom edge of label
2492  double ascentOffset = 0.25 * fontMetrics->ascent(); // labelfm->descent() is not enough
2493 
2494  int i = 0;
2495 
2496  bool adjustForAlignment = alignment != AlignLeft && ( mode != Label || textLines.size() > 1 );
2497 
2498  Q_FOREACH ( const QString &line, textLines )
2499  {
2500  context.painter()->save();
2501  if ( context.flags() & QgsRenderContext::Antialiasing )
2502  {
2503  context.painter()->setRenderHint( QPainter::Antialiasing );
2504  }
2505  context.painter()->translate( component.origin );
2506  if ( !qgsDoubleNear( component.rotation, 0.0 ) )
2507  context.painter()->rotate( -component.rotation * 180 / M_PI );
2508 
2509  // figure x offset for horizontal alignment of multiple lines
2510  double xMultiLineOffset = 0.0;
2511  double labelWidth = fontMetrics->width( line );
2512  if ( adjustForAlignment )
2513  {
2514  double labelWidthDiff = labelWidest - labelWidth;
2515  if ( alignment == AlignCenter )
2516  {
2517  labelWidthDiff /= 2;
2518  }
2519  switch ( mode )
2520  {
2521  case Label:
2522  case Rect:
2523  xMultiLineOffset = labelWidthDiff;
2524  break;
2525 
2526  case Point:
2527  if ( alignment == AlignRight )
2528  xMultiLineOffset = labelWidthDiff - labelWidest;
2529  else if ( alignment == AlignCenter )
2530  xMultiLineOffset = labelWidthDiff - labelWidest / 2.0;
2531 
2532  break;
2533  }
2534  //QgsDebugMsgLevel( QString( "xMultiLineOffset: %1" ).arg( xMultiLineOffset ), 4 );
2535  }
2536 
2537  double yMultiLineOffset = 0.0;
2538  switch ( mode )
2539  {
2540  case Label:
2541  // rendering labels needs special handling - in this case text should be
2542  // drawn with the bottom left corner coinciding with origin, vs top left
2543  // for standard text rendering. Line height is also slightly different.
2544  yMultiLineOffset = - ascentOffset - ( textLines.size() - 1 - i ) * labelHeight * format.lineHeight();
2545  break;
2546 
2547  case Rect:
2548  // standard rendering - designed to exactly replicate QPainter's drawText method
2549  yMultiLineOffset = - ascentOffset + labelHeight - 1 /*baseline*/ + format.lineHeight() * fontMetrics->lineSpacing() * i;
2550  break;
2551 
2552  case Point:
2553  // standard rendering - designed to exactly replicate QPainter's drawText rect method
2554  yMultiLineOffset = 0 - ( textLines.size() - 1 - i ) * fontMetrics->lineSpacing() * format.lineHeight();
2555  break;
2556 
2557  }
2558 
2559  context.painter()->translate( QPointF( xMultiLineOffset, yMultiLineOffset ) );
2560 
2561  Component subComponent;
2562  subComponent.text = line;
2563  subComponent.size = QSizeF( labelWidth, labelHeight );
2564  subComponent.offset = QPointF( 0.0, -ascentOffset );
2565  subComponent.rotation = -component.rotation * 180 / M_PI;
2566  subComponent.rotationOffset = 0.0;
2567 
2568  if ( drawType == QgsTextRenderer::Buffer )
2569  {
2570  QgsTextRenderer::drawBuffer( context, subComponent, format );
2571  }
2572  else
2573  {
2574  // draw text, QPainterPath method
2575  QPainterPath path;
2576  path.setFillRule( Qt::WindingFill );
2577  path.addText( 0, 0, format.scaledFont( context ), subComponent.text );
2578 
2579  // store text's drawing in QPicture for drop shadow call
2580  QPicture textPict;
2581  QPainter textp;
2582  textp.begin( &textPict );
2583  textp.setPen( Qt::NoPen );
2584  QColor textColor = format.color();
2585  textColor.setAlphaF( format.opacity() );
2586  textp.setBrush( textColor );
2587  textp.drawPath( path );
2588  // TODO: why are some font settings lost on drawPicture() when using drawText() inside QPicture?
2589  // e.g. some capitalization options, but not others
2590  //textp.setFont( tmpLyr.textFont );
2591  //textp.setPen( tmpLyr.textColor );
2592  //textp.drawText( 0, 0, component.text() );
2593  textp.end();
2594 
2595  if ( format.shadow().enabled() && format.shadow().shadowPlacement() == QgsTextShadowSettings::ShadowText )
2596  {
2597  subComponent.picture = textPict;
2598  subComponent.pictureBuffer = 0.0; // no pen width to deal with
2599  subComponent.origin = QPointF( 0.0, 0.0 );
2600 
2601  QgsTextRenderer::drawShadow( context, subComponent, format );
2602  }
2603 
2604  // paint the text
2605  if ( context.useAdvancedEffects() )
2606  {
2607  context.painter()->setCompositionMode( format.blendMode() );
2608  }
2609 
2610  // scale for any print output or image saving @ specific dpi
2611  context.painter()->scale( subComponent.dpiRatio, subComponent.dpiRatio );
2612 
2613  if ( drawAsOutlines )
2614  {
2615  // draw outlined text
2616  _fixQPictureDPI( context.painter() );
2617  context.painter()->drawPicture( 0, 0, textPict );
2618  }
2619  else
2620  {
2621  // draw text as text (for SVG and PDF exports)
2622  context.painter()->setFont( format.scaledFont( context ) );
2623  QColor textColor = format.color();
2624  textColor.setAlphaF( format.opacity() );
2625  context.painter()->setPen( textColor );
2626  context.painter()->setRenderHint( QPainter::TextAntialiasing );
2627  context.painter()->drawText( 0, 0, subComponent.text );
2628  }
2629  }
2630  context.painter()->restore();
2631  i++;
2632  }
2633 }
2634 
QPainter::CompositionMode blendMode() const
Returns the blending mode used for drawing the buffer.
The class is used as a container of context for various read/write operations on other objects...
QColor strokeColor() const
Returns the color used for outlining the background shape.
void setScale(int scale)
Sets the scaling used for the drop shadow (in percentage of original size).
Shape size is determined by adding a buffer margin around text.
void setLineHeight(double height)
Sets the line height for text.
void setRadiiUnit(QgsUnitTypes::RenderUnit units)
Sets the units used for the shape&#39;s radii.
void setStrokeWidth(double width)
Sets the width of the shape&#39;s stroke (stroke).
RotationType
Methods for determining the rotation of the background shape.
void setOffsetMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale object for the shadow offset distance.
void setOpacity(double opacity)
Sets the text&#39;s opacity.
void setStrokeWidthMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale object for the shape stroke width.
void setOffsetUnit(QgsUnitTypes::RenderUnit units)
Sets the units used for the shadow&#39;s offset.
QgsTextShadowSettings::ShadowPlacement shadowPlacement() const
Returns the placement for the drop shadow.
QSizeF radii() const
Returns the radii used for rounding the corners of shapes.
QSizeF size() const
Returns the size of the background shape.
double opacity() const
Returns the text&#39;s opacity.
void setSizeUnit(QgsUnitTypes::RenderUnit unit)
Sets the units used for the shape&#39;s size.
void readXml(const QDomElement &elem, const QgsReadWriteContext &context)
Read settings from a DOM element.
static QString svgSymbolPathToName(QString path, const QgsPathResolver &pathResolver)
Get SVG symbols&#39;s name from its path.
QPainter::CompositionMode blendMode() const
Returns the blending mode used for drawing the drop shadow.
QPointF offset() const
Returns the offset used for drawing the background shape.
QColor fillColor() const
Returns the color used for filing the background shape.
QgsMapUnitScale strokeWidthMapUnitScale() const
Returns the map unit scale object for the shape stroke width.
QgsTextShadowSettings & operator=(const QgsTextShadowSettings &other)
void readXml(const QDomElement &elem)
Read settings from a DOM element.
Use antialiasing while drawing.
Draw shadow under buffer.
ShadowPlacement
Placement positions for text shadow.
QgsTextShadowSettings & shadow()
Returns a reference to the text drop shadow settings.
double blurRadius() const
Returns the blur radius for the shadow.
QgsUnitTypes::RenderUnit convertFromOldLabelUnit(int val)
double opacity() const
Returns the background shape&#39;s opacity.
void setSizeMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale object for the size.
void setStrokeColor(const QColor &color)
Sets the color used for outlining the background shape.
void setSize(double size)
Sets the size of the buffer.
double strokeWidth() const
Returns the width of the shape&#39;s stroke (stroke).
void setSizeUnit(QgsUnitTypes::RenderUnit unit)
Sets the units used for the buffer size.
HAlignment
Horizontal alignment.
double offsetDistance() const
Returns the distance for offsetting the position of the shadow from the text.
void setFont(const QFont &font)
Sets the font used for rendering text.
void setJoinStyle(Qt::PenJoinStyle style)
Sets the join style used for drawing the buffer.
static bool isDefaultStack(QgsPaintEffect *effect)
Tests whether a paint effect matches the default effects stack.
static QgsPainting::BlendMode getBlendModeEnum(QPainter::CompositionMode blendMode)
Returns a BlendMode corresponding to a QPainter::CompositionMode.
Definition: qgspainting.cpp:80
Base class for visual effects which can be applied to QPicture drawings.
void setOffsetUnit(QgsUnitTypes::RenderUnit units)
Sets the units used for the shape&#39;s offset.
QColor color() const
Returns the color that text will be rendered in.
static void drawPart(const QRectF &rect, double rotation, HAlignment alignment, const QStringList &textLines, QgsRenderContext &context, const QgsTextFormat &format, TextPart part, bool drawAsOutlines=true)
Draws a single component of rendered text using the specified settings.
void setFillBufferInterior(bool fill)
Sets whether the interior of the buffer will be filled in.
void setBlurRadius(double blurRadius)
Sets the blur radius for the shadow.
void setPaintEffect(QgsPaintEffect *effect)
Sets the current paint effect for the buffer.
void setOpacity(double opacity)
Sets the shadow&#39;s opacity.
QgsTextBufferSettings & operator=(const QgsTextBufferSettings &other)
Copy constructor.
void setSizeMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale object for the shape size.
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
static QPainter::CompositionMode getCompositionMode(QgsPainting::BlendMode blendMode)
Returns a QPainter::CompositionMode corresponding to a BlendMode.
Definition: qgspainting.cpp:20
Flags flags() const
Return combination of flags used for rendering.
void setJoinStyle(Qt::PenJoinStyle style)
Sets the join style used for drawing the background shape.
void readXml(const QDomElement &elem, const QgsReadWriteContext &context)
Read settings from a DOM element.
static QString svgSymbolNameToPath(QString name, const QgsPathResolver &pathResolver)
Get SVG symbol&#39;s path from its name.
Container for settings relating to a text background object.
Percentage of another measurement (e.g., canvas size, feature size)
Definition: qgsunittypes.h:105
static int sizeToPixel(double size, const QgsRenderContext &c, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &mapUnitScale=QgsMapUnitScale())
Calculates pixel size (considering output size should be in pixel or map units, scale factors and opt...
QgsMapUnitScale offsetMapUnitScale() const
Returns the map unit scale object for the shape offset.
void setRadiiMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale object for the shape radii.
void setBlurRadiusUnit(QgsUnitTypes::RenderUnit units)
Sets the units used for the shadow&#39;s blur radius.
static QString translateNamedStyle(const QString &namedStyle)
Returns the localized named style of a font, if such a translation is available.
QMap< QString, QString > QgsStringMap
Definition: qgis.h:479
void setBlurRadiusMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale object for the shadow blur radius.
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Compare two doubles (but allow some difference)
Definition: qgis.h:251
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
Definition: MathUtils.cpp:786
void readXml(const QDomElement &elem)
Read settings from a DOM element.
static QgsPaintEffectRegistry * paintEffectRegistry()
Returns the application&#39;s paint effect registry, used for managing paint effects. ...
ShapeType type() const
Returns the type of background shape (e.g., square, ellipse, SVG).
void setOffset(QPointF offset)
Sets the offset used for drawing the background shape.
QDomElement writeXml(QDomDocument &doc, const QgsReadWriteContext &context) const
Write settings into a DOM element.
QDomElement writeXml(QDomDocument &doc) const
Write settings into a DOM element.
Qt::PenJoinStyle joinStyle() const
Returns the buffer join style.
static QString encodeColor(const QColor &color)
QgsMapUnitScale sizeMapUnitScale() const
Returns the map unit scale object for the buffer size.
static QgsTextFormat fromMimeData(const QMimeData *data, bool *ok=nullptr)
Attempts to parse the provided mime data as a QgsTextFormat.
void readFromLayer(QgsVectorLayer *layer)
Reads settings from a layer&#39;s custom properties (for QGIS 2.x projects).
void readFromLayer(QgsVectorLayer *layer)
Reads settings from a layer&#39;s custom properties (for QGIS 2.x projects).
void setBlendMode(QPainter::CompositionMode mode)
Sets the blending mode used for drawing the background shape.
Shape rotation is a fixed angle.
QgsMapUnitScale offsetMapUnitScale() const
Returns the map unit scale object for the shadow offset distance.
QPainter::CompositionMode blendMode() const
Returns the blending mode used for drawing the text.
const QgsPathResolver & pathResolver() const
Returns path resolver for conversion between relative and absolute paths.
QColor color() const
Returns the color of the drop shadow.
void setSize(double size)
Sets the size for rendered text.
void setOffsetDistance(double distance)
Sets the distance for offsetting the position of the shadow from the text.
void setScaleFactor(double factor)
Sets the scaling factor for the render to convert painter units to physical sizes.
#define FALLTHROUGH
Definition: qgis.h:548
QgsMapUnitScale blurRadiusMapUnitScale() const
Returns the map unit scale object for the shadow blur radius.
bool fillBufferInterior() const
Returns whether the interior of the buffer will be filled in.
void setOffsetMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale object for the shape offset.
QDomElement writeXml(QDomDocument &doc, const QgsReadWriteContext &context) const
Write settings into a DOM element.
void setBlurAlphaOnly(bool alphaOnly)
Sets whether only the alpha channel for the shadow should be blurred.
Buffer component.
RotationType rotationType() const
Returns the method used for rotating the background shape.
void setSizeMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale object for the buffer size.
QgsPaintEffect * paintEffect() const
Returns the current paint effect for the background shape.
double size() const
Returns the symbol size.
void setBlendMode(QPainter::CompositionMode mode)
Sets the blending mode used for drawing the drop shadow.
void setColor(const QColor &color)
Sets the color for the drop shadow.
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
void setEnabled(bool enabled)
Sets whether the text shadow will be drawn.
void setEnabled(bool enabled)
Sets whether the text buffer will be drawn.
void setColor(const QColor &color)
Sets the color that text will be rendered in.
Draw shadow under text.
QgsMapUnitScale radiiMapUnitScale() const
Returns the map unit scale object for the shape radii.
QString svgFile() const
Returns the absolute path to the background SVG file, if set.
Qt::PenJoinStyle joinStyle() const
Returns the join style used for drawing the background shape.
void setNamedStyle(const QString &style)
Sets the named style for the font used for rendering text.
void setPainter(QPainter *p)
Sets the destination QPainter for the render operation.
QgsTextBackgroundSettings & background()
Returns a reference to the text background settings.
QDomElement writeXml(QDomDocument &doc) const
Write settings into a DOM element.
QgsTextBufferSettings & buffer()
Returns a reference to the text buffer settings.
void setSizeUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the size of rendered text.
SizeType
Methods for determining the background shape size.
double opacity() const
Returns the buffer opacity.
bool enabled() const
Returns whether the effect is enabled.
Q_GUI_EXPORT int qt_defaultDpiY()
void setSize(QSizeF size)
Sets the size of the background shape.
static Q_INVOKABLE QgsUnitTypes::RenderUnit decodeRenderUnit(const QString &string, bool *ok=nullptr)
Decodes a render unit from a string.
QMimeData * toMimeData() const
Returns new mime data representing the text format settings.
void setRadii(QSizeF radii)
Sets the radii used for rounding the corners of shapes.
QgsUnitTypes::RenderUnit strokeWidthUnit() const
Returns the units used for the shape&#39;s stroke width.
void setSizeType(SizeType type)
Sets the method used to determine the size of the background shape (e.g., fixed size or buffer around...
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
void setType(ShapeType type)
Sets the type of background shape to draw (e.g., square, ellipse, SVG).
Draw shadow below all text components.
static Q_INVOKABLE QString encodeUnit(QgsUnitTypes::DistanceUnit unit)
Encodes a distance unit to a string.
QPainter::CompositionMode blendMode() const
Returns the blending mode used for drawing the background shape.
static void drawText(const QRectF &rect, double rotation, HAlignment alignment, const QStringList &textLines, QgsRenderContext &context, const QgsTextFormat &format, bool drawAsOutlines=true)
Draws text within a rectangle using the specified settings.
QgsUnitTypes::RenderUnit offsetUnit() const
Returns the units used for the shape&#39;s offset.
double rotation() const
Returns the rotation for the background shape, in degrees clockwise.
double lineHeight() const
Returns the line height for text.
void setEnabled(bool enabled)
Sets whether the text background will be drawn.
int offsetAngle() const
Returns the angle for offsetting the position of the shadow from the text.
ShapeType
Background shape types.
TextPart
Components of text.
static QString untranslateNamedStyle(const QString &namedStyle)
Returns the english named style of a font, if possible.
QFont scaledFont(const QgsRenderContext &context) const
Returns a font with the size scaled to match the format&#39;s size settings (including units and map unit...
Contains information about the context of a rendering operation.
Shape rotation is offset from text rotation.
double convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale()) const
Converts a size from the specified units to painter units (pixels).
static bool fontFamilyMatchOnSystem(const QString &family, QString *chosen=nullptr, bool *match=nullptr)
Check whether font family is on system.
QgsMapUnitScale sizeMapUnitScale() const
Returns the map unit scale object for the size.
QgsUnitTypes::RenderUnit sizeUnit() const
Returns the units for the buffer size.
QPainter * painter()
Returns the destination QPainter for the render operation.
const QgsMapToPixel & mapToPixel() const
static void blurImageInPlace(QImage &image, QRect rect, int radius, bool alphaOnly)
Blurs an image in place, e.g. creating Qt-independent drop shadows.
QgsUnitTypes::RenderUnit offsetUnit() const
Returns the units used for the shadow&#39;s offset.
bool enabled() const
Returns whether the shadow is enabled.
QgsTextBackgroundSettings & operator=(const QgsTextBackgroundSettings &other)
Struct for storing maximum and minimum scales for measurements in map units.
QgsTextFormat & operator=(const QgsTextFormat &other)
Container for settings relating to a text shadow.
bool offsetGlobal() const
Returns true if the global shadow offset will be used.
QColor color() const
Returns the color of the buffer.
double size() const
Returns the size of the buffer.
QgsUnitTypes::RenderUnit radiiUnit() const
Returns the units used for the shape&#39;s radii.
Container for settings relating to a text buffer.
void setFillColor(const QColor &color)
Sets the color used for filing the background shape.
static bool updateFontViaStyle(QFont &f, const QString &fontstyle, bool fallback=false)
Updates font with named style and retain all font properties.
bool useAdvancedEffects() const
Returns true if advanced effects such as blend modes such be used.
double size() const
Returns the size for rendered text.
void setStrokeWidthUnit(QgsUnitTypes::RenderUnit units)
Sets the units used for the shape&#39;s stroke width.
bool enabled() const
Returns whether the background is enabled.
void setMapToPixel(const QgsMapToPixel &mtp)
SizeType sizeType() const
Returns the method used to determine the size of the background shape (e.g., fixed size or buffer aro...
QgsUnitTypes::RenderUnit blurRadiusUnit() const
Returns the units used for the shadow&#39;s blur radius.
bool enabled() const
Returns whether the buffer is enabled.
int scale() const
Returns the scaling used for the drop shadow (in percentage of original size).
Shape rotation is synced with text rotation.
void setShadowPlacement(QgsTextShadowSettings::ShadowPlacement placement)
Sets the placement for the drop shadow.
void setBlendMode(QPainter::CompositionMode mode)
Sets the blending mode used for drawing the text.
Q_GUI_EXPORT int qt_defaultDpiX()
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
Draw shadow under background shape.
QgsPaintEffect * paintEffect() const
Returns the current paint effect for the buffer.
QVariant customProperty(const QString &value, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer.
QgsMapUnitScale sizeMapUnitScale() const
Returns the map unit scale object for the shape size.
Container for all settings relating to text rendering.
QgsUnitTypes::RenderUnit sizeUnit() const
Returns the units for the size of rendered text.
QgsPaintEffect * createEffect(const QString &name, const QgsStringMap &properties=QgsStringMap()) const
Creates a new paint effect given the effect name and properties map.
void setBlendMode(QPainter::CompositionMode mode)
Sets the blending mode used for drawing the buffer.
void setSvgFile(const QString &file)
Sets the path to the background SVG file.
bool blurAlphaOnly() const
Returns whether only the alpha channel for the shadow will be blurred.
void setOffsetGlobal(bool global)
Sets whether the global shadow offset should be used.
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
Represents a vector layer which manages a vector based data sets.
void setOpacity(double opacity)
Sets the buffer opacity.
Square - buffered sizes only.
void readFromLayer(QgsVectorLayer *layer)
Reads settings from a layer&#39;s custom properties (for QGIS 2.x projects).
virtual void end(QgsRenderContext &context)
Ends interception of paint operations to a render context, and draws the result to the render context...
bool containsAdvancedEffects() const
Returns true if any component of the font format requires advanced effects such as blend modes...
QString namedStyle() const
Returns the named style for the font used for rendering text (e.g., "bold").
QFont font() const
Returns the font used for rendering text.
void setRotationType(RotationType type)
Sets the method used for rotating the background shape.
void setColor(const QColor &color)
Sets the color for the buffer.
QgsUnitTypes::RenderUnit sizeUnit() const
Returns the units used for the shape&#39;s size.
virtual void begin(QgsRenderContext &context)
Begins intercepting paint operations to a render context.
double opacity() const
Returns the shadow&#39;s opacity.
void setOpacity(double opacity)
Sets the background shape&#39;s opacity.
RenderUnit
Rendering size units.
Definition: qgsunittypes.h:100
static QColor decodeColor(const QString &str)
void setPaintEffect(QgsPaintEffect *effect)
Sets the current paint effect for the background shape.
void setOffsetAngle(int angle)
Sets the angle for offsetting the position of the shadow from the text.
void readFromLayer(QgsVectorLayer *layer)
Reads settings from a layer&#39;s custom properties (for QGIS 2.x projects).
void setRotation(double rotation)
Sets the rotation for the background shape, in degrees clockwise.