1use font_types::Nullable;
4use types::{BigEndian, F2Dot14, FixedSize, Offset16};
5
6use super::ValueFormat;
7use crate::{
8 tables::{
9 layout::DeviceOrVariationIndex,
10 variations::{DeltaSetIndex, ItemVariationStore},
11 },
12 ResolveNullableOffset,
13};
14
15#[cfg(feature = "experimental_traverse")]
16use crate::traversal::{Field, FieldType, RecordResolver, SomeRecord};
17use crate::{ComputeSize, FontData, FontReadWithArgs, ReadArgs, ReadError};
18
19impl ValueFormat {
20 pub const ANY_DEVICE_OR_VARIDX: Self = ValueFormat {
22 bits: 0x0010 | 0x0020 | 0x0040 | 0x0080,
23 };
24
25 #[inline]
27 pub fn record_byte_len(self) -> usize {
28 self.bits().count_ones() as usize * u16::RAW_BYTE_LEN
29 }
30}
31
32#[derive(Clone, Default)]
37pub struct ValueContext<'a> {
38 coords: &'a [F2Dot14],
39 var_store: Option<ItemVariationStore<'a>>,
40}
41
42impl<'a> ValueContext<'a> {
43 pub fn new() -> Self {
45 Self::default()
46 }
47
48 pub fn with_coords(mut self, coords: &'a [F2Dot14]) -> Self {
50 self.coords = coords;
51 self
52 }
53
54 pub fn with_var_store(mut self, var_store: Option<ItemVariationStore<'a>>) -> Self {
58 self.var_store = var_store;
59 self
60 }
61
62 fn var_store_and_coords(&self) -> Option<(&ItemVariationStore<'a>, &'a [F2Dot14])> {
63 Some((self.var_store.as_ref()?, self.coords))
64 }
65}
66
67#[derive(Copy, Clone, PartialEq, Eq, Default, Debug)]
69pub struct Value {
70 pub format: ValueFormat,
71 pub x_placement: i16,
72 pub y_placement: i16,
73 pub x_advance: i16,
74 pub y_advance: i16,
75 pub x_placement_delta: i32,
76 pub y_placement_delta: i32,
77 pub x_advance_delta: i32,
78 pub y_advance_delta: i32,
79}
80
81impl Value {
82 #[inline]
87 pub fn read(
88 offset_data: FontData,
89 offset: usize,
90 format: ValueFormat,
91 context: &ValueContext,
92 ) -> Result<Self, ReadError> {
93 let mut value = Self {
94 format,
95 ..Default::default()
96 };
97 let mut cursor = offset_data.cursor();
98 cursor.advance_by(offset);
99 if format.contains(ValueFormat::X_PLACEMENT) {
100 value.x_placement = cursor.read()?;
101 }
102 if format.contains(ValueFormat::Y_PLACEMENT) {
103 value.y_placement = cursor.read()?;
104 }
105 if format.contains(ValueFormat::X_ADVANCE) {
106 value.x_advance = cursor.read()?;
107 }
108 if format.contains(ValueFormat::Y_ADVANCE) {
109 value.y_advance = cursor.read()?;
110 }
111 if !format.contains(ValueFormat::ANY_DEVICE_OR_VARIDX) {
112 return Ok(value);
113 }
114 if let Some((ivs, coords)) = context.var_store_and_coords() {
115 let compute_delta = |offset: u16| {
116 let rec_offset = offset_data.read_at::<u16>(offset as usize).ok()? as usize;
117 let format = offset_data.read_at::<u16>(rec_offset + 4).ok()?;
118 const VARIATION_INDEX_FORMAT: u16 = 0x8000;
121 if format != VARIATION_INDEX_FORMAT {
122 return Some(0);
123 }
124 let outer = offset_data.read_at::<u16>(rec_offset).ok()?;
125 let inner = offset_data.read_at::<u16>(rec_offset + 2).ok()?;
126 ivs.compute_delta(DeltaSetIndex { outer, inner }, coords)
127 .ok()
128 };
129 if format.contains(ValueFormat::X_PLACEMENT_DEVICE) {
130 value.x_placement_delta = compute_delta(cursor.read()?).unwrap_or_default();
131 }
132 if format.contains(ValueFormat::Y_PLACEMENT_DEVICE) {
133 value.y_placement_delta = compute_delta(cursor.read()?).unwrap_or_default();
134 }
135 if format.contains(ValueFormat::X_ADVANCE_DEVICE) {
136 value.x_advance_delta = compute_delta(cursor.read()?).unwrap_or_default();
137 }
138 if format.contains(ValueFormat::Y_ADVANCE_DEVICE) {
139 value.y_advance_delta = compute_delta(cursor.read()?).unwrap_or_default();
140 }
141 }
142 Ok(value)
143 }
144}
145
146#[derive(Clone, Default, Eq)]
152pub struct ValueRecord {
153 pub x_placement: Option<BigEndian<i16>>,
154 pub y_placement: Option<BigEndian<i16>>,
155 pub x_advance: Option<BigEndian<i16>>,
156 pub y_advance: Option<BigEndian<i16>>,
157 pub x_placement_device: BigEndian<Nullable<Offset16>>,
158 pub y_placement_device: BigEndian<Nullable<Offset16>>,
159 pub x_advance_device: BigEndian<Nullable<Offset16>>,
160 pub y_advance_device: BigEndian<Nullable<Offset16>>,
161 #[doc(hidden)]
162 pub format: ValueFormat,
164}
165
166impl PartialEq for ValueRecord {
168 fn eq(&self, other: &Self) -> bool {
169 self.x_placement == other.x_placement
170 && self.y_placement == other.y_placement
171 && self.x_advance == other.x_advance
172 && self.y_advance == other.y_advance
173 && self.x_placement_device == other.x_placement_device
174 && self.y_placement_device == other.y_placement_device
175 && self.x_advance_device == other.x_advance_device
176 && self.y_advance_device == other.y_advance_device
177 }
178}
179
180impl ValueRecord {
181 pub fn read(data: FontData, format: ValueFormat) -> Result<Self, ReadError> {
182 let mut this = ValueRecord {
183 format,
184 ..Default::default()
185 };
186 let mut cursor = data.cursor();
187
188 if format.contains(ValueFormat::X_PLACEMENT) {
189 this.x_placement = Some(cursor.read_be()?);
190 }
191 if format.contains(ValueFormat::Y_PLACEMENT) {
192 this.y_placement = Some(cursor.read_be()?);
193 }
194 if format.contains(ValueFormat::X_ADVANCE) {
195 this.x_advance = Some(cursor.read_be()?);
196 }
197 if format.contains(ValueFormat::Y_ADVANCE) {
198 this.y_advance = Some(cursor.read_be()?);
199 }
200 if format.contains(ValueFormat::X_PLACEMENT_DEVICE) {
201 this.x_placement_device = cursor.read_be()?;
202 }
203 if format.contains(ValueFormat::Y_PLACEMENT_DEVICE) {
204 this.y_placement_device = cursor.read_be()?;
205 }
206 if format.contains(ValueFormat::X_ADVANCE_DEVICE) {
207 this.x_advance_device = cursor.read_be()?;
208 }
209 if format.contains(ValueFormat::Y_ADVANCE_DEVICE) {
210 this.y_advance_device = cursor.read_be()?;
211 }
212 Ok(this)
213 }
214
215 pub fn x_placement(&self) -> Option<i16> {
216 self.x_placement.map(|val| val.get())
217 }
218
219 pub fn y_placement(&self) -> Option<i16> {
220 self.y_placement.map(|val| val.get())
221 }
222
223 pub fn x_advance(&self) -> Option<i16> {
224 self.x_advance.map(|val| val.get())
225 }
226
227 pub fn y_advance(&self) -> Option<i16> {
228 self.y_advance.map(|val| val.get())
229 }
230
231 pub fn x_placement_device<'a>(
232 &self,
233 data: FontData<'a>,
234 ) -> Option<Result<DeviceOrVariationIndex<'a>, ReadError>> {
235 self.x_placement_device.get().resolve(data)
236 }
237
238 pub fn y_placement_device<'a>(
239 &self,
240 data: FontData<'a>,
241 ) -> Option<Result<DeviceOrVariationIndex<'a>, ReadError>> {
242 self.y_placement_device.get().resolve(data)
243 }
244
245 pub fn x_advance_device<'a>(
246 &self,
247 data: FontData<'a>,
248 ) -> Option<Result<DeviceOrVariationIndex<'a>, ReadError>> {
249 self.x_advance_device.get().resolve(data)
250 }
251
252 pub fn y_advance_device<'a>(
253 &self,
254 data: FontData<'a>,
255 ) -> Option<Result<DeviceOrVariationIndex<'a>, ReadError>> {
256 self.y_advance_device.get().resolve(data)
257 }
258
259 pub fn value(&self, offset_data: FontData, context: &ValueContext) -> Result<Value, ReadError> {
265 let mut value = Value {
266 format: self.format,
267 x_placement: self.x_placement.unwrap_or_default().get(),
268 y_placement: self.y_placement.unwrap_or_default().get(),
269 x_advance: self.x_advance.unwrap_or_default().get(),
270 y_advance: self.y_advance.unwrap_or_default().get(),
271 ..Default::default()
272 };
273 if let Some((ivs, coords)) = context.var_store_and_coords() {
274 let compute_delta = |value: DeviceOrVariationIndex| match value {
275 DeviceOrVariationIndex::VariationIndex(var_idx) => {
276 let outer = var_idx.delta_set_outer_index();
277 let inner = var_idx.delta_set_inner_index();
278 ivs.compute_delta(DeltaSetIndex { outer, inner }, coords)
279 .ok()
280 }
281 _ => None,
282 };
283 if let Some(device) = self.x_placement_device(offset_data) {
284 value.x_placement_delta = compute_delta(device?).unwrap_or_default();
285 }
286 if let Some(device) = self.y_placement_device(offset_data) {
287 value.y_placement_delta = compute_delta(device?).unwrap_or_default();
288 }
289 if let Some(device) = self.x_advance_device(offset_data) {
290 value.x_advance_delta = compute_delta(device?).unwrap_or_default();
291 }
292 if let Some(device) = self.y_advance_device(offset_data) {
293 value.y_advance_delta = compute_delta(device?).unwrap_or_default();
294 }
295 }
296 Ok(value)
297 }
298}
299
300impl ReadArgs for ValueRecord {
301 type Args = ValueFormat;
302}
303
304impl<'a> FontReadWithArgs<'a> for ValueRecord {
305 fn read_with_args(data: FontData<'a>, args: &Self::Args) -> Result<Self, ReadError> {
306 ValueRecord::read(data, *args)
307 }
308}
309
310impl std::fmt::Debug for ValueRecord {
311 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
312 let mut f = f.debug_struct("ValueRecord");
313 self.x_placement.map(|x| f.field("x_placement", &x));
314 self.y_placement.map(|y| f.field("y_placement", &y));
315 self.x_advance.map(|x| f.field("x_advance", &x));
316 self.y_advance.map(|y| f.field("y_advance", &y));
317 if !self.x_placement_device.get().is_null() {
318 f.field("x_placement_device", &self.x_placement_device.get());
319 }
320 if !self.y_placement_device.get().is_null() {
321 f.field("y_placement_device", &self.y_placement_device.get());
322 }
323 if !self.x_advance_device.get().is_null() {
324 f.field("x_advance_device", &self.x_advance_device.get());
325 }
326 if !self.y_advance_device.get().is_null() {
327 f.field("y_advance_device", &self.y_advance_device.get());
328 }
329 f.finish()
330 }
331}
332
333impl ComputeSize for ValueRecord {
334 #[inline]
335 fn compute_size(args: &ValueFormat) -> Result<usize, ReadError> {
336 Ok(args.record_byte_len())
337 }
338}
339
340#[cfg(feature = "experimental_traverse")]
341impl<'a> ValueRecord {
342 pub(crate) fn traversal_type(&self, data: FontData<'a>) -> FieldType<'a> {
343 FieldType::Record(self.clone().traverse(data))
344 }
345
346 pub(crate) fn get_field(&self, idx: usize, data: FontData<'a>) -> Option<Field<'a>> {
347 let fields = [
348 self.x_placement.is_some().then_some("x_placement"),
349 self.y_placement.is_some().then_some("y_placement"),
350 self.x_advance.is_some().then_some("x_advance"),
351 self.y_advance.is_some().then_some("y_advance"),
352 (!self.x_placement_device.get().is_null()).then_some("x_placement_device"),
353 (!self.y_placement_device.get().is_null()).then_some("y_placement_device"),
354 (!self.x_advance_device.get().is_null()).then_some("x_advance_device"),
355 (!self.y_advance_device.get().is_null()).then_some("y_advance_device"),
356 ];
357
358 let name = fields.iter().filter_map(|x| *x).nth(idx)?;
359 let typ: FieldType = match name {
360 "x_placement" => self.x_placement().unwrap().into(),
361 "y_placement" => self.y_placement().unwrap().into(),
362 "x_advance" => self.x_advance().unwrap().into(),
363 "y_advance" => self.y_advance().unwrap().into(),
364 "x_placement_device" => {
365 FieldType::offset(self.x_placement_device.get(), self.x_placement_device(data))
366 }
367 "y_placement_device" => {
368 FieldType::offset(self.y_placement_device.get(), self.y_placement_device(data))
369 }
370 "x_advance_device" => {
371 FieldType::offset(self.x_advance_device.get(), self.x_advance_device(data))
372 }
373 "y_advance_device" => {
374 FieldType::offset(self.y_advance_device.get(), self.y_advance_device(data))
375 }
376 _ => panic!("hmm"),
377 };
378
379 Some(Field::new(name, typ))
380 }
381}
382
383#[cfg(feature = "experimental_traverse")]
384impl<'a> SomeRecord<'a> for ValueRecord {
385 fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
386 RecordResolver {
387 name: "ValueRecord",
388 data,
389 get_field: Box::new(move |idx, data| self.get_field(idx, data)),
390 }
391 }
392}
393
394#[cfg(test)]
395mod tests {
396 use super::*;
397
398 #[test]
399 fn sanity_check_format_const() {
400 let format = ValueFormat::X_ADVANCE_DEVICE
401 | ValueFormat::Y_ADVANCE_DEVICE
402 | ValueFormat::Y_PLACEMENT_DEVICE
403 | ValueFormat::X_PLACEMENT_DEVICE;
404 assert_eq!(format, ValueFormat::ANY_DEVICE_OR_VARIDX);
405 assert_eq!(format.record_byte_len(), 4 * 2);
406 }
407}