Bug 361986. Exclude complex script codepoints for fonts that lack AAT morphing data. r=vlad,smontagu, sr=pavlov

This commit is contained in:
jdaggett@mozilla.com 2008-02-11 23:23:44 -08:00
parent 45477e7a1d
commit 94f07ba724
2 changed files with 187 additions and 5 deletions

View File

@ -59,9 +59,10 @@
class gfxSparseBitSet {
private:
enum { BLOCK_SIZE = 32 };
enum { BLOCK_SIZE = 32 }; // ==> 256 codepoints per block
enum { BLOCK_SIZE_BITS = BLOCK_SIZE * 8 };
enum { BLOCK_INDEX_SHIFT = 8 };
struct Block {
Block(unsigned char memsetValue = 0) { memset(mBits, memsetValue, BLOCK_SIZE); }
PRUint8 mBits[BLOCK_SIZE];
@ -75,9 +76,67 @@ public:
Block *block = mBlocks[blockIndex];
if (!block)
return PR_FALSE;
return ((block->mBits[(aIndex/8) & (BLOCK_SIZE - 1)]) & (1 << (aIndex & 0x7))) != 0;
return ((block->mBits[(aIndex>>3) & (BLOCK_SIZE - 1)]) & (1 << (aIndex & 0x7))) != 0;
}
PRBool TestRange(PRUint32 aStart, PRUint32 aEnd) {
PRUint32 startBlock, endBlock, blockLen;
// start point is beyond the end of the block array? return false immediately
startBlock = aStart >> BLOCK_INDEX_SHIFT;
blockLen = mBlocks.Length();
if (startBlock >= blockLen) return PR_FALSE;
// check for blocks in range, if none, return false
PRUint32 blockIndex;
PRBool hasBlocksInRange = PR_FALSE;
endBlock = aEnd >> BLOCK_INDEX_SHIFT;
blockIndex = startBlock;
for (blockIndex = startBlock; blockIndex <= endBlock; blockIndex++) {
if (blockIndex < blockLen && mBlocks[blockIndex])
hasBlocksInRange = PR_TRUE;
}
if (!hasBlocksInRange) return PR_FALSE;
Block *block;
PRUint32 i, start, end;
// first block, check bits
if ((block = mBlocks[startBlock])) {
start = aStart;
end = PR_MIN(aEnd, ((startBlock+1) << BLOCK_INDEX_SHIFT) - 1);
for (i = start; i <= end; i++) {
if ((block->mBits[(i>>3) & (BLOCK_SIZE - 1)]) & (1 << (i & 0x7)))
return PR_TRUE;
}
}
if (endBlock == startBlock) return PR_FALSE;
// [2..n-1] blocks check bytes
for (blockIndex = startBlock + 1; blockIndex < endBlock; blockIndex++) {
PRUint32 index;
if (blockIndex >= blockLen || !(block = mBlocks[blockIndex])) continue;
for (index = 0; index < BLOCK_SIZE; index++) {
if (block->mBits[index])
return PR_TRUE;
}
}
// last block, check bits
if (endBlock < blockLen && (block = mBlocks[endBlock])) {
start = endBlock << BLOCK_INDEX_SHIFT;
end = aEnd;
for (i = start; i <= end; i++) {
if ((block->mBits[(i>>3) & (BLOCK_SIZE - 1)]) & (1 << (i & 0x7)))
return PR_TRUE;
}
}
return PR_FALSE;
}
void set(PRUint32 aIndex) {
PRUint32 blockIndex = aIndex/BLOCK_SIZE_BITS;
if (blockIndex >= mBlocks.Length()) {
@ -92,7 +151,7 @@ public:
return;
mBlocks[blockIndex] = block;
}
block->mBits[(aIndex/8) & (BLOCK_SIZE - 1)] |= 1 << (aIndex & 0x7);
block->mBits[(aIndex>>3) & (BLOCK_SIZE - 1)] |= 1 << (aIndex & 0x7);
}
void SetRange(PRUint32 aStart, PRUint32 aEnd) {
@ -130,7 +189,64 @@ public:
const PRUint32 end = PR_MIN(aEnd - blockFirstBit, BLOCK_SIZE_BITS - 1);
for (PRUint32 bit = start; bit <= end; ++bit) {
block->mBits[bit/8] |= 1 << (bit & 0x7);
block->mBits[bit>>3] |= 1 << (bit & 0x7);
}
}
}
void clear(PRUint32 aIndex) {
PRUint32 blockIndex = aIndex/BLOCK_SIZE_BITS;
if (blockIndex >= mBlocks.Length()) {
nsAutoPtr<Block> *blocks = mBlocks.AppendElements(blockIndex + 1 - mBlocks.Length());
if (NS_UNLIKELY(!blocks)) // OOM
return;
}
Block *block = mBlocks[blockIndex];
if (!block) {
block = new Block;
if (NS_UNLIKELY(!block)) // OOM
return;
mBlocks[blockIndex] = block;
}
block->mBits[(aIndex>>3) & (BLOCK_SIZE - 1)] &= ~(1 << (aIndex & 0x7));
}
void ClearRange(PRUint32 aStart, PRUint32 aEnd) {
const PRUint32 startIndex = aStart/BLOCK_SIZE_BITS;
const PRUint32 endIndex = aEnd/BLOCK_SIZE_BITS;
if (endIndex >= mBlocks.Length()) {
PRUint32 numNewBlocks = endIndex + 1 - mBlocks.Length();
nsAutoPtr<Block> *blocks = mBlocks.AppendElements(numNewBlocks);
if (NS_UNLIKELY(!blocks)) // OOM
return;
}
for (PRUint32 i = startIndex; i <= endIndex; ++i) {
const PRUint32 blockFirstBit = i * BLOCK_SIZE_BITS;
const PRUint32 blockLastBit = blockFirstBit + BLOCK_SIZE_BITS - 1;
Block *block = mBlocks[i];
if (!block) {
PRBool fullBlock = PR_FALSE;
if (aStart <= blockFirstBit && aEnd >= blockLastBit)
fullBlock = PR_TRUE;
block = new Block(fullBlock ? 0xFF : 0);
if (NS_UNLIKELY(!block)) // OOM
return;
mBlocks[i] = block;
if (fullBlock)
continue;
}
const PRUint32 start = aStart > blockFirstBit ? aStart - blockFirstBit : 0;
const PRUint32 end = PR_MIN(aEnd - blockFirstBit, BLOCK_SIZE_BITS - 1);
for (PRUint32 bit = start; bit <= end; ++bit) {
block->mBits[bit>>3] &= ~(1 << (bit & 0x7));
}
}
}

