Only grant permissions to new extensions from sync if they have the expected version
[chromium-blink-merge.git] / ui / gfx / font_fallback_win.cc
blob1612f3c72194b4defe7396ff71572fe9971e6dc1
1 // 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.
5 #include "ui/gfx/font_fallback_win.h"
7 #include <usp10.h>
9 #include <map>
11 #include "base/memory/singleton.h"
12 #include "base/profiler/scoped_tracker.h"
13 #include "base/strings/string_split.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/win/registry.h"
17 #include "ui/gfx/font.h"
18 #include "ui/gfx/font_fallback.h"
20 namespace gfx {
22 namespace {
24 // Queries the registry to get a mapping from font filenames to font names.
25 void QueryFontsFromRegistry(std::map<std::string, std::string>* map) {
26 const wchar_t* kFonts =
27 L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts";
29 base::win::RegistryValueIterator it(HKEY_LOCAL_MACHINE, kFonts);
30 for (; it.Valid(); ++it) {
31 const std::string filename =
32 base::ToLowerASCII(base::WideToUTF8(it.Value()));
33 (*map)[filename] = base::WideToUTF8(it.Name());
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.
40 void 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);
46 std::map<std::string, std::string>::const_iterator it =
47 font_map->find(base::ToLowerASCII(filename));
48 if (it == font_map->end())
49 return;
51 internal::ParseFontFamilyString(it->second, font_names);
54 // Returns true if |text| contains only ASCII digits.
55 bool ContainsOnlyDigits(const std::string& text) {
56 return text.find_first_not_of("0123456789") == base::string16::npos;
59 // Appends a Font with the given |name| and |size| to |fonts| unless the last
60 // entry is already a font with that name.
61 void 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));
66 // Queries the registry to get a list of linked fonts for |font|.
67 void QueryLinkedFontsFromRegistry(const Font& font,
68 std::map<std::string, std::string>* font_map,
69 std::vector<Font>* linked_fonts) {
70 const wchar_t* kSystemLink =
71 L"Software\\Microsoft\\Windows NT\\CurrentVersion\\FontLink\\SystemLink";
73 base::win::RegKey key;
74 if (FAILED(key.Open(HKEY_LOCAL_MACHINE, kSystemLink, KEY_READ)))
75 return;
77 const std::wstring original_font_name = base::UTF8ToWide(font.GetFontName());
78 std::vector<std::wstring> values;
79 if (FAILED(key.ReadValues(original_font_name.c_str(), &values))) {
80 key.Close();
81 return;
84 std::string filename;
85 std::string font_name;
86 for (size_t i = 0; i < values.size(); ++i) {
87 internal::ParseFontLinkEntry(
88 base::WideToUTF8(values[i]), &filename, &font_name);
89 // 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);
101 key.Close();
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.
107 class CachedFontLinkSettings {
108 public:
109 static CachedFontLinkSettings* GetInstance();
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);
115 private:
116 friend struct base::DefaultSingletonTraits<CachedFontLinkSettings>;
118 CachedFontLinkSettings();
119 virtual ~CachedFontLinkSettings();
121 // Map of system fonts, from file names to font families.
122 std::map<std::string, std::string> cached_system_fonts_;
124 // Map from font names to vectors of linked fonts.
125 std::map<std::string, std::vector<Font> > cached_linked_fonts_;
127 DISALLOW_COPY_AND_ASSIGN(CachedFontLinkSettings);
130 // static
131 CachedFontLinkSettings* CachedFontLinkSettings::GetInstance() {
132 return base::Singleton<
133 CachedFontLinkSettings,
134 base::LeakySingletonTraits<CachedFontLinkSettings>>::get();
137 const std::vector<Font>* CachedFontLinkSettings::GetLinkedFonts(
138 const Font& font) {
139 const std::string& font_name = font.GetFontName();
140 std::map<std::string, std::vector<Font> >::const_iterator it =
141 cached_linked_fonts_.find(font_name);
142 if (it != cached_linked_fonts_.end())
143 return &it->second;
145 cached_linked_fonts_[font_name] = std::vector<Font>();
146 std::vector<Font>* linked_fonts = &cached_linked_fonts_[font_name];
148 // TODO(ckocagil): Remove ScopedTracker below once crbug.com/441028 is fixed.
149 tracked_objects::ScopedTracker tracking_profile(
150 FROM_HERE_WITH_EXPLICIT_FUNCTION(
151 "441028 QueryLinkedFontsFromRegistry()"));
153 QueryLinkedFontsFromRegistry(font, &cached_system_fonts_, linked_fonts);
154 return linked_fonts;
157 CachedFontLinkSettings::CachedFontLinkSettings() {
160 CachedFontLinkSettings::~CachedFontLinkSettings() {
163 // Callback to |EnumEnhMetaFile()| to intercept font creation.
164 int CALLBACK MetaFileEnumProc(HDC hdc,
165 HANDLETABLE* table,
166 CONST ENHMETARECORD* record,
167 int table_entries,
168 LPARAM log_font) {
169 if (record->iType == EMR_EXTCREATEFONTINDIRECTW) {
170 const EMREXTCREATEFONTINDIRECTW* create_font_record =
171 reinterpret_cast<const EMREXTCREATEFONTINDIRECTW*>(record);
172 *reinterpret_cast<LOGFONT*>(log_font) = create_font_record->elfw.elfLogFont;
174 return 1;
177 } // namespace
179 namespace internal {
181 void ParseFontLinkEntry(const std::string& entry,
182 std::string* filename,
183 std::string* font_name) {
184 std::vector<std::string> parts = base::SplitString(
185 entry, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
186 filename->clear();
187 font_name->clear();
188 if (parts.size() > 0)
189 *filename = parts[0];
190 // The second entry may be the font name or the first scaling factor, if the
191 // entry does not contain a font name. If it contains only digits, assume it
192 // is a scaling factor.
193 if (parts.size() > 1 && !ContainsOnlyDigits(parts[1]))
194 *font_name = parts[1];
197 void ParseFontFamilyString(const std::string& family,
198 std::vector<std::string>* font_names) {
199 // The entry is comma separated, having the font filename as the first value
200 // followed optionally by the font family name and a pair of integer scaling
201 // factors.
202 // TODO(asvitkine): Should we support these scaling factors?
203 *font_names = base::SplitString(
204 family, "&", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
205 if (!font_names->empty()) {
206 const size_t index = font_names->back().find('(');
207 if (index != std::string::npos) {
208 font_names->back().resize(index);
209 base::TrimWhitespace(font_names->back(), base::TRIM_TRAILING,
210 &font_names->back());
215 LinkedFontsIterator::LinkedFontsIterator(Font font)
216 : original_font_(font),
217 next_font_set_(false),
218 linked_fonts_(NULL),
219 linked_font_index_(0) {
220 SetNextFont(original_font_);
223 LinkedFontsIterator::~LinkedFontsIterator() {
226 void LinkedFontsIterator::SetNextFont(Font font) {
227 next_font_ = font;
228 next_font_set_ = true;
231 bool LinkedFontsIterator::NextFont(Font* font) {
232 if (next_font_set_) {
233 next_font_set_ = false;
234 current_font_ = next_font_;
235 *font = current_font_;
236 return true;
239 // First time through, get the linked fonts list.
240 if (linked_fonts_ == NULL)
241 linked_fonts_ = GetLinkedFonts();
243 if (linked_font_index_ == linked_fonts_->size())
244 return false;
246 current_font_ = linked_fonts_->at(linked_font_index_++);
247 *font = current_font_;
248 return true;
251 const std::vector<Font>* LinkedFontsIterator::GetLinkedFonts() const {
252 CachedFontLinkSettings* font_link = CachedFontLinkSettings::GetInstance();
254 // First, try to get the list for the original font.
255 const std::vector<Font>* fonts = font_link->GetLinkedFonts(original_font_);
257 // If there are no linked fonts for the original font, try querying the
258 // ones for the current font. This may happen if the first font is a custom
259 // font that has no linked fonts in the registry.
261 // Note: One possibility would be to always merge both lists of fonts,
262 // but it is not clear whether there are any real world scenarios
263 // where this would actually help.
264 if (fonts->empty())
265 fonts = font_link->GetLinkedFonts(current_font_);
267 return fonts;
270 } // namespace internal
272 std::vector<std::string> GetFallbackFontFamilies(
273 const std::string& font_family) {
274 // LinkedFontsIterator doesn't care about the font size, so we always pass 10.
275 internal::LinkedFontsIterator linked_fonts(Font(font_family, 10));
276 std::vector<std::string> fallback_fonts;
277 Font current;
278 while (linked_fonts.NextFont(&current))
279 fallback_fonts.push_back(current.GetFontName());
280 return fallback_fonts;
283 bool GetUniscribeFallbackFont(const Font& font,
284 const wchar_t* text,
285 int text_length,
286 Font* result) {
287 // Adapted from WebKit's |FontCache::GetFontDataForCharacters()|.
288 // Uniscribe doesn't expose a method to query fallback fonts, so this works by
289 // drawing the text to an EMF object with Uniscribe's ScriptStringOut and then
290 // inspecting the EMF object to figure out which font Uniscribe used.
292 // DirectWrite in Windows 8.1 provides a cleaner alternative:
293 // http://msdn.microsoft.com/en-us/library/windows/desktop/dn280480.aspx
295 static HDC hdc = CreateCompatibleDC(NULL);
297 // Use a meta file to intercept the fallback font chosen by Uniscribe.
298 HDC meta_file_dc = CreateEnhMetaFile(hdc, NULL, NULL, NULL);
299 if (!meta_file_dc)
300 return false;
302 SelectObject(meta_file_dc, font.GetNativeFont());
304 SCRIPT_STRING_ANALYSIS script_analysis;
305 HRESULT hresult =
306 ScriptStringAnalyse(meta_file_dc, text, text_length, 0, -1,
307 SSA_METAFILE | SSA_FALLBACK | SSA_GLYPHS | SSA_LINK,
308 0, NULL, NULL, NULL, NULL, NULL, &script_analysis);
310 if (SUCCEEDED(hresult)) {
311 hresult = ScriptStringOut(script_analysis, 0, 0, 0, NULL, 0, 0, FALSE);
312 ScriptStringFree(&script_analysis);
315 bool found_fallback = false;
316 HENHMETAFILE meta_file = CloseEnhMetaFile(meta_file_dc);
317 if (SUCCEEDED(hresult)) {
318 LOGFONT log_font;
319 log_font.lfFaceName[0] = 0;
320 EnumEnhMetaFile(0, meta_file, MetaFileEnumProc, &log_font, NULL);
321 if (log_font.lfFaceName[0]) {
322 *result = Font(base::UTF16ToUTF8(log_font.lfFaceName),
323 font.GetFontSize());
324 found_fallback = true;
327 DeleteEnhMetaFile(meta_file);
329 return found_fallback;
332 } // namespace gfx