| // 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<usize>, |
| } |
| |
| impl GposMarker { |
| pub fn version_byte_range(&self) -> Range<usize> { |
| let start = 0; |
| start..start + MajorMinor::RAW_BYTE_LEN |
| } |
| |
| pub fn script_list_offset_byte_range(&self) -> Range<usize> { |
| let start = self.version_byte_range().end; |
| start..start + Offset16::RAW_BYTE_LEN |
| } |
| |
| pub fn feature_list_offset_byte_range(&self) -> Range<usize> { |
| let start = self.script_list_offset_byte_range().end; |
| start..start + Offset16::RAW_BYTE_LEN |
| } |
| |
| pub fn lookup_list_offset_byte_range(&self) -> Range<usize> { |
| let start = self.feature_list_offset_byte_range().end; |
| start..start + Offset16::RAW_BYTE_LEN |
| } |
| |
| pub fn feature_variations_offset_byte_range(&self) -> Option<Range<usize>> { |
| 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<usize> { |
| 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<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| let version: MajorMinor = cursor.read()?; |
| cursor.advance::<Offset16>(); |
| cursor.advance::<Offset16>(); |
| cursor.advance::<Offset16>(); |
| let feature_variations_offset_byte_start = version |
| .compatible((1u16, 1u16)) |
| .then(|| cursor.position()) |
| .transpose()?; |
| version |
| .compatible((1u16, 1u16)) |
| .then(|| cursor.advance::<Offset32>()); |
| 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<ScriptList<'a>, 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<FeatureList<'a>, 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<PositionLookupList<'a>, ReadError> { |
| let data = self.data; |
| self.lookup_list_offset().resolve(data) |
| } |
| |
| pub fn feature_variations_offset(&self) -> Option<Nullable<Offset32>> { |
| 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<Result<FeatureVariations<'a>, 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<Field<'a>> { |
| 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<Self, ReadError> { |
| 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<Field<'a>> { |
| 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<Self> { |
| 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 = <u16 as font_types::Scalar>::Raw; |
| fn to_raw(self) -> Self::Raw { |
| self.bits().to_raw() |
| } |
| fn from_raw(raw: Self::Raw) -> Self { |
| let t = <u16>::from_raw(raw); |
| Self::from_bits_truncate(t) |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> From<ValueFormat> 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<Self, ReadError> { |
| 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<usize> { |
| 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<Field<'a>> { |
| self.dyn_inner().get_field(idx) |
| } |
| } |
| |
| impl Format<u16> 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<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn x_coordinate_byte_range(&self) -> Range<usize> { |
| let start = self.anchor_format_byte_range().end; |
| start..start + i16::RAW_BYTE_LEN |
| } |
| |
| pub fn y_coordinate_byte_range(&self) -> Range<usize> { |
| let start = self.x_coordinate_byte_range().end; |
| start..start + i16::RAW_BYTE_LEN |
| } |
| } |
| |
| impl MinByteRange for AnchorFormat1Marker { |
| fn min_byte_range(&self) -> Range<usize> { |
| 0..self.y_coordinate_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for AnchorFormat1<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| cursor.advance::<u16>(); |
| cursor.advance::<i16>(); |
| cursor.advance::<i16>(); |
| 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<Field<'a>> { |
| 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<u16> 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<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn x_coordinate_byte_range(&self) -> Range<usize> { |
| let start = self.anchor_format_byte_range().end; |
| start..start + i16::RAW_BYTE_LEN |
| } |
| |
| pub fn y_coordinate_byte_range(&self) -> Range<usize> { |
| let start = self.x_coordinate_byte_range().end; |
| start..start + i16::RAW_BYTE_LEN |
| } |
| |
| pub fn anchor_point_byte_range(&self) -> Range<usize> { |
| let start = self.y_coordinate_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| } |
| |
| impl MinByteRange for AnchorFormat2Marker { |
| fn min_byte_range(&self) -> Range<usize> { |
| 0..self.anchor_point_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for AnchorFormat2<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| cursor.advance::<u16>(); |
| cursor.advance::<i16>(); |
| cursor.advance::<i16>(); |
| cursor.advance::<u16>(); |
| 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<Field<'a>> { |
| 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<u16> 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<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn x_coordinate_byte_range(&self) -> Range<usize> { |
| let start = self.anchor_format_byte_range().end; |
| start..start + i16::RAW_BYTE_LEN |
| } |
| |
| pub fn y_coordinate_byte_range(&self) -> Range<usize> { |
| let start = self.x_coordinate_byte_range().end; |
| start..start + i16::RAW_BYTE_LEN |
| } |
| |
| pub fn x_device_offset_byte_range(&self) -> Range<usize> { |
| let start = self.y_coordinate_byte_range().end; |
| start..start + Offset16::RAW_BYTE_LEN |
| } |
| |
| pub fn y_device_offset_byte_range(&self) -> Range<usize> { |
| 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<usize> { |
| 0..self.y_device_offset_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for AnchorFormat3<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| cursor.advance::<u16>(); |
| cursor.advance::<i16>(); |
| cursor.advance::<i16>(); |
| cursor.advance::<Offset16>(); |
| cursor.advance::<Offset16>(); |
| 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<Offset16> { |
| 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<Result<DeviceOrVariationIndex<'a>, 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<Offset16> { |
| 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<Result<DeviceOrVariationIndex<'a>, 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<Field<'a>> { |
| 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<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn mark_records_byte_range(&self) -> Range<usize> { |
| 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<usize> { |
| 0..self.mark_records_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for MarkArray<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| 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<Field<'a>> { |
| 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<u16>, |
| /// Offset to Anchor table, from beginning of MarkArray table. |
| pub mark_anchor_offset: BigEndian<Offset16>, |
| } |
| |
| 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<AnchorTable<'a>, 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<Self, ReadError> { |
| 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<usize> { |
| 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<Field<'a>> { |
| self.dyn_inner().get_field(idx) |
| } |
| } |
| |
| impl Format<u16> 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<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn coverage_offset_byte_range(&self) -> Range<usize> { |
| let start = self.pos_format_byte_range().end; |
| start..start + Offset16::RAW_BYTE_LEN |
| } |
| |
| pub fn value_format_byte_range(&self) -> Range<usize> { |
| let start = self.coverage_offset_byte_range().end; |
| start..start + ValueFormat::RAW_BYTE_LEN |
| } |
| |
| pub fn value_record_byte_range(&self) -> Range<usize> { |
| 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<usize> { |
| 0..self.value_record_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for SinglePosFormat1<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| cursor.advance::<u16>(); |
| cursor.advance::<Offset16>(); |
| let value_format: ValueFormat = cursor.read()?; |
| let value_record_byte_len = <ValueRecord as ComputeSize>::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<CoverageTable<'a>, 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<Field<'a>> { |
| 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<u16> 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<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn coverage_offset_byte_range(&self) -> Range<usize> { |
| let start = self.pos_format_byte_range().end; |
| start..start + Offset16::RAW_BYTE_LEN |
| } |
| |
| pub fn value_format_byte_range(&self) -> Range<usize> { |
| let start = self.coverage_offset_byte_range().end; |
| start..start + ValueFormat::RAW_BYTE_LEN |
| } |
| |
| pub fn value_count_byte_range(&self) -> Range<usize> { |
| let start = self.value_format_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn value_records_byte_range(&self) -> Range<usize> { |
| 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<usize> { |
| 0..self.value_records_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for SinglePosFormat2<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| cursor.advance::<u16>(); |
| cursor.advance::<Offset16>(); |
| let value_format: ValueFormat = cursor.read()?; |
| let value_count: u16 = cursor.read()?; |
| let value_records_byte_len = (value_count as usize) |
| .checked_mul(<ValueRecord as ComputeSize>::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<CoverageTable<'a>, 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<Field<'a>> { |
| 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<Self, ReadError> { |
| 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<usize> { |
| 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<Field<'a>> { |
| self.dyn_inner().get_field(idx) |
| } |
| } |
| |
| impl Format<u16> 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<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn coverage_offset_byte_range(&self) -> Range<usize> { |
| let start = self.pos_format_byte_range().end; |
| start..start + Offset16::RAW_BYTE_LEN |
| } |
| |
| pub fn value_format1_byte_range(&self) -> Range<usize> { |
| let start = self.coverage_offset_byte_range().end; |
| start..start + ValueFormat::RAW_BYTE_LEN |
| } |
| |
| pub fn value_format2_byte_range(&self) -> Range<usize> { |
| let start = self.value_format1_byte_range().end; |
| start..start + ValueFormat::RAW_BYTE_LEN |
| } |
| |
| pub fn pair_set_count_byte_range(&self) -> Range<usize> { |
| let start = self.value_format2_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn pair_set_offsets_byte_range(&self) -> Range<usize> { |
| 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<usize> { |
| 0..self.pair_set_offsets_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for PairPosFormat1<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| cursor.advance::<u16>(); |
| cursor.advance::<Offset16>(); |
| cursor.advance::<ValueFormat>(); |
| cursor.advance::<ValueFormat>(); |
| 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<CoverageTable<'a>, 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<Offset16>] { |
| 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<Field<'a>> { |
| 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::<PairSet>(), |
| self.pair_set_offsets(), |
| move |off| { |
| let target = off.get().resolve_with_args::<PairSet>(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<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn pair_value_records_byte_range(&self) -> Range<usize> { |
| 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<usize> { |
| 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<Self, ReadError> { |
| 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(<PairValueRecord as ComputeSize>::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<Self, ReadError> { |
| 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<Field<'a>> { |
| 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<GlyphId16>, |
| /// 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<usize, ReadError> { |
| 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(<ValueRecord as ComputeSize>::compute_size(&value_format1)?) |
| .ok_or(ReadError::OutOfBounds)?; |
| result = result |
| .checked_add(<ValueRecord as ComputeSize>::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<Self, ReadError> { |
| 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<Self, ReadError> { |
| 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<u16> 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<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn coverage_offset_byte_range(&self) -> Range<usize> { |
| let start = self.pos_format_byte_range().end; |
| start..start + Offset16::RAW_BYTE_LEN |
| } |
| |
| pub fn value_format1_byte_range(&self) -> Range<usize> { |
| let start = self.coverage_offset_byte_range().end; |
| start..start + ValueFormat::RAW_BYTE_LEN |
| } |
| |
| pub fn value_format2_byte_range(&self) -> Range<usize> { |
| let start = self.value_format1_byte_range().end; |
| start..start + ValueFormat::RAW_BYTE_LEN |
| } |
| |
| pub fn class_def1_offset_byte_range(&self) -> Range<usize> { |
| let start = self.value_format2_byte_range().end; |
| start..start + Offset16::RAW_BYTE_LEN |
| } |
| |
| pub fn class_def2_offset_byte_range(&self) -> Range<usize> { |
| let start = self.class_def1_offset_byte_range().end; |
| start..start + Offset16::RAW_BYTE_LEN |
| } |
| |
| pub fn class1_count_byte_range(&self) -> Range<usize> { |
| let start = self.class_def2_offset_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn class2_count_byte_range(&self) -> Range<usize> { |
| let start = self.class1_count_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn class1_records_byte_range(&self) -> Range<usize> { |
| 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<usize> { |
| 0..self.class1_records_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for PairPosFormat2<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| cursor.advance::<u16>(); |
| cursor.advance::<Offset16>(); |
| let value_format1: ValueFormat = cursor.read()?; |
| let value_format2: ValueFormat = cursor.read()?; |
| cursor.advance::<Offset16>(); |
| cursor.advance::<Offset16>(); |
| let class1_count: u16 = cursor.read()?; |
| let class2_count: u16 = cursor.read()?; |
| let class1_records_byte_len = (class1_count as usize) |
| .checked_mul(<Class1Record as ComputeSize>::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<CoverageTable<'a>, 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<ClassDef<'a>, 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<ClassDef<'a>, 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<Field<'a>> { |
| 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<usize, ReadError> { |
| let (class2_count, value_format1, value_format2) = *args; |
| Ok((class2_count as usize) |
| .checked_mul(<Class2Record as ComputeSize>::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<Self, ReadError> { |
| 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<Self, ReadError> { |
| 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<usize, ReadError> { |
| let (value_format1, value_format2) = *args; |
| let mut result = 0usize; |
| result = result |
| .checked_add(<ValueRecord as ComputeSize>::compute_size(&value_format1)?) |
| .ok_or(ReadError::OutOfBounds)?; |
| result = result |
| .checked_add(<ValueRecord as ComputeSize>::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<Self, ReadError> { |
| 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<Self, ReadError> { |
| 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<u16> 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<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn coverage_offset_byte_range(&self) -> Range<usize> { |
| let start = self.pos_format_byte_range().end; |
| start..start + Offset16::RAW_BYTE_LEN |
| } |
| |
| pub fn entry_exit_count_byte_range(&self) -> Range<usize> { |
| let start = self.coverage_offset_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn entry_exit_record_byte_range(&self) -> Range<usize> { |
| 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<usize> { |
| 0..self.entry_exit_record_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for CursivePosFormat1<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| cursor.advance::<u16>(); |
| cursor.advance::<Offset16>(); |
| 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<CoverageTable<'a>, 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<Field<'a>> { |
| 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<Nullable<Offset16>>, |
| /// Offset to exitAnchor table, from beginning of CursivePos |
| /// subtable (may be NULL). |
| pub exit_anchor_offset: BigEndian<Nullable<Offset16>>, |
| } |
| |
| impl EntryExitRecord { |
| /// Offset to entryAnchor table, from beginning of CursivePos |
| /// subtable (may be NULL). |
| pub fn entry_anchor_offset(&self) -> Nullable<Offset16> { |
| 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<Result<AnchorTable<'a>, 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<Offset16> { |
| 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<Result<AnchorTable<'a>, 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<u16> 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<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn mark_coverage_offset_byte_range(&self) -> Range<usize> { |
| let start = self.pos_format_byte_range().end; |
| start..start + Offset16::RAW_BYTE_LEN |
| } |
| |
| pub fn base_coverage_offset_byte_range(&self) -> Range<usize> { |
| let start = self.mark_coverage_offset_byte_range().end; |
| start..start + Offset16::RAW_BYTE_LEN |
| } |
| |
| pub fn mark_class_count_byte_range(&self) -> Range<usize> { |
| let start = self.base_coverage_offset_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn mark_array_offset_byte_range(&self) -> Range<usize> { |
| let start = self.mark_class_count_byte_range().end; |
| start..start + Offset16::RAW_BYTE_LEN |
| } |
| |
| pub fn base_array_offset_byte_range(&self) -> Range<usize> { |
| 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<usize> { |
| 0..self.base_array_offset_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for MarkBasePosFormat1<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| cursor.advance::<u16>(); |
| cursor.advance::<Offset16>(); |
| cursor.advance::<Offset16>(); |
| cursor.advance::<u16>(); |
| cursor.advance::<Offset16>(); |
| cursor.advance::<Offset16>(); |
| 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<CoverageTable<'a>, 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<CoverageTable<'a>, 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<MarkArray<'a>, 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<BaseArray<'a>, 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<Field<'a>> { |
| 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<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn base_records_byte_range(&self) -> Range<usize> { |
| 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<usize> { |
| 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<Self, ReadError> { |
| 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(<BaseRecord as ComputeSize>::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<Self, ReadError> { |
| 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<Field<'a>> { |
| 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<Nullable<Offset16>>], |
| } |
| |
| 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<Nullable<Offset16>>] { |
| 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<usize, ReadError> { |
| 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<Self, ReadError> { |
| 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<Self, ReadError> { |
| 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::<AnchorTable>(), |
| self.base_anchor_offsets(), |
| move |off| { |
| let target = off.get().resolve::<AnchorTable>(data); |
| FieldType::offset(off.get(), target) |
| }, |
| ), |
| ) |
| }), |
| _ => None, |
| }), |
| data, |
| } |
| } |
| } |
| |
| impl Format<u16> 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<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn mark_coverage_offset_byte_range(&self) -> Range<usize> { |
| let start = self.pos_format_byte_range().end; |
| start..start + Offset16::RAW_BYTE_LEN |
| } |
| |
| pub fn ligature_coverage_offset_byte_range(&self) -> Range<usize> { |
| let start = self.mark_coverage_offset_byte_range().end; |
| start..start + Offset16::RAW_BYTE_LEN |
| } |
| |
| pub fn mark_class_count_byte_range(&self) -> Range<usize> { |
| let start = self.ligature_coverage_offset_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn mark_array_offset_byte_range(&self) -> Range<usize> { |
| let start = self.mark_class_count_byte_range().end; |
| start..start + Offset16::RAW_BYTE_LEN |
| } |
| |
| pub fn ligature_array_offset_byte_range(&self) -> Range<usize> { |
| 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<usize> { |
| 0..self.ligature_array_offset_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for MarkLigPosFormat1<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| cursor.advance::<u16>(); |
| cursor.advance::<Offset16>(); |
| cursor.advance::<Offset16>(); |
| cursor.advance::<u16>(); |
| cursor.advance::<Offset16>(); |
| cursor.advance::<Offset16>(); |
| 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<CoverageTable<'a>, 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<CoverageTable<'a>, 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<MarkArray<'a>, 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<LigatureArray<'a>, 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<Field<'a>> { |
| 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<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn ligature_attach_offsets_byte_range(&self) -> Range<usize> { |
| 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<usize> { |
| 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<Self, ReadError> { |
| 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<Self, ReadError> { |
| 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<Offset16>] { |
| 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<Field<'a>> { |
| 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::<LigatureAttach>(), |
| self.ligature_attach_offsets(), |
| move |off| { |
| let target = off.get().resolve_with_args::<LigatureAttach>(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<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn component_records_byte_range(&self) -> Range<usize> { |
| 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<usize> { |
| 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<Self, ReadError> { |
| 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(<ComponentRecord as ComputeSize>::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<Self, ReadError> { |
| 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<Field<'a>> { |
| 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<Nullable<Offset16>>], |
| } |
| |
| 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<Nullable<Offset16>>] { |
| 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<usize, ReadError> { |
| 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<Self, ReadError> { |
| 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<Self, ReadError> { |
| 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::<AnchorTable>(), |
| self.ligature_anchor_offsets(), |
| move |off| { |
| let target = off.get().resolve::<AnchorTable>(data); |
| FieldType::offset(off.get(), target) |
| }, |
| ), |
| ) |
| }), |
| _ => None, |
| }), |
| data, |
| } |
| } |
| } |
| |
| impl Format<u16> 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<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn mark1_coverage_offset_byte_range(&self) -> Range<usize> { |
| let start = self.pos_format_byte_range().end; |
| start..start + Offset16::RAW_BYTE_LEN |
| } |
| |
| pub fn mark2_coverage_offset_byte_range(&self) -> Range<usize> { |
| let start = self.mark1_coverage_offset_byte_range().end; |
| start..start + Offset16::RAW_BYTE_LEN |
| } |
| |
| pub fn mark_class_count_byte_range(&self) -> Range<usize> { |
| let start = self.mark2_coverage_offset_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn mark1_array_offset_byte_range(&self) -> Range<usize> { |
| let start = self.mark_class_count_byte_range().end; |
| start..start + Offset16::RAW_BYTE_LEN |
| } |
| |
| pub fn mark2_array_offset_byte_range(&self) -> Range<usize> { |
| 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<usize> { |
| 0..self.mark2_array_offset_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for MarkMarkPosFormat1<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| cursor.advance::<u16>(); |
| cursor.advance::<Offset16>(); |
| cursor.advance::<Offset16>(); |
| cursor.advance::<u16>(); |
| cursor.advance::<Offset16>(); |
| cursor.advance::<Offset16>(); |
| 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<CoverageTable<'a>, 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<CoverageTable<'a>, 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<MarkArray<'a>, 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<Mark2Array<'a>, 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<Field<'a>> { |
| 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<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn mark2_records_byte_range(&self) -> Range<usize> { |
| 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<usize> { |
| 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<Self, ReadError> { |
| 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(<Mark2Record as ComputeSize>::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<Self, ReadError> { |
| 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<Field<'a>> { |
| 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<Nullable<Offset16>>], |
| } |
| |
| 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<Nullable<Offset16>>] { |
| 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<usize, ReadError> { |
| 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<Self, ReadError> { |
| 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<Self, ReadError> { |
| 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::<AnchorTable>(), |
| self.mark2_anchor_offsets(), |
| move |off| { |
| let target = off.get().resolve::<AnchorTable>(data); |
| FieldType::offset(off.get(), target) |
| }, |
| ), |
| ) |
| }), |
| _ => None, |
| }), |
| data, |
| } |
| } |
| } |
| |
| impl Format<u16> 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<T = ()> { |
| offset_type: std::marker::PhantomData<*const T>, |
| } |
| |
| impl<T> ExtensionPosFormat1Marker<T> { |
| pub fn pos_format_byte_range(&self) -> Range<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn extension_lookup_type_byte_range(&self) -> Range<usize> { |
| let start = self.pos_format_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn extension_offset_byte_range(&self) -> Range<usize> { |
| 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<usize> { |
| 0..self.extension_offset_byte_range().end |
| } |
| } |
| |
| impl<T> Clone for ExtensionPosFormat1Marker<T> { |
| fn clone(&self) -> Self { |
| *self |
| } |
| } |
| |
| impl<T> Copy for ExtensionPosFormat1Marker<T> {} |
| |
| impl<'a, T> FontRead<'a> for ExtensionPosFormat1<'a, T> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| cursor.advance::<u16>(); |
| cursor.advance::<u16>(); |
| cursor.advance::<Offset32>(); |
| cursor.finish(ExtensionPosFormat1Marker { |
| offset_type: std::marker::PhantomData, |
| }) |
| } |
| } |
| |
| impl<'a> ExtensionPosFormat1<'a, ()> { |
| #[allow(dead_code)] |
| pub(crate) fn into_concrete<T>(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<T>>; |
| |
| #[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<T, ReadError> |
| 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<Field<'a>> { |
| 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<Self, ReadError> { |
| 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<Field<'a>> { |
| 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) |
| } |
| } |