mttf 0.1.7

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

use std::ops::Deref;
use std::fmt::{ Debug, Formatter, Result as FmtResult };
use bytemuck::{ Zeroable, Pod };

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

// Version 0

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

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

impl Os2MetricsTable0 {
	pub fn version(&self) -> u16 { self.uint16(0) }
	pub fn avg_char_width(&self) -> i16 { self.int16(2) }
	pub fn weight_class(&self) -> u16 { self.uint16(4) }
	pub fn width_class(&self) -> u16 { self.uint16(6) }
	pub fn type_flags(&self) -> u16 { self.uint16(8) }
	pub fn subscript_size(&self) -> (i16, i16) { (self.int16(10), self.int16(12)) }
	pub fn subscript_offset(&self) -> (i16, i16) { (self.int16(14), self.int16(16)) }
	pub fn superscript_size(&self) -> (i16, i16) { (self.int16(18), self.int16(20)) }
	pub fn superscript_offset(&self) -> (i16, i16) { (self.int16(22), self.int16(24)) }
	pub fn strikeout_size(&self) -> i16 { self.int16(26) }
	pub fn strikeout_position(&self) -> i16 { self.int16(28) }
	pub fn family_class(&self) -> i16 { self.int16(30) }
	pub fn panose(&self) -> &[u8; 10] { self.item(32) }
	pub fn unicode_range_1(&self) -> u32 { self.uint32(42) }
	pub fn unicode_range_2(&self) -> u32 { self.uint32(46) }
	pub fn unicode_range_3(&self) -> u32 { self.uint32(50) }
	pub fn unicode_range_4(&self) -> u32 { self.uint32(54) }
	pub fn vendor_id(&self) -> &Tag { self.item(58) }
	pub fn selection_flags(&self) -> u16 { self.uint16(62) }
	pub fn first_char_index(&self) -> u16 { self.uint16(64) }
	pub fn last_char_index(&self) -> u16 { self.uint16(66) }
	pub fn typo_ascender(&self) -> i16 { self.int16(68) }
	pub fn typo_descender(&self) -> i16 { self.int16(70) }
	pub fn typo_line_gap(&self) -> i16 { self.int16(72) }
	pub fn win_ascent(&self) -> u16 { self.uint16(74) }
	pub fn win_descent(&self) -> u16 { self.uint16(76) }
}

impl Debug for Os2MetricsTable0 {
	fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
		f.debug_struct("Os2MetricsTable0")
			.field("version", &self.version())
			.field("avg_char_width", &self.avg_char_width())
			.field("weight_class", &self.weight_class())
			.field("width_class", &self.width_class())
			.field("type_flags", &self.type_flags())
			.field("subscript_size", &self.subscript_size())
			.field("subscript_offset", &self.subscript_offset())
			.field("superscript_size", &self.superscript_size())
			.field("superscript_offset", &self.superscript_offset())
			.field("strikeout_size", &self.strikeout_size())
			.field("strikeout_position", &self.strikeout_position())
			.field("family_class", &self.family_class())
			.field("panose", &self.panose())
			.field("unicode_range_1", &self.unicode_range_1())
			.field("unicode_range_2", &self.unicode_range_2())
			.field("unicode_range_3", &self.unicode_range_3())
			.field("unicode_range_4", &self.unicode_range_4())
			.field("vendor_id", &self.vendor_id())
			.field("selection_flags", &self.selection_flags())
			.field("first_char_index", &self.first_char_index())
			.field("last_char_index", &self.last_char_index())
			.field("typo_ascender", &self.typo_ascender())
			.field("typo_descender", &self.typo_descender())
			.field("typo_line_gap", &self.typo_line_gap())
			.field("win_ascent", &self.win_ascent())
			.field("win_descent", &self.win_descent())
			.finish()
	}
}

// Version 1

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

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

impl AsRef<Os2MetricsTable0> for Os2MetricsTable1 {
	fn as_ref(&self) -> &Os2MetricsTable0 { bytemuck::from_bytes(&self.0[0..78]) }
}

impl Deref for Os2MetricsTable1 {
	type Target = Os2MetricsTable0;
	fn deref(&self) -> &Self::Target { self.as_ref() }
}

impl Os2MetricsTable1 {
	pub fn code_page_range_1(&self) -> u32 { self.uint32(78) }
	pub fn code_page_range_2(&self) -> u32 { self.uint32(82) }
}

