ttc的头文件部分
伪代码结构体:
typedef struct tag_TTC_Header
{
char tag[4]; //"ttcf"
uint16 ver_h; //1/2
uint16 ver_l; //0
uint32 directoryCount; //N
uint32 directorys[directoryCount]; //N*4bytes
//Sig_Object here (option)
}TTC_Header;
typedef struct tag_Sig_Object
{
char tag[4];
uint32 length;
uint32 offset;
}Sig_Object;
解释一下:
这里用到的数据类都是大端(Big Endian)写入;如ver_h(uint16)=1写出为 00 01;
directoryCount(uint32)=4写出是 00 00 00 04
tag[4]固定是“ttcf”
ver_h通常是1或者2;版本高位=1通常是旧版的,可以没有签字(Sig_Object);但是也不绝对,我看到simsun.ttc就是这个字节=1,但是也有Sig_Object结构。版本高位=2通常都是有Sig_Object结构(微软上的解释等于2时包括Sig_Object部分)
ver_l通常为0;版本低位;
上述的版本描述不是字体自身的版本,它是ttc结构的描述,所以暂时我理解是1可以随便使用,2是组织机构那些有签字的使用
directoryCount是描述了这个ttc文件包含多少个字体(介绍里面说不一定都是ttf,但是我这里默认只包含ttf,后面的描述也是这样)
directorys[]是一个文件偏量的数组;例如第一个字体在ttc文件上的偏移是0x40,写入为 00 00 00 40 ;第二个字体的偏移是0x00054438,写入为 00 05 44 38;。。。同样看出来都是大端写入的,对于小端读写编码会麻烦一点点。
写完了文件偏移量后,如果有签名就将sig这个结构填上,重复说一遍,大端写入!签名要注意了,数据块的尺寸放在tag后面的,呵呵,不要混乱了。
接下来是ttf的结构,ttc也是有的,其中offset table是一样的,font table的偏移量是会变化的
typedef struct tag_Offset_Table
{
char tag[4]; //00 01 00 00 or 'OTTO'
uint16 numTables; //x font table
uint16 searchRange; //256 (Maximum power of 2 <= numTables) x 16.
uint16 entrySelector; //4 Log2(maximum power of 2 <= numTables).
uint16 rangeShift; //48 NumTables x 16-searchRange.
}Offset_Table;
tag 通常描述版本1.0还是2.0,如果是OTTO表示OpenType字体包含CFF数据(知道CFF是一个表名就可以了,暂时不深挖);不管是解包TTC还是打包TTC,不用管Offset Table的信息,直接复制就好,当然必须读出numTables的大小。
暂时不论对错,先写下个人的理解,如下:
searchRange= t_numTables*sizeof(Font_Table) = t_numTables*16;
其中t_numTables有限制是[2,16],也就是说searchRange的范围是32到256;
t_numTables = numTables>16?16:numTables;
t_numTables = t_numTables<2?0:t_numTables; //0代表失败
entrySelector=(int)log2(t_numTables); 它的范围[1,4];
rangeShift是numTables*sizeof(Font_Table)-searchRange = numTables*16-searchRange;如果numTables在2到16的范围,那么这里rangeShift=0;当numTables>16时,rangeShift为正数。
typedef struct tag_Font_Table
{
char tag[4];
uint32 checkSum;
uint32 offset;
uint32 length;
}Font_Table;
根据Offset Table得到numTables;这里可以知道需要读取sizeof(Font_Table)* numTables个字节,然后一个个对应填入;从而获得每一个font table对应的偏移和大小;
tag有很多,例如“name”代表字体名字,“cmap”是字符映射表等等,解包和打包TTC只需要改offset这个字节,其他的不用管。
Font_Table成员的理解:
tag:标签,表示。
checkSum:很简单地对数据字节加校验;类似这样的操作:
int sum=0;
for(int i=0;i { sum += data[i]; } 具体会不会重复,那就看运气了,对单个文件这个重复的几率不大,言尽于此。 offset:当前文件的偏移量,也就是读数据的开始位置 length:数据的大小,字面意思,没什么好解析的。 对于解包TTC,每一个单独的TTF都是有Offset table+N*font table组成;因为ttf初始偏移为0;那么就要将对应font table对应的offset根据ttf文件写入量进行修改。 对于打包TTC,要根据ttc写入量对font table的offset进行修改。如果要做数据共用的话,要先计算data的checkSum,能算一下md5更准确,然后将已经写入的包和checkSum/Md5做一个表查询,如果遇到相同的数据,则将对应font table的offset改到相应的值,而不需要重复写入。(这个有点麻烦,我没写) 以下是一些资料 关于TTF的翻译文件(工程上传GitHub之后发现的,中文描述比较全):htt-ps://github.com/luckyaibin/truetypefontparsetoy/blob/master/truetypefont.org 微软关于字体的描述:htt-ps://docs.microsoft.com/zh-cn/typography/opbuildpdf/TOC.pdf 获取TrueType字体信息:htt-ps://blog.csdn.net/kwfly/article/details/50986338?utm_source=blogxgwz2 本文相关的代码:htt-ps://github.com/fermi1981/TTC_TTF