QGIS API Documentation 3.39.0-Master (47f7b3a4989)
Loading...
Searching...
No Matches
qgstextdocumentmetrics.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgstextdocumentmetrics.cpp
3 -----------------
4 begin : September 2022
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 ***************************************************************************/
16#include "qgis.h"
17#include "qgsstringutils.h"
18#include "qgstextblock.h"
19#include "qgstextfragment.h"
20#include "qgstextformat.h"
21#include "qgstextdocument.h"
22#include "qgsrendercontext.h"
23#include "qgstextrenderer.h"
24
25#include <QFontMetricsF>
26
27// to match Qt behavior in QTextLine::draw
30
31QgsTextDocumentMetrics QgsTextDocumentMetrics::calculateMetrics( const QgsTextDocument &document, const QgsTextFormat &format, const QgsRenderContext &context, double scaleFactor )
32{
34
35 const QFont font = format.scaledFont( context, scaleFactor, &res.mIsNullSize );
36 if ( res.isNullFontSize() )
37 return res;
38
39 // for absolute line heights
40 const double lineHeightPainterUnits = context.convertToPainterUnits( format.lineHeight(), format.lineHeightUnit() );
41
42 const double tabStopDistancePainterUnits = format.tabStopDistanceUnit() == Qgis::RenderUnit::Percentage
43 ? format.tabStopDistance() * font.pixelSize() / scaleFactor
45
46 double width = 0;
47 double heightLabelMode = 0;
48 double heightPointRectMode = 0;
49 double heightCapHeightMode = 0;
50 double heightAscentMode = 0;
51 const int blockSize = document.size();
52 res.mFragmentFonts.reserve( blockSize );
53 double currentLabelBaseline = 0;
54 double currentPointBaseline = 0;
55 double currentRectBaseline = 0;
56 double currentCapHeightBasedBaseline = 0;
57 double currentAscentBasedBaseline = 0;
58 double lastLineLeading = 0;
59
60 double heightVerticalOrientation = 0;
61
62 QVector < double > blockVerticalLineSpacing;
63
64 double outerXMin = 0;
65 double outerXMax = 0;
66 double outerYMinLabel = 0;
67 double outerYMaxLabel = 0;
68
69 for ( int blockIndex = 0; blockIndex < blockSize; blockIndex++ )
70 {
71 const QgsTextBlock &block = document.at( blockIndex );
72
73 double blockWidth = 0;
74 double blockXMax = 0;
75 double blockYMaxAdjustLabel = 0;
76
77 double blockHeightUsingAscentDescent = 0;
78 double blockHeightUsingLineSpacing = 0;
79 double blockHeightVerticalOrientation = 0;
80
81 double blockHeightUsingAscentAccountingForVerticalOffset = 0;
82
83 const int fragmentSize = block.size();
84
85 double maxBlockAscent = 0;
86 double maxBlockDescent = 0;
87 double maxLineSpacing = 0;
88 double maxBlockLeading = 0;
89 double maxBlockMaxWidth = 0;
90 double maxBlockCapHeight = 0;
91
92 QList< double > fragmentVerticalOffsets;
93 fragmentVerticalOffsets.reserve( fragmentSize );
94
95 QList< QFont > fragmentFonts;
96 fragmentFonts.reserve( fragmentSize );
97 QList< double >fragmentHorizontalAdvance;
98 fragmentHorizontalAdvance.reserve( fragmentSize );
99
100 QFont previousNonSuperSubScriptFont;
101
102 bool isFirstNonTabFragment = true;
103
104 for ( int fragmentIndex = 0; fragmentIndex < fragmentSize; ++fragmentIndex )
105 {
106 const QgsTextFragment &fragment = block.at( fragmentIndex );
107 if ( fragment.isTab() )
108 {
109 // special handling for tab characters
110 const double nextTabStop = ( std::floor( blockXMax / tabStopDistancePainterUnits ) + 1 ) * tabStopDistancePainterUnits;
111 const double fragmentWidth = nextTabStop - blockXMax;
112
113 blockWidth += fragmentWidth;
114 blockXMax += fragmentWidth;
115
116 fragmentVerticalOffsets << 0;
117 fragmentHorizontalAdvance << fragmentWidth;
118 fragmentFonts << QFont();
119 }
120 else
121 {
122 const QgsTextCharacterFormat &fragmentFormat = fragment.characterFormat();
123
124 double fragmentHeightForVerticallyOffsetText = 0;
125 double fragmentYMaxAdjust = 0;
126
127 QFont updatedFont = font;
128 fragmentFormat.updateFontForFormat( updatedFont, context, scaleFactor );
129
130 QFontMetricsF fm( updatedFont );
131
132 if ( isFirstNonTabFragment )
133 previousNonSuperSubScriptFont = updatedFont;
134
135 double fragmentVerticalOffset = 0;
136 if ( fragmentFormat.hasVerticalAlignmentSet() )
137 {
138 switch ( fragmentFormat.verticalAlignment() )
139 {
141 previousNonSuperSubScriptFont = updatedFont;
142 break;
143
145 {
146 const QFontMetricsF previousFM( previousNonSuperSubScriptFont );
147
148 if ( fragmentFormat.fontPointSize() < 0 )
149 {
150 // if fragment has no explicit font size set, then we scale the inherited font size to 60% of base font size
151 // this allows for easier use of super/subscript in labels as "my text<sup>2</sup>" will automatically render
152 // the superscript in a smaller font size. BUT if the fragment format HAS a non -1 font size then it indicates
153 // that the document has an explicit font size for the super/subscript element, eg "my text<sup style="font-size: 6pt">2</sup>"
154 // which we should respect
155 updatedFont.setPixelSize( static_cast< int >( std::round( updatedFont.pixelSize() * QgsTextRenderer::SUPERSCRIPT_SUBSCRIPT_FONT_SIZE_SCALING_FACTOR ) ) );
156 fm = QFontMetricsF( updatedFont );
157 }
158
159 // to match Qt behavior in QTextLine::draw
160 fragmentVerticalOffset = -( previousFM.ascent() + previousFM.descent() ) * SUPERSCRIPT_VERTICAL_BASELINE_ADJUSTMENT_FACTOR / scaleFactor;
161
162 // note -- this should really be fm.ascent(), not fm.capHeight() -- but in practice the ascent of most fonts is too large
163 // and causes unnecessarily large bounding boxes of vertically offset text!
164 fragmentHeightForVerticallyOffsetText = -fragmentVerticalOffset + fm.capHeight() / scaleFactor;
165 break;
166 }
167
169 {
170 const QFontMetricsF previousFM( previousNonSuperSubScriptFont );
171
172 if ( fragmentFormat.fontPointSize() < 0 )
173 {
174 // see above!!
175 updatedFont.setPixelSize( static_cast< int>( std::round( updatedFont.pixelSize() * QgsTextRenderer::SUPERSCRIPT_SUBSCRIPT_FONT_SIZE_SCALING_FACTOR ) ) );
176 fm = QFontMetricsF( updatedFont );
177 }
178
179 // to match Qt behavior in QTextLine::draw
180 fragmentVerticalOffset = ( previousFM.ascent() + previousFM.descent() ) * SUBSCRIPT_VERTICAL_BASELINE_ADJUSTMENT_FACTOR / scaleFactor;
181
182 fragmentYMaxAdjust = fragmentVerticalOffset + fm.descent() / scaleFactor;
183 break;
184 }
185 }
186 }
187 else
188 {
189 previousNonSuperSubScriptFont = updatedFont;
190 }
191 fragmentVerticalOffsets << fragmentVerticalOffset;
192
193 const double fragmentWidth = fm.horizontalAdvance( fragment.text() ) / scaleFactor;
194
195 fragmentHorizontalAdvance << fragmentWidth;
196
197 const double fragmentHeightUsingAscentDescent = ( fm.ascent() + fm.descent() ) / scaleFactor;
198 const double fragmentHeightUsingLineSpacing = fm.lineSpacing() / scaleFactor;
199
200 blockWidth += fragmentWidth;
201 blockXMax += fragmentWidth;
202 blockHeightUsingAscentDescent = std::max( blockHeightUsingAscentDescent, fragmentHeightUsingAscentDescent );
203
204 blockHeightUsingLineSpacing = std::max( blockHeightUsingLineSpacing, fragmentHeightUsingLineSpacing );
205 maxBlockAscent = std::max( maxBlockAscent, fm.ascent() / scaleFactor );
206
207 maxBlockCapHeight = std::max( maxBlockCapHeight, fm.capHeight() / scaleFactor );
208
209 blockHeightUsingAscentAccountingForVerticalOffset = std::max( std::max( maxBlockAscent, fragmentHeightForVerticallyOffsetText ), blockHeightUsingAscentAccountingForVerticalOffset );
210
211 maxBlockDescent = std::max( maxBlockDescent, fm.descent() / scaleFactor );
212 maxBlockMaxWidth = std::max( maxBlockMaxWidth, fm.maxWidth() / scaleFactor );
213
214 blockYMaxAdjustLabel = std::max( blockYMaxAdjustLabel, fragmentYMaxAdjust );
215
216 if ( ( fm.lineSpacing() / scaleFactor ) > maxLineSpacing )
217 {
218 maxLineSpacing = fm.lineSpacing() / scaleFactor;
219 maxBlockLeading = fm.leading() / scaleFactor;
220 }
221
222 fragmentFonts << updatedFont;
223
224 const double verticalOrientationFragmentHeight = isFirstNonTabFragment ? ( fm.ascent() / scaleFactor * fragment.text().size() + ( fragment.text().size() - 1 ) * updatedFont.letterSpacing() / scaleFactor )
225 : ( fragment.text().size() * ( fm.ascent() / scaleFactor + updatedFont.letterSpacing() / scaleFactor ) );
226 blockHeightVerticalOrientation += verticalOrientationFragmentHeight;
227
228 isFirstNonTabFragment = false;
229 }
230 }
231
232 if ( blockIndex == 0 )
233 {
234 // same logic as used in QgsTextRenderer. (?!!)
235 // needed to move bottom of text's descender to within bottom edge of label
236 res.mFirstLineAscentOffset = 0.25 * maxBlockAscent; // descent() is not enough
237 res.mLastLineAscentOffset = res.mFirstLineAscentOffset;
238 res.mFirstLineCapHeight = maxBlockCapHeight;
239 const double lineHeight = ( maxBlockAscent + maxBlockDescent ); // ignore +1 for baseline
240
241 // rendering labels needs special handling - in this case text should be
242 // drawn with the bottom left corner coinciding with origin, vs top left
243 // for standard text rendering. Line height is also slightly different.
244 currentLabelBaseline = -res.mFirstLineAscentOffset;
245
246 if ( blockHeightUsingAscentAccountingForVerticalOffset > maxBlockAscent )
247 outerYMinLabel = maxBlockAscent - blockHeightUsingAscentAccountingForVerticalOffset;
248
249 // standard rendering - designed to exactly replicate QPainter's drawText method
250 currentRectBaseline = -res.mFirstLineAscentOffset + lineHeight - 1 /*baseline*/;
251
252 currentCapHeightBasedBaseline = res.mFirstLineCapHeight;
253 currentAscentBasedBaseline = maxBlockAscent;
254
255 // standard rendering - designed to exactly replicate QPainter's drawText rect method
256 currentPointBaseline = 0;
257
258 heightLabelMode += blockHeightUsingAscentDescent;
259 heightPointRectMode += blockHeightUsingAscentDescent;
260 heightCapHeightMode += maxBlockCapHeight;
261 heightAscentMode += maxBlockAscent;
262 }
263 else
264 {
265 const double thisLineHeightUsingAscentDescent = format.lineHeightUnit() == Qgis::RenderUnit::Percentage ? ( format.lineHeight() * ( maxBlockAscent + maxBlockDescent ) ) : lineHeightPainterUnits;
266 const double thisLineHeightUsingLineSpacing = format.lineHeightUnit() == Qgis::RenderUnit::Percentage ? ( format.lineHeight() * maxLineSpacing ) : lineHeightPainterUnits;
267
268 currentLabelBaseline += thisLineHeightUsingAscentDescent;
269 currentRectBaseline += thisLineHeightUsingLineSpacing;
270 currentPointBaseline += thisLineHeightUsingLineSpacing;
271 // using cap height??
272 currentCapHeightBasedBaseline += thisLineHeightUsingLineSpacing;
273 // using ascent?
274 currentAscentBasedBaseline += thisLineHeightUsingLineSpacing;
275
276 heightLabelMode += thisLineHeightUsingAscentDescent;
277 heightPointRectMode += thisLineHeightUsingLineSpacing;
278 heightCapHeightMode += thisLineHeightUsingLineSpacing;
279 heightAscentMode += thisLineHeightUsingLineSpacing;
280 if ( blockIndex == blockSize - 1 )
281 res.mLastLineAscentOffset = 0.25 * maxBlockAscent;
282 }
283
284 if ( blockIndex == blockSize - 1 )
285 {
286 if ( blockYMaxAdjustLabel > maxBlockDescent )
287 outerYMaxLabel = blockYMaxAdjustLabel - maxBlockDescent;
288 }
289
290 blockVerticalLineSpacing << ( format.lineHeightUnit() == Qgis::RenderUnit::Percentage ? ( maxBlockMaxWidth * format.lineHeight() ) : lineHeightPainterUnits );
291
292 res.mBlockHeights << blockHeightUsingLineSpacing;
293
294 width = std::max( width, blockWidth );
295 outerXMax = std::max( outerXMax, blockXMax );
296
297 heightVerticalOrientation = std::max( heightVerticalOrientation, blockHeightVerticalOrientation );
298 res.mBlockWidths << blockWidth;
299 res.mFragmentFonts << fragmentFonts;
300 res.mBaselineOffsetsLabelMode << currentLabelBaseline;
301 res.mBaselineOffsetsPointMode << currentPointBaseline;
302 res.mBaselineOffsetsRectMode << currentRectBaseline;
303 res.mBaselineOffsetsCapHeightMode << currentCapHeightBasedBaseline;
304 res.mBaselineOffsetsAscentBased << currentAscentBasedBaseline;
305 res.mBlockMaxDescent << maxBlockDescent;
306 res.mBlockMaxCharacterWidth << maxBlockMaxWidth;
307 res.mFragmentVerticalOffsetsLabelMode << fragmentVerticalOffsets;
308 res.mFragmentVerticalOffsetsRectMode << fragmentVerticalOffsets;
309 res.mFragmentVerticalOffsetsPointMode << fragmentVerticalOffsets;
310 res.mFragmentHorizontalAdvance << fragmentHorizontalAdvance;
311
312 if ( blockIndex > 0 )
313 lastLineLeading = maxBlockLeading;
314 }
315
316 heightLabelMode -= lastLineLeading;
317 heightPointRectMode -= lastLineLeading;
318
319 res.mDocumentSizeLabelMode = QSizeF( width, heightLabelMode );
320 res.mDocumentSizePointRectMode = QSizeF( width, heightPointRectMode );
321 res.mDocumentSizeCapHeightMode = QSizeF( width, heightCapHeightMode );
322 res.mDocumentSizeAscentMode = QSizeF( width, heightAscentMode );
323
324 // adjust baselines
325 if ( !res.mBaselineOffsetsLabelMode.isEmpty() )
326 {
327 const double labelModeBaselineAdjust = res.mBaselineOffsetsLabelMode.constLast() + res.mLastLineAscentOffset;
328 const double pointModeBaselineAdjust = res.mBaselineOffsetsPointMode.constLast();
329 for ( int i = 0; i < blockSize; ++i )
330 {
331 res.mBaselineOffsetsLabelMode[i] -= labelModeBaselineAdjust;
332 res.mBaselineOffsetsPointMode[i] -= pointModeBaselineAdjust;
333 }
334 }
335
336 if ( !res.mBlockMaxCharacterWidth.isEmpty() )
337 {
338 QList< double > adjustedRightToLeftXOffsets;
339 double currentOffset = 0;
340 const int size = res.mBlockMaxCharacterWidth.size();
341
342 double widthVerticalOrientation = 0;
343 for ( int i = 0; i < size; ++i )
344 {
345 const double rightToLeftBlockMaxCharacterWidth = res.mBlockMaxCharacterWidth[size - 1 - i ];
346 const double rightToLeftLineSpacing = blockVerticalLineSpacing[ size - 1 - i ];
347
348 adjustedRightToLeftXOffsets << currentOffset;
349 currentOffset += rightToLeftLineSpacing;
350
351 if ( i == size - 1 )
352 widthVerticalOrientation += rightToLeftBlockMaxCharacterWidth;
353 else
354 widthVerticalOrientation += rightToLeftLineSpacing;
355 }
356 std::reverse( adjustedRightToLeftXOffsets.begin(), adjustedRightToLeftXOffsets.end() );
357 res.mVerticalOrientationXOffsets = adjustedRightToLeftXOffsets;
358
359 res.mDocumentSizeVerticalOrientation = QSizeF( widthVerticalOrientation, heightVerticalOrientation );
360 }
361
362 res.mOuterBoundsLabelMode = QRectF( outerXMin, -outerYMaxLabel,
363 outerXMax - outerXMin,
364 heightLabelMode - outerYMinLabel + outerYMaxLabel );
365
366 return res;
367}
368
370{
371 switch ( orientation )
372 {
374 switch ( mode )
375 {
378 return mDocumentSizePointRectMode;
379
381 return mDocumentSizeCapHeightMode;
382
384 return mDocumentSizeAscentMode;
385
387 return mDocumentSizeLabelMode;
388 };
390
392 return mDocumentSizeVerticalOrientation;
394 return QSizeF(); // label mode only
395 }
396
398}
399
401{
402 switch ( orientation )
403 {
405 switch ( mode )
406 {
411 return QRectF();
412
414 return mOuterBoundsLabelMode;
415 };
417
420 return QRectF(); // label mode only
421 }
422
424}
425
426double QgsTextDocumentMetrics::blockWidth( int blockIndex ) const
427{
428 return mBlockWidths.value( blockIndex );
429}
430
431double QgsTextDocumentMetrics::blockHeight( int blockIndex ) const
432{
433 return mBlockHeights.value( blockIndex );
434}
435
437{
438 return mFirstLineCapHeight;
439}
440
442{
443 switch ( mode )
444 {
446 return mBaselineOffsetsRectMode.value( blockIndex );
448 return mBaselineOffsetsCapHeightMode.value( blockIndex );
450 return mBaselineOffsetsAscentBased.value( blockIndex );
452 return mBaselineOffsetsPointMode.value( blockIndex );
454 return mBaselineOffsetsLabelMode.value( blockIndex );
455 }
457}
458
459double QgsTextDocumentMetrics::fragmentHorizontalAdvance( int blockIndex, int fragmentIndex, Qgis::TextLayoutMode ) const
460{
461 return mFragmentHorizontalAdvance.value( blockIndex ).value( fragmentIndex );
462}
463
464double QgsTextDocumentMetrics::fragmentVerticalOffset( int blockIndex, int fragmentIndex, Qgis::TextLayoutMode mode ) const
465{
466 switch ( mode )
467 {
471 return mFragmentVerticalOffsetsRectMode.value( blockIndex ).value( fragmentIndex );
473 return mFragmentVerticalOffsetsPointMode.value( blockIndex ).value( fragmentIndex );
475 return mFragmentVerticalOffsetsLabelMode.value( blockIndex ).value( fragmentIndex );
476 }
478}
479
481{
482 return mVerticalOrientationXOffsets.value( blockIndex );
483}
484
486{
487 return mBlockMaxCharacterWidth.value( blockIndex );
488}
489
491{
492 return mBlockMaxDescent.value( blockIndex );
493}
494
495QFont QgsTextDocumentMetrics::fragmentFont( int blockIndex, int fragmentIndex ) const
496{
497 return mFragmentFonts.value( blockIndex ).value( fragmentIndex );
498}
499
TextLayoutMode
Text layout modes.
Definition qgis.h:2483
@ Labeling
Labeling-specific layout mode.
@ Point
Text at point of origin layout mode.
@ RectangleAscentBased
Similar to Rectangle mode, but uses ascents only when calculating font and line heights....
@ RectangleCapHeightBased
Similar to Rectangle mode, but uses cap height only when calculating font heights for the first line ...
@ Rectangle
Text within rectangle layout mode.
TextOrientation
Text orientations.
Definition qgis.h:2468
@ Vertical
Vertically oriented text.
@ RotationBased
Horizontally or vertically oriented text based on rotation (only available for map labeling)
@ Horizontal
Horizontally oriented text.
@ 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.
@ Percentage
Percentage of another measurement (e.g., canvas size, feature size)
Contains information about the context of a rendering operation.
double convertToPainterUnits(double size, Qgis::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale(), Qgis::RenderSubcomponentProperty property=Qgis::RenderSubcomponentProperty::Generic) const
Converts a size from the specified units to painter units (pixels).
Represents a block of text consisting of one or more QgsTextFragment objects.
int size() const
Returns the number of fragments in the block.
const QgsTextFragment & at(int index) const
Returns the fragment at the specified index.
Stores information relating to individual character formatting.
void updateFontForFormat(QFont &font, const QgsRenderContext &context, double scaleFactor=1.0) const
Updates the specified font in place, applying character formatting options which are applicable on a ...
Qgis::TextCharacterVerticalAlignment verticalAlignment() const
Returns the format vertical alignment.
bool hasVerticalAlignmentSet() const
Returns true if the format has an explicit vertical alignment set.
double fontPointSize() const
Returns the font point size, or -1 if the font size is not set and should be inherited.
Contains pre-calculated metrics of a QgsTextDocument.
double verticalOrientationXOffset(int blockIndex) const
Returns the vertical orientation x offset for the specified block.
double fragmentVerticalOffset(int blockIndex, int fragmentIndex, Qgis::TextLayoutMode mode) const
Returns the vertical offset from a text block's baseline which should be applied to the fragment at t...
double blockMaximumDescent(int blockIndex) const
Returns the maximum descent encountered in the specified block.
QSizeF documentSize(Qgis::TextLayoutMode mode, Qgis::TextOrientation orientation) const
Returns the overall size of the document.
double firstLineCapHeight() const
Returns the cap height for the first line of text.
QFont fragmentFont(int blockIndex, int fragmentIndex) const
Returns the calculated font for the fragment at the specified block and fragment indices.
double blockMaximumCharacterWidth(int blockIndex) const
Returns the maximum character width for the specified block.
double baselineOffset(int blockIndex, Qgis::TextLayoutMode mode) const
Returns the offset from the top of the document to the text baseline for the given block index.
QRectF outerBounds(Qgis::TextLayoutMode mode, Qgis::TextOrientation orientation) const
Returns the outer bounds of the document, which is the documentSize() adjusted to account for any tex...
static QgsTextDocumentMetrics calculateMetrics(const QgsTextDocument &document, const QgsTextFormat &format, const QgsRenderContext &context, double scaleFactor=1.0)
Returns precalculated text metrics for a text document, when rendered using the given base format and...
double blockHeight(int blockIndex) const
Returns the height of the block at the specified index.
double fragmentHorizontalAdvance(int blockIndex, int fragmentIndex, Qgis::TextLayoutMode mode) const
Returns the horizontal advance of the fragment at the specified block and fragment index.
bool isNullFontSize() const
Returns true if the metrics could not be calculated because the text format has a null font size.
double blockWidth(int blockIndex) const
Returns the width of the block at the specified index.
Represents a document consisting of one or more QgsTextBlock objects.
const QgsTextBlock & at(int index) const
Returns the block at the specified index.
int size() const
Returns the number of blocks in the document.
Container for all settings relating to text rendering.
double lineHeight() const
Returns the line height for text.
double tabStopDistance() const
Returns the distance for tab stops.
QFont scaledFont(const QgsRenderContext &context, double scaleFactor=1.0, bool *isZeroSize=nullptr) const
Returns a font with the size scaled to match the format's size settings (including units and map unit...
Qgis::RenderUnit lineHeightUnit() const
Returns the units for the line height for text.
Qgis::RenderUnit tabStopDistanceUnit() const
Returns the units for the tab stop distance.
QgsMapUnitScale tabStopDistanceMapUnitScale() const
Returns the map unit scale object for the tab stop distance.
Stores a fragment of text along with formatting overrides to be used when rendering the fragment.
QString text() const
Returns the text content of the fragment.
const QgsTextCharacterFormat & characterFormat() const
Returns the character formatting for the fragment.
bool isTab() const
Returns true if the fragment consists of just a tab character.
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.
#define BUILTIN_UNREACHABLE
Definition qgis.h:6119
constexpr double SUPERSCRIPT_VERTICAL_BASELINE_ADJUSTMENT_FACTOR
constexpr double SUBSCRIPT_VERTICAL_BASELINE_ADJUSTMENT_FACTOR