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)
}
}