Skip to content
Permalink
Browse files
[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!
  • Loading branch information
nyalldawson committed Nov 9, 2021
1 parent 044347d commit 730cd7e23a6c4dadadca18de6b5a0f2276e764b7
@@ -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
@@ -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

@@ -135,6 +135,7 @@ Contains settings for how a map layer will be labeled.
FontLetterSpacing,
FontWordSpacing,
FontBlendMode,
FontStretchFactor,

// text formatting
MultiLineWrapChar,
@@ -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;
@@ -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( "[<b>NoChange</b>|<b>Upper</b>|<br><b>Lower</b>|<b>Title</b>|<b>Capitalize</b>|<b>SmallCaps</b>|<b>AllSmallCaps</b>]" ), 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 ) )
{
@@ -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,
@@ -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 );
@@ -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()
@@ -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<QWidget *> 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() );
Loading

0 comments on commit 730cd7e

Please sign in to comment.