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

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

Issue 8575020: Improve RenderTextWin font fallback. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 9 years 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
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 8
9 #include "base/logging.h" 9 #include "base/logging.h"
10 #include "base/stl_util.h" 10 #include "base/stl_util.h"
(...skipping 273 matching lines...) Expand 10 before | Expand all | Expand 10 after
284 return RenderText::GetRightSelectionModel(selection, break_type); 284 return RenderText::GetRightSelectionModel(selection, break_type);
285 } 285 }
286 286
287 SelectionModel RenderTextWin::LeftEndSelectionModel() { 287 SelectionModel RenderTextWin::LeftEndSelectionModel() {
288 if (text().empty()) 288 if (text().empty())
289 return SelectionModel(0, 0, SelectionModel::LEADING); 289 return SelectionModel(0, 0, SelectionModel::LEADING);
290 290
291 EnsureLayout(); 291 EnsureLayout();
292 size_t cursor = base::i18n::IsRTL() ? text().length() : 0; 292 size_t cursor = base::i18n::IsRTL() ? text().length() : 0;
293 internal::TextRun* run = runs_[visual_to_logical_[0]]; 293 internal::TextRun* run = runs_[visual_to_logical_[0]];
294 bool rtl = run->script_analysis.fRTL; 294 size_t caret;
295 size_t caret = rtl ? run->range.end() - 1 : run->range.start(); 295 SelectionModel::CaretPlacement placement;
296 SelectionModel::CaretPlacement placement = 296 if (run->script_analysis.fRTL) {
297 rtl ? SelectionModel::TRAILING : SelectionModel::LEADING; 297 caret = IndexOfAdjacentGrapheme(run->range.end(), false);
298 placement = SelectionModel::TRAILING;
299 } else {
300 caret = run->range.start();
301 placement = SelectionModel::LEADING;
302 }
298 return SelectionModel(cursor, caret, placement); 303 return SelectionModel(cursor, caret, placement);
299 } 304 }
300 305
301 SelectionModel RenderTextWin::RightEndSelectionModel() { 306 SelectionModel RenderTextWin::RightEndSelectionModel() {
302 if (text().empty()) 307 if (text().empty())
303 return SelectionModel(0, 0, SelectionModel::LEADING); 308 return SelectionModel(0, 0, SelectionModel::LEADING);
304 309
305 EnsureLayout(); 310 EnsureLayout();
306 size_t cursor = base::i18n::IsRTL() ? 0 : text().length(); 311 size_t cursor = base::i18n::IsRTL() ? 0 : text().length();
307 internal::TextRun* run = runs_[visual_to_logical_[runs_.size() - 1]]; 312 internal::TextRun* run = runs_[visual_to_logical_[runs_.size() - 1]];
308 bool rtl = run->script_analysis.fRTL; 313 size_t caret;
309 size_t caret = rtl ? run->range.start() : run->range.end() - 1; 314 SelectionModel::CaretPlacement placement;
310 SelectionModel::CaretPlacement placement = 315 if (run->script_analysis.fRTL) {
311 rtl ? SelectionModel::LEADING : SelectionModel::TRAILING; 316 caret = run->range.start();
317 placement = SelectionModel::LEADING;
318 } else {
319 caret = IndexOfAdjacentGrapheme(run->range.end(), false);
320 placement = SelectionModel::TRAILING;
321 }
312 return SelectionModel(cursor, caret, placement); 322 return SelectionModel(cursor, caret, placement);
313 } 323 }
314 324
315 void RenderTextWin::GetSubstringBounds(size_t from, 325 void RenderTextWin::GetSubstringBounds(size_t from,
316 size_t to, 326 size_t to,
317 std::vector<Rect>* bounds) { 327 std::vector<Rect>* bounds) {
318 DCHECK(!needs_layout_); 328 DCHECK(!needs_layout_);
319 ui::Range range(from, to); 329 ui::Range range(from, to);
320 DCHECK(ui::Range(0, text().length()).Contains(range)); 330 DCHECK(ui::Range(0, text().length()).Contains(range));
321 Point display_offset(GetUpdatedDisplayOffset()); 331 Point display_offset(GetUpdatedDisplayOffset());
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after
454 size_t byte_length = run->glyph_count * sizeof(WORD); 464 size_t byte_length = run->glyph_count * sizeof(WORD);
455 canvas_skia->drawPosText(run->glyphs.get(), byte_length, &pos[0], paint); 465 canvas_skia->drawPosText(run->glyphs.get(), byte_length, &pos[0], paint);
456 466
457 if (run->strike || run->underline) 467 if (run->strike || run->underline)
458 DrawTextRunDecorations(canvas_skia, paint, *run, run_x, y); 468 DrawTextRunDecorations(canvas_skia, paint, *run, run_x, y);
459 } 469 }
460 } 470 }
461 471
462 size_t RenderTextWin::IndexOfAdjacentGrapheme(size_t index, bool next) { 472 size_t RenderTextWin::IndexOfAdjacentGrapheme(size_t index, bool next) {
463 EnsureLayout(); 473 EnsureLayout();
474
475 if (text().empty())
476 return 0;
477
478 if (index >= text().length()) {
479 if (next || index > text().length()) {
480 return text().length();
481 } else {
482 // If the requested |index| is past the end, use the index of the last
xji 2011/12/01 20:19:15 is the comment apply? till here, "index <= text()
Alexei Svitkine (slow) 2011/12/01 20:58:58 Good catch, the comment meant to say is "at the en
483 // character to find the grapheme.
484 index = text().length() - 1;
485 if (IsCursorablePosition(index))
486 return index;
487 }
488 }
489
464 size_t run_index = GetRunContainingPosition(index); 490 size_t run_index = GetRunContainingPosition(index);
465 internal::TextRun* run = run_index < runs_.size() ? runs_[run_index] : NULL; 491 DCHECK(run_index < runs_.size());
466 int start = run ? run->range.start() : 0; 492 internal::TextRun* run = runs_[run_index];
467 int length = run ? run->range.length() : text().length(); 493 int start = run->range.start();
494 int length = run->range.length();
468 int ch = index - start; 495 int ch = index - start;
469 WORD cluster = run ? run->logical_clusters[ch] : 0; 496 WORD cluster = run->logical_clusters[ch];
470 497
471 if (!next) { 498 if (!next) {
472 do { 499 // If |ch| is the start of the run, use the preceding run, if any.
500 if (ch == 0) {
501 if (run_index == 0)
502 return 0;
503 run = runs_[run_index - 1];
504 start = run->range.start();
xji 2011/12/01 20:19:15 you did not seem to use the 'start'.
Alexei Svitkine (slow) 2011/12/01 20:58:58 It's used at the return point. Also, just keeping
505 length = run->range.length();
506 ch = length - 1;
507 } else if (run->logical_clusters[ch - 1] != run->logical_clusters[ch]) {
xji 2011/12/01 20:19:15 So, the old code does not handle 'ch==0' correctly
Alexei Svitkine (slow) 2011/12/01 20:58:58 Correct. This is covered by the unit test.
508 // If |ch| is at a grapheme boundary, use the previous grapheme.
473 ch--; 509 ch--;
474 } while (ch >= 0 && run && run->logical_clusters[ch] == cluster); 510 }
511
512 // Find the start of the grapheme.
513 while (ch > 0 && run->logical_clusters[ch - 1] == run->logical_clusters[ch])
xji 2011/12/01 20:19:15 Is the logic correct here? for example: ABCDE, wh
Alexei Svitkine (slow) 2011/12/01 20:58:58 Let me change your example to ABCD|e|f, since ABCD
xji 2011/12/01 22:56:06 Ah, my bad, mis-matched the index and its characte
514 ch--;
475 } else { 515 } else {
476 while (ch < length && run && run->logical_clusters[ch] == cluster) 516 while (ch < length && run->logical_clusters[ch] == cluster)
477 ch++; 517 ch++;
478 } 518 }
519
479 return std::max(std::min(ch, length) + start, 0); 520 return std::max(std::min(ch, length) + start, 0);
480 } 521 }
481 522
482 void RenderTextWin::ItemizeLogicalText() { 523 void RenderTextWin::ItemizeLogicalText() {
483 STLDeleteContainerPointers(runs_.begin(), runs_.end()); 524 STLDeleteContainerPointers(runs_.begin(), runs_.end());
484 runs_.clear(); 525 runs_.clear();
485 string_width_ = 0; 526 string_width_ = 0;
486 if (text().empty()) 527 if (text().empty())
487 return; 528 return;
488 529
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
538 } 579 }
539 580
540 void RenderTextWin::LayoutVisualText() { 581 void RenderTextWin::LayoutVisualText() {
541 HRESULT hr = E_FAIL; 582 HRESULT hr = E_FAIL;
542 base::win::ScopedCreateDC hdc(CreateCompatibleDC(NULL)); 583 base::win::ScopedCreateDC hdc(CreateCompatibleDC(NULL));
543 std::vector<internal::TextRun*>::const_iterator run_iter; 584 std::vector<internal::TextRun*>::const_iterator run_iter;
544 for (run_iter = runs_.begin(); run_iter < runs_.end(); ++run_iter) { 585 for (run_iter = runs_.begin(); run_iter < runs_.end(); ++run_iter) {
545 internal::TextRun* run = *run_iter; 586 internal::TextRun* run = *run_iter;
546 size_t run_length = run->range.length(); 587 size_t run_length = run->range.length();
547 const wchar_t* run_text = &(text()[run->range.start()]); 588 const wchar_t* run_text = &(text()[run->range.start()]);
589 bool tried_fallback = false;
548 590
549 // Select the font desired for glyph generation. 591 // Select the font desired for glyph generation.
550 SelectObject(hdc, run->font.GetNativeFont()); 592 SelectObject(hdc, run->font.GetNativeFont());
551 593
552 run->logical_clusters.reset(new WORD[run_length]); 594 run->logical_clusters.reset(new WORD[run_length]);
553 run->glyph_count = 0; 595 run->glyph_count = 0;
554 // Max glyph guess: http://msdn.microsoft.com/en-us/library/dd368564.aspx 596 // Max glyph guess: http://msdn.microsoft.com/en-us/library/dd368564.aspx
555 size_t max_glyphs = static_cast<size_t>(1.5 * run_length + 16); 597 size_t max_glyphs = static_cast<size_t>(1.5 * run_length + 16);
556 while (max_glyphs < kMaxGlyphs) { 598 while (max_glyphs < kMaxGlyphs) {
557 run->glyphs.reset(new WORD[max_glyphs]); 599 run->glyphs.reset(new WORD[max_glyphs]);
558 run->visible_attributes.reset(new SCRIPT_VISATTR[max_glyphs]); 600 run->visible_attributes.reset(new SCRIPT_VISATTR[max_glyphs]);
559 hr = ScriptShape(hdc, 601 hr = ScriptShape(hdc,
560 &run->script_cache, 602 &run->script_cache,
561 run_text, 603 run_text,
562 run_length, 604 run_length,
563 max_glyphs, 605 max_glyphs,
564 &(run->script_analysis), 606 &(run->script_analysis),
565 run->glyphs.get(), 607 run->glyphs.get(),
566 run->logical_clusters.get(), 608 run->logical_clusters.get(),
567 run->visible_attributes.get(), 609 run->visible_attributes.get(),
568 &(run->glyph_count)); 610 &(run->glyph_count));
569 if (hr == E_OUTOFMEMORY) { 611 if (hr == E_OUTOFMEMORY) {
570 max_glyphs *= 2; 612 max_glyphs *= 2;
571 } else if (hr == USP_E_SCRIPT_NOT_IN_FONT) { 613 } else if (hr == USP_E_SCRIPT_NOT_IN_FONT) {
572 // TODO(msw): Don't use SCRIPT_UNDEFINED. Apparently Uniscribe can crash 614 // Only try font fallback if it hasn't yet been attempted for this run.
573 // on certain surrogate pairs with SCRIPT_UNDEFINED. 615 if (tried_fallback) {
574 // See https://bugzilla.mozilla.org/show_bug.cgi?id=341500 616 // TODO(msw): Don't use SCRIPT_UNDEFINED. Apparently Uniscribe can
575 // And http://maxradi.us/documents/uniscribe/ 617 // crash on certain surrogate pairs with SCRIPT_UNDEFINED.
576 if (run->script_analysis.eScript == SCRIPT_UNDEFINED) 618 // See https://bugzilla.mozilla.org/show_bug.cgi?id=341500
577 break; 619 // And http://maxradi.us/documents/uniscribe/
620 run->script_analysis.eScript = SCRIPT_UNDEFINED;
621 break;
622 }
578 623
579 // The run's font doesn't contain the required glyphs, use an alternate. 624 // The run's font doesn't contain the required glyphs, use an alternate.
580 if (ChooseFallbackFont(hdc, run->font, run_text, run_length, 625 if (ChooseFallbackFont(hdc, run->font, run_text, run_length,
581 &run->font)) { 626 &run->font)) {
582 ScriptFreeCache(&run->script_cache); 627 ScriptFreeCache(&run->script_cache);
583 SelectObject(hdc, run->font.GetNativeFont()); 628 SelectObject(hdc, run->font.GetNativeFont());
584 } 629 }
585 630
586 run->script_analysis.eScript = SCRIPT_UNDEFINED; 631 tried_fallback = true;
587 } else { 632 } else {
588 break; 633 break;
589 } 634 }
590 } 635 }
591 DCHECK(SUCCEEDED(hr)); 636 DCHECK(SUCCEEDED(hr));
592 637
593 if (run->glyph_count > 0) { 638 if (run->glyph_count > 0) {
594 run->advance_widths.reset(new int[run->glyph_count]); 639 run->advance_widths.reset(new int[run->glyph_count]);
595 run->offsets.reset(new GOFFSET[run->glyph_count]); 640 run->offsets.reset(new GOFFSET[run->glyph_count]);
596 hr = ScriptPlace(hdc, 641 hr = ScriptPlace(hdc,
(...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after
741 internal::TextRun* next = runs_[visual_to_logical_[visual_index + 1]]; 786 internal::TextRun* next = runs_[visual_to_logical_[visual_index + 1]];
742 return next->script_analysis.fRTL ? LastSelectionModelInsideRun(next) : 787 return next->script_analysis.fRTL ? LastSelectionModelInsideRun(next) :
743 FirstSelectionModelInsideRun(next); 788 FirstSelectionModelInsideRun(next);
744 } 789 }
745 790
746 RenderText* RenderText::CreateRenderText() { 791 RenderText* RenderText::CreateRenderText() {
747 return new RenderTextWin; 792 return new RenderTextWin;
748 } 793 }
749 794
750 } // namespace gfx 795 } // namespace gfx
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698