30 static void _fixQPictureDPI( QPainter *p )
36 p->scale(
static_cast< double >(
qt_defaultDpiX() ) / p->device()->logicalDpiX(),
37 static_cast< double >(
qt_defaultDpiY() ) / p->device()->logicalDpiY() );
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 );
80 tmpFormat = updateShadowPosition( tmpFormat );
87 drawPart( rect, rotation, alignment, vAlignment, document, context, tmpFormat,
Background );
92 drawPart( rect, rotation, alignment, vAlignment, document, context, tmpFormat,
Buffer );
95 drawPart( rect, rotation, alignment, vAlignment, document, context, tmpFormat,
Text );
103 tmpFormat = updateShadowPosition( tmpFormat );
115 drawPart( point, rotation, alignment, document, context, tmpFormat,
Buffer );
118 drawPart( point, rotation, alignment, document, context, tmpFormat,
Text );
147 drawPart( rect, rotation, alignment,
AlignTop, document, context, format, part );
158 component.dpiRatio = 1.0;
159 component.origin = rect.topLeft();
160 component.rotation = rotation;
161 component.size = rect.size();
162 component.hAlign = alignment;
175 double xc = rect.width() / 2.0;
176 double yc = rect.height() / 2.0;
178 double angle = -rotation;
179 double xd = xc * std::cos(
angle ) - yc * std::sin(
angle );
180 double yd = xc * std::sin(
angle ) + yc * std::cos(
angle );
182 component.center = QPointF( component.origin.x() + xd, component.origin.y() + yd );
186 component.center = rect.center();
189 QgsTextRenderer::drawBackground( context, component, format, document,
Rect );
203 drawTextInternal( part, context, format, component,
206 alignment, vAlignment );
215 drawPart( origin, rotation, alignment, document, context, format, part );
226 component.dpiRatio = 1.0;
227 component.origin = origin;
228 component.rotation = rotation;
229 component.hAlign = alignment;
238 QgsTextRenderer::drawBackground( context, component, format, document,
Point );
251 drawTextInternal( part, context, format, component,
263 return QFontMetricsF( format.
scaledFont( context, scaleFactor ), context.
painter() ? context.
painter()->device() :
nullptr );
268 QPainter *p = context.
painter();
273 if ( component.rotation >= -315 && component.rotation < -90 )
277 else if ( component.rotation >= -90 && component.rotation < -45 )
292 const QFont font = format.
scaledFont( context, scaleFactor );
295 path.setFillRule( Qt::WindingFill );
297 switch ( orientation )
304 QFont fragmentFont = font;
307 if ( component.extraWordSpacing || component.extraLetterSpacing )
308 applyExtraSpacingForLineJustification( fragmentFont, component.extraWordSpacing, component.extraLetterSpacing );
310 path.addText( xOffset, 0, fragmentFont, fragment.
text() );
321 double letterSpacing = font.letterSpacing();
322 double partYOffset = component.offset.y() * scaleFactor;
325 QFont fragmentFont = font;
328 QFontMetricsF fragmentMetrics( fragmentFont );
329 const double labelWidth = fragmentMetrics.maxWidth();
332 for (
const QString &part : parts )
334 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
335 double partXOffset = ( labelWidth - ( fragmentMetrics.width( part ) - letterSpacing ) ) / 2;
337 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() );
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( 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 );
1019 pen.setWidthF( penSize );
1021 pen.setJoinStyle( background.
joinStyle() );
1031 shapep.begin( &shapePict );
1032 shapep.setPen( pen );
1033 shapep.setBrush( background.
fillColor() );
1040 shapep.drawRoundedRect( rect, background.
radii().width(), background.
radii().height(), Qt::RelativeSize );
1046 shapep.drawRoundedRect( rect, xRadius, yRadius );
1052 shapep.drawEllipse( rect );
1058 component.picture = shapePict;
1059 component.pictureBuffer = penSize / 2.0;
1061 component.size = rect.size();
1062 component.offset = QPointF( rect.width() / 2, -rect.height() / 2 );
1063 drawShadow( context, component, format );
1066 p->setOpacity( background.
opacity() );
1069 p->setCompositionMode( background.
blendMode() );
1073 p->scale( component.dpiRatio, component.dpiRatio );
1074 _fixQPictureDPI( p );
1075 p->drawPicture( 0, 0, shapePict );
1082 tmpEffect->end( context );
1095 QPainter *p = context.
painter();
1096 double componentWidth = component.size.width(), componentHeight = component.size.height();
1097 double xOffset = component.offset.x(), yOffset = component.offset.y();
1098 double pictbuffer = component.pictureBuffer;
1103 radius /= ( mapUnits ? context.
scaleFactor() / component.dpiRatio : 1 );
1104 radius =
static_cast< int >( radius + 0.5 );
1108 double blurBufferClippingScale = 3.75;
1109 int blurbuffer = ( radius > 17 ? 16 : radius ) * blurBufferClippingScale;
1111 QImage blurImg( componentWidth + ( pictbuffer * 2.0 ) + ( blurbuffer * 2.0 ),
1112 componentHeight + ( pictbuffer * 2.0 ) + ( blurbuffer * 2.0 ),
1113 QImage::Format_ARGB32_Premultiplied );
1117 int minBlurImgSize = 1;
1121 int maxBlurImgSize = 40000;
1122 if ( blurImg.isNull()
1123 || ( blurImg.width() < minBlurImgSize || blurImg.height() < minBlurImgSize )
1124 || ( blurImg.width() > maxBlurImgSize || blurImg.height() > maxBlurImgSize ) )
1127 blurImg.fill( QColor( Qt::transparent ).rgba() );
1129 if ( !pictp.begin( &blurImg ) )
1131 pictp.setRenderHints( QPainter::Antialiasing | QPainter::SmoothPixmapTransform );
1132 QPointF imgOffset( blurbuffer + pictbuffer + xOffset,
1133 blurbuffer + pictbuffer + componentHeight + yOffset );
1135 pictp.drawPicture( imgOffset,
1136 component.picture );
1139 pictp.setCompositionMode( QPainter::CompositionMode_SourceIn );
1140 pictp.fillRect( blurImg.rect(), shadow.
color() );
1144 if ( shadow.
blurRadius() > 0.0 && radius > 0 )
1152 picti.begin( &blurImg );
1153 picti.setBrush( Qt::Dense7Pattern );
1154 QPen imgPen( QColor( 0, 0, 255, 255 ) );
1155 imgPen.setWidth( 1 );
1156 picti.setPen( imgPen );
1157 picti.setOpacity( 0.1 );
1158 picti.drawRect( 0, 0, blurImg.width(), blurImg.height() );
1163 double angleRad = shadow.
offsetAngle() * M_PI / 180;
1171 angleRad -= ( component.rotation * M_PI / 180 + component.rotationOffset * M_PI / 180 );
1174 QPointF transPt( -offsetDist * std::cos( angleRad + M_PI_2 ),
1175 -offsetDist * std::sin( angleRad + M_PI_2 ) );
1178 p->setRenderHint( QPainter::SmoothPixmapTransform );
1182 p->setCompositionMode( shadow.
blendMode() );
1184 p->setOpacity( shadow.
opacity() );
1186 double scale = shadow.
scale() / 100.0;
1188 p->scale( scale, scale );
1189 if ( component.useOrigin )
1191 p->translate( component.origin.x(), component.origin.y() );
1193 p->translate( transPt );
1194 p->translate( -imgOffset.x(),
1196 p->drawImage( 0, 0, blurImg );
1203 p->setBrush( Qt::NoBrush );
1204 QPen imgPen( QColor( 255, 0, 0, 10 ) );
1205 imgPen.setWidth( 2 );
1206 imgPen.setStyle( Qt::DashLine );
1207 p->setPen( imgPen );
1208 p->scale( scale, scale );
1209 if ( component.useOrigin() )
1211 p->translate( component.origin().x(), component.origin().y() );
1213 p->translate( transPt );
1214 p->translate( -imgOffset.x(),
1216 p->drawRect( 0, 0, blurImg.width(), blurImg.height() );
1221 p->setBrush( Qt::NoBrush );
1222 QPen componentRectPen( QColor( 0, 255, 0, 70 ) );
1223 componentRectPen.setWidth( 1 );
1224 if ( component.useOrigin() )
1226 p->translate( component.origin().x(), component.origin().y() );
1228 p->setPen( componentRectPen );
1229 p->drawRect( QRect( -xOffset, -componentHeight - yOffset, componentWidth, componentHeight ) );
1235 void QgsTextRenderer::drawTextInternal( TextPart drawType,
1238 const Component &component,
1240 const QFontMetricsF *fontMetrics,
1241 HAlignment alignment, VAlignment vAlignment, DrawMode mode )
1248 double fontScale = 1.0;
1249 std::unique_ptr< QFontMetricsF > tmpMetrics;
1253 const QFont f = format.
scaledFont( context, fontScale );
1254 tmpMetrics = qgis::make_unique< QFontMetricsF >( f );
1258 double rotation = 0;
1260 switch ( orientation )
1264 drawTextInternalHorizontal( context, format, drawType, mode, component, document, fontScale,
fontMetrics, alignment, vAlignment, rotation );
1271 drawTextInternalVertical( context, format, drawType, mode, component, document, fontScale,
fontMetrics, alignment, vAlignment, rotation );
1279 rotation = -component.rotation * 180 / M_PI;
1286 if ( rotation >= -315 && rotation < -90 )
1291 else if ( rotation >= -90 && rotation < -45 )
1307 void QgsTextRenderer::calculateExtraSpacingForLineJustification(
const double spaceToDistribute,
const QgsTextBlock &block,
double &extraWordSpace,
double &extraLetterSpace )
1310 QTextBoundaryFinder
finder( QTextBoundaryFinder::Word, blockText );
1312 int wordBoundaries = 0;
1313 while (
finder.toNextBoundary() != -1 )
1315 if (
finder.boundaryReasons() & QTextBoundaryFinder::StartOfItem )
1319 if ( wordBoundaries > 0 )
1322 extraWordSpace = spaceToDistribute / wordBoundaries;
1327 QTextBoundaryFinder
finder( QTextBoundaryFinder::Grapheme, blockText );
1330 int graphemeBoundaries = 0;
1331 while (
finder.toNextBoundary() != -1 )
1333 if (
finder.boundaryReasons() & QTextBoundaryFinder::StartOfItem )
1334 graphemeBoundaries++;
1337 if ( graphemeBoundaries > 0 )
1339 extraLetterSpace = spaceToDistribute / graphemeBoundaries;
1344 void QgsTextRenderer::applyExtraSpacingForLineJustification( QFont &font,
double extraWordSpace,
double extraLetterSpace )
1346 const double prevWordSpace = font.wordSpacing();
1347 font.setWordSpacing( prevWordSpace + extraWordSpace );
1348 const double prevLetterSpace = font.letterSpacing();
1349 font.setLetterSpacing( QFont::AbsoluteSpacing, prevLetterSpace + extraLetterSpace );
1352 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,
1353 VAlignment vAlignment,
double rotation )
1356 const QStringList textLines = document.
toPlainText();
1358 double labelWidest = 0.0;
1363 for (
const QString &line : textLines )
1365 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
1366 double labelWidth =
fontMetrics->width( line ) / fontScale;
1368 double labelWidth =
fontMetrics->horizontalAdvance( line ) / fontScale;
1370 if ( labelWidth > labelWidest )
1372 labelWidest = labelWidth;
1378 labelWidest = component.size.width();
1386 double ascentOffset = 0.25 *
fontMetrics->ascent() / fontScale;
1390 bool adjustForAlignment = hAlignment !=
AlignLeft && ( mode !=
Label || textLines.size() > 1 );
1395 const double overallHeight =
textHeight( context, format, textLines,
Rect );
1396 switch ( vAlignment )
1402 ascentOffset = -( component.size.height() - overallHeight ) * 0.5 + ascentOffset;
1406 ascentOffset = -( component.size.height() - overallHeight ) + ascentOffset;
1411 for (
const QString &line : qgis::as_const( textLines ) )
1415 const bool isFinalLine = i == document.
size() - 1;
1419 context.
painter()->translate( component.origin );
1421 context.
painter()->rotate( rotation );
1426 maskPainter->save();
1427 maskPainter->translate( component.origin );
1429 maskPainter->rotate( rotation );
1433 double xMultiLineOffset = 0.0;
1434 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
1435 double labelWidth =
fontMetrics->width( line ) / fontScale;
1437 double labelWidth =
fontMetrics->horizontalAdvance( line ) / fontScale;
1439 double extraWordSpace = 0;
1440 double extraLetterSpace = 0;
1441 if ( adjustForAlignment )
1443 double labelWidthDiff = 0;
1444 switch ( hAlignment )
1447 labelWidthDiff = ( labelWidest - labelWidth ) * 0.5;
1451 labelWidthDiff = labelWidest - labelWidth;
1455 if ( !isFinalLine && labelWidest > labelWidth )
1457 calculateExtraSpacingForLineJustification( labelWidest - labelWidth, block, extraWordSpace, extraLetterSpace );
1469 xMultiLineOffset = labelWidthDiff;
1474 switch ( hAlignment )
1477 xMultiLineOffset = labelWidthDiff - labelWidest;
1481 xMultiLineOffset = labelWidthDiff - labelWidest / 2.0;
1493 double yMultiLineOffset = ascentOffset;
1500 yMultiLineOffset = - ascentOffset - ( textLines.size() - 1 - i ) * labelHeight * format.
lineHeight();
1505 yMultiLineOffset = - ascentOffset + labelHeight - 1 + format.
lineHeight() *
fontMetrics->lineSpacing() * i / fontScale;
1510 yMultiLineOffset = 0 - ( textLines.size() - 1 - i ) *
fontMetrics->lineSpacing() * format.
lineHeight() / fontScale;
1515 context.
painter()->translate( QPointF( xMultiLineOffset, yMultiLineOffset ) );
1517 maskPainter->translate( QPointF( xMultiLineOffset, yMultiLineOffset ) );
1519 Component subComponent;
1520 subComponent.block = block;
1521 subComponent.
size = QSizeF( labelWidth, labelHeight );
1522 subComponent.offset = QPointF( 0.0, -ascentOffset );
1523 subComponent.rotation = -component.rotation * 180 / M_PI;
1524 subComponent.rotationOffset = 0.0;
1525 subComponent.extraWordSpacing = extraWordSpace * fontScale;
1526 subComponent.extraLetterSpacing = extraLetterSpace * fontScale;
1531 QgsTextRenderer::drawMask( context, subComponent, format );
1536 QgsTextRenderer::drawBuffer( context, subComponent, format );
1543 textp.begin( &textPict );
1544 textp.setPen( Qt::NoPen );
1545 const QFont font = format.
scaledFont( context, fontScale );
1546 textp.scale( 1 / fontScale, 1 / fontScale );
1553 path.setFillRule( Qt::WindingFill );
1555 QFont fragmentFont = font;
1558 if ( extraWordSpace || extraLetterSpace )
1559 applyExtraSpacingForLineJustification( fragmentFont, extraWordSpace * fontScale, extraLetterSpace * fontScale );
1561 path.addText( xOffset, 0, fragmentFont, fragment.
text() );
1564 textColor.setAlphaF( format.
opacity() );
1565 textp.setBrush( textColor );
1566 textp.drawPath( path );
1574 subComponent.picture = textPict;
1575 subComponent.pictureBuffer = 0.0;
1576 subComponent.origin = QPointF( 0.0, 0.0 );
1578 QgsTextRenderer::drawShadow( context, subComponent, format );
1588 context.
painter()->scale( subComponent.dpiRatio, subComponent.dpiRatio );
1595 _fixQPictureDPI( context.
painter() );
1596 context.
painter()->drawPicture( 0, 0, textPict );
1605 QFont fragmentFont = font;
1608 if ( extraWordSpace || extraLetterSpace )
1609 applyExtraSpacingForLineJustification( fragmentFont, extraWordSpace * fontScale, extraLetterSpace * fontScale );
1612 textColor.setAlphaF( format.
opacity() );
1614 context.
painter()->setPen( textColor );
1615 context.
painter()->setFont( fragmentFont );
1616 context.
painter()->setRenderHint( QPainter::TextAntialiasing );
1618 context.
painter()->scale( 1 / fontScale, 1 / fontScale );
1619 context.
painter()->drawText( xOffset, 0, fragment.
text() );
1620 context.
painter()->scale( fontScale, fontScale );
1628 maskPainter->restore();
1633 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 )
1636 const QStringList textLines = document.
toPlainText();
1638 const QFont font = format.
scaledFont( context, fontScale );
1639 double letterSpacing = font.letterSpacing() / fontScale;
1641 double labelWidth =
fontMetrics->maxWidth() / fontScale;
1642 double actualLabelWidest = labelWidth + ( textLines.size() - 1 ) * labelWidth * format.
lineHeight();
1643 double labelWidest = 0.0;
1648 labelWidest = actualLabelWidest;
1652 labelWidest = component.size.width();
1656 int maxLineLength = 0;
1657 for (
const QString &line : qgis::as_const( textLines ) )
1659 maxLineLength = std::max( maxLineLength, line.length() );
1661 double actualLabelHeight =
fontMetrics->ascent() / fontScale + (
fontMetrics->ascent() / fontScale + letterSpacing ) * ( maxLineLength - 1 );
1662 double ascentOffset =
fontMetrics->ascent() / fontScale;
1666 bool adjustForAlignment = hAlignment !=
AlignLeft && ( mode !=
Label || textLines.size() > 1 );
1673 context.
painter()->translate( component.origin );
1675 context.
painter()->rotate( rotation );
1680 maskPainter->save();
1681 maskPainter->translate( component.origin );
1683 maskPainter->rotate( rotation );
1687 double xOffset = actualLabelWidest - labelWidth - ( i * labelWidth * format.
lineHeight() );
1688 if ( adjustForAlignment )
1690 double labelWidthDiff = 0;
1691 switch ( hAlignment )
1694 labelWidthDiff = ( labelWidest - actualLabelWidest ) * 0.5;
1698 labelWidthDiff = labelWidest - actualLabelWidest;
1710 xOffset += labelWidthDiff;
1718 double yOffset = 0.0;
1724 if ( rotation >= -405 && rotation < -180 )
1726 yOffset = ascentOffset;
1728 else if ( rotation >= 0 && rotation < 45 )
1730 xOffset -= actualLabelWidest;
1731 yOffset = -actualLabelHeight + ascentOffset +
fontMetrics->descent() / fontScale;
1736 yOffset = -actualLabelHeight + ascentOffset;
1741 yOffset = -actualLabelHeight + ascentOffset;
1745 yOffset = ascentOffset;
1749 context.
painter()->translate( QPointF( xOffset, yOffset ) );
1751 double fragmentYOffset = 0;
1757 QFont fragmentFont( font );
1760 QFontMetricsF fragmentMetrics( fragmentFont );
1762 double labelHeight = fragmentMetrics.ascent() / fontScale + ( fragmentMetrics.ascent() / fontScale + letterSpacing ) * ( line.length() - 1 );
1764 Component subComponent;
1766 subComponent.size = QSizeF( labelWidth, labelHeight );
1767 subComponent.offset = QPointF( 0.0, fragmentYOffset );
1768 subComponent.rotation = -component.rotation * 180 / M_PI;
1769 subComponent.rotationOffset = 0.0;
1776 QgsTextRenderer::drawMask( context, subComponent, format );
1782 fragmentYOffset += QgsTextRenderer::drawBuffer( context, subComponent, format );
1788 path.setFillRule( Qt::WindingFill );
1790 double partYOffset = 0.0;
1791 for (
const auto &part : parts )
1793 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
1794 double partXOffset = ( labelWidth - ( fragmentMetrics.width( part ) / fontScale - letterSpacing ) ) / 2;
1796 double partXOffset = ( labelWidth - ( fragmentMetrics.horizontalAdvance( part ) / fontScale - letterSpacing ) ) / 2;
1798 path.addText( partXOffset * fontScale, partYOffset * fontScale, fragmentFont, part );
1799 partYOffset += fragmentMetrics.ascent() / fontScale + letterSpacing;
1805 textp.begin( &textPict );
1806 textp.setPen( Qt::NoPen );
1808 textColor.setAlphaF( format.
opacity() );
1809 textp.setBrush( textColor );
1810 textp.scale( 1 / fontScale, 1 / fontScale );
1811 textp.drawPath( path );
1821 subComponent.picture = textPict;
1822 subComponent.pictureBuffer = 0.0;
1823 subComponent.origin = QPointF( 0.0, fragmentYOffset );
1824 const double prevY = subComponent.offset.y();
1825 subComponent.offset = QPointF( 0, -labelHeight );
1826 subComponent.useOrigin =
true;
1827 QgsTextRenderer::drawShadow( context, subComponent, format );
1828 subComponent.useOrigin =
false;
1829 subComponent.offset = QPointF( 0, prevY );
1839 context.
painter()->scale( subComponent.dpiRatio, subComponent.dpiRatio );
1846 _fixQPictureDPI( context.
painter() );
1847 context.
painter()->drawPicture( 0, fragmentYOffset, textPict );
1848 fragmentYOffset += partYOffset;
1854 context.
painter()->setFont( fragmentFont );
1855 context.
painter()->setPen( textColor );
1856 context.
painter()->setRenderHint( QPainter::TextAntialiasing );
1858 double partYOffset = 0.0;
1859 for (
const QString &part : parts )
1861 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
1862 double partXOffset = ( labelWidth - ( fragmentMetrics.width( part ) / fontScale - letterSpacing ) ) / 2;
1864 double partXOffset = ( labelWidth - ( fragmentMetrics.horizontalAdvance( part ) / fontScale - letterSpacing ) ) / 2;
1866 context.
painter()->scale( 1 / fontScale, 1 / fontScale );
1867 context.
painter()->drawText( partXOffset * fontScale, ( fragmentYOffset + partYOffset ) * fontScale, part );
1868 context.
painter()->scale( fontScale, fontScale );
1869 partYOffset += fragmentMetrics.ascent() / fontScale + letterSpacing;
1871 fragmentYOffset += partYOffset;
1878 maskPainter->restore();