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() );