40#include <QTextBoundaryFinder>
44 if ( alignment & Qt::AlignLeft )
46 else if ( alignment & Qt::AlignRight )
48 else if ( alignment & Qt::AlignHCenter )
50 else if ( alignment & Qt::AlignJustify )
59 if ( alignment & Qt::AlignTop )
61 else if ( alignment & Qt::AlignBottom )
63 else if ( alignment & Qt::AlignVCenter )
66 else if ( alignment & Qt::AlignBaseline )
74 return static_cast< int >(
c.convertToPainterUnits( size, unit, mapUnitScale ) + 0.5 );
94 drawDocument( rect, lFormat, metrics.
document(), metrics, context, alignment, vAlignment, rotation, mode, flags );
99 const QgsTextFormat tmpFormat = updateShadowPosition( format );
117 drawParts( rect, rotation, horizontalAlignment, verticalAlignment, document, metrics, context, tmpFormat, components, mode );
125 lFormat = updateShadowPosition( lFormat );
137 const QgsTextFormat lFormat = updateShadowPosition( _format );
156 drawParts( point, rotation, alignment, document, metrics, context, lFormat, components, mode );
164 lFormat = updateShadowPosition( lFormat );
171 drawDocumentOnLine( line, lFormat, document, context, offsetAlongLine, offsetFromLine, flags );
177 QPolygonF labelBaselineCurve = line;
186#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<11
187 if ( offsetFromLine < 0 )
190 std::unique_ptr < QgsLineString > reversed( offsetCurve->reversed() );
194 offsetCurve = std::move( reversed );
198 labelBaselineCurve = offsetCurve->asQPolygonF();
203 const QFont baseFont = format.
scaledFont( context, fontScale );
204 const double letterSpacing = baseFont.letterSpacing() / fontScale;
205 const double wordSpacing = baseFont.wordSpacing() / fontScale;
207 QStringList graphemes;
208 QVector< QgsTextCharacterFormat > graphemeFormats;
209 QVector< QgsTextDocumentMetrics > graphemeMetrics;
211 for (
const QgsTextBlock &block : std::as_const( document ) )
216 for (
const QString &grapheme : fragmentGraphemes )
218 graphemes.append( grapheme );
219 graphemeFormats.append( fragment.characterFormat() );
229 QVector< double > characterWidths( graphemes.count() );
230 QVector< double > characterHeights( graphemes.count() );
231 QVector< double > characterDescents( graphemes.count() );
232 QFont previousNonSuperSubScriptFont;
234 for (
int i = 0; i < graphemes.count(); i++ )
239 double graphemeFirstCharHorizontalAdvanceWithLetterSpacing = 0;
240 double graphemeFirstCharHorizontalAdvance = 0;
241 double graphemeHorizontalAdvance = 0;
242 double characterDescent = 0;
243 double characterHeight = 0;
246 QFont graphemeFont = baseFont;
250 previousNonSuperSubScriptFont = graphemeFont;
257 previousNonSuperSubScriptFont = graphemeFont;
278 previousNonSuperSubScriptFont = graphemeFont;
281 const QFontMetricsF graphemeFontMetrics( graphemeFont );
282 graphemeFirstCharHorizontalAdvance = graphemeFontMetrics.horizontalAdvance( QString( graphemes[i].at( 0 ) ) ) / fontScale;
283 graphemeFirstCharHorizontalAdvanceWithLetterSpacing = graphemeFontMetrics.horizontalAdvance( graphemes[i].at( 0 ) ) / fontScale + letterSpacing;
284 graphemeHorizontalAdvance = graphemeFontMetrics.horizontalAdvance( QString( graphemes[i] ) ) / fontScale;
285 characterDescent = graphemeFontMetrics.descent() / fontScale;
286 characterHeight = graphemeFontMetrics.height() / fontScale;
288 qreal wordSpaceFix = qreal( 0.0 );
289 if ( graphemes[i] == QLatin1String(
" " ) )
293 wordSpaceFix = ( nxt < graphemes.count() && graphemes[nxt] != QLatin1String(
" " ) ) ? wordSpacing : qreal( 0.0 );
298 if ( graphemes[i].length() == 1 &&
299 !
qgsDoubleNear( graphemeFirstCharHorizontalAdvance, graphemeFirstCharHorizontalAdvanceWithLetterSpacing ) )
302 wordSpaceFix -= wordSpacing;
305 const double charWidth = graphemeHorizontalAdvance + wordSpaceFix;
306 characterWidths[i] = charWidth;
307 characterHeights[i] = characterHeight;
308 characterDescents[i] = characterDescent;
311 QgsPrecalculatedTextMetrics metrics( graphemes, std::move( characterWidths ), std::move( characterHeights ), std::move( characterDescents ) );
315 metrics, labelBaselineCurve, offsetAlongLine,
321 if ( placement->graphemePlacement.empty() )
328 QHash< int, QgsTextRenderer::Component > components;
329 components.reserve( placement->graphemePlacement.size() );
332 QgsTextRenderer::Component component;
333 component.origin = QPointF( grapheme.x, grapheme.y );
334 component.rotation = -grapheme.angle;
340 component.origin.rx() += verticalOffset * std::cos( grapheme.angle + M_PI_2 );
341 component.origin.ry() += verticalOffset * std::sin( grapheme.angle + M_PI_2 );
344 components.insert( grapheme.graphemeIndex, component );
352 const QgsTextRenderer::Component &component = components[grapheme.graphemeIndex ];
362 const QgsTextRenderer::Component &component = components[grapheme.graphemeIndex ];
379 const QgsTextRenderer::Component &component = components[grapheme.graphemeIndex ];
432 component.dpiRatio = 1.0;
433 component.origin = rect.topLeft();
434 component.rotation = rotation;
435 component.size = rect.size();
436 component.hAlign = alignment;
444 double xc = rect.width() / 2.0;
445 double yc = rect.height() / 2.0;
447 double angle = -rotation;
448 double xd = xc * std::cos( angle ) - yc * std::sin( angle );
449 double yd = xc * std::sin( angle ) + yc * std::cos( angle );
451 component.center = QPointF( component.origin.x() + xd, component.origin.y() + yd );
455 component.center = rect.center();
458 switch ( vAlignment )
480 drawTextInternal( parts, context, format, component,
482 alignment, vAlignment, mode );
503 component.dpiRatio = 1.0;
504 component.origin = origin;
505 component.rotation = rotation;
506 component.hAlign = alignment;
510 QgsTextRenderer::drawBackground( context, component, format, metrics, mode );
520 drawTextInternal( parts, context, format, component,
530 return QFontMetricsF( format.
scaledFont( context, scaleFactor ), context.
painter() ? context.
painter()->device() :
nullptr );
537 QPainter *p = context.
painter();
542 if ( component.rotation >= -315 && component.rotation < -90 )
546 else if ( component.rotation >= -90 && component.rotation < -45 )
556 QgsTextBufferSettings buffer = format.
buffer();
564 std::optional< QgsScopedRenderContextReferenceScaleOverride > referenceScaleOverride;
569 referenceScaleOverride.emplace( QgsScopedRenderContextReferenceScaleOverride( context, -1.0 ) );
575 referenceScaleOverride.reset();
578 path.setFillRule( Qt::WindingFill );
580 double height = component.size.height();
581 switch ( orientation )
592 double partYOffset = component.offset.y() * scaleFactor;
595 double partLastDescent = 0;
597 int fragmentIndex = 0;
598 for (
const QgsTextFragment &fragment : component.block )
600 const QFont fragmentFont = metrics.
fragmentFont( component.blockIndex, component.firstFragmentIndex + fragmentIndex );
601 const double letterSpacing = fragmentFont.letterSpacing() / scaleFactor;
603 const QFontMetricsF fragmentMetrics( fragmentFont );
605 const double fragmentYOffset = metrics.
fragmentVerticalOffset( component.blockIndex, fragmentIndex, mode );
608 for (
const QString &part : parts )
610 double partXOffset = ( blockMaximumCharacterWidth - ( fragmentMetrics.horizontalAdvance( part ) / scaleFactor - letterSpacing ) ) / 2;
611 partYOffset += fragmentMetrics.ascent() / scaleFactor;
612 path.addText( partXOffset, partYOffset + fragmentYOffset, fragmentFont, part );
613 partYOffset += letterSpacing;
615 partLastDescent = fragmentMetrics.descent() / scaleFactor;
619 height = partYOffset + partLastDescent;
620 advance = partYOffset - component.offset.y() * scaleFactor;
625 QColor bufferColor = buffer.
color();
626 bufferColor.setAlphaF( buffer.
opacity() );
627 QPen pen( bufferColor );
628 pen.setWidthF( penSize * scaleFactor );
630 QColor tmpColor( bufferColor );
634 tmpColor.setAlpha( 0 );
640 buffp.begin( &buffPict );
644 std::unique_ptr< QgsPaintEffect > tmpEffect( buffer.
paintEffect()->
clone() );
646 tmpEffect->begin( context );
647 context.
painter()->setPen( pen );
648 context.
painter()->setBrush( tmpColor );
649 if ( scaleFactor != 1.0 )
650 context.
painter()->scale( 1 / scaleFactor, 1 / scaleFactor );
651 context.
painter()->drawPath( path );
652 if ( scaleFactor != 1.0 )
653 context.
painter()->scale( scaleFactor, scaleFactor );
654 tmpEffect->end( context );
660 if ( scaleFactor != 1.0 )
661 buffp.scale( 1 / scaleFactor, 1 / scaleFactor );
663 buffp.setBrush( tmpColor );
664 buffp.drawPath( path );
670 QgsTextRenderer::Component bufferComponent = component;
671 bufferComponent.origin = QPointF( 0.0, 0.0 );
672 bufferComponent.picture = buffPict;
673 bufferComponent.pictureBuffer = penSize / 2.0;
674 bufferComponent.size.setHeight( height );
678 bufferComponent.offset.setY( - bufferComponent.size.height() );
680 drawShadow( context, bufferComponent, format );
683 QgsScopedQPainterState painterState( p );
688 p->setCompositionMode( buffer.
blendMode() );
692 p->scale( component.dpiRatio, component.dpiRatio );
694 p->drawPicture( 0, 0, buffPict );
696 return advance / scaleFactor;
702 QgsTextMaskSettings mask = format.
mask();
716 path.setFillRule( Qt::WindingFill );
723 std::optional< QgsScopedRenderContextReferenceScaleOverride > referenceScaleOverride;
728 referenceScaleOverride.emplace( QgsScopedRenderContextReferenceScaleOverride( context, -1.0 ) );
734 referenceScaleOverride.reset();
737 int fragmentIndex = 0;
738 for (
const QgsTextFragment &fragment : component.block )
742 const QFont fragmentFont = metrics.
fragmentFont( component.blockIndex, fragmentIndex );
744 const double fragmentYOffset = metrics.
fragmentVerticalOffset( component.blockIndex, fragmentIndex, mode );
745 path.addText( xOffset, fragmentYOffset, fragmentFont, fragment.
text() );
752 QColor bufferColor( Qt::gray );
753 bufferColor.setAlphaF( mask.
opacity() );
757 brush.setColor( bufferColor );
758 pen.setColor( bufferColor );
759 pen.setWidthF( penSize * scaleFactor );
762 QgsScopedQPainterState painterState( p );
766 p->scale( component.dpiRatio, component.dpiRatio );
769 QgsPainterSwapper swapper( context, p );
771 QgsEffectPainter effectPainter( context, mask.
paintEffect() );
772 if ( scaleFactor != 1.0 )
773 context.
painter()->scale( 1 / scaleFactor, 1 / scaleFactor );
774 context.
painter()->setPen( pen );
775 context.
painter()->setBrush( brush );
776 context.
painter()->drawPath( path );
777 if ( scaleFactor != 1.0 )
778 context.
painter()->scale( scaleFactor, scaleFactor );
783 if ( scaleFactor != 1.0 )
784 p->scale( 1 / scaleFactor, 1 / scaleFactor );
786 p->setBrush( brush );
788 if ( scaleFactor != 1.0 )
789 p->scale( scaleFactor, scaleFactor );
797 if ( doc.
size() == 0 )
800 return textWidth( context, format, doc );
817 for (
const QString &line : textLines )
821 lines.append(
wrappedText( context, line, maxLineWidth, format ) );
825 lines.append( line );
830 return textHeight( context, format, doc, mode );
837 bool isNullSize =
false;
838 const QFont baseFont = format.
scaledFont( context, scaleFactor, &isNullSize );
842 const QFontMetrics fm( baseFont );
843 const double height = ( character.isNull() ? fm.height() : fm.boundingRect( character ).height() ) / scaleFactor;
845 if ( !includeEffects )
848 double maxExtension = 0;
877 return height + maxExtension;
885 const QStringList multiLineSplit = text.split(
'\n' );
887 return currentTextWidth > width;
892 const QStringList lines = text.split(
'\n' );
893 QStringList outLines;
894 for (
const QString &line : lines )
899 const QStringList words = line.split(
' ' );
900 QStringList linesToProcess;
901 QString wordsInCurrentLine;
902 for (
const QString &word : words )
907 if ( !wordsInCurrentLine.isEmpty() )
908 linesToProcess << wordsInCurrentLine;
909 wordsInCurrentLine.clear();
910 linesToProcess << word;
914 if ( !wordsInCurrentLine.isEmpty() )
915 wordsInCurrentLine.append(
' ' );
916 wordsInCurrentLine.append( word );
919 if ( !wordsInCurrentLine.isEmpty() )
920 linesToProcess << wordsInCurrentLine;
922 for (
const QString &line : std::as_const( linesToProcess ) )
924 QString remainingText = line;
925 int lastPos = remainingText.lastIndexOf(
' ' );
926 while ( lastPos > -1 )
936 outLines << remainingText.left( lastPos );
937 remainingText = remainingText.mid( lastPos + 1 );
940 lastPos = remainingText.lastIndexOf(
' ', lastPos - 1 );
942 outLines << remainingText;
971 Component component =
c;
972 QgsTextBackgroundSettings background = format.
background();
974 QPainter *prevP = context.
painter();
975 QPainter *p = context.
painter();
976 std::unique_ptr< QgsPaintEffect > tmpEffect;
980 tmpEffect->begin( context );
989 const double originAdjustRotationRadians = -component.rotation;
992 component.rotation = -( component.rotation * 180 / M_PI );
993 component.rotationOffset =
998 component.rotation = 0.0;
999 component.rotationOffset = background.
rotation();
1008 double width = documentSize.width();
1009 double height = documentSize.height();
1016 switch ( component.hAlign )
1020 component.center = QPointF( component.origin.x() + width / 2.0,
1021 component.origin.y() + height / 2.0 );
1025 component.center = QPointF( component.origin.x() + component.size.width() / 2.0,
1026 component.origin.y() + height / 2.0 );
1030 component.center = QPointF( component.origin.x() + component.size.width() - width / 2.0,
1031 component.origin.y() + height / 2.0 );
1038 bool isNullSize =
false;
1039 QFontMetricsF fm( format.
scaledFont( context, scaleFactor, &isNullSize ) );
1040 double originAdjust = isNullSize ? 0 : ( fm.ascent() / scaleFactor / 2.0 - fm.leading() / scaleFactor / 2.0 );
1041 switch ( component.hAlign )
1045 component.center = QPointF( component.origin.x() + width / 2.0,
1046 component.origin.y() - height / 2.0 + originAdjust );
1050 component.center = QPointF( component.origin.x(),
1051 component.origin.y() - height / 2.0 + originAdjust );
1055 component.center = QPointF( component.origin.x() - width / 2.0,
1056 component.origin.y() - height / 2.0 + originAdjust );
1063 const double dx = component.center.x() - component.origin.x();
1064 const double dy = component.center.y() - component.origin.y();
1065 component.center.setX( component.origin.x() + ( std::cos( originAdjustRotationRadians ) * dx - std::sin( originAdjustRotationRadians ) * dy ) );
1066 component.center.setY( component.origin.y() + ( std::sin( originAdjustRotationRadians ) * dx + std::cos( originAdjustRotationRadians ) * dy ) );
1076 component.size = QSizeF( width, height );
1081 switch ( background.
type() )
1094 double sizeOut = 0.0;
1096 QgsScopedRenderContextReferenceScaleOverride referenceScaleOverride( context, -1 );
1105 sizeOut = std::max( component.size.width(), component.size.height() );
1109 sizeOut += bufferSize * 2;
1115 if ( sizeOut < 1.0 )
1118 std::unique_ptr< QgsMarkerSymbol > renderedSymbol;
1122 map[QStringLiteral(
"name" )] = background.
svgFile().trimmed();
1123 map[QStringLiteral(
"size" )] = QString::number( sizeOut );
1125 map[QStringLiteral(
"angle" )] = QString::number( 0.0 );
1133 map[QStringLiteral(
"fill" )] = background.
fillColor().name();
1134 map[QStringLiteral(
"outline" )] = background.
strokeColor().name();
1135 map[QStringLiteral(
"outline-width" )] = QString::number( background.
strokeWidth() );
1140 QgsTextShadowSettings shadow = format.
shadow();
1142 QVariantMap shdwmap( map );
1143 shdwmap[QStringLiteral(
"fill" )] = shadow.
color().name();
1144 shdwmap[QStringLiteral(
"outline" )] = shadow.
color().name();
1145 shdwmap[QStringLiteral(
"size" )] = QString::number( sizeOut );
1150 svgp.begin( &svgPict );
1158 QgsRenderContext shdwContext;
1164 QgsSvgMarkerSymbolLayer *svgShdwM =
static_cast<QgsSvgMarkerSymbolLayer *
>( symShdwL.get() );
1167 svgShdwM->
renderPoint( QPointF( sizeOut / 2, -sizeOut / 2 ), svgShdwContext );
1170 component.picture = svgPict;
1172 component.pictureBuffer = 0.0;
1174 component.size = QSizeF( sizeOut, sizeOut );
1175 component.offset = QPointF( 0.0, 0.0 );
1178 QgsScopedQPainterState painterState( p );
1181 p->translate( component.center.x(), component.center.y() );
1182 p->rotate( component.rotation );
1185 p->translate( QPointF( xoff, yoff ) );
1186 p->rotate( component.rotationOffset );
1187 p->translate( -sizeOut / 2, sizeOut / 2 );
1189 drawShadow( context, component, format );
1191 renderedSymbol.reset( );
1199 renderedSymbol->setSize( sizeOut );
1203 renderedSymbol->setOpacity( renderedSymbol->opacity() * background.
opacity() );
1206 QgsScopedQPainterState painterState( p );
1211 p->setCompositionMode( background.
blendMode() );
1213 p->translate( component.center.x(), component.center.y() );
1214 p->rotate( component.rotation );
1217 p->translate( QPointF( xoff, yoff ) );
1218 p->rotate( component.rotationOffset );
1222 renderedSymbol->renderPoint( QPointF( 0, 0 ), &f, context );
1223 renderedSymbol->stopRender( context );
1224 p->setCompositionMode( QPainter::CompositionMode_SourceOver );
1234 double w = component.size.width();
1235 double h = component.size.height();
1256 h = std::sqrt( std::pow( w, 2 ) + std::pow( h, 2 ) );
1262 h = h * M_SQRT1_2 * 2;
1263 w = w * M_SQRT1_2 * 2;
1271 w += bufferWidth * 2;
1272 h += bufferHeight * 2;
1276 QRectF rect( -w / 2.0, - h / 2.0, w, h );
1278 if ( rect.isNull() )
1281 QgsScopedQPainterState painterState( p );
1284 p->translate( QPointF( component.center.x(), component.center.y() ) );
1285 p->rotate( component.rotation );
1288 p->translate( QPointF( xoff, yoff ) );
1289 p->rotate( component.rotationOffset );
1295 QTransform t = QTransform::fromScale( 10, 10 );
1297 QTransform ti = t.inverted();
1304 path.addRoundedRect( rect, background.
radii().width(), background.
radii().height(), Qt::RelativeSize );
1310 path.addRoundedRect( rect, xRadius, yRadius );
1316 path.addEllipse( rect );
1318 QPolygonF tempPolygon = path.toFillPolygon( t );
1319 QPolygonF polygon = ti.map( tempPolygon );
1321 QPainter *oldp = context.
painter();
1324 shapep.begin( &shapePict );
1327 std::unique_ptr< QgsFillSymbol > renderedSymbol;
1329 renderedSymbol->setOpacity( renderedSymbol->opacity() * background.
opacity() );
1333 renderedSymbol->renderPolygon( polygon,
nullptr, &f, context );
1334 renderedSymbol->stopRender( context );
1341 component.picture = shapePict;
1344 component.size = rect.size();
1345 component.offset = QPointF( rect.width() / 2, -rect.height() / 2 );
1346 drawShadow( context, component, format );
1351 p->setCompositionMode( background.
blendMode() );
1355 p->scale( component.dpiRatio, component.dpiRatio );
1357 p->drawPicture( 0, 0, shapePict );
1358 p->setCompositionMode( QPainter::CompositionMode_SourceOver );
1365 tmpEffect->end( context );
1372 QgsTextShadowSettings shadow = format.
shadow();
1374 QPainter *p = context.
painter();
1375 const double componentWidth = component.size.width();
1376 const double componentHeight = component.size.height();
1377 const double xOffset = component.offset.x();
1378 const double yOffset = component.offset.y();
1379 double pictbuffer = component.pictureBuffer;
1388 radius /= ( mapUnits ? context.
scaleFactor() / component.dpiRatio : 1 );
1389 radius =
static_cast< int >( radius + 0.5 );
1393 double blurBufferClippingScale = 3.75;
1394 int blurbuffer = ( radius > 17 ? 16 : radius ) * blurBufferClippingScale;
1396 QImage blurImg( componentWidth + ( pictbuffer * 2.0 ) + ( blurbuffer * 2.0 ),
1397 componentHeight + ( pictbuffer * 2.0 ) + ( blurbuffer * 2.0 ),
1398 QImage::Format_ARGB32_Premultiplied );
1402 int minBlurImgSize = 1;
1406 int maxBlurImgSize = 40000;
1407 if ( blurImg.isNull()
1408 || ( blurImg.width() < minBlurImgSize || blurImg.height() < minBlurImgSize )
1409 || ( blurImg.width() > maxBlurImgSize || blurImg.height() > maxBlurImgSize ) )
1412 blurImg.fill( QColor( Qt::transparent ).rgba() );
1414 if ( !pictp.begin( &blurImg ) )
1416 pictp.setRenderHints( QPainter::Antialiasing | QPainter::SmoothPixmapTransform );
1417 QPointF imgOffset( blurbuffer + pictbuffer + xOffset,
1418 blurbuffer + pictbuffer + componentHeight + yOffset );
1420 pictp.drawPicture( imgOffset,
1421 component.picture );
1424 pictp.setCompositionMode( QPainter::CompositionMode_SourceIn );
1425 pictp.fillRect( blurImg.rect(), shadow.
color() );
1429 if ( shadow.
blurRadius() > 0.0 && radius > 0 )
1437 picti.begin( &blurImg );
1438 picti.setBrush( Qt::Dense7Pattern );
1439 QPen imgPen( QColor( 0, 0, 255, 255 ) );
1440 imgPen.setWidth( 1 );
1441 picti.setPen( imgPen );
1442 picti.setOpacity( 0.1 );
1443 picti.drawRect( 0, 0, blurImg.width(), blurImg.height() );
1450 double angleRad = shadow.
offsetAngle() * M_PI / 180;
1458 angleRad -= ( component.rotation * M_PI / 180 + component.rotationOffset * M_PI / 180 );
1461 QPointF transPt( -offsetDist * std::cos( angleRad + M_PI_2 ),
1462 -offsetDist * std::sin( angleRad + M_PI_2 ) );
1468 p->setRenderHint( QPainter::SmoothPixmapTransform );
1471 p->setCompositionMode( shadow.
blendMode() );
1473 p->setOpacity( shadow.
opacity() );
1475 double scale = shadow.
scale() / 100.0;
1477 p->scale( scale, scale );
1478 if ( component.useOrigin )
1480 p->translate( component.origin.x(), component.origin.y() );
1482 p->translate( transPt );
1483 p->translate( -imgOffset.x(),
1485 p->drawImage( 0, 0, blurImg );
1492 p->setBrush( Qt::NoBrush );
1493 QPen imgPen( QColor( 255, 0, 0, 10 ) );
1494 imgPen.setWidth( 2 );
1495 imgPen.setStyle( Qt::DashLine );
1496 p->setPen( imgPen );
1497 p->scale( scale, scale );
1498 if ( component.useOrigin() )
1500 p->translate( component.origin().x(), component.origin().y() );
1502 p->translate( transPt );
1503 p->translate( -imgOffset.x(),
1505 p->drawRect( 0, 0, blurImg.width(), blurImg.height() );
1510 p->setBrush( Qt::NoBrush );
1511 QPen componentRectPen( QColor( 0, 255, 0, 70 ) );
1512 componentRectPen.setWidth( 1 );
1513 if ( component.useOrigin() )
1515 p->translate( component.origin().x(), component.origin().y() );
1517 p->setPen( componentRectPen );
1518 p->drawRect( QRect( -xOffset, -componentHeight - yOffset, componentWidth, componentHeight ) );
1527 const Component &component,
1539 std::optional< QgsScopedRenderContextReferenceScaleOverride > referenceScaleOverride;
1544 referenceScaleOverride.emplace( QgsScopedRenderContextReferenceScaleOverride( context, -1.0 ) );
1550 referenceScaleOverride.reset();
1552 double rotation = 0;
1553 const Qgis::TextOrientation orientation = calculateRotationAndOrientationForComponent( format, component, rotation );
1554 switch ( orientation )
1558 drawTextInternalHorizontal( context, format, components, mode, component, document, metrics, fontScale, alignment, vAlignment, rotation );
1567 drawTextInternalVertical( context, format,
Qgis::TextComponent::Buffer, mode, component, document, metrics, fontScale, alignment, vAlignment, rotation );
1569 drawTextInternalVertical( context, format,
Qgis::TextComponent::Text, mode, component, document, metrics, fontScale, alignment, vAlignment, rotation );
1575Qgis::TextOrientation QgsTextRenderer::calculateRotationAndOrientationForComponent(
const QgsTextFormat &format,
const QgsTextRenderer::Component &component,
double &rotation )
1577 rotation = -component.rotation * 180 / M_PI;
1584 if ( rotation >= -315 && rotation < -90 )
1589 else if ( rotation >= -90 && rotation < -45 )
1605void QgsTextRenderer::calculateExtraSpacingForLineJustification(
const double spaceToDistribute,
const QgsTextBlock &block,
double &extraWordSpace,
double &extraLetterSpace )
1608 QTextBoundaryFinder
finder( QTextBoundaryFinder::Word, blockText );
1610 int wordBoundaries = 0;
1611 while (
finder.toNextBoundary() != -1 )
1613 if (
finder.boundaryReasons() & QTextBoundaryFinder::StartOfItem )
1617 if ( wordBoundaries > 0 )
1620 extraWordSpace = spaceToDistribute / wordBoundaries;
1625 QTextBoundaryFinder
finder( QTextBoundaryFinder::Grapheme, blockText );
1628 int graphemeBoundaries = 0;
1629 while (
finder.toNextBoundary() != -1 )
1631 if (
finder.boundaryReasons() & QTextBoundaryFinder::StartOfItem )
1632 graphemeBoundaries++;
1635 if ( graphemeBoundaries > 0 )
1637 extraLetterSpace = spaceToDistribute / graphemeBoundaries;
1642void QgsTextRenderer::applyExtraSpacingForLineJustification( QFont &font,
double extraWordSpace,
double extraLetterSpace )
1644 const double prevWordSpace = font.wordSpacing();
1645 font.setWordSpacing( prevWordSpace + extraWordSpace );
1646 const double prevLetterSpace = font.letterSpacing();
1647 font.setLetterSpacing( QFont::AbsoluteSpacing, prevLetterSpace + extraLetterSpace );
1651void QgsTextRenderer::renderBlockHorizontal(
const QgsTextBlock &block,
int blockIndex,
1654 QPainter *painter,
bool forceRenderAsPaths,
1655 double fontScale,
double extraWordSpace,
double extraLetterSpace,
1661 int fragmentIndex = 0;
1662 for (
const QgsTextFragment &fragment : block )
1667 QFont fragmentFont = metrics.
fragmentFont( blockIndex, fragmentIndex );
1670 applyExtraSpacingForLineJustification( fragmentFont, extraWordSpace * fontScale, extraLetterSpace * fontScale );
1677 if ( deferredRenderBlock )
1679 DeferredRenderFragment renderFragment;
1680 renderFragment.color = textColor;
1681 if ( forceRenderAsPaths )
1683 renderFragment.path.setFillRule( Qt::WindingFill );
1684 renderFragment.path.addText( xOffset, yOffset, fragmentFont, fragment.
text() );
1686 renderFragment.font = fragmentFont;
1687 renderFragment.point = QPointF( xOffset, yOffset );
1688 renderFragment.text = fragment.
text();
1689 deferredRenderBlock->fragments.append( renderFragment );
1691 else if ( forceRenderAsPaths )
1693 painter->setBrush( textColor );
1695 path.setFillRule( Qt::WindingFill );
1696 path.addText( xOffset, yOffset, fragmentFont, fragment.
text() );
1697 painter->drawPath( path );
1701 painter->setPen( textColor );
1702 painter->setFont( fragmentFont );
1703 painter->drawText( QPointF( xOffset, yOffset ), fragment.
text() );
1706 else if ( fragment.
isImage() )
1708 bool fitsInCache =
false;
1710 const double imageHeight = metrics.
fragmentFixedHeight( blockIndex, fragmentIndex, mode ) * fontScale;
1713 QSize(
static_cast< int >( std::round( imageWidth ) ),
1714 static_cast< int >( std::round( imageHeight ) ) ),
1718 const double yOffset = imageBaseline - image.height();
1719 if ( !image.isNull() )
1720 painter->drawImage( QPointF( xOffset, yOffset ), image );
1751 if ( format.
font().underline()
1752 || format.
font().overline()
1753 || format.
font().strikeOut()
1754 || std::any_of( document.begin(), document.end(), [](
const QgsTextBlock & block )
1756 return std::any_of( block.begin(), block.end(), []( const QgsTextFragment & fragment )
1758 return fragment.characterFormat().underline() == QgsTextCharacterFormat::BooleanValue::SetTrue
1759 || fragment.characterFormat().overline() == QgsTextCharacterFormat::BooleanValue::SetTrue
1760 || fragment.characterFormat().strikeOut() == QgsTextCharacterFormat::BooleanValue::SetTrue;
1773 return std::any_of( document.begin(), document.end(), [](
const QgsTextBlock & block )
1775 return std::any_of( block.begin(), block.end(), []( const QgsTextFragment & fragment )
1777 return fragment.isImage();
1784 QVector< BlockMetrics > blockMetrics;
1785 blockMetrics.reserve( document.
size() );
1788 for (
const QgsTextBlock &block : document )
1791 if ( block.blockFormat().hasHorizontalAlignmentSet() )
1792 blockAlignment = block.blockFormat().horizontalAlignment();
1795 || document.size() > 1 );
1797 const bool isFinalLineInParagraph = ( blockIndex == document.size() - 1 )
1798 || document.at( blockIndex + 1 ).toPlainText().trimmed().isEmpty();
1800 BlockMetrics thisBlockMetrics;
1802 thisBlockMetrics.width = metrics.
blockWidth( blockIndex );
1804 if ( adjustForAlignment )
1806 double blockWidthDiff = 0;
1807 switch ( blockAlignment )
1814 blockWidthDiff = targetWidth - thisBlockMetrics.width - metrics.
blockRightMargin( blockIndex );
1818 if ( !isFinalLineInParagraph && targetWidth > thisBlockMetrics.width )
1820 calculateExtraSpacingForLineJustification( targetWidth - thisBlockMetrics.width, block, thisBlockMetrics.extraWordSpace, thisBlockMetrics.extraLetterSpace );
1821 thisBlockMetrics.width = targetWidth;
1837 thisBlockMetrics.xOffset = blockWidthDiff;
1842 switch ( blockAlignment )
1845 thisBlockMetrics.xOffset = blockWidthDiff - targetWidth;
1849 thisBlockMetrics.xOffset = blockWidthDiff - targetWidth / 2.0;
1871 thisBlockMetrics.backgroundWidth = targetWidth;
1872 thisBlockMetrics.backgroundXOffset = 0;
1876 thisBlockMetrics.backgroundWidth = thisBlockMetrics.width;
1877 thisBlockMetrics.backgroundXOffset = thisBlockMetrics.xOffset;
1881 blockMetrics << thisBlockMetrics;
1884 return blockMetrics;
1887QBrush QgsTextRenderer::createBrushForPath(
QgsRenderContext &context,
const QString &path )
1889 bool fitsInCache =
false;
1893 const QSizeF originalSizeMmAt96Dpi = imageSize / 3.7795275590551185;
1895 const double imageWidth = originalSizeMmAt96Dpi.width() * pixelsPerMm;
1896 const double imageHeight = originalSizeMmAt96Dpi.height() * pixelsPerMm;
1898 if ( imageWidth == 0 || imageHeight == 0 )
1901 QSize(
static_cast< int >( std::round( imageWidth ) ),
1902 static_cast< int >( std::round( imageHeight ) ) ),
1906 if ( !image.isNull() )
1909 res.setTextureImage( image );
1917 context.
painter()->translate( component.origin );
1919 context.
painter()->rotate( rotation );
1921 context.
painter()->setPen( Qt::NoPen );
1922 context.
painter()->setBrush( Qt::NoBrush );
1923 for (
const QgsTextBlock &block : document )
1925 const double baseLineOffset = metrics.
baselineOffset( blockIndex, mode );
1929 if ( block.blockFormat().hasBackground() )
1931 QBrush backgroundBrush = block.blockFormat().backgroundBrush();
1932 if ( !block.blockFormat().backgroundImagePath().isEmpty() )
1934 const QBrush backgroundImageBrush = createBrushForPath( context, block.blockFormat().backgroundImagePath() );
1935 if ( backgroundImageBrush.style() == Qt::BrushStyle::TexturePattern )
1936 backgroundBrush = backgroundImageBrush;
1939 context.
painter()->setBrush( backgroundBrush );
1940 context.
painter()->drawRect( QRectF( blockMetrics[ blockIndex ].backgroundXOffset, baseLineOffset - blockMaximumAscent, blockMetrics[ blockIndex ].backgroundWidth, blockMaximumDescent + blockMaximumAscent ) );
1944 int fragmentIndex = 0;
1946 for (
const QgsTextFragment &fragment : block )
1949 const double ascent = metrics.
fragmentAscent( blockIndex, fragmentIndex, mode );
1950 const double descent = metrics.
fragmentDescent( blockIndex, fragmentIndex, mode );
1960 if ( backgroundImageBrush.style() == Qt::BrushStyle::TexturePattern )
1961 backgroundBrush = backgroundImageBrush;
1964 context.
painter()->setBrush( backgroundBrush );
1965 context.
painter()->drawRect( QRectF( blockMetrics[ blockIndex ].xOffset + xOffset,
1966 baseLineOffset + verticalAlignOffset + yOffset - ascent, horizontalAdvance, ascent + descent ) );
1969 xOffset += horizontalAdvance;
1976 context.
painter()->setBrush( Qt::NoBrush );
1979 context.
painter()->rotate( -rotation );
1980 context.
painter()->translate( -component.origin );
1990 double targetWidth = 0.0;
1995 targetWidth = documentSize.width();
2001 targetWidth = component.size.width();
2005 double verticalAlignOffset = 0;
2009 const double overallHeight = documentSize.height();
2010 switch ( vAlignment )
2017 verticalAlignOffset = ( component.size.height() - overallHeight ) * 0.5 + metrics.
blockVerticalMargin( - 1 );
2021 verticalAlignOffset = ( component.size.height() - overallHeight ) + metrics.
blockVerticalMargin( - 1 );
2031 const bool usePathsForText = usePathsToRender( context, format, document );
2035 std::unique_ptr< std::vector< DeferredRenderBlock > > deferredBlocks;
2042 if ( requiresMultiPassRendering )
2044 deferredBlocks = std::make_unique< std::vector< DeferredRenderBlock > >();
2045 deferredBlocks->reserve( document.
size() );
2052 const QVector< BlockMetrics > blockMetrics = calculateBlockMetrics( document, metrics, mode, targetWidth, hAlignment );
2056 renderDocumentBackgrounds( context, document, metrics, component, blockMetrics, mode, verticalAlignOffset, rotation );
2060 for (
const QgsTextBlock &block : document )
2062 const double blockHeight = metrics.
blockHeight( blockIndex );
2064 DeferredRenderBlock *deferredBlock =
nullptr;
2065 if ( requiresMultiPassRendering && deferredBlocks )
2067 deferredBlocks->emplace_back( DeferredRenderBlock() );
2068 deferredBlock = &deferredBlocks->back();
2069 deferredBlock->fragments.reserve( block.size() );
2072 QgsScopedQPainterState painterState( context.
painter() );
2074 context.
painter()->translate( component.origin );
2076 context.
painter()->rotate( rotation );
2081 maskPainter->save();
2082 maskPainter->translate( component.origin );
2084 maskPainter->rotate( rotation );
2087 const BlockMetrics thisBlockMetrics = blockMetrics[ blockIndex ];
2088 const double baseLineOffset = metrics.
baselineOffset( blockIndex, mode );
2090 const QPointF blockOrigin( thisBlockMetrics.xOffset, baseLineOffset + verticalAlignOffset );
2091 if ( deferredBlock )
2092 deferredBlock->origin = blockOrigin;
2094 context.
painter()->translate( blockOrigin );
2096 maskPainter->translate( blockOrigin );
2098 Component subComponent;
2099 subComponent.block = block;
2100 subComponent.blockIndex = blockIndex;
2101 subComponent.size = QSizeF( thisBlockMetrics.width, blockHeight );
2102 subComponent.offset = QPointF( 0.0, -metrics.
ascentOffset() );
2103 subComponent.rotation = -component.rotation * 180 / M_PI;
2104 subComponent.rotationOffset = 0.0;
2105 subComponent.extraWordSpacing = thisBlockMetrics.extraWordSpace * fontScale;
2106 subComponent.extraLetterSpacing = thisBlockMetrics.extraLetterSpace * fontScale;
2107 if ( deferredBlock )
2108 deferredBlock->component = subComponent;
2113 QgsTextRenderer::drawMask( context, subComponent, format, metrics, mode );
2117 const bool needsPaths = usePathsForText
2121 std::optional< QgsScopedRenderContextReferenceScaleOverride > referenceScaleOverride;
2126 referenceScaleOverride.emplace( QgsScopedRenderContextReferenceScaleOverride( context, -1.0 ) );
2129 referenceScaleOverride.reset();
2138 context.
painter()->scale( subComponent.dpiRatio, subComponent.dpiRatio );
2140 context.
painter()->scale( 1 / fontScale, 1 / fontScale );
2141 context.
painter()->setPen( Qt::NoPen );
2142 context.
painter()->setBrush( Qt::NoBrush );
2144 renderBlockHorizontal( block, blockIndex, metrics, context, format, context.
painter(), needsPaths,
2145 fontScale, thisBlockMetrics.extraWordSpace, thisBlockMetrics.extraLetterSpace, mode, deferredBlock );
2148 maskPainter->restore();
2154 if ( deferredBlocks )
2156 renderDeferredBlocks(
2157 context, format, components, *deferredBlocks, usePathsForText, fontScale, component, rotation
2165 const std::vector< DeferredRenderBlock > &deferredBlocks,
2166 bool usePathsForText,
2168 const Component &component,
2173 renderDeferredBuffer( context, format, components, deferredBlocks, fontScale, component, rotation );
2180 renderDeferredShadowForText( context, format, deferredBlocks, fontScale, component, rotation );
2189 renderDeferredText( context, deferredBlocks, usePathsForText, fontScale, component, rotation );
2193void QgsTextRenderer::renderDeferredShadowForText(
QgsRenderContext &context,
2195 const std::vector< DeferredRenderBlock > &deferredBlocks,
2197 const Component &component,
2200 QgsScopedQPainterState painterState( context.
painter() );
2202 context.
painter()->translate( component.origin );
2204 context.
painter()->rotate( rotation );
2206 context.
painter()->setPen( Qt::NoPen );
2207 context.
painter()->setBrush( Qt::NoBrush );
2209 for (
const DeferredRenderBlock &block : deferredBlocks )
2211 Component subComponent = block.component;
2213 QPainter painter( &subComponent.picture );
2214 painter.setPen( Qt::NoPen );
2215 painter.setBrush( Qt::NoBrush );
2216 painter.scale( 1 / fontScale, 1 / fontScale );
2218 for (
const DeferredRenderFragment &fragment : std::as_const( block.fragments ) )
2220 if ( !fragment.path.isEmpty() )
2222 painter.setBrush( fragment.color );
2223 painter.drawPath( fragment.path );
2227 painter.setPen( fragment.color );
2228 painter.setFont( fragment.font );
2229 painter.drawText( fragment.point, fragment.
text );
2234 subComponent.pictureBuffer = 1.0;
2235 subComponent.origin = QPointF( 0.0, 0.0 );
2236 const QRectF pictureBoundingRect = subComponent.picture.boundingRect();
2237 subComponent.size = pictureBoundingRect.size();
2238 subComponent.offset = QPointF( -pictureBoundingRect.left(), -pictureBoundingRect.height() - pictureBoundingRect.top() );
2240 context.
painter()->translate( block.origin );
2241 drawShadow( context, subComponent, format );
2242 context.
painter()->translate( -block.origin );
2249 const std::vector< DeferredRenderBlock > &deferredBlocks,
2251 const Component &component,
2254 QgsScopedQPainterState painterState( context.
painter() );
2261 std::unique_ptr< QPicture > bufferPicture;
2262 std::unique_ptr< QPainter > bufferPainter;
2263 QPainter *prevPainter = context.
painter();
2264 if ( needsShadowOnBuffer )
2266 bufferPicture = std::make_unique< QPicture >();
2267 bufferPainter = std::make_unique< QPainter >( bufferPicture.get() );
2271 std::unique_ptr< QgsPaintEffect > tmpEffect;
2275 tmpEffect->begin( context );
2280 QPen pen( bufferColor );
2281 const QgsTextBufferSettings &buffer = format.
buffer();
2285 pen.setWidthF( penSize * fontScale );
2287 context.
painter()->setPen( pen );
2292 bufferColor.setAlpha( 0 );
2294 context.
painter()->setBrush( bufferColor );
2296 context.
painter()->translate( component.origin );
2298 context.
painter()->rotate( rotation );
2305 for (
const DeferredRenderBlock &block : deferredBlocks )
2307 context.
painter()->translate( block.origin );
2308 context.
painter()->scale( 1 / fontScale, 1 / fontScale );
2309 for (
const DeferredRenderFragment &fragment : std::as_const( block.fragments ) )
2311 context.
painter()->drawPath( fragment.path );
2313 context.
painter()->scale( fontScale, fontScale );
2314 context.
painter()->translate( -block.origin );
2319 tmpEffect->end( context );
2322 if ( needsShadowOnBuffer && bufferPicture )
2324 bufferPainter->end();
2325 bufferPainter.reset();
2328 QgsTextRenderer::Component bufferComponent = component;
2329 bufferComponent.origin = QPointF( 0.0, 0.0 );
2330 bufferComponent.picture = *bufferPicture;
2331 bufferComponent.pictureBuffer = penSize / 2.0;
2332 const QRectF bufferBoundingBox = bufferPicture->boundingRect();
2333 bufferComponent.size = bufferBoundingBox.size();
2334 bufferComponent.offset = QPointF( -bufferBoundingBox.left(), -bufferBoundingBox.height() - bufferBoundingBox.top() );
2336 drawShadow( context, bufferComponent, format );
2345 context.
painter()->scale( component.dpiRatio, component.dpiRatio );
2351 const std::vector< DeferredRenderBlock > &deferredBlocks,
2352 bool usePathsForText,
2354 const Component &component,
2357 QgsScopedQPainterState painterState( context.
painter() );
2359 context.
painter()->translate( component.origin );
2361 context.
painter()->rotate( rotation );
2363 context.
painter()->setPen( Qt::NoPen );
2364 context.
painter()->setBrush( Qt::NoBrush );
2367 for (
const DeferredRenderBlock &block : deferredBlocks )
2369 context.
painter()->translate( block.origin );
2370 context.
painter()->scale( 1 / fontScale, 1 / fontScale );
2372 for (
const DeferredRenderFragment &fragment : std::as_const( block.fragments ) )
2374 if ( usePathsForText )
2376 context.
painter()->setBrush( fragment.color );
2377 context.
painter()->drawPath( fragment.path );
2381 context.
painter()->setPen( fragment.color );
2382 context.
painter()->setFont( fragment.font );
2383 context.
painter()->drawText( fragment.point, fragment.
text );
2387 context.
painter()->scale( fontScale, fontScale );
2388 context.
painter()->translate( -block.origin );
2392void QgsTextRenderer::drawTextInternalVertical(
QgsRenderContext &context,
const QgsTextFormat &format,
Qgis::TextComponents components,
Qgis::TextLayoutMode mode,
const QgsTextRenderer::Component &component,
const QgsTextDocument &document,
const QgsTextDocumentMetrics &metrics,
double fontScale,
Qgis::TextHorizontalAlignment hAlignment,
Qgis::TextVerticalAlignment,
double rotation )
2395 const QStringList textLines = document.
toPlainText();
2397 std::optional< QgsScopedRenderContextReferenceScaleOverride > referenceScaleOverride;
2402 referenceScaleOverride.emplace( QgsScopedRenderContextReferenceScaleOverride( context, -1.0 ) );
2408 referenceScaleOverride.reset();
2411 const double actualTextWidth = documentSize.width();
2412 double textRectWidth = 0.0;
2418 textRectWidth = actualTextWidth;
2424 textRectWidth = component.size.width();
2428 int maxLineLength = 0;
2429 for (
const QString &line : std::as_const( textLines ) )
2431 maxLineLength = std::max( maxLineLength,
static_cast<int>( line.length() ) );
2434 const double actualLabelHeight = documentSize.height();
2439 for (
const QgsTextBlock &block : document )
2441 QgsScopedQPainterState painterState( context.
painter() );
2444 context.
painter()->translate( component.origin );
2446 context.
painter()->rotate( rotation );
2451 maskPainter->save();
2452 maskPainter->translate( component.origin );
2454 maskPainter->rotate( rotation );
2461 if ( adjustForAlignment )
2463 double hAlignmentOffset = 0;
2464 switch ( hAlignment )
2467 hAlignmentOffset = ( textRectWidth - actualTextWidth ) * 0.5;
2471 hAlignmentOffset = textRectWidth - actualTextWidth;
2485 xOffset += hAlignmentOffset;
2493 double yOffset = 0.0;
2499 if ( rotation >= -405 && rotation < -180 )
2503 else if ( rotation >= 0 && rotation < 45 )
2505 xOffset -= actualTextWidth;
2511 yOffset = -actualLabelHeight;
2516 yOffset = -actualLabelHeight;
2526 context.
painter()->translate( QPointF( xOffset, yOffset ) );
2528 double currentBlockYOffset = 0;
2529 int fragmentIndex = 0;
2530 for (
const QgsTextFragment &fragment : block )
2532 QgsScopedQPainterState fragmentPainterState( context.
painter() );
2537 const QFont fragmentFont = metrics.
fragmentFont( blockIndex, fragmentIndex );
2539 QFontMetricsF fragmentMetrics( fragmentFont );
2541 const double letterSpacing = fragmentFont.letterSpacing() / fontScale;
2542 const double labelHeight = fragmentMetrics.ascent() / fontScale + ( fragmentMetrics.ascent() / fontScale + letterSpacing ) * ( line.length() - 1 );
2544 Component subComponent;
2545 subComponent.block = QgsTextBlock( fragment );
2546 subComponent.blockIndex = blockIndex;
2547 subComponent.firstFragmentIndex = fragmentIndex;
2548 subComponent.size = QSizeF( blockMaximumCharacterWidth, labelHeight + fragmentMetrics.descent() / fontScale );
2549 subComponent.offset = QPointF( 0.0, currentBlockYOffset );
2550 subComponent.rotation = -component.rotation * 180 / M_PI;
2551 subComponent.rotationOffset = 0.0;
2558 QgsTextRenderer::drawMask( context, subComponent, format );
2564 currentBlockYOffset += QgsTextRenderer::drawBuffer( context, subComponent, format, metrics, mode );
2570 path.setFillRule( Qt::WindingFill );
2572 double partYOffset = 0.0;
2573 for (
const QString &part : parts )
2575 double partXOffset = ( blockMaximumCharacterWidth - ( fragmentMetrics.horizontalAdvance( part ) / fontScale - letterSpacing ) ) / 2;
2576 partYOffset += fragmentMetrics.ascent() / fontScale;
2577 path.addText( partXOffset * fontScale, partYOffset * fontScale, fragmentFont, part );
2578 partYOffset += letterSpacing;
2584 textp.begin( &textPict );
2585 textp.setPen( Qt::NoPen );
2588 textp.setBrush( textColor );
2589 textp.scale( 1 / fontScale, 1 / fontScale );
2590 textp.drawPath( path );
2601 subComponent.picture = textPict;
2602 subComponent.pictureBuffer = 0.0;
2603 subComponent.origin = QPointF( 0.0, currentBlockYOffset );
2604 const double prevY = subComponent.offset.y();
2605 subComponent.offset = QPointF( 0, -subComponent.size.height() );
2606 subComponent.useOrigin =
true;
2607 QgsTextRenderer::drawShadow( context, subComponent, format );
2608 subComponent.useOrigin =
false;
2609 subComponent.offset = QPointF( 0, prevY );
2619 context.
painter()->scale( subComponent.dpiRatio, subComponent.dpiRatio );
2623 context.
painter()->translate( 0, currentBlockYOffset );
2625 context.
painter()->drawPicture( 0, 0, textPict );
2626 currentBlockYOffset += partYOffset;
2632 maskPainter->restore();
2650 if ( pixelSize < 50 )
2651 return 200 / pixelSize;
2654 else if ( pixelSize > 200 )
2655 return 200 / pixelSize;
@ ForceVector
Always force vector-based rendering, even when the result will be visually different to a raster-base...
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 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.
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())
Calculates curved text placement properties.
@ RespectPainterOrientation
Curved text will be placed respecting the painter orientation, and the actual line direction will be ...
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 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 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)
Draws a text document along a line using the specified settings.
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)
Draws text along a line using the specified settings.
static Qgis::TextHorizontalAlignment convertQtHAlignment(Qt::Alignment alignment)
Converts a Qt horizontal alignment flag to a Qgis::TextHorizontalAlignment value.
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