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 ValueRecord {
212 explicit_format: Some(from.format),
215 x_placement: from.x_placement(),
216 y_placement: from.y_placement(),
217 x_advance: from.x_advance(),
218 y_advance: from.y_advance(),
219 x_placement_device: from.x_placement_device(data).to_owned_obj(data),
220 y_placement_device: from.y_placement_device(data).to_owned_obj(data),
221 x_advance_device: from.x_advance_device(data).to_owned_obj(data),
222 y_advance_device: from.y_advance_device(data).to_owned_obj(data),
223 }
224 }
225}
226
227#[cfg(test)]
228mod tests {
229 use font_types::GlyphId16;
230 use read_fonts::FontRead;
231
232 use crate::tables::{
233 gpos::{SinglePos, SinglePosFormat1, SinglePosFormat2},
234 layout::{builders::CoverageTableBuilder, VariationIndex},
235 };
236
237 use super::*;
238 #[test]
239 fn serialize_explicit_value_record() {
240 let mut my_record = ValueRecord {
241 x_advance: Some(5),
242 ..Default::default()
243 };
244 my_record.set_explicit_value_format(ValueFormat::X_ADVANCE | ValueFormat::X_ADVANCE_DEVICE);
245 let bytes = crate::dump_table(&my_record).unwrap();
246 assert_eq!(bytes.len(), 4);
247 let read_back =
248 read_fonts::tables::gpos::ValueRecord::read(FontData::new(&bytes), my_record.format())
249 .unwrap();
250 assert!(read_back.x_advance_device.get().is_null());
251 }
252
253 #[test]
254 fn compile_devices() {
255 let my_record = ValueRecord::new().with_x_advance_device(VariationIndex::new(0xff, 0xee));
256 let a_table = SinglePos::format_1(
257 CoverageTableBuilder::from_glyphs(vec![GlyphId16::new(42)]).build(),
258 my_record,
259 );
260
261 let bytes = crate::dump_table(&a_table).unwrap();
262 let read_back = SinglePosFormat1::read(bytes.as_slice().into()).unwrap();
263 assert!(
264 matches!(read_back.value_record.x_advance_device.as_ref(), Some(DeviceOrVariationIndex::VariationIndex(var_idx)) if var_idx.delta_set_inner_index == 0xee)
265 )
266 }
267
268 #[test]
269 fn roundtrip_preserves_format() {
270 let format = ValueFormat::X_ADVANCE | ValueFormat::X_ADVANCE_DEVICE;
271 let record = ValueRecord::new()
272 .with_x_advance(5)
273 .with_explicit_value_format(format);
274 let bytes = crate::dump_table(&record).unwrap();
275 assert_eq!(bytes.len(), 4);
276
277 let read_back =
278 read_fonts::tables::gpos::ValueRecord::read(bytes.as_slice().into(), format).unwrap();
279 assert_eq!(read_back.x_advance(), Some(5));
280 assert!(read_back.x_advance_device(FontData::EMPTY).is_none());
281
282 let owned: ValueRecord = read_back.to_owned_obj(FontData::EMPTY);
283 assert_eq!(owned.explicit_format, Some(format))
284 }
285
286 #[test]
287 fn roundtrip_in_table() {
288 let format = ValueFormat::X_ADVANCE | ValueFormat::X_ADVANCE_DEVICE;
289
290 let a_table = SinglePos::format_2(
291 [GlyphId16::new(7), GlyphId16::new(9)].into_iter().collect(),
292 vec![
293 ValueRecord::new()
294 .with_explicit_value_format(format)
295 .with_x_advance(5),
296 ValueRecord::new()
297 .with_x_advance(7)
298 .with_x_advance_device(VariationIndex::new(0xde, 0xad)),
299 ],
300 );
301
302 let bytes = crate::dump_table(&a_table).unwrap();
303 let read_back = SinglePosFormat2::read(bytes.as_slice().into()).unwrap();
304 assert_eq!(read_back.value_records[0].explicit_format, Some(format));
305 assert_eq!(read_back.value_records[1].explicit_format, Some(format));
306 assert!(crate::dump_table(&read_back).is_ok());
307 }
308}