| 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 |