mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 19:04:45 +00:00
Bug 496573. Fix font load failure for fonts with family/fullname mismatch. r=vlad
This commit is contained in:
parent
4264822f8e
commit
bef34be58f
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)) {
|
||||
|
35
layout/reftests/font-face/load-badfullname-ref.html
Normal file
35
layout/reftests/font-face/load-badfullname-ref.html
Normal 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>
|
40
layout/reftests/font-face/load-badfullname.html
Normal file
40
layout/reftests/font-face/load-badfullname.html
Normal 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>
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user