1use super::aat::{safe_read_array_to_end, ExtendedStateTable, LookupU16, LookupU32};
4
5include!("../../generated/generated_kerx.rs");
6
7impl VarSize for Subtable<'_> {
8 type Size = u32;
9
10 fn read_len_at(data: FontData, pos: usize) -> Option<usize> {
11 data.read_at::<u32>(pos).ok().map(|size| size as usize)
15 }
16}
17
18impl<'a> Subtable<'a> {
19 pub const HEADER_LEN: usize = u32::RAW_BYTE_LEN * 3;
21
22 #[inline]
24 pub fn is_vertical(&self) -> bool {
25 self.coverage() & 0x80000000 != 0
26 }
27
28 #[inline]
30 pub fn is_horizontal(&self) -> bool {
31 !self.is_vertical()
32 }
33
34 #[inline]
44 pub fn is_cross_stream(&self) -> bool {
45 self.coverage() & 0x40000000 != 0
46 }
47
48 #[inline]
50 pub fn is_variable(&self) -> bool {
51 self.coverage() & 0x20000000 != 0
52 }
53
54 #[inline]
59 pub fn process_direction(&self) -> bool {
60 self.coverage() & 0x10000000 != 0
61 }
62
63 pub fn kind(&self) -> Result<SubtableKind<'a>, ReadError> {
65 SubtableKind::read_with_args(
66 FontData::new(self.data()),
67 &(self.coverage(), self.tuple_count()),
68 )
69 }
70}
71
72#[derive(Clone)]
74pub enum SubtableKind<'a> {
75 Format0(Subtable0<'a>),
76 Format1(Subtable1<'a>),
77 Format2(Subtable2<'a>),
78 Format4(Subtable4<'a>),
79 Format6(Subtable6<'a>),
80}
81
82impl ReadArgs for SubtableKind<'_> {
83 type Args = (u32, u32);
84}
85
86impl<'a> FontReadWithArgs<'a> for SubtableKind<'a> {
87 fn read_with_args(data: FontData<'a>, args: &Self::Args) -> Result<Self, ReadError> {
88 let format = args.0 & 0xFF;
90 let tuple_count = args.1;
91 match format {
92 0 => Ok(Self::Format0(Subtable0::read(data)?)),
93 1 => Ok(Self::Format1(Subtable1::read(data)?)),
94 2 => Ok(Self::Format2(Subtable2::read(data)?)),
95 4 => Ok(Self::Format4(Subtable4::read(data)?)),
97 6 => Ok(Self::Format6(Subtable6::read_with_args(
99 data,
100 &tuple_count,
101 )?)),
102 _ => Err(ReadError::InvalidFormat(format as _)),
103 }
104 }
105}
106
107impl Subtable0<'_> {
108 pub fn kerning(&self, left: GlyphId, right: GlyphId) -> Option<i32> {
110 pair_kerning(self.pairs(), left, right)
111 }
112}
113
114pub(crate) fn pair_kerning(pairs: &[Subtable0Pair], left: GlyphId, right: GlyphId) -> Option<i32> {
115 let left: GlyphId16 = left.try_into().ok()?;
116 let right: GlyphId16 = right.try_into().ok()?;
117 fn make_key(left: GlyphId16, right: GlyphId16) -> u32 {
118 (left.to_u32() << 16) | right.to_u32()
119 }
120 let idx = pairs
121 .binary_search_by_key(&make_key(left, right), |pair| {
122 make_key(pair.left(), pair.right())
123 })
124 .ok()?;
125 pairs.get(idx).map(|pair| pair.value() as i32)
126}
127
128#[derive(Clone)]
130pub struct Subtable1<'a> {
131 pub state_table: ExtendedStateTable<'a, BigEndian<u16>>,
132 pub values: &'a [BigEndian<i16>],
134}
135
136impl<'a> FontRead<'a> for Subtable1<'a> {
137 fn read(data: FontData<'a>) -> Result<Self, ReadError> {
138 let state_table = ExtendedStateTable::read(data)?;
139 let mut cursor = data.cursor();
140 cursor.advance_by(ExtendedStateTable::<()>::HEADER_LEN);
141 let values_offset = cursor.read::<u32>()? as usize;
142 let values = super::aat::safe_read_array_to_end(&data, values_offset)?;
143 Ok(Self {
144 state_table,
145 values,
146 })
147 }
148}
149
150#[derive(Clone)]
152pub struct Subtable2<'a> {
153 pub data: FontData<'a>,
154 pub left_offset_table: LookupU16<'a>,
156 pub right_offset_table: LookupU16<'a>,
158 pub array: &'a [BigEndian<i16>],
160}
161
162impl<'a> FontRead<'a> for Subtable2<'a> {
163 fn read(data: FontData<'a>) -> Result<Self, ReadError> {
164 let mut cursor = data.cursor();
165 cursor.advance_by(u32::RAW_BYTE_LEN);
167 let left_offset = (cursor.read::<u32>()? as usize)
171 .checked_sub(Subtable::HEADER_LEN)
172 .ok_or(ReadError::OutOfBounds)?;
173 let right_offset = (cursor.read::<u32>()? as usize)
174 .checked_sub(Subtable::HEADER_LEN)
175 .ok_or(ReadError::OutOfBounds)?;
176 let array_offset = (cursor.read::<u32>()? as usize)
177 .checked_sub(Subtable::HEADER_LEN)
178 .ok_or(ReadError::OutOfBounds)?;
179 let left_offset_table =
180 LookupU16::read(data.slice(left_offset..).ok_or(ReadError::OutOfBounds)?)?;
181 let right_offset_table =
182 LookupU16::read(data.slice(right_offset..).ok_or(ReadError::OutOfBounds)?)?;
183 let array = safe_read_array_to_end(&data, array_offset)?;
184 Ok(Self {
185 data,
186 left_offset_table,
187 right_offset_table,
188 array,
189 })
190 }
191}
192
193impl Subtable2<'_> {
194 pub fn kerning(&self, left: GlyphId, right: GlyphId) -> Option<i32> {
196 let left: u16 = left.to_u32().try_into().ok()?;
197 let right: u16 = right.to_u32().try_into().ok()?;
198 let left_idx = self.left_offset_table.value(left).unwrap_or(0) as usize;
199 let right_idx = self.right_offset_table.value(right).unwrap_or(0) as usize;
200 self.array
201 .get(left_idx + right_idx)
202 .map(|value| value.get() as i32)
203 }
204}
205
206#[derive(Clone)]
208pub struct Subtable4<'a> {
209 pub state_table: ExtendedStateTable<'a, BigEndian<u16>>,
210 pub flags: u32,
212 pub actions: Subtable4Actions<'a>,
213}
214
215impl<'a> FontRead<'a> for Subtable4<'a> {
216 fn read(data: FontData<'a>) -> Result<Self, ReadError> {
217 let state_table = ExtendedStateTable::read(data)?;
218 let mut cursor = data.cursor();
219 cursor.advance_by(ExtendedStateTable::<()>::HEADER_LEN);
220 let flags = cursor.read::<u32>()?;
221 let action_type = (flags & 0xC0000000) >> 30;
222 let offset = (flags & 0x00FFFFFF) as usize;
223 let actions = match action_type {
224 0 => Subtable4Actions::ControlPoints(safe_read_array_to_end(&data, offset)?),
225 1 => Subtable4Actions::AnchorPoints(safe_read_array_to_end(&data, offset)?),
226 2 => Subtable4Actions::ControlPointCoords(safe_read_array_to_end(&data, offset)?),
227 _ => {
228 return Err(ReadError::MalformedData(
229 "invalid action type in kerx subtable 4",
230 ))
231 }
232 };
233 Ok(Self {
234 state_table,
235 flags,
236 actions,
237 })
238 }
239}
240
241#[derive(Clone)]
243pub enum Subtable4Actions<'a> {
244 ControlPoints(&'a [BigEndian<u16>]),
246 AnchorPoints(&'a [BigEndian<u16>]),
248 ControlPointCoords(&'a [BigEndian<i16>]),
250}
251
252#[derive(Clone)]
254pub enum Subtable6<'a> {
255 ShortValues(
256 LookupU16<'a>,
257 LookupU16<'a>,
258 &'a [BigEndian<i16>],
259 Option<&'a [BigEndian<i16>]>,
260 ),
261 LongValues(
262 LookupU32<'a>,
263 LookupU32<'a>,
264 &'a [BigEndian<i32>],
265 Option<&'a [BigEndian<i16>]>,
266 ),
267}
268
269impl ReadArgs for Subtable6<'_> {
270 type Args = u32;
271}
272
273impl<'a> FontReadWithArgs<'a> for Subtable6<'a> {
274 fn read_with_args(data: FontData<'a>, args: &Self::Args) -> Result<Self, ReadError> {
275 let tuple_count = *args;
276 let mut cursor = data.cursor();
277 let flags = cursor.read::<u32>()?;
278 cursor.advance_by(u16::RAW_BYTE_LEN * 2);
280 let row_index_table_offset = (cursor.read::<u32>()? as usize)
282 .checked_sub(Subtable::HEADER_LEN)
283 .ok_or(ReadError::OutOfBounds)?;
284 let column_index_table_offset = (cursor.read::<u32>()? as usize)
285 .checked_sub(Subtable::HEADER_LEN)
286 .ok_or(ReadError::OutOfBounds)?;
287 let kerning_array_offset = (cursor.read::<u32>()? as usize)
288 .checked_sub(Subtable::HEADER_LEN)
289 .ok_or(ReadError::OutOfBounds)?;
290 let kerning_vector = if tuple_count != 0 {
291 let kerning_vector_offset = (cursor.read::<u32>()? as usize)
292 .checked_sub(Subtable::HEADER_LEN)
293 .ok_or(ReadError::OutOfBounds)?;
294 Some(safe_read_array_to_end(&data, kerning_vector_offset)?)
295 } else {
296 None
297 };
298 let row_data = data
299 .slice(row_index_table_offset..)
300 .ok_or(ReadError::OutOfBounds)?;
301 let column_data = data
302 .slice(column_index_table_offset..)
303 .ok_or(ReadError::OutOfBounds)?;
304 if flags & 1 == 0 {
305 let rows = LookupU16::read(row_data)?;
306 let columns = LookupU16::read(column_data)?;
307 let kerning_array = safe_read_array_to_end(&data, kerning_array_offset)?;
308 Ok(Self::ShortValues(
309 rows,
310 columns,
311 kerning_array,
312 kerning_vector,
313 ))
314 } else {
315 let rows = LookupU32::read(row_data)?;
316 let columns = LookupU32::read(column_data)?;
317 let kerning_array = safe_read_array_to_end(&data, kerning_array_offset)?;
318 Ok(Self::LongValues(
319 rows,
320 columns,
321 kerning_array,
322 kerning_vector,
323 ))
324 }
325 }
326}
327
328impl Subtable6<'_> {
329 pub fn kerning(&self, left: GlyphId, right: GlyphId) -> Option<i32> {
331 let left: u16 = left.to_u32().try_into().ok()?;
332 let right: u16 = right.to_u32().try_into().ok()?;
333 fn tuple_kern(value: i32, vector: &Option<&[BigEndian<i16>]>) -> Option<i32> {
334 if let Some(vector) = vector {
335 vector
336 .get(value as usize >> 1)
337 .map(|value| value.get() as i32)
338 } else {
339 Some(value)
340 }
341 }
342 match self {
343 Self::ShortValues(rows, columns, array, vector) => {
344 let left_idx = rows.value(left).unwrap_or_default();
345 let right_idx = columns.value(right).unwrap_or_default();
346 let idx = left_idx as usize + right_idx as usize;
347 let value = array.get(idx).map(|value| value.get() as i32)?;
348 tuple_kern(value, vector)
349 }
350 Self::LongValues(rows, columns, array, vector) => {
351 let left_idx = rows.value(left).unwrap_or_default();
352 let right_idx = columns.value(right).unwrap_or_default();
353 let idx = (left_idx as usize).checked_add(right_idx as usize)?;
354 let value = array.get(idx).map(|value| value.get())?;
355 tuple_kern(value, vector)
356 }
357 }
358 }
359}
360
361#[cfg(feature = "experimental_traverse")]
362impl<'a> SomeRecord<'a> for Subtable<'a> {
363 fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
364 RecordResolver {
365 name: "Subtable",
366 get_field: Box::new(move |idx, _data| match idx {
367 0usize => Some(Field::new("coverage", self.coverage())),
368 1usize => Some(Field::new("tuple_count", self.tuple_count())),
369 _ => None,
370 }),
371 data,
372 }
373 }
374}
375
376#[cfg(test)]
377mod tests {
378 use super::*;
379 use font_test_data::bebuffer::BeBuffer;
380
381 #[test]
382 fn parse_subtable0() {
383 let mut buf = BeBuffer::new();
384 buf = buf.extend([6u32, 0, 0, 0]);
386 let mut pairs = [
388 (0u32, 1u32, -10i32),
389 (2, 4, 22),
390 (0, 3, -6),
391 (8, 2, 500),
392 (10, 1, 42),
393 (9, 12, -1000),
394 ];
395 pairs.sort_by_key(|pair| (pair.0 << 16) | pair.1);
397 for pair in &pairs {
398 buf = buf
399 .push(pair.0 as u16)
400 .push(pair.1 as u16)
401 .push(pair.2 as i16);
402 }
403 let data = buf.to_vec();
404 let subtable0 = Subtable0::read(FontData::new(&data)).unwrap();
405 for pair in pairs {
406 assert_eq!(
407 subtable0.kerning(pair.0.into(), pair.1.into()),
408 Some(pair.2)
409 );
410 }
411 }
412
413 #[test]
414 fn parse_subtable1() {
415 let data = FormatOneFour::One.build_subtable();
416 let subtable1 = Subtable1::read(FontData::new(&data)).unwrap();
417 let values = subtable1
418 .values
419 .iter()
420 .take(ONE_EXPECTED.len())
423 .map(|value| value.get())
424 .collect::<Vec<_>>();
425 assert_eq!(values, &ONE_EXPECTED);
426 }
427
428 #[test]
429 fn parse_subtable2() {
430 let data = FormatTwoSix::Two.build_subtable();
431 let subtable = Subtable2::read(FontData::new(&data)).unwrap();
432 let mut values = vec![];
433 for left in 0u32..4 {
434 for right in 0u32..4 {
435 let Some(kerning) = subtable.kerning(left.into(), right.into()) else {
436 panic!("expected kerning value for {left} and {right}");
437 };
438 values.push(kerning);
439 }
440 }
441 assert_eq!(values, &TWO_SIX_EXPECTED);
442 }
443
444 #[test]
445 fn parse_subtable4_control_points() {
446 let data = FormatOneFour::FourControlPoints.build_subtable();
447 let subtable4 = Subtable4::read(FontData::new(&data)).unwrap();
448 let Subtable4Actions::ControlPoints(action) = &subtable4.actions else {
449 panic!("expected subtable 4 control points action");
450 };
451 let values = action
452 .chunks_exact(2)
453 .take(FOUR_OUTLINE_ANKR_EXPECTED.len())
454 .map(|values| (values[0].get(), values[1].get()))
455 .collect::<Vec<_>>();
456 assert_eq!(values, &FOUR_OUTLINE_ANKR_EXPECTED);
457 }
458
459 #[test]
460 fn parse_subtable4_anchor_points() {
461 let data = FormatOneFour::FourAnchorPoints.build_subtable();
462 let subtable4 = Subtable4::read(FontData::new(&data)).unwrap();
463 let Subtable4Actions::AnchorPoints(action) = &subtable4.actions else {
464 panic!("expected subtable 4 anchor points action");
465 };
466 let values = action
467 .chunks_exact(2)
468 .take(FOUR_OUTLINE_ANKR_EXPECTED.len())
469 .map(|values| (values[0].get(), values[1].get()))
470 .collect::<Vec<_>>();
471 assert_eq!(values, &FOUR_OUTLINE_ANKR_EXPECTED);
472 }
473
474 #[test]
475 fn parse_subtable4_coords() {
476 let data = FormatOneFour::FourCoords.build_subtable();
477 let subtable4 = Subtable4::read(FontData::new(&data)).unwrap();
478 let Subtable4Actions::ControlPointCoords(action) = &subtable4.actions else {
479 panic!("expected subtable 4 coords action");
480 };
481 let values = action
482 .chunks_exact(4)
483 .take(FOUR_COORDS_EXPECTED.len())
484 .map(|values| {
485 [
486 values[0].get(),
487 values[1].get(),
488 values[2].get(),
489 values[3].get(),
490 ]
491 })
492 .collect::<Vec<_>>();
493 assert_eq!(values, &FOUR_COORDS_EXPECTED);
494 }
495
496 #[test]
497 fn parse_subtable6_short() {
498 let data = FormatTwoSix::SixShort.build_subtable();
499 let subtable = Subtable6::read_with_args(FontData::new(&data), &0).unwrap();
500 let Subtable6::ShortValues(..) = &subtable else {
501 panic!("expected short values in subtable 6");
502 };
503 check_subtable6(subtable);
504 }
505
506 #[test]
507 fn parse_subtable6_long() {
508 let data = FormatTwoSix::SixLong.build_subtable();
509 let subtable = Subtable6::read_with_args(FontData::new(&data), &0).unwrap();
510 let Subtable6::LongValues(..) = &subtable else {
511 panic!("expected long values in subtable 6");
512 };
513 check_subtable6(subtable);
514 }
515
516 #[test]
517 fn parse_subtable6_long_vector() {
518 let data = FormatTwoSix::SixLongVector.build_subtable();
519 let subtable = Subtable6::read_with_args(FontData::new(&data), &1).unwrap();
520 let Subtable6::LongValues(..) = &subtable else {
521 panic!("expected long values in subtable 6");
522 };
523 check_subtable6(subtable);
524 }
525
526 fn check_subtable6(subtable: Subtable6) {
527 let mut values = vec![];
528 for left in 0u32..4 {
529 for right in 0u32..4 {
530 let Some(kerning) = subtable.kerning(left.into(), right.into()) else {
531 panic!("expected kerning value for {left} and {right}");
532 };
533 values.push(kerning);
534 }
535 }
536 assert_eq!(values, &TWO_SIX_EXPECTED);
537 }
538
539 const ONE_EXPECTED: [i16; 8] = [-40, -20, -10, 0, 10, 20, 40, 80];
541
542 const FOUR_OUTLINE_ANKR_EXPECTED: [(u16, u16); 4] = [(0, 2), (2, 4), (4, 8), (8, 16)];
545
546 const FOUR_COORDS_EXPECTED: [[i16; 4]; 4] = [
548 [-10, 10, -20, 20],
549 [1, 2, 3, 4],
550 [-1, -2, -3, -4],
551 [10, -10, 20, -20],
552 ];
553
554 enum FormatOneFour {
555 One,
556 FourControlPoints,
557 FourAnchorPoints,
558 FourCoords,
559 }
560
561 impl FormatOneFour {
562 fn build_subtable(&self) -> Vec<u8> {
563 let mut flags_offset = ExtendedStateTable::<()>::HEADER_LEN + u32::RAW_BYTE_LEN;
564 match self {
566 Self::FourAnchorPoints => {
567 flags_offset |= 1 << 30;
568 }
569 Self::FourCoords => {
570 flags_offset |= 2 << 30;
571 }
572 _ => {}
573 }
574 let mut buf = BeBuffer::new();
575 buf = buf.push(flags_offset as u32);
576 match self {
578 Self::One => {
579 buf = buf.extend(ONE_EXPECTED);
580 }
581 Self::FourControlPoints | Self::FourAnchorPoints => {
582 for indices in FOUR_OUTLINE_ANKR_EXPECTED {
583 buf = buf.push(indices.0).push(indices.1);
584 }
585 }
586 Self::FourCoords => {
587 for coords in FOUR_COORDS_EXPECTED {
588 buf = buf.extend(coords);
589 }
590 }
591 }
592 let payload = buf.to_vec();
593 let payload_len = payload.len() as u32;
594 #[rustfmt::skip]
595 let header = [
596 6_u32, payload_len + 16, payload_len + 52, payload_len + 88, ];
601 #[rustfmt::skip]
602 let class_table = [
603 6_u16, 4, 5, 16, 2, 0, 50, 4, 51, 4, 80, 5, 201, 4, 202, 4, !0, !0
615 ];
616 #[rustfmt::skip]
617 let state_array: [u16; 18] = [
618 0, 0, 0, 0, 0, 1,
619 0, 0, 0, 0, 0, 1,
620 0, 0, 0, 0, 2, 1,
621 ];
622 #[rustfmt::skip]
623 let entry_table: [u16; 9] = [
624 0, 0, 1,
625 2, 0, 2,
626 0, 0, 3,
627 ];
628 BeBuffer::new()
629 .extend(header)
630 .extend(payload)
631 .extend(class_table)
632 .extend(state_array)
633 .extend(entry_table)
634 .to_vec()
635 }
636 }
637
638 const TWO_SIX_EXPECTED: [i32; 16] =
639 [0i32, 10, 20, 0, 8, 4, -2, 8, 30, -10, -20, 30, 8, 4, -2, 8];
640
641 enum FormatTwoSix {
642 Two,
643 SixShort,
644 SixLong,
645 SixLongVector,
646 }
647
648 impl FormatTwoSix {
649 fn is_long(&self) -> bool {
650 matches!(self, Self::SixLong | Self::SixLongVector)
651 }
652
653 fn is_six(&self) -> bool {
654 !matches!(self, Self::Two)
655 }
656
657 fn has_kerning_vector(&self) -> bool {
658 matches!(self, Self::SixLongVector)
659 }
660
661 fn build_subtable(&self) -> Vec<u8> {
663 let mut buf = BeBuffer::new();
664 let row_count = 3u32;
665 let column_count = 3u32;
666 let is_long = self.is_long();
667 let has_kerning_vector = self.has_kerning_vector();
668 if self.is_six() {
669 buf = buf
671 .push(if is_long { 1u32 } else { 0u32 })
672 .push(row_count as u16)
673 .push(column_count as u16);
674 } else {
675 buf = buf.push(row_count);
677 }
678 #[allow(clippy::erasing_op, clippy::identity_op)]
685 let row_table = build_lookup(
686 &[
687 0 * column_count,
688 2 * column_count,
689 1 * column_count,
690 2 * column_count,
691 ],
692 is_long,
693 );
694 let column_table = build_lookup(&[0, 1, 2, 0], is_long);
695 let kerning_array = [0i32, 10, 20, 30, -10, -20, 8, 4, -2];
697 let mut offset =
698 Subtable::HEADER_LEN + u32::RAW_BYTE_LEN * if self.is_six() { 5 } else { 4 };
699 if has_kerning_vector {
700 offset += 4;
702 }
703 buf = buf.push(offset as u32);
705 offset += row_table.len();
706 buf = buf.push(offset as u32);
708 offset += column_table.len();
709 buf = buf.push(offset as u32);
711 if has_kerning_vector {
712 offset += 9 * 4;
714 buf = buf.push(offset as u32);
716 buf = buf.extend(row_table);
717 buf = buf.extend(column_table);
718 let offsets: [u32; 9] = core::array::from_fn(|idx| idx as u32 * 2);
720 buf = buf.extend(offsets);
721 for value in &kerning_array {
723 buf = buf.push(*value as i16);
724 }
725 } else {
726 buf = buf.extend(row_table);
727 buf = buf.extend(column_table);
728 if is_long {
729 buf = buf.extend(kerning_array);
730 } else {
731 for value in &kerning_array {
732 buf = buf.push(*value as i16);
733 }
734 }
735 }
736 buf.to_vec()
737 }
738 }
739
740 fn build_lookup(values: &[u32], is_long: bool) -> Vec<u8> {
745 let mut buf = BeBuffer::new();
746 buf = buf.push(0u16);
748 for value in values {
749 if is_long {
750 buf = buf.push(*value);
751 } else {
752 buf = buf.push(*value as u16);
753 }
754 }
755 buf.to_vec()
756 }
757}