Commit 671511b0 authored by Timothy Gu's avatar Timothy Gu Committed by Commit Bot

Set Linux fallback font locale for emojis to und-Zsye

Prior to this CL, Chrome on desktop Linux often inconsistently used
contour fonts (e.g., DejaVu Sans and Noto Sans Symbols2) for some
emojis, even when a dedicated emoji font (e.g., Noto Color Emoji) is
available and used for all other emojis. In order to force the color
emoji font to be used always, hacks [1] have been mentioned in online
forums that prepend the color emoji font to the list of system fonts in
fonts.conf, which can have highly deletorious effects on other
applications (like normal numerals becoming their emoji versions).

The ISO locale code "und-Zsye" [2] is one of the ways of informing
fontconfig (as of version 2.12.5) that color emoji fonts should be
preferred, along with the special font family "emoji" [3]. fontconfig
also has default configurations that automatically map the locale and
the "emoji" family to any available color emoji font. By explicitly
setting the locale to "und-Zsye", this CL provides the desired
out-of-the-box experience to desktop Linux users.

While we could also specify the "emoji" font family either instead of or
in addition to the locale, doing so requires more plumbing in the code
and may be done in the future.

[1]: https://github.com/googlefonts/noto-emoji/issues/36
[2]: https://unicode.org/reports/tr51/#Emoji_Script
[3]: https://bugs.freedesktop.org/show_bug.cgi?id=94551

