use super::{Gdef, Layout, Lookup, LookupFilter, LookupFlag, LookupKind, LookupRecord, Stage};
use crate::parse_prelude::*;
impl<'a> Layout<'a> {
pub fn new(stage: Stage, data: &'a [u8], gdef: Option<Gdef<'a>>) -> Self {
Self {
stage,
data: Buffer::new(data),
gdef,
}
}
pub fn stage(&self) -> Stage {
self.stage
}
pub fn data(&self) -> &'a [u8] {
self.data.data()
}
pub fn gdef(&self) -> Option<&Gdef<'a>> {
self.gdef.as_ref()
}
pub fn num_scripts(&self) -> u16 {
if let Some(base) = self.data.read_u16(4) {
self.data.read_u16(base as usize).unwrap_or(0)
} else {
0
}
}
pub fn script(&'a self, index: u16) -> Option<Script<'a>> {
let b = &self.data;
let list_base = b.read_u16(4)? as usize;
let len = b.read_u16(list_base)?;
if index >= len {
return None;
}
let record_base = list_base + 2 + index as usize * 6;
let tag = b.read_tag(record_base)?;
let mut offset = b.read_u16(record_base + 4)? as u32;
if offset == 0 {
return None;
}
offset += list_base as u32;
let num_languages = b.read_u16(offset as usize + 2)?;
let record = ScriptRecord {
tag,
offset,
num_languages,
};
Some(record.materialize(self))
}
pub fn scripts(&'a self) -> impl Iterator<Item = Script<'a>> + 'a + Clone {
(0..self.num_scripts()).filter_map(move |index| self.script(index))
}
pub fn num_features(&self) -> u16 {
if let Some(base) = self.data.read_u16(6) {
self.data.read_u16(base as usize).unwrap_or(0)
} else {
0
}
}
pub fn feature(&'a self, index: u16) -> Option<Feature<'a>> {
let b = &self.data;
let list_base = b.read_u16(6)? as usize;
let len = b.read_u16(list_base)?;
if index >= len {
return None;
}
let record_base = list_base + 2 + index as usize * 6;
let tag = b.read_tag(record_base)?;
let offset = b.read_u16(record_base + 4)? as u32;
if offset == 0 {
return None;
}
Some(
FeatureRecord {
index,
tag,
offset: list_base as u32 + offset,
}
.materialize(self),
)
}
pub fn features(&'a self) -> impl Iterator<Item = Feature<'a>> + 'a + Clone {
(0..self.num_features()).filter_map(move |index| self.feature(index))
}
pub fn feature_variations(&'a self) -> Option<FeatureVariations<'a>> {
if self.data.read_u16(2) >= Some(1) {
let offset = self.data.read_offset32(10, 0)? as usize;
let len = self.data.read_u32(offset + 4)?;
Some(FeatureVariations {
layout: self,
base: offset,
len,
})
} else {
None
}
}
pub fn num_lookups(&self) -> u16 {
if let Some(base) = self.data.read_u16(8) {
self.data.read_u16(base as usize).unwrap_or(0)
} else {
0
}
}
pub fn lookup(&'a self, index: u16) -> Option<Lookup<'a>> {
let b = &self.data;
let list_base = b.read_u16(8)? as usize;
let len = b.read_u16(list_base)?;
if index >= len {
return None;
}
let base = list_base + b.read_u16(list_base + 2 + index as usize * 2)? as usize;
let mut kind = b.read_u16(base)? as u8;
let flag = b.read_u16(base + 2)?;
let f = flag as u8;
let num_subtables = b.read_u16(base + 4)?;
let mark_class = (flag >> 8) as u8;
let ignore_marks = f & (1 << 3) != 0;
let mut mark_check = false;
let mut mark_set = 0;
if !ignore_marks {
if let Some(gdef) = &self.gdef {
mark_check = mark_class != 0 && gdef.has_mark_classes();
mark_set = if flag & 0x10 != 0 {
let idx = b.read_u16(base + 6 + num_subtables as usize * 2)?;
mark_check = true;
gdef.mark_set_offset(idx).unwrap_or(0)
} else {
0
};
}
}
let is_sub = self.stage == Stage::Substitution;
let subtables = base + 6;
let is_extension = (is_sub && kind == 7) || (!is_sub && kind == 9);
if is_extension && num_subtables > 0 {
let s = base + b.read_u16(subtables)? as usize;
kind = b.read_u16(s + 2)? as u8;
}
use LookupKind::*;
let kind = if is_sub {
match kind {
1 => SingleSubst,
2 => MultipleSubst,
3 => AlternateSubst,
4 => LigatureSubst,
5 => SeqContext,
6 => ChainContext,
8 => RevChainContext,
_ => return None,
}
} else {
match kind {
1 => SinglePos,
2 => PairPos,
3 => CursivePos,
4 => MarkPos,
5 => MarkLigaturePos,
6 => MarkMarkPos,
7 => SeqContext,
8 => ChainContext,
_ => return None,
}
};
let ignored_classes = ((f as u8) & 0b1110) | 1 << 5;
let filter = LookupFilter {
ignored_classes,
mask: 0,
mark_check,
mark_class,
mark_set,
};
Some(
LookupRecord {
index,
stage: self.stage,
kind,
flag: LookupFlag(flag),
filter,
is_extension,
offset: base as u32,
num_subtables,
}
.materialize(self),
)
}
pub fn lookups(&'a self) -> impl Iterator<Item = Lookup<'a>> + 'a + Clone {
(0..self.num_lookups()).filter_map(move |index| self.lookup(index))
}
}
#[derive(Copy, Clone)]
pub struct ScriptRecord {
pub tag: Tag,
pub offset: u32,
pub num_languages: u16,
}
impl ScriptRecord {
pub fn materialize<'a>(&self, layout: &'a Layout<'a>) -> Script<'a> {
Script {
layout,
record: *self,
}
}
}
#[derive(Copy, Clone)]
pub struct Script<'a> {
pub layout: &'a Layout<'a>,
pub record: ScriptRecord,
}
impl<'a> Script<'a> {
pub fn default_language(&self) -> Option<Language<'a>> {
let data = &self.layout.data;
let base = self.record.offset;
let offset = data.read_u16(base as usize)? as u32;
if offset != 0 {
let tag = Tag::new(b"DFLT");
let record = LanguageRecord {
script: self.record,
is_default: true,
tag,
offset: base + offset,
};
Some(Language {
script: *self,
record,
})
} else {
None
}
}
pub fn num_languages(&self) -> u16 {
self.record.num_languages
}
pub fn language(&self, index: u16) -> Option<Language<'a>> {
if index >= self.record.num_languages {
return None;
}
let data = &self.layout.data;
let base = self.record.offset;
let record_base = base as usize + 4 + index as usize * 6;
let tag = data.read_tag(record_base)?;
let mut offset = data.read_u16(record_base + 4)? as u32;
if offset == 0 {
return None;
}
offset += base;
let record = LanguageRecord {
script: self.record,
is_default: false,
tag,
offset,
};
Some(Language {
script: *self,
record,
})
}
pub fn languages(self) -> impl Iterator<Item = Language<'a>> + 'a + Clone {
(0..self.record.num_languages).filter_map(move |index| self.language(index))
}
}
#[derive(Copy, Clone)]
pub struct LanguageRecord {
pub script: ScriptRecord,
pub is_default: bool,
pub tag: Tag,
pub offset: u32,
}
impl LanguageRecord {
pub fn code(&self) -> Option<&'static str> {
None
}
pub fn materialize<'a>(&self, table: &'a Layout<'a>) -> Language<'a> {
let script = self.script.materialize(table);
Language {
script,
record: *self,
}
}
}
#[derive(Copy, Clone)]
pub struct Language<'a> {
pub script: Script<'a>,
pub record: LanguageRecord,
}
impl<'a> Language<'a> {
pub fn layout(&self) -> &Layout<'a> {
self.script.layout
}
pub fn tag(&self) -> Tag {
self.record.tag
}
pub fn code(&self) -> Option<&'static str> {
self.record.code()
}
pub fn feature_indices(&self) -> Slice<'a, u16> {
let data = &self.layout().data;
data.read_slice16(self.record.offset as usize + 4)
.unwrap_or_default()
}
pub fn features(&'a self) -> impl Iterator<Item = Feature<'a>> + 'a + Clone {
self.feature_indices()
.iter()
.filter_map(move |index| self.layout().feature(index))
}
}
#[derive(Copy, Clone, Debug)]
pub struct FeatureRecord {
pub index: u16,
pub tag: Tag,
pub offset: u32,
}
impl FeatureRecord {
pub fn materialize<'a>(&self, layout: &'a Layout<'a>) -> Feature<'a> {
Feature {
layout,
record: *self,
}
}
}
#[derive(Copy, Clone)]
pub struct Feature<'a> {
pub layout: &'a Layout<'a>,
pub record: FeatureRecord,
}
impl<'a> Feature<'a> {
pub fn lookup_indices(&self) -> Slice<'a, u16> {
self.layout
.data
.read_slice16(self.record.offset as usize + 2)
.unwrap_or_default()
}
pub fn lookups(&'a self) -> impl Iterator<Item = Lookup<'a>> + 'a + Clone {
self.lookup_indices()
.iter()
.filter_map(move |index| self.layout.lookup(index))
}
}
#[derive(Copy, Clone)]
pub struct FeatureVariations<'a> {
layout: &'a Layout<'a>,
base: usize,
len: u32,
}
impl<'a> FeatureVariations<'a> {
pub fn len(&self) -> u32 {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn get(&self, index: u32) -> Option<ConditionSet<'a>> {
if index >= self.len {
return None;
}
let data = &self.layout.data;
let record_base = self.base + 8 + index as usize * 8;
let condition_set = data.read_offset32(record_base, self.base as u32)? as usize;
let feature_subst = data.read_offset32(record_base + 4, self.base as u32)?;
let len = data.read_u16(condition_set)?;
Some(ConditionSet {
layout: self.layout,
base: condition_set,
feature_subst,
len,
})
}
pub fn iter(&'a self) -> impl Iterator<Item = ConditionSet<'a>> + 'a + Clone {
(0..self.len).filter_map(move |index| self.get(index))
}
pub fn find(&'a self, coords: &[NormalizedCoord]) -> Option<ConditionSet<'a>> {
for set in self.iter() {
let mut satisfied = true;
for condition in set.iter() {
let coord = coords
.get(condition.axis_index as usize)
.copied()
.unwrap_or(0);
if coord < condition.min_value || coord > condition.max_value {
satisfied = false;
break;
}
}
if satisfied {
return Some(set);
}
}
None
}
}
#[derive(Copy, Clone)]
pub struct ConditionSet<'a> {
layout: &'a Layout<'a>,
base: usize,
feature_subst: u32,
len: u16,
}
impl<'a> ConditionSet<'a> {
pub fn len(&self) -> u16 {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn get(&self, index: u16) -> Option<Condition> {
if index >= self.len {
return None;
}
let data = &self.layout.data;
let offset =
data.read_offset32(self.base + 2 + index as usize * 4, self.base as u32)? as usize;
let format = data.read_u16(offset)?;
if format != 1 {
return None;
}
let axis_index = data.read_u16(offset + 2)?;
let min_value = data.read_i16(offset + 4)?;
let max_value = data.read_i16(offset + 6)?;
Some(Condition {
axis_index,
min_value,
max_value,
})
}
pub fn iter(&'a self) -> impl Iterator<Item = Condition> + 'a + Clone {
(0..self.len).filter_map(move |index| self.get(index))
}
pub fn features(&self) -> FeatureSubst<'a> {
let data = &self.layout.data;
let len = data.read_u16(self.feature_subst as usize + 4).unwrap_or(0);
FeatureSubst {
layout: self.layout,
base: self.feature_subst as usize,
len,
}
}
}
#[derive(Copy, Clone, Debug)]
pub struct Condition {
pub axis_index: u16,
pub min_value: NormalizedCoord,
pub max_value: NormalizedCoord,
}
#[derive(Copy, Clone)]
pub struct FeatureSubst<'a> {
layout: &'a Layout<'a>,
base: usize,
len: u16,
}
impl<'a> FeatureSubst<'a> {
pub fn len(&self) -> u16 {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn get(&self, index: u16) -> Option<Feature<'a>> {
if index >= self.len() {
return None;
}
let data = &self.layout.data;
let subst_base = self.base + 6 + index as usize * 6;
let feature_index = data.read_u16(subst_base)?;
let offset = data.read_offset32(subst_base + 2, self.base as u32)?;
Some(
FeatureRecord {
tag: Tag(0),
index: feature_index,
offset,
}
.materialize(self.layout),
)
}
pub fn iter(&'a self) -> impl Iterator<Item = Feature<'a>> + 'a + Clone {
(0..self.len).filter_map(move |index| self.get(index))
}
pub fn find(&self, feature_index: u16) -> Option<Feature<'a>> {
for i in 0..self.len() {
if let Some(feature) = self.get(i) {
if feature.record.index == feature_index {
return Some(feature);
}
if feature.record.index > feature_index {
return None;
}
}
}
None
}
}