29 #include <QTextBoundaryFinder>
34 static void _fixQPictureDPI( QPainter *p )
40 p->scale(
static_cast< double >(
qt_defaultDpiX() ) / p->device()->logicalDpiX(),
41 static_cast< double >(
qt_defaultDpiY() ) / p->device()->logicalDpiY() );
46 if ( alignment & Qt::AlignLeft )
48 else if ( alignment & Qt::AlignRight )
50 else if ( alignment & Qt::AlignHCenter )
52 else if ( alignment & Qt::AlignJustify )
61 if ( alignment & Qt::AlignTop )
63 else if ( alignment & Qt::AlignBottom )
65 else if ( alignment & Qt::AlignVCenter )
68 else if ( alignment & Qt::AlignBaseline )
76 return static_cast< int >(
c.convertToPainterUnits( size, unit, mapUnitScale ) + 0.5 );
84 tmpFormat = updateShadowPosition( tmpFormat );
91 drawPart( rect, rotation, alignment, vAlignment, document, context, tmpFormat,
Background );
96 drawPart( rect, rotation, alignment, vAlignment, document, context, tmpFormat,
Buffer );
99 drawPart( rect, rotation, alignment, vAlignment, document, context, tmpFormat,
Text );
107 tmpFormat = updateShadowPosition( tmpFormat );
119 drawPart( point, rotation, alignment, document, context, tmpFormat,
Buffer );
122 drawPart( point, rotation, alignment, document, context, tmpFormat,
Text );
151 drawPart( rect, rotation, alignment,
AlignTop, document, context, format, part );
162 component.dpiRatio = 1.0;
163 component.origin = rect.topLeft();
164 component.rotation = rotation;
165 component.size = rect.size();
166 component.hAlign = alignment;
179 double xc = rect.width() / 2.0;
180 double yc = rect.height() / 2.0;
182 double angle = -rotation;
183 double xd = xc * std::cos(
angle ) - yc * std::sin(
angle );
184 double yd = xc * std::sin(
angle ) + yc * std::cos(
angle );
186 component.center = QPointF( component.origin.x() + xd, component.origin.y() + yd );
190 component.center = rect.center();
193 QgsTextRenderer::drawBackground( context, component, format, document,
Rect );
207 drawTextInternal( part, context, format, component,
210 alignment, vAlignment );
219 drawPart( origin, rotation, alignment, document, context, format, part );
230 component.dpiRatio = 1.0;
231 component.origin = origin;
232 component.rotation = rotation;
233 component.hAlign = alignment;
242 QgsTextRenderer::drawBackground( context, component, format, document,
Point );
255 drawTextInternal( part, context, format, component,
267 return QFontMetricsF( format.
scaledFont( context, scaleFactor ), context.
painter() ? context.
painter()->device() :
nullptr );
272 QPainter *p = context.
painter();
277 if ( component.rotation >= -315 && component.rotation < -90 )
281 else if ( component.rotation >= -90 && component.rotation < -45 )
296 const QFont font = format.
scaledFont( context, scaleFactor );
299 path.setFillRule( Qt::WindingFill );
301 switch ( orientation )
308 QFont fragmentFont = font;
311 if ( component.extraWordSpacing || component.extraLetterSpacing )
312 applyExtraSpacingForLineJustification( fragmentFont, component.extraWordSpacing, component.extraLetterSpacing );
314 path.addText( xOffset, 0, fragmentFont, fragment.
text() );
325 double letterSpacing = font.letterSpacing();
326 double partYOffset = component.offset.y() * scaleFactor;
329 QFont fragmentFont = font;
332 QFontMetricsF fragmentMetrics( fragmentFont );
333 const double labelWidth = fragmentMetrics.maxWidth();
336 for (
const QString &part : parts )
338 double partXOffset = ( labelWidth - ( fragmentMetrics.horizontalAdvance( part ) - letterSpacing ) ) / 2;
339 path.addText( partXOffset, partYOffset, fragmentFont, part );
340 partYOffset += fragmentMetrics.ascent() + letterSpacing;
343 advance = partYOffset - component.offset.y() * scaleFactor;
348 QColor bufferColor = buffer.
color();
349 bufferColor.setAlphaF( buffer.
opacity() );
350 QPen pen( bufferColor );
351 pen.setWidthF( penSize * scaleFactor );
353 QColor tmpColor( bufferColor );
357 tmpColor.setAlpha( 0 );
363 buffp.begin( &buffPict );
367 std::unique_ptr< QgsPaintEffect > tmpEffect( buffer.
paintEffect()->
clone() );
369 tmpEffect->begin( context );
370 context.
painter()->setPen( pen );
371 context.
painter()->setBrush( tmpColor );
372 if ( scaleFactor != 1.0 )
373 context.
painter()->scale( 1 / scaleFactor, 1 / scaleFactor );
374 context.
painter()->drawPath( path );
375 if ( scaleFactor != 1.0 )
376 context.
painter()->scale( scaleFactor, scaleFactor );
377 tmpEffect->end( context );
383 if ( scaleFactor != 1.0 )
384 buffp.scale( 1 / scaleFactor, 1 / scaleFactor );
386 buffp.setBrush( tmpColor );
387 buffp.drawPath( path );
393 QgsTextRenderer::Component bufferComponent = component;
394 bufferComponent.origin = QPointF( 0.0, 0.0 );
395 bufferComponent.picture = buffPict;
396 bufferComponent.pictureBuffer = penSize / 2.0;
400 bufferComponent.offset.setY( bufferComponent.offset.y() - bufferComponent.size.height() );
402 drawShadow( context, bufferComponent, format );
410 p->setCompositionMode( buffer.
blendMode() );
414 p->scale( component.dpiRatio, component.dpiRatio );
415 _fixQPictureDPI( p );
416 p->drawPicture( 0, 0, buffPict );
418 return advance / scaleFactor;
435 path.setFillRule( Qt::WindingFill );
442 const QFont font = format.
scaledFont( context, scaleFactor );
446 QFont fragmentFont = font;
449 path.addText( xOffset, 0, fragmentFont, fragment.
text() );
454 QColor bufferColor( Qt::gray );
455 bufferColor.setAlphaF( mask.
opacity() );
459 brush.setColor( bufferColor );
460 pen.setColor( bufferColor );
461 pen.setWidthF( penSize * scaleFactor );
468 p->scale( component.dpiRatio, component.dpiRatio );
474 if ( scaleFactor != 1.0 )
475 context.
painter()->scale( 1 / scaleFactor, 1 / scaleFactor );
476 context.
painter()->setPen( pen );
477 context.
painter()->setBrush( brush );
478 context.
painter()->drawPath( path );
479 if ( scaleFactor != 1.0 )
480 context.
painter()->scale( scaleFactor, scaleFactor );
485 if ( scaleFactor != 1.0 )
486 p->scale( 1 / scaleFactor, 1 / scaleFactor );
488 p->setBrush( brush );
490 if ( scaleFactor != 1.0 )
491 p->scale( scaleFactor, scaleFactor );
508 return textWidth( context, format, doc );
515 const QFont baseFont = format.
scaledFont( context, scaleFactor );
522 double maxLineWidth = 0;
525 double blockWidth = 0;
530 maxLineWidth = std::max( maxLineWidth, blockWidth );
532 width = maxLineWidth;
538 double totalLineWidth = 0;
542 double blockWidth = 0;
545 QFont fragmentFont = baseFont;
547 blockWidth = std::max( QFontMetricsF( fragmentFont ).maxWidth(), blockWidth );
550 totalLineWidth += blockIndex == 0 ? blockWidth : blockWidth * format.
lineHeight();
553 width = totalLineWidth;
564 return width / scaleFactor;
582 const QFont baseFont = format.
scaledFont( context, scaleFactor );
583 const QFontMetrics fm( baseFont );
584 const double height = ( character.isNull() ? fm.height() : fm.boundingRect( character ).height() ) / scaleFactor;
586 if ( !includeEffects )
589 double maxExtension = 0;
609 return height + maxExtension;
620 const QFont baseFont = format.
scaledFont( context, scaleFactor );
627 double totalHeight = 0;
628 double lastLineLeading = 0;
631 double maxBlockHeight = 0;
632 double maxBlockLineSpacing = 0;
633 double maxBlockLeading = 0;
636 QFont fragmentFont = baseFont;
638 const QFontMetricsF fm( fragmentFont );
640 const double fragmentHeight = fm.ascent() + fm.descent();
642 maxBlockHeight = std::max( maxBlockHeight, fragmentHeight );
643 if ( fm.lineSpacing() > maxBlockLineSpacing )
645 maxBlockLineSpacing = fm.lineSpacing();
646 maxBlockLeading = fm.leading();
656 totalHeight += blockIndex == 0 ? maxBlockHeight : maxBlockHeight * format.
lineHeight();
662 totalHeight += blockIndex == 0 ? maxBlockHeight : maxBlockLineSpacing * format.
lineHeight();
663 if ( blockIndex > 0 )
664 lastLineLeading = maxBlockLeading;
671 return ( totalHeight - lastLineLeading ) / scaleFactor;
676 double maxBlockHeight = 0;
679 double blockHeight = 0;
680 int fragmentIndex = 0;
683 QFont fragmentFont = baseFont;
685 const QFontMetricsF fm( fragmentFont );
687 const double labelHeight = fm.ascent();
688 const double letterSpacing = fragmentFont.letterSpacing();
690 blockHeight += fragmentIndex = 0 ? labelHeight * fragment.
text().size() + ( fragment.
text().size() - 1 ) * letterSpacing
691 : fragment.
text().size() * ( labelHeight + letterSpacing );
694 maxBlockHeight = std::max( maxBlockHeight, blockHeight );
697 return maxBlockHeight / scaleFactor;
714 QPainter *prevP = context.
painter();
715 QPainter *p = context.
painter();
716 std::unique_ptr< QgsPaintEffect > tmpEffect;
720 tmpEffect->begin( context );
731 component.rotation = -( component.rotation * 180 / M_PI );
732 component.rotationOffset =
737 component.rotation = 0.0;
738 component.rotationOffset = background.
rotation();
746 QFontMetricsF fm( format.
scaledFont( context, scaleFactor ) );
747 double width =
textWidth( context, format, document );
748 double height =
textHeight( context, format, document, mode );
753 switch ( component.hAlign )
757 component.center = QPointF( component.origin.x() + width / 2.0,
758 component.origin.y() + height / 2.0 );
762 component.center = QPointF( component.origin.x() + component.size.width() / 2.0,
763 component.origin.y() + height / 2.0 );
767 component.center = QPointF( component.origin.x() + component.size.width() - width / 2.0,
768 component.origin.y() + height / 2.0 );
775 double originAdjust = fm.ascent() / scaleFactor / 2.0 - fm.leading() / scaleFactor / 2.0;
776 switch ( component.hAlign )
780 component.center = QPointF( component.origin.x() + width / 2.0,
781 component.origin.y() - height / 2.0 + originAdjust );
785 component.center = QPointF( component.origin.x(),
786 component.origin.y() - height / 2.0 + originAdjust );
790 component.center = QPointF( component.origin.x() - width / 2.0,
791 component.origin.y() - height / 2.0 + originAdjust );
802 component.size = QSizeF( width, height );
807 switch ( background.
type() )
820 double sizeOut = 0.0;
828 sizeOut = std::max( component.size.width(), component.size.height() );
832 sizeOut += bufferSize * 2;
840 std::unique_ptr< QgsMarkerSymbol > renderedSymbol;
844 map[QStringLiteral(
"name" )] = background.
svgFile().trimmed();
845 map[QStringLiteral(
"size" )] = QString::number( sizeOut );
847 map[QStringLiteral(
"angle" )] = QString::number( 0.0 );
855 map[QStringLiteral(
"fill" )] = background.
fillColor().name();
856 map[QStringLiteral(
"outline" )] = background.
strokeColor().name();
857 map[QStringLiteral(
"outline-width" )] = QString::number( background.
strokeWidth() );
864 QVariantMap shdwmap( map );
865 shdwmap[QStringLiteral(
"fill" )] = shadow.
color().name();
866 shdwmap[QStringLiteral(
"outline" )] = shadow.
color().name();
867 shdwmap[QStringLiteral(
"size" )] = QString::number( sizeOut );
872 svgp.begin( &svgPict );
889 svgShdwM->
renderPoint( QPointF( sizeOut / 2, -sizeOut / 2 ), svgShdwContext );
892 component.picture = svgPict;
894 component.pictureBuffer = 0.0;
896 component.size = QSizeF( sizeOut, sizeOut );
897 component.offset = QPointF( 0.0, 0.0 );
903 p->translate( component.center.x(), component.center.y() );
904 p->rotate( component.rotation );
907 p->translate( QPointF( xoff, yoff ) );
908 p->rotate( component.rotationOffset );
909 p->translate( -sizeOut / 2, sizeOut / 2 );
911 drawShadow( context, component, format );
913 renderedSymbol.reset( );
921 renderedSymbol->setSize( sizeOut );
925 renderedSymbol->setOpacity( renderedSymbol->opacity() * background.
opacity() );
933 p->setCompositionMode( background.
blendMode() );
935 p->translate( component.center.x(), component.center.y() );
936 p->rotate( component.rotation );
939 p->translate( QPointF( xoff, yoff ) );
940 p->rotate( component.rotationOffset );
944 renderedSymbol->renderPoint( QPointF( 0, 0 ), &f, context );
945 renderedSymbol->stopRender( context );
946 p->setCompositionMode( QPainter::CompositionMode_SourceOver );
956 double w = component.size.width();
957 double h = component.size.height();
978 h = std::sqrt( std::pow( w, 2 ) + std::pow( h, 2 ) );
984 h = h * M_SQRT1_2 * 2;
985 w = w * M_SQRT1_2 * 2;
993 w += bufferWidth * 2;
994 h += bufferHeight * 2;
998 QRectF rect( -w / 2.0, - h / 2.0, w, h );
1000 if ( rect.isNull() )
1006 p->translate( QPointF( component.center.x(), component.center.y() ) );
1007 p->rotate( component.rotation );
1010 p->translate( QPointF( xoff, yoff ) );
1011 p->rotate( component.rotationOffset );
1017 QTransform t = QTransform::fromScale( 10, 10 );
1019 QTransform ti = t.inverted();
1026 path.addRoundedRect( rect, background.
radii().width(), background.
radii().height(), Qt::RelativeSize );
1032 path.addRoundedRect( rect, xRadius, yRadius );
1038 path.addEllipse( rect );
1040 QPolygonF tempPolygon = path.toFillPolygon( t );
1041 QPolygonF polygon = ti.map( tempPolygon );
1043 QPainter *oldp = context.
painter();
1046 shapep.begin( &shapePict );
1049 std::unique_ptr< QgsFillSymbol > renderedSymbol;
1051 renderedSymbol->setOpacity( renderedSymbol->opacity() * background.
opacity() );
1055 renderedSymbol->renderPolygon( polygon,
nullptr, &f, context );
1056 renderedSymbol->stopRender( context );
1063 component.picture = shapePict;
1066 component.size = rect.size();
1067 component.offset = QPointF( rect.width() / 2, -rect.height() / 2 );
1068 drawShadow( context, component, format );
1073 p->setCompositionMode( background.
blendMode() );
1077 p->scale( component.dpiRatio, component.dpiRatio );
1078 _fixQPictureDPI( p );
1079 p->drawPicture( 0, 0, shapePict );
1080 p->setCompositionMode( QPainter::CompositionMode_SourceOver );
1087 tmpEffect->end( context );
1100 QPainter *p = context.
painter();
1101 double componentWidth = component.size.width(), componentHeight = component.size.height();
1102 double xOffset = component.offset.x(), yOffset = component.offset.y();
1103 double pictbuffer = component.pictureBuffer;
1108 radius /= ( mapUnits ? context.
scaleFactor() / component.dpiRatio : 1 );
1109 radius =
static_cast< int >( radius + 0.5 );
1113 double blurBufferClippingScale = 3.75;
1114 int blurbuffer = ( radius > 17 ? 16 : radius ) * blurBufferClippingScale;
1116 QImage blurImg( componentWidth + ( pictbuffer * 2.0 ) + ( blurbuffer * 2.0 ),
1117 componentHeight + ( pictbuffer * 2.0 ) + ( blurbuffer * 2.0 ),
1118 QImage::Format_ARGB32_Premultiplied );
1122 int minBlurImgSize = 1;
1126 int maxBlurImgSize = 40000;
1127 if ( blurImg.isNull()
1128 || ( blurImg.width() < minBlurImgSize || blurImg.height() < minBlurImgSize )
1129 || ( blurImg.width() > maxBlurImgSize || blurImg.height() > maxBlurImgSize ) )
1132 blurImg.fill( QColor( Qt::transparent ).rgba() );
1134 if ( !pictp.begin( &blurImg ) )
1136 pictp.setRenderHints( QPainter::Antialiasing | QPainter::SmoothPixmapTransform );
1137 QPointF imgOffset( blurbuffer + pictbuffer + xOffset,
1138 blurbuffer + pictbuffer + componentHeight + yOffset );
1140 pictp.drawPicture( imgOffset,
1141 component.picture );
1144 pictp.setCompositionMode( QPainter::CompositionMode_SourceIn );
1145 pictp.fillRect( blurImg.rect(), shadow.
color() );
1149 if ( shadow.
blurRadius() > 0.0 && radius > 0 )
1157 picti.begin( &blurImg );
1158 picti.setBrush( Qt::Dense7Pattern );
1159 QPen imgPen( QColor( 0, 0, 255, 255 ) );
1160 imgPen.setWidth( 1 );
1161 picti.setPen( imgPen );
1162 picti.setOpacity( 0.1 );
1163 picti.drawRect( 0, 0, blurImg.width(), blurImg.height() );
1168 double angleRad = shadow.
offsetAngle() * M_PI / 180;
1176 angleRad -= ( component.rotation * M_PI / 180 + component.rotationOffset * M_PI / 180 );
1179 QPointF transPt( -offsetDist * std::cos( angleRad + M_PI_2 ),
1180 -offsetDist * std::sin( angleRad + M_PI_2 ) );
1183 p->setRenderHint( QPainter::SmoothPixmapTransform );
1187 p->setCompositionMode( shadow.
blendMode() );
1189 p->setOpacity( shadow.
opacity() );
1191 double scale = shadow.
scale() / 100.0;
1193 p->scale( scale, scale );
1194 if ( component.useOrigin )
1196 p->translate( component.origin.x(), component.origin.y() );
1198 p->translate( transPt );
1199 p->translate( -imgOffset.x(),
1201 p->drawImage( 0, 0, blurImg );
1208 p->setBrush( Qt::NoBrush );
1209 QPen imgPen( QColor( 255, 0, 0, 10 ) );
1210 imgPen.setWidth( 2 );
1211 imgPen.setStyle( Qt::DashLine );
1212 p->setPen( imgPen );
1213 p->scale( scale, scale );
1214 if ( component.useOrigin() )
1216 p->translate( component.origin().x(), component.origin().y() );
1218 p->translate( transPt );
1219 p->translate( -imgOffset.x(),
1221 p->drawRect( 0, 0, blurImg.width(), blurImg.height() );
1226 p->setBrush( Qt::NoBrush );
1227 QPen componentRectPen( QColor( 0, 255, 0, 70 ) );
1228 componentRectPen.setWidth( 1 );
1229 if ( component.useOrigin() )
1231 p->translate( component.origin().x(), component.origin().y() );
1233 p->setPen( componentRectPen );
1234 p->drawRect( QRect( -xOffset, -componentHeight - yOffset, componentWidth, componentHeight ) );
1240 void QgsTextRenderer::drawTextInternal( TextPart drawType,
1243 const Component &component,
1245 const QFontMetricsF *fontMetrics,
1246 HAlignment alignment, VAlignment vAlignment, DrawMode mode )
1253 double fontScale = 1.0;
1254 std::unique_ptr< QFontMetricsF > tmpMetrics;
1258 const QFont f = format.
scaledFont( context, fontScale );
1259 tmpMetrics = std::make_unique< QFontMetricsF >( f );
1263 double rotation = 0;
1265 switch ( orientation )
1269 drawTextInternalHorizontal( context, format, drawType, mode, component, document, fontScale,
fontMetrics, alignment, vAlignment, rotation );
1276 drawTextInternalVertical( context, format, drawType, mode, component, document, fontScale,
fontMetrics, alignment, vAlignment, rotation );
1284 rotation = -component.rotation * 180 / M_PI;
1291 if ( rotation >= -315 && rotation < -90 )
1296 else if ( rotation >= -90 && rotation < -45 )
1312 void QgsTextRenderer::calculateExtraSpacingForLineJustification(
const double spaceToDistribute,
const QgsTextBlock &block,
double &extraWordSpace,
double &extraLetterSpace )
1315 QTextBoundaryFinder
finder( QTextBoundaryFinder::Word, blockText );
1317 int wordBoundaries = 0;
1318 while (
finder.toNextBoundary() != -1 )
1320 if (
finder.boundaryReasons() & QTextBoundaryFinder::StartOfItem )
1324 if ( wordBoundaries > 0 )
1327 extraWordSpace = spaceToDistribute / wordBoundaries;
1332 QTextBoundaryFinder
finder( QTextBoundaryFinder::Grapheme, blockText );
1335 int graphemeBoundaries = 0;
1336 while (
finder.toNextBoundary() != -1 )
1338 if (
finder.boundaryReasons() & QTextBoundaryFinder::StartOfItem )
1339 graphemeBoundaries++;
1342 if ( graphemeBoundaries > 0 )
1344 extraLetterSpace = spaceToDistribute / graphemeBoundaries;
1349 void QgsTextRenderer::applyExtraSpacingForLineJustification( QFont &font,
double extraWordSpace,
double extraLetterSpace )
1351 const double prevWordSpace = font.wordSpacing();
1352 font.setWordSpacing( prevWordSpace + extraWordSpace );
1353 const double prevLetterSpace = font.letterSpacing();
1354 font.setLetterSpacing( QFont::AbsoluteSpacing, prevLetterSpace + extraLetterSpace );
1357 void QgsTextRenderer::drawTextInternalHorizontal(
QgsRenderContext &context,
const QgsTextFormat &format, TextPart drawType, DrawMode mode,
const Component &component,
const QgsTextDocument &document,
double fontScale,
const QFontMetricsF *fontMetrics, HAlignment hAlignment,
1358 VAlignment vAlignment,
double rotation )
1361 const QStringList textLines = document.
toPlainText();
1363 double labelWidest = 0.0;
1368 for (
const QString &line : textLines )
1370 double labelWidth =
fontMetrics->horizontalAdvance( line ) / fontScale;
1371 if ( labelWidth > labelWidest )
1373 labelWidest = labelWidth;
1379 labelWidest = component.size.width();
1387 double ascentOffset = 0.25 *
fontMetrics->ascent() / fontScale;
1391 bool adjustForAlignment = hAlignment !=
AlignLeft && ( mode !=
Label || textLines.size() > 1 );
1396 const double overallHeight =
textHeight( context, format, textLines,
Rect );
1397 switch ( vAlignment )
1403 ascentOffset = -( component.size.height() - overallHeight ) * 0.5 + ascentOffset;
1407 ascentOffset = -( component.size.height() - overallHeight ) + ascentOffset;
1412 for (
const QString &line : std::as_const( textLines ) )
1416 const bool isFinalLine = i == document.
size() - 1;
1420 context.
painter()->translate( component.origin );
1422 context.
painter()->rotate( rotation );
1427 maskPainter->save();
1428 maskPainter->translate( component.origin );
1430 maskPainter->rotate( rotation );
1434 double xMultiLineOffset = 0.0;
1435 double labelWidth =
fontMetrics->horizontalAdvance( line ) / fontScale;
1436 double extraWordSpace = 0;
1437 double extraLetterSpace = 0;
1438 if ( adjustForAlignment )
1440 double labelWidthDiff = 0;
1441 switch ( hAlignment )
1444 labelWidthDiff = ( labelWidest - labelWidth ) * 0.5;
1448 labelWidthDiff = labelWidest - labelWidth;
1452 if ( !isFinalLine && labelWidest > labelWidth )
1454 calculateExtraSpacingForLineJustification( labelWidest - labelWidth, block, extraWordSpace, extraLetterSpace );
1466 xMultiLineOffset = labelWidthDiff;
1471 switch ( hAlignment )
1474 xMultiLineOffset = labelWidthDiff - labelWidest;
1478 xMultiLineOffset = labelWidthDiff - labelWidest / 2.0;
1490 double yMultiLineOffset = ascentOffset;
1497 yMultiLineOffset = - ascentOffset - ( textLines.size() - 1 - i ) * labelHeight * format.
lineHeight();
1502 yMultiLineOffset = - ascentOffset + labelHeight - 1 + format.
lineHeight() *
fontMetrics->lineSpacing() * i / fontScale;
1507 yMultiLineOffset = 0 - ( textLines.size() - 1 - i ) *
fontMetrics->lineSpacing() * format.
lineHeight() / fontScale;
1512 context.
painter()->translate( QPointF( xMultiLineOffset, yMultiLineOffset ) );
1514 maskPainter->translate( QPointF( xMultiLineOffset, yMultiLineOffset ) );
1516 Component subComponent;
1517 subComponent.block = block;
1518 subComponent.
size = QSizeF( labelWidth, labelHeight );
1519 subComponent.offset = QPointF( 0.0, -ascentOffset );
1520 subComponent.rotation = -component.rotation * 180 / M_PI;
1521 subComponent.rotationOffset = 0.0;
1522 subComponent.extraWordSpacing = extraWordSpace * fontScale;
1523 subComponent.extraLetterSpacing = extraLetterSpace * fontScale;
1528 QgsTextRenderer::drawMask( context, subComponent, format );
1533 QgsTextRenderer::drawBuffer( context, subComponent, format );
1540 textp.begin( &textPict );
1541 textp.setPen( Qt::NoPen );
1542 const QFont font = format.
scaledFont( context, fontScale );
1543 textp.scale( 1 / fontScale, 1 / fontScale );
1550 path.setFillRule( Qt::WindingFill );
1552 QFont fragmentFont = font;
1555 if ( extraWordSpace || extraLetterSpace )
1556 applyExtraSpacingForLineJustification( fragmentFont, extraWordSpace * fontScale, extraLetterSpace * fontScale );
1558 path.addText( xOffset, 0, fragmentFont, fragment.
text() );
1562 textp.setBrush( textColor );
1563 textp.drawPath( path );
1571 subComponent.picture = textPict;
1572 subComponent.pictureBuffer = 0.0;
1573 subComponent.origin = QPointF( 0.0, 0.0 );
1575 QgsTextRenderer::drawShadow( context, subComponent, format );
1585 context.
painter()->scale( subComponent.dpiRatio, subComponent.dpiRatio );
1592 _fixQPictureDPI( context.
painter() );
1593 context.
painter()->drawPicture( 0, 0, textPict );
1602 QFont fragmentFont = font;
1605 if ( extraWordSpace || extraLetterSpace )
1606 applyExtraSpacingForLineJustification( fragmentFont, extraWordSpace * fontScale, extraLetterSpace * fontScale );
1611 context.
painter()->setPen( textColor );
1612 context.
painter()->setFont( fragmentFont );
1613 context.
painter()->setRenderHint( QPainter::TextAntialiasing );
1615 context.
painter()->scale( 1 / fontScale, 1 / fontScale );
1616 context.
painter()->drawText( xOffset, 0, fragment.
text() );
1617 context.
painter()->scale( fontScale, fontScale );
1625 maskPainter->restore();
1630 void QgsTextRenderer::drawTextInternalVertical(
QgsRenderContext &context,
const QgsTextFormat &format,
QgsTextRenderer::TextPart drawType,
QgsTextRenderer::DrawMode mode,
const QgsTextRenderer::Component &component,
const QgsTextDocument &document,
double fontScale,
const QFontMetricsF *fontMetrics,
QgsTextRenderer::HAlignment hAlignment,
QgsTextRenderer::VAlignment,
double rotation )
1633 const QStringList textLines = document.
toPlainText();
1635 const QFont font = format.
scaledFont( context, fontScale );
1636 double letterSpacing = font.letterSpacing() / fontScale;
1638 double labelWidth =
fontMetrics->maxWidth() / fontScale;
1639 double actualLabelWidest = labelWidth + ( textLines.size() - 1 ) * labelWidth * format.
lineHeight();
1640 double labelWidest = 0.0;
1645 labelWidest = actualLabelWidest;
1649 labelWidest = component.size.width();
1653 int maxLineLength = 0;
1654 for (
const QString &line : std::as_const( textLines ) )
1656 maxLineLength = std::max( maxLineLength,
static_cast<int>( line.length() ) );
1658 double actualLabelHeight =
fontMetrics->ascent() / fontScale + (
fontMetrics->ascent() / fontScale + letterSpacing ) * ( maxLineLength - 1 );
1659 double ascentOffset =
fontMetrics->ascent() / fontScale;
1663 bool adjustForAlignment = hAlignment !=
AlignLeft && ( mode !=
Label || textLines.size() > 1 );
1670 context.
painter()->translate( component.origin );
1672 context.
painter()->rotate( rotation );
1677 maskPainter->save();
1678 maskPainter->translate( component.origin );
1680 maskPainter->rotate( rotation );
1684 double xOffset = actualLabelWidest - labelWidth - ( i * labelWidth * format.
lineHeight() );
1685 if ( adjustForAlignment )
1687 double labelWidthDiff = 0;
1688 switch ( hAlignment )
1691 labelWidthDiff = ( labelWidest - actualLabelWidest ) * 0.5;
1695 labelWidthDiff = labelWidest - actualLabelWidest;
1707 xOffset += labelWidthDiff;
1715 double yOffset = 0.0;
1721 if ( rotation >= -405 && rotation < -180 )
1723 yOffset = ascentOffset;
1725 else if ( rotation >= 0 && rotation < 45 )
1727 xOffset -= actualLabelWidest;
1728 yOffset = -actualLabelHeight + ascentOffset +
fontMetrics->descent() / fontScale;
1733 yOffset = -actualLabelHeight + ascentOffset;
1738 yOffset = -actualLabelHeight + ascentOffset;
1742 yOffset = ascentOffset;
1746 context.
painter()->translate( QPointF( xOffset, yOffset ) );
1748 double fragmentYOffset = 0;
1754 QFont fragmentFont( font );
1757 QFontMetricsF fragmentMetrics( fragmentFont );
1759 double labelHeight = fragmentMetrics.ascent() / fontScale + ( fragmentMetrics.ascent() / fontScale + letterSpacing ) * ( line.length() - 1 );
1761 Component subComponent;
1763 subComponent.size = QSizeF( labelWidth, labelHeight );
1764 subComponent.offset = QPointF( 0.0, fragmentYOffset );
1765 subComponent.rotation = -component.rotation * 180 / M_PI;
1766 subComponent.rotationOffset = 0.0;
1773 QgsTextRenderer::drawMask( context, subComponent, format );
1779 fragmentYOffset += QgsTextRenderer::drawBuffer( context, subComponent, format );
1785 path.setFillRule( Qt::WindingFill );
1787 double partYOffset = 0.0;
1788 for (
const auto &part : parts )
1790 double partXOffset = ( labelWidth - ( fragmentMetrics.horizontalAdvance( part ) / fontScale - letterSpacing ) ) / 2;
1791 path.addText( partXOffset * fontScale, partYOffset * fontScale, fragmentFont, part );
1792 partYOffset += fragmentMetrics.ascent() / fontScale + letterSpacing;
1798 textp.begin( &textPict );
1799 textp.setPen( Qt::NoPen );
1802 textp.setBrush( textColor );
1803 textp.scale( 1 / fontScale, 1 / fontScale );
1804 textp.drawPath( path );
1814 subComponent.picture = textPict;
1815 subComponent.pictureBuffer = 0.0;
1816 subComponent.origin = QPointF( 0.0, fragmentYOffset );
1817 const double prevY = subComponent.offset.y();
1818 subComponent.offset = QPointF( 0, -labelHeight );
1819 subComponent.useOrigin =
true;
1820 QgsTextRenderer::drawShadow( context, subComponent, format );
1821 subComponent.useOrigin =
false;
1822 subComponent.offset = QPointF( 0, prevY );
1832 context.
painter()->scale( subComponent.dpiRatio, subComponent.dpiRatio );
1839 _fixQPictureDPI( context.
painter() );
1840 context.
painter()->drawPicture( 0, fragmentYOffset, textPict );
1841 fragmentYOffset += partYOffset;
1847 context.
painter()->setFont( fragmentFont );
1848 context.
painter()->setPen( textColor );
1849 context.
painter()->setRenderHint( QPainter::TextAntialiasing );
1851 double partYOffset = 0.0;
1852 for (
const QString &part : parts )
1854 double partXOffset = ( labelWidth - ( fragmentMetrics.horizontalAdvance( part ) / fontScale - letterSpacing ) ) / 2;
1855 context.
painter()->scale( 1 / fontScale, 1 / fontScale );
1856 context.
painter()->drawText( partXOffset * fontScale, ( fragmentYOffset + partYOffset ) * fontScale, part );
1857 context.
painter()->scale( fontScale, fontScale );
1858 partYOffset += fragmentMetrics.ascent() / fontScale + letterSpacing;
1860 fragmentYOffset += partYOffset;
1867 maskPainter->restore();
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.
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.
virtual QgsPaintEffect * clone() const =0
Duplicates an effect by creating a deep copy of the effect.
bool enabled() const
Returns whether the effect is enabled.
A class to manage painter saving and restoring required for drawing on a different painter (mask pain...
static QStringList splitToGraphemes(const QString &text)
Splits a text string to a list of graphemes, which are the smallest allowable character divisions in ...
bool hasActiveProperties() const override
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.
QPainter * maskPainter(int id=0)
Returns a mask QPainter for the render operation.
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.
@ TextFormatAlwaysText
Always render text as text objects.
@ TextFormatAlwaysOutlines
Always render text using path objects (AKA outlines/curves).
QPainter * painter()
Returns the destination QPainter for the render operation.
QgsExpressionContext & expressionContext()
Gets the expression context.
const QgsMapToPixel & mapToPixel() const
Returns the context's map to pixel transform, which transforms between map coordinates and device coo...
void setPainterFlagsUsingContext(QPainter *painter=nullptr) const
Sets relevant flags on a destination painter, using the flags and settings currently defined for the ...
bool isGuiPreview() const
Returns the Gui preview mode.
void setMapToPixel(const QgsMapToPixel &mtp)
Sets the context's map to pixel transform, which transforms between map coordinates and device coordi...
@ ApplyScalingWorkaroundForTextRendering
Whether a scaling workaround designed to stablise the rendering of small font sizes (or for painters ...
int currentMaskId() const
Returns the current mask id, which can be used with maskPainter()
Flags flags() const
Returns combination of flags used for rendering.
void setPainter(QPainter *p)
Sets the destination QPainter for the render operation.
double convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale()) const
Converts a size from the specified units to painter units (pixels).
TextRenderFormat textRenderFormat() const
Returns the text render format, which dictates how text is rendered (e.g.
Scoped object for saving and restoring a QPainter object's state.
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.
QgsUnitTypes::RenderUnit strokeWidthUnit() const
Returns the units used for the shape's stroke width.
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.
QgsUnitTypes::RenderUnit offsetUnit() const
Returns the units used for the shape's offset.
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...
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.
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.
QgsUnitTypes::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.
QgsUnitTypes::RenderUnit radiiUnit() const
Returns the units used for the shape's radii.
QgsMarkerSymbol * markerSymbol() const
Returns the marker symbol to be rendered in the background.
const QgsPaintEffect * paintEffect() const
Returns the current paint effect for the background shape.
QgsMapUnitScale offsetMapUnitScale() const
Returns the map unit scale object for the shape offset.
QPointF offset() const
Returns the offset used for drawing the background shape.
Represents a block of text consisting of one or more QgsTextFragment objects.
int size() const
Returns the number of fragments in the block.
QString toPlainText() const
Converts the block to plain text.
Container for settings relating to a text buffer.
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.
QgsUnitTypes::RenderUnit sizeUnit() const
Returns the units for the buffer size.
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.
QColor textColor() const
Returns the character's text color, or an invalid color if no color override is set and the default f...
void updateFontForFormat(QFont &font, double scaleFactor=1.0) const
Updates the specified font in place, applying character formatting options which are applicable on a ...
Represents a document consisting of one or more QgsTextBlock objects.
void applyCapitalization(QgsStringUtils::Capitalization capitalization)
Applies a capitalization style to the document's text.
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.
static QgsTextDocument fromHtml(const QStringList &lines)
Constructor for QgsTextDocument consisting of a set of HTML formatted lines.
static QgsTextDocument fromPlainText(const QStringList &lines)
Constructor for QgsTextDocument consisting of a set of plain text lines.
Container for all settings relating to text rendering.
double lineHeight() const
Returns the line height for text.
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.
QPainter::CompositionMode blendMode() const
Returns the blending mode used for drawing the text.
TextOrientation orientation() const
Returns the orientation of the text.
QgsTextMaskSettings & mask()
Returns a reference to the masking settings.
QgsTextBackgroundSettings & background()
Returns a reference to the text background settings.
TextOrientation
Text orientation.
@ HorizontalOrientation
Vertically oriented text.
@ RotationBasedOrientation
Horizontally or vertically oriented text based on rotation (only available for map labeling)
@ VerticalOrientation
Horizontally oriented text.
bool allowHtmlFormatting() const
Returns true if text should be treated as a HTML document and HTML tags should be used for formatting...
double opacity() const
Returns the text's opacity.
QFont scaledFont(const QgsRenderContext &context, double scaleFactor=1.0) const
Returns a font with the size scaled to match the format's size settings (including units and map unit...
QgsTextShadowSettings & shadow()
Returns a reference to the text drop shadow settings.
QgsStringUtils::Capitalization capitalization() const
Returns the text capitalization style.
QColor color() const
Returns the color that text will be rendered in.
QgsTextBufferSettings & buffer()
Returns a reference to the text buffer settings.
Stores a fragment of text along with formatting overrides to be used when rendering the fragment.
QString text() const
Returns the text content of the fragment.
double horizontalAdvance(const QFont &font, bool fontHasBeenUpdatedForFragment=false, double scaleFactor=1.0) const
Returns the horizontal advance associated with this fragment, when rendered using the specified base ...
const QgsTextCharacterFormat & characterFormat() const
Returns the character formatting for the fragment.
Container for settings relating to a selective masking around a text.
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.
QgsUnitTypes::RenderUnit sizeUnit() const
Returns the units for the buffer size.
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.
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 Q_DECL_DEPRECATED void drawPart(const QRectF &rect, double rotation, HAlignment alignment, const QStringList &textLines, QgsRenderContext &context, const QgsTextFormat &format, TextPart part, bool drawAsOutlines=true)
Draws a single component of rendered text using the specified settings.
VAlignment
Vertical alignment.
@ AlignBottom
Align to bottom.
@ AlignVCenter
Center align.
TextPart
Components of text.
@ Buffer
Buffer component.
@ Background
Background shape.
HAlignment
Horizontal alignment.
@ AlignCenter
Center align.
@ AlignJustify
Justify align.
static double textHeight(const QgsRenderContext &context, const QgsTextFormat &format, const QStringList &textLines, DrawMode mode=Point, QFontMetricsF *fontMetrics=nullptr)
Returns the height of a text based on a given format.
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 HAlignment convertQtHAlignment(Qt::Alignment alignment)
Converts a Qt horizontal alignment flag to a QgsTextRenderer::HAlignment value.
static int sizeToPixel(double size, const QgsRenderContext &c, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &mapUnitScale=QgsMapUnitScale())
Calculates pixel size (considering output size should be in pixel or map units, scale factors and opt...
static VAlignment convertQtVAlignment(Qt::Alignment alignment)
Converts a Qt vertical alignment flag to a QgsTextRenderer::VAlignment value.
static void drawText(const QRectF &rect, double rotation, HAlignment alignment, const QStringList &textLines, QgsRenderContext &context, const QgsTextFormat &format, bool drawAsOutlines=true, VAlignment vAlignment=AlignTop)
Draws text within a rectangle using the specified settings.
DrawMode
Draw mode to calculate width and height.
@ Point
Text at point of origin draw mode.
@ Rect
Text within rectangle draw mode.
@ Label
Label-specific draw mode.
static constexpr double FONT_WORKAROUND_SCALE
Scale factor for upscaling font sizes and downscaling destination painter devices.
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).
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.
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.
QgsUnitTypes::RenderUnit blurRadiusUnit() const
Returns the units used for the shadow's blur radius.
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.
QgsUnitTypes::RenderUnit offsetUnit() const
Returns the units used for the shadow's offset.
double blurRadius() const
Returns the blur radius for the shadow.
static Q_INVOKABLE QString encodeUnit(QgsUnitTypes::DistanceUnit unit)
Encodes a distance unit to a string.
RenderUnit
Rendering size units.
@ RenderUnknownUnit
Mixed or unknown units.
@ RenderPercentage
Percentage of another measurement (e.g., canvas size, feature size)
@ RenderMapUnits
Map units.
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)
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
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
QList< QgsSymbolLayer * > QgsSymbolLayerList
Q_GUI_EXPORT int qt_defaultDpiX()
Q_GUI_EXPORT int qt_defaultDpiY()