diff options
Diffstat (limited to 'src/gui/text/qfontmetrics.cpp')
-rw-r--r-- | src/gui/text/qfontmetrics.cpp | 324 |
1 files changed, 246 insertions, 78 deletions
diff --git a/src/gui/text/qfontmetrics.cpp b/src/gui/text/qfontmetrics.cpp index 8f3aa62842..f7e405f0b5 100644 --- a/src/gui/text/qfontmetrics.cpp +++ b/src/gui/text/qfontmetrics.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qfont.h" #include "qpaintdevice.h" @@ -111,15 +75,23 @@ extern void qt_format_text(const QFont& font, const QRectF &_r, inFont(). You can also treat the character as a string, and use the string functions on it. - The string functions include horizontalAdvance(), to return the width of a - string in pixels (or points, for a printer), boundingRect(), to - return a rectangle large enough to contain the rendered string, + The string functions include horizontalAdvance(), to return the advance + width of a string in pixels (or points, for a printer), boundingRect(), + to return a rectangle large enough to contain the rendered string, and size(), to return the size of that rectangle. + \note The advance width can be different from the width of the actual + rendered text. It refers to the distance from the origin of the string to + where you would append additional characters. As text may have overhang + (in the case of an italic font for instance) or padding between + characters, the advance width can be either smaller or larger than the + actual rendering of the text. This is called the right bearing of the + text. + Example: \snippet code/src_gui_text_qfontmetrics.cpp 0 - \sa QFont, QFontInfo, QFontDatabase, {Character Map Example} + \sa QFont, QFontInfo, QFontDatabase */ /*! @@ -261,9 +233,8 @@ bool QFontMetrics::operator ==(const QFontMetrics &other) const The ascent of a font is the distance from the baseline to the highest position characters extend to. In practice, some font designers break this rule, e.g. when they put more than one accent - on top of a character, or to accommodate an unusual character in - an exotic language, so it is possible (though rare) that this - value will be too small. + on top of a character, or to accommodate a certain character, so it + is possible (though rare) that this value will be too small. \sa descent() */ @@ -298,8 +269,8 @@ int QFontMetrics::capHeight() const The descent is the distance from the base line to the lowest point characters extend to. In practice, some font designers break this rule, - e.g. to accommodate an unusual character in an exotic language, so - it is possible (though rare) that this value will be too small. + e.g. to accommodate a certain character, so it is possible (though + rare) that this value will be too small. \sa ascent() */ @@ -510,10 +481,13 @@ int QFontMetrics::rightBearing(QChar ch) const return qRound(rb); } +static constexpr QLatin1Char s_variableLengthStringSeparator('\x9c'); + /*! Returns the horizontal advance in pixels of the first \a len characters of \a text. If \a len is negative (the default), the entire string is - used. + used. The entire length of \a text is analysed even if \a len is substantially + shorter. This is the distance appropriate for drawing a subsequent character after \a text. @@ -524,16 +498,45 @@ int QFontMetrics::rightBearing(QChar ch) const */ int QFontMetrics::horizontalAdvance(const QString &text, int len) const { - int pos = text.indexOf(QLatin1Char('\x9c')); + int pos = (len >= 0) + ? QStringView(text).left(len).indexOf(s_variableLengthStringSeparator) + : text.indexOf(s_variableLengthStringSeparator); if (pos != -1) { - len = (len < 0) ? pos : qMin(pos, len); + len = pos; } else if (len < 0) { - len = text.length(); + len = text.size(); + } + if (len == 0) + return 0; + + QStackTextEngine layout(text, QFont(d.data())); + 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(s_variableLengthStringSeparator); + int len = -1; + if (pos != -1) { + len = pos; + } else { + len = text.size(); } if (len == 0) return 0; QStackTextEngine layout(text, QFont(d.data())); + layout.option = option; return qRound(layout.width(0, len)); } @@ -614,12 +617,48 @@ int QFontMetrics::horizontalAdvance(QChar ch) const */ QRect QFontMetrics::boundingRect(const QString &text) const { - if (text.length() == 0) + if (text.size() == 0) + return QRect(); + + QStackTextEngine layout(text, QFont(d.data())); + layout.itemize(); + glyph_metrics_t gm = layout.boundingBox(0, text.size()); + 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.size() == 0) return QRect(); QStackTextEngine layout(text, QFont(d.data())); + layout.option = option; layout.itemize(); - glyph_metrics_t gm = layout.boundingBox(0, text.length()); + glyph_metrics_t gm = layout.boundingBox(0, text.size()); return QRect(qRound(gm.x), qRound(gm.y), qRound(gm.width), qRound(gm.height)); } @@ -759,8 +798,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, @@ -777,21 +814,53 @@ 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() */ QRect QFontMetrics::tightBoundingRect(const QString &text) const { - if (text.length() == 0) + if (text.size() == 0) return QRect(); QStackTextEngine layout(text, QFont(d.data())); layout.itemize(); - glyph_metrics_t gm = layout.tightBoundingBox(0, text.length()); + glyph_metrics_t gm = layout.tightBoundingBox(0, text.size()); 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.size() == 0) + return QRect(); + + QStackTextEngine layout(text, QFont(d.data())); + layout.option = option; + layout.itemize(); + glyph_metrics_t gm = layout.tightBoundingBox(0, text.size()); + return QRect(qRound(gm.x), qRound(gm.y), qRound(gm.width), qRound(gm.height)); +} /*! \since 4.2 @@ -820,13 +889,13 @@ QString QFontMetrics::elidedText(const QString &text, Qt::TextElideMode mode, in QString _text = text; if (!(flags & Qt::TextLongestVariant)) { int posA = 0; - int posB = _text.indexOf(QLatin1Char('\x9c')); + int posB = _text.indexOf(s_variableLengthStringSeparator); while (posB >= 0) { QString portion = _text.mid(posA, posB - posA); if (size(flags, portion).width() <= width) return portion; posA = posB + 1; - posB = _text.indexOf(QLatin1Char('\x9c'), posA); + posB = _text.indexOf(s_variableLengthStringSeparator, posA); } _text = _text.mid(posA); } @@ -1075,9 +1144,8 @@ bool QFontMetricsF::operator ==(const QFontMetricsF &other) const The ascent of a font is the distance from the baseline to the highest position characters extend to. In practice, some font designers break this rule, e.g. when they put more than one accent - on top of a character, or to accommodate an unusual character in - an exotic language, so it is possible (though rare) that this - value will be too small. + on top of a character, or to accommodate a certain character, so + it is possible (though rare) that this value will be too small. \sa descent() */ @@ -1113,8 +1181,8 @@ qreal QFontMetricsF::capHeight() const The descent is the distance from the base line to the lowest point characters extend to. (Note that this is different from X, which adds 1 pixel.) In practice, some font designers break this rule, - e.g. to accommodate an unusual character in an exotic language, so - it is possible (though rare) that this value will be too small. + e.g. to accommodate a certain character, so it is possible (though + rare) that this value will be too small. \sa ascent() */ @@ -1332,7 +1400,8 @@ qreal QFontMetricsF::rightBearing(QChar ch) const /*! Returns the horizontal advance in pixels of the first \a length characters of \a text. If \a length is negative (the default), the entire string is - used. + used. The entire length of \a text is analysed even if \a length is substantially + shorter. The advance is the distance appropriate for drawing a subsequent character after \a text. @@ -1343,16 +1412,46 @@ qreal QFontMetricsF::rightBearing(QChar ch) const */ qreal QFontMetricsF::horizontalAdvance(const QString &text, int length) const { - int pos = text.indexOf(QLatin1Char('\x9c')); + int pos = (length >= 0) + ? QStringView(text).left(length).indexOf(s_variableLengthStringSeparator) + : text.indexOf(s_variableLengthStringSeparator); if (pos != -1) - length = (length < 0) ? pos : qMin(pos, length); + length = pos; else if (length < 0) - length = text.length(); + length = text.size(); + + if (length == 0) + return 0; + + QStackTextEngine layout(text, QFont(d.data())); + layout.itemize(); + 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(s_variableLengthStringSeparator); + int length = -1; + if (pos != -1) + length = pos; + else + length = text.size(); if (length == 0) return 0; QStackTextEngine layout(text, QFont(d.data())); + layout.option = option; layout.itemize(); return layout.width(0, length).toReal(); } @@ -1433,7 +1532,7 @@ qreal QFontMetricsF::horizontalAdvance(QChar ch) const */ QRectF QFontMetricsF::boundingRect(const QString &text) const { - int len = text.length(); + int len = text.size(); if (len == 0) return QRectF(); @@ -1445,6 +1544,42 @@ QRectF QFontMetricsF::boundingRect(const QString &text) const } /*! + 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.size() == 0) + return QRectF(); + + QStackTextEngine layout(text, QFont(d.data())); + layout.option = option; + layout.itemize(); + glyph_metrics_t gm = layout.boundingBox(0, text.size()); + 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. @@ -1480,7 +1615,9 @@ QRectF QFontMetricsF::boundingRect(QChar ch) const Returns the bounding rectangle of the characters in the given \a text. This is the set of pixels the text would cover if drawn when constrained - to the bounding rectangle specified by \a rect. + to the bounding rectangle specified by \a rect. If \a rect is a reference + to a \nullptr object, e.g. when passing a default constructed QRectF, the + bounding rectangle will not constrain itself to the size. The \a flags argument is the bitwise OR of the following flags: \list @@ -1600,18 +1737,49 @@ 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(); + if (text.size() == 0) + return QRectF(); + + QStackTextEngine layout(text, QFont(d.data())); + layout.itemize(); + glyph_metrics_t gm = layout.tightBoundingBox(0, text.size()); + 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.size() == 0) + return QRectF(); QStackTextEngine layout(text, QFont(d.data())); + layout.option = option; layout.itemize(); - glyph_metrics_t gm = layout.tightBoundingBox(0, text.length()); + glyph_metrics_t gm = layout.tightBoundingBox(0, text.size()); return QRectF(gm.x.toReal(), gm.y.toReal(), gm.width.toReal(), gm.height.toReal()); } @@ -1641,13 +1809,13 @@ QString QFontMetricsF::elidedText(const QString &text, Qt::TextElideMode mode, q QString _text = text; if (!(flags & Qt::TextLongestVariant)) { int posA = 0; - int posB = _text.indexOf(QLatin1Char('\x9c')); + int posB = _text.indexOf(s_variableLengthStringSeparator); while (posB >= 0) { QString portion = _text.mid(posA, posB - posA); if (size(flags, portion).width() <= width) return portion; posA = posB + 1; - posB = _text.indexOf(QLatin1Char('\x9c'), posA); + posB = _text.indexOf(s_variableLengthStringSeparator, posA); } _text = _text.mid(posA); } |