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.
5 #include "content/renderer/renderer_font_platform_win.h"
10 #include <wrl/implements.h>
11 #include <wrl/wrappers/corewrappers.h>
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"
30 namespace mswr
= Microsoft::WRL
;
31 namespace mswrw
= Microsoft::WRL::Wrappers
;
33 static const char kFontKeyName
[] = "font_key_name";
35 class FontCollectionLoader
36 : public mswr::RuntimeClass
<mswr::RuntimeClassFlags
<mswr::ClassicCom
>,
37 IDWriteFontCollectionLoader
> {
39 // IDWriteFontCollectionLoader methods.
40 virtual HRESULT STDMETHODCALLTYPE
41 CreateEnumeratorFromKey(IDWriteFactory
* factory
,
44 IDWriteFontFileEnumerator
** file_enumerator
);
46 static HRESULT
Initialize(IDWriteFactory
* factory
);
48 UINT32
GetFontMapSize();
50 std::wstring
GetFontNameFromKey(UINT32 idx
);
52 bool LoadFontListFromRegistry();
53 bool LoadRestrictedFontList();
55 FontCollectionLoader() {};
56 virtual ~FontCollectionLoader() {};
59 mswr::ComPtr
<IDWriteFontFileLoader
> file_loader_
;
61 std::vector
<std::wstring
> reg_fonts_
;
64 mswr::ComPtr
<FontCollectionLoader
> g_font_loader
;
67 : public mswr::RuntimeClass
<mswr::RuntimeClassFlags
<mswr::ClassicCom
>,
68 IDWriteFontFileStream
> {
70 // IDWriteFontFileStream methods.
71 virtual HRESULT STDMETHODCALLTYPE
72 ReadFileFragment(void const** fragment_start
,
76 if (!memory_
.get() || !memory_
->IsValid() ||
77 file_offset
>= memory_
->length() ||
78 (file_offset
+ fragment_size
) > memory_
->length())
81 *fragment_start
= static_cast<BYTE
const*>(memory_
->data()) +
82 static_cast<size_t>(file_offset
);
87 virtual void STDMETHODCALLTYPE
ReleaseFileFragment(void* context
) {}
89 virtual HRESULT STDMETHODCALLTYPE
GetFileSize(UINT64
* file_size
) {
90 if (!memory_
.get() || !memory_
->IsValid())
93 *file_size
= memory_
->length();
97 virtual HRESULT STDMETHODCALLTYPE
GetLastWriteTime(UINT64
* last_write_time
) {
98 if (!memory_
.get() || !memory_
->IsValid())
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;
111 FontFileStream::FontFileStream() : font_key_(0) {
114 HRESULT
RuntimeClassInitialize(UINT32 font_key
) {
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());
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
);
126 if (!memory_
->Initialize(path
)) {
131 font_key_
= font_key
;
133 base::debug::SetCrashKeyValue(kFontKeyName
,
134 base::WideToUTF8(font_key_name
));
138 virtual ~FontFileStream() {}
141 scoped_ptr
<base::MemoryMappedFile
> memory_
;
145 : public mswr::RuntimeClass
<mswr::RuntimeClassFlags
<mswr::ClassicCom
>,
146 IDWriteFontFileLoader
> {
148 // IDWriteFontFileLoader methods.
149 virtual HRESULT STDMETHODCALLTYPE
150 CreateStreamFromKey(void const* ref_key
,
152 IDWriteFontFileStream
** stream
) {
153 if (ref_key_size
!= sizeof(UINT32
))
156 UINT32 font_key
= *static_cast<const UINT32
*>(ref_key
);
157 mswr::ComPtr
<FontFileStream
> font_stream
;
158 HRESULT hr
= mswr::MakeAndInitialize
<FontFileStream
>(&font_stream
,
161 *stream
= font_stream
.Detach();
168 virtual ~FontFileLoader() {}
171 class FontFileEnumerator
172 : public mswr::RuntimeClass
<mswr::RuntimeClassFlags
<mswr::ClassicCom
>,
173 IDWriteFontFileEnumerator
> {
175 // IDWriteFontFileEnumerator methods.
176 virtual HRESULT STDMETHODCALLTYPE
MoveNext(BOOL
* has_current_file
) {
177 *has_current_file
= FALSE
;
180 current_file_
.ReleaseAndGetAddressOf();
182 if (font_idx_
< g_font_loader
->GetFontMapSize()) {
184 factory_
->CreateCustomFontFileReference(&font_idx_
,
187 current_file_
.GetAddressOf());
188 DCHECK(SUCCEEDED(hr
));
189 *has_current_file
= TRUE
;
195 virtual HRESULT STDMETHODCALLTYPE
196 GetCurrentFontFile(IDWriteFontFile
** font_file
) {
197 if (!current_file_
) {
202 *font_file
= current_file_
.Detach();
206 FontFileEnumerator(const void* keys
,
208 IDWriteFactory
* factory
,
209 IDWriteFontFileLoader
* file_loader
)
210 : factory_(factory
), file_loader_(file_loader
), font_idx_(0) {}
212 virtual ~FontFileEnumerator() {}
214 mswr::ComPtr
<IDWriteFactory
> factory_
;
215 mswr::ComPtr
<IDWriteFontFile
> current_file_
;
216 mswr::ComPtr
<IDWriteFontFileLoader
> file_loader_
;
220 // IDWriteFontCollectionLoader methods.
221 HRESULT STDMETHODCALLTYPE
FontCollectionLoader::CreateEnumeratorFromKey(
222 IDWriteFactory
* factory
,
225 IDWriteFontFileEnumerator
** file_enumerator
) {
226 *file_enumerator
= mswr::Make
<FontFileEnumerator
>(
227 key
, key_size
, factory
, file_loader_
.Get()).Detach();
232 HRESULT
FontCollectionLoader::Initialize(IDWriteFactory
* factory
) {
233 DCHECK(g_font_loader
== NULL
);
235 g_font_loader
= mswr::Make
<FontCollectionLoader
>();
236 if (!g_font_loader
) {
241 CHECK(g_font_loader
->LoadFontListFromRegistry());
243 g_font_loader
->file_loader_
= mswr::Make
<FontFileLoader
>().Detach();
245 factory
->RegisterFontFileLoader(g_font_loader
->file_loader_
.Get());
246 factory
->RegisterFontCollectionLoader(g_font_loader
.Get());
251 UINT32
FontCollectionLoader::GetFontMapSize() {
252 return reg_fonts_
.size();
255 std::wstring
FontCollectionLoader::GetFontNameFromKey(UINT32 idx
) {
256 DCHECK(idx
< reg_fonts_
.size());
257 return reg_fonts_
[idx
];
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
) !=
270 base::FilePath system_font_path
;
271 PathService::Get(base::DIR_WINDOWS_FONTS
, &system_font_path
);
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());
290 UMA_HISTOGRAM_COUNTS("DirectWrite.Fonts.Loaded", reg_fonts_
.size());
291 UMA_HISTOGRAM_COUNTS("DirectWrite.Fonts.Ignored",
292 regkey
.GetValueCount() - reg_fonts_
.size());
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
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
356 bool FontCollectionLoader::LoadRestrictedFontList() {
358 reg_fonts_
.assign(kRestrictedFontSet
,
359 kRestrictedFontSet
+ _countof(kRestrictedFontSet
));
367 mswr::ComPtr
<IDWriteFontCollection
> g_font_collection
;
369 IDWriteFontCollection
* GetCustomFontCollection(IDWriteFactory
* factory
) {
370 if (g_font_collection
.Get() != NULL
)
371 return g_font_collection
.Get();
373 base::TimeTicks start_tick
= base::TimeTicks::Now();
375 FontCollectionLoader::Initialize(factory
);
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;
381 if (g_font_loader
->GetFontMapSize() < kMaxFontThreshold
) {
382 hr
= factory
->CreateCustomFontCollection(
383 g_font_loader
.Get(), NULL
, 0, g_font_collection
.GetAddressOf());
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());
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
);
401 CHECK(SUCCEEDED(hr
));
402 CHECK(g_font_collection
.Get() != NULL
);
404 UMA_HISTOGRAM_TIMES("DirectWrite.Fonts.LoadTime", time_delta
);
406 base::debug::ClearCrashKey(kFontKeyName
);
408 return g_font_collection
.Get();
411 } // namespace content