b=449356 font selection through Mozilla's PangoFcFontMap, r=roc

This commit is contained in:
Karl Tomlinson 2008-11-07 09:39:06 +13:00
parent 9b54f9e4af
commit 159005ad48
5 changed files with 1542 additions and 706 deletions

View File

@ -43,7 +43,10 @@
#include "gfxTypes.h"
#include "gfxFont.h"
#include "nsAutoRef.h"
#include <pango/pango.h>
#include <fontconfig/fontconfig.h>
// Control when we bypass Pango
// Enable this to use FreeType to glyph-convert 8bit-only textruns, but use Pango
@ -54,21 +57,7 @@
// anything other than simple Latin work though!
//#define ENABLE_FAST_PATH_ALWAYS
#include "nsDataHashtable.h"
#include "nsClassHashtable.h"
class gfxPangoTextRun;
// stub class until fuller implementation is flushed out
class gfxPangoFontEntry : public gfxFontEntry {
public:
gfxPangoFontEntry(const nsAString& aName)
: gfxFontEntry(aName)
{ }
~gfxPangoFontEntry() {}
};
class gfxFcPangoFontSet;
class THEBES_API gfxPangoFontGroup : public gfxFontGroup {
public:
@ -89,9 +78,33 @@ public:
static void Shutdown();
// Interfaces used internally
// (but public so that they can be accessed from non-member functions):
// The FontGroup holds the reference to the PangoFont (through the FontSet).
PangoFont *GetBasePangoFont();
// A language guessed from the gfxFontStyle
PangoLanguage *GetPangoLanguage() { return mPangoLanguage; }
// @param aLang [in] language to use for pref fonts and system default font
// selection, or NULL for the language guessed from the gfxFontStyle.
// The FontGroup holds a reference to this set.
gfxFcPangoFontSet *GetFontSet(PangoLanguage *aLang = NULL);
protected:
PangoFont *mBasePangoFont;
gfxFloat mAdjustedSize;
class FontSetByLangEntry {
public:
FontSetByLangEntry(PangoLanguage *aLang, gfxFcPangoFontSet *aFontSet);
PangoLanguage *mLang;
nsRefPtr<gfxFcPangoFontSet> mFontSet;
};
// There is only one of entry in this array unless characters from scripts
// of other languages are measured.
nsAutoTArray<FontSetByLangEntry,1> mFontSets;
gfxFloat mSizeAdjustFactor;
PangoLanguage *mPangoLanguage;
// ****** Textrun glyph conversion helpers ******
@ -123,55 +136,24 @@ protected:
const gchar *aUTF8, PRUint32 aUTF8Length);
#endif
void GetFcFamilies(nsAString &aFcFamilies);
PangoFont *GetBasePangoFont();
void GetFcFamilies(nsStringArray *aFcFamilyList,
const nsACString& aLangGroup);
// Check GetStyle()->sizeAdjust != 0.0 before calling this
gfxFloat GetAdjustedSize()
// @param aLang [in] language to use for pref fonts and system font
// resolution, or NULL to guess a language from the gfxFontStyle.
// @param aMatchPattern [out] if non-NULL, will return the pattern used.
already_AddRefed<gfxFcPangoFontSet>
MakeFontSet(PangoLanguage *aLang, gfxFloat aSizeAdjustFactor,
nsAutoRef<FcPattern> *aMatchPattern = NULL);
gfxFcPangoFontSet *GetBaseFontSet();
gfxFloat GetSizeAdjustFactor()
{
if (!mBasePangoFont)
GetBasePangoFont();
return mAdjustedSize;
if (mFontSets.Length() == 0)
GetBaseFontSet();
return mSizeAdjustFactor;
}
};
class gfxPangoFontWrapper {
public:
gfxPangoFontWrapper(PangoFont *aFont) {
mFont = aFont;
g_object_ref(mFont);
}
~gfxPangoFontWrapper() {
if (mFont)
g_object_unref(mFont);
}
PangoFont* Get() { return mFont; }
private:
PangoFont *mFont;
};
class gfxPangoFontCache
{
public:
gfxPangoFontCache();
~gfxPangoFontCache();
static gfxPangoFontCache* GetPangoFontCache() {
if (!sPangoFontCache)
sPangoFontCache = new gfxPangoFontCache();
return sPangoFontCache;
}
static void Shutdown() {
if (sPangoFontCache)
delete sPangoFontCache;
sPangoFontCache = nsnull;
}
void Put(const PangoFontDescription *aFontDesc, PangoFont *aPangoFont);
PangoFont* Get(const PangoFontDescription *aFontDesc);
private:
static gfxPangoFontCache *sPangoFontCache;
nsClassHashtable<nsUint32HashKey, gfxPangoFontWrapper> mPangoFonts;
};
#endif /* GFX_PANGOFONTS_H */

