Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(155)

Side by Side Diff: ui/gfx/render_text_linux.cc

Issue 7892044: Implement move by word, fix rendering of Arabic shape joining (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: address comment Created 9 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698