use crate::Error;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FaceKind {
Ttf,
Otf,
}
fn next_face_id() -> u64 {
use std::sync::atomic::{AtomicU64, Ordering};
static NEXT: AtomicU64 = AtomicU64::new(1);
NEXT.fetch_add(1, Ordering::Relaxed)
}
#[derive(Debug)]
pub struct Face {
bytes: Box<[u8]>,
id: u64,
kind: FaceKind,
units_per_em: u16,
ascent: i16,
descent: i16,
line_gap: i16,
family: Option<String>,
italic_angle: f32,
weight_class: u16,
}
impl Face {
pub fn from_ttf_bytes(bytes: Vec<u8>) -> Result<Self, Error> {
let bytes: Box<[u8]> = bytes.into_boxed_slice();
let (units_per_em, ascent, descent, line_gap, family, italic_angle, weight_class) = {
let font = oxideav_ttf::Font::from_bytes(&bytes).map_err(Error::from)?;
(
font.units_per_em(),
font.ascent(),
font.descent(),
font.line_gap(),
font.family_name().map(|s| s.to_string()),
font.italic_angle(),
font.weight_class(),
)
};
Ok(Self {
bytes,
id: next_face_id(),
kind: FaceKind::Ttf,
units_per_em,
ascent,
descent,
line_gap,
family,
italic_angle,
weight_class,
})
}
pub fn from_otf_bytes(bytes: Vec<u8>) -> Result<Self, Error> {
let bytes: Box<[u8]> = bytes.into_boxed_slice();
let (units_per_em, ascent, descent, line_gap, family) = {
let font = oxideav_otf::Font::from_bytes(&bytes).map_err(Error::from)?;
(
font.units_per_em(),
font.ascent(),
font.descent(),
font.line_gap(),
font.family_name().map(|s| s.to_string()),
)
};
Ok(Self {
bytes,
id: next_face_id(),
kind: FaceKind::Otf,
units_per_em,
ascent,
descent,
line_gap,
family,
italic_angle: 0.0,
weight_class: 400,
})
}
pub fn kind(&self) -> FaceKind {
self.kind
}
pub fn id(&self) -> u64 {
self.id
}
pub fn family_name(&self) -> Option<&str> {
self.family.as_deref()
}
pub fn units_per_em(&self) -> u16 {
self.units_per_em
}
pub fn ascent_px(&self, size_px: f32) -> f32 {
self.ascent as f32 * size_px / self.units_per_em as f32
}
pub fn descent_px(&self, size_px: f32) -> f32 {
self.descent as f32 * size_px / self.units_per_em as f32
}
pub fn line_height_px(&self, size_px: f32) -> f32 {
let units = self.ascent as i32 - self.descent as i32 + self.line_gap as i32;
units as f32 * size_px / self.units_per_em as f32
}
pub fn italic_angle(&self) -> f32 {
self.italic_angle
}
pub fn weight_class(&self) -> u16 {
self.weight_class
}
pub fn with_font<R>(&self, f: impl FnOnce(&oxideav_ttf::Font<'_>) -> R) -> Result<R, Error> {
if self.kind != FaceKind::Ttf {
return Err(Error::WrongFaceKind {
expected: FaceKind::Ttf,
actual: self.kind,
});
}
let font = oxideav_ttf::Font::from_bytes(&self.bytes).map_err(Error::from)?;
Ok(f(&font))
}
pub fn with_otf_font<R>(
&self,
f: impl FnOnce(&oxideav_otf::Font<'_>) -> R,
) -> Result<R, Error> {
if self.kind != FaceKind::Otf {
return Err(Error::WrongFaceKind {
expected: FaceKind::Otf,
actual: self.kind,
});
}
let font = oxideav_otf::Font::from_bytes(&self.bytes).map_err(Error::from)?;
Ok(f(&font))
}
}