use fontcull_read_fonts::types::Fixed;
use crate::collections::SmallVec;
pub type NormalizedCoord = fontcull_read_fonts::types::F2Dot14;
#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)]
pub struct Size(Option<f32>);
impl Size {
pub fn new(ppem: f32) -> Self {
Self(Some(ppem))
}
pub fn unscaled() -> Self {
Self(None)
}
pub fn ppem(self) -> Option<f32> {
self.0
}
pub fn linear_scale(self, units_per_em: u16) -> f32 {
match self.0 {
Some(ppem) if units_per_em != 0 => ppem / units_per_em as f32,
_ => 1.0,
}
}
pub(crate) fn fixed_linear_scale(self, units_per_em: u16) -> Fixed {
match self.0 {
Some(ppem) if units_per_em > 0 => {
Fixed::from_bits((ppem * 64.) as i32) / Fixed::from_bits(units_per_em as i32)
}
_ => {
Fixed::from_bits(0x10000 * 64)
}
}
}
}
#[derive(Copy, Clone, Default, Debug)]
pub struct LocationRef<'a>(&'a [NormalizedCoord]);
impl<'a> LocationRef<'a> {
pub fn new(coords: &'a [NormalizedCoord]) -> Self {
Self(coords)
}
pub fn coords(&self) -> &'a [NormalizedCoord] {
self.0
}
pub fn is_default(&self) -> bool {
self.0.is_empty() || self.0.iter().all(|coord| *coord == NormalizedCoord::ZERO)
}
pub(crate) fn effective_coords(&self) -> &'a [NormalizedCoord] {
if self.is_default() {
&[]
} else {
self.0
}
}
}
impl<'a> From<&'a [NormalizedCoord]> for LocationRef<'a> {
fn from(value: &'a [NormalizedCoord]) -> Self {
Self(value)
}
}
impl<'a> IntoIterator for LocationRef<'a> {
type IntoIter = core::slice::Iter<'a, NormalizedCoord>;
type Item = &'a NormalizedCoord;
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
impl<'a> IntoIterator for &'_ LocationRef<'a> {
type IntoIter = core::slice::Iter<'a, NormalizedCoord>;
type Item = &'a NormalizedCoord;
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
const MAX_INLINE_COORDS: usize = 8;
#[derive(Clone, Debug, Hash, Eq, PartialEq, Default)]
pub struct Location {
coords: SmallVec<NormalizedCoord, MAX_INLINE_COORDS>,
}
impl Location {
pub fn new(len: usize) -> Self {
Self {
coords: SmallVec::with_len(len, NormalizedCoord::default()),
}
}
pub fn coords(&self) -> &[NormalizedCoord] {
self.coords.as_slice()
}
pub fn coords_mut(&mut self) -> &mut [NormalizedCoord] {
self.coords.as_mut_slice()
}
}
impl<'a> From<&'a Location> for LocationRef<'a> {
fn from(value: &'a Location) -> Self {
LocationRef(value.coords())
}
}
impl<'a> IntoIterator for &'a Location {
type IntoIter = core::slice::Iter<'a, NormalizedCoord>;
type Item = &'a NormalizedCoord;
fn into_iter(self) -> Self::IntoIter {
self.coords().iter()
}
}
impl<'a> IntoIterator for &'a mut Location {
type IntoIter = core::slice::IterMut<'a, NormalizedCoord>;
type Item = &'a mut NormalizedCoord;
fn into_iter(self) -> Self::IntoIter {
self.coords_mut().iter_mut()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{FontRef, MetadataProvider};
#[test]
fn effective_coords() {
let font = FontRef::new(fontcull_font_test_data::AVAR2_CHECKER).unwrap();
let location = font.axes().location([("AVAR", 50.0), ("AVWK", 75.0)]);
let loc_ref = LocationRef::from(&location);
assert!(!loc_ref.is_default());
assert_eq!(loc_ref.effective_coords().len(), 2);
}
#[test]
fn effective_coords_for_default() {
let font = FontRef::new(fontcull_font_test_data::AVAR2_CHECKER).unwrap();
let location = font.axes().location([("AVAR", 0.0), ("AVWK", 0.0)]);
let loc_ref = LocationRef::from(&location);
assert!(loc_ref.is_default());
assert_eq!(loc_ref.effective_coords().len(), 0);
assert_eq!(loc_ref.coords().len(), 2);
}
}