impl Debug for Os2MetricsTable1 {
	fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
		f.debug_struct("Os2MetricsTable1")
			.field("version", &self.version())
			.field("avg_char_width", &self.avg_char_width())
			.field("weight_class", &self.weight_class())
			.field("width_class", &self.width_class())
			.field("type_flags", &self.type_flags())
			.field("subscript_size", &self.subscript_size())
			.field("subscript_offset", &self.subscript_offset())
			.field("superscript_size", &self.superscript_size())
			.field("superscript_offset", &self.superscript_offset())
			.field("strikeout_size", &self.strikeout_size())
			.field("strikeout_position", &self.strikeout_position())
			.field("family_class", &self.family_class())
			.field("panose", &self.panose())
			.field("unicode_range_1", &self.unicode_range_1())
			.field("unicode_range_2", &self.unicode_range_2())
			.field("unicode_range_3", &self.unicode_range_3())
			.field("unicode_range_4", &self.unicode_range_4())
			.field("vendor_id", &self.vendor_id())
			.field("selection_flags", &self.selection_flags())
			.field("first_char_index", &self.first_char_index())
			.field("last_char_index", &self.last_char_index())
			.field("typo_ascender", &self.typo_ascender())
			.field("typo_descender", &self.typo_descender())
			.field("typo_line_gap", &self.typo_line_gap())
			.field("win_ascent", &self.win_ascent())
			.field("win_descent", &self.win_descent())
			.field("code_page_range_1", &self.code_page_range_1())
			.field("code_page_range_2", &self.code_page_range_2())
			.finish()
	}
}

// Version 2-4

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

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

impl AsRef<Os2MetricsTable0> for Os2MetricsTable2 {
	fn as_ref(&self) -> &Os2MetricsTable0 { bytemuck::from_bytes(&self.0[0..78]) }
}

impl AsRef<Os2MetricsTable1> for Os2MetricsTable2 {
	fn as_ref(&self) -> &Os2MetricsTable1 { bytemuck::from_bytes(&self.0[0..86]) }
}

impl Deref for Os2MetricsTable2 {
	type Target = Os2MetricsTable1;
	fn deref(&self) -> &Self::Target { self.as_ref() }
}

impl Os2MetricsTable2 {
	pub fn x_height(&self) -> i16 { self.int16(86) }
	pub fn cap_height(&self) -> i16 { self.int16(88) }
	pub fn default_char(&self) -> u16 { self.uint16(90) }
	pub fn break_char(&self) -> u16 { self.uint16(92) }
	pub fn max_context(&self) -> u16 { self.uint16(94) }
}

impl Debug for Os2MetricsTable2 {
	fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
		f.debug_struct("Os2MetricsTable2")
			.field("version", &self.version())
			.field("avg_char_width", &self.avg_char_width())
			.field("weight_class", &self.weight_class())
			.field("width_class", &self.width_class())
			.field("type_flags", &self.type_flags())
			.field("subscript_size", &self.subscript_size())
			.field("subscript_offset", &self.subscript_offset())
			.field("superscript_size", &self.superscript_size())
			.field("superscript_offset", &self.superscript_offset())
			.field("strikeout_size", &self.strikeout_size())
			.field("strikeout_position", &self.strikeout_position())
			.field("family_class", &self.family_class())
			.field("panose", &self.panose())
			.field("unicode_range_1", &self.unicode_range_1())
			.field("unicode_range_2", &self.unicode_range_2())
			.field("unicode_range_3", &self.unicode_range_3())
			.field("unicode_range_4", &self.unicode_range_4())
			.field("vendor_id", &self.vendor_id())
			.field("selection_flags", &self.selection_flags())
			.field("first_char_index", &self.first_char_index())
			.field("last_char_index", &self.last_char_index())
			.field("typo_ascender", &self.typo_ascender())
			.field("typo_descender", &self.typo_descender())
			.field("typo_line_gap", &self.typo_line_gap())
			.field("win_ascent", &self.win_ascent())
			.field("win_descent", &self.win_descent())
			.field("code_page_range_1", &self.code_page_range_1())
			.field("code_page_range_2", &self.code_page_range_2())
			.field("x_height", &self.x_height())
			.field("cap_height", &self.cap_height())
			.field("default_char", &self.default_char())
			.field("break_char", &self.break_char())
			.field("max_context", &self.max_context())
			.finish()
	}
}

// Version 5

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

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

impl AsRef<Os2MetricsTable0> for Os2MetricsTable5 {
	fn as_ref(&self) -> &Os2MetricsTable0 { bytemuck::from_bytes(&self.0[0..78]) }
}

impl AsRef<Os2MetricsTable1> for Os2MetricsTable5 {
	fn as_ref(&self) -> &Os2MetricsTable1 { bytemuck::from_bytes(&self.0[0..86]) }
}

impl AsRef<Os2MetricsTable2> for Os2MetricsTable5 {
	fn as_ref(&self) -> &Os2MetricsTable2 { bytemuck::from_bytes(&self.0[0..96]) }
}

impl Deref for Os2MetricsTable5 {
	type Target = Os2MetricsTable2;
	fn deref(&self) -> &Self::Target { self.as_ref() }
}

impl Os2MetricsTable5 {
	pub fn lower_optical_point_size(&self) -> u16 { self.uint16(96) }
	pub fn upper_optical_point_size(&self) -> u16 { self.uint16(98) }
}

