| 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 | |