// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ui/gfx/font_fallback_win.h" #include #include "base/memory/singleton.h" #include "base/string_split.h" #include "base/string_util.h" #include "base/threading/thread_restrictions.h" #include "base/utf_string_conversions.h" #include "base/win/registry.h" #include "ui/gfx/font.h" namespace gfx { namespace { // Queries the registry to get a mapping from font filenames to font names. void QueryFontsFromRegistry(std::map* map) { const wchar_t* kFonts = L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"; base::win::RegistryValueIterator it(HKEY_LOCAL_MACHINE, kFonts); for (; it.Valid(); ++it) { const std::string filename = StringToLowerASCII(WideToUTF8(it.Value())); (*map)[filename] = WideToUTF8(it.Name()); } } // Fills |font_names| with a list of font families found in the font file at // |filename|. Takes in a |font_map| from font filename to font families, which // is filled-in by querying the registry, if empty. void GetFontNamesFromFilename(const std::string& filename, std::map* font_map, std::vector* font_names) { if (font_map->empty()) QueryFontsFromRegistry(font_map); std::map::const_iterator it = font_map->find(StringToLowerASCII(filename)); if (it == font_map->end()) return; internal::ParseFontFamilyString(it->second, font_names); } // Returns true if |text| contains only ASCII digits. bool ContainsOnlyDigits(const std::string& text) { return text.find_first_not_of("0123456789") == string16::npos; } // Appends a Font with the given |name| and |size| to |fonts| unless the last // entry is already a font with that name. void AppendFont(const std::string& name, int size, std::vector* fonts) { if (fonts->empty() || fonts->back().GetFontName() != name) fonts->push_back(Font(name, size)); } // Queries the registry to get a list of linked fonts for |font|. void QueryLinkedFontsFromRegistry(const Font& font, std::map* font_map, std::vector* linked_fonts) { // TODO(asvitkine): Find a way to do this on a non-UI thread. base::ThreadRestrictions::ScopedAllowIO allow_io; const wchar_t* kSystemLink = L"Software\\Microsoft\\Windows NT\\CurrentVersion\\FontLink\\SystemLink"; base::win::RegKey key; if (FAILED(key.Open(HKEY_LOCAL_MACHINE, kSystemLink, KEY_READ))) return; const std::wstring original_font_name = UTF8ToWide(font.GetFontName()); std::vector values; if (FAILED(key.ReadValues(original_font_name.c_str(), &values))) { key.Close(); return; } std::string filename; std::string font_name; for (size_t i = 0; i < values.size(); ++i) { internal::ParseFontLinkEntry(WideToUTF8(values[i]), &filename, &font_name); // If the font name is present, add that directly, otherwise add the // font names corresponding to the filename. if (!font_name.empty()) { AppendFont(font_name, font.GetFontSize(), linked_fonts); } else if (!filename.empty()) { std::vector font_names; GetFontNamesFromFilename(filename, font_map, &font_names); for (size_t i = 0; i < font_names.size(); ++i) AppendFont(font_names[i], font.GetFontSize(), linked_fonts); } } key.Close(); } // CachedFontLinkSettings is a singleton cache of the Windows font settings // from the registry. It maintains a cached view of the registry's list of // system fonts and their font link chains. class CachedFontLinkSettings { public: static CachedFontLinkSettings* GetInstance(); // Returns the linked fonts list correspond to |font|. Returned value will // never be null. const std::vector* GetLinkedFonts(const Font& font); private: friend struct DefaultSingletonTraits; CachedFontLinkSettings(); virtual ~CachedFontLinkSettings(); // Map of system fonts, from file names to font families. std::map cached_system_fonts_; // Map from font names to vectors of linked fonts. std::map > cached_linked_fonts_; DISALLOW_COPY_AND_ASSIGN(CachedFontLinkSettings); }; // static CachedFontLinkSettings* CachedFontLinkSettings::GetInstance() { return Singleton >::get(); } const std::vector* CachedFontLinkSettings::GetLinkedFonts( const Font& font) { const std::string& font_name = font.GetFontName(); std::map >::const_iterator it = cached_linked_fonts_.find(font_name); if (it != cached_linked_fonts_.end()) return &it->second; cached_linked_fonts_[font_name] = std::vector(); std::vector* linked_fonts = &cached_linked_fonts_[font_name]; QueryLinkedFontsFromRegistry(font, &cached_system_fonts_, linked_fonts); return linked_fonts; } CachedFontLinkSettings::CachedFontLinkSettings() { } CachedFontLinkSettings::~CachedFontLinkSettings() { } } // namespace namespace internal { void ParseFontLinkEntry(const std::string& entry, std::string* filename, std::string* font_name) { std::vector parts; base::SplitString(entry, ',', &parts); filename->clear(); font_name->clear(); if (parts.size() > 0) *filename = parts[0]; // The second entry may be the font name or the first scaling factor, if the // entry does not contain a font name. If it contains only digits, assume it // is a scaling factor. if (parts.size() > 1 && !ContainsOnlyDigits(parts[1])) *font_name = parts[1]; } void ParseFontFamilyString(const std::string& family, std::vector* font_names) { // The entry is comma separated, having the font filename as the first value // followed optionally by the font family name and a pair of integer scaling // factors. // TODO(asvitkine): Should we support these scaling factors? base::SplitString(family, '&', font_names); if (!font_names->empty()) { const size_t index = font_names->back().find('('); if (index != std::string::npos) { font_names->back().resize(index); TrimWhitespace(font_names->back(), TRIM_TRAILING, &font_names->back()); } } } } // namespace internal LinkedFontsIterator::LinkedFontsIterator(Font font) : original_font_(font), next_font_set_(false), linked_fonts_(NULL), linked_font_index_(0) { SetNextFont(original_font_); } LinkedFontsIterator::~LinkedFontsIterator() { } void LinkedFontsIterator::SetNextFont(Font font) { next_font_ = font; next_font_set_ = true; } bool LinkedFontsIterator::NextFont(Font* font) { if (next_font_set_) { next_font_set_ = false; current_font_ = next_font_; *font = current_font_; return true; } // First time through, get the linked fonts list. if (linked_fonts_ == NULL) linked_fonts_ = GetLinkedFonts(); if (linked_font_index_ == linked_fonts_->size()) return false; current_font_ = linked_fonts_->at(linked_font_index_++); *font = current_font_; return true; } const std::vector* LinkedFontsIterator::GetLinkedFonts() const { CachedFontLinkSettings* font_link = CachedFontLinkSettings::GetInstance(); // First, try to get the list for the original font. const std::vector* fonts = font_link->GetLinkedFonts(original_font_); // If there are no linked fonts for the original font, try querying the // ones for the current font. This may happen if the first font is a custom // font that has no linked fonts in the registry. // // Note: One possibility would be to always merge both lists of fonts, // but it is not clear whether there are any real world scenarios // where this would actually help. if (fonts->empty()) fonts = font_link->GetLinkedFonts(current_font_); return fonts; } } // namespace gfx