1//! An [Extended Kerning Table](
2//! https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6kerx.html) implementation.
3
4// TODO: find a way to test this table
5// This table is basically untested because it uses Apple's State Tables
6// and I have no idea how to generate them.
7
8use core::num::NonZeroU16;
9
10use crate::kern::KerningPair;
11use crate::parser::{FromData, LazyArray32, NumFrom, Offset, Offset32, Stream};
12use crate::{aat, GlyphId};
13
14const HEADER_SIZE: usize = 12;
15
16/// A format 0 subtable.
17///
18/// Ordered List of Kerning Pairs.
19///
20/// The same as in `kern`, but uses `LazyArray32` instead of `LazyArray16`.
21#[derive(Clone, Copy, Debug)]
22pub struct Subtable0<'a> {
23 /// A list of kerning pairs.
24 pub pairs: LazyArray32<'a, KerningPair>,
25}
26
27impl<'a> Subtable0<'a> {
28 /// Parses a subtable from raw data.
29 fn parse(data: &'a [u8]) -> Option<Self> {
30 let mut s: Stream<'_> = Stream::new(data);
31 let number_of_pairs: u32 = s.read::<u32>()?;
32 s.advance(len:12); // search_range (u32) + entry_selector (u32) + range_shift (u32)
33 let pairs: LazyArray32<'_, KerningPair> = s.read_array32::<KerningPair>(count:number_of_pairs)?;
34 Some(Self { pairs })
35 }
36
37 /// Returns kerning for a pair of glyphs.
38 #[inline]
39 pub fn glyphs_kerning(&self, left: GlyphId, right: GlyphId) -> Option<i16> {
40 let needle: u32 = u32::from(left.0) << 16 | u32::from(right.0);
41 self.pairs
42 .binary_search_by(|v: &KerningPair| v.pair.cmp(&needle))
43 .map(|(_, v: KerningPair)| v.value)
44 }
45}
46
47/// A state machine entry.
48#[derive(Clone, Copy, Debug)]
49pub struct EntryData {
50 /// An action index.
51 pub action_index: u16,
52}
53
54impl FromData for EntryData {
55 const SIZE: usize = 2;
56
57 #[inline]
58 fn parse(data: &[u8]) -> Option<Self> {
59 let mut s: Stream<'_> = Stream::new(data);
60 Some(EntryData {
61 action_index: s.read::<u16>()?,
62 })
63 }
64}
65
66/// A format 1 subtable.
67///
68/// State Table for Contextual Kerning.
69#[derive(Clone)]
70pub struct Subtable1<'a> {
71 /// A state table.
72 pub state_table: aat::ExtendedStateTable<'a, EntryData>,
73 actions_data: &'a [u8],
74}
75
76impl<'a> Subtable1<'a> {
77 fn parse(number_of_glyphs: NonZeroU16, data: &'a [u8]) -> Option<Self> {
78 let mut s: Stream<'_> = Stream::new(data);
79 let state_table: ExtendedStateTable<'_, EntryData> = aat::ExtendedStateTable::parse(number_of_glyphs, &mut s)?;
80
81 // Actions offset is right after the state table.
82 let actions_offset: Offset32 = s.read::<Offset32>()?;
83 // Actions offset is from the start of the state table and not from the start of subtable.
84 // And since we don't know the length of the actions data,
85 // simply store all the data after the offset.
86 let actions_data: &[u8] = data.get(index:actions_offset.to_usize()..)?;
87
88 Some(Subtable1 {
89 state_table,
90 actions_data,
91 })
92 }
93
94 /// Returns kerning at action index.
95 #[inline]
96 pub fn glyphs_kerning(&self, action_index: u16) -> Option<i16> {
97 Stream::read_at(self.actions_data, offset:usize::from(action_index) * i16::SIZE)
98 }
99}
100
101impl<'a> core::ops::Deref for Subtable1<'a> {
102 type Target = aat::ExtendedStateTable<'a, EntryData>;
103
104 fn deref(&self) -> &Self::Target {
105 &self.state_table
106 }
107}
108
109impl core::fmt::Debug for Subtable1<'_> {
110 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
111 write!(f, "Subtable1 {{ ... }}")
112 }
113}
114
115/// A format 2 subtable.
116///
117/// Simple n x m Array of Kerning Values.
118///
119/// The same as in `kern`, but uses 32bit offsets instead of 16bit one.
120#[derive(Clone, Copy)]
121pub struct Subtable2<'a>(&'a [u8]); // TODO: parse actual structure
122
123impl<'a> Subtable2<'a> {
124 /// Returns kerning for a pair of glyphs.
125 pub fn glyphs_kerning(&self, left: GlyphId, right: GlyphId) -> Option<i16> {
126 let mut s = Stream::new(self.0);
127 s.skip::<u32>(); // row_width
128
129 // Offsets are from beginning of the subtable and not from the `data` start,
130 // so we have to subtract the header.
131 let left_hand_table_offset = s.read::<Offset32>()?.to_usize().checked_sub(HEADER_SIZE)?;
132 let right_hand_table_offset = s.read::<Offset32>()?.to_usize().checked_sub(HEADER_SIZE)?;
133 let array_offset = s.read::<Offset32>()?.to_usize().checked_sub(HEADER_SIZE)?;
134
135 // 'The array can be indexed by completing the left-hand and right-hand class mappings,
136 // adding the class values to the address of the subtable,
137 // and fetching the kerning value to which the new address points.'
138
139 let left_class =
140 crate::kern::get_format2_class(left.0, left_hand_table_offset, self.0).unwrap_or(0);
141 let right_class =
142 crate::kern::get_format2_class(right.0, right_hand_table_offset, self.0).unwrap_or(0);
143
144 // 'Values within the left-hand offset table should not be less than the kerning array offset.'
145 if usize::from(left_class) < array_offset {
146 return None;
147 }
148
149 // Classes are already premultiplied, so we only need to sum them.
150 let index = usize::from(left_class) + usize::from(right_class);
151 let value_offset = index.checked_sub(HEADER_SIZE)?;
152 Stream::read_at::<i16>(self.0, value_offset)
153 }
154}
155
156impl core::fmt::Debug for Subtable2<'_> {
157 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
158 write!(f, "Subtable2 {{ ... }}")
159 }
160}
161
162/// A container of Anchor Points used by [`Subtable4`].
163#[derive(Clone, Copy)]
164pub struct AnchorPoints<'a>(&'a [u8]);
165
166impl AnchorPoints<'_> {
167 /// Returns a mark and current anchor points at action index.
168 pub fn get(&self, action_index: u16) -> Option<(u16, u16)> {
169 let offset: usize = usize::from(action_index) * u16::SIZE;
170 let mut s: Stream<'_> = Stream::new_at(self.0, offset)?;
171 Some((s.read::<u16>()?, s.read::<u16>()?))
172 }
173}
174
175impl core::fmt::Debug for AnchorPoints<'_> {
176 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
177 write!(f, "AnchorPoints {{ ... }}")
178 }
179}
180
181/// A format 4 subtable.
182///
183/// State Table for Control Point/Anchor Point Positioning.
184///
185/// Note: I wasn't able to find any fonts that actually use
186/// `ControlPointActions` and/or `ControlPointCoordinateActions`,
187/// therefore only `AnchorPointActions` is supported.
188#[derive(Clone)]
189pub struct Subtable4<'a> {
190 /// A state table.
191 pub state_table: aat::ExtendedStateTable<'a, EntryData>,
192 /// Anchor points.
193 pub anchor_points: AnchorPoints<'a>,
194}
195
196impl<'a> Subtable4<'a> {
197 fn parse(number_of_glyphs: NonZeroU16, data: &'a [u8]) -> Option<Self> {
198 let mut s: Stream<'_> = Stream::new(data);
199 let state_table: ExtendedStateTable<'_, EntryData> = aat::ExtendedStateTable::parse(number_of_glyphs, &mut s)?;
200 let flags: u32 = s.read::<u32>()?;
201 let action_type: u8 = ((flags & 0xC0000000) >> 30) as u8;
202 let points_offset: usize = usize::num_from(flags & 0x00FFFFFF);
203
204 // We support only Anchor Point Actions.
205 if action_type != 1 {
206 return None;
207 }
208
209 Some(Self {
210 state_table,
211 anchor_points: AnchorPoints(data.get(index:points_offset..)?),
212 })
213 }
214}
215
216impl<'a> core::ops::Deref for Subtable4<'a> {
217 type Target = aat::ExtendedStateTable<'a, EntryData>;
218
219 fn deref(&self) -> &Self::Target {
220 &self.state_table
221 }
222}
223
224impl core::fmt::Debug for Subtable4<'_> {
225 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
226 write!(f, "Subtable4 {{ ... }}")
227 }
228}
229
230/// A format 6 subtable.
231///
232/// Simple Index-based n x m Array of Kerning Values.
233#[derive(Clone, Copy)]
234pub struct Subtable6<'a> {
235 data: &'a [u8],
236 number_of_glyphs: NonZeroU16,
237}
238
239impl<'a> Subtable6<'a> {
240 // TODO: parse actual structure
241 fn parse(number_of_glyphs: NonZeroU16, data: &'a [u8]) -> Self {
242 Subtable6 {
243 number_of_glyphs,
244 data,
245 }
246 }
247
248 /// Returns kerning for a pair of glyphs.
249 pub fn glyphs_kerning(&self, left: GlyphId, right: GlyphId) -> Option<i16> {
250 use core::convert::TryFrom;
251
252 let mut s = Stream::new(self.data);
253 let flags = s.read::<u32>()?;
254 s.skip::<u16>(); // row_count
255 s.skip::<u16>(); // col_count
256 // All offsets are from the start of the subtable.
257 let row_index_table_offset = s.read::<Offset32>()?.to_usize().checked_sub(HEADER_SIZE)?;
258 let column_index_table_offset =
259 s.read::<Offset32>()?.to_usize().checked_sub(HEADER_SIZE)?;
260 let kerning_array_offset = s.read::<Offset32>()?.to_usize().checked_sub(HEADER_SIZE)?;
261 let kerning_vector_offset = s.read::<Offset32>()?.to_usize().checked_sub(HEADER_SIZE)?;
262
263 let row_index_table_data = self.data.get(row_index_table_offset..)?;
264 let column_index_table_data = self.data.get(column_index_table_offset..)?;
265 let kerning_array_data = self.data.get(kerning_array_offset..)?;
266 let kerning_vector_data = self.data.get(kerning_vector_offset..)?;
267
268 let has_long_values = flags & 0x00000001 != 0;
269 if has_long_values {
270 let l: u32 = aat::Lookup::parse(self.number_of_glyphs, row_index_table_data)?
271 .value(left)
272 .unwrap_or(0) as u32;
273
274 let r: u32 = aat::Lookup::parse(self.number_of_glyphs, column_index_table_data)?
275 .value(right)
276 .unwrap_or(0) as u32;
277
278 let array_offset = usize::try_from(l + r).ok()?.checked_mul(i32::SIZE)?;
279 let vector_offset: u32 = Stream::read_at(kerning_array_data, array_offset)?;
280
281 Stream::read_at(kerning_vector_data, usize::num_from(vector_offset))
282 } else {
283 let l: u16 = aat::Lookup::parse(self.number_of_glyphs, row_index_table_data)?
284 .value(left)
285 .unwrap_or(0);
286
287 let r: u16 = aat::Lookup::parse(self.number_of_glyphs, column_index_table_data)?
288 .value(right)
289 .unwrap_or(0);
290
291 let array_offset = usize::try_from(l + r).ok()?.checked_mul(i16::SIZE)?;
292 let vector_offset: u16 = Stream::read_at(kerning_array_data, array_offset)?;
293
294 Stream::read_at(kerning_vector_data, usize::from(vector_offset))
295 }
296 }
297}
298
299impl core::fmt::Debug for Subtable6<'_> {
300 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
301 write!(f, "Subtable6 {{ ... }}")
302 }
303}
304
305/// An extended kerning subtable format.
306#[allow(missing_docs)]
307#[derive(Clone, Debug)]
308pub enum Format<'a> {
309 Format0(Subtable0<'a>),
310 Format1(Subtable1<'a>),
311 Format2(Subtable2<'a>),
312 Format4(Subtable4<'a>),
313 Format6(Subtable6<'a>),
314}
315
316/// A kerning subtable.
317#[derive(Clone, Debug)]
318pub struct Subtable<'a> {
319 /// Indicates that subtable is for horizontal text.
320 pub horizontal: bool,
321 /// Indicates that subtable is variable.
322 pub variable: bool,
323 /// Indicates that subtable has a cross-stream values.
324 pub has_cross_stream: bool,
325 /// Indicates that subtable uses a state machine.
326 ///
327 /// In this case `glyphs_kerning()` will return `None`.
328 pub has_state_machine: bool,
329 /// The tuple count.
330 ///
331 /// This value is only used with variation fonts and should be 0 for all other fonts.
332 pub tuple_count: u32,
333 /// Subtable format.
334 pub format: Format<'a>,
335}
336
337impl<'a> Subtable<'a> {
338 /// Returns kerning for a pair of glyphs.
339 ///
340 /// Returns `None` in case of state machine based subtable.
341 #[inline]
342 pub fn glyphs_kerning(&self, left: GlyphId, right: GlyphId) -> Option<i16> {
343 match self.format {
344 Format::Format0(ref subtable: &Subtable0<'_>) => subtable.glyphs_kerning(left, right),
345 Format::Format1(_) => None,
346 Format::Format2(ref subtable: &Subtable2<'_>) => subtable.glyphs_kerning(left, right),
347 Format::Format4(_) => None,
348 Format::Format6(ref subtable: &Subtable6<'_>) => subtable.glyphs_kerning(left, right),
349 }
350 }
351}
352
353#[derive(Clone, Copy, Debug)]
354struct Coverage(u8);
355
356#[rustfmt::skip]
357impl Coverage {
358 // TODO: use hex
359 #[inline] pub fn is_horizontal(self) -> bool { self.0 & (1 << 7) == 0 }
360 #[inline] pub fn has_cross_stream(self) -> bool { self.0 & (1 << 6) != 0 }
361 #[inline] pub fn is_variable(self) -> bool { self.0 & (1 << 5) != 0 }
362}
363
364/// A list of extended kerning subtables.
365///
366/// The internal data layout is not designed for random access,
367/// therefore we're not providing the `get()` method and only an iterator.
368#[derive(Clone, Copy)]
369pub struct Subtables<'a> {
370 /// The number of glyphs from the `maxp` table.
371 number_of_glyphs: NonZeroU16,
372 /// The total number of tables.
373 number_of_tables: u32,
374 /// Actual data. Starts right after the `kerx` header.
375 data: &'a [u8],
376}
377
378impl core::fmt::Debug for Subtables<'_> {
379 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
380 write!(f, "Subtables {{ ... }}")
381 }
382}
383
384impl<'a> IntoIterator for Subtables<'a> {
385 type Item = Subtable<'a>;
386 type IntoIter = SubtablesIter<'a>;
387
388 #[inline]
389 fn into_iter(self) -> Self::IntoIter {
390 SubtablesIter {
391 number_of_glyphs: self.number_of_glyphs,
392 table_index: 0,
393 number_of_tables: self.number_of_tables,
394 stream: Stream::new(self.data),
395 }
396 }
397}
398
399/// An iterator over extended kerning subtables.
400#[allow(missing_debug_implementations)]
401#[derive(Clone)]
402pub struct SubtablesIter<'a> {
403 /// The number of glyphs from the `maxp` table.
404 number_of_glyphs: NonZeroU16,
405 /// The current table index.
406 table_index: u32,
407 /// The total number of tables.
408 number_of_tables: u32,
409 /// Actual data. Starts right after the `kerx` header.
410 stream: Stream<'a>,
411}
412
413impl<'a> Iterator for SubtablesIter<'a> {
414 type Item = Subtable<'a>;
415
416 fn next(&mut self) -> Option<Self::Item> {
417 if self.table_index == self.number_of_tables {
418 return None;
419 }
420
421 if self.stream.at_end() {
422 return None;
423 }
424
425 let s = &mut self.stream;
426
427 let table_len = s.read::<u32>()?;
428 let coverage = Coverage(s.read::<u8>()?);
429 s.skip::<u16>(); // unused
430 let raw_format = s.read::<u8>()?;
431 let tuple_count = s.read::<u32>()?;
432
433 // Subtract the header size.
434 let data_len = usize::num_from(table_len).checked_sub(HEADER_SIZE)?;
435 let data = s.read_bytes(data_len)?;
436
437 let format = match raw_format {
438 0 => Subtable0::parse(data).map(Format::Format0)?,
439 1 => Subtable1::parse(self.number_of_glyphs, data).map(Format::Format1)?,
440 2 => Format::Format2(Subtable2(data)),
441 4 => Subtable4::parse(self.number_of_glyphs, data).map(Format::Format4)?,
442 6 => Format::Format6(Subtable6::parse(self.number_of_glyphs, data)),
443 _ => {
444 // Unknown format.
445 return None;
446 }
447 };
448
449 Some(Subtable {
450 horizontal: coverage.is_horizontal(),
451 variable: coverage.is_variable(),
452 has_cross_stream: coverage.has_cross_stream(),
453 has_state_machine: raw_format == 1 || raw_format == 4,
454 tuple_count,
455 format,
456 })
457 }
458}
459
460/// An [Extended Kerning Table](
461/// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6kerx.html).
462#[derive(Clone, Copy, Debug)]
463pub struct Table<'a> {
464 /// A list of subtables.
465 pub subtables: Subtables<'a>,
466}
467
468impl<'a> Table<'a> {
469 /// Parses a table from raw data.
470 ///
471 /// `number_of_glyphs` is from the `maxp` table.
472 pub fn parse(number_of_glyphs: NonZeroU16, data: &'a [u8]) -> Option<Self> {
473 let mut s: Stream<'_> = Stream::new(data);
474 s.skip::<u16>(); // version
475 s.skip::<u16>(); // padding
476 let number_of_tables: u32 = s.read::<u32>()?;
477 let subtables: Subtables<'_> = Subtables {
478 number_of_glyphs,
479 number_of_tables,
480 data: s.tail()?,
481 };
482
483 Some(Table { subtables })
484 }
485}
486