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.
5 #include "ui/gfx/font_fallback_win.h"
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"
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";
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());
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
);
41 std::map
<std::string
, std::string
>::const_iterator it
=
42 font_map
->find(StringToLowerASCII(filename
));
43 if (it
== font_map
->end())
46 internal::ParseFontFamilyString(it
->second
, font_names
);
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
;
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
));
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";
68 base::win::RegKey key
;
69 if (FAILED(key
.Open(HKEY_LOCAL_MACHINE
, kSystemLink
, KEY_READ
)))
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
))) {
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
);
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
{
103 static CachedFontLinkSettings
* GetInstance();
105 // Returns the linked fonts list correspond to |font|. Returned value will
107 const std::vector
<Font
>* GetLinkedFonts(const Font
& font
);
110 friend struct DefaultSingletonTraits
<CachedFontLinkSettings
>;
112 CachedFontLinkSettings();
113 virtual ~CachedFontLinkSettings();
115 // Map of system fonts, from file names to font families.
116 std::map
<std::string
, std::string
> cached_system_fonts_
;
118 // Map from font names to vectors of linked fonts.
119 std::map
<std::string
, std::vector
<Font
> > cached_linked_fonts_
;
121 DISALLOW_COPY_AND_ASSIGN(CachedFontLinkSettings
);
125 CachedFontLinkSettings
* CachedFontLinkSettings::GetInstance() {
126 return Singleton
<CachedFontLinkSettings
,
127 LeakySingletonTraits
<CachedFontLinkSettings
> >::get();
130 const std::vector
<Font
>* CachedFontLinkSettings::GetLinkedFonts(
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())
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
);
144 CachedFontLinkSettings::CachedFontLinkSettings() {
147 CachedFontLinkSettings::~CachedFontLinkSettings() {
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
);
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];
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
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());
186 } // namespace internal
188 LinkedFontsIterator::LinkedFontsIterator(Font font
)
189 : original_font_(font
),
190 next_font_set_(false),
192 linked_font_index_(0) {
193 SetNextFont(original_font_
);
196 LinkedFontsIterator::~LinkedFontsIterator() {
199 void LinkedFontsIterator::SetNextFont(Font font
) {
201 next_font_set_
= true;
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_
;
212 // First time through, get the linked fonts list.
213 if (linked_fonts_
== NULL
)
214 linked_fonts_
= GetLinkedFonts();
216 if (linked_font_index_
== linked_fonts_
->size())
219 current_font_
= linked_fonts_
->at(linked_font_index_
++);
220 *font
= current_font_
;
224 const std::vector
<Font
>* LinkedFontsIterator::GetLinkedFonts() const {
225 CachedFontLinkSettings
* font_link
= CachedFontLinkSettings::GetInstance();
227 // First, try to get the list for the original font.
228 const std::vector
<Font
>* fonts
= font_link
->GetLinkedFonts(original_font_
);
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.
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.
238 fonts
= font_link
->GetLinkedFonts(current_font_
);