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