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