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