cc: Add more eviction categories to picture layer impl.
[chromium-blink-merge.git] / ui / gfx / render_text_harfbuzz.cc
blob01f75e312b983402abfc22116cf87f58b29060a4
1 // Copyright 2014 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.
5 #include "ui/gfx/render_text_harfbuzz.h"
7 #include <limits>
8 #include <map>
10 #include "base/i18n/bidi_line_iterator.h"
11 #include "base/i18n/break_iterator.h"
12 #include "base/i18n/char_iterator.h"
13 #include "base/lazy_instance.h"
14 #include "third_party/harfbuzz-ng/src/hb.h"
15 #include "third_party/icu/source/common/unicode/ubidi.h"
16 #include "third_party/skia/include/core/SkColor.h"
17 #include "third_party/skia/include/core/SkTypeface.h"
18 #include "ui/gfx/canvas.h"
19 #include "ui/gfx/font_fallback.h"
20 #include "ui/gfx/font_render_params.h"
21 #include "ui/gfx/utf16_indexing.h"
23 #if defined(OS_WIN)
24 #include "ui/gfx/font_fallback_win.h"
25 #endif
27 namespace gfx {
29 namespace {
31 // The maximum number of scripts a Unicode character can belong to. This value
32 // is arbitrarily chosen to be a good limit because it is unlikely for a single
33 // character to belong to more scripts.
34 const size_t kMaxScripts = 5;
36 // Maps from code points to glyph indices in a font.
37 typedef std::map<uint32_t, uint16_t> GlyphCache;
39 // Font data provider for HarfBuzz using Skia. Copied from Blink.
40 // TODO(ckocagil): Eliminate the duplication. http://crbug.com/368375
41 struct FontData {
42 FontData(GlyphCache* glyph_cache) : glyph_cache_(glyph_cache) {}
44 SkPaint paint_;
45 GlyphCache* glyph_cache_;
48 hb_position_t SkiaScalarToHarfBuzzPosition(SkScalar value) {
49 return SkScalarToFixed(value);
52 // Deletes the object at the given pointer after casting it to the given type.
53 template<typename Type>
54 void DeleteByType(void* data) {
55 Type* typed_data = reinterpret_cast<Type*>(data);
56 delete typed_data;
59 template<typename Type>
60 void DeleteArrayByType(void* data) {
61 Type* typed_data = reinterpret_cast<Type*>(data);
62 delete[] typed_data;
65 // Outputs the |width| and |extents| of the glyph with index |codepoint| in
66 // |paint|'s font.
67 void GetGlyphWidthAndExtents(SkPaint* paint,
68 hb_codepoint_t codepoint,
69 hb_position_t* width,
70 hb_glyph_extents_t* extents) {
71 DCHECK_LE(codepoint, 0xFFFFU);
72 paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
74 SkScalar sk_width;
75 SkRect sk_bounds;
76 uint16_t glyph = codepoint;
78 paint->getTextWidths(&glyph, sizeof(glyph), &sk_width, &sk_bounds);
79 if (width)
80 *width = SkiaScalarToHarfBuzzPosition(sk_width);
81 if (extents) {
82 // Invert y-axis because Skia is y-grows-down but we set up HarfBuzz to be
83 // y-grows-up.
84 extents->x_bearing = SkiaScalarToHarfBuzzPosition(sk_bounds.fLeft);
85 extents->y_bearing = SkiaScalarToHarfBuzzPosition(-sk_bounds.fTop);
86 extents->width = SkiaScalarToHarfBuzzPosition(sk_bounds.width());
87 extents->height = SkiaScalarToHarfBuzzPosition(-sk_bounds.height());
91 // Writes the |glyph| index for the given |unicode| code point. Returns whether
92 // the glyph exists, i.e. it is not a missing glyph.
93 hb_bool_t GetGlyph(hb_font_t* font,
94 void* data,
95 hb_codepoint_t unicode,
96 hb_codepoint_t variation_selector,
97 hb_codepoint_t* glyph,
98 void* user_data) {
99 FontData* font_data = reinterpret_cast<FontData*>(data);
100 GlyphCache* cache = font_data->glyph_cache_;
102 bool exists = cache->count(unicode) != 0;
103 if (!exists) {
104 SkPaint* paint = &font_data->paint_;
105 paint->setTextEncoding(SkPaint::kUTF32_TextEncoding);
106 paint->textToGlyphs(&unicode, sizeof(hb_codepoint_t), &(*cache)[unicode]);
108 *glyph = (*cache)[unicode];
109 return !!*glyph;
112 // Returns the horizontal advance value of the |glyph|.
113 hb_position_t GetGlyphHorizontalAdvance(hb_font_t* font,
114 void* data,
115 hb_codepoint_t glyph,
116 void* user_data) {
117 FontData* font_data = reinterpret_cast<FontData*>(data);
118 hb_position_t advance = 0;
120 GetGlyphWidthAndExtents(&font_data->paint_, glyph, &advance, 0);
121 return advance;
124 hb_bool_t GetGlyphHorizontalOrigin(hb_font_t* font,
125 void* data,
126 hb_codepoint_t glyph,
127 hb_position_t* x,
128 hb_position_t* y,
129 void* user_data) {
130 // Just return true, like the HarfBuzz-FreeType implementation.
131 return true;
134 hb_position_t GetGlyphKerning(FontData* font_data,
135 hb_codepoint_t first_glyph,
136 hb_codepoint_t second_glyph) {
137 SkTypeface* typeface = font_data->paint_.getTypeface();
138 const uint16_t glyphs[2] = { static_cast<uint16_t>(first_glyph),
139 static_cast<uint16_t>(second_glyph) };
140 int32_t kerning_adjustments[1] = { 0 };
142 if (!typeface->getKerningPairAdjustments(glyphs, 2, kerning_adjustments))
143 return 0;
145 SkScalar upm = SkIntToScalar(typeface->getUnitsPerEm());
146 SkScalar size = font_data->paint_.getTextSize();
147 return SkiaScalarToHarfBuzzPosition(
148 SkScalarMulDiv(SkIntToScalar(kerning_adjustments[0]), size, upm));
151 hb_position_t GetGlyphHorizontalKerning(hb_font_t* font,
152 void* data,
153 hb_codepoint_t left_glyph,
154 hb_codepoint_t right_glyph,
155 void* user_data) {
156 FontData* font_data = reinterpret_cast<FontData*>(data);
157 if (font_data->paint_.isVerticalText()) {
158 // We don't support cross-stream kerning.
159 return 0;
162 return GetGlyphKerning(font_data, left_glyph, right_glyph);
165 hb_position_t GetGlyphVerticalKerning(hb_font_t* font,
166 void* data,
167 hb_codepoint_t top_glyph,
168 hb_codepoint_t bottom_glyph,
169 void* user_data) {
170 FontData* font_data = reinterpret_cast<FontData*>(data);
171 if (!font_data->paint_.isVerticalText()) {
172 // We don't support cross-stream kerning.
173 return 0;
176 return GetGlyphKerning(font_data, top_glyph, bottom_glyph);
179 // Writes the |extents| of |glyph|.
180 hb_bool_t GetGlyphExtents(hb_font_t* font,
181 void* data,
182 hb_codepoint_t glyph,
183 hb_glyph_extents_t* extents,
184 void* user_data) {
185 FontData* font_data = reinterpret_cast<FontData*>(data);
187 GetGlyphWidthAndExtents(&font_data->paint_, glyph, 0, extents);
188 return true;
191 class FontFuncs {
192 public:
193 FontFuncs() : font_funcs_(hb_font_funcs_create()) {
194 hb_font_funcs_set_glyph_func(font_funcs_, GetGlyph, 0, 0);
195 hb_font_funcs_set_glyph_h_advance_func(
196 font_funcs_, GetGlyphHorizontalAdvance, 0, 0);
197 hb_font_funcs_set_glyph_h_kerning_func(
198 font_funcs_, GetGlyphHorizontalKerning, 0, 0);
199 hb_font_funcs_set_glyph_h_origin_func(
200 font_funcs_, GetGlyphHorizontalOrigin, 0, 0);
201 hb_font_funcs_set_glyph_v_kerning_func(
202 font_funcs_, GetGlyphVerticalKerning, 0, 0);
203 hb_font_funcs_set_glyph_extents_func(
204 font_funcs_, GetGlyphExtents, 0, 0);
205 hb_font_funcs_make_immutable(font_funcs_);
208 ~FontFuncs() {
209 hb_font_funcs_destroy(font_funcs_);
212 hb_font_funcs_t* get() { return font_funcs_; }
214 private:
215 hb_font_funcs_t* font_funcs_;
217 DISALLOW_COPY_AND_ASSIGN(FontFuncs);
220 base::LazyInstance<FontFuncs>::Leaky g_font_funcs = LAZY_INSTANCE_INITIALIZER;
222 // Returns the raw data of the font table |tag|.
223 hb_blob_t* GetFontTable(hb_face_t* face, hb_tag_t tag, void* user_data) {
224 SkTypeface* typeface = reinterpret_cast<SkTypeface*>(user_data);
226 const size_t table_size = typeface->getTableSize(tag);
227 if (!table_size)
228 return 0;
230 scoped_ptr<char[]> buffer(new char[table_size]);
231 if (!buffer)
232 return 0;
233 size_t actual_size = typeface->getTableData(tag, 0, table_size, buffer.get());
234 if (table_size != actual_size)
235 return 0;
237 char* buffer_raw = buffer.release();
238 return hb_blob_create(buffer_raw, table_size, HB_MEMORY_MODE_WRITABLE,
239 buffer_raw, DeleteArrayByType<char>);
242 void UnrefSkTypeface(void* data) {
243 SkTypeface* skia_face = reinterpret_cast<SkTypeface*>(data);
244 SkSafeUnref(skia_face);
247 // Wrapper class for a HarfBuzz face created from a given Skia face.
248 class HarfBuzzFace {
249 public:
250 HarfBuzzFace() : face_(NULL) {}
252 ~HarfBuzzFace() {
253 if (face_)
254 hb_face_destroy(face_);
257 void Init(SkTypeface* skia_face) {
258 SkSafeRef(skia_face);
259 face_ = hb_face_create_for_tables(GetFontTable, skia_face, UnrefSkTypeface);
260 DCHECK(face_);
263 hb_face_t* get() {
264 return face_;
267 private:
268 hb_face_t* face_;
271 // Creates a HarfBuzz font from the given Skia face and text size.
272 hb_font_t* CreateHarfBuzzFont(SkTypeface* skia_face, int text_size) {
273 typedef std::pair<HarfBuzzFace, GlyphCache> FaceCache;
275 // TODO(ckocagil): This shouldn't grow indefinitely. Maybe use base::MRUCache?
276 static std::map<SkFontID, FaceCache> face_caches;
278 FaceCache* face_cache = &face_caches[skia_face->uniqueID()];
279 if (face_cache->first.get() == NULL)
280 face_cache->first.Init(skia_face);
282 hb_font_t* harfbuzz_font = hb_font_create(face_cache->first.get());
283 const int scale = SkScalarToFixed(text_size);
284 hb_font_set_scale(harfbuzz_font, scale, scale);
285 FontData* hb_font_data = new FontData(&face_cache->second);
286 hb_font_data->paint_.setTypeface(skia_face);
287 hb_font_data->paint_.setTextSize(text_size);
288 hb_font_set_funcs(harfbuzz_font, g_font_funcs.Get().get(), hb_font_data,
289 DeleteByType<FontData>);
290 hb_font_make_immutable(harfbuzz_font);
291 return harfbuzz_font;
294 // Returns true if characters of |block_code| may trigger font fallback.
295 bool IsUnusualBlockCode(UBlockCode block_code) {
296 return block_code == UBLOCK_GEOMETRIC_SHAPES ||
297 block_code == UBLOCK_MISCELLANEOUS_SYMBOLS;
300 // Returns the index of the first unusual character after a usual character or
301 // vice versa. Unusual characters are defined by |IsUnusualBlockCode|.
302 size_t FindUnusualCharacter(const base::string16& text,
303 size_t run_start,
304 size_t run_break) {
305 const int32 run_length = static_cast<int32>(run_break - run_start);
306 base::i18n::UTF16CharIterator iter(text.c_str() + run_start,
307 run_length);
308 const UBlockCode first_block_code = ublock_getCode(iter.get());
309 const bool first_block_unusual = IsUnusualBlockCode(first_block_code);
310 while (iter.Advance() && iter.array_pos() < run_length) {
311 const UBlockCode current_block_code = ublock_getCode(iter.get());
312 if (current_block_code != first_block_code &&
313 (first_block_unusual || IsUnusualBlockCode(current_block_code))) {
314 return run_start + iter.array_pos();
317 return run_break;
320 // If the given scripts match, returns the one that isn't USCRIPT_COMMON or
321 // USCRIPT_INHERITED, i.e. the more specific one. Otherwise returns
322 // USCRIPT_INVALID_CODE.
323 UScriptCode ScriptIntersect(UScriptCode first, UScriptCode second) {
324 if (first == second ||
325 (second > USCRIPT_INVALID_CODE && second <= USCRIPT_INHERITED)) {
326 return first;
328 if (first > USCRIPT_INVALID_CODE && first <= USCRIPT_INHERITED)
329 return second;
330 return USCRIPT_INVALID_CODE;
333 // Writes the script and the script extensions of the character with the
334 // Unicode |codepoint|. Returns the number of written scripts.
335 int GetScriptExtensions(UChar32 codepoint, UScriptCode* scripts) {
336 UErrorCode icu_error = U_ZERO_ERROR;
337 // ICU documentation incorrectly states that the result of
338 // |uscript_getScriptExtensions| will contain the regular script property.
339 // Write the character's script property to the first element.
340 scripts[0] = uscript_getScript(codepoint, &icu_error);
341 if (U_FAILURE(icu_error))
342 return 0;
343 // Fill the rest of |scripts| with the extensions.
344 int count = uscript_getScriptExtensions(codepoint, scripts + 1,
345 kMaxScripts - 1, &icu_error);
346 if (U_FAILURE(icu_error))
347 count = 0;
348 return count + 1;
351 // Intersects the script extensions set of |codepoint| with |result| and writes
352 // to |result|, reading and updating |result_size|.
353 void ScriptSetIntersect(UChar32 codepoint,
354 UScriptCode* result,
355 size_t* result_size) {
356 UScriptCode scripts[kMaxScripts] = { USCRIPT_INVALID_CODE };
357 int count = GetScriptExtensions(codepoint, scripts);
359 size_t out_size = 0;
361 for (size_t i = 0; i < *result_size; ++i) {
362 for (int j = 0; j < count; ++j) {
363 UScriptCode intersection = ScriptIntersect(result[i], scripts[j]);
364 if (intersection != USCRIPT_INVALID_CODE) {
365 result[out_size++] = intersection;
366 break;
371 *result_size = out_size;
374 // Find the longest sequence of characters from 0 and up to |length| that
375 // have at least one common UScriptCode value. Writes the common script value to
376 // |script| and returns the length of the sequence. Takes the characters' script
377 // extensions into account. http://www.unicode.org/reports/tr24/#ScriptX
379 // Consider 3 characters with the script values {Kana}, {Hira, Kana}, {Kana}.
380 // Without script extensions only the first script in each set would be taken
381 // into account, resulting in 3 runs where 1 would be enough.
382 // TODO(ckocagil): Write a unit test for the case above.
383 int ScriptInterval(const base::string16& text,
384 size_t start,
385 size_t length,
386 UScriptCode* script) {
387 DCHECK_GT(length, 0U);
389 UScriptCode scripts[kMaxScripts] = { USCRIPT_INVALID_CODE };
391 base::i18n::UTF16CharIterator char_iterator(text.c_str() + start, length);
392 size_t scripts_size = GetScriptExtensions(char_iterator.get(), scripts);
393 *script = scripts[0];
395 while (char_iterator.Advance()) {
396 ScriptSetIntersect(char_iterator.get(), scripts, &scripts_size);
397 if (scripts_size == 0U)
398 return char_iterator.array_pos();
399 *script = scripts[0];
402 return length;
405 // A port of hb_icu_script_to_script because harfbuzz on CrOS is built without
406 // hb-icu. See http://crbug.com/356929
407 inline hb_script_t ICUScriptToHBScript(UScriptCode script) {
408 if (script == USCRIPT_INVALID_CODE)
409 return HB_SCRIPT_INVALID;
410 return hb_script_from_string(uscript_getShortName(script), -1);
413 // Helper template function for |TextRunHarfBuzz::GetClusterAt()|. |Iterator|
414 // can be a forward or reverse iterator type depending on the text direction.
415 template <class Iterator>
416 void GetClusterAtImpl(size_t pos,
417 Range range,
418 Iterator elements_begin,
419 Iterator elements_end,
420 bool reversed,
421 Range* chars,
422 Range* glyphs) {
423 Iterator element = std::upper_bound(elements_begin, elements_end, pos);
424 chars->set_end(element == elements_end ? range.end() : *element);
425 glyphs->set_end(reversed ? elements_end - element : element - elements_begin);
427 DCHECK(element != elements_begin);
428 while (--element != elements_begin && *element == *(element - 1));
429 chars->set_start(*element);
430 glyphs->set_start(
431 reversed ? elements_end - element : element - elements_begin);
432 if (reversed)
433 *glyphs = Range(glyphs->end(), glyphs->start());
435 DCHECK(!chars->is_reversed());
436 DCHECK(!chars->is_empty());
437 DCHECK(!glyphs->is_reversed());
438 DCHECK(!glyphs->is_empty());
441 } // namespace
443 namespace internal {
445 TextRunHarfBuzz::TextRunHarfBuzz()
446 : width(0.0f),
447 preceding_run_widths(0.0f),
448 is_rtl(false),
449 level(0),
450 script(USCRIPT_INVALID_CODE),
451 glyph_count(static_cast<size_t>(-1)),
452 font_size(0),
453 font_style(0),
454 strike(false),
455 diagonal_strike(false),
456 underline(false) {}
458 TextRunHarfBuzz::~TextRunHarfBuzz() {}
460 void TextRunHarfBuzz::GetClusterAt(size_t pos,
461 Range* chars,
462 Range* glyphs) const {
463 DCHECK(range.Contains(Range(pos, pos + 1)));
464 DCHECK(chars);
465 DCHECK(glyphs);
467 if (glyph_count == 0) {
468 *chars = range;
469 *glyphs = Range();
470 return;
473 if (is_rtl) {
474 GetClusterAtImpl(pos, range, glyph_to_char.rbegin(), glyph_to_char.rend(),
475 true, chars, glyphs);
476 return;
479 GetClusterAtImpl(pos, range, glyph_to_char.begin(), glyph_to_char.end(),
480 false, chars, glyphs);
483 Range TextRunHarfBuzz::CharRangeToGlyphRange(const Range& char_range) const {
484 DCHECK(range.Contains(char_range));
485 DCHECK(!char_range.is_reversed());
486 DCHECK(!char_range.is_empty());
488 Range start_glyphs;
489 Range end_glyphs;
490 Range temp_range;
491 GetClusterAt(char_range.start(), &temp_range, &start_glyphs);
492 GetClusterAt(char_range.end() - 1, &temp_range, &end_glyphs);
494 return is_rtl ? Range(end_glyphs.start(), start_glyphs.end()) :
495 Range(start_glyphs.start(), end_glyphs.end());
498 size_t TextRunHarfBuzz::CountMissingGlyphs() const {
499 static const int kMissingGlyphId = 0;
500 size_t missing = 0;
501 for (size_t i = 0; i < glyph_count; ++i)
502 missing += (glyphs[i] == kMissingGlyphId) ? 1 : 0;
503 return missing;
506 Range TextRunHarfBuzz::GetGraphemeBounds(
507 base::i18n::BreakIterator* grapheme_iterator,
508 size_t text_index) {
509 DCHECK_LT(text_index, range.end());
510 // TODO(msw): Support floating point grapheme bounds.
511 const int preceding_run_widths_int = SkScalarRoundToInt(preceding_run_widths);
512 if (glyph_count == 0)
513 return Range(preceding_run_widths_int, preceding_run_widths_int + width);
515 Range chars;
516 Range glyphs;
517 GetClusterAt(text_index, &chars, &glyphs);
518 const int cluster_begin_x = SkScalarRoundToInt(positions[glyphs.start()].x());
519 const int cluster_end_x = glyphs.end() < glyph_count ?
520 SkScalarRoundToInt(positions[glyphs.end()].x()) : width;
522 // A cluster consists of a number of code points and corresponds to a number
523 // of glyphs that should be drawn together. A cluster can contain multiple
524 // graphemes. In order to place the cursor at a grapheme boundary inside the
525 // cluster, we simply divide the cluster width by the number of graphemes.
526 if (chars.length() > 1 && grapheme_iterator) {
527 int before = 0;
528 int total = 0;
529 for (size_t i = chars.start(); i < chars.end(); ++i) {
530 if (grapheme_iterator->IsGraphemeBoundary(i)) {
531 if (i < text_index)
532 ++before;
533 ++total;
536 DCHECK_GT(total, 0);
537 if (total > 1) {
538 if (is_rtl)
539 before = total - before - 1;
540 DCHECK_GE(before, 0);
541 DCHECK_LT(before, total);
542 const int cluster_width = cluster_end_x - cluster_begin_x;
543 const int grapheme_begin_x = cluster_begin_x + static_cast<int>(0.5f +
544 cluster_width * before / static_cast<float>(total));
545 const int grapheme_end_x = cluster_begin_x + static_cast<int>(0.5f +
546 cluster_width * (before + 1) / static_cast<float>(total));
547 return Range(preceding_run_widths_int + grapheme_begin_x,
548 preceding_run_widths_int + grapheme_end_x);
552 return Range(preceding_run_widths_int + cluster_begin_x,
553 preceding_run_widths_int + cluster_end_x);
556 } // namespace internal
558 RenderTextHarfBuzz::RenderTextHarfBuzz()
559 : RenderText(),
560 needs_layout_(false) {}
562 RenderTextHarfBuzz::~RenderTextHarfBuzz() {}
564 Size RenderTextHarfBuzz::GetStringSize() {
565 const SizeF size_f = GetStringSizeF();
566 return Size(std::ceil(size_f.width()), size_f.height());
569 SizeF RenderTextHarfBuzz::GetStringSizeF() {
570 EnsureLayout();
571 return lines()[0].size;
574 SelectionModel RenderTextHarfBuzz::FindCursorPosition(const Point& point) {
575 EnsureLayout();
577 int x = ToTextPoint(point).x();
578 int offset = 0;
579 size_t run_index = GetRunContainingXCoord(x, &offset);
580 if (run_index >= runs_.size())
581 return EdgeSelectionModel((x < 0) ? CURSOR_LEFT : CURSOR_RIGHT);
582 const internal::TextRunHarfBuzz& run = *runs_[run_index];
584 for (size_t i = 0; i < run.glyph_count; ++i) {
585 const SkScalar end =
586 i + 1 == run.glyph_count ? run.width : run.positions[i + 1].x();
587 const SkScalar middle = (end + run.positions[i].x()) / 2;
589 if (offset < middle) {
590 return SelectionModel(LayoutIndexToTextIndex(
591 run.glyph_to_char[i] + (run.is_rtl ? 1 : 0)),
592 (run.is_rtl ? CURSOR_BACKWARD : CURSOR_FORWARD));
594 if (offset < end) {
595 return SelectionModel(LayoutIndexToTextIndex(
596 run.glyph_to_char[i] + (run.is_rtl ? 0 : 1)),
597 (run.is_rtl ? CURSOR_FORWARD : CURSOR_BACKWARD));
600 return EdgeSelectionModel(CURSOR_RIGHT);
603 std::vector<RenderText::FontSpan> RenderTextHarfBuzz::GetFontSpansForTesting() {
604 NOTIMPLEMENTED();
605 return std::vector<RenderText::FontSpan>();
608 Range RenderTextHarfBuzz::GetGlyphBounds(size_t index) {
609 EnsureLayout();
610 const size_t run_index =
611 GetRunContainingCaret(SelectionModel(index, CURSOR_FORWARD));
612 // Return edge bounds if the index is invalid or beyond the layout text size.
613 if (run_index >= runs_.size())
614 return Range(GetStringSize().width());
615 const size_t layout_index = TextIndexToLayoutIndex(index);
616 internal::TextRunHarfBuzz* run = runs_[run_index];
617 Range bounds = run->GetGraphemeBounds(grapheme_iterator_.get(), layout_index);
618 return run->is_rtl ? Range(bounds.end(), bounds.start()) : bounds;
621 int RenderTextHarfBuzz::GetLayoutTextBaseline() {
622 EnsureLayout();
623 return lines()[0].baseline;
626 SelectionModel RenderTextHarfBuzz::AdjacentCharSelectionModel(
627 const SelectionModel& selection,
628 VisualCursorDirection direction) {
629 DCHECK(!needs_layout_);
630 internal::TextRunHarfBuzz* run;
631 size_t run_index = GetRunContainingCaret(selection);
632 if (run_index >= runs_.size()) {
633 // The cursor is not in any run: we're at the visual and logical edge.
634 SelectionModel edge = EdgeSelectionModel(direction);
635 if (edge.caret_pos() == selection.caret_pos())
636 return edge;
637 int visual_index = (direction == CURSOR_RIGHT) ? 0 : runs_.size() - 1;
638 run = runs_[visual_to_logical_[visual_index]];
639 } else {
640 // If the cursor is moving within the current run, just move it by one
641 // grapheme in the appropriate direction.
642 run = runs_[run_index];
643 size_t caret = selection.caret_pos();
644 bool forward_motion = run->is_rtl == (direction == CURSOR_LEFT);
645 if (forward_motion) {
646 if (caret < LayoutIndexToTextIndex(run->range.end())) {
647 caret = IndexOfAdjacentGrapheme(caret, CURSOR_FORWARD);
648 return SelectionModel(caret, CURSOR_BACKWARD);
650 } else {
651 if (caret > LayoutIndexToTextIndex(run->range.start())) {
652 caret = IndexOfAdjacentGrapheme(caret, CURSOR_BACKWARD);
653 return SelectionModel(caret, CURSOR_FORWARD);
656 // The cursor is at the edge of a run; move to the visually adjacent run.
657 int visual_index = logical_to_visual_[run_index];
658 visual_index += (direction == CURSOR_LEFT) ? -1 : 1;
659 if (visual_index < 0 || visual_index >= static_cast<int>(runs_.size()))
660 return EdgeSelectionModel(direction);
661 run = runs_[visual_to_logical_[visual_index]];
663 bool forward_motion = run->is_rtl == (direction == CURSOR_LEFT);
664 return forward_motion ? FirstSelectionModelInsideRun(run) :
665 LastSelectionModelInsideRun(run);
668 SelectionModel RenderTextHarfBuzz::AdjacentWordSelectionModel(
669 const SelectionModel& selection,
670 VisualCursorDirection direction) {
671 if (obscured())
672 return EdgeSelectionModel(direction);
674 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD);
675 bool success = iter.Init();
676 DCHECK(success);
677 if (!success)
678 return selection;
680 // Match OS specific word break behavior.
681 #if defined(OS_WIN)
682 size_t pos;
683 if (direction == CURSOR_RIGHT) {
684 pos = std::min(selection.caret_pos() + 1, text().length());
685 while (iter.Advance()) {
686 pos = iter.pos();
687 if (iter.IsWord() && pos > selection.caret_pos())
688 break;
690 } else { // direction == CURSOR_LEFT
691 // Notes: We always iterate words from the beginning.
692 // This is probably fast enough for our usage, but we may
693 // want to modify WordIterator so that it can start from the
694 // middle of string and advance backwards.
695 pos = std::max<int>(selection.caret_pos() - 1, 0);
696 while (iter.Advance()) {
697 if (iter.IsWord()) {
698 size_t begin = iter.pos() - iter.GetString().length();
699 if (begin == selection.caret_pos()) {
700 // The cursor is at the beginning of a word.
701 // Move to previous word.
702 break;
703 } else if (iter.pos() >= selection.caret_pos()) {
704 // The cursor is in the middle or at the end of a word.
705 // Move to the top of current word.
706 pos = begin;
707 break;
709 pos = iter.pos() - iter.GetString().length();
713 return SelectionModel(pos, CURSOR_FORWARD);
714 #else
715 SelectionModel cur(selection);
716 for (;;) {
717 cur = AdjacentCharSelectionModel(cur, direction);
718 size_t run = GetRunContainingCaret(cur);
719 if (run == runs_.size())
720 break;
721 const bool is_forward = runs_[run]->is_rtl == (direction == CURSOR_LEFT);
722 size_t cursor = cur.caret_pos();
723 if (is_forward ? iter.IsEndOfWord(cursor) : iter.IsStartOfWord(cursor))
724 break;
726 return cur;
727 #endif
730 std::vector<Rect> RenderTextHarfBuzz::GetSubstringBounds(const Range& range) {
731 DCHECK(!needs_layout_);
732 DCHECK(Range(0, text().length()).Contains(range));
733 Range layout_range(TextIndexToLayoutIndex(range.start()),
734 TextIndexToLayoutIndex(range.end()));
735 DCHECK(Range(0, GetLayoutText().length()).Contains(layout_range));
737 std::vector<Rect> rects;
738 if (layout_range.is_empty())
739 return rects;
740 std::vector<Range> bounds;
742 // Add a Range for each run/selection intersection.
743 for (size_t i = 0; i < runs_.size(); ++i) {
744 internal::TextRunHarfBuzz* run = runs_[visual_to_logical_[i]];
745 Range intersection = run->range.Intersect(layout_range);
746 if (!intersection.IsValid())
747 continue;
748 DCHECK(!intersection.is_reversed());
749 const Range leftmost_character_x = run->GetGraphemeBounds(
750 grapheme_iterator_.get(),
751 run->is_rtl ? intersection.end() - 1 : intersection.start());
752 const Range rightmost_character_x = run->GetGraphemeBounds(
753 grapheme_iterator_.get(),
754 run->is_rtl ? intersection.start() : intersection.end() - 1);
755 Range range_x(leftmost_character_x.start(), rightmost_character_x.end());
756 DCHECK(!range_x.is_reversed());
757 if (range_x.is_empty())
758 continue;
760 // Union this with the last range if they're adjacent.
761 DCHECK(bounds.empty() || bounds.back().GetMax() <= range_x.GetMin());
762 if (!bounds.empty() && bounds.back().GetMax() == range_x.GetMin()) {
763 range_x = Range(bounds.back().GetMin(), range_x.GetMax());
764 bounds.pop_back();
766 bounds.push_back(range_x);
768 for (size_t i = 0; i < bounds.size(); ++i) {
769 std::vector<Rect> current_rects = TextBoundsToViewBounds(bounds[i]);
770 rects.insert(rects.end(), current_rects.begin(), current_rects.end());
772 return rects;
775 size_t RenderTextHarfBuzz::TextIndexToLayoutIndex(size_t index) const {
776 DCHECK_LE(index, text().length());
777 ptrdiff_t i = obscured() ? UTF16IndexToOffset(text(), 0, index) : index;
778 CHECK_GE(i, 0);
779 // Clamp layout indices to the length of the text actually used for layout.
780 return std::min<size_t>(GetLayoutText().length(), i);
783 size_t RenderTextHarfBuzz::LayoutIndexToTextIndex(size_t index) const {
784 if (!obscured())
785 return index;
787 DCHECK_LE(index, GetLayoutText().length());
788 const size_t text_index = UTF16OffsetToIndex(text(), 0, index);
789 DCHECK_LE(text_index, text().length());
790 return text_index;
793 bool RenderTextHarfBuzz::IsValidCursorIndex(size_t index) {
794 if (index == 0 || index == text().length())
795 return true;
796 if (!IsValidLogicalIndex(index))
797 return false;
798 EnsureLayout();
799 return !grapheme_iterator_ || grapheme_iterator_->IsGraphemeBoundary(index);
802 void RenderTextHarfBuzz::ResetLayout() {
803 needs_layout_ = true;
806 void RenderTextHarfBuzz::EnsureLayout() {
807 if (needs_layout_) {
808 runs_.clear();
809 grapheme_iterator_.reset();
811 if (!GetLayoutText().empty()) {
812 grapheme_iterator_.reset(new base::i18n::BreakIterator(GetLayoutText(),
813 base::i18n::BreakIterator::BREAK_CHARACTER));
814 if (!grapheme_iterator_->Init())
815 grapheme_iterator_.reset();
817 ItemizeText();
819 for (size_t i = 0; i < runs_.size(); ++i)
820 ShapeRun(runs_[i]);
822 // Precalculate run width information.
823 float preceding_run_widths = 0.0f;
824 for (size_t i = 0; i < runs_.size(); ++i) {
825 internal::TextRunHarfBuzz* run = runs_[visual_to_logical_[i]];
826 run->preceding_run_widths = preceding_run_widths;
827 preceding_run_widths += run->width;
831 needs_layout_ = false;
832 std::vector<internal::Line> empty_lines;
833 set_lines(&empty_lines);
836 if (lines().empty()) {
837 std::vector<internal::Line> lines;
838 lines.push_back(internal::Line());
839 lines[0].baseline = font_list().GetBaseline();
840 lines[0].size.set_height(font_list().GetHeight());
842 int current_x = 0;
843 SkPaint paint;
845 for (size_t i = 0; i < runs_.size(); ++i) {
846 const internal::TextRunHarfBuzz& run = *runs_[visual_to_logical_[i]];
847 internal::LineSegment segment;
848 segment.x_range = Range(current_x, current_x + run.width);
849 segment.char_range = run.range;
850 segment.run = i;
851 lines[0].segments.push_back(segment);
853 paint.setTypeface(run.skia_face.get());
854 paint.setTextSize(run.font_size);
855 SkPaint::FontMetrics metrics;
856 paint.getFontMetrics(&metrics);
858 lines[0].size.set_width(lines[0].size.width() + run.width);
859 lines[0].size.set_height(std::max(lines[0].size.height(),
860 metrics.fDescent - metrics.fAscent));
861 lines[0].baseline = std::max(lines[0].baseline,
862 SkScalarRoundToInt(-metrics.fAscent));
865 set_lines(&lines);
869 void RenderTextHarfBuzz::DrawVisualText(Canvas* canvas) {
870 DCHECK(!needs_layout_);
871 internal::SkiaTextRenderer renderer(canvas);
872 ApplyFadeEffects(&renderer);
873 ApplyTextShadows(&renderer);
875 #if defined(OS_WIN) || defined(OS_LINUX)
876 renderer.SetFontRenderParams(
877 font_list().GetPrimaryFont().GetFontRenderParams(),
878 background_is_transparent());
879 #endif
881 ApplyCompositionAndSelectionStyles();
883 int current_x = 0;
884 const Vector2d line_offset = GetLineOffset(0);
885 for (size_t i = 0; i < runs_.size(); ++i) {
886 const internal::TextRunHarfBuzz& run = *runs_[visual_to_logical_[i]];
887 renderer.SetTypeface(run.skia_face.get());
888 renderer.SetTextSize(run.font_size);
890 Vector2d origin = line_offset + Vector2d(current_x, lines()[0].baseline);
891 scoped_ptr<SkPoint[]> positions(new SkPoint[run.glyph_count]);
892 for (size_t j = 0; j < run.glyph_count; ++j) {
893 positions[j] = run.positions[j];
894 positions[j].offset(SkIntToScalar(origin.x()), SkIntToScalar(origin.y()));
897 for (BreakList<SkColor>::const_iterator it =
898 colors().GetBreak(run.range.start());
899 it != colors().breaks().end() && it->first < run.range.end();
900 ++it) {
901 const Range intersection = colors().GetRange(it).Intersect(run.range);
902 const Range colored_glyphs = run.CharRangeToGlyphRange(intersection);
903 // The range may be empty if a portion of a multi-character grapheme is
904 // selected, yielding two colors for a single glyph. For now, this just
905 // paints the glyph with a single style, but it should paint it twice,
906 // clipped according to selection bounds. See http://crbug.com/366786
907 if (colored_glyphs.is_empty())
908 continue;
910 renderer.SetForegroundColor(it->second);
911 renderer.DrawPosText(&positions[colored_glyphs.start()],
912 &run.glyphs[colored_glyphs.start()],
913 colored_glyphs.length());
914 int width = (colored_glyphs.end() == run.glyph_count ? run.width :
915 run.positions[colored_glyphs.end()].x()) -
916 run.positions[colored_glyphs.start()].x();
917 renderer.DrawDecorations(origin.x(), origin.y(), width, run.underline,
918 run.strike, run.diagonal_strike);
921 current_x += run.width;
924 renderer.EndDiagonalStrike();
926 UndoCompositionAndSelectionStyles();
929 size_t RenderTextHarfBuzz::GetRunContainingCaret(
930 const SelectionModel& caret) const {
931 DCHECK(!needs_layout_);
932 size_t layout_position = TextIndexToLayoutIndex(caret.caret_pos());
933 LogicalCursorDirection affinity = caret.caret_affinity();
934 for (size_t run = 0; run < runs_.size(); ++run) {
935 if (RangeContainsCaret(runs_[run]->range, layout_position, affinity))
936 return run;
938 return runs_.size();
941 size_t RenderTextHarfBuzz::GetRunContainingXCoord(int x, int* offset) const {
942 DCHECK(!needs_layout_);
943 if (x < 0)
944 return runs_.size();
945 // Find the text run containing the argument point (assumed already offset).
946 int current_x = 0;
947 for (size_t i = 0; i < runs_.size(); ++i) {
948 size_t run = visual_to_logical_[i];
949 current_x += runs_[run]->width;
950 if (x < current_x) {
951 *offset = x - (current_x - runs_[run]->width);
952 return run;
955 return runs_.size();
958 SelectionModel RenderTextHarfBuzz::FirstSelectionModelInsideRun(
959 const internal::TextRunHarfBuzz* run) {
960 size_t position = LayoutIndexToTextIndex(run->range.start());
961 position = IndexOfAdjacentGrapheme(position, CURSOR_FORWARD);
962 return SelectionModel(position, CURSOR_BACKWARD);
965 SelectionModel RenderTextHarfBuzz::LastSelectionModelInsideRun(
966 const internal::TextRunHarfBuzz* run) {
967 size_t position = LayoutIndexToTextIndex(run->range.end());
968 position = IndexOfAdjacentGrapheme(position, CURSOR_BACKWARD);
969 return SelectionModel(position, CURSOR_FORWARD);
972 void RenderTextHarfBuzz::ItemizeText() {
973 const base::string16& text = GetLayoutText();
974 const bool is_text_rtl = GetTextDirection() == base::i18n::RIGHT_TO_LEFT;
975 DCHECK_NE(0U, text.length());
977 // If ICU fails to itemize the text, we create a run that spans the entire
978 // text. This is needed because leaving the runs set empty causes some clients
979 // to misbehave since they expect non-zero text metrics from a non-empty text.
980 base::i18n::BiDiLineIterator bidi_iterator;
981 if (!bidi_iterator.Open(text, is_text_rtl, false)) {
982 internal::TextRunHarfBuzz* run = new internal::TextRunHarfBuzz;
983 run->range = Range(0, text.length());
984 runs_.push_back(run);
985 visual_to_logical_ = logical_to_visual_ = std::vector<int32_t>(1, 0);
986 return;
989 // Temporarily apply composition underlines and selection colors.
990 ApplyCompositionAndSelectionStyles();
992 // Build the list of runs from the script items and ranged styles. Use an
993 // empty color BreakList to avoid breaking runs at color boundaries.
994 BreakList<SkColor> empty_colors;
995 empty_colors.SetMax(text.length());
996 internal::StyleIterator style(empty_colors, styles());
998 for (size_t run_break = 0; run_break < text.length();) {
999 internal::TextRunHarfBuzz* run = new internal::TextRunHarfBuzz;
1000 run->range.set_start(run_break);
1001 run->font_style = (style.style(BOLD) ? Font::BOLD : 0) |
1002 (style.style(ITALIC) ? Font::ITALIC : 0);
1003 run->strike = style.style(STRIKE);
1004 run->diagonal_strike = style.style(DIAGONAL_STRIKE);
1005 run->underline = style.style(UNDERLINE);
1007 int32 script_item_break = 0;
1008 bidi_iterator.GetLogicalRun(run_break, &script_item_break, &run->level);
1009 // Odd BiDi embedding levels correspond to RTL runs.
1010 run->is_rtl = (run->level % 2) == 1;
1011 // Find the length and script of this script run.
1012 script_item_break = ScriptInterval(text, run_break,
1013 script_item_break - run_break, &run->script) + run_break;
1015 // Find the next break and advance the iterators as needed.
1016 run_break = std::min(static_cast<size_t>(script_item_break),
1017 TextIndexToLayoutIndex(style.GetRange().end()));
1019 // Break runs adjacent to character substrings in certain code blocks.
1020 // This avoids using their fallback fonts for more characters than needed,
1021 // in cases like "\x25B6 Media Title", etc. http://crbug.com/278913
1022 if (run_break > run->range.start())
1023 run_break = FindUnusualCharacter(text, run->range.start(), run_break);
1025 DCHECK(IsValidCodePointIndex(text, run_break));
1026 style.UpdatePosition(LayoutIndexToTextIndex(run_break));
1027 run->range.set_end(run_break);
1029 runs_.push_back(run);
1032 // Undo the temporarily applied composition underlines and selection colors.
1033 UndoCompositionAndSelectionStyles();
1035 const size_t num_runs = runs_.size();
1036 std::vector<UBiDiLevel> levels(num_runs);
1037 for (size_t i = 0; i < num_runs; ++i)
1038 levels[i] = runs_[i]->level;
1039 visual_to_logical_.resize(num_runs);
1040 ubidi_reorderVisual(&levels[0], num_runs, &visual_to_logical_[0]);
1041 logical_to_visual_.resize(num_runs);
1042 ubidi_reorderLogical(&levels[0], num_runs, &logical_to_visual_[0]);
1045 void RenderTextHarfBuzz::ShapeRun(internal::TextRunHarfBuzz* run) {
1046 const Font& primary_font = font_list().GetPrimaryFont();
1047 const std::string primary_font_name = primary_font.GetFontName();
1048 run->font_size = primary_font.GetFontSize();
1050 size_t best_font_missing = std::numeric_limits<size_t>::max();
1051 std::string best_font;
1052 std::string current_font;
1054 // Try shaping with |primary_font|.
1055 if (ShapeRunWithFont(run, primary_font_name)) {
1056 current_font = primary_font_name;
1057 size_t current_missing = run->CountMissingGlyphs();
1058 if (current_missing == 0)
1059 return;
1060 if (current_missing < best_font_missing) {
1061 best_font_missing = current_missing;
1062 best_font = current_font;
1066 #if defined(OS_WIN)
1067 Font uniscribe_font;
1068 const base::char16* run_text = &(GetLayoutText()[run->range.start()]);
1069 if (GetUniscribeFallbackFont(primary_font, run_text, run->range.length(),
1070 &uniscribe_font) &&
1071 ShapeRunWithFont(run, uniscribe_font.GetFontName())) {
1072 current_font = uniscribe_font.GetFontName();
1073 size_t current_missing = run->CountMissingGlyphs();
1074 if (current_missing == 0)
1075 return;
1076 if (current_missing < best_font_missing) {
1077 best_font_missing = current_missing;
1078 best_font = current_font;
1081 #endif
1083 // Try shaping with the fonts in the fallback list except the first, which is
1084 // |primary_font|.
1085 std::vector<std::string> fonts = GetFallbackFontFamilies(primary_font_name);
1086 for (size_t i = 1; i < fonts.size(); ++i) {
1087 if (!ShapeRunWithFont(run, fonts[i]))
1088 continue;
1089 current_font = fonts[i];
1090 size_t current_missing = run->CountMissingGlyphs();
1091 if (current_missing == 0)
1092 return;
1093 if (current_missing < best_font_missing) {
1094 best_font_missing = current_missing;
1095 best_font = current_font;
1099 if (!best_font.empty() &&
1100 (best_font == current_font || ShapeRunWithFont(run, best_font))) {
1101 return;
1104 run->glyph_count = 0;
1105 run->width = 0.0f;
1108 bool RenderTextHarfBuzz::ShapeRunWithFont(internal::TextRunHarfBuzz* run,
1109 const std::string& font_family) {
1110 const base::string16& text = GetLayoutText();
1111 skia::RefPtr<SkTypeface> skia_face =
1112 internal::CreateSkiaTypeface(font_family, run->font_style);
1113 if (skia_face == NULL)
1114 return false;
1115 run->skia_face = skia_face;
1116 hb_font_t* harfbuzz_font = CreateHarfBuzzFont(run->skia_face.get(),
1117 run->font_size);
1119 // Create a HarfBuzz buffer and add the string to be shaped. The HarfBuzz
1120 // buffer holds our text, run information to be used by the shaping engine,
1121 // and the resulting glyph data.
1122 hb_buffer_t* buffer = hb_buffer_create();
1123 hb_buffer_add_utf16(buffer, reinterpret_cast<const uint16*>(text.c_str()),
1124 text.length(), run->range.start(), run->range.length());
1125 hb_buffer_set_script(buffer, ICUScriptToHBScript(run->script));
1126 hb_buffer_set_direction(buffer,
1127 run->is_rtl ? HB_DIRECTION_RTL : HB_DIRECTION_LTR);
1128 // TODO(ckocagil): Should we determine the actual language?
1129 hb_buffer_set_language(buffer, hb_language_get_default());
1131 // Shape the text.
1132 hb_shape(harfbuzz_font, buffer, NULL, 0);
1134 // Populate the run fields with the resulting glyph data in the buffer.
1135 unsigned int glyph_count = 0;
1136 hb_glyph_info_t* infos = hb_buffer_get_glyph_infos(buffer, &glyph_count);
1137 run->glyph_count = glyph_count;
1138 hb_glyph_position_t* hb_positions =
1139 hb_buffer_get_glyph_positions(buffer, NULL);
1140 run->glyphs.reset(new uint16[run->glyph_count]);
1141 run->glyph_to_char.resize(run->glyph_count);
1142 run->positions.reset(new SkPoint[run->glyph_count]);
1143 run->width = 0.0f;
1144 for (size_t i = 0; i < run->glyph_count; ++i) {
1145 run->glyphs[i] = infos[i].codepoint;
1146 run->glyph_to_char[i] = infos[i].cluster;
1147 const int x_offset = SkFixedToScalar(hb_positions[i].x_offset);
1148 const int y_offset = SkFixedToScalar(hb_positions[i].y_offset);
1149 run->positions[i].set(run->width + x_offset, -y_offset);
1150 run->width += SkFixedToScalar(hb_positions[i].x_advance);
1153 hb_buffer_destroy(buffer);
1154 hb_font_destroy(harfbuzz_font);
1155 return true;
1158 } // namespace gfx