| // 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::*; |
| |
| /// [Script List Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#script-list-table-and-script-record) |
| #[derive(Debug, Clone, Copy)] |
| #[doc(hidden)] |
| pub struct ScriptListMarker { |
| script_records_byte_len: usize, |
| } |
| |
| impl ScriptListMarker { |
| pub fn script_count_byte_range(&self) -> Range<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn script_records_byte_range(&self) -> Range<usize> { |
| let start = self.script_count_byte_range().end; |
| start..start + self.script_records_byte_len |
| } |
| } |
| |
| impl MinByteRange for ScriptListMarker { |
| fn min_byte_range(&self) -> Range<usize> { |
| 0..self.script_records_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for ScriptList<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| let script_count: u16 = cursor.read()?; |
| let script_records_byte_len = (script_count as usize) |
| .checked_mul(ScriptRecord::RAW_BYTE_LEN) |
| .ok_or(ReadError::OutOfBounds)?; |
| cursor.advance_by(script_records_byte_len); |
| cursor.finish(ScriptListMarker { |
| script_records_byte_len, |
| }) |
| } |
| } |
| |
| /// [Script List Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#script-list-table-and-script-record) |
| pub type ScriptList<'a> = TableRef<'a, ScriptListMarker>; |
| |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> ScriptList<'a> { |
| /// Number of ScriptRecords |
| pub fn script_count(&self) -> u16 { |
| let range = self.shape.script_count_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Array of ScriptRecords, listed alphabetically by script tag |
| pub fn script_records(&self) -> &'a [ScriptRecord] { |
| let range = self.shape.script_records_byte_range(); |
| self.data.read_array(range).unwrap() |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> SomeTable<'a> for ScriptList<'a> { |
| fn type_name(&self) -> &str { |
| "ScriptList" |
| } |
| fn get_field(&self, idx: usize) -> Option<Field<'a>> { |
| match idx { |
| 0usize => Some(Field::new("script_count", self.script_count())), |
| 1usize => Some(Field::new( |
| "script_records", |
| traversal::FieldType::array_of_records( |
| stringify!(ScriptRecord), |
| self.script_records(), |
| self.offset_data(), |
| ), |
| )), |
| _ => None, |
| } |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> std::fmt::Debug for ScriptList<'a> { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| (self as &dyn SomeTable<'a>).fmt(f) |
| } |
| } |
| |
| /// [Script Record](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#script-list-table-and-script-record) |
| #[derive(Clone, Debug, Copy, bytemuck :: AnyBitPattern)] |
| #[repr(C)] |
| #[repr(packed)] |
| pub struct ScriptRecord { |
| /// 4-byte script tag identifier |
| pub script_tag: BigEndian<Tag>, |
| /// Offset to Script table, from beginning of ScriptList |
| pub script_offset: BigEndian<Offset16>, |
| } |
| |
| impl ScriptRecord { |
| /// 4-byte script tag identifier |
| pub fn script_tag(&self) -> Tag { |
| self.script_tag.get() |
| } |
| |
| /// Offset to Script table, from beginning of ScriptList |
| pub fn script_offset(&self) -> Offset16 { |
| self.script_offset.get() |
| } |
| |
| /// Offset to Script table, from beginning of ScriptList |
| /// |
| /// The `data` argument should be retrieved from the parent table |
| /// By calling its `offset_data` method. |
| pub fn script<'a>(&self, data: FontData<'a>) -> Result<Script<'a>, ReadError> { |
| self.script_offset().resolve(data) |
| } |
| } |
| |
| impl FixedSize for ScriptRecord { |
| const RAW_BYTE_LEN: usize = Tag::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN; |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> SomeRecord<'a> for ScriptRecord { |
| fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> { |
| RecordResolver { |
| name: "ScriptRecord", |
| get_field: Box::new(move |idx, _data| match idx { |
| 0usize => Some(Field::new("script_tag", self.script_tag())), |
| 1usize => Some(Field::new( |
| "script_offset", |
| FieldType::offset(self.script_offset(), self.script(_data)), |
| )), |
| _ => None, |
| }), |
| data, |
| } |
| } |
| } |
| |
| /// [Script Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#script-table-and-language-system-record) |
| #[derive(Debug, Clone, Copy)] |
| #[doc(hidden)] |
| pub struct ScriptMarker { |
| lang_sys_records_byte_len: usize, |
| } |
| |
| impl ScriptMarker { |
| pub fn default_lang_sys_offset_byte_range(&self) -> Range<usize> { |
| let start = 0; |
| start..start + Offset16::RAW_BYTE_LEN |
| } |
| |
| pub fn lang_sys_count_byte_range(&self) -> Range<usize> { |
| let start = self.default_lang_sys_offset_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn lang_sys_records_byte_range(&self) -> Range<usize> { |
| let start = self.lang_sys_count_byte_range().end; |
| start..start + self.lang_sys_records_byte_len |
| } |
| } |
| |
| impl MinByteRange for ScriptMarker { |
| fn min_byte_range(&self) -> Range<usize> { |
| 0..self.lang_sys_records_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for Script<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| cursor.advance::<Offset16>(); |
| let lang_sys_count: u16 = cursor.read()?; |
| let lang_sys_records_byte_len = (lang_sys_count as usize) |
| .checked_mul(LangSysRecord::RAW_BYTE_LEN) |
| .ok_or(ReadError::OutOfBounds)?; |
| cursor.advance_by(lang_sys_records_byte_len); |
| cursor.finish(ScriptMarker { |
| lang_sys_records_byte_len, |
| }) |
| } |
| } |
| |
| /// [Script Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#script-table-and-language-system-record) |
| pub type Script<'a> = TableRef<'a, ScriptMarker>; |
| |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> Script<'a> { |
| /// Offset to default LangSys table, from beginning of Script table |
| /// — may be NULL |
| pub fn default_lang_sys_offset(&self) -> Nullable<Offset16> { |
| let range = self.shape.default_lang_sys_offset_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Attempt to resolve [`default_lang_sys_offset`][Self::default_lang_sys_offset]. |
| pub fn default_lang_sys(&self) -> Option<Result<LangSys<'a>, ReadError>> { |
| let data = self.data; |
| self.default_lang_sys_offset().resolve(data) |
| } |
| |
| /// Number of LangSysRecords for this script — excluding the |
| /// default LangSys |
| pub fn lang_sys_count(&self) -> u16 { |
| let range = self.shape.lang_sys_count_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Array of LangSysRecords, listed alphabetically by LangSys tag |
| pub fn lang_sys_records(&self) -> &'a [LangSysRecord] { |
| let range = self.shape.lang_sys_records_byte_range(); |
| self.data.read_array(range).unwrap() |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> SomeTable<'a> for Script<'a> { |
| fn type_name(&self) -> &str { |
| "Script" |
| } |
| fn get_field(&self, idx: usize) -> Option<Field<'a>> { |
| match idx { |
| 0usize => Some(Field::new( |
| "default_lang_sys_offset", |
| FieldType::offset(self.default_lang_sys_offset(), self.default_lang_sys()), |
| )), |
| 1usize => Some(Field::new("lang_sys_count", self.lang_sys_count())), |
| 2usize => Some(Field::new( |
| "lang_sys_records", |
| traversal::FieldType::array_of_records( |
| stringify!(LangSysRecord), |
| self.lang_sys_records(), |
| self.offset_data(), |
| ), |
| )), |
| _ => None, |
| } |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> std::fmt::Debug for Script<'a> { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| (self as &dyn SomeTable<'a>).fmt(f) |
| } |
| } |
| |
| #[derive(Clone, Debug, Copy, bytemuck :: AnyBitPattern)] |
| #[repr(C)] |
| #[repr(packed)] |
| pub struct LangSysRecord { |
| /// 4-byte LangSysTag identifier |
| pub lang_sys_tag: BigEndian<Tag>, |
| /// Offset to LangSys table, from beginning of Script table |
| pub lang_sys_offset: BigEndian<Offset16>, |
| } |
| |
| impl LangSysRecord { |
| /// 4-byte LangSysTag identifier |
| pub fn lang_sys_tag(&self) -> Tag { |
| self.lang_sys_tag.get() |
| } |
| |
| /// Offset to LangSys table, from beginning of Script table |
| pub fn lang_sys_offset(&self) -> Offset16 { |
| self.lang_sys_offset.get() |
| } |
| |
| /// Offset to LangSys table, from beginning of Script table |
| /// |
| /// The `data` argument should be retrieved from the parent table |
| /// By calling its `offset_data` method. |
| pub fn lang_sys<'a>(&self, data: FontData<'a>) -> Result<LangSys<'a>, ReadError> { |
| self.lang_sys_offset().resolve(data) |
| } |
| } |
| |
| impl FixedSize for LangSysRecord { |
| const RAW_BYTE_LEN: usize = Tag::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN; |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> SomeRecord<'a> for LangSysRecord { |
| fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> { |
| RecordResolver { |
| name: "LangSysRecord", |
| get_field: Box::new(move |idx, _data| match idx { |
| 0usize => Some(Field::new("lang_sys_tag", self.lang_sys_tag())), |
| 1usize => Some(Field::new( |
| "lang_sys_offset", |
| FieldType::offset(self.lang_sys_offset(), self.lang_sys(_data)), |
| )), |
| _ => None, |
| }), |
| data, |
| } |
| } |
| } |
| |
| /// [Language System Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#language-system-table) |
| #[derive(Debug, Clone, Copy)] |
| #[doc(hidden)] |
| pub struct LangSysMarker { |
| feature_indices_byte_len: usize, |
| } |
| |
| impl LangSysMarker { |
| pub fn lookup_order_offset_byte_range(&self) -> Range<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn required_feature_index_byte_range(&self) -> Range<usize> { |
| let start = self.lookup_order_offset_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn feature_index_count_byte_range(&self) -> Range<usize> { |
| let start = self.required_feature_index_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn feature_indices_byte_range(&self) -> Range<usize> { |
| let start = self.feature_index_count_byte_range().end; |
| start..start + self.feature_indices_byte_len |
| } |
| } |
| |
| impl MinByteRange for LangSysMarker { |
| fn min_byte_range(&self) -> Range<usize> { |
| 0..self.feature_indices_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for LangSys<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| cursor.advance::<u16>(); |
| cursor.advance::<u16>(); |
| let feature_index_count: u16 = cursor.read()?; |
| let feature_indices_byte_len = (feature_index_count as usize) |
| .checked_mul(u16::RAW_BYTE_LEN) |
| .ok_or(ReadError::OutOfBounds)?; |
| cursor.advance_by(feature_indices_byte_len); |
| cursor.finish(LangSysMarker { |
| feature_indices_byte_len, |
| }) |
| } |
| } |
| |
| /// [Language System Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#language-system-table) |
| pub type LangSys<'a> = TableRef<'a, LangSysMarker>; |
| |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> LangSys<'a> { |
| /// Index of a feature required for this language system; if no |
| /// required features = 0xFFFF |
| pub fn required_feature_index(&self) -> u16 { |
| let range = self.shape.required_feature_index_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Number of feature index values for this language system — |
| /// excludes the required feature |
| pub fn feature_index_count(&self) -> u16 { |
| let range = self.shape.feature_index_count_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Array of indices into the FeatureList, in arbitrary order |
| pub fn feature_indices(&self) -> &'a [BigEndian<u16>] { |
| let range = self.shape.feature_indices_byte_range(); |
| self.data.read_array(range).unwrap() |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> SomeTable<'a> for LangSys<'a> { |
| fn type_name(&self) -> &str { |
| "LangSys" |
| } |
| fn get_field(&self, idx: usize) -> Option<Field<'a>> { |
| match idx { |
| 0usize => Some(Field::new( |
| "required_feature_index", |
| self.required_feature_index(), |
| )), |
| 1usize => Some(Field::new( |
| "feature_index_count", |
| self.feature_index_count(), |
| )), |
| 2usize => Some(Field::new("feature_indices", self.feature_indices())), |
| _ => None, |
| } |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> std::fmt::Debug for LangSys<'a> { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| (self as &dyn SomeTable<'a>).fmt(f) |
| } |
| } |
| |
| /// [Feature List Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#feature-list-table) |
| #[derive(Debug, Clone, Copy)] |
| #[doc(hidden)] |
| pub struct FeatureListMarker { |
| feature_records_byte_len: usize, |
| } |
| |
| impl FeatureListMarker { |
| pub fn feature_count_byte_range(&self) -> Range<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn feature_records_byte_range(&self) -> Range<usize> { |
| let start = self.feature_count_byte_range().end; |
| start..start + self.feature_records_byte_len |
| } |
| } |
| |
| impl MinByteRange for FeatureListMarker { |
| fn min_byte_range(&self) -> Range<usize> { |
| 0..self.feature_records_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for FeatureList<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| let feature_count: u16 = cursor.read()?; |
| let feature_records_byte_len = (feature_count as usize) |
| .checked_mul(FeatureRecord::RAW_BYTE_LEN) |
| .ok_or(ReadError::OutOfBounds)?; |
| cursor.advance_by(feature_records_byte_len); |
| cursor.finish(FeatureListMarker { |
| feature_records_byte_len, |
| }) |
| } |
| } |
| |
| /// [Feature List Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#feature-list-table) |
| pub type FeatureList<'a> = TableRef<'a, FeatureListMarker>; |
| |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> FeatureList<'a> { |
| /// Number of FeatureRecords in this table |
| pub fn feature_count(&self) -> u16 { |
| let range = self.shape.feature_count_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Array of FeatureRecords — zero-based (first feature has |
| /// FeatureIndex = 0), listed alphabetically by feature tag |
| pub fn feature_records(&self) -> &'a [FeatureRecord] { |
| let range = self.shape.feature_records_byte_range(); |
| self.data.read_array(range).unwrap() |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> SomeTable<'a> for FeatureList<'a> { |
| fn type_name(&self) -> &str { |
| "FeatureList" |
| } |
| fn get_field(&self, idx: usize) -> Option<Field<'a>> { |
| match idx { |
| 0usize => Some(Field::new("feature_count", self.feature_count())), |
| 1usize => Some(Field::new( |
| "feature_records", |
| traversal::FieldType::array_of_records( |
| stringify!(FeatureRecord), |
| self.feature_records(), |
| self.offset_data(), |
| ), |
| )), |
| _ => None, |
| } |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> std::fmt::Debug for FeatureList<'a> { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| (self as &dyn SomeTable<'a>).fmt(f) |
| } |
| } |
| |
| /// Part of [FeatureList] |
| #[derive(Clone, Debug, Copy, bytemuck :: AnyBitPattern)] |
| #[repr(C)] |
| #[repr(packed)] |
| pub struct FeatureRecord { |
| /// 4-byte feature identification tag |
| pub feature_tag: BigEndian<Tag>, |
| /// Offset to Feature table, from beginning of FeatureList |
| pub feature_offset: BigEndian<Offset16>, |
| } |
| |
| impl FeatureRecord { |
| /// 4-byte feature identification tag |
| pub fn feature_tag(&self) -> Tag { |
| self.feature_tag.get() |
| } |
| |
| /// Offset to Feature table, from beginning of FeatureList |
| pub fn feature_offset(&self) -> Offset16 { |
| self.feature_offset.get() |
| } |
| |
| /// Offset to Feature table, from beginning of FeatureList |
| /// |
| /// The `data` argument should be retrieved from the parent table |
| /// By calling its `offset_data` method. |
| pub fn feature<'a>(&self, data: FontData<'a>) -> Result<Feature<'a>, ReadError> { |
| let args = self.feature_tag(); |
| self.feature_offset().resolve_with_args(data, &args) |
| } |
| } |
| |
| impl FixedSize for FeatureRecord { |
| const RAW_BYTE_LEN: usize = Tag::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN; |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> SomeRecord<'a> for FeatureRecord { |
| fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> { |
| RecordResolver { |
| name: "FeatureRecord", |
| get_field: Box::new(move |idx, _data| match idx { |
| 0usize => Some(Field::new("feature_tag", self.feature_tag())), |
| 1usize => Some(Field::new( |
| "feature_offset", |
| FieldType::offset(self.feature_offset(), self.feature(_data)), |
| )), |
| _ => None, |
| }), |
| data, |
| } |
| } |
| } |
| |
| /// [Feature Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#feature-table) |
| #[derive(Debug, Clone, Copy)] |
| #[doc(hidden)] |
| pub struct FeatureMarker { |
| feature_tag: Tag, |
| lookup_list_indices_byte_len: usize, |
| } |
| |
| impl FeatureMarker { |
| pub fn feature_params_offset_byte_range(&self) -> Range<usize> { |
| let start = 0; |
| start..start + Offset16::RAW_BYTE_LEN |
| } |
| |
| pub fn lookup_index_count_byte_range(&self) -> Range<usize> { |
| let start = self.feature_params_offset_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn lookup_list_indices_byte_range(&self) -> Range<usize> { |
| let start = self.lookup_index_count_byte_range().end; |
| start..start + self.lookup_list_indices_byte_len |
| } |
| } |
| |
| impl MinByteRange for FeatureMarker { |
| fn min_byte_range(&self) -> Range<usize> { |
| 0..self.lookup_list_indices_byte_range().end |
| } |
| } |
| |
| impl ReadArgs for Feature<'_> { |
| type Args = Tag; |
| } |
| |
| impl<'a> FontReadWithArgs<'a> for Feature<'a> { |
| fn read_with_args(data: FontData<'a>, args: &Tag) -> Result<Self, ReadError> { |
| let feature_tag = *args; |
| let mut cursor = data.cursor(); |
| cursor.advance::<Offset16>(); |
| let lookup_index_count: u16 = cursor.read()?; |
| let lookup_list_indices_byte_len = (lookup_index_count as usize) |
| .checked_mul(u16::RAW_BYTE_LEN) |
| .ok_or(ReadError::OutOfBounds)?; |
| cursor.advance_by(lookup_list_indices_byte_len); |
| cursor.finish(FeatureMarker { |
| feature_tag, |
| lookup_list_indices_byte_len, |
| }) |
| } |
| } |
| |
| impl<'a> Feature<'a> { |
| /// A constructor that requires additional arguments. |
| /// |
| /// This type requires some external state in order to be |
| /// parsed. |
| pub fn read(data: FontData<'a>, feature_tag: Tag) -> Result<Self, ReadError> { |
| let args = feature_tag; |
| Self::read_with_args(data, &args) |
| } |
| } |
| |
| /// [Feature Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#feature-table) |
| pub type Feature<'a> = TableRef<'a, FeatureMarker>; |
| |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> Feature<'a> { |
| /// Offset from start of Feature table to FeatureParams table, if defined for the feature and present, else NULL |
| pub fn feature_params_offset(&self) -> Nullable<Offset16> { |
| let range = self.shape.feature_params_offset_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Attempt to resolve [`feature_params_offset`][Self::feature_params_offset]. |
| pub fn feature_params(&self) -> Option<Result<FeatureParams<'a>, ReadError>> { |
| let data = self.data; |
| let args = self.feature_tag(); |
| self.feature_params_offset().resolve_with_args(data, &args) |
| } |
| |
| /// Number of LookupList indices for this feature |
| pub fn lookup_index_count(&self) -> u16 { |
| let range = self.shape.lookup_index_count_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Array of indices into the LookupList — zero-based (first |
| /// lookup is LookupListIndex = 0) |
| pub fn lookup_list_indices(&self) -> &'a [BigEndian<u16>] { |
| let range = self.shape.lookup_list_indices_byte_range(); |
| self.data.read_array(range).unwrap() |
| } |
| |
| pub(crate) fn feature_tag(&self) -> Tag { |
| self.shape.feature_tag |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> SomeTable<'a> for Feature<'a> { |
| fn type_name(&self) -> &str { |
| "Feature" |
| } |
| fn get_field(&self, idx: usize) -> Option<Field<'a>> { |
| match idx { |
| 0usize => Some(Field::new( |
| "feature_params_offset", |
| FieldType::offset(self.feature_params_offset(), self.feature_params()), |
| )), |
| 1usize => Some(Field::new("lookup_index_count", self.lookup_index_count())), |
| 2usize => Some(Field::new( |
| "lookup_list_indices", |
| self.lookup_list_indices(), |
| )), |
| _ => None, |
| } |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> std::fmt::Debug for Feature<'a> { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| (self as &dyn SomeTable<'a>).fmt(f) |
| } |
| } |
| |
| /// [Lookup List Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#lookup-list-table) |
| #[derive(Debug)] |
| #[doc(hidden)] |
| pub struct LookupListMarker<T = ()> { |
| lookup_offsets_byte_len: usize, |
| offset_type: std::marker::PhantomData<*const T>, |
| } |
| |
| impl<T> LookupListMarker<T> { |
| pub fn lookup_count_byte_range(&self) -> Range<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn lookup_offsets_byte_range(&self) -> Range<usize> { |
| let start = self.lookup_count_byte_range().end; |
| start..start + self.lookup_offsets_byte_len |
| } |
| } |
| |
| impl MinByteRange for LookupListMarker { |
| fn min_byte_range(&self) -> Range<usize> { |
| 0..self.lookup_offsets_byte_range().end |
| } |
| } |
| |
| impl<T> Clone for LookupListMarker<T> { |
| fn clone(&self) -> Self { |
| *self |
| } |
| } |
| |
| impl<T> Copy for LookupListMarker<T> {} |
| |
| impl<'a, T> FontRead<'a> for LookupList<'a, T> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| let lookup_count: u16 = cursor.read()?; |
| let lookup_offsets_byte_len = (lookup_count as usize) |
| .checked_mul(Offset16::RAW_BYTE_LEN) |
| .ok_or(ReadError::OutOfBounds)?; |
| cursor.advance_by(lookup_offsets_byte_len); |
| cursor.finish(LookupListMarker { |
| lookup_offsets_byte_len, |
| offset_type: std::marker::PhantomData, |
| }) |
| } |
| } |
| |
| impl<'a> LookupList<'a, ()> { |
| #[allow(dead_code)] |
| pub(crate) fn into_concrete<T>(self) -> LookupList<'a, T> { |
| let TableRef { data, shape } = self; |
| TableRef { |
| shape: LookupListMarker { |
| lookup_offsets_byte_len: shape.lookup_offsets_byte_len, |
| offset_type: std::marker::PhantomData, |
| }, |
| data, |
| } |
| } |
| } |
| |
| impl<'a, T> LookupList<'a, T> { |
| #[allow(dead_code)] |
| /// Replace the specific generic type on this implementation with `()` |
| pub(crate) fn of_unit_type(&self) -> LookupList<'a, ()> { |
| let TableRef { data, shape } = self; |
| TableRef { |
| shape: LookupListMarker { |
| lookup_offsets_byte_len: shape.lookup_offsets_byte_len, |
| offset_type: std::marker::PhantomData, |
| }, |
| data: *data, |
| } |
| } |
| } |
| |
| /// [Lookup List Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#lookup-list-table) |
| pub type LookupList<'a, T> = TableRef<'a, LookupListMarker<T>>; |
| |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a, T> LookupList<'a, T> { |
| /// Number of lookups in this table |
| pub fn lookup_count(&self) -> u16 { |
| let range = self.shape.lookup_count_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Array of offsets to Lookup tables, from beginning of LookupList |
| /// — zero based (first lookup is Lookup index = 0) |
| pub fn lookup_offsets(&self) -> &'a [BigEndian<Offset16>] { |
| let range = self.shape.lookup_offsets_byte_range(); |
| self.data.read_array(range).unwrap() |
| } |
| |
| /// A dynamically resolving wrapper for [`lookup_offsets`][Self::lookup_offsets]. |
| pub fn lookups(&self) -> ArrayOfOffsets<'a, T, Offset16> |
| where |
| T: FontRead<'a>, |
| { |
| let data = self.data; |
| let offsets = self.lookup_offsets(); |
| ArrayOfOffsets::new(offsets, data, ()) |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a, T: FontRead<'a> + SomeTable<'a> + 'a> SomeTable<'a> for LookupList<'a, T> { |
| fn type_name(&self) -> &str { |
| "LookupList" |
| } |
| fn get_field(&self, idx: usize) -> Option<Field<'a>> { |
| match idx { |
| 0usize => Some(Field::new("lookup_count", self.lookup_count())), |
| 1usize => Some({ |
| let data = self.data; |
| Field::new( |
| "lookup_offsets", |
| FieldType::array_of_offsets( |
| better_type_name::<T>(), |
| self.lookup_offsets(), |
| move |off| { |
| let target = off.get().resolve::<T>(data); |
| FieldType::offset(off.get(), target) |
| }, |
| ), |
| ) |
| }), |
| _ => None, |
| } |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a, T: FontRead<'a> + SomeTable<'a> + 'a> std::fmt::Debug for LookupList<'a, T> { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| (self as &dyn SomeTable<'a>).fmt(f) |
| } |
| } |
| |
| /// [Lookup Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#lookup-table) |
| #[derive(Debug)] |
| #[doc(hidden)] |
| pub struct LookupMarker<T = ()> { |
| subtable_offsets_byte_len: usize, |
| mark_filtering_set_byte_start: Option<usize>, |
| offset_type: std::marker::PhantomData<*const T>, |
| } |
| |
| impl<T> LookupMarker<T> { |
| pub fn lookup_type_byte_range(&self) -> Range<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn lookup_flag_byte_range(&self) -> Range<usize> { |
| let start = self.lookup_type_byte_range().end; |
| start..start + LookupFlag::RAW_BYTE_LEN |
| } |
| |
| pub fn sub_table_count_byte_range(&self) -> Range<usize> { |
| let start = self.lookup_flag_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn subtable_offsets_byte_range(&self) -> Range<usize> { |
| let start = self.sub_table_count_byte_range().end; |
| start..start + self.subtable_offsets_byte_len |
| } |
| |
| pub fn mark_filtering_set_byte_range(&self) -> Option<Range<usize>> { |
| let start = self.mark_filtering_set_byte_start?; |
| Some(start..start + u16::RAW_BYTE_LEN) |
| } |
| } |
| |
| impl MinByteRange for LookupMarker { |
| fn min_byte_range(&self) -> Range<usize> { |
| 0..self.subtable_offsets_byte_range().end |
| } |
| } |
| |
| impl<T> Clone for LookupMarker<T> { |
| fn clone(&self) -> Self { |
| *self |
| } |
| } |
| |
| impl<T> Copy for LookupMarker<T> {} |
| |
| impl<'a, T> FontRead<'a> for Lookup<'a, T> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| cursor.advance::<u16>(); |
| let lookup_flag: LookupFlag = cursor.read()?; |
| let sub_table_count: u16 = cursor.read()?; |
| let subtable_offsets_byte_len = (sub_table_count as usize) |
| .checked_mul(Offset16::RAW_BYTE_LEN) |
| .ok_or(ReadError::OutOfBounds)?; |
| cursor.advance_by(subtable_offsets_byte_len); |
| let mark_filtering_set_byte_start = lookup_flag |
| .contains(LookupFlag::USE_MARK_FILTERING_SET) |
| .then(|| cursor.position()) |
| .transpose()?; |
| lookup_flag |
| .contains(LookupFlag::USE_MARK_FILTERING_SET) |
| .then(|| cursor.advance::<u16>()); |
| cursor.finish(LookupMarker { |
| subtable_offsets_byte_len, |
| mark_filtering_set_byte_start, |
| offset_type: std::marker::PhantomData, |
| }) |
| } |
| } |
| |
| impl<'a> Lookup<'a, ()> { |
| #[allow(dead_code)] |
| pub(crate) fn into_concrete<T>(self) -> Lookup<'a, T> { |
| let TableRef { data, shape } = self; |
| TableRef { |
| shape: LookupMarker { |
| subtable_offsets_byte_len: shape.subtable_offsets_byte_len, |
| mark_filtering_set_byte_start: shape.mark_filtering_set_byte_start, |
| offset_type: std::marker::PhantomData, |
| }, |
| data, |
| } |
| } |
| } |
| |
| impl<'a, T> Lookup<'a, T> { |
| #[allow(dead_code)] |
| /// Replace the specific generic type on this implementation with `()` |
| pub(crate) fn of_unit_type(&self) -> Lookup<'a, ()> { |
| let TableRef { data, shape } = self; |
| TableRef { |
| shape: LookupMarker { |
| subtable_offsets_byte_len: shape.subtable_offsets_byte_len, |
| mark_filtering_set_byte_start: shape.mark_filtering_set_byte_start, |
| offset_type: std::marker::PhantomData, |
| }, |
| data: *data, |
| } |
| } |
| } |
| |
| /// [Lookup Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#lookup-table) |
| pub type Lookup<'a, T> = TableRef<'a, LookupMarker<T>>; |
| |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a, T> Lookup<'a, T> { |
| /// Different enumerations for GSUB and GPOS |
| pub fn lookup_type(&self) -> u16 { |
| let range = self.shape.lookup_type_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Lookup qualifiers |
| pub fn lookup_flag(&self) -> LookupFlag { |
| let range = self.shape.lookup_flag_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Number of subtables for this lookup |
| pub fn sub_table_count(&self) -> u16 { |
| let range = self.shape.sub_table_count_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Array of offsets to lookup subtables, from beginning of Lookup |
| /// table |
| pub fn subtable_offsets(&self) -> &'a [BigEndian<Offset16>] { |
| let range = self.shape.subtable_offsets_byte_range(); |
| self.data.read_array(range).unwrap() |
| } |
| |
| /// A dynamically resolving wrapper for [`subtable_offsets`][Self::subtable_offsets]. |
| pub fn subtables(&self) -> ArrayOfOffsets<'a, T, Offset16> |
| where |
| T: FontRead<'a>, |
| { |
| let data = self.data; |
| let offsets = self.subtable_offsets(); |
| ArrayOfOffsets::new(offsets, data, ()) |
| } |
| |
| /// Index (base 0) into GDEF mark glyph sets structure. This field |
| /// is only present if the USE_MARK_FILTERING_SET lookup flag is |
| /// set. |
| pub fn mark_filtering_set(&self) -> Option<u16> { |
| let range = self.shape.mark_filtering_set_byte_range()?; |
| Some(self.data.read_at(range.start).unwrap()) |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a, T: FontRead<'a> + SomeTable<'a> + 'a> SomeTable<'a> for Lookup<'a, T> { |
| fn type_name(&self) -> &str { |
| "Lookup" |
| } |
| fn get_field(&self, idx: usize) -> Option<Field<'a>> { |
| let lookup_flag = self.lookup_flag(); |
| match idx { |
| 0usize => Some(Field::new("lookup_type", self.lookup_type())), |
| 1usize => Some(Field::new("lookup_flag", self.traverse_lookup_flag())), |
| 2usize => Some(Field::new("sub_table_count", self.sub_table_count())), |
| 3usize => Some({ |
| let data = self.data; |
| Field::new( |
| "subtable_offsets", |
| FieldType::array_of_offsets( |
| better_type_name::<T>(), |
| self.subtable_offsets(), |
| move |off| { |
| let target = off.get().resolve::<T>(data); |
| FieldType::offset(off.get(), target) |
| }, |
| ), |
| ) |
| }), |
| 4usize if lookup_flag.contains(LookupFlag::USE_MARK_FILTERING_SET) => Some(Field::new( |
| "mark_filtering_set", |
| self.mark_filtering_set().unwrap(), |
| )), |
| _ => None, |
| } |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a, T: FontRead<'a> + SomeTable<'a> + 'a> std::fmt::Debug for Lookup<'a, T> { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| (self as &dyn SomeTable<'a>).fmt(f) |
| } |
| } |
| |
| impl Format<u16> for CoverageFormat1Marker { |
| const FORMAT: u16 = 1; |
| } |
| |
| /// [Coverage Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#coverage-format-1) |
| #[derive(Debug, Clone, Copy)] |
| #[doc(hidden)] |
| pub struct CoverageFormat1Marker { |
| glyph_array_byte_len: usize, |
| } |
| |
| impl CoverageFormat1Marker { |
| pub fn coverage_format_byte_range(&self) -> Range<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn glyph_count_byte_range(&self) -> Range<usize> { |
| let start = self.coverage_format_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn glyph_array_byte_range(&self) -> Range<usize> { |
| let start = self.glyph_count_byte_range().end; |
| start..start + self.glyph_array_byte_len |
| } |
| } |
| |
| impl MinByteRange for CoverageFormat1Marker { |
| fn min_byte_range(&self) -> Range<usize> { |
| 0..self.glyph_array_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for CoverageFormat1<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| cursor.advance::<u16>(); |
| let glyph_count: u16 = cursor.read()?; |
| let glyph_array_byte_len = (glyph_count as usize) |
| .checked_mul(GlyphId16::RAW_BYTE_LEN) |
| .ok_or(ReadError::OutOfBounds)?; |
| cursor.advance_by(glyph_array_byte_len); |
| cursor.finish(CoverageFormat1Marker { |
| glyph_array_byte_len, |
| }) |
| } |
| } |
| |
| /// [Coverage Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#coverage-format-1) |
| pub type CoverageFormat1<'a> = TableRef<'a, CoverageFormat1Marker>; |
| |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> CoverageFormat1<'a> { |
| /// Format identifier — format = 1 |
| pub fn coverage_format(&self) -> u16 { |
| let range = self.shape.coverage_format_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Number of glyphs in the glyph array |
| pub fn glyph_count(&self) -> u16 { |
| let range = self.shape.glyph_count_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Array of glyph IDs — in numerical order |
| pub fn glyph_array(&self) -> &'a [BigEndian<GlyphId16>] { |
| let range = self.shape.glyph_array_byte_range(); |
| self.data.read_array(range).unwrap() |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> SomeTable<'a> for CoverageFormat1<'a> { |
| fn type_name(&self) -> &str { |
| "CoverageFormat1" |
| } |
| fn get_field(&self, idx: usize) -> Option<Field<'a>> { |
| match idx { |
| 0usize => Some(Field::new("coverage_format", self.coverage_format())), |
| 1usize => Some(Field::new("glyph_count", self.glyph_count())), |
| 2usize => Some(Field::new("glyph_array", self.glyph_array())), |
| _ => None, |
| } |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> std::fmt::Debug for CoverageFormat1<'a> { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| (self as &dyn SomeTable<'a>).fmt(f) |
| } |
| } |
| |
| impl Format<u16> for CoverageFormat2Marker { |
| const FORMAT: u16 = 2; |
| } |
| |
| /// [Coverage Format 2](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#coverage-format-2) |
| #[derive(Debug, Clone, Copy)] |
| #[doc(hidden)] |
| pub struct CoverageFormat2Marker { |
| range_records_byte_len: usize, |
| } |
| |
| impl CoverageFormat2Marker { |
| pub fn coverage_format_byte_range(&self) -> Range<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn range_count_byte_range(&self) -> Range<usize> { |
| let start = self.coverage_format_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn range_records_byte_range(&self) -> Range<usize> { |
| let start = self.range_count_byte_range().end; |
| start..start + self.range_records_byte_len |
| } |
| } |
| |
| impl MinByteRange for CoverageFormat2Marker { |
| fn min_byte_range(&self) -> Range<usize> { |
| 0..self.range_records_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for CoverageFormat2<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| cursor.advance::<u16>(); |
| let range_count: u16 = cursor.read()?; |
| let range_records_byte_len = (range_count as usize) |
| .checked_mul(RangeRecord::RAW_BYTE_LEN) |
| .ok_or(ReadError::OutOfBounds)?; |
| cursor.advance_by(range_records_byte_len); |
| cursor.finish(CoverageFormat2Marker { |
| range_records_byte_len, |
| }) |
| } |
| } |
| |
| /// [Coverage Format 2](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#coverage-format-2) |
| pub type CoverageFormat2<'a> = TableRef<'a, CoverageFormat2Marker>; |
| |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> CoverageFormat2<'a> { |
| /// Format identifier — format = 2 |
| pub fn coverage_format(&self) -> u16 { |
| let range = self.shape.coverage_format_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Number of RangeRecords |
| pub fn range_count(&self) -> u16 { |
| let range = self.shape.range_count_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Array of glyph ranges — ordered by startGlyphID. |
| pub fn range_records(&self) -> &'a [RangeRecord] { |
| let range = self.shape.range_records_byte_range(); |
| self.data.read_array(range).unwrap() |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> SomeTable<'a> for CoverageFormat2<'a> { |
| fn type_name(&self) -> &str { |
| "CoverageFormat2" |
| } |
| fn get_field(&self, idx: usize) -> Option<Field<'a>> { |
| match idx { |
| 0usize => Some(Field::new("coverage_format", self.coverage_format())), |
| 1usize => Some(Field::new("range_count", self.range_count())), |
| 2usize => Some(Field::new( |
| "range_records", |
| traversal::FieldType::array_of_records( |
| stringify!(RangeRecord), |
| self.range_records(), |
| self.offset_data(), |
| ), |
| )), |
| _ => None, |
| } |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> std::fmt::Debug for CoverageFormat2<'a> { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| (self as &dyn SomeTable<'a>).fmt(f) |
| } |
| } |
| |
| /// Used in [CoverageFormat2] |
| #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, bytemuck :: AnyBitPattern)] |
| #[repr(C)] |
| #[repr(packed)] |
| pub struct RangeRecord { |
| /// First glyph ID in the range |
| pub start_glyph_id: BigEndian<GlyphId16>, |
| /// Last glyph ID in the range |
| pub end_glyph_id: BigEndian<GlyphId16>, |
| /// Coverage Index of first glyph ID in range |
| pub start_coverage_index: BigEndian<u16>, |
| } |
| |
| impl RangeRecord { |
| /// First glyph ID in the range |
| pub fn start_glyph_id(&self) -> GlyphId16 { |
| self.start_glyph_id.get() |
| } |
| |
| /// Last glyph ID in the range |
| pub fn end_glyph_id(&self) -> GlyphId16 { |
| self.end_glyph_id.get() |
| } |
| |
| /// Coverage Index of first glyph ID in range |
| pub fn start_coverage_index(&self) -> u16 { |
| self.start_coverage_index.get() |
| } |
| } |
| |
| impl FixedSize for RangeRecord { |
| const RAW_BYTE_LEN: usize = |
| GlyphId16::RAW_BYTE_LEN + GlyphId16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN; |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> SomeRecord<'a> for RangeRecord { |
| fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> { |
| RecordResolver { |
| name: "RangeRecord", |
| get_field: Box::new(move |idx, _data| match idx { |
| 0usize => Some(Field::new("start_glyph_id", self.start_glyph_id())), |
| 1usize => Some(Field::new("end_glyph_id", self.end_glyph_id())), |
| 2usize => Some(Field::new( |
| "start_coverage_index", |
| self.start_coverage_index(), |
| )), |
| _ => None, |
| }), |
| data, |
| } |
| } |
| } |
| |
| /// [Coverage Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#coverage-table) |
| #[derive(Clone)] |
| pub enum CoverageTable<'a> { |
| Format1(CoverageFormat1<'a>), |
| Format2(CoverageFormat2<'a>), |
| } |
| |
| impl<'a> CoverageTable<'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 coverage_format(&self) -> u16 { |
| match self { |
| Self::Format1(item) => item.coverage_format(), |
| Self::Format2(item) => item.coverage_format(), |
| } |
| } |
| } |
| |
| impl<'a> FontRead<'a> for CoverageTable<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let format: u16 = data.read_at(0usize)?; |
| match format { |
| CoverageFormat1Marker::FORMAT => Ok(Self::Format1(FontRead::read(data)?)), |
| CoverageFormat2Marker::FORMAT => Ok(Self::Format2(FontRead::read(data)?)), |
| other => Err(ReadError::InvalidFormat(other.into())), |
| } |
| } |
| } |
| |
| impl MinByteRange for CoverageTable<'_> { |
| 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> CoverageTable<'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 CoverageTable<'_> { |
| 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 CoverageTable<'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 ClassDefFormat1Marker { |
| const FORMAT: u16 = 1; |
| } |
| |
| /// [Class Definition Table Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#class-definition-table-format-1) |
| #[derive(Debug, Clone, Copy)] |
| #[doc(hidden)] |
| pub struct ClassDefFormat1Marker { |
| class_value_array_byte_len: usize, |
| } |
| |
| impl ClassDefFormat1Marker { |
| pub fn class_format_byte_range(&self) -> Range<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn start_glyph_id_byte_range(&self) -> Range<usize> { |
| let start = self.class_format_byte_range().end; |
| start..start + GlyphId16::RAW_BYTE_LEN |
| } |
| |
| pub fn glyph_count_byte_range(&self) -> Range<usize> { |
| let start = self.start_glyph_id_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn class_value_array_byte_range(&self) -> Range<usize> { |
| let start = self.glyph_count_byte_range().end; |
| start..start + self.class_value_array_byte_len |
| } |
| } |
| |
| impl MinByteRange for ClassDefFormat1Marker { |
| fn min_byte_range(&self) -> Range<usize> { |
| 0..self.class_value_array_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for ClassDefFormat1<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| cursor.advance::<u16>(); |
| cursor.advance::<GlyphId16>(); |
| let glyph_count: u16 = cursor.read()?; |
| let class_value_array_byte_len = (glyph_count as usize) |
| .checked_mul(u16::RAW_BYTE_LEN) |
| .ok_or(ReadError::OutOfBounds)?; |
| cursor.advance_by(class_value_array_byte_len); |
| cursor.finish(ClassDefFormat1Marker { |
| class_value_array_byte_len, |
| }) |
| } |
| } |
| |
| /// [Class Definition Table Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#class-definition-table-format-1) |
| pub type ClassDefFormat1<'a> = TableRef<'a, ClassDefFormat1Marker>; |
| |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> ClassDefFormat1<'a> { |
| /// Format identifier — format = 1 |
| pub fn class_format(&self) -> u16 { |
| let range = self.shape.class_format_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// First glyph ID of the classValueArray |
| pub fn start_glyph_id(&self) -> GlyphId16 { |
| let range = self.shape.start_glyph_id_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Size of the classValueArray |
| pub fn glyph_count(&self) -> u16 { |
| let range = self.shape.glyph_count_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Array of Class Values — one per glyph ID |
| pub fn class_value_array(&self) -> &'a [BigEndian<u16>] { |
| let range = self.shape.class_value_array_byte_range(); |
| self.data.read_array(range).unwrap() |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> SomeTable<'a> for ClassDefFormat1<'a> { |
| fn type_name(&self) -> &str { |
| "ClassDefFormat1" |
| } |
| fn get_field(&self, idx: usize) -> Option<Field<'a>> { |
| match idx { |
| 0usize => Some(Field::new("class_format", self.class_format())), |
| 1usize => Some(Field::new("start_glyph_id", self.start_glyph_id())), |
| 2usize => Some(Field::new("glyph_count", self.glyph_count())), |
| 3usize => Some(Field::new("class_value_array", self.class_value_array())), |
| _ => None, |
| } |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> std::fmt::Debug for ClassDefFormat1<'a> { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| (self as &dyn SomeTable<'a>).fmt(f) |
| } |
| } |
| |
| impl Format<u16> for ClassDefFormat2Marker { |
| const FORMAT: u16 = 2; |
| } |
| |
| /// [Class Definition Table Format 2](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#class-definition-table-format-2) |
| #[derive(Debug, Clone, Copy)] |
| #[doc(hidden)] |
| pub struct ClassDefFormat2Marker { |
| class_range_records_byte_len: usize, |
| } |
| |
| impl ClassDefFormat2Marker { |
| pub fn class_format_byte_range(&self) -> Range<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn class_range_count_byte_range(&self) -> Range<usize> { |
| let start = self.class_format_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn class_range_records_byte_range(&self) -> Range<usize> { |
| let start = self.class_range_count_byte_range().end; |
| start..start + self.class_range_records_byte_len |
| } |
| } |
| |
| impl MinByteRange for ClassDefFormat2Marker { |
| fn min_byte_range(&self) -> Range<usize> { |
| 0..self.class_range_records_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for ClassDefFormat2<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| cursor.advance::<u16>(); |
| let class_range_count: u16 = cursor.read()?; |
| let class_range_records_byte_len = (class_range_count as usize) |
| .checked_mul(ClassRangeRecord::RAW_BYTE_LEN) |
| .ok_or(ReadError::OutOfBounds)?; |
| cursor.advance_by(class_range_records_byte_len); |
| cursor.finish(ClassDefFormat2Marker { |
| class_range_records_byte_len, |
| }) |
| } |
| } |
| |
| /// [Class Definition Table Format 2](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#class-definition-table-format-2) |
| pub type ClassDefFormat2<'a> = TableRef<'a, ClassDefFormat2Marker>; |
| |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> ClassDefFormat2<'a> { |
| /// Format identifier — format = 2 |
| pub fn class_format(&self) -> u16 { |
| let range = self.shape.class_format_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Number of ClassRangeRecords |
| pub fn class_range_count(&self) -> u16 { |
| let range = self.shape.class_range_count_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Array of ClassRangeRecords — ordered by startGlyphID |
| pub fn class_range_records(&self) -> &'a [ClassRangeRecord] { |
| let range = self.shape.class_range_records_byte_range(); |
| self.data.read_array(range).unwrap() |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> SomeTable<'a> for ClassDefFormat2<'a> { |
| fn type_name(&self) -> &str { |
| "ClassDefFormat2" |
| } |
| fn get_field(&self, idx: usize) -> Option<Field<'a>> { |
| match idx { |
| 0usize => Some(Field::new("class_format", self.class_format())), |
| 1usize => Some(Field::new("class_range_count", self.class_range_count())), |
| 2usize => Some(Field::new( |
| "class_range_records", |
| traversal::FieldType::array_of_records( |
| stringify!(ClassRangeRecord), |
| self.class_range_records(), |
| self.offset_data(), |
| ), |
| )), |
| _ => None, |
| } |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> std::fmt::Debug for ClassDefFormat2<'a> { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| (self as &dyn SomeTable<'a>).fmt(f) |
| } |
| } |
| |
| /// Used in [ClassDefFormat2] |
| #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, bytemuck :: AnyBitPattern)] |
| #[repr(C)] |
| #[repr(packed)] |
| pub struct ClassRangeRecord { |
| /// First glyph ID in the range |
| pub start_glyph_id: BigEndian<GlyphId16>, |
| /// Last glyph ID in the range |
| pub end_glyph_id: BigEndian<GlyphId16>, |
| /// Applied to all glyphs in the range |
| pub class: BigEndian<u16>, |
| } |
| |
| impl ClassRangeRecord { |
| /// First glyph ID in the range |
| pub fn start_glyph_id(&self) -> GlyphId16 { |
| self.start_glyph_id.get() |
| } |
| |
| /// Last glyph ID in the range |
| pub fn end_glyph_id(&self) -> GlyphId16 { |
| self.end_glyph_id.get() |
| } |
| |
| /// Applied to all glyphs in the range |
| pub fn class(&self) -> u16 { |
| self.class.get() |
| } |
| } |
| |
| impl FixedSize for ClassRangeRecord { |
| const RAW_BYTE_LEN: usize = |
| GlyphId16::RAW_BYTE_LEN + GlyphId16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN; |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> SomeRecord<'a> for ClassRangeRecord { |
| fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> { |
| RecordResolver { |
| name: "ClassRangeRecord", |
| get_field: Box::new(move |idx, _data| match idx { |
| 0usize => Some(Field::new("start_glyph_id", self.start_glyph_id())), |
| 1usize => Some(Field::new("end_glyph_id", self.end_glyph_id())), |
| 2usize => Some(Field::new("class", self.class())), |
| _ => None, |
| }), |
| data, |
| } |
| } |
| } |
| |
| /// A [Class Definition Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#class-definition-table) |
| #[derive(Clone)] |
| pub enum ClassDef<'a> { |
| Format1(ClassDefFormat1<'a>), |
| Format2(ClassDefFormat2<'a>), |
| } |
| |
| impl<'a> ClassDef<'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 class_format(&self) -> u16 { |
| match self { |
| Self::Format1(item) => item.class_format(), |
| Self::Format2(item) => item.class_format(), |
| } |
| } |
| } |
| |
| impl<'a> FontRead<'a> for ClassDef<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let format: u16 = data.read_at(0usize)?; |
| match format { |
| ClassDefFormat1Marker::FORMAT => Ok(Self::Format1(FontRead::read(data)?)), |
| ClassDefFormat2Marker::FORMAT => Ok(Self::Format2(FontRead::read(data)?)), |
| other => Err(ReadError::InvalidFormat(other.into())), |
| } |
| } |
| } |
| |
| impl MinByteRange for ClassDef<'_> { |
| 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> ClassDef<'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 ClassDef<'_> { |
| 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 ClassDef<'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) |
| } |
| } |
| |
| /// [Sequence Lookup Record](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#sequence-lookup-record) |
| #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, bytemuck :: AnyBitPattern)] |
| #[repr(C)] |
| #[repr(packed)] |
| pub struct SequenceLookupRecord { |
| /// Index (zero-based) into the input glyph sequence |
| pub sequence_index: BigEndian<u16>, |
| /// Index (zero-based) into the LookupList |
| pub lookup_list_index: BigEndian<u16>, |
| } |
| |
| impl SequenceLookupRecord { |
| /// Index (zero-based) into the input glyph sequence |
| pub fn sequence_index(&self) -> u16 { |
| self.sequence_index.get() |
| } |
| |
| /// Index (zero-based) into the LookupList |
| pub fn lookup_list_index(&self) -> u16 { |
| self.lookup_list_index.get() |
| } |
| } |
| |
| impl FixedSize for SequenceLookupRecord { |
| const RAW_BYTE_LEN: usize = u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN; |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> SomeRecord<'a> for SequenceLookupRecord { |
| fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> { |
| RecordResolver { |
| name: "SequenceLookupRecord", |
| get_field: Box::new(move |idx, _data| match idx { |
| 0usize => Some(Field::new("sequence_index", self.sequence_index())), |
| 1usize => Some(Field::new("lookup_list_index", self.lookup_list_index())), |
| _ => None, |
| }), |
| data, |
| } |
| } |
| } |
| |
| impl Format<u16> for SequenceContextFormat1Marker { |
| const FORMAT: u16 = 1; |
| } |
| |
| /// [Sequence Context Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#sequence-context-format-1-simple-glyph-contexts) |
| #[derive(Debug, Clone, Copy)] |
| #[doc(hidden)] |
| pub struct SequenceContextFormat1Marker { |
| seq_rule_set_offsets_byte_len: usize, |
| } |
| |
| impl SequenceContextFormat1Marker { |
| pub fn 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.format_byte_range().end; |
| start..start + Offset16::RAW_BYTE_LEN |
| } |
| |
| pub fn seq_rule_set_count_byte_range(&self) -> Range<usize> { |
| let start = self.coverage_offset_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn seq_rule_set_offsets_byte_range(&self) -> Range<usize> { |
| let start = self.seq_rule_set_count_byte_range().end; |
| start..start + self.seq_rule_set_offsets_byte_len |
| } |
| } |
| |
| impl MinByteRange for SequenceContextFormat1Marker { |
| fn min_byte_range(&self) -> Range<usize> { |
| 0..self.seq_rule_set_offsets_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for SequenceContextFormat1<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| cursor.advance::<u16>(); |
| cursor.advance::<Offset16>(); |
| let seq_rule_set_count: u16 = cursor.read()?; |
| let seq_rule_set_offsets_byte_len = (seq_rule_set_count as usize) |
| .checked_mul(Offset16::RAW_BYTE_LEN) |
| .ok_or(ReadError::OutOfBounds)?; |
| cursor.advance_by(seq_rule_set_offsets_byte_len); |
| cursor.finish(SequenceContextFormat1Marker { |
| seq_rule_set_offsets_byte_len, |
| }) |
| } |
| } |
| |
| /// [Sequence Context Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#sequence-context-format-1-simple-glyph-contexts) |
| pub type SequenceContextFormat1<'a> = TableRef<'a, SequenceContextFormat1Marker>; |
| |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> SequenceContextFormat1<'a> { |
| /// Format identifier: format = 1 |
| pub fn format(&self) -> u16 { |
| let range = self.shape.format_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Offset to Coverage table, from beginning of |
| /// SequenceContextFormat1 table |
| 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 SequenceRuleSet tables |
| pub fn seq_rule_set_count(&self) -> u16 { |
| let range = self.shape.seq_rule_set_count_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Array of offsets to SequenceRuleSet tables, from beginning of |
| /// SequenceContextFormat1 table (offsets may be NULL) |
| pub fn seq_rule_set_offsets(&self) -> &'a [BigEndian<Nullable<Offset16>>] { |
| let range = self.shape.seq_rule_set_offsets_byte_range(); |
| self.data.read_array(range).unwrap() |
| } |
| |
| /// A dynamically resolving wrapper for [`seq_rule_set_offsets`][Self::seq_rule_set_offsets]. |
| pub fn seq_rule_sets(&self) -> ArrayOfNullableOffsets<'a, SequenceRuleSet<'a>, Offset16> { |
| let data = self.data; |
| let offsets = self.seq_rule_set_offsets(); |
| ArrayOfNullableOffsets::new(offsets, data, ()) |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> SomeTable<'a> for SequenceContextFormat1<'a> { |
| fn type_name(&self) -> &str { |
| "SequenceContextFormat1" |
| } |
| fn get_field(&self, idx: usize) -> Option<Field<'a>> { |
| match idx { |
| 0usize => Some(Field::new("format", self.format())), |
| 1usize => Some(Field::new( |
| "coverage_offset", |
| FieldType::offset(self.coverage_offset(), self.coverage()), |
| )), |
| 2usize => Some(Field::new("seq_rule_set_count", self.seq_rule_set_count())), |
| 3usize => Some({ |
| let data = self.data; |
| Field::new( |
| "seq_rule_set_offsets", |
| FieldType::array_of_offsets( |
| better_type_name::<SequenceRuleSet>(), |
| self.seq_rule_set_offsets(), |
| move |off| { |
| let target = off.get().resolve::<SequenceRuleSet>(data); |
| FieldType::offset(off.get(), target) |
| }, |
| ), |
| ) |
| }), |
| _ => None, |
| } |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> std::fmt::Debug for SequenceContextFormat1<'a> { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| (self as &dyn SomeTable<'a>).fmt(f) |
| } |
| } |
| |
| /// Part of [SequenceContextFormat1] |
| #[derive(Debug, Clone, Copy)] |
| #[doc(hidden)] |
| pub struct SequenceRuleSetMarker { |
| seq_rule_offsets_byte_len: usize, |
| } |
| |
| impl SequenceRuleSetMarker { |
| pub fn seq_rule_count_byte_range(&self) -> Range<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn seq_rule_offsets_byte_range(&self) -> Range<usize> { |
| let start = self.seq_rule_count_byte_range().end; |
| start..start + self.seq_rule_offsets_byte_len |
| } |
| } |
| |
| impl MinByteRange for SequenceRuleSetMarker { |
| fn min_byte_range(&self) -> Range<usize> { |
| 0..self.seq_rule_offsets_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for SequenceRuleSet<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| let seq_rule_count: u16 = cursor.read()?; |
| let seq_rule_offsets_byte_len = (seq_rule_count as usize) |
| .checked_mul(Offset16::RAW_BYTE_LEN) |
| .ok_or(ReadError::OutOfBounds)?; |
| cursor.advance_by(seq_rule_offsets_byte_len); |
| cursor.finish(SequenceRuleSetMarker { |
| seq_rule_offsets_byte_len, |
| }) |
| } |
| } |
| |
| /// Part of [SequenceContextFormat1] |
| pub type SequenceRuleSet<'a> = TableRef<'a, SequenceRuleSetMarker>; |
| |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> SequenceRuleSet<'a> { |
| /// Number of SequenceRule tables |
| pub fn seq_rule_count(&self) -> u16 { |
| let range = self.shape.seq_rule_count_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Array of offsets to SequenceRule tables, from beginning of the |
| /// SequenceRuleSet table |
| pub fn seq_rule_offsets(&self) -> &'a [BigEndian<Offset16>] { |
| let range = self.shape.seq_rule_offsets_byte_range(); |
| self.data.read_array(range).unwrap() |
| } |
| |
| /// A dynamically resolving wrapper for [`seq_rule_offsets`][Self::seq_rule_offsets]. |
| pub fn seq_rules(&self) -> ArrayOfOffsets<'a, SequenceRule<'a>, Offset16> { |
| let data = self.data; |
| let offsets = self.seq_rule_offsets(); |
| ArrayOfOffsets::new(offsets, data, ()) |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> SomeTable<'a> for SequenceRuleSet<'a> { |
| fn type_name(&self) -> &str { |
| "SequenceRuleSet" |
| } |
| fn get_field(&self, idx: usize) -> Option<Field<'a>> { |
| match idx { |
| 0usize => Some(Field::new("seq_rule_count", self.seq_rule_count())), |
| 1usize => Some({ |
| let data = self.data; |
| Field::new( |
| "seq_rule_offsets", |
| FieldType::array_of_offsets( |
| better_type_name::<SequenceRule>(), |
| self.seq_rule_offsets(), |
| move |off| { |
| let target = off.get().resolve::<SequenceRule>(data); |
| FieldType::offset(off.get(), target) |
| }, |
| ), |
| ) |
| }), |
| _ => None, |
| } |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> std::fmt::Debug for SequenceRuleSet<'a> { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| (self as &dyn SomeTable<'a>).fmt(f) |
| } |
| } |
| |
| /// Part of [SequenceContextFormat1] |
| #[derive(Debug, Clone, Copy)] |
| #[doc(hidden)] |
| pub struct SequenceRuleMarker { |
| input_sequence_byte_len: usize, |
| seq_lookup_records_byte_len: usize, |
| } |
| |
| impl SequenceRuleMarker { |
| pub fn glyph_count_byte_range(&self) -> Range<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn seq_lookup_count_byte_range(&self) -> Range<usize> { |
| let start = self.glyph_count_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn input_sequence_byte_range(&self) -> Range<usize> { |
| let start = self.seq_lookup_count_byte_range().end; |
| start..start + self.input_sequence_byte_len |
| } |
| |
| pub fn seq_lookup_records_byte_range(&self) -> Range<usize> { |
| let start = self.input_sequence_byte_range().end; |
| start..start + self.seq_lookup_records_byte_len |
| } |
| } |
| |
| impl MinByteRange for SequenceRuleMarker { |
| fn min_byte_range(&self) -> Range<usize> { |
| 0..self.seq_lookup_records_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for SequenceRule<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| let glyph_count: u16 = cursor.read()?; |
| let seq_lookup_count: u16 = cursor.read()?; |
| let input_sequence_byte_len = (transforms::subtract(glyph_count, 1_usize)) |
| .checked_mul(GlyphId16::RAW_BYTE_LEN) |
| .ok_or(ReadError::OutOfBounds)?; |
| cursor.advance_by(input_sequence_byte_len); |
| let seq_lookup_records_byte_len = (seq_lookup_count as usize) |
| .checked_mul(SequenceLookupRecord::RAW_BYTE_LEN) |
| .ok_or(ReadError::OutOfBounds)?; |
| cursor.advance_by(seq_lookup_records_byte_len); |
| cursor.finish(SequenceRuleMarker { |
| input_sequence_byte_len, |
| seq_lookup_records_byte_len, |
| }) |
| } |
| } |
| |
| /// Part of [SequenceContextFormat1] |
| pub type SequenceRule<'a> = TableRef<'a, SequenceRuleMarker>; |
| |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> SequenceRule<'a> { |
| /// Number of glyphs in the input glyph sequence |
| pub fn glyph_count(&self) -> u16 { |
| let range = self.shape.glyph_count_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Number of SequenceLookupRecords |
| pub fn seq_lookup_count(&self) -> u16 { |
| let range = self.shape.seq_lookup_count_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Array of input glyph IDs—starting with the second glyph |
| pub fn input_sequence(&self) -> &'a [BigEndian<GlyphId16>] { |
| let range = self.shape.input_sequence_byte_range(); |
| self.data.read_array(range).unwrap() |
| } |
| |
| /// Array of Sequence lookup records |
| pub fn seq_lookup_records(&self) -> &'a [SequenceLookupRecord] { |
| let range = self.shape.seq_lookup_records_byte_range(); |
| self.data.read_array(range).unwrap() |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> SomeTable<'a> for SequenceRule<'a> { |
| fn type_name(&self) -> &str { |
| "SequenceRule" |
| } |
| fn get_field(&self, idx: usize) -> Option<Field<'a>> { |
| match idx { |
| 0usize => Some(Field::new("glyph_count", self.glyph_count())), |
| 1usize => Some(Field::new("seq_lookup_count", self.seq_lookup_count())), |
| 2usize => Some(Field::new("input_sequence", self.input_sequence())), |
| 3usize => Some(Field::new( |
| "seq_lookup_records", |
| traversal::FieldType::array_of_records( |
| stringify!(SequenceLookupRecord), |
| self.seq_lookup_records(), |
| self.offset_data(), |
| ), |
| )), |
| _ => None, |
| } |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> std::fmt::Debug for SequenceRule<'a> { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| (self as &dyn SomeTable<'a>).fmt(f) |
| } |
| } |
| |
| impl Format<u16> for SequenceContextFormat2Marker { |
| const FORMAT: u16 = 2; |
| } |
| |
| /// [Sequence Context Format 2](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#sequence-context-format-2-class-based-glyph-contexts) |
| #[derive(Debug, Clone, Copy)] |
| #[doc(hidden)] |
| pub struct SequenceContextFormat2Marker { |
| class_seq_rule_set_offsets_byte_len: usize, |
| } |
| |
| impl SequenceContextFormat2Marker { |
| pub fn 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.format_byte_range().end; |
| start..start + Offset16::RAW_BYTE_LEN |
| } |
| |
| pub fn class_def_offset_byte_range(&self) -> Range<usize> { |
| let start = self.coverage_offset_byte_range().end; |
| start..start + Offset16::RAW_BYTE_LEN |
| } |
| |
| pub fn class_seq_rule_set_count_byte_range(&self) -> Range<usize> { |
| let start = self.class_def_offset_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn class_seq_rule_set_offsets_byte_range(&self) -> Range<usize> { |
| let start = self.class_seq_rule_set_count_byte_range().end; |
| start..start + self.class_seq_rule_set_offsets_byte_len |
| } |
| } |
| |
| impl MinByteRange for SequenceContextFormat2Marker { |
| fn min_byte_range(&self) -> Range<usize> { |
| 0..self.class_seq_rule_set_offsets_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for SequenceContextFormat2<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| cursor.advance::<u16>(); |
| cursor.advance::<Offset16>(); |
| cursor.advance::<Offset16>(); |
| let class_seq_rule_set_count: u16 = cursor.read()?; |
| let class_seq_rule_set_offsets_byte_len = (class_seq_rule_set_count as usize) |
| .checked_mul(Offset16::RAW_BYTE_LEN) |
| .ok_or(ReadError::OutOfBounds)?; |
| cursor.advance_by(class_seq_rule_set_offsets_byte_len); |
| cursor.finish(SequenceContextFormat2Marker { |
| class_seq_rule_set_offsets_byte_len, |
| }) |
| } |
| } |
| |
| /// [Sequence Context Format 2](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#sequence-context-format-2-class-based-glyph-contexts) |
| pub type SequenceContextFormat2<'a> = TableRef<'a, SequenceContextFormat2Marker>; |
| |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> SequenceContextFormat2<'a> { |
| /// Format identifier: format = 2 |
| pub fn format(&self) -> u16 { |
| let range = self.shape.format_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Offset to Coverage table, from beginning of |
| /// SequenceContextFormat2 table |
| 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) |
| } |
| |
| /// Offset to ClassDef table, from beginning of |
| /// SequenceContextFormat2 table |
| pub fn class_def_offset(&self) -> Offset16 { |
| let range = self.shape.class_def_offset_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Attempt to resolve [`class_def_offset`][Self::class_def_offset]. |
| pub fn class_def(&self) -> Result<ClassDef<'a>, ReadError> { |
| let data = self.data; |
| self.class_def_offset().resolve(data) |
| } |
| |
| /// Number of ClassSequenceRuleSet tables |
| pub fn class_seq_rule_set_count(&self) -> u16 { |
| let range = self.shape.class_seq_rule_set_count_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Array of offsets to ClassSequenceRuleSet tables, from beginning |
| /// of SequenceContextFormat2 table (may be NULL) |
| pub fn class_seq_rule_set_offsets(&self) -> &'a [BigEndian<Nullable<Offset16>>] { |
| let range = self.shape.class_seq_rule_set_offsets_byte_range(); |
| self.data.read_array(range).unwrap() |
| } |
| |
| /// A dynamically resolving wrapper for [`class_seq_rule_set_offsets`][Self::class_seq_rule_set_offsets]. |
| pub fn class_seq_rule_sets( |
| &self, |
| ) -> ArrayOfNullableOffsets<'a, ClassSequenceRuleSet<'a>, Offset16> { |
| let data = self.data; |
| let offsets = self.class_seq_rule_set_offsets(); |
| ArrayOfNullableOffsets::new(offsets, data, ()) |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> SomeTable<'a> for SequenceContextFormat2<'a> { |
| fn type_name(&self) -> &str { |
| "SequenceContextFormat2" |
| } |
| fn get_field(&self, idx: usize) -> Option<Field<'a>> { |
| match idx { |
| 0usize => Some(Field::new("format", self.format())), |
| 1usize => Some(Field::new( |
| "coverage_offset", |
| FieldType::offset(self.coverage_offset(), self.coverage()), |
| )), |
| 2usize => Some(Field::new( |
| "class_def_offset", |
| FieldType::offset(self.class_def_offset(), self.class_def()), |
| )), |
| 3usize => Some(Field::new( |
| "class_seq_rule_set_count", |
| self.class_seq_rule_set_count(), |
| )), |
| 4usize => Some({ |
| let data = self.data; |
| Field::new( |
| "class_seq_rule_set_offsets", |
| FieldType::array_of_offsets( |
| better_type_name::<ClassSequenceRuleSet>(), |
| self.class_seq_rule_set_offsets(), |
| move |off| { |
| let target = off.get().resolve::<ClassSequenceRuleSet>(data); |
| FieldType::offset(off.get(), target) |
| }, |
| ), |
| ) |
| }), |
| _ => None, |
| } |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> std::fmt::Debug for SequenceContextFormat2<'a> { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| (self as &dyn SomeTable<'a>).fmt(f) |
| } |
| } |
| |
| /// Part of [SequenceContextFormat2] |
| #[derive(Debug, Clone, Copy)] |
| #[doc(hidden)] |
| pub struct ClassSequenceRuleSetMarker { |
| class_seq_rule_offsets_byte_len: usize, |
| } |
| |
| impl ClassSequenceRuleSetMarker { |
| pub fn class_seq_rule_count_byte_range(&self) -> Range<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn class_seq_rule_offsets_byte_range(&self) -> Range<usize> { |
| let start = self.class_seq_rule_count_byte_range().end; |
| start..start + self.class_seq_rule_offsets_byte_len |
| } |
| } |
| |
| impl MinByteRange for ClassSequenceRuleSetMarker { |
| fn min_byte_range(&self) -> Range<usize> { |
| 0..self.class_seq_rule_offsets_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for ClassSequenceRuleSet<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| let class_seq_rule_count: u16 = cursor.read()?; |
| let class_seq_rule_offsets_byte_len = (class_seq_rule_count as usize) |
| .checked_mul(Offset16::RAW_BYTE_LEN) |
| .ok_or(ReadError::OutOfBounds)?; |
| cursor.advance_by(class_seq_rule_offsets_byte_len); |
| cursor.finish(ClassSequenceRuleSetMarker { |
| class_seq_rule_offsets_byte_len, |
| }) |
| } |
| } |
| |
| /// Part of [SequenceContextFormat2] |
| pub type ClassSequenceRuleSet<'a> = TableRef<'a, ClassSequenceRuleSetMarker>; |
| |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> ClassSequenceRuleSet<'a> { |
| /// Number of ClassSequenceRule tables |
| pub fn class_seq_rule_count(&self) -> u16 { |
| let range = self.shape.class_seq_rule_count_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Array of offsets to ClassSequenceRule tables, from beginning of |
| /// ClassSequenceRuleSet table |
| pub fn class_seq_rule_offsets(&self) -> &'a [BigEndian<Offset16>] { |
| let range = self.shape.class_seq_rule_offsets_byte_range(); |
| self.data.read_array(range).unwrap() |
| } |
| |
| /// A dynamically resolving wrapper for [`class_seq_rule_offsets`][Self::class_seq_rule_offsets]. |
| pub fn class_seq_rules(&self) -> ArrayOfOffsets<'a, ClassSequenceRule<'a>, Offset16> { |
| let data = self.data; |
| let offsets = self.class_seq_rule_offsets(); |
| ArrayOfOffsets::new(offsets, data, ()) |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> SomeTable<'a> for ClassSequenceRuleSet<'a> { |
| fn type_name(&self) -> &str { |
| "ClassSequenceRuleSet" |
| } |
| fn get_field(&self, idx: usize) -> Option<Field<'a>> { |
| match idx { |
| 0usize => Some(Field::new( |
| "class_seq_rule_count", |
| self.class_seq_rule_count(), |
| )), |
| 1usize => Some({ |
| let data = self.data; |
| Field::new( |
| "class_seq_rule_offsets", |
| FieldType::array_of_offsets( |
| better_type_name::<ClassSequenceRule>(), |
| self.class_seq_rule_offsets(), |
| move |off| { |
| let target = off.get().resolve::<ClassSequenceRule>(data); |
| FieldType::offset(off.get(), target) |
| }, |
| ), |
| ) |
| }), |
| _ => None, |
| } |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> std::fmt::Debug for ClassSequenceRuleSet<'a> { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| (self as &dyn SomeTable<'a>).fmt(f) |
| } |
| } |
| |
| /// Part of [SequenceContextFormat2] |
| #[derive(Debug, Clone, Copy)] |
| #[doc(hidden)] |
| pub struct ClassSequenceRuleMarker { |
| input_sequence_byte_len: usize, |
| seq_lookup_records_byte_len: usize, |
| } |
| |
| impl ClassSequenceRuleMarker { |
| pub fn glyph_count_byte_range(&self) -> Range<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn seq_lookup_count_byte_range(&self) -> Range<usize> { |
| let start = self.glyph_count_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn input_sequence_byte_range(&self) -> Range<usize> { |
| let start = self.seq_lookup_count_byte_range().end; |
| start..start + self.input_sequence_byte_len |
| } |
| |
| pub fn seq_lookup_records_byte_range(&self) -> Range<usize> { |
| let start = self.input_sequence_byte_range().end; |
| start..start + self.seq_lookup_records_byte_len |
| } |
| } |
| |
| impl MinByteRange for ClassSequenceRuleMarker { |
| fn min_byte_range(&self) -> Range<usize> { |
| 0..self.seq_lookup_records_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for ClassSequenceRule<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| let glyph_count: u16 = cursor.read()?; |
| let seq_lookup_count: u16 = cursor.read()?; |
| let input_sequence_byte_len = (transforms::subtract(glyph_count, 1_usize)) |
| .checked_mul(u16::RAW_BYTE_LEN) |
| .ok_or(ReadError::OutOfBounds)?; |
| cursor.advance_by(input_sequence_byte_len); |
| let seq_lookup_records_byte_len = (seq_lookup_count as usize) |
| .checked_mul(SequenceLookupRecord::RAW_BYTE_LEN) |
| .ok_or(ReadError::OutOfBounds)?; |
| cursor.advance_by(seq_lookup_records_byte_len); |
| cursor.finish(ClassSequenceRuleMarker { |
| input_sequence_byte_len, |
| seq_lookup_records_byte_len, |
| }) |
| } |
| } |
| |
| /// Part of [SequenceContextFormat2] |
| pub type ClassSequenceRule<'a> = TableRef<'a, ClassSequenceRuleMarker>; |
| |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> ClassSequenceRule<'a> { |
| /// Number of glyphs to be matched |
| pub fn glyph_count(&self) -> u16 { |
| let range = self.shape.glyph_count_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Number of SequenceLookupRecords |
| pub fn seq_lookup_count(&self) -> u16 { |
| let range = self.shape.seq_lookup_count_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Sequence of classes to be matched to the input glyph sequence, |
| /// beginning with the second glyph position |
| pub fn input_sequence(&self) -> &'a [BigEndian<u16>] { |
| let range = self.shape.input_sequence_byte_range(); |
| self.data.read_array(range).unwrap() |
| } |
| |
| /// Array of SequenceLookupRecords |
| pub fn seq_lookup_records(&self) -> &'a [SequenceLookupRecord] { |
| let range = self.shape.seq_lookup_records_byte_range(); |
| self.data.read_array(range).unwrap() |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> SomeTable<'a> for ClassSequenceRule<'a> { |
| fn type_name(&self) -> &str { |
| "ClassSequenceRule" |
| } |
| fn get_field(&self, idx: usize) -> Option<Field<'a>> { |
| match idx { |
| 0usize => Some(Field::new("glyph_count", self.glyph_count())), |
| 1usize => Some(Field::new("seq_lookup_count", self.seq_lookup_count())), |
| 2usize => Some(Field::new("input_sequence", self.input_sequence())), |
| 3usize => Some(Field::new( |
| "seq_lookup_records", |
| traversal::FieldType::array_of_records( |
| stringify!(SequenceLookupRecord), |
| self.seq_lookup_records(), |
| self.offset_data(), |
| ), |
| )), |
| _ => None, |
| } |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> std::fmt::Debug for ClassSequenceRule<'a> { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| (self as &dyn SomeTable<'a>).fmt(f) |
| } |
| } |
| |
| impl Format<u16> for SequenceContextFormat3Marker { |
| const FORMAT: u16 = 3; |
| } |
| |
| /// [Sequence Context Format 3](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#sequence-context-format-3-coverage-based-glyph-contexts) |
| #[derive(Debug, Clone, Copy)] |
| #[doc(hidden)] |
| pub struct SequenceContextFormat3Marker { |
| coverage_offsets_byte_len: usize, |
| seq_lookup_records_byte_len: usize, |
| } |
| |
| impl SequenceContextFormat3Marker { |
| pub fn format_byte_range(&self) -> Range<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn glyph_count_byte_range(&self) -> Range<usize> { |
| let start = self.format_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn seq_lookup_count_byte_range(&self) -> Range<usize> { |
| let start = self.glyph_count_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn coverage_offsets_byte_range(&self) -> Range<usize> { |
| let start = self.seq_lookup_count_byte_range().end; |
| start..start + self.coverage_offsets_byte_len |
| } |
| |
| pub fn seq_lookup_records_byte_range(&self) -> Range<usize> { |
| let start = self.coverage_offsets_byte_range().end; |
| start..start + self.seq_lookup_records_byte_len |
| } |
| } |
| |
| impl MinByteRange for SequenceContextFormat3Marker { |
| fn min_byte_range(&self) -> Range<usize> { |
| 0..self.seq_lookup_records_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for SequenceContextFormat3<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| cursor.advance::<u16>(); |
| let glyph_count: u16 = cursor.read()?; |
| let seq_lookup_count: u16 = cursor.read()?; |
| let coverage_offsets_byte_len = (glyph_count as usize) |
| .checked_mul(Offset16::RAW_BYTE_LEN) |
| .ok_or(ReadError::OutOfBounds)?; |
| cursor.advance_by(coverage_offsets_byte_len); |
| let seq_lookup_records_byte_len = (seq_lookup_count as usize) |
| .checked_mul(SequenceLookupRecord::RAW_BYTE_LEN) |
| .ok_or(ReadError::OutOfBounds)?; |
| cursor.advance_by(seq_lookup_records_byte_len); |
| cursor.finish(SequenceContextFormat3Marker { |
| coverage_offsets_byte_len, |
| seq_lookup_records_byte_len, |
| }) |
| } |
| } |
| |
| /// [Sequence Context Format 3](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#sequence-context-format-3-coverage-based-glyph-contexts) |
| pub type SequenceContextFormat3<'a> = TableRef<'a, SequenceContextFormat3Marker>; |
| |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> SequenceContextFormat3<'a> { |
| /// Format identifier: format = 3 |
| pub fn format(&self) -> u16 { |
| let range = self.shape.format_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Number of glyphs in the input sequence |
| pub fn glyph_count(&self) -> u16 { |
| let range = self.shape.glyph_count_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Number of SequenceLookupRecords |
| pub fn seq_lookup_count(&self) -> u16 { |
| let range = self.shape.seq_lookup_count_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Array of offsets to Coverage tables, from beginning of |
| /// SequenceContextFormat3 subtable |
| pub fn coverage_offsets(&self) -> &'a [BigEndian<Offset16>] { |
| let range = self.shape.coverage_offsets_byte_range(); |
| self.data.read_array(range).unwrap() |
| } |
| |
| /// A dynamically resolving wrapper for [`coverage_offsets`][Self::coverage_offsets]. |
| pub fn coverages(&self) -> ArrayOfOffsets<'a, CoverageTable<'a>, Offset16> { |
| let data = self.data; |
| let offsets = self.coverage_offsets(); |
| ArrayOfOffsets::new(offsets, data, ()) |
| } |
| |
| /// Array of SequenceLookupRecords |
| pub fn seq_lookup_records(&self) -> &'a [SequenceLookupRecord] { |
| let range = self.shape.seq_lookup_records_byte_range(); |
| self.data.read_array(range).unwrap() |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> SomeTable<'a> for SequenceContextFormat3<'a> { |
| fn type_name(&self) -> &str { |
| "SequenceContextFormat3" |
| } |
| fn get_field(&self, idx: usize) -> Option<Field<'a>> { |
| match idx { |
| 0usize => Some(Field::new("format", self.format())), |
| 1usize => Some(Field::new("glyph_count", self.glyph_count())), |
| 2usize => Some(Field::new("seq_lookup_count", self.seq_lookup_count())), |
| 3usize => Some({ |
| let data = self.data; |
| Field::new( |
| "coverage_offsets", |
| FieldType::array_of_offsets( |
| better_type_name::<CoverageTable>(), |
| self.coverage_offsets(), |
| move |off| { |
| let target = off.get().resolve::<CoverageTable>(data); |
| FieldType::offset(off.get(), target) |
| }, |
| ), |
| ) |
| }), |
| 4usize => Some(Field::new( |
| "seq_lookup_records", |
| traversal::FieldType::array_of_records( |
| stringify!(SequenceLookupRecord), |
| self.seq_lookup_records(), |
| self.offset_data(), |
| ), |
| )), |
| _ => None, |
| } |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> std::fmt::Debug for SequenceContextFormat3<'a> { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| (self as &dyn SomeTable<'a>).fmt(f) |
| } |
| } |
| |
| #[derive(Clone)] |
| pub enum SequenceContext<'a> { |
| Format1(SequenceContextFormat1<'a>), |
| Format2(SequenceContextFormat2<'a>), |
| Format3(SequenceContextFormat3<'a>), |
| } |
| |
| impl<'a> SequenceContext<'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: format = 1 |
| pub fn format(&self) -> u16 { |
| match self { |
| Self::Format1(item) => item.format(), |
| Self::Format2(item) => item.format(), |
| Self::Format3(item) => item.format(), |
| } |
| } |
| } |
| |
| impl<'a> FontRead<'a> for SequenceContext<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let format: u16 = data.read_at(0usize)?; |
| match format { |
| SequenceContextFormat1Marker::FORMAT => Ok(Self::Format1(FontRead::read(data)?)), |
| SequenceContextFormat2Marker::FORMAT => Ok(Self::Format2(FontRead::read(data)?)), |
| SequenceContextFormat3Marker::FORMAT => Ok(Self::Format3(FontRead::read(data)?)), |
| other => Err(ReadError::InvalidFormat(other.into())), |
| } |
| } |
| } |
| |
| impl MinByteRange for SequenceContext<'_> { |
| 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> SequenceContext<'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 SequenceContext<'_> { |
| 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 SequenceContext<'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 ChainedSequenceContextFormat1Marker { |
| const FORMAT: u16 = 1; |
| } |
| |
| /// [Chained Sequence Context Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#chained-sequence-context-format-1-simple-glyph-contexts) |
| #[derive(Debug, Clone, Copy)] |
| #[doc(hidden)] |
| pub struct ChainedSequenceContextFormat1Marker { |
| chained_seq_rule_set_offsets_byte_len: usize, |
| } |
| |
| impl ChainedSequenceContextFormat1Marker { |
| pub fn 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.format_byte_range().end; |
| start..start + Offset16::RAW_BYTE_LEN |
| } |
| |
| pub fn chained_seq_rule_set_count_byte_range(&self) -> Range<usize> { |
| let start = self.coverage_offset_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn chained_seq_rule_set_offsets_byte_range(&self) -> Range<usize> { |
| let start = self.chained_seq_rule_set_count_byte_range().end; |
| start..start + self.chained_seq_rule_set_offsets_byte_len |
| } |
| } |
| |
| impl MinByteRange for ChainedSequenceContextFormat1Marker { |
| fn min_byte_range(&self) -> Range<usize> { |
| 0..self.chained_seq_rule_set_offsets_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for ChainedSequenceContextFormat1<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| cursor.advance::<u16>(); |
| cursor.advance::<Offset16>(); |
| let chained_seq_rule_set_count: u16 = cursor.read()?; |
| let chained_seq_rule_set_offsets_byte_len = (chained_seq_rule_set_count as usize) |
| .checked_mul(Offset16::RAW_BYTE_LEN) |
| .ok_or(ReadError::OutOfBounds)?; |
| cursor.advance_by(chained_seq_rule_set_offsets_byte_len); |
| cursor.finish(ChainedSequenceContextFormat1Marker { |
| chained_seq_rule_set_offsets_byte_len, |
| }) |
| } |
| } |
| |
| /// [Chained Sequence Context Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#chained-sequence-context-format-1-simple-glyph-contexts) |
| pub type ChainedSequenceContextFormat1<'a> = TableRef<'a, ChainedSequenceContextFormat1Marker>; |
| |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> ChainedSequenceContextFormat1<'a> { |
| /// Format identifier: format = 1 |
| pub fn format(&self) -> u16 { |
| let range = self.shape.format_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Offset to Coverage table, from beginning of |
| /// ChainSequenceContextFormat1 table |
| 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 ChainedSequenceRuleSet tables |
| pub fn chained_seq_rule_set_count(&self) -> u16 { |
| let range = self.shape.chained_seq_rule_set_count_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Array of offsets to ChainedSeqRuleSet tables, from beginning of |
| /// ChainedSequenceContextFormat1 table (may be NULL) |
| pub fn chained_seq_rule_set_offsets(&self) -> &'a [BigEndian<Nullable<Offset16>>] { |
| let range = self.shape.chained_seq_rule_set_offsets_byte_range(); |
| self.data.read_array(range).unwrap() |
| } |
| |
| /// A dynamically resolving wrapper for [`chained_seq_rule_set_offsets`][Self::chained_seq_rule_set_offsets]. |
| pub fn chained_seq_rule_sets( |
| &self, |
| ) -> ArrayOfNullableOffsets<'a, ChainedSequenceRuleSet<'a>, Offset16> { |
| let data = self.data; |
| let offsets = self.chained_seq_rule_set_offsets(); |
| ArrayOfNullableOffsets::new(offsets, data, ()) |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> SomeTable<'a> for ChainedSequenceContextFormat1<'a> { |
| fn type_name(&self) -> &str { |
| "ChainedSequenceContextFormat1" |
| } |
| fn get_field(&self, idx: usize) -> Option<Field<'a>> { |
| match idx { |
| 0usize => Some(Field::new("format", self.format())), |
| 1usize => Some(Field::new( |
| "coverage_offset", |
| FieldType::offset(self.coverage_offset(), self.coverage()), |
| )), |
| 2usize => Some(Field::new( |
| "chained_seq_rule_set_count", |
| self.chained_seq_rule_set_count(), |
| )), |
| 3usize => Some({ |
| let data = self.data; |
| Field::new( |
| "chained_seq_rule_set_offsets", |
| FieldType::array_of_offsets( |
| better_type_name::<ChainedSequenceRuleSet>(), |
| self.chained_seq_rule_set_offsets(), |
| move |off| { |
| let target = off.get().resolve::<ChainedSequenceRuleSet>(data); |
| FieldType::offset(off.get(), target) |
| }, |
| ), |
| ) |
| }), |
| _ => None, |
| } |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> std::fmt::Debug for ChainedSequenceContextFormat1<'a> { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| (self as &dyn SomeTable<'a>).fmt(f) |
| } |
| } |
| |
| /// Part of [ChainedSequenceContextFormat1] |
| #[derive(Debug, Clone, Copy)] |
| #[doc(hidden)] |
| pub struct ChainedSequenceRuleSetMarker { |
| chained_seq_rule_offsets_byte_len: usize, |
| } |
| |
| impl ChainedSequenceRuleSetMarker { |
| pub fn chained_seq_rule_count_byte_range(&self) -> Range<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn chained_seq_rule_offsets_byte_range(&self) -> Range<usize> { |
| let start = self.chained_seq_rule_count_byte_range().end; |
| start..start + self.chained_seq_rule_offsets_byte_len |
| } |
| } |
| |
| impl MinByteRange for ChainedSequenceRuleSetMarker { |
| fn min_byte_range(&self) -> Range<usize> { |
| 0..self.chained_seq_rule_offsets_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for ChainedSequenceRuleSet<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| let chained_seq_rule_count: u16 = cursor.read()?; |
| let chained_seq_rule_offsets_byte_len = (chained_seq_rule_count as usize) |
| .checked_mul(Offset16::RAW_BYTE_LEN) |
| .ok_or(ReadError::OutOfBounds)?; |
| cursor.advance_by(chained_seq_rule_offsets_byte_len); |
| cursor.finish(ChainedSequenceRuleSetMarker { |
| chained_seq_rule_offsets_byte_len, |
| }) |
| } |
| } |
| |
| /// Part of [ChainedSequenceContextFormat1] |
| pub type ChainedSequenceRuleSet<'a> = TableRef<'a, ChainedSequenceRuleSetMarker>; |
| |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> ChainedSequenceRuleSet<'a> { |
| /// Number of ChainedSequenceRule tables |
| pub fn chained_seq_rule_count(&self) -> u16 { |
| let range = self.shape.chained_seq_rule_count_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Array of offsets to ChainedSequenceRule tables, from beginning |
| /// of ChainedSequenceRuleSet table |
| pub fn chained_seq_rule_offsets(&self) -> &'a [BigEndian<Offset16>] { |
| let range = self.shape.chained_seq_rule_offsets_byte_range(); |
| self.data.read_array(range).unwrap() |
| } |
| |
| /// A dynamically resolving wrapper for [`chained_seq_rule_offsets`][Self::chained_seq_rule_offsets]. |
| pub fn chained_seq_rules(&self) -> ArrayOfOffsets<'a, ChainedSequenceRule<'a>, Offset16> { |
| let data = self.data; |
| let offsets = self.chained_seq_rule_offsets(); |
| ArrayOfOffsets::new(offsets, data, ()) |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> SomeTable<'a> for ChainedSequenceRuleSet<'a> { |
| fn type_name(&self) -> &str { |
| "ChainedSequenceRuleSet" |
| } |
| fn get_field(&self, idx: usize) -> Option<Field<'a>> { |
| match idx { |
| 0usize => Some(Field::new( |
| "chained_seq_rule_count", |
| self.chained_seq_rule_count(), |
| )), |
| 1usize => Some({ |
| let data = self.data; |
| Field::new( |
| "chained_seq_rule_offsets", |
| FieldType::array_of_offsets( |
| better_type_name::<ChainedSequenceRule>(), |
| self.chained_seq_rule_offsets(), |
| move |off| { |
| let target = off.get().resolve::<ChainedSequenceRule>(data); |
| FieldType::offset(off.get(), target) |
| }, |
| ), |
| ) |
| }), |
| _ => None, |
| } |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> std::fmt::Debug for ChainedSequenceRuleSet<'a> { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| (self as &dyn SomeTable<'a>).fmt(f) |
| } |
| } |
| |
| /// Part of [ChainedSequenceContextFormat1] |
| #[derive(Debug, Clone, Copy)] |
| #[doc(hidden)] |
| pub struct ChainedSequenceRuleMarker { |
| backtrack_sequence_byte_len: usize, |
| input_sequence_byte_len: usize, |
| lookahead_sequence_byte_len: usize, |
| seq_lookup_records_byte_len: usize, |
| } |
| |
| impl ChainedSequenceRuleMarker { |
| pub fn backtrack_glyph_count_byte_range(&self) -> Range<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn backtrack_sequence_byte_range(&self) -> Range<usize> { |
| let start = self.backtrack_glyph_count_byte_range().end; |
| start..start + self.backtrack_sequence_byte_len |
| } |
| |
| pub fn input_glyph_count_byte_range(&self) -> Range<usize> { |
| let start = self.backtrack_sequence_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn input_sequence_byte_range(&self) -> Range<usize> { |
| let start = self.input_glyph_count_byte_range().end; |
| start..start + self.input_sequence_byte_len |
| } |
| |
| pub fn lookahead_glyph_count_byte_range(&self) -> Range<usize> { |
| let start = self.input_sequence_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn lookahead_sequence_byte_range(&self) -> Range<usize> { |
| let start = self.lookahead_glyph_count_byte_range().end; |
| start..start + self.lookahead_sequence_byte_len |
| } |
| |
| pub fn seq_lookup_count_byte_range(&self) -> Range<usize> { |
| let start = self.lookahead_sequence_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn seq_lookup_records_byte_range(&self) -> Range<usize> { |
| let start = self.seq_lookup_count_byte_range().end; |
| start..start + self.seq_lookup_records_byte_len |
| } |
| } |
| |
| impl MinByteRange for ChainedSequenceRuleMarker { |
| fn min_byte_range(&self) -> Range<usize> { |
| 0..self.seq_lookup_records_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for ChainedSequenceRule<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| let backtrack_glyph_count: u16 = cursor.read()?; |
| let backtrack_sequence_byte_len = (backtrack_glyph_count as usize) |
| .checked_mul(GlyphId16::RAW_BYTE_LEN) |
| .ok_or(ReadError::OutOfBounds)?; |
| cursor.advance_by(backtrack_sequence_byte_len); |
| let input_glyph_count: u16 = cursor.read()?; |
| let input_sequence_byte_len = (transforms::subtract(input_glyph_count, 1_usize)) |
| .checked_mul(GlyphId16::RAW_BYTE_LEN) |
| .ok_or(ReadError::OutOfBounds)?; |
| cursor.advance_by(input_sequence_byte_len); |
| let lookahead_glyph_count: u16 = cursor.read()?; |
| let lookahead_sequence_byte_len = (lookahead_glyph_count as usize) |
| .checked_mul(GlyphId16::RAW_BYTE_LEN) |
| .ok_or(ReadError::OutOfBounds)?; |
| cursor.advance_by(lookahead_sequence_byte_len); |
| let seq_lookup_count: u16 = cursor.read()?; |
| let seq_lookup_records_byte_len = (seq_lookup_count as usize) |
| .checked_mul(SequenceLookupRecord::RAW_BYTE_LEN) |
| .ok_or(ReadError::OutOfBounds)?; |
| cursor.advance_by(seq_lookup_records_byte_len); |
| cursor.finish(ChainedSequenceRuleMarker { |
| backtrack_sequence_byte_len, |
| input_sequence_byte_len, |
| lookahead_sequence_byte_len, |
| seq_lookup_records_byte_len, |
| }) |
| } |
| } |
| |
| /// Part of [ChainedSequenceContextFormat1] |
| pub type ChainedSequenceRule<'a> = TableRef<'a, ChainedSequenceRuleMarker>; |
| |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> ChainedSequenceRule<'a> { |
| /// 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 backtrack glyph IDs |
| pub fn backtrack_sequence(&self) -> &'a [BigEndian<GlyphId16>] { |
| let range = self.shape.backtrack_sequence_byte_range(); |
| self.data.read_array(range).unwrap() |
| } |
| |
| /// Number of glyphs in the input sequence |
| pub fn input_glyph_count(&self) -> u16 { |
| let range = self.shape.input_glyph_count_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Array of input glyph IDs—start with second glyph |
| pub fn input_sequence(&self) -> &'a [BigEndian<GlyphId16>] { |
| let range = self.shape.input_sequence_byte_range(); |
| self.data.read_array(range).unwrap() |
| } |
| |
| /// Number of glyphs in the 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 lookahead glyph IDs |
| pub fn lookahead_sequence(&self) -> &'a [BigEndian<GlyphId16>] { |
| let range = self.shape.lookahead_sequence_byte_range(); |
| self.data.read_array(range).unwrap() |
| } |
| |
| /// Number of SequenceLookupRecords |
| pub fn seq_lookup_count(&self) -> u16 { |
| let range = self.shape.seq_lookup_count_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Array of SequenceLookupRecords |
| pub fn seq_lookup_records(&self) -> &'a [SequenceLookupRecord] { |
| let range = self.shape.seq_lookup_records_byte_range(); |
| self.data.read_array(range).unwrap() |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> SomeTable<'a> for ChainedSequenceRule<'a> { |
| fn type_name(&self) -> &str { |
| "ChainedSequenceRule" |
| } |
| fn get_field(&self, idx: usize) -> Option<Field<'a>> { |
| match idx { |
| 0usize => Some(Field::new( |
| "backtrack_glyph_count", |
| self.backtrack_glyph_count(), |
| )), |
| 1usize => Some(Field::new("backtrack_sequence", self.backtrack_sequence())), |
| 2usize => Some(Field::new("input_glyph_count", self.input_glyph_count())), |
| 3usize => Some(Field::new("input_sequence", self.input_sequence())), |
| 4usize => Some(Field::new( |
| "lookahead_glyph_count", |
| self.lookahead_glyph_count(), |
| )), |
| 5usize => Some(Field::new("lookahead_sequence", self.lookahead_sequence())), |
| 6usize => Some(Field::new("seq_lookup_count", self.seq_lookup_count())), |
| 7usize => Some(Field::new( |
| "seq_lookup_records", |
| traversal::FieldType::array_of_records( |
| stringify!(SequenceLookupRecord), |
| self.seq_lookup_records(), |
| self.offset_data(), |
| ), |
| )), |
| _ => None, |
| } |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> std::fmt::Debug for ChainedSequenceRule<'a> { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| (self as &dyn SomeTable<'a>).fmt(f) |
| } |
| } |
| |
| impl Format<u16> for ChainedSequenceContextFormat2Marker { |
| const FORMAT: u16 = 2; |
| } |
| |
| /// [Chained Sequence Context Format 2](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#chained-sequence-context-format-2-class-based-glyph-contexts) |
| #[derive(Debug, Clone, Copy)] |
| #[doc(hidden)] |
| pub struct ChainedSequenceContextFormat2Marker { |
| chained_class_seq_rule_set_offsets_byte_len: usize, |
| } |
| |
| impl ChainedSequenceContextFormat2Marker { |
| pub fn 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.format_byte_range().end; |
| start..start + Offset16::RAW_BYTE_LEN |
| } |
| |
| pub fn backtrack_class_def_offset_byte_range(&self) -> Range<usize> { |
| let start = self.coverage_offset_byte_range().end; |
| start..start + Offset16::RAW_BYTE_LEN |
| } |
| |
| pub fn input_class_def_offset_byte_range(&self) -> Range<usize> { |
| let start = self.backtrack_class_def_offset_byte_range().end; |
| start..start + Offset16::RAW_BYTE_LEN |
| } |
| |
| pub fn lookahead_class_def_offset_byte_range(&self) -> Range<usize> { |
| let start = self.input_class_def_offset_byte_range().end; |
| start..start + Offset16::RAW_BYTE_LEN |
| } |
| |
| pub fn chained_class_seq_rule_set_count_byte_range(&self) -> Range<usize> { |
| let start = self.lookahead_class_def_offset_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn chained_class_seq_rule_set_offsets_byte_range(&self) -> Range<usize> { |
| let start = self.chained_class_seq_rule_set_count_byte_range().end; |
| start..start + self.chained_class_seq_rule_set_offsets_byte_len |
| } |
| } |
| |
| impl MinByteRange for ChainedSequenceContextFormat2Marker { |
| fn min_byte_range(&self) -> Range<usize> { |
| 0..self.chained_class_seq_rule_set_offsets_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for ChainedSequenceContextFormat2<'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::<Offset16>(); |
| cursor.advance::<Offset16>(); |
| let chained_class_seq_rule_set_count: u16 = cursor.read()?; |
| let chained_class_seq_rule_set_offsets_byte_len = (chained_class_seq_rule_set_count |
| as usize) |
| .checked_mul(Offset16::RAW_BYTE_LEN) |
| .ok_or(ReadError::OutOfBounds)?; |
| cursor.advance_by(chained_class_seq_rule_set_offsets_byte_len); |
| cursor.finish(ChainedSequenceContextFormat2Marker { |
| chained_class_seq_rule_set_offsets_byte_len, |
| }) |
| } |
| } |
| |
| /// [Chained Sequence Context Format 2](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#chained-sequence-context-format-2-class-based-glyph-contexts) |
| pub type ChainedSequenceContextFormat2<'a> = TableRef<'a, ChainedSequenceContextFormat2Marker>; |
| |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> ChainedSequenceContextFormat2<'a> { |
| /// Format identifier: format = 2 |
| pub fn format(&self) -> u16 { |
| let range = self.shape.format_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Offset to Coverage table, from beginning of |
| /// ChainedSequenceContextFormat2 table |
| 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) |
| } |
| |
| /// Offset to ClassDef table containing backtrack sequence context, |
| /// from beginning of ChainedSequenceContextFormat2 table |
| pub fn backtrack_class_def_offset(&self) -> Offset16 { |
| let range = self.shape.backtrack_class_def_offset_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Attempt to resolve [`backtrack_class_def_offset`][Self::backtrack_class_def_offset]. |
| pub fn backtrack_class_def(&self) -> Result<ClassDef<'a>, ReadError> { |
| let data = self.data; |
| self.backtrack_class_def_offset().resolve(data) |
| } |
| |
| /// Offset to ClassDef table containing input sequence context, |
| /// from beginning of ChainedSequenceContextFormat2 table |
| pub fn input_class_def_offset(&self) -> Offset16 { |
| let range = self.shape.input_class_def_offset_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Attempt to resolve [`input_class_def_offset`][Self::input_class_def_offset]. |
| pub fn input_class_def(&self) -> Result<ClassDef<'a>, ReadError> { |
| let data = self.data; |
| self.input_class_def_offset().resolve(data) |
| } |
| |
| /// Offset to ClassDef table containing lookahead sequence context, |
| /// from beginning of ChainedSequenceContextFormat2 table |
| pub fn lookahead_class_def_offset(&self) -> Offset16 { |
| let range = self.shape.lookahead_class_def_offset_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Attempt to resolve [`lookahead_class_def_offset`][Self::lookahead_class_def_offset]. |
| pub fn lookahead_class_def(&self) -> Result<ClassDef<'a>, ReadError> { |
| let data = self.data; |
| self.lookahead_class_def_offset().resolve(data) |
| } |
| |
| /// Number of ChainedClassSequenceRuleSet tables |
| pub fn chained_class_seq_rule_set_count(&self) -> u16 { |
| let range = self.shape.chained_class_seq_rule_set_count_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Array of offsets to ChainedClassSequenceRuleSet tables, from |
| /// beginning of ChainedSequenceContextFormat2 table (may be NULL) |
| pub fn chained_class_seq_rule_set_offsets(&self) -> &'a [BigEndian<Nullable<Offset16>>] { |
| let range = self.shape.chained_class_seq_rule_set_offsets_byte_range(); |
| self.data.read_array(range).unwrap() |
| } |
| |
| /// A dynamically resolving wrapper for [`chained_class_seq_rule_set_offsets`][Self::chained_class_seq_rule_set_offsets]. |
| pub fn chained_class_seq_rule_sets( |
| &self, |
| ) -> ArrayOfNullableOffsets<'a, ChainedClassSequenceRuleSet<'a>, Offset16> { |
| let data = self.data; |
| let offsets = self.chained_class_seq_rule_set_offsets(); |
| ArrayOfNullableOffsets::new(offsets, data, ()) |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> SomeTable<'a> for ChainedSequenceContextFormat2<'a> { |
| fn type_name(&self) -> &str { |
| "ChainedSequenceContextFormat2" |
| } |
| fn get_field(&self, idx: usize) -> Option<Field<'a>> { |
| match idx { |
| 0usize => Some(Field::new("format", self.format())), |
| 1usize => Some(Field::new( |
| "coverage_offset", |
| FieldType::offset(self.coverage_offset(), self.coverage()), |
| )), |
| 2usize => Some(Field::new( |
| "backtrack_class_def_offset", |
| FieldType::offset( |
| self.backtrack_class_def_offset(), |
| self.backtrack_class_def(), |
| ), |
| )), |
| 3usize => Some(Field::new( |
| "input_class_def_offset", |
| FieldType::offset(self.input_class_def_offset(), self.input_class_def()), |
| )), |
| 4usize => Some(Field::new( |
| "lookahead_class_def_offset", |
| FieldType::offset( |
| self.lookahead_class_def_offset(), |
| self.lookahead_class_def(), |
| ), |
| )), |
| 5usize => Some(Field::new( |
| "chained_class_seq_rule_set_count", |
| self.chained_class_seq_rule_set_count(), |
| )), |
| 6usize => Some({ |
| let data = self.data; |
| Field::new( |
| "chained_class_seq_rule_set_offsets", |
| FieldType::array_of_offsets( |
| better_type_name::<ChainedClassSequenceRuleSet>(), |
| self.chained_class_seq_rule_set_offsets(), |
| move |off| { |
| let target = off.get().resolve::<ChainedClassSequenceRuleSet>(data); |
| FieldType::offset(off.get(), target) |
| }, |
| ), |
| ) |
| }), |
| _ => None, |
| } |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> std::fmt::Debug for ChainedSequenceContextFormat2<'a> { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| (self as &dyn SomeTable<'a>).fmt(f) |
| } |
| } |
| |
| /// Part of [ChainedSequenceContextFormat2] |
| #[derive(Debug, Clone, Copy)] |
| #[doc(hidden)] |
| pub struct ChainedClassSequenceRuleSetMarker { |
| chained_class_seq_rule_offsets_byte_len: usize, |
| } |
| |
| impl ChainedClassSequenceRuleSetMarker { |
| pub fn chained_class_seq_rule_count_byte_range(&self) -> Range<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn chained_class_seq_rule_offsets_byte_range(&self) -> Range<usize> { |
| let start = self.chained_class_seq_rule_count_byte_range().end; |
| start..start + self.chained_class_seq_rule_offsets_byte_len |
| } |
| } |
| |
| impl MinByteRange for ChainedClassSequenceRuleSetMarker { |
| fn min_byte_range(&self) -> Range<usize> { |
| 0..self.chained_class_seq_rule_offsets_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for ChainedClassSequenceRuleSet<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| let chained_class_seq_rule_count: u16 = cursor.read()?; |
| let chained_class_seq_rule_offsets_byte_len = (chained_class_seq_rule_count as usize) |
| .checked_mul(Offset16::RAW_BYTE_LEN) |
| .ok_or(ReadError::OutOfBounds)?; |
| cursor.advance_by(chained_class_seq_rule_offsets_byte_len); |
| cursor.finish(ChainedClassSequenceRuleSetMarker { |
| chained_class_seq_rule_offsets_byte_len, |
| }) |
| } |
| } |
| |
| /// Part of [ChainedSequenceContextFormat2] |
| pub type ChainedClassSequenceRuleSet<'a> = TableRef<'a, ChainedClassSequenceRuleSetMarker>; |
| |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> ChainedClassSequenceRuleSet<'a> { |
| /// Number of ChainedClassSequenceRule tables |
| pub fn chained_class_seq_rule_count(&self) -> u16 { |
| let range = self.shape.chained_class_seq_rule_count_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Array of offsets to ChainedClassSequenceRule tables, from |
| /// beginning of ChainedClassSequenceRuleSet |
| pub fn chained_class_seq_rule_offsets(&self) -> &'a [BigEndian<Offset16>] { |
| let range = self.shape.chained_class_seq_rule_offsets_byte_range(); |
| self.data.read_array(range).unwrap() |
| } |
| |
| /// A dynamically resolving wrapper for [`chained_class_seq_rule_offsets`][Self::chained_class_seq_rule_offsets]. |
| pub fn chained_class_seq_rules( |
| &self, |
| ) -> ArrayOfOffsets<'a, ChainedClassSequenceRule<'a>, Offset16> { |
| let data = self.data; |
| let offsets = self.chained_class_seq_rule_offsets(); |
| ArrayOfOffsets::new(offsets, data, ()) |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> SomeTable<'a> for ChainedClassSequenceRuleSet<'a> { |
| fn type_name(&self) -> &str { |
| "ChainedClassSequenceRuleSet" |
| } |
| fn get_field(&self, idx: usize) -> Option<Field<'a>> { |
| match idx { |
| 0usize => Some(Field::new( |
| "chained_class_seq_rule_count", |
| self.chained_class_seq_rule_count(), |
| )), |
| 1usize => Some({ |
| let data = self.data; |
| Field::new( |
| "chained_class_seq_rule_offsets", |
| FieldType::array_of_offsets( |
| better_type_name::<ChainedClassSequenceRule>(), |
| self.chained_class_seq_rule_offsets(), |
| move |off| { |
| let target = off.get().resolve::<ChainedClassSequenceRule>(data); |
| FieldType::offset(off.get(), target) |
| }, |
| ), |
| ) |
| }), |
| _ => None, |
| } |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> std::fmt::Debug for ChainedClassSequenceRuleSet<'a> { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| (self as &dyn SomeTable<'a>).fmt(f) |
| } |
| } |
| |
| /// Part of [ChainedSequenceContextFormat2] |
| #[derive(Debug, Clone, Copy)] |
| #[doc(hidden)] |
| pub struct ChainedClassSequenceRuleMarker { |
| backtrack_sequence_byte_len: usize, |
| input_sequence_byte_len: usize, |
| lookahead_sequence_byte_len: usize, |
| seq_lookup_records_byte_len: usize, |
| } |
| |
| impl ChainedClassSequenceRuleMarker { |
| pub fn backtrack_glyph_count_byte_range(&self) -> Range<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn backtrack_sequence_byte_range(&self) -> Range<usize> { |
| let start = self.backtrack_glyph_count_byte_range().end; |
| start..start + self.backtrack_sequence_byte_len |
| } |
| |
| pub fn input_glyph_count_byte_range(&self) -> Range<usize> { |
| let start = self.backtrack_sequence_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn input_sequence_byte_range(&self) -> Range<usize> { |
| let start = self.input_glyph_count_byte_range().end; |
| start..start + self.input_sequence_byte_len |
| } |
| |
| pub fn lookahead_glyph_count_byte_range(&self) -> Range<usize> { |
| let start = self.input_sequence_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn lookahead_sequence_byte_range(&self) -> Range<usize> { |
| let start = self.lookahead_glyph_count_byte_range().end; |
| start..start + self.lookahead_sequence_byte_len |
| } |
| |
| pub fn seq_lookup_count_byte_range(&self) -> Range<usize> { |
| let start = self.lookahead_sequence_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn seq_lookup_records_byte_range(&self) -> Range<usize> { |
| let start = self.seq_lookup_count_byte_range().end; |
| start..start + self.seq_lookup_records_byte_len |
| } |
| } |
| |
| impl MinByteRange for ChainedClassSequenceRuleMarker { |
| fn min_byte_range(&self) -> Range<usize> { |
| 0..self.seq_lookup_records_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for ChainedClassSequenceRule<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| let backtrack_glyph_count: u16 = cursor.read()?; |
| let backtrack_sequence_byte_len = (backtrack_glyph_count as usize) |
| .checked_mul(u16::RAW_BYTE_LEN) |
| .ok_or(ReadError::OutOfBounds)?; |
| cursor.advance_by(backtrack_sequence_byte_len); |
| let input_glyph_count: u16 = cursor.read()?; |
| let input_sequence_byte_len = (transforms::subtract(input_glyph_count, 1_usize)) |
| .checked_mul(u16::RAW_BYTE_LEN) |
| .ok_or(ReadError::OutOfBounds)?; |
| cursor.advance_by(input_sequence_byte_len); |
| let lookahead_glyph_count: u16 = cursor.read()?; |
| let lookahead_sequence_byte_len = (lookahead_glyph_count as usize) |
| .checked_mul(u16::RAW_BYTE_LEN) |
| .ok_or(ReadError::OutOfBounds)?; |
| cursor.advance_by(lookahead_sequence_byte_len); |
| let seq_lookup_count: u16 = cursor.read()?; |
| let seq_lookup_records_byte_len = (seq_lookup_count as usize) |
| .checked_mul(SequenceLookupRecord::RAW_BYTE_LEN) |
| .ok_or(ReadError::OutOfBounds)?; |
| cursor.advance_by(seq_lookup_records_byte_len); |
| cursor.finish(ChainedClassSequenceRuleMarker { |
| backtrack_sequence_byte_len, |
| input_sequence_byte_len, |
| lookahead_sequence_byte_len, |
| seq_lookup_records_byte_len, |
| }) |
| } |
| } |
| |
| /// Part of [ChainedSequenceContextFormat2] |
| pub type ChainedClassSequenceRule<'a> = TableRef<'a, ChainedClassSequenceRuleMarker>; |
| |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> ChainedClassSequenceRule<'a> { |
| /// 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 backtrack-sequence classes |
| pub fn backtrack_sequence(&self) -> &'a [BigEndian<u16>] { |
| let range = self.shape.backtrack_sequence_byte_range(); |
| self.data.read_array(range).unwrap() |
| } |
| |
| /// Total number of glyphs in the input sequence |
| pub fn input_glyph_count(&self) -> u16 { |
| let range = self.shape.input_glyph_count_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Array of input sequence classes, beginning with the second |
| /// glyph position |
| pub fn input_sequence(&self) -> &'a [BigEndian<u16>] { |
| let range = self.shape.input_sequence_byte_range(); |
| self.data.read_array(range).unwrap() |
| } |
| |
| /// Number of glyphs in the 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 lookahead-sequence classes |
| pub fn lookahead_sequence(&self) -> &'a [BigEndian<u16>] { |
| let range = self.shape.lookahead_sequence_byte_range(); |
| self.data.read_array(range).unwrap() |
| } |
| |
| /// Number of SequenceLookupRecords |
| pub fn seq_lookup_count(&self) -> u16 { |
| let range = self.shape.seq_lookup_count_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Array of SequenceLookupRecords |
| pub fn seq_lookup_records(&self) -> &'a [SequenceLookupRecord] { |
| let range = self.shape.seq_lookup_records_byte_range(); |
| self.data.read_array(range).unwrap() |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> SomeTable<'a> for ChainedClassSequenceRule<'a> { |
| fn type_name(&self) -> &str { |
| "ChainedClassSequenceRule" |
| } |
| fn get_field(&self, idx: usize) -> Option<Field<'a>> { |
| match idx { |
| 0usize => Some(Field::new( |
| "backtrack_glyph_count", |
| self.backtrack_glyph_count(), |
| )), |
| 1usize => Some(Field::new("backtrack_sequence", self.backtrack_sequence())), |
| 2usize => Some(Field::new("input_glyph_count", self.input_glyph_count())), |
| 3usize => Some(Field::new("input_sequence", self.input_sequence())), |
| 4usize => Some(Field::new( |
| "lookahead_glyph_count", |
| self.lookahead_glyph_count(), |
| )), |
| 5usize => Some(Field::new("lookahead_sequence", self.lookahead_sequence())), |
| 6usize => Some(Field::new("seq_lookup_count", self.seq_lookup_count())), |
| 7usize => Some(Field::new( |
| "seq_lookup_records", |
| traversal::FieldType::array_of_records( |
| stringify!(SequenceLookupRecord), |
| self.seq_lookup_records(), |
| self.offset_data(), |
| ), |
| )), |
| _ => None, |
| } |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> std::fmt::Debug for ChainedClassSequenceRule<'a> { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| (self as &dyn SomeTable<'a>).fmt(f) |
| } |
| } |
| |
| impl Format<u16> for ChainedSequenceContextFormat3Marker { |
| const FORMAT: u16 = 3; |
| } |
| |
| /// [Chained Sequence Context Format 3](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#chained-sequence-context-format-3-coverage-based-glyph-contexts) |
| #[derive(Debug, Clone, Copy)] |
| #[doc(hidden)] |
| pub struct ChainedSequenceContextFormat3Marker { |
| backtrack_coverage_offsets_byte_len: usize, |
| input_coverage_offsets_byte_len: usize, |
| lookahead_coverage_offsets_byte_len: usize, |
| seq_lookup_records_byte_len: usize, |
| } |
| |
| impl ChainedSequenceContextFormat3Marker { |
| pub fn format_byte_range(&self) -> Range<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn backtrack_glyph_count_byte_range(&self) -> Range<usize> { |
| let start = self.format_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn backtrack_coverage_offsets_byte_range(&self) -> Range<usize> { |
| let start = self.backtrack_glyph_count_byte_range().end; |
| start..start + self.backtrack_coverage_offsets_byte_len |
| } |
| |
| pub fn input_glyph_count_byte_range(&self) -> Range<usize> { |
| let start = self.backtrack_coverage_offsets_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn input_coverage_offsets_byte_range(&self) -> Range<usize> { |
| let start = self.input_glyph_count_byte_range().end; |
| start..start + self.input_coverage_offsets_byte_len |
| } |
| |
| pub fn lookahead_glyph_count_byte_range(&self) -> Range<usize> { |
| let start = self.input_coverage_offsets_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn lookahead_coverage_offsets_byte_range(&self) -> Range<usize> { |
| let start = self.lookahead_glyph_count_byte_range().end; |
| start..start + self.lookahead_coverage_offsets_byte_len |
| } |
| |
| pub fn seq_lookup_count_byte_range(&self) -> Range<usize> { |
| let start = self.lookahead_coverage_offsets_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn seq_lookup_records_byte_range(&self) -> Range<usize> { |
| let start = self.seq_lookup_count_byte_range().end; |
| start..start + self.seq_lookup_records_byte_len |
| } |
| } |
| |
| impl MinByteRange for ChainedSequenceContextFormat3Marker { |
| fn min_byte_range(&self) -> Range<usize> { |
| 0..self.seq_lookup_records_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for ChainedSequenceContextFormat3<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| cursor.advance::<u16>(); |
| 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 input_glyph_count: u16 = cursor.read()?; |
| let input_coverage_offsets_byte_len = (input_glyph_count as usize) |
| .checked_mul(Offset16::RAW_BYTE_LEN) |
| .ok_or(ReadError::OutOfBounds)?; |
| cursor.advance_by(input_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 seq_lookup_count: u16 = cursor.read()?; |
| let seq_lookup_records_byte_len = (seq_lookup_count as usize) |
| .checked_mul(SequenceLookupRecord::RAW_BYTE_LEN) |
| .ok_or(ReadError::OutOfBounds)?; |
| cursor.advance_by(seq_lookup_records_byte_len); |
| cursor.finish(ChainedSequenceContextFormat3Marker { |
| backtrack_coverage_offsets_byte_len, |
| input_coverage_offsets_byte_len, |
| lookahead_coverage_offsets_byte_len, |
| seq_lookup_records_byte_len, |
| }) |
| } |
| } |
| |
| /// [Chained Sequence Context Format 3](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#chained-sequence-context-format-3-coverage-based-glyph-contexts) |
| pub type ChainedSequenceContextFormat3<'a> = TableRef<'a, ChainedSequenceContextFormat3Marker>; |
| |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> ChainedSequenceContextFormat3<'a> { |
| /// Format identifier: format = 3 |
| pub fn format(&self) -> u16 { |
| let range = self.shape.format_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// 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 for the backtrack sequence |
| pub fn backtrack_coverage_offsets(&self) -> &'a [BigEndian<Offset16>] { |
| 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 the input sequence |
| pub fn input_glyph_count(&self) -> u16 { |
| let range = self.shape.input_glyph_count_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Array of offsets to coverage tables for the input sequence |
| pub fn input_coverage_offsets(&self) -> &'a [BigEndian<Offset16>] { |
| let range = self.shape.input_coverage_offsets_byte_range(); |
| self.data.read_array(range).unwrap() |
| } |
| |
| /// A dynamically resolving wrapper for [`input_coverage_offsets`][Self::input_coverage_offsets]. |
| pub fn input_coverages(&self) -> ArrayOfOffsets<'a, CoverageTable<'a>, Offset16> { |
| let data = self.data; |
| let offsets = self.input_coverage_offsets(); |
| ArrayOfOffsets::new(offsets, data, ()) |
| } |
| |
| /// Number of glyphs in the 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 for the lookahead sequence |
| pub fn lookahead_coverage_offsets(&self) -> &'a [BigEndian<Offset16>] { |
| 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 SequenceLookupRecords |
| pub fn seq_lookup_count(&self) -> u16 { |
| let range = self.shape.seq_lookup_count_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Array of SequenceLookupRecords |
| pub fn seq_lookup_records(&self) -> &'a [SequenceLookupRecord] { |
| let range = self.shape.seq_lookup_records_byte_range(); |
| self.data.read_array(range).unwrap() |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> SomeTable<'a> for ChainedSequenceContextFormat3<'a> { |
| fn type_name(&self) -> &str { |
| "ChainedSequenceContextFormat3" |
| } |
| fn get_field(&self, idx: usize) -> Option<Field<'a>> { |
| match idx { |
| 0usize => Some(Field::new("format", self.format())), |
| 1usize => Some(Field::new( |
| "backtrack_glyph_count", |
| self.backtrack_glyph_count(), |
| )), |
| 2usize => Some({ |
| let data = self.data; |
| Field::new( |
| "backtrack_coverage_offsets", |
| FieldType::array_of_offsets( |
| better_type_name::<CoverageTable>(), |
| self.backtrack_coverage_offsets(), |
| move |off| { |
| let target = off.get().resolve::<CoverageTable>(data); |
| FieldType::offset(off.get(), target) |
| }, |
| ), |
| ) |
| }), |
| 3usize => Some(Field::new("input_glyph_count", self.input_glyph_count())), |
| 4usize => Some({ |
| let data = self.data; |
| Field::new( |
| "input_coverage_offsets", |
| FieldType::array_of_offsets( |
| better_type_name::<CoverageTable>(), |
| self.input_coverage_offsets(), |
| move |off| { |
| let target = off.get().resolve::<CoverageTable>(data); |
| FieldType::offset(off.get(), target) |
| }, |
| ), |
| ) |
| }), |
| 5usize => Some(Field::new( |
| "lookahead_glyph_count", |
| self.lookahead_glyph_count(), |
| )), |
| 6usize => Some({ |
| let data = self.data; |
| Field::new( |
| "lookahead_coverage_offsets", |
| FieldType::array_of_offsets( |
| better_type_name::<CoverageTable>(), |
| self.lookahead_coverage_offsets(), |
| move |off| { |
| let target = off.get().resolve::<CoverageTable>(data); |
| FieldType::offset(off.get(), target) |
| }, |
| ), |
| ) |
| }), |
| 7usize => Some(Field::new("seq_lookup_count", self.seq_lookup_count())), |
| 8usize => Some(Field::new( |
| "seq_lookup_records", |
| traversal::FieldType::array_of_records( |
| stringify!(SequenceLookupRecord), |
| self.seq_lookup_records(), |
| self.offset_data(), |
| ), |
| )), |
| _ => None, |
| } |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> std::fmt::Debug for ChainedSequenceContextFormat3<'a> { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| (self as &dyn SomeTable<'a>).fmt(f) |
| } |
| } |
| |
| #[derive(Clone)] |
| pub enum ChainedSequenceContext<'a> { |
| Format1(ChainedSequenceContextFormat1<'a>), |
| Format2(ChainedSequenceContextFormat2<'a>), |
| Format3(ChainedSequenceContextFormat3<'a>), |
| } |
| |
| impl<'a> ChainedSequenceContext<'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: format = 1 |
| pub fn format(&self) -> u16 { |
| match self { |
| Self::Format1(item) => item.format(), |
| Self::Format2(item) => item.format(), |
| Self::Format3(item) => item.format(), |
| } |
| } |
| } |
| |
| impl<'a> FontRead<'a> for ChainedSequenceContext<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let format: u16 = data.read_at(0usize)?; |
| match format { |
| ChainedSequenceContextFormat1Marker::FORMAT => Ok(Self::Format1(FontRead::read(data)?)), |
| ChainedSequenceContextFormat2Marker::FORMAT => Ok(Self::Format2(FontRead::read(data)?)), |
| ChainedSequenceContextFormat3Marker::FORMAT => Ok(Self::Format3(FontRead::read(data)?)), |
| other => Err(ReadError::InvalidFormat(other.into())), |
| } |
| } |
| } |
| |
| impl MinByteRange for ChainedSequenceContext<'_> { |
| 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> ChainedSequenceContext<'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 ChainedSequenceContext<'_> { |
| 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 ChainedSequenceContext<'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) |
| } |
| } |
| |
| /// [Device](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#device-and-variationindex-tables) |
| /// delta formats |
| #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)] |
| #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] |
| #[repr(u16)] |
| #[allow(clippy::manual_non_exhaustive)] |
| pub enum DeltaFormat { |
| /// Signed 2-bit value, 8 values per uint16 |
| #[default] |
| Local2BitDeltas = 0x0001, |
| /// Signed 4-bit value, 4 values per uint16 |
| Local4BitDeltas = 0x0002, |
| /// Signed 8-bit value, 2 values per uint16 |
| Local8BitDeltas = 0x0003, |
| /// VariationIndex table, contains a delta-set index pair. |
| VariationIndex = 0x8000, |
| #[doc(hidden)] |
| /// If font data is malformed we will map unknown values to this variant |
| Unknown, |
| } |
| |
| impl DeltaFormat { |
| /// Create from a raw scalar. |
| /// |
| /// This will never fail; unknown values will be mapped to the `Unknown` variant |
| pub fn new(raw: u16) -> Self { |
| match raw { |
| 0x0001 => Self::Local2BitDeltas, |
| 0x0002 => Self::Local4BitDeltas, |
| 0x0003 => Self::Local8BitDeltas, |
| 0x8000 => Self::VariationIndex, |
| _ => Self::Unknown, |
| } |
| } |
| } |
| |
| impl font_types::Scalar for DeltaFormat { |
| type Raw = <u16 as font_types::Scalar>::Raw; |
| fn to_raw(self) -> Self::Raw { |
| (self as u16).to_raw() |
| } |
| fn from_raw(raw: Self::Raw) -> Self { |
| let t = <u16>::from_raw(raw); |
| Self::new(t) |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> From<DeltaFormat> for FieldType<'a> { |
| fn from(src: DeltaFormat) -> FieldType<'a> { |
| (src as u16).into() |
| } |
| } |
| |
| /// [Device Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#device-and-variationindex-tables) |
| #[derive(Debug, Clone, Copy)] |
| #[doc(hidden)] |
| pub struct DeviceMarker { |
| delta_value_byte_len: usize, |
| } |
| |
| impl DeviceMarker { |
| pub fn start_size_byte_range(&self) -> Range<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn end_size_byte_range(&self) -> Range<usize> { |
| let start = self.start_size_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn delta_format_byte_range(&self) -> Range<usize> { |
| let start = self.end_size_byte_range().end; |
| start..start + DeltaFormat::RAW_BYTE_LEN |
| } |
| |
| pub fn delta_value_byte_range(&self) -> Range<usize> { |
| let start = self.delta_format_byte_range().end; |
| start..start + self.delta_value_byte_len |
| } |
| } |
| |
| impl MinByteRange for DeviceMarker { |
| fn min_byte_range(&self) -> Range<usize> { |
| 0..self.delta_value_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for Device<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| let start_size: u16 = cursor.read()?; |
| let end_size: u16 = cursor.read()?; |
| let delta_format: DeltaFormat = cursor.read()?; |
| let delta_value_byte_len = (DeltaFormat::value_count(delta_format, start_size, end_size)) |
| .checked_mul(u16::RAW_BYTE_LEN) |
| .ok_or(ReadError::OutOfBounds)?; |
| cursor.advance_by(delta_value_byte_len); |
| cursor.finish(DeviceMarker { |
| delta_value_byte_len, |
| }) |
| } |
| } |
| |
| /// [Device Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#device-and-variationindex-tables) |
| pub type Device<'a> = TableRef<'a, DeviceMarker>; |
| |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> Device<'a> { |
| /// Smallest size to correct, in ppem |
| pub fn start_size(&self) -> u16 { |
| let range = self.shape.start_size_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Largest size to correct, in ppem |
| pub fn end_size(&self) -> u16 { |
| let range = self.shape.end_size_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Format of deltaValue array data: 0x0001, 0x0002, or 0x0003 |
| pub fn delta_format(&self) -> DeltaFormat { |
| let range = self.shape.delta_format_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Array of compressed data |
| pub fn delta_value(&self) -> &'a [BigEndian<u16>] { |
| let range = self.shape.delta_value_byte_range(); |
| self.data.read_array(range).unwrap() |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> SomeTable<'a> for Device<'a> { |
| fn type_name(&self) -> &str { |
| "Device" |
| } |
| fn get_field(&self, idx: usize) -> Option<Field<'a>> { |
| match idx { |
| 0usize => Some(Field::new("start_size", self.start_size())), |
| 1usize => Some(Field::new("end_size", self.end_size())), |
| 2usize => Some(Field::new("delta_format", self.delta_format())), |
| 3usize => Some(Field::new("delta_value", self.delta_value())), |
| _ => None, |
| } |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> std::fmt::Debug for Device<'a> { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| (self as &dyn SomeTable<'a>).fmt(f) |
| } |
| } |
| |
| /// Variation index table |
| #[derive(Debug, Clone, Copy)] |
| #[doc(hidden)] |
| pub struct VariationIndexMarker {} |
| |
| impl VariationIndexMarker { |
| pub fn delta_set_outer_index_byte_range(&self) -> Range<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn delta_set_inner_index_byte_range(&self) -> Range<usize> { |
| let start = self.delta_set_outer_index_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn delta_format_byte_range(&self) -> Range<usize> { |
| let start = self.delta_set_inner_index_byte_range().end; |
| start..start + DeltaFormat::RAW_BYTE_LEN |
| } |
| } |
| |
| impl MinByteRange for VariationIndexMarker { |
| fn min_byte_range(&self) -> Range<usize> { |
| 0..self.delta_format_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for VariationIndex<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| cursor.advance::<u16>(); |
| cursor.advance::<u16>(); |
| cursor.advance::<DeltaFormat>(); |
| cursor.finish(VariationIndexMarker {}) |
| } |
| } |
| |
| /// Variation index table |
| pub type VariationIndex<'a> = TableRef<'a, VariationIndexMarker>; |
| |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> VariationIndex<'a> { |
| /// A delta-set outer index — used to select an item variation |
| /// data subtable within the item variation store. |
| pub fn delta_set_outer_index(&self) -> u16 { |
| let range = self.shape.delta_set_outer_index_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// A delta-set inner index — used to select a delta-set row |
| /// within an item variation data subtable. |
| pub fn delta_set_inner_index(&self) -> u16 { |
| let range = self.shape.delta_set_inner_index_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Format, = 0x8000 |
| pub fn delta_format(&self) -> DeltaFormat { |
| let range = self.shape.delta_format_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> SomeTable<'a> for VariationIndex<'a> { |
| fn type_name(&self) -> &str { |
| "VariationIndex" |
| } |
| fn get_field(&self, idx: usize) -> Option<Field<'a>> { |
| match idx { |
| 0usize => Some(Field::new( |
| "delta_set_outer_index", |
| self.delta_set_outer_index(), |
| )), |
| 1usize => Some(Field::new( |
| "delta_set_inner_index", |
| self.delta_set_inner_index(), |
| )), |
| 2usize => Some(Field::new("delta_format", self.delta_format())), |
| _ => None, |
| } |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> std::fmt::Debug for VariationIndex<'a> { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| (self as &dyn SomeTable<'a>).fmt(f) |
| } |
| } |
| |
| /// Either a [Device] table (in a non-variable font) or a [VariationIndex] table (in a variable font) |
| #[derive(Clone)] |
| pub enum DeviceOrVariationIndex<'a> { |
| Device(Device<'a>), |
| VariationIndex(VariationIndex<'a>), |
| } |
| |
| impl<'a> DeviceOrVariationIndex<'a> { |
| ///Return the `FontData` used to resolve offsets for this table. |
| pub fn offset_data(&self) -> FontData<'a> { |
| match self { |
| Self::Device(item) => item.offset_data(), |
| Self::VariationIndex(item) => item.offset_data(), |
| } |
| } |
| } |
| |
| impl<'a> FontRead<'a> for DeviceOrVariationIndex<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let format: DeltaFormat = data.read_at(4usize)?; |
| |
| #[allow(clippy::redundant_guards)] |
| match format { |
| format if format != DeltaFormat::VariationIndex => { |
| Ok(Self::Device(FontRead::read(data)?)) |
| } |
| format if format == DeltaFormat::VariationIndex => { |
| Ok(Self::VariationIndex(FontRead::read(data)?)) |
| } |
| other => Err(ReadError::InvalidFormat(other.into())), |
| } |
| } |
| } |
| |
| impl MinByteRange for DeviceOrVariationIndex<'_> { |
| fn min_byte_range(&self) -> Range<usize> { |
| match self { |
| Self::Device(item) => item.min_byte_range(), |
| Self::VariationIndex(item) => item.min_byte_range(), |
| } |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> DeviceOrVariationIndex<'a> { |
| fn dyn_inner<'b>(&'b self) -> &'b dyn SomeTable<'a> { |
| match self { |
| Self::Device(table) => table, |
| Self::VariationIndex(table) => table, |
| } |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl std::fmt::Debug for DeviceOrVariationIndex<'_> { |
| 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 DeviceOrVariationIndex<'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) |
| } |
| } |
| |
| /// [FeatureVariations Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#featurevariations-table) |
| #[derive(Debug, Clone, Copy)] |
| #[doc(hidden)] |
| pub struct FeatureVariationsMarker { |
| feature_variation_records_byte_len: usize, |
| } |
| |
| impl FeatureVariationsMarker { |
| pub fn version_byte_range(&self) -> Range<usize> { |
| let start = 0; |
| start..start + MajorMinor::RAW_BYTE_LEN |
| } |
| |
| pub fn feature_variation_record_count_byte_range(&self) -> Range<usize> { |
| let start = self.version_byte_range().end; |
| start..start + u32::RAW_BYTE_LEN |
| } |
| |
| pub fn feature_variation_records_byte_range(&self) -> Range<usize> { |
| let start = self.feature_variation_record_count_byte_range().end; |
| start..start + self.feature_variation_records_byte_len |
| } |
| } |
| |
| impl MinByteRange for FeatureVariationsMarker { |
| fn min_byte_range(&self) -> Range<usize> { |
| 0..self.feature_variation_records_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for FeatureVariations<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| cursor.advance::<MajorMinor>(); |
| let feature_variation_record_count: u32 = cursor.read()?; |
| let feature_variation_records_byte_len = (feature_variation_record_count as usize) |
| .checked_mul(FeatureVariationRecord::RAW_BYTE_LEN) |
| .ok_or(ReadError::OutOfBounds)?; |
| cursor.advance_by(feature_variation_records_byte_len); |
| cursor.finish(FeatureVariationsMarker { |
| feature_variation_records_byte_len, |
| }) |
| } |
| } |
| |
| /// [FeatureVariations Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#featurevariations-table) |
| pub type FeatureVariations<'a> = TableRef<'a, FeatureVariationsMarker>; |
| |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> FeatureVariations<'a> { |
| pub fn version(&self) -> MajorMinor { |
| let range = self.shape.version_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Number of feature variation records. |
| pub fn feature_variation_record_count(&self) -> u32 { |
| let range = self.shape.feature_variation_record_count_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Array of feature variation records. |
| pub fn feature_variation_records(&self) -> &'a [FeatureVariationRecord] { |
| let range = self.shape.feature_variation_records_byte_range(); |
| self.data.read_array(range).unwrap() |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> SomeTable<'a> for FeatureVariations<'a> { |
| fn type_name(&self) -> &str { |
| "FeatureVariations" |
| } |
| fn get_field(&self, idx: usize) -> Option<Field<'a>> { |
| match idx { |
| 0usize => Some(Field::new("version", self.version())), |
| 1usize => Some(Field::new( |
| "feature_variation_record_count", |
| self.feature_variation_record_count(), |
| )), |
| 2usize => Some(Field::new( |
| "feature_variation_records", |
| traversal::FieldType::array_of_records( |
| stringify!(FeatureVariationRecord), |
| self.feature_variation_records(), |
| self.offset_data(), |
| ), |
| )), |
| _ => None, |
| } |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> std::fmt::Debug for FeatureVariations<'a> { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| (self as &dyn SomeTable<'a>).fmt(f) |
| } |
| } |
| |
| /// Part of [FeatureVariations] |
| #[derive(Clone, Debug, Copy, bytemuck :: AnyBitPattern)] |
| #[repr(C)] |
| #[repr(packed)] |
| pub struct FeatureVariationRecord { |
| /// Offset to a condition set table, from beginning of |
| /// FeatureVariations table. |
| pub condition_set_offset: BigEndian<Nullable<Offset32>>, |
| /// Offset to a feature table substitution table, from beginning of |
| /// the FeatureVariations table. |
| pub feature_table_substitution_offset: BigEndian<Nullable<Offset32>>, |
| } |
| |
| impl FeatureVariationRecord { |
| /// Offset to a condition set table, from beginning of |
| /// FeatureVariations table. |
| pub fn condition_set_offset(&self) -> Nullable<Offset32> { |
| self.condition_set_offset.get() |
| } |
| |
| /// Offset to a condition set table, from beginning of |
| /// FeatureVariations table. |
| /// |
| /// The `data` argument should be retrieved from the parent table |
| /// By calling its `offset_data` method. |
| pub fn condition_set<'a>( |
| &self, |
| data: FontData<'a>, |
| ) -> Option<Result<ConditionSet<'a>, ReadError>> { |
| self.condition_set_offset().resolve(data) |
| } |
| |
| /// Offset to a feature table substitution table, from beginning of |
| /// the FeatureVariations table. |
| pub fn feature_table_substitution_offset(&self) -> Nullable<Offset32> { |
| self.feature_table_substitution_offset.get() |
| } |
| |
| /// Offset to a feature table substitution table, from beginning of |
| /// the FeatureVariations table. |
| /// |
| /// The `data` argument should be retrieved from the parent table |
| /// By calling its `offset_data` method. |
| pub fn feature_table_substitution<'a>( |
| &self, |
| data: FontData<'a>, |
| ) -> Option<Result<FeatureTableSubstitution<'a>, ReadError>> { |
| self.feature_table_substitution_offset().resolve(data) |
| } |
| } |
| |
| impl FixedSize for FeatureVariationRecord { |
| const RAW_BYTE_LEN: usize = Offset32::RAW_BYTE_LEN + Offset32::RAW_BYTE_LEN; |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> SomeRecord<'a> for FeatureVariationRecord { |
| fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> { |
| RecordResolver { |
| name: "FeatureVariationRecord", |
| get_field: Box::new(move |idx, _data| match idx { |
| 0usize => Some(Field::new( |
| "condition_set_offset", |
| FieldType::offset(self.condition_set_offset(), self.condition_set(_data)), |
| )), |
| 1usize => Some(Field::new( |
| "feature_table_substitution_offset", |
| FieldType::offset( |
| self.feature_table_substitution_offset(), |
| self.feature_table_substitution(_data), |
| ), |
| )), |
| _ => None, |
| }), |
| data, |
| } |
| } |
| } |
| |
| /// [ConditionSet Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#conditionset-table) |
| #[derive(Debug, Clone, Copy)] |
| #[doc(hidden)] |
| pub struct ConditionSetMarker { |
| condition_offsets_byte_len: usize, |
| } |
| |
| impl ConditionSetMarker { |
| pub fn condition_count_byte_range(&self) -> Range<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn condition_offsets_byte_range(&self) -> Range<usize> { |
| let start = self.condition_count_byte_range().end; |
| start..start + self.condition_offsets_byte_len |
| } |
| } |
| |
| impl MinByteRange for ConditionSetMarker { |
| fn min_byte_range(&self) -> Range<usize> { |
| 0..self.condition_offsets_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for ConditionSet<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| let condition_count: u16 = cursor.read()?; |
| let condition_offsets_byte_len = (condition_count as usize) |
| .checked_mul(Offset32::RAW_BYTE_LEN) |
| .ok_or(ReadError::OutOfBounds)?; |
| cursor.advance_by(condition_offsets_byte_len); |
| cursor.finish(ConditionSetMarker { |
| condition_offsets_byte_len, |
| }) |
| } |
| } |
| |
| /// [ConditionSet Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#conditionset-table) |
| pub type ConditionSet<'a> = TableRef<'a, ConditionSetMarker>; |
| |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> ConditionSet<'a> { |
| /// Number of conditions for this condition set. |
| pub fn condition_count(&self) -> u16 { |
| let range = self.shape.condition_count_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Array of offsets to condition tables, from beginning of the |
| /// ConditionSet table. |
| pub fn condition_offsets(&self) -> &'a [BigEndian<Offset32>] { |
| let range = self.shape.condition_offsets_byte_range(); |
| self.data.read_array(range).unwrap() |
| } |
| |
| /// A dynamically resolving wrapper for [`condition_offsets`][Self::condition_offsets]. |
| pub fn conditions(&self) -> ArrayOfOffsets<'a, Condition<'a>, Offset32> { |
| let data = self.data; |
| let offsets = self.condition_offsets(); |
| ArrayOfOffsets::new(offsets, data, ()) |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> SomeTable<'a> for ConditionSet<'a> { |
| fn type_name(&self) -> &str { |
| "ConditionSet" |
| } |
| fn get_field(&self, idx: usize) -> Option<Field<'a>> { |
| match idx { |
| 0usize => Some(Field::new("condition_count", self.condition_count())), |
| 1usize => Some({ |
| let data = self.data; |
| Field::new( |
| "condition_offsets", |
| FieldType::array_of_offsets( |
| better_type_name::<Condition>(), |
| self.condition_offsets(), |
| move |off| { |
| let target = off.get().resolve::<Condition>(data); |
| FieldType::offset(off.get(), target) |
| }, |
| ), |
| ) |
| }), |
| _ => None, |
| } |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> std::fmt::Debug for ConditionSet<'a> { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| (self as &dyn SomeTable<'a>).fmt(f) |
| } |
| } |
| |
| /// [Condition Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#condition-table) |
| /// |
| /// Formats 2..5 are implementations of specification changes currently under debate at ISO for an OFF |
| /// update. For the time being the specification is <https://github.com/harfbuzz/boring-expansion-spec/blob/main/ConditionTree.md>. |
| #[derive(Clone)] |
| pub enum Condition<'a> { |
| Format1AxisRange(ConditionFormat1<'a>), |
| Format2VariableValue(ConditionFormat2<'a>), |
| Format3And(ConditionFormat3<'a>), |
| Format4Or(ConditionFormat4<'a>), |
| Format5Negate(ConditionFormat5<'a>), |
| } |
| |
| impl<'a> Condition<'a> { |
| ///Return the `FontData` used to resolve offsets for this table. |
| pub fn offset_data(&self) -> FontData<'a> { |
| match self { |
| Self::Format1AxisRange(item) => item.offset_data(), |
| Self::Format2VariableValue(item) => item.offset_data(), |
| Self::Format3And(item) => item.offset_data(), |
| Self::Format4Or(item) => item.offset_data(), |
| Self::Format5Negate(item) => item.offset_data(), |
| } |
| } |
| |
| /// Format, = 1 |
| pub fn format(&self) -> u16 { |
| match self { |
| Self::Format1AxisRange(item) => item.format(), |
| Self::Format2VariableValue(item) => item.format(), |
| Self::Format3And(item) => item.format(), |
| Self::Format4Or(item) => item.format(), |
| Self::Format5Negate(item) => item.format(), |
| } |
| } |
| } |
| |
| impl<'a> FontRead<'a> for Condition<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let format: u16 = data.read_at(0usize)?; |
| match format { |
| ConditionFormat1Marker::FORMAT => Ok(Self::Format1AxisRange(FontRead::read(data)?)), |
| ConditionFormat2Marker::FORMAT => Ok(Self::Format2VariableValue(FontRead::read(data)?)), |
| ConditionFormat3Marker::FORMAT => Ok(Self::Format3And(FontRead::read(data)?)), |
| ConditionFormat4Marker::FORMAT => Ok(Self::Format4Or(FontRead::read(data)?)), |
| ConditionFormat5Marker::FORMAT => Ok(Self::Format5Negate(FontRead::read(data)?)), |
| other => Err(ReadError::InvalidFormat(other.into())), |
| } |
| } |
| } |
| |
| impl MinByteRange for Condition<'_> { |
| fn min_byte_range(&self) -> Range<usize> { |
| match self { |
| Self::Format1AxisRange(item) => item.min_byte_range(), |
| Self::Format2VariableValue(item) => item.min_byte_range(), |
| Self::Format3And(item) => item.min_byte_range(), |
| Self::Format4Or(item) => item.min_byte_range(), |
| Self::Format5Negate(item) => item.min_byte_range(), |
| } |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> Condition<'a> { |
| fn dyn_inner<'b>(&'b self) -> &'b dyn SomeTable<'a> { |
| match self { |
| Self::Format1AxisRange(table) => table, |
| Self::Format2VariableValue(table) => table, |
| Self::Format3And(table) => table, |
| Self::Format4Or(table) => table, |
| Self::Format5Negate(table) => table, |
| } |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl std::fmt::Debug for Condition<'_> { |
| 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 Condition<'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 ConditionFormat1Marker { |
| const FORMAT: u16 = 1; |
| } |
| |
| /// [Condition Table Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#condition-table-format-1-font-variation-axis-range): Font Variation Axis Range |
| #[derive(Debug, Clone, Copy)] |
| #[doc(hidden)] |
| pub struct ConditionFormat1Marker {} |
| |
| impl ConditionFormat1Marker { |
| pub fn format_byte_range(&self) -> Range<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn axis_index_byte_range(&self) -> Range<usize> { |
| let start = self.format_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn filter_range_min_value_byte_range(&self) -> Range<usize> { |
| let start = self.axis_index_byte_range().end; |
| start..start + F2Dot14::RAW_BYTE_LEN |
| } |
| |
| pub fn filter_range_max_value_byte_range(&self) -> Range<usize> { |
| let start = self.filter_range_min_value_byte_range().end; |
| start..start + F2Dot14::RAW_BYTE_LEN |
| } |
| } |
| |
| impl MinByteRange for ConditionFormat1Marker { |
| fn min_byte_range(&self) -> Range<usize> { |
| 0..self.filter_range_max_value_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for ConditionFormat1<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| cursor.advance::<u16>(); |
| cursor.advance::<u16>(); |
| cursor.advance::<F2Dot14>(); |
| cursor.advance::<F2Dot14>(); |
| cursor.finish(ConditionFormat1Marker {}) |
| } |
| } |
| |
| /// [Condition Table Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#condition-table-format-1-font-variation-axis-range): Font Variation Axis Range |
| pub type ConditionFormat1<'a> = TableRef<'a, ConditionFormat1Marker>; |
| |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> ConditionFormat1<'a> { |
| /// Format, = 1 |
| pub fn format(&self) -> u16 { |
| let range = self.shape.format_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Index (zero-based) for the variation axis within the 'fvar' |
| /// table. |
| pub fn axis_index(&self) -> u16 { |
| let range = self.shape.axis_index_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Minimum value of the font variation instances that satisfy this |
| /// condition. |
| pub fn filter_range_min_value(&self) -> F2Dot14 { |
| let range = self.shape.filter_range_min_value_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Maximum value of the font variation instances that satisfy this |
| /// condition. |
| pub fn filter_range_max_value(&self) -> F2Dot14 { |
| let range = self.shape.filter_range_max_value_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> SomeTable<'a> for ConditionFormat1<'a> { |
| fn type_name(&self) -> &str { |
| "ConditionFormat1" |
| } |
| fn get_field(&self, idx: usize) -> Option<Field<'a>> { |
| match idx { |
| 0usize => Some(Field::new("format", self.format())), |
| 1usize => Some(Field::new("axis_index", self.axis_index())), |
| 2usize => Some(Field::new( |
| "filter_range_min_value", |
| self.filter_range_min_value(), |
| )), |
| 3usize => Some(Field::new( |
| "filter_range_max_value", |
| self.filter_range_max_value(), |
| )), |
| _ => None, |
| } |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> std::fmt::Debug for ConditionFormat1<'a> { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| (self as &dyn SomeTable<'a>).fmt(f) |
| } |
| } |
| |
| impl Format<u16> for ConditionFormat2Marker { |
| const FORMAT: u16 = 2; |
| } |
| |
| /// [Condition Table Format 2](https://github.com/fonttools/fonttools/blob/5e6b12d12fa08abafbeb7570f47707fbedf69a45/Lib/fontTools/ttLib/tables/otData.py#L3237-L3255): Variation index |
| #[derive(Debug, Clone, Copy)] |
| #[doc(hidden)] |
| pub struct ConditionFormat2Marker {} |
| |
| impl ConditionFormat2Marker { |
| pub fn format_byte_range(&self) -> Range<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn default_value_byte_range(&self) -> Range<usize> { |
| let start = self.format_byte_range().end; |
| start..start + i16::RAW_BYTE_LEN |
| } |
| |
| pub fn var_index_byte_range(&self) -> Range<usize> { |
| let start = self.default_value_byte_range().end; |
| start..start + u32::RAW_BYTE_LEN |
| } |
| } |
| |
| impl MinByteRange for ConditionFormat2Marker { |
| fn min_byte_range(&self) -> Range<usize> { |
| 0..self.var_index_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for ConditionFormat2<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| cursor.advance::<u16>(); |
| cursor.advance::<i16>(); |
| cursor.advance::<u32>(); |
| cursor.finish(ConditionFormat2Marker {}) |
| } |
| } |
| |
| /// [Condition Table Format 2](https://github.com/fonttools/fonttools/blob/5e6b12d12fa08abafbeb7570f47707fbedf69a45/Lib/fontTools/ttLib/tables/otData.py#L3237-L3255): Variation index |
| pub type ConditionFormat2<'a> = TableRef<'a, ConditionFormat2Marker>; |
| |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> ConditionFormat2<'a> { |
| /// Format, = 2 |
| pub fn format(&self) -> u16 { |
| let range = self.shape.format_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Value at default instance. |
| pub fn default_value(&self) -> i16 { |
| let range = self.shape.default_value_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Variation index to vary the value based on current designspace location. |
| pub fn var_index(&self) -> u32 { |
| let range = self.shape.var_index_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> SomeTable<'a> for ConditionFormat2<'a> { |
| fn type_name(&self) -> &str { |
| "ConditionFormat2" |
| } |
| fn get_field(&self, idx: usize) -> Option<Field<'a>> { |
| match idx { |
| 0usize => Some(Field::new("format", self.format())), |
| 1usize => Some(Field::new("default_value", self.default_value())), |
| 2usize => Some(Field::new("var_index", self.var_index())), |
| _ => None, |
| } |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> std::fmt::Debug for ConditionFormat2<'a> { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| (self as &dyn SomeTable<'a>).fmt(f) |
| } |
| } |
| |
| impl Format<u16> for ConditionFormat3Marker { |
| const FORMAT: u16 = 3; |
| } |
| |
| /// [Condition Table Format 3](https://github.com/fonttools/fonttools/blob/5e6b12d12fa08abafbeb7570f47707fbedf69a45/Lib/fontTools/ttLib/tables/otData.py#L3257-L3275): AND |
| #[derive(Debug, Clone, Copy)] |
| #[doc(hidden)] |
| pub struct ConditionFormat3Marker { |
| condition_offsets_byte_len: usize, |
| } |
| |
| impl ConditionFormat3Marker { |
| pub fn format_byte_range(&self) -> Range<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn condition_count_byte_range(&self) -> Range<usize> { |
| let start = self.format_byte_range().end; |
| start..start + u8::RAW_BYTE_LEN |
| } |
| |
| pub fn condition_offsets_byte_range(&self) -> Range<usize> { |
| let start = self.condition_count_byte_range().end; |
| start..start + self.condition_offsets_byte_len |
| } |
| } |
| |
| impl MinByteRange for ConditionFormat3Marker { |
| fn min_byte_range(&self) -> Range<usize> { |
| 0..self.condition_offsets_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for ConditionFormat3<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| cursor.advance::<u16>(); |
| let condition_count: u8 = cursor.read()?; |
| let condition_offsets_byte_len = (condition_count as usize) |
| .checked_mul(Offset24::RAW_BYTE_LEN) |
| .ok_or(ReadError::OutOfBounds)?; |
| cursor.advance_by(condition_offsets_byte_len); |
| cursor.finish(ConditionFormat3Marker { |
| condition_offsets_byte_len, |
| }) |
| } |
| } |
| |
| /// [Condition Table Format 3](https://github.com/fonttools/fonttools/blob/5e6b12d12fa08abafbeb7570f47707fbedf69a45/Lib/fontTools/ttLib/tables/otData.py#L3257-L3275): AND |
| pub type ConditionFormat3<'a> = TableRef<'a, ConditionFormat3Marker>; |
| |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> ConditionFormat3<'a> { |
| /// Format, = 3 |
| pub fn format(&self) -> u16 { |
| let range = self.shape.format_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Number of conditions. |
| pub fn condition_count(&self) -> u8 { |
| let range = self.shape.condition_count_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Array of condition tables for this conjunction (AND) expression. |
| pub fn condition_offsets(&self) -> &'a [BigEndian<Offset24>] { |
| let range = self.shape.condition_offsets_byte_range(); |
| self.data.read_array(range).unwrap() |
| } |
| |
| /// A dynamically resolving wrapper for [`condition_offsets`][Self::condition_offsets]. |
| pub fn conditions(&self) -> ArrayOfOffsets<'a, Condition<'a>, Offset24> { |
| let data = self.data; |
| let offsets = self.condition_offsets(); |
| ArrayOfOffsets::new(offsets, data, ()) |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> SomeTable<'a> for ConditionFormat3<'a> { |
| fn type_name(&self) -> &str { |
| "ConditionFormat3" |
| } |
| fn get_field(&self, idx: usize) -> Option<Field<'a>> { |
| match idx { |
| 0usize => Some(Field::new("format", self.format())), |
| 1usize => Some(Field::new("condition_count", self.condition_count())), |
| 2usize => Some({ |
| let data = self.data; |
| Field::new( |
| "condition_offsets", |
| FieldType::array_of_offsets( |
| better_type_name::<Condition>(), |
| self.condition_offsets(), |
| move |off| { |
| let target = off.get().resolve::<Condition>(data); |
| FieldType::offset(off.get(), target) |
| }, |
| ), |
| ) |
| }), |
| _ => None, |
| } |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> std::fmt::Debug for ConditionFormat3<'a> { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| (self as &dyn SomeTable<'a>).fmt(f) |
| } |
| } |
| |
| impl Format<u16> for ConditionFormat4Marker { |
| const FORMAT: u16 = 4; |
| } |
| |
| /// [Condition Table Format 4](https://github.com/fonttools/fonttools/blob/5e6b12d12fa08abafbeb7570f47707fbedf69a45/Lib/fontTools/ttLib/tables/otData.py#L3276-L3295): OR |
| #[derive(Debug, Clone, Copy)] |
| #[doc(hidden)] |
| pub struct ConditionFormat4Marker { |
| condition_offsets_byte_len: usize, |
| } |
| |
| impl ConditionFormat4Marker { |
| pub fn format_byte_range(&self) -> Range<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn condition_count_byte_range(&self) -> Range<usize> { |
| let start = self.format_byte_range().end; |
| start..start + u8::RAW_BYTE_LEN |
| } |
| |
| pub fn condition_offsets_byte_range(&self) -> Range<usize> { |
| let start = self.condition_count_byte_range().end; |
| start..start + self.condition_offsets_byte_len |
| } |
| } |
| |
| impl MinByteRange for ConditionFormat4Marker { |
| fn min_byte_range(&self) -> Range<usize> { |
| 0..self.condition_offsets_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for ConditionFormat4<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| cursor.advance::<u16>(); |
| let condition_count: u8 = cursor.read()?; |
| let condition_offsets_byte_len = (condition_count as usize) |
| .checked_mul(Offset24::RAW_BYTE_LEN) |
| .ok_or(ReadError::OutOfBounds)?; |
| cursor.advance_by(condition_offsets_byte_len); |
| cursor.finish(ConditionFormat4Marker { |
| condition_offsets_byte_len, |
| }) |
| } |
| } |
| |
| /// [Condition Table Format 4](https://github.com/fonttools/fonttools/blob/5e6b12d12fa08abafbeb7570f47707fbedf69a45/Lib/fontTools/ttLib/tables/otData.py#L3276-L3295): OR |
| pub type ConditionFormat4<'a> = TableRef<'a, ConditionFormat4Marker>; |
| |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> ConditionFormat4<'a> { |
| /// Format, = 4 |
| pub fn format(&self) -> u16 { |
| let range = self.shape.format_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Number of conditions. |
| pub fn condition_count(&self) -> u8 { |
| let range = self.shape.condition_count_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Array of condition tables for this disjunction (OR) expression. |
| pub fn condition_offsets(&self) -> &'a [BigEndian<Offset24>] { |
| let range = self.shape.condition_offsets_byte_range(); |
| self.data.read_array(range).unwrap() |
| } |
| |
| /// A dynamically resolving wrapper for [`condition_offsets`][Self::condition_offsets]. |
| pub fn conditions(&self) -> ArrayOfOffsets<'a, Condition<'a>, Offset24> { |
| let data = self.data; |
| let offsets = self.condition_offsets(); |
| ArrayOfOffsets::new(offsets, data, ()) |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> SomeTable<'a> for ConditionFormat4<'a> { |
| fn type_name(&self) -> &str { |
| "ConditionFormat4" |
| } |
| fn get_field(&self, idx: usize) -> Option<Field<'a>> { |
| match idx { |
| 0usize => Some(Field::new("format", self.format())), |
| 1usize => Some(Field::new("condition_count", self.condition_count())), |
| 2usize => Some({ |
| let data = self.data; |
| Field::new( |
| "condition_offsets", |
| FieldType::array_of_offsets( |
| better_type_name::<Condition>(), |
| self.condition_offsets(), |
| move |off| { |
| let target = off.get().resolve::<Condition>(data); |
| FieldType::offset(off.get(), target) |
| }, |
| ), |
| ) |
| }), |
| _ => None, |
| } |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> std::fmt::Debug for ConditionFormat4<'a> { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| (self as &dyn SomeTable<'a>).fmt(f) |
| } |
| } |
| |
| impl Format<u16> for ConditionFormat5Marker { |
| const FORMAT: u16 = 5; |
| } |
| |
| /// [Condition Table Format 5](https://github.com/fonttools/fonttools/blob/5e6b12d12fa08abafbeb7570f47707fbedf69a45/Lib/fontTools/ttLib/tables/otData.py#L3296-L3308): NOT |
| #[derive(Debug, Clone, Copy)] |
| #[doc(hidden)] |
| pub struct ConditionFormat5Marker {} |
| |
| impl ConditionFormat5Marker { |
| pub fn format_byte_range(&self) -> Range<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn condition_offset_byte_range(&self) -> Range<usize> { |
| let start = self.format_byte_range().end; |
| start..start + Offset24::RAW_BYTE_LEN |
| } |
| } |
| |
| impl MinByteRange for ConditionFormat5Marker { |
| fn min_byte_range(&self) -> Range<usize> { |
| 0..self.condition_offset_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for ConditionFormat5<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| cursor.advance::<u16>(); |
| cursor.advance::<Offset24>(); |
| cursor.finish(ConditionFormat5Marker {}) |
| } |
| } |
| |
| /// [Condition Table Format 5](https://github.com/fonttools/fonttools/blob/5e6b12d12fa08abafbeb7570f47707fbedf69a45/Lib/fontTools/ttLib/tables/otData.py#L3296-L3308): NOT |
| pub type ConditionFormat5<'a> = TableRef<'a, ConditionFormat5Marker>; |
| |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> ConditionFormat5<'a> { |
| /// Format, = 5 |
| pub fn format(&self) -> u16 { |
| let range = self.shape.format_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Condition to negate. |
| pub fn condition_offset(&self) -> Offset24 { |
| let range = self.shape.condition_offset_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Attempt to resolve [`condition_offset`][Self::condition_offset]. |
| pub fn condition(&self) -> Result<Condition<'a>, ReadError> { |
| let data = self.data; |
| self.condition_offset().resolve(data) |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> SomeTable<'a> for ConditionFormat5<'a> { |
| fn type_name(&self) -> &str { |
| "ConditionFormat5" |
| } |
| fn get_field(&self, idx: usize) -> Option<Field<'a>> { |
| match idx { |
| 0usize => Some(Field::new("format", self.format())), |
| 1usize => Some(Field::new( |
| "condition_offset", |
| FieldType::offset(self.condition_offset(), self.condition()), |
| )), |
| _ => None, |
| } |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> std::fmt::Debug for ConditionFormat5<'a> { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| (self as &dyn SomeTable<'a>).fmt(f) |
| } |
| } |
| |
| /// [FeatureTableSubstitution Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#featuretablesubstitution-table) |
| #[derive(Debug, Clone, Copy)] |
| #[doc(hidden)] |
| pub struct FeatureTableSubstitutionMarker { |
| substitutions_byte_len: usize, |
| } |
| |
| impl FeatureTableSubstitutionMarker { |
| pub fn version_byte_range(&self) -> Range<usize> { |
| let start = 0; |
| start..start + MajorMinor::RAW_BYTE_LEN |
| } |
| |
| pub fn substitution_count_byte_range(&self) -> Range<usize> { |
| let start = self.version_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn substitutions_byte_range(&self) -> Range<usize> { |
| let start = self.substitution_count_byte_range().end; |
| start..start + self.substitutions_byte_len |
| } |
| } |
| |
| impl MinByteRange for FeatureTableSubstitutionMarker { |
| fn min_byte_range(&self) -> Range<usize> { |
| 0..self.substitutions_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for FeatureTableSubstitution<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| cursor.advance::<MajorMinor>(); |
| let substitution_count: u16 = cursor.read()?; |
| let substitutions_byte_len = (substitution_count as usize) |
| .checked_mul(FeatureTableSubstitutionRecord::RAW_BYTE_LEN) |
| .ok_or(ReadError::OutOfBounds)?; |
| cursor.advance_by(substitutions_byte_len); |
| cursor.finish(FeatureTableSubstitutionMarker { |
| substitutions_byte_len, |
| }) |
| } |
| } |
| |
| /// [FeatureTableSubstitution Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#featuretablesubstitution-table) |
| pub type FeatureTableSubstitution<'a> = TableRef<'a, FeatureTableSubstitutionMarker>; |
| |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> FeatureTableSubstitution<'a> { |
| /// Major & minor version of the table: (1, 0) |
| pub fn version(&self) -> MajorMinor { |
| let range = self.shape.version_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Number of feature table substitution records. |
| pub fn substitution_count(&self) -> u16 { |
| let range = self.shape.substitution_count_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Array of feature table substitution records. |
| pub fn substitutions(&self) -> &'a [FeatureTableSubstitutionRecord] { |
| let range = self.shape.substitutions_byte_range(); |
| self.data.read_array(range).unwrap() |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> SomeTable<'a> for FeatureTableSubstitution<'a> { |
| fn type_name(&self) -> &str { |
| "FeatureTableSubstitution" |
| } |
| fn get_field(&self, idx: usize) -> Option<Field<'a>> { |
| match idx { |
| 0usize => Some(Field::new("version", self.version())), |
| 1usize => Some(Field::new("substitution_count", self.substitution_count())), |
| 2usize => Some(Field::new( |
| "substitutions", |
| traversal::FieldType::array_of_records( |
| stringify!(FeatureTableSubstitutionRecord), |
| self.substitutions(), |
| self.offset_data(), |
| ), |
| )), |
| _ => None, |
| } |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> std::fmt::Debug for FeatureTableSubstitution<'a> { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| (self as &dyn SomeTable<'a>).fmt(f) |
| } |
| } |
| |
| /// Used in [FeatureTableSubstitution] |
| #[derive(Clone, Debug, Copy, bytemuck :: AnyBitPattern)] |
| #[repr(C)] |
| #[repr(packed)] |
| pub struct FeatureTableSubstitutionRecord { |
| /// The feature table index to match. |
| pub feature_index: BigEndian<u16>, |
| /// Offset to an alternate feature table, from start of the |
| /// FeatureTableSubstitution table. |
| pub alternate_feature_offset: BigEndian<Offset32>, |
| } |
| |
| impl FeatureTableSubstitutionRecord { |
| /// The feature table index to match. |
| pub fn feature_index(&self) -> u16 { |
| self.feature_index.get() |
| } |
| |
| /// Offset to an alternate feature table, from start of the |
| /// FeatureTableSubstitution table. |
| pub fn alternate_feature_offset(&self) -> Offset32 { |
| self.alternate_feature_offset.get() |
| } |
| } |
| |
| impl FixedSize for FeatureTableSubstitutionRecord { |
| const RAW_BYTE_LEN: usize = u16::RAW_BYTE_LEN + Offset32::RAW_BYTE_LEN; |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> SomeRecord<'a> for FeatureTableSubstitutionRecord { |
| fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> { |
| RecordResolver { |
| name: "FeatureTableSubstitutionRecord", |
| get_field: Box::new(move |idx, _data| match idx { |
| 0usize => Some(Field::new("feature_index", self.feature_index())), |
| 1usize => Some(Field::new( |
| "alternate_feature_offset", |
| FieldType::offset( |
| self.alternate_feature_offset(), |
| self.alternate_feature(_data), |
| ), |
| )), |
| _ => None, |
| }), |
| data, |
| } |
| } |
| } |
| |
| #[derive(Debug, Clone, Copy)] |
| #[doc(hidden)] |
| pub struct SizeParamsMarker {} |
| |
| impl SizeParamsMarker { |
| pub fn design_size_byte_range(&self) -> Range<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn identifier_byte_range(&self) -> Range<usize> { |
| let start = self.design_size_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn name_entry_byte_range(&self) -> Range<usize> { |
| let start = self.identifier_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn range_start_byte_range(&self) -> Range<usize> { |
| let start = self.name_entry_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn range_end_byte_range(&self) -> Range<usize> { |
| let start = self.range_start_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| } |
| |
| impl MinByteRange for SizeParamsMarker { |
| fn min_byte_range(&self) -> Range<usize> { |
| 0..self.range_end_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for SizeParams<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| cursor.advance::<u16>(); |
| cursor.advance::<u16>(); |
| cursor.advance::<u16>(); |
| cursor.advance::<u16>(); |
| cursor.advance::<u16>(); |
| cursor.finish(SizeParamsMarker {}) |
| } |
| } |
| |
| pub type SizeParams<'a> = TableRef<'a, SizeParamsMarker>; |
| |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> SizeParams<'a> { |
| /// The first value represents the design size in 720/inch units (decipoints). |
| /// |
| /// The design size entry must be non-zero. When there is a design size but |
| /// no recommended size range, the rest of the array will consist of zeros. |
| pub fn design_size(&self) -> u16 { |
| let range = self.shape.design_size_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// The second value has no independent meaning, but serves as an identifier that associates fonts in a subfamily. |
| /// |
| /// All fonts which share a Typographic or Font Family name and which differ |
| /// only by size range shall have the same subfamily value, and no fonts |
| /// which differ in weight or style shall have the same subfamily value. |
| /// If this value is zero, the remaining fields in the array will be ignored. |
| pub fn identifier(&self) -> u16 { |
| let range = self.shape.identifier_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// The third value enables applications to use a single name for the subfamily identified by the second value. |
| /// |
| /// If the preceding value is non-zero, this value must be set in the range |
| /// 256 – 32767 (inclusive). It records the value of a field in the 'name' |
| /// table, which must contain English-language strings encoded in Windows |
| /// Unicode and Macintosh Roman, and may contain additional strings localized |
| /// to other scripts and languages. Each of these strings is the name |
| /// an application should use, in combination with the family name, to |
| /// represent the subfamily in a menu. Applications will choose the |
| /// appropriate version based on their selection criteria. |
| pub fn name_entry(&self) -> u16 { |
| let range = self.shape.name_entry_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// The fourth and fifth values represent the small end of the recommended |
| /// usage range (exclusive) and the large end of the recommended usage range |
| /// (inclusive), stored in 720/inch units (decipoints). |
| /// |
| /// Ranges must not overlap, and should generally be contiguous. |
| pub fn range_start(&self) -> u16 { |
| let range = self.shape.range_start_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| pub fn range_end(&self) -> u16 { |
| let range = self.shape.range_end_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> SomeTable<'a> for SizeParams<'a> { |
| fn type_name(&self) -> &str { |
| "SizeParams" |
| } |
| fn get_field(&self, idx: usize) -> Option<Field<'a>> { |
| match idx { |
| 0usize => Some(Field::new("design_size", self.design_size())), |
| 1usize => Some(Field::new("identifier", self.identifier())), |
| 2usize => Some(Field::new("name_entry", self.name_entry())), |
| 3usize => Some(Field::new("range_start", self.range_start())), |
| 4usize => Some(Field::new("range_end", self.range_end())), |
| _ => None, |
| } |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> std::fmt::Debug for SizeParams<'a> { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| (self as &dyn SomeTable<'a>).fmt(f) |
| } |
| } |
| |
| #[derive(Debug, Clone, Copy)] |
| #[doc(hidden)] |
| pub struct StylisticSetParamsMarker {} |
| |
| impl StylisticSetParamsMarker { |
| pub fn version_byte_range(&self) -> Range<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn ui_name_id_byte_range(&self) -> Range<usize> { |
| let start = self.version_byte_range().end; |
| start..start + NameId::RAW_BYTE_LEN |
| } |
| } |
| |
| impl MinByteRange for StylisticSetParamsMarker { |
| fn min_byte_range(&self) -> Range<usize> { |
| 0..self.ui_name_id_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for StylisticSetParams<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| cursor.advance::<u16>(); |
| cursor.advance::<NameId>(); |
| cursor.finish(StylisticSetParamsMarker {}) |
| } |
| } |
| |
| pub type StylisticSetParams<'a> = TableRef<'a, StylisticSetParamsMarker>; |
| |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> StylisticSetParams<'a> { |
| pub fn version(&self) -> u16 { |
| let range = self.shape.version_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// The 'name' table name ID that specifies a string (or strings, for |
| /// multiple languages) for a user-interface label for this feature. |
| /// |
| /// The value of uiLabelNameId is expected to be in the font-specific name |
| /// ID range (256-32767), though that is not a requirement in this Feature |
| /// Parameters specification. The user-interface label for the feature can |
| /// be provided in multiple languages. An English string should be included |
| /// as a fallback. The string should be kept to a minimal length to fit |
| /// comfortably with different application interfaces. |
| pub fn ui_name_id(&self) -> NameId { |
| let range = self.shape.ui_name_id_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> SomeTable<'a> for StylisticSetParams<'a> { |
| fn type_name(&self) -> &str { |
| "StylisticSetParams" |
| } |
| fn get_field(&self, idx: usize) -> Option<Field<'a>> { |
| match idx { |
| 0usize => Some(Field::new("version", self.version())), |
| 1usize => Some(Field::new("ui_name_id", self.ui_name_id())), |
| _ => None, |
| } |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> std::fmt::Debug for StylisticSetParams<'a> { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| (self as &dyn SomeTable<'a>).fmt(f) |
| } |
| } |
| |
| impl Format<u16> for CharacterVariantParamsMarker { |
| const FORMAT: u16 = 0; |
| } |
| |
| /// featureParams for ['cv01'-'cv99'](https://docs.microsoft.com/en-us/typography/opentype/spec/features_ae#cv01-cv99) |
| #[derive(Debug, Clone, Copy)] |
| #[doc(hidden)] |
| pub struct CharacterVariantParamsMarker { |
| character_byte_len: usize, |
| } |
| |
| impl CharacterVariantParamsMarker { |
| pub fn format_byte_range(&self) -> Range<usize> { |
| let start = 0; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn feat_ui_label_name_id_byte_range(&self) -> Range<usize> { |
| let start = self.format_byte_range().end; |
| start..start + NameId::RAW_BYTE_LEN |
| } |
| |
| pub fn feat_ui_tooltip_text_name_id_byte_range(&self) -> Range<usize> { |
| let start = self.feat_ui_label_name_id_byte_range().end; |
| start..start + NameId::RAW_BYTE_LEN |
| } |
| |
| pub fn sample_text_name_id_byte_range(&self) -> Range<usize> { |
| let start = self.feat_ui_tooltip_text_name_id_byte_range().end; |
| start..start + NameId::RAW_BYTE_LEN |
| } |
| |
| pub fn num_named_parameters_byte_range(&self) -> Range<usize> { |
| let start = self.sample_text_name_id_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn first_param_ui_label_name_id_byte_range(&self) -> Range<usize> { |
| let start = self.num_named_parameters_byte_range().end; |
| start..start + NameId::RAW_BYTE_LEN |
| } |
| |
| pub fn char_count_byte_range(&self) -> Range<usize> { |
| let start = self.first_param_ui_label_name_id_byte_range().end; |
| start..start + u16::RAW_BYTE_LEN |
| } |
| |
| pub fn character_byte_range(&self) -> Range<usize> { |
| let start = self.char_count_byte_range().end; |
| start..start + self.character_byte_len |
| } |
| } |
| |
| impl MinByteRange for CharacterVariantParamsMarker { |
| fn min_byte_range(&self) -> Range<usize> { |
| 0..self.character_byte_range().end |
| } |
| } |
| |
| impl<'a> FontRead<'a> for CharacterVariantParams<'a> { |
| fn read(data: FontData<'a>) -> Result<Self, ReadError> { |
| let mut cursor = data.cursor(); |
| cursor.advance::<u16>(); |
| cursor.advance::<NameId>(); |
| cursor.advance::<NameId>(); |
| cursor.advance::<NameId>(); |
| cursor.advance::<u16>(); |
| cursor.advance::<NameId>(); |
| let char_count: u16 = cursor.read()?; |
| let character_byte_len = (char_count as usize) |
| .checked_mul(Uint24::RAW_BYTE_LEN) |
| .ok_or(ReadError::OutOfBounds)?; |
| cursor.advance_by(character_byte_len); |
| cursor.finish(CharacterVariantParamsMarker { character_byte_len }) |
| } |
| } |
| |
| /// featureParams for ['cv01'-'cv99'](https://docs.microsoft.com/en-us/typography/opentype/spec/features_ae#cv01-cv99) |
| pub type CharacterVariantParams<'a> = TableRef<'a, CharacterVariantParamsMarker>; |
| |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> CharacterVariantParams<'a> { |
| /// Format number is set to 0. |
| pub fn format(&self) -> u16 { |
| let range = self.shape.format_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// The 'name' table name ID that specifies a string (or strings, |
| /// for multiple languages) for a user-interface label for this |
| /// feature. (May be NULL.) |
| pub fn feat_ui_label_name_id(&self) -> NameId { |
| let range = self.shape.feat_ui_label_name_id_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// The 'name' table name ID that specifies a string (or strings, |
| /// for multiple languages) that an application can use for tooltip |
| /// text for this feature. (May be NULL.) |
| pub fn feat_ui_tooltip_text_name_id(&self) -> NameId { |
| let range = self.shape.feat_ui_tooltip_text_name_id_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// The 'name' table name ID that specifies sample text that |
| /// illustrates the effect of this feature. (May be NULL.) |
| pub fn sample_text_name_id(&self) -> NameId { |
| let range = self.shape.sample_text_name_id_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// Number of named parameters. (May be zero.) |
| pub fn num_named_parameters(&self) -> u16 { |
| let range = self.shape.num_named_parameters_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// The first 'name' table name ID used to specify strings for |
| /// user-interface labels for the feature parameters. (Must be zero |
| /// if numParameters is zero.) |
| pub fn first_param_ui_label_name_id(&self) -> NameId { |
| let range = self.shape.first_param_ui_label_name_id_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// The count of characters for which this feature provides glyph |
| /// variants. (May be zero.) |
| pub fn char_count(&self) -> u16 { |
| let range = self.shape.char_count_byte_range(); |
| self.data.read_at(range.start).unwrap() |
| } |
| |
| /// The Unicode Scalar Value of the characters for which this |
| /// feature provides glyph variants. |
| pub fn character(&self) -> &'a [BigEndian<Uint24>] { |
| let range = self.shape.character_byte_range(); |
| self.data.read_array(range).unwrap() |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| impl<'a> SomeTable<'a> for CharacterVariantParams<'a> { |
| fn type_name(&self) -> &str { |
| "CharacterVariantParams" |
| } |
| fn get_field(&self, idx: usize) -> Option<Field<'a>> { |
| match idx { |
| 0usize => Some(Field::new("format", self.format())), |
| 1usize => Some(Field::new( |
| "feat_ui_label_name_id", |
| self.feat_ui_label_name_id(), |
| )), |
| 2usize => Some(Field::new( |
| "feat_ui_tooltip_text_name_id", |
| self.feat_ui_tooltip_text_name_id(), |
| )), |
| 3usize => Some(Field::new( |
| "sample_text_name_id", |
| self.sample_text_name_id(), |
| )), |
| 4usize => Some(Field::new( |
| "num_named_parameters", |
| self.num_named_parameters(), |
| )), |
| 5usize => Some(Field::new( |
| "first_param_ui_label_name_id", |
| self.first_param_ui_label_name_id(), |
| )), |
| 6usize => Some(Field::new("char_count", self.char_count())), |
| 7usize => Some(Field::new("character", self.character())), |
| _ => None, |
| } |
| } |
| } |
| |
| #[cfg(feature = "experimental_traverse")] |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a> std::fmt::Debug for CharacterVariantParams<'a> { |
| fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| (self as &dyn SomeTable<'a>).fmt(f) |
| } |
| } |