| // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "ui/gfx/platform_font_mac.h" | 
 |  | 
 | #include <cmath> | 
 |  | 
 | #include <Cocoa/Cocoa.h> | 
 |  | 
 | #include "base/mac/scoped_nsobject.h" | 
 | #include "base/strings/sys_string_conversions.h" | 
 | #include "base/strings/utf_string_conversions.h" | 
 | #include "ui/gfx/canvas.h" | 
 | #include "ui/gfx/font.h" | 
 | #include "ui/gfx/font_render_params.h" | 
 |  | 
 | namespace gfx { | 
 |  | 
 | namespace { | 
 |  | 
 | // Returns the font style for |font|. Disregards Font::UNDERLINE, since NSFont | 
 | // does not support it as a trait. | 
 | int GetFontStyleFromNSFont(NSFont* font) { | 
 |   int font_style = Font::NORMAL; | 
 |   NSFontSymbolicTraits traits = [[font fontDescriptor] symbolicTraits]; | 
 |   if (traits & NSFontItalicTrait) | 
 |     font_style |= Font::ITALIC; | 
 |   return font_style; | 
 | } | 
 |  | 
 | // Returns the Font weight for |font|. | 
 | Font::Weight GetFontWeightFromNSFont(NSFont* font) { | 
 |   NSFontSymbolicTraits traits = [[font fontDescriptor] symbolicTraits]; | 
 |   return (traits & NSFontBoldTrait) ? Font::Weight::BOLD : Font::Weight::NORMAL; | 
 | } | 
 |  | 
 | // Returns an autoreleased NSFont created with the passed-in specifications. | 
 | NSFont* NSFontWithSpec(const std::string& font_name, | 
 |                        int font_size, | 
 |                        int font_style, | 
 |                        Font::Weight font_weight) { | 
 |   NSFontSymbolicTraits trait_bits = 0; | 
 |   // TODO(mboc): Add support for other weights as well. | 
 |   if (font_weight >= Font::Weight::BOLD) | 
 |     trait_bits |= NSFontBoldTrait; | 
 |   if (font_style & Font::ITALIC) | 
 |     trait_bits |= NSFontItalicTrait; | 
 |   // The Mac doesn't support underline as a font trait, so just drop it. | 
 |   // (Underlines must be added as an attribute on an NSAttributedString.) | 
 |   NSDictionary* traits = @{ NSFontSymbolicTrait : @(trait_bits) }; | 
 |  | 
 |   NSDictionary* attrs = @{ | 
 |     NSFontFamilyAttribute : base::SysUTF8ToNSString(font_name), | 
 |     NSFontTraitsAttribute : traits | 
 |   }; | 
 |   NSFontDescriptor* descriptor = | 
 |       [NSFontDescriptor fontDescriptorWithFontAttributes:attrs]; | 
 |   NSFont* font = [NSFont fontWithDescriptor:descriptor size:font_size]; | 
 |   if (font) | 
 |     return font; | 
 |  | 
 |   // Make one fallback attempt by looking up via font name rather than font | 
 |   // family name. | 
 |   attrs = @{ | 
 |     NSFontNameAttribute : base::SysUTF8ToNSString(font_name), | 
 |     NSFontTraitsAttribute : traits | 
 |   }; | 
 |   descriptor = [NSFontDescriptor fontDescriptorWithFontAttributes:attrs]; | 
 |   return [NSFont fontWithDescriptor:descriptor size:font_size]; | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | //////////////////////////////////////////////////////////////////////////////// | 
 | // PlatformFontMac, public: | 
 |  | 
 | PlatformFontMac::PlatformFontMac() | 
 |     : PlatformFontMac([NSFont systemFontOfSize:[NSFont systemFontSize]]) { | 
 | } | 
 |  | 
 | PlatformFontMac::PlatformFontMac(NativeFont native_font) | 
 |     : PlatformFontMac(native_font, | 
 |                       base::SysNSStringToUTF8([native_font familyName]), | 
 |                       [native_font pointSize], | 
 |                       GetFontStyleFromNSFont(native_font), | 
 |                       GetFontWeightFromNSFont(native_font)) {} | 
 |  | 
 | PlatformFontMac::PlatformFontMac(const std::string& font_name, int font_size) | 
 |     : PlatformFontMac(font_name, | 
 |                       font_size, | 
 |                       Font::NORMAL, | 
 |                       Font::Weight::NORMAL) {} | 
 |  | 
 | //////////////////////////////////////////////////////////////////////////////// | 
 | // PlatformFontMac, PlatformFont implementation: | 
 |  | 
 | Font PlatformFontMac::DeriveFont(int size_delta, | 
 |                                  int style, | 
 |                                  Font::Weight weight) const { | 
 |   // For some reason, creating fonts using the NSFontDescriptor API's seem to be | 
 |   // unreliable. Hence use the NSFontManager. | 
 |   NSFont* derived_font = native_font_; | 
 |   NSFontManager* font_manager = [NSFontManager sharedFontManager]; | 
 |  | 
 |   NSFontTraitMask bold_trait_mask = | 
 |       weight >= Font::Weight::BOLD ? NSBoldFontMask : NSUnboldFontMask; | 
 |   derived_font = | 
 |       [font_manager convertFont:derived_font toHaveTrait:bold_trait_mask]; | 
 |  | 
 |   NSFontTraitMask italic_trait_mask = | 
 |       (style & Font::ITALIC) ? NSItalicFontMask : NSUnitalicFontMask; | 
 |   derived_font = | 
 |       [font_manager convertFont:derived_font toHaveTrait:italic_trait_mask]; | 
 |  | 
 |   derived_font = | 
 |       [font_manager convertFont:derived_font toSize:font_size_ + size_delta]; | 
 |  | 
 |   return Font(new PlatformFontMac(derived_font, font_name_, | 
 |                                   font_size_ + size_delta, style, weight)); | 
 | } | 
 |  | 
 | int PlatformFontMac::GetHeight() { | 
 |   return height_; | 
 | } | 
 |  | 
 | int PlatformFontMac::GetBaseline() { | 
 |   return ascent_; | 
 | } | 
 |  | 
 | int PlatformFontMac::GetCapHeight() { | 
 |   return cap_height_; | 
 | } | 
 |  | 
 | int PlatformFontMac::GetExpectedTextWidth(int length) { | 
 |   return length * average_width_; | 
 | } | 
 |  | 
 | int PlatformFontMac::GetStyle() const { | 
 |   return font_style_; | 
 | } | 
 |  | 
 | Font::Weight PlatformFontMac::GetWeight() const { | 
 |   return font_weight_; | 
 | } | 
 |  | 
 | const std::string& PlatformFontMac::GetFontName() const { | 
 |   return font_name_; | 
 | } | 
 |  | 
 | std::string PlatformFontMac::GetActualFontNameForTesting() const { | 
 |   return base::SysNSStringToUTF8([native_font_ familyName]); | 
 | } | 
 |  | 
 | int PlatformFontMac::GetFontSize() const { | 
 |   return font_size_; | 
 | } | 
 |  | 
 | const FontRenderParams& PlatformFontMac::GetFontRenderParams() { | 
 |   return render_params_; | 
 | } | 
 |  | 
 | NativeFont PlatformFontMac::GetNativeFont() const { | 
 |   return [[native_font_.get() retain] autorelease]; | 
 | } | 
 |  | 
 | //////////////////////////////////////////////////////////////////////////////// | 
 | // PlatformFontMac, private: | 
 |  | 
 | PlatformFontMac::PlatformFontMac(const std::string& font_name, | 
 |                                  int font_size, | 
 |                                  int font_style, | 
 |                                  Font::Weight font_weight) | 
 |     : PlatformFontMac( | 
 |           NSFontWithSpec(font_name, font_size, font_style, font_weight), | 
 |           font_name, | 
 |           font_size, | 
 |           font_style, | 
 |           font_weight) {} | 
 |  | 
 | PlatformFontMac::PlatformFontMac(NativeFont font, | 
 |                                  const std::string& font_name, | 
 |                                  int font_size, | 
 |                                  int font_style, | 
 |                                  Font::Weight font_weight) | 
 |     : native_font_([font retain]), | 
 |       font_name_(font_name), | 
 |       font_size_(font_size), | 
 |       font_style_(font_style), | 
 |       font_weight_(font_weight) { | 
 |   CalculateMetricsAndInitRenderParams(); | 
 | } | 
 |  | 
 | PlatformFontMac::~PlatformFontMac() { | 
 | } | 
 |  | 
 | void PlatformFontMac::CalculateMetricsAndInitRenderParams() { | 
 |   NSFont* font = native_font_.get(); | 
 |   if (!font) { | 
 |     // This object was constructed from a font name that doesn't correspond to | 
 |     // an actual font. Don't waste time working out metrics. | 
 |     height_ = 0; | 
 |     ascent_ = 0; | 
 |     cap_height_ = 0; | 
 |     average_width_ = 0; | 
 |     return; | 
 |   } | 
 |  | 
 |   ascent_ = ceil([font ascender]); | 
 |   cap_height_ = ceil([font capHeight]); | 
 |  | 
 |   // PlatformFontMac once used -[NSLayoutManager defaultLineHeightForFont:] to | 
 |   // initialize |height_|. However, it has a silly rounding bug. Essentially, it | 
 |   // gives round(ascent) + round(descent). E.g. Helvetica Neue at size 16 gives | 
 |   // ascent=15.4634, descent=3.38208 -> 15 + 3 = 18. When the height should be | 
 |   // at least 19. According to the OpenType specification, these values should | 
 |   // simply be added, so do that. Note this uses the already-rounded |ascent_| | 
 |   // to ensure GetBaseline() + descender fits within GetHeight() during layout. | 
 |   height_ = ceil(ascent_ + std::abs([font descender]) + [font leading]); | 
 |  | 
 |   average_width_ = | 
 |       NSWidth([font boundingRectForGlyph:[font glyphWithName:@"x"]]); | 
 |  | 
 |   FontRenderParamsQuery query; | 
 |   query.families.push_back(font_name_); | 
 |   query.pixel_size = font_size_; | 
 |   query.style = font_style_; | 
 |   query.weight = font_weight_; | 
 |   render_params_ = gfx::GetFontRenderParams(query, NULL); | 
 | } | 
 |  | 
 | //////////////////////////////////////////////////////////////////////////////// | 
 | // PlatformFont, public: | 
 |  | 
 | // static | 
 | PlatformFont* PlatformFont::CreateDefault() { | 
 |   return new PlatformFontMac; | 
 | } | 
 |  | 
 | // static | 
 | PlatformFont* PlatformFont::CreateFromNativeFont(NativeFont native_font) { | 
 |   return new PlatformFontMac(native_font); | 
 | } | 
 |  | 
 | // static | 
 | PlatformFont* PlatformFont::CreateFromNameAndSize(const std::string& font_name, | 
 |                                                   int font_size) { | 
 |   return new PlatformFontMac(font_name, font_size); | 
 | } | 
 |  | 
 | }  // namespace gfx |