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

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: fix lint error 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 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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698