#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Style {
pub italic: bool,
pub weight: u16,
}
impl Style {
pub const REGULAR: Style = Style {
italic: false,
weight: 400,
};
pub fn regular() -> Self {
Self::REGULAR
}
pub fn italic() -> Self {
Self {
italic: true,
weight: 400,
}
}
#[must_use]
pub fn with_italic(mut self, italic: bool) -> Self {
self.italic = italic;
self
}
#[must_use]
pub fn with_weight(mut self, weight: u16) -> Self {
self.weight = weight.clamp(1, 1000);
self
}
}
impl Default for Style {
fn default() -> Self {
Self::REGULAR
}
}
pub const DEFAULT_SYNTHETIC_ITALIC_DEG: f32 = 12.0;
pub const ITALIC_ANGLE_EPSILON_DEG: f32 = 0.5;
pub fn synthetic_italic_shear(style: Style, face_italic_deg: f32) -> f32 {
if !style.italic {
return 0.0;
}
if face_italic_deg.abs() > ITALIC_ANGLE_EPSILON_DEG {
return 0.0;
}
DEFAULT_SYNTHETIC_ITALIC_DEG.to_radians().tan()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_is_regular_upright() {
assert_eq!(Style::default(), Style::REGULAR);
assert!(!Style::default().italic);
assert_eq!(Style::default().weight, 400);
}
#[test]
fn italic_builder_sets_flag() {
let s = Style::italic();
assert!(s.italic);
assert_eq!(s.weight, 400);
}
#[test]
fn weight_is_clamped() {
assert_eq!(Style::REGULAR.with_weight(0).weight, 1);
assert_eq!(Style::REGULAR.with_weight(2_000).weight, 1000);
assert_eq!(Style::REGULAR.with_weight(700).weight, 700);
}
#[test]
fn upright_request_yields_zero_shear() {
assert_eq!(synthetic_italic_shear(Style::REGULAR, 0.0), 0.0);
assert_eq!(synthetic_italic_shear(Style::REGULAR, -12.0), 0.0);
}
#[test]
fn italic_request_on_upright_face_yields_default_shear() {
let shear = synthetic_italic_shear(Style::italic(), 0.0);
let expected = DEFAULT_SYNTHETIC_ITALIC_DEG.to_radians().tan();
assert!(
(shear - expected).abs() < 1e-6,
"shear = {shear}, expected = {expected}"
);
assert!(shear > 0.20 && shear < 0.22, "shear = {shear}");
}
#[test]
fn italic_request_on_italic_face_yields_zero() {
assert_eq!(synthetic_italic_shear(Style::italic(), -12.0), 0.0);
assert_eq!(synthetic_italic_shear(Style::italic(), 8.0), 0.0);
}
#[test]
fn epsilon_band_still_synthesises() {
let shear = synthetic_italic_shear(Style::italic(), 0.3);
assert!(shear > 0.20);
}
}