Skip to content
Permalink
Browse files
Don't try to render font marker symbols in massive font sizes
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
  • Loading branch information
nyalldawson committed May 29, 2021
1 parent 2983284 commit 12d1078a8bc11caef7002629e6315cf50522e732
@@ -41,6 +41,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.
@@ -3266,8 +3268,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 ) ) ) );
@@ -3489,6 +3503,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 ) );
@@ -3598,6 +3615,8 @@ QRectF QgsFontMarkerSymbolLayer::bounds( QPointF point, QgsSymbolRenderContext &
{
chrWidth *= scaledSize / mOrigSize;
}
if ( !qgsDoubleNear( mFontSizeScale, 1.0 ) )
chrWidth *= mFontSizeScale;

bool hasDataDefinedRotation = false;
QPointF offset;
@@ -1065,6 +1065,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;
@@ -62,6 +62,7 @@ class TestQgsFontMarkerSymbol : public QObject
void fontMarkerSymbolDataDefinedProperties();
void opacityWithDataDefinedColor();
void dataDefinedOpacity();
void massiveFont();

private:
bool mTestHasError = false ;
@@ -241,6 +242,26 @@ void TestQgsFontMarkerSymbol::dataDefinedOpacity()
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
//
Binary file not shown.

0 comments on commit 12d1078

Please sign in to comment.