A step out from Harfbuzz (reduce dependency)
authorKonstantin Ritt <ritt.ks@gmail.com>
Sun, 26 Aug 2012 01:09:09 +0000 (04:09 +0300)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Fri, 21 Sep 2012 22:47:40 +0000 (00:47 +0200)
Introduce QCharAttributes and use it instead of HB_CharAttributes everywhere in Qt
(in Harfbuzz, the HB_CharAttributes is only used in the text segmentation algorithm
which has been moved from HB to Qt (well, most of it)).
Rename some members to better reflect their meaning,
remember to keep HB_CharAttributes in sync with QCharAttributes.
Also replace HB_ScriptItem with a (temporary) QUnicodeTools::ScriptItem struct
that will be replaced with a more efficient/friendly solution a bit later.

The soft hyphen and the mandatory break detection has been factored out
of the default text breaking algorithm to a higher level in order to refactor
the QCharAttributes bitfields and to optimize the implementation for the common case.

Change-Id: Ieb365623ae954430f1c8b2dfcd65c82973143eec
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
15 files changed:
src/3rdparty/harfbuzz/src/harfbuzz-indic.cpp
src/3rdparty/harfbuzz/src/harfbuzz-khmer.c
src/3rdparty/harfbuzz/src/harfbuzz-myanmar.c
src/3rdparty/harfbuzz/src/harfbuzz-shaper.h
src/3rdparty/harfbuzz/src/harfbuzz-thai.c
src/3rdparty/harfbuzz/src/harfbuzz-tibetan.c
src/corelib/tools/qharfbuzz_p.h
src/corelib/tools/qtextboundaryfinder.cpp
src/corelib/tools/qunicodetools.cpp
src/corelib/tools/qunicodetools_p.h
src/gui/text/qharfbuzz_copy_p.h
src/gui/text/qtextengine.cpp
src/gui/text/qtextengine_p.h
src/gui/text/qtextlayout.cpp
tests/auto/gui/text/qtextlayout/tst_qtextlayout.cpp

