35#include <QTextBoundaryFinder> 
   40static void _fixQPictureDPI( QPainter *p )
 
   46  p->scale( 
static_cast< double >( 
qt_defaultDpiX() ) / p->device()->logicalDpiX(),
 
   47            static_cast< double >( 
qt_defaultDpiY() ) / p->device()->logicalDpiY() );
 
   52  if ( alignment & Qt::AlignLeft )
 
   53    return Qgis::TextHorizontalAlignment::Left;
 
   54  else if ( alignment & Qt::AlignRight )
 
   55    return Qgis::TextHorizontalAlignment::Right;
 
   56  else if ( alignment & Qt::AlignHCenter )
 
   57    return Qgis::TextHorizontalAlignment::Center;
 
   58  else if ( alignment & Qt::AlignJustify )
 
   59    return Qgis::TextHorizontalAlignment::Justify;
 
   62  return Qgis::TextHorizontalAlignment::Left;
 
   67  if ( alignment & Qt::AlignTop )
 
   68    return Qgis::TextVerticalAlignment::Top;
 
   69  else if ( alignment & Qt::AlignBottom )
 
   70    return Qgis::TextVerticalAlignment::Bottom;
 
   71  else if ( alignment & Qt::AlignVCenter )
 
   72    return Qgis::TextVerticalAlignment::VerticalCenter;
 
   74  else if ( alignment & Qt::AlignBaseline )
 
   75    return Qgis::TextVerticalAlignment::Bottom;
 
   77  return Qgis::TextVerticalAlignment::Top;
 
   82  return static_cast< int >( 
c.convertToPainterUnits( size, unit, mapUnitScale ) + 0.5 ); 
 
   92  QStringList textLines;
 
   93  for ( 
const QString &line : text )
 
   97      textLines.append( 
wrappedText( context, line, rect.width(), format ) );
 
  101      textLines.append( line );
 
  108  const double fontScale = calculateScaleFactorForFormat( context, format );
 
  111  drawDocument( rect, tmpFormat, document, metrics, context, alignment, vAlignment, rotation, mode, flags );
 
  116  const QgsTextFormat tmpFormat = updateShadowPosition( format );
 
  136  tmpFormat = updateShadowPosition( tmpFormat );
 
  140  const double fontScale = calculateScaleFactorForFormat( context, format );
 
  161  tmpFormat = updateShadowPosition( tmpFormat );
 
  167  drawDocumentOnLine( line, tmpFormat, document, context, offsetAlongLine, offsetFromLine );
 
  172  QPolygonF labelBaselineCurve = line;
 
  177    std::unique_ptr < QgsLineString > offsetCurve( 
dynamic_cast< QgsLineString * 
>( 
geos.offsetCurve( offsetFromLine, 4, Qgis::JoinStyle::Round, 2 ) ) );
 
  181#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<11 
  182    if ( offsetFromLine < 0 )
 
  185      std::unique_ptr < QgsLineString > reversed( offsetCurve->reversed() );
 
  189      offsetCurve = std::move( reversed );
 
  193    labelBaselineCurve = offsetCurve->asQPolygonF();
 
  196  const double fontScale = calculateScaleFactorForFormat( context, format );
 
  198  const QFont baseFont = format.
scaledFont( context, fontScale );
 
  199  const double letterSpacing = baseFont.letterSpacing() / fontScale;
 
  200  const double wordSpacing = baseFont.wordSpacing() / fontScale;
 
  202  QStringList graphemes;
 
  203  QVector< QgsTextCharacterFormat > graphemeFormats;
 
  204  QVector< QgsTextDocument > graphemeDocuments;
 
  205  QVector< QgsTextDocumentMetrics > graphemeMetrics;
 
  207  for ( 
const QgsTextBlock &block : std::as_const( document ) )
 
  212      for ( 
const QString &grapheme : fragmentGraphemes )
 
  214        graphemes.append( grapheme );
 
  215        graphemeFormats.append( fragment.characterFormat() );
 
  220        graphemeDocuments.append( document );
 
  226  QVector< double > characterWidths( graphemes.count() );
 
  227  QVector< double > characterHeights( graphemes.count() );
 
  228  QVector< double > characterDescents( graphemes.count() );
 
  229  QFont previousNonSuperSubScriptFont;
 
  231  for ( 
int i = 0; i < graphemes.count(); i++ )
 
  236    double graphemeFirstCharHorizontalAdvanceWithLetterSpacing = 0;
 
  237    double graphemeFirstCharHorizontalAdvance = 0;
 
  238    double graphemeHorizontalAdvance = 0;
 
  239    double characterDescent = 0;
 
  240    double characterHeight = 0;
 
  243    QFont graphemeFont = baseFont;
 
  247      previousNonSuperSubScriptFont = graphemeFont;
 
  254          previousNonSuperSubScriptFont = graphemeFont;
 
  275      previousNonSuperSubScriptFont = graphemeFont;
 
  278    const QFontMetricsF graphemeFontMetrics( graphemeFont );
 
  279    graphemeFirstCharHorizontalAdvance = graphemeFontMetrics.horizontalAdvance( QString( graphemes[i].at( 0 ) ) ) / fontScale;
 
  280    graphemeFirstCharHorizontalAdvanceWithLetterSpacing = graphemeFontMetrics.horizontalAdvance( graphemes[i].at( 0 ) )  / fontScale + letterSpacing;
 
  281    graphemeHorizontalAdvance = graphemeFontMetrics.horizontalAdvance( QString( graphemes[i] ) )  / fontScale;
 
  282    characterDescent = graphemeFontMetrics.descent() / fontScale;
 
  283    characterHeight = graphemeFontMetrics.height() / fontScale;
 
  285    qreal wordSpaceFix = qreal( 0.0 );
 
  286    if ( graphemes[i] == QLatin1String( 
" " ) )
 
  290      wordSpaceFix = ( nxt < graphemes.count() && graphemes[nxt] != QLatin1String( 
" " ) ) ? wordSpacing : qreal( 0.0 );
 
  295    if ( graphemes[i].length() == 1 &&
 
  296         !
qgsDoubleNear( graphemeFirstCharHorizontalAdvance, graphemeFirstCharHorizontalAdvanceWithLetterSpacing ) )
 
  299      wordSpaceFix -= wordSpacing;
 
  302    const double charWidth = graphemeHorizontalAdvance + wordSpaceFix;
 
  303    characterWidths[i] = charWidth;
 
  304    characterHeights[i] = characterHeight;
 
  305    characterDescents[i] = characterDescent;
 
  308  QgsPrecalculatedTextMetrics metrics( graphemes, std::move( characterWidths ), std::move( characterHeights ), std::move( characterDescents ) );
 
  312        metrics, labelBaselineCurve, offsetAlongLine,
 
  319  if ( placement->graphemePlacement.empty() )
 
  322  std::vector< QgsTextRenderer::Component > components;
 
  323  components.reserve( placement->graphemePlacement.size() );
 
  326    QgsTextRenderer::Component component;
 
  327    component.origin = QPointF( grapheme.x, grapheme.y );
 
  328    component.rotation = -grapheme.angle;
 
  334      component.origin.rx() += verticalOffset * std::cos( grapheme.angle + M_PI_2 );
 
  335      component.origin.ry() += verticalOffset * std::sin( grapheme.angle + M_PI_2 );
 
  338    components.emplace_back( component );
 
  346      const QgsTextRenderer::Component &component = components[grapheme.graphemeIndex ];
 
  355      const QgsTextDocument &document = graphemeDocuments.
at( grapheme.graphemeIndex );
 
  357      const QgsTextRenderer::Component &component = components[grapheme.graphemeIndex ];
 
  365                        Qgis::TextHorizontalAlignment::Left,
 
  366                        Qgis::TextVerticalAlignment::Top,
 
  373    const QgsTextDocument &document = graphemeDocuments.
at( grapheme.graphemeIndex );
 
  375    const QgsTextRenderer::Component &component = components[grapheme.graphemeIndex ];
 
  383                      Qgis::TextHorizontalAlignment::Left,
 
  384                      Qgis::TextVerticalAlignment::Top,
 
  414  const double fontScale = calculateScaleFactorForFormat( context, format );
 
  417  drawPart( rect, rotation, alignment, Qgis::TextVerticalAlignment::Top, document, metrics, context, format, part, Qgis::TextLayoutMode::Rectangle );
 
  428  component.dpiRatio = 1.0;
 
  429  component.origin = rect.topLeft();
 
  430  component.rotation = rotation;
 
  431  component.size = rect.size();
 
  432  component.hAlign = alignment;
 
  445        double xc = rect.width() / 2.0;
 
  446        double yc = rect.height() / 2.0;
 
  448        double angle = -rotation;
 
  449        double xd = xc * std::cos( 
angle ) - yc * std::sin( 
angle );
 
  450        double yd = xc * std::sin( 
angle ) + yc * std::cos( 
angle );
 
  452        component.center = QPointF( component.origin.x() + xd, component.origin.y() + yd );
 
  456        component.center = rect.center();
 
  459      switch ( vAlignment )
 
  461        case Qgis::TextVerticalAlignment::Top:
 
  463        case Qgis::TextVerticalAlignment::VerticalCenter:
 
  466        case Qgis::TextVerticalAlignment::Bottom:
 
  471      QgsTextRenderer::drawBackground( context, component, format, metrics, Qgis::TextLayoutMode::Rectangle );
 
  485      drawTextInternal( part, context, format, component,
 
  487                        alignment, vAlignment, mode );
 
  496  const double fontScale = calculateScaleFactorForFormat( context, format );
 
  510  component.dpiRatio = 1.0;
 
  511  component.origin = origin;
 
  512  component.rotation = rotation;
 
  513  component.hAlign = alignment;
 
  522      QgsTextRenderer::drawBackground( context, component, format, metrics, mode );
 
  535      drawTextInternal( part, context, format, component,
 
  538                        alignment, Qgis::TextVerticalAlignment::Top,
 
  547  return QFontMetricsF( format.
scaledFont( context, scaleFactor ), context.
painter() ? context.
painter()->device() : 
nullptr );
 
  554  QPainter *p = context.
painter();
 
  557  if ( format.
orientation() == Qgis::TextOrientation::RotationBased )
 
  559    if ( component.rotation >= -315 && component.rotation < -90 )
 
  561      orientation = Qgis::TextOrientation::Vertical;
 
  563    else if ( component.rotation >= -90 && component.rotation < -45 )
 
  565      orientation = Qgis::TextOrientation::Vertical;
 
  569      orientation = Qgis::TextOrientation::Horizontal;
 
  575  const double penSize =  buffer.
sizeUnit() == Qgis::RenderUnit::Percentage
 
  579  const double scaleFactor = calculateScaleFactorForFormat( context, format );
 
  581  std::optional< QgsScopedRenderContextReferenceScaleOverride > referenceScaleOverride;
 
  582  if ( mode == Qgis::TextLayoutMode::Labeling )
 
  592  referenceScaleOverride.reset();
 
  595  path.setFillRule( Qt::WindingFill );
 
  597  double height = component.size.height();
 
  598  switch ( orientation )
 
  600    case Qgis::TextOrientation::Horizontal:
 
  603      int fragmentIndex = 0;
 
  606        QFont fragmentFont = metrics.
fragmentFont( component.blockIndex, fragmentIndex );
 
  608        if ( component.extraWordSpacing || component.extraLetterSpacing )
 
  609          applyExtraSpacingForLineJustification( fragmentFont, component.extraWordSpacing, component.extraLetterSpacing );
 
  612        path.addText( xOffset, yOffset, fragmentFont, fragment.
text() );
 
  622    case Qgis::TextOrientation::Vertical:
 
  623    case Qgis::TextOrientation::RotationBased:
 
  625      double partYOffset = component.offset.y() * scaleFactor;
 
  628      double partLastDescent = 0;
 
  630      int fragmentIndex = 0;
 
  633        const QFont fragmentFont = metrics.
fragmentFont( component.blockIndex, component.firstFragmentIndex + fragmentIndex );
 
  634        const double letterSpacing = fragmentFont.letterSpacing() / scaleFactor;
 
  636        const QFontMetricsF fragmentMetrics( fragmentFont );
 
  638        const double fragmentYOffset = metrics.
fragmentVerticalOffset( component.blockIndex, fragmentIndex, mode );
 
  641        for ( 
const QString &part : parts )
 
  643          double partXOffset = ( blockMaximumCharacterWidth - ( fragmentMetrics.horizontalAdvance( part ) / scaleFactor - letterSpacing ) ) / 2;
 
  644          partYOffset += fragmentMetrics.ascent() / scaleFactor;
 
  645          path.addText( partXOffset, partYOffset + fragmentYOffset, fragmentFont, part );
 
  646          partYOffset += letterSpacing;
 
  648        partLastDescent = fragmentMetrics.descent() / scaleFactor;
 
  652      height = partYOffset + partLastDescent;
 
  653      advance = partYOffset - component.offset.y() * scaleFactor;
 
  658  QColor bufferColor = buffer.
color();
 
  659  bufferColor.setAlphaF( buffer.
opacity() );
 
  660  QPen pen( bufferColor );
 
  661  pen.setWidthF( penSize * scaleFactor );
 
  663  QColor tmpColor( bufferColor );
 
  667    tmpColor.setAlpha( 0 );
 
  673  buffp.begin( &buffPict );
 
  677    std::unique_ptr< QgsPaintEffect > tmpEffect( buffer.
paintEffect()->
clone() );
 
  679    tmpEffect->begin( context );
 
  680    context.
painter()->setPen( pen );
 
  681    context.
painter()->setBrush( tmpColor );
 
  682    if ( scaleFactor != 1.0 )
 
  683      context.
painter()->scale( 1 / scaleFactor, 1 / scaleFactor );
 
  684    context.
painter()->drawPath( path );
 
  685    if ( scaleFactor != 1.0 )
 
  686      context.
painter()->scale( scaleFactor, scaleFactor );
 
  687    tmpEffect->end( context );
 
  693    if ( scaleFactor != 1.0 )
 
  694      buffp.scale( 1 / scaleFactor, 1 / scaleFactor );
 
  696    buffp.setBrush( tmpColor );
 
  697    buffp.drawPath( path );
 
  703    QgsTextRenderer::Component bufferComponent = component;
 
  704    bufferComponent.origin = QPointF( 0.0, 0.0 );
 
  705    bufferComponent.picture = buffPict;
 
  706    bufferComponent.pictureBuffer = penSize / 2.0;
 
  707    bufferComponent.size.setHeight( height );
 
  709    if ( format.
orientation() == Qgis::TextOrientation::Vertical || format.
orientation() == Qgis::TextOrientation::RotationBased )
 
  711      bufferComponent.offset.setY( - bufferComponent.size.height() );
 
  713    drawShadow( context, bufferComponent, format );
 
  721    p->setCompositionMode( buffer.
blendMode() );
 
  725  p->scale( component.dpiRatio, component.dpiRatio );
 
  726  _fixQPictureDPI( p );
 
  727  p->drawPicture( 0, 0, buffPict );
 
  729  return advance / scaleFactor;
 
  743  double penSize = mask.
sizeUnit() == Qgis::RenderUnit::Percentage
 
  749  path.setFillRule( Qt::WindingFill );
 
  751  const double scaleFactor = calculateScaleFactorForFormat( context, format );
 
  756  std::optional< QgsScopedRenderContextReferenceScaleOverride > referenceScaleOverride;
 
  757  if ( mode == Qgis::TextLayoutMode::Labeling )
 
  767  referenceScaleOverride.reset();
 
  770  int fragmentIndex = 0;
 
  773    const QFont fragmentFont = metrics.
fragmentFont( component.blockIndex, fragmentIndex );
 
  775    const double fragmentYOffset = metrics.
fragmentVerticalOffset( component.blockIndex, fragmentIndex, mode );
 
  776    path.addText( xOffset, fragmentYOffset, fragmentFont, fragment.
text() );
 
  782  QColor bufferColor( Qt::gray );
 
  783  bufferColor.setAlphaF( mask.
opacity() );
 
  787  brush.setColor( bufferColor );
 
  788  pen.setColor( bufferColor );
 
  789  pen.setWidthF( penSize * scaleFactor );
 
  796  p->scale( component.dpiRatio, component.dpiRatio );
 
  802      if ( scaleFactor != 1.0 )
 
  803        context.
painter()->scale( 1 / scaleFactor, 1 / scaleFactor );
 
  804      context.
painter()->setPen( pen );
 
  805      context.
painter()->setBrush( brush );
 
  806      context.
painter()->drawPath( path );
 
  807      if ( scaleFactor != 1.0 )
 
  808        context.
painter()->scale( scaleFactor, scaleFactor );
 
  813    if ( scaleFactor != 1.0 )
 
  814      p->scale( 1 / scaleFactor, 1 / scaleFactor );
 
  816    p->setBrush( brush );
 
  818    if ( scaleFactor != 1.0 )
 
  819      p->scale( scaleFactor, scaleFactor );
 
  835  if ( doc.
size() == 0 )
 
  839  return textWidth( context, format, doc );
 
  845  const double scaleFactor = calculateScaleFactorForFormat( context, format );
 
  856  for ( 
const QString &line : textLines )
 
  860      lines.append( 
wrappedText( context, line, maxLineWidth, format ) );
 
  864      lines.append( line );
 
  880  const double scaleFactor = calculateScaleFactorForFormat( context, format );
 
  882  bool isNullSize = 
false;
 
  883  const QFont baseFont = format.
scaledFont( context, scaleFactor, &isNullSize );
 
  887  const QFontMetrics fm( baseFont );
 
  888  const double height = ( character.isNull() ? fm.height() : fm.boundingRect( character ).height() ) / scaleFactor;
 
  890  if ( !includeEffects )
 
  893  double maxExtension = 0;
 
  897    maxExtension += format.
buffer().
sizeUnit() == Qgis::RenderUnit::Percentage
 
  903    maxExtension += ( format.
shadow().
offsetUnit() == Qgis::RenderUnit::Percentage
 
  922  return height + maxExtension;
 
  930  const QStringList multiLineSplit = text.split( 
'\n' );
 
  932  return currentTextWidth > width;
 
  937  const QStringList lines = text.split( 
'\n' );
 
  938  QStringList outLines;
 
  939  for ( 
const QString &line : lines )
 
  944      const QStringList words = line.split( 
' ' );
 
  945      QStringList linesToProcess;
 
  946      QString wordsInCurrentLine;
 
  947      for ( 
const QString &word : words )
 
  952          if ( !wordsInCurrentLine.isEmpty() )
 
  953            linesToProcess << wordsInCurrentLine;
 
  954          wordsInCurrentLine.clear();
 
  955          linesToProcess << word;
 
  959          if ( !wordsInCurrentLine.isEmpty() )
 
  960            wordsInCurrentLine.append( 
' ' );
 
  961          wordsInCurrentLine.append( word );
 
  964      if ( !wordsInCurrentLine.isEmpty() )
 
  965        linesToProcess << wordsInCurrentLine;
 
  967      for ( 
const QString &line : std::as_const( linesToProcess ) )
 
  969        QString remainingText = line;
 
  970        int lastPos = remainingText.lastIndexOf( 
' ' );
 
  971        while ( lastPos > -1 )
 
  981            outLines << remainingText.left( lastPos );
 
  982            remainingText = remainingText.mid( lastPos + 1 );
 
  985          lastPos = remainingText.lastIndexOf( 
' ', lastPos - 1 );
 
  987        outLines << remainingText;
 
 1005  const double scaleFactor = calculateScaleFactorForFormat( context, format );
 
 1018  QPainter *prevP = context.
painter();
 
 1019  QPainter *p = context.
painter();
 
 1020  std::unique_ptr< QgsPaintEffect > tmpEffect;
 
 1024    tmpEffect->begin( context );
 
 1033  const double originAdjustRotationRadians = -component.rotation;
 
 1036    component.rotation = -( component.rotation * 180 / M_PI ); 
 
 1037    component.rotationOffset =
 
 1042    component.rotation = 0.0; 
 
 1043    component.rotationOffset = background.
rotation();
 
 1046  const double scaleFactor = calculateScaleFactorForFormat( context, format );
 
 1048  if ( mode != Qgis::TextLayoutMode::Labeling )
 
 1052    double width = documentSize.width();
 
 1053    double height = documentSize.height();
 
 1057      case Qgis::TextLayoutMode::Rectangle:
 
 1060        switch ( component.hAlign )
 
 1062          case Qgis::TextHorizontalAlignment::Left:
 
 1063          case Qgis::TextHorizontalAlignment::Justify:
 
 1064            component.center = QPointF( component.origin.x() + width / 2.0,
 
 1065                                        component.origin.y() + height / 2.0 );
 
 1068          case Qgis::TextHorizontalAlignment::Center:
 
 1069            component.center = QPointF( component.origin.x() + component.size.width() / 2.0,
 
 1070                                        component.origin.y() + height / 2.0 );
 
 1073          case Qgis::TextHorizontalAlignment::Right:
 
 1074            component.center = QPointF( component.origin.x() + component.size.width() - width / 2.0,
 
 1075                                        component.origin.y() + height / 2.0 );
 
 1082        bool isNullSize = 
false;
 
 1083        QFontMetricsF fm( format.
scaledFont( context, scaleFactor, &isNullSize ) );
 
 1084        double originAdjust = isNullSize ? 0 : ( fm.ascent() / scaleFactor / 2.0 - fm.leading() / scaleFactor / 2.0 );
 
 1085        switch ( component.hAlign )
 
 1087          case Qgis::TextHorizontalAlignment::Left:
 
 1088          case Qgis::TextHorizontalAlignment::Justify:
 
 1089            component.center = QPointF( component.origin.x() + width / 2.0,
 
 1090                                        component.origin.y() - height / 2.0 + originAdjust );
 
 1093          case Qgis::TextHorizontalAlignment::Center:
 
 1094            component.center = QPointF( component.origin.x(),
 
 1095                                        component.origin.y() - height / 2.0 + originAdjust );
 
 1098          case Qgis::TextHorizontalAlignment::Right:
 
 1099            component.center = QPointF( component.origin.x() - width / 2.0,
 
 1100                                        component.origin.y() - height / 2.0 + originAdjust );
 
 1107          const double dx = component.center.x() - component.origin.x();
 
 1108          const double dy = component.center.y() - component.origin.y();
 
 1109          component.center.setX( component.origin.x() + ( std::cos( originAdjustRotationRadians ) * dx - std::sin( originAdjustRotationRadians ) * dy ) );
 
 1110          component.center.setY( component.origin.y() + ( std::sin( originAdjustRotationRadians ) * dx + std::cos( originAdjustRotationRadians ) * dy ) );
 
 1115      case Qgis::TextLayoutMode::Labeling:
 
 1120      component.size = QSizeF( width, height );
 
 1125  switch ( background.
type() )
 
 1138      double sizeOut = 0.0;
 
 1149          sizeOut = std::max( component.size.width(), component.size.height() );
 
 1153          sizeOut += bufferSize * 2;
 
 1159      if ( sizeOut < 1.0 )
 
 1162      std::unique_ptr< QgsMarkerSymbol > renderedSymbol;
 
 1166        map[QStringLiteral( 
"name" )] = background.
svgFile().trimmed();
 
 1167        map[QStringLiteral( 
"size" )] = QString::number( sizeOut );
 
 1169        map[QStringLiteral( 
"angle" )] = QString::number( 0.0 ); 
 
 1177        map[QStringLiteral( 
"fill" )] = background.
fillColor().name();
 
 1178        map[QStringLiteral( 
"outline" )] = background.
strokeColor().name();
 
 1179        map[QStringLiteral( 
"outline-width" )] = QString::number( background.
strokeWidth() );
 
 1186          QVariantMap shdwmap( map );
 
 1187          shdwmap[QStringLiteral( 
"fill" )] = shadow.
color().name();
 
 1188          shdwmap[QStringLiteral( 
"outline" )] = shadow.
color().name();
 
 1189          shdwmap[QStringLiteral( 
"size" )] = QString::number( sizeOut );
 
 1194          svgp.begin( &svgPict );
 
 1211          svgShdwM->
renderPoint( QPointF( sizeOut / 2, -sizeOut / 2 ), svgShdwContext );
 
 1214          component.picture = svgPict;
 
 1216          component.pictureBuffer = 0.0;
 
 1218          component.size = QSizeF( sizeOut, sizeOut );
 
 1219          component.offset = QPointF( 0.0, 0.0 );
 
 1225          p->translate( component.center.x(), component.center.y() );
 
 1226          p->rotate( component.rotation );
 
 1229          p->translate( QPointF( xoff, yoff ) );
 
 1230          p->rotate( component.rotationOffset );
 
 1231          p->translate( -sizeOut / 2, sizeOut / 2 );
 
 1233          drawShadow( context, component, format );
 
 1235        renderedSymbol.reset( );
 
 1243        renderedSymbol->setSize( sizeOut );
 
 1244        renderedSymbol->setSizeUnit( Qgis::RenderUnit::Pixels );
 
 1247      renderedSymbol->setOpacity( renderedSymbol->opacity() * background.
opacity() );
 
 1255        p->setCompositionMode( background.
blendMode() );
 
 1257      p->translate( component.center.x(), component.center.y() );
 
 1258      p->rotate( component.rotation );
 
 1261      p->translate( QPointF( xoff, yoff ) );
 
 1262      p->rotate( component.rotationOffset );
 
 1266      renderedSymbol->renderPoint( QPointF( 0, 0 ), &f, context );
 
 1267      renderedSymbol->stopRender( context );
 
 1268      p->setCompositionMode( QPainter::CompositionMode_SourceOver ); 
 
 1278      double w = component.size.width();
 
 1279      double h = component.size.height();
 
 1300          h = std::sqrt( std::pow( w, 2 ) + std::pow( h, 2 ) );
 
 1306          h = h * M_SQRT1_2 * 2;
 
 1307          w = w * M_SQRT1_2 * 2;
 
 1315        w += bufferWidth * 2;
 
 1316        h += bufferHeight * 2;
 
 1320      QRectF rect( -w / 2.0, - h / 2.0, w, h );
 
 1322      if ( rect.isNull() )
 
 1328      p->translate( QPointF( component.center.x(), component.center.y() ) );
 
 1329      p->rotate( component.rotation );
 
 1332      p->translate( QPointF( xoff, yoff ) );
 
 1333      p->rotate( component.rotationOffset );
 
 1339      QTransform t = QTransform::fromScale( 10, 10 );
 
 1341      QTransform ti = t.inverted();
 
 1346        if ( background.
radiiUnit() == Qgis::RenderUnit::Percentage )
 
 1348          path.addRoundedRect( rect, background.
radii().width(), background.
radii().height(), Qt::RelativeSize );
 
 1354          path.addRoundedRect( rect, xRadius, yRadius );
 
 1360        path.addEllipse( rect );
 
 1362      QPolygonF tempPolygon = path.toFillPolygon( t );
 
 1363      QPolygonF polygon = ti.map( tempPolygon );
 
 1365      QPainter *oldp = context.
painter();
 
 1368      shapep.begin( &shapePict );
 
 1371      std::unique_ptr< QgsFillSymbol > renderedSymbol;
 
 1373      renderedSymbol->setOpacity( renderedSymbol->opacity() * background.
opacity() );
 
 1377      renderedSymbol->renderPolygon( polygon, 
nullptr, &f, context );
 
 1378      renderedSymbol->stopRender( context );
 
 1385        component.picture = shapePict;
 
 1388        component.size = rect.size();
 
 1389        component.offset = QPointF( rect.width() / 2, -rect.height() / 2 );
 
 1390        drawShadow( context, component, format );
 
 1395        p->setCompositionMode( background.
blendMode() );
 
 1399      p->scale( component.dpiRatio, component.dpiRatio );
 
 1400      _fixQPictureDPI( p );
 
 1401      p->drawPicture( 0, 0, shapePict );
 
 1402      p->setCompositionMode( QPainter::CompositionMode_SourceOver ); 
 
 1409    tmpEffect->end( context );
 
 1422  QPainter *p = context.
painter();
 
 1423  const double componentWidth = component.size.width();
 
 1424  const double componentHeight = component.size.height();
 
 1425  double xOffset = component.offset.x(), yOffset = component.offset.y();
 
 1426  double pictbuffer = component.pictureBuffer;
 
 1429  bool mapUnits = shadow.
blurRadiusUnit() == Qgis::RenderUnit::MapUnits;
 
 1432  double radius = shadow.
blurRadiusUnit() == Qgis::RenderUnit::Percentage
 
 1435  radius /= ( mapUnits ? context.
scaleFactor() / component.dpiRatio : 1 );
 
 1436  radius = 
static_cast< int >( radius + 0.5 ); 
 
 1440  double blurBufferClippingScale = 3.75;
 
 1441  int blurbuffer = ( radius > 17 ? 16 : radius ) * blurBufferClippingScale;
 
 1443  QImage blurImg( componentWidth + ( pictbuffer * 2.0 ) + ( blurbuffer * 2.0 ),
 
 1444                  componentHeight + ( pictbuffer * 2.0 ) + ( blurbuffer * 2.0 ),
 
 1445                  QImage::Format_ARGB32_Premultiplied );
 
 1449  int minBlurImgSize = 1;
 
 1453  int maxBlurImgSize = 40000;
 
 1454  if ( blurImg.isNull()
 
 1455       || ( blurImg.width() < minBlurImgSize || blurImg.height() < minBlurImgSize )
 
 1456       || ( blurImg.width() > maxBlurImgSize || blurImg.height() > maxBlurImgSize ) )
 
 1459  blurImg.fill( QColor( Qt::transparent ).rgba() );
 
 1461  if ( !pictp.begin( &blurImg ) )
 
 1463  pictp.setRenderHints( QPainter::Antialiasing | QPainter::SmoothPixmapTransform );
 
 1464  QPointF imgOffset( blurbuffer + pictbuffer + xOffset,
 
 1465                     blurbuffer + pictbuffer + componentHeight + yOffset );
 
 1467  pictp.drawPicture( imgOffset,
 
 1468                     component.picture );
 
 1471  pictp.setCompositionMode( QPainter::CompositionMode_SourceIn );
 
 1472  pictp.fillRect( blurImg.rect(), shadow.
color() );
 
 1476  if ( shadow.
blurRadius() > 0.0 && radius > 0 )
 
 1484  picti.begin( &blurImg );
 
 1485  picti.setBrush( Qt::Dense7Pattern );
 
 1486  QPen imgPen( QColor( 0, 0, 255, 255 ) );
 
 1487  imgPen.setWidth( 1 );
 
 1488  picti.setPen( imgPen );
 
 1489  picti.setOpacity( 0.1 );
 
 1490  picti.drawRect( 0, 0, blurImg.width(), blurImg.height() );
 
 1494  const double offsetDist = shadow.
offsetUnit() == Qgis::RenderUnit::Percentage
 
 1497  double angleRad = shadow.
offsetAngle() * M_PI / 180; 
 
 1505    angleRad -= ( component.rotation * M_PI / 180 + component.rotationOffset * M_PI / 180 );
 
 1508  QPointF transPt( -offsetDist * std::cos( angleRad + M_PI_2 ),
 
 1509                   -offsetDist * std::sin( angleRad + M_PI_2 ) );
 
 1515  p->setRenderHint( QPainter::SmoothPixmapTransform );
 
 1518    p->setCompositionMode( shadow.
blendMode() );
 
 1520  p->setOpacity( shadow.
opacity() );
 
 1522  double scale = shadow.
scale() / 100.0;
 
 1524  p->scale( scale, scale );
 
 1525  if ( component.useOrigin )
 
 1527    p->translate( component.origin.x(), component.origin.y() );
 
 1529  p->translate( transPt );
 
 1530  p->translate( -imgOffset.x(),
 
 1532  p->drawImage( 0, 0, blurImg );
 
 1539  p->setBrush( Qt::NoBrush );
 
 1540  QPen imgPen( QColor( 255, 0, 0, 10 ) );
 
 1541  imgPen.setWidth( 2 );
 
 1542  imgPen.setStyle( Qt::DashLine );
 
 1543  p->setPen( imgPen );
 
 1544  p->scale( scale, scale );
 
 1545  if ( component.useOrigin() )
 
 1547    p->translate( component.origin().x(), component.origin().y() );
 
 1549  p->translate( transPt );
 
 1550  p->translate( -imgOffset.x(),
 
 1552  p->drawRect( 0, 0, blurImg.width(), blurImg.height() );
 
 1557  p->setBrush( Qt::NoBrush );
 
 1558  QPen componentRectPen( QColor( 0, 255, 0, 70 ) );
 
 1559  componentRectPen.setWidth( 1 );
 
 1560  if ( component.useOrigin() )
 
 1562    p->translate( component.origin().x(), component.origin().y() );
 
 1564  p->setPen( componentRectPen );
 
 1565  p->drawRect( QRect( -xOffset, -componentHeight - yOffset, componentWidth, componentHeight ) );
 
 1574                                        const Component &component,
 
 1584  const double fontScale = calculateScaleFactorForFormat( context, format );
 
 1586  std::optional< QgsScopedRenderContextReferenceScaleOverride > referenceScaleOverride;
 
 1587  if ( mode == Qgis::TextLayoutMode::Labeling )
 
 1597  referenceScaleOverride.reset();
 
 1599  double rotation = 0;
 
 1600  const Qgis::TextOrientation orientation = calculateRotationAndOrientationForComponent( format, component, rotation );
 
 1601  switch ( orientation )
 
 1603    case Qgis::TextOrientation::Horizontal:
 
 1605      drawTextInternalHorizontal( context, format, drawType, mode, component, document, metrics, fontScale, alignment, vAlignment, rotation );
 
 1609    case Qgis::TextOrientation::Vertical:
 
 1610    case Qgis::TextOrientation::RotationBased:
 
 1612      drawTextInternalVertical( context, format, drawType, mode, component, document, metrics, fontScale, alignment, vAlignment, rotation );
 
 1618Qgis::TextOrientation QgsTextRenderer::calculateRotationAndOrientationForComponent( 
const QgsTextFormat &format, 
const QgsTextRenderer::Component &component, 
double &rotation )
 
 1620  rotation = -component.rotation * 180 / M_PI;
 
 1624    case Qgis::TextOrientation::RotationBased:
 
 1627      if ( rotation >= -315 && rotation < -90 )
 
 1630        return Qgis::TextOrientation::Vertical;
 
 1632      else if ( rotation >= -90 && rotation < -45 )
 
 1635        return Qgis::TextOrientation::Vertical;
 
 1638      return Qgis::TextOrientation::Horizontal;
 
 1641    case Qgis::TextOrientation::Horizontal:
 
 1642    case Qgis::TextOrientation::Vertical:
 
 1645  return Qgis::TextOrientation::Horizontal;
 
 1648void QgsTextRenderer::calculateExtraSpacingForLineJustification( 
const double spaceToDistribute, 
const QgsTextBlock &block, 
double &extraWordSpace, 
double &extraLetterSpace )
 
 1651  QTextBoundaryFinder 
finder( QTextBoundaryFinder::Word, blockText );
 
 1653  int wordBoundaries = 0;
 
 1654  while ( 
finder.toNextBoundary() != -1 )
 
 1656    if ( 
finder.boundaryReasons() & QTextBoundaryFinder::StartOfItem )
 
 1660  if ( wordBoundaries > 0 )
 
 1663    extraWordSpace = spaceToDistribute / wordBoundaries;
 
 1668    QTextBoundaryFinder 
finder( QTextBoundaryFinder::Grapheme, blockText );
 
 1671    int graphemeBoundaries = 0;
 
 1672    while ( 
finder.toNextBoundary() != -1 )
 
 1674      if ( 
finder.boundaryReasons() & QTextBoundaryFinder::StartOfItem )
 
 1675        graphemeBoundaries++;
 
 1678    if ( graphemeBoundaries > 0 )
 
 1680      extraLetterSpace = spaceToDistribute / graphemeBoundaries;
 
 1685void QgsTextRenderer::applyExtraSpacingForLineJustification( QFont &font, 
double extraWordSpace, 
double extraLetterSpace )
 
 1687  const double prevWordSpace = font.wordSpacing();
 
 1688  font.setWordSpacing( prevWordSpace + extraWordSpace );
 
 1689  const double prevLetterSpace = font.letterSpacing();
 
 1690  font.setLetterSpacing( QFont::AbsoluteSpacing, prevLetterSpace + extraLetterSpace );
 
 1697  const QStringList textLines = document.
toPlainText();
 
 1699  const QSizeF documentSize = metrics.
documentSize( mode, Qgis::TextOrientation::Horizontal );
 
 1701  double labelWidest = 0.0;
 
 1704    case Qgis::TextLayoutMode::Labeling:
 
 1706      labelWidest = documentSize.width();
 
 1709    case Qgis::TextLayoutMode::Rectangle:
 
 1712      labelWidest = component.size.width();
 
 1716  double verticalAlignOffset = 0;
 
 1718  bool adjustForAlignment = hAlignment != Qgis::TextHorizontalAlignment::Left && ( mode != Qgis::TextLayoutMode::Labeling || textLines.size() > 1 );
 
 1720  if ( mode == Qgis::TextLayoutMode::Rectangle && vAlignment != Qgis::TextVerticalAlignment::Top )
 
 1722    const double overallHeight = documentSize.height();
 
 1723    switch ( vAlignment )
 
 1725      case Qgis::TextVerticalAlignment::Top:
 
 1728      case Qgis::TextVerticalAlignment::VerticalCenter:
 
 1729        verticalAlignOffset = ( component.size.height() - overallHeight ) * 0.5;
 
 1732      case Qgis::TextVerticalAlignment::Bottom:
 
 1733        verticalAlignOffset = ( component.size.height() - overallHeight );
 
 1741    const bool isFinalLineInParagraph = ( blockIndex == document.size() - 1 )
 
 1742                                        || document.at( blockIndex + 1 ).toPlainText().trimmed().isEmpty();
 
 1744    const double blockHeight = metrics.
blockHeight( blockIndex );
 
 1748    context.
painter()->translate( component.origin );
 
 1750      context.
painter()->rotate( rotation );
 
 1755      maskPainter->save();
 
 1756      maskPainter->translate( component.origin );
 
 1758        maskPainter->rotate( rotation );
 
 1762    double xMultiLineOffset = 0.0;
 
 1763    double blockWidth = metrics.
blockWidth( blockIndex );
 
 1764    double extraWordSpace = 0;
 
 1765    double extraLetterSpace = 0;
 
 1766    if ( adjustForAlignment )
 
 1768      double labelWidthDiff = 0;
 
 1769      switch ( hAlignment )
 
 1771        case Qgis::TextHorizontalAlignment::Center:
 
 1772          labelWidthDiff = ( labelWidest - blockWidth ) * 0.5;
 
 1775        case Qgis::TextHorizontalAlignment::Right:
 
 1776          labelWidthDiff = labelWidest - blockWidth;
 
 1779        case Qgis::TextHorizontalAlignment::Justify:
 
 1780          if ( !isFinalLineInParagraph && labelWidest > blockWidth )
 
 1782            calculateExtraSpacingForLineJustification( labelWidest - blockWidth, block, extraWordSpace, extraLetterSpace );
 
 1783            blockWidth = labelWidest;
 
 1787        case Qgis::TextHorizontalAlignment::Left:
 
 1793        case Qgis::TextLayoutMode::Labeling:
 
 1794        case Qgis::TextLayoutMode::Rectangle:
 
 1797          xMultiLineOffset = labelWidthDiff;
 
 1802          switch ( hAlignment )
 
 1804            case Qgis::TextHorizontalAlignment::Right:
 
 1805              xMultiLineOffset = labelWidthDiff - labelWidest;
 
 1808            case Qgis::TextHorizontalAlignment::Center:
 
 1809              xMultiLineOffset = labelWidthDiff - labelWidest / 2.0;
 
 1812            case Qgis::TextHorizontalAlignment::Left:
 
 1813            case Qgis::TextHorizontalAlignment::Justify:
 
 1821    const double baseLineOffset = metrics.
baselineOffset( blockIndex, mode );
 
 1823    context.
painter()->translate( QPointF( xMultiLineOffset, baseLineOffset + verticalAlignOffset ) );
 
 1825      maskPainter->translate( QPointF( xMultiLineOffset, baseLineOffset + verticalAlignOffset ) );
 
 1827    Component subComponent;
 
 1828    subComponent.block = block;
 
 1829    subComponent.blockIndex = blockIndex;
 
 1830    subComponent.
size = QSizeF( blockWidth, blockHeight );
 
 1831    subComponent.offset = QPointF( 0.0, -metrics.
ascentOffset() );
 
 1832    subComponent.rotation = -component.rotation * 180 / M_PI;
 
 1833    subComponent.rotationOffset = 0.0;
 
 1834    subComponent.extraWordSpacing = extraWordSpace * fontScale;
 
 1835    subComponent.extraLetterSpacing = extraLetterSpace * fontScale;
 
 1840      QgsTextRenderer::drawMask( context, subComponent, format, metrics, mode );
 
 1845      QgsTextRenderer::drawBuffer( context, subComponent, format, metrics, mode );
 
 1852      textp.begin( &textPict );
 
 1853      textp.setPen( Qt::NoPen );
 
 1855      std::optional< QgsScopedRenderContextReferenceScaleOverride > referenceScaleOverride;
 
 1856      if ( mode == Qgis::TextLayoutMode::Labeling )
 
 1863      referenceScaleOverride.reset();
 
 1867        textp.scale( 1 / fontScale, 1 / fontScale );
 
 1870        int fragmentIndex = 0;
 
 1875          path.setFillRule( Qt::WindingFill );
 
 1877          QFont fragmentFont = metrics.
fragmentFont( blockIndex, fragmentIndex );
 
 1879          if ( extraWordSpace || extraLetterSpace )
 
 1880            applyExtraSpacingForLineJustification( fragmentFont, extraWordSpace * fontScale, extraLetterSpace * fontScale );
 
 1884          path.addText( xOffset, yOffset, fragmentFont, fragment.
text() );
 
 1888          textp.setBrush( textColor );
 
 1889          textp.drawPath( path );
 
 1899        subComponent.picture = textPict;
 
 1900        subComponent.pictureBuffer = 0.0; 
 
 1901        subComponent.origin = QPointF( 0.0, 0.0 );
 
 1903        QgsTextRenderer::drawShadow( context, subComponent, format );
 
 1913      context.
painter()->scale( subComponent.dpiRatio, subComponent.dpiRatio );
 
 1917        case Qgis::TextRenderFormat::AlwaysOutlines:
 
 1920          _fixQPictureDPI( context.
painter() );
 
 1921          context.
painter()->drawPicture( 0, 0, textPict );
 
 1925        case Qgis::TextRenderFormat::AlwaysText:
 
 1928          int fragmentIndex = 0;
 
 1931            QFont fragmentFont = metrics.
fragmentFont( blockIndex, fragmentIndex );
 
 1933            if ( extraWordSpace || extraLetterSpace )
 
 1934              applyExtraSpacingForLineJustification( fragmentFont, extraWordSpace * fontScale, extraLetterSpace * fontScale );
 
 1941            context.
painter()->setPen( textColor );
 
 1942            context.
painter()->setFont( fragmentFont );
 
 1943            context.
painter()->setRenderHint( QPainter::TextAntialiasing );
 
 1945            context.
painter()->scale( 1 / fontScale, 1 / fontScale );
 
 1946            context.
painter()->drawText( QPointF( xOffset, yOffset ), fragment.
text() );
 
 1947            context.
painter()->scale( fontScale, fontScale );
 
 1956      maskPainter->restore();
 
 1962void QgsTextRenderer::drawTextInternalVertical( 
QgsRenderContext &context, 
const QgsTextFormat &format, 
Qgis::TextComponent drawType, 
Qgis::TextLayoutMode mode, 
const QgsTextRenderer::Component &component, 
const QgsTextDocument &document, 
const QgsTextDocumentMetrics &metrics, 
double fontScale, 
Qgis::TextHorizontalAlignment hAlignment, 
Qgis::TextVerticalAlignment, 
double rotation )
 
 1965  const QStringList textLines = document.
toPlainText();
 
 1967  std::optional< QgsScopedRenderContextReferenceScaleOverride > referenceScaleOverride;
 
 1968  if ( mode == Qgis::TextLayoutMode::Labeling )
 
 1978  referenceScaleOverride.reset();
 
 1980  const QSizeF documentSize = metrics.
documentSize( mode, Qgis::TextOrientation::Vertical );
 
 1981  const double actualTextWidth = documentSize.width();
 
 1982  double textRectWidth = 0.0;
 
 1986    case Qgis::TextLayoutMode::Labeling:
 
 1988      textRectWidth = actualTextWidth;
 
 1991    case Qgis::TextLayoutMode::Rectangle:
 
 1994      textRectWidth = component.size.width();
 
 1998  int maxLineLength = 0;
 
 1999  for ( 
const QString &line : std::as_const( textLines ) )
 
 2001    maxLineLength = std::max( maxLineLength, 
static_cast<int>( line.length() ) );
 
 2004  const double actualLabelHeight = documentSize.height();
 
 2007  bool adjustForAlignment = hAlignment != Qgis::TextHorizontalAlignment::Left && ( mode != Qgis::TextLayoutMode::Labeling || textLines.size() > 1 );
 
 2014    context.
painter()->translate( component.origin );
 
 2016      context.
painter()->rotate( rotation );
 
 2021      maskPainter->save();
 
 2022      maskPainter->translate( component.origin );
 
 2024        maskPainter->rotate( rotation );
 
 2031    if ( adjustForAlignment )
 
 2033      double hAlignmentOffset = 0;
 
 2034      switch ( hAlignment )
 
 2036        case Qgis::TextHorizontalAlignment::Center:
 
 2037          hAlignmentOffset = ( textRectWidth - actualTextWidth ) * 0.5;
 
 2040        case Qgis::TextHorizontalAlignment::Right:
 
 2041          hAlignmentOffset = textRectWidth - actualTextWidth;
 
 2044        case Qgis::TextHorizontalAlignment::Left:
 
 2045        case Qgis::TextHorizontalAlignment::Justify:
 
 2051        case Qgis::TextLayoutMode::Labeling:
 
 2052        case Qgis::TextLayoutMode::Rectangle:
 
 2055          xOffset += hAlignmentOffset;
 
 2063    double yOffset = 0.0;
 
 2066      case Qgis::TextLayoutMode::Labeling:
 
 2067        if ( format.
orientation() == Qgis::TextOrientation::RotationBased )
 
 2069          if ( rotation >= -405 && rotation < -180 )
 
 2073          else if ( rotation >= 0 && rotation < 45 )
 
 2075            xOffset -= actualTextWidth;
 
 2081          yOffset = -actualLabelHeight;
 
 2086        yOffset = -actualLabelHeight;
 
 2089      case Qgis::TextLayoutMode::Rectangle:
 
 2096    context.
painter()->translate( QPointF( xOffset, yOffset ) );
 
 2098    double currentBlockYOffset = 0;
 
 2099    int fragmentIndex = 0;
 
 2107      const QFont fragmentFont = metrics.
fragmentFont( blockIndex, fragmentIndex );
 
 2109      QFontMetricsF fragmentMetrics( fragmentFont );
 
 2111      const double letterSpacing = fragmentFont.letterSpacing() / fontScale;
 
 2112      const double labelHeight = fragmentMetrics.ascent() / fontScale + ( fragmentMetrics.ascent() / fontScale + letterSpacing ) * ( line.length() - 1 );
 
 2114      Component subComponent;
 
 2116      subComponent.blockIndex = blockIndex;
 
 2117      subComponent.firstFragmentIndex = fragmentIndex;
 
 2118      subComponent.size = QSizeF( blockMaximumCharacterWidth, labelHeight + fragmentMetrics.descent() / fontScale );
 
 2119      subComponent.offset = QPointF( 0.0, currentBlockYOffset );
 
 2120      subComponent.rotation = -component.rotation * 180 / M_PI;
 
 2121      subComponent.rotationOffset = 0.0;
 
 2128        QgsTextRenderer::drawMask( context, subComponent, format );
 
 2134        currentBlockYOffset += QgsTextRenderer::drawBuffer( context, subComponent, format, metrics, mode );
 
 2140        path.setFillRule( Qt::WindingFill );
 
 2142        double partYOffset = 0.0;
 
 2143        for ( 
const QString &part : parts )
 
 2145          double partXOffset = ( blockMaximumCharacterWidth - ( fragmentMetrics.horizontalAdvance( part ) / fontScale - letterSpacing ) ) / 2;
 
 2146          partYOffset += fragmentMetrics.ascent() / fontScale;
 
 2147          path.addText( partXOffset * fontScale, partYOffset * fontScale, fragmentFont, part );
 
 2148          partYOffset += letterSpacing;
 
 2154        textp.begin( &textPict );
 
 2155        textp.setPen( Qt::NoPen );
 
 2158        textp.setBrush( textColor );
 
 2159        textp.scale( 1 / fontScale, 1 / fontScale );
 
 2160        textp.drawPath( path );
 
 2171          subComponent.picture = textPict;
 
 2172          subComponent.pictureBuffer = 0.0; 
 
 2173          subComponent.origin = QPointF( 0.0, currentBlockYOffset );
 
 2174          const double prevY = subComponent.offset.y();
 
 2175          subComponent.offset = QPointF( 0, -subComponent.size.height() );
 
 2176          subComponent.useOrigin = 
true;
 
 2177          QgsTextRenderer::drawShadow( context, subComponent, format );
 
 2178          subComponent.useOrigin = 
false;
 
 2179          subComponent.offset = QPointF( 0, prevY );
 
 2189        context.
painter()->scale( subComponent.dpiRatio, subComponent.dpiRatio );
 
 2193          case Qgis::TextRenderFormat::AlwaysOutlines:
 
 2196            context.
painter()->translate( 0, currentBlockYOffset );
 
 2197            _fixQPictureDPI( context.
painter() );
 
 2198            context.
painter()->drawPicture( 0, 0, textPict );
 
 2199            currentBlockYOffset += partYOffset;
 
 2203          case Qgis::TextRenderFormat::AlwaysText:
 
 2205            context.
painter()->setFont( fragmentFont );
 
 2206            context.
painter()->setPen( textColor );
 
 2207            context.
painter()->setRenderHint( QPainter::TextAntialiasing );
 
 2209            double partYOffset = 0.0;
 
 2210            for ( 
const QString &part : parts )
 
 2212              double partXOffset = ( blockMaximumCharacterWidth - ( fragmentMetrics.horizontalAdvance( part ) / fontScale - letterSpacing ) ) / 2;
 
 2213              context.
painter()->scale( 1 / fontScale, 1 / fontScale );
 
 2214              context.
painter()->drawText( QPointF( partXOffset * fontScale, ( currentBlockYOffset + partYOffset ) * fontScale ), part );
 
 2215              context.
painter()->scale( fontScale, fontScale );
 
 2216              partYOffset += fragmentMetrics.ascent() / fontScale + letterSpacing;
 
 2218            currentBlockYOffset += partYOffset;
 
 2226      maskPainter->restore();
 
 2241  if ( pixelSize < 50 )
 
 2245  else if ( pixelSize > 200 )
 
 2246    return 200 / pixelSize;
 
TextLayoutMode
Text layout modes.
 
@ Point
Text at point of origin layout mode.
 
@ RectangleAscentBased
Similar to Rectangle mode, but uses ascents only when calculating font and line heights....
 
@ RectangleCapHeightBased
Similar to Rectangle mode, but uses cap height only when calculating font heights for the first line ...
 
TextOrientation
Text orientations.
 
@ Normal
Adjacent characters are positioned in the standard way for text in the writing system in use.
 
@ SubScript
Characters are placed below the base line for normal text.
 
@ SuperScript
Characters are placed above the base line for normal text.
 
RenderUnit
Rendering size units.
 
@ ApplyScalingWorkaroundForTextRendering
Whether a scaling workaround designed to stablise the rendering of small font sizes (or for painters ...
 
TextVerticalAlignment
Text vertical alignment.
 
TextHorizontalAlignment
Text horizontal alignment.
 
@ WrapLines
Automatically wrap long lines of text.
 
TextComponent
Text components.
 
@ Buffer
Buffer component.
 
@ Background
Background shape.
 
A class to manager painter saving and restoring required for effect drawing.
 
QgsFeature feature() const
Convenience function for retrieving the feature for the context, if set.
 
QgsFields fields() const
Convenience function for retrieving the fields for the context, if set.
 
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
 
QgsFillSymbol * clone() const override
Returns a deep copy of this symbol.
 
Does vector analysis using the geos library and handles import, export, exception handling*.
 
Line string geometry type, with support for z-dimension and m-values.
 
static QgsLineString * fromQPolygonF(const QPolygonF &polygon)
Returns a new linestring from a QPolygonF polygon input.
 
Struct for storing maximum and minimum scales for measurements in map units.
 
A marker symbol type, for rendering Point and MultiPoint geometries.
 
QgsMarkerSymbol * clone() const override
Returns a deep copy of this symbol.
 
bool enabled() const
Returns whether the effect is enabled.
 
virtual QgsPaintEffect * clone() const =0
Duplicates an effect by creating a deep copy of the effect.
 
A class to manage painter saving and restoring required for drawing on a different painter (mask pain...
 
static QStringList splitToGraphemes(const QString &text)
Splits a text string to a list of graphemes, which are the smallest allowable character divisions in ...
 
Contains precalculated properties regarding text metrics for text to be renderered at a later stage.
 
void setGraphemeFormats(const QVector< QgsTextCharacterFormat > &formats)
Sets the character formats associated with the text graphemes().
 
bool hasActiveProperties() const override
Returns true if the collection has any active properties, or false if all properties within the colle...
 
Contains information about the context of a rendering operation.
 
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
 
bool useAdvancedEffects() const
Returns true if advanced effects such as blend modes such be used.
 
void setScaleFactor(double factor)
Sets the scaling factor for the render to convert painter units to physical sizes.
 
double convertToPainterUnits(double size, Qgis::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale(), Qgis::RenderSubcomponentProperty property=Qgis::RenderSubcomponentProperty::Generic) const
Converts a size from the specified units to painter units (pixels).
 
QPainter * painter()
Returns the destination QPainter for the render operation.
 
void setPainterFlagsUsingContext(QPainter *painter=nullptr) const
Sets relevant flags on a destination painter, using the flags and settings currently defined for the ...
 
QgsExpressionContext & expressionContext()
Gets the expression context.
 
bool isGuiPreview() const
Returns the Gui preview mode.
 
Qgis::TextRenderFormat textRenderFormat() const
Returns the text render format, which dictates how text is rendered (e.g.
 
const QgsMapToPixel & mapToPixel() const
Returns the context's map to pixel transform, which transforms between map coordinates and device coo...
 
QPainter * maskPainter(int id=0)
Returns a mask QPainter for the render operation.
 
void setMapToPixel(const QgsMapToPixel &mtp)
Sets the context's map to pixel transform, which transforms between map coordinates and device coordi...
 
int currentMaskId() const
Returns the current mask id, which can be used with maskPainter()
 
void setPainter(QPainter *p)
Sets the destination QPainter for the render operation.
 
Qgis::RenderContextFlags flags() const
Returns combination of flags used for rendering.
 
Scoped object for saving and restoring a QPainter object's state.
 
Scoped object for temporary override of the symbologyReferenceScale property of a QgsRenderContext.
 
static QString substituteVerticalCharacters(QString string)
Returns a string with characters having vertical representation form substituted.
 
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates the symbol.
 
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
 
static void blurImageInPlace(QImage &image, QRect rect, int radius, bool alphaOnly)
Blurs an image in place, e.g. creating Qt-independent drop shadows.
 
static double estimateMaxSymbolBleed(QgsSymbol *symbol, const QgsRenderContext &context)
Returns the maximum estimated bleed for the symbol.
 
Container for settings relating to a text background object.
 
QgsMapUnitScale strokeWidthMapUnitScale() const
Returns the map unit scale object for the shape stroke width.
 
RotationType rotationType() const
Returns the method used for rotating the background shape.
 
QString svgFile() const
Returns the absolute path to the background SVG file, if set.
 
QSizeF size() const
Returns the size of the background shape.
 
QSizeF radii() const
Returns the radii used for rounding the corners of shapes.
 
QgsMapUnitScale radiiMapUnitScale() const
Returns the map unit scale object for the shape radii.
 
Qgis::RenderUnit radiiUnit() const
Returns the units used for the shape's radii.
 
QPainter::CompositionMode blendMode() const
Returns the blending mode used for drawing the background shape.
 
@ SizeBuffer
Shape size is determined by adding a buffer margin around text.
 
bool enabled() const
Returns whether the background is enabled.
 
double opacity() const
Returns the background shape's opacity.
 
double rotation() const
Returns the rotation for the background shape, in degrees clockwise.
 
QColor fillColor() const
Returns the color used for filing the background shape.
 
SizeType sizeType() const
Returns the method used to determine the size of the background shape (e.g., fixed size or buffer aro...
 
Qgis::RenderUnit strokeWidthUnit() const
Returns the units used for the shape's stroke width.
 
ShapeType type() const
Returns the type of background shape (e.g., square, ellipse, SVG).
 
double strokeWidth() const
Returns the width of the shape's stroke (stroke).
 
@ ShapeMarkerSymbol
Marker symbol.
 
@ ShapeSquare
Square - buffered sizes only.
 
@ ShapeRectangle
Rectangle.
 
Qgis::RenderUnit offsetUnit() const
Returns the units used for the shape's offset.
 
QColor strokeColor() const
Returns the color used for outlining the background shape.
 
QgsFillSymbol * fillSymbol() const
Returns the fill symbol to be rendered in the background.
 
QgsMapUnitScale sizeMapUnitScale() const
Returns the map unit scale object for the shape size.
 
Qgis::RenderUnit sizeUnit() const
Returns the units used for the shape's size.
 
@ RotationOffset
Shape rotation is offset from text rotation.
 
@ RotationFixed
Shape rotation is a fixed angle.
 
QgsMarkerSymbol * markerSymbol() const
Returns the marker symbol to be rendered in the background.
 
const QgsPaintEffect * paintEffect() const
Returns the current paint effect for the background shape.
 
QgsMapUnitScale offsetMapUnitScale() const
Returns the map unit scale object for the shape offset.
 
QPointF offset() const
Returns the offset used for drawing the background shape.
 
Represents a block of text consisting of one or more QgsTextFragment objects.
 
int size() const
Returns the number of fragments in the block.
 
QString toPlainText() const
Converts the block to plain text.
 
Container for settings relating to a text buffer.
 
Qgis::RenderUnit sizeUnit() const
Returns the units for the buffer size.
 
Qt::PenJoinStyle joinStyle() const
Returns the buffer join style.
 
double size() const
Returns the size of the buffer.
 
QgsMapUnitScale sizeMapUnitScale() const
Returns the map unit scale object for the buffer size.
 
bool enabled() const
Returns whether the buffer is enabled.
 
double opacity() const
Returns the buffer opacity.
 
bool fillBufferInterior() const
Returns whether the interior of the buffer will be filled in.
 
const QgsPaintEffect * paintEffect() const
Returns the current paint effect for the buffer.
 
QColor color() const
Returns the color of the buffer.
 
QPainter::CompositionMode blendMode() const
Returns the blending mode used for drawing the buffer.
 
Stores information relating to individual character formatting.
 
void updateFontForFormat(QFont &font, const QgsRenderContext &context, double scaleFactor=1.0) const
Updates the specified font in place, applying character formatting options which are applicable on a ...
 
QColor textColor() const
Returns the character's text color, or an invalid color if no color override is set and the default f...
 
Qgis::TextCharacterVerticalAlignment verticalAlignment() const
Returns the format vertical alignment.
 
bool hasVerticalAlignmentSet() const
Returns true if the format has an explicit vertical alignment set.
 
double fontPointSize() const
Returns the font point size, or -1 if the font size is not set and should be inherited.
 
Contains pre-calculated metrics of a QgsTextDocument.
 
double verticalOrientationXOffset(int blockIndex) const
Returns the vertical orientation x offset for the specified block.
 
double fragmentVerticalOffset(int blockIndex, int fragmentIndex, Qgis::TextLayoutMode mode) const
Returns the vertical offset from a text block's baseline which should be applied to the fragment at t...
 
double blockMaximumDescent(int blockIndex) const
Returns the maximum descent encountered in the specified block.
 
QSizeF documentSize(Qgis::TextLayoutMode mode, Qgis::TextOrientation orientation) const
Returns the overall size of the document.
 
QFont fragmentFont(int blockIndex, int fragmentIndex) const
Returns the calculated font for the fragment at the specified block and fragment indices.
 
double blockMaximumCharacterWidth(int blockIndex) const
Returns the maximum character width for the specified block.
 
double baselineOffset(int blockIndex, Qgis::TextLayoutMode mode) const
Returns the offset from the top of the document to the text baseline for the given block index.
 
static QgsTextDocumentMetrics calculateMetrics(const QgsTextDocument &document, const QgsTextFormat &format, const QgsRenderContext &context, double scaleFactor=1.0)
Returns precalculated text metrics for a text document, when rendered using the given base format and...
 
double blockHeight(int blockIndex) const
Returns the height of the block at the specified index.
 
double fragmentHorizontalAdvance(int blockIndex, int fragmentIndex, Qgis::TextLayoutMode mode) const
Returns the horizontal advance of the fragment at the specified block and fragment index.
 
bool isNullFontSize() const
Returns true if the metrics could not be calculated because the text format has a null font size.
 
double blockWidth(int blockIndex) const
Returns the width of the block at the specified index.
 
double ascentOffset() const
Returns the ascent offset of the first block in the document.
 
Represents a document consisting of one or more QgsTextBlock objects.
 
const QgsTextBlock & at(int index) const
Returns the block at the specified index.
 
QStringList toPlainText() const
Returns a list of plain text lines of text representing the document.
 
int size() const
Returns the number of blocks in the document.
 
static QgsTextDocument fromHtml(const QStringList &lines)
Constructor for QgsTextDocument consisting of a set of HTML formatted lines.
 
static QgsTextDocument fromPlainText(const QStringList &lines)
Constructor for QgsTextDocument consisting of a set of plain text lines.
 
void append(const QgsTextBlock &block)
Appends a block to the document.
 
void applyCapitalization(Qgis::Capitalization capitalization)
Applies a capitalization style to the document's text.
 
Container for all settings relating to text rendering.
 
QgsMapUnitScale sizeMapUnitScale() const
Returns the map unit scale object for the size.
 
void updateDataDefinedProperties(QgsRenderContext &context)
Updates the format by evaluating current values of data defined properties.
 
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the format's property collection, used for data defined overrides.
 
QFont scaledFont(const QgsRenderContext &context, double scaleFactor=1.0, bool *isZeroSize=nullptr) const
Returns a font with the size scaled to match the format's size settings (including units and map unit...
 
QPainter::CompositionMode blendMode() const
Returns the blending mode used for drawing the text.
 
Qgis::Capitalization capitalization() const
Returns the text capitalization style.
 
QgsTextMaskSettings & mask()
Returns a reference to the masking settings.
 
QgsTextBackgroundSettings & background()
Returns a reference to the text background settings.
 
Qgis::RenderUnit sizeUnit() const
Returns the units for the size of rendered text.
 
bool allowHtmlFormatting() const
Returns true if text should be treated as a HTML document and HTML tags should be used for formatting...
 
double opacity() const
Returns the text's opacity.
 
Qgis::TextOrientation orientation() const
Returns the orientation of the text.
 
double size() const
Returns the size for rendered text.
 
QgsTextShadowSettings & shadow()
Returns a reference to the text drop shadow settings.
 
QColor color() const
Returns the color that text will be rendered in.
 
QgsTextBufferSettings & buffer()
Returns a reference to the text buffer settings.
 
Stores a fragment of text along with formatting overrides to be used when rendering the fragment.
 
QString text() const
Returns the text content of the fragment.
 
const QgsTextCharacterFormat & characterFormat() const
Returns the character formatting for the fragment.
 
Container for settings relating to a selective masking around a text.
 
Qgis::RenderUnit sizeUnit() const
Returns the units for the buffer size.
 
QgsMapUnitScale sizeMapUnitScale() const
Returns the map unit scale object for the buffer size.
 
double size() const
Returns the size of the buffer.
 
QgsPaintEffect * paintEffect() const
Returns the current paint effect for the mask.
 
double opacity() const
Returns the mask's opacity.
 
bool enabled() const
Returns whether the mask is enabled.
 
Qt::PenJoinStyle joinStyle() const
Returns the buffer join style.
 
Contains placement information for a single grapheme in a curved text layout.
 
@ RespectPainterOrientation
Curved text will be placed respecting the painter orientation, and the actual line direction will be ...
 
@ TruncateStringWhenLineIsTooShort
When a string is too long for the line, truncate characters instead of aborting the placement.
 
@ UseBaselinePlacement
Generate placement based on the character baselines instead of centers.
 
static std::unique_ptr< CurvePlacementProperties > generateCurvedTextPlacement(const QgsPrecalculatedTextMetrics &metrics, const QPolygonF &line, double offsetAlongLine, LabelLineDirection direction=RespectPainterOrientation, double maxConcaveAngle=-1, double maxConvexAngle=-1, CurvedTextFlags flags=CurvedTextFlags())
Flags controlling behavior of curved text generation.
 
static void drawDocumentOnLine(const QPolygonF &line, const QgsTextFormat &format, const QgsTextDocument &document, QgsRenderContext &context, double offsetAlongLine=0, double offsetFromLine=0)
Draws a text document along a line using the specified settings.
 
static Qgis::TextVerticalAlignment convertQtVAlignment(Qt::Alignment alignment)
Converts a Qt vertical alignment flag to a Qgis::TextVerticalAlignment value.
 
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.
 
static void drawDocument(const QRectF &rect, const QgsTextFormat &format, const QgsTextDocument &document, const QgsTextDocumentMetrics &metrics, QgsRenderContext &context, Qgis::TextHorizontalAlignment horizontalAlignment=Qgis::TextHorizontalAlignment::Left, Qgis::TextVerticalAlignment verticalAlignment=Qgis::TextVerticalAlignment::Top, double rotation=0, Qgis::TextLayoutMode mode=Qgis::TextLayoutMode::Rectangle, Qgis::TextRendererFlags flags=Qgis::TextRendererFlags())
Draws a text document within a rectangle using the specified settings.
 
static int sizeToPixel(double size, const QgsRenderContext &c, Qgis::RenderUnit unit, const QgsMapUnitScale &mapUnitScale=QgsMapUnitScale())
Calculates pixel size (considering output size should be in pixel or map units, scale factors and opt...
 
static Q_DECL_DEPRECATED void drawPart(const QRectF &rect, double rotation, Qgis::TextHorizontalAlignment alignment, const QStringList &textLines, QgsRenderContext &context, const QgsTextFormat &format, Qgis::TextComponent part, bool drawAsOutlines=true)
Draws a single component of rendered text using the specified settings.
 
static void drawText(const QRectF &rect, double rotation, Qgis::TextHorizontalAlignment alignment, const QStringList &textLines, QgsRenderContext &context, const QgsTextFormat &format, bool drawAsOutlines=true, Qgis::TextVerticalAlignment vAlignment=Qgis::TextVerticalAlignment::Top, Qgis::TextRendererFlags flags=Qgis::TextRendererFlags(), Qgis::TextLayoutMode mode=Qgis::TextLayoutMode::Rectangle)
Draws text within a rectangle using the specified settings.
 
static bool textRequiresWrapping(const QgsRenderContext &context, const QString &text, double width, const QgsTextFormat &format)
Returns true if the specified text requires line wrapping in order to fit within the specified width ...
 
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.
 
static void drawTextOnLine(const QPolygonF &line, const QString &text, QgsRenderContext &context, const QgsTextFormat &format, double offsetAlongLine=0, double offsetFromLine=0)
Draws text along a line using the specified settings.
 
static QStringList wrappedText(const QgsRenderContext &context, const QString &text, double width, const QgsTextFormat &format)
Wraps a text string to multiple lines, such that each individual line will fit within the specified w...
 
static double textHeight(const QgsRenderContext &context, const QgsTextFormat &format, const QStringList &textLines, Qgis::TextLayoutMode mode=Qgis::TextLayoutMode::Point, QFontMetricsF *fontMetrics=nullptr, Qgis::TextRendererFlags flags=Qgis::TextRendererFlags(), double maxLineWidth=0)
Returns the height of a text based on a given format.
 
static constexpr double SUPERSCRIPT_SUBSCRIPT_FONT_SIZE_SCALING_FACTOR
Scale factor to use for super or subscript text which doesn't have an explicit font size set.
 
static constexpr double FONT_WORKAROUND_SCALE
Scale factor for upscaling font sizes and downscaling destination painter devices.
 
static Qgis::TextHorizontalAlignment convertQtHAlignment(Qt::Alignment alignment)
Converts a Qt horizontal alignment flag to a Qgis::TextHorizontalAlignment value.
 
Container for settings relating to a text shadow.
 
int offsetAngle() const
Returns the angle for offsetting the position of the shadow from the text.
 
bool enabled() const
Returns whether the shadow is enabled.
 
int scale() const
Returns the scaling used for the drop shadow (in percentage of original size).
 
Qgis::RenderUnit offsetUnit() const
Returns the units used for the shadow's offset.
 
void setShadowPlacement(QgsTextShadowSettings::ShadowPlacement placement)
Sets the placement for the drop shadow.
 
double opacity() const
Returns the shadow's opacity.
 
QgsMapUnitScale blurRadiusMapUnitScale() const
Returns the map unit scale object for the shadow blur radius.
 
QColor color() const
Returns the color of the drop shadow.
 
@ ShadowBuffer
Draw shadow under buffer.
 
@ ShadowShape
Draw shadow under background shape.
 
@ ShadowLowest
Draw shadow below all text components.
 
@ ShadowText
Draw shadow under text.
 
QgsTextShadowSettings::ShadowPlacement shadowPlacement() const
Returns the placement for the drop shadow.
 
Qgis::RenderUnit blurRadiusUnit() const
Returns the units used for the shadow's blur radius.
 
double offsetDistance() const
Returns the distance for offsetting the position of the shadow from the text.
 
QPainter::CompositionMode blendMode() const
Returns the blending mode used for drawing the drop shadow.
 
QgsMapUnitScale offsetMapUnitScale() const
Returns the map unit scale object for the shadow offset distance.
 
bool blurAlphaOnly() const
Returns whether only the alpha channel for the shadow will be blurred.
 
bool offsetGlobal() const
Returns true if the global shadow offset will be used.
 
double blurRadius() const
Returns the blur radius for the shadow.
 
static Q_INVOKABLE QString encodeUnit(Qgis::DistanceUnit unit)
Encodes a distance unit to a string.
 
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)
 
Contains geos related utilities and functions.
 
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
 
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
 
QList< QgsSymbolLayer * > QgsSymbolLayerList
 
Q_GUI_EXPORT int qt_defaultDpiX()
 
Q_GUI_EXPORT int qt_defaultDpiY()