如何从一个字体文件中读取出需要的信息呢?比如字体名称(非文件名)、字体版权方等等,刚好最学习了浏览器里面的二进制,就尝试了用浏览器来解析字体文件信息。
首先介绍两种常见的字体文件,ttf(TrueType Font)与ttc(TrueType Collection)
TrueType是由美国苹果公司和微软公司共同开发的一种电脑轮廓字体(曲線描邊字)类型标准 – wiki
TrueType Collection (TTC) is an extension of TrueType format that allows combining multiple fonts into a single file, creating substantial space savings for a collection of fonts with many glyphs in common. – wiki
说白了,ttf表示单一字体,而ttc是多个ttf的合集,我们弄懂了ttf,那么ttc就非常简单了。
ttf
ttf文件主要由三大部分组成:
Header + N个Table Directory + N个Table Header + M个Table Record
我们以C类型的结构体形式表示各结构:
- Header
typedef struct _tagTT_OFFSET_TABLE{
uint16 uMajorVersion,
uint16 uMinorVersion
uint16 uNumOfTables, // 关注它--> Table的个数
uint16 uSearchRange,
uint16 uEntrySelector,
uint16 uRangeShift
}TTF_HEADER_TABLE;
Header
总共占据了2*6 = 12
,我们从文件最开始读取12个字节即可得到如上信息,其中若uMajorVersion
不为1且uMinorVersion
不为0,我们可以认为该文件不为合法的ttf文件。
紧接着,我们关注uNumOfTables
的值,其决定了接下来会有多少个连续的Table Directory
。
我们来看看Table Directory
的结构:
- Table Header
typedef struct _tagTT_TABLE_DIRECTORY{
char szTag[4]; // 关注它 --> Table Name
ULONG uCheckSum; // 校验和
ULONG uOffset; // 关注它--> 对应的Table的绝对位置
ULONG uLength; // 对应的Table长度
}TT_TABLE_DIRECTORY;
单个Table Directory
的长度为4*3=12
字节,从Header
的末尾开始,按长度12为单位,循环读取至多uNumOfTables
次,我们就可以读取到所有的Table Directory
了。当然,本文只打算读取字体的基本信息,所以,我们只关注szTag
为name
的Table Directory
。
一旦找到需要的Table Directory
,那么就可以根据其uOffset
的值找到Name Table Header
了,先看看Table Header
的结构:
- Table Header
typedef struct _tagTT_NAME_TABLE_HEADER{
uint16 uFSelector; // 始终为0
uint16 uNRCount; // 关注它--> Name Record 的数量
uint16 uStorageOffset; // Name Record相对于该结构起始位置的偏移值
}TT_NAME_TABLE_HEADER;
通过uStorageOffset
,我们即可从uOffset+uStorageOffset
开始读取uNRCount
个连续的Name Record
,其结构为:
typedef struct _tagTT_NAME_RECORD{
uint16 uPlatformID;
uint16 uEncodingID;
uint16 uLanguageID;
uint16 uNameID; // name id,我们暂时只取0-7的
uint16 uStringLength;
uint16 uStringOffset; //from start of storage area
}TT_NAME_RECORD;
每个Name Record
的长度为2*6=12
,我们要读取的信息为uNameID
范围0-7(更多),分别对应如下:
{
0: 'copyright',
1: 'fontFamily',
2: 'fontSubFamily',
3: 'fontIdentifier',
4: 'fontName',
5: 'fontVersion',
6: 'postscriptName',
7: 'trademark',
}
另外考虑读取的编码为Unicode情况,要求:
uPlatformID === 0 && uEncodingID === (0,1,3)
或
uPlatformID === 3 && uEncodingID === 1
满足如上条件后,我们通过uOffset+uStorageOffset+uStringOffset
分别获取每条Name Record
中的字符串值,即可对应到上面列出的八个属性的值。
到这里,我们就得到了单个ttf文件中需要的信息了。
ttc是由多个ttf文件构成的,其header结构为:
typedef struct _tagTTC_HEADER_LE {
uint32 tag;
uint16 uMajorVersion;
uint16 uMinorVersion;
uint32 uNumFonts;
} TTC_HEADER_LE;
其中tag
固定为ttcf
,uNumFonts
表明该文件包含了多少个ttf字体,同时,在紧挨着该结构后面是uNumFonts
个4字节的offset,用于表示每个ttf相对于文件起始位置的偏移。一旦找到这个偏移,读取每个ttf的方式就和前面一样了。
完整实现代码见github
参考
- https://docs.microsoft.com/en-us/typography/opentype/spec/otff
- https://blog.csdn.net/kwfly/article/details/50986338