Don't crash when SimpleCache index is corrupt.
[chromium-blink-merge.git] / ui / gfx / render_text_win.cc
blob0469c0f5b7a61a6770457d330cb22d6e1c09401b
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"
7 #include <algorithm>
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"
21 namespace gfx {
23 namespace {
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,
36 HANDLETABLE* table,
37 CONST ENHMETARECORD* record,
38 int table_entries,
39 LPARAM log_font) {
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;
45 return 1;
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,
54 const Font& font,
55 const wchar_t* text,
56 int text_length,
57 Font* result) {
58 // Use a meta file to intercept the fallback font chosen by Uniscribe.
59 HDC meta_file_dc = CreateEnhMetaFile(hdc, NULL, NULL, NULL);
60 if (!meta_file_dc)
61 return false;
63 SelectObject(meta_file_dc, font.GetNativeFont());
65 SCRIPT_STRING_ANALYSIS script_analysis;
66 HRESULT hresult =
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)) {
79 LOGFONT log_font;
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,
96 int font_height,
97 int font_style,
98 Font* font) {
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);
108 return;
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;
128 } // namespace
130 namespace internal {
132 TextRun::TextRun()
133 : foreground(0),
134 font_style(0),
135 strike(false),
136 diagonal_strike(false),
137 underline(false),
138 width(0),
139 preceding_run_widths(0),
140 glyph_count(0),
141 script_cache(NULL) {
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,
153 size_t index,
154 bool trailing) {
155 DCHECK_GE(index, run->range.start());
156 DCHECK_LT(index, run->range.end() + (trailing ? 0 : 1));
157 int x = 0;
158 HRESULT hr = ScriptCPtoX(
159 index - run->range.start(),
160 trailing,
161 run->range.length(),
162 run->glyph_count,
163 run->logical_clusters.get(),
164 run->visible_attributes.get(),
165 run->advance_widths.get(),
166 &run->script_analysis,
167 &x);
168 DCHECK(SUCCEEDED(hr));
169 return run->preceding_run_widths + x;
172 } // namespace internal
174 // static
175 HDC RenderTextWin::cached_hdc_ = NULL;
177 // static
178 std::map<std::string, Font> RenderTextWin::successful_substitute_fonts_;
180 RenderTextWin::RenderTextWin()
181 : RenderText(),
182 common_baseline_(0),
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() {
194 EnsureLayout();
195 return string_size_;
198 int RenderTextWin::GetBaseline() {
199 EnsureLayout();
200 return common_baseline_;
203 SelectionModel RenderTextWin::FindCursorPosition(const Point& point) {
204 if (text().empty())
205 return SelectionModel();
207 EnsureLayout();
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,
217 run->range.length(),
218 run->glyph_count,
219 run->logical_clusters.get(),
220 run->visible_attributes.get(),
221 run->advance_widths.get(),
222 &(run->script_analysis),
223 &position,
224 &trailing);
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() {
234 EnsureLayout();
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()))));
243 return spans;
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())
256 return edge;
257 int visual_index = (direction == CURSOR_RIGHT) ? 0 : runs_.size() - 1;
258 run = runs_[visual_to_logical_[visual_index]];
259 } else {
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);
271 } else {
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) {
293 if (obscured())
294 return EdgeSelectionModel(direction);
296 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD);
297 bool success = iter.Init();
298 DCHECK(success);
299 if (!success)
300 return selection;
302 size_t pos;
303 if (direction == CURSOR_RIGHT) {
304 pos = std::min(selection.caret_pos() + 1, text().length());
305 while (iter.Advance()) {
306 pos = iter.pos();
307 if (iter.IsWord() && pos > selection.caret_pos())
308 break;
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()) {
317 if (iter.IsWord()) {
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.
322 break;
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.
326 pos = begin;
327 break;
328 } else {
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.
342 ResetLayout();
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())
364 return bounds;
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());
380 bounds.pop_back();
382 bounds.push_back(rect);
385 return bounds;
388 size_t RenderTextWin::TextIndexToLayoutIndex(size_t index) const {
389 if (!obscured())
390 return index;
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 {
400 if (!obscured())
401 return index;
403 DCHECK_LE(index, GetLayoutText().length());
404 const size_t text_index = ui::UTF16OffsetToIndex(text(), 0, index);
405 DCHECK_LE(text_index, text().length());
406 return text_index;
409 bool RenderTextWin::IsCursorablePosition(size_t position) {
410 if (position == 0 || position == text().length())
411 return true;
412 EnsureLayout();
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() {
426 if (!needs_layout_)
427 return;
428 // TODO(msw): Skip complex processing if ScriptIsComplex returns false.
429 ItemizeLogicalText();
430 if (!runs_.empty())
431 LayoutVisualText();
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)
462 continue;
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);
480 x = glyph_x;
484 void RenderTextWin::ItemizeLogicalText() {
485 runs_.clear();
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;
493 if (text().empty())
494 return;
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(),
506 text_length,
507 n - 1,
508 &script_control_,
509 &script_state_,
510 &script_items[0],
511 &script_items_count);
513 DCHECK(SUCCEEDED(hr));
515 if (script_items_count <= 0)
516 return;
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)
546 script_item++;
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());
558 if (!cached_hdc_)
559 cached_hdc_ = CreateCompatibleDC(NULL);
561 HRESULT hr = E_FAIL;
562 string_size_.set_height(0);
563 for (size_t i = 0; i < runs_.size(); ++i) {
564 internal::TextRun* run = runs_[i];
565 LayoutTextRun(run);
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_,
575 &run->script_cache,
576 run->glyphs.get(),
577 run->glyph_count,
578 run->visible_attributes.get(),
579 &(run->script_analysis),
580 run->advance_widths.get(),
581 run->offsets.get(),
582 &(run->abc_widths));
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(),
596 levels.get(),
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;
626 Font current_font;
628 run->logical_clusters.reset(new WORD[run_length]);
629 while (fonts.NextFont(&current_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);
645 } else {
646 NOTREACHED() << hr;
649 // Use the font if it had glyphs for all characters.
650 if (!glyphs_missing) {
651 // Save the successful fallback font that was chosen.
652 if (tried_fallback)
653 successful_substitute_fonts_[original_font.GetFontName()] = run->font;
654 return;
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);
665 continue;
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;
675 Font fallback_font;
676 if (ChooseFallbackFont(cached_hdc_, run->font, run_text, run_length,
677 &fallback_font)) {
678 fonts.SetNextFont(fallback_font);
679 continue;
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);
689 return;
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
694 // garbage values.
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,
705 &properties);
706 if (hr == S_OK) {
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,
726 const Font& font) {
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();
732 run->font = font;
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_,
750 &run->script_cache,
751 run_text,
752 run_length,
753 max_glyphs,
754 &run->script_analysis,
755 run->glyphs.get(),
756 run->logical_clusters.get(),
757 run->visible_attributes.get(),
758 &run->glyph_count);
759 max_glyphs *= 2;
761 return hr;
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)
778 continue;
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])) {
788 continue;
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))
804 return run;
805 return runs_.size();
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))
814 return run;
816 return runs_.size();
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;
837 } // namespace gfx