24#include <QFontMetricsF>
37 const QFont font = format.
scaledFont( context, scaleFactor, &res.mIsNullSize );
45 double heightLabelMode = 0;
46 double heightPointRectMode = 0;
47 double heightCapHeightMode = 0;
48 double heightAscentMode = 0;
49 const int blockSize = document.
size();
50 res.mFragmentFonts.reserve( blockSize );
51 double currentLabelBaseline = 0;
52 double currentPointBaseline = 0;
53 double currentRectBaseline = 0;
54 double currentCapHeightBasedBaseline = 0;
55 double currentAscentBasedBaseline = 0;
56 double lastLineLeading = 0;
58 double heightVerticalOrientation = 0;
60 QVector < double > blockVerticalLineSpacing;
64 double outerYMinLabel = 0;
65 double outerYMaxLabel = 0;
67 for (
int blockIndex = 0; blockIndex < blockSize; blockIndex++ )
73 double blockYMaxAdjustLabel = 0;
75 double blockHeightUsingAscentDescent = 0;
76 double blockHeightUsingLineSpacing = 0;
77 double blockHeightVerticalOrientation = 0;
79 double blockHeightUsingAscentAccountingForVerticalOffset = 0;
81 const int fragmentSize = block.
size();
83 double maxBlockAscent = 0;
84 double maxBlockDescent = 0;
85 double maxLineSpacing = 0;
86 double maxBlockLeading = 0;
87 double maxBlockMaxWidth = 0;
88 double maxBlockCapHeight = 0;
90 QList< double > fragmentVerticalOffsets;
91 fragmentVerticalOffsets.reserve( fragmentSize );
93 QList< QFont > fragmentFonts;
94 fragmentFonts.reserve( fragmentSize );
98 QFont previousNonSuperSubScriptFont;
100 for (
int fragmentIndex = 0; fragmentIndex < fragmentSize; ++fragmentIndex )
105 double fragmentHeightForVerticallyOffsetText = 0;
106 double fragmentYMaxAdjust = 0;
108 QFont updatedFont = font;
111 QFontMetricsF fm( updatedFont );
113 if ( fragmentIndex == 0 )
114 previousNonSuperSubScriptFont = updatedFont;
122 previousNonSuperSubScriptFont = updatedFont;
127 const QFontMetricsF previousFM( previousNonSuperSubScriptFont );
137 fm = QFontMetricsF( updatedFont );
151 const QFontMetricsF previousFM( previousNonSuperSubScriptFont );
157 fm = QFontMetricsF( updatedFont );
170 previousNonSuperSubScriptFont = updatedFont;
174 const double fragmentWidth = fm.horizontalAdvance( fragment.
text() ) / scaleFactor;
178 const double fragmentHeightUsingAscentDescent = ( fm.ascent() + fm.descent() ) / scaleFactor;
179 const double fragmentHeightUsingLineSpacing = fm.lineSpacing() / scaleFactor;
182 blockXMax += fragmentWidth;
183 blockHeightUsingAscentDescent = std::max( blockHeightUsingAscentDescent, fragmentHeightUsingAscentDescent );
185 blockHeightUsingLineSpacing = std::max( blockHeightUsingLineSpacing, fragmentHeightUsingLineSpacing );
186 maxBlockAscent = std::max( maxBlockAscent, fm.ascent() / scaleFactor );
188 maxBlockCapHeight = std::max( maxBlockCapHeight, fm.capHeight() / scaleFactor );
190 blockHeightUsingAscentAccountingForVerticalOffset = std::max( std::max( maxBlockAscent, fragmentHeightForVerticallyOffsetText ), blockHeightUsingAscentAccountingForVerticalOffset );
192 maxBlockDescent = std::max( maxBlockDescent, fm.descent() / scaleFactor );
193 maxBlockMaxWidth = std::max( maxBlockMaxWidth, fm.maxWidth() / scaleFactor );
195 blockYMaxAdjustLabel = std::max( blockYMaxAdjustLabel, fragmentYMaxAdjust );
197 if ( ( fm.lineSpacing() / scaleFactor ) > maxLineSpacing )
199 maxLineSpacing = fm.lineSpacing() / scaleFactor;
200 maxBlockLeading = fm.leading() / scaleFactor;
203 fragmentFonts << updatedFont;
205 const double verticalOrientationFragmentHeight = fragmentIndex == 0 ? ( fm.ascent() / scaleFactor * fragment.
text().size() + ( fragment.
text().size() - 1 ) * updatedFont.letterSpacing() / scaleFactor )
206 : ( fragment.
text().size() * ( fm.ascent() / scaleFactor + updatedFont.letterSpacing() / scaleFactor ) );
207 blockHeightVerticalOrientation += verticalOrientationFragmentHeight;
210 if ( blockIndex == 0 )
214 res.mFirstLineAscentOffset = 0.25 * maxBlockAscent;
215 res.mLastLineAscentOffset = res.mFirstLineAscentOffset;
216 res.mFirstLineCapHeight = maxBlockCapHeight;
217 const double lineHeight = ( maxBlockAscent + maxBlockDescent );
222 currentLabelBaseline = -res.mFirstLineAscentOffset;
224 if ( blockHeightUsingAscentAccountingForVerticalOffset > maxBlockAscent )
225 outerYMinLabel = maxBlockAscent - blockHeightUsingAscentAccountingForVerticalOffset;
228 currentRectBaseline = -res.mFirstLineAscentOffset + lineHeight - 1 ;
230 currentCapHeightBasedBaseline = res.mFirstLineCapHeight;
231 currentAscentBasedBaseline = maxBlockAscent;
234 currentPointBaseline = 0;
236 heightLabelMode += blockHeightUsingAscentDescent;
237 heightPointRectMode += blockHeightUsingAscentDescent;
238 heightCapHeightMode += maxBlockCapHeight;
239 heightAscentMode += maxBlockAscent;
243 const double thisLineHeightUsingAscentDescent = format.
lineHeightUnit() == Qgis::RenderUnit::Percentage ? ( format.
lineHeight() * ( maxBlockAscent + maxBlockDescent ) ) : lineHeightPainterUnits;
244 const double thisLineHeightUsingLineSpacing = format.
lineHeightUnit() == Qgis::RenderUnit::Percentage ? ( format.
lineHeight() * maxLineSpacing ) : lineHeightPainterUnits;
246 currentLabelBaseline += thisLineHeightUsingAscentDescent;
247 currentRectBaseline += thisLineHeightUsingLineSpacing;
248 currentPointBaseline += thisLineHeightUsingLineSpacing;
250 currentCapHeightBasedBaseline += thisLineHeightUsingLineSpacing;
252 currentAscentBasedBaseline += thisLineHeightUsingLineSpacing;
254 heightLabelMode += thisLineHeightUsingAscentDescent;
255 heightPointRectMode += thisLineHeightUsingLineSpacing;
256 heightCapHeightMode += thisLineHeightUsingLineSpacing;
257 heightAscentMode += thisLineHeightUsingLineSpacing;
258 if ( blockIndex == blockSize - 1 )
259 res.mLastLineAscentOffset = 0.25 * maxBlockAscent;
262 if ( blockIndex == blockSize - 1 )
264 if ( blockYMaxAdjustLabel > maxBlockDescent )
265 outerYMaxLabel = blockYMaxAdjustLabel - maxBlockDescent;
268 blockVerticalLineSpacing << ( format.
lineHeightUnit() == Qgis::RenderUnit::Percentage ? ( maxBlockMaxWidth * format.
lineHeight() ) : lineHeightPainterUnits );
270 res.mBlockHeights << blockHeightUsingLineSpacing;
273 outerXMax = std::max( outerXMax, blockXMax );
275 heightVerticalOrientation = std::max( heightVerticalOrientation, blockHeightVerticalOrientation );
277 res.mFragmentFonts << fragmentFonts;
278 res.mBaselineOffsetsLabelMode << currentLabelBaseline;
279 res.mBaselineOffsetsPointMode << currentPointBaseline;
280 res.mBaselineOffsetsRectMode << currentRectBaseline;
281 res.mBaselineOffsetsCapHeightMode << currentCapHeightBasedBaseline;
282 res.mBaselineOffsetsAscentBased << currentAscentBasedBaseline;
283 res.mBlockMaxDescent << maxBlockDescent;
284 res.mBlockMaxCharacterWidth << maxBlockMaxWidth;
285 res.mFragmentVerticalOffsetsLabelMode << fragmentVerticalOffsets;
286 res.mFragmentVerticalOffsetsRectMode << fragmentVerticalOffsets;
287 res.mFragmentVerticalOffsetsPointMode << fragmentVerticalOffsets;
290 if ( blockIndex > 0 )
291 lastLineLeading = maxBlockLeading;
294 heightLabelMode -= lastLineLeading;
295 heightPointRectMode -= lastLineLeading;
297 res.mDocumentSizeLabelMode = QSizeF( width, heightLabelMode );
298 res.mDocumentSizePointRectMode = QSizeF( width, heightPointRectMode );
299 res.mDocumentSizeCapHeightMode = QSizeF( width, heightCapHeightMode );
300 res.mDocumentSizeAscentMode = QSizeF( width, heightAscentMode );
303 if ( !res.mBaselineOffsetsLabelMode.isEmpty() )
305 const double labelModeBaselineAdjust = res.mBaselineOffsetsLabelMode.constLast() + res.mLastLineAscentOffset;
306 const double pointModeBaselineAdjust = res.mBaselineOffsetsPointMode.constLast();
307 for (
int i = 0; i < blockSize; ++i )
309 res.mBaselineOffsetsLabelMode[i] -= labelModeBaselineAdjust;
310 res.mBaselineOffsetsPointMode[i] -= pointModeBaselineAdjust;
314 if ( !res.mBlockMaxCharacterWidth.isEmpty() )
316 QList< double > adjustedRightToLeftXOffsets;
317 double currentOffset = 0;
318 const int size = res.mBlockMaxCharacterWidth.size();
320 double widthVerticalOrientation = 0;
321 for (
int i = 0; i < size; ++i )
323 const double rightToLeftBlockMaxCharacterWidth = res.mBlockMaxCharacterWidth[size - 1 - i ];
324 const double rightToLeftLineSpacing = blockVerticalLineSpacing[ size - 1 - i ];
326 adjustedRightToLeftXOffsets << currentOffset;
327 currentOffset += rightToLeftLineSpacing;
330 widthVerticalOrientation += rightToLeftBlockMaxCharacterWidth;
332 widthVerticalOrientation += rightToLeftLineSpacing;
334 std::reverse( adjustedRightToLeftXOffsets.begin(), adjustedRightToLeftXOffsets.end() );
335 res.mVerticalOrientationXOffsets = adjustedRightToLeftXOffsets;
337 res.mDocumentSizeVerticalOrientation = QSizeF( widthVerticalOrientation, heightVerticalOrientation );
340 res.mOuterBoundsLabelMode = QRectF( outerXMin, -outerYMaxLabel,
341 outerXMax - outerXMin,
342 heightLabelMode - outerYMinLabel + outerYMaxLabel );
349 switch ( orientation )
351 case Qgis::TextOrientation::Horizontal:
354 case Qgis::TextLayoutMode::Rectangle:
356 return mDocumentSizePointRectMode;
359 return mDocumentSizeCapHeightMode;
362 return mDocumentSizeAscentMode;
364 case Qgis::TextLayoutMode::Labeling:
365 return mDocumentSizeLabelMode;
369 case Qgis::TextOrientation::Vertical:
370 return mDocumentSizeVerticalOrientation;
371 case Qgis::TextOrientation::RotationBased:
380 switch ( orientation )
382 case Qgis::TextOrientation::Horizontal:
385 case Qgis::TextLayoutMode::Rectangle:
391 case Qgis::TextLayoutMode::Labeling:
392 return mOuterBoundsLabelMode;
396 case Qgis::TextOrientation::Vertical:
397 case Qgis::TextOrientation::RotationBased:
406 return mBlockWidths.value( blockIndex );
411 return mBlockHeights.value( blockIndex );
416 return mFirstLineCapHeight;
423 case Qgis::TextLayoutMode::Rectangle:
424 return mBaselineOffsetsRectMode.value( blockIndex );
426 return mBaselineOffsetsCapHeightMode.value( blockIndex );
428 return mBaselineOffsetsAscentBased.value( blockIndex );
430 return mBaselineOffsetsPointMode.value( blockIndex );
431 case Qgis::TextLayoutMode::Labeling:
432 return mBaselineOffsetsLabelMode.value( blockIndex );
439 return mFragmentHorizontalAdvance.value( blockIndex ).value( fragmentIndex );
446 case Qgis::TextLayoutMode::Rectangle:
449 return mFragmentVerticalOffsetsRectMode.value( blockIndex ).value( fragmentIndex );
451 return mFragmentVerticalOffsetsPointMode.value( blockIndex ).value( fragmentIndex );
452 case Qgis::TextLayoutMode::Labeling:
453 return mFragmentVerticalOffsetsLabelMode.value( blockIndex ).value( fragmentIndex );
460 return mVerticalOrientationXOffsets.value( blockIndex );
465 return mBlockMaxCharacterWidth.value( blockIndex );
470 return mBlockMaxDescent.value( blockIndex );
475 return mFragmentFonts.value( blockIndex ).value( fragmentIndex );
TextLayoutMode
Text layout modes.
@ 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 ...
TextOrientation
Text orientations.
@ 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.
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.
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.
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.
#define BUILTIN_UNREACHABLE
constexpr double SUPERSCRIPT_VERTICAL_BASELINE_ADJUSTMENT_FACTOR
constexpr double SUBSCRIPT_VERTICAL_BASELINE_ADJUSTMENT_FACTOR
constexpr double SUPERSCRIPT_SUBSCRIPT_FONT_SIZE_SCALING_FACTOR