1include!("../../generated/generated_hdmx.rs");
4
5use std::cmp::Ordering;
6
7impl<'a> Hdmx<'a> {
8 pub fn record_for_size(&self, size: u8) -> Option<DeviceRecord<'a>> {
11 let records = self.records();
12 let mut lo = 0;
15 let mut hi = records.len();
16 while lo < hi {
17 let mid = (lo + hi) / 2;
18 let record = records.get(mid).ok()?;
19 match record.pixel_size.cmp(&size) {
20 Ordering::Less => lo = mid + 1,
21 Ordering::Greater => hi = mid,
22 Ordering::Equal => return Some(record),
23 }
24 }
25 None
26 }
27}
28
29#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
30pub struct DeviceRecord<'a> {
31 pub pixel_size: u8,
33 pub max_width: u8,
35 pub widths: &'a [u8],
37}
38
39impl<'a> DeviceRecord<'a> {
40 pub fn pixel_size(&self) -> u8 {
42 self.pixel_size
43 }
44
45 pub fn max_width(&self) -> u8 {
47 self.max_width
48 }
49
50 pub fn widths(&self) -> &'a [u8] {
52 self.widths
53 }
54}
55
56impl ReadArgs for DeviceRecord<'_> {
57 type Args = (u16, u32);
58}
59
60impl ComputeSize for DeviceRecord<'_> {
61 fn compute_size(args: &(u16, u32)) -> Result<usize, ReadError> {
62 let (_num_glyphs, size_device_record) = *args;
63 Ok(size_device_record as usize)
65 }
66}
67
68impl<'a> FontReadWithArgs<'a> for DeviceRecord<'a> {
69 fn read_with_args(data: FontData<'a>, args: &(u16, u32)) -> Result<Self, ReadError> {
70 let mut cursor = data.cursor();
71 let (num_glyphs, _size_device_record) = *args;
72 Ok(Self {
73 pixel_size: cursor.read()?,
74 max_width: cursor.read()?,
75 widths: cursor.read_array(num_glyphs as usize)?,
76 })
77 }
78}
79
80#[allow(clippy::needless_lifetimes)]
81impl<'a> DeviceRecord<'a> {
82 pub fn read(
87 data: FontData<'a>,
88 num_glyphs: u16,
89 size_device_record: u32,
90 ) -> Result<Self, ReadError> {
91 let args = (num_glyphs, size_device_record);
92 Self::read_with_args(data, &args)
93 }
94}
95
96#[cfg(feature = "experimental_traverse")]
97impl<'a> SomeRecord<'a> for DeviceRecord<'a> {
98 fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
99 RecordResolver {
100 name: "DeviceRecord",
101 get_field: Box::new(move |idx, _data| match idx {
102 0usize => Some(Field::new("pixel_size", self.pixel_size())),
103 1usize => Some(Field::new("max_width", self.max_width())),
104 2usize => Some(Field::new("widths", self.widths())),
105 _ => None,
106 }),
107 data,
108 }
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 use super::*;
115 use font_test_data::{be_buffer, bebuffer::BeBuffer};
116
117 #[test]
118 fn read_hdmx() {
119 let buf = make_hdmx();
120 let hdmx = Hdmx::read(buf.data().into(), 3).unwrap();
121 assert_eq!(hdmx.version(), 0);
122 assert_eq!(hdmx.num_records(), 3);
123 assert_eq!(hdmx.size_device_record(), 8);
131 let records = hdmx
132 .records()
133 .iter()
134 .map(|rec| rec.unwrap())
135 .collect::<Vec<_>>();
136 assert_eq!(records.len(), 3);
137 let expected_records = [
138 DeviceRecord {
139 pixel_size: 8,
140 max_width: 13,
141 widths: &[10, 12, 13],
142 },
143 DeviceRecord {
144 pixel_size: 16,
145 max_width: 21,
146 widths: &[18, 20, 21],
147 },
148 DeviceRecord {
149 pixel_size: 32,
150 max_width: 52,
151 widths: &[38, 40, 52],
152 },
153 ];
154 assert_eq!(records, expected_records);
155 }
156
157 #[test]
158 fn find_by_size() {
159 let buf = make_hdmx();
160 let hdmx = Hdmx::read(buf.data().into(), 3).unwrap();
161 assert_eq!(hdmx.record_for_size(8).unwrap().pixel_size, 8);
162 assert_eq!(hdmx.record_for_size(16).unwrap().pixel_size, 16);
163 assert_eq!(hdmx.record_for_size(32).unwrap().pixel_size, 32);
164 assert!(hdmx.record_for_size(7).is_none());
165 assert!(hdmx.record_for_size(20).is_none());
166 assert!(hdmx.record_for_size(72).is_none());
167 }
168
169 fn make_hdmx() -> BeBuffer {
170 be_buffer! {
171 0u16, 3u16, 8u32, [8u8, 13, 10, 12, 13, 0, 0, 0],
176 [16u8, 21, 18, 20, 21, 0, 0, 0],
177 [32u8, 52, 38, 40, 52, 0, 0, 0]
178 }
179 }
180}