Commit b0e19202 authored by Avi Drissman's avatar Avi Drissman Committed by Commit Bot

Do clean up in PlatformFontMac

Do not try to derive one weight of font from another.
Use the API to directly generate them.

PlatformFontMacTest.FontWeightAPIConsistency tested that
our understanding of shifting weights up and down was
correct. The code no longer shifts weights up and down
as that is unreliable and version-specific, so there is
no need to test weight shifting and it is removed.

This also fixes Big Sur issues.

This re-lands c2e39ede.
It turns out that DCHECKs do evaluate everything in them
so wrap it with DCHECK_IS_ON().

Bug: 1101426
Change-Id: I1194b4aa127223456b432d12c47d248264d87c85
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2342387Reviewed-by: default avatarElly Fong-Jones <ellyjones@chromium.org>
Commit-Queue: Avi Drissman <avi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#796478}
parent aeac4677
......@@ -8,18 +8,39 @@
#include "base/compiler_specific.h"
#include "base/mac/scoped_nsobject.h"
#include "base/macros.h"
#include "base/optional.h"
#include "ui/gfx/font_render_params.h"
#include "ui/gfx/platform_font.h"
namespace gfx {
class PlatformFontMac : public PlatformFont {
class GFX_EXPORT PlatformFontMac : public PlatformFont {
public:
PlatformFontMac();
// An enum indicating a type of system-specified font.
// - kGeneral: +[NSFont systemFontOfSize:(weight:)]
// - kMenu: +[NSFont menuFontOfSize:]
// - kToolTip: +[NSFont toolTipsFontOfSize:]
enum class SystemFontType { kGeneral, kMenu, kToolTip };
// Constructs a PlatformFontMac for a system-specified font of
// |system_font_type| type. For a non-system-specified font, use any other
// constructor.
explicit PlatformFontMac(SystemFontType system_font_type);
// Constructs a PlatformFontMac for containing the NSFont* |native_font|. Do
// not call this for a system-specified font; use the |SystemFontType|
// constructor for that. |native_font| must not be null.
explicit PlatformFontMac(NativeFont native_font);
// Constructs a PlatformFontMac representing the font with name |font_name|
// and the size |font_size|. Do not call this for a system-specified font; use
// the |SystemFontType| constructor for that.
PlatformFontMac(const std::string& font_name,
int font_size);
// Constructs a PlatformFontMac representing the font specified by |typeface|
// and the size |font_size_pixels|. Do not call this for a system-specified
// font; use the |SystemFontType| constructor for that.
PlatformFontMac(sk_sp<SkTypeface> typeface,
int font_size_pixels,
const base::Optional<FontRenderParams>& params);
......@@ -41,35 +62,44 @@ class PlatformFontMac : public PlatformFont {
NativeFont GetNativeFont() const override;
sk_sp<SkTypeface> GetNativeSkTypeface() const override;
// A utility function to get the weight of an NSFont. Used by the unit test.
static Font::Weight GetFontWeightFromNSFontForTesting(NSFont* font);
private:
PlatformFontMac(const std::string& font_name,
int font_size,
int font_style,
Font::Weight font_weight);
struct FontSpec {
std::string name; // Corresponds to -[NSFont fontFamily].
int size;
int style;
Font::Weight weight;
};
PlatformFontMac(NativeFont font,
const std::string& font_name,
int font_size,
int font_style,
Font::Weight font_weight);
base::Optional<SystemFontType> system_font_type);
PlatformFontMac(NativeFont font,
base::Optional<SystemFontType> system_font_type,
FontSpec spec);
~PlatformFontMac() override;
// Calculates and caches the font metrics and inits |render_params_|.
// Calculates and caches the font metrics and initializes |render_params_|.
void CalculateMetricsAndInitRenderParams();
// Returns an autoreleased NSFont created with the passed-in specifications.
NSFont* NSFontWithSpec(FontSpec font_spec) const;
// The NSFont instance for this object. If this object was constructed from an
// NSFont instance, this holds that NSFont instance. Otherwise this NSFont
// instance is constructed from the name, size, and style. If there is no
// active font that matched those criteria a default font is used.
base::scoped_nsobject<NSFont> native_font_;
// The name/size/style trio that specify the font. Initialized in the
// constructors.
const std::string font_name_; // Corresponds to -[NSFont fontFamily].
const int font_size_;
const int font_style_;
const Font::Weight font_weight_;
// If the font is a system font, and if so, what kind.
const base::Optional<SystemFontType> system_font_type_;
// The name/size/style/weight quartet that specify the font. Initialized in
// the constructors.
const FontSpec font_spec_;
// Cached metrics, generated in CalculateMetrics().
int height_;
......
......@@ -5,12 +5,15 @@
#include "ui/gfx/platform_font_mac.h"
#include <cmath>
#include <set>
#include <Cocoa/Cocoa.h>
#include "base/bit_cast.h"
#import "base/mac/foundation_util.h"
#include "base/mac/scoped_cftyperef.h"
#import "base/mac/scoped_nsobject.h"
#include "base/no_destructor.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "third_party/skia/include/ports/SkTypeface_mac.h"
......@@ -20,62 +23,14 @@
namespace gfx {
namespace {
// How to get from NORMAL weight to a fine-grained font weight using calls to
// -[NSFontManager convertWeight:(BOOL)upFlag ofFont:(NSFont)].
struct WeightSolver {
int steps_up; // Times to call with upFlag:YES.
int steps_down; // Times to call with upFlag:NO.
// Either NORMAL or BOLD: whether to set the NSBoldFontMask symbolic trait.
Font::Weight nearest;
};
using Weight = Font::Weight;
// Solve changes to the font weight according to the following table, from
// https://developer.apple.com/reference/appkit/nsfontmanager/1462321-convertweight
// 1. ultralight | none
// 2. thin | W1. ultralight
// 3. light, extralight | W2. extralight
// 4. book | W3. light
// 5. regular, plain, display, roman | W4. semilight
// 6. medium | W5. medium
// 7. demi, demibold | none
// 8. semi, semibold | W6. semibold
// 9. bold | W7. bold
// 10. extra, extrabold | W8. extrabold
// 11. heavy, heavyface | none
// 12. black, super | W9. ultrabold
// 13. ultra, ultrablack, fat | none
// 14. extrablack, obese, nord | none
WeightSolver WeightChangeFromNormal(Font::Weight desired) {
using Weight = Font::Weight;
switch (desired) {
case Weight::THIN:
// It's weird, but to get LIGHT and THIN fonts, first go up a step.
// Without this, the font stays stuck at NORMAL. See
// PlatformFontMacTest, FontWeightAPIConsistency.
return {1, 3, Weight::NORMAL};
case Weight::EXTRA_LIGHT:
return {1, 2, Weight::NORMAL};
case Weight::LIGHT:
return {1, 1, Weight::NORMAL};
case Weight::NORMAL:
return {0, 0, Weight::NORMAL};
case Weight::MEDIUM:
return {1, 0, Weight::NORMAL};
case Weight::SEMIBOLD:
return {0, 1, Weight::BOLD};
case Weight::BOLD:
return {0, 0, Weight::BOLD};
case Weight::EXTRA_BOLD:
return {1, 0, Weight::BOLD};
case Weight::BLACK:
return {3, 0, Weight::BOLD}; // Skip row 12.
case Weight::INVALID:
return {0, 0, Weight::NORMAL};
}
extern "C" {
bool CTFontDescriptorIsSystemUIFont(CTFontDescriptorRef);
}
namespace {
// Returns the font style for |font|. Disregards Font::UNDERLINE, since NSFont
// does not support it as a trait.
int GetFontStyleFromNSFont(NSFont* font) {
......@@ -86,21 +41,21 @@ int GetFontStyleFromNSFont(NSFont* font) {
return font_style;
}
// Returns the Font weight for |font|.
Font::Weight GetFontWeightFromNSFont(NSFont* font) {
// Returns the Font::Weight for |font|.
Weight GetFontWeightFromNSFont(NSFont* font) {
DCHECK(font);
// Map CoreText weights in a manner similar to ct_weight_to_fontstyle() from
// SkFontHost_mac.cpp, but adjusted for the weights actually used by the
// system fonts. See PlatformFontMacTest.FontWeightAPIConsistency for details.
// The Mac uses specific float values, but to avoid float comparison
// inaccuracies as well as for paranoia in case float values other than these
// appear, use ranges.
// macOS uses specific float values in its constants, but individual fonts can
// and do specify arbitrary values in the -1.0 to 1.0 range. Therefore, to
// accomodate that, and to avoid float comparison issues, use ranges.
constexpr struct {
// A range of CoreText weights.
CGFloat weight_lower;
CGFloat weight_upper;
Font::Weight gfx_weight;
Weight gfx_weight;
} weight_map[] = {
// NSFontWeight constants introduced in 10.11:
// NSFontWeightUltraLight: -0.80
......@@ -115,29 +70,35 @@ Font::Weight GetFontWeightFromNSFont(NSFont* font) {
//
// Actual system font weights:
// 10.10:
// .HelveticaNeueDeskInterface-UltraLightP2: -0.80
// .HelveticaNeueDeskInterface-Thin: -0.50
// .HelveticaNeueDeskInterface-Light: -0.425
// .HelveticaNeueDeskInterface-Regular: 0.0
// .HelveticaNeueDeskInterface-MediumP4: 0.23
// .HelveticaNeueDeskInterface-Bold: 0.4
// .HelveticaNeueDeskInterface-Heavy: 0.62
// .HelveticaNeueDeskInterface-Bold (if requested as semibold): 0.24
// .HelveticaNeueDeskInterface-Bold (if requested as bold): 0.4
// .HelveticaNeueDeskInterface-Heavy (if requested as heavy): 0.576
// .HelveticaNeueDeskInterface-Heavy (if requested as black): 0.662
// 10.11-:
// .AppleSystemUIFontUltraLight: -0.80 (10.12-)
// .AppleSystemUIFontLight: -0.40 (10.12-)
// .AppleSystemUIFont: 0 (10.11-)
// .AppleSystemUIFontMedium: 0.23 (10.12-)
// .AppleSystemUIFontDemi: 0.30 (10.12-)
// .AppleSystemUIFontBold: 0.40 (10.11)
// .AppleSystemUIFontEmphasized: 0.40 (10.12-)
// .AppleSystemUIFontHeavy: 0.56 (10.11-)
// .AppleSystemUIFontBlack: 0.62 (10.11-)
{-1.0, -0.70, Font::Weight::THIN}, // NSFontWeightUltraLight
{-0.70, -0.45, Font::Weight::EXTRA_LIGHT}, // NSFontWeightThin
{-0.45, -0.10, Font::Weight::LIGHT}, // NSFontWeightLight
{-0.10, 0.10, Font::Weight::NORMAL}, // NSFontWeightRegular
{0.10, 0.27, Font::Weight::MEDIUM}, // NSFontWeightMedium
{0.27, 0.35, Font::Weight::SEMIBOLD}, // NSFontWeightSemibold
{0.35, 0.50, Font::Weight::BOLD}, // NSFontWeightBold
{0.50, 0.60, Font::Weight::EXTRA_BOLD}, // NSFontWeightHeavy
{0.60, 1.0, Font::Weight::BLACK}, // NSFontWeightBlack
// .AppleSystemUIFontUltraLight: -0.80
// .AppleSystemUIFontThin: -0.60
// .AppleSystemUIFontLight: -0.40
// .AppleSystemUIFont: 0.0
// .AppleSystemUIFontMedium: 0.23
// .AppleSystemUIFontDemi: 0.30
// .AppleSystemUIFontBold (10.11): 0.40
// .AppleSystemUIFontEmphasized (10.12-): 0.40
// .AppleSystemUIFontHeavy: 0.56
// .AppleSystemUIFontBlack: 0.62
{-1.0, -0.70, Weight::THIN}, // NSFontWeightUltraLight
{-0.70, -0.45, Weight::EXTRA_LIGHT}, // NSFontWeightThin
{-0.45, -0.10, Weight::LIGHT}, // NSFontWeightLight
{-0.10, 0.10, Weight::NORMAL}, // NSFontWeightRegular
{0.10, 0.27, Weight::MEDIUM}, // NSFontWeightMedium
{0.27, 0.35, Weight::SEMIBOLD}, // NSFontWeightSemibold
{0.35, 0.50, Weight::BOLD}, // NSFontWeightBold
{0.50, 0.60, Weight::EXTRA_BOLD}, // NSFontWeightHeavy
{0.60, 1.0, Weight::BLACK}, // NSFontWeightBlack
};
base::ScopedCFTypeRef<CFDictionaryRef> traits(
......@@ -147,7 +108,7 @@ Font::Weight GetFontWeightFromNSFont(NSFont* font) {
traits, kCTFontWeightTrait);
// A missing weight attribute just means 0 -> NORMAL.
if (!cf_weight)
return Font::Weight::NORMAL;
return Weight::NORMAL;
// The value of kCTFontWeightTrait empirically is a kCFNumberFloat64Type
// (double) on all tested versions of macOS. However, that doesn't really
......@@ -160,47 +121,116 @@ Font::Weight GetFontWeightFromNSFont(NSFont* font) {
if (item.weight_lower <= weight && weight <= item.weight_upper)
return item.gfx_weight;
}
return Font::Weight::INVALID;
return Weight::INVALID;
}
// 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;
// Converts a Font::Weight value to the corresponding NSFontWeight value.
NSFontWeight ToNSFontWeight(Weight weight) {
if (@available(macOS 10.11, *)) {
switch (weight) {
case Weight::THIN:
return NSFontWeightUltraLight;
case Weight::EXTRA_LIGHT:
return NSFontWeightThin;
case Weight::LIGHT:
return NSFontWeightLight;
case Weight::INVALID:
case Weight::NORMAL:
return NSFontWeightRegular;
case Weight::MEDIUM:
return NSFontWeightMedium;
case Weight::SEMIBOLD:
return NSFontWeightSemibold;
case Weight::BOLD:
return NSFontWeightBold;
case Weight::EXTRA_BOLD:
return NSFontWeightHeavy;
case Weight::BLACK:
return NSFontWeightBlack;
}
} else {
// See third_party/blink/renderer/platform/fonts/mac/font_matcher_mac.mm.
uint64_t int_value = 0;
switch (weight) {
case Weight::THIN:
int_value = 0xbfe99999a0000000; // NSFontWeightUltraLight;
break;
case Weight::EXTRA_LIGHT:
int_value = 0xbfe3333340000000; // NSFontWeightThin;
break;
case Weight::LIGHT:
int_value = 0xbfd99999a0000000; // NSFontWeightLight;
break;
case Weight::INVALID:
case Weight::NORMAL:
int_value = 0x0000000000000000; // NSFontWeightRegular;
break;
case Weight::MEDIUM:
int_value = 0x3fcd70a3e0000000; // NSFontWeightMedium;
break;
case Weight::SEMIBOLD:
int_value = 0x3fd3333340000000; // NSFontWeightSemibold;
break;
case Weight::BOLD:
int_value = 0x3fd99999a0000000; // NSFontWeightBold;
break;
case Weight::EXTRA_BOLD:
int_value = 0x3fe1eb8520000000; // NSFontWeightHeavy;
break;
case Weight::BLACK:
int_value = 0x3fe3d70a40000000; // NSFontWeightBlack;
break;
}
// 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];
return bit_cast<CGFloat>(int_value);
}
}
// Returns |font| or a default font if |font| is nil.
NSFont* ValidateFont(NSFont* font) {
return font ? font : [NSFont systemFontOfSize:[NSFont systemFontSize]];
// Chromium uses the ISO-style, 9-value ladder of font weights (THIN-BLACK). The
// new font API in macOS also uses these weights, though they are constants
// defined in terms of CGFloat with values from -1.0 to 1.0.
//
// However, the old API used by the NSFontManager uses integer values on a
// "scale of 0 to 15". These values are used in:
//
// -[NSFontManager availableMembersOfFontFamily:]
// -[NSFontManager convertWeight:ofFont:]
// -[NSFontManager fontWithFamily:traits:weight:size:]
// -[NSFontManager weightOfFont:]
//
// Apple provides a chart of how the ISO values correspond:
// https://developer.apple.com/reference/appkit/nsfontmanager/1462321-convertweight
// However, it's more complicated than that. A survey of fonts yields the
// correspondence in this function, but the outliers imply that the ISO-style
// weight is more along the lines of "weight role within the font family" vs
// this number which is more like "how weighty is this font compared to all
// other fonts".
//
// These numbers can't really be forced to line up; different fonts disagree on
// how to map them. This function mostly follows the documented chart as
// inspired by actual fonts, and should be good enough.
NSInteger ToNSFontManagerWeight(Weight weight) {
switch (weight) {
case Weight::THIN:
return 2;
case Weight::EXTRA_LIGHT:
return 3;
case Weight::LIGHT:
return 4;
case Weight::INVALID:
case Weight::NORMAL:
return 5;
case Weight::MEDIUM:
return 6;
case Weight::SEMIBOLD:
return 8;
case Weight::BOLD:
return 9;
case Weight::EXTRA_BOLD:
return 10;
case Weight::BLACK:
return 11;
}
}
std::string GetFamilyNameFromTypeface(sk_sp<SkTypeface> typeface) {
......@@ -209,88 +239,155 @@ std::string GetFamilyNameFromTypeface(sk_sp<SkTypeface> typeface) {
return family.c_str();
}
NSFont* SystemFontForConstructorOfType(PlatformFontMac::SystemFontType type) {
switch (type) {
case PlatformFontMac::SystemFontType::kGeneral:
return [NSFont systemFontOfSize:[NSFont systemFontSize]];
case PlatformFontMac::SystemFontType::kMenu:
return [NSFont menuFontOfSize:0];
case PlatformFontMac::SystemFontType::kToolTip:
return [NSFont toolTipsFontOfSize:0];
}
}
base::Optional<PlatformFontMac::SystemFontType>
SystemFontTypeFromUndocumentedCTFontRefInternals(CTFontRef font) {
// The macOS APIs can't reliably derive one font from another. That's why for
// non-system fonts PlatformFontMac::DeriveFont() uses the family name of the
// font to find look up new fonts from scratch, and why, for system fonts, it
// uses the system font APIs to generate new system fonts.
//
// Skia's font handling assumes that given a font object, new fonts can be
// derived from it. That's absolutely not true on the Mac. However, this needs
// to be fixed, and a rewrite of how Skia handles fonts is not on the table.
//
// Therefore this sad hack. If Skia provides an SkTypeface, dig into the
// undocumented bowels of CoreText and magically determine if the font is a
// system font. This allows PlatformFontMac to correctly derive variants of
// the provided font.
//
// TODO(avi, etienneb): Figure out this font stuff.
base::ScopedCFTypeRef<CTFontDescriptorRef> descriptor(
CTFontCopyFontDescriptor(font));
if (CTFontDescriptorIsSystemUIFont(descriptor.get())) {
// Assume it's the standard system font. The fact that this much is known is
// enough.
return PlatformFontMac::SystemFontType::kGeneral;
} else {
return base::nullopt;
}
}
#if DCHECK_IS_ON()
const std::set<std::string>& SystemFontNames() {
static const base::NoDestructor<std::set<std::string>> names([] {
std::set<std::string> names;
names.insert(base::SysNSStringToUTF8(
[NSFont systemFontOfSize:[NSFont systemFontSize]].familyName));
names.insert(base::SysNSStringToUTF8([NSFont menuFontOfSize:0].familyName));
names.insert(
base::SysNSStringToUTF8([NSFont toolTipsFontOfSize:0].familyName));
return names;
}());
return *names;
}
#endif // DCHECK_IS_ON()
} // namespace
////////////////////////////////////////////////////////////////////////////////
// PlatformFontMac, public:
PlatformFontMac::PlatformFontMac()
: PlatformFontMac([NSFont systemFontOfSize:[NSFont systemFontSize]]) {
}
PlatformFontMac::PlatformFontMac(SystemFontType system_font_type)
: PlatformFontMac(SystemFontForConstructorOfType(system_font_type),
system_font_type) {}
PlatformFontMac::PlatformFontMac(NativeFont native_font)
: PlatformFontMac(native_font,
base::SysNSStringToUTF8([native_font familyName]),
[native_font pointSize],
GetFontStyleFromNSFont(native_font),
GetFontWeightFromNSFont(native_font)) {
DCHECK(native_font); // Null should not be passed to this constructor.
: PlatformFontMac(native_font, base::nullopt) {
DCHECK(native_font); // nil should not be passed to this constructor.
}
PlatformFontMac::PlatformFontMac(const std::string& font_name, int font_size)
: PlatformFontMac(font_name,
font_size,
Font::NORMAL,
Font::Weight::NORMAL) {}
: PlatformFontMac(
NSFontWithSpec({font_name, font_size, Font::NORMAL, Weight::NORMAL}),
base::nullopt,
{font_name, font_size, Font::NORMAL, Weight::NORMAL}) {}
PlatformFontMac::PlatformFontMac(sk_sp<SkTypeface> typeface,
int font_size_pixels,
const base::Optional<FontRenderParams>& params)
: PlatformFontMac(
base::mac::CFToNSCast(SkTypeface_GetCTFontRef(typeface.get())),
GetFamilyNameFromTypeface(typeface),
font_size_pixels,
SystemFontTypeFromUndocumentedCTFontRefInternals(
SkTypeface_GetCTFontRef(typeface.get())),
{GetFamilyNameFromTypeface(typeface), font_size_pixels,
(typeface->isItalic() ? Font::ITALIC : Font::NORMAL),
FontWeightFromInt(typeface->fontStyle().weight())) {}
FontWeightFromInt(typeface->fontStyle().weight())}) {}
////////////////////////////////////////////////////////////////////////////////
// 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 = native_font_;
NSFontManager* font_manager = [NSFontManager sharedFontManager];
if (weight != font_weight_) {
// Find a font without any bold traits. Ideally, all bold traits are
// removed here, but non-symbolic traits are read-only properties of a
// particular set of glyphs. And attempting to "reset" the attribute with a
// new font descriptor will lose internal properties that Mac decorates its
// UI fonts with. E.g., solving the plans below from NORMAL result in a
// CTFontDescriptor attribute entry of NSCTFontUIUsageAttribute in
// CTFont{Regular,Medium,Demi,Emphasized,Heavy,Black}Usage. Attempting to
// "solve" weights starting at other than NORMAL has unpredictable results.
if (font_weight_ != Font::Weight::NORMAL)
derived = [font_manager convertFont:derived toHaveTrait:NSUnboldFontMask];
DLOG_IF(WARNING, GetFontWeightFromNSFont(derived) != Font::Weight::NORMAL)
<< "Deriving from a font with an internal unmodifiable weight.";
WeightSolver plan = WeightChangeFromNormal(weight);
if (plan.nearest == Font::Weight::BOLD)
derived = [font_manager convertFont:derived toHaveTrait:NSBoldFontMask];
for (int i = 0; i < plan.steps_up; ++i)
derived = [font_manager convertWeight:YES ofFont:derived];
for (int i = 0; i < plan.steps_down; ++i)
derived = [font_manager convertWeight:NO ofFont:derived];
}
// Always apply the italic trait, even if the italic trait is not changing.
// it's possible for a change in the weight to trigger the font to go italic.
// This is due to an AppKit bug. See http://crbug.com/742261.
if (style != font_style_ || weight != font_weight_) {
Weight weight) const {
// What doesn't work?
//
// For all fonts, -[NSFontManager convertWeight:ofFont:] will reliably
// misbehave, skipping over particular weights of fonts, refusing to go
// lighter than regular unless you go heavier first, and in earlier versions
// of the system would accidentally introduce italic fonts when changing
// weights from a non-italic instance.
//
// For system fonts, -[NSFontManager convertFont:to(Not)HaveTrait:], if used
// to change weight, will sometimes switch to a compatibility system font that
// does not have all the weights available.
//
// For system fonts, the most reliable call to use is +[NSFont
// systemFontOfSize:weight:]. This uses the new-style NSFontWeight which maps
// perfectly to the ISO weights that Chromium uses. For non-system fonts,
// -[NSFontManager fontWithFamily:traits:weight:size:] is the only reasonable
// way to query fonts with more granularity than bold/non-bold short of
// walking the font family and querying their kCTFontWeightTrait values. Font
// descriptors hold promise but querying using them often fails to find fonts
// that match; hopefully their matching abilities will improve in future
// versions of the macOS.
if (system_font_type_ == SystemFontType::kGeneral) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunguarded-availability"
// +[NSFont systemFontOfSize:weight:] is declared as available on 10.11+,
// but actually it is there and works on 10.10.
NSFont* derived = [NSFont systemFontOfSize:font_spec_.size + size_delta
weight:ToNSFontWeight(weight)];
#pragma clang diagnostic pop
NSFontTraitMask italic_trait_mask =
(style & Font::ITALIC) ? NSItalicFontMask : NSUnitalicFontMask;
derived = [font_manager convertFont:derived toHaveTrait:italic_trait_mask];
derived = [[NSFontManager sharedFontManager] convertFont:derived
toHaveTrait:italic_trait_mask];
return Font(new PlatformFontMac(
derived, SystemFontType::kGeneral,
{font_spec_.name, font_spec_.size + size_delta, style, weight}));
} else if (system_font_type_ == SystemFontType::kMenu) {
NSFont* derived = [NSFont menuFontOfSize:font_spec_.size + size_delta];
return Font(new PlatformFontMac(
derived, SystemFontType::kMenu,
{font_spec_.name, font_spec_.size + size_delta, style, weight}));
} else if (system_font_type_ == SystemFontType::kToolTip) {
NSFont* derived = [NSFont toolTipsFontOfSize:font_spec_.size + size_delta];
return Font(new PlatformFontMac(
derived, SystemFontType::kToolTip,
{font_spec_.name, font_spec_.size + size_delta, style, weight}));
} else {
NSFont* derived = NSFontWithSpec(
{font_spec_.name, font_spec_.size + size_delta, style, weight});
return Font(new PlatformFontMac(
derived, base::nullopt,
{font_spec_.name, font_spec_.size + size_delta, style, weight}));
}
if (size_delta != 0)
derived = [font_manager convertFont:derived toSize:font_size_ + size_delta];
return Font(new PlatformFontMac(derived, font_name_, font_size_ + size_delta,
style, weight));
}
int PlatformFontMac::GetHeight() {
......@@ -323,15 +420,15 @@ int PlatformFontMac::GetExpectedTextWidth(int length) {
}
int PlatformFontMac::GetStyle() const {
return font_style_;
return font_spec_.style;
}
Font::Weight PlatformFontMac::GetWeight() const {
return font_weight_;
Weight PlatformFontMac::GetWeight() const {
return font_spec_.weight;
}
const std::string& PlatformFontMac::GetFontName() const {
return font_name_;
return font_spec_.name;
}
std::string PlatformFontMac::GetActualFontName() const {
......@@ -339,7 +436,7 @@ std::string PlatformFontMac::GetActualFontName() const {
}
int PlatformFontMac::GetFontSize() const {
return font_size_;
return font_spec_.size;
}
const FontRenderParams& PlatformFontMac::GetFontRenderParams() {
......@@ -354,30 +451,37 @@ sk_sp<SkTypeface> PlatformFontMac::GetNativeSkTypeface() const {
return SkMakeTypefaceFromCTFont(base::mac::NSToCFCast(GetNativeFont()));
}
// static
Weight PlatformFontMac::GetFontWeightFromNSFontForTesting(NSFont* font) {
return GetFontWeightFromNSFont(font);
}
////////////////////////////////////////////////////////////////////////////////
// PlatformFontMac, private:
PlatformFontMac::PlatformFontMac(const std::string& font_name,
int font_size,
int font_style,
Font::Weight font_weight)
PlatformFontMac::PlatformFontMac(
NativeFont font,
base::Optional<SystemFontType> system_font_type)
: 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_([ValidateFont(font) retain]),
font_name_(font_name),
font_size_(font_size),
font_style_(font_style),
font_weight_(font_weight) {
font,
system_font_type,
{base::SysNSStringToUTF8([font familyName]), [font pointSize],
GetFontStyleFromNSFont(font), GetFontWeightFromNSFont(font)}) {}
PlatformFontMac::PlatformFontMac(
NativeFont font,
base::Optional<SystemFontType> system_font_type,
FontSpec spec)
: native_font_([font retain]),
system_font_type_(system_font_type),
font_spec_(spec) {
#if DCHECK_IS_ON()
DCHECK(system_font_type.has_value() ||
SystemFontNames().count(spec.name) == 0)
<< "Do not pass a system font (" << spec.name << ") to PlatformFontMac; "
<< "use the SystemFontType constructor. Extend the SystemFontType enum "
<< "if necessary.";
#endif // DCHECK_IS_ON()
CalculateMetricsAndInitRenderParams();
}
......@@ -400,11 +504,71 @@ void PlatformFontMac::CalculateMetricsAndInitRenderParams() {
height_ = ceil(ascent_ + std::abs([font descender]) + [font leading]);
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);
query.families.push_back(font_spec_.name);
query.pixel_size = font_spec_.size;
query.style = font_spec_.style;
query.weight = font_spec_.weight;
render_params_ = gfx::GetFontRenderParams(query, nullptr);
}
NSFont* PlatformFontMac::NSFontWithSpec(FontSpec font_spec) const {
// One might think that a font descriptor with the NSFontWeightTrait/
// kCTFontWeightTrait trait could be used to look up a font with a specific
// weight. That doesn't work, though. You can ask a font for its weight, but
// you can't use weight to query for the font.
//
// The way that does work is to use the old-style integer weight API.
NSFontManager* font_manager = [NSFontManager sharedFontManager];
NSFontTraitMask traits = 0;
if (font_spec.style & Font::ITALIC)
traits |= NSItalicFontMask;
// The Mac doesn't support underline as a font trait, so just drop it.
// (Underlines must be added as an attribute on an NSAttributedString.) Do not
// add NSBoldFontMask here; if it is added then the weight parameter below
// will be ignored.
NSFont* font =
[font_manager fontWithFamily:base::SysUTF8ToNSString(font_spec.name)
traits:traits
weight:ToNSFontManagerWeight(font_spec.weight)
size:font_spec.size];
if (font)
return font;
// Make one fallback attempt by looking up via font name rather than font
// family name. With this API, the available granularity of font weight is
// bold/not-bold, but that's what's available.
NSFontSymbolicTraits trait_bits = 0;
if (font_spec.weight >= Weight::BOLD)
trait_bits |= NSFontBoldTrait;
if (font_spec.style & Font::ITALIC)
trait_bits |= NSFontItalicTrait;
NSDictionary* attrs = @{
NSFontNameAttribute : base::SysUTF8ToNSString(font_spec.name),
NSFontTraitsAttribute : @{NSFontSymbolicTrait : @(trait_bits)},
};
NSFontDescriptor* descriptor =
[NSFontDescriptor fontDescriptorWithFontAttributes:attrs];
font = [NSFont fontWithDescriptor:descriptor size:font_spec.size];
if (font)
return font;
// If that doesn't find a font, whip up a system font to stand in for the
// specified font.
if (@available(macOS 10.11, *)) {
font = [NSFont systemFontOfSize:font_spec.size
weight:ToNSFontWeight(font_spec.weight)];
return [font_manager convertFont:font toHaveTrait:traits];
} else {
font = [NSFont systemFontOfSize:font_spec.size];
if (font_spec.weight >= Weight::BOLD)
traits |= NSBoldFontMask;
return [font_manager convertFont:font toHaveTrait:traits];
}
}
////////////////////////////////////////////////////////////////////////////////
......@@ -412,12 +576,12 @@ void PlatformFontMac::CalculateMetricsAndInitRenderParams() {
// static
PlatformFont* PlatformFont::CreateDefault() {
return new PlatformFontMac;
return new PlatformFontMac(PlatformFontMac::SystemFontType::kGeneral);
}
// static
PlatformFont* PlatformFont::CreateFromNativeFont(NativeFont native_font) {
return new PlatformFontMac(ValidateFont(native_font));
return new PlatformFontMac(native_font);
}
// static
......
......@@ -7,292 +7,187 @@
#include <Cocoa/Cocoa.h>
#include <stddef.h>
#import "base/mac/foundation_util.h"
#include "base/mac/mac_util.h"
#include "base/mac/scoped_cftyperef.h"
#include "base/stl_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/font.h"
// TODO(tapted): Remove gfx:: prefixes.
namespace gfx {
using Weight = Font::Weight;
TEST(PlatformFontMacTest, DeriveFont) {
// Use a base font that support all traits.
gfx::Font base_font("Helvetica", 13);
// |weight_tri| is either -1, 0, or 1 meaning "light", "normal", or "bold".
auto CheckExpected = [](const Font& font, int weight_tri, bool isItalic) {
base::ScopedCFTypeRef<CFDictionaryRef> traits(
CTFontCopyTraits(base::mac::NSToCFCast(font.GetNativeFont())));
DCHECK(traits);
CFNumberRef cf_slant = base::mac::GetValueFromDictionary<CFNumberRef>(
traits, kCTFontSlantTrait);
CGFloat slant;
CFNumberGetValue(cf_slant, kCFNumberCGFloatType, &slant);
if (isItalic)
EXPECT_GT(slant, 0);
else
EXPECT_EQ(slant, 0);
CFNumberRef cf_weight = base::mac::GetValueFromDictionary<CFNumberRef>(
traits, kCTFontWeightTrait);
CGFloat weight;
CFNumberGetValue(cf_weight, kCFNumberCGFloatType, &weight);
if (weight_tri < 0)
EXPECT_LT(weight, 0);
else if (weight_tri == 0)
EXPECT_EQ(weight, 0);
else
EXPECT_GT(weight, 0);
};
// Bold
gfx::Font bold_font(
base_font.Derive(0, gfx::Font::NORMAL, gfx::Font::Weight::BOLD));
NSFontTraitMask traits = [[NSFontManager sharedFontManager]
traitsOfFont:bold_font.GetNativeFont()];
EXPECT_EQ(NSBoldFontMask, traits);
// Use a base font that support all traits.
Font base_font("Helvetica", 13);
{
SCOPED_TRACE("plain font");
CheckExpected(base_font, 0, false);
}
// Italic
gfx::Font italic_font(
base_font.Derive(0, gfx::Font::ITALIC, gfx::Font::Weight::NORMAL));
traits = [[NSFontManager sharedFontManager]
traitsOfFont:italic_font.GetNativeFont()];
EXPECT_EQ(NSItalicFontMask, traits);
Font italic_font(base_font.Derive(0, Font::ITALIC, Weight::NORMAL));
{
SCOPED_TRACE("italic font");
CheckExpected(italic_font, 0, true);
}
// Bold
Font bold_font(base_font.Derive(0, Font::NORMAL, Weight::BOLD));
{
SCOPED_TRACE("bold font");
CheckExpected(bold_font, 1, false);
}
// Bold italic
gfx::Font bold_italic_font(
base_font.Derive(0, gfx::Font::ITALIC, gfx::Font::Weight::BOLD));
traits = [[NSFontManager sharedFontManager]
traitsOfFont:bold_italic_font.GetNativeFont()];
EXPECT_EQ(static_cast<NSFontTraitMask>(NSBoldFontMask | NSItalicFontMask),
traits);
Font bold_italic_font(base_font.Derive(0, Font::ITALIC, Weight::BOLD));
{
SCOPED_TRACE("bold italic font");
CheckExpected(bold_italic_font, 1, true);
}
// Non-existent thin will return the closest weight, light
Font thin_font(base_font.Derive(0, Font::NORMAL, Weight::THIN));
{
SCOPED_TRACE("thin font");
CheckExpected(thin_font, -1, false);
}
// Non-existent black will return the closest weight, bold
Font black_font(base_font.Derive(0, Font::NORMAL, Weight::BLACK));
{
SCOPED_TRACE("black font");
CheckExpected(black_font, 1, false);
}
}
TEST(PlatformFontMacTest, DeriveFontUnderline) {
// Create a default font.
gfx::Font base_font;
Font base_font;
// Make the font underlined.
gfx::Font derived_font(base_font.Derive(
0, base_font.GetStyle() | gfx::Font::UNDERLINE, base_font.GetWeight()));
Font derived_font(base_font.Derive(0, base_font.GetStyle() | Font::UNDERLINE,
base_font.GetWeight()));
// Validate the derived font properties against its native font instance.
NSFontTraitMask traits = [[NSFontManager sharedFontManager]
traitsOfFont:derived_font.GetNativeFont()];
gfx::Font::Weight actual_weight = (traits & NSFontBoldTrait)
? gfx::Font::Weight::BOLD
: gfx::Font::Weight::NORMAL;
Weight actual_weight =
(traits & NSFontBoldTrait) ? Weight::BOLD : Weight::NORMAL;
int actual_style = gfx::Font::UNDERLINE;
int actual_style = Font::UNDERLINE;
if (traits & NSFontItalicTrait)
actual_style |= gfx::Font::ITALIC;
actual_style |= Font::ITALIC;
EXPECT_TRUE(derived_font.GetStyle() & gfx::Font::UNDERLINE);
EXPECT_TRUE(derived_font.GetStyle() & Font::UNDERLINE);
EXPECT_EQ(derived_font.GetStyle(), actual_style);
EXPECT_EQ(derived_font.GetWeight(), actual_weight);
}
// Tests internal methods for extracting gfx::Font properties from the
// Tests internal methods for extracting Font properties from the
// underlying CTFont representation.
TEST(PlatformFontMacTest, ConstructFromNativeFont) {
Font light_font([NSFont fontWithName:@"Helvetica-Light" size:12]);
EXPECT_EQ(12, light_font.GetFontSize());
EXPECT_EQ("Helvetica", light_font.GetFontName());
EXPECT_EQ(Font::NORMAL, light_font.GetStyle());
EXPECT_EQ(Weight::LIGHT, light_font.GetWeight());
Font light_italic_font([NSFont fontWithName:@"Helvetica-LightOblique"
size:14]);
EXPECT_EQ(14, light_italic_font.GetFontSize());
EXPECT_EQ("Helvetica", light_italic_font.GetFontName());
EXPECT_EQ(Font::ITALIC, light_italic_font.GetStyle());
EXPECT_EQ(Weight::LIGHT, light_italic_font.GetWeight());
Font normal_font([NSFont fontWithName:@"Helvetica" size:12]);
EXPECT_EQ(12, normal_font.GetFontSize());
EXPECT_EQ("Helvetica", normal_font.GetFontName());
EXPECT_EQ(Font::NORMAL, normal_font.GetStyle());
EXPECT_EQ(Font::Weight::NORMAL, normal_font.GetWeight());
Font bold_font([NSFont fontWithName:@"Helvetica-Bold" size:14]);
EXPECT_EQ(14, bold_font.GetFontSize());
EXPECT_EQ("Helvetica", bold_font.GetFontName());
EXPECT_EQ(Font::NORMAL, bold_font.GetStyle());
EXPECT_EQ(Font::Weight::BOLD, bold_font.GetWeight());
EXPECT_EQ(Weight::NORMAL, normal_font.GetWeight());
Font italic_font([NSFont fontWithName:@"Helvetica-Oblique" size:14]);
EXPECT_EQ(14, italic_font.GetFontSize());
EXPECT_EQ("Helvetica", italic_font.GetFontName());
EXPECT_EQ(Font::ITALIC, italic_font.GetStyle());
EXPECT_EQ(Font::Weight::NORMAL, italic_font.GetWeight());
EXPECT_EQ(Weight::NORMAL, italic_font.GetWeight());
Font bold_font([NSFont fontWithName:@"Helvetica-Bold" size:12]);
EXPECT_EQ(12, bold_font.GetFontSize());
EXPECT_EQ("Helvetica", bold_font.GetFontName());
EXPECT_EQ(Font::NORMAL, bold_font.GetStyle());
EXPECT_EQ(Weight::BOLD, bold_font.GetWeight());
Font bold_italic_font([NSFont fontWithName:@"Helvetica-BoldOblique" size:14]);
EXPECT_EQ(14, bold_italic_font.GetFontSize());
EXPECT_EQ("Helvetica", bold_italic_font.GetFontName());
EXPECT_EQ(Font::ITALIC, bold_italic_font.GetStyle());
EXPECT_EQ(Font::Weight::BOLD, bold_italic_font.GetWeight());
}
// Specific test for the mapping from the NSFont weight API to gfx::Font::Weight
// values.
TEST(PlatformFontMacTest, FontWeightAPIConsistency) {
// Vanilla Helvetica only has bold and normal, so use a system font.
NSFont* ns_font = [NSFont systemFontOfSize:13];
NSFontManager* manager = [NSFontManager sharedFontManager];
// -[NSFontManager convertWeight:ofFont] supposedly steps the font up and down
// in weight values according to a table at
// https://developer.apple.com/reference/appkit/nsfontmanager/1462321-convertweight
// Apple Terminology | ISO Equivalent
// 1. ultralight | none
// 2. thin | W1. ultralight
// 3. light, extralight | W2. extralight
// 4. book | W3. light
// 5. regular, plain, display, roman | W4. semilight
// 6. medium | W5. medium
// 7. demi, demibold | none
// 8. semi, semibold | W6. semibold
// 9. bold | W7. bold
// 10. extra, extrabold | W8. extrabold
// 11. heavy, heavyface | none
// 12. black, super | W9. ultrabold
// 13. ultra, ultrablack, fat | none
// 14. extrablack, obese, nord | none
EXPECT_EQ(Font::Weight::NORMAL, Font(ns_font).GetWeight()); // Row 5.
// Ensure the Bold "symbolic" trait from the NSFont traits API maps correctly
// to the weight (non-symbolic) trait from the CTFont API.
NSFont* bold_ns_font =
[manager convertFont:ns_font toHaveTrait:NSFontBoldTrait];
Font bold_font(bold_ns_font);
EXPECT_EQ(Font::Weight::BOLD, bold_font.GetWeight());
// No thin fonts on the lower rows of the table for San Francisco or earlier
// system fonts.
BOOL down = NO;
ns_font = [NSFont systemFontOfSize:13];
for (int row = 4; row > 0; --row) {
SCOPED_TRACE(testing::Message() << "Row: " << row);
ns_font = [manager convertWeight:down ofFont:ns_font];
EXPECT_EQ(Font::Weight::NORMAL, Font(ns_font).GetWeight());
}
BOOL up = YES;
// That is... unless we first go up by one and then down. A LIGHT and a THIN
// font reveal themselves somehow. Only tested on 10.12.
if (base::mac::IsAtLeastOS10_12()) {
ns_font = [NSFont systemFontOfSize:13];
ns_font = [manager convertWeight:up ofFont:ns_font];
ns_font = [manager convertWeight:down ofFont:ns_font];
EXPECT_EQ(Font::Weight::LIGHT, Font(ns_font).GetWeight());
ns_font = [manager convertWeight:down ofFont:ns_font];
EXPECT_EQ(Font::Weight::THIN, Font(ns_font).GetWeight());
}
ns_font = [NSFont systemFontOfSize:13];
if (base::mac::IsOS10_11()) {
// On 10.11 the API jumps to BOLD, but has heavier weights as well.
ns_font = [manager convertWeight:up ofFont:ns_font];
EXPECT_EQ(Font::Weight::BOLD, Font(ns_font).GetWeight());
ns_font = [manager convertWeight:up ofFont:ns_font];
EXPECT_EQ(Font::Weight::EXTRA_BOLD, Font(ns_font).GetWeight());
ns_font = [manager convertWeight:up ofFont:ns_font];
EXPECT_EQ(Font::Weight::BLACK, Font(ns_font).GetWeight());
return;
}
// Each typeface maps weight notches differently, and the weight is actually a
// floating point value that may not map directly to a gfx::Font::Weight. For
// example San Francisco on macOS 10.12 goes up from 0 in the sequence: [0.23,
// 0.23, 0.3, 0.4, 0.56, 0.62, 0.62, ...] and has no "thin" weights. But also
// iterating over weights does weird stuff sometimes - before macOS 10.15,
// occasionally the font goes italic, but going up another step goes back to
// non-italic, at a heavier weight.
// NSCTFontUIUsageAttribute = CTFontMediumUsage.
ns_font = [manager convertWeight:up ofFont:ns_font]; // 0.23.
EXPECT_EQ(Font::Weight::MEDIUM, Font(ns_font).GetWeight()); // Row 6.
// 10.15 fixed the bug where the step up from medium created a medium italic.
if (base::mac::IsAtMostOS10_14()) {
// Goes italic: NSCTFontUIUsageAttribute = CTFontMediumItalicUsage.
ns_font = [manager convertWeight:up ofFont:ns_font]; // 0.23.
EXPECT_EQ(Font::Weight::MEDIUM, Font(ns_font).GetWeight()); // Row 7.
}
// NSCTFontUIUsageAttribute = CTFontDemiUsage.
ns_font = [manager convertWeight:up ofFont:ns_font]; // 0.3.
if (base::mac::IsOS10_10()) {
// 10.10 is Helvetica Neue. It only has NORMAL, MEDIUM, BOLD and BLACK.
EXPECT_EQ(Font::Weight::BOLD, Font(ns_font).GetWeight()); // Row 8.
} else {
EXPECT_EQ(Font::Weight::SEMIBOLD, Font(ns_font).GetWeight()); // Row 8.
}
// NSCTFontUIUsageAttribute = CTFontEmphasizedUsage.
ns_font = [manager convertWeight:up ofFont:ns_font]; // 0.4 on 10.11+.
if (base::mac::IsOS10_10()) {
// Remaining rows are all BLACK on 10.10.
for (int row = 9; row <= 14; ++row) {
SCOPED_TRACE(testing::Message() << "Row: " << row);
ns_font = [manager convertWeight:up ofFont:ns_font];
EXPECT_EQ(Font::Weight::BLACK, Font(ns_font).GetWeight());
}
return;
}
EXPECT_EQ(Font::Weight::BOLD, Font(ns_font).GetWeight()); // Row 9.
// NSCTFontUIUsageAttribute = CTFontHeavyUsage.
ns_font = [manager convertWeight:up ofFont:ns_font]; // 0.56.
EXPECT_EQ(Font::Weight::EXTRA_BOLD, Font(ns_font).GetWeight()); // Row 10.
// NSCTFontUIUsageAttribute = CTFontBlackUsage.
ns_font = [manager convertWeight:up ofFont:ns_font]; // 0.62.
EXPECT_EQ(Font::Weight::BLACK, Font(ns_font).GetWeight()); // Row 11.
ns_font = [manager convertWeight:up ofFont:ns_font]; // 0.62.
EXPECT_EQ(Font::Weight::BLACK, Font(ns_font).GetWeight()); // Row 12.
ns_font = [manager convertWeight:up ofFont:ns_font]; // 0.62.
EXPECT_EQ(Font::Weight::BLACK, Font(ns_font).GetWeight()); // Row 13.
ns_font = [manager convertWeight:up ofFont:ns_font]; // 0.62.
EXPECT_EQ(Font::Weight::BLACK, Font(ns_font).GetWeight()); // Row 14.
EXPECT_EQ(Weight::BOLD, bold_italic_font.GetWeight());
}
// Test font derivation for fine-grained font weights.
TEST(PlatformFontMacTest, DerivedFineGrainedFonts) {
// Only test where San Francisco is available.
if (base::mac::IsAtMostOS10_10())
return;
using Weight = Font::Weight;
// The resulting, actual font weight after deriving |weight| from |base|.
auto DerivedIntWeight = [](const Font& base, Weight weight) {
auto DerivedIntWeight = [](Weight weight) {
Font base; // The default system font.
Font derived(base.Derive(0, 0, weight));
// PlatformFont should always pass the requested weight, not what the OS
// could provide. This just checks a constructor argument, so not very
// interesting.
EXPECT_EQ(static_cast<int>(weight), static_cast<int>(derived.GetWeight()));
// Return the weight int value that PlatformFontMac internally derives from
// the floating point weight given by the kCTFontWeightTrait of |font|. Do
// this by creating a new font based only off the NSFont in |derived|.
return static_cast<int>(Font(derived.GetNativeFont()).GetWeight());
return static_cast<int>(PlatformFontMac::GetFontWeightFromNSFontForTesting(
derived.GetNativeFont()));
};
// Only use NORMAL or BOLD as a base font. Mac font APIs go wacky otherwise.
// See comments in PlatformFontMac::DeriveFont().
Font base([NSFont systemFontOfSize:13]);
for (Weight base_weight : {Weight::NORMAL, Weight::BOLD}) {
SCOPED_TRACE(testing::Message()
<< "BaseWeight: " << static_cast<int>(base_weight));
if (base_weight != Weight::NORMAL) {
base = base.Derive(0, 0, base_weight);
EXPECT_EQ(static_cast<int>(base_weight),
static_cast<int>(base.GetWeight()));
}
// Normal and heavy weights map correctly on 10.11 and 10.12.
EXPECT_EQ(static_cast<int>(Weight::NORMAL),
DerivedIntWeight(base, Weight::NORMAL));
EXPECT_EQ(static_cast<int>(Weight::BOLD),
DerivedIntWeight(base, Weight::BOLD));
EXPECT_EQ(static_cast<int>(Weight::EXTRA_BOLD),
DerivedIntWeight(base, Weight::EXTRA_BOLD));
EXPECT_EQ(static_cast<int>(Weight::BLACK),
DerivedIntWeight(base, Weight::BLACK));
if (base::mac::IsAtMostOS10_11()) {
// The fine-grained font weights on 10.11 are incomplete.
EXPECT_EQ(static_cast<int>(Weight::NORMAL),
DerivedIntWeight(base, Weight::THIN));
EXPECT_EQ(static_cast<int>(Weight::NORMAL),
DerivedIntWeight(base, Weight::EXTRA_LIGHT));
EXPECT_EQ(static_cast<int>(Weight::NORMAL),
DerivedIntWeight(base, Weight::LIGHT));
EXPECT_EQ(static_cast<int>(Weight::BOLD),
DerivedIntWeight(base, Weight::MEDIUM));
EXPECT_EQ(static_cast<int>(Weight::BOLD),
DerivedIntWeight(base, Weight::SEMIBOLD));
continue;
}
// San Francisco doesn't offer anything between THIN and LIGHT.
EXPECT_EQ(static_cast<int>(Weight::THIN),
DerivedIntWeight(base, Weight::EXTRA_LIGHT));
// All the rest should map correctly.
EXPECT_EQ(static_cast<int>(Weight::THIN),
DerivedIntWeight(base, Weight::THIN));
EXPECT_EQ(static_cast<int>(Weight::LIGHT),
DerivedIntWeight(base, Weight::LIGHT));
EXPECT_EQ(static_cast<int>(Weight::THIN), DerivedIntWeight(Weight::THIN));
EXPECT_EQ(static_cast<int>(Weight::EXTRA_LIGHT),
DerivedIntWeight(Weight::EXTRA_LIGHT));
EXPECT_EQ(static_cast<int>(Weight::LIGHT), DerivedIntWeight(Weight::LIGHT));
EXPECT_EQ(static_cast<int>(Weight::NORMAL), DerivedIntWeight(Weight::NORMAL));
EXPECT_EQ(static_cast<int>(Weight::MEDIUM), DerivedIntWeight(Weight::MEDIUM));
if (base::mac::IsAtMostOS10_10()) {
// If a SEMIBOLD system font is requested, 10.10 will return the bold system
// font, but somehow bearing a weight number of 0.24, which is really a
// medium weight (0.23).
EXPECT_EQ(static_cast<int>(Weight::MEDIUM),
DerivedIntWeight(base, Weight::MEDIUM));
DerivedIntWeight(Weight::SEMIBOLD));
} else {
EXPECT_EQ(static_cast<int>(Weight::SEMIBOLD),
DerivedIntWeight(base, Weight::SEMIBOLD));
DerivedIntWeight(Weight::SEMIBOLD));
}
EXPECT_EQ(static_cast<int>(Weight::BOLD), DerivedIntWeight(Weight::BOLD));
EXPECT_EQ(static_cast<int>(Weight::EXTRA_BOLD),
DerivedIntWeight(Weight::EXTRA_BOLD));
EXPECT_EQ(static_cast<int>(Weight::BLACK), DerivedIntWeight(Weight::BLACK));
}
// Ensures that the Font's reported height is consistent with the native font's
......@@ -300,16 +195,14 @@ TEST(PlatformFontMacTest, DerivedFineGrainedFonts) {
TEST(PlatformFontMacTest, ValidateFontHeight) {
// Use the default ResourceBundle system font. E.g. Helvetica Neue in 10.10,
// Lucida Grande before that, and San Francisco after.
gfx::Font default_font;
gfx::Font::FontStyle styles[] = {gfx::Font::NORMAL, gfx::Font::ITALIC,
gfx::Font::UNDERLINE};
Font default_font;
Font::FontStyle styles[] = {Font::NORMAL, Font::ITALIC, Font::UNDERLINE};
for (size_t i = 0; i < base::size(styles); ++i) {
SCOPED_TRACE(testing::Message() << "Font::FontStyle: " << styles[i]);
// Include the range of sizes used by ResourceBundle::FontStyle (-1 to +8).
for (int delta = -1; delta <= 8; ++delta) {
gfx::Font font =
default_font.Derive(delta, styles[i], gfx::Font::Weight::NORMAL);
Font font = default_font.Derive(delta, styles[i], Weight::NORMAL);
SCOPED_TRACE(testing::Message() << "FontSize(): " << font.GetFontSize());
NSFont* native_font = font.GetNativeFont();
......@@ -343,15 +236,14 @@ TEST(PlatformFontMacTest, ValidateFontHeight) {
// Appkit's bug was detected on macOS 10.10 which uses Helvetica Neue as the
// system font.
TEST(PlatformFontMacTest, DerivedSemiboldFontIsNotItalic) {
gfx::Font base_font;
Font base_font;
{
NSFontTraitMask traits = [[NSFontManager sharedFontManager]
traitsOfFont:base_font.GetNativeFont()];
ASSERT_FALSE(traits & NSItalicFontMask);
}
gfx::Font semibold_font(
base_font.Derive(0, gfx::Font::NORMAL, gfx::Font::Weight::SEMIBOLD));
Font semibold_font(base_font.Derive(0, Font::NORMAL, Weight::SEMIBOLD));
NSFontTraitMask traits = [[NSFontManager sharedFontManager]
traitsOfFont:semibold_font.GetNativeFont()];
EXPECT_FALSE(traits & NSItalicFontMask);
......
......@@ -10,6 +10,7 @@
#include "ui/base/cocoa/cocoa_base_utils.h"
#include "ui/gfx/font_list.h"
#import "ui/gfx/mac/coordinate_conversion.h"
#include "ui/gfx/platform_font_mac.h"
namespace {
......@@ -32,8 +33,10 @@ int TooltipManagerMac::GetMaxWidth(const gfx::Point& location) const {
}
const gfx::FontList& TooltipManagerMac::GetFontList() const {
static base::NoDestructor<gfx::FontList> font_list(
[]() { return gfx::Font([NSFont toolTipsFontOfSize:0]); }());
static base::NoDestructor<gfx::FontList> font_list([]() {
return gfx::Font(new gfx::PlatformFontMac(
gfx::PlatformFontMac::SystemFontType::kToolTip));
}());
return *font_list;
}
......
......@@ -7,6 +7,7 @@
#import <AppKit/AppKit.h>
#include "base/mac/mac_util.h"
#include "ui/gfx/platform_font_mac.h"
namespace {
......@@ -42,7 +43,8 @@ void InitMaterialMenuConfig(views::MenuConfig* config) {
namespace views {
void MenuConfig::Init() {
font_list = gfx::FontList(gfx::Font([NSFont menuFontOfSize:0.0]));
font_list = gfx::FontList(gfx::Font(
new gfx::PlatformFontMac(gfx::PlatformFontMac::SystemFontType::kMenu)));
check_selected_combobox_item = true;
arrow_key_selection_wraps = false;
use_mnemonics = false;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment