#include "ui/gfx/font_fallback_win.h"
#include <algorithm>
#include <map>
#include <string_view>
#include "base/memory/singleton.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/current_thread.h"
#include "base/trace_event/trace_event.h"
#include "base/win/registry.h"
#include "ui/gfx/font.h"
#include "ui/gfx/font_fallback.h"
#include "ui/gfx/font_fallback_skia_impl.h"
#include "ui/gfx/platform_font.h"
namespace gfx {
namespace {
void QueryFontsFromRegistry(std::map<std::string, std::string>* map) {
const wchar_t* kFonts =
L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts";
base::win::RegistryValueIterator it(HKEY_LOCAL_MACHINE, kFonts);
for (; it.Valid(); ++it) {
const std::string filename =
base::ToLowerASCII(base::WideToUTF8(it.Value()));
(*map)[filename] = base::WideToUTF8(it.Name());
}
}
void GetFontNamesFromFilename(const std::string& filename,
std::map<std::string, std::string>* font_map,
std::vector<std::string>* font_names) {
if (font_map->empty())
QueryFontsFromRegistry(font_map);
std::map<std::string, std::string>::const_iterator it =
font_map->find(base::ToLowerASCII(filename));
if (it == font_map->end())
return;
internal::ParseFontFamilyString(it->second, font_names);
}
bool ContainsOnlyDigits(const std::string& text) {
return text.find_first_not_of("0123456789") == std::u16string::npos;
}
void AppendFont(const std::string& name, int size, std::vector<Font>* fonts) {
if (fonts->empty() || fonts->back().GetFontName() != name)
fonts->push_back(Font(name, size));
}
void QueryLinkedFontsFromRegistry(const Font& font,
std::map<std::string, std::string>* font_map,
std::vector<Font>* linked_fonts) {
const wchar_t* kSystemLink =
L"Software\\Microsoft\\Windows NT\\CurrentVersion\\FontLink\\SystemLink";
base::win::RegKey key;
if (FAILED(key.Open(HKEY_LOCAL_MACHINE, kSystemLink, KEY_READ)))
return;
const std::wstring original_font_name = base::UTF8ToWide(font.GetFontName());
std::vector<std::wstring> values;
if (FAILED(key.ReadValues(original_font_name.c_str(), &values))) {
key.Close();
return;
}
std::string filename;
std::string font_name;
for (size_t i = 0; i < values.size(); ++i) {
internal::ParseFontLinkEntry(
base::WideToUTF8(values[i]), &filename, &font_name);
if (!font_name.empty()) {
AppendFont(font_name, font.GetFontSize(), linked_fonts);
} else if (!filename.empty()) {
std::vector<std::string> filename_fonts;
GetFontNamesFromFilename(filename, font_map, &filename_fonts);
for (const std::string& filename_font : filename_fonts)
AppendFont(filename_font, font.GetFontSize(), linked_fonts);
}
}
key.Close();
}
class CachedFontLinkSettings {
public:
static CachedFontLinkSettings* GetInstance();
CachedFontLinkSettings(const CachedFontLinkSettings&) = delete;
CachedFontLinkSettings& operator=(const CachedFontLinkSettings&) = delete;
const std::vector<Font>* GetLinkedFonts(const Font& font);
private:
friend struct base::DefaultSingletonTraits<CachedFontLinkSettings>;
CachedFontLinkSettings();
virtual ~CachedFontLinkSettings();
std::map<std::string, std::string> cached_system_fonts_;
std::map<std::string, std::vector<Font> > cached_linked_fonts_;
};
CachedFontLinkSettings* CachedFontLinkSettings::GetInstance() {
return base::Singleton<
CachedFontLinkSettings,
base::LeakySingletonTraits<CachedFontLinkSettings>>::get();
}
const std::vector<Font>* CachedFontLinkSettings::GetLinkedFonts(
const Font& font) {
const std::string& font_name = font.GetFontName();
std::map<std::string, std::vector<Font> >::const_iterator it =
cached_linked_fonts_.find(font_name);
if (it != cached_linked_fonts_.end())
return &it->second;
std::vector<Font>* linked_fonts = &cached_linked_fonts_[font_name];
QueryLinkedFontsFromRegistry(font, &cached_system_fonts_, linked_fonts);
return linked_fonts;
}
CachedFontLinkSettings::CachedFontLinkSettings() {
}
CachedFontLinkSettings::~CachedFontLinkSettings() {
}
}
namespace internal {
void ParseFontLinkEntry(const std::string& entry,
std::string* filename,
std::string* font_name) {
std::vector<std::string> parts = base::SplitString(
entry, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
filename->clear();
font_name->clear();
if (parts.size() > 0)
*filename = parts[0];
if (parts.size() > 1 && !ContainsOnlyDigits(parts[1]))
*font_name = parts[1];
}
void ParseFontFamilyString(const std::string& family,
std::vector<std::string>* font_names) {
*font_names = base::SplitString(family, "&", base::TRIM_WHITESPACE,
base::SPLIT_WANT_ALL);
if (!font_names->empty()) {
const size_t index = font_names->back().find('(');
if (index != std::string::npos) {
font_names->back().resize(index);
base::TrimWhitespaceASCII(font_names->back(), base::TRIM_TRAILING,
&font_names->back());
}
}
}
}
std::vector<Font> GetFallbackFonts(const Font& font) {
TRACE_EVENT0("fonts", "gfx::GetFallbackFonts");
std::string font_family = font.GetFontName();
CachedFontLinkSettings* font_link = CachedFontLinkSettings::GetInstance();
return *font_link->GetLinkedFonts(Font(font_family, 10));
}
bool GetFallbackFont(const Font& font,
const std::string& locale,
std::u16string_view text,
Font* result) {
TRACE_EVENT0("fonts", "gfx::GetFallbackFont");
DCHECK(base::CurrentUIThread::IsSet());
if (text.empty())
return false;
constexpr char16_t kNulCharacter = '\0';
if (text.find(kNulCharacter) != std::u16string_view::npos) {
return false;
}
sk_sp<SkTypeface> fallback_typeface =
GetSkiaFallbackTypeface(font, locale, text);
if (!fallback_typeface)
return false;
*result = Font(PlatformFont::CreateFromSkTypeface(
std::move(fallback_typeface), font.GetFontSize(), std::nullopt));
return true;
}
}