Revert of Output closure-compiled JavaScript files (patchset #10 id:180001 of https...
[chromium-blink-merge.git] / ui / gfx / render_text_mac.cc
blob1c046648dc63220fcca566f1ec8c85a106cd3d5e
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()
21 : common_baseline_(0), runs_valid_(false) {
24 RenderTextMac::~RenderTextMac() {
27 scoped_ptr<RenderText> RenderTextMac::CreateInstanceOfSameType() const {
28 return make_scoped_ptr(new RenderTextMac);
31 const base::string16& RenderTextMac::GetDisplayText() {
32 return text_elided() ? display_text() : layout_text();
35 Size RenderTextMac::GetStringSize() {
36 EnsureLayout();
37 return Size(std::ceil(string_size_.width()), string_size_.height());
40 SizeF RenderTextMac::GetStringSizeF() {
41 EnsureLayout();
42 return string_size_;
45 SelectionModel RenderTextMac::FindCursorPosition(const Point& point) {
46 // TODO(asvitkine): Implement this. http://crbug.com/131618
47 return SelectionModel();
50 std::vector<RenderText::FontSpan> RenderTextMac::GetFontSpansForTesting() {
51 EnsureLayout();
52 if (!runs_valid_)
53 ComputeRuns();
55 std::vector<RenderText::FontSpan> spans;
56 for (size_t i = 0; i < runs_.size(); ++i) {
57 Font font(runs_[i].font_name, runs_[i].text_size);
58 const CFRange cf_range = CTRunGetStringRange(runs_[i].ct_run);
59 const Range range(cf_range.location, cf_range.location + cf_range.length);
60 spans.push_back(RenderText::FontSpan(font, range));
63 return spans;
66 int RenderTextMac::GetDisplayTextBaseline() {
67 EnsureLayout();
68 return common_baseline_;
71 SelectionModel RenderTextMac::AdjacentCharSelectionModel(
72 const SelectionModel& selection,
73 VisualCursorDirection direction) {
74 // TODO(asvitkine): Implement this. http://crbug.com/131618
75 return SelectionModel();
78 SelectionModel RenderTextMac::AdjacentWordSelectionModel(
79 const SelectionModel& selection,
80 VisualCursorDirection direction) {
81 // TODO(asvitkine): Implement this. http://crbug.com/131618
82 return SelectionModel();
85 Range RenderTextMac::GetGlyphBounds(size_t index) {
86 // TODO(asvitkine): Implement this. http://crbug.com/131618
87 return Range();
90 std::vector<Rect> RenderTextMac::GetSubstringBounds(const Range& range) {
91 // TODO(asvitkine): Implement this. http://crbug.com/131618
92 return std::vector<Rect>();
95 size_t RenderTextMac::TextIndexToDisplayIndex(size_t index) {
96 // TODO(asvitkine): Implement this. http://crbug.com/131618
97 return index;
100 size_t RenderTextMac::DisplayIndexToTextIndex(size_t index) {
101 // TODO(asvitkine): Implement this. http://crbug.com/131618
102 return index;
105 bool RenderTextMac::IsValidCursorIndex(size_t index) {
106 // TODO(asvitkine): Implement this. http://crbug.com/131618
107 return IsValidLogicalIndex(index);
110 void RenderTextMac::OnLayoutTextAttributeChanged(bool text_changed) {
111 DCHECK(!multiline()) << "RenderTextMac does not support multi line";
112 if (text_changed) {
113 if (elide_behavior() != NO_ELIDE &&
114 elide_behavior() != FADE_TAIL &&
115 !layout_text().empty()) {
116 UpdateDisplayText(GetContentWidth());
117 } else {
118 UpdateDisplayText(0);
121 line_.reset();
122 attributes_.reset();
123 runs_.clear();
124 runs_valid_ = false;
127 void RenderTextMac::OnDisplayTextAttributeChanged() {
128 OnLayoutTextAttributeChanged(true);
131 void RenderTextMac::EnsureLayout() {
132 if (line_.get())
133 return;
134 runs_.clear();
135 runs_valid_ = false;
137 CTFontRef ct_font = base::mac::NSToCFCast(
138 font_list().GetPrimaryFont().GetNativeFont());
140 const void* keys[] = { kCTFontAttributeName };
141 const void* values[] = { ct_font };
142 base::ScopedCFTypeRef<CFDictionaryRef> attributes(
143 CFDictionaryCreate(NULL,
144 keys,
145 values,
146 arraysize(keys),
147 NULL,
148 &kCFTypeDictionaryValueCallBacks));
150 base::ScopedCFTypeRef<CFStringRef> cf_text(
151 base::SysUTF16ToCFStringRef(text()));
152 base::ScopedCFTypeRef<CFAttributedStringRef> attr_text(
153 CFAttributedStringCreate(NULL, cf_text, attributes));
154 base::ScopedCFTypeRef<CFMutableAttributedStringRef> attr_text_mutable(
155 CFAttributedStringCreateMutableCopy(NULL, 0, attr_text));
157 // TODO(asvitkine|msw): Respect GetTextDirection(), which may not match the
158 // natural text direction. See kCTTypesetterOptionForcedEmbeddingLevel, etc.
160 ApplyStyles(attr_text_mutable, ct_font);
161 line_.reset(CTLineCreateWithAttributedString(attr_text_mutable));
163 CGFloat ascent = 0;
164 CGFloat descent = 0;
165 CGFloat leading = 0;
166 // TODO(asvitkine): Consider using CTLineGetBoundsWithOptions() on 10.8+.
167 double width = CTLineGetTypographicBounds(line_, &ascent, &descent, &leading);
168 // Ensure ascent and descent are not smaller than ones of the font list.
169 // Keep them tall enough to draw often-used characters.
170 // For example, if a text field contains a Japanese character, which is
171 // smaller than Latin ones, and then later a Latin one is inserted, this
172 // ensures that the text baseline does not shift.
173 CGFloat font_list_height = font_list().GetHeight();
174 CGFloat font_list_baseline = font_list().GetBaseline();
175 ascent = std::max(ascent, font_list_baseline);
176 descent = std::max(descent, font_list_height - font_list_baseline);
177 string_size_ =
178 SizeF(width, std::max(ascent + descent + leading,
179 static_cast<CGFloat>(min_line_height())));
180 common_baseline_ = ascent;
183 void RenderTextMac::DrawVisualText(Canvas* canvas) {
184 DCHECK(line_);
185 if (!runs_valid_)
186 ComputeRuns();
188 internal::SkiaTextRenderer renderer(canvas);
189 ApplyFadeEffects(&renderer);
190 ApplyTextShadows(&renderer);
192 for (size_t i = 0; i < runs_.size(); ++i) {
193 const TextRun& run = runs_[i];
194 renderer.SetForegroundColor(run.foreground);
195 renderer.SetTextSize(run.text_size);
196 renderer.SetFontFamilyWithStyle(run.font_name, run.font_style);
197 renderer.DrawPosText(&run.glyph_positions[0], &run.glyphs[0],
198 run.glyphs.size());
199 renderer.DrawDecorations(run.origin.x(), run.origin.y(), run.width,
200 run.underline, run.strike, run.diagonal_strike);
203 renderer.EndDiagonalStrike();
206 RenderTextMac::TextRun::TextRun()
207 : ct_run(NULL),
208 origin(SkPoint::Make(0, 0)),
209 width(0),
210 font_style(Font::NORMAL),
211 text_size(0),
212 foreground(SK_ColorBLACK),
213 underline(false),
214 strike(false),
215 diagonal_strike(false) {
218 RenderTextMac::TextRun::~TextRun() {
221 void RenderTextMac::ApplyStyles(CFMutableAttributedStringRef attr_string,
222 CTFontRef font) {
223 // Temporarily apply composition underlines and selection colors.
224 ApplyCompositionAndSelectionStyles();
226 // Note: CFAttributedStringSetAttribute() does not appear to retain the values
227 // passed in, as can be verified via CFGetRetainCount(). To ensure the
228 // attribute objects do not leak, they are saved to |attributes_|.
229 // Clear the attributes storage.
230 attributes_.reset(CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks));
232 // https://developer.apple.com/library/mac/#documentation/Carbon/Reference/CoreText_StringAttributes_Ref/Reference/reference.html
233 internal::StyleIterator style(colors(), styles());
234 const size_t layout_text_length = GetDisplayText().length();
235 for (size_t i = 0, end = 0; i < layout_text_length; i = end) {
236 end = TextIndexToDisplayIndex(style.GetRange().end());
237 const CFRange range = CFRangeMake(i, end - i);
238 base::ScopedCFTypeRef<CGColorRef> foreground(
239 CGColorCreateFromSkColor(style.color()));
240 CFAttributedStringSetAttribute(attr_string, range,
241 kCTForegroundColorAttributeName, foreground);
242 CFArrayAppendValue(attributes_, foreground);
244 if (style.style(UNDERLINE)) {
245 CTUnderlineStyle value = kCTUnderlineStyleSingle;
246 base::ScopedCFTypeRef<CFNumberRef> underline_value(
247 CFNumberCreate(NULL, kCFNumberSInt32Type, &value));
248 CFAttributedStringSetAttribute(attr_string, range,
249 kCTUnderlineStyleAttributeName,
250 underline_value);
251 CFArrayAppendValue(attributes_, underline_value);
254 const int traits = (style.style(BOLD) ? kCTFontBoldTrait : 0) |
255 (style.style(ITALIC) ? kCTFontItalicTrait : 0);
256 if (traits != 0) {
257 base::ScopedCFTypeRef<CTFontRef> styled_font(
258 CTFontCreateCopyWithSymbolicTraits(font, 0.0, NULL, traits, traits));
259 // TODO(asvitkine): Handle |styled_font| == NULL case better.
260 if (styled_font) {
261 CFAttributedStringSetAttribute(attr_string, range, kCTFontAttributeName,
262 styled_font);
263 CFArrayAppendValue(attributes_, styled_font);
267 style.UpdatePosition(DisplayIndexToTextIndex(end));
270 // Undo the temporarily applied composition underlines and selection colors.
271 UndoCompositionAndSelectionStyles();
274 void RenderTextMac::ComputeRuns() {
275 DCHECK(line_);
277 CFArrayRef ct_runs = CTLineGetGlyphRuns(line_);
278 const CFIndex ct_runs_count = CFArrayGetCount(ct_runs);
280 // TODO(asvitkine): Don't use GetLineOffset() until draw time, since it may be
281 // updated based on alignment changes without resetting the layout.
282 Vector2d text_offset = GetLineOffset(0);
283 // Skia will draw glyphs with respect to the baseline.
284 text_offset += Vector2d(0, common_baseline_);
286 const SkScalar x = SkIntToScalar(text_offset.x());
287 const SkScalar y = SkIntToScalar(text_offset.y());
288 SkPoint run_origin = SkPoint::Make(x, y);
290 const CFRange empty_cf_range = CFRangeMake(0, 0);
291 for (CFIndex i = 0; i < ct_runs_count; ++i) {
292 CTRunRef ct_run =
293 base::mac::CFCast<CTRunRef>(CFArrayGetValueAtIndex(ct_runs, i));
294 const size_t glyph_count = CTRunGetGlyphCount(ct_run);
295 const double run_width =
296 CTRunGetTypographicBounds(ct_run, empty_cf_range, NULL, NULL, NULL);
297 if (glyph_count == 0) {
298 run_origin.offset(run_width, 0);
299 continue;
302 runs_.push_back(TextRun());
303 TextRun* run = &runs_.back();
304 run->ct_run = ct_run;
305 run->origin = run_origin;
306 run->width = run_width;
307 run->glyphs.resize(glyph_count);
308 CTRunGetGlyphs(ct_run, empty_cf_range, &run->glyphs[0]);
309 // CTRunGetGlyphs() sometimes returns glyphs with value 65535 and zero
310 // width (this has been observed at the beginning of a string containing
311 // Arabic content). Passing these to Skia will trigger an assertion;
312 // instead set their values to 0.
313 for (size_t glyph = 0; glyph < glyph_count; glyph++) {
314 if (run->glyphs[glyph] == 65535)
315 run->glyphs[glyph] = 0;
318 run->glyph_positions.resize(glyph_count);
319 const CGPoint* positions_ptr = CTRunGetPositionsPtr(ct_run);
320 std::vector<CGPoint> positions;
321 if (positions_ptr == NULL) {
322 positions.resize(glyph_count);
323 CTRunGetPositions(ct_run, empty_cf_range, &positions[0]);
324 positions_ptr = &positions[0];
326 for (size_t glyph = 0; glyph < glyph_count; glyph++) {
327 SkPoint* point = &run->glyph_positions[glyph];
328 point->set(x + SkDoubleToScalar(positions_ptr[glyph].x),
329 y + SkDoubleToScalar(positions_ptr[glyph].y));
332 // TODO(asvitkine): Style boundaries are not necessarily per-run. Handle
333 // this better. Also, support strike and diagonal_strike.
334 CFDictionaryRef attributes = CTRunGetAttributes(ct_run);
335 CTFontRef ct_font =
336 base::mac::GetValueFromDictionary<CTFontRef>(attributes,
337 kCTFontAttributeName);
338 base::ScopedCFTypeRef<CFStringRef> font_name_ref(
339 CTFontCopyFamilyName(ct_font));
340 run->font_name = base::SysCFStringRefToUTF8(font_name_ref);
341 run->text_size = CTFontGetSize(ct_font);
343 CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(ct_font);
344 if (traits & kCTFontBoldTrait)
345 run->font_style |= Font::BOLD;
346 if (traits & kCTFontItalicTrait)
347 run->font_style |= Font::ITALIC;
349 const CGColorRef foreground =
350 base::mac::GetValueFromDictionary<CGColorRef>(
351 attributes, kCTForegroundColorAttributeName);
352 if (foreground)
353 run->foreground = CGColorRefToSkColor(foreground);
355 const CFNumberRef underline =
356 base::mac::GetValueFromDictionary<CFNumberRef>(
357 attributes, kCTUnderlineStyleAttributeName);
358 CTUnderlineStyle value = kCTUnderlineStyleNone;
359 if (underline && CFNumberGetValue(underline, kCFNumberSInt32Type, &value))
360 run->underline = (value == kCTUnderlineStyleSingle);
362 run_origin.offset(run_width, 0);
364 runs_valid_ = true;
367 } // namespace gfx