mttf 0.1.4

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

use num_enum::{ FromPrimitive, IntoPrimitive };
use enumset::{ EnumSet, EnumSetType };
use std::fmt::{ Debug, Formatter, Result as FmtResult };
use bytemuck::{ Zeroable, Pod };

use super::ReadError;
use super::core::*;

#[derive(EnumSetType, Debug, PartialOrd, Ord)]
#[repr(u16)]
pub enum MacStyle {
	Bold = 0,
	Italic = 1,
	Underline = 2,
	Outline = 3,
	Shadow = 4,
	Condensed = 5,
	Extended = 6,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[derive(FromPrimitive, IntoPrimitive)]
#[repr(i16)]
#[num_enum(error_type(name = i16, constructor = i16::from))]
pub enum IndexToLocationFormat {
	Offset16 = 0,
	Offset32 = 1,

	#[num_enum(catch_all)]
	Unknown(i16),
}

#[derive(Clone, Copy, Zeroable, Pod)]
#[repr(transparent)]
pub struct HeaderTable([u8; 54]);

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

impl HeaderTable {
	pub fn version(&self) -> (u16, u16) { (self.uint16(0), self.uint16(2)) }
	pub fn font_revision(&self) -> Fixed { self.fixed(4) }
	pub fn checksum_adjustment(&self) -> u32 { self.uint32(8) }
	pub fn magic_number(&self) -> u32 { self.uint32(12) }
	pub fn flags(&self) -> u16 { self.uint16(16) }
	pub fn units_per_em(&self) -> u16 { self.uint16(18) }
	pub fn created_date(&self) -> LongDateTime { self.longdatetime(20) }
	pub fn modified_date(&self) -> LongDateTime { self.longdatetime(28) }
	pub fn bounding_box(&self) -> (i16, i16, i16, i16) {
		let min_x = self.int16(36);
		let min_y = self.int16(38);
		let max_x = self.int16(40);
		let max_y = self.int16(42);
		(min_x, min_y, max_x, max_y)
	}
	pub fn mac_style(&self) -> Result<EnumSet<MacStyle>, u16> {
		let value = self.uint16(44);
		EnumSet::<MacStyle>::try_from_u16(value).ok_or(value)
	}
	pub fn lowest_rec_ppem(&self) -> u16 { self.uint16(46) }
	pub fn font_direction_hint(&self) -> i16 { self.int16(48) }
	pub fn index_to_location_format(&self) -> IndexToLocationFormat { self.int16(50).into() }
	pub fn glyph_data_format(&self) -> i16 { self.int16(52) }
}

impl Debug for HeaderTable {
	fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
		f.debug_struct("HeaderTable")
			.field("version", &self.version())
			.field("font_revision", &self.font_revision())
			.field("checksum_adjustment", &self.checksum_adjustment())
			.field("magic_number", &self.magic_number())
			.field("flags", &self.flags())
			.field("units_per_em", &self.units_per_em())
			.field("created_date", &self.created_date())
			.field("modified_date", &self.modified_date())
			.field("bounding_box", &self.bounding_box())
			.field("mac_style", &self.mac_style())
			.field("lowest_rec_ppem", &self.lowest_rec_ppem())
			.field("font_direction_hint", &self.font_direction_hint())
			.field("index_to_location_format", &self.index_to_location_format())
			.field("glyph_data_format", &self.glyph_data_format())
			.finish()
	}
}

impl<'a> TryFrom<&'a [u8]> for &'a HeaderTable {
	type Error = ReadError;
	fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
		if value.len() < 54 {
			return Err(ReadError::UnexpectedEof);
		}

		let value: Self = bytemuck::from_bytes(&value[0..54]);

		let version = value.version();
		if version != (1, 0) {
			return Err(ReadError::UnsupportedTableVersionPair(version.0, version.1));
		}

		let magic_number = value.magic_number();
		if magic_number != 0x5F0F3CF5 {
			return Err(ReadError::UnsupportedMagicNumber(magic_number));
		}

		let font_direction_hint = value.font_direction_hint();
		if font_direction_hint != 2 {
			return Err(ReadError::UnsupportedFontDirectionHint(font_direction_hint));
		}

		let glyph_data_format = value.glyph_data_format();
		if glyph_data_format != 0 {
			return Err(ReadError::UnknownGlyphDataFormat(glyph_data_format));
		}

		Ok(value)
	}
}