src/gui/text/qtextengine.cpp
changeset 37 758a864f9613
parent 33 3e2da88830cd
--- a/src/gui/text/qtextengine.cpp	Fri Sep 17 08:34:18 2010 +0300
+++ b/src/gui/text/qtextengine.cpp	Mon Oct 04 01:19:32 2010 +0300
@@ -923,6 +923,13 @@
         si.width += glyphs.advances_x[i];
 }
 
+static inline bool hasCaseChange(const QScriptItem &si)
+{
+    return si.analysis.flags == QScriptAnalysis::SmallCaps ||
+           si.analysis.flags == QScriptAnalysis::Uppercase ||
+           si.analysis.flags == QScriptAnalysis::Lowercase;
+}
+
 #if defined(Q_WS_WINCE) //TODO
 // set the glyph attributes heuristically. Assumes a 1 to 1 relationship between chars and glyphs
 // and no reordering.
@@ -1050,14 +1057,15 @@
     if (option.useDesignMetrics())
 	flags |= DesignMetrics;
 
-    attributes(); // pre-initialize char attributes
+    // pre-initialize char attributes
+    if (! attributes())
+        return;
 
     const int len = length(item);
     int num_glyphs = length(item);
     const QChar *str = layoutData->string.unicode() + si.position;
     ushort upperCased[256];
