27 #include <QTextBoundaryFinder>
32 static void _fixQPictureDPI( QPainter *p )
38 p->scale(
static_cast< double >(
qt_defaultDpiX() ) / p->device()->logicalDpiX(),
39 static_cast< double >(
qt_defaultDpiY() ) / p->device()->logicalDpiY() );
44 if ( alignment & Qt::AlignLeft )
46 else if ( alignment & Qt::AlignRight )
48 else if ( alignment & Qt::AlignHCenter )
50 else if ( alignment & Qt::AlignJustify )
59 if ( alignment & Qt::AlignTop )
61 else if ( alignment & Qt::AlignBottom )
63 else if ( alignment & Qt::AlignVCenter )
66 else if ( alignment & Qt::AlignBaseline )
74 return static_cast< int >(
c.convertToPainterUnits( size, unit, mapUnitScale ) + 0.5 );
82 tmpFormat = updateShadowPosition( tmpFormat );
89 drawPart( rect, rotation, alignment, vAlignment, document, context, tmpFormat,
Background );
94 drawPart( rect, rotation, alignment, vAlignment, document, context, tmpFormat,
Buffer );
97 drawPart( rect, rotation, alignment, vAlignment, document, context, tmpFormat,
Text );
105 tmpFormat = updateShadowPosition( tmpFormat );
117 drawPart( point, rotation, alignment, document, context, tmpFormat,
Buffer );
120 drawPart( point, rotation, alignment, document, context, tmpFormat,
Text );
149 drawPart( rect, rotation, alignment,
AlignTop, document, context, format, part );
160 component.dpiRatio = 1.0;
161 component.origin = rect.topLeft();
162 component.rotation = rotation;
163 component.size = rect.size();
164 component.hAlign = alignment;
177 double xc = rect.width() / 2.0;
178 double yc = rect.height() / 2.0;
180 double angle = -rotation;
181 double xd = xc * std::cos(
angle ) - yc * std::sin(
angle );
182 double yd = xc * std::sin(
angle ) + yc * std::cos(
angle );
184 component.center = QPointF( component.origin.x() + xd, component.origin.y() + yd );
188 component.center = rect.center();
191 QgsTextRenderer::drawBackground( context, component, format, document,
Rect );
205 drawTextInternal( part, context, format, component,
208 alignment, vAlignment );
217 drawPart( origin, rotation, alignment, document, context, format, part );
228 component.dpiRatio = 1.0;
229 component.origin = origin;
230 component.rotation = rotation;
231 component.hAlign = alignment;
240 QgsTextRenderer::drawBackground( context, component, format, document,
Point );
253 drawTextInternal( part, context, format, component,
265 return QFontMetricsF( format.
scaledFont( context, scaleFactor ), context.
painter() ? context.
painter()->device() :
nullptr );
270 QPainter *p = context.
painter();
275 if ( component.rotation >= -315 && component.rotation < -90 )
279 else if ( component.rotation >= -90 && component.rotation < -45 )
294 const QFont font = format.
scaledFont( context, scaleFactor );
297 path.setFillRule( Qt::WindingFill );
299 switch ( orientation )
306 QFont fragmentFont = font;
309 if ( component.extraWordSpacing || component.extraLetterSpacing )
310 applyExtraSpacingForLineJustification( fragmentFont, component.extraWordSpacing, component.extraLetterSpacing );
312 path.addText( xOffset, 0, fragmentFont, fragment.
text() );
323 double letterSpacing = font.letterSpacing();
324 double partYOffset = component.offset.y() * scaleFactor;
327 QFont fragmentFont = font;
330 QFontMetricsF fragmentMetrics( fragmentFont );
331 const double labelWidth = fragmentMetrics.maxWidth();
334 for (
const QString &part : parts )
336 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
337 double partXOffset = ( labelWidth - ( fragmentMetrics.width( part ) - letterSpacing ) ) / 2;
339 double partXOffset = ( labelWidth - ( fragmentMetrics.horizontalAdvance( part ) - letterSpacing ) ) / 2;
341 path.addText( partXOffset, partYOffset, fragmentFont, part );
342 partYOffset += fragmentMetrics.ascent() + letterSpacing;
345 advance = partYOffset - component.offset.y() * scaleFactor;
350 QColor bufferColor = buffer.
color();
351 bufferColor.setAlphaF( buffer.
opacity() );
352 QPen pen( bufferColor );
353 pen.setWidthF( penSize * scaleFactor );
355 QColor tmpColor( bufferColor );
359 tmpColor.setAlpha( 0 );
365 buffp.begin( &buffPict );
369 std::unique_ptr< QgsPaintEffect > tmpEffect( buffer.
paintEffect()->
clone() );
371 tmpEffect->begin( context );
372 context.
painter()->setPen( pen );
373 context.
painter()->setBrush( tmpColor );
374 if ( scaleFactor != 1.0 )
375 context.
painter()->scale( 1 / scaleFactor, 1 / scaleFactor );
376 context.
painter()->drawPath( path );
377 if ( scaleFactor != 1.0 )
378 context.
painter()->scale( scaleFactor, scaleFactor );
379 tmpEffect->end( context );
385 if ( scaleFactor != 1.0 )
386 buffp.scale( 1 / scaleFactor, 1 / scaleFactor );
388 buffp.setBrush( tmpColor );
389 buffp.drawPath( path );
395 QgsTextRenderer::Component bufferComponent = component;
396 bufferComponent.origin = QPointF( 0.0, 0.0 );
397 bufferComponent.picture = buffPict;
398 bufferComponent.pictureBuffer = penSize / 2.0;
402 bufferComponent.offset.setY( bufferComponent.offset.y() - bufferComponent.size.height() );
404 drawShadow( context, bufferComponent, format );
412 p->setCompositionMode( buffer.
blendMode() );
416 p->scale( component.dpiRatio, component.dpiRatio );
417 _fixQPictureDPI( p );
418 p->drawPicture( 0, 0, buffPict );
420 return advance / scaleFactor;
437 path.setFillRule( Qt::WindingFill );
444 const QFont font = format.
scaledFont( context, scaleFactor );
448 QFont fragmentFont = font;
451 path.addText( xOffset, 0, fragmentFont, fragment.
text() );
456 QColor bufferColor( Qt::gray );
457 bufferColor.setAlphaF( mask.
opacity() );
461 brush.setColor( bufferColor );
462 pen.setColor( bufferColor );
463 pen.setWidthF( penSize * scaleFactor );
470 p->scale( component.dpiRatio, component.dpiRatio );
476 if ( scaleFactor != 1.0 )
477 context.
painter()->scale( 1 / scaleFactor, 1 / scaleFactor );
478 context.
painter()->setPen( pen );
479 context.
painter()->setBrush( brush );
480 context.
painter()->drawPath( path );
481 if ( scaleFactor != 1.0 )
482 context.
painter()->scale( scaleFactor, scaleFactor );
487 if ( scaleFactor != 1.0 )
488 p->scale( 1 / scaleFactor, 1 / scaleFactor );
490 p->setBrush( brush );
492 if ( scaleFactor != 1.0 )
493 p->scale( scaleFactor, scaleFactor );
510 return textWidth( context, format, doc );
517 const QFont baseFont = format.
scaledFont( context, scaleFactor );
524 double maxLineWidth = 0;
527 double blockWidth = 0;
532 maxLineWidth = std::max( maxLineWidth, blockWidth );
534 width = maxLineWidth;
540 double totalLineWidth = 0;
544 double blockWidth = 0;
547 QFont fragmentFont = baseFont;
549 blockWidth = std::max( QFontMetricsF( fragmentFont ).maxWidth(), blockWidth );
552 totalLineWidth += blockIndex == 0 ? blockWidth : blockWidth * format.
lineHeight();
555 width = totalLineWidth;
566 return width / scaleFactor;
584 const QFont baseFont = format.
scaledFont( context, scaleFactor );
585 const QFontMetrics fm( baseFont );
586 const double height = ( character.isNull() ? fm.height() : fm.boundingRect( character ).height() ) / scaleFactor;
588 if ( !includeEffects )
591 double maxExtension = 0;
611 return height + maxExtension;
622 const QFont baseFont = format.
scaledFont( context, scaleFactor );
629 double totalHeight = 0;
630 double lastLineLeading = 0;
633 double maxBlockHeight = 0;
634 double maxBlockLineSpacing = 0;
635 double maxBlockLeading = 0;
638 QFont fragmentFont = baseFont;
640 const QFontMetricsF fm( fragmentFont );
642 const double fragmentHeight = fm.ascent() + fm.descent();
644 maxBlockHeight = std::max( maxBlockHeight, fragmentHeight );
645 if ( fm.lineSpacing() > maxBlockLineSpacing )
647 maxBlockLineSpacing = fm.lineSpacing();
648 maxBlockLeading = fm.leading();
658 totalHeight += blockIndex == 0 ? maxBlockHeight : maxBlockHeight * format.
lineHeight();
664 totalHeight += blockIndex == 0 ? maxBlockHeight : maxBlockLineSpacing * format.
lineHeight();
665 if ( blockIndex > 0 )
666 lastLineLeading = maxBlockLeading;
673 return ( totalHeight - lastLineLeading ) / scaleFactor;
678 double maxBlockHeight = 0;
681 double blockHeight = 0;
682 int fragmentIndex = 0;
685 QFont fragmentFont = baseFont;
687 const QFontMetricsF fm( fragmentFont );
689 const double labelHeight = fm.ascent();
690 const double letterSpacing = fragmentFont.letterSpacing();
692 blockHeight += fragmentIndex = 0 ? labelHeight * fragment.
text().size() + ( fragment.
text().size() - 1 ) * letterSpacing
693 : fragment.
text().size() * ( labelHeight + letterSpacing );
696 maxBlockHeight = std::max( maxBlockHeight, blockHeight );
699 return maxBlockHeight / scaleFactor;
716 QPainter *prevP = context.
painter();
717 QPainter *p = context.
painter();
718 std::unique_ptr< QgsPaintEffect > tmpEffect;
722 tmpEffect->begin( context );
733 component.rotation = -( component.rotation * 180 / M_PI );
734 component.rotationOffset =
739 component.rotation = 0.0;
740 component.rotationOffset = background.
rotation();
748 QFontMetricsF fm( format.
scaledFont( context, scaleFactor ) );
749 double width =
textWidth( context, format, document );
750 double height =
textHeight( context, format, document, mode );
755 switch ( component.hAlign )
759 component.center = QPointF( component.origin.x() + width / 2.0,
760 component.origin.y() + height / 2.0 );
764 component.center = QPointF( component.origin.x() + component.size.width() / 2.0,
765 component.origin.y() + height / 2.0 );
769 component.center = QPointF( component.origin.x() + component.size.width() - width / 2.0,
770 component.origin.y() + height / 2.0 );
777 double originAdjust = fm.ascent() / scaleFactor / 2.0 - fm.leading() / scaleFactor / 2.0;
778 switch ( component.hAlign )
782 component.center = QPointF( component.origin.x() + width / 2.0,
783 component.origin.y() - height / 2.0 + originAdjust );
787 component.center = QPointF( component.origin.x(),
788 component.origin.y() - height / 2.0 + originAdjust );
792 component.center = QPointF( component.origin.x() - width / 2.0,
793 component.origin.y() - height / 2.0 + originAdjust );
804 component.size = QSizeF( width, height );
809 switch ( background.
type() )
822 double sizeOut = 0.0;
830 sizeOut = std::max( component.size.width(), component.size.height() );
834 sizeOut += bufferSize * 2;
842 std::unique_ptr< QgsMarkerSymbol > renderedSymbol;
846 map[QStringLiteral(
"name" )] = background.
svgFile().trimmed();
847 map[QStringLiteral(
"size" )] = QString::number( sizeOut );
849 map[QStringLiteral(
"angle" )] = QString::number( 0.0 );
857 map[QStringLiteral(
"fill" )] = background.
fillColor().name();
858 map[QStringLiteral(
"outline" )] = background.
strokeColor().name();
859 map[QStringLiteral(
"outline-width" )] = QString::number( background.
strokeWidth() );
866 QVariantMap shdwmap( map );
867 shdwmap[QStringLiteral(
"fill" )] = shadow.
color().name();
868 shdwmap[QStringLiteral(
"outline" )] = shadow.
color().name();
869 shdwmap[QStringLiteral(
"size" )] = QString::number( sizeOut );
874 svgp.begin( &svgPict );
891 svgShdwM->
renderPoint( QPointF( sizeOut / 2, -sizeOut / 2 ), svgShdwContext );
894 component.picture = svgPict;
896 component.pictureBuffer = 0.0;
898 component.size = QSizeF( sizeOut, sizeOut );
899 component.offset = QPointF( 0.0, 0.0 );
905 p->translate( component.center.x(), component.center.y() );
906 p->rotate( component.rotation );
909 p->translate( QPointF( xoff, yoff ) );
910 p->rotate( component.rotationOffset );
911 p->translate( -sizeOut / 2, sizeOut / 2 );
913 drawShadow( context, component, format );
915 renderedSymbol.reset( );
923 renderedSymbol->setSize( sizeOut );
927 renderedSymbol->setOpacity( background.
opacity() );
935 p->setCompositionMode( background.
blendMode() );
937 p->translate( component.center.x(), component.center.y() );
938 p->rotate( component.rotation );
941 p->translate( QPointF( xoff, yoff ) );
942 p->rotate( component.rotationOffset );
946 renderedSymbol->renderPoint( QPointF( 0, 0 ), &f, context );
947 renderedSymbol->stopRender( context );
948 p->setCompositionMode( QPainter::CompositionMode_SourceOver );
958 double w = component.size.width();
959 double h = component.size.height();
980 h = std::sqrt( std::pow( w, 2 ) + std::pow( h, 2 ) );
986 h = h * M_SQRT1_2 * 2;
987 w = w * M_SQRT1_2 * 2;
995 w += bufferWidth * 2;
996 h += bufferHeight * 2;
1000 QRectF rect( -w / 2.0, - h / 2.0, w, h );
1002 if ( rect.isNull() )
1008 p->translate( QPointF( component.center.x(), component.center.y() ) );
1009 p->rotate( component.rotation );
1012 p->translate( QPointF( xoff, yoff ) );
1013 p->rotate( component.rotationOffset );
1021 pen.setWidthF( penSize );
1023 pen.setJoinStyle( background.
joinStyle() );
1033 shapep.begin( &shapePict );
1034 shapep.setPen( pen );
1035 shapep.setBrush( background.
fillColor() );
1042 shapep.drawRoundedRect( rect, background.
radii().width(), background.
radii().height(), Qt::RelativeSize );
1048 shapep.drawRoundedRect( rect, xRadius, yRadius );
1054 shapep.drawEllipse( rect );
1060 component.picture = shapePict;
1061 component.pictureBuffer = penSize / 2.0;
1063 component.size = rect.size();
1064 component.offset = QPointF( rect.width() / 2, -rect.height() / 2 );
1065 drawShadow( context, component, format );
1068 p->setOpacity( background.
opacity() );
1071 p->setCompositionMode( background.
blendMode() );
1075 p->scale( component.dpiRatio, component.dpiRatio );
1076 _fixQPictureDPI( p );
1077 p->drawPicture( 0, 0, shapePict );
1084 tmpEffect->end( context );
1097 QPainter *p = context.
painter();
1098 double componentWidth = component.size.width(), componentHeight = component.size.height();
1099 double xOffset = component.offset.x(), yOffset = component.offset.y();
1100 double pictbuffer = component.pictureBuffer;
1105 radius /= ( mapUnits ? context.
scaleFactor() / component.dpiRatio : 1 );
1106 radius =
static_cast< int >( radius + 0.5 );
1110 double blurBufferClippingScale = 3.75;
1111 int blurbuffer = ( radius > 17 ? 16 : radius ) * blurBufferClippingScale;
1113 QImage blurImg( componentWidth + ( pictbuffer * 2.0 ) + ( blurbuffer * 2.0 ),
1114 componentHeight + ( pictbuffer * 2.0 ) + ( blurbuffer * 2.0 ),
1115 QImage::Format_ARGB32_Premultiplied );
1119 int minBlurImgSize = 1;
1123 int maxBlurImgSize = 40000;
1124 if ( blurImg.isNull()
1125 || ( blurImg.width() < minBlurImgSize || blurImg.height() < minBlurImgSize )
1126 || ( blurImg.width() > maxBlurImgSize || blurImg.height() > maxBlurImgSize ) )
1129 blurImg.fill( QColor( Qt::transparent ).rgba() );
1131 if ( !pictp.begin( &blurImg ) )
1133 pictp.setRenderHints( QPainter::Antialiasing | QPainter::SmoothPixmapTransform );
1134 QPointF imgOffset( blurbuffer + pictbuffer + xOffset,
1135 blurbuffer + pictbuffer + componentHeight + yOffset );
1137 pictp.drawPicture( imgOffset,
1138 component.picture );
1141 pictp.setCompositionMode( QPainter::CompositionMode_SourceIn );
1142 pictp.fillRect( blurImg.rect(), shadow.
color() );
1146 if ( shadow.
blurRadius() > 0.0 && radius > 0 )
1154 picti.begin( &blurImg );
1155 picti.setBrush( Qt::Dense7Pattern );
1156 QPen imgPen( QColor( 0, 0, 255, 255 ) );
1157 imgPen.setWidth( 1 );
1158 picti.setPen( imgPen );
1159 picti.setOpacity( 0.1 );
1160 picti.drawRect( 0, 0, blurImg.width(), blurImg.height() );
1165 double angleRad = shadow.
offsetAngle() * M_PI / 180;
1173 angleRad -= ( component.rotation * M_PI / 180 + component.rotationOffset * M_PI / 180 );
1176 QPointF transPt( -offsetDist * std::cos( angleRad + M_PI_2 ),
1177 -offsetDist * std::sin( angleRad + M_PI_2 ) );
1180 p->setRenderHint( QPainter::SmoothPixmapTransform );
1184 p->setCompositionMode( shadow.
blendMode() );
1186 p->setOpacity( shadow.
opacity() );
1188 double scale = shadow.
scale() / 100.0;
1190 p->scale( scale, scale );
1191 if ( component.useOrigin )
1193 p->translate( component.origin.x(), component.origin.y() );
1195 p->translate( transPt );
1196 p->translate( -imgOffset.x(),
1198 p->drawImage( 0, 0, blurImg );
1205 p->setBrush( Qt::NoBrush );
1206 QPen imgPen( QColor( 255, 0, 0, 10 ) );
1207 imgPen.setWidth( 2 );
1208 imgPen.setStyle( Qt::DashLine );
1209 p->setPen( imgPen );
1210 p->scale( scale, scale );
1211 if ( component.useOrigin() )
1213 p->translate( component.origin().x(), component.origin().y() );
1215 p->translate( transPt );
1216 p->translate( -imgOffset.x(),
1218 p->drawRect( 0, 0, blurImg.width(), blurImg.height() );
1223 p->setBrush( Qt::NoBrush );
1224 QPen componentRectPen( QColor( 0, 255, 0, 70 ) );
1225 componentRectPen.setWidth( 1 );
1226 if ( component.useOrigin() )
1228 p->translate( component.origin().x(), component.origin().y() );
1230 p->setPen( componentRectPen );
1231 p->drawRect( QRect( -xOffset, -componentHeight - yOffset, componentWidth, componentHeight ) );
1237 void QgsTextRenderer::drawTextInternal( TextPart drawType,
1240 const Component &component,
1242 const QFontMetricsF *fontMetrics,
1243 HAlignment alignment, VAlignment vAlignment, DrawMode mode )
1250 double fontScale = 1.0;
1251 std::unique_ptr< QFontMetricsF > tmpMetrics;
1255 const QFont f = format.
scaledFont( context, fontScale );
1256 tmpMetrics = qgis::make_unique< QFontMetricsF >( f );
1260 double rotation = 0;
1262 switch ( orientation )
1266 drawTextInternalHorizontal( context, format, drawType, mode, component, document, fontScale,
fontMetrics, alignment, vAlignment, rotation );
1273 drawTextInternalVertical( context, format, drawType, mode, component, document, fontScale,
fontMetrics, alignment, vAlignment, rotation );
1281 rotation = -component.rotation * 180 / M_PI;
1288 if ( rotation >= -315 && rotation < -90 )
1293 else if ( rotation >= -90 && rotation < -45 )
1309 void QgsTextRenderer::calculateExtraSpacingForLineJustification(
const double spaceToDistribute,
const QgsTextBlock &block,
double &extraWordSpace,
double &extraLetterSpace )
1312 QTextBoundaryFinder
finder( QTextBoundaryFinder::Word, blockText );
1314 int wordBoundaries = 0;
1315 while (
finder.toNextBoundary() != -1 )
1317 if (
finder.boundaryReasons() & QTextBoundaryFinder::StartOfItem )
1321 if ( wordBoundaries > 0 )
1324 extraWordSpace = spaceToDistribute / wordBoundaries;
1329 QTextBoundaryFinder
finder( QTextBoundaryFinder::Grapheme, blockText );
1332 int graphemeBoundaries = 0;
1333 while (
finder.toNextBoundary() != -1 )
1335 if (
finder.boundaryReasons() & QTextBoundaryFinder::StartOfItem )
1336 graphemeBoundaries++;
1339 if ( graphemeBoundaries > 0 )
1341 extraLetterSpace = spaceToDistribute / graphemeBoundaries;
1346 void QgsTextRenderer::applyExtraSpacingForLineJustification( QFont &font,
double extraWordSpace,
double extraLetterSpace )
1348 const double prevWordSpace = font.wordSpacing();
1349 font.setWordSpacing( prevWordSpace + extraWordSpace );
1350 const double prevLetterSpace = font.letterSpacing();
1351 font.setLetterSpacing( QFont::AbsoluteSpacing, prevLetterSpace + extraLetterSpace );
1354 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,
1355 VAlignment vAlignment,
double rotation )
1358 const QStringList textLines = document.
toPlainText();
1360 double labelWidest = 0.0;
1365 for (
const QString &line : textLines )
1367 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
1368 double labelWidth =
fontMetrics->width( line ) / fontScale;
1370 double labelWidth =
fontMetrics->horizontalAdvance( line ) / fontScale;
1372 if ( labelWidth > labelWidest )
1374 labelWidest = labelWidth;
1380 labelWidest = component.size.width();
1388 double ascentOffset = 0.25 *
fontMetrics->ascent() / fontScale;
1392 bool adjustForAlignment = hAlignment !=
AlignLeft && ( mode !=
Label || textLines.size() > 1 );
1397 const double overallHeight =
textHeight( context, format, textLines,
Rect );
1398 switch ( vAlignment )
1404 ascentOffset = -( component.size.height() - overallHeight ) * 0.5 + ascentOffset;
1408 ascentOffset = -( component.size.height() - overallHeight ) + ascentOffset;
1413 for (
const QString &line : qgis::as_const( textLines ) )
1417 const bool isFinalLine = i == document.
size() - 1;
1421 context.
painter()->translate( component.origin );
1423 context.
painter()->rotate( rotation );
1428 maskPainter->save();
1429 maskPainter->translate( component.origin );
1431 maskPainter->rotate( rotation );
1435 double xMultiLineOffset = 0.0;
1436 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
1437 double labelWidth =
fontMetrics->width( line ) / fontScale;
1439 double labelWidth =
fontMetrics->horizontalAdvance( line ) / fontScale;
1441 double extraWordSpace = 0;
1442 double extraLetterSpace = 0;
1443 if ( adjustForAlignment )
1445 double labelWidthDiff = 0;
1446 switch ( hAlignment )
1449 labelWidthDiff = ( labelWidest - labelWidth ) * 0.5;
1453 labelWidthDiff = labelWidest - labelWidth;
1457 if ( !isFinalLine && labelWidest > labelWidth )
1459 calculateExtraSpacingForLineJustification( labelWidest - labelWidth, block, extraWordSpace, extraLetterSpace );
1471 xMultiLineOffset = labelWidthDiff;
1476 switch ( hAlignment )
1479 xMultiLineOffset = labelWidthDiff - labelWidest;
1483 xMultiLineOffset = labelWidthDiff - labelWidest / 2.0;
1495 double yMultiLineOffset = ascentOffset;
1502 yMultiLineOffset = - ascentOffset - ( textLines.size() - 1 - i ) * labelHeight * format.
lineHeight();
1507 yMultiLineOffset = - ascentOffset + labelHeight - 1 + format.
lineHeight() *
fontMetrics->lineSpacing() * i / fontScale;
1512 yMultiLineOffset = 0 - ( textLines.size() - 1 - i ) *
fontMetrics->lineSpacing() * format.
lineHeight() / fontScale;
1517 context.
painter()->translate( QPointF( xMultiLineOffset, yMultiLineOffset ) );
1519 maskPainter->translate( QPointF( xMultiLineOffset, yMultiLineOffset ) );
1521 Component subComponent;
1522 subComponent.block = block;
1523 subComponent.
size = QSizeF( labelWidth, labelHeight );
1524 subComponent.offset = QPointF( 0.0, -ascentOffset );
1525 subComponent.rotation = -component.rotation * 180 / M_PI;
1526 subComponent.rotationOffset = 0.0;
1527 subComponent.extraWordSpacing = extraWordSpace * fontScale;
1528 subComponent.extraLetterSpacing = extraLetterSpace * fontScale;
1533 QgsTextRenderer::drawMask( context, subComponent, format );
1538 QgsTextRenderer::drawBuffer( context, subComponent, format );
1545 textp.begin( &textPict );
1546 textp.setPen( Qt::NoPen );
1547 const QFont font = format.
scaledFont( context, fontScale );
1548 textp.scale( 1 / fontScale, 1 / fontScale );
1555 path.setFillRule( Qt::WindingFill );
1557 QFont fragmentFont = font;
1560 if ( extraWordSpace || extraLetterSpace )
1561 applyExtraSpacingForLineJustification( fragmentFont, extraWordSpace * fontScale, extraLetterSpace * fontScale );
1563 path.addText( xOffset, 0, fragmentFont, fragment.
text() );
1566 textColor.setAlphaF( format.
opacity() );
1567 textp.setBrush( textColor );
1568 textp.drawPath( path );
1576 subComponent.picture = textPict;
1577 subComponent.pictureBuffer = 0.0;
1578 subComponent.origin = QPointF( 0.0, 0.0 );
1580 QgsTextRenderer::drawShadow( context, subComponent, format );
1590 context.
painter()->scale( subComponent.dpiRatio, subComponent.dpiRatio );
1597 _fixQPictureDPI( context.
painter() );
1598 context.
painter()->drawPicture( 0, 0, textPict );
1607 QFont fragmentFont = font;
1610 if ( extraWordSpace || extraLetterSpace )
1611 applyExtraSpacingForLineJustification( fragmentFont, extraWordSpace * fontScale, extraLetterSpace * fontScale );
1614 textColor.setAlphaF( format.
opacity() );
1616 context.
painter()->setPen( textColor );
1617 context.
painter()->setFont( fragmentFont );
1618 context.
painter()->setRenderHint( QPainter::TextAntialiasing );
1620 context.
painter()->scale( 1 / fontScale, 1 / fontScale );
1621 context.
painter()->drawText( xOffset, 0, fragment.
text() );
1622 context.
painter()->scale( fontScale, fontScale );
1630 maskPainter->restore();
1635 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 )
1638 const QStringList textLines = document.
toPlainText();
1640 const QFont font = format.
scaledFont( context, fontScale );
1641 double letterSpacing = font.letterSpacing() / fontScale;
1643 double labelWidth =
fontMetrics->maxWidth() / fontScale;
1644 double actualLabelWidest = labelWidth + ( textLines.size() - 1 ) * labelWidth * format.
lineHeight();
1645 double labelWidest = 0.0;
1650 labelWidest = actualLabelWidest;
1654 labelWidest = component.size.width();
1658 int maxLineLength = 0;
1659 for (
const QString &line : qgis::as_const( textLines ) )
1661 maxLineLength = std::max( maxLineLength, line.length() );
1663 double actualLabelHeight =
fontMetrics->ascent() / fontScale + (
fontMetrics->ascent() / fontScale + letterSpacing ) * ( maxLineLength - 1 );
1664 double ascentOffset =
fontMetrics->ascent() / fontScale;
1668 bool adjustForAlignment = hAlignment !=
AlignLeft && ( mode !=
Label || textLines.size() > 1 );
1675 context.
painter()->translate( component.origin );
1677 context.
painter()->rotate( rotation );
1682 maskPainter->save();
1683 maskPainter->translate( component.origin );
1685 maskPainter->rotate( rotation );
1689 double xOffset = actualLabelWidest - labelWidth - ( i * labelWidth * format.
lineHeight() );
1690 if ( adjustForAlignment )
1692 double labelWidthDiff = 0;
1693 switch ( hAlignment )
1696 labelWidthDiff = ( labelWidest - actualLabelWidest ) * 0.5;
1700 labelWidthDiff = labelWidest - actualLabelWidest;
1712 xOffset += labelWidthDiff;
1720 double yOffset = 0.0;
1726 if ( rotation >= -405 && rotation < -180 )
1728 yOffset = ascentOffset;
1730 else if ( rotation >= 0 && rotation < 45 )
1732 xOffset -= actualLabelWidest;
1733 yOffset = -actualLabelHeight + ascentOffset +
fontMetrics->descent() / fontScale;
1738 yOffset = -actualLabelHeight + ascentOffset;
1743 yOffset = -actualLabelHeight + ascentOffset;
1747 yOffset = ascentOffset;
1751 context.
painter()->translate( QPointF( xOffset, yOffset ) );
1753 double fragmentYOffset = 0;
1759 QFont fragmentFont( font );
1762 QFontMetricsF fragmentMetrics( fragmentFont );
1764 double labelHeight = fragmentMetrics.ascent() / fontScale + ( fragmentMetrics.ascent() / fontScale + letterSpacing ) * ( line.length() - 1 );
1766 Component subComponent;
1768 subComponent.size = QSizeF( labelWidth, labelHeight );
1769 subComponent.offset = QPointF( 0.0, fragmentYOffset );
1770 subComponent.rotation = -component.rotation * 180 / M_PI;
1771 subComponent.rotationOffset = 0.0;
1778 QgsTextRenderer::drawMask( context, subComponent, format );
1784 fragmentYOffset += QgsTextRenderer::drawBuffer( context, subComponent, format );
1790 path.setFillRule( Qt::WindingFill );
1792 double partYOffset = 0.0;
1793 for (
const auto &part : parts )
1795 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
1796 double partXOffset = ( labelWidth - ( fragmentMetrics.width( part ) / fontScale - letterSpacing ) ) / 2;
1798 double partXOffset = ( labelWidth - ( fragmentMetrics.horizontalAdvance( part ) / fontScale - letterSpacing ) ) / 2;
1800 path.addText( partXOffset * fontScale, partYOffset * fontScale, fragmentFont, part );
1801 partYOffset += fragmentMetrics.ascent() / fontScale + letterSpacing;
1807 textp.begin( &textPict );
1808 textp.setPen( Qt::NoPen );
1810 textColor.setAlphaF( format.
opacity() );
1811 textp.setBrush( textColor );
1812 textp.scale( 1 / fontScale, 1 / fontScale );
1813 textp.drawPath( path );
1823 subComponent.picture = textPict;
1824 subComponent.pictureBuffer = 0.0;
1825 subComponent.origin = QPointF( 0.0, fragmentYOffset );
1826 const double prevY = subComponent.offset.y();
1827 subComponent.offset = QPointF( 0, -labelHeight );
1828 subComponent.useOrigin =
true;
1829 QgsTextRenderer::drawShadow( context, subComponent, format );
1830 subComponent.useOrigin =
false;
1831 subComponent.offset = QPointF( 0, prevY );
1841 context.
painter()->scale( subComponent.dpiRatio, subComponent.dpiRatio );
1848 _fixQPictureDPI( context.
painter() );
1849 context.
painter()->drawPicture( 0, fragmentYOffset, textPict );
1850 fragmentYOffset += partYOffset;
1856 context.
painter()->setFont( fragmentFont );
1857 context.
painter()->setPen( textColor );
1858 context.
painter()->setRenderHint( QPainter::TextAntialiasing );
1860 double partYOffset = 0.0;
1861 for (
const QString &part : parts )
1863 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
1864 double partXOffset = ( labelWidth - ( fragmentMetrics.width( part ) / fontScale - letterSpacing ) ) / 2;
1866 double partXOffset = ( labelWidth - ( fragmentMetrics.horizontalAdvance( part ) / fontScale - letterSpacing ) ) / 2;
1868 context.
painter()->scale( 1 / fontScale, 1 / fontScale );
1869 context.
painter()->drawText( partXOffset * fontScale, ( fragmentYOffset + partYOffset ) * fontScale, part );
1870 context.
painter()->scale( fontScale, fontScale );
1871 partYOffset += fragmentMetrics.ascent() / fontScale + letterSpacing;
1873 fragmentYOffset += partYOffset;
1880 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 id, geometry and a list of field/values...
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.
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.
Qt::PenJoinStyle joinStyle() const
Returns the join style 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.
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()