#![allow(unused_variables)]
#[cfg(feature = "variable-fonts")]
use super::FeatureVariations;
use super::LookupList;
#[cfg(feature = "variable-fonts")]
use crate::parser::Offset32;
use crate::parser::{FromData, LazyArray16, Offset, Offset16, Stream};
use crate::Tag;
#[derive(Clone, Copy, Debug)]
pub struct LayoutTable<'a> {
pub scripts: ScriptList<'a>,
pub features: FeatureList<'a>,
pub lookups: LookupList<'a>,
#[cfg(feature = "variable-fonts")]
pub variations: Option<FeatureVariations<'a>>,
}
impl<'a> LayoutTable<'a> {
pub(crate) fn parse(data: &'a [u8]) -> Option<Self> {
let mut s = Stream::new(data);
let major_version = s.read::<u16>()?;
let minor_version = s.read::<u16>()?;
if major_version != 1 {
return None;
}
let scripts = ScriptList::parse(s.read_at_offset16(data)?)?;
let features = FeatureList::parse(s.read_at_offset16(data)?)?;
let lookups = LookupList::parse(s.read_at_offset16(data)?)?;
#[cfg(feature = "variable-fonts")]
{
let mut variations_offset = None;
if minor_version >= 1 {
variations_offset = s.read::<Option<Offset32>>()?;
}
let variations = match variations_offset {
Some(offset) => data
.get(offset.to_usize()..)
.and_then(FeatureVariations::parse),
None => None,
};
Some(Self {
scripts,
features,
lookups,
variations,
})
}
#[cfg(not(feature = "variable-fonts"))]
{
Some(Self {
scripts,
features,
lookups,
})
}
}
}
pub type ScriptIndex = u16;
pub type LanguageIndex = u16;
pub type FeatureIndex = u16;
pub type LookupIndex = u16;
pub type VariationIndex = u32;
pub trait RecordListItem<'a>: Sized {
fn parse(tag: Tag, data: &'a [u8]) -> Option<Self>;
}
#[derive(Clone, Copy, Debug)]
pub struct RecordList<'a, T: RecordListItem<'a>> {
data: &'a [u8],
records: LazyArray16<'a, TagRecord>,
data_type: core::marker::PhantomData<T>,
}
impl<'a, T: RecordListItem<'a>> RecordList<'a, T> {
fn parse(data: &'a [u8]) -> Option<Self> {
let mut s = Stream::new(data);
let count = s.read::<u16>()?;
let records = s.read_array16(count)?;
Some(Self {
data,
records,
data_type: core::marker::PhantomData,
})
}
pub fn len(&self) -> u16 {
self.records.len()
}
pub fn is_empty(&self) -> bool {
self.records.is_empty()
}
pub fn get(&self, index: u16) -> Option<T> {
let record = self.records.get(index)?;
self.data
.get(record.offset.to_usize()..)
.and_then(|data| T::parse(record.tag, data))
}
pub fn find(&self, tag: Tag) -> Option<T> {
let record = self
.records
.binary_search_by(|record| record.tag.cmp(&tag))
.map(|p| p.1)?;
self.data
.get(record.offset.to_usize()..)
.and_then(|data| T::parse(record.tag, data))
}
pub fn index(&self, tag: Tag) -> Option<u16> {
self.records
.binary_search_by(|record| record.tag.cmp(&tag))
.map(|p| p.0)
}
}
impl<'a, T: RecordListItem<'a>> IntoIterator for RecordList<'a, T> {
type Item = T;
type IntoIter = RecordListIter<'a, T>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
RecordListIter {
list: self,
index: 0,
}
}
}
#[allow(missing_debug_implementations)]
pub struct RecordListIter<'a, T: RecordListItem<'a>> {
list: RecordList<'a, T>,
index: u16,
}
impl<'a, T: RecordListItem<'a>> Iterator for RecordListIter<'a, T> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
if self.index < self.list.len() {
self.index += 1;
self.list.get(self.index - 1)
} else {
None
}
}
}
pub type ScriptList<'a> = RecordList<'a, Script<'a>>;
pub type LanguageSystemList<'a> = RecordList<'a, LanguageSystem<'a>>;
pub type FeatureList<'a> = RecordList<'a, Feature<'a>>;
#[derive(Clone, Copy, Debug)]
struct TagRecord {
tag: Tag,
offset: Offset16,
}
impl FromData for TagRecord {
const SIZE: usize = 6;
#[inline]
fn parse(data: &[u8]) -> Option<Self> {
let mut s = Stream::new(data);
Some(Self {
tag: s.read::<Tag>()?,
offset: s.read::<Offset16>()?,
})
}
}
#[derive(Clone, Copy, Debug)]
pub struct Script<'a> {
pub tag: Tag,
pub default_language: Option<LanguageSystem<'a>>,
pub languages: LanguageSystemList<'a>,
}
impl<'a> RecordListItem<'a> for Script<'a> {
fn parse(tag: Tag, data: &'a [u8]) -> Option<Self> {
let mut s = Stream::new(data);
let mut default_language = None;
if let Some(offset) = s.read::<Option<Offset16>>()? {
default_language =
LanguageSystem::parse(Tag::from_bytes(b"dflt"), data.get(offset.to_usize()..)?);
}
let mut languages = RecordList::parse(s.tail()?)?;
languages.data = data;
Some(Self {
tag,
default_language,
languages,
})
}
}
#[derive(Clone, Copy, Debug)]
pub struct LanguageSystem<'a> {
pub tag: Tag,
pub required_feature: Option<FeatureIndex>,
pub feature_indices: LazyArray16<'a, FeatureIndex>,
}
impl<'a> RecordListItem<'a> for LanguageSystem<'a> {
fn parse(tag: Tag, data: &'a [u8]) -> Option<Self> {
let mut s = Stream::new(data);
let _lookup_order = s.read::<Offset16>()?; let required_feature = match s.read::<FeatureIndex>()? {
0xFFFF => None,
v => Some(v),
};
let count = s.read::<u16>()?;
let feature_indices = s.read_array16(count)?;
Some(Self {
tag,
required_feature,
feature_indices,
})
}
}
#[allow(missing_docs)]
#[derive(Clone, Copy, Debug)]
pub struct Feature<'a> {
pub tag: Tag,
pub lookup_indices: LazyArray16<'a, LookupIndex>,
}
impl<'a> RecordListItem<'a> for Feature<'a> {
fn parse(tag: Tag, data: &'a [u8]) -> Option<Self> {
let mut s = Stream::new(data);
let _params_offset = s.read::<Offset16>()?; let count = s.read::<u16>()?;
let lookup_indices = s.read_array16(count)?;
Some(Self {
tag,
lookup_indices,
})
}
}