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 size_t caret; | 294 bool rtl = run->script_analysis.fRTL; |
295 SelectionModel::CaretPlacement placement; | 295 size_t caret = rtl ? run->range.end() - 1 : run->range.start(); |
296 if (run->script_analysis.fRTL) { | 296 SelectionModel::CaretPlacement placement = |
297 caret = IndexOfAdjacentGrapheme(run->range.end(), false); | 297 rtl ? SelectionModel::TRAILING : SelectionModel::LEADING; |
298 placement = SelectionModel::TRAILING; | |
299 } else { | |
300 caret = run->range.start(); | |
301 placement = SelectionModel::LEADING; | |
302 } | |
303 return SelectionModel(cursor, caret, placement); | 298 return SelectionModel(cursor, caret, placement); |
304 } | 299 } |
305 | 300 |
306 SelectionModel RenderTextWin::RightEndSelectionModel() { | 301 SelectionModel RenderTextWin::RightEndSelectionModel() { |
307 if (text().empty()) | 302 if (text().empty()) |
308 return SelectionModel(0, 0, SelectionModel::LEADING); | 303 return SelectionModel(0, 0, SelectionModel::LEADING); |
309 | 304 |
310 EnsureLayout(); | 305 EnsureLayout(); |
311 size_t cursor = base::i18n::IsRTL() ? 0 : text().length(); | 306 size_t cursor = base::i18n::IsRTL() ? 0 : text().length(); |
312 internal::TextRun* run = runs_[visual_to_logical_[runs_.size() - 1]]; | 307 internal::TextRun* run = runs_[visual_to_logical_[runs_.size() - 1]]; |
313 size_t caret; | 308 bool rtl = run->script_analysis.fRTL; |
314 SelectionModel::CaretPlacement placement; | 309 size_t caret = rtl ? run->range.start() : run->range.end() - 1; |
315 if (run->script_analysis.fRTL) { | 310 SelectionModel::CaretPlacement placement = |
316 caret = run->range.start(); | 311 rtl ? SelectionModel::LEADING : SelectionModel::TRAILING; |
317 placement = SelectionModel::LEADING; | |
318 } else { | |
319 caret = IndexOfAdjacentGrapheme(run->range.end(), false); | |
320 placement = SelectionModel::TRAILING; | |
321 } | |
322 return SelectionModel(cursor, caret, placement); | 312 return SelectionModel(cursor, caret, placement); |
323 } | 313 } |
324 | 314 |
325 void RenderTextWin::GetSubstringBounds(size_t from, | 315 void RenderTextWin::GetSubstringBounds(size_t from, |
326 size_t to, | 316 size_t to, |
327 std::vector<Rect>* bounds) { | 317 std::vector<Rect>* bounds) { |
328 DCHECK(!needs_layout_); | 318 DCHECK(!needs_layout_); |
329 ui::Range range(from, to); | 319 ui::Range range(from, to); |
330 DCHECK(ui::Range(0, text().length()).Contains(range)); | 320 DCHECK(ui::Range(0, text().length()).Contains(range)); |
331 Point display_offset(GetUpdatedDisplayOffset()); | 321 Point display_offset(GetUpdatedDisplayOffset()); |
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
464 size_t byte_length = run->glyph_count * sizeof(WORD); | 454 size_t byte_length = run->glyph_count * sizeof(WORD); |
465 canvas_skia->drawPosText(run->glyphs.get(), byte_length, &pos[0], paint); | 455 canvas_skia->drawPosText(run->glyphs.get(), byte_length, &pos[0], paint); |
466 | 456 |
467 if (run->strike || run->underline) | 457 if (run->strike || run->underline) |
468 DrawTextRunDecorations(canvas_skia, paint, *run, run_x, y); | 458 DrawTextRunDecorations(canvas_skia, paint, *run, run_x, y); |
469 } | 459 } |
470 } | 460 } |
471 | 461 |
472 size_t RenderTextWin::IndexOfAdjacentGrapheme(size_t index, bool next) { | 462 size_t RenderTextWin::IndexOfAdjacentGrapheme(size_t index, bool next) { |
473 EnsureLayout(); | 463 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 | |
490 size_t run_index = GetRunContainingPosition(index); | 464 size_t run_index = GetRunContainingPosition(index); |
491 DCHECK(run_index < runs_.size()); | 465 internal::TextRun* run = run_index < runs_.size() ? runs_[run_index] : NULL; |
492 internal::TextRun* run = runs_[run_index]; | 466 int start = run ? run->range.start() : 0; |
493 size_t start = run->range.start(); | 467 int length = run ? run->range.length() : text().length(); |
494 size_t ch = index - start; | 468 int ch = index - start; |
| 469 WORD cluster = run ? run->logical_clusters[ch] : 0; |
495 | 470 |
496 if (!next) { | 471 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]; | |
508 do { | 472 do { |
509 ch--; | 473 ch--; |
510 } while (ch > 0 && run->logical_clusters[ch - 1] == cluster); | 474 } while (ch >= 0 && run && run->logical_clusters[ch] == cluster); |
511 } else { | 475 } else { |
512 WORD cluster = run->logical_clusters[ch]; | 476 while (ch < length && run && run->logical_clusters[ch] == cluster) |
513 while (ch < run->range.length() && run->logical_clusters[ch] == cluster) | |
514 ch++; | 477 ch++; |
515 } | 478 } |
516 | 479 return std::max(std::min(ch, length) + start, 0); |
517 return start + ch; | |
518 } | 480 } |
519 | 481 |
520 void RenderTextWin::ItemizeLogicalText() { | 482 void RenderTextWin::ItemizeLogicalText() { |
521 STLDeleteContainerPointers(runs_.begin(), runs_.end()); | 483 STLDeleteContainerPointers(runs_.begin(), runs_.end()); |
522 runs_.clear(); | 484 runs_.clear(); |
523 string_width_ = 0; | 485 string_width_ = 0; |
524 if (text().empty()) | 486 if (text().empty()) |
525 return; | 487 return; |
526 | 488 |
527 const wchar_t* raw_text = text().c_str(); | 489 const wchar_t* raw_text = text().c_str(); |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
576 } | 538 } |
577 | 539 |
578 void RenderTextWin::LayoutVisualText() { | 540 void RenderTextWin::LayoutVisualText() { |
579 HRESULT hr = E_FAIL; | 541 HRESULT hr = E_FAIL; |
580 base::win::ScopedCreateDC hdc(CreateCompatibleDC(NULL)); | 542 base::win::ScopedCreateDC hdc(CreateCompatibleDC(NULL)); |
581 std::vector<internal::TextRun*>::const_iterator run_iter; | 543 std::vector<internal::TextRun*>::const_iterator run_iter; |
582 for (run_iter = runs_.begin(); run_iter < runs_.end(); ++run_iter) { | 544 for (run_iter = runs_.begin(); run_iter < runs_.end(); ++run_iter) { |
583 internal::TextRun* run = *run_iter; | 545 internal::TextRun* run = *run_iter; |
584 size_t run_length = run->range.length(); | 546 size_t run_length = run->range.length(); |
585 const wchar_t* run_text = &(text()[run->range.start()]); | 547 const wchar_t* run_text = &(text()[run->range.start()]); |
586 bool tried_fallback = false; | |
587 | 548 |
588 // Select the font desired for glyph generation. | 549 // Select the font desired for glyph generation. |
589 SelectObject(hdc, run->font.GetNativeFont()); | 550 SelectObject(hdc, run->font.GetNativeFont()); |
590 | 551 |
591 run->logical_clusters.reset(new WORD[run_length]); | 552 run->logical_clusters.reset(new WORD[run_length]); |
592 run->glyph_count = 0; | 553 run->glyph_count = 0; |
593 // Max glyph guess: http://msdn.microsoft.com/en-us/library/dd368564.aspx | 554 // Max glyph guess: http://msdn.microsoft.com/en-us/library/dd368564.aspx |
594 size_t max_glyphs = static_cast<size_t>(1.5 * run_length + 16); | 555 size_t max_glyphs = static_cast<size_t>(1.5 * run_length + 16); |
595 while (max_glyphs < kMaxGlyphs) { | 556 while (max_glyphs < kMaxGlyphs) { |
596 run->glyphs.reset(new WORD[max_glyphs]); | 557 run->glyphs.reset(new WORD[max_glyphs]); |
597 run->visible_attributes.reset(new SCRIPT_VISATTR[max_glyphs]); | 558 run->visible_attributes.reset(new SCRIPT_VISATTR[max_glyphs]); |
598 hr = ScriptShape(hdc, | 559 hr = ScriptShape(hdc, |
599 &run->script_cache, | 560 &run->script_cache, |
600 run_text, | 561 run_text, |
601 run_length, | 562 run_length, |
602 max_glyphs, | 563 max_glyphs, |
603 &(run->script_analysis), | 564 &(run->script_analysis), |
604 run->glyphs.get(), | 565 run->glyphs.get(), |
605 run->logical_clusters.get(), | 566 run->logical_clusters.get(), |
606 run->visible_attributes.get(), | 567 run->visible_attributes.get(), |
607 &(run->glyph_count)); | 568 &(run->glyph_count)); |
608 if (hr == E_OUTOFMEMORY) { | 569 if (hr == E_OUTOFMEMORY) { |
609 max_glyphs *= 2; | 570 max_glyphs *= 2; |
610 } else if (hr == USP_E_SCRIPT_NOT_IN_FONT) { | 571 } else if (hr == USP_E_SCRIPT_NOT_IN_FONT) { |
611 // Only try font fallback if it hasn't yet been attempted for this run. | 572 // TODO(msw): Don't use SCRIPT_UNDEFINED. Apparently Uniscribe can crash |
612 if (tried_fallback) { | 573 // on certain surrogate pairs with SCRIPT_UNDEFINED. |
613 // TODO(msw): Don't use SCRIPT_UNDEFINED. Apparently Uniscribe can | 574 // See https://bugzilla.mozilla.org/show_bug.cgi?id=341500 |
614 // crash on certain surrogate pairs with SCRIPT_UNDEFINED. | 575 // And http://maxradi.us/documents/uniscribe/ |
615 // See https://bugzilla.mozilla.org/show_bug.cgi?id=341500 | 576 if (run->script_analysis.eScript == SCRIPT_UNDEFINED) |
616 // And http://maxradi.us/documents/uniscribe/ | 577 break; |
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 } | |
626 | 578 |
627 // The run's font doesn't contain the required glyphs, use an alternate. | 579 // The run's font doesn't contain the required glyphs, use an alternate. |
628 if (ChooseFallbackFont(hdc, run->font, run_text, run_length, | 580 if (ChooseFallbackFont(hdc, run->font, run_text, run_length, |
629 &run->font)) { | 581 &run->font)) { |
630 ScriptFreeCache(&run->script_cache); | 582 ScriptFreeCache(&run->script_cache); |
631 SelectObject(hdc, run->font.GetNativeFont()); | 583 SelectObject(hdc, run->font.GetNativeFont()); |
632 } | 584 } |
633 | 585 |
634 tried_fallback = true; | 586 run->script_analysis.eScript = SCRIPT_UNDEFINED; |
635 } else { | 587 } else { |
636 break; | 588 break; |
637 } | 589 } |
638 } | 590 } |
639 DCHECK(SUCCEEDED(hr)); | 591 DCHECK(SUCCEEDED(hr)); |
640 | 592 |
641 if (run->glyph_count > 0) { | 593 if (run->glyph_count > 0) { |
642 run->advance_widths.reset(new int[run->glyph_count]); | 594 run->advance_widths.reset(new int[run->glyph_count]); |
643 run->offsets.reset(new GOFFSET[run->glyph_count]); | 595 run->offsets.reset(new GOFFSET[run->glyph_count]); |
644 hr = ScriptPlace(hdc, | 596 hr = ScriptPlace(hdc, |
(...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
789 internal::TextRun* next = runs_[visual_to_logical_[visual_index + 1]]; | 741 internal::TextRun* next = runs_[visual_to_logical_[visual_index + 1]]; |
790 return next->script_analysis.fRTL ? LastSelectionModelInsideRun(next) : | 742 return next->script_analysis.fRTL ? LastSelectionModelInsideRun(next) : |
791 FirstSelectionModelInsideRun(next); | 743 FirstSelectionModelInsideRun(next); |
792 } | 744 } |
793 | 745 |
794 RenderText* RenderText::CreateRenderText() { | 746 RenderText* RenderText::CreateRenderText() { |
795 return new RenderTextWin; | 747 return new RenderTextWin; |
796 } | 748 } |
797 | 749 |
798 } // namespace gfx | 750 } // namespace gfx |
OLD | NEW |