From 730cd7e23a6c4dadadca18de6b5a0f2276e764b7 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 5 Nov 2021 12:27:59 +1000 Subject: [PATCH] [feature] Add "stretch" setting for labels and text formats Allows text to be horizontally stretched or condensed by a % factor. Handy for tweaking the widths of fonts to fit a bit of extra text into labels (when used sparingly, that is... you can certainly abuse font stretching with some horrendous results!) Requires Qt 6.3+ or KDE's 5.15 fork Thanks for KDAB for fixing the upstream issues blocking this! --- CMakeLists.txt | 1 + cmake_templates/qgsconfig.h.in | 1 + .../labeling/qgspallabeling.sip.in | 1 + .../textrenderer/qgstextformat.sip.in | 30 ++ src/core/labeling/qgspallabeling.cpp | 8 + src/core/labeling/qgspallabeling.h | 1 + src/core/textrenderer/qgstextformat.cpp | 23 + src/core/textrenderer/qgstextformat.h | 28 ++ src/gui/qgstextformatwidget.cpp | 14 + src/ui/qgstextformatwidgetbase.ui | 463 ++++++++++-------- tests/src/python/test_qgstextrenderer.py | 46 +- .../stretch_condense/stretch_condense.png | Bin 0 -> 3066 bytes .../stretch_expand/stretch_expand.png | Bin 0 -> 4070 bytes 13 files changed, 399 insertions(+), 217 deletions(-) create mode 100644 tests/testdata/control_images/text_renderer/stretch_condense/stretch_condense.png create mode 100644 tests/testdata/control_images/text_renderer/stretch_expand/stretch_expand.png diff --git a/CMakeLists.txt b/CMakeLists.txt index ae930d6c888..f1a9dc8c50a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -427,6 +427,7 @@ if(WITH_CORE) set(QT_VERSION_BASE "Qt5") set(HAS_KDE_QT5_PDF_TRANSFORM_FIX FALSE CACHE BOOL "Using KDE's Qt 5.15 fork with the PDF brush transform fix") set(HAS_KDE_QT5_SMALL_CAPS_FIX FALSE CACHE BOOL "Using KDE's Qt 5.15 fork with the QFont::SmallCaps fix") + set(HAS_KDE_QT5_FONT_STRETCH_FIX FALSE CACHE BOOL "Using KDE's Qt 5.15 fork with the QFont stretch fix") endif() # Use Qt5SerialPort optionally for GPS diff --git a/cmake_templates/qgsconfig.h.in b/cmake_templates/qgsconfig.h.in index c58524b00c6..e34c0ea9702 100644 --- a/cmake_templates/qgsconfig.h.in +++ b/cmake_templates/qgsconfig.h.in @@ -106,6 +106,7 @@ #cmakedefine HAS_KDE_QT5_PDF_TRANSFORM_FIX #cmakedefine HAS_KDE_QT5_SMALL_CAPS_FIX +#cmakedefine HAS_KDE_QT5_FONT_STRETCH_FIX #endif diff --git a/python/core/auto_generated/labeling/qgspallabeling.sip.in b/python/core/auto_generated/labeling/qgspallabeling.sip.in index efdeaf238e9..d1de565e6c6 100644 --- a/python/core/auto_generated/labeling/qgspallabeling.sip.in +++ b/python/core/auto_generated/labeling/qgspallabeling.sip.in @@ -135,6 +135,7 @@ Contains settings for how a map layer will be labeled. FontLetterSpacing, FontWordSpacing, FontBlendMode, + FontStretchFactor, // text formatting MultiLineWrapChar, diff --git a/python/core/auto_generated/textrenderer/qgstextformat.sip.in b/python/core/auto_generated/textrenderer/qgstextformat.sip.in index 1ddc66bd7f5..1bb25212a72 100644 --- a/python/core/auto_generated/textrenderer/qgstextformat.sip.in +++ b/python/core/auto_generated/textrenderer/qgstextformat.sip.in @@ -346,6 +346,36 @@ Sets the text's opacity. opaque) .. seealso:: :py:func:`opacity` +%End + + int stretchFactor() const; +%Docstring +Returns the text's stretch factor. + +The stretch factor matches a condensed or expanded version of the font or applies a stretch +transform that changes the width of all characters in the font by factor percent. + +For example, a factor of 150 results in all characters in the font being 1.5 times +(ie. 150%) wider. The minimum stretch factor is 1, and the maximum stretch factor is 4000. + +.. seealso:: :py:func:`setStretchFactor` + +.. versionadded:: 3.24 +%End + + void setStretchFactor( int factor ); +%Docstring +Sets the text's stretch ``factor``. + +The stretch factor matches a condensed or expanded version of the font or applies a stretch +transform that changes the width of all characters in the font by factor percent. + +For example, setting ``factor`` to 150 results in all characters in the font being 1.5 times +(ie. 150%) wider. The minimum stretch factor is 1, and the maximum stretch factor is 4000. + +.. seealso:: :py:func:`stretchFactor` + +.. versionadded:: 3.24 %End QPainter::CompositionMode blendMode() const; diff --git a/src/core/labeling/qgspallabeling.cpp b/src/core/labeling/qgspallabeling.cpp index b0b5a6239a2..f3cbfc8ef21 100644 --- a/src/core/labeling/qgspallabeling.cpp +++ b/src/core/labeling/qgspallabeling.cpp @@ -135,6 +135,7 @@ void QgsPalLayerSettings::initPropertyDefinitions() { QgsPalLayerSettings::FontSizeUnit, QgsPropertyDefinition( "FontSizeUnit", QObject::tr( "Font size units" ), QgsPropertyDefinition::RenderUnits, origin ) }, { QgsPalLayerSettings::FontTransp, QgsPropertyDefinition( "FontTransp", QObject::tr( "Text transparency" ), QgsPropertyDefinition::Opacity, origin ) }, { QgsPalLayerSettings::FontOpacity, QgsPropertyDefinition( "FontOpacity", QObject::tr( "Text opacity" ), QgsPropertyDefinition::Opacity, origin ) }, + { QgsPalLayerSettings::FontStretchFactor, QgsPropertyDefinition( "FontStretchFactor", QObject::tr( "Font stretch factor" ), QgsPropertyDefinition::IntegerPositiveGreaterZero, origin ) }, { QgsPalLayerSettings::FontCase, QgsPropertyDefinition( "FontCase", QgsPropertyDefinition::DataTypeString, QObject::tr( "Font case" ), QObject::tr( "string " ) + QStringLiteral( "[NoChange|Upper|
Lower|Title|Capitalize|SmallCaps|AllSmallCaps]" ), origin ) }, { QgsPalLayerSettings::FontLetterSpacing, QgsPropertyDefinition( "FontLetterSpacing", QObject::tr( "Letter spacing" ), QgsPropertyDefinition::Double, origin ) }, { QgsPalLayerSettings::FontWordSpacing, QgsPropertyDefinition( "FontWordSpacing", QObject::tr( "Word spacing" ), QgsPropertyDefinition::Double, origin ) }, @@ -3142,6 +3143,13 @@ void QgsPalLayerSettings::parseTextStyle( QFont &labelFont, labelFont.setStrikeOut( strikeout ); } + // data defined stretch + if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::FontStretchFactor ) ) + { + context.expressionContext().setOriginalValueVariable( mFormat.stretchFactor() ); + labelFont.setStretch( mDataDefinedProperties.valueAsInt( QgsPalLayerSettings::FontStretchFactor, context.expressionContext(), mFormat.stretchFactor() ) ); + } + // data defined underline font style? if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::Underline ) ) { diff --git a/src/core/labeling/qgspallabeling.h b/src/core/labeling/qgspallabeling.h index c092eac453b..b06d49edd56 100644 --- a/src/core/labeling/qgspallabeling.h +++ b/src/core/labeling/qgspallabeling.h @@ -238,6 +238,7 @@ class CORE_EXPORT QgsPalLayerSettings FontLetterSpacing = 28, //!< Letter spacing FontWordSpacing = 29, //!< Word spacing FontBlendMode = 30, //!< Text blend mode + FontStretchFactor = 113, //!< Font stretch factor, since QGIS 3.24 // text formatting MultiLineWrapChar = 31, diff --git a/src/core/textrenderer/qgstextformat.cpp b/src/core/textrenderer/qgstextformat.cpp index 79663a7cff4..77ccb559f3b 100644 --- a/src/core/textrenderer/qgstextformat.cpp +++ b/src/core/textrenderer/qgstextformat.cpp @@ -290,6 +290,17 @@ void QgsTextFormat::setOpacity( double opacity ) d->opacity = opacity; } +int QgsTextFormat::stretchFactor() const +{ + return d->textFont.stretch(); +} + +void QgsTextFormat::setStretchFactor( int factor ) +{ + d->isValid = true; + d->textFont.setStretch( factor ); +} + QPainter::CompositionMode QgsTextFormat::blendMode() const { return d->blendMode; @@ -551,6 +562,7 @@ void QgsTextFormat::readXml( const QDomElement &elem, const QgsReadWriteContext { d->opacity = ( textStyleElem.attribute( QStringLiteral( "textOpacity" ) ).toDouble() ); } + d->textFont.setStretch( textStyleElem.attribute( QStringLiteral( "stretchFactor" ), QStringLiteral( "100" ) ).toInt() ); d->orientation = QgsTextRendererUtils::decodeTextOrientation( textStyleElem.attribute( QStringLiteral( "textOrientation" ) ) ); d->previewBackgroundColor = QgsSymbolLayerUtils::decodeColor( textStyleElem.attribute( QStringLiteral( "previewBkgrdColor" ), QgsSymbolLayerUtils::encodeColor( Qt::white ) ) ); @@ -655,6 +667,7 @@ QDomElement QgsTextFormat::writeXml( QDomDocument &doc, const QgsReadWriteContex textStyleElem.setAttribute( QStringLiteral( "fontWordSpacing" ), d->textFont.wordSpacing() ); textStyleElem.setAttribute( QStringLiteral( "fontKerning" ), d->textFont.kerning() ); textStyleElem.setAttribute( QStringLiteral( "textOpacity" ), d->opacity ); + textStyleElem.setAttribute( QStringLiteral( "stretchFactor" ), d->textFont.stretch() ); textStyleElem.setAttribute( QStringLiteral( "textOrientation" ), QgsTextRendererUtils::encodeTextOrientation( d->orientation ) ); textStyleElem.setAttribute( QStringLiteral( "blendMode" ), QgsPainting::getBlendModeEnum( d->blendMode ) ); textStyleElem.setAttribute( QStringLiteral( "multilineHeight" ), d->multilineHeight ); @@ -964,6 +977,16 @@ void QgsTextFormat::updateDataDefinedProperties( QgsRenderContext &context ) } } + if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::FontStretchFactor ) ) + { + context.expressionContext().setOriginalValueVariable( d->textFont.stretch() ); + const QVariant val = d->mDataDefinedProperties.value( QgsPalLayerSettings::FontStretchFactor, context.expressionContext(), d->textFont.stretch() ); + if ( !val.isNull() ) + { + d->textFont.setStretch( val.toInt() ); + } + } + if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::TextOrientation ) ) { const QString encoded = QgsTextRendererUtils::encodeTextOrientation( d->orientation ); diff --git a/src/core/textrenderer/qgstextformat.h b/src/core/textrenderer/qgstextformat.h index 953e5ff92d1..446049d422f 100644 --- a/src/core/textrenderer/qgstextformat.h +++ b/src/core/textrenderer/qgstextformat.h @@ -324,6 +324,34 @@ class CORE_EXPORT QgsTextFormat */ void setOpacity( double opacity ); + /** + * Returns the text's stretch factor. + * + * The stretch factor matches a condensed or expanded version of the font or applies a stretch + * transform that changes the width of all characters in the font by factor percent. + * + * For example, a factor of 150 results in all characters in the font being 1.5 times + * (ie. 150%) wider. The minimum stretch factor is 1, and the maximum stretch factor is 4000. + * + * \see setStretchFactor() + * \since QGIS 3.24 + */ + int stretchFactor() const; + + /** + * Sets the text's stretch \a factor. + * + * The stretch factor matches a condensed or expanded version of the font or applies a stretch + * transform that changes the width of all characters in the font by factor percent. + * + * For example, setting \a factor to 150 results in all characters in the font being 1.5 times + * (ie. 150%) wider. The minimum stretch factor is 1, and the maximum stretch factor is 4000. + * + * \see stretchFactor() + * \since QGIS 3.24 + */ + void setStretchFactor( int factor ); + /** * Returns the blending mode used for drawing the text. * \see setBlendMode() diff --git a/src/gui/qgstextformatwidget.cpp b/src/gui/qgstextformatwidget.cpp index 66b38691734..92c78cce8d4 100644 --- a/src/gui/qgstextformatwidget.cpp +++ b/src/gui/qgstextformatwidget.cpp @@ -329,6 +329,12 @@ void QgsTextFormatWidget::initWidget() connect( mBackgroundEffectWidget, &QgsEffectStackCompactWidget::changed, this, &QgsTextFormatWidget::updatePreview ); mBackgroundEffectWidget->setPaintEffect( mBackgroundEffect.get() ); +#ifndef HAS_KDE_QT5_FONT_STRETCH_FIX + mLabelStretch->hide(); + mSpinStretch->hide(); + mFontStretchDDBtn->hide(); +#endif + setDockMode( false ); QList widgets; @@ -366,6 +372,7 @@ void QgsTextFormatWidget::initWidget() << mFontStyleComboBox << mTextOrientationComboBox << mTextOpacityWidget + << mSpinStretch << mFontWordSpacingSpinBox << mFormatNumChkBx << mFormatNumDecimalsSpnBx @@ -582,6 +589,10 @@ void QgsTextFormatWidget::toggleDDButtons( bool visible ) const auto buttons = findChildren< QgsPropertyOverrideButton * >(); for ( QgsPropertyOverrideButton *button : buttons ) { +#ifndef HAS_KDE_QT5_FONT_STRETCH_FIX + if ( button == mFontStretchDDBtn ) + continue; // always hidden +#endif button->setVisible( visible ); } } @@ -693,6 +704,7 @@ void QgsTextFormatWidget::populateDataDefinedButtons() registerDataDefinedButton( mFontLetterSpacingDDBtn, QgsPalLayerSettings::FontLetterSpacing ); registerDataDefinedButton( mFontWordSpacingDDBtn, QgsPalLayerSettings::FontWordSpacing ); registerDataDefinedButton( mFontBlendModeDDBtn, QgsPalLayerSettings::FontBlendMode ); + registerDataDefinedButton( mFontStretchDDBtn, QgsPalLayerSettings::FontStretchFactor ); // text formatting registerDataDefinedButton( mWrapCharDDBtn, QgsPalLayerSettings::MultiLineWrapChar ); @@ -887,6 +899,7 @@ void QgsTextFormatWidget::updateWidgetForFormat( const QgsTextFormat &format ) mRefFont = format.font(); mFontSizeSpinBox->setValue( format.size() ); btnTextColor->setColor( format.color() ); + whileBlocking( mSpinStretch )->setValue( format.stretchFactor() ); mTextOpacityWidget->setOpacity( format.opacity() ); comboBlendMode->setBlendMode( format.blendMode() ); mTextOrientationComboBox->setCurrentIndex( mTextOrientationComboBox->findData( format.orientation() ) ); @@ -1025,6 +1038,7 @@ QgsTextFormat QgsTextFormatWidget::format( bool includeDataDefinedProperties ) c format.setSize( mFontSizeSpinBox->value() ); format.setNamedStyle( mFontStyleComboBox->currentText() ); format.setOpacity( mTextOpacityWidget->opacity() ); + format.setStretchFactor( mSpinStretch->value() ); format.setBlendMode( comboBlendMode->blendMode() ); format.setSizeUnit( mFontSizeUnitWidget->unit() ); format.setSizeMapUnitScale( mFontSizeUnitWidget->getMapUnitScale() ); diff --git a/src/ui/qgstextformatwidgetbase.ui b/src/ui/qgstextformatwidgetbase.ui index f6a9375f07d..c0189c59579 100644 --- a/src/ui/qgstextformatwidgetbase.ui +++ b/src/ui/qgstextformatwidgetbase.ui @@ -109,7 +109,7 @@ 0 0 - 480 + 499 300 @@ -654,8 +654,8 @@ 0 0 - 455 - 414 + 485 + 429 @@ -1217,8 +1217,8 @@ font-style: italic; 0 0 - 427 - 931 + 471 + 742 @@ -1228,23 +1228,150 @@ font-style: italic; 6 - - + + + + true + - + 0 0 - - Type case + + + 16777215 + 16777215 + + + + Capitalization style of text - - + + + + + + + 0 + 0 + + + + word + + + + + + + + 0 + 0 + + + + Space in pixels or map units, relative to size unit choice + + + 4 + + + -1000.000000000000000 + + + 999999999.000000000000000 + + + 0.100000000000000 + + + true + + + + - + + + + Text orientation + + + + + + + + + + 0 + 0 + + + + letter + + + + + + + + 0 + 0 + + + + Space in pixels or map units, relative to size unit choice + + + 4 + + + -1000.000000000000000 + + + 999999999.000000000000000 + + + 0.100000000000000 + + + true + + + + + + + + + Spacing + + + + + + + Blend mode + + + + + + + If enabled, the label text will automatically be modified using a preset list of substitutes + + + Apply label text substitutes + + + + false @@ -1257,7 +1384,31 @@ font-style: italic; - + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1557,107 +1708,33 @@ font-style: italic; - - + + + + + 0 + 0 + + - + Type case - - - - Text orientation + + + + Qt::Vertical - - - - - - - - - 0 - 0 - - - - letter - - - - - - - - 0 - 0 - - - - Space in pixels or map units, relative to size unit choice - - - 4 - - - -1000.000000000000000 - - - 999999999.000000000000000 - - - 0.100000000000000 - - - true - - - - - - - - - + + + 20 + 0 + - + - - - - Blend mode - - - - - - - If enabled, the label text will automatically be modified using a preset list of substitutes - - - Apply label text substitutes - - - - - - - - - - - - - - - - - - - - - + 6 @@ -1765,74 +1842,21 @@ font-style: italic; - - - - - - - 0 - 0 - - - - word - - - - - - - - 0 - 0 - - - - Space in pixels or map units, relative to size unit choice - - - 4 - - - -1000.000000000000000 - - - 999999999.000000000000000 - - - 0.100000000000000 - - - true - - - - - - - - - true - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - Capitalization style of text + + + + - + + + + + + + + 6 @@ -2034,40 +2058,46 @@ font-style: italic; - - - - Qt::Vertical - - - - 20 - 0 - - - - - - - - Spacing - - - - - - - - - - - + Enable kerning + + + + + + + Stretch + + + + + + + % + + + 1 + + + 4000 + + + 100 + + + + + + + + + + @@ -2101,8 +2131,8 @@ font-style: italic; 0 0 - 338 - 401 + 299 + 308 @@ -2447,8 +2477,8 @@ font-style: italic; 0 0 - 338 - 351 + 296 + 291 @@ -2725,8 +2755,8 @@ font-style: italic; 0 0 - 513 - 1053 + 438 + 753 @@ -3476,8 +3506,8 @@ font-style: italic; 0 0 - 356 - 592 + 324 + 457 @@ -3904,8 +3934,8 @@ font-style: italic; 0 0 - 181 - 227 + 159 + 211 @@ -4054,8 +4084,8 @@ font-style: italic; 0 0 - 510 - 2074 + 472 + 1690 @@ -5757,8 +5787,8 @@ font-style: italic; 0 0 - 461 - 858 + 430 + 708 @@ -6700,6 +6730,8 @@ font-style: italic; mFontLetterSpacingDDBtn mFontWordSpacingSpinBox mFontWordSpacingDDBtn + mSpinStretch + mFontStretchDDBtn mKerningCheckBox mTextOrientationComboBox mTextOrientationDDBtn @@ -6926,6 +6958,7 @@ font-style: italic; mLimitLabelSpinBox mMinSizeSpinBox mFitInsidePolygonCheckBox + mCoordRotationUnitComboBox diff --git a/tests/src/python/test_qgstextrenderer.py b/tests/src/python/test_qgstextrenderer.py index 640429bec2a..a5645527c72 100644 --- a/tests/src/python/test_qgstextrenderer.py +++ b/tests/src/python/test_qgstextrenderer.py @@ -162,6 +162,10 @@ class PyQgsTextRenderer(unittest.TestCase): t.setFamilies(['Arial', 'Comic Sans']) self.assertTrue(t.isValid()) + t = QgsTextFormat() + t.setStretchFactor(110) + self.assertTrue(t.isValid()) + t = QgsTextFormat() t.dataDefinedProperties().setProperty(QgsPalLayerSettings.Bold, QgsProperty.fromValue(True)) self.assertTrue(t.isValid()) @@ -707,6 +711,9 @@ class PyQgsTextRenderer(unittest.TestCase): s.setPreviewBackgroundColor(QColor(100, 150, 200)) s.setOrientation(QgsTextFormat.VerticalOrientation) s.setAllowHtmlFormatting(True) + + s.setStretchFactor(110) + s.dataDefinedProperties().setProperty(QgsPalLayerSettings.Bold, QgsProperty.fromExpression('1>2')) return s @@ -808,6 +815,10 @@ class PyQgsTextRenderer(unittest.TestCase): s.setFamilies(['Times New Roman']) self.assertNotEqual(s, s2) + s = self.createFormatSettings() + s.setStretchFactor(120) + self.assertNotEqual(s, s2) + def checkTextFormat(self, s): """ test QgsTextFormat """ self.assertTrue(s.buffer().enabled()) @@ -835,6 +846,10 @@ class PyQgsTextRenderer(unittest.TestCase): self.assertTrue(s.allowHtmlFormatting()) self.assertEqual(s.dataDefinedProperties().property(QgsPalLayerSettings.Bold).expressionString(), '1>2') + if int(QT_VERSION_STR.split('.')[0]) > 6 or ( + int(QT_VERSION_STR.split('.')[0]) == 6 and int(QT_VERSION_STR.split('.')[1]) >= 3): + self.assertEqual(s.stretchFactor(), 110) + def testFormatGettersSetters(self): s = self.createFormatSettings() self.checkTextFormat(s) @@ -1295,6 +1310,13 @@ class PyQgsTextRenderer(unittest.TestCase): f.updateDataDefinedProperties(context) self.assertEqual(f.blendMode(), QPainter.CompositionMode_ColorBurn) + if int(QT_VERSION_STR.split('.')[0]) > 6 or ( + int(QT_VERSION_STR.split('.')[0]) == 6 and int(QT_VERSION_STR.split('.')[1]) >= 3): + # stretch + f.dataDefinedProperties().setProperty(QgsPalLayerSettings.FontStretchFactor, QgsProperty.fromExpression("135")) + f.updateDataDefinedProperties(context) + self.assertEqual(f.stretchFactor(), 135) + def testFontFoundFromLayer(self): layer = createEmptyLayer() layer.setCustomProperty('labeling/fontFamily', 'asdasd') @@ -1367,6 +1389,12 @@ class PyQgsTextRenderer(unittest.TestCase): qfont = s.toQFont() self.assertAlmostEqual(qfont.pointSizeF(), 360.0, 2) + if int(QT_VERSION_STR.split('.')[0]) > 6 or ( + int(QT_VERSION_STR.split('.')[0]) == 6 and int(QT_VERSION_STR.split('.')[1]) >= 3): + s.setStretchFactor(115) + qfont = s.toQFont() + self.assertEqual(qfont.stretch(), 115) + def testFontMetrics(self): """ Test calculating font metrics from scaled text formats @@ -1514,7 +1542,6 @@ class PyQgsTextRenderer(unittest.TestCase): format.setCapitalization(Qgis.Capitalization.SmallCaps) format.setSize(30) assert self.checkRender(format, 'mixed_small_caps', text=['Small Caps']) - assert False @unittest.skipIf(int(QT_VERSION_STR.split('.')[0]) < 6 or (int(QT_VERSION_STR.split('.')[0]) == 6 and int(QT_VERSION_STR.split('.')[1]) < 3), 'Too old Qt') def testDrawAllSmallCaps(self): @@ -1523,7 +1550,22 @@ class PyQgsTextRenderer(unittest.TestCase): format.setSize(30) format.setCapitalization(Qgis.Capitalization.AllSmallCaps) assert self.checkRender(format, 'all_small_caps', text=['Small Caps']) - assert False + + @unittest.skipIf(int(QT_VERSION_STR.split('.')[0]) < 6 or (int(QT_VERSION_STR.split('.')[0]) == 6 and int(QT_VERSION_STR.split('.')[1]) < 3), 'Too old Qt') + def testDrawStretch(self): + format = QgsTextFormat() + format.setFont(getTestFont('bold')) + format.setSize(30) + format.setStretchFactor(150) + assert self.checkRender(format, 'stretch_expand') + + @unittest.skipIf(int(QT_VERSION_STR.split('.')[0]) < 6 or (int(QT_VERSION_STR.split('.')[0]) == 6 and int(QT_VERSION_STR.split('.')[1]) < 3), 'Too old Qt') + def testDrawStretchCondense(self): + format = QgsTextFormat() + format.setFont(getTestFont('bold')) + format.setSize(30) + format.setStretchFactor(50) + assert self.checkRender(format, 'stretch_condense') def testDrawBackgroundDisabled(self): format = QgsTextFormat() diff --git a/tests/testdata/control_images/text_renderer/stretch_condense/stretch_condense.png b/tests/testdata/control_images/text_renderer/stretch_condense/stretch_condense.png new file mode 100644 index 0000000000000000000000000000000000000000..3f84bce9b511b650b96826a04246dd81f4a19fac GIT binary patch literal 3066 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 literal 0 HcmV?d00001