Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(69)

Side by Side Diff: ui/gfx/render_text_win.cc

Issue 8568002: Revert 109977 - Implement font fallback in RenderTextWin. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « ui/gfx/render_text_win.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Property Changes:
Deleted: svn:mergeinfo
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "ui/gfx/render_text_win.h" 5 #include "ui/gfx/render_text_win.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <map>
8 9
9 #include "base/logging.h" 10 #include "base/logging.h"
10 #include "base/stl_util.h" 11 #include "base/stl_util.h"
11 #include "base/string_util.h" 12 #include "base/string_util.h"
12 #include "base/utf_string_conversions.h"
13 #include "base/win/scoped_hdc.h" 13 #include "base/win/scoped_hdc.h"
14 #include "third_party/skia/include/core/SkTypeface.h" 14 #include "third_party/skia/include/core/SkTypeface.h"
15 #include "ui/gfx/canvas.h" 15 #include "ui/gfx/canvas.h"
16 #include "ui/gfx/canvas_skia.h" 16 #include "ui/gfx/canvas_skia.h"
17 #include "ui/gfx/platform_font.h"
18 17
19 namespace { 18 namespace {
20 19
21 // The maximum supported number of Uniscribe runs; a SCRIPT_ITEM is 8 bytes. 20 // The maximum supported number of Uniscribe runs; a SCRIPT_ITEM is 8 bytes.
22 // TODO(msw): Review memory use/failure? Max string length? Alternate approach? 21 // TODO(msw): Review memory use/failure? Max string length? Alternate approach?
23 const int kGuessItems = 100; 22 const int kGuessItems = 100;
24 const int kMaxItems = 10000; 23 const int kMaxItems = 10000;
25 24
26 // The maximum supported number of Uniscribe glyphs; a glyph is 1 word. 25 // The maximum supported number of Uniscribe glyphs; a glyph is 1 word.
27 // TODO(msw): Review memory use/failure? Max string length? Alternate approach? 26 // TODO(msw): Review memory use/failure? Max string length? Alternate approach?
(...skipping 28 matching lines...) Expand all
56 canvas_skia->drawRect(r, paint); 55 canvas_skia->drawRect(r, paint);
57 } 56 }
58 if (run.strike) { 57 if (run.strike) {
59 SkScalar offset = SkScalarMulAdd(text_size, kStrikeThroughOffset, y); 58 SkScalar offset = SkScalarMulAdd(text_size, kStrikeThroughOffset, y);
60 r.fTop = offset; 59 r.fTop = offset;
61 r.fBottom = offset + height; 60 r.fBottom = offset + height;
62 canvas_skia->drawRect(r, paint); 61 canvas_skia->drawRect(r, paint);
63 } 62 }
64 } 63 }
65 64
66 // Callback to |EnumEnhMetaFile()| to intercept font creation.
67 int CALLBACK MetaFileEnumProc(HDC hdc,
68 HANDLETABLE* table,
69 CONST ENHMETARECORD* record,
70 int table_entries,
71 LPARAM log_font) {
72 if (record->iType == EMR_EXTCREATEFONTINDIRECTW) {
73 const EMREXTCREATEFONTINDIRECTW* create_font_record =
74 reinterpret_cast<const EMREXTCREATEFONTINDIRECTW*>(record);
75 *reinterpret_cast<LOGFONT*>(log_font) = create_font_record->elfw.elfLogFont;
76 }
77 return 1;
78 }
79
80 // Finds a fallback font to use to render the specified |text| with respect to
81 // an initial |font|. Returns the resulting font via out param |result|. Returns
82 // |true| if a fallback font was found.
83 // Adapted from WebKit's |FontCache::GetFontDataForCharacters()|.
84 bool ChooseFallbackFont(HDC hdc,
85 const gfx::Font& font,
86 const wchar_t* text,
87 int text_length,
88 gfx::Font* result) {
89 // Use a meta file to intercept the fallback font chosen by Uniscribe.
90 HDC meta_file_dc = CreateEnhMetaFile(hdc, NULL, NULL, NULL);
91 if (!meta_file_dc)
92 return false;
93
94 SelectObject(meta_file_dc, font.GetNativeFont());
95
96 SCRIPT_STRING_ANALYSIS script_analysis;
97 HRESULT hresult =
98 ScriptStringAnalyse(meta_file_dc, text, text_length, 0, -1,
99 SSA_METAFILE | SSA_FALLBACK | SSA_GLYPHS | SSA_LINK,
100 0, NULL, NULL, NULL, NULL, NULL, &script_analysis);
101
102 if (SUCCEEDED(hresult)) {
103 hresult = ScriptStringOut(script_analysis, 0, 0, 0, NULL, 0, 0, FALSE);
104 ScriptStringFree(&script_analysis);
105 }
106
107 bool found_fallback = false;
108 HENHMETAFILE meta_file = CloseEnhMetaFile(meta_file_dc);
109 if (SUCCEEDED(hresult)) {
110 LOGFONT log_font;
111 log_font.lfFaceName[0] = 0;
112 EnumEnhMetaFile(0, meta_file, MetaFileEnumProc, &log_font, NULL);
113 if (log_font.lfFaceName[0]) {
114 *result = gfx::Font(UTF16ToUTF8(log_font.lfFaceName), font.GetFontSize());
115 found_fallback = true;
116 }
117 }
118 DeleteEnhMetaFile(meta_file);
119
120 return found_fallback;
121 }
122
123 } // namespace 65 } // namespace
124 66
125 namespace gfx { 67 namespace gfx {
126 68
127 namespace internal { 69 namespace internal {
128 70
129 TextRun::TextRun() 71 TextRun::TextRun()
130 : strike(false), 72 : strike(false),
131 underline(false), 73 underline(false),
132 width(0), 74 width(0),
133 preceding_run_widths(0), 75 preceding_run_widths(0),
134 glyph_count(0), 76 glyph_count(0) {
135 script_cache(NULL) {
136 } 77 }
137 78
138 } // namespace internal 79 } // namespace internal
139 80
140 RenderTextWin::RenderTextWin() 81 RenderTextWin::RenderTextWin()
141 : RenderText(), 82 : RenderText(),
142 script_control_(), 83 script_control_(),
143 script_state_(), 84 script_state_(),
85 script_cache_(NULL),
144 string_width_(0) { 86 string_width_(0) {
145 // Omitting default constructors for script_* would leave POD uninitialized. 87 // Omitting default constructors for script_* would leave POD uninitialized.
146 HRESULT hr = 0; 88 HRESULT hr = 0;
147 89
148 // TODO(msw): Call ScriptRecordDigitSubstitution on WM_SETTINGCHANGE message. 90 // TODO(msw): Call ScriptRecordDigitSubstitution on WM_SETTINGCHANGE message.
149 // TODO(msw): Use Chrome/profile locale/language settings? 91 // TODO(msw): Use Chrome/profile locale/language settings?
150 hr = ScriptRecordDigitSubstitution(LOCALE_USER_DEFAULT, &digit_substitute_); 92 hr = ScriptRecordDigitSubstitution(LOCALE_USER_DEFAULT, &digit_substitute_);
151 DCHECK(SUCCEEDED(hr)); 93 DCHECK(SUCCEEDED(hr));
152 94
153 hr = ScriptApplyDigitSubstitution(&digit_substitute_, 95 hr = ScriptApplyDigitSubstitution(&digit_substitute_,
154 &script_control_, 96 &script_control_,
155 &script_state_); 97 &script_state_);
156 DCHECK(SUCCEEDED(hr)); 98 DCHECK(SUCCEEDED(hr));
157 script_control_.fMergeNeutralItems = true; 99 script_control_.fMergeNeutralItems = true;
158 100
159 MoveCursorTo(LeftEndSelectionModel()); 101 MoveCursorTo(LeftEndSelectionModel());
160 } 102 }
161 103
162 RenderTextWin::~RenderTextWin() { 104 RenderTextWin::~RenderTextWin() {
163 for (size_t i = 0; i < runs_.size(); ++i) 105 ScriptFreeCache(&script_cache_);
164 ScriptFreeCache(&runs_[i]->script_cache);
165 STLDeleteContainerPointers(runs_.begin(), runs_.end()); 106 STLDeleteContainerPointers(runs_.begin(), runs_.end());
166 } 107 }
167 108
168 int RenderTextWin::GetStringWidth() { 109 int RenderTextWin::GetStringWidth() {
169 return string_width_; 110 return string_width_;
170 } 111 }
171 112
172 void RenderTextWin::Draw(Canvas* canvas) { 113 void RenderTextWin::Draw(Canvas* canvas) {
173 DrawSelection(canvas); 114 DrawSelection(canvas);
174 DrawVisualText(canvas); 115 DrawVisualText(canvas);
(...skipping 229 matching lines...) Expand 10 before | Expand all | Expand 10 after
404 STLDeleteContainerPointers(runs_.begin(), runs_.end()); 345 STLDeleteContainerPointers(runs_.begin(), runs_.end());
405 runs_.clear(); 346 runs_.clear();
406 if (text().empty()) 347 if (text().empty())
407 return; 348 return;
408 349
409 const wchar_t* raw_text = text().c_str(); 350 const wchar_t* raw_text = text().c_str();
410 const int text_length = text().length(); 351 const int text_length = text().length();
411 352
412 HRESULT hr = E_OUTOFMEMORY; 353 HRESULT hr = E_OUTOFMEMORY;
413 int script_items_count = 0; 354 int script_items_count = 0;
414 std::vector<SCRIPT_ITEM> script_items; 355 scoped_array<SCRIPT_ITEM> script_items;
415 for (size_t n = kGuessItems; hr == E_OUTOFMEMORY && n < kMaxItems; n *= 2) { 356 for (size_t n = kGuessItems; hr == E_OUTOFMEMORY && n < kMaxItems; n *= 2) {
416 // Derive the array of Uniscribe script items from the logical text. 357 // Derive the array of Uniscribe script items from the logical text.
417 // ScriptItemize always adds a terminal array item so that the length of the 358 // ScriptItemize always adds a terminal array item so that the length of the
418 // last item can be derived from the terminal SCRIPT_ITEM::iCharPos. 359 // last item can be derived from the terminal SCRIPT_ITEM::iCharPos.
419 script_items.resize(n); 360 script_items.reset(new SCRIPT_ITEM[n]);
420 hr = ScriptItemize(raw_text, 361 hr = ScriptItemize(raw_text,
421 text_length, 362 text_length,
422 n - 1, 363 n - 1,
423 &script_control_, 364 &script_control_,
424 &script_state_, 365 &script_state_,
425 &script_items[0], 366 script_items.get(),
426 &script_items_count); 367 &script_items_count);
427 } 368 }
428 DCHECK(SUCCEEDED(hr)); 369 DCHECK(SUCCEEDED(hr));
429 370
430 if (script_items_count <= 0) 371 if (script_items_count <= 0)
431 return; 372 return;
432 373
433 // Build the list of runs, merge font/underline styles. 374 // Build the list of runs, merge font/underline styles.
434 // TODO(msw): Only break for font changes, not color etc. See TextRun comment. 375 // TODO(msw): Only break for font changes, not color etc. See TextRun comment.
435 // TODO(msw): Apply the overriding selection and composition styles. 376 // TODO(msw): Apply the overriding selection and composition styles.
436 StyleRanges::const_iterator style = style_ranges().begin(); 377 StyleRanges::const_iterator style = style_ranges().begin();
437 SCRIPT_ITEM* script_item = &script_items[0]; 378 SCRIPT_ITEM* script_item = script_items.get();
438 for (int run_break = 0; run_break < text_length;) { 379 for (int run_break = 0; run_break < text_length;) {
439 internal::TextRun* run = new internal::TextRun(); 380 internal::TextRun* run = new internal::TextRun();
440 run->range.set_start(run_break); 381 run->range.set_start(run_break);
441 run->font = style->font; 382 run->font = style->font;
442 run->foreground = style->foreground; 383 run->foreground = style->foreground;
443 run->strike = style->strike; 384 run->strike = style->strike;
444 run->underline = style->underline; 385 run->underline = style->underline;
445 run->script_analysis = script_item->a; 386 run->script_analysis = script_item->a;
446 387
447 // Find the range end and advance the structures as needed. 388 // Find the range end and advance the structures as needed.
(...skipping 22 matching lines...) Expand all
470 SelectObject(hdc, run->font.GetNativeFont()); 411 SelectObject(hdc, run->font.GetNativeFont());
471 412
472 run->logical_clusters.reset(new WORD[run_length]); 413 run->logical_clusters.reset(new WORD[run_length]);
473 run->glyph_count = 0; 414 run->glyph_count = 0;
474 // Max glyph guess: http://msdn.microsoft.com/en-us/library/dd368564.aspx 415 // Max glyph guess: http://msdn.microsoft.com/en-us/library/dd368564.aspx
475 size_t max_glyphs = static_cast<size_t>(1.5 * run_length + 16); 416 size_t max_glyphs = static_cast<size_t>(1.5 * run_length + 16);
476 while (max_glyphs < kMaxGlyphs) { 417 while (max_glyphs < kMaxGlyphs) {
477 run->glyphs.reset(new WORD[max_glyphs]); 418 run->glyphs.reset(new WORD[max_glyphs]);
478 run->visible_attributes.reset(new SCRIPT_VISATTR[max_glyphs]); 419 run->visible_attributes.reset(new SCRIPT_VISATTR[max_glyphs]);
479 hr = ScriptShape(hdc, 420 hr = ScriptShape(hdc,
480 &run->script_cache, 421 &script_cache_,
481 run_text, 422 run_text,
482 run_length, 423 run_length,
483 max_glyphs, 424 max_glyphs,
484 &(run->script_analysis), 425 &(run->script_analysis),
485 run->glyphs.get(), 426 run->glyphs.get(),
486 run->logical_clusters.get(), 427 run->logical_clusters.get(),
487 run->visible_attributes.get(), 428 run->visible_attributes.get(),
488 &(run->glyph_count)); 429 &(run->glyph_count));
489 if (hr == E_OUTOFMEMORY) { 430 if (hr == E_OUTOFMEMORY) {
490 max_glyphs *= 2; 431 max_glyphs *= 2;
491 } else if (hr == USP_E_SCRIPT_NOT_IN_FONT) { 432 } else if (hr == USP_E_SCRIPT_NOT_IN_FONT) {
492 // TODO(msw): Don't use SCRIPT_UNDEFINED. Apparently Uniscribe can crash 433 // The run's font doesn't contain the required glyphs, use an alternate.
493 // on certain surrogate pairs when using SCRIPT_UNDEFINED. 434 // TODO(msw): Font fallback... Don't use SCRIPT_UNDEFINED.
494 // See https://bugzilla.mozilla.org/show_bug.cgi?id=341500 435 // See https://bugzilla.mozilla.org/show_bug.cgi?id=341500
495 // And http://maxradi.us/documents/uniscribe/ 436 // And http://maxradi.us/documents/uniscribe/
496 if (run->script_analysis.eScript == SCRIPT_UNDEFINED) 437 if (run->script_analysis.eScript == SCRIPT_UNDEFINED)
497 break; 438 break;
498
499 // The run's font doesn't contain the required glyphs, use an alternate.
500 if (ChooseFallbackFont(hdc, run->font, run_text, run_length,
501 &run->font)) {
502 ScriptFreeCache(&run->script_cache);
503 SelectObject(hdc, run->font.GetNativeFont());
504 }
505
506 run->script_analysis.eScript = SCRIPT_UNDEFINED; 439 run->script_analysis.eScript = SCRIPT_UNDEFINED;
507 } else { 440 } else {
508 break; 441 break;
509 } 442 }
510 } 443 }
511 DCHECK(SUCCEEDED(hr)); 444 DCHECK(SUCCEEDED(hr));
512 445
513 if (run->glyph_count > 0) { 446 if (run->glyph_count > 0) {
514 run->advance_widths.reset(new int[run->glyph_count]); 447 run->advance_widths.reset(new int[run->glyph_count]);
515 run->offsets.reset(new GOFFSET[run->glyph_count]); 448 run->offsets.reset(new GOFFSET[run->glyph_count]);
516 hr = ScriptPlace(hdc, 449 hr = ScriptPlace(hdc,
517 &run->script_cache, 450 &script_cache_,
518 run->glyphs.get(), 451 run->glyphs.get(),
519 run->glyph_count, 452 run->glyph_count,
520 run->visible_attributes.get(), 453 run->visible_attributes.get(),
521 &(run->script_analysis), 454 &(run->script_analysis),
522 run->advance_widths.get(), 455 run->advance_widths.get(),
523 run->offsets.get(), 456 run->offsets.get(),
524 &(run->abc_widths)); 457 &(run->abc_widths));
525 DCHECK(SUCCEEDED(hr)); 458 DCHECK(SUCCEEDED(hr));
526 } 459 }
527 } 460 }
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
567 size_t RenderTextWin::GetRunContainingPoint(const Point& point) const { 500 size_t RenderTextWin::GetRunContainingPoint(const Point& point) const {
568 // Find the text run containing the argument point (assumed already offset). 501 // Find the text run containing the argument point (assumed already offset).
569 size_t run = 0; 502 size_t run = 0;
570 for (; run < runs_.size(); ++run) 503 for (; run < runs_.size(); ++run)
571 if (runs_[run]->preceding_run_widths <= point.x() && 504 if (runs_[run]->preceding_run_widths <= point.x() &&
572 runs_[run]->preceding_run_widths + runs_[run]->width > point.x()) 505 runs_[run]->preceding_run_widths + runs_[run]->width > point.x())
573 break; 506 break;
574 return run; 507 return run;
575 } 508 }
576 509
510
577 SelectionModel RenderTextWin::FirstSelectionModelInsideRun( 511 SelectionModel RenderTextWin::FirstSelectionModelInsideRun(
578 internal::TextRun* run) { 512 internal::TextRun* run) {
579 size_t caret = run->range.start(); 513 size_t caret = run->range.start();
580 size_t cursor = IndexOfAdjacentGrapheme(caret, true); 514 size_t cursor = IndexOfAdjacentGrapheme(caret, true);
581 return SelectionModel(cursor, caret, SelectionModel::TRAILING); 515 return SelectionModel(cursor, caret, SelectionModel::TRAILING);
582 } 516 }
583 517
584 SelectionModel RenderTextWin::LastSelectionModelInsideRun( 518 SelectionModel RenderTextWin::LastSelectionModelInsideRun(
585 internal::TextRun* run) { 519 internal::TextRun* run) {
586 size_t caret = IndexOfAdjacentGrapheme(run->range.end(), false); 520 size_t caret = IndexOfAdjacentGrapheme(run->range.end(), false);
(...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after
733 Rect r(GetUpdatedCursorBounds()); 667 Rect r(GetUpdatedCursorBounds());
734 canvas->DrawRectInt(kCursorColor, r.x(), r.y(), r.width(), r.height()); 668 canvas->DrawRectInt(kCursorColor, r.x(), r.y(), r.width(), r.height());
735 } 669 }
736 } 670 }
737 671
738 RenderText* RenderText::CreateRenderText() { 672 RenderText* RenderText::CreateRenderText() {
739 return new RenderTextWin; 673 return new RenderTextWin;
740 } 674 }
741 675
742 } // namespace gfx 676 } // namespace gfx
OLDNEW
« no previous file with comments | « ui/gfx/render_text_win.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698