read_fonts/tables/
value_record.rs

1//! A GPOS ValueRecord
2
3use font_types::Nullable;
4use types::{BigEndian, FixedSize, Offset16};
5
6use super::ValueFormat;
7use crate::{tables::layout::DeviceOrVariationIndex, ResolveNullableOffset};
8
9#[cfg(feature = "experimental_traverse")]
10use crate::traversal::{Field, FieldType, RecordResolver, SomeRecord};
11use crate::{ComputeSize, FontData, FontReadWithArgs, ReadArgs, ReadError};
12
13impl 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]
21    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, Eq)]
32pub 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    #[doc(hidden)]
42    // exposed so that we can preserve format when we round-trip a value record
43    pub format: ValueFormat,
44}
45
46// we ignore the format for the purpose of equality testing, it's redundant
47impl PartialEq for ValueRecord {
48    fn eq(&self, other: &Self) -> bool {
49        self.x_placement == other.x_placement
50            && self.y_placement == other.y_placement
51            && self.x_advance == other.x_advance
52            && self.y_advance == other.y_advance
53            && self.x_placement_device == other.x_placement_device
54            && self.y_placement_device == other.y_placement_device
55            && self.x_advance_device == other.x_advance_device
56            && self.y_advance_device == other.y_advance_device
57    }
58}
59
60impl ValueRecord {
61    pub fn read(data: FontData, format: ValueFormat) -> Result<Self, ReadError> {
62        let mut this = ValueRecord {
63            format,
64            ..Default::default()
65        };
66        let mut cursor = data.cursor();
67
68        if format.contains(ValueFormat::X_PLACEMENT) {
69            this.x_placement = Some(cursor.read_be()?);
70        }
71        if format.contains(ValueFormat::Y_PLACEMENT) {
72            this.y_placement = Some(cursor.read_be()?);
73        }
74        if format.contains(ValueFormat::X_ADVANCE) {
75            this.x_advance = Some(cursor.read_be()?);
76        }
77        if format.contains(ValueFormat::Y_ADVANCE) {
78            this.y_advance = Some(cursor.read_be()?);
79        }
80        if format.contains(ValueFormat::X_PLACEMENT_DEVICE) {
81            this.x_placement_device = cursor.read_be()?;
82        }
83        if format.contains(ValueFormat::Y_PLACEMENT_DEVICE) {
84            this.y_placement_device = cursor.read_be()?;
85        }
86        if format.contains(ValueFormat::X_ADVANCE_DEVICE) {
87            this.x_advance_device = cursor.read_be()?;
88        }
89        if format.contains(ValueFormat::Y_ADVANCE_DEVICE) {
90            this.y_advance_device = cursor.read_be()?;
91        }
92        Ok(this)
93    }
94
95    pub fn x_placement(&self) -> Option<i16> {
96        self.x_placement.map(|val| val.get())
97    }
98
99    pub fn y_placement(&self) -> Option<i16> {
100        self.y_placement.map(|val| val.get())
101    }
102
103    pub fn x_advance(&self) -> Option<i16> {
104        self.x_advance.map(|val| val.get())
105    }
106
107    pub fn y_advance(&self) -> Option<i16> {
108        self.y_advance.map(|val| val.get())
109    }
110
111    pub fn x_placement_device<'a>(
112        &self,
113        data: FontData<'a>,
114    ) -> Option<Result<DeviceOrVariationIndex<'a>, ReadError>> {
115        self.x_placement_device.get().resolve(data)
116    }
117
118    pub fn y_placement_device<'a>(
119        &self,
120        data: FontData<'a>,
121    ) -> Option<Result<DeviceOrVariationIndex<'a>, ReadError>> {
122        self.y_placement_device.get().resolve(data)
123    }
124
125    pub fn x_advance_device<'a>(
126        &self,
127        data: FontData<'a>,
128    ) -> Option<Result<DeviceOrVariationIndex<'a>, ReadError>> {
129        self.x_advance_device.get().resolve(data)
130    }
131
132    pub fn y_advance_device<'a>(
133        &self,
134        data: FontData<'a>,
135    ) -> Option<Result<DeviceOrVariationIndex<'a>, ReadError>> {
136        self.y_advance_device.get().resolve(data)
137    }
138}
139
140impl ReadArgs for ValueRecord {
141    type Args = ValueFormat;
142}
143
144impl<'a> FontReadWithArgs<'a> for ValueRecord {
145    fn read_with_args(data: FontData<'a>, args: &Self::Args) -> Result<Self, ReadError> {
146        ValueRecord::read(data, *args)
147    }
148}
149
150impl std::fmt::Debug for ValueRecord {
151    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
152        let mut f = f.debug_struct("ValueRecord");
153        self.x_placement.map(|x| f.field("x_placement", &x));
154        self.y_placement.map(|y| f.field("y_placement", &y));
155        self.x_advance.map(|x| f.field("x_advance", &x));
156        self.y_advance.map(|y| f.field("y_advance", &y));
157        if !self.x_placement_device.get().is_null() {
158            f.field("x_placement_device", &self.x_placement_device.get());
159        }
160        if !self.y_placement_device.get().is_null() {
161            f.field("y_placement_device", &self.y_placement_device.get());
162        }
163        if !self.x_advance_device.get().is_null() {
164            f.field("x_advance_device", &self.x_advance_device.get());
165        }
166        if !self.y_advance_device.get().is_null() {
167            f.field("y_advance_device", &self.y_advance_device.get());
168        }
169        f.finish()
170    }
171}
172
173impl ComputeSize for ValueRecord {
174    #[inline]
175    fn compute_size(args: &ValueFormat) -> Result<usize, ReadError> {
176        Ok(args.record_byte_len())
177    }
178}
179
180#[cfg(feature = "experimental_traverse")]
181impl<'a> ValueRecord {
182    pub(crate) fn traversal_type(&self, data: FontData<'a>) -> FieldType<'a> {
183        FieldType::Record(self.clone().traverse(data))
184    }
185
186    pub(crate) fn get_field(&self, idx: usize, data: FontData<'a>) -> Option<Field<'a>> {
187        let fields = [
188            self.x_placement.is_some().then_some("x_placement"),
189            self.y_placement.is_some().then_some("y_placement"),
190            self.x_advance.is_some().then_some("x_advance"),
191            self.y_advance.is_some().then_some("y_advance"),
192            (!self.x_placement_device.get().is_null()).then_some("x_placement_device"),
193            (!self.y_placement_device.get().is_null()).then_some("y_placement_device"),
194            (!self.x_advance_device.get().is_null()).then_some("x_advance_device"),
195            (!self.y_advance_device.get().is_null()).then_some("y_advance_device"),
196        ];
197
198        let name = fields.iter().filter_map(|x| *x).nth(idx)?;
199        let typ: FieldType = match name {
200            "x_placement" => self.x_placement().unwrap().into(),
201            "y_placement" => self.y_placement().unwrap().into(),
202            "x_advance" => self.x_advance().unwrap().into(),
203            "y_advance" => self.y_advance().unwrap().into(),
204            "x_placement_device" => {
205                FieldType::offset(self.x_placement_device.get(), self.x_placement_device(data))
206            }
207            "y_placement_device" => {
208                FieldType::offset(self.y_placement_device.get(), self.y_placement_device(data))
209            }
210            "x_advance_device" => {
211                FieldType::offset(self.x_advance_device.get(), self.x_advance_device(data))
212            }
213            "y_advance_device" => {
214                FieldType::offset(self.y_advance_device.get(), self.y_advance_device(data))
215            }
216            _ => panic!("hmm"),
217        };
218
219        Some(Field::new(name, typ))
220    }
221}
222
223#[cfg(feature = "experimental_traverse")]
224impl<'a> SomeRecord<'a> for ValueRecord {
225    fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
226        RecordResolver {
227            name: "ValueRecord",
228            data,
229            get_field: Box::new(move |idx, data| self.get_field(idx, data)),
230        }
231    }
232}
233
234#[cfg(test)]
235mod tests {
236    use super::*;
237
238    #[test]
239    fn sanity_check_format_const() {
240        let format = ValueFormat::X_ADVANCE_DEVICE
241            | ValueFormat::Y_ADVANCE_DEVICE
242            | ValueFormat::Y_PLACEMENT_DEVICE
243            | ValueFormat::X_PLACEMENT_DEVICE;
244        assert_eq!(format, ValueFormat::ANY_DEVICE_OR_VARIDX);
245        assert_eq!(format.record_byte_len(), 4 * 2);
246    }
247}