use std::fmt::{ Debug, Formatter, Result as FmtResult };
use num_enum::{ FromPrimitive, IntoPrimitive };
use super::ReadError;
use super::core::*;
use super::common::{ CoverageTable, ScriptList, FeatureList, LookupTable as GenericLookupTable };
#[derive(Clone, Copy, Debug)]
pub enum SingleSubstitution<'a> {
DeltaGlyphId(i16),
SubstituteGlyphArray(U16Array<'a>),
}
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct SingleSubstitutionTable<'a>(&'a [u8]);
impl<'a> RandomAccess<'a> for SingleSubstitutionTable<'a> {
fn bytes(&self) -> &'a [u8] { self.0 }
}
impl<'a> SingleSubstitutionTable<'a> {
pub fn format(&self) -> u16 { self.uint16(0) }
pub fn matches(&self) -> Result<CoverageTable<'a>, ReadError> {
self.0[self.uint16(2) as usize..].try_into()
}
pub fn substitution(&self) -> SingleSubstitution<'a> {
match self.format() {
1 => SingleSubstitution::DeltaGlyphId(self.int16(4)),
2 => SingleSubstitution::SubstituteGlyphArray(self.uint16_array(6, self.uint16(4) as usize)),
_ => unreachable!(),
}
}
pub fn map(&self, glyph: u16) -> Result<Option<u16>, ReadError> {
match self.matches()?.map(glyph) {
Some(index) => Ok(Some(match self.substitution() {
SingleSubstitution::DeltaGlyphId(delta) => (glyph as i32 + delta as i32) as u16,
SingleSubstitution::SubstituteGlyphArray(array) => array.get(index as usize),
})),
None => Ok(None),
}
}
}
impl Debug for SingleSubstitutionTable<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
f.debug_struct("SingleSubstitutionTable")
.field("matches", &self.matches())
.field("substitution", &self.substitution())
.finish()
}
}
impl<'a> TryFrom<&'a [u8]> for SingleSubstitutionTable<'a> {
type Error = ReadError;
fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
if value.len() < 6 { return Err(ReadError::UnexpectedEof); }
match value.uint16(0) {
1 => Ok(SingleSubstitutionTable(&value[0..])),
2 => {
let size = 6 + value.uint16(4) as usize * 2;
if value.len() < size { return Err(ReadError::UnexpectedEof); }
Ok(SingleSubstitutionTable(&value[0..]))
},
x => Err(ReadError::UnknownSingleSubstitutionFormat(x)),
}
}
}
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct Ligature<'a>(&'a [u8]);
impl<'a> RandomAccess<'a> for Ligature<'a> {
fn bytes(&self) -> &'a [u8] { self.0 }
}
impl<'a> Ligature<'a> {
pub fn ligature_glyph(&self) -> u16 { self.uint16(0) }
pub fn component_glyphs(&self) -> U16Array<'a> { self.uint16_array(4, self.uint16(2) as usize - 1) }
}
impl Debug for Ligature<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
f.debug_struct("Ligature")
.field("ligature_glyph", &self.ligature_glyph())
.field("component_glyphs", &self.component_glyphs())
.finish()
}
}
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct LigatureSet<'a>(&'a [u8]);
impl<'a> RandomAccess<'a> for LigatureSet<'a> {
fn bytes(&self) -> &'a [u8] { self.0 }
}
impl<'a> LigatureSet<'a> {
pub fn count(&self) -> u16 { self.uint16(0) }
pub fn get(&self, index: u16) -> Result<Ligature<'a>, ReadError> {
let start = self.uint16(2 + index as usize * 2) as usize;
if start + 4 > self.0.len() { return Err(ReadError::UnexpectedEof); }
let stop = start + 2 + self.uint16(start + 2) as usize * 2;
if stop > self.0.len() { return Err(ReadError::UnexpectedEof); }
Ok(Ligature(&self.0[start..stop]))
}
pub fn iter(&self) -> impl ExactSizeIterator<Item = Result<Ligature<'a>, ReadError>> + '_ {
(0..self.count()).map(|x| self.get(x))
}
}
impl Debug for LigatureSet<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
f.debug_list()
.entries(self.iter())
.finish()
}
}
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct LigatureSubstitutionTable<'a>(&'a [u8]);
impl<'a> RandomAccess<'a> for LigatureSubstitutionTable<'a> {
fn bytes(&self) -> &'a [u8] { self.0 }
}
impl<'a> LigatureSubstitutionTable<'a> {
pub fn format(&self) -> u16 { self.uint16(0) }
pub fn prefixes(&self) -> Result<CoverageTable<'a>, ReadError> {
self.0[self.uint16(2) as usize..].try_into()
}
pub fn count(&self) -> u16 { self.uint16(4) }
pub fn get(&self, index: u16) -> Result<LigatureSet<'a>, ReadError> {
let start = self.uint16(6 + index as usize * 2) as usize;
if start + 2 > self.0.len() { return Err(ReadError::UnexpectedEof); }
let stop = start + 2 + self.uint16(start) as usize * 2;
if stop > self.0.len() { return Err(ReadError::UnexpectedEof); }
Ok(LigatureSet(&self.0[start..]))
}
pub fn iter(&self) -> impl ExactSizeIterator<Item = Result<LigatureSet<'a>, ReadError>> + '_ {
(0..self.count()).map(|x| self.get(x))
}
pub fn get_by_prefix(&self, prefix_glyph: u16) -> Result<Option<LigatureSet<'a>>, ReadError> {
if let Some(index) = self.prefixes()?.map(prefix_glyph) {
Ok(Some(self.get(index)?))
} else {
Ok(None)
}
}
}
impl Debug for LigatureSubstitutionTable<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
struct Items<'a>(LigatureSubstitutionTable<'a>);
impl Debug for Items<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
f.debug_list()
.entries(self.0.iter())
.finish()
}
}
f.debug_struct("LigatureSubstitutionTable")
.field("prefixes", &self.prefixes())
.field("ligatures", &Items(*self))
.finish()
}
}
impl<'a> TryFrom<&'a [u8]> for LigatureSubstitutionTable<'a> {
type Error = ReadError;
fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
Ok(Self(value))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[derive(FromPrimitive, IntoPrimitive)]
#[repr(u16)]
pub enum LookupType {
Single = 1,
Multiple = 2,
Alternate = 3,
Ligature = 4,
ContextualSubstitution = 5,
ChainedContextsSubstitution = 6,
SubstitutionExtension = 7,
ReverseChainingContextSingle = 8,
#[num_enum(catch_all)]
Unknown(u16),
}
#[derive(Debug, Clone, Copy)]
pub enum LookupTable<'a> {
Single(GenericLookupTable<'a, LookupType, SingleSubstitutionTable<'a>>),
Ligature(GenericLookupTable<'a, LookupType, LigatureSubstitutionTable<'a>>),
}
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct LookupList<'a>(&'a [u8]);
impl<'a> RandomAccess<'a> for LookupList<'a> {
fn bytes(&self) -> &'a [u8] { self.0 }
}
impl<'a> LookupList<'a> {
pub fn count(&self) -> u16 { self.uint16(0) }
pub fn get(&self, index: u16) -> Result<LookupTable<'a>, ReadError> {
let offset = self.uint16(2 + index as usize * 2) as usize;
if offset + 2 > self.0.len() { return Err(ReadError::UnexpectedEof); }
let lookup_type = self.uint16(offset).into();
match lookup_type {
LookupType::Single => Ok(LookupTable::Single(self.0[offset..].try_into()?)),
LookupType::Ligature => Ok(LookupTable::Ligature(self.0[offset..].try_into()?)),
x => Err(ReadError::UnsupportedGsubLookupType(x)),
}
}
pub fn iter(&self) -> impl ExactSizeIterator<Item = Result<LookupTable<'a>, ReadError>> + '_ {
(0..self.count()).map(|x| self.get(x))
}
}
impl Debug for LookupList<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
f.debug_list()
.entries(self.iter())
.finish()
}
}
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct GlyphSubstitutionTable<'a>(&'a [u8]);
impl<'a> RandomAccess<'a> for GlyphSubstitutionTable<'a> {
fn bytes(&self) -> &'a [u8] { self.0 }
}
impl<'a> GlyphSubstitutionTable<'a> {
pub fn version(&self) -> (u16, u16) { (self.uint16(0), self.uint16(2)) }
pub fn script_list(&self) -> Result<ScriptList<'a>, ReadError> {
let offset = self.uint16(4) as usize;
if offset >= self.0.len() { return Err(ReadError::UnexpectedEof); }
ScriptList::try_from(&self.0[offset..])
}
pub fn feature_list(&self) -> Result<FeatureList<'a>, ReadError> {
let offset = self.uint16(6) as usize;
if offset >= self.0.len() { return Err(ReadError::UnexpectedEof); }
FeatureList::try_from(&self.0[offset..])
}
pub fn lookup_list(&self) -> Result<LookupList<'a>, ReadError> {
let offset = self.uint16(8) as usize;
if offset >= self.0.len() { return Err(ReadError::UnexpectedEof); }
Ok(LookupList(&self.0[offset..]))
}
pub fn feature_variations_offset(&self) -> Option<u32> {
match self.version() {
(1, 0) => None,
(1, 1) => Some(self.uint32(10)),
_ => unreachable!(),
}
}
}
impl Debug for GlyphSubstitutionTable<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
f.debug_struct("GlyphSubstitutionTable")
.field("version", &self.version())
.field("script_list", &self.script_list())
.field("feature_list", &self.feature_list())
.field("lookup_list", &self.lookup_list())
.field("feature_variations_offset", &self.feature_variations_offset())
.finish()
}
}
impl<'a> TryFrom<&'a [u8]> for GlyphSubstitutionTable<'a> {
type Error = ReadError;
fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
if value.len() < 10 { return Err(ReadError::UnexpectedEof); }
match (value.uint16(0), value.uint16(2)) {
(1, 0) => {
Ok(GlyphSubstitutionTable(value))
},
(1, 1) => {
if value.len() < 14 { return Err(ReadError::UnexpectedEof); }
Ok(GlyphSubstitutionTable(value))
},
x => Err(ReadError::UnsupportedTableVersionPair(x.0, x.1))
}
}
}