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 #import "base/mac/foundation_util.h" | 11 #include "base/mac/scoped_nsobject.h" |
12 #include "base/mac/scoped_cftyperef.h" | |
13 #import "base/mac/scoped_nsobject.h" | |
14 #include "base/strings/sys_string_conversions.h" | 12 #include "base/strings/sys_string_conversions.h" |
15 #include "base/strings/utf_string_conversions.h" | 13 #include "base/strings/utf_string_conversions.h" |
16 #include "ui/gfx/canvas.h" | 14 #include "ui/gfx/canvas.h" |
17 #include "ui/gfx/font.h" | 15 #include "ui/gfx/font.h" |
18 #include "ui/gfx/font_render_params.h" | 16 #include "ui/gfx/font_render_params.h" |
19 | 17 |
20 namespace gfx { | 18 namespace gfx { |
21 | 19 |
22 namespace { | 20 namespace { |
23 | 21 |
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 | |
78 // Returns the font style for |font|. Disregards Font::UNDERLINE, since NSFont | 22 // Returns the font style for |font|. Disregards Font::UNDERLINE, since NSFont |
79 // does not support it as a trait. | 23 // does not support it as a trait. |
80 int GetFontStyleFromNSFont(NSFont* font) { | 24 int GetFontStyleFromNSFont(NSFont* font) { |
81 int font_style = Font::NORMAL; | 25 int font_style = Font::NORMAL; |
82 NSFontSymbolicTraits traits = [[font fontDescriptor] symbolicTraits]; | 26 NSFontSymbolicTraits traits = [[font fontDescriptor] symbolicTraits]; |
83 if (traits & NSFontItalicTrait) | 27 if (traits & NSFontItalicTrait) |
84 font_style |= Font::ITALIC; | 28 font_style |= Font::ITALIC; |
85 return font_style; | 29 return font_style; |
86 } | 30 } |
87 | 31 |
88 // Returns the Font weight for |font|. | 32 // Returns the Font weight for |font|. |
89 Font::Weight GetFontWeightFromNSFont(NSFont* font) { | 33 Font::Weight GetFontWeightFromNSFont(NSFont* font) { |
90 if (!font) | 34 NSFontSymbolicTraits traits = [[font fontDescriptor] symbolicTraits]; |
91 return Font::Weight::INVALID; | 35 return (traits & NSFontBoldTrait) ? Font::Weight::BOLD : Font::Weight::NORMAL; |
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; | |
140 } | 36 } |
141 | 37 |
142 // Returns an autoreleased NSFont created with the passed-in specifications. | 38 // Returns an autoreleased NSFont created with the passed-in specifications. |
143 NSFont* NSFontWithSpec(const std::string& font_name, | 39 NSFont* NSFontWithSpec(const std::string& font_name, |
144 int font_size, | 40 int font_size, |
145 int font_style, | 41 int font_style, |
146 Font::Weight font_weight) { | 42 Font::Weight font_weight) { |
147 NSFontSymbolicTraits trait_bits = 0; | 43 NSFontSymbolicTraits trait_bits = 0; |
148 // TODO(mboc): Add support for other weights as well. | 44 // TODO(mboc): Add support for other weights as well. |
149 if (font_weight >= Font::Weight::BOLD) | 45 if (font_weight >= Font::Weight::BOLD) |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
197 Font::Weight::NORMAL) {} | 93 Font::Weight::NORMAL) {} |
198 | 94 |
199 //////////////////////////////////////////////////////////////////////////////// | 95 //////////////////////////////////////////////////////////////////////////////// |
200 // PlatformFontMac, PlatformFont implementation: | 96 // PlatformFontMac, PlatformFont implementation: |
201 | 97 |
202 Font PlatformFontMac::DeriveFont(int size_delta, | 98 Font PlatformFontMac::DeriveFont(int size_delta, |
203 int style, | 99 int style, |
204 Font::Weight weight) const { | 100 Font::Weight weight) const { |
205 // For some reason, creating fonts using the NSFontDescriptor API's seem to be | 101 // For some reason, creating fonts using the NSFontDescriptor API's seem to be |
206 // unreliable. Hence use the NSFontManager. | 102 // unreliable. Hence use the NSFontManager. |
207 NSFont* derived = native_font_; | 103 NSFont* derived_font = native_font_; |
208 NSFontManager* font_manager = [NSFontManager sharedFontManager]; | 104 NSFontManager* font_manager = [NSFontManager sharedFontManager]; |
209 | 105 |
210 if (weight != font_weight_) { | 106 NSFontTraitMask bold_trait_mask = |
211 // Find a font without any bold traits. Ideally, all bold traits are | 107 weight >= Font::Weight::BOLD ? NSBoldFontMask : NSUnboldFontMask; |
212 // removed here, but non-symbolic traits are read-only properties of a | 108 derived_font = |
213 // particular set of glyphs. And attempting to "reset" the attribute with a | 109 [font_manager convertFont:derived_font toHaveTrait:bold_trait_mask]; |
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."; | |
223 | 110 |
224 WeightSolver plan = WeightChangeFromNormal(weight); | 111 NSFontTraitMask italic_trait_mask = |
225 if (plan.nearest == Font::Weight::BOLD) | 112 (style & Font::ITALIC) ? NSItalicFontMask : NSUnitalicFontMask; |
226 derived = [font_manager convertFont:derived toHaveTrait:NSBoldFontMask]; | 113 derived_font = |
227 for (int i = 0; i < plan.steps_up; ++i) | 114 [font_manager convertFont:derived_font toHaveTrait:italic_trait_mask]; |
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 } | |
232 | 115 |
233 if (style != font_style_) { | 116 derived_font = |
234 NSFontTraitMask italic_trait_mask = | 117 [font_manager convertFont:derived_font toSize:font_size_ + size_delta]; |
235 (style & Font::ITALIC) ? NSItalicFontMask : NSUnitalicFontMask; | |
236 derived = [font_manager convertFont:derived toHaveTrait:italic_trait_mask]; | |
237 } | |
238 | 118 |
239 if (size_delta != 0) | 119 return Font(new PlatformFontMac(derived_font, font_name_, |
240 derived = [font_manager convertFont:derived toSize:font_size_ + size_delta]; | 120 font_size_ + size_delta, style, weight)); |
241 | |
242 return Font(new PlatformFontMac(derived, font_name_, font_size_ + size_delta, | |
243 style, weight)); | |
244 } | 121 } |
245 | 122 |
246 int PlatformFontMac::GetHeight() { | 123 int PlatformFontMac::GetHeight() { |
247 return height_; | 124 return height_; |
248 } | 125 } |
249 | 126 |
250 int PlatformFontMac::GetBaseline() { | 127 int PlatformFontMac::GetBaseline() { |
251 return ascent_; | 128 return ascent_; |
252 } | 129 } |
253 | 130 |
(...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
374 return new PlatformFontMac(native_font); | 251 return new PlatformFontMac(native_font); |
375 } | 252 } |
376 | 253 |
377 // static | 254 // static |
378 PlatformFont* PlatformFont::CreateFromNameAndSize(const std::string& font_name, | 255 PlatformFont* PlatformFont::CreateFromNameAndSize(const std::string& font_name, |
379 int font_size) { | 256 int font_size) { |
380 return new PlatformFontMac(font_name, font_size); | 257 return new PlatformFontMac(font_name, font_size); |
381 } | 258 } |
382 | 259 |
383 } // namespace gfx | 260 } // namespace gfx |
OLD | NEW |