Chromium Code Reviews| Index: ui/gfx/render_text_win.cc |
| =================================================================== |
| --- ui/gfx/render_text_win.cc (revision 109873) |
| +++ ui/gfx/render_text_win.cc (working copy) |
| @@ -5,15 +5,16 @@ |
| #include "ui/gfx/render_text_win.h" |
| #include <algorithm> |
| -#include <map> |
| #include "base/logging.h" |
| #include "base/stl_util.h" |
| #include "base/string_util.h" |
| +#include "base/utf_string_conversions.h" |
| #include "base/win/scoped_hdc.h" |
| #include "third_party/skia/include/core/SkTypeface.h" |
| #include "ui/gfx/canvas.h" |
| #include "ui/gfx/canvas_skia.h" |
| +#include "ui/gfx/platform_font.h" |
| namespace { |
| @@ -62,6 +63,63 @@ |
| } |
| } |
| +// Callback to |EnumEnhMetaFile()| to intercept font creation. |
| +int CALLBACK MetaFileEnumProc(HDC hdc, |
| + HANDLETABLE* table, |
| + CONST ENHMETARECORD* record, |
| + int table_entries, |
| + LPARAM log_font) { |
| + if (record->iType == EMR_EXTCREATEFONTINDIRECTW) { |
| + LOGFONT* font = reinterpret_cast<LOGFONT*>(log_font); |
|
msw
2011/11/14 19:43:31
Can you just assign a reinterpret_cast'ed value to
Alexei Svitkine (slow)
2011/11/14 19:56:58
Done.
|
| + const EMREXTCREATEFONTINDIRECTW* create_font_record = |
| + reinterpret_cast<const EMREXTCREATEFONTINDIRECTW*>(record); |
| + *font = create_font_record->elfw.elfLogFont; |
| + } |
| + return 1; |
| +} |
| + |
| +// Finds a fallback font to use to render the specified |text| with respect to |
| +// an initial |font|. Returns the resulting font via out param |result|. Returns |
| +// |true| if a fallback font was found. |
| +bool ChooseFallbackFont(HDC hdc, |
|
msw
2011/11/14 19:43:31
It appears that this has been adapted from WebCore
Alexei Svitkine (slow)
2011/11/14 19:56:58
Done.
|
| + const gfx::Font& font, |
| + const wchar_t* text, |
| + int text_length, |
| + gfx::Font* result) { |
| + // Use a meta file to intercept the fallback font chosen by Uniscribe. |
| + HDC meta_file_dc = CreateEnhMetaFile(hdc, NULL, NULL, NULL); |
| + if (!meta_file_dc) |
| + return false; |
| + |
| + SelectObject(meta_file_dc, font.GetNativeFont()); |
| + |
| + SCRIPT_STRING_ANALYSIS script_analysis; |
| + HRESULT hresult = |
| + ScriptStringAnalyse(meta_file_dc, text, text_length, 0, -1, |
| + SSA_METAFILE | SSA_FALLBACK | SSA_GLYPHS | SSA_LINK, |
| + 0, NULL, NULL, NULL, NULL, NULL, &script_analysis); |
| + |
| + if (SUCCEEDED(hresult)) { |
| + hresult = ScriptStringOut(script_analysis, 0, 0, 0, NULL, 0, 0, FALSE); |
| + ScriptStringFree(&script_analysis); |
| + } |
| + |
| + bool found_fallback = false; |
| + HENHMETAFILE meta_file = CloseEnhMetaFile(meta_file_dc); |
| + if (SUCCEEDED(hresult)) { |
| + LOGFONT log_font; |
| + log_font.lfFaceName[0] = 0; |
| + EnumEnhMetaFile(0, meta_file, MetaFileEnumProc, &log_font, NULL); |
| + if (log_font.lfFaceName[0]) { |
| + *result = gfx::Font(UTF16ToUTF8(log_font.lfFaceName), font.GetFontSize()); |
| + found_fallback = true; |
| + } |
| + } |
| + DeleteEnhMetaFile(meta_file); |
| + |
| + return found_fallback; |
| +} |
| + |
| } // namespace |
| namespace gfx { |
| @@ -73,7 +131,8 @@ |
| underline(false), |
| width(0), |
| preceding_run_widths(0), |
| - glyph_count(0) { |
| + glyph_count(0), |
| + script_cache(NULL) { |
| } |
| } // namespace internal |
| @@ -82,7 +141,6 @@ |
| : RenderText(), |
| script_control_(), |
| script_state_(), |
| - script_cache_(NULL), |
| string_width_(0) { |
| // Omitting default constructors for script_* would leave POD uninitialized. |
| HRESULT hr = 0; |
| @@ -102,7 +160,8 @@ |
| } |
| RenderTextWin::~RenderTextWin() { |
| - ScriptFreeCache(&script_cache_); |
| + for (size_t i = 0; i < runs_.size(); ++i) |
| + ScriptFreeCache(&runs_[i]->script_cache); |
| STLDeleteContainerPointers(runs_.begin(), runs_.end()); |
| } |
| @@ -352,18 +411,18 @@ |
| HRESULT hr = E_OUTOFMEMORY; |
| int script_items_count = 0; |
| - scoped_array<SCRIPT_ITEM> script_items; |
| + std::vector<SCRIPT_ITEM> script_items; |
| for (size_t n = kGuessItems; hr == E_OUTOFMEMORY && n < kMaxItems; n *= 2) { |
| // Derive the array of Uniscribe script items from the logical text. |
| // ScriptItemize always adds a terminal array item so that the length of the |
| // last item can be derived from the terminal SCRIPT_ITEM::iCharPos. |
| - script_items.reset(new SCRIPT_ITEM[n]); |
| + script_items.resize(n); |
| hr = ScriptItemize(raw_text, |
| text_length, |
| n - 1, |
| &script_control_, |
| &script_state_, |
| - script_items.get(), |
| + &script_items[0], |
| &script_items_count); |
| } |
| DCHECK(SUCCEEDED(hr)); |
| @@ -375,7 +434,7 @@ |
| // TODO(msw): Only break for font changes, not color etc. See TextRun comment. |
| // TODO(msw): Apply the overriding selection and composition styles. |
| StyleRanges::const_iterator style = style_ranges().begin(); |
| - SCRIPT_ITEM* script_item = script_items.get(); |
| + SCRIPT_ITEM* script_item = &script_items[0]; |
| for (int run_break = 0; run_break < text_length;) { |
| internal::TextRun* run = new internal::TextRun(); |
| run->range.set_start(run_break); |
| @@ -418,7 +477,7 @@ |
| run->glyphs.reset(new WORD[max_glyphs]); |
| run->visible_attributes.reset(new SCRIPT_VISATTR[max_glyphs]); |
| hr = ScriptShape(hdc, |
| - &script_cache_, |
| + &run->script_cache, |
| run_text, |
| run_length, |
| max_glyphs, |
| @@ -430,12 +489,16 @@ |
| if (hr == E_OUTOFMEMORY) { |
| max_glyphs *= 2; |
| } else if (hr == USP_E_SCRIPT_NOT_IN_FONT) { |
| - // The run's font doesn't contain the required glyphs, use an alternate. |
| - // TODO(msw): Font fallback... Don't use SCRIPT_UNDEFINED. |
| - // See https://bugzilla.mozilla.org/show_bug.cgi?id=341500 |
| - // And http://maxradi.us/documents/uniscribe/ |
| if (run->script_analysis.eScript == SCRIPT_UNDEFINED) |
| break; |
| + |
| + // The run's font doesn't contain the required glyphs, use an alternate. |
| + if (ChooseFallbackFont(hdc, run->font, run_text, run_length, |
| + &run->font)) { |
| + ScriptFreeCache(&run->script_cache); |
|
msw
2011/11/14 19:43:31
Is it okay to call ScriptFreeCache here then re-us
Alexei Svitkine (slow)
2011/11/14 19:56:58
Yes, this is fine. ScriptFreeCache() will free it
msw
2011/11/14 20:05:25
Gotcha, thanks for clarifying.
|
| + SelectObject(hdc, run->font.GetNativeFont()); |
| + } |
| + |
| run->script_analysis.eScript = SCRIPT_UNDEFINED; |
|
msw
2011/11/14 19:43:31
I'm not sure, but I think we only ought to set SCR
Alexei Svitkine (slow)
2011/11/14 19:56:58
I think it make sense the way it is now. Here's wh
msw
2011/11/14 20:05:25
Fair enough, but if ChooseFallbackFont doesn't fin
|
| } else { |
| break; |
| @@ -447,7 +510,7 @@ |
| run->advance_widths.reset(new int[run->glyph_count]); |
| run->offsets.reset(new GOFFSET[run->glyph_count]); |
| hr = ScriptPlace(hdc, |
| - &script_cache_, |
| + &run->script_cache, |
| run->glyphs.get(), |
| run->glyph_count, |
| run->visible_attributes.get(), |
| @@ -507,7 +570,6 @@ |
| return run; |
| } |
| - |
| SelectionModel RenderTextWin::FirstSelectionModelInsideRun( |
| internal::TextRun* run) { |
| size_t caret = run->range.start(); |