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