mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-29 07:42:04 +00:00
b=449356 font selection through Mozilla's PangoFcFontMap, r=roc
This commit is contained in:
parent
9b54f9e4af
commit
159005ad48
@ -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 */
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user