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 000000000000..4b7955744af3 Binary files /dev/null and b/tests/testdata/control_images/symbol_fontmarker/expected_fontmarker_largesize/expected_fontmarker_largesize.png differ