QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
qgstextlabelfeature.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgstextlabelfeature.cpp
3  ---------------------
4  begin : December 2015
5  copyright : (C) 2015 by Martin Dobias
6  email : wonder dot sk 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 
16 #include "qgstextlabelfeature.h"
17 
18 #include "qgsgeometry.h"
19 #include "qgspallabeling.h"
20 #include "qgsmaptopixel.h"
21 #include "pal/feature.h"
22 #include "qgstextcharacterformat.h"
23 #include "qgstextfragment.h"
24 #include "qgstextblock.h"
25 
27  : QgsLabelFeature( id, std::move( geometry ), size )
28 {
29  mDefinedFont = QFont();
30 }
31 
32 
34 {
35  delete mFontMetrics;
36 }
37 
38 
39 QString QgsTextLabelFeature::text( int partId ) const
40 {
41  if ( partId == -1 )
42  return mLabelText;
43  else
44  return mClusters.at( partId );
45 }
46 
48 {
49  return mCharacterFormats.value( partId );
50 }
51 
53 {
54  return partId < mCharacterFormats.size();
55 }
56 
57 void QgsTextLabelFeature::calculateInfo( bool curvedLabeling, QFontMetricsF *fm, const QgsMapToPixel *xform, double maxinangle, double maxoutangle, QgsTextDocument *document )
58 {
59  if ( mInfo )
60  return;
61 
62  mFontMetrics = new QFontMetricsF( *fm ); // duplicate metrics for when drawing label
63 
64  qreal letterSpacing = mDefinedFont.letterSpacing();
65  qreal wordSpacing = mDefinedFont.wordSpacing();
66 
67  // max angle between curved label characters (20.0/-20.0 was default in QGIS <= 1.8)
68  if ( maxinangle < 20.0 )
69  maxinangle = 20.0;
70  if ( 60.0 < maxinangle )
71  maxinangle = 60.0;
72  if ( maxoutangle > -20.0 )
73  maxoutangle = -20.0;
74  if ( -95.0 > maxoutangle )
75  maxoutangle = -95.0;
76 
77  // create label info!
78  double mapScale = xform->mapUnitsPerPixel();
79  double labelHeight = mapScale * fm->height();
80 
81  // mLetterSpacing/mWordSpacing = 0.0 is default for non-curved labels
82  // (non-curved spacings handled by Qt in QgsPalLayerSettings/QgsPalLabeling)
83  qreal charWidth;
84  qreal wordSpaceFix;
85 
86  if ( document && curvedLabeling )
87  {
88  for ( const QgsTextBlock &block : qgis::as_const( *document ) )
89  {
90  for ( const QgsTextFragment &fragment : block )
91  {
92  const QStringList graphemes = QgsPalLabeling::splitToGraphemes( fragment.text() );
93  for ( const QString &grapheme : graphemes )
94  {
95  mClusters.append( grapheme );
96  mCharacterFormats.append( fragment.characterFormat() );
97  }
98  }
99  }
100  }
101  else
102  {
103  //split string by valid grapheme boundaries - required for certain scripts (see #6883)
105  }
106 
107  mInfo = new pal::LabelInfo( mClusters.count(), labelHeight, maxinangle, maxoutangle );
108  for ( int i = 0; i < mClusters.count(); i++ )
109  {
110  // reconstruct how Qt creates word spacing, then adjust per individual stored character
111  // this will allow PAL to create each candidate width = character width + correct spacing
112 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
113  charWidth = fm->width( mClusters[i] );
114 #else
115  charWidth = fm->horizontalAdvance( mClusters[i] );
116 #endif
117  if ( curvedLabeling )
118  {
119  wordSpaceFix = qreal( 0.0 );
120  if ( mClusters[i] == QLatin1String( " " ) )
121  {
122  // word spacing only gets added once at end of consecutive run of spaces, see QTextEngine::shapeText()
123  int nxt = i + 1;
124  wordSpaceFix = ( nxt < mClusters.count() && mClusters[nxt] != QLatin1String( " " ) ) ? wordSpacing : qreal( 0.0 );
125  }
126  // this workaround only works for clusters with a single character. Not sure how it should be handled
127  // with multi-character clusters.
128 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
129  if ( mClusters[i].length() == 1 &&
130  !qgsDoubleNear( fm->width( QString( mClusters[i].at( 0 ) ) ), fm->width( mClusters[i].at( 0 ) ) + letterSpacing ) )
131 #else
132  if ( mClusters[i].length() == 1 &&
133  !qgsDoubleNear( fm->horizontalAdvance( QString( mClusters[i].at( 0 ) ) ), fm->horizontalAdvance( mClusters[i].at( 0 ) ) + letterSpacing ) )
134 #endif
135  {
136  // word spacing applied when it shouldn't be
137  wordSpaceFix -= wordSpacing;
138  }
139 
140 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
141  charWidth = fm->width( QString( mClusters[i] ) ) + wordSpaceFix;
142 #else
143  charWidth = fm->horizontalAdvance( QString( mClusters[i] ) ) + wordSpaceFix;
144 #endif
145  }
146 
147  double labelWidth = mapScale * charWidth;
148  mInfo->char_info[i].width = labelWidth;
149  }
150 }
151 
153 {
154  return mDocument;
155 }
156 
158 {
160 }
QgsTextLabelFeature::setDocument
void setDocument(const QgsTextDocument &document)
Sets the document for the label.
Definition: qgstextlabelfeature.cpp:157
qgspallabeling.h
QgsTextLabelFeature::text
QString text(int partId) const
Returns the text component corresponding to a specified label part.
Definition: qgspalgeometry.h:53
pal::LabelInfo
Optional additional info about label (for curved labels)
Definition: feature.h:55
QgsMapToPixel::mapUnitsPerPixel
double mapUnitsPerPixel() const
Returns current map units per pixel.
Definition: qgsmaptopixel.cpp:128
QgsTextLabelFeature::hasCharacterFormat
bool hasCharacterFormat(int partId) const
Returns true if the feature contains specific character formatting for the part with matching ID.
Definition: qgstextlabelfeature.cpp:52
qgstextfragment.h
QgsTextLabelFeature::~QgsTextLabelFeature
~QgsTextLabelFeature()
Clean up.
Definition: qgspalgeometry.h:42
qgsmaptopixel.h
QgsTextLabelFeature::mFontMetrics
QFontMetricsF * mFontMetrics
Metrics of the font for rendering.
Definition: qgspalgeometry.h:145
QgsTextLabelFeature::characterFormat
QgsTextCharacterFormat characterFormat(int partId) const
Returns the character format corresponding to the specified label part.
Definition: qgstextlabelfeature.cpp:47
QgsTextLabelFeature::mDefinedFont
QFont mDefinedFont
Font for rendering.
Definition: qgspalgeometry.h:143
QgsTextCharacterFormat
Stores information relating to individual character formatting.
Definition: qgstextcharacterformat.h:40
QgsTextLabelFeature::mClusters
QStringList mClusters
List of graphemes (used for curved labels)
Definition: qgspalgeometry.h:141
geos::unique_ptr
std::unique_ptr< GEOSGeometry, GeosDeleter > unique_ptr
Scoped GEOS pointer.
Definition: qgsgeos.h:79
QgsTextLabelFeature::mCharacterFormats
QList< QgsTextCharacterFormat > mCharacterFormats
Definition: qgstextlabelfeature.h:99
feature.h
QgsTextFragment
Stores a fragment of text along with formatting overrides to be used when rendering the fragment.
Definition: qgstextfragment.h:36
qgsDoubleNear
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:315
QgsLabelFeature::mLabelText
QString mLabelText
text of the label
Definition: qgslabelfeature.h:564
QgsTextLabelFeature::mDocument
QgsTextDocument mDocument
Definition: qgstextlabelfeature.h:108
qgstextcharacterformat.h
QgsTextLabelFeature::calculateInfo
void calculateInfo(bool curvedLabeling, QFontMetricsF *fm, const QgsMapToPixel *xform, double fontScale, double maxinangle, double maxoutangle)
calculate data for info(). setDefinedFont() must have been called already.
Definition: qgspalgeometry.h:62
QgsTextBlock
Represents a block of text consisting of one or more QgsTextFragment objects.
Definition: qgstextblock.h:36
pal::LabelInfo::char_info
CharacterInfo * char_info
Definition: feature.h:82
qgsgeometry.h
QgsMapToPixel
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:38
QgsTextDocument
Represents a document consisting of one or more QgsTextBlock objects.
Definition: qgstextdocument.h:39
QgsTextLabelFeature::document
QgsTextDocument document() const
Returns the document for the label.
Definition: qgstextlabelfeature.cpp:152
QgsLabelFeature
The QgsLabelFeature class describes a feature that should be used within the labeling engine.
Definition: qgslabelfeature.h:57
QgsLabelFeature::mInfo
pal::LabelInfo * mInfo
extra information for curved labels (may be nullptr)
Definition: qgslabelfeature.h:566
qgstextlabelfeature.h
qgstextblock.h
QgsFeatureId
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
Definition: qgsfeatureid.h:28
QgsTextLabelFeature::QgsTextLabelFeature
QgsTextLabelFeature(QgsFeatureId id, GEOSGeometry *geometry, const QSizeF &size)
Construct text label feature.
Definition: qgspalgeometry.h:34
pal::LabelInfo::CharacterInfo::width
double width
Definition: feature.h:59
QgsPalLabeling::splitToGraphemes
static QStringList splitToGraphemes(const QString &text)
Splits a text string to a list of graphemes, which are the smallest allowable character divisions in ...
Definition: qgspallabeling.cpp:3653