Skip to content

Instantly share code, notes, and snippets.

@Norod
Last active August 29, 2015 13:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Norod/9392911 to your computer and use it in GitHub Desktop.
Save Norod/9392911 to your computer and use it in GitHub Desktop.
Demonstrate how to load an OTF file, change some if its internal values in-memory (Ascender, Descender, LineGap), register it and then create a UIFont object
#import <CoreText/CoreText.h>
#define kOTFhorizontalHeaderTableRecord_tag htonl((int32_t)('hhea'))
typedef struct otfTableRecordStruct
{
int32_t tag;
int32_t checksum;
int32_t offset;
int32_t length;
}otfTableRecord, *otfTableRecordPTR;
typedef struct otfHorizontalHeaderTableStruct
{
int32_t version;
int16_t ascender;
int16_t descender;
int16_t lineGap;
int16_t advancedWidthMax;
}otfHorizontalHeaderTable, *otfHorizontalHeaderTablePTR;
@interface OTFModifier ()
@property(nonatomic, strong) NSString* fontPostScriptName;
@property(nonatomic, strong) UIFont* uiFont;
@end
@implementation OTFModifier
CGFontRef cgFont;
- (void)dealloc
{
[self unregisterOTF];
}
#pragma mark - OTFun
- (BOOL)loadOTF:(NSURL*)otfFileURL withModifyAscender:(int)modifiedAscender modifiedDescender:(int)modifiedDescender modifiedLineGap:(int)modifiedLineGap
{
[self unregisterOTF];
NSData* otfData = [NSData dataWithContentsOfURL:otfFileURL];
const char* pRawDataBuffer = (const char*)[otfData bytes];
char* pCurOffset = ((char*)pRawDataBuffer) + 0x04; //Offset where the value that specifies the number of "Table Records" reside
int16_t numTables = htons(*((int16_t*)(pCurOffset)));
NSLog(@"numTables = %d", (int)numTables);
otfHorizontalHeaderTablePTR pHorizontalHeaderTable = NULL;
pCurOffset = ((char*)pRawDataBuffer) + 0x0C; //Offset where the list of "Table Records" begin
for (int16_t curTableIndex = 0; curTableIndex < numTables; ++curTableIndex)
{
otfTableRecordPTR pTableRecord = (otfTableRecordPTR)pCurOffset;
if (pTableRecord->tag == kOTFhorizontalHeaderTableRecord_tag)
{
NSLog(@"\nFound the Horizontal Header Table Record (tag=0x%x -> 'hhea')", (pTableRecord->tag));
NSLog(@"Loading Horizontal Header Table (offset=0x%x)\n", htonl(pTableRecord->offset));
pHorizontalHeaderTable = ((otfHorizontalHeaderTablePTR)(((char*)pRawDataBuffer) + htonl(pTableRecord->offset)));
}
else
{
NSLog(@"Skipping Header Table Record (tag=0x%x -> '%c%c%c%c')",
(pTableRecord->tag),
((char*)(&(pTableRecord->tag)))[0],
((char*)(&(pTableRecord->tag)))[1],
((char*)(&(pTableRecord->tag)))[2],
((char*)(&(pTableRecord->tag)))[3]
);
}
pCurOffset+= sizeof(otfTableRecord); //+=16
}
if (pHorizontalHeaderTable == NULL)
{
NSLog(@"Sorry, I looked and I looked but I could not find the Horizontal Header Table Record :(");
return NO;
}
NSLog(@"\nHorizontal Header Table Record:");
NSLog(@"version\t=\t0x%x",(int32_t)htonl(pHorizontalHeaderTable->version & 0xFFFFFFFF));
NSLog(@"ascender\t=\t%hi",(int16_t)htons(pHorizontalHeaderTable->ascender & 0x0000FFFF));
NSLog(@"descender\t=\t%hi",(int16_t)htons(pHorizontalHeaderTable->descender & 0x0000FFFF));
NSLog(@"lineGap\t=\t%hi",(int16_t)htons(pHorizontalHeaderTable->lineGap & 0x0000FFFF));
NSLog(@"advancedWidthMax\t=\t%hi",(int16_t)htons(pHorizontalHeaderTable->advancedWidthMax & 0x0000FFFF));
NSLog(@"\n");
NSLog(@"Patching \"ascender\" value to \"%d\"", (int)modifiedAscender);
pHorizontalHeaderTable->ascender = (int16_t)htons(modifiedAscender);
NSLog(@"Patching \"descender\" value to \"%d\"", (int)modifiedDescender);
pHorizontalHeaderTable->descender = (int16_t)htons(modifiedDescender);
NSLog(@"Patching \"lineGap\" value to \"%d\"", (int)modifiedLineGap);
pHorizontalHeaderTable->lineGap = (int16_t)htons(modifiedLineGap);
NSLog(@"Done patching\n");
NSLog(@"Creating Core-Graphics Font object");
CFErrorRef error;
CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)otfData);
cgFont = CGFontCreateWithDataProvider(provider);
if (cgFont == NULL)
{
NSLog(@"Failed :(");
CFRelease(provider);
return NO;
}
NSLog(@"Done\n");
CFStringRef cfFontPostScriptName = CGFontCopyPostScriptName(cgFont);
self.fontPostScriptName = CFBridgingRelease(cfFontPostScriptName);
if ([self.fontPostScriptName length] > 0)
{
self.title = self.fontPostScriptName;
}
NSLog(@"Registering the font \"%@\"", self.fontPostScriptName);
if (! CTFontManagerRegisterGraphicsFont(cgFont, &error)) {
CFStringRef errorDescription = CFErrorCopyDescription(error);
NSLog(@"Failed to register font: %@", errorDescription);
CFRelease(errorDescription);
}
CFRelease(provider);
NSLog(@"Done\n");
NSLog(@"Creating a UIFont object");
self.uiFont = [UIFont fontWithName:self.fontPostScriptName size:14.0f];
if (self.uiFont == nil)
{
NSLog(@"Failed :(");
return NO;
}
NSLog(@"Done\n");
//Test :)
self.uiLabel.font = self.uiFont;
self.uiTextField.font = self.uiFont;
self.uiButton.titleLabel.font = self.uiFont;
self.uiButton.titleLabel.text = [NSString stringWithFormat:@"%@", self.uiButton.titleLabel.text];
self.uiTextView.font = self.uiFont;
return YES;
}
- (BOOL)unregisterOTF
{
if (cgFont)
{
NSLog(@"Unregistering the font");
CFErrorRef error;
if (!CTFontManagerUnregisterGraphicsFont(cgFont, &error)) {
CFStringRef errorDescription = CFErrorCopyDescription(error);
NSLog(@"Failed to unregister font: %@", errorDescription);
CFRelease(errorDescription);
return NO;
}
else
{
NSLog(@"Done\n");
}
CGFontRelease(cgFont);
cgFont = NULL;
}
return YES;
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment