mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 05:11:16 +00:00
Bug 1371386 - Take account of requirements for emoji-style or text-style presentation during font selection & fallback. r=m_kato
Differential Revision: https://phabricator.services.mozilla.com/D87304
This commit is contained in:
parent
5dd6f7cbe3
commit
ba87878325
@ -33,7 +33,8 @@ static double WSSDistance(const Face* aFace, const gfxFontStyle& aStyle) {
|
||||
// weight/style/stretch priority: stretch >> style >> weight
|
||||
// so we multiply the stretch and style values to make them dominate
|
||||
// the result
|
||||
return stretchDist * 1.0e8 + styleDist * 1.0e4 + weightDist;
|
||||
return stretchDist * kStretchFactor + styleDist * kStyleFactor +
|
||||
weightDist * kWeightFactor;
|
||||
}
|
||||
|
||||
void* Pointer::ToPtr(FontList* aFontList) const {
|
||||
@ -398,6 +399,21 @@ void Family::SearchAllFontsForChar(FontList* aList,
|
||||
if (!charmap && !fe->HasCharacter(aMatchData->mCh)) {
|
||||
continue;
|
||||
}
|
||||
if (aMatchData->mPresentation != eFontPresentation::Any) {
|
||||
gfxFont* font = fe->FindOrMakeFont(&aMatchData->mStyle);
|
||||
if (!font) {
|
||||
continue;
|
||||
}
|
||||
bool hasColorGlyph =
|
||||
font->HasColorGlyphFor(aMatchData->mCh, aMatchData->mNextCh);
|
||||
if (hasColorGlyph !=
|
||||
(aMatchData->mPresentation == eFontPresentation::Emoji)) {
|
||||
distance += kPresentationMismatch;
|
||||
if (distance >= aMatchData->mMatchDistance) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
aMatchData->mBestMatch = fe;
|
||||
aMatchData->mMatchDistance = distance;
|
||||
aMatchData->mMatchedSharedFamily = this;
|
||||
|
@ -156,14 +156,14 @@ static bool IsJapaneseLocale() {
|
||||
}
|
||||
|
||||
void gfxAndroidPlatform::GetCommonFallbackFonts(
|
||||
uint32_t aCh, uint32_t aNextCh, Script aRunScript,
|
||||
uint32_t aCh, Script aRunScript, eFontPresentation aPresentation,
|
||||
nsTArray<const char*>& aFontList) {
|
||||
static const char kDroidSansJapanese[] = "Droid Sans Japanese";
|
||||
static const char kMotoyaLMaru[] = "MotoyaLMaru";
|
||||
static const char kNotoSansCJKJP[] = "Noto Sans CJK JP";
|
||||
static const char kNotoColorEmoji[] = "Noto Color Emoji";
|
||||
|
||||
if (ShouldPreferEmojiFont(aCh, aNextCh)) {
|
||||
if (aPresentation == eFontPresentation::Emoji) {
|
||||
aFontList.AppendElement(kNotoColorEmoji);
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,8 @@ class gfxAndroidPlatform final : public gfxPlatform {
|
||||
void ReadSystemFontList(
|
||||
nsTArray<mozilla::dom::SystemFontListEntry>* aFontList) override;
|
||||
|
||||
void GetCommonFallbackFonts(uint32_t aCh, uint32_t aNextCh, Script aRunScript,
|
||||
void GetCommonFallbackFonts(uint32_t aCh, Script aRunScript,
|
||||
eFontPresentation aPresentation,
|
||||
nsTArray<const char*>& aFontList) override;
|
||||
|
||||
bool FontHintingEnabled() override;
|
||||
|
@ -2409,6 +2409,40 @@ bool gfxFont::RenderColorGlyph(DrawTarget* aDrawTarget, gfxContext* aContext,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gfxFont::HasColorGlyphFor(uint32_t aCh, uint32_t aNextCh) {
|
||||
// Bitmap fonts are assumed to provide "color" glyphs for all supported chars.
|
||||
gfxFontEntry* fe = GetFontEntry();
|
||||
if (fe->HasColorBitmapTable()) {
|
||||
return true;
|
||||
}
|
||||
// Use harfbuzz shaper to look up the default glyph ID for the character.
|
||||
if (!mHarfBuzzShaper) {
|
||||
mHarfBuzzShaper = MakeUnique<gfxHarfBuzzShaper>(this);
|
||||
}
|
||||
auto* shaper = static_cast<gfxHarfBuzzShaper*>(mHarfBuzzShaper.get());
|
||||
if (!shaper->Initialize()) {
|
||||
return false;
|
||||
}
|
||||
uint32_t gid = 0;
|
||||
if (gfxFontUtils::IsVarSelector(aNextCh)) {
|
||||
gid = shaper->GetVariationGlyph(aCh, aNextCh);
|
||||
}
|
||||
if (!gid) {
|
||||
gid = shaper->GetNominalGlyph(aCh);
|
||||
}
|
||||
if (!gid) {
|
||||
return false;
|
||||
}
|
||||
// Check if there is a COLR/CPAL or SVG glyph for this ID.
|
||||
if (fe->TryGetColorGlyphs() && fe->HasColorLayersForGlyph(gid)) {
|
||||
return true;
|
||||
}
|
||||
if (fe->TryGetSVGData(this) && fe->HasSVGGlyph(gid)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void UnionRange(gfxFloat aX, gfxFloat* aDestMin, gfxFloat* aDestMax) {
|
||||
*aDestMin = std::min(*aDestMin, aX);
|
||||
*aDestMax = std::max(*aDestMax, aX);
|
||||
|
@ -1864,6 +1864,8 @@ class gfxFont {
|
||||
// glyphs. This does not add a reference to the returned font.
|
||||
gfxFont* GetSubSuperscriptFont(int32_t aAppUnitsPerDevPixel);
|
||||
|
||||
bool HasColorGlyphFor(uint32_t aCh, uint32_t aNextCh);
|
||||
|
||||
protected:
|
||||
virtual const Metrics& GetHorizontalMetrics() = 0;
|
||||
|
||||
|
@ -1556,7 +1556,8 @@ static inline double WeightStyleStretchDistance(
|
||||
// weight/style/stretch priority: stretch >> style >> weight
|
||||
// so we multiply the stretch and style values to make them dominate
|
||||
// the result
|
||||
return stretchDist * 1.0e8 + styleDist * 1.0e4 + weightDist;
|
||||
return stretchDist * kStretchFactor + styleDist * kStyleFactor +
|
||||
weightDist * kWeightFactor;
|
||||
}
|
||||
|
||||
void gfxFontFamily::FindAllFontsForStyle(
|
||||
@ -1794,6 +1795,18 @@ void gfxFontFamily::FindFontForChar(GlobalFontMatch* aMatchData) {
|
||||
|
||||
fe = e;
|
||||
distance = WeightStyleStretchDistance(fe, aMatchData->mStyle);
|
||||
if (aMatchData->mPresentation != eFontPresentation::Any) {
|
||||
RefPtr<gfxFont> font = fe->FindOrMakeFont(&aMatchData->mStyle);
|
||||
if (!font) {
|
||||
continue;
|
||||
}
|
||||
bool hasColorGlyph =
|
||||
font->HasColorGlyphFor(aMatchData->mCh, aMatchData->mNextCh);
|
||||
if (hasColorGlyph !=
|
||||
(aMatchData->mPresentation == eFontPresentation::Emoji)) {
|
||||
distance += kPresentationMismatch;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1802,7 +1815,8 @@ void gfxFontFamily::FindFontForChar(GlobalFontMatch* aMatchData) {
|
||||
// If style/weight/stretch was not Normal, see if we can
|
||||
// fall back to a next-best face (e.g. Arial Black -> Bold,
|
||||
// or Arial Narrow -> Regular).
|
||||
GlobalFontMatch data(aMatchData->mCh, aMatchData->mStyle);
|
||||
GlobalFontMatch data(aMatchData->mCh, aMatchData->mNextCh,
|
||||
aMatchData->mStyle, aMatchData->mPresentation);
|
||||
SearchAllFontsForChar(&data);
|
||||
if (!data.mBestMatch) {
|
||||
return;
|
||||
@ -1836,6 +1850,18 @@ void gfxFontFamily::SearchAllFontsForChar(GlobalFontMatch* aMatchData) {
|
||||
gfxFontEntry* fe = mAvailableFonts[i];
|
||||
if (fe && fe->HasCharacter(aMatchData->mCh)) {
|
||||
float distance = WeightStyleStretchDistance(fe, aMatchData->mStyle);
|
||||
if (aMatchData->mPresentation != eFontPresentation::Any) {
|
||||
RefPtr<gfxFont> font = fe->FindOrMakeFont(&aMatchData->mStyle);
|
||||
if (!font) {
|
||||
continue;
|
||||
}
|
||||
bool hasColorGlyph =
|
||||
font->HasColorGlyphFor(aMatchData->mCh, aMatchData->mNextCh);
|
||||
if (hasColorGlyph !=
|
||||
(aMatchData->mPresentation == eFontPresentation::Emoji)) {
|
||||
distance += kPresentationMismatch;
|
||||
}
|
||||
}
|
||||
if (distance < aMatchData->mMatchDistance ||
|
||||
(distance == aMatchData->mMatchDistance &&
|
||||
Compare(fe->Name(), aMatchData->mBestMatch->Name()) > 0)) {
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "gfxFontFeatures.h"
|
||||
#include "gfxFontUtils.h"
|
||||
#include "gfxFontVariations.h"
|
||||
#include "gfxPlatform.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsTHashtable.h"
|
||||
#include "mozilla/HashFunctions.h"
|
||||
@ -254,6 +255,15 @@ class gfxFontEntry {
|
||||
const mozilla::gfx::DeviceColor& aDefaultColor,
|
||||
nsTArray<uint16_t>& layerGlyphs,
|
||||
nsTArray<mozilla::gfx::DeviceColor>& layerColors);
|
||||
bool HasColorLayersForGlyph(uint32_t aGlyphId) {
|
||||
MOZ_ASSERT(mCOLR);
|
||||
return gfxFontUtils::HasColorLayersForGlyph(mCOLR, aGlyphId);
|
||||
}
|
||||
|
||||
bool HasColorBitmapTable() {
|
||||
return HasFontTable(TRUETYPE_TAG('C', 'B', 'D', 'T')) ||
|
||||
HasFontTable(TRUETYPE_TAG('s', 'b', 'i', 'x'));
|
||||
}
|
||||
|
||||
// Access to raw font table data (needed for Harfbuzz):
|
||||
// returns a pointer to data owned by the fontEntry or the OS,
|
||||
@ -752,17 +762,23 @@ inline bool gfxFontEntry::SupportsBold() {
|
||||
|
||||
// used when iterating over all fonts looking for a match for a given character
|
||||
struct GlobalFontMatch {
|
||||
GlobalFontMatch(const uint32_t aCharacter, const gfxFontStyle& aStyle)
|
||||
: mStyle(aStyle), mCh(aCharacter) {}
|
||||
GlobalFontMatch(uint32_t aCharacter, uint32_t aNextCh,
|
||||
const gfxFontStyle& aStyle, eFontPresentation aPresentation)
|
||||
: mStyle(aStyle),
|
||||
mCh(aCharacter),
|
||||
mNextCh(aNextCh),
|
||||
mPresentation(aPresentation) {}
|
||||
|
||||
RefPtr<gfxFontEntry> mBestMatch; // current best match
|
||||
RefPtr<gfxFontFamily> mMatchedFamily; // the family it belongs to
|
||||
mozilla::fontlist::Family* mMatchedSharedFamily = nullptr;
|
||||
const gfxFontStyle& mStyle; // style to match
|
||||
const uint32_t mCh; // codepoint to be matched
|
||||
uint32_t mCount = 0; // number of fonts matched
|
||||
uint32_t mCmapsTested = 0; // number of cmaps tested
|
||||
float mMatchDistance = INFINITY; // metric indicating closest match
|
||||
const gfxFontStyle& mStyle; // style to match
|
||||
const uint32_t mCh; // codepoint to be matched
|
||||
const uint32_t mNextCh; // following codepoint (or zero)
|
||||
eFontPresentation mPresentation;
|
||||
uint32_t mCount = 0; // number of fonts matched
|
||||
uint32_t mCmapsTested = 0; // number of cmaps tested
|
||||
double mMatchDistance = INFINITY; // metric indicating closest match
|
||||
};
|
||||
|
||||
// Installation status (base system / langpack / user-installed) may determine
|
||||
|
@ -1722,6 +1722,16 @@ bool gfxFontUtils::GetColorGlyphLayers(
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gfxFontUtils::HasColorLayersForGlyph(hb_blob_t* aCOLR, uint32_t aGlyphId) {
|
||||
unsigned int blobLength;
|
||||
const COLRHeader* colr =
|
||||
reinterpret_cast<const COLRHeader*>(hb_blob_get_data(aCOLR, &blobLength));
|
||||
MOZ_ASSERT(colr, "Cannot get COLR raw data");
|
||||
MOZ_ASSERT(blobLength, "Found COLR data, but length is 0");
|
||||
|
||||
return LookForBaseGlyphRecord(colr, aGlyphId);
|
||||
}
|
||||
|
||||
void gfxFontUtils::GetVariationData(
|
||||
gfxFontEntry* aFontEntry, nsTArray<gfxFontVariationAxis>* aAxes,
|
||||
nsTArray<gfxFontVariationInstance>* aInstances) {
|
||||
|
@ -1159,6 +1159,7 @@ class gfxFontUtils {
|
||||
const mozilla::gfx::DeviceColor& aDefaultColor,
|
||||
nsTArray<uint16_t>& aGlyphs,
|
||||
nsTArray<mozilla::gfx::DeviceColor>& aColors);
|
||||
static bool HasColorLayersForGlyph(hb_blob_t* aCOLR, uint32_t aGlyphId);
|
||||
|
||||
// Helper used to implement gfxFontEntry::GetVariation{Axes,Instances} for
|
||||
// platforms where the native font APIs don't provide the info we want
|
||||
@ -1204,6 +1205,16 @@ class gfxFontUtils {
|
||||
static const mozilla::Encoding* gMSFontNameCharsets[];
|
||||
};
|
||||
|
||||
// Factors used to weight the distances between the available and target font
|
||||
// properties during font-matching. These ensure that we respect the CSS-fonts
|
||||
// requirement that font-stretch >> font-style >> font-weight; and in addition,
|
||||
// a mismatch between the desired and actual glyph presentation (emoji vs text)
|
||||
// will take precedence over any of the style attributes.
|
||||
constexpr double kPresentationMismatch = 1.0e12;
|
||||
constexpr double kStretchFactor = 1.0e8;
|
||||
constexpr double kStyleFactor = 1.0e4;
|
||||
constexpr double kWeightFactor = 1.0e0;
|
||||
|
||||
// style distance ==> [0,500]
|
||||
static inline double StyleDistance(const mozilla::SlantStyleRange& aRange,
|
||||
mozilla::FontSlantStyle aTargetStyle) {
|
||||
|
@ -96,6 +96,10 @@ enum eGfxLog {
|
||||
eGfxLog_textperf = 5
|
||||
};
|
||||
|
||||
// Used during font matching to express a preference, if any, for whether
|
||||
// to use a font that will present a color or monochrome glyph.
|
||||
enum class eFontPresentation : uint8_t { Any = 0, Text = 1, Emoji = 2 };
|
||||
|
||||
// when searching through pref langs, max number of pref langs
|
||||
const uint32_t kMaxLenPrefLangList = 32;
|
||||
|
||||
@ -475,8 +479,8 @@ class gfxPlatform : public mozilla::layers::MemoryPressureListener {
|
||||
|
||||
// returns a list of commonly used fonts for a given character
|
||||
// these are *possible* matches, no cmap-checking is done at this level
|
||||
virtual void GetCommonFallbackFonts(uint32_t /*aCh*/, uint32_t /*aNextCh*/,
|
||||
Script /*aRunScript*/,
|
||||
virtual void GetCommonFallbackFonts(uint32_t /*aCh*/, Script /*aRunScript*/,
|
||||
eFontPresentation /*aPresentation*/,
|
||||
nsTArray<const char*>& /*aFontList*/) {
|
||||
// platform-specific override, by default do nothing
|
||||
}
|
||||
|
@ -863,20 +863,19 @@ void gfxPlatformFontList::GetFontFamilyList(
|
||||
}
|
||||
}
|
||||
|
||||
gfxFontEntry* gfxPlatformFontList::SystemFindFontForChar(
|
||||
gfxFont* gfxPlatformFontList::SystemFindFontForChar(
|
||||
uint32_t aCh, uint32_t aNextCh, Script aRunScript,
|
||||
const gfxFontStyle* aStyle, FontVisibility* aVisibility,
|
||||
FontMatchingStats* aFontMatchingStats) {
|
||||
eFontPresentation aPresentation, const gfxFontStyle* aStyle,
|
||||
FontVisibility* aVisibility, FontMatchingStats* aFontMatchingStats) {
|
||||
MOZ_ASSERT(!mCodepointsWithNoFonts.test(aCh),
|
||||
"don't call for codepoints already known to be unsupported");
|
||||
|
||||
gfxFontEntry* fontEntry = nullptr;
|
||||
|
||||
// Try to short-circuit font fallback for U+FFFD, used to represent
|
||||
// encoding errors: just use cached family from last time U+FFFD was seen.
|
||||
// This helps speed up pages with lots of encoding errors, binary-as-text,
|
||||
// etc.
|
||||
if (aCh == 0xFFFD) {
|
||||
gfxFontEntry* fontEntry = nullptr;
|
||||
if (mReplacementCharFallbackFamily.mIsShared &&
|
||||
mReplacementCharFallbackFamily.mShared) {
|
||||
fontlist::Face* face =
|
||||
@ -897,7 +896,7 @@ gfxFontEntry* gfxPlatformFontList::SystemFindFontForChar(
|
||||
// this should never fail, as we must have found U+FFFD in order to set
|
||||
// mReplacementCharFallbackFamily at all, but better play it safe
|
||||
if (fontEntry && fontEntry->HasCharacter(aCh)) {
|
||||
return fontEntry;
|
||||
return fontEntry->FindOrMakeFont(aStyle);
|
||||
}
|
||||
}
|
||||
|
||||
@ -906,15 +905,35 @@ gfxFontEntry* gfxPlatformFontList::SystemFindFontForChar(
|
||||
// search commonly available fonts
|
||||
bool common = true;
|
||||
FontFamily fallbackFamily;
|
||||
fontEntry =
|
||||
CommonFontFallback(aCh, aNextCh, aRunScript, aStyle, fallbackFamily);
|
||||
gfxFont* candidate = CommonFontFallback(
|
||||
aCh, aNextCh, aRunScript, aPresentation, aStyle, fallbackFamily);
|
||||
gfxFont* font = nullptr;
|
||||
if (candidate) {
|
||||
if (aPresentation == eFontPresentation::Any) {
|
||||
font = candidate;
|
||||
} else {
|
||||
bool hasColorGlyph = candidate->HasColorGlyphFor(aCh, aNextCh);
|
||||
if (hasColorGlyph == (aPresentation == eFontPresentation::Emoji)) {
|
||||
font = candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if didn't find a font, do system-wide fallback (except for specials)
|
||||
// If we didn't find a common font, or it was not the preferred type (color
|
||||
// or monochrome), do system-wide fallback (except for specials).
|
||||
uint32_t cmapCount = 0;
|
||||
if (!fontEntry) {
|
||||
if (!font) {
|
||||
common = false;
|
||||
fontEntry = GlobalFontFallback(aCh, aRunScript, aStyle, cmapCount,
|
||||
fallbackFamily, aFontMatchingStats);
|
||||
font = GlobalFontFallback(aCh, aNextCh, aRunScript, aPresentation, aStyle,
|
||||
cmapCount, fallbackFamily, aFontMatchingStats);
|
||||
// If the font we found doesn't match the requested type, and we also found
|
||||
// a candidate above, prefer that one.
|
||||
if (font && aPresentation != eFontPresentation::Any && candidate) {
|
||||
bool hasColorGlyph = font->HasColorGlyphFor(aCh, aNextCh);
|
||||
if (hasColorGlyph != (aPresentation == eFontPresentation::Emoji)) {
|
||||
font = candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
TimeDuration elapsed = TimeStamp::Now() - start;
|
||||
|
||||
@ -927,12 +946,12 @@ gfxFontEntry* gfxPlatformFontList::SystemFindFontForChar(
|
||||
"script: %d match: [%s]"
|
||||
" time: %dus cmaps: %d\n",
|
||||
(common ? "common" : "global"), aCh, static_cast<int>(script),
|
||||
(fontEntry ? fontEntry->Name().get() : "<none>"),
|
||||
(font ? font->GetFontEntry()->Name().get() : "<none>"),
|
||||
int32_t(elapsed.ToMicroseconds()), cmapCount));
|
||||
}
|
||||
|
||||
// no match? add to set of non-matching codepoints
|
||||
if (!fontEntry) {
|
||||
if (!font) {
|
||||
mCodepointsWithNoFonts.set(aCh);
|
||||
} else {
|
||||
*aVisibility = fallbackFamily.mIsShared
|
||||
@ -957,18 +976,19 @@ gfxFontEntry* gfxPlatformFontList::SystemFindFontForChar(
|
||||
Telemetry::Accumulate(Telemetry::SYSTEM_FONT_FALLBACK_SCRIPT,
|
||||
int(aRunScript) + 1);
|
||||
|
||||
return fontEntry;
|
||||
return font;
|
||||
}
|
||||
|
||||
#define NUM_FALLBACK_FONTS 8
|
||||
|
||||
gfxFontEntry* gfxPlatformFontList::CommonFontFallback(
|
||||
gfxFont* gfxPlatformFontList::CommonFontFallback(
|
||||
uint32_t aCh, uint32_t aNextCh, Script aRunScript,
|
||||
const gfxFontStyle* aMatchStyle, FontFamily& aMatchedFamily) {
|
||||
eFontPresentation aPresentation, const gfxFontStyle* aMatchStyle,
|
||||
FontFamily& aMatchedFamily) {
|
||||
AutoTArray<const char*, NUM_FALLBACK_FONTS> defaultFallbacks;
|
||||
gfxPlatform::GetPlatform()->GetCommonFallbackFonts(aCh, aNextCh, aRunScript,
|
||||
defaultFallbacks);
|
||||
GlobalFontMatch data(aCh, *aMatchStyle);
|
||||
gfxPlatform::GetPlatform()->GetCommonFallbackFonts(
|
||||
aCh, aRunScript, aPresentation, defaultFallbacks);
|
||||
GlobalFontMatch data(aCh, aNextCh, *aMatchStyle, aPresentation);
|
||||
if (SharedFontList()) {
|
||||
for (const auto name : defaultFallbacks) {
|
||||
fontlist::Family* family = FindSharedFamily(nsDependentCString(name));
|
||||
@ -978,7 +998,7 @@ gfxFontEntry* gfxPlatformFontList::CommonFontFallback(
|
||||
family->SearchAllFontsForChar(SharedFontList(), &data);
|
||||
if (data.mBestMatch) {
|
||||
aMatchedFamily = FontFamily(family);
|
||||
return data.mBestMatch;
|
||||
return data.mBestMatch->FindOrMakeFont(aMatchStyle);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -991,15 +1011,16 @@ gfxFontEntry* gfxPlatformFontList::CommonFontFallback(
|
||||
fallback->FindFontForChar(&data);
|
||||
if (data.mBestMatch) {
|
||||
aMatchedFamily = FontFamily(fallback);
|
||||
return data.mBestMatch;
|
||||
return data.mBestMatch->FindOrMakeFont(aMatchStyle);
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
gfxFontEntry* gfxPlatformFontList::GlobalFontFallback(
|
||||
const uint32_t aCh, Script aRunScript, const gfxFontStyle* aMatchStyle,
|
||||
gfxFont* gfxPlatformFontList::GlobalFontFallback(
|
||||
uint32_t aCh, uint32_t aNextCh, Script aRunScript,
|
||||
eFontPresentation aPresentation, const gfxFontStyle* aMatchStyle,
|
||||
uint32_t& aCmapCount, FontFamily& aMatchedFamily,
|
||||
FontMatchingStats* aFontMatchingStats) {
|
||||
bool useCmaps = IsFontFamilyWhitelistActive() ||
|
||||
@ -1012,12 +1033,30 @@ gfxFontEntry* gfxPlatformFontList::GlobalFontFallback(
|
||||
if (fe) {
|
||||
if (aMatchedFamily.mIsShared) {
|
||||
if (IsVisibleToCSS(*aMatchedFamily.mShared)) {
|
||||
return fe;
|
||||
gfxFont* font = fe->FindOrMakeFont(aMatchStyle);
|
||||
if (font) {
|
||||
if (aPresentation == eFontPresentation::Any) {
|
||||
return font;
|
||||
}
|
||||
bool hasColorGlyph = font->HasColorGlyphFor(aCh, aNextCh);
|
||||
if (hasColorGlyph == (aPresentation == eFontPresentation::Emoji)) {
|
||||
return font;
|
||||
}
|
||||
}
|
||||
}
|
||||
rejectedFallbackVisibility = aMatchedFamily.mShared->Visibility();
|
||||
} else {
|
||||
if (IsVisibleToCSS(*aMatchedFamily.mUnshared)) {
|
||||
return fe;
|
||||
gfxFont* font = fe->FindOrMakeFont(aMatchStyle);
|
||||
if (font) {
|
||||
if (aPresentation == eFontPresentation::Any) {
|
||||
return font;
|
||||
}
|
||||
bool hasColorGlyph = font->HasColorGlyphFor(aCh, aNextCh);
|
||||
if (hasColorGlyph == (aPresentation == eFontPresentation::Emoji)) {
|
||||
return font;
|
||||
}
|
||||
}
|
||||
}
|
||||
rejectedFallbackVisibility = aMatchedFamily.mUnshared->Visibility();
|
||||
}
|
||||
@ -1025,7 +1064,7 @@ gfxFontEntry* gfxPlatformFontList::GlobalFontFallback(
|
||||
}
|
||||
|
||||
// otherwise, try to find it among local fonts
|
||||
GlobalFontMatch data(aCh, *aMatchStyle);
|
||||
GlobalFontMatch data(aCh, aNextCh, *aMatchStyle, aPresentation);
|
||||
if (SharedFontList()) {
|
||||
fontlist::Family* families = SharedFontList()->Families();
|
||||
if (families) {
|
||||
@ -1042,7 +1081,7 @@ gfxFontEntry* gfxPlatformFontList::GlobalFontFallback(
|
||||
}
|
||||
if (data.mBestMatch) {
|
||||
aMatchedFamily = FontFamily(data.mMatchedSharedFamily);
|
||||
return data.mBestMatch;
|
||||
return data.mBestMatch->FindOrMakeFont(aMatchStyle);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -1064,7 +1103,7 @@ gfxFontEntry* gfxPlatformFontList::GlobalFontFallback(
|
||||
aCmapCount = data.mCmapsTested;
|
||||
if (data.mBestMatch) {
|
||||
aMatchedFamily = FontFamily(data.mMatchedFamily);
|
||||
return data.mBestMatch;
|
||||
return data.mBestMatch->FindOrMakeFont(aMatchStyle);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -209,11 +209,12 @@ class gfxPlatformFontList : public gfxFontInfoLoader {
|
||||
|
||||
void GetFontFamilyList(nsTArray<RefPtr<gfxFontFamily>>& aFamilyArray);
|
||||
|
||||
gfxFontEntry* SystemFindFontForChar(uint32_t aCh, uint32_t aNextCh,
|
||||
Script aRunScript,
|
||||
const gfxFontStyle* aStyle,
|
||||
FontVisibility* aVisibility,
|
||||
FontMatchingStats* aFontMatchingStats);
|
||||
gfxFont* SystemFindFontForChar(uint32_t aCh, uint32_t aNextCh,
|
||||
Script aRunScript,
|
||||
eFontPresentation aPresentation,
|
||||
const gfxFontStyle* aStyle,
|
||||
FontVisibility* aVisibility,
|
||||
FontMatchingStats* aFontMatchingStats);
|
||||
|
||||
// Flags to control optional behaviors in FindAndAddFamilies. The sense
|
||||
// of the bit flags have been chosen such that the default parameter of
|
||||
@ -622,17 +623,17 @@ class gfxPlatformFontList : public gfxFontInfoLoader {
|
||||
}
|
||||
|
||||
// returns default font for a given character, null otherwise
|
||||
gfxFontEntry* CommonFontFallback(uint32_t aCh, uint32_t aNextCh,
|
||||
Script aRunScript,
|
||||
const gfxFontStyle* aMatchStyle,
|
||||
FontFamily& aMatchedFamily);
|
||||
gfxFont* CommonFontFallback(uint32_t aCh, uint32_t aNextCh, Script aRunScript,
|
||||
eFontPresentation aPresentation,
|
||||
const gfxFontStyle* aMatchStyle,
|
||||
FontFamily& aMatchedFamily);
|
||||
|
||||
// Search fonts system-wide for a given character, null if not found.
|
||||
gfxFontEntry* GlobalFontFallback(const uint32_t aCh, Script aRunScript,
|
||||
const gfxFontStyle* aMatchStyle,
|
||||
uint32_t& aCmapCount,
|
||||
FontFamily& aMatchedFamily,
|
||||
FontMatchingStats* aFontMatchingStats);
|
||||
gfxFont* GlobalFontFallback(uint32_t aCh, uint32_t aNextCh, Script aRunScript,
|
||||
eFontPresentation aPresentation,
|
||||
const gfxFontStyle* aMatchStyle,
|
||||
uint32_t& aCmapCount, FontFamily& aMatchedFamily,
|
||||
FontMatchingStats* aFontMatchingStats);
|
||||
|
||||
// Platform-specific implementation of global font fallback, if any;
|
||||
// this may return nullptr in which case the default cmap-based fallback
|
||||
|
@ -206,10 +206,10 @@ static const char kFontWenQuanYiMicroHei[] = "WenQuanYi Micro Hei";
|
||||
static const char kFontNanumGothic[] = "NanumGothic";
|
||||
static const char kFontSymbola[] = "Symbola";
|
||||
|
||||
void gfxPlatformGtk::GetCommonFallbackFonts(uint32_t aCh, uint32_t aNextCh,
|
||||
Script aRunScript,
|
||||
void gfxPlatformGtk::GetCommonFallbackFonts(uint32_t aCh, Script aRunScript,
|
||||
eFontPresentation aPresentation,
|
||||
nsTArray<const char*>& aFontList) {
|
||||
if (ShouldPreferEmojiFont(aCh, aNextCh)) {
|
||||
if (aPresentation == eFontPresentation::Emoji) {
|
||||
aFontList.AppendElement(kFontTwemojiMozilla);
|
||||
}
|
||||
|
||||
|
@ -42,7 +42,8 @@ class gfxPlatformGtk final : public gfxPlatform {
|
||||
|
||||
nsresult UpdateFontList() override;
|
||||
|
||||
void GetCommonFallbackFonts(uint32_t aCh, uint32_t aNextCh, Script aRunScript,
|
||||
void GetCommonFallbackFonts(uint32_t aCh, Script aRunScript,
|
||||
eFontPresentation aPresentation,
|
||||
nsTArray<const char*>& aFontList) override;
|
||||
|
||||
gfxPlatformFontList* CreatePlatformFontList() override;
|
||||
|
@ -171,10 +171,10 @@ static const char kFontSTIXGeneral[] = "STIXGeneral";
|
||||
static const char kFontTamilMN[] = "Tamil MN";
|
||||
static const char kFontZapfDingbats[] = "Zapf Dingbats";
|
||||
|
||||
void gfxPlatformMac::GetCommonFallbackFonts(uint32_t aCh, uint32_t aNextCh,
|
||||
Script aRunScript,
|
||||
void gfxPlatformMac::GetCommonFallbackFonts(uint32_t aCh, Script aRunScript,
|
||||
eFontPresentation aPresentation,
|
||||
nsTArray<const char*>& aFontList) {
|
||||
if (ShouldPreferEmojiFont(aCh, aNextCh)) {
|
||||
if (aPresentation == eFontPresentation::Emoji) {
|
||||
aFontList.AppendElement(kFontAppleColorEmoji);
|
||||
}
|
||||
|
||||
|
@ -39,7 +39,8 @@ class gfxPlatformMac : public gfxPlatform {
|
||||
|
||||
bool IsFontFormatSupported(uint32_t aFormatFlags) override;
|
||||
|
||||
void GetCommonFallbackFonts(uint32_t aCh, uint32_t aNextCh, Script aRunScript,
|
||||
void GetCommonFallbackFonts(uint32_t aCh, Script aRunScript,
|
||||
eFontPresentation aPresentation,
|
||||
nsTArray<const char*>& aFontList) override;
|
||||
|
||||
// lookup the system font for a particular system font type and set
|
||||
|
@ -2909,9 +2909,10 @@ gfxTextRun* gfxFontGroup::GetEllipsisTextRun(
|
||||
return mCachedEllipsisTextRun.get();
|
||||
}
|
||||
|
||||
gfxFont* gfxFontGroup::FindFallbackFaceForChar(gfxFontFamily* aFamily,
|
||||
uint32_t aCh) {
|
||||
GlobalFontMatch data(aCh, mStyle);
|
||||
gfxFont* gfxFontGroup::FindFallbackFaceForChar(
|
||||
gfxFontFamily* aFamily, uint32_t aCh, uint32_t aNextCh,
|
||||
eFontPresentation aPresentation) {
|
||||
GlobalFontMatch data(aCh, aNextCh, mStyle, aPresentation);
|
||||
aFamily->SearchAllFontsForChar(&data);
|
||||
gfxFontEntry* fe = data.mBestMatch;
|
||||
if (!fe) {
|
||||
@ -2920,11 +2921,12 @@ gfxFont* gfxFontGroup::FindFallbackFaceForChar(gfxFontFamily* aFamily,
|
||||
return fe->FindOrMakeFont(&mStyle);
|
||||
}
|
||||
|
||||
gfxFont* gfxFontGroup::FindFallbackFaceForChar(fontlist::Family* aFamily,
|
||||
uint32_t aCh) {
|
||||
gfxFont* gfxFontGroup::FindFallbackFaceForChar(
|
||||
fontlist::Family* aFamily, uint32_t aCh, uint32_t aNextCh,
|
||||
eFontPresentation aPresentation) {
|
||||
fontlist::FontList* list =
|
||||
gfxPlatformFontList::PlatformFontList()->SharedFontList();
|
||||
GlobalFontMatch data(aCh, mStyle);
|
||||
GlobalFontMatch data(aCh, aNextCh, mStyle, aPresentation);
|
||||
aFamily->SearchAllFontsForChar(list, &data);
|
||||
gfxFontEntry* fe = data.mBestMatch;
|
||||
if (!fe) {
|
||||
@ -2933,12 +2935,15 @@ gfxFont* gfxFontGroup::FindFallbackFaceForChar(fontlist::Family* aFamily,
|
||||
return fe->FindOrMakeFont(&mStyle);
|
||||
}
|
||||
|
||||
gfxFont* gfxFontGroup::FindFallbackFaceForChar(const FamilyFace& aFamily,
|
||||
uint32_t aCh) {
|
||||
gfxFont* gfxFontGroup::FindFallbackFaceForChar(
|
||||
const FamilyFace& aFamily, uint32_t aCh, uint32_t aNextCh,
|
||||
eFontPresentation aPresentation) {
|
||||
if (aFamily.IsSharedFamily()) {
|
||||
return FindFallbackFaceForChar(aFamily.SharedFamily(), aCh);
|
||||
return FindFallbackFaceForChar(aFamily.SharedFamily(), aCh, aNextCh,
|
||||
aPresentation);
|
||||
}
|
||||
return FindFallbackFaceForChar(aFamily.OwnedFamily(), aCh);
|
||||
return FindFallbackFaceForChar(aFamily.OwnedFamily(), aCh, aNextCh,
|
||||
aPresentation);
|
||||
}
|
||||
|
||||
gfxFloat gfxFontGroup::GetUnderlineOffset() {
|
||||
@ -3030,12 +3035,41 @@ gfxFont* gfxFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh,
|
||||
bool isJoinControl = gfxFontUtils::IsJoinControl(aCh);
|
||||
bool wasJoinCauser = gfxFontUtils::IsJoinCauser(aPrevCh);
|
||||
bool isVarSelector = gfxFontUtils::IsVarSelector(aCh);
|
||||
bool nextIsVarSelector = gfxFontUtils::IsVarSelector(aNextCh);
|
||||
|
||||
// Whether we've seen a font that is currently loading a resource that may
|
||||
// provide this character (so we should not start a new load).
|
||||
bool loading = false;
|
||||
|
||||
if (!isJoinControl && !wasJoinCauser && !isVarSelector) {
|
||||
// Do we need to explicitly look for a font that does or does not provide a
|
||||
// color glyph for the given character?
|
||||
// For characters with no `EMOJI` property, we'll use whatever the family
|
||||
// list calls for; but if it's a potential emoji codepoint, we need to check
|
||||
// if there's a variation selector specifically asking for Text-style or
|
||||
// Emoji-style rendering and look for a suitable font.
|
||||
eFontPresentation presentation = eFontPresentation::Any;
|
||||
EmojiPresentation emojiPresentation = GetEmojiPresentation(aCh);
|
||||
if (emojiPresentation != TextOnly) {
|
||||
// If the prefer-emoji selector is present, or if it's a default-emoji char
|
||||
// and the prefer-text selector is NOT present, or if there's a skin-tone
|
||||
// modifier, we specifically look for a font with a color glyph.
|
||||
// If the prefer-text selector is present, we specifically look for a font
|
||||
// that will provide a monochrome glyph.
|
||||
// Otherwise, we'll accept either color or monochrome font-family entries,
|
||||
// so that a color font can be explicitly applied via font-family even to
|
||||
// characters that are not inherently emoji-style.
|
||||
if (aNextCh == kVariationSelector16 ||
|
||||
(emojiPresentation == EmojiPresentation::EmojiDefault &&
|
||||
aNextCh != kVariationSelector15) ||
|
||||
(aNextCh >= kEmojiSkinToneFirst && aNextCh <= kEmojiSkinToneLast)) {
|
||||
presentation = eFontPresentation::Emoji;
|
||||
} else if (aNextCh == kVariationSelector15) {
|
||||
presentation = eFontPresentation::Text;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isJoinControl && !wasJoinCauser && !isVarSelector &&
|
||||
!nextIsVarSelector && presentation == eFontPresentation::Any) {
|
||||
gfxFont* firstFont = GetFontAt(0, aCh, &loading);
|
||||
if (firstFont) {
|
||||
if (firstFont->HasCharacter(aCh)) {
|
||||
@ -3045,13 +3079,13 @@ gfxFont* gfxFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh,
|
||||
|
||||
gfxFont* font = nullptr;
|
||||
if (mFonts[0].CheckForFallbackFaces()) {
|
||||
font = FindFallbackFaceForChar(mFonts[0], aCh);
|
||||
font = FindFallbackFaceForChar(mFonts[0], aCh, aNextCh, presentation);
|
||||
} else if (!firstFont->GetFontEntry()->IsUserFont()) {
|
||||
// For platform fonts (but not userfonts), we may need to do
|
||||
// fallback within the family to handle cases where some faces
|
||||
// such as Italic or Black have reduced character sets compared
|
||||
// to the family's Regular face.
|
||||
font = FindFallbackFaceForChar(mFonts[0], aCh);
|
||||
font = FindFallbackFaceForChar(mFonts[0], aCh, aNextCh, presentation);
|
||||
}
|
||||
if (font) {
|
||||
*aMatchType = {FontMatchType::Kind::kFontGroup, mFonts[0].Generic()};
|
||||
@ -3093,6 +3127,36 @@ gfxFont* gfxFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh,
|
||||
return aPrevMatchedFont;
|
||||
}
|
||||
|
||||
// Used to remember the first "candidate" font that would provide a fallback
|
||||
// text-style rendering if no color glyph can be found.
|
||||
gfxFont* candidateFont = nullptr;
|
||||
FontMatchType candidateMatchType;
|
||||
|
||||
// Handle a candidate font that could support the character, returning true
|
||||
// if we should go ahead and return |f|, false to continue searching.
|
||||
auto CheckCandidate = [&](gfxFont* f, FontMatchType t) -> bool {
|
||||
// If no preference, then just accept the font.
|
||||
if (presentation == eFontPresentation::Any) {
|
||||
RefPtr<gfxFont> autoRefDeref(candidateFont);
|
||||
*aMatchType = t;
|
||||
return true;
|
||||
}
|
||||
// Does the candidate font provide a color glyph for the current character?
|
||||
bool hasColorGlyph = f->HasColorGlyphFor(aCh, aNextCh);
|
||||
// If the provided glyph matches the preference, accept the font.
|
||||
if (hasColorGlyph == (presentation == eFontPresentation::Emoji)) {
|
||||
RefPtr<gfxFont> autoRefDeref(candidateFont);
|
||||
*aMatchType = t;
|
||||
return true;
|
||||
}
|
||||
// Otherwise, remember the first potential fallback, but keep searching.
|
||||
if (!candidateFont) {
|
||||
candidateFont = f;
|
||||
candidateMatchType = t;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// 1. check remaining fonts in the font group
|
||||
for (uint32_t i = nextIndex; i < fontListLength; i++) {
|
||||
FamilyFace& ff = mFonts[i];
|
||||
@ -3107,8 +3171,10 @@ gfxFont* gfxFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh,
|
||||
if (font) {
|
||||
// if available, use already-made gfxFont and check for character
|
||||
if (font->HasCharacter(aCh)) {
|
||||
*aMatchType = {FontMatchType::Kind::kFontGroup, ff.Generic()};
|
||||
return font;
|
||||
if (CheckCandidate(font,
|
||||
{FontMatchType::Kind::kFontGroup, ff.Generic()})) {
|
||||
return font;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// don't have a gfxFont yet, test charmap before instantiating
|
||||
@ -3139,9 +3205,10 @@ gfxFont* gfxFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh,
|
||||
if (pfe && pfe->HasCharacter(aCh)) {
|
||||
font = GetFontAt(i, aCh, &loading);
|
||||
if (font) {
|
||||
*aMatchType = {FontMatchType::Kind::kFontGroup,
|
||||
mFonts[i].Generic()};
|
||||
return font;
|
||||
if (CheckCandidate(font, {FontMatchType::Kind::kFontGroup,
|
||||
mFonts[i].Generic()})) {
|
||||
return font;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (fe && fe->HasCharacter(aCh)) {
|
||||
@ -3149,8 +3216,10 @@ gfxFont* gfxFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh,
|
||||
// build the font via GetFontAt
|
||||
font = GetFontAt(i, aCh, &loading);
|
||||
if (font) {
|
||||
*aMatchType = {FontMatchType::Kind::kFontGroup, mFonts[i].Generic()};
|
||||
return font;
|
||||
if (CheckCandidate(font, {FontMatchType::Kind::kFontGroup,
|
||||
mFonts[i].Generic()})) {
|
||||
return font;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3171,10 +3240,12 @@ gfxFont* gfxFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh,
|
||||
"should only do fallback once per font family");
|
||||
}
|
||||
#endif
|
||||
font = FindFallbackFaceForChar(ff, aCh);
|
||||
font = FindFallbackFaceForChar(ff, aCh, aNextCh, presentation);
|
||||
if (font) {
|
||||
*aMatchType = {FontMatchType::Kind::kFontGroup, ff.Generic()};
|
||||
return font;
|
||||
if (CheckCandidate(font,
|
||||
{FontMatchType::Kind::kFontGroup, ff.Generic()})) {
|
||||
return font;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// For platform fonts, but not user fonts, consider intra-family
|
||||
@ -3182,10 +3253,12 @@ gfxFont* gfxFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh,
|
||||
// also above).
|
||||
gfxFontEntry* fe = ff.FontEntry();
|
||||
if (fe && !fe->mIsUserFontContainer && !fe->IsUserFont()) {
|
||||
font = FindFallbackFaceForChar(ff, aCh);
|
||||
font = FindFallbackFaceForChar(ff, aCh, aNextCh, presentation);
|
||||
if (font) {
|
||||
*aMatchType = {FontMatchType::Kind::kFontGroup, ff.Generic()};
|
||||
return font;
|
||||
if (CheckCandidate(font,
|
||||
{FontMatchType::Kind::kFontGroup, ff.Generic()})) {
|
||||
return font;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3194,8 +3267,9 @@ gfxFont* gfxFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh,
|
||||
if (fontListLength == 0) {
|
||||
gfxFont* defaultFont = GetDefaultFont();
|
||||
if (defaultFont->HasCharacter(aCh)) {
|
||||
*aMatchType = FontMatchType::Kind::kFontGroup;
|
||||
return defaultFont;
|
||||
if (CheckCandidate(defaultFont, FontMatchType::Kind::kFontGroup)) {
|
||||
return defaultFont;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3208,34 +3282,56 @@ gfxFont* gfxFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh,
|
||||
// fallback has already noted a failure.
|
||||
if (gfxPlatformFontList::PlatformFontList()->SkipFontFallbackForChar(aCh) ||
|
||||
GetGeneralCategory(aCh) == HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED) {
|
||||
return nullptr;
|
||||
if (candidateFont) {
|
||||
*aMatchType = candidateMatchType;
|
||||
}
|
||||
return candidateFont;
|
||||
}
|
||||
|
||||
// 2. search pref fonts
|
||||
gfxFont* font = WhichPrefFontSupportsChar(aCh, aNextCh);
|
||||
gfxFont* font = WhichPrefFontSupportsChar(aCh, aNextCh, presentation);
|
||||
if (font) {
|
||||
*aMatchType = FontMatchType::Kind::kPrefsFallback;
|
||||
return font;
|
||||
if (CheckCandidate(font, FontMatchType::Kind::kPrefsFallback)) {
|
||||
return font;
|
||||
}
|
||||
}
|
||||
|
||||
// For fallback searches, we don't want to use a color-emoji font unless
|
||||
// emoji-style presentation is specifically required, so we map Any to
|
||||
// Text here.
|
||||
if (presentation == eFontPresentation::Any) {
|
||||
presentation = eFontPresentation::Text;
|
||||
}
|
||||
|
||||
// 3. use fallback fonts
|
||||
// -- before searching for something else check the font used for the
|
||||
// previous character
|
||||
if (aPrevMatchedFont && aPrevMatchedFont->HasCharacter(aCh)) {
|
||||
*aMatchType = FontMatchType::Kind::kSystemFallback;
|
||||
return aPrevMatchedFont;
|
||||
if (CheckCandidate(aPrevMatchedFont,
|
||||
FontMatchType::Kind::kSystemFallback)) {
|
||||
return aPrevMatchedFont;
|
||||
}
|
||||
}
|
||||
|
||||
// for known "space" characters, don't do a full system-fallback search;
|
||||
// we'll synthesize appropriate-width spaces instead of missing-glyph boxes
|
||||
if (GetGeneralCategory(aCh) == HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR &&
|
||||
GetFirstValidFont()->SynthesizeSpaceWidth(aCh) >= 0.0) {
|
||||
RefPtr<gfxFont> autoRefDeref(candidateFont);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// -- otherwise look for other stuff
|
||||
*aMatchType = FontMatchType::Kind::kSystemFallback;
|
||||
return WhichSystemFontSupportsChar(aCh, aNextCh, aRunScript);
|
||||
font = WhichSystemFontSupportsChar(aCh, aNextCh, aRunScript, presentation);
|
||||
if (font) {
|
||||
if (CheckCandidate(font, FontMatchType::Kind::kSystemFallback)) {
|
||||
return font;
|
||||
}
|
||||
}
|
||||
if (candidateFont) {
|
||||
*aMatchType = candidateMatchType;
|
||||
}
|
||||
return candidateFont;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@ -3307,7 +3403,8 @@ void gfxFontGroup::ComputeRanges(nsTArray<TextRange>& aRanges, const T* aString,
|
||||
(!IsClusterExtender(ch) && ch != NARROW_NO_BREAK_SPACE &&
|
||||
!gfxFontUtils::IsJoinControl(ch) &&
|
||||
!gfxFontUtils::IsJoinCauser(prevCh) &&
|
||||
!gfxFontUtils::IsVarSelector(ch)))) {
|
||||
!gfxFontUtils::IsVarSelector(ch) &&
|
||||
GetEmojiPresentation(ch) == TextOnly))) {
|
||||
matchType = {FontMatchType::Kind::kFontGroup, mFonts[0].Generic()};
|
||||
} else {
|
||||
font =
|
||||
@ -3493,12 +3590,12 @@ bool gfxFontGroup::ContainsUserFont(const gfxUserFontEntry* aUserFont) {
|
||||
return false;
|
||||
}
|
||||
|
||||
gfxFont* gfxFontGroup::WhichPrefFontSupportsChar(uint32_t aCh,
|
||||
uint32_t aNextCh) {
|
||||
gfxFont* gfxFontGroup::WhichPrefFontSupportsChar(
|
||||
uint32_t aCh, uint32_t aNextCh, eFontPresentation aPresentation) {
|
||||
eFontPrefLang charLang;
|
||||
gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
|
||||
|
||||
if (ShouldPreferEmojiFont(aCh, aNextCh)) {
|
||||
if (aPresentation == eFontPresentation::Emoji) {
|
||||
charLang = eFontPrefLang_Emoji;
|
||||
} else {
|
||||
// get the pref font list if it hasn't been set up already
|
||||
@ -3574,8 +3671,10 @@ gfxFont* gfxFontGroup::WhichPrefFontSupportsChar(uint32_t aCh,
|
||||
// alternative face in the same family.
|
||||
if (!prefFont) {
|
||||
prefFont = family.mIsShared
|
||||
? FindFallbackFaceForChar(family.mShared, aCh)
|
||||
: FindFallbackFaceForChar(family.mUnshared, aCh);
|
||||
? FindFallbackFaceForChar(family.mShared, aCh, aNextCh,
|
||||
aPresentation)
|
||||
: FindFallbackFaceForChar(family.mUnshared, aCh, aNextCh,
|
||||
aPresentation);
|
||||
}
|
||||
if (prefFont) {
|
||||
mLastPrefFamily = family;
|
||||
@ -3593,14 +3692,15 @@ gfxFont* gfxFontGroup::WhichPrefFontSupportsChar(uint32_t aCh,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
gfxFont* gfxFontGroup::WhichSystemFontSupportsChar(uint32_t aCh,
|
||||
uint32_t aNextCh,
|
||||
Script aRunScript) {
|
||||
gfxFont* gfxFontGroup::WhichSystemFontSupportsChar(
|
||||
uint32_t aCh, uint32_t aNextCh, Script aRunScript,
|
||||
eFontPresentation aPresentation) {
|
||||
FontVisibility visibility;
|
||||
gfxFontEntry* fe =
|
||||
gfxFont* font =
|
||||
gfxPlatformFontList::PlatformFontList()->SystemFindFontForChar(
|
||||
aCh, aNextCh, aRunScript, &mStyle, &visibility, mFontMatchingStats);
|
||||
if (fe) {
|
||||
aCh, aNextCh, aRunScript, aPresentation, &mStyle, &visibility,
|
||||
mFontMatchingStats);
|
||||
if (font) {
|
||||
if (mFontMatchingStats) {
|
||||
switch (visibility) {
|
||||
case FontVisibility::Unknown:
|
||||
@ -3628,7 +3728,7 @@ gfxFont* gfxFontGroup::WhichSystemFontSupportsChar(uint32_t aCh,
|
||||
break;
|
||||
}
|
||||
}
|
||||
return fe->FindOrMakeFont(&mStyle);
|
||||
return font;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
|
@ -1113,10 +1113,12 @@ class gfxFontGroup final : public gfxTextRunFactory {
|
||||
|
||||
// search through pref fonts for a character, return nullptr if no matching
|
||||
// pref font
|
||||
gfxFont* WhichPrefFontSupportsChar(uint32_t aCh, uint32_t aNextCh);
|
||||
gfxFont* WhichPrefFontSupportsChar(uint32_t aCh, uint32_t aNextCh,
|
||||
eFontPresentation aPresentation);
|
||||
|
||||
gfxFont* WhichSystemFontSupportsChar(uint32_t aCh, uint32_t aNextCh,
|
||||
Script aRunScript);
|
||||
Script aRunScript,
|
||||
eFontPresentation aPresentation);
|
||||
|
||||
template <typename T>
|
||||
void ComputeRanges(nsTArray<TextRange>& aRanges, const T* aString,
|
||||
@ -1468,12 +1470,17 @@ class gfxFontGroup final : public gfxTextRunFactory {
|
||||
// Helper for font-matching:
|
||||
// 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
|
||||
gfxFont* FindFallbackFaceForChar(const FamilyFace& aFamily, uint32_t aCh);
|
||||
gfxFont* FindFallbackFaceForChar(const FamilyFace& aFamily, uint32_t aCh,
|
||||
uint32_t aNextCh,
|
||||
eFontPresentation aPresentation);
|
||||
|
||||
gfxFont* FindFallbackFaceForChar(mozilla::fontlist::Family* aFamily,
|
||||
uint32_t aCh);
|
||||
uint32_t aCh, uint32_t aNextCh,
|
||||
eFontPresentation aPresentation);
|
||||
|
||||
gfxFont* FindFallbackFaceForChar(gfxFontFamily* aFamily, uint32_t aCh);
|
||||
gfxFont* FindFallbackFaceForChar(gfxFontFamily* aFamily, uint32_t aCh,
|
||||
uint32_t aNextCh,
|
||||
eFontPresentation aPresentation);
|
||||
|
||||
// helper methods for looking up fonts
|
||||
|
||||
|
@ -663,9 +663,9 @@ static const char kFontUtsaah[] = "Utsaah";
|
||||
static const char kFontYuGothic[] = "Yu Gothic";
|
||||
|
||||
void gfxWindowsPlatform::GetCommonFallbackFonts(
|
||||
uint32_t aCh, uint32_t aNextCh, Script aRunScript,
|
||||
uint32_t aCh, Script aRunScript, eFontPresentation aPresentation,
|
||||
nsTArray<const char*>& aFontList) {
|
||||
if (ShouldPreferEmojiFont(aCh, aNextCh)) {
|
||||
if (aPresentation == eFontPresentation::Emoji) {
|
||||
aFontList.AppendElement(kFontSegoeUIEmoji);
|
||||
aFontList.AppendElement(kFontTwemojiMozilla);
|
||||
}
|
||||
|
@ -148,7 +148,8 @@ class gfxWindowsPlatform final : public gfxPlatform {
|
||||
*/
|
||||
void VerifyD2DDevice(bool aAttemptForce);
|
||||
|
||||
void GetCommonFallbackFonts(uint32_t aCh, uint32_t aNextCh, Script aRunScript,
|
||||
void GetCommonFallbackFonts(uint32_t aCh, Script aRunScript,
|
||||
eFontPresentation aPresentation,
|
||||
nsTArray<const char*>& aFontList) override;
|
||||
|
||||
bool CanUseHardwareVideoDecoding() override;
|
||||
|
@ -182,19 +182,6 @@ inline EmojiPresentation GetEmojiPresentation(uint32_t aCh) {
|
||||
return TextDefault;
|
||||
}
|
||||
|
||||
inline bool ShouldPreferEmojiFont(uint32_t aCh, uint32_t aNextCh) {
|
||||
EmojiPresentation emoji = GetEmojiPresentation(aCh);
|
||||
if (emoji != EmojiPresentation::TextOnly) {
|
||||
if (aNextCh == kVariationSelector16 ||
|
||||
(aNextCh != kVariationSelector15 &&
|
||||
emoji == EmojiPresentation::EmojiDefault) ||
|
||||
(aNextCh >= kEmojiSkinToneFirst && aNextCh <= kEmojiSkinToneLast)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// returns the simplified Gen Category as defined in nsUGenCategory
|
||||
inline nsUGenCategory GetGenCategory(uint32_t aCh) {
|
||||
return sDetailedToGeneralCategory[GetGeneralCategory(aCh)];
|
||||
|
@ -12,7 +12,7 @@
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id=test>⌚⌛🌀🌁</div>
|
||||
<div id=test>⌚︎⌛︎🌀︎🌁︎</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
@ -5,6 +5,10 @@
|
||||
<title>emoji fallback to text font</title>
|
||||
<style>
|
||||
#test {
|
||||
/* Because of the presence of the U+FE0E variation selectors, we should prefer the
|
||||
the monochrome fonts even though color-emoji fonts are listed first. */
|
||||
font-family: 'Segoe UI Emoji', 'Twemoji Mozilla', 'Apple Color Emoji', 'Noto Color Emoji',
|
||||
'Segoe UI Symbol', 'Apple Symbols', 'Noto Sans Symbols';
|
||||
font-size: 24pt;
|
||||
}
|
||||
</style>
|
||||
|
@ -5,7 +5,7 @@
|
||||
<title>emoji fallback to color font</title>
|
||||
<style>
|
||||
#test {
|
||||
font-family: 'Segoe UI Emoji', 'Apple Color Emoji', 'Twemoji Mozilla', 'Noto Color Emoji';
|
||||
font-family: 'Segoe UI Emoji', 'Twemoji Mozilla', 'Apple Color Emoji', 'Noto Color Emoji';
|
||||
font-size: 24pt;
|
||||
}
|
||||
</style>
|
||||
|
@ -5,6 +5,10 @@
|
||||
<title>emoji fallback to color font</title>
|
||||
<style>
|
||||
#test {
|
||||
/* Because of the presence of the U+FE0F variation selectors, we should find
|
||||
a color-emoji font even though the b/w symbol fonts are listed first. */
|
||||
font-family: 'Segoe UI Symbol', 'Apple Symbols', 'Noto Sans Symbols',
|
||||
'Segoe UI Emoji', 'Twemoji Mozilla', 'Apple Color Emoji', 'Noto Color Emoji';
|
||||
font-size: 24pt;
|
||||
}
|
||||
</style>
|
||||
|
@ -19,4 +19,4 @@ span {
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
P<span> (fail) </span>A<span>ሴ顶ꯍ</span>S<span>𐐀🌳</span>S
|
||||
P<span> (fail) </span>A<span>ሴ顶ꯍ</span>S<span>𐐀🌳︎</span>S
|
||||
|
@ -187,7 +187,7 @@ random-if(!winWidget) == arial-bold-lam-alef-1.html arial-bold-lam-alef-1-ref.ht
|
||||
fails-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == 1320665-cmap-format-13.html 1320665-cmap-format-13-ref.html # see bug 1320665 comments 8-9
|
||||
== 1331339-script-extensions-shaping-1.html 1331339-script-extensions-shaping-1-ref.html
|
||||
skip-if(!cocoaWidget) != 1349308-1.html 1349308-notref.html # macOS-specific test for -apple-system glyph metrics
|
||||
fuzzy-if(Android,0-128,0-233) fails-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu)) == 1463020-letter-spacing-text-transform-1.html 1463020-letter-spacing-text-transform-1-ref.html # Win10: regional indicators not supported by system emoji font
|
||||
fuzzy-if(Android,0-128,0-233) == 1463020-letter-spacing-text-transform-1.html 1463020-letter-spacing-text-transform-1-ref.html
|
||||
fails-if(Android) == 1463020-letter-spacing-text-transform-2.html 1463020-letter-spacing-text-transform-2-ref.html # missing font coverage on Android
|
||||
== 1507661-spurious-hyphenation-after-explicit.html 1507661-spurious-hyphenation-after-explicit-ref.html
|
||||
fuzzy-if(!webrender,12-66,288-1681) fails-if(gtkWidget&&!webrender) == 1522857-1.html 1522857-1-ref.html # antialiasing fuzz in non-webrender cases
|
||||
|
@ -21,7 +21,9 @@
|
||||
<script>
|
||||
<![CDATA[
|
||||
function text(n) {
|
||||
return "\u{1F310}".repeat(n);
|
||||
// Include U+FE0E variation selector to ensure font selection doesn't
|
||||
// seek out a color-emoji font in preference to Fira.
|
||||
return "\u{1F310}\u{FE0E}".repeat(n);
|
||||
}
|
||||
var e = "\u{2026}";
|
||||
document.getElementById("start").value = e + text(4);
|
||||
|
@ -21,7 +21,9 @@
|
||||
<script>
|
||||
<![CDATA[
|
||||
function text(n) {
|
||||
return "\u{1F310}".repeat(n);
|
||||
// Include U+FE0E variation selector to ensure font selection doesn't
|
||||
// seek out a color-emoji font in preference to Fira.
|
||||
return "\u{1F310}\u{FE0E}".repeat(n);
|
||||
}
|
||||
document.getElementById("start").value = text(10);
|
||||
document.getElementById("end").value = text(10);
|
||||
|
Loading…
Reference in New Issue
Block a user