Commit 7b2db2e2 authored by alekseys@chromium.org's avatar alekseys@chromium.org

Revert 109977 - Implement font fallback in RenderTextWin.

This is done by using a metafile to capture the font
that Uniscribe would use to render the text (since
there is no API to get this from Uniscribe itself).

Makes SCRIPT_CACHE be per-run, since different runs
may have different fonts and the SCRIPT_CACHE cannot
be re-used between these.

This is similar to what is done in WebKit in FontCacheWin.cpp

BUG=90426
TEST=Run chrome.exe --use-pure-views and paste some Hebrew
text into the omnibox. It should show up properly.

Review URL: http://codereview.chromium.org/8565011

TBR=asvitkine@chromium.org
Review URL: http://codereview.chromium.org/8568002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@110001 0039d316-1c4b-4281-b951-d872f2087c98
parent 18a8f2d0
...@@ -5,16 +5,15 @@ ...@@ -5,16 +5,15 @@
#include "ui/gfx/render_text_win.h" #include "ui/gfx/render_text_win.h"
#include <algorithm> #include <algorithm>
#include <map>
#include "base/logging.h" #include "base/logging.h"
#include "base/stl_util.h" #include "base/stl_util.h"
#include "base/string_util.h" #include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "base/win/scoped_hdc.h" #include "base/win/scoped_hdc.h"
#include "third_party/skia/include/core/SkTypeface.h" #include "third_party/skia/include/core/SkTypeface.h"
#include "ui/gfx/canvas.h" #include "ui/gfx/canvas.h"
#include "ui/gfx/canvas_skia.h" #include "ui/gfx/canvas_skia.h"
#include "ui/gfx/platform_font.h"
namespace { namespace {
...@@ -63,63 +62,6 @@ void DrawTextRunDecorations(SkCanvas* canvas_skia, ...@@ -63,63 +62,6 @@ void DrawTextRunDecorations(SkCanvas* canvas_skia,
} }
} }
// 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
namespace gfx { namespace gfx {
...@@ -131,8 +73,7 @@ TextRun::TextRun() ...@@ -131,8 +73,7 @@ TextRun::TextRun()
underline(false), underline(false),
width(0), width(0),
preceding_run_widths(0), preceding_run_widths(0),
glyph_count(0), glyph_count(0) {
script_cache(NULL) {
} }
} // namespace internal } // namespace internal
...@@ -141,6 +82,7 @@ RenderTextWin::RenderTextWin() ...@@ -141,6 +82,7 @@ RenderTextWin::RenderTextWin()
: RenderText(), : RenderText(),
script_control_(), script_control_(),
script_state_(), script_state_(),
script_cache_(NULL),
string_width_(0) { string_width_(0) {
// Omitting default constructors for script_* would leave POD uninitialized. // Omitting default constructors for script_* would leave POD uninitialized.
HRESULT hr = 0; HRESULT hr = 0;
...@@ -160,8 +102,7 @@ RenderTextWin::RenderTextWin() ...@@ -160,8 +102,7 @@ RenderTextWin::RenderTextWin()
} }
RenderTextWin::~RenderTextWin() { RenderTextWin::~RenderTextWin() {
for (size_t i = 0; i < runs_.size(); ++i) ScriptFreeCache(&script_cache_);
ScriptFreeCache(&runs_[i]->script_cache);
STLDeleteContainerPointers(runs_.begin(), runs_.end()); STLDeleteContainerPointers(runs_.begin(), runs_.end());
} }
...@@ -411,18 +352,18 @@ void RenderTextWin::ItemizeLogicalText() { ...@@ -411,18 +352,18 @@ void RenderTextWin::ItemizeLogicalText() {
HRESULT hr = E_OUTOFMEMORY; HRESULT hr = E_OUTOFMEMORY;
int script_items_count = 0; int script_items_count = 0;
std::vector<SCRIPT_ITEM> script_items; scoped_array<SCRIPT_ITEM> script_items;
for (size_t n = kGuessItems; hr == E_OUTOFMEMORY && n < kMaxItems; n *= 2) { for (size_t n = kGuessItems; hr == E_OUTOFMEMORY && n < kMaxItems; n *= 2) {
// Derive the array of Uniscribe script items from the logical text. // Derive the array of Uniscribe script items from the logical text.
// ScriptItemize always adds a terminal array item so that the length of the // ScriptItemize always adds a terminal array item so that the length of the
// last item can be derived from the terminal SCRIPT_ITEM::iCharPos. // last item can be derived from the terminal SCRIPT_ITEM::iCharPos.
script_items.resize(n); script_items.reset(new SCRIPT_ITEM[n]);
hr = ScriptItemize(raw_text, hr = ScriptItemize(raw_text,
text_length, text_length,
n - 1, n - 1,
&script_control_, &script_control_,
&script_state_, &script_state_,
&script_items[0], script_items.get(),
&script_items_count); &script_items_count);
} }
DCHECK(SUCCEEDED(hr)); DCHECK(SUCCEEDED(hr));
...@@ -434,7 +375,7 @@ void RenderTextWin::ItemizeLogicalText() { ...@@ -434,7 +375,7 @@ void RenderTextWin::ItemizeLogicalText() {
// TODO(msw): Only break for font changes, not color etc. See TextRun comment. // TODO(msw): Only break for font changes, not color etc. See TextRun comment.
// TODO(msw): Apply the overriding selection and composition styles. // TODO(msw): Apply the overriding selection and composition styles.
StyleRanges::const_iterator style = style_ranges().begin(); StyleRanges::const_iterator style = style_ranges().begin();
SCRIPT_ITEM* script_item = &script_items[0]; SCRIPT_ITEM* script_item = script_items.get();
for (int run_break = 0; run_break < text_length;) { for (int run_break = 0; run_break < text_length;) {
internal::TextRun* run = new internal::TextRun(); internal::TextRun* run = new internal::TextRun();
run->range.set_start(run_break); run->range.set_start(run_break);
...@@ -477,7 +418,7 @@ void RenderTextWin::LayoutVisualText() { ...@@ -477,7 +418,7 @@ void RenderTextWin::LayoutVisualText() {
run->glyphs.reset(new WORD[max_glyphs]); run->glyphs.reset(new WORD[max_glyphs]);
run->visible_attributes.reset(new SCRIPT_VISATTR[max_glyphs]); run->visible_attributes.reset(new SCRIPT_VISATTR[max_glyphs]);
hr = ScriptShape(hdc, hr = ScriptShape(hdc,
&run->script_cache, &script_cache_,
run_text, run_text,
run_length, run_length,
max_glyphs, max_glyphs,
...@@ -489,20 +430,12 @@ void RenderTextWin::LayoutVisualText() { ...@@ -489,20 +430,12 @@ void RenderTextWin::LayoutVisualText() {
if (hr == E_OUTOFMEMORY) { if (hr == E_OUTOFMEMORY) {
max_glyphs *= 2; max_glyphs *= 2;
} else if (hr == USP_E_SCRIPT_NOT_IN_FONT) { } else if (hr == USP_E_SCRIPT_NOT_IN_FONT) {
// TODO(msw): Don't use SCRIPT_UNDEFINED. Apparently Uniscribe can crash // The run's font doesn't contain the required glyphs, use an alternate.
// on certain surrogate pairs when using SCRIPT_UNDEFINED. // TODO(msw): Font fallback... Don't use SCRIPT_UNDEFINED.
// See https://bugzilla.mozilla.org/show_bug.cgi?id=341500 // See https://bugzilla.mozilla.org/show_bug.cgi?id=341500
// And http://maxradi.us/documents/uniscribe/ // And http://maxradi.us/documents/uniscribe/
if (run->script_analysis.eScript == SCRIPT_UNDEFINED) if (run->script_analysis.eScript == SCRIPT_UNDEFINED)
break; 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; run->script_analysis.eScript = SCRIPT_UNDEFINED;
} else { } else {
break; break;
...@@ -514,7 +447,7 @@ void RenderTextWin::LayoutVisualText() { ...@@ -514,7 +447,7 @@ void RenderTextWin::LayoutVisualText() {
run->advance_widths.reset(new int[run->glyph_count]); run->advance_widths.reset(new int[run->glyph_count]);
run->offsets.reset(new GOFFSET[run->glyph_count]); run->offsets.reset(new GOFFSET[run->glyph_count]);
hr = ScriptPlace(hdc, hr = ScriptPlace(hdc,
&run->script_cache, &script_cache_,
run->glyphs.get(), run->glyphs.get(),
run->glyph_count, run->glyph_count,
run->visible_attributes.get(), run->visible_attributes.get(),
...@@ -574,6 +507,7 @@ size_t RenderTextWin::GetRunContainingPoint(const Point& point) const { ...@@ -574,6 +507,7 @@ size_t RenderTextWin::GetRunContainingPoint(const Point& point) const {
return run; return run;
} }
SelectionModel RenderTextWin::FirstSelectionModelInsideRun( SelectionModel RenderTextWin::FirstSelectionModelInsideRun(
internal::TextRun* run) { internal::TextRun* run) {
size_t caret = run->range.start(); size_t caret = run->range.start();
......
...@@ -43,7 +43,6 @@ struct TextRun { ...@@ -43,7 +43,6 @@ struct TextRun {
scoped_array<int> advance_widths; scoped_array<int> advance_widths;
scoped_array<GOFFSET> offsets; scoped_array<GOFFSET> offsets;
ABC abc_widths; ABC abc_widths;
SCRIPT_CACHE script_cache;
private: private:
DISALLOW_COPY_AND_ASSIGN(TextRun); DISALLOW_COPY_AND_ASSIGN(TextRun);
...@@ -90,8 +89,8 @@ class RenderTextWin : public RenderText { ...@@ -90,8 +89,8 @@ class RenderTextWin : public RenderText {
// Given a |run|, returns the SelectionModel that contains the logical first // Given a |run|, returns the SelectionModel that contains the logical first
// or last caret position inside (not at a boundary of) the run. // or last caret position inside (not at a boundary of) the run.
// The returned value represents a cursor/caret position without a selection. // The returned value represents a cursor/caret position without a selection.
SelectionModel FirstSelectionModelInsideRun(internal::TextRun* run); SelectionModel FirstSelectionModelInsideRun(internal::TextRun*);
SelectionModel LastSelectionModelInsideRun(internal::TextRun* run); SelectionModel LastSelectionModelInsideRun(internal::TextRun*);
// Get the selection model visually left/right of |selection| by one grapheme. // Get the selection model visually left/right of |selection| by one grapheme.
// The returned value represents a cursor/caret position without a selection. // The returned value represents a cursor/caret position without a selection.
...@@ -112,6 +111,8 @@ class RenderTextWin : public RenderText { ...@@ -112,6 +111,8 @@ class RenderTextWin : public RenderText {
SCRIPT_CONTROL script_control_; SCRIPT_CONTROL script_control_;
SCRIPT_STATE script_state_; SCRIPT_STATE script_state_;
SCRIPT_CACHE script_cache_;
std::vector<internal::TextRun*> runs_; std::vector<internal::TextRun*> runs_;
int string_width_; int string_width_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment