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. 4 5#include "ui/gfx/font_fallback_win.h" 6 7#include <map> 8 9#include "base/memory/singleton.h" 10#include "base/strings/string_split.h" 11#include "base/strings/string_util.h" 12#include "base/strings/utf_string_conversions.h" 13#include "base/win/registry.h" 14#include "ui/gfx/font.h" 15 16namespace gfx { 17 18namespace { 19 20// Queries the registry to get a mapping from font filenames to font names. 21void QueryFontsFromRegistry(std::map<std::string, std::string>* map) { 22 const wchar_t* kFonts = 23 L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"; 24 25 base::win::RegistryValueIterator it(HKEY_LOCAL_MACHINE, kFonts); 26 for (; it.Valid(); ++it) { 27 const std::string filename = 28 StringToLowerASCII(base::WideToUTF8(it.Value())); 29 (*map)[filename] = base::WideToUTF8(it.Name()); 30 } 31} 32 33// Fills |font_names| with a list of font families found in the font file at 34// |filename|. Takes in a |font_map| from font filename to font families, which 35// is filled-in by querying the registry, if empty. 36void GetFontNamesFromFilename(const std::string& filename, 37 std::map<std::string, std::string>* font_map, 38 std::vector<std::string>* font_names) { 39 if (font_map->empty()) 40 QueryFontsFromRegistry(font_map); 41 42 std::map<std::string, std::string>::const_iterator it = 43 font_map->find(StringToLowerASCII(filename)); 44 if (it == font_map->end()) 45 return; 46 47 internal::ParseFontFamilyString(it->second, font_names); 48} 49 50// Returns true if |text| contains only ASCII digits. 51bool ContainsOnlyDigits(const std::string& text) { 52 return text.find_first_not_of("0123456789") == base::string16::npos; 53} 54 55// Appends a Font with the given |name| and |size| to |fonts| unless the last 56// entry is already a font with that name. 57void AppendFont(const std::string& name, int size, std::vector<Font>* fonts) { 58 if (fonts->empty() || fonts->back().GetFontName() != name) 59 fonts->push_back(Font(name, size)); 60} 61 62// Queries the registry to get a list of linked fonts for |font|. 63void QueryLinkedFontsFromRegistry(const Font& font, 64 std::map<std::string, std::string>* font_map, 65 std::vector<Font>* linked_fonts) { 66 const wchar_t* kSystemLink = 67 L"Software\\Microsoft\\Windows NT\\CurrentVersion\\FontLink\\SystemLink"; 68 69 base::win::RegKey key; 70 if (FAILED(key.Open(HKEY_LOCAL_MACHINE, kSystemLink, KEY_READ))) 71 return; 72 73 const std::wstring original_font_name = base::UTF8ToWide(font.GetFontName()); 74 std::vector<std::wstring> values; 75 if (FAILED(key.ReadValues(original_font_name.c_str(), &values))) { 76 key.Close(); 77 return; 78 } 79 80 std::string filename; 81 std::string font_name; 82 for (size_t i = 0; i < values.size(); ++i) { 83 internal::ParseFontLinkEntry( 84 base::WideToUTF8(values[i]), &filename, &font_name); 85 // If the font name is present, add that directly, otherwise add the 86 // font names corresponding to the filename. 87 if (!font_name.empty()) { 88 AppendFont(font_name, font.GetFontSize(), linked_fonts); 89 } else if (!filename.empty()) { 90 std::vector<std::string> font_names; 91 GetFontNamesFromFilename(filename, font_map, &font_names); 92 for (size_t i = 0; i < font_names.size(); ++i) 93 AppendFont(font_names[i], font.GetFontSize(), linked_fonts); 94 } 95 } 96 97 key.Close(); 98} 99 100// CachedFontLinkSettings is a singleton cache of the Windows font settings 101// from the registry. It maintains a cached view of the registry's list of 102// system fonts and their font link chains. 103class CachedFontLinkSettings { 104 public: 105 static CachedFontLinkSettings* GetInstance(); 106 107 // Returns the linked fonts list correspond to |font|. Returned value will 108 // never be null. 109 const std::vector<Font>* GetLinkedFonts(const Font& font); 110 111 private: 112 friend struct DefaultSingletonTraits<CachedFontLinkSettings>; 113 114 CachedFontLinkSettings(); 115 virtual ~CachedFontLinkSettings(); 116 117 // Map of system fonts, from file names to font families. 118 std::map<std::string, std::string> cached_system_fonts_; 119 120 // Map from font names to vectors of linked fonts. 121 std::map<std::string, std::vector<Font> > cached_linked_fonts_; 122 123 DISALLOW_COPY_AND_ASSIGN(CachedFontLinkSettings); 124}; 125 126// static 127CachedFontLinkSettings* CachedFontLinkSettings::GetInstance() { 128 return Singleton<CachedFontLinkSettings, 129 LeakySingletonTraits<CachedFontLinkSettings> >::get(); 130} 131 132const std::vector<Font>* CachedFontLinkSettings::GetLinkedFonts( 133 const Font& font) { 134 const std::string& font_name = font.GetFontName(); 135 std::map<std::string, std::vector<Font> >::const_iterator it = 136 cached_linked_fonts_.find(font_name); 137 if (it != cached_linked_fonts_.end()) 138 return &it->second; 139 140 cached_linked_fonts_[font_name] = std::vector<Font>(); 141 std::vector<Font>* linked_fonts = &cached_linked_fonts_[font_name]; 142 QueryLinkedFontsFromRegistry(font, &cached_system_fonts_, linked_fonts); 143 return linked_fonts; 144} 145 146CachedFontLinkSettings::CachedFontLinkSettings() { 147} 148 149CachedFontLinkSettings::~CachedFontLinkSettings() { 150} 151 152} // namespace 153 154namespace internal { 155 156void ParseFontLinkEntry(const std::string& entry, 157 std::string* filename, 158 std::string* font_name) { 159 std::vector<std::string> parts; 160 base::SplitString(entry, ',', &parts); 161 filename->clear(); 162 font_name->clear(); 163 if (parts.size() > 0) 164 *filename = parts[0]; 165 // The second entry may be the font name or the first scaling factor, if the 166 // entry does not contain a font name. If it contains only digits, assume it 167 // is a scaling factor. 168 if (parts.size() > 1 && !ContainsOnlyDigits(parts[1])) 169 *font_name = parts[1]; 170} 171 172void ParseFontFamilyString(const std::string& family, 173 std::vector<std::string>* font_names) { 174 // The entry is comma separated, having the font filename as the first value 175 // followed optionally by the font family name and a pair of integer scaling 176 // factors. 177 // TODO(asvitkine): Should we support these scaling factors? 178 base::SplitString(family, '&', font_names); 179 if (!font_names->empty()) { 180 const size_t index = font_names->back().find('('); 181 if (index != std::string::npos) { 182 font_names->back().resize(index); 183 base::TrimWhitespace(font_names->back(), base::TRIM_TRAILING, 184 &font_names->back()); 185 } 186 } 187} 188 189} // namespace internal 190 191LinkedFontsIterator::LinkedFontsIterator(Font font) 192 : original_font_(font), 193 next_font_set_(false), 194 linked_fonts_(NULL), 195 linked_font_index_(0) { 196 SetNextFont(original_font_); 197} 198 199LinkedFontsIterator::~LinkedFontsIterator() { 200} 201 202void LinkedFontsIterator::SetNextFont(Font font) { 203 next_font_ = font; 204 next_font_set_ = true; 205} 206 207bool LinkedFontsIterator::NextFont(Font* font) { 208 if (next_font_set_) { 209 next_font_set_ = false; 210 current_font_ = next_font_; 211 *font = current_font_; 212 return true; 213 } 214 215 // First time through, get the linked fonts list. 216 if (linked_fonts_ == NULL) 217 linked_fonts_ = GetLinkedFonts(); 218 219 if (linked_font_index_ == linked_fonts_->size()) 220 return false; 221 222 current_font_ = linked_fonts_->at(linked_font_index_++); 223 *font = current_font_; 224 return true; 225} 226 227const std::vector<Font>* LinkedFontsIterator::GetLinkedFonts() const { 228 CachedFontLinkSettings* font_link = CachedFontLinkSettings::GetInstance(); 229 230 // First, try to get the list for the original font. 231 const std::vector<Font>* fonts = font_link->GetLinkedFonts(original_font_); 232 233 // If there are no linked fonts for the original font, try querying the 234 // ones for the current font. This may happen if the first font is a custom 235 // font that has no linked fonts in the registry. 236 // 237 // Note: One possibility would be to always merge both lists of fonts, 238 // but it is not clear whether there are any real world scenarios 239 // where this would actually help. 240 if (fonts->empty()) 241 fonts = font_link->GetLinkedFonts(current_font_); 242 243 return fonts; 244} 245 246} // namespace gfx 247