实现自定义字体

从 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 权限检查获得信任。FontUpdaterFontManagerService 通信,以获取、安装、移除或更新当前的系统字体设置。FontUpdater 可以通过进程间通信 (IPC) 机制传递新的字体文件内容。FontManagerService 将内容保存到世界可读的存储位置,例如 /data/fonts 文件中。此存储受到保护。根据 SELinux 策略,只有 FontManagerService 可以写入它。

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"。)

制作豆腐字形:如果您想使用豆腐字形,可以显式指定豆腐字形 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 权限。