32#include <QApplication>
35#include <QFontMetrics>
40#include <QTextBoundaryFinder>
98 if ( !sPropertyDefinitions()->isEmpty() )
101 const QString origin = QStringLiteral(
"labeling" );
113 "e.g. Helvetica or Helvetica [Cronyx]" ), origin )
117 "e.g. Bold Condensed or Light Italic" ), origin )
158 "<b>Ellipse</b>|<b>Circle</b>|<b>SVG</b>]" ), origin )
182 "<b>Buffer</b>|<b>Background</b>]" ), origin )
198 "<b>3</b>=Left|<b>4</b>=Over|<b>5</b>=Right|<br>"
199 "<b>6</b>=Below Left|<b>7</b>=Below|<b>8</b>=Below Right]" ), origin )
219 + QStringLiteral(
"[<b>TL</b>=Top left|<b>TSL</b>=Top, slightly left|<b>T</b>=Top middle|<br>"
220 "<b>TSR</b>=Top, slightly right|<b>TR</b>=Top right|<br>"
221 "<b>L</b>=Left|<b>R</b>=Right|<br>"
222 "<b>BL</b>=Bottom left|<b>BSL</b>=Bottom, slightly left|<b>B</b>=Bottom middle|<br>"
223 "<b>BSR</b>=Bottom, slightly right|<b>BR</b>=Bottom right]" ), origin )
227 + QStringLiteral(
"[<b>OL</b>=On line|<b>AL</b>=Above line|<b>BL</b>=Below line|<br>"
228 "<b>LO</b>=Respect line orientation]" ), origin )
237 "<b>Half</b>|<b>Cap</b>|<b>Top</b>]" ), origin )
262 : predefinedPositionOrder( *DEFAULT_PLACEMENT_ORDER() )
265 initPropertyDefinitions();
274 , mDataDefinedProperties( s.mDataDefinedProperties )
309 mPolygonPlacementFlags = s.mPolygonPlacementFlags;
325 mRotationUnit = s.mRotationUnit;
346 mDataDefinedProperties = s.mDataDefinedProperties;
348 mCallout.reset( s.mCallout ? s.mCallout->clone() :
nullptr );
350 mPlacementSettings = s.mPlacementSettings;
351 mLineSettings = s.mLineSettings;
352 mObstacleSettings = s.mObstacleSettings;
353 mThinningSettings = s.mThinningSettings;
360 mLegendString = s.mLegendString;
362 mUnplacedVisibility = s.mUnplacedVisibility;
412 for (
const QString &name : referencedColumns )
414 attributeNames.insert( name );
473 for (
const QString &name : referencedColumns )
475 attributeNames.insert( name );
482 const auto referencedColumns = mCallout->referencedFields( context );
483 for (
const QString &name : referencedColumns )
485 attributeNames.insert( name );
494 QSet<QString> referenced;
522 referenced.unite( mCallout->referencedFields( context ) );
530 if ( mRenderStarted )
532 qWarning(
"Start render called for when a previous render was already underway!!" );
545 mCallout->startRender( context );
548 mRenderStarted =
true;
553 if ( !mRenderStarted )
555 qWarning(
"Stop render called for QgsPalLayerSettings without a startRender call!" );
561 mCallout->stopRender( context );
564 mRenderStarted =
false;
574 if ( mRenderStarted )
576 qWarning(
"stopRender was not called on QgsPalLayerSettings object!" );
587 initPropertyDefinitions();
588 return *sPropertyDefinitions();
602 return mRotationUnit;
607 mRotationUnit = angleUnit;
613 QString newValue = value;
614 if ( !value.isEmpty() && !value.contains( QLatin1String(
"~~" ) ) )
617 values << QStringLiteral(
"1" );
618 values << QStringLiteral(
"0" );
621 newValue = values.join( QLatin1String(
"~~" ) );
629 QString newPropertyName =
"labeling/dataDefined/" + sPropertyDefinitions()->value( p ).name();
630 QVariant newPropertyField = layer->
customProperty( newPropertyName, QVariant() );
632 if ( !newPropertyField.isValid() )
635 QString ddString = newPropertyField.toString();
637 if ( !ddString.isEmpty() && ddString != QLatin1String(
"0~~0~~~~" ) )
641 QStringList ddv = newStyleString.split( QStringLiteral(
"~~" ) );
643 bool active = ddv.at( 0 ).toInt();
644 if ( ddv.at( 1 ).toInt() )
660void QgsPalLayerSettings::readOldDataDefinedPropertyMap(
QgsVectorLayer *layer, QDomElement *parentElem )
662 if ( !layer && !parentElem )
667 QgsPropertiesDefinition::const_iterator i = sPropertyDefinitions()->constBegin();
668 for ( ; i != sPropertyDefinitions()->constEnd(); ++i )
673 readOldDataDefinedProperty( layer,
static_cast< Property >( i.key() ) );
675 else if ( parentElem )
678 QDomElement e = parentElem->firstChildElement( i.value().name() );
681 bool active = e.attribute( QStringLiteral(
"active" ) ).compare( QLatin1String(
"true" ), Qt::CaseInsensitive ) == 0;
682 bool isExpression = e.attribute( QStringLiteral(
"useExpr" ) ).compare( QLatin1String(
"true" ), Qt::CaseInsensitive ) == 0;
696void QgsPalLayerSettings::readFromLayerCustomProperties(
QgsVectorLayer *layer )
698 if ( layer->
customProperty( QStringLiteral(
"labeling" ) ).toString() != QLatin1String(
"pal" ) )
723 QDomDocument doc( QStringLiteral(
"substitutions" ) );
724 doc.setContent( layer->
customProperty( QStringLiteral(
"labeling/substitutions" ) ).toString() );
725 QDomElement replacementElem = doc.firstChildElement( QStringLiteral(
"substitutions" ) );
746 mLineSettings.
setPlacementFlags(
static_cast< QgsLabeling::LinePlacementFlags
>( layer->
customProperty( QStringLiteral(
"labeling/placementFlags" ) ).toUInt() ) );
755 if ( layer->
customProperty( QStringLiteral(
"labeling/distMapUnitScale" ) ).toString().isEmpty() )
758 double oldMin = layer->
customProperty( QStringLiteral(
"labeling/distMapUnitMinScale" ), 0.0 ).toDouble();
760 double oldMax = layer->
customProperty( QStringLiteral(
"labeling/distMapUnitMaxScale" ), 0.0 ).toDouble();
771 if ( layer->
customProperty( QStringLiteral(
"labeling/labelOffsetInMapUnits" ), QVariant(
true ) ).toBool() )
776 if ( layer->
customProperty( QStringLiteral(
"labeling/labelOffsetMapUnitScale" ) ).toString().isEmpty() )
779 double oldMin = layer->
customProperty( QStringLiteral(
"labeling/labelOffsetMapUnitMinScale" ), 0.0 ).toDouble();
781 double oldMax = layer->
customProperty( QStringLiteral(
"labeling/labelOffsetMapUnitMaxScale" ), 0.0 ).toDouble();
789 QVariant tempAngle = layer->
customProperty( QStringLiteral(
"labeling/angleOffset" ), QVariant() );
790 if ( tempAngle.isValid() )
792 double oldAngle = layer->
customProperty( QStringLiteral(
"labeling/angleOffset" ), QVariant( 0.0 ) ).toDouble();
806 switch ( layer->
customProperty( QStringLiteral(
"labeling/repeatDistanceUnit" ), QVariant( 1 ) ).toUInt() )
821 if ( layer->
customProperty( QStringLiteral(
"labeling/repeatDistanceMapUnitScale" ) ).toString().isEmpty() )
824 double oldMin = layer->
customProperty( QStringLiteral(
"labeling/repeatDistanceMapUnitMinScale" ), 0.0 ).toDouble();
826 double oldMax = layer->
customProperty( QStringLiteral(
"labeling/repeatDistanceMapUnitMaxScale" ), 0.0 ).toDouble();
835 double scalemn = layer->
customProperty( QStringLiteral(
"labeling/scaleMin" ), QVariant( 0 ) ).toDouble();
836 double scalemx = layer->
customProperty( QStringLiteral(
"labeling/scaleMax" ), QVariant( 0 ) ).toDouble();
839 QVariant scalevis = layer->
customProperty( QStringLiteral(
"labeling/scaleVisibility" ), QVariant() );
840 if ( scalevis.isValid() )
846 else if ( scalemn > 0 || scalemx > 0 )
862 if ( layer->
customProperty( QStringLiteral(
"labeling/displayAll" ), QVariant(
false ) ).toBool() )
880 mObstacleSettings.
setFactor( layer->
customProperty( QStringLiteral(
"labeling/obstacleFactor" ), QVariant( 1.0 ) ).toDouble() );
882 zIndex = layer->
customProperty( QStringLiteral(
"labeling/zIndex" ), QVariant( 0.0 ) ).toDouble();
884 mDataDefinedProperties.
clear();
885 if ( layer->
customProperty( QStringLiteral(
"labeling/ddProperties" ) ).isValid() )
887 QDomDocument doc( QStringLiteral(
"dd" ) );
888 doc.setContent( layer->
customProperty( QStringLiteral(
"labeling/ddProperties" ) ).toString() );
889 QDomElement elem = doc.firstChildElement( QStringLiteral(
"properties" ) );
890 mDataDefinedProperties.
readXml( elem, *sPropertyDefinitions() );
895 readOldDataDefinedPropertyMap( layer,
nullptr );
939 QDomElement textStyleElem = elem.firstChildElement( QStringLiteral(
"text-style" ) );
940 fieldName = textStyleElem.attribute( QStringLiteral(
"fieldName" ) );
941 isExpression = textStyleElem.attribute( QStringLiteral(
"isExpression" ) ).toInt();
943 mFormat.
readXml( elem, context );
945 previewBkgrdColor = QColor( textStyleElem.attribute( QStringLiteral(
"previewBkgrdColor" ), QStringLiteral(
"#ffffff" ) ) );
948 useSubstitutions = textStyleElem.attribute( QStringLiteral(
"useSubstitutions" ) ).toInt();
949 mLegendString = textStyleElem.attribute( QStringLiteral(
"legendString" ), QObject::tr(
"Aa" ) );
952 QDomElement textFormatElem = elem.firstChildElement( QStringLiteral(
"text-format" ) );
953 wrapChar = textFormatElem.attribute( QStringLiteral(
"wrapChar" ) );
954 autoWrapLength = textFormatElem.attribute( QStringLiteral(
"autoWrapLength" ), QStringLiteral(
"0" ) ).toInt();
955 useMaxLineLengthForAutoWrap = textFormatElem.attribute( QStringLiteral(
"useMaxLineLengthForAutoWrap" ), QStringLiteral(
"1" ) ).toInt();
956 multilineAlign =
static_cast< Qgis::LabelMultiLineAlignment >( textFormatElem.attribute( QStringLiteral(
"multilineAlign" ), QString::number(
static_cast< int >( Qgis::LabelMultiLineAlignment::FollowPlacement ) ) ).toUInt() );
957 mLineSettings.
setAddDirectionSymbol( textFormatElem.attribute( QStringLiteral(
"addDirectionSymbol" ) ).toInt() );
958 mLineSettings.
setLeftDirectionSymbol( textFormatElem.attribute( QStringLiteral(
"leftDirectionSymbol" ), QStringLiteral(
"<" ) ) );
959 mLineSettings.
setRightDirectionSymbol( textFormatElem.attribute( QStringLiteral(
"rightDirectionSymbol" ), QStringLiteral(
">" ) ) );
960 mLineSettings.
setReverseDirectionSymbol( textFormatElem.attribute( QStringLiteral(
"reverseDirectionSymbol" ) ).toInt() );
962 formatNumbers = textFormatElem.attribute( QStringLiteral(
"formatNumbers" ) ).toInt();
963 decimals = textFormatElem.attribute( QStringLiteral(
"decimals" ) ).toInt();
964 plusSign = textFormatElem.attribute( QStringLiteral(
"plussign" ) ).toInt();
967 QDomElement placementElem = elem.firstChildElement( QStringLiteral(
"placement" ) );
969 mLineSettings.
setPlacementFlags(
static_cast< QgsLabeling::LinePlacementFlags
>( placementElem.attribute( QStringLiteral(
"placementFlags" ) ).toUInt() ) );
970 mPolygonPlacementFlags =
static_cast< QgsLabeling::PolygonPlacementFlags
>( placementElem.attribute( QStringLiteral(
"polygonPlacementFlags" ), QString::number(
static_cast< int >( QgsLabeling::PolygonPlacementFlag::AllowPlacementInsideOfPolygon ) ) ).toInt() );
972 centroidWhole = placementElem.attribute( QStringLiteral(
"centroidWhole" ), QStringLiteral(
"0" ) ).toInt();
973 centroidInside = placementElem.attribute( QStringLiteral(
"centroidInside" ), QStringLiteral(
"0" ) ).toInt();
977 fitInPolygonOnly = placementElem.attribute( QStringLiteral(
"fitInPolygonOnly" ), QStringLiteral(
"0" ) ).toInt();
978 dist = placementElem.attribute( QStringLiteral(
"dist" ) ).toDouble();
979 if ( !placementElem.hasAttribute( QStringLiteral(
"distUnits" ) ) )
981 if ( placementElem.attribute( QStringLiteral(
"distInMapUnits" ) ).toInt() )
990 if ( !placementElem.hasAttribute( QStringLiteral(
"distMapUnitScale" ) ) )
993 double oldMin = placementElem.attribute( QStringLiteral(
"distMapUnitMinScale" ), QStringLiteral(
"0" ) ).toDouble();
995 double oldMax = placementElem.attribute( QStringLiteral(
"distMapUnitMaxScale" ), QStringLiteral(
"0" ) ).toDouble();
1003 quadOffset =
static_cast< Qgis::LabelQuadrantPosition >( placementElem.attribute( QStringLiteral(
"quadOffset" ), QString::number(
static_cast< int >( Qgis::LabelQuadrantPosition::Over ) ) ).toUInt() );
1004 xOffset = placementElem.attribute( QStringLiteral(
"xOffset" ), QStringLiteral(
"0" ) ).toDouble();
1005 yOffset = placementElem.attribute( QStringLiteral(
"yOffset" ), QStringLiteral(
"0" ) ).toDouble();
1006 if ( !placementElem.hasAttribute( QStringLiteral(
"offsetUnits" ) ) )
1014 if ( !placementElem.hasAttribute( QStringLiteral(
"labelOffsetMapUnitScale" ) ) )
1017 double oldMin = placementElem.attribute( QStringLiteral(
"labelOffsetMapUnitMinScale" ), QStringLiteral(
"0" ) ).toDouble();
1019 double oldMax = placementElem.attribute( QStringLiteral(
"labelOffsetMapUnitMaxScale" ), QStringLiteral(
"0" ) ).toDouble();
1027 if ( placementElem.hasAttribute( QStringLiteral(
"angleOffset" ) ) )
1029 double oldAngle = placementElem.attribute( QStringLiteral(
"angleOffset" ), QStringLiteral(
"0" ) ).toDouble();
1034 angleOffset = placementElem.attribute( QStringLiteral(
"rotationAngle" ), QStringLiteral(
"0" ) ).toDouble();
1037 preserveRotation = placementElem.attribute( QStringLiteral(
"preserveRotation" ), QStringLiteral(
"1" ) ).toInt();
1039 maxCurvedCharAngleIn = placementElem.attribute( QStringLiteral(
"maxCurvedCharAngleIn" ), QStringLiteral(
"25" ) ).toDouble();
1040 maxCurvedCharAngleOut = placementElem.attribute( QStringLiteral(
"maxCurvedCharAngleOut" ), QStringLiteral(
"-25" ) ).toDouble();
1041 priority = placementElem.attribute( QStringLiteral(
"priority" ) ).toInt();
1042 repeatDistance = placementElem.attribute( QStringLiteral(
"repeatDistance" ), QStringLiteral(
"0" ) ).toDouble();
1043 if ( !placementElem.hasAttribute( QStringLiteral(
"repeatDistanceUnits" ) ) )
1046 switch ( placementElem.attribute( QStringLiteral(
"repeatDistanceUnit" ), QString::number( 1 ) ).toUInt() )
1066 if ( !placementElem.hasAttribute( QStringLiteral(
"repeatDistanceMapUnitScale" ) ) )
1069 double oldMin = placementElem.attribute( QStringLiteral(
"repeatDistanceMapUnitMinScale" ), QStringLiteral(
"0" ) ).toDouble();
1071 double oldMax = placementElem.attribute( QStringLiteral(
"repeatDistanceMapUnitMaxScale" ), QStringLiteral(
"0" ) ).toDouble();
1079 mLineSettings.
setOverrunDistance( placementElem.attribute( QStringLiteral(
"overrunDistance" ), QStringLiteral(
"0" ) ).toDouble() );
1082 mLineSettings.
setLineAnchorPercent( placementElem.attribute( QStringLiteral(
"lineAnchorPercent" ), QStringLiteral(
"0.5" ) ).toDouble() );
1088 geometryGenerator = placementElem.attribute( QStringLiteral(
"geometryGenerator" ) );
1094 mPlacementSettings.
setAllowDegradedPlacement( placementElem.attribute( QStringLiteral(
"allowDegraded" ), QStringLiteral(
"0" ) ).toInt() );
1097 QDomElement renderingElem = elem.firstChildElement( QStringLiteral(
"rendering" ) );
1099 drawLabels = renderingElem.attribute( QStringLiteral(
"drawLabels" ), QStringLiteral(
"1" ) ).toInt();
1101 maximumScale = renderingElem.attribute( QStringLiteral(
"scaleMin" ), QStringLiteral(
"0" ) ).toDouble();
1102 minimumScale = renderingElem.attribute( QStringLiteral(
"scaleMax" ), QStringLiteral(
"0" ) ).toDouble();
1103 scaleVisibility = renderingElem.attribute( QStringLiteral(
"scaleVisibility" ) ).toInt();
1105 fontLimitPixelSize = renderingElem.attribute( QStringLiteral(
"fontLimitPixelSize" ), QStringLiteral(
"0" ) ).toInt();
1106 fontMinPixelSize = renderingElem.attribute( QStringLiteral(
"fontMinPixelSize" ), QStringLiteral(
"0" ) ).toInt();
1107 fontMaxPixelSize = renderingElem.attribute( QStringLiteral(
"fontMaxPixelSize" ), QStringLiteral(
"10000" ) ).toInt();
1109 if ( placementElem.hasAttribute( QStringLiteral(
"overlapHandling" ) ) )
1116 if ( renderingElem.attribute( QStringLiteral(
"displayAll" ), QStringLiteral(
"0" ) ).toInt() )
1127 upsidedownLabels =
static_cast< Qgis::UpsideDownLabelHandling >( renderingElem.attribute( QStringLiteral(
"upsidedownLabels" ), QString::number(
static_cast< int >( Qgis::UpsideDownLabelHandling::FlipUpsideDownLabels ) ) ).toUInt() );
1129 labelPerPart = renderingElem.attribute( QStringLiteral(
"labelPerPart" ) ).toInt();
1130 mLineSettings.
setMergeLines( renderingElem.attribute( QStringLiteral(
"mergeLines" ) ).toInt() );
1131 mThinningSettings.
setMinimumFeatureSize( renderingElem.attribute( QStringLiteral(
"minFeatureSize" ) ).toDouble() );
1132 mThinningSettings.
setLimitNumberLabelsEnabled( renderingElem.attribute( QStringLiteral(
"limitNumLabels" ), QStringLiteral(
"0" ) ).toInt() );
1133 mThinningSettings.
setMaximumNumberLabels( renderingElem.attribute( QStringLiteral(
"maxNumLabels" ), QStringLiteral(
"2000" ) ).toInt() );
1134 mObstacleSettings.
setIsObstacle( renderingElem.attribute( QStringLiteral(
"obstacle" ), QStringLiteral(
"1" ) ).toInt() );
1135 mObstacleSettings.
setFactor( renderingElem.attribute( QStringLiteral(
"obstacleFactor" ), QStringLiteral(
"1" ) ).toDouble() );
1137 zIndex = renderingElem.attribute( QStringLiteral(
"zIndex" ), QStringLiteral(
"0.0" ) ).toDouble();
1140 QDomElement ddElem = elem.firstChildElement( QStringLiteral(
"dd_properties" ) );
1141 if ( !ddElem.isNull() )
1143 mDataDefinedProperties.
readXml( ddElem, *sPropertyDefinitions() );
1148 mDataDefinedProperties.
clear();
1149 QDomElement ddElem = elem.firstChildElement( QStringLiteral(
"data-defined" ) );
1150 readOldDataDefinedPropertyMap(
nullptr, &ddElem );
1191 const QString calloutType = elem.attribute( QStringLiteral(
"calloutType" ) );
1192 if ( calloutType.isEmpty() )
1204 QDomElement textStyleElem = mFormat.
writeXml( doc, context );
1207 textStyleElem.setAttribute( QStringLiteral(
"fieldName" ),
fieldName );
1208 textStyleElem.setAttribute( QStringLiteral(
"isExpression" ),
isExpression );
1209 QDomElement replacementElem = doc.createElement( QStringLiteral(
"substitutions" ) );
1211 textStyleElem.appendChild( replacementElem );
1212 textStyleElem.setAttribute( QStringLiteral(
"useSubstitutions" ),
useSubstitutions );
1213 textStyleElem.setAttribute( QStringLiteral(
"legendString" ), mLegendString );
1216 QDomElement textFormatElem = doc.createElement( QStringLiteral(
"text-format" ) );
1217 textFormatElem.setAttribute( QStringLiteral(
"wrapChar" ),
wrapChar );
1218 textFormatElem.setAttribute( QStringLiteral(
"autoWrapLength" ),
autoWrapLength );
1220 textFormatElem.setAttribute( QStringLiteral(
"multilineAlign" ),
static_cast< unsigned int >(
multilineAlign ) );
1221 textFormatElem.setAttribute( QStringLiteral(
"addDirectionSymbol" ), mLineSettings.
addDirectionSymbol() );
1222 textFormatElem.setAttribute( QStringLiteral(
"leftDirectionSymbol" ), mLineSettings.
leftDirectionSymbol() );
1223 textFormatElem.setAttribute( QStringLiteral(
"rightDirectionSymbol" ), mLineSettings.
rightDirectionSymbol() );
1224 textFormatElem.setAttribute( QStringLiteral(
"reverseDirectionSymbol" ), mLineSettings.
reverseDirectionSymbol() );
1225 textFormatElem.setAttribute( QStringLiteral(
"placeDirectionSymbol" ),
static_cast< unsigned int >( mLineSettings.
directionSymbolPlacement() ) );
1226 textFormatElem.setAttribute( QStringLiteral(
"formatNumbers" ),
formatNumbers );
1227 textFormatElem.setAttribute( QStringLiteral(
"decimals" ),
decimals );
1228 textFormatElem.setAttribute( QStringLiteral(
"plussign" ),
plusSign );
1231 QDomElement placementElem = doc.createElement( QStringLiteral(
"placement" ) );
1232 placementElem.setAttribute( QStringLiteral(
"placement" ),
static_cast< int >(
placement ) );
1233 placementElem.setAttribute( QStringLiteral(
"polygonPlacementFlags" ),
static_cast< int >( mPolygonPlacementFlags ) );
1234 placementElem.setAttribute( QStringLiteral(
"placementFlags" ),
static_cast< unsigned int >( mLineSettings.
placementFlags() ) );
1235 placementElem.setAttribute( QStringLiteral(
"centroidWhole" ),
centroidWhole );
1236 placementElem.setAttribute( QStringLiteral(
"centroidInside" ),
centroidInside );
1238 placementElem.setAttribute( QStringLiteral(
"fitInPolygonOnly" ),
fitInPolygonOnly );
1239 placementElem.setAttribute( QStringLiteral(
"dist" ),
dist );
1242 placementElem.setAttribute( QStringLiteral(
"offsetType" ),
static_cast< unsigned int >(
offsetType ) );
1243 placementElem.setAttribute( QStringLiteral(
"quadOffset" ),
static_cast< unsigned int >(
quadOffset ) );
1244 placementElem.setAttribute( QStringLiteral(
"xOffset" ),
xOffset );
1245 placementElem.setAttribute( QStringLiteral(
"yOffset" ),
yOffset );
1248 placementElem.setAttribute( QStringLiteral(
"rotationAngle" ),
angleOffset );
1249 placementElem.setAttribute( QStringLiteral(
"preserveRotation" ),
preserveRotation );
1250 placementElem.setAttribute( QStringLiteral(
"rotationUnit" ),
qgsEnumValueToKey( mRotationUnit ) );
1253 placementElem.setAttribute( QStringLiteral(
"priority" ),
priority );
1254 placementElem.setAttribute( QStringLiteral(
"repeatDistance" ),
repeatDistance );
1257 placementElem.setAttribute( QStringLiteral(
"overrunDistance" ), mLineSettings.
overrunDistance() );
1260 placementElem.setAttribute( QStringLiteral(
"lineAnchorPercent" ), mLineSettings.
lineAnchorPercent() );
1261 placementElem.setAttribute( QStringLiteral(
"lineAnchorType" ),
static_cast< int >( mLineSettings.
anchorType() ) );
1262 placementElem.setAttribute( QStringLiteral(
"lineAnchorClipping" ),
static_cast< int >( mLineSettings.
anchorClipping() ) );
1265 placementElem.setAttribute( QStringLiteral(
"geometryGenerator" ),
geometryGenerator );
1267 const QMetaEnum metaEnum( QMetaEnum::fromType<QgsWkbTypes::GeometryType>() );
1268 placementElem.setAttribute( QStringLiteral(
"geometryGeneratorType" ), metaEnum.valueToKey(
geometryGeneratorType ) );
1270 placementElem.setAttribute( QStringLiteral(
"layerType" ), metaEnum.valueToKey(
layerType ) );
1273 placementElem.setAttribute( QStringLiteral(
"allowDegraded" ), mPlacementSettings.
allowDegradedPlacement() ? QStringLiteral(
"1" ) : QStringLiteral(
"0" ) );
1276 QDomElement renderingElem = doc.createElement( QStringLiteral(
"rendering" ) );
1277 renderingElem.setAttribute( QStringLiteral(
"drawLabels" ),
drawLabels );
1278 renderingElem.setAttribute( QStringLiteral(
"scaleVisibility" ),
scaleVisibility );
1279 renderingElem.setAttribute( QStringLiteral(
"scaleMin" ),
maximumScale );
1280 renderingElem.setAttribute( QStringLiteral(
"scaleMax" ),
minimumScale );
1281 renderingElem.setAttribute( QStringLiteral(
"fontLimitPixelSize" ),
fontLimitPixelSize );
1282 renderingElem.setAttribute( QStringLiteral(
"fontMinPixelSize" ),
fontMinPixelSize );
1283 renderingElem.setAttribute( QStringLiteral(
"fontMaxPixelSize" ),
fontMaxPixelSize );
1284 renderingElem.setAttribute( QStringLiteral(
"upsidedownLabels" ),
static_cast< unsigned int >(
upsidedownLabels ) );
1286 renderingElem.setAttribute( QStringLiteral(
"labelPerPart" ),
labelPerPart );
1287 renderingElem.setAttribute( QStringLiteral(
"mergeLines" ), mLineSettings.
mergeLines() );
1288 renderingElem.setAttribute( QStringLiteral(
"minFeatureSize" ), mThinningSettings.
minimumFeatureSize() );
1290 renderingElem.setAttribute( QStringLiteral(
"maxNumLabels" ), mThinningSettings.
maximumNumberLabels() );
1291 renderingElem.setAttribute( QStringLiteral(
"obstacle" ), mObstacleSettings.
isObstacle() );
1292 renderingElem.setAttribute( QStringLiteral(
"obstacleFactor" ), mObstacleSettings.
factor() );
1293 renderingElem.setAttribute( QStringLiteral(
"obstacleType" ),
static_cast< unsigned int >( mObstacleSettings.
type() ) );
1294 renderingElem.setAttribute( QStringLiteral(
"zIndex" ),
zIndex );
1295 renderingElem.setAttribute( QStringLiteral(
"unplacedVisibility" ),
static_cast< int >( mUnplacedVisibility ) );
1297 QDomElement ddElem = doc.createElement( QStringLiteral(
"dd_properties" ) );
1298 mDataDefinedProperties.
writeXml( ddElem, *sPropertyDefinitions() );
1300 QDomElement elem = doc.createElement( QStringLiteral(
"settings" ) );
1301 elem.appendChild( textStyleElem );
1302 elem.appendChild( textFormatElem );
1303 elem.appendChild( placementElem );
1304 elem.appendChild( renderingElem );
1305 elem.appendChild( ddElem );
1309 elem.setAttribute( QStringLiteral(
"calloutType" ), mCallout->type() );
1310 mCallout->saveProperties( doc, elem, context );
1325 QPixmap pixmap( size );
1326 pixmap.fill( Qt::transparent );
1328 painter.begin( &pixmap );
1330 painter.setRenderHint( QPainter::Antialiasing );
1332 QRect rect( 0, 0, size.width(), size.height() );
1335 painter.setPen( Qt::NoPen );
1337 if ( ( background1.lightnessF() < 0.7 ) )
1339 background1 = background1.darker( 125 );
1343 background1 = background1.lighter( 125 );
1346 QLinearGradient linearGrad( QPointF( 0, 0 ), QPointF( 0, rect.height() ) );
1347 linearGrad.setColorAt( 0, background1 );
1348 linearGrad.setColorAt( 1, background2 );
1349 painter.setBrush( QBrush( linearGrad ) );
1350 if ( size.width() > 30 )
1352 painter.drawRoundedRect( rect, 6, 6 );
1357 painter.drawRect( rect );
1359 painter.setBrush( Qt::NoBrush );
1360 painter.setPen( Qt::NoPen );
1369 QWidget *activeWindow = QApplication::activeWindow();
1370 const double logicalDpiX = activeWindow && activeWindow->screen() ? activeWindow->screen()->logicalDotsPerInchX() : 96.0;
1380 ? fontSize * tempFormat.
buffer().
size() / 100
1385 double ytrans = 0.0;
1388 ? fontSize * tempFormat.
buffer().
size() / 100
1393 const QStringList text = QStringList() << ( previewText.isEmpty() ? settings.
legendString() : previewText );
1395 QRectF textRect = rect;
1396 textRect.setLeft( xtrans + padding );
1397 textRect.setWidth( rect.width() - xtrans - 2 * padding );
1399 if ( textRect.width() > 2000 )
1400 textRect.setWidth( 2000 - 2 * padding );
1402 const double bottom = textRect.height() / 2 + textHeight / 2;
1403 textRect.setTop( bottom - textHeight );
1404 textRect.setBottom( bottom );
1415 QRectF labelRect( textRect.left() + ( textRect.width() - textWidth ) / 2.0, textRect.top(), textWidth, textRect.height() );
1422 if ( size.width() > 30 )
1427 rect.width() - iconWidth * 3, rect.height() - iconWidth * 3,
1428 iconWidth * 2, iconWidth * 2 ), Qt::AlignRight | Qt::AlignBottom );
1432 painter.setBrush( Qt::NoBrush );
1434 if ( size.width() > 30 )
1436 painter.drawRoundedRect( rect, 6, 6 );
1441 painter.drawRect( rect );
1450 return mUnplacedVisibility;
1455 mUnplacedVisibility = visibility;
1460 return QgsPalLabeling::checkMinimumSizeMM(
ct, geom, minSize );
1463void QgsPalLayerSettings::calculateLabelSize(
const QFontMetricsF *fm,
const QString &text,
double &labelX,
double &labelY,
const QgsFeature *f,
QgsRenderContext *context,
double *rotatedLabelX,
double *rotatedLabelY,
QgsTextDocument *document,
QgsTextDocumentMetrics *documentMetrics, QRectF *outerBounds )
1470 QString textCopy( text );
1473 std::unique_ptr< QgsRenderContext > scopedRc;
1478 scopedRc->expressionContext().setFeature( *f );
1594 if ( wrapchr.isEmpty() )
1596 wrapchr = QStringLiteral(
"\n" );
1601 && ( !leftDirSymb.isEmpty() || !rightDirSymb.isEmpty() ) )
1603 QString dirSym = leftDirSymb;
1605 if ( fm->horizontalAdvance( rightDirSymb ) > fm->horizontalAdvance( dirSym ) )
1606 dirSym = rightDirSymb;
1608 switch ( placeDirSymb )
1611 textCopy.append( dirSym );
1616 textCopy.prepend( dirSym + QStringLiteral(
"\n" ) );
1621 double w = 0.0, h = 0.0, rw = 0.0, rh = 0.0;
1622 double labelHeight = fm->ascent() + fm->descent();
1629 const QSizeF size = documentMetrics->
documentSize( Qgis::TextLayoutMode::Labeling, orientation );
1636 const int lines = multiLineSplit.size();
1640 switch ( orientation )
1642 case Qgis::TextOrientation::Horizontal:
1646 for (
const QString &line : std::as_const( multiLineSplit ) )
1648 w = std::max( w, fm->horizontalAdvance( line ) );
1653 case Qgis::TextOrientation::Vertical:
1655 double letterSpacing = mFormat.
scaledFont( *context ).letterSpacing();
1656 double labelWidth = fm->maxWidth();
1659 int maxLineLength = 0;
1660 for (
const QString &line : std::as_const( multiLineSplit ) )
1662 maxLineLength = std::max( maxLineLength,
static_cast<int>( line.length() ) );
1664 h = fm->ascent() * maxLineLength + ( maxLineLength - 1 ) * letterSpacing;
1668 case Qgis::TextOrientation::RotationBased:
1670 double widthHorizontal = 0.0;
1671 for (
const QString &line : std::as_const( multiLineSplit ) )
1673 widthHorizontal = std::max( w, fm->horizontalAdvance( line ) );
1676 double widthVertical = 0.0;
1677 double letterSpacing = mFormat.
scaledFont( *context ).letterSpacing();
1678 double labelWidth = fm->maxWidth();
1681 double heightHorizontal = 0.0;
1684 double heightVertical = 0.0;
1685 int maxLineLength = 0;
1686 for (
const QString &line : std::as_const( multiLineSplit ) )
1688 maxLineLength = std::max( maxLineLength,
static_cast<int>( line.length() ) );
1690 heightVertical = fm->ascent() * maxLineLength + ( maxLineLength - 1 ) * letterSpacing;
1692 w = widthHorizontal;
1693 rw = heightVertical;
1694 h = heightHorizontal;
1703 labelX = std::fabs( ptSize.
x() -
ptZero.
x() );
1704 labelY = std::fabs( ptSize.
y() -
ptZero.
y() );
1709 if ( rotatedLabelX && rotatedLabelY )
1711 *rotatedLabelX = rw * uPP;
1712 *rotatedLabelY = rh * uPP;
1716 if ( outerBounds && documentMetrics )
1718 const QRectF outerBoundsPixels = documentMetrics->
outerBounds( Qgis::TextLayoutMode::Labeling, orientation );
1720 *outerBounds = QRectF( outerBoundsPixels.left() * uPP,
1721 outerBoundsPixels.top() * uPP,
1722 outerBoundsPixels.width() * uPP,
1723 outerBoundsPixels.height() * uPP );
1738 bool isObstacle = mObstacleSettings.
isObstacle();
1746 return registerObstacleFeature( f, context, obstacleGeometry );
1761 if ( obstacleGeometry.
isNull() )
1774 dataDefinedValues.clear();
1791 if ( useScaleVisibility )
1804 maxScale = 1 / std::fabs( maxScale );
1823 minScale = 1 / std::fabs( minScale );
1832 QFont labelFont = mFormat.
font();
1840 QString units = exprVal.toString();
1841 if ( !units.isEmpty() )
1851 double fontSize = mFormat.
size();
1857 if ( fontSize <= 0.0 )
1864 if ( fontPixelSize < 1 )
1868 labelFont.setPixelSize( fontPixelSize );
1880 if ( fontMinPixel > labelFont.pixelSize() || labelFont.pixelSize() > fontMaxPixel )
1892 labelFont.setCapitalization( QFont::MixedCase );
1894 parseTextStyle( labelFont, fontunits, context );
1897 parseTextFormatting( context );
1898 parseTextBuffer( context );
1899 parseTextMask( context );
1900 parseShapeBackground( context );
1901 parseDropShadow( context );
1949 QString fcase = exprVal.toString().trimmed();
1950 QgsDebugMsgLevel( QStringLiteral(
"exprVal FontCase:%1" ).arg( fcase ), 4 );
1952 if ( !fcase.isEmpty() )
1954 if ( fcase.compare( QLatin1String(
"NoChange" ), Qt::CaseInsensitive ) == 0 )
1958 else if ( fcase.compare( QLatin1String(
"Upper" ), Qt::CaseInsensitive ) == 0 )
1962 else if ( fcase.compare( QLatin1String(
"Lower" ), Qt::CaseInsensitive ) == 0 )
1966 else if ( fcase.compare( QLatin1String(
"Capitalize" ), Qt::CaseInsensitive ) == 0 )
1970 else if ( fcase.compare( QLatin1String(
"Title" ), Qt::CaseInsensitive ) == 0 )
1974#if defined(HAS_KDE_QT5_SMALL_CAPS_FIX) || QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)
1975 else if ( fcase.compare( QLatin1String(
"SmallCaps" ), Qt::CaseInsensitive ) == 0 )
1979 else if ( fcase.compare( QLatin1String(
"AllSmallCaps" ), Qt::CaseInsensitive ) == 0 )
1995 if ( evalFormatNumbers )
1999 if ( decimalPlaces <= 0 )
2005 QVariant textV( labelText );
2007 double d = textV.toDouble( &ok );
2010 QString numberFormat;
2011 if ( d > 0 && signPlus )
2013 numberFormat.append(
'+' );
2015 numberFormat.append(
"%1" );
2016 labelText = numberFormat.arg( d, 0,
'f', decimalPlaces );
2021 std::unique_ptr<QFontMetricsF> labelFontMetrics(
new QFontMetricsF( labelFont ) );
2024 double rotatedLabelX;
2025 double rotatedLabelY;
2030 if (
format().allowHtmlFormatting() && !labelText.isEmpty() )
2034 calculateLabelSize( labelFontMetrics.get(), labelText, labelWidth, labelHeight,
mCurFeat, &context, &rotatedLabelX, &rotatedLabelY, &doc, &documentMetrics, &outerBounds );
2038 calculateLabelSize( labelFontMetrics.get(), labelText, labelWidth, labelHeight,
mCurFeat, &context, &rotatedLabelX, &rotatedLabelY,
nullptr,
nullptr, &outerBounds );
2043 double maxcharanglein = 20.0;
2044 double maxcharangleout = -20.0;
2059 maxcharanglein = std::clamp(
static_cast< double >( maxcharanglePt.x() ), 20.0, 60.0 );
2060 maxcharangleout = std::clamp(
static_cast< double >( maxcharanglePt.y() ), 20.0, 95.0 );
2064 maxcharangleout = -( std::fabs( maxcharangleout ) );
2074 QString
str = exprVal.toString().trimmed();
2077 if ( !
str.isEmpty() )
2079 if (
str.compare( QLatin1String(
"Visible" ), Qt::CaseInsensitive ) == 0 )
2081 wholeCentroid =
false;
2083 else if (
str.compare( QLatin1String(
"Whole" ), Qt::CaseInsensitive ) == 0 )
2085 wholeCentroid =
true;
2099 std::unique_ptr<QgsGeometry> scopedClonedGeom;
2105 geom = simplifier.
simplify( geom );
2122 bool doClip =
false;
2123 if ( !centroidPoly || !wholeCentroid )
2129 QgsLabeling::PolygonPlacementFlags polygonPlacement = mPolygonPlacementFlags;
2135 if ( dataDefinedOutside.type() == QVariant::String )
2137 const QString value = dataDefinedOutside.toString().trimmed();
2138 if ( value.compare( QLatin1String(
"force" ), Qt::CaseInsensitive ) == 0 )
2141 polygonPlacement &= ~static_cast< int >( QgsLabeling::PolygonPlacementFlag::AllowPlacementInsideOfPolygon );
2142 polygonPlacement |= QgsLabeling::PolygonPlacementFlag::AllowPlacementOutsideOfPolygon;
2144 else if ( value.compare( QLatin1String(
"yes" ), Qt::CaseInsensitive ) == 0 )
2147 polygonPlacement |= QgsLabeling::PolygonPlacementFlag::AllowPlacementOutsideOfPolygon;
2149 else if ( value.compare( QLatin1String(
"no" ), Qt::CaseInsensitive ) == 0 )
2152 polygonPlacement &= ~static_cast< int >( QgsLabeling::PolygonPlacementFlag::AllowPlacementOutsideOfPolygon );
2157 if ( dataDefinedOutside.toBool() )
2160 polygonPlacement |= QgsLabeling::PolygonPlacementFlag::AllowPlacementOutsideOfPolygon;
2165 polygonPlacement &= ~static_cast< int >( QgsLabeling::PolygonPlacementFlag::AllowPlacementOutsideOfPolygon );
2193 permissibleZone = geom;
2229 double minimumSize = 0.0;
2239 if ( !checkMinimumSizeMM( context, geom, featureThinningSettings.
minimumFeatureSize() ) )
2244 if ( !geos_geom_clone )
2273 bool layerDefinedRotation =
false;
2274 bool dataDefinedRotation =
false;
2275 double xPos = 0.0, yPos = 0.0,
angle = 0.0;
2276 double quadOffsetX = 0.0, quadOffsetY = 0.0;
2277 double offsetX = 0.0, offsetY = 0.0;
2289 bool ddFixedQuad =
false;
2298 int quadInt = exprVal.toInt( &ok );
2299 if ( ok && 0 <= quadInt && quadInt <= 8 )
2310 case Qgis::LabelQuadrantPosition::AboveLeft:
2314 case Qgis::LabelQuadrantPosition::Above:
2318 case Qgis::LabelQuadrantPosition::AboveRight:
2322 case Qgis::LabelQuadrantPosition::Left:
2326 case Qgis::LabelQuadrantPosition::Right:
2330 case Qgis::LabelQuadrantPosition::BelowLeft:
2334 case Qgis::LabelQuadrantPosition::Below:
2338 case Qgis::LabelQuadrantPosition::BelowRight:
2342 case Qgis::LabelQuadrantPosition::Over:
2369 QString units = exprVal.toString().trimmed();
2370 if ( !units.isEmpty() )
2376 offUnit = decodedUnits;
2391 layerDefinedRotation =
true;
2404 const double rotation = exprVal.toDouble( &ok );
2407 dataDefinedRotation =
true;
2415 angle = ( 360 - rotationDegrees ) * M_PI / 180.0;
2420 bool hasDataDefinedPosition =
false;
2422 bool ddPosition =
false;
2434 bool ddXPos =
false, ddYPos =
false;
2435 xPos = xPosProperty.toDouble( &ddXPos );
2436 yPos = yPosProperty.toDouble( &ddYPos );
2437 if ( ddXPos && ddYPos )
2438 hasDataDefinedPosition =
true;
2449 if ( pointPosProperty.userType() == QMetaType::type(
"QgsReferencedGeometry" ) )
2454 if ( !referencedGeometryPoint.
isNull()
2458 else if ( pointPosProperty.userType() == QMetaType::type(
"QgsGeometry" ) )
2465 hasDataDefinedPosition =
true;
2476 if ( hasDataDefinedPosition )
2479 if ( layerDefinedRotation && !dataDefinedRotation )
2490 QString haliString = exprVal.toString();
2491 if ( haliString.compare( QLatin1String(
"Center" ), Qt::CaseInsensitive ) == 0 )
2493 xdiff -= labelWidth / 2.0;
2495 else if ( haliString.compare( QLatin1String(
"Right" ), Qt::CaseInsensitive ) == 0 )
2497 xdiff -= labelWidth;
2508 QString valiString = exprVal.toString();
2509 if ( valiString.compare( QLatin1String(
"Bottom" ), Qt::CaseInsensitive ) != 0 )
2511 if ( valiString.compare( QLatin1String(
"Top" ), Qt::CaseInsensitive ) == 0 )
2513 ydiff -= labelHeight;;
2517 double descentRatio = labelFontMetrics->descent() / labelFontMetrics->height();
2518 if ( valiString.compare( QLatin1String(
"Base" ), Qt::CaseInsensitive ) == 0 )
2520 ydiff -= labelHeight * descentRatio;
2524 double capHeightRatio = ( labelFontMetrics->boundingRect(
'H' ).height() + 1 + labelFontMetrics->descent() ) / labelFontMetrics->height();
2525 ydiff -= labelHeight * capHeightRatio;
2526 if ( valiString.compare( QLatin1String(
"Half" ), Qt::CaseInsensitive ) == 0 )
2528 ydiff += labelHeight * ( capHeightRatio - descentRatio ) / 2.0;
2536 if ( dataDefinedRotation )
2539 double xd = xdiff * std::cos(
angle ) - ydiff * std::sin(
angle );
2540 double yd = xdiff * std::sin(
angle ) + ydiff * std::cos(
angle );
2550 if (
const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( ddPoint.
constGet() ) )
2558 QgsMessageLog::logMessage( QObject::tr(
"Invalid data defined label position (%1, %2)" ).arg( xPos ).arg( yPos ), QObject::tr(
"Labeling" ) );
2559 hasDataDefinedPosition =
false;
2578 bool alwaysShow =
false;
2600 QString units = exprVal.toString().trimmed();
2601 if ( !units.isEmpty() )
2607 repeatUnits = decodedUnits;
2633 bool labelAll =
labelPerPart && !hasDataDefinedPosition;
2634 if ( !hasDataDefinedPosition )
2644 std::unique_ptr< QgsTextLabelFeature > labelFeature = std::make_unique< QgsTextLabelFeature>( feature.
id(), std::move( geos_geom_clone ), QSizeF( labelWidth, labelHeight ) );
2645 labelFeature->setAnchorPosition( anchorPosition );
2646 labelFeature->setFeature( feature );
2647 labelFeature->setSymbol( symbol );
2648 labelFeature->setDocument( doc, documentMetrics );
2650 labelFeature->setRotatedSize( QSizeF( rotatedLabelX, rotatedLabelY ) );
2653 labelFeature->setHasFixedPosition( hasDataDefinedPosition );
2654 labelFeature->setFixedPosition(
QgsPointXY( xPos, yPos ) );
2656 labelFeature->setHasFixedAngle( dataDefinedRotation || ( !hasDataDefinedPosition && !
qgsDoubleNear(
angle, 0.0 ) ) );
2657 labelFeature->setFixedAngle(
angle );
2658 labelFeature->setQuadOffset( QPointF( quadOffsetX, quadOffsetY ) );
2659 labelFeature->setPositionOffset(
QgsPointXY( offsetX, offsetY ) );
2661 labelFeature->setAlwaysShow( alwaysShow );
2662 labelFeature->setRepeatDistance( repeatDist );
2663 labelFeature->setLabelText( labelText );
2664 labelFeature->setPermissibleZone( permissibleZone );
2665 labelFeature->setOverrunDistance( overrunDistanceEval );
2666 labelFeature->setOverrunSmoothDistance( overrunSmoothDist );
2670 labelFeature->setLabelAllParts( labelAll );
2672 labelFeature->setMinimumSize( minimumSize );
2676 labelFeature->setSymbolSize( QSizeF( obstacleGeometry.
boundingBox().
width(),
2680 if ( outerBounds.left() != 0 || outerBounds.top() != 0 || !
qgsDoubleNear( outerBounds.width(), labelWidth ) || !
qgsDoubleNear( outerBounds.height(), labelHeight ) )
2682 labelFeature->setOuterBounds( outerBounds );
2687 double topMargin = std::max( 0.25 * labelFontMetrics->ascent(), 0.0 );
2688 double bottomMargin = 1.0 + labelFontMetrics->descent();
2689 QgsMargins vm( 0.0, topMargin, 0.0, bottomMargin );
2691 labelFeature->setVisualMargin( vm );
2694 QgsDebugMsgLevel( QStringLiteral(
"PAL font stored definedFont: %1, Style: %2" ).arg( labelFont.toString(), labelFont.styleName() ), 4 );
2695 labelFeature->setDefinedFont( labelFont );
2697 labelFeature->setMaximumCharacterAngleInside( std::clamp( maxcharanglein, 20.0, 60.0 ) * M_PI / 180 );
2698 labelFeature->setMaximumCharacterAngleOutside( std::clamp( maxcharangleout, -95.0, -20.0 ) * M_PI / 180 );
2713 labelFeature->setTextMetrics(
QgsTextLabelFeature::calculateTextMetrics(
xform, context, labelFont, *labelFontMetrics, labelFont.letterSpacing(), labelFont.wordSpacing(), labelText,
format().allowHtmlFormatting() ? &doc :
nullptr,
format().allowHtmlFormatting() ? &documentMetrics :
nullptr ) );
2722 double distance =
dist;
2736 QString units = exprVal.toString().trimmed();
2737 QgsDebugMsgLevel( QStringLiteral(
"exprVal DistanceUnits:%1" ).arg( units ), 4 );
2738 if ( !units.isEmpty() )
2744 distUnit = decodedUnits;
2757 distance = ( distance < 0 ? -1 : 1 ) * std::max( std::fabs( distance ), 1.0 );
2765 distance = std::max( distance, 2.0 );
2771 labelFeature->setDistLabel( d );
2776 labelFeature->setHasFixedQuadrant(
true );
2781 labelFeature->setPolygonPlacementFlags( polygonPlacement );
2790 labelFeature->setZIndex( z );
2800 double priorityD = exprVal.toDouble( &ok );
2803 priorityD = std::clamp( priorityD, 0.0, 10.0 );
2804 priorityD = 1 - priorityD / 10.0;
2805 labelFeature->setPriority( priorityD );
2818 labelFeature->setAllowDegradedPlacement( allowDegradedPlacement );
2827 const QString cleanedString = handlingString.trimmed();
2828 if ( cleanedString.compare( QLatin1String(
"prevent" ), Qt::CaseInsensitive ) == 0 )
2830 else if ( cleanedString.compare( QLatin1String(
"allowifneeded" ), Qt::CaseInsensitive ) == 0 )
2832 else if ( cleanedString.compare( QLatin1String(
"alwaysallow" ), Qt::CaseInsensitive ) == 0 )
2835 labelFeature->setOverlapHandling( overlapHandling );
2842 labelFeature->setObstacleSettings( os );
2845 if ( positionOrder.isEmpty() )
2846 positionOrder = *DEFAULT_PLACEMENT_ORDER();
2852 if ( !dataDefinedOrder.isEmpty() )
2857 labelFeature->setPredefinedPositionOrder( positionOrder );
2860 labelFeature->setDataDefinedValues( dataDefinedValues );
2862 return labelFeature;
2870 if ( !obstacleGeometry.
isNull() )
2872 geom = obstacleGeometry;
2887 if ( ls->numPoints() < 2 )
2893 std::unique_ptr<QgsGeometry> scopedClonedGeom;
2899 geom = simplifier.
simplify( geom );
2903 std::unique_ptr<QgsGeometry> scopedPreparedGeom;
2911 if ( !geos_geom_clone )
2915 std::unique_ptr< QgsLabelFeature > obstacleFeature = std::make_unique< QgsLabelFeature >( f.
id(), std::move( geos_geom_clone ), QSizeF( 0, 0 ) );
2916 obstacleFeature->setFeature( f );
2921 obstacleFeature->setObstacleSettings( os );
2924 return obstacleFeature;
2927bool QgsPalLayerSettings::dataDefinedValEval( DataDefinedValueType valType,
2931 if ( !mDataDefinedProperties.
isActive( p ) )
2935 exprVal = mDataDefinedProperties.
value( p, context );
2942 bool bol = exprVal.toBool();
2943 dataDefinedValues.insert( p, QVariant( bol ) );
2949 int size = exprVal.toInt( &ok );
2953 dataDefinedValues.insert( p, QVariant( size ) );
2961 int size = exprVal.toInt( &ok );
2963 if ( ok && size > 0 )
2965 dataDefinedValues.insert( p, QVariant( size ) );
2973 double size = exprVal.toDouble( &ok );
2977 dataDefinedValues.insert( p, QVariant( size ) );
2985 double size = exprVal.toDouble( &ok );
2987 if ( ok && size > 0.0 )
2989 dataDefinedValues.insert( p, QVariant( size ) );
2997 double rot = exprVal.toDouble( &ok );
3000 if ( rot < -180.0 && rot >= -360 )
3004 if ( rot > 180.0 && rot <= 360 )
3008 if ( rot >= -180 && rot <= 180 )
3010 dataDefinedValues.insert( p, QVariant( rot ) );
3019 int size = exprVal.toInt( &ok );
3020 if ( ok && size >= 0 && size <= 100 )
3022 dataDefinedValues.insert( p, QVariant( size ) );
3029 QString
str = exprVal.toString();
3031 dataDefinedValues.insert( p, QVariant(
str ) );
3036 QString unitstr = exprVal.toString().trimmed();
3038 if ( !unitstr.isEmpty() )
3047 QString colorstr = exprVal.toString().trimmed();
3050 if ( color.isValid() )
3052 dataDefinedValues.insert( p, QVariant( color ) );
3059 QString joinstr = exprVal.toString().trimmed();
3061 if ( !joinstr.isEmpty() )
3070 QString blendstr = exprVal.toString().trimmed();
3072 if ( !blendstr.isEmpty() )
3085 dataDefinedValues.insert( p, res );
3096 dataDefinedValues.insert( p, res );
3106void QgsPalLayerSettings::parseTextStyle( QFont &labelFont,
3119 QString ddFontFamily;
3126 QString family = exprVal.toString().trimmed();
3127 QgsDebugMsgLevel( QStringLiteral(
"exprVal Font family:%1" ).arg( family ), 4 );
3130 if ( labelFont.family() != family )
3136 ddFontFamily = family;
3143 QString ddFontStyle;
3149 QString fontstyle = exprVal.toString().trimmed();
3150 QgsDebugMsgLevel( QStringLiteral(
"exprVal Font style:%1" ).arg( fontstyle ), 4 );
3151 ddFontStyle = fontstyle;
3156 bool ddBold =
false;
3164 bool ddItalic =
false;
3174 QFont appFont = QApplication::font();
3175 bool newFontBuilt =
false;
3176 if ( ddBold || ddItalic )
3179 newFont = QFont( !ddFontFamily.isEmpty() ? ddFontFamily : labelFont.family() );
3180 newFontBuilt =
true;
3181 newFont.setBold( ddBold );
3182 newFont.setItalic( ddItalic );
3184 else if ( !ddFontStyle.isEmpty()
3185 && ddFontStyle.compare( QLatin1String(
"Ignore" ), Qt::CaseInsensitive ) != 0 )
3187 if ( !ddFontFamily.isEmpty() )
3191 mFontDB = std::make_unique< QFontDatabase >();
3193 QFont styledfont = mFontDB->font( ddFontFamily, ddFontStyle, appFont.pointSize() );
3194 if ( appFont != styledfont )
3196 newFont = styledfont;
3197 newFontBuilt =
true;
3204 else if ( !ddFontFamily.isEmpty() )
3206 if ( ddFontStyle.compare( QLatin1String(
"Ignore" ), Qt::CaseInsensitive ) != 0 )
3210 mFontDB = std::make_unique< QFontDatabase >();
3211 QFont styledfont = mFontDB->font( ddFontFamily, mFormat.
namedStyle(), appFont.pointSize() );
3212 if ( appFont != styledfont )
3214 newFont = styledfont;
3215 newFontBuilt =
true;
3220 newFont = QFont( ddFontFamily );
3221 newFontBuilt =
true;
3229 newFont.setPixelSize( labelFont.pixelSize() );
3230 newFont.setUnderline( labelFont.underline() );
3231 newFont.setStrikeOut( labelFont.strikeOut() );
3232 newFont.setWordSpacing( labelFont.wordSpacing() );
3233 newFont.setLetterSpacing( QFont::AbsoluteSpacing, labelFont.letterSpacing() );
3235 labelFont = newFont;
3239 double wordspace = labelFont.wordSpacing();
3248 double letterspace = labelFont.letterSpacing();
3261 labelFont.setStrikeOut( strikeout );
3276 labelFont.setUnderline( underline );
3302 drawBuffer = exprVal.toBool();
3315 double bufrSize = buffer.
size();
3318 bufrSize = exprVal.toDouble();
3322 double bufferOpacity = buffer.
opacity() * 100;
3325 bufferOpacity = exprVal.toDouble();
3328 drawBuffer = ( drawBuffer && bufrSize > 0.0 && bufferOpacity > 0 );
3358 bool maskEnabled = mask.
enabled();
3361 maskEnabled = exprVal.toBool();
3370 double bufrSize = mask.
size();
3373 bufrSize = exprVal.toDouble();
3377 double opacity = mask.
opacity() * 100;
3380 opacity = exprVal.toDouble();
3383 maskEnabled = ( maskEnabled && bufrSize > 0.0 && opacity > 0 );
3408 wrapchr = exprVal.toString();
3414 evalAutoWrapLength = exprVal.toInt();
3427 QString
str = exprVal.toString().trimmed();
3430 if ( !
str.isEmpty() )
3435 if (
str.compare( QLatin1String(
"Center" ), Qt::CaseInsensitive ) == 0 )
3437 aligntype = Qgis::LabelMultiLineAlignment::Center;
3439 else if (
str.compare( QLatin1String(
"Right" ), Qt::CaseInsensitive ) == 0 )
3441 aligntype = Qgis::LabelMultiLineAlignment::Right;
3443 else if (
str.compare( QLatin1String(
"Follow" ), Qt::CaseInsensitive ) == 0 )
3445 aligntype = Qgis::LabelMultiLineAlignment::FollowPlacement;
3447 else if (
str.compare( QLatin1String(
"Justify" ), Qt::CaseInsensitive ) == 0 )
3449 aligntype = Qgis::LabelMultiLineAlignment::Justify;
3464 QString
str = exprVal.toString().trimmed();
3465 if ( !
str.isEmpty() )
3474 drawDirSymb = exprVal.toBool();
3489 QString
str = exprVal.toString().trimmed();
3492 if ( !
str.isEmpty() )
3497 if (
str.compare( QLatin1String(
"Above" ), Qt::CaseInsensitive ) == 0 )
3501 else if (
str.compare( QLatin1String(
"Below" ), Qt::CaseInsensitive ) == 0 )
3523 bool drawShape = background.
enabled();
3526 drawShape = exprVal.toBool();
3535 double shapeOpacity = background.
opacity() * 100;
3538 shapeOpacity = 100.0 * exprVal.toDouble();
3541 drawShape = ( drawShape && shapeOpacity > 0 );
3557 QString skind = exprVal.toString().trimmed();
3558 QgsDebugMsgLevel( QStringLiteral(
"exprVal ShapeKind:%1" ).arg( skind ), 4 );
3560 if ( !skind.isEmpty() )
3569 QString svgPath = background.
svgFile();
3576 QString svgfile = exprVal.toString().trimmed();
3577 QgsDebugMsgLevel( QStringLiteral(
"exprVal ShapeSVGFile:%1" ).arg( svgfile ), 4 );
3592 QString stype = exprVal.toString().trimmed();
3593 QgsDebugMsgLevel( QStringLiteral(
"exprVal ShapeSizeType:%1" ).arg( stype ), 4 );
3595 if ( !stype.isEmpty() )
3604 double ddShpSizeX = background.
size().width();
3607 ddShpSizeX = exprVal.toDouble();
3611 double ddShpSizeY = background.
size().height();
3614 ddShpSizeY = exprVal.toDouble();
3620 && ( svgPath.isEmpty()
3621 || ( !svgPath.isEmpty()
3623 && ddShpSizeX == 0.0 ) ) )
3631 && ddShpSizeX == 0.0 ) ) )
3638 && ( ddShpSizeX == 0.0 || ddShpSizeY == 0.0 ) )
3663 QString rotstr = exprVal.toString().trimmed();
3664 QgsDebugMsgLevel( QStringLiteral(
"exprVal ShapeRotationType:%1" ).arg( rotstr ), 4 );
3666 if ( !rotstr.isEmpty() )
3717 bool drawShadow = shadow.
enabled();
3720 drawShadow = exprVal.toBool();
3729 double shadowOpacity = shadow.
opacity() * 100;
3732 shadowOpacity = exprVal.toDouble();
3739 shadowOffDist = exprVal.toDouble();
3746 shadowRad = exprVal.toDouble();
3749 drawShadow = ( drawShadow && shadowOpacity > 0 && !( shadowOffDist == 0.0 && shadowRad == 0.0 ) );
3766 QString
str = exprVal.toString().trimmed();
3769 if ( !
str.isEmpty() )
3804 switch ( layer->
type() )
3808 const QgsVectorLayer *vl = qobject_cast< const QgsVectorLayer * >( layer );
3819 return !labeling->styles().empty();
3868QStringList
QgsPalLabeling::splitToLines(
const QString &text,
const QString &wrapCharacter,
const int autoWrapLength,
const bool useMaxLineLengthWhenAutoWrapping )
3870 QStringList multiLineSplit;
3871 if ( !wrapCharacter.isEmpty() && wrapCharacter != QLatin1String(
"\n" ) )
3874 const QStringList lines = text.split( wrapCharacter );
3875 for (
const QString &line : lines )
3877 multiLineSplit.append( line.split(
'\n' ) );
3882 multiLineSplit = text.split(
'\n' );
3886 if ( autoWrapLength != 0 )
3888 QStringList autoWrappedLines;
3889 autoWrappedLines.reserve( multiLineSplit.count() );
3890 for (
const QString &line : std::as_const( multiLineSplit ) )
3892 autoWrappedLines.append(
QgsStringUtils::wordWrap( line, autoWrapLength, useMaxLineLengthWhenAutoWrapping ).split(
'\n' ) );
3894 multiLineSplit = autoWrappedLines;
3896 return multiLineSplit;
3901 QStringList graphemes;
3902 QTextBoundaryFinder boundaryFinder( QTextBoundaryFinder::Grapheme, text );
3903 int currentBoundary = -1;
3904 int previousBoundary = 0;
3905 while ( ( currentBoundary = boundaryFinder.toNextBoundary() ) > 0 )
3907 graphemes << text.mid( previousBoundary, currentBoundary - previousBoundary );
3908 previousBoundary = currentBoundary;
3938 QgsDebugMsgLevel( QStringLiteral(
"Ignoring feature due to transformation exception" ), 4 );
3944 return std::isfinite( point.
x() ) && std::isfinite( point.
y() );
3948 cp->removeInvalidRings();
3950 else if (
QgsMultiSurface *ms = qgsgeometry_cast< QgsMultiSurface * >( geom.
get() ) )
3952 for (
int i = 0; i < ms->numGeometries(); ++i )
3954 if (
QgsCurvePolygon *cp = qgsgeometry_cast< QgsCurvePolygon * >( ms->geometryN( i ) ) )
3955 cp->removeInvalidRings();
3967 QgsDebugMsg( QStringLiteral(
"Error rotating geometry" ).arg( geom.
asWkt() ) );
3972 const bool mustClip = ( !clipGeometry.
isNull() &&
3976 bool mustClipExact =
false;
4000 QVector< QgsGeometry> parts;
4001 parts.reserve( qgsgeometry_cast< const QgsGeometryCollection * >( geom.
constGet() )->numGeometries() );
4010 parts.append( partGeom );
4018 if ( bufferGeom.
isNull() )
4020 QgsDebugMsg( QStringLiteral(
"Could not repair geometry: %1" ).arg( bufferGeom.
lastError() ) );
4027 if ( mustClipExact )
4062 double length = geom.
length();
4063 if ( length >= 0.0 )
4065 return ( length >= ( minSize * mapUnitsPerMM ) );
4070 double area = geom.
area();
4073 return ( std::sqrt( area ) >= ( minSize * mapUnitsPerMM ) );
4081 const QMap< QgsPalLayerSettings::Property, QVariant > &ddValues )
4084 bool changed =
false;
4090 format.
setColor( ddColor.value<QColor>() );
4115 const QMap< QgsPalLayerSettings::Property, QVariant > &ddValues )
4177 const QMap< QgsPalLayerSettings::Property, QVariant > &ddValues )
4180 bool changed =
false;
4228 buffer.
setColor( ddColor.value<QColor>() );
4255 const QMap< QgsPalLayerSettings::Property, QVariant > &ddValues )
4257 if ( ddValues.isEmpty() )
4261 bool changed =
false;
4321 const QMap< QgsPalLayerSettings::Property, QVariant > &ddValues )
4324 bool changed =
false;
4364 QSizeF size = background.
size();
4371 QSizeF size = background.
size();
4472 const QMap< QgsPalLayerSettings::Property, QVariant > &ddValues )
4475 bool changed =
false;
4534 shadow.
setColor( ddColor.value<QColor>() );
4574 double cx = lp->
getX() + w / 2.0;
4575 double cy = lp->
getY() + h / 2.0;
4578 double sw = w * scale;
4579 double sh = h * scale;
4580 QRectF rect( -sw / 2, -sh / 2, sw, sh );
4582 painter->translate( xform->
transform( QPointF( cx, cy ) ).toQPointF() );
4586 if ( lp->
getFeaturePart()->getLayer()->getArrangement() != P_POINT &&
4587 lp->
getFeaturePart()->getLayer()->getArrangement() != P_POINT_OVER &&
4590 painter->rotate( rotation );
4593 painter->translate( rect.bottomLeft() );
4594 painter->rotate( -lp->
getAlpha() * 180 / M_PI );
4595 painter->translate( -rect.bottomLeft() );
4598 QRectF rect( 0, 0, outPt2.
x() - outPt.
x(), outPt2.
y() - outPt.
y() );
4599 painter->translate( QPointF( outPt.
x(), outPt.
y() ) );
4600 painter->rotate( -lp->
getAlpha() * 180 / M_PI );
4605 painter->setPen( QColor( 255, 0, 0, 64 ) );
4609 painter->setPen( QColor( 0, 0, 0, 64 ) );
4611 painter->drawRect( rect );
4615 rect.moveTo( outPt.
x(), outPt.
y() );
@ Success
Operation succeeded.
LabelOffsetType
Behavior modifier for label offset and distance, only applies in some label placement modes.
@ FromPoint
Offset distance applies from point geometry.
@ FromSymbolBounds
Offset distance applies from rendered symbol bounds.
LabelPlacement
Placement modes which determine how label candidates are generated for a feature.
@ OverPoint
Arranges candidates over a point (or centroid of a polygon), or at a preset offset from the point....
@ Curved
Arranges candidates following the curvature of a line feature. Applies to line layers only.
@ AroundPoint
Arranges candidates in a circle around a point (or centroid of a polygon). Applies to point or polygo...
@ Line
Arranges candidates parallel to a generalised line representing the feature or parallel to a polygon'...
@ Free
Arranges candidates scattered throughout a polygon feature. Candidates are rotated to respect the pol...
@ OrderedPositionsAroundPoint
Candidates are placed in predefined positions around a point. Preference is given to positions with g...
@ Horizontal
Arranges horizontal candidates scattered throughout a polygon feature. Applies to polygon layers only...
@ PerimeterCurved
Arranges candidates following the curvature of a polygon's boundary. Applies to polygon layers only.
@ OutsidePolygons
Candidates are placed outside of polygon boundaries. Applies to polygon layers only....
@ Warning
Warning message.
Capitalization
String capitalization options.
@ AllSmallCaps
Force all characters to small caps (since QGIS 3.24)
@ MixedCase
Mixed case, ie no change.
@ AllLowercase
Convert all characters to lowercase.
@ TitleCase
Simple title case conversion - does not fully grammatically parse the text and uses simple rules only...
@ SmallCaps
Mixed case small caps (since QGIS 3.24)
@ ForceFirstLetterToCapital
Convert just the first letter of each word to uppercase, leave the rest untouched.
@ AllUppercase
Convert all characters to uppercase.
LabelQuadrantPosition
Label quadrant positions.
TextOrientation
Text orientations.
UnplacedLabelVisibility
Unplaced label visibility.
@ FollowEngineSetting
Respect the label engine setting.
LabelMultiLineAlignment
Text alignment for multi-line labels.