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

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: Resolve string formatting; you just can't please some compilers. 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
« ui/base/text/text_elider.cc ('K') | « 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 maximum supported number of text runs; an arbitrarily chosen value.
31 // TODO(msw): Review memory use/failure? Max string length? Alternate approach? 31 // TODO(msw): Support more runs; see https://codereview.chromium.org/17745005
32 const int kMaxGlyphs = 100000; 32 const size_t kMaxRuns = 10000;
33
34 // The maximum number of glyphs per run; ScriptShape fails on larger values.
35 const size_t kMaxGlyphs = 65535;
33 36
34 // Callback to |EnumEnhMetaFile()| to intercept font creation. 37 // Callback to |EnumEnhMetaFile()| to intercept font creation.
35 int CALLBACK MetaFileEnumProc(HDC hdc, 38 int CALLBACK MetaFileEnumProc(HDC hdc,
36 HANDLETABLE* table, 39 HANDLETABLE* table,
37 CONST ENHMETARECORD* record, 40 CONST ENHMETARECORD* record,
38 int table_entries, 41 int table_entries,
39 LPARAM log_font) { 42 LPARAM log_font) {
40 if (record->iType == EMR_EXTCREATEFONTINDIRECTW) { 43 if (record->iType == EMR_EXTCREATEFONTINDIRECTW) {
41 const EMREXTCREATEFONTINDIRECTW* create_font_record = 44 const EMREXTCREATEFONTINDIRECTW* create_font_record =
42 reinterpret_cast<const EMREXTCREATEFONTINDIRECTW*>(record); 45 reinterpret_cast<const EMREXTCREATEFONTINDIRECTW*>(record);
(...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after
174 // static 177 // static
175 HDC RenderTextWin::cached_hdc_ = NULL; 178 HDC RenderTextWin::cached_hdc_ = NULL;
176 179
177 // static 180 // static
178 std::map<std::string, Font> RenderTextWin::successful_substitute_fonts_; 181 std::map<std::string, Font> RenderTextWin::successful_substitute_fonts_;
179 182
180 RenderTextWin::RenderTextWin() 183 RenderTextWin::RenderTextWin()
181 : RenderText(), 184 : RenderText(),
182 common_baseline_(0), 185 common_baseline_(0),
183 needs_layout_(false) { 186 needs_layout_(false) {
187 set_truncate_length(kMaxUniscribeTextLength);
188
184 memset(&script_control_, 0, sizeof(script_control_)); 189 memset(&script_control_, 0, sizeof(script_control_));
185 memset(&script_state_, 0, sizeof(script_state_)); 190 memset(&script_state_, 0, sizeof(script_state_));
186 191
187 MoveCursorTo(EdgeSelectionModel(CURSOR_LEFT)); 192 MoveCursorTo(EdgeSelectionModel(CURSOR_LEFT));
188 } 193 }
189 194
190 RenderTextWin::~RenderTextWin() { 195 RenderTextWin::~RenderTextWin() {
191 } 196 }
192 197
193 Size RenderTextWin::GetStringSize() { 198 Size RenderTextWin::GetStringSize() {
194 EnsureLayout(); 199 EnsureLayout();
195 return string_size_; 200 return string_size_;
196 } 201 }
197 202
198 int RenderTextWin::GetBaseline() { 203 int RenderTextWin::GetBaseline() {
199 EnsureLayout(); 204 EnsureLayout();
200 return common_baseline_; 205 return common_baseline_;
201 } 206 }
202 207
203 SelectionModel RenderTextWin::FindCursorPosition(const Point& point) { 208 SelectionModel RenderTextWin::FindCursorPosition(const Point& point) {
204 if (text().empty()) 209 if (text().empty())
205 return SelectionModel(); 210 return SelectionModel();
206 211
207 EnsureLayout(); 212 EnsureLayout();
208 // Find the run that contains the point and adjust the argument location. 213 // Find the run that contains the point and adjust the argument location.
209 int x = ToTextPoint(point).x(); 214 int x = ToTextPoint(point).x();
210 size_t run_index = GetRunContainingXCoord(x); 215 size_t run_index = GetRunContainingXCoord(x);
211 if (run_index == runs_.size()) 216 if (run_index >= runs_.size())
212 return EdgeSelectionModel((x < 0) ? CURSOR_LEFT : CURSOR_RIGHT); 217 return EdgeSelectionModel((x < 0) ? CURSOR_LEFT : CURSOR_RIGHT);
213 internal::TextRun* run = runs_[run_index]; 218 internal::TextRun* run = runs_[run_index];
214 219
215 int position = 0, trailing = 0; 220 int position = 0, trailing = 0;
216 HRESULT hr = ScriptXtoCP(x - run->preceding_run_widths, 221 HRESULT hr = ScriptXtoCP(x - run->preceding_run_widths,
217 run->range.length(), 222 run->range.length(),
218 run->glyph_count, 223 run->glyph_count,
219 run->logical_clusters.get(), 224 run->logical_clusters.get(),
220 run->visible_attributes.get(), 225 run->visible_attributes.get(),
221 run->advance_widths.get(), 226 run->advance_widths.get(),
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after
338 RenderText::SetSelectionModel(model); 343 RenderText::SetSelectionModel(model);
339 // TODO(xji|msw): The text selection color is applied in ItemizeLogicalText(). 344 // 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. 345 // 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. 346 // Colors should be applied in DrawVisualText(), as done by RenderTextLinux.
342 ResetLayout(); 347 ResetLayout();
343 } 348 }
344 349
345 ui::Range RenderTextWin::GetGlyphBounds(size_t index) { 350 ui::Range RenderTextWin::GetGlyphBounds(size_t index) {
346 const size_t run_index = 351 const size_t run_index =
347 GetRunContainingCaret(SelectionModel(index, CURSOR_FORWARD)); 352 GetRunContainingCaret(SelectionModel(index, CURSOR_FORWARD));
348 DCHECK_LT(run_index, runs_.size()); 353 // Return edge bounds if the index is invalid or beyond the layout text size.
354 if (run_index >= runs_.size())
355 return ui::Range(string_size_.width());
349 internal::TextRun* run = runs_[run_index]; 356 internal::TextRun* run = runs_[run_index];
350 const size_t layout_index = TextIndexToLayoutIndex(index); 357 const size_t layout_index = TextIndexToLayoutIndex(index);
351 return ui::Range(GetGlyphXBoundary(run, layout_index, false), 358 return ui::Range(GetGlyphXBoundary(run, layout_index, false),
352 GetGlyphXBoundary(run, layout_index, true)); 359 GetGlyphXBoundary(run, layout_index, true));
353 } 360 }
354 361
355 std::vector<Rect> RenderTextWin::GetSubstringBounds(const ui::Range& range) { 362 std::vector<Rect> RenderTextWin::GetSubstringBounds(const ui::Range& range) {
356 DCHECK(!needs_layout_); 363 DCHECK(!needs_layout_);
357 DCHECK(ui::Range(0, text().length()).Contains(range)); 364 DCHECK(ui::Range(0, text().length()).Contains(range));
358 ui::Range layout_range(TextIndexToLayoutIndex(range.start()), 365 ui::Range layout_range(TextIndexToLayoutIndex(range.start()),
(...skipping 20 matching lines...) Expand all
379 rect.Union(bounds.back()); 386 rect.Union(bounds.back());
380 bounds.pop_back(); 387 bounds.pop_back();
381 } 388 }
382 bounds.push_back(rect); 389 bounds.push_back(rect);
383 } 390 }
384 } 391 }
385 return bounds; 392 return bounds;
386 } 393 }
387 394
388 size_t RenderTextWin::TextIndexToLayoutIndex(size_t index) const { 395 size_t RenderTextWin::TextIndexToLayoutIndex(size_t index) const {
389 if (!obscured())
390 return index;
391
392 DCHECK_LE(index, text().length()); 396 DCHECK_LE(index, text().length());
393 const ptrdiff_t offset = ui::UTF16IndexToOffset(text(), 0, index); 397 ptrdiff_t i = obscured() ? ui::UTF16IndexToOffset(text(), 0, index) : index;
394 DCHECK_GE(offset, 0); 398 CHECK_GE(i, 0);
395 DCHECK_LE(static_cast<size_t>(offset), GetLayoutText().length()); 399 // Clamp layout indices to the length of the text actually used for layout.
396 return static_cast<size_t>(offset); 400 return std::min<size_t>(GetLayoutText().length(), i);
397 } 401 }
398 402
399 size_t RenderTextWin::LayoutIndexToTextIndex(size_t index) const { 403 size_t RenderTextWin::LayoutIndexToTextIndex(size_t index) const {
400 if (!obscured()) 404 if (!obscured())
401 return index; 405 return index;
402 406
403 DCHECK_LE(index, GetLayoutText().length()); 407 DCHECK_LE(index, GetLayoutText().length());
404 const size_t text_index = ui::UTF16OffsetToIndex(text(), 0, index); 408 const size_t text_index = ui::UTF16OffsetToIndex(text(), 0, index);
405 DCHECK_LE(text_index, text().length()); 409 DCHECK_LE(text_index, text().length());
406 return text_index; 410 return text_index;
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after
489 // Set Uniscribe's base text direction. 493 // Set Uniscribe's base text direction.
490 script_state_.uBidiLevel = 494 script_state_.uBidiLevel =
491 (GetTextDirection() == base::i18n::RIGHT_TO_LEFT) ? 1 : 0; 495 (GetTextDirection() == base::i18n::RIGHT_TO_LEFT) ? 1 : 0;
492 496
493 if (text().empty()) 497 if (text().empty())
494 return; 498 return;
495 499
496 HRESULT hr = E_OUTOFMEMORY; 500 HRESULT hr = E_OUTOFMEMORY;
497 int script_items_count = 0; 501 int script_items_count = 0;
498 std::vector<SCRIPT_ITEM> script_items; 502 std::vector<SCRIPT_ITEM> script_items;
499 const size_t text_length = GetLayoutText().length(); 503
500 for (size_t n = kGuessItems; hr == E_OUTOFMEMORY && n < kMaxItems; n *= 2) { 504 // Guess the run count based on string sizes; Uniscribe requires 3 at minimum.
505 const size_t layout_text_length = GetLayoutText().length();
506 size_t run_guess = std::min(kMaxRuns, std::max(3U, layout_text_length / 10));
Alexei Svitkine (slow) 2013/06/28 15:37:02 The old code would start at 100 whereas your new c
msw 2013/06/28 16:31:23 Done.
507 while (hr == E_OUTOFMEMORY && run_guess <= 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(run_guess);
505 hr = ScriptItemize(GetLayoutText().c_str(), 512 hr = ScriptItemize(GetLayoutText().c_str(), layout_text_length,
506 text_length, 513 run_guess - 1, &script_control_, &script_state_,
507 n - 1, 514 &script_items[0], &script_items_count);
508 &script_control_, 515 // Ensure that |kMaxRuns| is attempted and the loop terminates afterward.
509 &script_state_, 516 run_guess = std::max(run_guess + 1, std::min(run_guess * 2, kMaxRuns));
510 &script_items[0],
511 &script_items_count);
512 } 517 }
513 DCHECK(SUCCEEDED(hr)); 518 DCHECK(SUCCEEDED(hr));
514 519 if (!SUCCEEDED(hr) || script_items_count <= 0)
515 if (script_items_count <= 0)
516 return; 520 return;
517 521
518 // Temporarily apply composition underlines and selection colors. 522 // Temporarily apply composition underlines and selection colors.
519 ApplyCompositionAndSelectionStyles(); 523 ApplyCompositionAndSelectionStyles();
520 524
521 // Build the list of runs from the script items and ranged colors/styles. 525 // 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. 526 // TODO(msw): Only break for bold/italic, not color etc. See TextRun comment.
523 internal::StyleIterator style(colors(), styles()); 527 internal::StyleIterator style(colors(), styles());
524 SCRIPT_ITEM* script_item = &script_items[0]; 528 SCRIPT_ITEM* script_item = &script_items[0];
525 const size_t layout_text_length = GetLayoutText().length(); 529 const size_t max_run_length = kMaxGlyphs / 2;
526 for (size_t run_break = 0; run_break < layout_text_length;) { 530 for (size_t run_break = 0; run_break < layout_text_length;) {
527 internal::TextRun* run = new internal::TextRun(); 531 internal::TextRun* run = new internal::TextRun();
528 run->range.set_start(run_break); 532 run->range.set_start(run_break);
529 run->font = GetFont(); 533 run->font = GetFont();
530 run->font_style = (style.style(BOLD) ? Font::BOLD : 0) | 534 run->font_style = (style.style(BOLD) ? Font::BOLD : 0) |
531 (style.style(ITALIC) ? Font::ITALIC : 0); 535 (style.style(ITALIC) ? Font::ITALIC : 0);
532 DeriveFontIfNecessary(run->font.GetFontSize(), run->font.GetHeight(), 536 DeriveFontIfNecessary(run->font.GetFontSize(), run->font.GetHeight(),
533 run->font_style, &run->font); 537 run->font_style, &run->font);
534 run->foreground = style.color(); 538 run->foreground = style.color();
535 run->strike = style.style(STRIKE); 539 run->strike = style.style(STRIKE);
536 run->diagonal_strike = style.style(DIAGONAL_STRIKE); 540 run->diagonal_strike = style.style(DIAGONAL_STRIKE);
537 run->underline = style.style(UNDERLINE); 541 run->underline = style.style(UNDERLINE);
538 run->script_analysis = script_item->a; 542 run->script_analysis = script_item->a;
539 543
540 // Find the next break and advance the iterators as needed. 544 // Find the next break and advance the iterators as needed.
541 const size_t script_item_break = (script_item + 1)->iCharPos; 545 const size_t script_item_break = (script_item + 1)->iCharPos;
542 run_break = std::min(script_item_break, 546 run_break = std::min(script_item_break,
543 TextIndexToLayoutIndex(style.GetRange().end())); 547 TextIndexToLayoutIndex(style.GetRange().end()));
548 // Clamp run lengths to avoid exceeding the maximum supported glyph count.
549 if ((run_break - run->range.start()) > max_run_length)
550 run_break = run->range.start() + max_run_length;
544 style.UpdatePosition(LayoutIndexToTextIndex(run_break)); 551 style.UpdatePosition(LayoutIndexToTextIndex(run_break));
545 if (script_item_break == run_break) 552 if (script_item_break == run_break)
546 script_item++; 553 script_item++;
547 run->range.set_end(run_break); 554 run->range.set_end(run_break);
548 runs_.push_back(run); 555 runs_.push_back(run);
549 } 556 }
550 557
551 // Undo the temporarily applied composition underlines and selection colors. 558 // Undo the temporarily applied composition underlines and selection colors.
552 UndoCompositionAndSelectionStyles(); 559 UndoCompositionAndSelectionStyles();
553 } 560 }
(...skipping 179 matching lines...) Expand 10 before | Expand all | Expand 10 after
733 DeriveFontIfNecessary(font_size, font_height, run->font_style, &run->font); 740 DeriveFontIfNecessary(font_size, font_height, run->font_style, &run->font);
734 ScriptFreeCache(&run->script_cache); 741 ScriptFreeCache(&run->script_cache);
735 } 742 }
736 743
737 // Select the font desired for glyph generation. 744 // Select the font desired for glyph generation.
738 SelectObject(cached_hdc_, run->font.GetNativeFont()); 745 SelectObject(cached_hdc_, run->font.GetNativeFont());
739 746
740 HRESULT hr = E_OUTOFMEMORY; 747 HRESULT hr = E_OUTOFMEMORY;
741 const size_t run_length = run->range.length(); 748 const size_t run_length = run->range.length();
742 const wchar_t* run_text = &(GetLayoutText()[run->range.start()]); 749 const wchar_t* run_text = &(GetLayoutText()[run->range.start()]);
743 // Max glyph guess: http://msdn.microsoft.com/en-us/library/dd368564.aspx 750 // Guess the expected number of glyphs from the length of the run.
751 // 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); 752 size_t max_glyphs = static_cast<size_t>(1.5 * run_length + 16);
745 while (hr == E_OUTOFMEMORY && max_glyphs < kMaxGlyphs) { 753 while (hr == E_OUTOFMEMORY && max_glyphs <= kMaxGlyphs) {
746 run->glyph_count = 0; 754 run->glyph_count = 0;
747 run->glyphs.reset(new WORD[max_glyphs]); 755 run->glyphs.reset(new WORD[max_glyphs]);
748 run->visible_attributes.reset(new SCRIPT_VISATTR[max_glyphs]); 756 run->visible_attributes.reset(new SCRIPT_VISATTR[max_glyphs]);
749 hr = ScriptShape(cached_hdc_, 757 hr = ScriptShape(cached_hdc_, &run->script_cache, run_text, run_length,
750 &run->script_cache, 758 max_glyphs, &run->script_analysis, run->glyphs.get(),
751 run_text, 759 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); 760 &run->glyph_count);
759 max_glyphs *= 2; 761 // Ensure that |kMaxGlyphs| is attempted and the loop terminates afterward.
762 max_glyphs = std::max(max_glyphs + 1, std::min(max_glyphs * 2, kMaxGlyphs));
Alexei Svitkine (slow) 2013/06/28 15:37:02 How about making a helper function for this constr
msw 2013/06/28 16:31:23 I'll pass; 3 uses or real complexity constitutes a
760 } 763 }
761 return hr; 764 return hr;
762 } 765 }
763 766
764 int RenderTextWin::CountCharsWithMissingGlyphs(internal::TextRun* run) const { 767 int RenderTextWin::CountCharsWithMissingGlyphs(internal::TextRun* run) const {
765 int chars_not_missing_glyphs = 0; 768 int chars_not_missing_glyphs = 0;
766 SCRIPT_FONTPROPERTIES properties; 769 SCRIPT_FONTPROPERTIES properties;
767 memset(&properties, 0, sizeof(properties)); 770 memset(&properties, 0, sizeof(properties));
768 properties.cBytes = sizeof(properties); 771 properties.cBytes = sizeof(properties);
769 ScriptGetFontProperties(cached_hdc_, &run->script_cache, &properties); 772 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()); 831 size_t position = LayoutIndexToTextIndex(run->range.end());
829 position = IndexOfAdjacentGrapheme(position, CURSOR_BACKWARD); 832 position = IndexOfAdjacentGrapheme(position, CURSOR_BACKWARD);
830 return SelectionModel(position, CURSOR_FORWARD); 833 return SelectionModel(position, CURSOR_FORWARD);
831 } 834 }
832 835
833 RenderText* RenderText::CreateInstance() { 836 RenderText* RenderText::CreateInstance() {
834 return new RenderTextWin; 837 return new RenderTextWin;
835 } 838 }
836 839
837 } // namespace gfx 840 } // namespace gfx
OLDNEW
« ui/base/text/text_elider.cc ('K') | « ui/gfx/render_text_unittest.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698