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 );
213 QPolygonF labelBaselineCurve = line;
222#if GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR < 11
223 if ( offsetFromLine < 0 )
226 std::unique_ptr< QgsLineString > reversed( offsetCurve->reversed() );
230 offsetCurve = std::move( reversed );
234 labelBaselineCurve = offsetCurve->asQPolygonF();
239 const QFont baseFont = format.
scaledFont( context, fontScale );
240 const double letterSpacing = baseFont.letterSpacing() / fontScale;
241 const double wordSpacing = baseFont.wordSpacing() / fontScale;
243 QStringList graphemes;
244 QVector< QgsTextCharacterFormat > graphemeFormats;
245 QVector< QgsTextDocumentMetrics > graphemeMetrics;
247 for (
const QgsTextBlock &block : std::as_const( document ) )
252 for (
const QString &grapheme : fragmentGraphemes )
254 graphemes.append( grapheme );
255 graphemeFormats.append( fragment.characterFormat() );
265 QVector< double > characterWidths( graphemes.count() );
266 QVector< double > characterHeights( graphemes.count() );
267 QVector< double > characterDescents( graphemes.count() );
268 QFont previousNonSuperSubScriptFont;
270 for (
int i = 0; i < graphemes.count(); i++ )
275 double graphemeFirstCharHorizontalAdvanceWithLetterSpacing = 0;
276 double graphemeFirstCharHorizontalAdvance = 0;
277 double graphemeHorizontalAdvance = 0;
278 double characterDescent = 0;
279 double characterHeight = 0;
282 QFont graphemeFont = baseFont;
286 previousNonSuperSubScriptFont = graphemeFont;
293 previousNonSuperSubScriptFont = graphemeFont;
314 previousNonSuperSubScriptFont = graphemeFont;
317 const QFontMetricsF graphemeFontMetrics( graphemeFont );
318 graphemeFirstCharHorizontalAdvance = graphemeFontMetrics.horizontalAdvance( QString( graphemes[i].at( 0 ) ) ) / fontScale;
319 graphemeFirstCharHorizontalAdvanceWithLetterSpacing = graphemeFontMetrics.horizontalAdvance( graphemes[i].at( 0 ) ) / fontScale + letterSpacing;
320 graphemeHorizontalAdvance = graphemeFontMetrics.horizontalAdvance( QString( graphemes[i] ) ) / fontScale;
321 characterDescent = graphemeFontMetrics.descent() / fontScale;
322 characterHeight = graphemeFontMetrics.height() / fontScale;
324 qreal wordSpaceFix = qreal( 0.0 );
325 if ( graphemes[i] ==
" "_L1 )
329 wordSpaceFix = ( nxt < graphemes.count() && graphemes[nxt] !=
" "_L1 ) ? wordSpacing : qreal( 0.0 );
334 if ( graphemes[i].length() == 1 && !
qgsDoubleNear( graphemeFirstCharHorizontalAdvance, graphemeFirstCharHorizontalAdvanceWithLetterSpacing ) )
337 wordSpaceFix -= wordSpacing;
340 const double charWidth = graphemeHorizontalAdvance + wordSpaceFix;
341 characterWidths[i] = charWidth;
342 characterHeights[i] = characterHeight;
343 characterDescents[i] = characterDescent;
346 QgsPrecalculatedTextMetrics metrics( graphemes, std::move( characterWidths ), std::move( characterHeights ), std::move( characterDescents ) );
349 std::unique_ptr< QgsTextRendererUtils::CurvePlacementProperties > placement
352 if ( placement->graphemePlacement.empty() )
359 QHash< int, QgsTextRenderer::Component > components;
360 components.reserve( placement->graphemePlacement.size() );
363 QgsTextRenderer::Component component;
364 component.origin = QPointF( grapheme.x, grapheme.y );
365 component.rotation = -grapheme.angle;
371 component.origin.rx() += verticalOffset * std::cos( grapheme.angle + M_PI_2 );
372 component.origin.ry() += verticalOffset * std::sin( grapheme.angle + M_PI_2 );
375 components.insert( grapheme.graphemeIndex, component );
383 const QgsTextRenderer::Component &component = components[grapheme.graphemeIndex];
393 const QgsTextRenderer::Component &component = components[grapheme.graphemeIndex];
402 const QgsTextRenderer::Component &component = components[grapheme.graphemeIndex];
440void QgsTextRenderer::drawParts(
459 component.dpiRatio = 1.0;
460 component.origin = rect.topLeft();
461 component.rotation = rotation;
462 component.size = rect.size();
463 component.hAlign = alignment;
471 double xc = rect.width() / 2.0;
472 double yc = rect.height() / 2.0;
474 double angle = -rotation;
475 double xd = xc * std::cos( angle ) - yc * std::sin( angle );
476 double yd = xc * std::sin( angle ) + yc * std::cos( angle );
478 component.center = QPointF( component.origin.x() + xd, component.origin.y() + yd );
482 component.center = rect.center();
485 switch ( vAlignment )
507 drawTextInternal( parts, context, format, component, document, metrics, alignment, vAlignment, mode );
522void QgsTextRenderer::drawParts(
540 component.dpiRatio = 1.0;
541 component.origin = origin;
542 component.rotation = rotation;
543 component.hAlign = alignment;
547 QgsTextRenderer::drawBackground( context, component, format, metrics, mode );
563 return QFontMetricsF( format.
scaledFont( context, scaleFactor ), context.
painter() ? context.
painter()->device() :
nullptr );
568 QPainter *p = context.
painter();
573 if ( component.rotation >= -315 && component.rotation < -90 )
577 else if ( component.rotation >= -90 && component.rotation < -45 )
587 QgsTextBufferSettings buffer = format.
buffer();
594 std::optional< QgsScopedRenderContextReferenceScaleOverride > referenceScaleOverride;
599 referenceScaleOverride.emplace( QgsScopedRenderContextReferenceScaleOverride( context, -1.0 ) );
605 referenceScaleOverride.reset();
608 path.setFillRule( Qt::WindingFill );
610 double height = component.size.height();
611 switch ( orientation )
622 double partYOffset = component.offset.y() * scaleFactor;
625 double partLastDescent = 0;
627 int fragmentIndex = 0;
628 for (
const QgsTextFragment &fragment : component.block )
630 const QFont fragmentFont = metrics.
fragmentFont( component.blockIndex, component.firstFragmentIndex + fragmentIndex );
631 const double letterSpacing = fragmentFont.letterSpacing() / scaleFactor;
633 const QFontMetricsF fragmentMetrics( fragmentFont );
635 const double fragmentYOffset = metrics.
fragmentVerticalOffset( component.blockIndex, fragmentIndex, mode );
638 for (
const QString &part : parts )
640 double partXOffset = ( blockMaximumCharacterWidth - ( fragmentMetrics.horizontalAdvance( part ) / scaleFactor - letterSpacing ) ) / 2;
641 partYOffset += fragmentMetrics.ascent() / scaleFactor;
642 path.addText( partXOffset, partYOffset + fragmentYOffset, fragmentFont, part );
643 partYOffset += letterSpacing;
645 partLastDescent = fragmentMetrics.descent() / scaleFactor;
649 height = partYOffset + partLastDescent;
650 advance = partYOffset - component.offset.y() * scaleFactor;
655 QColor bufferColor = buffer.
color();
656 bufferColor.setAlphaF( buffer.
opacity() );
657 QPen pen( bufferColor );
658 pen.setWidthF( penSize * scaleFactor );
660 QColor tmpColor( bufferColor );
664 tmpColor.setAlpha( 0 );
670 buffp.begin( &buffPict );
674 std::unique_ptr< QgsPaintEffect > tmpEffect( buffer.
paintEffect()->
clone() );
676 tmpEffect->begin( context );
677 context.
painter()->setPen( pen );
678 context.
painter()->setBrush( tmpColor );
679 if ( scaleFactor != 1.0 )
680 context.
painter()->scale( 1 / scaleFactor, 1 / scaleFactor );
681 context.
painter()->drawPath( path );
682 if ( scaleFactor != 1.0 )
683 context.
painter()->scale( scaleFactor, scaleFactor );
684 tmpEffect->end( context );
690 if ( scaleFactor != 1.0 )
691 buffp.scale( 1 / scaleFactor, 1 / scaleFactor );
693 buffp.setBrush( tmpColor );
694 buffp.drawPath( path );
700 QgsTextRenderer::Component bufferComponent = component;
701 bufferComponent.origin = QPointF( 0.0, 0.0 );
702 bufferComponent.picture = buffPict;
703 bufferComponent.pictureBuffer = penSize / 2.0;
704 bufferComponent.size.setHeight( height );
708 bufferComponent.offset.setY( -bufferComponent.size.height() );
710 drawShadow( context, bufferComponent, format );
713 QgsScopedQPainterState painterState( p );
718 p->setCompositionMode( buffer.
blendMode() );
722 p->scale( component.dpiRatio, component.dpiRatio );
724 p->drawPicture( 0, 0, buffPict );
726 return advance / scaleFactor;
731 QgsTextMaskSettings mask = format.
mask();
744 path.setFillRule( Qt::WindingFill );
751 std::optional< QgsScopedRenderContextReferenceScaleOverride > referenceScaleOverride;
756 referenceScaleOverride.emplace( QgsScopedRenderContextReferenceScaleOverride( context, -1.0 ) );
762 referenceScaleOverride.reset();
765 int fragmentIndex = 0;
766 for (
const QgsTextFragment &fragment : component.block )
770 const QFont fragmentFont = metrics.
fragmentFont( component.blockIndex, fragmentIndex );
772 const double fragmentYOffset = metrics.
fragmentVerticalOffset( component.blockIndex, fragmentIndex, mode );
773 path.addText( xOffset, fragmentYOffset, fragmentFont, fragment.
text() );
780 QColor bufferColor( Qt::gray );
781 bufferColor.setAlphaF( mask.
opacity() );
785 brush.setColor( bufferColor );
786 pen.setColor( bufferColor );
787 pen.setWidthF( penSize * scaleFactor );
790 QgsScopedQPainterState painterState( p );
794 p->scale( component.dpiRatio, component.dpiRatio );
797 QgsPainterSwapper swapper( context, p );
799 QgsEffectPainter effectPainter( context, mask.
paintEffect() );
800 if ( scaleFactor != 1.0 )
801 context.
painter()->scale( 1 / scaleFactor, 1 / scaleFactor );
802 context.
painter()->setPen( pen );
803 context.
painter()->setBrush( brush );
804 context.
painter()->drawPath( path );
805 if ( scaleFactor != 1.0 )
806 context.
painter()->scale( scaleFactor, scaleFactor );
811 if ( scaleFactor != 1.0 )
812 p->scale( 1 / scaleFactor, 1 / scaleFactor );
814 p->setBrush( brush );
816 if ( scaleFactor != 1.0 )
817 p->scale( scaleFactor, scaleFactor );
824 if ( doc.
size() == 0 )
827 return textWidth( context, format, doc );
846 for (
const QString &line : textLines )
850 lines.append(
wrappedText( context, line, maxLineWidth, format ) );
854 lines.append( line );
859 return textHeight( context, format, doc, mode );
866 bool isNullSize =
false;
867 const QFont baseFont = format.
scaledFont( context, scaleFactor, &isNullSize );
871 const QFontMetrics fm( baseFont );
872 const double height = ( character.isNull() ? fm.height() : fm.boundingRect( character ).height() ) / scaleFactor;
874 if ( !includeEffects )
877 double maxExtension = 0;
905 return height + maxExtension;
913 const QStringList multiLineSplit = text.split(
'\n' );
915 return currentTextWidth > width;
920 const QStringList lines = text.split(
'\n' );
921 QStringList outLines;
922 for (
const QString &line : lines )
927 const QStringList words = line.split(
' ' );
928 QStringList linesToProcess;
929 QString wordsInCurrentLine;
930 for (
const QString &word : words )
935 if ( !wordsInCurrentLine.isEmpty() )
936 linesToProcess << wordsInCurrentLine;
937 wordsInCurrentLine.clear();
938 linesToProcess << word;
942 if ( !wordsInCurrentLine.isEmpty() )
943 wordsInCurrentLine.append(
' ' );
944 wordsInCurrentLine.append( word );
947 if ( !wordsInCurrentLine.isEmpty() )
948 linesToProcess << wordsInCurrentLine;
950 for (
const QString &line : std::as_const( linesToProcess ) )
952 QString remainingText = line;
953 int lastPos = remainingText.lastIndexOf(
' ' );
954 while ( lastPos > -1 )
964 outLines << remainingText.left( lastPos );
965 remainingText = remainingText.mid( lastPos + 1 );
968 lastPos = remainingText.lastIndexOf(
' ', lastPos - 1 );
970 outLines << remainingText;
999 Component component =
c;
1000 QgsTextBackgroundSettings background = format.
background();
1002 QPainter *prevP = context.
painter();
1003 QPainter *p = context.
painter();
1004 std::unique_ptr< QgsPaintEffect > tmpEffect;
1008 tmpEffect->begin( context );
1017 const double originAdjustRotationRadians = -component.rotation;
1020 component.rotation = -( component.rotation * 180 / M_PI );
1025 component.rotation = 0.0;
1026 component.rotationOffset = background.
rotation();
1035 double width = documentSize.width();
1036 double height = documentSize.height();
1043 switch ( component.hAlign )
1047 component.center = QPointF( component.origin.x() + width / 2.0, component.origin.y() + height / 2.0 );
1051 component.center = QPointF( component.origin.x() + component.size.width() / 2.0, component.origin.y() + height / 2.0 );
1055 component.center = QPointF( component.origin.x() + component.size.width() - width / 2.0, component.origin.y() + height / 2.0 );
1062 bool isNullSize =
false;
1063 QFontMetricsF fm( format.
scaledFont( context, scaleFactor, &isNullSize ) );
1064 double originAdjust = isNullSize ? 0 : ( fm.ascent() / scaleFactor / 2.0 - fm.leading() / scaleFactor / 2.0 );
1065 switch ( component.hAlign )
1069 component.center = QPointF( component.origin.x() + width / 2.0, component.origin.y() - height / 2.0 + originAdjust );
1073 component.center = QPointF( component.origin.x(), component.origin.y() - height / 2.0 + originAdjust );
1077 component.center = QPointF( component.origin.x() - width / 2.0, component.origin.y() - height / 2.0 + originAdjust );
1084 const double dx = component.center.x() - component.origin.x();
1085 const double dy = component.center.y() - component.origin.y();
1086 component.center.setX( component.origin.x() + ( std::cos( originAdjustRotationRadians ) * dx - std::sin( originAdjustRotationRadians ) * dy ) );
1087 component.center.setY( component.origin.y() + ( std::sin( originAdjustRotationRadians ) * dx + std::cos( originAdjustRotationRadians ) * dy ) );
1097 component.size = QSizeF( width, height );
1102 switch ( background.
type() )
1115 double sizeOut = 0.0;
1117 QgsScopedRenderContextReferenceScaleOverride referenceScaleOverride( context, -1 );
1126 sizeOut = std::max( component.size.width(), component.size.height() );
1130 sizeOut += bufferSize * 2;
1136 if ( sizeOut < 1.0 )
1139 std::unique_ptr< QgsMarkerSymbol > renderedSymbol;
1143 map[u
"name"_s] = background.
svgFile().trimmed();
1144 map[u
"size"_s] = QString::number( sizeOut );
1146 map[u
"angle"_s] = QString::number( 0.0 );
1154 map[u
"fill"_s] = background.
fillColor().name();
1155 map[u
"outline"_s] = background.
strokeColor().name();
1156 map[u
"outline-width"_s] = QString::number( background.
strokeWidth() );
1161 QgsTextShadowSettings shadow = format.
shadow();
1163 QVariantMap shdwmap( map );
1164 shdwmap[u
"fill"_s] = shadow.
color().name();
1165 shdwmap[u
"outline"_s] = shadow.
color().name();
1166 shdwmap[u
"size"_s] = QString::number( sizeOut );
1171 svgp.begin( &svgPict );
1179 QgsRenderContext shdwContext;
1185 QgsSvgMarkerSymbolLayer *svgShdwM =
static_cast<QgsSvgMarkerSymbolLayer *
>( symShdwL.get() );
1188 svgShdwM->
renderPoint( QPointF( sizeOut / 2, -sizeOut / 2 ), svgShdwContext );
1191 component.picture = svgPict;
1193 component.pictureBuffer = 0.0;
1195 component.size = QSizeF( sizeOut, sizeOut );
1196 component.offset = QPointF( 0.0, 0.0 );
1199 QgsScopedQPainterState painterState( p );
1202 p->translate( component.center.x(), component.center.y() );
1203 p->rotate( component.rotation );
1206 p->translate( QPointF( xoff, yoff ) );
1207 p->rotate( component.rotationOffset );
1208 p->translate( -sizeOut / 2, sizeOut / 2 );
1210 drawShadow( context, component, format );
1212 renderedSymbol.reset();
1220 renderedSymbol->setSize( sizeOut );
1224 renderedSymbol->setOpacity( renderedSymbol->opacity() * background.
opacity() );
1227 QgsScopedQPainterState painterState( p );
1232 p->setCompositionMode( background.
blendMode() );
1234 p->translate( component.center.x(), component.center.y() );
1235 p->rotate( component.rotation );
1238 p->translate( QPointF( xoff, yoff ) );
1239 p->rotate( component.rotationOffset );
1243 renderedSymbol->renderPoint( QPointF( 0, 0 ), &f, context );
1244 renderedSymbol->stopRender( context );
1245 p->setCompositionMode( QPainter::CompositionMode_SourceOver );
1255 double w = component.size.width();
1256 double h = component.size.height();
1275 h = std::sqrt( std::pow( w, 2 ) + std::pow( h, 2 ) );
1281 h = h * M_SQRT1_2 * 2;
1282 w = w * M_SQRT1_2 * 2;
1288 w += bufferWidth * 2;
1289 h += bufferHeight * 2;
1293 QRectF rect( -w / 2.0, -h / 2.0, w, h );
1295 if ( rect.isNull() )
1298 QgsScopedQPainterState painterState( p );
1301 p->translate( QPointF( component.center.x(), component.center.y() ) );
1302 p->rotate( component.rotation );
1305 p->translate( QPointF( xoff, yoff ) );
1306 p->rotate( component.rotationOffset );
1312 QTransform t = QTransform::fromScale( 10, 10 );
1314 QTransform ti = t.inverted();
1320 path.addRoundedRect( rect, background.
radii().width(), background.
radii().height(), Qt::RelativeSize );
1326 path.addRoundedRect( rect, xRadius, yRadius );
1331 path.addEllipse( rect );
1333 QPolygonF tempPolygon = path.toFillPolygon( t );
1334 QPolygonF polygon = ti.map( tempPolygon );
1336 QPainter *oldp = context.
painter();
1339 shapep.begin( &shapePict );
1342 std::unique_ptr< QgsFillSymbol > renderedSymbol;
1344 renderedSymbol->setOpacity( renderedSymbol->opacity() * background.
opacity() );
1348 renderedSymbol->renderPolygon( polygon,
nullptr, &f, context );
1349 renderedSymbol->stopRender( context );
1356 component.picture = shapePict;
1359 component.size = rect.size();
1360 component.offset = QPointF( rect.width() / 2, -rect.height() / 2 );
1361 drawShadow( context, component, format );
1366 p->setCompositionMode( background.
blendMode() );
1370 p->scale( component.dpiRatio, component.dpiRatio );
1372 p->drawPicture( 0, 0, shapePict );
1373 p->setCompositionMode( QPainter::CompositionMode_SourceOver );
1380 tmpEffect->end( context );
1387 QgsTextShadowSettings shadow = format.
shadow();
1389 QPainter *p = context.
painter();
1390 const double componentWidth = component.size.width();
1391 const double componentHeight = component.size.height();
1392 const double xOffset = component.offset.x();
1393 const double yOffset = component.offset.y();
1394 double pictbuffer = component.pictureBuffer;
1402 radius /= ( mapUnits ? context.
scaleFactor() / component.dpiRatio : 1 );
1403 radius =
static_cast< int >( radius + 0.5 );
1407 double blurBufferClippingScale = 3.75;
1408 int blurbuffer = ( radius > 17 ? 16 : radius ) * blurBufferClippingScale;
1410 QImage blurImg( componentWidth + ( pictbuffer * 2.0 ) + ( blurbuffer * 2.0 ), componentHeight + ( pictbuffer * 2.0 ) + ( blurbuffer * 2.0 ), QImage::Format_ARGB32_Premultiplied );
1414 int minBlurImgSize = 1;
1418 int maxBlurImgSize = 40000;
1419 if ( blurImg.isNull() || ( blurImg.width() < minBlurImgSize || blurImg.height() < minBlurImgSize ) || ( blurImg.width() > maxBlurImgSize || blurImg.height() > maxBlurImgSize ) )
1422 blurImg.fill( QColor( Qt::transparent ).rgba() );
1424 if ( !pictp.begin( &blurImg ) )
1426 pictp.setRenderHints( QPainter::Antialiasing | QPainter::SmoothPixmapTransform );
1427 QPointF imgOffset( blurbuffer + pictbuffer + xOffset, blurbuffer + pictbuffer + componentHeight + yOffset );
1429 pictp.drawPicture( imgOffset, component.picture );
1432 pictp.setCompositionMode( QPainter::CompositionMode_SourceIn );
1433 pictp.fillRect( blurImg.rect(), shadow.
color() );
1437 if ( shadow.
blurRadius() > 0.0 && radius > 0 )
1445 picti.begin( &blurImg );
1446 picti.setBrush( Qt::Dense7Pattern );
1447 QPen imgPen( QColor( 0, 0, 255, 255 ) );
1448 imgPen.setWidth( 1 );
1449 picti.setPen( imgPen );
1450 picti.setOpacity( 0.1 );
1451 picti.drawRect( 0, 0, blurImg.width(), blurImg.height() );
1457 double angleRad = shadow.
offsetAngle() * M_PI / 180;
1465 angleRad -= ( component.rotation * M_PI / 180 + component.rotationOffset * M_PI / 180 );
1468 QPointF transPt( -offsetDist * std::cos( angleRad + M_PI_2 ), -offsetDist * std::sin( angleRad + M_PI_2 ) );
1474 p->setRenderHint( QPainter::SmoothPixmapTransform );
1477 p->setCompositionMode( shadow.
blendMode() );
1479 p->setOpacity( shadow.
opacity() );
1481 double scale = shadow.
scale() / 100.0;
1483 p->scale( scale, scale );
1484 if ( component.useOrigin )
1486 p->translate( component.origin.x(), component.origin.y() );
1488 p->translate( transPt );
1489 p->translate( -imgOffset.x(), -imgOffset.y() );
1490 p->drawImage( 0, 0, blurImg );
1497 p->setBrush( Qt::NoBrush );
1498 QPen imgPen( QColor( 255, 0, 0, 10 ) );
1499 imgPen.setWidth( 2 );
1500 imgPen.setStyle( Qt::DashLine );
1501 p->setPen( imgPen );
1502 p->scale( scale, scale );
1503 if ( component.useOrigin() )
1505 p->translate( component.origin().x(), component.origin().y() );
1507 p->translate( transPt );
1508 p->translate( -imgOffset.x(),
1510 p->drawRect( 0, 0, blurImg.width(), blurImg.height() );
1515 p->setBrush( Qt::NoBrush );
1516 QPen componentRectPen( QColor( 0, 255, 0, 70 ) );
1517 componentRectPen.setWidth( 1 );
1518 if ( component.useOrigin() )
1520 p->translate( component.origin().x(), component.origin().y() );
1522 p->setPen( componentRectPen );
1523 p->drawRect( QRect( -xOffset, -componentHeight - yOffset, componentWidth, componentHeight ) );
1529void QgsTextRenderer::drawTextInternal(
1533 const Component &component,
1548 std::optional< QgsScopedRenderContextReferenceScaleOverride > referenceScaleOverride;
1553 referenceScaleOverride.emplace( QgsScopedRenderContextReferenceScaleOverride( context, -1.0 ) );
1559 referenceScaleOverride.reset();
1561 double rotation = 0;
1562 const Qgis::TextOrientation orientation = calculateRotationAndOrientationForComponent( format, component, rotation );
1563 switch ( orientation )
1567 drawTextInternalHorizontal( context, format, components, mode, component, document, metrics, fontScale, alignment, vAlignment, rotation );
1576 drawTextInternalVertical( context, format,
Qgis::TextComponent::Buffer, mode, component, document, metrics, fontScale, alignment, vAlignment, rotation );
1578 drawTextInternalVertical( context, format,
Qgis::TextComponent::Text, mode, component, document, metrics, fontScale, alignment, vAlignment, rotation );
1584Qgis::TextOrientation QgsTextRenderer::calculateRotationAndOrientationForComponent(
const QgsTextFormat &format,
const QgsTextRenderer::Component &component,
double &rotation )
1586 rotation = -component.rotation * 180 / M_PI;
1593 if ( rotation >= -315 && rotation < -90 )
1598 else if ( rotation >= -90 && rotation < -45 )
1614void QgsTextRenderer::calculateExtraSpacingForLineJustification(
const double spaceToDistribute,
const QgsTextBlock &block,
double &extraWordSpace,
double &extraLetterSpace )
1617 QTextBoundaryFinder
finder( QTextBoundaryFinder::Word, blockText );
1619 int wordBoundaries = 0;
1620 while (
finder.toNextBoundary() != -1 )
1622 if (
finder.boundaryReasons() & QTextBoundaryFinder::StartOfItem )
1626 if ( wordBoundaries > 0 )
1629 extraWordSpace = spaceToDistribute / wordBoundaries;
1634 QTextBoundaryFinder
finder( QTextBoundaryFinder::Grapheme, blockText );
1637 int graphemeBoundaries = 0;
1638 while (
finder.toNextBoundary() != -1 )
1640 if (
finder.boundaryReasons() & QTextBoundaryFinder::StartOfItem )
1641 graphemeBoundaries++;
1644 if ( graphemeBoundaries > 0 )
1646 extraLetterSpace = spaceToDistribute / graphemeBoundaries;
1651void QgsTextRenderer::applyExtraSpacingForLineJustification( QFont &font,
double extraWordSpace,
double extraLetterSpace )
1653 const double prevWordSpace = font.wordSpacing();
1654 font.setWordSpacing( prevWordSpace + extraWordSpace );
1655 const double prevLetterSpace = font.letterSpacing();
1656 font.setLetterSpacing( QFont::AbsoluteSpacing, prevLetterSpace + extraLetterSpace );
1660void QgsTextRenderer::renderBlockHorizontal(
1667 bool forceRenderAsPaths,
1669 double extraWordSpace,
1670 double extraLetterSpace,
1672 DeferredRenderBlock *deferredRenderBlock
1678 int fragmentIndex = 0;
1679 for (
const QgsTextFragment &fragment : block )
1684 QFont fragmentFont = metrics.
fragmentFont( blockIndex, fragmentIndex );
1687 applyExtraSpacingForLineJustification( fragmentFont, extraWordSpace * fontScale, extraLetterSpace * fontScale );
1694 if ( deferredRenderBlock )
1696 DeferredRenderFragment renderFragment;
1697 renderFragment.color = textColor;
1698 if ( forceRenderAsPaths )
1700 renderFragment.path.setFillRule( Qt::WindingFill );
1701 renderFragment.path.addText( xOffset, yOffset, fragmentFont, fragment.
text() );
1703 renderFragment.font = fragmentFont;
1704 renderFragment.point = QPointF( xOffset, yOffset );
1705 renderFragment.text = fragment.
text();
1706 deferredRenderBlock->fragments.append( renderFragment );
1708 else if ( forceRenderAsPaths )
1710 painter->setBrush( textColor );
1712 path.setFillRule( Qt::WindingFill );
1713 path.addText( xOffset, yOffset, fragmentFont, fragment.
text() );
1714 painter->drawPath( path );
1718 painter->setPen( textColor );
1719 painter->setFont( fragmentFont );
1720 painter->drawText( QPointF( xOffset, yOffset ), fragment.
text() );
1723 else if ( fragment.
isImage() )
1725 bool fitsInCache =
false;
1727 const double imageHeight = metrics.
fragmentFixedHeight( blockIndex, fragmentIndex, mode ) * fontScale;
1731 QSize(
static_cast< int >( std::round( imageWidth ) ),
static_cast< int >( std::round( imageHeight ) ) ),
1738 const double yOffset = imageBaseline - image.height();
1739 if ( !image.isNull() )
1740 painter->drawImage( QPointF( xOffset, yOffset ), image );
1771 if ( format.
font().underline() || format.
font().overline() || format.
font().strikeOut() || std::any_of( document.begin(), document.end(), [](
const QgsTextBlock &block ) {
1772 return std::any_of( block.begin(), block.end(), []( const QgsTextFragment &fragment ) {
1773 return fragment.characterFormat().underline() == QgsTextCharacterFormat::BooleanValue::SetTrue
1774 || fragment.characterFormat().overline() == QgsTextCharacterFormat::BooleanValue::SetTrue
1775 || fragment.characterFormat().strikeOut() == QgsTextCharacterFormat::BooleanValue::SetTrue;
1788 return std::any_of( document.begin(), document.end(), [](
const QgsTextBlock &block ) {
1789 return std::any_of( block.begin(), block.end(), []( const QgsTextFragment &fragment ) { return fragment.isImage(); } );
1793QVector< QgsTextRenderer::BlockMetrics > QgsTextRenderer::calculateBlockMetrics(
1797 QVector< BlockMetrics > blockMetrics;
1798 blockMetrics.reserve( document.
size() );
1801 for (
const QgsTextBlock &block : document )
1804 if ( block.blockFormat().hasHorizontalAlignmentSet() )
1805 blockAlignment = block.blockFormat().horizontalAlignment();
1808 const bool isFinalLineInParagraph = ( blockIndex == document.size() - 1 ) || document.at( blockIndex + 1 ).toPlainText().trimmed().isEmpty();
1810 BlockMetrics thisBlockMetrics;
1812 thisBlockMetrics.width = metrics.
blockWidth( blockIndex );
1814 if ( adjustForAlignment )
1816 double blockWidthDiff = 0;
1817 switch ( blockAlignment )
1824 blockWidthDiff = targetWidth - thisBlockMetrics.width - metrics.
blockRightMargin( blockIndex );
1828 if ( !isFinalLineInParagraph && targetWidth > thisBlockMetrics.width )
1830 calculateExtraSpacingForLineJustification( targetWidth - thisBlockMetrics.width, block, thisBlockMetrics.extraWordSpace, thisBlockMetrics.extraLetterSpace );
1831 thisBlockMetrics.width = targetWidth;
1847 thisBlockMetrics.xOffset = blockWidthDiff;
1852 switch ( blockAlignment )
1855 thisBlockMetrics.xOffset = blockWidthDiff - targetWidth;
1859 thisBlockMetrics.xOffset = blockWidthDiff - targetWidth / 2.0;
1881 thisBlockMetrics.backgroundWidth = targetWidth;
1882 thisBlockMetrics.backgroundXOffset = 0;
1886 thisBlockMetrics.backgroundWidth = thisBlockMetrics.width;
1887 thisBlockMetrics.backgroundXOffset = thisBlockMetrics.xOffset;
1891 blockMetrics << thisBlockMetrics;
1894 return blockMetrics;
1897QBrush QgsTextRenderer::createBrushForPath(
QgsRenderContext &context,
const QString &path )
1899 bool fitsInCache =
false;
1903 const QSizeF originalSizeMmAt96Dpi = imageSize / 3.7795275590551185;
1905 const double imageWidth = originalSizeMmAt96Dpi.width() * pixelsPerMm;
1906 const double imageHeight = originalSizeMmAt96Dpi.height() * pixelsPerMm;
1908 if ( imageWidth == 0 || imageHeight == 0 )
1914 if ( !image.isNull() )
1916 res.setTextureImage( image );
1921void QgsTextRenderer::renderDocumentBackgrounds(
1925 const Component &component,
1926 const QVector< QgsTextRenderer::BlockMetrics > &blockMetrics,
1928 double verticalAlignOffset,
1933 context.
painter()->translate( component.origin );
1935 context.
painter()->rotate( rotation );
1937 context.
painter()->setPen( Qt::NoPen );
1938 context.
painter()->setBrush( Qt::NoBrush );
1939 for (
const QgsTextBlock &block : document )
1941 const double baseLineOffset = metrics.
baselineOffset( blockIndex, mode );
1945 if ( block.blockFormat().hasBackground() )
1947 QBrush backgroundBrush = block.blockFormat().backgroundBrush();
1948 if ( !block.blockFormat().backgroundImagePath().isEmpty() )
1950 const QBrush backgroundImageBrush = createBrushForPath( context, block.blockFormat().backgroundImagePath() );
1951 if ( backgroundImageBrush.style() == Qt::BrushStyle::TexturePattern )
1952 backgroundBrush = backgroundImageBrush;
1955 context.
painter()->setBrush( backgroundBrush );
1957 QRectF( blockMetrics[blockIndex].backgroundXOffset, baseLineOffset - blockMaximumAscent, blockMetrics[blockIndex].backgroundWidth, blockMaximumDescent + blockMaximumAscent )
1962 int fragmentIndex = 0;
1964 for (
const QgsTextFragment &fragment : block )
1967 const double ascent = metrics.
fragmentAscent( blockIndex, fragmentIndex, mode );
1968 const double descent = metrics.
fragmentDescent( blockIndex, fragmentIndex, mode );
1978 if ( backgroundImageBrush.style() == Qt::BrushStyle::TexturePattern )
1979 backgroundBrush = backgroundImageBrush;
1982 context.
painter()->setBrush( backgroundBrush );
1983 context.
painter()->drawRect( QRectF( blockMetrics[blockIndex].xOffset + xOffset, baseLineOffset + verticalAlignOffset + yOffset - ascent, horizontalAdvance, ascent + descent ) );
1986 xOffset += horizontalAdvance;
1993 context.
painter()->setBrush( Qt::NoBrush );
1996 context.
painter()->rotate( -rotation );
1997 context.
painter()->translate( -component.origin );
2000void QgsTextRenderer::drawTextInternalHorizontal(
2005 const Component &component,
2018 double targetWidth = 0.0;
2023 targetWidth = documentSize.width();
2029 targetWidth = component.size.width();
2033 double verticalAlignOffset = 0;
2037 const double overallHeight = documentSize.height();
2038 switch ( vAlignment )
2045 verticalAlignOffset = ( component.size.height() - overallHeight ) * 0.5 + metrics.
blockVerticalMargin( -1 );
2049 verticalAlignOffset = ( component.size.height() - overallHeight ) + metrics.
blockVerticalMargin( -1 );
2059 const bool usePathsForText = usePathsToRender( context, format, document );
2063 std::unique_ptr< std::vector< DeferredRenderBlock > > deferredBlocks;
2068 const bool requiresMultiPassRendering
2071 if ( requiresMultiPassRendering )
2073 deferredBlocks = std::make_unique< std::vector< DeferredRenderBlock > >();
2074 deferredBlocks->reserve( document.
size() );
2079 const QVector< BlockMetrics > blockMetrics = calculateBlockMetrics( document, metrics, mode, targetWidth, hAlignment );
2083 renderDocumentBackgrounds( context, document, metrics, component, blockMetrics, mode, verticalAlignOffset, rotation );
2087 for (
const QgsTextBlock &block : document )
2089 const double blockHeight = metrics.
blockHeight( blockIndex );
2091 DeferredRenderBlock *deferredBlock =
nullptr;
2092 if ( requiresMultiPassRendering && deferredBlocks )
2094 deferredBlocks->emplace_back( DeferredRenderBlock() );
2095 deferredBlock = &deferredBlocks->back();
2096 deferredBlock->fragments.reserve( block.size() );
2099 QgsScopedQPainterState painterState( context.
painter() );
2101 context.
painter()->translate( component.origin );
2103 context.
painter()->rotate( rotation );
2108 maskPainter->save();
2109 maskPainter->translate( component.origin );
2111 maskPainter->rotate( rotation );
2114 const BlockMetrics thisBlockMetrics = blockMetrics[blockIndex];
2115 const double baseLineOffset = metrics.
baselineOffset( blockIndex, mode );
2117 const QPointF blockOrigin( thisBlockMetrics.xOffset, baseLineOffset + verticalAlignOffset );
2118 if ( deferredBlock )
2119 deferredBlock->origin = blockOrigin;
2121 context.
painter()->translate( blockOrigin );
2123 maskPainter->translate( blockOrigin );
2125 Component subComponent;
2126 subComponent.block = block;
2127 subComponent.blockIndex = blockIndex;
2128 subComponent.size = QSizeF( thisBlockMetrics.width, blockHeight );
2129 subComponent.offset = QPointF( 0.0, -metrics.
ascentOffset() );
2130 subComponent.rotation = -component.rotation * 180 / M_PI;
2131 subComponent.rotationOffset = 0.0;
2132 subComponent.extraWordSpacing = thisBlockMetrics.extraWordSpace * fontScale;
2133 subComponent.extraLetterSpacing = thisBlockMetrics.extraLetterSpace * fontScale;
2134 if ( deferredBlock )
2135 deferredBlock->component = subComponent;
2140 QgsTextRenderer::drawMask( context, subComponent, format, metrics, mode );
2144 const bool needsPaths = usePathsForText
2148 std::optional< QgsScopedRenderContextReferenceScaleOverride > referenceScaleOverride;
2153 referenceScaleOverride.emplace( QgsScopedRenderContextReferenceScaleOverride( context, -1.0 ) );
2156 referenceScaleOverride.reset();
2165 context.
painter()->scale( subComponent.dpiRatio, subComponent.dpiRatio );
2167 context.
painter()->scale( 1 / fontScale, 1 / fontScale );
2168 context.
painter()->setPen( Qt::NoPen );
2169 context.
painter()->setBrush( Qt::NoBrush );
2171 renderBlockHorizontal( block, blockIndex, metrics, context, format, context.
painter(), needsPaths, fontScale, thisBlockMetrics.extraWordSpace, thisBlockMetrics.extraLetterSpace, mode, deferredBlock );
2174 maskPainter->restore();
2180 if ( deferredBlocks )
2182 renderDeferredBlocks( context, format, components, *deferredBlocks, usePathsForText, fontScale, component, rotation );
2186void QgsTextRenderer::renderDeferredBlocks(
2190 const std::vector< DeferredRenderBlock > &deferredBlocks,
2191 bool usePathsForText,
2193 const Component &component,
2199 renderDeferredBuffer( context, format, components, deferredBlocks, fontScale, component, rotation );
2204 renderDeferredShadowForText( context, format, deferredBlocks, fontScale, component, rotation );
2213 renderDeferredText( context, deferredBlocks, usePathsForText, fontScale, component, rotation );
2217void QgsTextRenderer::renderDeferredShadowForText(
2218 QgsRenderContext &context,
const QgsTextFormat &format,
const std::vector< DeferredRenderBlock > &deferredBlocks,
double fontScale,
const Component &component,
double rotation
2221 QgsScopedQPainterState painterState( context.
painter() );
2223 context.
painter()->translate( component.origin );
2225 context.
painter()->rotate( rotation );
2227 context.
painter()->setPen( Qt::NoPen );
2228 context.
painter()->setBrush( Qt::NoBrush );
2230 for (
const DeferredRenderBlock &block : deferredBlocks )
2232 Component subComponent = block.component;
2234 QPainter painter( &subComponent.picture );
2235 painter.setPen( Qt::NoPen );
2236 painter.setBrush( Qt::NoBrush );
2237 painter.scale( 1 / fontScale, 1 / fontScale );
2239 for (
const DeferredRenderFragment &fragment : std::as_const( block.fragments ) )
2241 if ( !fragment.path.isEmpty() )
2243 painter.setBrush( fragment.color );
2244 painter.drawPath( fragment.path );
2248 painter.setPen( fragment.color );
2249 painter.setFont( fragment.font );
2250 painter.drawText( fragment.point, fragment.
text );
2255 subComponent.pictureBuffer = 1.0;
2256 subComponent.origin = QPointF( 0.0, 0.0 );
2257 const QRectF pictureBoundingRect = subComponent.picture.boundingRect();
2258 subComponent.size = pictureBoundingRect.size();
2259 subComponent.offset = QPointF( -pictureBoundingRect.left(), -pictureBoundingRect.height() - pictureBoundingRect.top() );
2261 context.
painter()->translate( block.origin );
2262 drawShadow( context, subComponent, format );
2263 context.
painter()->translate( -block.origin );
2267void QgsTextRenderer::renderDeferredBuffer(
2271 QgsScopedQPainterState painterState( context.
painter() );
2278 std::unique_ptr< QPicture > bufferPicture;
2279 std::unique_ptr< QPainter > bufferPainter;
2280 QPainter *prevPainter = context.
painter();
2281 if ( needsShadowOnBuffer )
2283 bufferPicture = std::make_unique< QPicture >();
2284 bufferPainter = std::make_unique< QPainter >( bufferPicture.get() );
2288 std::unique_ptr< QgsPaintEffect > tmpEffect;
2292 tmpEffect->begin( context );
2297 QPen pen( bufferColor );
2298 const QgsTextBufferSettings &buffer = format.
buffer();
2301 pen.setWidthF( penSize * fontScale );
2303 context.
painter()->setPen( pen );
2308 bufferColor.setAlpha( 0 );
2310 context.
painter()->setBrush( bufferColor );
2312 context.
painter()->translate( component.origin );
2314 context.
painter()->rotate( rotation );
2321 for (
const DeferredRenderBlock &block : deferredBlocks )
2323 context.
painter()->translate( block.origin );
2324 context.
painter()->scale( 1 / fontScale, 1 / fontScale );
2325 for (
const DeferredRenderFragment &fragment : std::as_const( block.fragments ) )
2327 context.
painter()->drawPath( fragment.path );
2329 context.
painter()->scale( fontScale, fontScale );
2330 context.
painter()->translate( -block.origin );
2335 tmpEffect->end( context );
2338 if ( needsShadowOnBuffer && bufferPicture )
2340 bufferPainter->end();
2341 bufferPainter.reset();
2344 QgsTextRenderer::Component bufferComponent = component;
2345 bufferComponent.origin = QPointF( 0.0, 0.0 );
2346 bufferComponent.picture = *bufferPicture;
2347 bufferComponent.pictureBuffer = penSize / 2.0;
2348 const QRectF bufferBoundingBox = bufferPicture->boundingRect();
2349 bufferComponent.size = bufferBoundingBox.size();
2350 bufferComponent.offset = QPointF( -bufferBoundingBox.left(), -bufferBoundingBox.height() - bufferBoundingBox.top() );
2352 drawShadow( context, bufferComponent, format );
2361 context.
painter()->scale( component.dpiRatio, component.dpiRatio );
2366void QgsTextRenderer::renderDeferredText(
2367 QgsRenderContext &context,
const std::vector< DeferredRenderBlock > &deferredBlocks,
bool usePathsForText,
double fontScale,
const Component &component,
double rotation
2370 QgsScopedQPainterState painterState( context.
painter() );
2372 context.
painter()->translate( component.origin );
2374 context.
painter()->rotate( rotation );
2376 context.
painter()->setPen( Qt::NoPen );
2377 context.
painter()->setBrush( Qt::NoBrush );
2380 for (
const DeferredRenderBlock &block : deferredBlocks )
2382 context.
painter()->translate( block.origin );
2383 context.
painter()->scale( 1 / fontScale, 1 / fontScale );
2385 for (
const DeferredRenderFragment &fragment : std::as_const( block.fragments ) )
2387 if ( usePathsForText )
2389 context.
painter()->setBrush( fragment.color );
2390 context.
painter()->drawPath( fragment.path );
2394 context.
painter()->setPen( fragment.color );
2395 context.
painter()->setFont( fragment.font );
2396 context.
painter()->drawText( fragment.point, fragment.
text );
2400 context.
painter()->scale( fontScale, fontScale );
2401 context.
painter()->translate( -block.origin );
2405void QgsTextRenderer::drawTextInternalVertical(
2410 const QgsTextRenderer::Component &component,
2420 const QStringList textLines = document.
toPlainText();
2422 std::optional< QgsScopedRenderContextReferenceScaleOverride > referenceScaleOverride;
2427 referenceScaleOverride.emplace( QgsScopedRenderContextReferenceScaleOverride( context, -1.0 ) );
2433 referenceScaleOverride.reset();
2436 const double actualTextWidth = documentSize.width();
2437 double textRectWidth = 0.0;
2443 textRectWidth = actualTextWidth;
2449 textRectWidth = component.size.width();
2453 int maxLineLength = 0;
2454 for (
const QString &line : std::as_const( textLines ) )
2456 maxLineLength = std::max( maxLineLength,
static_cast<int>( line.length() ) );
2459 const double actualLabelHeight = documentSize.height();
2464 for (
const QgsTextBlock &block : document )
2466 QgsScopedQPainterState painterState( context.
painter() );
2469 context.
painter()->translate( component.origin );
2471 context.
painter()->rotate( rotation );
2476 maskPainter->save();
2477 maskPainter->translate( component.origin );
2479 maskPainter->rotate( rotation );
2486 if ( adjustForAlignment )
2488 double hAlignmentOffset = 0;
2489 switch ( hAlignment )
2492 hAlignmentOffset = ( textRectWidth - actualTextWidth ) * 0.5;
2496 hAlignmentOffset = textRectWidth - actualTextWidth;
2510 xOffset += hAlignmentOffset;
2518 double yOffset = 0.0;
2524 if ( rotation >= -405 && rotation < -180 )
2528 else if ( rotation >= 0 && rotation < 45 )
2530 xOffset -= actualTextWidth;
2536 yOffset = -actualLabelHeight;
2541 yOffset = -actualLabelHeight;
2551 context.
painter()->translate( QPointF( xOffset, yOffset ) );
2553 double currentBlockYOffset = 0;
2554 int fragmentIndex = 0;
2555 for (
const QgsTextFragment &fragment : block )
2557 QgsScopedQPainterState fragmentPainterState( context.
painter() );
2562 const QFont fragmentFont = metrics.
fragmentFont( blockIndex, fragmentIndex );
2564 QFontMetricsF fragmentMetrics( fragmentFont );
2566 const double letterSpacing = fragmentFont.letterSpacing() / fontScale;
2567 const double labelHeight = fragmentMetrics.ascent() / fontScale + ( fragmentMetrics.ascent() / fontScale + letterSpacing ) * ( line.length() - 1 );
2569 Component subComponent;
2570 subComponent.block = QgsTextBlock( fragment );
2571 subComponent.blockIndex = blockIndex;
2572 subComponent.firstFragmentIndex = fragmentIndex;
2573 subComponent.size = QSizeF( blockMaximumCharacterWidth, labelHeight + fragmentMetrics.descent() / fontScale );
2574 subComponent.offset = QPointF( 0.0, currentBlockYOffset );
2575 subComponent.rotation = -component.rotation * 180 / M_PI;
2576 subComponent.rotationOffset = 0.0;
2583 QgsTextRenderer::drawMask( context, subComponent, format );
2589 currentBlockYOffset += QgsTextRenderer::drawBuffer( context, subComponent, format, metrics, mode );
2595 path.setFillRule( Qt::WindingFill );
2597 double partYOffset = 0.0;
2598 for (
const QString &part : parts )
2600 double partXOffset = ( blockMaximumCharacterWidth - ( fragmentMetrics.horizontalAdvance( part ) / fontScale - letterSpacing ) ) / 2;
2601 partYOffset += fragmentMetrics.ascent() / fontScale;
2602 path.addText( partXOffset * fontScale, partYOffset * fontScale, fragmentFont, part );
2603 partYOffset += letterSpacing;
2609 textp.begin( &textPict );
2610 textp.setPen( Qt::NoPen );
2613 textp.setBrush( textColor );
2614 textp.scale( 1 / fontScale, 1 / fontScale );
2615 textp.drawPath( path );
2626 subComponent.picture = textPict;
2627 subComponent.pictureBuffer = 0.0;
2628 subComponent.origin = QPointF( 0.0, currentBlockYOffset );
2629 const double prevY = subComponent.offset.y();
2630 subComponent.offset = QPointF( 0, -subComponent.size.height() );
2631 subComponent.useOrigin =
true;
2632 QgsTextRenderer::drawShadow( context, subComponent, format );
2633 subComponent.useOrigin =
false;
2634 subComponent.offset = QPointF( 0, prevY );
2644 context.
painter()->scale( subComponent.dpiRatio, subComponent.dpiRatio );
2648 context.
painter()->translate( 0, currentBlockYOffset );
2650 context.
painter()->drawPicture( 0, 0, textPict );
2651 currentBlockYOffset += partYOffset;
2657 maskPainter->restore();
2675 if ( pixelSize < 50 )
2676 return 200 / pixelSize;
2679 else if ( pixelSize > 200 )
2680 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