37#include <QTextBoundaryFinder>
42 if ( alignment & Qt::AlignLeft )
44 else if ( alignment & Qt::AlignRight )
46 else if ( alignment & Qt::AlignHCenter )
48 else if ( alignment & Qt::AlignJustify )
57 if ( alignment & Qt::AlignTop )
59 else if ( alignment & Qt::AlignBottom )
61 else if ( alignment & Qt::AlignVCenter )
64 else if ( alignment & Qt::AlignBaseline )
72 return static_cast< int >(
c.convertToPainterUnits( size, unit, mapUnitScale ) + 0.5 );
92 drawDocument( rect, lFormat, metrics.
document(), metrics, context, alignment, vAlignment, rotation, mode, flags );
97 const QgsTextFormat tmpFormat = updateShadowPosition( format );
115 drawParts( rect, rotation, horizontalAlignment, verticalAlignment, document, metrics, context, tmpFormat, components, mode );
123 lFormat = updateShadowPosition( lFormat );
135 const QgsTextFormat lFormat = updateShadowPosition( _format );
154 drawParts( point, rotation, alignment, document, metrics, context, lFormat, components, mode );
162 lFormat = updateShadowPosition( lFormat );
169 drawDocumentOnLine( line, lFormat, document, context, offsetAlongLine, offsetFromLine );
174 QPolygonF labelBaselineCurve = line;
183#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<11
184 if ( offsetFromLine < 0 )
187 std::unique_ptr < QgsLineString > reversed( offsetCurve->reversed() );
191 offsetCurve = std::move( reversed );
195 labelBaselineCurve = offsetCurve->asQPolygonF();
200 const QFont baseFont = format.
scaledFont( context, fontScale );
201 const double letterSpacing = baseFont.letterSpacing() / fontScale;
202 const double wordSpacing = baseFont.wordSpacing() / fontScale;
204 QStringList graphemes;
205 QVector< QgsTextCharacterFormat > graphemeFormats;
206 QVector< QgsTextDocumentMetrics > graphemeMetrics;
208 for (
const QgsTextBlock &block : std::as_const( document ) )
213 for (
const QString &grapheme : fragmentGraphemes )
215 graphemes.append( grapheme );
216 graphemeFormats.append( fragment.characterFormat() );
226 QVector< double > characterWidths( graphemes.count() );
227 QVector< double > characterHeights( graphemes.count() );
228 QVector< double > characterDescents( graphemes.count() );
229 QFont previousNonSuperSubScriptFont;
231 for (
int i = 0; i < graphemes.count(); i++ )
236 double graphemeFirstCharHorizontalAdvanceWithLetterSpacing = 0;
237 double graphemeFirstCharHorizontalAdvance = 0;
238 double graphemeHorizontalAdvance = 0;
239 double characterDescent = 0;
240 double characterHeight = 0;
243 QFont graphemeFont = baseFont;
247 previousNonSuperSubScriptFont = graphemeFont;
254 previousNonSuperSubScriptFont = graphemeFont;
275 previousNonSuperSubScriptFont = graphemeFont;
278 const QFontMetricsF graphemeFontMetrics( graphemeFont );
279 graphemeFirstCharHorizontalAdvance = graphemeFontMetrics.horizontalAdvance( QString( graphemes[i].at( 0 ) ) ) / fontScale;
280 graphemeFirstCharHorizontalAdvanceWithLetterSpacing = graphemeFontMetrics.horizontalAdvance( graphemes[i].at( 0 ) ) / fontScale + letterSpacing;
281 graphemeHorizontalAdvance = graphemeFontMetrics.horizontalAdvance( QString( graphemes[i] ) ) / fontScale;
282 characterDescent = graphemeFontMetrics.descent() / fontScale;
283 characterHeight = graphemeFontMetrics.height() / fontScale;
285 qreal wordSpaceFix = qreal( 0.0 );
286 if ( graphemes[i] == QLatin1String(
" " ) )
290 wordSpaceFix = ( nxt < graphemes.count() && graphemes[nxt] != QLatin1String(
" " ) ) ? wordSpacing : qreal( 0.0 );
295 if ( graphemes[i].length() == 1 &&
296 !
qgsDoubleNear( graphemeFirstCharHorizontalAdvance, graphemeFirstCharHorizontalAdvanceWithLetterSpacing ) )
299 wordSpaceFix -= wordSpacing;
302 const double charWidth = graphemeHorizontalAdvance + wordSpaceFix;
303 characterWidths[i] = charWidth;
304 characterHeights[i] = characterHeight;
305 characterDescents[i] = characterDescent;
308 QgsPrecalculatedTextMetrics metrics( graphemes, std::move( characterWidths ), std::move( characterHeights ), std::move( characterDescents ) );
312 metrics, labelBaselineCurve, offsetAlongLine,
319 if ( placement->graphemePlacement.empty() )
326 QHash< int, QgsTextRenderer::Component > components;
327 components.reserve( placement->graphemePlacement.size() );
330 QgsTextRenderer::Component component;
331 component.origin = QPointF( grapheme.x, grapheme.y );
332 component.rotation = -grapheme.angle;
338 component.origin.rx() += verticalOffset * std::cos( grapheme.angle + M_PI_2 );
339 component.origin.ry() += verticalOffset * std::sin( grapheme.angle + M_PI_2 );
342 components.insert( grapheme.graphemeIndex, component );
350 const QgsTextRenderer::Component &component = components[grapheme.graphemeIndex ];
360 const QgsTextRenderer::Component &component = components[grapheme.graphemeIndex ];
377 const QgsTextRenderer::Component &component = components[grapheme.graphemeIndex ];
430 component.dpiRatio = 1.0;
431 component.origin = rect.topLeft();
432 component.rotation = rotation;
433 component.size = rect.size();
434 component.hAlign = alignment;
442 double xc = rect.width() / 2.0;
443 double yc = rect.height() / 2.0;
445 double angle = -rotation;
446 double xd = xc * std::cos( angle ) - yc * std::sin( angle );
447 double yd = xc * std::sin( angle ) + yc * std::cos( angle );
449 component.center = QPointF( component.origin.x() + xd, component.origin.y() + yd );
453 component.center = rect.center();
456 switch ( vAlignment )
478 drawTextInternal( parts, context, format, component,
480 alignment, vAlignment, mode );
501 component.dpiRatio = 1.0;
502 component.origin = origin;
503 component.rotation = rotation;
504 component.hAlign = alignment;
508 QgsTextRenderer::drawBackground( context, component, format, metrics, mode );
518 drawTextInternal( parts, context, format, component,
528 return QFontMetricsF( format.
scaledFont( context, scaleFactor ), context.
painter() ? context.
painter()->device() : nullptr );
535 QPainter *p = context.
painter();
540 if ( component.rotation >= -315 && component.rotation < -90 )
544 else if ( component.rotation >= -90 && component.rotation < -45 )
562 std::optional< QgsScopedRenderContextReferenceScaleOverride > referenceScaleOverride;
573 referenceScaleOverride.reset();
576 path.setFillRule( Qt::WindingFill );
578 double height = component.size.height();
579 switch ( orientation )
590 double partYOffset = component.offset.y() * scaleFactor;
593 double partLastDescent = 0;
595 int fragmentIndex = 0;
598 const QFont fragmentFont = metrics.
fragmentFont( component.blockIndex, component.firstFragmentIndex + fragmentIndex );
599 const double letterSpacing = fragmentFont.letterSpacing() / scaleFactor;
601 const QFontMetricsF fragmentMetrics( fragmentFont );
603 const double fragmentYOffset = metrics.
fragmentVerticalOffset( component.blockIndex, fragmentIndex, mode );
606 for (
const QString &part : parts )
608 double partXOffset = ( blockMaximumCharacterWidth - ( fragmentMetrics.horizontalAdvance( part ) / scaleFactor - letterSpacing ) ) / 2;
609 partYOffset += fragmentMetrics.ascent() / scaleFactor;
610 path.addText( partXOffset, partYOffset + fragmentYOffset, fragmentFont, part );
611 partYOffset += letterSpacing;
613 partLastDescent = fragmentMetrics.descent() / scaleFactor;
617 height = partYOffset + partLastDescent;
618 advance = partYOffset - component.offset.y() * scaleFactor;
623 QColor bufferColor = buffer.
color();
624 bufferColor.setAlphaF( buffer.
opacity() );
625 QPen pen( bufferColor );
626 pen.setWidthF( penSize * scaleFactor );
628 QColor tmpColor( bufferColor );
632 tmpColor.setAlpha( 0 );
638 buffp.begin( &buffPict );
642 std::unique_ptr< QgsPaintEffect > tmpEffect( buffer.
paintEffect()->
clone() );
644 tmpEffect->begin( context );
645 context.
painter()->setPen( pen );
646 context.
painter()->setBrush( tmpColor );
647 if ( scaleFactor != 1.0 )
648 context.
painter()->scale( 1 / scaleFactor, 1 / scaleFactor );
649 context.
painter()->drawPath( path );
650 if ( scaleFactor != 1.0 )
651 context.
painter()->scale( scaleFactor, scaleFactor );
652 tmpEffect->end( context );
658 if ( scaleFactor != 1.0 )
659 buffp.scale( 1 / scaleFactor, 1 / scaleFactor );
661 buffp.setBrush( tmpColor );
662 buffp.drawPath( path );
668 QgsTextRenderer::Component bufferComponent = component;
669 bufferComponent.origin = QPointF( 0.0, 0.0 );
670 bufferComponent.picture = buffPict;
671 bufferComponent.pictureBuffer = penSize / 2.0;
672 bufferComponent.size.setHeight( height );
676 bufferComponent.offset.setY( - bufferComponent.size.height() );
678 drawShadow( context, bufferComponent, format );
686 p->setCompositionMode( buffer.
blendMode() );
690 p->scale( component.dpiRatio, component.dpiRatio );
692 p->drawPicture( 0, 0, buffPict );
694 return advance / scaleFactor;
714 path.setFillRule( Qt::WindingFill );
721 std::optional< QgsScopedRenderContextReferenceScaleOverride > referenceScaleOverride;
732 referenceScaleOverride.reset();
735 int fragmentIndex = 0;
738 if ( !fragment.isWhitespace() && !fragment.isImage() )
740 const QFont fragmentFont = metrics.
fragmentFont( component.blockIndex, fragmentIndex );
742 const double fragmentYOffset = metrics.
fragmentVerticalOffset( component.blockIndex, fragmentIndex, mode );
743 path.addText( xOffset, fragmentYOffset, fragmentFont, fragment.text() );
750 QColor bufferColor( Qt::gray );
751 bufferColor.setAlphaF( mask.
opacity() );
755 brush.setColor( bufferColor );
756 pen.setColor( bufferColor );
757 pen.setWidthF( penSize * scaleFactor );
764 p->scale( component.dpiRatio, component.dpiRatio );
770 if ( scaleFactor != 1.0 )
771 context.
painter()->scale( 1 / scaleFactor, 1 / scaleFactor );
772 context.
painter()->setPen( pen );
773 context.
painter()->setBrush( brush );
774 context.
painter()->drawPath( path );
775 if ( scaleFactor != 1.0 )
776 context.
painter()->scale( scaleFactor, scaleFactor );
781 if ( scaleFactor != 1.0 )
782 p->scale( 1 / scaleFactor, 1 / scaleFactor );
784 p->setBrush( brush );
786 if ( scaleFactor != 1.0 )
787 p->scale( scaleFactor, scaleFactor );
795 if ( doc.
size() == 0 )
798 return textWidth( context, format, doc );
815 for (
const QString &line : textLines )
819 lines.append(
wrappedText( context, line, maxLineWidth, format ) );
823 lines.append( line );
828 return textHeight( context, format, doc, mode );
835 bool isNullSize =
false;
836 const QFont baseFont = format.
scaledFont( context, scaleFactor, &isNullSize );
840 const QFontMetrics fm( baseFont );
841 const double height = ( character.isNull() ? fm.height() : fm.boundingRect( character ).height() ) / scaleFactor;
843 if ( !includeEffects )
846 double maxExtension = 0;
875 return height + maxExtension;
883 const QStringList multiLineSplit = text.split(
'\n' );
885 return currentTextWidth > width;
890 const QStringList lines = text.split(
'\n' );
891 QStringList outLines;
892 for (
const QString &line : lines )
897 const QStringList words = line.split(
' ' );
898 QStringList linesToProcess;
899 QString wordsInCurrentLine;
900 for (
const QString &word : words )
905 if ( !wordsInCurrentLine.isEmpty() )
906 linesToProcess << wordsInCurrentLine;
907 wordsInCurrentLine.clear();
908 linesToProcess << word;
912 if ( !wordsInCurrentLine.isEmpty() )
913 wordsInCurrentLine.append(
' ' );
914 wordsInCurrentLine.append( word );
917 if ( !wordsInCurrentLine.isEmpty() )
918 linesToProcess << wordsInCurrentLine;
920 for (
const QString &line : std::as_const( linesToProcess ) )
922 QString remainingText = line;
923 int lastPos = remainingText.lastIndexOf(
' ' );
924 while ( lastPos > -1 )
934 outLines << remainingText.left( lastPos );
935 remainingText = remainingText.mid( lastPos + 1 );
938 lastPos = remainingText.lastIndexOf(
' ', lastPos - 1 );
940 outLines << remainingText;
969 Component component =
c;
972 QPainter *prevP = context.
painter();
973 QPainter *p = context.
painter();
974 std::unique_ptr< QgsPaintEffect > tmpEffect;
978 tmpEffect->begin( context );
987 const double originAdjustRotationRadians = -component.rotation;
990 component.rotation = -( component.rotation * 180 / M_PI );
991 component.rotationOffset =
996 component.rotation = 0.0;
997 component.rotationOffset = background.
rotation();
1006 double width = documentSize.width();
1007 double height = documentSize.height();
1014 switch ( component.hAlign )
1018 component.center = QPointF( component.origin.x() + width / 2.0,
1019 component.origin.y() + height / 2.0 );
1023 component.center = QPointF( component.origin.x() + component.size.width() / 2.0,
1024 component.origin.y() + height / 2.0 );
1028 component.center = QPointF( component.origin.x() + component.size.width() - width / 2.0,
1029 component.origin.y() + height / 2.0 );
1036 bool isNullSize =
false;
1037 QFontMetricsF fm( format.
scaledFont( context, scaleFactor, &isNullSize ) );
1038 double originAdjust = isNullSize ? 0 : ( fm.ascent() / scaleFactor / 2.0 - fm.leading() / scaleFactor / 2.0 );
1039 switch ( component.hAlign )
1043 component.center = QPointF( component.origin.x() + width / 2.0,
1044 component.origin.y() - height / 2.0 + originAdjust );
1048 component.center = QPointF( component.origin.x(),
1049 component.origin.y() - height / 2.0 + originAdjust );
1053 component.center = QPointF( component.origin.x() - width / 2.0,
1054 component.origin.y() - height / 2.0 + originAdjust );
1061 const double dx = component.center.x() - component.origin.x();
1062 const double dy = component.center.y() - component.origin.y();
1063 component.center.setX( component.origin.x() + ( std::cos( originAdjustRotationRadians ) * dx - std::sin( originAdjustRotationRadians ) * dy ) );
1064 component.center.setY( component.origin.y() + ( std::sin( originAdjustRotationRadians ) * dx + std::cos( originAdjustRotationRadians ) * dy ) );
1074 component.size = QSizeF( width, height );
1079 switch ( background.
type() )
1092 double sizeOut = 0.0;
1103 sizeOut = std::max( component.size.width(), component.size.height() );
1107 sizeOut += bufferSize * 2;
1113 if ( sizeOut < 1.0 )
1116 std::unique_ptr< QgsMarkerSymbol > renderedSymbol;
1120 map[QStringLiteral(
"name" )] = background.
svgFile().trimmed();
1121 map[QStringLiteral(
"size" )] = QString::number( sizeOut );
1123 map[QStringLiteral(
"angle" )] = QString::number( 0.0 );
1131 map[QStringLiteral(
"fill" )] = background.
fillColor().name();
1132 map[QStringLiteral(
"outline" )] = background.
strokeColor().name();
1133 map[QStringLiteral(
"outline-width" )] = QString::number( background.
strokeWidth() );
1140 QVariantMap shdwmap( map );
1141 shdwmap[QStringLiteral(
"fill" )] = shadow.
color().name();
1142 shdwmap[QStringLiteral(
"outline" )] = shadow.
color().name();
1143 shdwmap[QStringLiteral(
"size" )] = QString::number( sizeOut );
1148 svgp.begin( &svgPict );
1165 svgShdwM->
renderPoint( QPointF( sizeOut / 2, -sizeOut / 2 ), svgShdwContext );
1168 component.picture = svgPict;
1170 component.pictureBuffer = 0.0;
1172 component.size = QSizeF( sizeOut, sizeOut );
1173 component.offset = QPointF( 0.0, 0.0 );
1179 p->translate( component.center.x(), component.center.y() );
1180 p->rotate( component.rotation );
1183 p->translate( QPointF( xoff, yoff ) );
1184 p->rotate( component.rotationOffset );
1185 p->translate( -sizeOut / 2, sizeOut / 2 );
1187 drawShadow( context, component, format );
1189 renderedSymbol.reset( );
1197 renderedSymbol->setSize( sizeOut );
1201 renderedSymbol->setOpacity( renderedSymbol->opacity() * background.
opacity() );
1209 p->setCompositionMode( background.
blendMode() );
1211 p->translate( component.center.x(), component.center.y() );
1212 p->rotate( component.rotation );
1215 p->translate( QPointF( xoff, yoff ) );
1216 p->rotate( component.rotationOffset );
1220 renderedSymbol->renderPoint( QPointF( 0, 0 ), &f, context );
1221 renderedSymbol->stopRender( context );
1222 p->setCompositionMode( QPainter::CompositionMode_SourceOver );
1232 double w = component.size.width();
1233 double h = component.size.height();
1254 h = std::sqrt( std::pow( w, 2 ) + std::pow( h, 2 ) );
1260 h = h * M_SQRT1_2 * 2;
1261 w = w * M_SQRT1_2 * 2;
1269 w += bufferWidth * 2;
1270 h += bufferHeight * 2;
1274 QRectF rect( -w / 2.0, - h / 2.0, w, h );
1276 if ( rect.isNull() )
1282 p->translate( QPointF( component.center.x(), component.center.y() ) );
1283 p->rotate( component.rotation );
1286 p->translate( QPointF( xoff, yoff ) );
1287 p->rotate( component.rotationOffset );
1293 QTransform t = QTransform::fromScale( 10, 10 );
1295 QTransform ti = t.inverted();
1302 path.addRoundedRect( rect, background.
radii().width(), background.
radii().height(), Qt::RelativeSize );
1308 path.addRoundedRect( rect, xRadius, yRadius );
1314 path.addEllipse( rect );
1316 QPolygonF tempPolygon = path.toFillPolygon( t );
1317 QPolygonF polygon = ti.map( tempPolygon );
1319 QPainter *oldp = context.
painter();
1322 shapep.begin( &shapePict );
1325 std::unique_ptr< QgsFillSymbol > renderedSymbol;
1327 renderedSymbol->setOpacity( renderedSymbol->opacity() * background.
opacity() );
1331 renderedSymbol->renderPolygon( polygon,
nullptr, &f, context );
1332 renderedSymbol->stopRender( context );
1339 component.picture = shapePict;
1342 component.size = rect.size();
1343 component.offset = QPointF( rect.width() / 2, -rect.height() / 2 );
1344 drawShadow( context, component, format );
1349 p->setCompositionMode( background.
blendMode() );
1353 p->scale( component.dpiRatio, component.dpiRatio );
1355 p->drawPicture( 0, 0, shapePict );
1356 p->setCompositionMode( QPainter::CompositionMode_SourceOver );
1363 tmpEffect->end( context );
1372 QPainter *p = context.
painter();
1373 const double componentWidth = component.size.width();
1374 const double componentHeight = component.size.height();
1375 const double xOffset = component.offset.x();
1376 const double yOffset = component.offset.y();
1377 double pictbuffer = component.pictureBuffer;
1386 radius /= ( mapUnits ? context.
scaleFactor() / component.dpiRatio : 1 );
1387 radius =
static_cast< int >( radius + 0.5 );
1391 double blurBufferClippingScale = 3.75;
1392 int blurbuffer = ( radius > 17 ? 16 : radius ) * blurBufferClippingScale;
1394 QImage blurImg( componentWidth + ( pictbuffer * 2.0 ) + ( blurbuffer * 2.0 ),
1395 componentHeight + ( pictbuffer * 2.0 ) + ( blurbuffer * 2.0 ),
1396 QImage::Format_ARGB32_Premultiplied );
1400 int minBlurImgSize = 1;
1404 int maxBlurImgSize = 40000;
1405 if ( blurImg.isNull()
1406 || ( blurImg.width() < minBlurImgSize || blurImg.height() < minBlurImgSize )
1407 || ( blurImg.width() > maxBlurImgSize || blurImg.height() > maxBlurImgSize ) )
1410 blurImg.fill( QColor( Qt::transparent ).rgba() );
1412 if ( !pictp.begin( &blurImg ) )
1414 pictp.setRenderHints( QPainter::Antialiasing | QPainter::SmoothPixmapTransform );
1415 QPointF imgOffset( blurbuffer + pictbuffer + xOffset,
1416 blurbuffer + pictbuffer + componentHeight + yOffset );
1418 pictp.drawPicture( imgOffset,
1419 component.picture );
1422 pictp.setCompositionMode( QPainter::CompositionMode_SourceIn );
1423 pictp.fillRect( blurImg.rect(), shadow.
color() );
1427 if ( shadow.
blurRadius() > 0.0 && radius > 0 )
1435 picti.begin( &blurImg );
1436 picti.setBrush( Qt::Dense7Pattern );
1437 QPen imgPen( QColor( 0, 0, 255, 255 ) );
1438 imgPen.setWidth( 1 );
1439 picti.setPen( imgPen );
1440 picti.setOpacity( 0.1 );
1441 picti.drawRect( 0, 0, blurImg.width(), blurImg.height() );
1448 double angleRad = shadow.
offsetAngle() * M_PI / 180;
1456 angleRad -= ( component.rotation * M_PI / 180 + component.rotationOffset * M_PI / 180 );
1459 QPointF transPt( -offsetDist * std::cos( angleRad + M_PI_2 ),
1460 -offsetDist * std::sin( angleRad + M_PI_2 ) );
1466 p->setRenderHint( QPainter::SmoothPixmapTransform );
1469 p->setCompositionMode( shadow.
blendMode() );
1471 p->setOpacity( shadow.
opacity() );
1473 double scale = shadow.
scale() / 100.0;
1475 p->scale( scale, scale );
1476 if ( component.useOrigin )
1478 p->translate( component.origin.x(), component.origin.y() );
1480 p->translate( transPt );
1481 p->translate( -imgOffset.x(),
1483 p->drawImage( 0, 0, blurImg );
1490 p->setBrush( Qt::NoBrush );
1491 QPen imgPen( QColor( 255, 0, 0, 10 ) );
1492 imgPen.setWidth( 2 );
1493 imgPen.setStyle( Qt::DashLine );
1494 p->setPen( imgPen );
1495 p->scale( scale, scale );
1496 if ( component.useOrigin() )
1498 p->translate( component.origin().x(), component.origin().y() );
1500 p->translate( transPt );
1501 p->translate( -imgOffset.x(),
1503 p->drawRect( 0, 0, blurImg.width(), blurImg.height() );
1508 p->setBrush( Qt::NoBrush );
1509 QPen componentRectPen( QColor( 0, 255, 0, 70 ) );
1510 componentRectPen.setWidth( 1 );
1511 if ( component.useOrigin() )
1513 p->translate( component.origin().x(), component.origin().y() );
1515 p->setPen( componentRectPen );
1516 p->drawRect( QRect( -xOffset, -componentHeight - yOffset, componentWidth, componentHeight ) );
1525 const Component &component,
1537 std::optional< QgsScopedRenderContextReferenceScaleOverride > referenceScaleOverride;
1548 referenceScaleOverride.reset();
1550 double rotation = 0;
1551 const Qgis::TextOrientation orientation = calculateRotationAndOrientationForComponent( format, component, rotation );
1552 switch ( orientation )
1556 drawTextInternalHorizontal( context, format, components, mode, component, document, metrics, fontScale, alignment, vAlignment, rotation );
1565 drawTextInternalVertical( context, format,
Qgis::TextComponent::Buffer, mode, component, document, metrics, fontScale, alignment, vAlignment, rotation );
1567 drawTextInternalVertical( context, format,
Qgis::TextComponent::Text, mode, component, document, metrics, fontScale, alignment, vAlignment, rotation );
1573Qgis::TextOrientation QgsTextRenderer::calculateRotationAndOrientationForComponent(
const QgsTextFormat &format,
const QgsTextRenderer::Component &component,
double &rotation )
1575 rotation = -component.rotation * 180 / M_PI;
1582 if ( rotation >= -315 && rotation < -90 )
1587 else if ( rotation >= -90 && rotation < -45 )
1603void QgsTextRenderer::calculateExtraSpacingForLineJustification(
const double spaceToDistribute,
const QgsTextBlock &block,
double &extraWordSpace,
double &extraLetterSpace )
1606 QTextBoundaryFinder
finder( QTextBoundaryFinder::Word, blockText );
1608 int wordBoundaries = 0;
1609 while (
finder.toNextBoundary() != -1 )
1611 if (
finder.boundaryReasons() & QTextBoundaryFinder::StartOfItem )
1615 if ( wordBoundaries > 0 )
1618 extraWordSpace = spaceToDistribute / wordBoundaries;
1623 QTextBoundaryFinder
finder( QTextBoundaryFinder::Grapheme, blockText );
1626 int graphemeBoundaries = 0;
1627 while (
finder.toNextBoundary() != -1 )
1629 if (
finder.boundaryReasons() & QTextBoundaryFinder::StartOfItem )
1630 graphemeBoundaries++;
1633 if ( graphemeBoundaries > 0 )
1635 extraLetterSpace = spaceToDistribute / graphemeBoundaries;
1640void QgsTextRenderer::applyExtraSpacingForLineJustification( QFont &font,
double extraWordSpace,
double extraLetterSpace )
1642 const double prevWordSpace = font.wordSpacing();
1643 font.setWordSpacing( prevWordSpace + extraWordSpace );
1644 const double prevLetterSpace = font.letterSpacing();
1645 font.setLetterSpacing( QFont::AbsoluteSpacing, prevLetterSpace + extraLetterSpace );
1649void QgsTextRenderer::renderBlockHorizontal(
const QgsTextBlock &block,
int blockIndex,
1652 QPainter *painter,
bool forceRenderAsPaths,
1653 double fontScale,
double extraWordSpace,
double extraLetterSpace,
1659 int fragmentIndex = 0;
1663 if ( !fragment.isWhitespace() && !fragment.isImage() )
1665 QFont fragmentFont = metrics.
fragmentFont( blockIndex, fragmentIndex );
1668 applyExtraSpacingForLineJustification( fragmentFont, extraWordSpace * fontScale, extraLetterSpace * fontScale );
1672 QColor textColor = fragment.characterFormat().textColor().isValid() ? fragment.characterFormat().textColor() : format.
color();
1673 textColor.setAlphaF( fragment.characterFormat().textColor().isValid() ? textColor.alphaF() * format.
opacity() : format.opacity() );
1675 if ( deferredRenderBlock )
1677 DeferredRenderFragment renderFragment;
1678 renderFragment.color = textColor;
1679 if ( forceRenderAsPaths )
1681 renderFragment.path.setFillRule( Qt::WindingFill );
1682 renderFragment.path.addText( xOffset, yOffset, fragmentFont, fragment.text() );
1684 renderFragment.font = fragmentFont;
1685 renderFragment.point = QPointF( xOffset, yOffset );
1686 renderFragment.text = fragment.text();
1687 deferredRenderBlock->fragments.append( renderFragment );
1689 else if ( forceRenderAsPaths )
1691 painter->setBrush( textColor );
1693 path.setFillRule( Qt::WindingFill );
1694 path.addText( xOffset, yOffset, fragmentFont, fragment.text() );
1695 painter->drawPath( path );
1699 painter->setPen( textColor );
1700 painter->setFont( fragmentFont );
1701 painter->drawText( QPointF( xOffset, yOffset ), fragment.text() );
1704 else if ( fragment.isImage() )
1706 bool fitsInCache =
false;
1708 const double imageHeight = metrics.
fragmentFixedHeight( blockIndex, fragmentIndex, mode ) * fontScale;
1711 QSize(
static_cast< int >( std::round( imageWidth ) ),
1712 static_cast< int >( std::round( imageHeight ) ) ),
1716 const double yOffset = imageBaseline - image.height();
1717 if ( !image.isNull() )
1718 painter->drawImage( QPointF( xOffset, yOffset ), image );
1749 if ( format.
font().underline()
1750 || format.
font().overline()
1751 || format.
font().strikeOut()
1752 || std::any_of( document.begin(), document.end(), [](
const QgsTextBlock & block )
1754 return std::any_of( block.begin(), block.end(), []( const QgsTextFragment & fragment )
1756 return fragment.characterFormat().underline() == QgsTextCharacterFormat::BooleanValue::SetTrue
1757 || fragment.characterFormat().overline() == QgsTextCharacterFormat::BooleanValue::SetTrue
1758 || fragment.characterFormat().strikeOut() == QgsTextCharacterFormat::BooleanValue::SetTrue;
1771 return std::any_of( document.begin(), document.end(), [](
const QgsTextBlock & block )
1773 return std::any_of( block.begin(), block.end(), []( const QgsTextFragment & fragment )
1775 return fragment.isImage();
1782 QVector< BlockMetrics > blockMetrics;
1783 blockMetrics.reserve( document.
size() );
1793 || document.
size() > 1 );
1795 const bool isFinalLineInParagraph = ( blockIndex == document.
size() - 1 )
1796 || document.
at( blockIndex + 1 ).
toPlainText().trimmed().isEmpty();
1800 thisBlockMetrics.width = metrics.
blockWidth( blockIndex );
1802 if ( adjustForAlignment )
1804 double blockWidthDiff = 0;
1805 switch ( blockAlignment )
1812 blockWidthDiff = targetWidth - thisBlockMetrics.width - metrics.
blockRightMargin( blockIndex );
1816 if ( !isFinalLineInParagraph && targetWidth > thisBlockMetrics.width )
1818 calculateExtraSpacingForLineJustification( targetWidth - thisBlockMetrics.width, block, thisBlockMetrics.extraWordSpace, thisBlockMetrics.extraLetterSpace );
1819 thisBlockMetrics.width = targetWidth;
1835 thisBlockMetrics.xOffset = blockWidthDiff;
1840 switch ( blockAlignment )
1843 thisBlockMetrics.xOffset = blockWidthDiff - targetWidth;
1847 thisBlockMetrics.xOffset = blockWidthDiff - targetWidth / 2.0;
1869 thisBlockMetrics.backgroundWidth = targetWidth;
1870 thisBlockMetrics.backgroundXOffset = 0;
1874 thisBlockMetrics.backgroundWidth = thisBlockMetrics.width;
1875 thisBlockMetrics.backgroundXOffset = thisBlockMetrics.xOffset;
1879 blockMetrics << thisBlockMetrics;
1882 return blockMetrics;
1885QBrush QgsTextRenderer::createBrushForPath(
QgsRenderContext &context,
const QString &path )
1887 bool fitsInCache =
false;
1891 const QSizeF originalSizeMmAt96Dpi = imageSize / 3.7795275590551185;
1893 const double imageWidth = originalSizeMmAt96Dpi.width() * pixelsPerMm;
1894 const double imageHeight = originalSizeMmAt96Dpi.height() * pixelsPerMm;
1896 if ( imageWidth == 0 || imageHeight == 0 )
1899 QSize(
static_cast< int >( std::round( imageWidth ) ),
1900 static_cast< int >( std::round( imageHeight ) ) ),
1904 if ( !image.isNull() )
1907 res.setTextureImage( image );
1915 context.
painter()->translate( component.origin );
1917 context.
painter()->rotate( rotation );
1919 context.
painter()->setPen( Qt::NoPen );
1920 context.
painter()->setBrush( Qt::NoBrush );
1923 const double baseLineOffset = metrics.
baselineOffset( blockIndex, mode );
1933 if ( backgroundImageBrush.style() == Qt::BrushStyle::TexturePattern )
1934 backgroundBrush = backgroundImageBrush;
1937 context.
painter()->setBrush( backgroundBrush );
1938 context.
painter()->drawRect( QRectF( blockMetrics[ blockIndex ].backgroundXOffset, baseLineOffset - blockMaximumAscent, blockMetrics[ blockIndex ].backgroundWidth, blockMaximumDescent + blockMaximumAscent ) );
1942 int fragmentIndex = 0;
1947 const double ascent = metrics.
fragmentAscent( blockIndex, fragmentIndex, mode );
1948 const double descent = metrics.
fragmentDescent( blockIndex, fragmentIndex, mode );
1950 if ( fragment.characterFormat().hasBackground() )
1954 QBrush backgroundBrush = fragment.characterFormat().backgroundBrush();
1955 if ( !fragment.characterFormat().backgroundImagePath().isEmpty() )
1957 const QBrush backgroundImageBrush = createBrushForPath( context, fragment.characterFormat().backgroundImagePath() );
1958 if ( backgroundImageBrush.style() == Qt::BrushStyle::TexturePattern )
1959 backgroundBrush = backgroundImageBrush;
1962 context.
painter()->setBrush( backgroundBrush );
1963 context.
painter()->drawRect( QRectF( blockMetrics[ blockIndex ].xOffset + xOffset,
1964 baseLineOffset + verticalAlignOffset + yOffset - ascent, horizontalAdvance, ascent + descent ) );
1967 xOffset += horizontalAdvance;
1974 context.
painter()->setBrush( Qt::NoBrush );
1977 context.
painter()->rotate( -rotation );
1978 context.
painter()->translate( -component.origin );
1988 double targetWidth = 0.0;
1993 targetWidth = documentSize.width();
1999 targetWidth = component.size.width();
2003 double verticalAlignOffset = 0;
2007 const double overallHeight = documentSize.height();
2008 switch ( vAlignment )
2015 verticalAlignOffset = ( component.size.height() - overallHeight ) * 0.5 + metrics.
blockVerticalMargin( - 1 );
2019 verticalAlignOffset = ( component.size.height() - overallHeight ) + metrics.
blockVerticalMargin( - 1 );
2029 const bool usePathsForText = usePathsToRender( context, format, document );
2033 std::unique_ptr< std::vector< DeferredRenderBlock > > deferredBlocks;
2040 if ( requiresMultiPassRendering )
2042 deferredBlocks = std::make_unique< std::vector< DeferredRenderBlock > >();
2043 deferredBlocks->reserve( document.
size() );
2050 const QVector< BlockMetrics > blockMetrics = calculateBlockMetrics( document, metrics, mode, targetWidth, hAlignment );
2054 renderDocumentBackgrounds( context, document, metrics, component, blockMetrics, mode, verticalAlignOffset, rotation );
2060 const double blockHeight = metrics.
blockHeight( blockIndex );
2062 DeferredRenderBlock *deferredBlock =
nullptr;
2063 if ( requiresMultiPassRendering && deferredBlocks )
2065 deferredBlocks->emplace_back( DeferredRenderBlock() );
2066 deferredBlock = &deferredBlocks->back();
2067 deferredBlock->fragments.reserve( block.
size() );
2072 context.
painter()->translate( component.origin );
2074 context.
painter()->rotate( rotation );
2079 maskPainter->save();
2080 maskPainter->translate( component.origin );
2082 maskPainter->rotate( rotation );
2085 const BlockMetrics thisBlockMetrics = blockMetrics[ blockIndex ];
2086 const double baseLineOffset = metrics.
baselineOffset( blockIndex, mode );
2088 const QPointF blockOrigin( thisBlockMetrics.xOffset, baseLineOffset + verticalAlignOffset );
2089 if ( deferredBlock )
2090 deferredBlock->origin = blockOrigin;
2092 context.
painter()->translate( blockOrigin );
2094 maskPainter->translate( blockOrigin );
2096 Component subComponent;
2097 subComponent.block = block;
2098 subComponent.blockIndex = blockIndex;
2099 subComponent.
size = QSizeF( thisBlockMetrics.width, blockHeight );
2100 subComponent.offset = QPointF( 0.0, -metrics.
ascentOffset() );
2101 subComponent.rotation = -component.rotation * 180 / M_PI;
2102 subComponent.rotationOffset = 0.0;
2103 subComponent.extraWordSpacing = thisBlockMetrics.extraWordSpace * fontScale;
2104 subComponent.extraLetterSpacing = thisBlockMetrics.extraLetterSpace * fontScale;
2105 if ( deferredBlock )
2106 deferredBlock->component = subComponent;
2111 QgsTextRenderer::drawMask( context, subComponent, format, metrics, mode );
2115 const bool needsPaths = usePathsForText
2119 std::optional< QgsScopedRenderContextReferenceScaleOverride > referenceScaleOverride;
2127 referenceScaleOverride.reset();
2136 context.
painter()->scale( subComponent.dpiRatio, subComponent.dpiRatio );
2138 context.
painter()->scale( 1 / fontScale, 1 / fontScale );
2139 context.
painter()->setPen( Qt::NoPen );
2140 context.
painter()->setBrush( Qt::NoBrush );
2142 renderBlockHorizontal( block, blockIndex, metrics, context, format, context.
painter(), needsPaths,
2143 fontScale, thisBlockMetrics.extraWordSpace, thisBlockMetrics.extraLetterSpace, mode, deferredBlock );
2146 maskPainter->restore();
2152 if ( deferredBlocks )
2154 renderDeferredBlocks(
2155 context, format, components, *deferredBlocks, usePathsForText, fontScale, component, rotation
2163 const std::vector< DeferredRenderBlock > &deferredBlocks,
2164 bool usePathsForText,
2166 const Component &component,
2171 renderDeferredBuffer( context, format, components, deferredBlocks, fontScale, component, rotation );
2178 renderDeferredShadowForText( context, format, deferredBlocks, fontScale, component, rotation );
2187 renderDeferredText( context, deferredBlocks, usePathsForText, fontScale, component, rotation );
2191void QgsTextRenderer::renderDeferredShadowForText(
QgsRenderContext &context,
2193 const std::vector< DeferredRenderBlock > &deferredBlocks,
2195 const Component &component,
2200 context.
painter()->translate( component.origin );
2202 context.
painter()->rotate( rotation );
2204 context.
painter()->setPen( Qt::NoPen );
2205 context.
painter()->setBrush( Qt::NoBrush );
2207 for (
const DeferredRenderBlock &block : deferredBlocks )
2209 Component subComponent = block.component;
2211 QPainter painter( &subComponent.picture );
2212 painter.setPen( Qt::NoPen );
2213 painter.setBrush( Qt::NoBrush );
2214 painter.scale( 1 / fontScale, 1 / fontScale );
2216 for (
const DeferredRenderFragment &fragment : std::as_const( block.fragments ) )
2218 if ( !fragment.path.isEmpty() )
2220 painter.setBrush( fragment.color );
2221 painter.drawPath( fragment.path );
2225 painter.setPen( fragment.color );
2226 painter.setFont( fragment.font );
2227 painter.drawText( fragment.point, fragment.text );
2232 subComponent.pictureBuffer = 1.0;
2233 subComponent.origin = QPointF( 0.0, 0.0 );
2234 const QRectF pictureBoundingRect = subComponent.picture.boundingRect();
2235 subComponent.size = pictureBoundingRect.size();
2236 subComponent.offset = QPointF( -pictureBoundingRect.left(), -pictureBoundingRect.height() - pictureBoundingRect.top() );
2238 context.
painter()->translate( block.origin );
2239 drawShadow( context, subComponent, format );
2240 context.
painter()->translate( -block.origin );
2247 const std::vector< DeferredRenderBlock > &deferredBlocks,
2249 const Component &component,
2259 std::unique_ptr< QPicture > bufferPicture;
2260 std::unique_ptr< QPainter > bufferPainter;
2261 QPainter *prevPainter = context.
painter();
2262 if ( needsShadowOnBuffer )
2264 bufferPicture = std::make_unique< QPicture >();
2265 bufferPainter = std::make_unique< QPainter >( bufferPicture.get() );
2269 std::unique_ptr< QgsPaintEffect > tmpEffect;
2273 tmpEffect->begin( context );
2278 QPen pen( bufferColor );
2283 pen.setWidthF( penSize * fontScale );
2285 context.
painter()->setPen( pen );
2290 bufferColor.setAlpha( 0 );
2292 context.
painter()->setBrush( bufferColor );
2294 context.
painter()->translate( component.origin );
2296 context.
painter()->rotate( rotation );
2303 for (
const DeferredRenderBlock &block : deferredBlocks )
2305 context.
painter()->translate( block.origin );
2306 context.
painter()->scale( 1 / fontScale, 1 / fontScale );
2307 for (
const DeferredRenderFragment &fragment : std::as_const( block.fragments ) )
2309 context.
painter()->drawPath( fragment.path );
2311 context.
painter()->scale( fontScale, fontScale );
2312 context.
painter()->translate( -block.origin );
2317 tmpEffect->end( context );
2320 if ( needsShadowOnBuffer && bufferPicture )
2322 bufferPainter->end();
2323 bufferPainter.reset();
2326 QgsTextRenderer::Component bufferComponent = component;
2327 bufferComponent.origin = QPointF( 0.0, 0.0 );
2328 bufferComponent.picture = *bufferPicture;
2329 bufferComponent.pictureBuffer = penSize / 2.0;
2330 const QRectF bufferBoundingBox = bufferPicture->boundingRect();
2331 bufferComponent.size = bufferBoundingBox.size();
2332 bufferComponent.offset = QPointF( -bufferBoundingBox.left(), -bufferBoundingBox.height() - bufferBoundingBox.top() );
2334 drawShadow( context, bufferComponent, format );
2343 context.
painter()->scale( component.dpiRatio, component.dpiRatio );
2349 const std::vector< DeferredRenderBlock > &deferredBlocks,
2350 bool usePathsForText,
2352 const Component &component,
2357 context.
painter()->translate( component.origin );
2359 context.
painter()->rotate( rotation );
2361 context.
painter()->setPen( Qt::NoPen );
2362 context.
painter()->setBrush( Qt::NoBrush );
2365 for (
const DeferredRenderBlock &block : deferredBlocks )
2367 context.
painter()->translate( block.origin );
2368 context.
painter()->scale( 1 / fontScale, 1 / fontScale );
2370 for (
const DeferredRenderFragment &fragment : std::as_const( block.fragments ) )
2372 if ( usePathsForText )
2374 context.
painter()->setBrush( fragment.color );
2375 context.
painter()->drawPath( fragment.path );
2379 context.
painter()->setPen( fragment.color );
2380 context.
painter()->setFont( fragment.font );
2381 context.
painter()->drawText( fragment.point, fragment.text );
2385 context.
painter()->scale( fontScale, fontScale );
2386 context.
painter()->translate( -block.origin );
2390void 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 )
2393 const QStringList textLines = document.
toPlainText();
2395 std::optional< QgsScopedRenderContextReferenceScaleOverride > referenceScaleOverride;
2406 referenceScaleOverride.reset();
2409 const double actualTextWidth = documentSize.width();
2410 double textRectWidth = 0.0;
2416 textRectWidth = actualTextWidth;
2422 textRectWidth = component.size.width();
2426 int maxLineLength = 0;
2427 for (
const QString &line : std::as_const( textLines ) )
2429 maxLineLength = std::max( maxLineLength,
static_cast<int>( line.length() ) );
2432 const double actualLabelHeight = documentSize.height();
2442 context.
painter()->translate( component.origin );
2444 context.
painter()->rotate( rotation );
2449 maskPainter->save();
2450 maskPainter->translate( component.origin );
2452 maskPainter->rotate( rotation );
2459 if ( adjustForAlignment )
2461 double hAlignmentOffset = 0;
2462 switch ( hAlignment )
2465 hAlignmentOffset = ( textRectWidth - actualTextWidth ) * 0.5;
2469 hAlignmentOffset = textRectWidth - actualTextWidth;
2483 xOffset += hAlignmentOffset;
2491 double yOffset = 0.0;
2497 if ( rotation >= -405 && rotation < -180 )
2501 else if ( rotation >= 0 && rotation < 45 )
2503 xOffset -= actualTextWidth;
2509 yOffset = -actualLabelHeight;
2514 yOffset = -actualLabelHeight;
2524 context.
painter()->translate( QPointF( xOffset, yOffset ) );
2526 double currentBlockYOffset = 0;
2527 int fragmentIndex = 0;
2535 const QFont fragmentFont = metrics.
fragmentFont( blockIndex, fragmentIndex );
2537 QFontMetricsF fragmentMetrics( fragmentFont );
2539 const double letterSpacing = fragmentFont.letterSpacing() / fontScale;
2540 const double labelHeight = fragmentMetrics.ascent() / fontScale + ( fragmentMetrics.ascent() / fontScale + letterSpacing ) * ( line.length() - 1 );
2542 Component subComponent;
2544 subComponent.blockIndex = blockIndex;
2545 subComponent.firstFragmentIndex = fragmentIndex;
2546 subComponent.size = QSizeF( blockMaximumCharacterWidth, labelHeight + fragmentMetrics.descent() / fontScale );
2547 subComponent.offset = QPointF( 0.0, currentBlockYOffset );
2548 subComponent.rotation = -component.rotation * 180 / M_PI;
2549 subComponent.rotationOffset = 0.0;
2556 QgsTextRenderer::drawMask( context, subComponent, format );
2562 currentBlockYOffset += QgsTextRenderer::drawBuffer( context, subComponent, format, metrics, mode );
2568 path.setFillRule( Qt::WindingFill );
2570 double partYOffset = 0.0;
2571 for (
const QString &part : parts )
2573 double partXOffset = ( blockMaximumCharacterWidth - ( fragmentMetrics.horizontalAdvance( part ) / fontScale - letterSpacing ) ) / 2;
2574 partYOffset += fragmentMetrics.ascent() / fontScale;
2575 path.addText( partXOffset * fontScale, partYOffset * fontScale, fragmentFont, part );
2576 partYOffset += letterSpacing;
2582 textp.begin( &textPict );
2583 textp.setPen( Qt::NoPen );
2584 QColor textColor = fragment.characterFormat().textColor().isValid() ? fragment.characterFormat().textColor() : format.
color();
2585 textColor.setAlphaF( fragment.characterFormat().textColor().isValid() ? textColor.alphaF() * format.
opacity() : format.
opacity() );
2586 textp.setBrush( textColor );
2587 textp.scale( 1 / fontScale, 1 / fontScale );
2588 textp.drawPath( path );
2599 subComponent.picture = textPict;
2600 subComponent.pictureBuffer = 0.0;
2601 subComponent.origin = QPointF( 0.0, currentBlockYOffset );
2602 const double prevY = subComponent.offset.y();
2603 subComponent.offset = QPointF( 0, -subComponent.size.height() );
2604 subComponent.useOrigin =
true;
2605 QgsTextRenderer::drawShadow( context, subComponent, format );
2606 subComponent.useOrigin =
false;
2607 subComponent.offset = QPointF( 0, prevY );
2617 context.
painter()->scale( subComponent.dpiRatio, subComponent.dpiRatio );
2621 context.
painter()->translate( 0, currentBlockYOffset );
2623 context.
painter()->drawPicture( 0, 0, textPict );
2624 currentBlockYOffset += partYOffset;
2630 maskPainter->restore();
2648 if ( pixelSize < 50 )
2649 return 200 / pixelSize;
2652 else if ( pixelSize > 200 )
2653 return 200 / pixelSize;
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.
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.
A class to manager painter saving and restoring required for effect drawing.
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.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
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.
A marker symbol type, for rendering Point and MultiPoint geometries.
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.
A class to manage painter saving and restoring required for drawing on a different painter (mask pain...
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 renderered 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.
bool useAdvancedEffects() const
Returns true if advanced effects such as blend modes such be used.
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()
void setPainter(QPainter *p)
Sets the destination QPainter for the render operation.
Qgis::RenderContextFlags flags() const
Returns combination of flags used for rendering.
Scoped object for saving and restoring a QPainter object's state.
Scoped object for temporary override of the symbologyReferenceScale property of a QgsRenderContext.
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.
Container for settings relating to a text background object.
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.
Qgis::TextHorizontalAlignment horizontalAlignment() const
Returns the format horizontal alignment.
bool hasBackground() const
Returns true if the block has a background set.
QBrush backgroundBrush() const
Returns the brush used for rendering the background of the block.
QString backgroundImagePath() const
Returns the path for the image to be used for rendering the background of the fragment.
bool hasHorizontalAlignmentSet() const
Returns true if the format has an explicit horizontal alignment set.
Represents a block of text consisting of one or more QgsTextFragment objects.
int size() const
Returns the number of fragments in the block.
QString toPlainText() const
Converts the block to plain text.
const QgsTextBlockFormat & blockFormat() const
Returns the block formatting for the fragment.
Container for settings relating to a text buffer.
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 ...
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.
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.
const QgsTextBlock & at(int index) const
Returns the block at the specified index.
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.
Container for settings relating to a selective masking around a text.
Qgis::RenderUnit sizeUnit() const
Returns the units for the buffer size.
QgsMapUnitScale sizeMapUnitScale() const
Returns the map unit scale object for the buffer size.
double size() const
Returns the size of the buffer.
QgsPaintEffect * paintEffect() const
Returns the current paint effect for the mask.
double opacity() const
Returns the mask's opacity.
bool enabled() const
Returns whether the mask is enabled.
Qt::PenJoinStyle joinStyle() const
Returns the buffer join style.
Contains placement information for a single grapheme in a curved text layout.
@ RespectPainterOrientation
Curved text will be placed respecting the painter orientation, and the actual line direction will be ...
@ TruncateStringWhenLineIsTooShort
When a string is too long for the line, truncate characters instead of aborting the placement.
@ UseBaselinePlacement
Generate placement based on the character baselines instead of centers.
static std::unique_ptr< CurvePlacementProperties > generateCurvedTextPlacement(const QgsPrecalculatedTextMetrics &metrics, const QPolygonF &line, double offsetAlongLine, LabelLineDirection direction=RespectPainterOrientation, double maxConcaveAngle=-1, double maxConvexAngle=-1, CurvedTextFlags flags=CurvedTextFlags())
Calculates curved text placement properties.
static void drawDocumentOnLine(const QPolygonF &line, const QgsTextFormat &format, const QgsTextDocument &document, QgsRenderContext &context, double offsetAlongLine=0, double offsetFromLine=0)
Draws a text document along a line using the specified settings.
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 void drawTextOnLine(const QPolygonF &line, const QString &text, QgsRenderContext &context, const QgsTextFormat &format, double offsetAlongLine=0, double offsetFromLine=0)
Draws text along a line using the specified settings.
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 Qgis::TextHorizontalAlignment convertQtHAlignment(Qt::Alignment alignment)
Converts a Qt horizontal alignment flag to a Qgis::TextHorizontalAlignment value.
Container for settings relating to a text shadow.
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