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"
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"
24 #include "ui/gfx/font_fallback_win.h"
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
42 FontData(GlyphCache
* glyph_cache
) : glyph_cache_(glyph_cache
) {}
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
);
59 template<typename Type
>
60 void DeleteArrayByType(void* data
) {
61 Type
* typed_data
= reinterpret_cast<Type
*>(data
);
65 // Outputs the |width| and |extents| of the glyph with index |codepoint| in
67 void GetGlyphWidthAndExtents(SkPaint
* paint
,
68 hb_codepoint_t codepoint
,
70 hb_glyph_extents_t
* extents
) {
71 DCHECK_LE(codepoint
, 0xFFFFU
);
72 paint
->setTextEncoding(SkPaint::kGlyphID_TextEncoding
);
76 uint16_t glyph
= codepoint
;
78 paint
->getTextWidths(&glyph
, sizeof(glyph
), &sk_width
, &sk_bounds
);
80 *width
= SkiaScalarToHarfBuzzPosition(sk_width
);
82 // Invert y-axis because Skia is y-grows-down but we set up HarfBuzz to be
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
,
95 hb_codepoint_t unicode
,
96 hb_codepoint_t variation_selector
,
97 hb_codepoint_t
* glyph
,
99 FontData
* font_data
= reinterpret_cast<FontData
*>(data
);
100 GlyphCache
* cache
= font_data
->glyph_cache_
;
102 bool exists
= cache
->count(unicode
) != 0;
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
];
112 // Returns the horizontal advance value of the |glyph|.
113 hb_position_t
GetGlyphHorizontalAdvance(hb_font_t
* font
,
115 hb_codepoint_t glyph
,
117 FontData
* font_data
= reinterpret_cast<FontData
*>(data
);
118 hb_position_t advance
= 0;
120 GetGlyphWidthAndExtents(&font_data
->paint_
, glyph
, &advance
, 0);
124 hb_bool_t
GetGlyphHorizontalOrigin(hb_font_t
* font
,
126 hb_codepoint_t glyph
,
130 // Just return true, like the HarfBuzz-FreeType implementation.
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
))
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
,
153 hb_codepoint_t left_glyph
,
154 hb_codepoint_t right_glyph
,
156 FontData
* font_data
= reinterpret_cast<FontData
*>(data
);
157 if (font_data
->paint_
.isVerticalText()) {
158 // We don't support cross-stream kerning.
162 return GetGlyphKerning(font_data
, left_glyph
, right_glyph
);
165 hb_position_t
GetGlyphVerticalKerning(hb_font_t
* font
,
167 hb_codepoint_t top_glyph
,
168 hb_codepoint_t bottom_glyph
,
170 FontData
* font_data
= reinterpret_cast<FontData
*>(data
);
171 if (!font_data
->paint_
.isVerticalText()) {
172 // We don't support cross-stream kerning.
176 return GetGlyphKerning(font_data
, top_glyph
, bottom_glyph
);
179 // Writes the |extents| of |glyph|.
180 hb_bool_t
GetGlyphExtents(hb_font_t
* font
,
182 hb_codepoint_t glyph
,
183 hb_glyph_extents_t
* extents
,
185 FontData
* font_data
= reinterpret_cast<FontData
*>(data
);
187 GetGlyphWidthAndExtents(&font_data
->paint_
, glyph
, 0, extents
);
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_
);
209 hb_font_funcs_destroy(font_funcs_
);
212 hb_font_funcs_t
* get() { return font_funcs_
; }
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
);
230 scoped_ptr
<char[]> buffer(new char[table_size
]);
233 size_t actual_size
= typeface
->getTableData(tag
, 0, table_size
, buffer
.get());
234 if (table_size
!= actual_size
)
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.
250 HarfBuzzFace() : face_(NULL
) {}
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
);
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
,
305 const int32 run_length
= static_cast<int32
>(run_break
- run_start
);
306 base::i18n::UTF16CharIterator
iter(text
.c_str() + run_start
,
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();
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
)) {
328 if (first
> USCRIPT_INVALID_CODE
&& first
<= USCRIPT_INHERITED
)
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
))
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
))
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
,
355 size_t* result_size
) {
356 UScriptCode scripts
[kMaxScripts
] = { USCRIPT_INVALID_CODE
};
357 int count
= GetScriptExtensions(codepoint
, scripts
);
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
;
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
,
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];
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
,
418 Iterator elements_begin
,
419 Iterator elements_end
,
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
);
431 reversed
? elements_end
- element
: element
- elements_begin
);
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());
445 TextRunHarfBuzz::TextRunHarfBuzz()
447 preceding_run_widths(0.0f
),
450 script(USCRIPT_INVALID_CODE
),
451 glyph_count(static_cast<size_t>(-1)),
455 diagonal_strike(false),
458 TextRunHarfBuzz::~TextRunHarfBuzz() {}
460 void TextRunHarfBuzz::GetClusterAt(size_t pos
,
462 Range
* glyphs
) const {
463 DCHECK(range
.Contains(Range(pos
, pos
+ 1)));
467 if (glyph_count
== 0) {
474 GetClusterAtImpl(pos
, range
, glyph_to_char
.rbegin(), glyph_to_char
.rend(),
475 true, chars
, glyphs
);
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());
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;
501 for (size_t i
= 0; i
< glyph_count
; ++i
)
502 missing
+= (glyphs
[i
] == kMissingGlyphId
) ? 1 : 0;
506 Range
TextRunHarfBuzz::GetGraphemeBounds(
507 base::i18n::BreakIterator
* grapheme_iterator
,
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
);
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
) {
529 for (size_t i
= chars
.start(); i
< chars
.end(); ++i
) {
530 if (grapheme_iterator
->IsGraphemeBoundary(i
)) {
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()
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() {
571 return lines()[0].size
;
574 SelectionModel
RenderTextHarfBuzz::FindCursorPosition(const Point
& point
) {
577 int x
= ToTextPoint(point
).x();
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
) {
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
));
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() {
605 return std::vector
<RenderText::FontSpan
>();
608 Range
RenderTextHarfBuzz::GetGlyphBounds(size_t index
) {
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() {
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())
637 int visual_index
= (direction
== CURSOR_RIGHT
) ? 0 : runs_
.size() - 1;
638 run
= runs_
[visual_to_logical_
[visual_index
]];
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
);
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
) {
672 return EdgeSelectionModel(direction
);
674 base::i18n::BreakIterator
iter(text(), base::i18n::BreakIterator::BREAK_WORD
);
675 bool success
= iter
.Init();
680 // Match OS specific word break behavior.
683 if (direction
== CURSOR_RIGHT
) {
684 pos
= std::min(selection
.caret_pos() + 1, text().length());
685 while (iter
.Advance()) {
687 if (iter
.IsWord() && pos
> selection
.caret_pos())
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()) {
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.
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.
709 pos
= iter
.pos() - iter
.GetString().length();
713 return SelectionModel(pos
, CURSOR_FORWARD
);
715 SelectionModel
cur(selection
);
717 cur
= AdjacentCharSelectionModel(cur
, direction
);
718 size_t run
= GetRunContainingCaret(cur
);
719 if (run
== runs_
.size())
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
))
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())
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())
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())
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());
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());
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
;
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 {
787 DCHECK_LE(index
, GetLayoutText().length());
788 const size_t text_index
= UTF16OffsetToIndex(text(), 0, index
);
789 DCHECK_LE(text_index
, text().length());
793 bool RenderTextHarfBuzz::IsValidCursorIndex(size_t index
) {
794 if (index
== 0 || index
== text().length())
796 if (!IsValidLogicalIndex(index
))
799 return !grapheme_iterator_
|| grapheme_iterator_
->IsGraphemeBoundary(index
);
802 void RenderTextHarfBuzz::ResetLayout() {
803 needs_layout_
= true;
806 void RenderTextHarfBuzz::EnsureLayout() {
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();
819 for (size_t i
= 0; i
< runs_
.size(); ++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());
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
;
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
));
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());
881 ApplyCompositionAndSelectionStyles();
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();
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())
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
))
941 size_t RenderTextHarfBuzz::GetRunContainingXCoord(int x
, int* offset
) const {
942 DCHECK(!needs_layout_
);
945 // Find the text run containing the argument point (assumed already offset).
947 for (size_t i
= 0; i
< runs_
.size(); ++i
) {
948 size_t run
= visual_to_logical_
[i
];
949 current_x
+= runs_
[run
]->width
;
951 *offset
= x
- (current_x
- runs_
[run
]->width
);
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);
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)
1060 if (current_missing
< best_font_missing
) {
1061 best_font_missing
= current_missing
;
1062 best_font
= current_font
;
1067 Font uniscribe_font
;
1068 const base::char16
* run_text
= &(GetLayoutText()[run
->range
.start()]);
1069 if (GetUniscribeFallbackFont(primary_font
, run_text
, run
->range
.length(),
1071 ShapeRunWithFont(run
, uniscribe_font
.GetFontName())) {
1072 current_font
= uniscribe_font
.GetFontName();
1073 size_t current_missing
= run
->CountMissingGlyphs();
1074 if (current_missing
== 0)
1076 if (current_missing
< best_font_missing
) {
1077 best_font_missing
= current_missing
;
1078 best_font
= current_font
;
1083 // Try shaping with the fonts in the fallback list except the first, which is
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
]))
1089 current_font
= fonts
[i
];
1090 size_t current_missing
= run
->CountMissingGlyphs();
1091 if (current_missing
== 0)
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
))) {
1104 run
->glyph_count
= 0;
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
)
1115 run
->skia_face
= skia_face
;
1116 hb_font_t
* harfbuzz_font
= CreateHarfBuzzFont(run
->skia_face
.get(),
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());
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
]);
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
);