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 );
97 drawDocument( rect, lFormat, metrics.
document(), metrics, context, alignment, vAlignment, rotation, mode, flags );
102 const QgsTextFormat tmpFormat = updateShadowPosition( format );
120 drawParts( rect, rotation, horizontalAlignment, verticalAlignment, document, metrics, context, tmpFormat, components, mode );
128 lFormat = updateShadowPosition( lFormat );
140 const QgsTextFormat lFormat = updateShadowPosition( _format );
159 drawParts( point, rotation, alignment, document, metrics, context, lFormat, components, mode );
167 lFormat = updateShadowPosition( lFormat );
174 drawDocumentOnLine( line, lFormat, document, context, offsetAlongLine, offsetFromLine, flags );
180 QPolygonF labelBaselineCurve = line;
189#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<11
190 if ( offsetFromLine < 0 )
193 std::unique_ptr < QgsLineString > reversed( offsetCurve->reversed() );
197 offsetCurve = std::move( reversed );
201 labelBaselineCurve = offsetCurve->asQPolygonF();
206 const QFont baseFont = format.
scaledFont( context, fontScale );
207 const double letterSpacing = baseFont.letterSpacing() / fontScale;
208 const double wordSpacing = baseFont.wordSpacing() / fontScale;
210 QStringList graphemes;
211 QVector< QgsTextCharacterFormat > graphemeFormats;
212 QVector< QgsTextDocumentMetrics > graphemeMetrics;
214 for (
const QgsTextBlock &block : std::as_const( document ) )
219 for (
const QString &grapheme : fragmentGraphemes )
221 graphemes.append( grapheme );
222 graphemeFormats.append( fragment.characterFormat() );
232 QVector< double > characterWidths( graphemes.count() );
233 QVector< double > characterHeights( graphemes.count() );
234 QVector< double > characterDescents( graphemes.count() );
235 QFont previousNonSuperSubScriptFont;
237 for (
int i = 0; i < graphemes.count(); i++ )
242 double graphemeFirstCharHorizontalAdvanceWithLetterSpacing = 0;
243 double graphemeFirstCharHorizontalAdvance = 0;
244 double graphemeHorizontalAdvance = 0;
245 double characterDescent = 0;
246 double characterHeight = 0;
249 QFont graphemeFont = baseFont;
253 previousNonSuperSubScriptFont = graphemeFont;
260 previousNonSuperSubScriptFont = graphemeFont;
281 previousNonSuperSubScriptFont = graphemeFont;
284 const QFontMetricsF graphemeFontMetrics( graphemeFont );
285 graphemeFirstCharHorizontalAdvance = graphemeFontMetrics.horizontalAdvance( QString( graphemes[i].at( 0 ) ) ) / fontScale;
286 graphemeFirstCharHorizontalAdvanceWithLetterSpacing = graphemeFontMetrics.horizontalAdvance( graphemes[i].at( 0 ) ) / fontScale + letterSpacing;
287 graphemeHorizontalAdvance = graphemeFontMetrics.horizontalAdvance( QString( graphemes[i] ) ) / fontScale;
288 characterDescent = graphemeFontMetrics.descent() / fontScale;
289 characterHeight = graphemeFontMetrics.height() / fontScale;
291 qreal wordSpaceFix = qreal( 0.0 );
292 if ( graphemes[i] ==
" "_L1 )
296 wordSpaceFix = ( nxt < graphemes.count() && graphemes[nxt] !=
" "_L1 ) ? wordSpacing : qreal( 0.0 );
301 if ( graphemes[i].length() == 1 &&
302 !
qgsDoubleNear( graphemeFirstCharHorizontalAdvance, graphemeFirstCharHorizontalAdvanceWithLetterSpacing ) )
305 wordSpaceFix -= wordSpacing;
308 const double charWidth = graphemeHorizontalAdvance + wordSpaceFix;
309 characterWidths[i] = charWidth;
310 characterHeights[i] = characterHeight;
311 characterDescents[i] = characterDescent;
314 QgsPrecalculatedTextMetrics metrics( graphemes, std::move( characterWidths ), std::move( characterHeights ), std::move( characterDescents ) );
318 metrics, labelBaselineCurve, offsetAlongLine,
324 if ( placement->graphemePlacement.empty() )
331 QHash< int, QgsTextRenderer::Component > components;
332 components.reserve( placement->graphemePlacement.size() );
335 QgsTextRenderer::Component component;
336 component.origin = QPointF( grapheme.x, grapheme.y );
337 component.rotation = -grapheme.angle;
343 component.origin.rx() += verticalOffset * std::cos( grapheme.angle + M_PI_2 );
344 component.origin.ry() += verticalOffset * std::sin( grapheme.angle + M_PI_2 );
347 components.insert( grapheme.graphemeIndex, component );
355 const QgsTextRenderer::Component &component = components[grapheme.graphemeIndex ];
365 const QgsTextRenderer::Component &component = components[grapheme.graphemeIndex ];
382 const QgsTextRenderer::Component &component = components[grapheme.graphemeIndex ];
435 component.dpiRatio = 1.0;
436 component.origin = rect.topLeft();
437 component.rotation = rotation;
438 component.size = rect.size();
439 component.hAlign = alignment;
447 double xc = rect.width() / 2.0;
448 double yc = rect.height() / 2.0;
450 double angle = -rotation;
451 double xd = xc * std::cos( angle ) - yc * std::sin( angle );
452 double yd = xc * std::sin( angle ) + yc * std::cos( angle );
454 component.center = QPointF( component.origin.x() + xd, component.origin.y() + yd );
458 component.center = rect.center();
461 switch ( vAlignment )
483 drawTextInternal( parts, context, format, component,
485 alignment, vAlignment, mode );
506 component.dpiRatio = 1.0;
507 component.origin = origin;
508 component.rotation = rotation;
509 component.hAlign = alignment;
513 QgsTextRenderer::drawBackground( context, component, format, metrics, mode );
523 drawTextInternal( parts, context, format, component,
533 return QFontMetricsF( format.
scaledFont( context, scaleFactor ), context.
painter() ? context.
painter()->device() :
nullptr );
540 QPainter *p = context.
painter();
545 if ( component.rotation >= -315 && component.rotation < -90 )
549 else if ( component.rotation >= -90 && component.rotation < -45 )
559 QgsTextBufferSettings buffer = format.
buffer();
567 std::optional< QgsScopedRenderContextReferenceScaleOverride > referenceScaleOverride;
572 referenceScaleOverride.emplace( QgsScopedRenderContextReferenceScaleOverride( context, -1.0 ) );
578 referenceScaleOverride.reset();
581 path.setFillRule( Qt::WindingFill );
583 double height = component.size.height();
584 switch ( orientation )
595 double partYOffset = component.offset.y() * scaleFactor;
598 double partLastDescent = 0;
600 int fragmentIndex = 0;
601 for (
const QgsTextFragment &fragment : component.block )
603 const QFont fragmentFont = metrics.
fragmentFont( component.blockIndex, component.firstFragmentIndex + fragmentIndex );
604 const double letterSpacing = fragmentFont.letterSpacing() / scaleFactor;
606 const QFontMetricsF fragmentMetrics( fragmentFont );
608 const double fragmentYOffset = metrics.
fragmentVerticalOffset( component.blockIndex, fragmentIndex, mode );
611 for (
const QString &part : parts )
613 double partXOffset = ( blockMaximumCharacterWidth - ( fragmentMetrics.horizontalAdvance( part ) / scaleFactor - letterSpacing ) ) / 2;
614 partYOffset += fragmentMetrics.ascent() / scaleFactor;
615 path.addText( partXOffset, partYOffset + fragmentYOffset, fragmentFont, part );
616 partYOffset += letterSpacing;
618 partLastDescent = fragmentMetrics.descent() / scaleFactor;
622 height = partYOffset + partLastDescent;
623 advance = partYOffset - component.offset.y() * scaleFactor;
628 QColor bufferColor = buffer.
color();
629 bufferColor.setAlphaF( buffer.
opacity() );
630 QPen pen( bufferColor );
631 pen.setWidthF( penSize * scaleFactor );
633 QColor tmpColor( bufferColor );
637 tmpColor.setAlpha( 0 );
643 buffp.begin( &buffPict );
647 std::unique_ptr< QgsPaintEffect > tmpEffect( buffer.
paintEffect()->
clone() );
649 tmpEffect->begin( context );
650 context.
painter()->setPen( pen );
651 context.
painter()->setBrush( tmpColor );
652 if ( scaleFactor != 1.0 )
653 context.
painter()->scale( 1 / scaleFactor, 1 / scaleFactor );
654 context.
painter()->drawPath( path );
655 if ( scaleFactor != 1.0 )
656 context.
painter()->scale( scaleFactor, scaleFactor );
657 tmpEffect->end( context );
663 if ( scaleFactor != 1.0 )
664 buffp.scale( 1 / scaleFactor, 1 / scaleFactor );
666 buffp.setBrush( tmpColor );
667 buffp.drawPath( path );
673 QgsTextRenderer::Component bufferComponent = component;
674 bufferComponent.origin = QPointF( 0.0, 0.0 );
675 bufferComponent.picture = buffPict;
676 bufferComponent.pictureBuffer = penSize / 2.0;
677 bufferComponent.size.setHeight( height );
681 bufferComponent.offset.setY( - bufferComponent.size.height() );
683 drawShadow( context, bufferComponent, format );
686 QgsScopedQPainterState painterState( p );
691 p->setCompositionMode( buffer.
blendMode() );
695 p->scale( component.dpiRatio, component.dpiRatio );
697 p->drawPicture( 0, 0, buffPict );
699 return advance / scaleFactor;
705 QgsTextMaskSettings mask = format.
mask();
719 path.setFillRule( Qt::WindingFill );
726 std::optional< QgsScopedRenderContextReferenceScaleOverride > referenceScaleOverride;
731 referenceScaleOverride.emplace( QgsScopedRenderContextReferenceScaleOverride( context, -1.0 ) );
737 referenceScaleOverride.reset();
740 int fragmentIndex = 0;
741 for (
const QgsTextFragment &fragment : component.block )
745 const QFont fragmentFont = metrics.
fragmentFont( component.blockIndex, fragmentIndex );
747 const double fragmentYOffset = metrics.
fragmentVerticalOffset( component.blockIndex, fragmentIndex, mode );
748 path.addText( xOffset, fragmentYOffset, fragmentFont, fragment.
text() );
755 QColor bufferColor( Qt::gray );
756 bufferColor.setAlphaF( mask.
opacity() );
760 brush.setColor( bufferColor );
761 pen.setColor( bufferColor );
762 pen.setWidthF( penSize * scaleFactor );
765 QgsScopedQPainterState painterState( p );
769 p->scale( component.dpiRatio, component.dpiRatio );
772 QgsPainterSwapper swapper( context, p );
774 QgsEffectPainter effectPainter( context, mask.
paintEffect() );
775 if ( scaleFactor != 1.0 )
776 context.
painter()->scale( 1 / scaleFactor, 1 / scaleFactor );
777 context.
painter()->setPen( pen );
778 context.
painter()->setBrush( brush );
779 context.
painter()->drawPath( path );
780 if ( scaleFactor != 1.0 )
781 context.
painter()->scale( scaleFactor, scaleFactor );
786 if ( scaleFactor != 1.0 )
787 p->scale( 1 / scaleFactor, 1 / scaleFactor );
789 p->setBrush( brush );
791 if ( scaleFactor != 1.0 )
792 p->scale( scaleFactor, scaleFactor );
800 if ( doc.
size() == 0 )
803 return textWidth( context, format, doc );
820 for (
const QString &line : textLines )
824 lines.append(
wrappedText( context, line, maxLineWidth, format ) );
828 lines.append( line );
833 return textHeight( context, format, doc, mode );
840 bool isNullSize =
false;
841 const QFont baseFont = format.
scaledFont( context, scaleFactor, &isNullSize );
845 const QFontMetrics fm( baseFont );
846 const double height = ( character.isNull() ? fm.height() : fm.boundingRect( character ).height() ) / scaleFactor;
848 if ( !includeEffects )
851 double maxExtension = 0;
880 return height + maxExtension;
888 const QStringList multiLineSplit = text.split(
'\n' );
890 return currentTextWidth > width;
895 const QStringList lines = text.split(
'\n' );
896 QStringList outLines;
897 for (
const QString &line : lines )
902 const QStringList words = line.split(
' ' );
903 QStringList linesToProcess;
904 QString wordsInCurrentLine;
905 for (
const QString &word : words )
910 if ( !wordsInCurrentLine.isEmpty() )
911 linesToProcess << wordsInCurrentLine;
912 wordsInCurrentLine.clear();
913 linesToProcess << word;
917 if ( !wordsInCurrentLine.isEmpty() )
918 wordsInCurrentLine.append(
' ' );
919 wordsInCurrentLine.append( word );
922 if ( !wordsInCurrentLine.isEmpty() )
923 linesToProcess << wordsInCurrentLine;
925 for (
const QString &line : std::as_const( linesToProcess ) )
927 QString remainingText = line;
928 int lastPos = remainingText.lastIndexOf(
' ' );
929 while ( lastPos > -1 )
939 outLines << remainingText.left( lastPos );
940 remainingText = remainingText.mid( lastPos + 1 );
943 lastPos = remainingText.lastIndexOf(
' ', lastPos - 1 );
945 outLines << remainingText;
974 Component component =
c;
975 QgsTextBackgroundSettings background = format.
background();
977 QPainter *prevP = context.
painter();
978 QPainter *p = context.
painter();
979 std::unique_ptr< QgsPaintEffect > tmpEffect;
983 tmpEffect->begin( context );
992 const double originAdjustRotationRadians = -component.rotation;
995 component.rotation = -( component.rotation * 180 / M_PI );
996 component.rotationOffset =
1001 component.rotation = 0.0;
1002 component.rotationOffset = background.
rotation();
1011 double width = documentSize.width();
1012 double height = documentSize.height();
1019 switch ( component.hAlign )
1023 component.center = QPointF( component.origin.x() + width / 2.0,
1024 component.origin.y() + height / 2.0 );
1028 component.center = QPointF( component.origin.x() + component.size.width() / 2.0,
1029 component.origin.y() + height / 2.0 );
1033 component.center = QPointF( component.origin.x() + component.size.width() - width / 2.0,
1034 component.origin.y() + height / 2.0 );
1041 bool isNullSize =
false;
1042 QFontMetricsF fm( format.
scaledFont( context, scaleFactor, &isNullSize ) );
1043 double originAdjust = isNullSize ? 0 : ( fm.ascent() / scaleFactor / 2.0 - fm.leading() / scaleFactor / 2.0 );
1044 switch ( component.hAlign )
1048 component.center = QPointF( component.origin.x() + width / 2.0,
1049 component.origin.y() - height / 2.0 + originAdjust );
1053 component.center = QPointF( component.origin.x(),
1054 component.origin.y() - height / 2.0 + originAdjust );
1058 component.center = QPointF( component.origin.x() - width / 2.0,
1059 component.origin.y() - height / 2.0 + originAdjust );
1066 const double dx = component.center.x() - component.origin.x();
1067 const double dy = component.center.y() - component.origin.y();
1068 component.center.setX( component.origin.x() + ( std::cos( originAdjustRotationRadians ) * dx - std::sin( originAdjustRotationRadians ) * dy ) );
1069 component.center.setY( component.origin.y() + ( std::sin( originAdjustRotationRadians ) * dx + std::cos( originAdjustRotationRadians ) * dy ) );
1079 component.size = QSizeF( width, height );
1084 switch ( background.
type() )
1097 double sizeOut = 0.0;
1099 QgsScopedRenderContextReferenceScaleOverride referenceScaleOverride( context, -1 );
1108 sizeOut = std::max( component.size.width(), component.size.height() );
1112 sizeOut += bufferSize * 2;
1118 if ( sizeOut < 1.0 )
1121 std::unique_ptr< QgsMarkerSymbol > renderedSymbol;
1125 map[u
"name"_s] = background.
svgFile().trimmed();
1126 map[u
"size"_s] = QString::number( sizeOut );
1128 map[u
"angle"_s] = QString::number( 0.0 );
1136 map[u
"fill"_s] = background.
fillColor().name();
1137 map[u
"outline"_s] = background.
strokeColor().name();
1138 map[u
"outline-width"_s] = QString::number( background.
strokeWidth() );
1143 QgsTextShadowSettings shadow = format.
shadow();
1145 QVariantMap shdwmap( map );
1146 shdwmap[u
"fill"_s] = shadow.
color().name();
1147 shdwmap[u
"outline"_s] = shadow.
color().name();
1148 shdwmap[u
"size"_s] = QString::number( sizeOut );
1153 svgp.begin( &svgPict );
1161 QgsRenderContext shdwContext;
1167 QgsSvgMarkerSymbolLayer *svgShdwM =
static_cast<QgsSvgMarkerSymbolLayer *
>( symShdwL.get() );
1170 svgShdwM->
renderPoint( QPointF( sizeOut / 2, -sizeOut / 2 ), svgShdwContext );
1173 component.picture = svgPict;
1175 component.pictureBuffer = 0.0;
1177 component.size = QSizeF( sizeOut, sizeOut );
1178 component.offset = QPointF( 0.0, 0.0 );
1181 QgsScopedQPainterState painterState( p );
1184 p->translate( component.center.x(), component.center.y() );
1185 p->rotate( component.rotation );
1188 p->translate( QPointF( xoff, yoff ) );
1189 p->rotate( component.rotationOffset );
1190 p->translate( -sizeOut / 2, sizeOut / 2 );
1192 drawShadow( context, component, format );
1194 renderedSymbol.reset( );
1202 renderedSymbol->setSize( sizeOut );
1206 renderedSymbol->setOpacity( renderedSymbol->opacity() * background.
opacity() );
1209 QgsScopedQPainterState painterState( p );
1214 p->setCompositionMode( background.
blendMode() );
1216 p->translate( component.center.x(), component.center.y() );
1217 p->rotate( component.rotation );
1220 p->translate( QPointF( xoff, yoff ) );
1221 p->rotate( component.rotationOffset );
1225 renderedSymbol->renderPoint( QPointF( 0, 0 ), &f, context );
1226 renderedSymbol->stopRender( context );
1227 p->setCompositionMode( QPainter::CompositionMode_SourceOver );
1237 double w = component.size.width();
1238 double h = component.size.height();
1259 h = std::sqrt( std::pow( w, 2 ) + std::pow( h, 2 ) );
1265 h = h * M_SQRT1_2 * 2;
1266 w = w * M_SQRT1_2 * 2;
1274 w += bufferWidth * 2;
1275 h += bufferHeight * 2;
1279 QRectF rect( -w / 2.0, - h / 2.0, w, h );
1281 if ( rect.isNull() )
1284 QgsScopedQPainterState painterState( p );
1287 p->translate( QPointF( component.center.x(), component.center.y() ) );
1288 p->rotate( component.rotation );
1291 p->translate( QPointF( xoff, yoff ) );
1292 p->rotate( component.rotationOffset );
1298 QTransform t = QTransform::fromScale( 10, 10 );
1300 QTransform ti = t.inverted();
1307 path.addRoundedRect( rect, background.
radii().width(), background.
radii().height(), Qt::RelativeSize );
1313 path.addRoundedRect( rect, xRadius, yRadius );
1319 path.addEllipse( rect );
1321 QPolygonF tempPolygon = path.toFillPolygon( t );
1322 QPolygonF polygon = ti.map( tempPolygon );
1324 QPainter *oldp = context.
painter();
1327 shapep.begin( &shapePict );
1330 std::unique_ptr< QgsFillSymbol > renderedSymbol;
1332 renderedSymbol->setOpacity( renderedSymbol->opacity() * background.
opacity() );
1336 renderedSymbol->renderPolygon( polygon,
nullptr, &f, context );
1337 renderedSymbol->stopRender( context );
1344 component.picture = shapePict;
1347 component.size = rect.size();
1348 component.offset = QPointF( rect.width() / 2, -rect.height() / 2 );
1349 drawShadow( context, component, format );
1354 p->setCompositionMode( background.
blendMode() );
1358 p->scale( component.dpiRatio, component.dpiRatio );
1360 p->drawPicture( 0, 0, shapePict );
1361 p->setCompositionMode( QPainter::CompositionMode_SourceOver );
1368 tmpEffect->end( context );
1375 QgsTextShadowSettings shadow = format.
shadow();
1377 QPainter *p = context.
painter();
1378 const double componentWidth = component.size.width();
1379 const double componentHeight = component.size.height();
1380 const double xOffset = component.offset.x();
1381 const double yOffset = component.offset.y();
1382 double pictbuffer = component.pictureBuffer;
1391 radius /= ( mapUnits ? context.
scaleFactor() / component.dpiRatio : 1 );
1392 radius =
static_cast< int >( radius + 0.5 );
1396 double blurBufferClippingScale = 3.75;
1397 int blurbuffer = ( radius > 17 ? 16 : radius ) * blurBufferClippingScale;
1399 QImage blurImg( componentWidth + ( pictbuffer * 2.0 ) + ( blurbuffer * 2.0 ),
1400 componentHeight + ( pictbuffer * 2.0 ) + ( blurbuffer * 2.0 ),
1401 QImage::Format_ARGB32_Premultiplied );
1405 int minBlurImgSize = 1;
1409 int maxBlurImgSize = 40000;
1410 if ( blurImg.isNull()
1411 || ( blurImg.width() < minBlurImgSize || blurImg.height() < minBlurImgSize )
1412 || ( blurImg.width() > maxBlurImgSize || blurImg.height() > maxBlurImgSize ) )
1415 blurImg.fill( QColor( Qt::transparent ).rgba() );
1417 if ( !pictp.begin( &blurImg ) )
1419 pictp.setRenderHints( QPainter::Antialiasing | QPainter::SmoothPixmapTransform );
1420 QPointF imgOffset( blurbuffer + pictbuffer + xOffset,
1421 blurbuffer + pictbuffer + componentHeight + yOffset );
1423 pictp.drawPicture( imgOffset,
1424 component.picture );
1427 pictp.setCompositionMode( QPainter::CompositionMode_SourceIn );
1428 pictp.fillRect( blurImg.rect(), shadow.
color() );
1432 if ( shadow.
blurRadius() > 0.0 && radius > 0 )
1440 picti.begin( &blurImg );
1441 picti.setBrush( Qt::Dense7Pattern );
1442 QPen imgPen( QColor( 0, 0, 255, 255 ) );
1443 imgPen.setWidth( 1 );
1444 picti.setPen( imgPen );
1445 picti.setOpacity( 0.1 );
1446 picti.drawRect( 0, 0, blurImg.width(), blurImg.height() );
1453 double angleRad = shadow.
offsetAngle() * M_PI / 180;
1461 angleRad -= ( component.rotation * M_PI / 180 + component.rotationOffset * M_PI / 180 );
1464 QPointF transPt( -offsetDist * std::cos( angleRad + M_PI_2 ),
1465 -offsetDist * std::sin( angleRad + M_PI_2 ) );
1471 p->setRenderHint( QPainter::SmoothPixmapTransform );
1474 p->setCompositionMode( shadow.
blendMode() );
1476 p->setOpacity( shadow.
opacity() );
1478 double scale = shadow.
scale() / 100.0;
1480 p->scale( scale, scale );
1481 if ( component.useOrigin )
1483 p->translate( component.origin.x(), component.origin.y() );
1485 p->translate( transPt );
1486 p->translate( -imgOffset.x(),
1488 p->drawImage( 0, 0, blurImg );
1495 p->setBrush( Qt::NoBrush );
1496 QPen imgPen( QColor( 255, 0, 0, 10 ) );
1497 imgPen.setWidth( 2 );
1498 imgPen.setStyle( Qt::DashLine );
1499 p->setPen( imgPen );
1500 p->scale( scale, scale );
1501 if ( component.useOrigin() )
1503 p->translate( component.origin().x(), component.origin().y() );
1505 p->translate( transPt );
1506 p->translate( -imgOffset.x(),
1508 p->drawRect( 0, 0, blurImg.width(), blurImg.height() );
1513 p->setBrush( Qt::NoBrush );
1514 QPen componentRectPen( QColor( 0, 255, 0, 70 ) );
1515 componentRectPen.setWidth( 1 );
1516 if ( component.useOrigin() )
1518 p->translate( component.origin().x(), component.origin().y() );
1520 p->setPen( componentRectPen );
1521 p->drawRect( QRect( -xOffset, -componentHeight - yOffset, componentWidth, componentHeight ) );
1530 const Component &component,
1542 std::optional< QgsScopedRenderContextReferenceScaleOverride > referenceScaleOverride;
1547 referenceScaleOverride.emplace( QgsScopedRenderContextReferenceScaleOverride( context, -1.0 ) );
1553 referenceScaleOverride.reset();
1555 double rotation = 0;
1556 const Qgis::TextOrientation orientation = calculateRotationAndOrientationForComponent( format, component, rotation );
1557 switch ( orientation )
1561 drawTextInternalHorizontal( context, format, components, mode, component, document, metrics, fontScale, alignment, vAlignment, rotation );
1570 drawTextInternalVertical( context, format,
Qgis::TextComponent::Buffer, mode, component, document, metrics, fontScale, alignment, vAlignment, rotation );
1572 drawTextInternalVertical( context, format,
Qgis::TextComponent::Text, mode, component, document, metrics, fontScale, alignment, vAlignment, rotation );
1578Qgis::TextOrientation QgsTextRenderer::calculateRotationAndOrientationForComponent(
const QgsTextFormat &format,
const QgsTextRenderer::Component &component,
double &rotation )
1580 rotation = -component.rotation * 180 / M_PI;
1587 if ( rotation >= -315 && rotation < -90 )
1592 else if ( rotation >= -90 && rotation < -45 )
1608void QgsTextRenderer::calculateExtraSpacingForLineJustification(
const double spaceToDistribute,
const QgsTextBlock &block,
double &extraWordSpace,
double &extraLetterSpace )
1611 QTextBoundaryFinder
finder( QTextBoundaryFinder::Word, blockText );
1613 int wordBoundaries = 0;
1614 while (
finder.toNextBoundary() != -1 )
1616 if (
finder.boundaryReasons() & QTextBoundaryFinder::StartOfItem )
1620 if ( wordBoundaries > 0 )
1623 extraWordSpace = spaceToDistribute / wordBoundaries;
1628 QTextBoundaryFinder
finder( QTextBoundaryFinder::Grapheme, blockText );
1631 int graphemeBoundaries = 0;
1632 while (
finder.toNextBoundary() != -1 )
1634 if (
finder.boundaryReasons() & QTextBoundaryFinder::StartOfItem )
1635 graphemeBoundaries++;
1638 if ( graphemeBoundaries > 0 )
1640 extraLetterSpace = spaceToDistribute / graphemeBoundaries;
1645void QgsTextRenderer::applyExtraSpacingForLineJustification( QFont &font,
double extraWordSpace,
double extraLetterSpace )
1647 const double prevWordSpace = font.wordSpacing();
1648 font.setWordSpacing( prevWordSpace + extraWordSpace );
1649 const double prevLetterSpace = font.letterSpacing();
1650 font.setLetterSpacing( QFont::AbsoluteSpacing, prevLetterSpace + extraLetterSpace );
1654void QgsTextRenderer::renderBlockHorizontal(
const QgsTextBlock &block,
int blockIndex,
1657 QPainter *painter,
bool forceRenderAsPaths,
1658 double fontScale,
double extraWordSpace,
double extraLetterSpace,
1664 int fragmentIndex = 0;
1665 for (
const QgsTextFragment &fragment : block )
1670 QFont fragmentFont = metrics.
fragmentFont( blockIndex, fragmentIndex );
1673 applyExtraSpacingForLineJustification( fragmentFont, extraWordSpace * fontScale, extraLetterSpace * fontScale );
1680 if ( deferredRenderBlock )
1682 DeferredRenderFragment renderFragment;
1683 renderFragment.color = textColor;
1684 if ( forceRenderAsPaths )
1686 renderFragment.path.setFillRule( Qt::WindingFill );
1687 renderFragment.path.addText( xOffset, yOffset, fragmentFont, fragment.
text() );
1689 renderFragment.font = fragmentFont;
1690 renderFragment.point = QPointF( xOffset, yOffset );
1691 renderFragment.text = fragment.
text();
1692 deferredRenderBlock->fragments.append( renderFragment );
1694 else if ( forceRenderAsPaths )
1696 painter->setBrush( textColor );
1698 path.setFillRule( Qt::WindingFill );
1699 path.addText( xOffset, yOffset, fragmentFont, fragment.
text() );
1700 painter->drawPath( path );
1704 painter->setPen( textColor );
1705 painter->setFont( fragmentFont );
1706 painter->drawText( QPointF( xOffset, yOffset ), fragment.
text() );
1709 else if ( fragment.
isImage() )
1711 bool fitsInCache =
false;
1713 const double imageHeight = metrics.
fragmentFixedHeight( blockIndex, fragmentIndex, mode ) * fontScale;
1716 QSize(
static_cast< int >( std::round( imageWidth ) ),
1717 static_cast< int >( std::round( imageHeight ) ) ),
1721 const double yOffset = imageBaseline - image.height();
1722 if ( !image.isNull() )
1723 painter->drawImage( QPointF( xOffset, yOffset ), image );
1754 if ( format.
font().underline()
1755 || format.
font().overline()
1756 || format.
font().strikeOut()
1757 || std::any_of( document.begin(), document.end(), [](
const QgsTextBlock & block )
1759 return std::any_of( block.begin(), block.end(), []( const QgsTextFragment & fragment )
1761 return fragment.characterFormat().underline() == QgsTextCharacterFormat::BooleanValue::SetTrue
1762 || fragment.characterFormat().overline() == QgsTextCharacterFormat::BooleanValue::SetTrue
1763 || fragment.characterFormat().strikeOut() == QgsTextCharacterFormat::BooleanValue::SetTrue;
1776 return std::any_of( document.begin(), document.end(), [](
const QgsTextBlock & block )
1778 return std::any_of( block.begin(), block.end(), []( const QgsTextFragment & fragment )
1780 return fragment.isImage();
1787 QVector< BlockMetrics > blockMetrics;
1788 blockMetrics.reserve( document.
size() );
1791 for (
const QgsTextBlock &block : document )
1794 if ( block.blockFormat().hasHorizontalAlignmentSet() )
1795 blockAlignment = block.blockFormat().horizontalAlignment();
1798 || document.size() > 1 );
1800 const bool isFinalLineInParagraph = ( blockIndex == document.size() - 1 )
1801 || document.at( blockIndex + 1 ).toPlainText().trimmed().isEmpty();
1803 BlockMetrics thisBlockMetrics;
1805 thisBlockMetrics.width = metrics.
blockWidth( blockIndex );
1807 if ( adjustForAlignment )
1809 double blockWidthDiff = 0;
1810 switch ( blockAlignment )
1817 blockWidthDiff = targetWidth - thisBlockMetrics.width - metrics.
blockRightMargin( blockIndex );
1821 if ( !isFinalLineInParagraph && targetWidth > thisBlockMetrics.width )
1823 calculateExtraSpacingForLineJustification( targetWidth - thisBlockMetrics.width, block, thisBlockMetrics.extraWordSpace, thisBlockMetrics.extraLetterSpace );
1824 thisBlockMetrics.width = targetWidth;
1840 thisBlockMetrics.xOffset = blockWidthDiff;
1845 switch ( blockAlignment )
1848 thisBlockMetrics.xOffset = blockWidthDiff - targetWidth;
1852 thisBlockMetrics.xOffset = blockWidthDiff - targetWidth / 2.0;
1874 thisBlockMetrics.backgroundWidth = targetWidth;
1875 thisBlockMetrics.backgroundXOffset = 0;
1879 thisBlockMetrics.backgroundWidth = thisBlockMetrics.width;
1880 thisBlockMetrics.backgroundXOffset = thisBlockMetrics.xOffset;
1884 blockMetrics << thisBlockMetrics;
1887 return blockMetrics;
1890QBrush QgsTextRenderer::createBrushForPath(
QgsRenderContext &context,
const QString &path )
1892 bool fitsInCache =
false;
1896 const QSizeF originalSizeMmAt96Dpi = imageSize / 3.7795275590551185;
1898 const double imageWidth = originalSizeMmAt96Dpi.width() * pixelsPerMm;
1899 const double imageHeight = originalSizeMmAt96Dpi.height() * pixelsPerMm;
1901 if ( imageWidth == 0 || imageHeight == 0 )
1904 QSize(
static_cast< int >( std::round( imageWidth ) ),
1905 static_cast< int >( std::round( imageHeight ) ) ),
1909 if ( !image.isNull() )
1912 res.setTextureImage( image );
1920 context.
painter()->translate( component.origin );
1922 context.
painter()->rotate( rotation );
1924 context.
painter()->setPen( Qt::NoPen );
1925 context.
painter()->setBrush( Qt::NoBrush );
1926 for (
const QgsTextBlock &block : document )
1928 const double baseLineOffset = metrics.
baselineOffset( blockIndex, mode );
1932 if ( block.blockFormat().hasBackground() )
1934 QBrush backgroundBrush = block.blockFormat().backgroundBrush();
1935 if ( !block.blockFormat().backgroundImagePath().isEmpty() )
1937 const QBrush backgroundImageBrush = createBrushForPath( context, block.blockFormat().backgroundImagePath() );
1938 if ( backgroundImageBrush.style() == Qt::BrushStyle::TexturePattern )
1939 backgroundBrush = backgroundImageBrush;
1942 context.
painter()->setBrush( backgroundBrush );
1943 context.
painter()->drawRect( QRectF( blockMetrics[ blockIndex ].backgroundXOffset, baseLineOffset - blockMaximumAscent, blockMetrics[ blockIndex ].backgroundWidth, blockMaximumDescent + blockMaximumAscent ) );
1947 int fragmentIndex = 0;
1949 for (
const QgsTextFragment &fragment : block )
1952 const double ascent = metrics.
fragmentAscent( blockIndex, fragmentIndex, mode );
1953 const double descent = metrics.
fragmentDescent( blockIndex, fragmentIndex, mode );
1963 if ( backgroundImageBrush.style() == Qt::BrushStyle::TexturePattern )
1964 backgroundBrush = backgroundImageBrush;
1967 context.
painter()->setBrush( backgroundBrush );
1968 context.
painter()->drawRect( QRectF( blockMetrics[ blockIndex ].xOffset + xOffset,
1969 baseLineOffset + verticalAlignOffset + yOffset - ascent, horizontalAdvance, ascent + descent ) );
1972 xOffset += horizontalAdvance;
1979 context.
painter()->setBrush( Qt::NoBrush );
1982 context.
painter()->rotate( -rotation );
1983 context.
painter()->translate( -component.origin );
1993 double targetWidth = 0.0;
1998 targetWidth = documentSize.width();
2004 targetWidth = component.size.width();
2008 double verticalAlignOffset = 0;
2012 const double overallHeight = documentSize.height();
2013 switch ( vAlignment )
2020 verticalAlignOffset = ( component.size.height() - overallHeight ) * 0.5 + metrics.
blockVerticalMargin( - 1 );
2024 verticalAlignOffset = ( component.size.height() - overallHeight ) + metrics.
blockVerticalMargin( - 1 );
2034 const bool usePathsForText = usePathsToRender( context, format, document );
2038 std::unique_ptr< std::vector< DeferredRenderBlock > > deferredBlocks;
2045 if ( requiresMultiPassRendering )
2047 deferredBlocks = std::make_unique< std::vector< DeferredRenderBlock > >();
2048 deferredBlocks->reserve( document.
size() );
2055 const QVector< BlockMetrics > blockMetrics = calculateBlockMetrics( document, metrics, mode, targetWidth, hAlignment );
2059 renderDocumentBackgrounds( context, document, metrics, component, blockMetrics, mode, verticalAlignOffset, rotation );
2063 for (
const QgsTextBlock &block : document )
2065 const double blockHeight = metrics.
blockHeight( blockIndex );
2067 DeferredRenderBlock *deferredBlock =
nullptr;
2068 if ( requiresMultiPassRendering && deferredBlocks )
2070 deferredBlocks->emplace_back( DeferredRenderBlock() );
2071 deferredBlock = &deferredBlocks->back();
2072 deferredBlock->fragments.reserve( block.size() );
2075 QgsScopedQPainterState painterState( context.
painter() );
2077 context.
painter()->translate( component.origin );
2079 context.
painter()->rotate( rotation );
2084 maskPainter->save();
2085 maskPainter->translate( component.origin );
2087 maskPainter->rotate( rotation );
2090 const BlockMetrics thisBlockMetrics = blockMetrics[ blockIndex ];
2091 const double baseLineOffset = metrics.
baselineOffset( blockIndex, mode );
2093 const QPointF blockOrigin( thisBlockMetrics.xOffset, baseLineOffset + verticalAlignOffset );
2094 if ( deferredBlock )
2095 deferredBlock->origin = blockOrigin;
2097 context.
painter()->translate( blockOrigin );
2099 maskPainter->translate( blockOrigin );
2101 Component subComponent;
2102 subComponent.block = block;
2103 subComponent.blockIndex = blockIndex;
2104 subComponent.size = QSizeF( thisBlockMetrics.width, blockHeight );
2105 subComponent.offset = QPointF( 0.0, -metrics.
ascentOffset() );
2106 subComponent.rotation = -component.rotation * 180 / M_PI;
2107 subComponent.rotationOffset = 0.0;
2108 subComponent.extraWordSpacing = thisBlockMetrics.extraWordSpace * fontScale;
2109 subComponent.extraLetterSpacing = thisBlockMetrics.extraLetterSpace * fontScale;
2110 if ( deferredBlock )
2111 deferredBlock->component = subComponent;
2116 QgsTextRenderer::drawMask( context, subComponent, format, metrics, mode );
2120 const bool needsPaths = usePathsForText
2124 std::optional< QgsScopedRenderContextReferenceScaleOverride > referenceScaleOverride;
2129 referenceScaleOverride.emplace( QgsScopedRenderContextReferenceScaleOverride( context, -1.0 ) );
2132 referenceScaleOverride.reset();
2141 context.
painter()->scale( subComponent.dpiRatio, subComponent.dpiRatio );
2143 context.
painter()->scale( 1 / fontScale, 1 / fontScale );
2144 context.
painter()->setPen( Qt::NoPen );
2145 context.
painter()->setBrush( Qt::NoBrush );
2147 renderBlockHorizontal( block, blockIndex, metrics, context, format, context.
painter(), needsPaths,
2148 fontScale, thisBlockMetrics.extraWordSpace, thisBlockMetrics.extraLetterSpace, mode, deferredBlock );
2151 maskPainter->restore();
2157 if ( deferredBlocks )
2159 renderDeferredBlocks(
2160 context, format, components, *deferredBlocks, usePathsForText, fontScale, component, rotation
2168 const std::vector< DeferredRenderBlock > &deferredBlocks,
2169 bool usePathsForText,
2171 const Component &component,
2176 renderDeferredBuffer( context, format, components, deferredBlocks, fontScale, component, rotation );
2183 renderDeferredShadowForText( context, format, deferredBlocks, fontScale, component, rotation );
2192 renderDeferredText( context, deferredBlocks, usePathsForText, fontScale, component, rotation );
2196void QgsTextRenderer::renderDeferredShadowForText(
QgsRenderContext &context,
2198 const std::vector< DeferredRenderBlock > &deferredBlocks,
2200 const Component &component,
2203 QgsScopedQPainterState painterState( context.
painter() );
2205 context.
painter()->translate( component.origin );
2207 context.
painter()->rotate( rotation );
2209 context.
painter()->setPen( Qt::NoPen );
2210 context.
painter()->setBrush( Qt::NoBrush );
2212 for (
const DeferredRenderBlock &block : deferredBlocks )
2214 Component subComponent = block.component;
2216 QPainter painter( &subComponent.picture );
2217 painter.setPen( Qt::NoPen );
2218 painter.setBrush( Qt::NoBrush );
2219 painter.scale( 1 / fontScale, 1 / fontScale );
2221 for (
const DeferredRenderFragment &fragment : std::as_const( block.fragments ) )
2223 if ( !fragment.path.isEmpty() )
2225 painter.setBrush( fragment.color );
2226 painter.drawPath( fragment.path );
2230 painter.setPen( fragment.color );
2231 painter.setFont( fragment.font );
2232 painter.drawText( fragment.point, fragment.
text );
2237 subComponent.pictureBuffer = 1.0;
2238 subComponent.origin = QPointF( 0.0, 0.0 );
2239 const QRectF pictureBoundingRect = subComponent.picture.boundingRect();
2240 subComponent.size = pictureBoundingRect.size();
2241 subComponent.offset = QPointF( -pictureBoundingRect.left(), -pictureBoundingRect.height() - pictureBoundingRect.top() );
2243 context.
painter()->translate( block.origin );
2244 drawShadow( context, subComponent, format );
2245 context.
painter()->translate( -block.origin );
2252 const std::vector< DeferredRenderBlock > &deferredBlocks,
2254 const Component &component,
2257 QgsScopedQPainterState painterState( context.
painter() );
2264 std::unique_ptr< QPicture > bufferPicture;
2265 std::unique_ptr< QPainter > bufferPainter;
2266 QPainter *prevPainter = context.
painter();
2267 if ( needsShadowOnBuffer )
2269 bufferPicture = std::make_unique< QPicture >();
2270 bufferPainter = std::make_unique< QPainter >( bufferPicture.get() );
2274 std::unique_ptr< QgsPaintEffect > tmpEffect;
2278 tmpEffect->begin( context );
2283 QPen pen( bufferColor );
2284 const QgsTextBufferSettings &buffer = format.
buffer();
2288 pen.setWidthF( penSize * fontScale );
2290 context.
painter()->setPen( pen );
2295 bufferColor.setAlpha( 0 );
2297 context.
painter()->setBrush( bufferColor );
2299 context.
painter()->translate( component.origin );
2301 context.
painter()->rotate( rotation );
2308 for (
const DeferredRenderBlock &block : deferredBlocks )
2310 context.
painter()->translate( block.origin );
2311 context.
painter()->scale( 1 / fontScale, 1 / fontScale );
2312 for (
const DeferredRenderFragment &fragment : std::as_const( block.fragments ) )
2314 context.
painter()->drawPath( fragment.path );
2316 context.
painter()->scale( fontScale, fontScale );
2317 context.
painter()->translate( -block.origin );
2322 tmpEffect->end( context );
2325 if ( needsShadowOnBuffer && bufferPicture )
2327 bufferPainter->end();
2328 bufferPainter.reset();
2331 QgsTextRenderer::Component bufferComponent = component;
2332 bufferComponent.origin = QPointF( 0.0, 0.0 );
2333 bufferComponent.picture = *bufferPicture;
2334 bufferComponent.pictureBuffer = penSize / 2.0;
2335 const QRectF bufferBoundingBox = bufferPicture->boundingRect();
2336 bufferComponent.size = bufferBoundingBox.size();
2337 bufferComponent.offset = QPointF( -bufferBoundingBox.left(), -bufferBoundingBox.height() - bufferBoundingBox.top() );
2339 drawShadow( context, bufferComponent, format );
2348 context.
painter()->scale( component.dpiRatio, component.dpiRatio );
2354 const std::vector< DeferredRenderBlock > &deferredBlocks,
2355 bool usePathsForText,
2357 const Component &component,
2360 QgsScopedQPainterState painterState( context.
painter() );
2362 context.
painter()->translate( component.origin );
2364 context.
painter()->rotate( rotation );
2366 context.
painter()->setPen( Qt::NoPen );
2367 context.
painter()->setBrush( Qt::NoBrush );
2370 for (
const DeferredRenderBlock &block : deferredBlocks )
2372 context.
painter()->translate( block.origin );
2373 context.
painter()->scale( 1 / fontScale, 1 / fontScale );
2375 for (
const DeferredRenderFragment &fragment : std::as_const( block.fragments ) )
2377 if ( usePathsForText )
2379 context.
painter()->setBrush( fragment.color );
2380 context.
painter()->drawPath( fragment.path );
2384 context.
painter()->setPen( fragment.color );
2385 context.
painter()->setFont( fragment.font );
2386 context.
painter()->drawText( fragment.point, fragment.
text );
2390 context.
painter()->scale( fontScale, fontScale );
2391 context.
painter()->translate( -block.origin );
2395void QgsTextRenderer::drawTextInternalVertical(
QgsRenderContext &context,
const QgsTextFormat &format,
Qgis::TextComponents components,
Qgis::TextLayoutMode mode,
const QgsTextRenderer::Component &component,
const QgsTextDocument &document,
const QgsTextDocumentMetrics &metrics,
double fontScale,
Qgis::TextHorizontalAlignment hAlignment,
Qgis::TextVerticalAlignment,
double rotation )
2398 const QStringList textLines = document.
toPlainText();
2400 std::optional< QgsScopedRenderContextReferenceScaleOverride > referenceScaleOverride;
2405 referenceScaleOverride.emplace( QgsScopedRenderContextReferenceScaleOverride( context, -1.0 ) );
2411 referenceScaleOverride.reset();
2414 const double actualTextWidth = documentSize.width();
2415 double textRectWidth = 0.0;
2421 textRectWidth = actualTextWidth;
2427 textRectWidth = component.size.width();
2431 int maxLineLength = 0;
2432 for (
const QString &line : std::as_const( textLines ) )
2434 maxLineLength = std::max( maxLineLength,
static_cast<int>( line.length() ) );
2437 const double actualLabelHeight = documentSize.height();
2442 for (
const QgsTextBlock &block : document )
2444 QgsScopedQPainterState painterState( context.
painter() );
2447 context.
painter()->translate( component.origin );
2449 context.
painter()->rotate( rotation );
2454 maskPainter->save();
2455 maskPainter->translate( component.origin );
2457 maskPainter->rotate( rotation );
2464 if ( adjustForAlignment )
2466 double hAlignmentOffset = 0;
2467 switch ( hAlignment )
2470 hAlignmentOffset = ( textRectWidth - actualTextWidth ) * 0.5;
2474 hAlignmentOffset = textRectWidth - actualTextWidth;
2488 xOffset += hAlignmentOffset;
2496 double yOffset = 0.0;
2502 if ( rotation >= -405 && rotation < -180 )
2506 else if ( rotation >= 0 && rotation < 45 )
2508 xOffset -= actualTextWidth;
2514 yOffset = -actualLabelHeight;
2519 yOffset = -actualLabelHeight;
2529 context.
painter()->translate( QPointF( xOffset, yOffset ) );
2531 double currentBlockYOffset = 0;
2532 int fragmentIndex = 0;
2533 for (
const QgsTextFragment &fragment : block )
2535 QgsScopedQPainterState fragmentPainterState( context.
painter() );
2540 const QFont fragmentFont = metrics.
fragmentFont( blockIndex, fragmentIndex );
2542 QFontMetricsF fragmentMetrics( fragmentFont );
2544 const double letterSpacing = fragmentFont.letterSpacing() / fontScale;
2545 const double labelHeight = fragmentMetrics.ascent() / fontScale + ( fragmentMetrics.ascent() / fontScale + letterSpacing ) * ( line.length() - 1 );
2547 Component subComponent;
2548 subComponent.block = QgsTextBlock( fragment );
2549 subComponent.blockIndex = blockIndex;
2550 subComponent.firstFragmentIndex = fragmentIndex;
2551 subComponent.size = QSizeF( blockMaximumCharacterWidth, labelHeight + fragmentMetrics.descent() / fontScale );
2552 subComponent.offset = QPointF( 0.0, currentBlockYOffset );
2553 subComponent.rotation = -component.rotation * 180 / M_PI;
2554 subComponent.rotationOffset = 0.0;
2561 QgsTextRenderer::drawMask( context, subComponent, format );
2567 currentBlockYOffset += QgsTextRenderer::drawBuffer( context, subComponent, format, metrics, mode );
2573 path.setFillRule( Qt::WindingFill );
2575 double partYOffset = 0.0;
2576 for (
const QString &part : parts )
2578 double partXOffset = ( blockMaximumCharacterWidth - ( fragmentMetrics.horizontalAdvance( part ) / fontScale - letterSpacing ) ) / 2;
2579 partYOffset += fragmentMetrics.ascent() / fontScale;
2580 path.addText( partXOffset * fontScale, partYOffset * fontScale, fragmentFont, part );
2581 partYOffset += letterSpacing;
2587 textp.begin( &textPict );
2588 textp.setPen( Qt::NoPen );
2591 textp.setBrush( textColor );
2592 textp.scale( 1 / fontScale, 1 / fontScale );
2593 textp.drawPath( path );
2604 subComponent.picture = textPict;
2605 subComponent.pictureBuffer = 0.0;
2606 subComponent.origin = QPointF( 0.0, currentBlockYOffset );
2607 const double prevY = subComponent.offset.y();
2608 subComponent.offset = QPointF( 0, -subComponent.size.height() );
2609 subComponent.useOrigin =
true;
2610 QgsTextRenderer::drawShadow( context, subComponent, format );
2611 subComponent.useOrigin =
false;
2612 subComponent.offset = QPointF( 0, prevY );
2622 context.
painter()->scale( subComponent.dpiRatio, subComponent.dpiRatio );
2626 context.
painter()->translate( 0, currentBlockYOffset );
2628 context.
painter()->drawPicture( 0, 0, textPict );
2629 currentBlockYOffset += partYOffset;
2635 maskPainter->restore();
2653 if ( pixelSize < 50 )
2654 return 200 / pixelSize;
2657 else if ( pixelSize > 200 )
2658 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