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 |