15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/render_text_mac.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <ApplicationServices/ApplicationServices.h>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)#include <algorithm>
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <cmath>
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <utility>
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/mac/foundation_util.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/mac/scoped_cftyperef.h"
15c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/strings/sys_string_conversions.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "skia/ext/skia_utils_mac.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace gfx {
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)RenderTextMac::RenderTextMac() : common_baseline_(0), runs_valid_(false) {
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)RenderTextMac::~RenderTextMac() {
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Size RenderTextMac::GetStringSize() {
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EnsureLayout();
284e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  return Size(std::ceil(string_size_.width()), string_size_.height());
294e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
304e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
314e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)SizeF RenderTextMac::GetStringSizeF() {
324e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  EnsureLayout();
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return string_size_;
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SelectionModel RenderTextMac::FindCursorPosition(const Point& point) {
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(asvitkine): Implement this. http://crbug.com/131618
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return SelectionModel();
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)std::vector<RenderText::FontSpan> RenderTextMac::GetFontSpansForTesting() {
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EnsureLayout();
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!runs_valid_)
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ComputeRuns();
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<RenderText::FontSpan> spans;
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < runs_.size(); ++i) {
48010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    Font font(runs_[i].font_name, runs_[i].text_size);
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const CFRange cf_range = CTRunGetStringRange(runs_[i].ct_run);
50d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    const Range range(cf_range.location, cf_range.location + cf_range.length);
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    spans.push_back(RenderText::FontSpan(font, range));
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return spans;
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
570f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)int RenderTextMac::GetLayoutTextBaseline() {
580f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  EnsureLayout();
590f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  return common_baseline_;
600f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)}
610f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SelectionModel RenderTextMac::AdjacentCharSelectionModel(
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const SelectionModel& selection,
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    VisualCursorDirection direction) {
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(asvitkine): Implement this. http://crbug.com/131618
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return SelectionModel();
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SelectionModel RenderTextMac::AdjacentWordSelectionModel(
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const SelectionModel& selection,
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    VisualCursorDirection direction) {
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(asvitkine): Implement this. http://crbug.com/131618
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return SelectionModel();
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
76d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)Range RenderTextMac::GetGlyphBounds(size_t index) {
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(asvitkine): Implement this. http://crbug.com/131618
78d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  return Range();
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
81d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)std::vector<Rect> RenderTextMac::GetSubstringBounds(const Range& range) {
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(asvitkine): Implement this. http://crbug.com/131618
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return std::vector<Rect>();
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)size_t RenderTextMac::TextIndexToLayoutIndex(size_t index) const {
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(asvitkine): Implement this. http://crbug.com/131618
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return index;
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)size_t RenderTextMac::LayoutIndexToTextIndex(size_t index) const {
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(asvitkine): Implement this. http://crbug.com/131618
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return index;
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
96010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)bool RenderTextMac::IsValidCursorIndex(size_t index) {
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(asvitkine): Implement this. http://crbug.com/131618
98010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  return IsValidLogicalIndex(index);
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void RenderTextMac::ResetLayout() {
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  line_.reset();
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  attributes_.reset();
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  runs_.clear();
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  runs_valid_ = false;
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void RenderTextMac::EnsureLayout() {
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (line_.get())
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  runs_.clear();
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  runs_valid_ = false;
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  CTFontRef ct_font = base::mac::NSToCFCast(
1155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      font_list().GetPrimaryFont().GetNativeFont());
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const void* keys[] = { kCTFontAttributeName };
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const void* values[] = { ct_font };
119eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  base::ScopedCFTypeRef<CFDictionaryRef> attributes(
120eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      CFDictionaryCreate(NULL,
121eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                         keys,
122eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                         values,
123eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                         arraysize(keys),
124eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                         NULL,
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         &kCFTypeDictionaryValueCallBacks));
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
127eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  base::ScopedCFTypeRef<CFStringRef> cf_text(
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::SysUTF16ToCFStringRef(text()));
129eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  base::ScopedCFTypeRef<CFAttributedStringRef> attr_text(
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      CFAttributedStringCreate(NULL, cf_text, attributes));
131eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  base::ScopedCFTypeRef<CFMutableAttributedStringRef> attr_text_mutable(
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      CFAttributedStringCreateMutableCopy(NULL, 0, attr_text));
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(asvitkine|msw): Respect GetTextDirection(), which may not match the
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // natural text direction. See kCTTypesetterOptionForcedEmbeddingLevel, etc.
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ApplyStyles(attr_text_mutable, ct_font);
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  line_.reset(CTLineCreateWithAttributedString(attr_text_mutable));
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CGFloat ascent = 0;
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CGFloat descent = 0;
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CGFloat leading = 0;
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(asvitkine): Consider using CTLineGetBoundsWithOptions() on 10.8+.
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  double width = CTLineGetTypographicBounds(line_, &ascent, &descent, &leading);
145a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  // Ensure ascent and descent are not smaller than ones of the font list.
146a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  // Keep them tall enough to draw often-used characters.
147a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  // For example, if a text field contains a Japanese character, which is
148a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  // smaller than Latin ones, and then later a Latin one is inserted, this
149a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  // ensures that the text baseline does not shift.
150a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  CGFloat font_list_height = font_list().GetHeight();
151a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  CGFloat font_list_baseline = font_list().GetBaseline();
152a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  ascent = std::max(ascent, font_list_baseline);
153a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  descent = std::max(descent, font_list_height - font_list_baseline);
1544e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  string_size_ = SizeF(width, ascent + descent + leading);
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  common_baseline_ = ascent;
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void RenderTextMac::DrawVisualText(Canvas* canvas) {
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(line_);
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!runs_valid_)
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ComputeRuns();
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  internal::SkiaTextRenderer renderer(canvas);
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ApplyFadeEffects(&renderer);
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ApplyTextShadows(&renderer);
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < runs_.size(); ++i) {
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const TextRun& run = runs_[i];
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    renderer.SetForegroundColor(run.foreground);
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    renderer.SetTextSize(run.text_size);
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    renderer.SetFontFamilyWithStyle(run.font_name, run.font_style);
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    renderer.DrawPosText(&run.glyph_positions[0], &run.glyphs[0],
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         run.glyphs.size());
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    renderer.DrawDecorations(run.origin.x(), run.origin.y(), run.width,
1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                             run.underline, run.strike, run.diagonal_strike);
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
177effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
178effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  renderer.EndDiagonalStrike();
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)RenderTextMac::TextRun::TextRun()
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : ct_run(NULL),
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      origin(SkPoint::Make(0, 0)),
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      width(0),
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      font_style(Font::NORMAL),
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      text_size(0),
1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      foreground(SK_ColorBLACK),
1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      underline(false),
1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      strike(false),
1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      diagonal_strike(false) {
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)RenderTextMac::TextRun::~TextRun() {
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void RenderTextMac::ApplyStyles(CFMutableAttributedStringRef attr_string,
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                CTFontRef font) {
1982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Temporarily apply composition underlines and selection colors.
1992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ApplyCompositionAndSelectionStyles();
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Note: CFAttributedStringSetAttribute() does not appear to retain the values
2022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // passed in, as can be verified via CFGetRetainCount(). To ensure the
2032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // attribute objects do not leak, they are saved to |attributes_|.
2042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Clear the attributes storage.
2052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  attributes_.reset(CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks));
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // https://developer.apple.com/library/mac/#documentation/Carbon/Reference/CoreText_StringAttributes_Ref/Reference/reference.html
2082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  internal::StyleIterator style(colors(), styles());
2092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const size_t layout_text_length = GetLayoutText().length();
2102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (size_t i = 0, end = 0; i < layout_text_length; i = end) {
2112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    end = TextIndexToLayoutIndex(style.GetRange().end());
2122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const CFRange range = CFRangeMake(i, end - i);
213eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    base::ScopedCFTypeRef<CGColorRef> foreground(
214010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        CGColorCreateFromSkColor(style.color()));
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    CFAttributedStringSetAttribute(attr_string, range,
2162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        kCTForegroundColorAttributeName, foreground);
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    CFArrayAppendValue(attributes_, foreground);
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (style.style(UNDERLINE)) {
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      CTUnderlineStyle value = kCTUnderlineStyleSingle;
221eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      base::ScopedCFTypeRef<CFNumberRef> underline_value(
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          CFNumberCreate(NULL, kCFNumberSInt32Type, &value));
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      CFAttributedStringSetAttribute(attr_string, range,
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     kCTUnderlineStyleAttributeName,
2252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                     underline_value);
2262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      CFArrayAppendValue(attributes_, underline_value);
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const int traits = (style.style(BOLD) ? kCTFontBoldTrait : 0) |
2302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                       (style.style(ITALIC) ? kCTFontItalicTrait : 0);
2312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (traits != 0) {
232eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      base::ScopedCFTypeRef<CTFontRef> styled_font(
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          CTFontCreateCopyWithSymbolicTraits(font, 0.0, NULL, traits, traits));
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // TODO(asvitkine): Handle |styled_font| == NULL case better.
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (styled_font) {
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        CFAttributedStringSetAttribute(attr_string, range, kCTFontAttributeName,
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                       styled_font);
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        CFArrayAppendValue(attributes_, styled_font);
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    style.UpdatePosition(LayoutIndexToTextIndex(end));
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Undo the temporarily applied composition underlines and selection colors.
2462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  UndoCompositionAndSelectionStyles();
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void RenderTextMac::ComputeRuns() {
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(line_);
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CFArrayRef ct_runs = CTLineGetGlyphRuns(line_);
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const CFIndex ct_runs_count = CFArrayGetCount(ct_runs);
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
255d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // TODO(asvitkine): Don't use GetLineOffset() until draw time, since it may be
25690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  // updated based on alignment changes without resetting the layout.
257010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  Vector2d text_offset = GetLineOffset(0);
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Skia will draw glyphs with respect to the baseline.
259010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  text_offset += Vector2d(0, common_baseline_);
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const SkScalar x = SkIntToScalar(text_offset.x());
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const SkScalar y = SkIntToScalar(text_offset.y());
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SkPoint run_origin = SkPoint::Make(x, y);
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const CFRange empty_cf_range = CFRangeMake(0, 0);
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (CFIndex i = 0; i < ct_runs_count; ++i) {
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    CTRunRef ct_run =
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::mac::CFCast<CTRunRef>(CFArrayGetValueAtIndex(ct_runs, i));
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const size_t glyph_count = CTRunGetGlyphCount(ct_run);
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const double run_width =
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        CTRunGetTypographicBounds(ct_run, empty_cf_range, NULL, NULL, NULL);
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (glyph_count == 0) {
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      run_origin.offset(run_width, 0);
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      continue;
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    runs_.push_back(TextRun());
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    TextRun* run = &runs_.back();
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    run->ct_run = ct_run;
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    run->origin = run_origin;
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    run->width = run_width;
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    run->glyphs.resize(glyph_count);
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    CTRunGetGlyphs(ct_run, empty_cf_range, &run->glyphs[0]);
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // CTRunGetGlyphs() sometimes returns glyphs with value 65535 and zero
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // width (this has been observed at the beginning of a string containing
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Arabic content). Passing these to Skia will trigger an assertion;
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // instead set their values to 0.
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (size_t glyph = 0; glyph < glyph_count; glyph++) {
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (run->glyphs[glyph] == 65535)
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        run->glyphs[glyph] = 0;
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    run->glyph_positions.resize(glyph_count);
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const CGPoint* positions_ptr = CTRunGetPositionsPtr(ct_run);
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::vector<CGPoint> positions;
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (positions_ptr == NULL) {
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      positions.resize(glyph_count);
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      CTRunGetPositions(ct_run, empty_cf_range, &positions[0]);
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      positions_ptr = &positions[0];
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (size_t glyph = 0; glyph < glyph_count; glyph++) {
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SkPoint* point = &run->glyph_positions[glyph];
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      point->set(x + SkDoubleToScalar(positions_ptr[glyph].x),
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 y + SkDoubleToScalar(positions_ptr[glyph].y));
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // TODO(asvitkine): Style boundaries are not necessarily per-run. Handle
3082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    //                  this better. Also, support strike and diagonal_strike.
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    CFDictionaryRef attributes = CTRunGetAttributes(ct_run);
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    CTFontRef ct_font =
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::mac::GetValueFromDictionary<CTFontRef>(attributes,
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                     kCTFontAttributeName);
313eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    base::ScopedCFTypeRef<CFStringRef> font_name_ref(
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        CTFontCopyFamilyName(ct_font));
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    run->font_name = base::SysCFStringRefToUTF8(font_name_ref);
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    run->text_size = CTFontGetSize(ct_font);
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(ct_font);
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (traits & kCTFontBoldTrait)
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      run->font_style |= Font::BOLD;
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (traits & kCTFontItalicTrait)
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      run->font_style |= Font::ITALIC;
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const CGColorRef foreground =
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::mac::GetValueFromDictionary<CGColorRef>(
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            attributes, kCTForegroundColorAttributeName);
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (foreground)
328010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      run->foreground = CGColorRefToSkColor(foreground);
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const CFNumberRef underline =
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::mac::GetValueFromDictionary<CFNumberRef>(
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            attributes, kCTUnderlineStyleAttributeName);
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    CTUnderlineStyle value = kCTUnderlineStyleNone;
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (underline && CFNumberGetValue(underline, kCFNumberSInt32Type, &value))
3352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      run->underline = (value == kCTUnderlineStyleSingle);
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    run_origin.offset(run_width, 0);
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  runs_valid_ = true;
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
342cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)RenderText* RenderText::CreateNativeInstance() {
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return new RenderTextMac;
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace gfx
347