// 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::*; /// [GSUB](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#gsub-header) #[derive(Debug, Clone, Copy)] #[doc(hidden)] pub struct GsubMarker { feature_variations_offset_byte_start: Option, } impl GsubMarker { pub fn version_byte_range(&self) -> Range { let start = 0; start..start + MajorMinor::RAW_BYTE_LEN } pub fn script_list_offset_byte_range(&self) -> Range { let start = self.version_byte_range().end; start..start + Offset16::RAW_BYTE_LEN } pub fn feature_list_offset_byte_range(&self) -> Range { let start = self.script_list_offset_byte_range().end; start..start + Offset16::RAW_BYTE_LEN } pub fn lookup_list_offset_byte_range(&self) -> Range { let start = self.feature_list_offset_byte_range().end; start..start + Offset16::RAW_BYTE_LEN } pub fn feature_variations_offset_byte_range(&self) -> Option> { let start = self.feature_variations_offset_byte_start?; Some(start..start + Offset32::RAW_BYTE_LEN) } } impl MinByteRange for GsubMarker { fn min_byte_range(&self) -> Range { 0..self.lookup_list_offset_byte_range().end } } impl TopLevelTable for Gsub<'_> { /// `GSUB` const TAG: Tag = Tag::new(b"GSUB"); } impl<'a> FontRead<'a> for Gsub<'a> { fn read(data: FontData<'a>) -> Result { let mut cursor = data.cursor(); let version: MajorMinor = cursor.read()?; cursor.advance::(); cursor.advance::(); cursor.advance::(); let feature_variations_offset_byte_start = version .compatible((1u16, 1u16)) .then(|| cursor.position()) .transpose()?; version .compatible((1u16, 1u16)) .then(|| cursor.advance::()); cursor.finish(GsubMarker { feature_variations_offset_byte_start, }) } } /// [GSUB](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#gsub-header) pub type Gsub<'a> = TableRef<'a, GsubMarker>; #[allow(clippy::needless_lifetimes)] impl<'a> Gsub<'a> { /// The major and minor version of the GSUB 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 GSUB table pub fn script_list_offset(&self) -> Offset16 { let range = self.shape.script_list_offset_byte_range(); self.data.read_at(range.start).unwrap() } /// Attempt to resolve [`script_list_offset`][Self::script_list_offset]. pub fn script_list(&self) -> Result, ReadError> { let data = self.data; self.script_list_offset().resolve(data) } /// Offset to FeatureList table, from beginning of GSUB table pub fn feature_list_offset(&self) -> Offset16 { let range = self.shape.feature_list_offset_byte_range(); self.data.read_at(range.start).unwrap() } /// Attempt to resolve [`feature_list_offset`][Self::feature_list_offset]. pub fn feature_list(&self) -> Result, ReadError> { let data = self.data; self.feature_list_offset().resolve(data) } /// Offset to LookupList table, from beginning of GSUB table pub fn lookup_list_offset(&self) -> Offset16 { let range = self.shape.lookup_list_offset_byte_range(); self.data.read_at(range.start).unwrap() } /// Attempt to resolve [`lookup_list_offset`][Self::lookup_list_offset]. pub fn lookup_list(&self) -> Result, ReadError> { let data = self.data; self.lookup_list_offset().resolve(data) } /// Offset to FeatureVariations table, from beginning of the GSUB /// table (may be NULL) pub fn feature_variations_offset(&self) -> Option> { let range = self.shape.feature_variations_offset_byte_range()?; Some(self.data.read_at(range.start).unwrap()) } /// Attempt to resolve [`feature_variations_offset`][Self::feature_variations_offset]. pub fn feature_variations(&self) -> Option, ReadError>> { let data = self.data; self.feature_variations_offset().map(|x| x.resolve(data))? } } #[cfg(feature = "experimental_traverse")] impl<'a> SomeTable<'a> for Gsub<'a> { fn type_name(&self) -> &str { "Gsub" } fn get_field(&self, idx: usize) -> Option> { let version = self.version(); match idx { 0usize => Some(Field::new("version", self.version())), 1usize => Some(Field::new( "script_list_offset", FieldType::offset(self.script_list_offset(), self.script_list()), )), 2usize => Some(Field::new( "feature_list_offset", FieldType::offset(self.feature_list_offset(), self.feature_list()), )), 3usize => Some(Field::new( "lookup_list_offset", FieldType::offset(self.lookup_list_offset(), self.lookup_list()), )), 4usize if version.compatible((1u16, 1u16)) => Some(Field::new( "feature_variations_offset", FieldType::offset( self.feature_variations_offset().unwrap(), self.feature_variations(), ), )), _ => None, } } } #[cfg(feature = "experimental_traverse")] #[allow(clippy::needless_lifetimes)] impl<'a> std::fmt::Debug for Gsub<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { (self as &dyn SomeTable<'a>).fmt(f) } } /// A [GSUB Lookup](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#gsubLookupTypeEnum) subtable. pub enum SubstitutionLookup<'a> { Single(Lookup<'a, SingleSubst<'a>>), Multiple(Lookup<'a, MultipleSubstFormat1<'a>>), Alternate(Lookup<'a, AlternateSubstFormat1<'a>>), Ligature(Lookup<'a, LigatureSubstFormat1<'a>>), Contextual(Lookup<'a, SubstitutionSequenceContext<'a>>), ChainContextual(Lookup<'a, SubstitutionChainContext<'a>>), Extension(Lookup<'a, ExtensionSubtable<'a>>), Reverse(Lookup<'a, ReverseChainSingleSubstFormat1<'a>>), } impl<'a> FontRead<'a> for SubstitutionLookup<'a> { fn read(bytes: FontData<'a>) -> Result { let untyped = Lookup::read(bytes)?; match untyped.lookup_type() { 1 => Ok(SubstitutionLookup::Single(untyped.into_concrete())), 2 => Ok(SubstitutionLookup::Multiple(untyped.into_concrete())), 3 => Ok(SubstitutionLookup::Alternate(untyped.into_concrete())), 4 => Ok(SubstitutionLookup::Ligature(untyped.into_concrete())), 5 => Ok(SubstitutionLookup::Contextual(untyped.into_concrete())), 6 => Ok(SubstitutionLookup::ChainContextual(untyped.into_concrete())), 7 => Ok(SubstitutionLookup::Extension(untyped.into_concrete())), 8 => Ok(SubstitutionLookup::Reverse(untyped.into_concrete())), other => Err(ReadError::InvalidFormat(other.into())), } } } impl<'a> SubstitutionLookup<'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 { SubstitutionLookup::Single(inner) => inner.of_unit_type(), SubstitutionLookup::Multiple(inner) => inner.of_unit_type(), SubstitutionLookup::Alternate(inner) => inner.of_unit_type(), SubstitutionLookup::Ligature(inner) => inner.of_unit_type(), SubstitutionLookup::Contextual(inner) => inner.of_unit_type(), SubstitutionLookup::ChainContextual(inner) => inner.of_unit_type(), SubstitutionLookup::Extension(inner) => inner.of_unit_type(), SubstitutionLookup::Reverse(inner) => inner.of_unit_type(), } } } #[cfg(feature = "experimental_traverse")] impl<'a> SubstitutionLookup<'a> { fn dyn_inner(&self) -> &(dyn SomeTable<'a> + 'a) { match self { SubstitutionLookup::Single(table) => table, SubstitutionLookup::Multiple(table) => table, SubstitutionLookup::Alternate(table) => table, SubstitutionLookup::Ligature(table) => table, SubstitutionLookup::Contextual(table) => table, SubstitutionLookup::ChainContextual(table) => table, SubstitutionLookup::Extension(table) => table, SubstitutionLookup::Reverse(table) => table, } } } #[cfg(feature = "experimental_traverse")] impl<'a> SomeTable<'a> for SubstitutionLookup<'a> { fn get_field(&self, idx: usize) -> Option> { self.dyn_inner().get_field(idx) } fn type_name(&self) -> &str { self.dyn_inner().type_name() } } #[cfg(feature = "experimental_traverse")] impl std::fmt::Debug for SubstitutionLookup<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.dyn_inner().fmt(f) } } /// LookupType 1: [Single Substitution](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#lookuptype-1-single-substitution-subtable) Subtable #[derive(Clone)] pub enum SingleSubst<'a> { Format1(SingleSubstFormat1<'a>), Format2(SingleSubstFormat2<'a>), } impl<'a> SingleSubst<'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 subst_format(&self) -> u16 { match self { Self::Format1(item) => item.subst_format(), Self::Format2(item) => item.subst_format(), } } /// Offset to Coverage table, from beginning of substitution /// subtable pub fn coverage_offset(&self) -> Offset16 { match self { Self::Format1(item) => item.coverage_offset(), Self::Format2(item) => item.coverage_offset(), } } } impl<'a> FontRead<'a> for SingleSubst<'a> { fn read(data: FontData<'a>) -> Result { let format: u16 = data.read_at(0usize)?; match format { SingleSubstFormat1Marker::FORMAT => Ok(Self::Format1(FontRead::read(data)?)), SingleSubstFormat2Marker::FORMAT => Ok(Self::Format2(FontRead::read(data)?)), other => Err(ReadError::InvalidFormat(other.into())), } } } impl MinByteRange for SingleSubst<'_> { fn min_byte_range(&self) -> Range { match self { Self::Format1(item) => item.min_byte_range(), Self::Format2(item) => item.min_byte_range(), } } } #[cfg(feature = "experimental_traverse")] impl<'a> SingleSubst<'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 SingleSubst<'_> { 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 SingleSubst<'a> { fn type_name(&self) -> &str { self.dyn_inner().type_name() } fn get_field(&self, idx: usize) -> Option> { self.dyn_inner().get_field(idx) } } impl Format for SingleSubstFormat1Marker { const FORMAT: u16 = 1; } /// [Single Substitution Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#11-single-substitution-format-1) #[derive(Debug, Clone, Copy)] #[doc(hidden)] pub struct SingleSubstFormat1Marker {} impl SingleSubstFormat1Marker { pub fn subst_format_byte_range(&self) -> Range { let start = 0; start..start + u16::RAW_BYTE_LEN } pub fn coverage_offset_byte_range(&self) -> Range { let start = self.subst_format_byte_range().end; start..start + Offset16::RAW_BYTE_LEN } pub fn delta_glyph_id_byte_range(&self) -> Range { let start = self.coverage_offset_byte_range().end; start..start + i16::RAW_BYTE_LEN } } impl MinByteRange for SingleSubstFormat1Marker { fn min_byte_range(&self) -> Range { 0..self.delta_glyph_id_byte_range().end } } impl<'a> FontRead<'a> for SingleSubstFormat1<'a> { fn read(data: FontData<'a>) -> Result { let mut cursor = data.cursor(); cursor.advance::(); cursor.advance::(); cursor.advance::(); cursor.finish(SingleSubstFormat1Marker {}) } } /// [Single Substitution Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#11-single-substitution-format-1) pub type SingleSubstFormat1<'a> = TableRef<'a, SingleSubstFormat1Marker>; #[allow(clippy::needless_lifetimes)] impl<'a> SingleSubstFormat1<'a> { /// Format identifier: format = 1 pub fn subst_format(&self) -> u16 { let range = self.shape.subst_format_byte_range(); self.data.read_at(range.start).unwrap() } /// Offset to Coverage table, from beginning of substitution /// subtable pub fn coverage_offset(&self) -> Offset16 { let range = self.shape.coverage_offset_byte_range(); self.data.read_at(range.start).unwrap() } /// Attempt to resolve [`coverage_offset`][Self::coverage_offset]. pub fn coverage(&self) -> Result, ReadError> { let data = self.data; self.coverage_offset().resolve(data) } /// Add to original glyph ID to get substitute glyph ID pub fn delta_glyph_id(&self) -> i16 { let range = self.shape.delta_glyph_id_byte_range(); self.data.read_at(range.start).unwrap() } } #[cfg(feature = "experimental_traverse")] impl<'a> SomeTable<'a> for SingleSubstFormat1<'a> { fn type_name(&self) -> &str { "SingleSubstFormat1" } fn get_field(&self, idx: usize) -> Option> { match idx { 0usize => Some(Field::new("subst_format", self.subst_format())), 1usize => Some(Field::new( "coverage_offset", FieldType::offset(self.coverage_offset(), self.coverage()), )), 2usize => Some(Field::new("delta_glyph_id", self.delta_glyph_id())), _ => None, } } } #[cfg(feature = "experimental_traverse")] #[allow(clippy::needless_lifetimes)] impl<'a> std::fmt::Debug for SingleSubstFormat1<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { (self as &dyn SomeTable<'a>).fmt(f) } } impl Format for SingleSubstFormat2Marker { const FORMAT: u16 = 2; } /// [Single Substitution Format 2](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#12-single-substitution-format-2) #[derive(Debug, Clone, Copy)] #[doc(hidden)] pub struct SingleSubstFormat2Marker { substitute_glyph_ids_byte_len: usize, } impl SingleSubstFormat2Marker { pub fn subst_format_byte_range(&self) -> Range { let start = 0; start..start + u16::RAW_BYTE_LEN } pub fn coverage_offset_byte_range(&self) -> Range { let start = self.subst_format_byte_range().end; start..start + Offset16::RAW_BYTE_LEN } pub fn glyph_count_byte_range(&self) -> Range { let start = self.coverage_offset_byte_range().end; start..start + u16::RAW_BYTE_LEN } pub fn substitute_glyph_ids_byte_range(&self) -> Range { let start = self.glyph_count_byte_range().end; start..start + self.substitute_glyph_ids_byte_len } } impl MinByteRange for SingleSubstFormat2Marker { fn min_byte_range(&self) -> Range { 0..self.substitute_glyph_ids_byte_range().end } } impl<'a> FontRead<'a> for SingleSubstFormat2<'a> { fn read(data: FontData<'a>) -> Result { let mut cursor = data.cursor(); cursor.advance::(); cursor.advance::(); let glyph_count: u16 = cursor.read()?; let substitute_glyph_ids_byte_len = (glyph_count as usize) .checked_mul(GlyphId16::RAW_BYTE_LEN) .ok_or(ReadError::OutOfBounds)?; cursor.advance_by(substitute_glyph_ids_byte_len); cursor.finish(SingleSubstFormat2Marker { substitute_glyph_ids_byte_len, }) } } /// [Single Substitution Format 2](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#12-single-substitution-format-2) pub type SingleSubstFormat2<'a> = TableRef<'a, SingleSubstFormat2Marker>; #[allow(clippy::needless_lifetimes)] impl<'a> SingleSubstFormat2<'a> { /// Format identifier: format = 2 pub fn subst_format(&self) -> u16 { let range = self.shape.subst_format_byte_range(); self.data.read_at(range.start).unwrap() } /// Offset to Coverage table, from beginning of substitution /// subtable pub fn coverage_offset(&self) -> Offset16 { let range = self.shape.coverage_offset_byte_range(); self.data.read_at(range.start).unwrap() } /// Attempt to resolve [`coverage_offset`][Self::coverage_offset]. pub fn coverage(&self) -> Result, ReadError> { let data = self.data; self.coverage_offset().resolve(data) } /// Number of glyph IDs in the substituteGlyphIDs array pub fn glyph_count(&self) -> u16 { let range = self.shape.glyph_count_byte_range(); self.data.read_at(range.start).unwrap() } /// Array of substitute glyph IDs — ordered by Coverage index pub fn substitute_glyph_ids(&self) -> &'a [BigEndian] { let range = self.shape.substitute_glyph_ids_byte_range(); self.data.read_array(range).unwrap() } } #[cfg(feature = "experimental_traverse")] impl<'a> SomeTable<'a> for SingleSubstFormat2<'a> { fn type_name(&self) -> &str { "SingleSubstFormat2" } fn get_field(&self, idx: usize) -> Option> { match idx { 0usize => Some(Field::new("subst_format", self.subst_format())), 1usize => Some(Field::new( "coverage_offset", FieldType::offset(self.coverage_offset(), self.coverage()), )), 2usize => Some(Field::new("glyph_count", self.glyph_count())), 3usize => Some(Field::new( "substitute_glyph_ids", self.substitute_glyph_ids(), )), _ => None, } } } #[cfg(feature = "experimental_traverse")] #[allow(clippy::needless_lifetimes)] impl<'a> std::fmt::Debug for SingleSubstFormat2<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { (self as &dyn SomeTable<'a>).fmt(f) } } impl Format for MultipleSubstFormat1Marker { const FORMAT: u16 = 1; } /// [Multiple Substitution Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#21-multiple-substitution-format-1) #[derive(Debug, Clone, Copy)] #[doc(hidden)] pub struct MultipleSubstFormat1Marker { sequence_offsets_byte_len: usize, } impl MultipleSubstFormat1Marker { pub fn subst_format_byte_range(&self) -> Range { let start = 0; start..start + u16::RAW_BYTE_LEN } pub fn coverage_offset_byte_range(&self) -> Range { let start = self.subst_format_byte_range().end; start..start + Offset16::RAW_BYTE_LEN } pub fn sequence_count_byte_range(&self) -> Range { let start = self.coverage_offset_byte_range().end; start..start + u16::RAW_BYTE_LEN } pub fn sequence_offsets_byte_range(&self) -> Range { let start = self.sequence_count_byte_range().end; start..start + self.sequence_offsets_byte_len } } impl MinByteRange for MultipleSubstFormat1Marker { fn min_byte_range(&self) -> Range { 0..self.sequence_offsets_byte_range().end } } impl<'a> FontRead<'a> for MultipleSubstFormat1<'a> { fn read(data: FontData<'a>) -> Result { let mut cursor = data.cursor(); cursor.advance::(); cursor.advance::(); let sequence_count: u16 = cursor.read()?; let sequence_offsets_byte_len = (sequence_count as usize) .checked_mul(Offset16::RAW_BYTE_LEN) .ok_or(ReadError::OutOfBounds)?; cursor.advance_by(sequence_offsets_byte_len); cursor.finish(MultipleSubstFormat1Marker { sequence_offsets_byte_len, }) } } /// [Multiple Substitution Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#21-multiple-substitution-format-1) pub type MultipleSubstFormat1<'a> = TableRef<'a, MultipleSubstFormat1Marker>; #[allow(clippy::needless_lifetimes)] impl<'a> MultipleSubstFormat1<'a> { /// Format identifier: format = 1 pub fn subst_format(&self) -> u16 { let range = self.shape.subst_format_byte_range(); self.data.read_at(range.start).unwrap() } /// Offset to Coverage table, from beginning of substitution /// subtable pub fn coverage_offset(&self) -> Offset16 { let range = self.shape.coverage_offset_byte_range(); self.data.read_at(range.start).unwrap() } /// Attempt to resolve [`coverage_offset`][Self::coverage_offset]. pub fn coverage(&self) -> Result, ReadError> { let data = self.data; self.coverage_offset().resolve(data) } /// Number of Sequence table offsets in the sequenceOffsets array pub fn sequence_count(&self) -> u16 { let range = self.shape.sequence_count_byte_range(); self.data.read_at(range.start).unwrap() } /// Array of offsets to Sequence tables. Offsets are from beginning /// of substitution subtable, ordered by Coverage index pub fn sequence_offsets(&self) -> &'a [BigEndian] { let range = self.shape.sequence_offsets_byte_range(); self.data.read_array(range).unwrap() } /// A dynamically resolving wrapper for [`sequence_offsets`][Self::sequence_offsets]. pub fn sequences(&self) -> ArrayOfOffsets<'a, Sequence<'a>, Offset16> { let data = self.data; let offsets = self.sequence_offsets(); ArrayOfOffsets::new(offsets, data, ()) } } #[cfg(feature = "experimental_traverse")] impl<'a> SomeTable<'a> for MultipleSubstFormat1<'a> { fn type_name(&self) -> &str { "MultipleSubstFormat1" } fn get_field(&self, idx: usize) -> Option> { match idx { 0usize => Some(Field::new("subst_format", self.subst_format())), 1usize => Some(Field::new( "coverage_offset", FieldType::offset(self.coverage_offset(), self.coverage()), )), 2usize => Some(Field::new("sequence_count", self.sequence_count())), 3usize => Some({ let data = self.data; Field::new( "sequence_offsets", FieldType::array_of_offsets( better_type_name::(), self.sequence_offsets(), move |off| { let target = off.get().resolve::(data); FieldType::offset(off.get(), target) }, ), ) }), _ => None, } } } #[cfg(feature = "experimental_traverse")] #[allow(clippy::needless_lifetimes)] impl<'a> std::fmt::Debug for MultipleSubstFormat1<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { (self as &dyn SomeTable<'a>).fmt(f) } } /// Part of [MultipleSubstFormat1] #[derive(Debug, Clone, Copy)] #[doc(hidden)] pub struct SequenceMarker { substitute_glyph_ids_byte_len: usize, } impl SequenceMarker { pub fn glyph_count_byte_range(&self) -> Range { let start = 0; start..start + u16::RAW_BYTE_LEN } pub fn substitute_glyph_ids_byte_range(&self) -> Range { let start = self.glyph_count_byte_range().end; start..start + self.substitute_glyph_ids_byte_len } } impl MinByteRange for SequenceMarker { fn min_byte_range(&self) -> Range { 0..self.substitute_glyph_ids_byte_range().end } } impl<'a> FontRead<'a> for Sequence<'a> { fn read(data: FontData<'a>) -> Result { let mut cursor = data.cursor(); let glyph_count: u16 = cursor.read()?; let substitute_glyph_ids_byte_len = (glyph_count as usize) .checked_mul(GlyphId16::RAW_BYTE_LEN) .ok_or(ReadError::OutOfBounds)?; cursor.advance_by(substitute_glyph_ids_byte_len); cursor.finish(SequenceMarker { substitute_glyph_ids_byte_len, }) } } /// Part of [MultipleSubstFormat1] pub type Sequence<'a> = TableRef<'a, SequenceMarker>; #[allow(clippy::needless_lifetimes)] impl<'a> Sequence<'a> { /// Number of glyph IDs in the substituteGlyphIDs array. This must /// always be greater than 0. pub fn glyph_count(&self) -> u16 { let range = self.shape.glyph_count_byte_range(); self.data.read_at(range.start).unwrap() } /// String of glyph IDs to substitute pub fn substitute_glyph_ids(&self) -> &'a [BigEndian] { let range = self.shape.substitute_glyph_ids_byte_range(); self.data.read_array(range).unwrap() } } #[cfg(feature = "experimental_traverse")] impl<'a> SomeTable<'a> for Sequence<'a> { fn type_name(&self) -> &str { "Sequence" } fn get_field(&self, idx: usize) -> Option> { match idx { 0usize => Some(Field::new("glyph_count", self.glyph_count())), 1usize => Some(Field::new( "substitute_glyph_ids", self.substitute_glyph_ids(), )), _ => None, } } } #[cfg(feature = "experimental_traverse")] #[allow(clippy::needless_lifetimes)] impl<'a> std::fmt::Debug for Sequence<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { (self as &dyn SomeTable<'a>).fmt(f) } } impl Format for AlternateSubstFormat1Marker { const FORMAT: u16 = 1; } /// [Alternate Substitution Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#31-alternate-substitution-format-1) #[derive(Debug, Clone, Copy)] #[doc(hidden)] pub struct AlternateSubstFormat1Marker { alternate_set_offsets_byte_len: usize, } impl AlternateSubstFormat1Marker { pub fn subst_format_byte_range(&self) -> Range { let start = 0; start..start + u16::RAW_BYTE_LEN } pub fn coverage_offset_byte_range(&self) -> Range { let start = self.subst_format_byte_range().end; start..start + Offset16::RAW_BYTE_LEN } pub fn alternate_set_count_byte_range(&self) -> Range { let start = self.coverage_offset_byte_range().end; start..start + u16::RAW_BYTE_LEN } pub fn alternate_set_offsets_byte_range(&self) -> Range { let start = self.alternate_set_count_byte_range().end; start..start + self.alternate_set_offsets_byte_len } } impl MinByteRange for AlternateSubstFormat1Marker { fn min_byte_range(&self) -> Range { 0..self.alternate_set_offsets_byte_range().end } } impl<'a> FontRead<'a> for AlternateSubstFormat1<'a> { fn read(data: FontData<'a>) -> Result { let mut cursor = data.cursor(); cursor.advance::(); cursor.advance::(); let alternate_set_count: u16 = cursor.read()?; let alternate_set_offsets_byte_len = (alternate_set_count as usize) .checked_mul(Offset16::RAW_BYTE_LEN) .ok_or(ReadError::OutOfBounds)?; cursor.advance_by(alternate_set_offsets_byte_len); cursor.finish(AlternateSubstFormat1Marker { alternate_set_offsets_byte_len, }) } } /// [Alternate Substitution Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#31-alternate-substitution-format-1) pub type AlternateSubstFormat1<'a> = TableRef<'a, AlternateSubstFormat1Marker>; #[allow(clippy::needless_lifetimes)] impl<'a> AlternateSubstFormat1<'a> { /// Format identifier: format = 1 pub fn subst_format(&self) -> u16 { let range = self.shape.subst_format_byte_range(); self.data.read_at(range.start).unwrap() } /// Offset to Coverage table, from beginning of substitution /// subtable pub fn coverage_offset(&self) -> Offset16 { let range = self.shape.coverage_offset_byte_range(); self.data.read_at(range.start).unwrap() } /// Attempt to resolve [`coverage_offset`][Self::coverage_offset]. pub fn coverage(&self) -> Result, ReadError> { let data = self.data; self.coverage_offset().resolve(data) } /// Number of AlternateSet tables pub fn alternate_set_count(&self) -> u16 { let range = self.shape.alternate_set_count_byte_range(); self.data.read_at(range.start).unwrap() } /// Array of offsets to AlternateSet tables. Offsets are from /// beginning of substitution subtable, ordered by Coverage index pub fn alternate_set_offsets(&self) -> &'a [BigEndian] { let range = self.shape.alternate_set_offsets_byte_range(); self.data.read_array(range).unwrap() } /// A dynamically resolving wrapper for [`alternate_set_offsets`][Self::alternate_set_offsets]. pub fn alternate_sets(&self) -> ArrayOfOffsets<'a, AlternateSet<'a>, Offset16> { let data = self.data; let offsets = self.alternate_set_offsets(); ArrayOfOffsets::new(offsets, data, ()) } } #[cfg(feature = "experimental_traverse")] impl<'a> SomeTable<'a> for AlternateSubstFormat1<'a> { fn type_name(&self) -> &str { "AlternateSubstFormat1" } fn get_field(&self, idx: usize) -> Option> { match idx { 0usize => Some(Field::new("subst_format", self.subst_format())), 1usize => Some(Field::new( "coverage_offset", FieldType::offset(self.coverage_offset(), self.coverage()), )), 2usize => Some(Field::new( "alternate_set_count", self.alternate_set_count(), )), 3usize => Some({ let data = self.data; Field::new( "alternate_set_offsets", FieldType::array_of_offsets( better_type_name::(), self.alternate_set_offsets(), move |off| { let target = off.get().resolve::(data); FieldType::offset(off.get(), target) }, ), ) }), _ => None, } } } #[cfg(feature = "experimental_traverse")] #[allow(clippy::needless_lifetimes)] impl<'a> std::fmt::Debug for AlternateSubstFormat1<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { (self as &dyn SomeTable<'a>).fmt(f) } } /// Part of [AlternateSubstFormat1] #[derive(Debug, Clone, Copy)] #[doc(hidden)] pub struct AlternateSetMarker { alternate_glyph_ids_byte_len: usize, } impl AlternateSetMarker { pub fn glyph_count_byte_range(&self) -> Range { let start = 0; start..start + u16::RAW_BYTE_LEN } pub fn alternate_glyph_ids_byte_range(&self) -> Range { let start = self.glyph_count_byte_range().end; start..start + self.alternate_glyph_ids_byte_len } } impl MinByteRange for AlternateSetMarker { fn min_byte_range(&self) -> Range { 0..self.alternate_glyph_ids_byte_range().end } } impl<'a> FontRead<'a> for AlternateSet<'a> { fn read(data: FontData<'a>) -> Result { let mut cursor = data.cursor(); let glyph_count: u16 = cursor.read()?; let alternate_glyph_ids_byte_len = (glyph_count as usize) .checked_mul(GlyphId16::RAW_BYTE_LEN) .ok_or(ReadError::OutOfBounds)?; cursor.advance_by(alternate_glyph_ids_byte_len); cursor.finish(AlternateSetMarker { alternate_glyph_ids_byte_len, }) } } /// Part of [AlternateSubstFormat1] pub type AlternateSet<'a> = TableRef<'a, AlternateSetMarker>; #[allow(clippy::needless_lifetimes)] impl<'a> AlternateSet<'a> { /// Number of glyph IDs in the alternateGlyphIDs array pub fn glyph_count(&self) -> u16 { let range = self.shape.glyph_count_byte_range(); self.data.read_at(range.start).unwrap() } /// Array of alternate glyph IDs, in arbitrary order pub fn alternate_glyph_ids(&self) -> &'a [BigEndian] { let range = self.shape.alternate_glyph_ids_byte_range(); self.data.read_array(range).unwrap() } } #[cfg(feature = "experimental_traverse")] impl<'a> SomeTable<'a> for AlternateSet<'a> { fn type_name(&self) -> &str { "AlternateSet" } fn get_field(&self, idx: usize) -> Option> { match idx { 0usize => Some(Field::new("glyph_count", self.glyph_count())), 1usize => Some(Field::new( "alternate_glyph_ids", self.alternate_glyph_ids(), )), _ => None, } } } #[cfg(feature = "experimental_traverse")] #[allow(clippy::needless_lifetimes)] impl<'a> std::fmt::Debug for AlternateSet<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { (self as &dyn SomeTable<'a>).fmt(f) } } impl Format for LigatureSubstFormat1Marker { const FORMAT: u16 = 1; } /// [Ligature Substitution Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#41-ligature-substitution-format-1) #[derive(Debug, Clone, Copy)] #[doc(hidden)] pub struct LigatureSubstFormat1Marker { ligature_set_offsets_byte_len: usize, } impl LigatureSubstFormat1Marker { pub fn subst_format_byte_range(&self) -> Range { let start = 0; start..start + u16::RAW_BYTE_LEN } pub fn coverage_offset_byte_range(&self) -> Range { let start = self.subst_format_byte_range().end; start..start + Offset16::RAW_BYTE_LEN } pub fn ligature_set_count_byte_range(&self) -> Range { let start = self.coverage_offset_byte_range().end; start..start + u16::RAW_BYTE_LEN } pub fn ligature_set_offsets_byte_range(&self) -> Range { let start = self.ligature_set_count_byte_range().end; start..start + self.ligature_set_offsets_byte_len } } impl MinByteRange for LigatureSubstFormat1Marker { fn min_byte_range(&self) -> Range { 0..self.ligature_set_offsets_byte_range().end } } impl<'a> FontRead<'a> for LigatureSubstFormat1<'a> { fn read(data: FontData<'a>) -> Result { let mut cursor = data.cursor(); cursor.advance::(); cursor.advance::(); let ligature_set_count: u16 = cursor.read()?; let ligature_set_offsets_byte_len = (ligature_set_count as usize) .checked_mul(Offset16::RAW_BYTE_LEN) .ok_or(ReadError::OutOfBounds)?; cursor.advance_by(ligature_set_offsets_byte_len); cursor.finish(LigatureSubstFormat1Marker { ligature_set_offsets_byte_len, }) } } /// [Ligature Substitution Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#41-ligature-substitution-format-1) pub type LigatureSubstFormat1<'a> = TableRef<'a, LigatureSubstFormat1Marker>; #[allow(clippy::needless_lifetimes)] impl<'a> LigatureSubstFormat1<'a> { /// Format identifier: format = 1 pub fn subst_format(&self) -> u16 { let range = self.shape.subst_format_byte_range(); self.data.read_at(range.start).unwrap() } /// Offset to Coverage table, from beginning of substitution /// subtable pub fn coverage_offset(&self) -> Offset16 { let range = self.shape.coverage_offset_byte_range(); self.data.read_at(range.start).unwrap() } /// Attempt to resolve [`coverage_offset`][Self::coverage_offset]. pub fn coverage(&self) -> Result, ReadError> { let data = self.data; self.coverage_offset().resolve(data) } /// Number of LigatureSet tables pub fn ligature_set_count(&self) -> u16 { let range = self.shape.ligature_set_count_byte_range(); self.data.read_at(range.start).unwrap() } /// Array of offsets to LigatureSet tables. Offsets are from /// beginning of substitution subtable, ordered by Coverage index pub fn ligature_set_offsets(&self) -> &'a [BigEndian] { let range = self.shape.ligature_set_offsets_byte_range(); self.data.read_array(range).unwrap() } /// A dynamically resolving wrapper for [`ligature_set_offsets`][Self::ligature_set_offsets]. pub fn ligature_sets(&self) -> ArrayOfOffsets<'a, LigatureSet<'a>, Offset16> { let data = self.data; let offsets = self.ligature_set_offsets(); ArrayOfOffsets::new(offsets, data, ()) } } #[cfg(feature = "experimental_traverse")] impl<'a> SomeTable<'a> for LigatureSubstFormat1<'a> { fn type_name(&self) -> &str { "LigatureSubstFormat1" } fn get_field(&self, idx: usize) -> Option> { match idx { 0usize => Some(Field::new("subst_format", self.subst_format())), 1usize => Some(Field::new( "coverage_offset", FieldType::offset(self.coverage_offset(), self.coverage()), )), 2usize => Some(Field::new("ligature_set_count", self.ligature_set_count())), 3usize => Some({ let data = self.data; Field::new( "ligature_set_offsets", FieldType::array_of_offsets( better_type_name::(), self.ligature_set_offsets(), move |off| { let target = off.get().resolve::(data); FieldType::offset(off.get(), target) }, ), ) }), _ => None, } } } #[cfg(feature = "experimental_traverse")] #[allow(clippy::needless_lifetimes)] impl<'a> std::fmt::Debug for LigatureSubstFormat1<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { (self as &dyn SomeTable<'a>).fmt(f) } } /// Part of [LigatureSubstFormat1] #[derive(Debug, Clone, Copy)] #[doc(hidden)] pub struct LigatureSetMarker { ligature_offsets_byte_len: usize, } impl LigatureSetMarker { pub fn ligature_count_byte_range(&self) -> Range { let start = 0; start..start + u16::RAW_BYTE_LEN } pub fn ligature_offsets_byte_range(&self) -> Range { let start = self.ligature_count_byte_range().end; start..start + self.ligature_offsets_byte_len } } impl MinByteRange for LigatureSetMarker { fn min_byte_range(&self) -> Range { 0..self.ligature_offsets_byte_range().end } } impl<'a> FontRead<'a> for LigatureSet<'a> { fn read(data: FontData<'a>) -> Result { let mut cursor = data.cursor(); let ligature_count: u16 = cursor.read()?; let ligature_offsets_byte_len = (ligature_count as usize) .checked_mul(Offset16::RAW_BYTE_LEN) .ok_or(ReadError::OutOfBounds)?; cursor.advance_by(ligature_offsets_byte_len); cursor.finish(LigatureSetMarker { ligature_offsets_byte_len, }) } } /// Part of [LigatureSubstFormat1] pub type LigatureSet<'a> = TableRef<'a, LigatureSetMarker>; #[allow(clippy::needless_lifetimes)] impl<'a> LigatureSet<'a> { /// Number of Ligature tables 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 Ligature tables. Offsets are from beginning /// of LigatureSet table, ordered by preference. pub fn ligature_offsets(&self) -> &'a [BigEndian] { let range = self.shape.ligature_offsets_byte_range(); self.data.read_array(range).unwrap() } /// A dynamically resolving wrapper for [`ligature_offsets`][Self::ligature_offsets]. pub fn ligatures(&self) -> ArrayOfOffsets<'a, Ligature<'a>, Offset16> { let data = self.data; let offsets = self.ligature_offsets(); ArrayOfOffsets::new(offsets, data, ()) } } #[cfg(feature = "experimental_traverse")] impl<'a> SomeTable<'a> for LigatureSet<'a> { fn type_name(&self) -> &str { "LigatureSet" } fn get_field(&self, idx: usize) -> Option> { match idx { 0usize => Some(Field::new("ligature_count", self.ligature_count())), 1usize => Some({ let data = self.data; Field::new( "ligature_offsets", FieldType::array_of_offsets( better_type_name::(), self.ligature_offsets(), move |off| { let target = off.get().resolve::(data); FieldType::offset(off.get(), target) }, ), ) }), _ => None, } } } #[cfg(feature = "experimental_traverse")] #[allow(clippy::needless_lifetimes)] impl<'a> std::fmt::Debug for LigatureSet<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { (self as &dyn SomeTable<'a>).fmt(f) } } /// Part of [LigatureSubstFormat1] #[derive(Debug, Clone, Copy)] #[doc(hidden)] pub struct LigatureMarker { component_glyph_ids_byte_len: usize, } impl LigatureMarker { pub fn ligature_glyph_byte_range(&self) -> Range { let start = 0; start..start + GlyphId16::RAW_BYTE_LEN } pub fn component_count_byte_range(&self) -> Range { let start = self.ligature_glyph_byte_range().end; start..start + u16::RAW_BYTE_LEN } pub fn component_glyph_ids_byte_range(&self) -> Range { let start = self.component_count_byte_range().end; start..start + self.component_glyph_ids_byte_len } } impl MinByteRange for LigatureMarker { fn min_byte_range(&self) -> Range { 0..self.component_glyph_ids_byte_range().end } } impl<'a> FontRead<'a> for Ligature<'a> { fn read(data: FontData<'a>) -> Result { let mut cursor = data.cursor(); cursor.advance::(); let component_count: u16 = cursor.read()?; let component_glyph_ids_byte_len = (transforms::subtract(component_count, 1_usize)) .checked_mul(GlyphId16::RAW_BYTE_LEN) .ok_or(ReadError::OutOfBounds)?; cursor.advance_by(component_glyph_ids_byte_len); cursor.finish(LigatureMarker { component_glyph_ids_byte_len, }) } } /// Part of [LigatureSubstFormat1] pub type Ligature<'a> = TableRef<'a, LigatureMarker>; #[allow(clippy::needless_lifetimes)] impl<'a> Ligature<'a> { /// glyph ID of ligature to substitute pub fn ligature_glyph(&self) -> GlyphId16 { let range = self.shape.ligature_glyph_byte_range(); self.data.read_at(range.start).unwrap() } /// Number of components in the 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 glyph IDs — start with the second /// component, ordered in writing direction pub fn component_glyph_ids(&self) -> &'a [BigEndian] { let range = self.shape.component_glyph_ids_byte_range(); self.data.read_array(range).unwrap() } } #[cfg(feature = "experimental_traverse")] impl<'a> SomeTable<'a> for Ligature<'a> { fn type_name(&self) -> &str { "Ligature" } fn get_field(&self, idx: usize) -> Option> { match idx { 0usize => Some(Field::new("ligature_glyph", self.ligature_glyph())), 1usize => Some(Field::new("component_count", self.component_count())), 2usize => Some(Field::new( "component_glyph_ids", self.component_glyph_ids(), )), _ => None, } } } #[cfg(feature = "experimental_traverse")] #[allow(clippy::needless_lifetimes)] impl<'a> std::fmt::Debug for Ligature<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { (self as &dyn SomeTable<'a>).fmt(f) } } impl Format for ExtensionSubstFormat1Marker { const FORMAT: u16 = 1; } /// [Extension Substitution Subtable Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#71-extension-substitution-subtable-format-1) #[derive(Debug)] #[doc(hidden)] pub struct ExtensionSubstFormat1Marker { offset_type: std::marker::PhantomData<*const T>, } impl ExtensionSubstFormat1Marker { pub fn subst_format_byte_range(&self) -> Range { let start = 0; start..start + u16::RAW_BYTE_LEN } pub fn extension_lookup_type_byte_range(&self) -> Range { let start = self.subst_format_byte_range().end; start..start + u16::RAW_BYTE_LEN } pub fn extension_offset_byte_range(&self) -> Range { let start = self.extension_lookup_type_byte_range().end; start..start + Offset32::RAW_BYTE_LEN } } impl MinByteRange for ExtensionSubstFormat1Marker { fn min_byte_range(&self) -> Range { 0..self.extension_offset_byte_range().end } } impl Clone for ExtensionSubstFormat1Marker { fn clone(&self) -> Self { *self } } impl Copy for ExtensionSubstFormat1Marker {} impl<'a, T> FontRead<'a> for ExtensionSubstFormat1<'a, T> { fn read(data: FontData<'a>) -> Result { let mut cursor = data.cursor(); cursor.advance::(); cursor.advance::(); cursor.advance::(); cursor.finish(ExtensionSubstFormat1Marker { offset_type: std::marker::PhantomData, }) } } impl<'a> ExtensionSubstFormat1<'a, ()> { #[allow(dead_code)] pub(crate) fn into_concrete(self) -> ExtensionSubstFormat1<'a, T> { let TableRef { data, .. } = self; TableRef { shape: ExtensionSubstFormat1Marker { offset_type: std::marker::PhantomData, }, data, } } } impl<'a, T> ExtensionSubstFormat1<'a, T> { #[allow(dead_code)] /// Replace the specific generic type on this implementation with `()` pub(crate) fn of_unit_type(&self) -> ExtensionSubstFormat1<'a, ()> { let TableRef { data, .. } = self; TableRef { shape: ExtensionSubstFormat1Marker { offset_type: std::marker::PhantomData, }, data: *data, } } } /// [Extension Substitution Subtable Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#71-extension-substitution-subtable-format-1) pub type ExtensionSubstFormat1<'a, T> = TableRef<'a, ExtensionSubstFormat1Marker>; #[allow(clippy::needless_lifetimes)] impl<'a, T> ExtensionSubstFormat1<'a, T> { /// Format identifier. Set to 1. pub fn subst_format(&self) -> u16 { let range = self.shape.subst_format_byte_range(); self.data.read_at(range.start).unwrap() } /// Lookup type of subtable referenced by extensionOffset (that is, /// 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 /// ExtensionSubstFormat1 subtable. pub fn extension_offset(&self) -> Offset32 { let range = self.shape.extension_offset_byte_range(); self.data.read_at(range.start).unwrap() } /// Attempt to resolve [`extension_offset`][Self::extension_offset]. pub fn extension(&self) -> Result where T: FontRead<'a>, { let data = self.data; self.extension_offset().resolve(data) } } #[cfg(feature = "experimental_traverse")] impl<'a, T: FontRead<'a> + SomeTable<'a> + 'a> SomeTable<'a> for ExtensionSubstFormat1<'a, T> { fn type_name(&self) -> &str { "ExtensionSubstFormat1" } fn get_field(&self, idx: usize) -> Option> { match idx { 0usize => Some(Field::new("subst_format", self.subst_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 ExtensionSubstFormat1<'a, T> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { (self as &dyn SomeTable<'a>).fmt(f) } } /// A [GSUB Extension Substitution](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#ES) subtable pub enum ExtensionSubtable<'a> { Single(ExtensionSubstFormat1<'a, SingleSubst<'a>>), Multiple(ExtensionSubstFormat1<'a, MultipleSubstFormat1<'a>>), Alternate(ExtensionSubstFormat1<'a, AlternateSubstFormat1<'a>>), Ligature(ExtensionSubstFormat1<'a, LigatureSubstFormat1<'a>>), Contextual(ExtensionSubstFormat1<'a, SubstitutionSequenceContext<'a>>), ChainContextual(ExtensionSubstFormat1<'a, SubstitutionChainContext<'a>>), Reverse(ExtensionSubstFormat1<'a, ReverseChainSingleSubstFormat1<'a>>), } impl<'a> FontRead<'a> for ExtensionSubtable<'a> { fn read(bytes: FontData<'a>) -> Result { let untyped = ExtensionSubstFormat1::read(bytes)?; match untyped.extension_lookup_type() { 1 => Ok(ExtensionSubtable::Single(untyped.into_concrete())), 2 => Ok(ExtensionSubtable::Multiple(untyped.into_concrete())), 3 => Ok(ExtensionSubtable::Alternate(untyped.into_concrete())), 4 => Ok(ExtensionSubtable::Ligature(untyped.into_concrete())), 5 => Ok(ExtensionSubtable::Contextual(untyped.into_concrete())), 6 => Ok(ExtensionSubtable::ChainContextual(untyped.into_concrete())), 8 => Ok(ExtensionSubtable::Reverse(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) -> ExtensionSubstFormat1<'a, ()> { match self { ExtensionSubtable::Single(inner) => inner.of_unit_type(), ExtensionSubtable::Multiple(inner) => inner.of_unit_type(), ExtensionSubtable::Alternate(inner) => inner.of_unit_type(), ExtensionSubtable::Ligature(inner) => inner.of_unit_type(), ExtensionSubtable::Contextual(inner) => inner.of_unit_type(), ExtensionSubtable::ChainContextual(inner) => inner.of_unit_type(), ExtensionSubtable::Reverse(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::Multiple(table) => table, ExtensionSubtable::Alternate(table) => table, ExtensionSubtable::Ligature(table) => table, ExtensionSubtable::Contextual(table) => table, ExtensionSubtable::ChainContextual(table) => table, ExtensionSubtable::Reverse(table) => table, } } } #[cfg(feature = "experimental_traverse")] impl<'a> SomeTable<'a> for ExtensionSubtable<'a> { fn get_field(&self, idx: usize) -> Option> { self.dyn_inner().get_field(idx) } fn type_name(&self) -> &str { self.dyn_inner().type_name() } } #[cfg(feature = "experimental_traverse")] impl std::fmt::Debug for ExtensionSubtable<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.dyn_inner().fmt(f) } } impl Format for ReverseChainSingleSubstFormat1Marker { const FORMAT: u16 = 1; } /// [Reverse Chaining Contextual Single Substitution Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#81-reverse-chaining-contextual-single-substitution-format-1-coverage-based-glyph-contexts) #[derive(Debug, Clone, Copy)] #[doc(hidden)] pub struct ReverseChainSingleSubstFormat1Marker { backtrack_coverage_offsets_byte_len: usize, lookahead_coverage_offsets_byte_len: usize, substitute_glyph_ids_byte_len: usize, } impl ReverseChainSingleSubstFormat1Marker { pub fn subst_format_byte_range(&self) -> Range { let start = 0; start..start + u16::RAW_BYTE_LEN } pub fn coverage_offset_byte_range(&self) -> Range { let start = self.subst_format_byte_range().end; start..start + Offset16::RAW_BYTE_LEN } pub fn backtrack_glyph_count_byte_range(&self) -> Range { let start = self.coverage_offset_byte_range().end; start..start + u16::RAW_BYTE_LEN } pub fn backtrack_coverage_offsets_byte_range(&self) -> Range { let start = self.backtrack_glyph_count_byte_range().end; start..start + self.backtrack_coverage_offsets_byte_len } pub fn lookahead_glyph_count_byte_range(&self) -> Range { let start = self.backtrack_coverage_offsets_byte_range().end; start..start + u16::RAW_BYTE_LEN } pub fn lookahead_coverage_offsets_byte_range(&self) -> Range { let start = self.lookahead_glyph_count_byte_range().end; start..start + self.lookahead_coverage_offsets_byte_len } pub fn glyph_count_byte_range(&self) -> Range { let start = self.lookahead_coverage_offsets_byte_range().end; start..start + u16::RAW_BYTE_LEN } pub fn substitute_glyph_ids_byte_range(&self) -> Range { let start = self.glyph_count_byte_range().end; start..start + self.substitute_glyph_ids_byte_len } } impl MinByteRange for ReverseChainSingleSubstFormat1Marker { fn min_byte_range(&self) -> Range { 0..self.substitute_glyph_ids_byte_range().end } } impl<'a> FontRead<'a> for ReverseChainSingleSubstFormat1<'a> { fn read(data: FontData<'a>) -> Result { let mut cursor = data.cursor(); cursor.advance::(); cursor.advance::(); let backtrack_glyph_count: u16 = cursor.read()?; let backtrack_coverage_offsets_byte_len = (backtrack_glyph_count as usize) .checked_mul(Offset16::RAW_BYTE_LEN) .ok_or(ReadError::OutOfBounds)?; cursor.advance_by(backtrack_coverage_offsets_byte_len); let lookahead_glyph_count: u16 = cursor.read()?; let lookahead_coverage_offsets_byte_len = (lookahead_glyph_count as usize) .checked_mul(Offset16::RAW_BYTE_LEN) .ok_or(ReadError::OutOfBounds)?; cursor.advance_by(lookahead_coverage_offsets_byte_len); let glyph_count: u16 = cursor.read()?; let substitute_glyph_ids_byte_len = (glyph_count as usize) .checked_mul(GlyphId16::RAW_BYTE_LEN) .ok_or(ReadError::OutOfBounds)?; cursor.advance_by(substitute_glyph_ids_byte_len); cursor.finish(ReverseChainSingleSubstFormat1Marker { backtrack_coverage_offsets_byte_len, lookahead_coverage_offsets_byte_len, substitute_glyph_ids_byte_len, }) } } /// [Reverse Chaining Contextual Single Substitution Format 1](https://learn.microsoft.com/en-us/typography/opentype/spec/gsub#81-reverse-chaining-contextual-single-substitution-format-1-coverage-based-glyph-contexts) pub type ReverseChainSingleSubstFormat1<'a> = TableRef<'a, ReverseChainSingleSubstFormat1Marker>; #[allow(clippy::needless_lifetimes)] impl<'a> ReverseChainSingleSubstFormat1<'a> { /// Format identifier: format = 1 pub fn subst_format(&self) -> u16 { let range = self.shape.subst_format_byte_range(); self.data.read_at(range.start).unwrap() } /// Offset to Coverage table, from beginning of substitution /// subtable. pub fn coverage_offset(&self) -> Offset16 { let range = self.shape.coverage_offset_byte_range(); self.data.read_at(range.start).unwrap() } /// Attempt to resolve [`coverage_offset`][Self::coverage_offset]. pub fn coverage(&self) -> Result, ReadError> { let data = self.data; self.coverage_offset().resolve(data) } /// Number of glyphs in the backtrack sequence. pub fn backtrack_glyph_count(&self) -> u16 { let range = self.shape.backtrack_glyph_count_byte_range(); self.data.read_at(range.start).unwrap() } /// Array of offsets to coverage tables in backtrack sequence, in /// glyph sequence order. pub fn backtrack_coverage_offsets(&self) -> &'a [BigEndian] { let range = self.shape.backtrack_coverage_offsets_byte_range(); self.data.read_array(range).unwrap() } /// A dynamically resolving wrapper for [`backtrack_coverage_offsets`][Self::backtrack_coverage_offsets]. pub fn backtrack_coverages(&self) -> ArrayOfOffsets<'a, CoverageTable<'a>, Offset16> { let data = self.data; let offsets = self.backtrack_coverage_offsets(); ArrayOfOffsets::new(offsets, data, ()) } /// Number of glyphs in lookahead sequence. pub fn lookahead_glyph_count(&self) -> u16 { let range = self.shape.lookahead_glyph_count_byte_range(); self.data.read_at(range.start).unwrap() } /// Array of offsets to coverage tables in lookahead sequence, in /// glyph sequence order. pub fn lookahead_coverage_offsets(&self) -> &'a [BigEndian] { let range = self.shape.lookahead_coverage_offsets_byte_range(); self.data.read_array(range).unwrap() } /// A dynamically resolving wrapper for [`lookahead_coverage_offsets`][Self::lookahead_coverage_offsets]. pub fn lookahead_coverages(&self) -> ArrayOfOffsets<'a, CoverageTable<'a>, Offset16> { let data = self.data; let offsets = self.lookahead_coverage_offsets(); ArrayOfOffsets::new(offsets, data, ()) } /// Number of glyph IDs in the substituteGlyphIDs array. pub fn glyph_count(&self) -> u16 { let range = self.shape.glyph_count_byte_range(); self.data.read_at(range.start).unwrap() } /// Array of substitute glyph IDs — ordered by Coverage index. pub fn substitute_glyph_ids(&self) -> &'a [BigEndian] { let range = self.shape.substitute_glyph_ids_byte_range(); self.data.read_array(range).unwrap() } } #[cfg(feature = "experimental_traverse")] impl<'a> SomeTable<'a> for ReverseChainSingleSubstFormat1<'a> { fn type_name(&self) -> &str { "ReverseChainSingleSubstFormat1" } fn get_field(&self, idx: usize) -> Option> { match idx { 0usize => Some(Field::new("subst_format", self.subst_format())), 1usize => Some(Field::new( "coverage_offset", FieldType::offset(self.coverage_offset(), self.coverage()), )), 2usize => Some(Field::new( "backtrack_glyph_count", self.backtrack_glyph_count(), )), 3usize => Some({ let data = self.data; Field::new( "backtrack_coverage_offsets", FieldType::array_of_offsets( better_type_name::(), self.backtrack_coverage_offsets(), move |off| { let target = off.get().resolve::(data); FieldType::offset(off.get(), target) }, ), ) }), 4usize => Some(Field::new( "lookahead_glyph_count", self.lookahead_glyph_count(), )), 5usize => Some({ let data = self.data; Field::new( "lookahead_coverage_offsets", FieldType::array_of_offsets( better_type_name::(), self.lookahead_coverage_offsets(), move |off| { let target = off.get().resolve::(data); FieldType::offset(off.get(), target) }, ), ) }), 6usize => Some(Field::new("glyph_count", self.glyph_count())), 7usize => Some(Field::new( "substitute_glyph_ids", self.substitute_glyph_ids(), )), _ => None, } } } #[cfg(feature = "experimental_traverse")] #[allow(clippy::needless_lifetimes)] impl<'a> std::fmt::Debug for ReverseChainSingleSubstFormat1<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { (self as &dyn SomeTable<'a>).fmt(f) } }