Bug 1160506 - support intra-family font fallback. r=heycam

Fontconfig allows for fonts to be arbitrarily mapped into another font
family. Under some versions of Debian and Fedora, families like Droid
Sans Japanese were mapped into the Droid Sans family. To match these
font families properly, it's necessary to test multiple fonts within a
single given family. Since this is a relatively time consuming
procedure, only do this when there are multiple regular faces for a
given family.
This commit is contained in:
John Daggett 2015-11-11 21:13:33 +09:00
parent 2732ddf842
commit 8204f43ef5
4 changed files with 87 additions and 17 deletions

View File

@ -847,6 +847,7 @@ gfxFontconfigFontFamily::FindStyleVariations(FontInfoData *aFontInfoData)
// add font entries for each of the faces
uint32_t numFonts = mFontPatterns.Length();
NS_ASSERTION(numFonts, "font family containing no faces!!");
uint32_t numRegularFaces = 0;
for (uint32_t i = 0; i < numFonts; i++) {
FcPattern* face = mFontPatterns[i];
@ -859,6 +860,12 @@ gfxFontconfigFontFamily::FindStyleVariations(FontInfoData *aFontInfoData)
new gfxFontconfigFontEntry(faceName, face);
AddFontEntry(fontEntry);
if (fontEntry->IsUpright() &&
fontEntry->Weight() == NS_FONT_WEIGHT_NORMAL &&
fontEntry->Stretch() == NS_FONT_STRETCH_NORMAL) {
numRegularFaces++;
}
if (LOG_FONTLIST_ENABLED()) {
LOG_FONTLIST(("(fontlist) added (%s) to family (%s)"
" with style: %s weight: %d stretch: %d"
@ -872,6 +879,12 @@ gfxFontconfigFontFamily::FindStyleVariations(FontInfoData *aFontInfoData)
NS_ConvertUTF16toUTF8(fullname).get()));
}
}
// somewhat arbitrary, but define a family with two or more regular
// faces as a family for which intra-family fallback should be used
if (numRegularFaces > 1) {
mCheckForFallbackFaces = true;
}
mFaceNamesInitialized = true;
mFontPatterns.Clear();
SetHasStyles(true);

View File

