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(FontData::new(self.data()), &self.coverage())
66 }
67}
68
69#[derive(Clone)]
71pub enum SubtableKind<'a> {
72 Format0(Subtable0<'a>),
73 Format1(Subtable1<'a>),
74 Format2(Subtable2<'a>),
75 Format4(Subtable4<'a>),
76 Format6(Subtable6<'a>),
77}
78
79impl ReadArgs for SubtableKind<'_> {
80 type Args = u32;
81}
82
83impl<'a> FontReadWithArgs<'a> for SubtableKind<'a> {
84 fn read_with_args(data: FontData<'a>, args: &Self::Args) -> Result<Self, ReadError> {
85 let format = *args & 0xFF;
87 match format {
88 0 => Ok(Self::Format0(Subtable0::read(data)?)),
89 1 => Ok(Self::Format1(Subtable1::read(data)?)),
90 2 => Ok(Self::Format2(Subtable2::read(data)?)),
91 4 => Ok(Self::Format4(Subtable4::read(data)?)),
93 6 => Ok(Self::Format6(Subtable6::read(data)?)),
95 _ => Err(ReadError::InvalidFormat(format as _)),
96 }
97 }
98}
99
100impl Subtable0<'_> {
101 pub fn kerning(&self, left: GlyphId, right: GlyphId) -> Option<i32> {
103 let left: GlyphId16 = left.try_into().ok()?;
104 let right: GlyphId16 = right.try_into().ok()?;
105 fn make_key(left: GlyphId16, right: GlyphId16) -> u32 {
106 (left.to_u32() << 16) | right.to_u32()
107 }
108 let pairs = self.pairs();
109 let idx = pairs
110 .binary_search_by_key(&make_key(left, right), |pair| {
111 make_key(pair.left(), pair.right())
112 })
113 .ok()?;
114 pairs.get(idx).map(|pair| pair.value() as i32)
115 }
116}
117
118#[derive(Clone)]
120pub struct Subtable1<'a> {
121 pub state_table: ExtendedStateTable<'a, BigEndian<u16>>,
122 pub values: &'a [BigEndian<i16>],
124}
125
126impl<'a> FontRead<'a> for Subtable1<'a> {
127 fn read(data: FontData<'a>) -> Result<Self, ReadError> {
128 let state_table = ExtendedStateTable::read(data)?;
129 let mut cursor = data.cursor();
130 cursor.advance_by(ExtendedStateTable::<()>::HEADER_LEN);
131 let values_offset = cursor.read::<u32>()? as usize;
132 let values = super::aat::safe_read_array_to_end(&data, values_offset)?;
133 Ok(Self {
134 state_table,
135 values,
136 })
137 }
138}
139
140#[derive(Clone)]
142pub struct Subtable2<'a> {
143 pub data: FontData<'a>,
144 pub left_offset_table: LookupU16<'a>,
146 pub right_offset_table: LookupU16<'a>,
148 pub array: &'a [BigEndian<i16>],
150}
151
152impl<'a> FontRead<'a> for Subtable2<'a> {
153 fn read(data: FontData<'a>) -> Result<Self, ReadError> {
154 let mut cursor = data.cursor();
155 cursor.advance_by(u32::RAW_BYTE_LEN);
157 let left_offset = (cursor.read::<u32>()? as usize)
161 .checked_sub(Subtable::HEADER_LEN)
162 .ok_or(ReadError::OutOfBounds)?;
163 let right_offset = (cursor.read::<u32>()? as usize)
164 .checked_sub(Subtable::HEADER_LEN)
165 .ok_or(ReadError::OutOfBounds)?;
166 let array_offset = (cursor.read::<u32>()? as usize)
167 .checked_sub(Subtable::HEADER_LEN)
168 .ok_or(ReadError::OutOfBounds)?;
169 let left_offset_table =
170 LookupU16::read(data.slice(left_offset..).ok_or(ReadError::OutOfBounds)?)?;
171 let right_offset_table =
172 LookupU16::read(data.slice(right_offset..).ok_or(ReadError::OutOfBounds)?)?;
173 let array = safe_read_array_to_end(&data, array_offset)?;
174 Ok(Self {
175 data,
176 left_offset_table,
177 right_offset_table,
178 array,
179 })
180 }
181}
182
183impl Subtable2<'_> {
184 pub fn kerning(&self, left: GlyphId, right: GlyphId) -> Option<i32> {
186 let left: u16 = left.to_u32().try_into().ok()?;
187 let right: u16 = right.to_u32().try_into().ok()?;
188 let left_idx = self.left_offset_table.value(left).unwrap_or(0) as usize;
189 let right_idx = self.right_offset_table.value(right).unwrap_or(0) as usize;
190 self.array
191 .get(left_idx + right_idx)
192 .map(|value| value.get() as i32)
193 }
194}
195
196#[derive(Clone)]
198pub struct Subtable4<'a> {
199 pub state_table: ExtendedStateTable<'a, BigEndian<u16>>,
200 pub flags: u32,
202 pub actions: Subtable4Actions<'a>,
203}
204
205impl<'a> FontRead<'a> for Subtable4<'a> {
206 fn read(data: FontData<'a>) -> Result<Self, ReadError> {
207 let state_table = ExtendedStateTable::read(data)?;
208 let mut cursor = data.cursor();
209 cursor.advance_by(ExtendedStateTable::<()>::HEADER_LEN);
210 let flags = cursor.read::<u32>()?;
211 let action_type = (flags & 0xC0000000) >> 30;
212 let offset = (flags & 0x00FFFFFF) as usize;
213 let actions = match action_type {
214 0 => Subtable4Actions::ControlPoints(safe_read_array_to_end(&data, offset)?),
215 1 => Subtable4Actions::AnchorPoints(safe_read_array_to_end(&data, offset)?),
216 2 => Subtable4Actions::ControlPointCoords(safe_read_array_to_end(&data, offset)?),
217 _ => {
218 return Err(ReadError::MalformedData(
219 "invalid action type in kerx subtable 4",
220 ))
221 }
222 };
223 Ok(Self {
224 state_table,
225 flags,
226 actions,
227 })
228 }
229}
230
231#[derive(Clone)]
233pub enum Subtable4Actions<'a> {
234 ControlPoints(&'a [BigEndian<u16>]),
236 AnchorPoints(&'a [BigEndian<u16>]),
238 ControlPointCoords(&'a [BigEndian<i16>]),
240}
241
242#[derive(Clone)]
244pub enum Subtable6<'a> {
245 ShortValues(LookupU16<'a>, LookupU16<'a>, &'a [BigEndian<i16>]),
246 LongValues(LookupU32<'a>, LookupU32<'a>, &'a [BigEndian<i32>]),
247}
248
249impl<'a> FontRead<'a> for Subtable6<'a> {
250 fn read(data: FontData<'a>) -> Result<Self, ReadError> {
251 let mut cursor = data.cursor();
252 let flags = cursor.read::<u32>()?;
253 cursor.advance_by(u16::RAW_BYTE_LEN * 2);
255 let row_index_table_offset = (cursor.read::<u32>()? as usize)
257 .checked_sub(Subtable::HEADER_LEN)
258 .ok_or(ReadError::OutOfBounds)?;
259 let column_index_table_offset = (cursor.read::<u32>()? as usize)
260 .checked_sub(Subtable::HEADER_LEN)
261 .ok_or(ReadError::OutOfBounds)?;
262 let kerning_array_offset = (cursor.read::<u32>()? as usize)
263 .checked_sub(Subtable::HEADER_LEN)
264 .ok_or(ReadError::OutOfBounds)?;
265 let row_data = data
266 .slice(row_index_table_offset..)
267 .ok_or(ReadError::OutOfBounds)?;
268 let column_data = data
269 .slice(column_index_table_offset..)
270 .ok_or(ReadError::OutOfBounds)?;
271 if flags & 1 == 0 {
272 let rows = LookupU16::read(row_data)?;
273 let columns = LookupU16::read(column_data)?;
274 let kerning_array = safe_read_array_to_end(&data, kerning_array_offset)?;
275 Ok(Self::ShortValues(rows, columns, kerning_array))
276 } else {
277 let rows = LookupU32::read(row_data)?;
278 let columns = LookupU32::read(column_data)?;
279 let kerning_array = safe_read_array_to_end(&data, kerning_array_offset)?;
280 Ok(Self::LongValues(rows, columns, kerning_array))
281 }
282 }
283}
284
285impl Subtable6<'_> {
286 pub fn kerning(&self, left: GlyphId, right: GlyphId) -> Option<i32> {
288 let left: u16 = left.to_u32().try_into().ok()?;
289 let right: u16 = right.to_u32().try_into().ok()?;
290 match self {
291 Self::ShortValues(rows, columns, array) => {
292 let left_idx = rows.value(left).unwrap_or_default();
293 let right_idx = columns.value(right).unwrap_or_default();
294 let idx = left_idx as usize + right_idx as usize;
295 array.get(idx).map(|value| value.get() as i32)
296 }
297 Self::LongValues(rows, columns, array) => {
298 let left_idx = rows.value(left).unwrap_or_default();
299 let right_idx = columns.value(right).unwrap_or_default();
300 let idx = (left_idx as usize).checked_add(right_idx as usize)?;
301 array.get(idx).map(|value| value.get())
302 }
303 }
304 }
305}
306
307#[cfg(feature = "experimental_traverse")]
308impl<'a> SomeRecord<'a> for Subtable<'a> {
309 fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
310 RecordResolver {
311 name: "Subtable",
312 get_field: Box::new(move |idx, _data| match idx {
313 0usize => Some(Field::new("coverage", self.coverage())),
314 1usize => Some(Field::new("tuple_count", self.tuple_count())),
315 _ => None,
316 }),
317 data,
318 }
319 }
320}
321
322#[cfg(test)]
323mod tests {
324 use super::*;
325 use font_test_data::bebuffer::BeBuffer;
326
327 #[test]
328 fn parse_subtable0() {
329 let mut buf = BeBuffer::new();
330 buf = buf.extend([6u32, 0, 0, 0]);
332 let mut pairs = [
334 (0u32, 1u32, -10i32),
335 (2, 4, 22),
336 (0, 3, -6),
337 (8, 2, 500),
338 (10, 1, 42),
339 (9, 12, -1000),
340 ];
341 pairs.sort_by_key(|pair| (pair.0 << 16) | pair.1);
343 for pair in &pairs {
344 buf = buf
345 .push(pair.0 as u16)
346 .push(pair.1 as u16)
347 .push(pair.2 as i16);
348 }
349 let data = buf.to_vec();
350 let subtable0 = Subtable0::read(FontData::new(&data)).unwrap();
351 for pair in pairs {
352 assert_eq!(
353 subtable0.kerning(pair.0.into(), pair.1.into()),
354 Some(pair.2)
355 );
356 }
357 }
358
359 #[test]
360 fn parse_subtable1() {
361 let data = FormatOneFour::One.build_subtable();
362 let subtable1 = Subtable1::read(FontData::new(&data)).unwrap();
363 let values = subtable1
364 .values
365 .iter()
366 .take(ONE_EXPECTED.len())
369 .map(|value| value.get())
370 .collect::<Vec<_>>();
371 assert_eq!(values, &ONE_EXPECTED);
372 }
373
374 #[test]
375 fn parse_subtable2() {
376 let data = FormatTwoSix::Two.build_subtable();
377 let subtable = Subtable2::read(FontData::new(&data)).unwrap();
378 let mut values = vec![];
379 for left in 0u32..4 {
380 for right in 0u32..4 {
381 let Some(kerning) = subtable.kerning(left.into(), right.into()) else {
382 panic!("expected kerning value for {left} and {right}");
383 };
384 values.push(kerning);
385 }
386 }
387 assert_eq!(values, &TWO_SIX_EXPECTED);
388 }
389
390 #[test]
391 fn parse_subtable4_control_points() {
392 let data = FormatOneFour::FourControlPoints.build_subtable();
393 let subtable4 = Subtable4::read(FontData::new(&data)).unwrap();
394 let Subtable4Actions::ControlPoints(action) = &subtable4.actions else {
395 panic!("expected subtable 4 control points action");
396 };
397 let values = action
398 .chunks_exact(2)
399 .take(FOUR_OUTLINE_ANKR_EXPECTED.len())
400 .map(|values| (values[0].get(), values[1].get()))
401 .collect::<Vec<_>>();
402 assert_eq!(values, &FOUR_OUTLINE_ANKR_EXPECTED);
403 }
404
405 #[test]
406 fn parse_subtable4_anchor_points() {
407 let data = FormatOneFour::FourAnchorPoints.build_subtable();
408 let subtable4 = Subtable4::read(FontData::new(&data)).unwrap();
409 let Subtable4Actions::AnchorPoints(action) = &subtable4.actions else {
410 panic!("expected subtable 4 anchor points action");
411 };
412 let values = action
413 .chunks_exact(2)
414 .take(FOUR_OUTLINE_ANKR_EXPECTED.len())
415 .map(|values| (values[0].get(), values[1].get()))
416 .collect::<Vec<_>>();
417 assert_eq!(values, &FOUR_OUTLINE_ANKR_EXPECTED);
418 }
419
420 #[test]
421 fn parse_subtable4_coords() {
422 let data = FormatOneFour::FourCoords.build_subtable();
423 let subtable4 = Subtable4::read(FontData::new(&data)).unwrap();
424 let Subtable4Actions::ControlPointCoords(action) = &subtable4.actions else {
425 panic!("expected subtable 4 coords action");
426 };
427 let values = action
428 .chunks_exact(4)
429 .take(FOUR_COORDS_EXPECTED.len())
430 .map(|values| {
431 [
432 values[0].get(),
433 values[1].get(),
434 values[2].get(),
435 values[3].get(),
436 ]
437 })
438 .collect::<Vec<_>>();
439 assert_eq!(values, &FOUR_COORDS_EXPECTED);
440 }
441
442 #[test]
443 fn parse_subtable6_short() {
444 let data = FormatTwoSix::SixShort.build_subtable();
445 let subtable = Subtable6::read(FontData::new(&data)).unwrap();
446 let Subtable6::ShortValues(..) = &subtable else {
447 panic!("expected short values in subtable 6");
448 };
449 check_subtable6(subtable);
450 }
451
452 #[test]
453 fn parse_subtable6_long() {
454 let data = FormatTwoSix::SixLong.build_subtable();
455 let subtable = Subtable6::read(FontData::new(&data)).unwrap();
456 let Subtable6::LongValues(..) = &subtable else {
457 panic!("expected long values in subtable 6");
458 };
459 check_subtable6(subtable);
460 }
461
462 fn check_subtable6(subtable: Subtable6) {
463 let mut values = vec![];
464 for left in 0u32..4 {
465 for right in 0u32..4 {
466 let Some(kerning) = subtable.kerning(left.into(), right.into()) else {
467 panic!("expected kerning value for {left} and {right}");
468 };
469 values.push(kerning);
470 }
471 }
472 assert_eq!(values, &TWO_SIX_EXPECTED);
473 }
474
475 const ONE_EXPECTED: [i16; 8] = [-40, -20, -10, 0, 10, 20, 40, 80];
477
478 const FOUR_OUTLINE_ANKR_EXPECTED: [(u16, u16); 4] = [(0, 2), (2, 4), (4, 8), (8, 16)];
481
482 const FOUR_COORDS_EXPECTED: [[i16; 4]; 4] = [
484 [-10, 10, -20, 20],
485 [1, 2, 3, 4],
486 [-1, -2, -3, -4],
487 [10, -10, 20, -20],
488 ];
489
490 enum FormatOneFour {
491 One,
492 FourControlPoints,
493 FourAnchorPoints,
494 FourCoords,
495 }
496
497 impl FormatOneFour {
498 fn build_subtable(&self) -> Vec<u8> {
499 let mut flags_offset = ExtendedStateTable::<()>::HEADER_LEN + u32::RAW_BYTE_LEN;
500 match self {
502 Self::FourAnchorPoints => {
503 flags_offset |= 1 << 30;
504 }
505 Self::FourCoords => {
506 flags_offset |= 2 << 30;
507 }
508 _ => {}
509 }
510 let mut buf = BeBuffer::new();
511 buf = buf.push(flags_offset as u32);
512 match self {
514 Self::One => {
515 buf = buf.extend(ONE_EXPECTED);
516 }
517 Self::FourControlPoints | Self::FourAnchorPoints => {
518 for indices in FOUR_OUTLINE_ANKR_EXPECTED {
519 buf = buf.push(indices.0).push(indices.1);
520 }
521 }
522 Self::FourCoords => {
523 for coords in FOUR_COORDS_EXPECTED {
524 buf = buf.extend(coords);
525 }
526 }
527 }
528 let payload = buf.to_vec();
529 let payload_len = payload.len() as u32;
530 #[rustfmt::skip]
531 let header = [
532 6_u32, payload_len + 16, payload_len + 52, payload_len + 88, ];
537 #[rustfmt::skip]
538 let class_table = [
539 6_u16, 4, 5, 16, 2, 0, 50, 4, 51, 4, 80, 5, 201, 4, 202, 4, !0, !0
551 ];
552 #[rustfmt::skip]
553 let state_array: [u16; 18] = [
554 0, 0, 0, 0, 0, 1,
555 0, 0, 0, 0, 0, 1,
556 0, 0, 0, 0, 2, 1,
557 ];
558 #[rustfmt::skip]
559 let entry_table: [u16; 9] = [
560 0, 0, 1,
561 2, 0, 2,
562 0, 0, 3,
563 ];
564 BeBuffer::new()
565 .extend(header)
566 .extend(payload)
567 .extend(class_table)
568 .extend(state_array)
569 .extend(entry_table)
570 .to_vec()
571 }
572 }
573
574 const TWO_SIX_EXPECTED: [i32; 16] =
575 [0i32, 10, 20, 0, 8, 4, -2, 8, 30, -10, -20, 30, 8, 4, -2, 8];
576
577 enum FormatTwoSix {
578 Two,
579 SixShort,
580 SixLong,
581 }
582
583 impl FormatTwoSix {
584 fn is_long(&self) -> bool {
585 matches!(self, Self::SixLong)
586 }
587
588 fn is_six(&self) -> bool {
589 !matches!(self, Self::Two)
590 }
591
592 fn build_subtable(&self) -> Vec<u8> {
594 let mut buf = BeBuffer::new();
595 let row_count = 3u32;
596 let column_count = 3u32;
597 let is_long = self.is_long();
598 if self.is_six() {
599 buf = buf
601 .push(if is_long { 1u32 } else { 0u32 })
602 .push(row_count as u16)
603 .push(column_count as u16);
604 } else {
605 buf = buf.push(row_count);
607 }
608 #[allow(clippy::erasing_op, clippy::identity_op)]
615 let row_table = build_lookup(
616 &[
617 0 * column_count,
618 2 * column_count,
619 1 * column_count,
620 2 * column_count,
621 ],
622 is_long,
623 );
624 let column_table = build_lookup(&[0, 1, 2, 0], is_long);
625 let kerning_array = [0i32, 10, 20, 30, -10, -20, 8, 4, -2];
627 let mut offset =
628 Subtable::HEADER_LEN + u32::RAW_BYTE_LEN * if self.is_six() { 5 } else { 4 };
629 buf = buf.push(offset as u32);
631 offset += row_table.len();
632 buf = buf.push(offset as u32);
634 offset += column_table.len();
635 buf = buf.push(offset as u32);
637 buf = buf.extend(row_table);
638 buf = buf.extend(column_table);
639 if is_long {
640 buf = buf.extend(kerning_array);
641 } else {
642 for value in &kerning_array {
643 buf = buf.push(*value as i16);
644 }
645 }
646 buf.to_vec()
647 }
648 }
649
650 fn build_lookup(values: &[u32], is_long: bool) -> Vec<u8> {
655 let mut buf = BeBuffer::new();
656 buf = buf.push(0u16);
658 for value in values {
659 if is_long {
660 buf = buf.push(*value);
661 } else {
662 buf = buf.push(*value as u16);
663 }
664 }
665 buf.to_vec()
666 }
667}