mttf 0.1.4

A library for working with TrueType fonts. Most parts are zero-allocation.
Documentation

use std::mem::size_of;
use std::fmt::{ Debug, Formatter, Result as FmtResult };
use bytemuck::{ Zeroable, Pod };
use chrono::{ DateTime, Utc };

#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Fixed(i16, u16);

impl Fixed {
	pub const ZERO: Self = Self(0, 0);
	pub const ONE: Self = Self(1, 0);

	pub const fn integer(&self) -> i16 { self.0 }
	pub const fn fraction(&self) -> u16 { self.1 }

	pub fn fraction_f32(&self) -> f32 { self.fraction() as f32 / 65536.0 } // TODO is it faster to encode it myself?
	pub fn fraction_f64(&self) -> f64 { self.fraction() as f64 / 65536.0 } // TODO is it faster to encode it myself?

	pub fn as_f32(&self) -> f32 { self.integer() as f32 + self.fraction_f32() }
	pub fn as_f64(&self) -> f64 { self.integer() as f64 + self.fraction_f64() }
}

impl Debug for Fixed {
	fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
		write!(f, "{}", self.as_f32())
	}
}

#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct F2Dot14(i16);

impl F2Dot14 {
	pub const ZERO: Self = Self(0);
	pub const ONE: Self = Self(0x4000);

	pub const fn integer(&self) -> i16 { self.0 >> 14 }
	pub const fn fraction(&self) -> u16 { self.0 as u16 & 0x3FFF }

	pub fn fraction_f32(&self) -> f32 { self.fraction() as f32 / 16384.0 } // TODO is it faster to encode it myself?
	pub fn fraction_f64(&self) -> f64 { self.fraction() as f64 / 16384.0 } // TODO is it faster to encode it myself?

	pub fn as_f32(&self) -> f32 { self.integer() as f32 + self.fraction_f32() }
	pub fn as_f64(&self) -> f64 { self.integer() as f64 + self.fraction_f64() }
}

impl Debug for F2Dot14 {
	fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
		write!(f, "{}", self.as_f32())
	}
}

pub type LongDateTime = Result<DateTime<Utc>, i64>;

#[derive(Clone, Copy, Zeroable, Pod, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct Tag([u8; 4]);

impl Tag {
	pub const fn new(value: &[u8; 4]) -> Self { Self(*value) }
}

impl From<[u8; 4]> for Tag {
	fn from(value: [u8; 4]) -> Self { Self(value) }
}

impl AsRef<[u8; 4]> for Tag {
	fn as_ref(&self) -> &[u8; 4] { &self.0 }
}

impl Debug for Tag {
	fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
		write!(f, "{:?}", std::str::from_utf8(&self.0).unwrap())
	}
}

#[derive(Clone, Copy)]
pub struct Version16Dot16(pub u16, pub u16);

impl Debug for Version16Dot16 {
	fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
		write!(f, "Version16Dot16({}, {})", self.0, self.1)
	}
}

// Random Access

