Skip to content

Instantly share code, notes, and snippets.

@cxa
Forked from Bokugene/CGFontToFontData.m
Created May 5, 2012 15:06
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 cxa/2603139 to your computer and use it in GitHub Desktop.
Save cxa/2603139 to your computer and use it in GitHub Desktop.
Read Table Data from a CGFont, then wrap them into a OTF/TTF font.
typedef struct FontHeader {
int32_t fVersion;
uint16_t fNumTables;
uint16_t fSearchRange;
uint16_t fEntrySelector;
uint16_t fRangeShift;
}FontHeader;
typedef struct TableEntry {
uint32_t fTag;
uint32_t fCheckSum;
uint32_t fOffset;
uint32_t fLength;
}TableEntry;
static uint32_t CalcTableCheckSum(const uint32_t *table, uint32_t numberOfBytesInTable) {
uint32_t sum = 0;
uint32_t nLongs = (numberOfBytesInTable + 3) / 4;
while (nLongs-- > 0) {
sum += CFSwapInt32HostToBig(*table++);
}
return sum;
}
static uint32_t CalcTableDataRefCheckSum(CFDataRef dataRef) {
const uint32_t *dataBuff = (const uint32_t *)CFDataGetBytePtr(dataRef);
uint32_t dataLength = (uint32_t)CFDataGetLength(dataRef);
return CalcTableCheckSum(dataBuff, dataLength);
}
//Reference:
//http://skia.googlecode.com/svn-history/r1473/trunk/src/ports/SkFontHost_mac_coretext.cpp
- (NSData *)fontDataForCGFont:(CGFontRef)cgFont{
if (!cgFont) {
return nil;
}
CFRetain(cgFont);
CFArrayRef tags = CGFontCopyTableTags(cgFont);
int tableCount = CFArrayGetCount(tags);
size_t *tableSizes = malloc(sizeof(size_t) * tableCount);
memset(tableSizes, 0, sizeof(size_t) * tableCount);
BOOL containsCFFTable = NO;
size_t totalSize = sizeof(FontHeader) + sizeof(TableEntry) * tableCount;
for (int index = 0; index < tableCount; ++index) {
//get size
size_t tableSize = 0;
uint32_t aTag = (uint32_t)CFArrayGetValueAtIndex(tags, index);
if (aTag == 'CFF ' && !containsCFFTable) {
containsCFFTable = YES;
}
CFDataRef tableDataRef = CGFontCopyTableForTag(cgFont, aTag);
if (tableDataRef != NULL) {
tableSize = CFDataGetLength(tableDataRef);
CFRelease(tableDataRef);
}
totalSize += (tableSize + 3) & ~3;
tableSizes[index] = tableSize;
}
unsigned char *stream = malloc(totalSize);
memset(stream, 0, totalSize);
char* dataStart = (char*)stream;
char* dataPtr = dataStart;
// compute font header entries
uint16_t entrySelector = 0;
uint16_t searchRange = 1;
while (searchRange < tableCount >> 1) {
entrySelector++;
searchRange <<= 1;
}
searchRange <<= 4;
uint16_t rangeShift = (tableCount << 4) - searchRange;
// write font header (also called sfnt header, offset subtable)
FontHeader* offsetTable = (FontHeader*)dataPtr;
//OpenType Font contains CFF Table use 'OTTO' as version, and with .otf extension
//otherwise 0001 0000
offsetTable->fVersion = containsCFFTable ? 'OTTO' : CFSwapInt16HostToBig(1);
offsetTable->fNumTables = CFSwapInt16HostToBig((uint16_t)tableCount);
offsetTable->fSearchRange = CFSwapInt16HostToBig((uint16_t)searchRange);
offsetTable->fEntrySelector = CFSwapInt16HostToBig((uint16_t)entrySelector);
offsetTable->fRangeShift = CFSwapInt16HostToBig((uint16_t)rangeShift);
dataPtr += sizeof(FontHeader);
// write tables
TableEntry* entry = (TableEntry*)dataPtr;
dataPtr += sizeof(TableEntry) * tableCount;
for (int index = 0; index < tableCount; ++index) {
uint32_t aTag = (uint32_t)CFArrayGetValueAtIndex(tags, index);
CFDataRef tableDataRef = CGFontCopyTableForTag(cgFont, aTag);
size_t tableSize = CFDataGetLength(tableDataRef);
memcpy(dataPtr, CFDataGetBytePtr(tableDataRef), tableSize);
entry->fTag = CFSwapInt32HostToBig((uint32_t)aTag);
entry->fCheckSum = CFSwapInt32HostToBig(CalcTableCheckSum((uint32_t *)dataPtr, tableSize));
uint32_t offset = dataPtr - dataStart;
entry->fOffset = CFSwapInt32HostToBig((uint32_t)offset);
entry->fLength = CFSwapInt32HostToBig((uint32_t)tableSize);
dataPtr += (tableSize + 3) & ~3;
++entry;
CFRelease(tableDataRef);
}
CFRelease(cgFont);
free(tableSizes);
NSData *fontData = [NSData dataWithBytesNoCopy:stream
length:totalSize
freeWhenDone:YES];
return fontData;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment