1use font_types::Nullable;
4use types::{FixedSize, Offset16};
5
6use super::ValueFormat;
7use crate::{tables::layout::DeviceOrVariationIndex, ResolveNullableOffset};
8
9use crate::{ComputeSize, FontData, FontReadAt, ReadArgs, ReadError};
10
11impl ValueFormat {
12 pub const ANY_DEVICE_OR_VARIDX: Self = ValueFormat {
14 bits: 0x0010 | 0x0020 | 0x0040 | 0x0080,
15 };
16
17 #[inline]
19 pub fn record_byte_len(self) -> usize {
20 self.bits().count_ones() as usize * u16::RAW_BYTE_LEN
21 }
22}
23
24#[derive(Copy, Clone, Default)]
32pub struct ValueRecord<'a> {
33 data: FontData<'a>,
35 offset: u32,
37 format: ValueFormat,
38}
39
40impl<'a> ValueRecord<'a> {
41 #[inline]
47 pub fn new(data: FontData<'a>, offset: usize, format: ValueFormat) -> Self {
48 Self {
49 data,
50 offset: u32::try_from(offset).unwrap_or(u32::MAX),
53 format,
54 }
55 }
56
57 #[inline]
59 pub fn format(&self) -> ValueFormat {
60 self.format
61 }
62
63 #[inline]
65 pub fn byte_len(&self) -> usize {
66 self.format.record_byte_len()
67 }
68
69 #[inline]
71 pub fn is_empty(&self) -> bool {
72 self.format.is_empty()
73 }
74
75 #[inline]
77 pub fn offset_data(&self) -> FontData<'a> {
78 self.data
79 }
80
81 #[inline]
83 pub fn offset(&self) -> usize {
84 self.offset as usize
85 }
86
87 #[inline]
88 pub fn x_placement(&self) -> Option<i16> {
89 self.read_i16(ValueFormat::X_PLACEMENT)
90 }
91
92 #[inline]
93 pub fn y_placement(&self) -> Option<i16> {
94 self.read_i16(ValueFormat::Y_PLACEMENT)
95 }
96
97 #[inline]
98 pub fn x_advance(&self) -> Option<i16> {
99 self.read_i16(ValueFormat::X_ADVANCE)
100 }
101
102 #[inline]
103 pub fn y_advance(&self) -> Option<i16> {
104 self.read_i16(ValueFormat::Y_ADVANCE)
105 }
106
107 #[inline]
108 pub fn x_placement_device(&self) -> Option<Result<DeviceOrVariationIndex<'a>, ReadError>> {
109 self.read_device(ValueFormat::X_PLACEMENT_DEVICE)
110 }
111
112 #[inline]
113 pub fn y_placement_device(&self) -> Option<Result<DeviceOrVariationIndex<'a>, ReadError>> {
114 self.read_device(ValueFormat::Y_PLACEMENT_DEVICE)
115 }
116
117 #[inline]
118 pub fn x_advance_device(&self) -> Option<Result<DeviceOrVariationIndex<'a>, ReadError>> {
119 self.read_device(ValueFormat::X_ADVANCE_DEVICE)
120 }
121
122 #[inline]
123 pub fn y_advance_device(&self) -> Option<Result<DeviceOrVariationIndex<'a>, ReadError>> {
124 self.read_device(ValueFormat::Y_ADVANCE_DEVICE)
125 }
126
127 #[inline]
133 pub fn device_offset(&self, field: ValueFormat) -> Option<Nullable<Offset16>> {
134 self.data.read_at(self.field_offset(field)?).ok()
135 }
136
137 #[inline]
139 pub fn bytes(&self) -> Option<&'a [u8]> {
140 let start = self.offset as usize;
141 self.data
142 .as_bytes()
143 .get(start..start.checked_add(self.byte_len())?)
144 }
145
146 #[inline]
153 fn field_offset(&self, field: ValueFormat) -> Option<usize> {
154 if !self.format.contains(field) {
155 return None;
156 }
157 let preceding = (self.format.bits() & (field.bits() - 1)).count_ones() as usize;
158 Some(self.offset as usize + preceding * u16::RAW_BYTE_LEN)
159 }
160
161 #[inline]
162 fn read_i16(&self, field: ValueFormat) -> Option<i16> {
163 self.data.read_at(self.field_offset(field)?).ok()
164 }
165
166 #[inline]
167 fn read_device(
168 &self,
169 field: ValueFormat,
170 ) -> Option<Result<DeviceOrVariationIndex<'a>, ReadError>> {
171 let pos = self.field_offset(field)?;
172 match self.data.read_at::<Nullable<Offset16>>(pos) {
173 Ok(offset) => offset.resolve(self.data),
174 Err(err) => Some(Err(err)),
175 }
176 }
177}
178
179impl ReadArgs for ValueRecord<'_> {
180 type Args = ValueFormat;
181}
182
183impl ComputeSize for ValueRecord<'_> {
184 #[inline]
185 fn compute_size(args: ValueFormat) -> Result<usize, ReadError> {
186 Ok(args.record_byte_len())
187 }
188}
189
190impl<'a> FontReadAt<'a> for ValueRecord<'a> {
191 #[inline]
192 fn read_at(data: FontData<'a>, offset: usize, args: ValueFormat) -> Result<Self, ReadError> {
193 Ok(Self::new(data, offset, args))
194 }
195}
196
197impl PartialEq for ValueRecord<'_> {
200 fn eq(&self, other: &Self) -> bool {
201 self.format == other.format && self.bytes() == other.bytes()
202 }
203}
204
205impl Eq for ValueRecord<'_> {}
206
207impl std::fmt::Debug for ValueRecord<'_> {
208 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
209 let mut f = f.debug_struct("ValueRecord");
210 self.x_placement().map(|x| f.field("x_placement", &x));
211 self.y_placement().map(|y| f.field("y_placement", &y));
212 self.x_advance().map(|x| f.field("x_advance", &x));
213 self.y_advance().map(|y| f.field("y_advance", &y));
214 for (name, field) in [
215 ("x_placement_device", ValueFormat::X_PLACEMENT_DEVICE),
216 ("y_placement_device", ValueFormat::Y_PLACEMENT_DEVICE),
217 ("x_advance_device", ValueFormat::X_ADVANCE_DEVICE),
218 ("y_advance_device", ValueFormat::Y_ADVANCE_DEVICE),
219 ] {
220 match self.device_offset(field) {
221 Some(offset) if !offset.is_null() => {
222 f.field(name, &offset);
223 }
224 _ => (),
225 }
226 }
227 f.finish()
228 }
229}
230
231#[cfg(test)]
232mod tests {
233 use super::*;
234
235 #[test]
236 fn sanity_check_format_const() {
237 let format = ValueFormat::X_ADVANCE_DEVICE
238 | ValueFormat::Y_ADVANCE_DEVICE
239 | ValueFormat::Y_PLACEMENT_DEVICE
240 | ValueFormat::X_PLACEMENT_DEVICE;
241 assert_eq!(format, ValueFormat::ANY_DEVICE_OR_VARIDX);
242 assert_eq!(format.record_byte_len(), 4 * 2);
243 }
244
245 fn reference_field_positions(format: ValueFormat) -> Vec<(ValueFormat, usize)> {
249 let mut pos = 0;
250 let mut out = Vec::new();
251 for field in [
252 ValueFormat::X_PLACEMENT,
253 ValueFormat::Y_PLACEMENT,
254 ValueFormat::X_ADVANCE,
255 ValueFormat::Y_ADVANCE,
256 ValueFormat::X_PLACEMENT_DEVICE,
257 ValueFormat::Y_PLACEMENT_DEVICE,
258 ValueFormat::X_ADVANCE_DEVICE,
259 ValueFormat::Y_ADVANCE_DEVICE,
260 ] {
261 if format.contains(field) {
262 out.push((field, pos));
263 pos += 2;
264 }
265 }
266 out
267 }
268
269 #[test]
273 fn field_offsets_match_sequential_layout() {
274 let bytes: Vec<u8> = (0..8u16).flat_map(|i| (0x1100 + i).to_be_bytes()).collect();
276 const PAD: usize = 6;
278 let mut padded = vec![0u8; PAD];
279 padded.extend_from_slice(&bytes);
280 let data = FontData::new(&padded);
281
282 for bits in 0..=u8::MAX {
283 let format = ValueFormat { bits: bits as u16 };
284 let record = ValueRecord::new(data, PAD, format);
285
286 assert_eq!(record.format(), format);
287 assert_eq!(record.byte_len(), format.record_byte_len());
288
289 let expected = reference_field_positions(format);
290 assert_eq!(expected.len() * 2, record.byte_len(), "{format:?}");
291
292 for (field, offset) in expected {
293 let want = 0x1100u16 + (offset / 2) as u16;
295 let got = match field {
296 ValueFormat::X_PLACEMENT => record.x_placement().map(|v| v as u16),
297 ValueFormat::Y_PLACEMENT => record.y_placement().map(|v| v as u16),
298 ValueFormat::X_ADVANCE => record.x_advance().map(|v| v as u16),
299 ValueFormat::Y_ADVANCE => record.y_advance().map(|v| v as u16),
300 other => record
301 .device_offset(other)
302 .map(|off| off.offset().to_u32() as u16),
303 };
304 assert_eq!(got, Some(want), "{format:?} {field:?} at {offset}");
305 }
306
307 for field in [
309 ValueFormat::X_PLACEMENT,
310 ValueFormat::Y_PLACEMENT,
311 ValueFormat::X_ADVANCE,
312 ValueFormat::Y_ADVANCE,
313 ] {
314 if !format.contains(field) {
315 let got = match field {
316 ValueFormat::X_PLACEMENT => record.x_placement(),
317 ValueFormat::Y_PLACEMENT => record.y_placement(),
318 ValueFormat::X_ADVANCE => record.x_advance(),
319 _ => record.y_advance(),
320 };
321 assert_eq!(got, None, "{format:?} {field:?}");
322 }
323 }
324 for field in [
325 ValueFormat::X_PLACEMENT_DEVICE,
326 ValueFormat::Y_PLACEMENT_DEVICE,
327 ValueFormat::X_ADVANCE_DEVICE,
328 ValueFormat::Y_ADVANCE_DEVICE,
329 ] {
330 if !format.contains(field) {
331 assert_eq!(record.device_offset(field), None, "{format:?} {field:?}");
332 }
333 }
334 }
335 }
336
337 #[test]
340 fn lazy_fields_out_of_bounds() {
341 let format = ValueFormat::X_PLACEMENT | ValueFormat::Y_ADVANCE;
342 let bytes = [0u8, 1, 0, 2];
344 let lazy = ValueRecord::new(FontData::new(&bytes), 2, format);
345 assert_eq!(lazy.x_placement(), Some(2));
346 assert_eq!(lazy.y_advance(), None);
347
348 let huge = ValueRecord::new(FontData::new(&bytes), usize::MAX, format);
350 assert_eq!(huge.x_placement(), None);
351 }
352}