use thiserror::Error; use num_enum::{ FromPrimitive, IntoPrimitive }; use std::ops::Range; use std::fmt::{ Debug, Formatter, Result as FmtResult }; use bytemuck::{ Zeroable, Pod }; use mchr::Pullable; use mchr::str::StdDecoder; pub type BytesIter<'a> = std::iter::Copied>; use super::ReadError; use super::core::*; use super::common::*; // Name Record #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] #[derive(FromPrimitive, IntoPrimitive)] #[repr(u16)] pub enum NameKind { CopyrightNotice = 0, FamilyName = 1, FontSubfamily = 2, UniqueSubfamilyId = 3, FullName = 4, Version = 5, PostScriptName = 6, TrademarkNotice = 7, Manufacturer = 8, Designer = 9, Description = 10, VendorUrl = 11, DesignerUrl = 12, LicenseDescription = 13, LicenseUrl = 14, PreferredFamily = 16, PreferredSubfamily = 17, CompatibleFull = 18, SampleText = 19, // TODO 20-24 VariationsPostScriptNamePrefix = 25, #[num_enum(catch_all)] Other(u16), } #[derive(Clone, Copy, Zeroable, Pod)] #[repr(transparent)] pub struct NameRecord([u8; 12]); impl<'a> RandomAccess<'a> for &'a NameRecord { fn bytes(&self) -> &'a [u8] { &self.0 } } impl NameRecord { pub fn encoding_and_language(&self) -> PlatformEncodingAndLanguage { (self.uint16(0), self.uint16(2), self.uint16(4)).into() } pub fn kind(&self) -> NameKind { NameKind::from(self.uint16(6)) } pub fn text(&self) -> Range { let start = self.uint16(10); let stop = start + self.uint16(8); start..stop } } impl Debug for NameRecord { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { f.debug_struct("NameRecord") .field("encoding_and_language", &self.encoding_and_language()) .field("kind", &self.kind()) .field("text", &self.text()) .finish() } } // Language Tag Record #[derive(Clone, Copy, Zeroable, Pod)] #[repr(transparent)] pub struct LangTagRecord([u8; 4]); impl<'a> RandomAccess<'a> for &'a LangTagRecord { fn bytes(&self) -> &'a [u8] { &self.0 } } impl LangTagRecord { pub fn text(&self) -> Range { let start = self.uint16(2); let stop = start + self.uint16(0); start..stop } } impl Debug for LangTagRecord { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { f.debug_struct("LangTagRecord") .field("text", &self.text()) .finish() } } // Naming Table #[derive(Error, Debug)] pub enum StringError { #[error("invalid string range: {0:?}")] InvalidStringRange(Range), #[error("unsupported encoding: {0:?}")] UnsupportedPlatformEncoding(PlatformEncodingAndLanguage), } #[derive(Clone, Copy)] #[repr(transparent)] pub struct NamingTable<'a>(&'a [u8]); impl<'a> RandomAccess<'a> for NamingTable<'a> { fn bytes(&self) -> &'a [u8] { self.0 } } impl<'a> NamingTable<'a> { pub fn version(&self) -> u16 { self.uint16(0) } pub fn name_count(&self) -> u16 { self.uint16(2) } pub fn string_offset(&self) -> u16 { self.uint16(4) } pub fn name_records(&self) -> &'a [NameRecord] { self.array(6, self.name_count() as usize) } pub fn string_data(&self, range: Range) -> Result<&'a [u8], Range> { let offset = self.string_offset() as usize; let start = offset + range.start as usize; let stop = offset + range.end as usize; let data = self.bytes(); if stop <= data.len() { Ok(&data[start..stop]) } else { Err(range) } } pub fn string(&self, record: &NameRecord) -> Result>, StringError> { let data = self.string_data(record.text()).map_err(StringError::InvalidStringRange)?; let encoding = record.encoding_and_language(); let encoding = encoding.character_encoding().ok_or(StringError::UnsupportedPlatformEncoding(encoding))?; Ok(data.iter().copied().transcode(encoding)) } } impl<'a> Debug for NamingTable<'a> { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { f.debug_list() .entries(self.iter()) .finish() } } impl<'a> TryFrom<&'a [u8]> for NamingTable<'a> { type Error = ReadError; fn try_from(value: &'a [u8]) -> Result { if value.len() < 6 { return Err(ReadError::UnexpectedEof); } let value = NamingTable(value); let version = value.version(); if !matches!(version, 0 | 1) { return Err(ReadError::UnsupportedTableVersionSingle(version)); } if value.bytes().len() < 6 + value.name_count() as usize * 12 { return Err(ReadError::UnexpectedEof); } Ok(value) } } // Iterator #[derive(Clone, Copy)] pub struct Name<'a> { table: NamingTable<'a>, record: &'a NameRecord, } impl<'a> Name<'a> { pub fn encoding_and_language(&self) -> PlatformEncodingAndLanguage { self.record.encoding_and_language() } pub fn kind(&self) -> NameKind { self.record.kind() } pub fn text(&self) -> Result>, StringError> { self.table.string(self.record) } } impl<'a> NamingTable<'a> { pub fn iter(&self) -> impl ExactSizeIterator> + '_ { self.name_records().iter().map(|record| Name { table: *self, record }) } } impl<'a> Debug for Name<'a> { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { f.debug_struct("Name") .field("encoding_and_language", &self.encoding_and_language()) .field("kind", &self.kind()) .field("text", &self.text().map(|x| x.buffered().collect::())) .finish() } }