QGIS API Documentation  3.12.1-BucureČ™ti (121cc00ff0)
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 "qgspallabeling.h"
29 #include "qgspainterswapper.h"
30 #include <QFontDatabase>
31 #include <QDesktopWidget>
32 
33 Q_GUI_EXPORT extern int qt_defaultDpiX();
34 Q_GUI_EXPORT extern int qt_defaultDpiY();
35 
37 {
38  if ( val == 0 )
40  else if ( val == 1 )
42  else if ( val == 2 )
44  else if ( val == 3 )
46  else
48 }
49 
50 static void _fixQPictureDPI( QPainter *p )
51 {
52  // QPicture makes an assumption that we drawing to it with system DPI.
53  // Then when being drawn, it scales the painter. The following call
54  // negates the effect. There is no way of setting QPicture's DPI.
55  // See QTBUG-20361
56  p->scale( static_cast< double >( qt_defaultDpiX() ) / p->device()->logicalDpiX(),
57  static_cast< double >( qt_defaultDpiY() ) / p->device()->logicalDpiY() );
58 }
59 
60 static QColor _readColor( QgsVectorLayer *layer, const QString &property, const QColor &defaultColor = Qt::black, bool withAlpha = true )
61 {
62  int r = layer->customProperty( property + 'R', QVariant( defaultColor.red() ) ).toInt();
63  int g = layer->customProperty( property + 'G', QVariant( defaultColor.green() ) ).toInt();
64  int b = layer->customProperty( property + 'B', QVariant( defaultColor.blue() ) ).toInt();
65  int a = withAlpha ? layer->customProperty( property + 'A', QVariant( defaultColor.alpha() ) ).toInt() : 255;
66  return QColor( r, g, b, a );
67 }
68 
70 {
71  d = new QgsTextBufferSettingsPrivate();
72 }
73 
75  : d( other.d )
76 {
77 }
78 
80 {
81  d = other.d;
82  return *this;
83 }
84 
86 {
87 
88 }
89 
91 {
92  return d->enabled;
93 }
94 
96 {
97  d->enabled = enabled;
98 }
99 
101 {
102  return d->size;
103 }
104 
106 {
107  d->size = size;
108 }
109 
111 {
112  return d->sizeUnit;
113 }
114 
116 {
117  d->sizeUnit = unit;
118 }
119 
121 {
122  return d->sizeMapUnitScale;
123 }
124 
126 {
127  d->sizeMapUnitScale = scale;
128 }
129 
131 {
132  return d->color;
133 }
134 
136 {
137  d->color = color;
138 }
139 
141 {
142  return d->fillBufferInterior;
143 }
144 
146 {
147  d->fillBufferInterior = fill;
148 }
149 
151 {
152  return d->opacity;
153 }
154 
156 {
157  d->opacity = opacity;
158 }
159 
160 Qt::PenJoinStyle QgsTextBufferSettings::joinStyle() const
161 {
162  return d->joinStyle;
163 }
164 
165 void QgsTextBufferSettings::setJoinStyle( Qt::PenJoinStyle style )
166 {
167  d->joinStyle = style;
168 }
169 
170 QPainter::CompositionMode QgsTextBufferSettings::blendMode() const
171 {
172  return d->blendMode;
173 }
174 
175 void QgsTextBufferSettings::setBlendMode( QPainter::CompositionMode mode )
176 {
177  d->blendMode = mode;
178 }
179 
181 {
182  return d->paintEffect.get();
183 }
184 
186 {
187  d->paintEffect.reset( effect );
188 }
189 
191 {
192  if ( properties.isActive( QgsPalLayerSettings::BufferDraw ) )
193  {
194  context.expressionContext().setOriginalValueVariable( d->enabled );
195  d->enabled = properties.valueAsBool( QgsPalLayerSettings::BufferDraw, context.expressionContext(), d->enabled );
196  }
197 
198  if ( properties.isActive( QgsPalLayerSettings::BufferSize ) )
199  {
200  context.expressionContext().setOriginalValueVariable( d->size );
201  d->size = properties.valueAsDouble( QgsPalLayerSettings::BufferSize, context.expressionContext(), d->size );
202  }
203 
204  QVariant exprVal = properties.value( QgsPalLayerSettings::BufferUnit, context.expressionContext() );
205  if ( exprVal.isValid() )
206  {
207  QString units = exprVal.toString();
208  if ( !units.isEmpty() )
209  {
210  bool ok;
212  if ( ok )
213  d->sizeUnit = res;
214  }
215  }
216 
217  if ( properties.isActive( QgsPalLayerSettings::BufferOpacity ) )
218  {
219  context.expressionContext().setOriginalValueVariable( d->opacity * 100 );
220  d->opacity = properties.value( QgsPalLayerSettings::BufferOpacity, context.expressionContext(), d->opacity * 100 ).toDouble() / 100.0;
221  }
222 
223  if ( properties.isActive( QgsPalLayerSettings::BufferColor ) )
224  {
226  d->color = properties.valueAsColor( QgsPalLayerSettings::BufferColor, context.expressionContext(), d->color );
227  }
228 
230  {
231  exprVal = properties.value( QgsPalLayerSettings::BufferBlendMode, context.expressionContext() );
232  QString blendstr = exprVal.toString().trimmed();
233  if ( !blendstr.isEmpty() )
234  d->blendMode = QgsSymbolLayerUtils::decodeBlendMode( blendstr );
235  }
236 
238  {
239  exprVal = properties.value( QgsPalLayerSettings::BufferJoinStyle, context.expressionContext() );
240  QString joinstr = exprVal.toString().trimmed();
241  if ( !joinstr.isEmpty() )
242  {
243  d->joinStyle = QgsSymbolLayerUtils::decodePenJoinStyle( joinstr );
244  }
245  }
246 }
247 
249 {
250  // text buffer
251  double bufSize = layer->customProperty( QStringLiteral( "labeling/bufferSize" ), QVariant( 0.0 ) ).toDouble();
252 
253  // fix for buffer being keyed off of just its size in the past (<2.0)
254  QVariant drawBuffer = layer->customProperty( QStringLiteral( "labeling/bufferDraw" ), QVariant() );
255  if ( drawBuffer.isValid() )
256  {
257  d->enabled = drawBuffer.toBool();
258  d->size = bufSize;
259  }
260  else if ( bufSize != 0.0 )
261  {
262  d->enabled = true;
263  d->size = bufSize;
264  }
265  else
266  {
267  // keep bufferSize at new 1.0 default
268  d->enabled = false;
269  }
270 
271  if ( layer->customProperty( QStringLiteral( "labeling/bufferSizeUnits" ) ).toString().isEmpty() )
272  {
273  bool bufferSizeInMapUnits = layer->customProperty( QStringLiteral( "labeling/bufferSizeInMapUnits" ) ).toBool();
274  d->sizeUnit = bufferSizeInMapUnits ? QgsUnitTypes::RenderMapUnits : QgsUnitTypes::RenderMillimeters;
275  }
276  else
277  {
278  d->sizeUnit = QgsUnitTypes::decodeRenderUnit( layer->customProperty( QStringLiteral( "labeling/bufferSizeUnits" ) ).toString() );
279  }
280 
281  if ( layer->customProperty( QStringLiteral( "labeling/bufferSizeMapUnitScale" ) ).toString().isEmpty() )
282  {
283  //fallback to older property
284  double oldMin = layer->customProperty( QStringLiteral( "labeling/bufferSizeMapUnitMinScale" ), 0.0 ).toDouble();
285  d->sizeMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
286  double oldMax = layer->customProperty( QStringLiteral( "labeling/bufferSizeMapUnitMaxScale" ), 0.0 ).toDouble();
287  d->sizeMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
288  }
289  else
290  {
291  d->sizeMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( layer->customProperty( QStringLiteral( "labeling/bufferSizeMapUnitScale" ) ).toString() );
292  }
293  d->color = _readColor( layer, QStringLiteral( "labeling/bufferColor" ), Qt::white, false );
294  if ( layer->customProperty( QStringLiteral( "labeling/bufferOpacity" ) ).toString().isEmpty() )
295  {
296  d->opacity = ( 1 - layer->customProperty( QStringLiteral( "labeling/bufferTransp" ) ).toInt() / 100.0 ); //0 -100
297  }
298  else
299  {
300  d->opacity = ( layer->customProperty( QStringLiteral( "labeling/bufferOpacity" ) ).toDouble() );
301  }
302  d->blendMode = QgsPainting::getCompositionMode(
303  static_cast< QgsPainting::BlendMode >( layer->customProperty( QStringLiteral( "labeling/bufferBlendMode" ), QVariant( QgsPainting::BlendNormal ) ).toUInt() ) );
304  d->joinStyle = static_cast< Qt::PenJoinStyle >( layer->customProperty( QStringLiteral( "labeling/bufferJoinStyle" ), QVariant( Qt::RoundJoin ) ).toUInt() );
305 
306  d->fillBufferInterior = !layer->customProperty( QStringLiteral( "labeling/bufferNoFill" ), QVariant( false ) ).toBool();
307 
308  if ( layer->customProperty( QStringLiteral( "labeling/bufferEffect" ) ).isValid() )
309  {
310  QDomDocument doc( QStringLiteral( "effect" ) );
311  doc.setContent( layer->customProperty( QStringLiteral( "labeling/bufferEffect" ) ).toString() );
312  QDomElement effectElem = doc.firstChildElement( QStringLiteral( "effect" ) ).firstChildElement( QStringLiteral( "effect" ) );
313  setPaintEffect( QgsApplication::paintEffectRegistry()->createEffect( effectElem ) );
314  }
315  else
316  setPaintEffect( nullptr );
317 }
318 
319 void QgsTextBufferSettings::readXml( const QDomElement &elem )
320 {
321  QDomElement textBufferElem = elem.firstChildElement( QStringLiteral( "text-buffer" ) );
322  double bufSize = textBufferElem.attribute( QStringLiteral( "bufferSize" ), QStringLiteral( "0" ) ).toDouble();
323 
324  // fix for buffer being keyed off of just its size in the past (<2.0)
325  QVariant drawBuffer = textBufferElem.attribute( QStringLiteral( "bufferDraw" ) );
326  if ( drawBuffer.isValid() )
327  {
328  d->enabled = drawBuffer.toBool();
329  d->size = bufSize;
330  }
331  else if ( bufSize != 0.0 )
332  {
333  d->enabled = true;
334  d->size = bufSize;
335  }
336  else
337  {
338  // keep bufferSize at new 1.0 default
339  d->enabled = false;
340  }
341 
342  if ( !textBufferElem.hasAttribute( QStringLiteral( "bufferSizeUnits" ) ) )
343  {
344  bool bufferSizeInMapUnits = textBufferElem.attribute( QStringLiteral( "bufferSizeInMapUnits" ) ).toInt();
345  d->sizeUnit = bufferSizeInMapUnits ? QgsUnitTypes::RenderMapUnits : QgsUnitTypes::RenderMillimeters;
346  }
347  else
348  {
349  d->sizeUnit = QgsUnitTypes::decodeRenderUnit( textBufferElem.attribute( QStringLiteral( "bufferSizeUnits" ) ) );
350  }
351 
352  if ( !textBufferElem.hasAttribute( QStringLiteral( "bufferSizeMapUnitScale" ) ) )
353  {
354  //fallback to older property
355  double oldMin = textBufferElem.attribute( QStringLiteral( "bufferSizeMapUnitMinScale" ), QStringLiteral( "0" ) ).toDouble();
356  d->sizeMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
357  double oldMax = textBufferElem.attribute( QStringLiteral( "bufferSizeMapUnitMaxScale" ), QStringLiteral( "0" ) ).toDouble();
358  d->sizeMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
359  }
360  else
361  {
362  d->sizeMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( textBufferElem.attribute( QStringLiteral( "bufferSizeMapUnitScale" ) ) );
363  }
364  d->color = QgsSymbolLayerUtils::decodeColor( textBufferElem.attribute( QStringLiteral( "bufferColor" ), QgsSymbolLayerUtils::encodeColor( Qt::white ) ) );
365 
366  if ( !textBufferElem.hasAttribute( QStringLiteral( "bufferOpacity" ) ) )
367  {
368  d->opacity = ( 1 - textBufferElem.attribute( QStringLiteral( "bufferTransp" ) ).toInt() / 100.0 ); //0 -100
369  }
370  else
371  {
372  d->opacity = ( textBufferElem.attribute( QStringLiteral( "bufferOpacity" ) ).toDouble() );
373  }
374 
375  d->blendMode = QgsPainting::getCompositionMode(
376  static_cast< QgsPainting::BlendMode >( textBufferElem.attribute( QStringLiteral( "bufferBlendMode" ), QString::number( QgsPainting::BlendNormal ) ).toUInt() ) );
377  d->joinStyle = static_cast< Qt::PenJoinStyle >( textBufferElem.attribute( QStringLiteral( "bufferJoinStyle" ), QString::number( Qt::RoundJoin ) ).toUInt() );
378  d->fillBufferInterior = !textBufferElem.attribute( QStringLiteral( "bufferNoFill" ), QStringLiteral( "0" ) ).toInt();
379  QDomElement effectElem = textBufferElem.firstChildElement( QStringLiteral( "effect" ) );
380  if ( !effectElem.isNull() )
382  else
383  setPaintEffect( nullptr );
384 }
385 
386 QDomElement QgsTextBufferSettings::writeXml( QDomDocument &doc ) const
387 {
388  // text buffer
389  QDomElement textBufferElem = doc.createElement( QStringLiteral( "text-buffer" ) );
390  textBufferElem.setAttribute( QStringLiteral( "bufferDraw" ), d->enabled );
391  textBufferElem.setAttribute( QStringLiteral( "bufferSize" ), d->size );
392  textBufferElem.setAttribute( QStringLiteral( "bufferSizeUnits" ), QgsUnitTypes::encodeUnit( d->sizeUnit ) );
393  textBufferElem.setAttribute( QStringLiteral( "bufferSizeMapUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( d->sizeMapUnitScale ) );
394  textBufferElem.setAttribute( QStringLiteral( "bufferColor" ), QgsSymbolLayerUtils::encodeColor( d->color ) );
395  textBufferElem.setAttribute( QStringLiteral( "bufferNoFill" ), !d->fillBufferInterior );
396  textBufferElem.setAttribute( QStringLiteral( "bufferOpacity" ), d->opacity );
397  textBufferElem.setAttribute( QStringLiteral( "bufferJoinStyle" ), static_cast< unsigned int >( d->joinStyle ) );
398  textBufferElem.setAttribute( QStringLiteral( "bufferBlendMode" ), QgsPainting::getBlendModeEnum( d->blendMode ) );
399  if ( d->paintEffect && !QgsPaintEffectRegistry::isDefaultStack( d->paintEffect.get() ) )
400  d->paintEffect->saveProperties( doc, textBufferElem );
401  return textBufferElem;
402 }
403 
404 
405 //
406 // QgsTextBackgroundSettings
407 //
408 
410 {
411  d = new QgsTextBackgroundSettingsPrivate();
412 }
413 
415  : d( other.d )
416 {
417 
418 }
419 
421 {
422  d = other.d;
423  return *this;
424 }
425 
427 {
428 
429 }
430 
432 {
433  return d->enabled;
434 }
435 
437 {
438  d->enabled = enabled;
439 }
440 
442 {
443  return d->type;
444 }
445 
447 {
448  d->type = type;
449 }
450 
452 {
453  return d->svgFile;
454 }
455 
456 void QgsTextBackgroundSettings::setSvgFile( const QString &file )
457 {
458  d->svgFile = file;
459 }
460 
462 {
463  return d->markerSymbol.get();
464 }
465 
467 {
468  d->markerSymbol.reset( symbol );
469 }
470 
472 {
473  return d->sizeType;
474 }
475 
477 {
478  d->sizeType = type;
479 }
480 
482 {
483  return d->size;
484 }
485 
487 {
488  d->size = size;
489 }
490 
492 {
493  return d->sizeUnits;
494 }
495 
497 {
498  d->sizeUnits = unit;
499 }
500 
502 {
503  return d->sizeMapUnitScale;
504 }
505 
507 {
508  d->sizeMapUnitScale = scale;
509 }
510 
512 {
513  return d->rotationType;
514 }
515 
517 {
518  d->rotationType = type;
519 }
520 
522 {
523  return d->rotation;
524 }
525 
527 {
528  d->rotation = rotation;
529 }
530 
532 {
533  return d->offset;
534 }
535 
537 {
538  d->offset = offset;
539 }
540 
542 {
543  return d->offsetUnits;
544 }
545 
547 {
548  d->offsetUnits = units;
549 }
550 
552 {
553  return d->offsetMapUnitScale;
554 }
555 
557 {
558  d->offsetMapUnitScale = scale;
559 }
560 
562 {
563  return d->radii;
564 }
565 
567 {
568  d->radii = radii;
569 }
570 
572 {
573  return d->radiiUnits;
574 }
575 
577 {
578  d->radiiUnits = units;
579 }
580 
582 {
583  return d->radiiMapUnitScale;
584 }
585 
587 {
588  d->radiiMapUnitScale = scale;
589 }
590 
592 {
593  return d->opacity;
594 }
595 
597 {
598  d->opacity = opacity;
599 }
600 
601 QPainter::CompositionMode QgsTextBackgroundSettings::blendMode() const
602 {
603  return d->blendMode;
604 }
605 
606 void QgsTextBackgroundSettings::setBlendMode( QPainter::CompositionMode mode )
607 {
608  d->blendMode = mode;
609 }
610 
612 {
613  return d->fillColor;
614 }
615 
616 void QgsTextBackgroundSettings::setFillColor( const QColor &color )
617 {
618  d->fillColor = color;
619 }
620 
622 {
623  return d->strokeColor;
624 }
625 
626 void QgsTextBackgroundSettings::setStrokeColor( const QColor &color )
627 {
628  d->strokeColor = color;
629 }
630 
632 {
633  return d->strokeWidth;
634 }
635 
637 {
638  d->strokeWidth = width;
639 }
640 
642 {
643  return d->strokeWidthUnits;
644 }
645 
647 {
648  d->strokeWidthUnits = units;
649 }
650 
652 {
653  return d->strokeWidthMapUnitScale;
654 }
655 
657 {
658  d->strokeWidthMapUnitScale = scale;
659 }
660 
661 Qt::PenJoinStyle QgsTextBackgroundSettings::joinStyle() const
662 {
663  return d->joinStyle;
664 }
665 
666 void QgsTextBackgroundSettings::setJoinStyle( Qt::PenJoinStyle style )
667 {
668  d->joinStyle = style;
669 }
670 
672 {
673  return d->paintEffect.get();
674 }
675 
677 {
678  d->paintEffect.reset( effect );
679 }
680 
682 {
683  d->enabled = layer->customProperty( QStringLiteral( "labeling/shapeDraw" ), QVariant( false ) ).toBool();
684  d->type = static_cast< ShapeType >( layer->customProperty( QStringLiteral( "labeling/shapeType" ), QVariant( ShapeRectangle ) ).toUInt() );
685  d->svgFile = layer->customProperty( QStringLiteral( "labeling/shapeSVGFile" ), QVariant( "" ) ).toString();
686  d->sizeType = static_cast< SizeType >( layer->customProperty( QStringLiteral( "labeling/shapeSizeType" ), QVariant( SizeBuffer ) ).toUInt() );
687  d->size = QSizeF( layer->customProperty( QStringLiteral( "labeling/shapeSizeX" ), QVariant( 0.0 ) ).toDouble(),
688  layer->customProperty( QStringLiteral( "labeling/shapeSizeY" ), QVariant( 0.0 ) ).toDouble() );
689 
690  if ( layer->customProperty( QStringLiteral( "labeling/shapeSizeUnit" ) ).toString().isEmpty() )
691  {
692  d->sizeUnits = convertFromOldLabelUnit( layer->customProperty( QStringLiteral( "labeling/shapeSizeUnits" ), 0 ).toUInt() );
693  }
694  else
695  {
696  d->sizeUnits = QgsUnitTypes::decodeRenderUnit( layer->customProperty( QStringLiteral( "labeling/shapeSizeUnit" ) ).toString() );
697  }
698 
699  if ( layer->customProperty( QStringLiteral( "labeling/shapeSizeMapUnitScale" ) ).toString().isEmpty() )
700  {
701  //fallback to older property
702  double oldMin = layer->customProperty( QStringLiteral( "labeling/shapeSizeMapUnitMinScale" ), 0.0 ).toDouble();
703  d->sizeMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
704  double oldMax = layer->customProperty( QStringLiteral( "labeling/shapeSizeMapUnitMaxScale" ), 0.0 ).toDouble();
705  d->sizeMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
706  }
707  else
708  {
709  d->sizeMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( layer->customProperty( QStringLiteral( "labeling/shapeSizeMapUnitScale" ) ).toString() );
710  }
711  d->rotationType = static_cast< RotationType >( layer->customProperty( QStringLiteral( "labeling/shapeRotationType" ), QVariant( RotationSync ) ).toUInt() );
712  d->rotation = layer->customProperty( QStringLiteral( "labeling/shapeRotation" ), QVariant( 0.0 ) ).toDouble();
713  d->offset = QPointF( layer->customProperty( QStringLiteral( "labeling/shapeOffsetX" ), QVariant( 0.0 ) ).toDouble(),
714  layer->customProperty( QStringLiteral( "labeling/shapeOffsetY" ), QVariant( 0.0 ) ).toDouble() );
715 
716  if ( layer->customProperty( QStringLiteral( "labeling/shapeOffsetUnit" ) ).toString().isEmpty() )
717  {
718  d->offsetUnits = convertFromOldLabelUnit( layer->customProperty( QStringLiteral( "labeling/shapeOffsetUnits" ), 0 ).toUInt() );
719  }
720  else
721  {
722  d->offsetUnits = QgsUnitTypes::decodeRenderUnit( layer->customProperty( QStringLiteral( "labeling/shapeOffsetUnit" ) ).toString() );
723  }
724 
725  if ( layer->customProperty( QStringLiteral( "labeling/shapeOffsetMapUnitScale" ) ).toString().isEmpty() )
726  {
727  //fallback to older property
728  double oldMin = layer->customProperty( QStringLiteral( "labeling/shapeOffsetMapUnitMinScale" ), 0.0 ).toDouble();
729  d->offsetMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
730  double oldMax = layer->customProperty( QStringLiteral( "labeling/shapeOffsetMapUnitMaxScale" ), 0.0 ).toDouble();
731  d->offsetMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
732  }
733  else
734  {
735  d->offsetMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( layer->customProperty( QStringLiteral( "labeling/shapeOffsetMapUnitScale" ) ).toString() );
736  }
737  d->radii = QSizeF( layer->customProperty( QStringLiteral( "labeling/shapeRadiiX" ), QVariant( 0.0 ) ).toDouble(),
738  layer->customProperty( QStringLiteral( "labeling/shapeRadiiY" ), QVariant( 0.0 ) ).toDouble() );
739 
740 
741  if ( layer->customProperty( QStringLiteral( "labeling/shapeRadiiUnit" ) ).toString().isEmpty() )
742  {
743  d->radiiUnits = convertFromOldLabelUnit( layer->customProperty( QStringLiteral( "labeling/shapeRadiiUnits" ), 0 ).toUInt() );
744  }
745  else
746  {
747  d->radiiUnits = QgsUnitTypes::decodeRenderUnit( layer->customProperty( QStringLiteral( "labeling/shapeRadiiUnit" ) ).toString() );
748  }
749 
750  if ( layer->customProperty( QStringLiteral( "labeling/shapeRadiiMapUnitScale" ) ).toString().isEmpty() )
751  {
752  //fallback to older property
753  double oldMin = layer->customProperty( QStringLiteral( "labeling/shapeRadiiMapUnitMinScale" ), 0.0 ).toDouble();
754  d->radiiMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
755  double oldMax = layer->customProperty( QStringLiteral( "labeling/shapeRadiiMapUnitMaxScale" ), 0.0 ).toDouble();
756  d->radiiMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
757  }
758  else
759  {
760  d->radiiMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( layer->customProperty( QStringLiteral( "labeling/shapeRadiiMapUnitScale" ) ).toString() );
761  }
762  d->fillColor = _readColor( layer, QStringLiteral( "labeling/shapeFillColor" ), Qt::white, true );
763  d->strokeColor = _readColor( layer, QStringLiteral( "labeling/shapeBorderColor" ), Qt::darkGray, true );
764  d->strokeWidth = layer->customProperty( QStringLiteral( "labeling/shapeBorderWidth" ), QVariant( .0 ) ).toDouble();
765  if ( layer->customProperty( QStringLiteral( "labeling/shapeBorderWidthUnit" ) ).toString().isEmpty() )
766  {
767  d->strokeWidthUnits = convertFromOldLabelUnit( layer->customProperty( QStringLiteral( "labeling/shapeBorderWidthUnits" ), 0 ).toUInt() );
768  }
769  else
770  {
771  d->strokeWidthUnits = QgsUnitTypes::decodeRenderUnit( layer->customProperty( QStringLiteral( "labeling/shapeBorderWidthUnit" ) ).toString() );
772  }
773  if ( layer->customProperty( QStringLiteral( "labeling/shapeBorderWidthMapUnitScale" ) ).toString().isEmpty() )
774  {
775  //fallback to older property
776  double oldMin = layer->customProperty( QStringLiteral( "labeling/shapeBorderWidthMapUnitMinScale" ), 0.0 ).toDouble();
777  d->strokeWidthMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
778  double oldMax = layer->customProperty( QStringLiteral( "labeling/shapeBorderWidthMapUnitMaxScale" ), 0.0 ).toDouble();
779  d->strokeWidthMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
780  }
781  else
782  {
783  d->strokeWidthMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( layer->customProperty( QStringLiteral( "labeling/shapeBorderWidthMapUnitScale" ) ).toString() );
784  }
785  d->joinStyle = static_cast< Qt::PenJoinStyle >( layer->customProperty( QStringLiteral( "labeling/shapeJoinStyle" ), QVariant( Qt::BevelJoin ) ).toUInt() );
786 
787  if ( layer->customProperty( QStringLiteral( "labeling/shapeOpacity" ) ).toString().isEmpty() )
788  {
789  d->opacity = ( 1 - layer->customProperty( QStringLiteral( "labeling/shapeTransparency" ) ).toInt() / 100.0 ); //0 -100
790  }
791  else
792  {
793  d->opacity = ( layer->customProperty( QStringLiteral( "labeling/shapeOpacity" ) ).toDouble() );
794  }
795  d->blendMode = QgsPainting::getCompositionMode(
796  static_cast< QgsPainting::BlendMode >( layer->customProperty( QStringLiteral( "labeling/shapeBlendMode" ), QVariant( QgsPainting::BlendNormal ) ).toUInt() ) );
797 
798  if ( layer->customProperty( QStringLiteral( "labeling/shapeEffect" ) ).isValid() )
799  {
800  QDomDocument doc( QStringLiteral( "effect" ) );
801  doc.setContent( layer->customProperty( QStringLiteral( "labeling/shapeEffect" ) ).toString() );
802  QDomElement effectElem = doc.firstChildElement( QStringLiteral( "effect" ) ).firstChildElement( QStringLiteral( "effect" ) );
803  setPaintEffect( QgsApplication::paintEffectRegistry()->createEffect( effectElem ) );
804  }
805  else
806  setPaintEffect( nullptr );
807 }
808 
809 void QgsTextBackgroundSettings::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
810 {
811  QDomElement backgroundElem = elem.firstChildElement( QStringLiteral( "background" ) );
812  d->enabled = backgroundElem.attribute( QStringLiteral( "shapeDraw" ), QStringLiteral( "0" ) ).toInt();
813  d->type = static_cast< ShapeType >( backgroundElem.attribute( QStringLiteral( "shapeType" ), QString::number( ShapeRectangle ) ).toUInt() );
814  d->svgFile = QgsSymbolLayerUtils::svgSymbolNameToPath( backgroundElem.attribute( QStringLiteral( "shapeSVGFile" ) ), context.pathResolver() );
815  d->sizeType = static_cast< SizeType >( backgroundElem.attribute( QStringLiteral( "shapeSizeType" ), QString::number( SizeBuffer ) ).toUInt() );
816  d->size = QSizeF( backgroundElem.attribute( QStringLiteral( "shapeSizeX" ), QStringLiteral( "0" ) ).toDouble(),
817  backgroundElem.attribute( QStringLiteral( "shapeSizeY" ), QStringLiteral( "0" ) ).toDouble() );
818 
819  if ( !backgroundElem.hasAttribute( QStringLiteral( "shapeSizeUnit" ) ) )
820  {
821  d->sizeUnits = convertFromOldLabelUnit( backgroundElem.attribute( QStringLiteral( "shapeSizeUnits" ) ).toUInt() );
822  }
823  else
824  {
825  d->sizeUnits = QgsUnitTypes::decodeRenderUnit( backgroundElem.attribute( QStringLiteral( "shapeSizeUnit" ) ) );
826  }
827 
828  if ( !backgroundElem.hasAttribute( QStringLiteral( "shapeSizeMapUnitScale" ) ) )
829  {
830  //fallback to older property
831  double oldMin = backgroundElem.attribute( QStringLiteral( "shapeSizeMapUnitMinScale" ), QStringLiteral( "0" ) ).toDouble();
832  d->sizeMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
833  double oldMax = backgroundElem.attribute( QStringLiteral( "shapeSizeMapUnitMaxScale" ), QStringLiteral( "0" ) ).toDouble();
834  d->sizeMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
835  }
836  else
837  {
838  d->sizeMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( backgroundElem.attribute( QStringLiteral( "shapeSizeMapUnitScale" ) ) );
839  }
840  d->rotationType = static_cast< RotationType >( backgroundElem.attribute( QStringLiteral( "shapeRotationType" ), QString::number( RotationSync ) ).toUInt() );
841  d->rotation = backgroundElem.attribute( QStringLiteral( "shapeRotation" ), QStringLiteral( "0" ) ).toDouble();
842  d->offset = QPointF( backgroundElem.attribute( QStringLiteral( "shapeOffsetX" ), QStringLiteral( "0" ) ).toDouble(),
843  backgroundElem.attribute( QStringLiteral( "shapeOffsetY" ), QStringLiteral( "0" ) ).toDouble() );
844 
845  if ( !backgroundElem.hasAttribute( QStringLiteral( "shapeOffsetUnit" ) ) )
846  {
847  d->offsetUnits = convertFromOldLabelUnit( backgroundElem.attribute( QStringLiteral( "shapeOffsetUnits" ) ).toUInt() );
848  }
849  else
850  {
851  d->offsetUnits = QgsUnitTypes::decodeRenderUnit( backgroundElem.attribute( QStringLiteral( "shapeOffsetUnit" ) ) );
852  }
853 
854  if ( !backgroundElem.hasAttribute( QStringLiteral( "shapeOffsetMapUnitScale" ) ) )
855  {
856  //fallback to older property
857  double oldMin = backgroundElem.attribute( QStringLiteral( "shapeOffsetMapUnitMinScale" ), QStringLiteral( "0" ) ).toDouble();
858  d->offsetMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
859  double oldMax = backgroundElem.attribute( QStringLiteral( "shapeOffsetMapUnitMaxScale" ), QStringLiteral( "0" ) ).toDouble();
860  d->offsetMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
861  }
862  else
863  {
864  d->offsetMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( backgroundElem.attribute( QStringLiteral( "shapeOffsetMapUnitScale" ) ) );
865  }
866  d->radii = QSizeF( backgroundElem.attribute( QStringLiteral( "shapeRadiiX" ), QStringLiteral( "0" ) ).toDouble(),
867  backgroundElem.attribute( QStringLiteral( "shapeRadiiY" ), QStringLiteral( "0" ) ).toDouble() );
868 
869  if ( !backgroundElem.hasAttribute( QStringLiteral( "shapeRadiiUnit" ) ) )
870  {
871  d->radiiUnits = convertFromOldLabelUnit( backgroundElem.attribute( QStringLiteral( "shapeRadiiUnits" ) ).toUInt() );
872  }
873  else
874  {
875  d->radiiUnits = QgsUnitTypes::decodeRenderUnit( backgroundElem.attribute( QStringLiteral( "shapeRadiiUnit" ) ) );
876  }
877  if ( !backgroundElem.hasAttribute( QStringLiteral( "shapeRadiiMapUnitScale" ) ) )
878  {
879  //fallback to older property
880  double oldMin = backgroundElem.attribute( QStringLiteral( "shapeRadiiMapUnitMinScale" ), QStringLiteral( "0" ) ).toDouble();
881  d->radiiMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
882  double oldMax = backgroundElem.attribute( QStringLiteral( "shapeRadiiMapUnitMaxScale" ), QStringLiteral( "0" ) ).toDouble();
883  d->radiiMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
884  }
885  else
886  {
887  d->radiiMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( backgroundElem.attribute( QStringLiteral( "shapeRadiiMapUnitScale" ) ) );
888  }
889  d->fillColor = QgsSymbolLayerUtils::decodeColor( backgroundElem.attribute( QStringLiteral( "shapeFillColor" ), QgsSymbolLayerUtils::encodeColor( Qt::white ) ) );
890  d->strokeColor = QgsSymbolLayerUtils::decodeColor( backgroundElem.attribute( QStringLiteral( "shapeBorderColor" ), QgsSymbolLayerUtils::encodeColor( Qt::darkGray ) ) );
891  d->strokeWidth = backgroundElem.attribute( QStringLiteral( "shapeBorderWidth" ), QStringLiteral( "0" ) ).toDouble();
892 
893  if ( !backgroundElem.hasAttribute( QStringLiteral( "shapeBorderWidthUnit" ) ) )
894  {
895  d->strokeWidthUnits = convertFromOldLabelUnit( backgroundElem.attribute( QStringLiteral( "shapeBorderWidthUnits" ) ).toUInt() );
896  }
897  else
898  {
899  d->strokeWidthUnits = QgsUnitTypes::decodeRenderUnit( backgroundElem.attribute( QStringLiteral( "shapeBorderWidthUnit" ) ) );
900  }
901  if ( !backgroundElem.hasAttribute( QStringLiteral( "shapeBorderWidthMapUnitScale" ) ) )
902  {
903  //fallback to older property
904  double oldMin = backgroundElem.attribute( QStringLiteral( "shapeBorderWidthMapUnitMinScale" ), QStringLiteral( "0" ) ).toDouble();
905  d->strokeWidthMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
906  double oldMax = backgroundElem.attribute( QStringLiteral( "shapeBorderWidthMapUnitMaxScale" ), QStringLiteral( "0" ) ).toDouble();
907  d->strokeWidthMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
908  }
909  else
910  {
911  d->strokeWidthMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( backgroundElem.attribute( QStringLiteral( "shapeBorderWidthMapUnitScale" ) ) );
912  }
913  d->joinStyle = static_cast< Qt::PenJoinStyle >( backgroundElem.attribute( QStringLiteral( "shapeJoinStyle" ), QString::number( Qt::BevelJoin ) ).toUInt() );
914 
915  if ( !backgroundElem.hasAttribute( QStringLiteral( "shapeOpacity" ) ) )
916  {
917  d->opacity = ( 1 - backgroundElem.attribute( QStringLiteral( "shapeTransparency" ) ).toInt() / 100.0 ); //0 -100
918  }
919  else
920  {
921  d->opacity = ( backgroundElem.attribute( QStringLiteral( "shapeOpacity" ) ).toDouble() );
922  }
923 
924  d->blendMode = QgsPainting::getCompositionMode(
925  static_cast< QgsPainting::BlendMode >( backgroundElem.attribute( QStringLiteral( "shapeBlendMode" ), QString::number( QgsPainting::BlendNormal ) ).toUInt() ) );
926 
927  QDomElement effectElem = backgroundElem.firstChildElement( QStringLiteral( "effect" ) );
928  if ( !effectElem.isNull() )
930  else
931  setPaintEffect( nullptr );
932 
933  const QDomElement symbolElem = backgroundElem.firstChildElement( QStringLiteral( "symbol" ) );
934  if ( !symbolElem.isNull() )
935  setMarkerSymbol( QgsSymbolLayerUtils::loadSymbol< QgsMarkerSymbol >( symbolElem, context ) );
936  else
937  setMarkerSymbol( nullptr );
938 }
939 
940 QDomElement QgsTextBackgroundSettings::writeXml( QDomDocument &doc, const QgsReadWriteContext &context ) const
941 {
942  QDomElement backgroundElem = doc.createElement( QStringLiteral( "background" ) );
943  backgroundElem.setAttribute( QStringLiteral( "shapeDraw" ), d->enabled );
944  backgroundElem.setAttribute( QStringLiteral( "shapeType" ), static_cast< unsigned int >( d->type ) );
945  backgroundElem.setAttribute( QStringLiteral( "shapeSVGFile" ), QgsSymbolLayerUtils::svgSymbolPathToName( d->svgFile, context.pathResolver() ) );
946  backgroundElem.setAttribute( QStringLiteral( "shapeSizeType" ), static_cast< unsigned int >( d->sizeType ) );
947  backgroundElem.setAttribute( QStringLiteral( "shapeSizeX" ), d->size.width() );
948  backgroundElem.setAttribute( QStringLiteral( "shapeSizeY" ), d->size.height() );
949  backgroundElem.setAttribute( QStringLiteral( "shapeSizeUnit" ), QgsUnitTypes::encodeUnit( d->sizeUnits ) );
950  backgroundElem.setAttribute( QStringLiteral( "shapeSizeMapUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( d->sizeMapUnitScale ) );
951  backgroundElem.setAttribute( QStringLiteral( "shapeRotationType" ), static_cast< unsigned int >( d->rotationType ) );
952  backgroundElem.setAttribute( QStringLiteral( "shapeRotation" ), d->rotation );
953  backgroundElem.setAttribute( QStringLiteral( "shapeOffsetX" ), d->offset.x() );
954  backgroundElem.setAttribute( QStringLiteral( "shapeOffsetY" ), d->offset.y() );
955  backgroundElem.setAttribute( QStringLiteral( "shapeOffsetUnit" ), QgsUnitTypes::encodeUnit( d->offsetUnits ) );
956  backgroundElem.setAttribute( QStringLiteral( "shapeOffsetMapUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( d->offsetMapUnitScale ) );
957  backgroundElem.setAttribute( QStringLiteral( "shapeRadiiX" ), d->radii.width() );
958  backgroundElem.setAttribute( QStringLiteral( "shapeRadiiY" ), d->radii.height() );
959  backgroundElem.setAttribute( QStringLiteral( "shapeRadiiUnit" ), QgsUnitTypes::encodeUnit( d->radiiUnits ) );
960  backgroundElem.setAttribute( QStringLiteral( "shapeRadiiMapUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( d->radiiMapUnitScale ) );
961  backgroundElem.setAttribute( QStringLiteral( "shapeFillColor" ), QgsSymbolLayerUtils::encodeColor( d->fillColor ) );
962  backgroundElem.setAttribute( QStringLiteral( "shapeBorderColor" ), QgsSymbolLayerUtils::encodeColor( d->strokeColor ) );
963  backgroundElem.setAttribute( QStringLiteral( "shapeBorderWidth" ), d->strokeWidth );
964  backgroundElem.setAttribute( QStringLiteral( "shapeBorderWidthUnit" ), QgsUnitTypes::encodeUnit( d->strokeWidthUnits ) );
965  backgroundElem.setAttribute( QStringLiteral( "shapeBorderWidthMapUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( d->strokeWidthMapUnitScale ) );
966  backgroundElem.setAttribute( QStringLiteral( "shapeJoinStyle" ), static_cast< unsigned int >( d->joinStyle ) );
967  backgroundElem.setAttribute( QStringLiteral( "shapeOpacity" ), d->opacity );
968  backgroundElem.setAttribute( QStringLiteral( "shapeBlendMode" ), QgsPainting::getBlendModeEnum( d->blendMode ) );
969  if ( d->paintEffect && !QgsPaintEffectRegistry::isDefaultStack( d->paintEffect.get() ) )
970  d->paintEffect->saveProperties( doc, backgroundElem );
971 
972  if ( d->markerSymbol )
973  backgroundElem.appendChild( QgsSymbolLayerUtils::saveSymbol( QStringLiteral( "markerSymbol" ), d->markerSymbol.get(), doc, context ) );
974 
975  return backgroundElem;
976 }
977 
979 {
980  if ( properties.isActive( QgsPalLayerSettings::ShapeDraw ) )
981  {
982  context.expressionContext().setOriginalValueVariable( d->enabled );
983  d->enabled = properties.valueAsBool( QgsPalLayerSettings::ShapeDraw, context.expressionContext(), d->enabled );
984  }
985 
986  if ( properties.isActive( QgsPalLayerSettings::ShapeSizeX ) )
987  {
988  context.expressionContext().setOriginalValueVariable( d->size.width() );
989  d->size.setWidth( properties.valueAsDouble( QgsPalLayerSettings::ShapeSizeX, context.expressionContext(), d->size.width() ) );
990  }
991  if ( properties.isActive( QgsPalLayerSettings::ShapeSizeY ) )
992  {
993  context.expressionContext().setOriginalValueVariable( d->size.height() );
994  d->size.setHeight( properties.valueAsDouble( QgsPalLayerSettings::ShapeSizeY, context.expressionContext(), d->size.height() ) );
995  }
996 
997  QVariant exprVal = properties.value( QgsPalLayerSettings::ShapeSizeUnits, context.expressionContext() );
998  if ( exprVal.isValid() )
999  {
1000  QString units = exprVal.toString();
1001  if ( !units.isEmpty() )
1002  {
1003  bool ok;
1005  if ( ok )
1006  d->sizeUnits = res;
1007  }
1008  }
1009 
1010  exprVal = properties.value( QgsPalLayerSettings::ShapeKind, context.expressionContext() );
1011  if ( exprVal.isValid() )
1012  {
1013  const QString skind = exprVal.toString().trimmed();
1014  if ( !skind.isEmpty() )
1015  {
1016  d->type = QgsTextRendererUtils::decodeShapeType( skind );
1017  }
1018  }
1019 
1020  exprVal = properties.value( QgsPalLayerSettings::ShapeSizeType, context.expressionContext() );
1021  if ( exprVal.isValid() )
1022  {
1023  QString stype = exprVal.toString().trimmed();
1024  if ( !stype.isEmpty() )
1025  {
1026  d->sizeType = QgsTextRendererUtils::decodeBackgroundSizeType( stype );
1027  }
1028  }
1029 
1030  // data defined shape SVG path?
1031  context.expressionContext().setOriginalValueVariable( d->svgFile );
1032  exprVal = properties.value( QgsPalLayerSettings::ShapeSVGFile, context.expressionContext() );
1033  if ( exprVal.isValid() )
1034  {
1035  QString svgfile = exprVal.toString().trimmed();
1036  d->svgFile = QgsSymbolLayerUtils::svgSymbolNameToPath( svgfile, context.pathResolver() );
1037  }
1038 
1039  if ( properties.isActive( QgsPalLayerSettings::ShapeRotation ) )
1040  {
1041  context.expressionContext().setOriginalValueVariable( d->rotation );
1042  d->rotation = properties.valueAsDouble( QgsPalLayerSettings::ShapeRotation, context.expressionContext(), d->rotation );
1043  }
1044  exprVal = properties.value( QgsPalLayerSettings::ShapeRotationType, context.expressionContext() );
1045  if ( exprVal.isValid() )
1046  {
1047  QString rotstr = exprVal.toString().trimmed();
1048  if ( !rotstr.isEmpty() )
1049  {
1050  d->rotationType = QgsTextRendererUtils::decodeBackgroundRotationType( rotstr );
1051  }
1052  }
1053 
1054  exprVal = properties.value( QgsPalLayerSettings::ShapeOffset, context.expressionContext() );
1055  if ( exprVal.isValid() )
1056  {
1057  bool ok = false;
1058  const QPointF res = QgsSymbolLayerUtils::toPoint( exprVal, &ok );
1059  if ( ok )
1060  {
1061  d->offset = res;
1062  }
1063  }
1064  exprVal = properties.value( QgsPalLayerSettings::ShapeOffsetUnits, context.expressionContext() );
1065  if ( exprVal.isValid() )
1066  {
1067  QString units = exprVal.toString();
1068  if ( !units.isEmpty() )
1069  {
1070  bool ok;
1072  if ( ok )
1073  d->offsetUnits = res;
1074  }
1075  }
1076 
1077  exprVal = properties.value( QgsPalLayerSettings::ShapeRadii, context.expressionContext() );
1078  if ( exprVal.isValid() )
1079  {
1080  bool ok = false;
1081  const QSizeF res = QgsSymbolLayerUtils::toSize( exprVal, &ok );
1082  if ( ok )
1083  {
1084  d->radii = res;
1085  }
1086  }
1087 
1088  exprVal = properties.value( QgsPalLayerSettings::ShapeRadiiUnits, context.expressionContext() );
1089  if ( exprVal.isValid() )
1090  {
1091  QString units = exprVal.toString();
1092  if ( !units.isEmpty() )
1093  {
1094  bool ok;
1096  if ( ok )
1097  d->radiiUnits = res;
1098  }
1099  }
1100 
1101  if ( properties.isActive( QgsPalLayerSettings::ShapeOpacity ) )
1102  {
1103  context.expressionContext().setOriginalValueVariable( d->opacity * 100 );
1104  d->opacity = properties.value( QgsPalLayerSettings::ShapeOpacity, context.expressionContext(), d->opacity * 100 ).toDouble() / 100.0;
1105  }
1106 
1107  if ( properties.isActive( QgsPalLayerSettings::ShapeFillColor ) )
1108  {
1110  d->fillColor = properties.valueAsColor( QgsPalLayerSettings::ShapeFillColor, context.expressionContext(), d->fillColor );
1111  }
1112  if ( properties.isActive( QgsPalLayerSettings::ShapeStrokeColor ) )
1113  {
1115  d->strokeColor = properties.valueAsColor( QgsPalLayerSettings::ShapeStrokeColor, context.expressionContext(), d->strokeColor );
1116  }
1117 
1118  if ( properties.isActive( QgsPalLayerSettings::ShapeStrokeWidth ) )
1119  {
1120  context.expressionContext().setOriginalValueVariable( d->strokeWidth );
1121  d->strokeWidth = properties.valueAsDouble( QgsPalLayerSettings::ShapeStrokeWidth, context.expressionContext(), d->strokeWidth );
1122  }
1123  exprVal = properties.value( QgsPalLayerSettings::ShapeStrokeWidthUnits, context.expressionContext() );
1124  if ( exprVal.isValid() )
1125  {
1126  QString units = exprVal.toString();
1127  if ( !units.isEmpty() )
1128  {
1129  bool ok;
1131  if ( ok )
1132  d->strokeWidthUnits = res;
1133  }
1134  }
1135 
1136  if ( properties.isActive( QgsPalLayerSettings::ShapeBlendMode ) )
1137  {
1138  exprVal = properties.value( QgsPalLayerSettings::ShapeBlendMode, context.expressionContext() );
1139  QString blendstr = exprVal.toString().trimmed();
1140  if ( !blendstr.isEmpty() )
1141  d->blendMode = QgsSymbolLayerUtils::decodeBlendMode( blendstr );
1142  }
1143 
1144  if ( properties.isActive( QgsPalLayerSettings::ShapeJoinStyle ) )
1145  {
1146  exprVal = properties.value( QgsPalLayerSettings::ShapeJoinStyle, context.expressionContext() );
1147  QString joinstr = exprVal.toString().trimmed();
1148  if ( !joinstr.isEmpty() )
1149  {
1150  d->joinStyle = QgsSymbolLayerUtils::decodePenJoinStyle( joinstr );
1151  }
1152  }
1153 }
1154 
1155 
1156 //
1157 // QgsTextShadowSettings
1158 //
1159 
1161 {
1162  d = new QgsTextShadowSettingsPrivate();
1163 }
1164 
1166  : d( other.d )
1167 {
1168 
1169 }
1170 
1172 {
1173  d = other.d;
1174  return *this;
1175 }
1176 
1178 {
1179 
1180 }
1181 
1183 {
1184  return d->enabled;
1185 }
1186 
1188 {
1189  d->enabled = enabled;
1190 }
1191 
1193 {
1194  return d->shadowUnder;
1195 }
1196 
1198 {
1199  d->shadowUnder = placement;
1200 }
1201 
1203 {
1204  return d->offsetAngle;
1205 }
1206 
1208 {
1209  d->offsetAngle = angle;
1210 }
1211 
1213 {
1214  return d->offsetDist;
1215 }
1216 
1218 {
1219  d->offsetDist = distance;
1220 }
1221 
1223 {
1224  return d->offsetUnits;
1225 }
1226 
1228 {
1229  d->offsetUnits = units;
1230 }
1231 
1233 {
1234  return d->offsetMapUnitScale;
1235 }
1236 
1238 {
1239  d->offsetMapUnitScale = scale;
1240 }
1241 
1243 {
1244  return d->offsetGlobal;
1245 }
1246 
1248 {
1249  d->offsetGlobal = global;
1250 }
1251 
1253 {
1254  return d->radius;
1255 }
1256 
1258 {
1259  d->radius = radius;
1260 }
1261 
1263 {
1264  return d->radiusUnits;
1265 }
1266 
1268 {
1269  d->radiusUnits = units;
1270 }
1271 
1273 {
1274  return d->radiusMapUnitScale;
1275 }
1276 
1278 {
1279  d->radiusMapUnitScale = scale;
1280 }
1281 
1283 {
1284  return d->radiusAlphaOnly;
1285 }
1286 
1288 {
1289  d->radiusAlphaOnly = alphaOnly;
1290 }
1291 
1293 {
1294  return d->opacity;
1295 }
1296 
1298 {
1299  d->opacity = opacity;
1300 }
1301 
1303 {
1304  return d->scale;
1305 }
1306 
1308 {
1309  d->scale = scale;
1310 }
1311 
1313 {
1314  return d->color;
1315 }
1316 
1318 {
1319  d->color = color;
1320 }
1321 
1322 QPainter::CompositionMode QgsTextShadowSettings::blendMode() const
1323 {
1324  return d->blendMode;
1325 }
1326 
1327 void QgsTextShadowSettings::setBlendMode( QPainter::CompositionMode mode )
1328 {
1329  d->blendMode = mode;
1330 }
1331 
1333 {
1334  d->enabled = layer->customProperty( QStringLiteral( "labeling/shadowDraw" ), QVariant( false ) ).toBool();
1335  d->shadowUnder = static_cast< ShadowPlacement >( layer->customProperty( QStringLiteral( "labeling/shadowUnder" ), QVariant( ShadowLowest ) ).toUInt() );//ShadowLowest;
1336  d->offsetAngle = layer->customProperty( QStringLiteral( "labeling/shadowOffsetAngle" ), QVariant( 135 ) ).toInt();
1337  d->offsetDist = layer->customProperty( QStringLiteral( "labeling/shadowOffsetDist" ), QVariant( 1.0 ) ).toDouble();
1338 
1339  if ( layer->customProperty( QStringLiteral( "labeling/shadowOffsetUnit" ) ).toString().isEmpty() )
1340  {
1341  d->offsetUnits = convertFromOldLabelUnit( layer->customProperty( QStringLiteral( "labeling/shadowOffsetUnits" ), 0 ).toUInt() );
1342  }
1343  else
1344  {
1345  d->offsetUnits = QgsUnitTypes::decodeRenderUnit( layer->customProperty( QStringLiteral( "labeling/shadowOffsetUnit" ) ).toString() );
1346  }
1347  if ( layer->customProperty( QStringLiteral( "labeling/shadowOffsetMapUnitScale" ) ).toString().isEmpty() )
1348  {
1349  //fallback to older property
1350  double oldMin = layer->customProperty( QStringLiteral( "labeling/shadowOffsetMapUnitMinScale" ), 0.0 ).toDouble();
1351  d->offsetMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
1352  double oldMax = layer->customProperty( QStringLiteral( "labeling/shadowOffsetMapUnitMaxScale" ), 0.0 ).toDouble();
1353  d->offsetMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
1354  }
1355  else
1356  {
1357  d->offsetMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( layer->customProperty( QStringLiteral( "labeling/shadowOffsetMapUnitScale" ) ).toString() );
1358  }
1359  d->offsetGlobal = layer->customProperty( QStringLiteral( "labeling/shadowOffsetGlobal" ), QVariant( true ) ).toBool();
1360  d->radius = layer->customProperty( QStringLiteral( "labeling/shadowRadius" ), QVariant( 1.5 ) ).toDouble();
1361 
1362  if ( layer->customProperty( QStringLiteral( "labeling/shadowRadiusUnit" ) ).toString().isEmpty() )
1363  {
1364  d->radiusUnits = convertFromOldLabelUnit( layer->customProperty( QStringLiteral( "labeling/shadowRadiusUnits" ), 0 ).toUInt() );
1365  }
1366  else
1367  {
1368  d->radiusUnits = QgsUnitTypes::decodeRenderUnit( layer->customProperty( QStringLiteral( "labeling/shadowRadiusUnit" ) ).toString() );
1369  }
1370  if ( layer->customProperty( QStringLiteral( "labeling/shadowRadiusMapUnitScale" ) ).toString().isEmpty() )
1371  {
1372  //fallback to older property
1373  double oldMin = layer->customProperty( QStringLiteral( "labeling/shadowRadiusMapUnitMinScale" ), 0.0 ).toDouble();
1374  d->radiusMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
1375  double oldMax = layer->customProperty( QStringLiteral( "labeling/shadowRadiusMapUnitMaxScale" ), 0.0 ).toDouble();
1376  d->radiusMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
1377  }
1378  else
1379  {
1380  d->radiusMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( layer->customProperty( QStringLiteral( "labeling/shadowRadiusMapUnitScale" ) ).toString() );
1381  }
1382  d->radiusAlphaOnly = layer->customProperty( QStringLiteral( "labeling/shadowRadiusAlphaOnly" ), QVariant( false ) ).toBool();
1383 
1384  if ( layer->customProperty( QStringLiteral( "labeling/shadowOpacity" ) ).toString().isEmpty() )
1385  {
1386  d->opacity = ( 1 - layer->customProperty( QStringLiteral( "labeling/shadowTransparency" ) ).toInt() / 100.0 ); //0 -100
1387  }
1388  else
1389  {
1390  d->opacity = ( layer->customProperty( QStringLiteral( "labeling/shadowOpacity" ) ).toDouble() );
1391  }
1392  d->scale = layer->customProperty( QStringLiteral( "labeling/shadowScale" ), QVariant( 100 ) ).toInt();
1393  d->color = _readColor( layer, QStringLiteral( "labeling/shadowColor" ), Qt::black, false );
1394  d->blendMode = QgsPainting::getCompositionMode(
1395  static_cast< QgsPainting::BlendMode >( layer->customProperty( QStringLiteral( "labeling/shadowBlendMode" ), QVariant( QgsPainting::BlendMultiply ) ).toUInt() ) );
1396 }
1397 
1398 void QgsTextShadowSettings::readXml( const QDomElement &elem )
1399 {
1400  QDomElement shadowElem = elem.firstChildElement( QStringLiteral( "shadow" ) );
1401  d->enabled = shadowElem.attribute( QStringLiteral( "shadowDraw" ), QStringLiteral( "0" ) ).toInt();
1402  d->shadowUnder = static_cast< ShadowPlacement >( shadowElem.attribute( QStringLiteral( "shadowUnder" ), QString::number( ShadowLowest ) ).toUInt() );//ShadowLowest;
1403  d->offsetAngle = shadowElem.attribute( QStringLiteral( "shadowOffsetAngle" ), QStringLiteral( "135" ) ).toInt();
1404  d->offsetDist = shadowElem.attribute( QStringLiteral( "shadowOffsetDist" ), QStringLiteral( "1" ) ).toDouble();
1405 
1406  if ( !shadowElem.hasAttribute( QStringLiteral( "shadowOffsetUnit" ) ) )
1407  {
1408  d->offsetUnits = convertFromOldLabelUnit( shadowElem.attribute( QStringLiteral( "shadowOffsetUnits" ) ).toUInt() );
1409  }
1410  else
1411  {
1412  d->offsetUnits = QgsUnitTypes::decodeRenderUnit( shadowElem.attribute( QStringLiteral( "shadowOffsetUnit" ) ) );
1413  }
1414 
1415  if ( !shadowElem.hasAttribute( QStringLiteral( "shadowOffsetMapUnitScale" ) ) )
1416  {
1417  //fallback to older property
1418  double oldMin = shadowElem.attribute( QStringLiteral( "shadowOffsetMapUnitMinScale" ), QStringLiteral( "0" ) ).toDouble();
1419  d->offsetMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
1420  double oldMax = shadowElem.attribute( QStringLiteral( "shadowOffsetMapUnitMaxScale" ), QStringLiteral( "0" ) ).toDouble();
1421  d->offsetMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
1422  }
1423  else
1424  {
1425  d->offsetMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( shadowElem.attribute( QStringLiteral( "shadowOffsetMapUnitScale" ) ) );
1426  }
1427  d->offsetGlobal = shadowElem.attribute( QStringLiteral( "shadowOffsetGlobal" ), QStringLiteral( "1" ) ).toInt();
1428  d->radius = shadowElem.attribute( QStringLiteral( "shadowRadius" ), QStringLiteral( "1.5" ) ).toDouble();
1429 
1430  if ( !shadowElem.hasAttribute( QStringLiteral( "shadowRadiusUnit" ) ) )
1431  {
1432  d->radiusUnits = convertFromOldLabelUnit( shadowElem.attribute( QStringLiteral( "shadowRadiusUnits" ) ).toUInt() );
1433  }
1434  else
1435  {
1436  d->radiusUnits = QgsUnitTypes::decodeRenderUnit( shadowElem.attribute( QStringLiteral( "shadowRadiusUnit" ) ) );
1437  }
1438  if ( !shadowElem.hasAttribute( QStringLiteral( "shadowRadiusMapUnitScale" ) ) )
1439  {
1440  //fallback to older property
1441  double oldMin = shadowElem.attribute( QStringLiteral( "shadowRadiusMapUnitMinScale" ), QStringLiteral( "0" ) ).toDouble();
1442  d->radiusMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
1443  double oldMax = shadowElem.attribute( QStringLiteral( "shadowRadiusMapUnitMaxScale" ), QStringLiteral( "0" ) ).toDouble();
1444  d->radiusMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
1445  }
1446  else
1447  {
1448  d->radiusMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( shadowElem.attribute( QStringLiteral( "shadowRadiusMapUnitScale" ) ) );
1449  }
1450  d->radiusAlphaOnly = shadowElem.attribute( QStringLiteral( "shadowRadiusAlphaOnly" ), QStringLiteral( "0" ) ).toInt();
1451 
1452  if ( !shadowElem.hasAttribute( QStringLiteral( "shadowOpacity" ) ) )
1453  {
1454  d->opacity = ( 1 - shadowElem.attribute( QStringLiteral( "shadowTransparency" ) ).toInt() / 100.0 ); //0 -100
1455  }
1456  else
1457  {
1458  d->opacity = ( shadowElem.attribute( QStringLiteral( "shadowOpacity" ) ).toDouble() );
1459  }
1460  d->scale = shadowElem.attribute( QStringLiteral( "shadowScale" ), QStringLiteral( "100" ) ).toInt();
1461  d->color = QgsSymbolLayerUtils::decodeColor( shadowElem.attribute( QStringLiteral( "shadowColor" ), QgsSymbolLayerUtils::encodeColor( Qt::black ) ) );
1462  d->blendMode = QgsPainting::getCompositionMode(
1463  static_cast< QgsPainting::BlendMode >( shadowElem.attribute( QStringLiteral( "shadowBlendMode" ), QString::number( QgsPainting::BlendMultiply ) ).toUInt() ) );
1464 }
1465 
1466 QDomElement QgsTextShadowSettings::writeXml( QDomDocument &doc ) const
1467 {
1468  QDomElement shadowElem = doc.createElement( QStringLiteral( "shadow" ) );
1469  shadowElem.setAttribute( QStringLiteral( "shadowDraw" ), d->enabled );
1470  shadowElem.setAttribute( QStringLiteral( "shadowUnder" ), static_cast< unsigned int >( d->shadowUnder ) );
1471  shadowElem.setAttribute( QStringLiteral( "shadowOffsetAngle" ), d->offsetAngle );
1472  shadowElem.setAttribute( QStringLiteral( "shadowOffsetDist" ), d->offsetDist );
1473  shadowElem.setAttribute( QStringLiteral( "shadowOffsetUnit" ), QgsUnitTypes::encodeUnit( d->offsetUnits ) );
1474  shadowElem.setAttribute( QStringLiteral( "shadowOffsetMapUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( d->offsetMapUnitScale ) );
1475  shadowElem.setAttribute( QStringLiteral( "shadowOffsetGlobal" ), d->offsetGlobal );
1476  shadowElem.setAttribute( QStringLiteral( "shadowRadius" ), d->radius );
1477  shadowElem.setAttribute( QStringLiteral( "shadowRadiusUnit" ), QgsUnitTypes::encodeUnit( d->radiusUnits ) );
1478  shadowElem.setAttribute( QStringLiteral( "shadowRadiusMapUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( d->radiusMapUnitScale ) );
1479  shadowElem.setAttribute( QStringLiteral( "shadowRadiusAlphaOnly" ), d->radiusAlphaOnly );
1480  shadowElem.setAttribute( QStringLiteral( "shadowOpacity" ), d->opacity );
1481  shadowElem.setAttribute( QStringLiteral( "shadowScale" ), d->scale );
1482  shadowElem.setAttribute( QStringLiteral( "shadowColor" ), QgsSymbolLayerUtils::encodeColor( d->color ) );
1483  shadowElem.setAttribute( QStringLiteral( "shadowBlendMode" ), QgsPainting::getBlendModeEnum( d->blendMode ) );
1484  return shadowElem;
1485 }
1486 
1488 {
1489  if ( properties.isActive( QgsPalLayerSettings::ShadowDraw ) )
1490  {
1491  context.expressionContext().setOriginalValueVariable( d->enabled );
1492  d->enabled = properties.valueAsBool( QgsPalLayerSettings::ShadowDraw, context.expressionContext(), d->enabled );
1493  }
1494 
1495  // data defined shadow under type?
1496  QVariant exprVal = properties.value( QgsPalLayerSettings::ShadowUnder, context.expressionContext() );
1497  if ( exprVal.isValid() )
1498  {
1499  QString str = exprVal.toString().trimmed();
1500  if ( !str.isEmpty() )
1501  {
1502  d->shadowUnder = QgsTextRendererUtils::decodeShadowPlacementType( str );
1503  }
1504  }
1505 
1507  {
1508  context.expressionContext().setOriginalValueVariable( d->offsetAngle );
1509  d->offsetAngle = properties.valueAsInt( QgsPalLayerSettings::ShadowOffsetAngle, context.expressionContext(), d->offsetAngle );
1510  }
1511  if ( properties.isActive( QgsPalLayerSettings::ShadowOffsetDist ) )
1512  {
1513  context.expressionContext().setOriginalValueVariable( d->offsetDist );
1514  d->offsetDist = properties.valueAsDouble( QgsPalLayerSettings::ShadowOffsetDist, context.expressionContext(), d->offsetDist );
1515  }
1516 
1517  exprVal = properties.value( QgsPalLayerSettings::ShadowOffsetUnits, context.expressionContext() );
1518  if ( exprVal.isValid() )
1519  {
1520  QString units = exprVal.toString();
1521  if ( !units.isEmpty() )
1522  {
1523  bool ok;
1525  if ( ok )
1526  d->offsetUnits = res;
1527  }
1528  }
1529 
1530  if ( properties.isActive( QgsPalLayerSettings::ShadowRadius ) )
1531  {
1532  context.expressionContext().setOriginalValueVariable( d->radius );
1533  d->radius = properties.valueAsDouble( QgsPalLayerSettings::ShadowRadius, context.expressionContext(), d->radius );
1534  }
1535 
1536  exprVal = properties.value( QgsPalLayerSettings::ShadowRadiusUnits, context.expressionContext() );
1537  if ( exprVal.isValid() )
1538  {
1539  QString units = exprVal.toString();
1540  if ( !units.isEmpty() )
1541  {
1542  bool ok;
1544  if ( ok )
1545  d->radiusUnits = res;
1546  }
1547  }
1548 
1549  if ( properties.isActive( QgsPalLayerSettings::ShadowOpacity ) )
1550  {
1551  context.expressionContext().setOriginalValueVariable( d->opacity * 100 );
1552  d->opacity = properties.value( QgsPalLayerSettings::ShadowOpacity, context.expressionContext(), d->opacity * 100 ).toDouble() / 100.0;
1553  }
1554 
1555  if ( properties.isActive( QgsPalLayerSettings::ShadowScale ) )
1556  {
1557  context.expressionContext().setOriginalValueVariable( d->scale );
1558  d->scale = properties.valueAsInt( QgsPalLayerSettings::ShadowScale, context.expressionContext(), d->scale );
1559  }
1560 
1561  if ( properties.isActive( QgsPalLayerSettings::ShadowColor ) )
1562  {
1564  d->color = properties.valueAsColor( QgsPalLayerSettings::ShadowColor, context.expressionContext(), d->color );
1565  }
1566 
1567  if ( properties.isActive( QgsPalLayerSettings::ShadowBlendMode ) )
1568  {
1569  exprVal = properties.value( QgsPalLayerSettings::ShadowBlendMode, context.expressionContext() );
1570  QString blendstr = exprVal.toString().trimmed();
1571  if ( !blendstr.isEmpty() )
1572  d->blendMode = QgsSymbolLayerUtils::decodeBlendMode( blendstr );
1573  }
1574 }
1575 
1576 //
1577 // QgsTextMaskSettings
1578 //
1579 
1581 {
1582  d = new QgsTextMaskSettingsPrivate();
1583 }
1584 
1586 
1588  : d( other.d )
1589 {
1590 }
1591 
1593 {
1594  d = other.d;
1595  return *this;
1596 }
1597 
1599 {
1600  return d->enabled;
1601 }
1602 
1604 {
1605  d->enabled = enabled;
1606 }
1607 
1609 {
1610  return d->type;
1611 }
1612 
1614 {
1615  d->type = type;
1616 }
1617 
1618 
1620 {
1621  return d->size;
1622 }
1623 
1625 {
1626  d->size = size;
1627 }
1628 
1630 {
1631  return d->sizeUnit;
1632 }
1633 
1635 {
1636  d->sizeUnit = unit;
1637 }
1638 
1640 {
1641  return d->sizeMapUnitScale;
1642 }
1643 
1645 {
1646  d->sizeMapUnitScale = scale;
1647 }
1648 
1649 Qt::PenJoinStyle QgsTextMaskSettings::joinStyle() const
1650 {
1651  return d->joinStyle;
1652 }
1653 
1654 void QgsTextMaskSettings::setJoinStyle( Qt::PenJoinStyle style )
1655 {
1656  d->joinStyle = style;
1657 }
1658 
1660 {
1661  return d->opacity;
1662 }
1663 
1665 {
1666  d->opacity = opacity;
1667 }
1668 
1670 {
1671  return d->paintEffect.get();
1672 }
1673 
1675 {
1676  d->paintEffect.reset( effect );
1677 }
1678 
1680 {
1681  if ( properties.isActive( QgsPalLayerSettings::MaskEnabled ) )
1682  {
1683  context.expressionContext().setOriginalValueVariable( d->enabled );
1684  d->enabled = properties.valueAsBool( QgsPalLayerSettings::MaskEnabled, context.expressionContext(), d->enabled );
1685  }
1686 
1687  if ( properties.isActive( QgsPalLayerSettings::MaskBufferSize ) )
1688  {
1689  context.expressionContext().setOriginalValueVariable( d->size );
1690  d->size = properties.valueAsDouble( QgsPalLayerSettings::MaskBufferSize, context.expressionContext(), d->size );
1691  }
1692 
1693  if ( properties.isActive( QgsPalLayerSettings::MaskBufferUnit ) )
1694  {
1695  QVariant exprVal = properties.value( QgsPalLayerSettings::MaskBufferUnit, context.expressionContext() );
1696  if ( exprVal.isValid() )
1697  {
1698  QString units = exprVal.toString();
1699  if ( !units.isEmpty() )
1700  {
1701  bool ok;
1703  if ( ok )
1704  d->sizeUnit = res;
1705  }
1706  }
1707  }
1708 
1709  if ( properties.isActive( QgsPalLayerSettings::MaskOpacity ) )
1710  {
1711  context.expressionContext().setOriginalValueVariable( d->opacity * 100 );
1712  d->opacity = properties.value( QgsPalLayerSettings::MaskOpacity, context.expressionContext(), d->opacity * 100 ).toDouble() / 100.0;
1713  }
1714 
1715  if ( properties.isActive( QgsPalLayerSettings::MaskJoinStyle ) )
1716  {
1717  QVariant exprVal = properties.value( QgsPalLayerSettings::MaskJoinStyle, context.expressionContext() );
1718  QString joinstr = exprVal.toString().trimmed();
1719  if ( !joinstr.isEmpty() )
1720  {
1721  d->joinStyle = QgsSymbolLayerUtils::decodePenJoinStyle( joinstr );
1722  }
1723  }
1724 }
1725 
1726 void QgsTextMaskSettings::readXml( const QDomElement &elem )
1727 {
1728  QDomElement textMaskElem = elem.firstChildElement( QStringLiteral( "text-mask" ) );
1729  d->enabled = textMaskElem.attribute( QStringLiteral( "maskEnabled" ), QStringLiteral( "0" ) ).toInt();
1730  d->type = static_cast<QgsTextMaskSettings::MaskType>( textMaskElem.attribute( QStringLiteral( "maskType" ), QStringLiteral( "0" ) ).toInt() );
1731  d->size = textMaskElem.attribute( QStringLiteral( "maskSize" ), QStringLiteral( "0" ) ).toDouble();
1732  d->sizeUnit = QgsUnitTypes::decodeRenderUnit( textMaskElem.attribute( QStringLiteral( "maskSizeUnits" ) ) );
1733  d->sizeMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( textMaskElem.attribute( QStringLiteral( "maskSizeMapUnitScale" ) ) );
1734  d->joinStyle = static_cast< Qt::PenJoinStyle >( textMaskElem.attribute( QStringLiteral( "maskJoinStyle" ), QString::number( Qt::RoundJoin ) ).toUInt() );
1735  d->opacity = textMaskElem.attribute( QStringLiteral( "maskOpacity" ), QStringLiteral( "1.0" ) ).toDouble();
1736  QDomElement effectElem = textMaskElem.firstChildElement( QStringLiteral( "effect" ) );
1737  if ( !effectElem.isNull() )
1739  else
1740  setPaintEffect( nullptr );
1741  d->maskedSymbolLayers = stringToSymbolLayerReferenceList( textMaskElem.attribute( QStringLiteral( "maskedSymbolLayers" ) ) );
1742 }
1743 
1744 QDomElement QgsTextMaskSettings::writeXml( QDomDocument &doc ) const
1745 {
1746  QDomElement textMaskElem = doc.createElement( QStringLiteral( "text-mask" ) );
1747  textMaskElem.setAttribute( QStringLiteral( "maskEnabled" ), d->enabled );
1748  textMaskElem.setAttribute( QStringLiteral( "maskType" ), d->type );
1749  textMaskElem.setAttribute( QStringLiteral( "maskSize" ), d->size );
1750  textMaskElem.setAttribute( QStringLiteral( "maskSizeUnits" ), QgsUnitTypes::encodeUnit( d->sizeUnit ) );
1751  textMaskElem.setAttribute( QStringLiteral( "maskSizeMapUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( d->sizeMapUnitScale ) );
1752  textMaskElem.setAttribute( QStringLiteral( "maskJoinStyle" ), static_cast< unsigned int >( d->joinStyle ) );
1753  textMaskElem.setAttribute( QStringLiteral( "maskOpacity" ), d->opacity );
1754  if ( d->paintEffect && !QgsPaintEffectRegistry::isDefaultStack( d->paintEffect.get() ) )
1755  d->paintEffect->saveProperties( doc, textMaskElem );
1756  textMaskElem.setAttribute( QStringLiteral( "maskedSymbolLayers" ), symbolLayerReferenceListToString( d->maskedSymbolLayers ) );
1757  return textMaskElem;
1758 }
1759 
1761 {
1762  return d->maskedSymbolLayers;
1763 }
1764 
1766 {
1767  d->maskedSymbolLayers = maskedSymbols;
1768 }
1769 
1770 //
1771 // QgsTextFormat
1772 //
1773 
1775 {
1776  d = new QgsTextSettingsPrivate();
1777 }
1778 
1780  : mBufferSettings( other.mBufferSettings )
1781  , mBackgroundSettings( other.mBackgroundSettings )
1782  , mShadowSettings( other.mShadowSettings )
1783  , mMaskSettings( other.mMaskSettings )
1784  , mTextFontFamily( other.mTextFontFamily )
1785  , mTextFontFound( other.mTextFontFound )
1786  , d( other.d )
1787 {
1788 
1789 }
1790 
1792 {
1793  d = other.d;
1794  mBufferSettings = other.mBufferSettings;
1795  mBackgroundSettings = other.mBackgroundSettings;
1796  mShadowSettings = other.mShadowSettings;
1797  mMaskSettings = other.mMaskSettings;
1798  mTextFontFamily = other.mTextFontFamily;
1799  mTextFontFound = other.mTextFontFound;
1800  return *this;
1801 }
1802 
1804 {
1805 
1806 }
1807 
1808 QFont QgsTextFormat::font() const
1809 {
1810  return d->textFont;
1811 }
1812 
1813 QFont QgsTextFormat::scaledFont( const QgsRenderContext &context ) const
1814 {
1815  QFont font = d->textFont;
1816  int fontPixelSize = QgsTextRenderer::sizeToPixel( d->fontSize, context, d->fontSizeUnits,
1817  d->fontSizeMapUnitScale );
1818  font.setPixelSize( fontPixelSize );
1819  return font;
1820 }
1821 
1822 void QgsTextFormat::setFont( const QFont &font )
1823 {
1824  d->textFont = font;
1825 }
1826 
1828 {
1829  if ( !d->textNamedStyle.isEmpty() )
1830  return d->textNamedStyle;
1831 
1832  QFontDatabase db;
1833  return db.styleString( d->textFont );
1834 }
1835 
1836 void QgsTextFormat::setNamedStyle( const QString &style )
1837 {
1838  QgsFontUtils::updateFontViaStyle( d->textFont, style );
1839  d->textNamedStyle = style;
1840 }
1841 
1843 {
1844  return d->fontSizeUnits;
1845 }
1846 
1848 {
1849  d->fontSizeUnits = unit;
1850 }
1851 
1853 {
1854  return d->fontSizeMapUnitScale;
1855 }
1856 
1858 {
1859  d->fontSizeMapUnitScale = scale;
1860 }
1861 
1862 double QgsTextFormat::size() const
1863 {
1864  return d->fontSize;
1865 }
1866 
1868 {
1869  d->fontSize = size;
1870 }
1871 
1872 QColor QgsTextFormat::color() const
1873 {
1874  return d->textColor;
1875 }
1876 
1877 void QgsTextFormat::setColor( const QColor &color )
1878 {
1879  d->textColor = color;
1880 }
1881 
1883 {
1884  return d->opacity;
1885 }
1886 
1888 {
1889  d->opacity = opacity;
1890 }
1891 
1892 QPainter::CompositionMode QgsTextFormat::blendMode() const
1893 {
1894  return d->blendMode;
1895 }
1896 
1897 void QgsTextFormat::setBlendMode( QPainter::CompositionMode mode )
1898 {
1899  d->blendMode = mode;
1900 }
1901 
1903 {
1904  return d->multilineHeight;
1905 }
1906 
1907 void QgsTextFormat::setLineHeight( double height )
1908 {
1909  d->multilineHeight = height;
1910 }
1911 
1913 {
1914  return d->orientation;
1915 }
1916 
1918 {
1919  d->orientation = orientation;
1920 }
1921 
1923 {
1924  return d->previewBackgroundColor;
1925 }
1926 
1928 {
1929  d->previewBackgroundColor = color;
1930 }
1931 
1933 {
1934  QFont appFont = QApplication::font();
1935  mTextFontFamily = layer->customProperty( QStringLiteral( "labeling/fontFamily" ), QVariant( appFont.family() ) ).toString();
1936  QString fontFamily = mTextFontFamily;
1937  if ( mTextFontFamily != appFont.family() && !QgsFontUtils::fontFamilyMatchOnSystem( mTextFontFamily ) )
1938  {
1939  // trigger to notify about font family substitution
1940  mTextFontFound = false;
1941 
1942  // TODO: update when pref for how to resolve missing family (use matching algorithm or just default font) is implemented
1943  // currently only defaults to matching algorithm for resolving [foundry], if a font of similar family is found (default for QFont)
1944 
1945  // for now, do not use matching algorithm for substitution if family not found, substitute default instead
1946  fontFamily = appFont.family();
1947  }
1948  else
1949  {
1950  mTextFontFound = true;
1951  }
1952 
1953  if ( !layer->customProperty( QStringLiteral( "labeling/fontSize" ) ).isValid() )
1954  {
1955  d->fontSize = appFont.pointSizeF();
1956  }
1957  else
1958  {
1959  d->fontSize = layer->customProperty( QStringLiteral( "labeling/fontSize" ) ).toDouble();
1960  }
1961 
1962  if ( layer->customProperty( QStringLiteral( "labeling/fontSizeUnit" ) ).toString().isEmpty() )
1963  {
1964  d->fontSizeUnits = layer->customProperty( QStringLiteral( "labeling/fontSizeInMapUnits" ), QVariant( false ) ).toBool() ?
1966  }
1967  else
1968  {
1969  bool ok = false;
1970  d->fontSizeUnits = QgsUnitTypes::decodeRenderUnit( layer->customProperty( QStringLiteral( "labeling/fontSizeUnit" ) ).toString(), &ok );
1971  if ( !ok )
1972  d->fontSizeUnits = QgsUnitTypes::RenderPoints;
1973  }
1974  if ( layer->customProperty( QStringLiteral( "labeling/fontSizeMapUnitScale" ) ).toString().isEmpty() )
1975  {
1976  //fallback to older property
1977  double oldMin = layer->customProperty( QStringLiteral( "labeling/fontSizeMapUnitMinScale" ), 0.0 ).toDouble();
1978  d->fontSizeMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
1979  double oldMax = layer->customProperty( QStringLiteral( "labeling/fontSizeMapUnitMaxScale" ), 0.0 ).toDouble();
1980  d->fontSizeMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
1981  }
1982  else
1983  {
1984  d->fontSizeMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( layer->customProperty( QStringLiteral( "labeling/fontSizeMapUnitScale" ) ).toString() );
1985  }
1986  int fontWeight = layer->customProperty( QStringLiteral( "labeling/fontWeight" ) ).toInt();
1987  bool fontItalic = layer->customProperty( QStringLiteral( "labeling/fontItalic" ) ).toBool();
1988  d->textFont = QFont( fontFamily, d->fontSize, fontWeight, fontItalic );
1989  d->textNamedStyle = QgsFontUtils::translateNamedStyle( layer->customProperty( QStringLiteral( "labeling/namedStyle" ), QVariant( "" ) ).toString() );
1990  QgsFontUtils::updateFontViaStyle( d->textFont, d->textNamedStyle ); // must come after textFont.setPointSizeF()
1991  d->textFont.setCapitalization( static_cast< QFont::Capitalization >( layer->customProperty( QStringLiteral( "labeling/fontCapitals" ), QVariant( 0 ) ).toUInt() ) );
1992  d->textFont.setUnderline( layer->customProperty( QStringLiteral( "labeling/fontUnderline" ) ).toBool() );
1993  d->textFont.setStrikeOut( layer->customProperty( QStringLiteral( "labeling/fontStrikeout" ) ).toBool() );
1994  d->textFont.setLetterSpacing( QFont::AbsoluteSpacing, layer->customProperty( QStringLiteral( "labeling/fontLetterSpacing" ), QVariant( 0.0 ) ).toDouble() );
1995  d->textFont.setWordSpacing( layer->customProperty( QStringLiteral( "labeling/fontWordSpacing" ), QVariant( 0.0 ) ).toDouble() );
1996  d->textColor = _readColor( layer, QStringLiteral( "labeling/textColor" ), Qt::black, false );
1997  if ( layer->customProperty( QStringLiteral( "labeling/textOpacity" ) ).toString().isEmpty() )
1998  {
1999  d->opacity = ( 1 - layer->customProperty( QStringLiteral( "labeling/textTransp" ) ).toInt() / 100.0 ); //0 -100
2000  }
2001  else
2002  {
2003  d->opacity = ( layer->customProperty( QStringLiteral( "labeling/textOpacity" ) ).toDouble() );
2004  }
2005  d->blendMode = QgsPainting::getCompositionMode(
2006  static_cast< QgsPainting::BlendMode >( layer->customProperty( QStringLiteral( "labeling/blendMode" ), QVariant( QgsPainting::BlendNormal ) ).toUInt() ) );
2007  d->multilineHeight = layer->customProperty( QStringLiteral( "labeling/multilineHeight" ), QVariant( 1.0 ) ).toDouble();
2008  d->previewBackgroundColor = _readColor( layer, QStringLiteral( "labeling/previewBkgrdColor" ), QColor( 255, 255, 255 ), false );
2009 
2010  mBufferSettings.readFromLayer( layer );
2011  mShadowSettings.readFromLayer( layer );
2012  mBackgroundSettings.readFromLayer( layer );
2013 }
2014 
2015 void QgsTextFormat::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
2016 {
2017  QDomElement textStyleElem;
2018  if ( elem.nodeName() == QStringLiteral( "text-style" ) )
2019  textStyleElem = elem;
2020  else
2021  textStyleElem = elem.firstChildElement( QStringLiteral( "text-style" ) );
2022  QFont appFont = QApplication::font();
2023  mTextFontFamily = textStyleElem.attribute( QStringLiteral( "fontFamily" ), appFont.family() );
2024  QString fontFamily = mTextFontFamily;
2025  if ( mTextFontFamily != appFont.family() && !QgsFontUtils::fontFamilyMatchOnSystem( mTextFontFamily ) )
2026  {
2027  // trigger to notify user about font family substitution (signal emitted in QgsVectorLayer::prepareLabelingAndDiagrams)
2028  mTextFontFound = false;
2029 
2030  // TODO: update when pref for how to resolve missing family (use matching algorithm or just default font) is implemented
2031  // currently only defaults to matching algorithm for resolving [foundry], if a font of similar family is found (default for QFont)
2032 
2033  // for now, do not use matching algorithm for substitution if family not found, substitute default instead
2034  fontFamily = appFont.family();
2035  }
2036  else
2037  {
2038  mTextFontFound = true;
2039  }
2040 
2041  if ( textStyleElem.hasAttribute( QStringLiteral( "fontSize" ) ) )
2042  {
2043  d->fontSize = textStyleElem.attribute( QStringLiteral( "fontSize" ) ).toDouble();
2044  }
2045  else
2046  {
2047  d->fontSize = appFont.pointSizeF();
2048  }
2049 
2050  if ( !textStyleElem.hasAttribute( QStringLiteral( "fontSizeUnit" ) ) )
2051  {
2052  d->fontSizeUnits = textStyleElem.attribute( QStringLiteral( "fontSizeInMapUnits" ) ).toUInt() == 0 ? QgsUnitTypes::RenderPoints
2054  }
2055  else
2056  {
2057  d->fontSizeUnits = QgsUnitTypes::decodeRenderUnit( textStyleElem.attribute( QStringLiteral( "fontSizeUnit" ) ) );
2058  }
2059 
2060  if ( !textStyleElem.hasAttribute( QStringLiteral( "fontSizeMapUnitScale" ) ) )
2061  {
2062  //fallback to older property
2063  double oldMin = textStyleElem.attribute( QStringLiteral( "fontSizeMapUnitMinScale" ), QStringLiteral( "0" ) ).toDouble();
2064  d->fontSizeMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
2065  double oldMax = textStyleElem.attribute( QStringLiteral( "fontSizeMapUnitMaxScale" ), QStringLiteral( "0" ) ).toDouble();
2066  d->fontSizeMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
2067  }
2068  else
2069  {
2070  d->fontSizeMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( textStyleElem.attribute( QStringLiteral( "fontSizeMapUnitScale" ) ) );
2071  }
2072  int fontWeight = textStyleElem.attribute( QStringLiteral( "fontWeight" ) ).toInt();
2073  bool fontItalic = textStyleElem.attribute( QStringLiteral( "fontItalic" ) ).toInt();
2074  d->textFont = QFont( fontFamily, d->fontSize, fontWeight, fontItalic );
2075  d->textFont.setPointSizeF( d->fontSize ); //double precision needed because of map units
2076  d->textNamedStyle = QgsFontUtils::translateNamedStyle( textStyleElem.attribute( QStringLiteral( "namedStyle" ) ) );
2077  QgsFontUtils::updateFontViaStyle( d->textFont, d->textNamedStyle ); // must come after textFont.setPointSizeF()
2078  d->textFont.setCapitalization( static_cast< QFont::Capitalization >( textStyleElem.attribute( QStringLiteral( "fontCapitals" ), QStringLiteral( "0" ) ).toUInt() ) );
2079  d->textFont.setUnderline( textStyleElem.attribute( QStringLiteral( "fontUnderline" ) ).toInt() );
2080  d->textFont.setStrikeOut( textStyleElem.attribute( QStringLiteral( "fontStrikeout" ) ).toInt() );
2081  d->textFont.setKerning( textStyleElem.attribute( QStringLiteral( "fontKerning" ), QStringLiteral( "1" ) ).toInt() );
2082  d->textFont.setLetterSpacing( QFont::AbsoluteSpacing, textStyleElem.attribute( QStringLiteral( "fontLetterSpacing" ), QStringLiteral( "0" ) ).toDouble() );
2083  d->textFont.setWordSpacing( textStyleElem.attribute( QStringLiteral( "fontWordSpacing" ), QStringLiteral( "0" ) ).toDouble() );
2084  d->textColor = QgsSymbolLayerUtils::decodeColor( textStyleElem.attribute( QStringLiteral( "textColor" ), QgsSymbolLayerUtils::encodeColor( Qt::black ) ) );
2085  if ( !textStyleElem.hasAttribute( QStringLiteral( "textOpacity" ) ) )
2086  {
2087  d->opacity = ( 1 - textStyleElem.attribute( QStringLiteral( "textTransp" ) ).toInt() / 100.0 ); //0 -100
2088  }
2089  else
2090  {
2091  d->opacity = ( textStyleElem.attribute( QStringLiteral( "textOpacity" ) ).toDouble() );
2092  }
2093  d->orientation = QgsTextRendererUtils::decodeTextOrientation( textStyleElem.attribute( QStringLiteral( "textOrientation" ) ) );
2094  d->previewBackgroundColor = QgsSymbolLayerUtils::decodeColor( textStyleElem.attribute( QStringLiteral( "previewBkgrdColor" ), QgsSymbolLayerUtils::encodeColor( Qt::white ) ) );
2095 
2096  d->blendMode = QgsPainting::getCompositionMode(
2097  static_cast< QgsPainting::BlendMode >( textStyleElem.attribute( QStringLiteral( "blendMode" ), QString::number( QgsPainting::BlendNormal ) ).toUInt() ) );
2098 
2099  if ( !textStyleElem.hasAttribute( QStringLiteral( "multilineHeight" ) ) )
2100  {
2101  QDomElement textFormatElem = elem.firstChildElement( QStringLiteral( "text-format" ) );
2102  d->multilineHeight = textFormatElem.attribute( QStringLiteral( "multilineHeight" ), QStringLiteral( "1" ) ).toDouble();
2103  }
2104  else
2105  {
2106  d->multilineHeight = textStyleElem.attribute( QStringLiteral( "multilineHeight" ), QStringLiteral( "1" ) ).toDouble();
2107  }
2108 
2109  if ( textStyleElem.firstChildElement( QStringLiteral( "text-buffer" ) ).isNull() )
2110  {
2111  mBufferSettings.readXml( elem );
2112  }
2113  else
2114  {
2115  mBufferSettings.readXml( textStyleElem );
2116  }
2117  if ( textStyleElem.firstChildElement( QStringLiteral( "text-mask" ) ).isNull() )
2118  {
2119  mMaskSettings.readXml( elem );
2120  }
2121  else
2122  {
2123  mMaskSettings.readXml( textStyleElem );
2124  }
2125  if ( textStyleElem.firstChildElement( QStringLiteral( "shadow" ) ).isNull() )
2126  {
2127  mShadowSettings.readXml( elem );
2128  }
2129  else
2130  {
2131  mShadowSettings.readXml( textStyleElem );
2132  }
2133  if ( textStyleElem.firstChildElement( QStringLiteral( "background" ) ).isNull() )
2134  {
2135  mBackgroundSettings.readXml( elem, context );
2136  }
2137  else
2138  {
2139  mBackgroundSettings.readXml( textStyleElem, context );
2140  }
2141 
2142  QDomElement ddElem = textStyleElem.firstChildElement( QStringLiteral( "dd_properties" ) );
2143  if ( ddElem.isNull() )
2144  {
2145  ddElem = elem.firstChildElement( QStringLiteral( "dd_properties" ) );
2146  }
2147  if ( !ddElem.isNull() )
2148  {
2149  d->mDataDefinedProperties.readXml( ddElem, QgsPalLayerSettings::propertyDefinitions() );
2150  }
2151  else
2152  {
2153  d->mDataDefinedProperties.clear();
2154  }
2155 }
2156 
2157 QDomElement QgsTextFormat::writeXml( QDomDocument &doc, const QgsReadWriteContext &context ) const
2158 {
2159  // text style
2160  QDomElement textStyleElem = doc.createElement( QStringLiteral( "text-style" ) );
2161  textStyleElem.setAttribute( QStringLiteral( "fontFamily" ), d->textFont.family() );
2162  textStyleElem.setAttribute( QStringLiteral( "namedStyle" ), QgsFontUtils::untranslateNamedStyle( d->textNamedStyle ) );
2163  textStyleElem.setAttribute( QStringLiteral( "fontSize" ), d->fontSize );
2164  textStyleElem.setAttribute( QStringLiteral( "fontSizeUnit" ), QgsUnitTypes::encodeUnit( d->fontSizeUnits ) );
2165  textStyleElem.setAttribute( QStringLiteral( "fontSizeMapUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( d->fontSizeMapUnitScale ) );
2166  textStyleElem.setAttribute( QStringLiteral( "fontWeight" ), d->textFont.weight() );
2167  textStyleElem.setAttribute( QStringLiteral( "fontItalic" ), d->textFont.italic() );
2168  textStyleElem.setAttribute( QStringLiteral( "fontStrikeout" ), d->textFont.strikeOut() );
2169  textStyleElem.setAttribute( QStringLiteral( "fontUnderline" ), d->textFont.underline() );
2170  textStyleElem.setAttribute( QStringLiteral( "textColor" ), QgsSymbolLayerUtils::encodeColor( d->textColor ) );
2171  textStyleElem.setAttribute( QStringLiteral( "previewBkgrdColor" ), QgsSymbolLayerUtils::encodeColor( d->previewBackgroundColor ) );
2172  textStyleElem.setAttribute( QStringLiteral( "fontCapitals" ), static_cast< unsigned int >( d->textFont.capitalization() ) );
2173  textStyleElem.setAttribute( QStringLiteral( "fontLetterSpacing" ), d->textFont.letterSpacing() );
2174  textStyleElem.setAttribute( QStringLiteral( "fontWordSpacing" ), d->textFont.wordSpacing() );
2175  textStyleElem.setAttribute( QStringLiteral( "fontKerning" ), d->textFont.kerning() );
2176  textStyleElem.setAttribute( QStringLiteral( "textOpacity" ), d->opacity );
2177  textStyleElem.setAttribute( QStringLiteral( "textOrientation" ), QgsTextRendererUtils::encodeTextOrientation( d->orientation ) );
2178  textStyleElem.setAttribute( QStringLiteral( "blendMode" ), QgsPainting::getBlendModeEnum( d->blendMode ) );
2179  textStyleElem.setAttribute( QStringLiteral( "multilineHeight" ), d->multilineHeight );
2180 
2181  QDomElement ddElem = doc.createElement( QStringLiteral( "dd_properties" ) );
2182  d->mDataDefinedProperties.writeXml( ddElem, QgsPalLayerSettings::propertyDefinitions() );
2183 
2184  textStyleElem.appendChild( mBufferSettings.writeXml( doc ) );
2185  textStyleElem.appendChild( mMaskSettings.writeXml( doc ) );
2186  textStyleElem.appendChild( mBackgroundSettings.writeXml( doc, context ) );
2187  textStyleElem.appendChild( mShadowSettings.writeXml( doc ) );
2188  textStyleElem.appendChild( ddElem );
2189 
2190  return textStyleElem;
2191 }
2192 
2193 QMimeData *QgsTextFormat::toMimeData() const
2194 {
2195  //set both the mime color data, and the text (format settings).
2196  QMimeData *mimeData = new QMimeData;
2197  mimeData->setColorData( QVariant( color() ) );
2198 
2199  QgsReadWriteContext rwContext;
2200  QDomDocument textDoc;
2201  QDomElement textElem = writeXml( textDoc, rwContext );
2202  textDoc.appendChild( textElem );
2203  mimeData->setText( textDoc.toString() );
2204 
2205  return mimeData;
2206 }
2207 
2209 {
2210  QgsTextFormat format;
2211  format.setFont( font );
2212  if ( font.pointSizeF() > 0 )
2213  {
2214  format.setSize( font.pointSizeF() );
2216  }
2217  else if ( font.pixelSize() > 0 )
2218  {
2219  format.setSize( font.pixelSize() );
2221  }
2222 
2223  return format;
2224 }
2225 
2227 {
2228  QFont f = font();
2229  switch ( sizeUnit() )
2230  {
2232  f.setPointSizeF( size() );
2233  break;
2234 
2236  f.setPointSizeF( size() * 2.83464567 );
2237  break;
2238 
2240  f.setPointSizeF( size() * 72 );
2241  break;
2242 
2244  f.setPixelSize( static_cast< int >( std::round( size() ) ) );
2245  break;
2246 
2251  // no meaning here
2252  break;
2253  }
2254  return f;
2255 }
2256 
2257 QgsTextFormat QgsTextFormat::fromMimeData( const QMimeData *data, bool *ok )
2258 {
2259  if ( ok )
2260  *ok = false;
2261  QgsTextFormat format;
2262  if ( !data )
2263  return format;
2264 
2265  QString text = data->text();
2266  if ( !text.isEmpty() )
2267  {
2268  QDomDocument doc;
2269  QDomElement elem;
2270  QgsReadWriteContext rwContext;
2271 
2272  if ( doc.setContent( text ) )
2273  {
2274  elem = doc.documentElement();
2275 
2276  format.readXml( elem, rwContext );
2277  if ( ok )
2278  *ok = true;
2279  return format;
2280  }
2281  }
2282  return format;
2283 }
2284 
2286 {
2287  if ( d->blendMode != QPainter::CompositionMode_SourceOver )
2288  return true;
2289 
2290  if ( mBufferSettings.enabled() && mBufferSettings.blendMode() != QPainter::CompositionMode_SourceOver )
2291  return true;
2292 
2293  if ( mBackgroundSettings.enabled() && mBackgroundSettings.blendMode() != QPainter::CompositionMode_SourceOver )
2294  return true;
2295 
2296  if ( mShadowSettings.enabled() && mShadowSettings.blendMode() != QPainter::CompositionMode_SourceOver )
2297  return true;
2298 
2299  return false;
2300 }
2301 
2303 {
2304  return d->mDataDefinedProperties;
2305 }
2306 
2308 {
2309  return d->mDataDefinedProperties;
2310 }
2311 
2313 {
2314  d->mDataDefinedProperties = collection;
2315 }
2316 
2318 {
2319  if ( !d->mDataDefinedProperties.hasActiveProperties() )
2320  return;
2321 
2322  QString ddFontFamily;
2323  context.expressionContext().setOriginalValueVariable( d->textFont.family() );
2324  QVariant exprVal = d->mDataDefinedProperties.value( QgsPalLayerSettings::Family, context.expressionContext() );
2325  if ( exprVal.isValid() )
2326  {
2327  QString family = exprVal.toString().trimmed();
2328  if ( d->textFont.family() != family )
2329  {
2330  // testing for ddFontFamily in QFontDatabase.families() may be slow to do for every feature
2331  // (i.e. don't use QgsFontUtils::fontFamilyMatchOnSystem( family ) here)
2332  if ( QgsFontUtils::fontFamilyOnSystem( family ) )
2333  {
2334  ddFontFamily = family;
2335  }
2336  }
2337  }
2338 
2339  // data defined named font style?
2340  QString ddFontStyle;
2341  context.expressionContext().setOriginalValueVariable( d->textNamedStyle );
2342  exprVal = d->mDataDefinedProperties.value( QgsPalLayerSettings::FontStyle, context.expressionContext() );
2343  if ( exprVal.isValid() )
2344  {
2345  QString fontstyle = exprVal.toString().trimmed();
2346  ddFontStyle = fontstyle;
2347  }
2348 
2349  bool ddBold = false;
2350  if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Bold ) )
2351  {
2352  context.expressionContext().setOriginalValueVariable( d->textFont.bold() );
2353  ddBold = d->mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::Bold, context.expressionContext(), false ) ;
2354  }
2355 
2356  bool ddItalic = false;
2357  if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Italic ) )
2358  {
2359  context.expressionContext().setOriginalValueVariable( d->textFont.italic() );
2360  ddItalic = d->mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::Italic, context.expressionContext(), false );
2361  }
2362 
2363  // TODO: update when pref for how to resolve missing family (use matching algorithm or just default font) is implemented
2364  // (currently defaults to what has been read in from layer settings)
2365  QFont newFont;
2366  QFontDatabase fontDb;
2367  QFont appFont = QApplication::font();
2368  bool newFontBuilt = false;
2369  if ( ddBold || ddItalic )
2370  {
2371  // new font needs built, since existing style needs removed
2372  newFont = QFont( !ddFontFamily.isEmpty() ? ddFontFamily : d->textFont.family() );
2373  newFontBuilt = true;
2374  newFont.setBold( ddBold );
2375  newFont.setItalic( ddItalic );
2376  }
2377  else if ( !ddFontStyle.isEmpty()
2378  && ddFontStyle.compare( QLatin1String( "Ignore" ), Qt::CaseInsensitive ) != 0 )
2379  {
2380  if ( !ddFontFamily.isEmpty() )
2381  {
2382  // both family and style are different, build font from database
2383  QFont styledfont = fontDb.font( ddFontFamily, ddFontStyle, appFont.pointSize() );
2384  if ( appFont != styledfont )
2385  {
2386  newFont = styledfont;
2387  newFontBuilt = true;
2388  }
2389  }
2390 
2391  // update the font face style
2392  QgsFontUtils::updateFontViaStyle( newFontBuilt ? newFont : d->textFont, ddFontStyle );
2393  }
2394  else if ( !ddFontFamily.isEmpty() )
2395  {
2396  if ( ddFontStyle.compare( QLatin1String( "Ignore" ), Qt::CaseInsensitive ) != 0 )
2397  {
2398  // just family is different, build font from database
2399  QFont styledfont = fontDb.font( ddFontFamily, d->textNamedStyle, appFont.pointSize() );
2400  if ( appFont != styledfont )
2401  {
2402  newFont = styledfont;
2403  newFontBuilt = true;
2404  }
2405  }
2406  else
2407  {
2408  newFont = QFont( ddFontFamily );
2409  newFontBuilt = true;
2410  }
2411  }
2412 
2413  if ( newFontBuilt )
2414  {
2415  // copy over existing font settings
2416  newFont.setUnderline( d->textFont.underline() );
2417  newFont.setStrikeOut( d->textFont.strikeOut() );
2418  newFont.setWordSpacing( d->textFont.wordSpacing() );
2419  newFont.setLetterSpacing( QFont::AbsoluteSpacing, d->textFont.letterSpacing() );
2420  d->textFont = newFont;
2421  }
2422 
2423  if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Underline ) )
2424  {
2425  context.expressionContext().setOriginalValueVariable( d->textFont.underline() );
2426  d->textFont.setUnderline( d->mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::Underline, context.expressionContext(), d->textFont.underline() ) );
2427  }
2428 
2429  if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Strikeout ) )
2430  {
2431  context.expressionContext().setOriginalValueVariable( d->textFont.strikeOut() );
2432  d->textFont.setStrikeOut( d->mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::Strikeout, context.expressionContext(), d->textFont.strikeOut() ) );
2433  }
2434 
2435  if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Color ) )
2436  {
2438  d->textColor = d->mDataDefinedProperties.valueAsColor( QgsPalLayerSettings::Color, context.expressionContext(), d->textColor );
2439  }
2440 
2441  if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Size ) )
2442  {
2444  d->fontSize = d->mDataDefinedProperties.valueAsDouble( QgsPalLayerSettings::Size, context.expressionContext(), d->fontSize );
2445  }
2446 
2447  exprVal = d->mDataDefinedProperties.value( QgsPalLayerSettings::FontSizeUnit, context.expressionContext() );
2448  if ( exprVal.isValid() )
2449  {
2450  QString units = exprVal.toString();
2451  if ( !units.isEmpty() )
2452  {
2453  bool ok;
2455  if ( ok )
2456  d->fontSizeUnits = res;
2457  }
2458  }
2459 
2460  if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::FontOpacity ) )
2461  {
2462  context.expressionContext().setOriginalValueVariable( d->opacity * 100 );
2463  d->opacity = d->mDataDefinedProperties.value( QgsPalLayerSettings::FontOpacity, context.expressionContext(), d->opacity * 100 ).toDouble() / 100.0;
2464  }
2465 
2466  if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::TextOrientation ) )
2467  {
2468  const QString encoded = QgsTextRendererUtils::encodeTextOrientation( d->orientation );
2469  context.expressionContext().setOriginalValueVariable( encoded );
2470  d->orientation = QgsTextRendererUtils::decodeTextOrientation( d->mDataDefinedProperties.value( QgsPalLayerSettings::TextOrientation, context.expressionContext(), encoded ).toString() );
2471  }
2472 
2473  if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::FontLetterSpacing ) )
2474  {
2475  context.expressionContext().setOriginalValueVariable( d->textFont.letterSpacing() );
2476  d->textFont.setLetterSpacing( QFont::AbsoluteSpacing, d->mDataDefinedProperties.value( QgsPalLayerSettings::FontLetterSpacing, context.expressionContext(), d->textFont.letterSpacing() ).toDouble() );
2477  }
2478 
2479  if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::FontWordSpacing ) )
2480  {
2481  context.expressionContext().setOriginalValueVariable( d->textFont.wordSpacing() );
2482  d->textFont.setWordSpacing( d->mDataDefinedProperties.value( QgsPalLayerSettings::FontWordSpacing, context.expressionContext(), d->textFont.wordSpacing() ).toDouble() );
2483  }
2484 
2485  if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::FontBlendMode ) )
2486  {
2487  exprVal = d->mDataDefinedProperties.value( QgsPalLayerSettings::FontBlendMode, context.expressionContext() );
2488  QString blendstr = exprVal.toString().trimmed();
2489  if ( !blendstr.isEmpty() )
2490  d->blendMode = QgsSymbolLayerUtils::decodeBlendMode( blendstr );
2491  }
2492 
2493  mShadowSettings.updateDataDefinedProperties( context, d->mDataDefinedProperties );
2494  mBackgroundSettings.updateDataDefinedProperties( context, d->mDataDefinedProperties );
2495  mBufferSettings.updateDataDefinedProperties( context, d->mDataDefinedProperties );
2496  mMaskSettings.updateDataDefinedProperties( context, d->mDataDefinedProperties );
2497 }
2498 
2499 QPixmap QgsTextFormat::textFormatPreviewPixmap( const QgsTextFormat &format, QSize size, const QString &previewText, int padding )
2500 {
2501  QgsTextFormat tempFormat = format;
2502  QPixmap pixmap( size );
2503  pixmap.fill( Qt::transparent );
2504  QPainter painter;
2505  painter.begin( &pixmap );
2506 
2507  painter.setRenderHint( QPainter::Antialiasing );
2508 
2509  QRect rect( 0, 0, size.width(), size.height() );
2510 
2511  // shameless eye candy - use a subtle gradient when drawing background
2512  painter.setPen( Qt::NoPen );
2513  QColor background1 = tempFormat.previewBackgroundColor();
2514  if ( ( background1.lightnessF() < 0.7 ) )
2515  {
2516  background1 = background1.darker( 125 );
2517  }
2518  else
2519  {
2520  background1 = background1.lighter( 125 );
2521  }
2522  QColor background2 = tempFormat.previewBackgroundColor();
2523  QLinearGradient linearGrad( QPointF( 0, 0 ), QPointF( 0, rect.height() ) );
2524  linearGrad.setColorAt( 0, background1 );
2525  linearGrad.setColorAt( 1, background2 );
2526  painter.setBrush( QBrush( linearGrad ) );
2527  if ( size.width() > 30 )
2528  {
2529  painter.drawRoundedRect( rect, 6, 6 );
2530  }
2531  else
2532  {
2533  // don't use rounded rect for small previews
2534  painter.drawRect( rect );
2535  }
2536  painter.setBrush( Qt::NoBrush );
2537  painter.setPen( Qt::NoPen );
2538  padding += 1; // move text away from background border
2539 
2540  QgsRenderContext context;
2541  QgsMapToPixel newCoordXForm;
2542  newCoordXForm.setParameters( 1, 0, 0, 0, 0, 0 );
2543  context.setMapToPixel( newCoordXForm );
2544 
2545  context.setScaleFactor( QgsApplication::desktop()->logicalDpiX() / 25.4 );
2546  context.setUseAdvancedEffects( true );
2547  context.setPainter( &painter );
2548 
2549  // slightly inset text to account for buffer/background
2550  double xtrans = 0;
2551  if ( tempFormat.buffer().enabled() )
2552  xtrans = context.convertToPainterUnits( tempFormat.buffer().size(), tempFormat.buffer().sizeUnit(), tempFormat.buffer().sizeMapUnitScale() );
2553  if ( tempFormat.background().enabled() && tempFormat.background().sizeType() != QgsTextBackgroundSettings::SizeFixed )
2554  xtrans = std::max( xtrans, context.convertToPainterUnits( tempFormat.background().size().width(), tempFormat.background().sizeUnit(), tempFormat.background().sizeMapUnitScale() ) );
2555 
2556  double ytrans = 0.0;
2557  if ( tempFormat.buffer().enabled() )
2558  ytrans = std::max( ytrans, context.convertToPainterUnits( tempFormat.buffer().size(), tempFormat.buffer().sizeUnit(), tempFormat.buffer().sizeMapUnitScale() ) );
2559  if ( tempFormat.background().enabled() )
2560  ytrans = std::max( ytrans, context.convertToPainterUnits( tempFormat.background().size().height(), tempFormat.background().sizeUnit(), tempFormat.background().sizeMapUnitScale() ) );
2561 
2562  const QStringList text = QStringList() << ( previewText.isEmpty() ? QObject::tr( "Aa" ) : previewText );
2563  const double textHeight = QgsTextRenderer::textHeight( context, tempFormat, text, QgsTextRenderer::Rect );
2564  QRectF textRect = rect;
2565  textRect.setLeft( xtrans + padding );
2566  textRect.setWidth( rect.width() - xtrans - 2 * padding );
2567 
2568  if ( textRect.width() > 2000 )
2569  textRect.setWidth( 2000 - 2 * padding );
2570 
2571  const double bottom = textRect.height() / 2 + textHeight / 2;
2572  textRect.setTop( bottom - textHeight );
2573  textRect.setBottom( bottom );
2574 
2575  QgsTextRenderer::drawText( textRect, 0, QgsTextRenderer::AlignCenter, text, context, tempFormat );
2576 
2577  // draw border on top of text
2578  painter.setBrush( Qt::NoBrush );
2579  painter.setPen( QPen( tempFormat.previewBackgroundColor().darker( 150 ), 0 ) );
2580  if ( size.width() > 30 )
2581  {
2582  painter.drawRoundedRect( rect, 6, 6 );
2583  }
2584  else
2585  {
2586  // don't use rounded rect for small previews
2587  painter.drawRect( rect );
2588  }
2589  painter.end();
2590  return pixmap;
2591 }
2592 
2593 
2595 {
2596  return static_cast< int >( c.convertToPainterUnits( size, unit, mapUnitScale ) + 0.5 ); //NOLINT
2597 }
2598 
2599 void QgsTextRenderer::drawText( const QRectF &rect, double rotation, QgsTextRenderer::HAlignment alignment, const QStringList &textLines, QgsRenderContext &context, const QgsTextFormat &format, bool )
2600 {
2601  QgsTextFormat tmpFormat = format;
2602  if ( format.dataDefinedProperties().hasActiveProperties() ) // note, we use format instead of tmpFormat here, it's const and potentially avoids a detach
2603  tmpFormat.updateDataDefinedProperties( context );
2604  tmpFormat = updateShadowPosition( tmpFormat );
2605 
2606  if ( tmpFormat.background().enabled() )
2607  {
2608  drawPart( rect, rotation, alignment, textLines, context, tmpFormat, Background );
2609  }
2610 
2611  if ( tmpFormat.buffer().enabled() )
2612  {
2613  drawPart( rect, rotation, alignment, textLines, context, tmpFormat, Buffer );
2614  }
2615 
2616  drawPart( rect, rotation, alignment, textLines, context, tmpFormat, Text );
2617 }
2618 
2619 void QgsTextRenderer::drawText( QPointF point, double rotation, QgsTextRenderer::HAlignment alignment, const QStringList &textLines, QgsRenderContext &context, const QgsTextFormat &format, bool )
2620 {
2621  QgsTextFormat tmpFormat = format;
2622  if ( format.dataDefinedProperties().hasActiveProperties() ) // note, we use format instead of tmpFormat here, it's const and potentially avoids a detach
2623  tmpFormat.updateDataDefinedProperties( context );
2624  tmpFormat = updateShadowPosition( tmpFormat );
2625 
2626  if ( tmpFormat.background().enabled() )
2627  {
2628  drawPart( point, rotation, alignment, textLines, context, tmpFormat, Background );
2629  }
2630 
2631  if ( tmpFormat.buffer().enabled() )
2632  {
2633  drawPart( point, rotation, alignment, textLines, context, tmpFormat, Buffer );
2634  }
2635 
2636  drawPart( point, rotation, alignment, textLines, context, tmpFormat, Text );
2637 }
2638 
2639 QgsTextFormat QgsTextRenderer::updateShadowPosition( const QgsTextFormat &format )
2640 {
2641  if ( !format.shadow().enabled() || format.shadow().shadowPlacement() != QgsTextShadowSettings::ShadowLowest )
2642  return format;
2643 
2644  QgsTextFormat tmpFormat = format;
2645  if ( tmpFormat.background().enabled() && tmpFormat.background().type() != QgsTextBackgroundSettings::ShapeMarkerSymbol ) // background shadow not compatible with marker symbol backgrounds
2646  {
2648  }
2649  else if ( tmpFormat.buffer().enabled() )
2650  {
2652  }
2653  else
2654  {
2656  }
2657  return tmpFormat;
2658 }
2659 
2660 void QgsTextRenderer::drawPart( const QRectF &rect, double rotation, HAlignment alignment,
2661  const QStringList &textLines, QgsRenderContext &context, const QgsTextFormat &format, QgsTextRenderer::TextPart part, bool )
2662 {
2663  if ( !context.painter() )
2664  {
2665  return;
2666  }
2667 
2668  Component component;
2669  component.dpiRatio = 1.0;
2670  component.origin = rect.topLeft();
2671  component.rotation = rotation;
2672  component.size = rect.size();
2673  component.hAlign = alignment;
2674 
2675  switch ( part )
2676  {
2677  case Background:
2678  {
2679  if ( !format.background().enabled() )
2680  return;
2681 
2682  if ( !qgsDoubleNear( rotation, 0.0 ) )
2683  {
2684  // get rotated label's center point
2685 
2686  double xc = rect.width() / 2.0;
2687  double yc = rect.height() / 2.0;
2688 
2689  double angle = -rotation;
2690  double xd = xc * std::cos( angle ) - yc * std::sin( angle );
2691  double yd = xc * std::sin( angle ) + yc * std::cos( angle );
2692 
2693  component.center = QPointF( component.origin.x() + xd, component.origin.y() + yd );
2694  }
2695  else
2696  {
2697  component.center = rect.center();
2698  }
2699 
2700  QgsTextRenderer::drawBackground( context, component, format, textLines, Rect );
2701 
2702  break;
2703  }
2704 
2705  case Buffer:
2706  {
2707  if ( !format.buffer().enabled() )
2708  break;
2709  }
2710  FALLTHROUGH
2711  case Text:
2712  case Shadow:
2713  {
2714  QFontMetricsF fm( format.scaledFont( context ) );
2715  drawTextInternal( part, context, format, component,
2716  textLines,
2717  &fm,
2718  alignment );
2719  break;
2720  }
2721  }
2722 }
2723 
2724 void QgsTextRenderer::drawPart( QPointF origin, double rotation, QgsTextRenderer::HAlignment alignment, const QStringList &textLines, QgsRenderContext &context, const QgsTextFormat &format, QgsTextRenderer::TextPart part, bool )
2725 {
2726  if ( !context.painter() )
2727  {
2728  return;
2729  }
2730 
2731  Component component;
2732  component.dpiRatio = 1.0;
2733  component.origin = origin;
2734  component.rotation = rotation;
2735  component.hAlign = alignment;
2736 
2737  switch ( part )
2738  {
2739  case Background:
2740  {
2741  if ( !format.background().enabled() )
2742  return;
2743 
2744  QgsTextRenderer::drawBackground( context, component, format, textLines, Point );
2745  break;
2746  }
2747 
2748  case Buffer:
2749  {
2750  if ( !format.buffer().enabled() )
2751  break;
2752  }
2753  FALLTHROUGH
2754  case Text:
2755  case Shadow:
2756  {
2757  QFontMetricsF fm( format.scaledFont( context ) );
2758  drawTextInternal( part, context, format, component,
2759  textLines,
2760  &fm,
2761  alignment,
2762  Point );
2763  break;
2764  }
2765  }
2766 }
2767 
2768 QFontMetricsF QgsTextRenderer::fontMetrics( QgsRenderContext &context, const QgsTextFormat &format )
2769 {
2770  return QFontMetricsF( format.scaledFont( context ), context.painter() ? context.painter()->device() : nullptr );
2771 }
2772 
2773 void QgsTextRenderer::drawBuffer( QgsRenderContext &context, const QgsTextRenderer::Component &component, const QgsTextFormat &format, const QFontMetricsF *fontMetrics )
2774 {
2775  QPainter *p = context.painter();
2776 
2779  {
2780  if ( component.rotation >= -315 && component.rotation < -90 )
2781  {
2782  orientation = QgsTextFormat::VerticalOrientation;
2783  }
2784  else if ( component.rotation >= -90 && component.rotation < -45 )
2785  {
2786  orientation = QgsTextFormat::VerticalOrientation;
2787  }
2788  else
2789  {
2791  }
2792  }
2793 
2794  QgsTextBufferSettings buffer = format.buffer();
2795 
2796  double penSize = context.convertToPainterUnits( buffer.size(), buffer.sizeUnit(), buffer.sizeMapUnitScale() );
2797 
2798  QPainterPath path;
2799  path.setFillRule( Qt::WindingFill );
2800  switch ( orientation )
2801  {
2803  path.addText( 0, 0, format.scaledFont( context ), component.text );
2804  break;
2807  double letterSpacing = format.scaledFont( context ).letterSpacing();
2808  double labelWidth = fontMetrics->maxWidth();
2809  const QStringList parts = QgsPalLabeling::splitToGraphemes( component.text );
2810  double partYOffset = 0.0;
2811  for ( const auto &part : parts )
2812  {
2813  double partXOffset = ( labelWidth - ( fontMetrics->width( part ) - letterSpacing ) ) / 2;
2814  path.addText( partXOffset, partYOffset, format.scaledFont( context ), part );
2815  partYOffset += fontMetrics->ascent() + letterSpacing;
2816  }
2817  }
2818 
2819  QColor bufferColor = buffer.color();
2820  bufferColor.setAlphaF( buffer.opacity() );
2821  QPen pen( bufferColor );
2822  pen.setWidthF( penSize );
2823  pen.setJoinStyle( buffer.joinStyle() );
2824  QColor tmpColor( bufferColor );
2825  // honor pref for whether to fill buffer interior
2826  if ( !buffer.fillBufferInterior() )
2827  {
2828  tmpColor.setAlpha( 0 );
2829  }
2830 
2831  // store buffer's drawing in QPicture for drop shadow call
2832  QPicture buffPict;
2833  QPainter buffp;
2834  buffp.begin( &buffPict );
2835 
2836  if ( buffer.paintEffect() && buffer.paintEffect()->enabled() )
2837  {
2838  context.setPainter( &buffp );
2839 
2840  buffer.paintEffect()->begin( context );
2841  context.painter()->setPen( pen );
2842  context.painter()->setBrush( tmpColor );
2843  context.painter()->drawPath( path );
2844  buffer.paintEffect()->end( context );
2845 
2846  context.setPainter( p );
2847  }
2848  else
2849  {
2850  buffp.setPen( pen );
2851  buffp.setBrush( tmpColor );
2852  buffp.drawPath( path );
2853  }
2854  buffp.end();
2855 
2856  if ( format.shadow().enabled() && format.shadow().shadowPlacement() == QgsTextShadowSettings::ShadowBuffer )
2857  {
2858  QgsTextRenderer::Component bufferComponent = component;
2859  bufferComponent.origin = QPointF( 0.0, 0.0 );
2860  bufferComponent.picture = buffPict;
2861  bufferComponent.pictureBuffer = penSize / 2.0;
2862  drawShadow( context, bufferComponent, format );
2863  }
2864  p->save();
2865  if ( context.useAdvancedEffects() )
2866  {
2867  p->setCompositionMode( buffer.blendMode() );
2868  }
2869  if ( context.flags() & QgsRenderContext::Antialiasing )
2870  {
2871  p->setRenderHint( QPainter::Antialiasing );
2872  }
2873 
2874  // scale for any print output or image saving @ specific dpi
2875  p->scale( component.dpiRatio, component.dpiRatio );
2876  _fixQPictureDPI( p );
2877  p->drawPicture( 0, 0, buffPict );
2878  p->restore();
2879 }
2880 
2881 void QgsTextRenderer::drawMask( QgsRenderContext &context, const QgsTextRenderer::Component &component, const QgsTextFormat &format )
2882 {
2883  QgsTextMaskSettings mask = format.mask();
2884 
2885  // the mask is drawn to a side painter
2886  // or to the main painter for preview
2887  QPainter *p = context.isGuiPreview() ? context.painter() : context.maskPainter( context.currentMaskId() );
2888  if ( ! p )
2889  return;
2890 
2891  double penSize = context.convertToPainterUnits( mask.size(), mask.sizeUnit(), mask.sizeMapUnitScale() );
2892 
2893  // buffer: draw the text with a big pen
2894  QPainterPath path;
2895  path.setFillRule( Qt::WindingFill );
2896  path.addText( 0, 0, format.scaledFont( context ), component.text );
2897 
2898  QColor bufferColor( Qt::gray );
2899  bufferColor.setAlphaF( mask.opacity() );
2900 
2901  QPen pen;
2902  QBrush brush;
2903  brush.setColor( bufferColor );
2904  pen.setColor( bufferColor );
2905  pen.setWidthF( penSize );
2906  pen.setJoinStyle( mask.joinStyle() );
2907 
2908  p->save();
2909 
2910  if ( context.flags() & QgsRenderContext::Antialiasing )
2911  {
2912  p->setRenderHint( QPainter::Antialiasing );
2913  }
2914 
2915  // scale for any print output or image saving @ specific dpi
2916  p->scale( component.dpiRatio, component.dpiRatio );
2917  if ( mask.paintEffect() && mask.paintEffect()->enabled() )
2918  {
2919  QgsPainterSwapper swapper( context, p );
2920  {
2921  QgsEffectPainter effectPainter( context, mask.paintEffect() );
2922  context.painter()->setPen( pen );
2923  context.painter()->setBrush( brush );
2924  context.painter()->drawPath( path );
2925  }
2926  }
2927  else
2928  {
2929  p->setPen( pen );
2930  p->setBrush( brush );
2931  p->drawPath( path );
2932  }
2933  p->restore();
2934 }
2935 
2936 double QgsTextRenderer::textWidth( const QgsRenderContext &context, const QgsTextFormat &format, const QStringList &textLines, QFontMetricsF *fontMetrics )
2937 {
2938  //calculate max width of text lines
2939  std::unique_ptr< QFontMetricsF > newFm;
2940  if ( !fontMetrics )
2941  {
2942  newFm.reset( new QFontMetricsF( format.scaledFont( context ) ) );
2943  fontMetrics = newFm.get();
2944  }
2945 
2946  double width = 0;
2947  switch ( format.orientation() )
2948  {
2950  {
2951  double maxLineWidth = 0;
2952  const auto constTextLines = textLines;
2953  for ( const QString &line : constTextLines )
2954  {
2955  maxLineWidth = std::max( maxLineWidth, fontMetrics->width( line ) );
2956  }
2957  width = maxLineWidth;
2958  break;
2959  }
2960 
2962  {
2963  double labelWidth = fontMetrics->maxWidth();
2964  width = labelWidth + ( textLines.size() - 1 ) * labelWidth * format.lineHeight();
2965  break;
2966  }
2967 
2969  {
2970  // label mode only
2971  break;
2972  }
2973  }
2974 
2975  return width;
2976 }
2977 
2978 double QgsTextRenderer::textHeight( const QgsRenderContext &context, const QgsTextFormat &format, const QStringList &textLines, DrawMode mode, QFontMetricsF *fontMetrics )
2979 {
2980  //calculate max width of text lines
2981  std::unique_ptr< QFontMetricsF > newFm;
2982  if ( !fontMetrics )
2983  {
2984  newFm.reset( new QFontMetricsF( format.scaledFont( context ) ) );
2985  fontMetrics = newFm.get();
2986  }
2987 
2988  switch ( format.orientation() )
2989  {
2991  {
2992  double labelHeight = fontMetrics->ascent() + fontMetrics->descent(); // ignore +1 for baseline
2993  switch ( mode )
2994  {
2995  case Label:
2996  // rendering labels needs special handling - in this case text should be
2997  // drawn with the bottom left corner coinciding with origin, vs top left
2998  // for standard text rendering. Line height is also slightly different.
2999  return labelHeight + ( textLines.size() - 1 ) * labelHeight * format.lineHeight();
3000 
3001  case Rect:
3002  case Point:
3003  // standard rendering - designed to exactly replicate QPainter's drawText method
3004  return labelHeight + ( textLines.size() - 1 ) * fontMetrics->lineSpacing() * format.lineHeight();
3005  }
3006  break;
3007  }
3008 
3010  {
3011  double labelHeight = fontMetrics->ascent();
3012  double letterSpacing = format.scaledFont( context ).letterSpacing();
3013  int maxLineLength = 0;
3014  for ( const auto &line : textLines )
3015  {
3016  if ( line.length() > maxLineLength )
3017  maxLineLength = line.length();
3018  }
3019  return labelHeight * maxLineLength + ( maxLineLength - 1 ) * letterSpacing;
3020  break;
3021  }
3022 
3024  {
3025  // label mode only
3026  break;
3027  }
3028  }
3029 
3030  return 0;
3031 }
3032 
3033 void QgsTextRenderer::drawBackground( QgsRenderContext &context, QgsTextRenderer::Component component, const QgsTextFormat &format,
3034  const QStringList &textLines, DrawMode mode )
3035 {
3037 
3038  QPainter *prevP = context.painter();
3039  QPainter *p = context.painter();
3040  if ( background.paintEffect() && background.paintEffect()->enabled() )
3041  {
3042  background.paintEffect()->begin( context );
3043  p = context.painter();
3044  }
3045 
3046  //QgsDebugMsgLevel( QStringLiteral( "Background label rotation: %1" ).arg( component.rotation() ), 4 );
3047 
3048  // shared calculations between shapes and SVG
3049 
3050  // configure angles, set component rotation and rotationOffset
3052  {
3053  component.rotation = -( component.rotation * 180 / M_PI ); // RotationSync
3054  component.rotationOffset =
3055  background.rotationType() == QgsTextBackgroundSettings::RotationOffset ? background.rotation() : 0.0;
3056  }
3057  else // RotationFixed
3058  {
3059  component.rotation = 0.0; // don't use label's rotation
3060  component.rotationOffset = background.rotation();
3061  }
3062 
3063  if ( mode != Label )
3064  {
3065  // need to calculate size of text
3066  QFontMetricsF fm( format.scaledFont( context ) );
3067  double width = textWidth( context, format, textLines, &fm );
3068  double height = textHeight( context, format, textLines, mode, &fm );
3069 
3070  switch ( mode )
3071  {
3072  case Rect:
3073  switch ( component.hAlign )
3074  {
3075  case AlignLeft:
3076  component.center = QPointF( component.origin.x() + width / 2.0,
3077  component.origin.y() + height / 2.0 );
3078  break;
3079 
3080  case AlignCenter:
3081  component.center = QPointF( component.origin.x() + component.size.width() / 2.0,
3082  component.origin.y() + height / 2.0 );
3083  break;
3084 
3085  case AlignRight:
3086  component.center = QPointF( component.origin.x() + component.size.width() - width / 2.0,
3087  component.origin.y() + height / 2.0 );
3088  break;
3089  }
3090  break;
3091 
3092  case Point:
3093  {
3094  double originAdjust = fm.ascent() / 2.0 - fm.leading() / 2.0;
3095  switch ( component.hAlign )
3096  {
3097  case AlignLeft:
3098  component.center = QPointF( component.origin.x() + width / 2.0,
3099  component.origin.y() - height / 2.0 + originAdjust );
3100  break;
3101 
3102  case AlignCenter:
3103  component.center = QPointF( component.origin.x(),
3104  component.origin.y() - height / 2.0 + originAdjust );
3105  break;
3106 
3107  case AlignRight:
3108  component.center = QPointF( component.origin.x() - width / 2.0,
3109  component.origin.y() - height / 2.0 + originAdjust );
3110  break;
3111  }
3112  break;
3113  }
3114 
3115  case Label:
3116  break;
3117  }
3118 
3120  component.size = QSizeF( width, height );
3121  }
3122 
3123  // TODO: the following label-buffered generated shapes and SVG symbols should be moved into marker symbology classes
3124 
3125  switch ( background.type() )
3126  {
3129  {
3130  // all calculations done in shapeSizeUnits, which are then passed to symbology class for painting
3131 
3132  if ( background.type() == QgsTextBackgroundSettings::ShapeSVG && background.svgFile().isEmpty() )
3133  return;
3134 
3135  if ( background.type() == QgsTextBackgroundSettings::ShapeMarkerSymbol && !background.markerSymbol() )
3136  return;
3137 
3138  double sizeOut = 0.0;
3139  // only one size used for SVG/marker symbol sizing/scaling (no use of shapeSize.y() or Y field in gui)
3140  if ( background.sizeType() == QgsTextBackgroundSettings::SizeFixed )
3141  {
3142  sizeOut = context.convertToPainterUnits( background.size().width(), background.sizeUnit(), background.sizeMapUnitScale() );
3143  }
3144  else if ( background.sizeType() == QgsTextBackgroundSettings::SizeBuffer )
3145  {
3146  sizeOut = std::max( component.size.width(), component.size.height() );
3147  double bufferSize = context.convertToPainterUnits( background.size().width(), background.sizeUnit(), background.sizeMapUnitScale() );
3148 
3149  // add buffer
3150  sizeOut += bufferSize * 2;
3151  }
3152 
3153  // don't bother rendering symbols smaller than 1x1 pixels in size
3154  // TODO: add option to not show any svgs under/over a certain size
3155  if ( sizeOut < 1.0 )
3156  return;
3157 
3158  std::unique_ptr< QgsMarkerSymbol > renderedSymbol;
3159  if ( background.type() == QgsTextBackgroundSettings::ShapeSVG )
3160  {
3161  QgsStringMap map; // for SVG symbology marker
3162  map[QStringLiteral( "name" )] = background.svgFile().trimmed();
3163  map[QStringLiteral( "size" )] = QString::number( sizeOut );
3164  map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( QgsUnitTypes::RenderPixels );
3165  map[QStringLiteral( "angle" )] = QString::number( 0.0 ); // angle is handled by this local painter
3166 
3167  // offset is handled by this local painter
3168  // TODO: see why the marker renderer doesn't seem to translate offset *after* applying rotation
3169  //map["offset"] = QgsSymbolLayerUtils::encodePoint( tmpLyr.shapeOffset );
3170  //map["offset_unit"] = QgsUnitTypes::encodeUnit(
3171  // tmpLyr.shapeOffsetUnits == QgsPalLayerSettings::MapUnits ? QgsUnitTypes::MapUnit : QgsUnitTypes::MM );
3172 
3173  map[QStringLiteral( "fill" )] = background.fillColor().name();
3174  map[QStringLiteral( "outline" )] = background.strokeColor().name();
3175  map[QStringLiteral( "outline-width" )] = QString::number( background.strokeWidth() );
3176  map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( background.strokeWidthUnit() );
3177 
3178  if ( format.shadow().enabled() && format.shadow().shadowPlacement() == QgsTextShadowSettings::ShadowShape )
3179  {
3180  QgsTextShadowSettings shadow = format.shadow();
3181  // configure SVG shadow specs
3182  QgsStringMap shdwmap( map );
3183  shdwmap[QStringLiteral( "fill" )] = shadow.color().name();
3184  shdwmap[QStringLiteral( "outline" )] = shadow.color().name();
3185  shdwmap[QStringLiteral( "size" )] = QString::number( sizeOut );
3186 
3187  // store SVG's drawing in QPicture for drop shadow call
3188  QPicture svgPict;
3189  QPainter svgp;
3190  svgp.begin( &svgPict );
3191 
3192  // draw shadow symbol
3193 
3194  // clone current render context map unit/mm conversion factors, but not
3195  // other map canvas parameters, then substitute this painter for use in symbology painting
3196  // NOTE: this is because the shadow needs to be scaled correctly for output to map canvas,
3197  // but will be created relative to the SVG's computed size, not the current map canvas
3198  QgsRenderContext shdwContext;
3199  shdwContext.setMapToPixel( context.mapToPixel() );
3200  shdwContext.setScaleFactor( context.scaleFactor() );
3201  shdwContext.setPainter( &svgp );
3202 
3203  QgsSymbolLayer *symShdwL = QgsSvgMarkerSymbolLayer::create( shdwmap );
3204  QgsSvgMarkerSymbolLayer *svgShdwM = static_cast<QgsSvgMarkerSymbolLayer *>( symShdwL );
3205  QgsSymbolRenderContext svgShdwContext( shdwContext, QgsUnitTypes::RenderUnknownUnit, background.opacity() );
3206 
3207  svgShdwM->renderPoint( QPointF( sizeOut / 2, -sizeOut / 2 ), svgShdwContext );
3208  svgp.end();
3209 
3210  component.picture = svgPict;
3211  // TODO: when SVG symbol's stroke width/units is fixed in QgsSvgCache, adjust for it here
3212  component.pictureBuffer = 0.0;
3213 
3214  component.size = QSizeF( sizeOut, sizeOut );
3215  component.offset = QPointF( 0.0, 0.0 );
3216 
3217  // rotate about origin center of SVG
3218  p->save();
3219  p->translate( component.center.x(), component.center.y() );
3220  p->rotate( component.rotation );
3221  double xoff = context.convertToPainterUnits( background.offset().x(), background.offsetUnit(), background.offsetMapUnitScale() );
3222  double yoff = context.convertToPainterUnits( background.offset().y(), background.offsetUnit(), background.offsetMapUnitScale() );
3223  p->translate( QPointF( xoff, yoff ) );
3224  p->rotate( component.rotationOffset );
3225  p->translate( -sizeOut / 2, sizeOut / 2 );
3226  if ( context.flags() & QgsRenderContext::Antialiasing )
3227  {
3228  p->setRenderHint( QPainter::Antialiasing );
3229  }
3230 
3231  drawShadow( context, component, format );
3232  p->restore();
3233 
3234  delete svgShdwM;
3235  svgShdwM = nullptr;
3236  }
3237  renderedSymbol.reset( );
3238 
3240  renderedSymbol.reset( new QgsMarkerSymbol( QgsSymbolLayerList() << symL ) );
3241  }
3242  else
3243  {
3244  renderedSymbol.reset( background.markerSymbol()->clone() );
3245  renderedSymbol->setSize( sizeOut );
3246  renderedSymbol->setSizeUnit( QgsUnitTypes::RenderPixels );
3247  }
3248 
3249  renderedSymbol->setOpacity( background.opacity() );
3250 
3251  // draw the actual symbol
3252  p->save();
3253  if ( context.useAdvancedEffects() )
3254  {
3255  p->setCompositionMode( background.blendMode() );
3256  }
3257  if ( context.flags() & QgsRenderContext::Antialiasing )
3258  {
3259  p->setRenderHint( QPainter::Antialiasing );
3260  }
3261  p->translate( component.center.x(), component.center.y() );
3262  p->rotate( component.rotation );
3263  double xoff = context.convertToPainterUnits( background.offset().x(), background.offsetUnit(), background.offsetMapUnitScale() );
3264  double yoff = context.convertToPainterUnits( background.offset().y(), background.offsetUnit(), background.offsetMapUnitScale() );
3265  p->translate( QPointF( xoff, yoff ) );
3266  p->rotate( component.rotationOffset );
3267 
3268  const QgsFeature f = context.expressionContext().feature();
3269  renderedSymbol->startRender( context, context.expressionContext().fields() );
3270  renderedSymbol->renderPoint( QPointF( 0, 0 ), &f, context );
3271  renderedSymbol->stopRender( context );
3272  p->setCompositionMode( QPainter::CompositionMode_SourceOver ); // just to be sure
3273  p->restore();
3274 
3275  break;
3276  }
3277 
3282  {
3283  double w = component.size.width();
3284  double h = component.size.height();
3285 
3286  if ( background.sizeType() == QgsTextBackgroundSettings::SizeFixed )
3287  {
3288  w = context.convertToPainterUnits( background.size().width(), background.sizeUnit(),
3289  background.sizeMapUnitScale() );
3290  h = context.convertToPainterUnits( background.size().height(), background.sizeUnit(),
3291  background.sizeMapUnitScale() );
3292  }
3293  else if ( background.sizeType() == QgsTextBackgroundSettings::SizeBuffer )
3294  {
3295  if ( background.type() == QgsTextBackgroundSettings::ShapeSquare )
3296  {
3297  if ( w > h )
3298  h = w;
3299  else if ( h > w )
3300  w = h;
3301  }
3302  else if ( background.type() == QgsTextBackgroundSettings::ShapeCircle )
3303  {
3304  // start with label bound by circle
3305  h = std::sqrt( std::pow( w, 2 ) + std::pow( h, 2 ) );
3306  w = h;
3307  }
3308  else if ( background.type() == QgsTextBackgroundSettings::ShapeEllipse )
3309  {
3310  // start with label bound by ellipse
3311  h = h * M_SQRT1_2 * 2;
3312  w = w * M_SQRT1_2 * 2;
3313  }
3314 
3315  double bufferWidth = context.convertToPainterUnits( background.size().width(), background.sizeUnit(),
3316  background.sizeMapUnitScale() );
3317  double bufferHeight = context.convertToPainterUnits( background.size().height(), background.sizeUnit(),
3318  background.sizeMapUnitScale() );
3319 
3320  w += bufferWidth * 2;
3321  h += bufferHeight * 2;
3322  }
3323 
3324  // offsets match those of symbology: -x = left, -y = up
3325  QRectF rect( -w / 2.0, - h / 2.0, w, h );
3326 
3327  if ( rect.isNull() )
3328  return;
3329 
3330  p->save();
3331  if ( context.flags() & QgsRenderContext::Antialiasing )
3332  {
3333  p->setRenderHint( QPainter::Antialiasing );
3334  }
3335  p->translate( QPointF( component.center.x(), component.center.y() ) );
3336  p->rotate( component.rotation );
3337  double xoff = context.convertToPainterUnits( background.offset().x(), background.offsetUnit(), background.offsetMapUnitScale() );
3338  double yoff = context.convertToPainterUnits( background.offset().y(), background.offsetUnit(), background.offsetMapUnitScale() );
3339  p->translate( QPointF( xoff, yoff ) );
3340  p->rotate( component.rotationOffset );
3341 
3342  double penSize = context.convertToPainterUnits( background.strokeWidth(), background.strokeWidthUnit(), background.strokeWidthMapUnitScale() );
3343 
3344  QPen pen;
3345  if ( background.strokeWidth() > 0 )
3346  {
3347  pen.setColor( background.strokeColor() );
3348  pen.setWidthF( penSize );
3349  if ( background.type() == QgsTextBackgroundSettings::ShapeRectangle )
3350  pen.setJoinStyle( background.joinStyle() );
3351  }
3352  else
3353  {
3354  pen = Qt::NoPen;
3355  }
3356 
3357  // store painting in QPicture for shadow drawing
3358  QPicture shapePict;
3359  QPainter shapep;
3360  shapep.begin( &shapePict );
3361  shapep.setPen( pen );
3362  shapep.setBrush( background.fillColor() );
3363 
3364  if ( background.type() == QgsTextBackgroundSettings::ShapeRectangle
3365  || background.type() == QgsTextBackgroundSettings::ShapeSquare )
3366  {
3367  if ( background.radiiUnit() == QgsUnitTypes::RenderPercentage )
3368  {
3369  shapep.drawRoundedRect( rect, background.radii().width(), background.radii().height(), Qt::RelativeSize );
3370  }
3371  else
3372  {
3373  double xRadius = context.convertToPainterUnits( background.radii().width(), background.radiiUnit(), background.radiiMapUnitScale() );
3374  double yRadius = context.convertToPainterUnits( background.radii().height(), background.radiiUnit(), background.radiiMapUnitScale() );
3375  shapep.drawRoundedRect( rect, xRadius, yRadius );
3376  }
3377  }
3378  else if ( background.type() == QgsTextBackgroundSettings::ShapeEllipse
3379  || background.type() == QgsTextBackgroundSettings::ShapeCircle )
3380  {
3381  shapep.drawEllipse( rect );
3382  }
3383  shapep.end();
3384 
3385  if ( format.shadow().enabled() && format.shadow().shadowPlacement() == QgsTextShadowSettings::ShadowShape )
3386  {
3387  component.picture = shapePict;
3388  component.pictureBuffer = penSize / 2.0;
3389 
3390  component.size = rect.size();
3391  component.offset = QPointF( rect.width() / 2, -rect.height() / 2 );
3392  drawShadow( context, component, format );
3393  }
3394 
3395  p->setOpacity( background.opacity() );
3396  if ( context.useAdvancedEffects() )
3397  {
3398  p->setCompositionMode( background.blendMode() );
3399  }
3400 
3401  // scale for any print output or image saving @ specific dpi
3402  p->scale( component.dpiRatio, component.dpiRatio );
3403  _fixQPictureDPI( p );
3404  p->drawPicture( 0, 0, shapePict );
3405  p->restore();
3406  break;
3407  }
3408  }
3409 
3410  if ( background.paintEffect() && background.paintEffect()->enabled() )
3411  {
3412  background.paintEffect()->end( context );
3413  context.setPainter( prevP );
3414  }
3415 }
3416 
3417 void QgsTextRenderer::drawShadow( QgsRenderContext &context, const QgsTextRenderer::Component &component, const QgsTextFormat &format )
3418 {
3419  QgsTextShadowSettings shadow = format.shadow();
3420 
3421  // incoming component sizes should be multiplied by rasterCompressFactor, as
3422  // this allows shadows to be created at paint device dpi (e.g. high resolution),
3423  // then scale device painter by 1.0 / rasterCompressFactor for output
3424 
3425  QPainter *p = context.painter();
3426  double componentWidth = component.size.width(), componentHeight = component.size.height();
3427  double xOffset = component.offset.x(), yOffset = component.offset.y();
3428  double pictbuffer = component.pictureBuffer;
3429 
3430  // generate pixmap representation of label component drawing
3431  bool mapUnits = shadow.blurRadiusUnit() == QgsUnitTypes::RenderMapUnits;
3432  double radius = context.convertToPainterUnits( shadow.blurRadius(), shadow.blurRadiusUnit(), shadow.blurRadiusMapUnitScale() );
3433  radius /= ( mapUnits ? context.scaleFactor() / component.dpiRatio : 1 );
3434  radius = static_cast< int >( radius + 0.5 ); //NOLINT
3435 
3436  // TODO: add labeling gui option to adjust blurBufferClippingScale to minimize pixels, or
3437  // to ensure shadow isn't clipped too tight. (Or, find a better method of buffering)
3438  double blurBufferClippingScale = 3.75;
3439  int blurbuffer = ( radius > 17 ? 16 : radius ) * blurBufferClippingScale;
3440 
3441  QImage blurImg( componentWidth + ( pictbuffer * 2.0 ) + ( blurbuffer * 2.0 ),
3442  componentHeight + ( pictbuffer * 2.0 ) + ( blurbuffer * 2.0 ),
3443  QImage::Format_ARGB32_Premultiplied );
3444 
3445  // TODO: add labeling gui option to not show any shadows under/over a certain size
3446  // keep very small QImages from causing paint device issues, i.e. must be at least > 1
3447  int minBlurImgSize = 1;
3448  // max limitation on QgsSvgCache is 10,000 for screen, which will probably be reasonable for future caching here, too
3449  // 4 x QgsSvgCache limit for output to print/image at higher dpi
3450  // TODO: should it be higher, scale with dpi, or have no limit? Needs testing with very large labels rendered at high dpi output
3451  int maxBlurImgSize = 40000;
3452  if ( blurImg.isNull()
3453  || ( blurImg.width() < minBlurImgSize || blurImg.height() < minBlurImgSize )
3454  || ( blurImg.width() > maxBlurImgSize || blurImg.height() > maxBlurImgSize ) )
3455  return;
3456 
3457  blurImg.fill( QColor( Qt::transparent ).rgba() );
3458  QPainter pictp;
3459  if ( !pictp.begin( &blurImg ) )
3460  return;
3461  pictp.setRenderHints( QPainter::Antialiasing | QPainter::SmoothPixmapTransform );
3462  QPointF imgOffset( blurbuffer + pictbuffer + xOffset,
3463  blurbuffer + pictbuffer + componentHeight + yOffset );
3464 
3465  pictp.drawPicture( imgOffset,
3466  component.picture );
3467 
3468  // overlay shadow color
3469  pictp.setCompositionMode( QPainter::CompositionMode_SourceIn );
3470  pictp.fillRect( blurImg.rect(), shadow.color() );
3471  pictp.end();
3472 
3473  // blur the QImage in-place
3474  if ( shadow.blurRadius() > 0.0 && radius > 0 )
3475  {
3476  QgsSymbolLayerUtils::blurImageInPlace( blurImg, blurImg.rect(), radius, shadow.blurAlphaOnly() );
3477  }
3478 
3479 #if 0
3480  // debug rect for QImage shadow registration and clipping visualization
3481  QPainter picti;
3482  picti.begin( &blurImg );
3483  picti.setBrush( Qt::Dense7Pattern );
3484  QPen imgPen( QColor( 0, 0, 255, 255 ) );
3485  imgPen.setWidth( 1 );
3486  picti.setPen( imgPen );
3487  picti.setOpacity( 0.1 );
3488  picti.drawRect( 0, 0, blurImg.width(), blurImg.height() );
3489  picti.end();
3490 #endif
3491 
3492  double offsetDist = context.convertToPainterUnits( shadow.offsetDistance(), shadow.offsetUnit(), shadow.offsetMapUnitScale() );
3493  double angleRad = shadow.offsetAngle() * M_PI / 180; // to radians
3494  if ( shadow.offsetGlobal() )
3495  {
3496  // TODO: check for differences in rotation origin and cw/ccw direction,
3497  // when this shadow function is used for something other than labels
3498 
3499  // it's 0-->cw-->360 for labels
3500  //QgsDebugMsgLevel( QStringLiteral( "Shadow aggregated label rotation (degrees): %1" ).arg( component.rotation() + component.rotationOffset() ), 4 );
3501  angleRad -= ( component.rotation * M_PI / 180 + component.rotationOffset * M_PI / 180 );
3502  }
3503 
3504  QPointF transPt( -offsetDist * std::cos( angleRad + M_PI_2 ),
3505  -offsetDist * std::sin( angleRad + M_PI_2 ) );
3506 
3507  p->save();
3508  p->setRenderHint( QPainter::SmoothPixmapTransform );
3509  if ( context.flags() & QgsRenderContext::Antialiasing )
3510  {
3511  p->setRenderHint( QPainter::Antialiasing );
3512  }
3513  if ( context.useAdvancedEffects() )
3514  {
3515  p->setCompositionMode( shadow.blendMode() );
3516  }
3517  p->setOpacity( shadow.opacity() );
3518 
3519  double scale = shadow.scale() / 100.0;
3520  // TODO: scale from center/center, left/center or left/top, instead of default left/bottom?
3521  p->scale( scale, scale );
3522  if ( component.useOrigin )
3523  {
3524  p->translate( component.origin.x(), component.origin.y() );
3525  }
3526  p->translate( transPt );
3527  p->translate( -imgOffset.x(),
3528  -imgOffset.y() );
3529  p->drawImage( 0, 0, blurImg );
3530  p->restore();
3531 
3532  // debug rects
3533 #if 0
3534  // draw debug rect for QImage painting registration
3535  p->save();
3536  p->setBrush( Qt::NoBrush );
3537  QPen imgPen( QColor( 255, 0, 0, 10 ) );
3538  imgPen.setWidth( 2 );
3539  imgPen.setStyle( Qt::DashLine );
3540  p->setPen( imgPen );
3541  p->scale( scale, scale );
3542  if ( component.useOrigin() )
3543  {
3544  p->translate( component.origin().x(), component.origin().y() );
3545  }
3546  p->translate( transPt );
3547  p->translate( -imgOffset.x(),
3548  -imgOffset.y() );
3549  p->drawRect( 0, 0, blurImg.width(), blurImg.height() );
3550  p->restore();
3551 
3552  // draw debug rect for passed in component dimensions
3553  p->save();
3554  p->setBrush( Qt::NoBrush );
3555  QPen componentRectPen( QColor( 0, 255, 0, 70 ) );
3556  componentRectPen.setWidth( 1 );
3557  if ( component.useOrigin() )
3558  {
3559  p->translate( component.origin().x(), component.origin().y() );
3560  }
3561  p->setPen( componentRectPen );
3562  p->drawRect( QRect( -xOffset, -componentHeight - yOffset, componentWidth, componentHeight ) );
3563  p->restore();
3564 #endif
3565 }
3566 
3567 void QgsTextRenderer::drawTextInternal( TextPart drawType,
3568  QgsRenderContext &context,
3569  const QgsTextFormat &format,
3570  const Component &component,
3571  const QStringList &textLines,
3572  const QFontMetricsF *fontMetrics,
3573  HAlignment alignment, DrawMode mode )
3574 {
3575  if ( !context.painter() )
3576  {
3577  return;
3578  }
3579 
3580  QPainter *maskPainter = context.maskPainter( context.currentMaskId() );
3581 
3583  double rotation = -component.rotation * 180 / M_PI;
3585  {
3586  // Between 45 to 135 and 235 to 315 degrees, rely on vertical orientation
3587  if ( rotation >= -315 && rotation < -90 )
3588  {
3589  rotation -= 90;
3590  orientation = QgsTextFormat::VerticalOrientation;
3591  }
3592  else if ( rotation >= -90 && rotation < -45 )
3593  {
3594  rotation += 90;
3595  orientation = QgsTextFormat::VerticalOrientation;
3596  }
3597  else
3598  {
3600  }
3601  }
3602 
3603  switch ( orientation )
3604  {
3606  {
3607  double labelWidest = 0.0;
3608  switch ( mode )
3609  {
3610  case Label:
3611  case Point:
3612  for ( const QString &line : textLines )
3613  {
3614  double labelWidth = fontMetrics->width( line );
3615  if ( labelWidth > labelWidest )
3616  {
3617  labelWidest = labelWidth;
3618  }
3619  }
3620  break;
3621 
3622  case Rect:
3623  labelWidest = component.size.width();
3624  break;
3625  }
3626 
3627  double labelHeight = fontMetrics->ascent() + fontMetrics->descent(); // ignore +1 for baseline
3628  // double labelHighest = labelfm->height() + ( double )(( lines - 1 ) * labelHeight * tmpLyr.multilineHeight );
3629 
3630  // needed to move bottom of text's descender to within bottom edge of label
3631  double ascentOffset = 0.25 * fontMetrics->ascent(); // labelfm->descent() is not enough
3632 
3633  int i = 0;
3634 
3635  bool adjustForAlignment = alignment != AlignLeft && ( mode != Label || textLines.size() > 1 );
3636 
3637  const auto constTextLines = textLines;
3638  for ( const QString &line : constTextLines )
3639  {
3640  context.painter()->save();
3641  if ( context.flags() & QgsRenderContext::Antialiasing )
3642  {
3643  context.painter()->setRenderHint( QPainter::Antialiasing );
3644  }
3645  context.painter()->translate( component.origin );
3646  if ( !qgsDoubleNear( rotation, 0.0 ) )
3647  context.painter()->rotate( rotation );
3648 
3649  // apply to the mask painter the same transformations
3650  if ( maskPainter )
3651  {
3652  maskPainter->save();
3653  maskPainter->translate( component.origin );
3654  if ( !qgsDoubleNear( rotation, 0.0 ) )
3655  maskPainter->rotate( rotation );
3656  }
3657 
3658  // figure x offset for horizontal alignment of multiple lines
3659  double xMultiLineOffset = 0.0;
3660  double labelWidth = fontMetrics->width( line );
3661  if ( adjustForAlignment )
3662  {
3663  double labelWidthDiff = labelWidest - labelWidth;
3664  if ( alignment == AlignCenter )
3665  {
3666  labelWidthDiff /= 2;
3667  }
3668  switch ( mode )
3669  {
3670  case Label:
3671  case Rect:
3672  xMultiLineOffset = labelWidthDiff;
3673  break;
3674 
3675  case Point:
3676  if ( alignment == AlignRight )
3677  xMultiLineOffset = labelWidthDiff - labelWidest;
3678  else if ( alignment == AlignCenter )
3679  xMultiLineOffset = labelWidthDiff - labelWidest / 2.0;
3680 
3681  break;
3682  }
3683  //QgsDebugMsgLevel( QStringLiteral( "xMultiLineOffset: %1" ).arg( xMultiLineOffset ), 4 );
3684  }
3685 
3686  double yMultiLineOffset = ascentOffset;
3687  switch ( mode )
3688  {
3689  case Label:
3690  // rendering labels needs special handling - in this case text should be
3691  // drawn with the bottom left corner coinciding with origin, vs top left
3692  // for standard text rendering. Line height is also slightly different.
3693  yMultiLineOffset = - ascentOffset - ( textLines.size() - 1 - i ) * labelHeight * format.lineHeight();
3694  break;
3695 
3696  case Rect:
3697  // standard rendering - designed to exactly replicate QPainter's drawText method
3698  yMultiLineOffset = - ascentOffset + labelHeight - 1 /*baseline*/ + format.lineHeight() * fontMetrics->lineSpacing() * i;
3699  break;
3700 
3701  case Point:
3702  // standard rendering - designed to exactly replicate QPainter's drawText rect method
3703  yMultiLineOffset = 0 - ( textLines.size() - 1 - i ) * fontMetrics->lineSpacing() * format.lineHeight();
3704  break;
3705 
3706  }
3707 
3708  context.painter()->translate( QPointF( xMultiLineOffset, yMultiLineOffset ) );
3709  if ( maskPainter )
3710  maskPainter->translate( QPointF( xMultiLineOffset, yMultiLineOffset ) );
3711 
3712  Component subComponent;
3713  subComponent.text = line;
3714  subComponent.size = QSizeF( labelWidth, labelHeight );
3715  subComponent.offset = QPointF( 0.0, -ascentOffset );
3716  subComponent.rotation = -component.rotation * 180 / M_PI;
3717  subComponent.rotationOffset = 0.0;
3718 
3719  // draw the mask below the text (for preview)
3720  if ( format.mask().enabled() )
3721  {
3722  QgsTextRenderer::drawMask( context, subComponent, format );
3723  }
3724 
3725  if ( drawType == QgsTextRenderer::Buffer )
3726  {
3727  QgsTextRenderer::drawBuffer( context, subComponent, format, fontMetrics );
3728  }
3729  else
3730  {
3731  // draw text, QPainterPath method
3732  QPainterPath path;
3733  path.setFillRule( Qt::WindingFill );
3734  path.addText( 0, 0, format.scaledFont( context ), subComponent.text );
3735 
3736  // store text's drawing in QPicture for drop shadow call
3737  QPicture textPict;
3738  QPainter textp;
3739  textp.begin( &textPict );
3740  textp.setPen( Qt::NoPen );
3741  QColor textColor = format.color();
3742  textColor.setAlphaF( format.opacity() );
3743  textp.setBrush( textColor );
3744  textp.drawPath( path );
3745  // TODO: why are some font settings lost on drawPicture() when using drawText() inside QPicture?
3746  // e.g. some capitalization options, but not others
3747  //textp.setFont( tmpLyr.textFont );
3748  //textp.setPen( tmpLyr.textColor );
3749  //textp.drawText( 0, 0, component.text() );
3750  textp.end();
3751 
3752  if ( format.shadow().enabled() && format.shadow().shadowPlacement() == QgsTextShadowSettings::ShadowText )
3753  {
3754  subComponent.picture = textPict;
3755  subComponent.pictureBuffer = 0.0; // no pen width to deal with
3756  subComponent.origin = QPointF( 0.0, 0.0 );
3757 
3758  QgsTextRenderer::drawShadow( context, subComponent, format );
3759  }
3760 
3761  // paint the text
3762  if ( context.useAdvancedEffects() )
3763  {
3764  context.painter()->setCompositionMode( format.blendMode() );
3765  }
3766 
3767  // scale for any print output or image saving @ specific dpi
3768  context.painter()->scale( subComponent.dpiRatio, subComponent.dpiRatio );
3769 
3770  switch ( context.textRenderFormat() )
3771  {
3773  {
3774  // draw outlined text
3775  _fixQPictureDPI( context.painter() );
3776  context.painter()->drawPicture( 0, 0, textPict );
3777  break;
3778  }
3779 
3781  {
3782  context.painter()->setFont( format.scaledFont( context ) );
3783  QColor textColor = format.color();
3784  textColor.setAlphaF( format.opacity() );
3785  context.painter()->setPen( textColor );
3786  context.painter()->setRenderHint( QPainter::TextAntialiasing );
3787  context.painter()->drawText( 0, 0, subComponent.text );
3788  }
3789  }
3790  }
3791  context.painter()->restore();
3792  if ( maskPainter )
3793  maskPainter->restore();
3794  i++;
3795  }
3796  break;
3797  }
3798 
3801  {
3802  double letterSpacing = format.scaledFont( context ).letterSpacing();
3803 
3804  double labelWidth = fontMetrics->maxWidth(); // label width represents the width of one line of a multi-line label
3805  double actualLabelWidest = labelWidth + ( textLines.size() - 1 ) * labelWidth * format.lineHeight();
3806  double labelWidest = 0.0;
3807  switch ( mode )
3808  {
3809  case Label:
3810  case Point:
3811  labelWidest = actualLabelWidest;
3812  break;
3813 
3814  case Rect:
3815  labelWidest = component.size.width();
3816  break;
3817  }
3818 
3819  int maxLineLength = 0;
3820  for ( auto const &line : textLines )
3821  {
3822  maxLineLength = std::max( maxLineLength, line.length() );
3823  }
3824  double actualLabelHeight = fontMetrics->ascent() + ( fontMetrics->ascent() + letterSpacing ) * ( maxLineLength - 1 );
3825  double ascentOffset = fontMetrics->ascent();
3826 
3827  int i = 0;
3828 
3829  bool adjustForAlignment = alignment != AlignLeft && ( mode != Label || textLines.size() > 1 );
3830 
3831  // apply some character replacement to draw symbols in vertical presentation
3832  QString lines = textLines.join( '\n' );
3833  const auto constTextLines = QgsStringUtils::substituteVerticalCharacters( lines ).split( '\n' );
3834  for ( QString line : constTextLines )
3835  {
3836  context.painter()->save();
3837  if ( context.flags() & QgsRenderContext::Antialiasing )
3838  {
3839  context.painter()->setRenderHint( QPainter::Antialiasing );
3840  }
3841  context.painter()->translate( component.origin );
3842  if ( !qgsDoubleNear( rotation, 0.0 ) )
3843  context.painter()->rotate( rotation );
3844 
3845  // apply to the mask painter the same transformations
3846  if ( maskPainter )
3847  {
3848  maskPainter->save();
3849  maskPainter->translate( component.origin );
3850  if ( !qgsDoubleNear( rotation, 0.0 ) )
3851  maskPainter->rotate( rotation );
3852  }
3853 
3854  // figure x offset of multiple lines
3855  double xOffset = actualLabelWidest - labelWidth - ( i * labelWidth * format.lineHeight() );
3856  if ( adjustForAlignment )
3857  {
3858  double labelWidthDiff = labelWidest - actualLabelWidest;
3859  if ( alignment == AlignCenter )
3860  {
3861  labelWidthDiff /= 2;
3862  }
3863  switch ( mode )
3864  {
3865  case Label:
3866  case Rect:
3867  xOffset += labelWidthDiff;
3868  break;
3869 
3870  case Point:
3871  break;
3872  }
3873  }
3874 
3875  double yOffset = 0.0;
3876  switch ( mode )
3877  {
3878  case Label:
3880  {
3881  if ( rotation >= -405 && rotation < -180 )
3882  {
3883  yOffset = ascentOffset;
3884  }
3885  else if ( rotation >= 0 && rotation < 45 )
3886  {
3887  xOffset -= actualLabelWidest;
3888  yOffset = -actualLabelHeight + ascentOffset + fontMetrics->descent();
3889  }
3890  }
3891  else
3892  {
3893  yOffset = -actualLabelHeight + ascentOffset;
3894  }
3895  break;
3896 
3897  case Point:
3898  yOffset = -actualLabelHeight + ascentOffset;
3899  break;
3900 
3901  case Rect:
3902  yOffset = ascentOffset;
3903  break;
3904  }
3905 
3906  context.painter()->translate( QPointF( xOffset, yOffset ) );
3907 
3908  double labelHeight = fontMetrics->ascent() + ( fontMetrics->ascent() + letterSpacing ) * ( line.length() - 1 );
3909 
3910  Component subComponent;
3911  subComponent.text = line;
3912  subComponent.size = QSizeF( labelWidth, labelHeight );
3913  subComponent.offset = QPointF( 0.0, 0.0 );
3914  subComponent.rotation = -component.rotation * 180 / M_PI;
3915  subComponent.rotationOffset = 0.0;
3916 
3917  // draw the mask below the text (for preview)
3918  if ( format.mask().enabled() )
3919  {
3920  QgsTextRenderer::drawMask( context, subComponent, format );
3921  }
3922 
3923  if ( drawType == QgsTextRenderer::Buffer )
3924  {
3925  QgsTextRenderer::drawBuffer( context, subComponent, format, fontMetrics );
3926  }
3927  else
3928  {
3929  // draw text, QPainterPath method
3930  QPainterPath path;
3931  path.setFillRule( Qt::WindingFill );
3932  const QStringList parts = QgsPalLabeling::splitToGraphemes( subComponent.text );
3933  double partYOffset = 0.0;
3934  for ( const auto &part : parts )
3935  {
3936  double partXOffset = ( labelWidth - ( fontMetrics->width( part ) - letterSpacing ) ) / 2;
3937  path.addText( partXOffset, partYOffset, format.scaledFont( context ), part );
3938  partYOffset += fontMetrics->ascent() + letterSpacing;
3939  }
3940 
3941  // store text's drawing in QPicture for drop shadow call
3942  QPicture textPict;
3943  QPainter textp;
3944  textp.begin( &textPict );
3945  textp.setPen( Qt::NoPen );
3946  QColor textColor = format.color();
3947  textColor.setAlphaF( format.opacity() );
3948  textp.setBrush( textColor );
3949  textp.drawPath( path );
3950  // TODO: why are some font settings lost on drawPicture() when using drawText() inside QPicture?
3951  // e.g. some capitalization options, but not others
3952  //textp.setFont( tmpLyr.textFont );
3953  //textp.setPen( tmpLyr.textColor );
3954  //textp.drawText( 0, 0, component.text() );
3955  textp.end();
3956 
3957  if ( format.shadow().enabled() && format.shadow().shadowPlacement() == QgsTextShadowSettings::ShadowText )
3958  {
3959  subComponent.picture = textPict;
3960  subComponent.pictureBuffer = 0.0; // no pen width to deal with
3961  subComponent.origin = QPointF( 0.0, 0.0 );
3962 
3963  QgsTextRenderer::drawShadow( context, subComponent, format );
3964  }
3965 
3966  // paint the text
3967  if ( context.useAdvancedEffects() )
3968  {
3969  context.painter()->setCompositionMode( format.blendMode() );
3970  }
3971 
3972  // scale for any print output or image saving @ specific dpi
3973  context.painter()->scale( subComponent.dpiRatio, subComponent.dpiRatio );
3974 
3975  switch ( context.textRenderFormat() )
3976  {
3978  {
3979  // draw outlined text
3980  _fixQPictureDPI( context.painter() );
3981  context.painter()->drawPicture( 0, 0, textPict );
3982  break;
3983  }
3984 
3986  {
3987  context.painter()->setFont( format.scaledFont( context ) );
3988  QColor textColor = format.color();
3989  textColor.setAlphaF( format.opacity() );
3990  context.painter()->setPen( textColor );
3991  context.painter()->setRenderHint( QPainter::TextAntialiasing );
3992  context.painter()->drawText( 0, 0, subComponent.text );
3993  }
3994  }
3995  }
3996  context.painter()->restore();
3997  if ( maskPainter )
3998  maskPainter->restore();
3999  i++;
4000  }
4001  break;
4002  }
4003  }
4004 }
4005 
4006 
4007 //
4008 // QgsTextRendererUtils
4009 //
4010 
4012 {
4014  const QString skind = string.trimmed();
4015 
4016  if ( skind.compare( QLatin1String( "Square" ), Qt::CaseInsensitive ) == 0 )
4017  {
4019  }
4020  else if ( skind.compare( QLatin1String( "Ellipse" ), Qt::CaseInsensitive ) == 0 )
4021  {
4023  }
4024  else if ( skind.compare( QLatin1String( "Circle" ), Qt::CaseInsensitive ) == 0 )
4025  {
4027  }
4028  else if ( skind.compare( QLatin1String( "SVG" ), Qt::CaseInsensitive ) == 0 )
4029  {
4031  }
4032  else if ( skind.compare( QLatin1String( "marker" ), Qt::CaseInsensitive ) == 0 )
4033  {
4035  }
4036  return shpkind;
4037 }
4038 
4040 {
4041  const QString stype = string.trimmed();
4042  // "Buffer"
4044 
4045  if ( stype.compare( QLatin1String( "Fixed" ), Qt::CaseInsensitive ) == 0 )
4046  {
4048  }
4049  return sizType;
4050 }
4051 
4053 {
4054  const QString rotstr = string.trimmed();
4055  // "Sync"
4057 
4058  if ( rotstr.compare( QLatin1String( "Offset" ), Qt::CaseInsensitive ) == 0 )
4059  {
4061  }
4062  else if ( rotstr.compare( QLatin1String( "Fixed" ), Qt::CaseInsensitive ) == 0 )
4063  {
4065  }
4066  return rottype;
4067 }
4068 
4070 {
4071  const QString str = string.trimmed();
4072  // "Lowest"
4074 
4075  if ( str.compare( QLatin1String( "Text" ), Qt::CaseInsensitive ) == 0 )
4076  {
4078  }
4079  else if ( str.compare( QLatin1String( "Buffer" ), Qt::CaseInsensitive ) == 0 )
4080  {
4082  }
4083  else if ( str.compare( QLatin1String( "Background" ), Qt::CaseInsensitive ) == 0 )
4084  {
4086  }
4087  return shdwtype;
4088 }
4089 
4091 {
4092  switch ( orientation )
4093  {
4095  return QStringLiteral( "horizontal" );
4097  return QStringLiteral( "vertical" );
4099  return QStringLiteral( "rotation-based" );
4100  }
4101  return QString();
4102 }
4103 
4105 {
4106  if ( ok )
4107  *ok = true;
4108 
4109  QString cleaned = name.toLower().trimmed();
4110 
4111  if ( cleaned == QLatin1String( "horizontal" ) )
4113  else if ( cleaned == QLatin1String( "vertical" ) )
4115  else if ( cleaned == QLatin1String( "rotation-based" ) )
4117 
4118  if ( ok )
4119  *ok = false;
4121 }
TextOrientation
Text orientation.
QPainter::CompositionMode blendMode() const
Returns the blending mode used for drawing the buffer.
static const QgsPropertiesDefinition & propertyDefinitions()
Returns the labeling property definitions.
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.
QColor valueAsColor(int key, const QgsExpressionContext &context, const QColor &defaultColor=QColor(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a color...
Meters value as Map units.
Definition: qgsunittypes.h:154
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.
const QgsPathResolver & pathResolver() const
Returns the path resolver for conversion between relative and absolute paths during rendering operati...
void setOffsetMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale object for the shadow offset distance.
Horizontally or vertically oriented text based on rotation (only available for map labeling) ...
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.
static QPointF toPoint(const QVariant &value, bool *ok=nullptr)
Converts a value to a point.
void readXml(const QDomElement &elem)
Read settings from a DOM element.
MaskType
Mask shape types.
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.
void setOrientation(TextOrientation orientation)
Sets the orientation for the text.
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.
static QgsTextBackgroundSettings::RotationType decodeBackgroundRotationType(const QString &string)
Decodes a string representation of a background rotation type to a type.
double valueAsDouble(int key, const QgsExpressionContext &context, double defaultValue=0.0, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a double...
QFont toQFont() const
Returns a QFont matching the relevant settings from this text format.
QgsTextShadowSettings & operator=(const QgsTextShadowSettings &other)
static QString substituteVerticalCharacters(QString string)
Returns a string with characters having vertical representation form substituted. ...
void readXml(const QDomElement &elem)
Read settings from a DOM element.
Use antialiasing while drawing.
void setPreviewBackgroundColor(const QColor &color)
Sets the background color that text will be rendered on for previews.
Draw shadow under buffer.
ShadowPlacement
Placement positions for text shadow.
Always render text as text objects.
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).
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:315
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 setPaintEffect(QgsPaintEffect *effect)
Sets the current paint effect for the mask.
void setOffsetUnit(QgsUnitTypes::RenderUnit units)
Sets the units used for the shape&#39;s offset.
static QgsTextBackgroundSettings::SizeType decodeBackgroundSizeType(const QString &string)
Decodes a string representation of a background size type to a type.
QColor color() const
Returns the color that text will be rendered in.
double size() const
Returns the size of the buffer.
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)
Mixed or unknown units.
Definition: qgsunittypes.h:153
static QPainter::CompositionMode getCompositionMode(QgsPainting::BlendMode blendMode)
Returns a QPainter::CompositionMode corresponding to a BlendMode.
Definition: qgspainting.cpp:20
Flags flags() const
Returns combination of flags used for rendering.
static QDomElement saveSymbol(const QString &symbolName, const QgsSymbol *symbol, QDomDocument &doc, const QgsReadWriteContext &context)
Writes a symbol definition to XML.
QgsFields fields() const
Convenience function for retrieving the fields for the context, if set.
void setType(MaskType type)
Sets the type of mask shape.
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.
TextRenderFormat textRenderFormat() const
Returns the text render format, which dictates how text is rendered (e.g.
static QgsTextFormat fromQFont(const QFont &font)
Returns a text format matching the settings from an input font.
void setMarkerSymbol(QgsMarkerSymbol *symbol)
Sets the current marker symbol for the background shape.
Qt::PenJoinStyle joinStyle() const
Returns the buffer join style.
void updateDataDefinedProperties(QgsRenderContext &context, const QgsPropertyCollection &properties)
Updates the format by evaluating current values of data defined properties.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
Container for settings relating to a text background object.
Percentage of another measurement (e.g., canvas size, feature size)
Definition: qgsunittypes.h:150
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.
QgsMapUnitScale sizeMapUnitScale() const
Returns the map unit scale object for the buffer size.
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:694
void setBlurRadiusMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale object for the shadow blur radius.
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
A marker symbol type, for rendering Point and MultiPoint geometries.
Definition: qgssymbol.h:895
void readXml(const QDomElement &elem)
Read settings from a DOM element.
static Qt::PenJoinStyle decodePenJoinStyle(const QString &str)
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.
bool isActive(int key) const override
Returns true if the collection contains an active property with the specified key.
Qt::PenJoinStyle joinStyle() const
Returns the buffer join style.
QgsSymbolLayerReferenceList maskedSymbolLayers() const
Returns a list of references to symbol layers that are masked by this buffer.
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).
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
static QSizeF toSize(const QVariant &value, bool *ok=nullptr)
Converts a value to a size.
QVariant value(int key, const QgsExpressionContext &context, const QVariant &defaultValue=QVariant()) const override
Returns the calculated value of the property with the specified key from within the collection...
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:37
void readFromLayer(QgsVectorLayer *layer)
Reads settings from a layer&#39;s custom properties (for QGIS 2.x projects).
static QString svgSymbolPathToName(const QString &path, const QgsPathResolver &pathResolver)
Determines an SVG symbol&#39;s name from its path.
void setBlendMode(QPainter::CompositionMode mode)
Sets the blending mode used for drawing the background shape.
void updateDataDefinedProperties(QgsRenderContext &context, const QgsPropertyCollection &properties)
Updates the format by evaluating current values of data defined properties.
Shape rotation is a fixed angle.
static QString encodeTextOrientation(QgsTextFormat::TextOrientation orientation)
Encodes a text orientation.
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.
void setUseAdvancedEffects(bool enabled)
Used to enable or disable advanced effects such as blend modes.
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.
bool isGuiPreview() const
Returns the Gui preview mode.
static QgsTextFormat::TextOrientation decodeTextOrientation(const QString &name, bool *ok=nullptr)
Attempts to decode a string representation of a text orientation.
Text within rectangle draw mode.
#define FALLTHROUGH
Definition: qgis.h:763
QColor previewBackgroundColor() const
Returns the background color for text previews.
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.
TextOrientation orientation() const
Returns the orientation of the text.
double opacity() const
Returns the mask&#39;s opacity.
static bool fontFamilyOnSystem(const QString &family)
Check whether font family is on system in a quick manner, which does not compare [foundry].
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.
Always render text using path objects (AKA outlines/curves).
void setBlendMode(QPainter::CompositionMode mode)
Sets the blending mode used for drawing the drop shadow.
static QPixmap textFormatPreviewPixmap(const QgsTextFormat &format, QSize size, const QString &previewText=QString(), int padding=0)
Returns a pixmap preview for a text format.
void setColor(const QColor &color)
Sets the color for the drop shadow.
QList< QgsSymbolLayerReference > QgsSymbolLayerReferenceList
QgsTextMaskSettings & operator=(const QgsTextMaskSettings &other)
Copy constructor.
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.
QList< QgsSymbolLayer * > QgsSymbolLayerList
Definition: qgssymbol.h:52
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.
QgsTextMaskSettings & mask()
Returns a reference to the masking settings.
static QPainter::CompositionMode decodeBlendMode(const QString &s)
void updateDataDefinedProperties(QgsRenderContext &context)
Updates the format by evaluating current values of data defined properties.
bool enabled() const
Returns whether the effect is enabled.
Q_GUI_EXPORT int qt_defaultDpiY()
static QgsTextShadowSettings::ShadowPlacement decodeShadowPlacementType(const QString &string)
Decodes a string representation of a shadow placement type to a type.
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.
QPainter * maskPainter(int id=0)
Returns a mask QPainter for the render operation.
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.
static QFontMetricsF fontMetrics(QgsRenderContext &context, const QgsTextFormat &format)
Returns the font metrics for the given text format, when rendered in the specified render context...
void setJoinStyle(Qt::PenJoinStyle style)
Sets the join style used for drawing the buffer.
void setDataDefinedProperties(const QgsPropertyCollection &collection)
Sets the format&#39;s property collection, used for data defined overrides.
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 setSizeMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale object for the buffer size.
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.
QgsExpressionContext & expressionContext()
Gets the expression context.
void setMaskedSymbolLayers(QgsSymbolLayerReferenceList maskedLayers)
Sets the symbol layers that will be masked by this buffer.
QgsUnitTypes::RenderUnit offsetUnit() const
Returns the units used for the shape&#39;s offset.
void setParameters(double mapUnitsPerPixel, double centerX, double centerY, int widthPixels, int heightPixels, double rotation)
Set parameters for use in transforming coordinates.
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.
bool valueAsBool(int key, const QgsExpressionContext &context, bool defaultValue=false, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as an boolean...
TextPart
Components of text.
void updateDataDefinedProperties(QgsRenderContext &context, const QgsPropertyCollection &properties)
Updates the format by evaluating current values of data defined properties.
QgsPaintEffect * paintEffect() const
Returns the current paint effect for the mask.
static QString untranslateNamedStyle(const QString &namedStyle)
Returns the english named style of a font, if possible.
QgsSymbolLayerReferenceList stringToSymbolLayerReferenceList(const QString &str)
Utilitary function to parse a string originated from symbolLayerReferenceListToString into a QgsSymbo...
const QgsPathResolver & pathResolver() const
Returns path resolver for conversion between relative and absolute paths.
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
Returns the context&#39;s map to pixel transform, which transforms between map coordinates and device coo...
static void blurImageInPlace(QImage &image, QRect rect, int radius, bool alphaOnly)
Blurs an image in place, e.g. creating Qt-independent drop shadows.
Points (e.g., for font sizes)
Definition: qgsunittypes.h:151
QgsUnitTypes::RenderUnit offsetUnit() const
Returns the units used for the shadow&#39;s offset.
bool enabled() const
Returns whether the shadow is enabled.
void updateDataDefinedProperties(QgsRenderContext &context, const QgsPropertyCollection &properties)
Updates the format by evaluating current values of data defined properties.
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.
QDomElement writeXml(QDomDocument &doc) const
Write settings into a DOM element.
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)
Sets the context&#39;s map to pixel transform, which transforms between map coordinates and device coordi...
static QStringList splitToGraphemes(const QString &text)
Splits a text string to a list of graphemes, which are the smallest allowable character divisions in ...
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the format&#39;s property collection, used for data defined overrides.
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.
QString symbolLayerReferenceListToString(const QgsSymbolLayerReferenceList &lst)
Utilitary function to turn a QgsSymbolLayerReferenceList into a string.
MaskType type() const
Returns the type of mask shape.
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.
QgsFeature feature() const
Convenience function for retrieving the feature for the context, if set.
void setShadowPlacement(QgsTextShadowSettings::ShadowPlacement placement)
Sets the placement for the drop shadow.
A grouped map of multiple QgsProperty objects, each referenced by a integer key value.
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.
bool hasActiveProperties() const override
Returns true if the collection has any active properties, or false if all properties within the colle...
Whether the mask is enabled.
void setEnabled(bool)
Returns whether the mask is enabled.
QgsPaintEffect * paintEffect() const
Returns the current paint effect for the buffer.
Q_INVOKABLE 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.
A class to manage painter saving and restoring required for drawing on a different painter (mask pain...
static QString svgSymbolNameToPath(const QString &name, const QgsPathResolver &pathResolver)
Determines an SVG symbol&#39;s path from its name.
QgsUnitTypes::RenderUnit sizeUnit() const
Returns the units for the buffer size.
void setOpacity(double opacity)
Sets the mask&#39;s opacity.
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.
static double textWidth(const QgsRenderContext &context, const QgsTextFormat &format, const QStringList &textLines, QFontMetricsF *fontMetrics=nullptr)
Returns the width of a text based on a given format.
void setSize(double size)
Sets the size of the buffer.
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.
DrawMode
Draw mode to calculate width and height.
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.
int valueAsInt(int key, const QgsExpressionContext &context, int defaultValue=0, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as an integer...
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for the context.
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...
void setSizeUnit(QgsUnitTypes::RenderUnit unit)
Sets the units used for the buffer size.
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.
A class to manager painter saving and restoring required for effect drawing.
bool enabled() const
Returns whether the mask is enabled.
virtual void begin(QgsRenderContext &context)
Begins intercepting paint operations to a render context.
Container for settings relating to a selective masking around a text.
double opacity() const
Returns the shadow&#39;s opacity.
QgsMarkerSymbol * clone() const override
Returns a deep copy of this symbol.
Definition: qgssymbol.cpp:1766
void setOpacity(double opacity)
Sets the background shape&#39;s opacity.
RenderUnit
Rendering size units.
Definition: qgsunittypes.h:145
static QColor decodeColor(const QString &str)
QgsMarkerSymbol * markerSymbol() const
Returns the marker symbol to be rendered in the background.
int currentMaskId() const
Returns the current mask id, which can be used with maskPainter()
void setPaintEffect(QgsPaintEffect *effect)
Sets the current paint effect for the background shape.
Horizontally oriented text.
void setOffsetAngle(int angle)
Sets the angle for offsetting the position of the shadow from the text.
static QgsTextBackgroundSettings::ShapeType decodeShapeType(const QString &string)
Decodes a string representation of a background shape type to a type.
static double textHeight(const QgsRenderContext &context, const QgsTextFormat &format, const QStringList &textLines, DrawMode mode, QFontMetricsF *fontMetrics=nullptr)
Returns the height of a text based on a given format.
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.