1 //! Traits for interpreting font data 2 3 use types::{BigEndian, FixedSize, Scalar, Tag}; 4 5 use crate::font_data::FontData; 6 7 /// A type that can be read from raw table data. 8 /// 9 /// This trait is implemented for all font tables that are self-describing: that 10 /// is, tables that do not require any external state in order to interpret their 11 /// underlying bytes. (Tables that require external state implement 12 /// [`FontReadWithArgs`] instead) 13 pub trait FontRead<'a>: Sized { 14 /// Read an instace of `Self` from the provided data, performing validation. 15 /// 16 /// In the case of a table, this method is responsible for ensuring the input 17 /// data is consistent: this means ensuring that any versioned fields are 18 /// present as required by the version, and that any array lengths are not 19 /// out-of-bounds. read(data: FontData<'a>) -> Result<Self, ReadError>20 fn read(data: FontData<'a>) -> Result<Self, ReadError>; 21 } 22 23 //NOTE: this is separate so that it can be a super trait of FontReadWithArgs and 24 //ComputeSize, without them needing to know about each other? I'm not sure this 25 //is necessary, but I don't know the full heirarchy of traits I'm going to need 26 //yet, so this seems... okay? 27 28 /// A trait for a type that needs additional arguments to be read. 29 pub trait ReadArgs { 30 type Args: Copy; 31 } 32 33 /// A trait for types that require external data in order to be constructed. 34 /// 35 /// You should not need to use this directly; it is intended to be used from 36 /// generated code. Any type that requires external arguments also has a custom 37 /// `read` constructor where you can pass those arguments like normal. 38 pub trait FontReadWithArgs<'a>: Sized + ReadArgs { 39 /// read an item, using the provided args. 40 /// 41 /// If successful, returns a new item of this type, and the number of bytes 42 /// used to construct it. 43 /// 44 /// If a type requires multiple arguments, they will be passed as a tuple. read_with_args(data: FontData<'a>, args: &Self::Args) -> Result<Self, ReadError>45 fn read_with_args(data: FontData<'a>, args: &Self::Args) -> Result<Self, ReadError>; 46 } 47 48 // a blanket impl of ReadArgs/FontReadWithArgs for general FontRead types. 49 // 50 // This is used by ArrayOfOffsets/ArrayOfNullableOffsets to provide a common 51 // interface for regardless of whether a type has args. 52 impl<'a, T: FontRead<'a>> ReadArgs for T { 53 type Args = (); 54 } 55 56 impl<'a, T: FontRead<'a>> FontReadWithArgs<'a> for T { read_with_args(data: FontData<'a>, _: &Self::Args) -> Result<Self, ReadError>57 fn read_with_args(data: FontData<'a>, _: &Self::Args) -> Result<Self, ReadError> { 58 Self::read(data) 59 } 60 } 61 62 /// A trait for tables that have multiple possible formats. 63 pub trait Format<T> { 64 /// The format value for this table. 65 const FORMAT: T; 66 } 67 68 /// A type that can compute its size at runtime, based on some input. 69 /// 70 /// For types with a constant size, see [`FixedSize`] and 71 /// for types which store their size inline, see [`VarSize`]. 72 pub trait ComputeSize: ReadArgs { 73 /// Compute the number of bytes required to represent this type. compute_size(args: &Self::Args) -> usize74 fn compute_size(args: &Self::Args) -> usize; 75 } 76 77 /// A trait for types that have variable length. 78 /// 79 /// As a rule, these types have an initial length field. 80 /// 81 /// For types with a constant size, see [`FixedSize`] and 82 /// for types which can pre-compute their size, see [`ComputeSize`]. 83 pub trait VarSize { 84 /// The type of the first (length) field of the item. 85 /// 86 /// When reading this type, we will read this value first, and use it to 87 /// determine the total length. 88 type Size: Scalar + Into<u32>; 89 90 #[doc(hidden)] read_len_at(data: FontData, pos: usize) -> Option<usize>91 fn read_len_at(data: FontData, pos: usize) -> Option<usize> { 92 let asu32 = data.read_at::<Self::Size>(pos).ok()?.into(); 93 Some(asu32 as usize + Self::Size::RAW_BYTE_LEN) 94 } 95 } 96 97 /// A marker trait for types that can read from a big-endian buffer without copying. 98 /// 99 /// This is used as a trait bound on certain methods on [`FontData`] (such as 100 /// [`FontData::read_ref_at`] and [`FontData::read_array`]) in order to ensure 101 /// that those methods are only used with types that uphold certain safety 102 /// guarantees. 103 /// 104 /// WARNING: Do not implement this trait manually. Implementations are created 105 /// where appropriate during code generation, and there should be no conditions 106 /// under which this trait could be implemented, but cannot be implemented by 107 /// codegen. 108 /// 109 /// # Safety 110 /// 111 /// If a type `T` implements `FromBytes` then unsafe code may assume that it is 112 /// safe to interpret any sequence of bytes with length equal to 113 /// `std::mem::size_of::<T>()` as `T`. 114 /// 115 /// we additionally ensure the following conditions: 116 /// 117 /// - the type must have no internal padding 118 /// - `std::mem::align_of::<T>() == 1` 119 /// - for structs, the type is `repr(packed)` and `repr(C)`, and all fields are 120 /// also `FromBytes` 121 /// 122 /// In practice, this trait is only implemented for `u8`, `BigEndian<T>`, 123 /// and for structs where all fields are those base types. 124 pub unsafe trait FromBytes: FixedSize + sealed::Sealed { 125 /// You should not be implementing this trait! 126 #[doc(hidden)] this_trait_should_only_be_implemented_in_generated_code()127 fn this_trait_should_only_be_implemented_in_generated_code(); 128 } 129 130 // a sealed trait. see <https://rust-lang.github.io/api-guidelines/future-proofing.html> 131 pub(crate) mod sealed { 132 pub trait Sealed {} 133 } 134 135 impl sealed::Sealed for u8 {} 136 // SAFETY: any byte can be interpreted as any other byte 137 unsafe impl FromBytes for u8 { this_trait_should_only_be_implemented_in_generated_code()138 fn this_trait_should_only_be_implemented_in_generated_code() {} 139 } 140 141 impl<T: Scalar> sealed::Sealed for BigEndian<T> {} 142 // SAFETY: BigEndian<T> is always wrapper around a transparent fixed-size byte array 143 unsafe impl<T: Scalar> FromBytes for BigEndian<T> { this_trait_should_only_be_implemented_in_generated_code()144 fn this_trait_should_only_be_implemented_in_generated_code() {} 145 } 146 147 /// An error that occurs when reading font data 148 #[derive(Debug, Clone)] 149 pub enum ReadError { 150 OutOfBounds, 151 // i64 is flexible enough to store any value we might encounter 152 InvalidFormat(i64), 153 InvalidSfnt(u32), 154 InvalidTtc(Tag), 155 InvalidCollectionIndex(u32), 156 InvalidArrayLen, 157 ValidationError, 158 NullOffset, 159 TableIsMissing(Tag), 160 MetricIsMissing(Tag), 161 MalformedData(&'static str), 162 } 163 164 impl std::fmt::Display for ReadError { fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result165 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 166 match self { 167 ReadError::OutOfBounds => write!(f, "An offset was out of bounds"), 168 ReadError::InvalidFormat(x) => write!(f, "Invalid format '{x}'"), 169 ReadError::InvalidSfnt(ver) => write!(f, "Invalid sfnt version 0x{ver:08X}"), 170 ReadError::InvalidTtc(tag) => write!(f, "Invalid ttc tag {tag}"), 171 ReadError::InvalidCollectionIndex(ix) => { 172 write!(f, "Invalid index {ix} for font collection") 173 } 174 ReadError::InvalidArrayLen => { 175 write!(f, "Specified array length not a multiple of item size") 176 } 177 ReadError::ValidationError => write!(f, "A validation error occured"), 178 ReadError::NullOffset => write!(f, "An offset was unexpectedly null"), 179 ReadError::TableIsMissing(tag) => write!(f, "the {tag} table is missing"), 180 ReadError::MetricIsMissing(tag) => write!(f, "the {tag} metric is missing"), 181 ReadError::MalformedData(msg) => write!(f, "Malformed data: '{msg}'"), 182 } 183 } 184 } 185 186 #[cfg(feature = "std")] 187 impl std::error::Error for ReadError {} 188