QGIS API Documentation 4.3.0-Master (e0370aa57ef)
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
18#include "qgsdatadefinedutils.h"
20#include "qgsvectorlayer.h"
21
22#include <QString>
23
24using namespace Qt::StringLiterals;
25
27{
28 static constexpr auto SHAPE_TYPE_MAP = makeDataDefinedStringDecoder<QgsTextBackgroundSettings::ShapeType>( {
35 } );
36
37 auto evaluatedType = SHAPE_TYPE_MAP.lookup( string );
38 if ( evaluatedType )
39 {
40 return *evaluatedType;
41 }
43}
44
46{
47 static constexpr auto SIZE_TYPE_MAP = makeDataDefinedStringDecoder<QgsTextBackgroundSettings::SizeType>( {
50 } );
51
52 auto evaluatedType = SIZE_TYPE_MAP.lookup( string );
53 if ( evaluatedType )
54 {
55 return *evaluatedType;
56 }
58}
59
61{
62 static constexpr auto ROTATION_TYPE_MAP = makeDataDefinedStringDecoder<QgsTextBackgroundSettings::RotationType>( {
66 } );
67
68 auto evaluatedType = ROTATION_TYPE_MAP.lookup( string );
69 if ( evaluatedType )
70 {
71 return *evaluatedType;
72 }
74}
75
77{
78 static constexpr auto SHADOW_PLACEMENT_MAP = makeDataDefinedStringDecoder<QgsTextShadowSettings::ShadowPlacement>( {
81 { u"background", QgsTextShadowSettings::ShadowShape },
83 } );
84
85 auto evaluatedType = SHADOW_PLACEMENT_MAP.lookup( string );
86 if ( evaluatedType )
87 {
88 return *evaluatedType;
89 }
91}
92
94{
95 switch ( orientation )
96 {
98 return u"horizontal"_s;
100 return u"vertical"_s;
102 return u"rotation-based"_s;
103 }
104 return QString();
105}
106
108{
109 if ( ok )
110 *ok = true;
111
112 static constexpr auto ORIENTATION_MAP = makeDataDefinedStringDecoder<Qgis::TextOrientation>( {
113 { u"horizontal", Qgis::TextOrientation::Horizontal },
114 { u"vertical", Qgis::TextOrientation::Vertical },
115 { u"rotationbased", Qgis::TextOrientation::RotationBased },
116 } );
117
118 auto evaluatedOrientation = ORIENTATION_MAP.lookup( name );
119 if ( evaluatedOrientation )
120 {
121 return *evaluatedOrientation;
122 }
123
124 if ( ok )
125 *ok = false;
127}
128
130{
131 if ( val == 0 )
133 else if ( val == 1 )
135 else if ( val == 2 )
137 else if ( val == 3 )
139 else
141}
142
143QColor QgsTextRendererUtils::readColor( QgsVectorLayer *layer, const QString &property, const QColor &defaultColor, bool withAlpha )
144{
145 const int r = layer->customProperty( property + 'R', QVariant( defaultColor.red() ) ).toInt();
146 const int g = layer->customProperty( property + 'G', QVariant( defaultColor.green() ) ).toInt();
147 const int b = layer->customProperty( property + 'B', QVariant( defaultColor.blue() ) ).toInt();
148 const int a = withAlpha ? layer->customProperty( property + 'A', QVariant( defaultColor.alpha() ) ).toInt() : 255;
149 return QColor( r, g, b, a );
150}
151
152std::unique_ptr< QgsTextRendererUtils::CurvePlacementProperties > QgsTextRendererUtils::generateCurvedTextPlacement(
153 const QgsPrecalculatedTextMetrics &metrics,
154 const QPolygonF &line,
155 double offsetAlongLine,
156 LabelLineDirection direction,
157 double maxConcaveAngle,
158 double maxConvexAngle,
160 Qgis::TextAnchorPoint textAnchor
161)
162{
163 const std::size_t numPoints = line.size();
164 std::vector<double> pathDistances( numPoints );
165
166 const QPointF *p = line.data();
167 double dx, dy;
168
169 pathDistances[0] = 0;
170 double prevX = p->x();
171 double prevY = p->y();
172 p++;
173
174 std::vector< double > x( numPoints );
175 std::vector< double > y( numPoints );
176 x[0] = prevX;
177 y[0] = prevY;
178
179 for ( std::size_t i = 1; i < numPoints; ++i )
180 {
181 dx = p->x() - prevX;
182 dy = p->y() - prevY;
183 pathDistances[i] = std::sqrt( dx * dx + dy * dy );
184
185 prevX = p->x();
186 prevY = p->y();
187 p++;
188 x[i] = prevX;
189 y[i] = prevY;
190 }
191
192 return generateCurvedTextPlacementPrivate( metrics, x.data(), y.data(), numPoints, pathDistances, offsetAlongLine, direction, flags, maxConcaveAngle, maxConvexAngle, false, 0, 0, textAnchor );
193}
194
195std::unique_ptr< QgsTextRendererUtils::CurvePlacementProperties > QgsTextRendererUtils::generateCurvedTextPlacement(
196 const QgsPrecalculatedTextMetrics &metrics,
197 const double *x,
198 const double *y,
199 int numPoints,
200 const std::vector<double> &pathDistances,
201 double offsetAlongLine,
202 LabelLineDirection direction,
203 double maxConcaveAngle,
204 double maxConvexAngle,
206 double additionalCharacterSpacing,
207 double additionalWordSpacing,
208 Qgis::TextAnchorPoint textAnchor
209)
210{
211 return generateCurvedTextPlacementPrivate( metrics, x, y, numPoints, pathDistances, offsetAlongLine, direction, flags, maxConcaveAngle, maxConvexAngle, false, additionalCharacterSpacing, additionalWordSpacing, textAnchor );
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 Qgis::TextAnchorPoint textAnchor
229)
230{
231 auto output = std::make_unique< CurvePlacementProperties >();
232 output->graphemePlacement.reserve( metrics.count() );
233
234 if ( !qgsDoubleNear( additionalCharacterSpacing, 0 ) || !qgsDoubleNear( additionalWordSpacing, 0 ) )
236
237 double totalLineLength = 0.0;
238 for ( double pathDistance : pathDistances )
239 {
240 totalLineLength += pathDistance;
241 }
242
243 // cleanup incompatible parameters
244 switch ( textAnchor )
245 {
247 break;
248
251 // TruncateStringWhenLineIsTooShort, offset along line not supported in these modes
253 offsetAlongLine = 0;
254 break;
255
257 // follow placement mode not supported here!
259 break;
260 }
261
262 double totalTextWidth = 0.0;
263 int characterCount = metrics.count();
264 for ( int i = 0; i < characterCount; ++i )
265 {
266 const double currentCharacterWidth = metrics.characterWidth( i );
267 double spacing = 0.0;
268 if ( i > 0 )
269 {
270 spacing = additionalCharacterSpacing;
271 if ( !qgsDoubleNear( additionalWordSpacing, 0.0 ) )
272 {
273 const QString g = metrics.grapheme( i - 1 );
274 if ( !g.isEmpty() && g.at( 0 ).isSpace() )
275 {
276 spacing += additionalWordSpacing;
277 }
278 }
279 }
280 totalTextWidth += currentCharacterWidth + spacing;
281 }
282 double adjustedOffsetAlongLine = 0.0;
283 // expand calculated text width by a couple of pixels -- it's ok for us to run over the line
284 // by a couple of pixels, and we don't want to risk truncating text due to floating point
285 // calculation fuzziness
286 constexpr double TEXT_WIDTH_ADJUSTMENT_FACTOR = 1.5;
287 switch ( textAnchor )
288 {
290 adjustedOffsetAlongLine = offsetAlongLine;
291 break;
292
294 adjustedOffsetAlongLine = ( totalLineLength - ( totalTextWidth + TEXT_WIDTH_ADJUSTMENT_FACTOR ) ) / 2.0;
295 break;
296
298 adjustedOffsetAlongLine = totalLineLength - ( totalTextWidth + TEXT_WIDTH_ADJUSTMENT_FACTOR );
299 break;
300
302 break;
303 }
304
305 std::vector< double > modifiedX( x, x + numPoints );
306 std::vector< double > modifiedY( y, y + numPoints );
307 std::vector< double > modifiedPathDistances = pathDistances;
308
309 if ( adjustedOffsetAlongLine < 0.0 && ( flags & Qgis::CurvedTextFlag::ExtendLineToFitText ) )
310 {
311 // extend first segment of line to adjust for negative start offsets
312 const double extension = std::abs( adjustedOffsetAlongLine );
313 const double firstSegmentLength = modifiedPathDistances[1];
314 if ( firstSegmentLength > 0.0 )
315 {
316 const double dx = modifiedX[1] - modifiedX[0];
317 const double dy = modifiedY[1] - modifiedY[0];
318 modifiedX[0] -= ( dx / firstSegmentLength ) * extension;
319 modifiedY[0] -= ( dy / firstSegmentLength ) * extension;
320 modifiedPathDistances[1] += extension;
321 adjustedOffsetAlongLine = 0.0;
322 }
323 }
324
325 double offsetAlongSegment = adjustedOffsetAlongLine;
326 int index = 1;
327 // Find index of segment corresponding to starting offset
328 while ( index < numPoints && offsetAlongSegment > modifiedPathDistances[index] )
329 {
330 offsetAlongSegment -= modifiedPathDistances[index];
331 index += 1;
332 }
333 if ( index >= numPoints )
334 {
335 return output;
336 }
337
338 const double segmentLength = modifiedPathDistances[index];
339 if ( qgsDoubleNear( segmentLength, 0.0 ) )
340 {
341 // Not allowed to place across on 0 length segments or discontinuities
342 return output;
343 }
344
345 if ( direction == RespectPainterOrientation && !isSecondAttempt )
346 {
347 // Calculate the orientation based on the angle of the path segment under consideration
348
349 double distance = offsetAlongSegment;
350 int endindex = index;
351
352 double startLabelX = 0;
353 double startLabelY = 0;
354 double endLabelX = 0;
355 double endLabelY = 0;
356 for ( int i = 0; i < characterCount; i++ )
357 {
358 const double characterWidth = metrics.characterWidth( i );
359 double characterStartX, characterStartY;
360
361 // calculate additional spacing for this character
362 double currentSpacing = 0.0;
363 if ( i > 0 )
364 {
365 currentSpacing = additionalCharacterSpacing;
366 if ( !qgsDoubleNear( additionalWordSpacing, 0.0 ) )
367 {
368 const QString g = metrics.grapheme( i - 1 );
369 if ( !g.isEmpty() && g.at( 0 ).isSpace() )
370 {
371 currentSpacing += additionalWordSpacing;
372 }
373 }
374 }
375
376 if ( !nextCharPosition( characterWidth, modifiedPathDistances, modifiedX.data(), modifiedY.data(), numPoints, endindex, distance, characterStartX, characterStartY, endLabelX, endLabelY, flags, currentSpacing ) )
377 {
379 {
380 characterCount = i + 1;
381 break;
382 }
383 else
384 {
385 return output;
386 }
387 }
388 if ( i == 0 )
389 {
390 startLabelX = characterStartX;
391 startLabelY = characterStartY;
392 }
393 }
394
395 // Determine the angle of the path segment under consideration
396 const double dx = endLabelX - startLabelX;
397 const double dy = endLabelY - startLabelY;
398 const double lineAngle = std::atan2( -dy, dx ) * 180 / M_PI;
399
400 if ( lineAngle > 90 || lineAngle < -90 )
401 {
402 output->labeledLineSegmentIsRightToLeft = true;
403 }
404 }
405
406 if ( isSecondAttempt )
407 {
408 // we know that treating the segment as running from right to left gave too many upside down characters, so try again treating the
409 // segment as left to right
410 output->labeledLineSegmentIsRightToLeft = false;
411 output->flippedCharacterPlacementToGetUprightLabels = true;
412 }
413
414 const double dx = modifiedX[index] - modifiedX[index - 1];
415 const double dy = modifiedY[index] - modifiedY[index - 1];
416
417 double angle = std::atan2( -dy, dx );
418
419 const double maxCharacterDescent = metrics.maximumCharacterDescent();
420 const double maxCharacterHeight = metrics.maximumCharacterHeight();
421
422 for ( int i = 0; i < characterCount; i++ )
423 {
424 const double lastCharacterAngle = angle;
425
426 // next character index, depending on the orientation
427 const int k = !output->flippedCharacterPlacementToGetUprightLabels ? i : characterCount - i - 1;
428
429 // grab the next character according to the orientation
430 const double characterWidth = metrics.characterWidth( k );
431 if ( qgsDoubleNear( characterWidth, 0.0 ) )
432 // Certain scripts rely on zero-width character, skip those to prevent failure (see #15801)
433 continue;
434
435 const double characterHeight = metrics.characterHeight( k );
436 const double characterDescent = metrics.characterDescent( k );
437
438 double characterStartX = 0;
439 double characterStartY = 0;
440 double characterEndX = 0;
441 double characterEndY = 0;
442
443 // Calculate Spacing
444 double currentSpacing = 0.0;
445 if ( i > 0 )
446 {
447 currentSpacing = additionalCharacterSpacing;
448 if ( !qgsDoubleNear( additionalWordSpacing, 0.0 ) )
449 {
450 int prevCharIndex = !output->flippedCharacterPlacementToGetUprightLabels ? k - 1 : k + 1;
451 if ( prevCharIndex >= 0 && prevCharIndex < metrics.count() )
452 {
453 const QString g = metrics.grapheme( prevCharIndex );
454 if ( !g.isEmpty() && g.at( 0 ).isSpace() )
455 currentSpacing += additionalWordSpacing;
456 }
457 }
458 }
459
460 if ( !nextCharPosition( characterWidth, modifiedPathDistances, modifiedX.data(), modifiedY.data(), numPoints, index, offsetAlongSegment, characterStartX, characterStartY, characterEndX, characterEndY, flags, currentSpacing ) )
461 {
463 {
464 characterCount = i + 1;
465 break;
466 }
467 else
468 {
469 output->graphemePlacement.clear();
470 return output;
471 }
472 }
473
474 // Calculate angle from the start of the character to the end based on start/end of character
475 angle = std::atan2( characterStartY - characterEndY, characterEndX - characterStartX );
476
477 if ( maxConcaveAngle >= 0 || maxConvexAngle >= 0 )
478 {
479 // Test lastCharacterAngle vs angle
480 // since our rendering angle has changed then check against our
481 // max allowable angle change.
482 double angleDelta = lastCharacterAngle - angle;
483 // normalise between -180 and 180
484 while ( angleDelta > M_PI )
485 angleDelta -= 2 * M_PI;
486 while ( angleDelta < -M_PI )
487 angleDelta += 2 * M_PI;
488 if ( ( maxConcaveAngle >= 0 && angleDelta > 0 && angleDelta > maxConcaveAngle ) || ( maxConvexAngle >= 0 && angleDelta < 0 && angleDelta < -maxConvexAngle ) )
489 {
490 output->graphemePlacement.clear();
491 return output;
492 }
493 }
494
496 {
497 // Shift the character downwards since the draw position is specified at the baseline
498 // and we're calculating the mean line here
499 double dist = 0.9 * maxCharacterHeight / 2 - ( maxCharacterDescent - characterDescent );
500 if ( output->flippedCharacterPlacementToGetUprightLabels )
501 {
502 dist = -dist;
503 }
504 characterStartX += dist * std::cos( angle + M_PI_2 );
505 characterStartY -= dist * std::sin( angle + M_PI_2 );
506 }
507
508 double renderAngle = angle;
509 CurvedGraphemePlacement placement;
510 placement.graphemeIndex = !output->flippedCharacterPlacementToGetUprightLabels ? i : characterCount - i - 1;
511 placement.x = characterStartX;
512 placement.y = characterStartY;
513 placement.width = characterWidth;
514 placement.height = characterHeight;
515 const QString grapheme = metrics.grapheme( placement.graphemeIndex );
516 placement.isWhitespace = grapheme.isEmpty() || grapheme.at( 0 ).isSpace() || grapheme.at( 0 ) == '\t';
517 if ( output->flippedCharacterPlacementToGetUprightLabels )
518 {
519 // rotate in place
520 placement.x += characterWidth * std::cos( renderAngle );
521 placement.y -= characterWidth * std::sin( renderAngle );
522 renderAngle += M_PI;
523 }
524 placement.angle = -renderAngle;
525 output->graphemePlacement.push_back( placement );
526
527 // Normalise to 0 <= angle < 2PI
528 while ( renderAngle >= 2 * M_PI )
529 renderAngle -= 2 * M_PI;
530 while ( renderAngle < 0 )
531 renderAngle += 2 * M_PI;
532
533 if ( renderAngle > M_PI_2 && renderAngle < 1.5 * M_PI )
534 output->upsideDownCharCount++;
535 }
536
537 if ( !isSecondAttempt && ( flags & Qgis::CurvedTextFlag::UprightCharactersOnly ) && output->upsideDownCharCount >= characterCount / 2.0 )
538 {
539 // more of text is upside down then right side up...
540 // if text should be shown upright then retry with the opposite orientation
541 return generateCurvedTextPlacementPrivate( metrics, x, y, numPoints, pathDistances, offsetAlongLine, direction, flags, maxConcaveAngle, maxConvexAngle, true, additionalCharacterSpacing, additionalWordSpacing, textAnchor );
542 }
543
544 return output;
545}
546
547bool QgsTextRendererUtils::nextCharPosition(
548 double charWidth,
549 const std::vector<double> &pathDistances,
550 const double *x,
551 const double *y,
552 int numPoints,
553 int &index,
554 double &currentDistanceAlongSegment,
555 double &characterStartX,
556 double &characterStartY,
557 double &characterEndX,
558 double &characterEndY,
560 double additionalSpacing
561)
562{
563 if ( !qgsDoubleNear( additionalSpacing, 0.0 ) )
564 {
565 currentDistanceAlongSegment += additionalSpacing;
566
567 // forward spacing
568 while ( index < numPoints && currentDistanceAlongSegment > pathDistances[index] )
569 {
570 currentDistanceAlongSegment -= pathDistances[index];
571 index++;
572 }
573 // backward spacing (compression)
574 while ( currentDistanceAlongSegment < 0 )
575 {
576 index--;
577 if ( index < 1 )
578 return false;
579 currentDistanceAlongSegment += pathDistances[index];
580 }
581 }
582
583 // intentional for readability:
584 // NOLINTBEGIN(bugprone-branch-clone)
585 if ( index >= numPoints )
586 {
587 // do not support extending the line start or end points via additional spacing
588 return false;
589 }
590 else if ( qgsDoubleNear( pathDistances[index], 0.0 ) )
591 {
592 // Not allowed to place across on 0 length segments or discontinuities
593 return false;
594 }
595 // NOLINTEND(bugprone-branch-clone)
596
597 double segmentStartX = x[index - 1];
598 double segmentStartY = y[index - 1];
599
600 double segmentEndX = x[index];
601 double segmentEndY = y[index];
602
603 double segmentLength = pathDistances[index];
604
605 const double segmentDx = segmentEndX - segmentStartX;
606 const double segmentDy = segmentEndY - segmentStartY;
607
608 characterStartX = segmentStartX + segmentDx * currentDistanceAlongSegment / segmentLength;
609 characterStartY = segmentStartY + segmentDy * currentDistanceAlongSegment / segmentLength;
610
611 // Coordinates this character ends at, calculated below
612 characterEndX = 0;
613 characterEndY = 0;
614
615 if ( segmentLength - currentDistanceAlongSegment >= charWidth )
616 {
617 // if the distance remaining in this segment is enough, we just go further along the segment
618 currentDistanceAlongSegment += charWidth;
619 characterEndX = segmentStartX + segmentDx * currentDistanceAlongSegment / segmentLength;
620 characterEndY = segmentStartY + segmentDy * currentDistanceAlongSegment / segmentLength;
621 }
622 else
623 {
624 // If there isn't enough distance left on this segment
625 // then we need to search until we find the line segment that ends further than ci.width away
626 do
627 {
628 index++;
629 if ( index >= numPoints ) // Bail out if we run off the end of the shape
630 {
632 {
633 // here we should extend out the final segment of the line to fit the character
634 const double lastSegmentDx = segmentEndX - segmentStartX;
635 const double lastSegmentDy = segmentEndY - segmentStartY;
636 const double lastSegmentLength = std::sqrt( lastSegmentDx * lastSegmentDx + lastSegmentDy * lastSegmentDy );
637 if ( qgsDoubleNear( lastSegmentLength, 0.0 ) )
638 {
639 // last segment has 0 length, can't extend
640 return false;
641 }
642
643 segmentEndX = segmentStartX + ( lastSegmentDx / lastSegmentLength ) * charWidth;
644 segmentEndY = segmentStartY + ( lastSegmentDy / lastSegmentLength ) * charWidth;
645 index--;
646 break;
647 }
648 else
649 {
650 return false;
651 }
652 }
653
654 segmentStartX = segmentEndX;
655 segmentStartY = segmentEndY;
656 segmentEndX = x[index];
657 segmentEndY = y[index];
658 } while ( std::sqrt( std::pow( characterStartX - segmentEndX, 2 ) + std::pow( characterStartY - segmentEndY, 2 ) ) < charWidth ); // Distance from character start to end
659
660 // Calculate the position to place the end of the character on
661 findLineCircleIntersection( characterStartX, characterStartY, charWidth, segmentStartX, segmentStartY, segmentEndX, segmentEndY, characterEndX, characterEndY );
662
663 // Need to calculate distance on the new segment
664 currentDistanceAlongSegment = std::sqrt( std::pow( segmentStartX - characterEndX, 2 ) + std::pow( segmentStartY - characterEndY, 2 ) );
665 }
666 return true;
667}
668
669void QgsTextRendererUtils::findLineCircleIntersection( double cx, double cy, double radius, double x1, double y1, double x2, double y2, double &xRes, double &yRes )
670{
671 double multiplier = 1;
672 if ( radius < 10 )
673 {
674 // these calculations get unstable for small coordinates differences, e.g. as a result of map labeling in a geographic
675 // CRS
676 multiplier = 10000;
677 x1 *= multiplier;
678 y1 *= multiplier;
679 x2 *= multiplier;
680 y2 *= multiplier;
681 cx *= multiplier;
682 cy *= multiplier;
683 radius *= multiplier;
684 }
685
686 const double dx = x2 - x1;
687 const double dy = y2 - y1;
688
689 const double A = dx * dx + dy * dy;
690 const double B = 2 * ( dx * ( x1 - cx ) + dy * ( y1 - cy ) );
691 const double C = QgsGeometryUtilsBase::sqrDistance2D( x1, y1, cx, cy ) - radius * radius;
692
693 const double det = B * B - 4 * A * C;
694 if ( A <= 0.000000000001 || det < 0 )
695 // Should never happen, No real solutions.
696 return;
697
698 if ( qgsDoubleNear( det, 0.0 ) )
699 {
700 // Could potentially happen.... One solution.
701 const double t = -B / ( 2 * A );
702 xRes = x1 + t * dx;
703 yRes = y1 + t * dy;
704 }
705 else
706 {
707 // Two solutions.
708 // Always use the 1st one
709 // We only really have one solution here, as we know the line segment will start in the circle and end outside
710 const double t = ( -B + std::sqrt( det ) ) / ( 2 * A );
711 xRes = x1 + t * dx;
712 yRes = y1 + t * dy;
713 }
714
715 if ( multiplier != 1 )
716 {
717 xRes /= multiplier;
718 yRes /= multiplier;
719 }
720}
TextAnchorPoint
Anchor point of label text.
Definition qgis.h:1495
@ EndOfText
Anchor using end of text.
Definition qgis.h:1498
@ StartOfText
Anchor using start of text.
Definition qgis.h:1496
@ CenterOfText
Anchor using center of text.
Definition qgis.h:1497
@ FollowPlacement
Automatically set the anchor point based on the line anchor point value. Values <25% of line length w...
Definition qgis.h:1499
TextOrientation
Text orientations.
Definition qgis.h:3123
@ Vertical
Vertically oriented text.
Definition qgis.h:3125
@ RotationBased
Horizontally or vertically oriented text based on rotation (only available for map labeling).
Definition qgis.h:3126
@ Horizontal
Horizontally oriented text.
Definition qgis.h:3124
RenderUnit
Rendering size units.
Definition qgis.h:5770
@ Percentage
Percentage of another measurement (e.g., canvas size, feature size).
Definition qgis.h:5774
@ Millimeters
Millimeters.
Definition qgis.h:5771
@ Points
Points (e.g., for font sizes).
Definition qgis.h:5775
@ MapUnits
Map units.
Definition qgis.h:5772
@ TruncateStringWhenLineIsTooShort
When a string is too long for the line, truncate characters instead of aborting the placement.
Definition qgis.h:3228
@ UprightCharactersOnly
Permit upright characters only. If not present then upside down text placement is permitted.
Definition qgis.h:3230
@ ExtendLineToFitText
When a string is too long for the line, extend the line's final segment to fit the entire string.
Definition qgis.h:3231
@ UseBaselinePlacement
Generate placement based on the character baselines instead of centers.
Definition qgis.h:3229
QFlags< CurvedTextFlag > CurvedTextFlags
Flags controlling behavior of curved text generation.
Definition qgis.h:3240
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 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 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(), Qgis::TextAnchorPoint textAnchor=Qgis::TextAnchorPoint::StartOfText)
Calculates curved text placement properties.
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:7795
constexpr QgsDataDefinedStringDecoder< V, N > makeDataDefinedStringDecoder(const DataDefinedDecoderEntry< V >(&values)[N])
Creates a QgsDataDefinedStringDecoder from a hardcoded lookup table.