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

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

Issue 8565011: 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')
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>
9 8
10 #include "base/logging.h" 9 #include "base/logging.h"
11 #include "base/stl_util.h" 10 #include "base/stl_util.h"
12 #include "base/string_util.h" 11 #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"
17 18
18 namespace { 19 namespace {
19 20
20 // The maximum supported number of Uniscribe runs; a SCRIPT_ITEM is 8 bytes. 21 // The maximum supported number of Uniscribe runs; a SCRIPT_ITEM is 8 bytes.
21 // TODO(msw): Review memory use/failure? Max string length? Alternate approach? 22 // TODO(msw): Review memory use/failure? Max string length? Alternate approach?
22 const int kGuessItems = 100; 23 const int kGuessItems = 100;
23 const int kMaxItems = 10000; 24 const int kMaxItems = 10000;
24 25
25 // The maximum supported number of Uniscribe glyphs; a glyph is 1 word. 26 // The maximum supported number of Uniscribe glyphs; a glyph is 1 word.
26 // TODO(msw): Review memory use/failure? Max string length? Alternate approach? 27 // TODO(msw): Review memory use/failure? Max string length? Alternate approach?
(...skipping 28 matching lines...) Expand all
55 canvas_skia->drawRect(r, paint); 56 canvas_skia->drawRect(r, paint);
56 } 57 }
57 if (run.strike) { 58 if (run.strike) {
58 SkScalar offset = SkScalarMulAdd(text_size, kStrikeThroughOffset, y); 59 SkScalar offset = SkScalarMulAdd(text_size, kStrikeThroughOffset, y);
59 r.fTop = offset; 60 r.fTop = offset;
60 r.fBottom = offset + height; 61 r.fBottom = offset + height;
61 canvas_skia->drawRect(r, paint); 62 canvas_skia->drawRect(r, paint);
62 } 63 }
63 } 64 }
64 65
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 LOGFONT* font = reinterpret_cast<LOGFONT*>(log_font);
74 const EMREXTCREATEFONTINDIRECTW* create_font_record
75 = reinterpret_cast<const EMREXTCREATEFONTINDIRECTW*>(record);
sky 2011/11/14 16:30:41 = on previous line.
Alexei Svitkine (slow) 2011/11/14 16:41:38 Done.
76 *font = create_font_record->elfw.elfLogFont;
77 }
78 return 1;
79 }
80
81 // Finds a fallback font to use to render the specified |text| with respect to
82 // an initial |font|. Returns the resulting font via out param |result|. Returns
83 // |true| if a fallback font was found.
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
65 } // namespace 123 } // namespace
66 124
67 namespace gfx { 125 namespace gfx {
68 126
69 namespace internal { 127 namespace internal {
70 128
71 TextRun::TextRun() 129 TextRun::TextRun()
72 : strike(false), 130 : strike(false),
73 underline(false), 131 underline(false),
74 width(0), 132 width(0),
75 preceding_run_widths(0), 133 preceding_run_widths(0),
76 glyph_count(0) { 134 glyph_count(0),
135 script_cache(NULL) {
77 } 136 }
78 137
79 } // namespace internal 138 } // namespace internal
80 139
81 RenderTextWin::RenderTextWin() 140 RenderTextWin::RenderTextWin()
82 : RenderText(), 141 : RenderText(),
83 script_control_(), 142 script_control_(),
84 script_state_(), 143 script_state_(),
85 script_cache_(NULL),
86 string_width_(0) { 144 string_width_(0) {
87 // Omitting default constructors for script_* would leave POD uninitialized. 145 // Omitting default constructors for script_* would leave POD uninitialized.
88 HRESULT hr = 0; 146 HRESULT hr = 0;
89 147
90 // TODO(msw): Call ScriptRecordDigitSubstitution on WM_SETTINGCHANGE message. 148 // TODO(msw): Call ScriptRecordDigitSubstitution on WM_SETTINGCHANGE message.
91 // TODO(msw): Use Chrome/profile locale/language settings? 149 // TODO(msw): Use Chrome/profile locale/language settings?
92 hr = ScriptRecordDigitSubstitution(LOCALE_USER_DEFAULT, &digit_substitute_); 150 hr = ScriptRecordDigitSubstitution(LOCALE_USER_DEFAULT, &digit_substitute_);
93 DCHECK(SUCCEEDED(hr)); 151 DCHECK(SUCCEEDED(hr));
94 152
95 hr = ScriptApplyDigitSubstitution(&digit_substitute_, 153 hr = ScriptApplyDigitSubstitution(&digit_substitute_,
96 &script_control_, 154 &script_control_,
97 &script_state_); 155 &script_state_);
98 DCHECK(SUCCEEDED(hr)); 156 DCHECK(SUCCEEDED(hr));
99 script_control_.fMergeNeutralItems = true; 157 script_control_.fMergeNeutralItems = true;
100 158
101 MoveCursorTo(LeftEndSelectionModel()); 159 MoveCursorTo(LeftEndSelectionModel());
102 } 160 }
103 161
104 RenderTextWin::~RenderTextWin() { 162 RenderTextWin::~RenderTextWin() {
105 ScriptFreeCache(&script_cache_); 163 for (size_t i = 0; i < runs_.size(); ++i)
164 ScriptFreeCache(&runs_[i]->script_cache);
106 STLDeleteContainerPointers(runs_.begin(), runs_.end()); 165 STLDeleteContainerPointers(runs_.begin(), runs_.end());
107 } 166 }
108 167
109 int RenderTextWin::GetStringWidth() { 168 int RenderTextWin::GetStringWidth() {
110 return string_width_; 169 return string_width_;
111 } 170 }
112 171
113 void RenderTextWin::Draw(Canvas* canvas) { 172 void RenderTextWin::Draw(Canvas* canvas) {
114 DrawSelection(canvas); 173 DrawSelection(canvas);
115 DrawVisualText(canvas); 174 DrawVisualText(canvas);
(...skipping 229 matching lines...) Expand 10 before | Expand all | Expand 10 after
345 STLDeleteContainerPointers(runs_.begin(), runs_.end()); 404 STLDeleteContainerPointers(runs_.begin(), runs_.end());
346 runs_.clear(); 405 runs_.clear();
347 if (text().empty()) 406 if (text().empty())
348 return; 407 return;
349 408
350 const wchar_t* raw_text = text().c_str(); 409 const wchar_t* raw_text = text().c_str();
351 const int text_length = text().length(); 410 const int text_length = text().length();
352 411
353 HRESULT hr = E_OUTOFMEMORY; 412 HRESULT hr = E_OUTOFMEMORY;
354 int script_items_count = 0; 413 int script_items_count = 0;
355 scoped_array<SCRIPT_ITEM> script_items; 414 std::vector<SCRIPT_ITEM> script_items;
356 for (size_t n = kGuessItems; hr == E_OUTOFMEMORY && n < kMaxItems; n *= 2) { 415 for (size_t n = kGuessItems; hr == E_OUTOFMEMORY && n < kMaxItems; n *= 2) {
357 // Derive the array of Uniscribe script items from the logical text. 416 // Derive the array of Uniscribe script items from the logical text.
358 // ScriptItemize always adds a terminal array item so that the length of the 417 // ScriptItemize always adds a terminal array item so that the length of the
359 // last item can be derived from the terminal SCRIPT_ITEM::iCharPos. 418 // last item can be derived from the terminal SCRIPT_ITEM::iCharPos.
360 script_items.reset(new SCRIPT_ITEM[n]); 419 script_items.resize(n);
361 hr = ScriptItemize(raw_text, 420 hr = ScriptItemize(raw_text,
362 text_length, 421 text_length,
363 n - 1, 422 n - 1,
364 &script_control_, 423 &script_control_,
365 &script_state_, 424 &script_state_,
366 script_items.get(), 425 &script_items[0],
367 &script_items_count); 426 &script_items_count);
368 } 427 }
369 DCHECK(SUCCEEDED(hr)); 428 DCHECK(SUCCEEDED(hr));
370 429
371 if (script_items_count <= 0) 430 if (script_items_count <= 0)
372 return; 431 return;
373 432
374 // Build the list of runs, merge font/underline styles. 433 // Build the list of runs, merge font/underline styles.
375 // TODO(msw): Only break for font changes, not color etc. See TextRun comment. 434 // TODO(msw): Only break for font changes, not color etc. See TextRun comment.
376 // TODO(msw): Apply the overriding selection and composition styles. 435 // TODO(msw): Apply the overriding selection and composition styles.
377 StyleRanges::const_iterator style = style_ranges().begin(); 436 StyleRanges::const_iterator style = style_ranges().begin();
378 SCRIPT_ITEM* script_item = script_items.get(); 437 SCRIPT_ITEM* script_item = &script_items[0];
379 for (int run_break = 0; run_break < text_length;) { 438 for (int run_break = 0; run_break < text_length;) {
380 internal::TextRun* run = new internal::TextRun(); 439 internal::TextRun* run = new internal::TextRun();
381 run->range.set_start(run_break); 440 run->range.set_start(run_break);
382 run->font = style->font; 441 run->font = style->font;
383 run->foreground = style->foreground; 442 run->foreground = style->foreground;
384 run->strike = style->strike; 443 run->strike = style->strike;
385 run->underline = style->underline; 444 run->underline = style->underline;
386 run->script_analysis = script_item->a; 445 run->script_analysis = script_item->a;
387 446
388 // Find the range end and advance the structures as needed. 447 // Find the range end and advance the structures as needed.
(...skipping 22 matching lines...) Expand all
411 SelectObject(hdc, run->font.GetNativeFont()); 470 SelectObject(hdc, run->font.GetNativeFont());
412 471
413 run->logical_clusters.reset(new WORD[run_length]); 472 run->logical_clusters.reset(new WORD[run_length]);
414 run->glyph_count = 0; 473 run->glyph_count = 0;
415 // Max glyph guess: http://msdn.microsoft.com/en-us/library/dd368564.aspx 474 // Max glyph guess: http://msdn.microsoft.com/en-us/library/dd368564.aspx
416 size_t max_glyphs = static_cast<size_t>(1.5 * run_length + 16); 475 size_t max_glyphs = static_cast<size_t>(1.5 * run_length + 16);
417 while (max_glyphs < kMaxGlyphs) { 476 while (max_glyphs < kMaxGlyphs) {
418 run->glyphs.reset(new WORD[max_glyphs]); 477 run->glyphs.reset(new WORD[max_glyphs]);
419 run->visible_attributes.reset(new SCRIPT_VISATTR[max_glyphs]); 478 run->visible_attributes.reset(new SCRIPT_VISATTR[max_glyphs]);
420 hr = ScriptShape(hdc, 479 hr = ScriptShape(hdc,
421 &script_cache_, 480 &run->script_cache,
422 run_text, 481 run_text,
423 run_length, 482 run_length,
424 max_glyphs, 483 max_glyphs,
425 &(run->script_analysis), 484 &(run->script_analysis),
426 run->glyphs.get(), 485 run->glyphs.get(),
427 run->logical_clusters.get(), 486 run->logical_clusters.get(),
428 run->visible_attributes.get(), 487 run->visible_attributes.get(),
429 &(run->glyph_count)); 488 &(run->glyph_count));
430 if (hr == E_OUTOFMEMORY) { 489 if (hr == E_OUTOFMEMORY) {
431 max_glyphs *= 2; 490 max_glyphs *= 2;
432 } else if (hr == USP_E_SCRIPT_NOT_IN_FONT) { 491 } else if (hr == USP_E_SCRIPT_NOT_IN_FONT) {
433 // The run's font doesn't contain the required glyphs, use an alternate.
434 // TODO(msw): Font fallback... Don't use SCRIPT_UNDEFINED.
435 // See https://bugzilla.mozilla.org/show_bug.cgi?id=341500
436 // And http://maxradi.us/documents/uniscribe/
437 if (run->script_analysis.eScript == SCRIPT_UNDEFINED) 492 if (run->script_analysis.eScript == SCRIPT_UNDEFINED)
438 break; 493 break;
494
495 // The run's font doesn't contain the required glyphs, use an alternate.
496 if (ChooseFallbackFont(hdc, run->font, run_text, run_length,
497 &run->font)) {
498 ScriptFreeCache(&run->script_cache);
499 SelectObject(hdc, run->font.GetNativeFont());
sky 2011/11/14 16:30:41 Do we need to worry about selecting the old font o
Alexei Svitkine (slow) 2011/11/14 16:41:38 It's not an issue since this is a temporary dc.
500 }
501
439 run->script_analysis.eScript = SCRIPT_UNDEFINED; 502 run->script_analysis.eScript = SCRIPT_UNDEFINED;
440 } else { 503 } else {
441 break; 504 break;
442 } 505 }
443 } 506 }
444 DCHECK(SUCCEEDED(hr)); 507 DCHECK(SUCCEEDED(hr));
445 508
446 if (run->glyph_count > 0) { 509 if (run->glyph_count > 0) {
447 run->advance_widths.reset(new int[run->glyph_count]); 510 run->advance_widths.reset(new int[run->glyph_count]);
448 run->offsets.reset(new GOFFSET[run->glyph_count]); 511 run->offsets.reset(new GOFFSET[run->glyph_count]);
449 hr = ScriptPlace(hdc, 512 hr = ScriptPlace(hdc,
450 &script_cache_, 513 &run->script_cache,
451 run->glyphs.get(), 514 run->glyphs.get(),
452 run->glyph_count, 515 run->glyph_count,
453 run->visible_attributes.get(), 516 run->visible_attributes.get(),
454 &(run->script_analysis), 517 &(run->script_analysis),
455 run->advance_widths.get(), 518 run->advance_widths.get(),
456 run->offsets.get(), 519 run->offsets.get(),
457 &(run->abc_widths)); 520 &(run->abc_widths));
458 DCHECK(SUCCEEDED(hr)); 521 DCHECK(SUCCEEDED(hr));
459 } 522 }
460 } 523 }
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
500 size_t RenderTextWin::GetRunContainingPoint(const Point& point) const { 563 size_t RenderTextWin::GetRunContainingPoint(const Point& point) const {
501 // Find the text run containing the argument point (assumed already offset). 564 // Find the text run containing the argument point (assumed already offset).
502 size_t run = 0; 565 size_t run = 0;
503 for (; run < runs_.size(); ++run) 566 for (; run < runs_.size(); ++run)
504 if (runs_[run]->preceding_run_widths <= point.x() && 567 if (runs_[run]->preceding_run_widths <= point.x() &&
505 runs_[run]->preceding_run_widths + runs_[run]->width > point.x()) 568 runs_[run]->preceding_run_widths + runs_[run]->width > point.x())
506 break; 569 break;
507 return run; 570 return run;
508 } 571 }
509 572
510
511 SelectionModel RenderTextWin::FirstSelectionModelInsideRun( 573 SelectionModel RenderTextWin::FirstSelectionModelInsideRun(
512 internal::TextRun* run) { 574 internal::TextRun* run) {
513 size_t caret = run->range.start(); 575 size_t caret = run->range.start();
514 size_t cursor = IndexOfAdjacentGrapheme(caret, true); 576 size_t cursor = IndexOfAdjacentGrapheme(caret, true);
515 return SelectionModel(cursor, caret, SelectionModel::TRAILING); 577 return SelectionModel(cursor, caret, SelectionModel::TRAILING);
516 } 578 }
517 579
518 SelectionModel RenderTextWin::LastSelectionModelInsideRun( 580 SelectionModel RenderTextWin::LastSelectionModelInsideRun(
519 internal::TextRun* run) { 581 internal::TextRun* run) {
520 size_t caret = IndexOfAdjacentGrapheme(run->range.end(), false); 582 size_t caret = IndexOfAdjacentGrapheme(run->range.end(), false);
(...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after
667 Rect r(GetUpdatedCursorBounds()); 729 Rect r(GetUpdatedCursorBounds());
668 canvas->DrawRectInt(kCursorColor, r.x(), r.y(), r.width(), r.height()); 730 canvas->DrawRectInt(kCursorColor, r.x(), r.y(), r.width(), r.height());
669 } 731 }
670 } 732 }
671 733
672 RenderText* RenderText::CreateRenderText() { 734 RenderText* RenderText::CreateRenderText() {
673 return new RenderTextWin; 735 return new RenderTextWin;
674 } 736 }
675 737
676 } // namespace gfx 738 } // 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