From 10bea7e443151b56cbfa74e1e092a5cb5d8ce9f2 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 28 May 2021 13:25:22 +1000 Subject: [PATCH] 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 a87e3d2e45374ef4ad43449b72dc030e308821cd) --- src/core/symbology/qgsmarkersymbollayer.cpp | 20 ++++++++++++++++- src/core/symbology/qgsmarkersymbollayer.h | 2 ++ tests/src/core/testqgsfontmarker.cpp | 21 ++++++++++++++++++ .../expected_fontmarker_largesize.png | Bin 0 -> 641520 bytes 4 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 tests/testdata/control_images/symbol_fontmarker/expected_fontmarker_largesize/expected_fontmarker_largesize.png diff --git a/src/core/symbology/qgsmarkersymbollayer.cpp b/src/core/symbology/qgsmarkersymbollayer.cpp index fea1803009bc..feb442bb7e67 100644 --- a/src/core/symbology/qgsmarkersymbollayer.cpp +++ b/src/core/symbology/qgsmarkersymbollayer.cpp @@ -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; diff --git a/src/core/symbology/qgsmarkersymbollayer.h b/src/core/symbology/qgsmarkersymbollayer.h index 2f9adb28d92a..d092776c6830 100644 --- a/src/core/symbology/qgsmarkersymbollayer.h +++ b/src/core/symbology/qgsmarkersymbollayer.h @@ -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; diff --git a/tests/src/core/testqgsfontmarker.cpp b/tests/src/core/testqgsfontmarker.cpp index a865c111860b..e56bb1a494bb 100644 --- a/tests/src/core/testqgsfontmarker.cpp +++ b/tests/src/core/testqgsfontmarker.cpp @@ -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 // diff --git a/tests/testdata/control_images/symbol_fontmarker/expected_fontmarker_largesize/expected_fontmarker_largesize.png b/tests/testdata/control_images/symbol_fontmarker/expected_fontmarker_largesize/expected_fontmarker_largesize.png new file mode 100644 index 0000000000000000000000000000000000000000..4b7955744af34f03b7356eaf15958c80cb5e57a0 GIT binary patch literal 641520 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 literal 0 HcmV?d00001