Skip to content

Instantly share code, notes, and snippets.

@macprog-guy
Last active July 1, 2023 10:53
Show Gist options
  • Save macprog-guy/156d33bfefef570a7efb to your computer and use it in GitHub Desktop.
Save macprog-guy/156d33bfefef570a7efb to your computer and use it in GitHub Desktop.
NSFont Category to make it easy to add or remove traits.
A small category on NSFont to make it easy to manipulate fonts.
The header file is pretty self explanitory.
/**
\category NSFont(CFTraits)
\brief Adds a number of facilities to simplify working with fonts.
\details It's not always easy to get the bold or italic variant of a font
if it even exists. This category adds properties to NSFont that
indicate whether a font is bold, italic, condensed, monospaced
and more. Other properties can tell you if a bold or italic variant
exist. Finally, some methods return the bold or italic variations
of the font.
\author Eric Methot
\date 2012-04-10
\copyright Copyleft 2012 Eric Methot.
*/
#import <Cocoa/Cocoa.h>
@interface NSFont (CFTraits)
/// Returns YES if this font exists in a bold variant and NO otherwise.
@property (readonly) BOOL fontInFamilyExistsInBold;
/// Returns YES if this font exists in an italic variant and NO otherwise.
@property (readonly) BOOL fontInFamilyExistsInItalic;
/// Returns YES if this font is bold.
@property (readonly) BOOL isBold;
/// Returns YES if this font is italic.
@property (readonly) BOOL isItalic;
/// Returns YES if this font is condensed.
@property (readonly) BOOL isCondensed;
/// Returns YES if this font is expanded.
@property (readonly) BOOL isExpanded;
/// Returns YES if this font is monospaced.
@property (readonly) BOOL isMonospaced;
/// Returns YES if this font is vertical.
//@property (readonly) BOOL isVertical;
/// Returns YES if this font is UI optimized.
@property (readonly) BOOL isUIOptimized;
/// Returns the font size for this font instance.
@property (readonly) float fontSize;
/// Returns the bold variation of the font or the font itself
/// if such a variation does not exist.
@property (readonly) NSFont *fontVariationBold;
/// Returns the italic variation of the font or the font itself
/// if such a variation does not exist.
@property (readonly) NSFont *fontVariationItalic;
/// Returns the bold-italic variation of the font or the font itself
/// if such a variation does not exist.
@property (readonly) NSFont *fontVariationBoldItalic;
/// Returns the regular variation of the font or the font itself
/// if such a variation does not exist.
@property (readonly) NSFont *fontVariationRegular;
/// Returns the same font with new size.
- (NSFont*) fontVariationOfSize:(double)size;
/// Returns the value of the spc attribute in the fonts description.
@property (readonly) double spacing;
@end
/**
\category NSFont(CFTraits)
\author Eric Methot
\date 2012-04-10
\copyright Copyleft 2012 Eric Methot.
*/
#import "NSFont+CFTraits.h"
#import <objc/runtime.h>
/// Some traits are cached globally by fontName.
static NSMutableDictionary *gNSFontCachedCustomTraits = nil;
/// Some traits are
const uint64_t kNUFontTraitInFamilyExistsInBold = 0x0000000100000000ULL;
const uint64_t kNUFontTraitInFamilyExistsInItalic = 0x0000000200000000ULL;
@implementation NSFont (CFTraits)
- (BOOL) checkForCachedFontTrait:(NSUInteger)trait
{
// We use the cached traits if we have them.
NSNumber *cachedTraits = [gNSFontCachedCustomTraits objectForKey:self.fontName];
if (cachedTraits == nil) {
// We use the traits in the fontDescriptor
NSFontSymbolicTraits traits = self.fontDescriptor.symbolicTraits;
// Then we look at the other members of that font family to see if there are any bold or italic variations.
NSArray *members = [[NSFontManager sharedFontManager] availableMembersOfFontFamily:self.familyName];
uint64_t customTraits = traits;
for (NSArray *member in members) {
int memberTrait = [[member objectAtIndex:3] intValue];
if (memberTrait & NSBoldFontMask)
customTraits |= kNUFontTraitInFamilyExistsInBold;
if (memberTrait & NSItalicFontMask)
customTraits |= kNUFontTraitInFamilyExistsInItalic;
}
cachedTraits = @(customTraits);
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
gNSFontCachedCustomTraits = [NSMutableDictionary dictionaryWithCapacity:512];
});
@synchronized(gNSFontCachedCustomTraits) {
[gNSFontCachedCustomTraits setObject:cachedTraits forKey:self.fontName];
}
}
uint64_t flag = cachedTraits.unsignedIntegerValue & trait;
return (BOOL)(flag != 0);
}
- (BOOL) fontInFamilyExistsInBold
{
return [self checkForCachedFontTrait:kNUFontTraitInFamilyExistsInBold];
}
- (BOOL) fontInFamilyExistsInItalic
{
return [self checkForCachedFontTrait:kNUFontTraitInFamilyExistsInItalic];
}
- (BOOL) isBold
{
return [self checkForCachedFontTrait:NSFontBoldTrait];
}
- (BOOL) isItalic
{
return [self checkForCachedFontTrait:NSFontItalicTrait];
}
- (BOOL) isCondensed
{
return [self checkForCachedFontTrait:NSFontCondensedTrait];
}
- (BOOL) isExpanded
{
return [self checkForCachedFontTrait:NSFontExpandedTrait];
}
- (BOOL) isMonospaced
{
return [self checkForCachedFontTrait:NSFontMonoSpaceTrait];
}
//- (BOOL) isVertical
//{
// return [self checkForCachedFontTrait:NSFontVerticalTrait];
//}
- (BOOL) isUIOptimized
{
return [self checkForCachedFontTrait:NSFontUIOptimizedTrait];
}
- (float) fontSize
{
return [[self.fontDescriptor objectForKey:NSFontSizeAttribute] floatValue];
}
- (NSFont*) fontVariationWithTraits:(NSFontTraitMask)traitMask
{
NSFontManager *fontManager = [NSFontManager sharedFontManager];
return [fontManager convertFont:self toHaveTrait:traitMask];
}
- (NSFont*) fontVariationBold
{
return [self fontVariationWithTraits:NSBoldFontMask];
}
- (NSFont*) fontVariationItalic
{
return [self fontVariationWithTraits:NSItalicFontMask];
}
- (NSFont*) fontVariationBoldItalic
{
return [self fontVariationWithTraits:NSBoldFontMask | NSItalicFontMask];
}
- (NSFont*) fontVariationRegular
{
return [self fontVariationWithTraits:NSUnboldFontMask | NSUnitalicFontMask];
}
- (NSFont*) fontVariationOfSize:(double)size
{
return [NSFont fontWithName:self.fontName size:size];
}
- (double) spacing
{
NSString *descr = self.description;
NSRange range = [descr rangeOfString:@"spc="];
return [descr substringFromIndex:range.location + range.length].doubleValue;
}
@end
@LindaDisham
Copy link

looks like this fails on font family "Arial Black" when calling " fontInFamilyExistsInItalic"
"Arial Black" does not exists in Italic trait. Font book says "Arial Black" has only normal trait.
checkForCachedFontTrait gets a cached trait of 0x380000002
which is tested against trait kNUFontTraitInFamilyExistsInItalic = 0x0000000200000000ULL;
resulting flag = YES which looks wrong to me..

could some one explain what is wrong in my experience
Thanks to all
LD

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment