blob: e5771cff628f694f2884899d34948aa82a073d98 [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
[email protected]8c17e13d2014-07-19 03:01:397#include <usp10.h>
8
[email protected]76ac8d92012-09-07 20:12:579#include <map>
10
11#include "base/memory/singleton.h"
vadimta7e14922014-11-25 22:37:0212#include "base/profiler/scoped_tracker.h"
[email protected]d778e0422013-03-06 18:10:2213#include "base/strings/string_split.h"
[email protected]f3652ff92013-06-11 13:54:3114#include "base/strings/string_util.h"
[email protected]c7057fbe2013-06-07 18:54:0115#include "base/strings/utf_string_conversions.h"
[email protected]76ac8d92012-09-07 20:12:5716#include "base/win/registry.h"
17#include "ui/gfx/font.h"
[email protected]8c17e13d2014-07-19 03:01:3918#include "ui/gfx/font_fallback.h"
[email protected]76ac8d92012-09-07 20:12:5719
20namespace gfx {
21
22namespace {
23
24// Queries the registry to get a mapping from font filenames to font names.
25void QueryFontsFromRegistry(std::map<std::string, std::string>* map) {
26 const wchar_t* kFonts =
27 L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts";
28
29 base::win::RegistryValueIterator it(HKEY_LOCAL_MACHINE, kFonts);
30 for (; it.Valid(); ++it) {
[email protected]dd2cc802013-12-25 20:09:3631 const std::string filename =
[email protected]cb1f4ac2014-08-07 16:55:4232 base::StringToLowerASCII(base::WideToUTF8(it.Value()));
[email protected]dd2cc802013-12-25 20:09:3633 (*map)[filename] = base::WideToUTF8(it.Name());
[email protected]76ac8d92012-09-07 20:12:5734 }
35}
36
37// Fills |font_names| with a list of font families found in the font file at
38// |filename|. Takes in a |font_map| from font filename to font families, which
39// is filled-in by querying the registry, if empty.
40void GetFontNamesFromFilename(const std::string& filename,
41 std::map<std::string, std::string>* font_map,
42 std::vector<std::string>* font_names) {
43 if (font_map->empty())
44 QueryFontsFromRegistry(font_map);
45
46 std::map<std::string, std::string>::const_iterator it =
[email protected]cb1f4ac2014-08-07 16:55:4247 font_map->find(base::StringToLowerASCII(filename));
[email protected]76ac8d92012-09-07 20:12:5748 if (it == font_map->end())
49 return;
50
51 internal::ParseFontFamilyString(it->second, font_names);
52}
53
54// Returns true if |text| contains only ASCII digits.
55bool ContainsOnlyDigits(const std::string& text) {
[email protected]031ffed2013-06-09 03:32:3656 return text.find_first_not_of("0123456789") == base::string16::npos;
[email protected]76ac8d92012-09-07 20:12:5757}
58
59// Appends a Font with the given |name| and |size| to |fonts| unless the last
60// entry is already a font with that name.
61void AppendFont(const std::string& name, int size, std::vector<Font>* fonts) {
62 if (fonts->empty() || fonts->back().GetFontName() != name)
63 fonts->push_back(Font(name, size));
64}
65
66// Queries the registry to get a list of linked fonts for |font|.
67void QueryLinkedFontsFromRegistry(const Font& font,
68 std::map<std::string, std::string>* font_map,
69 std::vector<Font>* linked_fonts) {
[email protected]76ac8d92012-09-07 20:12:5770 const wchar_t* kSystemLink =
71 L"Software\\Microsoft\\Windows NT\\CurrentVersion\\FontLink\\SystemLink";
72
73 base::win::RegKey key;
74 if (FAILED(key.Open(HKEY_LOCAL_MACHINE, kSystemLink, KEY_READ)))
75 return;
76
[email protected]dd2cc802013-12-25 20:09:3677 const std::wstring original_font_name = base::UTF8ToWide(font.GetFontName());
[email protected]76ac8d92012-09-07 20:12:5778 std::vector<std::wstring> values;
79 if (FAILED(key.ReadValues(original_font_name.c_str(), &values))) {
80 key.Close();
81 return;
82 }
83
84 std::string filename;
85 std::string font_name;
86 for (size_t i = 0; i < values.size(); ++i) {
[email protected]dd2cc802013-12-25 20:09:3687 internal::ParseFontLinkEntry(
88 base::WideToUTF8(values[i]), &filename, &font_name);
[email protected]76ac8d92012-09-07 20:12:5789 // If the font name is present, add that directly, otherwise add the
90 // font names corresponding to the filename.
91 if (!font_name.empty()) {
92 AppendFont(font_name, font.GetFontSize(), linked_fonts);
93 } else if (!filename.empty()) {
94 std::vector<std::string> font_names;
95 GetFontNamesFromFilename(filename, font_map, &font_names);
96 for (size_t i = 0; i < font_names.size(); ++i)
97 AppendFont(font_names[i], font.GetFontSize(), linked_fonts);
98 }
99 }
100
101 key.Close();
102}
103
104// CachedFontLinkSettings is a singleton cache of the Windows font settings
105// from the registry. It maintains a cached view of the registry's list of
106// system fonts and their font link chains.
107class CachedFontLinkSettings {
108 public:
109 static CachedFontLinkSettings* GetInstance();
110
111 // Returns the linked fonts list correspond to |font|. Returned value will
112 // never be null.
113 const std::vector<Font>* GetLinkedFonts(const Font& font);
114
115 private:
116 friend struct DefaultSingletonTraits<CachedFontLinkSettings>;
117
118 CachedFontLinkSettings();
119 virtual ~CachedFontLinkSettings();
120
121 // Map of system fonts, from file names to font families.
122 std::map<std::string, std::string> cached_system_fonts_;
123
124 // Map from font names to vectors of linked fonts.
125 std::map<std::string, std::vector<Font> > cached_linked_fonts_;
126
127 DISALLOW_COPY_AND_ASSIGN(CachedFontLinkSettings);
128};
129
130// static
131CachedFontLinkSettings* CachedFontLinkSettings::GetInstance() {
132 return Singleton<CachedFontLinkSettings,
133 LeakySingletonTraits<CachedFontLinkSettings> >::get();
134}
135
136const std::vector<Font>* CachedFontLinkSettings::GetLinkedFonts(
137 const Font& font) {
138 const std::string& font_name = font.GetFontName();
139 std::map<std::string, std::vector<Font> >::const_iterator it =
140 cached_linked_fonts_.find(font_name);
141 if (it != cached_linked_fonts_.end())
142 return &it->second;
143
144 cached_linked_fonts_[font_name] = std::vector<Font>();
145 std::vector<Font>* linked_fonts = &cached_linked_fonts_[font_name];
vadimta7e14922014-11-25 22:37:02146
pkasting67cb1e82015-04-15 03:27:34147 // TODO(ckocagil): Remove ScopedTracker below once crbug.com/441028 is fixed.
vadimta7e14922014-11-25 22:37:02148 tracked_objects::ScopedTracker tracking_profile(
149 FROM_HERE_WITH_EXPLICIT_FUNCTION(
pkasting67cb1e82015-04-15 03:27:34150 "441028 QueryLinkedFontsFromRegistry()"));
vadimta7e14922014-11-25 22:37:02151
[email protected]76ac8d92012-09-07 20:12:57152 QueryLinkedFontsFromRegistry(font, &cached_system_fonts_, linked_fonts);
153 return linked_fonts;
154}
155
156CachedFontLinkSettings::CachedFontLinkSettings() {
157}
158
159CachedFontLinkSettings::~CachedFontLinkSettings() {
160}
161
[email protected]8c17e13d2014-07-19 03:01:39162// Callback to |EnumEnhMetaFile()| to intercept font creation.
163int CALLBACK MetaFileEnumProc(HDC hdc,
164 HANDLETABLE* table,
165 CONST ENHMETARECORD* record,
166 int table_entries,
167 LPARAM log_font) {
168 if (record->iType == EMR_EXTCREATEFONTINDIRECTW) {
169 const EMREXTCREATEFONTINDIRECTW* create_font_record =
170 reinterpret_cast<const EMREXTCREATEFONTINDIRECTW*>(record);
171 *reinterpret_cast<LOGFONT*>(log_font) = create_font_record->elfw.elfLogFont;
172 }
173 return 1;
174}
175
[email protected]76ac8d92012-09-07 20:12:57176} // namespace
177
178namespace internal {
179
180void ParseFontLinkEntry(const std::string& entry,
181 std::string* filename,
182 std::string* font_name) {
brettw26dab8f02015-08-08 00:28:47183 std::vector<std::string> parts = base::SplitString(
184 entry, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
[email protected]76ac8d92012-09-07 20:12:57185 filename->clear();
186 font_name->clear();
187 if (parts.size() > 0)
188 *filename = parts[0];
189 // The second entry may be the font name or the first scaling factor, if the
190 // entry does not contain a font name. If it contains only digits, assume it
191 // is a scaling factor.
192 if (parts.size() > 1 && !ContainsOnlyDigits(parts[1]))
193 *font_name = parts[1];
194}
195
196void ParseFontFamilyString(const std::string& family,
197 std::vector<std::string>* font_names) {
198 // The entry is comma separated, having the font filename as the first value
199 // followed optionally by the font family name and a pair of integer scaling
200 // factors.
201 // TODO(asvitkine): Should we support these scaling factors?
brettw26dab8f02015-08-08 00:28:47202 *font_names = base::SplitString(
203 family, "&", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
[email protected]76ac8d92012-09-07 20:12:57204 if (!font_names->empty()) {
205 const size_t index = font_names->back().find('(');
206 if (index != std::string::npos) {
207 font_names->back().resize(index);
[email protected]8af69c6c2014-03-03 19:05:31208 base::TrimWhitespace(font_names->back(), base::TRIM_TRAILING,
209 &font_names->back());
[email protected]76ac8d92012-09-07 20:12:57210 }
211 }
212}
213
[email protected]76ac8d92012-09-07 20:12:57214LinkedFontsIterator::LinkedFontsIterator(Font font)
215 : original_font_(font),
216 next_font_set_(false),
217 linked_fonts_(NULL),
218 linked_font_index_(0) {
219 SetNextFont(original_font_);
220}
221
222LinkedFontsIterator::~LinkedFontsIterator() {
223}
224
225void LinkedFontsIterator::SetNextFont(Font font) {
226 next_font_ = font;
227 next_font_set_ = true;
228}
229
230bool LinkedFontsIterator::NextFont(Font* font) {
231 if (next_font_set_) {
232 next_font_set_ = false;
233 current_font_ = next_font_;
234 *font = current_font_;
235 return true;
236 }
237
238 // First time through, get the linked fonts list.
239 if (linked_fonts_ == NULL)
240 linked_fonts_ = GetLinkedFonts();
241
242 if (linked_font_index_ == linked_fonts_->size())
243 return false;
244
245 current_font_ = linked_fonts_->at(linked_font_index_++);
246 *font = current_font_;
247 return true;
248}
249
250const std::vector<Font>* LinkedFontsIterator::GetLinkedFonts() const {
251 CachedFontLinkSettings* font_link = CachedFontLinkSettings::GetInstance();
252
253 // First, try to get the list for the original font.
254 const std::vector<Font>* fonts = font_link->GetLinkedFonts(original_font_);
255
256 // If there are no linked fonts for the original font, try querying the
257 // ones for the current font. This may happen if the first font is a custom
258 // font that has no linked fonts in the registry.
259 //
260 // Note: One possibility would be to always merge both lists of fonts,
261 // but it is not clear whether there are any real world scenarios
262 // where this would actually help.
263 if (fonts->empty())
264 fonts = font_link->GetLinkedFonts(current_font_);
265
266 return fonts;
267}
268
[email protected]8c17e13d2014-07-19 03:01:39269} // namespace internal
270
271std::vector<std::string> GetFallbackFontFamilies(
272 const std::string& font_family) {
273 // LinkedFontsIterator doesn't care about the font size, so we always pass 10.
274 internal::LinkedFontsIterator linked_fonts(Font(font_family, 10));
275 std::vector<std::string> fallback_fonts;
276 Font current;
277 while (linked_fonts.NextFont(&current))
278 fallback_fonts.push_back(current.GetFontName());
279 return fallback_fonts;
280}
281
282bool GetUniscribeFallbackFont(const Font& font,
283 const wchar_t* text,
284 int text_length,
285 Font* result) {
286 // Adapted from WebKit's |FontCache::GetFontDataForCharacters()|.
287 // Uniscribe doesn't expose a method to query fallback fonts, so this works by
288 // drawing the text to an EMF object with Uniscribe's ScriptStringOut and then
289 // inspecting the EMF object to figure out which font Uniscribe used.
290 //
291 // DirectWrite in Windows 8.1 provides a cleaner alternative:
292 // http://msdn.microsoft.com/en-us/library/windows/desktop/dn280480.aspx
293
294 static HDC hdc = CreateCompatibleDC(NULL);
295
296 // Use a meta file to intercept the fallback font chosen by Uniscribe.
297 HDC meta_file_dc = CreateEnhMetaFile(hdc, NULL, NULL, NULL);
298 if (!meta_file_dc)
299 return false;
300
301 SelectObject(meta_file_dc, font.GetNativeFont());
302
303 SCRIPT_STRING_ANALYSIS script_analysis;
304 HRESULT hresult =
305 ScriptStringAnalyse(meta_file_dc, text, text_length, 0, -1,
306 SSA_METAFILE | SSA_FALLBACK | SSA_GLYPHS | SSA_LINK,
307 0, NULL, NULL, NULL, NULL, NULL, &script_analysis);
308
309 if (SUCCEEDED(hresult)) {
310 hresult = ScriptStringOut(script_analysis, 0, 0, 0, NULL, 0, 0, FALSE);
311 ScriptStringFree(&script_analysis);
312 }
313
314 bool found_fallback = false;
315 HENHMETAFILE meta_file = CloseEnhMetaFile(meta_file_dc);
316 if (SUCCEEDED(hresult)) {
317 LOGFONT log_font;
318 log_font.lfFaceName[0] = 0;
319 EnumEnhMetaFile(0, meta_file, MetaFileEnumProc, &log_font, NULL);
320 if (log_font.lfFaceName[0]) {
321 *result = Font(base::UTF16ToUTF8(log_font.lfFaceName),
322 font.GetFontSize());
323 found_fallback = true;
324 }
325 }
326 DeleteEnhMetaFile(meta_file);
327
328 return found_fallback;
329}
330
[email protected]76ac8d92012-09-07 20:12:57331} // namespace gfx