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/public/common/dwrite_font_platform_win.h"
14 #include <wrl/implements.h>
15 #include <wrl/wrappers/corewrappers.h>
17 #include "base/command_line.h"
18 #include "base/debug/alias.h"
19 #include "base/debug/crash_logging.h"
20 #include "base/files/file_enumerator.h"
21 #include "base/files/file_path.h"
22 #include "base/files/file_util.h"
23 #include "base/files/memory_mapped_file.h"
24 #include "base/memory/scoped_ptr.h"
25 #include "base/memory/shared_memory.h"
26 #include "base/metrics/field_trial.h"
27 #include "base/metrics/histogram.h"
28 #include "base/path_service.h"
29 #include "base/process/process_handle.h"
30 #include "base/stl_util.h"
31 #include "base/strings/string_number_conversions.h"
32 #include "base/strings/string_util.h"
33 #include "base/strings/utf_string_conversions.h"
34 #include "base/synchronization/lock.h"
35 #include "base/time/time.h"
36 #include "base/win/registry.h"
37 #include "base/win/scoped_comptr.h"
38 #include "content/public/common/content_switches.h"
42 // Font Cache implementation short story:
43 // Due to our sandboxing restrictions, we cannot connect to Windows font cache
44 // service from Renderer and need to use DirectWrite isolated font loading
46 // DirectWrite needs to be initialized before any of the API could be used.
47 // During initialization DirectWrite loads all font files and populates
48 // internal cache, we refer this phase as enumeration and we are trying
49 // to optimize this phase in our cache approach. Using cache during
50 // initialization will help improve on startup latency in each renderer
52 // During enumeration DirectWrite reads various fragments from .ttf/.ttc
53 // font files. Our assumption is that these fragments are being read to
54 // cache information such as font families, supported sizes etc.
55 // For reading fragments DirectWrite calls ReadFragment of our FontFileStream
56 // implementation with parameters start_offset and length. We cache these
57 // parameters along with associated data chunk.
58 // Here is small example of how segments are read
59 // start_offset: 0, length: 16
60 // start_offset: 0, length: 12
61 // start_offset: 0, length: 117
62 // For better cache management we collapse segments if they overlap or are
65 namespace mswr
= Microsoft::WRL
;
67 const char kFontKeyName
[] = "font_key_name";
69 // We use this value to determine whether to cache file fragments
70 // or not. In our trials we observed that for some font files
71 // direct write ends up reading almost entire file during enumeration
72 // phase. If we don't use this percentile formula we will end up
73 // increasing significant cache size by caching entire file contents
74 // for some of the font files.
75 const double kMaxPercentileOfFontFileSizeToCache
= 0.6;
77 // With current implementation we map entire shared section into memory during
78 // renderer startup. This causes increase in working set of Chrome. As first
79 // step we want to see if caching is really improving any performance for our
80 // users, so we are putting arbitrary limit on cache file size. There are
81 // multiple ways we can tune our working size, like mapping only required part
82 // of section at any given time.
83 const double kArbitraryCacheFileSizeLimit
= (30 * 1024 * 1024);
85 // We have chosen current font file length arbitrarily. In our logic
86 // if we don't find file we are looking for in cache we end up loading
87 // that file directly from system fonts folder.
88 const unsigned int kMaxFontFileNameLength
= 34;
90 const DWORD kCacheFileVersion
= 103;
91 const DWORD kFileSignature
= 0x4D4F5243; // CROM
92 const DWORD kMagicCompletionSignature
= 0x454E4F44; // DONE
94 const DWORD kUndefinedDWORDS
= 36;
96 // Make sure that all structure sizes align with 8 byte boundary otherwise
97 // dr. memory test may complain.
99 // Cache file header, includes signature, completion bits and version.
100 struct CacheFileHeader
{
102 file_signature
= kFileSignature
;
103 magic_completion_signature
= 0;
104 version
= kCacheFileVersion
;
105 ::ZeroMemory(undefined
, sizeof(undefined
));
108 DWORD file_signature
;
109 DWORD magic_completion_signature
;
111 BYTE undefined
[kUndefinedDWORDS
];
114 // Entry for a particular font file within this cache.
115 struct CacheFileEntry
{
119 ::ZeroMemory(file_name
, sizeof(file_name
));
124 wchar_t file_name
[kMaxFontFileNameLength
];
127 // Offsets or data chunks that are cached for particular font file.
128 struct CacheFileOffsetEntry
{
129 CacheFileOffsetEntry() {
136 /* BYTE blob_[]; // Place holder for the blob that follows. */
140 bool ValidateFontCacheHeader(CacheFileHeader
* header
) {
141 return (header
->file_signature
== kFileSignature
&&
142 header
->magic_completion_signature
== kMagicCompletionSignature
&&
143 header
->version
== kCacheFileVersion
);
146 class FontCacheWriter
;
148 // This class implements main interface required for loading custom font
149 // collection as specified by DirectWrite. We also use this class for storing
150 // some state information as this is one of the centralized entity.
151 class FontCollectionLoader
152 : public mswr::RuntimeClass
<mswr::RuntimeClassFlags
<mswr::ClassicCom
>,
153 IDWriteFontCollectionLoader
> {
155 FontCollectionLoader()
156 : in_collection_building_mode_(false),
157 create_static_cache_(false) {}
159 ~FontCollectionLoader() override
;
161 HRESULT
RuntimeClassInitialize() {
165 // IDWriteFontCollectionLoader methods.
166 HRESULT STDMETHODCALLTYPE
167 CreateEnumeratorFromKey(IDWriteFactory
* factory
,
170 IDWriteFontFileEnumerator
** file_enumerator
) override
;
172 // Does all the initialization for required loading fonts from registry.
173 static HRESULT
Initialize(IDWriteFactory
* factory
);
175 // Returns font cache map size.
176 UINT32
GetFontMapSize();
178 // Returns font name string when given font index.
179 base::string16
GetFontNameFromKey(UINT32 idx
);
181 // Loads internal structure with fonts from registry.
182 bool LoadFontListFromRegistry();
184 // Loads restricted web safe fonts as fallback method to registry fonts.
185 bool LoadRestrictedFontList();
187 // Puts class in collection building mode. In collection building mode
188 // we use static cache if it is available as a look aside buffer.
189 void EnableCollectionBuildingMode(bool enable
);
191 // Returns current state of collection building.
192 bool InCollectionBuildingMode();
194 // Loads static cache file.
195 bool LoadCacheFile();
197 // Unloads cache file and related data.
198 void UnloadCacheFile();
200 // Puts class in static cache creating mode. In this mode we record all
201 // direct write requests and store chunks of font data.
202 void EnterStaticCacheMode(const WCHAR
* file_name
);
204 // Gets out of static cache building mode.
205 void LeaveStaticCacheMode();
207 // Returns if class is currently in static cache building mode.
208 bool IsBuildStaticCacheMode();
210 // Validates cache file for consistency.
211 bool ValidateCacheFile(base::File
* file
);
214 // Structure to represent each chunk within font file that we load in memory.
215 struct CacheTableOffsetEntry
{
218 BYTE
* inside_file_ptr
;
221 typedef std::vector
<CacheTableOffsetEntry
> OffsetVector
;
223 // Structure representing each font entry with cache.
224 struct CacheTableEntry
{
226 OffsetVector offset_entries
;
230 // Returns whether file we have particular font entry within cache or not.
231 bool IsFileCached(UINT32 font_key
);
232 // Returns cache fragment corresponding to specific font key.
233 void* GetCachedFragment(UINT32 font_key
, UINT64 start_offset
, UINT64 length
);
234 // Returns actual font file size at the time of caching.
235 UINT64
GetCachedFileSize(UINT32 font_key
);
237 // Returns instance of font cache writer. This class manages actual font
239 FontCacheWriter
* GetFontCacheWriter();
242 // Functions validates and loads cache into internal map.
243 bool ValidateAndLoadCacheMap();
245 mswr::ComPtr
<IDWriteFontFileLoader
> file_loader_
;
247 std::vector
<base::string16
> reg_fonts_
;
248 bool in_collection_building_mode_
;
249 bool create_static_cache_
;
250 scoped_ptr
<base::SharedMemory
> cache_
;
251 scoped_ptr
<FontCacheWriter
> cache_writer_
;
253 typedef std::map
<base::string16
, CacheTableEntry
*> CacheMap
;
256 DISALLOW_COPY_AND_ASSIGN(FontCollectionLoader
);
259 mswr::ComPtr
<FontCollectionLoader
> g_font_loader
;
260 base::win::ScopedHandle g_shared_font_cache
;
262 // Class responsible for handling font cache file format details as well as
263 // tracking various cache region requests by direct write.
264 class FontCacheWriter
{
266 FontCacheWriter() : count_font_entries_ignored_(0), cookie_counter_(0) {}
269 if (static_cache_
.get()) {
270 static_cache_
->Close();
275 // Holds data related to individual region as requested by direct write.
280 /* BYTE blob_[]; // Place holder for the blob that follows. */
283 // Function to create static font cache file.
284 bool Create(const wchar_t* file_name
) {
285 static_cache_
.reset(new base::File(base::FilePath(file_name
),
286 base::File::FLAG_OPEN_ALWAYS
| base::File::FLAG_WRITE
|
287 base::File::FLAG_EXCLUSIVE_WRITE
));
288 if (!static_cache_
->IsValid()) {
289 static_cache_
.reset();
292 CacheFileHeader header
;
294 // At offset 0 write cache version
295 static_cache_
->Write(0,
296 reinterpret_cast<const char*>(&header
),
299 static_cache_
->Flush();
303 // Closes static font cache file. Also writes completion signature to mark
304 // it as completely written.
306 if (static_cache_
.get()) {
307 CacheFileHeader header
;
308 header
.magic_completion_signature
= kMagicCompletionSignature
;
309 // At offset 0 write cache version
310 int bytes_written
= static_cache_
->Write(0,
311 reinterpret_cast<const char*>(&header
),
313 DCHECK_NE(bytes_written
, -1);
315 UMA_HISTOGRAM_MEMORY_KB("DirectWrite.Fonts.BuildCache.File.Size",
316 static_cache_
->GetLength() / 1024);
318 UMA_HISTOGRAM_COUNTS("DirectWrite.Fonts.BuildCache.Ignored",
319 count_font_entries_ignored_
);
321 static_cache_
->Close();
322 static_cache_
.reset(NULL
);
327 typedef std::vector
<CacheRegion
> RegionVector
;
329 // Structure to track various regions requested by direct write for particular
331 struct FontEntryInternal
{
332 FontEntryInternal(const wchar_t* name
, UINT64 size
)
337 base::string16 file_name
;
339 RegionVector regions
;
343 // Starts up new font entry to be tracked, returns cookie to identify this
345 UINT
NewFontEntry(const wchar_t* file_name
, UINT64 file_size
) {
346 base::AutoLock
lock(lock_
);
347 UINT old_counter
= cookie_counter_
;
348 FontEntryInternal
* font_entry
= new FontEntryInternal(file_name
, file_size
);
349 cookie_map_
[cookie_counter_
].reset(font_entry
);
354 // AddRegion function lets caller add various regions to be cached for
355 // particular font file. Once enumerating that particular font file is done
356 // (based on uniquely identifying cookie) changes could be committed using
358 bool AddRegion(UINT64 cookie
, UINT64 start
, UINT64 length
, const BYTE
* ptr
) {
359 base::AutoLock
lock(lock_
);
360 if (cookie_map_
.find(cookie
) == cookie_map_
.end())
362 RegionVector
& regions
= cookie_map_
[cookie
].get()->regions
;
364 region
.start_offset
= start
;
365 region
.length
= length
;
367 regions
.push_back(region
);
371 // Function which commits after merging all collected regions into cache file.
372 bool CommitFontEntry(UINT cookie
) {
373 base::AutoLock
lock(lock_
);
374 if (cookie_map_
.find(cookie
) == cookie_map_
.end())
377 // We will skip writing entries beyond allowed limit. Following condition
378 // doesn't enforce hard file size. We need to write complete font entry.
379 int64 length
= static_cache_
->GetLength();
380 if (length
== -1 || length
>= kArbitraryCacheFileSizeLimit
) {
381 count_font_entries_ignored_
++;
385 FontEntryInternal
* font_entry
= cookie_map_
[cookie
].get();
386 RegionVector
& regions
= font_entry
->regions
;
387 std::sort(regions
.begin(), regions
.end(), SortCacheRegions
);
389 // At this point, we have collected all regions to be cached. These regions
390 // are tuples of start, length, data for particular data segment.
391 // These tuples can overlap.
392 // e.g. (0, 12, data), (0, 117, data), (21, 314, data), (335, 15, data)
393 // In this case as you can see first three segments overlap and
394 // 4th is adjacent. If we cache them individually then we will end up
395 // caching duplicate data, so we merge these segments together to find
396 // superset for the cache. In above example our algorithm should
397 // produce (cache) single segment starting at offset 0 with length 350.
398 RegionVector merged_regions
;
399 RegionVector::iterator iter
;
401 for (iter
= regions
.begin(); iter
!= regions
.end(); iter
++) {
402 if (iter
== regions
.begin()) {
403 merged_regions
.push_back(*iter
);
406 CacheRegion
& base_region
= merged_regions
[idx
];
407 if (IsOverlap(&base_region
, &(*iter
))) {
408 UINT64 end1
= base_region
.start_offset
+ base_region
.length
;
409 UINT64 end2
= iter
->start_offset
+ iter
->length
;
410 if (base_region
.start_offset
> iter
->start_offset
) {
411 base_region
.start_offset
= iter
->start_offset
;
412 base_region
.ptr
= iter
->ptr
;
414 base_region
.length
= std::max(end1
, end2
) - base_region
.start_offset
;
416 merged_regions
.push_back(*iter
);
421 UINT64 total_merged_cache_in_bytes
= 0;
422 for (iter
= merged_regions
.begin(); iter
!= merged_regions
.end(); iter
++) {
423 total_merged_cache_in_bytes
+= iter
->length
;
426 // We want to adjust following parameter based on experiments. But general
427 // logic here is that if we are going to end up caching most of the contents
428 // for a file (e.g. simsunb.ttf > 90%) then we should avoid caching that
430 double percentile
= static_cast<double>(total_merged_cache_in_bytes
) /
431 font_entry
->file_size
;
432 if (percentile
> kMaxPercentileOfFontFileSizeToCache
) {
433 count_font_entries_ignored_
++;
437 CacheFileEntry entry
;
438 wcsncpy_s(entry
.file_name
, kMaxFontFileNameLength
,
439 font_entry
->file_name
.c_str(), _TRUNCATE
);
440 entry
.file_size
= font_entry
->file_size
;
441 entry
.entry_count
= merged_regions
.size();
442 static_cache_
->WriteAtCurrentPos(
443 reinterpret_cast<const char*>(&entry
),
445 for (iter
= merged_regions
.begin(); iter
!= merged_regions
.end(); iter
++) {
446 CacheFileOffsetEntry offset_entry
;
447 offset_entry
.start_offset
= iter
->start_offset
;
448 offset_entry
.length
= iter
->length
;
449 static_cache_
->WriteAtCurrentPos(
450 reinterpret_cast<const char*>(&offset_entry
),
451 sizeof(offset_entry
));
452 static_cache_
->WriteAtCurrentPos(
453 reinterpret_cast<const char*>(iter
->ptr
),
460 // This is the count of font entries that we reject based on size to be
462 unsigned int count_font_entries_ignored_
;
463 scoped_ptr
<base::File
> static_cache_
;
464 std::map
<UINT
, scoped_ptr
<FontEntryInternal
>> cookie_map_
;
465 UINT cookie_counter_
;
467 // Lock is required to protect internal data structures and access to file,
468 // According to MSDN documentation on ReadFileFragment and based on our
469 // experiments so far, there is possibility of ReadFileFragment getting called
470 // from multiple threads.
473 // Function checks if two regions overlap or are adjacent.
474 bool IsOverlap(CacheRegion
* region1
, CacheRegion
* region2
) {
476 !((region1
->start_offset
+ region1
->length
) < region2
->start_offset
||
477 region1
->start_offset
> (region2
->start_offset
+ region2
->length
));
480 // Function to sort cached regions.
481 static bool SortCacheRegions(const CacheRegion
& region1
,
482 const CacheRegion
& region2
) {
484 region1
.start_offset
== region2
.start_offset
?
485 region1
.length
< region2
.length
:
486 region1
.start_offset
< region2
.start_offset
;
489 DISALLOW_COPY_AND_ASSIGN(FontCacheWriter
);
492 // Class implements IDWriteFontFileStream interface as required by direct write.
494 : public mswr::RuntimeClass
<mswr::RuntimeClassFlags
<mswr::ClassicCom
>,
495 IDWriteFontFileStream
> {
497 // IDWriteFontFileStream methods.
498 HRESULT STDMETHODCALLTYPE
ReadFileFragment(
499 void const** fragment_start
,
501 UINT64 fragment_size
,
502 void** context
) override
{
504 *fragment_start
= g_font_loader
->GetCachedFragment(font_key_
,
507 if (*fragment_start
== NULL
) {
511 return *fragment_start
!= NULL
? S_OK
: E_FAIL
;
513 if (!memory_
.get() || !memory_
->IsValid() ||
514 file_offset
>= memory_
->length() ||
515 (file_offset
+ fragment_size
) > memory_
->length())
518 *fragment_start
= static_cast<BYTE
const*>(memory_
->data()) +
519 static_cast<size_t>(file_offset
);
521 if (g_font_loader
->IsBuildStaticCacheMode()) {
522 FontCacheWriter
* cache_writer
= g_font_loader
->GetFontCacheWriter();
523 cache_writer
->AddRegion(writer_cookie_
,
526 static_cast<const BYTE
*>(*fragment_start
));
531 void STDMETHODCALLTYPE
ReleaseFileFragment(void* context
) override
{}
533 HRESULT STDMETHODCALLTYPE
GetFileSize(UINT64
* file_size
) override
{
535 *file_size
= g_font_loader
->GetCachedFileSize(font_key_
);
539 if (!memory_
.get() || !memory_
->IsValid())
542 *file_size
= memory_
->length();
546 HRESULT STDMETHODCALLTYPE
GetLastWriteTime(UINT64
* last_write_time
) override
{
548 *last_write_time
= 0;
552 if (!memory_
.get() || !memory_
->IsValid())
555 // According to MSDN article http://goo.gl/rrSYzi the "last modified time"
556 // is used by DirectWrite font selection algorithms to determine whether
557 // one font resource is more up to date than another one.
558 // So by returning 0 we are assuming that it will treat all fonts to be
559 // equally up to date.
560 // TODO(shrikant): We should further investigate this.
561 *last_write_time
= 0;
565 FontFileStream() : font_key_(0), cached_data_(false) {}
567 HRESULT
RuntimeClassInitialize(UINT32 font_key
) {
568 if (g_font_loader
->InCollectionBuildingMode() &&
569 g_font_loader
->IsFileCached(font_key
)) {
571 font_key_
= font_key
;
576 PathService::Get(base::DIR_WINDOWS_FONTS
, &path
);
577 base::string16
font_key_name(g_font_loader
->GetFontNameFromKey(font_key
));
578 path
= path
.Append(font_key_name
.c_str());
579 memory_
.reset(new base::MemoryMappedFile());
581 // Put some debug information on stack.
582 WCHAR font_name
[MAX_PATH
];
583 path
.value().copy(font_name
, arraysize(font_name
));
584 base::debug::Alias(font_name
);
586 if (!memory_
->Initialize(path
)) {
591 font_key_
= font_key
;
593 base::debug::SetCrashKeyValue(kFontKeyName
,
594 base::WideToUTF8(font_key_name
));
596 if (g_font_loader
->IsBuildStaticCacheMode()) {
597 FontCacheWriter
* cache_writer
= g_font_loader
->GetFontCacheWriter();
598 writer_cookie_
= cache_writer
->NewFontEntry(font_key_name
.c_str(),
604 ~FontFileStream() override
{
605 if (g_font_loader
->IsBuildStaticCacheMode()) {
606 FontCacheWriter
* cache_writer
= g_font_loader
->GetFontCacheWriter();
607 cache_writer
->CommitFontEntry(writer_cookie_
);
613 scoped_ptr
<base::MemoryMappedFile
> memory_
;
617 DISALLOW_COPY_AND_ASSIGN(FontFileStream
);
620 // Implements IDWriteFontFileLoader as required by FontFileLoader.
622 : public mswr::RuntimeClass
<mswr::RuntimeClassFlags
<mswr::ClassicCom
>,
623 IDWriteFontFileLoader
> {
625 // IDWriteFontFileLoader methods.
626 HRESULT STDMETHODCALLTYPE
627 CreateStreamFromKey(void const* ref_key
,
629 IDWriteFontFileStream
** stream
) override
{
630 if (ref_key_size
!= sizeof(UINT32
))
633 UINT32 font_key
= *static_cast<const UINT32
*>(ref_key
);
634 mswr::ComPtr
<FontFileStream
> font_stream
;
635 HRESULT hr
= mswr::MakeAndInitialize
<FontFileStream
>(&font_stream
,
638 *stream
= font_stream
.Detach();
645 ~FontFileLoader() override
{}
648 DISALLOW_COPY_AND_ASSIGN(FontFileLoader
);
651 // Implements IDWriteFontFileEnumerator as required by direct write.
652 class FontFileEnumerator
653 : public mswr::RuntimeClass
<mswr::RuntimeClassFlags
<mswr::ClassicCom
>,
654 IDWriteFontFileEnumerator
> {
656 // IDWriteFontFileEnumerator methods.
657 HRESULT STDMETHODCALLTYPE
MoveNext(BOOL
* has_current_file
) override
{
658 *has_current_file
= FALSE
;
661 current_file_
.ReleaseAndGetAddressOf();
663 if (font_idx_
< g_font_loader
->GetFontMapSize()) {
665 factory_
->CreateCustomFontFileReference(&font_idx_
,
668 current_file_
.GetAddressOf());
669 DCHECK(SUCCEEDED(hr
));
670 *has_current_file
= TRUE
;
676 HRESULT STDMETHODCALLTYPE
677 GetCurrentFontFile(IDWriteFontFile
** font_file
) override
{
678 if (!current_file_
) {
683 *font_file
= current_file_
.Detach();
687 FontFileEnumerator(const void* keys
,
689 IDWriteFactory
* factory
,
690 IDWriteFontFileLoader
* file_loader
)
691 : factory_(factory
), file_loader_(file_loader
), font_idx_(0) {}
693 ~FontFileEnumerator() override
{}
695 mswr::ComPtr
<IDWriteFactory
> factory_
;
696 mswr::ComPtr
<IDWriteFontFile
> current_file_
;
697 mswr::ComPtr
<IDWriteFontFileLoader
> file_loader_
;
701 DISALLOW_COPY_AND_ASSIGN(FontFileEnumerator
);
704 // IDWriteFontCollectionLoader methods.
705 HRESULT STDMETHODCALLTYPE
FontCollectionLoader::CreateEnumeratorFromKey(
706 IDWriteFactory
* factory
,
709 IDWriteFontFileEnumerator
** file_enumerator
) {
710 *file_enumerator
= mswr::Make
<FontFileEnumerator
>(
711 key
, key_size
, factory
, file_loader_
.Get()).Detach();
716 HRESULT
FontCollectionLoader::Initialize(IDWriteFactory
* factory
) {
717 DCHECK(g_font_loader
== NULL
);
720 result
= mswr::MakeAndInitialize
<FontCollectionLoader
>(&g_font_loader
);
721 if (FAILED(result
) || !g_font_loader
) {
726 CHECK(g_font_loader
->LoadFontListFromRegistry());
728 g_font_loader
->file_loader_
= mswr::Make
<FontFileLoader
>().Detach();
730 factory
->RegisterFontFileLoader(g_font_loader
->file_loader_
.Get());
731 factory
->RegisterFontCollectionLoader(g_font_loader
.Get());
736 FontCollectionLoader::~FontCollectionLoader() {
737 STLDeleteContainerPairSecondPointers(cache_map_
.begin(), cache_map_
.end());
740 UINT32
FontCollectionLoader::GetFontMapSize() {
741 return reg_fonts_
.size();
744 base::string16
FontCollectionLoader::GetFontNameFromKey(UINT32 idx
) {
745 DCHECK(idx
< reg_fonts_
.size());
746 return reg_fonts_
[idx
];
749 const base::FilePath::CharType
* kFontExtensionsToIgnore
[] {
750 FILE_PATH_LITERAL(".FON"), // Bitmap or vector
751 FILE_PATH_LITERAL(".PFM"), // Adobe Type 1
752 FILE_PATH_LITERAL(".PFB"), // Adobe Type 1
755 const wchar_t* kFontsToIgnore
[] = {
756 // "Gill Sans Ultra Bold" turns into an Ultra Bold weight "Gill Sans" in
757 // DirectWrite, but most users don't have any other weights. The regular
758 // weight font is named "Gill Sans MT", but that ends up in a different
759 // family with that name. On Mac, there's a "Gill Sans" with various weights,
760 // so CSS authors use { 'font-family': 'Gill Sans', 'Gill Sans MT', ... } and
761 // because of the DirectWrite family futzing, they end up with an Ultra Bold
762 // font, when they just wanted "Gill Sans". Mozilla implemented a more
763 // complicated hack where they effectively rename the Ultra Bold font to
764 // "Gill Sans MT Ultra Bold", but because the Ultra Bold font is so ugly
765 // anyway, we simply ignore it. See
766 // http://www.microsoft.com/typography/fonts/font.aspx?FMID=978 for a picture
767 // of the font, and the file name. We also ignore "Gill Sans Ultra Bold
773 bool FontCollectionLoader::LoadFontListFromRegistry() {
774 const wchar_t kFontsRegistry
[] =
775 L
"Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts";
776 CHECK(reg_fonts_
.empty());
777 base::win::RegKey regkey
;
778 if (regkey
.Open(HKEY_LOCAL_MACHINE
, kFontsRegistry
, KEY_READ
) !=
783 base::FilePath system_font_path
;
784 PathService::Get(base::DIR_WINDOWS_FONTS
, &system_font_path
);
787 base::string16 value
;
788 for (DWORD idx
= 0; idx
< regkey
.GetValueCount(); idx
++) {
789 if (regkey
.GetValueNameAt(idx
, &name
) == ERROR_SUCCESS
&&
790 regkey
.ReadValue(name
.c_str(), &value
) == ERROR_SUCCESS
) {
791 base::FilePath
path(value
.c_str());
792 // We need to check if path in registry is absolute, if it is then
793 // we check if it is same as DIR_WINDOWS_FONTS otherwise we ignore.
794 bool absolute
= path
.IsAbsolute();
796 !base::FilePath::CompareEqualIgnoreCase(system_font_path
.value(),
797 path
.DirName().value())) {
801 // Ignore if path ends with a separator.
802 if (path
.EndsWithSeparator())
806 value
= path
.BaseName().value();
808 bool should_ignore
= false;
809 for (const auto& ignore
: kFontsToIgnore
) {
810 if (base::FilePath::CompareEqualIgnoreCase(value
, ignore
)) {
811 should_ignore
= true;
815 // DirectWrite doesn't support bitmap/vector fonts and Adobe type 1
816 // fonts, we will ignore those font extensions.
817 // MSDN article: http://goo.gl/TfCOA
818 if (!should_ignore
) {
819 for (const auto& ignore
: kFontExtensionsToIgnore
) {
820 if (path
.MatchesExtension(ignore
)) {
821 should_ignore
= true;
828 reg_fonts_
.push_back(value
.c_str());
831 UMA_HISTOGRAM_COUNTS("DirectWrite.Fonts.Loaded", reg_fonts_
.size());
832 UMA_HISTOGRAM_COUNTS("DirectWrite.Fonts.Ignored",
833 regkey
.GetValueCount() - reg_fonts_
.size());
837 // This list is mainly based on prefs/prefs_tab_helper.cc kFontDefaults.
838 const wchar_t* kRestrictedFontSet
[] = {
839 // These are the "Web Safe" fonts.
840 L
"times.ttf", // IDS_STANDARD_FONT_FAMILY
841 L
"timesbd.ttf", // IDS_STANDARD_FONT_FAMILY
842 L
"timesbi.ttf", // IDS_STANDARD_FONT_FAMILY
843 L
"timesi.ttf", // IDS_STANDARD_FONT_FAMILY
844 L
"cour.ttf", // IDS_FIXED_FONT_FAMILY
845 L
"courbd.ttf", // IDS_FIXED_FONT_FAMILY
846 L
"courbi.ttf", // IDS_FIXED_FONT_FAMILY
847 L
"couri.ttf", // IDS_FIXED_FONT_FAMILY
848 L
"consola.ttf", // IDS_FIXED_FONT_FAMILY_ALT_WIN
849 L
"consolab.ttf", // IDS_FIXED_FONT_FAMILY_ALT_WIN
850 L
"consolai.ttf", // IDS_FIXED_FONT_FAMILY_ALT_WIN
851 L
"consolaz.ttf", // IDS_FIXED_FONT_FAMILY_ALT_WIN
852 L
"arial.ttf", // IDS_SANS_SERIF_FONT_FAMILY
853 L
"arialbd.ttf", // IDS_SANS_SERIF_FONT_FAMILY
854 L
"arialbi.ttf", // IDS_SANS_SERIF_FONT_FAMILY
855 L
"ariali.ttf", // IDS_SANS_SERIF_FONT_FAMILY
856 L
"comic.ttf", // IDS_CURSIVE_FONT_FAMILY
857 L
"comicbd.ttf", // IDS_CURSIVE_FONT_FAMILY
858 L
"comici.ttf", // IDS_CURSIVE_FONT_FAMILY
859 L
"comicz.ttf", // IDS_CURSIVE_FONT_FAMILY
860 L
"impact.ttf", // IDS_FANTASY_FONT_FAMILY
873 L
"segoeui.ttf", // IDS_PICTOGRAPH_FONT_FAMILY
874 L
"segoeuib.ttf", // IDS_PICTOGRAPH_FONT_FAMILY
875 L
"segoeuii.ttf", // IDS_PICTOGRAPH_FONT_FAMILY
876 L
"msgothic.ttc", // IDS_STANDARD_FONT_FAMILY_JAPANESE
877 L
"msmincho.ttc", // IDS_SERIF_FONT_FAMILY_JAPANESE
878 L
"gulim.ttc", // IDS_FIXED_FONT_FAMILY_KOREAN
879 L
"batang.ttc", // IDS_SERIF_FONT_FAMILY_KOREAN
880 L
"simsun.ttc", // IDS_STANDARD_FONT_FAMILY_SIMPLIFIED_HAN
881 L
"mingliu.ttc", // IDS_SERIF_FONT_FAMILY_TRADITIONAL_HAN
883 // These are from the Blink fallback list.
884 L
"david.ttf", // USCRIPT_HEBREW
885 L
"davidbd.ttf", // USCRIPT_HEBREW
886 L
"euphemia.ttf", // USCRIPT_CANADIAN_ABORIGINAL
887 L
"gautami.ttf", // USCRIPT_TELUGU
888 L
"gautamib.ttf", // USCRIPT_TELUGU
889 L
"latha.ttf", // USCRIPT_TAMIL
890 L
"lathab.ttf", // USCRIPT_TAMIL
891 L
"mangal.ttf", // USCRIPT_DEVANAGARI
892 L
"mangalb.ttf", // USCRIPT_DEVANAGARI
893 L
"monbaiti.ttf", // USCRIPT_MONGOLIAN
894 L
"mvboli.ttf", // USCRIPT_THAANA
895 L
"plantc.ttf", // USCRIPT_CHEROKEE
896 L
"raavi.ttf", // USCRIPT_GURMUKHI
897 L
"raavib.ttf", // USCRIPT_GURMUKHI
898 L
"shruti.ttf", // USCRIPT_GUJARATI
899 L
"shrutib.ttf", // USCRIPT_GUJARATI
900 L
"sylfaen.ttf", // USCRIPT_GEORGIAN and USCRIPT_ARMENIAN
901 L
"tahoma.ttf", // USCRIPT_ARABIC,
902 L
"tahomabd.ttf", // USCRIPT_ARABIC,
903 L
"tunga.ttf", // USCRIPT_KANNADA
904 L
"tungab.ttf", // USCRIPT_KANNADA
905 L
"vrinda.ttf", // USCRIPT_BENGALI
906 L
"vrindab.ttf", // USCRIPT_BENGALI
909 bool FontCollectionLoader::LoadRestrictedFontList() {
911 reg_fonts_
.assign(kRestrictedFontSet
,
912 kRestrictedFontSet
+ _countof(kRestrictedFontSet
));
916 void FontCollectionLoader::EnableCollectionBuildingMode(bool enable
) {
917 in_collection_building_mode_
= enable
;
920 bool FontCollectionLoader::InCollectionBuildingMode() {
921 return in_collection_building_mode_
;
924 bool FontCollectionLoader::IsFileCached(UINT32 font_key
) {
925 if (!cache_
.get() || cache_
->memory() == NULL
) {
928 CacheMap::iterator iter
= cache_map_
.find(
929 GetFontNameFromKey(font_key
).c_str());
930 return iter
!= cache_map_
.end();;
933 bool FontCollectionLoader::LoadCacheFile() {
934 std::string font_cache_handle_string
=
935 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
936 switches::kFontCacheSharedHandle
);
937 if (font_cache_handle_string
.empty())
940 base::SharedMemoryHandle font_cache_handle
= NULL
;
941 base::StringToUint(font_cache_handle_string
,
942 reinterpret_cast<unsigned int*>(&font_cache_handle
));
943 DCHECK(font_cache_handle
);
945 base::SharedMemory
* shared_mem
= new base::SharedMemory(
946 font_cache_handle
, true);
947 // Map the cache file into memory.
950 cache_
.reset(shared_mem
);
952 if (base::StartsWith(base::FieldTrialList::FindFullName("LightSpeed"),
953 "PrefetchDWriteFontCache",
954 base::CompareCase::SENSITIVE
)) {
955 // Prefetch the cache, to avoid unordered IO when it is used.
956 // PrefetchVirtualMemory() is loaded dynamically because it is only
957 // available from Win8.
958 decltype(PrefetchVirtualMemory
)* prefetch_virtual_memory
=
959 reinterpret_cast<decltype(PrefetchVirtualMemory
)*>(::GetProcAddress(
960 ::GetModuleHandle(L
"kernel32.dll"), "PrefetchVirtualMemory"));
961 if (prefetch_virtual_memory
!= NULL
) {
962 WIN32_MEMORY_RANGE_ENTRY memory_range
;
963 memory_range
.VirtualAddress
= shared_mem
->memory();
964 memory_range
.NumberOfBytes
= shared_mem
->mapped_size();
965 prefetch_virtual_memory(::GetCurrentProcess(), 1, &memory_range
, 0);
969 if (!ValidateAndLoadCacheMap()) {
977 void FontCollectionLoader::UnloadCacheFile() {
979 STLDeleteContainerPairSecondPointers(cache_map_
.begin(), cache_map_
.end());
983 void FontCollectionLoader::EnterStaticCacheMode(const WCHAR
* file_name
) {
984 cache_writer_
.reset(new FontCacheWriter());
985 if (cache_writer_
->Create(file_name
))
986 create_static_cache_
= true;
989 void FontCollectionLoader::LeaveStaticCacheMode() {
990 cache_writer_
->Close();
991 cache_writer_
.reset(NULL
);
992 create_static_cache_
= false;
995 bool FontCollectionLoader::IsBuildStaticCacheMode() {
996 return create_static_cache_
;
999 bool FontCollectionLoader::ValidateAndLoadCacheMap() {
1000 BYTE
* mem_file_start
= static_cast<BYTE
*>(cache_
->memory());
1001 BYTE
* mem_file_end
= mem_file_start
+ cache_
->mapped_size();
1003 BYTE
* current_ptr
= mem_file_start
;
1004 CacheFileHeader
* file_header
=
1005 reinterpret_cast<CacheFileHeader
*>(current_ptr
);
1006 if (!ValidateFontCacheHeader(file_header
))
1009 current_ptr
= current_ptr
+ sizeof(CacheFileHeader
);
1010 if (current_ptr
>= mem_file_end
)
1013 while ((current_ptr
+ sizeof(CacheFileEntry
)) < mem_file_end
) {
1014 CacheFileEntry
* entry
= reinterpret_cast<CacheFileEntry
*>(current_ptr
);
1015 current_ptr
+= sizeof(CacheFileEntry
);
1016 WCHAR file_name
[kMaxFontFileNameLength
];
1017 wcsncpy_s(file_name
,
1018 kMaxFontFileNameLength
,
1021 CacheTableEntry
* table_entry
= NULL
;
1022 CacheMap::iterator iter
= cache_map_
.find(file_name
);
1023 if (iter
== cache_map_
.end()) {
1024 table_entry
= new CacheTableEntry();
1025 cache_map_
[file_name
] = table_entry
;
1027 table_entry
= iter
->second
;
1029 table_entry
->file_size
= entry
->file_size
;
1031 (current_ptr
+ sizeof(CacheFileOffsetEntry
)) < mem_file_end
&&
1032 idx
< entry
->entry_count
;
1034 CacheFileOffsetEntry
* offset_entry
=
1035 reinterpret_cast<CacheFileOffsetEntry
*>(current_ptr
);
1036 CacheTableOffsetEntry table_offset_entry
;
1037 table_offset_entry
.start_offset
= offset_entry
->start_offset
;
1038 table_offset_entry
.length
= offset_entry
->length
;
1039 table_offset_entry
.inside_file_ptr
=
1040 current_ptr
+ sizeof(CacheFileOffsetEntry
);
1041 table_entry
->offset_entries
.push_back(table_offset_entry
);
1042 current_ptr
+= sizeof(CacheFileOffsetEntry
);
1043 current_ptr
+= offset_entry
->length
;
1050 void* FontCollectionLoader::GetCachedFragment(UINT32 font_key
,
1051 UINT64 start_offset
,
1053 UINT64 just_past_end
= start_offset
+ length
;
1054 CacheMap::iterator iter
= cache_map_
.find(
1055 GetFontNameFromKey(font_key
).c_str());
1056 if (iter
!= cache_map_
.end()) {
1057 CacheTableEntry
* entry
= iter
->second
;
1058 OffsetVector::iterator offset_iter
= entry
->offset_entries
.begin();
1059 while (offset_iter
!= entry
->offset_entries
.end()) {
1060 UINT64 available_just_past_end
=
1061 offset_iter
->start_offset
+ offset_iter
->length
;
1062 if (offset_iter
->start_offset
<= start_offset
&&
1063 just_past_end
<= available_just_past_end
) {
1064 return offset_iter
->inside_file_ptr
+
1065 (start_offset
- offset_iter
->start_offset
);
1073 UINT64
FontCollectionLoader::GetCachedFileSize(UINT32 font_key
) {
1074 CacheMap::iterator iter
= cache_map_
.find(
1075 GetFontNameFromKey(font_key
).c_str());
1076 if (iter
!= cache_map_
.end()) {
1077 return iter
->second
->file_size
;
1082 FontCacheWriter
* FontCollectionLoader::GetFontCacheWriter() {
1083 return cache_writer_
.get();
1090 const char kFontCacheSharedSectionName
[] = "ChromeDWriteFontCache";
1092 mswr::ComPtr
<IDWriteFontCollection
> g_font_collection
;
1094 IDWriteFontCollection
* GetCustomFontCollection(IDWriteFactory
* factory
) {
1095 if (g_font_collection
.Get() != NULL
)
1096 return g_font_collection
.Get();
1098 base::TimeTicks start_tick
= base::TimeTicks::Now();
1100 FontCollectionLoader::Initialize(factory
);
1102 bool cache_file_loaded
= g_font_loader
->LoadCacheFile();
1104 // Arbitrary threshold to stop loading enormous number of fonts. Usual
1105 // side effect of loading large number of fonts results in renderer getting
1106 // killed as it appears to hang.
1107 const UINT32 kMaxFontThreshold
= 1750;
1108 HRESULT hr
= E_FAIL
;
1109 if (cache_file_loaded
||
1110 g_font_loader
->GetFontMapSize() < kMaxFontThreshold
) {
1111 g_font_loader
->EnableCollectionBuildingMode(true);
1112 hr
= factory
->CreateCustomFontCollection(
1113 g_font_loader
.Get(), NULL
, 0, g_font_collection
.GetAddressOf());
1114 g_font_loader
->UnloadCacheFile();
1115 g_font_loader
->EnableCollectionBuildingMode(false);
1117 bool loading_restricted
= false;
1118 if (FAILED(hr
) || !g_font_collection
.Get()) {
1119 loading_restricted
= true;
1120 // We will try here just one more time with restricted font set.
1121 g_font_loader
->LoadRestrictedFontList();
1122 hr
= factory
->CreateCustomFontCollection(
1123 g_font_loader
.Get(), NULL
, 0, g_font_collection
.GetAddressOf());
1126 base::TimeDelta time_delta
= base::TimeTicks::Now() - start_tick
;
1127 int64 delta
= time_delta
.ToInternalValue();
1128 base::debug::Alias(&delta
);
1129 UINT32 size
= g_font_loader
->GetFontMapSize();
1130 base::debug::Alias(&size
);
1131 base::debug::Alias(&loading_restricted
);
1133 CHECK(SUCCEEDED(hr
));
1134 CHECK(g_font_collection
.Get() != NULL
);
1136 if (cache_file_loaded
)
1137 UMA_HISTOGRAM_TIMES("DirectWrite.Fonts.LoadTime.Cached", time_delta
);
1139 UMA_HISTOGRAM_TIMES("DirectWrite.Fonts.LoadTime", time_delta
);
1141 base::debug::ClearCrashKey(kFontKeyName
);
1143 return g_font_collection
.Get();
1146 bool BuildFontCacheInternal(const WCHAR
* file_name
) {
1147 typedef decltype(DWriteCreateFactory
)* DWriteCreateFactoryProc
;
1148 HMODULE dwrite_dll
= LoadLibraryW(L
"dwrite.dll");
1150 DWORD load_library_get_last_error
= GetLastError();
1151 base::debug::Alias(&dwrite_dll
);
1152 base::debug::Alias(&load_library_get_last_error
);
1156 DWriteCreateFactoryProc dwrite_create_factory_proc
=
1157 reinterpret_cast<DWriteCreateFactoryProc
>(
1158 GetProcAddress(dwrite_dll
, "DWriteCreateFactory"));
1160 if (!dwrite_create_factory_proc
) {
1161 DWORD get_proc_address_get_last_error
= GetLastError();
1162 base::debug::Alias(&dwrite_create_factory_proc
);
1163 base::debug::Alias(&get_proc_address_get_last_error
);
1167 mswr::ComPtr
<IDWriteFactory
> factory
;
1170 dwrite_create_factory_proc(
1171 DWRITE_FACTORY_TYPE_ISOLATED
,
1172 __uuidof(IDWriteFactory
),
1173 reinterpret_cast<IUnknown
**>(factory
.GetAddressOf()))));
1175 base::TimeTicks start_tick
= base::TimeTicks::Now();
1177 FontCollectionLoader::Initialize(factory
.Get());
1179 g_font_loader
->EnterStaticCacheMode(file_name
);
1181 mswr::ComPtr
<IDWriteFontCollection
> font_collection
;
1183 HRESULT hr
= E_FAIL
;
1184 g_font_loader
->EnableCollectionBuildingMode(true);
1185 hr
= factory
->CreateCustomFontCollection(
1186 g_font_loader
.Get(), NULL
, 0, font_collection
.GetAddressOf());
1187 g_font_loader
->EnableCollectionBuildingMode(false);
1189 bool loading_restricted
= false;
1190 if (FAILED(hr
) || !font_collection
.Get()) {
1191 loading_restricted
= true;
1192 // We will try here just one more time with restricted font set.
1193 g_font_loader
->LoadRestrictedFontList();
1194 hr
= factory
->CreateCustomFontCollection(
1195 g_font_loader
.Get(), NULL
, 0, font_collection
.GetAddressOf());
1198 g_font_loader
->LeaveStaticCacheMode();
1200 base::TimeDelta time_delta
= base::TimeTicks::Now() - start_tick
;
1201 int64 delta
= time_delta
.ToInternalValue();
1202 base::debug::Alias(&delta
);
1203 UINT32 size
= g_font_loader
->GetFontMapSize();
1204 base::debug::Alias(&size
);
1205 base::debug::Alias(&loading_restricted
);
1207 CHECK(SUCCEEDED(hr
));
1208 CHECK(font_collection
.Get() != NULL
);
1210 base::debug::ClearCrashKey(kFontKeyName
);
1215 bool ValidateFontCacheFile(base::File
* file
) {
1216 DCHECK(file
!= NULL
);
1217 CacheFileHeader file_header
;
1218 if (file
->Read(0, reinterpret_cast<char*>(&file_header
), sizeof(file_header
))
1222 return ValidateFontCacheHeader(&file_header
);
1225 bool LoadFontCache(const base::FilePath
& path
) {
1226 scoped_ptr
<base::File
> file(new base::File(path
,
1227 base::File::FLAG_OPEN
| base::File::FLAG_READ
));
1228 if (!file
->IsValid())
1231 if (!ValidateFontCacheFile(file
.get()))
1234 base::string16
name(base::ASCIIToUTF16(content::kFontCacheSharedSectionName
));
1235 name
.append(base::UintToString16(base::GetCurrentProcId()));
1236 HANDLE mapping
= ::CreateFileMapping(
1237 file
->GetPlatformFile(),
1243 if (mapping
== INVALID_HANDLE_VALUE
)
1246 if (::GetLastError() == ERROR_ALREADY_EXISTS
) {
1247 CloseHandle(mapping
);
1248 // We crash here, as no one should have created this mapping except Chrome.
1253 DCHECK(!g_shared_font_cache
.IsValid());
1254 g_shared_font_cache
.Set(mapping
);
1259 bool BuildFontCache(const base::FilePath
& file
) {
1260 return BuildFontCacheInternal(file
.value().c_str());
1263 } // namespace content