Bug 496573. Fix font load failure for fonts with family/fullname mismatch. r=vlad

This commit is contained in:
John Daggett 2009-07-22 22:08:23 -10:00
parent 4264822f8e
commit bef34be58f
6 changed files with 219 additions and 71 deletions

View File

@ -292,6 +292,14 @@ public:
#define TRUETYPE_TAG(a, b, c, d) ((a) << 24 | (b) << 16 | (c) << 8 | (d))
// used for overlaying name changes without touching original font data
struct FontDataOverlay {
// overlaySrc != 0 ==> use overlay
PRUint32 overlaySrc; // src offset from start of font data
PRUint32 overlaySrcLen; // src length
PRUint32 overlayDest; // dest offset from start of font data
};
class THEBES_API gfxFontUtils {
public:
@ -332,6 +340,7 @@ public:
PRPackedBool& aUnicodeFont, PRPackedBool& aSymbolFont);
#ifdef XP_WIN
// given a TrueType/OpenType data file, produce a EOT-format header
// for use with Windows T2Embed API AddFontResource type API's
// effectively hide existing fonts with matching names aHeaderLen is
@ -339,7 +348,7 @@ public:
// EOT header on output
static nsresult
MakeEOTHeader(const PRUint8 *aFontData, PRUint32 aFontDataLength,
nsTArray<PRUint8> *aHeader);
nsTArray<PRUint8> *aHeader, FontDataOverlay *aOverlay);
#endif
// checks for valid SFNT table structure, returns true if valid

View File

