1/*!
2A [Kerning Table](
3https://docs.microsoft.com/en-us/typography/opentype/spec/kern) implementation.
4
5Supports both
6[OpenType](https://docs.microsoft.com/en-us/typography/opentype/spec/kern)
7and
8[Apple Advanced Typography](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6kern.html)
9variants.
10
11Since there is no single correct way to process a kerning data,
12we have to provide an access to kerning subtables, so a caller can implement
13a kerning algorithm manually.
14But we still try to keep the API as high-level as possible.
15*/
16
17#[cfg(feature = "apple-layout")]
18use crate::aat;
19use crate::parser::{FromData, LazyArray16, NumFrom, Offset, Offset16, Stream};
20use crate::GlyphId;
21
22#[derive(Clone, Copy, Debug)]
23struct OTCoverage(u8);
24
25#[rustfmt::skip]
26impl OTCoverage {
27 #[inline] fn is_horizontal(self) -> bool { self.0 & (1 << 0) != 0 }
28 #[inline] fn has_cross_stream(self) -> bool { self.0 & (1 << 2) != 0 }
29}
30
31impl FromData for OTCoverage {
32 const SIZE: usize = 1;
33
34 #[inline]
35 fn parse(data: &[u8]) -> Option<Self> {
36 data.get(index:0).copied().map(OTCoverage)
37 }
38}
39
40#[derive(Clone, Copy, Debug)]
41struct AATCoverage(u8);
42
43#[rustfmt::skip]
44impl AATCoverage {
45 #[inline] fn is_horizontal(self) -> bool { self.0 & (1 << 7) == 0 }
46 #[inline] fn has_cross_stream(self) -> bool { self.0 & (1 << 6) != 0 }
47 #[inline] fn is_variable(self) -> bool { self.0 & (1 << 5) != 0 }
48}
49
50impl FromData for AATCoverage {
51 const SIZE: usize = 1;
52
53 #[inline]
54 fn parse(data: &[u8]) -> Option<Self> {
55 data.get(index:0).copied().map(AATCoverage)
56 }
57}
58
59/// A kerning pair.
60#[derive(Clone, Copy, Debug)]
61pub struct KerningPair {
62 /// Glyphs pair.
63 ///
64 /// In the kern table spec, a kerning pair is stored as two u16,
65 /// but we are using one u32, so we can binary search it directly.
66 pub pair: u32,
67 /// Kerning value.
68 pub value: i16,
69}
70
71impl KerningPair {
72 /// Returns left glyph ID.
73 #[inline]
74 pub fn left(&self) -> GlyphId {
75 GlyphId((self.pair >> 16) as u16)
76 }
77
78 /// Returns right glyph ID.
79 #[inline]
80 pub fn right(&self) -> GlyphId {
81 GlyphId(self.pair as u16)
82 }
83}
84
85impl FromData for KerningPair {
86 const SIZE: usize = 6;
87
88 #[inline]
89 fn parse(data: &[u8]) -> Option<Self> {
90 let mut s: Stream<'_> = Stream::new(data);
91 Some(KerningPair {
92 pair: s.read::<u32>()?,
93 value: s.read::<i16>()?,
94 })
95 }
96}
97
98/// A kerning subtable format.
99#[allow(missing_docs)]
100#[derive(Clone, Debug)]
101pub enum Format<'a> {
102 Format0(Subtable0<'a>),
103 #[cfg(feature = "apple-layout")]
104 Format1(aat::StateTable<'a>),
105 #[cfg(not(feature = "apple-layout"))]
106 Format1,
107 Format2(Subtable2<'a>),
108 Format3(Subtable3<'a>),
109}
110
111/// A kerning subtable.
112#[derive(Clone, Debug)]
113pub struct Subtable<'a> {
114 /// Indicates that subtable is for horizontal text.
115 pub horizontal: bool,
116 /// Indicates that subtable is variable.
117 pub variable: bool,
118 /// Indicates that subtable has a cross-stream values.
119 pub has_cross_stream: bool,
120 /// Indicates that subtable uses a state machine.
121 ///
122 /// In this case `glyphs_kerning()` will return `None`.
123 pub has_state_machine: bool,
124 /// Subtable format.
125 pub format: Format<'a>,
126}
127
128impl<'a> Subtable<'a> {
129 /// Returns kerning for a pair of glyphs.
130 ///
131 /// Returns `None` in case of state machine based subtable.
132 #[inline]
133 pub fn glyphs_kerning(&self, left: GlyphId, right: GlyphId) -> Option<i16> {
134 match self.format {
135 Format::Format0(ref subtable: &Subtable0<'_>) => subtable.glyphs_kerning(left, right),
136 Format::Format2(ref subtable: &Subtable2<'_>) => subtable.glyphs_kerning(left, right),
137 Format::Format3(ref subtable: &Subtable3<'_>) => subtable.glyphs_kerning(left, right),
138 _ => None,
139 }
140 }
141}
142
143/// A list of subtables.
144///
145/// The internal data layout is not designed for random access,
146/// therefore we're not providing the `get()` method and only an iterator.
147#[derive(Clone, Copy)]
148pub struct Subtables<'a> {
149 /// Indicates an Apple Advanced Typography format.
150 is_aat: bool,
151 /// The total number of tables.
152 count: u32,
153 /// Actual data. Starts right after the `kern` header.
154 data: &'a [u8],
155}
156
157impl<'a> Subtables<'a> {
158 /// Returns the number of subtables.
159 pub fn len(&self) -> u32 {
160 self.count
161 }
162
163 /// Checks if there are any subtables.
164 pub fn is_empty(&self) -> bool {
165 self.count == 0
166 }
167}
168
169impl core::fmt::Debug for Subtables<'_> {
170 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
171 write!(f, "Subtables {{ ... }}")
172 }
173}
174
175impl<'a> IntoIterator for Subtables<'a> {
176 type Item = Subtable<'a>;
177 type IntoIter = SubtablesIter<'a>;
178
179 #[inline]
180 fn into_iter(self) -> Self::IntoIter {
181 SubtablesIter {
182 is_aat: self.is_aat,
183 table_index: 0,
184 number_of_tables: self.count,
185 stream: Stream::new(self.data),
186 }
187 }
188}
189
190/// An iterator over kerning subtables.
191#[allow(missing_debug_implementations)]
192#[derive(Clone, Default)]
193pub struct SubtablesIter<'a> {
194 /// Indicates an Apple Advanced Typography format.
195 is_aat: bool,
196 /// The current table index,
197 table_index: u32,
198 /// The total number of tables.
199 number_of_tables: u32,
200 /// Actual data. Starts right after `kern` header.
201 stream: Stream<'a>,
202}
203
204impl<'a> Iterator for SubtablesIter<'a> {
205 type Item = Subtable<'a>;
206
207 fn next(&mut self) -> Option<Self::Item> {
208 if self.table_index == self.number_of_tables {
209 return None;
210 }
211
212 if self.stream.at_end() {
213 return None;
214 }
215
216 if self.is_aat {
217 const HEADER_SIZE: u8 = 8;
218
219 let table_len = self.stream.read::<u32>()?;
220 let coverage = self.stream.read::<AATCoverage>()?;
221 let format_id = self.stream.read::<u8>()?;
222 self.stream.skip::<u16>(); // variation tuple index
223
224 if format_id > 3 {
225 // Unknown format.
226 return None;
227 }
228
229 // Subtract the header size.
230 let data_len = usize::num_from(table_len).checked_sub(usize::from(HEADER_SIZE))?;
231 let data = self.stream.read_bytes(data_len)?;
232
233 let format = match format_id {
234 0 => Format::Format0(Subtable0::parse(data)?),
235 #[cfg(feature = "apple-layout")]
236 1 => Format::Format1(aat::StateTable::parse(data)?),
237 #[cfg(not(feature = "apple-layout"))]
238 1 => Format::Format1,
239 2 => Format::Format2(Subtable2::parse(HEADER_SIZE, data)?),
240 3 => Format::Format3(Subtable3::parse(data)?),
241 _ => return None,
242 };
243
244 Some(Subtable {
245 horizontal: coverage.is_horizontal(),
246 variable: coverage.is_variable(),
247 has_cross_stream: coverage.has_cross_stream(),
248 has_state_machine: format_id == 1,
249 format,
250 })
251 } else {
252 const HEADER_SIZE: u8 = 6;
253
254 self.stream.skip::<u16>(); // version
255 let table_len = self.stream.read::<u16>()?;
256 // In the OpenType variant, `format` comes first.
257 let format_id = self.stream.read::<u8>()?;
258 let coverage = self.stream.read::<OTCoverage>()?;
259
260 if format_id != 0 && format_id != 2 {
261 // Unknown format.
262 return None;
263 }
264
265 let data_len = if self.number_of_tables == 1 {
266 // An OpenType `kern` table with just one subtable is a special case.
267 // The `table_len` property is mainly required to jump to the next subtable,
268 // but if there is only one subtable, this property can be ignored.
269 // This is abused by some fonts, to get around the `u16` size limit.
270 self.stream.tail()?.len()
271 } else {
272 // Subtract the header size.
273 usize::from(table_len).checked_sub(usize::from(HEADER_SIZE))?
274 };
275
276 let data = self.stream.read_bytes(data_len)?;
277
278 let format = match format_id {
279 0 => Format::Format0(Subtable0::parse(data)?),
280 2 => Format::Format2(Subtable2::parse(HEADER_SIZE, data)?),
281 _ => return None,
282 };
283
284 Some(Subtable {
285 horizontal: coverage.is_horizontal(),
286 variable: false, // Only AAT supports it.
287 has_cross_stream: coverage.has_cross_stream(),
288 has_state_machine: format_id == 1,
289 format,
290 })
291 }
292 }
293}
294
295/// A format 0 subtable.
296///
297/// Ordered List of Kerning Pairs.
298#[derive(Clone, Copy, Debug)]
299pub struct Subtable0<'a> {
300 /// A list of kerning pairs.
301 pub pairs: LazyArray16<'a, KerningPair>,
302}
303
304impl<'a> Subtable0<'a> {
305 /// Parses a subtable from raw data.
306 pub fn parse(data: &'a [u8]) -> Option<Self> {
307 let mut s: Stream<'_> = Stream::new(data);
308 let number_of_pairs: u16 = s.read::<u16>()?;
309 s.advance(len:6); // search_range (u16) + entry_selector (u16) + range_shift (u16)
310 let pairs: LazyArray16<'_, KerningPair> = s.read_array16::<KerningPair>(count:number_of_pairs)?;
311 Some(Self { pairs })
312 }
313
314 /// Returns kerning for a pair of glyphs.
315 #[inline]
316 pub fn glyphs_kerning(&self, left: GlyphId, right: GlyphId) -> Option<i16> {
317 let needle: u32 = u32::from(left.0) << 16 | u32::from(right.0);
318 self.pairs
319 .binary_search_by(|v: &KerningPair| v.pair.cmp(&needle))
320 .map(|(_, v: KerningPair)| v.value)
321 }
322}
323
324/// A format 2 subtable.
325///
326/// Simple n x m Array of Kerning Values.
327#[derive(Clone, Copy, Debug)]
328pub struct Subtable2<'a> {
329 // TODO: parse actual structure
330 data: &'a [u8],
331 header_len: u8,
332}
333
334impl<'a> Subtable2<'a> {
335 /// Parses a subtable from raw data.
336 pub fn parse(header_len: u8, data: &'a [u8]) -> Option<Self> {
337 Some(Self { header_len, data })
338 }
339
340 /// Returns kerning for a pair of glyphs.
341 pub fn glyphs_kerning(&self, left: GlyphId, right: GlyphId) -> Option<i16> {
342 let mut s = Stream::new(self.data);
343 s.skip::<u16>(); // row_width
344
345 // Offsets are from beginning of the subtable and not from the `data` start,
346 // so we have to subtract the header.
347 let header_len = usize::from(self.header_len);
348 let left_hand_table_offset = s.read::<Offset16>()?.to_usize().checked_sub(header_len)?;
349 let right_hand_table_offset = s.read::<Offset16>()?.to_usize().checked_sub(header_len)?;
350 let array_offset = s.read::<Offset16>()?.to_usize().checked_sub(header_len)?;
351
352 // 'The array can be indexed by completing the left-hand and right-hand class mappings,
353 // adding the class values to the address of the subtable,
354 // and fetching the kerning value to which the new address points.'
355
356 let left_class = get_format2_class(left.0, left_hand_table_offset, self.data).unwrap_or(0);
357 let right_class =
358 get_format2_class(right.0, right_hand_table_offset, self.data).unwrap_or(0);
359
360 // 'Values within the left-hand offset table should not be less than the kerning array offset.'
361 if usize::from(left_class) < array_offset {
362 return None;
363 }
364
365 // Classes are already premultiplied, so we only need to sum them.
366 let index = usize::from(left_class) + usize::from(right_class);
367 let value_offset = index.checked_sub(header_len)?;
368 Stream::read_at::<i16>(self.data, value_offset)
369 }
370}
371
372pub(crate) fn get_format2_class(glyph_id: u16, offset: usize, data: &[u8]) -> Option<u16> {
373 let mut s: Stream<'_> = Stream::new_at(data, offset)?;
374 let first_glyph: u16 = s.read::<u16>()?;
375 let index: u16 = glyph_id.checked_sub(first_glyph)?;
376
377 let number_of_classes: u16 = s.read::<u16>()?;
378 let classes: LazyArray16<'_, u16> = s.read_array16::<u16>(count:number_of_classes)?;
379 classes.get(index)
380}
381
382/// A format 3 subtable.
383///
384/// Simple n x m Array of Kerning Indices.
385#[derive(Clone, Copy, Debug)]
386pub struct Subtable3<'a> {
387 // TODO: parse actual structure
388 data: &'a [u8],
389}
390
391impl<'a> Subtable3<'a> {
392 /// Parses a subtable from raw data.
393 pub fn parse(data: &'a [u8]) -> Option<Self> {
394 Some(Self { data })
395 }
396
397 /// Returns kerning for a pair of glyphs.
398 #[inline]
399 pub fn glyphs_kerning(&self, left: GlyphId, right: GlyphId) -> Option<i16> {
400 let mut s = Stream::new(self.data);
401 let glyph_count = s.read::<u16>()?;
402 let kerning_values_count = s.read::<u8>()?;
403 let left_hand_classes_count = s.read::<u8>()?;
404 let right_hand_classes_count = s.read::<u8>()?;
405 s.skip::<u8>(); // reserved
406 let indices_count =
407 u16::from(left_hand_classes_count) * u16::from(right_hand_classes_count);
408
409 let kerning_values = s.read_array16::<i16>(u16::from(kerning_values_count))?;
410 let left_hand_classes = s.read_array16::<u8>(glyph_count)?;
411 let right_hand_classes = s.read_array16::<u8>(glyph_count)?;
412 let indices = s.read_array16::<u8>(indices_count)?;
413
414 let left_class = left_hand_classes.get(left.0)?;
415 let right_class = right_hand_classes.get(right.0)?;
416
417 if left_class > left_hand_classes_count || right_class > right_hand_classes_count {
418 return None;
419 }
420
421 let index =
422 u16::from(left_class) * u16::from(right_hand_classes_count) + u16::from(right_class);
423 let index = indices.get(index)?;
424 kerning_values.get(u16::from(index))
425 }
426}
427
428/// A [Kerning Table](https://docs.microsoft.com/en-us/typography/opentype/spec/kern).
429#[derive(Clone, Copy, Debug)]
430pub struct Table<'a> {
431 /// A list of subtables.
432 pub subtables: Subtables<'a>,
433}
434
435impl<'a> Table<'a> {
436 /// Parses a table from raw data.
437 pub fn parse(data: &'a [u8]) -> Option<Self> {
438 // The `kern` table has two variants: OpenType and Apple.
439 // And they both have different headers.
440 // There are no robust way to distinguish them, so we have to guess.
441 //
442 // The OpenType one has the first two bytes (UInt16) as a version set to 0.
443 // While Apple one has the first four bytes (Fixed) set to 1.0
444 // So the first two bytes in case of an OpenType format will be 0x0000
445 // and 0x0001 in case of an Apple format.
446 let mut s = Stream::new(data);
447 let version = s.read::<u16>()?;
448 let subtables = if version == 0 {
449 let count = s.read::<u16>()?;
450 Subtables {
451 is_aat: false,
452 count: u32::from(count),
453 data: s.tail()?,
454 }
455 } else {
456 s.skip::<u16>(); // Skip the second part of u32 version.
457 // Note that AAT stores the number of tables as u32 and not as u16.
458 let count = s.read::<u32>()?;
459 Subtables {
460 is_aat: true,
461 count,
462 data: s.tail()?,
463 }
464 };
465
466 Some(Self { subtables })
467 }
468}
469