Skip to content

Commit

Permalink
[api] Add vertical alignment control to QgsTextRenderer
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Jul 7, 2020
1 parent 59c53ba commit a49cb7c
Show file tree
Hide file tree
Showing 9 changed files with 94 additions and 15 deletions.
10 changes: 9 additions & 1 deletion python/core/auto_generated/textrenderer/qgstextrenderer.sip.in
Expand Up @@ -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)
Expand All @@ -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.

Expand All @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion src/core/labeling/qgsvectorlayerlabelprovider.cpp
Expand Up @@ -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() )
Expand Down
38 changes: 29 additions & 9 deletions src/core/textrenderer/qgstextrenderer.cpp
Expand Up @@ -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
Expand All @@ -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 )
Expand Down Expand Up @@ -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() )
{
Expand Down Expand Up @@ -171,7 +171,7 @@ void QgsTextRenderer::drawPart( const QRectF &rect, double rotation, QgsTextRend
drawTextInternal( part, context, format, component,
document,
nullptr,
alignment );
alignment, vAlignment );
break;
}
}
Expand Down Expand Up @@ -219,7 +219,7 @@ void QgsTextRenderer::drawPart( QPointF origin, double rotation, QgsTextRenderer
drawTextInternal( part, context, format, component,
document,
nullptr,
alignment,
alignment, AlignTop,
Point );
break;
}
Expand Down Expand Up @@ -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() )
{
Expand Down Expand Up @@ -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 );
Expand Down
18 changes: 16 additions & 2 deletions src/core/textrenderer/qgstextrenderer.h
Expand Up @@ -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
Expand All @@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -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 );

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

friend class QgsVectorLayerLabelProvider;
Expand Down
41 changes: 39 additions & 2 deletions tests/src/python/test_qgstextrenderer.py
Expand Up @@ -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)

Expand Down Expand Up @@ -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)))
Expand Down Expand Up @@ -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'))
Expand Down
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit a49cb7c

Please sign in to comment.