@ -1372,13 +1372,15 @@ DumpEOTHeader(PRUint8 *aHeader, PRUint32 aHeaderLen)
nsresult
gfxFontUtils::MakeEOTHeader(const PRUint8 *aFontData, PRUint32 aFontDataLength,
nsTArray<PRUint8> *aHeader)
nsTArray<PRUint8> *aHeader, FontDataOverlay *aOverlay)
{
NS_ASSERTION(aFontData && aFontDataLength != 0, "null font data");
NS_ASSERTION(aHeader, "null header");
NS_ASSERTION(aHeader->Length() == 0, "non-empty header passed in");
NS_ASSERTION(aOverlay, "null font overlay struct passed in");
aOverlay->overlaySrc = 0;
if (!aHeader->AppendElements(sizeof(EOTFixedHeader)))
return NS_ERROR_OUT_OF_MEMORY;
@ -1481,6 +1483,7 @@ gfxFontUtils::MakeEOTHeader(const PRUint8 *aFontData, PRUint32 aFontDataLength,
// -- first, read name table header
const NameHeader *nameHeader = reinterpret_cast<const NameHeader*>(aFontData + nameOffset);
PRUint32 nameStringsBase = PRUint32(nameHeader->stringOffset);
PRUint32 nameCount = nameHeader->count;
@ -1542,7 +1545,7 @@ gfxFontUtils::MakeEOTHeader(const PRUint8 *aFontData, PRUint32 aFontDataLength,
if (needNames != 0)
{
return NS_ERROR_FAILURE;
}
}
// -- expand buffer if needed to include variable-length portion
PRUint32 eotVariableLength = 0;
@ -1566,10 +1569,12 @@ gfxFontUtils::MakeEOTHeader(const PRUint8 *aFontData, PRUint32 aFontDataLength,
PRUint32 nameoff = names[i].offset; // offset from base of string storage
// sanity check the name string location
if (PRUint64(nameOffset) + PRUint64(PRUint32(nameHeader->stringOffset)) + PRUint64(nameoff) + PRUint64(namelen) > dataLength)
if (PRUint64(nameOffset) + PRUint64(nameStringsBase) + PRUint64(nameoff)
+ PRUint64(namelen) > dataLength) {
return NS_ERROR_FAILURE;
}
strOffset = nameOffset + PRUint32(nameHeader->stringOffset) + nameoff + namelen;
strOffset = nameOffset + nameStringsBase + nameoff + namelen;
// output 2-byte str size
strLen = namelen & (~1); // UTF-16 string len must be even
@ -1599,6 +1604,23 @@ gfxFontUtils::MakeEOTHeader(const PRUint8 *aFontData, PRUint32 aFontDataLength,
NS_ASSERTION(eotEnd == aHeader->Elements() + aHeader->Length(),
"header length calculation incorrect");
// bug 496573 -- fonts with a fullname that does not begin with the
// family name cause the EOT font loading API to hiccup
PRUint32 famOff = names[EOTFixedHeader::EOT_FAMILY_NAME_INDEX].offset;
PRUint32 famLen = names[EOTFixedHeader::EOT_FAMILY_NAME_INDEX].length;
PRUint32 fullOff = names[EOTFixedHeader::EOT_FULL_NAME_INDEX].offset;
PRUint32 fullLen = names[EOTFixedHeader::EOT_FULL_NAME_INDEX].length;
const PRUint8 *nameStrings = aFontData + nameOffset + nameStringsBase;
// assure that the start of the fullname matches the family name
if (famLen <= fullLen
&& memcmp(nameStrings + famOff, nameStrings + fullOff, famLen)) {
aOverlay->overlaySrc = nameOffset + nameStringsBase + famOff;
aOverlay->overlaySrcLen = famLen;
aOverlay->overlayDest = nameOffset + nameStringsBase + fullOff;
}
// -- OS/2 table data
const OS2Table *os2Data = reinterpret_cast<const OS2Table*>(aFontData + os2Offset);

View File

@ -411,13 +411,13 @@ void FontEntry::InitializeFontEmbeddingProcs()
class WinUserFontData : public gfxUserFontData {
public:
WinUserFontData(HANDLE aFontRef, PRBool aIsCFF)
: mFontRef(aFontRef), mIsCFF(aIsCFF)
WinUserFontData(HANDLE aFontRef, PRBool aIsEmbeddedFont)
: mFontRef(aFontRef), mIsEmbeddedFont(aIsEmbeddedFont)
{ }
virtual ~WinUserFontData()
{
if (mIsCFF) {
if (!mIsEmbeddedFont) {
RemoveFontMemResourceEx(mFontRef);
} else {
ULONG pulStatus;
@ -426,7 +426,7 @@ public:
}
HANDLE mFontRef;
PRPackedBool mIsCFF;
PRPackedBool mIsEmbeddedFont;
};
// used to control stream read by Windows TTLoadEmbeddedFont API
@ -434,12 +434,36 @@ public:
class EOTFontStreamReader {
public:
EOTFontStreamReader(const PRUint8 *aFontData, PRUint32 aLength, PRUint8 *aEOTHeader,
PRUint32 aEOTHeaderLen)
: mInHeader(PR_TRUE), mHeaderOffset(0), mEOTHeader(aEOTHeader),
mEOTHeaderLen(aEOTHeaderLen), mFontData(aFontData), mFontDataLen(aLength),
mFontDataOffset(0)
PRUint32 aEOTHeaderLen, FontDataOverlay *aNameOverlay)
: mCurrentChunk(0), mChunkOffset(0)
{
NS_ASSERTION(aFontData, "null font data ptr passed in");
NS_ASSERTION(aEOTHeader, "null EOT header ptr passed in");
NS_ASSERTION(aNameOverlay, "null name overlay struct passed in");
if (aNameOverlay->overlaySrc) {
mNumChunks = 4;
// 0 : EOT header
mDataChunks[0].mData = aEOTHeader;
mDataChunks[0].mLength = aEOTHeaderLen;
// 1 : start of font data to overlayDest
mDataChunks[1].mData = aFontData;
mDataChunks[1].mLength = aNameOverlay->overlayDest;
// 2 : overlay data
mDataChunks[2].mData = aFontData + aNameOverlay->overlaySrc;
mDataChunks[2].mLength = aNameOverlay->overlaySrcLen;
// 3 : rest of font data
mDataChunks[3].mData = aFontData + aNameOverlay->overlayDest + aNameOverlay->overlaySrcLen;
mDataChunks[3].mLength = aLength - aNameOverlay->overlayDest - aNameOverlay->overlaySrcLen;
} else {
mNumChunks = 2;
// 0 : EOT header
mDataChunks[0].mData = aEOTHeader;
mDataChunks[0].mLength = aEOTHeaderLen;
// 1 : font data
mDataChunks[1].mData = aFontData;
mDataChunks[1].mLength = aLength;
}
}
~EOTFontStreamReader()
@ -447,37 +471,36 @@ public:
}
PRPackedBool mInHeader;
PRUint32 mHeaderOffset;
PRUint8 *mEOTHeader;
PRUint32 mEOTHeaderLen;
const PRUint8 *mFontData;
PRUint32 mFontDataLen;
PRUint32 mFontDataOffset;
struct FontDataChunk {
const PRUint8 *mData;
PRUint32 mLength;
};
PRUint32 mNumChunks;
FontDataChunk mDataChunks[4];
PRUint32 mCurrentChunk;
PRUint32 mChunkOffset;
unsigned long Read(void *outBuffer, const unsigned long aBytesToRead)
{
PRUint32 bytesLeft = aBytesToRead;
PRUint32 bytesLeft = aBytesToRead; // bytes left in the out buffer
PRUint8 *out = static_cast<PRUint8*> (outBuffer);
// read from EOT header
if (mInHeader) {
PRUint32 toCopy = PR_MIN(aBytesToRead, mEOTHeaderLen - mHeaderOffset);
memcpy(out, mEOTHeader + mHeaderOffset, toCopy);
bytesLeft -= toCopy;
mHeaderOffset += toCopy;
out += toCopy;
if (mHeaderOffset == mEOTHeaderLen)
mInHeader = PR_FALSE;
}
while (mCurrentChunk < mNumChunks && bytesLeft) {
FontDataChunk& currentChunk = mDataChunks[mCurrentChunk];
PRUint32 bytesToCopy = PR_MIN(bytesLeft,
currentChunk.mLength - mChunkOffset);
memcpy(out, currentChunk.mData + mChunkOffset, bytesToCopy);
bytesLeft -= bytesToCopy;
mChunkOffset += bytesToCopy;
out += bytesToCopy;
if (bytesLeft) {
PRInt32 bytesRead = PR_MIN(bytesLeft, mFontDataLen - mFontDataOffset);
memcpy(out, mFontData, bytesRead);
mFontData += bytesRead;
mFontDataOffset += bytesRead;
if (bytesRead > 0)
bytesLeft -= bytesRead;
NS_ASSERTION(mChunkOffset <= currentChunk.mLength, "oops, buffer overrun");
if (mChunkOffset == currentChunk.mLength) {
mCurrentChunk++;
mChunkOffset = 0;
}
}
return aBytesToRead - bytesLeft;
@ -507,17 +530,55 @@ FontEntry::LoadFont(const gfxProxyFontEntry &aProxyEntry,
return nsnull;
nsresult rv;
HANDLE fontRef;
HANDLE fontRef = nsnull;
PRBool isEmbedded = PR_FALSE;
nsAutoString uniqueName;
rv = gfxFontUtils::MakeUniqueUserFontName(uniqueName);
if (NS_FAILED(rv))
return nsnull;
if (isCFF) {
// for TTF fonts, first try using the t2embed library
if (!isCFF) {
// TrueType-style glyphs, use EOT library
nsAutoTArray<PRUint8,2048> eotHeader;
PRUint8 *buffer;
PRUint32 eotlen;
isEmbedded = PR_TRUE;
PRUint32 nameLen = PR_MIN(uniqueName.Length(), LF_FACESIZE - 1);
nsPromiseFlatString fontName(Substring(uniqueName, 0, nameLen));
FontDataOverlay overlayNameData = {0, 0, 0};
rv = gfxFontUtils::MakeEOTHeader(aFontData, aLength, &eotHeader,
&overlayNameData);
if (NS_FAILED(rv))
return nsnull;
// load in embedded font data
eotlen = eotHeader.Length();
buffer = reinterpret_cast<PRUint8*> (eotHeader.Elements());
PRInt32 ret;
ULONG privStatus, pulStatus;
EOTFontStreamReader eotReader(aFontData, aLength, buffer, eotlen,
&overlayNameData);
ret = TTLoadEmbeddedFontPtr(&fontRef, TTLOAD_PRIVATE, &privStatus,
LICENSE_PREVIEWPRINT, &pulStatus,
EOTFontStreamReader::ReadEOTStream,
&eotReader, (PRUnichar*)(fontName.get()), 0, 0);
if (ret != E_NONE)
fontRef = nsnull;
}
// load CFF fonts or fonts that failed with t2embed loader
if (fontRef == nsnull) {
// Postscript-style glyphs, swizzle name table, load directly
nsTArray<PRUint8> newFontData;
isEmbedded = PR_FALSE;
rv = gfxFontUtils::RenameFont(uniqueName, aFontData, aLength, &newFontData);
if (NS_FAILED(rv))
@ -542,37 +603,10 @@ FontEntry::LoadFont(const gfxProxyFontEntry &aProxyEntry,
RemoveFontMemResourceEx(fontRef);
return nsnull;
}
} else {
// TrueType-style glyphs, use EOT library
nsAutoTArray<PRUint8,2048> eotHeader;
PRUint8 *buffer;
PRUint32 eotlen;
PRUint32 nameLen = PR_MIN(uniqueName.Length(), LF_FACESIZE - 1);
nsPromiseFlatString fontName(Substring(uniqueName, 0, nameLen));
rv = gfxFontUtils::MakeEOTHeader(aFontData, aLength, &eotHeader);
if (NS_FAILED(rv))
return nsnull;
// load in embedded font data
eotlen = eotHeader.Length();
buffer = reinterpret_cast<PRUint8*> (eotHeader.Elements());
PRInt32 ret;
ULONG privStatus, pulStatus;
EOTFontStreamReader eotReader(aFontData, aLength, buffer, eotlen);
ret = TTLoadEmbeddedFontPtr(&fontRef, TTLOAD_PRIVATE, &privStatus,
LICENSE_PREVIEWPRINT, &pulStatus,
EOTFontStreamReader::ReadEOTStream,
&eotReader, (PRUnichar*)(fontName.get()), 0, 0);
if (ret != E_NONE)
return nsnull;
}
// make a new font entry using the unique name
WinUserFontData *winUserFontData = new WinUserFontData(fontRef, isCFF);
WinUserFontData *winUserFontData = new WinUserFontData(fontRef, isEmbedded);
PRUint16 w = (aProxyEntry.mWeight == 0 ? 400 : aProxyEntry.mWeight);
FontEntry *fe = FontEntry::CreateFontEntry(uniqueName,
@ -583,6 +617,7 @@ FontEntry::LoadFont(const gfxProxyFontEntry &aProxyEntry,
if (!fe)
return fe;
// Uniscribe doesn't place CFF fonts loaded privately via AddFontMemResourceEx
if (isCFF)
fe->mForceGDI = PR_TRUE;
@ -2610,7 +2645,10 @@ gfxWindowsFontGroup::InitTextRunUniscribe(gfxContext *aContext, gfxTextRun *aRun
if (SUCCEEDED(rv)) {
rv = item->Place();
NS_ASSERTION(SUCCEEDED(rv), "Failed to place -- this is pretty bad.");
if (FAILED(rv)) {
// crap fonts may fail when placing (e.g. funky free fonts)
NS_WARNING("Failed to place with font -- this is pretty bad.");
}
}
if (FAILED(rv)) {

View File

@ -0,0 +1,35 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test of fonts with funky fullnames</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
@font-face {
font-family: test;
src: url(../fonts/markA.ttf);
}
body {
margin: 50px;
font-size: 12pt;
font-family: Gill Sans, Futura, sans-serif;
}
p.test1 { font-size: 48pt; font-family: test, Futura, sans-serif; }
p.test2 { font-size: 48pt; font-family: test, Futura, sans-serif; }
</style>
</head>
<body>
<p>Letter A should <strong>not</strong> appear below:</p>
<p class="test1">A</p>
<p class="test2">A</p>
</body>
</html>

View File

@ -0,0 +1,40 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test of fonts with funky fullnames</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
@font-face {
font-family: test1;
src: url(../fonts/markA-badfullname.ttf);
}
@font-face {
font-family: test2;
src: url(../fonts/markA-shortfullname.ttf);
}
body {
margin: 50px;
font-size: 12pt;
font-family: Gill Sans, Futura, sans-serif;
}
p.test1 { font-size: 48pt; font-family: test1, Futura, sans-serif; }
p.test2 { font-size: 48pt; font-family: test2, Futura, sans-serif; }
</style>
</head>
<body>
<p>Letter A should <strong>not</strong> appear below:</p>
<p class="test1">A</p>
<p class="test2">A</p>
</body>
</html>

View File

@ -91,3 +91,7 @@ HTTP(..) load 486974-1.html
# compare fonts with and without bad head checksum
HTTP(..) == load-badchecksum.html load-badchecksum-ref.html
# t2embed lib on windows is picky about fullname
HTTP(..) == load-badfullname.html load-badfullname-ref.html