36 #include <QApplication>
39 #include <QFontMetrics>
42 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
43 #include <QDesktopWidget>
48 #include <QTextBoundaryFinder>
115 if ( !sPropertyDefinitions()->isEmpty() )
118 const QString origin = QStringLiteral(
"labeling" );
130 "e.g. Helvetica or Helvetica [Cronyx]" ), origin )
134 "e.g. Bold Condensed or Light Italic" ), origin )
175 "<b>Ellipse</b>|<b>Circle</b>|<b>SVG</b>]" ), origin )
199 "<b>Buffer</b>|<b>Background</b>]" ), origin )
215 "<b>3</b>=Left|<b>4</b>=Over|<b>5</b>=Right|<br>"
216 "<b>6</b>=Below Left|<b>7</b>=Below|<b>8</b>=Below Right]" ), origin )
236 + QStringLiteral(
"[<b>TL</b>=Top left|<b>TSL</b>=Top, slightly left|<b>T</b>=Top middle|<br>"
237 "<b>TSR</b>=Top, slightly right|<b>TR</b>=Top right|<br>"
238 "<b>L</b>=Left|<b>R</b>=Right|<br>"
239 "<b>BL</b>=Bottom left|<b>BSL</b>=Bottom, slightly left|<b>B</b>=Bottom middle|<br>"
240 "<b>BSR</b>=Bottom, slightly right|<b>BR</b>=Bottom right]" ), origin )
244 + QStringLiteral(
"[<b>OL</b>=On line|<b>AL</b>=Above line|<b>BL</b>=Below line|<br>"
245 "<b>LO</b>=Respect line orientation]" ), origin )
254 "<b>Half</b>|<b>Cap</b>|<b>Top</b>]" ), origin )
279 : predefinedPositionOrder( *DEFAULT_PLACEMENT_ORDER() )
282 initPropertyDefinitions();
291 , mDataDefinedProperties( s.mDataDefinedProperties )
326 mPolygonPlacementFlags = s.mPolygonPlacementFlags;
342 mRotationUnit = s.mRotationUnit;
363 mDataDefinedProperties = s.mDataDefinedProperties;
365 mCallout.reset( s.mCallout ? s.mCallout->clone() :
nullptr );
367 mPlacementSettings = s.mPlacementSettings;
368 mLineSettings = s.mLineSettings;
369 mObstacleSettings = s.mObstacleSettings;
370 mThinningSettings = s.mThinningSettings;
377 mLegendString = s.mLegendString;
379 mUnplacedVisibility = s.mUnplacedVisibility;
429 for (
const QString &name : referencedColumns )
431 attributeNames.insert( name );
490 for (
const QString &name : referencedColumns )
492 attributeNames.insert( name );
499 const auto referencedColumns = mCallout->referencedFields( context );
500 for (
const QString &name : referencedColumns )
502 attributeNames.insert( name );
511 QSet<QString> referenced;
539 referenced.unite( mCallout->referencedFields( context ) );
547 if ( mRenderStarted )
549 qWarning(
"Start render called for when a previous render was already underway!!" );
562 mCallout->startRender( context );
565 mRenderStarted =
true;
570 if ( !mRenderStarted )
572 qWarning(
"Stop render called for QgsPalLayerSettings without a startRender call!" );
578 mCallout->stopRender( context );
581 mRenderStarted =
false;
591 if ( mRenderStarted )
593 qWarning(
"stopRender was not called on QgsPalLayerSettings object!" );
604 initPropertyDefinitions();
605 return *sPropertyDefinitions();
619 return mRotationUnit;
624 mRotationUnit = angleUnit;
630 QString newValue = value;
631 if ( !value.isEmpty() && !value.contains( QLatin1String(
"~~" ) ) )
634 values << QStringLiteral(
"1" );
635 values << QStringLiteral(
"0" );
638 newValue = values.join( QLatin1String(
"~~" ) );
646 QString newPropertyName =
"labeling/dataDefined/" + sPropertyDefinitions()->value( p ).name();
647 QVariant newPropertyField = layer->
customProperty( newPropertyName, QVariant() );
649 if ( !newPropertyField.isValid() )
652 QString ddString = newPropertyField.toString();
654 if ( !ddString.isEmpty() && ddString != QLatin1String(
"0~~0~~~~" ) )
658 QStringList ddv = newStyleString.split( QStringLiteral(
"~~" ) );
660 bool active = ddv.at( 0 ).toInt();
661 if ( ddv.at( 1 ).toInt() )
677 void QgsPalLayerSettings::readOldDataDefinedPropertyMap(
QgsVectorLayer *layer, QDomElement *parentElem )
679 if ( !layer && !parentElem )
684 QgsPropertiesDefinition::const_iterator i = sPropertyDefinitions()->constBegin();
685 for ( ; i != sPropertyDefinitions()->constEnd(); ++i )
690 readOldDataDefinedProperty( layer,
static_cast< Property >( i.key() ) );
692 else if ( parentElem )
695 QDomElement e = parentElem->firstChildElement( i.value().name() );
698 bool active = e.attribute( QStringLiteral(
"active" ) ).compare( QLatin1String(
"true" ), Qt::CaseInsensitive ) == 0;
699 bool isExpression = e.attribute( QStringLiteral(
"useExpr" ) ).compare( QLatin1String(
"true" ), Qt::CaseInsensitive ) == 0;
713 void QgsPalLayerSettings::readFromLayerCustomProperties(
QgsVectorLayer *layer )
715 if ( layer->
customProperty( QStringLiteral(
"labeling" ) ).toString() != QLatin1String(
"pal" ) )
740 QDomDocument doc( QStringLiteral(
"substitutions" ) );
741 doc.setContent( layer->
customProperty( QStringLiteral(
"labeling/substitutions" ) ).toString() );
742 QDomElement replacementElem = doc.firstChildElement( QStringLiteral(
"substitutions" ) );
763 mLineSettings.
setPlacementFlags(
static_cast< QgsLabeling::LinePlacementFlags
>( layer->
customProperty( QStringLiteral(
"labeling/placementFlags" ) ).toUInt() ) );
772 if ( layer->
customProperty( QStringLiteral(
"labeling/distMapUnitScale" ) ).toString().isEmpty() )
775 double oldMin = layer->
customProperty( QStringLiteral(
"labeling/distMapUnitMinScale" ), 0.0 ).toDouble();
777 double oldMax = layer->
customProperty( QStringLiteral(
"labeling/distMapUnitMaxScale" ), 0.0 ).toDouble();
788 if ( layer->
customProperty( QStringLiteral(
"labeling/labelOffsetInMapUnits" ), QVariant(
true ) ).toBool() )
793 if ( layer->
customProperty( QStringLiteral(
"labeling/labelOffsetMapUnitScale" ) ).toString().isEmpty() )
796 double oldMin = layer->
customProperty( QStringLiteral(
"labeling/labelOffsetMapUnitMinScale" ), 0.0 ).toDouble();
798 double oldMax = layer->
customProperty( QStringLiteral(
"labeling/labelOffsetMapUnitMaxScale" ), 0.0 ).toDouble();
806 QVariant tempAngle = layer->
customProperty( QStringLiteral(
"labeling/angleOffset" ), QVariant() );
807 if ( tempAngle.isValid() )
809 double oldAngle = layer->
customProperty( QStringLiteral(
"labeling/angleOffset" ), QVariant( 0.0 ) ).toDouble();
823 switch ( layer->
customProperty( QStringLiteral(
"labeling/repeatDistanceUnit" ), QVariant( 1 ) ).toUInt() )
838 if ( layer->
customProperty( QStringLiteral(
"labeling/repeatDistanceMapUnitScale" ) ).toString().isEmpty() )
841 double oldMin = layer->
customProperty( QStringLiteral(
"labeling/repeatDistanceMapUnitMinScale" ), 0.0 ).toDouble();
843 double oldMax = layer->
customProperty( QStringLiteral(
"labeling/repeatDistanceMapUnitMaxScale" ), 0.0 ).toDouble();
852 double scalemn = layer->
customProperty( QStringLiteral(
"labeling/scaleMin" ), QVariant( 0 ) ).toDouble();
853 double scalemx = layer->
customProperty( QStringLiteral(
"labeling/scaleMax" ), QVariant( 0 ) ).toDouble();
856 QVariant scalevis = layer->
customProperty( QStringLiteral(
"labeling/scaleVisibility" ), QVariant() );
857 if ( scalevis.isValid() )
863 else if ( scalemn > 0 || scalemx > 0 )
879 if ( layer->
customProperty( QStringLiteral(
"labeling/displayAll" ), QVariant(
false ) ).toBool() )
897 mObstacleSettings.
setFactor( layer->
customProperty( QStringLiteral(
"labeling/obstacleFactor" ), QVariant( 1.0 ) ).toDouble() );
899 zIndex = layer->
customProperty( QStringLiteral(
"labeling/zIndex" ), QVariant( 0.0 ) ).toDouble();
901 mDataDefinedProperties.
clear();
902 if ( layer->
customProperty( QStringLiteral(
"labeling/ddProperties" ) ).isValid() )
904 QDomDocument doc( QStringLiteral(
"dd" ) );
905 doc.setContent( layer->
customProperty( QStringLiteral(
"labeling/ddProperties" ) ).toString() );
906 QDomElement elem = doc.firstChildElement( QStringLiteral(
"properties" ) );
907 mDataDefinedProperties.
readXml( elem, *sPropertyDefinitions() );
912 readOldDataDefinedPropertyMap( layer,
nullptr );
956 QDomElement textStyleElem = elem.firstChildElement( QStringLiteral(
"text-style" ) );
957 fieldName = textStyleElem.attribute( QStringLiteral(
"fieldName" ) );
958 isExpression = textStyleElem.attribute( QStringLiteral(
"isExpression" ) ).toInt();
960 mFormat.
readXml( elem, context );
962 previewBkgrdColor = QColor( textStyleElem.attribute( QStringLiteral(
"previewBkgrdColor" ), QStringLiteral(
"#ffffff" ) ) );
965 useSubstitutions = textStyleElem.attribute( QStringLiteral(
"useSubstitutions" ) ).toInt();
966 mLegendString = textStyleElem.attribute( QStringLiteral(
"legendString" ), QObject::tr(
"Aa" ) );
969 QDomElement textFormatElem = elem.firstChildElement( QStringLiteral(
"text-format" ) );
970 wrapChar = textFormatElem.attribute( QStringLiteral(
"wrapChar" ) );
971 autoWrapLength = textFormatElem.attribute( QStringLiteral(
"autoWrapLength" ), QStringLiteral(
"0" ) ).toInt();
972 useMaxLineLengthForAutoWrap = textFormatElem.attribute( QStringLiteral(
"useMaxLineLengthForAutoWrap" ), QStringLiteral(
"1" ) ).toInt();
973 multilineAlign =
static_cast< Qgis::LabelMultiLineAlignment >( textFormatElem.attribute( QStringLiteral(
"multilineAlign" ), QString::number(
static_cast< int >( Qgis::LabelMultiLineAlignment::FollowPlacement ) ) ).toUInt() );
974 mLineSettings.
setAddDirectionSymbol( textFormatElem.attribute( QStringLiteral(
"addDirectionSymbol" ) ).toInt() );
975 mLineSettings.
setLeftDirectionSymbol( textFormatElem.attribute( QStringLiteral(
"leftDirectionSymbol" ), QStringLiteral(
"<" ) ) );
976 mLineSettings.
setRightDirectionSymbol( textFormatElem.attribute( QStringLiteral(
"rightDirectionSymbol" ), QStringLiteral(
">" ) ) );
977 mLineSettings.
setReverseDirectionSymbol( textFormatElem.attribute( QStringLiteral(
"reverseDirectionSymbol" ) ).toInt() );
979 formatNumbers = textFormatElem.attribute( QStringLiteral(
"formatNumbers" ) ).toInt();
980 decimals = textFormatElem.attribute( QStringLiteral(
"decimals" ) ).toInt();
981 plusSign = textFormatElem.attribute( QStringLiteral(
"plussign" ) ).toInt();
984 QDomElement placementElem = elem.firstChildElement( QStringLiteral(
"placement" ) );
986 mLineSettings.
setPlacementFlags(
static_cast< QgsLabeling::LinePlacementFlags
>( placementElem.attribute( QStringLiteral(
"placementFlags" ) ).toUInt() ) );
987 mPolygonPlacementFlags =
static_cast< QgsLabeling::PolygonPlacementFlags
>( placementElem.attribute( QStringLiteral(
"polygonPlacementFlags" ), QString::number(
static_cast< int >( QgsLabeling::PolygonPlacementFlag::AllowPlacementInsideOfPolygon ) ) ).toInt() );
989 centroidWhole = placementElem.attribute( QStringLiteral(
"centroidWhole" ), QStringLiteral(
"0" ) ).toInt();
990 centroidInside = placementElem.attribute( QStringLiteral(
"centroidInside" ), QStringLiteral(
"0" ) ).toInt();
994 fitInPolygonOnly = placementElem.attribute( QStringLiteral(
"fitInPolygonOnly" ), QStringLiteral(
"0" ) ).toInt();
995 dist = placementElem.attribute( QStringLiteral(
"dist" ) ).toDouble();
996 if ( !placementElem.hasAttribute( QStringLiteral(
"distUnits" ) ) )
998 if ( placementElem.attribute( QStringLiteral(
"distInMapUnits" ) ).toInt() )
1007 if ( !placementElem.hasAttribute( QStringLiteral(
"distMapUnitScale" ) ) )
1010 double oldMin = placementElem.attribute( QStringLiteral(
"distMapUnitMinScale" ), QStringLiteral(
"0" ) ).toDouble();
1012 double oldMax = placementElem.attribute( QStringLiteral(
"distMapUnitMaxScale" ), QStringLiteral(
"0" ) ).toDouble();
1020 quadOffset =
static_cast< Qgis::LabelQuadrantPosition >( placementElem.attribute( QStringLiteral(
"quadOffset" ), QString::number(
static_cast< int >( Qgis::LabelQuadrantPosition::Over ) ) ).toUInt() );
1021 xOffset = placementElem.attribute( QStringLiteral(
"xOffset" ), QStringLiteral(
"0" ) ).toDouble();
1022 yOffset = placementElem.attribute( QStringLiteral(
"yOffset" ), QStringLiteral(
"0" ) ).toDouble();
1023 if ( !placementElem.hasAttribute( QStringLiteral(
"offsetUnits" ) ) )
1031 if ( !placementElem.hasAttribute( QStringLiteral(
"labelOffsetMapUnitScale" ) ) )
1034 double oldMin = placementElem.attribute( QStringLiteral(
"labelOffsetMapUnitMinScale" ), QStringLiteral(
"0" ) ).toDouble();
1036 double oldMax = placementElem.attribute( QStringLiteral(
"labelOffsetMapUnitMaxScale" ), QStringLiteral(
"0" ) ).toDouble();
1044 if ( placementElem.hasAttribute( QStringLiteral(
"angleOffset" ) ) )
1046 double oldAngle = placementElem.attribute( QStringLiteral(
"angleOffset" ), QStringLiteral(
"0" ) ).toDouble();
1051 angleOffset = placementElem.attribute( QStringLiteral(
"rotationAngle" ), QStringLiteral(
"0" ) ).toDouble();
1054 preserveRotation = placementElem.attribute( QStringLiteral(
"preserveRotation" ), QStringLiteral(
"1" ) ).toInt();
1056 maxCurvedCharAngleIn = placementElem.attribute( QStringLiteral(
"maxCurvedCharAngleIn" ), QStringLiteral(
"25" ) ).toDouble();
1057 maxCurvedCharAngleOut = placementElem.attribute( QStringLiteral(
"maxCurvedCharAngleOut" ), QStringLiteral(
"-25" ) ).toDouble();
1058 priority = placementElem.attribute( QStringLiteral(
"priority" ) ).toInt();
1059 repeatDistance = placementElem.attribute( QStringLiteral(
"repeatDistance" ), QStringLiteral(
"0" ) ).toDouble();
1060 if ( !placementElem.hasAttribute( QStringLiteral(
"repeatDistanceUnits" ) ) )
1063 switch ( placementElem.attribute( QStringLiteral(
"repeatDistanceUnit" ), QString::number( 1 ) ).toUInt() )
1083 if ( !placementElem.hasAttribute( QStringLiteral(
"repeatDistanceMapUnitScale" ) ) )
1086 double oldMin = placementElem.attribute( QStringLiteral(
"repeatDistanceMapUnitMinScale" ), QStringLiteral(
"0" ) ).toDouble();
1088 double oldMax = placementElem.attribute( QStringLiteral(
"repeatDistanceMapUnitMaxScale" ), QStringLiteral(
"0" ) ).toDouble();
1096 mLineSettings.
setOverrunDistance( placementElem.attribute( QStringLiteral(
"overrunDistance" ), QStringLiteral(
"0" ) ).toDouble() );
1099 mLineSettings.
setLineAnchorPercent( placementElem.attribute( QStringLiteral(
"lineAnchorPercent" ), QStringLiteral(
"0.5" ) ).toDouble() );
1105 geometryGenerator = placementElem.attribute( QStringLiteral(
"geometryGenerator" ) );
1111 mPlacementSettings.
setAllowDegradedPlacement( placementElem.attribute( QStringLiteral(
"allowDegraded" ), QStringLiteral(
"0" ) ).toInt() );
1114 QDomElement renderingElem = elem.firstChildElement( QStringLiteral(
"rendering" ) );
1116 drawLabels = renderingElem.attribute( QStringLiteral(
"drawLabels" ), QStringLiteral(
"1" ) ).toInt();
1118 maximumScale = renderingElem.attribute( QStringLiteral(
"scaleMin" ), QStringLiteral(
"0" ) ).toDouble();
1119 minimumScale = renderingElem.attribute( QStringLiteral(
"scaleMax" ), QStringLiteral(
"0" ) ).toDouble();
1120 scaleVisibility = renderingElem.attribute( QStringLiteral(
"scaleVisibility" ) ).toInt();
1122 fontLimitPixelSize = renderingElem.attribute( QStringLiteral(
"fontLimitPixelSize" ), QStringLiteral(
"0" ) ).toInt();
1123 fontMinPixelSize = renderingElem.attribute( QStringLiteral(
"fontMinPixelSize" ), QStringLiteral(
"0" ) ).toInt();
1124 fontMaxPixelSize = renderingElem.attribute( QStringLiteral(
"fontMaxPixelSize" ), QStringLiteral(
"10000" ) ).toInt();
1126 if ( placementElem.hasAttribute( QStringLiteral(
"overlapHandling" ) ) )
1133 if ( renderingElem.attribute( QStringLiteral(
"displayAll" ), QStringLiteral(
"0" ) ).toInt() )
1144 upsidedownLabels =
static_cast< Qgis::UpsideDownLabelHandling >( renderingElem.attribute( QStringLiteral(
"upsidedownLabels" ), QString::number(
static_cast< int >( Qgis::UpsideDownLabelHandling::FlipUpsideDownLabels ) ) ).toUInt() );
1146 labelPerPart = renderingElem.attribute( QStringLiteral(
"labelPerPart" ) ).toInt();
1147 mLineSettings.
setMergeLines( renderingElem.attribute( QStringLiteral(
"mergeLines" ) ).toInt() );
1148 mThinningSettings.
setMinimumFeatureSize( renderingElem.attribute( QStringLiteral(
"minFeatureSize" ) ).toDouble() );
1149 mThinningSettings.
setLimitNumberLabelsEnabled( renderingElem.attribute( QStringLiteral(
"limitNumLabels" ), QStringLiteral(
"0" ) ).toInt() );
1150 mThinningSettings.
setMaximumNumberLabels( renderingElem.attribute( QStringLiteral(
"maxNumLabels" ), QStringLiteral(
"2000" ) ).toInt() );
1151 mObstacleSettings.
setIsObstacle( renderingElem.attribute( QStringLiteral(
"obstacle" ), QStringLiteral(
"1" ) ).toInt() );
1152 mObstacleSettings.
setFactor( renderingElem.attribute( QStringLiteral(
"obstacleFactor" ), QStringLiteral(
"1" ) ).toDouble() );
1154 zIndex = renderingElem.attribute( QStringLiteral(
"zIndex" ), QStringLiteral(
"0.0" ) ).toDouble();
1157 QDomElement ddElem = elem.firstChildElement( QStringLiteral(
"dd_properties" ) );
1158 if ( !ddElem.isNull() )
1160 mDataDefinedProperties.
readXml( ddElem, *sPropertyDefinitions() );
1165 mDataDefinedProperties.
clear();
1166 QDomElement ddElem = elem.firstChildElement( QStringLiteral(
"data-defined" ) );
1167 readOldDataDefinedPropertyMap(
nullptr, &ddElem );
1208 const QString calloutType = elem.attribute( QStringLiteral(
"calloutType" ) );
1209 if ( calloutType.isEmpty() )
1221 QDomElement textStyleElem = mFormat.
writeXml( doc, context );
1224 textStyleElem.setAttribute( QStringLiteral(
"fieldName" ),
fieldName );
1225 textStyleElem.setAttribute( QStringLiteral(
"isExpression" ),
isExpression );
1226 QDomElement replacementElem = doc.createElement( QStringLiteral(
"substitutions" ) );
1228 textStyleElem.appendChild( replacementElem );
1229 textStyleElem.setAttribute( QStringLiteral(
"useSubstitutions" ),
useSubstitutions );
1230 textStyleElem.setAttribute( QStringLiteral(
"legendString" ), mLegendString );
1233 QDomElement textFormatElem = doc.createElement( QStringLiteral(
"text-format" ) );
1234 textFormatElem.setAttribute( QStringLiteral(
"wrapChar" ),
wrapChar );
1235 textFormatElem.setAttribute( QStringLiteral(
"autoWrapLength" ),
autoWrapLength );
1237 textFormatElem.setAttribute( QStringLiteral(
"multilineAlign" ),
static_cast< unsigned int >(
multilineAlign ) );
1238 textFormatElem.setAttribute( QStringLiteral(
"addDirectionSymbol" ), mLineSettings.
addDirectionSymbol() );
1239 textFormatElem.setAttribute( QStringLiteral(
"leftDirectionSymbol" ), mLineSettings.
leftDirectionSymbol() );
1240 textFormatElem.setAttribute( QStringLiteral(
"rightDirectionSymbol" ), mLineSettings.
rightDirectionSymbol() );
1241 textFormatElem.setAttribute( QStringLiteral(
"reverseDirectionSymbol" ), mLineSettings.
reverseDirectionSymbol() );
1242 textFormatElem.setAttribute( QStringLiteral(
"placeDirectionSymbol" ),
static_cast< unsigned int >( mLineSettings.
directionSymbolPlacement() ) );
1243 textFormatElem.setAttribute( QStringLiteral(
"formatNumbers" ),
formatNumbers );
1244 textFormatElem.setAttribute( QStringLiteral(
"decimals" ),
decimals );
1245 textFormatElem.setAttribute( QStringLiteral(
"plussign" ),
plusSign );
1248 QDomElement placementElem = doc.createElement( QStringLiteral(
"placement" ) );
1249 placementElem.setAttribute( QStringLiteral(
"placement" ),
static_cast< int >(
placement ) );
1250 placementElem.setAttribute( QStringLiteral(
"polygonPlacementFlags" ),
static_cast< int >( mPolygonPlacementFlags ) );
1251 placementElem.setAttribute( QStringLiteral(
"placementFlags" ),
static_cast< unsigned int >( mLineSettings.
placementFlags() ) );
1252 placementElem.setAttribute( QStringLiteral(
"centroidWhole" ),
centroidWhole );
1253 placementElem.setAttribute( QStringLiteral(
"centroidInside" ),
centroidInside );
1255 placementElem.setAttribute( QStringLiteral(
"fitInPolygonOnly" ),
fitInPolygonOnly );
1256 placementElem.setAttribute( QStringLiteral(
"dist" ),
dist );
1259 placementElem.setAttribute( QStringLiteral(
"offsetType" ),
static_cast< unsigned int >(
offsetType ) );
1260 placementElem.setAttribute( QStringLiteral(
"quadOffset" ),
static_cast< unsigned int >(
quadOffset ) );
1261 placementElem.setAttribute( QStringLiteral(
"xOffset" ),
xOffset );
1262 placementElem.setAttribute( QStringLiteral(
"yOffset" ),
yOffset );
1265 placementElem.setAttribute( QStringLiteral(
"rotationAngle" ),
angleOffset );
1266 placementElem.setAttribute( QStringLiteral(
"preserveRotation" ),
preserveRotation );
1267 placementElem.setAttribute( QStringLiteral(
"rotationUnit" ),
qgsEnumValueToKey( mRotationUnit ) );
1270 placementElem.setAttribute( QStringLiteral(
"priority" ),
priority );
1271 placementElem.setAttribute( QStringLiteral(
"repeatDistance" ),
repeatDistance );
1274 placementElem.setAttribute( QStringLiteral(
"overrunDistance" ), mLineSettings.
overrunDistance() );
1277 placementElem.setAttribute( QStringLiteral(
"lineAnchorPercent" ), mLineSettings.
lineAnchorPercent() );
1278 placementElem.setAttribute( QStringLiteral(
"lineAnchorType" ),
static_cast< int >( mLineSettings.
anchorType() ) );
1279 placementElem.setAttribute( QStringLiteral(
"lineAnchorClipping" ),
static_cast< int >( mLineSettings.
anchorClipping() ) );
1282 placementElem.setAttribute( QStringLiteral(
"geometryGenerator" ),
geometryGenerator );
1284 const QMetaEnum metaEnum( QMetaEnum::fromType<QgsWkbTypes::GeometryType>() );
1285 placementElem.setAttribute( QStringLiteral(
"geometryGeneratorType" ), metaEnum.valueToKey(
geometryGeneratorType ) );
1287 placementElem.setAttribute( QStringLiteral(
"layerType" ), metaEnum.valueToKey(
layerType ) );
1290 placementElem.setAttribute( QStringLiteral(
"allowDegraded" ), mPlacementSettings.
allowDegradedPlacement() ? QStringLiteral(
"1" ) : QStringLiteral(
"0" ) );
1293 QDomElement renderingElem = doc.createElement( QStringLiteral(
"rendering" ) );
1294 renderingElem.setAttribute( QStringLiteral(
"drawLabels" ),
drawLabels );
1295 renderingElem.setAttribute( QStringLiteral(
"scaleVisibility" ),
scaleVisibility );
1296 renderingElem.setAttribute( QStringLiteral(
"scaleMin" ),
maximumScale );
1297 renderingElem.setAttribute( QStringLiteral(
"scaleMax" ),
minimumScale );
1298 renderingElem.setAttribute( QStringLiteral(
"fontLimitPixelSize" ),
fontLimitPixelSize );
1299 renderingElem.setAttribute( QStringLiteral(
"fontMinPixelSize" ),
fontMinPixelSize );
1300 renderingElem.setAttribute( QStringLiteral(
"fontMaxPixelSize" ),
fontMaxPixelSize );
1301 renderingElem.setAttribute( QStringLiteral(
"upsidedownLabels" ),
static_cast< unsigned int >(
upsidedownLabels ) );
1303 renderingElem.setAttribute( QStringLiteral(
"labelPerPart" ),
labelPerPart );
1304 renderingElem.setAttribute( QStringLiteral(
"mergeLines" ), mLineSettings.
mergeLines() );
1305 renderingElem.setAttribute( QStringLiteral(
"minFeatureSize" ), mThinningSettings.
minimumFeatureSize() );
1307 renderingElem.setAttribute( QStringLiteral(
"maxNumLabels" ), mThinningSettings.
maximumNumberLabels() );
1308 renderingElem.setAttribute( QStringLiteral(
"obstacle" ), mObstacleSettings.
isObstacle() );
1309 renderingElem.setAttribute( QStringLiteral(
"obstacleFactor" ), mObstacleSettings.
factor() );
1310 renderingElem.setAttribute( QStringLiteral(
"obstacleType" ),
static_cast< unsigned int >( mObstacleSettings.
type() ) );
1311 renderingElem.setAttribute( QStringLiteral(
"zIndex" ),
zIndex );
1312 renderingElem.setAttribute( QStringLiteral(
"unplacedVisibility" ),
static_cast< int >( mUnplacedVisibility ) );
1314 QDomElement ddElem = doc.createElement( QStringLiteral(
"dd_properties" ) );
1315 mDataDefinedProperties.
writeXml( ddElem, *sPropertyDefinitions() );
1317 QDomElement elem = doc.createElement( QStringLiteral(
"settings" ) );
1318 elem.appendChild( textStyleElem );
1319 elem.appendChild( textFormatElem );
1320 elem.appendChild( placementElem );
1321 elem.appendChild( renderingElem );
1322 elem.appendChild( ddElem );
1326 elem.setAttribute( QStringLiteral(
"calloutType" ), mCallout->type() );
1327 mCallout->saveProperties( doc, elem, context );
1342 QPixmap pixmap( size );
1343 pixmap.fill( Qt::transparent );
1345 painter.begin( &pixmap );
1347 painter.setRenderHint( QPainter::Antialiasing );
1349 QRect rect( 0, 0, size.width(), size.height() );
1352 painter.setPen( Qt::NoPen );
1354 if ( ( background1.lightnessF() < 0.7 ) )
1356 background1 = background1.darker( 125 );
1360 background1 = background1.lighter( 125 );
1363 QLinearGradient linearGrad( QPointF( 0, 0 ), QPointF( 0, rect.height() ) );
1364 linearGrad.setColorAt( 0, background1 );
1365 linearGrad.setColorAt( 1, background2 );
1366 painter.setBrush( QBrush( linearGrad ) );
1367 if ( size.width() > 30 )
1369 painter.drawRoundedRect( rect, 6, 6 );
1374 painter.drawRect( rect );
1376 painter.setBrush( Qt::NoBrush );
1377 painter.setPen( Qt::NoPen );
1386 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
1387 const double logicalDpiX = QgsApplication::desktop()->logicalDpiX();
1389 QWidget *activeWindow = QApplication::activeWindow();
1390 const double logicalDpiX = activeWindow && activeWindow->screen() ? activeWindow->screen()->logicalDotsPerInchX() : 96.0;
1402 ? fontSize * tempFormat.
buffer().
size() / 100
1407 double ytrans = 0.0;
1410 ? fontSize * tempFormat.
buffer().
size() / 100
1415 const QStringList text = QStringList() << ( previewText.isEmpty() ? settings.
legendString() : previewText );
1417 QRectF textRect = rect;
1418 textRect.setLeft( xtrans + padding );
1419 textRect.setWidth( rect.width() - xtrans - 2 * padding );
1421 if ( textRect.width() > 2000 )
1422 textRect.setWidth( 2000 - 2 * padding );
1424 const double bottom = textRect.height() / 2 + textHeight / 2;
1425 textRect.setTop( bottom - textHeight );
1426 textRect.setBottom( bottom );
1437 QRectF labelRect( textRect.left() + ( textRect.width() - textWidth ) / 2.0, textRect.top(), textWidth, textRect.height() );
1444 if ( size.width() > 30 )
1449 rect.width() - iconWidth * 3, rect.height() - iconWidth * 3,
1450 iconWidth * 2, iconWidth * 2 ), Qt::AlignRight | Qt::AlignBottom );
1454 painter.setBrush( Qt::NoBrush );
1456 if ( size.width() > 30 )
1458 painter.drawRoundedRect( rect, 6, 6 );
1463 painter.drawRect( rect );
1472 return mUnplacedVisibility;
1477 mUnplacedVisibility = visibility;
1482 return QgsPalLabeling::checkMinimumSizeMM(
ct, geom, minSize );
1492 QString textCopy( text );
1495 std::unique_ptr< QgsRenderContext > scopedRc;
1500 scopedRc->expressionContext().setFeature( *f );
1616 if ( wrapchr.isEmpty() )
1618 wrapchr = QStringLiteral(
"\n" );
1623 && ( !leftDirSymb.isEmpty() || !rightDirSymb.isEmpty() ) )
1625 QString dirSym = leftDirSymb;
1627 if ( fm->horizontalAdvance( rightDirSymb ) > fm->horizontalAdvance( dirSym ) )
1628 dirSym = rightDirSymb;
1630 switch ( placeDirSymb )
1633 textCopy.append( dirSym );
1638 textCopy.prepend( dirSym + QStringLiteral(
"\n" ) );
1643 double w = 0.0, h = 0.0, rw = 0.0, rh = 0.0;
1644 double labelHeight = fm->ascent() + fm->descent();
1646 QStringList multiLineSplit;
1658 int lines = multiLineSplit.size();
1660 switch ( orientation )
1664 h += fm->height() +
static_cast< double >( ( lines - 1 ) * labelHeight * multilineH );
1666 for (
const auto &line : multiLineSplit )
1668 w = std::max( w, fm->horizontalAdvance( line ) );
1675 double letterSpacing = mFormat.
scaledFont( *context ).letterSpacing();
1676 double labelWidth = fm->maxWidth();
1677 w = labelWidth + ( lines - 1 ) * labelWidth * multilineH;
1679 int maxLineLength = 0;
1680 for (
const auto &line : multiLineSplit )
1682 maxLineLength = std::max( maxLineLength,
static_cast<int>( line.length() ) );
1684 h = fm->ascent() * maxLineLength + ( maxLineLength - 1 ) * letterSpacing;
1690 double widthHorizontal = 0.0;
1691 for (
const auto &line : multiLineSplit )
1693 widthHorizontal = std::max( w, fm->horizontalAdvance( line ) );
1696 double widthVertical = 0.0;
1697 double letterSpacing = mFormat.
scaledFont( *context ).letterSpacing();
1698 double labelWidth = fm->maxWidth();
1699 widthVertical = labelWidth + ( lines - 1 ) * labelWidth * multilineH;
1701 double heightHorizontal = 0.0;
1702 heightHorizontal += fm->height() +
static_cast< double >( ( lines - 1 ) * labelHeight * multilineH );
1704 double heightVertical = 0.0;
1705 int maxLineLength = 0;
1706 for (
const auto &line : multiLineSplit )
1708 maxLineLength = std::max( maxLineLength,
static_cast<int>( line.length() ) );
1710 heightVertical = fm->ascent() * maxLineLength + ( maxLineLength - 1 ) * letterSpacing;
1712 w = widthHorizontal;
1713 rw = heightVertical;
1714 h = heightHorizontal;
1722 labelX = std::fabs( ptSize.
x() -
ptZero.
x() );
1723 labelY = std::fabs( ptSize.
y() -
ptZero.
y() );
1728 if ( rotatedLabelX && rotatedLabelY )
1730 *rotatedLabelX = rw * uPP;
1731 *rotatedLabelY = rh * uPP;
1747 bool isObstacle = mObstacleSettings.
isObstacle();
1755 return registerObstacleFeature( f, context, obstacleGeometry );
1770 if ( obstacleGeometry.
isNull() )
1783 dataDefinedValues.clear();
1800 if ( useScaleVisibility )
1813 maxScale = 1 / std::fabs( maxScale );
1832 minScale = 1 / std::fabs( minScale );
1841 QFont labelFont = mFormat.
font();
1847 if ( !exprVal.isNull() )
1849 QString units = exprVal.toString();
1850 if ( !units.isEmpty() )
1860 double fontSize = mFormat.
size();
1866 if ( fontSize <= 0.0 )
1873 if ( fontPixelSize < 1 )
1877 labelFont.setPixelSize( fontPixelSize );
1889 if ( fontMinPixel > labelFont.pixelSize() || labelFont.pixelSize() > fontMaxPixel )
1901 labelFont.setCapitalization( QFont::MixedCase );
1903 parseTextStyle( labelFont, fontunits, context );
1906 parseTextFormatting( context );
1907 parseTextBuffer( context );
1908 parseTextMask( context );
1909 parseShapeBackground( context );
1910 parseDropShadow( context );
1931 labelText = result.isNull() ? QString() : result.toString();
1936 labelText = v.isNull() ? QString() : v.toString();
1956 if ( !exprVal.isNull() )
1958 QString fcase = exprVal.toString().trimmed();
1959 QgsDebugMsgLevel( QStringLiteral(
"exprVal FontCase:%1" ).arg( fcase ), 4 );
1961 if ( !fcase.isEmpty() )
1963 if ( fcase.compare( QLatin1String(
"NoChange" ), Qt::CaseInsensitive ) == 0 )
1967 else if ( fcase.compare( QLatin1String(
"Upper" ), Qt::CaseInsensitive ) == 0 )
1971 else if ( fcase.compare( QLatin1String(
"Lower" ), Qt::CaseInsensitive ) == 0 )
1975 else if ( fcase.compare( QLatin1String(
"Capitalize" ), Qt::CaseInsensitive ) == 0 )
1979 else if ( fcase.compare( QLatin1String(
"Title" ), Qt::CaseInsensitive ) == 0 )
1983 #if defined(HAS_KDE_QT5_SMALL_CAPS_FIX) || QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)
1984 else if ( fcase.compare( QLatin1String(
"SmallCaps" ), Qt::CaseInsensitive ) == 0 )
1988 else if ( fcase.compare( QLatin1String(
"AllSmallCaps" ), Qt::CaseInsensitive ) == 0 )
2004 if ( evalFormatNumbers )
2008 if ( decimalPlaces <= 0 )
2014 QVariant textV( labelText );
2016 double d = textV.toDouble( &ok );
2019 QString numberFormat;
2020 if ( d > 0 && signPlus )
2022 numberFormat.append(
'+' );
2024 numberFormat.append(
"%1" );
2025 labelText = numberFormat.arg( d, 0,
'f', decimalPlaces );
2030 std::unique_ptr<QFontMetricsF> labelFontMetrics(
new QFontMetricsF( labelFont ) );
2031 double labelX, labelY, rotatedLabelX, rotatedLabelY;
2034 if (
format().allowHtmlFormatting() )
2038 calculateLabelSize( labelFontMetrics.get(), labelText, labelX, labelY,
mCurFeat, &context, &rotatedLabelX, &rotatedLabelY,
format().allowHtmlFormatting() ? &doc :
nullptr );
2042 double maxcharanglein = 20.0;
2043 double maxcharangleout = -20.0;
2058 maxcharanglein = std::clamp(
static_cast< double >( maxcharanglePt.x() ), 20.0, 60.0 );
2059 maxcharangleout = std::clamp(
static_cast< double >( maxcharanglePt.y() ), 20.0, 95.0 );
2063 maxcharangleout = -( std::fabs( maxcharangleout ) );
2071 if ( !exprVal.isNull() )
2073 QString
str = exprVal.toString().trimmed();
2076 if ( !
str.isEmpty() )
2078 if (
str.compare( QLatin1String(
"Visible" ), Qt::CaseInsensitive ) == 0 )
2080 wholeCentroid =
false;
2082 else if (
str.compare( QLatin1String(
"Whole" ), Qt::CaseInsensitive ) == 0 )
2084 wholeCentroid =
true;
2098 std::unique_ptr<QgsGeometry> scopedClonedGeom;
2104 geom = simplifier.
simplify( geom );
2121 bool doClip =
false;
2122 if ( !centroidPoly || !wholeCentroid )
2128 QgsLabeling::PolygonPlacementFlags polygonPlacement = mPolygonPlacementFlags;
2132 if ( !dataDefinedOutside.isNull() )
2134 if ( dataDefinedOutside.type() == QVariant::String )
2136 const QString value = dataDefinedOutside.toString().trimmed();
2137 if ( value.compare( QLatin1String(
"force" ), Qt::CaseInsensitive ) == 0 )
2140 polygonPlacement &= ~static_cast< int >( QgsLabeling::PolygonPlacementFlag::AllowPlacementInsideOfPolygon );
2141 polygonPlacement |= QgsLabeling::PolygonPlacementFlag::AllowPlacementOutsideOfPolygon;
2143 else if ( value.compare( QLatin1String(
"yes" ), Qt::CaseInsensitive ) == 0 )
2146 polygonPlacement |= QgsLabeling::PolygonPlacementFlag::AllowPlacementOutsideOfPolygon;
2148 else if ( value.compare( QLatin1String(
"no" ), Qt::CaseInsensitive ) == 0 )
2151 polygonPlacement &= ~static_cast< int >( QgsLabeling::PolygonPlacementFlag::AllowPlacementOutsideOfPolygon );
2156 if ( dataDefinedOutside.toBool() )
2159 polygonPlacement |= QgsLabeling::PolygonPlacementFlag::AllowPlacementOutsideOfPolygon;
2164 polygonPlacement &= ~static_cast< int >( QgsLabeling::PolygonPlacementFlag::AllowPlacementOutsideOfPolygon );
2192 permissibleZone = geom;
2228 double minimumSize = 0.0;
2238 if ( !checkMinimumSizeMM( context, geom, featureThinningSettings.
minimumFeatureSize() ) )
2243 if ( !geos_geom_clone )
2272 bool layerDefinedRotation =
false;
2273 bool dataDefinedRotation =
false;
2274 double xPos = 0.0, yPos = 0.0,
angle = 0.0;
2275 double quadOffsetX = 0.0, quadOffsetY = 0.0;
2276 double offsetX = 0.0, offsetY = 0.0;
2288 bool ddFixedQuad =
false;
2294 if ( !exprVal.isNull() )
2297 int quadInt = exprVal.toInt( &ok );
2298 if ( ok && 0 <= quadInt && quadInt <= 8 )
2309 case Qgis::LabelQuadrantPosition::AboveLeft:
2313 case Qgis::LabelQuadrantPosition::Above:
2317 case Qgis::LabelQuadrantPosition::AboveRight:
2321 case Qgis::LabelQuadrantPosition::Left:
2325 case Qgis::LabelQuadrantPosition::Right:
2329 case Qgis::LabelQuadrantPosition::BelowLeft:
2333 case Qgis::LabelQuadrantPosition::Below:
2337 case Qgis::LabelQuadrantPosition::BelowRight:
2341 case Qgis::LabelQuadrantPosition::Over:
2366 if ( !exprVal.isNull() )
2368 QString units = exprVal.toString().trimmed();
2369 if ( !units.isEmpty() )
2375 offUnit = decodedUnits;
2390 layerDefinedRotation =
true;
2400 if ( !exprVal.isNull() )
2403 const double rotation = exprVal.toDouble( &ok );
2406 dataDefinedRotation =
true;
2414 angle = ( 360 - rotationDegrees ) * M_PI / 180.0;
2419 bool hasDataDefinedPosition =
false;
2421 bool ddPosition =
false;
2428 if ( !xPosProperty.isNull()
2429 && !yPosProperty.isNull() )
2433 bool ddXPos =
false, ddYPos =
false;
2434 xPos = xPosProperty.toDouble( &ddXPos );
2435 yPos = yPosProperty.toDouble( &ddYPos );
2436 if ( ddXPos && ddYPos )
2437 hasDataDefinedPosition =
true;
2443 if ( !pointPosProperty.isNull() )
2453 if ( !referencedGeometryPoint.
isNull()
2457 else if ( pointPosProperty.canConvert<
QgsGeometry>() )
2464 hasDataDefinedPosition =
true;
2475 if ( hasDataDefinedPosition )
2478 if ( layerDefinedRotation && !dataDefinedRotation )
2487 if ( !exprVal.isNull() )
2489 QString haliString = exprVal.toString();
2490 if ( haliString.compare( QLatin1String(
"Center" ), Qt::CaseInsensitive ) == 0 )
2492 xdiff -= labelX / 2.0;
2494 else if ( haliString.compare( QLatin1String(
"Right" ), Qt::CaseInsensitive ) == 0 )
2505 if ( !exprVal.isNull() )
2507 QString valiString = exprVal.toString();
2508 if ( valiString.compare( QLatin1String(
"Bottom" ), Qt::CaseInsensitive ) != 0 )
2510 if ( valiString.compare( QLatin1String(
"Top" ), Qt::CaseInsensitive ) == 0 )
2516 double descentRatio = labelFontMetrics->descent() / labelFontMetrics->height();
2517 if ( valiString.compare( QLatin1String(
"Base" ), Qt::CaseInsensitive ) == 0 )
2519 ydiff -= labelY * descentRatio;
2523 double capHeightRatio = ( labelFontMetrics->boundingRect(
'H' ).height() + 1 + labelFontMetrics->descent() ) / labelFontMetrics->height();
2524 ydiff -= labelY * capHeightRatio;
2525 if ( valiString.compare( QLatin1String(
"Half" ), Qt::CaseInsensitive ) == 0 )
2527 ydiff += labelY * ( capHeightRatio - descentRatio ) / 2.0;
2535 if ( dataDefinedRotation )
2538 double xd = xdiff * std::cos(
angle ) - ydiff * std::sin(
angle );
2539 double yd = xdiff * std::sin(
angle ) + ydiff * std::cos(
angle );
2549 if (
const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( ddPoint.
constGet() ) )
2557 QgsMessageLog::logMessage( QObject::tr(
"Invalid data defined label position (%1, %2)" ).arg( xPos ).arg( yPos ), QObject::tr(
"Labeling" ) );
2558 hasDataDefinedPosition =
false;
2577 bool alwaysShow =
false;
2597 if ( !exprVal.isNull() )
2599 QString units = exprVal.toString().trimmed();
2600 if ( !units.isEmpty() )
2606 repeatUnits = decodedUnits;
2632 bool labelAll =
labelPerPart && !hasDataDefinedPosition;
2633 if ( !hasDataDefinedPosition )
2643 std::unique_ptr< QgsTextLabelFeature > labelFeature = std::make_unique< QgsTextLabelFeature>( feature.
id(), std::move( geos_geom_clone ), QSizeF( labelX, labelY ) );
2644 labelFeature->setAnchorPosition( anchorPosition );
2645 labelFeature->setFeature( feature );
2646 labelFeature->setSymbol( symbol );
2647 labelFeature->setDocument( doc );
2649 labelFeature->setRotatedSize( QSizeF( rotatedLabelX, rotatedLabelY ) );
2652 labelFeature->setHasFixedPosition( hasDataDefinedPosition );
2653 labelFeature->setFixedPosition(
QgsPointXY( xPos, yPos ) );
2655 labelFeature->setHasFixedAngle( dataDefinedRotation || ( !hasDataDefinedPosition && !
qgsDoubleNear(
angle, 0.0 ) ) );
2656 labelFeature->setFixedAngle(
angle );
2657 labelFeature->setQuadOffset( QPointF( quadOffsetX, quadOffsetY ) );
2658 labelFeature->setPositionOffset(
QgsPointXY( offsetX, offsetY ) );
2660 labelFeature->setAlwaysShow( alwaysShow );
2661 labelFeature->setRepeatDistance( repeatDist );
2662 labelFeature->setLabelText( labelText );
2663 labelFeature->setPermissibleZone( permissibleZone );
2664 labelFeature->setOverrunDistance( overrunDistanceEval );
2665 labelFeature->setOverrunSmoothDistance( overrunSmoothDist );
2669 labelFeature->setLabelAllParts( labelAll );
2671 labelFeature->setMinimumSize( minimumSize );
2675 labelFeature->setSymbolSize( QSizeF( obstacleGeometry.
boundingBox().
width(),
2681 double topMargin = std::max( 0.25 * labelFontMetrics->ascent(), 0.0 );
2682 double bottomMargin = 1.0 + labelFontMetrics->descent();
2683 QgsMargins vm( 0.0, topMargin, 0.0, bottomMargin );
2685 labelFeature->setVisualMargin( vm );
2688 QgsDebugMsgLevel( QStringLiteral(
"PAL font stored definedFont: %1, Style: %2" ).arg( labelFont.toString(), labelFont.styleName() ), 4 );
2689 labelFeature->setDefinedFont( labelFont );
2690 labelFeature->setFontMetrics( *labelFontMetrics );
2692 labelFeature->setMaximumCharacterAngleInside( std::clamp( maxcharanglein, 20.0, 60.0 ) * M_PI / 180 );
2693 labelFeature->setMaximumCharacterAngleOutside( std::clamp( maxcharangleout, -95.0, -20.0 ) * M_PI / 180 );
2717 double distance =
dist;
2729 if ( !exprVal.isNull() )
2731 QString units = exprVal.toString().trimmed();
2732 QgsDebugMsgLevel( QStringLiteral(
"exprVal DistanceUnits:%1" ).arg( units ), 4 );
2733 if ( !units.isEmpty() )
2739 distUnit = decodedUnits;
2752 distance = ( distance < 0 ? -1 : 1 ) * std::max( std::fabs( distance ), 1.0 );
2760 distance = std::max( distance, 2.0 );
2766 labelFeature->setDistLabel( d );
2771 labelFeature->setHasFixedQuadrant(
true );
2776 labelFeature->setPolygonPlacementFlags( polygonPlacement );
2785 labelFeature->setZIndex( z );
2792 if ( !exprVal.isNull() )
2795 double priorityD = exprVal.toDouble( &ok );
2798 priorityD = std::clamp( priorityD, 0.0, 10.0 );
2799 priorityD = 1 - priorityD / 10.0;
2800 labelFeature->setPriority( priorityD );
2813 labelFeature->setAllowDegradedPlacement( allowDegradedPlacement );
2822 const QString cleanedString = handlingString.trimmed();
2823 if ( cleanedString.compare( QLatin1String(
"prevent" ), Qt::CaseInsensitive ) == 0 )
2825 else if ( cleanedString.compare( QLatin1String(
"allowifneeded" ), Qt::CaseInsensitive ) == 0 )
2827 else if ( cleanedString.compare( QLatin1String(
"alwaysallow" ), Qt::CaseInsensitive ) == 0 )
2830 labelFeature->setOverlapHandling( overlapHandling );
2837 labelFeature->setObstacleSettings( os );
2840 if ( positionOrder.isEmpty() )
2841 positionOrder = *DEFAULT_PLACEMENT_ORDER();
2847 if ( !dataDefinedOrder.isEmpty() )
2852 labelFeature->setPredefinedPositionOrder( positionOrder );
2855 labelFeature->setDataDefinedValues( dataDefinedValues );
2857 return labelFeature;
2865 if ( !obstacleGeometry.
isNull() )
2867 geom = obstacleGeometry;
2882 if ( ls->numPoints() < 2 )
2888 std::unique_ptr<QgsGeometry> scopedClonedGeom;
2894 geom = simplifier.
simplify( geom );
2898 std::unique_ptr<QgsGeometry> scopedPreparedGeom;
2906 if ( !geos_geom_clone )
2910 std::unique_ptr< QgsLabelFeature > obstacleFeature = std::make_unique< QgsLabelFeature >( f.
id(), std::move( geos_geom_clone ), QSizeF( 0, 0 ) );
2911 obstacleFeature->setFeature( f );
2916 obstacleFeature->setObstacleSettings( os );
2919 return obstacleFeature;
2922 bool QgsPalLayerSettings::dataDefinedValEval( DataDefinedValueType valType,
2926 if ( !mDataDefinedProperties.
isActive( p ) )
2930 exprVal = mDataDefinedProperties.
value( p, context );
2931 if ( !exprVal.isNull() )
2937 bool bol = exprVal.toBool();
2938 dataDefinedValues.insert( p, QVariant( bol ) );
2944 int size = exprVal.toInt( &ok );
2948 dataDefinedValues.insert( p, QVariant( size ) );
2956 int size = exprVal.toInt( &ok );
2958 if ( ok && size > 0 )
2960 dataDefinedValues.insert( p, QVariant( size ) );
2968 double size = exprVal.toDouble( &ok );
2972 dataDefinedValues.insert( p, QVariant( size ) );
2980 double size = exprVal.toDouble( &ok );
2982 if ( ok && size > 0.0 )
2984 dataDefinedValues.insert( p, QVariant( size ) );
2992 double rot = exprVal.toDouble( &ok );
2995 if ( rot < -180.0 && rot >= -360 )
2999 if ( rot > 180.0 && rot <= 360 )
3003 if ( rot >= -180 && rot <= 180 )
3005 dataDefinedValues.insert( p, QVariant( rot ) );
3014 int size = exprVal.toInt( &ok );
3015 if ( ok && size >= 0 && size <= 100 )
3017 dataDefinedValues.insert( p, QVariant( size ) );
3024 QString
str = exprVal.toString();
3026 dataDefinedValues.insert( p, QVariant(
str ) );
3031 QString unitstr = exprVal.toString().trimmed();
3033 if ( !unitstr.isEmpty() )
3042 QString colorstr = exprVal.toString().trimmed();
3045 if ( color.isValid() )
3047 dataDefinedValues.insert( p, QVariant( color ) );
3054 QString joinstr = exprVal.toString().trimmed();
3056 if ( !joinstr.isEmpty() )
3065 QString blendstr = exprVal.toString().trimmed();
3067 if ( !blendstr.isEmpty() )
3080 dataDefinedValues.insert( p, res );
3091 dataDefinedValues.insert( p, res );
3101 void QgsPalLayerSettings::parseTextStyle( QFont &labelFont,
3114 QString ddFontFamily;
3119 if ( !exprVal.isNull() )
3121 QString family = exprVal.toString().trimmed();
3122 QgsDebugMsgLevel( QStringLiteral(
"exprVal Font family:%1" ).arg( family ), 4 );
3124 if ( labelFont.family() != family )
3130 ddFontFamily = family;
3137 QString ddFontStyle;
3141 if ( !exprVal.isNull() )
3143 QString fontstyle = exprVal.toString().trimmed();
3144 QgsDebugMsgLevel( QStringLiteral(
"exprVal Font style:%1" ).arg( fontstyle ), 4 );
3145 ddFontStyle = fontstyle;
3150 bool ddBold =
false;
3158 bool ddItalic =
false;
3168 QFont appFont = QApplication::font();
3169 bool newFontBuilt =
false;
3170 if ( ddBold || ddItalic )
3173 newFont = QFont( !ddFontFamily.isEmpty() ? ddFontFamily : labelFont.family() );
3174 newFontBuilt =
true;
3175 newFont.setBold( ddBold );
3176 newFont.setItalic( ddItalic );
3178 else if ( !ddFontStyle.isEmpty()
3179 && ddFontStyle.compare( QLatin1String(
"Ignore" ), Qt::CaseInsensitive ) != 0 )
3181 if ( !ddFontFamily.isEmpty() )
3185 mFontDB = std::make_unique< QFontDatabase >();
3187 QFont styledfont = mFontDB->font( ddFontFamily, ddFontStyle, appFont.pointSize() );
3188 if ( appFont != styledfont )
3190 newFont = styledfont;
3191 newFontBuilt =
true;
3198 else if ( !ddFontFamily.isEmpty() )
3200 if ( ddFontStyle.compare( QLatin1String(
"Ignore" ), Qt::CaseInsensitive ) != 0 )
3204 mFontDB = std::make_unique< QFontDatabase >();
3205 QFont styledfont = mFontDB->font( ddFontFamily, mFormat.
namedStyle(), appFont.pointSize() );
3206 if ( appFont != styledfont )
3208 newFont = styledfont;
3209 newFontBuilt =
true;
3214 newFont = QFont( ddFontFamily );
3215 newFontBuilt =
true;
3223 newFont.setPixelSize( labelFont.pixelSize() );
3224 newFont.setUnderline( labelFont.underline() );
3225 newFont.setStrikeOut( labelFont.strikeOut() );
3226 newFont.setWordSpacing( labelFont.wordSpacing() );
3227 newFont.setLetterSpacing( QFont::AbsoluteSpacing, labelFont.letterSpacing() );
3229 labelFont = newFont;
3233 double wordspace = labelFont.wordSpacing();
3242 double letterspace = labelFont.letterSpacing();
3255 labelFont.setStrikeOut( strikeout );
3270 labelFont.setUnderline( underline );
3296 drawBuffer = exprVal.toBool();
3309 double bufrSize = buffer.
size();
3312 bufrSize = exprVal.toDouble();
3316 double bufferOpacity = buffer.
opacity() * 100;
3319 bufferOpacity = exprVal.toDouble();
3322 drawBuffer = ( drawBuffer && bufrSize > 0.0 && bufferOpacity > 0 );
3352 bool maskEnabled = mask.
enabled();
3355 maskEnabled = exprVal.toBool();
3364 double bufrSize = mask.
size();
3367 bufrSize = exprVal.toDouble();
3371 double opacity = mask.
opacity() * 100;
3374 opacity = exprVal.toDouble();
3377 maskEnabled = ( maskEnabled && bufrSize > 0.0 && opacity > 0 );
3402 wrapchr = exprVal.toString();
3408 evalAutoWrapLength = exprVal.toInt();
3419 if ( !exprVal.isNull() )
3421 QString
str = exprVal.toString().trimmed();
3424 if ( !
str.isEmpty() )
3429 if (
str.compare( QLatin1String(
"Center" ), Qt::CaseInsensitive ) == 0 )
3431 aligntype = Qgis::LabelMultiLineAlignment::Center;
3433 else if (
str.compare( QLatin1String(
"Right" ), Qt::CaseInsensitive ) == 0 )
3435 aligntype = Qgis::LabelMultiLineAlignment::Right;
3437 else if (
str.compare( QLatin1String(
"Follow" ), Qt::CaseInsensitive ) == 0 )
3439 aligntype = Qgis::LabelMultiLineAlignment::FollowPlacement;
3441 else if (
str.compare( QLatin1String(
"Justify" ), Qt::CaseInsensitive ) == 0 )
3443 aligntype = Qgis::LabelMultiLineAlignment::Justify;
3456 if ( !exprVal.isNull() )
3458 QString
str = exprVal.toString().trimmed();
3459 if ( !
str.isEmpty() )
3468 drawDirSymb = exprVal.toBool();
3481 if ( !exprVal.isNull() )
3483 QString
str = exprVal.toString().trimmed();
3486 if ( !
str.isEmpty() )
3491 if (
str.compare( QLatin1String(
"Above" ), Qt::CaseInsensitive ) == 0 )
3495 else if (
str.compare( QLatin1String(
"Below" ), Qt::CaseInsensitive ) == 0 )
3510 void QgsPalLayerSettings::parseShapeBackground(
QgsRenderContext &context )
3517 bool drawShape = background.
enabled();
3520 drawShape = exprVal.toBool();
3529 double shapeOpacity = background.
opacity() * 100;
3532 shapeOpacity = 100.0 * exprVal.toDouble();
3535 drawShape = ( drawShape && shapeOpacity > 0 );
3549 if ( !exprVal.isNull() )
3551 QString skind = exprVal.toString().trimmed();
3552 QgsDebugMsgLevel( QStringLiteral(
"exprVal ShapeKind:%1" ).arg( skind ), 4 );
3554 if ( !skind.isEmpty() )
3563 QString svgPath = background.
svgFile();
3568 if ( !exprVal.isNull() )
3570 QString svgfile = exprVal.toString().trimmed();
3571 QgsDebugMsgLevel( QStringLiteral(
"exprVal ShapeSVGFile:%1" ).arg( svgfile ), 4 );
3584 if ( !exprVal.isNull() )
3586 QString stype = exprVal.toString().trimmed();
3587 QgsDebugMsgLevel( QStringLiteral(
"exprVal ShapeSizeType:%1" ).arg( stype ), 4 );
3589 if ( !stype.isEmpty() )
3598 double ddShpSizeX = background.
size().width();
3601 ddShpSizeX = exprVal.toDouble();
3605 double ddShpSizeY = background.
size().height();
3608 ddShpSizeY = exprVal.toDouble();
3614 && ( svgPath.isEmpty()
3615 || ( !svgPath.isEmpty()
3617 && ddShpSizeX == 0.0 ) ) )
3625 && ddShpSizeX == 0.0 ) ) )
3632 && ( ddShpSizeX == 0.0 || ddShpSizeY == 0.0 ) )
3655 if ( !exprVal.isNull() )
3657 QString rotstr = exprVal.toString().trimmed();
3658 QgsDebugMsgLevel( QStringLiteral(
"exprVal ShapeRotationType:%1" ).arg( rotstr ), 4 );
3660 if ( !rotstr.isEmpty() )
3711 bool drawShadow = shadow.
enabled();
3714 drawShadow = exprVal.toBool();
3723 double shadowOpacity = shadow.
opacity() * 100;
3726 shadowOpacity = exprVal.toDouble();
3733 shadowOffDist = exprVal.toDouble();
3740 shadowRad = exprVal.toDouble();
3743 drawShadow = ( drawShadow && shadowOpacity > 0 && !( shadowOffDist == 0.0 && shadowRad == 0.0 ) );
3758 if ( !exprVal.isNull() )
3760 QString
str = exprVal.toString().trimmed();
3763 if ( !
str.isEmpty() )
3798 switch ( layer->
type() )
3802 const QgsVectorLayer *vl = qobject_cast< const QgsVectorLayer * >( layer );
3813 return !labeling->styles().empty();
3862 QStringList
QgsPalLabeling::splitToLines(
const QString &text,
const QString &wrapCharacter,
const int autoWrapLength,
const bool useMaxLineLengthWhenAutoWrapping )
3864 QStringList multiLineSplit;
3865 if ( !wrapCharacter.isEmpty() && wrapCharacter != QLatin1String(
"\n" ) )
3868 const QStringList lines = text.split( wrapCharacter );
3869 for (
const QString &line : lines )
3871 multiLineSplit.append( line.split(
'\n' ) );
3876 multiLineSplit = text.split(
'\n' );
3880 if ( autoWrapLength != 0 )
3882 QStringList autoWrappedLines;
3883 autoWrappedLines.reserve( multiLineSplit.count() );
3884 for (
const QString &line : std::as_const( multiLineSplit ) )
3886 autoWrappedLines.append(
QgsStringUtils::wordWrap( line, autoWrapLength, useMaxLineLengthWhenAutoWrapping ).split(
'\n' ) );
3888 multiLineSplit = autoWrappedLines;
3890 return multiLineSplit;
3895 QStringList graphemes;
3896 QTextBoundaryFinder boundaryFinder( QTextBoundaryFinder::Grapheme, text );
3897 int currentBoundary = -1;
3898 int previousBoundary = 0;
3899 while ( ( currentBoundary = boundaryFinder.toNextBoundary() ) > 0 )
3901 graphemes << text.mid( previousBoundary, currentBoundary - previousBoundary );
3902 previousBoundary = currentBoundary;
3932 QgsDebugMsgLevel( QStringLiteral(
"Ignoring feature due to transformation exception" ), 4 );
3938 return std::isfinite( point.
x() ) && std::isfinite( point.
y() );
3942 cp->removeInvalidRings();
3944 else if (
QgsMultiSurface *ms = qgsgeometry_cast< QgsMultiSurface * >( geom.
get() ) )
3946 for (
int i = 0; i < ms->numGeometries(); ++i )
3948 if (
QgsCurvePolygon *cp = qgsgeometry_cast< QgsCurvePolygon * >( ms->geometryN( i ) ) )
3949 cp->removeInvalidRings();
3961 QgsDebugMsg( QStringLiteral(
"Error rotating geometry" ).arg( geom.
asWkt() ) );
3966 #if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=9 )
3968 const bool mustClip = ( !clipGeometry.
isNull() &&
3972 bool mustClipExact =
false;
3996 QVector< QgsGeometry> parts;
3997 parts.reserve( qgsgeometry_cast< const QgsGeometryCollection * >( geom.
constGet() )->numGeometries() );
4006 parts.append( partGeom );
4014 if ( bufferGeom.
isNull() )
4016 QgsDebugMsg( QStringLiteral(
"Could not repair geometry: %1" ).arg( bufferGeom.
lastError() ) );
4023 if ( mustClipExact )
4043 QVector< QgsGeometry> parts;
4044 parts.reserve( qgsgeometry_cast< const QgsGeometryCollection * >( geom.
constGet() )->numGeometries() );
4050 partGeom = partGeom.
buffer( 0, 0 );
4052 parts.append( partGeom );
4059 if ( bufferGeom.
isNull() )
4061 QgsDebugMsg( QStringLiteral(
"Could not repair geometry: %1" ).arg( bufferGeom.
lastError() ) );
4068 if ( !clipGeometry.
isNull() &&
4105 double length = geom.
length();
4106 if ( length >= 0.0 )
4108 return ( length >= ( minSize * mapUnitsPerMM ) );
4113 double area = geom.
area();
4116 return ( std::sqrt( area ) >= ( minSize * mapUnitsPerMM ) );
4124 const QMap< QgsPalLayerSettings::Property, QVariant > &ddValues )
4127 bool changed =
false;
4133 format.
setColor( ddColor.value<QColor>() );
4158 const QMap< QgsPalLayerSettings::Property, QVariant > &ddValues )
4220 const QMap< QgsPalLayerSettings::Property, QVariant > &ddValues )
4223 bool changed =
false;
4271 buffer.
setColor( ddColor.value<QColor>() );
4298 const QMap< QgsPalLayerSettings::Property, QVariant > &ddValues )
4300 if ( ddValues.isEmpty() )
4304 bool changed =
false;
4364 const QMap< QgsPalLayerSettings::Property, QVariant > &ddValues )
4367 bool changed =
false;
4407 QSizeF size = background.
size();
4414 QSizeF size = background.
size();
4515 const QMap< QgsPalLayerSettings::Property, QVariant > &ddValues )
4518 bool changed =
false;
4577 shadow.
setColor( ddColor.value<QColor>() );
4614 #if 0 // TODO: generalize some of this
4617 double cx = lp->
getX() + w / 2.0;
4618 double cy = lp->
getY() + h / 2.0;
4621 double sw = w * scale;
4622 double sh = h * scale;
4623 QRectF rect( -sw / 2, -sh / 2, sw, sh );
4625 painter->translate( xform->
transform( QPointF( cx, cy ) ).toQPointF() );
4629 if ( lp->
getFeaturePart()->getLayer()->getArrangement() != P_POINT &&
4630 lp->
getFeaturePart()->getLayer()->getArrangement() != P_POINT_OVER &&
4633 painter->rotate( rotation );
4636 painter->translate( rect.bottomLeft() );
4637 painter->rotate( -lp->
getAlpha() * 180 / M_PI );
4638 painter->translate( -rect.bottomLeft() );
4641 QRectF rect( 0, 0, outPt2.
x() - outPt.
x(), outPt2.
y() - outPt.
y() );
4642 painter->translate( QPointF( outPt.
x(), outPt.
y() ) );
4643 painter->rotate( -lp->
getAlpha() * 180 / M_PI );
4648 painter->setPen( QColor( 255, 0, 0, 64 ) );
4652 painter->setPen( QColor( 0, 0, 0, 64 ) );
4654 painter->drawRect( rect );
4658 rect.moveTo( outPt.
x(), outPt.
y() );