OLD | NEW |
---|---|
(Empty) | |
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 <cmath> | |
10 #include <utility> | |
11 | |
12 #include "base/mac/foundation_util.h" | |
13 #include "base/mac/scoped_cftyperef.h" | |
14 #include "base/sys_string_conversions.h" | |
15 #include "skia/ext/skia_utils_mac.h" | |
16 | |
17 namespace { | |
18 | |
19 // Returns the pixel height of |ct_font|. | |
Nico
2012/06/18 15:48:01
View units height, not pixel height, I think.
| |
20 CGFloat GetCTFontPixelSize(CTFontRef ct_font) { | |
21 return CTFontGetAscent(ct_font) + CTFontGetDescent(ct_font); | |
22 } | |
23 | |
24 // Creates a CTFont with the given font name and pixel size. Ownership is | |
Nico
2012/06/18 15:48:01
Again, not pixel size I think. (You don't want to
| |
25 // transferred to the caller. | |
26 CTFontRef CreateCTFontWithPixelSize(const std::string& font_name, | |
27 const int target_pixel_size) { | |
28 // Epsilon value used for comparing font sizes. | |
29 const CGFloat kEpsilon = 0.001; | |
30 // The observed pixel to points ratio for Lucida Grande on 10.6. Other fonts | |
31 // have other ratios and the documentation doesn't provide a guarantee that | |
32 // the relation is linear. So this ratio is used as a first try before | |
33 // falling back to the bisection method. | |
34 const CGFloat kPixelsToPointsRatio = 0.849088; | |
35 | |
36 base::mac::ScopedCFTypeRef<CFStringRef> font_name_cf_string( | |
37 base::SysUTF8ToCFStringRef(font_name)); | |
38 | |
39 // First, try using |kPixelsToPointsRatio|. | |
40 CGFloat point_size = target_pixel_size * kPixelsToPointsRatio; | |
41 base::mac::ScopedCFTypeRef<CTFontRef> ct_font( | |
42 CTFontCreateWithName(font_name_cf_string, point_size, NULL)); | |
43 CGFloat actual_pixel_size = GetCTFontPixelSize(ct_font); | |
44 if (std::fabs(actual_pixel_size - target_pixel_size) < kEpsilon) | |
45 return ct_font.release(); | |
46 | |
47 // |kPixelsToPointsRatio| wasn't correct. Use the bisection method to find the | |
48 // right size. | |
49 | |
50 // First, find the initial bisection range, so that the point size that | |
51 // corresponds to |target_pixel_size| is between |lo| and |hi|. | |
52 CGFloat lo = 0; | |
53 CGFloat hi = point_size; | |
54 while (actual_pixel_size < target_pixel_size) { | |
55 lo = hi; | |
56 hi *= 2; | |
57 ct_font.reset(CTFontCreateWithName(font_name_cf_string, hi, NULL)); | |
58 actual_pixel_size = GetCTFontPixelSize(ct_font); | |
59 } | |
60 | |
61 // Now, bisect to find the right size. | |
62 while (lo < hi) { | |
63 point_size = (hi - lo) * 0.5 + lo; | |
64 ct_font.reset(CTFontCreateWithName(font_name_cf_string, point_size, NULL)); | |
65 actual_pixel_size = GetCTFontPixelSize(ct_font); | |
66 if (std::fabs(actual_pixel_size - target_pixel_size) < kEpsilon) | |
67 break; | |
68 if (target_pixel_size > actual_pixel_size) | |
69 lo = point_size; | |
70 else | |
71 hi = point_size; | |
72 } | |
73 | |
74 return ct_font.release(); | |
75 } | |
76 | |
77 } // namespace | |
78 | |
79 namespace gfx { | |
80 | |
81 RenderTextMac::RenderTextMac() : common_baseline_(0), runs_valid_(false) { | |
82 } | |
83 | |
84 RenderTextMac::~RenderTextMac() { | |
85 } | |
86 | |
87 base::i18n::TextDirection RenderTextMac::GetTextDirection() { | |
88 return base::i18n::LEFT_TO_RIGHT; | |
89 } | |
90 | |
91 Size RenderTextMac::GetStringSize() { | |
92 EnsureLayout(); | |
93 return string_size_; | |
94 } | |
95 | |
96 SelectionModel RenderTextMac::FindCursorPosition(const Point& point) { | |
97 // TODO(asvitkine): Implement this. http://crbug.com/131618 | |
98 return SelectionModel(); | |
99 } | |
100 | |
101 std::vector<RenderText::FontSpan> RenderTextMac::GetFontSpansForTesting() { | |
102 EnsureLayout(); | |
103 if (!runs_valid_) | |
104 ComputeRuns(); | |
105 | |
106 std::vector<RenderText::FontSpan> spans; | |
107 for (size_t i = 0; i < runs_.size(); ++i) { | |
108 gfx::Font font(runs_[i].font_name, runs_[i].text_size); | |
109 const CFRange cf_range = CTRunGetStringRange(runs_[i].ct_run); | |
110 const ui::Range range(cf_range.location, | |
111 cf_range.location + cf_range.length); | |
112 spans.push_back(RenderText::FontSpan(font, range)); | |
113 } | |
114 | |
115 return spans; | |
116 } | |
117 | |
118 SelectionModel RenderTextMac::AdjacentCharSelectionModel( | |
119 const SelectionModel& selection, | |
120 VisualCursorDirection direction) { | |
121 // TODO(asvitkine): Implement this. http://crbug.com/131618 | |
122 return SelectionModel(); | |
123 } | |
124 | |
125 SelectionModel RenderTextMac::AdjacentWordSelectionModel( | |
126 const SelectionModel& selection, | |
127 VisualCursorDirection direction) { | |
128 // TODO(asvitkine): Implement this. http://crbug.com/131618 | |
129 return SelectionModel(); | |
130 } | |
131 | |
132 void RenderTextMac::GetGlyphBounds(size_t index, | |
133 ui::Range* xspan, | |
134 int* height) { | |
135 // TODO(asvitkine): Implement this. http://crbug.com/131618 | |
136 } | |
137 | |
138 std::vector<Rect> RenderTextMac::GetSubstringBounds(ui::Range range) { | |
139 // TODO(asvitkine): Implement this. http://crbug.com/131618 | |
140 return std::vector<Rect>(); | |
141 } | |
142 | |
143 bool RenderTextMac::IsCursorablePosition(size_t position) { | |
144 // TODO(asvitkine): Implement this. http://crbug.com/131618 | |
145 return false; | |
146 } | |
147 | |
148 void RenderTextMac::ResetLayout() { | |
149 line_.reset(); | |
150 runs_.clear(); | |
151 runs_valid_ = false; | |
152 } | |
153 | |
154 void RenderTextMac::EnsureLayout() { | |
155 if (line_.get()) | |
156 return; | |
157 runs_.clear(); | |
158 runs_valid_ = false; | |
159 | |
160 const Font& font = GetFont(); | |
161 CTFontRef ct_font = | |
162 CreateCTFontWithPixelSize(font.GetFontName(), font.GetFontSize()); | |
163 | |
164 const void* keys[] = { kCTFontAttributeName }; | |
165 const void* values[] = { ct_font }; | |
166 base::mac::ScopedCFTypeRef<CFDictionaryRef> attributes( | |
167 CFDictionaryCreate(NULL, keys, values, arraysize(keys), NULL, NULL)); | |
168 | |
169 base::mac::ScopedCFTypeRef<CFStringRef> cf_text( | |
170 base::SysUTF16ToCFStringRef(text())); | |
171 base::mac::ScopedCFTypeRef<CFAttributedStringRef> attr_text( | |
172 CFAttributedStringCreate(NULL, cf_text, attributes)); | |
173 base::mac::ScopedCFTypeRef<CFMutableAttributedStringRef> attr_text_mutable( | |
174 CFAttributedStringCreateMutableCopy(NULL, 0, attr_text)); | |
175 | |
176 ApplyStyles(attr_text_mutable, ct_font); | |
177 line_.reset(CTLineCreateWithAttributedString(attr_text_mutable)); | |
178 | |
179 CGFloat ascent = 0; | |
180 CGFloat descent = 0; | |
181 CGFloat leading = 0; | |
182 double width = CTLineGetTypographicBounds(line_, &ascent, &descent, &leading); | |
183 string_size_ = Size(width, ascent + descent + leading); | |
184 common_baseline_ = ascent; | |
185 } | |
186 | |
187 void RenderTextMac::DrawVisualText(Canvas* canvas) { | |
188 DCHECK(line_); | |
189 if (!runs_valid_) | |
190 ComputeRuns(); | |
191 | |
192 internal::SkiaTextRenderer renderer(canvas); | |
193 ApplyFadeEffects(&renderer); | |
194 ApplyTextShadows(&renderer); | |
195 | |
196 for (size_t i = 0; i < runs_.size(); ++i) { | |
197 const TextRun& run = runs_[i]; | |
198 renderer.SetForegroundColor(run.foreground); | |
199 renderer.SetTextSize(run.text_size); | |
200 renderer.SetFontFamilyWithStyle(run.font_name, run.font_style); | |
201 renderer.DrawPosText(&run.glyph_positions[0], &run.glyphs[0], | |
202 run.glyphs.size()); | |
203 renderer.DrawDecorations(run.origin.x(), run.origin.y(), run.width, | |
204 run.style); | |
205 } | |
206 } | |
207 | |
208 RenderTextMac::TextRun::TextRun() | |
209 : ct_run(NULL), | |
210 origin(SkPoint::Make(0, 0)), | |
211 width(0), | |
212 font_style(Font::NORMAL), | |
213 text_size(0), | |
214 foreground(SK_ColorBLACK) { | |
215 } | |
216 | |
217 RenderTextMac::TextRun::~TextRun() { | |
218 } | |
219 | |
220 void RenderTextMac::ApplyStyles(CFMutableAttributedStringRef attr_string, | |
221 CTFontRef font) { | |
222 // https://developer.apple.com/library/mac/#documentation/Carbon/Reference/Cor eText_StringAttributes_Ref/Reference/reference.html | |
223 for (size_t i = 0; i < style_ranges().size(); ++i) { | |
224 const StyleRange& style = style_ranges()[i]; | |
225 const CFRange range = CFRangeMake(style.range.start(), | |
226 style.range.length()); | |
227 | |
228 CGColorRef foreground = gfx::SkColorToCGColorRef(style.foreground); | |
229 CFAttributedStringSetAttribute(attr_string, range, | |
230 kCTForegroundColorAttributeName, | |
231 foreground); | |
232 | |
233 if (style.font_style & Font::UNDERLINED) { | |
234 CTUnderlineStyle value = kCTUnderlineStyleSingle; | |
235 CFNumberRef underline = CFNumberCreate(NULL, kCFNumberSInt32Type, &value); | |
236 CFAttributedStringSetAttribute(attr_string, range, | |
237 kCTUnderlineStyleAttributeName, | |
238 underline); | |
239 } | |
240 | |
241 if (style.font_style & (Font::BOLD | Font::ITALIC)) { | |
242 int traits = 0; | |
243 if (style.font_style & Font::BOLD) | |
244 traits |= kCTFontBoldTrait; | |
245 if (style.font_style & Font::ITALIC) | |
246 traits |= kCTFontItalicTrait; | |
247 CTFontRef styled_font = | |
248 CTFontCreateCopyWithSymbolicTraits(font, 0.0, NULL, traits, traits); | |
249 CFAttributedStringSetAttribute(attr_string, range, kCTFontAttributeName, | |
250 styled_font); | |
251 } | |
252 } | |
253 } | |
254 | |
255 void RenderTextMac::ComputeRuns() { | |
256 DCHECK(line_); | |
257 | |
258 CFArrayRef ct_runs = CTLineGetGlyphRuns(line_); | |
259 const CFIndex ct_runs_count = CFArrayGetCount(ct_runs); | |
260 | |
261 Point offset(GetTextOrigin()); | |
262 // Skia will draw glyphs with respect to the baseline. | |
263 offset.Offset(0, common_baseline_); | |
264 | |
265 const SkScalar x = SkIntToScalar(offset.x()); | |
266 const SkScalar y = SkIntToScalar(offset.y()); | |
267 SkPoint run_origin = SkPoint::Make(offset.x(), offset.y()); | |
268 | |
269 const CFRange empty_cf_range = CFRangeMake(0, 0); | |
270 for (CFIndex i = 0; i < ct_runs_count; ++i) { | |
271 CTRunRef ct_run = | |
272 base::mac::CFCast<CTRunRef>(CFArrayGetValueAtIndex(ct_runs, i)); | |
273 const size_t glyph_count = CTRunGetGlyphCount(ct_run); | |
274 const double run_width = | |
275 CTRunGetTypographicBounds(ct_run, empty_cf_range, NULL, NULL, NULL); | |
276 if (glyph_count == 0) { | |
277 run_origin.offset(run_width, 0); | |
278 continue; | |
279 } | |
280 | |
281 runs_.push_back(TextRun()); | |
282 TextRun* run = &runs_.back(); | |
283 run->ct_run = ct_run; | |
284 run->origin = run_origin; | |
285 run->width = run_width; | |
286 run->glyphs.resize(glyph_count); | |
287 CTRunGetGlyphs(ct_run, empty_cf_range, &run->glyphs[0]); | |
288 | |
289 run->glyph_positions.resize(glyph_count); | |
290 const CGPoint* positions_ptr = CTRunGetPositionsPtr(ct_run); | |
291 std::vector<CGPoint> positions; | |
292 if (positions_ptr == NULL) { | |
293 positions.resize(glyph_count); | |
294 CTRunGetPositions(ct_run, empty_cf_range, &positions[0]); | |
295 positions_ptr = &positions[0]; | |
296 } | |
297 for (size_t glyph = 0; glyph < glyph_count; glyph++) { | |
298 SkPoint* point = &run->glyph_positions[glyph]; | |
299 point->set(x + SkDoubleToScalar(positions_ptr[glyph].x), | |
300 y + SkDoubleToScalar(positions_ptr[glyph].y)); | |
301 } | |
302 | |
303 CFDictionaryRef attributes = CTRunGetAttributes(ct_run); | |
304 CTFontRef ct_font = | |
305 base::mac::GetValueFromDictionary<CTFontRef>(attributes, | |
306 kCTFontAttributeName); | |
307 base::mac::ScopedCFTypeRef<CFStringRef> font_name_ref( | |
308 CTFontCopyFamilyName(ct_font)); | |
309 run->font_name = base::SysCFStringRefToUTF8(font_name_ref); | |
310 run->text_size = GetCTFontPixelSize(ct_font); | |
311 | |
312 CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(ct_font); | |
313 if (traits & kCTFontBoldTrait) | |
314 run->font_style |= Font::BOLD; | |
315 if (traits & kCTFontItalicTrait) | |
316 run->font_style |= Font::ITALIC; | |
317 | |
318 const CGColorRef foreground = | |
319 base::mac::GetValueFromDictionary<CGColorRef>( | |
320 attributes, kCTForegroundColorAttributeName); | |
321 run->foreground = gfx::CGColorRefToSkColor(foreground); | |
322 | |
323 const CFNumberRef underline = | |
324 base::mac::GetValueFromDictionary<CFNumberRef>( | |
325 attributes, kCTUnderlineStyleAttributeName); | |
326 CTUnderlineStyle value = kCTUnderlineStyleNone; | |
327 if (underline && CFNumberGetValue(underline, kCFNumberSInt32Type, &value)) | |
328 run->style.underline = (value == kCTUnderlineStyleSingle); | |
329 | |
330 run_origin.offset(run_width, 0); | |
331 } | |
332 runs_valid_ = true; | |
333 } | |
334 | |
335 RenderText* RenderText::CreateRenderText() { | |
336 return new RenderTextMac; | |
337 } | |
338 | |
339 } // namespace gfx | |
OLD | NEW |