QGIS API Documentation  3.2.0-Bonn (bc43194)
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 drawAsOutlines )
1712 {
1713  QgsTextFormat tmpFormat = updateShadowPosition( format );
1714 
1715  if ( tmpFormat.background().enabled() )
1716  {
1717  drawPart( rect, rotation, alignment, textLines, context, tmpFormat, Background, drawAsOutlines );
1718  }
1719 
1720  if ( tmpFormat.buffer().enabled() )
1721  {
1722  drawPart( rect, rotation, alignment, textLines, context, tmpFormat, Buffer, drawAsOutlines );
1723  }
1724 
1725  drawPart( rect, rotation, alignment, textLines, context, tmpFormat, Text, drawAsOutlines );
1726 }
1727 
1728 void QgsTextRenderer::drawText( QPointF point, double rotation, QgsTextRenderer::HAlignment alignment, const QStringList &textLines, QgsRenderContext &context, const QgsTextFormat &format, bool drawAsOutlines )
1729 {
1730  QgsTextFormat tmpFormat = updateShadowPosition( format );
1731 
1732  if ( tmpFormat.background().enabled() )
1733  {
1734  drawPart( point, rotation, alignment, textLines, context, tmpFormat, Background, drawAsOutlines );
1735  }
1736 
1737  if ( tmpFormat.buffer().enabled() )
1738  {
1739  drawPart( point, rotation, alignment, textLines, context, tmpFormat, Buffer, drawAsOutlines );
1740  }
1741 
1742  drawPart( point, rotation, alignment, textLines, context, tmpFormat, Text, drawAsOutlines );
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 drawAsOutlines )
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  drawAsOutlines );
1826  break;
1827  }
1828  }
1829 }
1830 
1831 void QgsTextRenderer::drawPart( QPointF origin, double rotation, QgsTextRenderer::HAlignment alignment, const QStringList &textLines, QgsRenderContext &context, const QgsTextFormat &format, QgsTextRenderer::TextPart part, bool drawAsOutlines )
1832 {
1833  if ( !context.painter() )
1834  {
1835  return;
1836  }
1837 
1838  Component component;
1839  component.dpiRatio = 1.0;
1840  component.origin = origin;
1841  component.rotation = rotation;
1842  component.hAlign = alignment;
1843 
1844  switch ( part )
1845  {
1846  case Background:
1847  {
1848  if ( !format.background().enabled() )
1849  return;
1850 
1851  QgsTextRenderer::drawBackground( context, component, format, textLines, Point );
1852  break;
1853  }
1854 
1855  case Buffer:
1856  {
1857  if ( !format.buffer().enabled() )
1858  break;
1859  }
1860  FALLTHROUGH;
1861  case Text:
1862  case Shadow:
1863  {
1864  QFontMetricsF fm( format.scaledFont( context ) );
1865  drawTextInternal( part, context, format, component,
1866  textLines,
1867  &fm,
1868  alignment,
1869  drawAsOutlines,
1870  Point );
1871  break;
1872  }
1873  }
1874 }
1875 
1876 QFontMetricsF QgsTextRenderer::fontMetrics( QgsRenderContext &context, const QgsTextFormat &format )
1877 {
1878  return QFontMetricsF( format.scaledFont( context ), context.painter() ? context.painter()->device() : nullptr );
1879 }
1880 
1881 void QgsTextRenderer::drawBuffer( QgsRenderContext &context, const QgsTextRenderer::Component &component, const QgsTextFormat &format )
1882 {
1883  QPainter *p = context.painter();
1884 
1885  QgsTextBufferSettings buffer = format.buffer();
1886 
1887  double penSize = context.convertToPainterUnits( buffer.size(), buffer.sizeUnit(), buffer.sizeMapUnitScale() );
1888 
1889  QPainterPath path;
1890  path.setFillRule( Qt::WindingFill );
1891  path.addText( 0, 0, format.scaledFont( context ), component.text );
1892  QColor bufferColor = buffer.color();
1893  bufferColor.setAlphaF( buffer.opacity() );
1894  QPen pen( bufferColor );
1895  pen.setWidthF( penSize );
1896  pen.setJoinStyle( buffer.joinStyle() );
1897  QColor tmpColor( bufferColor );
1898  // honor pref for whether to fill buffer interior
1899  if ( !buffer.fillBufferInterior() )
1900  {
1901  tmpColor.setAlpha( 0 );
1902  }
1903 
1904  // store buffer's drawing in QPicture for drop shadow call
1905  QPicture buffPict;
1906  QPainter buffp;
1907  buffp.begin( &buffPict );
1908 
1909  if ( buffer.paintEffect() && buffer.paintEffect()->enabled() )
1910  {
1911  context.setPainter( &buffp );
1912 
1913  buffer.paintEffect()->begin( context );
1914  context.painter()->setPen( pen );
1915  context.painter()->setBrush( tmpColor );
1916  context.painter()->drawPath( path );
1917  buffer.paintEffect()->end( context );
1918 
1919  context.setPainter( p );
1920  }
1921  else
1922  {
1923  buffp.setPen( pen );
1924  buffp.setBrush( tmpColor );
1925  buffp.drawPath( path );
1926  }
1927  buffp.end();
1928 
1929  if ( format.shadow().enabled() && format.shadow().shadowPlacement() == QgsTextShadowSettings::ShadowBuffer )
1930  {
1931  QgsTextRenderer::Component bufferComponent = component;
1932  bufferComponent.origin = QPointF( 0.0, 0.0 );
1933  bufferComponent.picture = buffPict;
1934  bufferComponent.pictureBuffer = penSize / 2.0;
1935  drawShadow( context, bufferComponent, format );
1936  }
1937  p->save();
1938  if ( context.useAdvancedEffects() )
1939  {
1940  p->setCompositionMode( buffer.blendMode() );
1941  }
1942  if ( context.flags() & QgsRenderContext::Antialiasing )
1943  {
1944  p->setRenderHint( QPainter::Antialiasing );
1945  }
1946 
1947  // scale for any print output or image saving @ specific dpi
1948  p->scale( component.dpiRatio, component.dpiRatio );
1949  _fixQPictureDPI( p );
1950  p->drawPicture( 0, 0, buffPict );
1951  p->restore();
1952 }
1953 
1954 double QgsTextRenderer::textWidth( const QgsRenderContext &context, const QgsTextFormat &format, const QStringList &textLines, QFontMetricsF *fontMetrics )
1955 {
1956  //calculate max width of text lines
1957  std::unique_ptr< QFontMetricsF > newFm;
1958  if ( !fontMetrics )
1959  {
1960  newFm.reset( new QFontMetricsF( format.scaledFont( context ) ) );
1961  fontMetrics = newFm.get();
1962  }
1963 
1964  double maxWidth = 0;
1965  Q_FOREACH ( const QString &line, textLines )
1966  {
1967  maxWidth = std::max( maxWidth, fontMetrics->width( line ) );
1968  }
1969  return maxWidth;
1970 }
1971 
1972 double QgsTextRenderer::textHeight( const QgsRenderContext &context, const QgsTextFormat &format, const QStringList &textLines, DrawMode mode, QFontMetricsF *fontMetrics )
1973 {
1974  //calculate max width of text lines
1975  std::unique_ptr< QFontMetricsF > newFm;
1976  if ( !fontMetrics )
1977  {
1978  newFm.reset( new QFontMetricsF( format.scaledFont( context ) ) );
1979  fontMetrics = newFm.get();
1980  }
1981 
1982  double labelHeight = fontMetrics->ascent() + fontMetrics->descent(); // ignore +1 for baseline
1983 
1984  switch ( mode )
1985  {
1986  case Label:
1987  // rendering labels needs special handling - in this case text should be
1988  // drawn with the bottom left corner coinciding with origin, vs top left
1989  // for standard text rendering. Line height is also slightly different.
1990  return labelHeight + ( textLines.size() - 1 ) * labelHeight * format.lineHeight();
1991 
1992  case Rect:
1993  case Point:
1994  // standard rendering - designed to exactly replicate QPainter's drawText method
1995  return labelHeight + ( textLines.size() - 1 ) * fontMetrics->lineSpacing() * format.lineHeight();
1996  }
1997 
1998  return 0;
1999 }
2000 
2001 void QgsTextRenderer::drawBackground( QgsRenderContext &context, QgsTextRenderer::Component component, const QgsTextFormat &format,
2002  const QStringList &textLines, DrawMode mode )
2003 {
2005 
2006  QPainter *prevP = context.painter();
2007  QPainter *p = context.painter();
2008  if ( background.paintEffect() && background.paintEffect()->enabled() )
2009  {
2010  background.paintEffect()->begin( context );
2011  p = context.painter();
2012  }
2013 
2014  //QgsDebugMsgLevel( QString( "Background label rotation: %1" ).arg( component.rotation() ), 4 );
2015 
2016  // shared calculations between shapes and SVG
2017 
2018  // configure angles, set component rotation and rotationOffset
2020  {
2021  component.rotation = -( component.rotation * 180 / M_PI ); // RotationSync
2022  component.rotationOffset =
2023  background.rotationType() == QgsTextBackgroundSettings::RotationOffset ? background.rotation() : 0.0;
2024  }
2025  else // RotationFixed
2026  {
2027  component.rotation = 0.0; // don't use label's rotation
2028  component.rotationOffset = background.rotation();
2029  }
2030 
2031  if ( mode != Label )
2032  {
2033  // need to calculate size of text
2034  QFontMetricsF fm( format.scaledFont( context ) );
2035  double width = textWidth( context, format, textLines, &fm );
2036  double height = textHeight( context, format, textLines, mode, &fm );
2037 
2038  switch ( mode )
2039  {
2040  case Rect:
2041  switch ( component.hAlign )
2042  {
2043  case AlignLeft:
2044  component.center = QPointF( component.origin.x() + width / 2.0,
2045  component.origin.y() + height / 2.0 );
2046  break;
2047 
2048  case AlignCenter:
2049  component.center = QPointF( component.origin.x() + component.size.width() / 2.0,
2050  component.origin.y() + height / 2.0 );
2051  break;
2052 
2053  case AlignRight:
2054  component.center = QPointF( component.origin.x() + component.size.width() - width / 2.0,
2055  component.origin.y() + height / 2.0 );
2056  break;
2057  }
2058  break;
2059 
2060  case Point:
2061  {
2062  double originAdjust = fm.ascent() / 2.0 - fm.leading() / 2.0;
2063  switch ( component.hAlign )
2064  {
2065  case AlignLeft:
2066  component.center = QPointF( component.origin.x() + width / 2.0,
2067  component.origin.y() - height / 2.0 + originAdjust );
2068  break;
2069 
2070  case AlignCenter:
2071  component.center = QPointF( component.origin.x(),
2072  component.origin.y() - height / 2.0 + originAdjust );
2073  break;
2074 
2075  case AlignRight:
2076  component.center = QPointF( component.origin.x() - width / 2.0,
2077  component.origin.y() - height / 2.0 + originAdjust );
2078  break;
2079  }
2080  }
2081 
2082  case Label:
2083  break;
2084  }
2085 
2087  component.size = QSizeF( width, height );
2088  }
2089 
2090  // TODO: the following label-buffered generated shapes and SVG symbols should be moved into marker symbology classes
2091 
2092  if ( background.type() == QgsTextBackgroundSettings::ShapeSVG )
2093  {
2094  // all calculations done in shapeSizeUnits, which are then passed to symbology class for painting
2095 
2096  if ( background.svgFile().isEmpty() )
2097  return;
2098 
2099  double sizeOut = 0.0;
2100  // only one size used for SVG sizing/scaling (no use of shapeSize.y() or Y field in gui)
2101  if ( background.sizeType() == QgsTextBackgroundSettings::SizeFixed )
2102  {
2103  sizeOut = context.convertToPainterUnits( background.size().width(), background.sizeUnit(), background.sizeMapUnitScale() );
2104  }
2105  else if ( background.sizeType() == QgsTextBackgroundSettings::SizeBuffer )
2106  {
2107  sizeOut = std::max( component.size.width(), component.size.height() );
2108  double bufferSize = context.convertToPainterUnits( background.size().width(), background.sizeUnit(), background.sizeMapUnitScale() );
2109 
2110  // add buffer
2111  sizeOut += bufferSize * 2;
2112  }
2113 
2114  // don't bother rendering symbols smaller than 1x1 pixels in size
2115  // TODO: add option to not show any svgs under/over a certain size
2116  if ( sizeOut < 1.0 )
2117  return;
2118 
2119  QgsStringMap map; // for SVG symbology marker
2120  map[QStringLiteral( "name" )] = background.svgFile().trimmed();
2121  map[QStringLiteral( "size" )] = QString::number( sizeOut );
2122  map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( QgsUnitTypes::RenderPixels );
2123  map[QStringLiteral( "angle" )] = QString::number( 0.0 ); // angle is handled by this local painter
2124 
2125  // offset is handled by this local painter
2126  // TODO: see why the marker renderer doesn't seem to translate offset *after* applying rotation
2127  //map["offset"] = QgsSymbolLayerUtils::encodePoint( tmpLyr.shapeOffset );
2128  //map["offset_unit"] = QgsUnitTypes::encodeUnit(
2129  // tmpLyr.shapeOffsetUnits == QgsPalLayerSettings::MapUnits ? QgsUnitTypes::MapUnit : QgsUnitTypes::MM );
2130 
2131  map[QStringLiteral( "fill" )] = background.fillColor().name();
2132  map[QStringLiteral( "outline" )] = background.strokeColor().name();
2133  map[QStringLiteral( "outline-width" )] = QString::number( background.strokeWidth() );
2134  map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( background.strokeWidthUnit() );
2135 
2136  if ( format.shadow().enabled() && format.shadow().shadowPlacement() == QgsTextShadowSettings::ShadowShape )
2137  {
2138  QgsTextShadowSettings shadow = format.shadow();
2139  // configure SVG shadow specs
2140  QgsStringMap shdwmap( map );
2141  shdwmap[QStringLiteral( "fill" )] = shadow.color().name();
2142  shdwmap[QStringLiteral( "outline" )] = shadow.color().name();
2143  shdwmap[QStringLiteral( "size" )] = QString::number( sizeOut );
2144 
2145  // store SVG's drawing in QPicture for drop shadow call
2146  QPicture svgPict;
2147  QPainter svgp;
2148  svgp.begin( &svgPict );
2149 
2150  // draw shadow symbol
2151 
2152  // clone current render context map unit/mm conversion factors, but not
2153  // other map canvas parameters, then substitute this painter for use in symbology painting
2154  // NOTE: this is because the shadow needs to be scaled correctly for output to map canvas,
2155  // but will be created relative to the SVG's computed size, not the current map canvas
2156  QgsRenderContext shdwContext;
2157  shdwContext.setMapToPixel( context.mapToPixel() );
2158  shdwContext.setScaleFactor( context.scaleFactor() );
2159  shdwContext.setPainter( &svgp );
2160 
2161  QgsSymbolLayer *symShdwL = QgsSvgMarkerSymbolLayer::create( shdwmap );
2162  QgsSvgMarkerSymbolLayer *svgShdwM = static_cast<QgsSvgMarkerSymbolLayer *>( symShdwL );
2163  QgsSymbolRenderContext svgShdwContext( shdwContext, QgsUnitTypes::RenderUnknownUnit, background.opacity() );
2164 
2165  svgShdwM->renderPoint( QPointF( sizeOut / 2, -sizeOut / 2 ), svgShdwContext );
2166  svgp.end();
2167 
2168  component.picture = svgPict;
2169  // TODO: when SVG symbol's stroke width/units is fixed in QgsSvgCache, adjust for it here
2170  component.pictureBuffer = 0.0;
2171 
2172  component.size = QSizeF( sizeOut, sizeOut );
2173  component.offset = QPointF( 0.0, 0.0 );
2174 
2175  // rotate about origin center of SVG
2176  p->save();
2177  p->translate( component.center.x(), component.center.y() );
2178  p->rotate( component.rotation );
2179  double xoff = context.convertToPainterUnits( background.offset().x(), background.offsetUnit(), background.offsetMapUnitScale() );
2180  double yoff = context.convertToPainterUnits( background.offset().y(), background.offsetUnit(), background.offsetMapUnitScale() );
2181  p->translate( QPointF( xoff, yoff ) );
2182  p->rotate( component.rotationOffset );
2183  p->translate( -sizeOut / 2, sizeOut / 2 );
2184  if ( context.flags() & QgsRenderContext::Antialiasing )
2185  {
2186  p->setRenderHint( QPainter::Antialiasing );
2187  }
2188 
2189  drawShadow( context, component, format );
2190  p->restore();
2191 
2192  delete svgShdwM;
2193  svgShdwM = nullptr;
2194  }
2195 
2196  // draw the actual symbol
2198  QgsSvgMarkerSymbolLayer *svgM = static_cast<QgsSvgMarkerSymbolLayer *>( symL );
2199  QgsSymbolRenderContext svgContext( context, QgsUnitTypes::RenderUnknownUnit, background.opacity() );
2200 
2201  p->save();
2202  if ( context.useAdvancedEffects() )
2203  {
2204  p->setCompositionMode( background.blendMode() );
2205  }
2206  if ( context.flags() & QgsRenderContext::Antialiasing )
2207  {
2208  p->setRenderHint( QPainter::Antialiasing );
2209  }
2210  p->translate( component.center.x(), component.center.y() );
2211  p->rotate( component.rotation );
2212  double xoff = context.convertToPainterUnits( background.offset().x(), background.offsetUnit(), background.offsetMapUnitScale() );
2213  double yoff = context.convertToPainterUnits( background.offset().y(), background.offsetUnit(), background.offsetMapUnitScale() );
2214  p->translate( QPointF( xoff, yoff ) );
2215  p->rotate( component.rotationOffset );
2216  svgM->renderPoint( QPointF( 0, 0 ), svgContext );
2217  p->setCompositionMode( QPainter::CompositionMode_SourceOver ); // just to be sure
2218  p->restore();
2219 
2220  delete svgM;
2221  svgM = nullptr;
2222 
2223  }
2224  else // Generated Shapes
2225  {
2226  double w = component.size.width();
2227  double h = component.size.height();
2228 
2229  if ( background.sizeType() == QgsTextBackgroundSettings::SizeFixed )
2230  {
2231  w = context.convertToPainterUnits( background.size().width(), background.sizeUnit(),
2232  background.sizeMapUnitScale() );
2233  h = context.convertToPainterUnits( background.size().height(), background.sizeUnit(),
2234  background.sizeMapUnitScale() );
2235  }
2236  else if ( background.sizeType() == QgsTextBackgroundSettings::SizeBuffer )
2237  {
2238  if ( background.type() == QgsTextBackgroundSettings::ShapeSquare )
2239  {
2240  if ( w > h )
2241  h = w;
2242  else if ( h > w )
2243  w = h;
2244  }
2245  else if ( background.type() == QgsTextBackgroundSettings::ShapeCircle )
2246  {
2247  // start with label bound by circle
2248  h = std::sqrt( std::pow( w, 2 ) + std::pow( h, 2 ) );
2249  w = h;
2250  }
2251  else if ( background.type() == QgsTextBackgroundSettings::ShapeEllipse )
2252  {
2253  // start with label bound by ellipse
2254  h = h * M_SQRT1_2 * 2;
2255  w = w * M_SQRT1_2 * 2;
2256  }
2257 
2258  double bufferWidth = context.convertToPainterUnits( background.size().width(), background.sizeUnit(),
2259  background.sizeMapUnitScale() );
2260  double bufferHeight = context.convertToPainterUnits( background.size().height(), background.sizeUnit(),
2261  background.sizeMapUnitScale() );
2262 
2263  w += bufferWidth * 2;
2264  h += bufferHeight * 2;
2265  }
2266 
2267  // offsets match those of symbology: -x = left, -y = up
2268  QRectF rect( -w / 2.0, - h / 2.0, w, h );
2269 
2270  if ( rect.isNull() )
2271  return;
2272 
2273  p->save();
2274  if ( context.flags() & QgsRenderContext::Antialiasing )
2275  {
2276  p->setRenderHint( QPainter::Antialiasing );
2277  }
2278  p->translate( QPointF( component.center.x(), component.center.y() ) );
2279  p->rotate( component.rotation );
2280  double xoff = context.convertToPainterUnits( background.offset().x(), background.offsetUnit(), background.offsetMapUnitScale() );
2281  double yoff = context.convertToPainterUnits( background.offset().y(), background.offsetUnit(), background.offsetMapUnitScale() );
2282  p->translate( QPointF( xoff, yoff ) );
2283  p->rotate( component.rotationOffset );
2284 
2285  double penSize = context.convertToPainterUnits( background.strokeWidth(), background.strokeWidthUnit(), background.strokeWidthMapUnitScale() );
2286 
2287  QPen pen;
2288  if ( background.strokeWidth() > 0 )
2289  {
2290  pen.setColor( background.strokeColor() );
2291  pen.setWidthF( penSize );
2292  if ( background.type() == QgsTextBackgroundSettings::ShapeRectangle )
2293  pen.setJoinStyle( background.joinStyle() );
2294  }
2295  else
2296  {
2297  pen = Qt::NoPen;
2298  }
2299 
2300  // store painting in QPicture for shadow drawing
2301  QPicture shapePict;
2302  QPainter shapep;
2303  shapep.begin( &shapePict );
2304  shapep.setPen( pen );
2305  shapep.setBrush( background.fillColor() );
2306 
2307  if ( background.type() == QgsTextBackgroundSettings::ShapeRectangle
2308  || background.type() == QgsTextBackgroundSettings::ShapeSquare )
2309  {
2310  if ( background.radiiUnit() == QgsUnitTypes::RenderPercentage )
2311  {
2312  shapep.drawRoundedRect( rect, background.radii().width(), background.radii().height(), Qt::RelativeSize );
2313  }
2314  else
2315  {
2316  double xRadius = context.convertToPainterUnits( background.radii().width(), background.radiiUnit(), background.radiiMapUnitScale() );
2317  double yRadius = context.convertToPainterUnits( background.radii().height(), background.radiiUnit(), background.radiiMapUnitScale() );
2318  shapep.drawRoundedRect( rect, xRadius, yRadius );
2319  }
2320  }
2321  else if ( background.type() == QgsTextBackgroundSettings::ShapeEllipse
2322  || background.type() == QgsTextBackgroundSettings::ShapeCircle )
2323  {
2324  shapep.drawEllipse( rect );
2325  }
2326  shapep.end();
2327 
2328  if ( format.shadow().enabled() && format.shadow().shadowPlacement() == QgsTextShadowSettings::ShadowShape )
2329  {
2330  component.picture = shapePict;
2331  component.pictureBuffer = penSize / 2.0;
2332 
2333  component.size = rect.size();
2334  component.offset = QPointF( rect.width() / 2, -rect.height() / 2 );
2335  drawShadow( context, component, format );
2336  }
2337 
2338  p->setOpacity( background.opacity() );
2339  if ( context.useAdvancedEffects() )
2340  {
2341  p->setCompositionMode( background.blendMode() );
2342  }
2343 
2344  // scale for any print output or image saving @ specific dpi
2345  p->scale( component.dpiRatio, component.dpiRatio );
2346  _fixQPictureDPI( p );
2347  p->drawPicture( 0, 0, shapePict );
2348  p->restore();
2349  }
2350  if ( background.paintEffect() && background.paintEffect()->enabled() )
2351  {
2352  background.paintEffect()->end( context );
2353  context.setPainter( prevP );
2354  }
2355 }
2356 
2357 void QgsTextRenderer::drawShadow( QgsRenderContext &context, const QgsTextRenderer::Component &component, const QgsTextFormat &format )
2358 {
2359  QgsTextShadowSettings shadow = format.shadow();
2360 
2361  // incoming component sizes should be multiplied by rasterCompressFactor, as
2362  // this allows shadows to be created at paint device dpi (e.g. high resolution),
2363  // then scale device painter by 1.0 / rasterCompressFactor for output
2364 
2365  QPainter *p = context.painter();
2366  double componentWidth = component.size.width(), componentHeight = component.size.height();
2367  double xOffset = component.offset.x(), yOffset = component.offset.y();
2368  double pictbuffer = component.pictureBuffer;
2369 
2370  // generate pixmap representation of label component drawing
2371  bool mapUnits = shadow.blurRadiusUnit() == QgsUnitTypes::RenderMapUnits;
2372  double radius = context.convertToPainterUnits( shadow.blurRadius(), shadow.blurRadiusUnit(), shadow.blurRadiusMapUnitScale() );
2373  radius /= ( mapUnits ? context.scaleFactor() / component.dpiRatio : 1 );
2374  radius = static_cast< int >( radius + 0.5 ); //NOLINT
2375 
2376  // TODO: add labeling gui option to adjust blurBufferClippingScale to minimize pixels, or
2377  // to ensure shadow isn't clipped too tight. (Or, find a better method of buffering)
2378  double blurBufferClippingScale = 3.75;
2379  int blurbuffer = ( radius > 17 ? 16 : radius ) * blurBufferClippingScale;
2380 
2381  QImage blurImg( componentWidth + ( pictbuffer * 2.0 ) + ( blurbuffer * 2.0 ),
2382  componentHeight + ( pictbuffer * 2.0 ) + ( blurbuffer * 2.0 ),
2383  QImage::Format_ARGB32_Premultiplied );
2384 
2385  // TODO: add labeling gui option to not show any shadows under/over a certain size
2386  // keep very small QImages from causing paint device issues, i.e. must be at least > 1
2387  int minBlurImgSize = 1;
2388  // max limitation on QgsSvgCache is 10,000 for screen, which will probably be reasonable for future caching here, too
2389  // 4 x QgsSvgCache limit for output to print/image at higher dpi
2390  // TODO: should it be higher, scale with dpi, or have no limit? Needs testing with very large labels rendered at high dpi output
2391  int maxBlurImgSize = 40000;
2392  if ( blurImg.isNull()
2393  || ( blurImg.width() < minBlurImgSize || blurImg.height() < minBlurImgSize )
2394  || ( blurImg.width() > maxBlurImgSize || blurImg.height() > maxBlurImgSize ) )
2395  return;
2396 
2397  blurImg.fill( QColor( Qt::transparent ).rgba() );
2398  QPainter pictp;
2399  if ( !pictp.begin( &blurImg ) )
2400  return;
2401  pictp.setRenderHints( QPainter::Antialiasing | QPainter::SmoothPixmapTransform );
2402  QPointF imgOffset( blurbuffer + pictbuffer + xOffset,
2403  blurbuffer + pictbuffer + componentHeight + yOffset );
2404 
2405  pictp.drawPicture( imgOffset,
2406  component.picture );
2407 
2408  // overlay shadow color
2409  pictp.setCompositionMode( QPainter::CompositionMode_SourceIn );
2410  pictp.fillRect( blurImg.rect(), shadow.color() );
2411  pictp.end();
2412 
2413  // blur the QImage in-place
2414  if ( shadow.blurRadius() > 0.0 && radius > 0 )
2415  {
2416  QgsSymbolLayerUtils::blurImageInPlace( blurImg, blurImg.rect(), radius, shadow.blurAlphaOnly() );
2417  }
2418 
2419 #if 0
2420  // debug rect for QImage shadow registration and clipping visualization
2421  QPainter picti;
2422  picti.begin( &blurImg );
2423  picti.setBrush( Qt::Dense7Pattern );
2424  QPen imgPen( QColor( 0, 0, 255, 255 ) );
2425  imgPen.setWidth( 1 );
2426  picti.setPen( imgPen );
2427  picti.setOpacity( 0.1 );
2428  picti.drawRect( 0, 0, blurImg.width(), blurImg.height() );
2429  picti.end();
2430 #endif
2431 
2432  double offsetDist = context.convertToPainterUnits( shadow.offsetDistance(), shadow.offsetUnit(), shadow.offsetMapUnitScale() );
2433  double angleRad = shadow.offsetAngle() * M_PI / 180; // to radians
2434  if ( shadow.offsetGlobal() )
2435  {
2436  // TODO: check for differences in rotation origin and cw/ccw direction,
2437  // when this shadow function is used for something other than labels
2438 
2439  // it's 0-->cw-->360 for labels
2440  //QgsDebugMsgLevel( QString( "Shadow aggregated label rotation (degrees): %1" ).arg( component.rotation() + component.rotationOffset() ), 4 );
2441  angleRad -= ( component.rotation * M_PI / 180 + component.rotationOffset * M_PI / 180 );
2442  }
2443 
2444  QPointF transPt( -offsetDist * std::cos( angleRad + M_PI_2 ),
2445  -offsetDist * std::sin( angleRad + M_PI_2 ) );
2446 
2447  p->save();
2448  p->setRenderHint( QPainter::SmoothPixmapTransform );
2449  if ( context.flags() & QgsRenderContext::Antialiasing )
2450  {
2451  p->setRenderHint( QPainter::Antialiasing );
2452  }
2453  if ( context.useAdvancedEffects() )
2454  {
2455  p->setCompositionMode( shadow.blendMode() );
2456  }
2457  p->setOpacity( shadow.opacity() );
2458 
2459  double scale = shadow.scale() / 100.0;
2460  // TODO: scale from center/center, left/center or left/top, instead of default left/bottom?
2461  p->scale( scale, scale );
2462  if ( component.useOrigin )
2463  {
2464  p->translate( component.origin.x(), component.origin.y() );
2465  }
2466  p->translate( transPt );
2467  p->translate( -imgOffset.x(),
2468  -imgOffset.y() );
2469  p->drawImage( 0, 0, blurImg );
2470  p->restore();
2471 
2472  // debug rects
2473 #if 0
2474  // draw debug rect for QImage painting registration
2475  p->save();
2476  p->setBrush( Qt::NoBrush );
2477  QPen imgPen( QColor( 255, 0, 0, 10 ) );
2478  imgPen.setWidth( 2 );
2479  imgPen.setStyle( Qt::DashLine );
2480  p->setPen( imgPen );
2481  p->scale( scale, scale );
2482  if ( component.useOrigin() )
2483  {
2484  p->translate( component.origin().x(), component.origin().y() );
2485  }
2486  p->translate( transPt );
2487  p->translate( -imgOffset.x(),
2488  -imgOffset.y() );
2489  p->drawRect( 0, 0, blurImg.width(), blurImg.height() );
2490  p->restore();
2491 
2492  // draw debug rect for passed in component dimensions
2493  p->save();
2494  p->setBrush( Qt::NoBrush );
2495  QPen componentRectPen( QColor( 0, 255, 0, 70 ) );
2496  componentRectPen.setWidth( 1 );
2497  if ( component.useOrigin() )
2498  {
2499  p->translate( component.origin().x(), component.origin().y() );
2500  }
2501  p->setPen( componentRectPen );
2502  p->drawRect( QRect( -xOffset, -componentHeight - yOffset, componentWidth, componentHeight ) );
2503  p->restore();
2504 #endif
2505 }
2506 
2507 void QgsTextRenderer::drawTextInternal( TextPart drawType,
2508  QgsRenderContext &context,
2509  const QgsTextFormat &format,
2510  const Component &component,
2511  const QStringList &textLines,
2512  const QFontMetricsF *fontMetrics,
2513  HAlignment alignment,
2514  bool drawAsOutlines
2515  , DrawMode mode )
2516 {
2517  if ( !context.painter() )
2518  {
2519  return;
2520  }
2521 
2522  double labelWidest = 0.0;
2523  switch ( mode )
2524  {
2525  case Label:
2526  case Point:
2527  Q_FOREACH ( const QString &line, textLines )
2528  {
2529  double labelWidth = fontMetrics->width( line );
2530  if ( labelWidth > labelWidest )
2531  {
2532  labelWidest = labelWidth;
2533  }
2534  }
2535  break;
2536 
2537  case Rect:
2538  labelWidest = component.size.width();
2539  break;
2540  }
2541 
2542  double labelHeight = fontMetrics->ascent() + fontMetrics->descent(); // ignore +1 for baseline
2543  // double labelHighest = labelfm->height() + ( double )(( lines - 1 ) * labelHeight * tmpLyr.multilineHeight );
2544 
2545  // needed to move bottom of text's descender to within bottom edge of label
2546  double ascentOffset = 0.25 * fontMetrics->ascent(); // labelfm->descent() is not enough
2547 
2548  int i = 0;
2549 
2550  bool adjustForAlignment = alignment != AlignLeft && ( mode != Label || textLines.size() > 1 );
2551 
2552  Q_FOREACH ( const QString &line, textLines )
2553  {
2554  context.painter()->save();
2555  if ( context.flags() & QgsRenderContext::Antialiasing )
2556  {
2557  context.painter()->setRenderHint( QPainter::Antialiasing );
2558  }
2559  context.painter()->translate( component.origin );
2560  if ( !qgsDoubleNear( component.rotation, 0.0 ) )
2561  context.painter()->rotate( -component.rotation * 180 / M_PI );
2562 
2563  // figure x offset for horizontal alignment of multiple lines
2564  double xMultiLineOffset = 0.0;
2565  double labelWidth = fontMetrics->width( line );
2566  if ( adjustForAlignment )
2567  {
2568  double labelWidthDiff = labelWidest - labelWidth;
2569  if ( alignment == AlignCenter )
2570  {
2571  labelWidthDiff /= 2;
2572  }
2573  switch ( mode )
2574  {
2575  case Label:
2576  case Rect:
2577  xMultiLineOffset = labelWidthDiff;
2578  break;
2579 
2580  case Point:
2581  if ( alignment == AlignRight )
2582  xMultiLineOffset = labelWidthDiff - labelWidest;
2583  else if ( alignment == AlignCenter )
2584  xMultiLineOffset = labelWidthDiff - labelWidest / 2.0;
2585 
2586  break;
2587  }
2588  //QgsDebugMsgLevel( QString( "xMultiLineOffset: %1" ).arg( xMultiLineOffset ), 4 );
2589  }
2590 
2591  double yMultiLineOffset = 0.0;
2592  switch ( mode )
2593  {
2594  case Label:
2595  // rendering labels needs special handling - in this case text should be
2596  // drawn with the bottom left corner coinciding with origin, vs top left
2597  // for standard text rendering. Line height is also slightly different.
2598  yMultiLineOffset = - ascentOffset - ( textLines.size() - 1 - i ) * labelHeight * format.lineHeight();
2599  break;
2600 
2601  case Rect:
2602  // standard rendering - designed to exactly replicate QPainter's drawText method
2603  yMultiLineOffset = - ascentOffset + labelHeight - 1 /*baseline*/ + format.lineHeight() * fontMetrics->lineSpacing() * i;
2604  break;
2605 
2606  case Point:
2607  // standard rendering - designed to exactly replicate QPainter's drawText rect method
2608  yMultiLineOffset = 0 - ( textLines.size() - 1 - i ) * fontMetrics->lineSpacing() * format.lineHeight();
2609  break;
2610 
2611  }
2612 
2613  context.painter()->translate( QPointF( xMultiLineOffset, yMultiLineOffset ) );
2614 
2615  Component subComponent;
2616  subComponent.text = line;
2617  subComponent.size = QSizeF( labelWidth, labelHeight );
2618  subComponent.offset = QPointF( 0.0, -ascentOffset );
2619  subComponent.rotation = -component.rotation * 180 / M_PI;
2620  subComponent.rotationOffset = 0.0;
2621 
2622  if ( drawType == QgsTextRenderer::Buffer )
2623  {
2624  QgsTextRenderer::drawBuffer( context, subComponent, format );
2625  }
2626  else
2627  {
2628  // draw text, QPainterPath method
2629  QPainterPath path;
2630  path.setFillRule( Qt::WindingFill );
2631  path.addText( 0, 0, format.scaledFont( context ), subComponent.text );
2632 
2633  // store text's drawing in QPicture for drop shadow call
2634  QPicture textPict;
2635  QPainter textp;
2636  textp.begin( &textPict );
2637  textp.setPen( Qt::NoPen );
2638  QColor textColor = format.color();
2639  textColor.setAlphaF( format.opacity() );
2640  textp.setBrush( textColor );
2641  textp.drawPath( path );
2642  // TODO: why are some font settings lost on drawPicture() when using drawText() inside QPicture?
2643  // e.g. some capitalization options, but not others
2644  //textp.setFont( tmpLyr.textFont );
2645  //textp.setPen( tmpLyr.textColor );
2646  //textp.drawText( 0, 0, component.text() );
2647  textp.end();
2648 
2649  if ( format.shadow().enabled() && format.shadow().shadowPlacement() == QgsTextShadowSettings::ShadowText )
2650  {
2651  subComponent.picture = textPict;
2652  subComponent.pictureBuffer = 0.0; // no pen width to deal with
2653  subComponent.origin = QPointF( 0.0, 0.0 );
2654 
2655  QgsTextRenderer::drawShadow( context, subComponent, format );
2656  }
2657 
2658  // paint the text
2659  if ( context.useAdvancedEffects() )
2660  {
2661  context.painter()->setCompositionMode( format.blendMode() );
2662  }
2663 
2664  // scale for any print output or image saving @ specific dpi
2665  context.painter()->scale( subComponent.dpiRatio, subComponent.dpiRatio );
2666 
2667  if ( drawAsOutlines )
2668  {
2669  // draw outlined text
2670  _fixQPictureDPI( context.painter() );
2671  context.painter()->drawPicture( 0, 0, textPict );
2672  }
2673  else
2674  {
2675  // draw text as text (for SVG and PDF exports)
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  context.painter()->restore();
2685  i++;
2686  }
2687 }
2688 
QPainter::CompositionMode blendMode() const
Returns the blending mode used for drawing the buffer.
The class is used as a container of context for various read/write operations on other objects...
QColor strokeColor() const
Returns the color used for outlining the background shape.
Meters value as Map units.
Definition: qgsunittypes.h:109
void setScale(int scale)
Sets the scaling used for the drop shadow (in percentage of original size).
Shape size is determined by adding a buffer margin around text.
void setLineHeight(double height)
Sets the line height for text.
void setRadiiUnit(QgsUnitTypes::RenderUnit units)
Sets the units used for the shape&#39;s radii.
void setStrokeWidth(double width)
Sets the width of the shape&#39;s stroke (stroke).
RotationType
Methods for determining the rotation of the background shape.
void setOffsetMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale object for the shadow offset distance.
void setOpacity(double opacity)
Sets the text&#39;s opacity.
void setStrokeWidthMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale object for the shape stroke width.
void setOffsetUnit(QgsUnitTypes::RenderUnit units)
Sets the units used for the shadow&#39;s offset.
QgsTextShadowSettings::ShadowPlacement shadowPlacement() const
Returns the placement for the drop shadow.
QSizeF radii() const
Returns the radii used for rounding the corners of shapes.
QSizeF size() const
Returns the size of the background shape.
double opacity() const
Returns the text&#39;s opacity.
void setSizeUnit(QgsUnitTypes::RenderUnit unit)
Sets the units used for the shape&#39;s size.
void readXml(const QDomElement &elem, const QgsReadWriteContext &context)
Read settings from a DOM element.
QPainter::CompositionMode blendMode() const
Returns the blending mode used for drawing the drop shadow.
QPointF offset() const
Returns the offset used for drawing the background shape.
QColor fillColor() const
Returns the color used for filing the background shape.
QgsMapUnitScale strokeWidthMapUnitScale() const
Returns the map unit scale object for the shape stroke width.
QFont toQFont() const
Returns a QFont matching the relevant settings from this text format.
QgsTextShadowSettings & operator=(const QgsTextShadowSettings &other)
void readXml(const QDomElement &elem)
Read settings from a DOM element.
Use antialiasing while drawing.
Draw shadow under buffer.
ShadowPlacement
Placement positions for text shadow.
QgsTextShadowSettings & shadow()
Returns a reference to the text drop shadow settings.
double blurRadius() const
Returns the blur radius for the shadow.
QgsUnitTypes::RenderUnit convertFromOldLabelUnit(int val)
double opacity() const
Returns the background shape&#39;s opacity.
void setSizeMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale object for the size.
void setStrokeColor(const QColor &color)
Sets the color used for outlining the background shape.
void setSize(double size)
Sets the size of the buffer.
double strokeWidth() const
Returns the width of the shape&#39;s stroke (stroke).
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:251
void setSizeUnit(QgsUnitTypes::RenderUnit unit)
Sets the units used for the buffer size.
HAlignment
Horizontal alignment.
double offsetDistance() const
Returns the distance for offsetting the position of the shadow from the text.
void setFont(const QFont &font)
Sets the font used for rendering text.
void setJoinStyle(Qt::PenJoinStyle style)
Sets the join style used for drawing the buffer.
static bool isDefaultStack(QgsPaintEffect *effect)
Tests whether a paint effect matches the default effects stack.
static QgsPainting::BlendMode getBlendModeEnum(QPainter::CompositionMode blendMode)
Returns a BlendMode corresponding to a QPainter::CompositionMode.
Definition: qgspainting.cpp:80
Base class for visual effects which can be applied to QPicture drawings.
void setOffsetUnit(QgsUnitTypes::RenderUnit units)
Sets the units used for the shape&#39;s offset.
QColor color() const
Returns the color that text will be rendered in.
static void drawPart(const QRectF &rect, double rotation, HAlignment alignment, const QStringList &textLines, QgsRenderContext &context, const QgsTextFormat &format, TextPart part, bool drawAsOutlines=true)
Draws a single component of rendered text using the specified settings.
void setFillBufferInterior(bool fill)
Sets whether the interior of the buffer will be filled in.
void setBlurRadius(double blurRadius)
Sets the blur radius for the shadow.
void setPaintEffect(QgsPaintEffect *effect)
Sets the current paint effect for the buffer.
void setOpacity(double opacity)
Sets the shadow&#39;s opacity.
QgsTextBufferSettings & operator=(const QgsTextBufferSettings &other)
Copy constructor.
void setSizeMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale object for the shape size.
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
static QPainter::CompositionMode getCompositionMode(QgsPainting::BlendMode blendMode)
Returns a QPainter::CompositionMode corresponding to a BlendMode.
Definition: qgspainting.cpp:20
Flags flags() const
Returns combination of flags used for rendering.
void setJoinStyle(Qt::PenJoinStyle style)
Sets the join style used for drawing the background shape.
void readXml(const QDomElement &elem, const QgsReadWriteContext &context)
Read settings from a DOM element.
static QgsTextFormat fromQFont(const QFont &font)
Returns a text format matching the settings from an input font.
Container for settings relating to a text background object.
Percentage of another measurement (e.g., canvas size, feature size)
Definition: qgsunittypes.h:105
static int sizeToPixel(double size, const QgsRenderContext &c, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &mapUnitScale=QgsMapUnitScale())
Calculates pixel size (considering output size should be in pixel or map units, scale factors and opt...
QgsMapUnitScale offsetMapUnitScale() const
Returns the map unit scale object for the shape offset.
void setRadiiMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale object for the shape radii.
void setBlurRadiusUnit(QgsUnitTypes::RenderUnit units)
Sets the units used for the shadow&#39;s blur radius.
static QString translateNamedStyle(const QString &namedStyle)
Returns the localized named style of a font, if such a translation is available.
QMap< QString, QString > QgsStringMap
Definition: qgis.h:501
void setBlurRadiusMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale object for the shadow blur radius.
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
Definition: MathUtils.cpp:786
void readXml(const QDomElement &elem)
Read settings from a DOM element.
static QgsPaintEffectRegistry * paintEffectRegistry()
Returns the application&#39;s paint effect registry, used for managing paint effects. ...
ShapeType type() const
Returns the type of background shape (e.g., square, ellipse, SVG).
void setOffset(QPointF offset)
Sets the offset used for drawing the background shape.
QDomElement writeXml(QDomDocument &doc, const QgsReadWriteContext &context) const
Write settings into a DOM element.
QDomElement writeXml(QDomDocument &doc) const
Write settings into a DOM element.
Qt::PenJoinStyle joinStyle() const
Returns the buffer join style.
static QString encodeColor(const QColor &color)
QgsMapUnitScale sizeMapUnitScale() const
Returns the map unit scale object for the buffer size.
static QgsTextFormat fromMimeData(const QMimeData *data, bool *ok=nullptr)
Attempts to parse the provided mime data as a QgsTextFormat.
void readFromLayer(QgsVectorLayer *layer)
Reads settings from a layer&#39;s custom properties (for QGIS 2.x projects).
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
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.
QgsMapUnitScale offsetMapUnitScale() const
Returns the map unit scale object for the shadow offset distance.
QPainter::CompositionMode blendMode() const
Returns the blending mode used for drawing the text.
QColor color() const
Returns the color of the drop shadow.
void setSize(double size)
Sets the size for rendered text.
void setOffsetDistance(double distance)
Sets the distance for offsetting the position of the shadow from the text.
void setScaleFactor(double factor)
Sets the scaling factor for the render to convert painter units to physical sizes.
#define FALLTHROUGH
Definition: qgis.h:570
QgsMapUnitScale blurRadiusMapUnitScale() const
Returns the map unit scale object for the shadow blur radius.
bool fillBufferInterior() const
Returns whether the interior of the buffer will be filled in.
void setOffsetMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale object for the shape offset.
QDomElement writeXml(QDomDocument &doc, const QgsReadWriteContext &context) const
Write settings into a DOM element.
void setBlurAlphaOnly(bool alphaOnly)
Sets whether only the alpha channel for the shadow should be blurred.
Buffer component.
RotationType rotationType() const
Returns the method used for rotating the background shape.
void setSizeMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale object for the buffer size.
QgsPaintEffect * paintEffect() const
Returns the current paint effect for the background shape.
double size() const
Returns the symbol size.
void setBlendMode(QPainter::CompositionMode mode)
Sets the blending mode used for drawing the drop shadow.
void setColor(const QColor &color)
Sets the color for the drop shadow.
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
points (e.g., for font sizes)
Definition: qgsunittypes.h:107
void setEnabled(bool enabled)
Sets whether the text shadow will be drawn.
void setEnabled(bool enabled)
Sets whether the text buffer will be drawn.
void setColor(const QColor &color)
Sets the color that text will be rendered in.
Draw shadow under text.
QgsMapUnitScale radiiMapUnitScale() const
Returns the map unit scale object for the shape radii.
QString svgFile() const
Returns the absolute path to the background SVG file, if set.
Qt::PenJoinStyle joinStyle() const
Returns the join style used for drawing the background shape.
void setNamedStyle(const QString &style)
Sets the named style for the font used for rendering text.
void setPainter(QPainter *p)
Sets the destination QPainter for the render operation.
QgsTextBackgroundSettings & background()
Returns a reference to the text background settings.
QDomElement writeXml(QDomDocument &doc) const
Write settings into a DOM element.
QgsTextBufferSettings & buffer()
Returns a reference to the text buffer settings.
void setSizeUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the size of rendered text.
SizeType
Methods for determining the background shape size.
double opacity() const
Returns the buffer opacity.
bool enabled() const
Returns whether the effect is enabled.
Q_GUI_EXPORT int qt_defaultDpiY()
void setSize(QSizeF size)
Sets the size of the background shape.
static Q_INVOKABLE QgsUnitTypes::RenderUnit decodeRenderUnit(const QString &string, bool *ok=nullptr)
Decodes a render unit from a string.
QMimeData * toMimeData() const
Returns new mime data representing the text format settings.
void setRadii(QSizeF radii)
Sets the radii used for rounding the corners of shapes.
static QFontMetricsF fontMetrics(QgsRenderContext &context, const QgsTextFormat &format)
Returns the font metrics for the given text format, when rendered in the specified render context...
QgsUnitTypes::RenderUnit strokeWidthUnit() const
Returns the units used for the shape&#39;s stroke width.
void setSizeType(SizeType type)
Sets the method used to determine the size of the background shape (e.g., fixed size or buffer around...
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
void setType(ShapeType type)
Sets the type of background shape to draw (e.g., square, ellipse, SVG).
Draw shadow below all text components.
static Q_INVOKABLE QString encodeUnit(QgsUnitTypes::DistanceUnit unit)
Encodes a distance unit to a string.
QPainter::CompositionMode blendMode() const
Returns the blending mode used for drawing the background shape.
static void drawText(const QRectF &rect, double rotation, HAlignment alignment, const QStringList &textLines, QgsRenderContext &context, const QgsTextFormat &format, bool drawAsOutlines=true)
Draws text within a rectangle using the specified settings.
QgsUnitTypes::RenderUnit offsetUnit() const
Returns the units used for the shape&#39;s offset.
double rotation() const
Returns the rotation for the background shape, in degrees clockwise.
double lineHeight() const
Returns the line height for text.
void setEnabled(bool enabled)
Sets whether the text background will be drawn.
int offsetAngle() const
Returns the angle for offsetting the position of the shadow from the text.
ShapeType
Background shape types.
TextPart
Components of text.
static QString untranslateNamedStyle(const QString &namedStyle)
Returns the english named style of a font, if possible.
const QgsPathResolver & pathResolver() const
Returns path resolver for conversion between relative and absolute paths.
QFont scaledFont(const QgsRenderContext &context) const
Returns a font with the size scaled to match the format&#39;s size settings (including units and map unit...
Contains information about the context of a rendering operation.
Shape rotation is offset from text rotation.
double convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale()) const
Converts a size from the specified units to painter units (pixels).
static bool fontFamilyMatchOnSystem(const QString &family, QString *chosen=nullptr, bool *match=nullptr)
Check whether font family is on system.
QgsMapUnitScale sizeMapUnitScale() const
Returns the map unit scale object for the size.
QgsUnitTypes::RenderUnit sizeUnit() const
Returns the units for the buffer size.
QPainter * painter()
Returns the destination QPainter for the render operation.
const QgsMapToPixel & mapToPixel() const
static void blurImageInPlace(QImage &image, QRect rect, int radius, bool alphaOnly)
Blurs an image in place, e.g. creating Qt-independent drop shadows.
QgsUnitTypes::RenderUnit offsetUnit() const
Returns the units used for the shadow&#39;s offset.
bool enabled() const
Returns whether the shadow is enabled.
QgsTextBackgroundSettings & operator=(const QgsTextBackgroundSettings &other)
Struct for storing maximum and minimum scales for measurements in map units.
QgsTextFormat & operator=(const QgsTextFormat &other)
Container for settings relating to a text shadow.
bool offsetGlobal() const
Returns true if the global shadow offset will be used.
QColor color() const
Returns the color of the buffer.
double size() const
Returns the size of the buffer.
QgsUnitTypes::RenderUnit radiiUnit() const
Returns the units used for the shape&#39;s radii.
Container for settings relating to a text buffer.
void setFillColor(const QColor &color)
Sets the color used for filing the background shape.
static bool updateFontViaStyle(QFont &f, const QString &fontstyle, bool fallback=false)
Updates font with named style and retain all font properties.
bool useAdvancedEffects() const
Returns true if advanced effects such as blend modes such be used.
double size() const
Returns the size for rendered text.
void setStrokeWidthUnit(QgsUnitTypes::RenderUnit units)
Sets the units used for the shape&#39;s stroke width.
bool enabled() const
Returns whether the background is enabled.
void setMapToPixel(const QgsMapToPixel &mtp)
SizeType sizeType() const
Returns the method used to determine the size of the background shape (e.g., fixed size or buffer aro...
QgsUnitTypes::RenderUnit blurRadiusUnit() const
Returns the units used for the shadow&#39;s blur radius.
bool enabled() const
Returns whether the buffer is enabled.
int scale() const
Returns the scaling used for the drop shadow (in percentage of original size).
Shape rotation is synced with text rotation.
void setShadowPlacement(QgsTextShadowSettings::ShadowPlacement placement)
Sets the placement for the drop shadow.
void setBlendMode(QPainter::CompositionMode mode)
Sets the blending mode used for drawing the text.
Q_GUI_EXPORT int qt_defaultDpiX()
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
Draw shadow under background shape.
QgsPaintEffect * paintEffect() const
Returns the current paint effect for the buffer.
QVariant customProperty(const QString &value, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer.
QgsMapUnitScale sizeMapUnitScale() const
Returns the map unit scale object for the shape size.
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.
QgsUnitTypes::RenderUnit sizeUnit() const
Returns the units for the size of rendered text.
QgsPaintEffect * createEffect(const QString &name, const QgsStringMap &properties=QgsStringMap()) const
Creates a new paint effect given the effect name and properties map.
void setBlendMode(QPainter::CompositionMode mode)
Sets the blending mode used for drawing the buffer.
void setSvgFile(const QString &file)
Sets the path to the background SVG file.
static double textWidth(const QgsRenderContext &context, const QgsTextFormat &format, const QStringList &textLines, QFontMetricsF *fontMetrics=nullptr)
Returns the width of a text based on a given format.
bool blurAlphaOnly() const
Returns whether only the alpha channel for the shadow will be blurred.
void setOffsetGlobal(bool global)
Sets whether the global shadow offset should be used.
DrawMode
Draw mode to calculate width and height.
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
Represents a vector layer which manages a vector based data sets.
void setOpacity(double opacity)
Sets the buffer opacity.
Square - buffered sizes only.
void readFromLayer(QgsVectorLayer *layer)
Reads settings from a layer&#39;s custom properties (for QGIS 2.x projects).
virtual void end(QgsRenderContext &context)
Ends interception of paint operations to a render context, and draws the result to the render context...
bool containsAdvancedEffects() const
Returns true if any component of the font format requires advanced effects such as blend modes...
QString namedStyle() const
Returns the named style for the font used for rendering text (e.g., "bold").
QFont font() const
Returns the font used for rendering text.
void setRotationType(RotationType type)
Sets the method used for rotating the background shape.
void setColor(const QColor &color)
Sets the color for the buffer.
QgsUnitTypes::RenderUnit sizeUnit() const
Returns the units used for the shape&#39;s size.
virtual void begin(QgsRenderContext &context)
Begins intercepting paint operations to a render context.
double opacity() const
Returns the shadow&#39;s opacity.
void setOpacity(double opacity)
Sets the background shape&#39;s opacity.
RenderUnit
Rendering size units.
Definition: qgsunittypes.h:100
static QColor decodeColor(const QString &str)
void setPaintEffect(QgsPaintEffect *effect)
Sets the current paint effect for the background shape.
void setOffsetAngle(int angle)
Sets the angle for offsetting the position of the shadow from the text.
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.