OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "ui/gfx/platform_font_mac.h" | 5 #include "ui/gfx/platform_font_mac.h" |
6 | 6 |
7 #include <cmath> | 7 #include <cmath> |
8 | 8 |
9 #include <Cocoa/Cocoa.h> | 9 #include <Cocoa/Cocoa.h> |
10 | 10 |
11 #include "base/mac/scoped_nsobject.h" | 11 #import "base/mac/foundation_util.h" |
| 12 #include "base/mac/scoped_cftyperef.h" |
| 13 #import "base/mac/scoped_nsobject.h" |
12 #include "base/strings/sys_string_conversions.h" | 14 #include "base/strings/sys_string_conversions.h" |
13 #include "base/strings/utf_string_conversions.h" | 15 #include "base/strings/utf_string_conversions.h" |
14 #include "ui/gfx/canvas.h" | 16 #include "ui/gfx/canvas.h" |
15 #include "ui/gfx/font.h" | 17 #include "ui/gfx/font.h" |
16 #include "ui/gfx/font_render_params.h" | 18 #include "ui/gfx/font_render_params.h" |
17 | 19 |
18 namespace gfx { | 20 namespace gfx { |
19 | 21 |
20 namespace { | 22 namespace { |
21 | 23 |
| 24 // How to get from NORMAL weight to a fine-grained font weight using calls to |
| 25 // -[NSFontManager convertWeight:(BOOL)upFlag ofFont:(NSFont)]. |
| 26 struct WeightSolver { |
| 27 int steps_up; // Times to call with upFlag:YES. |
| 28 int steps_down; // Times to call with upFlag:NO. |
| 29 // Either NORMAL or BOLD: whether to set the NSBoldFontMask symbolic trait. |
| 30 Font::Weight nearest; |
| 31 }; |
| 32 |
| 33 // Solve changes to the font weight according to the following table, from |
| 34 // https://developer.apple.com/reference/appkit/nsfontmanager/1462321-convertwei
ght |
| 35 // 1. ultralight | none |
| 36 // 2. thin | W1. ultralight |
| 37 // 3. light, extralight | W2. extralight |
| 38 // 4. book | W3. light |
| 39 // 5. regular, plain, display, roman | W4. semilight |
| 40 // 6. medium | W5. medium |
| 41 // 7. demi, demibold | none |
| 42 // 8. semi, semibold | W6. semibold |
| 43 // 9. bold | W7. bold |
| 44 // 10. extra, extrabold | W8. extrabold |
| 45 // 11. heavy, heavyface | none |
| 46 // 12. black, super | W9. ultrabold |
| 47 // 13. ultra, ultrablack, fat | none |
| 48 // 14. extrablack, obese, nord | none |
| 49 WeightSolver WeightChangeFromNormal(Font::Weight desired) { |
| 50 using Weight = Font::Weight; |
| 51 switch (desired) { |
| 52 case Weight::THIN: |
| 53 // It's weird, but to get LIGHT and THIN fonts, first go up a step. |
| 54 // Without this, the font stays stuck at NORMAL. See |
| 55 // PlatformFontMacTest, FontWeightAPIConsistency. |
| 56 return {1, 3, Weight::NORMAL}; |
| 57 case Weight::EXTRA_LIGHT: |
| 58 return {1, 2, Weight::NORMAL}; |
| 59 case Weight::LIGHT: |
| 60 return {1, 1, Weight::NORMAL}; |
| 61 case Weight::NORMAL: |
| 62 return {0, 0, Weight::NORMAL}; |
| 63 case Weight::MEDIUM: |
| 64 return {1, 0, Weight::NORMAL}; |
| 65 case Weight::SEMIBOLD: |
| 66 return {0, 1, Weight::BOLD}; |
| 67 case Weight::BOLD: |
| 68 return {0, 0, Weight::BOLD}; |
| 69 case Weight::EXTRA_BOLD: |
| 70 return {1, 0, Weight::BOLD}; |
| 71 case Weight::BLACK: |
| 72 return {3, 0, Weight::BOLD}; // Skip row 12. |
| 73 case Weight::INVALID: |
| 74 return {0, 0, Weight::NORMAL}; |
| 75 } |
| 76 } |
| 77 |
22 // Returns the font style for |font|. Disregards Font::UNDERLINE, since NSFont | 78 // Returns the font style for |font|. Disregards Font::UNDERLINE, since NSFont |
23 // does not support it as a trait. | 79 // does not support it as a trait. |
24 int GetFontStyleFromNSFont(NSFont* font) { | 80 int GetFontStyleFromNSFont(NSFont* font) { |
25 int font_style = Font::NORMAL; | 81 int font_style = Font::NORMAL; |
26 NSFontSymbolicTraits traits = [[font fontDescriptor] symbolicTraits]; | 82 NSFontSymbolicTraits traits = [[font fontDescriptor] symbolicTraits]; |
27 if (traits & NSFontItalicTrait) | 83 if (traits & NSFontItalicTrait) |
28 font_style |= Font::ITALIC; | 84 font_style |= Font::ITALIC; |
29 return font_style; | 85 return font_style; |
30 } | 86 } |
31 | 87 |
32 // Returns the Font weight for |font|. | 88 // Returns the Font weight for |font|. |
33 Font::Weight GetFontWeightFromNSFont(NSFont* font) { | 89 Font::Weight GetFontWeightFromNSFont(NSFont* font) { |
34 NSFontSymbolicTraits traits = [[font fontDescriptor] symbolicTraits]; | 90 if (!font) |
35 return (traits & NSFontBoldTrait) ? Font::Weight::BOLD : Font::Weight::NORMAL; | 91 return Font::Weight::INVALID; |
| 92 |
| 93 // Map CoreText weights in a manner similar to ct_weight_to_fontstyle() from |
| 94 // SkFontHost_mac.cpp, but adjust for MEDIUM so that the San Francisco's |
| 95 // custom MEDIUM weight can be picked out. San Francisco has weights: |
| 96 // [0.23, 0.23, 0.3, 0.4, 0.56, 0.62, 0.62, ...] (no thin weights). |
| 97 // See PlatformFontMacTest.FontWeightAPIConsistency for details. |
| 98 // Note that the table Skia uses is also determined by experiment. |
| 99 constexpr struct { |
| 100 CGFloat ct_weight; |
| 101 Font::Weight gfx_weight; |
| 102 } weight_map[] = { |
| 103 // Missing: Apple "ultralight". |
| 104 {-0.70, Font::Weight::THIN}, |
| 105 {-0.50, Font::Weight::EXTRA_LIGHT}, |
| 106 {-0.23, Font::Weight::LIGHT}, |
| 107 {0.00, Font::Weight::NORMAL}, |
| 108 {0.23, Font::Weight::MEDIUM}, // Note: adjusted from 0.20 vs Skia. |
| 109 // Missing: Apple "demibold". |
| 110 {0.30, Font::Weight::SEMIBOLD}, |
| 111 {0.40, Font::Weight::BOLD}, |
| 112 {0.60, Font::Weight::EXTRA_BOLD}, |
| 113 // Missing: Apple "heavyface". |
| 114 // Values will be capped to BLACK (this entry is here for consistency). |
| 115 {0.80, Font::Weight::BLACK}, |
| 116 // Missing: Apple "ultrablack". |
| 117 // Missing: Apple "extrablack". |
| 118 }; |
| 119 base::ScopedCFTypeRef<CFDictionaryRef> traits( |
| 120 CTFontCopyTraits(base::mac::NSToCFCast(font))); |
| 121 DCHECK(traits); |
| 122 CFNumberRef cf_weight = base::mac::GetValueFromDictionary<CFNumberRef>( |
| 123 traits, kCTFontWeightTrait); |
| 124 // A missing weight attribute just means 0 -> NORMAL. |
| 125 if (!cf_weight) |
| 126 return Font::Weight::NORMAL; |
| 127 |
| 128 // Documentation is vague about what sized floating point type should be used. |
| 129 // However, numeric_limits::epsilon() for 64-bit types is too small to match |
| 130 // the above table, so use 32-bit float. |
| 131 float weight_value; |
| 132 Boolean success = |
| 133 CFNumberGetValue(cf_weight, kCFNumberFloatType, &weight_value); |
| 134 DCHECK(success); |
| 135 for (const auto& item : weight_map) { |
| 136 if (weight_value - item.ct_weight <= std::numeric_limits<float>::epsilon()) |
| 137 return item.gfx_weight; |
| 138 } |
| 139 return Font::Weight::BLACK; |
36 } | 140 } |
37 | 141 |
38 // Returns an autoreleased NSFont created with the passed-in specifications. | 142 // Returns an autoreleased NSFont created with the passed-in specifications. |
39 NSFont* NSFontWithSpec(const std::string& font_name, | 143 NSFont* NSFontWithSpec(const std::string& font_name, |
40 int font_size, | 144 int font_size, |
41 int font_style, | 145 int font_style, |
42 Font::Weight font_weight) { | 146 Font::Weight font_weight) { |
43 NSFontSymbolicTraits trait_bits = 0; | 147 NSFontSymbolicTraits trait_bits = 0; |
44 // TODO(mboc): Add support for other weights as well. | 148 // TODO(mboc): Add support for other weights as well. |
45 if (font_weight >= Font::Weight::BOLD) | 149 if (font_weight >= Font::Weight::BOLD) |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
93 Font::Weight::NORMAL) {} | 197 Font::Weight::NORMAL) {} |
94 | 198 |
95 //////////////////////////////////////////////////////////////////////////////// | 199 //////////////////////////////////////////////////////////////////////////////// |
96 // PlatformFontMac, PlatformFont implementation: | 200 // PlatformFontMac, PlatformFont implementation: |
97 | 201 |
98 Font PlatformFontMac::DeriveFont(int size_delta, | 202 Font PlatformFontMac::DeriveFont(int size_delta, |
99 int style, | 203 int style, |
100 Font::Weight weight) const { | 204 Font::Weight weight) const { |
101 // For some reason, creating fonts using the NSFontDescriptor API's seem to be | 205 // For some reason, creating fonts using the NSFontDescriptor API's seem to be |
102 // unreliable. Hence use the NSFontManager. | 206 // unreliable. Hence use the NSFontManager. |
103 NSFont* derived_font = native_font_; | 207 NSFont* derived = native_font_; |
104 NSFontManager* font_manager = [NSFontManager sharedFontManager]; | 208 NSFontManager* font_manager = [NSFontManager sharedFontManager]; |
105 | 209 |
106 NSFontTraitMask bold_trait_mask = | 210 if (weight != font_weight_) { |
107 weight >= Font::Weight::BOLD ? NSBoldFontMask : NSUnboldFontMask; | 211 // Find a font without any bold traits. Ideally, all bold traits are |
108 derived_font = | 212 // removed here, but non-symbolic traits are read-only properties of a |
109 [font_manager convertFont:derived_font toHaveTrait:bold_trait_mask]; | 213 // particular set of glyphs. And attempting to "reset" the attribute with a |
| 214 // new font descriptor will lose internal properties that Mac decorates its |
| 215 // UI fonts with. E.g., solving the plans below from NORMAL result in a |
| 216 // CTFontDescriptor attribute entry of NSCTFontUIUsageAttribute in |
| 217 // CTFont{Regular,Medium,Demi,Emphasized,Heavy,Black}Usage. Attempting to |
| 218 // "solve" weights starting at other than NORMAL has unpredictable results. |
| 219 if (font_weight_ != Font::Weight::NORMAL) |
| 220 derived = [font_manager convertFont:derived toHaveTrait:NSUnboldFontMask]; |
| 221 DLOG_IF(WARNING, GetFontWeightFromNSFont(derived) != Font::Weight::NORMAL) |
| 222 << "Deriving from a font with an internal unmodifiable weight."; |
110 | 223 |
111 NSFontTraitMask italic_trait_mask = | 224 WeightSolver plan = WeightChangeFromNormal(weight); |
112 (style & Font::ITALIC) ? NSItalicFontMask : NSUnitalicFontMask; | 225 if (plan.nearest == Font::Weight::BOLD) |
113 derived_font = | 226 derived = [font_manager convertFont:derived toHaveTrait:NSBoldFontMask]; |
114 [font_manager convertFont:derived_font toHaveTrait:italic_trait_mask]; | 227 for (int i = 0; i < plan.steps_up; ++i) |
| 228 derived = [font_manager convertWeight:YES ofFont:derived]; |
| 229 for (int i = 0; i < plan.steps_down; ++i) |
| 230 derived = [font_manager convertWeight:NO ofFont:derived]; |
| 231 } |
115 | 232 |
116 derived_font = | 233 if (style != font_style_) { |
117 [font_manager convertFont:derived_font toSize:font_size_ + size_delta]; | 234 NSFontTraitMask italic_trait_mask = |
| 235 (style & Font::ITALIC) ? NSItalicFontMask : NSUnitalicFontMask; |
| 236 derived = [font_manager convertFont:derived toHaveTrait:italic_trait_mask]; |
| 237 } |
118 | 238 |
119 return Font(new PlatformFontMac(derived_font, font_name_, | 239 if (size_delta != 0) |
120 font_size_ + size_delta, style, weight)); | 240 derived = [font_manager convertFont:derived toSize:font_size_ + size_delta]; |
| 241 |
| 242 return Font(new PlatformFontMac(derived, font_name_, font_size_ + size_delta, |
| 243 style, weight)); |
121 } | 244 } |
122 | 245 |
123 int PlatformFontMac::GetHeight() { | 246 int PlatformFontMac::GetHeight() { |
124 return height_; | 247 return height_; |
125 } | 248 } |
126 | 249 |
127 int PlatformFontMac::GetBaseline() { | 250 int PlatformFontMac::GetBaseline() { |
128 return ascent_; | 251 return ascent_; |
129 } | 252 } |
130 | 253 |
(...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
251 return new PlatformFontMac(native_font); | 374 return new PlatformFontMac(native_font); |
252 } | 375 } |
253 | 376 |
254 // static | 377 // static |
255 PlatformFont* PlatformFont::CreateFromNameAndSize(const std::string& font_name, | 378 PlatformFont* PlatformFont::CreateFromNameAndSize(const std::string& font_name, |
256 int font_size) { | 379 int font_size) { |
257 return new PlatformFontMac(font_name, font_size); | 380 return new PlatformFontMac(font_name, font_size); |
258 } | 381 } |
259 | 382 |
260 } // namespace gfx | 383 } // namespace gfx |
OLD | NEW |