1 // Copyright (c) 2012 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_win.h"
9 #include "base/i18n/break_iterator.h"
10 #include "base/i18n/char_iterator.h"
11 #include "base/i18n/rtl.h"
12 #include "base/logging.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/win/windows_version.h"
16 #include "third_party/icu/source/common/unicode/uchar.h"
17 #include "ui/gfx/canvas.h"
18 #include "ui/gfx/font_fallback_win.h"
19 #include "ui/gfx/font_render_params.h"
20 #include "ui/gfx/platform_font_win.h"
21 #include "ui/gfx/utf16_indexing.h"
27 // The maximum length of text supported for Uniscribe layout and display.
28 // This empirically chosen value should prevent major performance degradations.
29 // TODO(msw): Support longer text, partial layout/painting, etc.
30 const size_t kMaxUniscribeTextLength
= 10000;
32 // The initial guess and maximum supported number of runs; arbitrary values.
33 // TODO(msw): Support more runs, determine a better initial guess, etc.
34 const int kGuessRuns
= 100;
35 const size_t kMaxRuns
= 10000;
37 // The maximum number of glyphs per run; ScriptShape fails on larger values.
38 const size_t kMaxGlyphs
= 65535;
40 // Callback to |EnumEnhMetaFile()| to intercept font creation.
41 int CALLBACK
MetaFileEnumProc(HDC hdc
,
43 CONST ENHMETARECORD
* record
,
46 if (record
->iType
== EMR_EXTCREATEFONTINDIRECTW
) {
47 const EMREXTCREATEFONTINDIRECTW
* create_font_record
=
48 reinterpret_cast<const EMREXTCREATEFONTINDIRECTW
*>(record
);
49 *reinterpret_cast<LOGFONT
*>(log_font
) = create_font_record
->elfw
.elfLogFont
;
54 // Finds a fallback font to use to render the specified |text| with respect to
55 // an initial |font|. Returns the resulting font via out param |result|. Returns
56 // |true| if a fallback font was found.
57 // Adapted from WebKit's |FontCache::GetFontDataForCharacters()|.
58 // TODO(asvitkine): This should be moved to font_fallback_win.cc.
59 bool ChooseFallbackFont(HDC hdc
,
64 // Use a meta file to intercept the fallback font chosen by Uniscribe.
65 HDC meta_file_dc
= CreateEnhMetaFile(hdc
, NULL
, NULL
, NULL
);
69 SelectObject(meta_file_dc
, font
.GetNativeFont());
71 SCRIPT_STRING_ANALYSIS script_analysis
;
73 ScriptStringAnalyse(meta_file_dc
, text
, text_length
, 0, -1,
74 SSA_METAFILE
| SSA_FALLBACK
| SSA_GLYPHS
| SSA_LINK
,
75 0, NULL
, NULL
, NULL
, NULL
, NULL
, &script_analysis
);
77 if (SUCCEEDED(hresult
)) {
78 hresult
= ScriptStringOut(script_analysis
, 0, 0, 0, NULL
, 0, 0, FALSE
);
79 ScriptStringFree(&script_analysis
);
82 bool found_fallback
= false;
83 HENHMETAFILE meta_file
= CloseEnhMetaFile(meta_file_dc
);
84 if (SUCCEEDED(hresult
)) {
86 log_font
.lfFaceName
[0] = 0;
87 EnumEnhMetaFile(0, meta_file
, MetaFileEnumProc
, &log_font
, NULL
);
88 if (log_font
.lfFaceName
[0]) {
89 *result
= Font(base::UTF16ToUTF8(log_font
.lfFaceName
),
91 found_fallback
= true;
94 DeleteEnhMetaFile(meta_file
);
96 return found_fallback
;
99 // Changes |font| to have the specified |font_size| (or |font_height| on Windows
100 // XP) and |font_style| if it is not the case already. Only considers bold and
101 // italic styles, since the underlined style has no effect on glyph shaping.
102 void DeriveFontIfNecessary(int font_size
,
106 const int kStyleMask
= (Font::BOLD
| Font::ITALIC
);
107 const int target_style
= (font_style
& kStyleMask
);
109 // On Windows XP, the font must be resized using |font_height| instead of
110 // |font_size| to match GDI behavior.
111 if (base::win::GetVersion() < base::win::VERSION_VISTA
) {
112 PlatformFontWin
* platform_font
=
113 static_cast<PlatformFontWin
*>(font
->platform_font());
114 *font
= platform_font
->DeriveFontWithHeight(font_height
, target_style
);
118 const int current_style
= (font
->GetStyle() & kStyleMask
);
119 const int current_size
= font
->GetFontSize();
120 if (current_style
!= target_style
|| current_size
!= font_size
)
121 *font
= font
->Derive(font_size
- current_size
, target_style
);
124 // Returns true if |c| is a Unicode BiDi control character.
125 bool IsUnicodeBidiControlCharacter(base::char16 c
) {
126 return c
== base::i18n::kRightToLeftMark
||
127 c
== base::i18n::kLeftToRightMark
||
128 c
== base::i18n::kLeftToRightEmbeddingMark
||
129 c
== base::i18n::kRightToLeftEmbeddingMark
||
130 c
== base::i18n::kPopDirectionalFormatting
||
131 c
== base::i18n::kLeftToRightOverride
||
132 c
== base::i18n::kRightToLeftOverride
;
135 // Returns the corresponding glyph range of the given character range.
136 // |range| is in text-space (0 corresponds to |GetLayoutText()[0]|).
137 // Returned value is in run-space (0 corresponds to the first glyph in the run).
138 Range
CharRangeToGlyphRange(const internal::TextRun
& run
,
139 const Range
& range
) {
140 DCHECK(run
.range
.Contains(range
));
141 DCHECK(!range
.is_reversed());
142 DCHECK(!range
.is_empty());
143 const Range
run_range(range
.start() - run
.range
.start(),
144 range
.end() - run
.range
.start());
146 if (run
.script_analysis
.fRTL
) {
147 result
= Range(run
.logical_clusters
[run_range
.end() - 1],
148 run_range
.start() > 0 ? run
.logical_clusters
[run_range
.start() - 1]
151 result
= Range(run
.logical_clusters
[run_range
.start()],
152 run_range
.end() < run
.range
.length() ?
153 run
.logical_clusters
[run_range
.end()] : run
.glyph_count
);
155 DCHECK(!result
.is_reversed());
156 DCHECK(Range(0, run
.glyph_count
).Contains(result
));
160 // Starting from |start_char|, finds a suitable line break position at or before
161 // |available_width| using word break info from |breaks|. If |empty_line| is
162 // true, this function will not roll back to |start_char| and |*next_char| will
163 // be greater than |start_char| (to avoid constructing empty lines). Returns
164 // whether to skip the line before |*next_char|.
165 // TODO(ckocagil): Do not break ligatures and diacritics.
166 // TextRun::logical_clusters might help.
167 // TODO(ckocagil): We might have to reshape after breaking at ligatures.
168 // See whether resolving the TODO above resolves this too.
169 // TODO(ckocagil): Do not reserve width for whitespace at the end of lines.
170 bool BreakRunAtWidth(const wchar_t* text
,
171 const internal::TextRun
& run
,
172 const BreakList
<size_t>& breaks
,
178 DCHECK(run
.range
.Contains(Range(start_char
, start_char
+ 1)));
179 BreakList
<size_t>::const_iterator word
= breaks
.GetBreak(start_char
);
180 BreakList
<size_t>::const_iterator next_word
= word
+ 1;
181 // Width from |std::max(word->first, start_char)| to the current character.
185 for (size_t i
= start_char
; i
< run
.range
.end(); ++i
) {
186 if (U16_IS_SINGLE(text
[i
]) && text
[i
] == L
'\n') {
191 // |word| holds the word boundary at or before |i|, and |next_word| holds
192 // the word boundary right after |i|. Advance both |word| and |next_word|
193 // when |i| reaches |next_word|.
194 if (next_word
!= breaks
.breaks().end() && i
>= next_word
->first
) {
199 Range glyph_range
= CharRangeToGlyphRange(run
, Range(i
, i
+ 1));
201 for (size_t j
= glyph_range
.start(); j
< glyph_range
.end(); ++j
)
202 char_width
+= run
.advance_widths
[j
];
204 *width
+= char_width
;
205 word_width
+= char_width
;
207 if (*width
> available_width
) {
208 if (!empty_line
|| word_width
< *width
) {
209 // Roll back one word.
210 *width
-= word_width
;
211 *next_char
= std::max(word
->first
, start_char
);
212 } else if (char_width
< *width
) {
213 // Roll back one character.
214 *width
-= char_width
;
217 // Continue from the next character.
225 *next_char
= run
.range
.end();
229 // For segments in the same run, checks the continuity and order of |x_range|
230 // and |char_range| fields.
231 void CheckLineIntegrity(const std::vector
<internal::Line
>& lines
,
232 const ScopedVector
<internal::TextRun
>& runs
) {
233 size_t previous_segment_line
= 0;
234 const internal::LineSegment
* previous_segment
= NULL
;
236 for (size_t i
= 0; i
< lines
.size(); ++i
) {
237 for (size_t j
= 0; j
< lines
[i
].segments
.size(); ++j
) {
238 const internal::LineSegment
* segment
= &lines
[i
].segments
[j
];
239 internal::TextRun
* run
= runs
[segment
->run
];
241 if (!previous_segment
) {
242 previous_segment
= segment
;
243 } else if (runs
[previous_segment
->run
] != run
) {
244 previous_segment
= NULL
;
246 DCHECK_EQ(previous_segment
->char_range
.end(),
247 segment
->char_range
.start());
248 if (!run
->script_analysis
.fRTL
) {
249 DCHECK_EQ(previous_segment
->x_range
.end(), segment
->x_range
.start());
251 DCHECK_EQ(segment
->x_range
.end(), previous_segment
->x_range
.start());
254 previous_segment
= segment
;
255 previous_segment_line
= i
;
261 // Returns true if characters of |block_code| may trigger font fallback.
262 bool IsUnusualBlockCode(const UBlockCode block_code
) {
263 return block_code
== UBLOCK_GEOMETRIC_SHAPES
||
264 block_code
== UBLOCK_MISCELLANEOUS_SYMBOLS
;
267 // Returns the index of the first unusual character after a usual character or
268 // vice versa. Unusual characters are defined by |IsUnusualBlockCode|.
269 size_t FindUnusualCharacter(const base::string16
& text
,
272 const int32 run_length
= static_cast<int32
>(run_break
- run_start
);
273 base::i18n::UTF16CharIterator
iter(text
.c_str() + run_start
,
275 const UBlockCode first_block_code
= ublock_getCode(iter
.get());
276 const bool first_block_unusual
= IsUnusualBlockCode(first_block_code
);
277 while (iter
.Advance() && iter
.array_pos() < run_length
) {
278 const UBlockCode current_block_code
= ublock_getCode(iter
.get());
279 if (current_block_code
!= first_block_code
&&
280 (first_block_unusual
|| IsUnusualBlockCode(current_block_code
))) {
281 return run_start
+ iter
.array_pos();
294 diagonal_strike(false),
297 preceding_run_widths(0),
300 memset(&script_analysis
, 0, sizeof(script_analysis
));
301 memset(&abc_widths
, 0, sizeof(abc_widths
));
304 TextRun::~TextRun() {
305 ScriptFreeCache(&script_cache
);
308 // Returns the X coordinate of the leading or |trailing| edge of the glyph
309 // starting at |index|, relative to the left of the text (not the view).
310 int GetGlyphXBoundary(const internal::TextRun
* run
,
313 DCHECK_GE(index
, run
->range
.start());
314 DCHECK_LT(index
, run
->range
.end() + (trailing
? 0 : 1));
316 HRESULT hr
= ScriptCPtoX(
317 index
- run
->range
.start(),
321 run
->logical_clusters
.get(),
322 run
->visible_attributes
.get(),
323 run
->advance_widths
.get(),
324 &run
->script_analysis
,
326 DCHECK(SUCCEEDED(hr
));
327 return run
->preceding_run_widths
+ x
;
330 // Internal class to generate Line structures. If |multiline| is true, the text
331 // is broken into lines at |words| boundaries such that each line is no longer
332 // than |max_width|. If |multiline| is false, only outputs a single Line from
333 // the given runs. |min_baseline| and |min_height| are the minimum baseline and
334 // height for each line.
335 // TODO(ckocagil): Expose the interface of this class in the header and test
336 // this class directly.
339 LineBreaker(int max_width
,
344 const BreakList
<size_t>* words
,
345 const ScopedVector
<TextRun
>& runs
)
346 : max_width_(max_width
),
347 min_baseline_(min_baseline
),
348 min_height_(min_height
),
349 multiline_(multiline
),
360 // Breaks the run at given |run_index| into Line structs.
361 void AddRun(int run_index
) {
362 const TextRun
* run
= runs_
[run_index
];
363 bool run_fits
= !multiline_
;
364 if (multiline_
&& line_x_
+ run
->width
<= max_width_
) {
365 DCHECK(!run
->range
.is_empty());
366 const wchar_t first_char
= text_
[run
->range
.start()];
367 // Uniscribe always puts newline characters in their own runs.
368 if (!U16_IS_SINGLE(first_char
) || first_char
!= L
'\n')
375 AddSegment(run_index
, run
->range
, run
->width
);
378 // Finishes line breaking and outputs the results. Can be called at most once.
379 void Finalize(std::vector
<Line
>* lines
, Size
* size
) {
380 DCHECK(!lines_
.empty());
381 // Add an empty line to finish the line size calculation and remove it.
389 // A (line index, segment index) pair that specifies a segment in |lines_|.
390 typedef std::pair
<size_t, size_t> SegmentHandle
;
392 LineSegment
* SegmentFromHandle(const SegmentHandle
& handle
) {
393 return &lines_
[handle
.first
].segments
[handle
.second
];
396 // Breaks a run into segments that fit in the last line in |lines_| and adds
397 // them. Adds a new Line to the back of |lines_| whenever a new segment can't
398 // be added without the Line's width exceeding |max_width_|.
399 void BreakRun(int run_index
) {
401 const TextRun
* const run
= runs_
[run_index
];
403 size_t next_char
= run
->range
.start();
405 // Break the run until it fits the current line.
406 while (next_char
< run
->range
.end()) {
407 const size_t current_char
= next_char
;
408 const bool skip_line
= BreakRunAtWidth(text_
, *run
, *words_
, current_char
,
409 max_width_
- line_x_
, line_x_
== 0, &width
, &next_char
);
410 AddSegment(run_index
, Range(current_char
, next_char
), width
);
416 // RTL runs are broken in logical order but displayed in visual order. To find
417 // the text-space coordinate (where it would fall in a single-line text)
418 // |x_range| of RTL segments, segment widths are applied in reverse order.
419 // e.g. {[5, 10], [10, 40]} will become {[35, 40], [5, 35]}.
420 void UpdateRTLSegmentRanges() {
421 if (rtl_segments_
.empty())
423 int x
= SegmentFromHandle(rtl_segments_
[0])->x_range
.start();
424 for (size_t i
= rtl_segments_
.size(); i
> 0; --i
) {
425 LineSegment
* segment
= SegmentFromHandle(rtl_segments_
[i
- 1]);
426 const size_t segment_width
= segment
->x_range
.length();
427 segment
->x_range
= Range(x
, x
+ segment_width
);
430 rtl_segments_
.clear();
433 // Finishes the size calculations of the last Line in |lines_|. Adds a new
434 // Line to the back of |lines_|.
436 if (!lines_
.empty()) {
437 Line
* line
= &lines_
.back();
438 // TODO(ckocagil): Determine optimal multiline height behavior.
439 if (line_ascent_
+ line_descent_
== 0) {
440 line_ascent_
= min_baseline_
;
441 line_descent_
= min_height_
- min_baseline_
;
443 // Set the single-line mode Line's metrics to be at least
444 // |RenderText::font_list()| to not break the current single-line code.
445 line_ascent_
= std::max(line_ascent_
, min_baseline_
);
446 line_descent_
= std::max(line_descent_
, min_height_
- min_baseline_
);
448 line
->baseline
= line_ascent_
;
449 line
->size
.set_height(line_ascent_
+ line_descent_
);
450 line
->preceding_heights
= total_size_
.height();
451 total_size_
.set_height(total_size_
.height() + line
->size
.height());
452 total_size_
.set_width(std::max(total_size_
.width(), line
->size
.width()));
457 lines_
.push_back(Line());
460 // Adds a new segment with the given properties to |lines_.back()|.
461 void AddSegment(int run_index
, Range char_range
, int width
) {
462 if (char_range
.is_empty()) {
466 const TextRun
* run
= runs_
[run_index
];
467 line_ascent_
= std::max(line_ascent_
, run
->font
.GetBaseline());
468 line_descent_
= std::max(line_descent_
,
469 run
->font
.GetHeight() - run
->font
.GetBaseline());
472 segment
.run
= run_index
;
473 segment
.char_range
= char_range
;
474 segment
.x_range
= Range(text_x_
, text_x_
+ width
);
476 Line
* line
= &lines_
.back();
477 line
->segments
.push_back(segment
);
478 line
->size
.set_width(line
->size
.width() + segment
.x_range
.length());
479 if (run
->script_analysis
.fRTL
) {
480 rtl_segments_
.push_back(SegmentHandle(lines_
.size() - 1,
481 line
->segments
.size() - 1));
482 // If this is the last segment of an RTL run, reprocess the text-space x
483 // ranges of all segments from the run.
484 if (char_range
.end() == run
->range
.end())
485 UpdateRTLSegmentRanges();
491 const int max_width_
;
492 const int min_baseline_
;
493 const int min_height_
;
494 const bool multiline_
;
495 const wchar_t* text_
;
496 const BreakList
<size_t>* const words_
;
497 const ScopedVector
<TextRun
>& runs_
;
499 // Stores the resulting lines.
500 std::vector
<Line
> lines_
;
502 // Text space and line space x coordinates of the next segment to be added.
506 // Size of the multiline text, not including the currently processed line.
509 // Ascent and descent values of the current line, |lines_.back()|.
513 // The current RTL run segments, to be applied by |UpdateRTLSegmentRanges()|.
514 std::vector
<SegmentHandle
> rtl_segments_
;
516 DISALLOW_COPY_AND_ASSIGN(LineBreaker
);
519 } // namespace internal
522 HDC
RenderTextWin::cached_hdc_
= NULL
;
525 std::map
<std::string
, Font
> RenderTextWin::successful_substitute_fonts_
;
527 RenderTextWin::RenderTextWin() : RenderText(), needs_layout_(false) {
528 set_truncate_length(kMaxUniscribeTextLength
);
529 memset(&script_control_
, 0, sizeof(script_control_
));
530 memset(&script_state_
, 0, sizeof(script_state_
));
531 MoveCursorTo(EdgeSelectionModel(CURSOR_LEFT
));
534 RenderTextWin::~RenderTextWin() {}
536 Size
RenderTextWin::GetStringSize() {
538 return multiline_string_size_
;
541 SelectionModel
RenderTextWin::FindCursorPosition(const Point
& point
) {
543 return SelectionModel();
546 // Find the run that contains the point and adjust the argument location.
547 int x
= ToTextPoint(point
).x();
548 size_t run_index
= GetRunContainingXCoord(x
);
549 if (run_index
>= runs_
.size())
550 return EdgeSelectionModel((x
< 0) ? CURSOR_LEFT
: CURSOR_RIGHT
);
551 internal::TextRun
* run
= runs_
[run_index
];
553 int position
= 0, trailing
= 0;
554 HRESULT hr
= ScriptXtoCP(x
- run
->preceding_run_widths
,
557 run
->logical_clusters
.get(),
558 run
->visible_attributes
.get(),
559 run
->advance_widths
.get(),
560 &(run
->script_analysis
),
563 DCHECK(SUCCEEDED(hr
));
564 DCHECK_GE(trailing
, 0);
565 position
+= run
->range
.start();
566 const size_t cursor
= LayoutIndexToTextIndex(position
+ trailing
);
567 DCHECK_LE(cursor
, text().length());
568 return SelectionModel(cursor
, trailing
? CURSOR_BACKWARD
: CURSOR_FORWARD
);
571 std::vector
<RenderText::FontSpan
> RenderTextWin::GetFontSpansForTesting() {
574 std::vector
<RenderText::FontSpan
> spans
;
575 for (size_t i
= 0; i
< runs_
.size(); ++i
) {
576 spans
.push_back(RenderText::FontSpan(runs_
[i
]->font
,
577 Range(LayoutIndexToTextIndex(runs_
[i
]->range
.start()),
578 LayoutIndexToTextIndex(runs_
[i
]->range
.end()))));
584 int RenderTextWin::GetLayoutTextBaseline() {
586 return lines()[0].baseline
;
589 SelectionModel
RenderTextWin::AdjacentCharSelectionModel(
590 const SelectionModel
& selection
,
591 VisualCursorDirection direction
) {
592 DCHECK(!needs_layout_
);
593 internal::TextRun
* run
;
594 size_t run_index
= GetRunContainingCaret(selection
);
595 if (run_index
>= runs_
.size()) {
596 // The cursor is not in any run: we're at the visual and logical edge.
597 SelectionModel edge
= EdgeSelectionModel(direction
);
598 if (edge
.caret_pos() == selection
.caret_pos())
600 int visual_index
= (direction
== CURSOR_RIGHT
) ? 0 : runs_
.size() - 1;
601 run
= runs_
[visual_to_logical_
[visual_index
]];
603 // If the cursor is moving within the current run, just move it by one
604 // grapheme in the appropriate direction.
605 run
= runs_
[run_index
];
606 size_t caret
= selection
.caret_pos();
607 bool forward_motion
=
608 run
->script_analysis
.fRTL
== (direction
== CURSOR_LEFT
);
609 if (forward_motion
) {
610 if (caret
< LayoutIndexToTextIndex(run
->range
.end())) {
611 caret
= IndexOfAdjacentGrapheme(caret
, CURSOR_FORWARD
);
612 return SelectionModel(caret
, CURSOR_BACKWARD
);
615 if (caret
> LayoutIndexToTextIndex(run
->range
.start())) {
616 caret
= IndexOfAdjacentGrapheme(caret
, CURSOR_BACKWARD
);
617 return SelectionModel(caret
, CURSOR_FORWARD
);
620 // The cursor is at the edge of a run; move to the visually adjacent run.
621 int visual_index
= logical_to_visual_
[run_index
];
622 visual_index
+= (direction
== CURSOR_LEFT
) ? -1 : 1;
623 if (visual_index
< 0 || visual_index
>= static_cast<int>(runs_
.size()))
624 return EdgeSelectionModel(direction
);
625 run
= runs_
[visual_to_logical_
[visual_index
]];
627 bool forward_motion
= run
->script_analysis
.fRTL
== (direction
== CURSOR_LEFT
);
628 return forward_motion
? FirstSelectionModelInsideRun(run
) :
629 LastSelectionModelInsideRun(run
);
632 // TODO(msw): Implement word breaking for Windows.
633 SelectionModel
RenderTextWin::AdjacentWordSelectionModel(
634 const SelectionModel
& selection
,
635 VisualCursorDirection direction
) {
637 return EdgeSelectionModel(direction
);
639 base::i18n::BreakIterator
iter(text(), base::i18n::BreakIterator::BREAK_WORD
);
640 bool success
= iter
.Init();
646 if (direction
== CURSOR_RIGHT
) {
647 pos
= std::min(selection
.caret_pos() + 1, text().length());
648 while (iter
.Advance()) {
650 if (iter
.IsWord() && pos
> selection
.caret_pos())
653 } else { // direction == CURSOR_LEFT
654 // Notes: We always iterate words from the beginning.
655 // This is probably fast enough for our usage, but we may
656 // want to modify WordIterator so that it can start from the
657 // middle of string and advance backwards.
658 pos
= std::max
<int>(selection
.caret_pos() - 1, 0);
659 while (iter
.Advance()) {
661 size_t begin
= iter
.pos() - iter
.GetString().length();
662 if (begin
== selection
.caret_pos()) {
663 // The cursor is at the beginning of a word.
664 // Move to previous word.
666 } else if (iter
.pos() >= selection
.caret_pos()) {
667 // The cursor is in the middle or at the end of a word.
668 // Move to the top of current word.
672 pos
= iter
.pos() - iter
.GetString().length();
677 return SelectionModel(pos
, CURSOR_FORWARD
);
680 Range
RenderTextWin::GetGlyphBounds(size_t index
) {
682 const size_t run_index
=
683 GetRunContainingCaret(SelectionModel(index
, CURSOR_FORWARD
));
684 // Return edge bounds if the index is invalid or beyond the layout text size.
685 if (run_index
>= runs_
.size())
686 return Range(string_width_
);
687 internal::TextRun
* run
= runs_
[run_index
];
688 const size_t layout_index
= TextIndexToLayoutIndex(index
);
689 return Range(GetGlyphXBoundary(run
, layout_index
, false),
690 GetGlyphXBoundary(run
, layout_index
, true));
693 std::vector
<Rect
> RenderTextWin::GetSubstringBounds(const Range
& range
) {
694 DCHECK(!needs_layout_
);
695 DCHECK(Range(0, text().length()).Contains(range
));
696 Range
layout_range(TextIndexToLayoutIndex(range
.start()),
697 TextIndexToLayoutIndex(range
.end()));
698 DCHECK(Range(0, GetLayoutText().length()).Contains(layout_range
));
700 std::vector
<Rect
> rects
;
701 if (layout_range
.is_empty())
703 std::vector
<Range
> bounds
;
705 // Add a Range for each run/selection intersection.
706 // TODO(msw): The bounds should probably not always be leading the range ends.
707 for (size_t i
= 0; i
< runs_
.size(); ++i
) {
708 const internal::TextRun
* run
= runs_
[visual_to_logical_
[i
]];
709 Range intersection
= run
->range
.Intersect(layout_range
);
710 if (intersection
.IsValid()) {
711 DCHECK(!intersection
.is_reversed());
712 Range
range_x(GetGlyphXBoundary(run
, intersection
.start(), false),
713 GetGlyphXBoundary(run
, intersection
.end(), false));
714 if (range_x
.is_empty())
716 range_x
= Range(range_x
.GetMin(), range_x
.GetMax());
717 // Union this with the last range if they're adjacent.
718 DCHECK(bounds
.empty() || bounds
.back().GetMax() <= range_x
.GetMin());
719 if (!bounds
.empty() && bounds
.back().GetMax() == range_x
.GetMin()) {
720 range_x
= Range(bounds
.back().GetMin(), range_x
.GetMax());
723 bounds
.push_back(range_x
);
726 for (size_t i
= 0; i
< bounds
.size(); ++i
) {
727 std::vector
<Rect
> current_rects
= TextBoundsToViewBounds(bounds
[i
]);
728 rects
.insert(rects
.end(), current_rects
.begin(), current_rects
.end());
733 size_t RenderTextWin::TextIndexToLayoutIndex(size_t index
) const {
734 DCHECK_LE(index
, text().length());
735 ptrdiff_t i
= obscured() ? UTF16IndexToOffset(text(), 0, index
) : index
;
737 // Clamp layout indices to the length of the text actually used for layout.
738 return std::min
<size_t>(GetLayoutText().length(), i
);
741 size_t RenderTextWin::LayoutIndexToTextIndex(size_t index
) const {
745 DCHECK_LE(index
, GetLayoutText().length());
746 const size_t text_index
= UTF16OffsetToIndex(text(), 0, index
);
747 DCHECK_LE(text_index
, text().length());
751 bool RenderTextWin::IsValidCursorIndex(size_t index
) {
752 if (index
== 0 || index
== text().length())
754 if (!IsValidLogicalIndex(index
))
757 // Disallow indices amid multi-character graphemes by checking glyph bounds.
758 // These characters are not surrogate-pairs, but may yield a single glyph:
759 // \x0915\x093f - (ki) - one of many Devanagari biconsonantal conjuncts.
760 // \x0e08\x0e33 - (cho chan + sara am) - a Thai consonant and vowel pair.
761 return GetGlyphBounds(index
) != GetGlyphBounds(index
- 1);
764 void RenderTextWin::ResetLayout() {
765 // Layout is performed lazily as needed for drawing/metrics.
766 needs_layout_
= true;
769 void RenderTextWin::EnsureLayout() {
771 // TODO(msw): Skip complex processing if ScriptIsComplex returns false.
772 ItemizeLogicalText();
775 needs_layout_
= false;
776 std::vector
<internal::Line
> lines
;
780 // Compute lines if they're not valid. This is separate from the layout steps
781 // above to avoid text layout and shaping when we resize |display_rect_|.
782 if (lines().empty()) {
783 DCHECK(!needs_layout_
);
784 std::vector
<internal::Line
> lines
;
785 internal::LineBreaker
line_breaker(display_rect().width() - 1,
786 font_list().GetBaseline(),
787 font_list().GetHeight(), multiline(),
788 GetLayoutText().c_str(),
789 multiline() ? &GetLineBreaks() : NULL
,
791 for (size_t i
= 0; i
< runs_
.size(); ++i
)
792 line_breaker
.AddRun(visual_to_logical_
[i
]);
793 line_breaker
.Finalize(&lines
, &multiline_string_size_
);
794 DCHECK(!lines
.empty());
796 CheckLineIntegrity(lines
, runs_
);
802 void RenderTextWin::DrawVisualText(Canvas
* canvas
) {
803 DCHECK(!needs_layout_
);
804 DCHECK(!lines().empty());
806 std::vector
<SkPoint
> pos
;
808 internal::SkiaTextRenderer
renderer(canvas
);
809 ApplyFadeEffects(&renderer
);
810 ApplyTextShadows(&renderer
);
812 renderer
.SetFontRenderParams(
813 font_list().GetPrimaryFont().GetFontRenderParams(),
814 background_is_transparent());
816 ApplyCompositionAndSelectionStyles();
818 for (size_t i
= 0; i
< lines().size(); ++i
) {
819 const internal::Line
& line
= lines()[i
];
820 const Vector2d line_offset
= GetLineOffset(i
);
822 // Skip painting empty lines or lines outside the display rect area.
823 if (!display_rect().Intersects(Rect(PointAtOffsetFromOrigin(line_offset
),
827 const Vector2d text_offset
= line_offset
+ Vector2d(0, line
.baseline
);
828 int preceding_segment_widths
= 0;
830 for (size_t j
= 0; j
< line
.segments
.size(); ++j
) {
831 const internal::LineSegment
* segment
= &line
.segments
[j
];
832 const int segment_width
= segment
->x_range
.length();
833 const internal::TextRun
* run
= runs_
[segment
->run
];
834 DCHECK(!segment
->char_range
.is_empty());
835 DCHECK(run
->range
.Contains(segment
->char_range
));
836 Range glyph_range
= CharRangeToGlyphRange(*run
, segment
->char_range
);
837 DCHECK(!glyph_range
.is_empty());
838 // Skip painting segments outside the display rect area.
840 const Rect
segment_bounds(PointAtOffsetFromOrigin(line_offset
) +
841 Vector2d(preceding_segment_widths
, 0),
842 Size(segment_width
, line
.size
.height()));
843 if (!display_rect().Intersects(segment_bounds
)) {
844 preceding_segment_widths
+= segment_width
;
849 // |pos| contains the positions of glyphs. An extra terminal |pos| entry
850 // is added to simplify width calculations.
851 int segment_x
= preceding_segment_widths
;
852 pos
.resize(glyph_range
.length() + 1);
853 for (size_t k
= glyph_range
.start(); k
< glyph_range
.end(); ++k
) {
854 pos
[k
- glyph_range
.start()].set(
855 SkIntToScalar(text_offset
.x() + run
->offsets
[k
].du
+ segment_x
),
856 SkIntToScalar(text_offset
.y() - run
->offsets
[k
].dv
));
857 segment_x
+= run
->advance_widths
[k
];
859 pos
.back().set(SkIntToScalar(text_offset
.x() + segment_x
),
860 SkIntToScalar(text_offset
.y()));
862 renderer
.SetTextSize(run
->font
.GetFontSize());
863 renderer
.SetFontFamilyWithStyle(run
->font
.GetFontName(), run
->font_style
);
865 for (BreakList
<SkColor
>::const_iterator it
=
866 colors().GetBreak(segment
->char_range
.start());
867 it
!= colors().breaks().end() &&
868 it
->first
< segment
->char_range
.end();
870 const Range intersection
=
871 colors().GetRange(it
).Intersect(segment
->char_range
);
872 const Range colored_glyphs
= CharRangeToGlyphRange(*run
, intersection
);
873 // The range may be empty if a portion of a multi-character grapheme is
874 // selected, yielding two colors for a single glyph. For now, this just
875 // paints the glyph with a single style, but it should paint it twice,
876 // clipped according to selection bounds. See http://crbug.com/366786
877 if (colored_glyphs
.is_empty())
879 DCHECK(glyph_range
.Contains(colored_glyphs
));
880 const SkPoint
& start_pos
=
881 pos
[colored_glyphs
.start() - glyph_range
.start()];
882 const SkPoint
& end_pos
=
883 pos
[colored_glyphs
.end() - glyph_range
.start()];
885 renderer
.SetForegroundColor(it
->second
);
886 renderer
.DrawPosText(&start_pos
, &run
->glyphs
[colored_glyphs
.start()],
887 colored_glyphs
.length());
888 renderer
.DrawDecorations(start_pos
.x(), text_offset
.y(),
889 SkScalarCeilToInt(end_pos
.x() - start_pos
.x()),
890 run
->underline
, run
->strike
,
891 run
->diagonal_strike
);
894 preceding_segment_widths
+= segment_width
;
897 renderer
.EndDiagonalStrike();
900 UndoCompositionAndSelectionStyles();
903 void RenderTextWin::ItemizeLogicalText() {
906 multiline_string_size_
= Size();
908 // Set Uniscribe's base text direction.
909 script_state_
.uBidiLevel
=
910 (GetTextDirection() == base::i18n::RIGHT_TO_LEFT
) ? 1 : 0;
912 const base::string16
& layout_text
= GetLayoutText();
913 if (layout_text
.empty())
916 HRESULT hr
= E_OUTOFMEMORY
;
917 int script_items_count
= 0;
918 std::vector
<SCRIPT_ITEM
> script_items
;
919 const size_t layout_text_length
= layout_text
.length();
920 // Ensure that |kMaxRuns| is attempted and the loop terminates afterward.
921 for (size_t runs
= kGuessRuns
; hr
== E_OUTOFMEMORY
&& runs
<= kMaxRuns
;
922 runs
= std::max(runs
+ 1, std::min(runs
* 2, kMaxRuns
))) {
923 // Derive the array of Uniscribe script items from the logical text.
924 // ScriptItemize always adds a terminal array item so that the length of
925 // the last item can be derived from the terminal SCRIPT_ITEM::iCharPos.
926 script_items
.resize(runs
);
927 hr
= ScriptItemize(layout_text
.c_str(), layout_text_length
, runs
- 1,
928 &script_control_
, &script_state_
, &script_items
[0],
929 &script_items_count
);
931 DCHECK(SUCCEEDED(hr
));
932 if (!SUCCEEDED(hr
) || script_items_count
<= 0)
935 // Temporarily apply composition underlines and selection colors.
936 ApplyCompositionAndSelectionStyles();
938 // Build the list of runs from the script items and ranged styles. Use an
939 // empty color BreakList to avoid breaking runs at color boundaries.
940 BreakList
<SkColor
> empty_colors
;
941 empty_colors
.SetMax(layout_text_length
);
942 internal::StyleIterator
style(empty_colors
, styles());
943 SCRIPT_ITEM
* script_item
= &script_items
[0];
944 const size_t max_run_length
= kMaxGlyphs
/ 2;
945 for (size_t run_break
= 0; run_break
< layout_text_length
;) {
946 internal::TextRun
* run
= new internal::TextRun();
947 run
->range
.set_start(run_break
);
948 run
->font
= font_list().GetPrimaryFont();
949 run
->font_style
= (style
.style(BOLD
) ? Font::BOLD
: 0) |
950 (style
.style(ITALIC
) ? Font::ITALIC
: 0);
951 DeriveFontIfNecessary(run
->font
.GetFontSize(), run
->font
.GetHeight(),
952 run
->font_style
, &run
->font
);
953 run
->strike
= style
.style(STRIKE
);
954 run
->diagonal_strike
= style
.style(DIAGONAL_STRIKE
);
955 run
->underline
= style
.style(UNDERLINE
);
956 run
->script_analysis
= script_item
->a
;
958 // Find the next break and advance the iterators as needed.
959 const size_t script_item_break
= (script_item
+ 1)->iCharPos
;
960 run_break
= std::min(script_item_break
,
961 TextIndexToLayoutIndex(style
.GetRange().end()));
963 // Clamp run lengths to avoid exceeding the maximum supported glyph count.
964 if ((run_break
- run
->range
.start()) > max_run_length
) {
965 run_break
= run
->range
.start() + max_run_length
;
966 if (!IsValidCodePointIndex(layout_text
, run_break
))
970 // Break runs adjacent to character substrings in certain code blocks.
971 // This avoids using their fallback fonts for more characters than needed,
972 // in cases like "\x25B6 Media Title", etc. http://crbug.com/278913
973 if (run_break
> run
->range
.start()) {
975 FindUnusualCharacter(layout_text
, run
->range
.start(), run_break
);
978 DCHECK(IsValidCodePointIndex(layout_text
, run_break
));
980 style
.UpdatePosition(LayoutIndexToTextIndex(run_break
));
981 if (script_item_break
== run_break
)
983 run
->range
.set_end(run_break
);
984 runs_
.push_back(run
);
987 // Undo the temporarily applied composition underlines and selection colors.
988 UndoCompositionAndSelectionStyles();
991 void RenderTextWin::LayoutVisualText() {
992 DCHECK(!runs_
.empty());
995 cached_hdc_
= CreateCompatibleDC(NULL
);
998 // Ensure ascent and descent are not smaller than ones of the font list.
999 // Keep them tall enough to draw often-used characters.
1000 // For example, if a text field contains a Japanese character, which is
1001 // smaller than Latin ones, and then later a Latin one is inserted, this
1002 // ensures that the text baseline does not shift.
1003 int ascent
= font_list().GetBaseline();
1004 int descent
= font_list().GetHeight() - font_list().GetBaseline();
1005 for (size_t i
= 0; i
< runs_
.size(); ++i
) {
1006 internal::TextRun
* run
= runs_
[i
];
1009 ascent
= std::max(ascent
, run
->font
.GetBaseline());
1010 descent
= std::max(descent
,
1011 run
->font
.GetHeight() - run
->font
.GetBaseline());
1013 if (run
->glyph_count
> 0) {
1014 run
->advance_widths
.reset(new int[run
->glyph_count
]);
1015 run
->offsets
.reset(new GOFFSET
[run
->glyph_count
]);
1016 hr
= ScriptPlace(cached_hdc_
,
1020 run
->visible_attributes
.get(),
1021 &(run
->script_analysis
),
1022 run
->advance_widths
.get(),
1024 &(run
->abc_widths
));
1025 DCHECK(SUCCEEDED(hr
));
1029 // Build the array of bidirectional embedding levels.
1030 scoped_ptr
<BYTE
[]> levels(new BYTE
[runs_
.size()]);
1031 for (size_t i
= 0; i
< runs_
.size(); ++i
)
1032 levels
[i
] = runs_
[i
]->script_analysis
.s
.uBidiLevel
;
1034 // Get the maps between visual and logical run indices.
1035 visual_to_logical_
.reset(new int[runs_
.size()]);
1036 logical_to_visual_
.reset(new int[runs_
.size()]);
1037 hr
= ScriptLayout(runs_
.size(),
1039 visual_to_logical_
.get(),
1040 logical_to_visual_
.get());
1041 DCHECK(SUCCEEDED(hr
));
1043 // Precalculate run width information.
1044 size_t preceding_run_widths
= 0;
1045 for (size_t i
= 0; i
< runs_
.size(); ++i
) {
1046 internal::TextRun
* run
= runs_
[visual_to_logical_
[i
]];
1047 run
->preceding_run_widths
= preceding_run_widths
;
1048 const ABC
& abc
= run
->abc_widths
;
1049 run
->width
= abc
.abcA
+ abc
.abcB
+ abc
.abcC
;
1050 preceding_run_widths
+= run
->width
;
1052 string_width_
= preceding_run_widths
;
1055 void RenderTextWin::LayoutTextRun(internal::TextRun
* run
) {
1056 const size_t run_length
= run
->range
.length();
1057 const wchar_t* run_text
= &(GetLayoutText()[run
->range
.start()]);
1058 Font original_font
= run
->font
;
1059 LinkedFontsIterator
fonts(original_font
);
1061 run
->logical_clusters
.reset(new WORD
[run_length
]);
1063 // Try to shape with the first font in the fallback list, which is
1066 fonts
.NextFont(¤t_font
);
1067 int missing_count
= CountCharsWithMissingGlyphs(run
,
1068 ShapeTextRunWithFont(run
, current_font
));
1069 if (missing_count
== 0)
1072 // Keep track of the font that is able to display the greatest number of
1073 // characters for which ScriptShape() returned S_OK. This font will be used
1074 // in the case where no font is able to display the entire run.
1075 int best_partial_font_missing_char_count
= missing_count
;
1076 Font best_partial_font
= current_font
;
1078 // Try to shape with the cached font from previous runs, if any.
1079 std::map
<std::string
, Font
>::const_iterator it
=
1080 successful_substitute_fonts_
.find(original_font
.GetFontName());
1081 if (it
!= successful_substitute_fonts_
.end()) {
1082 current_font
= it
->second
;
1083 missing_count
= CountCharsWithMissingGlyphs(run
,
1084 ShapeTextRunWithFont(run
, current_font
));
1085 if (missing_count
== 0)
1087 if (missing_count
< best_partial_font_missing_char_count
) {
1088 best_partial_font_missing_char_count
= missing_count
;
1089 best_partial_font
= current_font
;
1093 // Try finding a fallback font using a meta file.
1094 // TODO(msw|asvitkine): Support RenderText's font_list()?
1095 if (ChooseFallbackFont(cached_hdc_
, run
->font
, run_text
, run_length
,
1097 missing_count
= CountCharsWithMissingGlyphs(run
,
1098 ShapeTextRunWithFont(run
, current_font
));
1099 if (missing_count
== 0) {
1100 successful_substitute_fonts_
[original_font
.GetFontName()] = current_font
;
1103 if (missing_count
< best_partial_font_missing_char_count
) {
1104 best_partial_font_missing_char_count
= missing_count
;
1105 best_partial_font
= current_font
;
1109 // Try the rest of fonts in the fallback list.
1110 while (fonts
.NextFont(¤t_font
)) {
1111 missing_count
= CountCharsWithMissingGlyphs(run
,
1112 ShapeTextRunWithFont(run
, current_font
));
1113 if (missing_count
== 0) {
1114 successful_substitute_fonts_
[original_font
.GetFontName()] = current_font
;
1117 if (missing_count
< best_partial_font_missing_char_count
) {
1118 best_partial_font_missing_char_count
= missing_count
;
1119 best_partial_font
= current_font
;
1123 // If a font was able to partially display the run, use that now.
1124 if (best_partial_font_missing_char_count
< static_cast<int>(run_length
)) {
1125 // Re-shape the run only if |best_partial_font| differs from the last font.
1126 if (best_partial_font
.GetNativeFont() != run
->font
.GetNativeFont())
1127 ShapeTextRunWithFont(run
, best_partial_font
);
1131 // If no font was able to partially display the run, replace all glyphs
1132 // with |wgDefault| from the original font to ensure to they don't hold
1134 // First, clear the cache and select the original font on the HDC.
1135 ScriptFreeCache(&run
->script_cache
);
1136 run
->font
= original_font
;
1137 SelectObject(cached_hdc_
, run
->font
.GetNativeFont());
1139 // Now, get the font's properties.
1140 SCRIPT_FONTPROPERTIES properties
;
1141 memset(&properties
, 0, sizeof(properties
));
1142 properties
.cBytes
= sizeof(properties
);
1143 HRESULT hr
= ScriptGetFontProperties(cached_hdc_
, &run
->script_cache
,
1146 // The initial values for the "missing" glyph and the space glyph are taken
1147 // from the recommendations section of the OpenType spec:
1148 // https://www.microsoft.com/typography/otspec/recom.htm
1149 WORD missing_glyph
= 0;
1150 WORD space_glyph
= 3;
1152 missing_glyph
= properties
.wgDefault
;
1153 space_glyph
= properties
.wgBlank
;
1156 // Finally, initialize |glyph_count|, |glyphs|, |visible_attributes| and
1157 // |logical_clusters| on the run (since they may not have been set yet).
1158 run
->glyph_count
= run_length
;
1159 memset(run
->visible_attributes
.get(), 0,
1160 run
->glyph_count
* sizeof(SCRIPT_VISATTR
));
1161 for (int i
= 0; i
< run
->glyph_count
; ++i
)
1162 run
->glyphs
[i
] = IsWhitespace(run_text
[i
]) ? space_glyph
: missing_glyph
;
1163 for (size_t i
= 0; i
< run_length
; ++i
) {
1164 run
->logical_clusters
[i
] = run
->script_analysis
.fRTL
?
1165 run_length
- 1 - i
: i
;
1168 // TODO(msw): Don't use SCRIPT_UNDEFINED. Apparently Uniscribe can
1169 // crash on certain surrogate pairs with SCRIPT_UNDEFINED.
1170 // See https://bugzilla.mozilla.org/show_bug.cgi?id=341500
1171 // And http://maxradi.us/documents/uniscribe/
1172 run
->script_analysis
.eScript
= SCRIPT_UNDEFINED
;
1175 HRESULT
RenderTextWin::ShapeTextRunWithFont(internal::TextRun
* run
,
1177 // Update the run's font only if necessary. If the two fonts wrap the same
1178 // PlatformFontWin object, their native fonts will have the same value.
1179 if (run
->font
.GetNativeFont() != font
.GetNativeFont()) {
1180 const int font_size
= run
->font
.GetFontSize();
1181 const int font_height
= run
->font
.GetHeight();
1183 DeriveFontIfNecessary(font_size
, font_height
, run
->font_style
, &run
->font
);
1184 ScriptFreeCache(&run
->script_cache
);
1187 // Select the font desired for glyph generation.
1188 SelectObject(cached_hdc_
, run
->font
.GetNativeFont());
1190 HRESULT hr
= E_OUTOFMEMORY
;
1191 const size_t run_length
= run
->range
.length();
1192 const wchar_t* run_text
= &(GetLayoutText()[run
->range
.start()]);
1193 // Guess the expected number of glyphs from the length of the run.
1194 // MSDN suggests this at http://msdn.microsoft.com/en-us/library/dd368564.aspx
1195 size_t max_glyphs
= static_cast<size_t>(1.5 * run_length
+ 16);
1196 while (hr
== E_OUTOFMEMORY
&& max_glyphs
<= kMaxGlyphs
) {
1197 run
->glyph_count
= 0;
1198 run
->glyphs
.reset(new WORD
[max_glyphs
]);
1199 run
->visible_attributes
.reset(new SCRIPT_VISATTR
[max_glyphs
]);
1200 hr
= ScriptShape(cached_hdc_
, &run
->script_cache
, run_text
, run_length
,
1201 max_glyphs
, &run
->script_analysis
, run
->glyphs
.get(),
1202 run
->logical_clusters
.get(), run
->visible_attributes
.get(),
1204 // Ensure that |kMaxGlyphs| is attempted and the loop terminates afterward.
1205 max_glyphs
= std::max(max_glyphs
+ 1, std::min(max_glyphs
* 2, kMaxGlyphs
));
1210 int RenderTextWin::CountCharsWithMissingGlyphs(internal::TextRun
* run
,
1211 HRESULT shaping_result
) const {
1212 if (shaping_result
!= S_OK
) {
1213 DCHECK_EQ(shaping_result
, USP_E_SCRIPT_NOT_IN_FONT
);
1217 // If |hr| is S_OK, there could still be missing glyphs in the output.
1218 // http://msdn.microsoft.com/en-us/library/windows/desktop/dd368564.aspx
1219 int chars_not_missing_glyphs
= 0;
1220 SCRIPT_FONTPROPERTIES properties
;
1221 memset(&properties
, 0, sizeof(properties
));
1222 properties
.cBytes
= sizeof(properties
);
1223 ScriptGetFontProperties(cached_hdc_
, &run
->script_cache
, &properties
);
1225 const wchar_t* run_text
= &(GetLayoutText()[run
->range
.start()]);
1226 for (size_t char_index
= 0; char_index
< run
->range
.length(); ++char_index
) {
1227 const int glyph_index
= run
->logical_clusters
[char_index
];
1228 DCHECK_GE(glyph_index
, 0);
1229 DCHECK_LT(glyph_index
, run
->glyph_count
);
1231 if (run
->glyphs
[glyph_index
] == properties
.wgDefault
)
1234 // Windows Vista sometimes returns glyphs equal to wgBlank (instead of
1235 // wgDefault), with fZeroWidth set. Treat such cases as having missing
1236 // glyphs if the corresponding character is not whitespace.
1237 // See: http://crbug.com/125629
1238 if (run
->glyphs
[glyph_index
] == properties
.wgBlank
&&
1239 run
->visible_attributes
[glyph_index
].fZeroWidth
&&
1240 !IsWhitespace(run_text
[char_index
]) &&
1241 !IsUnicodeBidiControlCharacter(run_text
[char_index
])) {
1245 ++chars_not_missing_glyphs
;
1248 DCHECK_LE(chars_not_missing_glyphs
, static_cast<int>(run
->range
.length()));
1249 return run
->range
.length() - chars_not_missing_glyphs
;
1252 size_t RenderTextWin::GetRunContainingCaret(const SelectionModel
& caret
) const {
1253 DCHECK(!needs_layout_
);
1254 size_t layout_position
= TextIndexToLayoutIndex(caret
.caret_pos());
1255 LogicalCursorDirection affinity
= caret
.caret_affinity();
1256 for (size_t run
= 0; run
< runs_
.size(); ++run
)
1257 if (RangeContainsCaret(runs_
[run
]->range
, layout_position
, affinity
))
1259 return runs_
.size();
1262 size_t RenderTextWin::GetRunContainingXCoord(int x
) const {
1263 DCHECK(!needs_layout_
);
1264 // Find the text run containing the argument point (assumed already offset).
1265 for (size_t run
= 0; run
< runs_
.size(); ++run
) {
1266 if ((runs_
[run
]->preceding_run_widths
<= x
) &&
1267 ((runs_
[run
]->preceding_run_widths
+ runs_
[run
]->width
) > x
))
1270 return runs_
.size();
1273 SelectionModel
RenderTextWin::FirstSelectionModelInsideRun(
1274 const internal::TextRun
* run
) {
1275 size_t position
= LayoutIndexToTextIndex(run
->range
.start());
1276 position
= IndexOfAdjacentGrapheme(position
, CURSOR_FORWARD
);
1277 return SelectionModel(position
, CURSOR_BACKWARD
);
1280 SelectionModel
RenderTextWin::LastSelectionModelInsideRun(
1281 const internal::TextRun
* run
) {
1282 size_t position
= LayoutIndexToTextIndex(run
->range
.end());
1283 position
= IndexOfAdjacentGrapheme(position
, CURSOR_BACKWARD
);
1284 return SelectionModel(position
, CURSOR_FORWARD
);
1287 RenderText
* RenderText::CreateNativeInstance() {
1288 return new RenderTextWin
;