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