QGIS API Documentation 3.99.0-Master (d270888f95f)
Loading...
Searching...
No Matches
qgstextrendererutils.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgstextrendererutils.h
3 -----------------
4 begin : May 2020
5 copyright : (C) Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
17
19#include "qgsvectorlayer.h"
20
21#include <QString>
22
23using namespace Qt::StringLiterals;
24
26{
28 const QString skind = string.trimmed();
29
30 if ( skind.compare( "Square"_L1, Qt::CaseInsensitive ) == 0 )
31 {
33 }
34 else if ( skind.compare( "Ellipse"_L1, Qt::CaseInsensitive ) == 0 )
35 {
37 }
38 else if ( skind.compare( "Circle"_L1, Qt::CaseInsensitive ) == 0 )
39 {
41 }
42 else if ( skind.compare( "SVG"_L1, Qt::CaseInsensitive ) == 0 )
43 {
45 }
46 else if ( skind.compare( "marker"_L1, Qt::CaseInsensitive ) == 0 )
47 {
49 }
50 return shpkind;
51}
52
54{
55 const QString stype = string.trimmed();
56 // "Buffer"
58
59 if ( stype.compare( "Fixed"_L1, Qt::CaseInsensitive ) == 0 )
60 {
62 }
63 return sizType;
64}
65
67{
68 const QString rotstr = string.trimmed();
69 // "Sync"
71
72 if ( rotstr.compare( "Offset"_L1, Qt::CaseInsensitive ) == 0 )
73 {
75 }
76 else if ( rotstr.compare( "Fixed"_L1, Qt::CaseInsensitive ) == 0 )
77 {
79 }
80 return rottype;
81}
82
84{
85 const QString str = string.trimmed();
86 // "Lowest"
88
89 if ( str.compare( "Text"_L1, Qt::CaseInsensitive ) == 0 )
90 {
92 }
93 else if ( str.compare( "Buffer"_L1, Qt::CaseInsensitive ) == 0 )
94 {
96 }
97 else if ( str.compare( "Background"_L1, Qt::CaseInsensitive ) == 0 )
98 {
100 }
101 return shdwtype;
102}
103
105{
106 switch ( orientation )
107 {
109 return u"horizontal"_s;
111 return u"vertical"_s;
113 return u"rotation-based"_s;
114 }
115 return QString();
116}
117
119{
120 if ( ok )
121 *ok = true;
122
123 const QString cleaned = name.toLower().trimmed();
124
125 if ( cleaned == "horizontal"_L1 )
127 else if ( cleaned == "vertical"_L1 )
129 else if ( cleaned == "rotation-based"_L1 )
131
132 if ( ok )
133 *ok = false;
135}
136
138{
139 if ( val == 0 )
141 else if ( val == 1 )
143 else if ( val == 2 )
145 else if ( val == 3 )
147 else
149}
150
151QColor QgsTextRendererUtils::readColor( QgsVectorLayer *layer, const QString &property, const QColor &defaultColor, bool withAlpha )
152{
153 const int r = layer->customProperty( property + 'R', QVariant( defaultColor.red() ) ).toInt();
154 const int g = layer->customProperty( property + 'G', QVariant( defaultColor.green() ) ).toInt();
155 const int b = layer->customProperty( property + 'B', QVariant( defaultColor.blue() ) ).toInt();
156 const int a = withAlpha ? layer->customProperty( property + 'A', QVariant( defaultColor.alpha() ) ).toInt() : 255;
157 return QColor( r, g, b, a );
158}
159
160std::unique_ptr< QgsTextRendererUtils::CurvePlacementProperties > QgsTextRendererUtils::generateCurvedTextPlacement( const QgsPrecalculatedTextMetrics &metrics, const QPolygonF &line, double offsetAlongLine, LabelLineDirection direction, double maxConcaveAngle, double maxConvexAngle, Qgis::CurvedTextFlags flags )
161{
162 const std::size_t numPoints = line.size();
163 std::vector<double> pathDistances( numPoints );
164
165 const QPointF *p = line.data();
166 double dx, dy;
167
168 pathDistances[0] = 0;
169 double prevX = p->x();
170 double prevY = p->y();
171 p++;
172
173 std::vector< double > x( numPoints );
174 std::vector< double > y( numPoints );
175 x[0] = prevX;
176 y[0] = prevY;
177
178 for ( std::size_t i = 1; i < numPoints; ++i )
179 {
180 dx = p->x() - prevX;
181 dy = p->y() - prevY;
182 pathDistances[i] = std::sqrt( dx * dx + dy * dy );
183
184 prevX = p->x();
185 prevY = p->y();
186 p++;
187 x[i] = prevX;
188 y[i] = prevY;
189 }
190
191 return generateCurvedTextPlacementPrivate( metrics, x.data(), y.data(), numPoints, pathDistances, offsetAlongLine, direction, flags, maxConcaveAngle, maxConvexAngle, false );
192}
193
194std::unique_ptr< QgsTextRendererUtils::CurvePlacementProperties > QgsTextRendererUtils::generateCurvedTextPlacement( const QgsPrecalculatedTextMetrics &metrics, const double *x, const double *y, int numPoints, const std::vector<double> &pathDistances, double offsetAlongLine, LabelLineDirection direction, double maxConcaveAngle, double maxConvexAngle, Qgis::CurvedTextFlags flags, double additionalCharacterSpacing, double additionalWordSpacing )
195{
196 return generateCurvedTextPlacementPrivate( metrics, x, y, numPoints, pathDistances, offsetAlongLine, direction, flags, maxConcaveAngle, maxConvexAngle, false, additionalCharacterSpacing, additionalWordSpacing );
197}
198
199std::unique_ptr< QgsTextRendererUtils::CurvePlacementProperties > QgsTextRendererUtils::generateCurvedTextPlacementPrivate( const QgsPrecalculatedTextMetrics &metrics, const double *x, const double *y, int numPoints, const std::vector<double> &pathDistances, double offsetAlongLine, LabelLineDirection direction, Qgis::CurvedTextFlags flags, double maxConcaveAngle, double maxConvexAngle, bool isSecondAttempt, double additionalCharacterSpacing, double additionalWordSpacing )
200{
201 auto output = std::make_unique< CurvePlacementProperties >();
202 output->graphemePlacement.reserve( metrics.count() );
203
204 if ( !qgsDoubleNear( additionalCharacterSpacing, 0 ) || !qgsDoubleNear( additionalWordSpacing, 0 ) )
206
207 double offsetAlongSegment = offsetAlongLine;
208 int index = 1;
209 // Find index of segment corresponding to starting offset
210 while ( index < numPoints && offsetAlongSegment > pathDistances[index] )
211 {
212 offsetAlongSegment -= pathDistances[index];
213 index += 1;
214 }
215 if ( index >= numPoints )
216 {
217 return output;
218 }
219
220 const double segmentLength = pathDistances[index];
221 if ( qgsDoubleNear( segmentLength, 0.0 ) )
222 {
223 // Not allowed to place across on 0 length segments or discontinuities
224 return output;
225 }
226
227 int characterCount = metrics.count();
228
229 if ( direction == RespectPainterOrientation && !isSecondAttempt )
230 {
231 // Calculate the orientation based on the angle of the path segment under consideration
232
233 double distance = offsetAlongSegment;
234 int endindex = index;
235
236 double startLabelX = 0;
237 double startLabelY = 0;
238 double endLabelX = 0;
239 double endLabelY = 0;
240 for ( int i = 0; i < characterCount; i++ )
241 {
242 const double characterWidth = metrics.characterWidth( i );
243 double characterStartX, characterStartY;
244
245 // calculate additional spacing for this character
246 double currentSpacing = 0.0;
247 if ( i > 0 )
248 {
249 currentSpacing = additionalCharacterSpacing;
250 if ( !qgsDoubleNear( additionalWordSpacing, 0.0 ) )
251 {
252 const QString g = metrics.grapheme( i - 1 );
253 if ( !g.isEmpty() && g.at( 0 ).isSpace() )
254 {
255 currentSpacing += additionalWordSpacing;
256 }
257 }
258 }
259
260 if ( !nextCharPosition( characterWidth, pathDistances, x, y, numPoints, endindex, distance, characterStartX, characterStartY, endLabelX, endLabelY, flags, currentSpacing ) )
261 {
263 {
264 characterCount = i + 1;
265 break;
266 }
267 else
268 {
269 return output;
270 }
271 }
272 if ( i == 0 )
273 {
274 startLabelX = characterStartX;
275 startLabelY = characterStartY;
276 }
277 }
278
279 // Determine the angle of the path segment under consideration
280 const double dx = endLabelX - startLabelX;
281 const double dy = endLabelY - startLabelY;
282 const double lineAngle = std::atan2( -dy, dx ) * 180 / M_PI;
283
284 if ( lineAngle > 90 || lineAngle < -90 )
285 {
286 output->labeledLineSegmentIsRightToLeft = true;
287 }
288 }
289
290 if ( isSecondAttempt )
291 {
292 // we know that treating the segment as running from right to left gave too many upside down characters, so try again treating the
293 // segment as left to right
294 output->labeledLineSegmentIsRightToLeft = false;
295 output->flippedCharacterPlacementToGetUprightLabels = true;
296 }
297
298 const double dx = x[index] - x[index - 1];
299 const double dy = y[index] - y[index - 1];
300
301 double angle = std::atan2( -dy, dx );
302
303 const double maxCharacterDescent = metrics.maximumCharacterDescent();
304 const double maxCharacterHeight = metrics.maximumCharacterHeight();
305
306 for ( int i = 0; i < characterCount; i++ )
307 {
308 const double lastCharacterAngle = angle;
309
310 // next character index, depending on the orientation
311 const int k = !output->flippedCharacterPlacementToGetUprightLabels ? i : characterCount - i - 1;
312
313 // grab the next character according to the orientation
314 const double characterWidth = metrics.characterWidth( k );
315 if ( qgsDoubleNear( characterWidth, 0.0 ) )
316 // Certain scripts rely on zero-width character, skip those to prevent failure (see #15801)
317 continue;
318
319 const double characterHeight = metrics.characterHeight( k );
320 const double characterDescent = metrics.characterDescent( k );
321
322 double characterStartX = 0;
323 double characterStartY = 0;
324 double characterEndX = 0;
325 double characterEndY = 0;
326
327 // Calculate Spacing
328 double currentSpacing = 0.0;
329 if ( i > 0 )
330 {
331 currentSpacing = additionalCharacterSpacing;
332 if ( !qgsDoubleNear( additionalWordSpacing, 0.0 ) )
333 {
334 int prevCharIndex = !output->flippedCharacterPlacementToGetUprightLabels ? k - 1 : k + 1;
335 if ( prevCharIndex >= 0 && prevCharIndex < metrics.count() )
336 {
337 const QString g = metrics.grapheme( prevCharIndex );
338 if ( !g.isEmpty() && g.at( 0 ).isSpace() )
339 currentSpacing += additionalWordSpacing;
340 }
341 }
342 }
343
344 if ( !nextCharPosition( characterWidth, pathDistances, x, y, numPoints, index, offsetAlongSegment, characterStartX, characterStartY, characterEndX, characterEndY, flags, currentSpacing ) )
345 {
347 {
348 characterCount = i + 1;
349 break;
350 }
351 else
352 {
353 output->graphemePlacement.clear();
354 return output;
355 }
356 }
357
358 // Calculate angle from the start of the character to the end based on start/end of character
359 angle = std::atan2( characterStartY - characterEndY, characterEndX - characterStartX );
360
361 if ( maxConcaveAngle >= 0 || maxConvexAngle >= 0 )
362 {
363 // Test lastCharacterAngle vs angle
364 // since our rendering angle has changed then check against our
365 // max allowable angle change.
366 double angleDelta = lastCharacterAngle - angle;
367 // normalise between -180 and 180
368 while ( angleDelta > M_PI )
369 angleDelta -= 2 * M_PI;
370 while ( angleDelta < -M_PI )
371 angleDelta += 2 * M_PI;
372 if ( ( maxConcaveAngle >= 0 && angleDelta > 0 && angleDelta > maxConcaveAngle ) || ( maxConvexAngle >= 0 && angleDelta < 0 && angleDelta < -maxConvexAngle ) )
373 {
374 output->graphemePlacement.clear();
375 return output;
376 }
377 }
378
380 {
381 // Shift the character downwards since the draw position is specified at the baseline
382 // and we're calculating the mean line here
383 double dist = 0.9 * maxCharacterHeight / 2 - ( maxCharacterDescent - characterDescent );
384 if ( output->flippedCharacterPlacementToGetUprightLabels )
385 {
386 dist = -dist;
387 }
388 characterStartX += dist * std::cos( angle + M_PI_2 );
389 characterStartY -= dist * std::sin( angle + M_PI_2 );
390 }
391
392 double renderAngle = angle;
393 CurvedGraphemePlacement placement;
394 placement.graphemeIndex = !output->flippedCharacterPlacementToGetUprightLabels ? i : characterCount - i - 1;
395 placement.x = characterStartX;
396 placement.y = characterStartY;
397 placement.width = characterWidth;
398 placement.height = characterHeight;
399 const QString grapheme = metrics.grapheme( placement.graphemeIndex );
400 placement.isWhitespace = grapheme.isEmpty() || grapheme.at( 0 ).isSpace() || grapheme.at( 0 ) == '\t';
401 if ( output->flippedCharacterPlacementToGetUprightLabels )
402 {
403 // rotate in place
404 placement.x += characterWidth * std::cos( renderAngle );
405 placement.y -= characterWidth * std::sin( renderAngle );
406 renderAngle += M_PI;
407 }
408 placement.angle = -renderAngle;
409 output->graphemePlacement.push_back( placement );
410
411 // Normalise to 0 <= angle < 2PI
412 while ( renderAngle >= 2 * M_PI )
413 renderAngle -= 2 * M_PI;
414 while ( renderAngle < 0 )
415 renderAngle += 2 * M_PI;
416
417 if ( renderAngle > M_PI_2 && renderAngle < 1.5 * M_PI )
418 output->upsideDownCharCount++;
419 }
420
421 if ( !isSecondAttempt && ( flags & Qgis::CurvedTextFlag::UprightCharactersOnly ) && output->upsideDownCharCount >= characterCount / 2.0 )
422 {
423 // more of text is upside down then right side up...
424 // if text should be shown upright then retry with the opposite orientation
425 return generateCurvedTextPlacementPrivate( metrics, x, y, numPoints, pathDistances, offsetAlongLine, direction, flags, maxConcaveAngle, maxConvexAngle, true, additionalCharacterSpacing, additionalWordSpacing );
426 }
427
428 return output;
429}
430
431bool QgsTextRendererUtils::nextCharPosition( double charWidth, const std::vector<double> &pathDistances, const double *x, const double *y, int numPoints, int &index, double &currentDistanceAlongSegment, double &characterStartX, double &characterStartY, double &characterEndX, double &characterEndY, Qgis::CurvedTextFlags flags, double additionalSpacing )
432{
433 if ( !qgsDoubleNear( additionalSpacing, 0.0 ) )
434 {
435 currentDistanceAlongSegment += additionalSpacing;
436
437 // forward spacing
438 while ( index < numPoints && currentDistanceAlongSegment > pathDistances[index] )
439 {
440 currentDistanceAlongSegment -= pathDistances[index];
441 index++;
442 }
443 // backward spacing (compression)
444 while ( currentDistanceAlongSegment < 0 )
445 {
446 index--;
447 if ( index < 1 )
448 return false;
449 currentDistanceAlongSegment += pathDistances[index];
450 }
451 }
452
453 // intentional for readability:
454 // NOLINTBEGIN(bugprone-branch-clone)
455 if ( index >= numPoints )
456 {
457 // do not support extending the line start or end points via additional spacing
458 return false;
459 }
460 else if ( qgsDoubleNear( pathDistances[index], 0.0 ) )
461 {
462 // Not allowed to place across on 0 length segments or discontinuities
463 return false;
464 }
465 // NOLINTEND(bugprone-branch-clone)
466
467 double segmentStartX = x[index - 1];
468 double segmentStartY = y[index - 1];
469
470 double segmentEndX = x[index];
471 double segmentEndY = y[index];
472
473 double segmentLength = pathDistances[index];
474
475 const double segmentDx = segmentEndX - segmentStartX;
476 const double segmentDy = segmentEndY - segmentStartY;
477
478 characterStartX = segmentStartX + segmentDx * currentDistanceAlongSegment / segmentLength;
479 characterStartY = segmentStartY + segmentDy * currentDistanceAlongSegment / segmentLength;
480
481 // Coordinates this character ends at, calculated below
482 characterEndX = 0;
483 characterEndY = 0;
484
485 if ( segmentLength - currentDistanceAlongSegment >= charWidth )
486 {
487 // if the distance remaining in this segment is enough, we just go further along the segment
488 currentDistanceAlongSegment += charWidth;
489 characterEndX = segmentStartX + segmentDx * currentDistanceAlongSegment / segmentLength;
490 characterEndY = segmentStartY + segmentDy * currentDistanceAlongSegment / segmentLength;
491 }
492 else
493 {
494 // If there isn't enough distance left on this segment
495 // then we need to search until we find the line segment that ends further than ci.width away
496 do
497 {
498 index++;
499 if ( index >= numPoints ) // Bail out if we run off the end of the shape
500 {
502 {
503 // here we should extend out the final segment of the line to fit the character
504 const double lastSegmentDx = segmentEndX - segmentStartX;
505 const double lastSegmentDy = segmentEndY - segmentStartY;
506 const double lastSegmentLength = std::sqrt( lastSegmentDx * lastSegmentDx + lastSegmentDy * lastSegmentDy );
507 if ( qgsDoubleNear( lastSegmentLength, 0.0 ) )
508 {
509 // last segment has 0 length, can't extend
510 return false;
511 }
512
513 segmentEndX = segmentStartX + ( lastSegmentDx / lastSegmentLength ) * charWidth;
514 segmentEndY = segmentStartY + ( lastSegmentDy / lastSegmentLength ) * charWidth;
515 index--;
516 break;
517
518 }
519 else
520 {
521 return false;
522 }
523 }
524
525 segmentStartX = segmentEndX;
526 segmentStartY = segmentEndY;
527 segmentEndX = x[index];
528 segmentEndY = y[index];
529 }
530 while ( std::sqrt( std::pow( characterStartX - segmentEndX, 2 ) + std::pow( characterStartY - segmentEndY, 2 ) ) < charWidth ); // Distance from character start to end
531
532 // Calculate the position to place the end of the character on
533 findLineCircleIntersection( characterStartX, characterStartY, charWidth, segmentStartX, segmentStartY, segmentEndX, segmentEndY, characterEndX, characterEndY );
534
535 // Need to calculate distance on the new segment
536 currentDistanceAlongSegment = std::sqrt( std::pow( segmentStartX - characterEndX, 2 ) + std::pow( segmentStartY - characterEndY, 2 ) );
537 }
538 return true;
539}
540
541void QgsTextRendererUtils::findLineCircleIntersection( double cx, double cy, double radius, double x1, double y1, double x2, double y2, double &xRes, double &yRes )
542{
543 double multiplier = 1;
544 if ( radius < 10 )
545 {
546 // these calculations get unstable for small coordinates differences, e.g. as a result of map labeling in a geographic
547 // CRS
548 multiplier = 10000;
549 x1 *= multiplier;
550 y1 *= multiplier;
551 x2 *= multiplier;
552 y2 *= multiplier;
553 cx *= multiplier;
554 cy *= multiplier;
555 radius *= multiplier;
556 }
557
558 const double dx = x2 - x1;
559 const double dy = y2 - y1;
560
561 const double A = dx * dx + dy * dy;
562 const double B = 2 * ( dx * ( x1 - cx ) + dy * ( y1 - cy ) );
563 const double C = QgsGeometryUtilsBase::sqrDistance2D( x1, y1, cx, cy ) - radius * radius;
564
565 const double det = B * B - 4 * A * C;
566 if ( A <= 0.000000000001 || det < 0 )
567 // Should never happen, No real solutions.
568 return;
569
570 if ( qgsDoubleNear( det, 0.0 ) )
571 {
572 // Could potentially happen.... One solution.
573 const double t = -B / ( 2 * A );
574 xRes = x1 + t * dx;
575 yRes = y1 + t * dy;
576 }
577 else
578 {
579 // Two solutions.
580 // Always use the 1st one
581 // We only really have one solution here, as we know the line segment will start in the circle and end outside
582 const double t = ( -B + std::sqrt( det ) ) / ( 2 * A );
583 xRes = x1 + t * dx;
584 yRes = y1 + t * dy;
585 }
586
587 if ( multiplier != 1 )
588 {
589 xRes /= multiplier;
590 yRes /= multiplier;
591 }
592}
TextOrientation
Text orientations.
Definition qgis.h:2944
@ Vertical
Vertically oriented text.
Definition qgis.h:2946
@ RotationBased
Horizontally or vertically oriented text based on rotation (only available for map labeling).
Definition qgis.h:2947
@ Horizontal
Horizontally oriented text.
Definition qgis.h:2945
RenderUnit
Rendering size units.
Definition qgis.h:5255
@ Percentage
Percentage of another measurement (e.g., canvas size, feature size).
Definition qgis.h:5259
@ Millimeters
Millimeters.
Definition qgis.h:5256
@ Points
Points (e.g., for font sizes).
Definition qgis.h:5260
@ MapUnits
Map units.
Definition qgis.h:5257
@ TruncateStringWhenLineIsTooShort
When a string is too long for the line, truncate characters instead of aborting the placement.
Definition qgis.h:3049
@ UprightCharactersOnly
Permit upright characters only. If not present then upside down text placement is permitted.
Definition qgis.h:3051
@ ExtendLineToFitText
When a string is too long for the line, extend the line's final segment to fit the entire string.
Definition qgis.h:3052
@ UseBaselinePlacement
Generate placement based on the character baselines instead of centers.
Definition qgis.h:3050
QFlags< CurvedTextFlag > CurvedTextFlags
Flags controlling behavior of curved text generation.
Definition qgis.h:3061
static double sqrDistance2D(double x1, double y1, double x2, double y2)
Returns the squared 2D distance between (x1, y1) and (x2, y2).
Q_INVOKABLE QVariant customProperty(const QString &value, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer.
Contains precalculated properties regarding text metrics for text to be rendered at a later stage.
double maximumCharacterHeight() const
Returns the maximum height of any character found in the text.
double characterDescent(int position) const
Returns the descent of the character at the specified position.
int count() const
Returns the total number of characters.
double maximumCharacterDescent() const
Returns the maximum descent of any character found in the text.
double characterWidth(int position) const
Returns the width of the character at the specified position.
QString grapheme(int index) const
Returns the grapheme at the specified index.
double characterHeight(int position) const
Returns the character height of the character at the specified position (actually font metrics height...
SizeType
Methods for determining the background shape size.
@ SizeBuffer
Shape size is determined by adding a buffer margin around text.
@ ShapeSquare
Square - buffered sizes only.
RotationType
Methods for determining the rotation of the background shape.
@ RotationOffset
Shape rotation is offset from text rotation.
@ RotationSync
Shape rotation is synced with text rotation.
@ RotationFixed
Shape rotation is a fixed angle.
Contains placement information for a single grapheme in a curved text layout.
int graphemeIndex
Index of corresponding grapheme.
static QgsTextBackgroundSettings::ShapeType decodeShapeType(const QString &string)
Decodes a string representation of a background shape type to a type.
static std::unique_ptr< CurvePlacementProperties > generateCurvedTextPlacement(const QgsPrecalculatedTextMetrics &metrics, const QPolygonF &line, double offsetAlongLine, LabelLineDirection direction=RespectPainterOrientation, double maxConcaveAngle=-1, double maxConvexAngle=-1, Qgis::CurvedTextFlags flags=Qgis::CurvedTextFlags())
Calculates curved text placement properties.
static Qgis::TextOrientation decodeTextOrientation(const QString &name, bool *ok=nullptr)
Attempts to decode a string representation of a text orientation.
LabelLineDirection
Controls behavior of curved text with respect to line directions.
@ RespectPainterOrientation
Curved text will be placed respecting the painter orientation, and the actual line direction will be ...
static QColor readColor(QgsVectorLayer *layer, const QString &property, const QColor &defaultColor=Qt::black, bool withAlpha=true)
Converts an encoded color value from a layer property.
static QgsTextShadowSettings::ShadowPlacement decodeShadowPlacementType(const QString &string)
Decodes a string representation of a shadow placement type to a type.
static QgsTextBackgroundSettings::RotationType decodeBackgroundRotationType(const QString &string)
Decodes a string representation of a background rotation type to a type.
static QString encodeTextOrientation(Qgis::TextOrientation orientation)
Encodes a text orientation.
static QgsTextBackgroundSettings::SizeType decodeBackgroundSizeType(const QString &string)
Decodes a string representation of a background size type to a type.
static Qgis::RenderUnit convertFromOldLabelUnit(int val)
Converts a unit from an old (pre 3.0) label unit.
ShadowPlacement
Placement positions for text shadow.
@ ShadowBuffer
Draw shadow under buffer.
@ ShadowShape
Draw shadow under background shape.
@ ShadowLowest
Draw shadow below all text components.
@ ShadowText
Draw shadow under text.
Represents a vector layer which manages a vector based dataset.
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored).
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6900