Skip to content
Permalink
Browse files

[api] Add vertical alignment control to QgsTextRenderer

  • Loading branch information
nyalldawson committed Jul 7, 2020
1 parent 59c53ba commit a49cb7c9f3cb829b6df28677f66d022b19f49510
@@ -46,6 +46,13 @@ and background shapes.
AlignRight,
};

enum VAlignment
{
AlignTop,
AlignVCenter,
AlignBottom,
};

static int sizeToPixel( double size, const QgsRenderContext &c, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &mapUnitScale = QgsMapUnitScale() );
%Docstring
Calculates pixel size (considering output size should be in pixel or map units, scale factors and optionally oversampling)
@@ -61,7 +68,7 @@ Calculates pixel size (considering output size should be in pixel or map units,

static void drawText( const QRectF &rect, double rotation, HAlignment alignment, const QStringList &textLines,
QgsRenderContext &context, const QgsTextFormat &format,
bool drawAsOutlines = true );
bool drawAsOutlines = true, VAlignment vAlignment = AlignTop );
%Docstring
Draws text within a rectangle using the specified settings.

@@ -75,6 +82,7 @@ Draws text within a rectangle using the specified settings.
formats like SVG to maintain text as text objects, but at the cost of degraded
rendering and may result in side effects like misaligned text buffers. This setting is deprecated and has no effect
as of QGIS 3.4.3 and the text format should be set using :py:func:`QgsRenderContext.setTextRenderFormat()` instead.
:param vAlignment: vertical alignment (since QGIS 3.16)
%End

static void drawText( QPointF point, double rotation, HAlignment alignment, const QStringList &textLines,
@@ -637,7 +637,7 @@ void QgsVectorLayerLabelProvider::drawLabelPrivate( pal::LabelPosition *label, Q
}

QgsTextRenderer::drawTextInternal( drawType, context, tmpLyr.format(), component, document, labelfm,
hAlign, QgsTextRenderer::Label );
hAlign, QgsTextRenderer::AlignTop, QgsTextRenderer::Label );

}
if ( label->nextPart() )
@@ -42,7 +42,7 @@ int QgsTextRenderer::sizeToPixel( double size, const QgsRenderContext &c, QgsUni
return static_cast< int >( c.convertToPainterUnits( size, unit, mapUnitScale ) + 0.5 ); //NOLINT
}

