// THIS FILE IS AUTOGENERATED. // Any changes to this file will be overwritten. // For more information about how codegen works, see font-codegen/README.md #[allow(unused_imports)] use crate::codegen_prelude::*; /// [Class Definition Table Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#class-definition-table-format-1) /// [GPOS Version 1.0](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#gpos-header) #[derive(Debug, Clone, Copy)] #[doc(hidden)] pub struct GposMarker { feature_variations_offset_byte_start: Option, } impl GposMarker { pub fn version_byte_range(&self) -> Range { let start = 0; start..start + MajorMinor::RAW_BYTE_LEN } pub fn script_list_offset_byte_range(&self) -> Range { let start = self.version_byte_range().end; start..start + Offset16::RAW_BYTE_LEN } pub fn feature_list_offset_byte_range(&self) -> Range { let start = self.script_list_offset_byte_range().end; start..start + Offset16::RAW_BYTE_LEN } pub fn lookup_list_offset_byte_range(&self) -> Range { let start = self.feature_list_offset_byte_range().end; start..start + Offset16::RAW_BYTE_LEN } pub fn feature_variations_offset_byte_range(&self) -> Option> { let start = self.feature_variations_offset_byte_start?; Some(start..start + Offset32::RAW_BYTE_LEN) } } impl MinByteRange for GposMarker { fn min_byte_range(&self) -> Range { 0..self.lookup_list_offset_byte_range().end } } impl TopLevelTable for Gpos<'_> { /// `GPOS` const TAG: Tag = Tag::new(b"GPOS"); } impl<'a> FontRead<'a> for Gpos<'a> { fn read(data: FontData<'a>) -> Result { let mut cursor = data.cursor(); let version: MajorMinor = cursor.read()?; cursor.advance::(); cursor.advance::(); cursor.advance::(); let feature_variations_offset_byte_start = version .compatible((1u16, 1u16)) .then(|| cursor.position()) .transpose()?; version .compatible((1u16, 1u16)) .then(|| cursor.advance::()); cursor.finish(GposMarker { feature_variations_offset_byte_start, }) } } /// [Class Definition Table Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#class-definition-table-format-1) /// [GPOS Version 1.0](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#gpos-header) pub type Gpos<'a> = TableRef<'a, GposMarker>; #[allow(clippy::needless_lifetimes)] impl<'a> Gpos<'a> { /// The major and minor version of the GPOS table, as a tuple (u16, u16) pub fn version(&self) -> MajorMinor { let range = self.shape.version_byte_range(); self.data.read_at(range.start).unwrap() } /// Offset to ScriptList table, from beginning of GPOS table pub fn script_list_offset(&self) -> Offset16 { let range = self.shape.script_list_offset_byte_range(); self.data.read_at(range.start).unwrap() } /// Attempt to resolve [`script_list_offset`][Self::script_list_offset]. pub fn script_list(&self) -> Result, ReadError> { let data = self.data; self.script_list_offset().resolve(data) } /// Offset to FeatureList table, from beginning of GPOS table pub fn feature_list_offset(&self) -> Offset16 { let range = self.shape.feature_list_offset_byte_range(); self.data.read_at(range.start).unwrap() } /// Attempt to resolve [`feature_list_offset`][Self::feature_list_offset]. pub fn feature_list(&self) -> Result, ReadError> { let data = self.data; self.feature_list_offset().resolve(data) } /// Offset to LookupList table, from beginning of GPOS table pub fn lookup_list_offset(&self) -> Offset16 { let range = self.shape.lookup_list_offset_byte_range(); self.data.read_at(range.start).unwrap() } /// Attempt to resolve [`lookup_list_offset`][Self::lookup_list_offset]. pub fn lookup_list(&self) -> Result, ReadError> { let data = self.data; self.lookup_list_offset().resolve(data) } pub fn feature_variations_offset(&self) -> Option> { let range = self.shape.feature_variations_offset_byte_range()?; Some(self.data.read_at(range.start).unwrap()) } /// Attempt to resolve [`feature_variations_offset`][Self::feature_variations_offset]. pub fn feature_variations(&self) -> Option, ReadError>> { let data = self.data; self.feature_variations_offset().map(|x| x.resolve(data))? } } #[cfg(feature = "experimental_traverse")] impl<'a> SomeTable<'a> for Gpos<'a> { fn type_name(&self) -> &str { "Gpos" } fn get_field(&self, idx: usize) -> Option> { let version = self.version(); match idx { 0usize => Some(Field::new("version", self.version())), 1usize => Some(Field::new( "script_list_offset", FieldType::offset(self.script_list_offset(), self.script_list()), )), 2usize => Some(Field::new( "feature_list_offset", FieldType::offset(self.feature_list_offset(), self.feature_list()), )), 3usize => Some(Field::new( "lookup_list_offset", FieldType::offset(self.lookup_list_offset(), self.lookup_list()), )), 4usize if version.compatible((1u16, 1u16)) => Some(Field::new( "feature_variations_offset", FieldType::offset( self.feature_variations_offset().unwrap(), self.feature_variations(), ), )), _ => None, } } } #[cfg(feature = "experimental_traverse")] #[allow(clippy::needless_lifetimes)] impl<'a> std::fmt::Debug for Gpos<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { (self as &dyn SomeTable<'a>).fmt(f) } } /// A [GPOS Lookup](https://learn.microsoft.com/en-us/typography/opentype/spec/gpos#gsubLookupTypeEnum) subtable. pub enum PositionLookup<'a> { Single(Lookup<'a, SinglePos<'a>>), Pair(Lookup<'a, PairPos<'a>>), Cursive(Lookup<'a, CursivePosFormat1<'a>>), MarkToBase(Lookup<'a, MarkBasePosFormat1<'a>>), MarkToLig(Lookup<'a, MarkLigPosFormat1<'a>>), MarkToMark(Lookup<'a, MarkMarkPosFormat1<'a>>), Contextual(Lookup<'a, PositionSequenceContext<'a>>), ChainContextual(Lookup<'a, PositionChainContext<'a>>), Extension(Lookup<'a, ExtensionSubtable<'a>>), } impl<'a> FontRead<'a> for PositionLookup<'a> { fn read(bytes: FontData<'a>) -> Result { let untyped = Lookup::read(bytes)?; match untyped.lookup_type() { 1 => Ok(PositionLookup::Single(untyped.into_concrete())), 2 => Ok(PositionLookup::Pair(untyped.into_concrete())), 3 => Ok(PositionLookup::Cursive(untyped.into_concrete())), 4 => Ok(PositionLookup::MarkToBase(untyped.into_concrete())), 5 => Ok(PositionLookup::MarkToLig(untyped.into_concrete())), 6 => Ok(PositionLookup::MarkToMark(untyped.into_concrete())), 7 => Ok(PositionLookup::Contextual(untyped.into_concrete())), 8 => Ok(PositionLookup::ChainContextual(untyped.into_concrete())), 9 => Ok(PositionLookup::Extension(untyped.into_concrete())), other => Err(ReadError::InvalidFormat(other.into())), } } } impl<'a> PositionLookup<'a> { #[allow(dead_code)] /// Return the inner table, removing the specific generics. /// /// This lets us return a single concrete type we can call methods on. pub(crate) fn of_unit_type(&self) -> Lookup<'a, ()> { match self { PositionLookup::Single(inner) => inner.of_unit_type(), PositionLookup::Pair(inner) => inner.of_unit_type(), PositionLookup::Cursive(inner) => inner.of_unit_type(), PositionLookup::MarkToBase(inner) => inner.of_unit_type(), PositionLookup::MarkToLig(inner) => inner.of_unit_type(), PositionLookup::MarkToMark(inner) => inner.of_unit_type(), PositionLookup::Contextual(inner) => inner.of_unit_type(), PositionLookup::ChainContextual(inner) => inner.of_unit_type(), PositionLookup::Extension(inner) => inner.of_unit_type(), } } } #[cfg(feature = "experimental_traverse")] impl<'a> PositionLookup<'a> { fn dyn_inner(&self) -> &(dyn SomeTable<'a> + 'a) { match self { PositionLookup::Single(table) => table, PositionLookup::Pair(table) => table, PositionLookup::Cursive(table) => table, PositionLookup::MarkToBase(table) => table, PositionLookup::MarkToLig(table) => table, PositionLookup::MarkToMark(table) => table, PositionLookup::Contextual(table) => table, PositionLookup::ChainContextual(table) => table, PositionLookup::Extension(table) => table, } } } #[cfg(feature = "experimental_traverse")] impl<'a> SomeTable<'a> for PositionLookup<'a> { fn get_field(&self, idx: usize) -> Option> { self.dyn_inner().get_field(idx) } fn type_name(&self) -> &str { self.dyn_inner().type_name() } } #[cfg(feature = "experimental_traverse")] impl std::fmt::Debug for PositionLookup<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.dyn_inner().fmt(f) } } /// See [ValueRecord] #[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, bytemuck :: AnyBitPattern)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[repr(transparent)] pub struct ValueFormat { bits: u16, } impl ValueFormat { /// Includes horizontal adjustment for placement pub const X_PLACEMENT: Self = Self { bits: 0x0001 }; /// Includes vertical adjustment for placement pub const Y_PLACEMENT: Self = Self { bits: 0x0002 }; /// Includes horizontal adjustment for advance pub const X_ADVANCE: Self = Self { bits: 0x0004 }; /// Includes vertical adjustment for advance pub const Y_ADVANCE: Self = Self { bits: 0x0008 }; /// Includes Device table (non-variable font) / VariationIndex /// table (variable font) for horizontal placement pub const X_PLACEMENT_DEVICE: Self = Self { bits: 0x0010 }; /// Includes Device table (non-variable font) / VariationIndex /// table (variable font) for vertical placement pub const Y_PLACEMENT_DEVICE: Self = Self { bits: 0x0020 }; /// Includes Device table (non-variable font) / VariationIndex /// table (variable font) for horizontal advance pub const X_ADVANCE_DEVICE: Self = Self { bits: 0x0040 }; /// Includes Device table (non-variable font) / VariationIndex /// table (variable font) for vertical advance pub const Y_ADVANCE_DEVICE: Self = Self { bits: 0x0080 }; } impl ValueFormat { /// Returns an empty set of flags. #[inline] pub const fn empty() -> Self { Self { bits: 0 } } /// Returns the set containing all flags. #[inline] pub const fn all() -> Self { Self { bits: Self::X_PLACEMENT.bits | Self::Y_PLACEMENT.bits | Self::X_ADVANCE.bits | Self::Y_ADVANCE.bits | Self::X_PLACEMENT_DEVICE.bits | Self::Y_PLACEMENT_DEVICE.bits | Self::X_ADVANCE_DEVICE.bits | Self::Y_ADVANCE_DEVICE.bits, } } /// Returns the raw value of the flags currently stored. #[inline] pub const fn bits(&self) -> u16 { self.bits } /// Convert from underlying bit representation, unless that /// representation contains bits that do not correspond to a flag. #[inline] pub const fn from_bits(bits: u16) -> Option { if (bits & !Self::all().bits()) == 0 { Some(Self { bits }) } else { None } } /// Convert from underlying bit representation, dropping any bits /// that do not correspond to flags. #[inline] pub const fn from_bits_truncate(bits: u16) -> Self { Self { bits: bits & Self::all().bits, } } /// Returns `true` if no flags are currently stored. #[inline] pub const fn is_empty(&self) -> bool { self.bits() == Self::empty().bits() } /// Returns `true` if there are flags common to both `self` and `other`. #[inline] pub const fn intersects(&self, other: Self) -> bool { !(Self { bits: self.bits & other.bits, }) .is_empty() } /// Returns `true` if all of the flags in `other` are contained within `self`. #[inline] pub const fn contains(&self, other: Self) -> bool { (self.bits & other.bits) == other.bits } /// Inserts the specified flags in-place. #[inline] pub fn insert(&mut self, other: Self) { self.bits |= other.bits; } /// Removes the specified flags in-place. #[inline] pub fn remove(&mut self, other: Self) { self.bits &= !other.bits; } /// Toggles the specified flags in-place. #[inline] pub fn toggle(&mut self, other: Self) { self.bits ^= other.bits; } /// Returns the intersection between the flags in `self` and /// `other`. /// /// Specifically, the returned set contains only the flags which are /// present in *both* `self` *and* `other`. /// /// This is equivalent to using the `&` operator (e.g. /// [`ops::BitAnd`]), as in `flags & other`. /// /// [`ops::BitAnd`]: https://doc.rust-lang.org/std/ops/trait.BitAnd.html #[inline] #[must_use] pub const fn intersection(self, other: Self) -> Self { Self { bits: self.bits & other.bits, } } /// Returns the union of between the flags in `self` and `other`. /// /// Specifically, the returned set contains all flags which are /// present in *either* `self` *or* `other`, including any which are /// present in both. /// /// This is equivalent to using the `|` operator (e.g. /// [`ops::BitOr`]), as in `flags | other`. /// /// [`ops::BitOr`]: https://doc.rust-lang.org/std/ops/trait.BitOr.html #[inline] #[must_use] pub const fn union(self, other: Self) -> Self { Self { bits: self.bits | other.bits, } } /// Returns the difference between the flags in `self` and `other`. /// /// Specifically, the returned set contains all flags present in /// `self`, except for the ones present in `other`. /// /// It is also conceptually equivalent to the "bit-clear" operation: /// `flags & !other` (and this syntax is also supported). /// /// This is equivalent to using the `-` operator (e.g. /// [`ops::Sub`]), as in `flags - other`. /// /// [`ops::Sub`]: https://doc.rust-lang.org/std/ops/trait.Sub.html #[inline] #[must_use] pub const fn difference(self, other: Self) -> Self { Self { bits: self.bits & !other.bits, } } } impl std::ops::BitOr for ValueFormat { type Output = Self; /// Returns the union of the two sets of flags. #[inline] fn bitor(self, other: ValueFormat) -> Self { Self { bits: self.bits | other.bits, } } } impl std::ops::BitOrAssign for ValueFormat { /// Adds the set of flags. #[inline] fn bitor_assign(&mut self, other: Self) { self.bits |= other.bits; } } impl std::ops::BitXor for ValueFormat { type Output = Self; /// Returns the left flags, but with all the right flags toggled. #[inline] fn bitxor(self, other: Self) -> Self { Self { bits: self.bits ^ other.bits, } } } impl std::ops::BitXorAssign for ValueFormat { /// Toggles the set of flags. #[inline] fn bitxor_assign(&mut self, other: Self) { self.bits ^= other.bits; } } impl std::ops::BitAnd for ValueFormat { type Output = Self; /// Returns the intersection between the two sets of flags. #[inline] fn bitand(self, other: Self) -> Self { Self { bits: self.bits & other.bits, } } } impl std::ops::BitAndAssign for ValueFormat { /// Disables all flags disabled in the set. #[inline] fn bitand_assign(&mut self, other: Self) { self.bits &= other.bits; } } impl std::ops::Sub for ValueFormat { type Output = Self; /// Returns the set difference of the two sets of flags. #[inline] fn sub(self, other: Self) -> Self { Self { bits: self.bits & !other.bits, } } } impl std::ops::SubAssign for ValueFormat { /// Disables all flags enabled in the set. #[inline] fn sub_assign(&mut self, other: Self) { self.bits &= !other.bits; } } impl std::ops::Not for ValueFormat { type Output = Self; /// Returns the complement of this set of flags. #[inline] fn not(self) -> Self { Self { bits: !self.bits } & Self::all() } } impl std::fmt::Debug for ValueFormat { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { let members: &[(&str, Self)] = &[ ("X_PLACEMENT", Self::X_PLACEMENT), ("Y_PLACEMENT", Self::Y_PLACEMENT), ("X_ADVANCE", Self::X_ADVANCE), ("Y_ADVANCE", Self::Y_ADVANCE), ("X_PLACEMENT_DEVICE", Self::X_PLACEMENT_DEVICE), ("Y_PLACEMENT_DEVICE", Self::Y_PLACEMENT_DEVICE), ("X_ADVANCE_DEVICE", Self::X_ADVANCE_DEVICE), ("Y_ADVANCE_DEVICE", Self::Y_ADVANCE_DEVICE), ]; let mut first = true; for (name, value) in members { if self.contains(*value) { if !first { f.write_str(" | ")?; } first = false; f.write_str(name)?; } } if first { f.write_str("(empty)")?; } Ok(()) } } impl std::fmt::Binary for ValueFormat { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { std::fmt::Binary::fmt(&self.bits, f) } } impl std::fmt::Octal for ValueFormat { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { std::fmt::Octal::fmt(&self.bits, f) } } impl std::fmt::LowerHex for ValueFormat { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { std::fmt::LowerHex::fmt(&self.bits, f) } } impl std::fmt::UpperHex for ValueFormat { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { std::fmt::UpperHex::fmt(&self.bits, f) } } impl font_types::Scalar for ValueFormat { type Raw = ::Raw; fn to_raw(self) -> Self::Raw { self.bits().to_raw() } fn from_raw(raw: Self::Raw) -> Self { let t = ::from_raw(raw); Self::from_bits_truncate(t) } } #[cfg(feature = "experimental_traverse")] impl<'a> From for FieldType<'a> { fn from(src: ValueFormat) -> FieldType<'a> { src.bits().into() } } /// [Anchor Tables](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#anchor-tables) /// position one glyph with respect to another. #[derive(Clone)] pub enum AnchorTable<'a> { Format1(AnchorFormat1<'a>), Format2(AnchorFormat2<'a>), Format3(AnchorFormat3<'a>), } impl<'a> AnchorTable<'a> { ///Return the `FontData` used to resolve offsets for this table. pub fn offset_data(&self) -> FontData<'a> { match self { Self::Format1(item) => item.offset_data(), Self::Format2(item) => item.offset_data(), Self::Format3(item) => item.offset_data(), } } /// Format identifier, = 1 pub fn anchor_format(&self) -> u16 { match self { Self::Format1(item) => item.anchor_format(), Self::Format2(item) => item.anchor_format(), Self::Format3(item) => item.anchor_format(), } } /// Horizontal value, in design units pub fn x_coordinate(&self) -> i16 { match self { Self::Format1(item) => item.x_coordinate(), Self::Format2(item) => item.x_coordinate(), Self::Format3(item) => item.x_coordinate(), } } /// Vertical value, in design units pub fn y_coordinate(&self) -> i16 { match self { Self::Format1(item) => item.y_coordinate(), Self::Format2(item) => item.y_coordinate(), Self::Format3(item) => item.y_coordinate(), } } } impl<'a> FontRead<'a> for AnchorTable<'a> { fn read(data: FontData<'a>) -> Result { let format: u16 = data.read_at(0usize)?; match format { AnchorFormat1Marker::FORMAT => Ok(Self::Format1(FontRead::read(data)?)), AnchorFormat2Marker::FORMAT => Ok(Self::Format2(FontRead::read(data)?)), AnchorFormat3Marker::FORMAT => Ok(Self::Format3(FontRead::read(data)?)), other => Err(ReadError::InvalidFormat(other.into())), } } } impl MinByteRange for AnchorTable<'_> { fn min_byte_range(&self) -> Range { match self { Self::Format1(item) => item.min_byte_range(), Self::Format2(item) => item.min_byte_range(), Self::Format3(item) => item.min_byte_range(), } } } #[cfg(feature = "experimental_traverse")] impl<'a> AnchorTable<'a> { fn dyn_inner<'b>(&'b self) -> &'b dyn SomeTable<'a> { match self { Self::Format1(table) => table, Self::Format2(table) => table, Self::Format3(table) => table, } } } #[cfg(feature = "experimental_traverse")] impl std::fmt::Debug for AnchorTable<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.dyn_inner().fmt(f) } } #[cfg(feature = "experimental_traverse")] impl<'a> SomeTable<'a> for AnchorTable<'a> { fn type_name(&self) -> &str { self.dyn_inner().type_name() } fn get_field(&self, idx: usize) -> Option> { self.dyn_inner().get_field(idx) } } impl Format for AnchorFormat1Marker { const FORMAT: u16 = 1; } /// [Anchor Table Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#anchor-table-format-1-design-units): Design Units #[derive(Debug, Clone, Copy)] #[doc(hidden)] pub struct AnchorFormat1Marker {} impl AnchorFormat1Marker { pub fn anchor_format_byte_range(&self) -> Range { let start = 0; start..start + u16::RAW_BYTE_LEN } pub fn x_coordinate_byte_range(&self) -> Range { let start = self.anchor_format_byte_range().end; start..start + i16::RAW_BYTE_LEN } pub fn y_coordinate_byte_range(&self) -> Range { let start = self.x_coordinate_byte_range().end; start..start + i16::RAW_BYTE_LEN } } impl MinByteRange for AnchorFormat1Marker { fn min_byte_range(&self) -> Range { 0..self.y_coordinate_byte_range().end } } impl<'a> FontRead<'a> for AnchorFormat1<'a> { fn read(data: FontData<'a>) -> Result { let mut cursor = data.cursor(); cursor.advance::(); cursor.advance::(); cursor.advance::(); cursor.finish(AnchorFormat1Marker {}) } } /// [Anchor Table Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#anchor-table-format-1-design-units): Design Units pub type AnchorFormat1<'a> = TableRef<'a, AnchorFormat1Marker>; #[allow(clippy::needless_lifetimes)] impl<'a> AnchorFormat1<'a> { /// Format identifier, = 1 pub fn anchor_format(&self) -> u16 { let range = self.shape.anchor_format_byte_range(); self.data.read_at(range.start).unwrap() } /// Horizontal value, in design units pub fn x_coordinate(&self) -> i16 { let range = self.shape.x_coordinate_byte_range(); self.data.read_at(range.start).unwrap() } /// Vertical value, in design units pub fn y_coordinate(&self) -> i16 { let range = self.shape.y_coordinate_byte_range(); self.data.read_at(range.start).unwrap() } } #[cfg(feature = "experimental_traverse")] impl<'a> SomeTable<'a> for AnchorFormat1<'a> { fn type_name(&self) -> &str { "AnchorFormat1" } fn get_field(&self, idx: usize) -> Option> { match idx { 0usize => Some(Field::new("anchor_format", self.anchor_format())), 1usize => Some(Field::new("x_coordinate", self.x_coordinate())), 2usize => Some(Field::new("y_coordinate", self.y_coordinate())), _ => None, } } } #[cfg(feature = "experimental_traverse")] #[allow(clippy::needless_lifetimes)] impl<'a> std::fmt::Debug for AnchorFormat1<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { (self as &dyn SomeTable<'a>).fmt(f) } } impl Format for AnchorFormat2Marker { const FORMAT: u16 = 2; } /// [Anchor Table Format 2](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#anchor-table-format-2-design-units-plus-contour-point): Design Units Plus Contour Point #[derive(Debug, Clone, Copy)] #[doc(hidden)] pub struct AnchorFormat2Marker {} impl AnchorFormat2Marker { pub fn anchor_format_byte_range(&self) -> Range { let start = 0; start..start + u16::RAW_BYTE_LEN } pub fn x_coordinate_byte_range(&self) -> Range { let start = self.anchor_format_byte_range().end; start..start + i16::RAW_BYTE_LEN } pub fn y_coordinate_byte_range(&self) -> Range { let start = self.x_coordinate_byte_range().end; start..start + i16::RAW_BYTE_LEN } pub fn anchor_point_byte_range(&self) -> Range { let start = self.y_coordinate_byte_range().end; start..start + u16::RAW_BYTE_LEN } } impl MinByteRange for AnchorFormat2Marker { fn min_byte_range(&self) -> Range { 0..self.anchor_point_byte_range().end } } impl<'a> FontRead<'a> for AnchorFormat2<'a> { fn read(data: FontData<'a>) -> Result { let mut cursor = data.cursor(); cursor.advance::(); cursor.advance::(); cursor.advance::(); cursor.advance::(); cursor.finish(AnchorFormat2Marker {}) } } /// [Anchor Table Format 2](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#anchor-table-format-2-design-units-plus-contour-point): Design Units Plus Contour Point pub type AnchorFormat2<'a> = TableRef<'a, AnchorFormat2Marker>; #[allow(clippy::needless_lifetimes)] impl<'a> AnchorFormat2<'a> { /// Format identifier, = 2 pub fn anchor_format(&self) -> u16 { let range = self.shape.anchor_format_byte_range(); self.data.read_at(range.start).unwrap() } /// Horizontal value, in design units pub fn x_coordinate(&self) -> i16 { let range = self.shape.x_coordinate_byte_range(); self.data.read_at(range.start).unwrap() } /// Vertical value, in design units pub fn y_coordinate(&self) -> i16 { let range = self.shape.y_coordinate_byte_range(); self.data.read_at(range.start).unwrap() } /// Index to glyph contour point pub fn anchor_point(&self) -> u16 { let range = self.shape.anchor_point_byte_range(); self.data.read_at(range.start).unwrap() } } #[cfg(feature = "experimental_traverse")] impl<'a> SomeTable<'a> for AnchorFormat2<'a> { fn type_name(&self) -> &str { "AnchorFormat2" } fn get_field(&self, idx: usize) -> Option> { match idx { 0usize => Some(Field::new("anchor_format", self.anchor_format())), 1usize => Some(Field::new("x_coordinate", self.x_coordinate())), 2usize => Some(Field::new("y_coordinate", self.y_coordinate())), 3usize => Some(Field::new("anchor_point", self.anchor_point())), _ => None, } } } #[cfg(feature = "experimental_traverse")] #[allow(clippy::needless_lifetimes)] impl<'a> std::fmt::Debug for AnchorFormat2<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { (self as &dyn SomeTable<'a>).fmt(f) } } impl Format for AnchorFormat3Marker { const FORMAT: u16 = 3; } /// [Anchor Table Format 3](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#anchor-table-format-3-design-units-plus-device-or-variationindex-tables): Design Units Plus Device or VariationIndex Tables #[derive(Debug, Clone, Copy)] #[doc(hidden)] pub struct AnchorFormat3Marker {} impl AnchorFormat3Marker { pub fn anchor_format_byte_range(&self) -> Range { let start = 0; start..start + u16::RAW_BYTE_LEN } pub fn x_coordinate_byte_range(&self) -> Range { let start = self.anchor_format_byte_range().end; start..start + i16::RAW_BYTE_LEN } pub fn y_coordinate_byte_range(&self) -> Range { let start = self.x_coordinate_byte_range().end; start..start + i16::RAW_BYTE_LEN } pub fn x_device_offset_byte_range(&self) -> Range { let start = self.y_coordinate_byte_range().end; start..start + Offset16::RAW_BYTE_LEN } pub fn y_device_offset_byte_range(&self) -> Range { let start = self.x_device_offset_byte_range().end; start..start + Offset16::RAW_BYTE_LEN } } impl MinByteRange for AnchorFormat3Marker { fn min_byte_range(&self) -> Range { 0..self.y_device_offset_byte_range().end } } impl<'a> FontRead<'a> for AnchorFormat3<'a> { fn read(data: FontData<'a>) -> Result { let mut cursor = data.cursor(); cursor.advance::(); cursor.advance::(); cursor.advance::(); cursor.advance::(); cursor.advance::(); cursor.finish(AnchorFormat3Marker {}) } } /// [Anchor Table Format 3](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#anchor-table-format-3-design-units-plus-device-or-variationindex-tables): Design Units Plus Device or VariationIndex Tables pub type AnchorFormat3<'a> = TableRef<'a, AnchorFormat3Marker>; #[allow(clippy::needless_lifetimes)] impl<'a> AnchorFormat3<'a> { /// Format identifier, = 3 pub fn anchor_format(&self) -> u16 { let range = self.shape.anchor_format_byte_range(); self.data.read_at(range.start).unwrap() } /// Horizontal value, in design units pub fn x_coordinate(&self) -> i16 { let range = self.shape.x_coordinate_byte_range(); self.data.read_at(range.start).unwrap() } /// Vertical value, in design units pub fn y_coordinate(&self) -> i16 { let range = self.shape.y_coordinate_byte_range(); self.data.read_at(range.start).unwrap() } /// Offset to Device table (non-variable font) / VariationIndex /// table (variable font) for X coordinate, from beginning of /// Anchor table (may be NULL) pub fn x_device_offset(&self) -> Nullable { let range = self.shape.x_device_offset_byte_range(); self.data.read_at(range.start).unwrap() } /// Attempt to resolve [`x_device_offset`][Self::x_device_offset]. pub fn x_device(&self) -> Option, ReadError>> { let data = self.data; self.x_device_offset().resolve(data) } /// Offset to Device table (non-variable font) / VariationIndex /// table (variable font) for Y coordinate, from beginning of /// Anchor table (may be NULL) pub fn y_device_offset(&self) -> Nullable { let range = self.shape.y_device_offset_byte_range(); self.data.read_at(range.start).unwrap() } /// Attempt to resolve [`y_device_offset`][Self::y_device_offset]. pub fn y_device(&self) -> Option, ReadError>> { let data = self.data; self.y_device_offset().resolve(data) } } #[cfg(feature = "experimental_traverse")] impl<'a> SomeTable<'a> for AnchorFormat3<'a> { fn type_name(&self) -> &str { "AnchorFormat3" } fn get_field(&self, idx: usize) -> Option> { match idx { 0usize => Some(Field::new("anchor_format", self.anchor_format())), 1usize => Some(Field::new("x_coordinate", self.x_coordinate())), 2usize => Some(Field::new("y_coordinate", self.y_coordinate())), 3usize => Some(Field::new( "x_device_offset", FieldType::offset(self.x_device_offset(), self.x_device()), )), 4usize => Some(Field::new( "y_device_offset", FieldType::offset(self.y_device_offset(), self.y_device()), )), _ => None, } } } #[cfg(feature = "experimental_traverse")] #[allow(clippy::needless_lifetimes)] impl<'a> std::fmt::Debug for AnchorFormat3<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { (self as &dyn SomeTable<'a>).fmt(f) } } /// [Mark Array Table](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#mark-array-table) #[derive(Debug, Clone, Copy)] #[doc(hidden)] pub struct MarkArrayMarker { mark_records_byte_len: usize, } impl MarkArrayMarker { pub fn mark_count_byte_range(&self) -> Range { let start = 0; start..start + u16::RAW_BYTE_LEN } pub fn mark_records_byte_range(&self) -> Range { let start = self.mark_count_byte_range().end; start..start + self.mark_records_byte_len } } impl MinByteRange for MarkArrayMarker { fn min_byte_range(&self) -> Range { 0..self.mark_records_byte_range().end } } impl<'a> FontRead<'a> for MarkArray<'a> { fn read(data: FontData<'a>) -> Result { let mut cursor = data.cursor(); let mark_count: u16 = cursor.read()?; let mark_records_byte_len = (mark_count as usize) .checked_mul(MarkRecord::RAW_BYTE_LEN) .ok_or(ReadError::OutOfBounds)?; cursor.advance_by(mark_records_byte_len); cursor.finish(MarkArrayMarker { mark_records_byte_len, }) } } /// [Mark Array Table](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#mark-array-table) pub type MarkArray<'a> = TableRef<'a, MarkArrayMarker>; #[allow(clippy::needless_lifetimes)] impl<'a> MarkArray<'a> { /// Number of MarkRecords pub fn mark_count(&self) -> u16 { let range = self.shape.mark_count_byte_range(); self.data.read_at(range.start).unwrap() } /// Array of MarkRecords, ordered by corresponding glyphs in the /// associated mark Coverage table. pub fn mark_records(&self) -> &'a [MarkRecord] { let range = self.shape.mark_records_byte_range(); self.data.read_array(range).unwrap() } } #[cfg(feature = "experimental_traverse")] impl<'a> SomeTable<'a> for MarkArray<'a> { fn type_name(&self) -> &str { "MarkArray" } fn get_field(&self, idx: usize) -> Option> { match idx { 0usize => Some(Field::new("mark_count", self.mark_count())), 1usize => Some(Field::new( "mark_records", traversal::FieldType::array_of_records( stringify!(MarkRecord), self.mark_records(), self.offset_data(), ), )), _ => None, } } } #[cfg(feature = "experimental_traverse")] #[allow(clippy::needless_lifetimes)] impl<'a> std::fmt::Debug for MarkArray<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { (self as &dyn SomeTable<'a>).fmt(f) } } /// Part of [MarkArray] #[derive(Clone, Debug, Copy, bytemuck :: AnyBitPattern)] #[repr(C)] #[repr(packed)] pub struct MarkRecord { /// Class defined for the associated mark. pub mark_class: BigEndian, /// Offset to Anchor table, from beginning of MarkArray table. pub mark_anchor_offset: BigEndian, } impl MarkRecord { /// Class defined for the associated mark. pub fn mark_class(&self) -> u16 { self.mark_class.get() } /// Offset to Anchor table, from beginning of MarkArray table. pub fn mark_anchor_offset(&self) -> Offset16 { self.mark_anchor_offset.get() } /// Offset to Anchor table, from beginning of MarkArray table. /// /// The `data` argument should be retrieved from the parent table /// By calling its `offset_data` method. pub fn mark_anchor<'a>(&self, data: FontData<'a>) -> Result, ReadError> { self.mark_anchor_offset().resolve(data) } } impl FixedSize for MarkRecord { const RAW_BYTE_LEN: usize = u16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN; } #[cfg(feature = "experimental_traverse")] impl<'a> SomeRecord<'a> for MarkRecord { fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> { RecordResolver { name: "MarkRecord", get_field: Box::new(move |idx, _data| match idx { 0usize => Some(Field::new("mark_class", self.mark_class())), 1usize => Some(Field::new( "mark_anchor_offset", FieldType::offset(self.mark_anchor_offset(), self.mark_anchor(_data)), )), _ => None, }), data, } } } /// [Lookup Type 1](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#lookup-type-1-single-adjustment-positioning-subtable): Single Adjustment Positioning Subtable #[derive(Clone)] pub enum SinglePos<'a> { Format1(SinglePosFormat1<'a>), Format2(SinglePosFormat2<'a>), } impl<'a> SinglePos<'a> { ///Return the `FontData` used to resolve offsets for this table. pub fn offset_data(&self) -> FontData<'a> { match self { Self::Format1(item) => item.offset_data(), Self::Format2(item) => item.offset_data(), } } /// Format identifier: format = 1 pub fn pos_format(&self) -> u16 { match self { Self::Format1(item) => item.pos_format(), Self::Format2(item) => item.pos_format(), } } /// Offset to Coverage table, from beginning of SinglePos subtable. pub fn coverage_offset(&self) -> Offset16 { match self { Self::Format1(item) => item.coverage_offset(), Self::Format2(item) => item.coverage_offset(), } } /// Defines the types of data in the ValueRecord. pub fn value_format(&self) -> ValueFormat { match self { Self::Format1(item) => item.value_format(), Self::Format2(item) => item.value_format(), } } } impl<'a> FontRead<'a> for SinglePos<'a> { fn read(data: FontData<'a>) -> Result { let format: u16 = data.read_at(0usize)?; match format { SinglePosFormat1Marker::FORMAT => Ok(Self::Format1(FontRead::read(data)?)), SinglePosFormat2Marker::FORMAT => Ok(Self::Format2(FontRead::read(data)?)), other => Err(ReadError::InvalidFormat(other.into())), } } } impl MinByteRange for SinglePos<'_> { fn min_byte_range(&self) -> Range { match self { Self::Format1(item) => item.min_byte_range(), Self::Format2(item) => item.min_byte_range(), } } } #[cfg(feature = "experimental_traverse")] impl<'a> SinglePos<'a> { fn dyn_inner<'b>(&'b self) -> &'b dyn SomeTable<'a> { match self { Self::Format1(table) => table, Self::Format2(table) => table, } } } #[cfg(feature = "experimental_traverse")] impl std::fmt::Debug for SinglePos<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.dyn_inner().fmt(f) } } #[cfg(feature = "experimental_traverse")] impl<'a> SomeTable<'a> for SinglePos<'a> { fn type_name(&self) -> &str { self.dyn_inner().type_name() } fn get_field(&self, idx: usize) -> Option> { self.dyn_inner().get_field(idx) } } impl Format for SinglePosFormat1Marker { const FORMAT: u16 = 1; } /// [Single Adjustment Positioning Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#single-adjustment-positioning-format-1-single-positioning-value): Single Positioning Value #[derive(Debug, Clone, Copy)] #[doc(hidden)] pub struct SinglePosFormat1Marker { value_record_byte_len: usize, } impl SinglePosFormat1Marker { pub fn pos_format_byte_range(&self) -> Range { let start = 0; start..start + u16::RAW_BYTE_LEN } pub fn coverage_offset_byte_range(&self) -> Range { let start = self.pos_format_byte_range().end; start..start + Offset16::RAW_BYTE_LEN } pub fn value_format_byte_range(&self) -> Range { let start = self.coverage_offset_byte_range().end; start..start + ValueFormat::RAW_BYTE_LEN } pub fn value_record_byte_range(&self) -> Range { let start = self.value_format_byte_range().end; start..start + self.value_record_byte_len } } impl MinByteRange for SinglePosFormat1Marker { fn min_byte_range(&self) -> Range { 0..self.value_record_byte_range().end } } impl<'a> FontRead<'a> for SinglePosFormat1<'a> { fn read(data: FontData<'a>) -> Result { let mut cursor = data.cursor(); cursor.advance::(); cursor.advance::(); let value_format: ValueFormat = cursor.read()?; let value_record_byte_len = ::compute_size(&value_format)?; cursor.advance_by(value_record_byte_len); cursor.finish(SinglePosFormat1Marker { value_record_byte_len, }) } } /// [Single Adjustment Positioning Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#single-adjustment-positioning-format-1-single-positioning-value): Single Positioning Value pub type SinglePosFormat1<'a> = TableRef<'a, SinglePosFormat1Marker>; #[allow(clippy::needless_lifetimes)] impl<'a> SinglePosFormat1<'a> { /// Format identifier: format = 1 pub fn pos_format(&self) -> u16 { let range = self.shape.pos_format_byte_range(); self.data.read_at(range.start).unwrap() } /// Offset to Coverage table, from beginning of SinglePos subtable. pub fn coverage_offset(&self) -> Offset16 { let range = self.shape.coverage_offset_byte_range(); self.data.read_at(range.start).unwrap() } /// Attempt to resolve [`coverage_offset`][Self::coverage_offset]. pub fn coverage(&self) -> Result, ReadError> { let data = self.data; self.coverage_offset().resolve(data) } /// Defines the types of data in the ValueRecord. pub fn value_format(&self) -> ValueFormat { let range = self.shape.value_format_byte_range(); self.data.read_at(range.start).unwrap() } /// Defines positioning value(s) — applied to all glyphs in the /// Coverage table. pub fn value_record(&self) -> ValueRecord { let range = self.shape.value_record_byte_range(); self.data .read_with_args(range, &self.value_format()) .unwrap() } } #[cfg(feature = "experimental_traverse")] impl<'a> SomeTable<'a> for SinglePosFormat1<'a> { fn type_name(&self) -> &str { "SinglePosFormat1" } fn get_field(&self, idx: usize) -> Option> { match idx { 0usize => Some(Field::new("pos_format", self.pos_format())), 1usize => Some(Field::new( "coverage_offset", FieldType::offset(self.coverage_offset(), self.coverage()), )), 2usize => Some(Field::new("value_format", self.value_format())), 3usize => Some(Field::new( "value_record", self.value_record().traversal_type(self.offset_data()), )), _ => None, } } } #[cfg(feature = "experimental_traverse")] #[allow(clippy::needless_lifetimes)] impl<'a> std::fmt::Debug for SinglePosFormat1<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { (self as &dyn SomeTable<'a>).fmt(f) } } impl Format for SinglePosFormat2Marker { const FORMAT: u16 = 2; } /// [Single Adjustment Positioning Format 2](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#single-adjustment-positioning-format-2-array-of-positioning-values): Array of Positioning Values #[derive(Debug, Clone, Copy)] #[doc(hidden)] pub struct SinglePosFormat2Marker { value_records_byte_len: usize, } impl SinglePosFormat2Marker { pub fn pos_format_byte_range(&self) -> Range { let start = 0; start..start + u16::RAW_BYTE_LEN } pub fn coverage_offset_byte_range(&self) -> Range { let start = self.pos_format_byte_range().end; start..start + Offset16::RAW_BYTE_LEN } pub fn value_format_byte_range(&self) -> Range { let start = self.coverage_offset_byte_range().end; start..start + ValueFormat::RAW_BYTE_LEN } pub fn value_count_byte_range(&self) -> Range { let start = self.value_format_byte_range().end; start..start + u16::RAW_BYTE_LEN } pub fn value_records_byte_range(&self) -> Range { let start = self.value_count_byte_range().end; start..start + self.value_records_byte_len } } impl MinByteRange for SinglePosFormat2Marker { fn min_byte_range(&self) -> Range { 0..self.value_records_byte_range().end } } impl<'a> FontRead<'a> for SinglePosFormat2<'a> { fn read(data: FontData<'a>) -> Result { let mut cursor = data.cursor(); cursor.advance::(); cursor.advance::(); let value_format: ValueFormat = cursor.read()?; let value_count: u16 = cursor.read()?; let value_records_byte_len = (value_count as usize) .checked_mul(::compute_size(&value_format)?) .ok_or(ReadError::OutOfBounds)?; cursor.advance_by(value_records_byte_len); cursor.finish(SinglePosFormat2Marker { value_records_byte_len, }) } } /// [Single Adjustment Positioning Format 2](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#single-adjustment-positioning-format-2-array-of-positioning-values): Array of Positioning Values pub type SinglePosFormat2<'a> = TableRef<'a, SinglePosFormat2Marker>; #[allow(clippy::needless_lifetimes)] impl<'a> SinglePosFormat2<'a> { /// Format identifier: format = 2 pub fn pos_format(&self) -> u16 { let range = self.shape.pos_format_byte_range(); self.data.read_at(range.start).unwrap() } /// Offset to Coverage table, from beginning of SinglePos subtable. pub fn coverage_offset(&self) -> Offset16 { let range = self.shape.coverage_offset_byte_range(); self.data.read_at(range.start).unwrap() } /// Attempt to resolve [`coverage_offset`][Self::coverage_offset]. pub fn coverage(&self) -> Result, ReadError> { let data = self.data; self.coverage_offset().resolve(data) } /// Defines the types of data in the ValueRecords. pub fn value_format(&self) -> ValueFormat { let range = self.shape.value_format_byte_range(); self.data.read_at(range.start).unwrap() } /// Number of ValueRecords — must equal glyphCount in the /// Coverage table. pub fn value_count(&self) -> u16 { let range = self.shape.value_count_byte_range(); self.data.read_at(range.start).unwrap() } /// Array of ValueRecords — positioning values applied to glyphs. pub fn value_records(&self) -> ComputedArray<'a, ValueRecord> { let range = self.shape.value_records_byte_range(); self.data .read_with_args(range, &self.value_format()) .unwrap() } } #[cfg(feature = "experimental_traverse")] impl<'a> SomeTable<'a> for SinglePosFormat2<'a> { fn type_name(&self) -> &str { "SinglePosFormat2" } fn get_field(&self, idx: usize) -> Option> { match idx { 0usize => Some(Field::new("pos_format", self.pos_format())), 1usize => Some(Field::new( "coverage_offset", FieldType::offset(self.coverage_offset(), self.coverage()), )), 2usize => Some(Field::new("value_format", self.value_format())), 3usize => Some(Field::new("value_count", self.value_count())), 4usize => Some(Field::new( "value_records", traversal::FieldType::computed_array( "ValueRecord", self.value_records(), self.offset_data(), ), )), _ => None, } } } #[cfg(feature = "experimental_traverse")] #[allow(clippy::needless_lifetimes)] impl<'a> std::fmt::Debug for SinglePosFormat2<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { (self as &dyn SomeTable<'a>).fmt(f) } } /// [Lookup Type 1](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#lookup-type-1-single-adjustment-positioning-subtable): Single Adjustment Positioning Subtable #[derive(Clone)] pub enum PairPos<'a> { Format1(PairPosFormat1<'a>), Format2(PairPosFormat2<'a>), } impl<'a> PairPos<'a> { ///Return the `FontData` used to resolve offsets for this table. pub fn offset_data(&self) -> FontData<'a> { match self { Self::Format1(item) => item.offset_data(), Self::Format2(item) => item.offset_data(), } } /// Format identifier: format = 1 pub fn pos_format(&self) -> u16 { match self { Self::Format1(item) => item.pos_format(), Self::Format2(item) => item.pos_format(), } } /// Offset to Coverage table, from beginning of PairPos subtable. pub fn coverage_offset(&self) -> Offset16 { match self { Self::Format1(item) => item.coverage_offset(), Self::Format2(item) => item.coverage_offset(), } } /// Defines the types of data in valueRecord1 — for the first /// glyph in the pair (may be zero). pub fn value_format1(&self) -> ValueFormat { match self { Self::Format1(item) => item.value_format1(), Self::Format2(item) => item.value_format1(), } } /// Defines the types of data in valueRecord2 — for the second /// glyph in the pair (may be zero). pub fn value_format2(&self) -> ValueFormat { match self { Self::Format1(item) => item.value_format2(), Self::Format2(item) => item.value_format2(), } } } impl<'a> FontRead<'a> for PairPos<'a> { fn read(data: FontData<'a>) -> Result { let format: u16 = data.read_at(0usize)?; match format { PairPosFormat1Marker::FORMAT => Ok(Self::Format1(FontRead::read(data)?)), PairPosFormat2Marker::FORMAT => Ok(Self::Format2(FontRead::read(data)?)), other => Err(ReadError::InvalidFormat(other.into())), } } } impl MinByteRange for PairPos<'_> { fn min_byte_range(&self) -> Range { match self { Self::Format1(item) => item.min_byte_range(), Self::Format2(item) => item.min_byte_range(), } } } #[cfg(feature = "experimental_traverse")] impl<'a> PairPos<'a> { fn dyn_inner<'b>(&'b self) -> &'b dyn SomeTable<'a> { match self { Self::Format1(table) => table, Self::Format2(table) => table, } } } #[cfg(feature = "experimental_traverse")] impl std::fmt::Debug for PairPos<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.dyn_inner().fmt(f) } } #[cfg(feature = "experimental_traverse")] impl<'a> SomeTable<'a> for PairPos<'a> { fn type_name(&self) -> &str { self.dyn_inner().type_name() } fn get_field(&self, idx: usize) -> Option> { self.dyn_inner().get_field(idx) } } impl Format for PairPosFormat1Marker { const FORMAT: u16 = 1; } /// [Pair Adjustment Positioning Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#pair-adjustment-positioning-format-1-adjustments-for-glyph-pairs): Adjustments for Glyph Pairs #[derive(Debug, Clone, Copy)] #[doc(hidden)] pub struct PairPosFormat1Marker { pair_set_offsets_byte_len: usize, } impl PairPosFormat1Marker { pub fn pos_format_byte_range(&self) -> Range { let start = 0; start..start + u16::RAW_BYTE_LEN } pub fn coverage_offset_byte_range(&self) -> Range { let start = self.pos_format_byte_range().end; start..start + Offset16::RAW_BYTE_LEN } pub fn value_format1_byte_range(&self) -> Range { let start = self.coverage_offset_byte_range().end; start..start + ValueFormat::RAW_BYTE_LEN } pub fn value_format2_byte_range(&self) -> Range { let start = self.value_format1_byte_range().end; start..start + ValueFormat::RAW_BYTE_LEN } pub fn pair_set_count_byte_range(&self) -> Range { let start = self.value_format2_byte_range().end; start..start + u16::RAW_BYTE_LEN } pub fn pair_set_offsets_byte_range(&self) -> Range { let start = self.pair_set_count_byte_range().end; start..start + self.pair_set_offsets_byte_len } } impl MinByteRange for PairPosFormat1Marker { fn min_byte_range(&self) -> Range { 0..self.pair_set_offsets_byte_range().end } } impl<'a> FontRead<'a> for PairPosFormat1<'a> { fn read(data: FontData<'a>) -> Result { let mut cursor = data.cursor(); cursor.advance::(); cursor.advance::(); cursor.advance::(); cursor.advance::(); let pair_set_count: u16 = cursor.read()?; let pair_set_offsets_byte_len = (pair_set_count as usize) .checked_mul(Offset16::RAW_BYTE_LEN) .ok_or(ReadError::OutOfBounds)?; cursor.advance_by(pair_set_offsets_byte_len); cursor.finish(PairPosFormat1Marker { pair_set_offsets_byte_len, }) } } /// [Pair Adjustment Positioning Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#pair-adjustment-positioning-format-1-adjustments-for-glyph-pairs): Adjustments for Glyph Pairs pub type PairPosFormat1<'a> = TableRef<'a, PairPosFormat1Marker>; #[allow(clippy::needless_lifetimes)] impl<'a> PairPosFormat1<'a> { /// Format identifier: format = 1 pub fn pos_format(&self) -> u16 { let range = self.shape.pos_format_byte_range(); self.data.read_at(range.start).unwrap() } /// Offset to Coverage table, from beginning of PairPos subtable. pub fn coverage_offset(&self) -> Offset16 { let range = self.shape.coverage_offset_byte_range(); self.data.read_at(range.start).unwrap() } /// Attempt to resolve [`coverage_offset`][Self::coverage_offset]. pub fn coverage(&self) -> Result, ReadError> { let data = self.data; self.coverage_offset().resolve(data) } /// Defines the types of data in valueRecord1 — for the first /// glyph in the pair (may be zero). pub fn value_format1(&self) -> ValueFormat { let range = self.shape.value_format1_byte_range(); self.data.read_at(range.start).unwrap() } /// Defines the types of data in valueRecord2 — for the second /// glyph in the pair (may be zero). pub fn value_format2(&self) -> ValueFormat { let range = self.shape.value_format2_byte_range(); self.data.read_at(range.start).unwrap() } /// Number of PairSet tables pub fn pair_set_count(&self) -> u16 { let range = self.shape.pair_set_count_byte_range(); self.data.read_at(range.start).unwrap() } /// Array of offsets to PairSet tables. Offsets are from beginning /// of PairPos subtable, ordered by Coverage Index. pub fn pair_set_offsets(&self) -> &'a [BigEndian] { let range = self.shape.pair_set_offsets_byte_range(); self.data.read_array(range).unwrap() } /// A dynamically resolving wrapper for [`pair_set_offsets`][Self::pair_set_offsets]. pub fn pair_sets(&self) -> ArrayOfOffsets<'a, PairSet<'a>, Offset16> { let data = self.data; let offsets = self.pair_set_offsets(); let args = (self.value_format1(), self.value_format2()); ArrayOfOffsets::new(offsets, data, args) } } #[cfg(feature = "experimental_traverse")] impl<'a> SomeTable<'a> for PairPosFormat1<'a> { fn type_name(&self) -> &str { "PairPosFormat1" } fn get_field(&self, idx: usize) -> Option> { match idx { 0usize => Some(Field::new("pos_format", self.pos_format())), 1usize => Some(Field::new( "coverage_offset", FieldType::offset(self.coverage_offset(), self.coverage()), )), 2usize => Some(Field::new("value_format1", self.value_format1())), 3usize => Some(Field::new("value_format2", self.value_format2())), 4usize => Some(Field::new("pair_set_count", self.pair_set_count())), 5usize => Some({ let data = self.data; let args = (self.value_format1(), self.value_format2()); Field::new( "pair_set_offsets", FieldType::array_of_offsets( better_type_name::(), self.pair_set_offsets(), move |off| { let target = off.get().resolve_with_args::(data, &args); FieldType::offset(off.get(), target) }, ), ) }), _ => None, } } } #[cfg(feature = "experimental_traverse")] #[allow(clippy::needless_lifetimes)] impl<'a> std::fmt::Debug for PairPosFormat1<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { (self as &dyn SomeTable<'a>).fmt(f) } } /// Part of [PairPosFormat1] #[derive(Debug, Clone, Copy)] #[doc(hidden)] pub struct PairSetMarker { value_format1: ValueFormat, value_format2: ValueFormat, pair_value_records_byte_len: usize, } impl PairSetMarker { pub fn pair_value_count_byte_range(&self) -> Range { let start = 0; start..start + u16::RAW_BYTE_LEN } pub fn pair_value_records_byte_range(&self) -> Range { let start = self.pair_value_count_byte_range().end; start..start + self.pair_value_records_byte_len } } impl MinByteRange for PairSetMarker { fn min_byte_range(&self) -> Range { 0..self.pair_value_records_byte_range().end } } impl ReadArgs for PairSet<'_> { type Args = (ValueFormat, ValueFormat); } impl<'a> FontReadWithArgs<'a> for PairSet<'a> { fn read_with_args( data: FontData<'a>, args: &(ValueFormat, ValueFormat), ) -> Result { let (value_format1, value_format2) = *args; let mut cursor = data.cursor(); let pair_value_count: u16 = cursor.read()?; let pair_value_records_byte_len = (pair_value_count as usize) .checked_mul(::compute_size(&( value_format1, value_format2, ))?) .ok_or(ReadError::OutOfBounds)?; cursor.advance_by(pair_value_records_byte_len); cursor.finish(PairSetMarker { value_format1, value_format2, pair_value_records_byte_len, }) } } impl<'a> PairSet<'a> { /// A constructor that requires additional arguments. /// /// This type requires some external state in order to be /// parsed. pub fn read( data: FontData<'a>, value_format1: ValueFormat, value_format2: ValueFormat, ) -> Result { let args = (value_format1, value_format2); Self::read_with_args(data, &args) } } /// Part of [PairPosFormat1] pub type PairSet<'a> = TableRef<'a, PairSetMarker>; #[allow(clippy::needless_lifetimes)] impl<'a> PairSet<'a> { /// Number of PairValueRecords pub fn pair_value_count(&self) -> u16 { let range = self.shape.pair_value_count_byte_range(); self.data.read_at(range.start).unwrap() } /// Array of PairValueRecords, ordered by glyph ID of the second /// glyph. pub fn pair_value_records(&self) -> ComputedArray<'a, PairValueRecord> { let range = self.shape.pair_value_records_byte_range(); self.data .read_with_args(range, &(self.value_format1(), self.value_format2())) .unwrap() } pub(crate) fn value_format1(&self) -> ValueFormat { self.shape.value_format1 } pub(crate) fn value_format2(&self) -> ValueFormat { self.shape.value_format2 } } #[cfg(feature = "experimental_traverse")] impl<'a> SomeTable<'a> for PairSet<'a> { fn type_name(&self) -> &str { "PairSet" } fn get_field(&self, idx: usize) -> Option> { match idx { 0usize => Some(Field::new("pair_value_count", self.pair_value_count())), 1usize => Some(Field::new( "pair_value_records", traversal::FieldType::computed_array( "PairValueRecord", self.pair_value_records(), self.offset_data(), ), )), _ => None, } } } #[cfg(feature = "experimental_traverse")] #[allow(clippy::needless_lifetimes)] impl<'a> std::fmt::Debug for PairSet<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { (self as &dyn SomeTable<'a>).fmt(f) } } /// Part of [PairSet] #[derive(Clone, Debug)] pub struct PairValueRecord { /// Glyph ID of second glyph in the pair (first glyph is listed in /// the Coverage table). pub second_glyph: BigEndian, /// Positioning data for the first glyph in the pair. pub value_record1: ValueRecord, /// Positioning data for the second glyph in the pair. pub value_record2: ValueRecord, } impl PairValueRecord { /// Glyph ID of second glyph in the pair (first glyph is listed in /// the Coverage table). pub fn second_glyph(&self) -> GlyphId16 { self.second_glyph.get() } /// Positioning data for the first glyph in the pair. pub fn value_record1(&self) -> &ValueRecord { &self.value_record1 } /// Positioning data for the second glyph in the pair. pub fn value_record2(&self) -> &ValueRecord { &self.value_record2 } } impl ReadArgs for PairValueRecord { type Args = (ValueFormat, ValueFormat); } impl ComputeSize for PairValueRecord { #[allow(clippy::needless_question_mark)] fn compute_size(args: &(ValueFormat, ValueFormat)) -> Result { let (value_format1, value_format2) = *args; let mut result = 0usize; result = result .checked_add(GlyphId16::RAW_BYTE_LEN) .ok_or(ReadError::OutOfBounds)?; result = result .checked_add(::compute_size(&value_format1)?) .ok_or(ReadError::OutOfBounds)?; result = result .checked_add(::compute_size(&value_format2)?) .ok_or(ReadError::OutOfBounds)?; Ok(result) } } impl<'a> FontReadWithArgs<'a> for PairValueRecord { fn read_with_args( data: FontData<'a>, args: &(ValueFormat, ValueFormat), ) -> Result { let mut cursor = data.cursor(); let (value_format1, value_format2) = *args; Ok(Self { second_glyph: cursor.read_be()?, value_record1: cursor.read_with_args(&value_format1)?, value_record2: cursor.read_with_args(&value_format2)?, }) } } #[allow(clippy::needless_lifetimes)] impl<'a> PairValueRecord { /// A constructor that requires additional arguments. /// /// This type requires some external state in order to be /// parsed. pub fn read( data: FontData<'a>, value_format1: ValueFormat, value_format2: ValueFormat, ) -> Result { let args = (value_format1, value_format2); Self::read_with_args(data, &args) } } #[cfg(feature = "experimental_traverse")] impl<'a> SomeRecord<'a> for PairValueRecord { fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> { RecordResolver { name: "PairValueRecord", get_field: Box::new(move |idx, _data| match idx { 0usize => Some(Field::new("second_glyph", self.second_glyph())), 1usize => Some(Field::new( "value_record1", self.value_record1().traversal_type(_data), )), 2usize => Some(Field::new( "value_record2", self.value_record2().traversal_type(_data), )), _ => None, }), data, } } } impl Format for PairPosFormat2Marker { const FORMAT: u16 = 2; } /// [Pair Adjustment Positioning Format 2](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#pair-adjustment-positioning-format-2-class-pair-adjustment): Class Pair Adjustment #[derive(Debug, Clone, Copy)] #[doc(hidden)] pub struct PairPosFormat2Marker { class1_records_byte_len: usize, } impl PairPosFormat2Marker { pub fn pos_format_byte_range(&self) -> Range { let start = 0; start..start + u16::RAW_BYTE_LEN } pub fn coverage_offset_byte_range(&self) -> Range { let start = self.pos_format_byte_range().end; start..start + Offset16::RAW_BYTE_LEN } pub fn value_format1_byte_range(&self) -> Range { let start = self.coverage_offset_byte_range().end; start..start + ValueFormat::RAW_BYTE_LEN } pub fn value_format2_byte_range(&self) -> Range { let start = self.value_format1_byte_range().end; start..start + ValueFormat::RAW_BYTE_LEN } pub fn class_def1_offset_byte_range(&self) -> Range { let start = self.value_format2_byte_range().end; start..start + Offset16::RAW_BYTE_LEN } pub fn class_def2_offset_byte_range(&self) -> Range { let start = self.class_def1_offset_byte_range().end; start..start + Offset16::RAW_BYTE_LEN } pub fn class1_count_byte_range(&self) -> Range { let start = self.class_def2_offset_byte_range().end; start..start + u16::RAW_BYTE_LEN } pub fn class2_count_byte_range(&self) -> Range { let start = self.class1_count_byte_range().end; start..start + u16::RAW_BYTE_LEN } pub fn class1_records_byte_range(&self) -> Range { let start = self.class2_count_byte_range().end; start..start + self.class1_records_byte_len } } impl MinByteRange for PairPosFormat2Marker { fn min_byte_range(&self) -> Range { 0..self.class1_records_byte_range().end } } impl<'a> FontRead<'a> for PairPosFormat2<'a> { fn read(data: FontData<'a>) -> Result { let mut cursor = data.cursor(); cursor.advance::(); cursor.advance::(); let value_format1: ValueFormat = cursor.read()?; let value_format2: ValueFormat = cursor.read()?; cursor.advance::(); cursor.advance::(); let class1_count: u16 = cursor.read()?; let class2_count: u16 = cursor.read()?; let class1_records_byte_len = (class1_count as usize) .checked_mul(::compute_size(&( class2_count, value_format1, value_format2, ))?) .ok_or(ReadError::OutOfBounds)?; cursor.advance_by(class1_records_byte_len); cursor.finish(PairPosFormat2Marker { class1_records_byte_len, }) } } /// [Pair Adjustment Positioning Format 2](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#pair-adjustment-positioning-format-2-class-pair-adjustment): Class Pair Adjustment pub type PairPosFormat2<'a> = TableRef<'a, PairPosFormat2Marker>; #[allow(clippy::needless_lifetimes)] impl<'a> PairPosFormat2<'a> { /// Format identifier: format = 2 pub fn pos_format(&self) -> u16 { let range = self.shape.pos_format_byte_range(); self.data.read_at(range.start).unwrap() } /// Offset to Coverage table, from beginning of PairPos subtable. pub fn coverage_offset(&self) -> Offset16 { let range = self.shape.coverage_offset_byte_range(); self.data.read_at(range.start).unwrap() } /// Attempt to resolve [`coverage_offset`][Self::coverage_offset]. pub fn coverage(&self) -> Result, ReadError> { let data = self.data; self.coverage_offset().resolve(data) } /// ValueRecord definition — for the first glyph of the pair (may /// be zero). pub fn value_format1(&self) -> ValueFormat { let range = self.shape.value_format1_byte_range(); self.data.read_at(range.start).unwrap() } /// ValueRecord definition — for the second glyph of the pair /// (may be zero). pub fn value_format2(&self) -> ValueFormat { let range = self.shape.value_format2_byte_range(); self.data.read_at(range.start).unwrap() } /// Offset to ClassDef table, from beginning of PairPos subtable /// — for the first glyph of the pair. pub fn class_def1_offset(&self) -> Offset16 { let range = self.shape.class_def1_offset_byte_range(); self.data.read_at(range.start).unwrap() } /// Attempt to resolve [`class_def1_offset`][Self::class_def1_offset]. pub fn class_def1(&self) -> Result, ReadError> { let data = self.data; self.class_def1_offset().resolve(data) } /// Offset to ClassDef table, from beginning of PairPos subtable /// — for the second glyph of the pair. pub fn class_def2_offset(&self) -> Offset16 { let range = self.shape.class_def2_offset_byte_range(); self.data.read_at(range.start).unwrap() } /// Attempt to resolve [`class_def2_offset`][Self::class_def2_offset]. pub fn class_def2(&self) -> Result, ReadError> { let data = self.data; self.class_def2_offset().resolve(data) } /// Number of classes in classDef1 table — includes Class 0. pub fn class1_count(&self) -> u16 { let range = self.shape.class1_count_byte_range(); self.data.read_at(range.start).unwrap() } /// Number of classes in classDef2 table — includes Class 0. pub fn class2_count(&self) -> u16 { let range = self.shape.class2_count_byte_range(); self.data.read_at(range.start).unwrap() } /// Array of Class1 records, ordered by classes in classDef1. pub fn class1_records(&self) -> ComputedArray<'a, Class1Record<'a>> { let range = self.shape.class1_records_byte_range(); self.data .read_with_args( range, &( self.class2_count(), self.value_format1(), self.value_format2(), ), ) .unwrap() } } #[cfg(feature = "experimental_traverse")] impl<'a> SomeTable<'a> for PairPosFormat2<'a> { fn type_name(&self) -> &str { "PairPosFormat2" } fn get_field(&self, idx: usize) -> Option> { match idx { 0usize => Some(Field::new("pos_format", self.pos_format())), 1usize => Some(Field::new( "coverage_offset", FieldType::offset(self.coverage_offset(), self.coverage()), )), 2usize => Some(Field::new("value_format1", self.value_format1())), 3usize => Some(Field::new("value_format2", self.value_format2())), 4usize => Some(Field::new( "class_def1_offset", FieldType::offset(self.class_def1_offset(), self.class_def1()), )), 5usize => Some(Field::new( "class_def2_offset", FieldType::offset(self.class_def2_offset(), self.class_def2()), )), 6usize => Some(Field::new("class1_count", self.class1_count())), 7usize => Some(Field::new("class2_count", self.class2_count())), 8usize => Some(Field::new( "class1_records", traversal::FieldType::computed_array( "Class1Record", self.class1_records(), self.offset_data(), ), )), _ => None, } } } #[cfg(feature = "experimental_traverse")] #[allow(clippy::needless_lifetimes)] impl<'a> std::fmt::Debug for PairPosFormat2<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { (self as &dyn SomeTable<'a>).fmt(f) } } /// Part of [PairPosFormat2] #[derive(Clone, Debug)] pub struct Class1Record<'a> { /// Array of Class2 records, ordered by classes in classDef2. pub class2_records: ComputedArray<'a, Class2Record>, } impl<'a> Class1Record<'a> { /// Array of Class2 records, ordered by classes in classDef2. pub fn class2_records(&self) -> &ComputedArray<'a, Class2Record> { &self.class2_records } } impl ReadArgs for Class1Record<'_> { type Args = (u16, ValueFormat, ValueFormat); } impl ComputeSize for Class1Record<'_> { #[allow(clippy::needless_question_mark)] fn compute_size(args: &(u16, ValueFormat, ValueFormat)) -> Result { let (class2_count, value_format1, value_format2) = *args; Ok((class2_count as usize) .checked_mul(::compute_size(&( value_format1, value_format2, ))?) .ok_or(ReadError::OutOfBounds)?) } } impl<'a> FontReadWithArgs<'a> for Class1Record<'a> { fn read_with_args( data: FontData<'a>, args: &(u16, ValueFormat, ValueFormat), ) -> Result { let mut cursor = data.cursor(); let (class2_count, value_format1, value_format2) = *args; Ok(Self { class2_records: cursor .read_computed_array(class2_count as usize, &(value_format1, value_format2))?, }) } } #[allow(clippy::needless_lifetimes)] impl<'a> Class1Record<'a> { /// A constructor that requires additional arguments. /// /// This type requires some external state in order to be /// parsed. pub fn read( data: FontData<'a>, class2_count: u16, value_format1: ValueFormat, value_format2: ValueFormat, ) -> Result { let args = (class2_count, value_format1, value_format2); Self::read_with_args(data, &args) } } #[cfg(feature = "experimental_traverse")] impl<'a> SomeRecord<'a> for Class1Record<'a> { fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> { RecordResolver { name: "Class1Record", get_field: Box::new(move |idx, _data| match idx { 0usize => Some(Field::new( "class2_records", traversal::FieldType::computed_array( "Class2Record", self.class2_records().clone(), FontData::new(&[]), ), )), _ => None, }), data, } } } /// Part of [PairPosFormat2] #[derive(Clone, Debug)] pub struct Class2Record { /// Positioning for first glyph — empty if valueFormat1 = 0. pub value_record1: ValueRecord, /// Positioning for second glyph — empty if valueFormat2 = 0. pub value_record2: ValueRecord, } impl Class2Record { /// Positioning for first glyph — empty if valueFormat1 = 0. pub fn value_record1(&self) -> &ValueRecord { &self.value_record1 } /// Positioning for second glyph — empty if valueFormat2 = 0. pub fn value_record2(&self) -> &ValueRecord { &self.value_record2 } } impl ReadArgs for Class2Record { type Args = (ValueFormat, ValueFormat); } impl ComputeSize for Class2Record { #[allow(clippy::needless_question_mark)] fn compute_size(args: &(ValueFormat, ValueFormat)) -> Result { let (value_format1, value_format2) = *args; let mut result = 0usize; result = result .checked_add(::compute_size(&value_format1)?) .ok_or(ReadError::OutOfBounds)?; result = result .checked_add(::compute_size(&value_format2)?) .ok_or(ReadError::OutOfBounds)?; Ok(result) } } impl<'a> FontReadWithArgs<'a> for Class2Record { fn read_with_args( data: FontData<'a>, args: &(ValueFormat, ValueFormat), ) -> Result { let mut cursor = data.cursor(); let (value_format1, value_format2) = *args; Ok(Self { value_record1: cursor.read_with_args(&value_format1)?, value_record2: cursor.read_with_args(&value_format2)?, }) } } #[allow(clippy::needless_lifetimes)] impl<'a> Class2Record { /// A constructor that requires additional arguments. /// /// This type requires some external state in order to be /// parsed. pub fn read( data: FontData<'a>, value_format1: ValueFormat, value_format2: ValueFormat, ) -> Result { let args = (value_format1, value_format2); Self::read_with_args(data, &args) } } #[cfg(feature = "experimental_traverse")] impl<'a> SomeRecord<'a> for Class2Record { fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> { RecordResolver { name: "Class2Record", get_field: Box::new(move |idx, _data| match idx { 0usize => Some(Field::new( "value_record1", self.value_record1().traversal_type(_data), )), 1usize => Some(Field::new( "value_record2", self.value_record2().traversal_type(_data), )), _ => None, }), data, } } } impl Format for CursivePosFormat1Marker { const FORMAT: u16 = 1; } /// [Cursive Attachment Positioning Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#cursive-attachment-positioning-format1-cursive-attachment): Cursvie attachment #[derive(Debug, Clone, Copy)] #[doc(hidden)] pub struct CursivePosFormat1Marker { entry_exit_record_byte_len: usize, } impl CursivePosFormat1Marker { pub fn pos_format_byte_range(&self) -> Range { let start = 0; start..start + u16::RAW_BYTE_LEN } pub fn coverage_offset_byte_range(&self) -> Range { let start = self.pos_format_byte_range().end; start..start + Offset16::RAW_BYTE_LEN } pub fn entry_exit_count_byte_range(&self) -> Range { let start = self.coverage_offset_byte_range().end; start..start + u16::RAW_BYTE_LEN } pub fn entry_exit_record_byte_range(&self) -> Range { let start = self.entry_exit_count_byte_range().end; start..start + self.entry_exit_record_byte_len } } impl MinByteRange for CursivePosFormat1Marker { fn min_byte_range(&self) -> Range { 0..self.entry_exit_record_byte_range().end } } impl<'a> FontRead<'a> for CursivePosFormat1<'a> { fn read(data: FontData<'a>) -> Result { let mut cursor = data.cursor(); cursor.advance::(); cursor.advance::(); let entry_exit_count: u16 = cursor.read()?; let entry_exit_record_byte_len = (entry_exit_count as usize) .checked_mul(EntryExitRecord::RAW_BYTE_LEN) .ok_or(ReadError::OutOfBounds)?; cursor.advance_by(entry_exit_record_byte_len); cursor.finish(CursivePosFormat1Marker { entry_exit_record_byte_len, }) } } /// [Cursive Attachment Positioning Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#cursive-attachment-positioning-format1-cursive-attachment): Cursvie attachment pub type CursivePosFormat1<'a> = TableRef<'a, CursivePosFormat1Marker>; #[allow(clippy::needless_lifetimes)] impl<'a> CursivePosFormat1<'a> { /// Format identifier: format = 1 pub fn pos_format(&self) -> u16 { let range = self.shape.pos_format_byte_range(); self.data.read_at(range.start).unwrap() } /// Offset to Coverage table, from beginning of CursivePos subtable. pub fn coverage_offset(&self) -> Offset16 { let range = self.shape.coverage_offset_byte_range(); self.data.read_at(range.start).unwrap() } /// Attempt to resolve [`coverage_offset`][Self::coverage_offset]. pub fn coverage(&self) -> Result, ReadError> { let data = self.data; self.coverage_offset().resolve(data) } /// Number of EntryExit records pub fn entry_exit_count(&self) -> u16 { let range = self.shape.entry_exit_count_byte_range(); self.data.read_at(range.start).unwrap() } /// Array of EntryExit records, in Coverage index order. pub fn entry_exit_record(&self) -> &'a [EntryExitRecord] { let range = self.shape.entry_exit_record_byte_range(); self.data.read_array(range).unwrap() } } #[cfg(feature = "experimental_traverse")] impl<'a> SomeTable<'a> for CursivePosFormat1<'a> { fn type_name(&self) -> &str { "CursivePosFormat1" } fn get_field(&self, idx: usize) -> Option> { match idx { 0usize => Some(Field::new("pos_format", self.pos_format())), 1usize => Some(Field::new( "coverage_offset", FieldType::offset(self.coverage_offset(), self.coverage()), )), 2usize => Some(Field::new("entry_exit_count", self.entry_exit_count())), 3usize => Some(Field::new( "entry_exit_record", traversal::FieldType::array_of_records( stringify!(EntryExitRecord), self.entry_exit_record(), self.offset_data(), ), )), _ => None, } } } #[cfg(feature = "experimental_traverse")] #[allow(clippy::needless_lifetimes)] impl<'a> std::fmt::Debug for CursivePosFormat1<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { (self as &dyn SomeTable<'a>).fmt(f) } } /// Part of [CursivePosFormat1] #[derive(Clone, Debug, Copy, bytemuck :: AnyBitPattern)] #[repr(C)] #[repr(packed)] pub struct EntryExitRecord { /// Offset to entryAnchor table, from beginning of CursivePos /// subtable (may be NULL). pub entry_anchor_offset: BigEndian>, /// Offset to exitAnchor table, from beginning of CursivePos /// subtable (may be NULL). pub exit_anchor_offset: BigEndian>, } impl EntryExitRecord { /// Offset to entryAnchor table, from beginning of CursivePos /// subtable (may be NULL). pub fn entry_anchor_offset(&self) -> Nullable { self.entry_anchor_offset.get() } /// Offset to entryAnchor table, from beginning of CursivePos /// subtable (may be NULL). /// /// The `data` argument should be retrieved from the parent table /// By calling its `offset_data` method. pub fn entry_anchor<'a>( &self, data: FontData<'a>, ) -> Option, ReadError>> { self.entry_anchor_offset().resolve(data) } /// Offset to exitAnchor table, from beginning of CursivePos /// subtable (may be NULL). pub fn exit_anchor_offset(&self) -> Nullable { self.exit_anchor_offset.get() } /// Offset to exitAnchor table, from beginning of CursivePos /// subtable (may be NULL). /// /// The `data` argument should be retrieved from the parent table /// By calling its `offset_data` method. pub fn exit_anchor<'a>( &self, data: FontData<'a>, ) -> Option, ReadError>> { self.exit_anchor_offset().resolve(data) } } impl FixedSize for EntryExitRecord { const RAW_BYTE_LEN: usize = Offset16::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN; } #[cfg(feature = "experimental_traverse")] impl<'a> SomeRecord<'a> for EntryExitRecord { fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> { RecordResolver { name: "EntryExitRecord", get_field: Box::new(move |idx, _data| match idx { 0usize => Some(Field::new( "entry_anchor_offset", FieldType::offset(self.entry_anchor_offset(), self.entry_anchor(_data)), )), 1usize => Some(Field::new( "exit_anchor_offset", FieldType::offset(self.exit_anchor_offset(), self.exit_anchor(_data)), )), _ => None, }), data, } } } impl Format for MarkBasePosFormat1Marker { const FORMAT: u16 = 1; } /// [Mark-to-Base Attachment Positioning Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#mark-to-base-attachment-positioning-format-1-mark-to-base-attachment-point): Mark-to-base Attachment Point #[derive(Debug, Clone, Copy)] #[doc(hidden)] pub struct MarkBasePosFormat1Marker {} impl MarkBasePosFormat1Marker { pub fn pos_format_byte_range(&self) -> Range { let start = 0; start..start + u16::RAW_BYTE_LEN } pub fn mark_coverage_offset_byte_range(&self) -> Range { let start = self.pos_format_byte_range().end; start..start + Offset16::RAW_BYTE_LEN } pub fn base_coverage_offset_byte_range(&self) -> Range { let start = self.mark_coverage_offset_byte_range().end; start..start + Offset16::RAW_BYTE_LEN } pub fn mark_class_count_byte_range(&self) -> Range { let start = self.base_coverage_offset_byte_range().end; start..start + u16::RAW_BYTE_LEN } pub fn mark_array_offset_byte_range(&self) -> Range { let start = self.mark_class_count_byte_range().end; start..start + Offset16::RAW_BYTE_LEN } pub fn base_array_offset_byte_range(&self) -> Range { let start = self.mark_array_offset_byte_range().end; start..start + Offset16::RAW_BYTE_LEN } } impl MinByteRange for MarkBasePosFormat1Marker { fn min_byte_range(&self) -> Range { 0..self.base_array_offset_byte_range().end } } impl<'a> FontRead<'a> for MarkBasePosFormat1<'a> { fn read(data: FontData<'a>) -> Result { let mut cursor = data.cursor(); cursor.advance::(); cursor.advance::(); cursor.advance::(); cursor.advance::(); cursor.advance::(); cursor.advance::(); cursor.finish(MarkBasePosFormat1Marker {}) } } /// [Mark-to-Base Attachment Positioning Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#mark-to-base-attachment-positioning-format-1-mark-to-base-attachment-point): Mark-to-base Attachment Point pub type MarkBasePosFormat1<'a> = TableRef<'a, MarkBasePosFormat1Marker>; #[allow(clippy::needless_lifetimes)] impl<'a> MarkBasePosFormat1<'a> { /// Format identifier: format = 1 pub fn pos_format(&self) -> u16 { let range = self.shape.pos_format_byte_range(); self.data.read_at(range.start).unwrap() } /// Offset to markCoverage table, from beginning of MarkBasePos /// subtable. pub fn mark_coverage_offset(&self) -> Offset16 { let range = self.shape.mark_coverage_offset_byte_range(); self.data.read_at(range.start).unwrap() } /// Attempt to resolve [`mark_coverage_offset`][Self::mark_coverage_offset]. pub fn mark_coverage(&self) -> Result, ReadError> { let data = self.data; self.mark_coverage_offset().resolve(data) } /// Offset to baseCoverage table, from beginning of MarkBasePos /// subtable. pub fn base_coverage_offset(&self) -> Offset16 { let range = self.shape.base_coverage_offset_byte_range(); self.data.read_at(range.start).unwrap() } /// Attempt to resolve [`base_coverage_offset`][Self::base_coverage_offset]. pub fn base_coverage(&self) -> Result, ReadError> { let data = self.data; self.base_coverage_offset().resolve(data) } /// Number of classes defined for marks pub fn mark_class_count(&self) -> u16 { let range = self.shape.mark_class_count_byte_range(); self.data.read_at(range.start).unwrap() } /// Offset to MarkArray table, from beginning of MarkBasePos /// subtable. pub fn mark_array_offset(&self) -> Offset16 { let range = self.shape.mark_array_offset_byte_range(); self.data.read_at(range.start).unwrap() } /// Attempt to resolve [`mark_array_offset`][Self::mark_array_offset]. pub fn mark_array(&self) -> Result, ReadError> { let data = self.data; self.mark_array_offset().resolve(data) } /// Offset to BaseArray table, from beginning of MarkBasePos /// subtable. pub fn base_array_offset(&self) -> Offset16 { let range = self.shape.base_array_offset_byte_range(); self.data.read_at(range.start).unwrap() } /// Attempt to resolve [`base_array_offset`][Self::base_array_offset]. pub fn base_array(&self) -> Result, ReadError> { let data = self.data; let args = self.mark_class_count(); self.base_array_offset().resolve_with_args(data, &args) } } #[cfg(feature = "experimental_traverse")] impl<'a> SomeTable<'a> for MarkBasePosFormat1<'a> { fn type_name(&self) -> &str { "MarkBasePosFormat1" } fn get_field(&self, idx: usize) -> Option> { match idx { 0usize => Some(Field::new("pos_format", self.pos_format())), 1usize => Some(Field::new( "mark_coverage_offset", FieldType::offset(self.mark_coverage_offset(), self.mark_coverage()), )), 2usize => Some(Field::new( "base_coverage_offset", FieldType::offset(self.base_coverage_offset(), self.base_coverage()), )), 3usize => Some(Field::new("mark_class_count", self.mark_class_count())), 4usize => Some(Field::new( "mark_array_offset", FieldType::offset(self.mark_array_offset(), self.mark_array()), )), 5usize => Some(Field::new( "base_array_offset", FieldType::offset(self.base_array_offset(), self.base_array()), )), _ => None, } } } #[cfg(feature = "experimental_traverse")] #[allow(clippy::needless_lifetimes)] impl<'a> std::fmt::Debug for MarkBasePosFormat1<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { (self as &dyn SomeTable<'a>).fmt(f) } } /// Part of [MarkBasePosFormat1] #[derive(Debug, Clone, Copy)] #[doc(hidden)] pub struct BaseArrayMarker { mark_class_count: u16, base_records_byte_len: usize, } impl BaseArrayMarker { pub fn base_count_byte_range(&self) -> Range { let start = 0; start..start + u16::RAW_BYTE_LEN } pub fn base_records_byte_range(&self) -> Range { let start = self.base_count_byte_range().end; start..start + self.base_records_byte_len } } impl MinByteRange for BaseArrayMarker { fn min_byte_range(&self) -> Range { 0..self.base_records_byte_range().end } } impl ReadArgs for BaseArray<'_> { type Args = u16; } impl<'a> FontReadWithArgs<'a> for BaseArray<'a> { fn read_with_args(data: FontData<'a>, args: &u16) -> Result { let mark_class_count = *args; let mut cursor = data.cursor(); let base_count: u16 = cursor.read()?; let base_records_byte_len = (base_count as usize) .checked_mul(::compute_size( &mark_class_count, )?) .ok_or(ReadError::OutOfBounds)?; cursor.advance_by(base_records_byte_len); cursor.finish(BaseArrayMarker { mark_class_count, base_records_byte_len, }) } } impl<'a> BaseArray<'a> { /// A constructor that requires additional arguments. /// /// This type requires some external state in order to be /// parsed. pub fn read(data: FontData<'a>, mark_class_count: u16) -> Result { let args = mark_class_count; Self::read_with_args(data, &args) } } /// Part of [MarkBasePosFormat1] pub type BaseArray<'a> = TableRef<'a, BaseArrayMarker>; #[allow(clippy::needless_lifetimes)] impl<'a> BaseArray<'a> { /// Number of BaseRecords pub fn base_count(&self) -> u16 { let range = self.shape.base_count_byte_range(); self.data.read_at(range.start).unwrap() } /// Array of BaseRecords, in order of baseCoverage Index. pub fn base_records(&self) -> ComputedArray<'a, BaseRecord<'a>> { let range = self.shape.base_records_byte_range(); self.data .read_with_args(range, &self.mark_class_count()) .unwrap() } pub(crate) fn mark_class_count(&self) -> u16 { self.shape.mark_class_count } } #[cfg(feature = "experimental_traverse")] impl<'a> SomeTable<'a> for BaseArray<'a> { fn type_name(&self) -> &str { "BaseArray" } fn get_field(&self, idx: usize) -> Option> { match idx { 0usize => Some(Field::new("base_count", self.base_count())), 1usize => Some(Field::new( "base_records", traversal::FieldType::computed_array( "BaseRecord", self.base_records(), self.offset_data(), ), )), _ => None, } } } #[cfg(feature = "experimental_traverse")] #[allow(clippy::needless_lifetimes)] impl<'a> std::fmt::Debug for BaseArray<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { (self as &dyn SomeTable<'a>).fmt(f) } } /// Part of [BaseArray] #[derive(Clone, Debug)] pub struct BaseRecord<'a> { /// Array of offsets (one per mark class) to Anchor tables. Offsets /// are from beginning of BaseArray table, ordered by class /// (offsets may be NULL). pub base_anchor_offsets: &'a [BigEndian>], } impl<'a> BaseRecord<'a> { /// Array of offsets (one per mark class) to Anchor tables. Offsets /// are from beginning of BaseArray table, ordered by class /// (offsets may be NULL). pub fn base_anchor_offsets(&self) -> &'a [BigEndian>] { self.base_anchor_offsets } /// Array of offsets (one per mark class) to Anchor tables. Offsets /// are from beginning of BaseArray table, ordered by class /// (offsets may be NULL). /// /// The `data` argument should be retrieved from the parent table /// By calling its `offset_data` method. pub fn base_anchors( &self, data: FontData<'a>, ) -> ArrayOfNullableOffsets<'a, AnchorTable<'a>, Offset16> { let offsets = self.base_anchor_offsets(); ArrayOfNullableOffsets::new(offsets, data, ()) } } impl ReadArgs for BaseRecord<'_> { type Args = u16; } impl ComputeSize for BaseRecord<'_> { #[allow(clippy::needless_question_mark)] fn compute_size(args: &u16) -> Result { let mark_class_count = *args; Ok((mark_class_count as usize) .checked_mul(Offset16::RAW_BYTE_LEN) .ok_or(ReadError::OutOfBounds)?) } } impl<'a> FontReadWithArgs<'a> for BaseRecord<'a> { fn read_with_args(data: FontData<'a>, args: &u16) -> Result { let mut cursor = data.cursor(); let mark_class_count = *args; Ok(Self { base_anchor_offsets: cursor.read_array(mark_class_count as usize)?, }) } } #[allow(clippy::needless_lifetimes)] impl<'a> BaseRecord<'a> { /// A constructor that requires additional arguments. /// /// This type requires some external state in order to be /// parsed. pub fn read(data: FontData<'a>, mark_class_count: u16) -> Result { let args = mark_class_count; Self::read_with_args(data, &args) } } #[cfg(feature = "experimental_traverse")] impl<'a> SomeRecord<'a> for BaseRecord<'a> { fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> { RecordResolver { name: "BaseRecord", get_field: Box::new(move |idx, _data| match idx { 0usize => Some({ Field::new( "base_anchor_offsets", FieldType::array_of_offsets( better_type_name::(), self.base_anchor_offsets(), move |off| { let target = off.get().resolve::(data); FieldType::offset(off.get(), target) }, ), ) }), _ => None, }), data, } } } impl Format for MarkLigPosFormat1Marker { const FORMAT: u16 = 1; } /// [Mark-to-Ligature Positioning Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#mark-to-ligature-attachment-positioning-format-1-mark-to-ligature-attachment): Mark-to-Ligature Attachment #[derive(Debug, Clone, Copy)] #[doc(hidden)] pub struct MarkLigPosFormat1Marker {} impl MarkLigPosFormat1Marker { pub fn pos_format_byte_range(&self) -> Range { let start = 0; start..start + u16::RAW_BYTE_LEN } pub fn mark_coverage_offset_byte_range(&self) -> Range { let start = self.pos_format_byte_range().end; start..start + Offset16::RAW_BYTE_LEN } pub fn ligature_coverage_offset_byte_range(&self) -> Range { let start = self.mark_coverage_offset_byte_range().end; start..start + Offset16::RAW_BYTE_LEN } pub fn mark_class_count_byte_range(&self) -> Range { let start = self.ligature_coverage_offset_byte_range().end; start..start + u16::RAW_BYTE_LEN } pub fn mark_array_offset_byte_range(&self) -> Range { let start = self.mark_class_count_byte_range().end; start..start + Offset16::RAW_BYTE_LEN } pub fn ligature_array_offset_byte_range(&self) -> Range { let start = self.mark_array_offset_byte_range().end; start..start + Offset16::RAW_BYTE_LEN } } impl MinByteRange for MarkLigPosFormat1Marker { fn min_byte_range(&self) -> Range { 0..self.ligature_array_offset_byte_range().end } } impl<'a> FontRead<'a> for MarkLigPosFormat1<'a> { fn read(data: FontData<'a>) -> Result { let mut cursor = data.cursor(); cursor.advance::(); cursor.advance::(); cursor.advance::(); cursor.advance::(); cursor.advance::(); cursor.advance::(); cursor.finish(MarkLigPosFormat1Marker {}) } } /// [Mark-to-Ligature Positioning Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#mark-to-ligature-attachment-positioning-format-1-mark-to-ligature-attachment): Mark-to-Ligature Attachment pub type MarkLigPosFormat1<'a> = TableRef<'a, MarkLigPosFormat1Marker>; #[allow(clippy::needless_lifetimes)] impl<'a> MarkLigPosFormat1<'a> { /// Format identifier: format = 1 pub fn pos_format(&self) -> u16 { let range = self.shape.pos_format_byte_range(); self.data.read_at(range.start).unwrap() } /// Offset to markCoverage table, from beginning of MarkLigPos /// subtable. pub fn mark_coverage_offset(&self) -> Offset16 { let range = self.shape.mark_coverage_offset_byte_range(); self.data.read_at(range.start).unwrap() } /// Attempt to resolve [`mark_coverage_offset`][Self::mark_coverage_offset]. pub fn mark_coverage(&self) -> Result, ReadError> { let data = self.data; self.mark_coverage_offset().resolve(data) } /// Offset to ligatureCoverage table, from beginning of MarkLigPos /// subtable. pub fn ligature_coverage_offset(&self) -> Offset16 { let range = self.shape.ligature_coverage_offset_byte_range(); self.data.read_at(range.start).unwrap() } /// Attempt to resolve [`ligature_coverage_offset`][Self::ligature_coverage_offset]. pub fn ligature_coverage(&self) -> Result, ReadError> { let data = self.data; self.ligature_coverage_offset().resolve(data) } /// Number of defined mark classes pub fn mark_class_count(&self) -> u16 { let range = self.shape.mark_class_count_byte_range(); self.data.read_at(range.start).unwrap() } /// Offset to MarkArray table, from beginning of MarkLigPos /// subtable. pub fn mark_array_offset(&self) -> Offset16 { let range = self.shape.mark_array_offset_byte_range(); self.data.read_at(range.start).unwrap() } /// Attempt to resolve [`mark_array_offset`][Self::mark_array_offset]. pub fn mark_array(&self) -> Result, ReadError> { let data = self.data; self.mark_array_offset().resolve(data) } /// Offset to LigatureArray table, from beginning of MarkLigPos /// subtable. pub fn ligature_array_offset(&self) -> Offset16 { let range = self.shape.ligature_array_offset_byte_range(); self.data.read_at(range.start).unwrap() } /// Attempt to resolve [`ligature_array_offset`][Self::ligature_array_offset]. pub fn ligature_array(&self) -> Result, ReadError> { let data = self.data; let args = self.mark_class_count(); self.ligature_array_offset().resolve_with_args(data, &args) } } #[cfg(feature = "experimental_traverse")] impl<'a> SomeTable<'a> for MarkLigPosFormat1<'a> { fn type_name(&self) -> &str { "MarkLigPosFormat1" } fn get_field(&self, idx: usize) -> Option> { match idx { 0usize => Some(Field::new("pos_format", self.pos_format())), 1usize => Some(Field::new( "mark_coverage_offset", FieldType::offset(self.mark_coverage_offset(), self.mark_coverage()), )), 2usize => Some(Field::new( "ligature_coverage_offset", FieldType::offset(self.ligature_coverage_offset(), self.ligature_coverage()), )), 3usize => Some(Field::new("mark_class_count", self.mark_class_count())), 4usize => Some(Field::new( "mark_array_offset", FieldType::offset(self.mark_array_offset(), self.mark_array()), )), 5usize => Some(Field::new( "ligature_array_offset", FieldType::offset(self.ligature_array_offset(), self.ligature_array()), )), _ => None, } } } #[cfg(feature = "experimental_traverse")] #[allow(clippy::needless_lifetimes)] impl<'a> std::fmt::Debug for MarkLigPosFormat1<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { (self as &dyn SomeTable<'a>).fmt(f) } } /// Part of [MarkLigPosFormat1] #[derive(Debug, Clone, Copy)] #[doc(hidden)] pub struct LigatureArrayMarker { mark_class_count: u16, ligature_attach_offsets_byte_len: usize, } impl LigatureArrayMarker { pub fn ligature_count_byte_range(&self) -> Range { let start = 0; start..start + u16::RAW_BYTE_LEN } pub fn ligature_attach_offsets_byte_range(&self) -> Range { let start = self.ligature_count_byte_range().end; start..start + self.ligature_attach_offsets_byte_len } } impl MinByteRange for LigatureArrayMarker { fn min_byte_range(&self) -> Range { 0..self.ligature_attach_offsets_byte_range().end } } impl ReadArgs for LigatureArray<'_> { type Args = u16; } impl<'a> FontReadWithArgs<'a> for LigatureArray<'a> { fn read_with_args(data: FontData<'a>, args: &u16) -> Result { let mark_class_count = *args; let mut cursor = data.cursor(); let ligature_count: u16 = cursor.read()?; let ligature_attach_offsets_byte_len = (ligature_count as usize) .checked_mul(Offset16::RAW_BYTE_LEN) .ok_or(ReadError::OutOfBounds)?; cursor.advance_by(ligature_attach_offsets_byte_len); cursor.finish(LigatureArrayMarker { mark_class_count, ligature_attach_offsets_byte_len, }) } } impl<'a> LigatureArray<'a> { /// A constructor that requires additional arguments. /// /// This type requires some external state in order to be /// parsed. pub fn read(data: FontData<'a>, mark_class_count: u16) -> Result { let args = mark_class_count; Self::read_with_args(data, &args) } } /// Part of [MarkLigPosFormat1] pub type LigatureArray<'a> = TableRef<'a, LigatureArrayMarker>; #[allow(clippy::needless_lifetimes)] impl<'a> LigatureArray<'a> { /// Number of LigatureAttach table offsets pub fn ligature_count(&self) -> u16 { let range = self.shape.ligature_count_byte_range(); self.data.read_at(range.start).unwrap() } /// Array of offsets to LigatureAttach tables. Offsets are from /// beginning of LigatureArray table, ordered by ligatureCoverage /// index. pub fn ligature_attach_offsets(&self) -> &'a [BigEndian] { let range = self.shape.ligature_attach_offsets_byte_range(); self.data.read_array(range).unwrap() } /// A dynamically resolving wrapper for [`ligature_attach_offsets`][Self::ligature_attach_offsets]. pub fn ligature_attaches(&self) -> ArrayOfOffsets<'a, LigatureAttach<'a>, Offset16> { let data = self.data; let offsets = self.ligature_attach_offsets(); let args = self.mark_class_count(); ArrayOfOffsets::new(offsets, data, args) } pub(crate) fn mark_class_count(&self) -> u16 { self.shape.mark_class_count } } #[cfg(feature = "experimental_traverse")] impl<'a> SomeTable<'a> for LigatureArray<'a> { fn type_name(&self) -> &str { "LigatureArray" } fn get_field(&self, idx: usize) -> Option> { match idx { 0usize => Some(Field::new("ligature_count", self.ligature_count())), 1usize => Some({ let data = self.data; let args = self.mark_class_count(); Field::new( "ligature_attach_offsets", FieldType::array_of_offsets( better_type_name::(), self.ligature_attach_offsets(), move |off| { let target = off.get().resolve_with_args::(data, &args); FieldType::offset(off.get(), target) }, ), ) }), _ => None, } } } #[cfg(feature = "experimental_traverse")] #[allow(clippy::needless_lifetimes)] impl<'a> std::fmt::Debug for LigatureArray<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { (self as &dyn SomeTable<'a>).fmt(f) } } /// Part of [MarkLigPosFormat1] #[derive(Debug, Clone, Copy)] #[doc(hidden)] pub struct LigatureAttachMarker { mark_class_count: u16, component_records_byte_len: usize, } impl LigatureAttachMarker { pub fn component_count_byte_range(&self) -> Range { let start = 0; start..start + u16::RAW_BYTE_LEN } pub fn component_records_byte_range(&self) -> Range { let start = self.component_count_byte_range().end; start..start + self.component_records_byte_len } } impl MinByteRange for LigatureAttachMarker { fn min_byte_range(&self) -> Range { 0..self.component_records_byte_range().end } } impl ReadArgs for LigatureAttach<'_> { type Args = u16; } impl<'a> FontReadWithArgs<'a> for LigatureAttach<'a> { fn read_with_args(data: FontData<'a>, args: &u16) -> Result { let mark_class_count = *args; let mut cursor = data.cursor(); let component_count: u16 = cursor.read()?; let component_records_byte_len = (component_count as usize) .checked_mul(::compute_size( &mark_class_count, )?) .ok_or(ReadError::OutOfBounds)?; cursor.advance_by(component_records_byte_len); cursor.finish(LigatureAttachMarker { mark_class_count, component_records_byte_len, }) } } impl<'a> LigatureAttach<'a> { /// A constructor that requires additional arguments. /// /// This type requires some external state in order to be /// parsed. pub fn read(data: FontData<'a>, mark_class_count: u16) -> Result { let args = mark_class_count; Self::read_with_args(data, &args) } } /// Part of [MarkLigPosFormat1] pub type LigatureAttach<'a> = TableRef<'a, LigatureAttachMarker>; #[allow(clippy::needless_lifetimes)] impl<'a> LigatureAttach<'a> { /// Number of ComponentRecords in this ligature pub fn component_count(&self) -> u16 { let range = self.shape.component_count_byte_range(); self.data.read_at(range.start).unwrap() } /// Array of Component records, ordered in writing direction. pub fn component_records(&self) -> ComputedArray<'a, ComponentRecord<'a>> { let range = self.shape.component_records_byte_range(); self.data .read_with_args(range, &self.mark_class_count()) .unwrap() } pub(crate) fn mark_class_count(&self) -> u16 { self.shape.mark_class_count } } #[cfg(feature = "experimental_traverse")] impl<'a> SomeTable<'a> for LigatureAttach<'a> { fn type_name(&self) -> &str { "LigatureAttach" } fn get_field(&self, idx: usize) -> Option> { match idx { 0usize => Some(Field::new("component_count", self.component_count())), 1usize => Some(Field::new( "component_records", traversal::FieldType::computed_array( "ComponentRecord", self.component_records(), self.offset_data(), ), )), _ => None, } } } #[cfg(feature = "experimental_traverse")] #[allow(clippy::needless_lifetimes)] impl<'a> std::fmt::Debug for LigatureAttach<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { (self as &dyn SomeTable<'a>).fmt(f) } } /// Part of [MarkLigPosFormat1] #[derive(Clone, Debug)] pub struct ComponentRecord<'a> { /// Array of offsets (one per class) to Anchor tables. Offsets are /// from beginning of LigatureAttach table, ordered by class /// (offsets may be NULL). pub ligature_anchor_offsets: &'a [BigEndian>], } impl<'a> ComponentRecord<'a> { /// Array of offsets (one per class) to Anchor tables. Offsets are /// from beginning of LigatureAttach table, ordered by class /// (offsets may be NULL). pub fn ligature_anchor_offsets(&self) -> &'a [BigEndian>] { self.ligature_anchor_offsets } /// Array of offsets (one per class) to Anchor tables. Offsets are /// from beginning of LigatureAttach table, ordered by class /// (offsets may be NULL). /// /// The `data` argument should be retrieved from the parent table /// By calling its `offset_data` method. pub fn ligature_anchors( &self, data: FontData<'a>, ) -> ArrayOfNullableOffsets<'a, AnchorTable<'a>, Offset16> { let offsets = self.ligature_anchor_offsets(); ArrayOfNullableOffsets::new(offsets, data, ()) } } impl ReadArgs for ComponentRecord<'_> { type Args = u16; } impl ComputeSize for ComponentRecord<'_> { #[allow(clippy::needless_question_mark)] fn compute_size(args: &u16) -> Result { let mark_class_count = *args; Ok((mark_class_count as usize) .checked_mul(Offset16::RAW_BYTE_LEN) .ok_or(ReadError::OutOfBounds)?) } } impl<'a> FontReadWithArgs<'a> for ComponentRecord<'a> { fn read_with_args(data: FontData<'a>, args: &u16) -> Result { let mut cursor = data.cursor(); let mark_class_count = *args; Ok(Self { ligature_anchor_offsets: cursor.read_array(mark_class_count as usize)?, }) } } #[allow(clippy::needless_lifetimes)] impl<'a> ComponentRecord<'a> { /// A constructor that requires additional arguments. /// /// This type requires some external state in order to be /// parsed. pub fn read(data: FontData<'a>, mark_class_count: u16) -> Result { let args = mark_class_count; Self::read_with_args(data, &args) } } #[cfg(feature = "experimental_traverse")] impl<'a> SomeRecord<'a> for ComponentRecord<'a> { fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> { RecordResolver { name: "ComponentRecord", get_field: Box::new(move |idx, _data| match idx { 0usize => Some({ Field::new( "ligature_anchor_offsets", FieldType::array_of_offsets( better_type_name::(), self.ligature_anchor_offsets(), move |off| { let target = off.get().resolve::(data); FieldType::offset(off.get(), target) }, ), ) }), _ => None, }), data, } } } impl Format for MarkMarkPosFormat1Marker { const FORMAT: u16 = 1; } /// [Mark-to-Mark Attachment Positioning Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#mark-to-mark-attachment-positioning-format-1-mark-to-mark-attachment): Mark-to-Mark Attachment #[derive(Debug, Clone, Copy)] #[doc(hidden)] pub struct MarkMarkPosFormat1Marker {} impl MarkMarkPosFormat1Marker { pub fn pos_format_byte_range(&self) -> Range { let start = 0; start..start + u16::RAW_BYTE_LEN } pub fn mark1_coverage_offset_byte_range(&self) -> Range { let start = self.pos_format_byte_range().end; start..start + Offset16::RAW_BYTE_LEN } pub fn mark2_coverage_offset_byte_range(&self) -> Range { let start = self.mark1_coverage_offset_byte_range().end; start..start + Offset16::RAW_BYTE_LEN } pub fn mark_class_count_byte_range(&self) -> Range { let start = self.mark2_coverage_offset_byte_range().end; start..start + u16::RAW_BYTE_LEN } pub fn mark1_array_offset_byte_range(&self) -> Range { let start = self.mark_class_count_byte_range().end; start..start + Offset16::RAW_BYTE_LEN } pub fn mark2_array_offset_byte_range(&self) -> Range { let start = self.mark1_array_offset_byte_range().end; start..start + Offset16::RAW_BYTE_LEN } } impl MinByteRange for MarkMarkPosFormat1Marker { fn min_byte_range(&self) -> Range { 0..self.mark2_array_offset_byte_range().end } } impl<'a> FontRead<'a> for MarkMarkPosFormat1<'a> { fn read(data: FontData<'a>) -> Result { let mut cursor = data.cursor(); cursor.advance::(); cursor.advance::(); cursor.advance::(); cursor.advance::(); cursor.advance::(); cursor.advance::(); cursor.finish(MarkMarkPosFormat1Marker {}) } } /// [Mark-to-Mark Attachment Positioning Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#mark-to-mark-attachment-positioning-format-1-mark-to-mark-attachment): Mark-to-Mark Attachment pub type MarkMarkPosFormat1<'a> = TableRef<'a, MarkMarkPosFormat1Marker>; #[allow(clippy::needless_lifetimes)] impl<'a> MarkMarkPosFormat1<'a> { /// Format identifier: format = 1 pub fn pos_format(&self) -> u16 { let range = self.shape.pos_format_byte_range(); self.data.read_at(range.start).unwrap() } /// Offset to Combining Mark Coverage table, from beginning of /// MarkMarkPos subtable. pub fn mark1_coverage_offset(&self) -> Offset16 { let range = self.shape.mark1_coverage_offset_byte_range(); self.data.read_at(range.start).unwrap() } /// Attempt to resolve [`mark1_coverage_offset`][Self::mark1_coverage_offset]. pub fn mark1_coverage(&self) -> Result, ReadError> { let data = self.data; self.mark1_coverage_offset().resolve(data) } /// Offset to Base Mark Coverage table, from beginning of /// MarkMarkPos subtable. pub fn mark2_coverage_offset(&self) -> Offset16 { let range = self.shape.mark2_coverage_offset_byte_range(); self.data.read_at(range.start).unwrap() } /// Attempt to resolve [`mark2_coverage_offset`][Self::mark2_coverage_offset]. pub fn mark2_coverage(&self) -> Result, ReadError> { let data = self.data; self.mark2_coverage_offset().resolve(data) } /// Number of Combining Mark classes defined pub fn mark_class_count(&self) -> u16 { let range = self.shape.mark_class_count_byte_range(); self.data.read_at(range.start).unwrap() } /// Offset to MarkArray table for mark1, from beginning of /// MarkMarkPos subtable. pub fn mark1_array_offset(&self) -> Offset16 { let range = self.shape.mark1_array_offset_byte_range(); self.data.read_at(range.start).unwrap() } /// Attempt to resolve [`mark1_array_offset`][Self::mark1_array_offset]. pub fn mark1_array(&self) -> Result, ReadError> { let data = self.data; self.mark1_array_offset().resolve(data) } /// Offset to Mark2Array table for mark2, from beginning of /// MarkMarkPos subtable. pub fn mark2_array_offset(&self) -> Offset16 { let range = self.shape.mark2_array_offset_byte_range(); self.data.read_at(range.start).unwrap() } /// Attempt to resolve [`mark2_array_offset`][Self::mark2_array_offset]. pub fn mark2_array(&self) -> Result, ReadError> { let data = self.data; let args = self.mark_class_count(); self.mark2_array_offset().resolve_with_args(data, &args) } } #[cfg(feature = "experimental_traverse")] impl<'a> SomeTable<'a> for MarkMarkPosFormat1<'a> { fn type_name(&self) -> &str { "MarkMarkPosFormat1" } fn get_field(&self, idx: usize) -> Option> { match idx { 0usize => Some(Field::new("pos_format", self.pos_format())), 1usize => Some(Field::new( "mark1_coverage_offset", FieldType::offset(self.mark1_coverage_offset(), self.mark1_coverage()), )), 2usize => Some(Field::new( "mark2_coverage_offset", FieldType::offset(self.mark2_coverage_offset(), self.mark2_coverage()), )), 3usize => Some(Field::new("mark_class_count", self.mark_class_count())), 4usize => Some(Field::new( "mark1_array_offset", FieldType::offset(self.mark1_array_offset(), self.mark1_array()), )), 5usize => Some(Field::new( "mark2_array_offset", FieldType::offset(self.mark2_array_offset(), self.mark2_array()), )), _ => None, } } } #[cfg(feature = "experimental_traverse")] #[allow(clippy::needless_lifetimes)] impl<'a> std::fmt::Debug for MarkMarkPosFormat1<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { (self as &dyn SomeTable<'a>).fmt(f) } } /// Part of [MarkMarkPosFormat1]Class2Record #[derive(Debug, Clone, Copy)] #[doc(hidden)] pub struct Mark2ArrayMarker { mark_class_count: u16, mark2_records_byte_len: usize, } impl Mark2ArrayMarker { pub fn mark2_count_byte_range(&self) -> Range { let start = 0; start..start + u16::RAW_BYTE_LEN } pub fn mark2_records_byte_range(&self) -> Range { let start = self.mark2_count_byte_range().end; start..start + self.mark2_records_byte_len } } impl MinByteRange for Mark2ArrayMarker { fn min_byte_range(&self) -> Range { 0..self.mark2_records_byte_range().end } } impl ReadArgs for Mark2Array<'_> { type Args = u16; } impl<'a> FontReadWithArgs<'a> for Mark2Array<'a> { fn read_with_args(data: FontData<'a>, args: &u16) -> Result { let mark_class_count = *args; let mut cursor = data.cursor(); let mark2_count: u16 = cursor.read()?; let mark2_records_byte_len = (mark2_count as usize) .checked_mul(::compute_size( &mark_class_count, )?) .ok_or(ReadError::OutOfBounds)?; cursor.advance_by(mark2_records_byte_len); cursor.finish(Mark2ArrayMarker { mark_class_count, mark2_records_byte_len, }) } } impl<'a> Mark2Array<'a> { /// A constructor that requires additional arguments. /// /// This type requires some external state in order to be /// parsed. pub fn read(data: FontData<'a>, mark_class_count: u16) -> Result { let args = mark_class_count; Self::read_with_args(data, &args) } } /// Part of [MarkMarkPosFormat1]Class2Record pub type Mark2Array<'a> = TableRef<'a, Mark2ArrayMarker>; #[allow(clippy::needless_lifetimes)] impl<'a> Mark2Array<'a> { /// Number of Mark2 records pub fn mark2_count(&self) -> u16 { let range = self.shape.mark2_count_byte_range(); self.data.read_at(range.start).unwrap() } /// Array of Mark2Records, in Coverage order. pub fn mark2_records(&self) -> ComputedArray<'a, Mark2Record<'a>> { let range = self.shape.mark2_records_byte_range(); self.data .read_with_args(range, &self.mark_class_count()) .unwrap() } pub(crate) fn mark_class_count(&self) -> u16 { self.shape.mark_class_count } } #[cfg(feature = "experimental_traverse")] impl<'a> SomeTable<'a> for Mark2Array<'a> { fn type_name(&self) -> &str { "Mark2Array" } fn get_field(&self, idx: usize) -> Option> { match idx { 0usize => Some(Field::new("mark2_count", self.mark2_count())), 1usize => Some(Field::new( "mark2_records", traversal::FieldType::computed_array( "Mark2Record", self.mark2_records(), self.offset_data(), ), )), _ => None, } } } #[cfg(feature = "experimental_traverse")] #[allow(clippy::needless_lifetimes)] impl<'a> std::fmt::Debug for Mark2Array<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { (self as &dyn SomeTable<'a>).fmt(f) } } /// Part of [MarkMarkPosFormat1] #[derive(Clone, Debug)] pub struct Mark2Record<'a> { /// Array of offsets (one per class) to Anchor tables. Offsets are /// from beginning of Mark2Array table, in class order (offsets may /// be NULL). pub mark2_anchor_offsets: &'a [BigEndian>], } impl<'a> Mark2Record<'a> { /// Array of offsets (one per class) to Anchor tables. Offsets are /// from beginning of Mark2Array table, in class order (offsets may /// be NULL). pub fn mark2_anchor_offsets(&self) -> &'a [BigEndian>] { self.mark2_anchor_offsets } /// Array of offsets (one per class) to Anchor tables. Offsets are /// from beginning of Mark2Array table, in class order (offsets may /// be NULL). /// /// The `data` argument should be retrieved from the parent table /// By calling its `offset_data` method. pub fn mark2_anchors( &self, data: FontData<'a>, ) -> ArrayOfNullableOffsets<'a, AnchorTable<'a>, Offset16> { let offsets = self.mark2_anchor_offsets(); ArrayOfNullableOffsets::new(offsets, data, ()) } } impl ReadArgs for Mark2Record<'_> { type Args = u16; } impl ComputeSize for Mark2Record<'_> { #[allow(clippy::needless_question_mark)] fn compute_size(args: &u16) -> Result { let mark_class_count = *args; Ok((mark_class_count as usize) .checked_mul(Offset16::RAW_BYTE_LEN) .ok_or(ReadError::OutOfBounds)?) } } impl<'a> FontReadWithArgs<'a> for Mark2Record<'a> { fn read_with_args(data: FontData<'a>, args: &u16) -> Result { let mut cursor = data.cursor(); let mark_class_count = *args; Ok(Self { mark2_anchor_offsets: cursor.read_array(mark_class_count as usize)?, }) } } #[allow(clippy::needless_lifetimes)] impl<'a> Mark2Record<'a> { /// A constructor that requires additional arguments. /// /// This type requires some external state in order to be /// parsed. pub fn read(data: FontData<'a>, mark_class_count: u16) -> Result { let args = mark_class_count; Self::read_with_args(data, &args) } } #[cfg(feature = "experimental_traverse")] impl<'a> SomeRecord<'a> for Mark2Record<'a> { fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> { RecordResolver { name: "Mark2Record", get_field: Box::new(move |idx, _data| match idx { 0usize => Some({ Field::new( "mark2_anchor_offsets", FieldType::array_of_offsets( better_type_name::(), self.mark2_anchor_offsets(), move |off| { let target = off.get().resolve::(data); FieldType::offset(off.get(), target) }, ), ) }), _ => None, }), data, } } } impl Format for ExtensionPosFormat1Marker { const FORMAT: u16 = 1; } /// [Extension Positioning Subtable Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#extension-positioning-subtable-format-1) #[derive(Debug)] #[doc(hidden)] pub struct ExtensionPosFormat1Marker { offset_type: std::marker::PhantomData<*const T>, } impl ExtensionPosFormat1Marker { pub fn pos_format_byte_range(&self) -> Range { let start = 0; start..start + u16::RAW_BYTE_LEN } pub fn extension_lookup_type_byte_range(&self) -> Range { let start = self.pos_format_byte_range().end; start..start + u16::RAW_BYTE_LEN } pub fn extension_offset_byte_range(&self) -> Range { let start = self.extension_lookup_type_byte_range().end; start..start + Offset32::RAW_BYTE_LEN } } impl MinByteRange for ExtensionPosFormat1Marker { fn min_byte_range(&self) -> Range { 0..self.extension_offset_byte_range().end } } impl Clone for ExtensionPosFormat1Marker { fn clone(&self) -> Self { *self } } impl Copy for ExtensionPosFormat1Marker {} impl<'a, T> FontRead<'a> for ExtensionPosFormat1<'a, T> { fn read(data: FontData<'a>) -> Result { let mut cursor = data.cursor(); cursor.advance::(); cursor.advance::(); cursor.advance::(); cursor.finish(ExtensionPosFormat1Marker { offset_type: std::marker::PhantomData, }) } } impl<'a> ExtensionPosFormat1<'a, ()> { #[allow(dead_code)] pub(crate) fn into_concrete(self) -> ExtensionPosFormat1<'a, T> { let TableRef { data, .. } = self; TableRef { shape: ExtensionPosFormat1Marker { offset_type: std::marker::PhantomData, }, data, } } } impl<'a, T> ExtensionPosFormat1<'a, T> { #[allow(dead_code)] /// Replace the specific generic type on this implementation with `()` pub(crate) fn of_unit_type(&self) -> ExtensionPosFormat1<'a, ()> { let TableRef { data, .. } = self; TableRef { shape: ExtensionPosFormat1Marker { offset_type: std::marker::PhantomData, }, data: *data, } } } /// [Extension Positioning Subtable Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#extension-positioning-subtable-format-1) pub type ExtensionPosFormat1<'a, T> = TableRef<'a, ExtensionPosFormat1Marker>; #[allow(clippy::needless_lifetimes)] impl<'a, T> ExtensionPosFormat1<'a, T> { /// Format identifier: format = 1 pub fn pos_format(&self) -> u16 { let range = self.shape.pos_format_byte_range(); self.data.read_at(range.start).unwrap() } /// Lookup type of subtable referenced by extensionOffset (i.e. the /// extension subtable). pub fn extension_lookup_type(&self) -> u16 { let range = self.shape.extension_lookup_type_byte_range(); self.data.read_at(range.start).unwrap() } /// Offset to the extension subtable, of lookup type /// extensionLookupType, relative to the start of the /// ExtensionPosFormat1 subtable. pub fn extension_offset(&self) -> Offset32 { let range = self.shape.extension_offset_byte_range(); self.data.read_at(range.start).unwrap() } /// Attempt to resolve [`extension_offset`][Self::extension_offset]. pub fn extension(&self) -> Result where T: FontRead<'a>, { let data = self.data; self.extension_offset().resolve(data) } } #[cfg(feature = "experimental_traverse")] impl<'a, T: FontRead<'a> + SomeTable<'a> + 'a> SomeTable<'a> for ExtensionPosFormat1<'a, T> { fn type_name(&self) -> &str { "ExtensionPosFormat1" } fn get_field(&self, idx: usize) -> Option> { match idx { 0usize => Some(Field::new("pos_format", self.pos_format())), 1usize => Some(Field::new( "extension_lookup_type", self.extension_lookup_type(), )), 2usize => Some(Field::new( "extension_offset", FieldType::offset(self.extension_offset(), self.extension()), )), _ => None, } } } #[cfg(feature = "experimental_traverse")] #[allow(clippy::needless_lifetimes)] impl<'a, T: FontRead<'a> + SomeTable<'a> + 'a> std::fmt::Debug for ExtensionPosFormat1<'a, T> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { (self as &dyn SomeTable<'a>).fmt(f) } } /// A [GPOS Extension Positioning](https://learn.microsoft.com/en-us/typography/opentype/spec/gpos#lookuptype-9-extension-positioning) subtable pub enum ExtensionSubtable<'a> { Single(ExtensionPosFormat1<'a, SinglePos<'a>>), Pair(ExtensionPosFormat1<'a, PairPos<'a>>), Cursive(ExtensionPosFormat1<'a, CursivePosFormat1<'a>>), MarkToBase(ExtensionPosFormat1<'a, MarkBasePosFormat1<'a>>), MarkToLig(ExtensionPosFormat1<'a, MarkLigPosFormat1<'a>>), MarkToMark(ExtensionPosFormat1<'a, MarkMarkPosFormat1<'a>>), Contextual(ExtensionPosFormat1<'a, PositionSequenceContext<'a>>), ChainContextual(ExtensionPosFormat1<'a, PositionChainContext<'a>>), } impl<'a> FontRead<'a> for ExtensionSubtable<'a> { fn read(bytes: FontData<'a>) -> Result { let untyped = ExtensionPosFormat1::read(bytes)?; match untyped.extension_lookup_type() { 1 => Ok(ExtensionSubtable::Single(untyped.into_concrete())), 2 => Ok(ExtensionSubtable::Pair(untyped.into_concrete())), 3 => Ok(ExtensionSubtable::Cursive(untyped.into_concrete())), 4 => Ok(ExtensionSubtable::MarkToBase(untyped.into_concrete())), 5 => Ok(ExtensionSubtable::MarkToLig(untyped.into_concrete())), 6 => Ok(ExtensionSubtable::MarkToMark(untyped.into_concrete())), 7 => Ok(ExtensionSubtable::Contextual(untyped.into_concrete())), 8 => Ok(ExtensionSubtable::ChainContextual(untyped.into_concrete())), other => Err(ReadError::InvalidFormat(other.into())), } } } impl<'a> ExtensionSubtable<'a> { #[allow(dead_code)] /// Return the inner table, removing the specific generics. /// /// This lets us return a single concrete type we can call methods on. pub(crate) fn of_unit_type(&self) -> ExtensionPosFormat1<'a, ()> { match self { ExtensionSubtable::Single(inner) => inner.of_unit_type(), ExtensionSubtable::Pair(inner) => inner.of_unit_type(), ExtensionSubtable::Cursive(inner) => inner.of_unit_type(), ExtensionSubtable::MarkToBase(inner) => inner.of_unit_type(), ExtensionSubtable::MarkToLig(inner) => inner.of_unit_type(), ExtensionSubtable::MarkToMark(inner) => inner.of_unit_type(), ExtensionSubtable::Contextual(inner) => inner.of_unit_type(), ExtensionSubtable::ChainContextual(inner) => inner.of_unit_type(), } } } #[cfg(feature = "experimental_traverse")] impl<'a> ExtensionSubtable<'a> { fn dyn_inner(&self) -> &(dyn SomeTable<'a> + 'a) { match self { ExtensionSubtable::Single(table) => table, ExtensionSubtable::Pair(table) => table, ExtensionSubtable::Cursive(table) => table, ExtensionSubtable::MarkToBase(table) => table, ExtensionSubtable::MarkToLig(table) => table, ExtensionSubtable::MarkToMark(table) => table, ExtensionSubtable::Contextual(table) => table, ExtensionSubtable::ChainContextual(table) => table, } } } #[cfg(feature = "experimental_traverse")] impl<'a> SomeTable<'a> for ExtensionSubtable<'a> { fn get_field(&self, idx: usize) -> Option> { self.dyn_inner().get_field(idx) } fn type_name(&self) -> &str { self.dyn_inner().type_name() } } #[cfg(feature = "experimental_traverse")] impl std::fmt::Debug for ExtensionSubtable<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.dyn_inner().fmt(f) } }