There are three basic layers to the build configuration for fonts:
The setup for getting a collection of fonts into a Fuchsia build is somewhat tricky. This is because
.ttf
files.The first layer establishes the catalogs of assets and metadata that are available for use in Fuchsia product builds.
The metadata and list of external sources for font files is maintained in a font data repo. For Fuchsia's open-source fonts, this is found at //fontdata
.
Note: The fontdata
repo is not part of Fuchsia's standard jiri
checkout; for editing, it must be checked out manually.
Whenever the contents of a font data repo change, Fuchsia‘s automated infrastructure automatically checks it out, fetches all of the fonts files that the repo describes, and bundles them into a CIPD package that is uploaded to Fuchsia’s CIPD server.
A font data repo contains the following files:
manifest.xml
This is a Jiri manifest file. In this case, it consists of a list of Git repos and revision IDs that contain the desired font files.
Note: Due to a technical limitation, a Jiri manifest cannot currently include external repos (e.g. on github.com) directly; repos must first be mirrored to fuchsia.googlesource.com/third_party/<repo-name>
.
Each imported repo is listed in a <project>
element.
Sample entry:
<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
: arbitrary name (doesn't affect anything)path
: determines where the repo is checked out relative to the root checkout directory.remote
: Git URL of remote reporemotebranch
: Branch name in remote reporevision
: Git commit hash at which to pin the checkoutcontents.json
{:#contents-json}These are instructions for Fuchsia's infrastructure to copy files from the checkout directory into the staging directory. Each entry of {destination, files}
defines a top-level folder in the staging directory, and the list of files that will be copied into it.
Sample entry:
{ "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}The font catalog file is a human-written bundle of metadata for all of the font families, assets, and typefaces in this font data repo.
For ease of editing, the JSON schema for .font_catalog.json
files is available at /src/fonts/tools/schemas/font_catalog.schema.json. The canonical schema is defined in Rust.
packing_script.py
{:#packing-script}Fuchsia's infrastructure invokes this script after populating the staging directory.
The script:
${catalog_name}.font_pkgs.json
file in the output directory.The script generally does not need to be modified.
As described above, an infrastructure recipe is automatically triggered whenever the fontdata
is modified. This recipe:
jiri init
and jiri update
.contents.json
.The bundle of open-source fonts is uploaded to the fuchsia/third_party/fonts
CIPD package.
The contents of the CIPD package make their way into a Fuchsia checkout via the prebuilts
jiri manifest:
<!-- Fonts --> <package name="fuchsia/third_party/fonts" version="git_revision:a10ce51e308f48c5e6d70ab5777434af46a5ddb8" path="prebuilt/third_party/fonts"/>
The version
ID for the fuchsia/third_party/fonts
package can be obtained from the “Tags” on latest instance on the CIPD package page.
//src/fonts/build/font_args.gni
declares several font-related build arguments. The two most important ones are
font_catalog_paths
: The locations of all .font_catalog.json
files.font_pkgs_paths
: The locations of all .font_pkgs.json
files.*.font_pkgs.json
files {:#font-pkgs-json}These files are designed to be parseable by GN's simple JSON parser, read_file
. They contain a list of entries of the form
{ "file_name": "AlphaSans-Regular.ttf", "safe_name": "alphasans-regular-ttf", "path_prefix": "alpha" },
file_name
: The name of a font file asset file. This is the canonical identifier for every font asset, and hence the key in this lookup table.safe_name
: A transformed version of the file name: converted to lowercase, with all special characters replaced with hyphens. This can be used to name Fuchsia packages.path_prefix
: Location of the asset's parent directory, relative to //prebuilt/third_party/fonts
.In //src/fonts/build/fonts.gni
, the contents of the .font_pkgs.json
files are merged into a single GN scope that can be used as a lookup table.
In addition to providing font assets directly to Font Provider's namespace, there is also the option of creating single-font Fuchsia packages. This is exactly what it sounds like: a package
containing just a single font in its resources
and nothing else.
This can be used for ephemeral font delivery, in conjunction with fuchsia.pkg.FontResolver
. (TODO: Link to more docs.)
Note: Ephemeral packages are currently disabled in production builds.
All possible font packages are predeclared in //src/fonts/packages/BUILD.gn
.
font-package-<font-safe-name>
(see safe_name
above), e.g. font-package-roboto-regular-ttf
.//src/fonts/packages:<package-name>
, e.g. //src/fonts/packages:font-package-roboto-regular.ttf
.fuchsia-pkg://fuchsia.com/<package-name>
, e.g. fuchsia-pkg://fuchsia.com/font-package-roboto-regular-ttf
.The second layer is optional; it is mainly intended to save some repetition. Instead of having to refer to font assets and packages individually, reusable groups are declared in //src/fonts/groups/BUILD.gn
.
local_font_bundle
{:#local_font_bundle}Defined in fonts.gni
A local font bundle is a set of font assets that are placed directly into the Font Provider's incoming namespace using build-time configuration data (config_data()
).
Most local font bundles are declared in //src/fonts/collections/BUILD.gn
.
Example:
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="src/fonts/collections/BUILD.gn" indented_block="local_font_bundle\(\"open-fonts-local\"\)" adjust_indentation="auto" %}
font_package_group
{:#font_package_group}Defined in fonts.gni
When referring to multiple font packages, the GN template font_package_group
obviates the need to derive or look up fonts assets' package names. Just list the names of the assets, as in this example in //src/fonts/groups/BUILD.gn
:
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="src/fonts/groups/BUILD.gn" indented_block="font_package_group\(\"roboto-slab\"\)" adjust_indentation="auto" %}
The target //src/fonts/groups:roboto-slab
contains a group
of all of the package
s corresponding to the listed asset names.
Finally, every product target that uses fonts needs to be configured with the specific font assets and metadata that it will include.
A .fontcfg.json
(or .fontcfg.json5
) file contains a human-written set of product-specific font settings. There is a JSON schema available for a better editor experience.
Currently, the main purpose of this file is to define a specific font fallback chain, i.e. a preferred sequence of typefaces to use when an exact match for the client's typeface request is not available.
Here is a basic example of a fallback chain from //src/fonts/collections/open-fonts-collection.fontcfg.json5
:
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="src/fonts/collections/open-fonts-collection.fontcfg.json5" regexp="fallback_chain: \[[^\]]*?\]" adjust_indentation="auto" %}
If an asset file contains multiple typefaces, a single typeface can be referenced by using a JSON object instead of just the file name:
fallback_chain: [ "SomeCompleteFont-Bold.ttf", { file_name: "NotoSansCJK-Regular.ttc", index: 1}, ]
The fallback chain is defined manually. Some guidelines to follow:
sans-serif
, serif
, and monospace
font families.font_collection
{:#font_collection}Defined in fonts.gni
After any needed local_font_bundle
s, font packages
, and/or font_package_group
s have been declared, they are assembled into a font_collection
.
(See fonts.gni for complete documentation.)
font_packages
: GN labels of font package
s and/or font_package_group
s that are in universe_package_labels
for the target product.local_font_bundles
: GN labels of local_font_bundle
s (or group
s thereof) for the target product. These will be included in the font provider's config data.local_asset_names
: List of local font asset names (creates an ad-hoc local_font_bundle
).product_config_path
: Path to a JSON file containing product-specific font configuration, including a fallback chain.manifest_prefix
: A prefix for the generated font manifest file name. Defaults to "all"
.A font_collection
traverses the transitive closure all of the font assets, packages, and groups that it contains. It collects their GN metadata to build lists of which fonts are available as local files and which as font packages.
The template passes this information to the font manifest generator (GN template, Rust source), along with the paths of the font catalogs and of all of the font assets.
The manifest generator selects from the font catalogs the predefined metadata for the font files that are included in the font_collection
. It also reads every included font file and extracts the list of code points (character set) that each font file supports.
All of this data is assembled into a single .font_manifest.json
file that supplied to the font provider service using a config_data
rule.
A font_collection
produces the following artifacts:
<manifest-prefix>.font_manifest.json
: This is the assembly of metadata described above. It is supplied to the font provider service using a config_data
rule.font_packages.json
. This file lists all included single-font Fuchsia packages and is provided to pkg-resolver
using a config_data
rule. This determines the font packages exposed through fuchsia.pkg.FontResolver
.The GN target created by font_collection
contains the config_data
for the two JSON files above, as well as config_data targets for the local font assets.
If a product only uses local fonts, it is sufficient to add the font_collection
target to the product's dependency labels (usually to base_package_labels
).
If a product also uses font package
s and/or font_package_group
s, those targets must be added explicitly to either base_package_labels
or universe_package_labels
, depending on whether the packages are meant to be ephemeral.