blob: c3b7881f2c84b51653c64c6009e9e51a6b0387e8 [file] [log] [blame]
// Copyright 2018 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.
use {
crate::font_service::font_info::CharSet,
failure::{self, format_err, ResultExt},
fidl_fuchsia_fonts::{GenericFontFamily, Slant, Width, WEIGHT_NORMAL},
fuchsia_url::pkg_url::PkgUrl,
lazy_static::lazy_static,
regex::Regex,
serde::de::{self, Deserialize, Deserializer, Error},
serde_derive::Deserialize,
serde_json,
std::{
fmt,
fs::{self, File},
io::Read,
path::{Path, PathBuf},
},
};
// Following structs are used to parse manifest.json.
#[derive(Debug, Deserialize)]
pub struct FontsManifest {
pub fallback: Option<String>,
pub families: Vec<Family>,
}
#[derive(Debug, Deserialize)]
pub struct Family {
pub family: String,
pub aliases: Option<Vec<String>>,
pub fonts: Vec<Font>,
#[serde(default = "default_fallback")]
pub fallback: bool,
#[serde(
alias = "fallback_group",
default = "default_generic_family",
deserialize_with = "deserialize_generic_family"
)]
pub generic_family: Option<GenericFontFamily>,
}
pub type LanguageSet = Vec<String>;
#[derive(Debug, Deserialize)]
pub struct Font {
pub asset: PathBuf,
#[serde(default = "default_index")]
pub index: u32,
#[serde(default = "default_slant", deserialize_with = "deserialize_slant")]
pub slant: Slant,
#[serde(default = "default_weight")]
pub weight: u16,
#[serde(default = "default_width", deserialize_with = "deserialize_width")]
pub width: Width,
#[serde(
alias = "language",
default = "default_languages",
deserialize_with = "deserialize_languages"
)]
pub languages: LanguageSet,
#[serde(default = "default_package", deserialize_with = "deserialize_package")]
pub package: Option<PkgUrl>,
#[serde(default, deserialize_with = "deserialize_code_points")]
pub code_points: CharSet,
}
fn default_fallback() -> bool {
false
}
fn default_generic_family() -> Option<GenericFontFamily> {
None
}
fn default_index() -> u32 {
0
}
fn default_slant() -> Slant {
Slant::Upright
}
fn default_weight() -> u16 {
WEIGHT_NORMAL
}
fn default_width() -> Width {
Width::Normal
}
fn default_languages() -> LanguageSet {
LanguageSet::new()
}
fn default_package() -> Option<PkgUrl> {
None
}
lazy_static! {
static ref SEPARATOR_REGEX: Regex = Regex::new(r"[_ ]").unwrap();
}
fn deserialize_generic_family<'d, D>(deserializer: D) -> Result<Option<GenericFontFamily>, D::Error>
where
D: Deserializer<'d>,
{
use GenericFontFamily::*;
let s = String::deserialize(deserializer)?;
let s: String = SEPARATOR_REGEX.replace_all(&s, "-").to_string();
match s.as_str() {
"serif" => Ok(Some(Serif)),
"sans-serif" => Ok(Some(SansSerif)),
"monospace" => Ok(Some(Monospace)),
"cursive" => Ok(Some(Cursive)),
"fantasy" => Ok(Some(Fantasy)),
"system-ui" => Ok(Some(SystemUi)),
"emoji" => Ok(Some(Emoji)),
"math" => Ok(Some(Math)),
"fangsong" => Ok(Some(Fangsong)),
x => Err(D::Error::custom(format!("unknown value for generic_family in manifest: {}", x))),
}
}
fn deserialize_slant<'d, D>(deserializer: D) -> Result<Slant, D::Error>
where
D: Deserializer<'d>,
{
use Slant::*;
let s = String::deserialize(deserializer)?;
match s.as_str() {
"upright" => Ok(Upright),
"italic" => Ok(Italic),
"oblique" => Ok(Oblique),
x => Err(D::Error::custom(format!("unknown value for slant in manifest: {}", x))),
}
}
fn deserialize_width<'d, D>(deserializer: D) -> Result<Width, D::Error>
where
D: Deserializer<'d>,
{
use Width::*;
let s = String::deserialize(deserializer)?;
if let Ok(numeric) = s.parse::<u16>() {
match Width::from_primitive(numeric.into()) {
Some(value) => Ok(value),
None => {
Err(D::Error::custom(format!("unknown value for width in manifest: {}", numeric)))
}
}
} else {
let s: String = SEPARATOR_REGEX.replace_all(&s, "-").to_string();
match s.as_str() {
"ultra-condensed" => Ok(UltraCondensed),
"extra-condensed" => Ok(ExtraCondensed),
"condensed" => Ok(Condensed),
"semi-condensed" => Ok(SemiCondensed),
"normal" => Ok(Normal),
"semi-expanded" => Ok(SemiExpanded),
"expanded" => Ok(Expanded),
"extra-expanded" => Ok(ExtraExpanded),
"ultra-expanded" => Ok(UltraExpanded),
x => Err(D::Error::custom(format!("unknown value for width in manifest: {}", x))),
}
}
}
/// Helper used to deserialize language field for a font. Language field can contain either a single
/// string or an array of strings.
fn deserialize_languages<'d, D>(deserializer: D) -> Result<LanguageSet, D::Error>
where
D: Deserializer<'d>,
{
struct LanguageSetVisitor;
impl<'de> de::Visitor<'de> for LanguageSetVisitor {
type Value = Vec<String>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("string or list of strings")
}
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(vec![s.to_string()])
}
fn visit_seq<S>(self, seq: S) -> Result<Self::Value, S::Error>
where
S: de::SeqAccess<'de>,
{
Deserialize::deserialize(de::value::SeqAccessDeserializer::new(seq))
}
}
deserializer.deserialize_any(LanguageSetVisitor)
}
fn deserialize_package<'d, D>(deserializer: D) -> Result<Option<PkgUrl>, D::Error>
where
D: Deserializer<'d>,
{
Some(PkgUrl::deserialize(deserializer)).transpose()
}
fn deserialize_code_points<'d, D>(deserializer: D) -> Result<CharSet, D::Error>
where
D: Deserializer<'d>,
{
let s = String::deserialize(deserializer)?;
CharSet::from_string(s).map_err(|e| D::Error::custom(format!("{:?}", e)))
}
impl FontsManifest {
pub fn load_from_file(path: &Path) -> Result<FontsManifest, failure::Error> {
let path = fs::canonicalize(path)?;
let base_dir =
path.parent().ok_or(format_err!("Invalid manifest path: {}", path.display()))?;
let mut f = File::open(path.clone())?;
let mut contents = String::new();
f.read_to_string(&mut contents)?;
let mut manifest: FontsManifest = serde_json::from_str(&contents)
.context(format!("Failed to load {}", path.display()))?;
// Make sure all paths are absolute.
for family in manifest.families.iter_mut() {
for font in family.fonts.iter_mut() {
if font.asset.is_relative() {
font.asset = base_dir.join(font.asset.clone());
}
}
}
Ok(manifest)
}
}