void QgsTextRenderer::drawText( const QRectF &rect, double rotation, QgsTextRenderer::HAlignment alignment, const QStringList &textLines, QgsRenderContext &context, const QgsTextFormat &format, bool )
void QgsTextRenderer::drawText( const QRectF &rect, double rotation, QgsTextRenderer::HAlignment alignment, const QStringList &textLines, QgsRenderContext &context, const QgsTextFormat &format, bool, VAlignment vAlignment )
{
QgsTextFormat tmpFormat = format;
if ( format.dataDefinedProperties().hasActiveProperties() ) // note, we use format instead of tmpFormat here, it's const and potentially avoids a detach
@@ -53,15 +53,15 @@ void QgsTextRenderer::drawText( const QRectF &rect, double rotation, QgsTextRend

if ( tmpFormat.background().enabled() )
{
drawPart( rect, rotation, alignment, document, context, tmpFormat, Background );
drawPart( rect, rotation, alignment, vAlignment, document, context, tmpFormat, Background );
}

if ( tmpFormat.buffer().enabled() )
{
drawPart( rect, rotation, alignment, document, context, tmpFormat, Buffer );
drawPart( rect, rotation, alignment, vAlignment, document, context, tmpFormat, Buffer );
}

drawPart( rect, rotation, alignment, document, context, tmpFormat, Text );
drawPart( rect, rotation, alignment, vAlignment, document, context, tmpFormat, Text );
}

void QgsTextRenderer::drawText( QPointF point, double rotation, QgsTextRenderer::HAlignment alignment, const QStringList &textLines, QgsRenderContext &context, const QgsTextFormat &format, bool )
@@ -112,10 +112,10 @@ void QgsTextRenderer::drawPart( const QRectF &rect, double rotation, HAlignment
{
const QgsTextDocument document = format.allowHtmlFormatting() ? QgsTextDocument::fromHtml( textLines ) : QgsTextDocument::fromPlainText( textLines );

drawPart( rect, rotation, alignment, document, context, format, part );
drawPart( rect, rotation, alignment, AlignTop, document, context, format, part );
}

void QgsTextRenderer::drawPart( const QRectF &rect, double rotation, QgsTextRenderer::HAlignment alignment, const QgsTextDocument &document, QgsRenderContext &context, const QgsTextFormat &format, QgsTextRenderer::TextPart part )
void QgsTextRenderer::drawPart( const QRectF &rect, double rotation, QgsTextRenderer::HAlignment alignment, VAlignment vAlignment, const QgsTextDocument &document, QgsRenderContext &context, const QgsTextFormat &format, QgsTextRenderer::TextPart part )
{
if ( !context.painter() )
{
@@ -171,7 +171,7 @@ void QgsTextRenderer::drawPart( const QRectF &rect, double rotation, QgsTextRend
drawTextInternal( part, context, format, component,
document,
nullptr,
alignment );
alignment, vAlignment );
break;
}
}
@@ -219,7 +219,7 @@ void QgsTextRenderer::drawPart( QPointF origin, double rotation, QgsTextRenderer
drawTextInternal( part, context, format, component,
document,
nullptr,
alignment,
alignment, AlignTop,
Point );
break;
}
@@ -1146,7 +1146,7 @@ void QgsTextRenderer::drawTextInternal( TextPart drawType,
const Component &component,
const QgsTextDocument &document,
const QFontMetricsF *fontMetrics,
HAlignment alignment, DrawMode mode )
HAlignment alignment, VAlignment vAlignment, DrawMode mode )
{
if ( !context.painter() )
{
@@ -1222,6 +1222,26 @@ void QgsTextRenderer::drawTextInternal( TextPart drawType,

bool adjustForAlignment = alignment != AlignLeft && ( mode != Label || textLines.size() > 1 );

if ( mode == Rect && vAlignment != AlignTop )
{
// need to calculate overall text height in advance so that we can adjust for vertical alignment
const double overallHeight = textHeight( context, format, textLines, Rect );
switch ( vAlignment )
{
case AlignTop:
break;

case AlignVCenter:
ascentOffset = -( component.size.height() - overallHeight ) * 0.5 + ascentOffset;
break;

case AlignBottom:
ascentOffset = -( component.size.height() - overallHeight ) + ascentOffset;
break;
}
}


for ( const QString &line : qgis::as_const( textLines ) )
{
const QgsTextBlock block = document.at( i );
@@ -62,6 +62,17 @@ class CORE_EXPORT QgsTextRenderer
AlignRight, //!< Right align
};

/**
* Vertical alignment
* \since QGIS 3.16
*/
enum VAlignment
{
AlignTop = 0, //!< Align to top
AlignVCenter, //!< Center align
AlignBottom, //!< Align to bottom
};

/**
* Calculates pixel size (considering output size should be in pixel or map units, scale factors and optionally oversampling)
* \param size size to convert
@@ -86,10 +97,11 @@ class CORE_EXPORT QgsTextRenderer
* formats like SVG to maintain text as text objects, but at the cost of degraded
* rendering and may result in side effects like misaligned text buffers. This setting is deprecated and has no effect
* as of QGIS 3.4.3 and the text format should be set using QgsRenderContext::setTextRenderFormat() instead.
* \param vAlignment vertical alignment (since QGIS 3.16)
*/
static void drawText( const QRectF &rect, double rotation, HAlignment alignment, const QStringList &textLines,
QgsRenderContext &context, const QgsTextFormat &format,
bool drawAsOutlines = true );
bool drawAsOutlines = true, VAlignment vAlignment = AlignTop );

/**
* Draws text at a point origin using the specified settings.
@@ -239,6 +251,7 @@ class CORE_EXPORT QgsTextRenderer
* \param rect destination rectangle for text
* \param rotation text rotation
* \param alignment horizontal alignment
* \param vAlignment vertical alignment
* \param document text document to draw
* \param context render context
* \param format text format
@@ -248,7 +261,7 @@ class CORE_EXPORT QgsTextRenderer
* \note Not available in Python bindings
* \since QGIS 3.14
*/
static void drawPart( const QRectF &rect, double rotation, HAlignment alignment, const QgsTextDocument &document,
static void drawPart( const QRectF &rect, double rotation, HAlignment alignment, VAlignment vAlignment, const QgsTextDocument &document,
QgsRenderContext &context, const QgsTextFormat &format,
TextPart part );

@@ -299,6 +312,7 @@ class CORE_EXPORT QgsTextRenderer
const QgsTextDocument &document,
const QFontMetricsF *fontMetrics,
HAlignment alignment,
VAlignment vAlignment,
DrawMode mode = Rect );

friend class QgsVectorLayerLabelProvider;
@@ -914,7 +914,8 @@ def imageCheck(self, name, reference_image, image):

def checkRender(self, format, name, part=None, angle=0, alignment=QgsTextRenderer.AlignLeft,
text=['test'],
rect=QRectF(100, 100, 50, 250)):
rect=QRectF(100, 100, 50, 250),
vAlignment=QgsTextRenderer.AlignTop):

image = QImage(400, 400, QImage.Format_RGB32)

@@ -950,7 +951,7 @@ def checkRender(self, format, name, part=None, angle=0, alignment=QgsTextRendere
alignment,
text,
context,
format)
format, vAlignment=vAlignment)

painter.setFont(format.scaledFont(context))
painter.setPen(QPen(QColor(255, 0, 255, 200)))
@@ -2126,6 +2127,42 @@ def testDrawTextRectRightAlign(self):
assert self.checkRender(format, 'text_rect_right_aligned', text=['test'],
alignment=QgsTextRenderer.AlignRight, rect=QRectF(100, 100, 200, 100))

def testDrawTextRectMultilineBottomAlign(self):
format = QgsTextFormat()
format.setFont(getTestFont('bold'))
format.setSize(30)
format.setSizeUnit(QgsUnitTypes.RenderPoints)

assert self.checkRender(format, 'text_rect_multiline_bottom_aligned', text=['test', 'bottom', 'aligned'],
alignment=QgsTextRenderer.AlignLeft, rect=QRectF(100, 100, 200, 100), vAlignment=QgsTextRenderer.AlignBottom)

def testDrawTextRectBottomAlign(self):
format = QgsTextFormat()
format.setFont(getTestFont('bold'))
format.setSize(30)
format.setSizeUnit(QgsUnitTypes.RenderPoints)

assert self.checkRender(format, 'text_rect_bottom_aligned', text=['bottom aligned'],
alignment=QgsTextRenderer.AlignLeft, rect=QRectF(100, 100, 200, 100), vAlignment=QgsTextRenderer.AlignBottom)

def testDrawTextRectMultilineVCenterAlign(self):
format = QgsTextFormat()
format.setFont(getTestFont('bold'))
format.setSize(30)
format.setSizeUnit(QgsUnitTypes.RenderPoints)

assert self.checkRender(format, 'text_rect_multiline_vcenter_aligned', text=['test', 'center', 'aligned'],
alignment=QgsTextRenderer.AlignLeft, rect=QRectF(100, 100, 200, 100), vAlignment=QgsTextRenderer.AlignVCenter)

def testDrawTextRectVCenterAlign(self):
format = QgsTextFormat()
format.setFont(getTestFont('bold'))
format.setSize(30)
format.setSizeUnit(QgsUnitTypes.RenderPoints)

assert self.checkRender(format, 'text_rect_vcenter_aligned', text=['center aligned'],
alignment=QgsTextRenderer.AlignLeft, rect=QRectF(100, 100, 200, 100), vAlignment=QgsTextRenderer.AlignVCenter)

def testDrawTextRectMultilineCenterAlign(self):
format = QgsTextFormat()
format.setFont(getTestFont('bold'))
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

0 comments on commit a49cb7c

Please sign in to comment.
You can’t perform that action at this time.