fixing font selection bug dealing with zero-width glyphs and begin using ScriptGetCMap when possible. bug 376300. r=roc

This commit is contained in:
pavlov@pavlov.net 2007-04-02 23:32:23 -07:00
Родитель 89069dea82
Коммит 6486ab880c
2 изменённых файлов: 61 добавлений и 42 удалений

Просмотреть файл

@ -115,7 +115,7 @@ gfxTextRunCache::Shutdown()
static PRUint32 static PRUint32
ComputeFlags(PRBool aIsRTL, PRBool aEnableSpacing) ComputeFlags(PRBool aIsRTL, PRBool aEnableSpacing)
{ {
PRUint32 flags = gfxTextRunFactory::TEXT_HAS_SURROGATES; PRUint32 flags = 0;
if (aIsRTL) { if (aIsRTL) {
flags |= gfxTextRunFactory::TEXT_IS_RTL; flags |= gfxTextRunFactory::TEXT_IS_RTL;
} }
@ -139,11 +139,21 @@ gfxTextRunCache::GetOrMakeTextRun(gfxContext *aContext, gfxFontGroup *aFontGroup
aContext, nsnull, nsnull, &skipChars, nsnull, 0, aAppUnitsPerDevUnit, aContext, nsnull, nsnull, &skipChars, nsnull, 0, aAppUnitsPerDevUnit,
ComputeFlags(aIsRTL, aEnableSpacing) ComputeFlags(aIsRTL, aEnableSpacing)
}; };
if (IsAscii(aString, aLength)) if (IsAscii(aString, aLength))
params.mFlags |= gfxTextRunFactory::TEXT_IS_ASCII; params.mFlags |= gfxTextRunFactory::TEXT_IS_ASCII;
// else if (Is8Bit(aString, aLength)) // else if (Is8Bit(aString, aLength))
// params.mFlags |= gfxTextRunFactory::TEXT_IS_8BIT; // params.mFlags |= gfxTextRunFactory::TEXT_IS_8BIT;
if (!(params.mFlags & gfxTextRunFactory::TEXT_IS_ASCII)) {
for (PRUint32 i = 0; i < aLength; ++i) {
if (NS_IS_HIGH_SURROGATE(aString[i])) {
params.mFlags |= gfxTextRunFactory::TEXT_HAS_SURROGATES;
break;
}
}
}
gfxTextRun *tr = nsnull; gfxTextRun *tr = nsnull;
// Don't cache textruns that use spacing // Don't cache textruns that use spacing
if (!gDisableCache && !aEnableSpacing) { if (!gDisableCache && !aEnableSpacing) {

Просмотреть файл

@ -976,6 +976,8 @@ public:
const PRUnichar *str = const PRUnichar *str =
mAlternativeString ? mAlternativeString : mString; mAlternativeString ? mAlternativeString : mString;
mScriptItem->a.fLogicalOrder = PR_TRUE; mScriptItem->a.fLogicalOrder = PR_TRUE;
mScriptItem->a.s.fDisplayZWG = PR_TRUE;
rv = ScriptShape(shapeDC, mCurrentFont->ScriptCache(), rv = ScriptShape(shapeDC, mCurrentFont->ScriptCache(),
str, mLength, str, mLength,
mMaxGlyphs, &mScriptItem->a, mMaxGlyphs, &mScriptItem->a,
@ -1016,55 +1018,44 @@ public:
GenerateAlternativeString(); GenerateAlternativeString();
} }
static PRBool IsZeroWidthUnicodeChar(PRUnichar aChar) { /* this should only be called if there are no surrogates
return aChar == 0x200b; // ZWSP * in the string */
} PRBool IsMissingGlyphsCMap() {
HRESULT rv;
HDC cmapDC = nsnull;
/** while (PR_TRUE) {
* @param aCharIndex the index in the string of the first character rv = ScriptGetCMap(cmapDC, mCurrentFont->ScriptCache(),
* for the cluster this glyph belongs to mString, mLength, 0, mGlyphs);
*/
PRBool IsGlyphMissing(SCRIPT_FONTPROPERTIES *aSFP, PRUint32 aGlyphIndex, if (rv == E_PENDING) {
PRUint32 aCharIndex) { SelectFont();
WORD glyph = mGlyphs[aGlyphIndex]; cmapDC = mDC;
if (glyph == aSFP->wgDefault || continue;
(glyph == aSFP->wgInvalid && glyph != aSFP->wgBlank)) }
return PR_TRUE;
// I'm not sure that claiming glyphs are missing if they're zero width is valid if (rv == S_OK)
// but we're seeing cases where some fonts return glyphs such as 0x03 and 0x04 return PR_FALSE;
// which are zero width and non-invalidGlyph.
// At any rate, only make this check for non-complex scripts. PR_LOG(gFontLog, PR_LOG_WARNING, ("cmap is missing a glyph"));
if (mAttr[aGlyphIndex].fZeroWidth && !ScriptProperties()->fComplex && for (PRUint32 i = 0; i < mLength; i++)
!IsZeroWidthUnicodeChar(mString[aCharIndex])) { PR_LOG(gFontLog, PR_LOG_WARNING, (" - %d", mGlyphs[i]));
PR_LOG(gFontLog, PR_LOG_WARNING, ("crappy font? glyph %04x is zero-width", glyph));
return PR_TRUE; return PR_TRUE;
} }
}
PRBool IsGlyphMissing(SCRIPT_FONTPROPERTIES *aSFP, PRUint32 aGlyphIndex) {
if (mGlyphs[aGlyphIndex] == aSFP->wgDefault)
return PR_TRUE;
return PR_FALSE; return PR_FALSE;
} }
/*
* Fonts are hopelessly inconsistent about how they handle characters that
* they don't have glyphs for.
* Some will return the glyph which ScriptFontProperties() lists as
* wgDefault; some will return the glyph listed as wgInvalid.
* Some list the same glyph as both wgInvalid and wgBlank, and use this
* glyph for valid whitespace characters.
* In some cases wgBlank with zero width represents a missing glyph,
* but in other cases, especially in complex scripts, wgBlank with zero
* width represents a valid zero width character such a zero width joiner
* or non-joiner.
*/
PRBool IsMissingGlyphs() { PRBool IsMissingGlyphs() {
SCRIPT_FONTPROPERTIES sfp; SCRIPT_FONTPROPERTIES sfp;
ScriptFontProperties(&sfp); ScriptFontProperties(&sfp);
PRUint32 charIndex = 0; PRUint32 charIndex = 0;
for (int i = 0; i < mNumGlyphs; ++i) { for (int i = 0; i < mNumGlyphs; ++i) {
// advance charIndex to the first character such that glyph i if (IsGlyphMissing(&sfp, i))
// is in its cluster (i.e. mClusters[charIndex + 1] > i)
while (charIndex + 1 < mLength && mClusters[charIndex + 1] <= i) {
++charIndex;
}
if (IsGlyphMissing(&sfp, i, charIndex))
return PR_TRUE; return PR_TRUE;
#ifdef DEBUG_pavlov // excess debugging code #ifdef DEBUG_pavlov // excess debugging code
PR_LOG(gFontLog, PR_LOG_DEBUG, ("%04x %04x %04x", sfp.wgBlank, sfp.wgDefault, sfp.wgInvalid)); PR_LOG(gFontLog, PR_LOG_DEBUG, ("%04x %04x %04x", sfp.wgBlank, sfp.wgDefault, sfp.wgInvalid));
@ -1173,7 +1164,7 @@ public:
PRUint32 k = mClusters[offset]; PRUint32 k = mClusters[offset];
PRUint32 glyphCount = mNumGlyphs - k; PRUint32 glyphCount = mNumGlyphs - k;
PRUint32 nextClusterOffset; PRUint32 nextClusterOffset;
PRBool missing = IsGlyphMissing(&sfp, k, offset); PRBool missing = IsGlyphMissing(&sfp, k);
for (nextClusterOffset = offset + 1; nextClusterOffset < mLength; ++nextClusterOffset) { for (nextClusterOffset = offset + 1; nextClusterOffset < mLength; ++nextClusterOffset) {
if (mClusters[nextClusterOffset] > k) { if (mClusters[nextClusterOffset] > k) {
glyphCount = mClusters[nextClusterOffset] - k; glyphCount = mClusters[nextClusterOffset] - k;
@ -1182,7 +1173,7 @@ public:
} }
PRUint32 j; PRUint32 j;
for (j = 1; j < glyphCount; ++j) { for (j = 1; j < glyphCount; ++j) {
if (IsGlyphMissing(&sfp, k + j, offset)) { if (IsGlyphMissing(&sfp, k + j)) {
missing = PR_TRUE; missing = PR_TRUE;
} }
} }
@ -1466,6 +1457,10 @@ public:
mIsComplex(ScriptIsComplex(aString, aLength, SIC_COMPLEX) == S_OK), mIsComplex(ScriptIsComplex(aString, aLength, SIC_COMPLEX) == S_OK),
mItems(nsnull) { mItems(nsnull) {
} }
~Uniscribe() {
if (mItems)
free(mItems);
}
void Init() { void Init() {
memset(&mControl, 0, sizeof(SCRIPT_CONTROL)); memset(&mControl, 0, sizeof(SCRIPT_CONTROL));
@ -1567,6 +1562,15 @@ gfxWindowsFontGroup::InitTextRunUniscribe(gfxContext *aContext, gfxTextRun *aRun
if (PR_LOG_TEST(gFontLog, PR_LOG_DEBUG)) if (PR_LOG_TEST(gFontLog, PR_LOG_DEBUG))
PR_LOG(gFontLog, PR_LOG_DEBUG, ("trying: %s", NS_LossyConvertUTF16toASCII(font->GetName()).get())); PR_LOG(gFontLog, PR_LOG_DEBUG, ("trying: %s", NS_LossyConvertUTF16toASCII(font->GetName()).get()));
PRBool cmapHasGlyphs = PR_FALSE; // false means "maybe" here
if (!giveUp && !(aRun->GetFlags() & TEXT_HAS_SURROGATES)) {
if (item->IsMissingGlyphsCMap())
continue;
else
cmapHasGlyphs = PR_TRUE;
}
rv = item->Shape(); rv = item->Shape();
if (giveUp) { if (giveUp) {
@ -1574,8 +1578,13 @@ gfxWindowsFontGroup::InitTextRunUniscribe(gfxContext *aContext, gfxTextRun *aRun
PR_LOG(gFontLog, PR_LOG_DEBUG, ("%s - gave up", NS_LossyConvertUTF16toASCII(font->GetName()).get())); PR_LOG(gFontLog, PR_LOG_DEBUG, ("%s - gave up", NS_LossyConvertUTF16toASCII(font->GetName()).get()));
goto SCRIPT_PLACE; goto SCRIPT_PLACE;
} }
if (FAILED(rv) || item->IsMissingGlyphs())
if (FAILED(rv))
continue; continue;
if (!cmapHasGlyphs && item->IsMissingGlyphs())
continue;
} else { } else {
#if 0 #if 0
/* code to try all the fonts again without shaping on. /* code to try all the fonts again without shaping on.