use super::internal::{head::Os2, RawFont};
use super::{tag_from_bytes, FontRef, Setting, Tag};
use core::fmt;
use core::hash::{Hash, Hasher};
const WDTH: Tag = tag_from_bytes(b"wdth");
const WGHT: Tag = tag_from_bytes(b"wght");
const SLNT: Tag = tag_from_bytes(b"slnt");
const ITAL: Tag = tag_from_bytes(b"ital");
#[derive(Copy, Clone)]
pub struct Attributes(pub u32);
impl Attributes {
pub const fn new(stretch: Stretch, weight: Weight, style: Style) -> Self {
let stretch = stretch.0 as u32 & 0x1FF;
let weight = weight.0 as u32 & 0x3FF;
let style = style.pack();
Self(style | weight << 9 | stretch << 19)
}
pub fn from_font<'a>(font: &FontRef<'a>) -> Self {
let mut attrs = Self::from_os2(font.os2().as_ref());
let mut var_bits = 0;
for var in font.variations() {
match var.tag() {
WDTH => var_bits |= 1,
WGHT => var_bits |= 2,
SLNT => var_bits |= 4,
ITAL => var_bits |= 8,
_ => {}
}
}
attrs.0 |= var_bits << 28;
attrs
}
pub(crate) fn from_os2(os2: Option<&Os2>) -> Self {
if let Some(os2) = os2 {
let flags = os2.selection_flags();
let style = if flags.italic() {
Style::Italic
} else if flags.oblique() {
Style::Oblique(ObliqueAngle::default())
} else {
Style::Normal
};
let weight = Weight(os2.weight_class() as u16);
let stretch = Stretch::from_raw(os2.width_class() as u16);
Self::new(stretch, weight, style)
} else {
Self::default()
}
}
pub fn stretch(&self) -> Stretch {
Stretch((self.0 >> 19 & 0x1FF) as u16)
}
pub fn weight(&self) -> Weight {
Weight((self.0 >> 9 & 0x3FF) as u16)
}
pub fn style(&self) -> Style {
Style::unpack(self.0 & 0x1FF)
}
pub fn parts(&self) -> (Stretch, Weight, Style) {
(self.stretch(), self.weight(), self.style())
}
pub fn has_variations(&self) -> bool {
(self.0 >> 28) != 0
}
pub fn has_stretch_variation(&self) -> bool {
let var_bits = self.0 >> 28;
var_bits & 1 != 0
}
pub fn has_weight_variation(&self) -> bool {
let var_bits = self.0 >> 28;
var_bits & 2 != 0
}
pub fn has_oblique_variation(&self) -> bool {
let var_bits = self.0 >> 28;
var_bits & 4 != 0
}
pub fn has_italic_variation(&self) -> bool {
let var_bits = self.0 >> 28;
var_bits & 8 != 0
}
pub fn synthesize(&self, requested: Attributes) -> Synthesis {
let mut synth = Synthesis::default();
if self.0 << 4 == requested.0 << 4 {
return synth;
}
let mut len = 0usize;
if self.has_stretch_variation() {
let stretch = self.stretch();
let req_stretch = requested.stretch();
if stretch != requested.stretch() {
synth.vars[len] = Setting {
tag: WDTH,
value: req_stretch.to_percentage(),
};
len += 1;
}
}
let (weight, req_weight) = (self.weight(), requested.weight());
if weight != req_weight {
if self.has_weight_variation() {
synth.vars[len] = Setting {
tag: WGHT,
value: req_weight.0 as f32,
};
len += 1;
} else if req_weight > weight {
synth.embolden = true;
}
}
let (style, req_style) = (self.style(), requested.style());
if style != req_style {
match req_style {
Style::Normal => {}
Style::Italic => {
if style == Style::Normal {
if self.has_italic_variation() {
synth.vars[len] = Setting {
tag: ITAL,
value: 1.,
};
len += 1;
} else if self.has_oblique_variation() {
synth.vars[len] = Setting {
tag: SLNT,
value: 14.,
};
len += 1;
} else {
synth.skew = 14;
}
}
}
Style::Oblique(angle) => {
if style == Style::Normal {
let degrees = angle.to_degrees();
if self.has_oblique_variation() {
synth.vars[len] = Setting {
tag: SLNT,
value: degrees,
};
len += 1;
} else if self.has_italic_variation() && degrees > 0. {
synth.vars[len] = Setting {
tag: ITAL,
value: 1.,
};
len += 1;
} else {
synth.skew = degrees as i8;
}
}
}
}
}
synth.len = len as u8;
synth
}
}
impl Default for Attributes {
fn default() -> Self {
Self::new(Stretch::NORMAL, Weight::NORMAL, Style::Normal)
}
}
impl PartialEq for Attributes {
fn eq(&self, other: &Self) -> bool {
self.0 << 4 == other.0 << 4
}
}
impl Eq for Attributes {}
impl Hash for Attributes {
fn hash<H: Hasher>(&self, state: &mut H) {
(self.0 << 4).hash(state);
}
}
impl fmt::Display for Attributes {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut space = "";
let (stretch, weight, style) = self.parts();
if style == Style::Normal && weight == Weight::NORMAL && stretch == Stretch::NORMAL {
return write!(f, "regular");
}
if stretch != Stretch::NORMAL {
write!(f, "{}", stretch)?;
space = " ";
}
if style != Style::Normal {
write!(f, "{}{}", space, style)?;
space = " ";
}
if weight != Weight::NORMAL {
write!(f, "{}{}", space, weight)?;
}
Ok(())
}
}
impl fmt::Debug for Attributes {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self.parts())?;
if self.has_stretch_variation() {
write!(f, "+wdth")?;
}
if self.has_weight_variation() {
write!(f, "+wght")?;
}
if self.has_italic_variation() {
write!(f, "+ital")?;
}
if self.has_oblique_variation() {
write!(f, "+slnt")?;
}
Ok(())
}
}
impl From<Stretch> for Attributes {
fn from(s: Stretch) -> Self {
Self::new(s, Weight::default(), Style::default())
}
}
impl From<Weight> for Attributes {
fn from(w: Weight) -> Self {
Self::new(Stretch::default(), w, Style::default())
}
}
impl From<Style> for Attributes {
fn from(s: Style) -> Self {
Self::new(Stretch::default(), Weight::default(), s)
}
}
impl From<()> for Attributes {
fn from(_: ()) -> Self {
Self::default()
}
}
impl From<(Stretch, Weight, Style)> for Attributes {
fn from(parts: (Stretch, Weight, Style)) -> Self {
Self::new(parts.0, parts.1, parts.2)
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct ObliqueAngle(pub(crate) u8);
impl ObliqueAngle {
pub fn from_degrees(degrees: f32) -> Self {
let a = degrees.clamp(-90., 90.) + 90.;
Self(a as u8)
}
pub fn from_radians(radians: f32) -> Self {
let degrees = radians * 180. / core::f32::consts::PI;
Self::from_degrees(degrees)
}
pub fn from_gradians(gradians: f32) -> Self {
Self::from_degrees(gradians / 400. * 360.)
}
pub fn from_turns(turns: f32) -> Self {
Self::from_degrees(turns * 360.)
}
pub fn to_degrees(self) -> f32 {
self.0 as f32 - 90.
}
}
impl Default for ObliqueAngle {
fn default() -> Self {
Self::from_degrees(14.)
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Style {
Normal,
Italic,
Oblique(ObliqueAngle),
}
impl Style {
pub fn parse(mut s: &str) -> Option<Self> {
s = s.trim();
Some(match s {
"normal" => Self::Normal,
"italic" => Self::Italic,
"oblique" => Self::Oblique(ObliqueAngle::from_degrees(14.)),
_ => {
if s.starts_with("oblique ") {
s = s.get(8..)?;
if s.ends_with("deg") {
s = s.get(..s.len() - 3)?;
if let Ok(a) = s.trim().parse::<f32>() {
return Some(Self::Oblique(ObliqueAngle::from_degrees(a)));
}
} else if s.ends_with("grad") {
s = s.get(..s.len() - 4)?;
if let Ok(a) = s.trim().parse::<f32>() {
return Some(Self::Oblique(ObliqueAngle::from_gradians(a)));
}
} else if s.ends_with("rad") {
s = s.get(..s.len() - 3)?;
if let Ok(a) = s.trim().parse::<f32>() {
return Some(Self::Oblique(ObliqueAngle::from_radians(a)));
}
} else if s.ends_with("turn") {
s = s.get(..s.len() - 4)?;
if let Ok(a) = s.trim().parse::<f32>() {
return Some(Self::Oblique(ObliqueAngle::from_turns(a)));
}
}
return Some(Self::Oblique(ObliqueAngle::default()));
}
return None;
}
})
}
pub fn from_degrees(degrees: f32) -> Self {
Self::Oblique(ObliqueAngle::from_degrees(degrees))
}
pub fn to_degrees(self) -> f32 {
match self {
Self::Italic => 14.,
Self::Oblique(angle) => angle.to_degrees(),
_ => 0.,
}
}
fn unpack(bits: u32) -> Self {
if bits & 1 != 0 {
Self::Oblique(ObliqueAngle((bits >> 1) as u8))
} else if bits == 0b110 {
Self::Italic
} else {
Self::Normal
}
}
const fn pack(&self) -> u32 {
match self {
Self::Normal => 0b10,
Self::Italic => 0b110,
Self::Oblique(angle) => 1 | (angle.0 as u32) << 1,
}
}
}
impl fmt::Display for Style {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
match self {
Self::Normal => "normal",
Self::Italic => "italic",
Self::Oblique(angle) => {
let degrees = angle.to_degrees();
if degrees == 14. {
"oblique"
} else {
return write!(f, "oblique({}deg)", degrees);
}
}
}
)
}
}
impl Default for Style {
fn default() -> Self {
Self::Normal
}
}
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Debug)]
pub struct Weight(pub u16);
impl Weight {
pub const THIN: Weight = Weight(100);
pub const EXTRA_LIGHT: Weight = Weight(200);
pub const LIGHT: Weight = Weight(300);
pub const NORMAL: Weight = Weight(400);
pub const MEDIUM: Weight = Weight(500);
pub const SEMI_BOLD: Weight = Weight(600);
pub const BOLD: Weight = Weight(700);
pub const EXTRA_BOLD: Weight = Weight(800);
pub const BLACK: Weight = Weight(900);
pub fn parse(s: &str) -> Option<Self> {
let s = s.trim();
Some(match s {
"normal" => Self::NORMAL,
"bold" => Self::BOLD,
_ => Self(s.parse::<u32>().ok()?.clamp(1, 1000) as u16),
})
}
}
impl fmt::Display for Weight {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let s = match *self {
Self::THIN => "thin",
Self::EXTRA_LIGHT => "extra-light",
Self::LIGHT => "light",
Self::NORMAL => "normal",
Self::MEDIUM => "medium",
Self::SEMI_BOLD => "semi-bold",
Self::BOLD => "bold",
Self::EXTRA_BOLD => "extra-bold",
Self::BLACK => "black",
_ => "",
};
if s.is_empty() {
write!(f, "{}", self.0)
} else {
write!(f, "{}", s)
}
}
}
impl Default for Weight {
fn default() -> Self {
Self::NORMAL
}
}
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
pub struct Stretch(pub(crate) u16);
impl Stretch {
pub const ULTRA_CONDENSED: Self = Self(0);
pub const EXTRA_CONDENSED: Self = Self(25);
pub const CONDENSED: Self = Self(50);
pub const SEMI_CONDENSED: Self = Self(75);
pub const NORMAL: Self = Self(100);
pub const SEMI_EXPANDED: Self = Self(125);
pub const EXPANDED: Self = Self(150);
pub const EXTRA_EXPANDED: Self = Self(200);
pub const ULTRA_EXPANDED: Self = Self(300);
pub fn from_percentage(percentage: f32) -> Self {
let value = ((percentage.clamp(50., 200.) - 50.) * 2.) as u16;
Self(value)
}
pub fn to_percentage(self) -> f32 {
(self.0 as f32) * 0.5 + 50.
}
pub fn is_normal(self) -> bool {
self == Self::NORMAL
}
pub fn is_condensed(self) -> bool {
self < Self::NORMAL
}
pub fn is_expanded(self) -> bool {
self > Self::NORMAL
}
pub fn parse(s: &str) -> Option<Self> {
let s = s.trim();
Some(match s {
"ultra-condensed" => Self::ULTRA_CONDENSED,
"extra-condensed" => Self::EXTRA_CONDENSED,
"condensed" => Self::CONDENSED,
"semi-condensed" => Self::SEMI_CONDENSED,
"normal" => Self::NORMAL,
"semi-expanded" => Self::SEMI_EXPANDED,
"extra-expanded" => Self::EXTRA_EXPANDED,
"ultra-expanded" => Self::ULTRA_EXPANDED,
_ => {
if s.ends_with('%') {
let p = s.get(..s.len() - 1)?.parse::<f32>().ok()?;
return Some(Self::from_percentage(p));
}
return None;
}
})
}
pub fn raw(self) -> u16 {
self.0
}
pub(crate) fn from_raw(raw: u16) -> Self {
match raw {
1 => Self::ULTRA_CONDENSED,
2 => Self::EXTRA_CONDENSED,
3 => Self::CONDENSED,
4 => Self::SEMI_CONDENSED,
5 => Self::NORMAL,
6 => Self::SEMI_EXPANDED,
7 => Self::EXPANDED,
8 => Self::EXTRA_EXPANDED,
9 => Self::ULTRA_EXPANDED,
_ => Self::NORMAL,
}
}
}
impl fmt::Display for Stretch {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
match *self {
Self::ULTRA_CONDENSED => "ultra-condensed",
Self::EXTRA_CONDENSED => "extra-condensed",
Self::CONDENSED => "condensed",
Self::SEMI_CONDENSED => "semi-condensed",
Self::NORMAL => "normal",
Self::SEMI_EXPANDED => "semi-expanded",
Self::EXPANDED => "expanded",
Self::EXTRA_EXPANDED => "extra-expanded",
Self::ULTRA_EXPANDED => "ultra-expanded",
_ => {
return write!(f, "{}%", self.to_percentage());
}
}
)
}
}
impl fmt::Debug for Stretch {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Stretch({})", self.to_percentage())
}
}
impl Default for Stretch {
fn default() -> Self {
Self::NORMAL
}
}
#[derive(Copy, Clone, Default)]
pub struct Synthesis {
vars: [Setting<f32>; 4],
len: u8,
embolden: bool,
skew: i8,
}
impl Synthesis {
#[doc(hidden)]
pub fn new(variations: impl Iterator<Item = Setting<f32>>, embolden: bool, skew: f32) -> Self {
let mut synth = Self {
embolden,
skew: skew as i8,
..Default::default()
};
for (i, setting) in variations.take(4).enumerate() {
synth.vars[i] = setting;
synth.len = i as u8 + 1;
}
synth
}
pub fn any(&self) -> bool {
self.len != 0 || self.embolden || self.skew != 0
}
pub fn variations(&self) -> &[Setting<f32>] {
&self.vars[..self.len as usize]
}
pub fn embolden(&self) -> bool {
self.embolden
}
pub fn skew(&self) -> Option<f32> {
if self.skew != 0 {
Some(self.skew as f32)
} else {
None
}
}
}
impl PartialEq for Synthesis {
fn eq(&self, other: &Self) -> bool {
if self.len != other.len {
return false;
}
if self.len != 0 && self.variations() != other.variations() {
return false;
}
self.embolden == other.embolden && self.skew == other.skew
}
}