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