mttf 0.1.7

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

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

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

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

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

impl LongVerticalMetric {
	pub fn advance_height(&self) -> u16 { self.uint16(0) }
	pub fn top_side_bearing(&self) -> i16 { self.int16(2) }
}

impl Debug for LongVerticalMetric {
	fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
		f.debug_struct("LongVerticalMetric")
			.field("advance_height", &self.advance_height())
			.field("top_side_bearing", &self.top_side_bearing())
			.finish()
	}
}

#[derive(Clone, Copy)]
pub struct VerticalMetric {
	pub advance_height: u16,
	pub top_side_bearing: i16,
}

impl From<&LongVerticalMetric> for VerticalMetric {
	fn from(value: &LongVerticalMetric) -> Self {
		Self {
			advance_height: value.advance_height(),
			top_side_bearing: value.top_side_bearing(),
		}
	}
}

impl From<LongVerticalMetric> for VerticalMetric {
	fn from(value: LongVerticalMetric) -> Self {
		Self {
			advance_height: value.advance_height(),
			top_side_bearing: value.top_side_bearing(),
		}
	}
}

impl Debug for VerticalMetric {
	fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
		f.debug_struct("VerticalMetric")
			.field("advance_height", &self.advance_height)
			.field("top_side_bearing", &self.top_side_bearing)
			.finish()
	}
}

#[derive(Clone, Copy)]
pub struct VerticalMetricsTable<'a> {
	number_of_vmetrics: u16,
	remaining_count: u16,
	data: &'a [u8],
}

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

impl<'a> VerticalMetricsTable<'a> {
	pub fn long_vertical_metrics(&self) -> &'a [LongVerticalMetric] { self.array(0, self.number_of_vmetrics as usize) }
	pub fn remaining_top_side_bearings(&self) -> I16Array<'a> {
		self.int16_array(self.number_of_vmetrics as usize * 4, self.remaining_count as usize)
	}

	pub fn default_advance_height(&self) -> Option<u16> {
		self.long_vertical_metrics().last().map(LongVerticalMetric::advance_height)
	}

	pub fn get(&self, glyph_index: u16) -> VerticalMetric {
		if glyph_index < self.number_of_vmetrics {
			let long_hor = &self.long_vertical_metrics()[glyph_index as usize];
			VerticalMetric::from(long_hor)
		} else {
			let advance_height = self.default_advance_height().unwrap_or_default();
			let top_side_bearing = self.remaining_top_side_bearings().get(glyph_index as usize - self.number_of_vmetrics as usize);
			VerticalMetric { advance_height, top_side_bearing }
		}
	}

	pub fn iter(&self) -> impl ExactSizeIterator<Item = VerticalMetric> + '_ {
		(0..self.number_of_vmetrics + self.remaining_count).map(|i| self.get(i))
	}
}

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

impl<'a> VerticalMetricsTable<'a> {
	pub fn try_from(data: &'a [u8], number_of_vmetrics: u16, num_glyphs: u16) -> Result<Self, ReadError> {
		let remaining_count = num_glyphs - number_of_vmetrics;
		if data.len() < number_of_vmetrics as usize * 4 + remaining_count as usize * 2 {
			return Err(ReadError::UnexpectedEof);
		}
		Ok(VerticalMetricsTable { number_of_vmetrics, remaining_count, data })
	}
}