From 69df3bc60b8e1fa16b1e73c295d75417530c0c7b Mon Sep 17 00:00:00 2001 From: Ed Morley Date: Fri, 23 Sep 2011 10:36:16 +0100 Subject: [PATCH] Backout changeset 864c08bd7c63 (bug 684889) for Android bustage --- dom/ipc/PContent.ipdl | 8 +- gfx/thebes/gfxAndroidPlatform.cpp | 552 +++++++++++++++++++++++++++++- gfx/thebes/gfxAndroidPlatform.h | 76 ++-- gfx/thebes/gfxFT2FontList.cpp | 525 +++------------------------- gfx/thebes/gfxFT2FontList.h | 28 +- gfx/thebes/gfxFT2Fonts.cpp | 184 +++++----- gfx/thebes/gfxFT2Fonts.h | 40 +-- gfx/thebes/gfxFont.cpp | 10 +- 8 files changed, 750 insertions(+), 673 deletions(-) diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index 71b7879d29c..22b2243750d 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -83,13 +83,9 @@ union StorageConstructData }; struct FontListEntry { - nsString familyName; - nsString faceName; + nsCString familyName; nsCString filepath; - PRUint16 weight; - PRInt16 stretch; - PRUint8 italic; - PRUint8 index; + PRUint32 index; }; rpc protocol PContent diff --git a/gfx/thebes/gfxAndroidPlatform.cpp b/gfx/thebes/gfxAndroidPlatform.cpp index a73785bff85..67d46fa9a13 100644 --- a/gfx/thebes/gfxAndroidPlatform.cpp +++ b/gfx/thebes/gfxAndroidPlatform.cpp @@ -35,16 +35,39 @@ * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ +#include "mozilla/dom/ContentChild.h" +#include "nsXULAppAPI.h" + +#include + +#include +#include +#include #include "gfxAndroidPlatform.h" -#include "gfxFT2FontList.h" +#include "cairo.h" +#include "cairo-ft.h" + #include "gfxImageSurface.h" -#include "cairo.h" +#include "nsUnicharUtils.h" + +#include "nsMathUtils.h" +#include "nsTArray.h" + +#include "qcms.h" #include "ft2build.h" #include FT_FREETYPE_H +#include "gfxFT2Fonts.h" +#include "gfxPlatformFontList.h" +#include "gfxFT2FontList.h" +#include "mozilla/scache/StartupCache.h" +#include "nsXPCOMStrings.h" + +using namespace mozilla; +using namespace dom; static FT_Library gPlatformFTLibrary = NULL; @@ -53,6 +76,13 @@ static FT_Library gPlatformFTLibrary = NULL; gfxAndroidPlatform::gfxAndroidPlatform() { FT_Init_FreeType(&gPlatformFTLibrary); + + mFonts.Init(200); + mFontAliases.Init(20); + mFontSubstitutes.Init(50); + mPrefFonts.Init(10); + + UpdateFontList(); } gfxAndroidPlatform::~gfxAndroidPlatform() @@ -76,27 +106,420 @@ gfxAndroidPlatform::CreateOffscreenSurface(const gfxIntSize& size, return newSurface.forget(); } +struct FontListData { + FontListData(nsIAtom *aLangGroup, const nsACString& aGenericFamily, nsTArray& aListOfFonts) : + mLangGroup(aLangGroup), mGenericFamily(aGenericFamily), mStringArray(aListOfFonts) {} + nsIAtom *mLangGroup; + const nsACString& mGenericFamily; + nsTArray& mStringArray; +}; + +static PLDHashOperator +FontListHashEnumFunc(nsStringHashKey::KeyType aKey, + nsRefPtr& aFontFamily, + void* userArg) +{ + FontListData *data = (FontListData*)userArg; + + // use the first variation for now. This data should be the same + // for all the variations and should probably be moved up to + // the Family + gfxFontStyle style; + style.language = data->mLangGroup; + nsRefPtr aFontEntry = aFontFamily->FindFontEntry(style); + NS_ASSERTION(aFontEntry, "couldn't find any font entry in family"); + if (!aFontEntry) + return PL_DHASH_NEXT; + + + data->mStringArray.AppendElement(aFontFamily->Name()); + + return PL_DHASH_NEXT; +} + nsresult gfxAndroidPlatform::GetFontList(nsIAtom *aLangGroup, const nsACString& aGenericFamily, nsTArray& aListOfFonts) { - gfxPlatformFontList::PlatformFontList()->GetFontList(aLangGroup, - aGenericFamily, - aListOfFonts); + FontListData data(aLangGroup, aGenericFamily, aListOfFonts); + + mFonts.Enumerate(FontListHashEnumFunc, &data); + + aListOfFonts.Sort(); + aListOfFonts.Compact(); + return NS_OK; } +class FontNameCache { +public: + typedef nsAutoTArray IndexList; + PLDHashTableOps ops; + FontNameCache() : mWriteNeeded(PR_FALSE) { + ops = (PLDHashTableOps) { + PL_DHashAllocTable, + PL_DHashFreeTable, + StringHash, + HashMatchEntry, + MoveEntry, + PL_DHashClearEntryStub, + PL_DHashFinalizeStub, + NULL}; + if (!PL_DHashTableInit(&mMap, &ops, nsnull, + sizeof(FNCMapEntry), 0)) { + mMap.ops = nsnull; + LOG("initializing the map failed"); + } + NS_ABORT_IF_FALSE(XRE_GetProcessType() == GeckoProcessType_Default, + "StartupCacheFontNameCache should only be used in chrome procsess"); + mCache = mozilla::scache::StartupCache::GetSingleton(); + Init(); + } + + void Init() + { + if (!mMap.ops || !mCache) + return; + nsCAutoString prefName("font.cache"); + PRUint32 size; + char* buf; + if (NS_FAILED(mCache->GetBuffer(prefName.get(), &buf, &size))) + return; + + LOG("got: %s from the cache", nsDependentCString(buf, size).get()); + char* entry = strtok(buf, ";"); + while (entry) { + nsCString faceList, filename, indexes; + PRUint32 timestamp, fileSize; + filename.Assign(entry); + entry = strtok(NULL, ";"); + if (!entry) + break; + faceList.Assign(entry); + entry = strtok(NULL, ";"); + if (!entry) + break; + char* endptr; + timestamp = strtoul(entry, &endptr, 10); + if (*endptr != '\0') + break; + entry = strtok(NULL, ";"); + if (!entry) + break; + fileSize = strtoul(entry, &endptr, 10); + if (*endptr != '\0') + break; + entry = strtok(NULL, ";"); + if (!entry) + break; + indexes.Assign(entry); + FNCMapEntry* mapEntry = + static_cast + (PL_DHashTableOperate(&mMap, filename.get(), PL_DHASH_ADD)); + if (mapEntry) { + mapEntry->mFilename = filename; + mapEntry->mTimestamp = timestamp; + mapEntry->mFilesize = fileSize; + mapEntry->mFaces.AssignWithConversion(faceList); + mapEntry->mIndexes = indexes; + } + entry = strtok(NULL, ";"); + } + free(buf); + } + + virtual void + GetInfoForFile(nsCString& aFileName, nsAString& aFaceList, + PRUint32 *aTimestamp, PRUint32 *aFileSize, + IndexList &aIndexList) + { + if (!mMap.ops) + return; + PLDHashEntryHdr *hdr = PL_DHashTableOperate(&mMap, aFileName.get(), PL_DHASH_LOOKUP); + if (!hdr) + return; + FNCMapEntry* entry = static_cast(hdr); + if (entry && entry->mTimestamp && entry->mFilesize) { + *aTimestamp = entry->mTimestamp; + *aFileSize = entry->mFilesize; + char* indexes = const_cast(entry->mIndexes.get()); + char* endptr = indexes + 1; + unsigned long index = strtoul(indexes, &endptr, 10); + while (indexes < endptr && indexes[0] != '\0') { + aIndexList.AppendElement(index); + indexes = endptr + 1; + } + aFaceList.Assign(entry->mFaces); + } + } + + virtual void + CacheFileInfo(nsCString& aFileName, nsAString& aFaceList, + PRUint32 aTimestamp, PRUint32 aFileSize, + IndexList &aIndexList) + { + if (!mMap.ops) + return; + FNCMapEntry* entry = + static_cast + (PL_DHashTableOperate(&mMap, aFileName.get(), PL_DHASH_ADD)); + if (entry) { + entry->mFilename = aFileName; + entry->mTimestamp = aTimestamp; + entry->mFilesize = aFileSize; + entry->mFaces.Assign(aFaceList); + for (PRUint32 i = 0; i < aIndexList.Length(); i++) { + entry->mIndexes.AppendInt(aIndexList[i]); + entry->mIndexes.Append(","); + } + } + mWriteNeeded = PR_TRUE; + } + ~FontNameCache() { + if (!mMap.ops) + return; + + if (!mWriteNeeded || !mCache) { + PL_DHashTableFinish(&mMap); + return; + } + + nsCAutoString buf; + PL_DHashTableEnumerate(&mMap, WriteOutMap, &buf); + PL_DHashTableFinish(&mMap); + nsCAutoString prefName("font.cache"); + mCache->PutBuffer(prefName.get(), buf.get(), buf.Length() + 1); + } +private: + mozilla::scache::StartupCache* mCache; + PLDHashTable mMap; + PRBool mWriteNeeded; + + static PLDHashOperator WriteOutMap(PLDHashTable *aTable, + PLDHashEntryHdr *aHdr, + PRUint32 aNumber, void *aData) + { + nsCAutoString* buf = (nsCAutoString*)aData; + FNCMapEntry* entry = static_cast(aHdr); + + buf->Append(entry->mFilename); + buf->Append(";"); + buf->AppendWithConversion(entry->mFaces); + buf->Append(";"); + buf->AppendInt(entry->mTimestamp); + buf->Append(";"); + buf->AppendInt(entry->mFilesize); + buf->Append(";"); + buf->Append(entry->mIndexes); + buf->Append(";"); + + return PL_DHASH_NEXT; + } + + typedef struct : public PLDHashEntryHdr { + public: + nsCString mFilename; + PRUint32 mTimestamp; + PRUint32 mFilesize; + nsString mFaces; + nsCString mIndexes; + } FNCMapEntry; + + + static PLDHashNumber StringHash(PLDHashTable *table, const void *key) { + PLDHashNumber h = 0; + for (const char *s = reinterpret_cast(key); *s; ++s) + h = PR_ROTATE_LEFT32(h, 4) ^ NS_ToLower(*s); + return h; + } + + static PRBool HashMatchEntry(PLDHashTable *table, + const PLDHashEntryHdr *aHdr, const void *key) + { + const FNCMapEntry* entry = + static_cast(aHdr); + return entry->mFilename.Equals((char*)key); + } + + static void MoveEntry(PLDHashTable *table, const PLDHashEntryHdr *aFrom, + PLDHashEntryHdr *aTo) + { + FNCMapEntry* to = + static_cast(aTo); + const FNCMapEntry* from = + static_cast(aFrom); + to->mFilename.Assign(from->mFilename); + to->mTimestamp = from->mTimestamp; + to->mFilesize = from->mFilesize; + to->mFaces.Assign(from->mFaces); + to->mIndexes.Assign(from->mIndexes); + } + +}; + +void +gfxAndroidPlatform::AppendFacesFromFontFile(const char *aFileName, FontNameCache* aFontCache, InfallibleTArray* aFontList) +{ + nsString faceList; + PRUint32 timestamp = 0; + PRUint32 filesize = 0; + FontNameCache::IndexList indexList; + nsCString fileName(aFileName); + if (aFontCache) + aFontCache->GetInfoForFile(fileName, faceList, ×tamp, &filesize, indexList); + struct stat s; + int stat_ret = stat(aFileName, &s); + if (!faceList.IsEmpty() && indexList.Length() && 0 == stat_ret && + s.st_mtime == timestamp && s.st_size == filesize) { + PRInt32 beginning = 0; + PRInt32 end = faceList.Find(",", PR_TRUE, beginning, -1); + for (PRUint32 i = 0; i < indexList.Length() && end != kNotFound; i++) { + nsDependentSubstring name(faceList, beginning, end); + ToLowerCase(name); + FontListEntry fle(NS_ConvertUTF16toUTF8(name), fileName, + indexList[i]); + aFontList->AppendElement(fle); + beginning = end + 1; + end = faceList.Find(",", PR_TRUE, beginning, -1); + } + return; + } + + faceList.AssignLiteral(""); + timestamp = s.st_mtime; + filesize = s.st_size; + FT_Face dummy; + if (FT_Err_Ok == FT_New_Face(GetFTLibrary(), aFileName, -1, &dummy)) { + for (FT_Long i = 0; i < dummy->num_faces; i++) { + FT_Face face; + if (FT_Err_Ok != FT_New_Face(GetFTLibrary(), aFileName, + i, &face)) + continue; + nsDependentCString name(face->family_name); + ToLowerCase(name); + + nsRefPtr ff; + faceList.AppendWithConversion(name); + faceList.AppendLiteral(","); + indexList.AppendElement(i); + ToLowerCase(name); + FontListEntry fle(name, fileName, i); + aFontList->AppendElement(fle); + } + FT_Done_Face(dummy); + if (aFontCache && 0 == stat_ret) + aFontCache->CacheFileInfo(fileName, faceList, timestamp, filesize, indexList); + } +} + +void +gfxAndroidPlatform::FindFontsInDirectory(const nsCString& aFontsDir, + FontNameCache* aFontCache) +{ + static const char* sStandardFonts[] = { + "DroidSans.ttf", + "DroidSans-Bold.ttf", + "DroidSerif-Regular.ttf", + "DroidSerif-Bold.ttf", + "DroidSerif-Italic.ttf", + "DroidSerif-BoldItalic.ttf", + "DroidSansMono.ttf", + "DroidSansArabic.ttf", + "DroidSansHebrew.ttf", + "DroidSansThai.ttf", + "MTLmr3m.ttf", + "MTLc3m.ttf", + "DroidSansJapanese.ttf", + "DroidSansFallback.ttf" + }; + + DIR *d = opendir(aFontsDir.get()); + struct dirent *ent = NULL; + while(d && (ent = readdir(d)) != NULL) { + int namelen = strlen(ent->d_name); + if (namelen > 4 && + strcasecmp(ent->d_name + namelen - 4, ".ttf") == 0) + { + bool isStdFont = false; + for (unsigned int i = 0; i < NS_ARRAY_LENGTH(sStandardFonts) && !isStdFont; i++) { + isStdFont = strcmp(sStandardFonts[i], ent->d_name) == 0; + } + if (!isStdFont) { + nsCString s(aFontsDir); + s.Append(nsDependentCString(ent->d_name)); + + AppendFacesFromFontFile(s.get(), aFontCache, &mFontList); + } + } + } + closedir(d); + for (unsigned int i = 0; i < NS_ARRAY_LENGTH(sStandardFonts); i++) { + nsCString s(aFontsDir); + s.Append(nsDependentCString(sStandardFonts[i])); + + AppendFacesFromFontFile(s.get(), aFontCache, &mFontList); + } +} + void gfxAndroidPlatform::GetFontList(InfallibleTArray* retValue) { - gfxFT2FontList::PlatformFontList()->GetFontList(retValue); + if (XRE_GetProcessType() != GeckoProcessType_Default) { + mozilla::dom::ContentChild::GetSingleton()->SendReadFontList(retValue); + return; + } + + if (mFontList.Length() > 0) { + *retValue = mFontList; + return; + } + + // ANDROID_ROOT is the root of the android system, typically /system + // font files are in /$ANDROID_ROOT/fonts/ + FontNameCache fnc; + FindFontsInDirectory(NS_LITERAL_CSTRING("/system/fonts/"), &fnc); + char *androidRoot = PR_GetEnv("ANDROID_ROOT"); + if (androidRoot && strcmp(androidRoot, "/system")) { + nsCString root(androidRoot); + root.Append("/fonts/"); + FindFontsInDirectory(root, &fnc); + } + + *retValue = mFontList; } nsresult gfxAndroidPlatform::UpdateFontList() { - gfxPlatformFontList::PlatformFontList()->UpdateFontList(); + gfxFontCache *fc = gfxFontCache::GetCache(); + if (fc) + fc->AgeAllGenerations(); + mFonts.Clear(); + mFontAliases.Clear(); + mFontSubstitutes.Clear(); + mPrefFonts.Clear(); + mCodepointsWithNoFonts.reset(); + + InfallibleTArray fontList; + GetFontList(&fontList); + for (PRUint32 i = 0; i < fontList.Length(); i++) { + NS_ConvertUTF8toUTF16 name(fontList[i].familyName()); + nsRefPtr ff; + if (!mFonts.Get(name, &ff)) { + ff = new FontFamily(name); + mFonts.Put(name, ff); + } + ff->AddFontFileAndIndex(fontList[i].filepath(), fontList[i].index()); + } + + // initialize the cmap loading process after font list has been initialized + //StartLoader(kDelayBeforeLoadingCmaps, kIntervalBetweenLoadingCmaps); + + // initialize ranges of characters for which system-wide font search should be skipped + mCodepointsWithNoFonts.SetRange(0,0x1f); // C0 controls + mCodepointsWithNoFonts.SetRange(0x7f,0x9f); // C1 controls + return NS_OK; } @@ -106,21 +529,47 @@ gfxAndroidPlatform::ResolveFontName(const nsAString& aFontName, void *aClosure, PRBool& aAborted) { + if (aFontName.IsEmpty()) + return NS_ERROR_FAILURE; + nsAutoString resolvedName; - if (!gfxPlatformFontList::PlatformFontList()-> - ResolveFontName(aFontName, resolvedName)) { - aAborted = PR_FALSE; - return NS_OK; + gfxPlatformFontList* platformFontList = gfxPlatformFontList::PlatformFontList(); + if (platformFontList) { + if (!platformFontList->ResolveFontName(aFontName, resolvedName)) { + aAborted = PR_FALSE; + return NS_OK; + } } - aAborted = !(*aCallback)(resolvedName, aClosure); + + nsAutoString keyName(aFontName); + ToLowerCase(keyName); + + nsRefPtr ff; + if (mFonts.Get(keyName, &ff) || + mFontSubstitutes.Get(keyName, &ff) || + mFontAliases.Get(keyName, &ff)) + { + aAborted = !(*aCallback)(ff->Name(), aClosure); + } else { + aAborted = PR_FALSE; + } + return NS_OK; } +static PRBool SimpleResolverCallback(const nsAString& aName, void* aClosure) +{ + nsString *result = static_cast(aClosure); + result->Assign(aName); + return PR_FALSE; +} + nsresult gfxAndroidPlatform::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName) { - gfxPlatformFontList::PlatformFontList()->GetStandardFamilyName(aFontName, aFamilyName); - return NS_OK; + aFamilyName.Truncate(); + PRBool aborted; + return ResolveFontName(aFontName, SimpleResolverCallback, &aFamilyName, aborted); } gfxPlatformFontList* @@ -162,7 +611,7 @@ gfxAndroidPlatform::CreateFontGroup(const nsAString &aFamilies, const gfxFontStyle *aStyle, gfxUserFontSet* aUserFontSet) { - return new gfxFontGroup(aFamilies, aStyle, aUserFontSet); + return new gfxFT2FontGroup(aFamilies, aStyle, aUserFontSet); } FT_Library @@ -171,6 +620,68 @@ gfxAndroidPlatform::GetFTLibrary() return gPlatformFTLibrary; } +FontFamily * +gfxAndroidPlatform::FindFontFamily(const nsAString& aName) +{ + nsAutoString name(aName); + ToLowerCase(name); + + nsRefPtr ff; + if (!mFonts.Get(name, &ff) && + !mFontSubstitutes.Get(name, &ff) && + !mFontAliases.Get(name, &ff)) { + return nsnull; + } + return ff.get(); +} + +FontEntry * +gfxAndroidPlatform::FindFontEntry(const nsAString& aName, const gfxFontStyle& aFontStyle) +{ + nsRefPtr ff = FindFontFamily(aName); + if (!ff) + return nsnull; + + return ff->FindFontEntry(aFontStyle); +} + +static PLDHashOperator +FindFontForCharProc(nsStringHashKey::KeyType aKey, + nsRefPtr& aFontFamily, + void* aUserArg) +{ + FontSearch *data = (FontSearch*)aUserArg; + aFontFamily->FindFontForChar(data); + return PL_DHASH_NEXT; +} + +already_AddRefed +gfxAndroidPlatform::FindFontForChar(PRUint32 aCh, gfxFont *aFont) +{ + // is codepoint with no matching font? return null immediately + if (mCodepointsWithNoFonts.test(aCh)) { + return nsnull; + } + + FontSearch data(aCh, aFont); + + // find fonts that support the character + mFonts.Enumerate(FindFontForCharProc, &data); + + if (data.mBestMatch) { + nsRefPtr font = + gfxFT2Font::GetOrMakeFont(static_cast(data.mBestMatch.get()), + aFont->GetStyle()); + gfxFont* ret = font.forget().get(); + return already_AddRefed(ret); + } + + // no match? add to set of non-matching codepoints + mCodepointsWithNoFonts.set(aCh); + + return nsnull; +} + gfxFontEntry* gfxAndroidPlatform::MakePlatformFont(const gfxProxyFontEntry *aProxyEntry, const PRUint8 *aFontData, PRUint32 aLength) @@ -180,3 +691,14 @@ gfxAndroidPlatform::MakePlatformFont(const gfxProxyFontEntry *aProxyEntry, aLength); } +PRBool +gfxAndroidPlatform::GetPrefFontEntries(const nsCString& aKey, nsTArray > *aFontEntryList) +{ + return mPrefFonts.Get(aKey, aFontEntryList); +} + +void +gfxAndroidPlatform::SetPrefFontEntries(const nsCString& aKey, nsTArray >& aFontEntryList) +{ + mPrefFonts.Put(aKey, aFontEntryList); +} diff --git a/gfx/thebes/gfxAndroidPlatform.h b/gfx/thebes/gfxAndroidPlatform.h index d33db9cbe51..4d49ed4a3b5 100644 --- a/gfx/thebes/gfxAndroidPlatform.h +++ b/gfx/thebes/gfxAndroidPlatform.h @@ -39,13 +39,27 @@ #ifndef GFX_PLATFORM_ANDROID_H #define GFX_PLATFORM_ANDROID_H +#include "gfxFontUtils.h" #include "gfxFT2Fonts.h" #include "gfxPlatform.h" -#include "gfxUserFontSet.h" +#include "nsDataHashtable.h" #include "nsTArray.h" typedef struct FT_LibraryRec_ *FT_Library; +class FontFamily; +class FontEntry; +namespace mozilla { + namespace dom { + class FontListEntry; + }; +}; + +using namespace mozilla; +using namespace dom; + +class FontNameCache; + class THEBES_API gfxAndroidPlatform : public gfxPlatform { public: gfxAndroidPlatform(); @@ -55,39 +69,57 @@ public: return (gfxAndroidPlatform*) gfxPlatform::GetPlatform(); } - virtual already_AddRefed - CreateOffscreenSurface(const gfxIntSize& size, - gfxASurface::gfxContentType contentType); - - virtual gfxImageFormat GetOffscreenFormat() { return gfxASurface::ImageFormatRGB16_565; } - - // to support IPC font list (sharing between chrome and content) void GetFontList(InfallibleTArray* retValue); - // platform implementations of font functions + already_AddRefed CreateOffscreenSurface(const gfxIntSize& size, + gfxASurface::gfxContentType contentType); + virtual PRBool IsFontFormatSupported(nsIURI *aFontURI, PRUint32 aFormatFlags); virtual gfxPlatformFontList* CreatePlatformFontList(); virtual gfxFontEntry* MakePlatformFont(const gfxProxyFontEntry *aProxyEntry, - const PRUint8 *aFontData, PRUint32 aLength); + const PRUint8 *aFontData, PRUint32 aLength); - virtual nsresult GetFontList(nsIAtom *aLangGroup, - const nsACString& aGenericFamily, - nsTArray& aListOfFonts); + nsresult GetFontList(nsIAtom *aLangGroup, + const nsACString& aGenericFamily, + nsTArray& aListOfFonts); - virtual nsresult UpdateFontList(); + nsresult UpdateFontList(); - virtual nsresult ResolveFontName(const nsAString& aFontName, - FontResolverCallback aCallback, - void *aClosure, PRBool& aAborted); + nsresult ResolveFontName(const nsAString& aFontName, + FontResolverCallback aCallback, + void *aClosure, PRBool& aAborted); - virtual nsresult GetStandardFamilyName(const nsAString& aFontName, - nsAString& aFamilyName); + nsresult GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName); - virtual gfxFontGroup *CreateFontGroup(const nsAString &aFamilies, - const gfxFontStyle *aStyle, - gfxUserFontSet* aUserFontSet); + gfxFontGroup *CreateFontGroup(const nsAString &aFamilies, + const gfxFontStyle *aStyle, + gfxUserFontSet* aUserFontSet); + + FontFamily *FindFontFamily(const nsAString& aName); + FontEntry *FindFontEntry(const nsAString& aFamilyName, const gfxFontStyle& aFontStyle); + already_AddRefed FindFontForChar(PRUint32 aCh, gfxFont *aFont); + PRBool GetPrefFontEntries(const nsCString& aLangGroup, nsTArray > *aFontEntryList); + void SetPrefFontEntries(const nsCString& aLangGroup, nsTArray >& aFontEntryList); FT_Library GetFTLibrary(); + + virtual gfxImageFormat GetOffscreenFormat() { return gfxASurface::ImageFormatRGB16_565; } + +protected: + void AppendFacesFromFontFile(const char *aFileName, FontNameCache* aFontCache, InfallibleTArray* retValue); + void FindFontsInDirectory(const nsCString& aFontsDir, FontNameCache* aFontCache); + + typedef nsDataHashtable > FontTable; + + FontTable mFonts; + FontTable mFontAliases; + FontTable mFontSubstitutes; + InfallibleTArray mFontList; + + // when system-wide font lookup fails for a character, cache it to skip future searches + gfxSparseBitSet mCodepointsWithNoFonts; + + nsDataHashtable > > mPrefFonts; }; #endif /* GFX_PLATFORM_ANDROID_H */ diff --git a/gfx/thebes/gfxFT2FontList.cpp b/gfx/thebes/gfxFT2FontList.cpp index ea4d1d71ceb..4604a5f9e51 100644 --- a/gfx/thebes/gfxFT2FontList.cpp +++ b/gfx/thebes/gfxFT2FontList.cpp @@ -39,16 +39,6 @@ * * ***** END LICENSE BLOCK ***** */ -#ifdef ANDROID -#include "mozilla/dom/ContentChild.h" -#include "nsXULAppAPI.h" - -#include "gfxAndroidPlatform.h" -#include -#include -#define ALOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gecko" , ## args) -#endif - #include "gfxFT2FontList.h" #include "gfxUserFontSet.h" #include "gfxFontUtils.h" @@ -66,18 +56,24 @@ #include "nsAppDirectoryServiceDefs.h" #include "nsISimpleEnumerator.h" -#include "mozilla/scache/StartupCache.h" -#include - #ifdef XP_WIN #include "nsIWindowsRegKey.h" #include #endif +#define ROUND(x) floor((x) + 0.5) + #ifdef PR_LOGGING static PRLogModuleInfo *gFontInfoLog = PR_NewLogModule("fontInfoLog"); #endif /* PR_LOGGING */ +#ifdef ANDROID +#include "gfxAndroidPlatform.h" +#include +#include +#define ALOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gecko" , ## args) + +#endif #define LOG(args) PR_LOG(gFontInfoLog, PR_LOG_DEBUG, args) #define LOG_ENABLED() PR_LOG_TEST(gFontInfoLog, PR_LOG_DEBUG) @@ -91,338 +87,49 @@ BuildKeyNameFromFontName(nsAString &aName) ToLowerCase(aName); } -#define CACHE_KEY "font.cached-list" - -class FontNameCache { -public: - FontNameCache() - : mWriteNeeded(PR_FALSE) - { - mOps = (PLDHashTableOps) { - PL_DHashAllocTable, - PL_DHashFreeTable, - StringHash, - HashMatchEntry, - MoveEntry, - PL_DHashClearEntryStub, - PL_DHashFinalizeStub, - NULL - }; - - if (!PL_DHashTableInit(&mMap, &mOps, nsnull, - sizeof(FNCMapEntry), 0)) - { - mMap.ops = nsnull; - LOG(("initializing the map failed")); - } - - NS_ABORT_IF_FALSE(XRE_GetProcessType() == GeckoProcessType_Default, - "StartupCacheFontNameCache should only be used in chrome process"); - mCache = mozilla::scache::StartupCache::GetSingleton(); - - Init(); - } - - ~FontNameCache() - { - if (!mMap.ops) { - return; - } - if (!mWriteNeeded || !mCache) { - PL_DHashTableFinish(&mMap); - return; - } - - nsCAutoString buf; - PL_DHashTableEnumerate(&mMap, WriteOutMap, &buf); - PL_DHashTableFinish(&mMap); - mCache->PutBuffer(CACHE_KEY, buf.get(), buf.Length() + 1); - } - - void Init() - { - if (!mMap.ops || !mCache) { - return; - } - PRUint32 size; - char* buf; - if (NS_FAILED(mCache->GetBuffer(CACHE_KEY, &buf, &size))) { - return; - } - - LOG(("got: %s from the cache", nsDependentCString(buf, size).get())); - - const char* beginning = buf; - const char* end = strchr(beginning, ';'); - while (end) { - nsCString filename(beginning, end - beginning); - beginning = end + 1; - if (!(end = strchr(beginning, ';'))) { - break; - } - nsCString faceList(beginning, end - beginning); - beginning = end + 1; - if (!(end = strchr(beginning, ';'))) { - break; - } - PRUint32 timestamp = strtoul(beginning, NULL, 10); - beginning = end + 1; - if (!(end = strchr(beginning, ';'))) { - break; - } - PRUint32 filesize = strtoul(beginning, NULL, 10); - - FNCMapEntry* mapEntry = - static_cast - (PL_DHashTableOperate(&mMap, filename.get(), PL_DHASH_ADD)); - if (mapEntry) { - mapEntry->mFilename.Assign(filename); - mapEntry->mTimestamp = timestamp; - mapEntry->mFilesize = filesize; - mapEntry->mFaces.Assign(faceList); - // entries from the startupcache are marked "non-existing" - // until we have confirmed that the file still exists - mapEntry->mFileExists = PR_FALSE; - } - - beginning = end + 1; - end = strchr(beginning, ';'); - } - - // Should we use free() or delete[] here? See bug 684700. - free(buf); - } - - virtual void - GetInfoForFile(nsCString& aFileName, nsCString& aFaceList, - PRUint32 *aTimestamp, PRUint32 *aFilesize) - { - if (!mMap.ops) { - return; - } - PLDHashEntryHdr *hdr = - PL_DHashTableOperate(&mMap, aFileName.get(), PL_DHASH_LOOKUP); - if (!hdr) { - return; - } - FNCMapEntry* entry = static_cast(hdr); - if (entry && entry->mTimestamp && entry->mFilesize) { - *aTimestamp = entry->mTimestamp; - *aFilesize = entry->mFilesize; - aFaceList.Assign(entry->mFaces); - // this entry does correspond to an existing file - // (although it might not be up-to-date, in which case - // it will get overwritten via CacheFileInfo) - entry->mFileExists = PR_TRUE; - } - } - - virtual void - CacheFileInfo(nsCString& aFileName, nsCString& aFaceList, - PRUint32 aTimestamp, PRUint32 aFilesize) - { - if (!mMap.ops) { - return; - } - FNCMapEntry* entry = - static_cast - (PL_DHashTableOperate(&mMap, aFileName.get(), PL_DHASH_ADD)); - if (entry) { - entry->mFilename.Assign(aFileName); - entry->mTimestamp = aTimestamp; - entry->mFilesize = aFilesize; - entry->mFaces.Assign(aFaceList); - entry->mFileExists = PR_TRUE; - } - mWriteNeeded = PR_TRUE; - } - -private: - mozilla::scache::StartupCache* mCache; - PLDHashTable mMap; - PRBool mWriteNeeded; - - PLDHashTableOps mOps; - - static PLDHashOperator WriteOutMap(PLDHashTable *aTable, - PLDHashEntryHdr *aHdr, - PRUint32 aNumber, void *aData) - { - FNCMapEntry* entry = static_cast(aHdr); - if (!entry->mFileExists) { - // skip writing entries for files that are no longer present - return PL_DHASH_NEXT; - } - - nsCAutoString* buf = reinterpret_cast(aData); - buf->Append(entry->mFilename); - buf->Append(';'); - buf->Append(entry->mFaces); - buf->Append(';'); - buf->AppendInt(entry->mTimestamp); - buf->Append(';'); - buf->AppendInt(entry->mFilesize); - buf->Append(';'); - return PL_DHASH_NEXT; - } - - typedef struct : public PLDHashEntryHdr { - public: - nsCString mFilename; - PRUint32 mTimestamp; - PRUint32 mFilesize; - nsCString mFaces; - PRBool mFileExists; - } FNCMapEntry; - - static PLDHashNumber StringHash(PLDHashTable *table, const void *key) - { - return HashString(reinterpret_cast(key)); - } - - static PRBool HashMatchEntry(PLDHashTable *table, - const PLDHashEntryHdr *aHdr, const void *key) - { - const FNCMapEntry* entry = - static_cast(aHdr); - return entry->mFilename.Equals(reinterpret_cast(key)); - } - - static void MoveEntry(PLDHashTable *table, const PLDHashEntryHdr *aFrom, - PLDHashEntryHdr *aTo) - { - FNCMapEntry* to = static_cast(aTo); - const FNCMapEntry* from = static_cast(aFrom); - to->mFilename.Assign(from->mFilename); - to->mTimestamp = from->mTimestamp; - to->mFilesize = from->mFilesize; - to->mFaces.Assign(from->mFaces); - to->mFileExists = from->mFileExists; - } -}; - /*************************************************************** * * gfxFT2FontList * */ -// For Mobile, we use gfxFT2Fonts, and we build the font list by directly -// scanning the system's Fonts directory for OpenType and TrueType files. +// For Mobile, we use gfxFT2Fonts, and we build the font list by directly scanning +// the system's Fonts directory for OpenType and TrueType files. // -// FontEntry is currently defined in gfxFT2Fonts.h, but should probably be -// moved here for consistency with other platform implementations, and -// because it logically "belongs" to the fontlist that manages the families -// and entries. +// FontEntry is currently defined in gfxFT2Fonts.h, but will probably be moved here +// as part of the Freetype/Linux font restructuring for Harfbuzz integration. +// +// TODO: investigate startup performance - we might be able to improve by avoiding +// the creation of FT_Faces here, and just reading names directly from the file; +// or even consider caching a mapping from font family name to (list of) filenames, +// so that we don't have to scan all the files before we can do any font lookups. gfxFT2FontList::gfxFT2FontList() { } void -gfxFT2FontList::AppendFacesFromCachedFaceList(nsCString& aFileName, - PRBool aStdFile, - nsCString& aFaceList) +gfxFT2FontList::AppendFacesFromFontFile(const PRUnichar *aFileName) { - const char *beginning = aFaceList.get(); - const char *end = strchr(beginning, ','); - while (end) { - nsString familyName = - NS_ConvertUTF8toUTF16(beginning, end - beginning); - ToLowerCase(familyName); - beginning = end + 1; - if (!(end = strchr(beginning, ','))) { - break; - } - nsString faceName = - NS_ConvertUTF8toUTF16(beginning, end - beginning); - beginning = end + 1; - if (!(end = strchr(beginning, ','))) { - break; - } - PRUint32 index = strtoul(beginning, NULL, 10); - beginning = end + 1; - if (!(end = strchr(beginning, ','))) { - break; - } - PRBool italic = (*beginning != '0'); - beginning = end + 1; - if (!(end = strchr(beginning, ','))) { - break; - } - PRUint32 weight = strtoul(beginning, NULL, 10); - beginning = end + 1; - if (!(end = strchr(beginning, ','))) { - break; - } - PRInt32 stretch = strtol(beginning, NULL, 10); - - FontListEntry fle(familyName, faceName, aFileName, - weight, stretch, italic, index); - AppendFaceFromFontListEntry(fle, aStdFile); - - beginning = end + 1; - end = strchr(beginning, ','); - } -} - -static void -AppendToFaceList(nsCString& aFaceList, - nsAString& aFamilyName, FontEntry* aFontEntry) -{ - aFaceList.Append(NS_ConvertUTF16toUTF8(aFamilyName)); - aFaceList.Append(','); - aFaceList.Append(NS_ConvertUTF16toUTF8(aFontEntry->Name())); - aFaceList.Append(','); - aFaceList.AppendInt(aFontEntry->mFTFontIndex); - aFaceList.Append(','); - aFaceList.Append(aFontEntry->IsItalic() ? '1' : '0'); - aFaceList.Append(','); - aFaceList.AppendInt(aFontEntry->Weight()); - aFaceList.Append(','); - aFaceList.AppendInt(aFontEntry->Stretch()); - aFaceList.Append(','); + AppendFacesFromFontFile(NS_ConvertUTF16toUTF8(aFileName).get()); } void -gfxFT2FontList::AppendFacesFromFontFile(nsCString& aFileName, - PRBool aStdFile, - FontNameCache *aCache) +gfxFT2FontList::AppendFacesFromFontFile(const char *aFileName) { - nsCString faceList; - PRUint32 filesize = 0, timestamp = 0; - if (aCache) { - aCache->GetInfoForFile(aFileName, faceList, ×tamp, &filesize); - } - - struct stat s; - int statRetval = stat(aFileName.get(), &s); - if (!faceList.IsEmpty() && 0 == statRetval && - s.st_mtime == timestamp && s.st_size == filesize) - { - LOG(("using cached font info for %s", aFileName.get())); - AppendFacesFromCachedFaceList(aFileName, aStdFile, faceList); - return; - } - #ifdef XP_WIN FT_Library ftLibrary = gfxWindowsPlatform::GetPlatform()->GetFTLibrary(); #elif defined(ANDROID) FT_Library ftLibrary = gfxAndroidPlatform::GetPlatform()->GetFTLibrary(); #endif FT_Face dummy; - if (FT_Err_Ok == FT_New_Face(ftLibrary, aFileName.get(), -1, &dummy)) { - LOG(("reading font info via FreeType for %s", aFileName.get())); - nsCString faceList; - timestamp = s.st_mtime; - filesize = s.st_size; + if (FT_Err_Ok == FT_New_Face(ftLibrary, aFileName, -1, &dummy)) { for (FT_Long i = 0; i < dummy->num_faces; i++) { FT_Face face; - if (FT_Err_Ok != FT_New_Face(ftLibrary, aFileName.get(), i, &face)) { + if (FT_Err_Ok != FT_New_Face(ftLibrary, aFileName, i, &face)) continue; - } - FontEntry* fe = FontEntry::CreateFontEntry(face, aFileName.get(), i); + FontEntry* fe = FontEntry::CreateFontEntryFromFace(face); if (fe) { NS_ConvertUTF8toUTF16 name(face->family_name); BuildKeyNameFromFontName(name); @@ -430,16 +137,13 @@ gfxFT2FontList::AppendFacesFromFontFile(nsCString& aFileName, if (!family) { family = new gfxFontFamily(name); mFontFamilies.Put(name, family); - if (mBadUnderlineFamilyNames.Contains(name)) { + if (mBadUnderlineFamilyNames.Contains(name)) family->SetBadUnderlineFamily(); - } } - fe->mStandardFace = aStdFile; family->AddFontEntry(fe); - if (family->IsBadUnderlineFamily()) { + family->SetHasStyles(PR_TRUE); + if (family->IsBadUnderlineFamily()) fe->mIsBadUnderlineFont = PR_TRUE; - } - AppendToFaceList(faceList, name, fe); #ifdef PR_LOGGING if (LOG_ENABLED()) { LOG(("(fontinit) added (%s) to family (%s)" @@ -453,33 +157,9 @@ gfxFT2FontList::AppendFacesFromFontFile(nsCString& aFileName, } } FT_Done_Face(dummy); - if (aCache && 0 == statRetval && !faceList.IsEmpty()) { - aCache->CacheFileInfo(aFileName, faceList, timestamp, filesize); - } } } -// Called on each family after all fonts are added to the list; -// this will sort faces to give priority to "standard" font files -// if aUserArg is non-null (i.e. we're using it as a boolean flag) -static PLDHashOperator -FinalizeFamilyMemberList(nsStringHashKey::KeyType aKey, - nsRefPtr& aFamily, - void* aUserArg) -{ - gfxFontFamily *family = aFamily.get(); - PRBool sortFaces = (aUserArg != nsnull); - - family->SetHasStyles(PR_TRUE); - - if (sortFaces) { - family->SortAvailableFonts(); - } - family->CheckForSimpleFamily(); - - return PL_DHASH_NEXT; -} - void gfxFT2FontList::FindFonts() { @@ -519,7 +199,7 @@ gfxFT2FontList::FindFonts() nsAutoString filePath(path); filePath.AppendLiteral("\\"); filePath.Append(results.cFileName); - AppendFacesFromFontFile(NS_ConvertUTF16toUTF8(filePath)); + AppendFacesFromFontFile(static_cast(filePath.get())); moreFiles = FindNextFile(handle, &results); } if (handle != INVALID_HANDLE_VALUE) @@ -533,144 +213,29 @@ gfxFT2FontList::FindFonts() mPrefFonts.Clear(); mCodepointsWithNoFonts.reset(); + DIR *d = opendir("/system/fonts"); + struct dirent *ent = NULL; + while(d && (ent = readdir(d)) != NULL) { + int namelen = strlen(ent->d_name); + if (namelen > 4 && + strcasecmp(ent->d_name + namelen - 4, ".ttf") == 0) + { + nsCString s("/system/fonts"); + s.Append("/"); + s.Append(nsDependentCString(ent->d_name)); + + AppendFacesFromFontFile(nsPromiseFlatCString(s).get()); + } + } + if (d) { + closedir(d); + } mCodepointsWithNoFonts.SetRange(0,0x1f); // C0 controls mCodepointsWithNoFonts.SetRange(0x7f,0x9f); // C1 controls - if (XRE_GetProcessType() != GeckoProcessType_Default) { - // Content process: ask the Chrome process to give us the list - InfallibleTArray fonts; - mozilla::dom::ContentChild::GetSingleton()->SendReadFontList(&fonts); - for (PRUint32 i = 0, n = fonts.Length(); i < n; ++i) { - AppendFaceFromFontListEntry(fonts[i], PR_FALSE); - } - // Passing null for userdata tells Finalize that it does not need - // to sort faces (because they were already sorted by chrome, - // so we just maintain the existing order) - mFontFamilies.Enumerate(FinalizeFamilyMemberList, nsnull); - LOG(("got font list from chrome process: %d faces in %d families", - fonts.Length(), mFontFamilies.Count())); - return; - } - - // Chrome process: get the cached list (if any) - FontNameCache fnc; - - // ANDROID_ROOT is the root of the android system, typically /system; - // font files are in /$ANDROID_ROOT/fonts/ - nsCString root; - char *androidRoot = PR_GetEnv("ANDROID_ROOT"); - if (androidRoot) { - root = androidRoot; - } else { - root = NS_LITERAL_CSTRING("/system"); - } - root.Append("/fonts"); - - static const char* sStandardFonts[] = { - "DroidSans.ttf", - "DroidSans-Bold.ttf", - "DroidSerif-Regular.ttf", - "DroidSerif-Bold.ttf", - "DroidSerif-Italic.ttf", - "DroidSerif-BoldItalic.ttf", - "DroidSansMono.ttf", - "DroidSansArabic.ttf", - "DroidSansHebrew.ttf", - "DroidSansThai.ttf", - "MTLmr3m.ttf", - "MTLc3m.ttf", - "DroidSansJapanese.ttf", - "DroidSansFallback.ttf" - }; - - DIR *d = opendir(root.get()); - if (!d) { - // if we can't find/read the font directory, we are doomed! - NS_RUNTIMEABORT("Could not read the system fonts directory"); - } - - struct dirent *ent = NULL; - while ((ent = readdir(d)) != NULL) { - int namelen = strlen(ent->d_name); - if (namelen <= 4) { - // cannot be a usable font filename - continue; - } - const char *ext = ent->d_name + namelen - 4; - if (strcasecmp(ext, ".ttf") == 0 || - strcasecmp(ext, ".otf") == 0 || - strcasecmp(ext, ".ttc") == 0) - { - bool isStdFont = false; - for (unsigned int i = 0; - i < NS_ARRAY_LENGTH(sStandardFonts) && !isStdFont; i++) - { - isStdFont = strcmp(sStandardFonts[i], ent->d_name) == 0; - } - - nsCString s(root); - s.Append('/'); - s.Append(ent->d_name, namelen); - - // Add the face(s) from this file to our font list; - // note that if we have cached info for this file in fnc, - // and the file is unchanged, we won't actually need to read it. - // If the file is new/changed, this will update the FontNameCache. - AppendFacesFromFontFile(s, isStdFont, &fnc); - } - } - closedir(d); - - // Finalize the families by sorting faces into standard order - // and marking "simple" families. - // Passing non-null userData here says that we want faces to be sorted. - mFontFamilies.Enumerate(FinalizeFamilyMemberList, this); #endif // XP_WIN && ANDROID } -void -gfxFT2FontList::AppendFaceFromFontListEntry(const FontListEntry& aFLE, - PRBool aStdFile) -{ - FontEntry* fe = FontEntry::CreateFontEntry(aFLE); - if (fe) { - fe->mStandardFace = aStdFile; - nsAutoString name(aFLE.familyName()); - gfxFontFamily *family = mFontFamilies.GetWeak(name); - if (!family) { - family = new gfxFontFamily(name); - mFontFamilies.Put(name, family); - if (mBadUnderlineFamilyNames.Contains(name)) { - family->SetBadUnderlineFamily(); - } - } - family->AddFontEntry(fe); - if (family->IsBadUnderlineFamily()) { - fe->mIsBadUnderlineFont = PR_TRUE; - } - } -} - -static PLDHashOperator -AddFamilyToFontList(nsStringHashKey::KeyType aKey, - nsRefPtr& aFamily, - void* aUserArg) -{ - InfallibleTArray* fontlist = - reinterpret_cast*>(aUserArg); - - FontFamily *family = static_cast(aFamily.get()); - family->AddFacesToFontList(fontlist); - - return PL_DHASH_NEXT; -} - -void -gfxFT2FontList::GetFontList(InfallibleTArray* retValue) -{ - mFontFamilies.Enumerate(AddFamilyToFontList, retValue); -} - nsresult gfxFT2FontList::InitFontList() { diff --git a/gfx/thebes/gfxFT2FontList.h b/gfx/thebes/gfxFT2FontList.h index 1d15c0dc98d..2cabe44a5b4 100644 --- a/gfx/thebes/gfxFT2FontList.h +++ b/gfx/thebes/gfxFT2FontList.h @@ -47,14 +47,7 @@ #endif #include "gfxPlatformFontList.h" -namespace mozilla { - namespace dom { - class FontListEntry; - }; -}; -using mozilla::dom::FontListEntry; - -class FontNameCache; +#include class gfxFT2FontList : public gfxPlatformFontList { @@ -71,26 +64,11 @@ public: const PRUint8 *aFontData, PRUint32 aLength); - void GetFontList(InfallibleTArray* retValue); - - static gfxFT2FontList* PlatformFontList() { - return static_cast(gfxPlatformFontList::PlatformFontList()); - } - protected: virtual nsresult InitFontList(); - void AppendFaceFromFontListEntry(const FontListEntry& aFLE, - PRBool isStdFile); - - void AppendFacesFromFontFile(nsCString& aFileName, - PRBool isStdFile = PR_FALSE, - FontNameCache *aCache = nsnull); - - void AppendFacesFromCachedFaceList(nsCString& aFileName, - PRBool isStdFile, - nsCString& aFaceList); - + void AppendFacesFromFontFile(const PRUnichar *aFileName); + void AppendFacesFromFontFile(const char *aFileName); void FindFonts(); }; diff --git a/gfx/thebes/gfxFT2Fonts.cpp b/gfx/thebes/gfxFT2Fonts.cpp index 9f062da0c1f..f5e19f4322b 100644 --- a/gfx/thebes/gfxFT2Fonts.cpp +++ b/gfx/thebes/gfxFT2Fonts.cpp @@ -46,7 +46,6 @@ #define gfxToolkitPlatform gfxWindowsPlatform #include "gfxFT2FontList.h" #elif defined(ANDROID) -#include "mozilla/dom/ContentChild.h" #include "gfxAndroidPlatform.h" #define gfxToolkitPlatform gfxAndroidPlatform #endif @@ -72,8 +71,19 @@ #include "mozilla/Preferences.h" +using namespace mozilla; + static PRLogModuleInfo *gFontLog = PR_NewLogModule("ft2fonts"); +static const char *sCJKLangGroup[] = { + "ja", + "ko", + "zh-cn", + "zh-hk", + "zh-tw" +}; +#define COUNT_OF_CJK_LANG_GROUP 5 + // rounding and truncation functions for a Freetype floating point number // (FT26Dot6) stored in a 32bit integer with high 26 bits for the integer // part and low 6 bits for the fractional part. @@ -82,54 +92,6 @@ static PRLogModuleInfo *gFontLog = PR_NewLogModule("ft2fonts"); #define CONVERT_DESIGN_UNITS_TO_PIXELS(v, s) \ MOZ_FT_TRUNC(MOZ_FT_ROUND(FT_MulFix((v) , (s)))) -static cairo_scaled_font_t * -CreateScaledFont(FontEntry *aFontEntry, const gfxFontStyle *aStyle) -{ - cairo_scaled_font_t *scaledFont = NULL; - - cairo_matrix_t sizeMatrix; - cairo_matrix_t identityMatrix; - - // XXX deal with adjusted size - cairo_matrix_init_scale(&sizeMatrix, aStyle->size, aStyle->size); - cairo_matrix_init_identity(&identityMatrix); - - // synthetic oblique by skewing via the font matrix - PRBool needsOblique = - !aFontEntry->mItalic && - (aStyle->style & (FONT_STYLE_ITALIC | FONT_STYLE_OBLIQUE)); - - if (needsOblique) { - const double kSkewFactor = 0.25; - - cairo_matrix_t style; - cairo_matrix_init(&style, - 1, //xx - 0, //yx - -1 * kSkewFactor, //xy - 1, //yy - 0, //x0 - 0); //y0 - cairo_matrix_multiply(&sizeMatrix, &sizeMatrix, &style); - } - - cairo_font_options_t *fontOptions = cairo_font_options_create(); - -#ifdef MOZ_GFX_OPTIMIZE_MOBILE - cairo_font_options_set_hint_metrics(fontOptions, CAIRO_HINT_METRICS_OFF); -#endif - - scaledFont = cairo_scaled_font_create(aFontEntry->CairoFontFace(), - &sizeMatrix, - &identityMatrix, fontOptions); - cairo_font_options_destroy(fontOptions); - - NS_ASSERTION(cairo_scaled_font_status(scaledFont) == CAIRO_STATUS_SUCCESS, - "Failed to make scaled font"); - - return scaledFont; -} - /** * FontEntry */ @@ -148,12 +110,9 @@ FontEntry::~FontEntry() } gfxFont* -FontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle, PRBool aNeedsBold) -{ - cairo_scaled_font_t *scaledFont = CreateScaledFont(this, aFontStyle); - gfxFont *font = new gfxFT2Font(scaledFont, this, aFontStyle, aNeedsBold); - cairo_scaled_font_destroy(scaledFont); - return font; +FontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle, PRBool aNeedsBold) { + already_AddRefed font = gfxFT2Font::GetOrMakeFont(this, aFontStyle, aNeedsBold); + return font.get(); } /* static */ @@ -173,7 +132,7 @@ FontEntry::CreateFontEntry(const gfxProxyFontEntry &aProxyEntry, NS_Free((void*)aFontData); return nsnull; } - FontEntry* fe = FontEntry::CreateFontEntry(face, nsnull, 0, aFontData); + FontEntry* fe = FontEntry::CreateFontEntryFromFace(face, aFontData); if (fe) { fe->mItalic = aProxyEntry.mItalic; fe->mWeight = aProxyEntry.mWeight; @@ -210,22 +169,7 @@ FTFontDestroyFunc(void *data) } /* static */ FontEntry* -FontEntry::CreateFontEntry(const FontListEntry& aFLE) -{ - FontEntry *fe = new FontEntry(aFLE.faceName()); - fe->mFilename = aFLE.filepath(); - fe->mFTFontIndex = aFLE.index(); - fe->mWeight = aFLE.weight(); - fe->mStretch = aFLE.stretch(); - fe->mItalic = aFLE.italic(); - return fe; -} - -/* static */ FontEntry* -FontEntry::CreateFontEntry(FT_Face aFace, - const char* aFilename, PRUint8 aIndex, - const PRUint8 *aFontData) -{ +FontEntry::CreateFontEntryFromFace(FT_Face aFace, const PRUint8 *aFontData) { static cairo_user_data_key_t key; if (!aFace->family_name) { @@ -247,8 +191,6 @@ FontEntry::CreateFontEntry(FT_Face aFace, #else fe->mFontFace = cairo_ft_font_face_create_for_ft_face(aFace, 0); #endif - fe->mFilename = aFilename; - fe->mFTFontIndex = aIndex; FTUserFontData *userFontData = new FTUserFontData(aFace, aFontData); cairo_font_face_set_user_data(fe->mFontFace, &key, userFontData, FTFontDestroyFunc); @@ -360,25 +302,37 @@ FontFamily::FindFontEntry(const gfxFontStyle& aFontStyle) return static_cast(FindFontForStyle(aFontStyle, needsBold)); } -void -FontFamily::AddFacesToFontList(InfallibleTArray* aFontList) +void FontFamily::AddFontFileAndIndex(nsCString aFilename, PRUint32 aIndex) { - for (int i = 0, n = mAvailableFonts.Length(); i < n; ++i) { - const FontEntry *fe = - static_cast(mAvailableFonts[i].get()); - if (!fe) { - continue; - } - - aFontList->AppendElement(FontListEntry(Name(), fe->Name(), - fe->mFilename, - fe->Weight(), fe->Stretch(), - fe->IsItalic(), - fe->mFTFontIndex)); + SetHasStyles(PR_FALSE); + mFilenames.AppendElement(new FileAndIndex(aFilename, aIndex)); +} + + + +void +FontFamily::FindStyleVariations() +{ + if (mHasStyles) { + return; } + mHasStyles = PR_TRUE; + + for (int i = 0; i < mFilenames.Length(); i++) { + FT_Face face; + gfxToolkitPlatform* platform = gfxToolkitPlatform::GetPlatform(); + if (FT_Err_Ok == FT_New_Face(platform->GetFTLibrary(), + mFilenames[i].filename.get(), + mFilenames[i].index, &face)) { + FontEntry* fe = FontEntry::CreateFontEntryFromFace(face); + if (fe) + AddFontEntry(fe); + } + } + mFilenames.Clear(); + SetHasStyles(PR_TRUE); } -#ifndef ANDROID // not needed on Android, we use the generic gfxFontGroup /** * gfxFT2FontGroup */ @@ -697,7 +651,7 @@ gfxFT2FontGroup::WhichPrefFontSupportsChar(PRUint32 aCh) already_AddRefed gfxFT2FontGroup::WhichSystemFontSupportsChar(PRUint32 aCh) { -#if defined(XP_WIN) || defined(ANDROID) +#ifdef XP_WIN FontEntry *fe = static_cast (gfxPlatformFontList::PlatformFontList()->FindFontForChar(aCh, GetFontAt(0))); if (fe) { @@ -716,8 +670,6 @@ gfxFT2FontGroup::WhichSystemFontSupportsChar(PRUint32 aCh) return nsnull; } -#endif // !ANDROID - /** * gfxFT2Font */ @@ -872,12 +824,57 @@ gfxFT2Font::CairoFontFace() return GetFontEntry()->CairoFontFace(); } +static cairo_scaled_font_t * +CreateScaledFont(FontEntry *aFontEntry, const gfxFontStyle *aStyle) +{ + cairo_scaled_font_t *scaledFont = NULL; + + cairo_matrix_t sizeMatrix; + cairo_matrix_t identityMatrix; + + // XXX deal with adjusted size + cairo_matrix_init_scale(&sizeMatrix, aStyle->size, aStyle->size); + cairo_matrix_init_identity(&identityMatrix); + + // synthetic oblique by skewing via the font matrix + PRBool needsOblique = (!aFontEntry->mItalic && (aStyle->style & (FONT_STYLE_ITALIC | FONT_STYLE_OBLIQUE))); + + if (needsOblique) { + const double kSkewFactor = 0.25; + + cairo_matrix_t style; + cairo_matrix_init(&style, + 1, //xx + 0, //yx + -1 * kSkewFactor, //xy + 1, //yy + 0, //x0 + 0); //y0 + cairo_matrix_multiply(&sizeMatrix, &sizeMatrix, &style); + } + + cairo_font_options_t *fontOptions = cairo_font_options_create(); + +#ifdef MOZ_GFX_OPTIMIZE_MOBILE + cairo_font_options_set_hint_metrics(fontOptions, CAIRO_HINT_METRICS_OFF); +#endif + + scaledFont = cairo_scaled_font_create(aFontEntry->CairoFontFace(), + &sizeMatrix, + &identityMatrix, fontOptions); + cairo_font_options_destroy(fontOptions); + + NS_ASSERTION(cairo_scaled_font_status(scaledFont) == CAIRO_STATUS_SUCCESS, + "Failed to make scaled font"); + + return scaledFont; +} + /** * Look up the font in the gfxFont cache. If we don't find it, create one. * In either case, add a ref, append it to the aFonts array, and return it --- * except for OOM in which case we do nothing and return null. */ -#ifndef ANDROID // this is only for gfxFT2FontGroup, which is not used on Android already_AddRefed gfxFT2Font::GetOrMakeFont(const nsAString& aName, const gfxFontStyle *aStyle, PRBool aNeedsBold) { @@ -891,7 +888,6 @@ gfxFT2Font::GetOrMakeFont(const nsAString& aName, const gfxFontStyle *aStyle, PR nsRefPtr font = GetOrMakeFont(fe, aStyle, aNeedsBold); return font.forget(); } -#endif already_AddRefed gfxFT2Font::GetOrMakeFont(FontEntry *aFontEntry, const gfxFontStyle *aStyle, PRBool aNeedsBold) diff --git a/gfx/thebes/gfxFT2Fonts.h b/gfx/thebes/gfxFT2Fonts.h index 5464a73e2db..ffe6c5ca5b6 100644 --- a/gfx/thebes/gfxFT2Fonts.h +++ b/gfx/thebes/gfxFT2Fonts.h @@ -47,15 +47,18 @@ #include "gfxFontUtils.h" #include "gfxUserFontSet.h" -namespace mozilla { - namespace dom { - class FontListEntry; - }; -}; -using mozilla::dom::FontListEntry; - typedef struct FT_FaceRec_* FT_Face; +class FileAndIndex { +public: + FileAndIndex(nsCString aFilename, PRUint32 aIndex) : + filename(aFilename), index(aIndex) {} + FileAndIndex(FileAndIndex* fai) : + filename(fai->filename), index(fai->index) {} + nsCString filename; + PRUint32 index; +}; + /** * FontFamily is a class that describes one of the fonts on the users system. It holds * each FontEntry (maps more directly to a font face) which holds font type, charset info @@ -69,9 +72,13 @@ public: gfxFontFamily(aName) { } FontEntry *FindFontEntry(const gfxFontStyle& aFontStyle); + virtual void FindStyleVariations(); + void AddFontFileAndIndex(nsCString aFilename, PRUint32 aIndex); - // Append this family's faces to the IPC fontlist - void AddFacesToFontList(InfallibleTArray* aFontList); +private: + // mFilenames are queus of font files that + // need to be lazily processed into font entries + nsTArray mFilenames; }; class FontEntry : public gfxFontEntry @@ -91,22 +98,12 @@ public: return Name(); } - // create a font entry for a downloaded font static FontEntry* CreateFontEntry(const gfxProxyFontEntry &aProxyEntry, const PRUint8 *aFontData, PRUint32 aLength); - // create a font entry representing an installed font, identified by - // a FontListEntry; the freetype and cairo faces will not be instantiated - // until actually needed - static FontEntry* - CreateFontEntry(const FontListEntry& aFLE); - - // create a font entry for a given freetype face; if it is an installed font, - // also record the filename and index static FontEntry* - CreateFontEntry(FT_Face aFace, const char *aFilename, PRUint8 aIndex, - const PRUint8 *aFontData = nsnull); + CreateFontEntryFromFace(FT_Face aFace, const PRUint8 *aFontData = nsnull); // aFontData is NS_Malloc'ed data that aFace depends on, to be freed // after the face is destroyed; null if there is no such buffer @@ -137,11 +134,8 @@ public: // new functions FontEntry *GetFontEntry(); -#ifndef ANDROID static already_AddRefed GetOrMakeFont(const nsAString& aName, const gfxFontStyle *aStyle, PRBool aNeedsBold = PR_FALSE); -#endif - static already_AddRefed GetOrMakeFont(FontEntry *aFontEntry, const gfxFontStyle *aStyle, PRBool aNeedsBold = PR_FALSE); diff --git a/gfx/thebes/gfxFont.cpp b/gfx/thebes/gfxFont.cpp index 297487c4b6d..4378551c2b7 100644 --- a/gfx/thebes/gfxFont.cpp +++ b/gfx/thebes/gfxFont.cpp @@ -540,21 +540,15 @@ gfxFontFamily::FindFontForStyle(const gfxFontStyle& aFontStyle, void gfxFontFamily::CheckForSimpleFamily() { - PRUint32 count = mAvailableFonts.Length(); - if (count > 4 || count == 0) { + if (mAvailableFonts.Length() > 4 || mAvailableFonts.Length() == 0) { return; // can't be "simple" if there are >4 faces; // if none then the family is unusable anyway } - if (count == 1) { - mIsSimpleFamily = PR_TRUE; - return; - } - PRInt16 firstStretch = mAvailableFonts[0]->Stretch(); gfxFontEntry *faces[4] = { 0 }; - for (PRUint8 i = 0; i < count; ++i) { + for (PRUint8 i = 0; i < mAvailableFonts.Length(); ++i) { gfxFontEntry *fe = mAvailableFonts[i]; if (fe->Stretch() != firstStretch) { return; // font-stretch doesn't match, don't treat as simple family