Separate Simple Backend creation from initialization.
[chromium-blink-merge.git] / ui / gfx / render_text_mac.cc
blobea396ff3eb0c7aa6308172635e99d101c6332667
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 <cmath>
10 #include <utility>
12 #include "base/mac/foundation_util.h"
13 #include "base/mac/scoped_cftyperef.h"
14 #include "base/strings/sys_string_conversions.h"
15 #include "skia/ext/skia_utils_mac.h"
17 namespace gfx {
19 RenderTextMac::RenderTextMac() : common_baseline_(0), runs_valid_(false) {
22 RenderTextMac::~RenderTextMac() {
25 Size RenderTextMac::GetStringSize() {
26 EnsureLayout();
27 return string_size_;
30 int RenderTextMac::GetBaseline() {
31 EnsureLayout();
32 return common_baseline_;
35 SelectionModel RenderTextMac::FindCursorPosition(const Point& point) {
36 // TODO(asvitkine): Implement this. http://crbug.com/131618
37 return SelectionModel();
40 std::vector<RenderText::FontSpan> RenderTextMac::GetFontSpansForTesting() {
41 EnsureLayout();
42 if (!runs_valid_)
43 ComputeRuns();
45 std::vector<RenderText::FontSpan> spans;
46 for (size_t i = 0; i < runs_.size(); ++i) {
47 gfx::Font font(runs_[i].font_name, runs_[i].text_size);
48 const CFRange cf_range = CTRunGetStringRange(runs_[i].ct_run);
49 const ui::Range range(cf_range.location,
50 cf_range.location + cf_range.length);
51 spans.push_back(RenderText::FontSpan(font, range));
54 return spans;
57 SelectionModel RenderTextMac::AdjacentCharSelectionModel(
58 const SelectionModel& selection,
59 VisualCursorDirection direction) {
60 // TODO(asvitkine): Implement this. http://crbug.com/131618
61 return SelectionModel();
64 SelectionModel RenderTextMac::AdjacentWordSelectionModel(
65 const SelectionModel& selection,
66 VisualCursorDirection direction) {
67 // TODO(asvitkine): Implement this. http://crbug.com/131618
68 return SelectionModel();
71 void RenderTextMac::GetGlyphBounds(size_t index,
72 ui::Range* xspan,
73 int* height) {
74 // TODO(asvitkine): Implement this. http://crbug.com/131618
77 std::vector<Rect> RenderTextMac::GetSubstringBounds(const ui::Range& range) {
78 // TODO(asvitkine): Implement this. http://crbug.com/131618
79 return std::vector<Rect>();
82 size_t RenderTextMac::TextIndexToLayoutIndex(size_t index) const {
83 // TODO(asvitkine): Implement this. http://crbug.com/131618
84 return index;
87 size_t RenderTextMac::LayoutIndexToTextIndex(size_t index) const {
88 // TODO(asvitkine): Implement this. http://crbug.com/131618
89 return index;
92 bool RenderTextMac::IsCursorablePosition(size_t position) {
93 // TODO(asvitkine): Implement this. http://crbug.com/131618
94 return false;
97 void RenderTextMac::ResetLayout() {
98 line_.reset();
99 attributes_.reset();
100 runs_.clear();
101 runs_valid_ = false;
104 void RenderTextMac::EnsureLayout() {
105 if (line_.get())
106 return;
107 runs_.clear();
108 runs_valid_ = false;
110 const Font& font = GetFont();
111 base::mac::ScopedCFTypeRef<CFStringRef> font_name_cf_string(
112 base::SysUTF8ToCFStringRef(font.GetFontName()));
113 base::mac::ScopedCFTypeRef<CTFontRef> ct_font(
114 CTFontCreateWithName(font_name_cf_string, font.GetFontSize(), NULL));
116 const void* keys[] = { kCTFontAttributeName };
117 const void* values[] = { ct_font };
118 base::mac::ScopedCFTypeRef<CFDictionaryRef> attributes(
119 CFDictionaryCreate(NULL, keys, values, arraysize(keys), NULL,
120 &kCFTypeDictionaryValueCallBacks));
122 base::mac::ScopedCFTypeRef<CFStringRef> cf_text(
123 base::SysUTF16ToCFStringRef(text()));
124 base::mac::ScopedCFTypeRef<CFAttributedStringRef> attr_text(
125 CFAttributedStringCreate(NULL, cf_text, attributes));
126 base::mac::ScopedCFTypeRef<CFMutableAttributedStringRef> attr_text_mutable(
127 CFAttributedStringCreateMutableCopy(NULL, 0, attr_text));
129 // TODO(asvitkine|msw): Respect GetTextDirection(), which may not match the
130 // natural text direction. See kCTTypesetterOptionForcedEmbeddingLevel, etc.
132 ApplyStyles(attr_text_mutable, ct_font);
133 line_.reset(CTLineCreateWithAttributedString(attr_text_mutable));
135 CGFloat ascent = 0;
136 CGFloat descent = 0;
137 CGFloat leading = 0;
138 // TODO(asvitkine): Consider using CTLineGetBoundsWithOptions() on 10.8+.
139 double width = CTLineGetTypographicBounds(line_, &ascent, &descent, &leading);
140 string_size_ = Size(width, ascent + descent + leading);
141 common_baseline_ = ascent;
144 void RenderTextMac::DrawVisualText(Canvas* canvas) {
145 DCHECK(line_);
146 if (!runs_valid_)
147 ComputeRuns();
149 internal::SkiaTextRenderer renderer(canvas);
150 ApplyFadeEffects(&renderer);
151 ApplyTextShadows(&renderer);
153 for (size_t i = 0; i < runs_.size(); ++i) {
154 const TextRun& run = runs_[i];
155 renderer.SetForegroundColor(run.foreground);
156 renderer.SetTextSize(run.text_size);
157 renderer.SetFontFamilyWithStyle(run.font_name, run.font_style);
158 renderer.DrawPosText(&run.glyph_positions[0], &run.glyphs[0],
159 run.glyphs.size());
160 renderer.DrawDecorations(run.origin.x(), run.origin.y(), run.width,
161 run.underline, run.strike, run.diagonal_strike);
165 RenderTextMac::TextRun::TextRun()
166 : ct_run(NULL),
167 origin(SkPoint::Make(0, 0)),
168 width(0),
169 font_style(Font::NORMAL),
170 text_size(0),
171 foreground(SK_ColorBLACK),
172 underline(false),
173 strike(false),
174 diagonal_strike(false) {
177 RenderTextMac::TextRun::~TextRun() {
180 void RenderTextMac::ApplyStyles(CFMutableAttributedStringRef attr_string,
181 CTFontRef font) {
182 // Temporarily apply composition underlines and selection colors.
183 ApplyCompositionAndSelectionStyles();
185 // Note: CFAttributedStringSetAttribute() does not appear to retain the values
186 // passed in, as can be verified via CFGetRetainCount(). To ensure the
187 // attribute objects do not leak, they are saved to |attributes_|.
188 // Clear the attributes storage.
189 attributes_.reset(CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks));
191 // https://developer.apple.com/library/mac/#documentation/Carbon/Reference/CoreText_StringAttributes_Ref/Reference/reference.html
192 internal::StyleIterator style(colors(), styles());
193 const size_t layout_text_length = GetLayoutText().length();
194 for (size_t i = 0, end = 0; i < layout_text_length; i = end) {
195 end = TextIndexToLayoutIndex(style.GetRange().end());
196 const CFRange range = CFRangeMake(i, end - i);
197 base::mac::ScopedCFTypeRef<CGColorRef> foreground(
198 gfx::CGColorCreateFromSkColor(style.color()));
199 CFAttributedStringSetAttribute(attr_string, range,
200 kCTForegroundColorAttributeName, foreground);
201 CFArrayAppendValue(attributes_, foreground);
203 if (style.style(UNDERLINE)) {
204 CTUnderlineStyle value = kCTUnderlineStyleSingle;
205 base::mac::ScopedCFTypeRef<CFNumberRef> underline_value(
206 CFNumberCreate(NULL, kCFNumberSInt32Type, &value));
207 CFAttributedStringSetAttribute(attr_string, range,
208 kCTUnderlineStyleAttributeName,
209 underline_value);
210 CFArrayAppendValue(attributes_, underline_value);
213 const int traits = (style.style(BOLD) ? kCTFontBoldTrait : 0) |
214 (style.style(ITALIC) ? kCTFontItalicTrait : 0);
215 if (traits != 0) {
216 base::mac::ScopedCFTypeRef<CTFontRef> styled_font(
217 CTFontCreateCopyWithSymbolicTraits(font, 0.0, NULL, traits, traits));
218 // TODO(asvitkine): Handle |styled_font| == NULL case better.
219 if (styled_font) {
220 CFAttributedStringSetAttribute(attr_string, range, kCTFontAttributeName,
221 styled_font);
222 CFArrayAppendValue(attributes_, styled_font);
226 style.UpdatePosition(LayoutIndexToTextIndex(end));
229 // Undo the temporarily applied composition underlines and selection colors.
230 UndoCompositionAndSelectionStyles();
233 void RenderTextMac::ComputeRuns() {
234 DCHECK(line_);
236 CFArrayRef ct_runs = CTLineGetGlyphRuns(line_);
237 const CFIndex ct_runs_count = CFArrayGetCount(ct_runs);
239 gfx::Vector2d text_offset = GetTextOffset();
240 // Skia will draw glyphs with respect to the baseline.
241 text_offset += gfx::Vector2d(0, common_baseline_);
243 const SkScalar x = SkIntToScalar(text_offset.x());
244 const SkScalar y = SkIntToScalar(text_offset.y());
245 SkPoint run_origin = SkPoint::Make(x, y);
247 const CFRange empty_cf_range = CFRangeMake(0, 0);
248 for (CFIndex i = 0; i < ct_runs_count; ++i) {
249 CTRunRef ct_run =
250 base::mac::CFCast<CTRunRef>(CFArrayGetValueAtIndex(ct_runs, i));
251 const size_t glyph_count = CTRunGetGlyphCount(ct_run);
252 const double run_width =
253 CTRunGetTypographicBounds(ct_run, empty_cf_range, NULL, NULL, NULL);
254 if (glyph_count == 0) {
255 run_origin.offset(run_width, 0);
256 continue;
259 runs_.push_back(TextRun());
260 TextRun* run = &runs_.back();
261 run->ct_run = ct_run;
262 run->origin = run_origin;
263 run->width = run_width;
264 run->glyphs.resize(glyph_count);
265 CTRunGetGlyphs(ct_run, empty_cf_range, &run->glyphs[0]);
266 // CTRunGetGlyphs() sometimes returns glyphs with value 65535 and zero
267 // width (this has been observed at the beginning of a string containing
268 // Arabic content). Passing these to Skia will trigger an assertion;
269 // instead set their values to 0.
270 for (size_t glyph = 0; glyph < glyph_count; glyph++) {
271 if (run->glyphs[glyph] == 65535)
272 run->glyphs[glyph] = 0;
275 run->glyph_positions.resize(glyph_count);
276 const CGPoint* positions_ptr = CTRunGetPositionsPtr(ct_run);
277 std::vector<CGPoint> positions;
278 if (positions_ptr == NULL) {
279 positions.resize(glyph_count);
280 CTRunGetPositions(ct_run, empty_cf_range, &positions[0]);
281 positions_ptr = &positions[0];
283 for (size_t glyph = 0; glyph < glyph_count; glyph++) {
284 SkPoint* point = &run->glyph_positions[glyph];
285 point->set(x + SkDoubleToScalar(positions_ptr[glyph].x),
286 y + SkDoubleToScalar(positions_ptr[glyph].y));
289 // TODO(asvitkine): Style boundaries are not necessarily per-run. Handle
290 // this better. Also, support strike and diagonal_strike.
291 CFDictionaryRef attributes = CTRunGetAttributes(ct_run);
292 CTFontRef ct_font =
293 base::mac::GetValueFromDictionary<CTFontRef>(attributes,
294 kCTFontAttributeName);
295 base::mac::ScopedCFTypeRef<CFStringRef> font_name_ref(
296 CTFontCopyFamilyName(ct_font));
297 run->font_name = base::SysCFStringRefToUTF8(font_name_ref);
298 run->text_size = CTFontGetSize(ct_font);
300 CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(ct_font);
301 if (traits & kCTFontBoldTrait)
302 run->font_style |= Font::BOLD;
303 if (traits & kCTFontItalicTrait)
304 run->font_style |= Font::ITALIC;
306 const CGColorRef foreground =
307 base::mac::GetValueFromDictionary<CGColorRef>(
308 attributes, kCTForegroundColorAttributeName);
309 if (foreground)
310 run->foreground = gfx::CGColorRefToSkColor(foreground);
312 const CFNumberRef underline =
313 base::mac::GetValueFromDictionary<CFNumberRef>(
314 attributes, kCTUnderlineStyleAttributeName);
315 CTUnderlineStyle value = kCTUnderlineStyleNone;
316 if (underline && CFNumberGetValue(underline, kCFNumberSInt32Type, &value))
317 run->underline = (value == kCTUnderlineStyleSingle);
319 run_origin.offset(run_width, 0);
321 runs_valid_ = true;
324 RenderText* RenderText::CreateInstance() {
325 return new RenderTextMac;
328 } // namespace gfx