read_fonts/tables/
value_record.rs1use 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 pub const ANY_DEVICE_OR_VARIDX: Self = ValueFormat {
16 bits: 0x0010 | 0x0020 | 0x0040 | 0x0080,
17 };
18
19 #[inline]
21 pub fn record_byte_len(self) -> usize {
22 self.bits().count_ones() as usize * u16::RAW_BYTE_LEN
23 }
24}
25
26#[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 pub format: ValueFormat,
44}
45
46impl 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}