View File

@ -140,6 +140,30 @@ ATSUFontID MacOSFontEntry::GetFontID()
return mATSUFontID;
}
// ATSUI requires AAT-enabled fonts to render complex scripts correctly.
// For now, simple clear out the cmap codepoints for fonts that have
// codepoints for complex scripts. (Bug 361986)
enum eComplexScript {
eComplexScriptArabic,
eComplexScriptIndic,
eComplexScriptTibetan
};
struct ScriptRange {
eComplexScript script;
PRUint32 rangeStart;
PRUint32 rangeEnd;
};
const ScriptRange gScriptsThatRequireShaping[] = {
{ eComplexScriptArabic, 0x0600, 0x077F }, // Basic Arabic and Arabic Supplement
{ eComplexScriptIndic, 0x0900, 0x0D7F }, // Indic scripts - Devanagari, Bengali, ..., Malayalam
{ eComplexScriptTibetan, 0x0F00, 0x0FFF } // Tibetan
// Thai seems to be "renderable" without AAT morphing tables
// xxx - Lao, Khmer?
};
nsresult
MacOSFontEntry::ReadCMAP()
{
@ -167,7 +191,49 @@ MacOSFontEntry::ReadCMAP()
nsresult rv = NS_ERROR_FAILURE;
PRPackedBool unicodeFont, symbolFont; // currently ignored
rv = gfxFontUtils::ReadCMAP(cmap, size, mCharacterMap, mUnicodeRanges, unicodeFont, symbolFont);
// for complex scripts, check for the presence of mort/morx
PRBool checkedForMorphTable = PR_FALSE, hasMorphTable = PR_FALSE;
PRUint32 s, numScripts = sizeof(gScriptsThatRequireShaping) / sizeof(ScriptRange);
for (s = 0; s < numScripts; s++) {
eComplexScript whichScript = gScriptsThatRequireShaping[s].script;
// check to see if the cmap includes complex script codepoints
if (mCharacterMap.TestRange(gScriptsThatRequireShaping[s].rangeStart, gScriptsThatRequireShaping[s].rangeEnd)) {
// check for mort/morx table, if haven't already
if (!checkedForMorphTable) {
status = ATSFontGetTable(fontID, 'morx', 0, 0, 0, &size);
if ( status == noErr ) {
checkedForMorphTable = PR_TRUE;
hasMorphTable = PR_TRUE;
} else {
// check for a mort table
status = ATSFontGetTable(fontID, 'mort', 0, 0, 0, &size);
checkedForMorphTable = PR_TRUE;
if ( status == noErr ) {
hasMorphTable = PR_TRUE;
}
}
}
// rude hack - the Chinese STxxx fonts on 10.4 contain morx tables and Arabic glyphs but
// lack the proper info for shaping Arabic, so exclude explicitly, ick
if (whichScript == eComplexScriptArabic && hasMorphTable) {
if (mPostscriptName.CharAt(0) == 'S' && mPostscriptName.CharAt(1) == 'T') {
mCharacterMap.ClearRange(gScriptsThatRequireShaping[s].rangeStart, gScriptsThatRequireShaping[s].rangeEnd);
}
}
// general exclusion - if no morph table, exclude codepoints
if (!hasMorphTable) {
mCharacterMap.ClearRange(gScriptsThatRequireShaping[s].rangeStart, gScriptsThatRequireShaping[s].rangeEnd);
}
}
}
return rv;
}