[api] Allow data defined properties to be set in QgsTextFormat, so

that these are automatically respected whenever the format is
rendered using QgsTextRenderer

This is done transparently to the caller - so by porting away
from the raw QPainter text apis to use QgsTextRenderer instead,
then they immediately gain the ability to use data defined
properties in their text formats.
This commit is contained in:
Nyall Dawson 2019-07-18 10:45:48 +10:00
parent 859d9a7233
commit 19a2b0e5d5
10 changed files with 1164 additions and 75 deletions

View File

@ -10,7 +10,6 @@
class QgsTextBufferSettings
{
%Docstring
@ -240,6 +239,13 @@ Sets the current paint ``effect`` for the buffer.
:param effect: paint effect. Ownership is transferred to the buffer settings.
.. seealso:: :py:func:`paintEffect`
%End
void updateDataDefinedProperties( QgsRenderContext &context, const QgsPropertyCollection &properties );
%Docstring
Updates the format by evaluating current values of data defined properties.
.. versionadded:: 3.10
%End
};
@ -827,6 +833,13 @@ Read settings from a DOM element.
Write settings into a DOM element.
.. seealso:: :py:func:`readXml`
%End
void updateDataDefinedProperties( QgsRenderContext &context, const QgsPropertyCollection &properties );
%Docstring
Updates the format by evaluating current values of data defined properties.
.. versionadded:: 3.10
%End
};
@ -1167,6 +1180,13 @@ Read settings from a DOM element.
Write settings into a DOM element.
.. seealso:: :py:func:`readXml`
%End
void updateDataDefinedProperties( QgsRenderContext &context, const QgsPropertyCollection &properties );
%Docstring
Updates the format by evaluating current values of data defined properties.
.. versionadded:: 3.10
%End
};
@ -1549,6 +1569,34 @@ was not found on the system this will return the name of the replacement
font.
.. seealso:: :py:func:`fontFound`
%End
QgsPropertyCollection &dataDefinedProperties();
%Docstring
Returns a reference to the format's property collection, used for data defined overrides.
.. seealso:: :py:func:`setDataDefinedProperties`
.. versionadded:: 3.10
%End
void setDataDefinedProperties( const QgsPropertyCollection &collection );
%Docstring
Sets the format's property collection, used for data defined overrides.
:param collection: property collection. Existing properties will be replaced.
.. seealso:: :py:func:`dataDefinedProperties`
.. versionadded:: 3.10
%End
void updateDataDefinedProperties( QgsRenderContext &context );
%Docstring
Updates the format by evaluating current values of data defined properties.
.. versionadded:: 3.10
%End
static QPixmap textFormatPreviewPixmap( const QgsTextFormat &format, QSize size, const QString &previewText = QString(), int padding = 0 );
@ -1727,6 +1775,42 @@ Returns the height of a text based on a given format.
};
class QgsTextRendererUtils
{
%Docstring
Utility functions for text rendering.
.. versionadded:: 3.10
%End
%TypeHeaderCode
#include "qgstextrenderer.h"
%End
public:
static QgsTextBackgroundSettings::ShapeType decodeShapeType( const QString &string );
%Docstring
Decodes a string representation of a background shape type to a type.
%End
static QgsTextBackgroundSettings::SizeType decodeBackgroundSizeType( const QString &string );
%Docstring
Decodes a string representation of a background size type to a type.
%End
static QgsTextBackgroundSettings::RotationType decodeBackgroundRotationType( const QString &string );
%Docstring
Decodes a string representation of a background rotation type to a type.
%End
static QgsTextShadowSettings::ShadowPlacement decodeShadowPlacementType( const QString &string );
%Docstring
Decodes a string representation of a shadow placement type to a type.
%End
};
/************************************************************************
* This file has been generated automatically from *
* *

View File

@ -591,13 +591,6 @@ QgsExpression *QgsPalLayerSettings::getLabelExpression()
return expression;
}
static Qt::PenJoinStyle _decodePenJoinStyle( const QString &str )
{
if ( str.compare( QLatin1String( "Miter" ), Qt::CaseInsensitive ) == 0 ) return Qt::MiterJoin;
if ( str.compare( QLatin1String( "Round" ), Qt::CaseInsensitive ) == 0 ) return Qt::RoundJoin;
return Qt::BevelJoin; // "Bevel"
}
QString updateDataDefinedString( const QString &value )
{
// TODO: update or remove this when project settings for labeling are migrated to better XML layout
@ -2530,7 +2523,7 @@ bool QgsPalLayerSettings::dataDefinedValEval( DataDefinedValueType valType,
if ( !joinstr.isEmpty() )
{
dataDefinedValues.insert( p, QVariant( static_cast< int >( _decodePenJoinStyle( joinstr ) ) ) );
dataDefinedValues.insert( p, QVariant( static_cast< int >( QgsSymbolLayerUtils::decodePenJoinStyle( joinstr ) ) ) );
return true;
}
return false;
@ -2920,31 +2913,8 @@ void QgsPalLayerSettings::parseShapeBackground( QgsRenderContext &context )
if ( !skind.isEmpty() )
{
// "Rectangle"
QgsTextBackgroundSettings::ShapeType shpkind = QgsTextBackgroundSettings::ShapeRectangle;
if ( skind.compare( QLatin1String( "Square" ), Qt::CaseInsensitive ) == 0 )
{
shpkind = QgsTextBackgroundSettings::ShapeSquare;
}
else if ( skind.compare( QLatin1String( "Ellipse" ), Qt::CaseInsensitive ) == 0 )
{
shpkind = QgsTextBackgroundSettings::ShapeEllipse;
}
else if ( skind.compare( QLatin1String( "Circle" ), Qt::CaseInsensitive ) == 0 )
{
shpkind = QgsTextBackgroundSettings::ShapeCircle;
}
else if ( skind.compare( QLatin1String( "SVG" ), Qt::CaseInsensitive ) == 0 )
{
shpkind = QgsTextBackgroundSettings::ShapeSVG;
}
else if ( skind.compare( QLatin1String( "marker" ), Qt::CaseInsensitive ) == 0 )
{
shpkind = QgsTextBackgroundSettings::ShapeMarkerSymbol;
}
shapeKind = shpkind;
dataDefinedValues.insert( QgsPalLayerSettings::ShapeKind, QVariant( static_cast< int >( shpkind ) ) );
shapeKind = QgsTextRendererUtils::decodeShapeType( skind );
dataDefinedValues.insert( QgsPalLayerSettings::ShapeKind, QVariant( static_cast< int >( shapeKind ) ) );
}
}
@ -2972,15 +2942,8 @@ void QgsPalLayerSettings::parseShapeBackground( QgsRenderContext &context )
if ( !stype.isEmpty() )
{
// "Buffer"
QgsTextBackgroundSettings::SizeType sizType = QgsTextBackgroundSettings::SizeBuffer;
if ( stype.compare( QLatin1String( "Fixed" ), Qt::CaseInsensitive ) == 0 )
{
sizType = QgsTextBackgroundSettings::SizeFixed;
}
shpSizeType = sizType;
dataDefinedValues.insert( QgsPalLayerSettings::ShapeSizeType, QVariant( static_cast< int >( sizType ) ) );
shpSizeType = QgsTextRendererUtils::decodeBackgroundSizeType( stype );
dataDefinedValues.insert( QgsPalLayerSettings::ShapeSizeType, QVariant( static_cast< int >( shpSizeType ) ) );
}
}
@ -3048,16 +3011,7 @@ void QgsPalLayerSettings::parseShapeBackground( QgsRenderContext &context )
if ( !rotstr.isEmpty() )
{
// "Sync"
QgsTextBackgroundSettings::RotationType rottype = QgsTextBackgroundSettings::RotationSync;
if ( rotstr.compare( QLatin1String( "Offset" ), Qt::CaseInsensitive ) == 0 )
{
rottype = QgsTextBackgroundSettings::RotationOffset;
}
else if ( rotstr.compare( QLatin1String( "Fixed" ), Qt::CaseInsensitive ) == 0 )
{
rottype = QgsTextBackgroundSettings::RotationFixed;
}
QgsTextBackgroundSettings::RotationType rottype = QgsTextRendererUtils::decodeBackgroundRotationType( rotstr );
dataDefinedValues.insert( QgsPalLayerSettings::ShapeRotationType, QVariant( static_cast< int >( rottype ) ) );
}
}
@ -3156,21 +3110,7 @@ void QgsPalLayerSettings::parseDropShadow( QgsRenderContext &context )
if ( !str.isEmpty() )
{
// "Lowest"
QgsTextShadowSettings::ShadowPlacement shdwtype = QgsTextShadowSettings::ShadowLowest;
if ( str.compare( QLatin1String( "Text" ), Qt::CaseInsensitive ) == 0 )
{
shdwtype = QgsTextShadowSettings::ShadowText;
}
else if ( str.compare( QLatin1String( "Buffer" ), Qt::CaseInsensitive ) == 0 )
{
shdwtype = QgsTextShadowSettings::ShadowBuffer;
}
else if ( str.compare( QLatin1String( "Background" ), Qt::CaseInsensitive ) == 0 )
{
shdwtype = QgsTextShadowSettings::ShadowShape;
}
QgsTextShadowSettings::ShadowPlacement shdwtype = QgsTextRendererUtils::decodeShadowPlacementType( str );
dataDefinedValues.insert( QgsPalLayerSettings::ShadowUnder, QVariant( static_cast< int >( shdwtype ) ) );
}
}

View File

@ -25,6 +25,7 @@
#include "qgspainting.h"
#include "qgsmarkersymbollayer.h"
#include "qgspainteffectregistry.h"
#include "qgspallabeling.h"
#include <QFontDatabase>
#include <QDesktopWidget>
@ -185,6 +186,64 @@ void QgsTextBufferSettings::setPaintEffect( QgsPaintEffect *effect )
d->paintEffect.reset( effect );
}
void QgsTextBufferSettings::updateDataDefinedProperties( QgsRenderContext &context, const QgsPropertyCollection &properties )
{
if ( properties.isActive( QgsPalLayerSettings::BufferDraw ) )
{
context.expressionContext().setOriginalValueVariable( d->enabled );
d->enabled = properties.valueAsBool( QgsPalLayerSettings::BufferDraw, context.expressionContext(), d->enabled );
}
if ( properties.isActive( QgsPalLayerSettings::BufferSize ) )
{
context.expressionContext().setOriginalValueVariable( d->size );
d->size = properties.valueAsDouble( QgsPalLayerSettings::BufferSize, context.expressionContext(), d->size );
}
QVariant exprVal = properties.value( QgsPalLayerSettings::BufferUnit, context.expressionContext() );
if ( exprVal.isValid() )
{
QString units = exprVal.toString();
if ( !units.isEmpty() )
{
bool ok;
QgsUnitTypes::RenderUnit res = QgsUnitTypes::decodeRenderUnit( units, &ok );
if ( ok )
d->sizeUnit = res;
}
}
if ( properties.isActive( QgsPalLayerSettings::BufferOpacity ) )
{
context.expressionContext().setOriginalValueVariable( d->opacity * 100 );
d->opacity = properties.value( QgsPalLayerSettings::BufferOpacity, context.expressionContext(), d->opacity * 100 ).toDouble() / 100.0;
}
if ( properties.isActive( QgsPalLayerSettings::BufferColor ) )
{
context.expressionContext().setOriginalValueVariable( QgsSymbolLayerUtils::encodeColor( d->color ) );
d->color = properties.valueAsColor( QgsPalLayerSettings::BufferColor, context.expressionContext(), d->color );
}
if ( properties.isActive( QgsPalLayerSettings::BufferBlendMode ) )
{
exprVal = properties.value( QgsPalLayerSettings::BufferBlendMode, context.expressionContext() );
QString blendstr = exprVal.toString().trimmed();
if ( !blendstr.isEmpty() )
d->blendMode = QgsSymbolLayerUtils::decodeBlendMode( blendstr );
}
if ( properties.isActive( QgsPalLayerSettings::BufferJoinStyle ) )
{
exprVal = properties.value( QgsPalLayerSettings::BufferJoinStyle, context.expressionContext() );
QString joinstr = exprVal.toString().trimmed();
if ( !joinstr.isEmpty() )
{
d->joinStyle = QgsSymbolLayerUtils::decodePenJoinStyle( joinstr );
}
}
}
void QgsTextBufferSettings::readFromLayer( QgsVectorLayer *layer )
{
// text buffer
@ -915,6 +974,181 @@ QDomElement QgsTextBackgroundSettings::writeXml( QDomDocument &doc, const QgsRea
return backgroundElem;
}
void QgsTextBackgroundSettings::updateDataDefinedProperties( QgsRenderContext &context, const QgsPropertyCollection &properties )
{
if ( properties.isActive( QgsPalLayerSettings::ShapeDraw ) )
{
context.expressionContext().setOriginalValueVariable( d->enabled );
d->enabled = properties.valueAsBool( QgsPalLayerSettings::ShapeDraw, context.expressionContext(), d->enabled );
}
if ( properties.isActive( QgsPalLayerSettings::ShapeSizeX ) )
{
context.expressionContext().setOriginalValueVariable( d->size.width() );
d->size.setWidth( properties.valueAsDouble( QgsPalLayerSettings::ShapeSizeX, context.expressionContext(), d->size.width() ) );
}
if ( properties.isActive( QgsPalLayerSettings::ShapeSizeY ) )
{
context.expressionContext().setOriginalValueVariable( d->size.height() );
d->size.setHeight( properties.valueAsDouble( QgsPalLayerSettings::ShapeSizeY, context.expressionContext(), d->size.height() ) );
}
QVariant exprVal = properties.value( QgsPalLayerSettings::ShapeSizeUnits, context.expressionContext() );
if ( exprVal.isValid() )
{
QString units = exprVal.toString();
if ( !units.isEmpty() )
{
bool ok;
QgsUnitTypes::RenderUnit res = QgsUnitTypes::decodeRenderUnit( units, &ok );
if ( ok )
d->sizeUnits = res;
}
}
exprVal = properties.value( QgsPalLayerSettings::ShapeKind, context.expressionContext() );
if ( exprVal.isValid() )
{
const QString skind = exprVal.toString().trimmed();
if ( !skind.isEmpty() )
{
d->type = QgsTextRendererUtils::decodeShapeType( skind );
}
}
exprVal = properties.value( QgsPalLayerSettings::ShapeSizeType, context.expressionContext() );
if ( exprVal.isValid() )
{
QString stype = exprVal.toString().trimmed();
if ( !stype.isEmpty() )
{
d->sizeType = QgsTextRendererUtils::decodeBackgroundSizeType( stype );
}
}
// data defined shape SVG path?
context.expressionContext().setOriginalValueVariable( d->svgFile );
exprVal = properties.value( QgsPalLayerSettings::ShapeSVGFile, context.expressionContext() );
if ( exprVal.isValid() )
{
QString svgfile = exprVal.toString().trimmed();
d->svgFile = QgsSymbolLayerUtils::svgSymbolNameToPath( svgfile, context.pathResolver() );
}
if ( properties.isActive( QgsPalLayerSettings::ShapeRotation ) )
{
context.expressionContext().setOriginalValueVariable( d->rotation );
d->rotation = properties.valueAsDouble( QgsPalLayerSettings::ShapeRotation, context.expressionContext(), d->rotation );
}
exprVal = properties.value( QgsPalLayerSettings::ShapeRotationType, context.expressionContext() );
if ( exprVal.isValid() )
{
QString rotstr = exprVal.toString().trimmed();
if ( !rotstr.isEmpty() )
{
d->rotationType = QgsTextRendererUtils::decodeBackgroundRotationType( rotstr );
}
}
exprVal = properties.value( QgsPalLayerSettings::ShapeOffset, context.expressionContext() );
if ( exprVal.isValid() )
{
QString offset = exprVal.toString();
if ( !offset.isEmpty() )
{
d->offset = QgsSymbolLayerUtils::decodePoint( offset );
}
}
exprVal = properties.value( QgsPalLayerSettings::ShapeOffsetUnits, context.expressionContext() );
if ( exprVal.isValid() )
{
QString units = exprVal.toString();
if ( !units.isEmpty() )
{
bool ok;
QgsUnitTypes::RenderUnit res = QgsUnitTypes::decodeRenderUnit( units, &ok );
if ( ok )
d->offsetUnits = res;
}
}
exprVal = properties.value( QgsPalLayerSettings::ShapeRadii, context.expressionContext() );
if ( exprVal.isValid() )
{
QString ptstr = exprVal.toString();
if ( !ptstr.isEmpty() )
{
d->radii = QgsSymbolLayerUtils::decodeSize( ptstr );
}
}
exprVal = properties.value( QgsPalLayerSettings::ShapeRadiiUnits, context.expressionContext() );
if ( exprVal.isValid() )
{
QString units = exprVal.toString();
if ( !units.isEmpty() )
{
bool ok;
QgsUnitTypes::RenderUnit res = QgsUnitTypes::decodeRenderUnit( units, &ok );
if ( ok )
d->radiiUnits = res;
}
}
if ( properties.isActive( QgsPalLayerSettings::ShapeOpacity ) )
{
context.expressionContext().setOriginalValueVariable( d->opacity * 100 );
d->opacity = properties.value( QgsPalLayerSettings::ShapeOpacity, context.expressionContext(), d->opacity * 100 ).toDouble() / 100.0;
}
if ( properties.isActive( QgsPalLayerSettings::ShapeFillColor ) )
{
context.expressionContext().setOriginalValueVariable( QgsSymbolLayerUtils::encodeColor( d->fillColor ) );
d->fillColor = properties.valueAsColor( QgsPalLayerSettings::ShapeFillColor, context.expressionContext(), d->fillColor );
}
if ( properties.isActive( QgsPalLayerSettings::ShapeStrokeColor ) )
{
context.expressionContext().setOriginalValueVariable( QgsSymbolLayerUtils::encodeColor( d->strokeColor ) );
d->strokeColor = properties.valueAsColor( QgsPalLayerSettings::ShapeStrokeColor, context.expressionContext(), d->strokeColor );
}
if ( properties.isActive( QgsPalLayerSettings::ShapeStrokeWidth ) )
{
context.expressionContext().setOriginalValueVariable( d->strokeWidth );
d->strokeWidth = properties.valueAsDouble( QgsPalLayerSettings::ShapeStrokeWidth, context.expressionContext(), d->strokeWidth );
}
exprVal = properties.value( QgsPalLayerSettings::ShapeStrokeWidthUnits, context.expressionContext() );
if ( exprVal.isValid() )
{
QString units = exprVal.toString();
if ( !units.isEmpty() )
{
bool ok;
QgsUnitTypes::RenderUnit res = QgsUnitTypes::decodeRenderUnit( units, &ok );
if ( ok )
d->strokeWidthUnits = res;
}
}
if ( properties.isActive( QgsPalLayerSettings::ShapeBlendMode ) )
{
exprVal = properties.value( QgsPalLayerSettings::ShapeBlendMode, context.expressionContext() );
QString blendstr = exprVal.toString().trimmed();
if ( !blendstr.isEmpty() )
d->blendMode = QgsSymbolLayerUtils::decodeBlendMode( blendstr );
}
if ( properties.isActive( QgsPalLayerSettings::ShapeJoinStyle ) )
{
exprVal = properties.value( QgsPalLayerSettings::ShapeJoinStyle, context.expressionContext() );
QString joinstr = exprVal.toString().trimmed();
if ( !joinstr.isEmpty() )
{
d->joinStyle = QgsSymbolLayerUtils::decodePenJoinStyle( joinstr );
}
}
}
//
// QgsTextShadowSettings
@ -1247,6 +1481,95 @@ QDomElement QgsTextShadowSettings::writeXml( QDomDocument &doc ) const
return shadowElem;
}
void QgsTextShadowSettings::updateDataDefinedProperties( QgsRenderContext &context, const QgsPropertyCollection &properties )
{
if ( properties.isActive( QgsPalLayerSettings::ShadowDraw ) )
{
context.expressionContext().setOriginalValueVariable( d->enabled );
d->enabled = properties.valueAsBool( QgsPalLayerSettings::ShadowDraw, context.expressionContext(), d->enabled );
}
// data defined shadow under type?
QVariant exprVal = properties.value( QgsPalLayerSettings::ShadowUnder, context.expressionContext() );
if ( exprVal.isValid() )
{
QString str = exprVal.toString().trimmed();
if ( !str.isEmpty() )
{
d->shadowUnder = QgsTextRendererUtils::decodeShadowPlacementType( str );
}
}
if ( properties.isActive( QgsPalLayerSettings::ShadowOffsetAngle ) )
{
context.expressionContext().setOriginalValueVariable( d->offsetAngle );
d->offsetAngle = properties.valueAsInt( QgsPalLayerSettings::ShadowOffsetAngle, context.expressionContext(), d->offsetAngle );
}
if ( properties.isActive( QgsPalLayerSettings::ShadowOffsetDist ) )
{
context.expressionContext().setOriginalValueVariable( d->offsetDist );
d->offsetDist = properties.valueAsDouble( QgsPalLayerSettings::ShadowOffsetDist, context.expressionContext(), d->offsetDist );
}
exprVal = properties.value( QgsPalLayerSettings::ShadowOffsetUnits, context.expressionContext() );
if ( exprVal.isValid() )
{
QString units = exprVal.toString();
if ( !units.isEmpty() )
{
bool ok;
QgsUnitTypes::RenderUnit res = QgsUnitTypes::decodeRenderUnit( units, &ok );
if ( ok )
d->offsetUnits = res;
}
}
if ( properties.isActive( QgsPalLayerSettings::ShadowRadius ) )
{
context.expressionContext().setOriginalValueVariable( d->radius );
d->radius = properties.valueAsDouble( QgsPalLayerSettings::ShadowRadius, context.expressionContext(), d->radius );
}
exprVal = properties.value( QgsPalLayerSettings::ShadowRadiusUnits, context.expressionContext() );
if ( exprVal.isValid() )
{
QString units = exprVal.toString();
if ( !units.isEmpty() )
{
bool ok;
QgsUnitTypes::RenderUnit res = QgsUnitTypes::decodeRenderUnit( units, &ok );
if ( ok )
d->radiusUnits = res;
}
}
if ( properties.isActive( QgsPalLayerSettings::ShadowOpacity ) )
{
context.expressionContext().setOriginalValueVariable( d->opacity * 100 );
d->opacity = properties.value( QgsPalLayerSettings::ShadowOpacity, context.expressionContext(), d->opacity * 100 ).toDouble() / 100.0;
}
if ( properties.isActive( QgsPalLayerSettings::ShadowScale ) )
{
context.expressionContext().setOriginalValueVariable( d->scale );
d->scale = properties.valueAsInt( QgsPalLayerSettings::ShadowScale, context.expressionContext(), d->scale );
}
if ( properties.isActive( QgsPalLayerSettings::ShadowColor ) )
{
context.expressionContext().setOriginalValueVariable( QgsSymbolLayerUtils::encodeColor( d->color ) );
d->color = properties.valueAsColor( QgsPalLayerSettings::ShadowColor, context.expressionContext(), d->color );
}
if ( properties.isActive( QgsPalLayerSettings::ShadowBlendMode ) )
{
exprVal = properties.value( QgsPalLayerSettings::ShadowBlendMode, context.expressionContext() );
QString blendstr = exprVal.toString().trimmed();
if ( !blendstr.isEmpty() )
d->blendMode = QgsSymbolLayerUtils::decodeBlendMode( blendstr );
}
}
//
// QgsTextFormat
//
@ -1596,6 +1919,29 @@ void QgsTextFormat::readXml( const QDomElement &elem, const QgsReadWriteContext
{
mBackgroundSettings.readXml( textStyleElem, context );
}
if ( textStyleElem.firstChildElement( QStringLiteral( "dd_properties" ) ).isNull() )
{
mBackgroundSettings.readXml( elem, context );
}
else
{
mBackgroundSettings.readXml( textStyleElem, context );
}
QDomElement ddElem = textStyleElem.firstChildElement( QStringLiteral( "dd_properties" ) );
if ( ddElem.isNull() )
{
ddElem = elem.firstChildElement( QStringLiteral( "dd_properties" ) );
}
if ( !ddElem.isNull() )
{
d->mDataDefinedProperties.readXml( ddElem, QgsPalLayerSettings::propertyDefinitions() );
}
else
{
d->mDataDefinedProperties.clear();
}
}
QDomElement QgsTextFormat::writeXml( QDomDocument &doc, const QgsReadWriteContext &context ) const
@ -1620,9 +1966,14 @@ QDomElement QgsTextFormat::writeXml( QDomDocument &doc, const QgsReadWriteContex
textStyleElem.setAttribute( QStringLiteral( "blendMode" ), QgsPainting::getBlendModeEnum( d->blendMode ) );
textStyleElem.setAttribute( QStringLiteral( "multilineHeight" ), d->multilineHeight );
QDomElement ddElem = doc.createElement( QStringLiteral( "dd_properties" ) );
d->mDataDefinedProperties.writeXml( ddElem, QgsPalLayerSettings::propertyDefinitions() );
textStyleElem.appendChild( mBufferSettings.writeXml( doc ) );
textStyleElem.appendChild( mBackgroundSettings.writeXml( doc, context ) );
textStyleElem.appendChild( mShadowSettings.writeXml( doc ) );
textStyleElem.appendChild( ddElem );
return textStyleElem;
}
@ -1735,6 +2086,195 @@ bool QgsTextFormat::containsAdvancedEffects() const
return false;
}
QgsPropertyCollection &QgsTextFormat::dataDefinedProperties()
{
return d->mDataDefinedProperties;
}
const QgsPropertyCollection &QgsTextFormat::dataDefinedProperties() const
{
return d->mDataDefinedProperties;
}
void QgsTextFormat::setDataDefinedProperties( const QgsPropertyCollection &collection )
{
d->mDataDefinedProperties = collection;
}
void QgsTextFormat::updateDataDefinedProperties( QgsRenderContext &context )
{
if ( !d->mDataDefinedProperties.hasActiveProperties() )
return;
QString ddFontFamily;
context.expressionContext().setOriginalValueVariable( d->textFont.family() );
QVariant exprVal = d->mDataDefinedProperties.value( QgsPalLayerSettings::Family, context.expressionContext() );
if ( exprVal.isValid() )
{
QString family = exprVal.toString().trimmed();
if ( d->textFont.family() != family )
{
// testing for ddFontFamily in QFontDatabase.families() may be slow to do for every feature
// (i.e. don't use QgsFontUtils::fontFamilyMatchOnSystem( family ) here)
if ( QgsFontUtils::fontFamilyOnSystem( family ) )
{
ddFontFamily = family;
}
}
}
// data defined named font style?
QString ddFontStyle;
context.expressionContext().setOriginalValueVariable( d->textNamedStyle );
exprVal = d->mDataDefinedProperties.value( QgsPalLayerSettings::FontStyle, context.expressionContext() );
if ( exprVal.isValid() )
{
QString fontstyle = exprVal.toString().trimmed();
ddFontStyle = fontstyle;
}
bool ddBold = false;
if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Bold ) )
{
context.expressionContext().setOriginalValueVariable( d->textFont.bold() );
ddBold = d->mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::Bold, context.expressionContext(), false ) ;
}
bool ddItalic = false;
if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Italic ) )
{
context.expressionContext().setOriginalValueVariable( d->textFont.italic() );
ddItalic = d->mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::Italic, context.expressionContext(), false );
}
// TODO: update when pref for how to resolve missing family (use matching algorithm or just default font) is implemented
// (currently defaults to what has been read in from layer settings)
QFont newFont;
QFontDatabase fontDb;
QFont appFont = QApplication::font();
bool newFontBuilt = false;
if ( ddBold || ddItalic )
{
// new font needs built, since existing style needs removed
newFont = QFont( !ddFontFamily.isEmpty() ? ddFontFamily : d->textFont.family() );
newFontBuilt = true;
newFont.setBold( ddBold );
newFont.setItalic( ddItalic );
}
else if ( !ddFontStyle.isEmpty()
&& ddFontStyle.compare( QLatin1String( "Ignore" ), Qt::CaseInsensitive ) != 0 )
{
if ( !ddFontFamily.isEmpty() )
{
// both family and style are different, build font from database
QFont styledfont = fontDb.font( ddFontFamily, ddFontStyle, appFont.pointSize() );
if ( appFont != styledfont )
{
newFont = styledfont;
newFontBuilt = true;
}
}
// update the font face style
QgsFontUtils::updateFontViaStyle( newFontBuilt ? newFont : d->textFont, ddFontStyle );
}
else if ( !ddFontFamily.isEmpty() )
{
if ( ddFontStyle.compare( QLatin1String( "Ignore" ), Qt::CaseInsensitive ) != 0 )
{
// just family is different, build font from database
QFont styledfont = fontDb.font( ddFontFamily, d->textNamedStyle, appFont.pointSize() );
if ( appFont != styledfont )
{
newFont = styledfont;
newFontBuilt = true;
}
}
else
{
newFont = QFont( ddFontFamily );
newFontBuilt = true;
}
}
if ( newFontBuilt )
{
// copy over existing font settings
newFont.setUnderline( d->textFont.underline() );
newFont.setStrikeOut( d->textFont.strikeOut() );
newFont.setWordSpacing( d->textFont.wordSpacing() );
newFont.setLetterSpacing( QFont::AbsoluteSpacing, d->textFont.letterSpacing() );
d->textFont = newFont;
}
if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Underline ) )
{
context.expressionContext().setOriginalValueVariable( d->textFont.underline() );
d->textFont.setUnderline( d->mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::Underline, context.expressionContext(), d->textFont.underline() ) );
}
if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Strikeout ) )
{
context.expressionContext().setOriginalValueVariable( d->textFont.strikeOut() );
d->textFont.setStrikeOut( d->mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::Strikeout, context.expressionContext(), d->textFont.strikeOut() ) );
}
if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Color ) )
{
context.expressionContext().setOriginalValueVariable( QgsSymbolLayerUtils::encodeColor( d->textColor ) );
d->textColor = d->mDataDefinedProperties.valueAsColor( QgsPalLayerSettings::Color, context.expressionContext(), d->textColor );
}
if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::Size ) )
{
context.expressionContext().setOriginalValueVariable( size() );
d->fontSize = d->mDataDefinedProperties.valueAsDouble( QgsPalLayerSettings::Size, context.expressionContext(), d->fontSize );
}
exprVal = d->mDataDefinedProperties.value( QgsPalLayerSettings::FontSizeUnit, context.expressionContext() );
if ( exprVal.isValid() )
{
QString units = exprVal.toString();
if ( !units.isEmpty() )
{
bool ok;
QgsUnitTypes::RenderUnit res = QgsUnitTypes::decodeRenderUnit( units, &ok );
if ( ok )
d->fontSizeUnits = res;
}
}
if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::FontOpacity ) )
{
context.expressionContext().setOriginalValueVariable( d->opacity * 100 );
d->opacity = d->mDataDefinedProperties.value( QgsPalLayerSettings::FontOpacity, context.expressionContext(), d->opacity * 100 ).toDouble() / 100.0;
}
if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::FontLetterSpacing ) )
{
context.expressionContext().setOriginalValueVariable( d->textFont.letterSpacing() );
d->textFont.setLetterSpacing( QFont::AbsoluteSpacing, d->mDataDefinedProperties.value( QgsPalLayerSettings::FontLetterSpacing, context.expressionContext(), d->textFont.letterSpacing() ).toDouble() );
}
if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::FontWordSpacing ) )
{
context.expressionContext().setOriginalValueVariable( d->textFont.wordSpacing() );
d->textFont.setWordSpacing( d->mDataDefinedProperties.value( QgsPalLayerSettings::FontWordSpacing, context.expressionContext(), d->textFont.wordSpacing() ).toDouble() );
}
if ( d->mDataDefinedProperties.isActive( QgsPalLayerSettings::FontBlendMode ) )
{
exprVal = d->mDataDefinedProperties.value( QgsPalLayerSettings::FontBlendMode, context.expressionContext() );
QString blendstr = exprVal.toString().trimmed();
if ( !blendstr.isEmpty() )
d->blendMode = QgsSymbolLayerUtils::decodeBlendMode( blendstr );
}
mShadowSettings.updateDataDefinedProperties( context, d->mDataDefinedProperties );
mBackgroundSettings.updateDataDefinedProperties( context, d->mDataDefinedProperties );
mBufferSettings.updateDataDefinedProperties( context, d->mDataDefinedProperties );
}
QPixmap QgsTextFormat::textFormatPreviewPixmap( const QgsTextFormat &format, QSize size, const QString &previewText, int padding )
{
QgsTextFormat tempFormat = format;
@ -1837,7 +2377,10 @@ int QgsTextRenderer::sizeToPixel( double size, const QgsRenderContext &c, QgsUni
void QgsTextRenderer::drawText( const QRectF &rect, double rotation, QgsTextRenderer::HAlignment alignment, const QStringList &textLines, QgsRenderContext &context, const QgsTextFormat &format, bool )
{
QgsTextFormat tmpFormat = updateShadowPosition( format );
QgsTextFormat tmpFormat = format;
if ( format.dataDefinedProperties().hasActiveProperties() ) // note, we use format instead of tmpFormat here, it's const and potentially avoids a detach
tmpFormat.updateDataDefinedProperties( context );
tmpFormat = updateShadowPosition( tmpFormat );
if ( tmpFormat.background().enabled() )
{
@ -1854,7 +2397,10 @@ void QgsTextRenderer::drawText( const QRectF &rect, double rotation, QgsTextRend
void QgsTextRenderer::drawText( QPointF point, double rotation, QgsTextRenderer::HAlignment alignment, const QStringList &textLines, QgsRenderContext &context, const QgsTextFormat &format, bool )
{
QgsTextFormat tmpFormat = updateShadowPosition( format );
QgsTextFormat tmpFormat = format;
if ( format.dataDefinedProperties().hasActiveProperties() ) // note, we use format instead of tmpFormat here, it's const and potentially avoids a detach
tmpFormat.updateDataDefinedProperties( context );
tmpFormat = updateShadowPosition( tmpFormat );
if ( tmpFormat.background().enabled() )
{
@ -2843,3 +3389,86 @@ void QgsTextRenderer::drawTextInternal( TextPart drawType,
}
}
//
// QgsTextRendererUtils
//
QgsTextBackgroundSettings::ShapeType QgsTextRendererUtils::decodeShapeType( const QString &string )
{
QgsTextBackgroundSettings::ShapeType shpkind = QgsTextBackgroundSettings::ShapeRectangle;
const QString skind = string.trimmed();
if ( skind.compare( QLatin1String( "Square" ), Qt::CaseInsensitive ) == 0 )
{
shpkind = QgsTextBackgroundSettings::ShapeSquare;
}
else if ( skind.compare( QLatin1String( "Ellipse" ), Qt::CaseInsensitive ) == 0 )
{
shpkind = QgsTextBackgroundSettings::ShapeEllipse;
}
else if ( skind.compare( QLatin1String( "Circle" ), Qt::CaseInsensitive ) == 0 )
{
shpkind = QgsTextBackgroundSettings::ShapeCircle;
}
else if ( skind.compare( QLatin1String( "SVG" ), Qt::CaseInsensitive ) == 0 )
{
shpkind = QgsTextBackgroundSettings::ShapeSVG;
}
else if ( skind.compare( QLatin1String( "marker" ), Qt::CaseInsensitive ) == 0 )
{
shpkind = QgsTextBackgroundSettings::ShapeMarkerSymbol;
}
return shpkind;
}
QgsTextBackgroundSettings::SizeType QgsTextRendererUtils::decodeBackgroundSizeType( const QString &string )
{
const QString stype = string.trimmed();
// "Buffer"
QgsTextBackgroundSettings::SizeType sizType = QgsTextBackgroundSettings::SizeBuffer;
if ( stype.compare( QLatin1String( "Fixed" ), Qt::CaseInsensitive ) == 0 )
{
sizType = QgsTextBackgroundSettings::SizeFixed;
}
return sizType;
}
QgsTextBackgroundSettings::RotationType QgsTextRendererUtils::decodeBackgroundRotationType( const QString &string )
{
const QString rotstr = string.trimmed();
// "Sync"
QgsTextBackgroundSettings::RotationType rottype = QgsTextBackgroundSettings::RotationSync;
if ( rotstr.compare( QLatin1String( "Offset" ), Qt::CaseInsensitive ) == 0 )
{
rottype = QgsTextBackgroundSettings::RotationOffset;
}
else if ( rotstr.compare( QLatin1String( "Fixed" ), Qt::CaseInsensitive ) == 0 )
{
rottype = QgsTextBackgroundSettings::RotationFixed;
}
return rottype;
}
QgsTextShadowSettings::ShadowPlacement QgsTextRendererUtils::decodeShadowPlacementType( const QString &string )
{
const QString str = string.trimmed();
// "Lowest"
QgsTextShadowSettings::ShadowPlacement shdwtype = QgsTextShadowSettings::ShadowLowest;
if ( str.compare( QLatin1String( "Text" ), Qt::CaseInsensitive ) == 0 )
{
shdwtype = QgsTextShadowSettings::ShadowText;
}
else if ( str.compare( QLatin1String( "Buffer" ), Qt::CaseInsensitive ) == 0 )
{
shdwtype = QgsTextShadowSettings::ShadowBuffer;
}
else if ( str.compare( QLatin1String( "Background" ), Qt::CaseInsensitive ) == 0 )
{
shdwtype = QgsTextShadowSettings::ShadowShape;
}
return shdwtype;
}

View File

@ -35,6 +35,7 @@ class QgsTextSettingsPrivate;
class QgsVectorLayer;
class QgsPaintEffect;
class QgsMarkerSymbol;
class QgsPropertyCollection;
/**
* \class QgsTextBufferSettings
@ -43,7 +44,6 @@ class QgsMarkerSymbol;
* \note QgsTextBufferSettings objects are implicitly shared.
* \since QGIS 3.0
*/
class CORE_EXPORT QgsTextBufferSettings
{
public:
@ -226,6 +226,12 @@ class CORE_EXPORT QgsTextBufferSettings
*/
void setPaintEffect( QgsPaintEffect *effect SIP_TRANSFER );
/**
* Updates the format by evaluating current values of data defined properties.
* \since QGIS 3.10
*/
void updateDataDefinedProperties( QgsRenderContext &context, const QgsPropertyCollection &properties );
private:
QSharedDataPointer<QgsTextBufferSettingsPrivate> d;
@ -700,6 +706,12 @@ class CORE_EXPORT QgsTextBackgroundSettings
*/
QDomElement writeXml( QDomDocument &doc, const QgsReadWriteContext &context ) const;
/**
* Updates the format by evaluating current values of data defined properties.
* \since QGIS 3.10
*/
void updateDataDefinedProperties( QgsRenderContext &context, const QgsPropertyCollection &properties );
private:
QSharedDataPointer<QgsTextBackgroundSettingsPrivate> d;
@ -980,6 +992,12 @@ class CORE_EXPORT QgsTextShadowSettings
*/
QDomElement writeXml( QDomDocument &doc ) const;
/**
* Updates the format by evaluating current values of data defined properties.
* \since QGIS 3.10
*/
void updateDataDefinedProperties( QgsRenderContext &context, const QgsPropertyCollection &properties );
private:
QSharedDataPointer<QgsTextShadowSettingsPrivate> d;
@ -1306,6 +1324,35 @@ class CORE_EXPORT QgsTextFormat
*/
QString resolvedFontFamily() const { return mTextFontFamily; }
/**
* Returns a reference to the format's property collection, used for data defined overrides.
* \see setDataDefinedProperties()
* \since QGIS 3.10
*/
QgsPropertyCollection &dataDefinedProperties();
/**
* Returns a reference to the format's property collection, used for data defined overrides.
* \see setDataDefinedProperties()
* \note not available in Python bindings
* \since QGIS 3.10
*/
const QgsPropertyCollection &dataDefinedProperties() const SIP_SKIP;
/**
* Sets the format's property collection, used for data defined overrides.
* \param collection property collection. Existing properties will be replaced.
* \see dataDefinedProperties()
* \since QGIS 3.10
*/
void setDataDefinedProperties( const QgsPropertyCollection &collection );
/**
* Updates the format by evaluating current values of data defined properties.
* \since QGIS 3.10
*/
void updateDataDefinedProperties( QgsRenderContext &context );
/**
* Returns a pixmap preview for a text \a format.
* \param format text format
@ -1550,4 +1597,37 @@ class CORE_EXPORT QgsTextRenderer
};
/**
* \class QgsTextRendererUtils
* \ingroup core
* Utility functions for text rendering.
* \since QGIS 3.10
*/
class CORE_EXPORT QgsTextRendererUtils
{
public:
/**
* Decodes a string representation of a background shape type to a type.
*/
static QgsTextBackgroundSettings::ShapeType decodeShapeType( const QString &string );
/**
* Decodes a string representation of a background size type to a type.
*/
static QgsTextBackgroundSettings::SizeType decodeBackgroundSizeType( const QString &string );
/**
* Decodes a string representation of a background rotation type to a type.
*/
static QgsTextBackgroundSettings::RotationType decodeBackgroundRotationType( const QString &string );
/**
* Decodes a string representation of a shadow placement type to a type.
*/
static QgsTextShadowSettings::ShadowPlacement decodeShadowPlacementType( const QString &string );
};
#endif // QGSTEXTRENDERER_H

View File

@ -218,6 +218,7 @@ class QgsTextSettingsPrivate : public QSharedData
, blendMode( other.blendMode )
, multilineHeight( other.multilineHeight )
, previewBackgroundColor( other.previewBackgroundColor )
, mDataDefinedProperties( other.mDataDefinedProperties )
{
}
@ -232,6 +233,10 @@ class QgsTextSettingsPrivate : public QSharedData
double multilineHeight = 1.0 ; //0.0 to 10.0, leading between lines as multiplyer of line height
QColor previewBackgroundColor = Qt::white;
//! Property collection for data defined settings
QgsPropertyCollection mDataDefinedProperties;
};

