From 2a19a7d52ae0c0748e86514daac14e558dfaabca Mon Sep 17 00:00:00 2001 From: "pavlov@pavlov.net" Date: Tue, 6 Nov 2007 22:35:38 -0800 Subject: [PATCH] bug 394751. hang with long lines of text and page break characters. patch from Chris Pearce . r=me --- gfx/thebes/src/gfxWindowsFonts.cpp | 119 ++++++++++++++++++++++++++--- 1 file changed, 109 insertions(+), 10 deletions(-) diff --git a/gfx/thebes/src/gfxWindowsFonts.cpp b/gfx/thebes/src/gfxWindowsFonts.cpp index 320a5dbaa81..a021e2dd157 100644 --- a/gfx/thebes/src/gfxWindowsFonts.cpp +++ b/gfx/thebes/src/gfxWindowsFonts.cpp @@ -879,6 +879,8 @@ static const char *sCJKLangGroup[] = { #define STATIC_STRING_LENGTH 100 +#define ESTIMATE_MAX_GLYPHS(L) (((3 * (L)) >> 1) + 16) + class UniscribeItem { public: @@ -890,9 +892,10 @@ public: mItemString(aString), mItemLength(aLength), mAlternativeString(nsnull), mScriptItem(aItem), mScript(aItem->a.eScript), mGroup(aGroup), - mNumGlyphs(0), mMaxGlyphs((int)(1.5 * aLength) + 16), + mNumGlyphs(0), mMaxGlyphs(ESTIMATE_MAX_GLYPHS(aLength)), mFontSelected(PR_FALSE) { + NS_ASSERTION(mMaxGlyphs < 65535, "UniscribeItem is too big, ScriptShape() will fail!"); mGlyphs.SetLength(mMaxGlyphs); mClusters.SetLength(mItemLength + 1); mAttr.SetLength(mMaxGlyphs); @@ -1479,9 +1482,9 @@ private: #define AVERAGE_ITEM_LENGTH 40 - nsAutoTArray mGlyphs; + nsAutoTArray mGlyphs; nsAutoTArray mClusters; - nsAutoTArray mAttr; + nsAutoTArray mAttr; nsAutoTArray mOffsets; nsAutoTArray mAdvances; @@ -1498,6 +1501,46 @@ private: nsTArray mRanges; }; + +#define MAX_ITEM_LENGTH 32768 + + + +static PRUint32 FindNextItemStart(int aOffset, int aLimit, + nsTArray &aLogAttr, + const PRUnichar *aString) +{ + if (aOffset + MAX_ITEM_LENGTH >= aLimit) { + // The item starting at aOffset can't be longer than the max length, + // so starting the next item at aLimit won't cause ScriptShape() to fail. + return aLimit; + } + + // Try to start the next item before or after a space, since spaces + // don't kern or ligate. + PRUint32 off; + int boundary = -1; + for (off = MAX_ITEM_LENGTH; off > 1; --off) { + if (aLogAttr[off].fCharStop) { + if (off > boundary) { + boundary = off; + } + if (aString[aOffset+off] == ' ' || aString[aOffset+off - 1] == ' ') + return aOffset+off; + } + } + + // Try to start the next item at the last cluster boundary in the range. + if (boundary > 0) { + return aOffset+boundary; + } + + // No nice cluster boundaries inside MAX_ITEM_LENGTH characters, break + // on the size limit. It won't be visually plesaing, but at least it + // won't cause ScriptShape() to fail. + return aOffset + MAX_ITEM_LENGTH; +} + class Uniscribe { public: @@ -1506,8 +1549,6 @@ public: mItems(nsnull) { } ~Uniscribe() { - if (mItems) - free(mItems); } void Init() { @@ -1519,22 +1560,78 @@ public: mState.fOverrideDirection = PR_TRUE; } +private: + + // Append mItems[aIndex] to aDest, adding extra items to aDest to ensure + // that no item is too long for ScriptShape() to handle. See bug 366643. + nsresult CopyItemSplitOversize(int aIndex, nsTArray &aDest) { + aDest.AppendElement(mItems[aIndex]); + const int itemLength = mItems[aIndex+1].iCharPos - mItems[aIndex].iCharPos; + if (ESTIMATE_MAX_GLYPHS(itemLength) > 65535) { + // This items length would cause ScriptShape() to fail. We need to + // add extra items here so that no item's length could cause the fail. + + // Get cluster boundaries, so we can break cleanly if possible. + nsTArray logAttr; + if (!logAttr.SetLength(itemLength)) + return NS_ERROR_FAILURE; + HRESULT rv= ScriptBreak(mString+mItems[aIndex].iCharPos, itemLength, + &mItems[aIndex].a, logAttr.Elements()); + if (FAILED(rv)) + return NS_ERROR_FAILURE; + + const int nextItemStart = mItems[aIndex+1].iCharPos; + int start = FindNextItemStart(mItems[aIndex].iCharPos, + nextItemStart, logAttr, mString); + + while (start < nextItemStart) { + SCRIPT_ITEM item = mItems[aIndex]; + item.iCharPos = start; + aDest.AppendElement(item); + start = FindNextItemStart(start, nextItemStart, logAttr, mString); + } + } + return NS_OK; + } + +public: + int Itemize() { HRESULT rv; int maxItems = 5; Init(); + // Allocate space for one more item than expected, to handle a rare // overflow in ScriptItemize (pre XP SP2). See bug 366643. - mItems = (SCRIPT_ITEM *)malloc((maxItems + 1) * sizeof(SCRIPT_ITEM)); + if (!mItems.SetLength(maxItems + 1)) { + return 0; + } while ((rv = ScriptItemize(mString, mLength, maxItems, &mControl, &mState, - mItems, &mNumItems)) == E_OUTOFMEMORY) { + mItems.Elements(), &mNumItems)) == E_OUTOFMEMORY) { maxItems *= 2; - mItems = (SCRIPT_ITEM *)realloc(mItems, (maxItems + 1) * sizeof(SCRIPT_ITEM)); + if (!mItems.SetLength(maxItems + 1)) { + return 0; + } Init(); } + if (ESTIMATE_MAX_GLYPHS(mLength) > 65535) { + // Any item of length > 43680 will cause ScriptShape() to fail, as its + // mMaxGlyphs value will be greater than 65535 (43680*1.5+16>65535). So we + // need to break up items which are longer than that upon cluster boundaries. + // See bug 394751 for details. + nsTArray items; + for (int i=0; i mItems; int mNumItems; }; @@ -1598,12 +1695,14 @@ gfxWindowsFontGroup::InitTextRunUniscribe(gfxContext *aContext, gfxTextRun *aRun if (!item->ShapingEnabled()) item->EnableShaping(); - while (FAILED(item->Shape())) { + rv = item->Shape(); + if (FAILED(rv)) { PR_LOG(gFontLog, PR_LOG_DEBUG, ("shaping failed")); // we know we have the glyphs to display this font already // so Uniscribe just doesn't know how to shape the script. // Render the glyphs without shaping. item->DisableShaping(); + rv = item->Shape(); } NS_ASSERTION(SUCCEEDED(rv), "Failed to shape -- we should never hit this");