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/geometry/size_conversions.h"
21 #include "ui/gfx/platform_font_win.h"
22 #include "ui/gfx/utf16_indexing.h"
28 // The maximum length of text supported for Uniscribe layout and display.
29 // This empirically chosen value should prevent major performance degradations.
30 // TODO(msw): Support longer text, partial layout/painting, etc.
31 const size_t kMaxUniscribeTextLength
= 10000;
33 // The initial guess and maximum supported number of runs; arbitrary values.
34 // TODO(msw): Support more runs, determine a better initial guess, etc.
35 const int kGuessRuns
= 100;
36 const size_t kMaxRuns
= 10000;
38 // The maximum number of glyphs per run; ScriptShape fails on larger values.
39 const size_t kMaxGlyphs
= 65535;
41 // Changes |font| to have the specified |font_size| (or |font_height| on Windows
42 // XP) and |font_style| if it is not the case already. Only considers bold and
43 // italic styles, since the underlined style has no effect on glyph shaping.
44 void DeriveFontIfNecessary(int font_size
,
48 const int kStyleMask
= (Font::BOLD
| Font::ITALIC
);
49 const int target_style
= (font_style
& kStyleMask
);
51 // On Windows XP, the font must be resized using |font_height| instead of
52 // |font_size| to match GDI behavior.
53 if (base::win::GetVersion() < base::win::VERSION_VISTA
) {
54 PlatformFontWin
* platform_font
=
55 static_cast<PlatformFontWin
*>(font
->platform_font());
56 *font
= platform_font
->DeriveFontWithHeight(font_height
, target_style
);
60 const int current_style
= (font
->GetStyle() & kStyleMask
);
61 const int current_size
= font
->GetFontSize();
62 if (current_style
!= target_style
|| current_size
!= font_size
)
63 *font
= font
->Derive(font_size
- current_size
, target_style
);
66 // Returns true if |c| is a Unicode BiDi control character.
67 bool IsUnicodeBidiControlCharacter(base::char16 c
) {
68 return c
== base::i18n::kRightToLeftMark
||
69 c
== base::i18n::kLeftToRightMark
||
70 c
== base::i18n::kLeftToRightEmbeddingMark
||
71 c
== base::i18n::kRightToLeftEmbeddingMark
||
72 c
== base::i18n::kPopDirectionalFormatting
||
73 c
== base::i18n::kLeftToRightOverride
||
74 c
== base::i18n::kRightToLeftOverride
;
77 // Returns the corresponding glyph range of the given character range.
78 // |range| is in text-space (0 corresponds to |GetLayoutText()[0]|).
79 // Returned value is in run-space (0 corresponds to the first glyph in the run).
80 Range
CharRangeToGlyphRange(const internal::TextRun
& run
,
82 DCHECK(run
.range
.Contains(range
));
83 DCHECK(!range
.is_reversed());
84 DCHECK(!range
.is_empty());
85 const Range
run_range(range
.start() - run
.range
.start(),
86 range
.end() - run
.range
.start());
88 if (run
.script_analysis
.fRTL
) {
89 result
= Range(run
.logical_clusters
[run_range
.end() - 1],
90 run_range
.start() > 0 ? run
.logical_clusters
[run_range
.start() - 1]
93 result
= Range(run
.logical_clusters
[run_range
.start()],
94 run_range
.end() < run
.range
.length() ?
95 run
.logical_clusters
[run_range
.end()] : run
.glyph_count
);
97 DCHECK(!result
.is_reversed());
98 DCHECK(Range(0, run
.glyph_count
).Contains(result
));
102 // Starting from |start_char|, finds a suitable line break position at or before
103 // |available_width| using word break info from |breaks|. If |empty_line| is
104 // true, this function will not roll back to |start_char| and |*next_char| will
105 // be greater than |start_char| (to avoid constructing empty lines). Returns
106 // whether to skip the line before |*next_char|.
107 // TODO(ckocagil): Do not break ligatures and diacritics.
108 // TextRun::logical_clusters might help.
109 // TODO(ckocagil): We might have to reshape after breaking at ligatures.
110 // See whether resolving the TODO above resolves this too.
111 // TODO(ckocagil): Do not reserve width for whitespace at the end of lines.
112 bool BreakRunAtWidth(const wchar_t* text
,
113 const internal::TextRun
& run
,
114 const BreakList
<size_t>& breaks
,
120 DCHECK(run
.range
.Contains(Range(start_char
, start_char
+ 1)));
121 BreakList
<size_t>::const_iterator word
= breaks
.GetBreak(start_char
);
122 BreakList
<size_t>::const_iterator next_word
= word
+ 1;
123 // Width from |std::max(word->first, start_char)| to the current character.
127 for (size_t i
= start_char
; i
< run
.range
.end(); ++i
) {
128 if (U16_IS_SINGLE(text
[i
]) && text
[i
] == L
'\n') {
133 // |word| holds the word boundary at or before |i|, and |next_word| holds
134 // the word boundary right after |i|. Advance both |word| and |next_word|
135 // when |i| reaches |next_word|.
136 if (next_word
!= breaks
.breaks().end() && i
>= next_word
->first
) {
141 Range glyph_range
= CharRangeToGlyphRange(run
, Range(i
, i
+ 1));
143 for (size_t j
= glyph_range
.start(); j
< glyph_range
.end(); ++j
)
144 char_width
+= run
.advance_widths
[j
];
146 *width
+= char_width
;
147 word_width
+= char_width
;
149 if (*width
> available_width
) {
150 if (!empty_line
|| word_width
< *width
) {
151 // Roll back one word.
152 *width
-= word_width
;
153 *next_char
= std::max(word
->first
, start_char
);
154 } else if (char_width
< *width
) {
155 // Roll back one character.
156 *width
-= char_width
;
159 // Continue from the next character.
167 *next_char
= run
.range
.end();
171 // For segments in the same run, checks the continuity and order of |x_range|
172 // and |char_range| fields.
173 void CheckLineIntegrity(const std::vector
<internal::Line
>& lines
,
174 const ScopedVector
<internal::TextRun
>& runs
) {
175 size_t previous_segment_line
= 0;
176 const internal::LineSegment
* previous_segment
= NULL
;
178 for (size_t i
= 0; i
< lines
.size(); ++i
) {
179 for (size_t j
= 0; j
< lines
[i
].segments
.size(); ++j
) {
180 const internal::LineSegment
* segment
= &lines
[i
].segments
[j
];
181 internal::TextRun
* run
= runs
[segment
->run
];
183 if (!previous_segment
) {
184 previous_segment
= segment
;
185 } else if (runs
[previous_segment
->run
] != run
) {
186 previous_segment
= NULL
;
188 DCHECK_EQ(previous_segment
->char_range
.end(),
189 segment
->char_range
.start());
190 if (!run
->script_analysis
.fRTL
) {
191 DCHECK_EQ(previous_segment
->x_range
.end(), segment
->x_range
.start());
193 DCHECK_EQ(segment
->x_range
.end(), previous_segment
->x_range
.start());
196 previous_segment
= segment
;
197 previous_segment_line
= i
;
203 // Returns true if characters of |block_code| may trigger font fallback.
204 bool IsUnusualBlockCode(const UBlockCode block_code
) {
205 return block_code
== UBLOCK_GEOMETRIC_SHAPES
||
206 block_code
== UBLOCK_MISCELLANEOUS_SYMBOLS
;
209 // Returns the index of the first unusual character after a usual character or
210 // vice versa. Unusual characters are defined by |IsUnusualBlockCode|.
211 size_t FindUnusualCharacter(const base::string16
& text
,
214 const int32 run_length
= static_cast<int32
>(run_break
- run_start
);
215 base::i18n::UTF16CharIterator
iter(text
.c_str() + run_start
,
217 const UBlockCode first_block_code
= ublock_getCode(iter
.get());
218 const bool first_block_unusual
= IsUnusualBlockCode(first_block_code
);
219 while (iter
.Advance() && iter
.array_pos() < run_length
) {
220 const UBlockCode current_block_code
= ublock_getCode(iter
.get());
221 if (current_block_code
!= first_block_code
&&
222 (first_block_unusual
|| IsUnusualBlockCode(current_block_code
))) {
223 return run_start
+ iter
.array_pos();
236 diagonal_strike(false),
239 preceding_run_widths(0),
242 memset(&script_analysis
, 0, sizeof(script_analysis
));
243 memset(&abc_widths
, 0, sizeof(abc_widths
));
246 TextRun::~TextRun() {
247 ScriptFreeCache(&script_cache
);
250 // Returns the X coordinate of the leading or |trailing| edge of the glyph
251 // starting at |index|, relative to the left of the text (not the view).
252 int GetGlyphXBoundary(const internal::TextRun
* run
,
255 DCHECK_GE(index
, run
->range
.start());
256 DCHECK_LT(index
, run
->range
.end() + (trailing
? 0 : 1));
258 HRESULT hr
= ScriptCPtoX(
259 index
- run
->range
.start(),
263 run
->logical_clusters
.get(),
264 run
->visible_attributes
.get(),
265 run
->advance_widths
.get(),
266 &run
->script_analysis
,
268 DCHECK(SUCCEEDED(hr
));
269 return run
->preceding_run_widths
+ x
;
272 // Internal class to generate Line structures. If |multiline| is true, the text
273 // is broken into lines at |words| boundaries such that each line is no longer
274 // than |max_width|. If |multiline| is false, only outputs a single Line from
275 // the given runs. |min_baseline| and |min_height| are the minimum baseline and
276 // height for each line.
277 // TODO(ckocagil): Expose the interface of this class in the header and test
278 // this class directly.
281 LineBreaker(int max_width
,
286 const BreakList
<size_t>* words
,
287 const ScopedVector
<TextRun
>& runs
)
288 : max_width_(max_width
),
289 min_baseline_(min_baseline
),
290 min_height_(min_height
),
291 multiline_(multiline
),
302 // Breaks the run at given |run_index| into Line structs.
303 void AddRun(int run_index
) {
304 const TextRun
* run
= runs_
[run_index
];
305 bool run_fits
= !multiline_
;
306 if (multiline_
&& line_x_
+ run
->width
<= max_width_
) {
307 DCHECK(!run
->range
.is_empty());
308 const wchar_t first_char
= text_
[run
->range
.start()];
309 // Uniscribe always puts newline characters in their own runs.
310 if (!U16_IS_SINGLE(first_char
) || first_char
!= L
'\n')
317 AddSegment(run_index
, run
->range
, run
->width
);
320 // Finishes line breaking and outputs the results. Can be called at most once.
321 void Finalize(std::vector
<Line
>* lines
, Size
* size
) {
322 DCHECK(!lines_
.empty());
323 // Add an empty line to finish the line size calculation and remove it.
331 // A (line index, segment index) pair that specifies a segment in |lines_|.
332 typedef std::pair
<size_t, size_t> SegmentHandle
;
334 LineSegment
* SegmentFromHandle(const SegmentHandle
& handle
) {
335 return &lines_
[handle
.first
].segments
[handle
.second
];
338 // Breaks a run into segments that fit in the last line in |lines_| and adds
339 // them. Adds a new Line to the back of |lines_| whenever a new segment can't
340 // be added without the Line's width exceeding |max_width_|.
341 void BreakRun(int run_index
) {
343 const TextRun
* const run
= runs_
[run_index
];
345 size_t next_char
= run
->range
.start();
347 // Break the run until it fits the current line.
348 while (next_char
< run
->range
.end()) {
349 const size_t current_char
= next_char
;
350 const bool skip_line
= BreakRunAtWidth(text_
, *run
, *words_
, current_char
,
351 max_width_
- line_x_
, line_x_
== 0, &width
, &next_char
);
352 AddSegment(run_index
, Range(current_char
, next_char
), width
);
358 // RTL runs are broken in logical order but displayed in visual order. To find
359 // the text-space coordinate (where it would fall in a single-line text)
360 // |x_range| of RTL segments, segment widths are applied in reverse order.
361 // e.g. {[5, 10], [10, 40]} will become {[35, 40], [5, 35]}.
362 void UpdateRTLSegmentRanges() {
363 if (rtl_segments_
.empty())
365 int x
= SegmentFromHandle(rtl_segments_
[0])->x_range
.start();
366 for (size_t i
= rtl_segments_
.size(); i
> 0; --i
) {
367 LineSegment
* segment
= SegmentFromHandle(rtl_segments_
[i
- 1]);
368 const size_t segment_width
= segment
->x_range
.length();
369 segment
->x_range
= Range(x
, x
+ segment_width
);
372 rtl_segments_
.clear();
375 // Finishes the size calculations of the last Line in |lines_|. Adds a new
376 // Line to the back of |lines_|.
378 if (!lines_
.empty()) {
379 Line
* line
= &lines_
.back();
380 // TODO(ckocagil): Determine optimal multiline height behavior.
381 if (line_ascent_
+ line_descent_
== 0) {
382 line_ascent_
= min_baseline_
;
383 line_descent_
= min_height_
- min_baseline_
;
385 // Set the single-line mode Line's metrics to be at least
386 // |RenderText::font_list()| to not break the current single-line code.
387 line_ascent_
= std::max(line_ascent_
, min_baseline_
);
388 line_descent_
= std::max(line_descent_
, min_height_
- min_baseline_
);
390 line
->baseline
= line_ascent_
;
391 line
->size
.set_height(line_ascent_
+ line_descent_
);
392 line
->preceding_heights
= total_size_
.height();
393 const Size
line_size(ToCeiledSize(line
->size
));
394 total_size_
.set_height(total_size_
.height() + line_size
.height());
395 total_size_
.set_width(std::max(total_size_
.width(), line_size
.width()));
400 lines_
.push_back(Line());
403 // Adds a new segment with the given properties to |lines_.back()|.
404 void AddSegment(int run_index
, Range char_range
, int width
) {
405 if (char_range
.is_empty()) {
409 const TextRun
* run
= runs_
[run_index
];
410 line_ascent_
= std::max(line_ascent_
, run
->font
.GetBaseline());
411 line_descent_
= std::max(line_descent_
,
412 run
->font
.GetHeight() - run
->font
.GetBaseline());
415 segment
.run
= run_index
;
416 segment
.char_range
= char_range
;
417 segment
.x_range
= Range(text_x_
, text_x_
+ width
);
419 Line
* line
= &lines_
.back();
420 line
->segments
.push_back(segment
);
421 line
->size
.set_width(line
->size
.width() + segment
.x_range
.length());
422 if (run
->script_analysis
.fRTL
) {
423 rtl_segments_
.push_back(SegmentHandle(lines_
.size() - 1,
424 line
->segments
.size() - 1));
425 // If this is the last segment of an RTL run, reprocess the text-space x
426 // ranges of all segments from the run.
427 if (char_range
.end() == run
->range
.end())
428 UpdateRTLSegmentRanges();
434 const int max_width_
;
435 const int min_baseline_
;
436 const int min_height_
;
437 const bool multiline_
;
438 const wchar_t* text_
;
439 const BreakList
<size_t>* const words_
;
440 const ScopedVector
<TextRun
>& runs_
;
442 // Stores the resulting lines.
443 std::vector
<Line
> lines_
;
445 // Text space and line space x coordinates of the next segment to be added.
449 // Size of the multiline text, not including the currently processed line.
452 // Ascent and descent values of the current line, |lines_.back()|.
456 // The current RTL run segments, to be applied by |UpdateRTLSegmentRanges()|.
457 std::vector
<SegmentHandle
> rtl_segments_
;
459 DISALLOW_COPY_AND_ASSIGN(LineBreaker
);
462 } // namespace internal
465 HDC
RenderTextWin::cached_hdc_
= NULL
;
468 std::map
<std::string
, Font
> RenderTextWin::successful_substitute_fonts_
;
470 RenderTextWin::RenderTextWin() : RenderText(), needs_layout_(false) {
471 set_truncate_length(kMaxUniscribeTextLength
);
472 memset(&script_control_
, 0, sizeof(script_control_
));
473 memset(&script_state_
, 0, sizeof(script_state_
));
474 MoveCursorTo(EdgeSelectionModel(CURSOR_LEFT
));
477 RenderTextWin::~RenderTextWin() {}
479 Size
RenderTextWin::GetStringSize() {
481 return multiline_string_size_
;
484 SelectionModel
RenderTextWin::FindCursorPosition(const Point
& point
) {
486 return SelectionModel();
489 // Find the run that contains the point and adjust the argument location.
490 int x
= ToTextPoint(point
).x();
491 size_t run_index
= GetRunContainingXCoord(x
);
492 if (run_index
>= runs_
.size())
493 return EdgeSelectionModel((x
< 0) ? CURSOR_LEFT
: CURSOR_RIGHT
);
494 internal::TextRun
* run
= runs_
[run_index
];
496 int position
= 0, trailing
= 0;
497 HRESULT hr
= ScriptXtoCP(x
- run
->preceding_run_widths
,
500 run
->logical_clusters
.get(),
501 run
->visible_attributes
.get(),
502 run
->advance_widths
.get(),
503 &(run
->script_analysis
),
506 DCHECK(SUCCEEDED(hr
));
507 DCHECK_GE(trailing
, 0);
508 position
+= run
->range
.start();
509 const size_t cursor
= LayoutIndexToTextIndex(position
+ trailing
);
510 DCHECK_LE(cursor
, text().length());
511 return SelectionModel(cursor
, trailing
? CURSOR_BACKWARD
: CURSOR_FORWARD
);
514 std::vector
<RenderText::FontSpan
> RenderTextWin::GetFontSpansForTesting() {
517 std::vector
<RenderText::FontSpan
> spans
;
518 for (size_t i
= 0; i
< runs_
.size(); ++i
) {
519 spans
.push_back(RenderText::FontSpan(runs_
[i
]->font
,
520 Range(LayoutIndexToTextIndex(runs_
[i
]->range
.start()),
521 LayoutIndexToTextIndex(runs_
[i
]->range
.end()))));
527 int RenderTextWin::GetLayoutTextBaseline() {
529 return lines()[0].baseline
;
532 SelectionModel
RenderTextWin::AdjacentCharSelectionModel(
533 const SelectionModel
& selection
,
534 VisualCursorDirection direction
) {
535 DCHECK(!needs_layout_
);
536 internal::TextRun
* run
;
537 size_t run_index
= GetRunContainingCaret(selection
);
538 if (run_index
>= runs_
.size()) {
539 // The cursor is not in any run: we're at the visual and logical edge.
540 SelectionModel edge
= EdgeSelectionModel(direction
);
541 if (edge
.caret_pos() == selection
.caret_pos())
543 int visual_index
= (direction
== CURSOR_RIGHT
) ? 0 : runs_
.size() - 1;
544 run
= runs_
[visual_to_logical_
[visual_index
]];
546 // If the cursor is moving within the current run, just move it by one
547 // grapheme in the appropriate direction.
548 run
= runs_
[run_index
];
549 size_t caret
= selection
.caret_pos();
550 bool forward_motion
=
551 run
->script_analysis
.fRTL
== (direction
== CURSOR_LEFT
);
552 if (forward_motion
) {
553 if (caret
< LayoutIndexToTextIndex(run
->range
.end())) {
554 caret
= IndexOfAdjacentGrapheme(caret
, CURSOR_FORWARD
);
555 return SelectionModel(caret
, CURSOR_BACKWARD
);
558 if (caret
> LayoutIndexToTextIndex(run
->range
.start())) {
559 caret
= IndexOfAdjacentGrapheme(caret
, CURSOR_BACKWARD
);
560 return SelectionModel(caret
, CURSOR_FORWARD
);
563 // The cursor is at the edge of a run; move to the visually adjacent run.
564 int visual_index
= logical_to_visual_
[run_index
];
565 visual_index
+= (direction
== CURSOR_LEFT
) ? -1 : 1;
566 if (visual_index
< 0 || visual_index
>= static_cast<int>(runs_
.size()))
567 return EdgeSelectionModel(direction
);
568 run
= runs_
[visual_to_logical_
[visual_index
]];
570 bool forward_motion
= run
->script_analysis
.fRTL
== (direction
== CURSOR_LEFT
);
571 return forward_motion
? FirstSelectionModelInsideRun(run
) :
572 LastSelectionModelInsideRun(run
);
575 // TODO(msw): Implement word breaking for Windows.
576 SelectionModel
RenderTextWin::AdjacentWordSelectionModel(
577 const SelectionModel
& selection
,
578 VisualCursorDirection direction
) {
580 return EdgeSelectionModel(direction
);
582 base::i18n::BreakIterator
iter(text(), base::i18n::BreakIterator::BREAK_WORD
);
583 bool success
= iter
.Init();
589 if (direction
== CURSOR_RIGHT
) {
590 pos
= std::min(selection
.caret_pos() + 1, text().length());
591 while (iter
.Advance()) {
593 if (iter
.IsWord() && pos
> selection
.caret_pos())
596 } else { // direction == CURSOR_LEFT
597 // Notes: We always iterate words from the beginning.
598 // This is probably fast enough for our usage, but we may
599 // want to modify WordIterator so that it can start from the
600 // middle of string and advance backwards.
601 pos
= std::max
<int>(selection
.caret_pos() - 1, 0);
602 while (iter
.Advance()) {
604 size_t begin
= iter
.pos() - iter
.GetString().length();
605 if (begin
== selection
.caret_pos()) {
606 // The cursor is at the beginning of a word.
607 // Move to previous word.
609 } else if (iter
.pos() >= selection
.caret_pos()) {
610 // The cursor is in the middle or at the end of a word.
611 // Move to the top of current word.
615 pos
= iter
.pos() - iter
.GetString().length();
620 return SelectionModel(pos
, CURSOR_FORWARD
);
623 Range
RenderTextWin::GetGlyphBounds(size_t index
) {
625 const size_t run_index
=
626 GetRunContainingCaret(SelectionModel(index
, CURSOR_FORWARD
));
627 // Return edge bounds if the index is invalid or beyond the layout text size.
628 if (run_index
>= runs_
.size())
629 return Range(string_width_
);
630 internal::TextRun
* run
= runs_
[run_index
];
631 const size_t layout_index
= TextIndexToLayoutIndex(index
);
632 return Range(GetGlyphXBoundary(run
, layout_index
, false),
633 GetGlyphXBoundary(run
, layout_index
, true));
636 std::vector
<Rect
> RenderTextWin::GetSubstringBounds(const Range
& range
) {
637 DCHECK(!needs_layout_
);
638 DCHECK(Range(0, text().length()).Contains(range
));
639 Range
layout_range(TextIndexToLayoutIndex(range
.start()),
640 TextIndexToLayoutIndex(range
.end()));
641 DCHECK(Range(0, GetLayoutText().length()).Contains(layout_range
));
643 std::vector
<Rect
> rects
;
644 if (layout_range
.is_empty())
646 std::vector
<Range
> bounds
;
648 // Add a Range for each run/selection intersection.
649 // TODO(msw): The bounds should probably not always be leading the range ends.
650 for (size_t i
= 0; i
< runs_
.size(); ++i
) {
651 const internal::TextRun
* run
= runs_
[visual_to_logical_
[i
]];
652 Range intersection
= run
->range
.Intersect(layout_range
);
653 if (intersection
.IsValid()) {
654 DCHECK(!intersection
.is_reversed());
655 Range
range_x(GetGlyphXBoundary(run
, intersection
.start(), false),
656 GetGlyphXBoundary(run
, intersection
.end(), false));
657 if (range_x
.is_empty())
659 range_x
= Range(range_x
.GetMin(), range_x
.GetMax());
660 // Union this with the last range if they're adjacent.
661 DCHECK(bounds
.empty() || bounds
.back().GetMax() <= range_x
.GetMin());
662 if (!bounds
.empty() && bounds
.back().GetMax() == range_x
.GetMin()) {
663 range_x
= Range(bounds
.back().GetMin(), range_x
.GetMax());
666 bounds
.push_back(range_x
);
669 for (size_t i
= 0; i
< bounds
.size(); ++i
) {
670 std::vector
<Rect
> current_rects
= TextBoundsToViewBounds(bounds
[i
]);
671 rects
.insert(rects
.end(), current_rects
.begin(), current_rects
.end());
676 size_t RenderTextWin::TextIndexToLayoutIndex(size_t index
) const {
677 DCHECK_LE(index
, text().length());
678 ptrdiff_t i
= obscured() ? UTF16IndexToOffset(text(), 0, index
) : index
;
680 // Clamp layout indices to the length of the text actually used for layout.
681 return std::min
<size_t>(GetLayoutText().length(), i
);
684 size_t RenderTextWin::LayoutIndexToTextIndex(size_t index
) const {
688 DCHECK_LE(index
, GetLayoutText().length());
689 const size_t text_index
= UTF16OffsetToIndex(text(), 0, index
);
690 DCHECK_LE(text_index
, text().length());
694 bool RenderTextWin::IsValidCursorIndex(size_t index
) {
695 if (index
== 0 || index
== text().length())
697 if (!IsValidLogicalIndex(index
))
700 // Disallow indices amid multi-character graphemes by checking glyph bounds.
701 // These characters are not surrogate-pairs, but may yield a single glyph:
702 // \x0915\x093f - (ki) - one of many Devanagari biconsonantal conjuncts.
703 // \x0e08\x0e33 - (cho chan + sara am) - a Thai consonant and vowel pair.
704 return GetGlyphBounds(index
) != GetGlyphBounds(index
- 1);
707 void RenderTextWin::ResetLayout() {
708 // Layout is performed lazily as needed for drawing/metrics.
709 needs_layout_
= true;
712 void RenderTextWin::EnsureLayout() {
714 // TODO(msw): Skip complex processing if ScriptIsComplex returns false.
715 ItemizeLogicalText();
718 needs_layout_
= false;
719 std::vector
<internal::Line
> lines
;
723 // Compute lines if they're not valid. This is separate from the layout steps
724 // above to avoid text layout and shaping when we resize |display_rect_|.
725 if (lines().empty()) {
726 DCHECK(!needs_layout_
);
727 std::vector
<internal::Line
> lines
;
728 internal::LineBreaker
line_breaker(display_rect().width() - 1,
729 font_list().GetBaseline(),
730 font_list().GetHeight(), multiline(),
731 GetLayoutText().c_str(),
732 multiline() ? &GetLineBreaks() : NULL
,
734 for (size_t i
= 0; i
< runs_
.size(); ++i
)
735 line_breaker
.AddRun(visual_to_logical_
[i
]);
736 line_breaker
.Finalize(&lines
, &multiline_string_size_
);
737 DCHECK(!lines
.empty());
739 CheckLineIntegrity(lines
, runs_
);
745 void RenderTextWin::DrawVisualText(Canvas
* canvas
) {
746 DCHECK(!needs_layout_
);
747 DCHECK(!lines().empty());
749 std::vector
<SkPoint
> pos
;
751 internal::SkiaTextRenderer
renderer(canvas
);
752 ApplyFadeEffects(&renderer
);
753 ApplyTextShadows(&renderer
);
755 renderer
.SetFontRenderParams(
756 font_list().GetPrimaryFont().GetFontRenderParams(),
757 background_is_transparent());
759 ApplyCompositionAndSelectionStyles();
761 for (size_t i
= 0; i
< lines().size(); ++i
) {
762 const internal::Line
& line
= lines()[i
];
763 const Vector2d line_offset
= GetLineOffset(i
);
765 // Skip painting empty lines or lines outside the display rect area.
766 if (!display_rect().Intersects(Rect(PointAtOffsetFromOrigin(line_offset
),
767 ToCeiledSize(line
.size
))))
770 const Vector2d text_offset
= line_offset
+ Vector2d(0, line
.baseline
);
771 int preceding_segment_widths
= 0;
773 for (size_t j
= 0; j
< line
.segments
.size(); ++j
) {
774 const internal::LineSegment
* segment
= &line
.segments
[j
];
775 const int segment_width
= segment
->x_range
.length();
776 const internal::TextRun
* run
= runs_
[segment
->run
];
777 DCHECK(!segment
->char_range
.is_empty());
778 DCHECK(run
->range
.Contains(segment
->char_range
));
779 Range glyph_range
= CharRangeToGlyphRange(*run
, segment
->char_range
);
780 DCHECK(!glyph_range
.is_empty());
781 // Skip painting segments outside the display rect area.
783 const Rect
segment_bounds(PointAtOffsetFromOrigin(line_offset
) +
784 Vector2d(preceding_segment_widths
, 0),
785 Size(segment_width
, line
.size
.height()));
786 if (!display_rect().Intersects(segment_bounds
)) {
787 preceding_segment_widths
+= segment_width
;
792 // |pos| contains the positions of glyphs. An extra terminal |pos| entry
793 // is added to simplify width calculations.
794 int segment_x
= preceding_segment_widths
;
795 pos
.resize(glyph_range
.length() + 1);
796 for (size_t k
= glyph_range
.start(); k
< glyph_range
.end(); ++k
) {
797 pos
[k
- glyph_range
.start()].set(
798 SkIntToScalar(text_offset
.x() + run
->offsets
[k
].du
+ segment_x
),
799 SkIntToScalar(text_offset
.y() - run
->offsets
[k
].dv
));
800 segment_x
+= run
->advance_widths
[k
];
802 pos
.back().set(SkIntToScalar(text_offset
.x() + segment_x
),
803 SkIntToScalar(text_offset
.y()));
805 renderer
.SetTextSize(run
->font
.GetFontSize());
806 renderer
.SetFontFamilyWithStyle(run
->font
.GetFontName(), run
->font_style
);
808 for (BreakList
<SkColor
>::const_iterator it
=
809 colors().GetBreak(segment
->char_range
.start());
810 it
!= colors().breaks().end() &&
811 it
->first
< segment
->char_range
.end();
813 const Range intersection
=
814 colors().GetRange(it
).Intersect(segment
->char_range
);
815 const Range colored_glyphs
= CharRangeToGlyphRange(*run
, intersection
);
816 // The range may be empty if a portion of a multi-character grapheme is
817 // selected, yielding two colors for a single glyph. For now, this just
818 // paints the glyph with a single style, but it should paint it twice,
819 // clipped according to selection bounds. See http://crbug.com/366786
820 if (colored_glyphs
.is_empty())
822 DCHECK(glyph_range
.Contains(colored_glyphs
));
823 const SkPoint
& start_pos
=
824 pos
[colored_glyphs
.start() - glyph_range
.start()];
825 const SkPoint
& end_pos
=
826 pos
[colored_glyphs
.end() - glyph_range
.start()];
828 renderer
.SetForegroundColor(it
->second
);
829 renderer
.DrawPosText(&start_pos
, &run
->glyphs
[colored_glyphs
.start()],
830 colored_glyphs
.length());
831 renderer
.DrawDecorations(start_pos
.x(), text_offset
.y(),
832 SkScalarCeilToInt(end_pos
.x() - start_pos
.x()),
833 run
->underline
, run
->strike
,
834 run
->diagonal_strike
);
837 preceding_segment_widths
+= segment_width
;
840 renderer
.EndDiagonalStrike();
843 UndoCompositionAndSelectionStyles();
846 void RenderTextWin::ItemizeLogicalText() {
849 multiline_string_size_
= Size();
851 // Set Uniscribe's base text direction.
852 script_state_
.uBidiLevel
=
853 (GetTextDirection() == base::i18n::RIGHT_TO_LEFT
) ? 1 : 0;
855 const base::string16
& layout_text
= GetLayoutText();
856 if (layout_text
.empty())
859 HRESULT hr
= E_OUTOFMEMORY
;
860 int script_items_count
= 0;
861 std::vector
<SCRIPT_ITEM
> script_items
;
862 const size_t layout_text_length
= layout_text
.length();
863 // Ensure that |kMaxRuns| is attempted and the loop terminates afterward.
864 for (size_t runs
= kGuessRuns
; hr
== E_OUTOFMEMORY
&& runs
<= kMaxRuns
;
865 runs
= std::max(runs
+ 1, std::min(runs
* 2, kMaxRuns
))) {
866 // Derive the array of Uniscribe script items from the logical text.
867 // ScriptItemize always adds a terminal array item so that the length of
868 // the last item can be derived from the terminal SCRIPT_ITEM::iCharPos.
869 script_items
.resize(runs
);
870 hr
= ScriptItemize(layout_text
.c_str(), layout_text_length
, runs
- 1,
871 &script_control_
, &script_state_
, &script_items
[0],
872 &script_items_count
);
874 DCHECK(SUCCEEDED(hr
));
875 if (!SUCCEEDED(hr
) || script_items_count
<= 0)
878 // Temporarily apply composition underlines and selection colors.
879 ApplyCompositionAndSelectionStyles();
881 // Build the list of runs from the script items and ranged styles. Use an
882 // empty color BreakList to avoid breaking runs at color boundaries.
883 BreakList
<SkColor
> empty_colors
;
884 empty_colors
.SetMax(layout_text_length
);
885 internal::StyleIterator
style(empty_colors
, styles());
886 SCRIPT_ITEM
* script_item
= &script_items
[0];
887 const size_t max_run_length
= kMaxGlyphs
/ 2;
888 for (size_t run_break
= 0; run_break
< layout_text_length
;) {
889 internal::TextRun
* run
= new internal::TextRun();
890 run
->range
.set_start(run_break
);
891 run
->font
= font_list().GetPrimaryFont();
892 run
->font_style
= (style
.style(BOLD
) ? Font::BOLD
: 0) |
893 (style
.style(ITALIC
) ? Font::ITALIC
: 0);
894 DeriveFontIfNecessary(run
->font
.GetFontSize(), run
->font
.GetHeight(),
895 run
->font_style
, &run
->font
);
896 run
->strike
= style
.style(STRIKE
);
897 run
->diagonal_strike
= style
.style(DIAGONAL_STRIKE
);
898 run
->underline
= style
.style(UNDERLINE
);
899 run
->script_analysis
= script_item
->a
;
901 // Find the next break and advance the iterators as needed.
902 const size_t script_item_break
= (script_item
+ 1)->iCharPos
;
903 run_break
= std::min(script_item_break
,
904 TextIndexToLayoutIndex(style
.GetRange().end()));
906 // Clamp run lengths to avoid exceeding the maximum supported glyph count.
907 if ((run_break
- run
->range
.start()) > max_run_length
) {
908 run_break
= run
->range
.start() + max_run_length
;
909 if (!IsValidCodePointIndex(layout_text
, run_break
))
913 // Break runs adjacent to character substrings in certain code blocks.
914 // This avoids using their fallback fonts for more characters than needed,
915 // in cases like "\x25B6 Media Title", etc. http://crbug.com/278913
916 if (run_break
> run
->range
.start()) {
918 FindUnusualCharacter(layout_text
, run
->range
.start(), run_break
);
921 DCHECK(IsValidCodePointIndex(layout_text
, run_break
));
923 style
.UpdatePosition(LayoutIndexToTextIndex(run_break
));
924 if (script_item_break
== run_break
)
926 run
->range
.set_end(run_break
);
927 runs_
.push_back(run
);
930 // Undo the temporarily applied composition underlines and selection colors.
931 UndoCompositionAndSelectionStyles();
934 void RenderTextWin::LayoutVisualText() {
935 DCHECK(!runs_
.empty());
938 cached_hdc_
= CreateCompatibleDC(NULL
);
941 // Ensure ascent and descent are not smaller than ones of the font list.
942 // Keep them tall enough to draw often-used characters.
943 // For example, if a text field contains a Japanese character, which is
944 // smaller than Latin ones, and then later a Latin one is inserted, this
945 // ensures that the text baseline does not shift.
946 int ascent
= font_list().GetBaseline();
947 int descent
= font_list().GetHeight() - font_list().GetBaseline();
948 for (size_t i
= 0; i
< runs_
.size(); ++i
) {
949 internal::TextRun
* run
= runs_
[i
];
952 ascent
= std::max(ascent
, run
->font
.GetBaseline());
953 descent
= std::max(descent
,
954 run
->font
.GetHeight() - run
->font
.GetBaseline());
956 if (run
->glyph_count
> 0) {
957 run
->advance_widths
.reset(new int[run
->glyph_count
]);
958 run
->offsets
.reset(new GOFFSET
[run
->glyph_count
]);
959 hr
= ScriptPlace(cached_hdc_
,
963 run
->visible_attributes
.get(),
964 &(run
->script_analysis
),
965 run
->advance_widths
.get(),
968 DCHECK(SUCCEEDED(hr
));
972 // Build the array of bidirectional embedding levels.
973 scoped_ptr
<BYTE
[]> levels(new BYTE
[runs_
.size()]);
974 for (size_t i
= 0; i
< runs_
.size(); ++i
)
975 levels
[i
] = runs_
[i
]->script_analysis
.s
.uBidiLevel
;
977 // Get the maps between visual and logical run indices.
978 visual_to_logical_
.reset(new int[runs_
.size()]);
979 logical_to_visual_
.reset(new int[runs_
.size()]);
980 hr
= ScriptLayout(runs_
.size(),
982 visual_to_logical_
.get(),
983 logical_to_visual_
.get());
984 DCHECK(SUCCEEDED(hr
));
986 // Precalculate run width information.
987 size_t preceding_run_widths
= 0;
988 for (size_t i
= 0; i
< runs_
.size(); ++i
) {
989 internal::TextRun
* run
= runs_
[visual_to_logical_
[i
]];
990 run
->preceding_run_widths
= preceding_run_widths
;
991 const ABC
& abc
= run
->abc_widths
;
992 run
->width
= abc
.abcA
+ abc
.abcB
+ abc
.abcC
;
993 preceding_run_widths
+= run
->width
;
995 string_width_
= preceding_run_widths
;
998 void RenderTextWin::LayoutTextRun(internal::TextRun
* run
) {
999 const size_t run_length
= run
->range
.length();
1000 const wchar_t* run_text
= &(GetLayoutText()[run
->range
.start()]);
1001 Font original_font
= run
->font
;
1003 run
->logical_clusters
.reset(new WORD
[run_length
]);
1005 // Try shaping with |original_font|.
1006 Font current_font
= original_font
;
1007 int missing_count
= CountCharsWithMissingGlyphs(run
,
1008 ShapeTextRunWithFont(run
, current_font
));
1009 if (missing_count
== 0)
1012 // Keep track of the font that is able to display the greatest number of
1013 // characters for which ScriptShape() returned S_OK. This font will be used
1014 // in the case where no font is able to display the entire run.
1015 int best_partial_font_missing_char_count
= missing_count
;
1016 Font best_partial_font
= current_font
;
1018 // Try to shape with the cached font from previous runs, if any.
1019 std::map
<std::string
, Font
>::const_iterator it
=
1020 successful_substitute_fonts_
.find(original_font
.GetFontName());
1021 if (it
!= successful_substitute_fonts_
.end()) {
1022 current_font
= it
->second
;
1023 missing_count
= CountCharsWithMissingGlyphs(run
,
1024 ShapeTextRunWithFont(run
, current_font
));
1025 if (missing_count
== 0)
1027 if (missing_count
< best_partial_font_missing_char_count
) {
1028 best_partial_font_missing_char_count
= missing_count
;
1029 best_partial_font
= current_font
;
1033 // Try finding a fallback font using a meta file.
1034 // TODO(msw|asvitkine): Support RenderText's font_list()?
1035 if (GetUniscribeFallbackFont(original_font
, run_text
, run_length
,
1037 missing_count
= CountCharsWithMissingGlyphs(run
,
1038 ShapeTextRunWithFont(run
, current_font
));
1039 if (missing_count
== 0) {
1040 successful_substitute_fonts_
[original_font
.GetFontName()] = current_font
;
1043 if (missing_count
< best_partial_font_missing_char_count
) {
1044 best_partial_font_missing_char_count
= missing_count
;
1045 best_partial_font
= current_font
;
1049 // Try fonts in the fallback list except the first, which is |original_font|.
1050 std::vector
<std::string
> fonts
=
1051 GetFallbackFontFamilies(original_font
.GetFontName());
1052 for (size_t i
= 1; i
< fonts
.size(); ++i
) {
1053 missing_count
= CountCharsWithMissingGlyphs(run
,
1054 ShapeTextRunWithFont(run
, Font(fonts
[i
], original_font
.GetFontSize())));
1055 if (missing_count
== 0) {
1056 successful_substitute_fonts_
[original_font
.GetFontName()] = current_font
;
1059 if (missing_count
< best_partial_font_missing_char_count
) {
1060 best_partial_font_missing_char_count
= missing_count
;
1061 best_partial_font
= current_font
;
1065 // If a font was able to partially display the run, use that now.
1066 if (best_partial_font_missing_char_count
< static_cast<int>(run_length
)) {
1067 // Re-shape the run only if |best_partial_font| differs from the last font.
1068 if (best_partial_font
.GetNativeFont() != run
->font
.GetNativeFont())
1069 ShapeTextRunWithFont(run
, best_partial_font
);
1073 // If no font was able to partially display the run, replace all glyphs
1074 // with |wgDefault| from the original font to ensure to they don't hold
1076 // First, clear the cache and select the original font on the HDC.
1077 ScriptFreeCache(&run
->script_cache
);
1078 run
->font
= original_font
;
1079 SelectObject(cached_hdc_
, run
->font
.GetNativeFont());
1081 // Now, get the font's properties.
1082 SCRIPT_FONTPROPERTIES properties
;
1083 memset(&properties
, 0, sizeof(properties
));
1084 properties
.cBytes
= sizeof(properties
);
1085 HRESULT hr
= ScriptGetFontProperties(cached_hdc_
, &run
->script_cache
,
1088 // The initial values for the "missing" glyph and the space glyph are taken
1089 // from the recommendations section of the OpenType spec:
1090 // https://www.microsoft.com/typography/otspec/recom.htm
1091 WORD missing_glyph
= 0;
1092 WORD space_glyph
= 3;
1094 missing_glyph
= properties
.wgDefault
;
1095 space_glyph
= properties
.wgBlank
;
1098 // Finally, initialize |glyph_count|, |glyphs|, |visible_attributes| and
1099 // |logical_clusters| on the run (since they may not have been set yet).
1100 run
->glyph_count
= run_length
;
1101 memset(run
->visible_attributes
.get(), 0,
1102 run
->glyph_count
* sizeof(SCRIPT_VISATTR
));
1103 for (int i
= 0; i
< run
->glyph_count
; ++i
)
1104 run
->glyphs
[i
] = IsWhitespace(run_text
[i
]) ? space_glyph
: missing_glyph
;
1105 for (size_t i
= 0; i
< run_length
; ++i
) {
1106 run
->logical_clusters
[i
] = run
->script_analysis
.fRTL
?
1107 run_length
- 1 - i
: i
;
1110 // TODO(msw): Don't use SCRIPT_UNDEFINED. Apparently Uniscribe can
1111 // crash on certain surrogate pairs with SCRIPT_UNDEFINED.
1112 // See https://bugzilla.mozilla.org/show_bug.cgi?id=341500
1113 // And http://maxradi.us/documents/uniscribe/
1114 run
->script_analysis
.eScript
= SCRIPT_UNDEFINED
;
1117 HRESULT
RenderTextWin::ShapeTextRunWithFont(internal::TextRun
* run
,
1119 // Update the run's font only if necessary. If the two fonts wrap the same
1120 // PlatformFontWin object, their native fonts will have the same value.
1121 if (run
->font
.GetNativeFont() != font
.GetNativeFont()) {
1122 const int font_size
= run
->font
.GetFontSize();
1123 const int font_height
= run
->font
.GetHeight();
1125 DeriveFontIfNecessary(font_size
, font_height
, run
->font_style
, &run
->font
);
1126 ScriptFreeCache(&run
->script_cache
);
1129 // Select the font desired for glyph generation.
1130 SelectObject(cached_hdc_
, run
->font
.GetNativeFont());
1132 HRESULT hr
= E_OUTOFMEMORY
;
1133 const size_t run_length
= run
->range
.length();
1134 const wchar_t* run_text
= &(GetLayoutText()[run
->range
.start()]);
1135 // Guess the expected number of glyphs from the length of the run.
1136 // MSDN suggests this at http://msdn.microsoft.com/en-us/library/dd368564.aspx
1137 size_t max_glyphs
= static_cast<size_t>(1.5 * run_length
+ 16);
1138 while (hr
== E_OUTOFMEMORY
&& max_glyphs
<= kMaxGlyphs
) {
1139 run
->glyph_count
= 0;
1140 run
->glyphs
.reset(new WORD
[max_glyphs
]);
1141 run
->visible_attributes
.reset(new SCRIPT_VISATTR
[max_glyphs
]);
1142 hr
= ScriptShape(cached_hdc_
, &run
->script_cache
, run_text
, run_length
,
1143 max_glyphs
, &run
->script_analysis
, run
->glyphs
.get(),
1144 run
->logical_clusters
.get(), run
->visible_attributes
.get(),
1146 // Ensure that |kMaxGlyphs| is attempted and the loop terminates afterward.
1147 max_glyphs
= std::max(max_glyphs
+ 1, std::min(max_glyphs
* 2, kMaxGlyphs
));
1152 int RenderTextWin::CountCharsWithMissingGlyphs(internal::TextRun
* run
,
1153 HRESULT shaping_result
) const {
1154 if (shaping_result
!= S_OK
) {
1155 DCHECK_EQ(shaping_result
, USP_E_SCRIPT_NOT_IN_FONT
);
1159 // If |hr| is S_OK, there could still be missing glyphs in the output.
1160 // http://msdn.microsoft.com/en-us/library/windows/desktop/dd368564.aspx
1161 int chars_not_missing_glyphs
= 0;
1162 SCRIPT_FONTPROPERTIES properties
;
1163 memset(&properties
, 0, sizeof(properties
));
1164 properties
.cBytes
= sizeof(properties
);
1165 ScriptGetFontProperties(cached_hdc_
, &run
->script_cache
, &properties
);
1167 const wchar_t* run_text
= &(GetLayoutText()[run
->range
.start()]);
1168 for (size_t char_index
= 0; char_index
< run
->range
.length(); ++char_index
) {
1169 const int glyph_index
= run
->logical_clusters
[char_index
];
1170 DCHECK_GE(glyph_index
, 0);
1171 DCHECK_LT(glyph_index
, run
->glyph_count
);
1173 if (run
->glyphs
[glyph_index
] == properties
.wgDefault
)
1176 // Windows Vista sometimes returns glyphs equal to wgBlank (instead of
1177 // wgDefault), with fZeroWidth set. Treat such cases as having missing
1178 // glyphs if the corresponding character is not whitespace.
1179 // See: http://crbug.com/125629
1180 if (run
->glyphs
[glyph_index
] == properties
.wgBlank
&&
1181 run
->visible_attributes
[glyph_index
].fZeroWidth
&&
1182 !IsWhitespace(run_text
[char_index
]) &&
1183 !IsUnicodeBidiControlCharacter(run_text
[char_index
])) {
1187 ++chars_not_missing_glyphs
;
1190 DCHECK_LE(chars_not_missing_glyphs
, static_cast<int>(run
->range
.length()));
1191 return run
->range
.length() - chars_not_missing_glyphs
;
1194 size_t RenderTextWin::GetRunContainingCaret(const SelectionModel
& caret
) const {
1195 DCHECK(!needs_layout_
);
1196 size_t layout_position
= TextIndexToLayoutIndex(caret
.caret_pos());
1197 LogicalCursorDirection affinity
= caret
.caret_affinity();
1198 for (size_t run
= 0; run
< runs_
.size(); ++run
)
1199 if (RangeContainsCaret(runs_
[run
]->range
, layout_position
, affinity
))
1201 return runs_
.size();
1204 size_t RenderTextWin::GetRunContainingXCoord(int x
) const {
1205 DCHECK(!needs_layout_
);
1206 // Find the text run containing the argument point (assumed already offset).
1207 for (size_t run
= 0; run
< runs_
.size(); ++run
) {
1208 if ((runs_
[run
]->preceding_run_widths
<= x
) &&
1209 ((runs_
[run
]->preceding_run_widths
+ runs_
[run
]->width
) > x
))
1212 return runs_
.size();
1215 SelectionModel
RenderTextWin::FirstSelectionModelInsideRun(
1216 const internal::TextRun
* run
) {
1217 size_t position
= LayoutIndexToTextIndex(run
->range
.start());
1218 position
= IndexOfAdjacentGrapheme(position
, CURSOR_FORWARD
);
1219 return SelectionModel(position
, CURSOR_BACKWARD
);
1222 SelectionModel
RenderTextWin::LastSelectionModelInsideRun(
1223 const internal::TextRun
* run
) {
1224 size_t position
= LayoutIndexToTextIndex(run
->range
.end());
1225 position
= IndexOfAdjacentGrapheme(position
, CURSOR_BACKWARD
);
1226 return SelectionModel(position
, CURSOR_FORWARD
);
1229 RenderText
* RenderText::CreateNativeInstance() {
1230 return new RenderTextWin
;