#include "ui/gfx/win/direct_write.h"
#include <dwrite.h>
#include <dwrite_2.h>
#include <dwrite_3.h>
#include <wrl.h>
#include <wrl/client.h>
#include <string>
#include <string_view>
#include "base/debug/alias.h"
#include "base/metrics/histogram_macros.h"
#include "base/notreached.h"
#include "base/strings/utf_string_conversions.h"
#include "base/trace_event/trace_event.h"
#include "skia/ext/font_utils.h"
#include "third_party/skia/include/core/SkFontMgr.h"
#include "third_party/skia/include/ports/SkTypeface_win.h"
namespace gfx {
namespace win {
namespace {
IDWriteFactory* g_direct_write_factory = nullptr;
std::vector<base::FilePath>* g_sideloaded_fonts = nullptr;
bool GetFontCollection(Microsoft::WRL::ComPtr<IDWriteFactory>& factory,
IDWriteFontCollection** collection) {
if (!g_sideloaded_fonts) {
return false;
}
Microsoft::WRL::ComPtr<IDWriteFactory2> factory2;
factory.As<IDWriteFactory2>(&factory2);
DCHECK(factory2);
Microsoft::WRL::ComPtr<IDWriteFactory3> factory3;
factory2.As<IDWriteFactory3>(&factory3);
DCHECK(factory3);
Microsoft::WRL::ComPtr<IDWriteFontSetBuilder> font_set_builder;
HRESULT hr = factory3->CreateFontSetBuilder(&font_set_builder);
DCHECK(SUCCEEDED(hr));
for (auto& path : *g_sideloaded_fonts) {
Microsoft::WRL::ComPtr<IDWriteFontFile> font_file;
hr = factory3->CreateFontFileReference(path.value().c_str(), nullptr,
&font_file);
DCHECK(SUCCEEDED(hr));
BOOL supported;
DWRITE_FONT_FILE_TYPE file_type;
UINT32 n_fonts;
hr = font_file->Analyze(&supported, &file_type, nullptr, &n_fonts);
DCHECK(SUCCEEDED(hr));
for (UINT32 font_index = 0; font_index < n_fonts; ++font_index) {
Microsoft::WRL::ComPtr<IDWriteFontFaceReference> font_face;
hr = factory3->CreateFontFaceReference(font_file.Get(), font_index,
DWRITE_FONT_SIMULATIONS_NONE,
&font_face);
DCHECK(SUCCEEDED(hr));
hr = font_set_builder->AddFontFaceReference(font_face.Get());
DCHECK(SUCCEEDED(hr));
}
}
Microsoft::WRL::ComPtr<IDWriteFontSet> system_font_set;
hr = factory3->GetSystemFontSet(&system_font_set);
DCHECK(SUCCEEDED(hr));
hr = font_set_builder->AddFontSet(system_font_set.Get());
DCHECK(SUCCEEDED(hr));
Microsoft::WRL::ComPtr<IDWriteFontSet> font_set;
hr = font_set_builder->CreateFontSet(&font_set);
DCHECK(SUCCEEDED(hr));
Microsoft::WRL::ComPtr<IDWriteFontCollection1> collection1;
hr = factory3->CreateFontCollectionFromFontSet(font_set.Get(), &collection1);
DCHECK(SUCCEEDED(hr));
hr = collection1->QueryInterface(collection);
DCHECK(SUCCEEDED(hr));
return true;
}
void SetDirectWriteFactory(IDWriteFactory* factory) {
DCHECK(!g_direct_write_factory);
factory->AddRef();
g_direct_write_factory = factory;
}
}
void SideLoadFontForTesting(base::FilePath path) {
if (!g_sideloaded_fonts) {
g_sideloaded_fonts = new std::vector<base::FilePath>();
}
g_sideloaded_fonts->push_back(path);
}
void CreateDWriteFactory(IDWriteFactory** factory) {
Microsoft::WRL::ComPtr<IUnknown> factory_unknown;
HRESULT hr =
DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory),
&factory_unknown);
if (FAILED(hr)) {
base::debug::Alias(&hr);
NOTREACHED();
}
factory_unknown.CopyTo(factory);
}
void InitializeDirectWrite() {
static bool tried_dwrite_initialize = false;
DCHECK(!tried_dwrite_initialize);
tried_dwrite_initialize = true;
TRACE_EVENT0("fonts", "gfx::InitializeDirectWrite");
Microsoft::WRL::ComPtr<IDWriteFactory> factory;
CreateDWriteFactory(&factory);
CHECK(!!factory);
SetDirectWriteFactory(factory.Get());
Microsoft::WRL::ComPtr<IDWriteFontCollection> collection;
bool should_use_collection = GetFontCollection(factory, &collection);
if (g_sideloaded_fonts) {
DCHECK(should_use_collection);
} else {
DCHECK(!should_use_collection);
}
sk_sp<SkFontMgr> direct_write_font_mgr = SkFontMgr_New_DirectWrite(
factory.Get(), should_use_collection ? collection.Get() : nullptr);
CHECK(!!direct_write_font_mgr);
skia::OverrideDefaultSkFontMgr(std::move(direct_write_font_mgr));
}
IDWriteFactory* GetDirectWriteFactory() {
if (!g_direct_write_factory)
InitializeDirectWrite();
return g_direct_write_factory;
}
std::optional<std::string> RetrieveLocalizedString(
IDWriteLocalizedStrings* names,
const std::string& locale) {
std::wstring locale_wide = base::UTF8ToWide(locale);
UINT32 index = 0;
BOOL exists = false;
if (!locale.empty() &&
(FAILED(names->FindLocaleName(locale_wide.c_str(), &index, &exists)) ||
!exists)) {
return std::nullopt;
}
UINT32 length = 0;
if (FAILED(names->GetStringLength(index, &length)))
return std::nullopt;
std::wstring buffer;
buffer.resize(length + 1);
if (FAILED(names->GetString(index, &buffer[0], buffer.size())))
return std::nullopt;
buffer.resize(length);
return base::WideToUTF8(buffer);
}
std::optional<std::string> RetrieveLocalizedFontName(
std::string_view font_name,
const std::string& locale) {
Microsoft::WRL::ComPtr<IDWriteFactory> factory;
CreateDWriteFactory(&factory);
Microsoft::WRL::ComPtr<IDWriteFontCollection> font_collection;
if (FAILED(factory->GetSystemFontCollection(&font_collection))) {
return std::nullopt;
}
UINT32 index = 0;
BOOL exists;
std::wstring font_name_wide = base::UTF8ToWide(font_name);
if (FAILED(font_collection->FindFamilyName(font_name_wide.c_str(), &index,
&exists)) ||
!exists) {
return std::nullopt;
}
Microsoft::WRL::ComPtr<IDWriteFontFamily> font_family;
Microsoft::WRL::ComPtr<IDWriteLocalizedStrings> family_names;
if (FAILED(font_collection->GetFontFamily(index, &font_family)) ||
FAILED(font_family->GetFamilyNames(&family_names))) {
return std::nullopt;
}
return RetrieveLocalizedString(family_names.Get(), locale);
}
}
}