| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ui/gfx/win/direct_write.h" |
| |
| #include <wrl/client.h> |
| |
| #include <string> |
| |
| #include "base/debug/alias.h" |
| #include "base/metrics/histogram_functions.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/trace_event/trace_event.h" |
| #include "base/win/windows_version.h" |
| #include "skia/ext/fontmgr_default.h" |
| #include "third_party/skia/include/core/SkFontMgr.h" |
| #include "third_party/skia/include/ports/SkTypeface_win.h" |
| |
| namespace gfx { |
| namespace win { |
| |
| namespace { |
| |
| // Pointer to the global IDWriteFactory interface. |
| IDWriteFactory* g_direct_write_factory = nullptr; |
| |
| void SetDirectWriteFactory(IDWriteFactory* factory) { |
| DCHECK(!g_direct_write_factory); |
| // We grab a reference on the DirectWrite factory. This reference is |
| // leaked, which is ok because skia leaks it as well. |
| factory->AddRef(); |
| g_direct_write_factory = factory; |
| } |
| |
| } // anonymous namespace |
| |
| 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); |
| CHECK(false); |
| return; |
| } |
| 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"); |
| SCOPED_UMA_HISTOGRAM_LONG_TIMER("DirectWrite.Fonts.Gfx.InitializeTime"); |
| |
| Microsoft::WRL::ComPtr<IDWriteFactory> factory; |
| CreateDWriteFactory(&factory); |
| CHECK(!!factory); |
| SetDirectWriteFactory(factory.Get()); |
| |
| // The skia call to create a new DirectWrite font manager instance can fail |
| // if we are unable to get the system font collection from the DirectWrite |
| // factory. The GetSystemFontCollection method in the IDWriteFactory |
| // interface fails with E_INVALIDARG on certain Windows 7 gold versions |
| // (6.1.7600.*). |
| sk_sp<SkFontMgr> direct_write_font_mgr = |
| SkFontMgr_New_DirectWrite(factory.Get()); |
| int iteration = 0; |
| if (!direct_write_font_mgr && |
| base::win::GetVersion() == base::win::Version::WIN7) { |
| // Windows (win7_rtm) may fail to map the service sections |
| // (crbug.com/956064). |
| constexpr int kMaxRetries = 5; |
| constexpr base::TimeDelta kRetrySleepTime = base::Microseconds(500); |
| while (iteration < kMaxRetries) { |
| base::PlatformThread::Sleep(kRetrySleepTime); |
| direct_write_font_mgr = SkFontMgr_New_DirectWrite(factory.Get()); |
| if (direct_write_font_mgr) |
| break; |
| ++iteration; |
| } |
| } |
| if (!direct_write_font_mgr) |
| iteration = -1; |
| base::UmaHistogramSparse("DirectWrite.Fonts.Gfx.InitializeLoopCount", |
| iteration); |
| // TODO(crbug.com/956064): Move to a CHECK when the cause of the crash is |
| // fixed and remove the if statement that fallback to GDI font manager. |
| DCHECK(!!direct_write_font_mgr); |
| if (!direct_write_font_mgr) |
| direct_write_font_mgr = SkFontMgr_New_GDI(); |
| |
| // Override the default skia font manager. This must be called before any |
| // use of the skia font manager is done (e.g. before any call to |
| // SkFontMgr::RefDefault()). |
| skia::OverrideDefaultSkFontMgr(std::move(direct_write_font_mgr)); |
| } |
| |
| IDWriteFactory* GetDirectWriteFactory() { |
| // Some unittests may access this accessor without any previous call to |
| // |InitializeDirectWrite|. A call to |InitializeDirectWrite| after this |
| // function being called is still invalid. |
| if (!g_direct_write_factory) |
| InitializeDirectWrite(); |
| return g_direct_write_factory; |
| } |
| |
| absl::optional<std::string> RetrieveLocalizedString( |
| IDWriteLocalizedStrings* names, |
| const std::string& locale) { |
| std::wstring locale_wide = base::UTF8ToWide(locale); |
| |
| // If locale is empty, index 0 will be used. Otherwise, the locale name must |
| // be found and must exist. |
| UINT32 index = 0; |
| BOOL exists = false; |
| if (!locale.empty() && |
| (FAILED(names->FindLocaleName(locale_wide.c_str(), &index, &exists)) || |
| !exists)) { |
| return absl::nullopt; |
| } |
| |
| // Get the string length. |
| UINT32 length = 0; |
| if (FAILED(names->GetStringLength(index, &length))) |
| return absl::nullopt; |
| |
| // The output buffer length needs to be one larger to receive the NUL |
| // character. |
| std::wstring buffer; |
| buffer.resize(length + 1); |
| if (FAILED(names->GetString(index, &buffer[0], buffer.size()))) |
| return absl::nullopt; |
| |
| // Shrink the string to fit the actual length. |
| buffer.resize(length); |
| |
| return base::WideToUTF8(buffer); |
| } |
| |
| absl::optional<std::string> RetrieveLocalizedFontName( |
| base::StringPiece 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 absl::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 absl::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 absl::nullopt; |
| } |
| |
| return RetrieveLocalizedString(family_names.Get(), locale); |
| } |
| |
| } // namespace win |
| } // namespace gfx |