| 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 <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/Cor
eText_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::CreateNativeInstance() { | |
| 343 return new RenderTextMac; | |
| 344 } | |
| 345 | |
| 346 } // namespace gfx | |
| OLD | NEW |