2009-10-07 07:13:40 -07:00
|
|
|
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
|
|
* ***** BEGIN LICENSE BLOCK *****
|
|
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
|
|
* the License. You may obtain a copy of the License at
|
|
|
|
* http://www.mozilla.org/MPL/
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
* for the specific language governing rights and limitations under the
|
|
|
|
* License.
|
|
|
|
*
|
|
|
|
* The Original Code is Mozilla Corporation code.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is Mozilla Corporation.
|
|
|
|
* Portions created by the Initial Developer are Copyright (C) 2009
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
|
|
|
* Stuart Parmenter <stuart@mozilla.com>
|
|
|
|
* Masayuki Nakano <masayuki@d-toybox.com>
|
|
|
|
* Mats Palmgren <mats.palmgren@bredband.net>
|
|
|
|
* John Daggett <jdaggett@mozilla.com>
|
|
|
|
* Jonathan Kew <jfkthame@gmail.com>
|
|
|
|
*
|
|
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
|
|
* the provisions above, a recipient may use your version of this file under
|
|
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
|
|
*
|
|
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
|
2011-09-23 04:15:36 -07:00
|
|
|
#ifdef ANDROID
|
|
|
|
#include "mozilla/dom/ContentChild.h"
|
|
|
|
#include "nsXULAppAPI.h"
|
|
|
|
|
|
|
|
#include "gfxAndroidPlatform.h"
|
|
|
|
#include <dirent.h>
|
|
|
|
#include <android/log.h>
|
|
|
|
#define ALOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gecko" , ## args)
|
|
|
|
#endif
|
|
|
|
|
2011-09-23 01:53:41 -07:00
|
|
|
#include "gfxFT2FontList.h"
|
|
|
|
#include "gfxUserFontSet.h"
|
|
|
|
#include "gfxFontUtils.h"
|
2009-10-07 07:13:40 -07:00
|
|
|
|
2011-09-23 02:34:42 -07:00
|
|
|
#include "ft2build.h"
|
|
|
|
#include FT_FREETYPE_H
|
|
|
|
#include "gfxFT2Fonts.h"
|
|
|
|
|
2009-10-07 07:13:40 -07:00
|
|
|
#include "nsServiceManagerUtils.h"
|
|
|
|
#include "nsTArray.h"
|
|
|
|
#include "nsUnicharUtils.h"
|
|
|
|
|
|
|
|
#include "nsDirectoryServiceUtils.h"
|
|
|
|
#include "nsDirectoryServiceDefs.h"
|
|
|
|
#include "nsAppDirectoryServiceDefs.h"
|
|
|
|
#include "nsISimpleEnumerator.h"
|
|
|
|
|
2011-09-23 04:15:36 -07:00
|
|
|
#include "mozilla/scache/StartupCache.h"
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
2009-10-07 07:13:40 -07:00
|
|
|
#ifdef XP_WIN
|
2010-07-15 23:03:45 -07:00
|
|
|
#include "nsIWindowsRegKey.h"
|
2009-10-07 07:13:40 -07:00
|
|
|
#include <windows.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef PR_LOGGING
|
|
|
|
static PRLogModuleInfo *gFontInfoLog = PR_NewLogModule("fontInfoLog");
|
|
|
|
#endif /* PR_LOGGING */
|
|
|
|
|
|
|
|
#define LOG(args) PR_LOG(gFontInfoLog, PR_LOG_DEBUG, args)
|
|
|
|
#define LOG_ENABLED() PR_LOG_TEST(gFontInfoLog, PR_LOG_DEBUG)
|
|
|
|
|
|
|
|
static __inline void
|
|
|
|
BuildKeyNameFromFontName(nsAString &aName)
|
|
|
|
{
|
2010-07-15 23:03:45 -07:00
|
|
|
#ifdef XP_WIN
|
2009-10-07 07:13:40 -07:00
|
|
|
if (aName.Length() >= LF_FACESIZE)
|
|
|
|
aName.Truncate(LF_FACESIZE - 1);
|
2010-07-15 23:03:45 -07:00
|
|
|
#endif
|
2009-10-07 07:13:40 -07:00
|
|
|
ToLowerCase(aName);
|
|
|
|
}
|
|
|
|
|
2011-09-23 04:15:36 -07:00
|
|
|
#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<FNCMapEntry*>
|
|
|
|
(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<FNCMapEntry*>(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<FNCMapEntry*>
|
|
|
|
(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<FNCMapEntry*>(aHdr);
|
|
|
|
if (!entry->mFileExists) {
|
|
|
|
// skip writing entries for files that are no longer present
|
|
|
|
return PL_DHASH_NEXT;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCAutoString* buf = reinterpret_cast<nsCAutoString*>(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<const char*>(key));
|
|
|
|
}
|
|
|
|
|
|
|
|
static PRBool HashMatchEntry(PLDHashTable *table,
|
|
|
|
const PLDHashEntryHdr *aHdr, const void *key)
|
|
|
|
{
|
|
|
|
const FNCMapEntry* entry =
|
|
|
|
static_cast<const FNCMapEntry*>(aHdr);
|
|
|
|
return entry->mFilename.Equals(reinterpret_cast<const char*>(key));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void MoveEntry(PLDHashTable *table, const PLDHashEntryHdr *aFrom,
|
|
|
|
PLDHashEntryHdr *aTo)
|
|
|
|
{
|
|
|
|
FNCMapEntry* to = static_cast<FNCMapEntry*>(aTo);
|
|
|
|
const FNCMapEntry* from = static_cast<const FNCMapEntry*>(aFrom);
|
|
|
|
to->mFilename.Assign(from->mFilename);
|
|
|
|
to->mTimestamp = from->mTimestamp;
|
|
|
|
to->mFilesize = from->mFilesize;
|
|
|
|
to->mFaces.Assign(from->mFaces);
|
|
|
|
to->mFileExists = from->mFileExists;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2009-10-07 07:13:40 -07:00
|
|
|
/***************************************************************
|
|
|
|
*
|
|
|
|
* gfxFT2FontList
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2011-09-23 04:15:36 -07:00
|
|
|
// For Mobile, we use gfxFT2Fonts, and we build the font list by directly
|
|
|
|
// scanning the system's Fonts directory for OpenType and TrueType files.
|
2011-09-23 02:34:42 -07:00
|
|
|
//
|
2011-09-23 04:15:36 -07:00
|
|
|
// 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.
|
2009-10-07 07:13:40 -07:00
|
|
|
|
|
|
|
gfxFT2FontList::gfxFT2FontList()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2011-09-23 04:15:36 -07:00
|
|
|
gfxFT2FontList::AppendFacesFromCachedFaceList(nsCString& aFileName,
|
|
|
|
PRBool aStdFile,
|
|
|
|
nsCString& aFaceList)
|
2009-10-07 07:13:40 -07:00
|
|
|
{
|
2011-09-23 04:15:36 -07:00
|
|
|
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(',');
|
2010-07-15 23:03:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2011-09-23 04:15:36 -07:00
|
|
|
gfxFT2FontList::AppendFacesFromFontFile(nsCString& aFileName,
|
|
|
|
PRBool aStdFile,
|
|
|
|
FontNameCache *aCache)
|
2010-07-15 23:03:45 -07:00
|
|
|
{
|
2011-09-23 04:15:36 -07:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2010-07-15 23:03:45 -07:00
|
|
|
#ifdef XP_WIN
|
2009-10-07 07:13:40 -07:00
|
|
|
FT_Library ftLibrary = gfxWindowsPlatform::GetPlatform()->GetFTLibrary();
|
2010-07-15 23:03:45 -07:00
|
|
|
#elif defined(ANDROID)
|
|
|
|
FT_Library ftLibrary = gfxAndroidPlatform::GetPlatform()->GetFTLibrary();
|
|
|
|
#endif
|
2009-10-07 07:13:40 -07:00
|
|
|
FT_Face dummy;
|
2011-09-23 04:15:36 -07:00
|
|
|
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;
|
2009-10-07 07:13:40 -07:00
|
|
|
for (FT_Long i = 0; i < dummy->num_faces; i++) {
|
|
|
|
FT_Face face;
|
2011-09-23 04:15:36 -07:00
|
|
|
if (FT_Err_Ok != FT_New_Face(ftLibrary, aFileName.get(), i, &face)) {
|
2009-10-07 07:13:40 -07:00
|
|
|
continue;
|
2011-09-23 04:15:36 -07:00
|
|
|
}
|
2009-10-07 07:13:40 -07:00
|
|
|
|
2011-09-23 04:15:36 -07:00
|
|
|
FontEntry* fe = FontEntry::CreateFontEntry(face, aFileName.get(), i);
|
2009-10-07 07:13:40 -07:00
|
|
|
if (fe) {
|
|
|
|
NS_ConvertUTF8toUTF16 name(face->family_name);
|
|
|
|
BuildKeyNameFromFontName(name);
|
|
|
|
gfxFontFamily *family = mFontFamilies.GetWeak(name);
|
|
|
|
if (!family) {
|
2011-09-23 02:34:42 -07:00
|
|
|
family = new gfxFontFamily(name);
|
2009-10-07 07:13:40 -07:00
|
|
|
mFontFamilies.Put(name, family);
|
2011-09-23 04:15:36 -07:00
|
|
|
if (mBadUnderlineFamilyNames.Contains(name)) {
|
2010-01-27 22:56:16 -08:00
|
|
|
family->SetBadUnderlineFamily();
|
2011-09-23 04:15:36 -07:00
|
|
|
}
|
2009-10-07 07:13:40 -07:00
|
|
|
}
|
2011-09-23 04:15:36 -07:00
|
|
|
fe->mStandardFace = aStdFile;
|
2009-10-07 07:13:40 -07:00
|
|
|
family->AddFontEntry(fe);
|
2011-09-23 04:15:36 -07:00
|
|
|
if (family->IsBadUnderlineFamily()) {
|
2010-01-27 22:56:16 -08:00
|
|
|
fe->mIsBadUnderlineFont = PR_TRUE;
|
2011-09-23 04:15:36 -07:00
|
|
|
}
|
|
|
|
AppendToFaceList(faceList, name, fe);
|
2009-10-07 07:13:40 -07:00
|
|
|
#ifdef PR_LOGGING
|
|
|
|
if (LOG_ENABLED()) {
|
|
|
|
LOG(("(fontinit) added (%s) to family (%s)"
|
|
|
|
" with style: %s weight: %d stretch: %d",
|
|
|
|
NS_ConvertUTF16toUTF8(fe->Name()).get(),
|
|
|
|
NS_ConvertUTF16toUTF8(family->Name()).get(),
|
|
|
|
fe->IsItalic() ? "italic" : "normal",
|
|
|
|
fe->Weight(), fe->Stretch()));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
FT_Done_Face(dummy);
|
2011-09-23 04:15:36 -07:00
|
|
|
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<gfxFontFamily>& aFamily,
|
|
|
|
void* aUserArg)
|
|
|
|
{
|
|
|
|
gfxFontFamily *family = aFamily.get();
|
|
|
|
PRBool sortFaces = (aUserArg != nsnull);
|
|
|
|
|
|
|
|
family->SetHasStyles(PR_TRUE);
|
|
|
|
|
|
|
|
if (sortFaces) {
|
|
|
|
family->SortAvailableFonts();
|
2009-10-07 07:13:40 -07:00
|
|
|
}
|
2011-09-23 04:15:36 -07:00
|
|
|
family->CheckForSimpleFamily();
|
|
|
|
|
|
|
|
return PL_DHASH_NEXT;
|
2009-10-07 07:13:40 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gfxFT2FontList::FindFonts()
|
|
|
|
{
|
2010-07-15 23:03:45 -07:00
|
|
|
#ifdef XP_WIN
|
2009-10-07 07:13:40 -07:00
|
|
|
nsTArray<nsString> searchPaths(3);
|
|
|
|
nsTArray<nsString> fontPatterns(3);
|
|
|
|
fontPatterns.AppendElement(NS_LITERAL_STRING("\\*.ttf"));
|
|
|
|
fontPatterns.AppendElement(NS_LITERAL_STRING("\\*.ttc"));
|
|
|
|
fontPatterns.AppendElement(NS_LITERAL_STRING("\\*.otf"));
|
|
|
|
wchar_t pathBuf[256];
|
|
|
|
SHGetSpecialFolderPathW(0, pathBuf, CSIDL_WINDOWS, 0);
|
|
|
|
searchPaths.AppendElement(pathBuf);
|
|
|
|
SHGetSpecialFolderPathW(0, pathBuf, CSIDL_FONTS, 0);
|
|
|
|
searchPaths.AppendElement(pathBuf);
|
|
|
|
nsCOMPtr<nsIFile> resDir;
|
|
|
|
NS_GetSpecialDirectory(NS_APP_RES_DIR, getter_AddRefs(resDir));
|
|
|
|
if (resDir) {
|
|
|
|
resDir->Append(NS_LITERAL_STRING("fonts"));
|
|
|
|
nsAutoString resPath;
|
|
|
|
resDir->GetPath(resPath);
|
|
|
|
searchPaths.AppendElement(resPath);
|
|
|
|
}
|
|
|
|
WIN32_FIND_DATAW results;
|
|
|
|
for (PRUint32 i = 0; i < searchPaths.Length(); i++) {
|
|
|
|
const nsString& path(searchPaths[i]);
|
|
|
|
for (PRUint32 j = 0; j < fontPatterns.Length(); j++) {
|
|
|
|
nsAutoString pattern(path);
|
|
|
|
pattern.Append(fontPatterns[j]);
|
|
|
|
HANDLE handle = FindFirstFileExW(pattern.get(),
|
|
|
|
FindExInfoStandard,
|
|
|
|
&results,
|
|
|
|
FindExSearchNameMatch,
|
|
|
|
NULL,
|
|
|
|
0);
|
|
|
|
PRBool moreFiles = handle != INVALID_HANDLE_VALUE;
|
|
|
|
while (moreFiles) {
|
|
|
|
nsAutoString filePath(path);
|
|
|
|
filePath.AppendLiteral("\\");
|
|
|
|
filePath.Append(results.cFileName);
|
2011-09-23 04:15:36 -07:00
|
|
|
AppendFacesFromFontFile(NS_ConvertUTF16toUTF8(filePath));
|
2009-10-07 07:13:40 -07:00
|
|
|
moreFiles = FindNextFile(handle, &results);
|
|
|
|
}
|
|
|
|
if (handle != INVALID_HANDLE_VALUE)
|
|
|
|
FindClose(handle);
|
|
|
|
}
|
|
|
|
}
|
2010-07-15 23:03:45 -07:00
|
|
|
#elif defined(ANDROID)
|
|
|
|
gfxFontCache *fc = gfxFontCache::GetCache();
|
|
|
|
if (fc)
|
|
|
|
fc->AgeAllGenerations();
|
|
|
|
mPrefFonts.Clear();
|
|
|
|
mCodepointsWithNoFonts.reset();
|
|
|
|
|
2011-09-23 04:15:36 -07:00
|
|
|
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<FontListEntry> 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");
|
|
|
|
}
|
|
|
|
|
2010-07-15 23:03:45 -07:00
|
|
|
struct dirent *ent = NULL;
|
2011-09-23 04:15:36 -07:00
|
|
|
while ((ent = readdir(d)) != NULL) {
|
2010-07-15 23:03:45 -07:00
|
|
|
int namelen = strlen(ent->d_name);
|
2011-09-23 04:15:36 -07:00
|
|
|
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)
|
2010-07-15 23:03:45 -07:00
|
|
|
{
|
2011-09-23 04:15:36 -07:00
|
|
|
bool isStdFont = false;
|
|
|
|
for (unsigned int i = 0;
|
|
|
|
i < NS_ARRAY_LENGTH(sStandardFonts) && !isStdFont; i++)
|
|
|
|
{
|
|
|
|
isStdFont = strcmp(sStandardFonts[i], ent->d_name) == 0;
|
|
|
|
}
|
2010-07-15 23:03:45 -07:00
|
|
|
|
2011-09-23 04:15:36 -07:00
|
|
|
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);
|
2010-07-15 23:03:45 -07:00
|
|
|
}
|
|
|
|
}
|
2011-09-23 04:15:36 -07:00
|
|
|
closedir(d);
|
2011-09-23 01:53:13 -07:00
|
|
|
|
2011-09-23 04:15:36 -07:00
|
|
|
// 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);
|
2011-09-23 02:36:16 -07:00
|
|
|
#endif // XP_WIN && ANDROID
|
2011-09-23 01:53:13 -07:00
|
|
|
}
|
|
|
|
|
2011-09-23 04:15:36 -07:00
|
|
|
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<gfxFontFamily>& aFamily,
|
|
|
|
void* aUserArg)
|
|
|
|
{
|
|
|
|
InfallibleTArray<FontListEntry>* fontlist =
|
|
|
|
reinterpret_cast<InfallibleTArray<FontListEntry>*>(aUserArg);
|
|
|
|
|
|
|
|
FontFamily *family = static_cast<FontFamily*>(aFamily.get());
|
|
|
|
family->AddFacesToFontList(fontlist);
|
|
|
|
|
|
|
|
return PL_DHASH_NEXT;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gfxFT2FontList::GetFontList(InfallibleTArray<FontListEntry>* retValue)
|
|
|
|
{
|
|
|
|
mFontFamilies.Enumerate(AddFamilyToFontList, retValue);
|
|
|
|
}
|
|
|
|
|
2010-11-08 03:02:27 -08:00
|
|
|
nsresult
|
2009-10-07 07:13:40 -07:00
|
|
|
gfxFT2FontList::InitFontList()
|
|
|
|
{
|
2010-01-28 17:41:25 -08:00
|
|
|
// reset font lists
|
|
|
|
gfxPlatformFontList::InitFontList();
|
|
|
|
|
2009-10-07 07:13:40 -07:00
|
|
|
FindFonts();
|
2010-11-08 03:02:27 -08:00
|
|
|
|
|
|
|
return NS_OK;
|
2009-10-07 07:13:40 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
struct FullFontNameSearch {
|
|
|
|
FullFontNameSearch(const nsAString& aFullName)
|
|
|
|
: mFullName(aFullName), mFontEntry(nsnull)
|
|
|
|
{ }
|
|
|
|
|
|
|
|
nsString mFullName;
|
|
|
|
gfxFontEntry *mFontEntry;
|
|
|
|
};
|
|
|
|
|
|
|
|
// callback called for each family name, based on the assumption that the
|
|
|
|
// first part of the full name is the family name
|
|
|
|
static PLDHashOperator
|
|
|
|
FindFullName(nsStringHashKey::KeyType aKey,
|
|
|
|
nsRefPtr<gfxFontFamily>& aFontFamily,
|
|
|
|
void* userArg)
|
|
|
|
{
|
|
|
|
FullFontNameSearch *data = reinterpret_cast<FullFontNameSearch*>(userArg);
|
|
|
|
|
|
|
|
// does the family name match up to the length of the family name?
|
|
|
|
const nsString& family = aFontFamily->Name();
|
|
|
|
|
|
|
|
nsString fullNameFamily;
|
|
|
|
data->mFullName.Left(fullNameFamily, family.Length());
|
|
|
|
|
|
|
|
// if so, iterate over faces in this family to see if there is a match
|
|
|
|
if (family.Equals(fullNameFamily)) {
|
|
|
|
nsTArray<nsRefPtr<gfxFontEntry> >& fontList = aFontFamily->GetFontList();
|
|
|
|
int index, len = fontList.Length();
|
|
|
|
for (index = 0; index < len; index++) {
|
|
|
|
if (fontList[index]->Name().Equals(data->mFullName)) {
|
|
|
|
data->mFontEntry = fontList[index];
|
|
|
|
return PL_DHASH_STOP;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return PL_DHASH_NEXT;
|
|
|
|
}
|
|
|
|
|
|
|
|
gfxFontEntry*
|
|
|
|
gfxFT2FontList::LookupLocalFont(const gfxProxyFontEntry *aProxyEntry,
|
2011-09-23 02:34:42 -07:00
|
|
|
const nsAString& aFontName)
|
2009-10-07 07:13:40 -07:00
|
|
|
{
|
|
|
|
// walk over list of names
|
|
|
|
FullFontNameSearch data(aFontName);
|
|
|
|
|
|
|
|
mFontFamilies.Enumerate(FindFullName, &data);
|
|
|
|
|
|
|
|
return data.mFontEntry;
|
|
|
|
}
|
|
|
|
|
|
|
|
gfxFontEntry*
|
|
|
|
gfxFT2FontList::GetDefaultFont(const gfxFontStyle* aStyle, PRBool& aNeedsBold)
|
|
|
|
{
|
|
|
|
#ifdef XP_WIN
|
|
|
|
HGDIOBJ hGDI = ::GetStockObject(SYSTEM_FONT);
|
|
|
|
LOGFONTW logFont;
|
|
|
|
if (hGDI && ::GetObjectW(hGDI, sizeof(logFont), &logFont)) {
|
|
|
|
nsAutoString resolvedName;
|
|
|
|
if (ResolveFontName(nsDependentString(logFont.lfFaceName), resolvedName)) {
|
|
|
|
return FindFontForFamily(resolvedName, aStyle, aNeedsBold);
|
|
|
|
}
|
|
|
|
}
|
2010-07-15 23:03:45 -07:00
|
|
|
#elif defined(ANDROID)
|
|
|
|
nsAutoString resolvedName;
|
2011-09-15 00:40:17 -07:00
|
|
|
if (ResolveFontName(NS_LITERAL_STRING("Droid Sans"), resolvedName))
|
2010-07-15 23:03:45 -07:00
|
|
|
return FindFontForFamily(resolvedName, aStyle, aNeedsBold);
|
2009-10-07 07:13:40 -07:00
|
|
|
#endif
|
|
|
|
/* TODO: what about Qt or other platforms that may use this? */
|
|
|
|
return nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
gfxFontEntry*
|
|
|
|
gfxFT2FontList::MakePlatformFont(const gfxProxyFontEntry *aProxyEntry,
|
|
|
|
const PRUint8 *aFontData,
|
|
|
|
PRUint32 aLength)
|
|
|
|
{
|
|
|
|
// The FT2 font needs the font data to persist, so we do NOT free it here
|
|
|
|
// but instead pass ownership to the font entry.
|
|
|
|
// Deallocation will happen later, when the font face is destroyed.
|
2011-09-23 02:34:42 -07:00
|
|
|
return FontEntry::CreateFontEntry(*aProxyEntry, aFontData, aLength);
|
2009-10-07 07:13:40 -07:00
|
|
|
}
|