Skip to content

Commit

Permalink
Don't try to render font marker symbols in massive font sizes
Browse files Browse the repository at this point in the history
Instead, scale the painter after the font exceeds a certain threshold.
The end result looks the same, but should avoid issues like the
crash described in #42270

Fixes #42270

(cherry picked from commit a87e3d2)
  • Loading branch information
nyalldawson committed May 31, 2021
1 parent 665d40d commit cd8d418
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 1 deletion.
20 changes: 19 additions & 1 deletion src/core/symbology/qgsmarkersymbollayer.cpp
Expand Up @@ -39,6 +39,8 @@
Q_GUI_EXPORT extern int qt_defaultDpiX();
Q_GUI_EXPORT extern int qt_defaultDpiY();

static constexpr int MAX_FONT_CHARACTER_SIZE_IN_PIXELS = 500;

static void _fixQPictureDPI( QPainter *p )
{
// QPicture makes an assumption that we drawing to it with system DPI.
Expand Down Expand Up @@ -3174,8 +3176,20 @@ void QgsFontMarkerSymbolLayer::startRender( QgsSymbolRenderContext &context )
mFont.setStyleName( QgsFontUtils::translateNamedStyle( mFontStyle ) );
}

const double sizePixels = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
double sizePixels = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
mNonZeroFontSize = !qgsDoubleNear( sizePixels, 0.0 );

if ( mNonZeroFontSize && sizePixels > MAX_FONT_CHARACTER_SIZE_IN_PIXELS )
{
// if font is too large (e.g using map units and map is very zoomed in), then we limit
// the font size and instead scale up the painter.
// this avoids issues with massive font sizes (eg https://github.com/qgis/QGIS/issues/42270)
mFontSizeScale = sizePixels / MAX_FONT_CHARACTER_SIZE_IN_PIXELS;
sizePixels = MAX_FONT_CHARACTER_SIZE_IN_PIXELS;
}
else
mFontSizeScale = 1.0;

// if a non zero, but small pixel size results, round up to 2 pixels so that a "dot" is at least visible
// (if we set a <=1 pixel size here Qt will reset the font to a default size, leading to much too large symbols)
mFont.setPixelSize( std::max( 2, static_cast< int >( std::round( sizePixels ) ) ) );
Expand Down Expand Up @@ -3405,6 +3419,9 @@ void QgsFontMarkerSymbolLayer::renderPoint( QPointF point, QgsSymbolRenderContex
transform.scale( s, s );
}

if ( !qgsDoubleNear( mFontSizeScale, 1.0 ) )
transform.scale( mFontSizeScale, mFontSizeScale );

if ( mUseCachedPath )
{
p->drawPath( transform.map( mCachedPath ) );
Expand Down Expand Up @@ -3514,6 +3531,7 @@ QRectF QgsFontMarkerSymbolLayer::bounds( QPointF point, QgsSymbolRenderContext &
{
chrWidth *= scaledSize / mOrigSize;
}
chrWidth *= mFontSizeScale;

bool hasDataDefinedRotation = false;
QPointF offset;
Expand Down
2 changes: 2 additions & 0 deletions src/core/symbology/qgsmarkersymbollayer.h
Expand Up @@ -996,6 +996,8 @@ class CORE_EXPORT QgsFontMarkerSymbolLayer : public QgsMarkerSymbolLayer

double mChrWidth = 0;
QPointF mChrOffset;
//! Scaling for font sizes, used if font size grows too large
double mFontSizeScale = 1.0;
double mOrigSize;

QColor mStrokeColor;
Expand Down
21 changes: 21 additions & 0 deletions tests/src/core/testqgsfontmarker.cpp
Expand Up @@ -60,6 +60,7 @@ class TestQgsFontMarkerSymbol : public QObject
void bounds();
void fontMarkerSymbolDataDefinedProperties();
void opacityWithDataDefinedColor();
void massiveFont();

private:
bool mTestHasError = false ;
Expand Down Expand Up @@ -219,6 +220,26 @@ void TestQgsFontMarkerSymbol::opacityWithDataDefinedColor()
QVERIFY( result );
}

void TestQgsFontMarkerSymbol::massiveFont()
{
// test rendering a massive font
mFontMarkerLayer->setColor( QColor( 0, 0, 0, 100 ) );
mFontMarkerLayer->setStrokeColor( QColor( 0, 0, 0, 0 ) );
QFont font = QgsFontUtils::getStandardTestFont( QStringLiteral( "Bold" ) );
mFontMarkerLayer->setFontFamily( font.family() );
mFontMarkerLayer->setDataDefinedProperties( QgsPropertyCollection() );
mFontMarkerLayer->setCharacter( QChar( 'X' ) );
mFontMarkerLayer->setSize( 200 );
mFontMarkerLayer->setSizeUnit( QgsUnitTypes::RenderMillimeters );
mFontMarkerLayer->setDataDefinedProperty( QgsSymbolLayer::PropertySize, QgsProperty::fromExpression( QStringLiteral( "if(importance > 2, 100, 350)" ) ) );
mFontMarkerLayer->setDataDefinedProperty( QgsSymbolLayer::PropertyLayerEnabled, QgsProperty::fromExpression( QStringLiteral( "$id in (1, 4)" ) ) ); // 3
mFontMarkerLayer->setStrokeWidth( 0.5 );

bool result = imageCheck( QStringLiteral( "fontmarker_largesize" ) );
mFontMarkerLayer->setDataDefinedProperties( QgsPropertyCollection() );
QVERIFY( result );
}

//
// Private helper functions not called directly by CTest
//
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 cd8d418

Please sign in to comment.