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> FontRead<'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(data, tuple_count)?)),
99 _ => Err(ReadError::InvalidFormat(format as _)),
100 }
101 }
102}
103
104impl Subtable0<'_> {
105 pub fn kerning(&self, left: GlyphId, right: GlyphId) -> Option<i32> {
107 pair_kerning(self.pairs(), left, right)
108 }
109}
110
111pub(crate) fn pair_kerning(pairs: &[Subtable0Pair], left: GlyphId, right: GlyphId) -> Option<i32> {
112 let left: GlyphId16 = left.try_into().ok()?;
113 let right: GlyphId16 = right.try_into().ok()?;
114 fn make_key(left: GlyphId16, right: GlyphId16) -> u32 {
115 (left.to_u32() << 16) | right.to_u32()
116 }
117 let idx = pairs
118 .binary_search_by_key(&make_key(left, right), |pair| {
119 make_key(pair.left(), pair.right())
120 })
121 .ok()?;
122 pairs.get(idx).map(|pair| pair.value() as i32)
123}
124
125#[derive(Clone)]
127pub struct Subtable1<'a> {
128 pub state_table: ExtendedStateTable<'a, BigEndian<u16>>,
129 pub values: &'a [BigEndian<i16>],
131}
132
133impl ReadArgs for Subtable1<'_> {
134 type Args = ();
135}
136
137impl<'a> FontRead<'a> for Subtable1<'a> {
138 fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
139 let state_table = ExtendedStateTable::read(data)?;
140 let mut cursor = data.cursor();
141 cursor.advance_by(ExtendedStateTable::<()>::HEADER_LEN);
142 let values_offset = cursor.read::<u32>()? as usize;
143 let values = super::aat::safe_read_array_to_end(&data, values_offset)?;
144 Ok(Self {
145 state_table,
146 values,
147 })
148 }
149}
150
151#[derive(Clone)]
153pub struct Subtable2<'a> {
154 pub data: FontData<'a>,
155 pub left_offset_table: LookupU16<'a>,
157 pub right_offset_table: LookupU16<'a>,
159 pub array: &'a [BigEndian<i16>],
161}
162
163impl ReadArgs for Subtable2<'_> {
164 type Args = ();
165}
166
167impl<'a> FontRead<'a> for Subtable2<'a> {
168 fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
169 let mut cursor = data.cursor();
170 cursor.advance_by(u32::RAW_BYTE_LEN);
172 let left_offset = (cursor.read::<u32>()? as usize)
176 .checked_sub(Subtable::HEADER_LEN)
177 .ok_or(ReadError::OutOfBounds)?;
178 let right_offset = (cursor.read::<u32>()? as usize)
179 .checked_sub(Subtable::HEADER_LEN)
180 .ok_or(ReadError::OutOfBounds)?;
181 let array_offset = (cursor.read::<u32>()? as usize)
182 .checked_sub(Subtable::HEADER_LEN)
183 .ok_or(ReadError::OutOfBounds)?;
184 let left_offset_table =
185 LookupU16::read(data.slice(left_offset..).ok_or(ReadError::OutOfBounds)?)?;
186 let right_offset_table =
187 LookupU16::read(data.slice(right_offset..).ok_or(ReadError::OutOfBounds)?)?;
188 let array = safe_read_array_to_end(&data, array_offset)?;
189 Ok(Self {
190 data,
191 left_offset_table,
192 right_offset_table,
193 array,
194 })
195 }
196}
197
198impl Subtable2<'_> {
199 pub fn kerning(&self, left: GlyphId, right: GlyphId) -> Option<i32> {
201 let left: u16 = left.to_u32().try_into().ok()?;
202 let right: u16 = right.to_u32().try_into().ok()?;
203 let left_idx = self.left_offset_table.value(left).unwrap_or(0) as usize;
204 let right_idx = self.right_offset_table.value(right).unwrap_or(0) as usize;
205 self.array
206 .get(left_idx + right_idx)
207 .map(|value| value.get() as i32)
208 }
209}
210
211#[derive(Clone)]
213pub struct Subtable4<'a> {
214 pub state_table: ExtendedStateTable<'a, BigEndian<u16>>,
215 pub flags: u32,
217 pub actions: Subtable4Actions<'a>,
218}
219
220impl ReadArgs for Subtable4<'_> {
221 type Args = ();
222}
223
224impl<'a> FontRead<'a> for Subtable4<'a> {
225 fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
226 let state_table = ExtendedStateTable::read(data)?;
227 let mut cursor = data.cursor();
228 cursor.advance_by(ExtendedStateTable::<()>::HEADER_LEN);
229 let flags = cursor.read::<u32>()?;
230 let action_type = (flags & 0xC0000000) >> 30;
231 let offset = (flags & 0x00FFFFFF) as usize;
232 let actions = match action_type {
233 0 => Subtable4Actions::ControlPoints(safe_read_array_to_end(&data, offset)?),
234 1 => Subtable4Actions::AnchorPoints(safe_read_array_to_end(&data, offset)?),
235 2 => Subtable4Actions::ControlPointCoords(safe_read_array_to_end(&data, offset)?),
236 _ => {
237 return Err(ReadError::MalformedData(
238 "invalid action type in kerx subtable 4",
239 ))
240 }
241 };
242 Ok(Self {
243 state_table,
244 flags,
245 actions,
246 })
247 }
248}
249
250#[derive(Clone)]
252pub enum Subtable4Actions<'a> {
253 ControlPoints(&'a [BigEndian<u16>]),
255 AnchorPoints(&'a [BigEndian<u16>]),
257 ControlPointCoords(&'a [BigEndian<i16>]),
259}
260
261#[derive(Clone)]
263pub enum Subtable6<'a> {
264 ShortValues(
265 LookupU16<'a>,
266 LookupU16<'a>,
267 &'a [BigEndian<i16>],
268 Option<&'a [BigEndian<i16>]>,
269 ),
270 LongValues(
271 LookupU32<'a>,
272 LookupU32<'a>,
273 &'a [BigEndian<i32>],
274 Option<&'a [BigEndian<i16>]>,
275 ),
276}
277
278impl ReadArgs for Subtable6<'_> {
279 type Args = u32;
280}
281
282impl<'a> FontRead<'a> for Subtable6<'a> {
283 fn read_with_args(data: FontData<'a>, args: Self::Args) -> Result<Self, ReadError> {
284 let tuple_count = args;
285 let mut cursor = data.cursor();
286 let flags = cursor.read::<u32>()?;
287 cursor.advance_by(u16::RAW_BYTE_LEN * 2);
289 let row_index_table_offset = (cursor.read::<u32>()? as usize)
291 .checked_sub(Subtable::HEADER_LEN)
292 .ok_or(ReadError::OutOfBounds)?;
293 let column_index_table_offset = (cursor.read::<u32>()? as usize)
294 .checked_sub(Subtable::HEADER_LEN)
295 .ok_or(ReadError::OutOfBounds)?;
296 let kerning_array_offset = (cursor.read::<u32>()? as usize)
297 .checked_sub(Subtable::HEADER_LEN)
298 .ok_or(ReadError::OutOfBounds)?;
299 let kerning_vector = if tuple_count != 0 {
300 let kerning_vector_offset = (cursor.read::<u32>()? as usize)
301 .checked_sub(Subtable::HEADER_LEN)
302 .ok_or(ReadError::OutOfBounds)?;
303 Some(safe_read_array_to_end(&data, kerning_vector_offset)?)
304 } else {
305 None
306 };
307 let row_data = data
308 .slice(row_index_table_offset..)
309 .ok_or(ReadError::OutOfBounds)?;
310 let column_data = data
311 .slice(column_index_table_offset..)
312 .ok_or(ReadError::OutOfBounds)?;
313 if flags & 1 == 0 {
314 let rows = LookupU16::read(row_data)?;
315 let columns = LookupU16::read(column_data)?;
316 let kerning_array = safe_read_array_to_end(&data, kerning_array_offset)?;
317 Ok(Self::ShortValues(
318 rows,
319 columns,
320 kerning_array,
321 kerning_vector,
322 ))
323 } else {
324 let rows = LookupU32::read(row_data)?;
325 let columns = LookupU32::read(column_data)?;
326 let kerning_array = safe_read_array_to_end(&data, kerning_array_offset)?;
327 Ok(Self::LongValues(
328 rows,
329 columns,
330 kerning_array,
331 kerning_vector,
332 ))
333 }
334 }
335}
336
337impl Subtable6<'_> {
338 pub fn kerning(&self, left: GlyphId, right: GlyphId) -> Option<i32> {
340 let left: u16 = left.to_u32().try_into().ok()?;
341 let right: u16 = right.to_u32().try_into().ok()?;
342 fn tuple_kern(value: i32, vector: &Option<&[BigEndian<i16>]>) -> Option<i32> {
343 if let Some(vector) = vector {
344 vector
345 .get(value as usize >> 1)
346 .map(|value| value.get() as i32)
347 } else {
348 Some(value)
349 }
350 }
351 match self {
352 Self::ShortValues(rows, columns, array, vector) => {
353 let left_idx = rows.value(left).unwrap_or_default();
354 let right_idx = columns.value(right).unwrap_or_default();
355 let idx = left_idx as usize + right_idx as usize;
356 let value = array.get(idx).map(|value| value.get() as i32)?;
357 tuple_kern(value, vector)
358 }
359 Self::LongValues(rows, columns, array, vector) => {
360 let left_idx = rows.value(left).unwrap_or_default();
361 let right_idx = columns.value(right).unwrap_or_default();
362 let idx = (left_idx as usize).checked_add(right_idx as usize)?;
363 let value = array.get(idx).map(|value| value.get())?;
364 tuple_kern(value, vector)
365 }
366 }
367 }
368}
369
370#[cfg(feature = "experimental_traverse")]
371impl<'a> SomeRecord<'a> for Subtable<'a> {
372 fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
373 RecordResolver {
374 name: "Subtable",
375 get_field: Box::new(move |idx, _data| match idx {
376 0usize => Some(Field::new("coverage", self.coverage())),
377 1usize => Some(Field::new("tuple_count", self.tuple_count())),
378 _ => None,
379 }),
380 data,
381 }
382 }
383}
384
385#[cfg(test)]
386mod tests {
387 use super::*;
388 use font_test_data::bebuffer::BeBuffer;
389
390 #[test]
391 fn parse_subtable0() {
392 let mut buf = BeBuffer::new();
393 buf = buf.extend([6u32, 0, 0, 0]);
395 let mut pairs = [
397 (0u32, 1u32, -10i32),
398 (2, 4, 22),
399 (0, 3, -6),
400 (8, 2, 500),
401 (10, 1, 42),
402 (9, 12, -1000),
403 ];
404 pairs.sort_by_key(|pair| (pair.0 << 16) | pair.1);
406 for pair in &pairs {
407 buf = buf
408 .push(pair.0 as u16)
409 .push(pair.1 as u16)
410 .push(pair.2 as i16);
411 }
412 let data = buf.to_vec();
413 let subtable0 = Subtable0::read(FontData::new(&data)).unwrap();
414 for pair in pairs {
415 assert_eq!(
416 subtable0.kerning(pair.0.into(), pair.1.into()),
417 Some(pair.2)
418 );
419 }
420 }
421
422 #[test]
423 fn parse_subtable1() {
424 let data = FormatOneFour::One.build_subtable();
425 let subtable1 = Subtable1::read(FontData::new(&data)).unwrap();
426 let values = subtable1
427 .values
428 .iter()
429 .take(ONE_EXPECTED.len())
432 .map(|value| value.get())
433 .collect::<Vec<_>>();
434 assert_eq!(values, &ONE_EXPECTED);
435 }
436
437 #[test]
438 fn parse_subtable2() {
439 let data = FormatTwoSix::Two.build_subtable();
440 let subtable = Subtable2::read(FontData::new(&data)).unwrap();
441 let mut values = vec![];
442 for left in 0u32..4 {
443 for right in 0u32..4 {
444 let Some(kerning) = subtable.kerning(left.into(), right.into()) else {
445 panic!("expected kerning value for {left} and {right}");
446 };
447 values.push(kerning);
448 }
449 }
450 assert_eq!(values, &TWO_SIX_EXPECTED);
451 }
452
453 #[test]
454 fn parse_subtable4_control_points() {
455 let data = FormatOneFour::FourControlPoints.build_subtable();
456 let subtable4 = Subtable4::read(FontData::new(&data)).unwrap();
457 let Subtable4Actions::ControlPoints(action) = &subtable4.actions else {
458 panic!("expected subtable 4 control points action");
459 };
460 let values = action
461 .chunks_exact(2)
462 .take(FOUR_OUTLINE_ANKR_EXPECTED.len())
463 .map(|values| (values[0].get(), values[1].get()))
464 .collect::<Vec<_>>();
465 assert_eq!(values, &FOUR_OUTLINE_ANKR_EXPECTED);
466 }
467
468 #[test]
469 fn parse_subtable4_anchor_points() {
470 let data = FormatOneFour::FourAnchorPoints.build_subtable();
471 let subtable4 = Subtable4::read(FontData::new(&data)).unwrap();
472 let Subtable4Actions::AnchorPoints(action) = &subtable4.actions else {
473 panic!("expected subtable 4 anchor points action");
474 };
475 let values = action
476 .chunks_exact(2)
477 .take(FOUR_OUTLINE_ANKR_EXPECTED.len())
478 .map(|values| (values[0].get(), values[1].get()))
479 .collect::<Vec<_>>();
480 assert_eq!(values, &FOUR_OUTLINE_ANKR_EXPECTED);
481 }
482
483 #[test]
484 fn parse_subtable4_coords() {
485 let data = FormatOneFour::FourCoords.build_subtable();
486 let subtable4 = Subtable4::read(FontData::new(&data)).unwrap();
487 let Subtable4Actions::ControlPointCoords(action) = &subtable4.actions else {
488 panic!("expected subtable 4 coords action");
489 };
490 let values = action
491 .chunks_exact(4)
492 .take(FOUR_COORDS_EXPECTED.len())
493 .map(|values| {
494 [
495 values[0].get(),
496 values[1].get(),
497 values[2].get(),
498 values[3].get(),
499 ]
500 })
501 .collect::<Vec<_>>();
502 assert_eq!(values, &FOUR_COORDS_EXPECTED);
503 }
504
505 #[test]
506 fn parse_subtable6_short() {
507 let data = FormatTwoSix::SixShort.build_subtable();
508 let subtable = Subtable6::read_with_args(FontData::new(&data), 0).unwrap();
509 let Subtable6::ShortValues(..) = &subtable else {
510 panic!("expected short values in subtable 6");
511 };
512 check_subtable6(subtable);
513 }
514
515 #[test]
516 fn parse_subtable6_long() {
517 let data = FormatTwoSix::SixLong.build_subtable();
518 let subtable = Subtable6::read_with_args(FontData::new(&data), 0).unwrap();
519 let Subtable6::LongValues(..) = &subtable else {
520 panic!("expected long values in subtable 6");
521 };
522 check_subtable6(subtable);
523 }
524
525 #[test]
526 fn parse_subtable6_long_vector() {
527 let data = FormatTwoSix::SixLongVector.build_subtable();
528 let subtable = Subtable6::read_with_args(FontData::new(&data), 1).unwrap();
529 let Subtable6::LongValues(..) = &subtable else {
530 panic!("expected long values in subtable 6");
531 };
532 check_subtable6(subtable);
533 }
534
535 fn check_subtable6(subtable: Subtable6) {
536 let mut values = vec![];
537 for left in 0u32..4 {
538 for right in 0u32..4 {
539 let Some(kerning) = subtable.kerning(left.into(), right.into()) else {
540 panic!("expected kerning value for {left} and {right}");
541 };
542 values.push(kerning);
543 }
544 }
545 assert_eq!(values, &TWO_SIX_EXPECTED);
546 }
547
548 const ONE_EXPECTED: [i16; 8] = [-40, -20, -10, 0, 10, 20, 40, 80];
550
551 const FOUR_OUTLINE_ANKR_EXPECTED: [(u16, u16); 4] = [(0, 2), (2, 4), (4, 8), (8, 16)];
554
555 const FOUR_COORDS_EXPECTED: [[i16; 4]; 4] = [
557 [-10, 10, -20, 20],
558 [1, 2, 3, 4],
559 [-1, -2, -3, -4],
560 [10, -10, 20, -20],
561 ];
562
563 enum FormatOneFour {
564 One,
565 FourControlPoints,
566 FourAnchorPoints,
567 FourCoords,
568 }
569
570 impl FormatOneFour {
571 fn build_subtable(&self) -> Vec<u8> {
572 let mut flags_offset = ExtendedStateTable::<()>::HEADER_LEN + u32::RAW_BYTE_LEN;
573 match self {
575 Self::FourAnchorPoints => {
576 flags_offset |= 1 << 30;
577 }
578 Self::FourCoords => {
579 flags_offset |= 2 << 30;
580 }
581 _ => {}
582 }
583 let mut buf = BeBuffer::new();
584 buf = buf.push(flags_offset as u32);
585 match self {
587 Self::One => {
588 buf = buf.extend(ONE_EXPECTED);
589 }
590 Self::FourControlPoints | Self::FourAnchorPoints => {
591 for indices in FOUR_OUTLINE_ANKR_EXPECTED {
592 buf = buf.push(indices.0).push(indices.1);
593 }
594 }
595 Self::FourCoords => {
596 for coords in FOUR_COORDS_EXPECTED {
597 buf = buf.extend(coords);
598 }
599 }
600 }
601 let payload = buf.to_vec();
602 let payload_len = payload.len() as u32;
603 #[rustfmt::skip]
604 let header = [
605 6_u32, payload_len + 16, payload_len + 52, payload_len + 88, ];
610 #[rustfmt::skip]
611 let class_table = [
612 6_u16, 4, 5, 16, 2, 0, 50, 4, 51, 4, 80, 5, 201, 4, 202, 4, !0, !0
624 ];
625 #[rustfmt::skip]
626 let state_array: [u16; 18] = [
627 0, 0, 0, 0, 0, 1,
628 0, 0, 0, 0, 0, 1,
629 0, 0, 0, 0, 2, 1,
630 ];
631 #[rustfmt::skip]
632 let entry_table: [u16; 9] = [
633 0, 0, 1,
634 2, 0, 2,
635 0, 0, 3,
636 ];
637 BeBuffer::new()
638 .extend(header)
639 .extend(payload)
640 .extend(class_table)
641 .extend(state_array)
642 .extend(entry_table)
643 .to_vec()
644 }
645 }
646
647 const TWO_SIX_EXPECTED: [i32; 16] =
648 [0i32, 10, 20, 0, 8, 4, -2, 8, 30, -10, -20, 30, 8, 4, -2, 8];
649
650 enum FormatTwoSix {
651 Two,
652 SixShort,
653 SixLong,
654 SixLongVector,
655 }
656
657 impl FormatTwoSix {
658 fn is_long(&self) -> bool {
659 matches!(self, Self::SixLong | Self::SixLongVector)
660 }
661
662 fn is_six(&self) -> bool {
663 !matches!(self, Self::Two)
664 }
665
666 fn has_kerning_vector(&self) -> bool {
667 matches!(self, Self::SixLongVector)
668 }
669
670 fn build_subtable(&self) -> Vec<u8> {
672 let mut buf = BeBuffer::new();
673 let row_count = 3u32;
674 let column_count = 3u32;
675 let is_long = self.is_long();
676 let has_kerning_vector = self.has_kerning_vector();
677 if self.is_six() {
678 buf = buf
680 .push(if is_long { 1u32 } else { 0u32 })
681 .push(row_count as u16)
682 .push(column_count as u16);
683 } else {
684 buf = buf.push(row_count);
686 }
687 #[allow(clippy::erasing_op, clippy::identity_op)]
694 let row_table = build_lookup(
695 &[
696 0 * column_count,
697 2 * column_count,
698 1 * column_count,
699 2 * column_count,
700 ],
701 is_long,
702 );
703 let column_table = build_lookup(&[0, 1, 2, 0], is_long);
704 let kerning_array = [0i32, 10, 20, 30, -10, -20, 8, 4, -2];
706 let mut offset =
707 Subtable::HEADER_LEN + u32::RAW_BYTE_LEN * if self.is_six() { 5 } else { 4 };
708 if has_kerning_vector {
709 offset += 4;
711 }
712 buf = buf.push(offset as u32);
714 offset += row_table.len();
715 buf = buf.push(offset as u32);
717 offset += column_table.len();
718 buf = buf.push(offset as u32);
720 if has_kerning_vector {
721 offset += 9 * 4;
723 buf = buf.push(offset as u32);
725 buf = buf.extend(row_table);
726 buf = buf.extend(column_table);
727 let offsets: [u32; 9] = core::array::from_fn(|idx| idx as u32 * 2);
729 buf = buf.extend(offsets);
730 for value in &kerning_array {
732 buf = buf.push(*value as i16);
733 }
734 } else {
735 buf = buf.extend(row_table);
736 buf = buf.extend(column_table);
737 if is_long {
738 buf = buf.extend(kerning_array);
739 } else {
740 for value in &kerning_array {
741 buf = buf.push(*value as i16);
742 }
743 }
744 }
745 buf.to_vec()
746 }
747 }
748
749 fn build_lookup(values: &[u32], is_long: bool) -> Vec<u8> {
754 let mut buf = BeBuffer::new();
755 buf = buf.push(0u16);
757 for value in values {
758 if is_long {
759 buf = buf.push(*value);
760 } else {
761 buf = buf.push(*value as u16);
762 }
763 }
764 buf.to_vec()
765 }
766}