Index: ui/gfx/render_text_win.cc |
=================================================================== |
--- ui/gfx/render_text_win.cc (revision 110489) |
+++ 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) { |
+ const EMREXTCREATEFONTINDIRECTW* create_font_record = |
+ reinterpret_cast<const EMREXTCREATEFONTINDIRECTW*>(record); |
+ *reinterpret_cast<LOGFONT*>(log_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. |
+// Adapted from WebKit's |FontCache::GetFontDataForCharacters()|. |
+bool ChooseFallbackFont(HDC hdc, |
+ 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,16 +131,20 @@ |
underline(false), |
width(0), |
preceding_run_widths(0), |
- glyph_count(0) { |
+ glyph_count(0), |
+ script_cache(NULL) { |
} |
+TextRun::~TextRun() { |
+ ScriptFreeCache(&script_cache); |
+} |
+ |
} // namespace internal |
RenderTextWin::RenderTextWin() |
: 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 +164,6 @@ |
} |
RenderTextWin::~RenderTextWin() { |
- ScriptFreeCache(&script_cache_); |
STLDeleteContainerPointers(runs_.begin(), runs_.end()); |
} |
@@ -341,7 +402,6 @@ |
} |
void RenderTextWin::ItemizeLogicalText() { |
- text_is_dirty_ = false; |
STLDeleteContainerPointers(runs_.begin(), runs_.end()); |
runs_.clear(); |
if (text().empty()) |
@@ -352,18 +412,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 +435,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 +478,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 +490,20 @@ |
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. |
+ // 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/ |
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); |
+ SelectObject(hdc, run->font.GetNativeFont()); |
+ } |
+ |
run->script_analysis.eScript = SCRIPT_UNDEFINED; |
} else { |
break; |
@@ -447,7 +515,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 +575,6 @@ |
return run; |
} |
- |
SelectionModel RenderTextWin::FirstSelectionModelInsideRun( |
internal::TextRun* run) { |
size_t caret = run->range.start(); |