Skip to content

Commit

Permalink
Add more efficient method to draw document when a QgsTextDocument is …
Browse files Browse the repository at this point in the history
…already available
  • Loading branch information
nyalldawson committed Nov 24, 2022
1 parent a10b971 commit cfbf5ef
Show file tree
Hide file tree
Showing 7 changed files with 216 additions and 9 deletions.
38 changes: 38 additions & 0 deletions python/core/auto_generated/textrenderer/qgstextrenderer.sip.in
Expand Up @@ -77,6 +77,44 @@ Draws text within a rectangle using the specified settings.
:param vAlignment: vertical alignment (since QGIS 3.16)
:param flags: text rendering flags (since QGIS 3.24)
:param mode: text layout mode. Only Qgis.TextLayoutMode.Rectangle, Qgis.TextLayoutMode.RectangleCapHeightBased and Qgis.TextLayoutMode.RectangleAscentBased are accepted (since QGIS 3.30)

.. seealso:: :py:func:`drawDocument`
%End

static void drawDocument( const QRectF &rect,
const QgsTextFormat &format,
const QgsTextDocument &document,
const QgsTextDocumentMetrics &metrics,
QgsRenderContext &context,
Qgis::TextHorizontalAlignment horizontalAlignment = Qgis::TextHorizontalAlignment::Left,
Qgis::TextVerticalAlignment verticalAlignment = Qgis::TextVerticalAlignment::Top,
double rotation = 0,
Qgis::TextLayoutMode mode = Qgis::TextLayoutMode::Rectangle,
Qgis::TextRendererFlags flags = Qgis::TextRendererFlags() );
%Docstring
Draws a text document within a rectangle using the specified settings.

Calling this method is more efficient than calling :py:func:`~QgsTextRenderer.drawText` if the text document and metrics have already
been calculated.

.. warning::

Unlike :py:func:`~QgsTextRenderer.drawText`, this method does not automatically update data defined properties in the text ``format``. This
is the caller's responsibility to do, and must be done prior to generating the text ``document`` and ``metrics``.

:param rect: destination rectangle for text
:param format: base text format
:param document: text document to draw
:param metrics: precalculated text metrics
:param context: destination render context
:param horizontalAlignment: horizontal alignment
:param verticalAlignment: vertical alignment
:param rotation: text rotation
:param mode: text layout mode. Only Qgis.TextLayoutMode.Rectangle, Qgis.TextLayoutMode.RectangleCapHeightBased and Qgis.TextLayoutMode.RectangleAscentBased are accepted.
:param flags: text rendering flags


.. versionadded:: 3.30
%End

static void drawText( QPointF point, double rotation, Qgis::TextHorizontalAlignment alignment, const QStringList &textLines,
Expand Down
17 changes: 11 additions & 6 deletions src/core/textrenderer/qgstextrenderer.cpp
Expand Up @@ -105,17 +105,22 @@ void QgsTextRenderer::drawText( const QRectF &rect, double rotation, Qgis::TextH
const double fontScale = calculateScaleFactorForFormat( context, format );
const QgsTextDocumentMetrics metrics = QgsTextDocumentMetrics::calculateMetrics( document, format, context, fontScale );

if ( tmpFormat.background().enabled() )
drawDocument( rect, tmpFormat, document, metrics, context, alignment, vAlignment, rotation, mode, flags );
}

void QgsTextRenderer::drawDocument( const QRectF &rect, const QgsTextFormat &format, const QgsTextDocument &document, const QgsTextDocumentMetrics &metrics, QgsRenderContext &context, Qgis::TextHorizontalAlignment horizontalAlignment, Qgis::TextVerticalAlignment verticalAlignment, double rotation, Qgis::TextLayoutMode mode, Qgis::TextRendererFlags )
{
if ( format.background().enabled() )
{
drawPart( rect, rotation, alignment, vAlignment, document, metrics, context, tmpFormat, Qgis::TextComponent::Background, Qgis::TextLayoutMode::Rectangle );
drawPart( rect, rotation, horizontalAlignment, verticalAlignment, document, metrics, context, format, Qgis::TextComponent::Background, mode );
}

if ( tmpFormat.buffer().enabled() )
if ( format.buffer().enabled() )
{
drawPart( rect, rotation, alignment, vAlignment, document, metrics, context, tmpFormat, Qgis::TextComponent::Buffer, Qgis::TextLayoutMode::Rectangle );
drawPart( rect, rotation, horizontalAlignment, verticalAlignment, document, metrics, context, format, Qgis::TextComponent::Buffer, mode );
}

drawPart( rect, rotation, alignment, vAlignment, document, metrics, context, tmpFormat, Qgis::TextComponent::Text, Qgis::TextLayoutMode::Rectangle );
drawPart( rect, rotation, horizontalAlignment, verticalAlignment, document, metrics, context, format, Qgis::TextComponent::Text, mode );
}

void QgsTextRenderer::drawText( QPointF point, double rotation, Qgis::TextHorizontalAlignment alignment, const QStringList &textLines, QgsRenderContext &context, const QgsTextFormat &format, bool )
Expand Down Expand Up @@ -241,7 +246,7 @@ void QgsTextRenderer::drawPart( const QRectF &rect, double rotation, Qgis::TextH
{
drawTextInternal( part, context, format, component,
document, metrics,
alignment, vAlignment );
alignment, vAlignment, mode );
break;
}
}
Expand Down
38 changes: 37 additions & 1 deletion src/core/textrenderer/qgstextrenderer.h
Expand Up @@ -86,11 +86,47 @@ class CORE_EXPORT QgsTextRenderer
* \param vAlignment vertical alignment (since QGIS 3.16)
* \param flags text rendering flags (since QGIS 3.24)
* \param mode text layout mode. Only Qgis::TextLayoutMode::Rectangle, Qgis::TextLayoutMode::RectangleCapHeightBased and Qgis::TextLayoutMode::RectangleAscentBased are accepted (since QGIS 3.30)
*
* \see drawDocument(), which is more efficient if the text document and metrics have already been calculated.
*/
static void drawText( const QRectF &rect, double rotation, Qgis::TextHorizontalAlignment alignment, const QStringList &textLines,
QgsRenderContext &context, const QgsTextFormat &format,
bool drawAsOutlines = true, Qgis::TextVerticalAlignment vAlignment = Qgis::TextVerticalAlignment::Top,
Qgis::TextRendererFlags flags = Qgis::TextRendererFlags() );
Qgis::TextRendererFlags flags = Qgis::TextRendererFlags(),
Qgis::TextLayoutMode mode = Qgis::TextLayoutMode::Rectangle );

