Windows 添加 flag emoji 支持

Windows 默认的 “Segoe UI Emoji” 字体中没有相应的字形,因此无法显示 emoji 旗帜。通过添加这些缺失的字形,就可以让 Windows 支持国旗显示。

fontTools

fontTools 是一个基于 Python 的字体编辑工具,可以通过 pip install fonttools 安装。安装完成后就可以在命令行里使用 pyfttools 提供的一些工具:

ttx

该命令可以将 TrueType / OpenType 字体“反编译”成可以直接用文本编辑器打开的 XML 文档,其后缀名为 ttx。也可以把修改后的 ttx 文件重新“编译”回 ttf。

pyftmerge

该命令用于合并字体。但是这里我们无法使用这个命令把其他字体的字形合并到 “Segoe UI Emoji” 中,因为 pyftmerge 不支持对 CPAL/COLR 表进行合并。

OpenType

关于 OpenType 规范的详细介绍,可以参考微软的文档 https://docs.microsoft.com/en-us/typography/opentype/spec/

OpenType 字体中有多种不同类型的表格,我们主要关注下面几张表:

cmap

指定某个码位对应的字形。不是所有的字形都会对应一个码位,但是每个在 cmap 表中声明的码位都需要对应一个字形(可以重复)。如果我们需要添加字形的 Unicode 码在原字体中不存在,就需要手动添加。某些字体可能会存在多张对应不同区域和语言的映射表,需要在所有映射表都加上相应的码位。一些防复制方案就是通过混淆 cmap 来实现的,可以实现选中复制后的文本和原始显示完全不同。

hmtx

指定每个字形的垂直度量。如果修改之后的字体的宽度和间距过宽或过窄,就说明这个表中的数值存在问题。

glyf

包含字形数据,每个字形都由一系列 XY 座标点组成的轮廓构成。

GSUB

定义了字形的替换规则。一类比较常见的字形替换是连字:当多个字形按照指定的顺序依次出现时,将其替换成另一个字形。国旗 emoji 没有单独的 Unicode 码位,就是通过连字实现。比如 “🇹” 和 “🇼” ,连在一起就会变成 “🇹🇼”。

彩色字形

Apple, Google, Microsoft 和 Mozilla 都提出了自己的彩色字形标准。通常情况下,相应平台对自己格式的支持是最好的,对于其他格式的支持则极为有限。有部分软件自己实现了字体渲染,导致它们可以支持一些原本平台不支持的格式,比如 Adobe 和 Mozilla 的软件就会支持 SVG 。

CBDT/CBLC

由 Google 提出,允许在字体中嵌入彩色位图。可以看作是 EBDT/EBLC 的升级版。

sbix

由 Apple 提出,也是一个基于位图的方案,原理和 Google 几乎没有区别。

CPAL/COLR

由 Microsoft 提出,彩色字形中的单个颜色作为一个“图层”存储于 glyf 中。CPAL 包含了各个颜色索引值所对应的颜色。COLR 则指定了某个字形由那些“图层”构成,以及各“图层”的颜色索引值。

SVG

由 Adobe 和 Mozilla 提出,通过直接在文件中嵌入 svg 来显示彩色字形。相比其他方案,SVG 的功能最为齐全,甚至可以实现动画等特殊效果。

修改要点

为了可以在 Windows 中使用,我们需要一个使用 CPAL/COLR,并且包含国旗 emoji 的字体。TwemojiMozilla 是一个使用 CPAL/COLR 的 Twitter emoji 字体,我们将其中的国旗 emoji 提取出来,添加到 “Segoe UI Emoji” 字体中。

使用 DirectDraw 的程序可以显示彩色 emoji,如果程序不支持 DirectDraw,只能显示黑白的 emoji。因此为了支持这些应用,我们还需要一份黑白的 emoji 字形,可以从 TwitterColorEmoji-SVGinOT 中提取。TwitterColorEmoji-SVGinOT 是一个使用 svg 的 Twitter emoji 字体,除了彩色字形之外,还包含了使用程序转换生成的黑白字形。黑白字形的辨识度相对较低,但总比完全无法显示要好得多。

