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 | // Each action contains two 16-bit fields, so we must |
170 | // double the action_index to get the correct offset here. |
171 | let offset: usize = usize::from(action_index) * u16::SIZE * 2; |
172 | let mut s: Stream<'_> = Stream::new_at(self.0, offset)?; |
173 | Some((s.read::<u16>()?, s.read::<u16>()?)) |
174 | } |
175 | } |
176 | |
177 | impl core::fmt::Debug for AnchorPoints<'_> { |
178 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
179 | write!(f, "AnchorPoints {{ ... }}" ) |
180 | } |
181 | } |
182 | |
183 | /// A format 4 subtable. |
184 | /// |
185 | /// State Table for Control Point/Anchor Point Positioning. |
186 | /// |
187 | /// Note: I wasn't able to find any fonts that actually use |
188 | /// `ControlPointActions` and/or `ControlPointCoordinateActions`, |
189 | /// therefore only `AnchorPointActions` is supported. |
190 | #[derive (Clone)] |
191 | pub struct Subtable4<'a> { |
192 | /// A state table. |
193 | pub state_table: aat::ExtendedStateTable<'a, EntryData>, |
194 | /// Anchor points. |
195 | pub anchor_points: AnchorPoints<'a>, |
196 | } |
197 | |
198 | impl<'a> Subtable4<'a> { |
199 | fn parse(number_of_glyphs: NonZeroU16, data: &'a [u8]) -> Option<Self> { |
200 | let mut s: Stream<'_> = Stream::new(data); |
201 | let state_table: ExtendedStateTable<'_, EntryData> = aat::ExtendedStateTable::parse(number_of_glyphs, &mut s)?; |
202 | let flags: u32 = s.read::<u32>()?; |
203 | let action_type: u8 = ((flags & 0xC0000000) >> 30) as u8; |
204 | let points_offset: usize = usize::num_from(flags & 0x00FFFFFF); |
205 | |
206 | // We support only Anchor Point Actions. |
207 | if action_type != 1 { |
208 | return None; |
209 | } |
210 | |
211 | Some(Self { |
212 | state_table, |
213 | anchor_points: AnchorPoints(data.get(index:points_offset..)?), |
214 | }) |
215 | } |
216 | } |
217 | |
218 | impl<'a> core::ops::Deref for Subtable4<'a> { |
219 | type Target = aat::ExtendedStateTable<'a, EntryData>; |
220 | |
221 | fn deref(&self) -> &Self::Target { |
222 | &self.state_table |
223 | } |
224 | } |
225 | |
226 | impl core::fmt::Debug for Subtable4<'_> { |
227 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
228 | write!(f, "Subtable4 {{ ... }}" ) |
229 | } |
230 | } |
231 | |
232 | /// A format 6 subtable. |
233 | /// |
234 | /// Simple Index-based n x m Array of Kerning Values. |
235 | #[derive (Clone, Copy)] |
236 | pub struct Subtable6<'a> { |
237 | data: &'a [u8], |
238 | number_of_glyphs: NonZeroU16, |
239 | } |
240 | |
241 | impl<'a> Subtable6<'a> { |
242 | // TODO: parse actual structure |
243 | fn parse(number_of_glyphs: NonZeroU16, data: &'a [u8]) -> Self { |
244 | Subtable6 { |
245 | number_of_glyphs, |
246 | data, |
247 | } |
248 | } |
249 | |
250 | /// Returns kerning for a pair of glyphs. |
251 | pub fn glyphs_kerning(&self, left: GlyphId, right: GlyphId) -> Option<i16> { |
252 | use core::convert::TryFrom; |
253 | |
254 | let mut s = Stream::new(self.data); |
255 | let flags = s.read::<u32>()?; |
256 | s.skip::<u16>(); // row_count |
257 | s.skip::<u16>(); // col_count |
258 | // All offsets are from the start of the subtable. |
259 | let row_index_table_offset = s.read::<Offset32>()?.to_usize().checked_sub(HEADER_SIZE)?; |
260 | let column_index_table_offset = |
261 | s.read::<Offset32>()?.to_usize().checked_sub(HEADER_SIZE)?; |
262 | let kerning_array_offset = s.read::<Offset32>()?.to_usize().checked_sub(HEADER_SIZE)?; |
263 | let kerning_vector_offset = s.read::<Offset32>()?.to_usize().checked_sub(HEADER_SIZE)?; |
264 | |
265 | let row_index_table_data = self.data.get(row_index_table_offset..)?; |
266 | let column_index_table_data = self.data.get(column_index_table_offset..)?; |
267 | let kerning_array_data = self.data.get(kerning_array_offset..)?; |
268 | let kerning_vector_data = self.data.get(kerning_vector_offset..)?; |
269 | |
270 | let has_long_values = flags & 0x00000001 != 0; |
271 | if has_long_values { |
272 | let l: u32 = aat::Lookup::parse(self.number_of_glyphs, row_index_table_data)? |
273 | .value(left) |
274 | .unwrap_or(0) as u32; |
275 | |
276 | let r: u32 = aat::Lookup::parse(self.number_of_glyphs, column_index_table_data)? |
277 | .value(right) |
278 | .unwrap_or(0) as u32; |
279 | |
280 | let array_offset = usize::try_from(l + r).ok()?.checked_mul(i32::SIZE)?; |
281 | let vector_offset: u32 = Stream::read_at(kerning_array_data, array_offset)?; |
282 | |
283 | Stream::read_at(kerning_vector_data, usize::num_from(vector_offset)) |
284 | } else { |
285 | let l: u16 = aat::Lookup::parse(self.number_of_glyphs, row_index_table_data)? |
286 | .value(left) |
287 | .unwrap_or(0); |
288 | |
289 | let r: u16 = aat::Lookup::parse(self.number_of_glyphs, column_index_table_data)? |
290 | .value(right) |
291 | .unwrap_or(0); |
292 | |
293 | let array_offset = usize::from(l + r).checked_mul(i16::SIZE)?; |
294 | let vector_offset: u16 = Stream::read_at(kerning_array_data, array_offset)?; |
295 | |
296 | Stream::read_at(kerning_vector_data, usize::from(vector_offset)) |
297 | } |
298 | } |
299 | } |
300 | |
301 | impl core::fmt::Debug for Subtable6<'_> { |
302 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
303 | write!(f, "Subtable6 {{ ... }}" ) |
304 | } |
305 | } |
306 | |
307 | /// An extended kerning subtable format. |
308 | #[allow (missing_docs)] |
309 | #[derive (Clone, Debug)] |
310 | pub enum Format<'a> { |
311 | Format0(Subtable0<'a>), |
312 | Format1(Subtable1<'a>), |
313 | Format2(Subtable2<'a>), |
314 | Format4(Subtable4<'a>), |
315 | Format6(Subtable6<'a>), |
316 | } |
317 | |
318 | /// A kerning subtable. |
319 | #[derive (Clone, Debug)] |
320 | pub struct Subtable<'a> { |
321 | /// Indicates that subtable is for horizontal text. |
322 | pub horizontal: bool, |
323 | /// Indicates that subtable is variable. |
324 | pub variable: bool, |
325 | /// Indicates that subtable has a cross-stream values. |
326 | pub has_cross_stream: bool, |
327 | /// Indicates that subtable uses a state machine. |
328 | /// |
329 | /// In this case `glyphs_kerning()` will return `None`. |
330 | pub has_state_machine: bool, |
331 | /// The tuple count. |
332 | /// |
333 | /// This value is only used with variation fonts and should be 0 for all other fonts. |
334 | pub tuple_count: u32, |
335 | /// Subtable format. |
336 | pub format: Format<'a>, |
337 | } |
338 | |
339 | impl<'a> Subtable<'a> { |
340 | /// Returns kerning for a pair of glyphs. |
341 | /// |
342 | /// Returns `None` in case of state machine based subtable. |
343 | #[inline ] |
344 | pub fn glyphs_kerning(&self, left: GlyphId, right: GlyphId) -> Option<i16> { |
345 | match self.format { |
346 | Format::Format0(ref subtable: &Subtable0<'_>) => subtable.glyphs_kerning(left, right), |
347 | Format::Format1(_) => None, |
348 | Format::Format2(ref subtable: &Subtable2<'_>) => subtable.glyphs_kerning(left, right), |
349 | Format::Format4(_) => None, |
350 | Format::Format6(ref subtable: &Subtable6<'_>) => subtable.glyphs_kerning(left, right), |
351 | } |
352 | } |
353 | } |
354 | |
355 | #[derive (Clone, Copy, Debug)] |
356 | struct Coverage(u8); |
357 | |
358 | #[rustfmt::skip] |
359 | impl Coverage { |
360 | // TODO: use hex |
361 | #[inline ] pub fn is_horizontal(self) -> bool { self.0 & (1 << 7) == 0 } |
362 | #[inline ] pub fn has_cross_stream(self) -> bool { self.0 & (1 << 6) != 0 } |
363 | #[inline ] pub fn is_variable(self) -> bool { self.0 & (1 << 5) != 0 } |
364 | } |
365 | |
366 | /// A list of extended kerning subtables. |
367 | /// |
368 | /// The internal data layout is not designed for random access, |
369 | /// therefore we're not providing the `get()` method and only an iterator. |
370 | #[derive (Clone, Copy)] |
371 | pub struct Subtables<'a> { |
372 | /// The number of glyphs from the `maxp` table. |
373 | number_of_glyphs: NonZeroU16, |
374 | /// The total number of tables. |
375 | number_of_tables: u32, |
376 | /// Actual data. Starts right after the `kerx` header. |
377 | data: &'a [u8], |
378 | } |
379 | |
380 | impl core::fmt::Debug for Subtables<'_> { |
381 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
382 | write!(f, "Subtables {{ ... }}" ) |
383 | } |
384 | } |
385 | |
386 | impl<'a> IntoIterator for Subtables<'a> { |
387 | type Item = Subtable<'a>; |
388 | type IntoIter = SubtablesIter<'a>; |
389 | |
390 | #[inline ] |
391 | fn into_iter(self) -> Self::IntoIter { |
392 | SubtablesIter { |
393 | number_of_glyphs: self.number_of_glyphs, |
394 | table_index: 0, |
395 | number_of_tables: self.number_of_tables, |
396 | stream: Stream::new(self.data), |
397 | } |
398 | } |
399 | } |
400 | |
401 | /// An iterator over extended kerning subtables. |
402 | #[allow (missing_debug_implementations)] |
403 | #[derive (Clone)] |
404 | pub struct SubtablesIter<'a> { |
405 | /// The number of glyphs from the `maxp` table. |
406 | number_of_glyphs: NonZeroU16, |
407 | /// The current table index. |
408 | table_index: u32, |
409 | /// The total number of tables. |
410 | number_of_tables: u32, |
411 | /// Actual data. Starts right after the `kerx` header. |
412 | stream: Stream<'a>, |
413 | } |
414 | |
415 | impl<'a> Iterator for SubtablesIter<'a> { |
416 | type Item = Subtable<'a>; |
417 | |
418 | fn next(&mut self) -> Option<Self::Item> { |
419 | if self.table_index == self.number_of_tables { |
420 | return None; |
421 | } |
422 | |
423 | if self.stream.at_end() { |
424 | return None; |
425 | } |
426 | |
427 | let s = &mut self.stream; |
428 | |
429 | let table_len = s.read::<u32>()?; |
430 | let coverage = Coverage(s.read::<u8>()?); |
431 | s.skip::<u16>(); // unused |
432 | let raw_format = s.read::<u8>()?; |
433 | let tuple_count = s.read::<u32>()?; |
434 | |
435 | // Subtract the header size. |
436 | let data_len = usize::num_from(table_len).checked_sub(HEADER_SIZE)?; |
437 | let data = s.read_bytes(data_len)?; |
438 | |
439 | let format = match raw_format { |
440 | 0 => Subtable0::parse(data).map(Format::Format0)?, |
441 | 1 => Subtable1::parse(self.number_of_glyphs, data).map(Format::Format1)?, |
442 | 2 => Format::Format2(Subtable2(data)), |
443 | 4 => Subtable4::parse(self.number_of_glyphs, data).map(Format::Format4)?, |
444 | 6 => Format::Format6(Subtable6::parse(self.number_of_glyphs, data)), |
445 | _ => { |
446 | // Unknown format. |
447 | return None; |
448 | } |
449 | }; |
450 | |
451 | self.table_index += 1; |
452 | |
453 | Some(Subtable { |
454 | horizontal: coverage.is_horizontal(), |
455 | variable: coverage.is_variable(), |
456 | has_cross_stream: coverage.has_cross_stream(), |
457 | has_state_machine: raw_format == 1 || raw_format == 4, |
458 | tuple_count, |
459 | format, |
460 | }) |
461 | } |
462 | } |
463 | |
464 | /// An [Extended Kerning Table]( |
465 | /// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6kerx.html). |
466 | #[derive (Clone, Copy, Debug)] |
467 | pub struct Table<'a> { |
468 | /// A list of subtables. |
469 | pub subtables: Subtables<'a>, |
470 | } |
471 | |
472 | impl<'a> Table<'a> { |
473 | /// Parses a table from raw data. |
474 | /// |
475 | /// `number_of_glyphs` is from the `maxp` table. |
476 | pub fn parse(number_of_glyphs: NonZeroU16, data: &'a [u8]) -> Option<Self> { |
477 | let mut s: Stream<'_> = Stream::new(data); |
478 | s.skip::<u16>(); // version |
479 | s.skip::<u16>(); // padding |
480 | let number_of_tables: u32 = s.read::<u32>()?; |
481 | let subtables: Subtables<'_> = Subtables { |
482 | number_of_glyphs, |
483 | number_of_tables, |
484 | data: s.tail()?, |
485 | }; |
486 | |
487 | Some(Table { subtables }) |
488 | } |
489 | } |
490 | |