DarkNode

Life, the Universe and Everything

CMap 表相关修改技术简要指南

本文发表于:
最后修改于:
分类:font
合计信息量:9.36kb

基本信息

CMap 表是用于将字符编码映射到字形索引的表,字体渲染过程中,首先会获得要渲染的字符的字符 Uni­code 编码,随后根据 CMap 表找到其对应的字形的索引值,再根据索引值读取对应字形的数据并进行渲染。其具体信息可以参考 Ap­ple 文档 和 Win­dows 文档

CMap 表细节解说

CMap 表可以通过 ttx 的命令导出:

ttx -t cmap Font.ttf

用 ttx 导出的 CMap 表通常是这个样子的:

<cmap>
    <tableVer­sion ver­sion="0"/>
    <cmap_for­mat_4 plat­formID="0" pla­tEn­cID="3" lan­guage="0">
        <map code="0x20" name="space"/><!-- SPACE -->
        <map code="0x21" name="ex­clam"/><!-- EX­CLA­MA­TION MARK -->
        <map code="0x22" name="quot­edbl"/><!-- QUO­TA­TION MARK -->
        <map code="0x23" name="num­ber­sign"/><!-- NUM­BER SIGN -->
        <map code="0x24" name="dol­lar"/><!-- DOL­LAR SIGN -->
        <map code="0x25" name="per­cent"/><!-- PER­CENT SIGN -->
        <map code="0x26" name="am­per­sand"/><!-- AM­PER­SAND -->
        <map code="0x27" name="quotesin­gle"/><!-- APOS­TRO­PHE -->
…
    <cmap_for­mat_4 plat­formID="3" pla­tEn­cID="1" lan­guage="0">
        <map code="0x20" name="space"/><!-- SPACE -->
        <map code="0x21" name="ex­clam"/><!-- EX­CLA­MA­TION MARK -->
        <map code="0x22" name="quot­edbl"/><!-- QUO­TA­TION MARK -->
        <map code="0x23" name="num­ber­sign"/><!-- NUM­BER SIGN -->
        <map code="0x24" name="dol­lar"/><!-- DOL­LAR SIGN -->
        <map code="0x25" name="per­cent"/><!-- PER­CENT SIGN -->
        <map code="0x26" name="am­per­sand"/><!-- AM­PER­SAND -->
        <map code="0x27" name="quotesin­gle"/><!-- APOS­TRO­PHE -->

CMap 表中通常包括数个子表,比如上面的例子中就有两个子表,分别是:

<cmap_for­mat_4 plat­formID="0" pla­tEn­cID="3" lan­guage="0">
<cmap_for­mat_4 plat­formID="3" pla­tEn­cID="1" lan­guage="0">

for­mat、plat­formID、pla­tEn­cID 这三组值指定了 CMap 表数据的编码方式与顺序,常见的字体通常具有多个子表。幸运的是,它们通常只有 4 个常见的组合:

首先,cmap­For­mat=4 只支持 65536 个字符,cmap­For­mat=12 是 cmap­For­mat=4 的超集,支持 2147483648 个字符。英文字体通常只使用 cmap­For­mat=4,CJK 字体则常常会用到 cmap­For­mat=12。在使用 cmap­For­mat=12 格式时仍然需要保留一个对应的 cmap­For­mat=4 格式的子表,否则 Win­dows 下将出现字体兼容性问题。

plat­formID=0 表示 Uni­code 平台,而 plat­formID=3 表示 Win­dows 平台,大多数字体渲染引擎或是程序会优先读取 Uni­code 平台的 CMap,但是 Win­dows 至今仍不支持 Uni­code 平台,需要保留 Win­dows 平台的子表以确保兼容性。

pla­tEn­cID 与 plat­formID 有关,表示在对应平台下的具体编码方式。其具体取值如下:

这些组合当中,cmap­For­mat=4 plat­formID=0 pla­tEn­cID=3 与 cmap­For­mat=4 plat­formID=3 pla­tEn­cID=1 等价,cmap­For­mat=12 plat­formID=0 pla­tEn­cID=4 与 cmap­For­mat=12 plat­formID=3 pla­tEn­cID=10 等价,表示相同的编码方式与顺序。

子表内容由许多条记录构成,比如:

<map code="0x25" name="per­cent"/><!-- PER­CENT SIGN -->

表示 Uni­code 编码为 0x25 的字符被对应到字体文件中 per­cent 这个字形。

Ping­Fang 的修改

Ping­Fang 是 Ap­ple 公司在 OS X 10.11 中新加入的字体,在最初的 DP1 版本中,只需对 ttc 文件进行解包即可在 Win­dows 下正常使用,而 DP2 之后的版本却不能这样,其根本原因是 DP1 版本的 Ping­Fang 有以下 4 个 CMap 子表:

<cmap_for­mat_4 plat­formID="0" pla­tEn­cID="3" lan­guage="0">
<cmap_for­mat_12 plat­formID="0" pla­tEn­cID="4" for­mat="12" re­served="0" length="185584" lan­guage="0" nGroups="15464">
<cmap_for­mat_4 plat­formID="3" pla­tEn­cID="1" lan­guage="0">
<cmap_for­mat_12 plat­formID="3" pla­tEn­cID="10" for­mat="12" re­served="0" length="185584" lan­guage="0" nGroups="15464">

