use core::marker::PhantomData;
use alloc::rc::Rc;
use crate::gdef;
use crate::layout::{ClassDef, Coverage, GDEFTable};
#[derive(Copy, Clone)]
pub struct LookupFlag(pub u16);
#[derive(Copy, Clone, PartialEq)]
pub enum IgnoreMarks {
NoIgnoreMarks,
IgnoreAllMarks,
IgnoreMarksExcept(u8),
}
#[derive(Copy, Clone)]
pub struct MatchType {
ignore_bases: bool,
ignore_ligatures: bool,
ignore_marks: IgnoreMarks,
}
pub enum GlyphTable<'a> {
Empty,
ById(&'a [u16]),
ByClassDef(Rc<ClassDef>, &'a [u16]),
ByCoverage(&'a [Rc<Coverage>]),
}
impl<'a> GlyphTable<'a> {
pub fn len(&self) -> usize {
match self {
GlyphTable::Empty => 0,
GlyphTable::ById(ref arr) => arr.len(),
GlyphTable::ByClassDef(_, ref arr) => arr.len(),
GlyphTable::ByCoverage(ref vec) => vec.len(),
}
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
pub struct MatchContext<'a> {
pub backtrack_table: GlyphTable<'a>,
pub input_table: GlyphTable<'a>,
pub lookahead_table: GlyphTable<'a>,
}
pub struct ContextLookupHelper<'a, T> {
pub match_context: MatchContext<'a>,
pub lookup_array: &'a [(u16, u16)],
phantom: PhantomData<T>,
}
impl<'a, T> ContextLookupHelper<'a, T> {
pub fn new(
match_context: MatchContext<'a>,
lookup_array: &'a [(u16, u16)],
) -> ContextLookupHelper<'a, T> {
ContextLookupHelper {
match_context,
lookup_array,
phantom: PhantomData,
}
}
}
pub trait Glyph {
fn get_glyph_index(&self) -> u16;
}
impl LookupFlag {
pub fn get_rtl(self) -> bool {
(self.0 & 0x0001) != 0
}
pub fn get_ignore_bases(self) -> bool {
(self.0 & 0x0002) != 0
}
pub fn get_ignore_ligatures(self) -> bool {
(self.0 & 0x0004) != 0
}
pub fn get_ignore_marks(self) -> IgnoreMarks {
if (self.0 & 0x8) != 0 {
IgnoreMarks::IgnoreAllMarks
} else if self.0 & 0xFF00 != 0 {
IgnoreMarks::IgnoreMarksExcept((self.0 >> 8) as u8)
} else {
IgnoreMarks::NoIgnoreMarks
}
}
}
impl MatchType {
pub fn ignore_marks() -> MatchType {
MatchType {
ignore_bases: false,
ignore_ligatures: false,
ignore_marks: IgnoreMarks::IgnoreAllMarks,
}
}
pub fn marks_only() -> MatchType {
MatchType {
ignore_bases: true,
ignore_ligatures: true,
ignore_marks: IgnoreMarks::NoIgnoreMarks,
}
}
pub fn from_lookup_flag(lookup_flag: LookupFlag) -> MatchType {
MatchType {
ignore_bases: lookup_flag.get_ignore_bases(),
ignore_ligatures: lookup_flag.get_ignore_ligatures(),
ignore_marks: lookup_flag.get_ignore_marks(),
}
}
pub fn match_glyph<G: Glyph>(self, opt_gdef_table: Option<&GDEFTable>, glyph: &G) -> bool {
if !self.ignore_bases
&& !self.ignore_ligatures
&& self.ignore_marks == IgnoreMarks::NoIgnoreMarks
{
return true;
}
let glyph_class = gdef::glyph_class(opt_gdef_table, glyph.get_glyph_index());
if self.ignore_bases && glyph_class == 1 {
return false;
}
if self.ignore_ligatures && glyph_class == 2 {
return false;
}
match self.ignore_marks {
IgnoreMarks::NoIgnoreMarks => true,
IgnoreMarks::IgnoreAllMarks => glyph_class != 3,
IgnoreMarks::IgnoreMarksExcept(keep_class) => {
let mark_attach_class =
gdef::mark_attach_class(opt_gdef_table, glyph.get_glyph_index());
(glyph_class != 3) || (mark_attach_class == u16::from(keep_class))
}
}
}
pub fn find_prev<G: Glyph>(
self,
opt_gdef_table: Option<&GDEFTable>,
glyphs: &[G],
mut index: usize,
) -> Option<usize> {
while index > 0 {
index -= 1;
if self.match_glyph(opt_gdef_table, &glyphs[index]) {
return Some(index);
}
}
None
}
pub fn find_next<G: Glyph>(
self,
opt_gdef_table: Option<&GDEFTable>,
glyphs: &[G],
mut index: usize,
) -> Option<usize> {
while index + 1 < glyphs.len() {
index += 1;
if self.match_glyph(opt_gdef_table, &glyphs[index]) {
return Some(index);
}
}
None
}
pub fn find_nth<G: Glyph>(
self,
opt_gdef_table: Option<&GDEFTable>,
glyphs: &[G],
mut index: usize,
count: usize,
) -> Option<usize> {
for _ in 0..count {
match self.find_next(opt_gdef_table, glyphs, index) {
Some(next_index) => index = next_index,
None => return None,
}
}
Some(index)
}
pub fn find_first<G: Glyph>(
self,
opt_gdef_table: Option<&GDEFTable>,
glyphs: &[G],
) -> Option<usize> {
for (index, glyph) in glyphs.iter().enumerate() {
if self.match_glyph(opt_gdef_table, glyph) {
return Some(index);
}
}
None
}
pub fn match_back<G: Glyph>(
self,
opt_gdef_table: Option<&GDEFTable>,
glyph_table: &GlyphTable<'_>,
glyphs: &[G],
mut index: usize,
) -> bool {
for i in 0..glyph_table.len() {
match self.find_prev(opt_gdef_table, glyphs, index) {
Some(prev_index) => {
index = prev_index;
let glyph_index = glyphs[index].get_glyph_index();
if !check_glyph_table(glyph_table, i, glyph_index) {
return false;
}
}
None => return false,
}
}
true
}
pub fn match_front<G: Glyph>(
self,
opt_gdef_table: Option<&GDEFTable>,
glyph_table: &GlyphTable<'_>,
glyphs: &[G],
mut index: usize,
last_index: &mut usize,
) -> bool {
for i in 0..glyph_table.len() {
match self.find_next(opt_gdef_table, glyphs, index) {
Some(next_index) => {
index = next_index;
let glyph_index = glyphs[index].get_glyph_index();
if !check_glyph_table(glyph_table, i, glyph_index) {
return false;
}
}
None => return false,
}
}
*last_index = index;
true
}
}
impl<'a> MatchContext<'a> {
pub fn matches<G: Glyph>(
&self,
opt_gdef_table: Option<&GDEFTable>,
match_type: MatchType,
glyphs: &[G],
index: usize,
) -> bool {
let mut front_index = index;
match_type.match_back(opt_gdef_table, &self.backtrack_table, glyphs, index)
&& match_type.match_front(
opt_gdef_table,
&self.input_table,
glyphs,
index,
&mut front_index,
)
&& match_type.match_front(
opt_gdef_table,
&self.lookahead_table,
glyphs,
front_index,
&mut front_index,
)
}
}
fn check_glyph_table(glyph_table: &GlyphTable<'_>, i: usize, glyph_index: u16) -> bool {
match *glyph_table {
GlyphTable::Empty => false,
GlyphTable::ById(ref table) => table[i] == glyph_index,
GlyphTable::ByClassDef(ref classdef, ref table) => {
classdef.glyph_class_value(glyph_index) == table[i]
}
GlyphTable::ByCoverage(ref vec) => vec[i].glyph_coverage_value(glyph_index).is_some(),
}
}