There are three basic layers to the build configuration for fonts:

1. [Common infrastructure and metadata](https://fuchsia.dev/fuchsia-src/development/internationalization/fonts/build#common-infra)
2. [Reusable font bundles](https://fuchsia.dev/fuchsia-src/development/internationalization/fonts/build#reusable)
3. [Product-specific configurations](https://fuchsia.dev/fuchsia-src/development/internationalization/fonts/build#product-specific)

## Background

The setup for getting a collection of fonts into a Fuchsia build is somewhat
tricky. This is because

- Fuchsia's developer guidelines prefer to keep prebuilt binary files in CIPD, not in Git repos.
- Fuchsia's font files come from a variety of sources.
- On a running Fuchsia system, it's not sufficient to just offer clients a directory full of `.ttf` files.
  - Font files are not fully self-describing; there is important metadata (such as names, aliases, and style properties) that must be obtained externally.
  - Other pieces of metadata, such as the list of [code points](https://fuchsia.dev/fuchsia-src/development/internationalization/fonts#gloss-code-point) supported by a font, could be obtained at runtime from each font file, but it would be much more efficient to precompute them at build time.
  - Client applications might not know exactly what font they are looking for; they might want to ask for a particular [generic font family](https://fuchsia.dev/fuchsia-src/development/internationalization/fonts#gloss-generic-font-family), [style](https://fuchsia.dev/fuchsia-src/development/internationalization/fonts#gloss-style), and language, but not actually have a specific font file in mind.
- Font metadata is verbose, often repetitive, and error-prone to maintain. It is preferable to define this metadata just once for every common font available on Fuchsia, than to ask maintainers of product targets and individual components to manage their own copies.

## Common infrastructure and metadata

The first layer establishes the catalogs of assets and metadata that are
available for use in Fuchsia product builds.

### Font data repo

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`](https://fuchsia.googlesource.com/fontdata/).

> [!NOTE]
> **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](https://fuchsia.dev/fuchsia-src/development/internationalization/fonts/build#recipe) 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](https://fuchsia.googlesource.com/jiri/+/HEAD/manifest.md).
In this case, it consists of a list of Git repos and revision IDs that contain
the desired font files.

> [!NOTE]
> **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"
        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 repo
- `revision`: Git commit hash at which to pin the checkout

#### `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`

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](https://cs.opensource.google/fuchsia/fuchsia/+/main:/src/fonts/tools/schemas/font_catalog.schema.json).
The canonical schema is
[defined in Rust](https://cs.opensource.google/fuchsia/fuchsia/+/main:/src/fonts/tools/manifest_generator/src/font_catalog.rs).

#### `packing_script.py`

Fuchsia's infrastructure invokes this script after populating the *staging
directory*.

The script:

- Copies all font and license files from the staging directory to the output directory.
- Generates a [`${catalog_name}.font_pkgs.json`](https://fuchsia.dev/fuchsia-src/development/internationalization/fonts/build#font-pkgs-json) file in the output directory.

The script generally does not need to be modified.

### Infrastructure recipe

As described above, an infrastructure recipe is automatically triggered whenever
the `fontdata` is modified. This recipe:

1. Checks out the referenced repos using `jiri init` and `jiri update`.
2. Stages the checked out files according to [`contents.json`](https://fuchsia.dev/fuchsia-src/development/internationalization/fonts/build#contents-json).
3. Invokes the [packing script](https://fuchsia.dev/fuchsia-src/development/internationalization/fonts/build#packing-script) to write files to the output directory.
4. Uploads the contents of the output directory to [CIPD](https://fuchsia.dev/fuchsia-src/development/internationalization/fonts/build#cipd-package).

### CIPD package

The bundle of open-source fonts is uploaded to the
[`fuchsia/third_party/fonts` CIPD package](https://chrome-infra-packages.appspot.com/p/fuchsia/third_party/fonts/+/).

### Jiri prebuilts

The contents of the CIPD package make their way into a Fuchsia checkout via the
[`prebuilts` jiri manifest](https://fuchsia.googlesource.com/integration/+/HEAD/prebuilts):

        <!--   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](https://chrome-infra-packages.appspot.com/p/fuchsia/third_party/fonts/+/).

![Screenshot of fuchsia/third_party/fonts CIPD package, highlighting the instance's tag, which starts with the prefix 'git_revision:'](https://fuchsia.dev/static/fuchsia-src/development/internationalization/fonts/fonts-cipd-tag.png)

### Global GN arguments

[`//src/fonts/build/font_args.gni`](https://cs.opensource.google/fuchsia/fuchsia/+/main:/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`](https://fuchsia.dev/fuchsia-src/development/internationalization/fonts/build#font-catalog-json) files.
- `font_pkgs_paths`: The locations of all [`.font_pkgs.json`](https://fuchsia.dev/fuchsia-src/development/internationalization/fonts/build#font-pkgs-json) files.

#### `*.font_pkgs.json` files

These files are designed to be parseable by GN's simple JSON parser,
[`read_file`](https://gn.googlesource.com/gn/+/HEAD/docs/reference.md#func_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`](https://cs.opensource.google/fuchsia/fuchsia/+/main:/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.

### Font packages

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]
> **Note:** Ephemeral packages are currently disabled in production builds.

All possible font packages are predeclared in
[`//src/fonts/packages/BUILD.gn`](https://cs.opensource.google/fuchsia/fuchsia/+/main:/src/fonts/packages/BUILD.gn).

- Each package name is of the form `font-package-<font-safe-name>` (see [`safe_name`](https://fuchsia.dev/fuchsia-src/development/internationalization/fonts/build#font-pkgs-json) above), e.g. `font-package-roboto-regular-ttf`.
- The GN target for a package is therefore `//src/fonts/packages:<package-name>`, e.g. `//src/fonts/packages:font-package-roboto-regular.ttf`.
- Each package's URL is of the form `fuchsia-pkg://fuchsia.com/<package-name>`, e.g. `fuchsia-pkg://fuchsia.com/font-package-roboto-regular-ttf`.

### `local_font_bundle`

*Defined in [fonts.gni](https://cs.opensource.google/fuchsia/fuchsia/+/main:/src/fonts/build/fonts.gni)*

A local font bundle is a set of font assets that are placed directly into the
Font Provider's namespace using
[`config_data()`](https://fuchsia.dev/docs/development/components/configuration/config_data).

Most local font bundles are declared in
[`//src/fonts/collections/BUILD.gn`](https://cs.opensource.google/fuchsia/fuchsia/+/main:/src/fonts/collections/BUILD.gn).

Example:

    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",
      ]
    }

## Product-specific font configurations

Finally, every
[product target](https://fuchsia.dev/docs/development/build/software_assembly/build_configuration#products)
that uses fonts needs to be configured with the specific font assets and
metadata that it will include.

### Font product config files

A `.fontcfg.json` (or `.fontcfg.json5`) file contains a human-written set of
product-specific font settings. There is a
[JSON schema](https://cs.opensource.google/fuchsia/fuchsia/+/main:/src/fonts/tools/schemas/fontcfg.schema.json) 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`](https://cs.opensource.google/fuchsia/fuchsia/+/main:/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",
        ]

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:

- Include at least one font for every supported script. (The set of supported scripts varies by product.)
- Try to cover at least the `sans-serif`, `serif`, and `monospace` font families.
- When there is overlapping coverage, put more specific assets higher in the list. For example, all of the *Noto Sans* script-specific fonts have glyphs for the ASCII range, but the Noto Sans Latin variant should go first.
- When there is overlapping coverage, put smaller font files higher in the list. This reduces UI jank when loading fallback fonts.

### `font_collection`

*Defined in [fonts.gni](https://cs.opensource.google/fuchsia/fuchsia/+/main:/src/fonts/build/fonts.gni)*

After any needed `local_font_bundle`s, and/or font `packages` have been
declared, they are assembled into a `font_collection`.

#### Inputs

(See fonts.gni for complete documentation.)

- `font_packages`: GN labels of font `package`s that are in `universe_package_labels` for the target product.
- `local_font_bundles`: GN labels of `local_font_bundle`s 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](https://fuchsia.dev/fuchsia-src/development/internationalization/fonts/build#product-config), including a fallback chain.
- `manifest_prefix`: A prefix for the generated font manifest file name. Defaults to `"all"`.

#### Internals

A `font_collection` traverses the transitive closure all of the font assets and
packages, that it contains. It collects their [GN
metadata](https://gn.googlesource.com/gn/+/HEAD/docs/reference.md#var_metadata)
to build lists of fonts that map to font packages in local files.

The template passes this information to the font manifest generator
([GN template](https://cs.opensource.google/fuchsia/fuchsia/+/main:/src/fonts/build/font_manifest.gni),
[Rust source](https://cs.opensource.google/fuchsia/fuchsia/+/main:/src/fonts/tools/manifest_generator)), along with the paths of the
[font catalogs](https://fuchsia.dev/fuchsia-src/development/internationalization/fonts/build#font-catalog-json) 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](https://fuchsia.dev/fuchsia-src/development/internationalization/fonts#gloss-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.

#### Outputs

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`](https://fuchsia.dev/docs/gen/build_arguments#base_package_labels)).

If a product also uses font `package`s, those targets must be added explicitly
to either
[`base_package_labels`](https://fuchsia.dev/docs/gen/build_arguments#base_package_labels) or
[`universe_package_labels`](https://fuchsia.dev/docs/gen/build_arguments#universe_package_labels),
depending on whether the packages are meant to be ephemeral.
[](https://issues.fuchsia.dev/issues/new?component=1439230&template=1881640&description=Issue+on:+https://fuchsia.dev/docs/development/internationalization/fonts/build.md "Report a bug or suggest improvements on this page") [](https://cs.opensource.google/fuchsia/fuchsia/+/main:docs/development/internationalization/fonts/build.md "View the source code of this page") [](https://ci.android.com/edit?repo=fuchsia/fuchsia/main&file=docs/development/internationalization/fonts/build.md&cloneRepo=fuchsia/fuchsia:refs/heads/main&fullPath=fuchsia/docs/development/internationalization/fonts/build.md "Edit the source code of this page")