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_linux.h" | 5 #include "ui/gfx/render_text_linux.h" |
6 | 6 |
7 #include <pango/pangocairo.h> | 7 #include <pango/pangocairo.h> |
8 | 8 |
9 #include <algorithm> | 9 #include <algorithm> |
10 | 10 |
| 11 #include "base/i18n/break_iterator.h" |
11 #include "base/logging.h" | 12 #include "base/logging.h" |
12 #include "ui/gfx/canvas_skia.h" | 13 #include "ui/gfx/canvas_skia.h" |
13 #include "ui/gfx/pango_util.h" | 14 #include "ui/gfx/pango_util.h" |
14 #include "unicode/uchar.h" | 15 #include "unicode/uchar.h" |
15 #include "unicode/ustring.h" | 16 #include "unicode/ustring.h" |
16 | 17 |
17 namespace { | 18 namespace { |
18 | 19 |
19 // TODO(xji): instead of converting each R or G or B from 8-bit to 16-bit, | 20 // TODO(xji): instead of converting each R or G or B from 8-bit to 16-bit, |
20 // it should also massage A in the conversion. | 21 // it should also massage A in the conversion. |
(...skipping 162 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
183 | 184 |
184 SelectionModel RenderTextLinux::GetLeftSelectionModel( | 185 SelectionModel RenderTextLinux::GetLeftSelectionModel( |
185 const SelectionModel& current, | 186 const SelectionModel& current, |
186 BreakType break_type) { | 187 BreakType break_type) { |
187 EnsureLayout(); | 188 EnsureLayout(); |
188 | 189 |
189 if (break_type == LINE_BREAK || text().empty()) | 190 if (break_type == LINE_BREAK || text().empty()) |
190 return LeftEndSelectionModel(); | 191 return LeftEndSelectionModel(); |
191 if (break_type == CHARACTER_BREAK) | 192 if (break_type == CHARACTER_BREAK) |
192 return LeftSelectionModel(current); | 193 return LeftSelectionModel(current); |
193 // TODO(xji): not implemented yet. | 194 DCHECK(break_type == WORD_BREAK); |
194 return RenderText::GetLeftSelectionModel(current, break_type); | 195 return LeftSelectionModelByWord(current); |
195 } | 196 } |
196 | 197 |
197 SelectionModel RenderTextLinux::GetRightSelectionModel( | 198 SelectionModel RenderTextLinux::GetRightSelectionModel( |
198 const SelectionModel& current, | 199 const SelectionModel& current, |
199 BreakType break_type) { | 200 BreakType break_type) { |
200 EnsureLayout(); | 201 EnsureLayout(); |
201 | 202 |
202 if (break_type == LINE_BREAK || text().empty()) | 203 if (break_type == LINE_BREAK || text().empty()) |
203 return RightEndSelectionModel(); | 204 return RightEndSelectionModel(); |
204 if (break_type == CHARACTER_BREAK) | 205 if (break_type == CHARACTER_BREAK) |
205 return RightSelectionModel(current); | 206 return RightSelectionModel(current); |
206 // TODO(xji): not implemented yet. | 207 DCHECK(break_type == WORD_BREAK); |
207 return RenderText::GetRightSelectionModel(current, break_type); | 208 return RightSelectionModelByWord(current); |
208 } | 209 } |
209 | 210 |
210 SelectionModel RenderTextLinux::LeftEndSelectionModel() { | 211 SelectionModel RenderTextLinux::LeftEndSelectionModel() { |
211 if (GetTextDirection() == base::i18n::RIGHT_TO_LEFT) { | 212 if (GetTextDirection() == base::i18n::RIGHT_TO_LEFT) { |
212 if (current_line_->runs) { | 213 if (current_line_->runs) { |
213 PangoLayoutRun* first_visual_run = | 214 PangoLayoutRun* first_visual_run = |
214 reinterpret_cast<PangoLayoutRun*>(current_line_->runs->data); | 215 reinterpret_cast<PangoLayoutRun*>(current_line_->runs->data); |
215 PangoItem* item = first_visual_run->item; | 216 PangoItem* item = first_visual_run->item; |
216 if (item->analysis.level % 2 == 0) { // LTR. | 217 if (item->analysis.level % 2 == 0) { // LTR. |
217 size_t caret = Utf8IndexToUtf16Index(item->offset); | 218 size_t caret = Utf8IndexToUtf16Index(item->offset); |
(...skipping 211 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
429 // The character is at the end of its run; advance to the next visual run. | 430 // The character is at the end of its run; advance to the next visual run. |
430 GSList* next_run = run->next; | 431 GSList* next_run = run->next; |
431 if (!next_run) | 432 if (!next_run) |
432 return RightEndSelectionModel(); | 433 return RightEndSelectionModel(); |
433 | 434 |
434 item = reinterpret_cast<PangoLayoutRun*>(next_run->data)->item; | 435 item = reinterpret_cast<PangoLayoutRun*>(next_run->data)->item; |
435 return (item->analysis.level % 2) ? LastSelectionModelInsideRun(item) : | 436 return (item->analysis.level % 2) ? LastSelectionModelInsideRun(item) : |
436 FirstSelectionModelInsideRun(item); | 437 FirstSelectionModelInsideRun(item); |
437 } | 438 } |
438 | 439 |
| 440 SelectionModel RenderTextLinux::LeftSelectionModelByWord( |
| 441 const SelectionModel& selection) { |
| 442 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD); |
| 443 bool success = iter.Init(); |
| 444 DCHECK(success); |
| 445 if (!success) |
| 446 return selection; |
| 447 |
| 448 SelectionModel left_end = LeftEndSelectionModel(); |
| 449 SelectionModel left(selection); |
| 450 while (!left.Equals(left_end)) { |
| 451 left = LeftSelectionModel(left); |
| 452 size_t caret = left.caret_pos(); |
| 453 GSList* run = GetRunContainingPosition(caret); |
| 454 DCHECK(run); |
| 455 PangoItem* item = reinterpret_cast<PangoLayoutRun*>(run->data)->item; |
| 456 size_t cursor = left.selection_end(); |
| 457 if (item->analysis.level % 2 == 0) { // LTR run. |
| 458 if (iter.IsStartOfWord(cursor)) |
| 459 return left; |
| 460 } else { // RTL run. |
| 461 if (iter.IsEndOfWord(cursor)) |
| 462 return left; |
| 463 } |
| 464 } |
| 465 |
| 466 return left_end; |
| 467 } |
| 468 |
| 469 SelectionModel RenderTextLinux::RightSelectionModelByWord( |
| 470 const SelectionModel& selection) { |
| 471 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD); |
| 472 bool success = iter.Init(); |
| 473 DCHECK(success); |
| 474 if (!success) |
| 475 return selection; |
| 476 |
| 477 SelectionModel right_end = RightEndSelectionModel(); |
| 478 SelectionModel right(selection); |
| 479 while (!right.Equals(right_end)) { |
| 480 right = RightSelectionModel(right); |
| 481 size_t caret = right.caret_pos(); |
| 482 GSList* run = GetRunContainingPosition(caret); |
| 483 DCHECK(run); |
| 484 PangoItem* item = reinterpret_cast<PangoLayoutRun*>(run->data)->item; |
| 485 size_t cursor = right.selection_end(); |
| 486 if (item->analysis.level % 2 == 0) { // LTR run. |
| 487 if (iter.IsEndOfWord(cursor)) |
| 488 return right; |
| 489 } else { // RTL run. |
| 490 if (iter.IsStartOfWord(cursor)) |
| 491 return right; |
| 492 } |
| 493 } |
| 494 |
| 495 return right_end; |
| 496 } |
| 497 |
439 PangoLayout* RenderTextLinux::EnsureLayout() { | 498 PangoLayout* RenderTextLinux::EnsureLayout() { |
440 if (layout_ == NULL) { | 499 if (layout_ == NULL) { |
441 CanvasSkia canvas(display_rect().width(), display_rect().height(), false); | 500 CanvasSkia canvas(display_rect().width(), display_rect().height(), false); |
442 skia::ScopedPlatformPaint scoped_platform_paint(&canvas); | 501 skia::ScopedPlatformPaint scoped_platform_paint(&canvas); |
443 cairo_t* cr = scoped_platform_paint.GetPlatformSurface(); | 502 cairo_t* cr = scoped_platform_paint.GetPlatformSurface(); |
444 | 503 |
445 layout_ = pango_cairo_create_layout(cr); | 504 layout_ = pango_cairo_create_layout(cr); |
446 SetupPangoLayout( | 505 SetupPangoLayout( |
447 layout_, | 506 layout_, |
448 text(), | 507 text(), |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
487 log_attrs_ = NULL; | 546 log_attrs_ = NULL; |
488 num_log_attrs_ = 0; | 547 num_log_attrs_ = 0; |
489 } | 548 } |
490 layout_text_ = NULL; | 549 layout_text_ = NULL; |
491 layout_text_len_ = 0; | 550 layout_text_len_ = 0; |
492 } | 551 } |
493 | 552 |
494 void RenderTextLinux::SetupPangoAttributes(PangoLayout* layout) { | 553 void RenderTextLinux::SetupPangoAttributes(PangoLayout* layout) { |
495 PangoAttrList* attrs = pango_attr_list_new(); | 554 PangoAttrList* attrs = pango_attr_list_new(); |
496 // Set selection background color. | 555 // Set selection background color. |
| 556 // TODO(xji): There's a bug in pango that it can't use two colors in one |
| 557 // glyph. Please refer to https://bugzilla.gnome.org/show_bug.cgi?id=648157 |
| 558 // for detail. So for example, if a font has "ffi" ligature, but you select |
| 559 // half of that glyph, you either get the entire "ffi" ligature |
| 560 // selection-colored, or none of it. |
| 561 // We could use clipping to render selection. |
| 562 // Use pango_glyph_item_get_logical_widths to find the exact boundaries of |
| 563 // selection, then cairo_clip that, paint background, set color to white and |
| 564 // redraw the layout. |
497 SkColor selection_color = | 565 SkColor selection_color = |
498 focused() ? kFocusedSelectionColor : kUnfocusedSelectionColor; | 566 focused() ? kFocusedSelectionColor : kUnfocusedSelectionColor; |
499 size_t start = std::min(MinOfSelection(), text().length()); | 567 size_t start = std::min(MinOfSelection(), text().length()); |
500 size_t end = std::min(MaxOfSelection(), text().length()); | 568 size_t end = std::min(MaxOfSelection(), text().length()); |
501 PangoAttribute* pango_attr; | 569 PangoAttribute* pango_attr; |
502 if (end > start) { | 570 if (end > start) { |
503 pango_attr = pango_attr_background_new( | 571 pango_attr = pango_attr_background_new( |
504 ConvertColorFrom8BitTo16Bit(SkColorGetR(selection_color)), | 572 ConvertColorFrom8BitTo16Bit(SkColorGetR(selection_color)), |
505 ConvertColorFrom8BitTo16Bit(SkColorGetG(selection_color)), | 573 ConvertColorFrom8BitTo16Bit(SkColorGetG(selection_color)), |
506 ConvertColorFrom8BitTo16Bit(SkColorGetB(selection_color))); | 574 ConvertColorFrom8BitTo16Bit(SkColorGetB(selection_color))); |
507 AppendPangoAttribute(start, end, pango_attr, attrs); | 575 AppendPangoAttribute(start, end, pango_attr, attrs); |
508 } | 576 } |
509 | 577 |
510 StyleRanges ranges_of_style(style_ranges()); | 578 StyleRanges ranges_of_style(style_ranges()); |
511 ApplyCompositionAndSelectionStyles(&ranges_of_style); | 579 ApplyCompositionAndSelectionStyles(&ranges_of_style); |
512 | 580 |
| 581 PlatformFont* default_platform_font = default_style().font.platform_font(); |
| 582 |
513 for (StyleRanges::const_iterator i = ranges_of_style.begin(); | 583 for (StyleRanges::const_iterator i = ranges_of_style.begin(); |
514 i < ranges_of_style.end(); ++i) { | 584 i < ranges_of_style.end(); ++i) { |
515 start = std::min(i->range.start(), text().length()); | 585 start = std::min(i->range.start(), text().length()); |
516 end = std::min(i->range.end(), text().length()); | 586 end = std::min(i->range.end(), text().length()); |
517 if (start >= end) | 587 if (start >= end) |
518 continue; | 588 continue; |
519 | 589 |
520 const Font& font = !i->underline ? i->font : | 590 const Font& font = i->font; |
521 i->font.DeriveFont(0, i->font.GetStyle() | Font::UNDERLINED); | 591 // In Pango, different fonts means different runs, and it breaks Arabic |
522 PangoFontDescription* desc = font.GetNativeFont(); | 592 // shaping acorss run boundaries. So, set font only when it is different |
523 pango_attr = pango_attr_font_desc_new(desc); | 593 // from the default faont. |
524 AppendPangoAttribute(start, end, pango_attr, attrs); | 594 // TODO(xji): we'll eventually need to split up StyleRange into components |
525 pango_font_description_free(desc); | 595 // (ColorRange, FontRange, etc.) so that we can combine adjacent ranges |
| 596 // with the same Fonts (to avoid unnecessarily splitting up runs) |
| 597 if (font.platform_font() != default_platform_font) { |
| 598 PangoFontDescription* desc = font.GetNativeFont(); |
| 599 pango_attr = pango_attr_font_desc_new(desc); |
| 600 AppendPangoAttribute(start, end, pango_attr, attrs); |
| 601 pango_font_description_free(desc); |
| 602 } |
526 | 603 |
527 SkColor foreground = i->foreground; | 604 SkColor foreground = i->foreground; |
528 pango_attr = pango_attr_foreground_new( | 605 pango_attr = pango_attr_foreground_new( |
529 ConvertColorFrom8BitTo16Bit(SkColorGetR(foreground)), | 606 ConvertColorFrom8BitTo16Bit(SkColorGetR(foreground)), |
530 ConvertColorFrom8BitTo16Bit(SkColorGetG(foreground)), | 607 ConvertColorFrom8BitTo16Bit(SkColorGetG(foreground)), |
531 ConvertColorFrom8BitTo16Bit(SkColorGetB(foreground))); | 608 ConvertColorFrom8BitTo16Bit(SkColorGetB(foreground))); |
532 AppendPangoAttribute(start, end, pango_attr, attrs); | 609 AppendPangoAttribute(start, end, pango_attr, attrs); |
533 | 610 |
| 611 if (i->underline) { |
| 612 pango_attr = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE); |
| 613 AppendPangoAttribute(start, end, pango_attr, attrs); |
| 614 } |
| 615 |
534 if (i->strike) { | 616 if (i->strike) { |
535 pango_attr = pango_attr_strikethrough_new(true); | 617 pango_attr = pango_attr_strikethrough_new(true); |
536 AppendPangoAttribute(start, end, pango_attr, attrs); | 618 AppendPangoAttribute(start, end, pango_attr, attrs); |
537 } | 619 } |
538 } | 620 } |
539 | 621 |
540 pango_layout_set_attributes(layout, attrs); | 622 pango_layout_set_attributes(layout, attrs); |
541 pango_attr_list_unref(attrs); | 623 pango_attr_list_unref(attrs); |
542 } | 624 } |
543 | 625 |
544 void RenderTextLinux::AppendPangoAttribute(size_t start, | 626 void RenderTextLinux::AppendPangoAttribute(size_t start, |
545 size_t end, | 627 size_t end, |
546 PangoAttribute* pango_attr, | 628 PangoAttribute* pango_attr, |
547 PangoAttrList* attrs) { | 629 PangoAttrList* attrs) { |
548 pango_attr->start_index = Utf16IndexToUtf8Index(start); | 630 pango_attr->start_index = Utf16IndexToUtf8Index(start); |
549 pango_attr->end_index = Utf16IndexToUtf8Index(end); | 631 pango_attr->end_index = Utf16IndexToUtf8Index(end); |
550 pango_attr_list_insert(attrs, pango_attr); | 632 pango_attr_list_insert(attrs, pango_attr); |
551 } | 633 } |
552 | 634 |
| 635 // TODO(xji): Keep a vector of runs to avoid using a singly-linked list. |
553 PangoLayoutRun* RenderTextLinux::GetPreviousRun(PangoLayoutRun* run) const { | 636 PangoLayoutRun* RenderTextLinux::GetPreviousRun(PangoLayoutRun* run) const { |
554 GSList* current = current_line_->runs; | 637 GSList* current = current_line_->runs; |
555 GSList* prev = NULL; | 638 GSList* prev = NULL; |
556 while (current) { | 639 while (current) { |
557 if (reinterpret_cast<PangoLayoutRun*>(current->data) == run) | 640 if (reinterpret_cast<PangoLayoutRun*>(current->data) == run) |
558 return prev ? reinterpret_cast<PangoLayoutRun*>(prev->data) : NULL; | 641 return prev ? reinterpret_cast<PangoLayoutRun*>(prev->data) : NULL; |
559 prev = current; | 642 prev = current; |
560 current = current->next; | 643 current = current->next; |
561 } | 644 } |
562 return NULL; | 645 return NULL; |
(...skipping 26 matching lines...) Expand all Loading... |
589 size_t RenderTextLinux::Utf8IndexToUtf16Index(size_t index) const { | 672 size_t RenderTextLinux::Utf8IndexToUtf16Index(size_t index) const { |
590 int32_t utf16_index = 0; | 673 int32_t utf16_index = 0; |
591 UErrorCode ec = U_ZERO_ERROR; | 674 UErrorCode ec = U_ZERO_ERROR; |
592 u_strFromUTF8(NULL, 0, &utf16_index, layout_text_, index, &ec); | 675 u_strFromUTF8(NULL, 0, &utf16_index, layout_text_, index, &ec); |
593 DCHECK(ec == U_BUFFER_OVERFLOW_ERROR || | 676 DCHECK(ec == U_BUFFER_OVERFLOW_ERROR || |
594 ec == U_STRING_NOT_TERMINATED_WARNING); | 677 ec == U_STRING_NOT_TERMINATED_WARNING); |
595 return utf16_index; | 678 return utf16_index; |
596 } | 679 } |
597 | 680 |
598 } // namespace gfx | 681 } // namespace gfx |
OLD | NEW |