QGIS API Documentation 4.3.0-Master (59e977eef4b)
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,
162 const QPolygonF &line,
163 double offsetAlongLine,
164 LabelLineDirection direction,
165 double maxConcaveAngle,
166 double maxConvexAngle,
168 Qgis::TextAnchorPoint textAnchor
169)
170{
171 const std::size_t numPoints = line.size();
172 std::vector<double> pathDistances( numPoints );
173
174 const QPointF *p = line.data();
175 double dx, dy;
176
177 pathDistances[0] = 0;
178 double prevX = p->x();
179 double prevY = p->y();
180 p++;
181
182 std::vector< double > x( numPoints );
183 std::vector< double > y( numPoints );
184 x[0] = prevX;
185 y[0] = prevY;
186
187 for ( std::size_t i = 1; i < numPoints; ++i )
188 {
189 dx = p->x() - prevX;
190 dy = p->y() - prevY;
191 pathDistances[i] = std::sqrt( dx * dx + dy * dy );
192
193 prevX = p->x();
194 prevY = p->y();
195 p++;
196 x[i] = prevX;
197 y[i] = prevY;
198 }
199
200 return generateCurvedTextPlacementPrivate( metrics, x.data(), y.data(), numPoints, pathDistances, offsetAlongLine, direction, flags, maxConcaveAngle, maxConvexAngle, false, 0, 0, textAnchor );
201}
202
203std::unique_ptr< QgsTextRendererUtils::CurvePlacementProperties > QgsTextRendererUtils::generateCurvedTextPlacement(
204 const QgsPrecalculatedTextMetrics &metrics,
205 const double *x,
206 const double *y,
207 int numPoints,
208 const std::vector<double> &pathDistances,
209 double offsetAlongLine,
210 LabelLineDirection direction,
211 double maxConcaveAngle,
212 double maxConvexAngle,
214 double additionalCharacterSpacing,
215 double additionalWordSpacing,
216 Qgis::TextAnchorPoint textAnchor
217)
218{
219 return generateCurvedTextPlacementPrivate( metrics, x, y, numPoints, pathDistances, offsetAlongLine, direction, flags, maxConcaveAngle, maxConvexAngle, false, additionalCharacterSpacing, additionalWordSpacing, textAnchor );
220}
221
222std::unique_ptr< QgsTextRendererUtils::CurvePlacementProperties > QgsTextRendererUtils::generateCurvedTextPlacementPrivate(
223 const QgsPrecalculatedTextMetrics &metrics,
224 const double *x,
225 const double *y,
226 int numPoints,
227 const std::vector<double> &pathDistances,
228 double offsetAlongLine,
229 LabelLineDirection direction,
231 double maxConcaveAngle,
232 double maxConvexAngle,
233 bool isSecondAttempt,
234 double additionalCharacterSpacing,
235 double additionalWordSpacing,
236 Qgis::TextAnchorPoint textAnchor
237)
238{
239 auto output = std::make_unique< CurvePlacementProperties >();
240 output->graphemePlacement.reserve( metrics.count() );
241
242 if ( !qgsDoubleNear( additionalCharacterSpacing, 0 ) || !qgsDoubleNear( additionalWordSpacing, 0 ) )
244
245 double totalLineLength = 0.0;
246 for ( double pathDistance : pathDistances )
247 {
248 totalLineLength += pathDistance;
249 }
250
251 // cleanup incompatible parameters
252 switch ( textAnchor )
253 {
255 break;
256
259 // TruncateStringWhenLineIsTooShort, offset along line not supported in these modes
261 offsetAlongLine = 0;
262 break;
263
265 // follow placement mode not supported here!
267 break;
268 }
269
270 double totalTextWidth = 0.0;
271 int characterCount = metrics.count();
272 for ( int i = 0; i < characterCount; ++i )
273 {
274 const double currentCharacterWidth = metrics.characterWidth( i );
275 double spacing = 0.0;
276 if ( i > 0 )
277 {
278 spacing = 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 spacing += additionalWordSpacing;
285 }
286 }
287 }
288 totalTextWidth += currentCharacterWidth + spacing;
289 }
290 double adjustedOffsetAlongLine = 0.0;
291 // expand calculated text width by a couple of pixels -- it's ok for us to run over the line
292 // by a couple of pixels, and we don't want to risk truncating text due to floating point
293 // calculation fuzziness
294 constexpr double TEXT_WIDTH_ADJUSTMENT_FACTOR = 1.5;
295 switch ( textAnchor )
296 {
298 adjustedOffsetAlongLine = offsetAlongLine;
299 break;
300
302 adjustedOffsetAlongLine = ( totalLineLength - ( totalTextWidth + TEXT_WIDTH_ADJUSTMENT_FACTOR ) ) / 2.0;
303 break;
304
306 adjustedOffsetAlongLine = totalLineLength - ( totalTextWidth + TEXT_WIDTH_ADJUSTMENT_FACTOR );
307 break;
308
310 break;
311 }
312
313 std::vector< double > modifiedX( x, x + numPoints );
314 std::vector< double > modifiedY( y, y + numPoints );
315 std::vector< double > modifiedPathDistances = pathDistances;
316
317 if ( adjustedOffsetAlongLine < 0.0 && ( flags & Qgis::CurvedTextFlag::ExtendLineToFitText ) )
318 {
319 // extend first segment of line to adjust for negative start offsets
320 const double extension = std::abs( adjustedOffsetAlongLine );
321 const double firstSegmentLength = modifiedPathDistances[1];
322 if ( firstSegmentLength > 0.0 )
323 {
324 const double dx = modifiedX[1] - modifiedX[0];
325 const double dy = modifiedY[1] - modifiedY[0];
326 modifiedX[0] -= ( dx / firstSegmentLength ) * extension;
327 modifiedY[0] -= ( dy / firstSegmentLength ) * extension;
328 modifiedPathDistances[1] += extension;
329 adjustedOffsetAlongLine = 0.0;
330 }
331 }
332
333 double offsetAlongSegment = adjustedOffsetAlongLine;
334 int index = 1;
335 // Find index of segment corresponding to starting offset
336 while ( index < numPoints && offsetAlongSegment > modifiedPathDistances[index] )
337 {
338 offsetAlongSegment -= modifiedPathDistances[index];
339 index += 1;
340 }
341 if ( index >= numPoints )
342 {
343 return output;
344 }
345
346 const double segmentLength = modifiedPathDistances[index];
347 if ( qgsDoubleNear( segmentLength, 0.0 ) )
348 {
349 // Not allowed to place across on 0 length segments or discontinuities
350 return output;
351 }
352
353 if ( direction == RespectPainterOrientation && !isSecondAttempt )
354 {
355 // Calculate the orientation based on the angle of the path segment under consideration
356
357 double distance = offsetAlongSegment;
358 int endindex = index;
359
360 double startLabelX = 0;
361 double startLabelY = 0;
362 double endLabelX = 0;
363 double endLabelY = 0;
364 for ( int i = 0; i < characterCount; i++ )
365 {
366 const double characterWidth = metrics.characterWidth( i );
367 double characterStartX, characterStartY;
368
369 // calculate additional spacing for this character
370 double currentSpacing = 0.0;
371 if ( i > 0 )
372 {
373 currentSpacing = additionalCharacterSpacing;
374 if ( !qgsDoubleNear( additionalWordSpacing, 0.0 ) )
375 {
376 const QString g = metrics.grapheme( i - 1 );
377 if ( !g.isEmpty() && g.at( 0 ).isSpace() )
378 {
379 currentSpacing += additionalWordSpacing;
380 }
381 }
382 }
383
384 if ( !nextCharPosition( characterWidth, modifiedPathDistances, modifiedX.data(), modifiedY.data(), numPoints, endindex, distance, characterStartX, characterStartY, endLabelX, endLabelY, flags, currentSpacing ) )
385 {
387 {
388 characterCount = i + 1;
389 break;
390 }
391 else
392 {
393 return output;
394 }
395 }
396 if ( i == 0 )
397 {
398 startLabelX = characterStartX;
399 startLabelY = characterStartY;
400 }
401 }
402
403 // Determine the angle of the path segment under consideration
404 const double dx = endLabelX - startLabelX;
405 const double dy = endLabelY - startLabelY;
406 const double lineAngle = std::atan2( -dy, dx ) * 180 / M_PI;
407
408 if ( lineAngle > 90 || lineAngle < -90 )
409 {
410 output->labeledLineSegmentIsRightToLeft = true;
411 }
412 }
413
414 if ( isSecondAttempt )
415 {
416 // we know that treating the segment as running from right to left gave too many upside down characters, so try again treating the
417 // segment as left to right
418 output->labeledLineSegmentIsRightToLeft = false;
419 output->flippedCharacterPlacementToGetUprightLabels = true;
420 }
421
422 const double dx = modifiedX[index] - modifiedX[index - 1];
423 const double dy = modifiedY[index] - modifiedY[index - 1];
424
425 double angle = std::atan2( -dy, dx );
426
427 const double maxCharacterDescent = metrics.maximumCharacterDescent();
428 const double maxCharacterHeight = metrics.maximumCharacterHeight();
429
430 for ( int i = 0; i < characterCount; i++ )
431 {
432 const double lastCharacterAngle = angle;
433
434 // next character index, depending on the orientation
435 const int k = !output->flippedCharacterPlacementToGetUprightLabels ? i : characterCount - i - 1;
436
437 // grab the next character according to the orientation
438 const double characterWidth = metrics.characterWidth( k );
439 if ( qgsDoubleNear( characterWidth, 0.0 ) )
440 // Certain scripts rely on zero-width character, skip those to prevent failure (see #15801)
441 continue;
442
443 const double characterHeight = metrics.characterHeight( k );
444 const double characterDescent = metrics.characterDescent( k );
445
446 double characterStartX = 0;
447 double characterStartY = 0;
448 double characterEndX = 0;
449 double characterEndY = 0;
450
451 // Calculate Spacing
452 double currentSpacing = 0.0;
453 if ( i > 0 )
454 {
455 currentSpacing = additionalCharacterSpacing;
456 if ( !qgsDoubleNear( additionalWordSpacing, 0.0 ) )
457 {
458 int prevCharIndex = !output->flippedCharacterPlacementToGetUprightLabels ? k - 1 : k + 1;
459 if ( prevCharIndex >= 0 && prevCharIndex < metrics.count() )
460 {
461 const QString g = metrics.grapheme( prevCharIndex );
462 if ( !g.isEmpty() && g.at( 0 ).isSpace() )
463 currentSpacing += additionalWordSpacing;
464 }
465 }
466 }
467
468 if ( !nextCharPosition( characterWidth, modifiedPathDistances, modifiedX.data(), modifiedY.data(), numPoints, index, offsetAlongSegment, characterStartX, characterStartY, characterEndX, characterEndY, flags, currentSpacing ) )
469 {
471 {
472 characterCount = i + 1;
473 break;
474 }
475 else
476 {
477 output->graphemePlacement.clear();
478 return output;
479 }
480 }
481
482 // Calculate angle from the start of the character to the end based on start/end of character
483 angle = std::atan2( characterStartY - characterEndY, characterEndX - characterStartX );
484
485 if ( maxConcaveAngle >= 0 || maxConvexAngle >= 0 )
486 {
487 // Test lastCharacterAngle vs angle
488 // since our rendering angle has changed then check against our
489 // max allowable angle change.
490 double angleDelta = lastCharacterAngle - angle;
491 // normalise between -180 and 180
492 while ( angleDelta > M_PI )
493 angleDelta -= 2 * M_PI;
494 while ( angleDelta < -M_PI )
495 angleDelta += 2 * M_PI;
496 if ( ( maxConcaveAngle >= 0 && angleDelta > 0 && angleDelta > maxConcaveAngle ) || ( maxConvexAngle >= 0 && angleDelta < 0 && angleDelta < -maxConvexAngle ) )
497 {
498 output->graphemePlacement.clear();
499 return output;
500 }
501 }
502
504 {
505 // Shift the character downwards since the draw position is specified at the baseline
506 // and we're calculating the mean line here
507 double dist = 0.9 * maxCharacterHeight / 2 - ( maxCharacterDescent - characterDescent );
508 if ( output->flippedCharacterPlacementToGetUprightLabels )
509 {
510 dist = -dist;
511 }
512 characterStartX += dist * std::cos( angle + M_PI_2 );
513 characterStartY -= dist * std::sin( angle + M_PI_2 );
514 }
515
516 double renderAngle = angle;
517 CurvedGraphemePlacement placement;
518 placement.graphemeIndex = !output->flippedCharacterPlacementToGetUprightLabels ? i : characterCount - i - 1;
519 placement.x = characterStartX;
520 placement.y = characterStartY;
521 placement.width = characterWidth;
522 placement.height = characterHeight;
523 const QString grapheme = metrics.grapheme( placement.graphemeIndex );
524 placement.isWhitespace = grapheme.isEmpty() || grapheme.at( 0 ).isSpace() || grapheme.at( 0 ) == '\t';
525 if ( output->flippedCharacterPlacementToGetUprightLabels )
526 {
527 // rotate in place
528 placement.x += characterWidth * std::cos( renderAngle );
529 placement.y -= characterWidth * std::sin( renderAngle );
530 renderAngle += M_PI;
531 }
532 placement.angle = -renderAngle;
533 output->graphemePlacement.push_back( placement );
534
535 // Normalise to 0 <= angle < 2PI
536 while ( renderAngle >= 2 * M_PI )
537 renderAngle -= 2 * M_PI;
538 while ( renderAngle < 0 )
539 renderAngle += 2 * M_PI;
540
541 if ( renderAngle > M_PI_2 && renderAngle < 1.5 * M_PI )
542 output->upsideDownCharCount++;
543 }
544
545 if ( !isSecondAttempt && ( flags & Qgis::CurvedTextFlag::UprightCharactersOnly ) && output->upsideDownCharCount >= characterCount / 2.0 )
546 {
547 // more of text is upside down then right side up...
548 // if text should be shown upright then retry with the opposite orientation
549 return generateCurvedTextPlacementPrivate( metrics, x, y, numPoints, pathDistances, offsetAlongLine, direction, flags, maxConcaveAngle, maxConvexAngle, true, additionalCharacterSpacing, additionalWordSpacing, textAnchor );
550 }
551
552 return output;
553}
554
555bool QgsTextRendererUtils::nextCharPosition(
556 double charWidth,
557 const std::vector<double> &pathDistances,
558 const double *x,
559 const double *y,
560 int numPoints,
561 int &index,
562 double &currentDistanceAlongSegment,
563 double &characterStartX,
564 double &characterStartY,
565 double &characterEndX,
566 double &characterEndY,
568 double additionalSpacing
569)
570{
571 if ( !qgsDoubleNear( additionalSpacing, 0.0 ) )
572 {
573 currentDistanceAlongSegment += additionalSpacing;
574
575 // forward spacing
576 while ( index < numPoints && currentDistanceAlongSegment > pathDistances[index] )
577 {
578 currentDistanceAlongSegment -= pathDistances[index];
579 index++;
580 }
581 // backward spacing (compression)
582 while ( currentDistanceAlongSegment < 0 )
583 {
584 index--;
585 if ( index < 1 )
586 return false;
587 currentDistanceAlongSegment += pathDistances[index];
588 }
589 }
590
591 // intentional for readability:
592 // NOLINTBEGIN(bugprone-branch-clone)
593 if ( index >= numPoints )
594 {
595 // do not support extending the line start or end points via additional spacing
596 return false;
597 }
598 else if ( qgsDoubleNear( pathDistances[index], 0.0 ) )
599 {
600 // Not allowed to place across on 0 length segments or discontinuities
601 return false;
602 }
603 // NOLINTEND(bugprone-branch-clone)
604
605 double segmentStartX = x[index - 1];
606 double segmentStartY = y[index - 1];
607
608 double segmentEndX = x[index];
609 double segmentEndY = y[index];
610
611 double segmentLength = pathDistances[index];
612
613 const double segmentDx = segmentEndX - segmentStartX;
614 const double segmentDy = segmentEndY - segmentStartY;
615
616 characterStartX = segmentStartX + segmentDx * currentDistanceAlongSegment / segmentLength;
617 characterStartY = segmentStartY + segmentDy * currentDistanceAlongSegment / segmentLength;
618
619 // Coordinates this character ends at, calculated below
620 characterEndX = 0;
621 characterEndY = 0;
622
623 if ( segmentLength - currentDistanceAlongSegment >= charWidth )
624 {
625 // if the distance remaining in this segment is enough, we just go further along the segment
626 currentDistanceAlongSegment += charWidth;
627 characterEndX = segmentStartX + segmentDx * currentDistanceAlongSegment / segmentLength;
628 characterEndY = segmentStartY + segmentDy * currentDistanceAlongSegment / segmentLength;
629 }
630 else
631 {
632 // If there isn't enough distance left on this segment
633 // then we need to search until we find the line segment that ends further than ci.width away
634 do
635 {
636 index++;
637 if ( index >= numPoints ) // Bail out if we run off the end of the shape
638 {
640 {
641 // here we should extend out the final segment of the line to fit the character
642 const double lastSegmentDx = segmentEndX - segmentStartX;
643 const double lastSegmentDy = segmentEndY - segmentStartY;
644 const double lastSegmentLength = std::sqrt( lastSegmentDx * lastSegmentDx + lastSegmentDy * lastSegmentDy );
645 if ( qgsDoubleNear( lastSegmentLength, 0.0 ) )
646 {
647 // last segment has 0 length, can't extend
648 return false;
649 }
650
651 segmentEndX = segmentStartX + ( lastSegmentDx / lastSegmentLength ) * charWidth;
652 segmentEndY = segmentStartY + ( lastSegmentDy / lastSegmentLength ) * charWidth;
653 index--;
654 break;
655 }
656 else
657 {
658 return false;
659 }
660 }
661
662 segmentStartX = segmentEndX;
663 segmentStartY = segmentEndY;
664 segmentEndX = x[index];
665 segmentEndY = y[index];
666 } while ( std::sqrt( std::pow( characterStartX - segmentEndX, 2 ) + std::pow( characterStartY - segmentEndY, 2 ) ) < charWidth ); // Distance from character start to end
667
668 // Calculate the position to place the end of the character on
669 findLineCircleIntersection( characterStartX, characterStartY, charWidth, segmentStartX, segmentStartY, segmentEndX, segmentEndY, characterEndX, characterEndY );
670
671 // Need to calculate distance on the new segment
672 currentDistanceAlongSegment = std::sqrt( std::pow( segmentStartX - characterEndX, 2 ) + std::pow( segmentStartY - characterEndY, 2 ) );
673 }
674 return true;
675}
676
677void QgsTextRendererUtils::findLineCircleIntersection( double cx, double cy, double radius, double x1, double y1, double x2, double y2, double &xRes, double &yRes )
678{
679 double multiplier = 1;
680 if ( radius < 10 )
681 {
682 // these calculations get unstable for small coordinates differences, e.g. as a result of map labeling in a geographic
683 // CRS
684 multiplier = 10000;
685 x1 *= multiplier;
686 y1 *= multiplier;
687 x2 *= multiplier;
688 y2 *= multiplier;
689 cx *= multiplier;
690 cy *= multiplier;
691 radius *= multiplier;
692 }
693
694 const double dx = x2 - x1;
695 const double dy = y2 - y1;
696
697 const double A = dx * dx + dy * dy;
698 const double B = 2 * ( dx * ( x1 - cx ) + dy * ( y1 - cy ) );
699 const double C = QgsGeometryUtilsBase::sqrDistance2D( x1, y1, cx, cy ) - radius * radius;
700
701 const double det = B * B - 4 * A * C;
702 if ( A <= 0.000000000001 || det < 0 )
703 // Should never happen, No real solutions.
704 return;
705
706 if ( qgsDoubleNear( det, 0.0 ) )
707 {
708 // Could potentially happen.... One solution.
709 const double t = -B / ( 2 * A );
710 xRes = x1 + t * dx;
711 yRes = y1 + t * dy;
712 }
713 else
714 {
715 // Two solutions.
716 // Always use the 1st one
717 // We only really have one solution here, as we know the line segment will start in the circle and end outside
718 const double t = ( -B + std::sqrt( det ) ) / ( 2 * A );
719 xRes = x1 + t * dx;
720 yRes = y1 + t * dy;
721 }
722
723 if ( multiplier != 1 )
724 {
725 xRes /= multiplier;
726 yRes /= multiplier;
727 }
728}
TextAnchorPoint
Anchor point of label text.
Definition qgis.h:1475
@ EndOfText
Anchor using end of text.
Definition qgis.h:1478
@ StartOfText
Anchor using start of text.
Definition qgis.h:1476
@ CenterOfText
Anchor using center of text.
Definition qgis.h:1477
@ FollowPlacement
Automatically set the anchor point based on the line anchor point value. Values <25% of line length w...
Definition qgis.h:1479
TextOrientation
Text orientations.
Definition qgis.h:3084
@ Vertical
Vertically oriented text.
Definition qgis.h:3086
@ RotationBased
Horizontally or vertically oriented text based on rotation (only available for map labeling).
Definition qgis.h:3087
@ Horizontal
Horizontally oriented text.
Definition qgis.h:3085
RenderUnit
Rendering size units.
Definition qgis.h:5650
@ Percentage
Percentage of another measurement (e.g., canvas size, feature size).
Definition qgis.h:5654
@ Millimeters
Millimeters.
Definition qgis.h:5651
@ Points
Points (e.g., for font sizes).
Definition qgis.h:5655
@ MapUnits
Map units.
Definition qgis.h:5652
@ TruncateStringWhenLineIsTooShort
When a string is too long for the line, truncate characters instead of aborting the placement.
Definition qgis.h:3189
@ UprightCharactersOnly
Permit upright characters only. If not present then upside down text placement is permitted.
Definition qgis.h:3191
@ ExtendLineToFitText
When a string is too long for the line, extend the line's final segment to fit the entire string.
Definition qgis.h:3192
@ UseBaselinePlacement
Generate placement based on the character baselines instead of centers.
Definition qgis.h:3190
QFlags< CurvedTextFlag > CurvedTextFlags
Flags controlling behavior of curved text generation.
Definition qgis.h:3201
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:7417