Index: ui/gfx/render_text_linux.cc |
=================================================================== |
--- ui/gfx/render_text_linux.cc (revision 99272) |
+++ ui/gfx/render_text_linux.cc (working copy) |
@@ -4,17 +4,583 @@ |
#include "ui/gfx/render_text_linux.h" |
+#include <pango/pangocairo.h> |
+ |
+#include <algorithm> |
+ |
+#include "base/logging.h" |
+#include "ui/gfx/canvas_skia.h" |
+#include "ui/gfx/pango_util.h" |
+#include "unicode/uchar.h" |
+#include "unicode/ustring.h" |
+ |
+namespace { |
+ |
+// TODO(xji): instead of converting each R or G or B from 8-bit to 16-bit, |
+// it should also massage A in the conversion. |
behdad_google
2011/09/06 16:05:03
Right. Do we have the background color around? I
xji
2011/09/06 19:54:13
Thanks for the offering.
Looks like the background
|
+int ConvertColorFrom8BitTo16Bit(int c) { |
+ return (c << 8) + c; |
+} |
+ |
+} // namespace |
+ |
+// TODO(xji): index saved in upper layer is utf16 index. Pango uses utf8 index. |
+// Since caret_pos is used internally, we could save utf8 index for caret_pos |
+// to avoid conversion. |
+ |
namespace gfx { |
RenderTextLinux::RenderTextLinux() |
- : RenderText() { |
+ : layout_(NULL), |
+ layout_line_(NULL) { |
} |
RenderTextLinux::~RenderTextLinux() { |
+ ResetLayout(); |
} |
RenderText* RenderText::CreateRenderText() { |
return new RenderTextLinux; |
} |
+void RenderTextLinux::SetText(const string16& text) { |
+ RenderText::SetText(text); |
+ ResetLayout(); |
+} |
+ |
+void RenderTextLinux::SetDisplayRect(const Rect&r) { |
+ RenderText::SetDisplayRect(r); |
+ ResetLayout(); |
+} |
+ |
+void RenderTextLinux::SetCompositionRange(const ui::Range& composition_range) { |
+ RenderText::SetCompositionRange(composition_range); |
+ ResetLayout(); |
+} |
+ |
+void RenderTextLinux::ApplyStyleRange(StyleRange style_range) { |
+ RenderText::ApplyStyleRange(style_range); |
+ ResetLayout(); |
+} |
+ |
+void RenderTextLinux::ApplyDefaultStyle() { |
+ RenderText::ApplyDefaultStyle(); |
+ ResetLayout(); |
+} |
+ |
+base::i18n::TextDirection RenderTextLinux::GetTextDirection() { |
+ EnsureLayout(); |
+ |
+ const char* layout_text = pango_layout_get_text(layout_); |
+ PangoDirection base_dir = |
+ pango_find_base_dir(layout_text, strlen(layout_text)); |
behdad_google
2011/09/06 16:05:03
Just pass -1 for length here.
xji
2011/09/06 19:54:13
Done.
|
+ if (base_dir == PANGO_DIRECTION_RTL || base_dir == PANGO_DIRECTION_WEAK_RTL) |
+ return base::i18n::RIGHT_TO_LEFT; |
+ return base::i18n::LEFT_TO_RIGHT; |
+} |
+ |
+int RenderTextLinux::GetStringWidth() { |
+ PangoLayout* layout = EnsureLayout(); |
+ int width, height; |
+ pango_layout_get_pixel_size(layout, &width, &height); |
behdad_google
2011/09/06 16:05:03
You can pass NULL for height.
xji
2011/09/06 19:54:13
Done.
|
+ return width; |
+} |
+ |
+void RenderTextLinux::Draw(Canvas* canvas) { |
+ PangoLayout* layout = EnsureLayout(); |
+ Rect bounds(display_rect()); |
+ |
+ // Clip the canvas to the text display area. |
+ CanvasSkia* canvas_skia = canvas->AsCanvasSkia(); |
+ canvas_skia->ClipRectInt( |
+ bounds.x(), bounds.y(), bounds.width(), bounds.height()); |
+ |
+ skia::ScopedPlatformPaint scoped_platform_paint(canvas_skia); |
+ cairo_t* cr = scoped_platform_paint.GetPlatformSurface(); |
+ cairo_save(cr); |
+ cairo_rectangle(cr, bounds.x(), bounds.y(), bounds.width(), bounds.height()); |
+ cairo_clip(cr); |
behdad_google
2011/09/06 16:05:03
I have a feeling that you shouldn't need the skia
xji
2011/09/06 19:54:13
hm... Looking at CanvasSkia::DrawStringInt() again
|
+ |
+ int text_width, text_height; |
+ pango_layout_get_pixel_size(layout, &text_width, &text_height); |
+ Point offset(ToViewPoint(Point())); |
+ // Vertically centered. |
+ int text_y = offset.y() + ((bounds.height() - text_height) / 2); |
+ // TODO(xji): need to use SkCanvas->drawPosText() for gpu acceleration. |
+ cairo_move_to(cr, offset.x(), text_y); |
+ pango_cairo_show_layout(cr, layout); |
+ |
+ cairo_restore(cr); |
+ |
+ // Paint cursor. |
+ bounds = GetUpdatedCursorBounds(); |
+ if (cursor_visible() && focused() && !bounds.IsEmpty()) { |
+ if (!bounds.IsEmpty()) |
behdad_google
2011/09/06 16:05:03
The second if is redundant since it's checked in t
xji
2011/09/06 19:54:13
Thanks for catching this.
Actually, we do not chec
|
+ canvas->DrawRectInt(kCursorColor, |
+ bounds.x(), |
+ bounds.y(), |
+ bounds.width(), |
+ bounds.height()); |
+ } |
+} |
+ |
+SelectionModel RenderTextLinux::FindCursorPosition(const Point& point) { |
+ // TODO(xji): when points outside of text, return HOME/END position. |
+ PangoLayout* layout = EnsureLayout(); |
+ |
+ if (text().empty()) |
+ return SelectionModel(0, 0, SelectionModel::LEADING); |
+ |
+ Point p(ToTextPoint(point)); |
+ int caret_pos, trailing; |
+ pango_layout_xy_to_index(layout, p.x() * PANGO_SCALE, p.y() * PANGO_SCALE, |
+ &caret_pos, &trailing); |
+ |
+ size_t selection_end = caret_pos; |
+ if (trailing > 0) { |
+ const char* pango_text = pango_layout_get_text(layout); |
+ const char* ch = g_utf8_offset_to_pointer(pango_text + caret_pos, trailing); |
+ DCHECK_GE(ch, pango_text); |
+ DCHECK_LE(ch, pango_text + strlen(pango_text)); |
+ selection_end = ch - pango_text; |
+ } |
+ |
+ return SelectionModel( |
+ Utf8IndexToUtf16Index(selection_end), |
+ Utf8IndexToUtf16Index(caret_pos), |
behdad_google
2011/09/06 16:05:03
I'm lost about what the two indices to SelectionMo
xji
2011/09/06 19:54:13
Hope the comments in render_text.h explains. I wil
|
+ trailing > 0 ? SelectionModel::TRAILING : SelectionModel::LEADING); |
+} |
+ |
+Rect RenderTextLinux::GetCursorBounds(const SelectionModel& selection, |
+ bool insert_mode) { |
behdad_google
2011/09/06 16:05:03
What does insert_mode==FALSE represent exactly? C
xji
2011/09/06 19:54:13
when insert mode is true, cursor is drawn as a ver
|
+ PangoLayout* layout = EnsureLayout(); |
+ |
+ size_t caret_pos = insert_mode ? selection.caret_pos() : |
+ selection.selection_end(); |
+ PangoRectangle pos; |
+ pango_layout_index_to_pos(layout, Utf16IndexToUtf8Index(caret_pos), &pos); |
+ |
+ SelectionModel::CaretPlacement caret_placement = selection.caret_placement(); |
+ int x = pos.x; |
+ if ((insert_mode && caret_placement == SelectionModel::TRAILING) || |
+ (!insert_mode && pos.width < 0)) |
behdad_google
2011/09/06 16:05:03
I can't understand why you need this if(). Why do
xji
2011/09/06 19:54:13
I do not quite understand your comments.
As to the
|
+ x += pos.width; |
+ x = x / PANGO_SCALE; |
behdad_google
2011/09/06 16:05:03
Use PANGO_PIXELS().
xji
2011/09/06 19:54:13
Done.
|
+ |
+ int h = std::min(display_rect().height(), pos.height / PANGO_SCALE); |
behdad_google
2011/09/06 16:05:03
Use PANGO_PIXELS().
xji
2011/09/06 19:54:13
Done.
|
+ Rect bounds(x, (display_rect().height() - h) / 2, 1, h); |
+ bounds.set_origin(ToViewPoint(bounds.origin())); |
+ |
+ if (!insert_mode) |
+ bounds.set_width(std::abs(pos.width)); |
+ |
+ return bounds; |
+} |
+ |
+SelectionModel RenderTextLinux::GetLeftSelectionModel( |
+ const SelectionModel& current, |
+ BreakType break_type) { |
+ EnsureLayout(); |
+ |
+ if (break_type == LINE_BREAK || text().empty()) |
+ return LeftEndSelectionModel(); |
+ if (break_type == CHARACTER_BREAK) |
+ return LeftSelectionModel(current); |
+ // TODO(xji): not implemented yet. |
+ return RenderText::GetLeftSelectionModel(current, break_type); |
+} |
+ |
+SelectionModel RenderTextLinux::GetRightSelectionModel( |
+ const SelectionModel& current, |
+ BreakType break_type) { |
+ EnsureLayout(); |
+ |
+ if (break_type == LINE_BREAK || text().empty()) |
+ return RightEndSelectionModel(); |
+ if (break_type == CHARACTER_BREAK) |
+ return RightSelectionModel(current); |
+ // TODO(xji): not implemented yet. |
+ return RenderText::GetRightSelectionModel(current, break_type); |
+} |
+ |
+SelectionModel RenderTextLinux::LeftEndSelectionModel() { |
+ if (GetTextDirection() == base::i18n::RIGHT_TO_LEFT) { |
+ GSList* first_visual_run = layout_line_->runs; |
+ if (first_visual_run) { |
behdad_google
2011/09/06 16:05:03
I would change the if to "if (layout_line_->runs)"
xji
2011/09/06 19:54:13
Done.
|
+ PangoItem* item = |
+ reinterpret_cast<PangoLayoutRun*>(first_visual_run->data)->item; |
+ if (item->analysis.level % 2 == 0) { // LTR. |
behdad_google
2011/09/06 16:05:03
Ok, I've reviewed up to here. Will study the Sele
|
+ size_t caret = Utf8IndexToUtf16Index(item->offset); |
+ return SelectionModel(text().length(), caret, SelectionModel::LEADING); |
+ } else { // RTL. |
+ size_t caret = Utf16IndexOfAdjacentGrapheme(item->offset + item->length, |
+ PREVIOUS); |
+ return SelectionModel(text().length(), caret, SelectionModel::TRAILING); |
+ } |
+ } |
+ } |
+ return SelectionModel(0, 0, SelectionModel::LEADING); |
+} |
+ |
+SelectionModel RenderTextLinux::RightEndSelectionModel() { |
+ if (GetTextDirection() == base::i18n::LEFT_TO_RIGHT) { |
+ GSList* last_visual_run = GetLastRun(); |
+ if (last_visual_run) { |
+ PangoItem* item = |
+ reinterpret_cast<PangoLayoutRun*>(last_visual_run->data)->item; |
+ if (item->analysis.level % 2 == 0) { // LTR. |
+ size_t caret = Utf16IndexOfAdjacentGrapheme(item->offset + item->length, |
+ PREVIOUS); |
+ return SelectionModel(text().length(), caret, SelectionModel::TRAILING); |
+ } else { // RTL. |
+ size_t caret = Utf8IndexToUtf16Index(item->offset); |
+ return SelectionModel(text().length(), caret, SelectionModel::LEADING); |
+ } |
+ } |
+ } |
+ return SelectionModel(0, 0, SelectionModel::LEADING); |
+} |
+ |
+size_t RenderTextLinux::GetIndexOfPreviousGrapheme(size_t position) { |
+ EnsureLayout(); |
+ size_t index = Utf16IndexToUtf8Index(position); |
+ return Utf16IndexOfAdjacentGrapheme(index, PREVIOUS); |
+} |
+ |
+size_t RenderTextLinux::GetIndexOfNextGrapheme(size_t position) { |
+ EnsureLayout(); |
+ size_t index = Utf16IndexToUtf8Index(position); |
+ return Utf16IndexOfAdjacentGrapheme(index, NEXT); |
+} |
+ |
+GSList* RenderTextLinux::GetRunContainingPosition(size_t position) const { |
+ GSList* run = layout_line_->runs; |
+ while (run) { |
+ PangoItem* box = reinterpret_cast<PangoLayoutRun*>(run->data)->item; |
+ size_t run_start = Utf8IndexToUtf16Index(box->offset); |
+ size_t run_end = Utf8IndexToUtf16Index(box->offset + box->length); |
+ |
+ if (position >= run_start && position < run_end) |
+ return run; |
+ run = run->next; |
+ } |
+ return NULL; |
+} |
+ |
+size_t RenderTextLinux::Utf8IndexOfAdjacentGrapheme( |
+ size_t utf8_index_of_current_grapheme, |
+ RelativeLogicalPosition pos) const { |
+ PangoLogAttr* log_attrs; |
+ gint n_attrs; |
+ pango_layout_get_log_attrs(layout_, &log_attrs, &n_attrs); |
+ |
+ const char* layout_text = pango_layout_get_text(layout_); |
+ const char* ch = layout_text + utf8_index_of_current_grapheme; |
+ int char_offset = static_cast<int>(g_utf8_pointer_to_offset(layout_text, ch)); |
+ |
+ if (pos == PREVIOUS) { |
+ if (ch > layout_text) { |
+ do { |
+ ch = g_utf8_find_prev_char(layout_text, ch); |
+ --char_offset; |
+ } while (ch && *ch && !log_attrs[char_offset].is_cursor_position); |
+ if (!ch) |
+ ch = layout_text; |
+ } |
+ } else { |
+ if (ch < layout_text + strlen(layout_text)) { |
+ do { |
+ ch = g_utf8_find_next_char(ch, NULL); |
+ ++char_offset; |
+ } while (ch && *ch && !log_attrs[char_offset].is_cursor_position); |
+ if (!ch || ch >= layout_text + strlen(layout_text)) |
+ ch = layout_text + strlen(layout_text); |
+ } |
+ } |
+ |
+ size_t utf8_index = static_cast<size_t>(ch - layout_text); |
+ g_free(log_attrs); |
+ return utf8_index; |
+} |
+ |
+size_t RenderTextLinux::Utf16IndexOfAdjacentGrapheme( |
+ size_t utf8_index_of_current_grapheme, |
+ RelativeLogicalPosition pos) const { |
+ size_t utf8_index = Utf8IndexOfAdjacentGrapheme( |
+ utf8_index_of_current_grapheme, pos); |
+ return Utf8IndexToUtf16Index(utf8_index); |
+} |
+ |
+SelectionModel RenderTextLinux::FirstSelectionModelInsideRun( |
+ const PangoItem* run) const { |
+ size_t caret = Utf8IndexToUtf16Index(run->offset); |
+ size_t cursor = Utf16IndexOfAdjacentGrapheme(run->offset, NEXT); |
+ return SelectionModel(cursor, caret, SelectionModel::TRAILING); |
+} |
+ |
+SelectionModel RenderTextLinux::LastSelectionModelInsideRun( |
+ const PangoItem* run) const { |
+ size_t caret = Utf16IndexOfAdjacentGrapheme(run->offset + run->length, |
+ PREVIOUS); |
+ return SelectionModel(caret, caret, SelectionModel::LEADING); |
+} |
+ |
+// Assume caret_pos in |current| is n, 'l' represents leading in |
+// caret_placement and 't' represents trailing in caret_placement. Following |
+// is the calculation from (caret_pos, caret_placement) in |current| to |
+// (selection_end, caret_pos, caret_placement) when moving cursor left by |
+// one grapheme (for simplicity, assume each grapheme is one character). |
+// If n is in LTR run, |
+// (n, t) ---> (n, n, l). |
+// (n, l) ---> (n-1, n-1, l) if n is inside run (not at boundary). |
+// (n, l) ---> goto across run case if n is at run boundary. |
+// If n is in RTL run, |
+// (n, l) --> (n+1, n, t). |
+// (n, t) --> (n+2, n+1, t) if n is inside run. |
+// (n, t) --> goto across run case if n is at run boundary. |
+// If n is at run boundary, get its visually left run, |
+// If left run is LTR run, |
+// (n, t) --> (left run's end, left run's end, l). |
+// If left run is RTL run, |
+// (n, t) --> (left run's begin + 1, left run's begin, t). |
+SelectionModel RenderTextLinux::LeftSelectionModel( |
+ const SelectionModel& selection) { |
+ size_t caret = selection.caret_pos(); |
+ SelectionModel::CaretPlacement caret_placement = selection.caret_placement(); |
+ GSList* run = GetRunContainingPosition(caret); |
+ DCHECK(run); |
+ |
+ PangoItem* box = reinterpret_cast<PangoLayoutRun*>(run->data)->item; |
+ size_t run_start = Utf8IndexToUtf16Index(box->offset); |
+ size_t run_end = Utf8IndexToUtf16Index(box->offset + box->length); |
+ |
+ if (box->analysis.level % 2 == 0) { // LTR run. |
+ if (caret_placement == SelectionModel::TRAILING) |
+ return SelectionModel(caret, caret, SelectionModel::LEADING); |
+ else if (caret > run_start) { |
+ caret = GetIndexOfPreviousGrapheme(caret); |
+ return SelectionModel(caret, caret, SelectionModel::LEADING); |
+ } |
+ } else { // RTL run. |
+ if (caret_placement == SelectionModel::LEADING) { |
+ size_t cursor = GetIndexOfNextGrapheme(caret); |
+ return SelectionModel(cursor, caret, SelectionModel::TRAILING); |
+ } else if (selection.selection_end() < run_end) { |
+ caret = GetIndexOfNextGrapheme(caret); |
+ size_t cursor = GetIndexOfNextGrapheme(caret); |
+ return SelectionModel(cursor, caret, SelectionModel::TRAILING); |
+ } |
+ } |
+ |
+ // The character is at the begin of its run; advance to the previous visual |
+ // run. |
+ GSList* prev_run = GetPreviousRun(run); |
+ if (!prev_run) // TODO(xji): check whether this is expected. |
+ return LeftEndSelectionModel(); |
+ |
+ box = reinterpret_cast<PangoLayoutRun*>(prev_run->data)->item; |
+ return (box->analysis.level % 2) ? FirstSelectionModelInsideRun(box) : |
+ LastSelectionModelInsideRun(box); |
+} |
+ |
+// Assume caret_pos in |current| is n, 'l' represents leading in |
+// caret_placement and 't' represents trailing in caret_placement. Following |
+// is the calculation from (caret_pos, caret_placement) in |current| to |
+// (selection_end, caret_pos, caret_placement) when moving cursor right by |
+// one grapheme (for simplicity, assume each grapheme is one character). |
+// If n is in LTR run, |
+// (n, l) ---> (n+1, n, t). |
+// (n, t) ---> (n+2, n+1, t) if n is inside run (not at boundary). |
+// (n, t) ---> goto across run case if n is at run boundary. |
+// If n is in RTL run, |
+// (n, t) --> (n, n, l). |
+// (n, l) --> (n-1, n-1, l) if n is inside run. |
+// (n, l) --> goto across run case if n is at run boundary. |
+// If n is at run boundary, get its visually right run, |
+// If right run is LTR run, |
+// (n, t) --> (right run's begin + 1, right run's begin, t). |
+// If right run is RTL run, |
+// (n, t) --> (right run's end, right run's end, l). |
+SelectionModel RenderTextLinux::RightSelectionModel( |
+ const SelectionModel& selection) { |
+ size_t caret = selection.caret_pos(); |
+ SelectionModel::CaretPlacement caret_placement = selection.caret_placement(); |
+ GSList* run = GetRunContainingPosition(caret); |
+ DCHECK(run); |
+ |
+ PangoItem* box = reinterpret_cast<PangoLayoutRun*>(run->data)->item; |
+ size_t run_start = Utf8IndexToUtf16Index(box->offset); |
+ size_t run_end = Utf8IndexToUtf16Index(box->offset + box->length); |
+ |
+ if (box->analysis.level % 2 == 0) { // LTR run. |
+ if (caret_placement == SelectionModel::LEADING) { |
+ size_t cursor = GetIndexOfNextGrapheme(caret); |
+ return SelectionModel(cursor, caret, SelectionModel::TRAILING); |
+ } else if (selection.selection_end() < run_end) { |
+ caret = GetIndexOfNextGrapheme(caret); |
+ size_t cursor = GetIndexOfNextGrapheme(caret); |
+ return SelectionModel(cursor, caret, SelectionModel::TRAILING); |
+ } |
+ } else { // RTL run. |
+ if (caret_placement == SelectionModel::TRAILING) |
+ return SelectionModel(caret, caret, SelectionModel::LEADING); |
+ else if (caret > run_start) { |
+ caret = GetIndexOfPreviousGrapheme(caret); |
+ return SelectionModel(caret, caret, SelectionModel::LEADING); |
+ } |
+ } |
+ |
+ // The character is at the end of its run; advance to the next visual run. |
+ GSList* next_run = run->next; |
+ if (!next_run) // TODO(xji): what is the expected behavior? |
+ return RightEndSelectionModel(); |
+ |
+ box = reinterpret_cast<PangoLayoutRun*>(next_run->data)->item; |
+ return (box->analysis.level % 2) ? LastSelectionModelInsideRun(box) : |
+ FirstSelectionModelInsideRun(box); |
+} |
+ |
+PangoLayout* RenderTextLinux::EnsureLayout() { |
+ if (layout_ == NULL) { |
+ CanvasSkia canvas(display_rect().width(), display_rect().height(), false); |
+ skia::ScopedPlatformPaint scoped_platform_paint(&canvas); |
+ cairo_t* cr = scoped_platform_paint.GetPlatformSurface(); |
+ |
+ layout_ = pango_cairo_create_layout(cr); |
+ SetupPangoLayout( |
+ layout_, |
+ text(), |
+ default_style().font, |
+ display_rect().width(), |
+ base::i18n::GetFirstStrongCharacterDirection(text()), |
+ CanvasSkia::DefaultCanvasTextAlignment()); |
+ |
+ pango_layout_set_width(layout_, -1); |
+ pango_layout_set_height(layout_, display_rect().height() * PANGO_SCALE); |
+ SetupPangoAttributes(layout_); |
+ |
+ layout_line_ = pango_layout_get_line_readonly(layout_, 0); |
+ pango_layout_line_ref(layout_line_); |
+ } |
+ return layout_; |
+} |
+ |
+void RenderTextLinux::ResetLayout() { |
+ // set_cached_bounds_and_offset_valid(false) is done in RenderText for every |
+ // operation that triggers ResetLayout(). |
+ if (layout_) { |
+ g_object_unref(layout_); |
+ layout_ = NULL; |
+ } |
+ if (layout_line_) { |
+ pango_layout_line_unref(layout_line_); |
+ layout_line_ = NULL; |
+ } |
+} |
+ |
+void RenderTextLinux::SetupPangoAttributes(PangoLayout* layout) { |
+ PangoAttrList* attrs = pango_attr_list_new(); |
+ // Set selection background color. |
+ SkColor selection_color = |
+ focused() ? kFocusedSelectionColor : kUnfocusedSelectionColor; |
+ size_t start = std::min(MinOfSelection(), text().length()); |
+ size_t end = std::min(MaxOfSelection(), text().length()); |
+ PangoAttribute* pango_attr; |
+ if (end > start) { |
+ pango_attr = pango_attr_background_new( |
+ ConvertColorFrom8BitTo16Bit(SkColorGetR(selection_color)), |
+ ConvertColorFrom8BitTo16Bit(SkColorGetG(selection_color)), |
+ ConvertColorFrom8BitTo16Bit(SkColorGetB(selection_color))); |
+ AppendPangoAttribute(start, end, pango_attr, attrs); |
+ } |
+ |
+ StyleRanges ranges_of_style(style_ranges()); |
+ ApplyCompositionAndSelectionStyles(&ranges_of_style); |
+ |
+ for (StyleRanges::const_iterator i = ranges_of_style.begin(); |
+ i < ranges_of_style.end(); ++i) { |
+ start = std::min(i->range.start(), text().length()); |
+ end = std::min(i->range.end(), text().length()); |
+ if (start >= end) |
+ continue; |
+ |
+ const Font& font = !i->underline ? i->font : |
+ i->font.DeriveFont(0, i->font.GetStyle() | Font::UNDERLINED); |
+ PangoFontDescription* desc = font.GetNativeFont(); |
+ pango_attr = pango_attr_font_desc_new(desc); |
+ AppendPangoAttribute(start, end, pango_attr, attrs); |
+ pango_font_description_free(desc); |
+ |
+ SkColor foreground = i->foreground; |
+ pango_attr = pango_attr_foreground_new( |
+ ConvertColorFrom8BitTo16Bit(SkColorGetR(foreground)), |
+ ConvertColorFrom8BitTo16Bit(SkColorGetG(foreground)), |
+ ConvertColorFrom8BitTo16Bit(SkColorGetB(foreground))); |
+ AppendPangoAttribute(start, end, pango_attr, attrs); |
+ |
+ if (i->strike) { |
+ pango_attr = pango_attr_strikethrough_new(true); |
+ AppendPangoAttribute(start, end, pango_attr, attrs); |
+ } |
+ } |
+ |
+ pango_layout_set_attributes(layout, attrs); |
+ pango_attr_list_unref(attrs); |
+} |
+ |
+void RenderTextLinux::AppendPangoAttribute(size_t start, |
+ size_t end, |
+ PangoAttribute* pango_attr, |
+ PangoAttrList* attrs) { |
+ pango_attr->start_index = Utf16IndexToUtf8Index(start); |
+ pango_attr->end_index = Utf16IndexToUtf8Index(end); |
+ pango_attr_list_insert(attrs, pango_attr); |
+} |
+ |
+GSList* RenderTextLinux::GetPreviousRun(GSList* run) const { |
+ GSList* current = layout_line_->runs; |
+ GSList* prev = NULL; |
+ while (current) { |
+ if (current == run) |
+ return prev; |
+ prev = current; |
+ current = current->next; |
+ } |
+ return NULL; |
+} |
+ |
+GSList* RenderTextLinux::GetLastRun() const { |
+ GSList* current = layout_line_->runs; |
+ while (current && current->next) { |
+ current = current->next; |
+ } |
+ return current; |
+} |
+ |
+size_t RenderTextLinux::Utf16IndexToUtf8Index(size_t index) const { |
+ int32_t utf8_index = 0; |
+ UErrorCode ec = U_ZERO_ERROR; |
+ u_strToUTF8(NULL, 0, &utf8_index, text().data(), index, &ec); |
+ // Even given a destination buffer as NULL and destination capacity as 0, |
+ // if the output length is equal to or greater than the capacity, then the |
+ // UErrorCode is set to U_STRING_NOT_TERMINATED_WARNING or |
+ // U_BUFFER_OVERFLOW_ERROR respectively. |
+ // Please refer to |
+ // http://userguide.icu-project.org/strings#TOC-Using-C-Strings:-NUL-Terminated-vs |
+ // for detail (search for "Note that" below "Preflighting"). |
+ DCHECK(ec == U_BUFFER_OVERFLOW_ERROR || |
+ ec == U_STRING_NOT_TERMINATED_WARNING); |
+ return utf8_index; |
+} |
+ |
+size_t RenderTextLinux::Utf8IndexToUtf16Index(size_t index) const { |
+ int32_t utf16_index = 0; |
+ UErrorCode ec = U_ZERO_ERROR; |
+ const char* layout_text = pango_layout_get_text(layout_); |
+ u_strFromUTF8(NULL, 0, &utf16_index, layout_text, index, &ec); |
+ DCHECK(ec == U_BUFFER_OVERFLOW_ERROR || |
+ ec == U_STRING_NOT_TERMINATED_WARNING); |
+ return utf16_index; |
+} |
+ |
} // namespace gfx |