use super::{aat, at};
use super::buffer::*;
use super::internal::{self, at::Gdef, raw_tag, Bytes, RawFont, RawTag};
use crate::font::FontRef;
use crate::text::{Language, Script};
use core::ops::Range;
pub struct Engine<'a> {
pub data: Bytes<'a>,
pub gdef: Gdef<'a>,
pub gsub: at::StageOffsets,
pub gpos: at::StageOffsets,
pub morx: u32,
pub ltag: u32,
pub kerx: u32,
pub ankr: u32,
pub kern: u32,
pub storage: at::Storage,
pub coords: &'a [i16],
pub script: Script,
pub lang: Option<Language>,
pub tags: [RawTag; 4],
pub sub_mode: SubMode,
pub pos_mode: PosMode,
pub use_ot: bool,
pub use_aat: bool,
pub mode: EngineMode,
}
impl<'a> Engine<'a> {
pub fn new(
metadata: &EngineMetadata,
font_data: &'a [u8],
coords: &'a [i16],
script: Script,
lang: Option<Language>,
) -> Self {
let data = Bytes::new(font_data);
let gdef = Gdef::from_offset(font_data, metadata.gdef).unwrap_or_else(Gdef::empty);
let script_tag = script.to_opentype();
let lang_tag = lang.and_then(|l| l.to_opentype());
let (gsub, stags) = if metadata.sub_mode == SubMode::Gsub {
at::StageOffsets::new(&data, metadata.gsub, script_tag, lang_tag).unwrap_or_default()
} else {
(at::StageOffsets::default(), [0, 0])
};
let (gpos, ptags) = if metadata.pos_mode == PosMode::Gpos {
at::StageOffsets::new(&data, metadata.gpos, script_tag, lang_tag).unwrap_or_default()
} else {
(at::StageOffsets::default(), [0, 0])
};
let tags = [stags[0], stags[1], ptags[0], ptags[1]];
let use_ot = gsub.lang != 0 || gpos.lang != 0;
let use_aat = metadata.morx != 0 || metadata.kerx != 0;
let mode = if gsub.lang != 0 && script.is_complex() {
if script == Script::Myanmar {
EngineMode::Myanmar
} else {
EngineMode::Complex
}
} else {
EngineMode::Simple
};
let mut sub_mode = metadata.sub_mode;
let mut pos_mode = metadata.pos_mode;
if sub_mode == SubMode::Gsub && gsub.lang == 0 {
sub_mode = SubMode::None;
}
if pos_mode == PosMode::Gpos && gpos.lang == 0 {
pos_mode = PosMode::None;
}
Self {
data,
gdef,
gsub,
gpos,
morx: metadata.morx,
ltag: metadata.ltag,
kerx: metadata.kerx,
ankr: metadata.ankr,
kern: metadata.kern,
storage: at::Storage::default(),
coords,
script,
lang,
tags,
sub_mode,
pos_mode,
use_ot,
use_aat,
mode,
}
}
}
impl<'a> Engine<'a> {
pub fn tags(&self) -> &[RawTag; 4] {
&self.tags
}
pub fn collect_features(
&self,
builder: &mut at::FeatureStoreBuilder,
store: &mut at::FeatureStore,
) {
builder.build(
store,
self.data.data(),
self.coords,
&self.gdef,
&self.gsub,
&self.gpos,
);
store.groups = store.groups(self.script);
}
pub fn has_feature_vars(&self) -> bool {
self.gsub.var != 0 || self.gpos.var != 0
}
pub fn set_classes(&self, buffer: &mut Buffer, range: Option<Range<usize>>) {
if !self.gdef.ok() {
return;
}
let slice = if let Some(range) = range {
&mut buffer.glyphs[range]
} else {
&mut buffer.glyphs[..]
};
let gdef = &self.gdef;
if gdef.has_mark_classes() {
for g in slice.iter_mut() {
g.class = gdef.class(g.id) as u8;
g.mark_type = gdef.mark_class(g.id) as u8;
}
} else {
for g in slice.iter_mut() {
g.class = gdef.class(g.id) as u8;
}
}
}
pub fn gsub(
&mut self,
store: &at::FeatureStore,
feature_mask: impl Into<at::FeatureMask>,
buffer: &mut Buffer,
buffer_range: Option<Range<usize>>,
) -> bool {
at::apply(
0,
&self.data,
self.gsub.base,
self.coords,
&self.gdef,
&mut self.storage,
store,
feature_mask.into(),
buffer,
buffer_range,
) == Some(true)
}
pub fn gpos(
&mut self,
store: &at::FeatureStore,
feature_mask: impl Into<at::FeatureMask>,
buffer: &mut Buffer,
buffer_range: Option<Range<usize>>,
) -> bool {
at::apply(
1,
&self.data,
self.gpos.base,
self.coords,
&self.gdef,
&mut self.storage,
store,
feature_mask.into(),
buffer,
buffer_range,
) == Some(true)
}
}
impl<'a> Engine<'a> {
pub fn collect_selectors(&self, features: &[(RawTag, u16)], selectors: &mut Vec<(u16, u16)>) {
use internal::aat::morx::feature_from_tag;
selectors.clear();
for (tag, value) in features {
if let Some((selector, [on, off])) = feature_from_tag(*tag) {
let setting = if *value == 0 { off } else { on };
selectors.push((selector, setting));
}
}
selectors.sort_unstable();
}
pub fn morx(&self, buffer: &mut Buffer, selectors: &[(u16, u16)]) {
if self.morx != 0 {
aat::apply_morx(self.data.data(), self.morx, buffer, selectors);
buffer.ensure_order(false);
}
}
pub fn kerx(&self, buffer: &mut Buffer, disable_kern: bool) {
if self.kerx != 0 {
aat::apply_kerx(self.data.data(), self.kerx, self.ankr, buffer, disable_kern);
buffer.ensure_order(false);
}
}
pub fn kern(&self, buffer: &mut Buffer) {
if self.kern != 0 {
aat::apply_kern(self.data.data(), self.kern, buffer);
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum EngineMode {
Simple,
Myanmar,
Complex,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum SubMode {
None,
Gsub,
Morx,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum PosMode {
None,
Gpos,
Kerx,
Kern,
}
#[derive(Copy, Clone)]
pub struct EngineMetadata {
pub gdef: u32,
pub gsub: u32,
pub gpos: u32,
pub morx: u32,
pub ltag: u32,
pub kerx: u32,
pub ankr: u32,
pub kern: u32,
pub sub_mode: SubMode,
pub pos_mode: PosMode,
}
impl EngineMetadata {
pub fn from_font(font: &FontRef) -> Self {
let mut this = Self {
gdef: font.table_offset(raw_tag(b"GDEF")),
gsub: font.table_offset(raw_tag(b"GSUB")),
gpos: font.table_offset(raw_tag(b"GPOS")),
morx: font.table_offset(raw_tag(b"morx")),
ltag: font.table_offset(raw_tag(b"ltag")),
kerx: font.table_offset(raw_tag(b"kerx")),
ankr: font.table_offset(raw_tag(b"ankr")),
kern: font.table_offset(raw_tag(b"kern")),
sub_mode: SubMode::None,
pos_mode: PosMode::None,
};
if this.gsub != 0 {
this.sub_mode = SubMode::Gsub;
} else if this.morx != 0 {
this.sub_mode = SubMode::Morx;
}
if this.gpos != 0 {
this.pos_mode = PosMode::Gpos;
} else if this.kerx != 0 {
this.pos_mode = PosMode::Kerx;
} else if this.kern != 0 {
this.pos_mode = PosMode::Kern;
}
this
}
}