Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 287 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 298 return SelectionModel(cursor, caret, placement); | 298 return SelectionModel(cursor, caret, placement); |
| 299 } | 299 } |
| 300 | 300 |
| 301 SelectionModel RenderTextWin::RightEndSelectionModel() { | 301 SelectionModel RenderTextWin::RightEndSelectionModel() { |
| 302 if (text().empty()) | 302 if (text().empty()) |
| 303 return SelectionModel(0, 0, SelectionModel::LEADING); | 303 return SelectionModel(0, 0, SelectionModel::LEADING); |
| 304 | 304 |
| 305 EnsureLayout(); | 305 EnsureLayout(); |
| 306 size_t cursor = base::i18n::IsRTL() ? 0 : text().length(); | 306 size_t cursor = base::i18n::IsRTL() ? 0 : text().length(); |
| 307 internal::TextRun* run = runs_[visual_to_logical_[runs_.size() - 1]]; | 307 internal::TextRun* run = runs_[visual_to_logical_[runs_.size() - 1]]; |
| 308 bool rtl = run->script_analysis.fRTL; | 308 size_t caret; |
| 309 size_t caret = rtl ? run->range.start() : run->range.end() - 1; | 309 SelectionModel::CaretPlacement placement; |
| 310 SelectionModel::CaretPlacement placement = | 310 if (run->script_analysis.fRTL) { |
| 311 rtl ? SelectionModel::LEADING : SelectionModel::TRAILING; | 311 caret = run->range.start(); |
| 312 placement = SelectionModel::LEADING; | |
| 313 } else { | |
| 314 caret = IndexOfAdjacentGrapheme(run->range.end(), false); | |
| 315 placement = SelectionModel::TRAILING; | |
| 316 } | |
| 312 return SelectionModel(cursor, caret, placement); | 317 return SelectionModel(cursor, caret, placement); |
| 313 } | 318 } |
| 314 | 319 |
| 315 void RenderTextWin::GetSubstringBounds(size_t from, | 320 void RenderTextWin::GetSubstringBounds(size_t from, |
| 316 size_t to, | 321 size_t to, |
| 317 std::vector<Rect>* bounds) { | 322 std::vector<Rect>* bounds) { |
| 318 DCHECK(!needs_layout_); | 323 DCHECK(!needs_layout_); |
| 319 ui::Range range(from, to); | 324 ui::Range range(from, to); |
| 320 DCHECK(ui::Range(0, text().length()).Contains(range)); | 325 DCHECK(ui::Range(0, text().length()).Contains(range)); |
| 321 Point display_offset(GetUpdatedDisplayOffset()); | 326 Point display_offset(GetUpdatedDisplayOffset()); |
| (...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 454 size_t byte_length = run->glyph_count * sizeof(WORD); | 459 size_t byte_length = run->glyph_count * sizeof(WORD); |
| 455 canvas_skia->drawPosText(run->glyphs.get(), byte_length, &pos[0], paint); | 460 canvas_skia->drawPosText(run->glyphs.get(), byte_length, &pos[0], paint); |
| 456 | 461 |
| 457 if (run->strike || run->underline) | 462 if (run->strike || run->underline) |
| 458 DrawTextRunDecorations(canvas_skia, paint, *run, run_x, y); | 463 DrawTextRunDecorations(canvas_skia, paint, *run, run_x, y); |
| 459 } | 464 } |
| 460 } | 465 } |
| 461 | 466 |
| 462 size_t RenderTextWin::IndexOfAdjacentGrapheme(size_t index, bool next) { | 467 size_t RenderTextWin::IndexOfAdjacentGrapheme(size_t index, bool next) { |
| 463 EnsureLayout(); | 468 EnsureLayout(); |
| 469 | |
| 470 if (text().empty()) | |
| 471 return 0; | |
| 472 | |
| 473 if (index >= text().length()) { | |
| 474 if (next) { | |
| 475 return text().length(); | |
| 476 } else { | |
| 477 size_t last = text().length() - 1; | |
| 478 index = IndexOfAdjacentGrapheme(last, false); | |
|
xji
2011/12/01 08:31:58
is the reason of this special handling that:
run_i
Alexei Svitkine (slow)
2011/12/01 18:17:24
Correct. The previous logic was incorrect in this
| |
| 479 if (index == last) | |
| 480 return index; | |
| 481 next = true; | |
| 482 } | |
| 483 } | |
| 484 | |
| 464 size_t run_index = GetRunContainingPosition(index); | 485 size_t run_index = GetRunContainingPosition(index); |
| 465 internal::TextRun* run = run_index < runs_.size() ? runs_[run_index] : NULL; | 486 DCHECK(run_index < runs_.size()); |
| 466 int start = run ? run->range.start() : 0; | 487 internal::TextRun* run = runs_[run_index]; |
| 467 int length = run ? run->range.length() : text().length(); | 488 int start = run->range.start(); |
| 489 int length = run->range.length(); | |
| 468 int ch = index - start; | 490 int ch = index - start; |
| 469 WORD cluster = run ? run->logical_clusters[ch] : 0; | 491 WORD cluster = run->logical_clusters[ch]; |
| 470 | 492 |
| 471 if (!next) { | 493 if (!next) { |
| 472 do { | 494 do { |
| 473 ch--; | 495 ch--; |
| 474 } while (ch >= 0 && run && run->logical_clusters[ch] == cluster); | 496 } while (ch >= 0 && run->logical_clusters[ch] == cluster); |
| 497 | |
| 498 // Now, |ch| is at the end of the previous grapheme. Find the start. | |
| 499 while (ch > 0 && run->logical_clusters[ch - 1] == run->logical_clusters[ch]) | |
| 500 ch--; | |
|
xji
2011/12/01 08:31:58
this does not seem right.
for example: ABCDE, assu
Alexei Svitkine (slow)
2011/12/01 18:17:24
I see - this wasn't clear to me before. I've adjus
| |
| 475 } else { | 501 } else { |
| 476 while (ch < length && run && run->logical_clusters[ch] == cluster) | 502 while (ch < length && run->logical_clusters[ch] == cluster) |
| 477 ch++; | 503 ch++; |
| 478 } | 504 } |
| 505 | |
| 479 return std::max(std::min(ch, length) + start, 0); | 506 return std::max(std::min(ch, length) + start, 0); |
| 480 } | 507 } |
| 481 | 508 |
| 482 void RenderTextWin::ItemizeLogicalText() { | 509 void RenderTextWin::ItemizeLogicalText() { |
| 483 STLDeleteContainerPointers(runs_.begin(), runs_.end()); | 510 STLDeleteContainerPointers(runs_.begin(), runs_.end()); |
| 484 runs_.clear(); | 511 runs_.clear(); |
| 485 string_width_ = 0; | 512 string_width_ = 0; |
| 486 if (text().empty()) | 513 if (text().empty()) |
| 487 return; | 514 return; |
| 488 | 515 |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 538 } | 565 } |
| 539 | 566 |
| 540 void RenderTextWin::LayoutVisualText() { | 567 void RenderTextWin::LayoutVisualText() { |
| 541 HRESULT hr = E_FAIL; | 568 HRESULT hr = E_FAIL; |
| 542 base::win::ScopedCreateDC hdc(CreateCompatibleDC(NULL)); | 569 base::win::ScopedCreateDC hdc(CreateCompatibleDC(NULL)); |
| 543 std::vector<internal::TextRun*>::const_iterator run_iter; | 570 std::vector<internal::TextRun*>::const_iterator run_iter; |
| 544 for (run_iter = runs_.begin(); run_iter < runs_.end(); ++run_iter) { | 571 for (run_iter = runs_.begin(); run_iter < runs_.end(); ++run_iter) { |
| 545 internal::TextRun* run = *run_iter; | 572 internal::TextRun* run = *run_iter; |
| 546 size_t run_length = run->range.length(); | 573 size_t run_length = run->range.length(); |
| 547 const wchar_t* run_text = &(text()[run->range.start()]); | 574 const wchar_t* run_text = &(text()[run->range.start()]); |
| 575 bool tried_fallback = false; | |
| 548 | 576 |
| 549 // Select the font desired for glyph generation. | 577 // Select the font desired for glyph generation. |
| 550 SelectObject(hdc, run->font.GetNativeFont()); | 578 SelectObject(hdc, run->font.GetNativeFont()); |
| 551 | 579 |
| 552 run->logical_clusters.reset(new WORD[run_length]); | 580 run->logical_clusters.reset(new WORD[run_length]); |
| 553 run->glyph_count = 0; | 581 run->glyph_count = 0; |
| 554 // Max glyph guess: http://msdn.microsoft.com/en-us/library/dd368564.aspx | 582 // 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); | 583 size_t max_glyphs = static_cast<size_t>(1.5 * run_length + 16); |
| 556 while (max_glyphs < kMaxGlyphs) { | 584 while (max_glyphs < kMaxGlyphs) { |
| 557 run->glyphs.reset(new WORD[max_glyphs]); | 585 run->glyphs.reset(new WORD[max_glyphs]); |
| 558 run->visible_attributes.reset(new SCRIPT_VISATTR[max_glyphs]); | 586 run->visible_attributes.reset(new SCRIPT_VISATTR[max_glyphs]); |
| 559 hr = ScriptShape(hdc, | 587 hr = ScriptShape(hdc, |
| 560 &run->script_cache, | 588 &run->script_cache, |
| 561 run_text, | 589 run_text, |
| 562 run_length, | 590 run_length, |
| 563 max_glyphs, | 591 max_glyphs, |
| 564 &(run->script_analysis), | 592 &(run->script_analysis), |
| 565 run->glyphs.get(), | 593 run->glyphs.get(), |
| 566 run->logical_clusters.get(), | 594 run->logical_clusters.get(), |
| 567 run->visible_attributes.get(), | 595 run->visible_attributes.get(), |
| 568 &(run->glyph_count)); | 596 &(run->glyph_count)); |
| 569 if (hr == E_OUTOFMEMORY) { | 597 if (hr == E_OUTOFMEMORY) { |
| 570 max_glyphs *= 2; | 598 max_glyphs *= 2; |
| 571 } else if (hr == USP_E_SCRIPT_NOT_IN_FONT) { | 599 } else if (hr == USP_E_SCRIPT_NOT_IN_FONT) { |
| 572 // TODO(msw): Don't use SCRIPT_UNDEFINED. Apparently Uniscribe can crash | 600 // Only try font fallback if it hasn't yet been attempted for this run. |
| 573 // on certain surrogate pairs with SCRIPT_UNDEFINED. | 601 if (tried_fallback) { |
| 574 // See https://bugzilla.mozilla.org/show_bug.cgi?id=341500 | 602 // TODO(msw): Don't use SCRIPT_UNDEFINED. Apparently Uniscribe can |
| 575 // And http://maxradi.us/documents/uniscribe/ | 603 // crash on certain surrogate pairs with SCRIPT_UNDEFINED. |
| 576 if (run->script_analysis.eScript == SCRIPT_UNDEFINED) | 604 // See https://bugzilla.mozilla.org/show_bug.cgi?id=341500 |
| 577 break; | 605 // And http://maxradi.us/documents/uniscribe/ |
| 606 run->script_analysis.eScript = SCRIPT_UNDEFINED; | |
| 607 break; | |
| 608 } | |
| 578 | 609 |
| 579 // The run's font doesn't contain the required glyphs, use an alternate. | 610 // The run's font doesn't contain the required glyphs, use an alternate. |
| 580 if (ChooseFallbackFont(hdc, run->font, run_text, run_length, | 611 if (ChooseFallbackFont(hdc, run->font, run_text, run_length, |
| 581 &run->font)) { | 612 &run->font)) { |
| 582 ScriptFreeCache(&run->script_cache); | 613 ScriptFreeCache(&run->script_cache); |
| 583 SelectObject(hdc, run->font.GetNativeFont()); | 614 SelectObject(hdc, run->font.GetNativeFont()); |
| 584 } | 615 } |
| 585 | 616 |
| 586 run->script_analysis.eScript = SCRIPT_UNDEFINED; | 617 tried_fallback = true; |
| 587 } else { | 618 } else { |
| 588 break; | 619 break; |
| 589 } | 620 } |
| 590 } | 621 } |
| 591 DCHECK(SUCCEEDED(hr)); | 622 DCHECK(SUCCEEDED(hr)); |
| 592 | 623 |
| 593 if (run->glyph_count > 0) { | 624 if (run->glyph_count > 0) { |
| 594 run->advance_widths.reset(new int[run->glyph_count]); | 625 run->advance_widths.reset(new int[run->glyph_count]); |
| 595 run->offsets.reset(new GOFFSET[run->glyph_count]); | 626 run->offsets.reset(new GOFFSET[run->glyph_count]); |
| 596 hr = ScriptPlace(hdc, | 627 hr = ScriptPlace(hdc, |
| (...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 741 internal::TextRun* next = runs_[visual_to_logical_[visual_index + 1]]; | 772 internal::TextRun* next = runs_[visual_to_logical_[visual_index + 1]]; |
| 742 return next->script_analysis.fRTL ? LastSelectionModelInsideRun(next) : | 773 return next->script_analysis.fRTL ? LastSelectionModelInsideRun(next) : |
| 743 FirstSelectionModelInsideRun(next); | 774 FirstSelectionModelInsideRun(next); |
| 744 } | 775 } |
| 745 | 776 |
| 746 RenderText* RenderText::CreateRenderText() { | 777 RenderText* RenderText::CreateRenderText() { |
| 747 return new RenderTextWin; | 778 return new RenderTextWin; |
| 748 } | 779 } |
| 749 | 780 |
| 750 } // namespace gfx | 781 } // namespace gfx |
| OLD | NEW |