-    if (si.analysis.flags == QScriptAnalysis::SmallCaps || si.analysis.flags == QScriptAnalysis::Uppercase
-            || si.analysis.flags == QScriptAnalysis::Lowercase) {
+    if (hasCaseChange(si)) {
         ushort *uc = upperCased;
         if (len > 256)
             uc = new ushort[len];
@@ -1071,7 +1079,14 @@
     }
 
     while (true) {
-        ensureSpace(num_glyphs);
+        if (! ensureSpace(num_glyphs)) {
+            // If str is converted to uppercase/lowercase form with a new buffer,
+            // we need to delete that buffer before return for error
+            const ushort *uc = reinterpret_cast<const ushort *>(str);
+            if (hasCaseChange(si) && uc != upperCased)
+                delete [] uc;
+            return;
+        }
         num_glyphs = layoutData->glyphLayout.numGlyphs - layoutData->used;
 
         QGlyphLayout g = availableGlyphs(&si);
@@ -1092,9 +1107,7 @@
     layoutData->used += si.num_glyphs;
 
     const ushort *uc = reinterpret_cast<const ushort *>(str);
-    if ((si.analysis.flags == QScriptAnalysis::SmallCaps || si.analysis.flags == QScriptAnalysis::Uppercase
-         || si.analysis.flags == QScriptAnalysis::Lowercase)
-        && uc != upperCased)
+    if (hasCaseChange(si) && uc != upperCased)
         delete [] uc;
 }
 #endif
@@ -1133,8 +1146,7 @@
     entire_shaper_item.item.bidiLevel = si.analysis.bidiLevel;
 
     HB_UChar16 upperCased[256]; // XXX what about making this 4096, so we don't have to extend it ever.
-    if (si.analysis.flags == QScriptAnalysis::SmallCaps || si.analysis.flags == QScriptAnalysis::Uppercase
-            || si.analysis.flags == QScriptAnalysis::Lowercase) {
+    if (hasCaseChange(si)) {
         HB_UChar16 *uc = upperCased;
         if (entire_shaper_item.item.length > 256)
             uc = new HB_UChar16[entire_shaper_item.item.length];
@@ -1156,17 +1168,24 @@
         entire_shaper_item.shaperFlags |= HB_ShaperFlag_UseDesignMetrics;
 
     entire_shaper_item.num_glyphs = qMax(layoutData->glyphLayout.numGlyphs - layoutData->used, int(entire_shaper_item.item.length));
-    ensureSpace(entire_shaper_item.num_glyphs);
+    if (! ensureSpace(entire_shaper_item.num_glyphs)) {
+        if (hasCaseChange(si))
+            delete [] const_cast<HB_UChar16 *>(entire_shaper_item.string);
+        return;
+    }
     QGlyphLayout initialGlyphs = availableGlyphs(&si).mid(0, entire_shaper_item.num_glyphs);
 
     if (!stringToGlyphs(&entire_shaper_item, &initialGlyphs, font)) {
-        ensureSpace(entire_shaper_item.num_glyphs);
+        if (! ensureSpace(entire_shaper_item.num_glyphs)) {
+            if (hasCaseChange(si))
+                delete [] const_cast<HB_UChar16 *>(entire_shaper_item.string);
+            return;
+        }
         initialGlyphs = availableGlyphs(&si).mid(0, entire_shaper_item.num_glyphs);
 
         if (!stringToGlyphs(&entire_shaper_item, &initialGlyphs, font)) {
             // ############ if this happens there's a bug in the fontengine
-            if ((si.analysis.flags == QScriptAnalysis::SmallCaps || si.analysis.flags == QScriptAnalysis::Uppercase
-                    || si.analysis.flags == QScriptAnalysis::Lowercase) && entire_shaper_item.string != upperCased)
+            if (hasCaseChange(si) && entire_shaper_item.string != upperCased)
                 delete [] const_cast<HB_UChar16 *>(entire_shaper_item.string);
             return;
         }
@@ -1231,7 +1250,11 @@
         remaining_glyphs -= shaper_item.initialGlyphCount;
 
         do {
-            ensureSpace(glyph_pos + shaper_item.num_glyphs + remaining_glyphs);
+            if (! ensureSpace(glyph_pos + shaper_item.num_glyphs + remaining_glyphs)) {
+                if (hasCaseChange(si))
+                    delete [] const_cast<HB_UChar16 *>(entire_shaper_item.string);
+                return;
+            }
 
             const QGlyphLayout g = availableGlyphs(&si).mid(glyph_pos);
             moveGlyphData(g.mid(shaper_item.num_glyphs), g.mid(shaper_item.initialGlyphCount), remaining_glyphs);
@@ -1271,8 +1294,7 @@
 
     layoutData->used += si.num_glyphs;
 
-    if ((si.analysis.flags == QScriptAnalysis::SmallCaps || si.analysis.flags == QScriptAnalysis::Uppercase)
-        && entire_shaper_item.string != upperCased)
+    if (hasCaseChange(si) && entire_shaper_item.string != upperCased)
         delete [] const_cast<HB_UChar16 *>(entire_shaper_item.string);
 }
 
@@ -1317,7 +1339,8 @@
         return (HB_CharAttributes *) layoutData->memory;
 
     itemize();
-    ensureSpace(layoutData->string.length());
+    if (! ensureSpace(layoutData->string.length()))
+        return NULL;
 
     QVarLengthArray<HB_ScriptItem> hbScriptItems(layoutData->items.size());
 
@@ -1538,14 +1561,19 @@
 int QTextEngine::findItem(int strPos) const
 {
     itemize();
-
-    // ##### use binary search
-    int item;
-    for (item = layoutData->items.size()-1; item > 0; --item) {
-        if (layoutData->items[item].position <= strPos)
-            break;
+    int left = 0;
+    int right = layoutData->items.size()-1;
+    while(left <= right) {
+        int middle = ((right-left)/2)+left;
+        if (strPos > layoutData->items[middle].position)
+            left = middle+1;
+        else if(strPos < layoutData->items[middle].position)
+            right = middle-1;
+        else {
+            return middle;
+        }
     }
-    return item;
+    return right;
 }
 
 QFixed QTextEngine::width(int from, int len) const
@@ -1618,7 +1646,6 @@
 
     for (int i = 0; i < layoutData->items.size(); i++) {
         const QScriptItem *si = layoutData->items.constData() + i;
-        QFontEngine *fe = fontEngine(*si);
 
         int pos = si->position;
         int ilen = length(i);
@@ -1648,6 +1675,7 @@
                 while (charFrom < ilen && logClusters[charFrom] == glyphStart)
                     charFrom++;
             if (charFrom < ilen) {
+                QFontEngine *fe = fontEngine(*si);
                 glyphStart = logClusters[charFrom];
                 int charEnd = from + len - 1 - pos;
                 if (charEnd >= ilen)
@@ -1666,11 +1694,6 @@
                     gm.yoff += m.yoff;
                 }
             }
-
-            glyph_t glyph = glyphs.glyphs[logClusters[ilen - 1]];
-            glyph_metrics_t gi = fe->boundingBox(glyph);
-            if (gi.isValid())
-                gm.width -= qRound(gi.xoff - gi.x - gi.width);
         }
     }
     return gm;
@@ -1864,7 +1887,10 @@
 
     // don't include trailing white spaces when doing justification
     int line_length = line.length;
-    const HB_CharAttributes *a = attributes()+line.from;
+    const HB_CharAttributes *a = attributes();
+    if (! a)
+        return;
+    a += line.from;
     while (line_length && a[line_length-1].whiteSpace)
         --line_length;
     // subtract one char more, as we can't justfy after the last character
@@ -2045,7 +2071,7 @@
     memory_on_stack = false;
     used = 0;
     hasBidi = false;
-    inLayout = false;
+    layoutState = LayoutEmpty;
     haveCharAttributes = false;
     logClustersPtr = 0;
     available_glyphs = 0;
@@ -2079,7 +2105,7 @@
     }
     used = 0;
     hasBidi = false;
-    inLayout = false;
+    layoutState = LayoutEmpty;
     haveCharAttributes = false;
 }
 
@@ -2090,12 +2116,12 @@
     memory = 0;
 }
 
