summaryrefslogtreecommitdiffstats
path: root/src/gui/text/qtextengine.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/text/qtextengine.cpp')
-rw-r--r--src/gui/text/qtextengine.cpp366
1 files changed, 194 insertions, 172 deletions
diff --git a/src/gui/text/qtextengine.cpp b/src/gui/text/qtextengine.cpp
index 9e294d083e..cb945b73ce 100644
--- a/src/gui/text/qtextengine.cpp
+++ b/src/gui/text/qtextengine.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 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) 2021 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 <QtGui/private/qtguiglobal_p.h>
#include "qdebug.h"
@@ -43,6 +7,7 @@
#include "qtextformat_p.h"
#include "qtextengine_p.h"
#include "qabstracttextdocumentlayout.h"
+#include "qabstracttextdocumentlayout_p.h"
#include "qtextlayout.h"
#include "qtextboundaryfinder.h"
#include <QtCore/private/qunicodetables_p.h>
@@ -71,15 +36,10 @@ public:
Itemizer(const QString &string, const QScriptAnalysis *analysis, QScriptItemArray &items)
: m_string(string),
m_analysis(analysis),
- m_items(items),
- m_splitter(nullptr)
+ m_items(items)
{
}
- ~Itemizer()
- {
- delete m_splitter;
- }
-
+ ~Itemizer() = default;
/// generate the script items
/// The caps parameter is used to choose the algorithm of splitting text and assigning roles to the textitems
void generate(int start, int length, QFont::Capitalization caps)
@@ -120,7 +80,7 @@ private:
for (int i = start + 1; i < end; ++i) {
if (m_analysis[i].bidiLevel == m_analysis[start].bidiLevel
&& m_analysis[i].flags == m_analysis[start].flags
- && (m_analysis[i].script == m_analysis[start].script || m_string[i] == QLatin1Char('.'))
+ && (m_analysis[i].script == m_analysis[start].script || m_string[i] == u'.')
&& m_analysis[i].flags < QScriptAnalysis::SpaceTabOrObject
&& i - start < MaxItemLength)
continue;
@@ -136,8 +96,8 @@ private:
return;
if (!m_splitter)
- m_splitter = new QTextBoundaryFinder(QTextBoundaryFinder::Word,
- m_string.constData(), m_string.length(),
+ m_splitter = std::make_unique<QTextBoundaryFinder>(QTextBoundaryFinder::Word,
+ m_string.constData(), m_string.size(),
/*buffer*/nullptr, /*buffer size*/0);
m_splitter->setPosition(start);
@@ -206,7 +166,7 @@ private:
const QString &m_string;
const QScriptAnalysis * const m_analysis;
QScriptItemArray &m_items;
- QTextBoundaryFinder *m_splitter;
+ std::unique_ptr<QTextBoundaryFinder> m_splitter;
};
// -----------------------------------------------------------------------------------------------------
@@ -1247,9 +1207,9 @@ enum JustificationClass {
Adds an inter character justification opportunity after the number or letter
character and a space justification opportunity after the space character.
*/
-static inline void qt_getDefaultJustificationOpportunities(const ushort *string, int length, const QGlyphLayout &g, ushort *log_clusters, int spaceAs)
+static inline void qt_getDefaultJustificationOpportunities(const ushort *string, qsizetype length, const QGlyphLayout &g, ushort *log_clusters, int spaceAs)
{
- int str_pos = 0;
+ qsizetype str_pos = 0;
while (str_pos < length) {
int glyph_pos = log_clusters[str_pos];
@@ -1281,7 +1241,7 @@ static inline void qt_getDefaultJustificationOpportunities(const ushort *string,
}
}
-static inline void qt_getJustificationOpportunities(const ushort *string, int length, const QScriptItem &si, const QGlyphLayout &g, ushort *log_clusters)
+static inline void qt_getJustificationOpportunities(const ushort *string, qsizetype length, const QScriptItem &si, const QGlyphLayout &g, ushort *log_clusters)
{
Q_ASSERT(length > 0 && g.numGlyphs > 0);
@@ -1354,10 +1314,6 @@ void QTextEngine::shapeLine(const QScriptLine &line)
}
}
-#if QT_CONFIG(harfbuzz)
-extern bool qt_useHarfbuzzNG(); // defined in qfontengine.cpp
-#endif
-
static void applyVisibilityRules(ushort ucs, QGlyphLayout *glyphs, uint glyphPosition, QFontEngine *fontEngine)
{
// hide characters that should normally be invisible
@@ -1435,36 +1391,42 @@ void QTextEngine::shapeText(int item) const
}
if (Q_UNLIKELY(!ensureSpace(itemLength))) {
- Q_UNREACHABLE(); // ### report OOM error somehow
- return;
+ Q_UNREACHABLE_RETURN(); // ### report OOM error somehow
}
QFontEngine *fontEngine = this->fontEngine(si, &si.ascent, &si.descent, &si.leading);
bool kerningEnabled;
bool letterSpacingIsAbsolute;
- bool shapingEnabled;
+ bool shapingEnabled = false;
+ QHash<QFont::Tag, quint32> features;
QFixed letterSpacing, wordSpacing;
#ifndef QT_NO_RAWFONT
if (useRawFont) {
QTextCharFormat f = format(&si);
QFont font = f.font();
kerningEnabled = font.kerning();
+# if QT_CONFIG(harfbuzz)
shapingEnabled = QFontEngine::scriptRequiresOpenType(QChar::Script(si.analysis.script))
|| (font.styleStrategy() & QFont::PreferNoShaping) == 0;
+# endif
wordSpacing = QFixed::fromReal(font.wordSpacing());
letterSpacing = QFixed::fromReal(font.letterSpacing());
letterSpacingIsAbsolute = true;
+ features = font.d->features;
} else
#endif
{
QFont font = this->font(si);
kerningEnabled = font.d->kerning;
+#if QT_CONFIG(harfbuzz)
shapingEnabled = QFontEngine::scriptRequiresOpenType(QChar::Script(si.analysis.script))
|| (font.d->request.styleStrategy & QFont::PreferNoShaping) == 0;
+#endif
letterSpacingIsAbsolute = font.d->letterSpacingIsAbsolute;
letterSpacing = font.d->letterSpacing;
wordSpacing = font.d->wordSpacing;
+ features = font.d->features;
if (letterSpacingIsAbsolute && letterSpacing.value())
letterSpacing *= font.d->dpi / qt_defaultDpiY();
@@ -1472,8 +1434,7 @@ void QTextEngine::shapeText(int item) const
// split up the item into parts that come from different font engines
// k * 3 entries, array[k] == index in string, array[k + 1] == index in glyphs, array[k + 2] == engine index
- QList<uint> itemBoundaries;
- itemBoundaries.reserve(24);
+ QVarLengthArray<uint, 24> itemBoundaries;
QGlyphLayout initialGlyphs = availableGlyphs(&si);
int nGlyphs = initialGlyphs.numGlyphs;
@@ -1493,9 +1454,9 @@ void QTextEngine::shapeText(int item) const
for (int i = 0, glyph_pos = 0; i < itemLength; ++i, ++glyph_pos) {
const uint engineIdx = initialGlyphs.glyphs[glyph_pos] >> 24;
if (lastEngine != engineIdx) {
- itemBoundaries.append(i);
- itemBoundaries.append(glyph_pos);
- itemBoundaries.append(engineIdx);
+ itemBoundaries.push_back(i);
+ itemBoundaries.push_back(glyph_pos);
+ itemBoundaries.push_back(engineIdx);
if (engineIdx != 0) {
QFontEngine *actualFontEngine = static_cast<QFontEngineMulti *>(fontEngine)->engine(engineIdx);
@@ -1511,14 +1472,21 @@ void QTextEngine::shapeText(int item) const
++i;
}
} else {
- itemBoundaries.append(0);
- itemBoundaries.append(0);
- itemBoundaries.append(0);
+ itemBoundaries.push_back(0);
+ itemBoundaries.push_back(0);
+ itemBoundaries.push_back(0);
}
#if QT_CONFIG(harfbuzz)
- if (Q_LIKELY(shapingEnabled && qt_useHarfbuzzNG())) {
- si.num_glyphs = shapeTextWithHarfbuzzNG(si, string, itemLength, fontEngine, itemBoundaries, kerningEnabled, letterSpacing != 0);
+ if (Q_LIKELY(shapingEnabled)) {
+ si.num_glyphs = shapeTextWithHarfbuzzNG(si,
+ string,
+ itemLength,
+ fontEngine,
+ itemBoundaries,
+ kerningEnabled,
+ letterSpacing != 0,
+ features);
} else
#endif
{
@@ -1578,8 +1546,7 @@ void QTextEngine::shapeText(int item) const
QGlyphLayout glyphs = shapedGlyphs(&si);
#if QT_CONFIG(harfbuzz)
- if (Q_LIKELY(qt_useHarfbuzzNG()))
- qt_getJustificationOpportunities(string, itemLength, si, glyphs, logClusters(&si));
+ qt_getJustificationOpportunities(string, itemLength, si, glyphs, logClusters(&si));
#endif
if (letterSpacing != 0) {
@@ -1629,9 +1596,10 @@ int QTextEngine::shapeTextWithHarfbuzzNG(const QScriptItem &si,
const ushort *string,
int itemLength,
QFontEngine *fontEngine,
- const QList<uint> &itemBoundaries,
+ QSpan<uint> itemBoundaries,
bool kerningEnabled,
- bool hasLetterSpacing) const
+ bool hasLetterSpacing,
+ const QHash<QFont::Tag, quint32> &fontFeatures) const
{
uint glyphs_shaped = 0;
@@ -1650,7 +1618,7 @@ int QTextEngine::shapeTextWithHarfbuzzNG(const QScriptItem &si,
// ### TODO get_default_for_script?
props.language = hb_language_get_default(); // use default language from locale
- for (int k = 0; k < itemBoundaries.size(); k += 3) {
+ for (qsizetype k = 0; k < itemBoundaries.size(); k += 3) {
const uint item_pos = itemBoundaries[k];
const uint item_length = (k + 4 < itemBoundaries.size() ? itemBoundaries[k + 3] : itemLength) - item_pos;
const uint engineIdx = itemBoundaries[k + 2];
@@ -1685,14 +1653,24 @@ int QTextEngine::shapeTextWithHarfbuzzNG(const QScriptItem &si,
|| script == QChar::Script_Khmer || script == QChar::Script_Nko);
bool dontLigate = hasLetterSpacing && !scriptRequiresOpenType;
- const hb_feature_t features[5] = {
- { HB_TAG('k','e','r','n'), !!kerningEnabled, HB_FEATURE_GLOBAL_START, HB_FEATURE_GLOBAL_END },
- { HB_TAG('l','i','g','a'), false, HB_FEATURE_GLOBAL_START, HB_FEATURE_GLOBAL_END },
- { HB_TAG('c','l','i','g'), false, HB_FEATURE_GLOBAL_START, HB_FEATURE_GLOBAL_END },
- { HB_TAG('d','l','i','g'), false, HB_FEATURE_GLOBAL_START, HB_FEATURE_GLOBAL_END },
- { HB_TAG('h','l','i','g'), false, HB_FEATURE_GLOBAL_START, HB_FEATURE_GLOBAL_END }
- };
- const int num_features = dontLigate ? 5 : 1;
+
+ QHash<QFont::Tag, quint32> features;
+ features.insert(QFont::Tag("kern"), !!kerningEnabled);
+ if (dontLigate) {
+ features.insert(QFont::Tag("liga"), false);
+ features.insert(QFont::Tag("clig"), false);
+ features.insert(QFont::Tag("dlig"), false);
+ features.insert(QFont::Tag("hlig"), false);
+ }
+ features.insert(fontFeatures);
+
+ QVarLengthArray<hb_feature_t, 16> featureArray;
+ for (auto it = features.constBegin(); it != features.constEnd(); ++it) {
+ featureArray.append({ it.key().value(),
+ it.value(),
+ HB_FEATURE_GLOBAL_START,
+ HB_FEATURE_GLOBAL_END });
+ }
// whitelist cross-platforms shapers only
static const char *shaper_list[] = {
@@ -1702,7 +1680,11 @@ int QTextEngine::shapeTextWithHarfbuzzNG(const QScriptItem &si,
nullptr
};
- bool shapedOk = hb_shape_full(hb_font, buffer, features, num_features, shaper_list);
+ bool shapedOk = hb_shape_full(hb_font,
+ buffer,
+ featureArray.constData(),
+ features.size(),
+ shaper_list);
if (Q_UNLIKELY(!shapedOk)) {
hb_buffer_destroy(buffer);
return 0;
@@ -1712,9 +1694,14 @@ int QTextEngine::shapeTextWithHarfbuzzNG(const QScriptItem &si,
hb_buffer_reverse(buffer);
}
- const uint num_glyphs = hb_buffer_get_length(buffer);
+ uint num_glyphs = hb_buffer_get_length(buffer);
+ const bool has_glyphs = num_glyphs > 0;
+ // If Harfbuzz returns zero glyphs, we have to manually add a missing glyph
+ if (Q_UNLIKELY(!has_glyphs))
+ num_glyphs = 1;
+
// ensure we have enough space for shaped glyphs and metrics
- if (Q_UNLIKELY(num_glyphs == 0 || !ensureSpace(glyphs_shaped + num_glyphs))) {
+ if (Q_UNLIKELY(!ensureSpace(glyphs_shaped + num_glyphs))) {
hb_buffer_destroy(buffer);
return 0;
}
@@ -1722,35 +1709,44 @@ int QTextEngine::shapeTextWithHarfbuzzNG(const QScriptItem &si,
// fetch the shaped glyphs and metrics
QGlyphLayout g = availableGlyphs(&si).mid(glyphs_shaped, num_glyphs);
ushort *log_clusters = logClusters(&si) + item_pos;
-
- hb_glyph_info_t *infos = hb_buffer_get_glyph_infos(buffer, nullptr);
- hb_glyph_position_t *positions = hb_buffer_get_glyph_positions(buffer, nullptr);
- uint str_pos = 0;
- uint last_cluster = ~0u;
- uint last_glyph_pos = glyphs_shaped;
- for (uint i = 0; i < num_glyphs; ++i, ++infos, ++positions) {
- g.glyphs[i] = infos->codepoint;
-
- g.advances[i] = QFixed::fromFixed(positions->x_advance);
- g.offsets[i].x = QFixed::fromFixed(positions->x_offset);
- g.offsets[i].y = QFixed::fromFixed(positions->y_offset);
-
- uint cluster = infos->cluster;
- if (Q_LIKELY(last_cluster != cluster)) {
- g.attributes[i].clusterStart = true;
-
- // fix up clusters so that the cluster indices will be monotonic
- // and thus we never return out-of-order indices
- while (last_cluster++ < cluster && str_pos < item_length)
- log_clusters[str_pos++] = last_glyph_pos;
- last_glyph_pos = i + glyphs_shaped;
- last_cluster = cluster;
-
- applyVisibilityRules(string[item_pos + str_pos], &g, i, actualFontEngine);
+ if (Q_LIKELY(has_glyphs)) {
+ hb_glyph_info_t *infos = hb_buffer_get_glyph_infos(buffer, nullptr);
+ hb_glyph_position_t *positions = hb_buffer_get_glyph_positions(buffer, nullptr);
+ uint str_pos = 0;
+ uint last_cluster = ~0u;
+ uint last_glyph_pos = glyphs_shaped;
+ for (uint i = 0; i < num_glyphs; ++i, ++infos, ++positions) {
+ g.glyphs[i] = infos->codepoint;
+
+ g.advances[i] = QFixed::fromFixed(positions->x_advance);
+ g.offsets[i].x = QFixed::fromFixed(positions->x_offset);
+ g.offsets[i].y = QFixed::fromFixed(positions->y_offset);
+
+ uint cluster = infos->cluster;
+ if (Q_LIKELY(last_cluster != cluster)) {
+ g.attributes[i].clusterStart = true;
+
+ // fix up clusters so that the cluster indices will be monotonic
+ // and thus we never return out-of-order indices
+ while (last_cluster++ < cluster && str_pos < item_length)
+ log_clusters[str_pos++] = last_glyph_pos;
+ last_glyph_pos = i + glyphs_shaped;
+ last_cluster = cluster;
+
+ applyVisibilityRules(string[item_pos + str_pos], &g, i, actualFontEngine);
+ }
}
+ while (str_pos < item_length)
+ log_clusters[str_pos++] = last_glyph_pos;
+ } else { // Harfbuzz did not return a glyph for the character, so we add a placeholder
+ g.glyphs[0] = 0;
+ g.advances[0] = QFixed{};
+ g.offsets[0].x = QFixed{};
+ g.offsets[0].y = QFixed{};
+ g.attributes[0].clusterStart = true;
+ g.attributes[0].dontPrint = true;
+ log_clusters[0] = glyphs_shaped;
}
- while (str_pos < item_length)
- log_clusters[str_pos++] = last_glyph_pos;
if (Q_UNLIKELY(engineIdx != 0)) {
for (quint32 i = 0; i < num_glyphs; ++i)
@@ -1758,8 +1754,10 @@ int QTextEngine::shapeTextWithHarfbuzzNG(const QScriptItem &si,
}
if (!actualFontEngine->supportsHorizontalSubPixelPositions()) {
- for (uint i = 0; i < num_glyphs; ++i)
+ for (uint i = 0; i < num_glyphs; ++i) {
g.advances[i] = g.advances[i].round();
+ g.offsets[i].x = g.offsets[i].x.round();
+ }
}
glyphs_shaped += num_glyphs;
@@ -1818,7 +1816,7 @@ const QCharAttributes *QTextEngine::attributes() const
return (QCharAttributes *) layoutData->memory;
itemize();
- if (! ensureSpace(layoutData->string.length()))
+ if (! ensureSpace(layoutData->string.size()))
return nullptr;
QVarLengthArray<QUnicodeTools::ScriptItem> scriptItems(layoutData->items.size());
@@ -1925,7 +1923,7 @@ void QTextEngine::itemize() const
if (layoutData->items.size())
return;
- int length = layoutData->string.length();
+ int length = layoutData->string.size();
if (!length)
return;
@@ -1942,9 +1940,9 @@ void QTextEngine::itemize() const
{
QUnicodeTools::ScriptItemArray scriptItems;
QUnicodeTools::initScripts(layoutData->string, &scriptItems);
- for (int i = 0; i < scriptItems.length(); ++i) {
+ for (int i = 0; i < scriptItems.size(); ++i) {
const auto &item = scriptItems.at(i);
- int end = i < scriptItems.length() - 1 ? scriptItems.at(i + 1).position : length;
+ int end = i < scriptItems.size() - 1 ? scriptItems.at(i + 1).position : length;
for (int j = item.position; j < end; ++j)
analysis[j].script = item.script;
}
@@ -1955,7 +1953,17 @@ void QTextEngine::itemize() const
while (uc < e) {
switch (*uc) {
case QChar::ObjectReplacementCharacter:
- analysis->flags = QScriptAnalysis::Object;
+ {
+ const QTextDocumentPrivate *doc_p = QTextDocumentPrivate::get(block);
+ if (doc_p != nullptr
+ && doc_p->layout() != nullptr
+ && QAbstractTextDocumentLayoutPrivate::get(doc_p->layout()) != nullptr
+ && QAbstractTextDocumentLayoutPrivate::get(doc_p->layout())->hasHandlers()) {
+ analysis->flags = QScriptAnalysis::Object;
+ } else {
+ analysis->flags = QScriptAnalysis::None;
+ }
+ }
break;
case QChar::LineSeparator:
analysis->flags = QScriptAnalysis::LineOrParagraphSeparator;
@@ -2007,7 +2015,7 @@ void QTextEngine::itemize() const
const QTextFragmentData * const frag = it.value();
if (it == end || format != frag->format) {
if (s && position >= preeditPosition) {
- position += s->preeditText.length();
+ position += s->preeditText.size();
preeditPosition = INT_MAX;
}
Q_ASSERT(position <= length);
@@ -2016,7 +2024,7 @@ void QTextEngine::itemize() const
? formatCollection()->charFormat(format).fontCapitalization()
: formatCollection()->defaultFont().capitalization();
if (s) {
- for (const auto &range : qAsConst(s->formats)) {
+ for (const auto &range : std::as_const(s->formats)) {
if (range.start + range.length <= prevPosition || range.start >= position)
continue;
if (range.format.hasProperty(QTextFormat::FontCapitalization)) {
@@ -2282,6 +2290,12 @@ QFontEngine *QTextEngine::fontEngine(const QScriptItem &si, QFixed *ascent, QFix
if (feCache.prevScaledFontEngine) {
scaledEngine = feCache.prevScaledFontEngine;
} else {
+ // GCC 12 gets confused about QFontEngine::ref, for some non-obvious reason
+ // warning: ‘unsigned int __atomic_or_fetch_4(volatile void*, unsigned int, int)’ writing 4 bytes
+ // into a region of size 0 overflows the destination [-Wstringop-overflow=]
+ QT_WARNING_PUSH
+ QT_WARNING_DISABLE_GCC("-Wstringop-overflow")
+
QFontEngine *scEngine = rawFont.d->fontEngine->cloneWithSize(smallCapsFraction * rawFont.pixelSize());
scEngine->ref.ref();
scaledEngine = QFontEngineMulti::createMultiFontEngine(scEngine, script);
@@ -2291,6 +2305,7 @@ QFontEngine *QTextEngine::fontEngine(const QScriptItem &si, QFixed *ascent, QFix
if (!scEngine->ref.deref())
delete scEngine;
+ QT_WARNING_POP
}
}
} else
@@ -2429,7 +2444,7 @@ void QTextEngine::justify(const QScriptLine &line)
if (!forceJustification) {
int end = line.from + (int)line.length + line.trailingSpaces;
- if (end == layoutData->string.length())
+ if (end == layoutData->string.size())
return; // no justification at end of paragraph
if (end && layoutData->items.at(findItem(end - 1)).analysis.flags == QScriptAnalysis::LineOrParagraphSeparator)
return; // no justification at the end of an explicitly separated line
@@ -2635,18 +2650,20 @@ QTextEngine::LayoutData::LayoutData()
haveCharAttributes = false;
logClustersPtr = nullptr;
available_glyphs = 0;
+ currentMaxWidth = 0;
}
-QTextEngine::LayoutData::LayoutData(const QString &str, void **stack_memory, int _allocated)
+QTextEngine::LayoutData::LayoutData(const QString &str, void **stack_memory, qsizetype _allocated)
: string(str)
{
allocated = _allocated;
- int space_charAttributes = int(sizeof(QCharAttributes) * string.length() / sizeof(void*) + 1);
- int space_logClusters = int(sizeof(unsigned short) * string.length() / sizeof(void*) + 1);
- available_glyphs = ((int)allocated - space_charAttributes - space_logClusters)*(int)sizeof(void*)/(int)QGlyphLayout::SpaceNeeded;
+ constexpr qsizetype voidSize = sizeof(void*);
+ qsizetype space_charAttributes = sizeof(QCharAttributes) * string.size() / voidSize + 1;
+ qsizetype space_logClusters = sizeof(unsigned short) * string.size() / voidSize + 1;
+ available_glyphs = (allocated - space_charAttributes - space_logClusters) * voidSize / QGlyphLayout::SpaceNeeded;
- if (available_glyphs < str.length()) {
+ if (available_glyphs < str.size()) {
// need to allocate on the heap
allocated = 0;
@@ -2659,7 +2676,7 @@ QTextEngine::LayoutData::LayoutData(const QString &str, void **stack_memory, int
logClustersPtr = (unsigned short *)(memory + space_charAttributes);
void *m = memory + space_charAttributes + space_logClusters;
- glyphLayout = QGlyphLayout(reinterpret_cast<char *>(m), str.length());
+ glyphLayout = QGlyphLayout(reinterpret_cast<char *>(m), str.size());
glyphLayout.clear();
memset(memory, 0, space_charAttributes*sizeof(void *));
}
@@ -2667,6 +2684,7 @@ QTextEngine::LayoutData::LayoutData(const QString &str, void **stack_memory, int
hasBidi = false;
layoutState = LayoutEmpty;
haveCharAttributes = false;
+ currentMaxWidth = 0;
}
QTextEngine::LayoutData::~LayoutData()
@@ -2684,15 +2702,16 @@ bool QTextEngine::LayoutData::reallocate(int totalGlyphs)
return true;
}
- int space_charAttributes = int(sizeof(QCharAttributes) * string.length() / sizeof(void*) + 1);
- int space_logClusters = int(sizeof(unsigned short) * string.length() / sizeof(void*) + 1);
- int space_glyphs = (totalGlyphs * QGlyphLayout::SpaceNeeded) / sizeof(void *) + 2;
+ const qsizetype space_charAttributes = (sizeof(QCharAttributes) * string.size() / sizeof(void*) + 1);
+ const qsizetype space_logClusters = (sizeof(unsigned short) * string.size() / sizeof(void*) + 1);
+ const qsizetype space_glyphs = qsizetype(totalGlyphs) * QGlyphLayout::SpaceNeeded / sizeof(void *) + 2;
- int newAllocated = space_charAttributes + space_glyphs + space_logClusters;
- // These values can be negative if the length of string/glyphs causes overflow,
+ const qsizetype newAllocated = space_charAttributes + space_glyphs + space_logClusters;
+ // Check if the length of string/glyphs causes int overflow,
// we can't layout such a long string all at once, so return false here to
// indicate there is a failure
- if (space_charAttributes < 0 || space_logClusters < 0 || space_glyphs < 0 || newAllocated < allocated) {
+ if (size_t(space_charAttributes) > INT_MAX || size_t(space_logClusters) > INT_MAX || totalGlyphs < 0
+ || size_t(space_glyphs) > INT_MAX || size_t(newAllocated) > INT_MAX || newAllocated < allocated) {
layoutState = LayoutFailed;
return false;
}
@@ -2712,7 +2731,7 @@ bool QTextEngine::LayoutData::reallocate(int totalGlyphs)
logClustersPtr = (unsigned short *) m;
m += space_logClusters;
- const int space_preGlyphLayout = space_charAttributes + space_logClusters;
+ const qsizetype space_preGlyphLayout = space_charAttributes + space_logClusters;
if (allocated < space_preGlyphLayout)
memset(memory + allocated, 0, (space_preGlyphLayout - allocated)*sizeof(void *));
@@ -2752,6 +2771,7 @@ void QTextEngine::freeMemory()
layoutData->hasBidi = false;
layoutData->layoutState = LayoutEmpty;
layoutData->haveCharAttributes = false;
+ layoutData->currentMaxWidth = 0;
layoutData->items.clear();
}
if (specialData)
@@ -2775,10 +2795,10 @@ int QTextEngine::formatIndex(const QScriptItem *si) const
return -1;
int pos = si->position;
if (specialData && si->position >= specialData->preeditPosition) {
- if (si->position < specialData->preeditPosition + specialData->preeditText.length())
+ if (si->position < specialData->preeditPosition + specialData->preeditText.size())
pos = qMax(qMin(block.length(), specialData->preeditPosition) - 1, 0);
else
- pos -= specialData->preeditText.length();
+ pos -= specialData->preeditText.size();
}
QTextDocumentPrivate::FragmentIterator it = p->find(block.position() + pos);
return it.value()->format;
@@ -2913,9 +2933,9 @@ void QTextEngine::indexFormats()
*/
static inline bool nextCharJoins(const QString &string, int pos)
{
- while (pos < string.length() && string.at(pos).category() == QChar::Mark_NonSpacing)
+ while (pos < string.size() && string.at(pos).category() == QChar::Mark_NonSpacing)
++pos;
- if (pos == string.length())
+ if (pos == string.size())
return false;
QChar::JoiningType joining = string.at(pos).joiningType();
return joining != QChar::Joining_None && joining != QChar::Joining_Transparent;
@@ -2931,11 +2951,11 @@ static inline bool prevCharJoins(const QString &string, int pos)
return joining == QChar::Joining_Dual || joining == QChar::Joining_Causing;
}
-static inline bool isRetainableControlCode(QChar c)
+static constexpr bool isRetainableControlCode(char16_t c) noexcept
{
- return (c.unicode() >= 0x202a && c.unicode() <= 0x202e) // LRE, RLE, PDF, LRO, RLO
- || (c.unicode() >= 0x200e && c.unicode() <= 0x200f) // LRM, RLM
- || (c.unicode() >= 0x2066 && c.unicode() <= 0x2069); // LRI, RLI, FSI, PDI
+ return (c >= 0x202a && c <= 0x202e) // LRE, RLE, PDF, LRO, RLO
+ || (c >= 0x200e && c <= 0x200f) // LRM, RLM
+ || (c >= 0x2066 && c <= 0x2069); // LRI, RLI, FSI, PDI
}
static QString stringMidRetainingBidiCC(const QString &string,
@@ -2948,14 +2968,14 @@ static QString stringMidRetainingBidiCC(const QString &string,
{
QString prefix;
for (int i=subStringFrom; i<midStart; ++i) {
- QChar c = string.at(i);
+ char16_t c = string.at(i).unicode();
if (isRetainableControlCode(c))
prefix += c;
}
QString suffix;
for (int i=midStart + midLength; i<subStringTo; ++i) {
- QChar c = string.at(i);
+ char16_t c = string.at(i).unicode();
if (isRetainableControlCode(c))
suffix += c;
}
@@ -2963,7 +2983,7 @@ static QString stringMidRetainingBidiCC(const QString &string,
return prefix + ellidePrefix + QStringView{string}.mid(midStart, midLength) + ellideSuffix + suffix;
}
-QString QTextEngine::elidedText(Qt::TextElideMode mode, const QFixed &width, int flags, int from, int count) const
+QString QTextEngine::elidedText(Qt::TextElideMode mode, QFixed width, int flags, int from, int count) const
{
// qDebug() << "elidedText; available width" << width.toReal() << "text width:" << this->width(0, layoutData->string.length()).toReal();
@@ -2982,14 +3002,14 @@ QString QTextEngine::elidedText(Qt::TextElideMode mode, const QFixed &width, int
const int end = si.position + length(&si);
for (int i = si.position; i < end - 1; ++i) {
- if (layoutData->string.at(i) == QLatin1Char('&')
+ if (layoutData->string.at(i) == u'&'
&& !attributes[i + 1].whiteSpace && attributes[i + 1].graphemeBoundary) {
const int gp = logClusters[i - si.position];
glyphs.attributes[gp].dontPrint = true;
// emulate grapheme cluster
attributes[i] = attributes[i + 1];
memset(attributes + i + 1, 0, sizeof(QCharAttributes));
- if (layoutData->string.at(i + 1) == QLatin1Char('&'))
+ if (layoutData->string.at(i + 1) == u'&')
++i;
}
}
@@ -2998,12 +3018,12 @@ QString QTextEngine::elidedText(Qt::TextElideMode mode, const QFixed &width, int
validate();
- const int to = count >= 0 && count <= layoutData->string.length() - from
+ const int to = count >= 0 && count <= layoutData->string.size() - from
? from + count
- : layoutData->string.length();
+ : layoutData->string.size();
if (mode == Qt::ElideNone
- || this->width(from, layoutData->string.length()) <= width
+ || this->width(from, layoutData->string.size()) <= width
|| to - from <= 1)
return layoutData->string.mid(from, from - to);
@@ -3012,7 +3032,7 @@ QString QTextEngine::elidedText(Qt::TextElideMode mode, const QFixed &width, int
{
QFontEngine *engine = fnt.d->engineForScript(QChar::Script_Common);
- QChar ellipsisChar = u'\x2026';
+ constexpr char16_t ellipsisChar = u'\x2026';
// We only want to use the ellipsis character if it is from the main
// font (not one of the fallbacks), since using a fallback font
@@ -3024,7 +3044,7 @@ QString QTextEngine::elidedText(Qt::TextElideMode mode, const QFixed &width, int
engine = multiEngine->engine(0);
}
- glyph_t glyph = engine->glyphIndex(ellipsisChar.unicode());
+ glyph_t glyph = engine->glyphIndex(ellipsisChar);
QGlyphLayout glyphs;
glyphs.numGlyphs = 1;
@@ -3044,7 +3064,7 @@ QString QTextEngine::elidedText(Qt::TextElideMode mode, const QFixed &width, int
ellipsisText = QStringLiteral("...");
} else {
engine = fnt.d->engineForScript(QChar::Script_Common);
- glyph = engine->glyphIndex(ellipsisChar.unicode());
+ glyph = engine->glyphIndex(ellipsisChar);
engine->recalcAdvances(&glyphs, { });
ellipsisText = ellipsisChar;
}
@@ -3070,7 +3090,7 @@ QString QTextEngine::elidedText(Qt::TextElideMode mode, const QFixed &width, int
pos = nextBreak;
++nextBreak;
- while (nextBreak < layoutData->string.length() && !attributes[nextBreak].graphemeBoundary)
+ while (nextBreak < layoutData->string.size() && !attributes[nextBreak].graphemeBoundary)
++nextBreak;
currentWidth += this->width(pos, nextBreak - pos);
@@ -3122,7 +3142,7 @@ QString QTextEngine::elidedText(Qt::TextElideMode mode, const QFixed &width, int
rightPos = nextRightBreak;
++nextLeftBreak;
- while (nextLeftBreak < layoutData->string.length() && !attributes[nextLeftBreak].graphemeBoundary)
+ while (nextLeftBreak < layoutData->string.size() && !attributes[nextLeftBreak].graphemeBoundary)
++nextLeftBreak;
--nextRightBreak;
@@ -3161,7 +3181,7 @@ void QTextEngine::setBoundary(int strPos) const
QFixed QTextEngine::calculateTabWidth(int item, QFixed x) const
{
- const QScriptItem &si = layoutData->items[item];
+ const QScriptItem &si = layoutData->items.at(item);
QFixed dpiScale = 1;
if (QTextDocumentPrivate::get(block) != nullptr && QTextDocumentPrivate::get(block)->layout() != nullptr) {
@@ -3195,15 +3215,15 @@ QFixed QTextEngine::calculateTabWidth(int item, QFixed x) const
}
}
}
- for (const QTextOption::Tab &tabSpec : qAsConst(tabArray)) {
+ for (const QTextOption::Tab &tabSpec : std::as_const(tabArray)) {
QFixed tab = QFixed::fromReal(tabSpec.position) * dpiScale;
if (tab > x) { // this is the tab we need.
- int tabSectionEnd = layoutData->string.count();
+ int tabSectionEnd = layoutData->string.size();
if (tabSpec.type == QTextOption::RightTab || tabSpec.type == QTextOption::CenterTab) {
// find next tab to calculate the width required.
tab = QFixed::fromReal(tabSpec.position);
- for (int i=item + 1; i < layoutData->items.count(); i++) {
- const QScriptItem &item = layoutData->items[i];
+ for (int i=item + 1; i < layoutData->items.size(); i++) {
+ const QScriptItem &item = layoutData->items.at(i);
if (item.analysis.flags == QScriptAnalysis::TabOrObject) { // found it.
tabSectionEnd = item.position;
break;
@@ -3217,7 +3237,7 @@ QFixed QTextEngine::calculateTabWidth(int item, QFixed x) const
if (tabSectionEnd > si.position) {
QFixed length;
// Calculate the length of text between this tab and the tabSectionEnd
- for (int i=item; i < layoutData->items.count(); i++) {
+ for (int i=item; i < layoutData->items.size(); i++) {
const QScriptItem &item = layoutData->items.at(i);
if (item.position > tabSectionEnd || item.position <= si.position)
continue;
@@ -3289,7 +3309,7 @@ void QTextEngine::resolveFormats() const
QTextFormatCollection *collection = formatCollection();
- QList<QTextCharFormat> resolvedFormats(layoutData->items.count());
+ QList<QTextCharFormat> resolvedFormats(layoutData->items.size());
QVarLengthArray<int, 64> formatsSortedByStart;
formatsSortedByStart.reserve(specialData->formats.size());
@@ -3307,7 +3327,7 @@ void QTextEngine::resolveFormats() const
const int *startIt = formatsSortedByStart.constBegin();
const int *endIt = formatsSortedByEnd.constBegin();
- for (int i = 0; i < layoutData->items.count(); ++i) {
+ for (int i = 0; i < layoutData->items.size(); ++i) {
const QScriptItem *si = &layoutData->items.at(i);
int end = si->position + length(si);
@@ -3483,8 +3503,8 @@ int QTextEngine::previousLogicalPosition(int oldPos) const
{
const QCharAttributes *attrs = attributes();
int len = block.isValid() ? block.length() - 1
- : layoutData->string.length();
- Q_ASSERT(len <= layoutData->string.length());
+ : layoutData->string.size();
+ Q_ASSERT(len <= layoutData->string.size());
if (!attrs || oldPos <= 0 || oldPos > len)
return oldPos;
@@ -3498,8 +3518,8 @@ int QTextEngine::nextLogicalPosition(int oldPos) const
{
const QCharAttributes *attrs = attributes();
int len = block.isValid() ? block.length() - 1
- : layoutData->string.length();
- Q_ASSERT(len <= layoutData->string.length());
+ : layoutData->string.size();
+ Q_ASSERT(len <= layoutData->string.size());
if (!attrs || oldPos < 0 || oldPos >= len)
return oldPos;
@@ -3513,7 +3533,7 @@ int QTextEngine::lineNumberForTextPosition(int pos)
{
if (!layoutData)
itemize();
- if (pos == layoutData->string.length() && lines.size())
+ if (pos == layoutData->string.size() && lines.size())
return lines.size() - 1;
for (int i = 0; i < lines.size(); ++i) {
const QScriptLine& line = lines[i];
@@ -3837,10 +3857,12 @@ QTextLineItemIterator::QTextLineItemIterator(QTextEngine *_eng, int _lineNum, co
x += eng->alignLine(line);
- QVarLengthArray<uchar> levels(nItems);
- for (int i = 0; i < nItems; ++i)
- levels[i] = eng->layoutData->items.at(i + firstItem).analysis.bidiLevel;
- QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
+ if (nItems > 0) {
+ QVarLengthArray<uchar> levels(nItems);
+ for (int i = 0; i < nItems; ++i)
+ levels[i] = eng->layoutData->items.at(i + firstItem).analysis.bidiLevel;
+ QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
+ }
eng->shapeLine(line);
}