blob: db0c3b7819b1baa380aad37cd37730cb35f18fb0 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
mod constants;
mod fake_font_info_loader;
mod font_catalog;
mod font_db;
mod font_pkgs;
mod font_sets;
mod generator;
mod merge;
mod product_config;
mod serde_ext;
pub(crate) use crate::font_catalog::{FontCatalog, TypefaceInAssetIndex};
pub(crate) use crate::font_pkgs::FontPackageListing;
pub(crate) use crate::font_sets::{FontSet, FontSets};
pub(crate) use crate::product_config::ProductConfig;
use {
anyhow::Error,
fake_font_info_loader::FakeFontInfoLoaderImpl,
font_info::{FontAssetSource, FontInfo, FontInfoLoader, FontInfoLoaderImpl},
manifest::{v2::FontsManifest, FontManifestWrapper},
std::{
convert::TryInto,
env, fs,
io::{self, Write},
path::PathBuf,
},
structopt::StructOpt,
};
#[derive(Debug, StructOpt)]
#[structopt(name = "Font Manifest Generator")]
struct Args {
#[structopt(
long = "empty",
raw(
conflicts_with_all = r#"&["all_fonts", "local_fonts", "font_pkgs", "font_catalog", "product_config", "font_dir", "fake_code_points"]"#
),
help = "If true, generates an empty manifest"
)]
empty: bool,
#[structopt(
long = "all-fonts",
required_unless = "empty",
value_name = "FILE",
help = "Path to local_fonts.json file containing list of all font file names for the target product, generated by GN."
)]
all_fonts: Option<PathBuf>,
#[structopt(
long = "local-fonts",
required_unless = "empty",
value_name = "FILE",
help = "Path to local_fonts.json file containing list of *local* font file names for the target product, generated by GN."
)]
local_fonts: Option<PathBuf>,
#[structopt(
long = "font-pkgs",
required_unless = "empty",
value_name = "FILE",
help = "Paths to .font_pkgs.json files, each containing a list of all font files and Fuchsia package names available in a CIPD fonts repo, generated by LUCI. There should be one file for each CIPD fonts repo used for the target product."
)]
font_pkgs: Vec<PathBuf>,
#[structopt(
long = "font-catalog",
required_unless = "empty",
value_name = "FILE",
help = "Paths to .font_catalog.json files, each containing an index of all font families, assets, and typefaces available in a CIPD fonts repo. This file is maintained by hand. There should be one file for each CIPD fonts repo used for the target product."
)]
font_catalog: Vec<PathBuf>,
#[structopt(
long = "product-config",
value_name = "FILE",
help = "Path to a .fontcfg.json file, containing font configurations for the target product, including a fallback sequence of typefaces. This file is maintained by hand."
)]
product_config: Option<PathBuf>,
#[structopt(
long = "font-dir",
value_name = "DIR",
required_unless = "empty",
help = "Path to the base directory containing all font files checked out from CIPD. Usually, this will end in \"prebuilt/third_party/fonts\"."
)]
font_dir: Option<PathBuf>,
#[structopt(
long = "fake-code-points",
help = "If true, will write fake code points instead of trying to read actual font files."
)]
fake_code_points: bool,
#[structopt(long = "pretty-print", help = "If true, indents output JSON.")]
pretty_print: bool,
#[structopt(
long = "verbose",
short = "v",
help = "If true, outputs extra logs while generating the manifest"
)]
verbose: bool,
#[structopt(
long = "output",
value_name = "FILE",
help = "Optional path to output file ending in .font_manifest.json. If omitted, writes to STDOUT."
)]
output: Option<PathBuf>,
}
/// Required because Rust can't create trait objects (`dyn FontInfoLoader`) with generic methods.
enum FontInfoLoaderType {
Real(FontInfoLoaderImpl),
Fake(FakeFontInfoLoaderImpl),
}
impl FontInfoLoader for FontInfoLoaderType {
fn load_font_info<S, E>(&self, source: S, index: u32) -> Result<FontInfo, Error>
where
S: Sized + TryInto<FontAssetSource, Error = E>,
E: Sized + Sync + Send + Into<Error>,
{
match self {
FontInfoLoaderType::Real(loader) => loader.load_font_info(source, index),
FontInfoLoaderType::Fake(loader) => loader.load_font_info(source, index),
}
}
}
fn main() -> Result<(), Error> {
env::set_var("RUST_BACKTRACE", "full");
let args: Args = Args::from_args();
let manifest = if args.empty {
FontManifestWrapper::Version2(FontsManifest::empty())
} else {
let font_sets = FontSets::load_from_all_and_local_paths(
args.all_fonts.unwrap(),
args.local_fonts.unwrap(),
)?;
let font_pkgs = FontPackageListing::load_from_paths(args.font_pkgs)?;
let font_catalog = FontCatalog::load_from_paths(args.font_catalog)?;
let product_config = args.product_config.map_or_else(
|| Ok(ProductConfig::default()),
|path| ProductConfig::load_from_path(path),
)?;
let font_info_loader = if args.fake_code_points {
FontInfoLoaderType::Fake(FakeFontInfoLoaderImpl::new())
} else {
FontInfoLoaderType::Real(FontInfoLoaderImpl::new()?)
};
generator::generate_manifest(
font_catalog,
font_pkgs,
font_sets,
product_config,
font_info_loader,
args.font_dir.unwrap(),
args.verbose,
)?
};
let manifest_str = if args.pretty_print {
serde_json::to_string_pretty(&manifest)
} else {
serde_json::to_string(&manifest)
}?;
if let Some(output_path) = args.output {
fs::write(output_path, manifest_str.as_bytes())?;
} else {
io::stdout().write_all(manifest_str.as_bytes())?;
}
Ok(())
}