-void QTextEngine::LayoutData::reallocate(int totalGlyphs)
+bool QTextEngine::LayoutData::reallocate(int totalGlyphs)
 {
     Q_ASSERT(totalGlyphs >= glyphLayout.numGlyphs);
     if (memory_on_stack && available_glyphs >= totalGlyphs) {
         glyphLayout.grow(glyphLayout.data(), totalGlyphs);
-        return;
+        return true;
     }
 
     int space_charAttributes = sizeof(HB_CharAttributes)*string.length()/sizeof(void*) + 1;
@@ -2103,11 +2129,21 @@
     int space_glyphs = QGlyphLayout::spaceNeededForGlyphLayout(totalGlyphs)/sizeof(void*) + 2;
 
     int newAllocated = space_charAttributes + space_glyphs + space_logClusters;
-    Q_ASSERT(newAllocated >= allocated);
+    // These values can be negative if the length of string/glyphs causes overflow,
+    // we can't layout such a long string all at once, so return false here to
+    // indicate there is a failure
+    if (space_charAttributes < 0 || space_logClusters < 0 || space_glyphs < 0 || newAllocated < allocated) {
+        layoutState = LayoutFailed;
+        return false;
+    }
+
     void **newMem = memory;
     newMem = (void **)::realloc(memory_on_stack ? 0 : memory, newAllocated*sizeof(void *));
-    Q_CHECK_PTR(newMem);
-    if (memory_on_stack && newMem)
+    if (!newMem) {
+        layoutState = LayoutFailed;
+        return false;
+    }
+    if (memory_on_stack)
         memcpy(newMem, memory, allocated*sizeof(void *));
     memory = newMem;
     memory_on_stack = false;
@@ -2124,6 +2160,7 @@
     glyphLayout.grow(reinterpret_cast<char *>(m), totalGlyphs);
 
     allocated = newAllocated;
+    return true;
 }
 
 // grow to the new size, copying the existing data to the new layout
@@ -2155,7 +2192,7 @@
     } else {
         layoutData->used = 0;
         layoutData->hasBidi = false;
-        layoutData->inLayout = false;
+        layoutData->layoutState = LayoutEmpty;
         layoutData->haveCharAttributes = false;
     }
     for (int i = 0; i < lines.size(); ++i) {
@@ -2226,6 +2263,9 @@
     case ',':
     case '?':
     case '!':
+    case '@':
+    case '#':
+    case '$':
     case ':':
     case ';':
     case '-':
@@ -2246,6 +2286,7 @@
     case '*':
     case '\'':
     case '"':
+    case '`':
     case '~':
     case '|':
         return true;
@@ -2314,6 +2355,9 @@
                 shape(i);
 
             HB_CharAttributes *attributes = const_cast<HB_CharAttributes *>(this->attributes());
+            if (!attributes)
+                return QString();
+
             unsigned short *logClusters = this->logClusters(&si);
             QGlyphLayout glyphs = shapedGlyphs(&si);
 
@@ -2385,6 +2429,8 @@
         return QString();
 
     const HB_CharAttributes *attributes = this->attributes();
+    if (!attributes)
+        return QString();
 
     if (mode == Qt::ElideRight) {
         QFixed currentWidth;