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