1use read_fonts::FontData;
4
5use super::ValueFormat;
6use crate::{
7 from_obj::{FromObjRef, ToOwnedObj},
8 offsets::NullableOffsetMarker,
9 tables::layout::DeviceOrVariationIndex,
10 validate::Validate,
11 write::{FontWrite, TableWriter},
12};
13
14#[derive(Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
25#[non_exhaustive]
26#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
27pub struct ValueRecord {
28 explicit_format: Option<ValueFormat>,
48 pub x_placement: Option<i16>,
49 pub y_placement: Option<i16>,
50 pub x_advance: Option<i16>,
51 pub y_advance: Option<i16>,
52 pub x_placement_device: NullableOffsetMarker<DeviceOrVariationIndex>,
53 pub y_placement_device: NullableOffsetMarker<DeviceOrVariationIndex>,
54 pub x_advance_device: NullableOffsetMarker<DeviceOrVariationIndex>,
55 pub y_advance_device: NullableOffsetMarker<DeviceOrVariationIndex>,
56}
57
58impl ValueRecord {
59 pub fn new() -> ValueRecord {
60 ValueRecord::default()
61 }
62
63 pub fn with_x_placement(mut self, val: i16) -> Self {
64 self.x_placement = Some(val);
65 self
66 }
67
68 pub fn with_y_placement(mut self, val: i16) -> Self {
69 self.y_placement = Some(val);
70 self
71 }
72
73 pub fn with_x_advance(mut self, val: i16) -> Self {
74 self.x_advance = Some(val);
75 self
76 }
77
78 pub fn with_y_advance(mut self, val: i16) -> Self {
79 self.y_advance = Some(val);
80 self
81 }
82
83 pub fn with_x_placement_device(mut self, val: impl Into<DeviceOrVariationIndex>) -> Self {
84 self.x_placement_device = val.into().into();
85 self
86 }
87
88 pub fn with_y_placement_device(mut self, val: impl Into<DeviceOrVariationIndex>) -> Self {
89 self.y_placement_device = val.into().into();
90 self
91 }
92
93 pub fn with_x_advance_device(mut self, val: impl Into<DeviceOrVariationIndex>) -> Self {
94 self.x_advance_device = val.into().into();
95 self
96 }
97
98 pub fn with_y_advance_device(mut self, val: impl Into<DeviceOrVariationIndex>) -> Self {
99 self.y_advance_device = val.into().into();
100 self
101 }
102
103 pub fn with_explicit_value_format(mut self, format: ValueFormat) -> Self {
104 self.set_explicit_value_format(format);
105 self
106 }
107
108 pub fn set_explicit_value_format(&mut self, format: ValueFormat) {
113 self.explicit_format = Some(format)
114 }
115
116 pub fn format(&self) -> ValueFormat {
118 if let Some(format) = self.explicit_format {
119 return format;
120 }
121
122 macro_rules! flag_if_true {
123 ($field:expr, $flag:expr) => {
124 $field
125 .is_some()
126 .then(|| $flag)
127 .unwrap_or(ValueFormat::empty())
128 };
129 }
130
131 flag_if_true!(self.x_placement, ValueFormat::X_PLACEMENT)
132 | flag_if_true!(self.y_placement, ValueFormat::Y_PLACEMENT)
133 | flag_if_true!(self.x_advance, ValueFormat::X_ADVANCE)
134 | flag_if_true!(self.y_advance, ValueFormat::Y_ADVANCE)
135 | flag_if_true!(self.x_placement_device, ValueFormat::X_PLACEMENT_DEVICE)
136 | flag_if_true!(self.y_placement_device, ValueFormat::Y_PLACEMENT_DEVICE)
137 | flag_if_true!(self.x_advance_device, ValueFormat::X_ADVANCE_DEVICE)
138 | flag_if_true!(self.y_advance_device, ValueFormat::Y_ADVANCE_DEVICE)
139 }
140
141 pub fn encoded_size(&self) -> usize {
143 self.format().bits().count_ones() as usize * 2
144 }
145}
146
147impl FontWrite for ValueRecord {
148 fn write_into(&self, writer: &mut TableWriter) {
149 let format = self.format();
150 macro_rules! write_field {
151 ($field:expr, $flag:expr) => {
152 if format.contains($flag) {
153 $field.unwrap_or_default().write_into(writer);
154 }
155 };
156 ($field:expr, $flag:expr, off) => {
157 if format.contains($flag) {
158 $field.write_into(writer);
159 }
160 };
161 }
162
163 write_field!(self.x_placement, ValueFormat::X_PLACEMENT);
164 write_field!(self.y_placement, ValueFormat::Y_PLACEMENT);
165 write_field!(self.x_advance, ValueFormat::X_ADVANCE);
166 write_field!(self.y_advance, ValueFormat::Y_ADVANCE);
167 write_field!(
168 self.x_placement_device,
169 ValueFormat::X_PLACEMENT_DEVICE,
170 off
171 );
172 write_field!(
173 self.y_placement_device,
174 ValueFormat::Y_PLACEMENT_DEVICE,
175 off
176 );
177 write_field!(self.x_advance_device, ValueFormat::X_ADVANCE_DEVICE, off);
178 write_field!(self.y_advance_device, ValueFormat::Y_ADVANCE_DEVICE, off);
179 }
180}
181
182impl std::fmt::Debug for ValueRecord {
183 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
184 let mut f = f.debug_struct("ValueRecord");
185 self.x_placement.map(|x| f.field("x_placement", &x));
186 self.y_placement.map(|y| f.field("y_placement", &y));
187 self.x_advance.map(|x| f.field("x_advance", &x));
188 self.y_advance.map(|y| f.field("y_advance", &y));
189 self.x_placement_device
190 .as_ref()
191 .map(|x| f.field("x_placement_device", &x));
192 self.y_placement_device
193 .as_ref()
194 .map(|y| f.field("y_placement_device", &y));
195 self.x_advance_device
196 .as_ref()
197 .map(|x| f.field("x_advance_device", &x));
198 self.y_advance_device
199 .as_ref()
200 .map(|y| f.field("y_advance_device", &y));
201 f.finish()
202 }
203}
204
205impl Validate for ValueRecord {
206 fn validate_impl(&self, _ctx: &mut crate::validate::ValidationCtx) {}
207}
208
209impl FromObjRef<read_fonts::tables::gpos::ValueRecord<'_>> for ValueRecord {
210 fn from_obj_ref(from: &read_fonts::tables::gpos::ValueRecord, _data: FontData) -> Self {
211 let data = from.offset_data();
215 ValueRecord {
216 explicit_format: Some(from.format()),
219 x_placement: from.x_placement(),
220 y_placement: from.y_placement(),
221 x_advance: from.x_advance(),
222 y_advance: from.y_advance(),
223 x_placement_device: from.x_placement_device().to_owned_obj(data),
224 y_placement_device: from.y_placement_device().to_owned_obj(data),
225 x_advance_device: from.x_advance_device().to_owned_obj(data),
226 y_advance_device: from.y_advance_device().to_owned_obj(data),
227 }
228 }
229}
230
231#[cfg(test)]
232mod tests {
233 use font_types::GlyphId16;
234 use read_fonts::FontRead;
235
236 use crate::tables::{
237 gpos::{SinglePos, SinglePosFormat1, SinglePosFormat2},
238 layout::{builders::CoverageTableBuilder, VariationIndex},
239 };
240
241 use super::*;
242 #[test]
243 fn serialize_explicit_value_record() {
244 let mut my_record = ValueRecord {
245 x_advance: Some(5),
246 ..Default::default()
247 };
248 my_record.set_explicit_value_format(ValueFormat::X_ADVANCE | ValueFormat::X_ADVANCE_DEVICE);
249 let bytes = crate::dump_table(&my_record).unwrap();
250 assert_eq!(bytes.len(), 4);
251 let read_back = read_fonts::tables::gpos::ValueRecord::new(
252 FontData::new(&bytes),
253 0,
254 my_record.format(),
255 );
256 assert!(read_back
257 .device_offset(ValueFormat::X_ADVANCE_DEVICE)
258 .unwrap()
259 .is_null());
260 }
261
262 #[test]
263 fn compile_devices() {
264 let my_record = ValueRecord::new().with_x_advance_device(VariationIndex::new(0xff, 0xee));
265 let a_table = SinglePos::format_1(
266 CoverageTableBuilder::from_glyphs(vec![GlyphId16::new(42)]).build(),
267 my_record,
268 );
269
270 let bytes = crate::dump_table(&a_table).unwrap();
271 let read_back = SinglePosFormat1::read(bytes.as_slice().into()).unwrap();
272 assert!(
273 matches!(read_back.value_record.x_advance_device.as_ref(), Some(DeviceOrVariationIndex::VariationIndex(var_idx)) if var_idx.delta_set_inner_index == 0xee)
274 )
275 }
276
277 #[test]
278 fn roundtrip_preserves_format() {
279 let format = ValueFormat::X_ADVANCE | ValueFormat::X_ADVANCE_DEVICE;
280 let record = ValueRecord::new()
281 .with_x_advance(5)
282 .with_explicit_value_format(format);
283 let bytes = crate::dump_table(&record).unwrap();
284 assert_eq!(bytes.len(), 4);
285
286 let read_back =
287 read_fonts::tables::gpos::ValueRecord::new(bytes.as_slice().into(), 0, format);
288 assert_eq!(read_back.x_advance(), Some(5));
289 assert!(read_back.x_advance_device().is_none());
290
291 let owned: ValueRecord = read_back.to_owned_obj(FontData::EMPTY);
292 assert_eq!(owned.explicit_format, Some(format))
293 }
294
295 #[test]
296 fn roundtrip_in_table() {
297 let format = ValueFormat::X_ADVANCE | ValueFormat::X_ADVANCE_DEVICE;
298
299 let a_table = SinglePos::format_2(
300 [GlyphId16::new(7), GlyphId16::new(9)].into_iter().collect(),
301 vec![
302 ValueRecord::new()
303 .with_explicit_value_format(format)
304 .with_x_advance(5),
305 ValueRecord::new()
306 .with_x_advance(7)
307 .with_x_advance_device(VariationIndex::new(0xde, 0xad)),
308 ],
309 );
310
311 let bytes = crate::dump_table(&a_table).unwrap();
312 let read_back = SinglePosFormat2::read(bytes.as_slice().into()).unwrap();
313 assert_eq!(read_back.value_records[0].explicit_format, Some(format));
314 assert_eq!(read_back.value_records[1].explicit_format, Some(format));
315 assert!(crate::dump_table(&read_back).is_ok());
316 }
317}