实现自定义字体

从 Android 15 开始,可变字体会在运行时呈现,并且效率和精细度更高。在此更新中,供应商必须将新的可变字体配置添加到 font_fallback.xml,而不是 fonts.xml,因为 fonts.xml 已被废弃。如需了解详情,请参阅对可变字体的支持

在 Android 11 及更低版本中,更新 AOSP 中设备已安装的字体文件(位于 /system/fonts 分区中)或供应商分区中设备已安装的字体文件(位于 /product/fonts/system/fonts 分区中)需要由原始设备制造商 (OEM) 进行系统更新。此要求对表情符号的兼容性具有重大影响。在 Android 12 中,借助 FontManager 系统服务,您可以管理已安装的字体文件,并且无需进行系统更新即可更新设备已安装的字体文件。

Android 12 中存在三个进程的交互:FontManagerServiceFont UpdaterApplication

FontManagerService 是系统服务器中的中央管理系统。FontManagerService 存储着每位用户的最新系统字体设置。

FontUpdater 是受 signature|privileged 权限检查信任的可插入字体更新程序。FontUpdater 会与 FontManagerService 通信,以获取、安装、移除或更新当前系统字体设置。FontUpdater 可以通过进程间通信 (IPC) 机制传递新字体文件的内容。FontManagerService 将内容保存到全局可读的存储位置,例如 /data/fonts 文件中。此存储空间受到保护,只能由 FontManagerService 通过 SELinux 政策对其执行写入操作。

Application 类启动时,它会将系统字体设置作为 bindApplication 方法的参数传递;然后,它会初始化字体设置,供应用进程使用。

对可变字体的支持

从 Android 15 开始,可变字体配置在 font_fallback.xml 中使用以下格式指定:

<family lang="und-Ethi" supportedAxes="wght,ital">
    <font>NotoSansEthiopic-VF.ttf</font>
</family>

在此格式中,可变字体具有静态字体的所有属性,并额外包含一个 supportedAxes 属性。supportedAxes 属性是支持的轴标记的逗号分隔列表。在 Android 15 中,只能指定 wghtital 轴。

如果未指定 supportedAxes 属性,font 节点将作为使用 axis 子项指定的可变字体单个实例的静态字体。

如果指定了 supportedAxes 属性,系统会在运行时为给定的粗细和样式值动态创建字体实例。

开发者可以使用 android.graphics.fonts.SystemFonts#getAvailableFonts Java API 或 ASystemFontIterator_open NDK API 获取系统安装的字体文件列表。如需了解支持此更新的开发者 API,请参阅改进的 OpenType 可变字体 APIbuildVariableFamily

自定义字体

有些 OEM 会在 AOSP 中安装或替换字体文件以展示自己的品牌。Android 12 支持此功能,但增加了保持更新设备中的表情符号字体的要求。不修改或不更新表情符号字体文件的 OEM 无需使用此功能。

Google 会更新字体文件,尤其是通过 GMS Core 更新 NotoColorEmoji 文件,因此请不要修改或移除 /system 分区中的 NotoColorEmoji.ttf 文件,也不要从 /frameworks/base/data/fonts/fonts.xml 移除该文件。请注意,您可以通过以下三种方式自定义字体:

  1. NotoColorEmoji.ttf 文件替换为 OEM 品牌的表情符号字体。
  2. 根据当地市场的需求修改 NotoColorEmoji.ttf 文件。
  3. 替换或修改其他字体文件。

如果不打算在 AOSP 中修改表情符号字体,就无需执行任何操作。如果要自定义表情符号字体,请按照以下几部分的说明操作。

将 NotoColorEmoji.ttf 替换为 OEM 品牌的表情符号字体

如需将 NotoColorEmoji.ttf 文件替换为 OEM 品牌的表情符号字体文件,请将表情符号字体放在后备字体链的前面:

  1. 将您自己的字体(名为 OEMCustomEmoji.ttf)放到 /system 分区中。
  2. 修改 /frameworks/base/data/fonts/fonts.xml(以及 Android 15 及更高版本中的 /frameworks/base/data/fonts/font-fallback.xml),如以下代码所示:

    <family lang="ko">
    <font weight="400" style="normal" index="1">NotoSansCJK-Regular.ttc</font>
    </family>
    <!-- ADD FOLLOWING LINE -->
    <family lang="und-Zsye">
       <font weight="400" style="normal">OEMCustomEmoji.ttf</font>
    </family>
    <!-- END OF MODIFICATION -->
    <family lang="und-Zsye">
       <font weight="400" style="normal">NotoColorEmoji.ttf</font>
    </family>
    <family lang="und-Zsym">
       <font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted2.ttf</font>
    </family>
    

根据当地市场的需求修改 NotoColorEmoji.ttf

请按照以下步骤,根据当地市场的需求进行自定义:

  1. 使用其他名称创建您自己的 NotoColorEmoji 文件;例如,将其命名为 Modified\_NotoColorEmoji.ttf
  2. 将该文件放在原始 NotoColorEmoji.ttf 文件的前面。

当您执行完第 2 步之后,系统将显示 Modified\NotoColorEmoji.ttf 支持的修改后的字形,而不是原始 NotoColorEmoji.ttf 支持的字形。Google 的建议如下:

  • 仅包含此字体中必需的字形。
  • 将未经修改的字形委托给原始 NotoColorEmoji.ttf 文件,以便设备接收未来表情符号版本中做出的任何设计修正。

移除字形:如需从 NotoColorEmoji.ttf 文件中移除字形,请按照第 1 步和第 2 步操作,并在 cmap 中指定 glyph ID = 0

使用地区标志:如果目标字形是地区标志,请将字形 ID 指定为未知的国家/地区代码。(请使用 country code = "ZZ"。)

指定 tofu 字形:如果想使用 tofu 字形,可以明确指定 tofu 字形 ID。如果指定 glyphID = 0,相关应用会将其解读为“字形不可用”。例如,使用此属性时,Paint#hasGlyph 应用会返回 false

替换或修改其他字体文件

如需替换或修改其他字体,自定义过程与根据当地市场的需求修改 TTF 文件类似。AOSP 中在运行时更新的未知字体文件会被忽略,也不会更新。Google 会忽略您的设备中的未知字体,包括在 AOSP 中的原始字体基础上修改过的字体文件。

虽然字体更新由 Google 在 GMS Core 中完成,但常规字体更新机制面向所有 OEM 开放。OEM 可以按照满足前提条件为字体文件签名进行运行时字体更新部分所述的步骤,安装其他字体更新程序。

满足前提条件

字体更新机制使用 fs-verity Linux 内核功能。验证您的设备是否符合 fs-verity 的要求,并在设备中添加证书。

为字体文件签名

由于字体文件是存在风险的资源,因此必须通过可信的密钥对其进行验证。请仔细检查所有要更新的字体文件,然后使用您的私钥对其签名。此签名必须符合 fs-verity 的要求。

进行运行时字体更新

字体更新由 FontManager 系统应用执行。FontManager 应用提供最新的已安装系统字体状态,以及使用签名更新字体文件的功能。如需调用更新应用,请将 UPDATE_FONT signature|privileged 权限添加到应用许可名单清单中。

为应用的更新程序函数提供 UPDATE_FONT signature|privileged 权限。