Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / ui / gfx / render_text_mac.cc
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.
4
5 #include "ui/gfx/render_text_mac.h"
6
7 #include <ApplicationServices/ApplicationServices.h>
8
9 #include <algorithm>
10 #include <cmath>
11 #include <utility>
12
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"
17
18 namespace gfx {
19
20 RenderTextMac::RenderTextMac() : common_baseline_(0), runs_valid_(false) {
21 }
22
23 RenderTextMac::~RenderTextMac() {
24 }
25
26 Size RenderTextMac::GetStringSize() {
27   EnsureLayout();
28   return Size(std::ceil(string_size_.width()), string_size_.height());
29 }
30
31 SizeF RenderTextMac::GetStringSizeF() {
32   EnsureLayout();
33   return string_size_;
34 }
35
36 SelectionModel RenderTextMac::FindCursorPosition(const Point& point) {
37   // TODO(asvitkine): Implement this. http://crbug.com/131618
38   return SelectionModel();
39 }
40
41 std::vector<RenderText::FontSpan> RenderTextMac::GetFontSpansForTesting() {
42   EnsureLayout();
43   if (!runs_valid_)
44     ComputeRuns();
45
46   std::vector<RenderText::FontSpan> spans;
47   for (size_t i = 0; i < runs_.size(); ++i) {
48     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));
52   }
53
54   return spans;
55 }
56
57 int RenderTextMac::GetLayoutTextBaseline() {
58   EnsureLayout();
59   return common_baseline_;
60 }
61
62 SelectionModel RenderTextMac::AdjacentCharSelectionModel(
63     const SelectionModel& selection,
64     VisualCursorDirection direction) {
65   // TODO(asvitkine): Implement this. http://crbug.com/131618
66   return SelectionModel();
67 }
68
69 SelectionModel RenderTextMac::AdjacentWordSelectionModel(
70     const SelectionModel& selection,
71     VisualCursorDirection direction) {
72   // TODO(asvitkine): Implement this. http://crbug.com/131618
73   return SelectionModel();
74 }
75
76 Range RenderTextMac::GetGlyphBounds(size_t index) {
77   // TODO(asvitkine): Implement this. http://crbug.com/131618
78   return Range();
79 }
80
81 std::vector<Rect> RenderTextMac::GetSubstringBounds(const Range& range) {
82   // TODO(asvitkine): Implement this. http://crbug.com/131618
83   return std::vector<Rect>();
84 }
85
86 size_t RenderTextMac::TextIndexToLayoutIndex(size_t index) const {
87   // TODO(asvitkine): Implement this. http://crbug.com/131618
88   return index;
89 }
90
91 size_t RenderTextMac::LayoutIndexToTextIndex(size_t index) const {
92   // TODO(asvitkine): Implement this. http://crbug.com/131618
93   return index;
94 }
95
96 bool RenderTextMac::IsValidCursorIndex(size_t index) {
97   // TODO(asvitkine): Implement this. http://crbug.com/131618
98   return IsValidLogicalIndex(index);
99 }
100
101 void RenderTextMac::ResetLayout() {
102   line_.reset();
103   attributes_.reset();
104   runs_.clear();
105   runs_valid_ = false;
106 }
107
108 void RenderTextMac::EnsureLayout() {
109   if (line_.get())
110     return;
111   runs_.clear();
112   runs_valid_ = false;
113
114   CTFontRef ct_font = base::mac::NSToCFCast(
115       font_list().GetPrimaryFont().GetNativeFont());
116
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));
126
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));
133
134   // TODO(asvitkine|msw): Respect GetTextDirection(), which may not match the
135   // natural text direction. See kCTTypesetterOptionForcedEmbeddingLevel, etc.
136
137   ApplyStyles(attr_text_mutable, ct_font);
138   line_.reset(CTLineCreateWithAttributedString(attr_text_mutable));
139
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;
156 }
157
158 void RenderTextMac::DrawVisualText(Canvas* canvas) {
159   DCHECK(line_);
160   if (!runs_valid_)
161     ComputeRuns();
162
163   internal::SkiaTextRenderer renderer(canvas);
164   ApplyFadeEffects(&renderer);
165   ApplyTextShadows(&renderer);
166
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);
176   }
177
178   renderer.EndDiagonalStrike();
179 }
180
181 RenderTextMac::TextRun::TextRun()
182     : ct_run(NULL),
183       origin(SkPoint::Make(0, 0)),
184       width(0),
185       font_style(Font::NORMAL),
186       text_size(0),
187       foreground(SK_ColorBLACK),
188       underline(false),
189       strike(false),
190       diagonal_strike(false) {
191 }
192
193 RenderTextMac::TextRun::~TextRun() {
194 }
195
196 void RenderTextMac::ApplyStyles(CFMutableAttributedStringRef attr_string,
197                                 CTFontRef font) {
198   // Temporarily apply composition underlines and selection colors.
199   ApplyCompositionAndSelectionStyles();
200
201   // Note: CFAttributedStringSetAttribute() does not appear to retain the values
202   // passed in, as can be verified via CFGetRetainCount(). To ensure the
203   // attribute objects do not leak, they are saved to |attributes_|.
204   // Clear the attributes storage.
205   attributes_.reset(CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks));
206
207   // https://developer.apple.com/library/mac/#documentation/Carbon/Reference/CoreText_StringAttributes_Ref/Reference/reference.html
208   internal::StyleIterator style(colors(), styles());
209   const size_t layout_text_length = GetLayoutText().length();
210   for (size_t i = 0, end = 0; i < layout_text_length; i = end) {
211     end = TextIndexToLayoutIndex(style.GetRange().end());
212     const CFRange range = CFRangeMake(i, end - i);
213     base::ScopedCFTypeRef<CGColorRef> foreground(
214         CGColorCreateFromSkColor(style.color()));
215     CFAttributedStringSetAttribute(attr_string, range,
216         kCTForegroundColorAttributeName, foreground);
217     CFArrayAppendValue(attributes_, foreground);
218
219     if (style.style(UNDERLINE)) {
220       CTUnderlineStyle value = kCTUnderlineStyleSingle;
221       base::ScopedCFTypeRef<CFNumberRef> underline_value(
222           CFNumberCreate(NULL, kCFNumberSInt32Type, &value));
223       CFAttributedStringSetAttribute(attr_string, range,
224                                      kCTUnderlineStyleAttributeName,
225                                      underline_value);
226       CFArrayAppendValue(attributes_, underline_value);
227     }
228
229     const int traits = (style.style(BOLD) ? kCTFontBoldTrait : 0) |
230                        (style.style(ITALIC) ? kCTFontItalicTrait : 0);
231     if (traits != 0) {
232       base::ScopedCFTypeRef<CTFontRef> styled_font(
233           CTFontCreateCopyWithSymbolicTraits(font, 0.0, NULL, traits, traits));
234       // TODO(asvitkine): Handle |styled_font| == NULL case better.
235       if (styled_font) {
236         CFAttributedStringSetAttribute(attr_string, range, kCTFontAttributeName,
237                                        styled_font);
238         CFArrayAppendValue(attributes_, styled_font);
239       }
240     }
241
242     style.UpdatePosition(LayoutIndexToTextIndex(end));
243   }
244
245   // Undo the temporarily applied composition underlines and selection colors.
246   UndoCompositionAndSelectionStyles();
247 }
248
249 void RenderTextMac::ComputeRuns() {
250   DCHECK(line_);
251
252   CFArrayRef ct_runs = CTLineGetGlyphRuns(line_);
253   const CFIndex ct_runs_count = CFArrayGetCount(ct_runs);
254
255   // TODO(asvitkine): Don't use GetLineOffset() until draw time, since it may be
256   // updated based on alignment changes without resetting the layout.
257   Vector2d text_offset = GetLineOffset(0);
258   // Skia will draw glyphs with respect to the baseline.
259   text_offset += Vector2d(0, common_baseline_);
260
261   const SkScalar x = SkIntToScalar(text_offset.x());
262   const SkScalar y = SkIntToScalar(text_offset.y());
263   SkPoint run_origin = SkPoint::Make(x, y);
264
265   const CFRange empty_cf_range = CFRangeMake(0, 0);
266   for (CFIndex i = 0; i < ct_runs_count; ++i) {
267     CTRunRef ct_run =
268         base::mac::CFCast<CTRunRef>(CFArrayGetValueAtIndex(ct_runs, i));
269     const size_t glyph_count = CTRunGetGlyphCount(ct_run);
270     const double run_width =
271         CTRunGetTypographicBounds(ct_run, empty_cf_range, NULL, NULL, NULL);
272     if (glyph_count == 0) {
273       run_origin.offset(run_width, 0);
274       continue;
275     }
276
277     runs_.push_back(TextRun());
278     TextRun* run = &runs_.back();
279     run->ct_run = ct_run;
280     run->origin = run_origin;
281     run->width = run_width;
282     run->glyphs.resize(glyph_count);
283     CTRunGetGlyphs(ct_run, empty_cf_range, &run->glyphs[0]);
284     // CTRunGetGlyphs() sometimes returns glyphs with value 65535 and zero
285     // width (this has been observed at the beginning of a string containing
286     // Arabic content). Passing these to Skia will trigger an assertion;
287     // instead set their values to 0.
288     for (size_t glyph = 0; glyph < glyph_count; glyph++) {
289       if (run->glyphs[glyph] == 65535)
290         run->glyphs[glyph] = 0;
291     }
292
293     run->glyph_positions.resize(glyph_count);
294     const CGPoint* positions_ptr = CTRunGetPositionsPtr(ct_run);
295     std::vector<CGPoint> positions;
296     if (positions_ptr == NULL) {
297       positions.resize(glyph_count);
298       CTRunGetPositions(ct_run, empty_cf_range, &positions[0]);
299       positions_ptr = &positions[0];
300     }
301     for (size_t glyph = 0; glyph < glyph_count; glyph++) {
302       SkPoint* point = &run->glyph_positions[glyph];
303       point->set(x + SkDoubleToScalar(positions_ptr[glyph].x),
304                  y + SkDoubleToScalar(positions_ptr[glyph].y));
305     }
306
307     // TODO(asvitkine): Style boundaries are not necessarily per-run. Handle
308     //                  this better. Also, support strike and diagonal_strike.
309     CFDictionaryRef attributes = CTRunGetAttributes(ct_run);
310     CTFontRef ct_font =
311         base::mac::GetValueFromDictionary<CTFontRef>(attributes,
312                                                      kCTFontAttributeName);
313     base::ScopedCFTypeRef<CFStringRef> font_name_ref(
314         CTFontCopyFamilyName(ct_font));
315     run->font_name = base::SysCFStringRefToUTF8(font_name_ref);
316     run->text_size = CTFontGetSize(ct_font);
317
318     CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(ct_font);
319     if (traits & kCTFontBoldTrait)
320       run->font_style |= Font::BOLD;
321     if (traits & kCTFontItalicTrait)
322       run->font_style |= Font::ITALIC;
323
324     const CGColorRef foreground =
325         base::mac::GetValueFromDictionary<CGColorRef>(
326             attributes, kCTForegroundColorAttributeName);
327     if (foreground)
328       run->foreground = CGColorRefToSkColor(foreground);
329
330     const CFNumberRef underline =
331         base::mac::GetValueFromDictionary<CFNumberRef>(
332             attributes, kCTUnderlineStyleAttributeName);
333     CTUnderlineStyle value = kCTUnderlineStyleNone;
334     if (underline && CFNumberGetValue(underline, kCFNumberSInt32Type, &value))
335       run->underline = (value == kCTUnderlineStyleSingle);
336
337     run_origin.offset(run_width, 0);
338   }
339   runs_valid_ = true;
340 }
341
342 RenderText* RenderText::CreateInstance() {
343   return new RenderTextMac;
344 }
345
346 }  // namespace gfx