Home | History | Annotate | Download | only in gfx
      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 <map>
      8 
      9 #include "base/memory/singleton.h"
     10 #include "base/strings/string_split.h"
     11 #include "base/strings/string_util.h"
     12 #include "base/strings/utf_string_conversions.h"
     13 #include "base/win/registry.h"
     14 #include "ui/gfx/font.h"
     15 
     16 namespace gfx {
     17 
     18 namespace {
     19 
     20 // Queries the registry to get a mapping from font filenames to font names.
     21 void QueryFontsFromRegistry(std::map<std::string, std::string>* map) {
     22   const wchar_t* kFonts =
     23       L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts";
     24 
     25   base::win::RegistryValueIterator it(HKEY_LOCAL_MACHINE, kFonts);
     26   for (; it.Valid(); ++it) {
     27     const std::string filename = StringToLowerASCII(WideToUTF8(it.Value()));
     28     (*map)[filename] = WideToUTF8(it.Name());
     29   }
     30 }
     31 
     32 // Fills |font_names| with a list of font families found in the font file at
     33 // |filename|. Takes in a |font_map| from font filename to font families, which
     34 // is filled-in by querying the registry, if empty.
     35 void GetFontNamesFromFilename(const std::string& filename,
     36                               std::map<std::string, std::string>* font_map,
     37                               std::vector<std::string>* font_names) {
     38   if (font_map->empty())
     39     QueryFontsFromRegistry(font_map);
     40 
     41   std::map<std::string, std::string>::const_iterator it =
     42       font_map->find(StringToLowerASCII(filename));
     43   if (it == font_map->end())
     44     return;
     45 
     46   internal::ParseFontFamilyString(it->second, font_names);
     47 }
     48 
     49 // Returns true if |text| contains only ASCII digits.
     50 bool ContainsOnlyDigits(const std::string& text) {
     51   return text.find_first_not_of("0123456789") == base::string16::npos;
     52 }
     53 
     54 // Appends a Font with the given |name| and |size| to |fonts| unless the last
     55 // entry is already a font with that name.
     56 void AppendFont(const std::string& name, int size, std::vector<Font>* fonts) {
     57   if (fonts->empty() || fonts->back().GetFontName() != name)
     58     fonts->push_back(Font(name, size));
     59 }
     60 
     61 // Queries the registry to get a list of linked fonts for |font|.
     62 void QueryLinkedFontsFromRegistry(const Font& font,
     63                                   std::map<std::string, std::string>* font_map,
     64                                   std::vector<Font>* linked_fonts) {
     65   const wchar_t* kSystemLink =
     66       L"Software\\Microsoft\\Windows NT\\CurrentVersion\\FontLink\\SystemLink";
     67 
     68   base::win::RegKey key;
     69   if (FAILED(key.Open(HKEY_LOCAL_MACHINE, kSystemLink, KEY_READ)))
     70     return;
     71 
     72   const std::wstring original_font_name = UTF8ToWide(font.GetFontName());
     73   std::vector<std::wstring> values;
     74   if (FAILED(key.ReadValues(original_font_name.c_str(), &values))) {
     75     key.Close();
     76     return;
     77   }
     78 
     79   std::string filename;
     80   std::string font_name;
     81   for (size_t i = 0; i < values.size(); ++i) {
     82     internal::ParseFontLinkEntry(WideToUTF8(values[i]), &filename, &font_name);
     83     // If the font name is present, add that directly, otherwise add the
     84     // font names corresponding to the filename.
     85     if (!font_name.empty()) {
     86       AppendFont(font_name, font.GetFontSize(), linked_fonts);
     87     } else if (!filename.empty()) {
     88       std::vector<std::string> font_names;
     89       GetFontNamesFromFilename(filename, font_map, &font_names);
     90       for (size_t i = 0; i < font_names.size(); ++i)
     91         AppendFont(font_names[i], font.GetFontSize(), linked_fonts);
     92     }
     93   }
     94 
     95   key.Close();
     96 }
     97 
     98 // CachedFontLinkSettings is a singleton cache of the Windows font settings
     99 // from the registry. It maintains a cached view of the registry's list of
    100 // system fonts and their font link chains.
    101 class CachedFontLinkSettings {
    102  public:
    103   static CachedFontLinkSettings* GetInstance();
    104 
    105   // Returns the linked fonts list correspond to |font|. Returned value will
    106   // never be null.
    107   const std::vector<Font>* GetLinkedFonts(const Font& font);
    108 
    109  private:
    110   friend struct DefaultSingletonTraits<CachedFontLinkSettings>;
    111 
    112   CachedFontLinkSettings();
    113   virtual ~CachedFontLinkSettings();
    114 
    115   // Map of system fonts, from file names to font families.
    116   std::map<std::string, std::string> cached_system_fonts_;
    117 
    118   // Map from font names to vectors of linked fonts.
    119   std::map<std::string, std::vector<Font> > cached_linked_fonts_;
    120 
    121   DISALLOW_COPY_AND_ASSIGN(CachedFontLinkSettings);
    122 };
    123 
    124 // static
    125 CachedFontLinkSettings* CachedFontLinkSettings::GetInstance() {
    126   return Singleton<CachedFontLinkSettings,
    127                    LeakySingletonTraits<CachedFontLinkSettings> >::get();
    128 }
    129 
    130 const std::vector<Font>* CachedFontLinkSettings::GetLinkedFonts(
    131     const Font& font) {
    132   const std::string& font_name = font.GetFontName();
    133   std::map<std::string, std::vector<Font> >::const_iterator it =
    134       cached_linked_fonts_.find(font_name);
    135   if (it != cached_linked_fonts_.end())
    136     return &it->second;
    137 
    138   cached_linked_fonts_[font_name] = std::vector<Font>();
    139   std::vector<Font>* linked_fonts = &cached_linked_fonts_[font_name];
    140   QueryLinkedFontsFromRegistry(font, &cached_system_fonts_, linked_fonts);
    141   return linked_fonts;
    142 }
    143 
    144 CachedFontLinkSettings::CachedFontLinkSettings() {
    145 }
    146 
    147 CachedFontLinkSettings::~CachedFontLinkSettings() {
    148 }
    149 
    150 }  // namespace
    151 
    152 namespace internal {
    153 
    154 void ParseFontLinkEntry(const std::string& entry,
    155                         std::string* filename,
    156                         std::string* font_name) {
    157   std::vector<std::string> parts;
    158   base::SplitString(entry, ',', &parts);
    159   filename->clear();
    160   font_name->clear();
    161   if (parts.size() > 0)
    162     *filename = parts[0];
    163   // The second entry may be the font name or the first scaling factor, if the
    164   // entry does not contain a font name. If it contains only digits, assume it
    165   // is a scaling factor.
    166   if (parts.size() > 1 && !ContainsOnlyDigits(parts[1]))
    167     *font_name = parts[1];
    168 }
    169 
    170 void ParseFontFamilyString(const std::string& family,
    171                            std::vector<std::string>* font_names) {
    172   // The entry is comma separated, having the font filename as the first value
    173   // followed optionally by the font family name and a pair of integer scaling
    174   // factors.
    175   // TODO(asvitkine): Should we support these scaling factors?
    176   base::SplitString(family, '&', font_names);
    177   if (!font_names->empty()) {
    178     const size_t index = font_names->back().find('(');
    179     if (index != std::string::npos) {
    180       font_names->back().resize(index);
    181       TrimWhitespace(font_names->back(), TRIM_TRAILING, &font_names->back());
    182     }
    183   }
    184 }
    185 
    186 }  // namespace internal
    187 
    188 LinkedFontsIterator::LinkedFontsIterator(Font font)
    189     : original_font_(font),
    190       next_font_set_(false),
    191       linked_fonts_(NULL),
    192       linked_font_index_(0) {
    193   SetNextFont(original_font_);
    194 }
    195 
    196 LinkedFontsIterator::~LinkedFontsIterator() {
    197 }
    198 
    199 void LinkedFontsIterator::SetNextFont(Font font) {
    200   next_font_ = font;
    201   next_font_set_ = true;
    202 }
    203 
    204 bool LinkedFontsIterator::NextFont(Font* font) {
    205   if (next_font_set_) {
    206     next_font_set_ = false;
    207     current_font_ = next_font_;
    208     *font = current_font_;
    209     return true;
    210   }
    211 
    212   // First time through, get the linked fonts list.
    213   if (linked_fonts_ == NULL)
    214     linked_fonts_ = GetLinkedFonts();
    215 
    216   if (linked_font_index_ == linked_fonts_->size())
    217     return false;
    218 
    219   current_font_ = linked_fonts_->at(linked_font_index_++);
    220   *font = current_font_;
    221   return true;
    222 }
    223 
    224 const std::vector<Font>* LinkedFontsIterator::GetLinkedFonts() const {
    225   CachedFontLinkSettings* font_link = CachedFontLinkSettings::GetInstance();
    226 
    227   // First, try to get the list for the original font.
    228   const std::vector<Font>* fonts = font_link->GetLinkedFonts(original_font_);
    229 
    230   // If there are no linked fonts for the original font, try querying the
    231   // ones for the current font. This may happen if the first font is a custom
    232   // font that has no linked fonts in the registry.
    233   //
    234   // Note: One possibility would be to always merge both lists of fonts,
    235   //       but it is not clear whether there are any real world scenarios
    236   //       where this would actually help.
    237   if (fonts->empty())
    238     fonts = font_link->GetLinkedFonts(current_font_);
    239 
    240   return fonts;
    241 }
    242 
    243 }  // namespace gfx
    244