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 LongHorizontalMetric([u8; 4]);

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

impl LongHorizontalMetric {
	pub fn advance_width(&self) -> u16 { self.uint16(0) }
	pub fn left_side_bearing(&self) -> i16 { self.int16(2) }
}

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

#[derive(Clone, Copy)]
pub struct HorizontalMetric {
	pub advance_width: u16,
	pub left_side_bearing: i16,
}

impl From<&LongHorizontalMetric> for HorizontalMetric {
	fn from(value: &LongHorizontalMetric) -> Self {
		Self {
			advance_width: value.advance_width(),
			left_side_bearing: value.left_side_bearing(),
		}
	}
}

impl From<LongHorizontalMetric> for HorizontalMetric {
	fn from(value: LongHorizontalMetric) -> Self {
		Self {
			advance_width: value.advance_width(),
			left_side_bearing: value.left_side_bearing(),
		}
	}
}

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

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

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

impl<'a> HorizontalMetricsTable<'a> {
	pub fn long_horizontal_metrics(&self) -> &'a [LongHorizontalMetric] { self.array(0, self.number_of_hmetrics as usize) }
	pub fn remaining_left_side_bearings(&self) -> I16Array<'a> {
		self.int16_array(self.number_of_hmetrics as usize * 4, self.remaining_count as usize)
	}

	pub fn default_advance_width(&self) -> Option<u16> {
		self.long_horizontal_metrics().last().map(LongHorizontalMetric::advance_width)
	}

	pub fn get(&self, glyph_index: u16) -> HorizontalMetric {
		if glyph_index < self.number_of_hmetrics {
			let long_hor = &self.long_horizontal_metrics()[glyph_index as usize];
			HorizontalMetric::from(long_hor)
		} else {
			let advance_width = self.default_advance_width().unwrap_or_default();
			let left_side_bearing = self.remaining_left_side_bearings().get(glyph_index as usize - self.number_of_hmetrics as usize);
			HorizontalMetric { advance_width, left_side_bearing }
		}
	}

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

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

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