diff options
Diffstat (limited to 'src/gui/text/qtextengine.cpp')
-rw-r--r-- | src/gui/text/qtextengine.cpp | 266 |
1 files changed, 148 insertions, 118 deletions
diff --git a/src/gui/text/qtextengine.cpp b/src/gui/text/qtextengine.cpp index a3059008be..17bc4fde98 100644 --- a/src/gui/text/qtextengine.cpp +++ b/src/gui/text/qtextengine.cpp @@ -59,10 +59,6 @@ #include <algorithm> #include <stdlib.h> -#ifndef QT_NO_RAWFONT -#include "qfontengine_qpa_p.h" -#endif - QT_BEGIN_NAMESPACE static const float smallCapsFraction = 0.7f; @@ -88,7 +84,7 @@ public: /// The caps parameter is used to choose the algoritm of splitting text and assiging roles to the textitems void generate(int start, int length, QFont::Capitalization caps) { - if ((int)caps == (int)QFont::SmallCaps) + if (caps == QFont::SmallCaps) generateScriptItemsSmallCaps(reinterpret_cast<const ushort *>(m_string.unicode()), start, length); else if(caps == QFont::Capitalize) generateScriptItemsCapitalize(start, length); @@ -122,9 +118,7 @@ private: return; const int end = start + length; 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 + if (m_analysis[i] == m_analysis[start] && m_analysis[i].flags < QScriptAnalysis::SpaceTabOrObject && i - start < MaxItemLength) continue; @@ -843,6 +837,81 @@ enum JustificationClass { Justification_Arabic_Kashida = 13 // User-inserted Kashida(U+0640) }; +#ifdef QT_ENABLE_HARFBUZZ_NG + +/* + 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, QGlyphLayout g, ushort *log_clusters, int spaceAs) +{ + int str_pos = 0; + while (str_pos < length) { + int glyph_pos = log_clusters[str_pos]; + + Q_ASSERT(glyph_pos < g.numGlyphs && g.attributes[glyph_pos].clusterStart); + + uint ucs4 = string[str_pos]; + if (QChar::isHighSurrogate(ucs4) && str_pos + 1 < length) { + ushort low = string[str_pos + 1]; + if (QChar::isLowSurrogate(low)) { + ++str_pos; + ucs4 = QChar::surrogateToUcs4(ucs4, low); + } + } + + // skip whole cluster + do { + ++str_pos; + } while (str_pos < length && log_clusters[str_pos] == glyph_pos); + do { + ++glyph_pos; + } while (glyph_pos < g.numGlyphs && !g.attributes[glyph_pos].clusterStart); + --glyph_pos; + + // justification opportunity at the end of cluster + if (Q_LIKELY(QChar::isLetterOrNumber(ucs4))) + g.attributes[glyph_pos].justification = Justification_Character; + else if (Q_LIKELY(QChar::isSpace(ucs4))) + g.attributes[glyph_pos].justification = spaceAs; + } +} + +static inline void qt_getJustificationOpportunities(const ushort *string, int length, const QScriptItem &si, QGlyphLayout g, ushort *log_clusters) +{ + Q_ASSERT(length > 0 && g.numGlyphs > 0); + + for (int glyph_pos = 0; glyph_pos < g.numGlyphs; ++glyph_pos) + g.attributes[glyph_pos].justification = Justification_Prohibited; + + int spaceAs; + + switch (si.analysis.script) { + case QChar::Script_Nko: + case QChar::Script_Mandaic: + case QChar::Script_Mongolian: + case QChar::Script_PhagsPa: + // same as default but inter character justification takes precedence + spaceAs = Justification_Arabic_Space; + break; + + case QChar::Script_Hiragana: + case QChar::Script_Katakana: + case QChar::Script_Han: + // same as default but inter character justification is the only option + spaceAs = Justification_Character; + break; + + default: + spaceAs = Justification_Space; + break; + } + + qt_getDefaultJustificationOpportunities(string, length, g, log_clusters, spaceAs); +} + +#endif // QT_ENABLE_HARFBUZZ_NG + // shape all the items that intersect with the line, taking tab widths into account to find out what text actually fits in the line. void QTextEngine::shapeLine(const QScriptLine &line) @@ -853,7 +922,7 @@ void QTextEngine::shapeLine(const QScriptLine &line) int item = findItem(line.from); if (item == -1) return; - for (item = findItem(line.from); item <= end; ++item) { + for ( ; item <= end; ++item) { QScriptItem &si = layoutData->items[item]; if (si.analysis.flags == QScriptAnalysis::Tab) { ensureSpace(1); @@ -1003,6 +1072,11 @@ void QTextEngine::shapeText(int item) const QGlyphLayout glyphs = shapedGlyphs(&si); +#ifdef QT_ENABLE_HARFBUZZ_NG + if (useHarfbuzzNG) + qt_getJustificationOpportunities(string, itemLength, si, glyphs, logClusters(&si)); +#endif + if (letterSpacing != 0) { for (int i = 1; i < si.num_glyphs; ++i) { if (glyphs.attributes[i].clusterStart) { @@ -1082,7 +1156,7 @@ int QTextEngine::shapeTextWithHarfbuzzNG(const QScriptItem &si, const ushort *st hb_buffer_set_segment_properties(buffer, &props); hb_buffer_guess_segment_properties(buffer); - uint buffer_flags = 0; // HB_BUFFER_FLAG_DEFAULT + uint buffer_flags = HB_BUFFER_FLAG_DEFAULT; // Symbol encoding used to encode various crap in the 32..255 character code range, // and thus might override U+00AD [SHY]; avoid hiding default ignorables if (actualFontEngine->symbol) @@ -1424,8 +1498,6 @@ void QTextEngine::invalidate() freeMemory(); minWidth = 0; maxWidth = 0; - if (specialData) - specialData->resolvedFormats.clear(); resetFontEngineCache(); } @@ -1586,9 +1658,9 @@ void QTextEngine::itemize() const } Q_ASSERT(position <= length); QFont::Capitalization capitalization = - formats()->charFormat(format).hasProperty(QTextFormat::FontCapitalization) - ? formats()->charFormat(format).fontCapitalization() - : formats()->defaultFont().capitalization(); + formatCollection()->charFormat(format).hasProperty(QTextFormat::FontCapitalization) + ? formatCollection()->charFormat(format).fontCapitalization() + : formatCollection()->defaultFont().capitalization(); itemizer.generate(prevPosition, position - prevPosition, capitalization); if (it == end) { if (position < length) @@ -1605,8 +1677,8 @@ void QTextEngine::itemize() const #ifndef QT_NO_RAWFONT if (useRawFont && specialData) { int lastIndex = 0; - for (int i = 0; i < specialData->addFormats.size(); ++i) { - const QTextLayout::FormatRange &range = specialData->addFormats.at(i); + for (int i = 0; i < specialData->formats.size(); ++i) { + const QTextLayout::FormatRange &range = specialData->formats.at(i); const QTextCharFormat &format = range.format; if (format.hasProperty(QTextFormat::FontCapitalization)) { itemizer.generate(lastIndex, range.start - lastIndex, QFont::MixedCase); @@ -1621,7 +1693,7 @@ void QTextEngine::itemize() const } addRequiredBoundaries(); - resolveAdditionalFormats(); + resolveFormats(); } bool QTextEngine::isRightToLeft() const @@ -1646,6 +1718,9 @@ bool QTextEngine::isRightToLeft() const int QTextEngine::findItem(int strPos) const { itemize(); + if (strPos < 0 || strPos >= layoutData->string.size()) + return -1; + int left = 1; int right = layoutData->items.size()-1; while(left <= right) { @@ -1884,7 +1959,7 @@ QFontEngine *QTextEngine::fontEngine(const QScriptItem &si, QFixed *ascent, QFix if (feCache.prevFontEngine && feCache.prevFontEngine->type() == QFontEngine::Multi && feCache.prevScript == script) { engine = feCache.prevFontEngine; } else { - engine = QFontEngineMultiQPA::createMultiFontEngine(rawFont.d->fontEngine, script); + engine = QFontEngineMultiBasicImpl::createMultiFontEngine(rawFont.d->fontEngine, script); feCache.prevFontEngine = engine; feCache.prevScript = script; engine->ref.ref(); @@ -1893,13 +1968,13 @@ QFontEngine *QTextEngine::fontEngine(const QScriptItem &si, QFixed *ascent, QFix feCache.prevScaledFontEngine = 0; } } - if (si.analysis.flags & QFont::SmallCaps) { + if (si.analysis.flags == QScriptAnalysis::SmallCaps) { if (feCache.prevScaledFontEngine) { scaledEngine = feCache.prevScaledFontEngine; } else { QFontEngine *scEngine = rawFont.d->fontEngine->cloneWithSize(smallCapsFraction * rawFont.pixelSize()); scEngine->ref.ref(); - scaledEngine = QFontEngineMultiQPA::createMultiFontEngine(scEngine, script); + scaledEngine = QFontEngineMultiBasicImpl::createMultiFontEngine(scEngine, script); scaledEngine->ref.ref(); feCache.prevScaledFontEngine = scaledEngine; // If scEngine is not ref'ed by scaledEngine, make sure it is deallocated and not leaked. @@ -2065,7 +2140,8 @@ void QTextEngine::justify(const QScriptLine &line) return; int firstItem = findItem(line.from); - int nItems = findItem(line.from + line_length - 1) - firstItem + 1; + int lastItem = findItem(line.from + line_length - 1); + int nItems = (firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0; QVarLengthArray<QJustificationPoint> justificationPoints; int nPoints = 0; @@ -2367,6 +2443,8 @@ void QTextEngine::freeMemory() layoutData->haveCharAttributes = false; layoutData->items.clear(); } + if (specialData) + specialData->resolvedFormats.clear(); for (int i = 0; i < lines.size(); ++i) { lines[i].justified = 0; lines[i].gridfitted = 0; @@ -2376,7 +2454,7 @@ void QTextEngine::freeMemory() int QTextEngine::formatIndex(const QScriptItem *si) const { if (specialData && !specialData->resolvedFormats.isEmpty()) { - QTextFormatCollection *collection = formats(); + QTextFormatCollection *collection = formatCollection(); Q_ASSERT(collection); return collection->indexForFormat(specialData->resolvedFormats.at(si - &layoutData->items[0])); } @@ -2398,16 +2476,16 @@ int QTextEngine::formatIndex(const QScriptItem *si) const QTextCharFormat QTextEngine::format(const QScriptItem *si) const { - if (const QTextFormatCollection *formats = this->formats()) - return formats->charFormat(formatIndex(si)); + if (const QTextFormatCollection *collection = formatCollection()) + return collection->charFormat(formatIndex(si)); return QTextCharFormat(); } void QTextEngine::addRequiredBoundaries() const { if (specialData) { - for (int i = 0; i < specialData->addFormats.size(); ++i) { - const QTextLayout::FormatRange &r = specialData->addFormats.at(i); + for (int i = 0; i < specialData->formats.size(); ++i) { + const QTextLayout::FormatRange &r = specialData->formats.at(i); setBoundary(r.start); setBoundary(r.start + r.length); //qDebug("adding boundaries %d %d", r.start, r.start+r.length); @@ -2476,7 +2554,7 @@ void QTextEngine::setPreeditArea(int position, const QString &preeditText) if (preeditText.isEmpty()) { if (!specialData) return; - if (specialData->addFormats.isEmpty()) { + if (specialData->formats.isEmpty()) { delete specialData; specialData = 0; } else { @@ -2493,41 +2571,41 @@ void QTextEngine::setPreeditArea(int position, const QString &preeditText) clearLineData(); } -void QTextEngine::setAdditionalFormats(const QList<QTextLayout::FormatRange> &formatList) +void QTextEngine::setFormats(const QList<QTextLayout::FormatRange> &formats) { - if (formatList.isEmpty()) { + if (formats.isEmpty()) { if (!specialData) return; if (specialData->preeditText.isEmpty()) { delete specialData; specialData = 0; } else { - specialData->addFormats.clear(); + specialData->formats.clear(); } } else { if (!specialData) { specialData = new SpecialData; specialData->preeditPosition = -1; } - specialData->addFormats = formatList; - indexAdditionalFormats(); + specialData->formats = formats; + indexFormats(); } invalidate(); clearLineData(); } -void QTextEngine::indexAdditionalFormats() +void QTextEngine::indexFormats() { - QTextFormatCollection *collection = formats(); + QTextFormatCollection *collection = formatCollection(); if (!collection) { Q_ASSERT(!block.docHandle()); - specialData->formats.reset(new QTextFormatCollection); - collection = specialData->formats.data(); + specialData->formatCollection.reset(new QTextFormatCollection); + collection = specialData->formatCollection.data(); } // replace with shared copies - for (int i = 0; i < specialData->addFormats.count(); ++i) { - QTextCharFormat &format = specialData->addFormats[i].format; + for (int i = 0; i < specialData->formats.size(); ++i) { + QTextCharFormat &format = specialData->formats[i].format; format = collection->charFormat(collection->indexForFormat(format)); } } @@ -2755,64 +2833,17 @@ QString QTextEngine::elidedText(Qt::TextElideMode mode, const QFixed &width, int return layoutData->string.mid(from, to - from); } -namespace { -struct QScriptItemComparator { - bool operator()(int p, const QScriptItem &b) { return p < b.position; } -#if defined(Q_CC_MSVC) && _MSC_VER < 1600 -//The STL implementation of MSVC 2008 requires the definition - bool operator()(const QScriptItem &a, int p) { return a.position < p; } - bool operator()(const QScriptItem &a, const QScriptItem &b) { return a.position < b.position; } -#endif -}; -} - void QTextEngine::setBoundary(int strPos) const { - if (strPos <= 0 || strPos >= layoutData->string.length()) + const int item = findItem(strPos); + if (item < 0) return; - const QScriptItem* it = std::upper_bound(layoutData->items.constBegin(), layoutData->items.constEnd(), - strPos, QScriptItemComparator()); - Q_ASSERT(it > layoutData->items.constBegin()); - --it; - if (it->position == strPos) { - // already a split at the requested position - return; + QScriptItem newItem = layoutData->items.at(item); + if (newItem.position != strPos) { + newItem.position = strPos; + layoutData->items.insert(item + 1, newItem); } - splitItem(it - layoutData->items.constBegin(), strPos - it->position); -} - -void QTextEngine::splitItem(int item, int pos) const -{ - if (pos <= 0) - return; - - layoutData->items.insert(item + 1, layoutData->items[item]); - QScriptItem &oldItem = layoutData->items[item]; - QScriptItem &newItem = layoutData->items[item+1]; - newItem.position += pos; - - if (oldItem.num_glyphs) { - // already shaped, break glyphs aswell - int breakGlyph = logClusters(&oldItem)[pos]; - - newItem.num_glyphs = oldItem.num_glyphs - breakGlyph; - oldItem.num_glyphs = breakGlyph; - newItem.glyph_data_offset = oldItem.glyph_data_offset + breakGlyph; - - for (int i = 0; i < newItem.num_glyphs; i++) - logClusters(&newItem)[i] -= breakGlyph; - - QFixed w = 0; - const QGlyphLayout g = shapedGlyphs(&oldItem); - for(int j = 0; j < breakGlyph; ++j) - w += g.advances[j] * !g.attributes[j].dontPrint; - - newItem.width = oldItem.width - w; - oldItem.width = w; - } - -// qDebug("split at position %d itempos=%d", pos, item); } QFixed QTextEngine::calculateTabWidth(int item, QFixed x) const @@ -2932,45 +2963,44 @@ public: }; } -void QTextEngine::resolveAdditionalFormats() const +void QTextEngine::resolveFormats() const { - if (!specialData || specialData->addFormats.isEmpty() - || !specialData->resolvedFormats.isEmpty()) + if (!specialData || specialData->formats.isEmpty()) return; + Q_ASSERT(specialData->resolvedFormats.isEmpty()); - QTextFormatCollection *collection = formats(); + QTextFormatCollection *collection = formatCollection(); - specialData->resolvedFormats.clear(); QVector<QTextCharFormat> resolvedFormats(layoutData->items.count()); - QVarLengthArray<int, 64> addFormatSortedByStart; - addFormatSortedByStart.reserve(specialData->addFormats.count()); - for (int i = 0; i < specialData->addFormats.count(); ++i) { - if (specialData->addFormats.at(i).length >= 0) - addFormatSortedByStart.append(i); + QVarLengthArray<int, 64> formatsSortedByStart; + formatsSortedByStart.reserve(specialData->formats.size()); + for (int i = 0; i < specialData->formats.size(); ++i) { + if (specialData->formats.at(i).length >= 0) + formatsSortedByStart.append(i); } - QVarLengthArray<int, 64> addFormatSortedByEnd = addFormatSortedByStart; - std::sort(addFormatSortedByStart.begin(), addFormatSortedByStart.end(), - FormatRangeComparatorByStart(specialData->addFormats)); - std::sort(addFormatSortedByEnd.begin(), addFormatSortedByEnd.end(), - FormatRangeComparatorByEnd(specialData->addFormats)); + QVarLengthArray<int, 64> formatsSortedByEnd = formatsSortedByStart; + std::sort(formatsSortedByStart.begin(), formatsSortedByStart.end(), + FormatRangeComparatorByStart(specialData->formats)); + std::sort(formatsSortedByEnd.begin(), formatsSortedByEnd.end(), + FormatRangeComparatorByEnd(specialData->formats)); QVarLengthArray<int, 16> currentFormats; - const int *startIt = addFormatSortedByStart.constBegin(); - const int *endIt = addFormatSortedByEnd.constBegin(); + const int *startIt = formatsSortedByStart.constBegin(); + const int *endIt = formatsSortedByEnd.constBegin(); for (int i = 0; i < layoutData->items.count(); ++i) { const QScriptItem *si = &layoutData->items.at(i); int end = si->position + length(si); - while (startIt != addFormatSortedByStart.constEnd() && - specialData->addFormats.at(*startIt).start <= si->position) { + while (startIt != formatsSortedByStart.constEnd() && + specialData->formats.at(*startIt).start <= si->position) { currentFormats.insert(std::upper_bound(currentFormats.begin(), currentFormats.end(), *startIt), *startIt); ++startIt; } - while (endIt != addFormatSortedByEnd.constEnd() && - specialData->addFormats.at(*endIt).start + specialData->addFormats.at(*endIt).length < end) { + while (endIt != formatsSortedByEnd.constEnd() && + specialData->formats.at(*endIt).start + specialData->formats.at(*endIt).length < end) { int *currentFormatIterator = std::lower_bound(currentFormats.begin(), currentFormats.end(), *endIt); if (*endIt < *currentFormatIterator) currentFormatIterator = currentFormats.end(); @@ -2986,7 +3016,7 @@ void QTextEngine::resolveAdditionalFormats() const } if (!currentFormats.isEmpty()) { foreach (int cur, currentFormats) { - const QTextLayout::FormatRange &range = specialData->addFormats.at(cur); + const QTextLayout::FormatRange &range = specialData->formats.at(cur); Q_ASSERT(range.start <= si->position && range.start + range.length >= end); format.merge(range.format); } @@ -3486,15 +3516,15 @@ QTextLineItemIterator::QTextLineItemIterator(QTextEngine *_eng, int _lineNum, co logicalItem(-1), item(-1), visualOrder(nItems), - levels(nItems), selection(_selection) { - pos_x = x = QFixed::fromReal(pos.x()); + x = QFixed::fromReal(pos.x()); x += line.x; x += eng->alignLine(line); + QVarLengthArray<uchar> levels(nItems); for (int i = 0; i < nItems; ++i) levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel; QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data()); @@ -3565,7 +3595,7 @@ bool QTextLineItemIterator::getSelectionBounds(QFixed *selectionX, QFixed *selec return false; int start_glyph = logClusters[from]; - int end_glyph = (to == eng->length(item)) ? si->num_glyphs : logClusters[to]; + int end_glyph = (to == itemLength) ? si->num_glyphs : logClusters[to]; QFixed soff; QFixed swidth; if (si->analysis.bidiLevel %2) { @@ -3590,7 +3620,7 @@ bool QTextLineItemIterator::getSelectionBounds(QFixed *selectionX, QFixed *selec // If the ending character is also part of a ligature, swidth does // not contain that part yet, we also need to find out the width of // that left part - *selectionWidth += eng->offsetInLigature(si, to, eng->length(item), end_glyph); + *selectionWidth += eng->offsetInLigature(si, to, itemLength, end_glyph); } return true; } |