QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
qgstextrenderer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgstextrenderer.cpp
3  -------------------
4  begin : September 2015
5  copyright : (C) Nyall Dawson
6  email : nyall dot dawson at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include "qgstextrenderer.h"
17 #include "qgsvectorlayer.h"
18 #include "qgstextformat.h"
19 #include "qgstextdocument.h"
20 #include "qgstextfragment.h"
21 #include "qgspallabeling.h"
22 #include "qgspainteffect.h"
23 #include "qgspainterswapper.h"
24 #include "qgsmarkersymbollayer.h"
25 #include "qgssymbollayerutils.h"
26 
27 Q_GUI_EXPORT extern int qt_defaultDpiX();
28 Q_GUI_EXPORT extern int qt_defaultDpiY();
29 
30 static void _fixQPictureDPI( QPainter *p )
31 {
32  // QPicture makes an assumption that we drawing to it with system DPI.
33  // Then when being drawn, it scales the painter. The following call
34  // negates the effect. There is no way of setting QPicture's DPI.
35  // See QTBUG-20361
36  p->scale( static_cast< double >( qt_defaultDpiX() ) / p->device()->logicalDpiX(),
37  static_cast< double >( qt_defaultDpiY() ) / p->device()->logicalDpiY() );
38 }
39 
41 {
42  if ( alignment & Qt::AlignLeft )
43  return AlignLeft;
44  else if ( alignment & Qt::AlignRight )
45  return AlignRight;
46  else if ( alignment & Qt::AlignHCenter )
47  return AlignCenter;
48  else if ( alignment & Qt::AlignJustify )
49  return AlignJustify;
50 
51  // not supported?
52  return AlignLeft;
53 }
54 
56 {
57  if ( alignment & Qt::AlignTop )
58  return AlignTop;
59  else if ( alignment & Qt::AlignBottom )
60  return AlignBottom;
61  else if ( alignment & Qt::AlignVCenter )
62  return AlignVCenter;
63  //not supported
64  else if ( alignment & Qt::AlignBaseline )
65  return AlignBottom;
66 
67  return AlignTop;
68 }
69 
70 int QgsTextRenderer::sizeToPixel( double size, const QgsRenderContext &c, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &mapUnitScale )
71 {
72  return static_cast< int >( c.convertToPainterUnits( size, unit, mapUnitScale ) + 0.5 ); //NOLINT
73 }
74 
75 void QgsTextRenderer::drawText( const QRectF &rect, double rotation, QgsTextRenderer::HAlignment alignment, const QStringList &textLines, QgsRenderContext &context, const QgsTextFormat &format, bool, VAlignment vAlignment )
76 {
77  QgsTextFormat tmpFormat = format;
78  if ( format.dataDefinedProperties().hasActiveProperties() ) // note, we use format instead of tmpFormat here, it's const and potentially avoids a detach
79  tmpFormat.updateDataDefinedProperties( context );
80  tmpFormat = updateShadowPosition( tmpFormat );
81 
82  QgsTextDocument document = format.allowHtmlFormatting() ? QgsTextDocument::fromHtml( textLines ) : QgsTextDocument::fromPlainText( textLines );
83  document.applyCapitalization( format.capitalization() );
84 
85  if ( tmpFormat.background().enabled() )
86  {
87  drawPart( rect, rotation, alignment, vAlignment, document, context, tmpFormat, Background );
88  }
89 
90  if ( tmpFormat.buffer().enabled() )
91  {
92  drawPart( rect, rotation, alignment, vAlignment, document, context, tmpFormat, Buffer );
93  }
94 
95  drawPart( rect, rotation, alignment, vAlignment, document, context, tmpFormat, Text );
96 }
97 
98 void QgsTextRenderer::drawText( QPointF point, double rotation, QgsTextRenderer::HAlignment alignment, const QStringList &textLines, QgsRenderContext &context, const QgsTextFormat &format, bool )
99 {
100  QgsTextFormat tmpFormat = format;
101  if ( format.dataDefinedProperties().hasActiveProperties() ) // note, we use format instead of tmpFormat here, it's const and potentially avoids a detach
102  tmpFormat.updateDataDefinedProperties( context );
103  tmpFormat = updateShadowPosition( tmpFormat );
104 
105  QgsTextDocument document = format.allowHtmlFormatting() ? QgsTextDocument::fromHtml( textLines ) : QgsTextDocument::fromPlainText( textLines );
106  document.applyCapitalization( format.capitalization() );
107 
108  if ( tmpFormat.background().enabled() )
109  {
110  drawPart( point, rotation, alignment, document, context, tmpFormat, Background );
111  }
112 
113  if ( tmpFormat.buffer().enabled() )
114  {
115  drawPart( point, rotation, alignment, document, context, tmpFormat, Buffer );
116  }
117 
118  drawPart( point, rotation, alignment, document, context, tmpFormat, Text );
119 }
120 
121 QgsTextFormat QgsTextRenderer::updateShadowPosition( const QgsTextFormat &format )
122 {
123  if ( !format.shadow().enabled() || format.shadow().shadowPlacement() != QgsTextShadowSettings::ShadowLowest )
124  return format;
125 
126  QgsTextFormat tmpFormat = format;
127  if ( tmpFormat.background().enabled() && tmpFormat.background().type() != QgsTextBackgroundSettings::ShapeMarkerSymbol ) // background shadow not compatible with marker symbol backgrounds
128  {
130  }
131  else if ( tmpFormat.buffer().enabled() )
132  {
134  }
135  else
136  {
138  }
139  return tmpFormat;
140 }
141 
142 void QgsTextRenderer::drawPart( const QRectF &rect, double rotation, HAlignment alignment,
143  const QStringList &textLines, QgsRenderContext &context, const QgsTextFormat &format, QgsTextRenderer::TextPart part, bool )
144 {
145  const QgsTextDocument document = format.allowHtmlFormatting() ? QgsTextDocument::fromHtml( textLines ) : QgsTextDocument::fromPlainText( textLines );
146 
147  drawPart( rect, rotation, alignment, AlignTop, document, context, format, part );
148 }
149 
150 void QgsTextRenderer::drawPart( const QRectF &rect, double rotation, QgsTextRenderer::HAlignment alignment, VAlignment vAlignment, const QgsTextDocument &document, QgsRenderContext &context, const QgsTextFormat &format, QgsTextRenderer::TextPart part )
151 {
152  if ( !context.painter() )
153  {
154  return;
155  }
156 
157  Component component;
158  component.dpiRatio = 1.0;
159  component.origin = rect.topLeft();
160  component.rotation = rotation;
161  component.size = rect.size();
162  component.hAlign = alignment;
163 
164  switch ( part )
165  {
166  case Background:
167  {
168  if ( !format.background().enabled() )
169  return;
170 
171  if ( !qgsDoubleNear( rotation, 0.0 ) )
172  {
173  // get rotated label's center point
174 
175  double xc = rect.width() / 2.0;
176  double yc = rect.height() / 2.0;
177 
178  double angle = -rotation;
179  double xd = xc * std::cos( angle ) - yc * std::sin( angle );
180  double yd = xc * std::sin( angle ) + yc * std::cos( angle );
181 
182  component.center = QPointF( component.origin.x() + xd, component.origin.y() + yd );
183  }
184  else
185  {
186  component.center = rect.center();
187  }
188 
189  QgsTextRenderer::drawBackground( context, component, format, document, Rect );
190 
191  break;
192  }
193 
194  case Buffer:
195  {
196  if ( !format.buffer().enabled() )
197  break;
198  }
200  case Text:
201  case Shadow:
202  {
203  drawTextInternal( part, context, format, component,
204  document,
205  nullptr,
206  alignment, vAlignment );
207  break;
208  }
209  }
210 }
211 
212 void QgsTextRenderer::drawPart( QPointF origin, double rotation, QgsTextRenderer::HAlignment alignment, const QStringList &textLines, QgsRenderContext &context, const QgsTextFormat &format, QgsTextRenderer::TextPart part, bool )
213 {
214  const QgsTextDocument document = format.allowHtmlFormatting() ? QgsTextDocument::fromHtml( textLines ) : QgsTextDocument::fromPlainText( textLines );
215  drawPart( origin, rotation, alignment, document, context, format, part );
216 }
217 
218 void QgsTextRenderer::drawPart( QPointF origin, double rotation, QgsTextRenderer::HAlignment alignment, const QgsTextDocument &document, QgsRenderContext &context, const QgsTextFormat &format, QgsTextRenderer::TextPart part )
219 {
220  if ( !context.painter() )
221  {
222  return;
223  }
224 
225  Component component;
226  component.dpiRatio = 1.0;
227  component.origin = origin;
228  component.rotation = rotation;
229  component.hAlign = alignment;
230 
231  switch ( part )
232  {
233  case Background:
234  {
235  if ( !format.background().enabled() )
236  return;
237 
238  QgsTextRenderer::drawBackground( context, component, format, document, Point );
239  break;
240  }
241 
242  case Buffer:
243  {
244  if ( !format.buffer().enabled() )
245  break;
246  }
248  case Text:
249  case Shadow:
250  {
251  drawTextInternal( part, context, format, component,
252  document,
253  nullptr,
254  alignment, AlignTop,
255  Point );
256  break;
257  }
258  }
259 }
260 
261 QFontMetricsF QgsTextRenderer::fontMetrics( QgsRenderContext &context, const QgsTextFormat &format, const double scaleFactor )
262 {
263  return QFontMetricsF( format.scaledFont( context, scaleFactor ), context.painter() ? context.painter()->device() : nullptr );
264 }
265 
266 double QgsTextRenderer::drawBuffer( QgsRenderContext &context, const QgsTextRenderer::Component &component, const QgsTextFormat &format )
267 {
268  QPainter *p = context.painter();
269 
270  QgsTextFormat::TextOrientation orientation = format.orientation();
272  {
273  if ( component.rotation >= -315 && component.rotation < -90 )
274  {
276  }
277  else if ( component.rotation >= -90 && component.rotation < -45 )
278  {
280  }
281  else
282  {
284  }
285  }
286 
287  QgsTextBufferSettings buffer = format.buffer();
288 
289  const double penSize = context.convertToPainterUnits( buffer.size(), buffer.sizeUnit(), buffer.sizeMapUnitScale() );
290 
291  const double scaleFactor = ( context.flags() & QgsRenderContext::ApplyScalingWorkaroundForTextRendering ) ? FONT_WORKAROUND_SCALE : 1.0;
292  const QFont font = format.scaledFont( context, scaleFactor );
293 
294  QPainterPath path;
295  path.setFillRule( Qt::WindingFill );
296  double advance = 0;
297  switch ( orientation )
298  {
300  {
301  double xOffset = 0;
302  for ( const QgsTextFragment &fragment : component.block )
303  {
304  QFont fragmentFont = font;
305  fragment.characterFormat().updateFontForFormat( fragmentFont, scaleFactor );
306 
307  if ( component.extraWordSpacing || component.extraLetterSpacing )
308  applyExtraSpacingForLineJustification( fragmentFont, component.extraWordSpacing, component.extraLetterSpacing );
309 
310  path.addText( xOffset, 0, fragmentFont, fragment.text() );
311 
312  xOffset += fragment.horizontalAdvance( fragmentFont, true, scaleFactor );
313  }
314  advance = xOffset;
315  break;
316  }
317 
320  {
321  double letterSpacing = font.letterSpacing();
322  double partYOffset = component.offset.y() * scaleFactor;
323  for ( const QgsTextFragment &fragment : component.block )
324  {
325  QFont fragmentFont = font;
326  fragment.characterFormat().updateFontForFormat( fragmentFont, scaleFactor );
327 
328  QFontMetricsF fragmentMetrics( fragmentFont );
329  const double labelWidth = fragmentMetrics.maxWidth();
330 
331  const QStringList parts = QgsPalLabeling::splitToGraphemes( fragment.text() );
332  for ( const QString &part : parts )
333  {
334 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
335  double partXOffset = ( labelWidth - ( fragmentMetrics.width( part ) - letterSpacing ) ) / 2;
336 #else
337  double partXOffset = ( labelWidth - ( fragmentMetrics.horizontalAdvance( part ) - letterSpacing ) ) / 2;
338 #endif
339  path.addText( partXOffset, partYOffset, fragmentFont, part );
340  partYOffset += fragmentMetrics.ascent() + letterSpacing;
341  }
342  }
343  advance = partYOffset - component.offset.y() * scaleFactor;
344  break;
345  }
346  }
347 
348  QColor bufferColor = buffer.color();
349  bufferColor.setAlphaF( buffer.opacity() );
350  QPen pen( bufferColor );
351  pen.setWidthF( penSize * scaleFactor );
352  pen.setJoinStyle( buffer.joinStyle() );
353  QColor tmpColor( bufferColor );
354  // honor pref for whether to fill buffer interior
355  if ( !buffer.fillBufferInterior() )
356  {
357  tmpColor.setAlpha( 0 );
358  }
359 
360  // store buffer's drawing in QPicture for drop shadow call
361  QPicture buffPict;
362  QPainter buffp;
363  buffp.begin( &buffPict );
364  if ( buffer.paintEffect() && buffer.paintEffect()->enabled() )
365  {
366  context.setPainter( &buffp );
367  std::unique_ptr< QgsPaintEffect > tmpEffect( buffer.paintEffect()->clone() );
368 
369  tmpEffect->begin( context );
370  context.painter()->setPen( pen );
371  context.painter()->setBrush( tmpColor );
372  if ( scaleFactor != 1.0 )
373  context.painter()->scale( 1 / scaleFactor, 1 / scaleFactor );
374  context.painter()->drawPath( path );
375  if ( scaleFactor != 1.0 )
376  context.painter()->scale( scaleFactor, scaleFactor );
377  tmpEffect->end( context );
378 
379  context.setPainter( p );
380  }
381  else
382  {
383  if ( scaleFactor != 1.0 )
384  buffp.scale( 1 / scaleFactor, 1 / scaleFactor );
385  buffp.setPen( pen );
386  buffp.setBrush( tmpColor );
387  buffp.drawPath( path );
388  }
389  buffp.end();
390 
391  if ( format.shadow().enabled() && format.shadow().shadowPlacement() == QgsTextShadowSettings::ShadowBuffer )
392  {
393  QgsTextRenderer::Component bufferComponent = component;
394  bufferComponent.origin = QPointF( 0.0, 0.0 );
395  bufferComponent.picture = buffPict;
396  bufferComponent.pictureBuffer = penSize / 2.0;
397 
399  {
400  bufferComponent.offset.setY( bufferComponent.offset.y() - bufferComponent.size.height() );
401  }
402  drawShadow( context, bufferComponent, format );
403  }
404 
405  QgsScopedQPainterState painterState( p );
406  context.setPainterFlagsUsingContext( p );
407 
408  if ( context.useAdvancedEffects() )
409  {
410  p->setCompositionMode( buffer.blendMode() );
411  }
412 
413  // scale for any print output or image saving @ specific dpi
414  p->scale( component.dpiRatio, component.dpiRatio );
415  _fixQPictureDPI( p );
416  p->drawPicture( 0, 0, buffPict );
417 
418  return advance / scaleFactor;
419 }
420 
421 void QgsTextRenderer::drawMask( QgsRenderContext &context, const QgsTextRenderer::Component &component, const QgsTextFormat &format )
422 {
423  QgsTextMaskSettings mask = format.mask();
424 
425  // the mask is drawn to a side painter
426  // or to the main painter for preview
427  QPainter *p = context.isGuiPreview() ? context.painter() : context.maskPainter( context.currentMaskId() );
428  if ( ! p )
429  return;
430 
431  double penSize = context.convertToPainterUnits( mask.size(), mask.sizeUnit(), mask.sizeMapUnitScale() );
432 
433  // buffer: draw the text with a big pen
434  QPainterPath path;
435  path.setFillRule( Qt::WindingFill );
436 
437  const double scaleFactor = ( context.flags() & QgsRenderContext::ApplyScalingWorkaroundForTextRendering ) ? FONT_WORKAROUND_SCALE : 1.0;
438 
439  // TODO: vertical text mode was ignored when masking feature was added.
440  // Hopefully Oslandia come back and fix this? Hint hint...
441 
442  const QFont font = format.scaledFont( context, scaleFactor );
443  double xOffset = 0;
444  for ( const QgsTextFragment &fragment : component.block )
445  {
446  QFont fragmentFont = font;
447  fragment.characterFormat().updateFontForFormat( fragmentFont, scaleFactor );
448 
449  path.addText( xOffset, 0, fragmentFont, fragment.text() );
450 
451  xOffset += fragment.horizontalAdvance( fragmentFont, true );
452  }
453 
454  QColor bufferColor( Qt::gray );
455  bufferColor.setAlphaF( mask.opacity() );
456 
457  QPen pen;
458  QBrush brush;
459  brush.setColor( bufferColor );
460  pen.setColor( bufferColor );
461  pen.setWidthF( penSize * scaleFactor );
462  pen.setJoinStyle( mask.joinStyle() );
463 
464  QgsScopedQPainterState painterState( p );
465  context.setPainterFlagsUsingContext( p );
466 
467  // scale for any print output or image saving @ specific dpi
468  p->scale( component.dpiRatio, component.dpiRatio );
469  if ( mask.paintEffect() && mask.paintEffect()->enabled() )
470  {
471  QgsPainterSwapper swapper( context, p );
472  {
473  QgsEffectPainter effectPainter( context, mask.paintEffect() );
474  if ( scaleFactor != 1.0 )
475  context.painter()->scale( 1 / scaleFactor, 1 / scaleFactor );
476  context.painter()->setPen( pen );
477  context.painter()->setBrush( brush );
478  context.painter()->drawPath( path );
479  if ( scaleFactor != 1.0 )
480  context.painter()->scale( scaleFactor, scaleFactor );
481  }
482  }
483  else
484  {
485  if ( scaleFactor != 1.0 )
486  p->scale( 1 / scaleFactor, 1 / scaleFactor );
487  p->setPen( pen );
488  p->setBrush( brush );
489  p->drawPath( path );
490  if ( scaleFactor != 1.0 )
491  p->scale( scaleFactor, scaleFactor );
492 
493  }
494 }
495 
496 double QgsTextRenderer::textWidth( const QgsRenderContext &context, const QgsTextFormat &format, const QStringList &textLines, QFontMetricsF * )
497 {
498  QgsTextDocument doc;
499  if ( !format.allowHtmlFormatting() )
500  {
501  doc = QgsTextDocument::fromPlainText( textLines );
502  }
503  else
504  {
505  doc = QgsTextDocument::fromHtml( textLines );
506  }
507  doc.applyCapitalization( format.capitalization() );
508  return textWidth( context, format, doc );
509 }
510 
511 double QgsTextRenderer::textWidth( const QgsRenderContext &context, const QgsTextFormat &format, const QgsTextDocument &document )
512 {
513  //calculate max width of text lines
514  const double scaleFactor = ( context.flags() & QgsRenderContext::ApplyScalingWorkaroundForTextRendering ) ? FONT_WORKAROUND_SCALE : 1.0;
515  const QFont baseFont = format.scaledFont( context, scaleFactor );
516 
517  double width = 0;
518  switch ( format.orientation() )
519  {
521  {
522  double maxLineWidth = 0;
523  for ( const QgsTextBlock &block : document )
524  {
525  double blockWidth = 0;
526  for ( const QgsTextFragment &fragment : block )
527  {
528  blockWidth += fragment.horizontalAdvance( baseFont, scaleFactor );
529  }
530  maxLineWidth = std::max( maxLineWidth, blockWidth );
531  }
532  width = maxLineWidth;
533  break;
534  }
535 
537  {
538  double totalLineWidth = 0;
539  int blockIndex = 0;
540  for ( const QgsTextBlock &block : document )
541  {
542  double blockWidth = 0;
543  for ( const QgsTextFragment &fragment : block )
544  {
545  QFont fragmentFont = baseFont;
546  fragment.characterFormat().updateFontForFormat( fragmentFont, scaleFactor );
547  blockWidth = std::max( QFontMetricsF( fragmentFont ).maxWidth(), blockWidth );
548  }
549 
550  totalLineWidth += blockIndex == 0 ? blockWidth : blockWidth * format.lineHeight();
551  blockIndex++;
552  }
553  width = totalLineWidth;
554  break;
555  }
556 
558  {
559  // label mode only
560  break;
561  }
562  }
563 
564  return width / scaleFactor;
565 }
566 
567 double QgsTextRenderer::textHeight( const QgsRenderContext &context, const QgsTextFormat &format, const QStringList &textLines, DrawMode mode, QFontMetricsF * )
568 {
569  if ( !format.allowHtmlFormatting() )
570  {
571  return textHeight( context, format, QgsTextDocument::fromPlainText( textLines ), mode );
572  }
573  else
574  {
575  return textHeight( context, format, QgsTextDocument::fromHtml( textLines ), mode );
576  }
577 }
578 
579 double QgsTextRenderer::textHeight( const QgsRenderContext &context, const QgsTextFormat &format, QChar character, bool includeEffects )
580 {
581  const double scaleFactor = ( context.flags() & QgsRenderContext::ApplyScalingWorkaroundForTextRendering ) ? FONT_WORKAROUND_SCALE : 1.0;
582  const QFont baseFont = format.scaledFont( context, scaleFactor );
583  const QFontMetrics fm( baseFont );
584  const double height = ( character.isNull() ? fm.height() : fm.boundingRect( character ).height() ) / scaleFactor;
585 
586  if ( !includeEffects )
587  return height;
588 
589  double maxExtension = 0;
590  if ( format.buffer().enabled() )
591  {
592  maxExtension += context.convertToPainterUnits( format.buffer().size(), format.buffer().sizeUnit(), format.buffer().sizeMapUnitScale() );
593  }
594  if ( format.shadow().enabled() )
595  {
596  maxExtension += context.convertToPainterUnits( format.shadow().offsetDistance(), format.shadow().offsetUnit(), format.shadow().offsetMapUnitScale() )
597  + context.convertToPainterUnits( format.shadow().blurRadius(), format.shadow().blurRadiusUnit(), format.shadow().blurRadiusMapUnitScale() );
598  }
599  if ( format.background().enabled() )
600  {
601  maxExtension += context.convertToPainterUnits( std::fabs( format.background().offset().y() ), format.background().offsetUnit(), format.background().offsetMapUnitScale() )
602  + context.convertToPainterUnits( format.background().strokeWidth(), format.background().strokeWidthUnit(), format.background().strokeWidthMapUnitScale() ) / 2.0;
603  if ( format.background().sizeType() == QgsTextBackgroundSettings::SizeBuffer && format.background().size().height() > 0 )
604  {
605  maxExtension += context.convertToPainterUnits( format.background().size().height(), format.background().sizeUnit(), format.background().sizeMapUnitScale() );
606  }
607  }
608 
609  return height + maxExtension;
610 }
611 
612 double QgsTextRenderer::textHeight( const QgsRenderContext &context, const QgsTextFormat &format, const QgsTextDocument &doc, DrawMode mode )
613 {
614  QgsTextDocument document = doc;
615  document.applyCapitalization( format.capitalization() );
616 
617  //calculate max height of text lines
618  const double scaleFactor = ( context.flags() & QgsRenderContext::ApplyScalingWorkaroundForTextRendering ) ? FONT_WORKAROUND_SCALE : 1.0;
619 
620  const QFont baseFont = format.scaledFont( context, scaleFactor );
621 
622  switch ( format.orientation() )
623  {
625  {
626  int blockIndex = 0;
627  double totalHeight = 0;
628  double lastLineLeading = 0;
629  for ( const QgsTextBlock &block : document )
630  {
631  double maxBlockHeight = 0;
632  double maxBlockLineSpacing = 0;
633  double maxBlockLeading = 0;
634  for ( const QgsTextFragment &fragment : block )
635  {
636  QFont fragmentFont = baseFont;
637  fragment.characterFormat().updateFontForFormat( fragmentFont, scaleFactor );
638  const QFontMetricsF fm( fragmentFont );
639 
640  const double fragmentHeight = fm.ascent() + fm.descent(); // ignore +1 for baseline
641 
642  maxBlockHeight = std::max( maxBlockHeight, fragmentHeight );
643  if ( fm.lineSpacing() > maxBlockLineSpacing )
644  {
645  maxBlockLineSpacing = fm.lineSpacing();
646  maxBlockLeading = fm.leading();
647  }
648  }
649 
650  switch ( mode )
651  {
652  case Label:
653  // rendering labels needs special handling - in this case text should be
654  // drawn with the bottom left corner coinciding with origin, vs top left
655  // for standard text rendering. Line height is also slightly different.
656  totalHeight += blockIndex == 0 ? maxBlockHeight : maxBlockHeight * format.lineHeight();
657  break;
658 
659  case Rect:
660  case Point:
661  // standard rendering - designed to exactly replicate QPainter's drawText method
662  totalHeight += blockIndex == 0 ? maxBlockHeight : maxBlockLineSpacing * format.lineHeight();
663  if ( blockIndex > 0 )
664  lastLineLeading = maxBlockLeading;
665  break;
666  }
667 
668  blockIndex++;
669  }
670 
671  return ( totalHeight - lastLineLeading ) / scaleFactor;
672  }
673 
675  {
676  double maxBlockHeight = 0;
677  for ( const QgsTextBlock &block : document )
678  {
679  double blockHeight = 0;
680  int fragmentIndex = 0;
681  for ( const QgsTextFragment &fragment : block )
682  {
683  QFont fragmentFont = baseFont;
684  fragment.characterFormat().updateFontForFormat( fragmentFont, scaleFactor );
685  const QFontMetricsF fm( fragmentFont );
686 
687  const double labelHeight = fm.ascent();
688  const double letterSpacing = fragmentFont.letterSpacing();
689 
690  blockHeight += fragmentIndex = 0 ? labelHeight * fragment.text().size() + ( fragment.text().size() - 1 ) * letterSpacing
691  : fragment.text().size() * ( labelHeight + letterSpacing );
692  fragmentIndex++;
693  }
694  maxBlockHeight = std::max( maxBlockHeight, blockHeight );
695  }
696 
697  return maxBlockHeight / scaleFactor;
698  }
699 
701  {
702  // label mode only
703  break;
704  }
705  }
706 
707  return 0;
708 }
709 
710 void QgsTextRenderer::drawBackground( QgsRenderContext &context, QgsTextRenderer::Component component, const QgsTextFormat &format, const QgsTextDocument &document, QgsTextRenderer::DrawMode mode )
711 {
712  QgsTextBackgroundSettings background = format.background();
713 
714  QPainter *prevP = context.painter();
715  QPainter *p = context.painter();
716  std::unique_ptr< QgsPaintEffect > tmpEffect;
717  if ( background.paintEffect() && background.paintEffect()->enabled() )
718  {
719  tmpEffect.reset( background.paintEffect()->clone() );
720  tmpEffect->begin( context );
721  p = context.painter();
722  }
723 
724  //QgsDebugMsgLevel( QStringLiteral( "Background label rotation: %1" ).arg( component.rotation() ), 4 );
725 
726  // shared calculations between shapes and SVG
727 
728  // configure angles, set component rotation and rotationOffset
730  {
731  component.rotation = -( component.rotation * 180 / M_PI ); // RotationSync
732  component.rotationOffset =
733  background.rotationType() == QgsTextBackgroundSettings::RotationOffset ? background.rotation() : 0.0;
734  }
735  else // RotationFixed
736  {
737  component.rotation = 0.0; // don't use label's rotation
738  component.rotationOffset = background.rotation();
739  }
740 
741  const double scaleFactor = ( context.flags() & QgsRenderContext::ApplyScalingWorkaroundForTextRendering ) ? FONT_WORKAROUND_SCALE : 1;
742 
743  if ( mode != Label )
744  {
745  // need to calculate size of text
746  QFontMetricsF fm( format.scaledFont( context, scaleFactor ) );
747  double width = textWidth( context, format, document );
748  double height = textHeight( context, format, document, mode );
749 
750  switch ( mode )
751  {
752  case Rect:
753  switch ( component.hAlign )
754  {
755  case AlignLeft:
756  case AlignJustify:
757  component.center = QPointF( component.origin.x() + width / 2.0,
758  component.origin.y() + height / 2.0 );
759  break;
760 
761  case AlignCenter:
762  component.center = QPointF( component.origin.x() + component.size.width() / 2.0,
763  component.origin.y() + height / 2.0 );
764  break;
765 
766  case AlignRight:
767  component.center = QPointF( component.origin.x() + component.size.width() - width / 2.0,
768  component.origin.y() + height / 2.0 );
769  break;
770  }
771  break;
772 
773  case Point:
774  {
775  double originAdjust = fm.ascent() / scaleFactor / 2.0 - fm.leading() / scaleFactor / 2.0;
776  switch ( component.hAlign )
777  {
778  case AlignLeft:
779  case AlignJustify:
780  component.center = QPointF( component.origin.x() + width / 2.0,
781  component.origin.y() - height / 2.0 + originAdjust );
782  break;
783 
784  case AlignCenter:
785  component.center = QPointF( component.origin.x(),
786  component.origin.y() - height / 2.0 + originAdjust );
787  break;
788 
789  case AlignRight:
790  component.center = QPointF( component.origin.x() - width / 2.0,
791  component.origin.y() - height / 2.0 + originAdjust );
792  break;
793  }
794  break;
795  }
796 
797  case Label:
798  break;
799  }
800 
802  component.size = QSizeF( width, height );
803  }
804 
805  // TODO: the following label-buffered generated shapes and SVG symbols should be moved into marker symbology classes
806 
807  switch ( background.type() )
808  {
811  {
812  // all calculations done in shapeSizeUnits, which are then passed to symbology class for painting
813 
814  if ( background.type() == QgsTextBackgroundSettings::ShapeSVG && background.svgFile().isEmpty() )
815  return;
816 
817  if ( background.type() == QgsTextBackgroundSettings::ShapeMarkerSymbol && !background.markerSymbol() )
818  return;
819 
820  double sizeOut = 0.0;
821  // only one size used for SVG/marker symbol sizing/scaling (no use of shapeSize.y() or Y field in gui)
822  if ( background.sizeType() == QgsTextBackgroundSettings::SizeFixed )
823  {
824  sizeOut = context.convertToPainterUnits( background.size().width(), background.sizeUnit(), background.sizeMapUnitScale() );
825  }
826  else if ( background.sizeType() == QgsTextBackgroundSettings::SizeBuffer )
827  {
828  sizeOut = std::max( component.size.width(), component.size.height() );
829  double bufferSize = context.convertToPainterUnits( background.size().width(), background.sizeUnit(), background.sizeMapUnitScale() );
830 
831  // add buffer
832  sizeOut += bufferSize * 2;
833  }
834 
835  // don't bother rendering symbols smaller than 1x1 pixels in size
836  // TODO: add option to not show any svgs under/over a certain size
837  if ( sizeOut < 1.0 )
838  return;
839 
840  std::unique_ptr< QgsMarkerSymbol > renderedSymbol;
841  if ( background.type() == QgsTextBackgroundSettings::ShapeSVG )
842  {
843  QgsStringMap map; // for SVG symbology marker
844  map[QStringLiteral( "name" )] = background.svgFile().trimmed();
845  map[QStringLiteral( "size" )] = QString::number( sizeOut );
846  map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( QgsUnitTypes::RenderPixels );
847  map[QStringLiteral( "angle" )] = QString::number( 0.0 ); // angle is handled by this local painter
848 
849  // offset is handled by this local painter
850  // TODO: see why the marker renderer doesn't seem to translate offset *after* applying rotation
851  //map["offset"] = QgsSymbolLayerUtils::encodePoint( tmpLyr.shapeOffset );
852  //map["offset_unit"] = QgsUnitTypes::encodeUnit(
853  // tmpLyr.shapeOffsetUnits == QgsPalLayerSettings::MapUnits ? QgsUnitTypes::MapUnit : QgsUnitTypes::MM );
854 
855  map[QStringLiteral( "fill" )] = background.fillColor().name();
856  map[QStringLiteral( "outline" )] = background.strokeColor().name();
857  map[QStringLiteral( "outline-width" )] = QString::number( background.strokeWidth() );
858  map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( background.strokeWidthUnit() );
859 
860  if ( format.shadow().enabled() && format.shadow().shadowPlacement() == QgsTextShadowSettings::ShadowShape )
861  {
862  QgsTextShadowSettings shadow = format.shadow();
863  // configure SVG shadow specs
864  QgsStringMap shdwmap( map );
865  shdwmap[QStringLiteral( "fill" )] = shadow.color().name();
866  shdwmap[QStringLiteral( "outline" )] = shadow.color().name();
867  shdwmap[QStringLiteral( "size" )] = QString::number( sizeOut );
868 
869  // store SVG's drawing in QPicture for drop shadow call
870  QPicture svgPict;
871  QPainter svgp;
872  svgp.begin( &svgPict );
873 
874  // draw shadow symbol
875 
876  // clone current render context map unit/mm conversion factors, but not
877  // other map canvas parameters, then substitute this painter for use in symbology painting
878  // NOTE: this is because the shadow needs to be scaled correctly for output to map canvas,
879  // but will be created relative to the SVG's computed size, not the current map canvas
880  QgsRenderContext shdwContext;
881  shdwContext.setMapToPixel( context.mapToPixel() );
882  shdwContext.setScaleFactor( context.scaleFactor() );
883  shdwContext.setPainter( &svgp );
884 
885  std::unique_ptr< QgsSymbolLayer > symShdwL( QgsSvgMarkerSymbolLayer::create( shdwmap ) );
886  QgsSvgMarkerSymbolLayer *svgShdwM = static_cast<QgsSvgMarkerSymbolLayer *>( symShdwL.get() );
887  QgsSymbolRenderContext svgShdwContext( shdwContext, QgsUnitTypes::RenderUnknownUnit, background.opacity() );
888 
889  svgShdwM->renderPoint( QPointF( sizeOut / 2, -sizeOut / 2 ), svgShdwContext );
890  svgp.end();
891 
892  component.picture = svgPict;
893  // TODO: when SVG symbol's stroke width/units is fixed in QgsSvgCache, adjust for it here
894  component.pictureBuffer = 0.0;
895 
896  component.size = QSizeF( sizeOut, sizeOut );
897  component.offset = QPointF( 0.0, 0.0 );
898 
899  // rotate about origin center of SVG
900  QgsScopedQPainterState painterState( p );
901  context.setPainterFlagsUsingContext( p );
902 
903  p->translate( component.center.x(), component.center.y() );
904  p->rotate( component.rotation );
905  double xoff = context.convertToPainterUnits( background.offset().x(), background.offsetUnit(), background.offsetMapUnitScale() );
906  double yoff = context.convertToPainterUnits( background.offset().y(), background.offsetUnit(), background.offsetMapUnitScale() );
907  p->translate( QPointF( xoff, yoff ) );
908  p->rotate( component.rotationOffset );
909  p->translate( -sizeOut / 2, sizeOut / 2 );
910 
911  drawShadow( context, component, format );
912  }
913  renderedSymbol.reset( );
914 
916  renderedSymbol.reset( new QgsMarkerSymbol( QgsSymbolLayerList() << symL ) );
917  }
918  else
919  {
920  renderedSymbol.reset( background.markerSymbol()->clone() );
921  renderedSymbol->setSize( sizeOut );
922  renderedSymbol->setSizeUnit( QgsUnitTypes::RenderPixels );
923  }
924 
925  renderedSymbol->setOpacity( background.opacity() );
926 
927  // draw the actual symbol
928  QgsScopedQPainterState painterState( p );
929  context.setPainterFlagsUsingContext( p );
930 
931  if ( context.useAdvancedEffects() )
932  {
933  p->setCompositionMode( background.blendMode() );
934  }
935  p->translate( component.center.x(), component.center.y() );
936  p->rotate( component.rotation );
937  double xoff = context.convertToPainterUnits( background.offset().x(), background.offsetUnit(), background.offsetMapUnitScale() );
938  double yoff = context.convertToPainterUnits( background.offset().y(), background.offsetUnit(), background.offsetMapUnitScale() );
939  p->translate( QPointF( xoff, yoff ) );
940  p->rotate( component.rotationOffset );
941 
942  const QgsFeature f = context.expressionContext().feature();
943  renderedSymbol->startRender( context, context.expressionContext().fields() );
944  renderedSymbol->renderPoint( QPointF( 0, 0 ), &f, context );
945  renderedSymbol->stopRender( context );
946  p->setCompositionMode( QPainter::CompositionMode_SourceOver ); // just to be sure
947 
948  break;
949  }
950 
955  {
956  double w = component.size.width();
957  double h = component.size.height();
958 
959  if ( background.sizeType() == QgsTextBackgroundSettings::SizeFixed )
960  {
961  w = context.convertToPainterUnits( background.size().width(), background.sizeUnit(),
962  background.sizeMapUnitScale() );
963  h = context.convertToPainterUnits( background.size().height(), background.sizeUnit(),
964  background.sizeMapUnitScale() );
965  }
966  else if ( background.sizeType() == QgsTextBackgroundSettings::SizeBuffer )
967  {
968  if ( background.type() == QgsTextBackgroundSettings::ShapeSquare )
969  {
970  if ( w > h )
971  h = w;
972  else if ( h > w )
973  w = h;
974  }
975  else if ( background.type() == QgsTextBackgroundSettings::ShapeCircle )
976  {
977  // start with label bound by circle
978  h = std::sqrt( std::pow( w, 2 ) + std::pow( h, 2 ) );
979  w = h;
980  }
981  else if ( background.type() == QgsTextBackgroundSettings::ShapeEllipse )
982  {
983  // start with label bound by ellipse
984  h = h * M_SQRT1_2 * 2;
985  w = w * M_SQRT1_2 * 2;
986  }
987 
988  double bufferWidth = context.convertToPainterUnits( background.size().width(), background.sizeUnit(),
989  background.sizeMapUnitScale() );
990  double bufferHeight = context.convertToPainterUnits( background.size().height(), background.sizeUnit(),
991  background.sizeMapUnitScale() );
992 
993  w += bufferWidth * 2;
994  h += bufferHeight * 2;
995  }
996 
997  // offsets match those of symbology: -x = left, -y = up
998  QRectF rect( -w / 2.0, - h / 2.0, w, h );
999 
1000  if ( rect.isNull() )
1001  return;
1002 
1003  QgsScopedQPainterState painterState( p );
1004  context.setPainterFlagsUsingContext( p );
1005 
1006  p->translate( QPointF( component.center.x(), component.center.y() ) );
1007  p->rotate( component.rotation );
1008  double xoff = context.convertToPainterUnits( background.offset().x(), background.offsetUnit(), background.offsetMapUnitScale() );
1009  double yoff = context.convertToPainterUnits( background.offset().y(), background.offsetUnit(), background.offsetMapUnitScale() );
1010  p->translate( QPointF( xoff, yoff ) );
1011  p->rotate( component.rotationOffset );
1012 
1013  double penSize = context.convertToPainterUnits( background.strokeWidth(), background.strokeWidthUnit(), background.strokeWidthMapUnitScale() );
1014 
1015  QPen pen;
1016  if ( background.strokeWidth() > 0 )
1017  {
1018  pen.setColor( background.strokeColor() );
1019  pen.setWidthF( penSize );
1020  if ( background.type() == QgsTextBackgroundSettings::ShapeRectangle )
1021  pen.setJoinStyle( background.joinStyle() );
1022  }
1023  else
1024  {
1025  pen = Qt::NoPen;
1026  }
1027 
1028  // store painting in QPicture for shadow drawing
1029  QPicture shapePict;
1030  QPainter shapep;
1031  shapep.begin( &shapePict );
1032  shapep.setPen( pen );
1033  shapep.setBrush( background.fillColor() );
1034 
1035  if ( background.type() == QgsTextBackgroundSettings::ShapeRectangle
1036  || background.type() == QgsTextBackgroundSettings::ShapeSquare )
1037  {
1038  if ( background.radiiUnit() == QgsUnitTypes::RenderPercentage )
1039  {
1040  shapep.drawRoundedRect( rect, background.radii().width(), background.radii().height(), Qt::RelativeSize );
1041  }
1042  else
1043  {
1044  double xRadius = context.convertToPainterUnits( background.radii().width(), background.radiiUnit(), background.radiiMapUnitScale() );
1045  double yRadius = context.convertToPainterUnits( background.radii().height(), background.radiiUnit(), background.radiiMapUnitScale() );
1046  shapep.drawRoundedRect( rect, xRadius, yRadius );
1047  }
1048  }
1049  else if ( background.type() == QgsTextBackgroundSettings::ShapeEllipse
1050  || background.type() == QgsTextBackgroundSettings::ShapeCircle )
1051  {
1052  shapep.drawEllipse( rect );
1053  }
1054  shapep.end();
1055 
1056  if ( format.shadow().enabled() && format.shadow().shadowPlacement() == QgsTextShadowSettings::ShadowShape )
1057  {
1058  component.picture = shapePict;
1059  component.pictureBuffer = penSize / 2.0;
1060 
1061  component.size = rect.size();
1062  component.offset = QPointF( rect.width() / 2, -rect.height() / 2 );
1063  drawShadow( context, component, format );
1064  }
1065 
1066  p->setOpacity( background.opacity() );
1067  if ( context.useAdvancedEffects() )
1068  {
1069  p->setCompositionMode( background.blendMode() );
1070  }
1071 
1072  // scale for any print output or image saving @ specific dpi
1073  p->scale( component.dpiRatio, component.dpiRatio );
1074  _fixQPictureDPI( p );
1075  p->drawPicture( 0, 0, shapePict );
1076  break;
1077  }
1078  }
1079 
1080  if ( tmpEffect )
1081  {
1082  tmpEffect->end( context );
1083  context.setPainter( prevP );
1084  }
1085 }
1086 
1087 void QgsTextRenderer::drawShadow( QgsRenderContext &context, const QgsTextRenderer::Component &component, const QgsTextFormat &format )
1088 {
1089  QgsTextShadowSettings shadow = format.shadow();
1090 
1091  // incoming component sizes should be multiplied by rasterCompressFactor, as
1092  // this allows shadows to be created at paint device dpi (e.g. high resolution),
1093  // then scale device painter by 1.0 / rasterCompressFactor for output
1094 
1095  QPainter *p = context.painter();
1096  double componentWidth = component.size.width(), componentHeight = component.size.height();
1097  double xOffset = component.offset.x(), yOffset = component.offset.y();
1098  double pictbuffer = component.pictureBuffer;
1099 
1100  // generate pixmap representation of label component drawing
1101  bool mapUnits = shadow.blurRadiusUnit() == QgsUnitTypes::RenderMapUnits;
1102  double radius = context.convertToPainterUnits( shadow.blurRadius(), shadow.blurRadiusUnit(), shadow.blurRadiusMapUnitScale() );
1103  radius /= ( mapUnits ? context.scaleFactor() / component.dpiRatio : 1 );
1104  radius = static_cast< int >( radius + 0.5 ); //NOLINT
1105 
1106  // TODO: add labeling gui option to adjust blurBufferClippingScale to minimize pixels, or
1107  // to ensure shadow isn't clipped too tight. (Or, find a better method of buffering)
1108  double blurBufferClippingScale = 3.75;
1109  int blurbuffer = ( radius > 17 ? 16 : radius ) * blurBufferClippingScale;
1110 
1111  QImage blurImg( componentWidth + ( pictbuffer * 2.0 ) + ( blurbuffer * 2.0 ),
1112  componentHeight + ( pictbuffer * 2.0 ) + ( blurbuffer * 2.0 ),
1113  QImage::Format_ARGB32_Premultiplied );
1114 
1115  // TODO: add labeling gui option to not show any shadows under/over a certain size
1116  // keep very small QImages from causing paint device issues, i.e. must be at least > 1
1117  int minBlurImgSize = 1;
1118  // max limitation on QgsSvgCache is 10,000 for screen, which will probably be reasonable for future caching here, too
1119  // 4 x QgsSvgCache limit for output to print/image at higher dpi
1120  // TODO: should it be higher, scale with dpi, or have no limit? Needs testing with very large labels rendered at high dpi output
1121  int maxBlurImgSize = 40000;
1122  if ( blurImg.isNull()
1123  || ( blurImg.width() < minBlurImgSize || blurImg.height() < minBlurImgSize )
1124  || ( blurImg.width() > maxBlurImgSize || blurImg.height() > maxBlurImgSize ) )
1125  return;
1126 
1127  blurImg.fill( QColor( Qt::transparent ).rgba() );
1128  QPainter pictp;
1129  if ( !pictp.begin( &blurImg ) )
1130  return;
1131  pictp.setRenderHints( QPainter::Antialiasing | QPainter::SmoothPixmapTransform );
1132  QPointF imgOffset( blurbuffer + pictbuffer + xOffset,
1133  blurbuffer + pictbuffer + componentHeight + yOffset );
1134 
1135  pictp.drawPicture( imgOffset,
1136  component.picture );
1137 
1138  // overlay shadow color
1139  pictp.setCompositionMode( QPainter::CompositionMode_SourceIn );
1140  pictp.fillRect( blurImg.rect(), shadow.color() );
1141  pictp.end();
1142 
1143  // blur the QImage in-place
1144  if ( shadow.blurRadius() > 0.0 && radius > 0 )
1145  {
1146  QgsSymbolLayerUtils::blurImageInPlace( blurImg, blurImg.rect(), radius, shadow.blurAlphaOnly() );
1147  }
1148 
1149 #if 0
1150  // debug rect for QImage shadow registration and clipping visualization
1151  QPainter picti;
1152  picti.begin( &blurImg );
1153  picti.setBrush( Qt::Dense7Pattern );
1154  QPen imgPen( QColor( 0, 0, 255, 255 ) );
1155  imgPen.setWidth( 1 );
1156  picti.setPen( imgPen );
1157  picti.setOpacity( 0.1 );
1158  picti.drawRect( 0, 0, blurImg.width(), blurImg.height() );
1159  picti.end();
1160 #endif
1161 
1162  double offsetDist = context.convertToPainterUnits( shadow.offsetDistance(), shadow.offsetUnit(), shadow.offsetMapUnitScale() );
1163  double angleRad = shadow.offsetAngle() * M_PI / 180; // to radians
1164  if ( shadow.offsetGlobal() )
1165  {
1166  // TODO: check for differences in rotation origin and cw/ccw direction,
1167  // when this shadow function is used for something other than labels
1168 
1169  // it's 0-->cw-->360 for labels
1170  //QgsDebugMsgLevel( QStringLiteral( "Shadow aggregated label rotation (degrees): %1" ).arg( component.rotation() + component.rotationOffset() ), 4 );
1171  angleRad -= ( component.rotation * M_PI / 180 + component.rotationOffset * M_PI / 180 );
1172  }
1173 
1174  QPointF transPt( -offsetDist * std::cos( angleRad + M_PI_2 ),
1175  -offsetDist * std::sin( angleRad + M_PI_2 ) );
1176 
1177  p->save();
1178  p->setRenderHint( QPainter::SmoothPixmapTransform );
1179  context.setPainterFlagsUsingContext( p );
1180  if ( context.useAdvancedEffects() )
1181  {
1182  p->setCompositionMode( shadow.blendMode() );
1183  }
1184  p->setOpacity( shadow.opacity() );
1185 
1186  double scale = shadow.scale() / 100.0;
1187  // TODO: scale from center/center, left/center or left/top, instead of default left/bottom?
1188  p->scale( scale, scale );
1189  if ( component.useOrigin )
1190  {
1191  p->translate( component.origin.x(), component.origin.y() );
1192  }
1193  p->translate( transPt );
1194  p->translate( -imgOffset.x(),
1195  -imgOffset.y() );
1196  p->drawImage( 0, 0, blurImg );
1197  p->restore();
1198 
1199  // debug rects
1200 #if 0
1201  // draw debug rect for QImage painting registration
1202  p->save();
1203  p->setBrush( Qt::NoBrush );
1204  QPen imgPen( QColor( 255, 0, 0, 10 ) );
1205  imgPen.setWidth( 2 );
1206  imgPen.setStyle( Qt::DashLine );
1207  p->setPen( imgPen );
1208  p->scale( scale, scale );
1209  if ( component.useOrigin() )
1210  {
1211  p->translate( component.origin().x(), component.origin().y() );
1212  }
1213  p->translate( transPt );
1214  p->translate( -imgOffset.x(),
1215  -imgOffset.y() );
1216  p->drawRect( 0, 0, blurImg.width(), blurImg.height() );
1217  p->restore();
1218 
1219  // draw debug rect for passed in component dimensions
1220  p->save();
1221  p->setBrush( Qt::NoBrush );
1222  QPen componentRectPen( QColor( 0, 255, 0, 70 ) );
1223  componentRectPen.setWidth( 1 );
1224  if ( component.useOrigin() )
1225  {
1226  p->translate( component.origin().x(), component.origin().y() );
1227  }
1228  p->setPen( componentRectPen );
1229  p->drawRect( QRect( -xOffset, -componentHeight - yOffset, componentWidth, componentHeight ) );
1230  p->restore();
1231 #endif
1232 }
1233 
1234 
1235 void QgsTextRenderer::drawTextInternal( TextPart drawType,
1236  QgsRenderContext &context,
1237  const QgsTextFormat &format,
1238  const Component &component,
1239  const QgsTextDocument &document,
1240  const QFontMetricsF *fontMetrics,
1241  HAlignment alignment, VAlignment vAlignment, DrawMode mode )
1242 {
1243  if ( !context.painter() )
1244  {
1245  return;
1246  }
1247 
1248  double fontScale = 1.0;
1249  std::unique_ptr< QFontMetricsF > tmpMetrics;
1250  if ( !fontMetrics )
1251  {
1253  const QFont f = format.scaledFont( context, fontScale );
1254  tmpMetrics = qgis::make_unique< QFontMetricsF >( f );
1255  fontMetrics = tmpMetrics.get();
1256  }
1257 
1258  double rotation = 0;
1259  const QgsTextFormat::TextOrientation orientation = calculateRotationAndOrientationForComponent( format, component, rotation );
1260  switch ( orientation )
1261  {
1263  {
1264  drawTextInternalHorizontal( context, format, drawType, mode, component, document, fontScale, fontMetrics, alignment, vAlignment, rotation );
1265  break;
1266  }
1267 
1270  {
1271  drawTextInternalVertical( context, format, drawType, mode, component, document, fontScale, fontMetrics, alignment, vAlignment, rotation );
1272  break;
1273  }
1274  }
1275 }
1276 
1277 QgsTextFormat::TextOrientation QgsTextRenderer::calculateRotationAndOrientationForComponent( const QgsTextFormat &format, const QgsTextRenderer::Component &component, double &rotation )
1278 {
1279  rotation = -component.rotation * 180 / M_PI;
1280 
1281  switch ( format.orientation() )
1282  {
1284  {
1285  // Between 45 to 135 and 235 to 315 degrees, rely on vertical orientation
1286  if ( rotation >= -315 && rotation < -90 )
1287  {
1288  rotation -= 90;
1290  }
1291  else if ( rotation >= -90 && rotation < -45 )
1292  {
1293  rotation += 90;
1295  }
1296 
1298  }
1299 
1302  return format.orientation();
1303  }
1305 }
1306 
1307 void QgsTextRenderer::calculateExtraSpacingForLineJustification( const double spaceToDistribute, const QgsTextBlock &block, double &extraWordSpace, double &extraLetterSpace )
1308 {
1309  const QString blockText = block.toPlainText();
1310  QTextBoundaryFinder finder( QTextBoundaryFinder::Word, blockText );
1311  finder.toStart();
1312  int wordBoundaries = 0;
1313  while ( finder.toNextBoundary() != -1 )
1314  {
1315  if ( finder.boundaryReasons() & QTextBoundaryFinder::StartOfItem )
1316  wordBoundaries++;
1317  }
1318 
1319  if ( wordBoundaries > 0 )
1320  {
1321  // word boundaries found => justify by padding word spacing
1322  extraWordSpace = spaceToDistribute / wordBoundaries;
1323  }
1324  else
1325  {
1326  // no word boundaries found => justify by letter spacing
1327  QTextBoundaryFinder finder( QTextBoundaryFinder::Grapheme, blockText );
1328  finder.toStart();
1329 
1330  int graphemeBoundaries = 0;
1331  while ( finder.toNextBoundary() != -1 )
1332  {
1333  if ( finder.boundaryReasons() & QTextBoundaryFinder::StartOfItem )
1334  graphemeBoundaries++;
1335  }
1336 
1337  if ( graphemeBoundaries > 0 )
1338  {
1339  extraLetterSpace = spaceToDistribute / graphemeBoundaries;
1340  }
1341  }
1342 }
1343 
1344 void QgsTextRenderer::applyExtraSpacingForLineJustification( QFont &font, double extraWordSpace, double extraLetterSpace )
1345 {
1346  const double prevWordSpace = font.wordSpacing();
1347  font.setWordSpacing( prevWordSpace + extraWordSpace );
1348  const double prevLetterSpace = font.letterSpacing();
1349  font.setLetterSpacing( QFont::AbsoluteSpacing, prevLetterSpace + extraLetterSpace );
1350 }
1351 
1352 void QgsTextRenderer::drawTextInternalHorizontal( QgsRenderContext &context, const QgsTextFormat &format, TextPart drawType, DrawMode mode, const Component &component, const QgsTextDocument &document, double fontScale, const QFontMetricsF *fontMetrics, HAlignment hAlignment,
1353  VAlignment vAlignment, double rotation )
1354 {
1355  QPainter *maskPainter = context.maskPainter( context.currentMaskId() );
1356  const QStringList textLines = document.toPlainText();
1357 
1358  double labelWidest = 0.0;
1359  switch ( mode )
1360  {
1361  case Label:
1362  case Point:
1363  for ( const QString &line : textLines )
1364  {
1365 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
1366  double labelWidth = fontMetrics->width( line ) / fontScale;
1367 #else
1368  double labelWidth = fontMetrics->horizontalAdvance( line ) / fontScale;
1369 #endif
1370  if ( labelWidth > labelWidest )
1371  {
1372  labelWidest = labelWidth;
1373  }
1374  }
1375  break;
1376 
1377  case Rect:
1378  labelWidest = component.size.width();
1379  break;
1380  }
1381 
1382  double labelHeight = ( fontMetrics->ascent() + fontMetrics->descent() ) / fontScale; // ignore +1 for baseline
1383  // double labelHighest = labelfm->height() + ( double )(( lines - 1 ) * labelHeight * tmpLyr.multilineHeight );
1384 
1385  // needed to move bottom of text's descender to within bottom edge of label
1386  double ascentOffset = 0.25 * fontMetrics->ascent() / fontScale; // labelfm->descent() is not enough
1387 
1388  int i = 0;
1389 
1390  bool adjustForAlignment = hAlignment != AlignLeft && ( mode != Label || textLines.size() > 1 );
1391 
1392  if ( mode == Rect && vAlignment != AlignTop )
1393  {
1394  // need to calculate overall text height in advance so that we can adjust for vertical alignment
1395  const double overallHeight = textHeight( context, format, textLines, Rect );
1396  switch ( vAlignment )
1397  {
1398  case AlignTop:
1399  break;
1400 
1401  case AlignVCenter:
1402  ascentOffset = -( component.size.height() - overallHeight ) * 0.5 + ascentOffset;
1403  break;
1404 
1405  case AlignBottom:
1406  ascentOffset = -( component.size.height() - overallHeight ) + ascentOffset;
1407  break;
1408  }
1409  }
1410 
1411  for ( const QString &line : qgis::as_const( textLines ) )
1412  {
1413  const QgsTextBlock block = document.at( i );
1414 
1415  const bool isFinalLine = i == document.size() - 1;
1416 
1417  QgsScopedQPainterState painterState( context.painter() );
1418  context.setPainterFlagsUsingContext();
1419  context.painter()->translate( component.origin );
1420  if ( !qgsDoubleNear( rotation, 0.0 ) )
1421  context.painter()->rotate( rotation );
1422 
1423  // apply to the mask painter the same transformations
1424  if ( maskPainter )
1425  {
1426  maskPainter->save();
1427  maskPainter->translate( component.origin );
1428  if ( !qgsDoubleNear( rotation, 0.0 ) )
1429  maskPainter->rotate( rotation );
1430  }
1431 
1432  // figure x offset for horizontal alignment of multiple lines
1433  double xMultiLineOffset = 0.0;
1434 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
1435  double labelWidth = fontMetrics->width( line ) / fontScale;
1436 #else
1437  double labelWidth = fontMetrics->horizontalAdvance( line ) / fontScale;
1438 #endif
1439  double extraWordSpace = 0;
1440  double extraLetterSpace = 0;
1441  if ( adjustForAlignment )
1442  {
1443  double labelWidthDiff = 0;
1444  switch ( hAlignment )
1445  {
1446  case AlignCenter:
1447  labelWidthDiff = ( labelWidest - labelWidth ) * 0.5;
1448  break;
1449 
1450  case AlignRight:
1451  labelWidthDiff = labelWidest - labelWidth;
1452  break;
1453 
1454  case AlignJustify:
1455  if ( !isFinalLine && labelWidest > labelWidth )
1456  {
1457  calculateExtraSpacingForLineJustification( labelWidest - labelWidth, block, extraWordSpace, extraLetterSpace );
1458  }
1459  break;
1460 
1461  case AlignLeft:
1462  break;
1463  }
1464 
1465  switch ( mode )
1466  {
1467  case Label:
1468  case Rect:
1469  xMultiLineOffset = labelWidthDiff;
1470  break;
1471 
1472  case Point:
1473  {
1474  switch ( hAlignment )
1475  {
1476  case AlignRight:
1477  xMultiLineOffset = labelWidthDiff - labelWidest;
1478  break;
1479 
1480  case AlignCenter:
1481  xMultiLineOffset = labelWidthDiff - labelWidest / 2.0;
1482  break;
1483 
1484  case AlignLeft:
1485  case AlignJustify:
1486  break;
1487  }
1488  }
1489  break;
1490  }
1491  }
1492 
1493  double yMultiLineOffset = ascentOffset;
1494  switch ( mode )
1495  {
1496  case Label:
1497  // rendering labels needs special handling - in this case text should be
1498  // drawn with the bottom left corner coinciding with origin, vs top left
1499  // for standard text rendering. Line height is also slightly different.
1500  yMultiLineOffset = - ascentOffset - ( textLines.size() - 1 - i ) * labelHeight * format.lineHeight();
1501  break;
1502 
1503  case Rect:
1504  // standard rendering - designed to exactly replicate QPainter's drawText method
1505  yMultiLineOffset = - ascentOffset + labelHeight - 1 /*baseline*/ + format.lineHeight() * fontMetrics->lineSpacing() * i / fontScale;
1506  break;
1507 
1508  case Point:
1509  // standard rendering - designed to exactly replicate QPainter's drawText rect method
1510  yMultiLineOffset = 0 - ( textLines.size() - 1 - i ) * fontMetrics->lineSpacing() * format.lineHeight() / fontScale;
1511  break;
1512 
1513  }
1514 
1515  context.painter()->translate( QPointF( xMultiLineOffset, yMultiLineOffset ) );
1516  if ( maskPainter )
1517  maskPainter->translate( QPointF( xMultiLineOffset, yMultiLineOffset ) );
1518 
1519  Component subComponent;
1520  subComponent.block = block;
1521  subComponent.size = QSizeF( labelWidth, labelHeight );
1522  subComponent.offset = QPointF( 0.0, -ascentOffset );
1523  subComponent.rotation = -component.rotation * 180 / M_PI;
1524  subComponent.rotationOffset = 0.0;
1525  subComponent.extraWordSpacing = extraWordSpace * fontScale;
1526  subComponent.extraLetterSpacing = extraLetterSpace * fontScale;
1527 
1528  // draw the mask below the text (for preview)
1529  if ( format.mask().enabled() )
1530  {
1531  QgsTextRenderer::drawMask( context, subComponent, format );
1532  }
1533 
1534  if ( drawType == QgsTextRenderer::Buffer )
1535  {
1536  QgsTextRenderer::drawBuffer( context, subComponent, format );
1537  }
1538  else
1539  {
1540  // store text's drawing in QPicture for drop shadow call
1541  QPicture textPict;
1542  QPainter textp;
1543  textp.begin( &textPict );
1544  textp.setPen( Qt::NoPen );
1545  const QFont font = format.scaledFont( context, fontScale );
1546  textp.scale( 1 / fontScale, 1 / fontScale );
1547 
1548  double xOffset = 0;
1549  for ( const QgsTextFragment &fragment : block )
1550  {
1551  // draw text, QPainterPath method
1552  QPainterPath path;
1553  path.setFillRule( Qt::WindingFill );
1554 
1555  QFont fragmentFont = font;
1556  fragment.characterFormat().updateFontForFormat( fragmentFont, fontScale );
1557 
1558  if ( extraWordSpace || extraLetterSpace )
1559  applyExtraSpacingForLineJustification( fragmentFont, extraWordSpace * fontScale, extraLetterSpace * fontScale );
1560 
1561  path.addText( xOffset, 0, fragmentFont, fragment.text() );
1562 
1563  QColor textColor = fragment.characterFormat().textColor().isValid() ? fragment.characterFormat().textColor() : format.color();
1564  textColor.setAlphaF( format.opacity() );
1565  textp.setBrush( textColor );
1566  textp.drawPath( path );
1567 
1568  xOffset += fragment.horizontalAdvance( fragmentFont, true );
1569  }
1570  textp.end();
1571 
1572  if ( format.shadow().enabled() && format.shadow().shadowPlacement() == QgsTextShadowSettings::ShadowText )
1573  {
1574  subComponent.picture = textPict;
1575  subComponent.pictureBuffer = 0.0; // no pen width to deal with
1576  subComponent.origin = QPointF( 0.0, 0.0 );
1577 
1578  QgsTextRenderer::drawShadow( context, subComponent, format );
1579  }
1580 
1581  // paint the text
1582  if ( context.useAdvancedEffects() )
1583  {
1584  context.painter()->setCompositionMode( format.blendMode() );
1585  }
1586 
1587  // scale for any print output or image saving @ specific dpi
1588  context.painter()->scale( subComponent.dpiRatio, subComponent.dpiRatio );
1589 
1590  switch ( context.textRenderFormat() )
1591  {
1593  {
1594  // draw outlined text
1595  _fixQPictureDPI( context.painter() );
1596  context.painter()->drawPicture( 0, 0, textPict );
1597  break;
1598  }
1599 
1601  {
1602  double xOffset = 0;
1603  for ( const QgsTextFragment &fragment : block )
1604  {
1605  QFont fragmentFont = font;
1606  fragment.characterFormat().updateFontForFormat( fragmentFont, fontScale );
1607 
1608  if ( extraWordSpace || extraLetterSpace )
1609  applyExtraSpacingForLineJustification( fragmentFont, extraWordSpace * fontScale, extraLetterSpace * fontScale );
1610 
1611  QColor textColor = fragment.characterFormat().textColor().isValid() ? fragment.characterFormat().textColor() : format.color();
1612  textColor.setAlphaF( format.opacity() );
1613 
1614  context.painter()->setPen( textColor );
1615  context.painter()->setFont( fragmentFont );
1616  context.painter()->setRenderHint( QPainter::TextAntialiasing );
1617 
1618  context.painter()->scale( 1 / fontScale, 1 / fontScale );
1619  context.painter()->drawText( xOffset, 0, fragment.text() );
1620  context.painter()->scale( fontScale, fontScale );
1621 
1622  xOffset += fragment.horizontalAdvance( fragmentFont, true, fontScale );
1623  }
1624  }
1625  }
1626  }
1627  if ( maskPainter )
1628  maskPainter->restore();
1629  i++;
1630  }
1631 }
1632 
1633 void QgsTextRenderer::drawTextInternalVertical( QgsRenderContext &context, const QgsTextFormat &format, QgsTextRenderer::TextPart drawType, QgsTextRenderer::DrawMode mode, const QgsTextRenderer::Component &component, const QgsTextDocument &document, double fontScale, const QFontMetricsF *fontMetrics, QgsTextRenderer::HAlignment hAlignment, QgsTextRenderer::VAlignment, double rotation )
1634 {
1635  QPainter *maskPainter = context.maskPainter( context.currentMaskId() );
1636  const QStringList textLines = document.toPlainText();
1637 
1638  const QFont font = format.scaledFont( context, fontScale );
1639  double letterSpacing = font.letterSpacing() / fontScale;
1640 
1641  double labelWidth = fontMetrics->maxWidth() / fontScale; // label width represents the width of one line of a multi-line label
1642  double actualLabelWidest = labelWidth + ( textLines.size() - 1 ) * labelWidth * format.lineHeight();
1643  double labelWidest = 0.0;
1644  switch ( mode )
1645  {
1646  case Label:
1647  case Point:
1648  labelWidest = actualLabelWidest;
1649  break;
1650 
1651  case Rect:
1652  labelWidest = component.size.width();
1653  break;
1654  }
1655 
1656  int maxLineLength = 0;
1657  for ( const QString &line : qgis::as_const( textLines ) )
1658  {
1659  maxLineLength = std::max( maxLineLength, line.length() );
1660  }
1661  double actualLabelHeight = fontMetrics->ascent() / fontScale + ( fontMetrics->ascent() / fontScale + letterSpacing ) * ( maxLineLength - 1 );
1662  double ascentOffset = fontMetrics->ascent() / fontScale;
1663 
1664  int i = 0;
1665 
1666  bool adjustForAlignment = hAlignment != AlignLeft && ( mode != Label || textLines.size() > 1 );
1667 
1668  for ( const QgsTextBlock &block : document )
1669  {
1670  QgsScopedQPainterState painterState( context.painter() );
1671  context.setPainterFlagsUsingContext();
1672 
1673  context.painter()->translate( component.origin );
1674  if ( !qgsDoubleNear( rotation, 0.0 ) )
1675  context.painter()->rotate( rotation );
1676 
1677  // apply to the mask painter the same transformations
1678  if ( maskPainter )
1679  {
1680  maskPainter->save();
1681  maskPainter->translate( component.origin );
1682  if ( !qgsDoubleNear( rotation, 0.0 ) )
1683  maskPainter->rotate( rotation );
1684  }
1685 
1686  // figure x offset of multiple lines
1687  double xOffset = actualLabelWidest - labelWidth - ( i * labelWidth * format.lineHeight() );
1688  if ( adjustForAlignment )
1689  {
1690  double labelWidthDiff = 0;
1691  switch ( hAlignment )
1692  {
1693  case AlignCenter:
1694  labelWidthDiff = ( labelWidest - actualLabelWidest ) * 0.5;
1695  break;
1696 
1697  case AlignRight:
1698  labelWidthDiff = labelWidest - actualLabelWidest;
1699  break;
1700 
1701  case AlignLeft:
1702  case AlignJustify:
1703  break;
1704  }
1705 
1706  switch ( mode )
1707  {
1708  case Label:
1709  case Rect:
1710  xOffset += labelWidthDiff;
1711  break;
1712 
1713  case Point:
1714  break;
1715  }
1716  }
1717 
1718  double yOffset = 0.0;
1719  switch ( mode )
1720  {
1721  case Label:
1723  {
1724  if ( rotation >= -405 && rotation < -180 )
1725  {
1726  yOffset = ascentOffset;
1727  }
1728  else if ( rotation >= 0 && rotation < 45 )
1729  {
1730  xOffset -= actualLabelWidest;
1731  yOffset = -actualLabelHeight + ascentOffset + fontMetrics->descent() / fontScale;
1732  }
1733  }
1734  else
1735  {
1736  yOffset = -actualLabelHeight + ascentOffset;
1737  }
1738  break;
1739 
1740  case Point:
1741  yOffset = -actualLabelHeight + ascentOffset;
1742  break;
1743 
1744  case Rect:
1745  yOffset = ascentOffset;
1746  break;
1747  }
1748 
1749  context.painter()->translate( QPointF( xOffset, yOffset ) );
1750 
1751  double fragmentYOffset = 0;
1752  for ( const QgsTextFragment &fragment : block )
1753  {
1754  // apply some character replacement to draw symbols in vertical presentation
1755  const QString line = QgsStringUtils::substituteVerticalCharacters( fragment.text() );
1756 
1757  QFont fragmentFont( font );
1758  fragment.characterFormat().updateFontForFormat( fragmentFont, fontScale );
1759 
1760  QFontMetricsF fragmentMetrics( fragmentFont );
1761 
1762  double labelHeight = fragmentMetrics.ascent() / fontScale + ( fragmentMetrics.ascent() / fontScale + letterSpacing ) * ( line.length() - 1 );
1763 
1764  Component subComponent;
1765  subComponent.block = QgsTextBlock( fragment );
1766  subComponent.size = QSizeF( labelWidth, labelHeight );
1767  subComponent.offset = QPointF( 0.0, fragmentYOffset );
1768  subComponent.rotation = -component.rotation * 180 / M_PI;
1769  subComponent.rotationOffset = 0.0;
1770 
1771  // draw the mask below the text (for preview)
1772  if ( format.mask().enabled() )
1773  {
1774  // WARNING: totally broken! (has been since mask was introduced)
1775 #if 0
1776  QgsTextRenderer::drawMask( context, subComponent, format );
1777 #endif
1778  }
1779 
1780  if ( drawType == QgsTextRenderer::Buffer )
1781  {
1782  fragmentYOffset += QgsTextRenderer::drawBuffer( context, subComponent, format );
1783  }
1784  else
1785  {
1786  // draw text, QPainterPath method
1787  QPainterPath path;
1788  path.setFillRule( Qt::WindingFill );
1789  const QStringList parts = QgsPalLabeling::splitToGraphemes( fragment.text() );
1790  double partYOffset = 0.0;
1791  for ( const auto &part : parts )
1792  {
1793 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
1794  double partXOffset = ( labelWidth - ( fragmentMetrics.width( part ) / fontScale - letterSpacing ) ) / 2;
1795 #else
1796  double partXOffset = ( labelWidth - ( fragmentMetrics.horizontalAdvance( part ) / fontScale - letterSpacing ) ) / 2;
1797 #endif
1798  path.addText( partXOffset * fontScale, partYOffset * fontScale, fragmentFont, part );
1799  partYOffset += fragmentMetrics.ascent() / fontScale + letterSpacing;
1800  }
1801 
1802  // store text's drawing in QPicture for drop shadow call
1803  QPicture textPict;
1804  QPainter textp;
1805  textp.begin( &textPict );
1806  textp.setPen( Qt::NoPen );
1807  QColor textColor = fragment.characterFormat().textColor().isValid() ? fragment.characterFormat().textColor() : format.color();
1808  textColor.setAlphaF( format.opacity() );
1809  textp.setBrush( textColor );
1810  textp.scale( 1 / fontScale, 1 / fontScale );
1811  textp.drawPath( path );
1812  // TODO: why are some font settings lost on drawPicture() when using drawText() inside QPicture?
1813  // e.g. some capitalization options, but not others
1814  //textp.setFont( tmpLyr.textFont );
1815  //textp.setPen( tmpLyr.textColor );
1816  //textp.drawText( 0, 0, component.text() );
1817  textp.end();
1818 
1819  if ( format.shadow().enabled() && format.shadow().shadowPlacement() == QgsTextShadowSettings::ShadowText )
1820  {
1821  subComponent.picture = textPict;
1822  subComponent.pictureBuffer = 0.0; // no pen width to deal with
1823  subComponent.origin = QPointF( 0.0, fragmentYOffset );
1824  const double prevY = subComponent.offset.y();
1825  subComponent.offset = QPointF( 0, -labelHeight );
1826  subComponent.useOrigin = true;
1827  QgsTextRenderer::drawShadow( context, subComponent, format );
1828  subComponent.useOrigin = false;
1829  subComponent.offset = QPointF( 0, prevY );
1830  }
1831 
1832  // paint the text
1833  if ( context.useAdvancedEffects() )
1834  {
1835  context.painter()->setCompositionMode( format.blendMode() );
1836  }
1837 
1838  // scale for any print output or image saving @ specific dpi
1839  context.painter()->scale( subComponent.dpiRatio, subComponent.dpiRatio );
1840 
1841  switch ( context.textRenderFormat() )
1842  {
1844  {
1845  // draw outlined text
1846  _fixQPictureDPI( context.painter() );
1847  context.painter()->drawPicture( 0, fragmentYOffset, textPict );
1848  fragmentYOffset += partYOffset;
1849  break;
1850  }
1851 
1853  {
1854  context.painter()->setFont( fragmentFont );
1855  context.painter()->setPen( textColor );
1856  context.painter()->setRenderHint( QPainter::TextAntialiasing );
1857 
1858  double partYOffset = 0.0;
1859  for ( const QString &part : parts )
1860  {
1861 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
1862  double partXOffset = ( labelWidth - ( fragmentMetrics.width( part ) / fontScale - letterSpacing ) ) / 2;
1863 #else
1864  double partXOffset = ( labelWidth - ( fragmentMetrics.horizontalAdvance( part ) / fontScale - letterSpacing ) ) / 2;
1865 #endif
1866  context.painter()->scale( 1 / fontScale, 1 / fontScale );
1867  context.painter()->drawText( partXOffset * fontScale, ( fragmentYOffset + partYOffset ) * fontScale, part );
1868  context.painter()->scale( fontScale, fontScale );
1869  partYOffset += fragmentMetrics.ascent() / fontScale + letterSpacing;
1870  }
1871  fragmentYOffset += partYOffset;
1872  }
1873  }
1874  }
1875  }
1876 
1877  if ( maskPainter )
1878  maskPainter->restore();
1879  i++;
1880  }
1881 }
QgsRenderContext::textRenderFormat
TextRenderFormat textRenderFormat() const
Returns the text render format, which dictates how text is rendered (e.g.
Definition: qgsrendercontext.h:695
QgsTextRenderer::drawPart
static Q_DECL_DEPRECATED 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.
Definition: qgstextrenderer.cpp:142
QgsSymbolLayerUtils::blurImageInPlace
static void blurImageInPlace(QImage &image, QRect rect, int radius, bool alphaOnly)
Blurs an image in place, e.g. creating Qt-independent drop shadows.
Definition: qgssymbollayerutils.cpp:3767
qgstextdocument.h
QgsRenderContext::maskPainter
QPainter * maskPainter(int id=0)
Returns a mask QPainter for the render operation.
Definition: qgsrendercontext.h:211
QgsTextMaskSettings::enabled
bool enabled() const
Returns whether the mask is enabled.
Definition: qgstextmasksettings.cpp:64
QgsTextDocument::fromPlainText
static QgsTextDocument fromPlainText(const QStringList &lines)
Constructor for QgsTextDocument consisting of a set of plain text lines.
Definition: qgstextdocument.cpp:38
qgspallabeling.h
QgsRenderContext::convertToPainterUnits
double convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale()) const
Converts a size from the specified units to painter units (pixels).
Definition: qgsrendercontext.cpp:318
QgsRenderContext::setPainterFlagsUsingContext
void setPainterFlagsUsingContext(QPainter *painter=nullptr) const
Sets relevant flags on a destination painter, using the flags and settings currently defined for the ...
Definition: qgsrendercontext.cpp:143
QgsTextShadowSettings::offsetDistance
double offsetDistance() const
Returns the distance for offsetting the position of the shadow from the text.
Definition: qgstextshadowsettings.cpp:103
QgsRenderContext::mapToPixel
const QgsMapToPixel & mapToPixel() const
Returns the context's map to pixel transform, which transforms between map coordinates and device coo...
Definition: qgsrendercontext.h:325
QgsTextBackgroundSettings::radiiUnit
QgsUnitTypes::RenderUnit radiiUnit() const
Returns the units used for the shape's radii.
Definition: qgstextbackgroundsettings.cpp:230
QgsTextFormat::scaledFont
QFont scaledFont(const QgsRenderContext &context, double scaleFactor=1.0) const
Returns a font with the size scaled to match the format's size settings (including units and map unit...
Definition: qgstextformat.cpp:156
QgsTextRenderer::TextPart
TextPart
Components of text.
Definition: qgstextrenderer.h:50
QgsTextBufferSettings::sizeMapUnitScale
QgsMapUnitScale sizeMapUnitScale() const
Returns the map unit scale object for the buffer size.
Definition: qgstextbuffersettings.cpp:102
QgsUnitTypes::RenderUnit
RenderUnit
Rendering size units.
Definition: qgsunittypes.h:167
QgsRenderContext::expressionContext
QgsExpressionContext & expressionContext()
Gets the expression context.
Definition: qgsrendercontext.h:596
QgsTextRenderer::drawText
static void drawText(const QRectF &rect, double rotation, HAlignment alignment, const QStringList &textLines, QgsRenderContext &context, const QgsTextFormat &format, bool drawAsOutlines=true, VAlignment vAlignment=AlignTop)
Draws text within a rectangle using the specified settings.
Definition: qgstextrenderer.cpp:75
QgsTextBackgroundSettings::enabled
bool enabled() const
Returns whether the background is enabled.
Definition: qgstextbackgroundsettings.cpp:90
QgsTextDocument::size
int size() const
Returns the number of blocks in the document.
Definition: qgstextdocument.cpp:110
QgsEffectPainter
A class to manager painter saving and restoring required for effect drawing.
Definition: qgspainteffect.h:397
QgsTextFormat::shadow
QgsTextShadowSettings & shadow()
Returns a reference to the text drop shadow settings.
Definition: qgstextformat.cpp:127
QgsTextRenderer::convertQtVAlignment
static VAlignment convertQtVAlignment(Qt::Alignment alignment)
Converts a Qt vertical alignment flag to a QgsTextRenderer::VAlignment value.
Definition: qgstextrenderer.cpp:55
QgsTextShadowSettings::blurRadiusMapUnitScale
QgsMapUnitScale blurRadiusMapUnitScale() const
Returns the map unit scale object for the shadow blur radius.
Definition: qgstextshadowsettings.cpp:163
qgstextfragment.h
QgsTextRenderer::Background
@ Background
Background shape.
Definition: qgstextrenderer.h:53
qgstextrenderer.h
QgsTextBufferSettings::joinStyle
Qt::PenJoinStyle joinStyle() const
Returns the buffer join style.
Definition: qgstextbuffersettings.cpp:142
QgsTextFormat::orientation
TextOrientation orientation() const
Returns the orientation of the text.
Definition: qgstextformat.cpp:276
QgsTextShadowSettings::ShadowBuffer
@ ShadowBuffer
Draw shadow under buffer.
Definition: qgstextshadowsettings.h:48
QgsTextFormat::buffer
QgsTextBufferSettings & buffer()
Returns a reference to the text buffer settings.
Definition: qgstextformat.cpp:103
QgsTextRenderer::AlignCenter
@ AlignCenter
Center align.
Definition: qgstextrenderer.h:61
QgsTextBackgroundSettings::offsetMapUnitScale
QgsMapUnitScale offsetMapUnitScale() const
Returns the map unit scale object for the shape offset.
Definition: qgstextbackgroundsettings.cpp:210
QgsTextRenderer::AlignRight
@ AlignRight
Right align.
Definition: qgstextrenderer.h:62
QgsRenderContext::setPainter
void setPainter(QPainter *p)
Sets the destination QPainter for the render operation.
Definition: qgsrendercontext.h:491
QgsTextShadowSettings::offsetUnit
QgsUnitTypes::RenderUnit offsetUnit() const
Returns the units used for the shadow's offset.
Definition: qgstextshadowsettings.cpp:113
qgssymbollayerutils.h
QgsTextFormat::background
QgsTextBackgroundSettings & background()
Returns a reference to the text background settings.
Definition: qgstextformat.cpp:115
QgsTextRenderer::fontMetrics
static QFontMetricsF fontMetrics(QgsRenderContext &context, const QgsTextFormat &format, double scaleFactor=1.0)
Returns the font metrics for the given text format, when rendered in the specified render context.
Definition: qgstextrenderer.cpp:261
QgsTextBlock::size
int size() const
Returns the number of fragments in the block.
Definition: qgstextblock.cpp:54
qgsmarkersymbollayer.h
QgsTextBackgroundSettings::fillColor
QColor fillColor() const
Returns the color used for filing the background shape.
Definition: qgstextbackgroundsettings.cpp:270
QgsTextBackgroundSettings
Container for settings relating to a text background object.
Definition: qgstextbackgroundsettings.h:46
QgsTextFragment::text
QString text() const
Returns the text content of the fragment.
Definition: qgstextfragment.cpp:32
QgsUnitTypes::RenderPercentage
@ RenderPercentage
Percentage of another measurement (e.g., canvas size, feature size)
Definition: qgsunittypes.h:171
QgsTextShadowSettings::scale
int scale() const
Returns the scaling used for the drop shadow (in percentage of original size).
Definition: qgstextshadowsettings.cpp:193
QgsTextBufferSettings::opacity
double opacity() const
Returns the buffer opacity.
Definition: qgstextbuffersettings.cpp:132
QgsRenderContext
Contains information about the context of a rendering operation.
Definition: qgsrendercontext.h:58
QgsTextCharacterFormat::textColor
QColor textColor() const
Returns the character's text color, or an invalid color if no color override is set and the default f...
Definition: qgstextcharacterformat.cpp:35
QgsTextRenderer::VAlignment
VAlignment
Vertical alignment.
Definition: qgstextrenderer.h:79
QgsTextBackgroundSettings::offset
QPointF offset() const
Returns the offset used for drawing the background shape.
Definition: qgstextbackgroundsettings.cpp:190
QgsTextBackgroundSettings::strokeWidthMapUnitScale
QgsMapUnitScale strokeWidthMapUnitScale() const
Returns the map unit scale object for the shape stroke width.
Definition: qgstextbackgroundsettings.cpp:310
QgsTextRenderer::Shadow
@ Shadow
Drop shadow.
Definition: qgstextrenderer.h:54
QgsTextBackgroundSettings::opacity
double opacity() const
Returns the background shape's opacity.
Definition: qgstextbackgroundsettings.cpp:250
QgsTextBackgroundSettings::RotationOffset
@ RotationOffset
Shape rotation is offset from text rotation.
Definition: qgstextbackgroundsettings.h:78
FALLTHROUGH
#define FALLTHROUGH
Definition: qgis.h:828
QgsTextBackgroundSettings::strokeWidthUnit
QgsUnitTypes::RenderUnit strokeWidthUnit() const
Returns the units used for the shape's stroke width.
Definition: qgstextbackgroundsettings.cpp:300
qgspainteffect.h
QgsRenderContext::scaleFactor
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
Definition: qgsrendercontext.h:333
qt_defaultDpiY
Q_GUI_EXPORT int qt_defaultDpiY()
QgsTextRenderer::Text
@ Text
Text component.
Definition: qgstextrenderer.h:51
QgsTextShadowSettings::blurRadius
double blurRadius() const
Returns the blur radius for the shadow.
Definition: qgstextshadowsettings.cpp:143
QgsTextBackgroundSettings::sizeType
SizeType sizeType() const
Returns the method used to determine the size of the background shape (e.g., fixed size or buffer aro...
Definition: qgstextbackgroundsettings.cpp:130
QgsTextBackgroundSettings::blendMode
QPainter::CompositionMode blendMode() const
Returns the blending mode used for drawing the background shape.
Definition: qgstextbackgroundsettings.cpp:260
QgsExpressionContext::fields
QgsFields fields() const
Convenience function for retrieving the fields for the context, if set.
Definition: qgsexpressioncontext.cpp:561
QgsTextShadowSettings::enabled
bool enabled() const
Returns whether the shadow is enabled.
Definition: qgstextshadowsettings.cpp:73
QgsTextMaskSettings::size
double size() const
Returns the size of the buffer.
Definition: qgstextmasksettings.cpp:85
QgsMarkerSymbol::clone
QgsMarkerSymbol * clone() const override
Returns a deep copy of this symbol.
Definition: qgssymbol.cpp:1937
QgsTextBackgroundSettings::ShapeSquare
@ ShapeSquare
Square - buffered sizes only.
Definition: qgstextbackgroundsettings.h:55
QgsRenderContext::currentMaskId
int currentMaskId() const
Returns the current mask id, which can be used with maskPainter()
Definition: qgsrendercontext.h:755
QgsTextBlock::toPlainText
QString toPlainText() const
Converts the block to plain text.
Definition: qgstextblock.cpp:24
QgsTextFormat::VerticalOrientation
@ VerticalOrientation
Horizontally oriented text.
Definition: qgstextformat.h:47
QgsTextFormat::color
QColor color() const
Returns the color that text will be rendered in.
Definition: qgstextformat.cpp:232
QgsTextRenderer::textWidth
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.
Definition: qgstextrenderer.cpp:496
QgsTextRenderer::Rect
@ Rect
Text within rectangle draw mode.
Definition: qgstextrenderer.h:43
QgsTextDocument::applyCapitalization
void applyCapitalization(QgsStringUtils::Capitalization capitalization)
Applies a capitalization style to the document's text.
Definition: qgstextdocument.cpp:190
QgsTextRenderer::DrawMode
DrawMode
Draw mode to calculate width and height.
Definition: qgstextrenderer.h:42
QgsSymbolRenderContext
Definition: qgssymbol.h:695
QgsTextShadowSettings::blurAlphaOnly
bool blurAlphaOnly() const
Returns whether only the alpha channel for the shadow will be blurred.
Definition: qgstextshadowsettings.cpp:173
QgsTextBackgroundSettings::sizeMapUnitScale
QgsMapUnitScale sizeMapUnitScale() const
Returns the map unit scale object for the shape size.
Definition: qgstextbackgroundsettings.cpp:160
QgsTextBackgroundSettings::rotationType
RotationType rotationType() const
Returns the method used for rotating the background shape.
Definition: qgstextbackgroundsettings.cpp:170
qt_defaultDpiX
Q_GUI_EXPORT int qt_defaultDpiX()
QgsTextFormat
Container for all settings relating to text rendering.
Definition: qgstextformat.h:40
QgsTextBackgroundSettings::size
QSizeF size() const
Returns the size of the background shape.
Definition: qgstextbackgroundsettings.cpp:140
QgsTextShadowSettings::blendMode
QPainter::CompositionMode blendMode() const
Returns the blending mode used for drawing the drop shadow.
Definition: qgstextshadowsettings.cpp:213
QgsSymbolLayer
Definition: qgssymbollayer.h:53
QgsTextRenderer::Point
@ Point
Text at point of origin draw mode.
Definition: qgstextrenderer.h:44
QgsTextBackgroundSettings::ShapeRectangle
@ ShapeRectangle
Rectangle.
Definition: qgstextbackgroundsettings.h:54
QgsRenderContext::useAdvancedEffects
bool useAdvancedEffects() const
Returns true if advanced effects such as blend modes such be used.
Definition: qgsrendercontext.cpp:251
QgsTextBackgroundSettings::joinStyle
Qt::PenJoinStyle joinStyle() const
Returns the join style used for drawing the background shape.
Definition: qgstextbackgroundsettings.cpp:320
QgsUnitTypes::encodeUnit
static Q_INVOKABLE QString encodeUnit(QgsUnitTypes::DistanceUnit unit)
Encodes a distance unit to a string.
Definition: qgsunittypes.cpp:122
QgsTextRenderer::AlignTop
@ AlignTop
Align to top.
Definition: qgstextrenderer.h:80
finder
const char * finder(const char *name)
Definition: qgscoordinatetransform.cpp:950
QgsTextFormat::updateDataDefinedProperties
void updateDataDefinedProperties(QgsRenderContext &context)
Updates the format by evaluating current values of data defined properties.
Definition: qgstextformat.cpp:728
QgsTextDocument::at
const QgsTextBlock & at(int index) const
Returns the block at the specified index.
Definition: qgstextdocument.cpp:100
QgsStringUtils::substituteVerticalCharacters
static QString substituteVerticalCharacters(QString string)
Returns a string with characters having vertical representation form substituted.
Definition: qgsstringutils.cpp:655
QgsPropertyCollection::hasActiveProperties
bool hasActiveProperties() const override
Returns true if the collection has any active properties, or false if all properties within the colle...
Definition: qgspropertycollection.cpp:304
QgsTextDocument::toPlainText
QStringList toPlainText() const
Returns a list of plain text lines of text representing the document.
Definition: qgstextdocument.cpp:115
QgsTextFragment
Stores a fragment of text along with formatting overrides to be used when rendering the fragment.
Definition: qgstextfragment.h:36
QgsTextMaskSettings
Container for settings relating to a selective masking around a text.
Definition: qgstextmasksettings.h:42
QgsMarkerSymbol
A marker symbol type, for rendering Point and MultiPoint geometries.
Definition: qgssymbol.h:931
qgsDoubleNear
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:315
QgsTextBackgroundSettings::ShapeCircle
@ ShapeCircle
Circle.
Definition: qgstextbackgroundsettings.h:57
QgsTextShadowSettings::ShadowLowest
@ ShadowLowest
Draw shadow below all text components.
Definition: qgstextshadowsettings.h:46
QgsRenderContext::TextFormatAlwaysText
@ TextFormatAlwaysText
Always render text as text objects.
Definition: qgsrendercontext.h:132
QgsTextBackgroundSettings::strokeWidth
double strokeWidth() const
Returns the width of the shape's stroke (stroke).
Definition: qgstextbackgroundsettings.cpp:290
QgsPaintEffect::clone
virtual QgsPaintEffect * clone() const =0
Duplicates an effect by creating a deep copy of the effect.
QgsSvgMarkerSymbolLayer
Definition: qgsmarkersymbollayer.h:483
QgsTextShadowSettings::offsetMapUnitScale
QgsMapUnitScale offsetMapUnitScale() const
Returns the map unit scale object for the shadow offset distance.
Definition: qgstextshadowsettings.cpp:123
QgsTextRenderer::AlignJustify
@ AlignJustify
Justify align.
Definition: qgstextrenderer.h:63
QgsTextFormat::allowHtmlFormatting
bool allowHtmlFormatting() const
Returns true if text should be treated as a HTML document and HTML tags should be used for formatting...
Definition: qgstextformat.cpp:300
QgsTextShadowSettings::color
QColor color() const
Returns the color of the drop shadow.
Definition: qgstextshadowsettings.cpp:203
QgsSvgMarkerSymbolLayer::create
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
Definition: qgsmarkersymbollayer.cpp:1761
QgsRenderContext::setMapToPixel
void setMapToPixel(const QgsMapToPixel &mtp)
Sets the context's map to pixel transform, which transforms between map coordinates and device coordi...
Definition: qgsrendercontext.h:420
QgsTextFormat::lineHeight
double lineHeight() const
Returns the line height for text.
Definition: qgstextformat.cpp:265
QgsTextRenderer::AlignLeft
@ AlignLeft
Left align.
Definition: qgstextrenderer.h:60
QgsTextBackgroundSettings::SizeBuffer
@ SizeBuffer
Shape size is determined by adding a buffer margin around text.
Definition: qgstextbackgroundsettings.h:67
QgsTextBackgroundSettings::rotation
double rotation() const
Returns the rotation for the background shape, in degrees clockwise.
Definition: qgstextbackgroundsettings.cpp:180
QgsSvgMarkerSymbolLayer::renderPoint
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
Definition: qgsmarkersymbollayer.cpp:1961
QgsTextMaskSettings::opacity
double opacity() const
Returns the mask's opacity.
Definition: qgstextmasksettings.cpp:125
QgsTextFormat::opacity
double opacity() const
Returns the text's opacity.
Definition: qgstextformat.cpp:243
QgsTextShadowSettings::setShadowPlacement
void setShadowPlacement(QgsTextShadowSettings::ShadowPlacement placement)
Sets the placement for the drop shadow.
Definition: qgstextshadowsettings.cpp:88
QgsTextShadowSettings::offsetGlobal
bool offsetGlobal() const
Returns true if the global shadow offset will be used.
Definition: qgstextshadowsettings.cpp:133
QgsTextMaskSettings::sizeMapUnitScale
QgsMapUnitScale sizeMapUnitScale() const
Returns the map unit scale object for the buffer size.
Definition: qgstextmasksettings.cpp:105
QgsTextRenderer::textHeight
static double textHeight(const QgsRenderContext &context, const QgsTextFormat &format, const QStringList &textLines, DrawMode mode=Point, QFontMetricsF *fontMetrics=nullptr)
Returns the height of a text based on a given format.
Definition: qgstextrenderer.cpp:567
QgsMapUnitScale
Struct for storing maximum and minimum scales for measurements in map units.
Definition: qgsmapunitscale.h:38
QgsTextFormat::capitalization
QgsStringUtils::Capitalization capitalization() const
Returns the text capitalization style.
Definition: qgstextformat.cpp:287
QgsTextDocument::fromHtml
static QgsTextDocument fromHtml(const QStringList &lines)
Constructor for QgsTextDocument consisting of a set of HTML formatted lines.
Definition: qgstextdocument.cpp:47
QgsTextBackgroundSettings::radiiMapUnitScale
QgsMapUnitScale radiiMapUnitScale() const
Returns the map unit scale object for the shape radii.
Definition: qgstextbackgroundsettings.cpp:240
QgsScopedQPainterState
Scoped object for saving and restoring a QPainter object's state.
Definition: qgsrendercontext.h:1120
QgsTextBufferSettings
Container for settings relating to a text buffer.
Definition: qgstextbuffersettings.h:43
QgsTextBufferSettings::sizeUnit
QgsUnitTypes::RenderUnit sizeUnit() const
Returns the units for the buffer size.
Definition: qgstextbuffersettings.cpp:92
QgsTextBackgroundSettings::type
ShapeType type() const
Returns the type of background shape (e.g., square, ellipse, SVG).
Definition: qgstextbackgroundsettings.cpp:100
QgsTextBackgroundSettings::svgFile
QString svgFile() const
Returns the absolute path to the background SVG file, if set.
Definition: qgstextbackgroundsettings.cpp:110
QgsTextShadowSettings::ShadowShape
@ ShadowShape
Draw shadow under background shape.
Definition: qgstextshadowsettings.h:49
QgsTextBufferSettings::size
double size() const
Returns the size of the buffer.
Definition: qgstextbuffersettings.cpp:82
QgsUnitTypes::RenderPixels
@ RenderPixels
Pixels.
Definition: qgsunittypes.h:170
QgsTextShadowSettings::offsetAngle
int offsetAngle() const
Returns the angle for offsetting the position of the shadow from the text.
Definition: qgstextshadowsettings.cpp:93
QgsTextBackgroundSettings::ShapeEllipse
@ ShapeEllipse
Ellipse.
Definition: qgstextbackgroundsettings.h:56
QgsTextBlock
Represents a block of text consisting of one or more QgsTextFragment objects.
Definition: qgstextblock.h:36
qgsvectorlayer.h
QgsTextFormat::mask
QgsTextMaskSettings & mask()
Returns a reference to the masking settings.
Definition: qgstextformat.cpp:139
QgsTextRenderer::sizeToPixel
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...
Definition: qgstextrenderer.cpp:70
QgsTextShadowSettings
Container for settings relating to a text shadow.
Definition: qgstextshadowsettings.h:38
QgsTextBufferSettings::enabled
bool enabled() const
Returns whether the buffer is enabled.
Definition: qgstextbuffersettings.cpp:72
QgsTextCharacterFormat::updateFontForFormat
void updateFontForFormat(QFont &font, double scaleFactor=1.0) const
Updates the specified font in place, applying character formatting options which are applicable on a ...
Definition: qgstextcharacterformat.cpp:75
QgsStringMap
QMap< QString, QString > QgsStringMap
Definition: qgis.h:758
QgsTextFragment::characterFormat
const QgsTextCharacterFormat & characterFormat() const
Returns the character formatting for the fragment.
Definition: qgstextfragment.h:68
QgsTextFormat::RotationBasedOrientation
@ RotationBasedOrientation
Horizontally or vertically oriented text based on rotation (only available for map labeling)
Definition: qgstextformat.h:48
QgsTextBackgroundSettings::ShapeSVG
@ ShapeSVG
SVG file.
Definition: qgstextbackgroundsettings.h:58
QgsTextFormat::dataDefinedProperties
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the format's property collection, used for data defined overrides.
Definition: qgstextformat.cpp:701
c
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
Definition: porting_processing.dox:1
QgsTextBackgroundSettings::SizeFixed
@ SizeFixed
Fixed size.
Definition: qgstextbackgroundsettings.h:68
QgsSymbolLayerList
QList< QgsSymbolLayer * > QgsSymbolLayerList
Definition: qgssymbol.h:53
QgsTextBackgroundSettings::paintEffect
const QgsPaintEffect * paintEffect() const
Returns the current paint effect for the background shape.
Definition: qgstextbackgroundsettings.cpp:330
QgsTextDocument
Represents a document consisting of one or more QgsTextBlock objects.
Definition: qgstextdocument.h:39
QgsExpressionContext::feature
QgsFeature feature() const
Convenience function for retrieving the feature for the context, if set.
Definition: qgsexpressioncontext.cpp:540
QgsTextShadowSettings::ShadowText
@ ShadowText
Draw shadow under text.
Definition: qgstextshadowsettings.h:47
QgsRenderContext::ApplyScalingWorkaroundForTextRendering
@ ApplyScalingWorkaroundForTextRendering
Whether a scaling workaround designed to stablise the rendering of small font sizes (or for painters ...
Definition: qgsrendercontext.h:85
QgsRenderContext::isGuiPreview
bool isGuiPreview() const
Returns the Gui preview mode.
Definition: qgsrendercontext.h:778
QgsUnitTypes::RenderUnknownUnit
@ RenderUnknownUnit
Mixed or unknown units.
Definition: qgsunittypes.h:174
QgsTextRenderer::Label
@ Label
Label-specific draw mode.
Definition: qgstextrenderer.h:45
QgsTextMaskSettings::paintEffect
QgsPaintEffect * paintEffect() const
Returns the current paint effect for the mask.
Definition: qgstextmasksettings.cpp:135
QgsTextMaskSettings::sizeUnit
QgsUnitTypes::RenderUnit sizeUnit() const
Returns the units for the buffer size.
Definition: qgstextmasksettings.cpp:95
QgsTextFragment::horizontalAdvance
double horizontalAdvance(const QFont &font, bool fontHasBeenUpdatedForFragment=false, double scaleFactor=1.0) const
Returns the horizontal advance associated with this fragment, when rendered using the specified base ...
Definition: qgstextfragment.cpp:47
QgsTextRenderer::AlignVCenter
@ AlignVCenter
Center align.
Definition: qgstextrenderer.h:81
QgsTextBackgroundSettings::sizeUnit
QgsUnitTypes::RenderUnit sizeUnit() const
Returns the units used for the shape's size.
Definition: qgstextbackgroundsettings.cpp:150
qgspainterswapper.h
QgsTextShadowSettings::opacity
double opacity() const
Returns the shadow's opacity.
Definition: qgstextshadowsettings.cpp:183
QgsFeature
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:56
qgstextformat.h
QgsTextRenderer::Buffer
@ Buffer
Buffer component.
Definition: qgstextrenderer.h:52
QgsTextFormat::blendMode
QPainter::CompositionMode blendMode() const
Returns the blending mode used for drawing the text.
Definition: qgstextformat.cpp:254
QgsRenderContext::painter
QPainter * painter()
Returns the destination QPainter for the render operation.
Definition: qgsrendercontext.h:179
QgsPaintEffect::enabled
bool enabled() const
Returns whether the effect is enabled.
Definition: qgspainteffect.h:198
QgsTextBackgroundSettings::ShapeMarkerSymbol
@ ShapeMarkerSymbol
Marker symbol.
Definition: qgstextbackgroundsettings.h:59
QgsTextBufferSettings::color
QColor color() const
Returns the color of the buffer.
Definition: qgstextbuffersettings.cpp:112
QgsTextBufferSettings::fillBufferInterior
bool fillBufferInterior() const
Returns whether the interior of the buffer will be filled in.
Definition: qgstextbuffersettings.cpp:122
QgsTextBackgroundSettings::RotationFixed
@ RotationFixed
Shape rotation is a fixed angle.
Definition: qgstextbackgroundsettings.h:79
QgsRenderContext::TextFormatAlwaysOutlines
@ TextFormatAlwaysOutlines
Always render text using path objects (AKA outlines/curves).
Definition: qgsrendercontext.h:116
QgsTextRenderer::convertQtHAlignment
static HAlignment convertQtHAlignment(Qt::Alignment alignment)
Converts a Qt horizontal alignment flag to a QgsTextRenderer::HAlignment value.
Definition: qgstextrenderer.cpp:40
QgsTextBackgroundSettings::markerSymbol
QgsMarkerSymbol * markerSymbol() const
Returns the marker symbol to be rendered in the background.
Definition: qgstextbackgroundsettings.cpp:120
QgsTextShadowSettings::shadowPlacement
QgsTextShadowSettings::ShadowPlacement shadowPlacement() const
Returns the placement for the drop shadow.
Definition: qgstextshadowsettings.cpp:83
QgsTextFormat::HorizontalOrientation
@ HorizontalOrientation
Vertically oriented text.
Definition: qgstextformat.h:46
QgsTextBufferSettings::blendMode
QPainter::CompositionMode blendMode() const
Returns the blending mode used for drawing the buffer.
Definition: qgstextbuffersettings.cpp:152
MathUtils::angle
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
QgsTextShadowSettings::blurRadiusUnit
QgsUnitTypes::RenderUnit blurRadiusUnit() const
Returns the units used for the shadow's blur radius.
Definition: qgstextshadowsettings.cpp:153
QgsTextRenderer::FONT_WORKAROUND_SCALE
static constexpr double FONT_WORKAROUND_SCALE
Scale factor for upscaling font sizes and downscaling destination painter devices.
Definition: qgstextrenderer.h:240
QgsUnitTypes::RenderMapUnits
@ RenderMapUnits
Map units.
Definition: qgsunittypes.h:169
QgsPainterSwapper
A class to manage painter saving and restoring required for drawing on a different painter (mask pain...
Definition: qgspainterswapper.h:35
QgsTextFormat::TextOrientation
TextOrientation
Text orientation.
Definition: qgstextformat.h:45
QgsTextRenderer::AlignBottom
@ AlignBottom
Align to bottom.
Definition: qgstextrenderer.h:82
QgsTextBackgroundSettings::strokeColor
QColor strokeColor() const
Returns the color used for outlining the background shape.
Definition: qgstextbackgroundsettings.cpp:280
QgsRenderContext::setScaleFactor
void setScaleFactor(double factor)
Sets the scaling factor for the render to convert painter units to physical sizes.
Definition: qgsrendercontext.h:476
QgsTextBackgroundSettings::offsetUnit
QgsUnitTypes::RenderUnit offsetUnit() const
Returns the units used for the shape's offset.
Definition: qgstextbackgroundsettings.cpp:200
QgsTextMaskSettings::joinStyle
Qt::PenJoinStyle joinStyle() const
Returns the buffer join style.
Definition: qgstextmasksettings.cpp:115
QgsTextBackgroundSettings::radii
QSizeF radii() const
Returns the radii used for rounding the corners of shapes.
Definition: qgstextbackgroundsettings.cpp:220
QgsTextRenderer::HAlignment
HAlignment
Horizontal alignment.
Definition: qgstextrenderer.h:59
QgsRenderContext::flags
Flags flags() const
Returns combination of flags used for rendering.
Definition: qgsrendercontext.cpp:187
QgsTextBufferSettings::paintEffect
const QgsPaintEffect * paintEffect() const
Returns the current paint effect for the buffer.
Definition: qgstextbuffersettings.cpp:162
QgsPalLabeling::splitToGraphemes
static QStringList splitToGraphemes(const QString &text)
Splits a text string to a list of graphemes, which are the smallest allowable character divisions in ...
Definition: qgspallabeling.cpp:3653