View File

@ -186,9 +186,13 @@ QString QgsSymbolLayerUtils::encodePenJoinStyle( Qt::PenJoinStyle style )
Qt::PenJoinStyle QgsSymbolLayerUtils::decodePenJoinStyle( const QString &str )
{
if ( str == QLatin1String( "bevel" ) ) return Qt::BevelJoin;
if ( str == QLatin1String( "miter" ) ) return Qt::MiterJoin;
if ( str == QLatin1String( "round" ) ) return Qt::RoundJoin;
const QString cleaned = str.toLower().trimmed();
if ( cleaned == QLatin1String( "bevel" ) )
return Qt::BevelJoin;
if ( cleaned == QLatin1String( "miter" ) )
return Qt::MiterJoin;
if ( cleaned == QLatin1String( "round" ) )
return Qt::RoundJoin;
return Qt::BevelJoin;
}

View File

@ -27,7 +27,10 @@ from qgis.core import (QgsTextBufferSettings,
QgsRectangle,
QgsRenderChecker,
QgsBlurEffect,
QgsMarkerSymbol)
QgsMarkerSymbol,
QgsPalLayerSettings,
QgsProperty,
QgsFontUtils)
from qgis.PyQt.QtGui import (QColor, QPainter, QFont, QImage, QBrush, QPen, QFontMetricsF)
from qgis.PyQt.QtCore import (Qt, QSizeF, QPointF, QRectF, QDir, QSize)
from qgis.PyQt.QtXml import QDomDocument
@ -48,6 +51,7 @@ class PyQgsTextRenderer(unittest.TestCase):
def setUp(self):
self.report = "<h1>Python QgsTextRenderer Tests</h1>\n"
QgsFontUtils.loadStandardTestFonts(['Bold', 'Oblique'])
def tearDown(self):
report_file_path = "%s/qgistest.html" % QDir.tempPath()
@ -289,6 +293,7 @@ class PyQgsTextRenderer(unittest.TestCase):
s.setBlendMode(QPainter.CompositionMode_DestinationAtop)
s.setLineHeight(5)
s.setPreviewBackgroundColor(QColor(100, 150, 200))
s.dataDefinedProperties().setProperty(QgsPalLayerSettings.Bold, QgsProperty.fromExpression('1>2'))
return s
def checkTextFormat(self, s):
@ -309,6 +314,7 @@ class PyQgsTextRenderer(unittest.TestCase):
self.assertEqual(s.blendMode(), QPainter.CompositionMode_DestinationAtop)
self.assertEqual(s.lineHeight(), 5)
self.assertEqual(s.previewBackgroundColor().name(), '#6496c8')
self.assertEqual(s.dataDefinedProperties().property(QgsPalLayerSettings.Bold).expressionString(), '1>2')
def testFormatGettersSetters(self):
s = self.createFormatSettings()
@ -372,6 +378,318 @@ class PyQgsTextRenderer(unittest.TestCase):
t.shadow().setBlendMode(QPainter.CompositionMode_SourceOver)
self.assertFalse(t.containsAdvancedEffects())
def testDataDefinedBufferSettings(self):
f = QgsTextFormat()
context = QgsRenderContext()
# buffer enabled
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.BufferDraw, QgsProperty.fromExpression('1'))
f.updateDataDefinedProperties(context)
self.assertTrue(f.buffer().enabled())
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.BufferDraw, QgsProperty.fromExpression('0'))
context = QgsRenderContext()
f.updateDataDefinedProperties(context)
self.assertFalse(f.buffer().enabled())
# buffer size
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.BufferSize, QgsProperty.fromExpression('7.8'))
f.updateDataDefinedProperties(context)
self.assertEqual(f.buffer().size(), 7.8)
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.BufferUnit, QgsProperty.fromExpression("'pixel'"))
f.updateDataDefinedProperties(context)
self.assertEqual(f.buffer().sizeUnit(), QgsUnitTypes.RenderPixels)
# buffer opacity
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.BufferOpacity, QgsProperty.fromExpression('37'))
f.updateDataDefinedProperties(context)
self.assertEqual(f.buffer().opacity(), 0.37)
# blend mode
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.BufferBlendMode, QgsProperty.fromExpression("'burn'"))
f.updateDataDefinedProperties(context)
self.assertEqual(f.buffer().blendMode(), QPainter.CompositionMode_ColorBurn)
# join style
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.BufferJoinStyle, QgsProperty.fromExpression("'miter'"))
f.updateDataDefinedProperties(context)
self.assertEqual(f.buffer().joinStyle(), Qt.MiterJoin)
# color
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.BufferColor, QgsProperty.fromExpression("'#ff0088'"))
f.updateDataDefinedProperties(context)
self.assertEqual(f.buffer().color().name(), '#ff0088')
def testDataDefinedBackgroundSettings(self):
f = QgsTextFormat()
context = QgsRenderContext()
# background enabled
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.ShapeDraw, QgsProperty.fromExpression('1'))
f.updateDataDefinedProperties(context)
self.assertTrue(f.background().enabled())
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.ShapeDraw, QgsProperty.fromExpression('0'))
context = QgsRenderContext()
f.updateDataDefinedProperties(context)
self.assertFalse(f.background().enabled())
# background size
f.background().setSize(QSizeF(13, 14))
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.ShapeSizeX, QgsProperty.fromExpression('7.8'))
f.updateDataDefinedProperties(context)
self.assertEqual(f.background().size().width(), 7.8)
self.assertEqual(f.background().size().height(), 14)
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.ShapeSizeY, QgsProperty.fromExpression('17.8'))
f.updateDataDefinedProperties(context)
self.assertEqual(f.background().size().width(), 7.8)
self.assertEqual(f.background().size().height(), 17.8)
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.ShapeSizeUnits, QgsProperty.fromExpression("'pixel'"))
f.updateDataDefinedProperties(context)
self.assertEqual(f.background().sizeUnit(), QgsUnitTypes.RenderPixels)
# shape kind
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.ShapeKind, QgsProperty.fromExpression("'square'"))
f.updateDataDefinedProperties(context)
self.assertEqual(f.background().type(), QgsTextBackgroundSettings.ShapeSquare)
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.ShapeKind, QgsProperty.fromExpression("'ellipse'"))
f.updateDataDefinedProperties(context)
self.assertEqual(f.background().type(), QgsTextBackgroundSettings.ShapeEllipse)
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.ShapeKind, QgsProperty.fromExpression("'circle'"))
f.updateDataDefinedProperties(context)
self.assertEqual(f.background().type(), QgsTextBackgroundSettings.ShapeCircle)
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.ShapeKind, QgsProperty.fromExpression("'svg'"))
f.updateDataDefinedProperties(context)
self.assertEqual(f.background().type(), QgsTextBackgroundSettings.ShapeSVG)
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.ShapeKind, QgsProperty.fromExpression("'marker'"))
f.updateDataDefinedProperties(context)
self.assertEqual(f.background().type(), QgsTextBackgroundSettings.ShapeMarkerSymbol)
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.ShapeKind, QgsProperty.fromExpression("'rect'"))
f.updateDataDefinedProperties(context)
self.assertEqual(f.background().type(), QgsTextBackgroundSettings.ShapeRectangle)
# size type
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.ShapeSizeType, QgsProperty.fromExpression("'fixed'"))
f.updateDataDefinedProperties(context)
self.assertEqual(f.background().sizeType(), QgsTextBackgroundSettings.SizeFixed)
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.ShapeSizeType, QgsProperty.fromExpression("'buffer'"))
f.updateDataDefinedProperties(context)
self.assertEqual(f.background().sizeType(), QgsTextBackgroundSettings.SizeBuffer)
# svg path
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.ShapeSVGFile, QgsProperty.fromExpression("'my.svg'"))
f.updateDataDefinedProperties(context)
self.assertEqual(f.background().svgFile(), 'my.svg')
# shape rotation
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.ShapeRotation, QgsProperty.fromExpression('67'))
f.updateDataDefinedProperties(context)
self.assertEqual(f.background().rotation(), 67)
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.ShapeRotationType, QgsProperty.fromExpression("'offset'"))
f.updateDataDefinedProperties(context)
self.assertEqual(f.background().rotationType(), QgsTextBackgroundSettings.RotationOffset)
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.ShapeRotationType, QgsProperty.fromExpression("'fixed'"))
f.updateDataDefinedProperties(context)
self.assertEqual(f.background().rotationType(), QgsTextBackgroundSettings.RotationFixed)
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.ShapeRotationType, QgsProperty.fromExpression("'sync'"))
f.updateDataDefinedProperties(context)
self.assertEqual(f.background().rotationType(), QgsTextBackgroundSettings.RotationSync)
# shape offset
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.ShapeOffset, QgsProperty.fromExpression("'7,9'"))
f.updateDataDefinedProperties(context)
self.assertEqual(f.background().offset(), QPointF(7, 9))
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.ShapeOffsetUnits, QgsProperty.fromExpression("'pixel'"))
f.updateDataDefinedProperties(context)
self.assertEqual(f.background().offsetUnit(), QgsUnitTypes.RenderPixels)
# shape radii
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.ShapeRadii, QgsProperty.fromExpression("'18,19'"))
f.updateDataDefinedProperties(context)
self.assertEqual(f.background().radii(), QSizeF(18, 19))
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.ShapeRadiiUnits, QgsProperty.fromExpression("'pixel'"))
f.updateDataDefinedProperties(context)
self.assertEqual(f.background().radiiUnit(), QgsUnitTypes.RenderPixels)
# shape opacity
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.ShapeOpacity, QgsProperty.fromExpression('37'))
f.updateDataDefinedProperties(context)
self.assertEqual(f.background().opacity(), 0.37)
# color
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.ShapeFillColor, QgsProperty.fromExpression("'#ff0088'"))
f.updateDataDefinedProperties(context)
self.assertEqual(f.background().fillColor().name(), '#ff0088')
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.ShapeStrokeColor, QgsProperty.fromExpression("'#8800ff'"))
f.updateDataDefinedProperties(context)
self.assertEqual(f.background().strokeColor().name(), '#8800ff')
# stroke width
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.ShapeStrokeWidth, QgsProperty.fromExpression('88'))
f.updateDataDefinedProperties(context)
self.assertEqual(f.background().strokeWidth(), 88)
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.ShapeStrokeWidthUnits, QgsProperty.fromExpression("'pixel'"))
f.updateDataDefinedProperties(context)
self.assertEqual(f.background().strokeWidthUnit(), QgsUnitTypes.RenderPixels)
# blend mode
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.ShapeBlendMode, QgsProperty.fromExpression("'burn'"))
f.updateDataDefinedProperties(context)
self.assertEqual(f.background().blendMode(), QPainter.CompositionMode_ColorBurn)
# join style
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.ShapeJoinStyle, QgsProperty.fromExpression("'miter'"))
f.updateDataDefinedProperties(context)
self.assertEqual(f.background().joinStyle(), Qt.MiterJoin)
def testDataDefinedShadowSettings(self):
f = QgsTextFormat()
context = QgsRenderContext()
# shadow enabled
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.ShadowDraw, QgsProperty.fromExpression('1'))
f.updateDataDefinedProperties(context)
self.assertTrue(f.shadow().enabled())
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.ShadowDraw, QgsProperty.fromExpression('0'))
context = QgsRenderContext()
f.updateDataDefinedProperties(context)
self.assertFalse(f.shadow().enabled())
# placement type
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.ShadowUnder, QgsProperty.fromExpression("'text'"))
f.updateDataDefinedProperties(context)
self.assertEqual(f.shadow().shadowPlacement(), QgsTextShadowSettings.ShadowText)
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.ShadowUnder, QgsProperty.fromExpression("'buffer'"))
f.updateDataDefinedProperties(context)
self.assertEqual(f.shadow().shadowPlacement(), QgsTextShadowSettings.ShadowBuffer)
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.ShadowUnder, QgsProperty.fromExpression("'background'"))
f.updateDataDefinedProperties(context)
self.assertEqual(f.shadow().shadowPlacement(), QgsTextShadowSettings.ShadowShape)
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.ShadowUnder, QgsProperty.fromExpression("'svg'"))
f.updateDataDefinedProperties(context)
self.assertEqual(f.shadow().shadowPlacement(), QgsTextShadowSettings.ShadowLowest)
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.ShadowUnder, QgsProperty.fromExpression("'lowest'"))
# offset angle
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.ShadowOffsetAngle, QgsProperty.fromExpression('67'))
f.updateDataDefinedProperties(context)
self.assertEqual(f.shadow().offsetAngle(), 67)
# offset distance
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.ShadowOffsetDist, QgsProperty.fromExpression('38'))
f.updateDataDefinedProperties(context)
self.assertEqual(f.shadow().offsetDistance(), 38)
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.ShadowOffsetUnits, QgsProperty.fromExpression("'pixel'"))
f.updateDataDefinedProperties(context)
self.assertEqual(f.shadow().offsetUnit(), QgsUnitTypes.RenderPixels)
# radius
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.ShadowRadius, QgsProperty.fromExpression('58'))
f.updateDataDefinedProperties(context)
self.assertEqual(f.shadow().blurRadius(), 58)
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.ShadowRadiusUnits, QgsProperty.fromExpression("'pixel'"))
f.updateDataDefinedProperties(context)
self.assertEqual(f.shadow().blurRadiusUnit(), QgsUnitTypes.RenderPixels)
# opacity
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.ShadowOpacity, QgsProperty.fromExpression('37'))
f.updateDataDefinedProperties(context)
self.assertEqual(f.shadow().opacity(), 0.37)
# color
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.ShadowColor, QgsProperty.fromExpression("'#ff0088'"))
f.updateDataDefinedProperties(context)
self.assertEqual(f.shadow().color().name(), '#ff0088')
# blend mode
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.ShadowBlendMode, QgsProperty.fromExpression("'burn'"))
f.updateDataDefinedProperties(context)
self.assertEqual(f.shadow().blendMode(), QPainter.CompositionMode_ColorBurn)
def testDataDefinedFormatSettings(self):
f = QgsTextFormat()
font = f.font()
font.setUnderline(True)
font.setStrikeOut(True)
font.setWordSpacing(5.7)
font.setLetterSpacing(QFont.AbsoluteSpacing, 3.3)
f.setFont(font)
context = QgsRenderContext()
# family
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.Family, QgsProperty.fromExpression("'{}'".format(QgsFontUtils.getStandardTestFont().family())))
f.updateDataDefinedProperties(context)
self.assertEqual(f.font().family(), QgsFontUtils.getStandardTestFont().family())
# style
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.FontStyle, QgsProperty.fromExpression("'Bold'"))
f.updateDataDefinedProperties(context)
self.assertEqual(f.font().styleName(), 'Bold')
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.FontStyle, QgsProperty.fromExpression("'Roman'"))
f.updateDataDefinedProperties(context)
self.assertEqual(f.font().styleName(), 'Roman')
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.Bold, QgsProperty.fromExpression("1"))
f.updateDataDefinedProperties(context)
self.assertTrue(f.font().bold())
self.assertFalse(f.font().italic())
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.Bold, QgsProperty.fromExpression("0"))
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.Italic, QgsProperty.fromExpression("1"))
f.updateDataDefinedProperties(context)
self.assertFalse(f.font().bold())
self.assertTrue(f.font().italic())
self.assertTrue(f.font().underline())
self.assertTrue(f.font().strikeOut())
self.assertAlmostEqual(f.font().wordSpacing(), 5.7, 1)
self.assertAlmostEqual(f.font().letterSpacing(), 3.3, 1)
# underline
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.Underline, QgsProperty.fromExpression("0"))
f.updateDataDefinedProperties(context)
self.assertFalse(f.font().underline())
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.Underline, QgsProperty.fromExpression("1"))
f.updateDataDefinedProperties(context)
self.assertTrue(f.font().underline())
# strikeout
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.Strikeout, QgsProperty.fromExpression("0"))
f.updateDataDefinedProperties(context)
self.assertFalse(f.font().strikeOut())
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.Strikeout, QgsProperty.fromExpression("1"))
f.updateDataDefinedProperties(context)
self.assertTrue(f.font().strikeOut())
# color
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.Color, QgsProperty.fromExpression("'#ff0088'"))
f.updateDataDefinedProperties(context)
self.assertEqual(f.color().name(), '#ff0088')
# size
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.Size, QgsProperty.fromExpression('38'))
f.updateDataDefinedProperties(context)
self.assertEqual(f.size(), 38)
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.FontSizeUnit, QgsProperty.fromExpression("'pixel'"))
f.updateDataDefinedProperties(context)
self.assertEqual(f.sizeUnit(), QgsUnitTypes.RenderPixels)
# opacity
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.FontOpacity, QgsProperty.fromExpression('37'))
f.updateDataDefinedProperties(context)
self.assertEqual(f.opacity(), 0.37)
# letter spacing
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.FontLetterSpacing, QgsProperty.fromExpression('58'))
f.updateDataDefinedProperties(context)
self.assertEqual(f.font().letterSpacing(), 58)
# word spacing
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.FontWordSpacing, QgsProperty.fromExpression('8'))
f.updateDataDefinedProperties(context)
self.assertEqual(f.font().wordSpacing(), 8)
# blend mode
f.dataDefinedProperties().setProperty(QgsPalLayerSettings.FontBlendMode, QgsProperty.fromExpression("'burn'"))
f.updateDataDefinedProperties(context)
self.assertEqual(f.blendMode(), QPainter.CompositionMode_ColorBurn)
def testFontFoundFromLayer(self):
layer = createEmptyLayer()
layer.setCustomProperty('labeling/fontFamily', 'asdasd')
@ -1733,6 +2051,35 @@ class PyQgsTextRenderer(unittest.TestCase):
assert self.checkRenderPoint(format, 'text_point_center_aligned', text=['test'],
alignment=QgsTextRenderer.AlignCenter, point=QPointF(200, 200))
def testDrawTextDataDefinedColorPoint(self):
format = QgsTextFormat()
format.setFont(getTestFont('bold'))
format.setSize(60)
format.setSizeUnit(QgsUnitTypes.RenderPoints)
format.setColor(QColor(0, 255, 0))
format.dataDefinedProperties().setProperty(QgsPalLayerSettings.Color, QgsProperty.fromExpression("'#bb00cc'"))
assert self.checkRenderPoint(format, 'text_dd_color_point', None, text=['test'], point=QPointF(50, 200))
def testDrawTextDataDefinedColorRect(self):
format = QgsTextFormat()
format.setFont(getTestFont('bold'))
format.setSize(60)
format.setSizeUnit(QgsUnitTypes.RenderPoints)
format.setColor(QColor(0, 255, 0))
format.dataDefinedProperties().setProperty(QgsPalLayerSettings.Color, QgsProperty.fromExpression("'#bb00cc'"))
assert self.checkRender(format, 'text_dd_color_rect', None, text=['test'], alignment=QgsTextRenderer.AlignCenter, rect=QRectF(100, 100, 100, 100))
def testDrawTextDataDefinedBufferColorPoint(self):
format = QgsTextFormat()
format.setFont(getTestFont('bold'))
format.setSize(60)
format.setSizeUnit(QgsUnitTypes.RenderPoints)
format.setColor(QColor(0, 255, 0))
format.dataDefinedProperties().setProperty(QgsPalLayerSettings.BufferColor, QgsProperty.fromExpression("'#bb00cc'"))
format.buffer().setEnabled(True)
format.buffer().setSize(5)
assert self.checkRenderPoint(format, 'text_dd_buffer_color', None, text=['test'], point=QPointF(50, 200))
def testTextRenderFormat(self):
format = QgsTextFormat()
format.setFont(getTestFont('bold'))

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB