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/rtl.h"
11 #include "base/logging.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/win/windows_version.h"
15 #include "ui/base/text/utf16_indexing.h"
16 #include "ui/gfx/canvas.h"
17 #include "ui/gfx/font_fallback_win.h"
18 #include "ui/gfx/font_smoothing_win.h"
19 #include "ui/gfx/platform_font_win.h"
25 // The maximum supported number of Uniscribe runs; a SCRIPT_ITEM is 8 bytes.
26 // TODO(msw): Review memory use/failure? Max string length? Alternate approach?
27 const int kGuessItems
= 100;
28 const int kMaxItems
= 10000;
30 // The maximum supported number of Uniscribe glyphs; a glyph is 1 word.
31 // TODO(msw): Review memory use/failure? Max string length? Alternate approach?
32 const int kMaxGlyphs
= 100000;
34 // Callback to |EnumEnhMetaFile()| to intercept font creation.
35 int CALLBACK
MetaFileEnumProc(HDC hdc
,
37 CONST ENHMETARECORD
* record
,
40 if (record
->iType
== EMR_EXTCREATEFONTINDIRECTW
) {
41 const EMREXTCREATEFONTINDIRECTW
* create_font_record
=
42 reinterpret_cast<const EMREXTCREATEFONTINDIRECTW
*>(record
);
43 *reinterpret_cast<LOGFONT
*>(log_font
) = create_font_record
->elfw
.elfLogFont
;
48 // Finds a fallback font to use to render the specified |text| with respect to
49 // an initial |font|. Returns the resulting font via out param |result|. Returns
50 // |true| if a fallback font was found.
51 // Adapted from WebKit's |FontCache::GetFontDataForCharacters()|.
52 // TODO(asvitkine): This should be moved to font_fallback_win.cc.
53 bool ChooseFallbackFont(HDC hdc
,
58 // Use a meta file to intercept the fallback font chosen by Uniscribe.
59 HDC meta_file_dc
= CreateEnhMetaFile(hdc
, NULL
, NULL
, NULL
);
63 SelectObject(meta_file_dc
, font
.GetNativeFont());
65 SCRIPT_STRING_ANALYSIS script_analysis
;
67 ScriptStringAnalyse(meta_file_dc
, text
, text_length
, 0, -1,
68 SSA_METAFILE
| SSA_FALLBACK
| SSA_GLYPHS
| SSA_LINK
,
69 0, NULL
, NULL
, NULL
, NULL
, NULL
, &script_analysis
);
71 if (SUCCEEDED(hresult
)) {
72 hresult
= ScriptStringOut(script_analysis
, 0, 0, 0, NULL
, 0, 0, FALSE
);
73 ScriptStringFree(&script_analysis
);
76 bool found_fallback
= false;
77 HENHMETAFILE meta_file
= CloseEnhMetaFile(meta_file_dc
);
78 if (SUCCEEDED(hresult
)) {
80 log_font
.lfFaceName
[0] = 0;
81 EnumEnhMetaFile(0, meta_file
, MetaFileEnumProc
, &log_font
, NULL
);
82 if (log_font
.lfFaceName
[0]) {
83 *result
= Font(UTF16ToUTF8(log_font
.lfFaceName
), font
.GetFontSize());
84 found_fallback
= true;
87 DeleteEnhMetaFile(meta_file
);
89 return found_fallback
;
92 // Changes |font| to have the specified |font_size| (or |font_height| on Windows
93 // XP) and |font_style| if it is not the case already. Only considers bold and
94 // italic styles, since the underlined style has no effect on glyph shaping.
95 void DeriveFontIfNecessary(int font_size
,
99 const int kStyleMask
= (Font::BOLD
| Font::ITALIC
);
100 const int target_style
= (font_style
& kStyleMask
);
102 // On Windows XP, the font must be resized using |font_height| instead of
103 // |font_size| to match GDI behavior.
104 if (base::win::GetVersion() < base::win::VERSION_VISTA
) {
105 PlatformFontWin
* platform_font
=
106 static_cast<PlatformFontWin
*>(font
->platform_font());
107 *font
= platform_font
->DeriveFontWithHeight(font_height
, target_style
);
111 const int current_style
= (font
->GetStyle() & kStyleMask
);
112 const int current_size
= font
->GetFontSize();
113 if (current_style
!= target_style
|| current_size
!= font_size
)
114 *font
= font
->DeriveFont(font_size
- current_size
, target_style
);
117 // Returns true if |c| is a Unicode BiDi control character.
118 bool IsUnicodeBidiControlCharacter(char16 c
) {
119 return c
== base::i18n::kRightToLeftMark
||
120 c
== base::i18n::kLeftToRightMark
||
121 c
== base::i18n::kLeftToRightEmbeddingMark
||
122 c
== base::i18n::kRightToLeftEmbeddingMark
||
123 c
== base::i18n::kPopDirectionalFormatting
||
124 c
== base::i18n::kLeftToRightOverride
||
125 c
== base::i18n::kRightToLeftOverride
;
136 diagonal_strike(false),
139 preceding_run_widths(0),
142 memset(&script_analysis
, 0, sizeof(script_analysis
));
143 memset(&abc_widths
, 0, sizeof(abc_widths
));
146 TextRun::~TextRun() {
147 ScriptFreeCache(&script_cache
);
150 // Returns the X coordinate of the leading or |trailing| edge of the glyph
151 // starting at |index|, relative to the left of the text (not the view).
152 int GetGlyphXBoundary(const internal::TextRun
* run
,
155 DCHECK_GE(index
, run
->range
.start());
156 DCHECK_LT(index
, run
->range
.end() + (trailing
? 0 : 1));
158 HRESULT hr
= ScriptCPtoX(
159 index
- run
->range
.start(),
163 run
->logical_clusters
.get(),
164 run
->visible_attributes
.get(),
165 run
->advance_widths
.get(),
166 &run
->script_analysis
,
168 DCHECK(SUCCEEDED(hr
));
169 return run
->preceding_run_widths
+ x
;
172 } // namespace internal
175 HDC
RenderTextWin::cached_hdc_
= NULL
;
178 std::map
<std::string
, Font
> RenderTextWin::successful_substitute_fonts_
;
180 RenderTextWin::RenderTextWin()
183 needs_layout_(false) {
184 memset(&script_control_
, 0, sizeof(script_control_
));
185 memset(&script_state_
, 0, sizeof(script_state_
));
187 MoveCursorTo(EdgeSelectionModel(CURSOR_LEFT
));
190 RenderTextWin::~RenderTextWin() {
193 Size
RenderTextWin::GetStringSize() {
198 int RenderTextWin::GetBaseline() {
200 return common_baseline_
;
203 SelectionModel
RenderTextWin::FindCursorPosition(const Point
& point
) {
205 return SelectionModel();
208 // Find the run that contains the point and adjust the argument location.
209 int x
= ToTextPoint(point
).x();
210 size_t run_index
= GetRunContainingXCoord(x
);
211 if (run_index
== runs_
.size())
212 return EdgeSelectionModel((x
< 0) ? CURSOR_LEFT
: CURSOR_RIGHT
);
213 internal::TextRun
* run
= runs_
[run_index
];
215 int position
= 0, trailing
= 0;
216 HRESULT hr
= ScriptXtoCP(x
- run
->preceding_run_widths
,
219 run
->logical_clusters
.get(),
220 run
->visible_attributes
.get(),
221 run
->advance_widths
.get(),
222 &(run
->script_analysis
),
225 DCHECK(SUCCEEDED(hr
));
226 DCHECK_GE(trailing
, 0);
227 position
+= run
->range
.start();
228 const size_t cursor
= LayoutIndexToTextIndex(position
+ trailing
);
229 DCHECK_LE(cursor
, text().length());
230 return SelectionModel(cursor
, trailing
? CURSOR_BACKWARD
: CURSOR_FORWARD
);
233 std::vector
<RenderText::FontSpan
> RenderTextWin::GetFontSpansForTesting() {
236 std::vector
<RenderText::FontSpan
> spans
;
237 for (size_t i
= 0; i
< runs_
.size(); ++i
) {
238 spans
.push_back(RenderText::FontSpan(runs_
[i
]->font
,
239 ui::Range(LayoutIndexToTextIndex(runs_
[i
]->range
.start()),
240 LayoutIndexToTextIndex(runs_
[i
]->range
.end()))));
246 SelectionModel
RenderTextWin::AdjacentCharSelectionModel(
247 const SelectionModel
& selection
,
248 VisualCursorDirection direction
) {
249 DCHECK(!needs_layout_
);
250 internal::TextRun
* run
;
251 size_t run_index
= GetRunContainingCaret(selection
);
252 if (run_index
>= runs_
.size()) {
253 // The cursor is not in any run: we're at the visual and logical edge.
254 SelectionModel edge
= EdgeSelectionModel(direction
);
255 if (edge
.caret_pos() == selection
.caret_pos())
257 int visual_index
= (direction
== CURSOR_RIGHT
) ? 0 : runs_
.size() - 1;
258 run
= runs_
[visual_to_logical_
[visual_index
]];
260 // If the cursor is moving within the current run, just move it by one
261 // grapheme in the appropriate direction.
262 run
= runs_
[run_index
];
263 size_t caret
= selection
.caret_pos();
264 bool forward_motion
=
265 run
->script_analysis
.fRTL
== (direction
== CURSOR_LEFT
);
266 if (forward_motion
) {
267 if (caret
< LayoutIndexToTextIndex(run
->range
.end())) {
268 caret
= IndexOfAdjacentGrapheme(caret
, CURSOR_FORWARD
);
269 return SelectionModel(caret
, CURSOR_BACKWARD
);
272 if (caret
> LayoutIndexToTextIndex(run
->range
.start())) {
273 caret
= IndexOfAdjacentGrapheme(caret
, CURSOR_BACKWARD
);
274 return SelectionModel(caret
, CURSOR_FORWARD
);
277 // The cursor is at the edge of a run; move to the visually adjacent run.
278 int visual_index
= logical_to_visual_
[run_index
];
279 visual_index
+= (direction
== CURSOR_LEFT
) ? -1 : 1;
280 if (visual_index
< 0 || visual_index
>= static_cast<int>(runs_
.size()))
281 return EdgeSelectionModel(direction
);
282 run
= runs_
[visual_to_logical_
[visual_index
]];
284 bool forward_motion
= run
->script_analysis
.fRTL
== (direction
== CURSOR_LEFT
);
285 return forward_motion
? FirstSelectionModelInsideRun(run
) :
286 LastSelectionModelInsideRun(run
);
289 // TODO(msw): Implement word breaking for Windows.
290 SelectionModel
RenderTextWin::AdjacentWordSelectionModel(
291 const SelectionModel
& selection
,
292 VisualCursorDirection direction
) {
294 return EdgeSelectionModel(direction
);
296 base::i18n::BreakIterator
iter(text(), base::i18n::BreakIterator::BREAK_WORD
);
297 bool success
= iter
.Init();
303 if (direction
== CURSOR_RIGHT
) {
304 pos
= std::min(selection
.caret_pos() + 1, text().length());
305 while (iter
.Advance()) {
307 if (iter
.IsWord() && pos
> selection
.caret_pos())
310 } else { // direction == CURSOR_LEFT
311 // Notes: We always iterate words from the beginning.
312 // This is probably fast enough for our usage, but we may
313 // want to modify WordIterator so that it can start from the
314 // middle of string and advance backwards.
315 pos
= std::max
<int>(selection
.caret_pos() - 1, 0);
316 while (iter
.Advance()) {
318 size_t begin
= iter
.pos() - iter
.GetString().length();
319 if (begin
== selection
.caret_pos()) {
320 // The cursor is at the beginning of a word.
321 // Move to previous word.
323 } else if (iter
.pos() >= selection
.caret_pos()) {
324 // The cursor is in the middle or at the end of a word.
325 // Move to the top of current word.
329 pos
= iter
.pos() - iter
.GetString().length();
334 return SelectionModel(pos
, CURSOR_FORWARD
);
337 void RenderTextWin::SetSelectionModel(const SelectionModel
& model
) {
338 RenderText::SetSelectionModel(model
);
339 // TODO(xji|msw): The text selection color is applied in ItemizeLogicalText().
340 // So, the layout must be updated in order to draw the proper selection range.
341 // Colors should be applied in DrawVisualText(), as done by RenderTextLinux.
345 ui::Range
RenderTextWin::GetGlyphBounds(size_t index
) {
346 const size_t run_index
=
347 GetRunContainingCaret(SelectionModel(index
, CURSOR_FORWARD
));
348 DCHECK_LT(run_index
, runs_
.size());
349 internal::TextRun
* run
= runs_
[run_index
];
350 const size_t layout_index
= TextIndexToLayoutIndex(index
);
351 return ui::Range(GetGlyphXBoundary(run
, layout_index
, false),
352 GetGlyphXBoundary(run
, layout_index
, true));
355 std::vector
<Rect
> RenderTextWin::GetSubstringBounds(const ui::Range
& range
) {
356 DCHECK(!needs_layout_
);
357 DCHECK(ui::Range(0, text().length()).Contains(range
));
358 ui::Range
layout_range(TextIndexToLayoutIndex(range
.start()),
359 TextIndexToLayoutIndex(range
.end()));
360 DCHECK(ui::Range(0, GetLayoutText().length()).Contains(layout_range
));
362 std::vector
<Rect
> bounds
;
363 if (layout_range
.is_empty())
366 // Add a Rect for each run/selection intersection.
367 // TODO(msw): The bounds should probably not always be leading the range ends.
368 for (size_t i
= 0; i
< runs_
.size(); ++i
) {
369 const internal::TextRun
* run
= runs_
[visual_to_logical_
[i
]];
370 ui::Range intersection
= run
->range
.Intersect(layout_range
);
371 if (intersection
.IsValid()) {
372 DCHECK(!intersection
.is_reversed());
373 ui::Range
range_x(GetGlyphXBoundary(run
, intersection
.start(), false),
374 GetGlyphXBoundary(run
, intersection
.end(), false));
375 Rect
rect(range_x
.GetMin(), 0, range_x
.length(), run
->font
.GetHeight());
376 rect
.set_origin(ToViewPoint(rect
.origin()));
377 // Union this with the last rect if they're adjacent.
378 if (!bounds
.empty() && rect
.SharesEdgeWith(bounds
.back())) {
379 rect
.Union(bounds
.back());
382 bounds
.push_back(rect
);
388 size_t RenderTextWin::TextIndexToLayoutIndex(size_t index
) const {
392 DCHECK_LE(index
, text().length());
393 const ptrdiff_t offset
= ui::UTF16IndexToOffset(text(), 0, index
);
394 DCHECK_GE(offset
, 0);
395 DCHECK_LE(static_cast<size_t>(offset
), GetLayoutText().length());
396 return static_cast<size_t>(offset
);
399 size_t RenderTextWin::LayoutIndexToTextIndex(size_t index
) const {
403 DCHECK_LE(index
, GetLayoutText().length());
404 const size_t text_index
= ui::UTF16OffsetToIndex(text(), 0, index
);
405 DCHECK_LE(text_index
, text().length());
409 bool RenderTextWin::IsCursorablePosition(size_t position
) {
410 if (position
== 0 || position
== text().length())
414 // Check if the index is at a valid code point (not mid-surrgate-pair) with
415 // distinct glyph bounds (not mid-multi-character-grapheme, eg. \x0915\x093f).
416 return ui::IsValidCodePointIndex(text(), position
) &&
417 GetGlyphBounds(position
) != GetGlyphBounds(position
- 1);
420 void RenderTextWin::ResetLayout() {
421 // Layout is performed lazily as needed for drawing/metrics.
422 needs_layout_
= true;
425 void RenderTextWin::EnsureLayout() {
428 // TODO(msw): Skip complex processing if ScriptIsComplex returns false.
429 ItemizeLogicalText();
432 needs_layout_
= false;
435 void RenderTextWin::DrawVisualText(Canvas
* canvas
) {
436 DCHECK(!needs_layout_
);
438 // Skia will draw glyphs with respect to the baseline.
439 Vector2d
offset(GetTextOffset() + Vector2d(0, common_baseline_
));
441 SkScalar x
= SkIntToScalar(offset
.x());
442 SkScalar y
= SkIntToScalar(offset
.y());
444 std::vector
<SkPoint
> pos
;
446 internal::SkiaTextRenderer
renderer(canvas
);
447 ApplyFadeEffects(&renderer
);
448 ApplyTextShadows(&renderer
);
450 bool smoothing_enabled
;
451 bool cleartype_enabled
;
452 GetCachedFontSmoothingSettings(&smoothing_enabled
, &cleartype_enabled
);
453 // Note that |cleartype_enabled| corresponds to Skia's |enable_lcd_text|.
454 renderer
.SetFontSmoothingSettings(
455 smoothing_enabled
, cleartype_enabled
&& !background_is_transparent());
457 for (size_t i
= 0; i
< runs_
.size(); ++i
) {
458 // Get the run specified by the visual-to-logical map.
459 internal::TextRun
* run
= runs_
[visual_to_logical_
[i
]];
461 if (run
->glyph_count
== 0)
464 // Based on WebCore::skiaDrawText.
465 pos
.resize(run
->glyph_count
);
466 SkScalar glyph_x
= x
;
467 for (int glyph
= 0; glyph
< run
->glyph_count
; glyph
++) {
468 pos
[glyph
].set(glyph_x
+ run
->offsets
[glyph
].du
,
469 y
+ run
->offsets
[glyph
].dv
);
470 glyph_x
+= SkIntToScalar(run
->advance_widths
[glyph
]);
473 renderer
.SetTextSize(run
->font
.GetFontSize());
474 renderer
.SetFontFamilyWithStyle(run
->font
.GetFontName(), run
->font_style
);
475 renderer
.SetForegroundColor(run
->foreground
);
476 renderer
.DrawPosText(&pos
[0], run
->glyphs
.get(), run
->glyph_count
);
477 renderer
.DrawDecorations(x
, y
, run
->width
, run
->underline
, run
->strike
,
478 run
->diagonal_strike
);
484 void RenderTextWin::ItemizeLogicalText() {
486 string_size_
= Size(0, GetFont().GetHeight());
487 common_baseline_
= 0;
489 // Set Uniscribe's base text direction.
490 script_state_
.uBidiLevel
=
491 (GetTextDirection() == base::i18n::RIGHT_TO_LEFT
) ? 1 : 0;
496 HRESULT hr
= E_OUTOFMEMORY
;
497 int script_items_count
= 0;
498 std::vector
<SCRIPT_ITEM
> script_items
;
499 const size_t text_length
= GetLayoutText().length();
500 for (size_t n
= kGuessItems
; hr
== E_OUTOFMEMORY
&& n
< kMaxItems
; n
*= 2) {
501 // Derive the array of Uniscribe script items from the logical text.
502 // ScriptItemize always adds a terminal array item so that the length of the
503 // last item can be derived from the terminal SCRIPT_ITEM::iCharPos.
504 script_items
.resize(n
);
505 hr
= ScriptItemize(GetLayoutText().c_str(),
511 &script_items_count
);
513 DCHECK(SUCCEEDED(hr
));
515 if (script_items_count
<= 0)
518 // Temporarily apply composition underlines and selection colors.
519 ApplyCompositionAndSelectionStyles();
521 // Build the list of runs from the script items and ranged colors/styles.
522 // TODO(msw): Only break for bold/italic, not color etc. See TextRun comment.
523 internal::StyleIterator
style(colors(), styles());
524 SCRIPT_ITEM
* script_item
= &script_items
[0];
525 const size_t layout_text_length
= GetLayoutText().length();
526 for (size_t run_break
= 0; run_break
< layout_text_length
;) {
527 internal::TextRun
* run
= new internal::TextRun();
528 run
->range
.set_start(run_break
);
529 run
->font
= GetFont();
530 run
->font_style
= (style
.style(BOLD
) ? Font::BOLD
: 0) |
531 (style
.style(ITALIC
) ? Font::ITALIC
: 0);
532 DeriveFontIfNecessary(run
->font
.GetFontSize(), run
->font
.GetHeight(),
533 run
->font_style
, &run
->font
);
534 run
->foreground
= style
.color();
535 run
->strike
= style
.style(STRIKE
);
536 run
->diagonal_strike
= style
.style(DIAGONAL_STRIKE
);
537 run
->underline
= style
.style(UNDERLINE
);
538 run
->script_analysis
= script_item
->a
;
540 // Find the next break and advance the iterators as needed.
541 const size_t script_item_break
= (script_item
+ 1)->iCharPos
;
542 run_break
= std::min(script_item_break
,
543 TextIndexToLayoutIndex(style
.GetRange().end()));
544 style
.UpdatePosition(LayoutIndexToTextIndex(run_break
));
545 if (script_item_break
== run_break
)
547 run
->range
.set_end(run_break
);
548 runs_
.push_back(run
);
551 // Undo the temporarily applied composition underlines and selection colors.
552 UndoCompositionAndSelectionStyles();
555 void RenderTextWin::LayoutVisualText() {
556 DCHECK(!runs_
.empty());
559 cached_hdc_
= CreateCompatibleDC(NULL
);
562 string_size_
.set_height(0);
563 for (size_t i
= 0; i
< runs_
.size(); ++i
) {
564 internal::TextRun
* run
= runs_
[i
];
567 string_size_
.set_height(std::max(string_size_
.height(),
568 run
->font
.GetHeight()));
569 common_baseline_
= std::max(common_baseline_
, run
->font
.GetBaseline());
571 if (run
->glyph_count
> 0) {
572 run
->advance_widths
.reset(new int[run
->glyph_count
]);
573 run
->offsets
.reset(new GOFFSET
[run
->glyph_count
]);
574 hr
= ScriptPlace(cached_hdc_
,
578 run
->visible_attributes
.get(),
579 &(run
->script_analysis
),
580 run
->advance_widths
.get(),
583 DCHECK(SUCCEEDED(hr
));
587 // Build the array of bidirectional embedding levels.
588 scoped_ptr
<BYTE
[]> levels(new BYTE
[runs_
.size()]);
589 for (size_t i
= 0; i
< runs_
.size(); ++i
)
590 levels
[i
] = runs_
[i
]->script_analysis
.s
.uBidiLevel
;
592 // Get the maps between visual and logical run indices.
593 visual_to_logical_
.reset(new int[runs_
.size()]);
594 logical_to_visual_
.reset(new int[runs_
.size()]);
595 hr
= ScriptLayout(runs_
.size(),
597 visual_to_logical_
.get(),
598 logical_to_visual_
.get());
599 DCHECK(SUCCEEDED(hr
));
601 // Precalculate run width information.
602 size_t preceding_run_widths
= 0;
603 for (size_t i
= 0; i
< runs_
.size(); ++i
) {
604 internal::TextRun
* run
= runs_
[visual_to_logical_
[i
]];
605 run
->preceding_run_widths
= preceding_run_widths
;
606 const ABC
& abc
= run
->abc_widths
;
607 run
->width
= abc
.abcA
+ abc
.abcB
+ abc
.abcC
;
608 preceding_run_widths
+= run
->width
;
610 string_size_
.set_width(preceding_run_widths
);
613 void RenderTextWin::LayoutTextRun(internal::TextRun
* run
) {
614 const size_t run_length
= run
->range
.length();
615 const wchar_t* run_text
= &(GetLayoutText()[run
->range
.start()]);
616 Font original_font
= run
->font
;
617 LinkedFontsIterator
fonts(original_font
);
618 bool tried_cached_font
= false;
619 bool tried_fallback
= false;
620 // Keep track of the font that is able to display the greatest number of
621 // characters for which ScriptShape() returned S_OK. This font will be used
622 // in the case where no font is able to display the entire run.
623 int best_partial_font_missing_char_count
= INT_MAX
;
624 Font best_partial_font
= original_font
;
625 bool using_best_partial_font
= false;
628 run
->logical_clusters
.reset(new WORD
[run_length
]);
629 while (fonts
.NextFont(¤t_font
)) {
630 HRESULT hr
= ShapeTextRunWithFont(run
, current_font
);
632 bool glyphs_missing
= false;
633 if (hr
== USP_E_SCRIPT_NOT_IN_FONT
) {
634 glyphs_missing
= true;
635 } else if (hr
== S_OK
) {
636 // If |hr| is S_OK, there could still be missing glyphs in the output.
637 // http://msdn.microsoft.com/en-us/library/windows/desktop/dd368564.aspx
638 const int missing_count
= CountCharsWithMissingGlyphs(run
);
639 // Track the font that produced the least missing glyphs.
640 if (missing_count
< best_partial_font_missing_char_count
) {
641 best_partial_font_missing_char_count
= missing_count
;
642 best_partial_font
= run
->font
;
644 glyphs_missing
= (missing_count
!= 0);
649 // Use the font if it had glyphs for all characters.
650 if (!glyphs_missing
) {
651 // Save the successful fallback font that was chosen.
653 successful_substitute_fonts_
[original_font
.GetFontName()] = run
->font
;
657 // First, try the cached font from previous runs, if any.
658 if (!tried_cached_font
) {
659 tried_cached_font
= true;
661 std::map
<std::string
, Font
>::const_iterator it
=
662 successful_substitute_fonts_
.find(original_font
.GetFontName());
663 if (it
!= successful_substitute_fonts_
.end()) {
664 fonts
.SetNextFont(it
->second
);
669 // If there are missing glyphs, first try finding a fallback font using a
670 // meta file, if it hasn't yet been attempted for this run.
671 // TODO(msw|asvitkine): Support RenderText's font_list()?
672 if (!tried_fallback
) {
673 tried_fallback
= true;
676 if (ChooseFallbackFont(cached_hdc_
, run
->font
, run_text
, run_length
,
678 fonts
.SetNextFont(fallback_font
);
684 // If a font was able to partially display the run, use that now.
685 if (best_partial_font_missing_char_count
< static_cast<int>(run_length
)) {
686 // Re-shape the run only if |best_partial_font| differs from the last font.
687 if (best_partial_font
.GetNativeFont() != run
->font
.GetNativeFont())
688 ShapeTextRunWithFont(run
, best_partial_font
);
692 // If no font was able to partially display the run, replace all glyphs
693 // with |wgDefault| from the original font to ensure to they don't hold
695 // First, clear the cache and select the original font on the HDC.
696 ScriptFreeCache(&run
->script_cache
);
697 run
->font
= original_font
;
698 SelectObject(cached_hdc_
, run
->font
.GetNativeFont());
700 // Now, get the font's properties.
701 SCRIPT_FONTPROPERTIES properties
;
702 memset(&properties
, 0, sizeof(properties
));
703 properties
.cBytes
= sizeof(properties
);
704 HRESULT hr
= ScriptGetFontProperties(cached_hdc_
, &run
->script_cache
,
707 // Finally, initialize |glyph_count|, |glyphs| and |visible_attributes| on
708 // the run (since they may not have been set yet).
709 run
->glyph_count
= run_length
;
710 memset(run
->visible_attributes
.get(), 0,
711 run
->glyph_count
* sizeof(SCRIPT_VISATTR
));
712 for (int i
= 0; i
< run
->glyph_count
; ++i
) {
713 run
->glyphs
[i
] = IsWhitespace(run_text
[i
]) ? properties
.wgBlank
:
714 properties
.wgDefault
;
718 // TODO(msw): Don't use SCRIPT_UNDEFINED. Apparently Uniscribe can
719 // crash on certain surrogate pairs with SCRIPT_UNDEFINED.
720 // See https://bugzilla.mozilla.org/show_bug.cgi?id=341500
721 // And http://maxradi.us/documents/uniscribe/
722 run
->script_analysis
.eScript
= SCRIPT_UNDEFINED
;
725 HRESULT
RenderTextWin::ShapeTextRunWithFont(internal::TextRun
* run
,
727 // Update the run's font only if necessary. If the two fonts wrap the same
728 // PlatformFontWin object, their native fonts will have the same value.
729 if (run
->font
.GetNativeFont() != font
.GetNativeFont()) {
730 const int font_size
= run
->font
.GetFontSize();
731 const int font_height
= run
->font
.GetHeight();
733 DeriveFontIfNecessary(font_size
, font_height
, run
->font_style
, &run
->font
);
734 ScriptFreeCache(&run
->script_cache
);
737 // Select the font desired for glyph generation.
738 SelectObject(cached_hdc_
, run
->font
.GetNativeFont());
740 HRESULT hr
= E_OUTOFMEMORY
;
741 const size_t run_length
= run
->range
.length();
742 const wchar_t* run_text
= &(GetLayoutText()[run
->range
.start()]);
743 // Max glyph guess: http://msdn.microsoft.com/en-us/library/dd368564.aspx
744 size_t max_glyphs
= static_cast<size_t>(1.5 * run_length
+ 16);
745 while (hr
== E_OUTOFMEMORY
&& max_glyphs
< kMaxGlyphs
) {
746 run
->glyph_count
= 0;
747 run
->glyphs
.reset(new WORD
[max_glyphs
]);
748 run
->visible_attributes
.reset(new SCRIPT_VISATTR
[max_glyphs
]);
749 hr
= ScriptShape(cached_hdc_
,
754 &run
->script_analysis
,
756 run
->logical_clusters
.get(),
757 run
->visible_attributes
.get(),
764 int RenderTextWin::CountCharsWithMissingGlyphs(internal::TextRun
* run
) const {
765 int chars_not_missing_glyphs
= 0;
766 SCRIPT_FONTPROPERTIES properties
;
767 memset(&properties
, 0, sizeof(properties
));
768 properties
.cBytes
= sizeof(properties
);
769 ScriptGetFontProperties(cached_hdc_
, &run
->script_cache
, &properties
);
771 const wchar_t* run_text
= &(GetLayoutText()[run
->range
.start()]);
772 for (size_t char_index
= 0; char_index
< run
->range
.length(); ++char_index
) {
773 const int glyph_index
= run
->logical_clusters
[char_index
];
774 DCHECK_GE(glyph_index
, 0);
775 DCHECK_LT(glyph_index
, run
->glyph_count
);
777 if (run
->glyphs
[glyph_index
] == properties
.wgDefault
)
780 // Windows Vista sometimes returns glyphs equal to wgBlank (instead of
781 // wgDefault), with fZeroWidth set. Treat such cases as having missing
782 // glyphs if the corresponding character is not whitespace.
783 // See: http://crbug.com/125629
784 if (run
->glyphs
[glyph_index
] == properties
.wgBlank
&&
785 run
->visible_attributes
[glyph_index
].fZeroWidth
&&
786 !IsWhitespace(run_text
[char_index
]) &&
787 !IsUnicodeBidiControlCharacter(run_text
[char_index
])) {
791 ++chars_not_missing_glyphs
;
794 DCHECK_LE(chars_not_missing_glyphs
, static_cast<int>(run
->range
.length()));
795 return run
->range
.length() - chars_not_missing_glyphs
;
798 size_t RenderTextWin::GetRunContainingCaret(const SelectionModel
& caret
) const {
799 DCHECK(!needs_layout_
);
800 size_t layout_position
= TextIndexToLayoutIndex(caret
.caret_pos());
801 LogicalCursorDirection affinity
= caret
.caret_affinity();
802 for (size_t run
= 0; run
< runs_
.size(); ++run
)
803 if (RangeContainsCaret(runs_
[run
]->range
, layout_position
, affinity
))
808 size_t RenderTextWin::GetRunContainingXCoord(int x
) const {
809 DCHECK(!needs_layout_
);
810 // Find the text run containing the argument point (assumed already offset).
811 for (size_t run
= 0; run
< runs_
.size(); ++run
) {
812 if ((runs_
[run
]->preceding_run_widths
<= x
) &&
813 ((runs_
[run
]->preceding_run_widths
+ runs_
[run
]->width
) > x
))
819 SelectionModel
RenderTextWin::FirstSelectionModelInsideRun(
820 const internal::TextRun
* run
) {
821 size_t position
= LayoutIndexToTextIndex(run
->range
.start());
822 position
= IndexOfAdjacentGrapheme(position
, CURSOR_FORWARD
);
823 return SelectionModel(position
, CURSOR_BACKWARD
);
826 SelectionModel
RenderTextWin::LastSelectionModelInsideRun(
827 const internal::TextRun
* run
) {
828 size_t position
= LayoutIndexToTextIndex(run
->range
.end());
829 position
= IndexOfAdjacentGrapheme(position
, CURSOR_BACKWARD
);
830 return SelectionModel(position
, CURSOR_FORWARD
);
833 RenderText
* RenderText::CreateInstance() {
834 return new RenderTextWin
;