impl Debug for Os2MetricsTable5 {
	fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
		f.debug_struct("Os2MetricsTable5")
			.field("version", &self.version())
			.field("avg_char_width", &self.avg_char_width())
			.field("weight_class", &self.weight_class())
			.field("width_class", &self.width_class())
			.field("type_flags", &self.type_flags())
			.field("subscript_size", &self.subscript_size())
			.field("subscript_offset", &self.subscript_offset())
			.field("superscript_size", &self.superscript_size())
			.field("superscript_offset", &self.superscript_offset())
			.field("strikeout_size", &self.strikeout_size())
			.field("strikeout_position", &self.strikeout_position())
			.field("family_class", &self.family_class())
			.field("panose", &self.panose())
			.field("unicode_range_1", &self.unicode_range_1())
			.field("unicode_range_2", &self.unicode_range_2())
			.field("unicode_range_3", &self.unicode_range_3())
			.field("unicode_range_4", &self.unicode_range_4())
			.field("vendor_id", &self.vendor_id())
			.field("selection_flags", &self.selection_flags())
			.field("first_char_index", &self.first_char_index())
			.field("last_char_index", &self.last_char_index())
			.field("typo_ascender", &self.typo_ascender())
			.field("typo_descender", &self.typo_descender())
			.field("typo_line_gap", &self.typo_line_gap())
			.field("win_ascent", &self.win_ascent())
			.field("win_descent", &self.win_descent())
			.field("code_page_range_1", &self.code_page_range_1())
			.field("code_page_range_2", &self.code_page_range_2())
			.field("x_height", &self.x_height())
			.field("cap_height", &self.cap_height())
			.field("default_char", &self.default_char())
			.field("break_char", &self.break_char())
			.field("max_context", &self.max_context())
			.field("lower_optical_point_size", &self.lower_optical_point_size())
			.field("upper_optical_point_size", &self.upper_optical_point_size())
			.finish()
	}
}

// Generic

#[derive(Clone, Copy)]
pub enum Os2MetricsTable<'a> {
	Version0(&'a Os2MetricsTable0),
	Version1(&'a Os2MetricsTable1),
	Version2(&'a Os2MetricsTable2),
	Version3(&'a Os2MetricsTable2),
	Version4(&'a Os2MetricsTable2),
	Version5(&'a Os2MetricsTable5),
}

impl<'a> RandomAccess<'a> for Os2MetricsTable<'a> {
	fn bytes(&self) -> &'a [u8] {
		match self {
			Self::Version0(x) => x.bytes(),
			Self::Version1(x) => x.bytes(),
			Self::Version2(x) => x.bytes(),
			Self::Version3(x) => x.bytes(),
			Self::Version4(x) => x.bytes(),
			Self::Version5(x) => x.bytes(),
		}
	}
}

impl<'a> Deref for Os2MetricsTable<'a> {
	type Target = Os2MetricsTable0;
	fn deref(&self) -> &Self::Target {
		match self {
			Self::Version0(x) => x,
			Self::Version1(x) => x,
			Self::Version2(x) => x,
			Self::Version3(x) => x,
			Self::Version4(x) => x,
			Self::Version5(x) => x,
		}
	}
}

impl<'a> Debug for Os2MetricsTable<'a> {
	fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
		match self {
			Self::Version0(x) => x.fmt(f),
			Self::Version1(x) => x.fmt(f),
			Self::Version2(x) => x.fmt(f),
			Self::Version3(x) => x.fmt(f),
			Self::Version4(x) => x.fmt(f),
			Self::Version5(x) => x.fmt(f),
		}
	}
}

impl<'a> TryFrom<&'a [u8]> for Os2MetricsTable<'a> {
	type Error = ReadError;
	fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
		if value.len() < 2 { return Err(ReadError::UnexpectedEof); }
		match value.uint16(0) {
			0 => {
				if value.len() < 78 { return Err(ReadError::UnexpectedEof); }
				Ok(Os2MetricsTable::Version0(bytemuck::from_bytes(&value[0..78])))
			},
			1 => {
				if value.len() < 86 { return Err(ReadError::UnexpectedEof); }
				Ok(Os2MetricsTable::Version1(bytemuck::from_bytes(&value[0..86])))
			},
			2 => {
				if value.len() < 96 { return Err(ReadError::UnexpectedEof); }
				Ok(Os2MetricsTable::Version2(bytemuck::from_bytes(&value[0..96])))
			},
			3 => {
				if value.len() < 96 { return Err(ReadError::UnexpectedEof); }
				Ok(Os2MetricsTable::Version3(bytemuck::from_bytes(&value[0..96])))
			},
			4 => {
				if value.len() < 96 { return Err(ReadError::UnexpectedEof); }
				Ok(Os2MetricsTable::Version4(bytemuck::from_bytes(&value[0..96])))
			},
			5 => {
				if value.len() < 100 { return Err(ReadError::UnexpectedEof); }
				Ok(Os2MetricsTable::Version5(bytemuck::from_bytes(&value[0..100])))
			},
			x => Err(ReadError::UnsupportedTableVersionSingle(x))
		}
	}
}