use ttf_parser::parser::{Stream, FromData, LazyArray32, NumFrom, Offset32, Offset};
use ttf_parser::GlyphId;
use crate::Mask;
use crate::tables::aat;
#[derive(Clone, Copy)]
pub struct Chains<'a> {
index: u32,
len: u32,
stream: Stream<'a>,
number_of_glyphs: u16,
}
impl<'a> Chains<'a> {
pub fn parse(data: &'a [u8], number_of_glyphs: u16) -> Option<Self> {
let mut s = Stream::new(data);
s.skip::<u16>(); s.skip::<u16>(); let count: u32 = s.read()?;
Some(Chains {
index: 0,
len: count,
stream: s,
number_of_glyphs,
})
}
}
impl<'a> Iterator for Chains<'a> {
type Item = Chain<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.index == self.len {
return None;
}
if self.stream.at_end() {
return None;
}
let default_flags: u32 = self.stream.read()?;
let len: u32 = self.stream.read()?;
let features_count: u32 = self.stream.read()?;
let subtables_count: u32 = self.stream.read()?;
let features: LazyArray32<Feature> = self.stream.read_array32(features_count)?;
const HEADER_LEN: usize = 16;
let len = usize::num_from(len)
.checked_sub(HEADER_LEN)?
.checked_sub(Feature::SIZE * usize::num_from(features_count))?;
let subtables_data = self.stream.read_bytes(len)?;
Some(Chain {
default_flags,
features,
subtables_count,
subtables_data,
number_of_glyphs: self.number_of_glyphs,
})
}
}
#[derive(Clone, Copy)]
pub struct Chain<'a> {
default_flags: Mask,
features: LazyArray32<'a, Feature>,
subtables_count: u32,
subtables_data: &'a [u8],
number_of_glyphs: u16,
}
impl<'a> Chain<'a> {
pub fn default_flags(&self) -> Mask {
self.default_flags
}
pub fn features(&self) -> LazyArray32<'a, Feature> {
self.features
}
pub fn subtables(&self) -> Subtables<'a> {
Subtables {
index: 0,
len: self.subtables_count,
stream: Stream::new(self.subtables_data),
number_of_glyphs: self.number_of_glyphs,
}
}
}
#[derive(Clone, Copy)]
pub struct Feature {
pub kind: u16,
pub setting: u16,
pub enable_flags: u32,
pub disable_flags: u32,
}
impl FromData for Feature {
const SIZE: usize = 12;
#[inline]
fn parse(data: &[u8]) -> Option<Self> {
let mut s = Stream::new(data);
Some(Feature {
kind: s.read()?,
setting: s.read()?,
enable_flags: s.read()?,
disable_flags: s.read()?,
})
}
}
#[derive(Clone, Copy)]
pub struct Subtables<'a> {
index: u32,
len: u32,
stream: Stream<'a>,
number_of_glyphs: u16,
}
impl<'a> Iterator for Subtables<'a> {
type Item = Subtable<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.index == self.len {
return None;
}
if self.stream.at_end() {
return None;
}
let len: u32 = self.stream.read()?;
let coverage: u8 = self.stream.read()?;
self.stream.skip::<u16>(); let kind: u8 = self.stream.read()?;
let feature_flags: u32 = self.stream.read()?;
const HEADER_LEN: usize = 12;
let len = usize::num_from(len).checked_sub(HEADER_LEN)?;
let subtables_data = self.stream.read_bytes(len)?;
let kind = match kind {
0 => {
let table = aat::StateTable::parse(subtables_data, self.number_of_glyphs)?;
SubtableKind::Rearrangement(table)
}
1 => {
let table = ContextualSubtable::parse(subtables_data, self.number_of_glyphs)?;
SubtableKind::Contextual(table)
}
2 => {
let table = LigatureSubtable::parse(subtables_data, self.number_of_glyphs)?;
SubtableKind::Ligature(table)
}
4 => {
SubtableKind::NonContextual(aat::Lookup::parse(subtables_data)?)
}
5 => {
let table = InsertionSubtable::parse(subtables_data, self.number_of_glyphs)?;
SubtableKind::Insertion(table)
}
_ => return None,
};
Some(Subtable {
kind,
coverage,
feature_flags,
})
}
}
pub struct Subtable<'a> {
pub kind: SubtableKind<'a>,
pub coverage: u8,
pub feature_flags: u32,
}
impl Subtable<'_> {
pub fn is_logical(&self) -> bool {
self.coverage & 0x10 != 0
}
pub fn is_all_directions(&self) -> bool {
self.coverage & 0x20 != 0
}
pub fn is_backwards(&self) -> bool {
self.coverage & 0x40 != 0
}
pub fn is_vertical(&self) -> bool {
self.coverage & 0x80 != 0
}
}
pub enum SubtableKind<'a> {
Rearrangement(aat::StateTable<'a>),
Contextual(ContextualSubtable<'a>),
Ligature(LigatureSubtable<'a>),
NonContextual(aat::Lookup<'a>),
Insertion(InsertionSubtable<'a>),
}
pub struct ContextualSubtable<'a> {
pub offsets_data: &'a [u8],
pub machine: aat::StateTable<'a>,
pub offsets: LazyArray32<'a, Offset32>,
}
impl<'a> ContextualSubtable<'a> {
fn parse(data: &'a [u8], number_of_glyphs: u16) -> Option<Self> {
let mut s = Stream::new(data);
let machine = aat::StateTable::parse(data, number_of_glyphs)?;
s.advance(aat::StateTable::SIZE);
let offset = s.read::<Offset32>()?.to_usize();
let offsets_data = data.get(offset..)?;
let offsets = LazyArray32::new(offsets_data);
Some(ContextualSubtable {
offsets_data,
machine,
offsets,
})
}
}
#[derive(Copy, Clone)]
pub struct ContextualEntry {
pub mark_index: u16,
pub current_index: u16,
}
impl FromData for ContextualEntry {
const SIZE: usize = 4;
#[inline]
fn parse(data: &[u8]) -> Option<Self> {
let mut s = Stream::new(data);
Some(ContextualEntry {
mark_index: s.read()?,
current_index: s.read()?,
})
}
}
pub struct LigatureSubtable<'a> {
pub machine: aat::StateTable<'a>,
pub ligature_actions: LazyArray32<'a, u32>,
pub components: LazyArray32<'a, u16>,
pub ligatures: LazyArray32<'a, GlyphId>,
}
impl<'a> LigatureSubtable<'a> {
fn parse(data: &'a [u8], number_of_glyphs: u16) -> Option<Self> {
let mut s = Stream::new(data);
let machine = aat::StateTable::parse(data, number_of_glyphs)?;
s.advance(aat::StateTable::SIZE);
let ligature_action_offset = s.read::<Offset32>()?.to_usize();
let component_offset = s.read::<Offset32>()?.to_usize();
let ligature_offset = s.read::<Offset32>()?.to_usize();
let ligature_actions = LazyArray32::new(data.get(ligature_action_offset..)?);
let components = LazyArray32::new(data.get(component_offset..)?);
let ligatures = LazyArray32::new(data.get(ligature_offset..)?);
Some(LigatureSubtable {
machine,
ligature_actions,
components,
ligatures,
})
}
}
#[derive(Copy, Clone)]
pub struct InsertionEntry {
pub current_insert_index: u16,
pub marked_insert_index: u16,
}
impl FromData for InsertionEntry {
const SIZE: usize = 4;
#[inline]
fn parse(data: &[u8]) -> Option<Self> {
let mut s = Stream::new(data);
Some(InsertionEntry {
current_insert_index: s.read()?,
marked_insert_index: s.read()?,
})
}
}
pub struct InsertionSubtable<'a> {
pub machine: aat::StateTable<'a>,
pub glyphs: LazyArray32<'a, GlyphId>,
}
impl<'a> InsertionSubtable<'a> {
fn parse(data: &'a [u8], number_of_glyphs: u16) -> Option<Self> {
let mut s = Stream::new(data);
let machine = aat::StateTable::parse(data, number_of_glyphs)?;
s.advance(aat::StateTable::SIZE);
let offset = s.read::<Offset32>()?.to_usize();
let glyphs = LazyArray32::new(data.get(offset..)?);
Some(InsertionSubtable {
machine,
glyphs,
})
}
}