QGIS API Documentation 4.1.0-Master (60fea48833c)
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(
161 const QgsPrecalculatedTextMetrics &metrics, const QPolygonF &line, double offsetAlongLine, LabelLineDirection direction, double maxConcaveAngle, double maxConvexAngle, Qgis::CurvedTextFlags flags
162)
163{
164 const std::size_t numPoints = line.size();
165 std::vector<double> pathDistances( numPoints );
166
167 const QPointF *p = line.data();
168 double dx, dy;
169
170 pathDistances[0] = 0;
171 double prevX = p->x();
172 double prevY = p->y();
173 p++;
174
175 std::vector< double > x( numPoints );
176 std::vector< double > y( numPoints );
177 x[0] = prevX;
178 y[0] = prevY;
179
180 for ( std::size_t i = 1; i < numPoints; ++i )
181 {
182 dx = p->x() - prevX;
183 dy = p->y() - prevY;
184 pathDistances[i] = std::sqrt( dx * dx + dy * dy );
185
186 prevX = p->x();
187 prevY = p->y();
188 p++;
189 x[i] = prevX;
190 y[i] = prevY;
191 }
192
193 return generateCurvedTextPlacementPrivate( metrics, x.data(), y.data(), numPoints, pathDistances, offsetAlongLine, direction, flags, maxConcaveAngle, maxConvexAngle, false );
194}
195
196std::unique_ptr< QgsTextRendererUtils::CurvePlacementProperties > QgsTextRendererUtils::generateCurvedTextPlacement(
197 const QgsPrecalculatedTextMetrics &metrics,
198 const double *x,
199 const double *y,
200 int numPoints,
201 const std::vector<double> &pathDistances,
202 double offsetAlongLine,
203 LabelLineDirection direction,
204 double maxConcaveAngle,
205 double maxConvexAngle,
207 double additionalCharacterSpacing,
208 double additionalWordSpacing
209)
210{
211 return generateCurvedTextPlacementPrivate( metrics, x, y, numPoints, pathDistances, offsetAlongLine, direction, flags, maxConcaveAngle, maxConvexAngle, false, additionalCharacterSpacing, additionalWordSpacing );
212}
213
214std::unique_ptr< QgsTextRendererUtils::CurvePlacementProperties > QgsTextRendererUtils::generateCurvedTextPlacementPrivate(
215 const QgsPrecalculatedTextMetrics &metrics,
216 const double *x,
217 const double *y,
218 int numPoints,
219 const std::vector<double> &pathDistances,
220 double offsetAlongLine,
221 LabelLineDirection direction,
223 double maxConcaveAngle,
224 double maxConvexAngle,
225 bool isSecondAttempt,
226 double additionalCharacterSpacing,
227 double additionalWordSpacing
228)
229{
230 auto output = std::make_unique< CurvePlacementProperties >();
231 output->graphemePlacement.reserve( metrics.count() );
232
233 if ( !qgsDoubleNear( additionalCharacterSpacing, 0 ) || !qgsDoubleNear( additionalWordSpacing, 0 ) )
235
236 double offsetAlongSegment = offsetAlongLine;
237 int index = 1;
238 // Find index of segment corresponding to starting offset
239 while ( index < numPoints && offsetAlongSegment > pathDistances[index] )
240 {
241 offsetAlongSegment -= pathDistances[index];
242 index += 1;
243 }
244 if ( index >= numPoints )
245 {
246 return output;
247 }
248
249 const double segmentLength = pathDistances[index];
250 if ( qgsDoubleNear( segmentLength, 0.0 ) )
251 {
252 // Not allowed to place across on 0 length segments or discontinuities
253 return output;
254 }
255
256 int characterCount = metrics.count();
257
258 if ( direction == RespectPainterOrientation && !isSecondAttempt )
259 {
260 // Calculate the orientation based on the angle of the path segment under consideration
261
262 double distance = offsetAlongSegment;
263 int endindex = index;
264
265 double startLabelX = 0;
266 double startLabelY = 0;
267 double endLabelX = 0;
268 double endLabelY = 0;
269 for ( int i = 0; i < characterCount; i++ )
270 {
271 const double characterWidth = metrics.characterWidth( i );
272 double characterStartX, characterStartY;
273
274 // calculate additional spacing for this character
275 double currentSpacing = 0.0;
276 if ( i > 0 )
277 {
278 currentSpacing = additionalCharacterSpacing;
279 if ( !qgsDoubleNear( additionalWordSpacing, 0.0 ) )
280 {
281 const QString g = metrics.grapheme( i - 1 );
282 if ( !g.isEmpty() && g.at( 0 ).isSpace() )
283 {
284 currentSpacing += additionalWordSpacing;
285 }
286 }
287 }
288
289 if ( !nextCharPosition( characterWidth, pathDistances, x, y, numPoints, endindex, distance, characterStartX, characterStartY, endLabelX, endLabelY, flags, currentSpacing ) )
290 {
292 {
293 characterCount = i + 1;
294 break;
295 }
296 else
297 {
298 return output;
299 }
300 }
301 if ( i == 0 )
302 {
303 startLabelX = characterStartX;
304 startLabelY = characterStartY;
305 }
306 }
307
308 // Determine the angle of the path segment under consideration
309 const double dx = endLabelX - startLabelX;
310 const double dy = endLabelY - startLabelY;
311 const double lineAngle = std::atan2( -dy, dx ) * 180 / M_PI;
312
313 if ( lineAngle > 90 || lineAngle < -90 )
314 {
315 output->labeledLineSegmentIsRightToLeft = true;
316 }
317 }
318
319 if ( isSecondAttempt )
320 {
321 // we know that treating the segment as running from right to left gave too many upside down characters, so try again treating the
322 // segment as left to right
323 output->labeledLineSegmentIsRightToLeft = false;
324 output->flippedCharacterPlacementToGetUprightLabels = true;
325 }
326
327 const double dx = x[index] - x[index - 1];
328 const double dy = y[index] - y[index - 1];
329
330 double angle = std::atan2( -dy, dx );
331
332 const double maxCharacterDescent = metrics.maximumCharacterDescent();
333 const double maxCharacterHeight = metrics.maximumCharacterHeight();
334
335 for ( int i = 0; i < characterCount; i++ )
336 {
337 const double lastCharacterAngle = angle;
338
339 // next character index, depending on the orientation
340 const int k = !output->flippedCharacterPlacementToGetUprightLabels ? i : characterCount - i - 1;
341
342 // grab the next character according to the orientation
343 const double characterWidth = metrics.characterWidth( k );
344 if ( qgsDoubleNear( characterWidth, 0.0 ) )
345 // Certain scripts rely on zero-width character, skip those to prevent failure (see #15801)
346 continue;
347
348 const double characterHeight = metrics.characterHeight( k );
349 const double characterDescent = metrics.characterDescent( k );
350
351 double characterStartX = 0;
352 double characterStartY = 0;
353 double characterEndX = 0;
354 double characterEndY = 0;
355
356 // Calculate Spacing
357 double currentSpacing = 0.0;
358 if ( i > 0 )
359 {
360 currentSpacing = additionalCharacterSpacing;
361 if ( !qgsDoubleNear( additionalWordSpacing, 0.0 ) )
362 {
363 int prevCharIndex = !output->flippedCharacterPlacementToGetUprightLabels ? k - 1 : k + 1;
364 if ( prevCharIndex >= 0 && prevCharIndex < metrics.count() )
365 {
366 const QString g = metrics.grapheme( prevCharIndex );
367 if ( !g.isEmpty() && g.at( 0 ).isSpace() )
368 currentSpacing += additionalWordSpacing;
369 }
370 }
371 }
372
373 if ( !nextCharPosition( characterWidth, pathDistances, x, y, numPoints, index, offsetAlongSegment, characterStartX, characterStartY, characterEndX, characterEndY, flags, currentSpacing ) )
374 {
376 {
377 characterCount = i + 1;
378 break;
379 }
380 else
381 {
382 output->graphemePlacement.clear();
383 return output;
384 }
385 }
386
387 // Calculate angle from the start of the character to the end based on start/end of character
388 angle = std::atan2( characterStartY - characterEndY, characterEndX - characterStartX );
389
390 if ( maxConcaveAngle >= 0 || maxConvexAngle >= 0 )
391 {
392 // Test lastCharacterAngle vs angle
393 // since our rendering angle has changed then check against our
394 // max allowable angle change.
395 double angleDelta = lastCharacterAngle - angle;
396 // normalise between -180 and 180
397 while ( angleDelta > M_PI )
398 angleDelta -= 2 * M_PI;
399 while ( angleDelta < -M_PI )
400 angleDelta += 2 * M_PI;
401 if ( ( maxConcaveAngle >= 0 && angleDelta > 0 && angleDelta > maxConcaveAngle ) || ( maxConvexAngle >= 0 && angleDelta < 0 && angleDelta < -maxConvexAngle ) )
402 {
403 output->graphemePlacement.clear();
404 return output;
405 }
406 }
407
409 {
410 // Shift the character downwards since the draw position is specified at the baseline
411 // and we're calculating the mean line here
412 double dist = 0.9 * maxCharacterHeight / 2 - ( maxCharacterDescent - characterDescent );
413 if ( output->flippedCharacterPlacementToGetUprightLabels )
414 {
415 dist = -dist;
416 }
417 characterStartX += dist * std::cos( angle + M_PI_2 );
418 characterStartY -= dist * std::sin( angle + M_PI_2 );
419 }
420
421 double renderAngle = angle;
422 CurvedGraphemePlacement placement;
423 placement.graphemeIndex = !output->flippedCharacterPlacementToGetUprightLabels ? i : characterCount - i - 1;
424 placement.x = characterStartX;
425 placement.y = characterStartY;
426 placement.width = characterWidth;
427 placement.height = characterHeight;
428 const QString grapheme = metrics.grapheme( placement.graphemeIndex );
429 placement.isWhitespace = grapheme.isEmpty() || grapheme.at( 0 ).isSpace() || grapheme.at( 0 ) == '\t';
430 if ( output->flippedCharacterPlacementToGetUprightLabels )
431 {
432 // rotate in place
433 placement.x += characterWidth * std::cos( renderAngle );
434 placement.y -= characterWidth * std::sin( renderAngle );
435 renderAngle += M_PI;
436 }
437 placement.angle = -renderAngle;
438 output->graphemePlacement.push_back( placement );
439
440 // Normalise to 0 <= angle < 2PI
441 while ( renderAngle >= 2 * M_PI )
442 renderAngle -= 2 * M_PI;
443 while ( renderAngle < 0 )
444 renderAngle += 2 * M_PI;
445
446 if ( renderAngle > M_PI_2 && renderAngle < 1.5 * M_PI )
447 output->upsideDownCharCount++;
448 }
449
450 if ( !isSecondAttempt && ( flags & Qgis::CurvedTextFlag::UprightCharactersOnly ) && output->upsideDownCharCount >= characterCount / 2.0 )
451 {
452 // more of text is upside down then right side up...
453 // if text should be shown upright then retry with the opposite orientation
454 return generateCurvedTextPlacementPrivate( metrics, x, y, numPoints, pathDistances, offsetAlongLine, direction, flags, maxConcaveAngle, maxConvexAngle, true, additionalCharacterSpacing, additionalWordSpacing );
455 }
456
457 return output;
458}
459
460bool QgsTextRendererUtils::nextCharPosition(
461 double charWidth,
462 const std::vector<double> &pathDistances,
463 const double *x,
464 const double *y,
465 int numPoints,
466 int &index,
467 double &currentDistanceAlongSegment,
468 double &characterStartX,
469 double &characterStartY,
470 double &characterEndX,
471 double &characterEndY,
473 double additionalSpacing
474)
475{
476 if ( !qgsDoubleNear( additionalSpacing, 0.0 ) )
477 {
478 currentDistanceAlongSegment += additionalSpacing;
479
480 // forward spacing
481 while ( index < numPoints && currentDistanceAlongSegment > pathDistances[index] )
482 {
483 currentDistanceAlongSegment -= pathDistances[index];
484 index++;
485 }
486 // backward spacing (compression)
487 while ( currentDistanceAlongSegment < 0 )
488 {
489 index--;
490 if ( index < 1 )
491 return false;
492 currentDistanceAlongSegment += pathDistances[index];
493 }
494 }
495
496 // intentional for readability:
497 // NOLINTBEGIN(bugprone-branch-clone)
498 if ( index >= numPoints )
499 {
500 // do not support extending the line start or end points via additional spacing
501 return false;
502 }
503 else if ( qgsDoubleNear( pathDistances[index], 0.0 ) )
504 {
505 // Not allowed to place across on 0 length segments or discontinuities
506 return false;
507 }
508 // NOLINTEND(bugprone-branch-clone)
509
510 double segmentStartX = x[index - 1];
511 double segmentStartY = y[index - 1];
512
513 double segmentEndX = x[index];
514 double segmentEndY = y[index];
515
516 double segmentLength = pathDistances[index];
517
518 const double segmentDx = segmentEndX - segmentStartX;
519 const double segmentDy = segmentEndY - segmentStartY;
520
521 characterStartX = segmentStartX + segmentDx * currentDistanceAlongSegment / segmentLength;
522 characterStartY = segmentStartY + segmentDy * currentDistanceAlongSegment / segmentLength;
523
524 // Coordinates this character ends at, calculated below
525 characterEndX = 0;
526 characterEndY = 0;
527
528 if ( segmentLength - currentDistanceAlongSegment >= charWidth )
529 {
530 // if the distance remaining in this segment is enough, we just go further along the segment
531 currentDistanceAlongSegment += charWidth;
532 characterEndX = segmentStartX + segmentDx * currentDistanceAlongSegment / segmentLength;
533 characterEndY = segmentStartY + segmentDy * currentDistanceAlongSegment / segmentLength;
534 }
535 else
536 {
537 // If there isn't enough distance left on this segment
538 // then we need to search until we find the line segment that ends further than ci.width away
539 do
540 {
541 index++;
542 if ( index >= numPoints ) // Bail out if we run off the end of the shape
543 {
545 {
546 // here we should extend out the final segment of the line to fit the character
547 const double lastSegmentDx = segmentEndX - segmentStartX;
548 const double lastSegmentDy = segmentEndY - segmentStartY;
549 const double lastSegmentLength = std::sqrt( lastSegmentDx * lastSegmentDx + lastSegmentDy * lastSegmentDy );
550 if ( qgsDoubleNear( lastSegmentLength, 0.0 ) )
551 {
552 // last segment has 0 length, can't extend
553 return false;
554 }
555
556 segmentEndX = segmentStartX + ( lastSegmentDx / lastSegmentLength ) * charWidth;
557 segmentEndY = segmentStartY + ( lastSegmentDy / lastSegmentLength ) * charWidth;
558 index--;
559 break;
560 }
561 else
562 {
563 return false;
564 }
565 }
566
567 segmentStartX = segmentEndX;
568 segmentStartY = segmentEndY;
569 segmentEndX = x[index];
570 segmentEndY = y[index];
571 } while ( std::sqrt( std::pow( characterStartX - segmentEndX, 2 ) + std::pow( characterStartY - segmentEndY, 2 ) ) < charWidth ); // Distance from character start to end
572
573 // Calculate the position to place the end of the character on
574 findLineCircleIntersection( characterStartX, characterStartY, charWidth, segmentStartX, segmentStartY, segmentEndX, segmentEndY, characterEndX, characterEndY );
575
576 // Need to calculate distance on the new segment
577 currentDistanceAlongSegment = std::sqrt( std::pow( segmentStartX - characterEndX, 2 ) + std::pow( segmentStartY - characterEndY, 2 ) );
578 }
579 return true;
580}
581
582void QgsTextRendererUtils::findLineCircleIntersection( double cx, double cy, double radius, double x1, double y1, double x2, double y2, double &xRes, double &yRes )
583{
584 double multiplier = 1;
585 if ( radius < 10 )
586 {
587 // these calculations get unstable for small coordinates differences, e.g. as a result of map labeling in a geographic
588 // CRS
589 multiplier = 10000;
590 x1 *= multiplier;
591 y1 *= multiplier;
592 x2 *= multiplier;
593 y2 *= multiplier;
594 cx *= multiplier;
595 cy *= multiplier;
596 radius *= multiplier;
597 }
598
599 const double dx = x2 - x1;
600 const double dy = y2 - y1;
601
602 const double A = dx * dx + dy * dy;
603 const double B = 2 * ( dx * ( x1 - cx ) + dy * ( y1 - cy ) );
604 const double C = QgsGeometryUtilsBase::sqrDistance2D( x1, y1, cx, cy ) - radius * radius;
605
606 const double det = B * B - 4 * A * C;
607 if ( A <= 0.000000000001 || det < 0 )
608 // Should never happen, No real solutions.
609 return;
610
611 if ( qgsDoubleNear( det, 0.0 ) )
612 {
613 // Could potentially happen.... One solution.
614 const double t = -B / ( 2 * A );
615 xRes = x1 + t * dx;
616 yRes = y1 + t * dy;
617 }
618 else
619 {
620 // Two solutions.
621 // Always use the 1st one
622 // We only really have one solution here, as we know the line segment will start in the circle and end outside
623 const double t = ( -B + std::sqrt( det ) ) / ( 2 * A );
624 xRes = x1 + t * dx;
625 yRes = y1 + t * dy;
626 }
627
628 if ( multiplier != 1 )
629 {
630 xRes /= multiplier;
631 yRes /= multiplier;
632 }
633}
TextOrientation
Text orientations.
Definition qgis.h:2987
@ Vertical
Vertically oriented text.
Definition qgis.h:2989
@ RotationBased
Horizontally or vertically oriented text based on rotation (only available for map labeling).
Definition qgis.h:2990
@ Horizontal
Horizontally oriented text.
Definition qgis.h:2988
RenderUnit
Rendering size units.
Definition qgis.h:5340
@ Percentage
Percentage of another measurement (e.g., canvas size, feature size).
Definition qgis.h:5344
@ Millimeters
Millimeters.
Definition qgis.h:5341
@ Points
Points (e.g., for font sizes).
Definition qgis.h:5345
@ MapUnits
Map units.
Definition qgis.h:5342
@ TruncateStringWhenLineIsTooShort
When a string is too long for the line, truncate characters instead of aborting the placement.
Definition qgis.h:3092
@ UprightCharactersOnly
Permit upright characters only. If not present then upside down text placement is permitted.
Definition qgis.h:3094
@ ExtendLineToFitText
When a string is too long for the line, extend the line's final segment to fit the entire string.
Definition qgis.h:3095
@ UseBaselinePlacement
Generate placement based on the character baselines instead of centers.
Definition qgis.h:3093
QFlags< CurvedTextFlag > CurvedTextFlags
Flags controlling behavior of curved text generation.
Definition qgis.h:3104
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:6975