build 配置

字体的构建配置包含三个基本层:

  1. 通用基础架构和元数据
  2. 可重复使用的字体包
  3. 特定于产品的配置

背景

将字体集合添加到 Fuchsia build 的设置有点棘手。这是因为

  • Fuchsia 的开发者指南倾向于将预构建的二进制文件保留在 CIPD 中,而不是保存在 Git 代码库中。
  • Fuchsia 的字体文件有多种来源。
  • 在正在运行的 Fuchsia 系统中,仅为客户端提供包含 .ttf 文件的目录是不够的。
    • 字体文件并非完全自描述;一些重要的元数据(如名称、别名和样式属性)必须从外部获取。
    • 其他元数据部分(例如字体支持的码位列表)可在运行时从每个字体文件获取,但在构建时进行预计算会更高效。
    • 客户端应用可能不知道自己要查找的确切字体;它们可能想要请求特定的通用字体系列样式和语言,但实际上并没有想到特定的字体文件。
  • 字体元数据冗长、经常重复,并且易于维护。与要求产品定位和各个组件的维护人员管理他们自己的副本相比,建议仅为 Fuchsia 上可用的每种常见字体定义一次此元数据。

通用基础架构和元数据

第一层建立可在 Fuchsia 产品 build 中使用的资产和元数据目录。

字体数据存储库

字体文件的元数据和外部来源列表保存在字体数据代码库中。如需了解 Fuchsia 的开源字体,请参阅 //fontdata

每当字体数据存储库的内容发生更改时,Fuchsia 的自动化基础架构都会自动进行检查,提取代码库描述的所有字体文件,并将它们捆绑到上传到 Fuchsia CIPD 服务器的 CIPD 软件包中。

字体数据存储库包含以下文件:

manifest.xml

这是一个 Jiri 清单文件。在本例中,它由一系列 Git 代码库和包含所需字体文件的修订版本 ID 组成。

每个导入的代码库都列在一个 <project> 元素中。

示例条目:

<project
    name="github.com/googlefonts/noto-cjk"
    path="github.com/googlefonts/noto-cjk"
    remote="https://fuchsia.googlesource.com/third_party/github.com/googlefonts/noto-cjk"
    remotebranch="upstream/master"
    revision="be6c059ac1587e556e2412b27f5155c8eb3ddbe6"/>
  • name:任意名称(不影响任何内容)
  • path:确定相对于根 checkout Directory 检出代码库的位置。
  • remote:远程代码库的 Git 网址
  • remotebranch:远程代码库中的分支名称
  • revision:要固定结账位置的 Git 提交哈希值

contents.json

以下说明介绍了 Fuchsia 基础架构如何将文件从结账目录复制到暂存目录。{destination, files} 的每个条目都定义暂存目录中的顶层文件夹,以及将复制到该文件夹中的文件列表。

示例条目:

{
    "destination": "material",
    "files": [
      "github.com/google/material-design-icons/iconfont/MaterialIcons-Regular.ttf",
      "github.com/google/material-design-icons/LICENSE"
    ]
}

${catalog_name}.font_catalog.json

字体目录文件是人工编写的元数据包,其中包含此字体数据代码库中所有字体系列、资源和字体的元数据。

为便于修改,.font_catalog.json 文件的 JSON 架构位于 /src/fonts/tools/schemas/font_catalog.schema.json。 规范架构使用 Rust 定义

packing_script.py

Fuchsia 的基础架构会在填充暂存目录后调用此脚本。

脚本:

脚本通常不需要修改。

基础架构方案

如上所述,每当修改 fontdata 时,基础架构方案都会自动触发。食谱:

  1. 使用 jiri initjiri update 检出引用的代码库。
  2. 根据 contents.json 暂存已检出的文件。
  3. 调用打包脚本,将文件写入输出目录。
  4. 将输出目录的内容上传到 CIPD

CIPD 软件包

开源字体软件包会上传到 fuchsia/third_party/fonts CIPD 软件包

Jiri 预构建文件

CIPD 软件包的内容会通过 prebuilts jiri 清单进入 Fuchsia 结账流程:

    <!--   Fonts -->
    <package name="fuchsia/third_party/fonts"
             version="git_revision:a10ce51e308f48c5e6d70ab5777434af46a5ddb8"
             path="prebuilt/third_party/fonts"/>

fuchsia/third_party/fonts 软件包的 version ID 可以从 CIPD 软件包页面上最新实例的“代码”中获取。

fuchsia/third_party/fonts CIPD 软件包的屏幕截图,突出显示了以前缀“git_revision:”开头的实例的标记

全局 GN 参数

//src/fonts/build/font_args.gni 声明了多个与字体相关的构建参数。其中最重要的两个

*.font_pkgs.json 个文件