/**
* Draws a text document within a rectangle using the specified settings.
*
* Calling this method is more efficient than calling drawText() if the text document and metrics have already
* been calculated.
*
* \warning Unlike drawText(), this method does not automatically update data defined properties in the text \a format. This
* is the caller's responsibility to do, and must be done prior to generating the text \a document and \a metrics.
*
* \param rect destination rectangle for text
* \param format base text format
* \param document text document to draw
* \param metrics precalculated text metrics
* \param context destination render context
* \param horizontalAlignment horizontal alignment
* \param verticalAlignment vertical alignment
* \param rotation text rotation
* \param mode text layout mode. Only Qgis::TextLayoutMode::Rectangle, Qgis::TextLayoutMode::RectangleCapHeightBased and Qgis::TextLayoutMode::RectangleAscentBased are accepted.
* \param flags text rendering flags
*
* \since QGIS 3.30
*/
static void drawDocument( const QRectF &rect,
const QgsTextFormat &format,
const QgsTextDocument &document,
const QgsTextDocumentMetrics &metrics,
QgsRenderContext &context,
Qgis::TextHorizontalAlignment horizontalAlignment = Qgis::TextHorizontalAlignment::Left,
Qgis::TextVerticalAlignment verticalAlignment = Qgis::TextVerticalAlignment::Top,
double rotation = 0,
Qgis::TextLayoutMode mode = Qgis::TextLayoutMode::Rectangle,
Qgis::TextRendererFlags flags = Qgis::TextRendererFlags() );

