Skip to content

Commit

Permalink
Workaround qt bug which causes word spacing to shrink to nothing
Browse files Browse the repository at this point in the history
when very large font sizes are rendered

Works around https://bugreports.qt.io/browse/QTBUG-98778

(cherry picked from commit fa5c53e)
  • Loading branch information
nyalldawson committed Dec 7, 2021
1 parent 5d82289 commit 5297a96
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 12 deletions.
34 changes: 27 additions & 7 deletions src/core/textrenderer/qgstextrenderer.cpp
Expand Up @@ -294,7 +294,7 @@ double QgsTextRenderer::drawBuffer( QgsRenderContext &context, const QgsTextRend

const double penSize = context.convertToPainterUnits( buffer.size(), buffer.sizeUnit(), buffer.sizeMapUnitScale() );

const double scaleFactor = ( context.flags() & Qgis::RenderContextFlag::ApplyScalingWorkaroundForTextRendering ) ? FONT_WORKAROUND_SCALE : 1.0;
const double scaleFactor = calculateScaleFactorForFormat( context, format );

std::optional< QgsScopedRenderContextReferenceScaleOverride > referenceScaleOverride;
if ( mode == Label )
Expand Down Expand Up @@ -451,7 +451,7 @@ void QgsTextRenderer::drawMask( QgsRenderContext &context, const QgsTextRenderer
QPainterPath path;
path.setFillRule( Qt::WindingFill );

const double scaleFactor = ( context.flags() & Qgis::RenderContextFlag::ApplyScalingWorkaroundForTextRendering ) ? FONT_WORKAROUND_SCALE : 1.0;
const double scaleFactor = calculateScaleFactorForFormat( context, format );

// TODO: vertical text mode was ignored when masking feature was added.
// Hopefully Oslandia come back and fix this? Hint hint...
Expand Down Expand Up @@ -542,7 +542,7 @@ double QgsTextRenderer::textWidth( const QgsRenderContext &context, const QgsTex
double QgsTextRenderer::textWidth( const QgsRenderContext &context, const QgsTextFormat &format, const QgsTextDocument &document )
{
//calculate max width of text lines
const double scaleFactor = ( context.flags() & Qgis::RenderContextFlag::ApplyScalingWorkaroundForTextRendering ) ? FONT_WORKAROUND_SCALE : 1.0;
const double scaleFactor = calculateScaleFactorForFormat( context, format );

bool isNullSize = false;
const QFont baseFont = format.scaledFont( context, scaleFactor, &isNullSize );
Expand Down Expand Up @@ -613,7 +613,7 @@ double QgsTextRenderer::textHeight( const QgsRenderContext &context, const QgsTe

double QgsTextRenderer::textHeight( const QgsRenderContext &context, const QgsTextFormat &format, QChar character, bool includeEffects )
{
const double scaleFactor = ( context.flags() & Qgis::RenderContextFlag::ApplyScalingWorkaroundForTextRendering ) ? FONT_WORKAROUND_SCALE : 1.0;
const double scaleFactor = calculateScaleFactorForFormat( context, format );
bool isNullSize = false;
const QFont baseFont = format.scaledFont( context, scaleFactor, &isNullSize );
if ( isNullSize )
Expand Down Expand Up @@ -654,7 +654,7 @@ double QgsTextRenderer::textHeight( const QgsRenderContext &context, const QgsTe
document.applyCapitalization( format.capitalization() );

//calculate max height of text lines
const double scaleFactor = ( context.flags() & Qgis::RenderContextFlag::ApplyScalingWorkaroundForTextRendering ) ? FONT_WORKAROUND_SCALE : 1.0;
const double scaleFactor = calculateScaleFactorForFormat( context, format );

bool isNullSize = false;
const QFont baseFont = format.scaledFont( context, scaleFactor, &isNullSize );
Expand Down Expand Up @@ -781,7 +781,7 @@ void QgsTextRenderer::drawBackground( QgsRenderContext &context, QgsTextRenderer
component.rotationOffset = background.rotation();
}

const double scaleFactor = ( context.flags() & Qgis::RenderContextFlag::ApplyScalingWorkaroundForTextRendering ) ? FONT_WORKAROUND_SCALE : 1;
const double scaleFactor = calculateScaleFactorForFormat( context, format );

if ( mode != Label )
{
Expand Down Expand Up @@ -1307,7 +1307,7 @@ void QgsTextRenderer::drawTextInternal( TextPart drawType,
std::unique_ptr< QFontMetricsF > tmpMetrics;
if ( !fontMetrics )
{
fontScale = ( context.flags() & Qgis::RenderContextFlag::ApplyScalingWorkaroundForTextRendering ) ? FONT_WORKAROUND_SCALE : 1.0;
fontScale = calculateScaleFactorForFormat( context, format );

std::optional< QgsScopedRenderContextReferenceScaleOverride > referenceScaleOverride;
if ( mode == Label )
Expand Down Expand Up @@ -1969,3 +1969,23 @@ void QgsTextRenderer::drawTextInternalVertical( QgsRenderContext &context, const
}
}

double QgsTextRenderer::calculateScaleFactorForFormat( const QgsRenderContext &context, const QgsTextFormat &format )
{
if ( !( context.flags() & Qgis::RenderContextFlag::ApplyScalingWorkaroundForTextRendering ) )
return 1.0;

const double pixelSize = context.convertToPainterUnits( format.size(), format.sizeUnit(), format.sizeMapUnitScale() );

// THESE THRESHOLD MAY NEED TWEAKING!

// for small font sizes we need to apply a growth scaling workaround designed to stablise the rendering of small font sizes
if ( pixelSize < 50 )
return FONT_WORKAROUND_SCALE;
//... but for font sizes we might run into https://bugreports.qt.io/browse/QTBUG-98778, which messes up the spacing between words for large fonts!
// so instead we scale down the painter so that we render the text at 200 pixel size and let painter scaling handle making it the correct size
else if ( pixelSize > 200 )
return 200 / pixelSize;
else
return 1.0;
}

2 changes: 2 additions & 0 deletions src/core/textrenderer/qgstextrenderer.h
Expand Up @@ -381,6 +381,8 @@ class CORE_EXPORT QgsTextRenderer
VAlignment vAlignment,
double rotation );

static double calculateScaleFactorForFormat( const QgsRenderContext &context, const QgsTextFormat &format );

friend class QgsVectorLayerLabelProvider;
friend class QgsLabelPreview;

Expand Down
25 changes: 20 additions & 5 deletions tests/src/python/test_qgstextrenderer.py
Expand Up @@ -17,7 +17,8 @@
from qgis.PyQt.QtCore import (Qt, QSizeF, QPointF, QRectF, QDir, QSize)
from qgis.PyQt.QtGui import (QColor, QPainter, QFont, QImage, QBrush, QPen)
from qgis.PyQt.QtXml import QDomDocument
from qgis.core import (QgsTextBufferSettings,
from qgis.core import (Qgis,
QgsTextBufferSettings,
QgsTextMaskSettings,
QgsTextBackgroundSettings,
QgsTextShadowSettings,
Expand Down Expand Up @@ -1408,9 +1409,10 @@ 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),
vAlignment=QgsTextRenderer.AlignTop):
vAlignment=QgsTextRenderer.AlignTop,
image_size=400):

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

painter = QPainter()
ms = QgsMapSettings()
Expand Down Expand Up @@ -1462,8 +1464,9 @@ def checkRender(self, format, name, part=None, angle=0, alignment=QgsTextRendere

def checkRenderPoint(self, format, name, part=None, angle=0, alignment=QgsTextRenderer.AlignLeft,
text=['test'],
point=QPointF(100, 200)):
image = QImage(400, 400, QImage.Format_RGB32)
point=QPointF(100, 200),
image_size=400):
image = QImage(image_size, image_size, QImage.Format_RGB32)

painter = QPainter()
ms = QgsMapSettings()
Expand Down Expand Up @@ -1506,6 +1509,18 @@ def checkRenderPoint(self, format, name, part=None, angle=0, alignment=QgsTextRe
painter.end()
return self.imageCheck(name, name, image)

def testDrawMassiveFont(self):
"""
Test that we aren't bitten by https://bugreports.qt.io/browse/QTBUG-98778
This test should pass when there's a correct WORD space between the 'a' and 't' characters, or fail when
the spacing between these characters is nill or close to a letter spacing
"""
format = QgsTextFormat()
format.setFont(getTestFont('bold'))
format.setSize(1100)
assert self.checkRender(format, 'massive_font', rect=QRectF(-800, -600, 1000, 1000), text=['a t'], image_size=800)

def testDrawBackgroundDisabled(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.

0 comments on commit 5297a96

Please sign in to comment.