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
  • Loading branch information
nyalldawson committed Dec 1, 2021
1 parent e4e2451 commit fa5c53e
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 11 deletions.
34 changes: 27 additions & 7 deletions src/core/textrenderer/qgstextrenderer.cpp
Expand Up @@ -307,7 +307,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 @@ -464,7 +464,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 @@ -555,7 +555,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 @@ -639,7 +639,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 @@ -754,7 +754,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 @@ -881,7 +881,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 @@ -1409,7 +1409,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 @@ -2071,3 +2071,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 @@ -401,6 +401,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
22 changes: 18 additions & 4 deletions tests/src/python/test_qgstextrenderer.py
Expand Up @@ -1438,9 +1438,10 @@ def checkRender(self, format, name, part=None, angle=0, alignment=QgsTextRendere
text=['test'],
rect=QRectF(100, 100, 50, 250),
vAlignment=QgsTextRenderer.AlignTop,
flags=Qgis.TextRendererFlags()):
flags=Qgis.TextRendererFlags(),
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 @@ -1492,8 +1493,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 @@ -1536,6 +1538,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)

@unittest.skipIf(int(QT_VERSION_STR.split('.')[0]) < 6 or (int(QT_VERSION_STR.split('.')[0]) == 6 and int(QT_VERSION_STR.split('.')[1]) < 3), 'Too old Qt')
def testDrawSmallCaps(self):
format = QgsTextFormat()
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 fa5c53e

Please sign in to comment.