| // Copyright 2020 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/browser/font_access/font_enumeration_data_source_win.h" |
| |
| #include <dwrite.h> |
| #include <stdint.h> |
| #include <wrl/client.h> |
| |
| #include <optional> |
| #include <string> |
| |
| #include "base/location.h" |
| #include "base/notreached.h" |
| #include "base/sequence_checker.h" |
| #include "base/threading/scoped_blocking_call.h" |
| #include "content/browser/font_access/font_enumeration_cache.h" |
| #include "third_party/blink/public/common/font_access/font_enumeration_table.pb.h" |
| #include "ui/gfx/win/direct_write.h" |
| |
| namespace content { |
| |
| namespace { |
| |
| // Retrieves a DirectWrite font collection for all fonts on the system. |
| // |
| // This operation may be expensive, and its result should be cached. |
| // |
| // Returns nullptr in case of failure. |
| Microsoft::WRL::ComPtr<IDWriteFontCollection> GetSystemFonts() { |
| base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, |
| base::BlockingType::MAY_BLOCK); |
| // Returned from all code paths, to enable return value optimization. |
| Microsoft::WRL::ComPtr<IDWriteFontCollection> collection = nullptr; |
| |
| Microsoft::WRL::ComPtr<IDWriteFactory> factory; |
| gfx::win::CreateDWriteFactory(&factory); |
| if (!factory) |
| return collection; |
| |
| HRESULT hr = factory->GetSystemFontCollection(&collection); |
| if (FAILED(hr)) |
| collection = nullptr; |
| |
| return collection; |
| } |
| |
| // Retrieves a string from a font's information table. |
| // |
| // Returns nullptr in case of failure. |
| Microsoft::WRL::ComPtr<IDWriteLocalizedStrings> GetFontInformation( |
| IDWriteFont* font, |
| DWRITE_INFORMATIONAL_STRING_ID string_id) { |
| base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, |
| base::BlockingType::MAY_BLOCK); |
| |
| // Returned from all code paths, to enable return value optimization. |
| Microsoft::WRL::ComPtr<IDWriteLocalizedStrings> localized_strings; |
| BOOL font_has_info; |
| |
| HRESULT hr = font->GetInformationalStrings(string_id, &localized_strings, |
| &font_has_info); |
| if (FAILED(hr) || !font_has_info) |
| localized_strings = nullptr; |
| |
| return localized_strings; |
| } |
| |
| // Retrieves a string matching a locale from a DirectWrite string collection. |
| // |
| // If a string in the given locale does not exist, falls back to retrieving the |
| // first string in the collection. |
| // |
| // Returns nullopt in case the string does not exist. |
| std::optional<std::string> GetLocalizedString(IDWriteLocalizedStrings* names, |
| const std::string& locale) { |
| std::optional<std::string> localized_name = |
| gfx::win::RetrieveLocalizedString(names, locale); |
| if (!localized_name.has_value()) { |
| // Fall back to returning the first string in the collection. |
| localized_name = gfx::win::RetrieveLocalizedString(names, std::string()); |
| } |
| return localized_name; |
| } |
| |
| // Retrieves a font family name that can be reported by the Fonts Access API. |
| // |
| // Returns nullopt in case of failure. |
| std::optional<std::string> GetFamilyName(IDWriteFontFamily* family) { |
| std::optional<std::string> family_name; |
| |
| Microsoft::WRL::ComPtr<IDWriteLocalizedStrings> family_names; |
| HRESULT hr = family->GetFamilyNames(&family_names); |
| if (FAILED(hr)) |
| return family_name; |
| |
| family_name = GetLocalizedString(family_names.Get(), "en-us"); |
| return family_name; |
| } |
| |
| // Retrieves a font's PostScript name, to be reported by the Fonts Access API. |
| // |
| // Returns nullopt in case of failure. |
| std::optional<std::string> GetFontPostScriptName(IDWriteFont* font) { |
| std::optional<std::string> postscript_name; |
| |
| // DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_NAME and |
| // DWRITE_INFORMATIONAL_STRING_FULL_NAME are only supported on Windows 7 with |
| // KB2670838 (https://support.microsoft.com/en-us/kb/2670838) installed. It is |
| // possible to use a fallback as can be observed in Firefox: |
| // https://bugzilla.mozilla.org/show_bug.cgi?id=947812 However, this might not |
| // be worth the effort. |
| |
| Microsoft::WRL::ComPtr<IDWriteLocalizedStrings> postscript_names = |
| GetFontInformation(font, DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_NAME); |
| if (!postscript_names) |
| return postscript_name; |
| |
| postscript_name = GetLocalizedString(postscript_names.Get(), "en-us"); |
| return postscript_name; |
| } |
| |
| // Retrieves a font's full name, to be reported by the Fonts Access API. |
| // |
| // Returns nullopt in case of failure. |
| std::optional<std::string> GetFontFullName(IDWriteFont* font, |
| const std::string& locale) { |
| std::optional<std::string> full_name; |
| |
| Microsoft::WRL::ComPtr<IDWriteLocalizedStrings> full_names = |
| GetFontInformation(font, DWRITE_INFORMATIONAL_STRING_FULL_NAME); |
| if (!full_names) |
| return full_name; |
| |
| full_name = GetLocalizedString(full_names.Get(), locale); |
| return full_name; |
| } |
| |
| // Returns a font's style name, to be reported by the Fonts Access API. |
| // |
| // Returns nullopt in case of failure. |
| std::optional<std::string> GetFontStyleName(IDWriteFont* font) { |
| std::optional<std::string> style_name; |
| |
| // All fonts should have a subfamily name compatible with Windows GDI, |
| // available as the string DWRITE_INFORMATIONAL_STRING_WIN32_SUBFAMILY_NAMES. |
| // |
| // In some cases, the family / sub-family names preferred by designer wouldn't |
| // be compatible with Windows GDI, and the desiner. In these cases, the |
| // designer-preferred subfamily name is availabe as the string |
| // DWRITE_INFORMATIONAL_STRING_PREFERRED_SUBFAMILY_NAMES. |
| // |
| // More details at |
| // https://docs.microsoft.com/en-us/windows/win32/api/dwrite/ne-dwrite-dwrite_informational_string_id |
| |
| Microsoft::WRL::ComPtr<IDWriteLocalizedStrings> style_names = |
| GetFontInformation(font, |
| DWRITE_INFORMATIONAL_STRING_PREFERRED_SUBFAMILY_NAMES); |
| if (!style_names) { |
| style_names = GetFontInformation( |
| font, DWRITE_INFORMATIONAL_STRING_WIN32_SUBFAMILY_NAMES); |
| if (!style_names) |
| return style_name; |
| } |
| |
| style_name = GetLocalizedString(style_names.Get(), "en-us"); |
| return style_name; |
| } |
| |
| } // namespace |
| |
| FontEnumerationDataSourceWin::FontEnumerationDataSourceWin() { |
| DETACH_FROM_SEQUENCE(sequence_checker_); |
| } |
| |
| FontEnumerationDataSourceWin::~FontEnumerationDataSourceWin() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| } |
| |
| blink::FontEnumerationTable FontEnumerationDataSourceWin::GetFonts( |
| const std::string& locale) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| blink::FontEnumerationTable font_enumeration_table; |
| |
| Microsoft::WRL::ComPtr<IDWriteFontCollection> collection = GetSystemFonts(); |
| uint32_t family_count; |
| { |
| base::ScopedBlockingCall scoped_blocking_call( |
| FROM_HERE, base::BlockingType::MAY_BLOCK); |
| |
| family_count = collection->GetFontFamilyCount(); |
| } |
| |
| // Used to filter duplicates. |
| std::set<std::string> fonts_seen; |
| |
| for (uint32_t family_index = 0; family_index < family_count; ++family_index) { |
| Microsoft::WRL::ComPtr<IDWriteFontFamily> family; |
| HRESULT hr = collection->GetFontFamily(family_index, &family); |
| if (FAILED(hr)) |
| continue; |
| |
| std::optional<std::string> family_name = GetFamilyName(family.Get()); |
| |
| uint32_t font_count = family->GetFontCount(); |
| for (uint32_t font_index = 0; font_index < font_count; ++font_index) { |
| Microsoft::WRL::ComPtr<IDWriteFont> font; |
| { |
| base::ScopedBlockingCall scoped_blocking_call( |
| FROM_HERE, base::BlockingType::MAY_BLOCK); |
| hr = family->GetFont(font_index, &font); |
| } |
| |
| if (FAILED(hr)) |
| continue; |
| |
| // Skip this font if it's a simulation. |
| if (font->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE) |
| continue; |
| |
| std::optional<std::string> postscript_name = |
| GetFontPostScriptName(font.Get()); |
| if (!postscript_name) |
| continue; |
| |
| auto it_and_success = fonts_seen.emplace(postscript_name.value()); |
| if (!it_and_success.second) { |
| // Skip duplicates. |
| continue; |
| } |
| |
| std::optional<std::string> localized_full_name = |
| GetFontFullName(font.Get(), locale); |
| if (!localized_full_name) |
| localized_full_name = postscript_name; |
| |
| std::optional<std::string> style_name = GetFontStyleName(font.Get()); |
| if (!style_name) |
| continue; |
| |
| blink::FontEnumerationTable_FontData* data = |
| font_enumeration_table.add_fonts(); |
| data->set_postscript_name(std::move(postscript_name).value()); |
| data->set_full_name(std::move(localized_full_name).value()); |
| data->set_family(family_name.value()); |
| data->set_style(style_name ? std::move(style_name.value()) |
| : std::string()); |
| } |
| } |
| |
| return font_enumeration_table; |
| } |
| |
| } // namespace content |