1use super::aat::StateTable;
4pub use super::kerx::Subtable0Pair;
5
6include!("../../generated/generated_kern.rs");
7
8#[derive(Clone)]
10pub enum Kern<'a> {
11 Ot(OtKern<'a>),
12 Aat(AatKern<'a>),
13}
14
15impl TopLevelTable for Kern<'_> {
16 const TAG: Tag = Tag::new(b"kern");
17}
18
19impl ReadArgs for Kern<'_> {
20 type Args = ();
21}
22
23impl<'a> FontRead<'a> for Kern<'a> {
24 fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
25 if data.read_at::<u16>(0)? == 0 {
30 OtKern::read(data).map(Self::Ot)
31 } else {
32 AatKern::read(data).map(Self::Aat)
33 }
34 }
35}
36
37impl<'a> Kern<'a> {
38 pub fn subtables(&self) -> impl Iterator<Item = Result<Subtable<'a>, ReadError>> + 'a + Clone {
40 let (data, is_aat, n_tables) = match self {
41 Self::Ot(table) => (table.subtable_data(), false, table.n_tables() as u32),
42 Self::Aat(table) => (table.subtable_data(), true, table.n_tables()),
43 };
44 let data = FontData::new(data);
45 Subtables {
46 data,
47 is_aat,
48 n_tables,
49 }
50 }
51}
52
53#[derive(Clone)]
55struct Subtables<'a> {
56 data: FontData<'a>,
57 is_aat: bool,
58 n_tables: u32,
59}
60
61impl<'a> Iterator for Subtables<'a> {
62 type Item = Result<Subtable<'a>, ReadError>;
63
64 fn next(&mut self) -> Option<Self::Item> {
65 if self.n_tables == 0 || self.data.is_empty() {
66 return None;
67 }
68 self.n_tables -= 1;
69 let len = if self.is_aat {
70 self.data.read_at::<u32>(0).ok()? as usize
71 } else if self.n_tables == 0 {
72 self.data.len()
79 } else {
80 self.data.read_at::<u16>(2).ok()? as usize
81 };
82 if len == 0 {
83 return None;
84 }
85 let data = self.data.take_up_to(len)?;
86 Some(Subtable::read_with_args(data, self.is_aat))
87 }
88}
89
90impl OtSubtable<'_> {
91 const HEADER_LEN: usize = u16::RAW_BYTE_LEN * 3;
93}
94
95impl AatSubtable<'_> {
96 const HEADER_LEN: usize = u32::RAW_BYTE_LEN + u16::RAW_BYTE_LEN * 2;
98}
99
100#[derive(Clone)]
102pub enum Subtable<'a> {
103 Ot(OtSubtable<'a>),
104 Aat(AatSubtable<'a>),
105}
106
107impl ReadArgs for Subtable<'_> {
108 type Args = bool;
110}
111
112impl<'a> FontRead<'a> for Subtable<'a> {
113 fn read_with_args(data: FontData<'a>, args: Self::Args) -> Result<Self, ReadError> {
114 let is_aat = args;
115 if is_aat {
116 Ok(Self::Aat(AatSubtable::read(data)?))
117 } else {
118 Ok(Self::Ot(OtSubtable::read(data)?))
119 }
120 }
121}
122
123impl<'a> Subtable<'a> {
124 #[inline]
126 pub fn is_vertical(&self) -> bool {
127 match self {
128 Self::Ot(subtable) => subtable.coverage() & (1 << 0) == 0,
129 Self::Aat(subtable) => subtable.coverage() & 0x8000 != 0,
130 }
131 }
132
133 #[inline]
135 pub fn is_horizontal(&self) -> bool {
136 !self.is_vertical()
137 }
138
139 #[inline]
149 pub fn is_cross_stream(&self) -> bool {
150 match self {
151 Self::Ot(subtable) => subtable.coverage() & (1 << 2) != 0,
152 Self::Aat(subtable) => subtable.coverage() & 0x4000 != 0,
153 }
154 }
155
156 #[inline]
158 pub fn is_variable(&self) -> bool {
159 match self {
160 Self::Ot(_) => false,
161 Self::Aat(subtable) => subtable.coverage() & 0x2000 != 0,
162 }
163 }
164
165 #[inline]
167 pub fn is_state_machine(&self) -> bool {
168 self.data_and_format().1 == 1
170 }
171
172 pub fn kind(&self) -> Result<SubtableKind<'a>, ReadError> {
174 let (data, format) = self.data_and_format();
175 let is_aat = matches!(self, Self::Aat(_));
176 SubtableKind::read_with_args(FontData::new(data), (format, is_aat))
177 }
178
179 fn data_and_format(&self) -> (&'a [u8], u8) {
180 match self {
181 Self::Ot(subtable) => (subtable.data(), ((subtable.coverage() & 0xFF00) >> 8) as u8),
182 Self::Aat(subtable) => (subtable.data(), subtable.coverage() as u8),
183 }
184 }
185}
186
187#[derive(Clone)]
189pub enum SubtableKind<'a> {
190 Format0(Subtable0<'a>),
191 Format1(StateTable<'a>),
192 Format2(Subtable2<'a>),
193 Format3(Subtable3<'a>),
194}
195
196impl ReadArgs for SubtableKind<'_> {
197 type Args = (u8, bool);
198}
199
200impl<'a> FontRead<'a> for SubtableKind<'a> {
201 fn read_with_args(data: FontData<'a>, args: Self::Args) -> Result<Self, ReadError> {
202 let (format, is_aat) = args;
203 match format {
204 0 => Ok(Self::Format0(Subtable0::read(data)?)),
205 1 => Ok(Self::Format1(StateTable::read(data)?)),
206 2 => {
207 let header_len = if is_aat {
208 AatSubtable::HEADER_LEN
209 } else {
210 OtSubtable::HEADER_LEN
211 };
212 Ok(Self::Format2(Subtable2::read_with_args(data, header_len)?))
213 }
214 3 => Ok(Self::Format3(Subtable3::read(data)?)),
215 _ => Err(ReadError::InvalidFormat(format as _)),
216 }
217 }
218}
219
220impl Subtable0<'_> {
221 pub fn kerning(&self, left: GlyphId, right: GlyphId) -> Option<i32> {
223 super::kerx::pair_kerning(self.pairs(), left, right)
224 }
225}
226
227#[derive(Clone)]
229pub struct Subtable2<'a> {
230 pub data: FontData<'a>,
231 pub header_len: usize,
233 pub left_offset_table: Subtable2ClassTable<'a>,
235 pub right_offset_table: Subtable2ClassTable<'a>,
237 pub array_offset: usize,
239}
240
241impl ReadArgs for Subtable2<'_> {
242 type Args = usize;
243}
244
245impl<'a> FontRead<'a> for Subtable2<'a> {
246 fn read_with_args(data: FontData<'a>, args: Self::Args) -> Result<Self, ReadError> {
247 let mut cursor = data.cursor();
248 let header_len = args;
249 cursor.advance_by(u16::RAW_BYTE_LEN);
251 let left_offset = (cursor.read::<u16>()? as usize)
255 .checked_sub(header_len)
256 .ok_or(ReadError::OutOfBounds)?;
257 let right_offset = (cursor.read::<u16>()? as usize)
258 .checked_sub(header_len)
259 .ok_or(ReadError::OutOfBounds)?;
260 let array_offset = (cursor.read::<u16>()? as usize)
261 .checked_sub(header_len)
262 .ok_or(ReadError::OutOfBounds)?;
263 let left_offset_table =
264 Subtable2ClassTable::read(data.slice(left_offset..).ok_or(ReadError::OutOfBounds)?)?;
265 let right_offset_table =
266 Subtable2ClassTable::read(data.slice(right_offset..).ok_or(ReadError::OutOfBounds)?)?;
267 Ok(Self {
268 data,
269 header_len,
270 left_offset_table,
271 right_offset_table,
272 array_offset,
273 })
274 }
275}
276
277impl Subtable2<'_> {
278 pub fn kerning(&self, left: GlyphId, right: GlyphId) -> Option<i32> {
280 let left_offset = self.left_offset_table.value(left).unwrap_or(0) as usize;
281 let right_offset = self.right_offset_table.value(right).unwrap_or(0) as usize;
282 let left_offset = left_offset.checked_sub(self.header_len)?;
286 if left_offset < self.array_offset {
289 return None;
290 }
291 let offset = left_offset.checked_add(right_offset)?;
294 self.data
295 .read_at::<i16>(offset)
296 .ok()
297 .map(|value| value as i32)
298 }
299}
300
301impl Subtable2ClassTable<'_> {
302 fn value(&self, glyph_id: GlyphId) -> Option<u16> {
303 let glyph_id: u16 = glyph_id.to_u32().try_into().ok()?;
304 let index = glyph_id.checked_sub(self.first_glyph().to_u16())?;
305 self.offsets()
306 .get(index as usize)
307 .map(|offset| offset.get())
308 }
309}
310
311impl Subtable3<'_> {
312 pub fn kerning(&self, left: GlyphId, right: GlyphId) -> Option<i32> {
314 let left_class = self.left_class().get(left.to_u32() as usize).copied()? as usize;
315 let right_class = self.right_class().get(right.to_u32() as usize).copied()? as usize;
316 let index = self
317 .kern_index()
318 .get(left_class * self.right_class_count() as usize + right_class)
319 .copied()? as usize;
320 self.kern_value().get(index).map(|value| value.get() as i32)
321 }
322}
323
324#[cfg(test)]
325mod tests {
326 use font_test_data::bebuffer::BeBuffer;
327
328 use super::*;
329
330 #[test]
331 fn ot_format_0() {
332 #[rustfmt::skip]
334 const KERN_VER_0_FMT_0_DATA: &[u8] = &[
335 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x01, 0x00, 0x03, 0x00, 0x0C, 0x00, 0x01, 0x00, 0x06, 0x00, 0x04, 0x00, 0x0C, 0xFF, 0xD8, 0x00, 0x04, 0x00, 0x1C, 0x00, 0x28, 0x00, 0x05, 0x00, 0x28, 0xFF, 0xCE, ];
349 let kern = Kern::read(FontData::new(KERN_VER_0_FMT_0_DATA)).unwrap();
350 let Kern::Ot(ot_kern) = &kern else {
351 panic!("Should be an OpenType kerning table");
352 };
353 assert_eq!(ot_kern.version(), 0);
354 assert_eq!(ot_kern.n_tables(), 1);
355 let subtables = kern.subtables().collect::<Vec<_>>();
356 assert_eq!(subtables.len(), 1);
357 let subtable = subtables.first().unwrap().as_ref().unwrap();
358 assert!(subtable.is_horizontal());
359 let Subtable::Ot(ot_subtable) = subtable else {
360 panic!("Should be an OpenType subtable");
361 };
362 assert_eq!(ot_subtable.coverage(), 1);
363 assert_eq!(ot_subtable.length(), 32);
364 check_format_0(subtable);
365 }
366
367 #[test]
368 fn aat_format_0() {
369 #[rustfmt::skip]
371 const KERN_VER_1_FMT_0_DATA: &[u8] = &[
372 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0C, 0x00, 0x01, 0x00, 0x06, 0x00, 0x04, 0x00, 0x0C, 0xFF, 0xD8, 0x00, 0x04, 0x00, 0x1C, 0x00, 0x28, 0x00, 0x05, 0x00, 0x28, 0xFF, 0xCE, ];
386 let kern = Kern::read(FontData::new(KERN_VER_1_FMT_0_DATA)).unwrap();
387 let Kern::Aat(aat_kern) = &kern else {
388 panic!("Should be an AAT kerning table");
389 };
390 assert_eq!(aat_kern.version(), MajorMinor::VERSION_1_0);
391 assert_eq!(aat_kern.n_tables(), 1);
392 let subtables = kern.subtables().collect::<Vec<_>>();
393 assert_eq!(subtables.len(), 1);
394 let subtable = subtables.first().unwrap().as_ref().unwrap();
395 assert!(subtable.is_horizontal());
396 let Subtable::Aat(aat_subtable) = subtable else {
397 panic!("Should be an AAT subtable");
398 };
399 assert_eq!(aat_subtable.coverage(), 0);
400 assert_eq!(aat_subtable.length(), 34);
401 check_format_0(subtable);
402 }
403
404 fn check_format_0(subtable: &Subtable) {
405 let SubtableKind::Format0(format0) = subtable.kind().unwrap() else {
406 panic!("Should be a format 0 subtable");
407 };
408 const EXPECTED: &[(u32, u32, i32)] = &[(4, 12, -40), (4, 28, 40), (5, 40, -50)];
409 let pairs = format0
410 .pairs()
411 .iter()
412 .map(|pair| {
413 (
414 pair.left().to_u32(),
415 pair.right().to_u32(),
416 pair.value() as i32,
417 )
418 })
419 .collect::<Vec<_>>();
420 assert_eq!(pairs, EXPECTED);
421 for (left, right, value) in EXPECTED.iter().copied() {
422 assert_eq!(
423 format0.kerning(left.into(), right.into()),
424 Some(value),
425 "left = {left}, right = {right}"
426 );
427 }
428 }
429
430 #[test]
431 fn format_2() {
432 let kern = Kern::read(FontData::new(KERN_FORMAT_2)).unwrap();
433 let subtables = kern.subtables().filter_map(|t| t.ok()).collect::<Vec<_>>();
434 assert_eq!(subtables.len(), 3);
435 check_format_2(
437 &subtables[1],
438 &[
439 (68, 60, -100),
440 (68, 61, -20),
441 (68, 88, -20),
442 (69, 67, -30),
443 (69, 69, -30),
444 (69, 70, -30),
445 (69, 71, -30),
446 (69, 73, -30),
447 (69, 81, -30),
448 (69, 83, -30),
449 (72, 67, -20),
450 (72, 69, -20),
451 (72, 70, -20),
452 (72, 71, -20),
453 (72, 73, -20),
454 (72, 81, -20),
455 (72, 83, -20),
456 (81, 60, -100),
457 (81, 61, -20),
458 (81, 88, -20),
459 (82, 60, -100),
460 (82, 61, -20),
461 (82, 88, -20),
462 (84, 67, -50),
463 (84, 69, -50),
464 (84, 70, -50),
465 (84, 71, -50),
466 (84, 73, -50),
467 (84, 81, -50),
468 (84, 83, -50),
469 (88, 67, -20),
470 (88, 69, -20),
471 (88, 70, -20),
472 (88, 71, -20),
473 (88, 73, -20),
474 (88, 81, -20),
475 (88, 83, -20),
476 ],
477 );
478 check_format_2(
479 &subtables[2],
480 &[
481 (60, 67, -100),
482 (60, 69, -100),
483 (60, 70, -100),
484 (60, 71, -100),
485 (60, 73, -100),
486 (60, 81, -100),
487 (60, 83, -100),
488 ],
489 );
490 }
491
492 fn check_format_2(subtable: &Subtable, expected: &[(u32, u32, i32)]) {
493 let SubtableKind::Format2(format2) = subtable.kind().unwrap() else {
494 panic!("Should be a format 2 subtable");
495 };
496 for (left, right, value) in expected.iter().copied() {
497 assert_eq!(
498 format2.kerning(left.into(), right.into()),
499 Some(value),
500 "left = {left}, right = {right}"
501 );
502 }
503 }
504
505 const KERN_FORMAT_2: &[u8] = &[
509 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x16, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
510 0x0, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0x0, 0x3C, 0xFF, 0x7E, 0x0, 0x0, 0x1, 0x74, 0x0,
511 0x2, 0x0, 0x0, 0x0, 0x12, 0x0, 0x7C, 0x0, 0xB0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
512 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0xEC, 0x0, 0x0,
513 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0xEC,
514 0xFF, 0xEC, 0xFF, 0xBA, 0xFF, 0x9C, 0xFF, 0xD8, 0xFF, 0xE2, 0xFF, 0x7E, 0x0, 0x0, 0xFF,
515 0xEC, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF,
516 0xE2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF,
517 0xCE, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x44, 0x0,
518 0x18, 0x0, 0x34, 0x0, 0x58, 0x0, 0x10, 0x0, 0x10, 0x0, 0x22, 0x0, 0x10, 0x0, 0x10, 0x0,
519 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x34, 0x0, 0x34, 0x0,
520 0x10, 0x0, 0x6A, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x46, 0x0, 0x10, 0x0, 0x10, 0x0,
521 0x46, 0x0, 0x37, 0x0, 0x60, 0x0, 0x10, 0x0, 0x0, 0x0, 0x8, 0x0, 0xE, 0x0, 0xC, 0x0, 0xA,
522 0x0, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x2,
523 0x0, 0x2, 0x0, 0x2, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
524 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
525 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
526 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
527 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x2, 0x0, 0x2, 0x0, 0x2, 0x0, 0x0,
528 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
529 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
530 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
531 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2,
532 0x0, 0x0, 0x3, 0x84, 0x0, 0x2, 0x0, 0x0, 0x0, 0x16, 0x1, 0x44, 0x2, 0x64, 0x0, 0x10, 0x0,
533 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
534 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0xC4, 0xFF, 0xCE, 0xFF, 0xB0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
535 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x9C, 0xFF, 0xC4, 0xFF, 0x7E, 0x0,
536 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0xD8,
537 0xFF, 0xD8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
538 0x0, 0x0, 0xFF, 0xE2, 0xFF, 0xD8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
539 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x7E, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x60, 0x0, 0x0,
540 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0xD8, 0x0, 0x0, 0x0, 0x0,
541 0x0, 0x0, 0xFF, 0xE2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF,
542 0xD8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
543 0x0, 0x0, 0x0, 0xFF, 0xD8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
544 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0xD8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
545 0xFF, 0x6A, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x7E, 0xFF, 0xE2, 0xFF,
546 0x9C, 0xFF, 0x56, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
547 0x0, 0x0, 0x0, 0x0, 0xFF, 0xCE, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0xCE, 0xFF, 0xD8, 0xFF,
548 0xD8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0xCE, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x9C,
549 0xFF, 0xB0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0xEC, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0xEC, 0x0,
550 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0x0, 0x8E, 0x1,
551 0x18, 0x0, 0x10, 0x0, 0x10, 0x1, 0x2, 0x0, 0x10, 0x0, 0x94, 0x0, 0x10, 0x0, 0x10, 0x0,
552 0x10, 0x1, 0x2E, 0x0, 0x10, 0x0, 0xD6, 0x0, 0x10, 0x0, 0x10, 0x1, 0x2, 0x0, 0xAA, 0x0,
553 0x10, 0x0, 0xC0, 0x0, 0x10, 0x0, 0xEC, 0x1, 0x2E, 0x0, 0x26, 0x0, 0x68, 0x0, 0x52, 0x0,
554 0x3C, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0,
555 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0,
556 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0,
557 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0,
558 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x7E, 0x0,
559 0x10, 0x1, 0x2, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0,
560 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0,
561 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0,
562 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0,
563 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x7E, 0x0, 0x10, 0x0,
564 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x1, 0x18, 0x0, 0x10, 0x0,
565 0x10, 0x0, 0x7E, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0,
566 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0,
567 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x7E, 0x0, 0x10, 0x0, 0x7E, 0x0, 0x7E, 0x0,
568 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x1, 0x2, 0x0, 0x24, 0x0, 0x8E, 0x0, 0x6,
569 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8,
570 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
571 0x0, 0xC, 0x0, 0x14, 0x0, 0xE, 0x0, 0x10, 0x0, 0x12, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
572 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x2, 0x0, 0x2, 0x0, 0x2, 0x0,
573 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xA, 0x0, 0xA, 0x0,
574 0x2, 0x0, 0x0, 0x0, 0x2, 0x0, 0xA, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
575 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0,
576 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
577 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x2, 0x0, 0x2, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
578 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
579 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
580 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
581 0x0, 0x0, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0,
582 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
583 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
584 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4,
585 ];
586
587 #[test]
588 fn format_3() {
589 let mut buf = BeBuffer::new();
591 buf = buf.push(5u16); buf = buf.push(4u8); buf = buf.push(3u8); buf = buf.push(2u8); buf = buf.push(0u8); buf = buf.extend([0i16, -10, -20, 12]); buf = buf.extend([0u8, 2, 1, 1, 2]); buf = buf.extend([0u8, 1, 1, 0, 1]); buf = buf.extend([0u8, 1, 2, 3, 2, 1]); let format3 = Subtable3::read(FontData::new(buf.as_slice())).unwrap();
601 const EXPECTED: [(u32, u32, i32); 25] = [
602 (0, 0, 0),
603 (0, 1, -10),
604 (0, 2, -10),
605 (0, 3, 0),
606 (0, 4, -10),
607 (1, 0, -20),
608 (1, 1, -10),
609 (1, 2, -10),
610 (1, 3, -20),
611 (1, 4, -10),
612 (2, 0, -20),
613 (2, 1, 12),
614 (2, 2, 12),
615 (2, 3, -20),
616 (2, 4, 12),
617 (3, 0, -20),
618 (3, 1, 12),
619 (3, 2, 12),
620 (3, 3, -20),
621 (3, 4, 12),
622 (4, 0, -20),
623 (4, 1, -10),
624 (4, 2, -10),
625 (4, 3, -20),
626 (4, 4, -10),
627 ];
628 for (left, right, value) in EXPECTED {
629 assert_eq!(
630 format3.kerning(left.into(), right.into()),
631 Some(value),
632 "left = {left}, right = {right}"
633 );
634 }
635 }
636}