PDA

完整版本 : 原创 CMap表相关修改技术简要指南


BlackGear
2015-07-10, 17:04:01
本文原载于我的个人Blog (https://darknode.in/font/cmap-modify-tutorial/),作为一个系列作品,详细介绍用fonttools系列工具修改字体的各种特性的方法,本篇将cmap表有关的各种修改,下一篇将以name表为主题。由于论坛的界面排版比较难以控制,所以排版丑陋还请见谅,希望拥有较好排版感受的人可以前往我的Blog观看,不过关于帖子内容的讨论还请在themex这边留言。

读完本文,您应该可以用ttx将Mac下的字体修改为Windows下可用的版本,关于对字体的各种信息进行更改,请期待下一篇文章。


基本信息
CMap表是用于将字符编码映射到字形索引的表,字体渲染过程中,首先会获得要渲染的字符的字符Unicode编码,随后根据CMap表找到其对应的字形的索引值,再根据索引值读取对应字形的数据并进行渲染。其具体信息可以参考Apple文档 (https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6cmap.html)或Windows文档 (https://www.microsoft.com/typography/otspec/cmap.htm)。

CMap表细节解说

CMap表可以通过ttx的命令导出:
ttx -t cmap Font.ttf


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

<cmap>
<tableVersion version="0"/>
<cmap_format_4 platformID="0" platEncID="3" language="0">
<map code="0x20" name="space"/><!-- SPACE -->
<map code="0x21" name="exclam"/><!-- EXCLAMATION MARK -->
<map code="0x22" name="quotedbl"/><!-- QUOTATION MARK -->
<map code="0x23" name="numbersign"/><!-- NUMBER SIGN -->
<map code="0x24" name="dollar"/><!-- DOLLAR SIGN -->
<map code="0x25" name="percent"/><!-- PERCENT SIGN -->
<map code="0x26" name="ampersand"/><!-- AMPERSAND -->
<map code="0x27" name="quotesingle"/><!-- APOSTROPHE -->

<cmap_format_4 platformID="3" platEncID="1" language="0">
<map code="0x20" name="space"/><!-- SPACE -->
<map code="0x21" name="exclam"/><!-- EXCLAMATION MARK -->
<map code="0x22" name="quotedbl"/><!-- QUOTATION MARK -->
<map code="0x23" name="numbersign"/><!-- NUMBER SIGN -->
<map code="0x24" name="dollar"/><!-- DOLLAR SIGN -->
<map code="0x25" name="percent"/><!-- PERCENT SIGN -->
<map code="0x26" name="ampersand"/><!-- AMPERSAND -->
<map code="0x27" name="quotesingle"/><!-- APOSTROPHE -->


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

<cmap_format_4 platformID="0" platEncID="3" language="0">
<cmap_format_4 platformID="3" platEncID="1" language="0">


format platformID platEncID这三组值指定了CMap表数据的编码方式与顺序,常见的字体通常具有多个子表。幸运的是,它们通常只有4个常见的组合:


| format | platformID | platEncID | Description |
| ------ | ---------- | --------- | ------------- |
| 4 | 0 | 3 | Unicode UCS-2 |
| 4 | 3 | 1 | Windows UCS-2 |
| 12 | 0 | 4 | Unicode UCS-4 |
| 12 | 3 | 10 | Windows UCS-4 |


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

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

platEncID与platformID有关,表示在对应平台下的具体编码方式。其具体取值如下:


| platformID | platEncID | Description |
| ---------- | --------- | ------------------------------------------------------------- |
| 0 | 0 | Default semantics |
| 0 | 1 | Version 1.1 semantics |
| 0 | 2 | ISO 10646 1993 semantics (deprecated) |
| 0 | 3 | Unicode 2.0 or later semantics (BMP only) |
| 0 | 4 | Unicode 2.0 or later semantics (non-BMP characters allowed) |
| 0 | 5 | Unicode Variation Sequences |
| 0 | 6 | Full Unicode coverage (used with type 13.0 cmaps by OpenType) |

| platformID | platEncID | Description |
| ---------- | --------- | ------------------- |
| 3 | 0 | Symbol |
| 3 | 1 | Unicode BMP (UCS-2) |
| 3 | 2 | ShiftJIS |
| 3 | 3 | PRC |
| 3 | 4 | Big5 |
| 3 | 5 | Wansung |
| 3 | 6 | Johab |
| 3 | 7 | Reserved |
| 3 | 8 | Reserved |
| 3 | 9 | Reserved |
| 3 | 10| Unicode UCS-4 |


这些组合当中,0-3与3-1等价,0-4与3-10等价,表示相同的编码方式与顺序。

子表内容由许多条记录构成,比如:
<map code="0x25" name="percent"/><!-- PERCENT SIGN -->

表示Unicode编码为0x25的字符被对应到字体文件中percent这个字形。

PingFang的修改

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

<cmap_format_4 platformID="0" platEncID="3" language="0">
<cmap_format_12 platformID="0" platEncID="4" format="12" reserved="0" length="185584" language="0" nGroups="15464">
<cmap_format_4 platformID="3" platEncID="1" language="0">
<cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="185584" language="0" nGroups="15464">


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

<cmap_format_4 platformID="0" platEncID="3" language="0">
<cmap_format_12 platformID="0" platEncID="4" format="12" reserved="0" length="189520" language="0" nGroups="15792">


由于缺乏platformID为3的子表,Windows将其视为了无效的字体文件。

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

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

ttx -t cmap PingFang-SC-Regular.otf
sed -i 's/platformID="0" platEncID="3"/platformID="3" platEncID="1"/g' PingFang-SC-Regular.ttx
sed -i 's/platformID="0" platEncID="4"/platformID="3" platEncID="10"/g' PingFang-SC-Regular.ttx
ttx -b -m PingFang-SC-Regular.otf PingFang-SC-Regular.ttx


即可使这些otf文件在Windows下可用。

Hiragino Sans的修改

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

<cmap_format_14 platformID="0" platEncID="5" format="14" length="23120" numVarSelectorRecords="6">
<cmap_format_2 platformID="1" platEncID="1" language="0">
<cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="128992" language="0" nGroups="10748">


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

我们首先关注已有的format 12的子表:

<cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="128992" language="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 VERTICAL -->
<map code="0x1f100" name="cid08061"/><!-- ???? -->

<map code="0x2f920" name="cid07839"/><!-- ???? -->


之前提到过,format=4 platformID=3 platEncID=1的子表其实就是format=12 platformID=3 platEncID=10的子集,确切的说是其0x0000至0xffff的部分,那么加入新的子表就很简单了。

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

<cmap_format_4 platformID="0" platEncID="3" language="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 VERTICAL -->


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

cjmyuhui6
2015-07-10, 21:14:57
FLS的这个貌似就是改CMap表的,改起来比较简单,但是各项代表的意义需要先清楚。。

BlackGear
2015-07-10, 23:23:05
FLS的这个貌似就是改CMap表的,改起来比较简单,但是各项代表的意义需要先清楚。。

这里是name表,不是cmap表,下一篇文章讲name表的修改。

大部分所谓OS X字体解密其实就是在改cmap表,了解了这个就用不着用那些大型程序来解密字体了。

digidea
2015-07-11, 15:52:54
大家用 ttx 转换回字体文件时记得加 -b 参数.

lrk293
2015-07-12, 14:09:37
请问对于GB12345,有没有基于cmap实现快速unicode化的方法?

BlackGear
2015-07-13, 00:38:50
请问对于GB12345,有没有基于cmap实现快速unicode化的方法?

没太明白你这个unicode化是个什么意思……

lrk293
2015-07-13, 01:28:58
没太明白你这个unicode化是个什么意思……


将字形映射到正确的Unicode码位,就是输繁出繁,而不是字体原来的输简出繁