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 | |
8 | use core::num::NonZeroU16; |
9 | |
10 | use crate::kern::KerningPair; |
11 | use crate::parser::{FromData, LazyArray32, NumFrom, Offset, Offset32, Stream}; |
12 | use crate::{aat, GlyphId}; |
13 | |
14 | const 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)] |
22 | pub struct Subtable0<'a> { |
23 | /// A list of kerning pairs. |
24 | pub pairs: LazyArray32<'a, KerningPair>, |
25 | } |
26 | |
27 | impl<'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)] |
49 | pub struct EntryData { |
50 | /// An action index. |
51 | pub action_index: u16, |
52 | } |
53 | |
54 | impl 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)] |
70 | pub struct Subtable1<'a> { |
71 | /// A state table. |
72 | pub state_table: aat::ExtendedStateTable<'a, EntryData>, |
73 | actions_data: &'a [u8], |
74 | } |
75 | |
76 | impl<'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 | |
101 | impl<'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 | |
109 | impl 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)] |
121 | pub struct Subtable2<'a>(&'a [u8]); // TODO: parse actual structure |
122 | |
123 | impl<'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 | |
156 | impl 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)] |
164 | pub struct AnchorPoints<'a>(&'a [u8]); |
165 | |
166 | impl 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 | |
175 | impl 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)] |
189 | pub 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 | |
196 | impl<'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 | |
216 | impl<'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 | |
224 | impl 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)] |
234 | pub struct Subtable6<'a> { |
235 | data: &'a [u8], |
236 | number_of_glyphs: NonZeroU16, |
237 | } |
238 | |
239 | impl<'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 | |
299 | impl 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)] |
308 | pub 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)] |
318 | pub 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 | |
337 | impl<'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)] |
354 | struct Coverage(u8); |
355 | |
356 | #[rustfmt::skip] |
357 | impl 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)] |
369 | pub 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 | |
378 | impl core::fmt::Debug for Subtables<'_> { |
379 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
380 | write!(f, "Subtables {{ ... }}" ) |
381 | } |
382 | } |
383 | |
384 | impl<'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)] |
402 | pub 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 | |
413 | impl<'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)] |
463 | pub struct Table<'a> { |
464 | /// A list of subtables. |
465 | pub subtables: Subtables<'a>, |
466 | } |
467 | |
468 | impl<'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 | |