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

Unified Diff: ui/gfx/render_text_linux.cc

Issue 7511029: Implement Pango RenderText for Linux. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: add dcheck and todo Created 9 years, 4 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 side-by-side diff with in-line comments
Download patch
Index: ui/gfx/render_text_linux.cc
===================================================================
--- ui/gfx/render_text_linux.cc (revision 97884)
+++ ui/gfx/render_text_linux.cc (working copy)
@@ -4,17 +4,584 @@
#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.
+int ConvertColorFrom8BitTo16Bit(int c) {
+ return (c << 8) + c;
+}
+
+}
msw 2011/08/29 20:38:05 } // namespace
xji 2011/08/30 00:39:41 Done.
+
+// 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));
+ 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);
+ 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());
+
+ // TODO(xji): use ScopedPlatformPaint.
msw 2011/08/29 20:38:05 You should be able to do: skia::ScopedPlatformPain
xji 2011/08/30 00:39:41 Done.
+ cairo_t* cr = skia::BeginPlatformPaint(canvas_skia);
+ cairo_save(cr);
+ cairo_rectangle(cr, bounds.x(), bounds.y(), bounds.width(), bounds.height());
+ cairo_clip(cr);
+
+ 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);
+ cairo_move_to(cr, offset.x(), text_y);
+ pango_cairo_show_layout(cr, layout);
msw 2011/08/29 20:38:05 We ought to check with a Pango/Skia expert to dete
xji 2011/08/30 00:39:41 talked with Mike Reed, who said that "The only way
msw 2011/08/30 01:17:07 Ok, that's definitely outside the scope of this ch
+
+ // Destructor.
+ cairo_restore(cr);
+ skia::EndPlatformPaint(canvas_skia);
+
+ // Paint cursor.
+ bounds = GetUpdatedCursorBounds();
+ if (cursor_visible() && focused() && !bounds.IsEmpty()) {
+ if (!bounds.IsEmpty())
+ canvas->DrawRectInt(kCursorColor,
msw 2011/08/29 20:38:05 outdent this line by one space.
xji 2011/08/30 00:39:41 Done.
+ 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),
+ trailing > 0 ? SelectionModel::TRAILING : SelectionModel::LEADING);
+}
+
+Rect RenderTextLinux::GetCursorBounds(const SelectionModel& selection,
+ bool insert_mode) {
+ 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))
+ x += pos.width;
+ x = x / PANGO_SCALE;
+
+ int h = std::min(display_rect().height(), pos.height / PANGO_SCALE);
+ Rect bounds(x, (display_rect().height() - h) / 2, 0, 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);
+}
+
+size_t RenderTextLinux::GetIndexOfPreviousGrapheme(size_t position) {
+ EnsureLayout();
+ return Utf16IndexOfAdjacentGrapheme(position, PREVIOUS);
+}
+
+SelectionModel RenderTextLinux::LeftEndSelectionModel() {
+ if (GetTextDirection() == base::i18n::RIGHT_TO_LEFT) {
+ GSList* first_visual_run = layout_line_->runs;
+ if (first_visual_run) {
+ PangoItem* item =
+ reinterpret_cast<PangoLayoutRun*>(first_visual_run->data)->item;
+ if (item->analysis.level % 2 == 0) { // LTR.
+ size_t caret = Utf8IndexToUtf16Index(item->offset);
+ return SelectionModel(text().length(), caret, SelectionModel::LEADING);
msw 2011/08/29 20:38:05 Why do you set the cursor to text().length() here?
xji 2011/08/30 00:39:41 pango's text direction is determined by its first
msw 2011/08/30 01:17:07 Ah, you're right. Sorry, this behavior can just be
xji 2011/08/30 04:39:39 Yes. that is right.
+ } else { // RTL.
+ // TODO(xji): Utf8IndexToUtf16Index conversion should be able to avoid.
+ size_t caret = Utf8IndexToUtf16Index(item->offset + item->length);
+ caret = Utf16IndexOfAdjacentGrapheme(caret, PREVIOUS);
+ return SelectionModel(text().length(), caret, SelectionModel::TRAILING);
msw 2011/08/29 20:38:05 Shouldn't this cursor be at position corresponding
xji 2011/08/30 00:39:41 same reason as above. in RTL context, Left end is
+ }
+ }
+ }
+ 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.
+ // TODO(xji): Utf8IndexToUtf16Index conversion should be able to avoid.
+ size_t caret = Utf8IndexToUtf16Index(item->offset + item->length);
+ caret = Utf16IndexOfAdjacentGrapheme(caret, PREVIOUS);
+ return SelectionModel(text().length(), caret, SelectionModel::TRAILING);
msw 2011/08/29 20:38:05 Shouldn't this cursor be at item->offset + item->l
xji 2011/08/30 00:39:41 ditto. in LTR context, right end is logical end, c
+ } else { // RTL.
+ size_t caret = Utf8IndexToUtf16Index(item->offset);
+ return SelectionModel(text().length(), caret, SelectionModel::LEADING);
msw 2011/08/29 20:38:05 shouldn't this cursor be at item->offset?
xji 2011/08/30 00:39:41 ditto. in LTR context, right end is logical end, c
+ }
+ }
+ }
+ return SelectionModel(0, 0, SelectionModel::LEADING);
+}
+
+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 utf16_index_of_current_grapheme,
+ RelativeLogicalPosition pos) const {
msw 2011/08/29 20:38:05 I implemented a similar function: size_t IndexOfAd
+ 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 +
+ Utf16IndexToUtf8Index(utf16_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 utf16_index_of_current_grapheme,
+ RelativeLogicalPosition pos) const {
+ size_t utf8_index = Utf8IndexOfAdjacentGrapheme(
+ utf16_index_of_current_grapheme, pos);
+ return Utf8IndexToUtf16Index(utf8_index);
+}
+
+SelectionModel RenderTextLinux::FirstSelectionModelInsideRun(
+ const PangoItem* run) const {
+ // TODO(xji): Utf8IndexToUtf16Index conversion should be able to avoid.
+ size_t caret = Utf8IndexToUtf16Index(run->offset);
+ size_t cursor = Utf16IndexOfAdjacentGrapheme(caret, NEXT);
+ return SelectionModel(cursor, caret, SelectionModel::TRAILING);
+}
+
+SelectionModel RenderTextLinux::LastSelectionModelInsideRun(
+ const PangoItem* run) const {
+ // TODO(xji): Utf8IndexToUtf16Index conversion should be able to avoid.
+ size_t caret = Utf8IndexToUtf16Index(run->offset + run->length);
+ caret = Utf16IndexOfAdjacentGrapheme(caret, 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 = Utf16IndexOfAdjacentGrapheme(caret, PREVIOUS);
+ return SelectionModel(caret, caret, SelectionModel::LEADING);
+ }
+ } else { // RTL run.
+ if (caret_placement == SelectionModel::LEADING) {
+ size_t cursor = Utf16IndexOfAdjacentGrapheme(caret, NEXT);
+ return SelectionModel(cursor, caret, SelectionModel::TRAILING);
+ } else if (selection.selection_end() < run_end) {
+ caret = Utf16IndexOfAdjacentGrapheme(caret, NEXT);
+ size_t cursor = Utf16IndexOfAdjacentGrapheme(caret, NEXT);
+ 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.
msw 2011/08/29 20:38:05 If the caret is at the beginning of the first run,
xji 2011/08/30 00:39:41 Given an example, logical text "ABCdef", visually
+ 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 = Utf16IndexOfAdjacentGrapheme(caret, NEXT);
+ return SelectionModel(cursor, caret, SelectionModel::TRAILING);
+ } else if (selection.selection_end() < run_end) {
+ caret = Utf16IndexOfAdjacentGrapheme(caret, NEXT);
+ size_t cursor = Utf16IndexOfAdjacentGrapheme(caret, NEXT);
+ 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 = Utf16IndexOfAdjacentGrapheme(caret, PREVIOUS);
+ 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?
msw 2011/08/29 20:38:05 If the caret happens to be greater than or equal t
xji 2011/08/30 00:39:41 ditto as above reason. consider case "abcDEF" visu
+ 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);
+ // TODO(xji): can use ScopedPlatformPaint ?
msw 2011/08/29 20:38:05 You should be able to do: skia::ScopedPlatformPain
xji 2011/08/30 00:39:41 Done.
+ cairo_t* cr = skia::BeginPlatformPaint(&canvas);
+
+ layout_ = pango_cairo_create_layout(cr);
+ SetupPangoLayout(
+ layout_,
+ text(),
+ default_style().font,
+ display_rect().width(),
+ base::i18n::GetFirstStrongCharacterDirection(text()),
+ CanvasSkia::DefaultCanvasTextAlignment());
+
+ pango_layout_set_height(layout_, display_rect().height() * PANGO_SCALE);
+ SetupPangoAttributes(layout_);
+
+ skia::EndPlatformPaint(&canvas);
+
+ 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 trigger ResetLayout().
msw 2011/08/29 20:38:05 trigger*s*, and we should probably add some common
xji 2011/08/30 00:39:41 Done. agree on the consolidation part.
+ 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);
msw 2011/08/29 20:38:05 This function is private, and all the callers chec
xji 2011/08/30 00:39:41 I am removing the 'end' check here. it should chec
+ size_t e = std::min(end, text().length());
+ pango_attr->end_index = Utf16IndexToUtf8Index(e);
+ 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

Powered by Google App Engine
This is Rietveld 408576698