blob: 4bc023693175f050c17f024530411d484f48010c [file] [log] [blame]
[email protected]76ac8d92012-09-07 20:12:571// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "ui/gfx/font_fallback_win.h"
6
kulshince612eb2016-06-29 18:46:517#include <dwrite_2.h>
[email protected]8c17e13d2014-07-19 03:01:398#include <usp10.h>
kulshince612eb2016-06-29 18:46:519#include <wrl.h>
[email protected]8c17e13d2014-07-19 03:01:3910
kulshine521a202016-07-06 21:56:2711#include <algorithm>
[email protected]76ac8d92012-09-07 20:12:5712#include <map>
13
kulshince612eb2016-06-29 18:46:5114#include "base/i18n/rtl.h"
avic89eb8d42015-12-23 08:08:1815#include "base/macros.h"
[email protected]76ac8d92012-09-07 20:12:5716#include "base/memory/singleton.h"
vadimta7e14922014-11-25 22:37:0217#include "base/profiler/scoped_tracker.h"
[email protected]d778e0422013-03-06 18:10:2218#include "base/strings/string_split.h"
[email protected]f3652ff92013-06-11 13:54:3119#include "base/strings/string_util.h"
[email protected]c7057fbe2013-06-07 18:54:0120#include "base/strings/utf_string_conversions.h"
[email protected]76ac8d92012-09-07 20:12:5721#include "base/win/registry.h"
kulshince612eb2016-06-29 18:46:5122#include "base/win/scoped_comptr.h"
[email protected]76ac8d92012-09-07 20:12:5723#include "ui/gfx/font.h"
[email protected]8c17e13d2014-07-19 03:01:3924#include "ui/gfx/font_fallback.h"
kulshince612eb2016-06-29 18:46:5125#include "ui/gfx/platform_font_win.h"
26#include "ui/gfx/win/direct_write.h"
27#include "ui/gfx/win/text_analysis_source.h"
[email protected]76ac8d92012-09-07 20:12:5728
29namespace gfx {
30
31namespace {
32
kulshin4a1eba32016-07-08 00:38:3333IDWriteFactory* g_factory = nullptr;
34
[email protected]76ac8d92012-09-07 20:12:5735// Queries the registry to get a mapping from font filenames to font names.
36void QueryFontsFromRegistry(std::map<std::string, std::string>* map) {
37 const wchar_t* kFonts =
38 L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts";
39
40 base::win::RegistryValueIterator it(HKEY_LOCAL_MACHINE, kFonts);
41 for (; it.Valid(); ++it) {
[email protected]dd2cc802013-12-25 20:09:3642 const std::string filename =
brettw8e2106d2015-08-11 19:30:2243 base::ToLowerASCII(base::WideToUTF8(it.Value()));
[email protected]dd2cc802013-12-25 20:09:3644 (*map)[filename] = base::WideToUTF8(it.Name());
[email protected]76ac8d92012-09-07 20:12:5745 }
46}
47
48// Fills |font_names| with a list of font families found in the font file at
49// |filename|. Takes in a |font_map| from font filename to font families, which
50// is filled-in by querying the registry, if empty.
51void GetFontNamesFromFilename(const std::string& filename,
52 std::map<std::string, std::string>* font_map,
53 std::vector<std::string>* font_names) {
54 if (font_map->empty())
55 QueryFontsFromRegistry(font_map);
56
57 std::map<std::string, std::string>::const_iterator it =
brettw8e2106d2015-08-11 19:30:2258 font_map->find(base::ToLowerASCII(filename));
[email protected]76ac8d92012-09-07 20:12:5759 if (it == font_map->end())
60 return;
61
62 internal::ParseFontFamilyString(it->second, font_names);
63}
64
65// Returns true if |text| contains only ASCII digits.
66bool ContainsOnlyDigits(const std::string& text) {
[email protected]031ffed2013-06-09 03:32:3667 return text.find_first_not_of("0123456789") == base::string16::npos;
[email protected]76ac8d92012-09-07 20:12:5768}
69
70// Appends a Font with the given |name| and |size| to |fonts| unless the last
71// entry is already a font with that name.
72void AppendFont(const std::string& name, int size, std::vector<Font>* fonts) {
73 if (fonts->empty() || fonts->back().GetFontName() != name)
74 fonts->push_back(Font(name, size));
75}
76
77// Queries the registry to get a list of linked fonts for |font|.
78void QueryLinkedFontsFromRegistry(const Font& font,
79 std::map<std::string, std::string>* font_map,
80 std::vector<Font>* linked_fonts) {
[email protected]76ac8d92012-09-07 20:12:5781 const wchar_t* kSystemLink =
82 L"Software\\Microsoft\\Windows NT\\CurrentVersion\\FontLink\\SystemLink";
83
84 base::win::RegKey key;
85 if (FAILED(key.Open(HKEY_LOCAL_MACHINE, kSystemLink, KEY_READ)))
86 return;
87
[email protected]dd2cc802013-12-25 20:09:3688 const std::wstring original_font_name = base::UTF8ToWide(font.GetFontName());
[email protected]76ac8d92012-09-07 20:12:5789 std::vector<std::wstring> values;
90 if (FAILED(key.ReadValues(original_font_name.c_str(), &values))) {
91 key.Close();
92 return;
93 }
94
95 std::string filename;
96 std::string font_name;
97 for (size_t i = 0; i < values.size(); ++i) {
[email protected]dd2cc802013-12-25 20:09:3698 internal::ParseFontLinkEntry(
99 base::WideToUTF8(values[i]), &filename, &font_name);
[email protected]76ac8d92012-09-07 20:12:57100 // If the font name is present, add that directly, otherwise add the
101 // font names corresponding to the filename.
102 if (!font_name.empty()) {
103 AppendFont(font_name, font.GetFontSize(), linked_fonts);
104 } else if (!filename.empty()) {
105 std::vector<std::string> font_names;
106 GetFontNamesFromFilename(filename, font_map, &font_names);
107 for (size_t i = 0; i < font_names.size(); ++i)
108 AppendFont(font_names[i], font.GetFontSize(), linked_fonts);
109 }
110 }
111
112 key.Close();
113}
114
115// CachedFontLinkSettings is a singleton cache of the Windows font settings
116// from the registry. It maintains a cached view of the registry's list of
117// system fonts and their font link chains.
118class CachedFontLinkSettings {
119 public:
120 static CachedFontLinkSettings* GetInstance();
121
122 // Returns the linked fonts list correspond to |font|. Returned value will
123 // never be null.
124 const std::vector<Font>* GetLinkedFonts(const Font& font);
125
126 private:
olli.raula36aa8be2015-09-10 11:14:22127 friend struct base::DefaultSingletonTraits<CachedFontLinkSettings>;
[email protected]76ac8d92012-09-07 20:12:57128
129 CachedFontLinkSettings();
130 virtual ~CachedFontLinkSettings();
131
132 // Map of system fonts, from file names to font families.
133 std::map<std::string, std::string> cached_system_fonts_;
134
135 // Map from font names to vectors of linked fonts.
136 std::map<std::string, std::vector<Font> > cached_linked_fonts_;
137
138 DISALLOW_COPY_AND_ASSIGN(CachedFontLinkSettings);
139};
140
141// static
142CachedFontLinkSettings* CachedFontLinkSettings::GetInstance() {
olli.raula36aa8be2015-09-10 11:14:22143 return base::Singleton<
144 CachedFontLinkSettings,
145 base::LeakySingletonTraits<CachedFontLinkSettings>>::get();
[email protected]76ac8d92012-09-07 20:12:57146}
147
148const std::vector<Font>* CachedFontLinkSettings::GetLinkedFonts(
149 const Font& font) {
150 const std::string& font_name = font.GetFontName();
151 std::map<std::string, std::vector<Font> >::const_iterator it =
152 cached_linked_fonts_.find(font_name);
153 if (it != cached_linked_fonts_.end())
154 return &it->second;
155
156 cached_linked_fonts_[font_name] = std::vector<Font>();
157 std::vector<Font>* linked_fonts = &cached_linked_fonts_[font_name];
vadimta7e14922014-11-25 22:37:02158
pkasting67cb1e82015-04-15 03:27:34159 // TODO(ckocagil): Remove ScopedTracker below once crbug.com/441028 is fixed.
vadimta7e14922014-11-25 22:37:02160 tracked_objects::ScopedTracker tracking_profile(
161 FROM_HERE_WITH_EXPLICIT_FUNCTION(
pkasting67cb1e82015-04-15 03:27:34162 "441028 QueryLinkedFontsFromRegistry()"));
vadimta7e14922014-11-25 22:37:02163
[email protected]76ac8d92012-09-07 20:12:57164 QueryLinkedFontsFromRegistry(font, &cached_system_fonts_, linked_fonts);
165 return linked_fonts;
166}
167
168CachedFontLinkSettings::CachedFontLinkSettings() {
169}
170
171CachedFontLinkSettings::~CachedFontLinkSettings() {
172}
173
[email protected]8c17e13d2014-07-19 03:01:39174// Callback to |EnumEnhMetaFile()| to intercept font creation.
175int CALLBACK MetaFileEnumProc(HDC hdc,
176 HANDLETABLE* table,
177 CONST ENHMETARECORD* record,
178 int table_entries,
179 LPARAM log_font) {
180 if (record->iType == EMR_EXTCREATEFONTINDIRECTW) {
181 const EMREXTCREATEFONTINDIRECTW* create_font_record =
182 reinterpret_cast<const EMREXTCREATEFONTINDIRECTW*>(record);
183 *reinterpret_cast<LOGFONT*>(log_font) = create_font_record->elfw.elfLogFont;
184 }
185 return 1;
186}
187
kulshince612eb2016-06-29 18:46:51188bool GetUniscribeFallbackFont(const Font& font,
189 const wchar_t* text,
190 int text_length,
191 Font* result) {
192 // Adapted from WebKit's |FontCache::GetFontDataForCharacters()|.
193 // Uniscribe doesn't expose a method to query fallback fonts, so this works by
194 // drawing the text to an EMF object with Uniscribe's ScriptStringOut and then
195 // inspecting the EMF object to figure out which font Uniscribe used.
196 //
197 // DirectWrite in Windows 8.1 provides a cleaner alternative:
198 // http://msdn.microsoft.com/en-us/library/windows/desktop/dn280480.aspx
199
200 static HDC hdc = CreateCompatibleDC(NULL);
201
202 // Use a meta file to intercept the fallback font chosen by Uniscribe.
203 HDC meta_file_dc = CreateEnhMetaFile(hdc, NULL, NULL, NULL);
204 if (!meta_file_dc)
205 return false;
206
207 SelectObject(meta_file_dc, font.GetNativeFont());
208
209 SCRIPT_STRING_ANALYSIS script_analysis;
210 HRESULT hresult =
211 ScriptStringAnalyse(meta_file_dc, text, text_length, 0, -1,
212 SSA_METAFILE | SSA_FALLBACK | SSA_GLYPHS | SSA_LINK,
213 0, NULL, NULL, NULL, NULL, NULL, &script_analysis);
214
215 if (SUCCEEDED(hresult)) {
216 hresult = ScriptStringOut(script_analysis, 0, 0, 0, NULL, 0, 0, FALSE);
217 ScriptStringFree(&script_analysis);
218 }
219
220 bool found_fallback = false;
221 HENHMETAFILE meta_file = CloseEnhMetaFile(meta_file_dc);
222 if (SUCCEEDED(hresult)) {
223 LOGFONT log_font;
224 log_font.lfFaceName[0] = 0;
225 EnumEnhMetaFile(0, meta_file, MetaFileEnumProc, &log_font, NULL);
226 if (log_font.lfFaceName[0]) {
227 *result =
228 Font(base::UTF16ToUTF8(log_font.lfFaceName), font.GetFontSize());
229 found_fallback = true;
230 }
231 }
232 DeleteEnhMetaFile(meta_file);
233
234 return found_fallback;
235}
236
[email protected]76ac8d92012-09-07 20:12:57237} // namespace
238
239namespace internal {
240
241void ParseFontLinkEntry(const std::string& entry,
242 std::string* filename,
243 std::string* font_name) {
brettw26dab8f02015-08-08 00:28:47244 std::vector<std::string> parts = base::SplitString(
245 entry, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
[email protected]76ac8d92012-09-07 20:12:57246 filename->clear();
247 font_name->clear();
248 if (parts.size() > 0)
249 *filename = parts[0];
250 // The second entry may be the font name or the first scaling factor, if the
251 // entry does not contain a font name. If it contains only digits, assume it
252 // is a scaling factor.
253 if (parts.size() > 1 && !ContainsOnlyDigits(parts[1]))
254 *font_name = parts[1];
255}
256
257void ParseFontFamilyString(const std::string& family,
258 std::vector<std::string>* font_names) {
259 // The entry is comma separated, having the font filename as the first value
260 // followed optionally by the font family name and a pair of integer scaling
261 // factors.
262 // TODO(asvitkine): Should we support these scaling factors?
brettw26dab8f02015-08-08 00:28:47263 *font_names = base::SplitString(
264 family, "&", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
[email protected]76ac8d92012-09-07 20:12:57265 if (!font_names->empty()) {
266 const size_t index = font_names->back().find('(');
267 if (index != std::string::npos) {
268 font_names->back().resize(index);
tfarina023b1dcc2015-12-06 13:25:41269 base::TrimWhitespaceASCII(font_names->back(), base::TRIM_TRAILING,
270 &font_names->back());
[email protected]76ac8d92012-09-07 20:12:57271 }
272 }
273}
274
[email protected]76ac8d92012-09-07 20:12:57275LinkedFontsIterator::LinkedFontsIterator(Font font)
276 : original_font_(font),
277 next_font_set_(false),
278 linked_fonts_(NULL),
279 linked_font_index_(0) {
280 SetNextFont(original_font_);
281}
282
283LinkedFontsIterator::~LinkedFontsIterator() {
284}
285
286void LinkedFontsIterator::SetNextFont(Font font) {
287 next_font_ = font;
288 next_font_set_ = true;
289}
290
291bool LinkedFontsIterator::NextFont(Font* font) {
292 if (next_font_set_) {
293 next_font_set_ = false;
294 current_font_ = next_font_;
295 *font = current_font_;
296 return true;
297 }
298
299 // First time through, get the linked fonts list.
300 if (linked_fonts_ == NULL)
301 linked_fonts_ = GetLinkedFonts();
302
303 if (linked_font_index_ == linked_fonts_->size())
304 return false;
305
306 current_font_ = linked_fonts_->at(linked_font_index_++);
307 *font = current_font_;
308 return true;
309}
310
311const std::vector<Font>* LinkedFontsIterator::GetLinkedFonts() const {
312 CachedFontLinkSettings* font_link = CachedFontLinkSettings::GetInstance();
313
314 // First, try to get the list for the original font.
315 const std::vector<Font>* fonts = font_link->GetLinkedFonts(original_font_);
316
317 // If there are no linked fonts for the original font, try querying the
318 // ones for the current font. This may happen if the first font is a custom
319 // font that has no linked fonts in the registry.
320 //
321 // Note: One possibility would be to always merge both lists of fonts,
322 // but it is not clear whether there are any real world scenarios
323 // where this would actually help.
324 if (fonts->empty())
325 fonts = font_link->GetLinkedFonts(current_font_);
326
327 return fonts;
328}
329
[email protected]8c17e13d2014-07-19 03:01:39330} // namespace internal
331
erikchen98ef6152015-12-09 00:27:22332std::vector<Font> GetFallbackFonts(const Font& font) {
333 std::string font_family = font.GetFontName();
334
[email protected]8c17e13d2014-07-19 03:01:39335 // LinkedFontsIterator doesn't care about the font size, so we always pass 10.
336 internal::LinkedFontsIterator linked_fonts(Font(font_family, 10));
erikchen98ef6152015-12-09 00:27:22337 std::vector<Font> fallback_fonts;
[email protected]8c17e13d2014-07-19 03:01:39338 Font current;
339 while (linked_fonts.NextFont(&current))
erikchen98ef6152015-12-09 00:27:22340 fallback_fonts.push_back(current);
[email protected]8c17e13d2014-07-19 03:01:39341 return fallback_fonts;
342}
343
kulshince612eb2016-06-29 18:46:51344bool GetFallbackFont(const Font& font,
345 const wchar_t* text,
346 int text_length,
347 Font* result) {
348 // Creating a DirectWrite font fallback can be expensive. It's ok in the
349 // browser process because we can use the shared system fallback, but in the
350 // renderer this can cause hangs. Code that needs font fallback in the
351 // renderer should instead use the font proxy.
352 DCHECK(base::MessageLoopForUI::IsCurrent());
[email protected]8c17e13d2014-07-19 03:01:39353
kulshine521a202016-07-06 21:56:27354 // Check that we have at least as much text as was claimed. If we have less
355 // text than expected then DirectWrite will become confused and crash. This
356 // shouldn't happen, but crbug.com/624905 shows that it happens sometimes.
357 DCHECK_GE(wcslen(text), static_cast<size_t>(text_length));
358 text_length = std::min(wcslen(text), static_cast<size_t>(text_length));
359
kulshin4a1eba32016-07-08 00:38:33360 if (g_factory == nullptr) {
361 gfx::win::CreateDWriteFactory(&g_factory);
362 }
kulshince612eb2016-06-29 18:46:51363 base::win::ScopedComPtr<IDWriteFactory2> factory2;
kulshin4a1eba32016-07-08 00:38:33364 g_factory->QueryInterface(factory2.Receive());
kulshince612eb2016-06-29 18:46:51365 if (!factory2) {
366 // IDWriteFactory2 is not available before Win8.1
367 return GetUniscribeFallbackFont(font, text, text_length, result);
368 }
[email protected]8c17e13d2014-07-19 03:01:39369
kulshince612eb2016-06-29 18:46:51370 base::win::ScopedComPtr<IDWriteFontFallback> fallback;
371 if (FAILED(factory2->GetSystemFontFallback(fallback.Receive())))
[email protected]8c17e13d2014-07-19 03:01:39372 return false;
373
kulshince612eb2016-06-29 18:46:51374 base::string16 locale = base::UTF8ToUTF16(base::i18n::GetConfiguredLocale());
[email protected]8c17e13d2014-07-19 03:01:39375
kulshince612eb2016-06-29 18:46:51376 base::win::ScopedComPtr<IDWriteNumberSubstitution> number_substitution;
377 if (FAILED(factory2->CreateNumberSubstitution(
378 DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE, locale.c_str(),
379 true /* ignoreUserOverride */, number_substitution.Receive()))) {
380 return false;
[email protected]8c17e13d2014-07-19 03:01:39381 }
382
kulshince612eb2016-06-29 18:46:51383 uint32_t mapped_length = 0;
384 base::win::ScopedComPtr<IDWriteFont> mapped_font;
385 float scale = 0;
386 base::win::ScopedComPtr<IDWriteTextAnalysisSource> text_analysis;
387 DWRITE_READING_DIRECTION reading_direction =
388 base::i18n::IsRTL() ? DWRITE_READING_DIRECTION_RIGHT_TO_LEFT
389 : DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
390 if (FAILED(Microsoft::WRL::MakeAndInitialize<gfx::win::TextAnalysisSource>(
391 text_analysis.Receive(), text, locale.c_str(),
392 number_substitution.get(), reading_direction))) {
393 return false;
[email protected]8c17e13d2014-07-19 03:01:39394 }
kulshince612eb2016-06-29 18:46:51395 base::string16 original_name = base::UTF8ToUTF16(font.GetFontName());
396 DWRITE_FONT_STYLE font_style = DWRITE_FONT_STYLE_NORMAL;
397 if (font.GetStyle() & Font::ITALIC)
398 font_style = DWRITE_FONT_STYLE_ITALIC;
399 if (FAILED(fallback->MapCharacters(
400 text_analysis.get(), 0, text_length, nullptr, original_name.c_str(),
401 static_cast<DWRITE_FONT_WEIGHT>(font.GetWeight()), font_style,
402 DWRITE_FONT_STRETCH_NORMAL, &mapped_length, mapped_font.Receive(),
403 &scale))) {
404 return false;
405 }
[email protected]8c17e13d2014-07-19 03:01:39406
kulshince612eb2016-06-29 18:46:51407 if (mapped_font) {
408 base::string16 name;
409 if (FAILED(GetFamilyNameFromDirectWriteFont(mapped_font.get(), &name)))
410 return false;
411 *result = Font(base::UTF16ToUTF8(name), font.GetFontSize() * scale);
412 return true;
413 }
414 return false;
[email protected]8c17e13d2014-07-19 03:01:39415}
416
[email protected]76ac8d92012-09-07 20:12:57417} // namespace gfx