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

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: Disabling more under XP. 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 // The requested |index| is at the end of the text. Use the index of the
483 // last 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 size_t start = run->range.start();
468 int ch = index - start; 494 size_t ch = index - start;
469 WORD cluster = run ? run->logical_clusters[ch] : 0;
470 495
471 if (!next) { 496 if (!next) {
497 // If |ch| is the start of the run, use the preceding run, if any.
498 if (ch == 0) {
499 if (run_index == 0)
500 return 0;
501 run = runs_[run_index - 1];
502 start = run->range.start();
503 ch = run->range.length();
504 }
505
506 // Loop to find the start of the grapheme.
507 WORD cluster = run->logical_clusters[ch - 1];
472 do { 508 do {
473 ch--; 509 ch--;
474 } while (ch >= 0 && run && run->logical_clusters[ch] == cluster); 510 } while (ch > 0 && run->logical_clusters[ch - 1] == cluster);
475 } else { 511 } else {
476 while (ch < length && run && run->logical_clusters[ch] == cluster) 512 WORD cluster = run->logical_clusters[ch];
513 while (ch < run->range.length() && run->logical_clusters[ch] == cluster)
477 ch++; 514 ch++;
478 } 515 }
479 return std::max(std::min(ch, length) + start, 0); 516
517 return start + ch;
480 } 518 }
481 519
482 void RenderTextWin::ItemizeLogicalText() { 520 void RenderTextWin::ItemizeLogicalText() {
483 STLDeleteContainerPointers(runs_.begin(), runs_.end()); 521 STLDeleteContainerPointers(runs_.begin(), runs_.end());
484 runs_.clear(); 522 runs_.clear();
485 string_width_ = 0; 523 string_width_ = 0;
486 if (text().empty()) 524 if (text().empty())
487 return; 525 return;
488 526
489 const wchar_t* raw_text = text().c_str(); 527 const wchar_t* raw_text = text().c_str();
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
538 } 576 }
539 577
540 void RenderTextWin::LayoutVisualText() { 578 void RenderTextWin::LayoutVisualText() {
541 HRESULT hr = E_FAIL; 579 HRESULT hr = E_FAIL;
542 base::win::ScopedCreateDC hdc(CreateCompatibleDC(NULL)); 580 base::win::ScopedCreateDC hdc(CreateCompatibleDC(NULL));
543 std::vector<internal::TextRun*>::const_iterator run_iter; 581 std::vector<internal::TextRun*>::const_iterator run_iter;
544 for (run_iter = runs_.begin(); run_iter < runs_.end(); ++run_iter) { 582 for (run_iter = runs_.begin(); run_iter < runs_.end(); ++run_iter) {
545 internal::TextRun* run = *run_iter; 583 internal::TextRun* run = *run_iter;
546 size_t run_length = run->range.length(); 584 size_t run_length = run->range.length();
547 const wchar_t* run_text = &(text()[run->range.start()]); 585 const wchar_t* run_text = &(text()[run->range.start()]);
586 bool tried_fallback = false;
548 587
549 // Select the font desired for glyph generation. 588 // Select the font desired for glyph generation.
550 SelectObject(hdc, run->font.GetNativeFont()); 589 SelectObject(hdc, run->font.GetNativeFont());
551 590
552 run->logical_clusters.reset(new WORD[run_length]); 591 run->logical_clusters.reset(new WORD[run_length]);
553 run->glyph_count = 0; 592 run->glyph_count = 0;
554 // Max glyph guess: http://msdn.microsoft.com/en-us/library/dd368564.aspx 593 // 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); 594 size_t max_glyphs = static_cast<size_t>(1.5 * run_length + 16);
556 while (max_glyphs < kMaxGlyphs) { 595 while (max_glyphs < kMaxGlyphs) {
557 run->glyphs.reset(new WORD[max_glyphs]); 596 run->glyphs.reset(new WORD[max_glyphs]);
558 run->visible_attributes.reset(new SCRIPT_VISATTR[max_glyphs]); 597 run->visible_attributes.reset(new SCRIPT_VISATTR[max_glyphs]);
559 hr = ScriptShape(hdc, 598 hr = ScriptShape(hdc,
560 &run->script_cache, 599 &run->script_cache,
561 run_text, 600 run_text,
562 run_length, 601 run_length,
563 max_glyphs, 602 max_glyphs,
564 &(run->script_analysis), 603 &(run->script_analysis),
565 run->glyphs.get(), 604 run->glyphs.get(),
566 run->logical_clusters.get(), 605 run->logical_clusters.get(),
567 run->visible_attributes.get(), 606 run->visible_attributes.get(),
568 &(run->glyph_count)); 607 &(run->glyph_count));
569 if (hr == E_OUTOFMEMORY) { 608 if (hr == E_OUTOFMEMORY) {
570 max_glyphs *= 2; 609 max_glyphs *= 2;
571 } else if (hr == USP_E_SCRIPT_NOT_IN_FONT) { 610 } else if (hr == USP_E_SCRIPT_NOT_IN_FONT) {
572 // TODO(msw): Don't use SCRIPT_UNDEFINED. Apparently Uniscribe can crash 611 // Only try font fallback if it hasn't yet been attempted for this run.
573 // on certain surrogate pairs with SCRIPT_UNDEFINED. 612 if (tried_fallback) {
574 // See https://bugzilla.mozilla.org/show_bug.cgi?id=341500 613 // TODO(msw): Don't use SCRIPT_UNDEFINED. Apparently Uniscribe can
575 // And http://maxradi.us/documents/uniscribe/ 614 // crash on certain surrogate pairs with SCRIPT_UNDEFINED.
576 if (run->script_analysis.eScript == SCRIPT_UNDEFINED) 615 // See https://bugzilla.mozilla.org/show_bug.cgi?id=341500
577 break; 616 // And http://maxradi.us/documents/uniscribe/
617 run->script_analysis.eScript = SCRIPT_UNDEFINED;
618 // Reset |hr| to 0 to not trigger the DCHECK() below when a font is
619 // not found that can display the text. This is expected behavior
620 // under Windows XP without additional language packs installed and
621 // may also happen on newer versions when trying to display text in
622 // an obscure script that the system doesn't have the right font for.
623 hr = 0;
624 break;
625 }
578 626
579 // The run's font doesn't contain the required glyphs, use an alternate. 627 // The run's font doesn't contain the required glyphs, use an alternate.
580 if (ChooseFallbackFont(hdc, run->font, run_text, run_length, 628 if (ChooseFallbackFont(hdc, run->font, run_text, run_length,
581 &run->font)) { 629 &run->font)) {
582 ScriptFreeCache(&run->script_cache); 630 ScriptFreeCache(&run->script_cache);
583 SelectObject(hdc, run->font.GetNativeFont()); 631 SelectObject(hdc, run->font.GetNativeFont());
584 } 632 }
585 633
586 run->script_analysis.eScript = SCRIPT_UNDEFINED; 634 tried_fallback = true;
587 } else { 635 } else {
588 break; 636 break;
589 } 637 }
590 } 638 }
591 DCHECK(SUCCEEDED(hr)); 639 DCHECK(SUCCEEDED(hr));
592 640
593 if (run->glyph_count > 0) { 641 if (run->glyph_count > 0) {
594 run->advance_widths.reset(new int[run->glyph_count]); 642 run->advance_widths.reset(new int[run->glyph_count]);
595 run->offsets.reset(new GOFFSET[run->glyph_count]); 643 run->offsets.reset(new GOFFSET[run->glyph_count]);
596 hr = ScriptPlace(hdc, 644 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]]; 789 internal::TextRun* next = runs_[visual_to_logical_[visual_index + 1]];
742 return next->script_analysis.fRTL ? LastSelectionModelInsideRun(next) : 790 return next->script_analysis.fRTL ? LastSelectionModelInsideRun(next) :
743 FirstSelectionModelInsideRun(next); 791 FirstSelectionModelInsideRun(next);
744 } 792 }
745 793
746 RenderText* RenderText::CreateRenderText() { 794 RenderText* RenderText::CreateRenderText() {
747 return new RenderTextWin; 795 return new RenderTextWin;
748 } 796 }
749 797
750 } // namespace gfx 798 } // namespace gfx
OLDNEW
« no previous file with comments | « ui/gfx/render_text_unittest.cc ('k') | ui/views/controls/textfield/textfield_views_model_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698