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