/**
* Draws text at a point origin using the specified settings.
Expand Down
132 changes: 130 additions & 2 deletions tests/src/python/test_qgstextrenderer.py
Expand Up @@ -41,7 +41,9 @@
QgsFontUtils,
QgsSymbolLayerId,
QgsSymbolLayerReference,
QgsStringUtils)
QgsStringUtils,
QgsTextDocument,
QgsTextDocumentMetrics)
from qgis.testing import unittest, start_app

from utilities import getTestFont, svgSymbolsPath
Expand Down Expand Up @@ -1597,14 +1599,56 @@ def testDrawMassiveFont(self):

def testDrawRectMixedHtml(self):
"""
Test drawing text in rect mode with cap height based line heights
Test drawing text in rect mode with mixed html fonts
"""
format = QgsTextFormat()
format.setFont(getTestFont('bold'))
format.setAllowHtmlFormatting(True)
format.setSize(30)
assert self.checkRender(format, 'rect_html', rect=QRectF(100, 100, 100, 100), text=['first <span style="font-size:50pt">line</span>', 'second <span style="font-size:50pt">line</span>', 'third line'])

def testDrawDocumentRect(self):
"""
Test drawing text document in rect mode
"""
format = QgsTextFormat()
format.setFont(getTestFont('bold'))
format.setAllowHtmlFormatting(True)
format.setSize(30)

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

painter = QPainter()
ms = QgsMapSettings()
ms.setExtent(QgsRectangle(0, 0, 50, 50))
ms.setOutputSize(image.size())
context = QgsRenderContext.fromMapSettings(ms)
context.setPainter(painter)
context.setScaleFactor(96 / 25.4) # 96 DPI
context.setFlag(QgsRenderContext.ApplyScalingWorkaroundForTextRendering, True)

painter.begin(image)
painter.setRenderHint(QPainter.Antialiasing)
image.fill(QColor(152, 219, 249))

painter.setBrush(QBrush(QColor(182, 239, 255)))
painter.setPen(Qt.NoPen)

doc = QgsTextDocument.fromHtml(['first <span style="font-size:50pt">line</span>', 'second <span style="font-size:50pt">line</span>', 'third line'])

metrics = QgsTextDocumentMetrics.calculateMetrics(doc, format, context, QgsTextRenderer.FONT_WORKAROUND_SCALE)

QgsTextRenderer.drawDocument(QRectF(100, 100, 100, 100),
format,
doc,
metrics,
context,
mode=Qgis.TextLayoutMode.Rectangle)

painter.end()

self.assertTrue(self.imageCheck('draw_document_rect', 'draw_document_rect', image))

def testDrawRectCapHeightMode(self):
"""
Test drawing text in rect mode with cap height based line heights
Expand All @@ -1624,6 +1668,48 @@ def testDrawRectCapHeightModeMixedHtml(self):
format.setSize(30)
assert self.checkRender(format, 'rect_cap_height_mode_html', rect=QRectF(100, 100, 100, 100), text=['first <span style="font-size:50pt">line</span>', 'second <span style="font-size:50pt">line</span>', 'third line'], mode=Qgis.TextLayoutMode.RectangleCapHeightBased)

def testDrawDocumentRectCapHeightMode(self):
"""
Test drawing text document in rect cap height mode
"""
format = QgsTextFormat()
format.setFont(getTestFont('bold'))
format.setAllowHtmlFormatting(True)
format.setSize(30)

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

painter = QPainter()
ms = QgsMapSettings()
ms.setExtent(QgsRectangle(0, 0, 50, 50))
ms.setOutputSize(image.size())
context = QgsRenderContext.fromMapSettings(ms)
context.setPainter(painter)
context.setScaleFactor(96 / 25.4) # 96 DPI
context.setFlag(QgsRenderContext.ApplyScalingWorkaroundForTextRendering, True)

painter.begin(image)
painter.setRenderHint(QPainter.Antialiasing)
image.fill(QColor(152, 219, 249))

painter.setBrush(QBrush(QColor(182, 239, 255)))
painter.setPen(Qt.NoPen)

doc = QgsTextDocument.fromHtml(['first <span style="font-size:50pt">line</span>', 'second <span style="font-size:50pt">line</span>', 'third line'])

metrics = QgsTextDocumentMetrics.calculateMetrics(doc, format, context, QgsTextRenderer.FONT_WORKAROUND_SCALE)

QgsTextRenderer.drawDocument(QRectF(100, 100, 100, 100),
format,
doc,
metrics,
context,
mode=Qgis.TextLayoutMode.RectangleCapHeightBased)

painter.end()

self.assertTrue(self.imageCheck('draw_document_rect_cap_height', 'draw_document_rect_cap_height', image))

def testDrawRectAscentMode(self):
"""
Test drawing text in rect mode with cap height based line heights
Expand All @@ -1643,6 +1729,48 @@ def testDrawRectAscentModeMixedHtml(self):
format.setSize(30)
assert self.checkRender(format, 'rect_ascent_mode_html', rect=QRectF(100, 100, 100, 100), text=['first <span style="font-size:50pt">line</span>', 'second <span style="font-size:50pt">line</span>', 'third line'], mode=Qgis.TextLayoutMode.RectangleAscentBased)

def testDrawDocumentRectAscentMode(self):
"""
Test drawing text document in rect ascent mode
"""
format = QgsTextFormat()
format.setFont(getTestFont('bold'))
format.setAllowHtmlFormatting(True)
format.setSize(30)

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

painter = QPainter()
ms = QgsMapSettings()
ms.setExtent(QgsRectangle(0, 0, 50, 50))
ms.setOutputSize(image.size())
context = QgsRenderContext.fromMapSettings(ms)
context.setPainter(painter)
context.setScaleFactor(96 / 25.4) # 96 DPI
context.setFlag(QgsRenderContext.ApplyScalingWorkaroundForTextRendering, True)

painter.begin(image)
painter.setRenderHint(QPainter.Antialiasing)
image.fill(QColor(152, 219, 249))

painter.setBrush(QBrush(QColor(182, 239, 255)))
painter.setPen(Qt.NoPen)

doc = QgsTextDocument.fromHtml(['first <span style="font-size:50pt">line</span>', 'second <span style="font-size:50pt">line</span>', 'third line'])

metrics = QgsTextDocumentMetrics.calculateMetrics(doc, format, context, QgsTextRenderer.FONT_WORKAROUND_SCALE)

QgsTextRenderer.drawDocument(QRectF(100, 100, 100, 100),
format,
doc,
metrics,
context,
mode=Qgis.TextLayoutMode.RectangleAscentBased)

painter.end()

self.assertTrue(self.imageCheck('draw_document_rect_ascent', 'draw_document_rect_ascent', image))

def testDrawForcedItalic(self):
"""
Test drawing with forced italic
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.

0 comments on commit cfbf5ef

Please sign in to comment.