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