1 | /*! |
2 | A [Kerning Table]( |
3 | https://docs.microsoft.com/en-us/typography/opentype/spec/kern) implementation. |
4 | |
5 | Supports both |
6 | [OpenType](https://docs.microsoft.com/en-us/typography/opentype/spec/kern) |
7 | and |
8 | [Apple Advanced Typography](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6kern.html) |
9 | variants. |
10 | |
11 | Since there is no single correct way to process a kerning data, |
12 | we have to provide an access to kerning subtables, so a caller can implement |
13 | a kerning algorithm manually. |
14 | But we still try to keep the API as high-level as possible. |
15 | */ |
16 | |
17 | #[cfg (feature = "apple-layout" )] |
18 | use crate::aat; |
19 | use crate::parser::{FromData, LazyArray16, NumFrom, Offset, Offset16, Stream}; |
20 | use crate::GlyphId; |
21 | |
22 | #[derive (Clone, Copy, Debug)] |
23 | struct OTCoverage(u8); |
24 | |
25 | #[rustfmt::skip] |
26 | impl 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 | |
31 | impl 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)] |
41 | struct AATCoverage(u8); |
42 | |
43 | #[rustfmt::skip] |
44 | impl 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 | |
50 | impl 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)] |
61 | pub 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 | |
71 | impl 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 | |
85 | impl 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)] |
101 | pub 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)] |
113 | pub 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 | |
128 | impl<'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)] |
148 | pub 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 | |
157 | impl<'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 | |
169 | impl core::fmt::Debug for Subtables<'_> { |
170 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
171 | write!(f, "Subtables {{ ... }}" ) |
172 | } |
173 | } |
174 | |
175 | impl<'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)] |
193 | pub 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 | |
204 | impl<'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)] |
299 | pub struct Subtable0<'a> { |
300 | /// A list of kerning pairs. |
301 | pub pairs: LazyArray16<'a, KerningPair>, |
302 | } |
303 | |
304 | impl<'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)] |
328 | pub struct Subtable2<'a> { |
329 | // TODO: parse actual structure |
330 | data: &'a [u8], |
331 | header_len: u8, |
332 | } |
333 | |
334 | impl<'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 | |
372 | pub(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)] |
386 | pub struct Subtable3<'a> { |
387 | // TODO: parse actual structure |
388 | data: &'a [u8], |
389 | } |
390 | |
391 | impl<'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)] |
430 | pub struct Table<'a> { |
431 | /// A list of subtables. |
432 | pub subtables: Subtables<'a>, |
433 | } |
434 | |
435 | impl<'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 | |