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

(cherry picked from commit a87e3d2)
  • Loading branch information
nyalldawson committed Jun 14, 2021
1 parent 0212796 commit ba29f22a6817460a5d2d1be04ca2ae7d34b08760
@@ -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.
@@ -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 ) ) ) );
@@ -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 ) );
@@ -3514,6 +3531,7 @@ QRectF QgsFontMarkerSymbolLayer::bounds( QPointF point, QgsSymbolRenderContext &
{
chrWidth *= scaledSize / mOrigSize;
}
chrWidth *= mFontSizeScale;

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

private:
bool mTestHasError = false ;
@@ -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
//
Binary file not shown.

0 comments on commit ba29f22

Please sign in to comment.