/*
* Copyright (c) 2006, 2007, 2008, 2009, 2010, 2012 Google Inc. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif
#include "third_party/blink/renderer/platform/fonts/win/font_fallback_win.h"
#include <unicode/uchar.h>
#include <limits>
#include "base/check_op.h"
#include "third_party/blink/renderer/platform/fonts/font_cache.h"
#include "third_party/blink/renderer/platform/text/icu_error.h"
#include "third_party/blink/renderer/platform/wtf/hash_map.h"
#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
#include "third_party/skia/include/core/SkFontMgr.h"
#include "third_party/skia/include/core/SkTypeface.h"
namespace blink {
namespace {
inline bool IsFontPresent(const char* font_name_utf8,
const SkFontMgr& font_manager) {
sk_sp<SkTypeface> tf(
font_manager.matchFamilyStyle(font_name_utf8, SkFontStyle()));
if (!tf)
return false;
if (RuntimeEnabledFeatures::FontPresentWinEnabled()) {
return true;
}
const String font_name = String::FromUTF8(font_name_utf8);
SkTypeface::LocalizedStrings* actual_families =
tf->createFamilyNameIterator();
bool matches_requested_family = false;
SkTypeface::LocalizedString actual_family;
while (actual_families->next(&actual_family)) {
if (DeprecatedEqualIgnoringCase(
font_name, String::FromUTF8(actual_family.fString.c_str()))) {
matches_requested_family = true;
break;
}
}
actual_families->unref();
return matches_requested_family;
}
const char* FirstAvailableFont(
base::span<const char* const> candidate_family_names,
const SkFontMgr& font_manager) {
for (const char* family : candidate_family_names) {
if (IsFontPresent(family, font_manager)) {
return family;
}
}
return nullptr;
}
struct FontMapping {
const char* FirstAvailableFont(const SkFontMgr& font_manager) {
if (!candidate_family_names.empty()) {
family_name =
blink::FirstAvailableFont(candidate_family_names, font_manager);
candidate_family_names = {};
}
return family_name;
}
const char* family_name;
base::span<const char* const> candidate_family_names;
};
struct ScriptToFontFamilies {
UScriptCode script;
base::span<const char* const> families;
};
// A simple mapping from UScriptCode to family name. This is a sparse array,
// which works well since the range of UScriptCode values is small.
class ScriptToFontMap {
public:
static constexpr UScriptCode kSize = USCRIPT_CODE_LIMIT;
FontMapping& operator[](UScriptCode script) { return mappings_[script]; }
void Set(base::span<const ScriptToFontFamilies> families) {
for (const auto& family : families) {
mappings_[family.script].candidate_family_names = family.families;
}
}
private:
FontMapping mappings_[kSize];
};
const AtomicString& FindMonospaceFontForScript(UScriptCode script) {
if (script == USCRIPT_ARABIC || script == USCRIPT_HEBREW) {
DEFINE_THREAD_SAFE_STATIC_LOCAL(AtomicString, kCourierNew, ("courier new"));
return kCourierNew;
}
return g_null_atom;
}
void InitializeScriptFontMap(ScriptToFontMap& script_font_map) {
// For the following scripts, multiple fonts may be listed. They are tried
// in order. The first slot is preferred but the font may not be available,
// if so the remaining slots are tried in order.
// In general the order is the Windows 10 font follow by the 8.1, 8.0 and
// finally the font for Windows 7.
// For scripts where an optional or region specific font may be available
// that should be listed before the generic one.
// Based on the "Script and Font Support in Windows" MSDN documentation [1]
// with overrides and additional fallbacks as needed.
// 1: https://msdn.microsoft.com/en-us/goglobal/bb688099.aspx
static const char* const kArabicFonts[] = {"Tahoma", "Segoe UI"};
static const char* const kArmenianFonts[] = {"Segoe UI", "Sylfaen"};
static const char* const kBengaliFonts[] = {"Nirmala UI", "Vrinda"};
static const char* const kBrahmiFonts[] = {"Segoe UI Historic"};
static const char* const kBrailleFonts[] = {"Segoe UI Symbol"};
static const char* const kBugineseFonts[] = {"Leelawadee UI"};
static const char* const kCanadianAaboriginalFonts[] = {"Gadugi", "Euphemia"};
static const char* const kCarianFonts[] = {"Segoe UI Historic"};
static const char* const kCherokeeFonts[] = {"Gadugi", "Plantagenet"};
static const char* const kCopticFonts[] = {"Segoe UI Symbol"};
static const char* const kCuneiformFonts[] = {"Segoe UI Historic"};
static const char* const kCypriotFonts[] = {"Segoe UI Historic"};
static const char* const kCyrillicFonts[] = {"Times New Roman"};
static const char* const kDeseretFonts[] = {"Segoe UI Symbol"};
static const char* const kDevanagariFonts[] = {"Nirmala UI", "Mangal"};
static const char* const kEgyptianHieroglyphsFonts[] = {"Segoe UI Historic"};
static const char* const kEthiopicFonts[] = {"Nyala",
"Abyssinica SIL",
"Ethiopia Jiret",
"Visual Geez Unicode",
"GF Zemen Unicode",
"Ebrima"};
static const char* const kGeorgianFonts[] = {"Sylfaen", "Segoe UI"};
static const char* const kGlagoliticFonts[] = {"Segoe UI Historic",
"Segoe UI Symbol"};
static const char* const kGothicFonts[] = {"Segoe UI Historic",
"Segoe UI Symbol"};
static const char* const kGreekFonts[] = {"Times New Roman"};
static const char* const kGujaratiFonts[] = {"Nirmala UI", "Shruti"};
static const char* const kGurmukhiFonts[] = {"Nirmala UI", "Raavi"};
static const char* const kHangulFonts[] = {"Noto Sans KR", "Noto Sans CJK KR",
"Malgun Gothic", "Gulim"};
static const char* const kHangulFontsNoNoto[] = {"Malgun Gothic", "Gulim"};
static const char* const kHebrewFonts[] = {"David", "Segoe UI"};
static const char* const kImperialAramaicFonts[] = {"Segoe UI Historic"};
static const char* const kInscriptionalPahlaviFonts[] = {"Segoe UI Historic"};
static const char* const kInscriptionalParthianFonts[] = {
"Segoe UI Historic"};
static const char* const kJavaneseFonts[] = {"Javanese Text"};
static const char* const kKannadaFonts[] = {"Tunga", "Nirmala UI"};
static const char* const kKatakanaOrHiraganaFonts[] = {
"Noto Sans JP", "Noto Sans CJK JP", "Meiryo",
"Yu Gothic", "MS PGothic", "Microsoft YaHei"};
static const char* const kKatakanaOrHiraganaFontsNoNoto[] = {
"Meiryo", "Yu Gothic", "MS PGothic", "Microsoft YaHei"};
static const char* const kKharoshthiFonts[] = {"Segoe UI Historic"};
// Try Khmer OS before Vista fonts as it goes along better with Latin
// and looks better/larger for the same size.
static const char* const kKhmerFonts[] = {
"Leelawadee UI", "Khmer UI", "Khmer OS", "MoolBoran", "DaunPenh"};
static const char* const kLaoFonts[] = {"Leelawadee UI", "Lao UI",
"DokChampa", "Saysettha OT",
"Phetsarath OT", "Code2000"};
static const char* const kLatinFonts[] = {"Times New Roman"};
static const char* const kLisuFonts[] = {"Segoe UI"};
static const char* const kLycianFonts[] = {"Segoe UI Historic"};
static const char* const kLydianFonts[] = {"Segoe UI Historic"};
static const char* const kMalayalamFonts[] = {"Nirmala UI", "Kartika"};
static const char* const kMeroiticCursiveFonts[] = {"Segoe UI Historic",
"Segoe UI Symbol"};
static const char* const kMongolianFonts[] = {"Mongolian Baiti"};
static const char* const kMyanmarFonts[] = {
"Myanmar Text", "Padauk", "Parabaik", "Myanmar3", "Code2000"};
static const char* const kNewTaiLueFonts[] = {"Microsoft New Tai Lue"};
static const char* const kNkoFonts[] = {"Ebrima"};
static const char* const kOghamFonts[] = {"Segoe UI Historic",
"Segoe UI Symbol"};
static const char* const kOlChikiFonts[] = {"Nirmala UI"};
static const char* const kOldItalicFonts[] = {"Segoe UI Historic",
"Segoe UI Symbol"};
static const char* const kOldPersianFonts[] = {"Segoe UI Historic"};
static const char* const kOldSouthArabianFonts[] = {"Segoe UI Historic"};
static const char* const kOriyaFonts[] = {"Kalinga", "ori1Uni", "Lohit Oriya",
"Nirmala UI"};
static const char* const kOrkhonFonts[] = {"Segoe UI Historic",
"Segoe UI Symbol"};
static const char* const kOsmanyaFonts[] = {"Ebrima"};
static const char* const kPhagsPaFonts[] = {"Microsoft PhagsPa"};
static const char* const kRunicFonts[] = {"Segoe UI Historic",
"Segoe UI Symbol"};
static const char* const kShavianFonts[] = {"Segoe UI Historic"};
static const char* const kSimplifiedHanFonts[] = {
"Noto Sans SC", "Noto Sans CJK SC", "Microsoft YaHei", "simsun"};
static const char* const kSimplifiedHanFontsNoNoto[] = {"Microsoft YaHei",
"simsun"};
static const char* const kSinhalaFonts[] = {"Iskoola Pota", "AksharUnicode",
"Nirmala UI"};
static const char* const kSoraSompengFonts[] = {"Nirmala UI"};
static const char* const kSymbolsFonts[] = {"Segoe UI Symbol"};
static const char* const kSyriacFonts[] = {"Estrangelo Edessa",
"Estrangelo Nisibin", "Code2000"};
static const char* const kTaiLeFonts[] = {"Microsoft Tai Le"};
static const char* const kTamilFonts[] = {"Nirmala UI", "Latha"};
static const char* const kTeluguFonts[] = {"Nirmala UI", "Gautami"};
static const char* const kThaanaFonts[] = {"MV Boli"};
static const char* const kThaiFonts[] = {"Tahoma", "Leelawadee UI",
"Leelawadee"};
static const char* const kTibetanFonts[] = {"Microsoft Himalaya", "Jomolhari",
"Tibetan Machine Uni"};
static const char* const kTifinaghFonts[] = {"Ebrima"};
static const char* const kTraditionalHanFonts[] = {
"Noto Sans TC", "Noto Sans CJK TC", "Microsoft JhengHei", "pmingli"};
static const char* const kTraditionalHanFontsNoNoto[] = {"Microsoft JhengHei",
"pmingli"};
static const char* const kVaiFonts[] = {"Ebrima"};
static const char* const kYiFonts[] = {"Microsoft Yi Baiti", "Nuosu SIL",
"Code2000"};
static const ScriptToFontFamilies kScriptToFontFamilies[] = {
{USCRIPT_ARABIC, kArabicFonts},
{USCRIPT_ARMENIAN, kArmenianFonts},
{USCRIPT_BENGALI, kBengaliFonts},
{USCRIPT_BRAHMI, kBrahmiFonts},
{USCRIPT_BRAILLE, kBrailleFonts},
{USCRIPT_BUGINESE, kBugineseFonts},
{USCRIPT_CANADIAN_ABORIGINAL, kCanadianAaboriginalFonts},
{USCRIPT_CARIAN, kCarianFonts},
{USCRIPT_CHEROKEE, kCherokeeFonts},
{USCRIPT_COPTIC, kCopticFonts},
{USCRIPT_CUNEIFORM, kCuneiformFonts},
{USCRIPT_CYPRIOT, kCypriotFonts},
{USCRIPT_CYRILLIC, kCyrillicFonts},
{USCRIPT_DESERET, kDeseretFonts},
{USCRIPT_DEVANAGARI, kDevanagariFonts},
{USCRIPT_EGYPTIAN_HIEROGLYPHS, kEgyptianHieroglyphsFonts},
{USCRIPT_ETHIOPIC, kEthiopicFonts},
{USCRIPT_GEORGIAN, kGeorgianFonts},
{USCRIPT_GLAGOLITIC, kGlagoliticFonts},
{USCRIPT_GOTHIC, kGothicFonts},
{USCRIPT_GREEK, kGreekFonts},
{USCRIPT_GUJARATI, kGujaratiFonts},
{USCRIPT_GURMUKHI, kGurmukhiFonts},
{USCRIPT_HANGUL, kHangulFonts},
{USCRIPT_HEBREW, kHebrewFonts},
{USCRIPT_HIRAGANA, kKatakanaOrHiraganaFonts},
{USCRIPT_IMPERIAL_ARAMAIC, kImperialAramaicFonts},
{USCRIPT_INSCRIPTIONAL_PAHLAVI, kInscriptionalPahlaviFonts},
{USCRIPT_INSCRIPTIONAL_PARTHIAN, kInscriptionalParthianFonts},
{USCRIPT_JAVANESE, kJavaneseFonts},
{USCRIPT_KANNADA, kKannadaFonts},
{USCRIPT_KATAKANA, kKatakanaOrHiraganaFonts},
{USCRIPT_KATAKANA_OR_HIRAGANA, kKatakanaOrHiraganaFonts},
{USCRIPT_KHAROSHTHI, kKharoshthiFonts},
{USCRIPT_KHMER, kKhmerFonts},
{USCRIPT_LAO, kLaoFonts},
{USCRIPT_LATIN, kLatinFonts},
{USCRIPT_LISU, kLisuFonts},
{USCRIPT_LYCIAN, kLycianFonts},
{USCRIPT_LYDIAN, kLydianFonts},
{USCRIPT_MALAYALAM, kMalayalamFonts},
{USCRIPT_MEROITIC_CURSIVE, kMeroiticCursiveFonts},
{USCRIPT_MONGOLIAN, kMongolianFonts},
{USCRIPT_MYANMAR, kMyanmarFonts},
{USCRIPT_NEW_TAI_LUE, kNewTaiLueFonts},
{USCRIPT_NKO, kNkoFonts},
{USCRIPT_OGHAM, kOghamFonts},
{USCRIPT_OL_CHIKI, kOlChikiFonts},
{USCRIPT_OLD_ITALIC, kOldItalicFonts},
{USCRIPT_OLD_PERSIAN, kOldPersianFonts},
{USCRIPT_OLD_SOUTH_ARABIAN, kOldSouthArabianFonts},
{USCRIPT_ORIYA, kOriyaFonts},
{USCRIPT_ORKHON, kOrkhonFonts},
{USCRIPT_OSMANYA, kOsmanyaFonts},
{USCRIPT_PHAGS_PA, kPhagsPaFonts},
{USCRIPT_RUNIC, kRunicFonts},
{USCRIPT_SHAVIAN, kShavianFonts},
{USCRIPT_SIMPLIFIED_HAN, kSimplifiedHanFonts},
{USCRIPT_SINHALA, kSinhalaFonts},
{USCRIPT_SORA_SOMPENG, kSoraSompengFonts},
{USCRIPT_SYMBOLS, kSymbolsFonts},
{USCRIPT_SYRIAC, kSyriacFonts},
{USCRIPT_TAI_LE, kTaiLeFonts},
{USCRIPT_TAMIL, kTamilFonts},
{USCRIPT_TELUGU, kTeluguFonts},
{USCRIPT_THAANA, kThaanaFonts},
{USCRIPT_THAI, kThaiFonts},
{USCRIPT_TIBETAN, kTibetanFonts},
{USCRIPT_TIFINAGH, kTifinaghFonts},
{USCRIPT_TRADITIONAL_HAN, kTraditionalHanFonts},
{USCRIPT_VAI, kVaiFonts},
{USCRIPT_YI, kYiFonts}};
script_font_map.Set(kScriptToFontFamilies);
if (!RuntimeEnabledFeatures::FontSystemFallbackNotoCjkEnabled())
[[unlikely]] {
const ScriptToFontFamilies no_noto[] = {
{USCRIPT_HANGUL, kHangulFontsNoNoto},
{USCRIPT_HIRAGANA, kKatakanaOrHiraganaFontsNoNoto},
{USCRIPT_KATAKANA, kKatakanaOrHiraganaFontsNoNoto},
{USCRIPT_KATAKANA_OR_HIRAGANA, kKatakanaOrHiraganaFontsNoNoto},
{USCRIPT_SIMPLIFIED_HAN, kSimplifiedHanFontsNoNoto},
{USCRIPT_TRADITIONAL_HAN, kTraditionalHanFontsNoNoto},
};
script_font_map.Set(no_noto);
}
// Initialize the locale-dependent mapping from system locale.
UScriptCode han_script = LayoutLocale::GetSystem().GetScriptForHan();
DCHECK_NE(han_script, USCRIPT_HAN);
const FontMapping& han_mapping = script_font_map[han_script];
if (!han_mapping.candidate_family_names.empty()) {
script_font_map[USCRIPT_HAN].candidate_family_names =
han_mapping.candidate_family_names;
}
}
// There are a lot of characters in USCRIPT_COMMON that can be covered
// by fonts for scripts closely related to them. See
// http://unicode.org/cldr/utility/list-unicodeset.jsp?a=[:Script=Common:]
// FIXME: make this more efficient with a wider coverage
UScriptCode GetScriptBasedOnUnicodeBlock(int ucs4) {
UBlockCode block = ublock_getCode(ucs4);
switch (block) {
case UBLOCK_CJK_SYMBOLS_AND_PUNCTUATION:
return USCRIPT_HAN;
case UBLOCK_HIRAGANA:
case UBLOCK_KATAKANA:
return USCRIPT_KATAKANA_OR_HIRAGANA;
case UBLOCK_ARABIC:
return USCRIPT_ARABIC;
case UBLOCK_THAI:
return USCRIPT_THAI;
case UBLOCK_GREEK:
return USCRIPT_GREEK;
case UBLOCK_DEVANAGARI:
// For Danda and Double Danda (U+0964, U+0965), use a Devanagari
// font for now although they're used by other scripts as well.
// Without a context, we can't do any better.
return USCRIPT_DEVANAGARI;
case UBLOCK_ARMENIAN:
return USCRIPT_ARMENIAN;
case UBLOCK_GEORGIAN:
return USCRIPT_GEORGIAN;
case UBLOCK_KANNADA:
return USCRIPT_KANNADA;
case UBLOCK_GOTHIC:
return USCRIPT_GOTHIC;
default:
return USCRIPT_COMMON;
}
}
UScriptCode GetScript(int ucs4) {
ICUError err;
UScriptCode script = uscript_getScript(ucs4, &err);
// If script is invalid, common or inherited or there's an error,
// infer a script based on the unicode block of a character.
if (script <= USCRIPT_INHERITED || U_FAILURE(err))
script = GetScriptBasedOnUnicodeBlock(ucs4);
return script;
}
const char* AvailableColorEmojiFont(const SkFontMgr& font_manager) {
static const char* const kEmojiFonts[] = {"Segoe UI Emoji",
"Segoe UI Symbol"};
static const char* emoji_font = nullptr;
// `std::once()` may cause hangs. crbug.com/349456407
static bool initialized = false;
if (!initialized) {
emoji_font = FirstAvailableFont(kEmojiFonts, font_manager);
initialized = true;
}
return emoji_font;
}
const char* AvailableMonoEmojiFont(const SkFontMgr& font_manager) {
static const char* const kEmojiFonts[] = {"Segoe UI Symbol",
"Segoe UI Emoji"};
static const char* emoji_font = nullptr;
// `std::once()` may cause hangs. crbug.com/349456407
static bool initialized = false;
if (!initialized) {
emoji_font = FirstAvailableFont(kEmojiFonts, font_manager);
initialized = true;
}
return emoji_font;
}
const char* FirstAvailableMathFont(const SkFontMgr& font_manager) {
static const char* const kMathFonts[] = {"Cambria Math", "Segoe UI Symbol",
"Code2000"};
static const char* math_font = nullptr;
// `std::once()` may cause hangs. crbug.com/349456407
static bool initialized = false;
if (!initialized) {
math_font = FirstAvailableFont(kMathFonts, font_manager);
initialized = true;
}
return math_font;
}
const AtomicString& GetColorEmojiFont(const SkFontMgr& font_manager) {
// Calling `AvailableColorEmojiFont()` from `DEFINE_THREAD_SAFE_STATIC_LOCAL`
// may cause hangs. crbug.com/349456407
DEFINE_THREAD_SAFE_STATIC_LOCAL(AtomicString, emoji_font, (g_empty_atom));
if (emoji_font.empty() && !emoji_font.IsNull()) {
emoji_font = AtomicString(AvailableColorEmojiFont(font_manager));
CHECK(!emoji_font.empty() || emoji_font.IsNull());
}
return emoji_font;
}
const AtomicString& GetMonoEmojiFont(const SkFontMgr& font_manager) {
// Calling `AvailableMonoEmojiFont()` from `DEFINE_THREAD_SAFE_STATIC_LOCAL`
// may cause hangs. crbug.com/349456407
DEFINE_THREAD_SAFE_STATIC_LOCAL(AtomicString, emoji_font, (g_empty_atom));
if (emoji_font.empty() && !emoji_font.IsNull()) {
emoji_font = AtomicString(AvailableMonoEmojiFont(font_manager));
CHECK(!emoji_font.empty() || emoji_font.IsNull());
}
return emoji_font;
}
const AtomicString& GetMathFont(const SkFontMgr& font_manager) {
// Calling `AvailableMonoEmojiFont()` from `DEFINE_THREAD_SAFE_STATIC_LOCAL`
// may cause hangs. crbug.com/349456407
DEFINE_THREAD_SAFE_STATIC_LOCAL(AtomicString, math_font, (g_empty_atom));
if (math_font.empty() && !math_font.IsNull()) {
math_font = AtomicString(FirstAvailableMathFont(font_manager));
CHECK(!math_font.empty() || math_font.IsNull());
}
return math_font;
}
const AtomicString& GetFontBasedOnUnicodeBlock(UBlockCode block_code,
const SkFontMgr& font_manager) {
switch (block_code) {
case UBLOCK_EMOTICONS:
case UBLOCK_ENCLOSED_ALPHANUMERIC_SUPPLEMENT:
// We call this function only when FallbackPriority is not kEmojiEmoji,
// so we need a text presentation of emoji.
return GetMonoEmojiFont(font_manager);
case UBLOCK_PLAYING_CARDS:
case UBLOCK_MISCELLANEOUS_SYMBOLS:
case UBLOCK_MISCELLANEOUS_SYMBOLS_AND_ARROWS:
case UBLOCK_MISCELLANEOUS_SYMBOLS_AND_PICTOGRAPHS:
case UBLOCK_TRANSPORT_AND_MAP_SYMBOLS:
case UBLOCK_ALCHEMICAL_SYMBOLS:
case UBLOCK_DINGBATS:
case UBLOCK_GOTHIC: {
DEFINE_THREAD_SAFE_STATIC_LOCAL(AtomicString, kSymbolFont,
("Segoe UI Symbol"));
return kSymbolFont;
}
case UBLOCK_ARROWS:
case UBLOCK_MATHEMATICAL_OPERATORS:
case UBLOCK_MISCELLANEOUS_TECHNICAL:
case UBLOCK_GEOMETRIC_SHAPES:
case UBLOCK_MISCELLANEOUS_MATHEMATICAL_SYMBOLS_A:
case UBLOCK_SUPPLEMENTAL_ARROWS_A:
case UBLOCK_SUPPLEMENTAL_ARROWS_B:
case UBLOCK_MISCELLANEOUS_MATHEMATICAL_SYMBOLS_B:
case UBLOCK_SUPPLEMENTAL_MATHEMATICAL_OPERATORS:
case UBLOCK_MATHEMATICAL_ALPHANUMERIC_SYMBOLS:
case UBLOCK_ARABIC_MATHEMATICAL_ALPHABETIC_SYMBOLS:
case UBLOCK_GEOMETRIC_SHAPES_EXTENDED:
return GetMathFont(font_manager);
default:
return g_null_atom;
}
}
} // namespace
// FIXME: this is font fallback code version 0.1
// - Cover all the scripts
// - Get the default font for each script/generic family from the
// preference instead of hardcoding in the source.
// (at least, read values from the registry for IE font settings).
// - Support generic families (from FontDescription)
// - If the default font for a script is not available,
// try some more fonts known to support it. Finally, we can
// use EnumFontFamilies or similar APIs to come up with a list of
// fonts supporting the script and cache the result.
// - Consider using UnicodeSet (or UnicodeMap) converted from
// GLYPHSET (BMP) or directly read from truetype cmap tables to
// keep track of which character is supported by which font
// - Update script_font_cache in response to WM_FONTCHANGE
const AtomicString& GetFontFamilyForScript(
UScriptCode script,
FontDescription::GenericFamilyType generic,
const SkFontMgr& font_manager) {
if (script < 0 || script >= ScriptToFontMap::kSize) [[unlikely]] {
return g_null_atom;
}
if (generic == FontDescription::kMonospaceFamily) {
if (const AtomicString& family = FindMonospaceFontForScript(script)) {
return family;
}
}
// Try the `AtomicString` cache first. `AtomicString` must be per thread, and
// thus it can't be added to `ScriptToFontMap`.
struct AtomicFamilies {
std::optional<AtomicString> families[ScriptToFontMap::kSize];
};
DEFINE_THREAD_SAFE_STATIC_LOCAL(AtomicFamilies, families, ());
std::optional<AtomicString>& family = families.families[script];
if (family) {
return *family;
}
static ScriptToFontMap script_font_map;
static std::once_flag once_flag;
std::call_once(once_flag, [] { InitializeScriptFontMap(script_font_map); });
family.emplace(script_font_map[script].FirstAvailableFont(font_manager));
return *family;
}
// FIXME:
// - Handle 'Inherited', 'Common' and 'Unknown'
// (see http://www.unicode.org/reports/tr24/#Usage_Model )
// For 'Inherited' and 'Common', perhaps we need to
// accept another parameter indicating the previous family
// and just return it.
// - All the characters (or characters up to the point a single
// font can cover) need to be taken into account
const AtomicString& GetFallbackFamily(
UChar32 character,
FontDescription::GenericFamilyType generic,
const LayoutLocale* content_locale,
FontFallbackPriority fallback_priority,
const SkFontMgr& font_manager,
UScriptCode& script_out) {
DCHECK(character);
if (fallback_priority == FontFallbackPriority::kEmojiEmoji) [[unlikely]] {
if (const AtomicString& family = GetColorEmojiFont(font_manager)) {
script_out = USCRIPT_INVALID_CODE;
return family;
}
} else {
const UBlockCode block = ublock_getCode(character);
if (const AtomicString& family =
GetFontBasedOnUnicodeBlock(block, font_manager)) {
script_out = USCRIPT_INVALID_CODE;
return family;
}
}
UScriptCode script = GetScript(character);
// For the full-width ASCII characters (U+FF00 - U+FF5E), use the font for
// Han (determined in a locale-dependent way above). Full-width ASCII
// characters are rather widely used in Japanese and Chinese documents and
// they're fully covered by Chinese, Japanese and Korean fonts.
if (0xFF00 < character && character < 0xFF5F)
script = USCRIPT_HAN;
if (script == USCRIPT_COMMON)
script = GetScriptBasedOnUnicodeBlock(character);
// For unified-Han scripts, try the lang attribute, system, or
// accept-languages.
if (script == USCRIPT_HAN) {
if (const LayoutLocale* locale_for_han =
LayoutLocale::LocaleForHan(content_locale))
script = locale_for_han->GetScriptForHan();
// If still unknown, USCRIPT_HAN uses UI locale.
// See initializeScriptFontMap().
}
script_out = script;
// TODO(kojii): Limiting `GetFontFamilyForScript()` only to BMP may need
// review to match the modern environment. This was done in 2010 for
// https://bugs.webkit.org/show_bug.cgi?id=35605.
if (character <= 0xFFFF) {
if (const AtomicString& family =
GetFontFamilyForScript(script, generic, font_manager)) {
return family;
}
}
// Another lame work-around to cover non-BMP characters.
// If the font family for script is not found or the character is
// not in BMP (> U+FFFF), we resort to the hard-coded list of
// fallback fonts for now.
int plane = character >> 16;
switch (plane) {
case 1: {
DEFINE_THREAD_SAFE_STATIC_LOCAL(AtomicString, kPlane1, ("code2001"));
return kPlane1;
}
case 2:
// Use a Traditional Chinese ExtB font if in Traditional Chinese locale.
// Otherwise, use a Simplified Chinese ExtB font. Windows Japanese
// fonts do support a small subset of ExtB (that are included in JIS X
// 0213), but its coverage is rather sparse.
// Eventually, this should be controlled by lang/xml:lang.
if (icu::Locale::getDefault() == icu::Locale::getTraditionalChinese()) {
DEFINE_THREAD_SAFE_STATIC_LOCAL(AtomicString, kPlane2zht,
("pmingliu-extb"));
return kPlane2zht;
}
DEFINE_THREAD_SAFE_STATIC_LOCAL(AtomicString, kPlane2zhs,
("simsun-extb"));
return kPlane2zhs;
}
DEFINE_THREAD_SAFE_STATIC_LOCAL(AtomicString, kLastResort,
("lucida sans unicode"));
return kLastResort;
}
} // namespace blink