Skip boundry neutral characters in bidi itemization
[profile/ivi/qtbase.git] / src / gui / text / qtextengine.cpp
index dab4bb7..a7e4748 100644 (file)
@@ -7,29 +7,29 @@
 ** This file is part of the QtGui module of the Qt Toolkit.
 **
 ** $QT_BEGIN_LICENSE:LGPL$
-** No Commercial Usage
-** This file contains pre-release code and may not be distributed.
-** You may use this file in accordance with the terms and conditions
-** contained in the Technology Preview License Agreement accompanying
-** this package.
-**
 ** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file.  Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
 **
 ** In addition, as a special exception, Nokia gives you certain additional
-** rights.  These rights are described in the Nokia Qt LGPL Exception
+** rights. These rights are described in the Nokia Qt LGPL Exception
 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
 **
-** If you have questions regarding the use of this file, please contact
-** Nokia at qt-info@nokia.com.
-**
-**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
 **
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
 **
 **
 **
@@ -319,6 +319,26 @@ static void appendItems(QScriptAnalysis *analysis, int &start, int &stop, const
     start = stop;
 }
 
+static QChar::Direction skipBoundryNeutrals(QScriptAnalysis *analysis,
+                                            const ushort *unicode, int length,
+                                            int &sor, int &eor, QBidiControl &control)
+{
+    QChar::Direction dir;
+    int level = sor > 0 ? analysis[sor - 1].bidiLevel : control.level;
+    while (sor < length) {
+        dir = QChar::direction(unicode[sor]);
+        // Keep skipping DirBN as if it doesn't exist
+        if (dir != QChar::DirBN)
+            break;
+        analysis[sor++].bidiLevel = level;
+    }
+
+    eor = sor;
+    if (eor == length)
+        dir = control.basicDirection();
+
+    return dir;
+}
 
 // creates the next QScript items.
 static bool bidiItemize(QTextEngine *engine, QScriptAnalysis *analysis, QBidiControl &control)
@@ -430,8 +450,7 @@ static bool bidiItemize(QTextEngine *engine, QScriptAnalysis *analysis, QBidiCon
                 case QChar::DirAN:
                     if (eor >= 0) {
                         appendItems(analysis, sor, eor, control, dir);
-                        dir = eor < length ? QChar::direction(unicode[eor]) : control.basicDirection();
-                        status.eor = dir;
+                        status.eor = dir = skipBoundryNeutrals(analysis, unicode, length, sor, eor, control);
                     } else {
                         eor = current; status.eor = dir;
                     }
@@ -455,8 +474,7 @@ static bool bidiItemize(QTextEngine *engine, QScriptAnalysis *analysis, QBidiCon
                             }
                             eor = current - 1;
                             appendItems(analysis, sor, eor, control, dir);
-                            dir = eor < length ? QChar::direction(unicode[eor]) : control.basicDirection();
-                            status.eor = dir;
+                            status.eor = dir = skipBoundryNeutrals(analysis, unicode, length, sor, eor, control);
                         } else {
                             if(status.eor != QChar::DirL) {
                                 appendItems(analysis, sor, eor, control, dir);
@@ -856,7 +874,7 @@ void QTextEngine::shapeLine(const QScriptLine &line)
     }
 }
 
-#if !defined(QT_ENABLE_HARFBUZZ_FOR_MAC)
+#if !defined(QT_ENABLE_HARFBUZZ_FOR_MAC) && defined(Q_WS_MAC)
 static bool enableHarfBuzz()
 {
     static enum { Yes, No, Unknown } status = Unknown;
@@ -1274,6 +1292,10 @@ void QTextEngine::shapeTextWithHarfbuzz(int item) const
             actualFontEngine = static_cast<QFontEngineMulti *>(font)->engine(engineIdx);
         }
 
+        si.ascent = qMax(actualFontEngine->ascent(), si.ascent);
+        si.descent = qMax(actualFontEngine->descent(), si.descent);
+        si.leading = qMax(actualFontEngine->leading(), si.leading);
+
         shaper_item.font = actualFontEngine->harfbuzzFont();
         shaper_item.face = actualFontEngine->harfbuzzFace();
 
@@ -1598,6 +1620,8 @@ bool QTextEngine::isRightToLeft() const
     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;
@@ -2777,12 +2801,14 @@ 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 (!line.justified && line.width != QFIXED_MAX) {
         int align = option.alignment();
+        if (align & Qt::AlignLeft)
+            x -= leadingSpaceWidth(line);
         if (align & Qt::AlignJustify && isRightToLeft())
             align = Qt::AlignRight;
         if (align & Qt::AlignRight)
             x = line.width - (line.textAdvance + leadingSpaceWidth(line));
         else if (align & Qt::AlignHCenter)
-            x = (line.width - line.textAdvance)/2;
+            x = (line.width - line.textAdvance)/2 - leadingSpaceWidth(line);
     }
     return x;
 }
@@ -2817,6 +2843,76 @@ QFixed QTextEngine::offsetInLigature(const QScriptItem *si, int pos, int max, in
     return 0;
 }
 
