| 1 | //! A [Glyph Positioning Table](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos) |
| 2 | //! implementation. |
| 3 | |
| 4 | // A heavily modified port of https://github.com/harfbuzz/rustybuzz implementation |
| 5 | // originally written by https://github.com/laurmaedje |
| 6 | |
| 7 | use core::convert::TryFrom; |
| 8 | |
| 9 | use crate::opentype_layout::ChainedContextLookup; |
| 10 | use crate::opentype_layout::{Class, ClassDefinition, ContextLookup, Coverage, LookupSubtable}; |
| 11 | use crate::parser::{ |
| 12 | FromData, FromSlice, LazyArray16, LazyArray32, NumFrom, Offset, Offset16, Stream, |
| 13 | }; |
| 14 | use crate::GlyphId; |
| 15 | |
| 16 | /// A [Device Table]( |
| 17 | /// https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#devVarIdxTbls) |
| 18 | /// hinting values. |
| 19 | #[derive (Clone, Copy)] |
| 20 | pub struct HintingDevice<'a> { |
| 21 | start_size: u16, |
| 22 | end_size: u16, |
| 23 | delta_format: u16, |
| 24 | delta_values: LazyArray16<'a, u16>, |
| 25 | } |
| 26 | |
| 27 | impl HintingDevice<'_> { |
| 28 | /// Returns X-axis delta. |
| 29 | pub fn x_delta(&self, units_per_em: u16, pixels_per_em: Option<(u16, u16)>) -> Option<i32> { |
| 30 | let ppem = pixels_per_em.map(|(x, _)| x)?; |
| 31 | self.get_delta(ppem, units_per_em) |
| 32 | } |
| 33 | |
| 34 | /// Returns Y-axis delta. |
| 35 | pub fn y_delta(&self, units_per_em: u16, pixels_per_em: Option<(u16, u16)>) -> Option<i32> { |
| 36 | let ppem = pixels_per_em.map(|(_, y)| y)?; |
| 37 | self.get_delta(ppem, units_per_em) |
| 38 | } |
| 39 | |
| 40 | fn get_delta(&self, ppem: u16, scale: u16) -> Option<i32> { |
| 41 | let f = self.delta_format; |
| 42 | debug_assert!(matches!(f, 1..=3)); |
| 43 | |
| 44 | if ppem == 0 || ppem < self.start_size || ppem > self.end_size { |
| 45 | return None; |
| 46 | } |
| 47 | |
| 48 | let s = ppem - self.start_size; |
| 49 | let byte = self.delta_values.get(s >> (4 - f))?; |
| 50 | let bits = byte >> (16 - (((s & ((1 << (4 - f)) - 1)) + 1) << f)); |
| 51 | let mask = 0xFFFF >> (16 - (1 << f)); |
| 52 | |
| 53 | let mut delta = i64::from(bits & mask); |
| 54 | if delta >= i64::from((mask + 1) >> 1) { |
| 55 | delta -= i64::from(mask + 1); |
| 56 | } |
| 57 | |
| 58 | i32::try_from(delta * i64::from(scale) / i64::from(ppem)).ok() |
| 59 | } |
| 60 | } |
| 61 | |
| 62 | impl core::fmt::Debug for HintingDevice<'_> { |
| 63 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
| 64 | write!(f, "HintingDevice {{ ... }}" ) |
| 65 | } |
| 66 | } |
| 67 | |
| 68 | /// A [Device Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#devVarIdxTbls) |
| 69 | /// indexes into [Item Variation Store]( |
| 70 | /// https://docs.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#IVS). |
| 71 | #[allow (missing_docs)] |
| 72 | #[derive (Clone, Copy, Debug)] |
| 73 | pub struct VariationDevice { |
| 74 | pub outer_index: u16, |
| 75 | pub inner_index: u16, |
| 76 | } |
| 77 | |
| 78 | /// A [Device Table]( |
| 79 | /// https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#devVarIdxTbls). |
| 80 | #[allow (missing_docs)] |
| 81 | #[derive (Clone, Copy, Debug)] |
| 82 | pub enum Device<'a> { |
| 83 | Hinting(HintingDevice<'a>), |
| 84 | Variation(VariationDevice), |
| 85 | } |
| 86 | |
| 87 | impl<'a> Device<'a> { |
| 88 | pub(crate) fn parse(data: &'a [u8]) -> Option<Self> { |
| 89 | let mut s = Stream::new(data); |
| 90 | let first = s.read::<u16>()?; |
| 91 | let second = s.read::<u16>()?; |
| 92 | let format = s.read::<u16>()?; |
| 93 | match format { |
| 94 | 1..=3 => { |
| 95 | let start_size = first; |
| 96 | let end_size = second; |
| 97 | let count = (1 + (end_size - start_size)) >> (4 - format); |
| 98 | let delta_values = s.read_array16(count)?; |
| 99 | Some(Self::Hinting(HintingDevice { |
| 100 | start_size, |
| 101 | end_size, |
| 102 | delta_format: format, |
| 103 | delta_values, |
| 104 | })) |
| 105 | } |
| 106 | 0x8000 => Some(Self::Variation(VariationDevice { |
| 107 | outer_index: first, |
| 108 | inner_index: second, |
| 109 | })), |
| 110 | _ => None, |
| 111 | } |
| 112 | } |
| 113 | } |
| 114 | |
| 115 | #[derive (Clone, Copy, Default, Debug)] |
| 116 | struct ValueFormatFlags(u8); |
| 117 | |
| 118 | #[rustfmt::skip] |
| 119 | impl ValueFormatFlags { |
| 120 | #[inline ] fn x_placement(self) -> bool { self.0 & 0x01 != 0 } |
| 121 | #[inline ] fn y_placement(self) -> bool { self.0 & 0x02 != 0 } |
| 122 | #[inline ] fn x_advance(self) -> bool { self.0 & 0x04 != 0 } |
| 123 | #[inline ] fn y_advance(self) -> bool { self.0 & 0x08 != 0 } |
| 124 | #[inline ] fn x_placement_device(self) -> bool { self.0 & 0x10 != 0 } |
| 125 | #[inline ] fn y_placement_device(self) -> bool { self.0 & 0x20 != 0 } |
| 126 | #[inline ] fn x_advance_device(self) -> bool { self.0 & 0x40 != 0 } |
| 127 | #[inline ] fn y_advance_device(self) -> bool { self.0 & 0x80 != 0 } |
| 128 | |
| 129 | // The ValueRecord struct constrain either i16 values or Offset16 offsets |
| 130 | // and the total size depend on how many flags are enabled. |
| 131 | fn size(self) -> usize { |
| 132 | // The high 8 bits are not used, so make sure we ignore them using 0xFF. |
| 133 | u16::SIZE * usize::num_from(self.0.count_ones()) |
| 134 | } |
| 135 | } |
| 136 | |
| 137 | impl FromData for ValueFormatFlags { |
| 138 | const SIZE: usize = 2; |
| 139 | |
| 140 | #[inline ] |
| 141 | fn parse(data: &[u8]) -> Option<Self> { |
| 142 | // There is no data in high 8 bits, so skip it. |
| 143 | Some(Self(data[1])) |
| 144 | } |
| 145 | } |
| 146 | |
| 147 | /// A [Value Record](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#value-record). |
| 148 | #[derive (Clone, Copy, Default, Debug)] |
| 149 | pub struct ValueRecord<'a> { |
| 150 | /// Horizontal adjustment for placement, in design units. |
| 151 | pub x_placement: i16, |
| 152 | /// Vertical adjustment for placement, in design units. |
| 153 | pub y_placement: i16, |
| 154 | /// Horizontal adjustment for advance, in design units — only used for horizontal layout. |
| 155 | pub x_advance: i16, |
| 156 | /// Vertical adjustment for advance, in design units — only used for vertical layout. |
| 157 | pub y_advance: i16, |
| 158 | |
| 159 | /// A [`Device`] table with horizontal adjustment for placement. |
| 160 | pub x_placement_device: Option<Device<'a>>, |
| 161 | /// A [`Device`] table with vertical adjustment for placement. |
| 162 | pub y_placement_device: Option<Device<'a>>, |
| 163 | /// A [`Device`] table with horizontal adjustment for advance. |
| 164 | pub x_advance_device: Option<Device<'a>>, |
| 165 | /// A [`Device`] table with vertical adjustment for advance. |
| 166 | pub y_advance_device: Option<Device<'a>>, |
| 167 | } |
| 168 | |
| 169 | impl<'a> ValueRecord<'a> { |
| 170 | // Returns `None` only on parsing error. |
| 171 | fn parse( |
| 172 | table_data: &'a [u8], |
| 173 | s: &mut Stream, |
| 174 | flags: ValueFormatFlags, |
| 175 | ) -> Option<ValueRecord<'a>> { |
| 176 | let mut record = ValueRecord::default(); |
| 177 | |
| 178 | if flags.x_placement() { |
| 179 | record.x_placement = s.read::<i16>()?; |
| 180 | } |
| 181 | |
| 182 | if flags.y_placement() { |
| 183 | record.y_placement = s.read::<i16>()?; |
| 184 | } |
| 185 | |
| 186 | if flags.x_advance() { |
| 187 | record.x_advance = s.read::<i16>()?; |
| 188 | } |
| 189 | |
| 190 | if flags.y_advance() { |
| 191 | record.y_advance = s.read::<i16>()?; |
| 192 | } |
| 193 | |
| 194 | if flags.x_placement_device() { |
| 195 | if let Some(offset) = s.read::<Option<Offset16>>()? { |
| 196 | record.x_placement_device = |
| 197 | table_data.get(offset.to_usize()..).and_then(Device::parse) |
| 198 | } |
| 199 | } |
| 200 | |
| 201 | if flags.y_placement_device() { |
| 202 | if let Some(offset) = s.read::<Option<Offset16>>()? { |
| 203 | record.y_placement_device = |
| 204 | table_data.get(offset.to_usize()..).and_then(Device::parse) |
| 205 | } |
| 206 | } |
| 207 | |
| 208 | if flags.x_advance_device() { |
| 209 | if let Some(offset) = s.read::<Option<Offset16>>()? { |
| 210 | record.x_advance_device = |
| 211 | table_data.get(offset.to_usize()..).and_then(Device::parse) |
| 212 | } |
| 213 | } |
| 214 | |
| 215 | if flags.y_advance_device() { |
| 216 | if let Some(offset) = s.read::<Option<Offset16>>()? { |
| 217 | record.y_advance_device = |
| 218 | table_data.get(offset.to_usize()..).and_then(Device::parse) |
| 219 | } |
| 220 | } |
| 221 | |
| 222 | Some(record) |
| 223 | } |
| 224 | } |
| 225 | |
| 226 | /// An array of |
| 227 | /// [Value Records](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#value-record). |
| 228 | #[derive (Clone, Copy)] |
| 229 | pub struct ValueRecordsArray<'a> { |
| 230 | // We have to store the original table data because ValueRecords can have |
| 231 | // a offset to Device tables and offset is from the beginning of the table. |
| 232 | table_data: &'a [u8], |
| 233 | // A slice that contains all ValueRecords. |
| 234 | data: &'a [u8], |
| 235 | // Number of records. |
| 236 | len: u16, |
| 237 | // Size of the single record. |
| 238 | value_len: usize, |
| 239 | // Flags, used during ValueRecord parsing. |
| 240 | flags: ValueFormatFlags, |
| 241 | } |
| 242 | |
| 243 | impl<'a> ValueRecordsArray<'a> { |
| 244 | fn parse( |
| 245 | table_data: &'a [u8], |
| 246 | count: u16, |
| 247 | flags: ValueFormatFlags, |
| 248 | s: &mut Stream<'a>, |
| 249 | ) -> Option<Self> { |
| 250 | Some(Self { |
| 251 | table_data, |
| 252 | flags, |
| 253 | len: count, |
| 254 | value_len: flags.size(), |
| 255 | data: s.read_bytes(usize::from(count) * flags.size())?, |
| 256 | }) |
| 257 | } |
| 258 | |
| 259 | /// Returns array's length. |
| 260 | #[inline ] |
| 261 | pub fn len(&self) -> u16 { |
| 262 | self.len |
| 263 | } |
| 264 | |
| 265 | /// Checks if the array is empty. |
| 266 | pub fn is_empty(&self) -> bool { |
| 267 | self.len == 0 |
| 268 | } |
| 269 | |
| 270 | /// Returns a [`ValueRecord`] at index. |
| 271 | pub fn get(&self, index: u16) -> Option<ValueRecord<'a>> { |
| 272 | let start = usize::from(index) * self.value_len; |
| 273 | let end = start + self.value_len; |
| 274 | let data = self.data.get(start..end)?; |
| 275 | let mut s = Stream::new(data); |
| 276 | ValueRecord::parse(self.table_data, &mut s, self.flags) |
| 277 | } |
| 278 | } |
| 279 | |
| 280 | impl core::fmt::Debug for ValueRecordsArray<'_> { |
| 281 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
| 282 | write!(f, "ValueRecordsArray {{ ... }}" ) |
| 283 | } |
| 284 | } |
| 285 | |
| 286 | /// A [Single Adjustment Positioning Subtable]( |
| 287 | /// https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#SP). |
| 288 | #[allow (missing_docs)] |
| 289 | #[derive (Clone, Copy, Debug)] |
| 290 | pub enum SingleAdjustment<'a> { |
| 291 | Format1 { |
| 292 | coverage: Coverage<'a>, |
| 293 | value: ValueRecord<'a>, |
| 294 | }, |
| 295 | Format2 { |
| 296 | coverage: Coverage<'a>, |
| 297 | values: ValueRecordsArray<'a>, |
| 298 | }, |
| 299 | } |
| 300 | |
| 301 | impl<'a> SingleAdjustment<'a> { |
| 302 | fn parse(data: &'a [u8]) -> Option<Self> { |
| 303 | let mut s = Stream::new(data); |
| 304 | match s.read::<u16>()? { |
| 305 | 1 => { |
| 306 | let coverage = Coverage::parse(s.read_at_offset16(data)?)?; |
| 307 | let flags = s.read::<ValueFormatFlags>()?; |
| 308 | let value = ValueRecord::parse(data, &mut s, flags)?; |
| 309 | Some(Self::Format1 { coverage, value }) |
| 310 | } |
| 311 | 2 => { |
| 312 | let coverage = Coverage::parse(s.read_at_offset16(data)?)?; |
| 313 | let flags = s.read::<ValueFormatFlags>()?; |
| 314 | let count = s.read::<u16>()?; |
| 315 | let values = ValueRecordsArray::parse(data, count, flags, &mut s)?; |
| 316 | Some(Self::Format2 { coverage, values }) |
| 317 | } |
| 318 | _ => None, |
| 319 | } |
| 320 | } |
| 321 | |
| 322 | /// Returns the subtable coverage. |
| 323 | #[inline ] |
| 324 | pub fn coverage(&self) -> Coverage<'a> { |
| 325 | match self { |
| 326 | Self::Format1 { coverage, .. } => *coverage, |
| 327 | Self::Format2 { coverage, .. } => *coverage, |
| 328 | } |
| 329 | } |
| 330 | } |
| 331 | |
| 332 | /// A [`ValueRecord`] pairs set used by [`PairAdjustment`]. |
| 333 | #[derive (Clone, Copy)] |
| 334 | pub struct PairSet<'a> { |
| 335 | data: &'a [u8], |
| 336 | flags: (ValueFormatFlags, ValueFormatFlags), |
| 337 | record_len: u8, |
| 338 | } |
| 339 | |
| 340 | impl<'a> PairSet<'a> { |
| 341 | fn parse(data: &'a [u8], flags: (ValueFormatFlags, ValueFormatFlags)) -> Option<Self> { |
| 342 | let mut s = Stream::new(data); |
| 343 | let count = s.read::<u16>()?; |
| 344 | // Max len is 34, so u8 is just enough. |
| 345 | let record_len = (GlyphId::SIZE + flags.0.size() + flags.1.size()) as u8; |
| 346 | let data = s.read_bytes(usize::from(count) * usize::from(record_len))?; |
| 347 | Some(Self { |
| 348 | data, |
| 349 | flags, |
| 350 | record_len, |
| 351 | }) |
| 352 | } |
| 353 | |
| 354 | #[inline ] |
| 355 | fn binary_search(&self, second: GlyphId) -> Option<&'a [u8]> { |
| 356 | // Based on Rust std implementation. |
| 357 | |
| 358 | let mut size = self.data.len() / usize::from(self.record_len); |
| 359 | if size == 0 { |
| 360 | return None; |
| 361 | } |
| 362 | |
| 363 | let get_record = |index| { |
| 364 | let start = index * usize::from(self.record_len); |
| 365 | let end = start + usize::from(self.record_len); |
| 366 | self.data.get(start..end) |
| 367 | }; |
| 368 | |
| 369 | let get_glyph = |data: &[u8]| GlyphId(u16::from_be_bytes([data[0], data[1]])); |
| 370 | |
| 371 | let mut base = 0; |
| 372 | while size > 1 { |
| 373 | let half = size / 2; |
| 374 | let mid = base + half; |
| 375 | // mid is always in [0, size), that means mid is >= 0 and < size. |
| 376 | // mid >= 0: by definition |
| 377 | // mid < size: mid = size / 2 + size / 4 + size / 8 ... |
| 378 | let cmp = get_glyph(get_record(mid)?).cmp(&second); |
| 379 | base = if cmp == core::cmp::Ordering::Greater { |
| 380 | base |
| 381 | } else { |
| 382 | mid |
| 383 | }; |
| 384 | size -= half; |
| 385 | } |
| 386 | |
| 387 | // base is always in [0, size) because base <= mid. |
| 388 | let value = get_record(base)?; |
| 389 | if get_glyph(value).cmp(&second) == core::cmp::Ordering::Equal { |
| 390 | Some(value) |
| 391 | } else { |
| 392 | None |
| 393 | } |
| 394 | } |
| 395 | |
| 396 | /// Returns a [`ValueRecord`] pair using the second glyph. |
| 397 | pub fn get(&self, second: GlyphId) -> Option<(ValueRecord<'a>, ValueRecord<'a>)> { |
| 398 | let record_data = self.binary_search(second)?; |
| 399 | let mut s = Stream::new(record_data); |
| 400 | s.skip::<GlyphId>(); |
| 401 | Some(( |
| 402 | ValueRecord::parse(self.data, &mut s, self.flags.0)?, |
| 403 | ValueRecord::parse(self.data, &mut s, self.flags.1)?, |
| 404 | )) |
| 405 | } |
| 406 | } |
| 407 | |
| 408 | impl core::fmt::Debug for PairSet<'_> { |
| 409 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
| 410 | write!(f, "PairSet {{ ... }}" ) |
| 411 | } |
| 412 | } |
| 413 | |
| 414 | // Essentially a `LazyOffsetArray16` but stores additional data required to parse [`PairSet`]. |
| 415 | |
| 416 | /// A list of [`PairSet`]s. |
| 417 | #[derive (Clone, Copy)] |
| 418 | pub struct PairSets<'a> { |
| 419 | data: &'a [u8], |
| 420 | // Zero offsets must be ignored, therefore we're using `Option<Offset16>`. |
| 421 | offsets: LazyArray16<'a, Option<Offset16>>, |
| 422 | flags: (ValueFormatFlags, ValueFormatFlags), |
| 423 | } |
| 424 | |
| 425 | impl<'a> PairSets<'a> { |
| 426 | fn new( |
| 427 | data: &'a [u8], |
| 428 | offsets: LazyArray16<'a, Option<Offset16>>, |
| 429 | flags: (ValueFormatFlags, ValueFormatFlags), |
| 430 | ) -> Self { |
| 431 | Self { |
| 432 | data, |
| 433 | offsets, |
| 434 | flags, |
| 435 | } |
| 436 | } |
| 437 | |
| 438 | /// Returns a value at `index`. |
| 439 | #[inline ] |
| 440 | pub fn get(&self, index: u16) -> Option<PairSet<'a>> { |
| 441 | let offset = self.offsets.get(index)??.to_usize(); |
| 442 | self.data |
| 443 | .get(offset..) |
| 444 | .and_then(|data| PairSet::parse(data, self.flags)) |
| 445 | } |
| 446 | |
| 447 | /// Returns array's length. |
| 448 | #[inline ] |
| 449 | pub fn len(&self) -> u16 { |
| 450 | self.offsets.len() |
| 451 | } |
| 452 | |
| 453 | /// Checks if the array is empty. |
| 454 | pub fn is_empty(&self) -> bool { |
| 455 | self.offsets.is_empty() |
| 456 | } |
| 457 | } |
| 458 | |
| 459 | impl core::fmt::Debug for PairSets<'_> { |
| 460 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
| 461 | write!(f, "PairSets {{ ... }}" ) |
| 462 | } |
| 463 | } |
| 464 | |
| 465 | /// A [`ValueRecord`] pairs matrix used by [`PairAdjustment`]. |
| 466 | #[derive (Clone, Copy)] |
| 467 | pub struct ClassMatrix<'a> { |
| 468 | // We have to store table's original slice, |
| 469 | // because offsets in ValueRecords are from the begging of the table. |
| 470 | table_data: &'a [u8], |
| 471 | matrix: &'a [u8], |
| 472 | counts: (u16, u16), |
| 473 | flags: (ValueFormatFlags, ValueFormatFlags), |
| 474 | record_len: u8, |
| 475 | } |
| 476 | |
| 477 | impl<'a> ClassMatrix<'a> { |
| 478 | fn parse( |
| 479 | table_data: &'a [u8], |
| 480 | counts: (u16, u16), |
| 481 | flags: (ValueFormatFlags, ValueFormatFlags), |
| 482 | s: &mut Stream<'a>, |
| 483 | ) -> Option<Self> { |
| 484 | let count = usize::num_from(u32::from(counts.0) * u32::from(counts.1)); |
| 485 | // Max len is 32, so u8 is just enough. |
| 486 | let record_len = (flags.0.size() + flags.1.size()) as u8; |
| 487 | let matrix = s.read_bytes(count * usize::from(record_len))?; |
| 488 | Some(Self { |
| 489 | table_data, |
| 490 | matrix, |
| 491 | counts, |
| 492 | flags, |
| 493 | record_len, |
| 494 | }) |
| 495 | } |
| 496 | |
| 497 | /// Returns a [`ValueRecord`] pair using specified classes. |
| 498 | pub fn get(&self, classes: (u16, u16)) -> Option<(ValueRecord<'a>, ValueRecord<'a>)> { |
| 499 | if classes.0 >= self.counts.0 || classes.1 >= self.counts.1 { |
| 500 | return None; |
| 501 | } |
| 502 | |
| 503 | let idx = usize::from(classes.0) * usize::from(self.counts.1) + usize::from(classes.1); |
| 504 | let record = self.matrix.get(idx * usize::from(self.record_len)..)?; |
| 505 | |
| 506 | let mut s = Stream::new(record); |
| 507 | Some(( |
| 508 | ValueRecord::parse(self.table_data, &mut s, self.flags.0)?, |
| 509 | ValueRecord::parse(self.table_data, &mut s, self.flags.1)?, |
| 510 | )) |
| 511 | } |
| 512 | } |
| 513 | |
| 514 | impl core::fmt::Debug for ClassMatrix<'_> { |
| 515 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
| 516 | write!(f, "ClassMatrix {{ ... }}" ) |
| 517 | } |
| 518 | } |
| 519 | |
| 520 | /// A [Pair Adjustment Positioning Subtable]( |
| 521 | /// https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#PP). |
| 522 | #[allow (missing_docs)] |
| 523 | #[derive (Clone, Copy, Debug)] |
| 524 | pub enum PairAdjustment<'a> { |
| 525 | Format1 { |
| 526 | coverage: Coverage<'a>, |
| 527 | sets: PairSets<'a>, |
| 528 | }, |
| 529 | Format2 { |
| 530 | coverage: Coverage<'a>, |
| 531 | classes: (ClassDefinition<'a>, ClassDefinition<'a>), |
| 532 | matrix: ClassMatrix<'a>, |
| 533 | }, |
| 534 | } |
| 535 | |
| 536 | impl<'a> PairAdjustment<'a> { |
| 537 | fn parse(data: &'a [u8]) -> Option<Self> { |
| 538 | let mut s = Stream::new(data); |
| 539 | match s.read::<u16>()? { |
| 540 | 1 => { |
| 541 | let coverage = Coverage::parse(s.read_at_offset16(data)?)?; |
| 542 | let flags = (s.read::<ValueFormatFlags>()?, s.read::<ValueFormatFlags>()?); |
| 543 | let count = s.read::<u16>()?; |
| 544 | let offsets = s.read_array16(count)?; |
| 545 | Some(Self::Format1 { |
| 546 | coverage, |
| 547 | sets: PairSets::new(data, offsets, flags), |
| 548 | }) |
| 549 | } |
| 550 | 2 => { |
| 551 | let coverage = Coverage::parse(s.read_at_offset16(data)?)?; |
| 552 | let flags = (s.read::<ValueFormatFlags>()?, s.read::<ValueFormatFlags>()?); |
| 553 | let classes = ( |
| 554 | ClassDefinition::parse(s.read_at_offset16(data)?)?, |
| 555 | ClassDefinition::parse(s.read_at_offset16(data)?)?, |
| 556 | ); |
| 557 | let counts = (s.read::<u16>()?, s.read::<u16>()?); |
| 558 | Some(Self::Format2 { |
| 559 | coverage, |
| 560 | classes, |
| 561 | matrix: ClassMatrix::parse(data, counts, flags, &mut s)?, |
| 562 | }) |
| 563 | } |
| 564 | _ => None, |
| 565 | } |
| 566 | } |
| 567 | |
| 568 | /// Returns the subtable coverage. |
| 569 | #[inline ] |
| 570 | pub fn coverage(&self) -> Coverage<'a> { |
| 571 | match self { |
| 572 | Self::Format1 { coverage, .. } => *coverage, |
| 573 | Self::Format2 { coverage, .. } => *coverage, |
| 574 | } |
| 575 | } |
| 576 | } |
| 577 | |
| 578 | #[derive (Clone, Copy)] |
| 579 | struct EntryExitRecord { |
| 580 | entry_anchor_offset: Option<Offset16>, |
| 581 | exit_anchor_offset: Option<Offset16>, |
| 582 | } |
| 583 | |
| 584 | impl FromData for EntryExitRecord { |
| 585 | const SIZE: usize = 4; |
| 586 | |
| 587 | #[inline ] |
| 588 | fn parse(data: &[u8]) -> Option<Self> { |
| 589 | let mut s: Stream<'_> = Stream::new(data); |
| 590 | Some(Self { |
| 591 | entry_anchor_offset: s.read::<Option<Offset16>>()?, |
| 592 | exit_anchor_offset: s.read::<Option<Offset16>>()?, |
| 593 | }) |
| 594 | } |
| 595 | } |
| 596 | |
| 597 | /// A list of entry and exit [`Anchor`] pairs. |
| 598 | #[derive (Clone, Copy)] |
| 599 | pub struct CursiveAnchorSet<'a> { |
| 600 | data: &'a [u8], |
| 601 | records: LazyArray16<'a, EntryExitRecord>, |
| 602 | } |
| 603 | |
| 604 | impl<'a> CursiveAnchorSet<'a> { |
| 605 | /// Returns an entry [`Anchor`] at index. |
| 606 | pub fn entry(&self, index: u16) -> Option<Anchor<'a>> { |
| 607 | let offset: usize = self.records.get(index)?.entry_anchor_offset?.to_usize(); |
| 608 | self.data.get(index:offset..).and_then(Anchor::parse) |
| 609 | } |
| 610 | |
| 611 | /// Returns an exit [`Anchor`] at index. |
| 612 | pub fn exit(&self, index: u16) -> Option<Anchor<'a>> { |
| 613 | let offset: usize = self.records.get(index)?.exit_anchor_offset?.to_usize(); |
| 614 | self.data.get(index:offset..).and_then(Anchor::parse) |
| 615 | } |
| 616 | |
| 617 | /// Returns the number of items. |
| 618 | pub fn len(&self) -> u16 { |
| 619 | self.records.len() |
| 620 | } |
| 621 | |
| 622 | /// Checks if the set is empty. |
| 623 | pub fn is_empty(&self) -> bool { |
| 624 | self.records.is_empty() |
| 625 | } |
| 626 | } |
| 627 | |
| 628 | impl core::fmt::Debug for CursiveAnchorSet<'_> { |
| 629 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
| 630 | write!(f, "CursiveAnchorSet {{ ... }}" ) |
| 631 | } |
| 632 | } |
| 633 | |
| 634 | /// A [Cursive Attachment Positioning Subtable]( |
| 635 | /// https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#CAP). |
| 636 | #[allow (missing_docs)] |
| 637 | #[derive (Clone, Copy, Debug)] |
| 638 | pub struct CursiveAdjustment<'a> { |
| 639 | pub coverage: Coverage<'a>, |
| 640 | pub sets: CursiveAnchorSet<'a>, |
| 641 | } |
| 642 | |
| 643 | impl<'a> CursiveAdjustment<'a> { |
| 644 | fn parse(data: &'a [u8]) -> Option<Self> { |
| 645 | let mut s: Stream<'_> = Stream::new(data); |
| 646 | match s.read::<u16>()? { |
| 647 | 1 => { |
| 648 | let coverage: Coverage<'_> = Coverage::parse(data:s.read_at_offset16(data)?)?; |
| 649 | let count: u16 = s.read::<u16>()?; |
| 650 | let records: LazyArray16<'_, EntryExitRecord> = s.read_array16(count)?; |
| 651 | Some(Self { |
| 652 | coverage, |
| 653 | sets: CursiveAnchorSet { data, records }, |
| 654 | }) |
| 655 | } |
| 656 | _ => None, |
| 657 | } |
| 658 | } |
| 659 | } |
| 660 | |
| 661 | /// A [Mark-to-Base Attachment Positioning Subtable]( |
| 662 | /// https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#MBP). |
| 663 | #[derive (Clone, Copy, Debug)] |
| 664 | pub struct MarkToBaseAdjustment<'a> { |
| 665 | /// A mark coverage. |
| 666 | pub mark_coverage: Coverage<'a>, |
| 667 | /// A base coverage. |
| 668 | pub base_coverage: Coverage<'a>, |
| 669 | /// A list of mark anchors. |
| 670 | pub marks: MarkArray<'a>, |
| 671 | /// An anchors matrix. |
| 672 | pub anchors: AnchorMatrix<'a>, |
| 673 | } |
| 674 | |
| 675 | impl<'a> MarkToBaseAdjustment<'a> { |
| 676 | fn parse(data: &'a [u8]) -> Option<Self> { |
| 677 | let mut s: Stream<'_> = Stream::new(data); |
| 678 | match s.read::<u16>()? { |
| 679 | 1 => { |
| 680 | let mark_coverage: Coverage<'_> = Coverage::parse(data:s.read_at_offset16(data)?)?; |
| 681 | let base_coverage: Coverage<'_> = Coverage::parse(data:s.read_at_offset16(data)?)?; |
| 682 | let class_count: u16 = s.read::<u16>()?; |
| 683 | let marks: MarkArray<'_> = MarkArray::parse(data:s.read_at_offset16(data)?)?; |
| 684 | let anchors: AnchorMatrix<'_> = AnchorMatrix::parse(data:s.read_at_offset16(data)?, cols:class_count)?; |
| 685 | Some(Self { |
| 686 | mark_coverage, |
| 687 | base_coverage, |
| 688 | marks, |
| 689 | anchors, |
| 690 | }) |
| 691 | } |
| 692 | _ => None, |
| 693 | } |
| 694 | } |
| 695 | } |
| 696 | |
| 697 | /// A [Mark-to-Ligature Attachment Positioning Subtable]( |
| 698 | /// https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#MLP). |
| 699 | #[allow (missing_docs)] |
| 700 | #[derive (Clone, Copy, Debug)] |
| 701 | pub struct MarkToLigatureAdjustment<'a> { |
| 702 | pub mark_coverage: Coverage<'a>, |
| 703 | pub ligature_coverage: Coverage<'a>, |
| 704 | pub marks: MarkArray<'a>, |
| 705 | pub ligature_array: LigatureArray<'a>, |
| 706 | } |
| 707 | |
| 708 | impl<'a> MarkToLigatureAdjustment<'a> { |
| 709 | fn parse(data: &'a [u8]) -> Option<Self> { |
| 710 | let mut s: Stream<'_> = Stream::new(data); |
| 711 | match s.read::<u16>()? { |
| 712 | 1 => { |
| 713 | let mark_coverage: Coverage<'_> = Coverage::parse(data:s.read_at_offset16(data)?)?; |
| 714 | let ligature_coverage: Coverage<'_> = Coverage::parse(data:s.read_at_offset16(data)?)?; |
| 715 | let class_count: u16 = s.read::<u16>()?; |
| 716 | let marks: MarkArray<'_> = MarkArray::parse(data:s.read_at_offset16(data)?)?; |
| 717 | let ligature_array: LigatureArray<'_> = LigatureArray::parse(data:s.read_at_offset16(data)?, class_count)?; |
| 718 | Some(Self { |
| 719 | mark_coverage, |
| 720 | ligature_coverage, |
| 721 | marks, |
| 722 | ligature_array, |
| 723 | }) |
| 724 | } |
| 725 | _ => None, |
| 726 | } |
| 727 | } |
| 728 | } |
| 729 | |
| 730 | /// An array or ligature anchor matrices. |
| 731 | #[derive (Clone, Copy)] |
| 732 | pub struct LigatureArray<'a> { |
| 733 | data: &'a [u8], |
| 734 | class_count: u16, |
| 735 | offsets: LazyArray16<'a, Offset16>, |
| 736 | } |
| 737 | |
| 738 | impl<'a> LigatureArray<'a> { |
| 739 | fn parse(data: &'a [u8], class_count: u16) -> Option<Self> { |
| 740 | let mut s = Stream::new(data); |
| 741 | let count = s.read::<u16>()?; |
| 742 | let offsets = s.read_array16(count)?; |
| 743 | Some(Self { |
| 744 | data, |
| 745 | class_count, |
| 746 | offsets, |
| 747 | }) |
| 748 | } |
| 749 | |
| 750 | /// Returns an [`AnchorMatrix`] at index. |
| 751 | pub fn get(&self, index: u16) -> Option<AnchorMatrix<'a>> { |
| 752 | let offset = self.offsets.get(index)?.to_usize(); |
| 753 | let data = self.data.get(offset..)?; |
| 754 | AnchorMatrix::parse(data, self.class_count) |
| 755 | } |
| 756 | |
| 757 | /// Returns the array length. |
| 758 | pub fn len(&self) -> u16 { |
| 759 | self.offsets.len() |
| 760 | } |
| 761 | |
| 762 | /// Checks if the array is empty. |
| 763 | pub fn is_empty(&self) -> bool { |
| 764 | self.offsets.is_empty() |
| 765 | } |
| 766 | } |
| 767 | |
| 768 | impl core::fmt::Debug for LigatureArray<'_> { |
| 769 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
| 770 | write!(f, "LigatureArray {{ ... }}" ) |
| 771 | } |
| 772 | } |
| 773 | |
| 774 | #[derive (Clone, Copy)] |
| 775 | struct MarkRecord { |
| 776 | class: Class, |
| 777 | mark_anchor: Offset16, |
| 778 | } |
| 779 | |
| 780 | impl FromData for MarkRecord { |
| 781 | const SIZE: usize = 4; |
| 782 | |
| 783 | #[inline ] |
| 784 | fn parse(data: &[u8]) -> Option<Self> { |
| 785 | let mut s: Stream<'_> = Stream::new(data); |
| 786 | Some(Self { |
| 787 | class: s.read::<Class>()?, |
| 788 | mark_anchor: s.read::<Offset16>()?, |
| 789 | }) |
| 790 | } |
| 791 | } |
| 792 | |
| 793 | /// A [Mark Array](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#mark-array-table). |
| 794 | #[derive (Clone, Copy)] |
| 795 | pub struct MarkArray<'a> { |
| 796 | data: &'a [u8], |
| 797 | array: LazyArray16<'a, MarkRecord>, |
| 798 | } |
| 799 | |
| 800 | impl<'a> MarkArray<'a> { |
| 801 | fn parse(data: &'a [u8]) -> Option<Self> { |
| 802 | let mut s = Stream::new(data); |
| 803 | let count = s.read::<u16>()?; |
| 804 | let array = s.read_array16(count)?; |
| 805 | Some(Self { data, array }) |
| 806 | } |
| 807 | |
| 808 | /// Returns contained data at index. |
| 809 | pub fn get(&self, index: u16) -> Option<(Class, Anchor<'a>)> { |
| 810 | let record = self.array.get(index)?; |
| 811 | let anchor = self |
| 812 | .data |
| 813 | .get(record.mark_anchor.to_usize()..) |
| 814 | .and_then(Anchor::parse)?; |
| 815 | Some((record.class, anchor)) |
| 816 | } |
| 817 | |
| 818 | /// Returns the array length. |
| 819 | pub fn len(&self) -> u16 { |
| 820 | self.array.len() |
| 821 | } |
| 822 | |
| 823 | /// Checks if the array is empty. |
| 824 | pub fn is_empty(&self) -> bool { |
| 825 | self.array.is_empty() |
| 826 | } |
| 827 | } |
| 828 | |
| 829 | impl core::fmt::Debug for MarkArray<'_> { |
| 830 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
| 831 | write!(f, "MarkArray {{ ... }}" ) |
| 832 | } |
| 833 | } |
| 834 | |
| 835 | /// An [Anchor Table](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#anchor-tables). |
| 836 | /// |
| 837 | /// The *Anchor Table Format 2: Design Units Plus Contour Point* is not supported. |
| 838 | #[derive (Clone, Copy, Debug)] |
| 839 | pub struct Anchor<'a> { |
| 840 | /// Horizontal value, in design units. |
| 841 | pub x: i16, |
| 842 | /// Vertical value, in design units. |
| 843 | pub y: i16, |
| 844 | /// A [`Device`] table with horizontal value. |
| 845 | pub x_device: Option<Device<'a>>, |
| 846 | /// A [`Device`] table with vertical value. |
| 847 | pub y_device: Option<Device<'a>>, |
| 848 | } |
| 849 | |
| 850 | impl<'a> Anchor<'a> { |
| 851 | fn parse(data: &'a [u8]) -> Option<Self> { |
| 852 | let mut s = Stream::new(data); |
| 853 | let format = s.read::<u16>()?; |
| 854 | if !matches!(format, 1..=3) { |
| 855 | return None; |
| 856 | } |
| 857 | |
| 858 | let mut table = Anchor { |
| 859 | x: s.read::<i16>()?, |
| 860 | y: s.read::<i16>()?, |
| 861 | x_device: None, |
| 862 | y_device: None, |
| 863 | }; |
| 864 | |
| 865 | // Note: Format 2 is not handled since there is currently no way to |
| 866 | // get a glyph contour point by index. |
| 867 | |
| 868 | if format == 3 { |
| 869 | table.x_device = s |
| 870 | .read::<Option<Offset16>>()? |
| 871 | .and_then(|offset| data.get(offset.to_usize()..)) |
| 872 | .and_then(Device::parse); |
| 873 | |
| 874 | table.y_device = s |
| 875 | .read::<Option<Offset16>>()? |
| 876 | .and_then(|offset| data.get(offset.to_usize()..)) |
| 877 | .and_then(Device::parse); |
| 878 | } |
| 879 | |
| 880 | Some(table) |
| 881 | } |
| 882 | } |
| 883 | |
| 884 | /// An [`Anchor`] parsing helper. |
| 885 | #[derive (Clone, Copy)] |
| 886 | pub struct AnchorMatrix<'a> { |
| 887 | data: &'a [u8], |
| 888 | /// Number of rows in the matrix. |
| 889 | pub rows: u16, |
| 890 | /// Number of columns in the matrix. |
| 891 | pub cols: u16, |
| 892 | matrix: LazyArray32<'a, Option<Offset16>>, |
| 893 | } |
| 894 | |
| 895 | impl<'a> AnchorMatrix<'a> { |
| 896 | fn parse(data: &'a [u8], cols: u16) -> Option<Self> { |
| 897 | let mut s: Stream<'_> = Stream::new(data); |
| 898 | let rows: u16 = s.read::<u16>()?; |
| 899 | let count: u32 = u32::from(rows) * u32::from(cols); |
| 900 | let matrix: LazyArray32<'_, Option> = s.read_array32(count)?; |
| 901 | Some(Self { |
| 902 | data, |
| 903 | rows, |
| 904 | cols, |
| 905 | matrix, |
| 906 | }) |
| 907 | } |
| 908 | |
| 909 | /// Returns an [`Anchor`] at position. |
| 910 | pub fn get(&self, row: u16, col: u16) -> Option<Anchor> { |
| 911 | let idx: u32 = u32::from(row) * u32::from(self.cols) + u32::from(col); |
| 912 | let offset: usize = self.matrix.get(index:idx)??.to_usize(); |
| 913 | Anchor::parse(self.data.get(index:offset..)?) |
| 914 | } |
| 915 | } |
| 916 | |
| 917 | impl core::fmt::Debug for AnchorMatrix<'_> { |
| 918 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
| 919 | write!(f, "AnchorMatrix {{ ... }}" ) |
| 920 | } |
| 921 | } |
| 922 | |
| 923 | /// A [Mark-to-Mark Attachment Positioning Subtable]( |
| 924 | /// https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#MMP). |
| 925 | #[allow (missing_docs)] |
| 926 | #[derive (Clone, Copy, Debug)] |
| 927 | pub struct MarkToMarkAdjustment<'a> { |
| 928 | pub mark1_coverage: Coverage<'a>, |
| 929 | pub mark2_coverage: Coverage<'a>, |
| 930 | pub marks: MarkArray<'a>, |
| 931 | pub mark2_matrix: AnchorMatrix<'a>, |
| 932 | } |
| 933 | |
| 934 | impl<'a> MarkToMarkAdjustment<'a> { |
| 935 | fn parse(data: &'a [u8]) -> Option<Self> { |
| 936 | let mut s: Stream<'_> = Stream::new(data); |
| 937 | match s.read::<u16>()? { |
| 938 | 1 => { |
| 939 | let mark1_coverage: Coverage<'_> = Coverage::parse(data:s.read_at_offset16(data)?)?; |
| 940 | let mark2_coverage: Coverage<'_> = Coverage::parse(data:s.read_at_offset16(data)?)?; |
| 941 | let class_count: u16 = s.read::<u16>()?; |
| 942 | let marks: MarkArray<'_> = MarkArray::parse(data:s.read_at_offset16(data)?)?; |
| 943 | let mark2_matrix: AnchorMatrix<'_> = AnchorMatrix::parse(data:s.read_at_offset16(data)?, cols:class_count)?; |
| 944 | Some(Self { |
| 945 | mark1_coverage, |
| 946 | mark2_coverage, |
| 947 | marks, |
| 948 | mark2_matrix, |
| 949 | }) |
| 950 | } |
| 951 | _ => None, |
| 952 | } |
| 953 | } |
| 954 | } |
| 955 | |
| 956 | /// A glyph positioning |
| 957 | /// [lookup subtable](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#table-organization) |
| 958 | /// enumeration. |
| 959 | #[allow (missing_docs)] |
| 960 | #[derive (Clone, Copy, Debug)] |
| 961 | pub enum PositioningSubtable<'a> { |
| 962 | Single(SingleAdjustment<'a>), |
| 963 | Pair(PairAdjustment<'a>), |
| 964 | Cursive(CursiveAdjustment<'a>), |
| 965 | MarkToBase(MarkToBaseAdjustment<'a>), |
| 966 | MarkToLigature(MarkToLigatureAdjustment<'a>), |
| 967 | MarkToMark(MarkToMarkAdjustment<'a>), |
| 968 | Context(ContextLookup<'a>), |
| 969 | ChainContext(ChainedContextLookup<'a>), |
| 970 | } |
| 971 | |
| 972 | impl<'a> LookupSubtable<'a> for PositioningSubtable<'a> { |
| 973 | fn parse(data: &'a [u8], kind: u16) -> Option<Self> { |
| 974 | match kind { |
| 975 | 1 => SingleAdjustment::parse(data).map(Self::Single), |
| 976 | 2 => PairAdjustment::parse(data).map(Self::Pair), |
| 977 | 3 => CursiveAdjustment::parse(data).map(Self::Cursive), |
| 978 | 4 => MarkToBaseAdjustment::parse(data).map(Self::MarkToBase), |
| 979 | 5 => MarkToLigatureAdjustment::parse(data).map(Self::MarkToLigature), |
| 980 | 6 => MarkToMarkAdjustment::parse(data).map(Self::MarkToMark), |
| 981 | 7 => ContextLookup::parse(data).map(Self::Context), |
| 982 | 8 => ChainedContextLookup::parse(data).map(Self::ChainContext), |
| 983 | 9 => crate::ggg::parse_extension_lookup(data, Self::parse), |
| 984 | _ => None, |
| 985 | } |
| 986 | } |
| 987 | } |
| 988 | |
| 989 | impl<'a> PositioningSubtable<'a> { |
| 990 | /// Returns the subtable coverage. |
| 991 | #[inline ] |
| 992 | pub fn coverage(&self) -> Coverage<'a> { |
| 993 | match self { |
| 994 | Self::Single(t: &SingleAdjustment<'a>) => t.coverage(), |
| 995 | Self::Pair(t: &PairAdjustment<'a>) => t.coverage(), |
| 996 | Self::Cursive(t: &CursiveAdjustment<'a>) => t.coverage, |
| 997 | Self::MarkToBase(t: &MarkToBaseAdjustment<'a>) => t.mark_coverage, |
| 998 | Self::MarkToLigature(t: &MarkToLigatureAdjustment<'a>) => t.mark_coverage, |
| 999 | Self::MarkToMark(t: &MarkToMarkAdjustment<'a>) => t.mark1_coverage, |
| 1000 | Self::Context(t: &ContextLookup<'a>) => t.coverage(), |
| 1001 | Self::ChainContext(t: &ChainedContextLookup<'a>) => t.coverage(), |
| 1002 | } |
| 1003 | } |
| 1004 | } |
| 1005 | |