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, u16> { let value = self.uint16(44); EnumSet::::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 { 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) } }