summaryrefslogtreecommitdiffstats
path: root/ui/gfx/render_text_win.cc
diff options
context:
space:
mode:
authorasvitkine@chromium.org <asvitkine@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-09-07 20:12:57 +0000
committerasvitkine@chromium.org <asvitkine@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-09-07 20:12:57 +0000
commit76ac8d9ca0e0f3e1a039af11dda6a2943f341909 (patch)
tree49b43a2bb8e6b2ceae774061125aa5fd47ad8ff1 /ui/gfx/render_text_win.cc
parentb8850d0f8e000fddd5cbe4de767139c8a50647aa (diff)
downloadchromium_src-76ac8d9ca0e0f3e1a039af11dda6a2943f341909.zip
chromium_src-76ac8d9ca0e0f3e1a039af11dda6a2943f341909.tar.gz
chromium_src-76ac8d9ca0e0f3e1a039af11dda6a2943f341909.tar.bz2
Refactor font linking code out of RenderTextWin.
This makes the RenderTextWin code more readable and allows for better testing of the font linking code (this CL adds some unit tests for it). Furthermore, this is a step towards print headers / footers supporting display of CJK characters on Windows - which currently doesn't work because the printing code runs RenderText code in a sandbox, which cannot access the registry to get the font linking info. Splitting this code out of RenderTextWin would allow for an architecture where the font linking info is queried outside of RenderTextWin, which could be done in the browser process and passed through IPC to the sandboxed renderer used for printing. This CL merely refactors the code and does not introduce any functionality changes. BUG=136527 TEST=existing and new unit tests; no visual changes Review URL: https://chromiumcodereview.appspot.com/10913098 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@155465 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui/gfx/render_text_win.cc')
-rw-r--r--ui/gfx/render_text_win.cc307
1 files changed, 77 insertions, 230 deletions
diff --git a/ui/gfx/render_text_win.cc b/ui/gfx/render_text_win.cc
index 5f69ea8..cd9c074 100644
--- a/ui/gfx/render_text_win.cc
+++ b/ui/gfx/render_text_win.cc
@@ -9,13 +9,11 @@
#include "base/i18n/break_iterator.h"
#include "base/i18n/rtl.h"
#include "base/logging.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 "base/win/windows_version.h"
#include "ui/gfx/canvas.h"
+#include "ui/gfx/font_fallback_win.h"
#include "ui/gfx/font_smoothing_win.h"
#include "ui/gfx/platform_font_win.h"
@@ -50,6 +48,7 @@ int CALLBACK MetaFileEnumProc(HDC hdc,
// an initial |font|. Returns the resulting font via out param |result|. Returns
// |true| if a fallback font was found.
// Adapted from WebKit's |FontCache::GetFontDataForCharacters()|.
+// TODO(asvitkine): This should be moved to font_fallback_win.cc.
bool ChooseFallbackFont(HDC hdc,
const Font& font,
const wchar_t* text,
@@ -89,117 +88,6 @@ bool ChooseFallbackFont(HDC hdc,
return found_fallback;
}
-// Queries the Registry to get a mapping from font filenames to font names.
-void QueryFontsFromRegistry(std::map<std::string, std::string>* 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<std::string, std::string>* font_map,
- std::vector<std::string>* font_names) {
- if (font_map->empty())
- QueryFontsFromRegistry(font_map);
-
- std::map<std::string, std::string>::const_iterator it =
- font_map->find(StringToLowerASCII(filename));
- if (it == font_map->end())
- return;
-
- // The family string is in the format "FamilyFoo & FamilyBar (TrueType)".
- // Split by '&' and strip off the trailing parenthesized experession.
- base::SplitString(it->second, '&', 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());
- }
- }
-}
-
-// Returns true if |text| contains only ASCII digits.
-bool ContainsOnlyDigits(const std::string& text) {
- return text.find_first_not_of("0123456789") == string16::npos;
-}
-
-// Parses the font's name and filename out of a SystemLink entry string, setting
-// |font_name| and |filename| respectively. If a field is not present or could
-// not be parsed, the corresponding param will be cleared.
-void ParseFontLinkEntry(const std::string& entry,
- std::string* filename,
- std::string* font_name) {
- // Each 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. See: http://msdn.microsoft.com/en-us/goglobal/bb688134.aspx
- // TODO(asvitkine): Should we support these scaling factors?
- std::vector<std::string> 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];
-}
-
-// 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<Font>* 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<std::string, std::string>* font_map,
- std::vector<Font>* linked_fonts) {
- 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<std::wstring> 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) {
- 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<std::string> 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();
-}
-
// Changes |font| to have the specified |font_size| (or |font_height| on Windows
// XP) and |font_style| if it is not the case already. Only considers bold and
// italic styles, since the underlined style has no effect on glyph shaping.
@@ -284,12 +172,6 @@ int GetGlyphXBoundary(internal::TextRun* run, size_t index, bool trailing) {
HDC RenderTextWin::cached_hdc_ = NULL;
// static
-std::map<std::string, std::vector<Font> > RenderTextWin::cached_linked_fonts_;
-
-// static
-std::map<std::string, std::string> RenderTextWin::cached_system_fonts_;
-
-// static
std::map<std::string, Font> RenderTextWin::successful_substitute_fonts_;
RenderTextWin::RenderTextWin()
@@ -716,52 +598,30 @@ void RenderTextWin::LayoutVisualText() {
}
void RenderTextWin::LayoutTextRun(internal::TextRun* run) {
- HRESULT hr = E_FAIL;
const size_t run_length = run->range.length();
const wchar_t* run_text = &(text()[run->range.start()]);
+ Font original_font = run->font;
+ LinkedFontsIterator fonts(original_font);
bool tried_cached_font = false;
bool tried_fallback = false;
- size_t linked_font_index = 0;
- const std::vector<Font>* linked_fonts = NULL;
- Font original_font = run->font;
// Keep track of the font that is able to display the greatest number of
// characters for which ScriptShape() returned S_OK. This font will be used
// in the case where no font is able to display the entire run.
int best_partial_font_missing_char_count = INT_MAX;
- Font best_partial_font = run->font;
+ Font best_partial_font = original_font;
bool using_best_partial_font = false;
-
- // Select the font desired for glyph generation.
- SelectObject(cached_hdc_, run->font.GetNativeFont());
+ Font current_font;
run->logical_clusters.reset(new WORD[run_length]);
- run->glyph_count = 0;
- // Max glyph guess: http://msdn.microsoft.com/en-us/library/dd368564.aspx
- size_t max_glyphs = static_cast<size_t>(1.5 * run_length + 16);
- while (max_glyphs < kMaxGlyphs) {
- run->glyphs.reset(new WORD[max_glyphs]);
- run->visible_attributes.reset(new SCRIPT_VISATTR[max_glyphs]);
- hr = ScriptShape(cached_hdc_,
- &run->script_cache,
- run_text,
- run_length,
- max_glyphs,
- &(run->script_analysis),
- run->glyphs.get(),
- run->logical_clusters.get(),
- run->visible_attributes.get(),
- &(run->glyph_count));
- if (hr == E_OUTOFMEMORY) {
- max_glyphs *= 2;
- continue;
- }
+ while (fonts.NextFont(&current_font)) {
+ HRESULT hr = ShapeTextRunWithFont(run, current_font);
bool glyphs_missing = false;
if (hr == USP_E_SCRIPT_NOT_IN_FONT) {
glyphs_missing = true;
} else if (hr == S_OK) {
- // If |hr| is S_OK, there could still be missing glyphs in the output,
- // see: http://msdn.microsoft.com/en-us/library/windows/desktop/dd368564.aspx
+ // If |hr| is S_OK, there could still be missing glyphs in the output.
+ // http://msdn.microsoft.com/en-us/library/windows/desktop/dd368564.aspx
const int missing_count = CountCharsWithMissingGlyphs(run);
// Track the font that produced the least missing glyphs.
if (missing_count < best_partial_font_missing_char_count) {
@@ -769,24 +629,26 @@ void RenderTextWin::LayoutTextRun(internal::TextRun* run) {
best_partial_font = run->font;
}
glyphs_missing = (missing_count != 0);
+ } else {
+ NOTREACHED() << hr;
}
- // Skip font substitution if there are no missing glyphs or if the font
- // with the least missing glyphs is being used as a last resort.
- if (!glyphs_missing || using_best_partial_font) {
+ // Use the font if it had glyphs for all characters.
+ if (!glyphs_missing) {
// Save the successful fallback font that was chosen.
- if (tried_fallback && !using_best_partial_font)
+ if (tried_fallback)
successful_substitute_fonts_[original_font.GetFontName()] = run->font;
- break;
+ return;
}
// First, try the cached font from previous runs, if any.
if (!tried_cached_font) {
tried_cached_font = true;
+
std::map<std::string, Font>::const_iterator it =
successful_substitute_fonts_.find(original_font.GetFontName());
if (it != successful_substitute_fonts_.end()) {
- ApplySubstituteFont(run, it->second);
+ fonts.SetNextFont(it->second);
continue;
}
}
@@ -794,81 +656,79 @@ void RenderTextWin::LayoutTextRun(internal::TextRun* run) {
// If there are missing glyphs, first try finding a fallback font using a
// meta file, if it hasn't yet been attempted for this run.
// TODO(msw|asvitkine): Support RenderText's font_list()?
- // TODO(msw|asvitkine): Cache previous successful replacement fonts?
if (!tried_fallback) {
tried_fallback = true;
Font fallback_font;
if (ChooseFallbackFont(cached_hdc_, run->font, run_text, run_length,
&fallback_font)) {
- ApplySubstituteFont(run, fallback_font);
+ fonts.SetNextFont(fallback_font);
continue;
}
}
+ }
- // The meta file approach did not yield a replacement font, try to find
- // one using font linking. First time through, get the linked fonts list.
- if (linked_fonts == NULL) {
- // First, try to get the list for the original font.
- linked_fonts = GetLinkedFonts(original_font);
-
- // If there are no linked fonts for the original font, try querying the
- // ones for the Uniscribe fallback 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 (linked_fonts->empty())
- linked_fonts = GetLinkedFonts(run->font);
- }
-
- // None of the fallback fonts were able to display the entire run.
- if (linked_font_index == linked_fonts->size()) {
- // If a font was able to partially display the run, use that now.
- if (best_partial_font_missing_char_count != INT_MAX) {
- ApplySubstituteFont(run, best_partial_font);
- using_best_partial_font = true;
- continue;
- }
-
- // If no font was able to partially display the run, replace all glyphs
- // with |wgDefault| to ensure they don't hold garbage values.
- SCRIPT_FONTPROPERTIES properties;
- memset(&properties, 0, sizeof(properties));
- properties.cBytes = sizeof(properties);
- ScriptGetFontProperties(cached_hdc_, &run->script_cache, &properties);
- for (int i = 0; i < run->glyph_count; ++i)
- run->glyphs[i] = properties.wgDefault;
-
- // TODO(msw): Don't use SCRIPT_UNDEFINED. Apparently Uniscribe can
- // crash on certain surrogate pairs with SCRIPT_UNDEFINED.
- // See https://bugzilla.mozilla.org/show_bug.cgi?id=341500
- // And http://maxradi.us/documents/uniscribe/
- run->script_analysis.eScript = SCRIPT_UNDEFINED;
- // Reset |hr| to 0 to not trigger the DCHECK() below when a font is
- // not found that can display the text. This is expected behavior
- // under Windows XP without additional language packs installed and
- // may also happen on newer versions when trying to display text in
- // an obscure script that the system doesn't have the right font for.
- hr = 0;
- break;
- }
+ // If a font was able to partially display the run, use that now.
+ if (best_partial_font_missing_char_count != INT_MAX) {
+ // Re-shape the run only if |best_partial_font| differs from the last font.
+ if (best_partial_font.GetNativeFont() != run->font.GetNativeFont())
+ ShapeTextRunWithFont(run, best_partial_font);
+ return;
+ }
- // Try the next linked font.
- ApplySubstituteFont(run, linked_fonts->at(linked_font_index++));
+ // If no font was able to partially display the run, replace all glyphs
+ // with |wgDefault| to ensure they don't hold garbage values.
+ SCRIPT_FONTPROPERTIES properties;
+ memset(&properties, 0, sizeof(properties));
+ properties.cBytes = sizeof(properties);
+ ScriptGetFontProperties(cached_hdc_, &run->script_cache, &properties);
+ for (int i = 0; i < run->glyph_count; ++i)
+ run->glyphs[i] = properties.wgDefault;
+
+ // TODO(msw): Don't use SCRIPT_UNDEFINED. Apparently Uniscribe can
+ // crash on certain surrogate pairs with SCRIPT_UNDEFINED.
+ // See https://bugzilla.mozilla.org/show_bug.cgi?id=341500
+ // And http://maxradi.us/documents/uniscribe/
+ run->script_analysis.eScript = SCRIPT_UNDEFINED;
+}
+
+HRESULT RenderTextWin::ShapeTextRunWithFont(internal::TextRun* run,
+ const Font& font) {
+ // Update the run's font only if necessary. If the two fonts wrap the same
+ // PlatformFontWin object, their native fonts will have the same value.
+ if (run->font.GetNativeFont() != font.GetNativeFont()) {
+ const int font_size = run->font.GetFontSize();
+ const int font_height = run->font.GetHeight();
+ run->font = font;
+ DeriveFontIfNecessary(font_size, font_height, run->font_style, &run->font);
+ ScriptFreeCache(&run->script_cache);
}
- DCHECK(SUCCEEDED(hr));
-}
-void RenderTextWin::ApplySubstituteFont(internal::TextRun* run,
- const Font& font) {
- const int font_size = run->font.GetFontSize();
- const int font_height = run->font.GetHeight();
- run->font = font;
- DeriveFontIfNecessary(font_size, font_height, run->font_style, &run->font);
- ScriptFreeCache(&run->script_cache);
+ // Select the font desired for glyph generation.
SelectObject(cached_hdc_, run->font.GetNativeFont());
+
+ HRESULT hr = E_OUTOFMEMORY;
+ const size_t run_length = run->range.length();
+ const wchar_t* run_text = &(text()[run->range.start()]);
+ // Max glyph guess: http://msdn.microsoft.com/en-us/library/dd368564.aspx
+ size_t max_glyphs = static_cast<size_t>(1.5 * run_length + 16);
+ while (hr == E_OUTOFMEMORY && max_glyphs < kMaxGlyphs) {
+ run->glyph_count = 0;
+ run->glyphs.reset(new WORD[max_glyphs]);
+ run->visible_attributes.reset(new SCRIPT_VISATTR[max_glyphs]);
+ hr = ScriptShape(cached_hdc_,
+ &run->script_cache,
+ run_text,
+ run_length,
+ max_glyphs,
+ &run->script_analysis,
+ run->glyphs.get(),
+ run->logical_clusters.get(),
+ run->visible_attributes.get(),
+ &run->glyph_count);
+ max_glyphs *= 2;
+ }
+ return hr;
}
int RenderTextWin::CountCharsWithMissingGlyphs(internal::TextRun* run) const {
@@ -905,19 +765,6 @@ int RenderTextWin::CountCharsWithMissingGlyphs(internal::TextRun* run) const {
return run->range.length() - chars_not_missing_glyphs;
}
-const std::vector<Font>* RenderTextWin::GetLinkedFonts(const Font& font) const {
- const std::string& font_name = font.GetFontName();
- std::map<std::string, std::vector<Font> >::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<Font>();
- std::vector<Font>* linked_fonts = &cached_linked_fonts_[font_name];
- QueryLinkedFontsFromRegistry(font, &cached_system_fonts_, linked_fonts);
- return linked_fonts;
-}
-
size_t RenderTextWin::GetRunContainingCaret(const SelectionModel& caret) const {
DCHECK(!needs_layout_);
size_t position = caret.caret_pos();