这些文件设计为可由 GN 的简单 JSON 解析器 read_file 解析。其中包含以下形式的条目列表:

    {
      "file_name": "AlphaSans-Regular.ttf",
      "safe_name": "alphasans-regular-ttf",
      "path_prefix": "alpha"
    },
  • file_name:字体文件资源文件的名称。这是每个字体资源的规范标识符,因此也是此对照表中的键。
  • safe_name:文件名的转换版本:转换为小写,并将所有特殊字符替换为连字符。此属性可用于为 Fuchsia 软件包命名。
  • path_prefix:资产的父目录相对于 //prebuilt/third_party/fonts 的位置。

//src/fonts/build/fonts.gni 中,.font_pkgs.json 文件的内容会合并到单个 GN 作用域中,该作用域可用作对照表。

字体软件包

除了直接向字体提供程序的命名空间提供字体资源外,您还可以选择创建单字体 Fuchsia 软件包。顾名思义,packageresources 中仅包含一种字体,不包含任何其他内容。

它可与 fuchsia.pkg.FontResolver 结合使用,用于临时字体传送。(TODO:链接到更多文档。)

所有可能的字体包都在 //src/fonts/packages/BUILD.gn 中预先声明。

  • 每个软件包名称的格式为 font-package-<font-safe-name>(请参阅上文的 safe_name),例如 font-package-roboto-regular-ttf
  • 因此,软件包的 GN 目标为 //src/fonts/packages:<package-name>,例如 //src/fonts/packages:font-package-roboto-regular.ttf
  • 每个软件包的网址都采用 fuchsia-pkg://fuchsia.com/<package-name> 格式,例如 fuchsia-pkg://fuchsia.com/font-package-roboto-regular-ttf

local_font_bundle

fonts.gni 中定义

本地字体包是一组使用 config_data() 直接放置在字体提供程序的命名空间中的字体资源。

大多数本地字体包是在 //src/fonts/collections/BUILD.gn 中声明的。

示例:

```gn
local_font_bundle("small-open-fonts-local") {
  asset_names = [
    "MaterialIcons-Regular.ttf",
    "Roboto-Regular.ttf",
    "Roboto-Light.ttf",
    "Roboto-Medium.ttf",
    "RobotoMono-Regular.ttf",
    "RobotoSlab-Regular.ttf",
  ]
}

产品专用的字体配置

最后,每个使用字体的产品定位都需要使用其将包含的特定字体资源和元数据进行配置。

字体产品配置文件

.fontcfg.json(或 .fontcfg.json5)文件包含一组人工编写的产品专用字体设置。您可以使用 JSON 架构提供更好的编辑器体验。

目前,此文件的主要用途是定义一个特定的字体回退链,即当客户端的字体请求无法完全匹配时要使用的首选字体序列。

下面是来自 //src/fonts/collections/open-fonts-collection.fontcfg.json5 的回退链的基本示例:

fallback_chain: [
        ///
        ///
        /// SANS SERIF LATIN
        "Roboto-Regular.ttf",
        "Roboto-Black.ttf",
        "Roboto-BlackItalic.ttf",
        "Roboto-Bold.ttf",
        "Roboto-BoldItalic.ttf",
        "Roboto-Italic.ttf",
        "Roboto-Light.ttf",
        "Roboto-LightItalic.ttf",
        "Roboto-Medium.ttf",
        "Roboto-MediumItalic.ttf",
        "Roboto-Thin.ttf",
        "Roboto-ThinItalic.ttf",
        "RobotoCondensed-Regular.ttf",
        "RobotoCondensed-Bold.ttf",
        "RobotoCondensed-BoldItalic.ttf",
        "RobotoCondensed-Italic.ttf",
        "RobotoCondensed-Light.ttf",
        "RobotoCondensed-LightItalic.ttf",
        "DroidSans-Regular.ttf",
        "DroidSans-Bold.ttf",

        ///
        ///
        /// SANS-SERIF NON-LATIN
        "NotoNaskhArabicUI-Regular.ttf",
        "NotoSansArmenian-Regular.ttf",
        "NotoSansEthiopic-Regular.ttf",
        "NotoSansGeorgian-Regular.ttf",
        "NotoSansHebrew-Regular.ttf",
        "NotoSansThaiUI-Regular.ttf",

        // All Indian scripts should come after Devanagari, due to shared danda characters.
        "NotoSansDevanagariUI-Regular.ttf",
        "NotoSansBengaliUI-Regular.ttf",
        "NotoSansGujaratiUI-Regular.ttf",
        "NotoSansKannada-Regular.ttf",
        "NotoSansMalayalamUI-Regular.ttf",
        "NotoSansTamilUI-Regular.ttf",
        "NotoSansTelugu-Regular.ttf",

        ///
        ///
        /// SANS SERIF CJK
        {
            full_name: "Noto Sans CJK SC",
        },
        {
            full_name: "Noto Sans CJK TC",
        },
        {
            full_name: "Noto Sans CJK HK",
        },
        {
            full_name: "Noto Sans CJK KR",
        },
        {
            full_name: "Noto Sans CJK JP",
        },
        {
            full_name: "Noto Sans CJK SC Bold",
        },
        {
            full_name: "Noto Sans CJK TC Bold",
        },
        {
            full_name: "Noto Sans CJK HK Bold",
        },
        {
            full_name: "Noto Sans CJK KR Bold",
        },
        {
            full_name: "Noto Sans CJK JP Bold",
        },
        {
            full_name: "Noto Sans CJK SC Black",
        },
        {
            full_name: "Noto Sans CJK TC Black",
        },
        {
            full_name: "Noto Sans CJK HK Black",
        },
        {
            full_name: "Noto Sans CJK KR Black",
        },
        {
            full_name: "Noto Sans CJK JP Black",
        },
        {
            full_name: "Noto Sans CJK SC DemiLight",
        },
        {
            full_name: "Noto Sans CJK TC DemiLight",
        },
        {
            full_name: "Noto Sans CJK HK DemiLight",
        },
        {
            full_name: "Noto Sans CJK KR DemiLight",
        },
        {
            full_name: "Noto Sans CJK JP DemiLight",
        },
        {
            full_name: "Noto Sans CJK SC Light",
        },
        {
            full_name: "Noto Sans CJK TC Light",
        },
        {
            full_name: "Noto Sans CJK HK Light",
        },
        {
            full_name: "Noto Sans CJK KR Light",
        },
        {
            full_name: "Noto Sans CJK JP Light",
        },
        {
            full_name: "Noto Sans CJK SC Medium",
        },
        {
            full_name: "Noto Sans CJK TC Medium",
        },
        {
            full_name: "Noto Sans CJK HK Medium",
        },
        {
            full_name: "Noto Sans CJK KR Medium",
        },
        {
            full_name: "Noto Sans CJK JP Medium",
        },
        {
            full_name: "Noto Sans CJK SC Thin",
        },
        {
            full_name: "Noto Sans CJK TC Thin",
        },
        {
            full_name: "Noto Sans CJK HK Thin",
        },
        {
            full_name: "Noto Sans CJK KR Thin",
        },
        {
            full_name: "Noto Sans CJK JP Thin",
        },

        ///
        ///
        /// SERIF LATIN
        "RobotoSlab-Regular.ttf",
        "RobotoSlab-Bold.ttf",
        "RobotoSlab-Light.ttf",
        "RobotoSlab-Thin.ttf",
        "DroidSerif-Regular.ttf",
        "DroidSerif-Bold.ttf",
        "DroidSerif-BoldItalic.ttf",
        "DroidSerif-Italic.ttf",

        ///
        ///
        /// SERIF CJK
        {
            full_name: "Noto Serif CJK SC",
        },
        {
            full_name: "Noto Serif CJK TC",
        },
        {
            full_name: "Noto Serif CJK KR",
        },
        {
            full_name: "Noto Serif CJK JP",
        },
        {
            full_name: "Noto Serif CJK SC Bold",
        },
        {
            full_name: "Noto Serif CJK TC Bold",
        },
        {
            full_name: "Noto Serif CJK KR Bold",
        },
        {
            full_name: "Noto Serif CJK JP Bold",
        },
        {
            full_name: "Noto Serif CJK SC Black",
        },
        {
            full_name: "Noto Serif CJK TC Black",
        },
        {
            full_name: "Noto Serif CJK KR Black",
        },
        {
            full_name: "Noto Serif CJK JP Black",
        },
        {
            full_name: "Noto Serif CJK SC ExtraLight",
        },
        {
            full_name: "Noto Serif CJK TC ExtraLight",
        },
        {
            full_name: "Noto Serif CJK KR ExtraLight",
        },
        {
            full_name: "Noto Serif CJK JP ExtraLight",
        },
        {
            full_name: "Noto Serif CJK SC Light",
        },
        {
            full_name: "Noto Serif CJK TC Light",
        },
        {
            full_name: "Noto Serif CJK KR Light",
        },
        {
            full_name: "Noto Serif CJK JP Light",
        },
        {
            full_name: "Noto Serif CJK SC Medium",
        },
        {
            full_name: "Noto Serif CJK TC Medium",
        },
        {
            full_name: "Noto Serif CJK KR Medium",
        },
        {
            full_name: "Noto Serif CJK JP Medium",
        },
        {
            full_name: "Noto Serif CJK SC SemiBold",
        },
        {
            full_name: "Noto Serif CJK TC SemiBold",
        },
        {
            full_name: "Noto Serif CJK KR SemiBold",
        },
        {
            full_name: "Noto Serif CJK JP SemiBold",
        },

        ///
        ///
        /// MONOSPACE LATIN
        "RobotoMono-Bold.ttf",
        "RobotoMono-BoldItalic.ttf",
        "RobotoMono-Italic.ttf",
        "RobotoMono-Light.ttf",
        "RobotoMono-LightItalic.ttf",
        "RobotoMono-Regular.ttf",
        "RobotoMono-Medium.ttf",
        "RobotoMono-MediumItalic.ttf",
        "RobotoMono-Thin.ttf",
        "RobotoMono-ThinItalic.ttf",
        "DroidSansMono-Regular.ttf",

        ///
        ///
        /// MONOSPACE CJK
        {
            full_name: "Noto Sans Mono CJK SC",
        },
        {
            full_name: "Noto Sans Mono CJK TC",
        },
        {
            full_name: "Noto Sans Mono CJK HK",
        },
        {
            full_name: "Noto Sans Mono CJK KR",
        },
        {
            full_name: "Noto Sans Mono CJK JP",
        },
        {
            full_name: "Noto Sans Mono CJK SC Bold",
        },
        {
            full_name: "Noto Sans Mono CJK TC Bold",
        },
        {
            full_name: "Noto Sans Mono CJK HK Bold",
        },
        {
            full_name: "Noto Sans Mono CJK KR Bold",
        },
        {
            full_name: "Noto Sans Mono CJK JP Bold",
        },

        ///
        ///
        /// CURSIVE LATIN
        "Quintessential-Regular.ttf",

        ///
        ///
        /// EMOJI
        "NotoColorEmoji.ttf",

        ///
        ///
        /// SYMBOLS
        "NotoSansSymbols-Regular.ttf",
        "NotoSansSymbols2-Regular.ttf",
    ]

如果素材资源文件包含多个字体,则可以通过使用 JSON 对象(而不只是文件名)来引用单个字体:

fallback_chain: [
    "SomeCompleteFont-Bold.ttf",
    { file_name: "NotoSansCJK-Regular.ttc", index: 1},
]

后备链是手动定义的。请遵循以下准则:

  • 为每个支持的脚本至少添加一种字体。(支持的脚本集因产品而异。)
  • 请尝试至少涵盖 sans-serifserifmonospace 字体系列。
  • 当覆盖率重叠时,请将更具体的素材资源放在列表中较高的位置。例如,所有 Noto Sans 脚本专用字体都有适用于 ASCII 范围的字形,但 Noto Sans Latin 变体应首先显示。
  • 当存在重叠时,将较小的字体文件放在列表中更靠前的位置。这样可以减少加载回退字体时的界面卡顿。

font_collection

fonts.gni 中定义

在声明任何所需的 local_font_bundle 和/或字体 packages 后,它们会组合到 font_collection 中。

输入源

(如需查看完整文档,请参阅 fonts.gni。)

  • font_packages:目标产品 universe_package_labels 中的字体 package 的 GN 标签。
  • local_font_bundles:目标商品的 local_font_bundle 的 GN 标签。这些信息将包含在字体提供程序的配置数据中。
  • local_asset_names:本地字体素材资源名称的列表(创建一个临时 local_font_bundle)。
  • product_config_path:包含特定于产品的字体配置(包括回退链)的 JSON 文件的路径。
  • manifest_prefix:生成的字体清单文件名称的前缀。默认设置为 "all"

内部原理

font_collection 会遍历包含它包含的所有字体资源和软件包的传递闭包。它会收集其 GN 元数据,以构建映射到本地文件中字体包的字体列表。

该模板会将这些信息连同字体目录和所有字体资源的路径传递给字体清单生成器(GN 模板Rust 源代码)。

清单生成器从字体中选择 font_collection 中包含的字体文件的预定义元数据。它还会读取包含的所有字体文件,并提取每个字体文件支持的码位(字符集)列表。

所有这些数据都会组合到使用 config_data 规则提供给字体提供程序服务的单个 .font_manifest.json 文件中。

输出

font_collection 会生成以下工件:

  • <manifest-prefix>.font_manifest.json:这是上述元数据的集合。它通过 config_data 规则提供给字体提供程序服务。
  • font_packages.json。此文件列出了所有包含的单字体 Fuchsia 软件包,并使用 config_data 规则将其提供给 pkg-resolver。这决定了通过 fuchsia.pkg.FontResolver 公开的字体包。

font_collection 创建的 GN 目标包含上述两个 JSON 文件的 config_data,以及本地字体资源的 config_data 目标。

如果产品仅使用本地字体,只需将 font_collection 目标添加到产品的依赖项标签(通常为 base_package_labels)中即可。

如果产品还使用字体 package,则必须将这些目标显式添加到 base_package_labelsuniverse_package_labels,具体取决于软件包是否为临时软件包。