@ -663,7 +663,8 @@ public:
mIsSimpleFamily(false),
mIsBadUnderlineFamily(false),
mFamilyCharacterMapInitialized(false),
mSkipDefaultFeatureSpaceCheck(false)
mSkipDefaultFeatureSpaceCheck(false),
mCheckForFallbackFaces(false)
{ }
const nsString& Name() { return mName; }
@ -767,6 +768,7 @@ public:
}
bool IsBadUnderlineFamily() const { return mIsBadUnderlineFamily; }
bool CheckForFallbackFaces() const { return mCheckForFallbackFaces; }
// sort available fonts to put preferred (standard) faces towards the end
void SortAvailableFonts();
@ -822,6 +824,7 @@ protected:
bool mIsBadUnderlineFamily : 1;
bool mFamilyCharacterMapInitialized : 1;
bool mSkipDefaultFeatureSpaceCheck : 1;
bool mCheckForFallbackFaces : 1; // check other faces for character
enum {
// for "simple" families, the faces are stored in mAvailableFonts

View File

@ -1648,6 +1648,12 @@ gfxFontGroup::AddFamilyToFontList(gfxFontFamily* aFamily)
mFonts.AppendElement(ff);
}
}
// for a family marked as "check fallback faces", only mark the last
// entry so that fallbacks for a family are only checked once
if (aFamily->CheckForFallbackFaces() &&
!fontEntryList.IsEmpty() && !mFonts.IsEmpty()) {
mFonts.LastElement().SetCheckForFallbackFaces();
}
}
bool
@ -2555,6 +2561,23 @@ gfxFontGroup::FindNonItalicFaceForChar(gfxFontFamily* aFamily, uint32_t aCh)
return font.forget();
}
already_AddRefed<gfxFont>
gfxFontGroup::FindFallbackFaceForChar(gfxFontFamily* aFamily, uint32_t aCh,
int32_t aRunScript)
{
GlobalFontMatch data(aCh, aRunScript, &mStyle);
aFamily->SearchAllFontsForChar(&data);
gfxFontEntry* fe = data.mBestMatch;
if (!fe) {
return nullptr;
}
bool needsBold = mStyle.weight >= 600 && !fe->IsBold() &&
mStyle.allowSyntheticWeight;
RefPtr<gfxFont> font = fe->FindOrMakeFont(&mStyle, needsBold);
return font.forget();
}
gfxFloat
gfxFontGroup::GetUnderlineOffset()
{
@ -2619,10 +2642,17 @@ gfxFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh, uint32_t aNextCh,
return firstFont.forget();
}
// If italic, test the regular face to see if it supports character.
// Only do this for platform fonts, not userfonts.
if (mStyle.style != NS_FONT_STYLE_NORMAL &&
!firstFont->GetFontEntry()->IsUserFont()) {
if (mFonts[0].CheckForFallbackFaces()) {
RefPtr<gfxFont> font =
FindFallbackFaceForChar(mFonts[0].Family(), aCh, aRunScript);
if (font) {
*aMatchType = gfxTextRange::kFontGroup;
return font.forget();
}
} else if (mStyle.style != NS_FONT_STYLE_NORMAL &&
!firstFont->GetFontEntry()->IsUserFont()) {
// If italic, test the regular face to see if it supports
// character. Only do this for platform fonts, not userfonts.
RefPtr<gfxFont> font =
FindNonItalicFaceForChar(mFonts[0].Family(), aCh);
if (font) {
@ -2722,17 +2752,30 @@ gfxFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh, uint32_t aNextCh,
}
}
// If italic, test the regular face to see if it supports the character.
// Only do this for platform fonts, not userfonts.
fe = ff.FontEntry();
if (mStyle.style != NS_FONT_STYLE_NORMAL &&
!fe->mIsUserFontContainer &&
!fe->IsUserFont()) {
font = FindNonItalicFaceForChar(ff.Family(), aCh);
// check other family faces if needed
if (ff.CheckForFallbackFaces()) {
NS_ASSERTION(i == 0 ? true :
!mFonts[i-1].CheckForFallbackFaces() ||
!mFonts[i-1].Family()->Name().Equals(ff.Family()->Name()),
"should only do fallback once per font family");
font = FindFallbackFaceForChar(ff.Family(), aCh, aRunScript);
if (font) {
*aMatchType = gfxTextRange::kFontGroup;
return font.forget();
}
} else {
// If italic, test the regular face to see if it supports the
// character. Only do this for platform fonts, not userfonts.
fe = ff.FontEntry();
if (mStyle.style != NS_FONT_STYLE_NORMAL &&
!fe->mIsUserFontContainer &&
!fe->IsUserFont()) {
font = FindNonItalicFaceForChar(ff.Family(), aCh);
if (font) {
*aMatchType = gfxTextRange::kFontGroup;
return font.forget();
}
}
}
}

View File

@ -882,12 +882,13 @@ protected:
public:
FamilyFace() : mFamily(nullptr), mFontEntry(nullptr),
mNeedsBold(false), mFontCreated(false),
mLoading(false), mInvalid(false)
mLoading(false), mInvalid(false),
mCheckForFallbackFaces(false)
{ }
FamilyFace(gfxFontFamily* aFamily, gfxFont* aFont)
: mFamily(aFamily), mNeedsBold(false), mFontCreated(true),
mLoading(false), mInvalid(false)
mLoading(false), mInvalid(false), mCheckForFallbackFaces(false)
{
NS_ASSERTION(aFont, "font pointer must not be null");
NS_ASSERTION(!aFamily ||
@ -900,7 +901,7 @@ protected:
FamilyFace(gfxFontFamily* aFamily, gfxFontEntry* aFontEntry,
bool aNeedsBold)
: mFamily(aFamily), mNeedsBold(aNeedsBold), mFontCreated(false),
mLoading(false), mInvalid(false)
mLoading(false), mInvalid(false), mCheckForFallbackFaces(false)
{
NS_ASSERTION(aFontEntry, "font entry pointer must not be null");
NS_ASSERTION(!aFamily ||
@ -915,7 +916,8 @@ protected:
mNeedsBold(aOtherFamilyFace.mNeedsBold),
mFontCreated(aOtherFamilyFace.mFontCreated),
mLoading(aOtherFamilyFace.mLoading),
mInvalid(aOtherFamilyFace.mInvalid)
mInvalid(aOtherFamilyFace.mInvalid),
mCheckForFallbackFaces(aOtherFamilyFace.mCheckForFallbackFaces)
{
if (mFontCreated) {
mFont = aOtherFamilyFace.mFont;
@ -978,6 +980,8 @@ protected:
void CheckState(bool& aSkipDrawing);
void SetLoading(bool aIsLoading) { mLoading = aIsLoading; }
void SetInvalid() { mInvalid = true; }
bool CheckForFallbackFaces() const { return mCheckForFallbackFaces; }
void SetCheckForFallbackFaces() { mCheckForFallbackFaces = true; }
void SetFont(gfxFont* aFont)
{
@ -1006,6 +1010,7 @@ protected:
bool mFontCreated : 1;
bool mLoading : 1;
bool mInvalid : 1;
bool mCheckForFallbackFaces : 1;
};
// List of font families, either named or generic.
@ -1102,7 +1107,13 @@ protected:
already_AddRefed<gfxFont>
FindNonItalicFaceForChar(gfxFontFamily* aFamily, uint32_t aCh);
// helper methods for looking up fonts
// search all faces in a family for a fallback in cases where it's unclear
// whether the family might have a font for a given character
already_AddRefed<gfxFont>
FindFallbackFaceForChar(gfxFontFamily* aFamily, uint32_t aCh,
int32_t aRunScript);
// helper methods for looking up fonts
// lookup and add a font with a given name (i.e. *not* a generic!)
void AddPlatformFont(const nsAString& aName,