//! Experimental generic traversal of font tables. //! //! This module defines functionality that allows untyped access to font table //! data. This is used as the basis for things like debug printing. //! //! The basis of traversal is the [`SomeTable`] trait, which is implemented for //! all font tables. This trait provides the table's name, as well as ordered access //! to the table's fields. Using this, it is possible to iterate through a table //! and its subtables, records, and values. //! //! # Warning //! //! This functionality is considered experimental, and the API may break or be //! removed without warning. use std::{fmt::Debug, ops::Deref}; use types::{ BigEndian, F2Dot14, FWord, Fixed, GlyphId16, GlyphId24, Int24, LongDateTime, MajorMinor, NameId, Nullable, Offset16, Offset24, Offset32, Scalar, Tag, UfWord, Uint24, Version16Dot16, }; use crate::{ array::{ComputedArray, VarLenArray}, read::{ComputeSize, ReadArgs}, FontData, FontRead, FontReadWithArgs, ReadError, VarSize, }; /// Types of fields in font tables. /// /// Fields can either be scalars, offsets to tables, or arrays. pub enum FieldType<'a> { I8(i8), U8(u8), I16(i16), U16(u16), I32(i32), U32(u32), I24(Int24), U24(Uint24), Tag(Tag), FWord(FWord), UfWord(UfWord), MajorMinor(MajorMinor), Version16Dot16(Version16Dot16), F2Dot14(F2Dot14), Fixed(Fixed), LongDateTime(LongDateTime), GlyphId16(GlyphId16), GlyphId24(GlyphId24), NameId(NameId), BareOffset(OffsetType), ResolvedOffset(ResolvedOffset<'a>), /// Used in tables like name/post so we can actually print the strings StringOffset(StringOffset<'a>), /// Used in COLR/CPAL ArrayOffset(ArrayOffset<'a>), Record(RecordResolver<'a>), Array(Box + 'a>), Unknown, } /// Any offset type. #[derive(Clone, Copy)] pub enum OffsetType { Offset16(u16), Offset24(Uint24), Offset32(u32), } impl OffsetType { /// Return this offset as a u32. pub fn to_u32(self) -> u32 { match self { Self::Offset16(val) => val.into(), Self::Offset24(val) => val.into(), Self::Offset32(val) => val, } } } /// An offset, as well as the table it references. pub struct ResolvedOffset<'a> { /// The raw offset pub offset: OffsetType, /// The parsed table pointed to by this offset, or an error if parsing fails. pub target: Result + 'a>, ReadError>, } /// An offset to string data. /// /// This is a special case for the name table (and maybe elsewhere?) pub struct StringOffset<'a> { pub offset: OffsetType, pub target: Result + 'a>, ReadError>, } /// An offset to an array. pub struct ArrayOffset<'a> { pub offset: OffsetType, pub target: Result + 'a>, ReadError>, } impl<'a> FieldType<'a> { /// makes a field, handling the case where this array may not be present in /// all versions pub fn array_of_records( type_name: &'static str, records: &'a [T], data: FontData<'a>, ) -> FieldType<'a> where T: Clone + SomeRecord<'a> + 'a, { ArrayOfRecords { type_name, data, records, } .into() } // Convenience method for handling computed arrays pub fn computed_array( type_name: &'static str, array: ComputedArray<'a, T>, data: FontData<'a>, ) -> FieldType<'a> where T: FontReadWithArgs<'a> + ComputeSize + SomeRecord<'a> + 'a, T::Args: Copy + 'static, { ComputedArrayOfRecords { type_name, data, array, } .into() } // Convenience method for handling VarLenArrays pub fn var_array( type_name: &'static str, array: VarLenArray<'a, T>, data: FontData<'a>, ) -> FieldType<'a> where T: FontRead<'a> + VarSize + SomeRecord<'a> + 'a, { VarLenArrayOfRecords { type_name, data, array, } .into() } /// Convenience method for creating a `FieldType` from an offset to an array. pub fn offset_to_array_of_scalars + 'a>( offset: impl Into, result: impl Into>>, ) -> Self { let offset = offset.into(); match result.into() { Some(target) => FieldType::ArrayOffset(ArrayOffset { offset, target: target.map(|x| Box::new(x) as Box), }), None => FieldType::BareOffset(offset), } } /// Convenience method for creating a `FieldType` from an offset to an array. pub fn offset_to_array_of_records + 'a>( offset: impl Into, result: impl Into>>, type_name: &'static str, data: FontData<'a>, ) -> Self { let offset = offset.into(); match result.into() { Some(target) => { let target = target.map(|records| { Box::new(ArrayOfRecords { type_name, data, records, }) as Box }); FieldType::ArrayOffset(ArrayOffset { offset, target }) } None => FieldType::BareOffset(offset), } } //FIXME: I bet this is generating a *lot* of code /// Convenience method for creating a `FieldType` for a resolved offset. /// /// This handles cases where offsets are nullable, in which case the `result` /// argument may be `None`. pub fn offset + 'a>( offset: impl Into, result: impl Into>>, ) -> Self { let offset = offset.into(); match result.into() { Some(target) => FieldType::ResolvedOffset(ResolvedOffset { offset, target: target.map(|x| Box::new(x) as Box), }), None => FieldType::BareOffset(offset), } } /// Convenience method for creating a `FieldType` from an unknown offset. pub fn unknown_offset(offset: impl Into) -> Self { Self::BareOffset(offset.into()) } } /// A generic field in a font table. pub struct Field<'a> { /// The field's name. pub name: &'static str, /// The field's value. pub value: FieldType<'a>, } /// A generic table type. /// /// This is intended to be used as a trait object, and is a way of generically /// representing any table, providing ordered access to that table's fields. pub trait SomeTable<'a> { /// The name of this table fn type_name(&self) -> &str; /// Access this table's fields, in declaration order. fn get_field(&self, idx: usize) -> Option>; } impl<'a> dyn SomeTable<'a> + 'a { /// Returns an iterator over this table's fields. pub fn iter(&self) -> impl Iterator> + '_ { FieldIter { table: self, idx: 0, } } } struct FieldIter<'a, 'b> { table: &'b dyn SomeTable<'a>, idx: usize, } impl<'a> Iterator for FieldIter<'a, '_> { type Item = Field<'a>; fn next(&mut self) -> Option { let this = self.idx; self.idx += 1; self.table.get_field(this) } } impl<'a> SomeTable<'a> for Box + 'a> { fn type_name(&self) -> &str { self.deref().type_name() } fn get_field(&self, idx: usize) -> Option> { self.deref().get_field(idx) } } /// A generic trait for records, which need to be passed in data /// in order to fully resolve themselves. pub trait SomeRecord<'a> { fn traverse(self, data: FontData<'a>) -> RecordResolver<'a>; } /// A struct created from a record and the data it needs to resolve any /// contained offsets. pub struct RecordResolver<'a> { pub(crate) name: &'static str, pub(crate) get_field: Box) -> Option> + 'a>, pub(crate) data: FontData<'a>, } /// A generic trait for arrays. pub trait SomeArray<'a> { /// The name of this type. For an array of u16s, this is `[u16]`. fn type_name(&self) -> &str; /// The length of the array. fn len(&self) -> usize; /// Returns `true` if this array is empty. fn is_empty(&self) -> bool { self.len() == 0 } /// Return the item at `idx`, or `None` if `idx` is out of bounds. fn get(&self, idx: usize) -> Option>; } impl<'a> dyn SomeArray<'a> + 'a { /// Return an iterator over the contents of this array. pub fn iter(&self) -> impl Iterator> + '_ { ArrayIter { array: self, idx: 0, } } } struct ArrayIter<'a, 'b> { array: &'b dyn SomeArray<'a>, idx: usize, } impl<'a> Iterator for ArrayIter<'a, '_> { type Item = FieldType<'a>; fn next(&mut self) -> Option { let this = self.idx; self.idx += 1; self.array.get(this) } } impl<'a, T: Scalar + Into>> SomeArray<'a> for &'a [BigEndian] where BigEndian: Copy, // i don't know why i need this?? { fn len(&self) -> usize { (*self).len() } fn get(&self, idx: usize) -> Option> { (*self).get(idx).map(|val| val.get().into()) } fn type_name(&self) -> &str { let full_name = std::any::type_name::(); full_name.split("::").last().unwrap_or(full_name) } } impl<'a> SomeArray<'a> for &'a [u8] { fn type_name(&self) -> &str { "u8" } fn len(&self) -> usize { (*self).len() } fn get(&self, idx: usize) -> Option> { (*self).get(idx).copied().map(Into::into) } } impl<'a> SomeArray<'a> for Box + 'a> { fn type_name(&self) -> &str { self.deref().type_name() } fn len(&self) -> usize { self.deref().len() } fn get(&self, idx: usize) -> Option> { self.deref().get(idx) } } pub trait SomeString<'a> { fn iter_chars(&self) -> Box + 'a>; } impl<'a> SomeString<'a> for Box + 'a> { fn iter_chars(&self) -> Box + 'a> { self.deref().iter_chars() } } // only used as Box> struct ArrayOfRecords<'a, T> { pub(crate) type_name: &'static str, pub(crate) data: FontData<'a>, pub(crate) records: &'a [T], } // only used as Box> struct ComputedArrayOfRecords<'a, T: ReadArgs> { pub(crate) type_name: &'static str, pub(crate) data: FontData<'a>, pub(crate) array: ComputedArray<'a, T>, } struct VarLenArrayOfRecords<'a, T> { pub(crate) type_name: &'static str, pub(crate) data: FontData<'a>, pub(crate) array: VarLenArray<'a, T>, } impl<'a, T> SomeArray<'a> for ComputedArrayOfRecords<'a, T> where T: FontReadWithArgs<'a> + ComputeSize + SomeRecord<'a> + 'a, T::Args: Copy + 'static, Self: 'a, { fn len(&self) -> usize { self.array.len() } fn get(&self, idx: usize) -> Option> { self.array .get(idx) .ok() .map(|record| record.traverse(self.data).into()) } fn type_name(&self) -> &str { self.type_name } } impl<'a, T: SomeRecord<'a> + Clone> SomeArray<'a> for ArrayOfRecords<'a, T> { fn type_name(&self) -> &str { self.type_name } fn len(&self) -> usize { self.records.len() } fn get(&self, idx: usize) -> Option> { self.records .get(idx) .map(|record| record.clone().traverse(self.data).into()) } } impl<'a, T> SomeArray<'a> for VarLenArrayOfRecords<'a, T> where T: FontRead<'a> + VarSize + SomeRecord<'a> + 'a, Self: 'a, { fn len(&self) -> usize { self.array.iter().count() } fn get(&self, idx: usize) -> Option> { self.array .get(idx)? .ok() .map(|record| record.traverse(self.data).into()) } fn type_name(&self) -> &str { self.type_name } } impl<'a> Field<'a> { /// Create a new field with the given name and value. pub fn new(name: &'static str, value: impl Into>) -> Self { Field { name, value: value.into(), } } } /// A wrapper type that implements `Debug` for any table. struct DebugPrintTable<'a, 'b>(pub &'b (dyn SomeTable<'a> + 'a)); /// A wrapper type that implements `Debug` for any array. struct DebugPrintArray<'a, 'b>(pub &'b (dyn SomeArray<'a> + 'a)); impl<'a> Debug for FieldType<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::I8(arg0) => arg0.fmt(f), Self::U8(arg0) => arg0.fmt(f), Self::I16(arg0) => arg0.fmt(f), Self::U16(arg0) => arg0.fmt(f), Self::I32(arg0) => arg0.fmt(f), Self::U32(arg0) => arg0.fmt(f), Self::I24(arg0) => arg0.fmt(f), Self::U24(arg0) => arg0.fmt(f), Self::Tag(arg0) => arg0.fmt(f), Self::FWord(arg0) => arg0.to_i16().fmt(f), Self::UfWord(arg0) => arg0.to_u16().fmt(f), Self::MajorMinor(arg0) => write!(f, "{}.{}", arg0.major, arg0.minor), Self::Version16Dot16(arg0) => arg0.fmt(f), Self::F2Dot14(arg0) => arg0.fmt(f), Self::Fixed(arg0) => arg0.fmt(f), Self::LongDateTime(arg0) => arg0.as_secs().fmt(f), Self::GlyphId16(arg0) => { write!(f, "g")?; arg0.to_u16().fmt(f) } Self::GlyphId24(arg0) => { write!(f, "g")?; arg0.to_u32().fmt(f) } Self::NameId(arg0) => arg0.fmt(f), Self::StringOffset(string) => match &string.target { Ok(arg0) => arg0.as_ref().fmt(f), Err(_) => string.target.fmt(f), }, Self::ArrayOffset(array) => match &array.target { Ok(arg0) => arg0.as_ref().fmt(f), Err(_) => array.target.fmt(f), }, Self::BareOffset(arg0) => write!(f, "0x{:04X}", arg0.to_u32()), Self::ResolvedOffset(ResolvedOffset { target: Ok(arg0), .. }) => arg0.fmt(f), Self::ResolvedOffset(arg0) => arg0.target.fmt(f), Self::Record(arg0) => (arg0 as &(dyn SomeTable<'a> + 'a)).fmt(f), Self::Array(arg0) => arg0.fmt(f), Self::Unknown => write!(f, "no repr available"), } } } impl std::fmt::Debug for DebugPrintTable<'_, '_> { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { let mut debug_struct = f.debug_struct(self.0.type_name()); for field in self.0.iter() { debug_struct.field(field.name, &field.value); } debug_struct.finish() } } impl<'a> Debug for dyn SomeTable<'a> + 'a { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { DebugPrintTable(self).fmt(f) } } impl<'a> Debug for dyn SomeString<'a> + 'a { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "\"")?; for c in self.iter_chars() { write!(f, "{c}")? } write!(f, "\"") } } impl std::fmt::Debug for DebugPrintArray<'_, '_> { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { let mut debug_list = f.debug_list(); let mut idx = 0; while let Some(item) = self.0.get(idx) { idx += 1; debug_list.entry(&item); } debug_list.finish() } } impl<'a> Debug for dyn SomeArray<'a> + 'a { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { DebugPrintArray(self).fmt(f) } } impl std::fmt::Display for OffsetType { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{:+}", self.to_u32()) } } // used to give us an auto-impl of Debug impl<'a> SomeTable<'a> for RecordResolver<'a> { fn type_name(&self) -> &str { self.name } fn get_field(&self, idx: usize) -> Option> { (self.get_field)(idx, self.data) } } impl<'a> From for FieldType<'a> { fn from(src: u8) -> FieldType<'a> { FieldType::U8(src) } } impl<'a> From for FieldType<'a> { fn from(src: i8) -> FieldType<'a> { FieldType::I8(src) } } impl<'a> From for FieldType<'a> { fn from(src: u16) -> FieldType<'a> { FieldType::U16(src) } } impl<'a> From for FieldType<'a> { fn from(src: i16) -> FieldType<'a> { FieldType::I16(src) } } impl<'a> From for FieldType<'a> { fn from(src: u32) -> FieldType<'a> { FieldType::U32(src) } } impl<'a> From for FieldType<'a> { fn from(src: i32) -> FieldType<'a> { FieldType::I32(src) } } impl<'a> From for FieldType<'a> { fn from(src: Uint24) -> FieldType<'a> { FieldType::U24(src) } } impl<'a> From for FieldType<'a> { fn from(src: Int24) -> FieldType<'a> { FieldType::I24(src) } } impl<'a> From for FieldType<'a> { fn from(src: Tag) -> FieldType<'a> { FieldType::Tag(src) } } impl<'a> From for FieldType<'a> { fn from(src: FWord) -> FieldType<'a> { FieldType::FWord(src) } } impl<'a> From for FieldType<'a> { fn from(src: UfWord) -> FieldType<'a> { FieldType::UfWord(src) } } impl<'a> From for FieldType<'a> { fn from(src: Fixed) -> FieldType<'a> { FieldType::Fixed(src) } } impl<'a> From for FieldType<'a> { fn from(src: F2Dot14) -> FieldType<'a> { FieldType::F2Dot14(src) } } impl<'a> From for FieldType<'a> { fn from(src: LongDateTime) -> FieldType<'a> { FieldType::LongDateTime(src) } } impl<'a> From for FieldType<'a> { fn from(src: MajorMinor) -> FieldType<'a> { FieldType::MajorMinor(src) } } impl<'a> From for FieldType<'a> { fn from(src: Version16Dot16) -> FieldType<'a> { FieldType::Version16Dot16(src) } } impl<'a> From for FieldType<'a> { fn from(src: GlyphId16) -> FieldType<'a> { FieldType::GlyphId16(src) } } impl<'a> From for FieldType<'a> { fn from(src: GlyphId24) -> FieldType<'a> { FieldType::GlyphId24(src) } } impl<'a> From for FieldType<'a> { fn from(src: NameId) -> FieldType<'a> { FieldType::NameId(src) } } impl<'a> From> for FieldType<'a> { fn from(src: RecordResolver<'a>) -> Self { FieldType::Record(src) } } impl<'a, T: SomeArray<'a> + 'a> From for FieldType<'a> { fn from(src: T) -> Self { FieldType::Array(Box::new(src)) } } impl From for OffsetType { fn from(src: Offset16) -> OffsetType { OffsetType::Offset16(src.to_u32() as u16) } } impl From for OffsetType { fn from(src: Offset24) -> OffsetType { OffsetType::Offset24(Uint24::new(src.to_u32())) } } impl From for OffsetType { fn from(src: Offset32) -> OffsetType { OffsetType::Offset32(src.to_u32()) } } impl<'a> From for FieldType<'a> { fn from(src: Offset16) -> FieldType<'a> { FieldType::BareOffset(src.into()) } } impl<'a> From for FieldType<'a> { fn from(src: Offset24) -> FieldType<'a> { FieldType::BareOffset(src.into()) } } impl<'a> From for FieldType<'a> { fn from(src: Offset32) -> FieldType<'a> { FieldType::BareOffset(src.into()) } } impl + Clone> From> for OffsetType { fn from(src: Nullable) -> Self { src.offset().clone().into() } }