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

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: sync 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
« no previous file with comments | « ui/gfx/render_text_linux.h ('k') | ui/gfx/render_text_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 162 matching lines...) Expand 10 before | Expand all | Expand 10 after
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
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
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
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
OLDNEW
« no previous file with comments | « ui/gfx/render_text_linux.h ('k') | ui/gfx/render_text_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698