#[cfg(feature = "apple-layout")]
use crate::aat;
use crate::parser::{FromData, LazyArray16, NumFrom, Offset, Offset16, Stream};
use crate::GlyphId;
#[derive(Clone, Copy, Debug)]
struct OTCoverage(u8);
#[rustfmt::skip]
impl OTCoverage {
#[inline] fn is_horizontal(self) -> bool { self.0 & (1 << 0) != 0 }
#[inline] fn has_cross_stream(self) -> bool { self.0 & (1 << 2) != 0 }
}
impl FromData for OTCoverage {
const SIZE: usize = 1;
#[inline]
fn parse(data: &[u8]) -> Option<Self> {
data.get(0).copied().map(OTCoverage)
}
}
#[derive(Clone, Copy, Debug)]
struct AATCoverage(u8);
#[rustfmt::skip]
impl AATCoverage {
#[inline] fn is_horizontal(self) -> bool { self.0 & (1 << 7) == 0 }
#[inline] fn has_cross_stream(self) -> bool { self.0 & (1 << 6) != 0 }
#[inline] fn is_variable(self) -> bool { self.0 & (1 << 5) != 0 }
}
impl FromData for AATCoverage {
const SIZE: usize = 1;
#[inline]
fn parse(data: &[u8]) -> Option<Self> {
data.get(0).copied().map(AATCoverage)
}
}
#[derive(Clone, Copy, Debug)]
pub struct KerningPair {
pub pair: u32,
pub value: i16,
}
impl KerningPair {
#[inline]
pub fn left(&self) -> GlyphId {
GlyphId((self.pair >> 16) as u16)
}
#[inline]
pub fn right(&self) -> GlyphId {
GlyphId(self.pair as u16)
}
}
impl FromData for KerningPair {
const SIZE: usize = 6;
#[inline]
fn parse(data: &[u8]) -> Option<Self> {
let mut s = Stream::new(data);
Some(KerningPair {
pair: s.read::<u32>()?,
value: s.read::<i16>()?,
})
}
}
#[allow(missing_docs)]
#[derive(Clone, Debug)]
pub enum Format<'a> {
Format0(Subtable0<'a>),
#[cfg(feature = "apple-layout")]
Format1(aat::StateTable<'a>),
#[cfg(not(feature = "apple-layout"))]
Format1,
Format2(Subtable2<'a>),
Format3(Subtable3<'a>),
}
#[derive(Clone, Debug)]
pub struct Subtable<'a> {
pub horizontal: bool,
pub variable: bool,
pub has_cross_stream: bool,
pub has_state_machine: bool,
pub format: Format<'a>,
}
impl<'a> Subtable<'a> {
#[inline]
pub fn glyphs_kerning(&self, left: GlyphId, right: GlyphId) -> Option<i16> {
match self.format {
Format::Format0(ref subtable) => subtable.glyphs_kerning(left, right),
Format::Format2(ref subtable) => subtable.glyphs_kerning(left, right),
Format::Format3(ref subtable) => subtable.glyphs_kerning(left, right),
_ => None,
}
}
}
#[derive(Clone, Copy)]
pub struct Subtables<'a> {
is_aat: bool,
count: u32,
data: &'a [u8],
}
impl<'a> Subtables<'a> {
pub fn len(&self) -> u32 {
self.count
}
pub fn is_empty(&self) -> bool {
self.count == 0
}
}
impl core::fmt::Debug for Subtables<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "Subtables {{ ... }}")
}
}
impl<'a> IntoIterator for Subtables<'a> {
type Item = Subtable<'a>;
type IntoIter = SubtablesIter<'a>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
SubtablesIter {
is_aat: self.is_aat,
table_index: 0,
number_of_tables: self.count,
stream: Stream::new(self.data),
}
}
}
#[allow(missing_debug_implementations)]
#[derive(Clone, Default)]
pub struct SubtablesIter<'a> {
is_aat: bool,
table_index: u32,
number_of_tables: u32,
stream: Stream<'a>,
}
impl<'a> Iterator for SubtablesIter<'a> {
type Item = Subtable<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.table_index == self.number_of_tables {
return None;
}
if self.stream.at_end() {
return None;
}
if self.is_aat {
const HEADER_SIZE: u8 = 8;
let table_len = self.stream.read::<u32>()?;
let coverage = self.stream.read::<AATCoverage>()?;
let format_id = self.stream.read::<u8>()?;
self.stream.skip::<u16>(); if format_id > 3 {
return None;
}
let data_len = usize::num_from(table_len).checked_sub(usize::from(HEADER_SIZE))?;
let data = self.stream.read_bytes(data_len)?;
let format = match format_id {
0 => Format::Format0(Subtable0::parse(data)?),
#[cfg(feature = "apple-layout")]
1 => Format::Format1(aat::StateTable::parse(data)?),
#[cfg(not(feature = "apple-layout"))]
1 => Format::Format1,
2 => Format::Format2(Subtable2::parse(HEADER_SIZE, data)?),
3 => Format::Format3(Subtable3::parse(data)?),
_ => return None,
};
Some(Subtable {
horizontal: coverage.is_horizontal(),
variable: coverage.is_variable(),
has_cross_stream: coverage.has_cross_stream(),
has_state_machine: format_id == 1,
format,
})
} else {
const HEADER_SIZE: u8 = 6;
self.stream.skip::<u16>(); let table_len = self.stream.read::<u16>()?;
let format_id = self.stream.read::<u8>()?;
let coverage = self.stream.read::<OTCoverage>()?;
if format_id != 0 && format_id != 2 {
return None;
}
let data_len = if self.number_of_tables == 1 {
self.stream.tail()?.len()
} else {
usize::from(table_len).checked_sub(usize::from(HEADER_SIZE))?
};
let data = self.stream.read_bytes(data_len)?;
let format = match format_id {
0 => Format::Format0(Subtable0::parse(data)?),
2 => Format::Format2(Subtable2::parse(HEADER_SIZE, data)?),
_ => return None,
};
Some(Subtable {
horizontal: coverage.is_horizontal(),
variable: false, has_cross_stream: coverage.has_cross_stream(),
has_state_machine: format_id == 1,
format,
})
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct Subtable0<'a> {
pub pairs: LazyArray16<'a, KerningPair>,
}
impl<'a> Subtable0<'a> {
pub fn parse(data: &'a [u8]) -> Option<Self> {
let mut s = Stream::new(data);
let number_of_pairs = s.read::<u16>()?;
s.advance(6); let pairs = s.read_array16::<KerningPair>(number_of_pairs)?;
Some(Self { pairs })
}
#[inline]
pub fn glyphs_kerning(&self, left: GlyphId, right: GlyphId) -> Option<i16> {
let needle = u32::from(left.0) << 16 | u32::from(right.0);
self.pairs
.binary_search_by(|v| v.pair.cmp(&needle))
.map(|(_, v)| v.value)
}
}
#[derive(Clone, Copy, Debug)]
pub struct Subtable2<'a> {
data: &'a [u8],
header_len: u8,
}
impl<'a> Subtable2<'a> {
pub fn parse(header_len: u8, data: &'a [u8]) -> Option<Self> {
Some(Self { header_len, data })
}
pub fn glyphs_kerning(&self, left: GlyphId, right: GlyphId) -> Option<i16> {
let mut s = Stream::new(self.data);
s.skip::<u16>(); let header_len = usize::from(self.header_len);
let left_hand_table_offset = s.read::<Offset16>()?.to_usize().checked_sub(header_len)?;
let right_hand_table_offset = s.read::<Offset16>()?.to_usize().checked_sub(header_len)?;
let array_offset = s.read::<Offset16>()?.to_usize().checked_sub(header_len)?;
let left_class = get_format2_class(left.0, left_hand_table_offset, self.data).unwrap_or(0);
let right_class =
get_format2_class(right.0, right_hand_table_offset, self.data).unwrap_or(0);
if usize::from(left_class) < array_offset {
return None;
}
let index = usize::from(left_class) + usize::from(right_class);
let value_offset = index.checked_sub(header_len)?;
Stream::read_at::<i16>(self.data, value_offset)
}
}
pub(crate) fn get_format2_class(glyph_id: u16, offset: usize, data: &[u8]) -> Option<u16> {
let mut s = Stream::new_at(data, offset)?;
let first_glyph = s.read::<u16>()?;
let index = glyph_id.checked_sub(first_glyph)?;
let number_of_classes = s.read::<u16>()?;
let classes = s.read_array16::<u16>(number_of_classes)?;
classes.get(index)
}
#[derive(Clone, Copy, Debug)]
pub struct Subtable3<'a> {
data: &'a [u8],
}
impl<'a> Subtable3<'a> {
pub fn parse(data: &'a [u8]) -> Option<Self> {
Some(Self { data })
}
#[inline]
pub fn glyphs_kerning(&self, left: GlyphId, right: GlyphId) -> Option<i16> {
let mut s = Stream::new(self.data);
let glyph_count = s.read::<u16>()?;
let kerning_values_count = s.read::<u8>()?;
let left_hand_classes_count = s.read::<u8>()?;
let right_hand_classes_count = s.read::<u8>()?;
s.skip::<u8>(); let indices_count =
u16::from(left_hand_classes_count) * u16::from(right_hand_classes_count);
let kerning_values = s.read_array16::<i16>(u16::from(kerning_values_count))?;
let left_hand_classes = s.read_array16::<u8>(glyph_count)?;
let right_hand_classes = s.read_array16::<u8>(glyph_count)?;
let indices = s.read_array16::<u8>(indices_count)?;
let left_class = left_hand_classes.get(left.0)?;
let right_class = right_hand_classes.get(right.0)?;
if left_class > left_hand_classes_count || right_class > right_hand_classes_count {
return None;
}
let index =
u16::from(left_class) * u16::from(right_hand_classes_count) + u16::from(right_class);
let index = indices.get(index)?;
kerning_values.get(u16::from(index))
}
}
#[derive(Clone, Copy, Debug)]
pub struct Table<'a> {
pub subtables: Subtables<'a>,
}
impl<'a> Table<'a> {
pub fn parse(data: &'a [u8]) -> Option<Self> {
let mut s = Stream::new(data);
let version = s.read::<u16>()?;
let subtables = if version == 0 {
let count = s.read::<u16>()?;
Subtables {
is_aat: false,
count: u32::from(count),
data: s.tail()?,
}
} else {
s.skip::<u16>(); let count = s.read::<u32>()?;
Subtables {
is_aat: true,
count,
data: s.tail()?,
}
};
Some(Self { subtables })
}
}