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
,
274 const FontRenderParams
& params
,
275 bool background_is_transparent
) {
276 typedef std::pair
<HarfBuzzFace
, GlyphCache
> FaceCache
;
278 // TODO(ckocagil): This shouldn't grow indefinitely. Maybe use base::MRUCache?
279 static std::map
<SkFontID
, FaceCache
> face_caches
;
281 FaceCache
* face_cache
= &face_caches
[skia_face
->uniqueID()];
282 if (face_cache
->first
.get() == NULL
)
283 face_cache
->first
.Init(skia_face
);
285 hb_font_t
* harfbuzz_font
= hb_font_create(face_cache
->first
.get());
286 const int scale
= SkScalarToFixed(text_size
);
287 hb_font_set_scale(harfbuzz_font
, scale
, scale
);
288 FontData
* hb_font_data
= new FontData(&face_cache
->second
);
289 hb_font_data
->paint_
.setTypeface(skia_face
);
290 hb_font_data
->paint_
.setTextSize(text_size
);
291 // TODO(ckocagil): Do we need to update these params later?
292 internal::ApplyRenderParams(params
, background_is_transparent
,
293 &hb_font_data
->paint_
);
294 hb_font_set_funcs(harfbuzz_font
, g_font_funcs
.Get().get(), hb_font_data
,
295 DeleteByType
<FontData
>);
296 hb_font_make_immutable(harfbuzz_font
);
297 return harfbuzz_font
;
300 // Returns true if characters of |block_code| may trigger font fallback.
301 bool IsUnusualBlockCode(UBlockCode block_code
) {
302 return block_code
== UBLOCK_GEOMETRIC_SHAPES
||
303 block_code
== UBLOCK_MISCELLANEOUS_SYMBOLS
;
306 // Returns the index of the first unusual character after a usual character or
307 // vice versa. Unusual characters are defined by |IsUnusualBlockCode|.
308 size_t FindUnusualCharacter(const base::string16
& text
,
311 const int32 run_length
= static_cast<int32
>(run_break
- run_start
);
312 base::i18n::UTF16CharIterator
iter(text
.c_str() + run_start
,
314 const UBlockCode first_block_code
= ublock_getCode(iter
.get());
315 const bool first_block_unusual
= IsUnusualBlockCode(first_block_code
);
316 while (iter
.Advance() && iter
.array_pos() < run_length
) {
317 const UBlockCode current_block_code
= ublock_getCode(iter
.get());
318 if (current_block_code
!= first_block_code
&&
319 (first_block_unusual
|| IsUnusualBlockCode(current_block_code
))) {
320 return run_start
+ iter
.array_pos();
326 // If the given scripts match, returns the one that isn't USCRIPT_COMMON or
327 // USCRIPT_INHERITED, i.e. the more specific one. Otherwise returns
328 // USCRIPT_INVALID_CODE.
329 UScriptCode
ScriptIntersect(UScriptCode first
, UScriptCode second
) {
330 if (first
== second
||
331 (second
> USCRIPT_INVALID_CODE
&& second
<= USCRIPT_INHERITED
)) {
334 if (first
> USCRIPT_INVALID_CODE
&& first
<= USCRIPT_INHERITED
)
336 return USCRIPT_INVALID_CODE
;
339 // Writes the script and the script extensions of the character with the
340 // Unicode |codepoint|. Returns the number of written scripts.
341 int GetScriptExtensions(UChar32 codepoint
, UScriptCode
* scripts
) {
342 UErrorCode icu_error
= U_ZERO_ERROR
;
343 // ICU documentation incorrectly states that the result of
344 // |uscript_getScriptExtensions| will contain the regular script property.
345 // Write the character's script property to the first element.
346 scripts
[0] = uscript_getScript(codepoint
, &icu_error
);
347 if (U_FAILURE(icu_error
))
349 // Fill the rest of |scripts| with the extensions.
350 int count
= uscript_getScriptExtensions(codepoint
, scripts
+ 1,
351 kMaxScripts
- 1, &icu_error
);
352 if (U_FAILURE(icu_error
))
357 // Intersects the script extensions set of |codepoint| with |result| and writes
358 // to |result|, reading and updating |result_size|.
359 void ScriptSetIntersect(UChar32 codepoint
,
361 size_t* result_size
) {
362 UScriptCode scripts
[kMaxScripts
] = { USCRIPT_INVALID_CODE
};
363 int count
= GetScriptExtensions(codepoint
, scripts
);
367 for (size_t i
= 0; i
< *result_size
; ++i
) {
368 for (int j
= 0; j
< count
; ++j
) {
369 UScriptCode intersection
= ScriptIntersect(result
[i
], scripts
[j
]);
370 if (intersection
!= USCRIPT_INVALID_CODE
) {
371 result
[out_size
++] = intersection
;
377 *result_size
= out_size
;
380 // Find the longest sequence of characters from 0 and up to |length| that
381 // have at least one common UScriptCode value. Writes the common script value to
382 // |script| and returns the length of the sequence. Takes the characters' script
383 // extensions into account. http://www.unicode.org/reports/tr24/#ScriptX
385 // Consider 3 characters with the script values {Kana}, {Hira, Kana}, {Kana}.
386 // Without script extensions only the first script in each set would be taken
387 // into account, resulting in 3 runs where 1 would be enough.
388 // TODO(ckocagil): Write a unit test for the case above.
389 int ScriptInterval(const base::string16
& text
,
392 UScriptCode
* script
) {
393 DCHECK_GT(length
, 0U);
395 UScriptCode scripts
[kMaxScripts
] = { USCRIPT_INVALID_CODE
};
397 base::i18n::UTF16CharIterator
char_iterator(text
.c_str() + start
, length
);
398 size_t scripts_size
= GetScriptExtensions(char_iterator
.get(), scripts
);
399 *script
= scripts
[0];
401 while (char_iterator
.Advance()) {
402 ScriptSetIntersect(char_iterator
.get(), scripts
, &scripts_size
);
403 if (scripts_size
== 0U)
404 return char_iterator
.array_pos();
405 *script
= scripts
[0];
411 // A port of hb_icu_script_to_script because harfbuzz on CrOS is built without
412 // hb-icu. See http://crbug.com/356929
413 inline hb_script_t
ICUScriptToHBScript(UScriptCode script
) {
414 if (script
== USCRIPT_INVALID_CODE
)
415 return HB_SCRIPT_INVALID
;
416 return hb_script_from_string(uscript_getShortName(script
), -1);
419 // Helper template function for |TextRunHarfBuzz::GetClusterAt()|. |Iterator|
420 // can be a forward or reverse iterator type depending on the text direction.
421 template <class Iterator
>
422 void GetClusterAtImpl(size_t pos
,
424 Iterator elements_begin
,
425 Iterator elements_end
,
429 Iterator element
= std::upper_bound(elements_begin
, elements_end
, pos
);
430 chars
->set_end(element
== elements_end
? range
.end() : *element
);
431 glyphs
->set_end(reversed
? elements_end
- element
: element
- elements_begin
);
433 DCHECK(element
!= elements_begin
);
434 while (--element
!= elements_begin
&& *element
== *(element
- 1));
435 chars
->set_start(*element
);
437 reversed
? elements_end
- element
: element
- elements_begin
);
439 *glyphs
= Range(glyphs
->end(), glyphs
->start());
441 DCHECK(!chars
->is_reversed());
442 DCHECK(!chars
->is_empty());
443 DCHECK(!glyphs
->is_reversed());
444 DCHECK(!glyphs
->is_empty());
451 TextRunHarfBuzz::TextRunHarfBuzz()
453 preceding_run_widths(0.0f
),
456 script(USCRIPT_INVALID_CODE
),
457 glyph_count(static_cast<size_t>(-1)),
461 diagonal_strike(false),
464 TextRunHarfBuzz::~TextRunHarfBuzz() {}
466 void TextRunHarfBuzz::GetClusterAt(size_t pos
,
468 Range
* glyphs
) const {
469 DCHECK(range
.Contains(Range(pos
, pos
+ 1)));
473 if (glyph_count
== 0) {
480 GetClusterAtImpl(pos
, range
, glyph_to_char
.rbegin(), glyph_to_char
.rend(),
481 true, chars
, glyphs
);
485 GetClusterAtImpl(pos
, range
, glyph_to_char
.begin(), glyph_to_char
.end(),
486 false, chars
, glyphs
);
489 Range
TextRunHarfBuzz::CharRangeToGlyphRange(const Range
& char_range
) const {
490 DCHECK(range
.Contains(char_range
));
491 DCHECK(!char_range
.is_reversed());
492 DCHECK(!char_range
.is_empty());
497 GetClusterAt(char_range
.start(), &temp_range
, &start_glyphs
);
498 GetClusterAt(char_range
.end() - 1, &temp_range
, &end_glyphs
);
500 return is_rtl
? Range(end_glyphs
.start(), start_glyphs
.end()) :
501 Range(start_glyphs
.start(), end_glyphs
.end());
504 size_t TextRunHarfBuzz::CountMissingGlyphs() const {
505 static const int kMissingGlyphId
= 0;
507 for (size_t i
= 0; i
< glyph_count
; ++i
)
508 missing
+= (glyphs
[i
] == kMissingGlyphId
) ? 1 : 0;
512 Range
TextRunHarfBuzz::GetGraphemeBounds(
513 base::i18n::BreakIterator
* grapheme_iterator
,
515 DCHECK_LT(text_index
, range
.end());
516 // TODO(msw): Support floating point grapheme bounds.
517 const int preceding_run_widths_int
= SkScalarRoundToInt(preceding_run_widths
);
518 if (glyph_count
== 0)
519 return Range(preceding_run_widths_int
, preceding_run_widths_int
+ width
);
523 GetClusterAt(text_index
, &chars
, &glyphs
);
524 const int cluster_begin_x
= SkScalarRoundToInt(positions
[glyphs
.start()].x());
525 const int cluster_end_x
= glyphs
.end() < glyph_count
?
526 SkScalarRoundToInt(positions
[glyphs
.end()].x()) : width
;
528 // A cluster consists of a number of code points and corresponds to a number
529 // of glyphs that should be drawn together. A cluster can contain multiple
530 // graphemes. In order to place the cursor at a grapheme boundary inside the
531 // cluster, we simply divide the cluster width by the number of graphemes.
532 if (chars
.length() > 1 && grapheme_iterator
) {
535 for (size_t i
= chars
.start(); i
< chars
.end(); ++i
) {
536 if (grapheme_iterator
->IsGraphemeBoundary(i
)) {
545 before
= total
- before
- 1;
546 DCHECK_GE(before
, 0);
547 DCHECK_LT(before
, total
);
548 const int cluster_width
= cluster_end_x
- cluster_begin_x
;
549 const int grapheme_begin_x
= cluster_begin_x
+ static_cast<int>(0.5f
+
550 cluster_width
* before
/ static_cast<float>(total
));
551 const int grapheme_end_x
= cluster_begin_x
+ static_cast<int>(0.5f
+
552 cluster_width
* (before
+ 1) / static_cast<float>(total
));
553 return Range(preceding_run_widths_int
+ grapheme_begin_x
,
554 preceding_run_widths_int
+ grapheme_end_x
);
558 return Range(preceding_run_widths_int
+ cluster_begin_x
,
559 preceding_run_widths_int
+ cluster_end_x
);
562 } // namespace internal
564 RenderTextHarfBuzz::RenderTextHarfBuzz()
566 needs_layout_(false) {}
568 RenderTextHarfBuzz::~RenderTextHarfBuzz() {}
570 Size
RenderTextHarfBuzz::GetStringSize() {
571 const SizeF size_f
= GetStringSizeF();
572 return Size(std::ceil(size_f
.width()), size_f
.height());
575 SizeF
RenderTextHarfBuzz::GetStringSizeF() {
577 return lines()[0].size
;
580 SelectionModel
RenderTextHarfBuzz::FindCursorPosition(const Point
& point
) {
583 int x
= ToTextPoint(point
).x();
585 size_t run_index
= GetRunContainingXCoord(x
, &offset
);
586 if (run_index
>= runs_
.size())
587 return EdgeSelectionModel((x
< 0) ? CURSOR_LEFT
: CURSOR_RIGHT
);
588 const internal::TextRunHarfBuzz
& run
= *runs_
[run_index
];
590 for (size_t i
= 0; i
< run
.glyph_count
; ++i
) {
592 i
+ 1 == run
.glyph_count
? run
.width
: run
.positions
[i
+ 1].x();
593 const SkScalar middle
= (end
+ run
.positions
[i
].x()) / 2;
595 if (offset
< middle
) {
596 return SelectionModel(LayoutIndexToTextIndex(
597 run
.glyph_to_char
[i
] + (run
.is_rtl
? 1 : 0)),
598 (run
.is_rtl
? CURSOR_BACKWARD
: CURSOR_FORWARD
));
601 return SelectionModel(LayoutIndexToTextIndex(
602 run
.glyph_to_char
[i
] + (run
.is_rtl
? 0 : 1)),
603 (run
.is_rtl
? CURSOR_FORWARD
: CURSOR_BACKWARD
));
606 return EdgeSelectionModel(CURSOR_RIGHT
);
609 std::vector
<RenderText::FontSpan
> RenderTextHarfBuzz::GetFontSpansForTesting() {
611 return std::vector
<RenderText::FontSpan
>();
614 Range
RenderTextHarfBuzz::GetGlyphBounds(size_t index
) {
616 const size_t run_index
=
617 GetRunContainingCaret(SelectionModel(index
, CURSOR_FORWARD
));
618 // Return edge bounds if the index is invalid or beyond the layout text size.
619 if (run_index
>= runs_
.size())
620 return Range(GetStringSize().width());
621 const size_t layout_index
= TextIndexToLayoutIndex(index
);
622 internal::TextRunHarfBuzz
* run
= runs_
[run_index
];
623 Range bounds
= run
->GetGraphemeBounds(grapheme_iterator_
.get(), layout_index
);
624 return run
->is_rtl
? Range(bounds
.end(), bounds
.start()) : bounds
;
627 int RenderTextHarfBuzz::GetLayoutTextBaseline() {
629 return lines()[0].baseline
;
632 SelectionModel
RenderTextHarfBuzz::AdjacentCharSelectionModel(
633 const SelectionModel
& selection
,
634 VisualCursorDirection direction
) {
635 DCHECK(!needs_layout_
);
636 internal::TextRunHarfBuzz
* run
;
637 size_t run_index
= GetRunContainingCaret(selection
);
638 if (run_index
>= runs_
.size()) {
639 // The cursor is not in any run: we're at the visual and logical edge.
640 SelectionModel edge
= EdgeSelectionModel(direction
);
641 if (edge
.caret_pos() == selection
.caret_pos())
643 int visual_index
= (direction
== CURSOR_RIGHT
) ? 0 : runs_
.size() - 1;
644 run
= runs_
[visual_to_logical_
[visual_index
]];
646 // If the cursor is moving within the current run, just move it by one
647 // grapheme in the appropriate direction.
648 run
= runs_
[run_index
];
649 size_t caret
= selection
.caret_pos();
650 bool forward_motion
= run
->is_rtl
== (direction
== CURSOR_LEFT
);
651 if (forward_motion
) {
652 if (caret
< LayoutIndexToTextIndex(run
->range
.end())) {
653 caret
= IndexOfAdjacentGrapheme(caret
, CURSOR_FORWARD
);
654 return SelectionModel(caret
, CURSOR_BACKWARD
);
657 if (caret
> LayoutIndexToTextIndex(run
->range
.start())) {
658 caret
= IndexOfAdjacentGrapheme(caret
, CURSOR_BACKWARD
);
659 return SelectionModel(caret
, CURSOR_FORWARD
);
662 // The cursor is at the edge of a run; move to the visually adjacent run.
663 int visual_index
= logical_to_visual_
[run_index
];
664 visual_index
+= (direction
== CURSOR_LEFT
) ? -1 : 1;
665 if (visual_index
< 0 || visual_index
>= static_cast<int>(runs_
.size()))
666 return EdgeSelectionModel(direction
);
667 run
= runs_
[visual_to_logical_
[visual_index
]];
669 bool forward_motion
= run
->is_rtl
== (direction
== CURSOR_LEFT
);
670 return forward_motion
? FirstSelectionModelInsideRun(run
) :
671 LastSelectionModelInsideRun(run
);
674 SelectionModel
RenderTextHarfBuzz::AdjacentWordSelectionModel(
675 const SelectionModel
& selection
,
676 VisualCursorDirection direction
) {
678 return EdgeSelectionModel(direction
);
680 base::i18n::BreakIterator
iter(text(), base::i18n::BreakIterator::BREAK_WORD
);
681 bool success
= iter
.Init();
686 // Match OS specific word break behavior.
689 if (direction
== CURSOR_RIGHT
) {
690 pos
= std::min(selection
.caret_pos() + 1, text().length());
691 while (iter
.Advance()) {
693 if (iter
.IsWord() && pos
> selection
.caret_pos())
696 } else { // direction == CURSOR_LEFT
697 // Notes: We always iterate words from the beginning.
698 // This is probably fast enough for our usage, but we may
699 // want to modify WordIterator so that it can start from the
700 // middle of string and advance backwards.
701 pos
= std::max
<int>(selection
.caret_pos() - 1, 0);
702 while (iter
.Advance()) {
704 size_t begin
= iter
.pos() - iter
.GetString().length();
705 if (begin
== selection
.caret_pos()) {
706 // The cursor is at the beginning of a word.
707 // Move to previous word.
709 } else if (iter
.pos() >= selection
.caret_pos()) {
710 // The cursor is in the middle or at the end of a word.
711 // Move to the top of current word.
715 pos
= iter
.pos() - iter
.GetString().length();
719 return SelectionModel(pos
, CURSOR_FORWARD
);
721 SelectionModel
cur(selection
);
723 cur
= AdjacentCharSelectionModel(cur
, direction
);
724 size_t run
= GetRunContainingCaret(cur
);
725 if (run
== runs_
.size())
727 const bool is_forward
= runs_
[run
]->is_rtl
== (direction
== CURSOR_LEFT
);
728 size_t cursor
= cur
.caret_pos();
729 if (is_forward
? iter
.IsEndOfWord(cursor
) : iter
.IsStartOfWord(cursor
))
736 std::vector
<Rect
> RenderTextHarfBuzz::GetSubstringBounds(const Range
& range
) {
737 DCHECK(!needs_layout_
);
738 DCHECK(Range(0, text().length()).Contains(range
));
739 Range
layout_range(TextIndexToLayoutIndex(range
.start()),
740 TextIndexToLayoutIndex(range
.end()));
741 DCHECK(Range(0, GetLayoutText().length()).Contains(layout_range
));
743 std::vector
<Rect
> rects
;
744 if (layout_range
.is_empty())
746 std::vector
<Range
> bounds
;
748 // Add a Range for each run/selection intersection.
749 for (size_t i
= 0; i
< runs_
.size(); ++i
) {
750 internal::TextRunHarfBuzz
* run
= runs_
[visual_to_logical_
[i
]];
751 Range intersection
= run
->range
.Intersect(layout_range
);
752 if (!intersection
.IsValid())
754 DCHECK(!intersection
.is_reversed());
755 const Range leftmost_character_x
= run
->GetGraphemeBounds(
756 grapheme_iterator_
.get(),
757 run
->is_rtl
? intersection
.end() - 1 : intersection
.start());
758 const Range rightmost_character_x
= run
->GetGraphemeBounds(
759 grapheme_iterator_
.get(),
760 run
->is_rtl
? intersection
.start() : intersection
.end() - 1);
761 Range
range_x(leftmost_character_x
.start(), rightmost_character_x
.end());
762 DCHECK(!range_x
.is_reversed());
763 if (range_x
.is_empty())
766 // Union this with the last range if they're adjacent.
767 DCHECK(bounds
.empty() || bounds
.back().GetMax() <= range_x
.GetMin());
768 if (!bounds
.empty() && bounds
.back().GetMax() == range_x
.GetMin()) {
769 range_x
= Range(bounds
.back().GetMin(), range_x
.GetMax());
772 bounds
.push_back(range_x
);
774 for (size_t i
= 0; i
< bounds
.size(); ++i
) {
775 std::vector
<Rect
> current_rects
= TextBoundsToViewBounds(bounds
[i
]);
776 rects
.insert(rects
.end(), current_rects
.begin(), current_rects
.end());
781 size_t RenderTextHarfBuzz::TextIndexToLayoutIndex(size_t index
) const {
782 DCHECK_LE(index
, text().length());
783 ptrdiff_t i
= obscured() ? UTF16IndexToOffset(text(), 0, index
) : index
;
785 // Clamp layout indices to the length of the text actually used for layout.
786 return std::min
<size_t>(GetLayoutText().length(), i
);
789 size_t RenderTextHarfBuzz::LayoutIndexToTextIndex(size_t index
) const {
793 DCHECK_LE(index
, GetLayoutText().length());
794 const size_t text_index
= UTF16OffsetToIndex(text(), 0, index
);
795 DCHECK_LE(text_index
, text().length());
799 bool RenderTextHarfBuzz::IsValidCursorIndex(size_t index
) {
800 if (index
== 0 || index
== text().length())
802 if (!IsValidLogicalIndex(index
))
805 return !grapheme_iterator_
|| grapheme_iterator_
->IsGraphemeBoundary(index
);
808 void RenderTextHarfBuzz::ResetLayout() {
809 needs_layout_
= true;
812 void RenderTextHarfBuzz::EnsureLayout() {
815 grapheme_iterator_
.reset();
817 if (!GetLayoutText().empty()) {
818 grapheme_iterator_
.reset(new base::i18n::BreakIterator(GetLayoutText(),
819 base::i18n::BreakIterator::BREAK_CHARACTER
));
820 if (!grapheme_iterator_
->Init())
821 grapheme_iterator_
.reset();
825 for (size_t i
= 0; i
< runs_
.size(); ++i
)
828 // Precalculate run width information.
829 float preceding_run_widths
= 0.0f
;
830 for (size_t i
= 0; i
< runs_
.size(); ++i
) {
831 internal::TextRunHarfBuzz
* run
= runs_
[visual_to_logical_
[i
]];
832 run
->preceding_run_widths
= preceding_run_widths
;
833 preceding_run_widths
+= run
->width
;
837 needs_layout_
= false;
838 std::vector
<internal::Line
> empty_lines
;
839 set_lines(&empty_lines
);
842 if (lines().empty()) {
843 std::vector
<internal::Line
> lines
;
844 lines
.push_back(internal::Line());
845 lines
[0].baseline
= font_list().GetBaseline();
846 lines
[0].size
.set_height(font_list().GetHeight());
851 for (size_t i
= 0; i
< runs_
.size(); ++i
) {
852 const internal::TextRunHarfBuzz
& run
= *runs_
[visual_to_logical_
[i
]];
853 internal::LineSegment segment
;
854 segment
.x_range
= Range(current_x
, current_x
+ run
.width
);
855 segment
.char_range
= run
.range
;
857 lines
[0].segments
.push_back(segment
);
859 paint
.setTypeface(run
.skia_face
.get());
860 paint
.setTextSize(run
.font_size
);
861 SkPaint::FontMetrics metrics
;
862 paint
.getFontMetrics(&metrics
);
864 lines
[0].size
.set_width(lines
[0].size
.width() + run
.width
);
865 lines
[0].size
.set_height(std::max(lines
[0].size
.height(),
866 metrics
.fDescent
- metrics
.fAscent
));
867 lines
[0].baseline
= std::max(lines
[0].baseline
,
868 SkScalarRoundToInt(-metrics
.fAscent
));
875 void RenderTextHarfBuzz::DrawVisualText(Canvas
* canvas
) {
876 DCHECK(!needs_layout_
);
877 internal::SkiaTextRenderer
renderer(canvas
);
878 ApplyFadeEffects(&renderer
);
879 ApplyTextShadows(&renderer
);
880 ApplyCompositionAndSelectionStyles();
883 const Vector2d line_offset
= GetLineOffset(0);
884 for (size_t i
= 0; i
< runs_
.size(); ++i
) {
885 const internal::TextRunHarfBuzz
& run
= *runs_
[visual_to_logical_
[i
]];
886 renderer
.SetTypeface(run
.skia_face
.get());
887 renderer
.SetTextSize(run
.font_size
);
888 renderer
.SetFontRenderParams(run
.render_params
,
889 background_is_transparent());
891 Vector2d origin
= line_offset
+ Vector2d(current_x
, lines()[0].baseline
);
892 scoped_ptr
<SkPoint
[]> positions(new SkPoint
[run
.glyph_count
]);
893 for (size_t j
= 0; j
< run
.glyph_count
; ++j
) {
894 positions
[j
] = run
.positions
[j
];
895 positions
[j
].offset(SkIntToScalar(origin
.x()), SkIntToScalar(origin
.y()));
898 for (BreakList
<SkColor
>::const_iterator it
=
899 colors().GetBreak(run
.range
.start());
900 it
!= colors().breaks().end() && it
->first
< run
.range
.end();
902 const Range intersection
= colors().GetRange(it
).Intersect(run
.range
);
903 const Range colored_glyphs
= run
.CharRangeToGlyphRange(intersection
);
904 // The range may be empty if a portion of a multi-character grapheme is
905 // selected, yielding two colors for a single glyph. For now, this just
906 // paints the glyph with a single style, but it should paint it twice,
907 // clipped according to selection bounds. See http://crbug.com/366786
908 if (colored_glyphs
.is_empty())
911 renderer
.SetForegroundColor(it
->second
);
912 renderer
.DrawPosText(&positions
[colored_glyphs
.start()],
913 &run
.glyphs
[colored_glyphs
.start()],
914 colored_glyphs
.length());
915 int width
= (colored_glyphs
.end() == run
.glyph_count
? run
.width
:
916 run
.positions
[colored_glyphs
.end()].x()) -
917 run
.positions
[colored_glyphs
.start()].x();
918 renderer
.DrawDecorations(origin
.x(), origin
.y(), width
, run
.underline
,
919 run
.strike
, run
.diagonal_strike
);
922 current_x
+= run
.width
;
925 renderer
.EndDiagonalStrike();
927 UndoCompositionAndSelectionStyles();
930 size_t RenderTextHarfBuzz::GetRunContainingCaret(
931 const SelectionModel
& caret
) const {
932 DCHECK(!needs_layout_
);
933 size_t layout_position
= TextIndexToLayoutIndex(caret
.caret_pos());
934 LogicalCursorDirection affinity
= caret
.caret_affinity();
935 for (size_t run
= 0; run
< runs_
.size(); ++run
) {
936 if (RangeContainsCaret(runs_
[run
]->range
, layout_position
, affinity
))
942 size_t RenderTextHarfBuzz::GetRunContainingXCoord(int x
, int* offset
) const {
943 DCHECK(!needs_layout_
);
946 // Find the text run containing the argument point (assumed already offset).
948 for (size_t i
= 0; i
< runs_
.size(); ++i
) {
949 size_t run
= visual_to_logical_
[i
];
950 current_x
+= runs_
[run
]->width
;
952 *offset
= x
- (current_x
- runs_
[run
]->width
);
959 SelectionModel
RenderTextHarfBuzz::FirstSelectionModelInsideRun(
960 const internal::TextRunHarfBuzz
* run
) {
961 size_t position
= LayoutIndexToTextIndex(run
->range
.start());
962 position
= IndexOfAdjacentGrapheme(position
, CURSOR_FORWARD
);
963 return SelectionModel(position
, CURSOR_BACKWARD
);
966 SelectionModel
RenderTextHarfBuzz::LastSelectionModelInsideRun(
967 const internal::TextRunHarfBuzz
* run
) {
968 size_t position
= LayoutIndexToTextIndex(run
->range
.end());
969 position
= IndexOfAdjacentGrapheme(position
, CURSOR_BACKWARD
);
970 return SelectionModel(position
, CURSOR_FORWARD
);
973 void RenderTextHarfBuzz::ItemizeText() {
974 const base::string16
& text
= GetLayoutText();
975 const bool is_text_rtl
= GetTextDirection() == base::i18n::RIGHT_TO_LEFT
;
976 DCHECK_NE(0U, text
.length());
978 // If ICU fails to itemize the text, we create a run that spans the entire
979 // text. This is needed because leaving the runs set empty causes some clients
980 // to misbehave since they expect non-zero text metrics from a non-empty text.
981 base::i18n::BiDiLineIterator bidi_iterator
;
982 if (!bidi_iterator
.Open(text
, is_text_rtl
, false)) {
983 internal::TextRunHarfBuzz
* run
= new internal::TextRunHarfBuzz
;
984 run
->range
= Range(0, text
.length());
985 runs_
.push_back(run
);
986 visual_to_logical_
= logical_to_visual_
= std::vector
<int32_t>(1, 0);
990 // Temporarily apply composition underlines and selection colors.
991 ApplyCompositionAndSelectionStyles();
993 // Build the list of runs from the script items and ranged styles. Use an
994 // empty color BreakList to avoid breaking runs at color boundaries.
995 BreakList
<SkColor
> empty_colors
;
996 empty_colors
.SetMax(text
.length());
997 internal::StyleIterator
style(empty_colors
, styles());
999 for (size_t run_break
= 0; run_break
< text
.length();) {
1000 internal::TextRunHarfBuzz
* run
= new internal::TextRunHarfBuzz
;
1001 run
->range
.set_start(run_break
);
1002 run
->font_style
= (style
.style(BOLD
) ? Font::BOLD
: 0) |
1003 (style
.style(ITALIC
) ? Font::ITALIC
: 0);
1004 run
->strike
= style
.style(STRIKE
);
1005 run
->diagonal_strike
= style
.style(DIAGONAL_STRIKE
);
1006 run
->underline
= style
.style(UNDERLINE
);
1008 int32 script_item_break
= 0;
1009 bidi_iterator
.GetLogicalRun(run_break
, &script_item_break
, &run
->level
);
1010 // Odd BiDi embedding levels correspond to RTL runs.
1011 run
->is_rtl
= (run
->level
% 2) == 1;
1012 // Find the length and script of this script run.
1013 script_item_break
= ScriptInterval(text
, run_break
,
1014 script_item_break
- run_break
, &run
->script
) + run_break
;
1016 // Find the next break and advance the iterators as needed.
1017 run_break
= std::min(static_cast<size_t>(script_item_break
),
1018 TextIndexToLayoutIndex(style
.GetRange().end()));
1020 // Break runs adjacent to character substrings in certain code blocks.
1021 // This avoids using their fallback fonts for more characters than needed,
1022 // in cases like "\x25B6 Media Title", etc. http://crbug.com/278913
1023 if (run_break
> run
->range
.start())
1024 run_break
= FindUnusualCharacter(text
, run
->range
.start(), run_break
);
1026 DCHECK(IsValidCodePointIndex(text
, run_break
));
1027 style
.UpdatePosition(LayoutIndexToTextIndex(run_break
));
1028 run
->range
.set_end(run_break
);
1030 runs_
.push_back(run
);
1033 // Undo the temporarily applied composition underlines and selection colors.
1034 UndoCompositionAndSelectionStyles();
1036 const size_t num_runs
= runs_
.size();
1037 std::vector
<UBiDiLevel
> levels(num_runs
);
1038 for (size_t i
= 0; i
< num_runs
; ++i
)
1039 levels
[i
] = runs_
[i
]->level
;
1040 visual_to_logical_
.resize(num_runs
);
1041 ubidi_reorderVisual(&levels
[0], num_runs
, &visual_to_logical_
[0]);
1042 logical_to_visual_
.resize(num_runs
);
1043 ubidi_reorderLogical(&levels
[0], num_runs
, &logical_to_visual_
[0]);
1046 void RenderTextHarfBuzz::ShapeRun(internal::TextRunHarfBuzz
* run
) {
1047 const Font
& primary_font
= font_list().GetPrimaryFont();
1048 const std::string primary_font_name
= primary_font
.GetFontName();
1049 run
->font_size
= primary_font
.GetFontSize();
1051 size_t best_font_missing
= std::numeric_limits
<size_t>::max();
1052 std::string best_font
;
1053 std::string current_font
;
1055 // Try shaping with |primary_font|.
1056 if (ShapeRunWithFont(run
, primary_font_name
)) {
1057 current_font
= primary_font_name
;
1058 size_t current_missing
= run
->CountMissingGlyphs();
1059 if (current_missing
== 0)
1061 if (current_missing
< best_font_missing
) {
1062 best_font_missing
= current_missing
;
1063 best_font
= current_font
;
1068 Font uniscribe_font
;
1069 const base::char16
* run_text
= &(GetLayoutText()[run
->range
.start()]);
1070 if (GetUniscribeFallbackFont(primary_font
, run_text
, run
->range
.length(),
1072 ShapeRunWithFont(run
, uniscribe_font
.GetFontName())) {
1073 current_font
= uniscribe_font
.GetFontName();
1074 size_t current_missing
= run
->CountMissingGlyphs();
1075 if (current_missing
== 0)
1077 if (current_missing
< best_font_missing
) {
1078 best_font_missing
= current_missing
;
1079 best_font
= current_font
;
1084 // Try shaping with the fonts in the fallback list except the first, which is
1086 std::vector
<std::string
> fonts
= GetFallbackFontFamilies(primary_font_name
);
1087 for (size_t i
= 1; i
< fonts
.size(); ++i
) {
1088 if (!ShapeRunWithFont(run
, fonts
[i
]))
1090 current_font
= fonts
[i
];
1091 size_t current_missing
= run
->CountMissingGlyphs();
1092 if (current_missing
== 0)
1094 if (current_missing
< best_font_missing
) {
1095 best_font_missing
= current_missing
;
1096 best_font
= current_font
;
1100 if (!best_font
.empty() &&
1101 (best_font
== current_font
|| ShapeRunWithFont(run
, best_font
))) {
1105 run
->glyph_count
= 0;
1109 bool RenderTextHarfBuzz::ShapeRunWithFont(internal::TextRunHarfBuzz
* run
,
1110 const std::string
& font_family
) {
1111 const base::string16
& text
= GetLayoutText();
1112 skia::RefPtr
<SkTypeface
> skia_face
=
1113 internal::CreateSkiaTypeface(font_family
, run
->font_style
);
1114 if (skia_face
== NULL
)
1116 run
->skia_face
= skia_face
;
1117 FontRenderParamsQuery
query(false);
1118 query
.families
.push_back(font_family
);
1119 query
.pixel_size
= run
->font_size
;
1120 query
.style
= run
->font_style
;
1121 run
->render_params
= GetFontRenderParams(query
, NULL
);
1122 hb_font_t
* harfbuzz_font
= CreateHarfBuzzFont(run
->skia_face
.get(),
1123 run
->font_size
, run
->render_params
, background_is_transparent());
1125 // Create a HarfBuzz buffer and add the string to be shaped. The HarfBuzz
1126 // buffer holds our text, run information to be used by the shaping engine,
1127 // and the resulting glyph data.
1128 hb_buffer_t
* buffer
= hb_buffer_create();
1129 hb_buffer_add_utf16(buffer
, reinterpret_cast<const uint16
*>(text
.c_str()),
1130 text
.length(), run
->range
.start(), run
->range
.length());
1131 hb_buffer_set_script(buffer
, ICUScriptToHBScript(run
->script
));
1132 hb_buffer_set_direction(buffer
,
1133 run
->is_rtl
? HB_DIRECTION_RTL
: HB_DIRECTION_LTR
);
1134 // TODO(ckocagil): Should we determine the actual language?
1135 hb_buffer_set_language(buffer
, hb_language_get_default());
1138 hb_shape(harfbuzz_font
, buffer
, NULL
, 0);
1140 // Populate the run fields with the resulting glyph data in the buffer.
1141 unsigned int glyph_count
= 0;
1142 hb_glyph_info_t
* infos
= hb_buffer_get_glyph_infos(buffer
, &glyph_count
);
1143 run
->glyph_count
= glyph_count
;
1144 hb_glyph_position_t
* hb_positions
=
1145 hb_buffer_get_glyph_positions(buffer
, NULL
);
1146 run
->glyphs
.reset(new uint16
[run
->glyph_count
]);
1147 run
->glyph_to_char
.resize(run
->glyph_count
);
1148 run
->positions
.reset(new SkPoint
[run
->glyph_count
]);
1150 for (size_t i
= 0; i
< run
->glyph_count
; ++i
) {
1151 run
->glyphs
[i
] = infos
[i
].codepoint
;
1152 run
->glyph_to_char
[i
] = infos
[i
].cluster
;
1153 const int x_offset
= SkFixedToScalar(hb_positions
[i
].x_offset
);
1154 const int y_offset
= SkFixedToScalar(hb_positions
[i
].y_offset
);
1155 run
->positions
[i
].set(run
->width
+ x_offset
, -y_offset
);
1156 run
->width
+= SkFixedToScalar(hb_positions
[i
].x_advance
);
1157 #if defined(OS_LINUX)
1158 // Match Pango's glyph rounding logic on Linux.
1159 if (!run
->render_params
.subpixel_positioning
)
1160 run
->width
= std::floor(run
->width
+ 0.5f
);
1164 hb_buffer_destroy(buffer
);
1165 hb_font_destroy(harfbuzz_font
);