From c1a5f08ba055edc1032d3ca3bcaa978fd14234e9 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Wed, 13 Oct 2021 15:55:49 +0200 Subject: [PATCH] Add QFontMetricsF methods taking text options into account This is needed by Qt Quick to be able to calculate font metrics using design metrics matching Quick text rendering. Task-number: QTBUG-85936 Task-number: QTBUG-94023 Change-Id: I67c74e2a912bd58df7a57349a858f20f04609f0f Reviewed-by: Lars Knoll --- src/gui/text/qfontmetrics.cpp | 200 +++++++++++++++++++++++++++++++++- src/gui/text/qfontmetrics.h | 7 ++ 2 files changed, 201 insertions(+), 6 deletions(-) diff --git a/src/gui/text/qfontmetrics.cpp b/src/gui/text/qfontmetrics.cpp index 42c17ced58..f5018539c3 100644 --- a/src/gui/text/qfontmetrics.cpp +++ b/src/gui/text/qfontmetrics.cpp @@ -536,6 +536,33 @@ int QFontMetrics::horizontalAdvance(const QString &text, int len) const return qRound(layout.width(0, len)); } +/*! + Returns the horizontal advance in pixels of \a text laid out using \a option. + + The advance is the distance appropriate for drawing a subsequent + character after \a text. + + \since 6.3 + + \sa boundingRect() +*/ +int QFontMetrics::horizontalAdvance(const QString &text, const QTextOption &option) const +{ + int pos = text.indexOf(QLatin1Char('\x9c')); + int len = -1; + if (pos != -1) { + len = (len < 0) ? pos : qMin(pos, len); + } else if (len < 0) { + len = text.length(); + } + if (len == 0) + return 0; + + QStackTextEngine layout(text, QFont(d.data())); + layout.option = option; + return qRound(layout.width(0, len)); +} + /*! \overload @@ -622,6 +649,42 @@ QRect QFontMetrics::boundingRect(const QString &text) const return QRect(qRound(gm.x), qRound(gm.y), qRound(gm.width), qRound(gm.height)); } +/*! + Returns the bounding rectangle of the characters in the string + specified by \a text laid out using \a option. The bounding rectangle always + covers at least the set of pixels the text would cover if drawn at (0, 0). + + Note that the bounding rectangle may extend to the left of (0, 0), + e.g. for italicized fonts, and that the width of the returned + rectangle might be different than what the horizontalAdvance() method + returns. + + If you want to know the advance width of the string (to lay out + a set of strings next to each other), use horizontalAdvance() instead. + + Newline characters are processed as normal characters, \e not as + linebreaks. + + The height of the bounding rectangle is at least as large as the + value returned by height(). + + \since 6.3 + + \sa horizontalAdvance(), height(), QPainter::boundingRect(), + tightBoundingRect() +*/ +QRect QFontMetrics::boundingRect(const QString &text, const QTextOption &option) const +{ + if (text.length() == 0) + return QRect(); + + QStackTextEngine layout(text, QFont(d.data())); + layout.option = option; + layout.itemize(); + glyph_metrics_t gm = layout.boundingBox(0, text.length()); + return QRect(qRound(gm.x), qRound(gm.y), qRound(gm.width), qRound(gm.height)); +} + /*! Returns the rectangle that is covered by ink if character \a ch were to be drawn at the origin of the coordinate system. @@ -758,8 +821,6 @@ QSize QFontMetrics::size(int flags, const QString &text, int tabStops, int *tabA } /*! - \since 4.3 - Returns a tight bounding rectangle around the characters in the string specified by \a text. The bounding rectangle always covers at least the set of pixels the text would cover if drawn at (0, @@ -776,7 +837,7 @@ QSize QFontMetrics::size(int flags, const QString &text, int tabStops, int *tabA Newline characters are processed as normal characters, \e not as linebreaks. - \warning Calling this method is very slow on Windows. + \since 4.3 \sa horizontalAdvance(), height(), boundingRect() */ @@ -791,6 +852,38 @@ QRect QFontMetrics::tightBoundingRect(const QString &text) const return QRect(qRound(gm.x), qRound(gm.y), qRound(gm.width), qRound(gm.height)); } +/*! + Returns a tight bounding rectangle around the characters in the + string specified by \a text laid out using \a option. The bounding + rectangle always covers at least the set of pixels the text would + cover if drawn at (0, 0). + + Note that the bounding rectangle may extend to the left of (0, 0), + e.g. for italicized fonts, and that the width of the returned + rectangle might be different than what the horizontalAdvance() method + returns. + + If you want to know the advance width of the string (to lay out + a set of strings next to each other), use horizontalAdvance() instead. + + Newline characters are processed as normal characters, \e not as + linebreaks. + + \since 6.3 + + \sa horizontalAdvance(), height(), boundingRect() +*/ +QRect QFontMetrics::tightBoundingRect(const QString &text, const QTextOption &option) const +{ + if (text.length() == 0) + return QRect(); + + QStackTextEngine layout(text, QFont(d.data())); + layout.option = option; + layout.itemize(); + glyph_metrics_t gm = layout.tightBoundingBox(0, text.length()); + return QRect(qRound(gm.x), qRound(gm.y), qRound(gm.width), qRound(gm.height)); +} /*! \since 4.2 @@ -1355,6 +1448,34 @@ qreal QFontMetricsF::horizontalAdvance(const QString &text, int length) const return layout.width(0, length).toReal(); } +/*! + Returns the horizontal advance in pixels of \a text laid out using \a option. + + The advance is the distance appropriate for drawing a subsequent + character after \a text. + + \since 6.3 + + \sa boundingRect() +*/ +qreal QFontMetricsF::horizontalAdvance(const QString &text, const QTextOption &option) const +{ + int pos = text.indexOf(QLatin1Char('\x9c')); + int length = -1; + if (pos != -1) + length = (length < 0) ? pos : qMin(pos, length); + else if (length < 0) + length = text.length(); + + if (length == 0) + return 0; + + QStackTextEngine layout(text, QFont(d.data())); + layout.option = option; + layout.itemize(); + return layout.width(0, length).toReal(); +} + /*! \overload @@ -1442,6 +1563,42 @@ QRectF QFontMetricsF::boundingRect(const QString &text) const gm.width.toReal(), gm.height.toReal()); } +/*! + Returns the bounding rectangle of the characters in the string + specified by \a text laid out using \a option. The bounding + rectangle always covers at least the set of pixels the text + would cover if drawn at (0, 0). + + Note that the bounding rectangle may extend to the left of (0, 0), + e.g. for italicized fonts, and that the width of the returned + rectangle might be different than what the horizontalAdvance() method returns. + + If you want to know the advance width of the string (to lay out + a set of strings next to each other), use horizontalAdvance() instead. + + Newline characters are processed as normal characters, \e not as + linebreaks. + + The height of the bounding rectangle is at least as large as the + value returned height(). + + \since 6.3 + \sa horizontalAdvance(), height(), QPainter::boundingRect() +*/ +QRectF QFontMetricsF::boundingRect(const QString &text, const QTextOption &option) const +{ + if (text.length() == 0) + return QRectF(); + + QStackTextEngine layout(text, QFont(d.data())); + layout.option = option; + layout.itemize(); + glyph_metrics_t gm = layout.boundingBox(0, text.length()); + return QRectF(gm.x.toReal(), gm.y.toReal(), + gm.width.toReal(), gm.height.toReal()); +} + + /*! Returns the bounding rectangle of the character \a ch relative to the left-most point on the base line. @@ -1600,14 +1757,12 @@ QSizeF QFontMetricsF::size(int flags, const QString &text, int tabStops, int *ta Newline characters are processed as normal characters, \e not as linebreaks. - \warning Calling this method is very slow on Windows. - \sa horizontalAdvance(), height(), boundingRect() */ QRectF QFontMetricsF::tightBoundingRect(const QString &text) const { if (text.length() == 0) - return QRect(); + return QRectF(); QStackTextEngine layout(text, QFont(d.data())); layout.itemize(); @@ -1615,6 +1770,39 @@ QRectF QFontMetricsF::tightBoundingRect(const QString &text) const return QRectF(gm.x.toReal(), gm.y.toReal(), gm.width.toReal(), gm.height.toReal()); } +/*! + Returns a tight bounding rectangle around the characters in the + string specified by \a text laid out using \a option. The bounding + rectangle always covers at least the set of pixels the text would + cover if drawn at (0,0). + + Note that the bounding rectangle may extend to the left of (0, 0), + e.g. for italicized fonts, and that the width of the returned + rectangle might be different than what the horizontalAdvance() method + returns. + + If you want to know the advance width of the string (to lay out + a set of strings next to each other), use horizontalAdvance() instead. + + Newline characters are processed as normal characters, \e not as + linebreaks. + + \since 6.3 + + \sa horizontalAdvance(), height(), boundingRect() +*/ +QRectF QFontMetricsF::tightBoundingRect(const QString &text, const QTextOption &option) const +{ + if (text.length() == 0) + return QRectF(); + + QStackTextEngine layout(text, QFont(d.data())); + layout.option = option; + layout.itemize(); + glyph_metrics_t gm = layout.tightBoundingBox(0, text.length()); + return QRectF(gm.x.toReal(), gm.y.toReal(), gm.width.toReal(), gm.height.toReal()); +} + /*! \since 4.2 diff --git a/src/gui/text/qfontmetrics.h b/src/gui/text/qfontmetrics.h index a199e20373..cd438fff7c 100644 --- a/src/gui/text/qfontmetrics.h +++ b/src/gui/text/qfontmetrics.h @@ -50,6 +50,7 @@ QT_BEGIN_NAMESPACE class QRect; +class QTextOption; class Q_GUI_EXPORT QFontMetrics { @@ -85,11 +86,13 @@ public: int rightBearing(QChar) const; int horizontalAdvance(const QString &, int len = -1) const; + int horizontalAdvance(const QString &, const QTextOption &textOption) const; int horizontalAdvance(QChar) const; QRect boundingRect(QChar) const; QRect boundingRect(const QString &text) const; + QRect boundingRect(const QString &text, const QTextOption &textOption) const; QRect boundingRect(const QRect &r, int flags, const QString &text, int tabstops = 0, int *tabarray = nullptr) const; inline QRect boundingRect(int x, int y, int w, int h, int flags, const QString &text, int tabstops = 0, int *tabarray = nullptr) const @@ -97,6 +100,7 @@ public: QSize size(int flags, const QString& str, int tabstops = 0, int *tabarray = nullptr) const; QRect tightBoundingRect(const QString &text) const; + QRect tightBoundingRect(const QString &text, const QTextOption &textOption) const; QString elidedText(const QString &text, Qt::TextElideMode mode, int width, int flags = 0) const; @@ -155,13 +159,16 @@ public: qreal horizontalAdvance(const QString &string, int length = -1) const; qreal horizontalAdvance(QChar) const; + qreal horizontalAdvance(const QString &string, const QTextOption &textOption) const; QRectF boundingRect(const QString &string) const; + QRectF boundingRect(const QString &text, const QTextOption &textOption) const; QRectF boundingRect(QChar) const; QRectF boundingRect(const QRectF &r, int flags, const QString& string, int tabstops = 0, int *tabarray = nullptr) const; QSizeF size(int flags, const QString& str, int tabstops = 0, int *tabarray = nullptr) const; QRectF tightBoundingRect(const QString &text) const; + QRectF tightBoundingRect(const QString &text, const QTextOption &textOption) const; QString elidedText(const QString &text, Qt::TextElideMode mode, qreal width, int flags = 0) const;