use std::{borrow::Cow, path::Path};
use fontdb::Source;
use ttf_parser::{
name::{name_id, Table as NameTable},
Language, RawFace,
};
use super::{
cmap::CMapTable,
error::{BROKEN_NAME_TABLE, MISSING_NAME_TABLE, NAME_TAG},
Error, Result,
};
use crate::loader::database;
pub struct FaceInfo {
pub id: fontdb::ID,
pub family: &'static str,
pub name: Cow<'static, str>,
pub path: &'static Path,
pub index: u32,
pub gid: u16,
}
impl FaceInfo {
pub fn parse_if_contains(face: &'static fontdb::FaceInfo, c: char) -> Result<Option<Self>> {
let Some((gid, name)) = database()
.with_face_data(face.id, |data, index| -> Result<_> {
let rf = RawFace::parse(data, index)?;
let Some(gid) = CMapTable::parse(rf)?.glyph_index(c) else {
return Ok(None);
};
let name = Self::parse_full_name(rf)?;
Ok(Some((gid.0, name)))
})
.expect("we only load font from database so it must not None")?
else {
return Ok(None);
};
let family = face
.families
.get(0)
.map(|(s, _)| s.as_str())
.ok_or(Error::MissingFamilyName)?;
let name = name
.map(Cow::Owned)
.unwrap_or_else(|| face.post_script_name.as_str().into());
let path = match face.source {
Source::File(ref path) => path,
_ => unreachable!("we only load font file, so source must be File variant"),
};
Ok(Some(FaceInfo {
id: face.id,
family,
name,
path,
index: face.index,
gid,
}))
}
fn parse_full_name(rf: RawFace<'_>) -> Result<Option<String>> {
let name_data = rf.table(NAME_TAG).ok_or(MISSING_NAME_TABLE)?;
let name_table = NameTable::parse(name_data).ok_or(BROKEN_NAME_TABLE)?;
for i in 0..name_table.names.len() {
let name = name_table.names.get(i).ok_or(BROKEN_NAME_TABLE)?;
if name.name_id == name_id::FULL_NAME
&& name.is_unicode()
&& name.language() == Language::English_UnitedStates
{
if let Some(name) = name.to_string() {
return Ok(Some(name));
}
}
}
Ok(None)
}
}