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<'a> FontRead<'a> for Kern<'a> {
20 fn read(data: FontData<'a>) -> Result<Self, ReadError> {
21 if data.read_at::<u16>(0)? == 0 {
26 OtKern::read(data).map(Self::Ot)
27 } else {
28 AatKern::read(data).map(Self::Aat)
29 }
30 }
31}
32
33impl<'a> Kern<'a> {
34 pub fn subtables(&self) -> impl Iterator<Item = Result<Subtable<'a>, ReadError>> + 'a + Clone {
36 let (data, is_aat, n_tables) = match self {
37 Self::Ot(table) => (table.subtable_data(), false, table.n_tables() as u32),
38 Self::Aat(table) => (table.subtable_data(), true, table.n_tables()),
39 };
40 let data = FontData::new(data);
41 Subtables {
42 data,
43 is_aat,
44 n_tables,
45 }
46 }
47}
48
49#[derive(Clone)]
51struct Subtables<'a> {
52 data: FontData<'a>,
53 is_aat: bool,
54 n_tables: u32,
55}
56
57impl<'a> Iterator for Subtables<'a> {
58 type Item = Result<Subtable<'a>, ReadError>;
59
60 fn next(&mut self) -> Option<Self::Item> {
61 let len = if self.is_aat {
62 self.data.read_at::<u32>(0).ok()? as usize
63 } else if self.n_tables == 1 {
64 self.data.len()
71 } else {
72 self.data.read_at::<u16>(2).ok()? as usize
73 };
74 if len == 0 {
75 return None;
76 }
77 let data = self.data.take_up_to(len)?;
78 Some(Subtable::read_with_args(data, &self.is_aat))
79 }
80}
81
82impl OtSubtable<'_> {
83 const HEADER_LEN: usize = u16::RAW_BYTE_LEN * 3;
85}
86
87impl AatSubtable<'_> {
88 const HEADER_LEN: usize = u32::RAW_BYTE_LEN + u16::RAW_BYTE_LEN * 2;
90}
91
92#[derive(Clone)]
94pub enum Subtable<'a> {
95 Ot(OtSubtable<'a>),
96 Aat(AatSubtable<'a>),
97}
98
99impl ReadArgs for Subtable<'_> {
100 type Args = bool;
102}
103
104impl<'a> FontReadWithArgs<'a> for Subtable<'a> {
105 fn read_with_args(data: FontData<'a>, args: &Self::Args) -> Result<Self, ReadError> {
106 let is_aat = *args;
107 if is_aat {
108 Ok(Self::Aat(AatSubtable::read(data)?))
109 } else {
110 Ok(Self::Ot(OtSubtable::read(data)?))
111 }
112 }
113}
114
115impl<'a> Subtable<'a> {
116 #[inline]
118 pub fn is_vertical(&self) -> bool {
119 match self {
120 Self::Ot(subtable) => subtable.coverage() & (1 << 0) == 0,
121 Self::Aat(subtable) => subtable.coverage() & 0x8000 != 0,
122 }
123 }
124
125 #[inline]
127 pub fn is_horizontal(&self) -> bool {
128 !self.is_vertical()
129 }
130
131 #[inline]
141 pub fn is_cross_stream(&self) -> bool {
142 match self {
143 Self::Ot(subtable) => subtable.coverage() & (1 << 2) != 0,
144 Self::Aat(subtable) => subtable.coverage() & 0x4000 != 0,
145 }
146 }
147
148 #[inline]
150 pub fn is_variable(&self) -> bool {
151 match self {
152 Self::Ot(_) => false,
153 Self::Aat(subtable) => subtable.coverage() & 0x2000 != 0,
154 }
155 }
156
157 #[inline]
159 pub fn is_state_machine(&self) -> bool {
160 self.data_and_format().1 == 1
162 }
163
164 pub fn kind(&self) -> Result<SubtableKind<'a>, ReadError> {
166 let (data, format) = self.data_and_format();
167 let is_aat = matches!(self, Self::Aat(_));
168 SubtableKind::read_with_args(FontData::new(data), &(format, is_aat))
169 }
170
171 fn data_and_format(&self) -> (&'a [u8], u8) {
172 match self {
173 Self::Ot(subtable) => (subtable.data(), ((subtable.coverage() & 0xFF00) >> 8) as u8),
174 Self::Aat(subtable) => (subtable.data(), subtable.coverage() as u8),
175 }
176 }
177}
178
179#[derive(Clone)]
181pub enum SubtableKind<'a> {
182 Format0(Subtable0<'a>),
183 Format1(StateTable<'a>),
184 Format2(Subtable2<'a>),
185 Format3(Subtable3<'a>),
186}
187
188impl ReadArgs for SubtableKind<'_> {
189 type Args = (u8, bool);
190}
191
192impl<'a> FontReadWithArgs<'a> for SubtableKind<'a> {
193 fn read_with_args(data: FontData<'a>, args: &Self::Args) -> Result<Self, ReadError> {
194 let (format, is_aat) = *args;
195 match format {
196 0 => Ok(Self::Format0(Subtable0::read(data)?)),
197 1 => Ok(Self::Format1(StateTable::read(data)?)),
198 2 => {
199 let header_len = if is_aat {
200 AatSubtable::HEADER_LEN
201 } else {
202 OtSubtable::HEADER_LEN
203 };
204 Ok(Self::Format2(Subtable2::read_with_args(data, &header_len)?))
205 }
206 3 => Ok(Self::Format3(Subtable3::read(data)?)),
207 _ => Err(ReadError::InvalidFormat(format as _)),
208 }
209 }
210}
211
212impl Subtable0<'_> {
213 pub fn kerning(&self, left: GlyphId, right: GlyphId) -> Option<i32> {
215 super::kerx::pair_kerning(self.pairs(), left, right)
216 }
217}
218
219#[derive(Clone)]
221pub struct Subtable2<'a> {
222 pub data: FontData<'a>,
223 pub header_len: usize,
225 pub left_offset_table: Subtable2ClassTable<'a>,
227 pub right_offset_table: Subtable2ClassTable<'a>,
229 pub array_offset: usize,
231}
232
233impl ReadArgs for Subtable2<'_> {
234 type Args = usize;
235}
236
237impl<'a> FontReadWithArgs<'a> for Subtable2<'a> {
238 fn read_with_args(data: FontData<'a>, args: &Self::Args) -> Result<Self, ReadError> {
239 let mut cursor = data.cursor();
240 let header_len = *args;
241 cursor.advance_by(u16::RAW_BYTE_LEN);
243 let left_offset = (cursor.read::<u16>()? as usize)
247 .checked_sub(header_len)
248 .ok_or(ReadError::OutOfBounds)?;
249 let right_offset = (cursor.read::<u16>()? as usize)
250 .checked_sub(header_len)
251 .ok_or(ReadError::OutOfBounds)?;
252 let array_offset = (cursor.read::<u16>()? as usize)
253 .checked_sub(header_len)
254 .ok_or(ReadError::OutOfBounds)?;
255 let left_offset_table =
256 Subtable2ClassTable::read(data.slice(left_offset..).ok_or(ReadError::OutOfBounds)?)?;
257 let right_offset_table =
258 Subtable2ClassTable::read(data.slice(right_offset..).ok_or(ReadError::OutOfBounds)?)?;
259 Ok(Self {
260 data,
261 header_len,
262 left_offset_table,
263 right_offset_table,
264 array_offset,
265 })
266 }
267}
268
269impl Subtable2<'_> {
270 pub fn kerning(&self, left: GlyphId, right: GlyphId) -> Option<i32> {
272 let left_offset = self.left_offset_table.value(left).unwrap_or(0) as usize;
273 let right_offset = self.right_offset_table.value(right).unwrap_or(0) as usize;
274 let left_offset = left_offset.checked_sub(self.header_len)?;
278 if left_offset < self.array_offset {
281 return None;
282 }
283 let offset = left_offset.checked_add(right_offset)?;
286 self.data
287 .read_at::<i16>(offset)
288 .ok()
289 .map(|value| value as i32)
290 }
291}
292
293impl Subtable2ClassTable<'_> {
294 fn value(&self, glyph_id: GlyphId) -> Option<u16> {
295 let glyph_id: u16 = glyph_id.to_u32().try_into().ok()?;
296 let index = glyph_id.checked_sub(self.first_glyph().to_u16())?;
297 self.offsets()
298 .get(index as usize)
299 .map(|offset| offset.get())
300 }
301}
302
303impl Subtable3<'_> {
304 pub fn kerning(&self, left: GlyphId, right: GlyphId) -> Option<i32> {
306 let left_class = self.left_class().get(left.to_u32() as usize).copied()? as usize;
307 let right_class = self.right_class().get(right.to_u32() as usize).copied()? as usize;
308 let index = self
309 .kern_index()
310 .get(left_class * self.right_class_count() as usize + right_class)
311 .copied()? as usize;
312 self.kern_value().get(index).map(|value| value.get() as i32)
313 }
314}
315
316#[cfg(test)]
317mod tests {
318 use font_test_data::bebuffer::BeBuffer;
319
320 use super::*;
321
322 #[test]
323 fn ot_format_0() {
324 #[rustfmt::skip]
326 const KERN_VER_0_FMT_0_DATA: &[u8] = &[
327 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, ];
341 let kern = Kern::read(FontData::new(KERN_VER_0_FMT_0_DATA)).unwrap();
342 let Kern::Ot(ot_kern) = &kern else {
343 panic!("Should be an OpenType kerning table");
344 };
345 assert_eq!(ot_kern.version(), 0);
346 assert_eq!(ot_kern.n_tables(), 1);
347 let subtables = kern.subtables().collect::<Vec<_>>();
348 assert_eq!(subtables.len(), 1);
349 let subtable = subtables.first().unwrap().as_ref().unwrap();
350 assert!(subtable.is_horizontal());
351 let Subtable::Ot(ot_subtable) = subtable else {
352 panic!("Should be an OpenType subtable");
353 };
354 assert_eq!(ot_subtable.coverage(), 1);
355 assert_eq!(ot_subtable.length(), 32);
356 check_format_0(subtable);
357 }
358
359 #[test]
360 fn aat_format_0() {
361 #[rustfmt::skip]
363 const KERN_VER_1_FMT_0_DATA: &[u8] = &[
364 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, ];
378 let kern = Kern::read(FontData::new(KERN_VER_1_FMT_0_DATA)).unwrap();
379 let Kern::Aat(aat_kern) = &kern else {
380 panic!("Should be an AAT kerning table");
381 };
382 assert_eq!(aat_kern.version(), MajorMinor::VERSION_1_0);
383 assert_eq!(aat_kern.n_tables(), 1);
384 let subtables = kern.subtables().collect::<Vec<_>>();
385 assert_eq!(subtables.len(), 1);
386 let subtable = subtables.first().unwrap().as_ref().unwrap();
387 assert!(subtable.is_horizontal());
388 let Subtable::Aat(aat_subtable) = subtable else {
389 panic!("Should be an AAT subtable");
390 };
391 assert_eq!(aat_subtable.coverage(), 0);
392 assert_eq!(aat_subtable.length(), 34);
393 check_format_0(subtable);
394 }
395
396 fn check_format_0(subtable: &Subtable) {
397 let SubtableKind::Format0(format0) = subtable.kind().unwrap() else {
398 panic!("Should be a format 0 subtable");
399 };
400 const EXPECTED: &[(u32, u32, i32)] = &[(4, 12, -40), (4, 28, 40), (5, 40, -50)];
401 let pairs = format0
402 .pairs()
403 .iter()
404 .map(|pair| {
405 (
406 pair.left().to_u32(),
407 pair.right().to_u32(),
408 pair.value() as i32,
409 )
410 })
411 .collect::<Vec<_>>();
412 assert_eq!(pairs, EXPECTED);
413 for (left, right, value) in EXPECTED.iter().copied() {
414 assert_eq!(
415 format0.kerning(left.into(), right.into()),
416 Some(value),
417 "left = {left}, right = {right}"
418 );
419 }
420 }
421
422 #[test]
423 fn format_2() {
424 let kern = Kern::read(FontData::new(KERN_FORMAT_2)).unwrap();
425 let subtables = kern.subtables().filter_map(|t| t.ok()).collect::<Vec<_>>();
426 assert_eq!(subtables.len(), 3);
427 check_format_2(
429 &subtables[1],
430 &[
431 (68, 60, -100),
432 (68, 61, -20),
433 (68, 88, -20),
434 (69, 67, -30),
435 (69, 69, -30),
436 (69, 70, -30),
437 (69, 71, -30),
438 (69, 73, -30),
439 (69, 81, -30),
440 (69, 83, -30),
441 (72, 67, -20),
442 (72, 69, -20),
443 (72, 70, -20),
444 (72, 71, -20),
445 (72, 73, -20),
446 (72, 81, -20),
447 (72, 83, -20),
448 (81, 60, -100),
449 (81, 61, -20),
450 (81, 88, -20),
451 (82, 60, -100),
452 (82, 61, -20),
453 (82, 88, -20),
454 (84, 67, -50),
455 (84, 69, -50),
456 (84, 70, -50),
457 (84, 71, -50),
458 (84, 73, -50),
459 (84, 81, -50),
460 (84, 83, -50),
461 (88, 67, -20),
462 (88, 69, -20),
463 (88, 70, -20),
464 (88, 71, -20),
465 (88, 73, -20),
466 (88, 81, -20),
467 (88, 83, -20),
468 ],
469 );
470 check_format_2(
471 &subtables[2],
472 &[
473 (60, 67, -100),
474 (60, 69, -100),
475 (60, 70, -100),
476 (60, 71, -100),
477 (60, 73, -100),
478 (60, 81, -100),
479 (60, 83, -100),
480 ],
481 );
482 }
483
484 fn check_format_2(subtable: &Subtable, expected: &[(u32, u32, i32)]) {
485 let SubtableKind::Format2(format2) = subtable.kind().unwrap() else {
486 panic!("Should be a format 2 subtable");
487 };
488 for (left, right, value) in expected.iter().copied() {
489 assert_eq!(
490 format2.kerning(left.into(), right.into()),
491 Some(value),
492 "left = {left}, right = {right}"
493 );
494 }
495 }
496
497 const KERN_FORMAT_2: &[u8] = &[
501 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x16, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
502 0x0, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0x0, 0x3C, 0xFF, 0x7E, 0x0, 0x0, 0x1, 0x74, 0x0,
503 0x2, 0x0, 0x0, 0x0, 0x12, 0x0, 0x7C, 0x0, 0xB0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
504 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0xEC, 0x0, 0x0,
505 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0xEC,
506 0xFF, 0xEC, 0xFF, 0xBA, 0xFF, 0x9C, 0xFF, 0xD8, 0xFF, 0xE2, 0xFF, 0x7E, 0x0, 0x0, 0xFF,
507 0xEC, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF,
508 0xE2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF,
509 0xCE, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x44, 0x0,
510 0x18, 0x0, 0x34, 0x0, 0x58, 0x0, 0x10, 0x0, 0x10, 0x0, 0x22, 0x0, 0x10, 0x0, 0x10, 0x0,
511 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x34, 0x0, 0x34, 0x0,
512 0x10, 0x0, 0x6A, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x46, 0x0, 0x10, 0x0, 0x10, 0x0,
513 0x46, 0x0, 0x37, 0x0, 0x60, 0x0, 0x10, 0x0, 0x0, 0x0, 0x8, 0x0, 0xE, 0x0, 0xC, 0x0, 0xA,
514 0x0, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x2,
515 0x0, 0x2, 0x0, 0x2, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
516 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
517 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
518 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
519 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x2, 0x0, 0x2, 0x0, 0x2, 0x0, 0x0,
520 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
521 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
522 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
523 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2,
524 0x0, 0x0, 0x3, 0x84, 0x0, 0x2, 0x0, 0x0, 0x0, 0x16, 0x1, 0x44, 0x2, 0x64, 0x0, 0x10, 0x0,
525 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
526 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0xC4, 0xFF, 0xCE, 0xFF, 0xB0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
527 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x9C, 0xFF, 0xC4, 0xFF, 0x7E, 0x0,
528 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0xD8,
529 0xFF, 0xD8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
530 0x0, 0x0, 0xFF, 0xE2, 0xFF, 0xD8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
531 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x7E, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x60, 0x0, 0x0,
532 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0xD8, 0x0, 0x0, 0x0, 0x0,
533 0x0, 0x0, 0xFF, 0xE2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF,
534 0xD8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
535 0x0, 0x0, 0x0, 0xFF, 0xD8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
536 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0xD8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
537 0xFF, 0x6A, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x7E, 0xFF, 0xE2, 0xFF,
538 0x9C, 0xFF, 0x56, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
539 0x0, 0x0, 0x0, 0x0, 0xFF, 0xCE, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0xCE, 0xFF, 0xD8, 0xFF,
540 0xD8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0xCE, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0x9C,
541 0xFF, 0xB0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0xEC, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFF, 0xEC, 0x0,
542 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0x0, 0x8E, 0x1,
543 0x18, 0x0, 0x10, 0x0, 0x10, 0x1, 0x2, 0x0, 0x10, 0x0, 0x94, 0x0, 0x10, 0x0, 0x10, 0x0,
544 0x10, 0x1, 0x2E, 0x0, 0x10, 0x0, 0xD6, 0x0, 0x10, 0x0, 0x10, 0x1, 0x2, 0x0, 0xAA, 0x0,
545 0x10, 0x0, 0xC0, 0x0, 0x10, 0x0, 0xEC, 0x1, 0x2E, 0x0, 0x26, 0x0, 0x68, 0x0, 0x52, 0x0,
546 0x3C, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0,
547 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0,
548 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0,
549 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0,
550 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x7E, 0x0,
551 0x10, 0x1, 0x2, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0,
552 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0,
553 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0,
554 0x10, 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, 0x7E, 0x0, 0x10, 0x0,
556 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x1, 0x18, 0x0, 0x10, 0x0,
557 0x10, 0x0, 0x7E, 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, 0x10, 0x0,
559 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x7E, 0x0, 0x10, 0x0, 0x7E, 0x0, 0x7E, 0x0,
560 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x1, 0x2, 0x0, 0x24, 0x0, 0x8E, 0x0, 0x6,
561 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8,
562 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
563 0x0, 0xC, 0x0, 0x14, 0x0, 0xE, 0x0, 0x10, 0x0, 0x12, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
564 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x2, 0x0, 0x2, 0x0, 0x2, 0x0,
565 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xA, 0x0, 0xA, 0x0,
566 0x2, 0x0, 0x0, 0x0, 0x2, 0x0, 0xA, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
567 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0,
568 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
569 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x2, 0x0, 0x2, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
570 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
571 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
572 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
573 0x0, 0x0, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0,
574 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 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, 0x0, 0x0,
576 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4,
577 ];
578
579 #[test]
580 fn format_3() {
581 let mut buf = BeBuffer::new();
583 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();
593 const EXPECTED: [(u32, u32, i32); 25] = [
594 (0, 0, 0),
595 (0, 1, -10),
596 (0, 2, -10),
597 (0, 3, 0),
598 (0, 4, -10),
599 (1, 0, -20),
600 (1, 1, -10),
601 (1, 2, -10),
602 (1, 3, -20),
603 (1, 4, -10),
604 (2, 0, -20),
605 (2, 1, 12),
606 (2, 2, 12),
607 (2, 3, -20),
608 (2, 4, 12),
609 (3, 0, -20),
610 (3, 1, 12),
611 (3, 2, 12),
612 (3, 3, -20),
613 (3, 4, 12),
614 (4, 0, -20),
615 (4, 1, -10),
616 (4, 2, -10),
617 (4, 3, -20),
618 (4, 4, -10),
619 ];
620 for (left, right, value) in EXPECTED {
621 assert_eq!(
622 format3.kerning(left.into(), right.into()),
623 Some(value),
624 "left = {left}, right = {right}"
625 );
626 }
627 }
628}