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. 4 5 #include "ui/gfx/render_text_win.h" 6 7 #include <algorithm> 8 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_smoothing_win.h" 20 #include "ui/gfx/platform_font_win.h" 21 #include "ui/gfx/utf16_indexing.h" 22 23 namespace gfx { 24 25 namespace { 26 27 // The maximum length of text supported for Uniscribe layout and display. 28 // This empirically chosen value should prevent major performance degradations. 29 // TODO(msw): Support longer text, partial layout/painting, etc. 30 const size_t kMaxUniscribeTextLength = 10000; 31 32 // The initial guess and maximum supported number of runs; arbitrary values. 33 // TODO(msw): Support more runs, determine a better initial guess, etc. 34 const int kGuessRuns = 100; 35 const size_t kMaxRuns = 10000; 36 37 // The maximum number of glyphs per run; ScriptShape fails on larger values. 38 const size_t kMaxGlyphs = 65535; 39 40 // Callback to |EnumEnhMetaFile()| to intercept font creation. 41 int CALLBACK MetaFileEnumProc(HDC hdc, 42 HANDLETABLE* table, 43 CONST ENHMETARECORD* record, 44 int table_entries, 45 LPARAM log_font) { 46 if (record->iType == EMR_EXTCREATEFONTINDIRECTW) { 47 const EMREXTCREATEFONTINDIRECTW* create_font_record = 48 reinterpret_cast<const EMREXTCREATEFONTINDIRECTW*>(record); 49 *reinterpret_cast<LOGFONT*>(log_font) = create_font_record->elfw.elfLogFont; 50 } 51 return 1; 52 } 53 54 // Finds a fallback font to use to render the specified |text| with respect to 55 // an initial |font|. Returns the resulting font via out param |result|. Returns 56 // |true| if a fallback font was found. 57 // Adapted from WebKit's |FontCache::GetFontDataForCharacters()|. 58 // TODO(asvitkine): This should be moved to font_fallback_win.cc. 59 bool ChooseFallbackFont(HDC hdc, 60 const Font& font, 61 const wchar_t* text, 62 int text_length, 63 Font* result) { 64 // Use a meta file to intercept the fallback font chosen by Uniscribe. 65 HDC meta_file_dc = CreateEnhMetaFile(hdc, NULL, NULL, NULL); 66 if (!meta_file_dc) 67 return false; 68 69 SelectObject(meta_file_dc, font.GetNativeFont()); 70 71 SCRIPT_STRING_ANALYSIS script_analysis; 72 HRESULT hresult = 73 ScriptStringAnalyse(meta_file_dc, text, text_length, 0, -1, 74 SSA_METAFILE | SSA_FALLBACK | SSA_GLYPHS | SSA_LINK, 75 0, NULL, NULL, NULL, NULL, NULL, &script_analysis); 76 77 if (SUCCEEDED(hresult)) { 78 hresult = ScriptStringOut(script_analysis, 0, 0, 0, NULL, 0, 0, FALSE); 79 ScriptStringFree(&script_analysis); 80 } 81 82 bool found_fallback = false; 83 HENHMETAFILE meta_file = CloseEnhMetaFile(meta_file_dc); 84 if (SUCCEEDED(hresult)) { 85 LOGFONT log_font; 86 log_font.lfFaceName[0] = 0; 87 EnumEnhMetaFile(0, meta_file, MetaFileEnumProc, &log_font, NULL); 88 if (log_font.lfFaceName[0]) { 89 *result = Font(base::UTF16ToUTF8(log_font.lfFaceName), 90 font.GetFontSize()); 91 found_fallback = true; 92 } 93 } 94 DeleteEnhMetaFile(meta_file); 95 96 return found_fallback; 97 } 98 99 // Changes |font| to have the specified |font_size| (or |font_height| on Windows 100 // XP) and |font_style| if it is not the case already. Only considers bold and 101 // italic styles, since the underlined style has no effect on glyph shaping. 102 void DeriveFontIfNecessary(int font_size, 103 int font_height, 104 int font_style, 105 Font* font) { 106 const int kStyleMask = (Font::BOLD | Font::ITALIC); 107 const int target_style = (font_style & kStyleMask); 108 109 // On Windows XP, the font must be resized using |font_height| instead of 110 // |font_size| to match GDI behavior. 111 if (base::win::GetVersion() < base::win::VERSION_VISTA) { 112 PlatformFontWin* platform_font = 113 static_cast<PlatformFontWin*>(font->platform_font()); 114 *font = platform_font->DeriveFontWithHeight(font_height, target_style); 115 return; 116 } 117 118 const int current_style = (font->GetStyle() & kStyleMask); 119 const int current_size = font->GetFontSize(); 120 if (current_style != target_style || current_size != font_size) 121 *font = font->Derive(font_size - current_size, target_style); 122 } 123 124 // Returns true if |c| is a Unicode BiDi control character. 125 bool IsUnicodeBidiControlCharacter(base::char16 c) { 126 return c == base::i18n::kRightToLeftMark || 127 c == base::i18n::kLeftToRightMark || 128 c == base::i18n::kLeftToRightEmbeddingMark || 129 c == base::i18n::kRightToLeftEmbeddingMark || 130 c == base::i18n::kPopDirectionalFormatting || 131 c == base::i18n::kLeftToRightOverride || 132 c == base::i18n::kRightToLeftOverride; 133 } 134 135 // Returns the corresponding glyph range of the given character range. 136 // |range| is in text-space (0 corresponds to |GetLayoutText()[0]|). 137 // Returned value is in run-space (0 corresponds to the first glyph in the run). 138 Range CharRangeToGlyphRange(const internal::TextRun& run, 139 const Range& range) { 140 DCHECK(run.range.Contains(range)); 141 DCHECK(!range.is_reversed()); 142 DCHECK(!range.is_empty()); 143 const Range run_range(range.start() - run.range.start(), 144 range.end() - run.range.start()); 145 Range result; 146 if (run.script_analysis.fRTL) { 147 result = Range(run.logical_clusters[run_range.end() - 1], 148 run_range.start() > 0 ? run.logical_clusters[run_range.start() - 1] 149 : run.glyph_count); 150 } else { 151 result = Range(run.logical_clusters[run_range.start()], 152 run_range.end() < run.range.length() ? 153 run.logical_clusters[run_range.end()] : run.glyph_count); 154 } 155 DCHECK(!result.is_reversed()); 156 DCHECK(Range(0, run.glyph_count).Contains(result)); 157 return result; 158 } 159 160 // Starting from |start_char|, finds a suitable line break position at or before 161 // |available_width| using word break info from |breaks|. If |empty_line| is 162 // true, this function will not roll back to |start_char| and |*next_char| will 163 // be greater than |start_char| (to avoid constructing empty lines). Returns 164 // whether to skip the line before |*next_char|. 165 // TODO(ckocagil): Do not break ligatures and diacritics. 166 // TextRun::logical_clusters might help. 167 // TODO(ckocagil): We might have to reshape after breaking at ligatures. 168 // See whether resolving the TODO above resolves this too. 169 // TODO(ckocagil): Do not reserve width for whitespace at the end of lines. 170 bool BreakRunAtWidth(const wchar_t* text, 171 const internal::TextRun& run, 172 const BreakList<size_t>& breaks, 173 size_t start_char, 174 int available_width, 175 bool empty_line, 176 int* width, 177 size_t* next_char) { 178 DCHECK(run.range.Contains(Range(start_char, start_char + 1))); 179 BreakList<size_t>::const_iterator word = breaks.GetBreak(start_char); 180 BreakList<size_t>::const_iterator next_word = word + 1; 181 // Width from |std::max(word->first, start_char)| to the current character. 182 int word_width = 0; 183 *width = 0; 184 185 for (size_t i = start_char; i < run.range.end(); ++i) { 186 if (U16_IS_SINGLE(text[i]) && text[i] == L'\n') { 187 *next_char = i + 1; 188 return true; 189 } 190 191 // |word| holds the word boundary at or before |i|, and |next_word| holds 192 // the word boundary right after |i|. Advance both |word| and |next_word| 193 // when |i| reaches |next_word|. 194 if (next_word != breaks.breaks().end() && i >= next_word->first) { 195 word = next_word++; 196 word_width = 0; 197 } 198 199 Range glyph_range = CharRangeToGlyphRange(run, Range(i, i + 1)); 200 int char_width = 0; 201 for (size_t j = glyph_range.start(); j < glyph_range.end(); ++j) 202 char_width += run.advance_widths[j]; 203 204 *width += char_width; 205 word_width += char_width; 206 207 if (*width > available_width) { 208 if (!empty_line || word_width < *width) { 209 // Roll back one word. 210 *width -= word_width; 211 *next_char = std::max(word->first, start_char); 212 } else if (char_width < *width) { 213 // Roll back one character. 214 *width -= char_width; 215 *next_char = i; 216 } else { 217 // Continue from the next character. 218 *next_char = i + 1; 219 } 220 221 return true; 222 } 223 } 224 225 *next_char = run.range.end(); 226 return false; 227 } 228 229 // For segments in the same run, checks the continuity and order of |x_range| 230 // and |char_range| fields. 231 void CheckLineIntegrity(const std::vector<internal::Line>& lines, 232 const ScopedVector<internal::TextRun>& runs) { 233 size_t previous_segment_line = 0; 234 const internal::LineSegment* previous_segment = NULL; 235 236 for (size_t i = 0; i < lines.size(); ++i) { 237 for (size_t j = 0; j < lines[i].segments.size(); ++j) { 238 const internal::LineSegment* segment = &lines[i].segments[j]; 239 internal::TextRun* run = runs[segment->run]; 240 241 if (!previous_segment) { 242 previous_segment = segment; 243 } else if (runs[previous_segment->run] != run) { 244 previous_segment = NULL; 245 } else { 246 DCHECK_EQ(previous_segment->char_range.end(), 247 segment->char_range.start()); 248 if (!run->script_analysis.fRTL) { 249 DCHECK_EQ(previous_segment->x_range.end(), segment->x_range.start()); 250 } else { 251 DCHECK_EQ(segment->x_range.end(), previous_segment->x_range.start()); 252 } 253 254 previous_segment = segment; 255 previous_segment_line = i; 256 } 257 } 258 } 259 } 260 261 // Returns true if characters of |block_code| may trigger font fallback. 262 bool IsUnusualBlockCode(const UBlockCode block_code) { 263 return block_code == UBLOCK_GEOMETRIC_SHAPES || 264 block_code == UBLOCK_MISCELLANEOUS_SYMBOLS; 265 } 266 267 // Returns the index of the first unusual character after a usual character or 268 // vice versa. Unusual characters are defined by |IsUnusualBlockCode|. 269 size_t FindUnusualCharacter(const base::string16& text, 270 size_t run_start, 271 size_t run_break) { 272 const int32 run_length = static_cast<int32>(run_break - run_start); 273 base::i18n::UTF16CharIterator iter(text.c_str() + run_start, 274 run_length); 275 const UBlockCode first_block_code = ublock_getCode(iter.get()); 276 const bool first_block_unusual = IsUnusualBlockCode(first_block_code); 277 while (iter.Advance() && iter.array_pos() < run_length) { 278 const UBlockCode current_block_code = ublock_getCode(iter.get()); 279 if (current_block_code != first_block_code && 280 (first_block_unusual || IsUnusualBlockCode(current_block_code))) { 281 return run_start + iter.array_pos(); 282 } 283 } 284 return run_break; 285 } 286 287 } // namespace 288 289 namespace internal { 290 291 TextRun::TextRun() 292 : font_style(0), 293 strike(false), 294 diagonal_strike(false), 295 underline(false), 296 width(0), 297 preceding_run_widths(0), 298 glyph_count(0), 299 script_cache(NULL) { 300 memset(&script_analysis, 0, sizeof(script_analysis)); 301 memset(&abc_widths, 0, sizeof(abc_widths)); 302 } 303 304 TextRun::~TextRun() { 305 ScriptFreeCache(&script_cache); 306 } 307 308 // Returns the X coordinate of the leading or |trailing| edge of the glyph 309 // starting at |index|, relative to the left of the text (not the view). 310 int GetGlyphXBoundary(const internal::TextRun* run, 311 size_t index, 312 bool trailing) { 313 DCHECK_GE(index, run->range.start()); 314 DCHECK_LT(index, run->range.end() + (trailing ? 0 : 1)); 315 int x = 0; 316 HRESULT hr = ScriptCPtoX( 317 index - run->range.start(), 318 trailing, 319 run->range.length(), 320 run->glyph_count, 321 run->logical_clusters.get(), 322 run->visible_attributes.get(), 323 run->advance_widths.get(), 324 &run->script_analysis, 325 &x); 326 DCHECK(SUCCEEDED(hr)); 327 return run->preceding_run_widths + x; 328 } 329 330 // Internal class to generate Line structures. If |multiline| is true, the text 331 // is broken into lines at |words| boundaries such that each line is no longer 332 // than |max_width|. If |multiline| is false, only outputs a single Line from 333 // the given runs. |min_baseline| and |min_height| are the minimum baseline and 334 // height for each line. 335 // TODO(ckocagil): Expose the interface of this class in the header and test 336 // this class directly. 337 class LineBreaker { 338 public: 339 LineBreaker(int max_width, 340 int min_baseline, 341 int min_height, 342 bool multiline, 343 const wchar_t* text, 344 const BreakList<size_t>* words, 345 const ScopedVector<TextRun>& runs) 346 : max_width_(max_width), 347 min_baseline_(min_baseline), 348 min_height_(min_height), 349 multiline_(multiline), 350 text_(text), 351 words_(words), 352 runs_(runs), 353 text_x_(0), 354 line_x_(0), 355 line_ascent_(0), 356 line_descent_(0) { 357 AdvanceLine(); 358 } 359 360 // Breaks the run at given |run_index| into Line structs. 361 void AddRun(int run_index) { 362 const TextRun* run = runs_[run_index]; 363 bool run_fits = !multiline_; 364 if (multiline_ && line_x_ + run->width <= max_width_) { 365 DCHECK(!run->range.is_empty()); 366 const wchar_t first_char = text_[run->range.start()]; 367 // Uniscribe always puts newline characters in their own runs. 368 if (!U16_IS_SINGLE(first_char) || first_char != L'\n') 369 run_fits = true; 370 } 371 372 if (!run_fits) 373 BreakRun(run_index); 374 else 375 AddSegment(run_index, run->range, run->width); 376 } 377 378 // Finishes line breaking and outputs the results. Can be called at most once. 379 void Finalize(std::vector<Line>* lines, Size* size) { 380 DCHECK(!lines_.empty()); 381 // Add an empty line to finish the line size calculation and remove it. 382 AdvanceLine(); 383 lines_.pop_back(); 384 *size = total_size_; 385 lines->swap(lines_); 386 } 387 388 private: 389 // A (line index, segment index) pair that specifies a segment in |lines_|. 390 typedef std::pair<size_t, size_t> SegmentHandle; 391 392 LineSegment* SegmentFromHandle(const SegmentHandle& handle) { 393 return &lines_[handle.first].segments[handle.second]; 394 } 395 396 // Breaks a run into segments that fit in the last line in |lines_| and adds 397 // them. Adds a new Line to the back of |lines_| whenever a new segment can't 398 // be added without the Line's width exceeding |max_width_|. 399 void BreakRun(int run_index) { 400 DCHECK(words_); 401 const TextRun* const run = runs_[run_index]; 402 int width = 0; 403 size_t next_char = run->range.start(); 404 405 // Break the run until it fits the current line. 406 while (next_char < run->range.end()) { 407 const size_t current_char = next_char; 408 const bool skip_line = BreakRunAtWidth(text_, *run, *words_, current_char, 409 max_width_ - line_x_, line_x_ == 0, &width, &next_char); 410 AddSegment(run_index, Range(current_char, next_char), width); 411 if (skip_line) 412 AdvanceLine(); 413 } 414 } 415 416 // RTL runs are broken in logical order but displayed in visual order. To find 417 // the text-space coordinate (where it would fall in a single-line text) 418 // |x_range| of RTL segments, segment widths are applied in reverse order. 419 // e.g. {[5, 10], [10, 40]} will become {[35, 40], [5, 35]}. 420 void UpdateRTLSegmentRanges() { 421 if (rtl_segments_.empty()) 422 return; 423 int x = SegmentFromHandle(rtl_segments_[0])->x_range.start(); 424 for (size_t i = rtl_segments_.size(); i > 0; --i) { 425 LineSegment* segment = SegmentFromHandle(rtl_segments_[i - 1]); 426 const size_t segment_width = segment->x_range.length(); 427 segment->x_range = Range(x, x + segment_width); 428 x += segment_width; 429 } 430 rtl_segments_.clear(); 431 } 432 433 // Finishes the size calculations of the last Line in |lines_|. Adds a new 434 // Line to the back of |lines_|. 435 void AdvanceLine() { 436 if (!lines_.empty()) { 437 Line* line = &lines_.back(); 438 // TODO(ckocagil): Determine optimal multiline height behavior. 439 if (line_ascent_ + line_descent_ == 0) { 440 line_ascent_ = min_baseline_; 441 line_descent_ = min_height_ - min_baseline_; 442 } 443 // Set the single-line mode Line's metrics to be at least 444 // |RenderText::font_list()| to not break the current single-line code. 445 line_ascent_ = std::max(line_ascent_, min_baseline_); 446 line_descent_ = std::max(line_descent_, min_height_ - min_baseline_); 447 448 line->baseline = line_ascent_; 449 line->size.set_height(line_ascent_ + line_descent_); 450 line->preceding_heights = total_size_.height(); 451 total_size_.set_height(total_size_.height() + line->size.height()); 452 total_size_.set_width(std::max(total_size_.width(), line->size.width())); 453 } 454 line_x_ = 0; 455 line_ascent_ = 0; 456 line_descent_ = 0; 457 lines_.push_back(Line()); 458 } 459 460 // Adds a new segment with the given properties to |lines_.back()|. 461 void AddSegment(int run_index, Range char_range, int width) { 462 if (char_range.is_empty()) { 463 DCHECK_EQ(width, 0); 464 return; 465 } 466 const TextRun* run = runs_[run_index]; 467 line_ascent_ = std::max(line_ascent_, run->font.GetBaseline()); 468 line_descent_ = std::max(line_descent_, 469 run->font.GetHeight() - run->font.GetBaseline()); 470 471 LineSegment segment; 472 segment.run = run_index; 473 segment.char_range = char_range; 474 segment.x_range = Range(text_x_, text_x_ + width); 475 476 Line* line = &lines_.back(); 477 line->segments.push_back(segment); 478 line->size.set_width(line->size.width() + segment.x_range.length()); 479 if (run->script_analysis.fRTL) { 480 rtl_segments_.push_back(SegmentHandle(lines_.size() - 1, 481 line->segments.size() - 1)); 482 // If this is the last segment of an RTL run, reprocess the text-space x 483 // ranges of all segments from the run. 484 if (char_range.end() == run->range.end()) 485 UpdateRTLSegmentRanges(); 486 } 487 text_x_ += width; 488 line_x_ += width; 489 } 490 491 const int max_width_; 492 const int min_baseline_; 493 const int min_height_; 494 const bool multiline_; 495 const wchar_t* text_; 496 const BreakList<size_t>* const words_; 497 const ScopedVector<TextRun>& runs_; 498 499 // Stores the resulting lines. 500 std::vector<Line> lines_; 501 502 // Text space and line space x coordinates of the next segment to be added. 503 int text_x_; 504 int line_x_; 505 506 // Size of the multiline text, not including the currently processed line. 507 Size total_size_; 508 509 // Ascent and descent values of the current line, |lines_.back()|. 510 int line_ascent_; 511 int line_descent_; 512 513 // The current RTL run segments, to be applied by |UpdateRTLSegmentRanges()|. 514 std::vector<SegmentHandle> rtl_segments_; 515 516 DISALLOW_COPY_AND_ASSIGN(LineBreaker); 517 }; 518 519 } // namespace internal 520 521 // static 522 HDC RenderTextWin::cached_hdc_ = NULL; 523 524 // static 525 std::map<std::string, Font> RenderTextWin::successful_substitute_fonts_; 526 527 RenderTextWin::RenderTextWin() : RenderText(), needs_layout_(false) { 528 set_truncate_length(kMaxUniscribeTextLength); 529 memset(&script_control_, 0, sizeof(script_control_)); 530 memset(&script_state_, 0, sizeof(script_state_)); 531 MoveCursorTo(EdgeSelectionModel(CURSOR_LEFT)); 532 } 533 534 RenderTextWin::~RenderTextWin() {} 535 536 Size RenderTextWin::GetStringSize() { 537 EnsureLayout(); 538 return multiline_string_size_; 539 } 540 541 SelectionModel RenderTextWin::FindCursorPosition(const Point& point) { 542 if (text().empty()) 543 return SelectionModel(); 544 545 EnsureLayout(); 546 // Find the run that contains the point and adjust the argument location. 547 int x = ToTextPoint(point).x(); 548 size_t run_index = GetRunContainingXCoord(x); 549 if (run_index >= runs_.size()) 550 return EdgeSelectionModel((x < 0) ? CURSOR_LEFT : CURSOR_RIGHT); 551 internal::TextRun* run = runs_[run_index]; 552 553 int position = 0, trailing = 0; 554 HRESULT hr = ScriptXtoCP(x - run->preceding_run_widths, 555 run->range.length(), 556 run->glyph_count, 557 run->logical_clusters.get(), 558 run->visible_attributes.get(), 559 run->advance_widths.get(), 560 &(run->script_analysis), 561 &position, 562 &trailing); 563 DCHECK(SUCCEEDED(hr)); 564 DCHECK_GE(trailing, 0); 565 position += run->range.start(); 566 const size_t cursor = LayoutIndexToTextIndex(position + trailing); 567 DCHECK_LE(cursor, text().length()); 568 return SelectionModel(cursor, trailing ? CURSOR_BACKWARD : CURSOR_FORWARD); 569 } 570 571 std::vector<RenderText::FontSpan> RenderTextWin::GetFontSpansForTesting() { 572 EnsureLayout(); 573 574 std::vector<RenderText::FontSpan> spans; 575 for (size_t i = 0; i < runs_.size(); ++i) { 576 spans.push_back(RenderText::FontSpan(runs_[i]->font, 577 Range(LayoutIndexToTextIndex(runs_[i]->range.start()), 578 LayoutIndexToTextIndex(runs_[i]->range.end())))); 579 } 580 581 return spans; 582 } 583 584 int RenderTextWin::GetLayoutTextBaseline() { 585 EnsureLayout(); 586 return lines()[0].baseline; 587 } 588 589 SelectionModel RenderTextWin::AdjacentCharSelectionModel( 590 const SelectionModel& selection, 591 VisualCursorDirection direction) { 592 DCHECK(!needs_layout_); 593 internal::TextRun* run; 594 size_t run_index = GetRunContainingCaret(selection); 595 if (run_index >= runs_.size()) { 596 // The cursor is not in any run: we're at the visual and logical edge. 597 SelectionModel edge = EdgeSelectionModel(direction); 598 if (edge.caret_pos() == selection.caret_pos()) 599 return edge; 600 int visual_index = (direction == CURSOR_RIGHT) ? 0 : runs_.size() - 1; 601 run = runs_[visual_to_logical_[visual_index]]; 602 } else { 603 // If the cursor is moving within the current run, just move it by one 604 // grapheme in the appropriate direction. 605 run = runs_[run_index]; 606 size_t caret = selection.caret_pos(); 607 bool forward_motion = 608 run->script_analysis.fRTL == (direction == CURSOR_LEFT); 609 if (forward_motion) { 610 if (caret < LayoutIndexToTextIndex(run->range.end())) { 611 caret = IndexOfAdjacentGrapheme(caret, CURSOR_FORWARD); 612 return SelectionModel(caret, CURSOR_BACKWARD); 613 } 614 } else { 615 if (caret > LayoutIndexToTextIndex(run->range.start())) { 616 caret = IndexOfAdjacentGrapheme(caret, CURSOR_BACKWARD); 617 return SelectionModel(caret, CURSOR_FORWARD); 618 } 619 } 620 // The cursor is at the edge of a run; move to the visually adjacent run. 621 int visual_index = logical_to_visual_[run_index]; 622 visual_index += (direction == CURSOR_LEFT) ? -1 : 1; 623 if (visual_index < 0 || visual_index >= static_cast<int>(runs_.size())) 624 return EdgeSelectionModel(direction); 625 run = runs_[visual_to_logical_[visual_index]]; 626 } 627 bool forward_motion = run->script_analysis.fRTL == (direction == CURSOR_LEFT); 628 return forward_motion ? FirstSelectionModelInsideRun(run) : 629 LastSelectionModelInsideRun(run); 630 } 631 632 // TODO(msw): Implement word breaking for Windows. 633 SelectionModel RenderTextWin::AdjacentWordSelectionModel( 634 const SelectionModel& selection, 635 VisualCursorDirection direction) { 636 if (obscured()) 637 return EdgeSelectionModel(direction); 638 639 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD); 640 bool success = iter.Init(); 641 DCHECK(success); 642 if (!success) 643 return selection; 644 645 size_t pos; 646 if (direction == CURSOR_RIGHT) { 647 pos = std::min(selection.caret_pos() + 1, text().length()); 648 while (iter.Advance()) { 649 pos = iter.pos(); 650 if (iter.IsWord() && pos > selection.caret_pos()) 651 break; 652 } 653 } else { // direction == CURSOR_LEFT 654 // Notes: We always iterate words from the beginning. 655 // This is probably fast enough for our usage, but we may 656 // want to modify WordIterator so that it can start from the 657 // middle of string and advance backwards. 658 pos = std::max<int>(selection.caret_pos() - 1, 0); 659 while (iter.Advance()) { 660 if (iter.IsWord()) { 661 size_t begin = iter.pos() - iter.GetString().length(); 662 if (begin == selection.caret_pos()) { 663 // The cursor is at the beginning of a word. 664 // Move to previous word. 665 break; 666 } else if (iter.pos() >= selection.caret_pos()) { 667 // The cursor is in the middle or at the end of a word. 668 // Move to the top of current word. 669 pos = begin; 670 break; 671 } else { 672 pos = iter.pos() - iter.GetString().length(); 673 } 674 } 675 } 676 } 677 return SelectionModel(pos, CURSOR_FORWARD); 678 } 679 680 Range RenderTextWin::GetGlyphBounds(size_t index) { 681 EnsureLayout(); 682 const size_t run_index = 683 GetRunContainingCaret(SelectionModel(index, CURSOR_FORWARD)); 684 // Return edge bounds if the index is invalid or beyond the layout text size. 685 if (run_index >= runs_.size()) 686 return Range(string_width_); 687 internal::TextRun* run = runs_[run_index]; 688 const size_t layout_index = TextIndexToLayoutIndex(index); 689 return Range(GetGlyphXBoundary(run, layout_index, false), 690 GetGlyphXBoundary(run, layout_index, true)); 691 } 692 693 std::vector<Rect> RenderTextWin::GetSubstringBounds(const Range& range) { 694 DCHECK(!needs_layout_); 695 DCHECK(Range(0, text().length()).Contains(range)); 696 Range layout_range(TextIndexToLayoutIndex(range.start()), 697 TextIndexToLayoutIndex(range.end())); 698 DCHECK(Range(0, GetLayoutText().length()).Contains(layout_range)); 699 700 std::vector<Rect> rects; 701 if (layout_range.is_empty()) 702 return rects; 703 std::vector<Range> bounds; 704 705 // Add a Range for each run/selection intersection. 706 // TODO(msw): The bounds should probably not always be leading the range ends. 707 for (size_t i = 0; i < runs_.size(); ++i) { 708 const internal::TextRun* run = runs_[visual_to_logical_[i]]; 709 Range intersection = run->range.Intersect(layout_range); 710 if (intersection.IsValid()) { 711 DCHECK(!intersection.is_reversed()); 712 Range range_x(GetGlyphXBoundary(run, intersection.start(), false), 713 GetGlyphXBoundary(run, intersection.end(), false)); 714 if (range_x.is_empty()) 715 continue; 716 range_x = Range(range_x.GetMin(), range_x.GetMax()); 717 // Union this with the last range if they're adjacent. 718 DCHECK(bounds.empty() || bounds.back().GetMax() <= range_x.GetMin()); 719 if (!bounds.empty() && bounds.back().GetMax() == range_x.GetMin()) { 720 range_x = Range(bounds.back().GetMin(), range_x.GetMax()); 721 bounds.pop_back(); 722 } 723 bounds.push_back(range_x); 724 } 725 } 726 for (size_t i = 0; i < bounds.size(); ++i) { 727 std::vector<Rect> current_rects = TextBoundsToViewBounds(bounds[i]); 728 rects.insert(rects.end(), current_rects.begin(), current_rects.end()); 729 } 730 return rects; 731 } 732 733 size_t RenderTextWin::TextIndexToLayoutIndex(size_t index) const { 734 DCHECK_LE(index, text().length()); 735 ptrdiff_t i = obscured() ? UTF16IndexToOffset(text(), 0, index) : index; 736 CHECK_GE(i, 0); 737 // Clamp layout indices to the length of the text actually used for layout. 738 return std::min<size_t>(GetLayoutText().length(), i); 739 } 740 741 size_t RenderTextWin::LayoutIndexToTextIndex(size_t index) const { 742 if (!obscured()) 743 return index; 744 745 DCHECK_LE(index, GetLayoutText().length()); 746 const size_t text_index = UTF16OffsetToIndex(text(), 0, index); 747 DCHECK_LE(text_index, text().length()); 748 return text_index; 749 } 750 751 bool RenderTextWin::IsValidCursorIndex(size_t index) { 752 if (index == 0 || index == text().length()) 753 return true; 754 if (!IsValidLogicalIndex(index)) 755 return false; 756 EnsureLayout(); 757 // Disallow indices amid multi-character graphemes by checking glyph bounds. 758 // These characters are not surrogate-pairs, but may yield a single glyph: 759 // \x0915\x093f - (ki) - one of many Devanagari biconsonantal conjuncts. 760 // \x0e08\x0e33 - (cho chan + sara am) - a Thai consonant and vowel pair. 761 return GetGlyphBounds(index) != GetGlyphBounds(index - 1); 762 } 763 764 void RenderTextWin::ResetLayout() { 765 // Layout is performed lazily as needed for drawing/metrics. 766 needs_layout_ = true; 767 } 768 769 void RenderTextWin::EnsureLayout() { 770 if (needs_layout_) { 771 // TODO(msw): Skip complex processing if ScriptIsComplex returns false. 772 ItemizeLogicalText(); 773 if (!runs_.empty()) 774 LayoutVisualText(); 775 needs_layout_ = false; 776 std::vector<internal::Line> lines; 777 set_lines(&lines); 778 } 779 780 // Compute lines if they're not valid. This is separate from the layout steps 781 // above to avoid text layout and shaping when we resize |display_rect_|. 782 if (lines().empty()) { 783 DCHECK(!needs_layout_); 784 std::vector<internal::Line> lines; 785 internal::LineBreaker line_breaker(display_rect().width() - 1, 786 font_list().GetBaseline(), 787 font_list().GetHeight(), multiline(), 788 GetLayoutText().c_str(), 789 multiline() ? &GetLineBreaks() : NULL, 790 runs_); 791 for (size_t i = 0; i < runs_.size(); ++i) 792 line_breaker.AddRun(visual_to_logical_[i]); 793 line_breaker.Finalize(&lines, &multiline_string_size_); 794 DCHECK(!lines.empty()); 795 #ifndef NDEBUG 796 CheckLineIntegrity(lines, runs_); 797 #endif 798 set_lines(&lines); 799 } 800 } 801 802 void RenderTextWin::DrawVisualText(Canvas* canvas) { 803 DCHECK(!needs_layout_); 804 DCHECK(!lines().empty()); 805 806 std::vector<SkPoint> pos; 807 808 internal::SkiaTextRenderer renderer(canvas); 809 ApplyFadeEffects(&renderer); 810 ApplyTextShadows(&renderer); 811 812 bool smoothing_enabled; 813 bool cleartype_enabled; 814 GetCachedFontSmoothingSettings(&smoothing_enabled, &cleartype_enabled); 815 // Note that |cleartype_enabled| corresponds to Skia's |enable_lcd_text|. 816 renderer.SetFontSmoothingSettings( 817 smoothing_enabled, cleartype_enabled && !background_is_transparent(), 818 smoothing_enabled /* subpixel_positioning */); 819 820 ApplyCompositionAndSelectionStyles(); 821 822 for (size_t i = 0; i < lines().size(); ++i) { 823 const internal::Line& line = lines()[i]; 824 const Vector2d line_offset = GetLineOffset(i); 825 826 // Skip painting empty lines or lines outside the display rect area. 827 if (!display_rect().Intersects(Rect(PointAtOffsetFromOrigin(line_offset), 828 line.size))) 829 continue; 830 831 const Vector2d text_offset = line_offset + Vector2d(0, line.baseline); 832 int preceding_segment_widths = 0; 833 834 for (size_t j = 0; j < line.segments.size(); ++j) { 835 const internal::LineSegment* segment = &line.segments[j]; 836 const int segment_width = segment->x_range.length(); 837 const internal::TextRun* run = runs_[segment->run]; 838 DCHECK(!segment->char_range.is_empty()); 839 DCHECK(run->range.Contains(segment->char_range)); 840 Range glyph_range = CharRangeToGlyphRange(*run, segment->char_range); 841 DCHECK(!glyph_range.is_empty()); 842 // Skip painting segments outside the display rect area. 843 if (!multiline()) { 844 const Rect segment_bounds(PointAtOffsetFromOrigin(line_offset) + 845 Vector2d(preceding_segment_widths, 0), 846 Size(segment_width, line.size.height())); 847 if (!display_rect().Intersects(segment_bounds)) { 848 preceding_segment_widths += segment_width; 849 continue; 850 } 851 } 852 853 // |pos| contains the positions of glyphs. An extra terminal |pos| entry 854 // is added to simplify width calculations. 855 int segment_x = preceding_segment_widths; 856 pos.resize(glyph_range.length() + 1); 857 for (size_t k = glyph_range.start(); k < glyph_range.end(); ++k) { 858 pos[k - glyph_range.start()].set( 859 SkIntToScalar(text_offset.x() + run->offsets[k].du + segment_x), 860 SkIntToScalar(text_offset.y() - run->offsets[k].dv)); 861 segment_x += run->advance_widths[k]; 862 } 863 pos.back().set(SkIntToScalar(text_offset.x() + segment_x), 864 SkIntToScalar(text_offset.y())); 865 866 renderer.SetTextSize(run->font.GetFontSize()); 867 renderer.SetFontFamilyWithStyle(run->font.GetFontName(), run->font_style); 868 869 for (BreakList<SkColor>::const_iterator it = 870 colors().GetBreak(segment->char_range.start()); 871 it != colors().breaks().end() && 872 it->first < segment->char_range.end(); 873 ++it) { 874 const Range intersection = 875 colors().GetRange(it).Intersect(segment->char_range); 876 const Range colored_glyphs = CharRangeToGlyphRange(*run, intersection); 877 // The range may be empty if a portion of a multi-character grapheme is 878 // selected, yielding two colors for a single glyph. For now, this just 879 // paints the glyph with a single style, but it should paint it twice, 880 // clipped according to selection bounds. See http://crbug.com/366786 881 if (colored_glyphs.is_empty()) 882 continue; 883 DCHECK(glyph_range.Contains(colored_glyphs)); 884 const SkPoint& start_pos = 885 pos[colored_glyphs.start() - glyph_range.start()]; 886 const SkPoint& end_pos = 887 pos[colored_glyphs.end() - glyph_range.start()]; 888 889 renderer.SetForegroundColor(it->second); 890 renderer.DrawPosText(&start_pos, &run->glyphs[colored_glyphs.start()], 891 colored_glyphs.length()); 892 renderer.DrawDecorations(start_pos.x(), text_offset.y(), 893 SkScalarCeilToInt(end_pos.x() - start_pos.x()), 894 run->underline, run->strike, 895 run->diagonal_strike); 896 } 897 898 preceding_segment_widths += segment_width; 899 } 900 901 renderer.EndDiagonalStrike(); 902 } 903 904 UndoCompositionAndSelectionStyles(); 905 } 906 907 void RenderTextWin::ItemizeLogicalText() { 908 runs_.clear(); 909 string_width_ = 0; 910 multiline_string_size_ = Size(); 911 912 // Set Uniscribe's base text direction. 913 script_state_.uBidiLevel = 914 (GetTextDirection() == base::i18n::RIGHT_TO_LEFT) ? 1 : 0; 915 916 const base::string16& layout_text = GetLayoutText(); 917 if (layout_text.empty()) 918 return; 919 920 HRESULT hr = E_OUTOFMEMORY; 921 int script_items_count = 0; 922 std::vector<SCRIPT_ITEM> script_items; 923 const size_t layout_text_length = layout_text.length(); 924 // Ensure that |kMaxRuns| is attempted and the loop terminates afterward. 925 for (size_t runs = kGuessRuns; hr == E_OUTOFMEMORY && runs <= kMaxRuns; 926 runs = std::max(runs + 1, std::min(runs * 2, kMaxRuns))) { 927 // Derive the array of Uniscribe script items from the logical text. 928 // ScriptItemize always adds a terminal array item so that the length of 929 // the last item can be derived from the terminal SCRIPT_ITEM::iCharPos. 930 script_items.resize(runs); 931 hr = ScriptItemize(layout_text.c_str(), layout_text_length, runs - 1, 932 &script_control_, &script_state_, &script_items[0], 933 &script_items_count); 934 } 935 DCHECK(SUCCEEDED(hr)); 936 if (!SUCCEEDED(hr) || script_items_count <= 0) 937 return; 938 939 // Temporarily apply composition underlines and selection colors. 940 ApplyCompositionAndSelectionStyles(); 941 942 // Build the list of runs from the script items and ranged styles. Use an 943 // empty color BreakList to avoid breaking runs at color boundaries. 944 BreakList<SkColor> empty_colors; 945 empty_colors.SetMax(layout_text_length); 946 internal::StyleIterator style(empty_colors, styles()); 947 SCRIPT_ITEM* script_item = &script_items[0]; 948 const size_t max_run_length = kMaxGlyphs / 2; 949 for (size_t run_break = 0; run_break < layout_text_length;) { 950 internal::TextRun* run = new internal::TextRun(); 951 run->range.set_start(run_break); 952 run->font = font_list().GetPrimaryFont(); 953 run->font_style = (style.style(BOLD) ? Font::BOLD : 0) | 954 (style.style(ITALIC) ? Font::ITALIC : 0); 955 DeriveFontIfNecessary(run->font.GetFontSize(), run->font.GetHeight(), 956 run->font_style, &run->font); 957 run->strike = style.style(STRIKE); 958 run->diagonal_strike = style.style(DIAGONAL_STRIKE); 959 run->underline = style.style(UNDERLINE); 960 run->script_analysis = script_item->a; 961 962 // Find the next break and advance the iterators as needed. 963 const size_t script_item_break = (script_item + 1)->iCharPos; 964 run_break = std::min(script_item_break, 965 TextIndexToLayoutIndex(style.GetRange().end())); 966 967 // Clamp run lengths to avoid exceeding the maximum supported glyph count. 968 if ((run_break - run->range.start()) > max_run_length) { 969 run_break = run->range.start() + max_run_length; 970 if (!IsValidCodePointIndex(layout_text, run_break)) 971 --run_break; 972 } 973 974 // Break runs adjacent to character substrings in certain code blocks. 975 // This avoids using their fallback fonts for more characters than needed, 976 // in cases like "\x25B6 Media Title", etc. http://crbug.com/278913 977 if (run_break > run->range.start()) { 978 run_break = 979 FindUnusualCharacter(layout_text, run->range.start(), run_break); 980 } 981 982 DCHECK(IsValidCodePointIndex(layout_text, run_break)); 983 984 style.UpdatePosition(LayoutIndexToTextIndex(run_break)); 985 if (script_item_break == run_break) 986 script_item++; 987 run->range.set_end(run_break); 988 runs_.push_back(run); 989 } 990 991 // Undo the temporarily applied composition underlines and selection colors. 992 UndoCompositionAndSelectionStyles(); 993 } 994 995 void RenderTextWin::LayoutVisualText() { 996 DCHECK(!runs_.empty()); 997 998 if (!cached_hdc_) 999 cached_hdc_ = CreateCompatibleDC(NULL); 1000 1001 HRESULT hr = E_FAIL; 1002 // Ensure ascent and descent are not smaller than ones of the font list. 1003 // Keep them tall enough to draw often-used characters. 1004 // For example, if a text field contains a Japanese character, which is 1005 // smaller than Latin ones, and then later a Latin one is inserted, this 1006 // ensures that the text baseline does not shift. 1007 int ascent = font_list().GetBaseline(); 1008 int descent = font_list().GetHeight() - font_list().GetBaseline(); 1009 for (size_t i = 0; i < runs_.size(); ++i) { 1010 internal::TextRun* run = runs_[i]; 1011 LayoutTextRun(run); 1012 1013 ascent = std::max(ascent, run->font.GetBaseline()); 1014 descent = std::max(descent, 1015 run->font.GetHeight() - run->font.GetBaseline()); 1016 1017 if (run->glyph_count > 0) { 1018 run->advance_widths.reset(new int[run->glyph_count]); 1019 run->offsets.reset(new GOFFSET[run->glyph_count]); 1020 hr = ScriptPlace(cached_hdc_, 1021 &run->script_cache, 1022 run->glyphs.get(), 1023 run->glyph_count, 1024 run->visible_attributes.get(), 1025 &(run->script_analysis), 1026 run->advance_widths.get(), 1027 run->offsets.get(), 1028 &(run->abc_widths)); 1029 DCHECK(SUCCEEDED(hr)); 1030 } 1031 } 1032 1033 // Build the array of bidirectional embedding levels. 1034 scoped_ptr<BYTE[]> levels(new BYTE[runs_.size()]); 1035 for (size_t i = 0; i < runs_.size(); ++i) 1036 levels[i] = runs_[i]->script_analysis.s.uBidiLevel; 1037 1038 // Get the maps between visual and logical run indices. 1039 visual_to_logical_.reset(new int[runs_.size()]); 1040 logical_to_visual_.reset(new int[runs_.size()]); 1041 hr = ScriptLayout(runs_.size(), 1042 levels.get(), 1043 visual_to_logical_.get(), 1044 logical_to_visual_.get()); 1045 DCHECK(SUCCEEDED(hr)); 1046 1047 // Precalculate run width information. 1048 size_t preceding_run_widths = 0; 1049 for (size_t i = 0; i < runs_.size(); ++i) { 1050 internal::TextRun* run = runs_[visual_to_logical_[i]]; 1051 run->preceding_run_widths = preceding_run_widths; 1052 const ABC& abc = run->abc_widths; 1053 run->width = abc.abcA + abc.abcB + abc.abcC; 1054 preceding_run_widths += run->width; 1055 } 1056 string_width_ = preceding_run_widths; 1057 } 1058 1059 void RenderTextWin::LayoutTextRun(internal::TextRun* run) { 1060 const size_t run_length = run->range.length(); 1061 const wchar_t* run_text = &(GetLayoutText()[run->range.start()]); 1062 Font original_font = run->font; 1063 LinkedFontsIterator fonts(original_font); 1064 bool tried_cached_font = false; 1065 bool tried_fallback = false; 1066 // Keep track of the font that is able to display the greatest number of 1067 // characters for which ScriptShape() returned S_OK. This font will be used 1068 // in the case where no font is able to display the entire run. 1069 int best_partial_font_missing_char_count = INT_MAX; 1070 Font best_partial_font = original_font; 1071 Font current_font; 1072 1073 run->logical_clusters.reset(new WORD[run_length]); 1074 while (fonts.NextFont(¤t_font)) { 1075 HRESULT hr = ShapeTextRunWithFont(run, current_font); 1076 1077 bool glyphs_missing = false; 1078 if (hr == USP_E_SCRIPT_NOT_IN_FONT) { 1079 glyphs_missing = true; 1080 } else if (hr == S_OK) { 1081 // If |hr| is S_OK, there could still be missing glyphs in the output. 1082 // http://msdn.microsoft.com/en-us/library/windows/desktop/dd368564.aspx 1083 const int missing_count = CountCharsWithMissingGlyphs(run); 1084 // Track the font that produced the least missing glyphs. 1085 if (missing_count < best_partial_font_missing_char_count) { 1086 best_partial_font_missing_char_count = missing_count; 1087 best_partial_font = run->font; 1088 } 1089 glyphs_missing = (missing_count != 0); 1090 } else { 1091 NOTREACHED() << hr; 1092 } 1093 1094 // Use the font if it had glyphs for all characters. 1095 if (!glyphs_missing) { 1096 // Save the successful fallback font that was chosen. 1097 if (tried_fallback) 1098 successful_substitute_fonts_[original_font.GetFontName()] = run->font; 1099 return; 1100 } 1101 1102 // First, try the cached font from previous runs, if any. 1103 if (!tried_cached_font) { 1104 tried_cached_font = true; 1105 1106 std::map<std::string, Font>::const_iterator it = 1107 successful_substitute_fonts_.find(original_font.GetFontName()); 1108 if (it != successful_substitute_fonts_.end()) { 1109 fonts.SetNextFont(it->second); 1110 continue; 1111 } 1112 } 1113 1114 // If there are missing glyphs, first try finding a fallback font using a 1115 // meta file, if it hasn't yet been attempted for this run. 1116 // TODO(msw|asvitkine): Support RenderText's font_list()? 1117 if (!tried_fallback) { 1118 tried_fallback = true; 1119 1120 Font fallback_font; 1121 if (ChooseFallbackFont(cached_hdc_, run->font, run_text, run_length, 1122 &fallback_font)) { 1123 fonts.SetNextFont(fallback_font); 1124 continue; 1125 } 1126 } 1127 } 1128 1129 // If a font was able to partially display the run, use that now. 1130 if (best_partial_font_missing_char_count < static_cast<int>(run_length)) { 1131 // Re-shape the run only if |best_partial_font| differs from the last font. 1132 if (best_partial_font.GetNativeFont() != run->font.GetNativeFont()) 1133 ShapeTextRunWithFont(run, best_partial_font); 1134 return; 1135 } 1136 1137 // If no font was able to partially display the run, replace all glyphs 1138 // with |wgDefault| from the original font to ensure to they don't hold 1139 // garbage values. 1140 // First, clear the cache and select the original font on the HDC. 1141 ScriptFreeCache(&run->script_cache); 1142 run->font = original_font; 1143 SelectObject(cached_hdc_, run->font.GetNativeFont()); 1144 1145 // Now, get the font's properties. 1146 SCRIPT_FONTPROPERTIES properties; 1147 memset(&properties, 0, sizeof(properties)); 1148 properties.cBytes = sizeof(properties); 1149 HRESULT hr = ScriptGetFontProperties(cached_hdc_, &run->script_cache, 1150 &properties); 1151 1152 // The initial values for the "missing" glyph and the space glyph are taken 1153 // from the recommendations section of the OpenType spec: 1154 // https://www.microsoft.com/typography/otspec/recom.htm 1155 WORD missing_glyph = 0; 1156 WORD space_glyph = 3; 1157 if (hr == S_OK) { 1158 missing_glyph = properties.wgDefault; 1159 space_glyph = properties.wgBlank; 1160 } 1161 1162 // Finally, initialize |glyph_count|, |glyphs|, |visible_attributes| and 1163 // |logical_clusters| on the run (since they may not have been set yet). 1164 run->glyph_count = run_length; 1165 memset(run->visible_attributes.get(), 0, 1166 run->glyph_count * sizeof(SCRIPT_VISATTR)); 1167 for (int i = 0; i < run->glyph_count; ++i) 1168 run->glyphs[i] = IsWhitespace(run_text[i]) ? space_glyph : missing_glyph; 1169 for (size_t i = 0; i < run_length; ++i) { 1170 run->logical_clusters[i] = run->script_analysis.fRTL ? 1171 run_length - 1 - i : i; 1172 } 1173 1174 // TODO(msw): Don't use SCRIPT_UNDEFINED. Apparently Uniscribe can 1175 // crash on certain surrogate pairs with SCRIPT_UNDEFINED. 1176 // See https://bugzilla.mozilla.org/show_bug.cgi?id=341500 1177 // And http://maxradi.us/documents/uniscribe/ 1178 run->script_analysis.eScript = SCRIPT_UNDEFINED; 1179 } 1180 1181 HRESULT RenderTextWin::ShapeTextRunWithFont(internal::TextRun* run, 1182 const Font& font) { 1183 // Update the run's font only if necessary. If the two fonts wrap the same 1184 // PlatformFontWin object, their native fonts will have the same value. 1185 if (run->font.GetNativeFont() != font.GetNativeFont()) { 1186 const int font_size = run->font.GetFontSize(); 1187 const int font_height = run->font.GetHeight(); 1188 run->font = font; 1189 DeriveFontIfNecessary(font_size, font_height, run->font_style, &run->font); 1190 ScriptFreeCache(&run->script_cache); 1191 } 1192 1193 // Select the font desired for glyph generation. 1194 SelectObject(cached_hdc_, run->font.GetNativeFont()); 1195 1196 HRESULT hr = E_OUTOFMEMORY; 1197 const size_t run_length = run->range.length(); 1198 const wchar_t* run_text = &(GetLayoutText()[run->range.start()]); 1199 // Guess the expected number of glyphs from the length of the run. 1200 // MSDN suggests this at http://msdn.microsoft.com/en-us/library/dd368564.aspx 1201 size_t max_glyphs = static_cast<size_t>(1.5 * run_length + 16); 1202 while (hr == E_OUTOFMEMORY && max_glyphs <= kMaxGlyphs) { 1203 run->glyph_count = 0; 1204 run->glyphs.reset(new WORD[max_glyphs]); 1205 run->visible_attributes.reset(new SCRIPT_VISATTR[max_glyphs]); 1206 hr = ScriptShape(cached_hdc_, &run->script_cache, run_text, run_length, 1207 max_glyphs, &run->script_analysis, run->glyphs.get(), 1208 run->logical_clusters.get(), run->visible_attributes.get(), 1209 &run->glyph_count); 1210 // Ensure that |kMaxGlyphs| is attempted and the loop terminates afterward. 1211 max_glyphs = std::max(max_glyphs + 1, std::min(max_glyphs * 2, kMaxGlyphs)); 1212 } 1213 return hr; 1214 } 1215 1216 int RenderTextWin::CountCharsWithMissingGlyphs(internal::TextRun* run) const { 1217 int chars_not_missing_glyphs = 0; 1218 SCRIPT_FONTPROPERTIES properties; 1219 memset(&properties, 0, sizeof(properties)); 1220 properties.cBytes = sizeof(properties); 1221 ScriptGetFontProperties(cached_hdc_, &run->script_cache, &properties); 1222 1223 const wchar_t* run_text = &(GetLayoutText()[run->range.start()]); 1224 for (size_t char_index = 0; char_index < run->range.length(); ++char_index) { 1225 const int glyph_index = run->logical_clusters[char_index]; 1226 DCHECK_GE(glyph_index, 0); 1227 DCHECK_LT(glyph_index, run->glyph_count); 1228 1229 if (run->glyphs[glyph_index] == properties.wgDefault) 1230 continue; 1231 1232 // Windows Vista sometimes returns glyphs equal to wgBlank (instead of 1233 // wgDefault), with fZeroWidth set. Treat such cases as having missing 1234 // glyphs if the corresponding character is not whitespace. 1235 // See: http://crbug.com/125629 1236 if (run->glyphs[glyph_index] == properties.wgBlank && 1237 run->visible_attributes[glyph_index].fZeroWidth && 1238 !IsWhitespace(run_text[char_index]) && 1239 !IsUnicodeBidiControlCharacter(run_text[char_index])) { 1240 continue; 1241 } 1242 1243 ++chars_not_missing_glyphs; 1244 } 1245 1246 DCHECK_LE(chars_not_missing_glyphs, static_cast<int>(run->range.length())); 1247 return run->range.length() - chars_not_missing_glyphs; 1248 } 1249 1250 size_t RenderTextWin::GetRunContainingCaret(const SelectionModel& caret) const { 1251 DCHECK(!needs_layout_); 1252 size_t layout_position = TextIndexToLayoutIndex(caret.caret_pos()); 1253 LogicalCursorDirection affinity = caret.caret_affinity(); 1254 for (size_t run = 0; run < runs_.size(); ++run) 1255 if (RangeContainsCaret(runs_[run]->range, layout_position, affinity)) 1256 return run; 1257 return runs_.size(); 1258 } 1259 1260 size_t RenderTextWin::GetRunContainingXCoord(int x) const { 1261 DCHECK(!needs_layout_); 1262 // Find the text run containing the argument point (assumed already offset). 1263 for (size_t run = 0; run < runs_.size(); ++run) { 1264 if ((runs_[run]->preceding_run_widths <= x) && 1265 ((runs_[run]->preceding_run_widths + runs_[run]->width) > x)) 1266 return run; 1267 } 1268 return runs_.size(); 1269 } 1270 1271 SelectionModel RenderTextWin::FirstSelectionModelInsideRun( 1272 const internal::TextRun* run) { 1273 size_t position = LayoutIndexToTextIndex(run->range.start()); 1274 position = IndexOfAdjacentGrapheme(position, CURSOR_FORWARD); 1275 return SelectionModel(position, CURSOR_BACKWARD); 1276 } 1277 1278 SelectionModel RenderTextWin::LastSelectionModelInsideRun( 1279 const internal::TextRun* run) { 1280 size_t position = LayoutIndexToTextIndex(run->range.end()); 1281 position = IndexOfAdjacentGrapheme(position, CURSOR_BACKWARD); 1282 return SelectionModel(position, CURSOR_FORWARD); 1283 } 1284 1285 RenderText* RenderText::CreateNativeInstance() { 1286 return new RenderTextWin; 1287 } 1288 1289 } // namespace gfx 1290