Bug: 767754
Change-Id: Ie8019cc0540a177c816c8131ca7c6a504952aa09
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2207627
Commit-Queue: Timothy Gu <timothygu@chromium.org>
Reviewed-by: default avatarDominik Röttsches <drott@chromium.org>
Cr-Commit-Position: refs/heads/master@{#772414}
parent 7b7c10b1
......@@ -52,12 +52,12 @@ Blink uses the following prioritized list to determine the script.
This result is available at `ComputedStyle::getFontDescription().localeOrDefault().script()`.
[generic-family]: https://drafts.csswg.org/css-fonts-3/#generic-family-value
[generic-family]: https://drafts.csswg.org/css-fonts/#generic-family-value
[Advanced Font Settings]: https://chrome.google.com/webstore/detail/advanced-font-settings/caclkomlalccbpcdllchkeecicepbmbm
## System Font Fallback
## Installed Font Fallback
[CSS Fonts] defines a concept of [system font fallback],
[CSS Fonts] defines a concept of [installed font fallback],
though its behavior is UA dependent.
As Blink tries to match the font fallback behavior
......@@ -66,8 +66,27 @@ the logic varies by platforms.
While the complete logic varies by platforms,
we try to share parts of the logic where possible.
[CSS Fonts]: https://drafts.csswg.org/css-fonts-3/
[system font fallback]: https://drafts.csswg.org/css-fonts-3/#system-font-fallback
[CSS Fonts]: https://drafts.csswg.org/css-fonts/
[installed font fallback]: https://drafts.csswg.org/css-fonts/#installed-font-fallback
### Emojis
If we've determined that a character is [emoji-default], also known as "emoji
in emoji" representation, we treat the character a bit differently. The goal is
to not only find a font that supports emojis, but also to prioritize color
emoji fonts over traditional monochrome fonts that happen to have the glyph.
On Android/Skia, Linux, and Windows, Blink will pass the special locale
`und-Zsye` to the operating system when looking for an emoji font. The [Zsye]
script tag is defined by UTS #51 as "prefer emoji style for characters that
have both text and emoji styles available", which is precisely what we need.
On Linux, Blink will additionally always use U+1F46A FAMILY (👪) when matching
potential candidates to increase the odds of finding the right emoji font, in
case the installed emoji font doesn't support the actual emoji in question.
[emoji-default]: https://unicode.org/reports/tr51/#Presentation_Style
[Zsye]: https://unicode.org/reports/tr51/#Emoji_Script
### Unified Han Ideographs
......@@ -75,7 +94,7 @@ As seen in [CJK Unified Ideographs code charts] in Unicode,
glyphs of Han Ideographs vary by locales.
To render correct glyphs,
the system font fallback uses the following prioritized list of locales.
the installed font fallback uses the following prioritized list of locales.
1. The [language of a node] as defined in HTML, if known.
2. The list of languages the browser sends in the [Accept-Language] header.
......@@ -89,7 +108,7 @@ For this purpose,
`LayoutLocale::hasScriptForHan()` determines whether
the locale can choose the correct font for the Unified Han Ideographs or not.
When the system font fallback needs to determine the font
When the installed font fallback needs to determine the font
for a Unified Han Ideograph,
it uses `scriptForHan()` of the first locale in the prioritized list
that has `hasScriptForHan()` true.
......
......@@ -157,7 +157,7 @@ Emoji place additional requirements in isolating sub-runs for shaping. Emoji
Unicode code points and code point sequences have different default presentation
styles, text-default, or emoji-default. This is defined in the
section
[Presentation Style of Unicode Technical Report #51](http://unicode.org/draft/reports/tr51/tr51.html#Presentation_Style). So
[Presentation Style of Unicode Technical Standard #51](https://unicode.org/reports/tr51/#Presentation_Style). So
in order to select the correct font for emoji presentation — either a color
font, or a regular contour font — the incoming text needs to be segmented and
isolated by its emoji properties as well.
......@@ -359,7 +359,9 @@ fonts are searched next. This behavior matches the requirements of the font
style matching algorithm of
the
[CSS Fonts specification](https://drafts.csswg.org/css-fonts/#font-style-matching),
which mandates to prioritize web fonts over system fonts.
which mandates to prioritize web fonts over system fonts. Some additional
details can be found in
[LocaleInFonts.md](https://chromium.googlesource.com/chromium/src/+/master/third_party/blink/renderer/platform/fonts/LocaleInFonts.md#Installed-Font-Fallback).
`FontFallbackIterator` is intialized with a `FontFallbackList` and starts
retrieving fonts from this list as its first source for fonts. If during shaping
......
......@@ -76,10 +76,7 @@ const base::Feature kFontCacheNoSizeInKey{"FontCacheNoSizeInKey",
base::FEATURE_DISABLED_BY_DEFAULT};
}
// Special locale for retrieving the color emoji font based on the proposed
// changes in UTR #51 for introducing an Emoji script code:
// https://unicode.org/reports/tr51/#Emoji_Script
static const char kColorEmojiLocale[] = "und-Zsye";
const char kColorEmojiLocale[] = "und-Zsye";
SkFontMgr* FontCache::static_font_manager_ = nullptr;
......
......@@ -110,6 +110,10 @@ typedef HashMap<FallbackListCompositeKey,
FallbackListShaperCache;
typedef std::vector<FontEnumerationEntry> FontEnumerationCache;
// "und-Zsye", the special locale for retrieving the color emoji font defined
// in UTS #51: https://unicode.org/reports/tr51/#Emoji_Script
extern const char kColorEmojiLocale[];
class PLATFORM_EXPORT FontCache {
friend class FontCachePurgePreventer;
......
......@@ -4,6 +4,10 @@
#include "third_party/blink/renderer/platform/fonts/font_cache.h"
#include <unicode/unistr.h>
#include <string>
#include <tuple>
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/platform/platform.h"
......@@ -43,6 +47,57 @@ TEST(FontCache, NoFallbackForPrivateUseArea) {
}
}
#if defined(OS_LINUX)
TEST(FontCache, FallbackForEmojis) {
FontCache* font_cache = FontCache::GetFontCache();
ASSERT_TRUE(font_cache);
FontCachePurgePreventer purge_preventer;
FontDescription font_description;
font_description.SetGenericFamily(FontDescription::kStandardFamily);
static constexpr char kNotoColorEmoji[] = "Noto Color Emoji";
// We should use structured binding when it becomes available...
for (auto info : {
std::pair<UChar32, bool>{U'☺', true},
{U'👪', true},
{U'🤣', false},
}) {
UChar32 character = info.first;
// Set to true if the installed contour fonts support this glyph.
bool available_in_contour_font = info.second;
std::string character_utf8;
icu::UnicodeString(character).toUTF8String(character_utf8);
{
scoped_refptr<SimpleFontData> font_data =
font_cache->FallbackFontForCharacter(
font_description, character, nullptr,
FontFallbackPriority::kEmojiEmoji);
EXPECT_EQ(font_data->PlatformData().FontFamilyName(), kNotoColorEmoji)
<< "Character " << character_utf8
<< " doesn't match what we expected for kEmojiEmoji.";
}
{
scoped_refptr<SimpleFontData> font_data =
font_cache->FallbackFontForCharacter(
font_description, character, nullptr,
FontFallbackPriority::kEmojiText);
if (available_in_contour_font) {
EXPECT_NE(font_data->PlatformData().FontFamilyName(), kNotoColorEmoji)
<< "Character " << character_utf8
<< " doesn't match what we expected for kEmojiText.";
} else {
EXPECT_EQ(font_data->PlatformData().FontFamilyName(), kNotoColorEmoji)
<< "Character " << character_utf8
<< " doesn't match what we expected for kEmojiText.";
}
}
}
}
#endif // defined(OS_LINUX)
TEST(FontCache, firstAvailableOrFirst) {
EXPECT_TRUE(FontCache::FirstAvailableOrFirst("").IsEmpty());
EXPECT_TRUE(FontCache::FirstAvailableOrFirst(String()).IsEmpty());
......
......@@ -104,7 +104,10 @@ scoped_refptr<SimpleFontData> FontCache::PlatformFallbackFontForCharacter(
gfx::FallbackFontData fallback_font;
if (!FontCache::GetFontForCharacter(
c, font_description.LocaleOrDefault().Ascii().c_str(),
c,
fallback_priority == FontFallbackPriority::kEmojiEmoji
? kColorEmojiLocale
: font_description.LocaleOrDefault().Ascii().c_str(),
&fallback_font))
return nullptr;
......
......@@ -154,7 +154,6 @@ sk_sp<SkTypeface> FindUniqueFontNameFromSideloadedFonts(
return return_typeface;
}
static const char kColorEmojiLocale[] = "und-Zsye";
static const char kChineseSimplified[] = "zh-Hant";
// For Windows out-of-process fallback calls, there is a limiation: only one
......
......@@ -25,9 +25,8 @@
🌱🌲🌳🌴🌵🌷🌸🌹🌺🌻🌼💐🌾🌿🍀🍁🍂🍃🍄🌰☺️😀👪
#emoji:
"Noto Color Emoji" : 21,
"Tinos" : 2,
"Noto Sans Symbols2" : 1
"Noto Color Emoji" : 22,
"Tinos" : 2
𓀀𓀁𓀂𓀃𓀄𓀅𓀆𓀇𓀈𓀉𓀊𓀋𓀌𓀍𓀎𓀏
#egyptian_hieroglyphs:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment