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 "base/profiler/scoped_tracker.h"
15 #include "third_party/harfbuzz-ng/src/hb.h"
16 #include "third_party/icu/source/common/unicode/ubidi.h"
17 #include "third_party/skia/include/core/SkColor.h"
18 #include "third_party/skia/include/core/SkTypeface.h"
19 #include "ui/gfx/canvas.h"
20 #include "ui/gfx/font_fallback.h"
21 #include "ui/gfx/font_render_params.h"
22 #include "ui/gfx/utf16_indexing.h"
25 #include "ui/gfx/font_fallback_win.h"
28 using gfx::internal::RangeF
;
29 using gfx::internal::RoundRangeF
;
35 // Text length limit. Longer strings are slow and not fully tested.
36 const size_t kMaxTextLength
= 10000;
38 // The maximum number of scripts a Unicode character can belong to. This value
39 // is arbitrarily chosen to be a good limit because it is unlikely for a single
40 // character to belong to more scripts.
41 const size_t kMaxScripts
= 5;
43 // Maps from code points to glyph indices in a font.
44 typedef std::map
<uint32_t, uint16_t> GlyphCache
;
46 // Font data provider for HarfBuzz using Skia. Copied from Blink.
47 // TODO(ckocagil): Eliminate the duplication. http://crbug.com/368375
49 FontData(GlyphCache
* glyph_cache
) : glyph_cache_(glyph_cache
) {}
52 GlyphCache
* glyph_cache_
;
55 hb_position_t
SkiaScalarToHarfBuzzPosition(SkScalar value
) {
56 return SkScalarToFixed(value
);
59 // Deletes the object at the given pointer after casting it to the given type.
60 template<typename Type
>
61 void DeleteByType(void* data
) {
62 Type
* typed_data
= reinterpret_cast<Type
*>(data
);
66 template<typename Type
>
67 void DeleteArrayByType(void* data
) {
68 Type
* typed_data
= reinterpret_cast<Type
*>(data
);
72 // Outputs the |width| and |extents| of the glyph with index |codepoint| in
74 void GetGlyphWidthAndExtents(SkPaint
* paint
,
75 hb_codepoint_t codepoint
,
77 hb_glyph_extents_t
* extents
) {
78 DCHECK_LE(codepoint
, std::numeric_limits
<uint16
>::max());
79 paint
->setTextEncoding(SkPaint::kGlyphID_TextEncoding
);
83 uint16_t glyph
= static_cast<uint16_t>(codepoint
);
85 paint
->getTextWidths(&glyph
, sizeof(glyph
), &sk_width
, &sk_bounds
);
87 *width
= SkiaScalarToHarfBuzzPosition(sk_width
);
89 // Invert y-axis because Skia is y-grows-down but we set up HarfBuzz to be
91 extents
->x_bearing
= SkiaScalarToHarfBuzzPosition(sk_bounds
.fLeft
);
92 extents
->y_bearing
= SkiaScalarToHarfBuzzPosition(-sk_bounds
.fTop
);
93 extents
->width
= SkiaScalarToHarfBuzzPosition(sk_bounds
.width());
94 extents
->height
= SkiaScalarToHarfBuzzPosition(-sk_bounds
.height());
98 // Writes the |glyph| index for the given |unicode| code point. Returns whether
99 // the glyph exists, i.e. it is not a missing glyph.
100 hb_bool_t
GetGlyph(hb_font_t
* font
,
102 hb_codepoint_t unicode
,
103 hb_codepoint_t variation_selector
,
104 hb_codepoint_t
* glyph
,
106 FontData
* font_data
= reinterpret_cast<FontData
*>(data
);
107 GlyphCache
* cache
= font_data
->glyph_cache_
;
109 bool exists
= cache
->count(unicode
) != 0;
111 SkPaint
* paint
= &font_data
->paint_
;
112 paint
->setTextEncoding(SkPaint::kUTF32_TextEncoding
);
113 paint
->textToGlyphs(&unicode
, sizeof(hb_codepoint_t
), &(*cache
)[unicode
]);
115 *glyph
= (*cache
)[unicode
];
119 // Returns the horizontal advance value of the |glyph|.
120 hb_position_t
GetGlyphHorizontalAdvance(hb_font_t
* font
,
122 hb_codepoint_t glyph
,
124 FontData
* font_data
= reinterpret_cast<FontData
*>(data
);
125 hb_position_t advance
= 0;
127 GetGlyphWidthAndExtents(&font_data
->paint_
, glyph
, &advance
, 0);
131 hb_bool_t
GetGlyphHorizontalOrigin(hb_font_t
* font
,
133 hb_codepoint_t glyph
,
137 // Just return true, like the HarfBuzz-FreeType implementation.
141 hb_position_t
GetGlyphKerning(FontData
* font_data
,
142 hb_codepoint_t first_glyph
,
143 hb_codepoint_t second_glyph
) {
144 SkTypeface
* typeface
= font_data
->paint_
.getTypeface();
145 const uint16_t glyphs
[2] = { static_cast<uint16_t>(first_glyph
),
146 static_cast<uint16_t>(second_glyph
) };
147 int32_t kerning_adjustments
[1] = { 0 };
149 if (!typeface
->getKerningPairAdjustments(glyphs
, 2, kerning_adjustments
))
152 SkScalar upm
= SkIntToScalar(typeface
->getUnitsPerEm());
153 SkScalar size
= font_data
->paint_
.getTextSize();
154 return SkiaScalarToHarfBuzzPosition(
155 SkScalarMulDiv(SkIntToScalar(kerning_adjustments
[0]), size
, upm
));
158 hb_position_t
GetGlyphHorizontalKerning(hb_font_t
* font
,
160 hb_codepoint_t left_glyph
,
161 hb_codepoint_t right_glyph
,
163 FontData
* font_data
= reinterpret_cast<FontData
*>(data
);
164 if (font_data
->paint_
.isVerticalText()) {
165 // We don't support cross-stream kerning.
169 return GetGlyphKerning(font_data
, left_glyph
, right_glyph
);
172 hb_position_t
GetGlyphVerticalKerning(hb_font_t
* font
,
174 hb_codepoint_t top_glyph
,
175 hb_codepoint_t bottom_glyph
,
177 FontData
* font_data
= reinterpret_cast<FontData
*>(data
);
178 if (!font_data
->paint_
.isVerticalText()) {
179 // We don't support cross-stream kerning.
183 return GetGlyphKerning(font_data
, top_glyph
, bottom_glyph
);
186 // Writes the |extents| of |glyph|.
187 hb_bool_t
GetGlyphExtents(hb_font_t
* font
,
189 hb_codepoint_t glyph
,
190 hb_glyph_extents_t
* extents
,
192 FontData
* font_data
= reinterpret_cast<FontData
*>(data
);
194 GetGlyphWidthAndExtents(&font_data
->paint_
, glyph
, 0, extents
);
200 FontFuncs() : font_funcs_(hb_font_funcs_create()) {
201 hb_font_funcs_set_glyph_func(font_funcs_
, GetGlyph
, 0, 0);
202 hb_font_funcs_set_glyph_h_advance_func(
203 font_funcs_
, GetGlyphHorizontalAdvance
, 0, 0);
204 hb_font_funcs_set_glyph_h_kerning_func(
205 font_funcs_
, GetGlyphHorizontalKerning
, 0, 0);
206 hb_font_funcs_set_glyph_h_origin_func(
207 font_funcs_
, GetGlyphHorizontalOrigin
, 0, 0);
208 hb_font_funcs_set_glyph_v_kerning_func(
209 font_funcs_
, GetGlyphVerticalKerning
, 0, 0);
210 hb_font_funcs_set_glyph_extents_func(
211 font_funcs_
, GetGlyphExtents
, 0, 0);
212 hb_font_funcs_make_immutable(font_funcs_
);
216 hb_font_funcs_destroy(font_funcs_
);
219 hb_font_funcs_t
* get() { return font_funcs_
; }
222 hb_font_funcs_t
* font_funcs_
;
224 DISALLOW_COPY_AND_ASSIGN(FontFuncs
);
227 base::LazyInstance
<FontFuncs
>::Leaky g_font_funcs
= LAZY_INSTANCE_INITIALIZER
;
229 // Returns the raw data of the font table |tag|.
230 hb_blob_t
* GetFontTable(hb_face_t
* face
, hb_tag_t tag
, void* user_data
) {
231 SkTypeface
* typeface
= reinterpret_cast<SkTypeface
*>(user_data
);
233 const size_t table_size
= typeface
->getTableSize(tag
);
237 scoped_ptr
<char[]> buffer(new char[table_size
]);
240 size_t actual_size
= typeface
->getTableData(tag
, 0, table_size
, buffer
.get());
241 if (table_size
!= actual_size
)
244 char* buffer_raw
= buffer
.release();
245 return hb_blob_create(buffer_raw
, table_size
, HB_MEMORY_MODE_WRITABLE
,
246 buffer_raw
, DeleteArrayByType
<char>);
249 void UnrefSkTypeface(void* data
) {
250 SkTypeface
* skia_face
= reinterpret_cast<SkTypeface
*>(data
);
251 SkSafeUnref(skia_face
);
254 // Wrapper class for a HarfBuzz face created from a given Skia face.
257 HarfBuzzFace() : face_(NULL
) {}
261 hb_face_destroy(face_
);
264 void Init(SkTypeface
* skia_face
) {
265 SkSafeRef(skia_face
);
266 face_
= hb_face_create_for_tables(GetFontTable
, skia_face
, UnrefSkTypeface
);
278 // Creates a HarfBuzz font from the given Skia face and text size.
279 hb_font_t
* CreateHarfBuzzFont(SkTypeface
* skia_face
,
281 const FontRenderParams
& params
,
282 bool background_is_transparent
) {
283 typedef std::pair
<HarfBuzzFace
, GlyphCache
> FaceCache
;
285 // TODO(ckocagil): This shouldn't grow indefinitely. Maybe use base::MRUCache?
286 static std::map
<SkFontID
, FaceCache
> face_caches
;
288 FaceCache
* face_cache
= &face_caches
[skia_face
->uniqueID()];
289 if (face_cache
->first
.get() == NULL
)
290 face_cache
->first
.Init(skia_face
);
292 hb_font_t
* harfbuzz_font
= hb_font_create(face_cache
->first
.get());
293 const int scale
= SkScalarToFixed(text_size
);
294 hb_font_set_scale(harfbuzz_font
, scale
, scale
);
295 FontData
* hb_font_data
= new FontData(&face_cache
->second
);
296 hb_font_data
->paint_
.setTypeface(skia_face
);
297 hb_font_data
->paint_
.setTextSize(text_size
);
298 // TODO(ckocagil): Do we need to update these params later?
299 internal::ApplyRenderParams(params
, background_is_transparent
,
300 &hb_font_data
->paint_
);
301 hb_font_set_funcs(harfbuzz_font
, g_font_funcs
.Get().get(), hb_font_data
,
302 DeleteByType
<FontData
>);
303 hb_font_make_immutable(harfbuzz_font
);
304 return harfbuzz_font
;
307 // Returns true if characters of |block_code| may trigger font fallback.
308 bool IsUnusualBlockCode(UBlockCode block_code
) {
309 return block_code
== UBLOCK_GEOMETRIC_SHAPES
||
310 block_code
== UBLOCK_MISCELLANEOUS_SYMBOLS
;
313 bool IsBracket(UChar32 character
) {
314 static const char kBrackets
[] = { '(', ')', '{', '}', '<', '>', };
315 static const char* kBracketsEnd
= kBrackets
+ arraysize(kBrackets
);
316 return std::find(kBrackets
, kBracketsEnd
, character
) != kBracketsEnd
;
319 // Returns the boundary between a special and a regular character. Special
320 // characters are brackets or characters that satisfy |IsUnusualBlockCode|.
321 size_t FindRunBreakingCharacter(const base::string16
& text
,
324 const int32 run_length
= static_cast<int32
>(run_break
- run_start
);
325 base::i18n::UTF16CharIterator
iter(text
.c_str() + run_start
, run_length
);
326 const UChar32 first_char
= iter
.get();
327 const UBlockCode first_block
= ublock_getCode(first_char
);
328 const bool first_block_unusual
= IsUnusualBlockCode(first_block
);
329 const bool first_bracket
= IsBracket(first_char
);
331 while (iter
.Advance() && iter
.array_pos() < run_length
) {
332 const UChar32 current_char
= iter
.get();
333 const UBlockCode current_block
= ublock_getCode(current_char
);
334 const bool block_break
= current_block
!= first_block
&&
335 (first_block_unusual
|| IsUnusualBlockCode(current_block
));
336 if (block_break
|| first_bracket
!= IsBracket(current_char
))
337 return run_start
+ iter
.array_pos();
342 // If the given scripts match, returns the one that isn't USCRIPT_COMMON or
343 // USCRIPT_INHERITED, i.e. the more specific one. Otherwise returns
344 // USCRIPT_INVALID_CODE.
345 UScriptCode
ScriptIntersect(UScriptCode first
, UScriptCode second
) {
346 if (first
== second
||
347 (second
> USCRIPT_INVALID_CODE
&& second
<= USCRIPT_INHERITED
)) {
350 if (first
> USCRIPT_INVALID_CODE
&& first
<= USCRIPT_INHERITED
)
352 return USCRIPT_INVALID_CODE
;
355 // Writes the script and the script extensions of the character with the
356 // Unicode |codepoint|. Returns the number of written scripts.
357 int GetScriptExtensions(UChar32 codepoint
, UScriptCode
* scripts
) {
358 UErrorCode icu_error
= U_ZERO_ERROR
;
359 // ICU documentation incorrectly states that the result of
360 // |uscript_getScriptExtensions| will contain the regular script property.
361 // Write the character's script property to the first element.
362 scripts
[0] = uscript_getScript(codepoint
, &icu_error
);
363 if (U_FAILURE(icu_error
))
365 // Fill the rest of |scripts| with the extensions.
366 int count
= uscript_getScriptExtensions(codepoint
, scripts
+ 1,
367 kMaxScripts
- 1, &icu_error
);
368 if (U_FAILURE(icu_error
))
373 // Intersects the script extensions set of |codepoint| with |result| and writes
374 // to |result|, reading and updating |result_size|.
375 void ScriptSetIntersect(UChar32 codepoint
,
377 size_t* result_size
) {
378 UScriptCode scripts
[kMaxScripts
] = { USCRIPT_INVALID_CODE
};
379 int count
= GetScriptExtensions(codepoint
, scripts
);
383 for (size_t i
= 0; i
< *result_size
; ++i
) {
384 for (int j
= 0; j
< count
; ++j
) {
385 UScriptCode intersection
= ScriptIntersect(result
[i
], scripts
[j
]);
386 if (intersection
!= USCRIPT_INVALID_CODE
) {
387 result
[out_size
++] = intersection
;
393 *result_size
= out_size
;
396 // Find the longest sequence of characters from 0 and up to |length| that
397 // have at least one common UScriptCode value. Writes the common script value to
398 // |script| and returns the length of the sequence. Takes the characters' script
399 // extensions into account. http://www.unicode.org/reports/tr24/#ScriptX
401 // Consider 3 characters with the script values {Kana}, {Hira, Kana}, {Kana}.
402 // Without script extensions only the first script in each set would be taken
403 // into account, resulting in 3 runs where 1 would be enough.
404 // TODO(ckocagil): Write a unit test for the case above.
405 int ScriptInterval(const base::string16
& text
,
408 UScriptCode
* script
) {
409 DCHECK_GT(length
, 0U);
411 UScriptCode scripts
[kMaxScripts
] = { USCRIPT_INVALID_CODE
};
413 base::i18n::UTF16CharIterator
char_iterator(text
.c_str() + start
, length
);
414 size_t scripts_size
= GetScriptExtensions(char_iterator
.get(), scripts
);
415 *script
= scripts
[0];
417 while (char_iterator
.Advance()) {
418 ScriptSetIntersect(char_iterator
.get(), scripts
, &scripts_size
);
419 if (scripts_size
== 0U)
420 return char_iterator
.array_pos();
421 *script
= scripts
[0];
427 // A port of hb_icu_script_to_script because harfbuzz on CrOS is built without
428 // hb-icu. See http://crbug.com/356929
429 inline hb_script_t
ICUScriptToHBScript(UScriptCode script
) {
430 if (script
== USCRIPT_INVALID_CODE
)
431 return HB_SCRIPT_INVALID
;
432 return hb_script_from_string(uscript_getShortName(script
), -1);
435 // Helper template function for |TextRunHarfBuzz::GetClusterAt()|. |Iterator|
436 // can be a forward or reverse iterator type depending on the text direction.
437 template <class Iterator
>
438 void GetClusterAtImpl(size_t pos
,
440 Iterator elements_begin
,
441 Iterator elements_end
,
445 Iterator element
= std::upper_bound(elements_begin
, elements_end
, pos
);
446 chars
->set_end(element
== elements_end
? range
.end() : *element
);
447 glyphs
->set_end(reversed
? elements_end
- element
: element
- elements_begin
);
449 DCHECK(element
!= elements_begin
);
450 while (--element
!= elements_begin
&& *element
== *(element
- 1));
451 chars
->set_start(*element
);
453 reversed
? elements_end
- element
: element
- elements_begin
);
455 *glyphs
= Range(glyphs
->end(), glyphs
->start());
457 DCHECK(!chars
->is_reversed());
458 DCHECK(!chars
->is_empty());
459 DCHECK(!glyphs
->is_reversed());
460 DCHECK(!glyphs
->is_empty());
467 Range
RoundRangeF(const RangeF
& range_f
) {
468 return Range(std::floor(range_f
.first
+ 0.5f
),
469 std::floor(range_f
.second
+ 0.5f
));
472 TextRunHarfBuzz::TextRunHarfBuzz()
474 preceding_run_widths(0.0f
),
477 script(USCRIPT_INVALID_CODE
),
478 glyph_count(static_cast<size_t>(-1)),
482 diagonal_strike(false),
485 TextRunHarfBuzz::~TextRunHarfBuzz() {}
487 void TextRunHarfBuzz::GetClusterAt(size_t pos
,
489 Range
* glyphs
) const {
490 DCHECK(range
.Contains(Range(pos
, pos
+ 1)));
494 if (glyph_count
== 0) {
501 GetClusterAtImpl(pos
, range
, glyph_to_char
.rbegin(), glyph_to_char
.rend(),
502 true, chars
, glyphs
);
506 GetClusterAtImpl(pos
, range
, glyph_to_char
.begin(), glyph_to_char
.end(),
507 false, chars
, glyphs
);
510 Range
TextRunHarfBuzz::CharRangeToGlyphRange(const Range
& char_range
) const {
511 DCHECK(range
.Contains(char_range
));
512 DCHECK(!char_range
.is_reversed());
513 DCHECK(!char_range
.is_empty());
518 GetClusterAt(char_range
.start(), &temp_range
, &start_glyphs
);
519 GetClusterAt(char_range
.end() - 1, &temp_range
, &end_glyphs
);
521 return is_rtl
? Range(end_glyphs
.start(), start_glyphs
.end()) :
522 Range(start_glyphs
.start(), end_glyphs
.end());
525 size_t TextRunHarfBuzz::CountMissingGlyphs() const {
526 static const int kMissingGlyphId
= 0;
528 for (size_t i
= 0; i
< glyph_count
; ++i
)
529 missing
+= (glyphs
[i
] == kMissingGlyphId
) ? 1 : 0;
533 RangeF
TextRunHarfBuzz::GetGraphemeBounds(
534 base::i18n::BreakIterator
* grapheme_iterator
,
536 DCHECK_LT(text_index
, range
.end());
537 if (glyph_count
== 0)
538 return RangeF(preceding_run_widths
, preceding_run_widths
+ width
);
542 GetClusterAt(text_index
, &chars
, &glyphs
);
543 const float cluster_begin_x
= positions
[glyphs
.start()].x();
544 const float cluster_end_x
= glyphs
.end() < glyph_count
?
545 positions
[glyphs
.end()].x() : SkFloatToScalar(width
);
547 // A cluster consists of a number of code points and corresponds to a number
548 // of glyphs that should be drawn together. A cluster can contain multiple
549 // graphemes. In order to place the cursor at a grapheme boundary inside the
550 // cluster, we simply divide the cluster width by the number of graphemes.
551 if (chars
.length() > 1 && grapheme_iterator
) {
554 for (size_t i
= chars
.start(); i
< chars
.end(); ++i
) {
555 if (grapheme_iterator
->IsGraphemeBoundary(i
)) {
564 before
= total
- before
- 1;
565 DCHECK_GE(before
, 0);
566 DCHECK_LT(before
, total
);
567 const int cluster_width
= cluster_end_x
- cluster_begin_x
;
568 const int grapheme_begin_x
= cluster_begin_x
+ static_cast<int>(0.5f
+
569 cluster_width
* before
/ static_cast<float>(total
));
570 const int grapheme_end_x
= cluster_begin_x
+ static_cast<int>(0.5f
+
571 cluster_width
* (before
+ 1) / static_cast<float>(total
));
572 return RangeF(preceding_run_widths
+ grapheme_begin_x
,
573 preceding_run_widths
+ grapheme_end_x
);
577 return RangeF(preceding_run_widths
+ cluster_begin_x
,
578 preceding_run_widths
+ cluster_end_x
);
581 } // namespace internal
583 RenderTextHarfBuzz::RenderTextHarfBuzz()
585 needs_layout_(false) {
586 set_truncate_length(kMaxTextLength
);
589 RenderTextHarfBuzz::~RenderTextHarfBuzz() {}
591 scoped_ptr
<RenderText
> RenderTextHarfBuzz::CreateInstanceOfSameType() const {
592 return scoped_ptr
<RenderTextHarfBuzz
>(new RenderTextHarfBuzz
);
595 Size
RenderTextHarfBuzz::GetStringSize() {
596 const SizeF size_f
= GetStringSizeF();
597 return Size(std::ceil(size_f
.width()), size_f
.height());
600 SizeF
RenderTextHarfBuzz::GetStringSizeF() {
602 return lines()[0].size
;
605 SelectionModel
RenderTextHarfBuzz::FindCursorPosition(const Point
& point
) {
608 int x
= ToTextPoint(point
).x();
610 size_t run_index
= GetRunContainingXCoord(x
, &offset
);
611 if (run_index
>= runs_
.size())
612 return EdgeSelectionModel((x
< 0) ? CURSOR_LEFT
: CURSOR_RIGHT
);
613 const internal::TextRunHarfBuzz
& run
= *runs_
[run_index
];
615 for (size_t i
= 0; i
< run
.glyph_count
; ++i
) {
617 i
+ 1 == run
.glyph_count
? run
.width
: run
.positions
[i
+ 1].x();
618 const SkScalar middle
= (end
+ run
.positions
[i
].x()) / 2;
620 if (offset
< middle
) {
621 return SelectionModel(LayoutIndexToTextIndex(
622 run
.glyph_to_char
[i
] + (run
.is_rtl
? 1 : 0)),
623 (run
.is_rtl
? CURSOR_BACKWARD
: CURSOR_FORWARD
));
626 return SelectionModel(LayoutIndexToTextIndex(
627 run
.glyph_to_char
[i
] + (run
.is_rtl
? 0 : 1)),
628 (run
.is_rtl
? CURSOR_FORWARD
: CURSOR_BACKWARD
));
631 return EdgeSelectionModel(CURSOR_RIGHT
);
634 std::vector
<RenderText::FontSpan
> RenderTextHarfBuzz::GetFontSpansForTesting() {
637 std::vector
<RenderText::FontSpan
> spans
;
638 for (size_t i
= 0; i
< runs_
.size(); ++i
) {
639 SkString family_name
;
640 runs_
[i
]->skia_face
->getFamilyName(&family_name
);
641 Font
font(family_name
.c_str(), runs_
[i
]->font_size
);
642 spans
.push_back(RenderText::FontSpan(font
,
643 Range(LayoutIndexToTextIndex(runs_
[i
]->range
.start()),
644 LayoutIndexToTextIndex(runs_
[i
]->range
.end()))));
650 Range
RenderTextHarfBuzz::GetGlyphBounds(size_t index
) {
652 const size_t run_index
=
653 GetRunContainingCaret(SelectionModel(index
, CURSOR_FORWARD
));
654 // Return edge bounds if the index is invalid or beyond the layout text size.
655 if (run_index
>= runs_
.size())
656 return Range(GetStringSize().width());
657 const size_t layout_index
= TextIndexToLayoutIndex(index
);
658 internal::TextRunHarfBuzz
* run
= runs_
[run_index
];
660 run
->GetGraphemeBounds(grapheme_iterator_
.get(), layout_index
);
661 // If cursor is enabled, extend the last glyph up to the rightmost cursor
662 // position since clients expect them to be contiguous.
663 if (cursor_enabled() && run_index
== runs_
.size() - 1 &&
664 index
== (run
->is_rtl
? run
->range
.start() : run
->range
.end() - 1))
665 bounds
.second
= std::ceil(bounds
.second
);
666 return RoundRangeF(run
->is_rtl
?
667 RangeF(bounds
.second
, bounds
.first
) : bounds
);
670 int RenderTextHarfBuzz::GetLayoutTextBaseline() {
672 return lines()[0].baseline
;
675 SelectionModel
RenderTextHarfBuzz::AdjacentCharSelectionModel(
676 const SelectionModel
& selection
,
677 VisualCursorDirection direction
) {
678 DCHECK(!needs_layout_
);
679 internal::TextRunHarfBuzz
* run
;
680 size_t run_index
= GetRunContainingCaret(selection
);
681 if (run_index
>= runs_
.size()) {
682 // The cursor is not in any run: we're at the visual and logical edge.
683 SelectionModel edge
= EdgeSelectionModel(direction
);
684 if (edge
.caret_pos() == selection
.caret_pos())
686 int visual_index
= (direction
== CURSOR_RIGHT
) ? 0 : runs_
.size() - 1;
687 run
= runs_
[visual_to_logical_
[visual_index
]];
689 // If the cursor is moving within the current run, just move it by one
690 // grapheme in the appropriate direction.
691 run
= runs_
[run_index
];
692 size_t caret
= selection
.caret_pos();
693 bool forward_motion
= run
->is_rtl
== (direction
== CURSOR_LEFT
);
694 if (forward_motion
) {
695 if (caret
< LayoutIndexToTextIndex(run
->range
.end())) {
696 caret
= IndexOfAdjacentGrapheme(caret
, CURSOR_FORWARD
);
697 return SelectionModel(caret
, CURSOR_BACKWARD
);
700 if (caret
> LayoutIndexToTextIndex(run
->range
.start())) {
701 caret
= IndexOfAdjacentGrapheme(caret
, CURSOR_BACKWARD
);
702 return SelectionModel(caret
, CURSOR_FORWARD
);
705 // The cursor is at the edge of a run; move to the visually adjacent run.
706 int visual_index
= logical_to_visual_
[run_index
];
707 visual_index
+= (direction
== CURSOR_LEFT
) ? -1 : 1;
708 if (visual_index
< 0 || visual_index
>= static_cast<int>(runs_
.size()))
709 return EdgeSelectionModel(direction
);
710 run
= runs_
[visual_to_logical_
[visual_index
]];
712 bool forward_motion
= run
->is_rtl
== (direction
== CURSOR_LEFT
);
713 return forward_motion
? FirstSelectionModelInsideRun(run
) :
714 LastSelectionModelInsideRun(run
);
717 SelectionModel
RenderTextHarfBuzz::AdjacentWordSelectionModel(
718 const SelectionModel
& selection
,
719 VisualCursorDirection direction
) {
721 return EdgeSelectionModel(direction
);
723 base::i18n::BreakIterator
iter(text(), base::i18n::BreakIterator::BREAK_WORD
);
724 bool success
= iter
.Init();
729 // Match OS specific word break behavior.
732 if (direction
== CURSOR_RIGHT
) {
733 pos
= std::min(selection
.caret_pos() + 1, text().length());
734 while (iter
.Advance()) {
736 if (iter
.IsWord() && pos
> selection
.caret_pos())
739 } else { // direction == CURSOR_LEFT
740 // Notes: We always iterate words from the beginning.
741 // This is probably fast enough for our usage, but we may
742 // want to modify WordIterator so that it can start from the
743 // middle of string and advance backwards.
744 pos
= std::max
<int>(selection
.caret_pos() - 1, 0);
745 while (iter
.Advance()) {
747 size_t begin
= iter
.pos() - iter
.GetString().length();
748 if (begin
== selection
.caret_pos()) {
749 // The cursor is at the beginning of a word.
750 // Move to previous word.
752 } else if (iter
.pos() >= selection
.caret_pos()) {
753 // The cursor is in the middle or at the end of a word.
754 // Move to the top of current word.
758 pos
= iter
.pos() - iter
.GetString().length();
762 return SelectionModel(pos
, CURSOR_FORWARD
);
764 SelectionModel
cur(selection
);
766 cur
= AdjacentCharSelectionModel(cur
, direction
);
767 size_t run
= GetRunContainingCaret(cur
);
768 if (run
== runs_
.size())
770 const bool is_forward
= runs_
[run
]->is_rtl
== (direction
== CURSOR_LEFT
);
771 size_t cursor
= cur
.caret_pos();
772 if (is_forward
? iter
.IsEndOfWord(cursor
) : iter
.IsStartOfWord(cursor
))
779 std::vector
<Rect
> RenderTextHarfBuzz::GetSubstringBounds(const Range
& range
) {
780 DCHECK(!needs_layout_
);
781 DCHECK(Range(0, text().length()).Contains(range
));
782 Range
layout_range(TextIndexToLayoutIndex(range
.start()),
783 TextIndexToLayoutIndex(range
.end()));
784 DCHECK(Range(0, GetLayoutText().length()).Contains(layout_range
));
786 std::vector
<Rect
> rects
;
787 if (layout_range
.is_empty())
789 std::vector
<Range
> bounds
;
791 // Add a Range for each run/selection intersection.
792 for (size_t i
= 0; i
< runs_
.size(); ++i
) {
793 internal::TextRunHarfBuzz
* run
= runs_
[visual_to_logical_
[i
]];
794 Range intersection
= run
->range
.Intersect(layout_range
);
795 if (!intersection
.IsValid())
797 DCHECK(!intersection
.is_reversed());
798 const Range leftmost_character_x
= RoundRangeF(run
->GetGraphemeBounds(
799 grapheme_iterator_
.get(),
800 run
->is_rtl
? intersection
.end() - 1 : intersection
.start()));
801 const Range rightmost_character_x
= RoundRangeF(run
->GetGraphemeBounds(
802 grapheme_iterator_
.get(),
803 run
->is_rtl
? intersection
.start() : intersection
.end() - 1));
804 Range
range_x(leftmost_character_x
.start(), rightmost_character_x
.end());
805 DCHECK(!range_x
.is_reversed());
806 if (range_x
.is_empty())
809 // Union this with the last range if they're adjacent.
810 DCHECK(bounds
.empty() || bounds
.back().GetMax() <= range_x
.GetMin());
811 if (!bounds
.empty() && bounds
.back().GetMax() == range_x
.GetMin()) {
812 range_x
= Range(bounds
.back().GetMin(), range_x
.GetMax());
815 bounds
.push_back(range_x
);
817 for (size_t i
= 0; i
< bounds
.size(); ++i
) {
818 std::vector
<Rect
> current_rects
= TextBoundsToViewBounds(bounds
[i
]);
819 rects
.insert(rects
.end(), current_rects
.begin(), current_rects
.end());
824 size_t RenderTextHarfBuzz::TextIndexToLayoutIndex(size_t index
) const {
825 DCHECK_LE(index
, text().length());
826 ptrdiff_t i
= obscured() ? UTF16IndexToOffset(text(), 0, index
) : index
;
828 // Clamp layout indices to the length of the text actually used for layout.
829 return std::min
<size_t>(GetLayoutText().length(), i
);
832 size_t RenderTextHarfBuzz::LayoutIndexToTextIndex(size_t index
) const {
836 DCHECK_LE(index
, GetLayoutText().length());
837 const size_t text_index
= UTF16OffsetToIndex(text(), 0, index
);
838 DCHECK_LE(text_index
, text().length());
842 bool RenderTextHarfBuzz::IsValidCursorIndex(size_t index
) {
843 if (index
== 0 || index
== text().length())
845 if (!IsValidLogicalIndex(index
))
848 return !grapheme_iterator_
|| grapheme_iterator_
->IsGraphemeBoundary(index
);
851 void RenderTextHarfBuzz::ResetLayout() {
852 needs_layout_
= true;
855 void RenderTextHarfBuzz::EnsureLayout() {
856 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed.
857 tracked_objects::ScopedTracker
tracking_profile(
858 FROM_HERE_WITH_EXPLICIT_FUNCTION(
859 "431326 RenderTextHarfBuzz::EnsureLayout"));
863 grapheme_iterator_
.reset();
865 if (!GetLayoutText().empty()) {
866 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is
868 tracked_objects::ScopedTracker
tracking_profile1(
869 FROM_HERE_WITH_EXPLICIT_FUNCTION(
870 "431326 RenderTextHarfBuzz::EnsureLayout1"));
872 grapheme_iterator_
.reset(new base::i18n::BreakIterator(GetLayoutText(),
873 base::i18n::BreakIterator::BREAK_CHARACTER
));
874 if (!grapheme_iterator_
->Init())
875 grapheme_iterator_
.reset();
877 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is
879 tracked_objects::ScopedTracker
tracking_profile11(
880 FROM_HERE_WITH_EXPLICIT_FUNCTION(
881 "431326 RenderTextHarfBuzz::EnsureLayout11"));
885 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is
887 tracked_objects::ScopedTracker
tracking_profile12(
888 FROM_HERE_WITH_EXPLICIT_FUNCTION(
889 "431326 RenderTextHarfBuzz::EnsureLayout12"));
891 for (size_t i
= 0; i
< runs_
.size(); ++i
)
894 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is
896 tracked_objects::ScopedTracker
tracking_profile13(
897 FROM_HERE_WITH_EXPLICIT_FUNCTION(
898 "431326 RenderTextHarfBuzz::EnsureLayout13"));
900 // Precalculate run width information.
901 float preceding_run_widths
= 0.0f
;
902 for (size_t i
= 0; i
< runs_
.size(); ++i
) {
903 internal::TextRunHarfBuzz
* run
= runs_
[visual_to_logical_
[i
]];
904 run
->preceding_run_widths
= preceding_run_widths
;
905 preceding_run_widths
+= run
->width
;
909 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is
911 tracked_objects::ScopedTracker
tracking_profile14(
912 FROM_HERE_WITH_EXPLICIT_FUNCTION(
913 "431326 RenderTextHarfBuzz::EnsureLayout14"));
915 needs_layout_
= false;
916 std::vector
<internal::Line
> empty_lines
;
917 set_lines(&empty_lines
);
920 if (lines().empty()) {
921 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed.
922 tracked_objects::ScopedTracker
tracking_profile2(
923 FROM_HERE_WITH_EXPLICIT_FUNCTION(
924 "431326 RenderTextHarfBuzz::EnsureLayout2"));
926 std::vector
<internal::Line
> lines
;
927 lines
.push_back(internal::Line());
928 lines
[0].baseline
= font_list().GetBaseline();
929 lines
[0].size
.set_height(font_list().GetHeight());
934 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed.
935 tracked_objects::ScopedTracker
tracking_profile3(
936 FROM_HERE_WITH_EXPLICIT_FUNCTION(
937 "431326 RenderTextHarfBuzz::EnsureLayout3"));
939 for (size_t i
= 0; i
< runs_
.size(); ++i
) {
940 const internal::TextRunHarfBuzz
& run
= *runs_
[visual_to_logical_
[i
]];
941 internal::LineSegment segment
;
942 segment
.x_range
= Range(current_x
, current_x
+ run
.width
);
943 segment
.char_range
= run
.range
;
945 lines
[0].segments
.push_back(segment
);
947 paint
.setTypeface(run
.skia_face
.get());
948 paint
.setTextSize(SkIntToScalar(run
.font_size
));
949 paint
.setAntiAlias(run
.render_params
.antialiasing
);
950 SkPaint::FontMetrics metrics
;
951 paint
.getFontMetrics(&metrics
);
953 lines
[0].size
.set_width(lines
[0].size
.width() + run
.width
);
954 lines
[0].size
.set_height(std::max(lines
[0].size
.height(),
955 metrics
.fDescent
- metrics
.fAscent
));
956 lines
[0].baseline
= std::max(lines
[0].baseline
,
957 SkScalarRoundToInt(-metrics
.fAscent
));
964 void RenderTextHarfBuzz::DrawVisualText(Canvas
* canvas
) {
965 DCHECK(!needs_layout_
);
966 internal::SkiaTextRenderer
renderer(canvas
);
967 ApplyFadeEffects(&renderer
);
968 ApplyTextShadows(&renderer
);
969 ApplyCompositionAndSelectionStyles();
971 const Vector2d line_offset
= GetLineOffset(0);
972 for (size_t i
= 0; i
< runs_
.size(); ++i
) {
973 const internal::TextRunHarfBuzz
& run
= *runs_
[visual_to_logical_
[i
]];
974 renderer
.SetTypeface(run
.skia_face
.get());
975 renderer
.SetTextSize(SkIntToScalar(run
.font_size
));
976 renderer
.SetFontRenderParams(run
.render_params
,
977 background_is_transparent());
979 Vector2d origin
= line_offset
+ Vector2d(0, lines()[0].baseline
);
980 scoped_ptr
<SkPoint
[]> positions(new SkPoint
[run
.glyph_count
]);
981 for (size_t j
= 0; j
< run
.glyph_count
; ++j
) {
982 positions
[j
] = run
.positions
[j
];
983 positions
[j
].offset(SkIntToScalar(origin
.x()) + run
.preceding_run_widths
,
984 SkIntToScalar(origin
.y()));
987 for (BreakList
<SkColor
>::const_iterator it
=
988 colors().GetBreak(run
.range
.start());
989 it
!= colors().breaks().end() && it
->first
< run
.range
.end();
991 const Range intersection
= colors().GetRange(it
).Intersect(run
.range
);
992 const Range colored_glyphs
= run
.CharRangeToGlyphRange(intersection
);
993 // The range may be empty if a portion of a multi-character grapheme is
994 // selected, yielding two colors for a single glyph. For now, this just
995 // paints the glyph with a single style, but it should paint it twice,
996 // clipped according to selection bounds. See http://crbug.com/366786
997 if (colored_glyphs
.is_empty())
1000 renderer
.SetForegroundColor(it
->second
);
1001 renderer
.DrawPosText(&positions
[colored_glyphs
.start()],
1002 &run
.glyphs
[colored_glyphs
.start()],
1003 colored_glyphs
.length());
1004 int start_x
= SkScalarRoundToInt(positions
[colored_glyphs
.start()].x());
1005 int end_x
= SkScalarRoundToInt((colored_glyphs
.end() == run
.glyph_count
) ?
1006 (run
.width
+ run
.preceding_run_widths
+ SkIntToScalar(origin
.x())) :
1007 positions
[colored_glyphs
.end()].x());
1008 renderer
.DrawDecorations(start_x
, origin
.y(), end_x
- start_x
,
1009 run
.underline
, run
.strike
, run
.diagonal_strike
);
1013 renderer
.EndDiagonalStrike();
1015 UndoCompositionAndSelectionStyles();
1018 size_t RenderTextHarfBuzz::GetRunContainingCaret(
1019 const SelectionModel
& caret
) const {
1020 DCHECK(!needs_layout_
);
1021 size_t layout_position
= TextIndexToLayoutIndex(caret
.caret_pos());
1022 LogicalCursorDirection affinity
= caret
.caret_affinity();
1023 for (size_t run
= 0; run
< runs_
.size(); ++run
) {
1024 if (RangeContainsCaret(runs_
[run
]->range
, layout_position
, affinity
))
1027 return runs_
.size();
1030 size_t RenderTextHarfBuzz::GetRunContainingXCoord(float x
,
1031 float* offset
) const {
1032 DCHECK(!needs_layout_
);
1034 return runs_
.size();
1035 // Find the text run containing the argument point (assumed already offset).
1036 float current_x
= 0;
1037 for (size_t i
= 0; i
< runs_
.size(); ++i
) {
1038 size_t run
= visual_to_logical_
[i
];
1039 current_x
+= runs_
[run
]->width
;
1040 if (x
< current_x
) {
1041 *offset
= x
- (current_x
- runs_
[run
]->width
);
1045 return runs_
.size();
1048 SelectionModel
RenderTextHarfBuzz::FirstSelectionModelInsideRun(
1049 const internal::TextRunHarfBuzz
* run
) {
1050 size_t position
= LayoutIndexToTextIndex(run
->range
.start());
1051 position
= IndexOfAdjacentGrapheme(position
, CURSOR_FORWARD
);
1052 return SelectionModel(position
, CURSOR_BACKWARD
);
1055 SelectionModel
RenderTextHarfBuzz::LastSelectionModelInsideRun(
1056 const internal::TextRunHarfBuzz
* run
) {
1057 size_t position
= LayoutIndexToTextIndex(run
->range
.end());
1058 position
= IndexOfAdjacentGrapheme(position
, CURSOR_BACKWARD
);
1059 return SelectionModel(position
, CURSOR_FORWARD
);
1062 void RenderTextHarfBuzz::ItemizeText() {
1063 const base::string16
& text
= GetLayoutText();
1064 const bool is_text_rtl
= GetTextDirection() == base::i18n::RIGHT_TO_LEFT
;
1065 DCHECK_NE(0U, text
.length());
1067 // If ICU fails to itemize the text, we create a run that spans the entire
1068 // text. This is needed because leaving the runs set empty causes some clients
1069 // to misbehave since they expect non-zero text metrics from a non-empty text.
1070 base::i18n::BiDiLineIterator bidi_iterator
;
1071 if (!bidi_iterator
.Open(text
, is_text_rtl
, false)) {
1072 internal::TextRunHarfBuzz
* run
= new internal::TextRunHarfBuzz
;
1073 run
->range
= Range(0, text
.length());
1074 runs_
.push_back(run
);
1075 visual_to_logical_
= logical_to_visual_
= std::vector
<int32_t>(1, 0);
1079 // Temporarily apply composition underlines and selection colors.
1080 ApplyCompositionAndSelectionStyles();
1082 // Build the list of runs from the script items and ranged styles. Use an
1083 // empty color BreakList to avoid breaking runs at color boundaries.
1084 BreakList
<SkColor
> empty_colors
;
1085 empty_colors
.SetMax(text
.length());
1086 internal::StyleIterator
style(empty_colors
, styles());
1088 for (size_t run_break
= 0; run_break
< text
.length();) {
1089 internal::TextRunHarfBuzz
* run
= new internal::TextRunHarfBuzz
;
1090 run
->range
.set_start(run_break
);
1091 run
->font_style
= (style
.style(BOLD
) ? Font::BOLD
: 0) |
1092 (style
.style(ITALIC
) ? Font::ITALIC
: 0);
1093 run
->strike
= style
.style(STRIKE
);
1094 run
->diagonal_strike
= style
.style(DIAGONAL_STRIKE
);
1095 run
->underline
= style
.style(UNDERLINE
);
1097 int32 script_item_break
= 0;
1098 bidi_iterator
.GetLogicalRun(run_break
, &script_item_break
, &run
->level
);
1099 // Odd BiDi embedding levels correspond to RTL runs.
1100 run
->is_rtl
= (run
->level
% 2) == 1;
1101 // Find the length and script of this script run.
1102 script_item_break
= ScriptInterval(text
, run_break
,
1103 script_item_break
- run_break
, &run
->script
) + run_break
;
1105 // Find the next break and advance the iterators as needed.
1106 run_break
= std::min(static_cast<size_t>(script_item_break
),
1107 TextIndexToLayoutIndex(style
.GetRange().end()));
1109 // Break runs at certain characters that need to be rendered separately to
1110 // prevent either an unusual character from forcing a fallback font on the
1111 // entire run, or brackets from being affected by a fallback font.
1112 // http://crbug.com/278913, http://crbug.com/396776
1113 if (run_break
> run
->range
.start())
1114 run_break
= FindRunBreakingCharacter(text
, run
->range
.start(), run_break
);
1116 DCHECK(IsValidCodePointIndex(text
, run_break
));
1117 style
.UpdatePosition(LayoutIndexToTextIndex(run_break
));
1118 run
->range
.set_end(run_break
);
1120 runs_
.push_back(run
);
1123 // Undo the temporarily applied composition underlines and selection colors.
1124 UndoCompositionAndSelectionStyles();
1126 const size_t num_runs
= runs_
.size();
1127 std::vector
<UBiDiLevel
> levels(num_runs
);
1128 for (size_t i
= 0; i
< num_runs
; ++i
)
1129 levels
[i
] = runs_
[i
]->level
;
1130 visual_to_logical_
.resize(num_runs
);
1131 ubidi_reorderVisual(&levels
[0], num_runs
, &visual_to_logical_
[0]);
1132 logical_to_visual_
.resize(num_runs
);
1133 ubidi_reorderLogical(&levels
[0], num_runs
, &logical_to_visual_
[0]);
1136 bool RenderTextHarfBuzz::CompareFamily(
1137 internal::TextRunHarfBuzz
* run
,
1138 const std::string
& family
,
1139 const gfx::FontRenderParams
& render_params
,
1140 std::string
* best_family
,
1141 gfx::FontRenderParams
* best_render_params
,
1142 size_t* best_missing_glyphs
) {
1143 if (!ShapeRunWithFont(run
, family
, render_params
))
1146 const size_t missing_glyphs
= run
->CountMissingGlyphs();
1147 if (missing_glyphs
< *best_missing_glyphs
) {
1148 *best_family
= family
;
1149 *best_render_params
= render_params
;
1150 *best_missing_glyphs
= missing_glyphs
;
1152 return missing_glyphs
== 0;
1155 void RenderTextHarfBuzz::ShapeRun(internal::TextRunHarfBuzz
* run
) {
1156 const Font
& primary_font
= font_list().GetPrimaryFont();
1157 const std::string primary_family
= primary_font
.GetFontName();
1158 run
->font_size
= primary_font
.GetFontSize();
1160 std::string best_family
;
1161 FontRenderParams best_render_params
;
1162 size_t best_missing_glyphs
= std::numeric_limits
<size_t>::max();
1164 for (const Font
& font
: font_list().GetFonts()) {
1165 if (CompareFamily(run
, font
.GetFontName(), font
.GetFontRenderParams(),
1166 &best_family
, &best_render_params
, &best_missing_glyphs
))
1171 Font uniscribe_font
;
1172 std::string uniscribe_family
;
1173 const base::char16
* run_text
= &(GetLayoutText()[run
->range
.start()]);
1174 if (GetUniscribeFallbackFont(primary_font
, run_text
, run
->range
.length(),
1176 uniscribe_family
= uniscribe_font
.GetFontName();
1177 if (CompareFamily(run
, uniscribe_family
,
1178 uniscribe_font
.GetFontRenderParams(),
1179 &best_family
, &best_render_params
, &best_missing_glyphs
))
1184 std::vector
<std::string
> fallback_families
=
1185 GetFallbackFontFamilies(primary_family
);
1188 // Append fonts in the fallback list of the Uniscribe font.
1189 if (!uniscribe_family
.empty()) {
1190 std::vector
<std::string
> uniscribe_fallbacks
=
1191 GetFallbackFontFamilies(uniscribe_family
);
1192 fallback_families
.insert(fallback_families
.end(),
1193 uniscribe_fallbacks
.begin(), uniscribe_fallbacks
.end());
1197 // Try shaping with the fallback fonts.
1198 for (const auto& family
: fallback_families
) {
1199 if (family
== primary_family
)
1202 if (family
== uniscribe_family
)
1205 FontRenderParamsQuery
query(false);
1206 query
.families
.push_back(family
);
1207 query
.pixel_size
= run
->font_size
;
1208 query
.style
= run
->font_style
;
1209 FontRenderParams fallback_render_params
= GetFontRenderParams(query
, NULL
);
1210 if (CompareFamily(run
, family
, fallback_render_params
, &best_family
,
1211 &best_render_params
, &best_missing_glyphs
))
1215 if (!best_family
.empty() &&
1216 (best_family
== run
->family
||
1217 ShapeRunWithFont(run
, best_family
, best_render_params
)))
1220 run
->glyph_count
= 0;
1224 bool RenderTextHarfBuzz::ShapeRunWithFont(internal::TextRunHarfBuzz
* run
,
1225 const std::string
& font_family
,
1226 const FontRenderParams
& params
) {
1227 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed.
1228 tracked_objects::ScopedTracker
tracking_profile0(
1229 FROM_HERE_WITH_EXPLICIT_FUNCTION(
1230 "431326 RenderTextHarfBuzz::ShapeRunWithFont0"));
1232 const base::string16
& text
= GetLayoutText();
1233 skia::RefPtr
<SkTypeface
> skia_face
=
1234 internal::CreateSkiaTypeface(font_family
, run
->font_style
);
1235 if (skia_face
== NULL
)
1237 run
->skia_face
= skia_face
;
1238 run
->family
= font_family
;
1239 run
->render_params
= params
;
1241 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed.
1242 tracked_objects::ScopedTracker
tracking_profile01(
1243 FROM_HERE_WITH_EXPLICIT_FUNCTION(
1244 "431326 RenderTextHarfBuzz::ShapeRunWithFont01"));
1246 hb_font_t
* harfbuzz_font
= CreateHarfBuzzFont(
1247 run
->skia_face
.get(), SkIntToScalar(run
->font_size
), run
->render_params
,
1248 background_is_transparent());
1250 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed.
1251 tracked_objects::ScopedTracker
tracking_profile1(
1252 FROM_HERE_WITH_EXPLICIT_FUNCTION(
1253 "431326 RenderTextHarfBuzz::ShapeRunWithFont1"));
1255 // Create a HarfBuzz buffer and add the string to be shaped. The HarfBuzz
1256 // buffer holds our text, run information to be used by the shaping engine,
1257 // and the resulting glyph data.
1258 hb_buffer_t
* buffer
= hb_buffer_create();
1260 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed.
1261 tracked_objects::ScopedTracker
tracking_profile1q(
1262 FROM_HERE_WITH_EXPLICIT_FUNCTION(
1263 "431326 RenderTextHarfBuzz::ShapeRunWithFont11"));
1265 hb_buffer_add_utf16(buffer
, reinterpret_cast<const uint16
*>(text
.c_str()),
1266 text
.length(), run
->range
.start(), run
->range
.length());
1268 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed.
1269 tracked_objects::ScopedTracker
tracking_profile12(
1270 FROM_HERE_WITH_EXPLICIT_FUNCTION(
1271 "431326 RenderTextHarfBuzz::ShapeRunWithFont12"));
1273 hb_buffer_set_script(buffer
, ICUScriptToHBScript(run
->script
));
1275 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed.
1276 tracked_objects::ScopedTracker
tracking_profile13(
1277 FROM_HERE_WITH_EXPLICIT_FUNCTION(
1278 "431326 RenderTextHarfBuzz::ShapeRunWithFont13"));
1280 hb_buffer_set_direction(buffer
,
1281 run
->is_rtl
? HB_DIRECTION_RTL
: HB_DIRECTION_LTR
);
1283 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed.
1284 tracked_objects::ScopedTracker
tracking_profile14(
1285 FROM_HERE_WITH_EXPLICIT_FUNCTION(
1286 "431326 RenderTextHarfBuzz::ShapeRunWithFont14"));
1288 // TODO(ckocagil): Should we determine the actual language?
1289 hb_buffer_set_language(buffer
, hb_language_get_default());
1291 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed.
1292 tracked_objects::ScopedTracker
tracking_profile15(
1293 FROM_HERE_WITH_EXPLICIT_FUNCTION(
1294 "431326 RenderTextHarfBuzz::ShapeRunWithFont15"));
1297 hb_shape(harfbuzz_font
, buffer
, NULL
, 0);
1299 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed.
1300 tracked_objects::ScopedTracker
tracking_profile2(
1301 FROM_HERE_WITH_EXPLICIT_FUNCTION(
1302 "431326 RenderTextHarfBuzz::ShapeRunWithFont2"));
1304 // Populate the run fields with the resulting glyph data in the buffer.
1305 unsigned int glyph_count
= 0;
1306 hb_glyph_info_t
* infos
= hb_buffer_get_glyph_infos(buffer
, &glyph_count
);
1307 run
->glyph_count
= glyph_count
;
1308 hb_glyph_position_t
* hb_positions
=
1309 hb_buffer_get_glyph_positions(buffer
, NULL
);
1310 run
->glyphs
.reset(new uint16
[run
->glyph_count
]);
1311 run
->glyph_to_char
.resize(run
->glyph_count
);
1312 run
->positions
.reset(new SkPoint
[run
->glyph_count
]);
1315 // TODO(vadimt): Remove ScopedTracker below once crbug.com/431326 is fixed.
1316 tracked_objects::ScopedTracker
tracking_profile3(
1317 FROM_HERE_WITH_EXPLICIT_FUNCTION(
1318 "431326 RenderTextHarfBuzz::ShapeRunWithFont3"));
1320 for (size_t i
= 0; i
< run
->glyph_count
; ++i
) {
1321 DCHECK_LE(infos
[i
].codepoint
, std::numeric_limits
<uint16
>::max());
1322 run
->glyphs
[i
] = static_cast<uint16
>(infos
[i
].codepoint
);
1323 run
->glyph_to_char
[i
] = infos
[i
].cluster
;
1324 const SkScalar x_offset
= SkFixedToScalar(hb_positions
[i
].x_offset
);
1325 const SkScalar y_offset
= SkFixedToScalar(hb_positions
[i
].y_offset
);
1326 run
->positions
[i
].set(run
->width
+ x_offset
, -y_offset
);
1327 run
->width
+= SkFixedToScalar(hb_positions
[i
].x_advance
);
1328 // Round run widths if subpixel positioning is off to match native behavior.
1329 if (!run
->render_params
.subpixel_positioning
)
1330 run
->width
= std::floor(run
->width
+ 0.5f
);
1333 hb_buffer_destroy(buffer
);
1334 hb_font_destroy(harfbuzz_font
);