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.cpp266
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;
}