YYKit源码探究(十二) —— UIFont分类之Dump font data(四)

版本记录

版本号 时间
V1.0 2018.03.23

前言

iOS圈内有几个人大家基本都知道,比如说王巍、唐巧,还有YYKit框架的作者现任职于滴滴的郭曜源 - ibireme等。这里有一篇唐巧对他的专访,还有他的 GitHub - Yaoyuan博客,这里贴出来框架YYKit 框架。接下来几篇我们就一起来看一下这个框架。感兴趣的可以看上面写的几篇。
1. YYKit源码探究(一) —— 基本概览
2. YYKit源码探究(二) —— NSString分类之Hash(一)
3. YYKit源码探究(三) —— NSString分类之Encode and decode(二)
4. YYKit源码探究(四) —— NSString分类之Drawing(三)
5. YYKit源码探究(五) —— NSString分类之Regular Expression(四)
6. YYKit源码探究(六) —— NSString分类之NSNumber Compatible(五)
7. YYKit源码探究(七) —— NSString分类之Utilities(六)
8. YYKit源码探究(八) —— NSNumber分类(一)
9. YYKit源码探究(九) —— UIFont分类之架构分析和Font Traits(一)
10. YYKit源码探究(十) —— UIFont分类之Create font(二)
11. YYKit源码探究(十一) —— UIFont分类之Load and unload font(三)

回顾

上一篇主要介绍了Load and unload font的说明,这一篇我们就看一下Dump font data部分。


API接口

下面我们看一下API接口。

/**
 Serialize and return the font data.
 
 @param font The font.
 
 @return data in TTF, or nil if an error occurs.
 */
+ (nullable NSData *)dataFromFont:(UIFont *)font;

/**
 Serialize and return the font data.
 
 @param cgFont The font.
 
 @return data in TTF, or nil if an error occurs.
 */
+ (nullable NSData *)dataFromCGFont:(CGFontRef)cgFont;

1. + (nullable NSData *)dataFromFont:(UIFont *)font;

该方法的作用就是根据UIFont对象,进行序列化,返回NSData类型的数据。

下面我们看一下实现方法。

+ (NSData *)dataFromFont:(UIFont *)font {
    CGFontRef cgFont = font.CGFontRef;
    NSData *data = [self dataFromCGFont:cgFont];
    CGFontRelease(cgFont);
    return data;
}
//Reference:
//https://github.com/google/skia/blob/master/src%2Fports%2FSkFontHost_mac.cpp

+ (NSData *)dataFromCGFont:(CGFontRef)cgFont {
    if (!cgFont) return nil;
    
    CFRetain(cgFont);
    
    CFArrayRef tags = CGFontCopyTableTags(cgFont);
    if (!tags) return nil;
    CFIndex 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 (CFIndex index = 0; index < tableCount; index++) {
        size_t tableSize = 0;
        uint32_t aTag = (uint32_t)CFArrayGetValueAtIndex(tags, index);
        if (aTag == kCTFontTableCFF && !containsCFFTable) {
            containsCFFTable = YES;
        }
        CFDataRef tableDataRef = CGFontCopyTableForTag(cgFont, aTag);
        if (tableDataRef) {
            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, (uint32_t)tableSize));
        
        uint32_t offset = (uint32_t)dataPtr - (uint32_t)dataStart;
        entry->fOffset = CFSwapInt32HostToBig((uint32_t)offset);
        entry->fLength = CFSwapInt32HostToBig((uint32_t)tableSize);
        dataPtr += (tableSize + 3) & ~3;
        ++entry;
        CFRelease(tableDataRef);
    }
    
    CFRelease(cgFont);
    CFRelease(tags);
    free(tableSizes);
    NSData *fontData = [NSData dataWithBytesNoCopy:stream length:totalSize freeWhenDone:YES];
    return fontData;
}

2. + (nullable NSData *)dataFromCGFont:(CGFontRef)cgFont;

该方法的作用就是根据CGFontRef返回一个NSData的数据类型。

这个方法的实现就在上面,就不多说了,这个也是作者借鉴Github上的例子。

后记

本篇主要介绍了Dump font data部分,感兴趣的给个赞或者关注吧~~~

推荐阅读更多精彩内容