41#include <QTextBoundaryFinder>
43using namespace Qt::StringLiterals;
47 if ( alignment & Qt::AlignLeft )
49 else if ( alignment & Qt::AlignRight )
51 else if ( alignment & Qt::AlignHCenter )
53 else if ( alignment & Qt::AlignJustify )
62 if ( alignment & Qt::AlignTop )
64 else if ( alignment & Qt::AlignBottom )
66 else if ( alignment & Qt::AlignVCenter )
69 else if ( alignment & Qt::AlignBaseline )
77 return static_cast< int >(
c.convertToPainterUnits( size, unit, mapUnitScale ) + 0.5 );
84 const QStringList &text,
107 drawDocument( rect, lFormat, metrics.
document(), metrics, context, alignment, vAlignment, rotation, mode, flags );
123 const QgsTextFormat tmpFormat = updateShadowPosition( format );
141 drawParts( rect, rotation, horizontalAlignment, verticalAlignment, document, metrics, context, tmpFormat, components, mode );
149 lFormat = updateShadowPosition( lFormat );
170 const QgsTextFormat lFormat = updateShadowPosition( _format );
189 drawParts( point, rotation, alignment, document, metrics, context, lFormat, components, mode );
199 lFormat = updateShadowPosition( lFormat );
206 drawDocumentOnLine( line, lFormat, document, context, offsetAlongLine, offsetFromLine, flags, textAnchor );
210 const QPolygonF &line,
214 double offsetAlongLine,
215 double offsetFromLine,
220 QPolygonF labelBaselineCurve = line;
229#if GEOS_VERSION_NUM < GEOS_COMPUTE_VERSION( 3, 11, 0 )
230 if ( offsetFromLine < 0 )
233 std::unique_ptr< QgsLineString > reversed( offsetCurve->reversed() );
237 offsetCurve = std::move( reversed );
241 labelBaselineCurve = offsetCurve->asQPolygonF();
246 const QFont baseFont = format.
scaledFont( context, fontScale );
247 const double letterSpacing = baseFont.letterSpacing() / fontScale;
248 const double wordSpacing = baseFont.wordSpacing() / fontScale;
250 QStringList graphemes;
251 QVector< QgsTextCharacterFormat > graphemeFormats;
252 QVector< QgsTextDocumentMetrics > graphemeMetrics;
254 for (
const QgsTextBlock &block : std::as_const( document ) )
259 for (
const QString &grapheme : fragmentGraphemes )
261 graphemes.append( grapheme );
262 graphemeFormats.append( fragment.characterFormat() );
272 QVector< double > characterWidths( graphemes.count() );
273 QVector< double > characterHeights( graphemes.count() );
274 QVector< double > characterDescents( graphemes.count() );
275 QFont previousNonSuperSubScriptFont;
277 for (
int i = 0; i < graphemes.count(); i++ )
282 double graphemeFirstCharHorizontalAdvanceWithLetterSpacing = 0;
283 double graphemeFirstCharHorizontalAdvance = 0;
284 double graphemeHorizontalAdvance = 0;
285 double characterDescent = 0;
286 double characterHeight = 0;
289 QFont graphemeFont = baseFont;
293 previousNonSuperSubScriptFont = graphemeFont;
300 previousNonSuperSubScriptFont = graphemeFont;
321 previousNonSuperSubScriptFont = graphemeFont;
324 const QFontMetricsF graphemeFontMetrics( graphemeFont );
325 graphemeFirstCharHorizontalAdvance = graphemeFontMetrics.horizontalAdvance( QString( graphemes[i].at( 0 ) ) ) / fontScale;
326 graphemeFirstCharHorizontalAdvanceWithLetterSpacing = graphemeFontMetrics.horizontalAdvance( graphemes[i].at( 0 ) ) / fontScale + letterSpacing;
327 graphemeHorizontalAdvance = graphemeFontMetrics.horizontalAdvance( QString( graphemes[i] ) ) / fontScale;
328 characterDescent = graphemeFontMetrics.descent() / fontScale;
329 characterHeight = graphemeFontMetrics.height() / fontScale;
331 qreal wordSpaceFix = qreal( 0.0 );
332 if ( graphemes[i] ==
" "_L1 )
336 wordSpaceFix = ( nxt < graphemes.count() && graphemes[nxt] !=
" "_L1 ) ? wordSpacing : qreal( 0.0 );
341 if ( graphemes[i].length() == 1 && !
qgsDoubleNear( graphemeFirstCharHorizontalAdvance, graphemeFirstCharHorizontalAdvanceWithLetterSpacing ) )
344 wordSpaceFix -= wordSpacing;
347 const double charWidth = graphemeHorizontalAdvance + wordSpaceFix;
348 characterWidths[i] = charWidth;
349 characterHeights[i] = characterHeight;
350 characterDescents[i] = characterDescent;
353 QgsPrecalculatedTextMetrics metrics( graphemes, std::move( characterWidths ), std::move( characterHeights ), std::move( characterDescents ) );
356 std::unique_ptr< QgsTextRendererUtils::CurvePlacementProperties > placement
359 if ( placement->graphemePlacement.empty() )
366 QHash< int, QgsTextRenderer::Component > components;
367 components.reserve( placement->graphemePlacement.size() );
370 QgsTextRenderer::Component component;
371 component.origin = QPointF( grapheme.x, grapheme.y );
372 component.rotationRadians = -grapheme.angle;
378 component.origin.rx() += verticalOffset * std::cos( grapheme.angle + M_PI_2 );
379 component.origin.ry() += verticalOffset * std::sin( grapheme.angle + M_PI_2 );
382 components.insert( grapheme.graphemeIndex, component );
390 const QgsTextRenderer::Component &component = components[grapheme.graphemeIndex];
400 const QgsTextRenderer::Component &component = components[grapheme.graphemeIndex];
409 const QgsTextRenderer::Component &component = components[grapheme.graphemeIndex];
447void QgsTextRenderer::drawParts(
466 component.dpiRatio = 1.0;
467 component.origin = rect.topLeft();
468 component.rotationRadians = rotation;
469 component.size = rect.size();
470 component.hAlign = alignment;
474 Component backgroundComponent = component;
479 double xc = rect.width() / 2.0;
480 double yc = rect.height() / 2.0;
482 double angle = -rotation;
483 double xd = xc * std::cos( angle ) - yc * std::sin( angle );
484 double yd = xc * std::sin( angle ) + yc * std::cos( angle );
486 backgroundComponent.center = QPointF( backgroundComponent.origin.x() + xd, backgroundComponent.origin.y() + yd );
490 backgroundComponent.center = rect.center();
493 switch ( vAlignment )
498 backgroundComponent.origin.ry() += ( rect.height() - metrics.
documentSize( mode, format.
orientation() ).height() ) / 2;
501 backgroundComponent.origin.ry() += ( rect.height() - metrics.
documentSize( mode, format.
orientation() ).height() );
515 drawTextInternal( parts, context, format, component, document, metrics, alignment, vAlignment, mode );
530void QgsTextRenderer::drawParts(
548 component.dpiRatio = 1.0;
549 component.origin = origin;
550 component.rotationRadians = rotation;
551 component.hAlign = alignment;
555 QgsTextRenderer::drawBackground( context, component, format, metrics, mode );
571 return QFontMetricsF( format.
scaledFont( context, scaleFactor ), context.
painter() ? context.
painter()->device() :
nullptr );
576 QPainter *p = context.
painter();
581 constexpr double DEGREES_315_TO_RADIANS = 315 * ( M_PI / 180 );
582 constexpr double DEGREES_90_TO_RADIANS = 90 * ( M_PI / 180 );
583 constexpr double DEGREES_45_TO_RADIANS = 45 * ( M_PI / 180 );
584 if ( component.rotationRadians >= -DEGREES_315_TO_RADIANS && component.rotationRadians < -DEGREES_90_TO_RADIANS )
588 else if ( component.rotationRadians >= -DEGREES_90_TO_RADIANS && component.rotationRadians < -DEGREES_45_TO_RADIANS )
598 QgsTextBufferSettings buffer = format.
buffer();
605 std::optional< QgsScopedRenderContextReferenceScaleOverride > referenceScaleOverride;
610 referenceScaleOverride.emplace( QgsScopedRenderContextReferenceScaleOverride( context, -1.0 ) );
616 referenceScaleOverride.reset();
619 path.setFillRule( Qt::WindingFill );
621 double height = component.size.height();
622 switch ( orientation )
633 double partYOffset = component.offset.y() * scaleFactor;
636 double partLastDescent = 0;
638 int fragmentIndex = 0;
639 for (
const QgsTextFragment &fragment : component.block )
641 const QFont fragmentFont = metrics.
fragmentFont( component.blockIndex, component.firstFragmentIndex + fragmentIndex );
642 const double letterSpacing = fragmentFont.letterSpacing() / scaleFactor;
644 const QFontMetricsF fragmentMetrics( fragmentFont );
646 const double fragmentYOffset = metrics.
fragmentVerticalOffset( component.blockIndex, fragmentIndex, mode );
649 for (
const QString &part : parts )
651 double partXOffset = ( blockMaximumCharacterWidth - ( fragmentMetrics.horizontalAdvance( part ) / scaleFactor - letterSpacing ) ) / 2;
652 partYOffset += fragmentMetrics.ascent() / scaleFactor;
653 path.addText( partXOffset, partYOffset + fragmentYOffset, fragmentFont, part );
654 partYOffset += letterSpacing;
656 partLastDescent = fragmentMetrics.descent() / scaleFactor;
660 height = partYOffset + partLastDescent;
661 advance = partYOffset - component.offset.y() * scaleFactor;
666 QColor bufferColor = buffer.
color();
667 bufferColor.setAlphaF( buffer.
opacity() );
668 QPen pen( bufferColor );
669 pen.setWidthF( penSize * scaleFactor );
671 QColor tmpColor( bufferColor );
675 tmpColor.setAlpha( 0 );
681 buffp.begin( &buffPict );
685 std::unique_ptr< QgsPaintEffect > tmpEffect( buffer.
paintEffect()->
clone() );
687 tmpEffect->begin( context );
688 context.
painter()->setPen( pen );
689 context.
painter()->setBrush( tmpColor );
690 if ( scaleFactor != 1.0 )
691 context.
painter()->scale( 1 / scaleFactor, 1 / scaleFactor );
692 context.
painter()->drawPath( path );
693 if ( scaleFactor != 1.0 )
694 context.
painter()->scale( scaleFactor, scaleFactor );
695 tmpEffect->end( context );
701 if ( scaleFactor != 1.0 )
702 buffp.scale( 1 / scaleFactor, 1 / scaleFactor );
704 buffp.setBrush( tmpColor );
705 buffp.drawPath( path );
711 QgsTextRenderer::Component bufferComponent = component;
712 bufferComponent.origin = QPointF( 0.0, 0.0 );
713 bufferComponent.picture = buffPict;
714 bufferComponent.pictureBuffer = penSize / 2.0;
715 bufferComponent.size.setHeight( height );
719 bufferComponent.offset.setY( -bufferComponent.size.height() );
721 drawShadow( context, bufferComponent, format );
724 QgsScopedQPainterState painterState( p );
729 p->setCompositionMode( buffer.
blendMode() );
733 p->scale( component.dpiRatio, component.dpiRatio );
735 p->drawPicture( 0, 0, buffPict );
737 return advance / scaleFactor;
742 QgsTextMaskSettings mask = format.
mask();
755 path.setFillRule( Qt::WindingFill );
762 std::optional< QgsScopedRenderContextReferenceScaleOverride > referenceScaleOverride;
767 referenceScaleOverride.emplace( QgsScopedRenderContextReferenceScaleOverride( context, -1.0 ) );
773 referenceScaleOverride.reset();
776 int fragmentIndex = 0;
777 for (
const QgsTextFragment &fragment : component.block )
781 const QFont fragmentFont = metrics.
fragmentFont( component.blockIndex, fragmentIndex );
783 const double fragmentYOffset = metrics.
fragmentVerticalOffset( component.blockIndex, fragmentIndex, mode );
784 path.addText( xOffset, fragmentYOffset, fragmentFont, fragment.
text() );
791 QColor bufferColor( Qt::gray );
792 bufferColor.setAlphaF( mask.
opacity() );
796 brush.setColor( bufferColor );
797 pen.setColor( bufferColor );
798 pen.setWidthF( penSize * scaleFactor );
801 QgsScopedQPainterState painterState( p );
805 p->scale( component.dpiRatio, component.dpiRatio );
808 QgsPainterSwapper swapper( context, p );
810 QgsEffectPainter effectPainter( context, mask.
paintEffect() );
811 if ( scaleFactor != 1.0 )
812 context.
painter()->scale( 1 / scaleFactor, 1 / scaleFactor );
813 context.
painter()->setPen( pen );
814 context.
painter()->setBrush( brush );
815 context.
painter()->drawPath( path );
816 if ( scaleFactor != 1.0 )
817 context.
painter()->scale( scaleFactor, scaleFactor );
822 if ( scaleFactor != 1.0 )
823 p->scale( 1 / scaleFactor, 1 / scaleFactor );
825 p->setBrush( brush );
827 if ( scaleFactor != 1.0 )
828 p->scale( scaleFactor, scaleFactor );
835 if ( doc.
size() == 0 )
838 return textWidth( context, format, doc );
857 for (
const QString &line : textLines )
861 lines.append(
wrappedText( context, line, maxLineWidth, format ) );
865 lines.append( line );
870 return textHeight( context, format, doc, mode );
877 bool isNullSize =
false;
878 const QFont baseFont = format.
scaledFont( context, scaleFactor, &isNullSize );
882 const QFontMetrics fm( baseFont );
883 const double height = ( character.isNull() ? fm.height() : fm.boundingRect( character ).height() ) / scaleFactor;
885 if ( !includeEffects )
888 double maxExtension = 0;
916 return height + maxExtension;
924 const QStringList multiLineSplit = text.split(
'\n' );
926 return currentTextWidth > width;
931 const QStringList lines = text.split(
'\n' );
932 QStringList outLines;
933 for (
const QString &line : lines )
938 const QStringList words = line.split(
' ' );
939 QStringList linesToProcess;
940 QString wordsInCurrentLine;
941 for (
const QString &word : words )
946 if ( !wordsInCurrentLine.isEmpty() )
947 linesToProcess << wordsInCurrentLine;
948 wordsInCurrentLine.clear();
949 linesToProcess << word;
953 if ( !wordsInCurrentLine.isEmpty() )
954 wordsInCurrentLine.append(
' ' );
955 wordsInCurrentLine.append( word );
958 if ( !wordsInCurrentLine.isEmpty() )
959 linesToProcess << wordsInCurrentLine;
961 for (
const QString &line : std::as_const( linesToProcess ) )
963 QString remainingText = line;
964 int lastPos = remainingText.lastIndexOf(
' ' );
965 while ( lastPos > -1 )
975 outLines << remainingText.left( lastPos );
976 remainingText = remainingText.mid( lastPos + 1 );
979 lastPos = remainingText.lastIndexOf(
' ', lastPos - 1 );
981 outLines << remainingText;
1010 Component component =
c;
1011 QgsTextBackgroundSettings background = format.
background();
1013 QPainter *prevP = context.
painter();
1014 QPainter *p = context.
painter();
1015 std::unique_ptr< QgsPaintEffect > tmpEffect;
1019 tmpEffect->begin( context );
1028 const double originAdjustRotationRadians = -component.rotationRadians;
1031 component.rotationRadians = -( component.rotationRadians );
1036 component.rotationRadians = 0.0;
1037 component.rotationOffsetRadians = background.
rotation() * M_PI / 180;
1046 double width = documentSize.width();
1047 double height = documentSize.height();
1054 switch ( component.hAlign )
1058 component.center = QPointF( component.origin.x() + width / 2.0, component.origin.y() + height / 2.0 );
1062 component.center = QPointF( component.origin.x() + component.size.width() / 2.0, component.origin.y() + height / 2.0 );
1066 component.center = QPointF( component.origin.x() + component.size.width() - width / 2.0, component.origin.y() + height / 2.0 );
1073 bool isNullSize =
false;
1074 QFontMetricsF fm( format.
scaledFont( context, scaleFactor, &isNullSize ) );
1075 double originAdjust = isNullSize ? 0 : ( fm.ascent() / scaleFactor / 2.0 - fm.leading() / scaleFactor / 2.0 );
1076 switch ( component.hAlign )
1080 component.center = QPointF( component.origin.x() + width / 2.0, component.origin.y() - height / 2.0 + originAdjust );
1084 component.center = QPointF( component.origin.x(), component.origin.y() - height / 2.0 + originAdjust );
1088 component.center = QPointF( component.origin.x() - width / 2.0, component.origin.y() - height / 2.0 + originAdjust );
1095 const double dx = component.center.x() - component.origin.x();
1096 const double dy = component.center.y() - component.origin.y();
1097 component.center.setX( component.origin.x() + ( std::cos( originAdjustRotationRadians ) * dx - std::sin( originAdjustRotationRadians ) * dy ) );
1098 component.center.setY( component.origin.y() + ( std::sin( originAdjustRotationRadians ) * dx + std::cos( originAdjustRotationRadians ) * dy ) );
1108 component.size = QSizeF( width, height );
1113 switch ( background.
type() )
1126 double sizeOut = 0.0;
1128 QgsScopedRenderContextReferenceScaleOverride referenceScaleOverride( context, -1 );
1137 sizeOut = std::max( component.size.width(), component.size.height() );
1141 sizeOut += bufferSize * 2;
1147 if ( sizeOut < 1.0 )
1150 std::unique_ptr< QgsMarkerSymbol > renderedSymbol;
1154 map[u
"name"_s] = background.
svgFile().trimmed();
1155 map[u
"size"_s] = QString::number( sizeOut );
1157 map[u
"angle"_s] = QString::number( 0.0 );
1165 map[u
"fill"_s] = background.
fillColor().name();
1166 map[u
"outline"_s] = background.
strokeColor().name();
1167 map[u
"outline-width"_s] = QString::number( background.
strokeWidth() );
1172 QgsTextShadowSettings shadow = format.
shadow();
1174 QVariantMap shdwmap( map );
1175 shdwmap[u
"fill"_s] = shadow.
color().name();
1176 shdwmap[u
"outline"_s] = shadow.
color().name();
1177 shdwmap[u
"size"_s] = QString::number( sizeOut );
1182 svgp.begin( &svgPict );
1190 QgsRenderContext shdwContext;
1196 QgsSvgMarkerSymbolLayer *svgShdwM =
static_cast<QgsSvgMarkerSymbolLayer *
>( symShdwL.get() );
1199 svgShdwM->
renderPoint( QPointF( sizeOut / 2, -sizeOut / 2 ), svgShdwContext );
1202 component.picture = svgPict;
1204 component.pictureBuffer = 0.0;
1206 component.size = QSizeF( sizeOut, sizeOut );
1207 component.offset = QPointF( 0.0, 0.0 );
1210 QgsScopedQPainterState painterState( p );
1213 p->translate( component.center.x(), component.center.y() );
1214 p->rotate( component.rotationRadians * 180 / M_PI );
1217 p->translate( QPointF( xoff, yoff ) );
1218 p->rotate( component.rotationOffsetRadians * 180 / M_PI );
1219 p->translate( -sizeOut / 2, sizeOut / 2 );
1221 drawShadow( context, component, format );
1223 renderedSymbol.reset();
1226 renderedSymbol = std::make_unique<QgsMarkerSymbol>(
QgsSymbolLayerList() << symL.release() );
1231 renderedSymbol->setSize( sizeOut );
1235 renderedSymbol->setOpacity( renderedSymbol->opacity() * background.
opacity() );
1238 QgsScopedQPainterState painterState( p );
1243 p->setCompositionMode( background.
blendMode() );
1245 p->translate( component.center.x(), component.center.y() );
1246 p->rotate( component.rotationRadians * 180 / M_PI );
1249 p->translate( QPointF( xoff, yoff ) );
1250 p->rotate( component.rotationOffsetRadians * 180 / M_PI );
1254 renderedSymbol->renderPoint( QPointF( 0, 0 ), &f, context );
1255 renderedSymbol->stopRender( context );
1256 p->setCompositionMode( QPainter::CompositionMode_SourceOver );
1266 double w = component.size.width();
1267 double h = component.size.height();
1286 h = std::sqrt( std::pow( w, 2 ) + std::pow( h, 2 ) );
1292 h = h * M_SQRT1_2 * 2;
1293 w = w * M_SQRT1_2 * 2;
1299 w += bufferWidth * 2;
1300 h += bufferHeight * 2;
1304 QRectF rect( -w / 2.0, -h / 2.0, w, h );
1306 if ( rect.isNull() )
1309 QgsScopedQPainterState painterState( p );
1312 p->translate( QPointF( component.center.x(), component.center.y() ) );
1313 p->rotate( component.rotationRadians * 180 / M_PI );
1316 p->translate( QPointF( xoff, yoff ) );
1317 p->rotate( component.rotationOffsetRadians * 180 / M_PI );
1323 QTransform t = QTransform::fromScale( 10, 10 );
1325 QTransform ti = t.inverted();
1331 path.addRoundedRect( rect, background.
radii().width(), background.
radii().height(), Qt::RelativeSize );
1337 path.addRoundedRect( rect, xRadius, yRadius );
1342 path.addEllipse( rect );
1344 QPolygonF tempPolygon = path.toFillPolygon( t );
1345 QPolygonF polygon = ti.map( tempPolygon );
1347 QPainter *oldp = context.
painter();
1350 shapep.begin( &shapePict );
1353 std::unique_ptr< QgsFillSymbol > renderedSymbol;
1355 renderedSymbol->setOpacity( renderedSymbol->opacity() * background.
opacity() );
1359 renderedSymbol->renderPolygon( polygon,
nullptr, &f, context );
1360 renderedSymbol->stopRender( context );
1367 component.picture = shapePict;
1370 component.size = rect.size();
1371 component.offset = QPointF( rect.width() / 2, -rect.height() / 2 );
1372 drawShadow( context, component, format );
1377 p->setCompositionMode( background.
blendMode() );
1381 p->scale( component.dpiRatio, component.dpiRatio );
1383 p->drawPicture( 0, 0, shapePict );
1384 p->setCompositionMode( QPainter::CompositionMode_SourceOver );
1391 tmpEffect->end( context );
1398 QgsTextShadowSettings shadow = format.
shadow();
1400 QPainter *p = context.
painter();
1401 const double componentWidth = component.size.width();
1402 const double componentHeight = component.size.height();
1403 const double xOffset = component.offset.x();
1404 const double yOffset = component.offset.y();
1405 double pictbuffer = component.pictureBuffer;
1413 radius /= ( mapUnits ? context.
scaleFactor() / component.dpiRatio : 1 );
1414 radius =
static_cast< int >( radius + 0.5 );
1418 double blurBufferClippingScale = 3.75;
1419 int blurbuffer = ( radius > 17 ? 16 : radius ) * blurBufferClippingScale;
1421 QImage blurImg( componentWidth + ( pictbuffer * 2.0 ) + ( blurbuffer * 2.0 ), componentHeight + ( pictbuffer * 2.0 ) + ( blurbuffer * 2.0 ), QImage::Format_ARGB32_Premultiplied );
1425 int minBlurImgSize = 1;
1429 int maxBlurImgSize = 40000;
1430 if ( blurImg.isNull() || ( blurImg.width() < minBlurImgSize || blurImg.height() < minBlurImgSize ) || ( blurImg.width() > maxBlurImgSize || blurImg.height() > maxBlurImgSize ) )
1433 blurImg.fill( QColor( Qt::transparent ).rgba() );
1435 if ( !pictp.begin( &blurImg ) )
1437 pictp.setRenderHints( QPainter::Antialiasing | QPainter::SmoothPixmapTransform );
1438 QPointF imgOffset( blurbuffer + pictbuffer + xOffset, blurbuffer + pictbuffer + componentHeight + yOffset );
1440 pictp.drawPicture( imgOffset, component.picture );
1443 pictp.setCompositionMode( QPainter::CompositionMode_SourceIn );
1444 pictp.fillRect( blurImg.rect(), shadow.
color() );
1448 if ( shadow.
blurRadius() > 0.0 && radius > 0 )
1456 picti.begin( &blurImg );
1457 picti.setBrush( Qt::Dense7Pattern );
1458 QPen imgPen( QColor( 0, 0, 255, 255 ) );
1459 imgPen.setWidth( 1 );
1460 picti.setPen( imgPen );
1461 picti.setOpacity( 0.1 );
1462 picti.drawRect( 0, 0, blurImg.width(), blurImg.height() );
1468 double angleRad = shadow.
offsetAngle() * M_PI / 180;
1476 angleRad -= ( component.rotationRadians + component.rotationOffsetRadians );
1479 QPointF transPt( -offsetDist * std::cos( angleRad + M_PI_2 ), -offsetDist * std::sin( angleRad + M_PI_2 ) );
1481 QgsScopedQPainterState painterState( p );
1485 p->setRenderHint( QPainter::SmoothPixmapTransform );
1488 p->setCompositionMode( shadow.
blendMode() );
1490 p->setOpacity( shadow.
opacity() );
1492 double scale = shadow.
scale() / 100.0;
1494 p->scale( scale, scale );
1495 if ( component.useOrigin )
1497 p->translate( component.origin.x(), component.origin.y() );
1499 p->translate( transPt );
1500 p->translate( -imgOffset.x(), -imgOffset.y() );
1501 p->drawImage( 0, 0, blurImg );
1503 painterState.restore();
1509 p->setBrush( Qt::NoBrush );
1510 QPen imgPen( QColor( 255, 0, 0, 10 ) );
1511 imgPen.setWidth( 2 );
1512 imgPen.setStyle( Qt::DashLine );
1513 p->setPen( imgPen );
1514 p->scale( scale, scale );
1515 if ( component.useOrigin() )
1517 p->translate( component.origin().x(), component.origin().y() );
1519 p->translate( transPt );
1520 p->translate( -imgOffset.x(),
1522 p->drawRect( 0, 0, blurImg.width(), blurImg.height() );
1527 p->setBrush( Qt::NoBrush );
1528 QPen componentRectPen( QColor( 0, 255, 0, 70 ) );
1529 componentRectPen.setWidth( 1 );
1530 if ( component.useOrigin() )
1532 p->translate( component.origin().x(), component.origin().y() );
1534 p->setPen( componentRectPen );
1535 p->drawRect( QRect( -xOffset, -componentHeight - yOffset, componentWidth, componentHeight ) );
1541void QgsTextRenderer::drawTextInternal(
1545 const Component &component,
1560 std::optional< QgsScopedRenderContextReferenceScaleOverride > referenceScaleOverride;
1565 referenceScaleOverride.emplace( QgsScopedRenderContextReferenceScaleOverride( context, -1.0 ) );
1571 referenceScaleOverride.reset();
1573 double rotationDegrees = 0;
1574 const Qgis::TextOrientation orientation = calculateRotationAndOrientationForComponent( format, component, rotationDegrees );
1575 switch ( orientation )
1579 drawTextInternalHorizontal( context, format, components, mode, component, document, metrics, fontScale, alignment, vAlignment, rotationDegrees );
1588 drawTextInternalVertical( context, format,
Qgis::TextComponent::Buffer, mode, component, document, metrics, fontScale, alignment, vAlignment, rotationDegrees );
1590 drawTextInternalVertical( context, format,
Qgis::TextComponent::Text, mode, component, document, metrics, fontScale, alignment, vAlignment, rotationDegrees );
1596Qgis::TextOrientation QgsTextRenderer::calculateRotationAndOrientationForComponent(
const QgsTextFormat &format,
const QgsTextRenderer::Component &component,
double &rotationDegrees )
1598 rotationDegrees = -component.rotationRadians * 180 / M_PI;
1605 if ( rotationDegrees >= -315 && rotationDegrees < -90 )
1607 rotationDegrees -= 90;
1610 else if ( rotationDegrees >= -90 && rotationDegrees < -45 )
1612 rotationDegrees += 90;
1626void QgsTextRenderer::calculateExtraSpacingForLineJustification(
const double spaceToDistribute,
const QgsTextBlock &block,
double &extraWordSpace,
double &extraLetterSpace )
1629 QTextBoundaryFinder
finder( QTextBoundaryFinder::Word, blockText );
1631 int wordBoundaries = 0;
1632 while (
finder.toNextBoundary() != -1 )
1634 if (
finder.boundaryReasons() & QTextBoundaryFinder::StartOfItem )
1638 if ( wordBoundaries > 0 )
1641 extraWordSpace = spaceToDistribute / wordBoundaries;
1646 QTextBoundaryFinder
finder( QTextBoundaryFinder::Grapheme, blockText );
1649 int graphemeBoundaries = 0;
1650 while (
finder.toNextBoundary() != -1 )
1652 if (
finder.boundaryReasons() & QTextBoundaryFinder::StartOfItem )
1653 graphemeBoundaries++;
1656 if ( graphemeBoundaries > 0 )
1658 extraLetterSpace = spaceToDistribute / graphemeBoundaries;
1663void QgsTextRenderer::applyExtraSpacingForLineJustification( QFont &font,
double extraWordSpace,
double extraLetterSpace )
1665 const double prevWordSpace = font.wordSpacing();
1666 font.setWordSpacing( prevWordSpace + extraWordSpace );
1667 const double prevLetterSpace = font.letterSpacing();
1668 font.setLetterSpacing( QFont::AbsoluteSpacing, prevLetterSpace + extraLetterSpace );
1672void QgsTextRenderer::renderBlockHorizontal(
1679 bool forceRenderAsPaths,
1681 double extraWordSpace,
1682 double extraLetterSpace,
1684 DeferredRenderBlock *deferredRenderBlock
1690 int fragmentIndex = 0;
1691 for (
const QgsTextFragment &fragment : block )
1696 QFont fragmentFont = metrics.
fragmentFont( blockIndex, fragmentIndex );
1699 applyExtraSpacingForLineJustification( fragmentFont, extraWordSpace * fontScale, extraLetterSpace * fontScale );
1706 if ( deferredRenderBlock )
1708 DeferredRenderFragment renderFragment;
1709 renderFragment.color = textColor;
1710 if ( forceRenderAsPaths )
1712 renderFragment.path.setFillRule( Qt::WindingFill );
1713 renderFragment.path.addText( xOffset, yOffset, fragmentFont, fragment.
text() );
1715 renderFragment.font = fragmentFont;
1716 renderFragment.point = QPointF( xOffset, yOffset );
1717 renderFragment.text = fragment.
text();
1718 deferredRenderBlock->fragments.append( renderFragment );
1720 else if ( forceRenderAsPaths )
1722 painter->setBrush( textColor );
1724 path.setFillRule( Qt::WindingFill );
1725 path.addText( xOffset, yOffset, fragmentFont, fragment.
text() );
1726 painter->drawPath( path );
1730 painter->setPen( textColor );
1731 painter->setFont( fragmentFont );
1732 painter->drawText( QPointF( xOffset, yOffset ), fragment.
text() );
1735 else if ( fragment.
isImage() )
1737 bool fitsInCache =
false;
1739 const double imageHeight = metrics.
fragmentFixedHeight( blockIndex, fragmentIndex, mode ) * fontScale;
1743 QSize(
static_cast< int >( std::round( imageWidth ) ),
static_cast< int >( std::round( imageHeight ) ) ),
1750 const double yOffset = imageBaseline - image.height();
1751 if ( !image.isNull() )
1752 painter->drawImage( QPointF( xOffset, yOffset ), image );
1783 if ( format.
font().underline() || format.
font().overline() || format.
font().strikeOut() || std::any_of( document.begin(), document.end(), [](
const QgsTextBlock &block ) {
1784 return std::any_of( block.begin(), block.end(), []( const QgsTextFragment &fragment ) {
1785 return fragment.characterFormat().underline() == QgsTextCharacterFormat::BooleanValue::SetTrue
1786 || fragment.characterFormat().overline() == QgsTextCharacterFormat::BooleanValue::SetTrue
1787 || fragment.characterFormat().strikeOut() == QgsTextCharacterFormat::BooleanValue::SetTrue;
1800 return std::any_of( document.begin(), document.end(), [](
const QgsTextBlock &block ) {
1801 return std::any_of( block.begin(), block.end(), []( const QgsTextFragment &fragment ) { return fragment.isImage(); } );
1805QVector< QgsTextRenderer::BlockMetrics > QgsTextRenderer::calculateBlockMetrics(
1809 QVector< BlockMetrics > blockMetrics;
1810 blockMetrics.reserve( document.
size() );
1813 for (
const QgsTextBlock &block : document )
1816 if ( block.blockFormat().hasHorizontalAlignmentSet() )
1817 blockAlignment = block.blockFormat().horizontalAlignment();
1820 const bool isFinalLineInParagraph = ( blockIndex == document.size() - 1 ) || document.at( blockIndex + 1 ).toPlainText().trimmed().isEmpty();
1822 BlockMetrics thisBlockMetrics;
1824 thisBlockMetrics.width = metrics.
blockWidth( blockIndex );
1826 if ( adjustForAlignment )
1828 double blockWidthDiff = 0;
1829 switch ( blockAlignment )
1836 blockWidthDiff = targetWidth - thisBlockMetrics.width - metrics.
blockRightMargin( blockIndex );
1840 if ( !isFinalLineInParagraph && targetWidth > thisBlockMetrics.width )
1842 calculateExtraSpacingForLineJustification( targetWidth - thisBlockMetrics.width, block, thisBlockMetrics.extraWordSpace, thisBlockMetrics.extraLetterSpace );
1843 thisBlockMetrics.width = targetWidth;
1859 thisBlockMetrics.xOffset = blockWidthDiff;
1864 switch ( blockAlignment )
1867 thisBlockMetrics.xOffset = blockWidthDiff - targetWidth;
1871 thisBlockMetrics.xOffset = blockWidthDiff - targetWidth / 2.0;
1893 thisBlockMetrics.backgroundWidth = targetWidth;
1894 thisBlockMetrics.backgroundXOffset = 0;
1898 thisBlockMetrics.backgroundWidth = thisBlockMetrics.width;
1899 thisBlockMetrics.backgroundXOffset = thisBlockMetrics.xOffset;
1903 blockMetrics << thisBlockMetrics;
1906 return blockMetrics;
1909QBrush QgsTextRenderer::createBrushForPath(
QgsRenderContext &context,
const QString &path )
1911 bool fitsInCache =
false;
1915 const QSizeF originalSizeMmAt96Dpi = imageSize / 3.7795275590551185;
1917 const double imageWidth = originalSizeMmAt96Dpi.width() * pixelsPerMm;
1918 const double imageHeight = originalSizeMmAt96Dpi.height() * pixelsPerMm;
1920 if ( imageWidth == 0 || imageHeight == 0 )
1926 if ( !image.isNull() )
1928 res.setTextureImage( image );
1933void QgsTextRenderer::renderDocumentBackgrounds(
1937 const Component &component,
1938 const QVector< QgsTextRenderer::BlockMetrics > &blockMetrics,
1940 double verticalAlignOffset,
1941 double rotationDegrees
1945 context.
painter()->translate( component.origin );
1947 context.
painter()->rotate( rotationDegrees );
1949 context.
painter()->setPen( Qt::NoPen );
1950 context.
painter()->setBrush( Qt::NoBrush );
1951 for (
const QgsTextBlock &block : document )
1953 const double baseLineOffset = metrics.
baselineOffset( blockIndex, mode );
1957 if ( block.blockFormat().hasBackground() )
1959 QBrush backgroundBrush = block.blockFormat().backgroundBrush();
1960 if ( !block.blockFormat().backgroundImagePath().isEmpty() )
1962 const QBrush backgroundImageBrush = createBrushForPath( context, block.blockFormat().backgroundImagePath() );
1963 if ( backgroundImageBrush.style() == Qt::BrushStyle::TexturePattern )
1964 backgroundBrush = backgroundImageBrush;
1967 context.
painter()->setBrush( backgroundBrush );
1969 QRectF( blockMetrics[blockIndex].backgroundXOffset, baseLineOffset - blockMaximumAscent, blockMetrics[blockIndex].backgroundWidth, blockMaximumDescent + blockMaximumAscent )
1974 int fragmentIndex = 0;
1976 for (
const QgsTextFragment &fragment : block )
1979 const double ascent = metrics.
fragmentAscent( blockIndex, fragmentIndex, mode );
1980 const double descent = metrics.
fragmentDescent( blockIndex, fragmentIndex, mode );
1990 if ( backgroundImageBrush.style() == Qt::BrushStyle::TexturePattern )
1991 backgroundBrush = backgroundImageBrush;
1994 context.
painter()->setBrush( backgroundBrush );
1995 context.
painter()->drawRect( QRectF( blockMetrics[blockIndex].xOffset + xOffset, baseLineOffset + verticalAlignOffset + yOffset - ascent, horizontalAdvance, ascent + descent ) );
1998 xOffset += horizontalAdvance;
2005 context.
painter()->setBrush( Qt::NoBrush );
2008 context.
painter()->rotate( -rotationDegrees );
2009 context.
painter()->translate( -component.origin );
2012void QgsTextRenderer::drawTextInternalHorizontal(
2017 const Component &component,
2023 double rotationDegrees
2030 double targetWidth = 0.0;
2035 targetWidth = documentSize.width();
2041 targetWidth = component.size.width();
2045 double verticalAlignOffset = 0;
2049 const double overallHeight = documentSize.height();
2050 switch ( vAlignment )
2057 verticalAlignOffset = ( component.size.height() - overallHeight ) * 0.5 + metrics.
blockVerticalMargin( -1 );
2061 verticalAlignOffset = ( component.size.height() - overallHeight ) + metrics.
blockVerticalMargin( -1 );
2071 const bool usePathsForText = usePathsToRender( context, format, document );
2075 std::unique_ptr< std::vector< DeferredRenderBlock > > deferredBlocks;
2080 const bool requiresMultiPassRendering
2083 if ( requiresMultiPassRendering )
2085 deferredBlocks = std::make_unique< std::vector< DeferredRenderBlock > >();
2086 deferredBlocks->reserve( document.
size() );
2091 const QVector< BlockMetrics > blockMetrics = calculateBlockMetrics( document, metrics, mode, targetWidth, hAlignment );
2095 renderDocumentBackgrounds( context, document, metrics, component, blockMetrics, mode, verticalAlignOffset, rotationDegrees );
2099 for (
const QgsTextBlock &block : document )
2101 const double blockHeight = metrics.
blockHeight( blockIndex );
2103 DeferredRenderBlock *deferredBlock =
nullptr;
2104 if ( requiresMultiPassRendering && deferredBlocks )
2106 deferredBlocks->emplace_back( DeferredRenderBlock() );
2107 deferredBlock = &deferredBlocks->back();
2108 deferredBlock->fragments.reserve( block.size() );
2111 QgsScopedQPainterState painterState( context.
painter() );
2113 context.
painter()->translate( component.origin );
2115 context.
painter()->rotate( rotationDegrees );
2118 std::unique_ptr< QgsScopedQPainterState > maskPainterState;
2121 maskPainterState = std::make_unique< QgsScopedQPainterState>( maskPainter );
2122 maskPainter->translate( component.origin );
2124 maskPainter->rotate( rotationDegrees );
2127 const BlockMetrics thisBlockMetrics = blockMetrics[blockIndex];
2128 const double baseLineOffset = metrics.
baselineOffset( blockIndex, mode );
2130 const QPointF blockOrigin( thisBlockMetrics.xOffset, baseLineOffset + verticalAlignOffset );
2131 if ( deferredBlock )
2132 deferredBlock->origin = blockOrigin;
2134 context.
painter()->translate( blockOrigin );
2136 maskPainter->translate( blockOrigin );
2138 Component subComponent;
2139 subComponent.block = block;
2140 subComponent.blockIndex = blockIndex;
2141 subComponent.size = QSizeF( thisBlockMetrics.width, blockHeight );
2142 subComponent.offset = QPointF( 0.0, -metrics.
ascentOffset() );
2143 subComponent.rotationRadians = -component.rotationRadians;
2144 subComponent.rotationOffsetRadians = 0.0;
2145 subComponent.extraWordSpacing = thisBlockMetrics.extraWordSpace * fontScale;
2146 subComponent.extraLetterSpacing = thisBlockMetrics.extraLetterSpace * fontScale;
2147 if ( deferredBlock )
2148 deferredBlock->component = subComponent;
2153 QgsTextRenderer::drawMask( context, subComponent, format, metrics, mode );
2157 const bool needsPaths = usePathsForText
2161 std::optional< QgsScopedRenderContextReferenceScaleOverride > referenceScaleOverride;
2166 referenceScaleOverride.emplace( QgsScopedRenderContextReferenceScaleOverride( context, -1.0 ) );
2169 referenceScaleOverride.reset();
2178 context.
painter()->scale( subComponent.dpiRatio, subComponent.dpiRatio );
2180 context.
painter()->scale( 1 / fontScale, 1 / fontScale );
2181 context.
painter()->setPen( Qt::NoPen );
2182 context.
painter()->setBrush( Qt::NoBrush );
2184 renderBlockHorizontal( block, blockIndex, metrics, context, format, context.
painter(), needsPaths, fontScale, thisBlockMetrics.extraWordSpace, thisBlockMetrics.extraLetterSpace, mode, deferredBlock );
2190 if ( deferredBlocks )
2192 renderDeferredBlocks( context, format, components, *deferredBlocks, usePathsForText, fontScale, component, rotationDegrees );
2196void QgsTextRenderer::renderDeferredBlocks(
2200 const std::vector< DeferredRenderBlock > &deferredBlocks,
2201 bool usePathsForText,
2203 const Component &component,
2204 double rotationDegrees
2209 renderDeferredBuffer( context, format, components, deferredBlocks, fontScale, component, rotationDegrees );
2214 renderDeferredShadowForText( context, format, deferredBlocks, fontScale, component, rotationDegrees );
2223 renderDeferredText( context, deferredBlocks, usePathsForText, fontScale, component, rotationDegrees );
2227void QgsTextRenderer::renderDeferredShadowForText(
2228 QgsRenderContext &context,
const QgsTextFormat &format,
const std::vector< DeferredRenderBlock > &deferredBlocks,
double fontScale,
const Component &component,
double rotationDegrees
2231 QgsScopedQPainterState painterState( context.
painter() );
2233 context.
painter()->translate( component.origin );
2235 context.
painter()->rotate( rotationDegrees );
2237 context.
painter()->setPen( Qt::NoPen );
2238 context.
painter()->setBrush( Qt::NoBrush );
2240 for (
const DeferredRenderBlock &block : deferredBlocks )
2242 Component subComponent = block.component;
2244 QPainter painter( &subComponent.picture );
2245 painter.setPen( Qt::NoPen );
2246 painter.setBrush( Qt::NoBrush );
2247 painter.scale( 1 / fontScale, 1 / fontScale );
2249 for (
const DeferredRenderFragment &fragment : std::as_const( block.fragments ) )
2251 if ( !fragment.path.isEmpty() )
2253 painter.setBrush( fragment.color );
2254 painter.drawPath( fragment.path );
2258 painter.setPen( fragment.color );
2259 painter.setFont( fragment.font );
2260 painter.drawText( fragment.point, fragment.
text );
2265 subComponent.pictureBuffer = 1.0;
2266 subComponent.origin = QPointF( 0.0, 0.0 );
2267 const QRectF pictureBoundingRect = subComponent.picture.boundingRect();
2268 subComponent.size = pictureBoundingRect.size();
2269 subComponent.offset = QPointF( -pictureBoundingRect.left(), -pictureBoundingRect.height() - pictureBoundingRect.top() );
2271 context.
painter()->translate( block.origin );
2272 drawShadow( context, subComponent, format );
2273 context.
painter()->translate( -block.origin );
2277void QgsTextRenderer::renderDeferredBuffer(
2281 QgsScopedQPainterState painterState( context.
painter() );
2288 std::unique_ptr< QPicture > bufferPicture;
2289 std::unique_ptr< QPainter > bufferPainter;
2290 QPainter *prevPainter = context.
painter();
2291 if ( needsShadowOnBuffer )
2293 bufferPicture = std::make_unique< QPicture >();
2294 bufferPainter = std::make_unique< QPainter >( bufferPicture.get() );
2298 std::unique_ptr< QgsPaintEffect > tmpEffect;
2302 tmpEffect->begin( context );
2307 QPen pen( bufferColor );
2308 const QgsTextBufferSettings &buffer = format.
buffer();
2311 pen.setWidthF( penSize * fontScale );
2313 context.
painter()->setPen( pen );
2318 bufferColor.setAlpha( 0 );
2320 context.
painter()->setBrush( bufferColor );
2322 context.
painter()->translate( component.origin );
2324 context.
painter()->rotate( rotationDegrees );
2331 for (
const DeferredRenderBlock &block : deferredBlocks )
2333 context.
painter()->translate( block.origin );
2334 context.
painter()->scale( 1 / fontScale, 1 / fontScale );
2335 for (
const DeferredRenderFragment &fragment : std::as_const( block.fragments ) )
2337 context.
painter()->drawPath( fragment.path );
2339 context.
painter()->scale( fontScale, fontScale );
2340 context.
painter()->translate( -block.origin );
2345 tmpEffect->end( context );
2348 if ( needsShadowOnBuffer && bufferPicture )
2350 bufferPainter->end();
2351 bufferPainter.reset();
2354 QgsTextRenderer::Component bufferComponent = component;
2355 bufferComponent.origin = QPointF( 0.0, 0.0 );
2356 bufferComponent.picture = *bufferPicture;
2357 bufferComponent.pictureBuffer = penSize / 2.0;
2358 const QRectF bufferBoundingBox = bufferPicture->boundingRect();
2359 bufferComponent.size = bufferBoundingBox.size();
2360 bufferComponent.offset = QPointF( -bufferBoundingBox.left(), -bufferBoundingBox.height() - bufferBoundingBox.top() );
2362 drawShadow( context, bufferComponent, format );
2371 context.
painter()->scale( component.dpiRatio, component.dpiRatio );
2376void QgsTextRenderer::renderDeferredText(
2377 QgsRenderContext &context,
const std::vector< DeferredRenderBlock > &deferredBlocks,
bool usePathsForText,
double fontScale,
const Component &component,
double rotationDegrees
2380 QgsScopedQPainterState painterState( context.
painter() );
2382 context.
painter()->translate( component.origin );
2384 context.
painter()->rotate( rotationDegrees );
2386 context.
painter()->setPen( Qt::NoPen );
2387 context.
painter()->setBrush( Qt::NoBrush );
2390 for (
const DeferredRenderBlock &block : deferredBlocks )
2392 context.
painter()->translate( block.origin );
2393 context.
painter()->scale( 1 / fontScale, 1 / fontScale );
2395 for (
const DeferredRenderFragment &fragment : std::as_const( block.fragments ) )
2397 if ( usePathsForText )
2399 context.
painter()->setBrush( fragment.color );
2400 context.
painter()->drawPath( fragment.path );
2404 context.
painter()->setPen( fragment.color );
2405 context.
painter()->setFont( fragment.font );
2406 context.
painter()->drawText( fragment.point, fragment.
text );
2410 context.
painter()->scale( fontScale, fontScale );
2411 context.
painter()->translate( -block.origin );
2415void QgsTextRenderer::drawTextInternalVertical(
2420 const QgsTextRenderer::Component &component,
2426 double rotationDegrees
2430 const QStringList textLines = document.
toPlainText();
2432 std::optional< QgsScopedRenderContextReferenceScaleOverride > referenceScaleOverride;
2437 referenceScaleOverride.emplace( QgsScopedRenderContextReferenceScaleOverride( context, -1.0 ) );
2443 referenceScaleOverride.reset();
2446 const double actualTextWidth = documentSize.width();
2447 double textRectWidth = 0.0;
2453 textRectWidth = actualTextWidth;
2459 textRectWidth = component.size.width();
2463 int maxLineLength = 0;
2464 for (
const QString &line : std::as_const( textLines ) )
2466 maxLineLength = std::max( maxLineLength,
static_cast<int>( line.length() ) );
2469 const double actualLabelHeight = documentSize.height();
2474 for (
const QgsTextBlock &block : document )
2476 QgsScopedQPainterState painterState( context.
painter() );
2479 context.
painter()->translate( component.origin );
2481 context.
painter()->rotate( rotationDegrees );
2484 std::unique_ptr<QgsScopedQPainterState > maskPainterState;
2487 maskPainterState = std::make_unique< QgsScopedQPainterState >( maskPainter );
2488 maskPainter->translate( component.origin );
2490 maskPainter->rotate( rotationDegrees );
2497 if ( adjustForAlignment )
2499 double hAlignmentOffset = 0;
2500 switch ( hAlignment )
2503 hAlignmentOffset = ( textRectWidth - actualTextWidth ) * 0.5;
2507 hAlignmentOffset = textRectWidth - actualTextWidth;
2521 xOffset += hAlignmentOffset;
2529 double yOffset = 0.0;
2535 if ( rotationDegrees >= -405 && rotationDegrees < -180 )
2539 else if ( rotationDegrees >= 0 && rotationDegrees < 45 )
2541 xOffset -= actualTextWidth;
2547 yOffset = -actualLabelHeight;
2552 yOffset = -actualLabelHeight;
2562 context.
painter()->translate( QPointF( xOffset, yOffset ) );
2564 double currentBlockYOffset = 0;
2565 int fragmentIndex = 0;
2566 for (
const QgsTextFragment &fragment : block )
2568 QgsScopedQPainterState fragmentPainterState( context.
painter() );
2573 const QFont fragmentFont = metrics.
fragmentFont( blockIndex, fragmentIndex );
2575 QFontMetricsF fragmentMetrics( fragmentFont );
2577 const double letterSpacing = fragmentFont.letterSpacing() / fontScale;
2578 const double labelHeight = fragmentMetrics.ascent() / fontScale + ( fragmentMetrics.ascent() / fontScale + letterSpacing ) * ( line.length() - 1 );
2580 Component subComponent;
2581 subComponent.block = QgsTextBlock( fragment );
2582 subComponent.blockIndex = blockIndex;
2583 subComponent.firstFragmentIndex = fragmentIndex;
2584 subComponent.size = QSizeF( blockMaximumCharacterWidth, labelHeight + fragmentMetrics.descent() / fontScale );
2585 subComponent.offset = QPointF( 0.0, currentBlockYOffset );
2586 subComponent.rotationRadians = -component.rotationRadians;
2587 subComponent.rotationOffsetRadians = 0.0;
2594 QgsTextRenderer::drawMask( context, subComponent, format );
2600 currentBlockYOffset += QgsTextRenderer::drawBuffer( context, subComponent, format, metrics, mode );
2606 path.setFillRule( Qt::WindingFill );
2608 double partYOffset = 0.0;
2609 for (
const QString &part : parts )
2611 double partXOffset = ( blockMaximumCharacterWidth - ( fragmentMetrics.horizontalAdvance( part ) / fontScale - letterSpacing ) ) / 2;
2612 partYOffset += fragmentMetrics.ascent() / fontScale;
2613 path.addText( partXOffset * fontScale, partYOffset * fontScale, fragmentFont, part );
2614 partYOffset += letterSpacing;
2620 textp.begin( &textPict );
2621 textp.setPen( Qt::NoPen );
2624 textp.setBrush( textColor );
2625 textp.scale( 1 / fontScale, 1 / fontScale );
2626 textp.drawPath( path );
2637 subComponent.picture = textPict;
2638 subComponent.pictureBuffer = 0.0;
2639 subComponent.origin = QPointF( 0.0, currentBlockYOffset );
2640 const double prevY = subComponent.offset.y();
2641 subComponent.offset = QPointF( 0, -subComponent.size.height() );
2642 subComponent.useOrigin =
true;
2643 QgsTextRenderer::drawShadow( context, subComponent, format );
2644 subComponent.useOrigin =
false;
2645 subComponent.offset = QPointF( 0, prevY );
2655 context.
painter()->scale( subComponent.dpiRatio, subComponent.dpiRatio );
2659 context.
painter()->translate( 0, currentBlockYOffset );
2661 context.
painter()->drawPicture( 0, 0, textPict );
2662 currentBlockYOffset += partYOffset;
2684 if ( pixelSize < 50 )
2685 return 200 / pixelSize;
2688 else if ( pixelSize > 200 )
2689 return 200 / pixelSize;
@ ForceVector
Always force vector-based rendering, even when the result will be visually different to a raster-base...
TextAnchorPoint
Anchor point of label text.
TextLayoutMode
Text layout modes.
@ Labeling
Labeling-specific layout mode.
@ 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 ...
@ Rectangle
Text within rectangle layout mode.
QFlags< TextRendererFlag > TextRendererFlags
TextOrientation
Text orientations.
@ Vertical
Vertically oriented text.
@ RotationBased
Horizontally or vertically oriented text based on rotation (only available for map labeling).
@ Horizontal
Horizontally oriented text.
@ Round
Use rounded joins.
@ 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.
@ PreferText
Render text as text objects, unless doing so results in rendering artifacts or poor quality rendering...
@ AlwaysOutlines
Always render text using path objects (AKA outlines/curves). This setting guarantees the best quality...
@ AlwaysText
Always render text as text objects. While this mode preserves text objects as text for post-processin...
RenderUnit
Rendering size units.
@ Percentage
Percentage of another measurement (e.g., canvas size, feature size).
@ Unknown
Mixed or unknown units.
@ ApplyScalingWorkaroundForTextRendering
Whether a scaling workaround designed to stablise the rendering of small font sizes (or for painters ...
@ RenderBlocking
Render and load remote sources in the same thread to ensure rendering remote sources (svg and images)...
TextVerticalAlignment
Text vertical alignment.
@ VerticalCenter
Center align.
QFlags< TextComponent > TextComponents
Text components.
TextHorizontalAlignment
Text horizontal alignment.
@ WrapLines
Automatically wrap long lines of text.
QFlags< CurvedTextFlag > CurvedTextFlags
Flags controlling behavior of curved text generation.
TextComponent
Text components.
@ Buffer
Buffer component.
@ Background
Background shape.
static QgsImageCache * imageCache()
Returns the application's image cache, used for caching resampled versions of raster images.
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.
QgsFillSymbol * clone() const override
Returns a deep copy of this symbol.
Does vector analysis using the GEOS library and handles import, export, and exception handling.
QSize originalSize(const QString &path, bool blocking=false) const
Returns the original size (in pixels) of the image at the specified path.
QImage pathAsImage(const QString &path, const QSize size, const bool keepAspectRatio, const double opacity, bool &fitsInCache, bool blocking=false, double targetDpi=96, int frameNumber=-1, bool *isMissing=nullptr)
Returns the specified path rendered as an image.
Line string geometry type, with support for z-dimension and m-values.
static std::unique_ptr< 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.
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.
static void applyScaleFixForQPictureDpi(QPainter *painter)
Applies a workaround to a painter to avoid an issue with incorrect scaling when drawing QPictures.
static void drawPicture(QPainter *painter, const QPointF &point, const QPicture &picture)
Draws a picture onto a painter, correctly applying workarounds to avoid issues with incorrect scaling...
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 rendered at a later stage.
void setGraphemeFormats(const QVector< QgsTextCharacterFormat > &formats)
Sets the character formats associated with the text graphemes().
bool hasActiveProperties() const final
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.
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().
Qgis::RasterizedRenderingPolicy rasterizedRenderingPolicy() const
Returns the policy controlling when rasterisation of content during renders is permitted.
void setPainter(QPainter *p)
Sets the destination QPainter for the render operation.
Qgis::RenderContextFlags flags() const
Returns combination of flags used for rendering.
static QString substituteVerticalCharacters(QString string)
Returns a string with characters having vertical representation form substituted.
static std::unique_ptr< 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.
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.
QString toPlainText() const
Converts the block to plain text.
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...
QString backgroundImagePath() const
Returns the path for the image to be used for rendering the background of the fragment.
QBrush backgroundBrush() const
Returns the brush used for rendering the background of the fragment.
QString imagePath() const
Returns the path to the image to render, if the format applies to a document image fragment.
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.
bool hasBackground() const
Returns true if the fragment has a background set.
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.
double fragmentDescent(int blockIndex, int fragmentIndex, Qgis::TextLayoutMode mode) const
Returns the descent of the fragment at the specified block and fragment index.
QSizeF documentSize(Qgis::TextLayoutMode mode, Qgis::TextOrientation orientation) const
Returns the overall size of the document.
double blockRightMargin(int blockIndex) const
Returns the margin for the right side of the specified block index.
static QgsTextDocumentMetrics calculateMetrics(const QgsTextDocument &document, const QgsTextFormat &format, const QgsRenderContext &context, double scaleFactor=1.0, const QgsTextDocumentRenderContext &documentContext=QgsTextDocumentRenderContext())
Returns precalculated text metrics for a text document, when rendered using the given base format and...
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.
double fragmentFixedHeight(int blockIndex, int fragmentIndex, Qgis::TextLayoutMode mode) const
Returns the fixed height of the fragment at the specified block and fragment index,...
double blockLeftMargin(int blockIndex) const
Returns the margin for the left side of the specified block index.
double blockMaximumAscent(int blockIndex) const
Returns the maximum ascent encountered in the specified block.
double fragmentAscent(int blockIndex, int fragmentIndex, Qgis::TextLayoutMode mode) const
Returns the ascent of the fragment at the specified block and fragment index.
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.
const QgsTextDocument & document() const
Returns the document associated with the calculated metrics.
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.
double blockVerticalMargin(int blockIndex) const
Returns the vertical margin for the specified block index.
Encapsulates the context in which a text document is to be rendered.
void setFlags(Qgis::TextRendererFlags flags)
Sets associated text renderer flags.
void setMaximumWidth(double width)
Sets the maximum width (in painter units) for rendered text.
Represents a document consisting of one or more QgsTextBlock objects.
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.
void append(const QgsTextBlock &block)
Appends a block to the document.
static QgsTextDocument fromTextAndFormat(const QStringList &lines, const QgsTextFormat &format)
Constructor for QgsTextDocument consisting of a set of lines, respecting settings from a text format.
void applyCapitalization(Qgis::Capitalization capitalization)
Applies a capitalization style to the document's text.
bool hasBackgrounds() const
Returns true if any blocks or fragments in the document have background brushes set.
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.
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.
QFont font() const
Returns the font used for rendering text.
QgsTextBufferSettings & buffer()
Returns a reference to the text buffer settings.
Stores a fragment of document 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.
bool isImage() const
Returns true if the fragment represents an image.
bool isWhitespace() const
Returns true if the fragment consists of just whitespace characters, and does not contain any content...
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 ...
static std::unique_ptr< CurvePlacementProperties > generateCurvedTextPlacement(const QgsPrecalculatedTextMetrics &metrics, const QPolygonF &line, double offsetAlongLine, LabelLineDirection direction=RespectPainterOrientation, double maxConcaveAngle=-1, double maxConvexAngle=-1, Qgis::CurvedTextFlags flags=Qgis::CurvedTextFlags(), Qgis::TextAnchorPoint textAnchor=Qgis::TextAnchorPoint::StartOfText)
Calculates curved text placement properties.
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 double calculateScaleFactorForFormat(const QgsRenderContext &context, const QgsTextFormat &format)
Returns the scale factor used for upscaling font sizes and downscaling destination painter devices.
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 void drawTextOnLine(const QPolygonF &line, const QString &text, QgsRenderContext &context, const QgsTextFormat &format, double offsetAlongLine=0, double offsetFromLine=0, Qgis::CurvedTextFlags flags=Qgis::CurvedTextFlag::UseBaselinePlacement|Qgis::CurvedTextFlag::TruncateStringWhenLineIsTooShort, Qgis::TextAnchorPoint textAnchor=Qgis::TextAnchorPoint::StartOfText)
Draws text along a line using the specified settings.
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 Qgis::TextHorizontalAlignment convertQtHAlignment(Qt::Alignment alignment)
Converts a Qt horizontal alignment flag to a Qgis::TextHorizontalAlignment value.
static void drawDocumentOnLine(const QPolygonF &line, const QgsTextFormat &format, const QgsTextDocument &document, QgsRenderContext &context, double offsetAlongLine=0, double offsetFromLine=0, Qgis::CurvedTextFlags flags=Qgis::CurvedTextFlag::UseBaselinePlacement|Qgis::CurvedTextFlag::TruncateStringWhenLineIsTooShort, Qgis::TextAnchorPoint textAnchor=Qgis::TextAnchorPoint::StartOfText)
Draws a text document along a line using the specified settings.
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
#define BUILTIN_UNREACHABLE
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
QList< QgsSymbolLayer * > QgsSymbolLayerList