index bbf479e..0dcddc4 100644 (file)
@@ -1880,12 +1880,12 @@ void HB_IndicAttributes(HB_Script script, const HB_UChar16 *text, hb_uint32 from
     while (i < len) {
         bool invalid;
         hb_uint32 boundary = indic_nextSyllableBoundary(script, text, from+i, end, &invalid) - from;
-         attributes[i].charStop = true;
+         attributes[i].graphemeBoundary = true;
 
         if (boundary > len-1) boundary = len;
         i++;
         while (i < boundary) {
-            attributes[i].charStop = false;
+            attributes[i].graphemeBoundary = false;
             ++uc;
             ++i;
         }
index 958069e..1f3c7e2 100644 (file)
@@ -652,12 +652,12 @@ void HB_KhmerAttributes(HB_Script script, const HB_UChar16 *text, hb_uint32 from
        HB_Bool invalid;
        hb_uint32 boundary = khmer_nextSyllableBoundary( text, from+i, end, &invalid ) - from;
 
-       attributes[i].charStop = TRUE;
+    attributes[i].graphemeBoundary = TRUE;
 
        if ( boundary > len-1 ) boundary = len;
        i++;
        while ( i < boundary ) {
-           attributes[i].charStop = FALSE;
+        attributes[i].graphemeBoundary = FALSE;
            ++uc;
            ++i;
        }
index 1327b18..b18b830 100644 (file)
@@ -520,14 +520,14 @@ void HB_MyanmarAttributes(HB_Script script, const HB_UChar16 *text, hb_uint32 fr
        HB_Bool invalid;
        hb_uint32 boundary = myanmar_nextSyllableBoundary(text, from+i, end, &invalid) - from;
 
-       attributes[i].charStop = TRUE;
-    attributes[i].lineBreakType = HB_Break;
+    attributes[i].graphemeBoundary = TRUE;
+    attributes[i].lineBreak = TRUE;
 
        if (boundary > len-1)
             boundary = len;
        i++;
        while (i < boundary) {
-           attributes[i].charStop = FALSE;
+        attributes[i].graphemeBoundary = FALSE;
            ++uc;
            ++i;
        }
index 6dfcdd2..2ff52ea 100644 (file)
@@ -126,21 +126,14 @@ typedef struct
     hb_uint8 bidiLevel;
 } HB_ScriptItem;
 
-typedef enum {
-    HB_NoBreak,
-    HB_SoftHyphen,
-    HB_Break,
-    HB_ForcedBreak
-} HB_LineBreakType;
-
 
 typedef struct {
-    /*HB_LineBreakType*/ hb_bitfield lineBreakType  :2;
-    /*HB_Bool*/ hb_bitfield whiteSpace              :1;     /* A unicode whitespace character, except NBSP, ZWNBSP */
-    /*HB_Bool*/ hb_bitfield charStop                :1;     /* Valid cursor position (for left/right arrow) */
-    /*HB_Bool*/ hb_bitfield wordBoundary            :1;
-    /*HB_Bool*/ hb_bitfield sentenceBoundary        :1;
-    hb_bitfield unused                  :2;
+    hb_bitfield graphemeBoundary : 1;     /* Valid cursor position (for left/right arrow) */
+    hb_bitfield wordBreak        : 1;
+    hb_bitfield sentenceBoundary : 1;
+    hb_bitfield lineBreak        : 1;
+    hb_bitfield whiteSpace       : 1;     /* A unicode whitespace character */
+    hb_bitfield unused           : 3;
 } HB_CharAttributes;
 
 void HB_GetTailoredCharAttributes(const HB_UChar16 *string, hb_uint32 stringLength,
index ecb722d..70c1d57 100644 (file)
@@ -395,8 +395,8 @@ static void HB_ThaiAssignAttributes(const HB_UChar16 *string, hb_uint32 len, HB_
     to_tis620(string, len, cstr);
 
     for (i = 0; i < len; ++i) {
-        attributes[i].lineBreakType = HB_NoBreak;
-        attributes[i].wordBoundary = FALSE;
+        attributes[i].lineBreak = FALSE;
+        attributes[i].wordBreak = FALSE;
     }
 
     if (len > 128) {
@@ -410,29 +410,29 @@ static void HB_ThaiAssignAttributes(const HB_UChar16 *string, hb_uint32 len, HB_
     }
 
     if (break_positions) {
-        attributes[0].wordBoundary = TRUE;
+        attributes[0].wordBreak = TRUE;
         numbreaks = th_brk((const unsigned char *)cstr, break_positions, brp_size);
         for (i = 0; i < numbreaks; ++i) {
-            attributes[break_positions[i]].wordBoundary = TRUE;
-            attributes[break_positions[i]].lineBreakType = HB_Break;
+            attributes[break_positions[i]].wordBreak = TRUE;
+            attributes[break_positions[i]].lineBreak = TRUE;
         }
 
         if (break_positions != brp)
             free(break_positions);
     }
 
-    /* manage charStop */
+    /* manage grapheme boundaries */
     i = 0;
     while (i < len) {
         cell_length = th_next_cell((const unsigned char *)cstr + i, len - i, &tis_cell, true);
 
-        attributes[i].charStop = true;
+        attributes[i].graphemeBoundary = true;
         for (j = 1; j < cell_length; j++)
-            attributes[i + j].charStop = false;
+            attributes[i + j].graphemeBoundary = false;
 
-        /* Set charStop for SARA AM */
+        /* Set graphemeBoundary for SARA AM */
         if (cstr[i + cell_length - 1] == (char)0xd3)
-            attributes[i + cell_length - 1].charStop = true;
+            attributes[i + cell_length - 1].graphemeBoundary = true;
 
         i += cell_length;
     }
index e0c263d..c357097 100644 (file)
@@ -260,12 +260,12 @@ void HB_TibetanAttributes(HB_Script script, const HB_UChar16 *text, hb_uint32 fr
         HB_Bool invalid;
         hb_uint32 boundary = tibetan_nextSyllableBoundary(text, from+i, end, &invalid) - from;
 
-        attributes[i].charStop = TRUE;
+        attributes[i].graphemeBoundary = TRUE;
 
         if (boundary > len-1) boundary = len;
         i++;
         while (i < boundary) {
-            attributes[i].charStop = FALSE;
+            attributes[i].graphemeBoundary = FALSE;
             ++uc;
             ++i;
         }
index 23de3ef..72d5bda 100644 (file)
@@ -65,9 +65,6 @@ Q_CORE_EXPORT HB_Face qHBNewFace(void *font, HB_GetFontTableFunc tableFunc);
 Q_CORE_EXPORT void qHBFreeFace(HB_Face);
 Q_CORE_EXPORT HB_Face qHBLoadFace(HB_Face face);
 
-Q_DECLARE_TYPEINFO(HB_CharAttributes, Q_PRIMITIVE_TYPE);
-Q_DECLARE_TYPEINFO(HB_ScriptItem, Q_PRIMITIVE_TYPE);
-
 Q_DECLARE_TYPEINFO(HB_GlyphAttributes, Q_PRIMITIVE_TYPE);
 Q_DECLARE_TYPEINFO(HB_FixedPoint, Q_PRIMITIVE_TYPE);
 
index c3d44da..f928932 100644 (file)
@@ -49,12 +49,12 @@ QT_BEGIN_NAMESPACE
 class QTextBoundaryFinderPrivate
 {
 public:
-    HB_CharAttributes attributes[1];
+    QCharAttributes attributes[1];
 };
 
-static void init(QTextBoundaryFinder::BoundaryType type, const QChar *chars, int length, HB_CharAttributes *attributes)
+static void init(QTextBoundaryFinder::BoundaryType type, const QChar *chars, int length, QCharAttributes *attributes)
 {
-    QVarLengthArray<HB_ScriptItem> scriptItems;
+    QVarLengthArray<QUnicodeTools::ScriptItem> scriptItems;
 
     const ushort *string = reinterpret_cast<const ushort *>(chars);
     const ushort *unicode = string;
@@ -72,11 +72,9 @@ static void init(QTextBoundaryFinder::BoundaryType type, const QChar *chars, int
             script = QUnicodeTables::Common;
         if (script != lastScript) {
             if (uc != start) {
-                HB_ScriptItem item;
-                item.pos = start - string;
-                item.length = uc - start;
-                item.script = (HB_Script)lastScript;
-                item.bidiLevel = 0; // ### what's the proper value?
+                QUnicodeTools::ScriptItem item;
+                item.position = start - string;
+                item.script = lastScript;
                 scriptItems.append(item);
                 start = uc;
             }
@@ -85,11 +83,9 @@ static void init(QTextBoundaryFinder::BoundaryType type, const QChar *chars, int
         ++uc;
     }
     if (uc != start) {
-        HB_ScriptItem item;
-        item.pos = start - string;
-        item.length = uc - start;
-        item.script = (HB_Script)lastScript;
-        item.bidiLevel = 0; // ### what's the proper value?
+        QUnicodeTools::ScriptItem item;
+        item.position = start - string;
+        item.script = lastScript;
         scriptItems.append(item);
     }
 
@@ -193,9 +189,9 @@ QTextBoundaryFinder::QTextBoundaryFinder(const QTextBoundaryFinder &other)
     , pos(other.pos)
     , freePrivate(true)
 {
-    d = (QTextBoundaryFinderPrivate *) malloc(length*sizeof(HB_CharAttributes));
+    d = (QTextBoundaryFinderPrivate *) malloc(length*sizeof(QCharAttributes));
     Q_CHECK_PTR(d);
-    memcpy(d, other.d, length*sizeof(HB_CharAttributes));
+    memcpy(d, other.d, length*sizeof(QCharAttributes));
 }
 
 /*!
@@ -213,11 +209,11 @@ QTextBoundaryFinder &QTextBoundaryFinder::operator=(const QTextBoundaryFinder &o
     pos = other.pos;
 
     QTextBoundaryFinderPrivate *newD = (QTextBoundaryFinderPrivate *)
-        realloc(freePrivate ? d : 0, length*sizeof(HB_CharAttributes));
+        realloc(freePrivate ? d : 0, length*sizeof(QCharAttributes));
     Q_CHECK_PTR(newD);
     freePrivate = true;
     d = newD;
-    memcpy(d, other.d, length*sizeof(HB_CharAttributes));
+    memcpy(d, other.d, length*sizeof(QCharAttributes));
 
     return *this;
 }
@@ -242,7 +238,7 @@ QTextBoundaryFinder::QTextBoundaryFinder(BoundaryType type, const QString &strin
     , pos(0)
     , freePrivate(true)
 {
-    d = (QTextBoundaryFinderPrivate *) malloc(length*sizeof(HB_CharAttributes));
+    d = (QTextBoundaryFinderPrivate *) malloc(length*sizeof(QCharAttributes));
     Q_CHECK_PTR(d);
     init(t, chars, length, d->attributes);
 }
@@ -266,11 +262,11 @@ QTextBoundaryFinder::QTextBoundaryFinder(BoundaryType type, const QChar *chars,
     , length(length)
     , pos(0)
 {
-    if (buffer && (uint)bufferSize >= length*sizeof(HB_CharAttributes)) {
+    if (buffer && (uint)bufferSize >= length*sizeof(QCharAttributes)) {
         d = (QTextBoundaryFinderPrivate *)buffer;
         freePrivate = false;
     } else {
-        d = (QTextBoundaryFinderPrivate *) malloc(length*sizeof(HB_CharAttributes));
+        d = (QTextBoundaryFinderPrivate *) malloc(length*sizeof(QCharAttributes));
         Q_CHECK_PTR(d);
         freePrivate = true;
     }
@@ -368,11 +364,11 @@ int QTextBoundaryFinder::toNextBoundary()
     
     switch(t) {
     case Grapheme:
-        while (pos < length && !d->attributes[pos].charStop)
+        while (pos < length && !d->attributes[pos].graphemeBoundary)
             ++pos;
         break;
     case Word:
-        while (pos < length && !d->attributes[pos].wordBoundary)
+        while (pos < length && !d->attributes[pos].wordBreak)
             ++pos;
         break;
     case Sentence:
@@ -380,7 +376,7 @@ int QTextBoundaryFinder::toNextBoundary()
             ++pos;
         break;
     case Line:
-        while (pos < length && d->attributes[pos].lineBreakType == HB_NoBreak)
+        while (pos < length && !d->attributes[pos].lineBreak)
             ++pos;
         break;
     }
@@ -410,11 +406,11 @@ int QTextBoundaryFinder::toPreviousBoundary()
 
     switch(t) {
     case Grapheme:
-        while (pos > 0 && !d->attributes[pos].charStop)
+        while (pos > 0 && !d->attributes[pos].graphemeBoundary)
             --pos;
         break;
     case Word:
-        while (pos > 0 && !d->attributes[pos].wordBoundary)
+        while (pos > 0 && !d->attributes[pos].wordBreak)
             --pos;
         break;
     case Sentence:
@@ -422,7 +418,7 @@ int QTextBoundaryFinder::toPreviousBoundary()
             --pos;
         break;
     case Line:
-        while (pos > 0 && d->attributes[pos].lineBreakType == HB_NoBreak)
+        while (pos > 0 && !d->attributes[pos].lineBreak)
             --pos;
         break;
     }
@@ -443,11 +439,11 @@ bool QTextBoundaryFinder::isAtBoundary() const
 
     switch(t) {
     case Grapheme:
-        return d->attributes[pos].charStop;
+        return d->attributes[pos].graphemeBoundary;
     case Word:
-        return d->attributes[pos].wordBoundary;
+        return d->attributes[pos].wordBreak;
     case Line:
-        return pos == 0 || d->attributes[pos].lineBreakType != HB_NoBreak;
+        return pos == 0 || d->attributes[pos].lineBreak;
     case Sentence:
         return d->attributes[pos].sentenceBoundary;
     }
@@ -463,8 +459,6 @@ QTextBoundaryFinder::BoundaryReasons QTextBoundaryFinder::boundaryReasons() cons
         return NotAtBoundary;
     if (! isAtBoundary())
         return NotAtBoundary;
-    if (t == Line && pos < length && d->attributes[pos].lineBreakType == HB_SoftHyphen)
-        return SoftHyphen;
     if (pos == 0) {
         if (d->attributes[pos].whiteSpace)
             return NotAtBoundary;
@@ -476,6 +470,12 @@ QTextBoundaryFinder::BoundaryReasons QTextBoundaryFinder::boundaryReasons() cons
         return EndWord;
     }
 
+    if (t == Line && chars[pos - 1].unicode() == QChar::SoftHyphen)
+        return SoftHyphen;
+
+    if (t != Word)
+        return BoundaryReasons(StartWord | EndWord);
+
     const bool nextIsSpace = d->attributes[pos].whiteSpace;
     const bool prevIsSpace = d->attributes[pos - 1].whiteSpace;
 
index f8db6cb..ffb9512 100644 (file)
@@ -42,6 +42,9 @@
 #include "qunicodetools_p.h"
 
 #include "qunicodetables_p.h"
+#include "qvarlengtharray.h"
+
+#include <harfbuzz-shaper.h>
 
 #define FLAG(x) (1 << (x))
 
@@ -77,7 +80,7 @@ static const uchar breakTable[QUnicodeTables::GraphemeBreak_LVT + 1][QUnicodeTab
 
 } // namespace GB
 
-static void getGraphemeBreaks(const ushort *string, quint32 len, HB_CharAttributes *attributes)
+static void getGraphemeBreaks(const ushort *string, quint32 len, QCharAttributes *attributes)
 {
     QUnicodeTables::GraphemeBreakClass lcls = QUnicodeTables::GraphemeBreak_LF; // to meet GB1
     for (quint32 i = 0; i != len; ++i) {
@@ -94,7 +97,8 @@ static void getGraphemeBreaks(const ushort *string, quint32 len, HB_CharAttribut
         const QUnicodeTables::Properties *prop = QUnicodeTables::properties(ucs4);
         QUnicodeTables::GraphemeBreakClass cls = (QUnicodeTables::GraphemeBreakClass) prop->graphemeBreakClass;
 
-        attributes[pos].charStop = GB::breakTable[lcls][cls];
+        if (Q_LIKELY(GB::breakTable[lcls][cls]))
+            attributes[pos].graphemeBoundary = true;
 
         lcls = cls;
     }
@@ -127,7 +131,7 @@ static const uchar breakTable[QUnicodeTables::WordBreak_ExtendNumLet + 1][QUnico
 
 } // namespace WB
 
-static void getWordBreaks(const ushort *string, quint32 len, HB_CharAttributes *attributes)
+static void getWordBreaks(const ushort *string, quint32 len, QCharAttributes *attributes)
 {
     QUnicodeTables::WordBreakClass cls = QUnicodeTables::WordBreak_LF; // to meet WB1
     for (quint32 i = 0; i != len; ++i) {
@@ -175,7 +179,7 @@ static void getWordBreaks(const ushort *string, quint32 len, HB_CharAttributes *
         }
         cls = ncls;
         if (action == WB::Break)
-            attributes[pos].wordBoundary = true;
+            attributes[pos].wordBreak = true;
     }
 }
 
@@ -217,7 +221,7 @@ static const uchar breakTable[BAfter + 1][QUnicodeTables::SentenceBreak_Close +
 
 } // namespace SB
 
-static void getSentenceBreaks(const ushort *string, quint32 len, HB_CharAttributes *attributes)
+static void getSentenceBreaks(const ushort *string, quint32 len, QCharAttributes *attributes)
 {
     uchar state = SB::BAfter; // to meet SB1
     for (quint32 i = 0; i != len; ++i) {
@@ -402,12 +406,11 @@ static const uchar breakTable[QUnicodeTables::LineBreak_CB + 1][QUnicodeTables::
 
 } // namespace LB
 
-static void getLineBreaks(const ushort *string, quint32 len, HB_CharAttributes *attributes)
+static void getLineBreaks(const ushort *string, quint32 len, QCharAttributes *attributes)
 {
     quint32 nestart = 0;
     LB::NS::Class nelast = LB::NS::XX;
 
-    uint lucs4 = 0;
     QUnicodeTables::LineBreakClass lcls = QUnicodeTables::LineBreak_LF; // to meet LB10
     QUnicodeTables::LineBreakClass cls = lcls;
     for (quint32 i = 0; i != len; ++i) {
@@ -443,7 +446,7 @@ static void getLineBreaks(const ushort *string, quint32 len, HB_CharAttributes *
             case LB::NS::Break:
                 // do not change breaks before and after the expression
                 for (quint32 j = nestart + 1; j < pos; ++j)
-                    attributes[j].lineBreakType = HB_NoBreak;
+                    attributes[j].lineBreak = false;
                 // fall through
             case LB::NS::None:
                 nelast = LB::NS::XX; // reset state
@@ -457,12 +460,10 @@ static void getLineBreaks(const ushort *string, quint32 len, HB_CharAttributes *
             }
         }
 
-        HB_LineBreakType lineBreakType = HB_NoBreak;
-
         if (Q_UNLIKELY(lcls >= QUnicodeTables::LineBreak_CR)) {
             // LB4: BK!, LB5: (CRxLF|CR|LF|NL)!
             if (lcls > QUnicodeTables::LineBreak_CR || ncls != QUnicodeTables::LineBreak_LF)
-                lineBreakType = HB_ForcedBreak;
+                attributes[pos].lineBreak = true;
             goto next;
         }
 
@@ -479,18 +480,16 @@ static void getLineBreaks(const ushort *string, quint32 len, HB_CharAttributes *
 
         switch (LB::breakTable[cls][ncls < QUnicodeTables::LineBreak_SA ? ncls : QUnicodeTables::LineBreak_AL]) {
         case LB::DirectBreak:
-            lineBreakType = HB_Break;
-            if (lucs4 == QChar::SoftHyphen)
-                lineBreakType = HB_SoftHyphen;
+            attributes[pos].lineBreak = true;
             break;
         case LB::IndirectBreak:
             if (lcls == QUnicodeTables::LineBreak_SP)
-                lineBreakType = HB_Break;
+                attributes[pos].lineBreak = true;
             break;
         case LB::CombiningIndirectBreak:
             if (lcls != QUnicodeTables::LineBreak_SP)
                 goto next_no_cls_update;
-            lineBreakType = HB_Break;
+            attributes[pos].lineBreak = true;
             break;
         case LB::CombiningProhibitedBreak:
             if (lcls != QUnicodeTables::LineBreak_SP)
@@ -504,24 +503,21 @@ static void getLineBreaks(const ushort *string, quint32 len, HB_CharAttributes *
 
     next:
         cls = ncls;
-        lucs4 = ucs4;
     next_no_cls_update:
         lcls = ncls;
-        if (Q_LIKELY(lineBreakType != HB_NoBreak))
-            attributes[pos].lineBreakType = lineBreakType;
     }
 
     if (Q_UNLIKELY(LB::NS::actionTable[nelast][LB::NS::XX] == LB::NS::Break)) {
         // LB25: do not break lines inside numbers
         for (quint32 j = nestart + 1; j < len; ++j)
-            attributes[j].lineBreakType = HB_NoBreak;
+            attributes[j].lineBreak = false;
     }
 
-    attributes[0].lineBreakType = HB_NoBreak; // LB2
+    attributes[0].lineBreak = false; // LB2
 }
 
 
-static void getWhiteSpaces(const ushort *string, quint32 len, HB_CharAttributes *attributes)
+static void getWhiteSpaces(const ushort *string, quint32 len, QCharAttributes *attributes)
 {
     for (quint32 i = 0; i != len; ++i) {
         uint ucs4 = string[i];
@@ -540,14 +536,14 @@ static void getWhiteSpaces(const ushort *string, quint32 len, HB_CharAttributes
 
 
 Q_CORE_EXPORT void initCharAttributes(const ushort *string, int length,
-                                      const HB_ScriptItem *items, int numItems,
-                                      HB_CharAttributes *attributes, CharAttributeOptions options)
+                                      const ScriptItem *items, int numItems,
+                                      QCharAttributes *attributes, CharAttributeOptions options)
 {
     if (length <= 0)
         return;
 
     if (!(options & DontClearAttributes))
-        ::memset(attributes, 0, length * sizeof(HB_CharAttributes));
+        ::memset(attributes, 0, length * sizeof(QCharAttributes));
 
     if (options & GraphemeBreaks)
         getGraphemeBreaks(string, length, attributes);
@@ -562,8 +558,34 @@ Q_CORE_EXPORT void initCharAttributes(const ushort *string, int length,
 
     if (!items || numItems <= 0)
         return;
-    if (!qt_initcharattributes_default_algorithm_only)
-        HB_GetTailoredCharAttributes(string, length, items, numItems, attributes);
+    if (!qt_initcharattributes_default_algorithm_only) {
+        QVarLengthArray<HB_ScriptItem, 64> scriptItems;
+        scriptItems.reserve(numItems);
+        int start = 0;
+        for (int i = start + 1; i < numItems; ++i) {
+            if (items[i].script == items[start].script)
+                continue;
+            HB_ScriptItem item;
+            item.pos = items[start].position;
+            item.length = items[i].position - items[start].position;
+            item.script = (HB_Script)items[start].script;
+            item.bidiLevel = 0; // unused
+            scriptItems.append(item);
+            start = i;
+        }
+        if (items[start].position + 1 < length) {
+            HB_ScriptItem item;
+            item.pos = items[start].position;
+            item.length = length - items[start].position;
+            item.script = (HB_Script)items[start].script;
+            item.bidiLevel = 0; // unused
+            scriptItems.append(item);
+        }
+        Q_STATIC_ASSERT(sizeof(QCharAttributes) == sizeof(HB_CharAttributes));
+        HB_GetTailoredCharAttributes(string, length,
+                                     scriptItems.constData(), scriptItems.size(),
+                                     reinterpret_cast<HB_CharAttributes *>(attributes));
+    }
 }
 
 } // namespace QUnicodeTools
index ea407e7..3396c33 100644 (file)
 // We mean it.
 //
 
-#include <private/qharfbuzz_p.h>
+#include <QtCore/qchar.h>
 
 QT_BEGIN_NAMESPACE
 
+struct Q_PACKED QCharAttributes
+{
+    uchar graphemeBoundary : 1;
+    uchar wordBreak        : 1;
+    uchar sentenceBoundary : 1;
+    uchar lineBreak        : 1;
+    uchar whiteSpace       : 1;
+    uchar unused           : 3;
+};
+Q_DECLARE_TYPEINFO(QCharAttributes, Q_PRIMITIVE_TYPE);
+
 namespace QUnicodeTools {
 
+// ### temporary
+struct ScriptItem
+{
+    int position;
+    int script;
+};
+
 enum CharAttributeOption {
     GraphemeBreaks = 0x01,
     WordBreaks = 0x02,
@@ -72,8 +90,8 @@ enum CharAttributeOption {
 Q_DECLARE_FLAGS(CharAttributeOptions, CharAttributeOption)
 
 Q_CORE_EXPORT void initCharAttributes(const ushort *string, int length,
-                                      const HB_ScriptItem *items, int numItems,
-                                      HB_CharAttributes *attributes, CharAttributeOptions options = DefaultOptionsCompat);
+                                      const ScriptItem *items, int numItems,
+                                      QCharAttributes *attributes, CharAttributeOptions options = DefaultOptionsCompat);
 
 } // namespace QUnicodeTools
 
index aa48667..75b1240 100644 (file)
@@ -66,13 +66,6 @@ typedef enum {
   HB_Err_Out_Of_Memory                = 0xDEAD
 } HB_Error;
 
-typedef enum {
-    HB_NoBreak,
-    HB_SoftHyphen,
-    HB_Break,
-    HB_ForcedBreak
-} HB_LineBreakType;
-
 typedef QT_PREPEND_NAMESPACE(quint32) HB_Glyph;
 typedef void * HB_Font;
 typedef void * HB_Face;
@@ -95,17 +88,6 @@ typedef struct {
     hb_bitfield combiningClass  :8;
 } HB_GlyphAttributes;
 
-// This struct is strictly not needed, but we replicate it completely in
-// case the compiler tries to get clever with padding.
-typedef struct {
-    /*HB_LineBreakType*/ hb_bitfield lineBreakType  :2;
-    /*HB_Bool*/ hb_bitfield whiteSpace              :1;     /* A unicode whitespace character, except NBSP, ZWNBSP */
-    /*HB_Bool*/ hb_bitfield charStop                :1;     /* Valid cursor position (for left/right arrow) */
-    /*HB_Bool*/ hb_bitfield wordBoundary            :1;
-    /*HB_Bool*/ hb_bitfield sentenceBoundary        :1;
-    hb_bitfield unused                  :2;
-} HB_CharAttributes;
-
 }
 
 #endif // ifdef QT_BUILD_GUI_LIB
index e4572e7..b2f7600 100644 (file)
@@ -52,7 +52,6 @@
 #include "qfontengine_p.h"
 #include "qstring.h"
 #include <private/qunicodetables_p.h>
-#include <private/qunicodetools_p.h>
 #include "qtextdocument_p.h"
 #include "qrawfont.h"
 #include "qrawfont_p.h"
@@ -1198,33 +1197,30 @@ QTextEngine::~QTextEngine()
     resetFontEngineCache();
 }
 
-const HB_CharAttributes *QTextEngine::attributes() const
+const QCharAttributes *QTextEngine::attributes() const
 {
     if (layoutData && layoutData->haveCharAttributes)
-        return (HB_CharAttributes *) layoutData->memory;
+        return (QCharAttributes *) layoutData->memory;
 
     itemize();
     if (! ensureSpace(layoutData->string.length()))
         return NULL;
 
-    QVarLengthArray<HB_ScriptItem> hbScriptItems(layoutData->items.size());
-
+    QVarLengthArray<QUnicodeTools::ScriptItem> scriptItems(layoutData->items.size());
     for (int i = 0; i < layoutData->items.size(); ++i) {
         const QScriptItem &si = layoutData->items[i];
-        hbScriptItems[i].pos = si.position;
-        hbScriptItems[i].length = length(i);
-        hbScriptItems[i].bidiLevel = si.analysis.bidiLevel;
-        hbScriptItems[i].script = (HB_Script)si.analysis.script;
+        scriptItems[i].position = si.position;
+        scriptItems[i].script = si.analysis.script;
     }
 
     QUnicodeTools::initCharAttributes(reinterpret_cast<const HB_UChar16 *>(layoutData->string.constData()),
                                       layoutData->string.length(),
-                                      hbScriptItems.data(), hbScriptItems.size(),
-                                      (HB_CharAttributes *)layoutData->memory);
+                                      scriptItems.data(), scriptItems.size(),
+                                      (QCharAttributes *)layoutData->memory);
 
 
     layoutData->haveCharAttributes = true;
-    return (HB_CharAttributes *) layoutData->memory;
+    return (QCharAttributes *) layoutData->memory;
 }
 
 void QTextEngine::shape(int item) const
@@ -1858,7 +1854,7 @@ void QTextEngine::justify(const QScriptLine &line)
 
     // don't include trailing white spaces when doing justification
     int line_length = line.length;
-    const HB_CharAttributes *a = attributes();
+    const QCharAttributes *a = attributes();
     if (! a)
         return;
     a += line.from;
@@ -2058,7 +2054,7 @@ QTextEngine::LayoutData::LayoutData(const QString &str, void **stack_memory, int
 {
     allocated = _allocated;
 
-    int space_charAttributes = sizeof(HB_CharAttributes)*string.length()/sizeof(void*) + 1;
+    int space_charAttributes = sizeof(QCharAttributes)*string.length()/sizeof(void*) + 1;
     int space_logClusters = sizeof(unsigned short)*string.length()/sizeof(void*) + 1;
     available_glyphs = ((int)allocated - space_charAttributes - space_logClusters)*(int)sizeof(void*)/(int)QGlyphLayout::spaceNeededForGlyphLayout(1);
 
@@ -2100,7 +2096,7 @@ bool QTextEngine::LayoutData::reallocate(int totalGlyphs)
         return true;
     }
 
-    int space_charAttributes = sizeof(HB_CharAttributes)*string.length()/sizeof(void*) + 1;
+    int space_charAttributes = sizeof(QCharAttributes)*string.length()/sizeof(void*) + 1;
     int space_logClusters = sizeof(unsigned short)*string.length()/sizeof(void*) + 1;
     int space_glyphs = QGlyphLayout::spaceNeededForGlyphLayout(totalGlyphs)/sizeof(void*) + 2;
 
@@ -2371,7 +2367,7 @@ QString QTextEngine::elidedText(Qt::TextElideMode mode, const QFixed &width, int
 
     if (flags & Qt::TextShowMnemonic) {
         itemize();
-        HB_CharAttributes *attributes = const_cast<HB_CharAttributes *>(this->attributes());
+        QCharAttributes *attributes = const_cast<QCharAttributes *>(this->attributes());
         if (!attributes)
             return QString();
         for (int i = 0; i < layoutData->items.size(); ++i) {
@@ -2387,9 +2383,9 @@ QString QTextEngine::elidedText(Qt::TextElideMode mode, const QFixed &width, int
                 if (layoutData->string.at(i) == QLatin1Char('&')) {
                     const int gp = logClusters[i - si.position];
                     glyphs.attributes[gp].dontPrint = true;
-                    attributes[i + 1].charStop = false;
+                    attributes[i + 1].graphemeBoundary = false;
+                    attributes[i + 1].lineBreak = false;
                     attributes[i + 1].whiteSpace = false;
-                    attributes[i + 1].lineBreakType = HB_NoBreak;
                     if (layoutData->string.at(i + 1) == QLatin1Char('&'))
                         ++i;
                 }
@@ -2451,7 +2447,7 @@ QString QTextEngine::elidedText(Qt::TextElideMode mode, const QFixed &width, int
     if (availableWidth < 0)
         return QString();
 
-    const HB_CharAttributes *attributes = this->attributes();
+    const QCharAttributes *attributes = this->attributes();
     if (!attributes)
         return QString();
 
@@ -2464,7 +2460,7 @@ QString QTextEngine::elidedText(Qt::TextElideMode mode, const QFixed &width, int
             pos = nextBreak;
 
             ++nextBreak;
-            while (nextBreak < layoutData->string.length() && !attributes[nextBreak].charStop)
+            while (nextBreak < layoutData->string.length() && !attributes[nextBreak].graphemeBoundary)
                 ++nextBreak;
 
             currentWidth += this->width(pos, nextBreak - pos);
@@ -2487,7 +2483,7 @@ QString QTextEngine::elidedText(Qt::TextElideMode mode, const QFixed &width, int
             pos = nextBreak;
 
             --nextBreak;
-            while (nextBreak > 0 && !attributes[nextBreak].charStop)
+            while (nextBreak > 0 && !attributes[nextBreak].graphemeBoundary)
                 --nextBreak;
 
             currentWidth += this->width(nextBreak, pos - nextBreak);
@@ -2516,11 +2512,11 @@ QString QTextEngine::elidedText(Qt::TextElideMode mode, const QFixed &width, int
             rightPos = nextRightBreak;
 
             ++nextLeftBreak;
-            while (nextLeftBreak < layoutData->string.length() && !attributes[nextLeftBreak].charStop)
+            while (nextLeftBreak < layoutData->string.length() && !attributes[nextLeftBreak].graphemeBoundary)
                 ++nextLeftBreak;
 
             --nextRightBreak;
-            while (nextRightBreak > from && !attributes[nextRightBreak].charStop)
+            while (nextRightBreak > from && !attributes[nextRightBreak].graphemeBoundary)
                 --nextRightBreak;
 
             leftWidth += this->width(leftPos, nextLeftBreak - leftPos);
@@ -2833,12 +2829,12 @@ QFixed QTextEngine::offsetInLigature(const QScriptItem *si, int pos, int max, in
 
 // Scan in logClusters[from..to-1] for glyph_pos
 int QTextEngine::getClusterLength(unsigned short *logClusters,
-                                  const HB_CharAttributes *attributes,
+                                  const QCharAttributes *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 (logClusters[i] == glyph_pos && attributes[i].graphemeBoundary) {
             if (*start < 0)
                 *start = i;
             clusterLength++;
@@ -2877,7 +2873,7 @@ int QTextEngine::positionInLigature(const QScriptItem *si, int end,
             glyph_pos--;
     }
 
-    const HB_CharAttributes *attrs = attributes();
+    const QCharAttributes *attrs = attributes();
     logClusters = this->logClusters(si);
     clusterLength = getClusterLength(logClusters, attrs, 0, end, glyph_pos, &clusterStart);
 
@@ -2895,8 +2891,8 @@ int QTextEngine::positionInLigature(const QScriptItem *si, int end,
         if (cursorOnCharacter && closestItem > 0)
             closestItem--;
         int pos = si->position + clusterStart + closestItem;
-        // Jump to the next charStop
-        while (pos < end && !attrs[pos].charStop)
+        // Jump to the next grapheme boundary
+        while (pos < end && !attrs[pos].graphemeBoundary)
             pos++;
         return pos;
     }
@@ -2905,21 +2901,21 @@ int QTextEngine::positionInLigature(const QScriptItem *si, int end,
 
 int QTextEngine::previousLogicalPosition(int oldPos) const
 {
-    const HB_CharAttributes *attrs = attributes();
+    const QCharAttributes *attrs = attributes();
     if (!attrs || oldPos < 0)
         return oldPos;
 
     if (oldPos <= 0)
         return 0;
     oldPos--;
-    while (oldPos && !attrs[oldPos].charStop)
+    while (oldPos && !attrs[oldPos].graphemeBoundary)
         oldPos--;
     return oldPos;
 }
 
 int QTextEngine::nextLogicalPosition(int oldPos) const
 {
-    const HB_CharAttributes *attrs = attributes();
+    const QCharAttributes *attrs = attributes();
     int len = block.isValid() ? block.length() - 1
                               : layoutData->string.length();
     Q_ASSERT(len <= layoutData->string.length());
@@ -2927,7 +2923,7 @@ int QTextEngine::nextLogicalPosition(int oldPos) const
         return oldPos;
 
     oldPos++;
-    while (oldPos < len && !attrs[oldPos].charStop)
+    while (oldPos < len && !attrs[oldPos].graphemeBoundary)
         oldPos++;
     return oldPos;
 }
index c2362e6..ac4bc27 100644 (file)
@@ -75,6 +75,8 @@
 
 #include "private/qfixed_p.h"
 
+#include <private/qunicodetools_p.h>
+
 #include <stdlib.h>
 
 QT_BEGIN_NAMESPACE
@@ -468,7 +470,7 @@ public:
     bool isRightToLeft() const;
     static void bidiReorder(int numRuns, const quint8 *levels, int *visualOrder);
 
-    const HB_CharAttributes *attributes() const;
+    const QCharAttributes *attributes() const;
 
     void shape(int item) const;
 
@@ -672,7 +674,7 @@ private:
     void resolveAdditionalFormats() const;
     int endOfLine(int lineNum);
     int beginningOfLine(int lineNum);
-    int getClusterLength(unsigned short *logClusters, const HB_CharAttributes *attributes, int from, int to, int glyph_pos, int *start);
+    int getClusterLength(unsigned short *logClusters, const QCharAttributes *attributes, int from, int to, int glyph_pos, int *start);
 };
 
 class Q_GUI_EXPORT QStackTextEngine : public QTextEngine {
index a49b411..7591b46 100644 (file)
@@ -683,7 +683,7 @@ void QTextLayout::clearLayout()
 */
 int QTextLayout::nextCursorPosition(int oldPos, CursorMode mode) const
 {
-    const HB_CharAttributes *attributes = d->attributes();
+    const QCharAttributes *attributes = d->attributes();
     int len = d->block.isValid() ? d->block.length() - 1
                                  : d->layoutData->string.length();
     Q_ASSERT(len <= d->layoutData->string.length());
@@ -692,7 +692,7 @@ int QTextLayout::nextCursorPosition(int oldPos, CursorMode mode) const
 
     if (mode == SkipCharacters) {
         oldPos++;
-        while (oldPos < len && !attributes[oldPos].charStop)
+        while (oldPos < len && !attributes[oldPos].graphemeBoundary)
             oldPos++;
     } else {
         if (oldPos < len && d->atWordSeparator(oldPos)) {
@@ -719,13 +719,13 @@ int QTextLayout::nextCursorPosition(int oldPos, CursorMode mode) const
 */
 int QTextLayout::previousCursorPosition(int oldPos, CursorMode mode) const
 {
-    const HB_CharAttributes *attributes = d->attributes();
+    const QCharAttributes *attributes = d->attributes();
     if (!attributes || oldPos <= 0 || oldPos > d->layoutData->string.length())
         return oldPos;
 
     if (mode == SkipCharacters) {
         oldPos--;
-        while (oldPos && !attributes[oldPos].charStop)
+        while (oldPos && !attributes[oldPos].graphemeBoundary)
             oldPos--;
     } else {
         while (oldPos && d->atSpace(oldPos-1))
@@ -789,10 +789,10 @@ int QTextLayout::leftCursorPosition(int oldPos) const
 */
 bool QTextLayout::isValidCursorPosition(int pos) const
 {
-    const HB_CharAttributes *attributes = d->attributes();
+    const QCharAttributes *attributes = d->attributes();
     if (!attributes || pos < 0 || pos > (int)d->layoutData->string.length())
         return false;
-    return attributes[pos].charStop;
+    return attributes[pos].graphemeBoundary;
 }
 
 /*!
@@ -1770,7 +1770,7 @@ void QTextLine::layout_helper(int maxGlyphs)
 
     Qt::Alignment alignment = eng->option.alignment();
 
-    const HB_CharAttributes *attributes = eng->attributes();
+    const QCharAttributes *attributes = eng->attributes();
     if (!attributes)
         return;
     lbh.currentPosition = line.from;
@@ -1875,17 +1875,18 @@ void QTextLine::layout_helper(int maxGlyphs)
 
                 if (lbh.currentPosition >= eng->layoutData->string.length()
                     || attributes[lbh.currentPosition].whiteSpace
-                    || attributes[lbh.currentPosition].lineBreakType != HB_NoBreak) {
+                    || attributes[lbh.currentPosition].lineBreak) {
                     sb_or_ws = true;
                     break;
-                } else if (breakany && attributes[lbh.currentPosition].charStop) {
+                } else if (breakany && attributes[lbh.currentPosition].graphemeBoundary) {
                     break;
                 }
             } while (lbh.currentPosition < end);
             lbh.minw = qMax(lbh.tmpData.textWidth, lbh.minw);
 
             if (lbh.currentPosition > 0 && lbh.currentPosition < end
-                && attributes[lbh.currentPosition].lineBreakType == HB_SoftHyphen) {
+                && attributes[lbh.currentPosition].lineBreak
+                && eng->layoutData->string.at(lbh.currentPosition - 1).unicode() == QChar::SoftHyphen) {
                 // if we are splitting up a word because of
                 // a soft hyphen then we ...
                 //
@@ -2605,12 +2606,12 @@ qreal QTextLine::cursorToX(int *cursorPos, Edge edge) const
     int lineEnd = line.from + line.length + line.trailingSpaces;
     int pos = *cursorPos;
     int itm;
-    const HB_CharAttributes *attributes = eng->attributes();
+    const QCharAttributes *attributes = eng->attributes();
     if (!attributes) {
         *cursorPos = 0;
         return x.toReal();
     }
-    while (pos < lineEnd && !attributes[pos].charStop)
+    while (pos < lineEnd && !attributes[pos].graphemeBoundary)
         pos++;
     if (pos == lineEnd) {
         // end of line ensure we have the last item on the line
index 1190736..6b5edc5 100644 (file)
@@ -97,7 +97,7 @@ private slots:
     void charWordStopOnLineSeparator();
     void xToCursorAtEndOfLine();
     void boundingRectTopLeft();
-    void charStopForSurrogatePairs();
+    void graphemeBoundaryForSurrogatePairs();
     void tabStops();
     void integerOverflow();
     void testDefaultTabs();
@@ -274,14 +274,14 @@ void tst_QTextLayout::lineBreaking()
     while (b->utf8) {
         QString str = QString::fromUtf8(b->utf8);
         QTextEngine engine(str, QFont());
-        const HB_CharAttributes *attrs = engine.attributes();
-        QVERIFY(attrs[0].lineBreakType == HB_NoBreak);
+        const QCharAttributes *attrs = engine.attributes();
+        QVERIFY(!attrs[0].lineBreak);
         int i;
         for (i = 0; i < (int)str.length() - 1; ++i) {
             QVERIFY(b->breaks[i] != 0xff);
-            if ( (attrs[i + 1].lineBreakType != HB_NoBreak) != (bool)b->breaks[i] ) {
-                qDebug("test case \"%s\" failed at char %d; break type: %d", b->utf8, i, attrs[i + 1].lineBreakType);
-                QCOMPARE( (attrs[i + 1].lineBreakType != HB_NoBreak), (bool)b->breaks[i] );
+            if ( attrs[i + 1].lineBreak != (bool)b->breaks[i] ) {
+                qDebug("test case \"%s\" failed at char %d; break type: %d", b->utf8, i, attrs[i + 1].lineBreak);
+                QCOMPARE( attrs[i + 1].lineBreak, (bool)b->breaks[i] );
             }
         }
         QCOMPARE(b->breaks[i], (uchar)0xff);
@@ -1041,9 +1041,9 @@ void tst_QTextLayout::charWordStopOnLineSeparator()
     txt.append(lineSeparator);
     QTextLayout layout(txt, testFont);
     QTextEngine *engine = layout.engine();
-    const HB_CharAttributes *attrs = engine->attributes();
+    const QCharAttributes *attrs = engine->attributes();
     QVERIFY(attrs);
-    QVERIFY(attrs[1].charStop);
+    QVERIFY(attrs[1].graphemeBoundary);
 }
 
 void tst_QTextLayout::xToCursorAtEndOfLine()
@@ -1093,7 +1093,7 @@ void tst_QTextLayout::boundingRectTopLeft()
     QCOMPARE(layout.boundingRect().topLeft(), firstLine.position());
 }
 
-void tst_QTextLayout::charStopForSurrogatePairs()
+void tst_QTextLayout::graphemeBoundaryForSurrogatePairs()
 {
     QString txt;
     txt.append("a");
@@ -1102,12 +1102,12 @@ void tst_QTextLayout::charStopForSurrogatePairs()
     txt.append("b");
     QTextLayout layout(txt, testFont);
     QTextEngine *engine = layout.engine();
-    const HB_CharAttributes *attrs = engine->attributes();
+    const QCharAttributes *attrs = engine->attributes();
     QVERIFY(attrs);
-    QVERIFY(attrs[0].charStop);
-    QVERIFY(attrs[1].charStop);
-    QVERIFY(!attrs[2].charStop);
-    QVERIFY(attrs[3].charStop);
+    QVERIFY(attrs[0].graphemeBoundary);
+    QVERIFY(attrs[1].graphemeBoundary);
+    QVERIFY(!attrs[2].graphemeBoundary);
+    QVERIFY(attrs[3].graphemeBoundary);
 }
 
 void tst_QTextLayout::tabStops()