use fontcull_read_fonts::{
tables::{
head::{Head, MacStyle},
os2::{Os2, SelectionFlags},
post::Post,
},
FontRef, TableProvider,
};
#[derive(Copy, Clone, PartialEq, Debug, Default)]
pub struct Attributes {
pub stretch: Stretch,
pub style: Style,
pub weight: Weight,
}
impl Attributes {
pub fn new(font: &FontRef) -> Self {
if let Ok(os2) = font.os2() {
Self::from_os2_post(os2, font.post().ok())
} else if let Ok(head) = font.head() {
Self::from_head(head)
} else {
Self::default()
}
}
fn from_os2_post(os2: Os2, post: Option<Post>) -> Self {
let stretch = Stretch::from_width_class(os2.us_width_class());
let fs_selection = os2.fs_selection();
let style = if fs_selection.contains(SelectionFlags::ITALIC) {
Style::Italic
} else if fs_selection.contains(SelectionFlags::OBLIQUE) {
let angle = post.map(|post| post.italic_angle().to_f64() as f32);
Style::Oblique(angle)
} else {
Style::Normal
};
let weight = Weight(os2.us_weight_class() as f32);
Self {
stretch,
style,
weight,
}
}
fn from_head(head: Head) -> Self {
let mac_style = head.mac_style();
let style = if mac_style.contains(MacStyle::ITALIC) {
Style::Italic
} else {
Default::default()
};
let weight = if mac_style.contains(MacStyle::BOLD) {
Weight::BOLD
} else {
Default::default()
};
Self {
stretch: Stretch::default(),
style,
weight,
}
}
}
#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)]
pub struct Stretch(f32);
impl Stretch {
pub const ULTRA_CONDENSED: Self = Self(0.5);
pub const EXTRA_CONDENSED: Self = Self(0.625);
pub const CONDENSED: Self = Self(0.75);
pub const SEMI_CONDENSED: Self = Self(0.875);
pub const NORMAL: Self = Self(1.0);
pub const SEMI_EXPANDED: Self = Self(1.125);
pub const EXPANDED: Self = Self(1.25);
pub const EXTRA_EXPANDED: Self = Self(1.5);
pub const ULTRA_EXPANDED: Self = Self(2.0);
}
impl Stretch {
pub const fn new(ratio: f32) -> Self {
Self(ratio)
}
fn from_width_class(width_class: u16) -> Self {
match width_class {
0..=1 => Stretch::ULTRA_CONDENSED,
2 => Stretch::EXTRA_CONDENSED,
3 => Stretch::CONDENSED,
4 => Stretch::SEMI_CONDENSED,
5 => Stretch::NORMAL,
6 => Stretch::SEMI_EXPANDED,
7 => Stretch::EXPANDED,
8 => Stretch::EXTRA_EXPANDED,
_ => Stretch::ULTRA_EXPANDED,
}
}
pub const fn ratio(self) -> f32 {
self.0
}
pub fn percentage(self) -> f32 {
self.0 * 100.0
}
}
impl Default for Stretch {
fn default() -> Self {
Self::NORMAL
}
}
#[derive(Copy, Clone, PartialEq, Default, Debug)]
pub enum Style {
#[default]
Normal,
Italic,
Oblique(Option<f32>),
}
#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)]
pub struct Weight(f32);
impl Weight {
pub const THIN: Self = Self(100.0);
pub const EXTRA_LIGHT: Self = Self(200.0);
pub const LIGHT: Self = Self(300.0);
pub const SEMI_LIGHT: Self = Self(350.0);
pub const NORMAL: Self = Self(400.0);
pub const MEDIUM: Self = Self(500.0);
pub const SEMI_BOLD: Self = Self(600.0);
pub const BOLD: Self = Self(700.0);
pub const EXTRA_BOLD: Self = Self(800.0);
pub const BLACK: Self = Self(900.0);
pub const EXTRA_BLACK: Self = Self(950.0);
}
impl Weight {
pub const fn new(weight: f32) -> Self {
Self(weight)
}
pub const fn value(self) -> f32 {
self.0
}
}
impl Default for Weight {
fn default() -> Self {
Self::NORMAL
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::prelude::*;
#[test]
fn missing_os2() {
let font = FontRef::new(fontcull_font_test_data::CMAP12_FONT1).unwrap();
let attrs = font.attributes();
assert_eq!(attrs.stretch, Stretch::NORMAL);
assert_eq!(attrs.style, Style::Italic);
assert_eq!(attrs.weight, Weight::BOLD);
}
#[test]
fn so_stylish() {
let font = FontRef::new(fontcull_font_test_data::CMAP14_FONT1).unwrap();
let attrs = font.attributes();
assert_eq!(attrs.stretch, Stretch::SEMI_CONDENSED);
assert_eq!(attrs.style, Style::Oblique(Some(-14.0)));
assert_eq!(attrs.weight, Weight::EXTRA_BOLD);
}
}