drop BiDi support

still wonky on selection:
https://ibb.co/D4bWVqX

Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
This commit is contained in:
Ivailo Monev 2022-01-02 16:46:26 +02:00
parent 57dbb8d1cd
commit a699a16de9
7 changed files with 54 additions and 389 deletions

View file

@ -4789,17 +4789,11 @@ void QPainter::drawText(const QPointF &p, const QString &str)
engine.shapeLine(line); engine.shapeLine(line);
int nItems = engine.layoutData->items.size(); int nItems = engine.layoutData->items.size();
QVarLengthArray<int> visualOrder(nItems);
QVarLengthArray<uchar> levels(nItems);
for (int i = 0; i < nItems; ++i)
levels[i] = engine.layoutData->items[i].analysis.bidiLevel;
QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
QFixed x = QFixed::fromReal(p.x()); QFixed x = QFixed::fromReal(p.x());
for (int i = 0; i < nItems; ++i) { for (int i = 0; i < nItems; ++i) {
int item = visualOrder[i]; const QScriptItem &si = engine.layoutData->items.at(i);
const QScriptItem &si = engine.layoutData->items.at(item);
if (si.analysis.flags >= QScriptAnalysis::TabOrObject) { if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
x += si.width; x += si.width;
continue; continue;
@ -4808,7 +4802,7 @@ void QPainter::drawText(const QPointF &p, const QString &str)
QTextItemInt gf(si, &f); QTextItemInt gf(si, &f);
gf.glyphs = engine.shapedGlyphs(&si); gf.glyphs = engine.shapedGlyphs(&si);
gf.chars = engine.layoutData->string.unicode() + si.position; gf.chars = engine.layoutData->string.unicode() + si.position;
gf.num_chars = engine.length(item); gf.num_chars = engine.length(i);
if (engine.forceJustification) { if (engine.forceJustification) {
for (int j=0; j<gf.glyphs.numGlyphs; ++j) for (int j=0; j<gf.glyphs.numGlyphs; ++j)
gf.width += gf.glyphs.effectiveAdvance(j); gf.width += gf.glyphs.effectiveAdvance(j);
@ -6379,9 +6373,7 @@ start_lengthVariant:
qreal advance = line.horizontalAdvance(); qreal advance = line.horizontalAdvance();
xoff = 0; xoff = 0;
if (tf & Qt::AlignRight) { if (tf & Qt::AlignRight) {
QTextEngine *eng = textLayout.engine(); xoff = r.width() - advance;
xoff = r.width() - advance -
eng->leadingSpaceWidth(eng->lines[line.lineNumber()]).toReal();
} }
else if (tf & Qt::AlignHCenter) else if (tf & Qt::AlignHCenter)
xoff = (r.width() - advance) / 2; xoff = (r.width() - advance) / 2;

View file

@ -1104,24 +1104,14 @@ void QPainterPath::addText(const QPointF &point, const QFont &f, const QString &
qreal x(point.x()); qreal x(point.x());
qreal y(point.y()); qreal y(point.y());
QVarLengthArray<int> visualOrder(nItems);
QVarLengthArray<uchar> levels(nItems);
for (int i = 0; i < nItems; ++i)
levels[i] = eng->layoutData->items[i].analysis.bidiLevel;
QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
for (int i = 0; i < nItems; ++i) { for (int i = 0; i < nItems; ++i) {
int item = visualOrder[i]; QScriptItem &si = eng->layoutData->items[i];
QScriptItem &si = eng->layoutData->items[item];
if (si.analysis.flags < QScriptAnalysis::TabOrObject) { if (si.analysis.flags < QScriptAnalysis::TabOrObject) {
QGlyphLayout glyphs = eng->shapedGlyphs(&si); QGlyphLayout glyphs = eng->shapedGlyphs(&si);
QFontEngine *fe = f.d->engineForScript(si.analysis.script); QFontEngine *fe = f.d->engineForScript(si.analysis.script);
Q_ASSERT(fe); Q_ASSERT(fe);
fe->addOutlineToPath(x, y, glyphs, this, fe->addOutlineToPath(x, y, glyphs, this, QTextItem::RenderFlags(0));
si.analysis.bidiLevel % 2
? QTextItem::RenderFlags(QTextItem::RightToLeft)
: QTextItem::RenderFlags(0));
const qreal lw = fe->lineThickness().toReal(); const qreal lw = fe->lineThickness().toReal();
if (f.d->underline) { if (f.d->underline) {

View file

@ -113,8 +113,6 @@ static bool qHB_ConvertStringToGlyphIndices(HB_ShaperItem *shaper_item)
QVarLengthGlyphLayoutArray qglyphs(shaper_item->num_glyphs); QVarLengthGlyphLayoutArray qglyphs(shaper_item->num_glyphs);
QTextEngine::ShaperFlags shaperFlags(QTextEngine::GlyphIndicesOnly); QTextEngine::ShaperFlags shaperFlags(QTextEngine::GlyphIndicesOnly);
if (shaper_item->item.bidiLevel % 2)
shaperFlags |= QTextEngine::RightToLeft;
int nGlyphs = shaper_item->num_glyphs; int nGlyphs = shaper_item->num_glyphs;
bool result = fe->stringToCMap(reinterpret_cast<const QChar *>(shaper_item->string + shaper_item->item.pos), bool result = fe->stringToCMap(reinterpret_cast<const QChar *>(shaper_item->string + shaper_item->item.pos),
@ -270,8 +268,6 @@ static inline void positionCluster(HB_ShaperItem *item, int gfrom, int glast)
//qreal offsetBase = (size - 4) / 4 + qMin<qreal>(size, 4) + 1; //qreal offsetBase = (size - 4) / 4 + qMin<qreal>(size, 4) + 1;
// qDebug("offset = %f", offsetBase); // qDebug("offset = %f", offsetBase);
bool rightToLeft = item->item.bidiLevel % 2;
int i; int i;
unsigned char lastCmb = 0; unsigned char lastCmb = 0;
HB_GlyphMetrics attachmentRect; HB_GlyphMetrics attachmentRect;
@ -365,13 +361,8 @@ static inline void positionCluster(HB_ShaperItem *item, int gfrom, int glast)
attachmentRect = unitedAttachmentRect; attachmentRect = unitedAttachmentRect;
lastCmb = cmb; lastCmb = cmb;
if (rightToLeft) { item->offsets[gfrom+i].x = p.x - baseMetrics.xOffset;
item->offsets[gfrom+i].x = p.x; item->offsets[gfrom+i].y = p.y - baseMetrics.yOffset;
item->offsets[gfrom+i].y = p.y;
} else {
item->offsets[gfrom+i].x = p.x - baseMetrics.xOffset;
item->offsets[gfrom+i].y = p.y - baseMetrics.yOffset;
}
item->advances[gfrom+i] = 0; item->advances[gfrom+i] = 0;
} }
} }

View file

@ -400,7 +400,6 @@ typedef struct {
uint32_t pos; uint32_t pos;
uint32_t length; uint32_t length;
HB_Script script; HB_Script script;
uint8_t bidiLevel;
} HB_ScriptItem; } HB_ScriptItem;
typedef struct { typedef struct {

View file

@ -78,8 +78,7 @@ public:
// the font and because Japanese and Chinese are also aliases of the script "Common", // the font and because Japanese and Chinese are also aliases of the script "Common",
// doing this would break too many things. So instead we only pass the full stop // doing this would break too many things. So instead we only pass the full stop
// along, and nothing else. // along, and nothing else.
if (m_analysis[i].bidiLevel == m_analysis[start].bidiLevel if (m_analysis[i].flags == m_analysis[start].flags
&& 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] == QLatin1Char('.'))
&& m_analysis[i].flags < QScriptAnalysis::SpaceTabOrObject && m_analysis[i].flags < QScriptAnalysis::SpaceTabOrObject
&& i - start < MaxItemLength) && i - start < MaxItemLength)
@ -99,88 +98,12 @@ private:
}; };
} }
// ----------------------------------------------------------------------------
//
// The BiDi algorithm
//
// ----------------------------------------------------------------------------
#if (BIDI_DEBUG >= 1)
static const char *directions[] = {
"DirL", "DirR", "DirEN", "DirES", "DirET", "DirAN", "DirCS", "DirB", "DirS", "DirWS", "DirON",
"DirLRE", "DirLRO", "DirAL", "DirRLE", "DirRLO", "DirPDF", "DirNSM", "DirBN", "DirLRI",
"DirRLI", "DirFSI", "DirPDI"
};
#endif
void QTextEngine::bidiReorder(int numItems, const quint8 *levels, int *visualOrder)
{
// first find highest and lowest levels
quint8 levelLow = 128;
quint8 levelHigh = 0;
int i = 0;
while (i < numItems) {
//printf("level = %d\n", r->level);
if (levels[i] > levelHigh)
levelHigh = levels[i];
if (levels[i] < levelLow)
levelLow = levels[i];
i++;
}
// implements reordering of the line (L2 according to BiDi spec):
// L2. From the highest level found in the text to the lowest odd level on each line,
// reverse any contiguous sequence of characters that are at that level or higher.
// reversing is only done up to the lowest odd level
if(!(levelLow%2)) levelLow++;
#if (BIDI_DEBUG >= 1)
// qDebug() << "reorderLine: lineLow = " << (uint)levelLow << ", lineHigh = " << (uint)levelHigh;
#endif
int count = numItems - 1;
for (i = 0; i < numItems; i++)
visualOrder[i] = i;
while(levelHigh >= levelLow) {
int i = 0;
while (i < count) {
while(i < count && levels[i] < levelHigh) i++;
int start = i;
while(i <= count && levels[i] >= levelHigh) i++;
int end = i-1;
if(start != end) {
//qDebug() << "reversing from " << start << " to " << end;
for(int j = 0; j < (end-start+1)/2; j++) {
int tmp = visualOrder[start+j];
visualOrder[start+j] = visualOrder[end-j];
visualOrder[end-j] = tmp;
}
}
i++;
}
levelHigh--;
}
#if (BIDI_DEBUG >= 1)
// qDebug() << "visual order is:";
// for (i = 0; i < numItems; i++)
// qDebug() << visualOrder[i];
#endif
}
// ask the font engine to find out which glyphs (as an index in the specific font) to use for the text in one item. // ask the font engine to find out which glyphs (as an index in the specific font) to use for the text in one item.
static bool stringToGlyphs(HB_ShaperItem *item, QGlyphLayout *glyphs, QFontEngine *fontEngine) static bool stringToGlyphs(HB_ShaperItem *item, QGlyphLayout *glyphs, QFontEngine *fontEngine)
{ {
int nGlyphs = item->num_glyphs; int nGlyphs = item->num_glyphs;
QTextEngine::ShaperFlags shaperFlags(QTextEngine::GlyphIndicesOnly); QTextEngine::ShaperFlags shaperFlags(QTextEngine::GlyphIndicesOnly);
if (item->item.bidiLevel % 2)
shaperFlags |= QTextEngine::RightToLeft;
bool result = fontEngine->stringToCMap(reinterpret_cast<const QChar *>(item->string + item->item.pos), item->item.length, glyphs, &nGlyphs, shaperFlags); bool result = fontEngine->stringToCMap(reinterpret_cast<const QChar *>(item->string + item->item.pos), item->item.length, glyphs, &nGlyphs, shaperFlags);
item->num_glyphs = nGlyphs; item->num_glyphs = nGlyphs;
@ -298,7 +221,6 @@ void QTextEngine::shapeTextWithHarfbuzz(int item) const
entire_shaper_item.item.script = (HB_Script)si.analysis.script; entire_shaper_item.item.script = (HB_Script)si.analysis.script;
entire_shaper_item.item.pos = si.position; entire_shaper_item.item.pos = si.position;
entire_shaper_item.item.length = length(item); entire_shaper_item.item.length = length(item);
entire_shaper_item.item.bidiLevel = si.analysis.bidiLevel;
entire_shaper_item.shaperFlags = 0; entire_shaper_item.shaperFlags = 0;
if (!kerningEnabled) if (!kerningEnabled)
@ -487,12 +409,6 @@ void QTextEngine::itemize() const
QSTACKARRAY(QScriptAnalysis, scriptAnalysis, length); QSTACKARRAY(QScriptAnalysis, scriptAnalysis, length);
QScriptAnalysis *analysis = scriptAnalysis; QScriptAnalysis *analysis = scriptAnalysis;
if (option.textDirection() == Qt::RightToLeft) {
for (int i = 0; i < length; ++i)
analysis[i].bidiLevel = 1;
layoutData->hasBidi = true;
}
const ushort *uc = reinterpret_cast<const ushort *>(layoutData->string.unicode()); const ushort *uc = reinterpret_cast<const ushort *>(layoutData->string.unicode());
const ushort *e = uc + length; const ushort *e = uc + length;
QUnicodeTables::Script lastScript = QUnicodeTables::Common; QUnicodeTables::Script lastScript = QUnicodeTables::Common;
@ -503,8 +419,6 @@ void QTextEngine::itemize() const
analysis->flags = QScriptAnalysis::Object; analysis->flags = QScriptAnalysis::Object;
break; break;
case QChar::LineSeparator: case QChar::LineSeparator:
if (analysis->bidiLevel % 2)
--analysis->bidiLevel;
analysis->script = QUnicodeTables::Common; analysis->script = QUnicodeTables::Common;
analysis->flags = QScriptAnalysis::LineOrParagraphSeparator; analysis->flags = QScriptAnalysis::LineOrParagraphSeparator;
if (option.flags() & QTextOption::ShowLineAndParagraphSeparators) if (option.flags() & QTextOption::ShowLineAndParagraphSeparators)
@ -513,14 +427,12 @@ void QTextEngine::itemize() const
case 9: // Tab case 9: // Tab
analysis->script = QUnicodeTables::Common; analysis->script = QUnicodeTables::Common;
analysis->flags = QScriptAnalysis::Tab; analysis->flags = QScriptAnalysis::Tab;
analysis->bidiLevel = 0;
break; break;
case 32: // Space case 32: // Space
case QChar::Nbsp: case QChar::Nbsp:
if (option.flags() & QTextOption::ShowTabsAndSpaces) { if (option.flags() & QTextOption::ShowTabsAndSpaces) {
analysis->script = QUnicodeTables::Common; analysis->script = QUnicodeTables::Common;
analysis->flags = QScriptAnalysis::Space; analysis->flags = QScriptAnalysis::Space;
analysis->bidiLevel = 0;
break; break;
} }
// fall through // fall through
@ -578,25 +490,6 @@ void QTextEngine::itemize() const
resolveAdditionalFormats(); resolveAdditionalFormats();
} }
bool QTextEngine::isRightToLeft() const
{
switch (option.textDirection()) {
case Qt::LeftToRight:
return false;
case Qt::RightToLeft:
return true;
default:
break;
}
if (!layoutData)
itemize();
// this places the cursor in the right position depending on the keyboard layout
if (layoutData->string.isEmpty())
return QApplication::keyboardInputDirection() == Qt::RightToLeft;
return layoutData->string.isRightToLeft();
}
int QTextEngine::findItem(int strPos) const int QTextEngine::findItem(int strPos) const
{ {
itemize(); itemize();
@ -1015,8 +908,7 @@ void QTextEngine::justify(const QScriptLine &line)
} }
} }
QFixed leading = leadingSpaceWidth(line); QFixed need = line.width - line.textWidth;
QFixed need = line.width - line.textWidth - leading;
if (need < 0) { if (need < 0) {
// line overflows already! // line overflows already!
const_cast<QScriptLine &>(line).justified = true; const_cast<QScriptLine &>(line).justified = true;
@ -1105,7 +997,6 @@ QTextEngine::LayoutData::LayoutData()
allocated = 0; allocated = 0;
memory_on_stack = false; memory_on_stack = false;
used = 0; used = 0;
hasBidi = false;
layoutState = LayoutEmpty; layoutState = LayoutEmpty;
haveCharAttributes = false; haveCharAttributes = false;
logClustersPtr = 0; logClustersPtr = 0;
@ -1139,7 +1030,6 @@ QTextEngine::LayoutData::LayoutData(const QString &str, void **stack_memory, int
::memset(memory, 0, space_charAttributes * QT_POINTER_SIZE); ::memset(memory, 0, space_charAttributes * QT_POINTER_SIZE);
} }
used = 0; used = 0;
hasBidi = false;
layoutState = LayoutEmpty; layoutState = LayoutEmpty;
haveCharAttributes = false; haveCharAttributes = false;
} }
@ -1225,7 +1115,6 @@ void QTextEngine::freeMemory()
layoutData = 0; layoutData = 0;
} else { } else {
layoutData->used = 0; layoutData->used = 0;
layoutData->hasBidi = false;
layoutData->layoutState = LayoutEmpty; layoutData->layoutState = LayoutEmpty;
layoutData->haveCharAttributes = false; layoutData->haveCharAttributes = false;
} }
@ -1566,20 +1455,6 @@ QFixed QTextEngine::calculateTabWidth(int item, QFixed x) const
QList<QTextOption::Tab> tabArray = option.tabs(); QList<QTextOption::Tab> tabArray = option.tabs();
if (!tabArray.isEmpty()) { if (!tabArray.isEmpty()) {
if (isRightToLeft()) { // rebase the tabArray positions.
QList<QTextOption::Tab> newTabs;
QList<QTextOption::Tab>::Iterator iter = tabArray.begin();
while(iter != tabArray.end()) {
QTextOption::Tab tab = *iter;
if (tab.type == QTextOption::LeftTab)
tab.type = QTextOption::RightTab;
else if (tab.type == QTextOption::RightTab)
tab.type = QTextOption::LeftTab;
newTabs << tab;
++iter;
}
tabArray = newTabs;
}
for (int i = 0; i < tabArray.size(); ++i) { for (int i = 0; i < tabArray.size(); ++i) {
QFixed tab = QFixed::fromReal(tabArray[i].position) * dpiScale; QFixed tab = QFixed::fromReal(tabArray[i].position) * dpiScale;
if (tab > x) { // this is the tab we need. if (tab > x) { // this is the tab we need.
@ -1663,16 +1538,6 @@ void QTextEngine::resolveAdditionalFormats() const
specialData->resolvedFormatIndices = indices; specialData->resolvedFormatIndices = indices;
} }
QFixed QTextEngine::leadingSpaceWidth(const QScriptLine &line)
{
if (!line.hasTrailingSpaces
|| (option.flags() & QTextOption::IncludeTrailingSpaces)
|| !isRightToLeft())
return QFixed();
return width(line.from + line.length, line.trailingSpaces);
}
QFixed QTextEngine::alignLine(const QScriptLine &line) QFixed QTextEngine::alignLine(const QScriptLine &line)
{ {
QFixed x = 0; QFixed x = 0;
@ -1680,8 +1545,6 @@ QFixed QTextEngine::alignLine(const QScriptLine &line)
// if width is QFIXED_MAX that means we used setNumColumns() and that implicitly makes this line left aligned. // if width is QFIXED_MAX that means we used setNumColumns() and that implicitly makes this line left aligned.
if (!line.justified && line.width != QFIXED_MAX) { if (!line.justified && line.width != QFIXED_MAX) {
int align = option.alignment(); int align = option.alignment();
if (align & Qt::AlignJustify && isRightToLeft())
align = Qt::AlignRight;
if (align & Qt::AlignRight) if (align & Qt::AlignRight)
x = line.width - (line.textAdvance); x = line.width - (line.textAdvance);
else if (align & Qt::AlignHCenter) else if (align & Qt::AlignHCenter)
@ -1838,25 +1701,16 @@ int QTextEngine::lineNumberForTextPosition(int pos)
void QTextEngine::insertionPointsForLine(int lineNum, QVector<int> &insertionPoints) void QTextEngine::insertionPointsForLine(int lineNum, QVector<int> &insertionPoints)
{ {
QTextLineItemIterator iterator(this, lineNum); QTextLineItemIterator iterator(this, lineNum);
bool rtl = isRightToLeft();
bool lastLine = lineNum >= lines.size() - 1; bool lastLine = lineNum >= lines.size() - 1;
while (!iterator.atEnd()) { while (!iterator.atEnd()) {
iterator.next(); iterator.next();
const QScriptItem *si = &layoutData->items[iterator.item]; const QScriptItem *si = &layoutData->items[iterator.item];
if (si->analysis.bidiLevel % 2) { int i = iterator.itemStart, max = iterator.itemEnd;
int i = iterator.itemEnd - 1, min = iterator.itemStart; if (lastLine && iterator.atEnd())
if (lastLine && (rtl ? iterator.atBeginning() : iterator.atEnd())) max++;
i++; for (; i < max; i++)
for (; i >= min; i--) insertionPoints.push_back(i);
insertionPoints.push_back(i);
} else {
int i = iterator.itemStart, max = iterator.itemEnd;
if (lastLine && (rtl ? iterator.atBeginning() : iterator.atEnd()))
max++;
for (; i < max; i++)
insertionPoints.push_back(i);
}
} }
} }
@ -1886,37 +1740,8 @@ int QTextEngine::positionAfterVisualMovement(int pos, QTextCursor::MoveOperation
itemize(); itemize();
bool moveRight = (op == QTextCursor::Right); bool moveRight = (op == QTextCursor::Right);
bool alignRight = isRightToLeft();
if (!layoutData->hasBidi)
return moveRight ^ alignRight ? nextLogicalPosition(pos) : previousLogicalPosition(pos);
int lineNum = lineNumberForTextPosition(pos); return (moveRight ? nextLogicalPosition(pos) : previousLogicalPosition(pos));
Q_ASSERT(lineNum >= 0);
QVector<int> insertionPoints;
insertionPointsForLine(lineNum, insertionPoints);
int i, max = insertionPoints.size();
for (i = 0; i < max; i++)
if (pos == insertionPoints[i]) {
if (moveRight) {
if (i + 1 < max)
return insertionPoints[i + 1];
} else {
if (i > 0)
return insertionPoints[i - 1];
}
if (moveRight ^ alignRight) {
if (lineNum + 1 < lines.size())
return alignRight ? endOfLine(lineNum + 1) : beginningOfLine(lineNum + 1);
}
else {
if (lineNum > 0)
return alignRight ? beginningOfLine(lineNum - 1) : endOfLine(lineNum - 1);
}
}
return pos;
} }
QStackTextEngine::QStackTextEngine(const QString &string, const QFont &f) QStackTextEngine::QStackTextEngine(const QString &string, const QFont &f)
@ -1950,8 +1775,6 @@ void QTextItemInt::initWithScriptItem(const QScriptItem &si)
// explicitly initialize flags so that initFontAttributes can be called // explicitly initialize flags so that initFontAttributes can be called
// multiple times on the same TextItem // multiple times on the same TextItem
flags = 0; flags = 0;
if (si.analysis.bidiLevel %2)
flags |= QTextItem::RightToLeft;
ascent = si.ascent; ascent = si.ascent;
descent = si.descent; descent = si.descent;
@ -1984,8 +1807,6 @@ QTextLineItemIterator::QTextLineItemIterator(QTextEngine *_eng, int _lineNum, co
nItems((firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0), nItems((firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0),
logicalItem(-1), logicalItem(-1),
item(-1), item(-1),
visualOrder(nItems),
levels(nItems),
selection(_selection) selection(_selection)
{ {
pos_x = x = QFixed::fromReal(pos.x()); pos_x = x = QFixed::fromReal(pos.x());
@ -1994,10 +1815,6 @@ QTextLineItemIterator::QTextLineItemIterator(QTextEngine *_eng, int _lineNum, co
x += eng->alignLine(line); x += eng->alignLine(line);
for (int i = 0; i < nItems; ++i)
levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
eng->shapeLine(line); eng->shapeLine(line);
} }
@ -2006,7 +1823,7 @@ QScriptItem &QTextLineItemIterator::next()
x += itemWidth; x += itemWidth;
++logicalItem; ++logicalItem;
item = visualOrder[logicalItem] + firstItem; item = (logicalItem + firstItem);
itemLength = eng->length(item); itemLength = eng->length(item);
si = &eng->layoutData->items[item]; si = &eng->layoutData->items[item];
if (!si->num_glyphs) if (!si->num_glyphs)
@ -2068,17 +1885,10 @@ bool QTextLineItemIterator::getSelectionBounds(QFixed *selectionX, QFixed *selec
int end_glyph = (to == eng->length(item)) ? si->num_glyphs : logClusters[to]; int end_glyph = (to == eng->length(item)) ? si->num_glyphs : logClusters[to];
QFixed soff; QFixed soff;
QFixed swidth; QFixed swidth;
if (si->analysis.bidiLevel %2) { for (int g = glyphsStart; g < start_glyph; ++g)
for (int g = glyphsEnd - 1; g >= end_glyph; --g) soff += glyphs.effectiveAdvance(g);
soff += glyphs.effectiveAdvance(g); for (int g = start_glyph; g < end_glyph; ++g)
for (int g = end_glyph - 1; g >= start_glyph; --g) swidth += glyphs.effectiveAdvance(g);
swidth += glyphs.effectiveAdvance(g);
} else {
for (int g = glyphsStart; g < start_glyph; ++g)
soff += glyphs.effectiveAdvance(g);
for (int g = start_glyph; g < end_glyph; ++g)
swidth += glyphs.effectiveAdvance(g);
}
// If the starting character is in the middle of a ligature, // If the starting character is in the middle of a ligature,
// selection should only contain the right part of that ligature // selection should only contain the right part of that ligature

View file

@ -90,10 +90,9 @@ struct Q_AUTOTEST_EXPORT QScriptAnalysis
Object = 4 Object = 4
}; };
QUnicodeTables::Script script; QUnicodeTables::Script script;
unsigned short bidiLevel; // Unicode Bidi algorithm embedding level (0-61)
Flags flags; Flags flags;
inline bool operator == (const QScriptAnalysis &other) const { inline bool operator == (const QScriptAnalysis &other) const {
return script == other.script && bidiLevel == other.bidiLevel && flags == other.flags; return script == other.script && flags == other.flags;
} }
}; };
Q_DECLARE_TYPEINFO(QScriptAnalysis, Q_PRIMITIVE_TYPE); Q_DECLARE_TYPEINFO(QScriptAnalysis, Q_PRIMITIVE_TYPE);
@ -356,7 +355,6 @@ public:
unsigned short *logClustersPtr; unsigned short *logClustersPtr;
QGlyphLayout glyphLayout; QGlyphLayout glyphLayout;
mutable int used; mutable int used;
bool hasBidi;
LayoutState layoutState; LayoutState layoutState;
bool memory_on_stack; bool memory_on_stack;
bool haveCharAttributes; bool haveCharAttributes;
@ -383,9 +381,6 @@ public:
void validate() const; void validate() const;
void itemize() const; void itemize() const;
bool isRightToLeft() const;
static void bidiReorder(int numRuns, const quint8 *levels, int *visualOrder);
const HB_CharAttributes *attributes() const; const HB_CharAttributes *attributes() const;
void shape(int item) const; void shape(int item) const;
@ -509,7 +504,6 @@ public:
QString elidedText(Qt::TextElideMode mode, const QFixed &width, int flags = 0) const; QString elidedText(Qt::TextElideMode mode, const QFixed &width, int flags = 0) const;
void shapeLine(const QScriptLine &line); void shapeLine(const QScriptLine &line);
QFixed leadingSpaceWidth(const QScriptLine &line);
QFixed offsetInLigature(const QScriptItem *si, int pos, int max, int glyph_pos); QFixed offsetInLigature(const QScriptItem *si, int pos, int max, int glyph_pos);
int positionInLigature(const QScriptItem *si, int end, QFixed x, QFixed edge, int glyph_pos, bool cursorOnCharacter); int positionInLigature(const QScriptItem *si, int end, QFixed x, QFixed edge, int glyph_pos, bool cursorOnCharacter);
@ -577,9 +571,6 @@ struct QTextLineItemIterator
QFixed itemWidth; QFixed itemWidth;
QVarLengthArray<int> visualOrder;
QVarLengthArray<uchar> levels;
const QTextLayout::FormatRange *selection; const QTextLayout::FormatRange *selection;
}; };

View file

@ -224,7 +224,7 @@ QTextFormat QTextInlineObject::format() const
*/ */
Qt::LayoutDirection QTextInlineObject::textDirection() const Qt::LayoutDirection QTextInlineObject::textDirection() const
{ {
return (eng->layoutData->items[itm].analysis.bidiLevel % 2 ? Qt::RightToLeft : Qt::LeftToRight); return Qt::LeftToRight;
} }
/*! /*!
@ -1027,7 +1027,6 @@ void QTextLayout::draw(QPainter *p, const QPointF &pos, const QVector<FormatRang
QRectF lineRect(tl.naturalTextRect()); QRectF lineRect(tl.naturalTextRect());
lineRect.translate(position); lineRect.translate(position);
lineRect.adjust(0, 0, d->leadingSpaceWidth(sl).toReal(), 0);
bool isLastLineInBlock = (line == d->lines.size()-1); bool isLastLineInBlock = (line == d->lines.size()-1);
int sl_length = sl.length + (isLastLineInBlock? 1 : 0); // the infamous newline int sl_length = sl.length + (isLastLineInBlock? 1 : 0); // the infamous newline
@ -1202,14 +1201,12 @@ void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition
QFixed base = sl.base(); QFixed base = sl.base();
QFixed descent = sl.descent; QFixed descent = sl.descent;
bool rightToLeft = d->isRightToLeft();
if (itm >= 0) { if (itm >= 0) {
const QScriptItem &si = d->layoutData->items.at(itm); const QScriptItem &si = d->layoutData->items.at(itm);
if (si.ascent > 0) if (si.ascent > 0)
base = si.ascent; base = si.ascent;
if (si.descent > 0) if (si.descent > 0)
descent = si.descent; descent = si.descent;
rightToLeft = si.analysis.bidiLevel % 2;
} }
qreal y = position.y() + (sl.y + sl.base() - base).toReal(); qreal y = position.y() + (sl.y + sl.base() - base).toReal();
bool toggleAntialiasing = !(p->renderHints() & QPainter::Antialiasing) bool toggleAntialiasing = !(p->renderHints() & QPainter::Antialiasing)
@ -1219,13 +1216,6 @@ void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition
p->fillRect(QRectF(x, y, qreal(width), (base + descent + 1).toReal()), p->pen().brush()); p->fillRect(QRectF(x, y, qreal(width), (base + descent + 1).toReal()), p->pen().brush());
if (toggleAntialiasing) if (toggleAntialiasing)
p->setRenderHint(QPainter::Antialiasing, false); p->setRenderHint(QPainter::Antialiasing, false);
if (d->layoutData->hasBidi) {
const int arrow_extent = 4;
int sign = rightToLeft ? -1 : 1;
p->drawLine(QLineF(x, y, x + (sign * arrow_extent/2), y + arrow_extent/2));
p->drawLine(QLineF(x, y+arrow_extent, x + (sign * arrow_extent/2), y + arrow_extent/2));
}
return;
} }
/*! /*!
@ -1966,9 +1956,6 @@ static void drawMenuText(QPainter *p, QFixed x, QFixed y, const QScriptItem &si,
if (ul) if (ul)
while (*ul != -1 && *ul < start) while (*ul != -1 && *ul < start)
++ul; ++ul;
bool rtl = si.analysis.bidiLevel % 2;
if (rtl)
x += si.width;
do { do {
int gtmp = ge; int gtmp = ge;
@ -1988,12 +1975,9 @@ static void drawMenuText(QPainter *p, QFixed x, QFixed y, const QScriptItem &si,
} }
start = stmp; start = stmp;
gf.width = w; gf.width = w;
if (rtl)
x -= w;
if (gf.num_chars) if (gf.num_chars)
p->drawTextItem(QPointF(x.toReal(), y.toReal()), gf); p->drawTextItem(QPointF(x.toReal(), y.toReal()), gf);
if (!rtl) x += w;
x += w;
if (ul && *ul != -1 && *ul < end) { if (ul && *ul != -1 && *ul < end) {
// draw underline // draw underline
gtmp = (*ul == end-1) ? ge : logClusters[*ul+1-si.position]; gtmp = (*ul == end-1) ? ge : logClusters[*ul+1-si.position];
@ -2010,11 +1994,8 @@ static void drawMenuText(QPainter *p, QFixed x, QFixed y, const QScriptItem &si,
++start; ++start;
gf.width = w; gf.width = w;
gf.underlineStyle = QTextCharFormat::SingleUnderline; gf.underlineStyle = QTextCharFormat::SingleUnderline;
if (rtl)
x -= w;
p->drawTextItem(QPointF(x.toReal(), y.toReal()), gf); p->drawTextItem(QPointF(x.toReal(), y.toReal()), gf);
if (!rtl) x += w;
x += w;
gf.underlineStyle = QTextCharFormat::NoUnderline; gf.underlineStyle = QTextCharFormat::NoUnderline;
++gf.chars; ++gf.chars;
++ul; ++ul;
@ -2279,7 +2260,7 @@ qreal QTextLine::cursorToX(int *cursorPos, Edge edge) const
bool lastLine = i >= eng->lines.size() - 1; bool lastLine = i >= eng->lines.size() - 1;
QFixed x = line.x; QFixed x = line.x;
x += eng->alignLine(line) - eng->leadingSpaceWidth(line); x += eng->alignLine(line);
if (!i && !eng->layoutData->items.size()) { if (!i && !eng->layoutData->items.size()) {
*cursorPos = 0; *cursorPos = 0;
@ -2326,23 +2307,14 @@ qreal QTextLine::cursorToX(int *cursorPos, Edge edge) const
glyph_pos++; glyph_pos++;
} }
bool reverse = eng->layoutData->items[itm].analysis.bidiLevel % 2;
// add the items left of the cursor // add the items left of the cursor
int firstItem = eng->findItem(line.from); int firstItem = eng->findItem(line.from);
int lastItem = eng->findItem(lineEnd - 1); int lastItem = eng->findItem(lineEnd - 1);
int nItems = (firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0; int nItems = (firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0;
QVarLengthArray<int> visualOrder(nItems);
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());
for (int i = 0; i < nItems; ++i) { for (int i = 0; i < nItems; ++i) {
int item = visualOrder[i]+firstItem; int item = (i + firstItem);
if (item == itm) if (item == itm)
break; break;
QScriptItem &si = eng->layoutData->items[item]; QScriptItem &si = eng->layoutData->items[item];
@ -2372,27 +2344,17 @@ qreal QTextLine::cursorToX(int *cursorPos, Edge edge) const
logClusters = eng->logClusters(si); logClusters = eng->logClusters(si);
glyphs = eng->shapedGlyphs(si); glyphs = eng->shapedGlyphs(si);
if (si->analysis.flags >= QScriptAnalysis::TabOrObject) { if (si->analysis.flags >= QScriptAnalysis::TabOrObject) {
if (pos == (reverse ? 0 : l)) if (pos == l)
x += si->width; x += si->width;
} else { } else {
bool rtl = eng->isRightToLeft();
bool visual = eng->visualCursorMovement(); bool visual = eng->visualCursorMovement();
int end = qMin(lineEnd, si->position + l) - si->position; int end = qMin(lineEnd, si->position + l) - si->position;
if (reverse) { int start = qMax(line.from - si->position, 0);
int glyph_end = end == l ? si->num_glyphs : logClusters[end]; int glyph_start = logClusters[start];
int glyph_start = glyph_pos; int glyph_end = glyph_pos;
if (visual && !rtl && !(lastLine && itm == (visualOrder[nItems - 1] + firstItem))) glyph_end--;
glyph_start++; for (int i = glyph_start; i <= glyph_end; i++) {
for (int i = glyph_end - 1; i >= glyph_start; i--) x += glyphs.effectiveAdvance(i);
x += glyphs.effectiveAdvance(i);
} else {
int start = qMax(line.from - si->position, 0);
int glyph_start = logClusters[start];
int glyph_end = glyph_pos;
if (!visual || !rtl || (lastLine && itm == visualOrder[0] + firstItem))
glyph_end--;
for (int i = glyph_start; i <= glyph_end; i++)
x += glyphs.effectiveAdvance(i);
} }
x += eng->offsetInLigature(si, pos, end, glyph_pos); x += eng->offsetInLigature(si, pos, end, glyph_pos);
} }
@ -2441,22 +2403,14 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
x -= eng->alignLine(line); x -= eng->alignLine(line);
// qDebug("xToCursor: x=%f, cpos=%d", x.toReal(), cpos); // qDebug("xToCursor: x=%f, cpos=%d", x.toReal(), cpos);
QSTACKARRAY(int, visualOrder, nItems);
QSTACKARRAY(unsigned char, levels, nItems);
for (int i = 0; i < nItems; ++i)
levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
QTextEngine::bidiReorder(nItems, levels, visualOrder);
bool visual = eng->visualCursorMovement(); bool visual = eng->visualCursorMovement();
if (x <= 0) { if (x <= 0) {
// left of first item // left of first item
int item = visualOrder[0]+firstItem; int item = firstItem;
QScriptItem &si = eng->layoutData->items[item]; QScriptItem &si = eng->layoutData->items[item];
if (!si.num_glyphs) if (!si.num_glyphs)
eng->shape(item); eng->shape(item);
int pos = si.position; int pos = si.position;
if (si.analysis.bidiLevel % 2)
pos += eng->length(item);
pos = qMax(line.from, pos); pos = qMax(line.from, pos);
pos = qMin(line.from + line_length, pos); pos = qMin(line.from + line_length, pos);
return pos; return pos;
@ -2464,15 +2418,11 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
|| (line.justified && x < line.width)) { || (line.justified && x < line.width)) {
// has to be in one of the runs // has to be in one of the runs
QFixed pos; QFixed pos;
bool rtl = eng->isRightToLeft();
eng->shapeLine(line); eng->shapeLine(line);
QVector<int> insertionPoints;
if (visual && rtl)
eng->insertionPointsForLine(lineNum, insertionPoints);
int nchars = 0; int nchars = 0;
for (int i = 0; i < nItems; ++i) { for (int i = 0; i < nItems; ++i) {
int item = visualOrder[i]+firstItem; int item = (i + firstItem);
QScriptItem &si = eng->layoutData->items[item]; QScriptItem &si = eng->layoutData->items[item];
int item_length = eng->length(item); int item_length = eng->length(item);
// qDebug(" item %d, visual %d x_remain=%f", i, item, x.toReal()); // qDebug(" item %d, visual %d x_remain=%f", i, item, x.toReal());
@ -2509,7 +2459,7 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
return si.position; return si.position;
bool left_half = (x - pos) < item_width/2; bool left_half = (x - pos) < item_width/2;
if (bool(si.analysis.bidiLevel % 2) != left_half) if (left_half)
return si.position; return si.position;
return si.position + 1; return si.position + 1;
} }
@ -2518,91 +2468,34 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
QFixed edge; QFixed edge;
// has to be inside run // has to be inside run
if (cpos == QTextLine::CursorOnCharacter) { if (cpos == QTextLine::CursorOnCharacter) {
if (si.analysis.bidiLevel % 2) { glyph_pos = gs;
pos += item_width; while (gs <= ge) {
glyph_pos = gs; if (glyphs.attributes[gs].clusterStart) {
while (gs <= ge) { if (pos > x)
if (glyphs.attributes[gs].clusterStart) {
if (pos < x)
break;
glyph_pos = gs;
edge = pos;
break; break;
} glyph_pos = gs;
pos -= glyphs.effectiveAdvance(gs); edge = pos;
++gs;
}
} else {
glyph_pos = gs;
while (gs <= ge) {
if (glyphs.attributes[gs].clusterStart) {
if (pos > x)
break;
glyph_pos = gs;
edge = pos;
}
pos += glyphs.effectiveAdvance(gs);
++gs;
} }
pos += glyphs.effectiveAdvance(gs);
++gs;
} }
} else { } else {
QFixed dist = INT_MAX/256; QFixed dist = INT_MAX/256;
if (si.analysis.bidiLevel % 2) { while (gs <= ge) {
if (!visual || rtl || (lastLine && i == nItems - 1)) { if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
pos += item_width; glyph_pos = gs;
while (gs <= ge) { edge = pos;
if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) { dist = qAbs(x-pos);
glyph_pos = gs;
edge = pos;
dist = qAbs(x-pos);
}
pos -= glyphs.effectiveAdvance(gs);
++gs;
}
} else {
while (ge >= gs) {
if (glyphs.attributes[ge].clusterStart && qAbs(x-pos) < dist) {
glyph_pos = ge;
edge = pos;
dist = qAbs(x-pos);
}
pos += glyphs.effectiveAdvance(ge);
--ge;
}
}
} else {
if (!visual || !rtl || (lastLine && i == 0)) {
while (gs <= ge) {
if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
glyph_pos = gs;
edge = pos;
dist = qAbs(x-pos);
}
pos += glyphs.effectiveAdvance(gs);
++gs;
}
} else {
QFixed oldPos = pos;
while (gs <= ge) {
pos += glyphs.effectiveAdvance(gs);
if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
glyph_pos = gs;
edge = pos;
dist = qAbs(x-pos);
}
++gs;
}
pos = oldPos;
} }
pos += glyphs.effectiveAdvance(gs);
++gs;
} }
if (qAbs(x-pos) < dist) { if (qAbs(x-pos) < dist) {
if (visual) { if (visual) {
if (!rtl && i < nItems - 1) { if (i < nItems - 1) {
nchars += end; nchars += end;
continue; continue;
} }
if (rtl && nchars > 0)
return insertionPoints[lastLine ? nchars : nchars - 1];
} }
return eng->positionInLigature(&si, end, x, pos, -1, return eng->positionInLigature(&si, end, x, pos, -1,
cpos == QTextLine::CursorOnCharacter); cpos == QTextLine::CursorOnCharacter);
@ -2615,13 +2508,12 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
} }
// right of last item // right of last item
// qDebug() << "right of last"; // qDebug() << "right of last";
int item = visualOrder[nItems-1]+firstItem; int item = (nItems -1 + firstItem);
QScriptItem &si = eng->layoutData->items[item]; QScriptItem &si = eng->layoutData->items[item];
if (!si.num_glyphs) if (!si.num_glyphs)
eng->shape(item); eng->shape(item);
int pos = si.position; int pos = si.position;
if (!(si.analysis.bidiLevel % 2)) pos += eng->length(item);
pos += eng->length(item);
pos = qMax(line.from, pos); pos = qMax(line.from, pos);
int maxPos = line.from + line_length; int maxPos = line.from + line_length;