Android上使用字体使用OTC和可变字体时需要修改fonts.xml,而Noto CJK需要删除一些cmap条目。

依赖

安装Python和pipx

Android对Noto CJK的修改

详情可见此README.android

安装nototools

1
pipx install notofonttools

从AOSP下载subset_noto_cjk.pyDownload raw file · Issue #106 · google/gitiles):

1
curl "https://android.googlesource.com/platform/external/noto-fonts/+/refs/heads/master/cjk/subset_noto_cjk.py?format=TEXT" | base64 --decode > subset_noto_cjk.py

subset_noto_cjk.py中的这行:

1
TTC_NAMES = ('NotoSansCJK-Regular.ttc', 'NotoSerifCJK-Regular.ttc')

修改为:

1
TTC_NAMES = ('NotoSansCJK-VF.otf.ttc', )

注意,只有一个字体时,不要把末尾的逗号去掉。

执行:

1
2
3
4
5
# notofonttools虚拟环境
source ~/.local/pipx/venvs/notofonttools/bin/activate
python3 ./subset_noto_cjk.py
# 退出虚拟环境
deactivate

这个脚本会去掉一些cmap,避免Emoji显示的问题。输出的字体在当前文件夹的subsetted文件夹下。

标点挤压(Contextual Spacing)

详情可见此README.android,但早期版本的Android实际上并没有使用过这个工具。

安装East Asian Contextual Spacing

1
pipx install east-asian-spacing

执行:

1
east-asian-spacing -o build subsetted/NotoSansCJK-VF.otf.ttc

-o build参数代表字体输出在当前文件夹的build文件夹下。

OTC Index

安装afdko

1
pipx install afdko

然后执行:

1
2
3
4
5
6
7
otc2otf -r NotoSansCJK-VF.otf.ttc | grep Font

Font 0: NotoSansCJKjp-Thin.otf
Font 1: NotoSansCJKkr-Thin.otf
Font 2: NotoSansCJKsc-Thin.otf
Font 3: NotoSansCJKtc-Thin.otf
Font 4: NotoSansCJKhk-Thin.otf

这里的数字与TTC index一一对应,如简体中文为2,fonts.xml中zh-Hans的index就为2:

1
2
3
<family lang="zh-Hans">
<font weight="100" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">NotoSansCJK-VF.otf.ttc
<!-- ... -->

可变字体

安装fonttools

1
pipx install fonttools

执行:

1
2
# -y 代表OTC index
ttx -y 2 -t fvar NotoSansCJK-VF.otf.ttc

输出内容:

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
48
49
50
51
52
53
54
55
56
57
58
59
60
cat NotoSansCJK-VF.otf.ttx
<?xml version="1.0" encoding="UTF-8"?>
<ttFont sfntVersion="OTTO" ttLibVersion="4.28">

<fvar>

<!-- Weight -->
<Axis>
<AxisTag>wght</AxisTag>
<Flags>0x0</Flags>
<MinValue>100.0</MinValue>
<DefaultValue>100.0</DefaultValue>
<MaxValue>900.0</MaxValue>
<AxisNameID>265</AxisNameID>
</Axis>

<!-- Thin -->
<!-- PostScript: NotoSansCJKsc-Thin -->
<NamedInstance flags="0x0" postscriptNameID="267" subfamilyNameID="266">
<coord axis="wght" value="100.0"/>
</NamedInstance>

<!-- Light -->
<!-- PostScript: NotoSansCJKsc-Light -->
<NamedInstance flags="0x0" postscriptNameID="269" subfamilyNameID="268">
<coord axis="wght" value="300.0"/>
</NamedInstance>

<!-- DemiLight -->
<!-- PostScript: NotoSansCJKsc-DemiLight -->
<NamedInstance flags="0x0" postscriptNameID="271" subfamilyNameID="270">
<coord axis="wght" value="350.0"/>
</NamedInstance>

<!-- Regular -->
<!-- PostScript: NotoSansCJKsc-Regular -->
<NamedInstance flags="0x0" postscriptNameID="273" subfamilyNameID="272">
<coord axis="wght" value="400.0"/>
</NamedInstance>

<!-- Medium -->
<!-- PostScript: NotoSansCJKsc-Medium -->
<NamedInstance flags="0x0" postscriptNameID="275" subfamilyNameID="274">
<coord axis="wght" value="500.0"/>
</NamedInstance>

<!-- Bold -->
<!-- PostScript: NotoSansCJKsc-Bold -->
<NamedInstance flags="0x0" postscriptNameID="277" subfamilyNameID="276">
<coord axis="wght" value="700.0"/>
</NamedInstance>

<!-- Black -->
<!-- PostScript: NotoSansCJKsc-Black -->
<NamedInstance flags="0x0" postscriptNameID="279" subfamilyNameID="278">
<coord axis="wght" value="900.0"/>
</NamedInstance>
</fvar>

</ttFont>

找出Noto Sans CJK各字重的weight值:

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
# afdko,见上文OTC index
otc2otf -r NotoSansCJK.ttc | grep jp
Font 0: NotoSansCJKjp-Thin.otf
Font 5: NotoSansCJKjp-Light.otf
Font 10: NotoSansCJKjp-DemiLight.otf
Font 15: NotoSansCJKjp-Medium.otf
Font 20: NotoSansCJKjp-Black.otf
Font 25: NotoSansCJKjp-Regular.otf
Font 30: NotoSansMonoCJKjp-Regular.otf
Font 35: NotoSansCJKjp-Bold.otf
Font 40: NotoSansMonoCJKjp-Bold.otf

ttx -y 0 -t OS/2 NotoSansCJK.ttc
<usWeightClass value="100"/>

ttx -y 5 -t OS/2 NotoSansCJK.ttc
<usWeightClass value="300"/>

ttx -y 10 -t OS/2 NotoSansCJK.ttc
<usWeightClass value="350"/>

ttx -y 25 -t OS/2 NotoSansCJK.ttc
<usWeightClass value="400"/>

ttx -y 15 -t OS/2 NotoSansCJK.ttc
<usWeightClass value="500"/>

ttx -y 35 -t OS/2 NotoSansCJK.ttc
<usWeightClass value="700"/>

ttx -y 20 -t OS/2 NotoSansCJK.ttc
<usWeightClass value="900"/>
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
<family lang="zh-Hans">
<font weight="100" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">NotoSansCJK-VF.otf.ttc
<axis tag="wght" stylevalue="100.0" />
</font>
<font weight="300" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">NotoSansCJK-VF.otf.ttc
<axis tag="wght" stylevalue="300.0" />
</font>
<font weight="350" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">NotoSansCJK-VF.otf.ttc
<axis tag="wght" stylevalue="350.0" />
</font>
<font weight="400" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">NotoSansCJK-VF.otf.ttc
<axis tag="wght" stylevalue="400.0" />
</font>
<font weight="500" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">NotoSansCJK-VF.otf.ttc
<axis tag="wght" stylevalue="500.0" />
</font>
<font weight="700" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">NotoSansCJK-VF.otf.ttc
<axis tag="wght" stylevalue="700.0" />
</font>
<font weight="900" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">NotoSansCJK-VF.otf.ttc
<axis tag="wght" stylevalue="900.0" />
</font>

<!-- ... -->
</family>

我记录下来的Noto Sans CJK和Noto Serif CJK字重数值表

测试

此处安利本人写的可变字体测试
variable-font-test-zh-tw