View File

@ -40,6 +40,7 @@
#define GFX_PLATFORM_GTK_H
#include "gfxPlatform.h"
#include "nsAutoRef.h"
extern "C" {
typedef struct _GdkDrawable GdkDrawable;
@ -52,7 +53,12 @@ class FontEntry;
typedef struct FT_LibraryRec_ *FT_Library;
#endif
template <class T>
class gfxGObjectRefTraits : public nsPointerRefTraits<T> {
public:
static void Release(T *aPtr) { g_object_unref(aPtr); }
static void AddRef(T *aPtr) { g_object_ref(aPtr); }
};
class THEBES_API gfxPlatformGtk : public gfxPlatform {
public:

View File

@ -77,6 +77,17 @@ gfxFontconfigUtils::GetThebesStyle(FcPattern *aPattern)
return FONT_STYLE_NORMAL;
}
/* static */ int
gfxFontconfigUtils::GetFcSlant(const gfxFontStyle& aFontStyle)
{
if (aFontStyle.style == FONT_STYLE_ITALIC)
return FC_SLANT_ITALIC;
if (aFontStyle.style == FONT_STYLE_OBLIQUE)
return FC_SLANT_OBLIQUE;
return FC_SLANT_ROMAN;
}
// OS/2 weight classes were introduced in fontconfig-2.1.93 (2003).
#ifndef FC_WEIGHT_THIN
#define FC_WEIGHT_THIN 0 // 2.1.93
@ -88,6 +99,10 @@ gfxFontconfigUtils::GetThebesStyle(FcPattern *aPattern)
#ifndef FC_WEIGHT_BOOK
#define FC_WEIGHT_BOOK 75
#endif
// extra black was introduced in fontconfig-2.4.91 (2007)
#ifndef FC_WEIGHT_EXTRABLACK
#define FC_WEIGHT_EXTRABLACK 215
#endif
/* static */ PRUint16
gfxFontconfigUtils::GetThebesWeight(FcPattern *aPattern)
@ -116,14 +131,134 @@ gfxFontconfigUtils::GetThebesWeight(FcPattern *aPattern)
if (weight <= FC_WEIGHT_BLACK)
return 900;
// FC_WEIGHT_EXTRABLACK was introduced in fontconfig-2.4.91 (2007)
// including FC_WEIGHT_EXTRABLACK
return 901;
}
/* static */ int
gfxFontconfigUtils::FcWeightForBaseWeight(PRInt8 aBaseWeight)
{
switch (aBaseWeight) {
case 2:
return FC_WEIGHT_EXTRALIGHT;
case 3:
return FC_WEIGHT_LIGHT;
case 4:
return FC_WEIGHT_REGULAR;
case 5:
return FC_WEIGHT_MEDIUM;
case 6:
return FC_WEIGHT_DEMIBOLD;
case 7:
return FC_WEIGHT_BOLD;
case 8:
return FC_WEIGHT_EXTRABOLD;
case 9:
return FC_WEIGHT_BLACK;
}
// extremes
return aBaseWeight < 2 ? FC_WEIGHT_THIN : FC_WEIGHT_EXTRABLACK;
}
// This makes a guess at an FC_WEIGHT corresponding to a base weight and
// offset (without any knowledge of which weights are available).
/* static */ int
GuessFcWeight(const gfxFontStyle& aFontStyle)
{
/*
* weights come in two parts crammed into one
* integer -- the "base" weight is weight / 100,
* the rest of the value is the "offset" from that
* weight -- the number of steps to move to adjust
* the weight in the list of supported font weights,
* this value can be negative or positive.
*/
PRInt8 weight;
PRInt8 offset;
aFontStyle.ComputeWeightAndOffset(&weight, &offset);
// ComputeWeightAndOffset trimmed the range of weights for us
NS_ASSERTION(weight >= 0 && weight <= 10,
"base weight out of range");
// Most font families do not support every weight. The tables here are
// chosen such that a normal (4) base weight and an offset of +1 will
// guess bold.
// Mapping from weight to a guess of the nearest available lighter weight
static const int lighterGuess[11] =
{ 0, 0, 1, 1, 2, 3, 4, 4, 6, 7, 8 };
// Mapping from weight to a guess of the nearest available bolder weight
static const int bolderGuess[11] =
{ 2, 3, 4, 6, 7, 7, 8, 9, 10, 10, 10 };
while (offset < 0) {
weight = lighterGuess[weight];
offset++;
}
while (offset > 0) {
weight = bolderGuess[weight];
offset--;
}
return gfxFontconfigUtils::FcWeightForBaseWeight(weight);
}
static void
AddString(FcPattern *aPattern, const char *object, const char *aString)
{
// Cast from signed chars used in nsString to unsigned in fontconfig
const FcChar8 *fcString = gfxFontconfigUtils::ToFcChar8(aString);
// and cast away the const for fontconfig, that will merely make a copy.
FcPatternAddString(aPattern, object, const_cast<FcChar8*>(fcString));
}
static void
AddLangGroup(FcPattern *aPattern, const nsACString& aLangGroup)
{
// Translate from mozilla's internal mapping into fontconfig's
nsCAutoString lang;
gfxFontconfigUtils::GetSampleLangForGroup(aLangGroup, &lang);
if (!lang.IsEmpty()) {
AddString(aPattern, FC_LANG, lang.get());
}
}
nsReturnRef<FcPattern>
gfxFontconfigUtils::NewPattern(const nsStringArray& aFamilies,
const gfxFontStyle& aFontStyle,
const char *aLang)
{
nsAutoRef<FcPattern> pattern(FcPatternCreate());
if (!pattern)
return nsReturnRef<FcPattern>();
FcPatternAddDouble(pattern, FC_PIXEL_SIZE, aFontStyle.size);
FcPatternAddInteger(pattern, FC_SLANT, GetFcSlant(aFontStyle));
FcPatternAddInteger(pattern, FC_WEIGHT, GuessFcWeight(aFontStyle));
if (aLang) {
AddString(pattern, FC_LANG, aLang);
}
for (PRInt32 i = 0; i < aFamilies.Count(); ++i) {
NS_ConvertUTF16toUTF8 family(*aFamilies[i]);
AddString(pattern, FC_FAMILY, family.get());
}
return pattern.out();
}
gfxFontconfigUtils::gfxFontconfigUtils()
: mLastConfig(NULL)
{
mAliasTable.Init(50);
mFontsByFamily.Init(50);
mLangSupportTable.Init(20);
UpdateFontListInternal();
}
nsresult
@ -133,22 +268,15 @@ gfxFontconfigUtils::GetFontList(const nsACString& aLangGroup,
{
aListOfFonts.Clear();
nsresult rv = UpdateFontListInternal();
nsCStringArray fonts;
nsresult rv = GetFontListInternal(fonts, aLangGroup);
if (NS_FAILED(rv))
return rv;
nsCStringArray tmpFonts;
nsCStringArray *fonts = &mFonts;
if (!aLangGroup.IsEmpty() || !aGenericFamily.IsEmpty()) {
rv = GetFontListInternal(tmpFonts, &aLangGroup);
if (NS_FAILED(rv))
return rv;
fonts = &tmpFonts;
for (PRInt32 i = 0; i < fonts.Count(); ++i) {
aListOfFonts.AppendString(NS_ConvertUTF8toUTF16(*fonts.CStringAt(i)));
}
for (PRInt32 i = 0; i < fonts->Count(); ++i)
aListOfFonts.AppendString(NS_ConvertUTF8toUTF16(*fonts->CStringAt(i)));
aListOfFonts.Sort();
PRInt32 serif = 0, sansSerif = 0, monospace = 0;
@ -254,7 +382,7 @@ gfxFontconfigUtils::GetSampleLangForGroup(const nsACString& aLangGroup,
const MozLangGroupData *langGroup = nsnull;
for (unsigned int i=0; i < NS_ARRAY_LENGTH(MozLangGroups); ++i) {
for (unsigned int i = 0; i < NS_ARRAY_LENGTH(MozLangGroups); ++i) {
if (aLangGroup.Equals(MozLangGroups[i].mozLangGroup,
nsCaseInsensitiveCStringComparator())) {
langGroup = &MozLangGroups[i];
@ -309,25 +437,9 @@ gfxFontconfigUtils::GetSampleLangForGroup(const nsACString& aLangGroup,
}
}
static void
AddLangGroup(FcPattern *aPattern, const nsACString& aLangGroup)
{
// Translate from mozilla's internal mapping into fontconfig's
nsCAutoString lang;
gfxFontconfigUtils::GetSampleLangForGroup(aLangGroup, &lang);
if (!lang.IsEmpty()) {
// cast from signed chars used in nsString to unsigned in fontconfig
const FcChar8 *fcString = reinterpret_cast<const FcChar8*>(lang.get());
// and cast away the const for fontconfig, that will merely make a copy.
FcPatternAddString(aPattern, FC_LANG, const_cast<FcChar8*>(fcString));
}
}
nsresult
gfxFontconfigUtils::GetFontListInternal(nsCStringArray& aListOfFonts,
const nsACString *aLangGroup)
const nsACString& aLangGroup)
{
FcPattern *pat = NULL;
FcObjectSet *os = NULL;
@ -345,8 +457,8 @@ gfxFontconfigUtils::GetFontListInternal(nsCStringArray& aListOfFonts,
goto end;
// take the pattern and add the lang group to it
if (aLangGroup && !aLangGroup->IsEmpty()) {
AddLangGroup(pat, *aLangGroup);
if (!aLangGroup.IsEmpty()) {
AddLangGroup(pat, aLangGroup);
}
fs = FcFontList(NULL, pat, os);
@ -413,16 +525,38 @@ gfxFontconfigUtils::UpdateFontListInternal(PRBool aForce)
if (currentConfig == mLastConfig)
return NS_OK;
mFonts.Clear();
mAliasForSingleFont.Clear();
// This FcFontSet is owned by fontconfig
FcFontSet *fontSet = FcConfigGetFonts(currentConfig, FcSetSystem);
mFontsByFamily.Clear();
mLangSupportTable.Clear();
mAliasForMultiFonts.Clear();
mNonExistingFonts.Clear();
mAliasTable.Clear();
// Record the existing font families
for (int f = 0; f < fontSet->nfont; ++f) {
FcPattern *font = fontSet->fonts[f];
nsresult rv = GetFontListInternal(mFonts);
if (NS_FAILED(rv))
return rv;
FcChar8 *family;
for (int v = 0;
FcPatternGetString(font, FC_FAMILY, v, &family) == FcResultMatch;
++v) {
FontsByFcStrEntry *entry = mFontsByFamily.PutEntry(family);
if (entry) {
PRBool added = entry->AddFont(font);
if (!entry->mKey) {
// The reference to the font pattern keeps the pointer to
// string for the key valid. If adding the font failed
// then the entry must be removed.
if (added) {
entry->mKey = family;
} else {
mFontsByFamily.RawRemoveEntry(entry);
}
}
}
}
}
// XXX we don't support all alias names.
// Because if we don't check whether the given font name is alias name,
@ -463,72 +597,10 @@ gfxFontconfigUtils::UpdateFontListInternal(PRBool aForce)
}
}
for (PRInt32 i = 0; i < mAliasForMultiFonts.Count(); i++) {
nsRefPtr<gfxFontNameList> fonts = new gfxFontNameList;
nsCAutoString fontname(*mAliasForMultiFonts.CStringAt(i));
rv = GetResolvedFonts(fontname, fonts);
if (NS_FAILED(rv))
return rv;
nsCAutoString key;
ToLowerCase(fontname, key);
mAliasTable.Put(key, fonts);
}
mLastConfig = currentConfig;
return NS_OK;
}
nsresult
gfxFontconfigUtils::GetResolvedFonts(const nsACString& aName,
gfxFontNameList* aResult)
{
FcPattern *pat = NULL;
FcFontSet *fs = NULL;
FcResult fresult;
aResult->Clear();
nsresult rv = NS_ERROR_FAILURE;
pat = FcPatternCreate();
if (!pat)
goto end;
FcDefaultSubstitute(pat);
FcPatternAddString(pat, FC_FAMILY,
(FcChar8 *)nsPromiseFlatCString(aName).get());
// Delete the lang param. We need lang independent alias list.
FcPatternDel(pat, FC_LANG);
FcConfigSubstitute(NULL, pat, FcMatchPattern);
fs = FcFontSort(NULL, pat, FcTrue, NULL, &fresult);
if (!fs)
goto end;
rv = NS_OK;
for (int i = 0; i < fs->nfont; i++) {
char *family;
if (FcPatternGetString(fs->fonts[i], FC_FAMILY, 0,
(FcChar8 **) &family) != FcResultMatch ||
mAliasForMultiFonts.IndexOfIgnoreCase(nsDependentCString(family)) >= 0 ||
IsExistingFont(nsDependentCString(family)) == 0)
{
continue;
}
NS_ConvertUTF8toUTF16 actualName(family);
if (aResult->Exists(actualName))
continue;
aResult->AppendElement(actualName);
}
end:
if (pat)
FcPatternDestroy(pat);
if (fs)
FcFontSetDestroy(fs);
return rv;
}
nsresult
gfxFontconfigUtils::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName)
{
@ -548,12 +620,8 @@ gfxFontconfigUtils::GetStandardFamilyName(const nsAString& aFontName, nsAString&
NS_ConvertUTF16toUTF8 fontname(aFontName);
if (mFonts.IndexOf(fontname) >= 0) {
aFamilyName.Assign(aFontName);
return NS_OK;
}
if (mNonExistingFonts.IndexOf(fontname) >= 0)
// return empty string if no such family exists
if (!IsExistingFamily(fontname))
return NS_OK;
FcPattern *pat = NULL;
@ -655,87 +723,203 @@ gfxFontconfigUtils::ResolveFontName(const nsAString& aFontName,
return rv;
NS_ConvertUTF16toUTF8 fontname(aFontName);
if (mAliasForMultiFonts.IndexOfIgnoreCase(fontname) >= 0) {
nsCAutoString key;
ToLowerCase(fontname, key);
nsRefPtr<gfxFontNameList> fonts;
if (!mAliasTable.Get(key, &fonts))
NS_ERROR("The mAliasTable was broken!");
for (PRUint32 i = 0; i < fonts->Length(); i++) {
aAborted = !(*aCallback)(fonts->ElementAt(i), aClosure);
if (aAborted)
break;
}
} else {
PRInt32 result = IsExistingFont(fontname);
if (result < 0)
return NS_ERROR_FAILURE;
if (result > 0)
aAborted = !(*aCallback)(aFontName, aClosure);
}
// Sometimes, the font has two or more names (e.g., "Sazanami Gothic" has
// Japanese localized name). We should not resolve to a single name
// because different names sometimes have different behavior. e.g., with
// the default settings of "Sazanami" on Fedora Core 5, the non-localized
// name uses anti-alias, but the localized name uses it. So, we should
// check just whether the font is existing, without resolving to regular
// name.
//
// The family names in mAliasForMultiFonts are names understood by
// fontconfig. The actual font to which they resolve depends on the
// entire match pattern. That info is not available here, but there
// will be a font so leave the resolving to the gfxFontGroup.
if (IsExistingFamily(fontname) ||
mAliasForMultiFonts.IndexOfIgnoreCase(fontname))
aAborted = !(*aCallback)(aFontName, aClosure);
return NS_OK;
}
PRInt32
gfxFontconfigUtils::IsExistingFont(const nsACString &aFontName)
PRBool
gfxFontconfigUtils::IsExistingFamily(const nsCString& aFamilyName)
{
// Very many sites may specify the font-family only for Windows and Mac.
// We should check negative cache at first.
if (mNonExistingFonts.IndexOf(aFontName) >= 0)
return 0;
if (mAliasForSingleFont.IndexOf(aFontName) >= 0)
return 1;
if (mFonts.IndexOf(aFontName) >= 0)
return 1;
return mFontsByFamily.GetEntry(ToFcChar8(aFamilyName.get())) != nsnull;
}
// XXX Sometimes, the font has two or more names (e.g., "Sazanami Gothic"
// has Japanese localized name). The another name doesn't including the
// cache. Therefore, we need to check the name.
// But we don't need to resolve the name. Because both names are not same
// behavior. E.g., the default settings of "Sazanami" on Fedora Core 5,
// the non-localized name uses Anti-alias, but the localized name uses it.
// So, we should check just whether the font is existing, don't resolve
// to regular name.
const nsTArray< nsCountedRef<FcPattern> >&
gfxFontconfigUtils::GetFontsForFamily(const FcChar8 *aFamilyName)
{
FontsByFcStrEntry *entry = mFontsByFamily.GetEntry(aFamilyName);
FcPattern *pat = NULL;
FcObjectSet *os = NULL;
FcFontSet *fs = NULL;
PRInt32 result = -1;
if (!entry)
return mEmptyPatternArray;
pat = FcPatternCreate();
if (!pat)
goto end;
return entry->GetFonts();
}
FcPatternAddString(pat, FC_FAMILY,
(FcChar8 *)nsPromiseFlatCString(aFontName).get());
static FcLangResult
CompareLangString(const FcChar8 *aLangA, const FcChar8 *aLangB) {
FcLangResult result = FcLangDifferentLang;
for (PRUint32 i = 0; ; ++i) {
FcChar8 a = FcToLower(aLangA[i]);
FcChar8 b = FcToLower(aLangB[i]);
os = FcObjectSetBuild(FC_FAMILY, NULL);
if (!os)
goto end;
if (a != b) {
if ((a == '\0' && b == '-') || (a == '-' && b == '\0'))
return FcLangDifferentCountry;
fs = FcFontList(NULL, pat, os);
if (!fs)
goto end;
return result;
}
if (a == '\0')
return FcLangEqual;
// There can be more than one matching set of family names: see bug 393819.
if (fs->nfont > 0) {
mAliasForSingleFont.AppendCString(aFontName);
result = 1;
} else {
mNonExistingFonts.AppendCString(aFontName);
result = 0;
if (a == '-') {
result = FcLangDifferentCountry;
}
}
}
/* static */
FcLangResult
gfxFontconfigUtils::GetLangSupport(FcPattern *aFont, const FcChar8 *aLang)
{
// When fontconfig builds a pattern for the font, it will set a single
// LangSet property value for the font. That value may be removed and
// additional string values may be added through FcConfigSubsitute with
// FcMatchScan. Values that are neither LangSet nor string are considered
// errors in fontconfig sort and match functions.
FcValue value;
FcLangResult best = FcLangDifferentLang;
int v = 0;
while (FcPatternGet(aFont, FC_LANG, v, &value) == FcResultMatch) {
++v;
FcLangResult support;
switch (value.type) {
case FcTypeLangSet:
support = FcLangSetHasLang(value.u.l, aLang);
break;
case FcTypeString:
support = CompareLangString(value.u.s, aLang);
break;
default:
// error
return FcLangEqual;
}
if (support < best) { // lower is better
if (support == FcLangEqual)
return support;
best = support;
}
}
end:
if (pat)
FcPatternDestroy(pat);
if (os)
FcObjectSetDestroy(os);
if (fs)
FcFontSetDestroy(fs);
return result;
// A missing FC_LANG property is considered a match in fontconfig sort
// and match functions.
if (v == 0)
return FcLangEqual;
return best;
}
gfxFontconfigUtils::LangSupportEntry *
gfxFontconfigUtils::GetLangSupportEntry(const FcChar8 *aLang, PRBool aWithFonts)
{
// Currently any unrecognized languages from documents will be converted
// to x-unicode by nsILanguageAtomService, so there is a limit on the
// langugages that will be added here. Reconsider when/if document
// languages are passed to this routine.
LangSupportEntry *entry = mLangSupportTable.PutEntry(aLang);
if (!entry)
return nsnull;
FcLangResult best = FcLangDifferentLang;
if (!entry->IsKeyInitialized()) {
entry->InitKey(aLang);
} else {
// mSupport is already initialized.
if (!aWithFonts)
return entry;
best = entry->mSupport;
// If there is support for this language, an empty font list indicates
// that the list hasn't been initialized yet.
if (best == FcLangDifferentLang || entry->mFonts.Length() > 0)
return entry;
}
// This FcFontSet is owned by fontconfig
FcFontSet *fontSet = FcConfigGetFonts(NULL, FcSetSystem);
nsAutoTArray<FcPattern*,100> fonts;
for (int f = 0; f < fontSet->nfont; ++f) {
FcPattern *font = fontSet->fonts[f];
FcLangResult support = GetLangSupport(font, aLang);
if (support < best) { // lower is better
best = support;
if (aWithFonts) {
fonts.Clear();
} else if (best == FcLangEqual) {
break;
}
}
// The font list in the LangSupportEntry is expected to be used only
// when no default fonts support the language. There would be a large
// number of fonts in entries for languages using Latin script but
// these do not need to be created because default fonts already
// support these languages.
if (aWithFonts && support != FcLangDifferentLang && support == best) {
fonts.AppendElement(font);
}
}
entry->mSupport = best;
if (aWithFonts) {
if (fonts.Length() != 0) {
entry->mFonts.AppendElements(fonts.Elements(), fonts.Length());
} else if (best != FcLangDifferentLang) {
// Previously there was a font that supported this language at the
// level of entry->mSupport, but it has now disappeared. At least
// entry->mSupport needs to be recalculated, but this is an
// indication that the set of installed fonts has changed, so
// update all caches.
mLastConfig = NULL; // invalidates caches
UpdateFontListInternal(PR_TRUE);
return GetLangSupportEntry(aLang, aWithFonts);
}
}
return entry;
}
FcLangResult
gfxFontconfigUtils::GetBestLangSupport(const FcChar8 *aLang)
{
UpdateFontListInternal();
LangSupportEntry *entry = GetLangSupportEntry(aLang, PR_FALSE);
if (!entry)
return FcLangEqual;
return entry->mSupport;
}
const nsTArray< nsCountedRef<FcPattern> >&
gfxFontconfigUtils::GetFontsForLang(const FcChar8 *aLang)
{
LangSupportEntry *entry = GetLangSupportEntry(aLang, PR_TRUE);
if (!entry)
return mEmptyPatternArray;
return entry->mFonts;
}
PRBool
@ -746,4 +930,3 @@ gfxFontNameList::Exists(nsAString& aName) {
}
return PR_FALSE;
}

View File

@ -20,6 +20,7 @@
*
* Contributor(s):
* Masayuki Nakano <masayuki@d-toybox.com>
* Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
*
* 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
@ -40,15 +41,40 @@
#include "gfxPlatform.h"
#include "nsAutoRef.h"
#include "nsTArray.h"
#include "nsDataHashtable.h"
#include "nsTHashtable.h"
#include <fontconfig/fontconfig.h>
NS_SPECIALIZE_TEMPLATE
class nsAutoRefTraits<FcPattern> : public nsPointerRefTraits<FcPattern>
{
public:
static void Release(FcPattern *ptr) { FcPatternDestroy(ptr); }
static void AddRef(FcPattern *ptr) { FcPatternReference(ptr); }
};
NS_SPECIALIZE_TEMPLATE
class nsAutoRefTraits<FcFontSet> : public nsPointerRefTraits<FcFontSet>
{
public:
static void Release(FcFontSet *ptr) { FcFontSetDestroy(ptr); }
};
NS_SPECIALIZE_TEMPLATE
class nsAutoRefTraits<FcCharSet> : public nsPointerRefTraits<FcCharSet>
{
public:
static void Release(FcCharSet *ptr) { FcCharSetDestroy(ptr); }
};
class gfxFontNameList : public nsTArray<nsString>
{
public:
THEBES_INLINE_DECL_REFCOUNTING(gfxFontList)
THEBES_INLINE_DECL_REFCOUNTING(gfxFontNameList)
PRBool Exists(nsAString& aName);
};
@ -76,9 +102,42 @@ public:
nsresult GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName);
const nsTArray< nsCountedRef<FcPattern> >&
GetFontsForFamily(const FcChar8 *aFamilyName);
// Returns the best support that any font offers for |aLang|.
FcLangResult GetBestLangSupport(const FcChar8 *aLang);
// Returns the fonts offering this best level of support.
const nsTArray< nsCountedRef<FcPattern> >&
GetFontsForLang(const FcChar8 *aLang);
// Retuns the language support for a fontconfig font pattern
static FcLangResult GetLangSupport(FcPattern *aFont, const FcChar8 *aLang);
// Conversions between FcChar8*, which is unsigned char*,
// and (signed) char*, that check the type of the argument.
static const FcChar8 *ToFcChar8(const char *aCharPtr)
{
return reinterpret_cast<const FcChar8*>(aCharPtr);
}
static const char *ToCString(const FcChar8 *aChar8Ptr)
{
return reinterpret_cast<const char*>(aChar8Ptr);
}
static PRUint8 GetThebesStyle(FcPattern *aPattern); // slant
static PRUint16 GetThebesWeight(FcPattern *aPattern);
static int GetFcSlant(const gfxFontStyle& aFontStyle);
// Returns a precise FC_WEIGHT from CSS weight |aBaseWeight|.
static int FcWeightForBaseWeight(PRInt8 aBaseWeight);
// This doesn't consider which faces exist, and so initializes the pattern
// using a guessed weight, and doesn't consider sizeAdjust.
static nsReturnRef<FcPattern>
NewPattern(const nsStringArray& aFamilies, const gfxFontStyle& aFontStyle,
const char *aLang);
/**
* @param aLangGroup [in] a Mozilla langGroup.
* @param aFcLang [out] returns a language suitable for fontconfig
@ -88,22 +147,133 @@ public:
nsACString *aFcLang);
protected:
// Base class for hash table entries with case-insensitive FcChar8
// string keys.
class FcStrEntryBase : public PLDHashEntryHdr {
public:
typedef const FcChar8 *KeyType;
typedef const FcChar8 *KeyTypePointer;
static KeyTypePointer KeyToPointer(KeyType aKey) { return aKey; }
// Case-insensitive hash.
//
// fontconfig always ignores case of ASCII characters in family
// names and languages, but treatment of whitespace in families is
// not consistent. FcFontSort and FcFontMatch ignore whitespace
// except for whitespace in the first character, while FcFontList
// and config subsitution tests require whitespace to match
// exactly. CSS 2.1 implies that whitespace is important in the
// font-family property. FcStrCmpIgnoreCase considers whitespace
// important.
static PLDHashNumber HashKey(const FcChar8 *aKey) {
PRUint32 hash = 0;
for (const FcChar8* c = aKey; *c != '\0'; ++c) {
hash = PR_ROTATE_LEFT32(hash, 3) ^ FcToLower(*c);
}
return hash;
}
enum { ALLOW_MEMMOVE = PR_TRUE };
};
public:
// Hash entry with a dependent const FcChar8* pointer to an external
// string for a key (and no data). The user must ensure that the string
// associated with the pointer is not destroyed. This entry type is
// useful for family name keys as the family name string is held in the
// font pattern.
class DepFcStrEntry : public FcStrEntryBase {
public:
// When constructing a new entry in the hashtable, the key is left
// blank. The caller of PutEntry() must fill in mKey when NULL. This
// provides a mechanism for the caller of PutEntry() to determine
// whether the entry has been initialized.
DepFcStrEntry(KeyTypePointer aName)
: mKey(NULL) { }
DepFcStrEntry(const DepFcStrEntry& toCopy)
: mKey(toCopy.mKey) { }
PRBool KeyEquals(KeyTypePointer aKey) const {
return FcStrCmpIgnoreCase(aKey, mKey) == 0;
}
const FcChar8 *mKey;
};
// Hash entry that uses a copy of an FcChar8 string to store the key.
// This entry type is useful for language keys, as languages are usually
// not stored as strings in font patterns.
class CopiedFcStrEntry : public FcStrEntryBase {
public:
// When constructing a new entry in the hashtable, the key is void.
// The caller of PutEntry() must call InitKey() when IsKeyInitialized()
// returns false. This provides a mechanism for the caller of
// PutEntry() to determine whether the entry has been initialized.
CopiedFcStrEntry(KeyTypePointer aName) {
mKey.SetIsVoid(PR_TRUE);
}
CopiedFcStrEntry(const CopiedFcStrEntry& toCopy)
: mKey(toCopy.mKey) { }
PRBool KeyEquals(KeyTypePointer aKey) const {
return FcStrCmpIgnoreCase(aKey, ToFcChar8(mKey.get())) == 0;
}
PRBool IsKeyInitialized() { return !mKey.IsVoid(); }
void InitKey(const FcChar8* aKey) { mKey.Assign(ToCString(aKey)); }
private:
nsCString mKey;
};
protected:
class FontsByFcStrEntry : public DepFcStrEntry {
public:
FontsByFcStrEntry(KeyTypePointer aName)
: DepFcStrEntry(aName) { }
FontsByFcStrEntry(const FontsByFcStrEntry& toCopy)
: DepFcStrEntry(toCopy), mFonts(toCopy.mFonts) { }
PRBool AddFont(FcPattern *aFont) {
return mFonts.AppendElement(aFont) != nsnull;
}
const nsTArray< nsCountedRef<FcPattern> >& GetFonts() {
return mFonts;
}
private:
nsTArray< nsCountedRef<FcPattern> > mFonts;
};
class LangSupportEntry : public CopiedFcStrEntry {
public:
LangSupportEntry(KeyTypePointer aName)
: CopiedFcStrEntry(aName) { }
LangSupportEntry(const LangSupportEntry& toCopy)
: CopiedFcStrEntry(toCopy), mSupport(toCopy.mSupport) { }
FcLangResult mSupport;
nsTArray< nsCountedRef<FcPattern> > mFonts;
};
static gfxFontconfigUtils* sUtils;
PRInt32 IsExistingFont(const nsACString& aFontName);
nsresult GetResolvedFonts(const nsACString& aName,
gfxFontNameList* aResult);
PRBool IsExistingFamily(const nsCString& aFamilyName);
nsresult GetFontListInternal(nsCStringArray& aListOfFonts,
const nsACString *aLangGroup = nsnull);
const nsACString& aLangGroup);
nsresult UpdateFontListInternal(PRBool aForce = PR_FALSE);
nsCStringArray mFonts;
nsCStringArray mNonExistingFonts;
nsCStringArray mAliasForSingleFont;
nsCStringArray mAliasForMultiFonts;
LangSupportEntry *GetLangSupportEntry(const FcChar8 *aLang,
PRBool aWithFonts);
nsDataHashtable<nsCStringHashKey, nsRefPtr<gfxFontNameList> > mAliasTable;
nsTHashtable<FontsByFcStrEntry> mFontsByFamily;
nsTHashtable<LangSupportEntry> mLangSupportTable;
const nsTArray< nsCountedRef<FcPattern> > mEmptyPatternArray;
nsCStringArray mAliasForMultiFonts;
FcConfig *mLastConfig;
};

File diff suppressed because it is too large Load Diff