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