TwemojiMozilla 的 units per em 是 512,而另外两个字体是 2048,因此在合并之前需要对 TwemojiMozilla 中的所有 XY 座标值乘 4。下面是一个简单的 Python 脚本,来实现 hmtx 和 glyf 中的座标值乘 4:

1
2
3
4
import re
f = open("hmtx_and_glyf.txt", "r")
for line in f.readlines():
print(re.sub(' (x|y|width|lsb)="(|-)(\d+)"',lambda x: ' '+x.group(1)+'="'+x.group(2)+str(int(x.group(3))*4)+'"' ,line), end='')

每在 glyf 表里添加一个字形,都需要在 hmtx 、GlyphOrder 和 GDEF 里面添加对应的项,即这四个表的元素数目需要保持一致。

在合并 COLR 之前,需要先合并 CPAL ,然后对 colorID 作相应的映射。原始微软字体中有一些 CPAL 项没有被使用,在合并时可以回收利用这些 colorID。下面是一个简单的 Python 脚本,来实现 CPAL 的合并和 COLR 的 colorID 映射:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import re

f = open("colr_to_add.txt", "r")
colors = set()
for line in f.readlines():
m = re.search('<layer colorID="(\d+?)" name=', line)
if m:
colorID = int(m.group(1))
colors.add(colorID)

f = open("cpal_original.txt", "r")
cpal = []
available = []
for line in f.readlines():
m = re.search('<color index="\d+?" value="#([0-9A-F]{6})[0-9A-F]{2}"/>', line)
if m:
color = m.group(1)
if color == '000001':
available.append(len(cpal))
cpal.append(color)

f = open("cpal_to_add.txt", "r")
cpal2 = []
for line in f.readlines():
m = re.search('<color index="\d+?" value="#([0-9A-F]{6})[0-9A-F]{2}"/>', line)
if m:
color = m.group(1)
cpal2.append(color)

mapping = {}
for i in colors:
if cpal2[i] in cpal:
mapping[i] = cpal.index(cpal2[i])
elif available:
idx = available.pop()
cpal[idx] = cpal2[i]
mapping[i] = idx
else:
mapping[i] = len(cpal)
cpal.append(cpal2[i])

f = open("colr_to_add.txt", "r")
for line in f.readlines():
print(re.sub('<layer colorID="(\d+?)" name=',lambda x: '<layer colorID="'+str(mapping[int(x.group(1))])+'" name=' ,line), end='')

for i,color in enumerate(cpal):
print(' <color index="'+str(i)+'" value="#'+color+'FF"/>')

原字体 CPAL 中有两张调色板,都需要作相应修改。fontTools 可以自动计算大多数的统计值,但是不支持自动计算 CPAL 里面的 numPaletteEntries,因此需要手动更新这一项。

原字体有数字签名,修改后重新编译的字体数字签名会失效,因此可以删掉最后的 DSIG 表。

安装修改后的字体

下载

seguiemj mod ver 1.31

seguiemj mod ver 1.33

1.31 版适用于 Win10 和 Server 2019/2022,1.33 版适用于 Win11。这两个版本的不同之处在 emojipedia (Archive) 上有比较详细的说明。

替换系统字体

使用 copy 命令将字体复制到 %windir%\Fonts 中。为了更好的兼容性,新字体文件名的 ASCII 排序应当在原字体 “seguiemj.ttf” 之前,同时使用 8.3 格式。

1
copy seguiemj_mod.ttf %windir%\Fonts\segoeemj.ttf

修改下列注册表项,重启系统即可生效。

1
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts" /v "Segoe UI Emoji (TrueType)" /d segoeemj.ttf /f