• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
QueryFontsFromRegistry(std::map<std::string,std::string> * map)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.
GetFontNamesFromFilename(const std::string & filename,std::map<std::string,std::string> * font_map,std::vector<std::string> * font_names)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.
ContainsOnlyDigits(const std::string & text)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.
AppendFont(const std::string & name,int size,std::vector<Font> * fonts)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|.
QueryLinkedFontsFromRegistry(const Font & font,std::map<std::string,std::string> * font_map,std::vector<Font> * linked_fonts)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
GetInstance()125 CachedFontLinkSettings* CachedFontLinkSettings::GetInstance() {
126   return Singleton<CachedFontLinkSettings,
127                    LeakySingletonTraits<CachedFontLinkSettings> >::get();
128 }
129 
GetLinkedFonts(const Font & font)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 
CachedFontLinkSettings()144 CachedFontLinkSettings::CachedFontLinkSettings() {
145 }
146 
~CachedFontLinkSettings()147 CachedFontLinkSettings::~CachedFontLinkSettings() {
148 }
149 
150 }  // namespace
151 
152 namespace internal {
153 
ParseFontLinkEntry(const std::string & entry,std::string * filename,std::string * font_name)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 
ParseFontFamilyString(const std::string & family,std::vector<std::string> * font_names)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 
LinkedFontsIterator(Font font)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 
~LinkedFontsIterator()196 LinkedFontsIterator::~LinkedFontsIterator() {
197 }
198 
SetNextFont(Font font)199 void LinkedFontsIterator::SetNextFont(Font font) {
200   next_font_ = font;
201   next_font_set_ = true;
202 }
203 
NextFont(Font * font)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 
GetLinkedFonts() const224 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