pub trait RandomAccess<'a> {

	fn bytes(&self) -> &'a [u8];

	fn uint8(&self, offset: usize) -> u8 { u8::from_be_bytes(*self.item(offset)) }
	fn uint16(&self, offset: usize) -> u16 { u16::from_be_bytes(*self.item(offset)) }
	fn uint32(&self, offset: usize) -> u32 { u32::from_be_bytes(*self.item(offset)) }

	fn uint24(&self, offset: usize) -> u32 {
		let bytes: &[u8; 3] = self.item(offset);
		u32::from_be_bytes([0, bytes[0], bytes[1], bytes[2]])
	}

	fn int8(&self, offset: usize) -> i8 { i8::from_be_bytes(*self.item(offset)) }
	fn int16(&self, offset: usize) -> i16 { i16::from_be_bytes(*self.item(offset)) }
	fn int32(&self, offset: usize) -> i32 { i32::from_be_bytes(*self.item(offset)) }

	fn fixed(&self, offset: usize) -> Fixed {
		let value = self.uint32(offset);
		Fixed((value >> 16) as i16, (value & 0xFFFF) as u16)
	}

	fn f2dot14(&self, offset: usize) -> F2Dot14 { F2Dot14(self.int16(offset)) }

	fn longdatetime(&self, offset: usize) -> LongDateTime {
		let timestamp = i64::from_be_bytes(*self.item(offset));
		DateTime::from_timestamp(timestamp - 2082844800, 0).ok_or(timestamp)
	}

	fn tag(&self, offset: usize) -> &'a Tag { self.item(offset) }

	fn version16dot16(&self, offset: usize) -> Version16Dot16 {
		let value = self.uint32(offset);
		Version16Dot16((value >> 16) as u16, (value & 0xFFFF) as u16)
	}

	fn item<T: Pod>(&self, offset: usize) -> &'a T {
		bytemuck::from_bytes(&self.bytes()[offset..offset + size_of::<T>()])
	}

	fn array<T: Pod>(&self, offset: usize, count: usize) -> &'a [T] {
		bytemuck::cast_slice(&self.bytes()[offset..offset + size_of::<T>() * count])
	}
	
	fn uint16_array(&self, offset: usize, count: usize) -> U16Array<'a> {
		let end = offset + (count << 1);
		U16Array(&self.bytes()[offset..end])
	}
	
	fn int16_array(&self, offset: usize, count: usize) -> I16Array<'a> {
		let end = offset + (count << 1);
		I16Array(&self.bytes()[offset..end])
	}
	
	fn uint32_array(&self, offset: usize, count: usize) -> U32Array<'a> {
		let end = offset + (count << 2);
		U32Array(&self.bytes()[offset..end])
	}
}

impl<'a> RandomAccess<'a> for &'a [u8] {
	fn bytes(&self) -> &'a [u8] { self }
}

// U16Array

#[derive(Clone, Copy)]
pub struct U16Array<'a>(&'a [u8]);

impl<'a> RandomAccess<'a> for U16Array<'a> {
	fn bytes(&self) -> &'a [u8] { self.0 }
}

impl<'a> U16Array<'a> {
	pub fn len(&self) -> usize { self.0.len() >> 1 }
	pub fn is_empty(&self) -> bool { self.len() == 0 }
	pub fn get(&self, index: usize) -> u16 { self.0.uint16(index << 1) }
	pub fn iter(&self) -> impl ExactSizeIterator<Item = u16> + '_ { (0..self.0.len()).step_by(2).map(|x| self.0.uint16(x)) }
}

impl<'a> Debug for U16Array<'a> {
	fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
		f.debug_list()
			.entries(self.iter())
			.finish()
	}
}

// I16Array

#[derive(Clone, Copy)]
pub struct I16Array<'a>(&'a [u8]);

impl<'a> RandomAccess<'a> for I16Array<'a> {
	fn bytes(&self) -> &'a [u8] { self.0 }
}

impl<'a> I16Array<'a> {
	pub fn len(&self) -> usize { self.0.len() >> 1 }
	pub fn is_empty(&self) -> bool { self.len() == 0 }
	pub fn get(&self, index: usize) -> i16 { self.0.int16(index << 1) }
	pub fn iter(&self) -> impl ExactSizeIterator<Item = i16> + '_ { (0..self.0.len()).step_by(2).map(|x| self.0.int16(x)) }
}

impl<'a> Debug for I16Array<'a> {
	fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
		f.debug_list()
			.entries(self.iter())
			.finish()
	}
}

// U32Array

#[derive(Clone, Copy)]
pub struct U32Array<'a>(&'a [u8]);

impl<'a> RandomAccess<'a> for U32Array<'a> {
	fn bytes(&self) -> &'a [u8] { self.0 }
}

impl<'a> U32Array<'a> {
	pub fn len(&self) -> usize { self.0.len() >> 2 }
	pub fn is_empty(&self) -> bool { self.len() == 0 }
	pub fn get(&self, index: usize) -> u32 { self.0.uint32(index << 2) }
	pub fn iter(&self) -> impl ExactSizeIterator<Item = u32> + '_ { (0..self.0.len()).step_by(4).map(|x| self.0.uint32(x)) }
}

impl<'a> Debug for U32Array<'a> {
	fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
		f.debug_list()
			.entries(self.iter())
			.finish()
	}
}