1 //! A GPOS ValueRecord 2 3 use font_types::Nullable; 4 use types::{BigEndian, FixedSize, Offset16}; 5 6 use super::ValueFormat; 7 use crate::{tables::layout::DeviceOrVariationIndex, ResolveNullableOffset}; 8 9 #[cfg(feature = "traversal")] 10 use crate::traversal::{Field, FieldType, RecordResolver, SomeRecord}; 11 use crate::{ComputeSize, FontData, FontReadWithArgs, ReadArgs, ReadError}; 12 13 impl ValueFormat { 14 /// A mask with all the device/variation index bits set 15 pub const ANY_DEVICE_OR_VARIDX: Self = ValueFormat { 16 bits: 0x0010 | 0x0020 | 0x0040 | 0x0080, 17 }; 18 19 /// Return the number of bytes required to store a [`ValueRecord`] in this format. 20 #[inline] record_byte_len(self) -> usize21 pub fn record_byte_len(self) -> usize { 22 self.bits().count_ones() as usize * u16::RAW_BYTE_LEN 23 } 24 } 25 26 /// A Positioning ValueRecord. 27 /// 28 /// NOTE: we create these manually, since parsing is weird and depends on the 29 /// associated valueformat. That said, this isn't a great representation? 30 /// we could definitely do something much more in the zero-copy mode.. 31 #[derive(Clone, Default, PartialEq, Eq)] 32 pub struct ValueRecord { 33 pub x_placement: Option<BigEndian<i16>>, 34 pub y_placement: Option<BigEndian<i16>>, 35 pub x_advance: Option<BigEndian<i16>>, 36 pub y_advance: Option<BigEndian<i16>>, 37 pub x_placement_device: BigEndian<Nullable<Offset16>>, 38 pub y_placement_device: BigEndian<Nullable<Offset16>>, 39 pub x_advance_device: BigEndian<Nullable<Offset16>>, 40 pub y_advance_device: BigEndian<Nullable<Offset16>>, 41 } 42 43 impl ValueRecord { read_old(data: &[u8], format: ValueFormat) -> Result<Self, ReadError>44 pub fn read_old(data: &[u8], format: ValueFormat) -> Result<Self, ReadError> { 45 let data = FontData::new(data); 46 Self::read(data, format) 47 } 48 read(data: FontData, format: ValueFormat) -> Result<Self, ReadError>49 pub fn read(data: FontData, format: ValueFormat) -> Result<Self, ReadError> { 50 let mut this = ValueRecord::default(); 51 let mut cursor = data.cursor(); 52 53 if format.contains(ValueFormat::X_PLACEMENT) { 54 this.x_placement = Some(cursor.read_be()?); 55 } 56 if format.contains(ValueFormat::Y_PLACEMENT) { 57 this.y_placement = Some(cursor.read_be()?); 58 } 59 if format.contains(ValueFormat::X_ADVANCE) { 60 this.x_advance = Some(cursor.read_be()?); 61 } 62 if format.contains(ValueFormat::Y_ADVANCE) { 63 this.y_advance = Some(cursor.read_be()?); 64 } 65 if format.contains(ValueFormat::X_PLACEMENT_DEVICE) { 66 this.x_placement_device = cursor.read_be()?; 67 } 68 if format.contains(ValueFormat::Y_PLACEMENT_DEVICE) { 69 this.y_placement_device = cursor.read_be()?; 70 } 71 if format.contains(ValueFormat::X_ADVANCE_DEVICE) { 72 this.x_advance_device = cursor.read_be()?; 73 } 74 if format.contains(ValueFormat::Y_ADVANCE_DEVICE) { 75 this.y_advance_device = cursor.read_be()?; 76 } 77 Ok(this) 78 } 79 x_placement(&self) -> Option<i16>80 pub fn x_placement(&self) -> Option<i16> { 81 self.x_placement.map(|val| val.get()) 82 } 83 y_placement(&self) -> Option<i16>84 pub fn y_placement(&self) -> Option<i16> { 85 self.y_placement.map(|val| val.get()) 86 } 87 x_advance(&self) -> Option<i16>88 pub fn x_advance(&self) -> Option<i16> { 89 self.x_advance.map(|val| val.get()) 90 } 91 y_advance(&self) -> Option<i16>92 pub fn y_advance(&self) -> Option<i16> { 93 self.y_advance.map(|val| val.get()) 94 } 95 x_placement_device<'a>( &self, data: FontData<'a>, ) -> Option<Result<DeviceOrVariationIndex<'a>, ReadError>>96 pub fn x_placement_device<'a>( 97 &self, 98 data: FontData<'a>, 99 ) -> Option<Result<DeviceOrVariationIndex<'a>, ReadError>> { 100 self.x_placement_device.get().resolve(data) 101 } 102 y_placement_device<'a>( &self, data: FontData<'a>, ) -> Option<Result<DeviceOrVariationIndex<'a>, ReadError>>103 pub fn y_placement_device<'a>( 104 &self, 105 data: FontData<'a>, 106 ) -> Option<Result<DeviceOrVariationIndex<'a>, ReadError>> { 107 self.y_placement_device.get().resolve(data) 108 } 109 x_advance_device<'a>( &self, data: FontData<'a>, ) -> Option<Result<DeviceOrVariationIndex<'a>, ReadError>>110 pub fn x_advance_device<'a>( 111 &self, 112 data: FontData<'a>, 113 ) -> Option<Result<DeviceOrVariationIndex<'a>, ReadError>> { 114 self.x_advance_device.get().resolve(data) 115 } 116 y_advance_device<'a>( &self, data: FontData<'a>, ) -> Option<Result<DeviceOrVariationIndex<'a>, ReadError>>117 pub fn y_advance_device<'a>( 118 &self, 119 data: FontData<'a>, 120 ) -> Option<Result<DeviceOrVariationIndex<'a>, ReadError>> { 121 self.y_advance_device.get().resolve(data) 122 } 123 } 124 125 impl ReadArgs for ValueRecord { 126 type Args = ValueFormat; 127 } 128 129 impl<'a> FontReadWithArgs<'a> for ValueRecord { read_with_args(data: FontData<'a>, args: &Self::Args) -> Result<Self, ReadError>130 fn read_with_args(data: FontData<'a>, args: &Self::Args) -> Result<Self, ReadError> { 131 ValueRecord::read(data, *args) 132 } 133 } 134 135 impl std::fmt::Debug for ValueRecord { fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result136 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 137 let mut f = f.debug_struct("ValueRecord"); 138 self.x_placement.map(|x| f.field("x_placement", &x)); 139 self.y_placement.map(|y| f.field("y_placement", &y)); 140 self.x_advance.map(|x| f.field("x_advance", &x)); 141 self.y_advance.map(|y| f.field("y_advance", &y)); 142 if !self.x_placement_device.get().is_null() { 143 f.field("x_placement_device", &self.x_placement_device.get()); 144 } 145 if !self.y_placement_device.get().is_null() { 146 f.field("y_placement_device", &self.y_placement_device.get()); 147 } 148 if !self.x_advance_device.get().is_null() { 149 f.field("x_advance_device", &self.x_advance_device.get()); 150 } 151 if !self.y_advance_device.get().is_null() { 152 f.field("y_advance_device", &self.y_advance_device.get()); 153 } 154 f.finish() 155 } 156 } 157 158 impl ComputeSize for ValueRecord { 159 #[inline] compute_size(args: &ValueFormat) -> usize160 fn compute_size(args: &ValueFormat) -> usize { 161 args.record_byte_len() 162 } 163 } 164 165 #[cfg(feature = "traversal")] 166 impl<'a> ValueRecord { traversal_type(&self, data: FontData<'a>) -> FieldType<'a>167 pub(crate) fn traversal_type(&self, data: FontData<'a>) -> FieldType<'a> { 168 FieldType::Record(self.clone().traverse(data)) 169 } 170 get_field(&self, idx: usize, data: FontData<'a>) -> Option<Field<'a>>171 pub(crate) fn get_field(&self, idx: usize, data: FontData<'a>) -> Option<Field<'a>> { 172 let fields = [ 173 self.x_placement.is_some().then_some("x_placement"), 174 self.y_placement.is_some().then_some("y_placement"), 175 self.x_advance.is_some().then_some("x_advance"), 176 self.y_advance.is_some().then_some("y_advance"), 177 (!self.x_placement_device.get().is_null()).then_some("x_placement_device"), 178 (!self.y_placement_device.get().is_null()).then_some("y_placement_device"), 179 (!self.x_advance_device.get().is_null()).then_some("x_advance_device"), 180 (!self.y_advance_device.get().is_null()).then_some("y_advance_device"), 181 ]; 182 183 let name = fields.iter().filter_map(|x| *x).nth(idx)?; 184 let typ: FieldType = match name { 185 "x_placement" => self.x_placement().unwrap().into(), 186 "y_placement" => self.y_placement().unwrap().into(), 187 "x_advance" => self.x_advance().unwrap().into(), 188 "y_advance" => self.y_advance().unwrap().into(), 189 "x_placement_device" => { 190 FieldType::offset(self.x_placement_device.get(), self.x_placement_device(data)) 191 } 192 "y_placement_device" => { 193 FieldType::offset(self.y_placement_device.get(), self.y_placement_device(data)) 194 } 195 "x_advance_device" => { 196 FieldType::offset(self.x_advance_device.get(), self.x_advance_device(data)) 197 } 198 "y_advance_device" => { 199 FieldType::offset(self.y_advance_device.get(), self.y_advance_device(data)) 200 } 201 _ => panic!("hmm"), 202 }; 203 204 Some(Field::new(name, typ)) 205 } 206 } 207 208 #[cfg(feature = "traversal")] 209 impl<'a> SomeRecord<'a> for ValueRecord { traverse(self, data: FontData<'a>) -> RecordResolver<'a>210 fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> { 211 RecordResolver { 212 name: "ValueRecord", 213 data, 214 get_field: Box::new(move |idx, data| self.get_field(idx, data)), 215 } 216 } 217 } 218 219 #[cfg(test)] 220 mod tests { 221 use super::*; 222 223 #[test] sanity_check_format_const()224 fn sanity_check_format_const() { 225 let format = ValueFormat::X_ADVANCE_DEVICE 226 | ValueFormat::Y_ADVANCE_DEVICE 227 | ValueFormat::Y_PLACEMENT_DEVICE 228 | ValueFormat::X_PLACEMENT_DEVICE; 229 assert_eq!(format, ValueFormat::ANY_DEVICE_OR_VARIDX); 230 assert_eq!(format.record_byte_len(), 4 * 2); 231 } 232 } 233