Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(224)

Side by Side Diff: ui/gfx/render_text_win.cc

Issue 17745005: Clamp RenderTextWin layout length to 10,000 code points. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Adjust ellipsis const, restore initial run guess of 100. Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « ui/gfx/render_text_unittest.cc ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "ui/gfx/render_text_win.h" 5 #include "ui/gfx/render_text_win.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 8
9 #include "base/i18n/break_iterator.h" 9 #include "base/i18n/break_iterator.h"
10 #include "base/i18n/rtl.h" 10 #include "base/i18n/rtl.h"
11 #include "base/logging.h" 11 #include "base/logging.h"
12 #include "base/strings/string_util.h" 12 #include "base/strings/string_util.h"
13 #include "base/strings/utf_string_conversions.h" 13 #include "base/strings/utf_string_conversions.h"
14 #include "base/win/windows_version.h" 14 #include "base/win/windows_version.h"
15 #include "ui/base/text/utf16_indexing.h" 15 #include "ui/base/text/utf16_indexing.h"
16 #include "ui/gfx/canvas.h" 16 #include "ui/gfx/canvas.h"
17 #include "ui/gfx/font_fallback_win.h" 17 #include "ui/gfx/font_fallback_win.h"
18 #include "ui/gfx/font_smoothing_win.h" 18 #include "ui/gfx/font_smoothing_win.h"
19 #include "ui/gfx/platform_font_win.h" 19 #include "ui/gfx/platform_font_win.h"
20 20
21 namespace gfx { 21 namespace gfx {
22 22
23 namespace { 23 namespace {
24 24
25 // The maximum supported number of Uniscribe runs; a SCRIPT_ITEM is 8 bytes. 25 // The maximum length of text supported for Uniscribe layout and display.
26 // TODO(msw): Review memory use/failure? Max string length? Alternate approach? 26 // This empirically chosen value should prevent major performance degradations.
27 const int kGuessItems = 100; 27 // TODO(msw): Support longer text, partial layout/painting, etc.
28 const int kMaxItems = 10000; 28 const size_t kMaxUniscribeTextLength = 10000;
29 29
30 // The maximum supported number of Uniscribe glyphs; a glyph is 1 word. 30 // The initial guess and maximum supported number of runs; arbitrary values.
31 // TODO(msw): Review memory use/failure? Max string length? Alternate approach? 31 // TODO(msw): Support more runs, determine a better initial guess, etc.
32 const int kMaxGlyphs = 100000; 32 const int kGuessRuns = 100;
33 const size_t kMaxRuns = 10000;
34
35 // The maximum number of glyphs per run; ScriptShape fails on larger values.
36 const size_t kMaxGlyphs = 65535;
33 37
34 // Callback to |EnumEnhMetaFile()| to intercept font creation. 38 // Callback to |EnumEnhMetaFile()| to intercept font creation.
35 int CALLBACK MetaFileEnumProc(HDC hdc, 39 int CALLBACK MetaFileEnumProc(HDC hdc,
36 HANDLETABLE* table, 40 HANDLETABLE* table,
37 CONST ENHMETARECORD* record, 41 CONST ENHMETARECORD* record,
38 int table_entries, 42 int table_entries,
39 LPARAM log_font) { 43 LPARAM log_font) {
40 if (record->iType == EMR_EXTCREATEFONTINDIRECTW) { 44 if (record->iType == EMR_EXTCREATEFONTINDIRECTW) {
41 const EMREXTCREATEFONTINDIRECTW* create_font_record = 45 const EMREXTCREATEFONTINDIRECTW* create_font_record =
42 reinterpret_cast<const EMREXTCREATEFONTINDIRECTW*>(record); 46 reinterpret_cast<const EMREXTCREATEFONTINDIRECTW*>(record);
(...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after
174 // static 178 // static
175 HDC RenderTextWin::cached_hdc_ = NULL; 179 HDC RenderTextWin::cached_hdc_ = NULL;
176 180
177 // static 181 // static
178 std::map<std::string, Font> RenderTextWin::successful_substitute_fonts_; 182 std::map<std::string, Font> RenderTextWin::successful_substitute_fonts_;
179 183
180 RenderTextWin::RenderTextWin() 184 RenderTextWin::RenderTextWin()
181 : RenderText(), 185 : RenderText(),
182 common_baseline_(0), 186 common_baseline_(0),
183 needs_layout_(false) { 187 needs_layout_(false) {
188 set_truncate_length(kMaxUniscribeTextLength);
189
184 memset(&script_control_, 0, sizeof(script_control_)); 190 memset(&script_control_, 0, sizeof(script_control_));
185 memset(&script_state_, 0, sizeof(script_state_)); 191 memset(&script_state_, 0, sizeof(script_state_));
186 192
187 MoveCursorTo(EdgeSelectionModel(CURSOR_LEFT)); 193 MoveCursorTo(EdgeSelectionModel(CURSOR_LEFT));
188 } 194 }
189 195
190 RenderTextWin::~RenderTextWin() { 196 RenderTextWin::~RenderTextWin() {
191 } 197 }
192 198
193 Size RenderTextWin::GetStringSize() { 199 Size RenderTextWin::GetStringSize() {
194 EnsureLayout(); 200 EnsureLayout();
195 return string_size_; 201 return string_size_;
196 } 202 }
197 203
198 int RenderTextWin::GetBaseline() { 204 int RenderTextWin::GetBaseline() {
199 EnsureLayout(); 205 EnsureLayout();
200 return common_baseline_; 206 return common_baseline_;
201 } 207 }
202 208
203 SelectionModel RenderTextWin::FindCursorPosition(const Point& point) { 209 SelectionModel RenderTextWin::FindCursorPosition(const Point& point) {
204 if (text().empty()) 210 if (text().empty())
205 return SelectionModel(); 211 return SelectionModel();
206 212
207 EnsureLayout(); 213 EnsureLayout();
208 // Find the run that contains the point and adjust the argument location. 214 // Find the run that contains the point and adjust the argument location.
209 int x = ToTextPoint(point).x(); 215 int x = ToTextPoint(point).x();
210 size_t run_index = GetRunContainingXCoord(x); 216 size_t run_index = GetRunContainingXCoord(x);
211 if (run_index == runs_.size()) 217 if (run_index >= runs_.size())
212 return EdgeSelectionModel((x < 0) ? CURSOR_LEFT : CURSOR_RIGHT); 218 return EdgeSelectionModel((x < 0) ? CURSOR_LEFT : CURSOR_RIGHT);
213 internal::TextRun* run = runs_[run_index]; 219 internal::TextRun* run = runs_[run_index];
214 220
215 int position = 0, trailing = 0; 221 int position = 0, trailing = 0;
216 HRESULT hr = ScriptXtoCP(x - run->preceding_run_widths, 222 HRESULT hr = ScriptXtoCP(x - run->preceding_run_widths,
217 run->range.length(), 223 run->range.length(),
218 run->glyph_count, 224 run->glyph_count,
219 run->logical_clusters.get(), 225 run->logical_clusters.get(),
220 run->visible_attributes.get(), 226 run->visible_attributes.get(),
221 run->advance_widths.get(), 227 run->advance_widths.get(),
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after
338 RenderText::SetSelectionModel(model); 344 RenderText::SetSelectionModel(model);
339 // TODO(xji|msw): The text selection color is applied in ItemizeLogicalText(). 345 // TODO(xji|msw): The text selection color is applied in ItemizeLogicalText().
340 // So, the layout must be updated in order to draw the proper selection range. 346 // So, the layout must be updated in order to draw the proper selection range.
341 // Colors should be applied in DrawVisualText(), as done by RenderTextLinux. 347 // Colors should be applied in DrawVisualText(), as done by RenderTextLinux.
342 ResetLayout(); 348 ResetLayout();
343 } 349 }
344 350
345 ui::Range RenderTextWin::GetGlyphBounds(size_t index) { 351 ui::Range RenderTextWin::GetGlyphBounds(size_t index) {
346 const size_t run_index = 352 const size_t run_index =
347 GetRunContainingCaret(SelectionModel(index, CURSOR_FORWARD)); 353 GetRunContainingCaret(SelectionModel(index, CURSOR_FORWARD));
348 DCHECK_LT(run_index, runs_.size()); 354 // Return edge bounds if the index is invalid or beyond the layout text size.
355 if (run_index >= runs_.size())
356 return ui::Range(string_size_.width());
349 internal::TextRun* run = runs_[run_index]; 357 internal::TextRun* run = runs_[run_index];
350 const size_t layout_index = TextIndexToLayoutIndex(index); 358 const size_t layout_index = TextIndexToLayoutIndex(index);
351 return ui::Range(GetGlyphXBoundary(run, layout_index, false), 359 return ui::Range(GetGlyphXBoundary(run, layout_index, false),
352 GetGlyphXBoundary(run, layout_index, true)); 360 GetGlyphXBoundary(run, layout_index, true));
353 } 361 }
354 362
355 std::vector<Rect> RenderTextWin::GetSubstringBounds(const ui::Range& range) { 363 std::vector<Rect> RenderTextWin::GetSubstringBounds(const ui::Range& range) {
356 DCHECK(!needs_layout_); 364 DCHECK(!needs_layout_);
357 DCHECK(ui::Range(0, text().length()).Contains(range)); 365 DCHECK(ui::Range(0, text().length()).Contains(range));
358 ui::Range layout_range(TextIndexToLayoutIndex(range.start()), 366 ui::Range layout_range(TextIndexToLayoutIndex(range.start()),
(...skipping 20 matching lines...) Expand all
379 rect.Union(bounds.back()); 387 rect.Union(bounds.back());
380 bounds.pop_back(); 388 bounds.pop_back();
381 } 389 }
382 bounds.push_back(rect); 390 bounds.push_back(rect);
383 } 391 }
384 } 392 }
385 return bounds; 393 return bounds;
386 } 394 }
387 395
388 size_t RenderTextWin::TextIndexToLayoutIndex(size_t index) const { 396 size_t RenderTextWin::TextIndexToLayoutIndex(size_t index) const {
389 if (!obscured())
390 return index;
391
392 DCHECK_LE(index, text().length()); 397 DCHECK_LE(index, text().length());
393 const ptrdiff_t offset = ui::UTF16IndexToOffset(text(), 0, index); 398 ptrdiff_t i = obscured() ? ui::UTF16IndexToOffset(text(), 0, index) : index;
394 DCHECK_GE(offset, 0); 399 CHECK_GE(i, 0);
395 DCHECK_LE(static_cast<size_t>(offset), GetLayoutText().length()); 400 // Clamp layout indices to the length of the text actually used for layout.
396 return static_cast<size_t>(offset); 401 return std::min<size_t>(GetLayoutText().length(), i);
397 } 402 }
398 403
399 size_t RenderTextWin::LayoutIndexToTextIndex(size_t index) const { 404 size_t RenderTextWin::LayoutIndexToTextIndex(size_t index) const {
400 if (!obscured()) 405 if (!obscured())
401 return index; 406 return index;
402 407
403 DCHECK_LE(index, GetLayoutText().length()); 408 DCHECK_LE(index, GetLayoutText().length());
404 const size_t text_index = ui::UTF16OffsetToIndex(text(), 0, index); 409 const size_t text_index = ui::UTF16OffsetToIndex(text(), 0, index);
405 DCHECK_LE(text_index, text().length()); 410 DCHECK_LE(text_index, text().length());
406 return text_index; 411 return text_index;
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after
489 // Set Uniscribe's base text direction. 494 // Set Uniscribe's base text direction.
490 script_state_.uBidiLevel = 495 script_state_.uBidiLevel =
491 (GetTextDirection() == base::i18n::RIGHT_TO_LEFT) ? 1 : 0; 496 (GetTextDirection() == base::i18n::RIGHT_TO_LEFT) ? 1 : 0;
492 497
493 if (text().empty()) 498 if (text().empty())
494 return; 499 return;
495 500
496 HRESULT hr = E_OUTOFMEMORY; 501 HRESULT hr = E_OUTOFMEMORY;
497 int script_items_count = 0; 502 int script_items_count = 0;
498 std::vector<SCRIPT_ITEM> script_items; 503 std::vector<SCRIPT_ITEM> script_items;
499 const size_t text_length = GetLayoutText().length(); 504 const size_t layout_text_length = GetLayoutText().length();
500 for (size_t n = kGuessItems; hr == E_OUTOFMEMORY && n < kMaxItems; n *= 2) { 505 // Ensure that |kMaxRuns| is attempted and the loop terminates afterward.
506 for (size_t runs = kGuessRuns; hr == E_OUTOFMEMORY && runs <= kMaxRuns;
507 runs = std::max(runs + 1, std::min(runs * 2, kMaxRuns))) {
501 // Derive the array of Uniscribe script items from the logical text. 508 // Derive the array of Uniscribe script items from the logical text.
502 // ScriptItemize always adds a terminal array item so that the length of the 509 // ScriptItemize always adds a terminal array item so that the length of
503 // last item can be derived from the terminal SCRIPT_ITEM::iCharPos. 510 // the last item can be derived from the terminal SCRIPT_ITEM::iCharPos.
504 script_items.resize(n); 511 script_items.resize(runs);
505 hr = ScriptItemize(GetLayoutText().c_str(), 512 hr = ScriptItemize(GetLayoutText().c_str(), layout_text_length,
506 text_length, 513 runs - 1, &script_control_, &script_state_,
507 n - 1, 514 &script_items[0], &script_items_count);
508 &script_control_,
509 &script_state_,
510 &script_items[0],
511 &script_items_count);
512 } 515 }
513 DCHECK(SUCCEEDED(hr)); 516 DCHECK(SUCCEEDED(hr));
514 517 if (!SUCCEEDED(hr) || script_items_count <= 0)
515 if (script_items_count <= 0)
516 return; 518 return;
517 519
518 // Temporarily apply composition underlines and selection colors. 520 // Temporarily apply composition underlines and selection colors.
519 ApplyCompositionAndSelectionStyles(); 521 ApplyCompositionAndSelectionStyles();
520 522
521 // Build the list of runs from the script items and ranged colors/styles. 523 // Build the list of runs from the script items and ranged colors/styles.
522 // TODO(msw): Only break for bold/italic, not color etc. See TextRun comment. 524 // TODO(msw): Only break for bold/italic, not color etc. See TextRun comment.
523 internal::StyleIterator style(colors(), styles()); 525 internal::StyleIterator style(colors(), styles());
524 SCRIPT_ITEM* script_item = &script_items[0]; 526 SCRIPT_ITEM* script_item = &script_items[0];
525 const size_t layout_text_length = GetLayoutText().length(); 527 const size_t max_run_length = kMaxGlyphs / 2;
526 for (size_t run_break = 0; run_break < layout_text_length;) { 528 for (size_t run_break = 0; run_break < layout_text_length;) {
527 internal::TextRun* run = new internal::TextRun(); 529 internal::TextRun* run = new internal::TextRun();
528 run->range.set_start(run_break); 530 run->range.set_start(run_break);
529 run->font = GetFont(); 531 run->font = GetFont();
530 run->font_style = (style.style(BOLD) ? Font::BOLD : 0) | 532 run->font_style = (style.style(BOLD) ? Font::BOLD : 0) |
531 (style.style(ITALIC) ? Font::ITALIC : 0); 533 (style.style(ITALIC) ? Font::ITALIC : 0);
532 DeriveFontIfNecessary(run->font.GetFontSize(), run->font.GetHeight(), 534 DeriveFontIfNecessary(run->font.GetFontSize(), run->font.GetHeight(),
533 run->font_style, &run->font); 535 run->font_style, &run->font);
534 run->foreground = style.color(); 536 run->foreground = style.color();
535 run->strike = style.style(STRIKE); 537 run->strike = style.style(STRIKE);
536 run->diagonal_strike = style.style(DIAGONAL_STRIKE); 538 run->diagonal_strike = style.style(DIAGONAL_STRIKE);
537 run->underline = style.style(UNDERLINE); 539 run->underline = style.style(UNDERLINE);
538 run->script_analysis = script_item->a; 540 run->script_analysis = script_item->a;
539 541
540 // Find the next break and advance the iterators as needed. 542 // Find the next break and advance the iterators as needed.
541 const size_t script_item_break = (script_item + 1)->iCharPos; 543 const size_t script_item_break = (script_item + 1)->iCharPos;
542 run_break = std::min(script_item_break, 544 run_break = std::min(script_item_break,
543 TextIndexToLayoutIndex(style.GetRange().end())); 545 TextIndexToLayoutIndex(style.GetRange().end()));
546 // Clamp run lengths to avoid exceeding the maximum supported glyph count.
547 if ((run_break - run->range.start()) > max_run_length)
548 run_break = run->range.start() + max_run_length;
544 style.UpdatePosition(LayoutIndexToTextIndex(run_break)); 549 style.UpdatePosition(LayoutIndexToTextIndex(run_break));
545 if (script_item_break == run_break) 550 if (script_item_break == run_break)
546 script_item++; 551 script_item++;
547 run->range.set_end(run_break); 552 run->range.set_end(run_break);
548 runs_.push_back(run); 553 runs_.push_back(run);
549 } 554 }
550 555
551 // Undo the temporarily applied composition underlines and selection colors. 556 // Undo the temporarily applied composition underlines and selection colors.
552 UndoCompositionAndSelectionStyles(); 557 UndoCompositionAndSelectionStyles();
553 } 558 }
(...skipping 179 matching lines...) Expand 10 before | Expand all | Expand 10 after
733 DeriveFontIfNecessary(font_size, font_height, run->font_style, &run->font); 738 DeriveFontIfNecessary(font_size, font_height, run->font_style, &run->font);
734 ScriptFreeCache(&run->script_cache); 739 ScriptFreeCache(&run->script_cache);
735 } 740 }
736 741
737 // Select the font desired for glyph generation. 742 // Select the font desired for glyph generation.
738 SelectObject(cached_hdc_, run->font.GetNativeFont()); 743 SelectObject(cached_hdc_, run->font.GetNativeFont());
739 744
740 HRESULT hr = E_OUTOFMEMORY; 745 HRESULT hr = E_OUTOFMEMORY;
741 const size_t run_length = run->range.length(); 746 const size_t run_length = run->range.length();
742 const wchar_t* run_text = &(GetLayoutText()[run->range.start()]); 747 const wchar_t* run_text = &(GetLayoutText()[run->range.start()]);
743 // Max glyph guess: http://msdn.microsoft.com/en-us/library/dd368564.aspx 748 // Guess the expected number of glyphs from the length of the run.
749 // MSDN suggests this at http://msdn.microsoft.com/en-us/library/dd368564.aspx
744 size_t max_glyphs = static_cast<size_t>(1.5 * run_length + 16); 750 size_t max_glyphs = static_cast<size_t>(1.5 * run_length + 16);
745 while (hr == E_OUTOFMEMORY && max_glyphs < kMaxGlyphs) { 751 while (hr == E_OUTOFMEMORY && max_glyphs <= kMaxGlyphs) {
746 run->glyph_count = 0; 752 run->glyph_count = 0;
747 run->glyphs.reset(new WORD[max_glyphs]); 753 run->glyphs.reset(new WORD[max_glyphs]);
748 run->visible_attributes.reset(new SCRIPT_VISATTR[max_glyphs]); 754 run->visible_attributes.reset(new SCRIPT_VISATTR[max_glyphs]);
749 hr = ScriptShape(cached_hdc_, 755 hr = ScriptShape(cached_hdc_, &run->script_cache, run_text, run_length,
750 &run->script_cache, 756 max_glyphs, &run->script_analysis, run->glyphs.get(),
751 run_text, 757 run->logical_clusters.get(), run->visible_attributes.get(),
752 run_length,
753 max_glyphs,
754 &run->script_analysis,
755 run->glyphs.get(),
756 run->logical_clusters.get(),
757 run->visible_attributes.get(),
758 &run->glyph_count); 758 &run->glyph_count);
759 max_glyphs *= 2; 759 // Ensure that |kMaxGlyphs| is attempted and the loop terminates afterward.
760 max_glyphs = std::max(max_glyphs + 1, std::min(max_glyphs * 2, kMaxGlyphs));
760 } 761 }
761 return hr; 762 return hr;
762 } 763 }
763 764
764 int RenderTextWin::CountCharsWithMissingGlyphs(internal::TextRun* run) const { 765 int RenderTextWin::CountCharsWithMissingGlyphs(internal::TextRun* run) const {
765 int chars_not_missing_glyphs = 0; 766 int chars_not_missing_glyphs = 0;
766 SCRIPT_FONTPROPERTIES properties; 767 SCRIPT_FONTPROPERTIES properties;
767 memset(&properties, 0, sizeof(properties)); 768 memset(&properties, 0, sizeof(properties));
768 properties.cBytes = sizeof(properties); 769 properties.cBytes = sizeof(properties);
769 ScriptGetFontProperties(cached_hdc_, &run->script_cache, &properties); 770 ScriptGetFontProperties(cached_hdc_, &run->script_cache, &properties);
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
828 size_t position = LayoutIndexToTextIndex(run->range.end()); 829 size_t position = LayoutIndexToTextIndex(run->range.end());
829 position = IndexOfAdjacentGrapheme(position, CURSOR_BACKWARD); 830 position = IndexOfAdjacentGrapheme(position, CURSOR_BACKWARD);
830 return SelectionModel(position, CURSOR_FORWARD); 831 return SelectionModel(position, CURSOR_FORWARD);
831 } 832 }
832 833
833 RenderText* RenderText::CreateInstance() { 834 RenderText* RenderText::CreateInstance() {
834 return new RenderTextWin; 835 return new RenderTextWin;
835 } 836 }
836 837
837 } // namespace gfx 838 } // namespace gfx
OLDNEW
« no previous file with comments | « ui/gfx/render_text_unittest.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698