зеркало из https://github.com/mozilla/pjs.git
Bug 375662. Fix ATSUI issues with cluster detection. Also fix extraction of glyphs when trailing whitespace doesn't get the dominant text direction; remove the current workaround and replace it with a simpler workaround. r=vlad
This commit is contained in:
Родитель
59edcd91ad
Коммит
5bb822a577
|
@ -101,11 +101,14 @@ public:
|
|||
Parameters* aParams);
|
||||
virtual gfxTextRun *MakeTextRun(const PRUint8* aString, PRUint32 aLength,
|
||||
Parameters* aParams);
|
||||
// Here, aString is actually aLength + aHeaderChars*2 chars long; the first char
|
||||
// may be a LRO or RLO bidi control character to force setting the direction
|
||||
// for all characters, and if so the last character will be a PDF
|
||||
// When aWrapped is true, the string includes bidi control
|
||||
// characters. The first character will be LRO or LRO to force setting the
|
||||
// direction for all characters, the last character is PDF, and the
|
||||
// second to last character is a non-whitespace character --- to ensure
|
||||
// that there is no "trailing whitespace" in the string, see
|
||||
// http://weblogs.mozillazine.org/roc/archives/2007/02/superlaser_targ.html#comments
|
||||
gfxTextRun *MakeTextRunInternal(const PRUnichar *aString, PRUint32 aLength,
|
||||
Parameters *aParams, PRUint32 aheaderChars);
|
||||
PRBool aWrapped, Parameters *aParams);
|
||||
|
||||
ATSUFontFallbacks *GetATSUFontFallbacksPtr() { return &mFallbacks; }
|
||||
|
||||
|
@ -121,7 +124,7 @@ protected:
|
|||
void *closure);
|
||||
|
||||
void InitTextRun(gfxTextRun *aRun, const PRUnichar *aString, PRUint32 aLength,
|
||||
PRUint32 aHeaderChars);
|
||||
PRBool aWrapped);
|
||||
|
||||
ATSUFontFallbacks mFallbacks;
|
||||
};
|
||||
|
|
|
@ -349,7 +349,7 @@ gfxAtsuiFontGroup::Copy(const gfxFontStyle *aStyle)
|
|||
}
|
||||
|
||||
static void
|
||||
SetupClusterBoundaries(gfxTextRun *aTextRun, const PRUnichar *aString, PRUint32 aLength)
|
||||
SetupClusterBoundaries(gfxTextRun *aTextRun, const PRUnichar *aString)
|
||||
{
|
||||
TextBreakLocatorRef locator;
|
||||
OSStatus status = UCCreateTextBreakLocator(NULL, 0, kUCTextBreakClusterMask,
|
||||
|
@ -357,50 +357,51 @@ SetupClusterBoundaries(gfxTextRun *aTextRun, const PRUnichar *aString, PRUint32
|
|||
if (status != noErr)
|
||||
return;
|
||||
UniCharArrayOffset breakOffset;
|
||||
status = UCFindTextBreak(locator, kUCTextBreakClusterMask, 0, aString, aLength,
|
||||
0, &breakOffset);
|
||||
PRUint32 length = aTextRun->GetLength();
|
||||
status = UCFindTextBreak(locator, kUCTextBreakClusterMask,
|
||||
kUCTextBreakLeadingEdgeMask, aString, length, 0, &breakOffset);
|
||||
if (status != noErr) {
|
||||
UCDisposeTextBreakLocator(&locator);
|
||||
return;
|
||||
}
|
||||
NS_ASSERTION(breakOffset == 0, "Cluster should start at offset zero");
|
||||
gfxTextRun::CompressedGlyph g;
|
||||
while (breakOffset < aLength) {
|
||||
PRUint32 curOffset = breakOffset;
|
||||
PRUint32 lastBreak = 1;
|
||||
do {
|
||||
while (lastBreak < breakOffset) {
|
||||
aTextRun->SetCharacterGlyph(lastBreak, g.SetClusterContinuation());
|
||||
++lastBreak;
|
||||
}
|
||||
status = UCFindTextBreak(locator, kUCTextBreakClusterMask,
|
||||
kUCTextBreakIterateMask,
|
||||
aString, aLength, curOffset, &breakOffset);
|
||||
kUCTextBreakIterateMask|kUCTextBreakLeadingEdgeMask,
|
||||
aString, length, breakOffset, &breakOffset);
|
||||
if (status != noErr) {
|
||||
UCDisposeTextBreakLocator(&locator);
|
||||
return;
|
||||
}
|
||||
PRUint32 j;
|
||||
for (j = curOffset + 1; j < breakOffset; ++j) {
|
||||
aTextRun->SetCharacterGlyph(j, g.SetClusterContinuation());
|
||||
}
|
||||
}
|
||||
NS_ASSERTION(breakOffset == aLength, "Should have found a final break");
|
||||
++lastBreak;
|
||||
} while (breakOffset < length);
|
||||
NS_ASSERTION(breakOffset == length, "Should have found a final break");
|
||||
UCDisposeTextBreakLocator(&locator);
|
||||
}
|
||||
|
||||
gfxTextRun *
|
||||
gfxAtsuiFontGroup::MakeTextRunInternal(const PRUnichar *aString, PRUint32 aLength,
|
||||
Parameters *aParams, PRUint32 aHeaderChars)
|
||||
PRBool aWrapped, Parameters *aParams)
|
||||
{
|
||||
// NS_ASSERTION(!(aParams->mFlags & TEXT_NEED_BOUNDING_BOX),
|
||||
// "Glyph extents not yet supported");
|
||||
|
||||
gfxTextRun *textRun = new gfxTextRun(aParams, aLength);
|
||||
gfxTextRun *textRun = new gfxTextRun(aParams, aLength - (aWrapped ? 3 : 0));
|
||||
if (!textRun)
|
||||
return nsnull;
|
||||
|
||||
// There's a one-char header in the string and a one-char trailer
|
||||
textRun->RecordSurrogates(aString + aHeaderChars);
|
||||
const PRUnichar *realString = aString + (aWrapped ? 1 : 0);
|
||||
textRun->RecordSurrogates(realString);
|
||||
if (!(aParams->mFlags & TEXT_IS_8BIT)) {
|
||||
SetupClusterBoundaries(textRun, aString + aHeaderChars, aLength);
|
||||
SetupClusterBoundaries(textRun, realString);
|
||||
}
|
||||
|
||||
InitTextRun(textRun, aString, aLength, aHeaderChars);
|
||||
InitTextRun(textRun, aString, aLength, aWrapped);
|
||||
return textRun;
|
||||
}
|
||||
|
||||
|
@ -422,8 +423,11 @@ gfxAtsuiFontGroup::MakeTextRun(const PRUnichar *aString, PRUint32 aLength,
|
|||
nsAutoString utf16;
|
||||
AppendDirectionalIndicator(aParams->mFlags, utf16);
|
||||
utf16.Append(aString, aLength);
|
||||
// Ensure that none of the whitespace in the run is considered "trailing"
|
||||
// by ATSUI's bidi algorithm
|
||||
utf16.Append('.');
|
||||
utf16.Append(UNICODE_PDF);
|
||||
return MakeTextRunInternal(utf16.get(), aLength, aParams, 1);
|
||||
return MakeTextRunInternal(utf16.get(), utf16.Length(), PR_TRUE, aParams);
|
||||
}
|
||||
|
||||
gfxTextRun *
|
||||
|
@ -434,16 +438,16 @@ gfxAtsuiFontGroup::MakeTextRun(const PRUint8 *aString, PRUint32 aLength,
|
|||
nsDependentCSubstring cString(reinterpret_cast<const char*>(aString),
|
||||
reinterpret_cast<const char*>(aString + aLength));
|
||||
nsAutoString utf16;
|
||||
PRUint32 headerChars = 0;
|
||||
if (aParams->mFlags & TEXT_IS_RTL) {
|
||||
PRBool wrapBidi = (aParams->mFlags & TEXT_IS_RTL) != 0;
|
||||
if (wrapBidi) {
|
||||
AppendDirectionalIndicator(aParams->mFlags, utf16);
|
||||
headerChars = 1;
|
||||
}
|
||||
AppendASCIItoUTF16(cString, utf16);
|
||||
if (aParams->mFlags & TEXT_IS_RTL) {
|
||||
if (wrapBidi) {
|
||||
utf16.Append('.');
|
||||
utf16.Append(UNICODE_PDF);
|
||||
}
|
||||
return MakeTextRunInternal(utf16.get(), aLength, aParams, headerChars);
|
||||
return MakeTextRunInternal(utf16.get(), utf16.Length(), wrapBidi, aParams);
|
||||
}
|
||||
|
||||
gfxAtsuiFont*
|
||||
|
@ -616,7 +620,8 @@ SetGlyphsForCharacterGroup(ATSLayoutRecord *aGlyphs, PRUint32 aGlyphCount,
|
|||
|
||||
static void
|
||||
PostLayoutCallback(ATSULineRef aLine, gfxTextRun *aRun,
|
||||
const PRUnichar *aString, const PRPackedBool *aUnmatched)
|
||||
const PRUnichar *aString, PRBool aWrapped,
|
||||
const PRPackedBool *aUnmatched)
|
||||
{
|
||||
// AutoLayoutDataArrayPtr advanceDeltasArray(aLine, kATSUDirectDataAdvanceDeltaFixedArray);
|
||||
// Fixed *advanceDeltas = NS_STATIC_CAST(Fixed *, advanceDeltasArray.mArray);
|
||||
|
@ -640,50 +645,25 @@ PostLayoutCallback(ATSULineRef aLine, gfxTextRun *aRun,
|
|||
return;
|
||||
|
||||
PRUint32 appUnitsPerDevUnit = aRun->GetAppUnitsPerDevUnit();
|
||||
|
||||
// ATSUI seems to have a bug where trailing whitespace in a run,
|
||||
// even after we've forced the direction with LRO/RLO/PDF, does not
|
||||
// necessarily get the required direction.
|
||||
// A specific testcase is "RLO space space PDF"; we get
|
||||
// glyphRecords[0] = { originalOffset:0, realPos:0; }
|
||||
// glyphRecords[1] = { originalOffset:2, realPos:>0 }
|
||||
// We count the number of glyphs that appear to be this sort of erroneous
|
||||
// whitespace.
|
||||
// In RTL situations, the bug manifests as the trailing whitespace characters
|
||||
// being rendered in LTR order at the end of the glyph array.
|
||||
// In LTR situations, the bug manifests as the trailing whitespace characters
|
||||
// being rendered in RTL order at the start of the glyph array.
|
||||
// Compensate for this bug now by detecting those characters, setting up
|
||||
// the glyphs for those characters, and then chopping those glyphs off
|
||||
// the glyph array we need to look at.
|
||||
PRUint32 stringTailOffset = aRun->GetLength() - 1;
|
||||
PRBool isRTL = aRun->IsRightToLeft();
|
||||
if (isRTL) {
|
||||
while (numGlyphs > 0 &&
|
||||
glyphRecords[numGlyphs - 1].originalOffset == stringTailOffset*2 &&
|
||||
aString[stringTailOffset] == ' ') {
|
||||
SetGlyphsForCharacterGroup(glyphRecords + numGlyphs - 1, 1,
|
||||
baselineDeltas ? baselineDeltas + numGlyphs - 1 : nsnull,
|
||||
appUnitsPerDevUnit, aRun, aUnmatched,
|
||||
aString);
|
||||
--stringTailOffset;
|
||||
--numGlyphs;
|
||||
}
|
||||
} else {
|
||||
while (numGlyphs > 0 &&
|
||||
glyphRecords[0].originalOffset == stringTailOffset*2 &&
|
||||
aString[stringTailOffset] == ' ') {
|
||||
SetGlyphsForCharacterGroup(glyphRecords, 1,
|
||||
baselineDeltas,
|
||||
appUnitsPerDevUnit, aRun, aUnmatched,
|
||||
aString);
|
||||
--stringTailOffset;
|
||||
--numGlyphs;
|
||||
++glyphRecords;
|
||||
|
||||
if (aWrapped) {
|
||||
// The glyph array includes a glyph for the artificial trailing
|
||||
// non-whitespace character. Strip that glyph from the array now.
|
||||
if (isRTL) {
|
||||
NS_ASSERTION(glyphRecords[0].originalOffset == aRun->GetLength()*2,
|
||||
"Couldn't find glyph for trailing marker");
|
||||
glyphRecords++;
|
||||
} else {
|
||||
NS_ASSERTION(glyphRecords[numGlyphs - 1].originalOffset == aRun->GetLength()*2,
|
||||
"Couldn't find glyph for trailing marker");
|
||||
}
|
||||
--numGlyphs;
|
||||
if (numGlyphs == 0)
|
||||
return;
|
||||
}
|
||||
|
||||
// Now process the rest of the glyphs, which should basically be in
|
||||
// Now process the glyphs, which should basically be in
|
||||
// the textrun's desired order, so process them in textrun order
|
||||
PRInt32 direction = PRInt32(aRun->GetDirection());
|
||||
while (numGlyphs > 0) {
|
||||
|
@ -703,6 +683,12 @@ PostLayoutCallback(ATSULineRef aLine, gfxTextRun *aRun,
|
|||
// In this case we need to make sure the glyph for the consonant
|
||||
// is added to the group containing the vowel.
|
||||
if (lastOffset < glyphOffset) {
|
||||
if (!aRun->IsClusterStart(glyphOffset/2)) {
|
||||
// next character is a cluster continuation,
|
||||
// add it to the current group
|
||||
lastOffset = glyphOffset;
|
||||
continue;
|
||||
}
|
||||
// We could be at the end of a character group
|
||||
if (glyph->glyphID != ATSUI_SPECIAL_GLYPH_ID) {
|
||||
// Next character is a normal character, stop the group here
|
||||
|
@ -741,6 +727,9 @@ PostLayoutCallback(ATSULineRef aLine, gfxTextRun *aRun,
|
|||
struct PostLayoutCallbackClosure {
|
||||
gfxTextRun *mTextRun;
|
||||
const PRUnichar *mString;
|
||||
// This is true when we inserted an artifical trailing character at the
|
||||
// end of the string when computing the ATSUI layout.
|
||||
PRPackedBool mWrapped;
|
||||
// Either null or an array of stringlength booleans set to true for
|
||||
// each character that did not match any fonts
|
||||
nsAutoArrayPtr<PRPackedBool> mUnmatchedChars;
|
||||
|
@ -757,7 +746,8 @@ PostLayoutOperationCallback(ATSULayoutOperationSelector iCurrentOperation,
|
|||
ATSULayoutOperationCallbackStatus *oCallbackStatus)
|
||||
{
|
||||
PostLayoutCallback(iLineRef, gCallbackClosure->mTextRun,
|
||||
gCallbackClosure->mString, gCallbackClosure->mUnmatchedChars);
|
||||
gCallbackClosure->mString, gCallbackClosure->mWrapped,
|
||||
gCallbackClosure->mUnmatchedChars);
|
||||
*oCallbackStatus = kATSULayoutOperationCallbackStatusContinue;
|
||||
return noErr;
|
||||
}
|
||||
|
@ -765,30 +755,35 @@ PostLayoutOperationCallback(ATSULayoutOperationSelector iCurrentOperation,
|
|||
void
|
||||
gfxAtsuiFontGroup::InitTextRun(gfxTextRun *aRun,
|
||||
const PRUnichar *aString, PRUint32 aLength,
|
||||
PRUint32 aHeaderChars)
|
||||
PRBool aWrapped)
|
||||
{
|
||||
OSStatus status;
|
||||
gfxAtsuiFont *atsuiFont = GetFontAt(0);
|
||||
ATSUStyle mainStyle = atsuiFont->GetATSUStyle();
|
||||
nsTArray<ATSUStyle> stylesToDispose;
|
||||
PRUint32 headerChars = aWrapped ? 1 : 0;
|
||||
const PRUnichar *realString = aString + headerChars;
|
||||
PRUint32 realLength = aRun->GetLength();
|
||||
NS_ASSERTION(realLength == aLength - (aWrapped ? 3 : 0),
|
||||
"Length mismatch");
|
||||
|
||||
#ifdef DUMP_TEXT_RUNS
|
||||
NS_ConvertUTF16toUTF8 str(aString + 1, aLength);
|
||||
NS_ConvertUTF16toUTF8 str(realString, realLength);
|
||||
NS_ConvertUTF16toUTF8 families(mFamilies);
|
||||
printf("%p(%s) TEXTRUN \"%s\" ENDTEXTRUN\n", this, families.get(), str.get());
|
||||
#endif
|
||||
|
||||
UniCharCount runLengths = aLength;
|
||||
UniCharCount runLengths = realLength;
|
||||
ATSUTextLayout layout;
|
||||
// The string is actually aLength + 2*aHeaderChars chars, with optionally
|
||||
// a header char to set the direction and a trailer char to pop it. So
|
||||
// create the text layout giving the whole string as context, although we
|
||||
// only want glyphs for the inner substring.
|
||||
// Create the text layout for the whole string, but only produce glyphs
|
||||
// for the text inside LRO/RLO - PDF, if present. For wrapped strings
|
||||
// we do need to produce glyphs for the trailing non-whitespace
|
||||
// character to ensure that ATSUI treats all whitespace as non-trailing.
|
||||
status = ATSUCreateTextLayoutWithTextPtr
|
||||
(aString,
|
||||
aHeaderChars,
|
||||
headerChars,
|
||||
realLength + (aWrapped ? 1 : 0),
|
||||
aLength,
|
||||
aLength + aHeaderChars*2,
|
||||
1,
|
||||
&runLengths,
|
||||
&mainStyle,
|
||||
|
@ -797,8 +792,8 @@ gfxAtsuiFontGroup::InitTextRun(gfxTextRun *aRun,
|
|||
|
||||
PostLayoutCallbackClosure closure;
|
||||
closure.mTextRun = aRun;
|
||||
// Pass the real string to the closure, ignoring the header
|
||||
closure.mString = aString + aHeaderChars;
|
||||
closure.mString = realString;
|
||||
closure.mWrapped = aWrapped;
|
||||
NS_ASSERTION(!gCallbackClosure, "Reentering InitTextRun? Expect disaster!");
|
||||
gCallbackClosure = &closure;
|
||||
|
||||
|
@ -833,9 +828,9 @@ gfxAtsuiFontGroup::InitTextRun(gfxTextRun *aRun,
|
|||
|
||||
/* Now go through and update the styles for the text, based on font matching. */
|
||||
|
||||
UniCharArrayOffset runStart = aHeaderChars;
|
||||
UniCharCount totalLength = aLength + aHeaderChars;
|
||||
UniCharCount runLength = aLength;
|
||||
UniCharArrayOffset runStart = headerChars;
|
||||
UniCharCount totalLength = runStart + realLength;
|
||||
UniCharCount runLength = realLength;
|
||||
|
||||
//fprintf (stderr, "==== Starting font maching [string length: %d]\n", totalLength);
|
||||
while (runStart < totalLength) {
|
||||
|
@ -848,7 +843,7 @@ gfxAtsuiFontGroup::InitTextRun(gfxTextRun *aRun,
|
|||
if (status == noErr) {
|
||||
//fprintf (stderr, "ATSUMatchFontsToText returned noErr\n");
|
||||
// everything's good, finish up
|
||||
aRun->AddGlyphRun(atsuiFont, runStart - aHeaderChars);
|
||||
aRun->AddGlyphRun(atsuiFont, runStart - headerChars);
|
||||
break;
|
||||
} else if (status == kATSUFontsMatched) {
|
||||
//fprintf (stderr, "ATSUMatchFontsToText returned kATSUFontsMatched: FID %d\n", substituteFontID);
|
||||
|
@ -864,14 +859,14 @@ gfxAtsuiFontGroup::InitTextRun(gfxTextRun *aRun,
|
|||
ATSUSetAttributes (subStyle, 1, fontTags, fontArgSizes, fontArgs);
|
||||
|
||||
if (changedOffset > runStart) {
|
||||
aRun->AddGlyphRun(atsuiFont, runStart - aHeaderChars);
|
||||
aRun->AddGlyphRun(atsuiFont, runStart - headerChars);
|
||||
}
|
||||
|
||||
ATSUSetRunStyle (layout, subStyle, changedOffset, changedLength);
|
||||
|
||||
gfxAtsuiFont *font = FindFontFor(substituteFontID);
|
||||
if (font) {
|
||||
aRun->AddGlyphRun(font, changedOffset - aHeaderChars);
|
||||
aRun->AddGlyphRun(font, changedOffset - headerChars);
|
||||
}
|
||||
|
||||
stylesToDispose.AppendElement(subStyle);
|
||||
|
@ -879,7 +874,7 @@ gfxAtsuiFontGroup::InitTextRun(gfxTextRun *aRun,
|
|||
//fprintf (stderr, "ATSUMatchFontsToText returned kATSUFontsNotMatched\n");
|
||||
/* I need to select the last resort font; how the heck do I do that? */
|
||||
// Record which font is associated with these glyphs, anyway
|
||||
aRun->AddGlyphRun(atsuiFont, runStart - aHeaderChars);
|
||||
aRun->AddGlyphRun(atsuiFont, runStart - headerChars);
|
||||
|
||||
if (!closure.mUnmatchedChars) {
|
||||
closure.mUnmatchedChars = new PRPackedBool[aLength];
|
||||
|
@ -888,7 +883,7 @@ gfxAtsuiFontGroup::InitTextRun(gfxTextRun *aRun,
|
|||
}
|
||||
}
|
||||
if (closure.mUnmatchedChars) {
|
||||
memset(closure.mUnmatchedChars.get() + changedOffset - aHeaderChars,
|
||||
memset(closure.mUnmatchedChars.get() + changedOffset - headerChars,
|
||||
PR_TRUE, changedLength);
|
||||
}
|
||||
}
|
||||
|
@ -903,7 +898,7 @@ gfxAtsuiFontGroup::InitTextRun(gfxTextRun *aRun,
|
|||
// the result of this call.
|
||||
ATSTrapezoid trap;
|
||||
ItemCount trapCount;
|
||||
ATSUGetGlyphBounds(layout, 0, 0, aHeaderChars, aLength,
|
||||
ATSUGetGlyphBounds(layout, 0, 0, headerChars, realLength,
|
||||
kATSUseFractionalOrigins, 1, &trap, &trapCount);
|
||||
|
||||
ATSUDisposeTextLayout(layout);
|
||||
|
|
Загрузка…
Ссылка в новой задаче