1 // Copyright 2014 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 "content/renderer/renderer_font_platform_win.h" 6 7 #include <dwrite.h> 8 #include <string> 9 #include <vector> 10 #include <wrl/implements.h> 11 #include <wrl/wrappers/corewrappers.h> 12 13 #include "base/debug/alias.h" 14 #include "base/debug/crash_logging.h" 15 #include "base/files/file_enumerator.h" 16 #include "base/files/file_path.h" 17 #include "base/files/memory_mapped_file.h" 18 #include "base/memory/scoped_ptr.h" 19 #include "base/memory/scoped_vector.h" 20 #include "base/metrics/histogram.h" 21 #include "base/path_service.h" 22 #include "base/strings/utf_string_conversions.h" 23 #include "base/time/time.h" 24 #include "base/win/iat_patch_function.h" 25 #include "base/win/registry.h" 26 #include "base/win/scoped_comptr.h" 27 28 namespace { 29 30 namespace mswr = Microsoft::WRL; 31 namespace mswrw = Microsoft::WRL::Wrappers; 32 33 static const char kFontKeyName[] = "font_key_name"; 34 35 class FontCollectionLoader 36 : public mswr::RuntimeClass<mswr::RuntimeClassFlags<mswr::ClassicCom>, 37 IDWriteFontCollectionLoader> { 38 public: 39 // IDWriteFontCollectionLoader methods. 40 virtual HRESULT STDMETHODCALLTYPE 41 CreateEnumeratorFromKey(IDWriteFactory* factory, 42 void const* key, 43 UINT32 key_size, 44 IDWriteFontFileEnumerator** file_enumerator); 45 46 static HRESULT Initialize(IDWriteFactory* factory); 47 48 UINT32 GetFontMapSize(); 49 50 std::wstring GetFontNameFromKey(UINT32 idx); 51 52 bool LoadFontListFromRegistry(); 53 bool LoadRestrictedFontList(); 54 55 FontCollectionLoader() {}; 56 virtual ~FontCollectionLoader() {}; 57 58 private: 59 mswr::ComPtr<IDWriteFontFileLoader> file_loader_; 60 61 std::vector<std::wstring> reg_fonts_; 62 }; 63 64 mswr::ComPtr<FontCollectionLoader> g_font_loader; 65 66 class FontFileStream 67 : public mswr::RuntimeClass<mswr::RuntimeClassFlags<mswr::ClassicCom>, 68 IDWriteFontFileStream> { 69 public: 70 // IDWriteFontFileStream methods. 71 virtual HRESULT STDMETHODCALLTYPE 72 ReadFileFragment(void const** fragment_start, 73 UINT64 file_offset, 74 UINT64 fragment_size, 75 void** context) { 76 if (!memory_.get() || !memory_->IsValid() || 77 file_offset >= memory_->length() || 78 (file_offset + fragment_size) > memory_->length()) 79 return E_FAIL; 80 81 *fragment_start = static_cast<BYTE const*>(memory_->data()) + 82 static_cast<size_t>(file_offset); 83 *context = NULL; 84 return S_OK; 85 } 86 87 virtual void STDMETHODCALLTYPE ReleaseFileFragment(void* context) {} 88 89 virtual HRESULT STDMETHODCALLTYPE GetFileSize(UINT64* file_size) { 90 if (!memory_.get() || !memory_->IsValid()) 91 return E_FAIL; 92 93 *file_size = memory_->length(); 94 return S_OK; 95 } 96 97 virtual HRESULT STDMETHODCALLTYPE GetLastWriteTime(UINT64* last_write_time) { 98 if (!memory_.get() || !memory_->IsValid()) 99 return E_FAIL; 100 101 // According to MSDN article http://goo.gl/rrSYzi the "last modified time" 102 // is used by DirectWrite font selection algorithms to determine whether 103 // one font resource is more up to date than another one. 104 // So by returning 0 we are assuming that it will treat all fonts to be 105 // equally up to date. 106 // TODO(shrikant): We should further investigate this. 107 *last_write_time = 0; 108 return S_OK; 109 } 110 111 FontFileStream::FontFileStream() : font_key_(0) { 112 }; 113 114 HRESULT RuntimeClassInitialize(UINT32 font_key) { 115 base::FilePath path; 116 PathService::Get(base::DIR_WINDOWS_FONTS, &path); 117 std::wstring font_key_name(g_font_loader->GetFontNameFromKey(font_key)); 118 path = path.Append(font_key_name.c_str()); 119 memory_.reset(new base::MemoryMappedFile()); 120 121 // Put some debug information on stack. 122 WCHAR font_name[256]; 123 path.value().copy(font_name, arraysize(font_name)); 124 base::debug::Alias(font_name); 125 126 if (!memory_->Initialize(path)) { 127 memory_.reset(); 128 return E_FAIL; 129 } 130 131 font_key_ = font_key; 132 133 base::debug::SetCrashKeyValue(kFontKeyName, 134 base::WideToUTF8(font_key_name)); 135 return S_OK; 136 } 137 138 virtual ~FontFileStream() {} 139 140 UINT32 font_key_; 141 scoped_ptr<base::MemoryMappedFile> memory_; 142 }; 143 144 class FontFileLoader 145 : public mswr::RuntimeClass<mswr::RuntimeClassFlags<mswr::ClassicCom>, 146 IDWriteFontFileLoader> { 147 public: 148 // IDWriteFontFileLoader methods. 149 virtual HRESULT STDMETHODCALLTYPE 150 CreateStreamFromKey(void const* ref_key, 151 UINT32 ref_key_size, 152 IDWriteFontFileStream** stream) { 153 if (ref_key_size != sizeof(UINT32)) 154 return E_FAIL; 155 156 UINT32 font_key = *static_cast<const UINT32*>(ref_key); 157 mswr::ComPtr<FontFileStream> font_stream; 158 HRESULT hr = mswr::MakeAndInitialize<FontFileStream>(&font_stream, 159 font_key); 160 if (SUCCEEDED(hr)) { 161 *stream = font_stream.Detach(); 162 return S_OK; 163 } 164 return E_FAIL; 165 } 166 167 FontFileLoader() {} 168 virtual ~FontFileLoader() {} 169 }; 170 171 class FontFileEnumerator 172 : public mswr::RuntimeClass<mswr::RuntimeClassFlags<mswr::ClassicCom>, 173 IDWriteFontFileEnumerator> { 174 public: 175 // IDWriteFontFileEnumerator methods. 176 virtual HRESULT STDMETHODCALLTYPE MoveNext(BOOL* has_current_file) { 177 *has_current_file = FALSE; 178 179 if (current_file_) 180 current_file_.ReleaseAndGetAddressOf(); 181 182 if (font_idx_ < g_font_loader->GetFontMapSize()) { 183 HRESULT hr = 184 factory_->CreateCustomFontFileReference(&font_idx_, 185 sizeof(UINT32), 186 file_loader_.Get(), 187 current_file_.GetAddressOf()); 188 DCHECK(SUCCEEDED(hr)); 189 *has_current_file = TRUE; 190 font_idx_++; 191 } 192 return S_OK; 193 } 194 195 virtual HRESULT STDMETHODCALLTYPE 196 GetCurrentFontFile(IDWriteFontFile** font_file) { 197 if (!current_file_) { 198 *font_file = NULL; 199 return E_FAIL; 200 } 201 202 *font_file = current_file_.Detach(); 203 return S_OK; 204 } 205 206 FontFileEnumerator(const void* keys, 207 UINT32 buffer_size, 208 IDWriteFactory* factory, 209 IDWriteFontFileLoader* file_loader) 210 : factory_(factory), file_loader_(file_loader), font_idx_(0) {} 211 212 virtual ~FontFileEnumerator() {} 213 214 mswr::ComPtr<IDWriteFactory> factory_; 215 mswr::ComPtr<IDWriteFontFile> current_file_; 216 mswr::ComPtr<IDWriteFontFileLoader> file_loader_; 217 UINT32 font_idx_; 218 }; 219 220 // IDWriteFontCollectionLoader methods. 221 HRESULT STDMETHODCALLTYPE FontCollectionLoader::CreateEnumeratorFromKey( 222 IDWriteFactory* factory, 223 void const* key, 224 UINT32 key_size, 225 IDWriteFontFileEnumerator** file_enumerator) { 226 *file_enumerator = mswr::Make<FontFileEnumerator>( 227 key, key_size, factory, file_loader_.Get()).Detach(); 228 return S_OK; 229 } 230 231 // static 232 HRESULT FontCollectionLoader::Initialize(IDWriteFactory* factory) { 233 DCHECK(g_font_loader == NULL); 234 235 g_font_loader = mswr::Make<FontCollectionLoader>(); 236 if (!g_font_loader) { 237 DCHECK(FALSE); 238 return E_FAIL; 239 } 240 241 CHECK(g_font_loader->LoadFontListFromRegistry()); 242 243 g_font_loader->file_loader_ = mswr::Make<FontFileLoader>().Detach(); 244 245 factory->RegisterFontFileLoader(g_font_loader->file_loader_.Get()); 246 factory->RegisterFontCollectionLoader(g_font_loader.Get()); 247 248 return S_OK; 249 } 250 251 UINT32 FontCollectionLoader::GetFontMapSize() { 252 return reg_fonts_.size(); 253 } 254 255 std::wstring FontCollectionLoader::GetFontNameFromKey(UINT32 idx) { 256 DCHECK(idx < reg_fonts_.size()); 257 return reg_fonts_[idx]; 258 } 259 260 bool FontCollectionLoader::LoadFontListFromRegistry() { 261 const wchar_t kFontsRegistry[] = 262 L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"; 263 CHECK(reg_fonts_.empty()); 264 base::win::RegKey regkey; 265 if (regkey.Open(HKEY_LOCAL_MACHINE, kFontsRegistry, KEY_READ) != 266 ERROR_SUCCESS) { 267 return false; 268 } 269 270 base::FilePath system_font_path; 271 PathService::Get(base::DIR_WINDOWS_FONTS, &system_font_path); 272 273 std::wstring name; 274 std::wstring value; 275 for (DWORD idx = 0; idx < regkey.GetValueCount(); idx++) { 276 if (regkey.GetValueNameAt(idx, &name) == ERROR_SUCCESS && 277 regkey.ReadValue(name.c_str(), &value) == ERROR_SUCCESS) { 278 base::FilePath path(value.c_str()); 279 // We need to check if file name is the only component that exists, 280 // we will ignore all other registry entries. 281 std::vector<base::FilePath::StringType> components; 282 path.GetComponents(&components); 283 if (components.size() == 1 || 284 base::FilePath::CompareEqualIgnoreCase(system_font_path.value(), 285 path.DirName().value())) { 286 reg_fonts_.push_back(path.BaseName().value()); 287 } 288 } 289 } 290 UMA_HISTOGRAM_COUNTS("DirectWrite.Fonts.Loaded", reg_fonts_.size()); 291 UMA_HISTOGRAM_COUNTS("DirectWrite.Fonts.Ignored", 292 regkey.GetValueCount() - reg_fonts_.size()); 293 return true; 294 } 295 296 // This list is mainly based on prefs/prefs_tab_helper.cc kFontDefaults. 297 const wchar_t* kRestrictedFontSet[] = { 298 // These are the "Web Safe" fonts. 299 L"times.ttf", // IDS_STANDARD_FONT_FAMILY 300 L"timesbd.ttf", // IDS_STANDARD_FONT_FAMILY 301 L"timesbi.ttf", // IDS_STANDARD_FONT_FAMILY 302 L"timesi.ttf", // IDS_STANDARD_FONT_FAMILY 303 L"cour.ttf", // IDS_FIXED_FONT_FAMILY 304 L"courbd.ttf", // IDS_FIXED_FONT_FAMILY 305 L"courbi.ttf", // IDS_FIXED_FONT_FAMILY 306 L"couri.ttf", // IDS_FIXED_FONT_FAMILY 307 L"consola.ttf", // IDS_FIXED_FONT_FAMILY_ALT_WIN 308 L"consolab.ttf", // IDS_FIXED_FONT_FAMILY_ALT_WIN 309 L"consolai.ttf", // IDS_FIXED_FONT_FAMILY_ALT_WIN 310 L"consolaz.ttf", // IDS_FIXED_FONT_FAMILY_ALT_WIN 311 L"arial.ttf", // IDS_SANS_SERIF_FONT_FAMILY 312 L"arialbd.ttf", // IDS_SANS_SERIF_FONT_FAMILY 313 L"arialbi.ttf", // IDS_SANS_SERIF_FONT_FAMILY 314 L"ariali.ttf", // IDS_SANS_SERIF_FONT_FAMILY 315 L"comic.ttf", // IDS_CURSIVE_FONT_FAMILY 316 L"comicbd.ttf", // IDS_CURSIVE_FONT_FAMILY 317 L"comici.ttf", // IDS_CURSIVE_FONT_FAMILY 318 L"comicz.ttf", // IDS_CURSIVE_FONT_FAMILY 319 L"impact.ttf", // IDS_FANTASY_FONT_FAMILY 320 L"segoeui.ttf", // IDS_PICTOGRAPH_FONT_FAMILY 321 L"segoeuib.ttf", // IDS_PICTOGRAPH_FONT_FAMILY 322 L"segoeuii.ttf", // IDS_PICTOGRAPH_FONT_FAMILY 323 L"msgothic.ttc", // IDS_STANDARD_FONT_FAMILY_JAPANESE 324 L"msmincho.ttc", // IDS_SERIF_FONT_FAMILY_JAPANESE 325 L"gulim.ttc", // IDS_FIXED_FONT_FAMILY_KOREAN 326 L"batang.ttc", // IDS_SERIF_FONT_FAMILY_KOREAN 327 L"simsun.ttc", // IDS_STANDARD_FONT_FAMILY_SIMPLIFIED_HAN 328 L"mingliu.ttc", // IDS_SERIF_FONT_FAMILY_TRADITIONAL_HAN 329 330 // These are from the Blink fallback list. 331 L"david.ttf", // USCRIPT_HEBREW 332 L"davidbd.ttf", // USCRIPT_HEBREW 333 L"euphemia.ttf", // USCRIPT_CANADIAN_ABORIGINAL 334 L"gautami.ttf", // USCRIPT_TELUGU 335 L"gautamib.ttf", // USCRIPT_TELUGU 336 L"latha.ttf", // USCRIPT_TAMIL 337 L"lathab.ttf", // USCRIPT_TAMIL 338 L"mangal.ttf", // USCRIPT_DEVANAGARI 339 L"mangalb.ttf", // USCRIPT_DEVANAGARI 340 L"monbaiti.ttf", // USCRIPT_MONGOLIAN 341 L"mvboli.ttf", // USCRIPT_THAANA 342 L"plantc.ttf", // USCRIPT_CHEROKEE 343 L"raavi.ttf", // USCRIPT_GURMUKHI 344 L"raavib.ttf", // USCRIPT_GURMUKHI 345 L"shruti.ttf", // USCRIPT_GUJARATI 346 L"shrutib.ttf", // USCRIPT_GUJARATI 347 L"sylfaen.ttf", // USCRIPT_GEORGIAN and USCRIPT_ARMENIAN 348 L"tahoma.ttf", // USCRIPT_ARABIC, 349 L"tahomabd.ttf", // USCRIPT_ARABIC, 350 L"tunga.ttf", // USCRIPT_KANNADA 351 L"tungab.ttf", // USCRIPT_KANNADA 352 L"vrinda.ttf", // USCRIPT_BENGALI 353 L"vrindab.ttf", // USCRIPT_BENGALI 354 }; 355 356 bool FontCollectionLoader::LoadRestrictedFontList() { 357 reg_fonts_.clear(); 358 reg_fonts_.assign(kRestrictedFontSet, 359 kRestrictedFontSet + _countof(kRestrictedFontSet)); 360 return true; 361 } 362 363 } // namespace 364 365 namespace content { 366 367 mswr::ComPtr<IDWriteFontCollection> g_font_collection; 368 369 IDWriteFontCollection* GetCustomFontCollection(IDWriteFactory* factory) { 370 if (g_font_collection.Get() != NULL) 371 return g_font_collection.Get(); 372 373 base::TimeTicks start_tick = base::TimeTicks::Now(); 374 375 FontCollectionLoader::Initialize(factory); 376 377 // We try here to put arbitrary limit on max number of fonts that could 378 // be loaded, otherwise we fallback to restricted set of fonts. 379 const UINT32 kMaxFontThreshold = 1750; 380 HRESULT hr = E_FAIL; 381 if (g_font_loader->GetFontMapSize() < kMaxFontThreshold) { 382 hr = factory->CreateCustomFontCollection( 383 g_font_loader.Get(), NULL, 0, g_font_collection.GetAddressOf()); 384 } 385 386 bool loadingRestricted = false; 387 if (FAILED(hr) || !g_font_collection.Get()) { 388 // We will try here just one more time with restricted font set. 389 g_font_loader->LoadRestrictedFontList(); 390 hr = factory->CreateCustomFontCollection( 391 g_font_loader.Get(), NULL, 0, g_font_collection.GetAddressOf()); 392 } 393 394 base::TimeDelta time_delta = base::TimeTicks::Now() - start_tick; 395 int64 delta = time_delta.ToInternalValue(); 396 base::debug::Alias(&delta); 397 UINT32 size = g_font_loader->GetFontMapSize(); 398 base::debug::Alias(&size); 399 base::debug::Alias(&loadingRestricted); 400 401 CHECK(SUCCEEDED(hr)); 402 CHECK(g_font_collection.Get() != NULL); 403 404 UMA_HISTOGRAM_TIMES("DirectWrite.Fonts.LoadTime", time_delta); 405 406 base::debug::ClearCrashKey(kFontKeyName); 407 408 return g_font_collection.Get(); 409 } 410 411 } // namespace content 412