| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "ui/gfx/render_text_linux.h" | |
| 6 | |
| 7 #include <pango/pangocairo.h> | |
| 8 #include <algorithm> | |
| 9 #include <string> | |
| 10 #include <vector> | |
| 11 | |
| 12 #include "base/i18n/break_iterator.h" | |
| 13 #include "base/logging.h" | |
| 14 #include "third_party/skia/include/core/SkTypeface.h" | |
| 15 #include "ui/gfx/canvas.h" | |
| 16 #include "ui/gfx/font.h" | |
| 17 #include "ui/gfx/font_render_params_linux.h" | |
| 18 #include "ui/gfx/pango_util.h" | |
| 19 #include "ui/gfx/utf16_indexing.h" | |
| 20 | |
| 21 namespace gfx { | |
| 22 | |
| 23 namespace { | |
| 24 | |
| 25 // Returns the preceding element in a GSList (O(n)). | |
| 26 GSList* GSListPrevious(GSList* head, GSList* item) { | |
| 27 GSList* prev = NULL; | |
| 28 for (GSList* cur = head; cur != item; cur = cur->next) { | |
| 29 DCHECK(cur); | |
| 30 prev = cur; | |
| 31 } | |
| 32 return prev; | |
| 33 } | |
| 34 | |
| 35 // Returns true if the given visual cursor |direction| is logically forward | |
| 36 // motion in the given Pango |item|. | |
| 37 bool IsForwardMotion(VisualCursorDirection direction, const PangoItem* item) { | |
| 38 bool rtl = item->analysis.level & 1; | |
| 39 return rtl == (direction == CURSOR_LEFT); | |
| 40 } | |
| 41 | |
| 42 // Checks whether |range| contains |index|. This is not the same as calling | |
| 43 // |range.Contains(gfx::Range(index))| - as that would return true when | |
| 44 // |index| == |range.end()|. | |
| 45 bool IndexInRange(const Range& range, size_t index) { | |
| 46 return index >= range.start() && index < range.end(); | |
| 47 } | |
| 48 | |
| 49 // Sets underline metrics on |renderer| according to Pango font |desc|. | |
| 50 void SetPangoUnderlineMetrics(PangoFontDescription *desc, | |
| 51 internal::SkiaTextRenderer* renderer) { | |
| 52 PangoFontMetrics* metrics = GetPangoFontMetrics(desc); | |
| 53 int thickness = pango_font_metrics_get_underline_thickness(metrics); | |
| 54 // Pango returns the position "above the baseline". Change its sign to convert | |
| 55 // it to a vertical offset from the baseline. | |
| 56 int position = -pango_font_metrics_get_underline_position(metrics); | |
| 57 pango_quantize_line_geometry(&thickness, &position); | |
| 58 // Note: pango_quantize_line_geometry() guarantees pixel boundaries, so | |
| 59 // PANGO_PIXELS() is safe to use. | |
| 60 renderer->SetUnderlineMetrics(PANGO_PIXELS(thickness), | |
| 61 PANGO_PIXELS(position)); | |
| 62 } | |
| 63 | |
| 64 } // namespace | |
| 65 | |
| 66 // TODO(xji): index saved in upper layer is utf16 index. Pango uses utf8 index. | |
| 67 // Since caret_pos is used internally, we could save utf8 index for caret_pos | |
| 68 // to avoid conversion. | |
| 69 | |
| 70 RenderTextLinux::RenderTextLinux() | |
| 71 : layout_(NULL), | |
| 72 current_line_(NULL), | |
| 73 log_attrs_(NULL), | |
| 74 num_log_attrs_(0), | |
| 75 layout_text_(NULL) { | |
| 76 } | |
| 77 | |
| 78 RenderTextLinux::~RenderTextLinux() { | |
| 79 ResetLayout(); | |
| 80 } | |
| 81 | |
| 82 Size RenderTextLinux::GetStringSize() { | |
| 83 EnsureLayout(); | |
| 84 int width = 0, height = 0; | |
| 85 pango_layout_get_pixel_size(layout_, &width, &height); | |
| 86 // Keep a consistent height between this particular string's PangoLayout and | |
| 87 // potentially larger text supported by the FontList. | |
| 88 // For example, if a text field contains a Japanese character, which is | |
| 89 // smaller than Latin ones, and then later a Latin one is inserted, this | |
| 90 // ensures that the text baseline does not shift. | |
| 91 return Size(width, std::max(height, font_list().GetHeight())); | |
| 92 } | |
| 93 | |
| 94 SelectionModel RenderTextLinux::FindCursorPosition(const Point& point) { | |
| 95 EnsureLayout(); | |
| 96 | |
| 97 if (text().empty()) | |
| 98 return SelectionModel(0, CURSOR_FORWARD); | |
| 99 | |
| 100 Point p(ToTextPoint(point)); | |
| 101 | |
| 102 // When the point is outside of text, return HOME/END position. | |
| 103 if (p.x() < 0) | |
| 104 return EdgeSelectionModel(CURSOR_LEFT); | |
| 105 if (p.x() > GetStringSize().width()) | |
| 106 return EdgeSelectionModel(CURSOR_RIGHT); | |
| 107 | |
| 108 int caret_pos = 0, trailing = 0; | |
| 109 pango_layout_xy_to_index(layout_, p.x() * PANGO_SCALE, p.y() * PANGO_SCALE, | |
| 110 &caret_pos, &trailing); | |
| 111 | |
| 112 DCHECK_GE(trailing, 0); | |
| 113 if (trailing > 0) { | |
| 114 caret_pos = g_utf8_offset_to_pointer(layout_text_ + caret_pos, | |
| 115 trailing) - layout_text_; | |
| 116 DCHECK_LE(static_cast<size_t>(caret_pos), strlen(layout_text_)); | |
| 117 } | |
| 118 | |
| 119 return SelectionModel(LayoutIndexToTextIndex(caret_pos), | |
| 120 (trailing > 0) ? CURSOR_BACKWARD : CURSOR_FORWARD); | |
| 121 } | |
| 122 | |
| 123 std::vector<RenderText::FontSpan> RenderTextLinux::GetFontSpansForTesting() { | |
| 124 EnsureLayout(); | |
| 125 | |
| 126 std::vector<RenderText::FontSpan> spans; | |
| 127 for (GSList* it = current_line_->runs; it; it = it->next) { | |
| 128 PangoItem* item = reinterpret_cast<PangoLayoutRun*>(it->data)->item; | |
| 129 const int start = LayoutIndexToTextIndex(item->offset); | |
| 130 const int end = LayoutIndexToTextIndex(item->offset + item->length); | |
| 131 const Range range(start, end); | |
| 132 | |
| 133 ScopedPangoFontDescription desc(pango_font_describe(item->analysis.font)); | |
| 134 spans.push_back(RenderText::FontSpan(Font(desc.get()), range)); | |
| 135 } | |
| 136 | |
| 137 return spans; | |
| 138 } | |
| 139 | |
| 140 int RenderTextLinux::GetLayoutTextBaseline() { | |
| 141 EnsureLayout(); | |
| 142 return PANGO_PIXELS(pango_layout_get_baseline(layout_)); | |
| 143 } | |
| 144 | |
| 145 SelectionModel RenderTextLinux::AdjacentCharSelectionModel( | |
| 146 const SelectionModel& selection, | |
| 147 VisualCursorDirection direction) { | |
| 148 GSList* run = GetRunContainingCaret(selection); | |
| 149 if (!run) { | |
| 150 // The cursor is not in any run: we're at the visual and logical edge. | |
| 151 SelectionModel edge = EdgeSelectionModel(direction); | |
| 152 if (edge.caret_pos() == selection.caret_pos()) | |
| 153 return edge; | |
| 154 else | |
| 155 run = (direction == CURSOR_RIGHT) ? | |
| 156 current_line_->runs : g_slist_last(current_line_->runs); | |
| 157 } else { | |
| 158 // If the cursor is moving within the current run, just move it by one | |
| 159 // grapheme in the appropriate direction. | |
| 160 PangoItem* item = reinterpret_cast<PangoLayoutRun*>(run->data)->item; | |
| 161 size_t caret = selection.caret_pos(); | |
| 162 if (IsForwardMotion(direction, item)) { | |
| 163 if (caret < LayoutIndexToTextIndex(item->offset + item->length)) { | |
| 164 caret = IndexOfAdjacentGrapheme(caret, CURSOR_FORWARD); | |
| 165 return SelectionModel(caret, CURSOR_BACKWARD); | |
| 166 } | |
| 167 } else { | |
| 168 if (caret > LayoutIndexToTextIndex(item->offset)) { | |
| 169 caret = IndexOfAdjacentGrapheme(caret, CURSOR_BACKWARD); | |
| 170 return SelectionModel(caret, CURSOR_FORWARD); | |
| 171 } | |
| 172 } | |
| 173 // The cursor is at the edge of a run; move to the visually adjacent run. | |
| 174 // TODO(xji): Keep a vector of runs to avoid using a singly-linked list. | |
| 175 run = (direction == CURSOR_RIGHT) ? | |
| 176 run->next : GSListPrevious(current_line_->runs, run); | |
| 177 if (!run) | |
| 178 return EdgeSelectionModel(direction); | |
| 179 } | |
| 180 PangoItem* item = reinterpret_cast<PangoLayoutRun*>(run->data)->item; | |
| 181 return IsForwardMotion(direction, item) ? | |
| 182 FirstSelectionModelInsideRun(item) : LastSelectionModelInsideRun(item); | |
| 183 } | |
| 184 | |
| 185 SelectionModel RenderTextLinux::AdjacentWordSelectionModel( | |
| 186 const SelectionModel& selection, | |
| 187 VisualCursorDirection direction) { | |
| 188 if (obscured()) | |
| 189 return EdgeSelectionModel(direction); | |
| 190 | |
| 191 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD); | |
| 192 bool success = iter.Init(); | |
| 193 DCHECK(success); | |
| 194 if (!success) | |
| 195 return selection; | |
| 196 | |
| 197 SelectionModel cur(selection); | |
| 198 for (;;) { | |
| 199 cur = AdjacentCharSelectionModel(cur, direction); | |
| 200 GSList* run = GetRunContainingCaret(cur); | |
| 201 if (!run) | |
| 202 break; | |
| 203 PangoItem* item = reinterpret_cast<PangoLayoutRun*>(run->data)->item; | |
| 204 size_t cursor = cur.caret_pos(); | |
| 205 if (IsForwardMotion(direction, item) ? | |
| 206 iter.IsEndOfWord(cursor) : iter.IsStartOfWord(cursor)) | |
| 207 break; | |
| 208 } | |
| 209 | |
| 210 return cur; | |
| 211 } | |
| 212 | |
| 213 Range RenderTextLinux::GetGlyphBounds(size_t index) { | |
| 214 PangoRectangle pos; | |
| 215 pango_layout_index_to_pos(layout_, TextIndexToLayoutIndex(index), &pos); | |
| 216 // TODO(derat): Support fractional ranges for subpixel positioning? | |
| 217 return Range(PANGO_PIXELS(pos.x), PANGO_PIXELS(pos.x + pos.width)); | |
| 218 } | |
| 219 | |
| 220 std::vector<Rect> RenderTextLinux::GetSubstringBounds(const Range& range) { | |
| 221 DCHECK_LE(range.GetMax(), text().length()); | |
| 222 if (range.is_empty()) | |
| 223 return std::vector<Rect>(); | |
| 224 | |
| 225 EnsureLayout(); | |
| 226 int* ranges = NULL; | |
| 227 int n_ranges = 0; | |
| 228 pango_layout_line_get_x_ranges(current_line_, | |
| 229 TextIndexToLayoutIndex(range.GetMin()), | |
| 230 TextIndexToLayoutIndex(range.GetMax()), | |
| 231 &ranges, | |
| 232 &n_ranges); | |
| 233 | |
| 234 const int height = GetStringSize().height(); | |
| 235 | |
| 236 std::vector<Rect> bounds; | |
| 237 for (int i = 0; i < n_ranges; ++i) { | |
| 238 // TODO(derat): Support fractional bounds for subpixel positioning? | |
| 239 int x = PANGO_PIXELS(ranges[2 * i]); | |
| 240 int width = PANGO_PIXELS(ranges[2 * i + 1]) - x; | |
| 241 Rect rect(x, 0, width, height); | |
| 242 rect.set_origin(ToViewPoint(rect.origin())); | |
| 243 bounds.push_back(rect); | |
| 244 } | |
| 245 g_free(ranges); | |
| 246 return bounds; | |
| 247 } | |
| 248 | |
| 249 size_t RenderTextLinux::TextIndexToLayoutIndex(size_t index) const { | |
| 250 DCHECK(layout_); | |
| 251 ptrdiff_t offset = gfx::UTF16IndexToOffset(text(), 0, index); | |
| 252 // Clamp layout indices to the length of the text actually used for layout. | |
| 253 offset = std::min<size_t>(offset, g_utf8_strlen(layout_text_, -1)); | |
| 254 const char* layout_pointer = g_utf8_offset_to_pointer(layout_text_, offset); | |
| 255 return (layout_pointer - layout_text_); | |
| 256 } | |
| 257 | |
| 258 size_t RenderTextLinux::LayoutIndexToTextIndex(size_t index) const { | |
| 259 DCHECK(layout_); | |
| 260 const char* layout_pointer = layout_text_ + index; | |
| 261 const long offset = g_utf8_pointer_to_offset(layout_text_, layout_pointer); | |
| 262 return gfx::UTF16OffsetToIndex(text(), 0, offset); | |
| 263 } | |
| 264 | |
| 265 bool RenderTextLinux::IsCursorablePosition(size_t position) { | |
| 266 if (position == 0 && text().empty()) | |
| 267 return true; | |
| 268 if (position >= text().length()) | |
| 269 return position == text().length(); | |
| 270 if (!gfx::IsValidCodePointIndex(text(), position)) | |
| 271 return false; | |
| 272 | |
| 273 EnsureLayout(); | |
| 274 ptrdiff_t offset = gfx::UTF16IndexToOffset(text(), 0, position); | |
| 275 // Check that the index corresponds with a valid text code point, that it is | |
| 276 // marked as a legitimate cursor position by Pango, and that it is not | |
| 277 // truncated from layout text (its glyph is shown on screen). | |
| 278 return (offset < num_log_attrs_ && log_attrs_[offset].is_cursor_position && | |
| 279 offset < g_utf8_strlen(layout_text_, -1)); | |
| 280 } | |
| 281 | |
| 282 void RenderTextLinux::ResetLayout() { | |
| 283 // set_cached_bounds_and_offset_valid(false) is done in RenderText for every | |
| 284 // operation that triggers ResetLayout(). | |
| 285 if (layout_) { | |
| 286 // TODO(msw): Keep |layout_| across text changes, etc.; it can be re-used. | |
| 287 g_object_unref(layout_); | |
| 288 layout_ = NULL; | |
| 289 } | |
| 290 if (current_line_) { | |
| 291 pango_layout_line_unref(current_line_); | |
| 292 current_line_ = NULL; | |
| 293 } | |
| 294 if (log_attrs_) { | |
| 295 g_free(log_attrs_); | |
| 296 log_attrs_ = NULL; | |
| 297 num_log_attrs_ = 0; | |
| 298 } | |
| 299 layout_text_ = NULL; | |
| 300 } | |
| 301 | |
| 302 void RenderTextLinux::EnsureLayout() { | |
| 303 if (layout_ == NULL) { | |
| 304 cairo_surface_t* surface = | |
| 305 cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 0, 0); | |
| 306 CHECK_EQ(CAIRO_STATUS_SUCCESS, cairo_surface_status(surface)); | |
| 307 cairo_t* cr = cairo_create(surface); | |
| 308 CHECK_EQ(CAIRO_STATUS_SUCCESS, cairo_status(cr)); | |
| 309 | |
| 310 layout_ = pango_cairo_create_layout(cr); | |
| 311 CHECK_NE(static_cast<PangoLayout*>(NULL), layout_); | |
| 312 cairo_destroy(cr); | |
| 313 cairo_surface_destroy(surface); | |
| 314 | |
| 315 SetupPangoLayoutWithFontDescription(layout_, | |
| 316 GetLayoutText(), | |
| 317 font_list().GetFontDescriptionString(), | |
| 318 0, | |
| 319 GetTextDirection(), | |
| 320 Canvas::DefaultCanvasTextAlignment()); | |
| 321 | |
| 322 // No width set so that the x-axis position is relative to the start of the | |
| 323 // text. ToViewPoint and ToTextPoint take care of the position conversion | |
| 324 // between text space and view spaces. | |
| 325 pango_layout_set_width(layout_, -1); | |
| 326 // TODO(xji): If RenderText will be used for displaying purpose, such as | |
| 327 // label, we will need to remove the single-line-mode setting. | |
| 328 pango_layout_set_single_paragraph_mode(layout_, true); | |
| 329 | |
| 330 layout_text_ = pango_layout_get_text(layout_); | |
| 331 SetupPangoAttributes(layout_); | |
| 332 | |
| 333 current_line_ = pango_layout_get_line_readonly(layout_, 0); | |
| 334 CHECK_NE(static_cast<PangoLayoutLine*>(NULL), current_line_); | |
| 335 pango_layout_line_ref(current_line_); | |
| 336 | |
| 337 pango_layout_get_log_attrs(layout_, &log_attrs_, &num_log_attrs_); | |
| 338 } | |
| 339 } | |
| 340 | |
| 341 void RenderTextLinux::SetupPangoAttributes(PangoLayout* layout) { | |
| 342 PangoAttrList* attrs = pango_attr_list_new(); | |
| 343 | |
| 344 // Splitting text runs to accommodate styling can break Arabic glyph shaping. | |
| 345 // Only split text runs as needed for bold and italic font styles changes. | |
| 346 BreakList<bool>::const_iterator bold = styles()[BOLD].breaks().begin(); | |
| 347 BreakList<bool>::const_iterator italic = styles()[ITALIC].breaks().begin(); | |
| 348 while (bold != styles()[BOLD].breaks().end() && | |
| 349 italic != styles()[ITALIC].breaks().end()) { | |
| 350 const int style = (bold->second ? Font::BOLD : 0) | | |
| 351 (italic->second ? Font::ITALIC : 0); | |
| 352 const size_t bold_end = styles()[BOLD].GetRange(bold).end(); | |
| 353 const size_t italic_end = styles()[ITALIC].GetRange(italic).end(); | |
| 354 const size_t style_end = std::min(bold_end, italic_end); | |
| 355 if (style != font_list().GetFontStyle()) { | |
| 356 FontList derived_font_list = font_list().DeriveFontList(style); | |
| 357 ScopedPangoFontDescription desc(pango_font_description_from_string( | |
| 358 derived_font_list.GetFontDescriptionString().c_str())); | |
| 359 | |
| 360 PangoAttribute* pango_attr = pango_attr_font_desc_new(desc.get()); | |
| 361 pango_attr->start_index = | |
| 362 TextIndexToLayoutIndex(std::max(bold->first, italic->first)); | |
| 363 pango_attr->end_index = TextIndexToLayoutIndex(style_end); | |
| 364 pango_attr_list_insert(attrs, pango_attr); | |
| 365 } | |
| 366 bold += bold_end == style_end ? 1 : 0; | |
| 367 italic += italic_end == style_end ? 1 : 0; | |
| 368 } | |
| 369 DCHECK(bold == styles()[BOLD].breaks().end()); | |
| 370 DCHECK(italic == styles()[ITALIC].breaks().end()); | |
| 371 | |
| 372 pango_layout_set_attributes(layout, attrs); | |
| 373 pango_attr_list_unref(attrs); | |
| 374 } | |
| 375 | |
| 376 void RenderTextLinux::DrawVisualText(Canvas* canvas) { | |
| 377 DCHECK(layout_); | |
| 378 | |
| 379 // Skia will draw glyphs with respect to the baseline. | |
| 380 Vector2d offset(GetLineOffset(0) + Vector2d(0, GetLayoutTextBaseline())); | |
| 381 | |
| 382 SkScalar x = SkIntToScalar(offset.x()); | |
| 383 SkScalar y = SkIntToScalar(offset.y()); | |
| 384 | |
| 385 std::vector<SkPoint> pos; | |
| 386 std::vector<uint16> glyphs; | |
| 387 | |
| 388 internal::SkiaTextRenderer renderer(canvas); | |
| 389 ApplyFadeEffects(&renderer); | |
| 390 ApplyTextShadows(&renderer); | |
| 391 | |
| 392 // TODO(derat): Use font-specific params: http://crbug.com/125235 | |
| 393 const gfx::FontRenderParams& render_params = | |
| 394 gfx::GetDefaultFontRenderParams(); | |
| 395 const bool use_subpixel_rendering = | |
| 396 render_params.subpixel_rendering != | |
| 397 gfx::FontRenderParams::SUBPIXEL_RENDERING_NONE; | |
| 398 renderer.SetFontSmoothingSettings( | |
| 399 render_params.antialiasing, | |
| 400 use_subpixel_rendering && !background_is_transparent()); | |
| 401 | |
| 402 // Temporarily apply composition underlines and selection colors. | |
| 403 ApplyCompositionAndSelectionStyles(); | |
| 404 | |
| 405 internal::StyleIterator style(colors(), styles()); | |
| 406 for (GSList* it = current_line_->runs; it; it = it->next) { | |
| 407 PangoLayoutRun* run = reinterpret_cast<PangoLayoutRun*>(it->data); | |
| 408 int glyph_count = run->glyphs->num_glyphs; | |
| 409 // TODO(msw): Skip painting runs outside the display rect area, like Win. | |
| 410 if (glyph_count == 0) | |
| 411 continue; | |
| 412 | |
| 413 ScopedPangoFontDescription desc( | |
| 414 pango_font_describe(run->item->analysis.font)); | |
| 415 | |
| 416 const std::string family_name = | |
| 417 pango_font_description_get_family(desc.get()); | |
| 418 renderer.SetTextSize(GetPangoFontSizeInPixels(desc.get())); | |
| 419 | |
| 420 glyphs.resize(glyph_count); | |
| 421 pos.resize(glyph_count); | |
| 422 | |
| 423 // Track the current glyph and the glyph at the start of its styled range. | |
| 424 int glyph_index = 0; | |
| 425 int style_start_glyph_index = glyph_index; | |
| 426 | |
| 427 // Track the x-coordinates for each styled range (|x| marks the current). | |
| 428 SkScalar style_start_x = x; | |
| 429 | |
| 430 // Track the current style and its text (not layout) index range. | |
| 431 style.UpdatePosition(GetGlyphTextIndex(run, style_start_glyph_index)); | |
| 432 Range style_range = style.GetRange(); | |
| 433 | |
| 434 do { | |
| 435 const PangoGlyphInfo& glyph = run->glyphs->glyphs[glyph_index]; | |
| 436 glyphs[glyph_index] = static_cast<uint16>(glyph.glyph); | |
| 437 // Use pango_units_to_double() rather than PANGO_PIXELS() here, so units | |
| 438 // are not rounded to the pixel grid if subpixel positioning is enabled. | |
| 439 pos[glyph_index].set(x + pango_units_to_double(glyph.geometry.x_offset), | |
| 440 y + pango_units_to_double(glyph.geometry.y_offset)); | |
| 441 x += pango_units_to_double(glyph.geometry.width); | |
| 442 | |
| 443 ++glyph_index; | |
| 444 const size_t glyph_text_index = (glyph_index == glyph_count) ? | |
| 445 style_range.end() : GetGlyphTextIndex(run, glyph_index); | |
| 446 if (!IndexInRange(style_range, glyph_text_index)) { | |
| 447 // TODO(asvitkine): For cases like "fi", where "fi" is a single glyph | |
| 448 // but can span multiple styles, Pango splits the | |
| 449 // styles evenly over the glyph. We can do this too by | |
| 450 // clipping and drawing the glyph several times. | |
| 451 renderer.SetForegroundColor(style.color()); | |
| 452 const int font_style = (style.style(BOLD) ? Font::BOLD : 0) | | |
| 453 (style.style(ITALIC) ? Font::ITALIC : 0); | |
| 454 renderer.SetFontFamilyWithStyle(family_name, font_style); | |
| 455 renderer.DrawPosText(&pos[style_start_glyph_index], | |
| 456 &glyphs[style_start_glyph_index], | |
| 457 glyph_index - style_start_glyph_index); | |
| 458 if (style.style(UNDERLINE)) | |
| 459 SetPangoUnderlineMetrics(desc.get(), &renderer); | |
| 460 renderer.DrawDecorations(style_start_x, y, x - style_start_x, | |
| 461 style.style(UNDERLINE), style.style(STRIKE), | |
| 462 style.style(DIAGONAL_STRIKE)); | |
| 463 style.UpdatePosition(glyph_text_index); | |
| 464 style_range = style.GetRange(); | |
| 465 style_start_glyph_index = glyph_index; | |
| 466 style_start_x = x; | |
| 467 } | |
| 468 } while (glyph_index < glyph_count); | |
| 469 } | |
| 470 | |
| 471 // Undo the temporarily applied composition underlines and selection colors. | |
| 472 UndoCompositionAndSelectionStyles(); | |
| 473 } | |
| 474 | |
| 475 GSList* RenderTextLinux::GetRunContainingCaret( | |
| 476 const SelectionModel& caret) const { | |
| 477 size_t position = TextIndexToLayoutIndex(caret.caret_pos()); | |
| 478 LogicalCursorDirection affinity = caret.caret_affinity(); | |
| 479 GSList* run = current_line_->runs; | |
| 480 while (run) { | |
| 481 PangoItem* item = reinterpret_cast<PangoLayoutRun*>(run->data)->item; | |
| 482 Range item_range(item->offset, item->offset + item->length); | |
| 483 if (RangeContainsCaret(item_range, position, affinity)) | |
| 484 return run; | |
| 485 run = run->next; | |
| 486 } | |
| 487 return NULL; | |
| 488 } | |
| 489 | |
| 490 SelectionModel RenderTextLinux::FirstSelectionModelInsideRun( | |
| 491 const PangoItem* item) { | |
| 492 size_t caret = IndexOfAdjacentGrapheme( | |
| 493 LayoutIndexToTextIndex(item->offset), CURSOR_FORWARD); | |
| 494 return SelectionModel(caret, CURSOR_BACKWARD); | |
| 495 } | |
| 496 | |
| 497 SelectionModel RenderTextLinux::LastSelectionModelInsideRun( | |
| 498 const PangoItem* item) { | |
| 499 size_t caret = IndexOfAdjacentGrapheme( | |
| 500 LayoutIndexToTextIndex(item->offset + item->length), CURSOR_BACKWARD); | |
| 501 return SelectionModel(caret, CURSOR_FORWARD); | |
| 502 } | |
| 503 | |
| 504 size_t RenderTextLinux::GetGlyphTextIndex(PangoLayoutRun* run, | |
| 505 int glyph_index) const { | |
| 506 return LayoutIndexToTextIndex(run->item->offset + | |
| 507 run->glyphs->log_clusters[glyph_index]); | |
| 508 } | |
| 509 | |
| 510 RenderText* RenderText::CreateInstance() { | |
| 511 return new RenderTextLinux; | |
| 512 } | |
| 513 | |
| 514 } // namespace gfx | |
| OLD | NEW |