Correct blacklist entry message
[chromium-blink-merge.git] / ui / gfx / render_text_mac.cc
blob4feb9302c1860af44dd69912603678f75d474ba3
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 gfx {
20 RenderTextMac::RenderTextMac() : common_baseline_(0), runs_valid_(false) {
23 RenderTextMac::~RenderTextMac() {
26 Size RenderTextMac::GetStringSize() {
27 EnsureLayout();
28 return Size(std::ceil(string_size_.width()), string_size_.height());
31 SizeF RenderTextMac::GetStringSizeF() {
32 EnsureLayout();
33 return string_size_;
36 SelectionModel RenderTextMac::FindCursorPosition(const Point& point) {
37 // TODO(asvitkine): Implement this. http://crbug.com/131618
38 return SelectionModel();
41 std::vector<RenderText::FontSpan> RenderTextMac::GetFontSpansForTesting() {
42 EnsureLayout();
43 if (!runs_valid_)
44 ComputeRuns();
46 std::vector<RenderText::FontSpan> spans;
47 for (size_t i = 0; i < runs_.size(); ++i) {
48 gfx::Font font(runs_[i].font_name, runs_[i].text_size);
49 const CFRange cf_range = CTRunGetStringRange(runs_[i].ct_run);
50 const Range range(cf_range.location, cf_range.location + cf_range.length);
51 spans.push_back(RenderText::FontSpan(font, range));
54 return spans;
57 int RenderTextMac::GetLayoutTextBaseline() {
58 EnsureLayout();
59 return common_baseline_;
62 SelectionModel RenderTextMac::AdjacentCharSelectionModel(
63 const SelectionModel& selection,
64 VisualCursorDirection direction) {
65 // TODO(asvitkine): Implement this. http://crbug.com/131618
66 return SelectionModel();
69 SelectionModel RenderTextMac::AdjacentWordSelectionModel(
70 const SelectionModel& selection,
71 VisualCursorDirection direction) {
72 // TODO(asvitkine): Implement this. http://crbug.com/131618
73 return SelectionModel();
76 Range RenderTextMac::GetGlyphBounds(size_t index) {
77 // TODO(asvitkine): Implement this. http://crbug.com/131618
78 return Range();
81 std::vector<Rect> RenderTextMac::GetSubstringBounds(const Range& range) {
82 // TODO(asvitkine): Implement this. http://crbug.com/131618
83 return std::vector<Rect>();
86 size_t RenderTextMac::TextIndexToLayoutIndex(size_t index) const {
87 // TODO(asvitkine): Implement this. http://crbug.com/131618
88 return index;
91 size_t RenderTextMac::LayoutIndexToTextIndex(size_t index) const {
92 // TODO(asvitkine): Implement this. http://crbug.com/131618
93 return index;
96 bool RenderTextMac::IsCursorablePosition(size_t position) {
97 // TODO(asvitkine): Implement this. http://crbug.com/131618
98 return true;
101 void RenderTextMac::ResetLayout() {
102 line_.reset();
103 attributes_.reset();
104 runs_.clear();
105 runs_valid_ = false;
108 void RenderTextMac::EnsureLayout() {
109 if (line_.get())
110 return;
111 runs_.clear();
112 runs_valid_ = false;
114 const Font& font = GetPrimaryFont();
115 CTFontRef ct_font = base::mac::NSToCFCast(font.GetNativeFont());
117 const void* keys[] = { kCTFontAttributeName };
118 const void* values[] = { ct_font };
119 base::ScopedCFTypeRef<CFDictionaryRef> attributes(
120 CFDictionaryCreate(NULL,
121 keys,
122 values,
123 arraysize(keys),
124 NULL,
125 &kCFTypeDictionaryValueCallBacks));
127 base::ScopedCFTypeRef<CFStringRef> cf_text(
128 base::SysUTF16ToCFStringRef(text()));
129 base::ScopedCFTypeRef<CFAttributedStringRef> attr_text(
130 CFAttributedStringCreate(NULL, cf_text, attributes));
131 base::ScopedCFTypeRef<CFMutableAttributedStringRef> attr_text_mutable(
132 CFAttributedStringCreateMutableCopy(NULL, 0, attr_text));
134 // TODO(asvitkine|msw): Respect GetTextDirection(), which may not match the
135 // natural text direction. See kCTTypesetterOptionForcedEmbeddingLevel, etc.
137 ApplyStyles(attr_text_mutable, ct_font);
138 line_.reset(CTLineCreateWithAttributedString(attr_text_mutable));
140 CGFloat ascent = 0;
141 CGFloat descent = 0;
142 CGFloat leading = 0;
143 // TODO(asvitkine): Consider using CTLineGetBoundsWithOptions() on 10.8+.
144 double width = CTLineGetTypographicBounds(line_, &ascent, &descent, &leading);
145 // Ensure ascent and descent are not smaller than ones of the font list.
146 // Keep them tall enough to draw often-used characters.
147 // For example, if a text field contains a Japanese character, which is
148 // smaller than Latin ones, and then later a Latin one is inserted, this
149 // ensures that the text baseline does not shift.
150 CGFloat font_list_height = font_list().GetHeight();
151 CGFloat font_list_baseline = font_list().GetBaseline();
152 ascent = std::max(ascent, font_list_baseline);
153 descent = std::max(descent, font_list_height - font_list_baseline);
154 string_size_ = SizeF(width, ascent + descent + leading);
155 common_baseline_ = ascent;
158 void RenderTextMac::DrawVisualText(Canvas* canvas) {
159 DCHECK(line_);
160 if (!runs_valid_)
161 ComputeRuns();
163 internal::SkiaTextRenderer renderer(canvas);
164 ApplyFadeEffects(&renderer);
165 ApplyTextShadows(&renderer);
167 for (size_t i = 0; i < runs_.size(); ++i) {
168 const TextRun& run = runs_[i];
169 renderer.SetForegroundColor(run.foreground);
170 renderer.SetTextSize(run.text_size);
171 renderer.SetFontFamilyWithStyle(run.font_name, run.font_style);
172 renderer.DrawPosText(&run.glyph_positions[0], &run.glyphs[0],
173 run.glyphs.size());
174 renderer.DrawDecorations(run.origin.x(), run.origin.y(), run.width,
175 run.underline, run.strike, run.diagonal_strike);
179 RenderTextMac::TextRun::TextRun()
180 : ct_run(NULL),
181 origin(SkPoint::Make(0, 0)),
182 width(0),
183 font_style(Font::NORMAL),
184 text_size(0),
185 foreground(SK_ColorBLACK),
186 underline(false),
187 strike(false),
188 diagonal_strike(false) {
191 RenderTextMac::TextRun::~TextRun() {
194 void RenderTextMac::ApplyStyles(CFMutableAttributedStringRef attr_string,
195 CTFontRef font) {
196 // Temporarily apply composition underlines and selection colors.
197 ApplyCompositionAndSelectionStyles();
199 // Note: CFAttributedStringSetAttribute() does not appear to retain the values
200 // passed in, as can be verified via CFGetRetainCount(). To ensure the
201 // attribute objects do not leak, they are saved to |attributes_|.
202 // Clear the attributes storage.
203 attributes_.reset(CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks));
205 // https://developer.apple.com/library/mac/#documentation/Carbon/Reference/CoreText_StringAttributes_Ref/Reference/reference.html
206 internal::StyleIterator style(colors(), styles());
207 const size_t layout_text_length = GetLayoutText().length();
208 for (size_t i = 0, end = 0; i < layout_text_length; i = end) {
209 end = TextIndexToLayoutIndex(style.GetRange().end());
210 const CFRange range = CFRangeMake(i, end - i);
211 base::ScopedCFTypeRef<CGColorRef> foreground(
212 gfx::CGColorCreateFromSkColor(style.color()));
213 CFAttributedStringSetAttribute(attr_string, range,
214 kCTForegroundColorAttributeName, foreground);
215 CFArrayAppendValue(attributes_, foreground);
217 if (style.style(UNDERLINE)) {
218 CTUnderlineStyle value = kCTUnderlineStyleSingle;
219 base::ScopedCFTypeRef<CFNumberRef> underline_value(
220 CFNumberCreate(NULL, kCFNumberSInt32Type, &value));
221 CFAttributedStringSetAttribute(attr_string, range,
222 kCTUnderlineStyleAttributeName,
223 underline_value);
224 CFArrayAppendValue(attributes_, underline_value);
227 const int traits = (style.style(BOLD) ? kCTFontBoldTrait : 0) |
228 (style.style(ITALIC) ? kCTFontItalicTrait : 0);
229 if (traits != 0) {
230 base::ScopedCFTypeRef<CTFontRef> styled_font(
231 CTFontCreateCopyWithSymbolicTraits(font, 0.0, NULL, traits, traits));
232 // TODO(asvitkine): Handle |styled_font| == NULL case better.
233 if (styled_font) {
234 CFAttributedStringSetAttribute(attr_string, range, kCTFontAttributeName,
235 styled_font);
236 CFArrayAppendValue(attributes_, styled_font);
240 style.UpdatePosition(LayoutIndexToTextIndex(end));
243 // Undo the temporarily applied composition underlines and selection colors.
244 UndoCompositionAndSelectionStyles();
247 void RenderTextMac::ComputeRuns() {
248 DCHECK(line_);
250 CFArrayRef ct_runs = CTLineGetGlyphRuns(line_);
251 const CFIndex ct_runs_count = CFArrayGetCount(ct_runs);
253 // TODO(asvitkine): Don't use GetLineOffset() until draw time, since it may be
254 // updated based on alignment changes without resetting the layout.
255 gfx::Vector2d text_offset = GetLineOffset(0);
256 // Skia will draw glyphs with respect to the baseline.
257 text_offset += gfx::Vector2d(0, common_baseline_);
259 const SkScalar x = SkIntToScalar(text_offset.x());
260 const SkScalar y = SkIntToScalar(text_offset.y());
261 SkPoint run_origin = SkPoint::Make(x, y);
263 const CFRange empty_cf_range = CFRangeMake(0, 0);
264 for (CFIndex i = 0; i < ct_runs_count; ++i) {
265 CTRunRef ct_run =
266 base::mac::CFCast<CTRunRef>(CFArrayGetValueAtIndex(ct_runs, i));
267 const size_t glyph_count = CTRunGetGlyphCount(ct_run);
268 const double run_width =
269 CTRunGetTypographicBounds(ct_run, empty_cf_range, NULL, NULL, NULL);
270 if (glyph_count == 0) {
271 run_origin.offset(run_width, 0);
272 continue;
275 runs_.push_back(TextRun());
276 TextRun* run = &runs_.back();
277 run->ct_run = ct_run;
278 run->origin = run_origin;
279 run->width = run_width;
280 run->glyphs.resize(glyph_count);
281 CTRunGetGlyphs(ct_run, empty_cf_range, &run->glyphs[0]);
282 // CTRunGetGlyphs() sometimes returns glyphs with value 65535 and zero
283 // width (this has been observed at the beginning of a string containing
284 // Arabic content). Passing these to Skia will trigger an assertion;
285 // instead set their values to 0.
286 for (size_t glyph = 0; glyph < glyph_count; glyph++) {
287 if (run->glyphs[glyph] == 65535)
288 run->glyphs[glyph] = 0;
291 run->glyph_positions.resize(glyph_count);
292 const CGPoint* positions_ptr = CTRunGetPositionsPtr(ct_run);
293 std::vector<CGPoint> positions;
294 if (positions_ptr == NULL) {
295 positions.resize(glyph_count);
296 CTRunGetPositions(ct_run, empty_cf_range, &positions[0]);
297 positions_ptr = &positions[0];
299 for (size_t glyph = 0; glyph < glyph_count; glyph++) {
300 SkPoint* point = &run->glyph_positions[glyph];
301 point->set(x + SkDoubleToScalar(positions_ptr[glyph].x),
302 y + SkDoubleToScalar(positions_ptr[glyph].y));
305 // TODO(asvitkine): Style boundaries are not necessarily per-run. Handle
306 // this better. Also, support strike and diagonal_strike.
307 CFDictionaryRef attributes = CTRunGetAttributes(ct_run);
308 CTFontRef ct_font =
309 base::mac::GetValueFromDictionary<CTFontRef>(attributes,
310 kCTFontAttributeName);
311 base::ScopedCFTypeRef<CFStringRef> font_name_ref(
312 CTFontCopyFamilyName(ct_font));
313 run->font_name = base::SysCFStringRefToUTF8(font_name_ref);
314 run->text_size = CTFontGetSize(ct_font);
316 CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(ct_font);
317 if (traits & kCTFontBoldTrait)
318 run->font_style |= Font::BOLD;
319 if (traits & kCTFontItalicTrait)
320 run->font_style |= Font::ITALIC;
322 const CGColorRef foreground =
323 base::mac::GetValueFromDictionary<CGColorRef>(
324 attributes, kCTForegroundColorAttributeName);
325 if (foreground)
326 run->foreground = gfx::CGColorRefToSkColor(foreground);
328 const CFNumberRef underline =
329 base::mac::GetValueFromDictionary<CFNumberRef>(
330 attributes, kCTUnderlineStyleAttributeName);
331 CTUnderlineStyle value = kCTUnderlineStyleNone;
332 if (underline && CFNumberGetValue(underline, kCFNumberSInt32Type, &value))
333 run->underline = (value == kCTUnderlineStyleSingle);
335 run_origin.offset(run_width, 0);
337 runs_valid_ = true;
340 RenderText* RenderText::CreateInstance() {
341 return new RenderTextMac;
344 } // namespace gfx