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