Exclude TrayIMETest.PerformActionOnDetailedView under valgrind, where it crashes.
[chromium-blink-merge.git] / ui / gfx / render_text_mac.cc
blobae5523142ad6de24a411260c6d376bf943d6075c
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ui/gfx/render_text_mac.h"
7 #include <ApplicationServices/ApplicationServices.h>
9 #include <algorithm>
10 #include <cmath>
11 #include <utility>
13 #include "base/mac/foundation_util.h"
14 #include "base/mac/scoped_cftyperef.h"
15 #include "base/strings/sys_string_conversions.h"
16 #include "skia/ext/skia_utils_mac.h"
18 namespace {
20 // This function makes a copy of |font| with the given symbolic traits. This
21 // function is similar to CTFontCreateCopyWithSymbolicTraits, but this function
22 // works on OSX 10.10, unlike CTFontCreateCopyWithSymbolicTraits.
23 base::ScopedCFTypeRef<CTFontRef> CopyFontWithSymbolicTraits(CTFontRef font,
24 int sym_traits) {
25 base::ScopedCFTypeRef<CTFontDescriptorRef> orig_desc(
26 CTFontCopyFontDescriptor(font));
27 base::ScopedCFTypeRef<CFDictionaryRef> orig_attributes(
28 CTFontDescriptorCopyAttributes(orig_desc));
29 // Make a mutable copy of orig_attributes.
30 base::ScopedCFTypeRef<CFMutableDictionaryRef> attributes(
31 CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, orig_attributes));
33 base::ScopedCFTypeRef<CFMutableDictionaryRef> traits(
34 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
35 &kCFTypeDictionaryKeyCallBacks,
36 &kCFTypeDictionaryValueCallBacks));
37 base::ScopedCFTypeRef<CFNumberRef> n(
38 CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &sym_traits));
39 CFDictionarySetValue(traits, kCTFontSymbolicTrait, n.release());
40 CFDictionarySetValue(attributes, kCTFontTraitsAttribute, traits.release());
42 base::ScopedCFTypeRef<CFStringRef> family_name(CTFontCopyFamilyName(font));
43 CFDictionarySetValue(attributes, kCTFontNameAttribute, family_name.release());
45 base::ScopedCFTypeRef<CTFontDescriptorRef> desc(
46 CTFontDescriptorCreateWithAttributes(attributes));
47 return base::ScopedCFTypeRef<CTFontRef>(
48 CTFontCreateWithFontDescriptor(desc, 0.0, nullptr));
51 } // namespace
53 namespace gfx {
55 RenderTextMac::RenderTextMac()
56 : common_baseline_(0), runs_valid_(false) {
59 RenderTextMac::~RenderTextMac() {
62 scoped_ptr<RenderText> RenderTextMac::CreateInstanceOfSameType() const {
63 return make_scoped_ptr(new RenderTextMac);
66 bool RenderTextMac::MultilineSupported() const {
67 return false;
70 const base::string16& RenderTextMac::GetDisplayText() {
71 return text_elided() ? display_text() : layout_text();
74 Size RenderTextMac::GetStringSize() {
75 SizeF size_f = GetStringSizeF();
76 return Size(std::ceil(size_f.width()), size_f.height());
79 SizeF RenderTextMac::GetStringSizeF() {
80 EnsureLayout();
81 return string_size_;
84 SelectionModel RenderTextMac::FindCursorPosition(const Point& point) {
85 // TODO(asvitkine): Implement this. http://crbug.com/131618
86 return SelectionModel();
89 std::vector<RenderText::FontSpan> RenderTextMac::GetFontSpansForTesting() {
90 EnsureLayout();
91 if (!runs_valid_)
92 ComputeRuns();
94 std::vector<RenderText::FontSpan> spans;
95 for (size_t i = 0; i < runs_.size(); ++i) {
96 Font font(runs_[i].font_name, runs_[i].text_size);
97 const CFRange cf_range = CTRunGetStringRange(runs_[i].ct_run);
98 const Range range(cf_range.location, cf_range.location + cf_range.length);
99 spans.push_back(RenderText::FontSpan(font, range));
102 return spans;
105 int RenderTextMac::GetDisplayTextBaseline() {
106 EnsureLayout();
107 return common_baseline_;
110 SelectionModel RenderTextMac::AdjacentCharSelectionModel(
111 const SelectionModel& selection,
112 VisualCursorDirection direction) {
113 // TODO(asvitkine): Implement this. http://crbug.com/131618
114 return SelectionModel();
117 SelectionModel RenderTextMac::AdjacentWordSelectionModel(
118 const SelectionModel& selection,
119 VisualCursorDirection direction) {
120 // TODO(asvitkine): Implement this. http://crbug.com/131618
121 return SelectionModel();
124 Range RenderTextMac::GetGlyphBounds(size_t index) {
125 // TODO(asvitkine): Implement this. http://crbug.com/131618
126 return Range();
129 std::vector<Rect> RenderTextMac::GetSubstringBounds(const Range& range) {
130 // TODO(asvitkine): Implement this. http://crbug.com/131618
131 return std::vector<Rect>();
134 size_t RenderTextMac::TextIndexToDisplayIndex(size_t index) {
135 // TODO(asvitkine): Implement this. http://crbug.com/131618
136 return index;
139 size_t RenderTextMac::DisplayIndexToTextIndex(size_t index) {
140 // TODO(asvitkine): Implement this. http://crbug.com/131618
141 return index;
144 bool RenderTextMac::IsValidCursorIndex(size_t index) {
145 // TODO(asvitkine): Implement this. http://crbug.com/131618
146 return IsValidLogicalIndex(index);
149 void RenderTextMac::OnLayoutTextAttributeChanged(bool text_changed) {
150 DCHECK(!multiline()) << "RenderTextMac does not support multi line";
151 if (text_changed) {
152 if (elide_behavior() != NO_ELIDE &&
153 elide_behavior() != FADE_TAIL &&
154 !layout_text().empty()) {
155 UpdateDisplayText(std::ceil(GetLayoutTextWidth()));
156 } else {
157 UpdateDisplayText(0);
160 line_.reset();
161 attributes_.reset();
162 runs_.clear();
163 runs_valid_ = false;
166 void RenderTextMac::OnDisplayTextAttributeChanged() {
167 OnLayoutTextAttributeChanged(true);
170 void RenderTextMac::EnsureLayout() {
171 if (line_.get())
172 return;
173 runs_.clear();
174 runs_valid_ = false;
176 line_ = EnsureLayoutInternal(GetDisplayText(), &attributes_);
177 string_size_ = GetCTLineSize(line_.get(), &common_baseline_);
180 void RenderTextMac::DrawVisualText(Canvas* canvas) {
181 DCHECK(line_);
182 if (!runs_valid_)
183 ComputeRuns();
185 internal::SkiaTextRenderer renderer(canvas);
186 ApplyFadeEffects(&renderer);
187 ApplyTextShadows(&renderer);
189 for (size_t i = 0; i < runs_.size(); ++i) {
190 const TextRun& run = runs_[i];
191 renderer.SetForegroundColor(run.foreground);
192 renderer.SetTextSize(run.text_size);
193 renderer.SetFontFamilyWithStyle(run.font_name, run.font_style);
194 renderer.DrawPosText(&run.glyph_positions[0], &run.glyphs[0],
195 run.glyphs.size());
196 renderer.DrawDecorations(run.origin.x(), run.origin.y(), run.width,
197 run.underline, run.strike, run.diagonal_strike);
200 renderer.EndDiagonalStrike();
203 RenderTextMac::TextRun::TextRun()
204 : ct_run(NULL),
205 origin(SkPoint::Make(0, 0)),
206 width(0),
207 font_style(Font::NORMAL),
208 text_size(0),
209 foreground(SK_ColorBLACK),
210 underline(false),
211 strike(false),
212 diagonal_strike(false) {
215 RenderTextMac::TextRun::~TextRun() {
218 float RenderTextMac::GetLayoutTextWidth() {
219 base::ScopedCFTypeRef<CFMutableArrayRef> attributes_owner;
220 base::ScopedCFTypeRef<CTLineRef> line(
221 EnsureLayoutInternal(layout_text(), &attributes_owner));
222 SkScalar baseline;
223 return GetCTLineSize(line.get(), &baseline).width();
226 gfx::SizeF RenderTextMac::GetCTLineSize(CTLineRef line, SkScalar* baseline) {
227 CGFloat ascent = 0;
228 CGFloat descent = 0;
229 CGFloat leading = 0;
230 // TODO(asvitkine): Consider using CTLineGetBoundsWithOptions() on 10.8+.
231 double width = CTLineGetTypographicBounds(line, &ascent, &descent, &leading);
232 // Ensure ascent and descent are not smaller than ones of the font list.
233 // Keep them tall enough to draw often-used characters.
234 // For example, if a text field contains a Japanese character, which is
235 // smaller than Latin ones, and then later a Latin one is inserted, this
236 // ensures that the text baseline does not shift.
237 CGFloat font_list_height = font_list().GetHeight();
238 CGFloat font_list_baseline = font_list().GetBaseline();
239 ascent = std::max(ascent, font_list_baseline);
240 descent = std::max(descent, font_list_height - font_list_baseline);
241 *baseline = ascent;
242 return SizeF(
243 width, std::max(ascent + descent + leading,
244 static_cast<CGFloat>(min_line_height())));
247 base::ScopedCFTypeRef<CTLineRef> RenderTextMac::EnsureLayoutInternal(
248 const base::string16& text,
249 base::ScopedCFTypeRef<CFMutableArrayRef>* attributes_owner) {
250 CTFontRef ct_font = base::mac::NSToCFCast(
251 font_list().GetPrimaryFont().GetNativeFont());
253 const void* keys[] = { kCTFontAttributeName };
254 const void* values[] = { ct_font };
255 base::ScopedCFTypeRef<CFDictionaryRef> attributes(
256 CFDictionaryCreate(NULL,
257 keys,
258 values,
259 arraysize(keys),
260 NULL,
261 &kCFTypeDictionaryValueCallBacks));
263 base::ScopedCFTypeRef<CFStringRef> cf_text(
264 base::SysUTF16ToCFStringRef(text));
265 base::ScopedCFTypeRef<CFAttributedStringRef> attr_text(
266 CFAttributedStringCreate(NULL, cf_text, attributes));
267 base::ScopedCFTypeRef<CFMutableAttributedStringRef> attr_text_mutable(
268 CFAttributedStringCreateMutableCopy(NULL, 0, attr_text));
270 // TODO(asvitkine|msw): Respect GetTextDirection(), which may not match the
271 // natural text direction. See kCTTypesetterOptionForcedEmbeddingLevel, etc.
273 *attributes_owner = ApplyStyles(text, attr_text_mutable, ct_font);
274 return base::ScopedCFTypeRef<CTLineRef>(
275 CTLineCreateWithAttributedString(attr_text_mutable));
278 base::ScopedCFTypeRef<CFMutableArrayRef> RenderTextMac::ApplyStyles(
279 const base::string16& text,
280 CFMutableAttributedStringRef attr_string,
281 CTFontRef font) {
282 // Temporarily apply composition underlines and selection colors.
283 ApplyCompositionAndSelectionStyles();
285 // Note: CFAttributedStringSetAttribute() does not appear to retain the values
286 // passed in, as can be verified via CFGetRetainCount(). To ensure the
287 // attribute objects do not leak, they are saved to |attributes_|.
288 // Clear the attributes storage.
289 base::ScopedCFTypeRef<CFMutableArrayRef> attributes(
290 CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks));
292 // https://developer.apple.com/library/mac/#documentation/Carbon/Reference/CoreText_StringAttributes_Ref/Reference/reference.html
293 internal::StyleIterator style(colors(), baselines(), styles());
294 const size_t layout_text_length = CFAttributedStringGetLength(attr_string);
295 for (size_t i = 0, end = 0; i < layout_text_length; i = end) {
296 end = TextIndexToGivenTextIndex(text, style.GetRange().end());
297 const CFRange range = CFRangeMake(i, end - i);
298 base::ScopedCFTypeRef<CGColorRef> foreground(
299 CGColorCreateFromSkColor(style.color()));
300 CFAttributedStringSetAttribute(attr_string, range,
301 kCTForegroundColorAttributeName, foreground);
302 CFArrayAppendValue(attributes, foreground);
304 if (style.style(UNDERLINE)) {
305 CTUnderlineStyle value = kCTUnderlineStyleSingle;
306 base::ScopedCFTypeRef<CFNumberRef> underline_value(
307 CFNumberCreate(NULL, kCFNumberSInt32Type, &value));
308 CFAttributedStringSetAttribute(attr_string, range,
309 kCTUnderlineStyleAttributeName,
310 underline_value);
311 CFArrayAppendValue(attributes, underline_value);
314 const int traits = (style.style(BOLD) ? kCTFontBoldTrait : 0) |
315 (style.style(ITALIC) ? kCTFontItalicTrait : 0);
316 if (traits != 0) {
317 base::ScopedCFTypeRef<CTFontRef> styled_font =
318 CopyFontWithSymbolicTraits(font, traits);
319 // TODO(asvitkine): Handle |styled_font| == NULL case better.
320 if (styled_font) {
321 CFAttributedStringSetAttribute(attr_string, range, kCTFontAttributeName,
322 styled_font);
323 CFArrayAppendValue(attributes, styled_font);
327 style.UpdatePosition(DisplayIndexToTextIndex(end));
330 // Undo the temporarily applied composition underlines and selection colors.
331 UndoCompositionAndSelectionStyles();
333 return attributes;
336 void RenderTextMac::ComputeRuns() {
337 DCHECK(line_);
339 CFArrayRef ct_runs = CTLineGetGlyphRuns(line_);
340 const CFIndex ct_runs_count = CFArrayGetCount(ct_runs);
342 // TODO(asvitkine): Don't use GetLineOffset() until draw time, since it may be
343 // updated based on alignment changes without resetting the layout.
344 Vector2d text_offset = GetLineOffset(0);
345 // Skia will draw glyphs with respect to the baseline.
346 text_offset += Vector2d(0, common_baseline_);
348 const SkScalar x = SkIntToScalar(text_offset.x());
349 const SkScalar y = SkIntToScalar(text_offset.y());
350 SkPoint run_origin = SkPoint::Make(x, y);
352 const CFRange empty_cf_range = CFRangeMake(0, 0);
353 for (CFIndex i = 0; i < ct_runs_count; ++i) {
354 CTRunRef ct_run =
355 base::mac::CFCast<CTRunRef>(CFArrayGetValueAtIndex(ct_runs, i));
356 const size_t glyph_count = CTRunGetGlyphCount(ct_run);
357 const double run_width =
358 CTRunGetTypographicBounds(ct_run, empty_cf_range, NULL, NULL, NULL);
359 if (glyph_count == 0) {
360 run_origin.offset(run_width, 0);
361 continue;
364 runs_.push_back(TextRun());
365 TextRun* run = &runs_.back();
366 run->ct_run = ct_run;
367 run->origin = run_origin;
368 run->width = run_width;
369 run->glyphs.resize(glyph_count);
370 CTRunGetGlyphs(ct_run, empty_cf_range, &run->glyphs[0]);
371 // CTRunGetGlyphs() sometimes returns glyphs with value 65535 and zero
372 // width (this has been observed at the beginning of a string containing
373 // Arabic content). Passing these to Skia will trigger an assertion;
374 // instead set their values to 0.
375 for (size_t glyph = 0; glyph < glyph_count; glyph++) {
376 if (run->glyphs[glyph] == 65535)
377 run->glyphs[glyph] = 0;
380 run->glyph_positions.resize(glyph_count);
381 const CGPoint* positions_ptr = CTRunGetPositionsPtr(ct_run);
382 std::vector<CGPoint> positions;
383 if (positions_ptr == NULL) {
384 positions.resize(glyph_count);
385 CTRunGetPositions(ct_run, empty_cf_range, &positions[0]);
386 positions_ptr = &positions[0];
388 for (size_t glyph = 0; glyph < glyph_count; glyph++) {
389 SkPoint* point = &run->glyph_positions[glyph];
390 point->set(x + SkDoubleToScalar(positions_ptr[glyph].x),
391 y + SkDoubleToScalar(positions_ptr[glyph].y));
394 // TODO(asvitkine): Style boundaries are not necessarily per-run. Handle
395 // this better. Also, support strike and diagonal_strike.
396 CFDictionaryRef attributes = CTRunGetAttributes(ct_run);
397 CTFontRef ct_font =
398 base::mac::GetValueFromDictionary<CTFontRef>(attributes,
399 kCTFontAttributeName);
400 base::ScopedCFTypeRef<CFStringRef> font_name_ref(
401 CTFontCopyFamilyName(ct_font));
402 run->font_name = base::SysCFStringRefToUTF8(font_name_ref);
403 run->text_size = CTFontGetSize(ct_font);
405 CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(ct_font);
406 if (traits & kCTFontBoldTrait)
407 run->font_style |= Font::BOLD;
408 if (traits & kCTFontItalicTrait)
409 run->font_style |= Font::ITALIC;
411 const CGColorRef foreground =
412 base::mac::GetValueFromDictionary<CGColorRef>(
413 attributes, kCTForegroundColorAttributeName);
414 if (foreground)
415 run->foreground = CGColorRefToSkColor(foreground);
417 const CFNumberRef underline =
418 base::mac::GetValueFromDictionary<CFNumberRef>(
419 attributes, kCTUnderlineStyleAttributeName);
420 CTUnderlineStyle value = kCTUnderlineStyleNone;
421 if (underline && CFNumberGetValue(underline, kCFNumberSInt32Type, &value))
422 run->underline = (value == kCTUnderlineStyleSingle);
424 run_origin.offset(run_width, 0);
426 runs_valid_ = true;
429 } // namespace gfx