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 273 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 |
OLD | NEW |