QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
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 "qgspallabeling.h"
19#include "qgsmaptopixel.h"
21#include "qgstextfragment.h"
22#include "qgstextblock.h"
23#include "qgstextrenderer.h"
24
25
27 : QgsLabelFeature( id, std::move( geometry ), size )
28{
29 mDefinedFont = QFont();
30}
31
33
34QString QgsTextLabelFeature::text( int partId ) const
35{
36 if ( partId == -1 )
37 return mLabelText;
38 else
39 return mTextMetrics->grapheme( partId );
40}
41
43{
44 return mTextMetrics.has_value() ? mTextMetrics->graphemeFormat( partId ) : QgsTextCharacterFormat();
45}
46
48{
49 return mTextMetrics.has_value() && partId < mTextMetrics->graphemeFormatCount();
50}
51
52QgsPrecalculatedTextMetrics QgsTextLabelFeature::calculateTextMetrics( const QgsMapToPixel *xform, const QgsRenderContext &context, const QFont &baseFont, const QFontMetricsF &fontMetrics, double letterSpacing, double wordSpacing, const QString &text, QgsTextDocument *document, QgsTextDocumentMetrics * )
53{
54 // create label info!
55 const double mapScale = xform->mapUnitsPerPixel();
56 QStringList graphemes;
57 QVector< QgsTextCharacterFormat > graphemeFormats;
58
59 if ( document )
60 {
61 for ( const QgsTextBlock &block : std::as_const( *document ) )
62 {
63 for ( const QgsTextFragment &fragment : block )
64 {
65 const QStringList fragmentGraphemes = QgsPalLabeling::splitToGraphemes( fragment.text() );
66 for ( const QString &grapheme : fragmentGraphemes )
67 {
68 graphemes.append( grapheme );
69 graphemeFormats.append( fragment.characterFormat() );
70 }
71 }
72 }
73 }
74 else
75 {
76 //split string by valid grapheme boundaries - required for certain scripts (see #6883)
78 }
79
80 QVector< double > characterWidths( graphemes.count() );
81 QVector< double > characterHeights( graphemes.count() );
82 QVector< double > characterDescents( graphemes.count() );
83
84 QFont previousNonSuperSubScriptFont;
85
86 for ( int i = 0; i < graphemes.count(); i++ )
87 {
88 // reconstruct how Qt creates word spacing, then adjust per individual stored character
89 // this will allow PAL to create each candidate width = character width + correct spacing
90
91 double graphemeFirstCharHorizontalAdvanceWithLetterSpacing = 0;
92 double graphemeFirstCharHorizontalAdvance = 0;
93 double graphemeHorizontalAdvance = 0;
94 double characterDescent = 0;
95 double characterHeight = 0;
96 if ( const QgsTextCharacterFormat *graphemeFormat = !graphemeFormats.empty() ? &graphemeFormats[i] : nullptr )
97 {
98 QFont graphemeFont = baseFont;
99 graphemeFormat->updateFontForFormat( graphemeFont, context, 1 );
100
101 if ( i == 0 )
102 previousNonSuperSubScriptFont = graphemeFont;
103
104 if ( graphemeFormat->hasVerticalAlignmentSet() )
105 {
106 switch ( graphemeFormat->verticalAlignment() )
107 {
109 previousNonSuperSubScriptFont = graphemeFont;
110 break;
111
114 {
115 if ( graphemeFormat->fontPointSize() < 0 )
116 {
117 // if fragment has no explicit font size set, then we scale the inherited font size to 60% of base font size
118 // this allows for easier use of super/subscript in labels as "my text<sup>2</sup>" will automatically render
119 // the superscript in a smaller font size. BUT if the fragment format HAS a non -1 font size then it indicates
120 // that the document has an explicit font size for the super/subscript element, eg "my text<sup style="font-size: 6pt">2</sup>"
121 // which we should respect
122 graphemeFont.setPixelSize( static_cast< int >( std::round( graphemeFont.pixelSize() * QgsTextRenderer::SUPERSCRIPT_SUBSCRIPT_FONT_SIZE_SCALING_FACTOR ) ) );
123 }
124 break;
125 }
126 }
127 }
128 else
129 {
130 previousNonSuperSubScriptFont = graphemeFont;
131 }
132
133 const QFontMetricsF graphemeFontMetrics( graphemeFont );
134 graphemeFirstCharHorizontalAdvance = graphemeFontMetrics.horizontalAdvance( QString( graphemes[i].at( 0 ) ) );
135 graphemeFirstCharHorizontalAdvanceWithLetterSpacing = graphemeFontMetrics.horizontalAdvance( graphemes[i].at( 0 ) ) + letterSpacing;
136 graphemeHorizontalAdvance = graphemeFontMetrics.horizontalAdvance( QString( graphemes[i] ) );
137 characterDescent = graphemeFontMetrics.descent();
138 characterHeight = graphemeFontMetrics.height();
139 }
140 else
141 {
142 graphemeFirstCharHorizontalAdvance = fontMetrics.horizontalAdvance( QString( graphemes[i].at( 0 ) ) );
143 graphemeFirstCharHorizontalAdvanceWithLetterSpacing = fontMetrics.horizontalAdvance( graphemes[i].at( 0 ) ) + letterSpacing;
144 graphemeHorizontalAdvance = fontMetrics.horizontalAdvance( QString( graphemes[i] ) );
145 characterDescent = fontMetrics.descent();
146 characterHeight = fontMetrics.height();
147 }
148
149 qreal wordSpaceFix = qreal( 0.0 );
150 if ( graphemes[i] == QLatin1String( " " ) )
151 {
152 // word spacing only gets added once at end of consecutive run of spaces, see QTextEngine::shapeText()
153 int nxt = i + 1;
154 wordSpaceFix = ( nxt < graphemes.count() && graphemes[nxt] != QLatin1String( " " ) ) ? wordSpacing : qreal( 0.0 );
155 }
156
157 // this workaround only works for clusters with a single character. Not sure how it should be handled
158 // with multi-character clusters.
159 if ( graphemes[i].length() == 1 &&
160 !qgsDoubleNear( graphemeFirstCharHorizontalAdvance, graphemeFirstCharHorizontalAdvanceWithLetterSpacing ) )
161 {
162 // word spacing applied when it shouldn't be
163 wordSpaceFix -= wordSpacing;
164 }
165
166 const double charWidth = graphemeHorizontalAdvance + wordSpaceFix;
167 characterWidths[i] = mapScale * charWidth;
168 characterHeights[i] = mapScale * characterHeight;
169 characterDescents[i] = mapScale * characterDescent;
170 }
171
172 QgsPrecalculatedTextMetrics res( graphemes, std::move( characterWidths ), std::move( characterHeights ), std::move( characterDescents ) );
173 res.setGraphemeFormats( graphemeFormats );
174 return res;
175}
176
178{
179 return mDocument;
180}
181
183{
184 return mDocumentMetrics;
185}
186
188{
190 mDocumentMetrics = metrics;
191}
@ Normal
Adjacent characters are positioned in the standard way for text in the writing system in use.
@ SubScript
Characters are placed below the base line for normal text.
@ SuperScript
Characters are placed above the base line for normal text.
The QgsLabelFeature class describes a feature that should be used within the labeling engine.
QString mLabelText
text of the label
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:39
double mapUnitsPerPixel() const
Returns the current map units per pixel.
static QStringList splitToGraphemes(const QString &text)
Splits a text string to a list of graphemes, which are the smallest allowable character divisions in ...
Contains precalculated properties regarding text metrics for text to be renderered at a later stage.
void setGraphemeFormats(const QVector< QgsTextCharacterFormat > &formats)
Sets the character formats associated with the text graphemes().
Contains information about the context of a rendering operation.
Represents a block of text consisting of one or more QgsTextFragment objects.
Definition: qgstextblock.h:36
Stores information relating to individual character formatting.
Contains pre-calculated metrics of a QgsTextDocument.
Represents a document consisting of one or more QgsTextBlock objects.
Stores a fragment of text along with formatting overrides to be used when rendering the fragment.
QgsTextLabelFeature(QgsFeatureId id, geos::unique_ptr geometry, QSizeF size)
Construct text label feature.
QgsTextDocument document() const
Returns the document for the label.
static QgsPrecalculatedTextMetrics calculateTextMetrics(const QgsMapToPixel *xform, const QgsRenderContext &context, const QFont &baseFont, const QFontMetricsF &fontMetrics, double letterSpacing, double wordSpacing, const QString &text=QString(), QgsTextDocument *document=nullptr, QgsTextDocumentMetrics *metrics=nullptr)
Calculate text metrics for later retrieval via textMetrics().
QgsTextDocument mDocument
~QgsTextLabelFeature() override
Clean up.
QgsTextCharacterFormat characterFormat(int partId) const
Returns the character format corresponding to the specified label part.
QFont mDefinedFont
Font for rendering.
QgsTextDocumentMetrics mDocumentMetrics
QgsTextDocumentMetrics documentMetrics() const
Returns the document metrics for the label.
QString text(int partId) const
Returns the text component corresponding to a specified label part.
bool hasCharacterFormat(int partId) const
Returns true if the feature contains specific character formatting for the part with matching ID.
std::optional< QgsPrecalculatedTextMetrics > mTextMetrics
void setDocument(const QgsTextDocument &document, const QgsTextDocumentMetrics &metrics)
Sets the document and document metrics for the label.
static constexpr double SUPERSCRIPT_SUBSCRIPT_FONT_SIZE_SCALING_FACTOR
Scale factor to use for super or subscript text which doesn't have an explicit font size set.
std::unique_ptr< GEOSGeometry, GeosDeleter > unique_ptr
Scoped GEOS pointer.
Definition: qgsgeos.h:73
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:5207
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
Definition: qgsfeatureid.h:28