前些天小伙伴留言提了一个与 Kindle 自定义字体功能相关的问题。他按照《如何使用 Kindle 的原生自定义字体功能》这篇文章提供的步骤,将字体文件放到 Kindle 根目录中的 fonts 文件夹后,在字体选择面板中选择字体时发现,本该显示中文字体名称的地方却出现了如下图所示的一串问号。
▲ 中文字体名称显示问号
此问题的出现可能是字体文件制作不规范导致的,我们可以参考 OpenType 规范(下称“规范”)并利用合适的工具修复此问题。本文提供了一种解决方案,可有效地解决 OpenType 格式(文件扩展名为 .otf、.otc、.ttf 或 .ttc)字体名称在 Kindle 的字体选择面板中无法正确显示的问题。
一、解决思路
OpenType 格式字体文件包含一系列以表格形式呈现的数据,这些数据的类型有很多,具体可查看规范。本文只关注与字体名称(选择字体时看到的字样)相关的元数据信息,即字体的“name”表。
字体的“name”表可以将多种语言的字符串(文本)关联到字体上,比如版权声明、字体名称、字族名称、样式名称等,其目的是为了在不同语言环境的操作系统中显示这些信息的相应语言版本。
如果一个字体的名称没有正确显示就意味着字体文件中的“name”表有问题,为解决这个问题,我们可以先使用工具将其提取出来,然后按照规范进行修正,最后再把修正好的“name”表合并到字体文件中。
二、准备工具
字体文件(即扩展名为 .otf、.otc、.ttf 或 .ttc 的文件)是独立的二进制文件,我们无法直接对其进行编辑,为了能够对其进行我们所需要的修改,必须借助专门的工具或将其转换成人类可读的文本文件。
本文使用的工具是 fontTools,一款基于 Python 的字体处理程序库,其中包含一款名为 ttx 的命令行工具,它可以将字体文件转换成扩展名为 .ttx 的 XML 文本文件,方便我们修改字体的相关信息。
使用 fontTools 需要确保你的操作系统安装版本大于等于 3.6 的 Python。如果你的系统没有安装 Python 或者版本低于 3.6,请前往 Python 官网下载安装(macOS 系统推荐通过 Homebrew 安装)。
Python 环境准备好后,可在“终端”或“命令提示符”中输入以下命令安装 fontTools:
pip3 install fonttools
fontTools 安装完成后,可输入如下命令,如果能正常输出版本号就表示安装成功:
ttx --version
三、操作步骤
虽然 ttx 程序可以将整个字体文件转换成 .ttx 文件,也能将 .ttx 文件转换回字体文件,但是如果字体文件较大的话,转换的 .ttx 文件也会很大,这会降低编辑和转换的效率。因此,为了大幅提高效率,我们只需单独提取出字体文件的“name”表进行修改,再利用 ttx 的合并功能将修改后的表合并回字体文件。
为方便展示操作步骤,下面虚构了一个有名称显示问题的字体文件 SampleSong.ttf(在实际操作时将该文件名换成实际的字体文件名即可)。对于步骤中涉及到规范的地方,书楼会做必要的解释。
1、从字体文件中提取出“name”表
要将字体文件中的“name”表单独提取出来,可先切换到字体所在的目录,然后运行如下命令:
ttx -t name SampleSong.ttf
* 提示:如果你处理的是“字体集(Font Collections)”文件(扩展名为 .ttc 或 .otc),需要在命令中指定字体集中单个字体的编号,即在选项 -t
前添加选项 -y
,并在该选项后指定编号。如 ttx -y 0 -t 'name' SampleFonts.ttc
。
默认情况下,提取的“name”表文件名与字体文件名相同,存放位置也与字体文件相同,只是扩展名变成了 .ttx。本例中得到的文件为 SampleSong.ttx,用代码编辑器打开该文件,会看到类似这样的内容:
<?xml version="1.0" encoding="UTF-8"?>
<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.22">
<name>
<namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
示例宋体
</namerecord>
<namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
Regular
</namerecord>
<namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
示例宋体 Regular; Version 1.0; 2019-09-27
</namerecord>
<namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
示例宋体 Regular
</namerecord>
<namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
Version 1.0 September 27, 2019
</namerecord>
<namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
SampleSong-Regular
</namerecord>
</name>
</ttFont>
这是字体文件中所有的“名称记录(Name Records)”,它由多个 <namerecord>
元素组成,每个元素都含有 nameID
、platformID
、platEncID
和 langID
属性(分别对应着规范中的 nameID、platformID、encodingID 和 languageID),这些属性定义了各元素在字体中的意义。这些属性的含义如下:
- nameID:名称 ID。常用的是 26 个预定义 ID(从 0 到 25),每个 ID 对应一种特定含义。其中 1 和 2 分别对应着“字族名称(Font Family name)”和“子字族名称(Font Subfamily name)”。
- platformID:平台 ID。0 对应 Unicode,1 对应 Macintosh 平台,3 对应 Windows 平台。接下来的两个属性 encodingID 和 languageID 的值均取决于此 ID。本例采用 Windows 平台。
- platEncID:特定平台编码 ID。Windows 平台下常用的编码 ID 为 1(Unicode BMP)。
- langID:语言 ID。Windows 平台下的语言 ID 的值以 16 进制表示,其中美式英文为 0x409,简体中文为 0x804。这个属性是解决问题的关键,它决定了让系统以何种语言解析名称记录元素的内容。
* 提示:“字族名称”也称“系列名称”,即选择字体时显示的名称;“子字族名称”也称“样式名称”,即字重、斜体等字体特性。
名称记录元素的先后顺序取决于这些属性,必须先按平台 ID 排序,再按特定平台编码 ID 排序,接着按语言 ID 排序,最后按名称 ID 排序。这种有序的排列也有助于我们快速辨别某组记录对应何种语言。
了解了这些技术细节之后,我们就能够看出示例“name”表存在的问题,含有简体中文字符串的名称记录,其语言 ID 属性值却是 0x409(即美式英文),显然这会让某些系统(如 Kindle)无法正确解析。
为解决这个问题,就需要修正字体的名称记录中错误的语言 ID,使其与字符串的实际语言相匹配。
2、修正“name”表中错误的语言 ID
修正语言 ID 有两种方法:一种是把所有含有简体中文字符串的名称记录的语言 ID 都更改成 0x804;另一种是在不修改原有名称记录的情况下新增针对简体中文的名称记录,在本例中,只需要添加一项名称 ID 为 1 的名称记录,并将其语言 ID 设为简体中文(如下所示),就足以解决字体名称显示问题了。
<?xml version="1.0" encoding="UTF-8"?>
<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.22">
<name>
<namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
示例宋体
</namerecord>
<namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
Regular
</namerecord>
<namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
示例宋体 Regular; Version 1.0; 2019-09-27
</namerecord>
<namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
示例宋体 Regular
</namerecord>
<namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
Version 1.0 April 25, 2021
</namerecord>
<namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
SampleSong-Regular
</namerecord>
<namerecord nameID="1" platformID="3" platEncID="1" langID="0x804">
示例宋体
</namerecord>
</name>
</ttFont>
书楼推荐第二种方法。如果你选用第一种方法可能会产生一些副作用。以名称 ID 为 6 的名称记录为例,规范对该记录的建议为:应包含 Macintosh 和 Windows 两个平台的名称记录,且都应将语言 ID 设置为英文。若不小心设为中文,在为 macOS 系统添加字体时,就会遇到字体验证提示表结构错误的情况。
3、将修正的“name”表合并到字体
将字体“name”表中的语言 ID 错误修正之后,即可运行如下命令将“name”表合并回字体文件:
ttx -m SampleSong.ttf SampleSong.ttx
* 提示:如果你要处理的是“字体集(Font Collections)”文件(扩展名为 .ttc 或 .otc),需要注意,由于 ttx 无法直接对字体集文件进行合并操作,因此你需要先使用工具套装 Adobe Font Development Kit for OpenType (AFDKO) 中的 otc2otf 程序将字体集中的单个字体文件全部提取出来,再用 ttx 进行处理,最后用工具套装里面的 otf2ot 程序将重新合并。
命令执行成功后,会生成一个新的字体文件,默认情况下,新字体文件名会在原有文件名的基础上添加一个序号 #1,如 SampleSong#1.ttf(你也可在选项 -m
前添加选项 -o
,并在其后自定义文件名)。
* 提示:在 macOS 系统中,如果字体文件名带有 # 符号,即便字体结构没问题,添加到字体册时也会出现奇怪的“系统验证”错误,无法通过字体的验证。如果你处理的字体文件需要在 macOS 系统中使用,建议删掉字体文件名中的 # 符号。
得到新生成的字体文件后,将其拷贝到 Kindle 中即可查看修正效果,如下所示:
▲ 中文字体名称显示问号修复效果
至此,自定义字体名称无法在 Kindle 的字体选择面板中正常显示的问题就解决了。文章看起来很长,主要是因为添加了一些规范的描述,简单的说就是先利用 fontTools 里的 ttx 工具从字体文件中提取“name”表,然后并按照规范修正错误,最后将修正的“name”重新合并到字体文件中得到新的字体文件。
未经允许不得转载:书路 » 如何修复 Kindle 自定义字体名称显示问号的问题