而 DP2 之后的版本却变成了 2 个 CMap 子表:

<cmap_for­mat_4 plat­formID="0" pla­tEn­cID="3" lan­guage="0">
<cmap_for­mat_12 plat­formID="0" pla­tEn­cID="4" for­mat="12" re­served="0" length="189520" lan­guage="0" nGroups="15792">

由于缺乏 plat­formID 为 3 的子表,Win­dows 将其视为了无效的字体文件。

根据前面的介绍,将 DP2 之后的字体文件修改为兼容 Win­dows 的字体文件的方法就是加入对应的表了,不过我们有个偷懒的办法:将 cmap­For­mat=4 plat­formID=0 pla­tEn­cID=3 直接改成 cmap­For­mat=4 plat­formID=3 pla­tEn­cID=1,将 cmap­For­mat=12 plat­formID=0 pla­tEn­cID=4 直接改为 cmap­For­mat=12 plat­formID=3 pla­tEn­cID=10,由于这两组对应的编码方式与顺序完全相同,所以我们并不需要修改后续的子表内容就能使这个字体在 Win­dows 下可用。

先使用 otc2otf 将 Ping­Fang.ttc 解包,随后在 OS X 下执行:

ttx -t cmap Ping­Fang-SC-Reg­u­lar.otf
sed -i '' 's/plat­formID="0" pla­tEn­cID="3"/plat­formID="3" pla­tEn­cID="1"/g' Ping­Fang-SC-Reg­u­lar.ttx
sed -i '' 's/plat­formID="0" pla­tEn­cID="4"/plat­formID="3" pla­tEn­cID="10"/g' Ping­Fang-SC-Reg­u­lar.ttx
ttx -b -m Ping­Fang-SC-Reg­u­lar.otf Ping­Fang-SC-Reg­u­lar.ttx

即可使这些 otf 文件在 Win­dows 下可用。

Hi­ragino Sans 的修改

Hi­ragino Sans 也是 OS X 10.11 中新引入的字体。它的前身是 Hi­ragino Kaku Gothic,在加入多个新字重,补全为从 W0 至 W9 的庞大家族后,更名为 Hi­ragino Sans。这个字体的 CMap 表有 3 个子表:

<cmap_for­mat_14 plat­formID="0" pla­tEn­cID="5" for­mat="14" length="23120" num­VarS­e­lec­tor­Records="6">
<cmap_for­mat_2 plat­formID="1" pla­tEn­cID="1" lan­guage="0">
<cmap_for­mat_12 plat­formID="3" pla­tEn­cID="10" for­mat="12" re­served="0" length="128992" lan­guage="0" nGroups="10748">

由于 CMap 表中缺乏 cmap­For­mat=4 的子表,这个字体无法被 Win­dows 识别,其修改方式比较麻烦,需要手动加入一个新的子表并写入对应内容才行。

我们首先关注已有的 cmap­For­mat=12 的子表:

<cmap_for­mat_12 plat­formID="3" pla­tEn­cID="10" for­mat="12" re­served="0" length="128992" lan­guage="0" nGroups="10748">
    <map code="0x0" name="cid00001"/><!-- ???? -->
    <map code="0x1" name="cid00001"/><!-- ???? -->
    <map code="0x2" name="cid00001"/><!-- ???? -->
    <map code="0x3" name="cid00001"/><!-- ???? -->
    <map code="0x4" name="cid00001"/><!-- ???? -->
…
    <map code="0xffe8" name="cid00323"/><!-- HALFWIDTH FORMS LIGHT VER­TI­CAL -->
    <map code="0x1f100" name="cid08061"/><!-- ???? -->
…
    <map code="0x2f920" name="cid07839"/><!-- ???? -->

之前提到过,cmap­For­mat=4 plat­formID=3 pla­tEn­cID=1 这个子表其实就是 cmap­For­mat=12 plat­formID=3 pla­tEn­cID=10 这个子表的子集,确切的说是其 0x0000 至 0xffff 的部分,那么加入新的子表就很简单了。

把 map code 从 0x0000 一直到 0xffff 的段落全部复制下来,粘贴到新加入的 cmap­For­mat=4 的子表之后:

<cmap_for­mat_4 plat­formID="0" pla­tEn­cID="3" lan­guage="0">
        <map code="0x0" name="cid00001"/><!-- ???? -->
    <map code="0x1" name="cid00001"/><!-- ???? -->
    <map code="0x2" name="cid00001"/><!-- ???? -->
    <map code="0x3" name="cid00001"/><!-- ???? -->
    <map code="0x4" name="cid00001"/><!-- ???? -->
…
    <map code="0xffe8" name="cid00323"/><!-- HALFWIDTH FORMS LIGHT VER­TI­CAL -->

最后将修改后的 ttx 文件合并回源文件,到此为止,其兼容 Win­dows 的版本就修改完成了。