15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "ui/gfx/render_text_pango.h" 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <pango/pangocairo.h> 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <algorithm> 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string> 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <vector> 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/i18n/break_iterator.h" 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h" 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "third_party/skia/include/core/SkTypeface.h" 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/canvas.h" 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/font.h" 17116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "ui/gfx/font_list.h" 18116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "ui/gfx/font_render_params.h" 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/pango_util.h" 20116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "ui/gfx/platform_font_pango.h" 21d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "ui/gfx/utf16_indexing.h" 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace gfx { 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace { 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Returns the preceding element in a GSList (O(n)). 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)GSList* GSListPrevious(GSList* head, GSList* item) { 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GSList* prev = NULL; 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (GSList* cur = head; cur != item; cur = cur->next) { 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(cur); 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) prev = cur; 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return prev; 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Returns true if the given visual cursor |direction| is logically forward 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// motion in the given Pango |item|. 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool IsForwardMotion(VisualCursorDirection direction, const PangoItem* item) { 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bool rtl = item->analysis.level & 1; 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return rtl == (direction == CURSOR_LEFT); 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Checks whether |range| contains |index|. This is not the same as calling 45010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// range.Contains(Range(index)), which returns true if |index| == |range.end()|. 46d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)bool IndexInRange(const Range& range, size_t index) { 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return index >= range.start() && index < range.end(); 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Sets underline metrics on |renderer| according to Pango font |desc|. 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SetPangoUnderlineMetrics(PangoFontDescription *desc, 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) internal::SkiaTextRenderer* renderer) { 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PangoFontMetrics* metrics = GetPangoFontMetrics(desc); 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int thickness = pango_font_metrics_get_underline_thickness(metrics); 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Pango returns the position "above the baseline". Change its sign to convert 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // it to a vertical offset from the baseline. 575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int position = -pango_font_metrics_get_underline_position(metrics); 581320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Note: pango_quantize_line_geometry() guarantees pixel boundaries, so 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // PANGO_PIXELS() is safe to use. 611320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci pango_quantize_line_geometry(&thickness, &position); 621320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci int thickness_pixels = PANGO_PIXELS(thickness); 631320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci int position_pixels = PANGO_PIXELS(position); 641320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 651320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // Ugly hack: make sure that underlines don't get clipped. See 661320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // http://crbug.com/393117. 671320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci int descent_pixels = PANGO_PIXELS(pango_font_metrics_get_descent(metrics)); 681320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci position_pixels = std::min(position_pixels, 691320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci descent_pixels - thickness_pixels); 701320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 711320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci renderer->SetUnderlineMetrics(thickness_pixels, position_pixels); 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// TODO(xji): index saved in upper layer is utf16 index. Pango uses utf8 index. 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Since caret_pos is used internally, we could save utf8 index for caret_pos 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// to avoid conversion. 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 80f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)RenderTextPango::RenderTextPango() 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) : layout_(NULL), 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) current_line_(NULL), 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) log_attrs_(NULL), 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) num_log_attrs_(0), 857dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch layout_text_(NULL) { 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 88f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)RenderTextPango::~RenderTextPango() { 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ResetLayout(); 905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 92f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)Size RenderTextPango::GetStringSize() { 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) EnsureLayout(); 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int width = 0, height = 0; 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pango_layout_get_pixel_size(layout_, &width, &height); 96116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 97116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // Pango returns 0 widths for very long strings (of 0x40000 chars or more). 98116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // This is caused by an int overflow in pango_glyph_string_extents_range. 99116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // Absurdly long strings may even report non-zero garbage values for width; 100116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // while detecting that isn't worthwhile, this handles the 0 width cases. 101116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch const long kAbsurdLength = 100000; 102116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if (width == 0 && g_utf8_strlen(layout_text_, -1) > kAbsurdLength) 103116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch width = font_list().GetExpectedTextWidth(g_utf8_strlen(layout_text_, -1)); 104116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 105ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch // Keep a consistent height between this particular string's PangoLayout and 106ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch // potentially larger text supported by the FontList. 107a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) // For example, if a text field contains a Japanese character, which is 108a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) // smaller than Latin ones, and then later a Latin one is inserted, this 109a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) // ensures that the text baseline does not shift. 110ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch return Size(width, std::max(height, font_list().GetHeight())); 1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 113f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)SelectionModel RenderTextPango::FindCursorPosition(const Point& point) { 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) EnsureLayout(); 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (text().empty()) 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return SelectionModel(0, CURSOR_FORWARD); 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Point p(ToTextPoint(point)); 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // When the point is outside of text, return HOME/END position. 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (p.x() < 0) 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return EdgeSelectionModel(CURSOR_LEFT); 12490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) if (p.x() > GetStringSize().width()) 1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return EdgeSelectionModel(CURSOR_RIGHT); 1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int caret_pos = 0, trailing = 0; 1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pango_layout_xy_to_index(layout_, p.x() * PANGO_SCALE, p.y() * PANGO_SCALE, 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) &caret_pos, &trailing); 1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK_GE(trailing, 0); 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (trailing > 0) { 1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) caret_pos = g_utf8_offset_to_pointer(layout_text_ + caret_pos, 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) trailing) - layout_text_; 1357dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch DCHECK_LE(static_cast<size_t>(caret_pos), strlen(layout_text_)); 1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return SelectionModel(LayoutIndexToTextIndex(caret_pos), 1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) (trailing > 0) ? CURSOR_BACKWARD : CURSOR_FORWARD); 1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 142f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)std::vector<RenderText::FontSpan> RenderTextPango::GetFontSpansForTesting() { 1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) EnsureLayout(); 1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::vector<RenderText::FontSpan> spans; 1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (GSList* it = current_line_->runs; it; it = it->next) { 1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PangoItem* item = reinterpret_cast<PangoLayoutRun*>(it->data)->item; 1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const int start = LayoutIndexToTextIndex(item->offset); 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const int end = LayoutIndexToTextIndex(item->offset + item->length); 150d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) const Range range(start, end); 1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ScopedPangoFontDescription desc(pango_font_describe(item->analysis.font)); 1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) spans.push_back(RenderText::FontSpan(Font(desc.get()), range)); 1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return spans; 1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 159f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)int RenderTextPango::GetLayoutTextBaseline() { 1600f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) EnsureLayout(); 1610f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) return PANGO_PIXELS(pango_layout_get_baseline(layout_)); 1620f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)} 1630f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) 164f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)SelectionModel RenderTextPango::AdjacentCharSelectionModel( 1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const SelectionModel& selection, 1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) VisualCursorDirection direction) { 1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GSList* run = GetRunContainingCaret(selection); 1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!run) { 1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // The cursor is not in any run: we're at the visual and logical edge. 1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SelectionModel edge = EdgeSelectionModel(direction); 1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (edge.caret_pos() == selection.caret_pos()) 1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return edge; 1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else 1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) run = (direction == CURSOR_RIGHT) ? 1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) current_line_->runs : g_slist_last(current_line_->runs); 1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If the cursor is moving within the current run, just move it by one 1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // grapheme in the appropriate direction. 1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PangoItem* item = reinterpret_cast<PangoLayoutRun*>(run->data)->item; 1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) size_t caret = selection.caret_pos(); 1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (IsForwardMotion(direction, item)) { 1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (caret < LayoutIndexToTextIndex(item->offset + item->length)) { 1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) caret = IndexOfAdjacentGrapheme(caret, CURSOR_FORWARD); 1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return SelectionModel(caret, CURSOR_BACKWARD); 1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (caret > LayoutIndexToTextIndex(item->offset)) { 1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) caret = IndexOfAdjacentGrapheme(caret, CURSOR_BACKWARD); 1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return SelectionModel(caret, CURSOR_FORWARD); 1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // The cursor is at the edge of a run; move to the visually adjacent run. 1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // TODO(xji): Keep a vector of runs to avoid using a singly-linked list. 1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) run = (direction == CURSOR_RIGHT) ? 1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) run->next : GSListPrevious(current_line_->runs, run); 1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!run) 1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return EdgeSelectionModel(direction); 1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PangoItem* item = reinterpret_cast<PangoLayoutRun*>(run->data)->item; 2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return IsForwardMotion(direction, item) ? 2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FirstSelectionModelInsideRun(item) : LastSelectionModelInsideRun(item); 2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 204f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)SelectionModel RenderTextPango::AdjacentWordSelectionModel( 2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const SelectionModel& selection, 2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) VisualCursorDirection direction) { 2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (obscured()) 2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return EdgeSelectionModel(direction); 2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD); 2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bool success = iter.Init(); 2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(success); 2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!success) 2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return selection; 2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SelectionModel cur(selection); 2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (;;) { 2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cur = AdjacentCharSelectionModel(cur, direction); 2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GSList* run = GetRunContainingCaret(cur); 2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!run) 2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) break; 2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PangoItem* item = reinterpret_cast<PangoLayoutRun*>(run->data)->item; 2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) size_t cursor = cur.caret_pos(); 2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (IsForwardMotion(direction, item) ? 2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) iter.IsEndOfWord(cursor) : iter.IsStartOfWord(cursor)) 2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) break; 2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return cur; 2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 232f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)Range RenderTextPango::GetGlyphBounds(size_t index) { 233a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) EnsureLayout(); 2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PangoRectangle pos; 2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pango_layout_index_to_pos(layout_, TextIndexToLayoutIndex(index), &pos); 2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // TODO(derat): Support fractional ranges for subpixel positioning? 237d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) return Range(PANGO_PIXELS(pos.x), PANGO_PIXELS(pos.x + pos.width)); 2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 240f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)std::vector<Rect> RenderTextPango::GetSubstringBounds(const Range& range) { 2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK_LE(range.GetMax(), text().length()); 2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (range.is_empty()) 2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return std::vector<Rect>(); 2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) EnsureLayout(); 246c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) int* ranges = NULL; 247c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) int n_ranges = 0; 248c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) pango_layout_line_get_x_ranges(current_line_, 249c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) TextIndexToLayoutIndex(range.GetMin()), 250c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) TextIndexToLayoutIndex(range.GetMax()), 251c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) &ranges, 252c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) &n_ranges); 253c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 254a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) const int height = GetStringSize().height(); 255c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 256c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) std::vector<Rect> bounds; 257c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) for (int i = 0; i < n_ranges; ++i) { 258c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // TODO(derat): Support fractional bounds for subpixel positioning? 259c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) int x = PANGO_PIXELS(ranges[2 * i]); 260c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) int width = PANGO_PIXELS(ranges[2 * i + 1]) - x; 26190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) Rect rect(x, 0, width, height); 262c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) rect.set_origin(ToViewPoint(rect.origin())); 263c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) bounds.push_back(rect); 264c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) } 265c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) g_free(ranges); 266c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) return bounds; 2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 269f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)size_t RenderTextPango::TextIndexToLayoutIndex(size_t index) const { 2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(layout_); 271010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) ptrdiff_t offset = UTF16IndexToOffset(text(), 0, index); 2727dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch // Clamp layout indices to the length of the text actually used for layout. 2737dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch offset = std::min<size_t>(offset, g_utf8_strlen(layout_text_, -1)); 2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const char* layout_pointer = g_utf8_offset_to_pointer(layout_text_, offset); 2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return (layout_pointer - layout_text_); 2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 278f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)size_t RenderTextPango::LayoutIndexToTextIndex(size_t index) const { 2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(layout_); 2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const char* layout_pointer = layout_text_ + index; 2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const long offset = g_utf8_pointer_to_offset(layout_text_, layout_pointer); 282010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) return UTF16OffsetToIndex(text(), 0, offset); 2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 285010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)bool RenderTextPango::IsValidCursorIndex(size_t index) { 286010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) if (index == 0 || index == text().length()) 2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 288010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) if (!IsValidLogicalIndex(index)) 2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) EnsureLayout(); 292010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) ptrdiff_t offset = UTF16IndexToOffset(text(), 0, index); 293010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) // Check that the index is marked as a legitimate cursor position by Pango. 294010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) return offset < num_log_attrs_ && log_attrs_[offset].is_cursor_position; 2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 297f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void RenderTextPango::ResetLayout() { 2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // set_cached_bounds_and_offset_valid(false) is done in RenderText for every 2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // operation that triggers ResetLayout(). 3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (layout_) { 30168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) // TODO(msw): Keep |layout_| across text changes, etc.; it can be re-used. 3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) g_object_unref(layout_); 3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) layout_ = NULL; 3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (current_line_) { 3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pango_layout_line_unref(current_line_); 3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) current_line_ = NULL; 3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (log_attrs_) { 3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) g_free(log_attrs_); 3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) log_attrs_ = NULL; 3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) num_log_attrs_ = 0; 3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) layout_text_ = NULL; 3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 317f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void RenderTextPango::EnsureLayout() { 3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (layout_ == NULL) { 3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cairo_surface_t* surface = 3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 0, 0); 32168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) CHECK_EQ(CAIRO_STATUS_SUCCESS, cairo_surface_status(surface)); 3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cairo_t* cr = cairo_create(surface); 32368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) CHECK_EQ(CAIRO_STATUS_SUCCESS, cairo_status(cr)); 3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) layout_ = pango_cairo_create_layout(cr); 32668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) CHECK_NE(static_cast<PangoLayout*>(NULL), layout_); 3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cairo_destroy(cr); 3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cairo_surface_destroy(surface); 3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 330116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch SetUpPangoLayout(layout_, GetLayoutText(), font_list(), GetTextDirection(), 331116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch Canvas::DefaultCanvasTextAlignment()); 3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // No width set so that the x-axis position is relative to the start of the 3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // text. ToViewPoint and ToTextPoint take care of the position conversion 3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // between text space and view spaces. 3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pango_layout_set_width(layout_, -1); 3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // TODO(xji): If RenderText will be used for displaying purpose, such as 3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // label, we will need to remove the single-line-mode setting. 3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pango_layout_set_single_paragraph_mode(layout_, true); 3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) layout_text_ = pango_layout_get_text(layout_); 3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SetupPangoAttributes(layout_); 3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) current_line_ = pango_layout_get_line_readonly(layout_, 0); 34568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) CHECK_NE(static_cast<PangoLayoutLine*>(NULL), current_line_); 3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pango_layout_line_ref(current_line_); 3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pango_layout_get_log_attrs(layout_, &log_attrs_, &num_log_attrs_); 3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 352f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void RenderTextPango::SetupPangoAttributes(PangoLayout* layout) { 3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PangoAttrList* attrs = pango_attr_list_new(); 3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Splitting text runs to accommodate styling can break Arabic glyph shaping. 3562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Only split text runs as needed for bold and italic font styles changes. 3572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) BreakList<bool>::const_iterator bold = styles()[BOLD].breaks().begin(); 3582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) BreakList<bool>::const_iterator italic = styles()[ITALIC].breaks().begin(); 3592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) while (bold != styles()[BOLD].breaks().end() && 3602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) italic != styles()[ITALIC].breaks().end()) { 3612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const int style = (bold->second ? Font::BOLD : 0) | 3622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) (italic->second ? Font::ITALIC : 0); 3632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const size_t bold_end = styles()[BOLD].GetRange(bold).end(); 3642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const size_t italic_end = styles()[ITALIC].GetRange(italic).end(); 3652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const size_t style_end = std::min(bold_end, italic_end); 3662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (style != font_list().GetFontStyle()) { 3675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // TODO(derat): Don't interpret gfx::FontList font descriptions as Pango 3685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) // font descriptions: http://crbug.com/393067 3695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) FontList derived_font_list = font_list().DeriveWithStyle(style); 3705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) ScopedPangoFontDescription desc( 3715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) derived_font_list.GetFontDescriptionString()); 3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PangoAttribute* pango_attr = pango_attr_font_desc_new(desc.get()); 3742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) pango_attr->start_index = 3752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) TextIndexToLayoutIndex(std::max(bold->first, italic->first)); 3762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) pango_attr->end_index = TextIndexToLayoutIndex(style_end); 3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pango_attr_list_insert(attrs, pango_attr); 3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) bold += bold_end == style_end ? 1 : 0; 3802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) italic += italic_end == style_end ? 1 : 0; 3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) DCHECK(bold == styles()[BOLD].breaks().end()); 3832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) DCHECK(italic == styles()[ITALIC].breaks().end()); 3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pango_layout_set_attributes(layout, attrs); 3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pango_attr_list_unref(attrs); 3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 389f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void RenderTextPango::DrawVisualText(Canvas* canvas) { 3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(layout_); 3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Skia will draw glyphs with respect to the baseline. 3930f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) Vector2d offset(GetLineOffset(0) + Vector2d(0, GetLayoutTextBaseline())); 3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SkScalar x = SkIntToScalar(offset.x()); 3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SkScalar y = SkIntToScalar(offset.y()); 3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::vector<SkPoint> pos; 3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::vector<uint16> glyphs; 4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) internal::SkiaTextRenderer renderer(canvas); 4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ApplyFadeEffects(&renderer); 4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ApplyTextShadows(&renderer); 404116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch renderer.SetFontRenderParams( 405116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch font_list().GetPrimaryFont().GetFontRenderParams(), 406116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch background_is_transparent()); 4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Temporarily apply composition underlines and selection colors. 4092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) ApplyCompositionAndSelectionStyles(); 4102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 4112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) internal::StyleIterator style(colors(), styles()); 4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (GSList* it = current_line_->runs; it; it = it->next) { 4131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // Skip painting runs outside the display area. 4141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (SkScalarTruncToInt(x) >= display_rect().right()) 4151320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci break; 4161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PangoLayoutRun* run = reinterpret_cast<PangoLayoutRun*>(it->data); 4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int glyph_count = run->glyphs->num_glyphs; 4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (glyph_count == 0) 4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue; 4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ScopedPangoFontDescription desc( 4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pango_font_describe(run->item->analysis.font)); 4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const std::string family_name = 4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pango_font_description_get_family(desc.get()); 4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) renderer.SetTextSize(GetPangoFontSizeInPixels(desc.get())); 4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) glyphs.resize(glyph_count); 4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) pos.resize(glyph_count); 4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Track the current glyph and the glyph at the start of its styled range. 4322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) int glyph_index = 0; 4332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) int style_start_glyph_index = glyph_index; 4342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 4352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Track the x-coordinates for each styled range (|x| marks the current). 4362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) SkScalar style_start_x = x; 4372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 4382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Track the current style and its text (not layout) index range. 4392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) style.UpdatePosition(GetGlyphTextIndex(run, style_start_glyph_index)); 440d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) Range style_range = style.GetRange(); 4412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 4422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) do { 4432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const PangoGlyphInfo& glyph = run->glyphs->glyphs[glyph_index]; 4442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) glyphs[glyph_index] = static_cast<uint16>(glyph.glyph); 4452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Use pango_units_to_double() rather than PANGO_PIXELS() here, so units 4462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // are not rounded to the pixel grid if subpixel positioning is enabled. 4472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) pos[glyph_index].set(x + pango_units_to_double(glyph.geometry.x_offset), 4482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) y + pango_units_to_double(glyph.geometry.y_offset)); 4492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) x += pango_units_to_double(glyph.geometry.width); 4502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 4512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) ++glyph_index; 4521320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // If this is the last glyph of the range or the last glyph inside the 4531320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // display area (which would cause early termination of the loop), paint 4541320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // the range. 4552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const size_t glyph_text_index = (glyph_index == glyph_count) ? 4562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) style_range.end() : GetGlyphTextIndex(run, glyph_index); 4571320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (!IndexInRange(style_range, glyph_text_index) || 4581320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci SkScalarTruncToInt(x) >= display_rect().right()) { 4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // TODO(asvitkine): For cases like "fi", where "fi" is a single glyph 4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // but can span multiple styles, Pango splits the 4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // styles evenly over the glyph. We can do this too by 4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // clipping and drawing the glyph several times. 4632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) renderer.SetForegroundColor(style.color()); 4642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const int font_style = (style.style(BOLD) ? Font::BOLD : 0) | 4652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) (style.style(ITALIC) ? Font::ITALIC : 0); 4662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) renderer.SetFontFamilyWithStyle(family_name, font_style); 4672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) renderer.DrawPosText(&pos[style_start_glyph_index], 4682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) &glyphs[style_start_glyph_index], 4692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) glyph_index - style_start_glyph_index); 4702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (style.style(UNDERLINE)) 4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SetPangoUnderlineMetrics(desc.get(), &renderer); 4722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) renderer.DrawDecorations(style_start_x, y, x - style_start_x, 4732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) style.style(UNDERLINE), style.style(STRIKE), 4742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) style.style(DIAGONAL_STRIKE)); 4752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) style.UpdatePosition(glyph_text_index); 4762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) style_range = style.GetRange(); 4772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) style_start_glyph_index = glyph_index; 4782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) style_start_x = x; 4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4801320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // Terminates loop when the end of the range has been reached or the next 4811320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // glyph falls outside the display area. 4821320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci } while (glyph_index < glyph_count && 4831320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci SkScalarTruncToInt(x) < display_rect().right()); 4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 486effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch renderer.EndDiagonalStrike(); 487effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 4882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Undo the temporarily applied composition underlines and selection colors. 4892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) UndoCompositionAndSelectionStyles(); 4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 492f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)GSList* RenderTextPango::GetRunContainingCaret( 4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const SelectionModel& caret) const { 4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) size_t position = TextIndexToLayoutIndex(caret.caret_pos()); 4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LogicalCursorDirection affinity = caret.caret_affinity(); 4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GSList* run = current_line_->runs; 4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) while (run) { 4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PangoItem* item = reinterpret_cast<PangoLayoutRun*>(run->data)->item; 499d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) Range item_range(item->offset, item->offset + item->length); 5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (RangeContainsCaret(item_range, position, affinity)) 5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return run; 5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) run = run->next; 5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return NULL; 5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 507f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)SelectionModel RenderTextPango::FirstSelectionModelInsideRun( 5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const PangoItem* item) { 5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) size_t caret = IndexOfAdjacentGrapheme( 5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LayoutIndexToTextIndex(item->offset), CURSOR_FORWARD); 5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return SelectionModel(caret, CURSOR_BACKWARD); 5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 514f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)SelectionModel RenderTextPango::LastSelectionModelInsideRun( 5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const PangoItem* item) { 5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) size_t caret = IndexOfAdjacentGrapheme( 5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LayoutIndexToTextIndex(item->offset + item->length), CURSOR_BACKWARD); 5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return SelectionModel(caret, CURSOR_FORWARD); 5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 521f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)size_t RenderTextPango::GetGlyphTextIndex(PangoLayoutRun* run, 5222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) int glyph_index) const { 5232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return LayoutIndexToTextIndex(run->item->offset + 5242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) run->glyphs->log_clusters[glyph_index]); 5252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 5262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 527cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)RenderText* RenderText::CreateNativeInstance() { 528f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return new RenderTextPango; 5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace gfx 532