27#include <QFontMetricsF>
96 res.mLastLineAscentOffset = res.mFirstLineAscentOffset;
127 thisLineHeightUsingAscentDescent = std::max( thisLineHeightUsingAscentDescent, metrics.
maxBlockFixedItemHeight );
175 res.mDocument.
append( outputBlock );
188 if ( fragment.
isTab() )
192 const double fragmentWidth = nextTabStop - thisBlockMetrics.
blockXMax;
195 thisBlockMetrics.
blockXMax += fragmentWidth;
201 currentOutputBlock.
append( fragment );
207 double fragmentHeightForVerticallyOffsetText = 0;
208 double fragmentYMaxAdjust = 0;
210 QFont updatedFont = font;
213 QFontMetricsF fm( updatedFont );
241 fm = QFontMetricsF( updatedFont );
261 fm = QFontMetricsF( updatedFont );
279 &fragmentHeightForVerticallyOffsetText,
285 thisBlockMetrics.blockYMaxAdjustLabel = std::max( thisBlockMetrics.blockYMaxAdjustLabel, fragmentYMaxAdjust );
286 thisBlockMetrics.blockHeightUsingAscentAccountingForVerticalOffset = std::max( std::max( thisBlockMetrics.maxBlockAscent, fragmentHeightForVerticallyOffsetText ), thisBlockMetrics.blockHeightUsingAscentAccountingForVerticalOffset );
288 thisBlockMetrics.fragmentHorizontalAdvance << fragmentWidth;
290 thisBlockMetrics.blockWidth += fragmentWidth;
291 thisBlockMetrics.blockXMax += fragmentWidth;
293 thisBlockMetrics.fragmentFonts << updatedFont;
295 const double verticalOrientationFragmentHeight = thisBlockMetrics.isFirstNonTabFragment ? ( fm.ascent() / scaleFactor * fragment.
text().size() + ( fragment.
text().size() - 1 ) * updatedFont.letterSpacing() / scaleFactor )
296 : ( fragment.text().size() * ( fm.ascent() / scaleFactor + updatedFont.letterSpacing() / scaleFactor ) );
297 thisBlockMetrics.blockHeightVerticalOrientation += verticalOrientationFragmentHeight;
299 thisBlockMetrics.isFirstNonTabFragment =
false;
305 double imageHeight = 0;
306 double imageWidth = 0;
313 const QSizeF originalSizeMmAt96Dpi = imageSize / 3.7795275590551185;
315 imageWidth = originalSizeMmAt96Dpi.width() * pixelsPerMm;
316 imageHeight = originalSizeMmAt96Dpi.height() * pixelsPerMm;
323 imageWidth = originalImageSize.width() * imageHeight / originalImageSize.height();
330 imageHeight = originalImageSize.height() * imageWidth / originalImageSize.width();
340 && ( thisBlockMetrics.blockXMax + imageWidth > documentContext.
maximumWidth() )
341 && !currentOutputBlock.
empty() )
344 finalizeBlock( res, format, documentMetrics, currentOutputBlock, thisBlockMetrics );
345 thisBlockMetrics.isFirstBlock =
false;
349 thisBlockMetrics.blockHeightUsingAscentDescent = std::max( thisBlockMetrics.blockHeightUsingAscentDescent, imageHeight + fm.descent() / scaleFactor );
350 thisBlockMetrics.blockHeightUsingLineSpacing = std::max( thisBlockMetrics.blockHeightUsingLineSpacing, imageHeight + fm.leading() );
352 thisBlockMetrics.maxBlockAscent = std::max( thisBlockMetrics.maxBlockAscent, imageHeight );
353 thisBlockMetrics.maxBlockCapHeight = std::max( thisBlockMetrics.maxBlockCapHeight, imageHeight );
354 thisBlockMetrics.maxLineSpacing = std::max( thisBlockMetrics.maxLineSpacing, imageHeight + fm.leading() / scaleFactor );
355 thisBlockMetrics.maxBlockLeading = std::max( thisBlockMetrics.maxBlockLeading, fm.leading() / scaleFactor );
356 thisBlockMetrics.maxBlockMaxWidth = std::max( thisBlockMetrics.maxBlockMaxWidth, imageWidth );
357 thisBlockMetrics.maxBlockFixedItemHeight = std::max( thisBlockMetrics.maxBlockFixedItemHeight, imageHeight );
358 thisBlockMetrics.fragmentFixedHeights << imageHeight;
359 updateCommonBlockMetrics( thisBlockMetrics, imageWidth, fragment );
360 currentOutputBlock.
append( fragment );
364 const double fragmentHeightUsingAscentDescent = ( fm.ascent() + fm.descent() ) / scaleFactor;
365 const double fragmentHeightUsingLineSpacing = fm.lineSpacing() / scaleFactor;
367 auto finalizeTextFragment = [fragmentHeightUsingAscentDescent,
368 fragmentHeightUsingLineSpacing,
372 &updateCommonBlockMetrics
375 thisBlockMetrics.blockHeightUsingAscentDescent = std::max( thisBlockMetrics.blockHeightUsingAscentDescent, fragmentHeightUsingAscentDescent );
377 thisBlockMetrics.blockHeightUsingLineSpacing = std::max( thisBlockMetrics.blockHeightUsingLineSpacing, fragmentHeightUsingLineSpacing );
378 thisBlockMetrics.maxBlockAscent = std::max( thisBlockMetrics.maxBlockAscent, fm.ascent() / scaleFactor );
379 thisBlockMetrics.maxBlockAscentForTextFragments = std::max( thisBlockMetrics.maxBlockAscentForTextFragments, fm.ascent() / scaleFactor );
381 thisBlockMetrics.maxBlockCapHeight = std::max( thisBlockMetrics.maxBlockCapHeight, fm.capHeight() / scaleFactor );
383 thisBlockMetrics.maxBlockDescent = std::max( thisBlockMetrics.maxBlockDescent, fm.descent() / scaleFactor );
384 thisBlockMetrics.maxBlockMaxWidth = std::max( thisBlockMetrics.maxBlockMaxWidth, fm.maxWidth() / scaleFactor );
386 if ( ( fm.lineSpacing() / scaleFactor ) > thisBlockMetrics.maxLineSpacing )
388 thisBlockMetrics.maxLineSpacing = fm.lineSpacing() / scaleFactor;
389 thisBlockMetrics.maxBlockLeading = fm.leading() / scaleFactor;
391 thisBlockMetrics.fragmentFixedHeights << -1;
392 updateCommonBlockMetrics( thisBlockMetrics, fragmentWidth, fragment );
393 currentOutputBlock.append( fragment );
396 double fragmentWidth = fm.horizontalAdvance( fragment.text() ) / scaleFactor;
400 && ( thisBlockMetrics.blockXMax + fragmentWidth > documentContext.
maximumWidth() ) )
405 const QStringList words = fragment.text().split(
' ' );
406 QStringList linesToProcess;
407 QStringList wordsInCurrentLine;
408 double remainingWidthInCurrentLine = documentContext.
maximumWidth() - thisBlockMetrics.blockXMax;
409 for (
const QString &word : words )
411 const double wordWidth = fm.horizontalAdvance( word ) / scaleFactor;
412 if ( wordWidth > remainingWidthInCurrentLine )
415 if ( !wordsInCurrentLine.isEmpty() )
416 linesToProcess << wordsInCurrentLine.join(
' ' );
417 wordsInCurrentLine.clear();
418 linesToProcess << word;
419 remainingWidthInCurrentLine = documentContext.
maximumWidth();
423 wordsInCurrentLine.append( word );
426 if ( !wordsInCurrentLine.isEmpty() )
427 linesToProcess << wordsInCurrentLine.join(
' ' );
429 remainingWidthInCurrentLine = documentContext.
maximumWidth() - thisBlockMetrics.blockXMax;
430 for (
int lineIndex = 0; lineIndex < linesToProcess.size(); ++lineIndex )
432 QString remainingText = linesToProcess.at( lineIndex );
433 int lastPos = remainingText.lastIndexOf(
' ' );
434 while ( lastPos > -1 )
437 if ( ( fm.horizontalAdvance( remainingText ) / scaleFactor ) <= remainingWidthInCurrentLine )
442 const double widthTextToLastPos = fm.horizontalAdvance( remainingText.left( lastPos ) ) / scaleFactor;
443 if ( widthTextToLastPos <= remainingWidthInCurrentLine )
447 thisLineFragment.
setText( remainingText.left( lastPos ) );
448 finalizeTextFragment( thisBlockMetrics, thisLineFragment, widthTextToLastPos );
450 finalizeBlock( res, format, documentMetrics, currentOutputBlock, thisBlockMetrics );
451 thisBlockMetrics.isFirstBlock =
false;
452 remainingWidthInCurrentLine = documentContext.
maximumWidth();
453 remainingText = remainingText.mid( lastPos + 1 );
456 lastPos = remainingText.lastIndexOf(
' ', lastPos - 1 );
460 if ( ( fm.horizontalAdvance( remainingText ) / scaleFactor ) > remainingWidthInCurrentLine && !currentOutputBlock.empty() )
462 finalizeBlock( res, format, documentMetrics, currentOutputBlock, thisBlockMetrics );
463 thisBlockMetrics.isFirstBlock =
false;
464 remainingWidthInCurrentLine = documentContext.
maximumWidth();
469 thisLineFragment.
setText( remainingText );
470 finalizeTextFragment( thisBlockMetrics, thisLineFragment, fm.horizontalAdvance( remainingText ) / scaleFactor );
472 if ( lineIndex < linesToProcess.size() - 1 )
475 finalizeBlock( res, format, documentMetrics, currentOutputBlock, thisBlockMetrics );
476 thisBlockMetrics.isFirstBlock =
false;
477 remainingWidthInCurrentLine = documentContext.
maximumWidth();
480 thisBlockMetrics.isFirstBlock =
false;
486 finalizeTextFragment( thisBlockMetrics, fragment, fragmentWidth );
496 const QFont font = format.
scaledFont( context, scaleFactor, &res.mIsNullSize );
511 res.mFragmentFonts.reserve( documentMetrics.
blockSize );
513 for (
int blockIndex = 0; blockIndex < documentMetrics.
blockSize; blockIndex++ )
520 const int fragmentSize = block.
size();
531 for (
int fragmentIndex = 0; fragmentIndex < fragmentSize; ++fragmentIndex )
534 processFragment( res, format, context, documentContext, scaleFactor, documentMetrics, thisBlockMetrics, font, fragment, outputBlock );
537 finalizeBlock( res, format, documentMetrics, outputBlock, thisBlockMetrics );
549 if ( !res.mBaselineOffsetsLabelMode.isEmpty() )
551 const double labelModeBaselineAdjust = res.mBaselineOffsetsLabelMode.constLast() + res.mLastLineAscentOffset;
552 const double pointModeBaselineAdjust = res.mBaselineOffsetsPointMode.constLast();
553 for (
int i = 0; i < documentMetrics.
blockSize; ++i )
555 res.mBaselineOffsetsLabelMode[i] -= labelModeBaselineAdjust;
556 res.mBaselineOffsetsPointMode[i] -= pointModeBaselineAdjust;
560 if ( !res.mBlockMaxCharacterWidth.isEmpty() )
562 QList< double > adjustedRightToLeftXOffsets;
563 double currentOffset = 0;
564 const int size = res.mBlockMaxCharacterWidth.size();
566 double widthVerticalOrientation = 0;
567 for (
int i = 0; i < size; ++i )
569 const double rightToLeftBlockMaxCharacterWidth = res.mBlockMaxCharacterWidth[size - 1 - i ];
572 adjustedRightToLeftXOffsets << currentOffset;
573 currentOffset += rightToLeftLineSpacing;
576 widthVerticalOrientation += rightToLeftBlockMaxCharacterWidth;
578 widthVerticalOrientation += rightToLeftLineSpacing;
580 std::reverse( adjustedRightToLeftXOffsets.begin(), adjustedRightToLeftXOffsets.end() );
581 res.mVerticalOrientationXOffsets = adjustedRightToLeftXOffsets;
595 switch ( orientation )
602 return mDocumentSizePointRectMode;
605 return mDocumentSizeCapHeightMode;
608 return mDocumentSizeAscentMode;
611 return mDocumentSizeLabelMode;
616 return mDocumentSizeVerticalOrientation;
626 switch ( orientation )
638 return mOuterBoundsLabelMode;
652 return mBlockWidths.value( blockIndex );
657 return mBlockHeights.value( blockIndex );
662 return mFirstLineCapHeight;
670 return mBaselineOffsetsRectMode.value( blockIndex );
672 return mBaselineOffsetsCapHeightMode.value( blockIndex );
674 return mBaselineOffsetsAscentBased.value( blockIndex );
676 return mBaselineOffsetsPointMode.value( blockIndex );
678 return mBaselineOffsetsLabelMode.value( blockIndex );
685 return mFragmentHorizontalAdvance.value( blockIndex ).value( fragmentIndex );
695 return mFragmentVerticalOffsetsRectMode.value( blockIndex ).value( fragmentIndex );
697 return mFragmentVerticalOffsetsPointMode.value( blockIndex ).value( fragmentIndex );
699 return mFragmentVerticalOffsetsLabelMode.value( blockIndex ).value( fragmentIndex );
706 return mFragmentFixedHeights.value( blockIndex ).value( fragmentIndex );
711 return mVerticalOrientationXOffsets.value( blockIndex );
716 return mBlockMaxCharacterWidth.value( blockIndex );
721 return mBlockMaxDescent.value( blockIndex );
726 return mFragmentFonts.value( blockIndex ).value( fragmentIndex );
TextLayoutMode
Text layout modes.
@ 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.
@ 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)
@ Points
Points (e.g., for font sizes)
@ RenderBlocking
Render and load remote sources in the same thread to ensure rendering remote sources (svg and images)...
@ WrapLines
Automatically wrap long lines of text.
static QgsImageCache * imageCache()
Returns the application's image cache, used for caching resampled versions of raster images.
QSize originalSize(const QString &path, bool blocking=false) const
Returns the original size (in pixels) of the image at the specified path.
Contains information about the context of a rendering operation.
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
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).
Qgis::RenderContextFlags flags() const
Returns combination of flags used for rendering.
Represents a block of text consisting of one or more QgsTextFragment objects.
int size() const
Returns the number of fragments in the block.
const QgsTextBlockFormat & blockFormat() const
Returns the block formatting for the fragment.
void clear()
Clears the block, removing all its contents.
void reserve(int count)
Reserves the specified count of fragments for optimised fragment appending.
void setBlockFormat(const QgsTextBlockFormat &format)
Sets the block format for the fragment.
void append(const QgsTextFragment &fragment)
Appends a fragment to the block.
const QgsTextFragment & at(int index) const
Returns the fragment at the specified index.
bool empty() const
Returns true if the block is empty.
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 ...
QSizeF imageSize() const
Returns the image size, if the format applies to a document image fragment.
QString imagePath() const
Returns the path to the image to render, if the format applies to a document image fragment.
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.
static QgsTextDocumentMetrics calculateMetrics(const QgsTextDocument &document, const QgsTextFormat &format, const QgsRenderContext &context, double scaleFactor=1.0, const QgsTextDocumentRenderContext &documentContext=QgsTextDocumentRenderContext())
Returns precalculated text metrics for a text document, when rendered using the given base format and...
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.
double fragmentFixedHeight(int blockIndex, int fragmentIndex, Qgis::TextLayoutMode mode) const
Returns the fixed height of the fragment at the specified block and fragment 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...
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.
const QgsTextDocument & document() const
Returns the document associated with the calculated metrics.
double blockWidth(int blockIndex) const
Returns the width of the block at the specified index.
Encapsulates the context in which a text document is to be rendered.
Qgis::TextRendererFlags flags() const
Returns associated text renderer flags.
double maximumWidth() const
Returns the maximum width (in painter units) for rendered text.
Represents a document consisting of one or more QgsTextBlock objects.
const QgsTextBlock & at(int index) const
Returns the block at the specified index.
void reserve(int count)
Reserves the specified count of blocks for optimised block appending.
int size() const
Returns the number of blocks in the document.
void append(const QgsTextBlock &block)
Appends a block to 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 document along with formatting overrides to be used when rendering the fragment.
void setText(const QString &text)
Sets the text content of the fragment.
QString text() const
Returns the text content of the fragment.
void setCharacterFormat(const QgsTextCharacterFormat &format)
Sets the character format for the fragment.
const QgsTextCharacterFormat & characterFormat() const
Returns the character formatting for the fragment.
bool isImage() const
Returns true if the fragment represents an image.
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
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
constexpr double SUPERSCRIPT_VERTICAL_BASELINE_ADJUSTMENT_FACTOR
constexpr double SUBSCRIPT_VERTICAL_BASELINE_ADJUSTMENT_FACTOR
double maxBlockFixedItemHeight
double maxBlockAscentForTextFragments
double blockHeightUsingLineSpacing
QList< QFont > fragmentFonts
QList< double > fragmentFixedHeights
QList< double > fragmentHorizontalAdvance
bool isFirstNonTabFragment
double blockHeightUsingAscentDescent
QFont previousNonSuperSubScriptFont
double blockHeightUsingAscentAccountingForVerticalOffset
QList< double > fragmentVerticalOffsets
double blockYMaxAdjustLabel
double blockHeightVerticalOrientation
double heightVerticalOrientation
double tabStopDistancePainterUnits
double heightCapHeightMode
double currentCapHeightBasedBaseline
double currentLabelBaseline
QVector< double > blockVerticalLineSpacing
double currentRectBaseline
double currentPointBaseline
double heightPointRectMode
double currentAscentBasedBaseline
double lineHeightPainterUnits