Home | History | Annotate | Download | only in renderer
      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