+// Scan in logClusters[from..to-1] for glyph_pos
+int QTextEngine::getClusterLength(unsigned short *logClusters,
+                                  const HB_CharAttributes *attributes,
+                                  int from, int to, int glyph_pos, int *start)
+{
+    int clusterLength = 0;
+    for (int i = from; i < to; i++) {
+        if (logClusters[i] == glyph_pos && attributes[i].charStop) {
+            if (*start < 0)
+                *start = i;
+            clusterLength++;
+        }
+        else if (clusterLength)
+            break;
+    }
+    return clusterLength;
+}
+
+int QTextEngine::positionInLigature(const QScriptItem *si, int end,
+                                    QFixed x, QFixed edge, int glyph_pos,
+                                    bool cursorOnCharacter)
+{
+    unsigned short *logClusters = this->logClusters(si);
+    int clusterStart = -1;
+    int clusterLength = 0;
+
+    if (si->analysis.script != QUnicodeTables::Common &&
+        si->analysis.script != QUnicodeTables::Greek) {
+        if (glyph_pos == -1)
+            return si->position + end;
+        else {
+            int i;
+            for (i = 0; i < end; i++)
+                if (logClusters[i] == glyph_pos)
+                    break;
+            return si->position + i;
+        }
+    }
+
+    if (glyph_pos == -1 && end > 0)
+        glyph_pos = logClusters[end - 1];
+    else {
+        if (x <= edge)
+            glyph_pos--;
+    }
+
+    const HB_CharAttributes *attrs = attributes();
+    logClusters = this->logClusters(si);
+    clusterLength = getClusterLength(logClusters, attrs, 0, end, glyph_pos, &clusterStart);
+
+    if (clusterLength) {
+        const QGlyphLayout &glyphs = shapedGlyphs(si);
+        QFixed glyphWidth = glyphs.effectiveAdvance(glyph_pos);
+        // the approximate width of each individual element of the ligature
+        QFixed perItemWidth = glyphWidth / clusterLength;
+        QFixed left = x > edge ? edge : edge - glyphWidth;
+        int n = ((x - left) / perItemWidth).floor().toInt();
+        QFixed dist = x - left - n * perItemWidth;
+        int closestItem = dist > (perItemWidth / 2) ? n + 1 : n;
+        if (cursorOnCharacter && closestItem > 0)
+            closestItem--;
+        int pos = si->position + clusterStart + closestItem;
+        // Jump to the next charStop
+        while (!attrs[pos].charStop && pos < end)
+            pos++;
+        return pos;
+    }
+    return si->position + end;
+}
+
 int QTextEngine::previousLogicalPosition(int oldPos) const
 {
     const HB_CharAttributes *attrs = attributes();
@@ -2956,6 +3052,22 @@ QTextItemInt::QTextItemInt(const QScriptItem &si, QFont *font, const QTextCharFo
     : justified(false), underlineStyle(QTextCharFormat::NoUnderline), charFormat(format),
       num_chars(0), chars(0), logClusters(0), f(0), fontEngine(0)
 {
+    f = font;
+    fontEngine = f->d->engineForScript(si.analysis.script);
+    Q_ASSERT(fontEngine);
+
+    initWithScriptItem(si);
+}
+
+QTextItemInt::QTextItemInt(const QGlyphLayout &g, QFont *font, const QChar *chars_, int numChars, QFontEngine *fe, const QTextCharFormat &format)
+    : flags(0), justified(false), underlineStyle(QTextCharFormat::NoUnderline), charFormat(format),
+      num_chars(numChars), chars(chars_), logClusters(0), f(font),  glyphs(g), fontEngine(fe)
+{
+}
+
+// Fix up flags and underlineStyle with given info
+void QTextItemInt::initWithScriptItem(const QScriptItem &si)
+{
     // explicitly initialize flags so that initFontAttributes can be called
     // multiple times on the same TextItem
     flags = 0;
@@ -2963,13 +3075,10 @@ QTextItemInt::QTextItemInt(const QScriptItem &si, QFont *font, const QTextCharFo
         flags |= QTextItem::RightToLeft;
     ascent = si.ascent;
     descent = si.descent;
-    f = font;
-    fontEngine = f->d->engineForScript(si.analysis.script);
-    Q_ASSERT(fontEngine);
 
-    if (format.hasProperty(QTextFormat::TextUnderlineStyle)) {
-        underlineStyle = format.underlineStyle();
-    } else if (format.boolProperty(QTextFormat::FontUnderline)
+    if (charFormat.hasProperty(QTextFormat::TextUnderlineStyle)) {
+        underlineStyle = charFormat.underlineStyle();
+    } else if (charFormat.boolProperty(QTextFormat::FontUnderline)
                || f->d->underline) {
         underlineStyle = QTextCharFormat::SingleUnderline;
     }
@@ -2978,18 +3087,12 @@ QTextItemInt::QTextItemInt(const QScriptItem &si, QFont *font, const QTextCharFo
     if (underlineStyle == QTextCharFormat::SingleUnderline)
         flags |= QTextItem::Underline;
 
-    if (f->d->overline || format.fontOverline())
+    if (f->d->overline || charFormat.fontOverline())
         flags |= QTextItem::Overline;
-    if (f->d->strikeOut || format.fontStrikeOut())
+    if (f->d->strikeOut || charFormat.fontStrikeOut())
         flags |= QTextItem::StrikeOut;
 }
 
-QTextItemInt::QTextItemInt(const QGlyphLayout &g, QFont *font, const QChar *chars_, int numChars, QFontEngine *fe)
-    : flags(0), justified(false), underlineStyle(QTextCharFormat::NoUnderline),
-      num_chars(numChars), chars(chars_), logClusters(0), f(font),  glyphs(g), fontEngine(fe)
-{
-}
-
 QTextItemInt QTextItemInt::midItem(QFontEngine *fontEngine, int firstGlyphIndex, int numGlyphs) const
 {
     QTextItemInt ti = *this;