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_MAJOR == 3 && GEOS_VERSION_MINOR < 11
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.rotation = -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.rotation = 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.rotation = 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 if ( component.rotation >= -315 && component.rotation < -90 )
585 else if ( component.rotation >= -90 && component.rotation < -45 )
595 QgsTextBufferSettings buffer = format.
buffer();
602 std::optional< QgsScopedRenderContextReferenceScaleOverride > referenceScaleOverride;
607 referenceScaleOverride.emplace( QgsScopedRenderContextReferenceScaleOverride( context, -1.0 ) );
613 referenceScaleOverride.reset();
616 path.setFillRule( Qt::WindingFill );
618 double height = component.size.height();
619 switch ( orientation )
630 double partYOffset = component.offset.y() * scaleFactor;
633 double partLastDescent = 0;
635 int fragmentIndex = 0;
636 for (
const QgsTextFragment &fragment : component.block )
638 const QFont fragmentFont = metrics.
fragmentFont( component.blockIndex, component.firstFragmentIndex + fragmentIndex );
639 const double letterSpacing = fragmentFont.letterSpacing() / scaleFactor;
641 const QFontMetricsF fragmentMetrics( fragmentFont );
643 const double fragmentYOffset = metrics.
fragmentVerticalOffset( component.blockIndex, fragmentIndex, mode );
646 for (
const QString &part : parts )
648 double partXOffset = ( blockMaximumCharacterWidth - ( fragmentMetrics.horizontalAdvance( part ) / scaleFactor - letterSpacing ) ) / 2;
649 partYOffset += fragmentMetrics.ascent() / scaleFactor;
650 path.addText( partXOffset, partYOffset + fragmentYOffset, fragmentFont, part );
651 partYOffset += letterSpacing;
653 partLastDescent = fragmentMetrics.descent() / scaleFactor;
657 height = partYOffset + partLastDescent;
658 advance = partYOffset - component.offset.y() * scaleFactor;
663 QColor bufferColor = buffer.
color();
664 bufferColor.setAlphaF( buffer.
opacity() );
665 QPen pen( bufferColor );
666 pen.setWidthF( penSize * scaleFactor );
668 QColor tmpColor( bufferColor );
672 tmpColor.setAlpha( 0 );
678 buffp.begin( &buffPict );
682 std::unique_ptr< QgsPaintEffect > tmpEffect( buffer.
paintEffect()->
clone() );
684 tmpEffect->begin( context );
685 context.
painter()->setPen( pen );
686 context.
painter()->setBrush( tmpColor );
687 if ( scaleFactor != 1.0 )
688 context.
painter()->scale( 1 / scaleFactor, 1 / scaleFactor );
689 context.
painter()->drawPath( path );
690 if ( scaleFactor != 1.0 )
691 context.
painter()->scale( scaleFactor, scaleFactor );
692 tmpEffect->end( context );
698 if ( scaleFactor != 1.0 )
699 buffp.scale( 1 / scaleFactor, 1 / scaleFactor );
701 buffp.setBrush( tmpColor );
702 buffp.drawPath( path );
708 QgsTextRenderer::Component bufferComponent = component;
709 bufferComponent.origin = QPointF( 0.0, 0.0 );
710 bufferComponent.picture = buffPict;
711 bufferComponent.pictureBuffer = penSize / 2.0;
712 bufferComponent.size.setHeight( height );
716 bufferComponent.offset.setY( -bufferComponent.size.height() );
718 drawShadow( context, bufferComponent, format );
721 QgsScopedQPainterState painterState( p );
726 p->setCompositionMode( buffer.
blendMode() );
730 p->scale( component.dpiRatio, component.dpiRatio );
732 p->drawPicture( 0, 0, buffPict );
734 return advance / scaleFactor;
739 QgsTextMaskSettings mask = format.
mask();
752 path.setFillRule( Qt::WindingFill );
759 std::optional< QgsScopedRenderContextReferenceScaleOverride > referenceScaleOverride;
764 referenceScaleOverride.emplace( QgsScopedRenderContextReferenceScaleOverride( context, -1.0 ) );
770 referenceScaleOverride.reset();
773 int fragmentIndex = 0;
774 for (
const QgsTextFragment &fragment : component.block )
778 const QFont fragmentFont = metrics.
fragmentFont( component.blockIndex, fragmentIndex );
780 const double fragmentYOffset = metrics.
fragmentVerticalOffset( component.blockIndex, fragmentIndex, mode );
781 path.addText( xOffset, fragmentYOffset, fragmentFont, fragment.
text() );
788 QColor bufferColor( Qt::gray );
789 bufferColor.setAlphaF( mask.
opacity() );
793 brush.setColor( bufferColor );
794 pen.setColor( bufferColor );
795 pen.setWidthF( penSize * scaleFactor );
798 QgsScopedQPainterState painterState( p );
802 p->scale( component.dpiRatio, component.dpiRatio );
805 QgsPainterSwapper swapper( context, p );
807 QgsEffectPainter effectPainter( context, mask.
paintEffect() );
808 if ( scaleFactor != 1.0 )
809 context.
painter()->scale( 1 / scaleFactor, 1 / scaleFactor );
810 context.
painter()->setPen( pen );
811 context.
painter()->setBrush( brush );
812 context.
painter()->drawPath( path );
813 if ( scaleFactor != 1.0 )
814 context.
painter()->scale( scaleFactor, scaleFactor );
819 if ( scaleFactor != 1.0 )
820 p->scale( 1 / scaleFactor, 1 / scaleFactor );
822 p->setBrush( brush );
824 if ( scaleFactor != 1.0 )
825 p->scale( scaleFactor, scaleFactor );
832 if ( doc.
size() == 0 )
835 return textWidth( context, format, doc );
854 for (
const QString &line : textLines )
858 lines.append(
wrappedText( context, line, maxLineWidth, format ) );
862 lines.append( line );
867 return textHeight( context, format, doc, mode );
874 bool isNullSize =
false;
875 const QFont baseFont = format.
scaledFont( context, scaleFactor, &isNullSize );
879 const QFontMetrics fm( baseFont );
880 const double height = ( character.isNull() ? fm.height() : fm.boundingRect( character ).height() ) / scaleFactor;
882 if ( !includeEffects )
885 double maxExtension = 0;
913 return height + maxExtension;
921 const QStringList multiLineSplit = text.split(
'\n' );
923 return currentTextWidth > width;
928 const QStringList lines = text.split(
'\n' );
929 QStringList outLines;
930 for (
const QString &line : lines )
935 const QStringList words = line.split(
' ' );
936 QStringList linesToProcess;
937 QString wordsInCurrentLine;
938 for (
const QString &word : words )
943 if ( !wordsInCurrentLine.isEmpty() )
944 linesToProcess << wordsInCurrentLine;
945 wordsInCurrentLine.clear();
946 linesToProcess << word;
950 if ( !wordsInCurrentLine.isEmpty() )
951 wordsInCurrentLine.append(
' ' );
952 wordsInCurrentLine.append( word );
955 if ( !wordsInCurrentLine.isEmpty() )
956 linesToProcess << wordsInCurrentLine;
958 for (
const QString &line : std::as_const( linesToProcess ) )
960 QString remainingText = line;
961 int lastPos = remainingText.lastIndexOf(
' ' );
962 while ( lastPos > -1 )
972 outLines << remainingText.left( lastPos );
973 remainingText = remainingText.mid( lastPos + 1 );
976 lastPos = remainingText.lastIndexOf(
' ', lastPos - 1 );
978 outLines << remainingText;
1007 Component component =
c;
1008 QgsTextBackgroundSettings background = format.
background();
1010 QPainter *prevP = context.
painter();
1011 QPainter *p = context.
painter();
1012 std::unique_ptr< QgsPaintEffect > tmpEffect;
1016 tmpEffect->begin( context );
1025 const double originAdjustRotationRadians = -component.rotation;
1028 component.rotation = -( component.rotation * 180 / M_PI );
1033 component.rotation = 0.0;
1034 component.rotationOffset = background.
rotation();
1043 double width = documentSize.width();
1044 double height = documentSize.height();
1051 switch ( component.hAlign )
1055 component.center = QPointF( component.origin.x() + width / 2.0, component.origin.y() + height / 2.0 );
1059 component.center = QPointF( component.origin.x() + component.size.width() / 2.0, component.origin.y() + height / 2.0 );
1063 component.center = QPointF( component.origin.x() + component.size.width() - width / 2.0, component.origin.y() + height / 2.0 );
1070 bool isNullSize =
false;
1071 QFontMetricsF fm( format.
scaledFont( context, scaleFactor, &isNullSize ) );
1072 double originAdjust = isNullSize ? 0 : ( fm.ascent() / scaleFactor / 2.0 - fm.leading() / scaleFactor / 2.0 );
1073 switch ( component.hAlign )
1077 component.center = QPointF( component.origin.x() + width / 2.0, component.origin.y() - height / 2.0 + originAdjust );
1081 component.center = QPointF( component.origin.x(), component.origin.y() - height / 2.0 + originAdjust );
1085 component.center = QPointF( component.origin.x() - width / 2.0, component.origin.y() - height / 2.0 + originAdjust );
1092 const double dx = component.center.x() - component.origin.x();
1093 const double dy = component.center.y() - component.origin.y();
1094 component.center.setX( component.origin.x() + ( std::cos( originAdjustRotationRadians ) * dx - std::sin( originAdjustRotationRadians ) * dy ) );
1095 component.center.setY( component.origin.y() + ( std::sin( originAdjustRotationRadians ) * dx + std::cos( originAdjustRotationRadians ) * dy ) );
1105 component.size = QSizeF( width, height );
1110 switch ( background.
type() )
1123 double sizeOut = 0.0;
1125 QgsScopedRenderContextReferenceScaleOverride referenceScaleOverride( context, -1 );
1134 sizeOut = std::max( component.size.width(), component.size.height() );
1138 sizeOut += bufferSize * 2;
1144 if ( sizeOut < 1.0 )
1147 std::unique_ptr< QgsMarkerSymbol > renderedSymbol;
1151 map[u
"name"_s] = background.
svgFile().trimmed();
1152 map[u
"size"_s] = QString::number( sizeOut );
1154 map[u
"angle"_s] = QString::number( 0.0 );
1162 map[u
"fill"_s] = background.
fillColor().name();
1163 map[u
"outline"_s] = background.
strokeColor().name();
1164 map[u
"outline-width"_s] = QString::number( background.
strokeWidth() );
1169 QgsTextShadowSettings shadow = format.
shadow();
1171 QVariantMap shdwmap( map );
1172 shdwmap[u
"fill"_s] = shadow.
color().name();
1173 shdwmap[u
"outline"_s] = shadow.
color().name();
1174 shdwmap[u
"size"_s] = QString::number( sizeOut );
1179 svgp.begin( &svgPict );
1187 QgsRenderContext shdwContext;
1193 QgsSvgMarkerSymbolLayer *svgShdwM =
static_cast<QgsSvgMarkerSymbolLayer *
>( symShdwL.get() );
1196 svgShdwM->
renderPoint( QPointF( sizeOut / 2, -sizeOut / 2 ), svgShdwContext );
1199 component.picture = svgPict;
1201 component.pictureBuffer = 0.0;
1203 component.size = QSizeF( sizeOut, sizeOut );
1204 component.offset = QPointF( 0.0, 0.0 );
1207 QgsScopedQPainterState painterState( p );
1210 p->translate( component.center.x(), component.center.y() );
1211 p->rotate( component.rotation );
1214 p->translate( QPointF( xoff, yoff ) );
1215 p->rotate( component.rotationOffset );
1216 p->translate( -sizeOut / 2, sizeOut / 2 );
1218 drawShadow( context, component, format );
1220 renderedSymbol.reset();
1228 renderedSymbol->setSize( sizeOut );
1232 renderedSymbol->setOpacity( renderedSymbol->opacity() * background.
opacity() );
1235 QgsScopedQPainterState painterState( p );
1240 p->setCompositionMode( background.
blendMode() );
1242 p->translate( component.center.x(), component.center.y() );
1243 p->rotate( component.rotation );
1246 p->translate( QPointF( xoff, yoff ) );
1247 p->rotate( component.rotationOffset );
1251 renderedSymbol->renderPoint( QPointF( 0, 0 ), &f, context );
1252 renderedSymbol->stopRender( context );
1253 p->setCompositionMode( QPainter::CompositionMode_SourceOver );
1263 double w = component.size.width();
1264 double h = component.size.height();
1283 h = std::sqrt( std::pow( w, 2 ) + std::pow( h, 2 ) );
1289 h = h * M_SQRT1_2 * 2;
1290 w = w * M_SQRT1_2 * 2;
1296 w += bufferWidth * 2;
1297 h += bufferHeight * 2;
1301 QRectF rect( -w / 2.0, -h / 2.0, w, h );
1303 if ( rect.isNull() )
1306 QgsScopedQPainterState painterState( p );
1309 p->translate( QPointF( component.center.x(), component.center.y() ) );
1310 p->rotate( component.rotation );
1313 p->translate( QPointF( xoff, yoff ) );
1314 p->rotate( component.rotationOffset );
1320 QTransform t = QTransform::fromScale( 10, 10 );
1322 QTransform ti = t.inverted();
1328 path.addRoundedRect( rect, background.
radii().width(), background.
radii().height(), Qt::RelativeSize );
1334 path.addRoundedRect( rect, xRadius, yRadius );
1339 path.addEllipse( rect );
1341 QPolygonF tempPolygon = path.toFillPolygon( t );
1342 QPolygonF polygon = ti.map( tempPolygon );
1344 QPainter *oldp = context.
painter();
1347 shapep.begin( &shapePict );
1350 std::unique_ptr< QgsFillSymbol > renderedSymbol;
1352 renderedSymbol->setOpacity( renderedSymbol->opacity() * background.
opacity() );
1356 renderedSymbol->renderPolygon( polygon,
nullptr, &f, context );
1357 renderedSymbol->stopRender( context );
1364 component.picture = shapePict;
1367 component.size = rect.size();
1368 component.offset = QPointF( rect.width() / 2, -rect.height() / 2 );
1369 drawShadow( context, component, format );
1374 p->setCompositionMode( background.
blendMode() );
1378 p->scale( component.dpiRatio, component.dpiRatio );
1380 p->drawPicture( 0, 0, shapePict );
1381 p->setCompositionMode( QPainter::CompositionMode_SourceOver );
1388 tmpEffect->end( context );
1395 QgsTextShadowSettings shadow = format.
shadow();
1397 QPainter *p = context.
painter();
1398 const double componentWidth = component.size.width();
1399 const double componentHeight = component.size.height();
1400 const double xOffset = component.offset.x();
1401 const double yOffset = component.offset.y();
1402 double pictbuffer = component.pictureBuffer;
1410 radius /= ( mapUnits ? context.
scaleFactor() / component.dpiRatio : 1 );
1411 radius =
static_cast< int >( radius + 0.5 );
1415 double blurBufferClippingScale = 3.75;
1416 int blurbuffer = ( radius > 17 ? 16 : radius ) * blurBufferClippingScale;
1418 QImage blurImg( componentWidth + ( pictbuffer * 2.0 ) + ( blurbuffer * 2.0 ), componentHeight + ( pictbuffer * 2.0 ) + ( blurbuffer * 2.0 ), QImage::Format_ARGB32_Premultiplied );
1422 int minBlurImgSize = 1;
1426 int maxBlurImgSize = 40000;
1427 if ( blurImg.isNull() || ( blurImg.width() < minBlurImgSize || blurImg.height() < minBlurImgSize ) || ( blurImg.width() > maxBlurImgSize || blurImg.height() > maxBlurImgSize ) )
1430 blurImg.fill( QColor( Qt::transparent ).rgba() );
1432 if ( !pictp.begin( &blurImg ) )
1434 pictp.setRenderHints( QPainter::Antialiasing | QPainter::SmoothPixmapTransform );
1435 QPointF imgOffset( blurbuffer + pictbuffer + xOffset, blurbuffer + pictbuffer + componentHeight + yOffset );
1437 pictp.drawPicture( imgOffset, component.picture );
1440 pictp.setCompositionMode( QPainter::CompositionMode_SourceIn );
1441 pictp.fillRect( blurImg.rect(), shadow.
color() );
1445 if ( shadow.
blurRadius() > 0.0 && radius > 0 )
1453 picti.begin( &blurImg );
1454 picti.setBrush( Qt::Dense7Pattern );
1455 QPen imgPen( QColor( 0, 0, 255, 255 ) );
1456 imgPen.setWidth( 1 );
1457 picti.setPen( imgPen );
1458 picti.setOpacity( 0.1 );
1459 picti.drawRect( 0, 0, blurImg.width(), blurImg.height() );
1465 double angleRad = shadow.
offsetAngle() * M_PI / 180;
1473 angleRad -= ( component.rotation * M_PI / 180 + component.rotationOffset * M_PI / 180 );
1476 QPointF transPt( -offsetDist * std::cos( angleRad + M_PI_2 ), -offsetDist * std::sin( angleRad + M_PI_2 ) );
1482 p->setRenderHint( QPainter::SmoothPixmapTransform );
1485 p->setCompositionMode( shadow.
blendMode() );
1487 p->setOpacity( shadow.
opacity() );
1489 double scale = shadow.
scale() / 100.0;
1491 p->scale( scale, scale );
1492 if ( component.useOrigin )
1494 p->translate( component.origin.x(), component.origin.y() );
1496 p->translate( transPt );
1497 p->translate( -imgOffset.x(), -imgOffset.y() );
1498 p->drawImage( 0, 0, blurImg );
1505 p->setBrush( Qt::NoBrush );
1506 QPen imgPen( QColor( 255, 0, 0, 10 ) );
1507 imgPen.setWidth( 2 );
1508 imgPen.setStyle( Qt::DashLine );
1509 p->setPen( imgPen );
1510 p->scale( scale, scale );
1511 if ( component.useOrigin() )
1513 p->translate( component.origin().x(), component.origin().y() );
1515 p->translate( transPt );
1516 p->translate( -imgOffset.x(),
1518 p->drawRect( 0, 0, blurImg.width(), blurImg.height() );
1523 p->setBrush( Qt::NoBrush );
1524 QPen componentRectPen( QColor( 0, 255, 0, 70 ) );
1525 componentRectPen.setWidth( 1 );
1526 if ( component.useOrigin() )
1528 p->translate( component.origin().x(), component.origin().y() );
1530 p->setPen( componentRectPen );
1531 p->drawRect( QRect( -xOffset, -componentHeight - yOffset, componentWidth, componentHeight ) );
1537void QgsTextRenderer::drawTextInternal(
1541 const Component &component,
1556 std::optional< QgsScopedRenderContextReferenceScaleOverride > referenceScaleOverride;
1561 referenceScaleOverride.emplace( QgsScopedRenderContextReferenceScaleOverride( context, -1.0 ) );
1567 referenceScaleOverride.reset();
1569 double rotation = 0;
1570 const Qgis::TextOrientation orientation = calculateRotationAndOrientationForComponent( format, component, rotation );
1571 switch ( orientation )
1575 drawTextInternalHorizontal( context, format, components, mode, component, document, metrics, fontScale, alignment, vAlignment, rotation );
1584 drawTextInternalVertical( context, format,
Qgis::TextComponent::Buffer, mode, component, document, metrics, fontScale, alignment, vAlignment, rotation );
1586 drawTextInternalVertical( context, format,
Qgis::TextComponent::Text, mode, component, document, metrics, fontScale, alignment, vAlignment, rotation );
1592Qgis::TextOrientation QgsTextRenderer::calculateRotationAndOrientationForComponent(
const QgsTextFormat &format,
const QgsTextRenderer::Component &component,
double &rotation )
1594 rotation = -component.rotation * 180 / M_PI;
1601 if ( rotation >= -315 && rotation < -90 )
1606 else if ( rotation >= -90 && rotation < -45 )
1622void QgsTextRenderer::calculateExtraSpacingForLineJustification(
const double spaceToDistribute,
const QgsTextBlock &block,
double &extraWordSpace,
double &extraLetterSpace )
1625 QTextBoundaryFinder
finder( QTextBoundaryFinder::Word, blockText );
1627 int wordBoundaries = 0;
1628 while (
finder.toNextBoundary() != -1 )
1630 if (
finder.boundaryReasons() & QTextBoundaryFinder::StartOfItem )
1634 if ( wordBoundaries > 0 )
1637 extraWordSpace = spaceToDistribute / wordBoundaries;
1642 QTextBoundaryFinder
finder( QTextBoundaryFinder::Grapheme, blockText );
1645 int graphemeBoundaries = 0;
1646 while (
finder.toNextBoundary() != -1 )
1648 if (
finder.boundaryReasons() & QTextBoundaryFinder::StartOfItem )
1649 graphemeBoundaries++;
1652 if ( graphemeBoundaries > 0 )
1654 extraLetterSpace = spaceToDistribute / graphemeBoundaries;
1659void QgsTextRenderer::applyExtraSpacingForLineJustification( QFont &font,
double extraWordSpace,
double extraLetterSpace )
1661 const double prevWordSpace = font.wordSpacing();
1662 font.setWordSpacing( prevWordSpace + extraWordSpace );
1663 const double prevLetterSpace = font.letterSpacing();
1664 font.setLetterSpacing( QFont::AbsoluteSpacing, prevLetterSpace + extraLetterSpace );
1668void QgsTextRenderer::renderBlockHorizontal(
1675 bool forceRenderAsPaths,
1677 double extraWordSpace,
1678 double extraLetterSpace,
1680 DeferredRenderBlock *deferredRenderBlock
1686 int fragmentIndex = 0;
1687 for (
const QgsTextFragment &fragment : block )
1692 QFont fragmentFont = metrics.
fragmentFont( blockIndex, fragmentIndex );
1695 applyExtraSpacingForLineJustification( fragmentFont, extraWordSpace * fontScale, extraLetterSpace * fontScale );
1702 if ( deferredRenderBlock )
1704 DeferredRenderFragment renderFragment;
1705 renderFragment.color = textColor;
1706 if ( forceRenderAsPaths )
1708 renderFragment.path.setFillRule( Qt::WindingFill );
1709 renderFragment.path.addText( xOffset, yOffset, fragmentFont, fragment.
text() );
1711 renderFragment.font = fragmentFont;
1712 renderFragment.point = QPointF( xOffset, yOffset );
1713 renderFragment.text = fragment.
text();
1714 deferredRenderBlock->fragments.append( renderFragment );
1716 else if ( forceRenderAsPaths )
1718 painter->setBrush( textColor );
1720 path.setFillRule( Qt::WindingFill );
1721 path.addText( xOffset, yOffset, fragmentFont, fragment.
text() );
1722 painter->drawPath( path );
1726 painter->setPen( textColor );
1727 painter->setFont( fragmentFont );
1728 painter->drawText( QPointF( xOffset, yOffset ), fragment.
text() );
1731 else if ( fragment.
isImage() )
1733 bool fitsInCache =
false;
1735 const double imageHeight = metrics.
fragmentFixedHeight( blockIndex, fragmentIndex, mode ) * fontScale;
1739 QSize(
static_cast< int >( std::round( imageWidth ) ),
static_cast< int >( std::round( imageHeight ) ) ),
1746 const double yOffset = imageBaseline - image.height();
1747 if ( !image.isNull() )
1748 painter->drawImage( QPointF( xOffset, yOffset ), image );
1779 if ( format.
font().underline() || format.
font().overline() || format.
font().strikeOut() || std::any_of( document.begin(), document.end(), [](
const QgsTextBlock &block ) {
1780 return std::any_of( block.begin(), block.end(), []( const QgsTextFragment &fragment ) {
1781 return fragment.characterFormat().underline() == QgsTextCharacterFormat::BooleanValue::SetTrue
1782 || fragment.characterFormat().overline() == QgsTextCharacterFormat::BooleanValue::SetTrue
1783 || fragment.characterFormat().strikeOut() == QgsTextCharacterFormat::BooleanValue::SetTrue;
1796 return std::any_of( document.begin(), document.end(), [](
const QgsTextBlock &block ) {
1797 return std::any_of( block.begin(), block.end(), []( const QgsTextFragment &fragment ) { return fragment.isImage(); } );
1801QVector< QgsTextRenderer::BlockMetrics > QgsTextRenderer::calculateBlockMetrics(
1805 QVector< BlockMetrics > blockMetrics;
1806 blockMetrics.reserve( document.
size() );
1809 for (
const QgsTextBlock &block : document )
1812 if ( block.blockFormat().hasHorizontalAlignmentSet() )
1813 blockAlignment = block.blockFormat().horizontalAlignment();
1816 const bool isFinalLineInParagraph = ( blockIndex == document.size() - 1 ) || document.at( blockIndex + 1 ).toPlainText().trimmed().isEmpty();
1818 BlockMetrics thisBlockMetrics;
1820 thisBlockMetrics.width = metrics.
blockWidth( blockIndex );
1822 if ( adjustForAlignment )
1824 double blockWidthDiff = 0;
1825 switch ( blockAlignment )
1832 blockWidthDiff = targetWidth - thisBlockMetrics.width - metrics.
blockRightMargin( blockIndex );
1836 if ( !isFinalLineInParagraph && targetWidth > thisBlockMetrics.width )
1838 calculateExtraSpacingForLineJustification( targetWidth - thisBlockMetrics.width, block, thisBlockMetrics.extraWordSpace, thisBlockMetrics.extraLetterSpace );
1839 thisBlockMetrics.width = targetWidth;
1855 thisBlockMetrics.xOffset = blockWidthDiff;
1860 switch ( blockAlignment )
1863 thisBlockMetrics.xOffset = blockWidthDiff - targetWidth;
1867 thisBlockMetrics.xOffset = blockWidthDiff - targetWidth / 2.0;
1889 thisBlockMetrics.backgroundWidth = targetWidth;
1890 thisBlockMetrics.backgroundXOffset = 0;
1894 thisBlockMetrics.backgroundWidth = thisBlockMetrics.width;
1895 thisBlockMetrics.backgroundXOffset = thisBlockMetrics.xOffset;
1899 blockMetrics << thisBlockMetrics;
1902 return blockMetrics;
1905QBrush QgsTextRenderer::createBrushForPath(
QgsRenderContext &context,
const QString &path )
1907 bool fitsInCache =
false;
1911 const QSizeF originalSizeMmAt96Dpi = imageSize / 3.7795275590551185;
1913 const double imageWidth = originalSizeMmAt96Dpi.width() * pixelsPerMm;
1914 const double imageHeight = originalSizeMmAt96Dpi.height() * pixelsPerMm;
1916 if ( imageWidth == 0 || imageHeight == 0 )
1922 if ( !image.isNull() )
1924 res.setTextureImage( image );
1929void QgsTextRenderer::renderDocumentBackgrounds(
1933 const Component &component,
1934 const QVector< QgsTextRenderer::BlockMetrics > &blockMetrics,
1936 double verticalAlignOffset,
1941 context.
painter()->translate( component.origin );
1943 context.
painter()->rotate( rotation );
1945 context.
painter()->setPen( Qt::NoPen );
1946 context.
painter()->setBrush( Qt::NoBrush );
1947 for (
const QgsTextBlock &block : document )
1949 const double baseLineOffset = metrics.
baselineOffset( blockIndex, mode );
1953 if ( block.blockFormat().hasBackground() )
1955 QBrush backgroundBrush = block.blockFormat().backgroundBrush();
1956 if ( !block.blockFormat().backgroundImagePath().isEmpty() )
1958 const QBrush backgroundImageBrush = createBrushForPath( context, block.blockFormat().backgroundImagePath() );
1959 if ( backgroundImageBrush.style() == Qt::BrushStyle::TexturePattern )
1960 backgroundBrush = backgroundImageBrush;
1963 context.
painter()->setBrush( backgroundBrush );
1965 QRectF( blockMetrics[blockIndex].backgroundXOffset, baseLineOffset - blockMaximumAscent, blockMetrics[blockIndex].backgroundWidth, blockMaximumDescent + blockMaximumAscent )
1970 int fragmentIndex = 0;
1972 for (
const QgsTextFragment &fragment : block )
1975 const double ascent = metrics.
fragmentAscent( blockIndex, fragmentIndex, mode );
1976 const double descent = metrics.
fragmentDescent( blockIndex, fragmentIndex, mode );
1986 if ( backgroundImageBrush.style() == Qt::BrushStyle::TexturePattern )
1987 backgroundBrush = backgroundImageBrush;
1990 context.
painter()->setBrush( backgroundBrush );
1991 context.
painter()->drawRect( QRectF( blockMetrics[blockIndex].xOffset + xOffset, baseLineOffset + verticalAlignOffset + yOffset - ascent, horizontalAdvance, ascent + descent ) );
1994 xOffset += horizontalAdvance;
2001 context.
painter()->setBrush( Qt::NoBrush );
2004 context.
painter()->rotate( -rotation );
2005 context.
painter()->translate( -component.origin );
2008void QgsTextRenderer::drawTextInternalHorizontal(
2013 const Component &component,
2026 double targetWidth = 0.0;
2031 targetWidth = documentSize.width();
2037 targetWidth = component.size.width();
2041 double verticalAlignOffset = 0;
2045 const double overallHeight = documentSize.height();
2046 switch ( vAlignment )
2053 verticalAlignOffset = ( component.size.height() - overallHeight ) * 0.5 + metrics.
blockVerticalMargin( -1 );
2057 verticalAlignOffset = ( component.size.height() - overallHeight ) + metrics.
blockVerticalMargin( -1 );
2067 const bool usePathsForText = usePathsToRender( context, format, document );
2071 std::unique_ptr< std::vector< DeferredRenderBlock > > deferredBlocks;
2076 const bool requiresMultiPassRendering
2079 if ( requiresMultiPassRendering )
2081 deferredBlocks = std::make_unique< std::vector< DeferredRenderBlock > >();
2082 deferredBlocks->reserve( document.
size() );
2087 const QVector< BlockMetrics > blockMetrics = calculateBlockMetrics( document, metrics, mode, targetWidth, hAlignment );
2091 renderDocumentBackgrounds( context, document, metrics, component, blockMetrics, mode, verticalAlignOffset, rotation );
2095 for (
const QgsTextBlock &block : document )
2097 const double blockHeight = metrics.
blockHeight( blockIndex );
2099 DeferredRenderBlock *deferredBlock =
nullptr;
2100 if ( requiresMultiPassRendering && deferredBlocks )
2102 deferredBlocks->emplace_back( DeferredRenderBlock() );
2103 deferredBlock = &deferredBlocks->back();
2104 deferredBlock->fragments.reserve( block.size() );
2107 QgsScopedQPainterState painterState( context.
painter() );
2109 context.
painter()->translate( component.origin );
2111 context.
painter()->rotate( rotation );
2116 maskPainter->save();
2117 maskPainter->translate( component.origin );
2119 maskPainter->rotate( rotation );
2122 const BlockMetrics thisBlockMetrics = blockMetrics[blockIndex];
2123 const double baseLineOffset = metrics.
baselineOffset( blockIndex, mode );
2125 const QPointF blockOrigin( thisBlockMetrics.xOffset, baseLineOffset + verticalAlignOffset );
2126 if ( deferredBlock )
2127 deferredBlock->origin = blockOrigin;
2129 context.
painter()->translate( blockOrigin );
2131 maskPainter->translate( blockOrigin );
2133 Component subComponent;
2134 subComponent.block = block;
2135 subComponent.blockIndex = blockIndex;
2136 subComponent.size = QSizeF( thisBlockMetrics.width, blockHeight );
2137 subComponent.offset = QPointF( 0.0, -metrics.
ascentOffset() );
2138 subComponent.rotation = -component.rotation * 180 / M_PI;
2139 subComponent.rotationOffset = 0.0;
2140 subComponent.extraWordSpacing = thisBlockMetrics.extraWordSpace * fontScale;
2141 subComponent.extraLetterSpacing = thisBlockMetrics.extraLetterSpace * fontScale;
2142 if ( deferredBlock )
2143 deferredBlock->component = subComponent;
2148 QgsTextRenderer::drawMask( context, subComponent, format, metrics, mode );
2152 const bool needsPaths = usePathsForText
2156 std::optional< QgsScopedRenderContextReferenceScaleOverride > referenceScaleOverride;
2161 referenceScaleOverride.emplace( QgsScopedRenderContextReferenceScaleOverride( context, -1.0 ) );
2164 referenceScaleOverride.reset();
2173 context.
painter()->scale( subComponent.dpiRatio, subComponent.dpiRatio );
2175 context.
painter()->scale( 1 / fontScale, 1 / fontScale );
2176 context.
painter()->setPen( Qt::NoPen );
2177 context.
painter()->setBrush( Qt::NoBrush );
2179 renderBlockHorizontal( block, blockIndex, metrics, context, format, context.
painter(), needsPaths, fontScale, thisBlockMetrics.extraWordSpace, thisBlockMetrics.extraLetterSpace, mode, deferredBlock );
2182 maskPainter->restore();
2188 if ( deferredBlocks )
2190 renderDeferredBlocks( context, format, components, *deferredBlocks, usePathsForText, fontScale, component, rotation );
2194void QgsTextRenderer::renderDeferredBlocks(
2198 const std::vector< DeferredRenderBlock > &deferredBlocks,
2199 bool usePathsForText,
2201 const Component &component,
2207 renderDeferredBuffer( context, format, components, deferredBlocks, fontScale, component, rotation );
2212 renderDeferredShadowForText( context, format, deferredBlocks, fontScale, component, rotation );
2221 renderDeferredText( context, deferredBlocks, usePathsForText, fontScale, component, rotation );
2225void QgsTextRenderer::renderDeferredShadowForText(
2226 QgsRenderContext &context,
const QgsTextFormat &format,
const std::vector< DeferredRenderBlock > &deferredBlocks,
double fontScale,
const Component &component,
double rotation
2229 QgsScopedQPainterState painterState( context.
painter() );
2231 context.
painter()->translate( component.origin );
2233 context.
painter()->rotate( rotation );
2235 context.
painter()->setPen( Qt::NoPen );
2236 context.
painter()->setBrush( Qt::NoBrush );
2238 for (
const DeferredRenderBlock &block : deferredBlocks )
2240 Component subComponent = block.component;
2242 QPainter painter( &subComponent.picture );
2243 painter.setPen( Qt::NoPen );
2244 painter.setBrush( Qt::NoBrush );
2245 painter.scale( 1 / fontScale, 1 / fontScale );
2247 for (
const DeferredRenderFragment &fragment : std::as_const( block.fragments ) )
2249 if ( !fragment.path.isEmpty() )
2251 painter.setBrush( fragment.color );
2252 painter.drawPath( fragment.path );
2256 painter.setPen( fragment.color );
2257 painter.setFont( fragment.font );
2258 painter.drawText( fragment.point, fragment.
text );
2263 subComponent.pictureBuffer = 1.0;
2264 subComponent.origin = QPointF( 0.0, 0.0 );
2265 const QRectF pictureBoundingRect = subComponent.picture.boundingRect();
2266 subComponent.size = pictureBoundingRect.size();
2267 subComponent.offset = QPointF( -pictureBoundingRect.left(), -pictureBoundingRect.height() - pictureBoundingRect.top() );
2269 context.
painter()->translate( block.origin );
2270 drawShadow( context, subComponent, format );
2271 context.
painter()->translate( -block.origin );
2275void QgsTextRenderer::renderDeferredBuffer(
2279 QgsScopedQPainterState painterState( context.
painter() );
2286 std::unique_ptr< QPicture > bufferPicture;
2287 std::unique_ptr< QPainter > bufferPainter;
2288 QPainter *prevPainter = context.
painter();
2289 if ( needsShadowOnBuffer )
2291 bufferPicture = std::make_unique< QPicture >();
2292 bufferPainter = std::make_unique< QPainter >( bufferPicture.get() );
2296 std::unique_ptr< QgsPaintEffect > tmpEffect;
2300 tmpEffect->begin( context );
2305 QPen pen( bufferColor );
2306 const QgsTextBufferSettings &buffer = format.
buffer();
2309 pen.setWidthF( penSize * fontScale );
2311 context.
painter()->setPen( pen );
2316 bufferColor.setAlpha( 0 );
2318 context.
painter()->setBrush( bufferColor );
2320 context.
painter()->translate( component.origin );
2322 context.
painter()->rotate( rotation );
2329 for (
const DeferredRenderBlock &block : deferredBlocks )
2331 context.
painter()->translate( block.origin );
2332 context.
painter()->scale( 1 / fontScale, 1 / fontScale );
2333 for (
const DeferredRenderFragment &fragment : std::as_const( block.fragments ) )
2335 context.
painter()->drawPath( fragment.path );
2337 context.
painter()->scale( fontScale, fontScale );
2338 context.
painter()->translate( -block.origin );
2343 tmpEffect->end( context );
2346 if ( needsShadowOnBuffer && bufferPicture )
2348 bufferPainter->end();
2349 bufferPainter.reset();
2352 QgsTextRenderer::Component bufferComponent = component;
2353 bufferComponent.origin = QPointF( 0.0, 0.0 );
2354 bufferComponent.picture = *bufferPicture;
2355 bufferComponent.pictureBuffer = penSize / 2.0;
2356 const QRectF bufferBoundingBox = bufferPicture->boundingRect();
2357 bufferComponent.size = bufferBoundingBox.size();
2358 bufferComponent.offset = QPointF( -bufferBoundingBox.left(), -bufferBoundingBox.height() - bufferBoundingBox.top() );
2360 drawShadow( context, bufferComponent, format );
2369 context.
painter()->scale( component.dpiRatio, component.dpiRatio );
2374void QgsTextRenderer::renderDeferredText(
2375 QgsRenderContext &context,
const std::vector< DeferredRenderBlock > &deferredBlocks,
bool usePathsForText,
double fontScale,
const Component &component,
double rotation
2378 QgsScopedQPainterState painterState( context.
painter() );
2380 context.
painter()->translate( component.origin );
2382 context.
painter()->rotate( rotation );
2384 context.
painter()->setPen( Qt::NoPen );
2385 context.
painter()->setBrush( Qt::NoBrush );
2388 for (
const DeferredRenderBlock &block : deferredBlocks )
2390 context.
painter()->translate( block.origin );
2391 context.
painter()->scale( 1 / fontScale, 1 / fontScale );
2393 for (
const DeferredRenderFragment &fragment : std::as_const( block.fragments ) )
2395 if ( usePathsForText )
2397 context.
painter()->setBrush( fragment.color );
2398 context.
painter()->drawPath( fragment.path );
2402 context.
painter()->setPen( fragment.color );
2403 context.
painter()->setFont( fragment.font );
2404 context.
painter()->drawText( fragment.point, fragment.
text );
2408 context.
painter()->scale( fontScale, fontScale );
2409 context.
painter()->translate( -block.origin );
2413void QgsTextRenderer::drawTextInternalVertical(
2418 const QgsTextRenderer::Component &component,
2428 const QStringList textLines = document.
toPlainText();
2430 std::optional< QgsScopedRenderContextReferenceScaleOverride > referenceScaleOverride;
2435 referenceScaleOverride.emplace( QgsScopedRenderContextReferenceScaleOverride( context, -1.0 ) );
2441 referenceScaleOverride.reset();
2444 const double actualTextWidth = documentSize.width();
2445 double textRectWidth = 0.0;
2451 textRectWidth = actualTextWidth;
2457 textRectWidth = component.size.width();
2461 int maxLineLength = 0;
2462 for (
const QString &line : std::as_const( textLines ) )
2464 maxLineLength = std::max( maxLineLength,
static_cast<int>( line.length() ) );
2467 const double actualLabelHeight = documentSize.height();
2472 for (
const QgsTextBlock &block : document )
2474 QgsScopedQPainterState painterState( context.
painter() );
2477 context.
painter()->translate( component.origin );
2479 context.
painter()->rotate( rotation );
2484 maskPainter->save();
2485 maskPainter->translate( component.origin );
2487 maskPainter->rotate( rotation );
2494 if ( adjustForAlignment )
2496 double hAlignmentOffset = 0;
2497 switch ( hAlignment )
2500 hAlignmentOffset = ( textRectWidth - actualTextWidth ) * 0.5;
2504 hAlignmentOffset = textRectWidth - actualTextWidth;
2518 xOffset += hAlignmentOffset;
2526 double yOffset = 0.0;
2532 if ( rotation >= -405 && rotation < -180 )
2536 else if ( rotation >= 0 && rotation < 45 )
2538 xOffset -= actualTextWidth;
2544 yOffset = -actualLabelHeight;
2549 yOffset = -actualLabelHeight;
2559 context.
painter()->translate( QPointF( xOffset, yOffset ) );
2561 double currentBlockYOffset = 0;
2562 int fragmentIndex = 0;
2563 for (
const QgsTextFragment &fragment : block )
2565 QgsScopedQPainterState fragmentPainterState( context.
painter() );
2570 const QFont fragmentFont = metrics.
fragmentFont( blockIndex, fragmentIndex );
2572 QFontMetricsF fragmentMetrics( fragmentFont );
2574 const double letterSpacing = fragmentFont.letterSpacing() / fontScale;
2575 const double labelHeight = fragmentMetrics.ascent() / fontScale + ( fragmentMetrics.ascent() / fontScale + letterSpacing ) * ( line.length() - 1 );
2577 Component subComponent;
2578 subComponent.block = QgsTextBlock( fragment );
2579 subComponent.blockIndex = blockIndex;
2580 subComponent.firstFragmentIndex = fragmentIndex;
2581 subComponent.size = QSizeF( blockMaximumCharacterWidth, labelHeight + fragmentMetrics.descent() / fontScale );
2582 subComponent.offset = QPointF( 0.0, currentBlockYOffset );
2583 subComponent.rotation = -component.rotation * 180 / M_PI;
2584 subComponent.rotationOffset = 0.0;
2591 QgsTextRenderer::drawMask( context, subComponent, format );
2597 currentBlockYOffset += QgsTextRenderer::drawBuffer( context, subComponent, format, metrics, mode );
2603 path.setFillRule( Qt::WindingFill );
2605 double partYOffset = 0.0;
2606 for (
const QString &part : parts )
2608 double partXOffset = ( blockMaximumCharacterWidth - ( fragmentMetrics.horizontalAdvance( part ) / fontScale - letterSpacing ) ) / 2;
2609 partYOffset += fragmentMetrics.ascent() / fontScale;
2610 path.addText( partXOffset * fontScale, partYOffset * fontScale, fragmentFont, part );
2611 partYOffset += letterSpacing;
2617 textp.begin( &textPict );
2618 textp.setPen( Qt::NoPen );
2621 textp.setBrush( textColor );
2622 textp.scale( 1 / fontScale, 1 / fontScale );
2623 textp.drawPath( path );
2634 subComponent.picture = textPict;
2635 subComponent.pictureBuffer = 0.0;
2636 subComponent.origin = QPointF( 0.0, currentBlockYOffset );
2637 const double prevY = subComponent.offset.y();
2638 subComponent.offset = QPointF( 0, -subComponent.size.height() );
2639 subComponent.useOrigin =
true;
2640 QgsTextRenderer::drawShadow( context, subComponent, format );
2641 subComponent.useOrigin =
false;
2642 subComponent.offset = QPointF( 0, prevY );
2652 context.
painter()->scale( subComponent.dpiRatio, subComponent.dpiRatio );
2656 context.
painter()->translate( 0, currentBlockYOffset );
2658 context.
painter()->drawPicture( 0, 0, textPict );
2659 currentBlockYOffset += partYOffset;
2665 maskPainter->restore();
2683 if ( pixelSize < 50 )
2684 return 200 / pixelSize;
2687 else if ( pixelSize > 200 )
2688 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 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