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 );
159 lFormat = updateShadowPosition( lFormat );
166 drawDocumentOnLine( line, lFormat, document, context, offsetAlongLine, offsetFromLine );
171 QPolygonF labelBaselineCurve = line;
180#if GEOS_VERSION_MAJOR==3 && GEOS_VERSION_MINOR<11
181 if ( offsetFromLine < 0 )
184 std::unique_ptr < QgsLineString > reversed( offsetCurve->reversed() );
188 offsetCurve = std::move( reversed );
192 labelBaselineCurve = offsetCurve->asQPolygonF();
197 const QFont baseFont = format.
scaledFont( context, fontScale );
198 const double letterSpacing = baseFont.letterSpacing() / fontScale;
199 const double wordSpacing = baseFont.wordSpacing() / fontScale;
201 QStringList graphemes;
202 QVector< QgsTextCharacterFormat > graphemeFormats;
203 QVector< QgsTextDocumentMetrics > graphemeMetrics;
205 for (
const QgsTextBlock &block : std::as_const( document ) )
210 for (
const QString &grapheme : fragmentGraphemes )
212 graphemes.append( grapheme );
213 graphemeFormats.append( fragment.characterFormat() );
223 QVector< double > characterWidths( graphemes.count() );
224 QVector< double > characterHeights( graphemes.count() );
225 QVector< double > characterDescents( graphemes.count() );
226 QFont previousNonSuperSubScriptFont;
228 for (
int i = 0; i < graphemes.count(); i++ )
233 double graphemeFirstCharHorizontalAdvanceWithLetterSpacing = 0;
234 double graphemeFirstCharHorizontalAdvance = 0;
235 double graphemeHorizontalAdvance = 0;
236 double characterDescent = 0;
237 double characterHeight = 0;
240 QFont graphemeFont = baseFont;
244 previousNonSuperSubScriptFont = graphemeFont;
251 previousNonSuperSubScriptFont = graphemeFont;
272 previousNonSuperSubScriptFont = graphemeFont;
275 const QFontMetricsF graphemeFontMetrics( graphemeFont );
276 graphemeFirstCharHorizontalAdvance = graphemeFontMetrics.horizontalAdvance( QString( graphemes[i].at( 0 ) ) ) / fontScale;
277 graphemeFirstCharHorizontalAdvanceWithLetterSpacing = graphemeFontMetrics.horizontalAdvance( graphemes[i].at( 0 ) ) / fontScale + letterSpacing;
278 graphemeHorizontalAdvance = graphemeFontMetrics.horizontalAdvance( QString( graphemes[i] ) ) / fontScale;
279 characterDescent = graphemeFontMetrics.descent() / fontScale;
280 characterHeight = graphemeFontMetrics.height() / fontScale;
282 qreal wordSpaceFix = qreal( 0.0 );
283 if ( graphemes[i] == QLatin1String(
" " ) )
287 wordSpaceFix = ( nxt < graphemes.count() && graphemes[nxt] != QLatin1String(
" " ) ) ? wordSpacing : qreal( 0.0 );
292 if ( graphemes[i].length() == 1 &&
293 !
qgsDoubleNear( graphemeFirstCharHorizontalAdvance, graphemeFirstCharHorizontalAdvanceWithLetterSpacing ) )
296 wordSpaceFix -= wordSpacing;
299 const double charWidth = graphemeHorizontalAdvance + wordSpaceFix;
300 characterWidths[i] = charWidth;
301 characterHeights[i] = characterHeight;
302 characterDescents[i] = characterDescent;
305 QgsPrecalculatedTextMetrics metrics( graphemes, std::move( characterWidths ), std::move( characterHeights ), std::move( characterDescents ) );
309 metrics, labelBaselineCurve, offsetAlongLine,
316 if ( placement->graphemePlacement.empty() )
323 QHash< int, QgsTextRenderer::Component > components;
324 components.reserve( placement->graphemePlacement.size() );
327 QgsTextRenderer::Component component;
328 component.origin = QPointF( grapheme.x, grapheme.y );
329 component.rotation = -grapheme.angle;
335 component.origin.rx() += verticalOffset * std::cos( grapheme.angle + M_PI_2 );
336 component.origin.ry() += verticalOffset * std::sin( grapheme.angle + M_PI_2 );
339 components.insert( grapheme.graphemeIndex, component );
347 const QgsTextRenderer::Component &component = components[grapheme.graphemeIndex ];
357 const QgsTextRenderer::Component &component = components[grapheme.graphemeIndex ];
374 const QgsTextRenderer::Component &component = components[grapheme.graphemeIndex ];
427 component.dpiRatio = 1.0;
428 component.origin = rect.topLeft();
429 component.rotation = rotation;
430 component.size = rect.size();
431 component.hAlign = alignment;
439 double xc = rect.width() / 2.0;
440 double yc = rect.height() / 2.0;
442 double angle = -rotation;
443 double xd = xc * std::cos( angle ) - yc * std::sin( angle );
444 double yd = xc * std::sin( angle ) + yc * std::cos( angle );
446 component.center = QPointF( component.origin.x() + xd, component.origin.y() + yd );
450 component.center = rect.center();
453 switch ( vAlignment )
475 drawTextInternal( parts, context, format, component,
477 alignment, vAlignment, mode );
498 component.dpiRatio = 1.0;
499 component.origin = origin;
500 component.rotation = rotation;
501 component.hAlign = alignment;
505 QgsTextRenderer::drawBackground( context, component, format, metrics, mode );
515 drawTextInternal( parts, context, format, component,
525 return QFontMetricsF( format.
scaledFont( context, scaleFactor ), context.
painter() ? context.
painter()->device() : nullptr );
532 QPainter *p = context.
painter();
537 if ( component.rotation >= -315 && component.rotation < -90 )
541 else if ( component.rotation >= -90 && component.rotation < -45 )
559 std::optional< QgsScopedRenderContextReferenceScaleOverride > referenceScaleOverride;
570 referenceScaleOverride.reset();
573 path.setFillRule( Qt::WindingFill );
575 double height = component.size.height();
576 switch ( orientation )
587 double partYOffset = component.offset.y() * scaleFactor;
590 double partLastDescent = 0;
592 int fragmentIndex = 0;
595 const QFont fragmentFont = metrics.
fragmentFont( component.blockIndex, component.firstFragmentIndex + fragmentIndex );
596 const double letterSpacing = fragmentFont.letterSpacing() / scaleFactor;
598 const QFontMetricsF fragmentMetrics( fragmentFont );
600 const double fragmentYOffset = metrics.
fragmentVerticalOffset( component.blockIndex, fragmentIndex, mode );
603 for (
const QString &part : parts )
605 double partXOffset = ( blockMaximumCharacterWidth - ( fragmentMetrics.horizontalAdvance( part ) / scaleFactor - letterSpacing ) ) / 2;
606 partYOffset += fragmentMetrics.ascent() / scaleFactor;
607 path.addText( partXOffset, partYOffset + fragmentYOffset, fragmentFont, part );
608 partYOffset += letterSpacing;
610 partLastDescent = fragmentMetrics.descent() / scaleFactor;
614 height = partYOffset + partLastDescent;
615 advance = partYOffset - component.offset.y() * scaleFactor;
620 QColor bufferColor = buffer.
color();
621 bufferColor.setAlphaF( buffer.
opacity() );
622 QPen pen( bufferColor );
623 pen.setWidthF( penSize * scaleFactor );
625 QColor tmpColor( bufferColor );
629 tmpColor.setAlpha( 0 );
635 buffp.begin( &buffPict );
639 std::unique_ptr< QgsPaintEffect > tmpEffect( buffer.
paintEffect()->
clone() );
641 tmpEffect->begin( context );
642 context.
painter()->setPen( pen );
643 context.
painter()->setBrush( tmpColor );
644 if ( scaleFactor != 1.0 )
645 context.
painter()->scale( 1 / scaleFactor, 1 / scaleFactor );
646 context.
painter()->drawPath( path );
647 if ( scaleFactor != 1.0 )
648 context.
painter()->scale( scaleFactor, scaleFactor );
649 tmpEffect->end( context );
655 if ( scaleFactor != 1.0 )
656 buffp.scale( 1 / scaleFactor, 1 / scaleFactor );
658 buffp.setBrush( tmpColor );
659 buffp.drawPath( path );
665 QgsTextRenderer::Component bufferComponent = component;
666 bufferComponent.origin = QPointF( 0.0, 0.0 );
667 bufferComponent.picture = buffPict;
668 bufferComponent.pictureBuffer = penSize / 2.0;
669 bufferComponent.size.setHeight( height );
673 bufferComponent.offset.setY( - bufferComponent.size.height() );
675 drawShadow( context, bufferComponent, format );
683 p->setCompositionMode( buffer.
blendMode() );
687 p->scale( component.dpiRatio, component.dpiRatio );
689 p->drawPicture( 0, 0, buffPict );
691 return advance / scaleFactor;
711 path.setFillRule( Qt::WindingFill );
718 std::optional< QgsScopedRenderContextReferenceScaleOverride > referenceScaleOverride;
729 referenceScaleOverride.reset();
732 int fragmentIndex = 0;
735 if ( !fragment.isWhitespace() && !fragment.isImage() )
737 const QFont fragmentFont = metrics.
fragmentFont( component.blockIndex, fragmentIndex );
739 const double fragmentYOffset = metrics.
fragmentVerticalOffset( component.blockIndex, fragmentIndex, mode );
740 path.addText( xOffset, fragmentYOffset, fragmentFont, fragment.text() );
747 QColor bufferColor( Qt::gray );
748 bufferColor.setAlphaF( mask.
opacity() );
752 brush.setColor( bufferColor );
753 pen.setColor( bufferColor );
754 pen.setWidthF( penSize * scaleFactor );
761 p->scale( component.dpiRatio, component.dpiRatio );
767 if ( scaleFactor != 1.0 )
768 context.
painter()->scale( 1 / scaleFactor, 1 / scaleFactor );
769 context.
painter()->setPen( pen );
770 context.
painter()->setBrush( brush );
771 context.
painter()->drawPath( path );
772 if ( scaleFactor != 1.0 )
773 context.
painter()->scale( scaleFactor, scaleFactor );
778 if ( scaleFactor != 1.0 )
779 p->scale( 1 / scaleFactor, 1 / scaleFactor );
781 p->setBrush( brush );
783 if ( scaleFactor != 1.0 )
784 p->scale( scaleFactor, scaleFactor );
792 if ( doc.
size() == 0 )
795 return textWidth( context, format, doc );
812 for (
const QString &line : textLines )
816 lines.append(
wrappedText( context, line, maxLineWidth, format ) );
820 lines.append( line );
825 return textHeight( context, format, doc, mode );
832 bool isNullSize =
false;
833 const QFont baseFont = format.
scaledFont( context, scaleFactor, &isNullSize );
837 const QFontMetrics fm( baseFont );
838 const double height = ( character.isNull() ? fm.height() : fm.boundingRect( character ).height() ) / scaleFactor;
840 if ( !includeEffects )
843 double maxExtension = 0;
872 return height + maxExtension;
880 const QStringList multiLineSplit = text.split(
'\n' );
882 return currentTextWidth > width;
887 const QStringList lines = text.split(
'\n' );
888 QStringList outLines;
889 for (
const QString &line : lines )
894 const QStringList words = line.split(
' ' );
895 QStringList linesToProcess;
896 QString wordsInCurrentLine;
897 for (
const QString &word : words )
902 if ( !wordsInCurrentLine.isEmpty() )
903 linesToProcess << wordsInCurrentLine;
904 wordsInCurrentLine.clear();
905 linesToProcess << word;
909 if ( !wordsInCurrentLine.isEmpty() )
910 wordsInCurrentLine.append(
' ' );
911 wordsInCurrentLine.append( word );
914 if ( !wordsInCurrentLine.isEmpty() )
915 linesToProcess << wordsInCurrentLine;
917 for (
const QString &line : std::as_const( linesToProcess ) )
919 QString remainingText = line;
920 int lastPos = remainingText.lastIndexOf(
' ' );
921 while ( lastPos > -1 )
931 outLines << remainingText.left( lastPos );
932 remainingText = remainingText.mid( lastPos + 1 );
935 lastPos = remainingText.lastIndexOf(
' ', lastPos - 1 );
937 outLines << remainingText;
966 Component component =
c;
969 QPainter *prevP = context.
painter();
970 QPainter *p = context.
painter();
971 std::unique_ptr< QgsPaintEffect > tmpEffect;
975 tmpEffect->begin( context );
984 const double originAdjustRotationRadians = -component.rotation;
987 component.rotation = -( component.rotation * 180 / M_PI );
988 component.rotationOffset =
993 component.rotation = 0.0;
994 component.rotationOffset = background.
rotation();
1003 double width = documentSize.width();
1004 double height = documentSize.height();
1011 switch ( component.hAlign )
1015 component.center = QPointF( component.origin.x() + width / 2.0,
1016 component.origin.y() + height / 2.0 );
1020 component.center = QPointF( component.origin.x() + component.size.width() / 2.0,
1021 component.origin.y() + height / 2.0 );
1025 component.center = QPointF( component.origin.x() + component.size.width() - width / 2.0,
1026 component.origin.y() + height / 2.0 );
1033 bool isNullSize =
false;
1034 QFontMetricsF fm( format.
scaledFont( context, scaleFactor, &isNullSize ) );
1035 double originAdjust = isNullSize ? 0 : ( fm.ascent() / scaleFactor / 2.0 - fm.leading() / scaleFactor / 2.0 );
1036 switch ( component.hAlign )
1040 component.center = QPointF( component.origin.x() + width / 2.0,
1041 component.origin.y() - height / 2.0 + originAdjust );
1045 component.center = QPointF( component.origin.x(),
1046 component.origin.y() - height / 2.0 + originAdjust );
1050 component.center = QPointF( component.origin.x() - width / 2.0,
1051 component.origin.y() - height / 2.0 + originAdjust );
1058 const double dx = component.center.x() - component.origin.x();
1059 const double dy = component.center.y() - component.origin.y();
1060 component.center.setX( component.origin.x() + ( std::cos( originAdjustRotationRadians ) * dx - std::sin( originAdjustRotationRadians ) * dy ) );
1061 component.center.setY( component.origin.y() + ( std::sin( originAdjustRotationRadians ) * dx + std::cos( originAdjustRotationRadians ) * dy ) );
1071 component.size = QSizeF( width, height );
1076 switch ( background.
type() )
1089 double sizeOut = 0.0;
1100 sizeOut = std::max( component.size.width(), component.size.height() );
1104 sizeOut += bufferSize * 2;
1110 if ( sizeOut < 1.0 )
1113 std::unique_ptr< QgsMarkerSymbol > renderedSymbol;
1117 map[QStringLiteral(
"name" )] = background.
svgFile().trimmed();
1118 map[QStringLiteral(
"size" )] = QString::number( sizeOut );
1120 map[QStringLiteral(
"angle" )] = QString::number( 0.0 );
1128 map[QStringLiteral(
"fill" )] = background.
fillColor().name();
1129 map[QStringLiteral(
"outline" )] = background.
strokeColor().name();
1130 map[QStringLiteral(
"outline-width" )] = QString::number( background.
strokeWidth() );
1137 QVariantMap shdwmap( map );
1138 shdwmap[QStringLiteral(
"fill" )] = shadow.
color().name();
1139 shdwmap[QStringLiteral(
"outline" )] = shadow.
color().name();
1140 shdwmap[QStringLiteral(
"size" )] = QString::number( sizeOut );
1145 svgp.begin( &svgPict );
1162 svgShdwM->
renderPoint( QPointF( sizeOut / 2, -sizeOut / 2 ), svgShdwContext );
1165 component.picture = svgPict;
1167 component.pictureBuffer = 0.0;
1169 component.size = QSizeF( sizeOut, sizeOut );
1170 component.offset = QPointF( 0.0, 0.0 );
1176 p->translate( component.center.x(), component.center.y() );
1177 p->rotate( component.rotation );
1180 p->translate( QPointF( xoff, yoff ) );
1181 p->rotate( component.rotationOffset );
1182 p->translate( -sizeOut / 2, sizeOut / 2 );
1184 drawShadow( context, component, format );
1186 renderedSymbol.reset( );
1194 renderedSymbol->setSize( sizeOut );
1198 renderedSymbol->setOpacity( renderedSymbol->opacity() * background.
opacity() );
1206 p->setCompositionMode( background.
blendMode() );
1208 p->translate( component.center.x(), component.center.y() );
1209 p->rotate( component.rotation );
1212 p->translate( QPointF( xoff, yoff ) );
1213 p->rotate( component.rotationOffset );
1217 renderedSymbol->renderPoint( QPointF( 0, 0 ), &f, context );
1218 renderedSymbol->stopRender( context );
1219 p->setCompositionMode( QPainter::CompositionMode_SourceOver );
1229 double w = component.size.width();
1230 double h = component.size.height();
1251 h = std::sqrt( std::pow( w, 2 ) + std::pow( h, 2 ) );
1257 h = h * M_SQRT1_2 * 2;
1258 w = w * M_SQRT1_2 * 2;
1266 w += bufferWidth * 2;
1267 h += bufferHeight * 2;
1271 QRectF rect( -w / 2.0, - h / 2.0, w, h );
1273 if ( rect.isNull() )
1279 p->translate( QPointF( component.center.x(), component.center.y() ) );
1280 p->rotate( component.rotation );
1283 p->translate( QPointF( xoff, yoff ) );
1284 p->rotate( component.rotationOffset );
1290 QTransform t = QTransform::fromScale( 10, 10 );
1292 QTransform ti = t.inverted();
1299 path.addRoundedRect( rect, background.
radii().width(), background.
radii().height(), Qt::RelativeSize );
1305 path.addRoundedRect( rect, xRadius, yRadius );
1311 path.addEllipse( rect );
1313 QPolygonF tempPolygon = path.toFillPolygon( t );
1314 QPolygonF polygon = ti.map( tempPolygon );
1316 QPainter *oldp = context.
painter();
1319 shapep.begin( &shapePict );
1322 std::unique_ptr< QgsFillSymbol > renderedSymbol;
1324 renderedSymbol->setOpacity( renderedSymbol->opacity() * background.
opacity() );
1328 renderedSymbol->renderPolygon( polygon,
nullptr, &f, context );
1329 renderedSymbol->stopRender( context );
1336 component.picture = shapePict;
1339 component.size = rect.size();
1340 component.offset = QPointF( rect.width() / 2, -rect.height() / 2 );
1341 drawShadow( context, component, format );
1346 p->setCompositionMode( background.
blendMode() );
1350 p->scale( component.dpiRatio, component.dpiRatio );
1352 p->drawPicture( 0, 0, shapePict );
1353 p->setCompositionMode( QPainter::CompositionMode_SourceOver );
1360 tmpEffect->end( context );
1369 QPainter *p = context.
painter();
1370 const double componentWidth = component.size.width();
1371 const double componentHeight = component.size.height();
1372 const double xOffset = component.offset.x();
1373 const double yOffset = component.offset.y();
1374 double pictbuffer = component.pictureBuffer;
1383 radius /= ( mapUnits ? context.
scaleFactor() / component.dpiRatio : 1 );
1384 radius =
static_cast< int >( radius + 0.5 );
1388 double blurBufferClippingScale = 3.75;
1389 int blurbuffer = ( radius > 17 ? 16 : radius ) * blurBufferClippingScale;
1391 QImage blurImg( componentWidth + ( pictbuffer * 2.0 ) + ( blurbuffer * 2.0 ),
1392 componentHeight + ( pictbuffer * 2.0 ) + ( blurbuffer * 2.0 ),
1393 QImage::Format_ARGB32_Premultiplied );
1397 int minBlurImgSize = 1;
1401 int maxBlurImgSize = 40000;
1402 if ( blurImg.isNull()
1403 || ( blurImg.width() < minBlurImgSize || blurImg.height() < minBlurImgSize )
1404 || ( blurImg.width() > maxBlurImgSize || blurImg.height() > maxBlurImgSize ) )
1407 blurImg.fill( QColor( Qt::transparent ).rgba() );
1409 if ( !pictp.begin( &blurImg ) )
1411 pictp.setRenderHints( QPainter::Antialiasing | QPainter::SmoothPixmapTransform );
1412 QPointF imgOffset( blurbuffer + pictbuffer + xOffset,
1413 blurbuffer + pictbuffer + componentHeight + yOffset );
1415 pictp.drawPicture( imgOffset,
1416 component.picture );
1419 pictp.setCompositionMode( QPainter::CompositionMode_SourceIn );
1420 pictp.fillRect( blurImg.rect(), shadow.
color() );
1424 if ( shadow.
blurRadius() > 0.0 && radius > 0 )
1432 picti.begin( &blurImg );
1433 picti.setBrush( Qt::Dense7Pattern );
1434 QPen imgPen( QColor( 0, 0, 255, 255 ) );
1435 imgPen.setWidth( 1 );
1436 picti.setPen( imgPen );
1437 picti.setOpacity( 0.1 );
1438 picti.drawRect( 0, 0, blurImg.width(), blurImg.height() );
1445 double angleRad = shadow.
offsetAngle() * M_PI / 180;
1453 angleRad -= ( component.rotation * M_PI / 180 + component.rotationOffset * M_PI / 180 );
1456 QPointF transPt( -offsetDist * std::cos( angleRad + M_PI_2 ),
1457 -offsetDist * std::sin( angleRad + M_PI_2 ) );
1463 p->setRenderHint( QPainter::SmoothPixmapTransform );
1466 p->setCompositionMode( shadow.
blendMode() );
1468 p->setOpacity( shadow.
opacity() );
1470 double scale = shadow.
scale() / 100.0;
1472 p->scale( scale, scale );
1473 if ( component.useOrigin )
1475 p->translate( component.origin.x(), component.origin.y() );
1477 p->translate( transPt );
1478 p->translate( -imgOffset.x(),
1480 p->drawImage( 0, 0, blurImg );
1487 p->setBrush( Qt::NoBrush );
1488 QPen imgPen( QColor( 255, 0, 0, 10 ) );
1489 imgPen.setWidth( 2 );
1490 imgPen.setStyle( Qt::DashLine );
1491 p->setPen( imgPen );
1492 p->scale( scale, scale );
1493 if ( component.useOrigin() )
1495 p->translate( component.origin().x(), component.origin().y() );
1497 p->translate( transPt );
1498 p->translate( -imgOffset.x(),
1500 p->drawRect( 0, 0, blurImg.width(), blurImg.height() );
1505 p->setBrush( Qt::NoBrush );
1506 QPen componentRectPen( QColor( 0, 255, 0, 70 ) );
1507 componentRectPen.setWidth( 1 );
1508 if ( component.useOrigin() )
1510 p->translate( component.origin().x(), component.origin().y() );
1512 p->setPen( componentRectPen );
1513 p->drawRect( QRect( -xOffset, -componentHeight - yOffset, componentWidth, componentHeight ) );
1522 const Component &component,
1534 std::optional< QgsScopedRenderContextReferenceScaleOverride > referenceScaleOverride;
1545 referenceScaleOverride.reset();
1547 double rotation = 0;
1548 const Qgis::TextOrientation orientation = calculateRotationAndOrientationForComponent( format, component, rotation );
1549 switch ( orientation )
1553 drawTextInternalHorizontal( context, format, components, mode, component, document, metrics, fontScale, alignment, vAlignment, rotation );
1562 drawTextInternalVertical( context, format,
Qgis::TextComponent::Buffer, mode, component, document, metrics, fontScale, alignment, vAlignment, rotation );
1564 drawTextInternalVertical( context, format,
Qgis::TextComponent::Text, mode, component, document, metrics, fontScale, alignment, vAlignment, rotation );
1570Qgis::TextOrientation QgsTextRenderer::calculateRotationAndOrientationForComponent(
const QgsTextFormat &format,
const QgsTextRenderer::Component &component,
double &rotation )
1572 rotation = -component.rotation * 180 / M_PI;
1579 if ( rotation >= -315 && rotation < -90 )
1584 else if ( rotation >= -90 && rotation < -45 )
1600void QgsTextRenderer::calculateExtraSpacingForLineJustification(
const double spaceToDistribute,
const QgsTextBlock &block,
double &extraWordSpace,
double &extraLetterSpace )
1603 QTextBoundaryFinder
finder( QTextBoundaryFinder::Word, blockText );
1605 int wordBoundaries = 0;
1606 while (
finder.toNextBoundary() != -1 )
1608 if (
finder.boundaryReasons() & QTextBoundaryFinder::StartOfItem )
1612 if ( wordBoundaries > 0 )
1615 extraWordSpace = spaceToDistribute / wordBoundaries;
1620 QTextBoundaryFinder
finder( QTextBoundaryFinder::Grapheme, blockText );
1623 int graphemeBoundaries = 0;
1624 while (
finder.toNextBoundary() != -1 )
1626 if (
finder.boundaryReasons() & QTextBoundaryFinder::StartOfItem )
1627 graphemeBoundaries++;
1630 if ( graphemeBoundaries > 0 )
1632 extraLetterSpace = spaceToDistribute / graphemeBoundaries;
1637void QgsTextRenderer::applyExtraSpacingForLineJustification( QFont &font,
double extraWordSpace,
double extraLetterSpace )
1639 const double prevWordSpace = font.wordSpacing();
1640 font.setWordSpacing( prevWordSpace + extraWordSpace );
1641 const double prevLetterSpace = font.letterSpacing();
1642 font.setLetterSpacing( QFont::AbsoluteSpacing, prevLetterSpace + extraLetterSpace );
1646void QgsTextRenderer::renderBlockHorizontal(
const QgsTextBlock &block,
int blockIndex,
1649 QPainter *painter,
bool forceRenderAsPaths,
1650 double fontScale,
double extraWordSpace,
double extraLetterSpace,
1656 int fragmentIndex = 0;
1660 if ( !fragment.isWhitespace() && !fragment.isImage() )
1662 QFont fragmentFont = metrics.
fragmentFont( blockIndex, fragmentIndex );
1665 applyExtraSpacingForLineJustification( fragmentFont, extraWordSpace * fontScale, extraLetterSpace * fontScale );
1669 QColor textColor = fragment.characterFormat().textColor().isValid() ? fragment.characterFormat().textColor() : format.
color();
1670 textColor.setAlphaF( fragment.characterFormat().textColor().isValid() ? textColor.alphaF() * format.
opacity() : format.opacity() );
1672 if ( deferredRenderBlock )
1674 DeferredRenderFragment renderFragment;
1675 renderFragment.color = textColor;
1676 if ( forceRenderAsPaths )
1678 renderFragment.path.setFillRule( Qt::WindingFill );
1679 renderFragment.path.addText( xOffset, yOffset, fragmentFont, fragment.text() );
1681 renderFragment.font = fragmentFont;
1682 renderFragment.point = QPointF( xOffset, yOffset );
1683 renderFragment.text = fragment.text();
1684 deferredRenderBlock->fragments.append( renderFragment );
1686 else if ( forceRenderAsPaths )
1688 painter->setBrush( textColor );
1690 path.setFillRule( Qt::WindingFill );
1691 path.addText( xOffset, yOffset, fragmentFont, fragment.text() );
1692 painter->drawPath( path );
1696 painter->setPen( textColor );
1697 painter->setFont( fragmentFont );
1698 painter->drawText( QPointF( xOffset, yOffset ), fragment.text() );
1701 else if ( fragment.isImage() )
1703 bool fitsInCache =
false;
1705 const double imageHeight = metrics.
fragmentFixedHeight( blockIndex, fragmentIndex, mode ) * fontScale;
1708 QSize(
static_cast< int >( std::round( imageWidth ) ),
1709 static_cast< int >( std::round( imageHeight ) ) ),
1713 const double yOffset = imageBaseline - image.height();
1714 if ( !image.isNull() )
1715 painter->drawImage( QPointF( xOffset, yOffset ), image );
1746 if ( format.
font().underline()
1747 || format.
font().overline()
1748 || format.
font().strikeOut()
1749 || std::any_of( document.begin(), document.end(), [](
const QgsTextBlock & block )
1751 return std::any_of( block.begin(), block.end(), []( const QgsTextFragment & fragment )
1753 return fragment.characterFormat().underline() == QgsTextCharacterFormat::BooleanValue::SetTrue
1754 || fragment.characterFormat().overline() == QgsTextCharacterFormat::BooleanValue::SetTrue
1755 || fragment.characterFormat().strikeOut() == QgsTextCharacterFormat::BooleanValue::SetTrue;
1768 return std::any_of( document.begin(), document.end(), [](
const QgsTextBlock & block )
1770 return std::any_of( block.begin(), block.end(), []( const QgsTextFragment & fragment )
1772 return fragment.isImage();
1779 QVector< BlockMetrics > blockMetrics;
1780 blockMetrics.reserve( document.
size() );
1790 || document.
size() > 1 );
1792 const bool isFinalLineInParagraph = ( blockIndex == document.
size() - 1 )
1793 || document.
at( blockIndex + 1 ).
toPlainText().trimmed().isEmpty();
1797 thisBlockMetrics.width = metrics.
blockWidth( blockIndex );
1799 if ( adjustForAlignment )
1801 double blockWidthDiff = 0;
1802 switch ( blockAlignment )
1809 blockWidthDiff = targetWidth - thisBlockMetrics.width - metrics.
blockRightMargin( blockIndex );
1813 if ( !isFinalLineInParagraph && targetWidth > thisBlockMetrics.width )
1815 calculateExtraSpacingForLineJustification( targetWidth - thisBlockMetrics.width, block, thisBlockMetrics.extraWordSpace, thisBlockMetrics.extraLetterSpace );
1816 thisBlockMetrics.width = targetWidth;
1832 thisBlockMetrics.xOffset = blockWidthDiff;
1837 switch ( blockAlignment )
1840 thisBlockMetrics.xOffset = blockWidthDiff - targetWidth;
1844 thisBlockMetrics.xOffset = blockWidthDiff - targetWidth / 2.0;
1866 thisBlockMetrics.backgroundWidth = targetWidth;
1867 thisBlockMetrics.backgroundXOffset = 0;
1871 thisBlockMetrics.backgroundWidth = thisBlockMetrics.width;
1872 thisBlockMetrics.backgroundXOffset = thisBlockMetrics.xOffset;
1876 blockMetrics << thisBlockMetrics;
1879 return blockMetrics;
1882QBrush QgsTextRenderer::createBrushForPath(
QgsRenderContext &context,
const QString &path )
1884 bool fitsInCache =
false;
1888 const QSizeF originalSizeMmAt96Dpi = imageSize / 3.7795275590551185;
1890 const double imageWidth = originalSizeMmAt96Dpi.width() * pixelsPerMm;
1891 const double imageHeight = originalSizeMmAt96Dpi.height() * pixelsPerMm;
1893 if ( imageWidth == 0 || imageHeight == 0 )
1896 QSize(
static_cast< int >( std::round( imageWidth ) ),
1897 static_cast< int >( std::round( imageHeight ) ) ),
1901 if ( !image.isNull() )
1904 res.setTextureImage( image );
1912 context.
painter()->translate( component.origin );
1914 context.
painter()->rotate( rotation );
1916 context.
painter()->setPen( Qt::NoPen );
1917 context.
painter()->setBrush( Qt::NoBrush );
1920 const double baseLineOffset = metrics.
baselineOffset( blockIndex, mode );
1930 if ( backgroundImageBrush.style() == Qt::BrushStyle::TexturePattern )
1931 backgroundBrush = backgroundImageBrush;
1934 context.
painter()->setBrush( backgroundBrush );
1935 context.
painter()->drawRect( QRectF( blockMetrics[ blockIndex ].backgroundXOffset, baseLineOffset - blockMaximumAscent, blockMetrics[ blockIndex ].backgroundWidth, blockMaximumDescent + blockMaximumAscent ) );
1939 int fragmentIndex = 0;
1944 const double ascent = metrics.
fragmentAscent( blockIndex, fragmentIndex, mode );
1945 const double descent = metrics.
fragmentDescent( blockIndex, fragmentIndex, mode );
1947 if ( fragment.characterFormat().hasBackground() )
1951 QBrush backgroundBrush = fragment.characterFormat().backgroundBrush();
1952 if ( !fragment.characterFormat().backgroundImagePath().isEmpty() )
1954 const QBrush backgroundImageBrush = createBrushForPath( context, fragment.characterFormat().backgroundImagePath() );
1955 if ( backgroundImageBrush.style() == Qt::BrushStyle::TexturePattern )
1956 backgroundBrush = backgroundImageBrush;
1959 context.
painter()->setBrush( backgroundBrush );
1960 context.
painter()->drawRect( QRectF( blockMetrics[ blockIndex ].xOffset + xOffset,
1961 baseLineOffset + verticalAlignOffset + yOffset - ascent, horizontalAdvance, ascent + descent ) );
1964 xOffset += horizontalAdvance;
1971 context.
painter()->setBrush( Qt::NoBrush );
1974 context.
painter()->rotate( -rotation );
1975 context.
painter()->translate( -component.origin );
1985 double targetWidth = 0.0;
1990 targetWidth = documentSize.width();
1996 targetWidth = component.size.width();
2000 double verticalAlignOffset = 0;
2004 const double overallHeight = documentSize.height();
2005 switch ( vAlignment )
2012 verticalAlignOffset = ( component.size.height() - overallHeight ) * 0.5 + metrics.
blockVerticalMargin( - 1 );
2016 verticalAlignOffset = ( component.size.height() - overallHeight ) + metrics.
blockVerticalMargin( - 1 );
2026 const bool usePathsForText = usePathsToRender( context, format, document );
2030 std::unique_ptr< std::vector< DeferredRenderBlock > > deferredBlocks;
2037 if ( requiresMultiPassRendering )
2039 deferredBlocks = std::make_unique< std::vector< DeferredRenderBlock > >();
2040 deferredBlocks->reserve( document.
size() );
2047 const QVector< BlockMetrics > blockMetrics = calculateBlockMetrics( document, metrics, mode, targetWidth, hAlignment );
2051 renderDocumentBackgrounds( context, document, metrics, component, blockMetrics, mode, verticalAlignOffset, rotation );
2057 const double blockHeight = metrics.
blockHeight( blockIndex );
2059 DeferredRenderBlock *deferredBlock =
nullptr;
2060 if ( requiresMultiPassRendering && deferredBlocks )
2062 deferredBlocks->emplace_back( DeferredRenderBlock() );
2063 deferredBlock = &deferredBlocks->back();
2064 deferredBlock->fragments.reserve( block.
size() );
2069 context.
painter()->translate( component.origin );
2071 context.
painter()->rotate( rotation );
2076 maskPainter->save();
2077 maskPainter->translate( component.origin );
2079 maskPainter->rotate( rotation );
2082 const BlockMetrics thisBlockMetrics = blockMetrics[ blockIndex ];
2083 const double baseLineOffset = metrics.
baselineOffset( blockIndex, mode );
2085 const QPointF blockOrigin( thisBlockMetrics.xOffset, baseLineOffset + verticalAlignOffset );
2086 if ( deferredBlock )
2087 deferredBlock->origin = blockOrigin;
2089 context.
painter()->translate( blockOrigin );
2091 maskPainter->translate( blockOrigin );
2093 Component subComponent;
2094 subComponent.block = block;
2095 subComponent.blockIndex = blockIndex;
2096 subComponent.
size = QSizeF( thisBlockMetrics.width, blockHeight );
2097 subComponent.offset = QPointF( 0.0, -metrics.
ascentOffset() );
2098 subComponent.rotation = -component.rotation * 180 / M_PI;
2099 subComponent.rotationOffset = 0.0;
2100 subComponent.extraWordSpacing = thisBlockMetrics.extraWordSpace * fontScale;
2101 subComponent.extraLetterSpacing = thisBlockMetrics.extraLetterSpace * fontScale;
2102 if ( deferredBlock )
2103 deferredBlock->component = subComponent;
2108 QgsTextRenderer::drawMask( context, subComponent, format, metrics, mode );
2112 const bool needsPaths = usePathsForText
2116 std::optional< QgsScopedRenderContextReferenceScaleOverride > referenceScaleOverride;
2124 referenceScaleOverride.reset();
2133 context.
painter()->scale( subComponent.dpiRatio, subComponent.dpiRatio );
2135 context.
painter()->scale( 1 / fontScale, 1 / fontScale );
2136 context.
painter()->setPen( Qt::NoPen );
2137 context.
painter()->setBrush( Qt::NoBrush );
2139 renderBlockHorizontal( block, blockIndex, metrics, context, format, context.
painter(), needsPaths,
2140 fontScale, thisBlockMetrics.extraWordSpace, thisBlockMetrics.extraLetterSpace, mode, deferredBlock );
2143 maskPainter->restore();
2149 if ( deferredBlocks )
2151 renderDeferredBlocks(
2152 context, format, components, *deferredBlocks, usePathsForText, fontScale, component, rotation
2160 const std::vector< DeferredRenderBlock > &deferredBlocks,
2161 bool usePathsForText,
2163 const Component &component,
2168 renderDeferredBuffer( context, format, components, deferredBlocks, fontScale, component, rotation );
2175 renderDeferredShadowForText( context, format, deferredBlocks, fontScale, component, rotation );
2184 renderDeferredText( context, deferredBlocks, usePathsForText, fontScale, component, rotation );
2188void QgsTextRenderer::renderDeferredShadowForText(
QgsRenderContext &context,
2190 const std::vector< DeferredRenderBlock > &deferredBlocks,
2192 const Component &component,
2197 context.
painter()->translate( component.origin );
2199 context.
painter()->rotate( rotation );
2201 context.
painter()->setPen( Qt::NoPen );
2202 context.
painter()->setBrush( Qt::NoBrush );
2204 for (
const DeferredRenderBlock &block : deferredBlocks )
2206 Component subComponent = block.component;
2208 QPainter painter( &subComponent.picture );
2209 painter.setPen( Qt::NoPen );
2210 painter.setBrush( Qt::NoBrush );
2211 painter.scale( 1 / fontScale, 1 / fontScale );
2213 for (
const DeferredRenderFragment &fragment : std::as_const( block.fragments ) )
2215 if ( !fragment.path.isEmpty() )
2217 painter.setBrush( fragment.color );
2218 painter.drawPath( fragment.path );
2222 painter.setPen( fragment.color );
2223 painter.setFont( fragment.font );
2224 painter.drawText( fragment.point, fragment.text );
2229 subComponent.pictureBuffer = 1.0;
2230 subComponent.origin = QPointF( 0.0, 0.0 );
2231 const QRectF pictureBoundingRect = subComponent.picture.boundingRect();
2232 subComponent.size = pictureBoundingRect.size();
2233 subComponent.offset = QPointF( -pictureBoundingRect.left(), -pictureBoundingRect.height() - pictureBoundingRect.top() );
2235 context.
painter()->translate( block.origin );
2236 drawShadow( context, subComponent, format );
2237 context.
painter()->translate( -block.origin );
2244 const std::vector< DeferredRenderBlock > &deferredBlocks,
2246 const Component &component,
2256 std::unique_ptr< QPicture > bufferPicture;
2257 std::unique_ptr< QPainter > bufferPainter;
2258 QPainter *prevPainter = context.
painter();
2259 if ( needsShadowOnBuffer )
2261 bufferPicture = std::make_unique< QPicture >();
2262 bufferPainter = std::make_unique< QPainter >( bufferPicture.get() );
2266 std::unique_ptr< QgsPaintEffect > tmpEffect;
2270 tmpEffect->begin( context );
2275 QPen pen( bufferColor );
2280 pen.setWidthF( penSize * fontScale );
2282 context.
painter()->setPen( pen );
2287 bufferColor.setAlpha( 0 );
2289 context.
painter()->setBrush( bufferColor );
2291 context.
painter()->translate( component.origin );
2293 context.
painter()->rotate( rotation );
2300 for (
const DeferredRenderBlock &block : deferredBlocks )
2302 context.
painter()->translate( block.origin );
2303 context.
painter()->scale( 1 / fontScale, 1 / fontScale );
2304 for (
const DeferredRenderFragment &fragment : std::as_const( block.fragments ) )
2306 context.
painter()->drawPath( fragment.path );
2308 context.
painter()->scale( fontScale, fontScale );
2309 context.
painter()->translate( -block.origin );
2314 tmpEffect->end( context );
2317 if ( needsShadowOnBuffer && bufferPicture )
2319 bufferPainter->end();
2320 bufferPainter.reset();
2323 QgsTextRenderer::Component bufferComponent = component;
2324 bufferComponent.origin = QPointF( 0.0, 0.0 );
2325 bufferComponent.picture = *bufferPicture;
2326 bufferComponent.pictureBuffer = penSize / 2.0;
2327 const QRectF bufferBoundingBox = bufferPicture->boundingRect();
2328 bufferComponent.size = bufferBoundingBox.size();
2329 bufferComponent.offset = QPointF( -bufferBoundingBox.left(), -bufferBoundingBox.height() - bufferBoundingBox.top() );
2331 drawShadow( context, bufferComponent, format );
2340 context.
painter()->scale( component.dpiRatio, component.dpiRatio );
2346 const std::vector< DeferredRenderBlock > &deferredBlocks,
2347 bool usePathsForText,
2349 const Component &component,
2354 context.
painter()->translate( component.origin );
2356 context.
painter()->rotate( rotation );
2358 context.
painter()->setPen( Qt::NoPen );
2359 context.
painter()->setBrush( Qt::NoBrush );
2362 for (
const DeferredRenderBlock &block : deferredBlocks )
2364 context.
painter()->translate( block.origin );
2365 context.
painter()->scale( 1 / fontScale, 1 / fontScale );
2367 for (
const DeferredRenderFragment &fragment : std::as_const( block.fragments ) )
2369 if ( usePathsForText )
2371 context.
painter()->setBrush( fragment.color );
2372 context.
painter()->drawPath( fragment.path );
2376 context.
painter()->setPen( fragment.color );
2377 context.
painter()->setFont( fragment.font );
2378 context.
painter()->drawText( fragment.point, fragment.text );
2382 context.
painter()->scale( fontScale, fontScale );
2383 context.
painter()->translate( -block.origin );
2387void 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 )
2390 const QStringList textLines = document.
toPlainText();
2392 std::optional< QgsScopedRenderContextReferenceScaleOverride > referenceScaleOverride;
2403 referenceScaleOverride.reset();
2406 const double actualTextWidth = documentSize.width();
2407 double textRectWidth = 0.0;
2413 textRectWidth = actualTextWidth;
2419 textRectWidth = component.size.width();
2423 int maxLineLength = 0;
2424 for (
const QString &line : std::as_const( textLines ) )
2426 maxLineLength = std::max( maxLineLength,
static_cast<int>( line.length() ) );
2429 const double actualLabelHeight = documentSize.height();
2439 context.
painter()->translate( component.origin );
2441 context.
painter()->rotate( rotation );
2446 maskPainter->save();
2447 maskPainter->translate( component.origin );
2449 maskPainter->rotate( rotation );
2456 if ( adjustForAlignment )
2458 double hAlignmentOffset = 0;
2459 switch ( hAlignment )
2462 hAlignmentOffset = ( textRectWidth - actualTextWidth ) * 0.5;
2466 hAlignmentOffset = textRectWidth - actualTextWidth;
2480 xOffset += hAlignmentOffset;
2488 double yOffset = 0.0;
2494 if ( rotation >= -405 && rotation < -180 )
2498 else if ( rotation >= 0 && rotation < 45 )
2500 xOffset -= actualTextWidth;
2506 yOffset = -actualLabelHeight;
2511 yOffset = -actualLabelHeight;
2521 context.
painter()->translate( QPointF( xOffset, yOffset ) );
2523 double currentBlockYOffset = 0;
2524 int fragmentIndex = 0;
2532 const QFont fragmentFont = metrics.
fragmentFont( blockIndex, fragmentIndex );
2534 QFontMetricsF fragmentMetrics( fragmentFont );
2536 const double letterSpacing = fragmentFont.letterSpacing() / fontScale;
2537 const double labelHeight = fragmentMetrics.ascent() / fontScale + ( fragmentMetrics.ascent() / fontScale + letterSpacing ) * ( line.length() - 1 );
2539 Component subComponent;
2541 subComponent.blockIndex = blockIndex;
2542 subComponent.firstFragmentIndex = fragmentIndex;
2543 subComponent.size = QSizeF( blockMaximumCharacterWidth, labelHeight + fragmentMetrics.descent() / fontScale );
2544 subComponent.offset = QPointF( 0.0, currentBlockYOffset );
2545 subComponent.rotation = -component.rotation * 180 / M_PI;
2546 subComponent.rotationOffset = 0.0;
2553 QgsTextRenderer::drawMask( context, subComponent, format );
2559 currentBlockYOffset += QgsTextRenderer::drawBuffer( context, subComponent, format, metrics, mode );
2565 path.setFillRule( Qt::WindingFill );
2567 double partYOffset = 0.0;
2568 for (
const QString &part : parts )
2570 double partXOffset = ( blockMaximumCharacterWidth - ( fragmentMetrics.horizontalAdvance( part ) / fontScale - letterSpacing ) ) / 2;
2571 partYOffset += fragmentMetrics.ascent() / fontScale;
2572 path.addText( partXOffset * fontScale, partYOffset * fontScale, fragmentFont, part );
2573 partYOffset += letterSpacing;
2579 textp.begin( &textPict );
2580 textp.setPen( Qt::NoPen );
2581 QColor textColor = fragment.characterFormat().textColor().isValid() ? fragment.characterFormat().textColor() : format.
color();
2582 textColor.setAlphaF( fragment.characterFormat().textColor().isValid() ? textColor.alphaF() * format.
opacity() : format.
opacity() );
2583 textp.setBrush( textColor );
2584 textp.scale( 1 / fontScale, 1 / fontScale );
2585 textp.drawPath( path );
2596 subComponent.picture = textPict;
2597 subComponent.pictureBuffer = 0.0;
2598 subComponent.origin = QPointF( 0.0, currentBlockYOffset );
2599 const double prevY = subComponent.offset.y();
2600 subComponent.offset = QPointF( 0, -subComponent.size.height() );
2601 subComponent.useOrigin =
true;
2602 QgsTextRenderer::drawShadow( context, subComponent, format );
2603 subComponent.useOrigin =
false;
2604 subComponent.offset = QPointF( 0, prevY );
2614 context.
painter()->scale( subComponent.dpiRatio, subComponent.dpiRatio );
2618 context.
painter()->translate( 0, currentBlockYOffset );
2620 context.
painter()->drawPicture( 0, 0, textPict );
2621 currentBlockYOffset += partYOffset;
2627 maskPainter->restore();
2645 if ( pixelSize < 50 )
2646 return 200 / pixelSize;
2649 else if ( pixelSize > 200 )
2650 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, 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 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