Add more efficient method to draw document when a QgsTextDocument is already available

This commit is contained in:
Nyall Dawson 2022-11-22 11:58:35 +10:00
parent a10b971156
commit cfbf5ef51d
7 changed files with 216 additions and 9 deletions

View File

@ -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,

View File

@ -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 )
@ -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;
}
}

View File

@ -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.

View File

@ -41,7 +41,9 @@ from qgis.core import (Qgis,
QgsFontUtils,
QgsSymbolLayerId,
QgsSymbolLayerReference,
QgsStringUtils)
QgsStringUtils,
QgsTextDocument,
QgsTextDocumentMetrics)
from qgis.testing import unittest, start_app
from utilities import getTestFont, svgSymbolsPath
@ -1597,7 +1599,7 @@ class PyQgsTextRenderer(unittest.TestCase):
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'))
@ -1605,6 +1607,48 @@ class PyQgsTextRenderer(unittest.TestCase):
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
@ -1624,6 +1668,48 @@ class PyQgsTextRenderer(unittest.TestCase):
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
@ -1643,6 +1729,48 @@ class PyQgsTextRenderer(unittest.TestCase):
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

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB